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"
109 class PrevControl(Elaboratable
):
110 """ contains signals that come *from* the previous stage (both in and out)
111 * valid_i: previous stage indicating all incoming data is valid.
112 may be a multi-bit signal, where all bits are required
113 to be asserted to indicate "valid".
114 * ready_o: output to next stage indicating readiness to accept data
115 * data_i : an input - MUST be added by the USER of this class
118 def __init__(self
, i_width
=1, stage_ctl
=False, maskwid
=0, offs
=0):
119 self
.stage_ctl
= stage_ctl
120 self
.maskwid
= maskwid
122 self
.mask_i
= Signal(maskwid
) # prev >>in self
123 self
.stop_i
= Signal(maskwid
) # prev >>in self
124 self
.valid_i
= Signal(i_width
, name
="p_valid_i") # prev >>in self
125 self
._ready
_o
= Signal(name
="p_ready_o") # prev <<out self
126 self
.data_i
= None # XXX MUST BE ADDED BY USER
128 self
.s_ready_o
= Signal(name
="p_s_o_rdy") # prev <<out self
129 self
.trigger
= Signal(reset_less
=True)
133 """ public-facing API: indicates (externally) that stage is ready
136 return self
.s_ready_o
# set dynamically by stage
137 return self
._ready
_o
# return this when not under dynamic control
139 def _connect_in(self
, prev
, direct
=False, fn
=None,
140 do_data
=True, do_stop
=True):
141 """ internal helper function to connect stage to an input source.
142 do not use to connect stage-to-stage!
144 valid_i
= prev
.valid_i
if direct
else prev
.valid_i_test
145 res
= [self
.valid_i
.eq(valid_i
),
146 prev
.ready_o
.eq(self
.ready_o
)]
148 res
.append(self
.mask_i
.eq(prev
.mask_i
))
150 res
.append(self
.stop_i
.eq(prev
.stop_i
))
153 data_i
= fn(prev
.data_i
) if fn
is not None else prev
.data_i
154 return res
+ [nmoperator
.eq(self
.data_i
, data_i
)]
157 def valid_i_test(self
):
158 vlen
= len(self
.valid_i
)
160 # multi-bit case: valid only when valid_i is all 1s
161 all1s
= Const(-1, (len(self
.valid_i
), False))
162 valid_i
= (self
.valid_i
== all1s
)
164 # single-bit valid_i case
165 valid_i
= self
.valid_i
167 # when stage indicates not ready, incoming data
168 # must "appear" to be not ready too
170 valid_i
= valid_i
& self
.s_ready_o
174 def elaborate(self
, platform
):
176 m
.d
.comb
+= self
.trigger
.eq(self
.valid_i_test
& self
.ready_o
)
180 res
= [nmoperator
.eq(self
.data_i
, i
.data_i
),
181 self
.ready_o
.eq(i
.ready_o
),
182 self
.valid_i
.eq(i
.valid_i
)]
184 res
.append(self
.mask_i
.eq(i
.mask_i
))
193 if hasattr(self
.data_i
, "ports"):
194 yield from self
.data_i
.ports()
195 elif (isinstance(self
.data_i
, Sequence
) or
196 isinstance(self
.data_i
, Iterable
)):
197 yield from self
.data_i
205 class NextControl(Elaboratable
):
206 """ contains the signals that go *to* the next stage (both in and out)
207 * valid_o: output indicating to next stage that data is valid
208 * ready_i: input from next stage indicating that it can accept data
209 * data_o : an output - MUST be added by the USER of this class
211 def __init__(self
, stage_ctl
=False, maskwid
=0):
212 self
.stage_ctl
= stage_ctl
213 self
.maskwid
= maskwid
215 self
.mask_o
= Signal(maskwid
) # self out>> next
216 self
.stop_o
= Signal(maskwid
) # self out>> next
217 self
.valid_o
= Signal(name
="n_valid_o") # self out>> next
218 self
.ready_i
= Signal(name
="n_ready_i") # self <<in next
219 self
.data_o
= None # XXX MUST BE ADDED BY USER
221 self
.d_valid
= Signal(reset
=1) # INTERNAL (data valid)
222 self
.trigger
= Signal(reset_less
=True)
225 def ready_i_test(self
):
227 return self
.ready_i
& self
.d_valid
230 def connect_to_next(self
, nxt
, do_data
=True, do_stop
=True):
231 """ helper function to connect to the next stage data/valid/ready.
232 data/valid is passed *TO* nxt, and ready comes *IN* from nxt.
233 use this when connecting stage-to-stage
235 note: a "connect_from_prev" is completely unnecessary: it's
236 just nxt.connect_to_next(self)
238 res
= [nxt
.valid_i
.eq(self
.valid_o
),
239 self
.ready_i
.eq(nxt
.ready_o
)]
241 res
.append(nxt
.mask_i
.eq(self
.mask_o
))
243 res
.append(nxt
.stop_i
.eq(self
.stop_o
))
245 res
.append(nmoperator
.eq(nxt
.data_i
, self
.data_o
))
246 print ("connect to next", self
, self
.maskwid
, nxt
.data_i
, do_data
, do_stop
)
249 def _connect_out(self
, nxt
, direct
=False, fn
=None,
250 do_data
=True, do_stop
=True):
251 """ internal helper function to connect stage to an output source.
252 do not use to connect stage-to-stage!
254 ready_i
= nxt
.ready_i
if direct
else nxt
.ready_i_test
255 res
= [nxt
.valid_o
.eq(self
.valid_o
),
256 self
.ready_i
.eq(ready_i
)]
258 res
.append(nxt
.mask_o
.eq(self
.mask_o
))
260 res
.append(nxt
.stop_o
.eq(self
.stop_o
))
263 data_o
= fn(nxt
.data_o
) if fn
is not None else nxt
.data_o
264 return res
+ [nmoperator
.eq(data_o
, self
.data_o
)]
266 def elaborate(self
, platform
):
268 m
.d
.comb
+= self
.trigger
.eq(self
.ready_i_test
& self
.valid_o
)
277 if hasattr(self
.data_o
, "ports"):
278 yield from self
.data_o
.ports()
279 elif (isinstance(self
.data_o
, Sequence
) or
280 isinstance(self
.data_o
, Iterable
)):
281 yield from self
.data_o