4e5af42e66d762e724c2668c738abba7b8a82378
[litex.git] / liteeth / arp / __init__.py
1 from liteeth.common import *
2 from liteeth.generic.depacketizer import LiteEthDepacketizer
3 from liteeth.generic.packetizer import LiteEthPacketizer
4
5 _arp_table_layout = [
6 ("reply", 1),
7 ("request", 1),
8 ("ip_address", 32),
9 ("mac_address", 48)
10 ]
11
12 class LiteEthARPDepacketizer(LiteEthDepacketizer):
13 def __init__(self):
14 LiteEthDepacketizer.__init__(self,
15 eth_mac_description(8),
16 eth_arp_description(8),
17 arp_header,
18 arp_header_len)
19
20 class LiteEthARPPacketizer(LiteEthPacketizer):
21 def __init__(self):
22 LiteEthPacketizer.__init__(self,
23 eth_arp_description(8),
24 eth_mac_description(8),
25 arp_header,
26 arp_header_len)
27
28 class LiteEthARPTX(Module):
29 def __init__(self, mac_address, ip_address):
30 self.sink = sink = Sink(_arp_table_layout)
31 self.source = Source(eth_mac_description(8))
32 ###
33 packetizer = LiteEthARPPacketizer()
34 self.submodules += packetizer
35 source = packetizer.sink
36
37 counter = Counter(max=arp_packet_length)
38 self.submodules += counter
39
40 fsm = FSM(reset_state="IDLE")
41 self.submodules += fsm
42 fsm.act("IDLE",
43 sink.ack.eq(1),
44 counter.reset.eq(1),
45 If(sink.stb,
46 sink.ack.eq(0),
47 NextState("SEND")
48 )
49 )
50 self.comb += [
51 source.hardware_type.eq(arp_hwtype_ethernet),
52 source.protocol_type.eq(arp_proto_ip),
53 source.hardware_address_length.eq(6),
54 source.protocol_address_length.eq(4),
55 source.source_mac_address.eq(mac_address),
56 source.source_ip_address.eq(ip_address),
57 If(sink.reply,
58 source.operation.eq(arp_opcode_reply),
59 source.destination_mac_address.eq(sink.mac_address),
60 source.destination_ip_address.eq(sink.ip_address)
61 ).Elif(sink.request,
62 source.operation.eq(arp_opcode_request),
63 source.destination_mac_address.eq(0xffffffffffff),
64 source.destination_ip_address.eq(sink.ip_address)
65 )
66 ]
67 fsm.act("SEND",
68 source.stb.eq(1),
69 source.sop.eq(counter.value == 0),
70 source.eop.eq(counter.value == arp_packet_length-1),
71 Record.connect(packetizer.source, self.source),
72 self.source.destination_mac_address.eq(source.destination_mac_address),
73 self.source.source_mac_address.eq(mac_address),
74 self.source.ethernet_type.eq(ethernet_type_arp),
75 If(self.source.stb & self.source.ack,
76 sink.ack.eq(source.eop),
77 counter.ce.eq(1),
78 If(self.source.eop,
79 NextState("IDLE")
80 )
81 )
82 )
83
84 class LiteEthARPRX(Module):
85 def __init__(self, mac_address, ip_address):
86 self.sink = Sink(eth_mac_description(8))
87 self.source = source = Source(_arp_table_layout)
88 ###
89 depacketizer = LiteEthARPDepacketizer()
90 self.submodules += depacketizer
91 self.comb += Record.connect(self.sink, depacketizer.sink)
92 sink = depacketizer.source
93
94 fsm = FSM(reset_state="IDLE")
95 self.submodules += fsm
96 fsm.act("IDLE",
97 sink.ack.eq(1),
98 If(sink.stb & sink.sop,
99 NextState("CHECK")
100 )
101 )
102 valid = Signal()
103 self.comb += valid.eq(
104 sink.stb &
105 (sink.hardware_type == arp_hwtype_ethernet) &
106 (sink.protocol_type == arp_proto_ip) &
107 (sink.hardware_address_length == 6) &
108 (sink.protocol_address_length == 4) &
109 (sink.destination_ip_address == ip_address)
110 )
111 reply = Signal()
112 request = Signal()
113 self.comb += Case(sink.operation, {
114 arp_opcode_request : [request.eq(1)],
115 arp_opcode_reply : [reply.eq(1)],
116 "default" : []
117 })
118 self.comb += [
119 source.ip_address.eq(sink.source_ip_address),
120 source.mac_address.eq(sink.source_mac_address)
121 ]
122 fsm.act("CHECK",
123 If(valid,
124 source.stb.eq(1),
125 source.reply.eq(reply),
126 source.request.eq(request)
127 ),
128 NextState("TERMINATE")
129 ),
130 fsm.act("TERMINATE",
131 sink.ack.eq(1),
132 If(sink.stb & sink.eop,
133 NextState("IDLE")
134 )
135 )
136
137 arp_table_request_layout = [
138 ("ip_address", 32)
139 ]
140
141 arp_table_response_layout = [
142 ("failed", 1),
143 ("mac_address", 48)
144 ]
145
146 class LiteEthARPTable(Module):
147 def __init__(self):
148 self.sink = sink = Sink(_arp_table_layout) # from arp_rx
149 self.source = source = Source(_arp_table_layout) # to arp_tx
150
151 # Request/Response interface
152 self.request = request = Sink(arp_table_request_layout)
153 self.response = response = Source(arp_table_response_layout)
154 ###
155 request_timeout = Timeout(512) # XXX fix me 100ms?
156 request_pending = FlipFlop()
157 self.submodules += request_timeout, request_pending
158 self.comb += [
159 request_timeout.ce.eq(request_pending.q),
160 request_pending.d.eq(1)
161 ]
162
163 # Note: Store only one ip/mac couple, replace this with
164 # a real ARP table
165 update = Signal()
166 cached_ip_address = Signal(32)
167 cached_mac_address = Signal(48)
168
169 fsm = FSM(reset_state="IDLE")
170 self.submodules += fsm
171 fsm.act("IDLE",
172 # Note: for simplicicy, if APR table is busy response from arp_rx
173 # is lost. This is compensated by the protocol (retrys)
174 If(sink.stb & sink.request,
175 NextState("SEND_REPLY")
176 ).Elif(sink.stb & sink.reply & request_pending.q,
177 NextState("UPDATE_TABLE")
178 ).Elif(request.stb | (request_pending.q & request_timeout.reached),
179 NextState("CHECK_TABLE")
180 )
181 )
182 fsm.act("SEND_REPLY",
183 source.stb.eq(1),
184 source.reply.eq(1),
185 source.ip_address.eq(sink.ip_address),
186 If(source.ack,
187 NextState("IDLE")
188 )
189 )
190 fsm.act("UPDATE_TABLE",
191 request_pending.reset.eq(1),
192 update.eq(1),
193 NextState("CHECK_TABLE")
194 )
195 self.sync += [
196 If(update,
197 cached_ip_address.eq(sink.ip_address),
198 cached_mac_address.eq(sink.mac_address)
199 )
200 ]
201 found = Signal()
202 fsm.act("CHECK_TABLE",
203 # XXX: add a live time for cached_mac_address
204 If(request.ip_address == cached_ip_address,
205 request.ack.eq(request.stb),
206 NextState("PRESENT_RESPONSE")
207 ).Else(
208 NextState("SEND_REQUEST")
209 )
210 )
211 fsm.act("SEND_REQUEST",
212 source.stb.eq(1),
213 source.request.eq(1),
214 source.ip_address.eq(request.ip_address),
215 If(source.ack,
216 request_timeout.reset.eq(1),
217 request_pending.ce.eq(1),
218 request.ack.eq(1),
219 NextState("IDLE")
220 )
221 )
222 fsm.act("PRESENT_RESPONSE",
223 response.stb.eq(1),
224 response.failed.eq(0), # XXX add timeout to trigger failed
225 response.mac_address.eq(cached_mac_address),
226 If(response.ack,
227 NextState("IDLE")
228 )
229 )
230
231 class LiteEthARP(Module):
232 def __init__(self, mac_address, ip_address):
233 self.submodules.tx = LiteEthARPTX(mac_address, ip_address)
234 self.submodules.rx = LiteEthARPRX(mac_address, ip_address)
235 self.submodules.table = LiteEthARPTable()
236 self.comb += [
237 Record.connect(self.rx.source, self.table.sink),
238 Record.connect(self.table.source, self.tx.sink)
239 ]
240 self.sink, self.source = self.rx.sink, self.tx.source
241 self.request, self.response = self.table.request, self.table.response