3 Associated development bugs:
4 * http://bugs.libre-riscv.org/show_bug.cgi?id=148
5 * http://bugs.libre-riscv.org/show_bug.cgi?id=64
6 * http://bugs.libre-riscv.org/show_bug.cgi?id=57
8 Important: see Stage API (stageapi.py) in combination with below
10 Main classes: PrevControl and NextControl.
12 These classes manage the data and the synchronisation state
13 to the previous and next stage, respectively. ready/valid
14 signals are used by the Pipeline classes to tell if data
15 may be safely passed from stage to stage.
17 The connection from one stage to the next is carried out with
18 NextControl.connect_to_next. It is *not* necessary to have
19 a PrevControl.connect_to_prev because it is functionally
20 directly equivalent to prev->next->connect_to_next.
23 from nmigen
import Signal
, Cat
, Const
, Module
, Value
, Elaboratable
24 from nmigen
.cli
import verilog
, rtlil
25 from nmigen
.hdl
.rec
import Record
27 from collections
.abc
import Sequence
, Iterable
28 from collections
import OrderedDict
30 from nmutil
import nmoperator
35 self
.fields
= OrderedDict()
37 def __setattr__(self
, k
, v
):
39 if (k
.startswith('_') or k
in ["fields", "name", "src_loc"] or
40 k
in dir(Object
) or "fields" not in self
.__dict
__):
41 return object.__setattr
__(self
, k
, v
)
44 def __getattr__(self
, k
):
45 if k
in self
.__dict
__:
46 return object.__getattr
__(self
, k
)
50 raise AttributeError(e
)
53 for x
in self
.fields
.values(): # OrderedDict so order is preserved
54 if isinstance(x
, Iterable
):
61 for (k
, o
) in self
.fields
.items():
65 if isinstance(rres
, Sequence
):
72 def ports(self
): # being called "keys" would be much better
76 class RecordObject(Record
):
77 def __init__(self
, layout
=None, name
=None):
78 Record
.__init
__(self
, layout
=layout
or [], name
=name
)
80 def __setattr__(self
, k
, v
):
82 if (k
.startswith('_') or k
in ["fields", "name", "src_loc"] or
83 k
in dir(Record
) or "fields" not in self
.__dict
__):
84 return object.__setattr
__(self
, k
, v
)
86 #print ("RecordObject setattr", k, v)
87 if isinstance(v
, Record
):
88 newlayout
= {k
: (k
, v
.layout
)}
89 elif isinstance(v
, Value
):
90 newlayout
= {k
: (k
, v
.shape())}
92 newlayout
= {k
: (k
, nmoperator
.shape(v
))}
93 self
.layout
.fields
.update(newlayout
)
96 for x
in self
.fields
.values(): # remember: fields is an OrderedDict
97 if isinstance(x
, Record
):
98 for f
in x
.fields
.values():
100 elif isinstance(x
, Iterable
):
101 yield from x
# a bit like flatten (nmigen.tools)
105 def ports(self
): # would be better being called "keys"
107 # If the record itself contains records, flatten them
108 for item
in list(self
):
109 ports_fun
= getattr(item
, "ports", None)
110 if callable(ports_fun
):
111 results
.extend(ports_fun())
117 class PrevControl(Elaboratable
):
118 """ contains signals that come *from* the previous stage (both in and out)
119 * valid_i: previous stage indicating all incoming data is valid.
120 may be a multi-bit signal, where all bits are required
121 to be asserted to indicate "valid".
122 * ready_o: output to next stage indicating readiness to accept data
123 * data_i : an input - MUST be added by the USER of this class
126 def __init__(self
, i_width
=1, stage_ctl
=False, maskwid
=0, offs
=0):
127 self
.stage_ctl
= stage_ctl
128 self
.maskwid
= maskwid
130 self
.mask_i
= Signal(maskwid
) # prev >>in self
131 self
.stop_i
= Signal(maskwid
) # prev >>in self
132 self
.valid_i
= Signal(i_width
, name
="p_valid_i") # prev >>in self
133 self
._ready
_o
= Signal(name
="p_ready_o") # prev <<out self
134 self
.data_i
= None # XXX MUST BE ADDED BY USER
136 self
.s_ready_o
= Signal(name
="p_s_o_rdy") # prev <<out self
137 self
.trigger
= Signal(reset_less
=True)
141 """ public-facing API: indicates (externally) that stage is ready
144 return self
.s_ready_o
# set dynamically by stage
145 return self
._ready
_o
# return this when not under dynamic control
147 def _connect_in(self
, prev
, direct
=False, fn
=None,
148 do_data
=True, do_stop
=True):
149 """ internal helper function to connect stage to an input source.
150 do not use to connect stage-to-stage!
152 valid_i
= prev
.valid_i
if direct
else prev
.valid_i_test
153 res
= [self
.valid_i
.eq(valid_i
),
154 prev
.ready_o
.eq(self
.ready_o
)]
156 res
.append(self
.mask_i
.eq(prev
.mask_i
))
158 res
.append(self
.stop_i
.eq(prev
.stop_i
))
161 data_i
= fn(prev
.data_i
) if fn
is not None else prev
.data_i
162 return res
+ [nmoperator
.eq(self
.data_i
, data_i
)]
165 def valid_i_test(self
):
166 vlen
= len(self
.valid_i
)
168 # multi-bit case: valid only when valid_i is all 1s
169 all1s
= Const(-1, (len(self
.valid_i
), False))
170 valid_i
= (self
.valid_i
== all1s
)
172 # single-bit valid_i case
173 valid_i
= self
.valid_i
175 # when stage indicates not ready, incoming data
176 # must "appear" to be not ready too
178 valid_i
= valid_i
& self
.s_ready_o
182 def elaborate(self
, platform
):
184 m
.d
.comb
+= self
.trigger
.eq(self
.valid_i_test
& self
.ready_o
)
188 res
= [nmoperator
.eq(self
.data_i
, i
.data_i
),
189 self
.ready_o
.eq(i
.ready_o
),
190 self
.valid_i
.eq(i
.valid_i
)]
192 res
.append(self
.mask_i
.eq(i
.mask_i
))
201 if hasattr(self
.data_i
, "ports"):
202 yield from self
.data_i
.ports()
203 elif (isinstance(self
.data_i
, Sequence
) or
204 isinstance(self
.data_i
, Iterable
)):
205 yield from self
.data_i
213 class NextControl(Elaboratable
):
214 """ contains the signals that go *to* the next stage (both in and out)
215 * valid_o: output indicating to next stage that data is valid
216 * ready_i: input from next stage indicating that it can accept data
217 * data_o : an output - MUST be added by the USER of this class
219 def __init__(self
, stage_ctl
=False, maskwid
=0):
220 self
.stage_ctl
= stage_ctl
221 self
.maskwid
= maskwid
223 self
.mask_o
= Signal(maskwid
) # self out>> next
224 self
.stop_o
= Signal(maskwid
) # self out>> next
225 self
.valid_o
= Signal(name
="n_valid_o") # self out>> next
226 self
.ready_i
= Signal(name
="n_ready_i") # self <<in next
227 self
.data_o
= None # XXX MUST BE ADDED BY USER
229 self
.d_valid
= Signal(reset
=1) # INTERNAL (data valid)
230 self
.trigger
= Signal(reset_less
=True)
233 def ready_i_test(self
):
235 return self
.ready_i
& self
.d_valid
238 def connect_to_next(self
, nxt
, do_data
=True, do_stop
=True):
239 """ helper function to connect to the next stage data/valid/ready.
240 data/valid is passed *TO* nxt, and ready comes *IN* from nxt.
241 use this when connecting stage-to-stage
243 note: a "connect_from_prev" is completely unnecessary: it's
244 just nxt.connect_to_next(self)
246 res
= [nxt
.valid_i
.eq(self
.valid_o
),
247 self
.ready_i
.eq(nxt
.ready_o
)]
249 res
.append(nxt
.mask_i
.eq(self
.mask_o
))
251 res
.append(nxt
.stop_i
.eq(self
.stop_o
))
253 res
.append(nmoperator
.eq(nxt
.data_i
, self
.data_o
))
254 print ("connect to next", self
, self
.maskwid
, nxt
.data_i
, do_data
, do_stop
)
257 def _connect_out(self
, nxt
, direct
=False, fn
=None,
258 do_data
=True, do_stop
=True):
259 """ internal helper function to connect stage to an output source.
260 do not use to connect stage-to-stage!
262 ready_i
= nxt
.ready_i
if direct
else nxt
.ready_i_test
263 res
= [nxt
.valid_o
.eq(self
.valid_o
),
264 self
.ready_i
.eq(ready_i
)]
266 res
.append(nxt
.mask_o
.eq(self
.mask_o
))
268 res
.append(nxt
.stop_o
.eq(self
.stop_o
))
271 data_o
= fn(nxt
.data_o
) if fn
is not None else nxt
.data_o
272 return res
+ [nmoperator
.eq(data_o
, self
.data_o
)]
274 def elaborate(self
, platform
):
276 m
.d
.comb
+= self
.trigger
.eq(self
.ready_i_test
& self
.valid_o
)
285 if hasattr(self
.data_o
, "ports"):
286 yield from self
.data_o
.ports()
287 elif (isinstance(self
.data_o
, Sequence
) or
288 isinstance(self
.data_o
, Iterable
)):
289 yield from self
.data_o