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
26 from nmigen
import tracer
28 from collections
.abc
import Sequence
, Iterable
29 from collections
import OrderedDict
31 from nmutil
import nmoperator
36 self
.fields
= OrderedDict()
38 def __setattr__(self
, k
, v
):
40 if (k
.startswith('_') or k
in ["fields", "name", "src_loc"] or
41 k
in dir(Object
) or "fields" not in self
.__dict
__):
42 return object.__setattr
__(self
, k
, v
)
45 def __getattr__(self
, k
):
46 if k
in self
.__dict
__:
47 return object.__getattr
__(self
, k
)
51 raise AttributeError(e
)
54 for x
in self
.fields
.values(): # OrderedDict so order is preserved
55 if isinstance(x
, Iterable
):
62 for (k
, o
) in self
.fields
.items():
66 if isinstance(rres
, Sequence
):
73 def ports(self
): # being called "keys" would be much better
77 def add_prefix_to_record_signals(prefix
, record
):
78 """recursively hunt through Records, modifying names to add a prefix
80 for key
, val
in record
.fields
.items():
81 if isinstance(val
, Signal
):
82 val
.name
= prefix
+ val
.name
83 elif isinstance(val
, Record
):
84 add_prefix_to_record_signals(prefix
, val
)
87 class RecordObject(Record
):
88 def __init__(self
, layout
=None, name
=None):
90 # name = tracer.get_var_name(depth=2, default="$ro")
91 Record
.__init
__(self
, layout
=layout
or [], name
=name
)
94 def __setattr__(self
, k
, v
):
95 #print(f"RecordObject setattr({k}, {v})")
97 if (k
.startswith('_') or k
in ["fields", "name", "src_loc"] or
98 k
in dir(Record
) or "fields" not in self
.__dict
__):
99 return object.__setattr
__(self
, k
, v
)
101 if self
.name
is None:
104 prefix
= self
.name
+ "_"
105 # Prefix the signal name with the name of the recordobject
106 if isinstance(v
, Signal
):
107 print (self
, self
.name
, v
.name
)
108 v
.name
= prefix
+ v
.name
109 elif isinstance(v
, Record
):
110 add_prefix_to_record_signals(prefix
, v
)
113 #print ("RecordObject setattr", k, v)
114 if isinstance(v
, Record
):
115 newlayout
= {k
: (k
, v
.layout
)}
116 elif isinstance(v
, Value
):
117 newlayout
= {k
: (k
, v
.shape())}
119 newlayout
= {k
: (k
, nmoperator
.shape(v
))}
120 self
.layout
.fields
.update(newlayout
)
123 for x
in self
.fields
.values(): # remember: fields is an OrderedDict
124 if isinstance(x
, Record
):
125 for f
in x
.fields
.values():
127 elif isinstance(x
, Iterable
):
128 yield from x
# a bit like flatten (nmigen.tools)
132 def ports(self
): # would be better being called "keys"
136 class PrevControl(Elaboratable
):
137 """ contains signals that come *from* the previous stage (both in and out)
138 * valid_i: previous stage indicating all incoming data is valid.
139 may be a multi-bit signal, where all bits are required
140 to be asserted to indicate "valid".
141 * ready_o: output to next stage indicating readiness to accept data
142 * data_i : an input - MUST be added by the USER of this class
145 def __init__(self
, i_width
=1, stage_ctl
=False, maskwid
=0, offs
=0):
146 self
.stage_ctl
= stage_ctl
147 self
.maskwid
= maskwid
149 self
.mask_i
= Signal(maskwid
) # prev >>in self
150 self
.stop_i
= Signal(maskwid
) # prev >>in self
151 self
.valid_i
= Signal(i_width
, name
="p_valid_i") # prev >>in self
152 self
._ready
_o
= Signal(name
="p_ready_o") # prev <<out self
153 self
.data_i
= None # XXX MUST BE ADDED BY USER
155 self
.s_ready_o
= Signal(name
="p_s_o_rdy") # prev <<out self
156 self
.trigger
= Signal(reset_less
=True)
160 """ public-facing API: indicates (externally) that stage is ready
163 return self
.s_ready_o
# set dynamically by stage
164 return self
._ready
_o
# return this when not under dynamic control
166 def _connect_in(self
, prev
, direct
=False, fn
=None,
167 do_data
=True, do_stop
=True):
168 """ internal helper function to connect stage to an input source.
169 do not use to connect stage-to-stage!
171 valid_i
= prev
.valid_i
if direct
else prev
.valid_i_test
172 res
= [self
.valid_i
.eq(valid_i
),
173 prev
.ready_o
.eq(self
.ready_o
)]
175 res
.append(self
.mask_i
.eq(prev
.mask_i
))
177 res
.append(self
.stop_i
.eq(prev
.stop_i
))
180 data_i
= fn(prev
.data_i
) if fn
is not None else prev
.data_i
181 return res
+ [nmoperator
.eq(self
.data_i
, data_i
)]
184 def valid_i_test(self
):
185 vlen
= len(self
.valid_i
)
187 # multi-bit case: valid only when valid_i is all 1s
188 all1s
= Const(-1, (len(self
.valid_i
), False))
189 valid_i
= (self
.valid_i
== all1s
)
191 # single-bit valid_i case
192 valid_i
= self
.valid_i
194 # when stage indicates not ready, incoming data
195 # must "appear" to be not ready too
197 valid_i
= valid_i
& self
.s_ready_o
201 def elaborate(self
, platform
):
203 m
.d
.comb
+= self
.trigger
.eq(self
.valid_i_test
& self
.ready_o
)
207 res
= [nmoperator
.eq(self
.data_i
, i
.data_i
),
208 self
.ready_o
.eq(i
.ready_o
),
209 self
.valid_i
.eq(i
.valid_i
)]
211 res
.append(self
.mask_i
.eq(i
.mask_i
))
220 if hasattr(self
.data_i
, "ports"):
221 yield from self
.data_i
.ports()
222 elif (isinstance(self
.data_i
, Sequence
) or
223 isinstance(self
.data_i
, Iterable
)):
224 yield from self
.data_i
232 class NextControl(Elaboratable
):
233 """ contains the signals that go *to* the next stage (both in and out)
234 * valid_o: output indicating to next stage that data is valid
235 * ready_i: input from next stage indicating that it can accept data
236 * data_o : an output - MUST be added by the USER of this class
238 def __init__(self
, stage_ctl
=False, maskwid
=0):
239 self
.stage_ctl
= stage_ctl
240 self
.maskwid
= maskwid
242 self
.mask_o
= Signal(maskwid
) # self out>> next
243 self
.stop_o
= Signal(maskwid
) # self out>> next
244 self
.valid_o
= Signal(name
="n_valid_o") # self out>> next
245 self
.ready_i
= Signal(name
="n_ready_i") # self <<in next
246 self
.data_o
= None # XXX MUST BE ADDED BY USER
248 self
.d_valid
= Signal(reset
=1) # INTERNAL (data valid)
249 self
.trigger
= Signal(reset_less
=True)
252 def ready_i_test(self
):
254 return self
.ready_i
& self
.d_valid
257 def connect_to_next(self
, nxt
, do_data
=True, do_stop
=True):
258 """ helper function to connect to the next stage data/valid/ready.
259 data/valid is passed *TO* nxt, and ready comes *IN* from nxt.
260 use this when connecting stage-to-stage
262 note: a "connect_from_prev" is completely unnecessary: it's
263 just nxt.connect_to_next(self)
265 res
= [nxt
.valid_i
.eq(self
.valid_o
),
266 self
.ready_i
.eq(nxt
.ready_o
)]
268 res
.append(nxt
.mask_i
.eq(self
.mask_o
))
270 res
.append(nxt
.stop_i
.eq(self
.stop_o
))
272 res
.append(nmoperator
.eq(nxt
.data_i
, self
.data_o
))
273 print ("connect to next", self
, self
.maskwid
, nxt
.data_i
, do_data
, do_stop
)
276 def _connect_out(self
, nxt
, direct
=False, fn
=None,
277 do_data
=True, do_stop
=True):
278 """ internal helper function to connect stage to an output source.
279 do not use to connect stage-to-stage!
281 ready_i
= nxt
.ready_i
if direct
else nxt
.ready_i_test
282 res
= [nxt
.valid_o
.eq(self
.valid_o
),
283 self
.ready_i
.eq(ready_i
)]
285 res
.append(nxt
.mask_o
.eq(self
.mask_o
))
287 res
.append(nxt
.stop_o
.eq(self
.stop_o
))
290 data_o
= fn(nxt
.data_o
) if fn
is not None else nxt
.data_o
291 return res
+ [nmoperator
.eq(data_o
, self
.data_o
)]
293 def elaborate(self
, platform
):
295 m
.d
.comb
+= self
.trigger
.eq(self
.ready_i_test
& self
.valid_o
)
304 if hasattr(self
.data_o
, "ports"):
305 yield from self
.data_o
.ports()
306 elif (isinstance(self
.data_o
, Sequence
) or
307 isinstance(self
.data_o
, Iterable
)):
308 yield from self
.data_o