Run autopep8
[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(
31 f[0] + " already attributed in payload layout")
32 if f[0] in reserved:
33 raise ValueError(f[0] + " cannot be used in endpoint layout")
34 attributed.add(f[0])
35
36 full_layout = [
37 ("valid", 1, DIR_FANOUT),
38 ("ready", 1, DIR_FANIN),
39 ("first", 1, DIR_FANOUT),
40 ("last", 1, DIR_FANOUT),
41 ("payload", _make_fanout(self.payload_layout))
42 ]
43 return full_layout
44
45
46 class Endpoint(Record):
47 def __init__(self, layout_or_description, **kwargs):
48 if isinstance(layout_or_description, EndpointDescription):
49 self.description = layout_or_description
50 else:
51 self.description = EndpointDescription(layout_or_description)
52 super().__init__(self.description.get_full_layout(), src_loc_at=1, **kwargs)
53
54 def __getattr__(self, name):
55 try:
56 return super().__getattr__(name)
57 except AttributeError:
58 return self.fields["payload"][name]
59
60
61 class _FIFOWrapper:
62 def __init__(self, payload_layout):
63 self.sink = Endpoint(payload_layout)
64 self.source = Endpoint(payload_layout)
65
66 self.layout = Layout([
67 ("payload", self.sink.description.payload_layout),
68 ("first", 1, DIR_FANOUT),
69 ("last", 1, DIR_FANOUT)
70 ])
71
72 def elaborate(self, platform):
73 m = Module()
74
75 fifo = m.submodules.fifo = self.fifo
76 fifo_din = Record(self.layout)
77 fifo_dout = Record(self.layout)
78 m.d.comb += [
79 fifo.w_data.eq(fifo_din),
80 fifo_dout.eq(fifo.r_data),
81
82 self.sink.ready.eq(fifo.w_rdy),
83 fifo.w_en.eq(self.sink.valid),
84 fifo_din.first.eq(self.sink.first),
85 fifo_din.last.eq(self.sink.last),
86 fifo_din.payload.eq(self.sink.payload),
87
88 self.source.valid.eq(fifo.r_rdy),
89 self.source.first.eq(fifo_dout.first),
90 self.source.last.eq(fifo_dout.last),
91 self.source.payload.eq(fifo_dout.payload),
92 fifo.r_en.eq(self.source.ready)
93 ]
94
95 return m
96
97
98 class SyncFIFO(Elaboratable, _FIFOWrapper):
99 def __init__(self, layout, depth, fwft=True, buffered=False):
100 super().__init__(layout)
101 if buffered:
102 self.fifo = fifo.SyncFIFOBuffered(
103 width=len(Record(self.layout)), depth=depth, fwft=fwft)
104 else:
105 self.fifo = fifo.SyncFIFO(
106 width=len(Record(self.layout)), depth=depth, fwft=fwft)
107 self.depth = self.fifo.depth
108 self.level = self.fifo.level
109
110
111 class AsyncFIFO(Elaboratable, _FIFOWrapper):
112 def __init__(self, layout, depth, r_domain="read", w_domain="write"):
113 super().__init__(layout)
114 self.fifo = fifo.AsyncFIFO(width=len(Record(self.layout)), depth=depth,
115 r_domain=r_domain, w_domain=w_domain)
116 self.depth = self.fifo.depth
117
118
119 class PipeValid(Elaboratable):
120 """Pipe valid/payload to cut timing path"""
121
122 def __init__(self, layout):
123 self.sink = Endpoint(layout)
124 self.source = Endpoint(layout)
125
126 def elaborate(self, platform):
127 m = Module()
128
129 # Pipe when source is not valid or is ready.
130 with m.If(~self.source.valid | self.source.ready):
131 m.d.sync += [
132 self.source.valid.eq(self.sink.valid),
133 self.source.first.eq(self.sink.first),
134 self.source.last.eq(self.sink.last),
135 self.source.payload.eq(self.sink.payload),
136 # self.source.param.eq(self.sink.param), # TODO ensure this can be commented
137 ]
138 m.d.comb += self.sink.ready.eq(~self.source.valid | self.source.ready)
139
140 return m
141
142
143 class Buffer(PipeValid):
144 pass # FIXME: Replace Buffer with PipeValid in codebase?