3c5e89da87cf1f6a0a9c228b8e190217ade7b33e
[litex.git] / liteeth / common.py
1 import math
2 from collections import OrderedDict
3
4 from migen.fhdl.std import *
5 from migen.fhdl.decorators import ModuleDecorator
6 from migen.genlib.resetsync import AsyncResetSynchronizer
7 from migen.genlib.record import *
8 from migen.genlib.fsm import FSM, NextState
9 from migen.genlib.misc import chooser
10 from migen.flow.actor import *
11 from migen.flow.plumbing import Buffer
12 from migen.actorlib.structuring import Converter, Pipeline
13 from migen.actorlib.fifo import SyncFIFO, AsyncFIFO
14 from migen.bank.description import *
15
16 eth_mtu = 1532
17 eth_min_len = 46
18 eth_interpacket_gap = 12
19 eth_preamble = 0xD555555555555555
20 buffer_depth = 2**log2_int(eth_mtu, need_pow2=False)
21
22 class HField():
23 def __init__(self, byte, offset, width):
24 self.byte = byte
25 self.offset = offset
26 self.width = width
27
28 ethernet_type_ip = 0x800
29 ethernet_type_arp = 0x806
30
31 mac_header_len = 14
32 mac_header = {
33 "target_mac": HField(0, 0, 48),
34 "sender_mac": HField(6, 0, 48),
35 "ethernet_type": HField(12, 0, 16)
36 }
37
38 arp_hwtype_ethernet = 0x0001
39 arp_proto_ip = 0x0800
40 arp_opcode_request = 0x0001
41 arp_opcode_reply = 0x0002
42
43 arp_header_len = 28
44 arp_header = {
45 "hwtype": HField( 0, 0, 16),
46 "proto": HField( 2, 0, 16),
47 "hwsize": HField( 4, 0, 8),
48 "protosize": HField( 5, 0, 8),
49 "opcode": HField( 6, 0, 16),
50 "sender_mac": HField( 8, 0, 48),
51 "sender_ip": HField(14, 0, 32),
52 "target_mac": HField(18, 0, 48),
53 "target_ip": HField(24, 0, 32)
54 }
55
56 ipv4_header_len = 20
57 ipv4_header = {
58 "ihl": HField(0, 0, 4),
59 "version": HField(0, 4, 4),
60 "total_length": HField(2, 0, 16),
61 "identification": HField(4, 0, 16),
62 "ttl": HField(8, 0, 8),
63 "protocol": HField(9, 0, 8),
64 "checksum": HField(10, 0, 16),
65 "sender_ip": HField(12, 0, 32),
66 "target_ip": HField(16, 0, 32)
67 }
68
69 icmp_header_len = 8
70 icmp_header = {
71 "msgtype": HField( 0, 0, 8),
72 "code": HField( 1, 0, 8),
73 "checksum": HField( 2, 0, 16),
74 "quench": HField( 4, 0, 32)
75 }
76 icmp_protocol = 0x01
77
78 udp_header_len = 8
79 udp_header = {
80 "src_port": HField( 0, 0, 16),
81 "dst_port": HField( 2, 0, 16),
82 "length": HField( 4, 0, 16),
83 "checksum": HField( 6, 0, 16)
84 }
85
86 udp_protocol = 0x11
87
88 etherbone_magic = 0x4e6f
89 etherbone_version = 1
90 etherbone_packet_header_len = 8
91 etherbone_packet_header = {
92 "magic": HField( 0, 0, 16),
93
94 "version": HField( 2, 4, 4),
95 "nr": HField( 2, 2, 1),
96 "pr": HField( 2, 1, 1),
97 "pf": HField( 2, 0, 1),
98
99 "addr_size": HField( 3, 4, 4),
100 "port_size": HField( 3, 0, 4)
101 }
102
103 etherbone_record_header_len = 4
104 etherbone_record_header = {
105 "bca": HField( 0, 0, 1),
106 "rca": HField( 0, 1, 1),
107 "rff": HField( 0, 2, 1),
108 "cyc": HField( 0, 4, 1),
109 "wca": HField( 0, 5, 1),
110 "wff": HField( 0, 6, 1),
111
112 "byte_enable": HField( 1, 0, 8),
113
114 "wcount": HField( 2, 0, 8),
115
116 "rcount": HField( 3, 0, 8)
117 }
118
119 def reverse_bytes(v):
120 n = math.ceil(flen(v)/8)
121 r = []
122 for i in reversed(range(n)):
123 r.append(v[i*8:min((i+1)*8, flen(v))])
124 return Cat(iter(r))
125
126 # layouts
127 def _layout_from_header(header):
128 _layout = []
129 for k, v in sorted(header.items()):
130 _layout.append((k, v.width))
131 return _layout
132
133 def _remove_from_layout(layout, *args):
134 r = []
135 for f in layout:
136 remove = False
137 for arg in args:
138 if f[0] == arg:
139 remove = True
140 if not remove:
141 r.append(f)
142 return r
143
144 def eth_raw_description(dw):
145 payload_layout = [
146 ("data", dw),
147 ("error", dw//8)
148 ]
149 return EndpointDescription(payload_layout, packetized=True)
150
151 def eth_phy_description(dw):
152 payload_layout = [
153 ("data", dw),
154 ("last_be", dw//8),
155 ("error", dw//8)
156 ]
157 return EndpointDescription(payload_layout, packetized=True)
158
159 def eth_mac_description(dw):
160 payload_layout = _layout_from_header(mac_header) + [
161 ("data", dw),
162 ("last_be", dw//8),
163 ("error", dw//8)
164 ]
165 return EndpointDescription(payload_layout, packetized=True)
166
167 def eth_arp_description(dw):
168 payload_layout = [
169 ("data", dw),
170 ("error", dw//8)
171 ]
172 param_layout = _layout_from_header(arp_header)
173 return EndpointDescription(payload_layout, param_layout, packetized=True)
174
175 arp_table_request_layout = [
176 ("ip_address", 32)
177 ]
178
179 arp_table_response_layout = [
180 ("failed", 1),
181 ("mac_address", 48)
182 ]
183
184 def eth_ipv4_description(dw):
185 payload_layout = [
186 ("data", dw),
187 ("error", dw//8)
188 ]
189 param_layout = _layout_from_header(ipv4_header)
190 return EndpointDescription(payload_layout, param_layout, packetized=True)
191
192 def eth_ipv4_user_description(dw):
193 payload_layout = [
194 ("data", dw),
195 ("error", dw//8)
196 ]
197 param_layout = [
198 ("length", 16),
199 ("protocol", 8),
200 ("ip_address", 32)
201 ]
202 return EndpointDescription(payload_layout, param_layout, packetized=True)
203
204 def convert_ip(s):
205 ip = 0
206 for e in s.split("."):
207 ip = ip << 8
208 ip += int(e)
209 return ip
210
211 def eth_icmp_description(dw):
212 payload_layout = [
213 ("data", dw),
214 ("error", dw//8)
215 ]
216 param_layout = _layout_from_header(icmp_header)
217 return EndpointDescription(payload_layout, param_layout, packetized=True)
218
219 def eth_icmp_user_description(dw):
220 payload_layout = [
221 ("data", dw),
222 ("error", dw//8)
223 ]
224 param_layout = _layout_from_header(icmp_header) + [
225 ("ip_address", 32),
226 ("length", 16)
227 ]
228 return EndpointDescription(payload_layout, param_layout, packetized=True)
229
230 def eth_udp_description(dw):
231 payload_layout = [
232 ("data", dw),
233 ("error", dw//8)
234 ]
235 param_layout = _layout_from_header(udp_header)
236 return EndpointDescription(payload_layout, param_layout, packetized=True)
237
238 def eth_udp_user_description(dw):
239 payload_layout = [
240 ("data", dw),
241 ("error", dw//8)
242 ]
243 param_layout = [
244 ("src_port", 16),
245 ("dst_port", 16),
246 ("ip_address", 32),
247 ("length", 16)
248 ]
249 return EndpointDescription(payload_layout, param_layout, packetized=True)
250
251 def eth_etherbone_packet_description(dw):
252 payload_layout = [
253 ("data", dw),
254 ("error", dw//8)
255 ]
256 param_layout = _layout_from_header(etherbone_packet_header)
257 return EndpointDescription(payload_layout, param_layout, packetized=True)
258
259 def eth_etherbone_packet_user_description(dw):
260 payload_layout = [
261 ("data", dw),
262 ("error", dw//8)
263 ]
264 param_layout = _layout_from_header(etherbone_packet_header)
265 param_layout = _remove_from_layout(param_layout, "magic", "portsize", "addrsize", "version")
266 param_layout += eth_udp_user_description(dw).param_layout
267 return EndpointDescription(payload_layout, param_layout, packetized=True)
268
269 def eth_etherbone_record_description(dw):
270 payload_layout = [
271 ("data", dw),
272 ("error", dw//8)
273 ]
274 param_layout = _layout_from_header(etherbone_record_header)
275 return EndpointDescription(payload_layout, param_layout, packetized=True)
276
277 def eth_etherbone_mmap_description(dw):
278 payload_layout = [
279 ("addr", 32),
280 ("data", dw)
281 ]
282 param_layout = [
283 ("count", 8),
284 ("base_addr", 32),
285 ("be", dw//8)
286 ]
287 return EndpointDescription(payload_layout, param_layout, packetized=True)
288
289 # Generic classes
290 class Port:
291 def connect(self, port):
292 r = [
293 Record.connect(self.source, port.sink),
294 Record.connect(port.source, self.sink)
295 ]
296 return r
297
298 # Generic modules
299 @DecorateModule(InsertReset)
300 @DecorateModule(InsertCE)
301 class FlipFlop(Module):
302 def __init__(self, *args, **kwargs):
303 self.d = Signal(*args, **kwargs)
304 self.q = Signal(*args, **kwargs)
305 self.sync += self.q.eq(self.d)
306
307 @DecorateModule(InsertReset)
308 @DecorateModule(InsertCE)
309 class Counter(Module):
310 def __init__(self, signal=None, **kwargs):
311 if signal is None:
312 self.value = Signal(**kwargs)
313 else:
314 self.value = signal
315 self.width = flen(self.value)
316 self.sync += self.value.eq(self.value+1)
317
318 @DecorateModule(InsertReset)
319 @DecorateModule(InsertCE)
320 class Timeout(Module):
321 def __init__(self, length):
322 self.reached = Signal()
323 ###
324 value = Signal(max=length)
325 self.sync += If(~self.reached, value.eq(value+1))
326 self.comb += self.reached.eq(value == (length-1))
327
328 class BufferizeEndpoints(ModuleDecorator):
329 def __init__(self, submodule, *args):
330 ModuleDecorator.__init__(self, submodule)
331
332 endpoints = get_endpoints(submodule)
333 sinks = {}
334 sources = {}
335 for name, endpoint in endpoints.items():
336 if name in args or len(args) == 0:
337 if isinstance(endpoint, Sink):
338 sinks.update({name : endpoint})
339 elif isinstance(endpoint, Source):
340 sources.update({name : endpoint})
341
342 # add buffer on sinks
343 for name, sink in sinks.items():
344 buf = Buffer(sink.description)
345 self.submodules += buf
346 setattr(self, name, buf.d)
347 self.comb += Record.connect(buf.q, sink)
348
349 # add buffer on sources
350 for name, source in sources.items():
351 buf = Buffer(source.description)
352 self.submodules += buf
353 self.comb += Record.connect(source, buf.d)
354 setattr(self, name, buf.q)
355
356 class EndpointPacketStatus(Module):
357 def __init__(self, endpoint):
358 self.start = Signal()
359 self.done = Signal()
360 self.ongoing = Signal()
361
362 ongoing = Signal()
363 self.comb += [
364 self.start.eq(endpoint.stb & endpoint.sop & endpoint.ack),
365 self.done.eq(endpoint.stb & endpoint.eop & endpoint.ack)
366 ]
367 self.sync += \
368 If(self.start,
369 ongoing.eq(1)
370 ).Elif(self.done,
371 ongoing.eq(0)
372 )
373 self.comb += self.ongoing.eq((self.start | ongoing) & ~self.done)
374
375 class PacketBuffer(Module):
376 def __init__(self, description, data_depth, cmd_depth=4, almost_full=None):
377 self.sink = sink = Sink(description)
378 self.source = source = Source(description)
379
380 ###
381 sink_status = EndpointPacketStatus(self.sink)
382 source_status = EndpointPacketStatus(self.source)
383 self.submodules += sink_status, source_status
384
385 # store incoming packets
386 # cmds
387 def cmd_description():
388 layout = [("error", 1)]
389 return EndpointDescription(layout)
390 cmd_fifo = SyncFIFO(cmd_description(), cmd_depth)
391 self.submodules += cmd_fifo
392 self.comb += cmd_fifo.sink.stb.eq(sink_status.done)
393 if hasattr(sink, "error"):
394 self.comb += cmd_fifo.sink.error.eq(sink.error)
395
396 # data
397 data_fifo = SyncFIFO(description, data_depth, buffered=True)
398 self.submodules += data_fifo
399 self.comb += [
400 Record.connect(self.sink, data_fifo.sink),
401 data_fifo.sink.stb.eq(self.sink.stb & cmd_fifo.sink.ack),
402 self.sink.ack.eq(data_fifo.sink.ack & cmd_fifo.sink.ack),
403 ]
404
405 # output packets
406 self.fsm = fsm = FSM(reset_state="IDLE")
407 self.submodules += fsm
408 fsm.act("IDLE",
409 If(cmd_fifo.source.stb,
410 NextState("SEEK_SOP")
411 )
412 )
413 fsm.act("SEEK_SOP",
414 If(~data_fifo.source.sop,
415 data_fifo.source.ack.eq(1)
416 ).Else(
417 NextState("OUTPUT")
418 )
419 )
420 if hasattr(source, "error"):
421 source_error = self.source.error
422 else:
423 source_error = Signal()
424
425 fsm.act("OUTPUT",
426 Record.connect(data_fifo.source, self.source),
427 source_error.eq(cmd_fifo.source.error),
428 If(source_status.done,
429 cmd_fifo.source.ack.eq(1),
430 NextState("IDLE")
431 )
432 )
433
434 # compute almost full
435 if almost_full is not None:
436 self.almost_full = Signal()
437 self.comb += self.almost_full.eq(data_fifo.fifo.level > almost_full)