Connect dramcore to SoC bus in ECPIX-5 example
[gram.git] / gram / stream.py
1 # This file is Copyright (c) 2020 LambdaConcept <contact@lambdaconcept.com>
2
3 from nmigen import *
4 from nmigen.hdl.rec import *
5 from nmigen.lib import fifo
6
7
8 __all__ = ["Endpoint", "SyncFIFO", "AsyncFIFO", "Buffer"]
9
10
11 def _make_fanout(layout):
12 r = []
13 for f in layout:
14 if isinstance(f[1], (int, tuple)):
15 r.append((f[0], f[1], DIR_FANOUT))
16 else:
17 r.append((f[0], _make_fanout(f[1])))
18 return r
19
20
21 class EndpointDescription:
22 def __init__(self, payload_layout):
23 self.payload_layout = payload_layout
24
25 def get_full_layout(self):
26 reserved = {"valid", "ready", "first", "last", "payload"}
27 attributed = set()
28 for f in self.payload_layout:
29 if f[0] in attributed:
30 raise ValueError(f[0] + " already attributed in payload layout")
31 if f[0] in reserved:
32 raise ValueError(f[0] + " cannot be used in endpoint layout")
33 attributed.add(f[0])
34
35 full_layout = [
36 ("valid", 1, DIR_FANOUT),
37 ("ready", 1, DIR_FANIN),
38 ("first", 1, DIR_FANOUT),
39 ("last", 1, DIR_FANOUT),
40 ("payload", _make_fanout(self.payload_layout))
41 ]
42 return full_layout
43
44
45 class Endpoint(Record):
46 def __init__(self, layout_or_description, **kwargs):
47 if isinstance(layout_or_description, EndpointDescription):
48 self.description = layout_or_description
49 else:
50 self.description = EndpointDescription(layout_or_description)
51 super().__init__(self.description.get_full_layout(), src_loc_at=1, **kwargs)
52
53 def __getattr__(self, name):
54 try:
55 return super().__getattr__(name)
56 except AttributeError:
57 return self.fields["payload"][name]
58
59
60 class _FIFOWrapper:
61 def __init__(self, payload_layout):
62 self.sink = Endpoint(payload_layout)
63 self.source = Endpoint(payload_layout)
64
65 self.layout = Layout([
66 ("payload", self.sink.description.payload_layout),
67 ("first", 1, DIR_FANOUT),
68 ("last", 1, DIR_FANOUT)
69 ])
70
71 def elaborate(self, platform):
72 m = Module()
73
74 fifo = m.submodules.fifo = self.fifo
75 fifo_din = Record(self.layout)
76 fifo_dout = Record(self.layout)
77 m.d.comb += [
78 fifo.w_data.eq(fifo_din),
79 fifo_dout.eq(fifo.r_data),
80
81 self.sink.ready.eq(fifo.w_rdy),
82 fifo.w_en.eq(self.sink.valid),
83 fifo_din.first.eq(self.sink.first),
84 fifo_din.last.eq(self.sink.last),
85 fifo_din.payload.eq(self.sink.payload),
86
87 self.source.valid.eq(fifo.r_rdy),
88 self.source.first.eq(fifo_dout.first),
89 self.source.last.eq(fifo_dout.last),
90 self.source.payload.eq(fifo_dout.payload),
91 fifo.r_en.eq(self.source.ready)
92 ]
93
94 return m
95
96
97 class SyncFIFO(Elaboratable, _FIFOWrapper):
98 def __init__(self, layout, depth, fwft=True, buffered=False):
99 super().__init__(layout)
100 if buffered:
101 self.fifo = fifo.SyncFIFOBuffered(width=len(Record(self.layout)), depth=depth, fwft=fwft)
102 else:
103 self.fifo = fifo.SyncFIFO(width=len(Record(self.layout)), depth=depth, fwft=fwft)
104 self.depth = self.fifo.depth
105 self.level = self.fifo.level
106
107
108 class AsyncFIFO(Elaboratable, _FIFOWrapper):
109 def __init__(self, layout, depth, r_domain="read", w_domain="write"):
110 super().__init__(layout)
111 self.fifo = fifo.AsyncFIFO(width=len(Record(self.layout)), depth=depth,
112 r_domain=r_domain, w_domain=w_domain)
113 self.depth = self.fifo.depth
114
115 class PipeValid(Elaboratable):
116 """Pipe valid/payload to cut timing path"""
117 def __init__(self, layout):
118 self.sink = Endpoint(layout)
119 self.source = Endpoint(layout)
120
121 def elaborate(self, platform):
122 m = Module()
123
124 # Pipe when source is not valid or is ready.
125 with m.If(~self.source.valid | self.source.ready):
126 m.d.sync += [
127 self.source.valid.eq(self.sink.valid),
128 self.source.first.eq(self.sink.first),
129 self.source.last.eq(self.sink.last),
130 self.source.payload.eq(self.sink.payload),
131 #self.source.param.eq(self.sink.param), # TODO ensure this can be commented
132 ]
133 m.d.comb += self.sink.ready.eq(~self.source.valid | self.source.ready)
134
135 return m
136
137 class Buffer(PipeValid): pass # FIXME: Replace Buffer with PipeValid in codebase?