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
, Iterable
):
98 yield from x
# a bit like flatten (nmigen.tools)
102 def ports(self
): # would be better being called "keys"
106 class PrevControl(Elaboratable
):
107 """ contains signals that come *from* the previous stage (both in and out)
108 * valid_i: previous stage indicating all incoming data is valid.
109 may be a multi-bit signal, where all bits are required
110 to be asserted to indicate "valid".
111 * ready_o: output to next stage indicating readiness to accept data
112 * data_i : an input - MUST be added by the USER of this class
115 def __init__(self
, i_width
=1, stage_ctl
=False, maskwid
=0, offs
=0):
116 self
.stage_ctl
= stage_ctl
117 self
.maskwid
= maskwid
119 self
.mask_i
= Signal(maskwid
) # prev >>in self
120 self
.stop_i
= Signal(maskwid
) # prev >>in self
121 self
.valid_i
= Signal(i_width
, name
="p_valid_i") # prev >>in self
122 self
._ready
_o
= Signal(name
="p_ready_o") # prev <<out self
123 self
.data_i
= None # XXX MUST BE ADDED BY USER
125 self
.s_ready_o
= Signal(name
="p_s_o_rdy") # prev <<out self
126 self
.trigger
= Signal(reset_less
=True)
130 """ public-facing API: indicates (externally) that stage is ready
133 return self
.s_ready_o
# set dynamically by stage
134 return self
._ready
_o
# return this when not under dynamic control
136 def _connect_in(self
, prev
, direct
=False, fn
=None,
137 do_data
=True, do_stop
=True):
138 """ internal helper function to connect stage to an input source.
139 do not use to connect stage-to-stage!
141 valid_i
= prev
.valid_i
if direct
else prev
.valid_i_test
142 res
= [self
.valid_i
.eq(valid_i
),
143 prev
.ready_o
.eq(self
.ready_o
)]
145 res
.append(self
.mask_i
.eq(prev
.mask_i
))
147 res
.append(self
.stop_i
.eq(prev
.stop_i
))
150 data_i
= fn(prev
.data_i
) if fn
is not None else prev
.data_i
151 return res
+ [nmoperator
.eq(self
.data_i
, data_i
)]
154 def valid_i_test(self
):
155 vlen
= len(self
.valid_i
)
157 # multi-bit case: valid only when valid_i is all 1s
158 all1s
= Const(-1, (len(self
.valid_i
), False))
159 valid_i
= (self
.valid_i
== all1s
)
161 # single-bit valid_i case
162 valid_i
= self
.valid_i
164 # when stage indicates not ready, incoming data
165 # must "appear" to be not ready too
167 valid_i
= valid_i
& self
.s_ready_o
171 def elaborate(self
, platform
):
173 m
.d
.comb
+= self
.trigger
.eq(self
.valid_i_test
& self
.ready_o
)
177 res
= [nmoperator
.eq(self
.data_i
, i
.data_i
),
178 self
.ready_o
.eq(i
.ready_o
),
179 self
.valid_i
.eq(i
.valid_i
)]
181 res
.append(self
.mask_i
.eq(i
.mask_i
))
190 if hasattr(self
.data_i
, "ports"):
191 yield from self
.data_i
.ports()
192 elif isinstance(self
.data_i
, Sequence
):
193 yield from self
.data_i
201 class NextControl(Elaboratable
):
202 """ contains the signals that go *to* the next stage (both in and out)
203 * valid_o: output indicating to next stage that data is valid
204 * ready_i: input from next stage indicating that it can accept data
205 * data_o : an output - MUST be added by the USER of this class
207 def __init__(self
, stage_ctl
=False, maskwid
=0):
208 self
.stage_ctl
= stage_ctl
209 self
.maskwid
= maskwid
211 self
.mask_o
= Signal(maskwid
) # self out>> next
212 self
.stop_o
= Signal(maskwid
) # self out>> next
213 self
.valid_o
= Signal(name
="n_valid_o") # self out>> next
214 self
.ready_i
= Signal(name
="n_ready_i") # self <<in next
215 self
.data_o
= None # XXX MUST BE ADDED BY USER
217 self
.d_valid
= Signal(reset
=1) # INTERNAL (data valid)
218 self
.trigger
= Signal(reset_less
=True)
221 def ready_i_test(self
):
223 return self
.ready_i
& self
.d_valid
226 def connect_to_next(self
, nxt
, do_data
=True, do_stop
=True):
227 """ helper function to connect to the next stage data/valid/ready.
228 data/valid is passed *TO* nxt, and ready comes *IN* from nxt.
229 use this when connecting stage-to-stage
231 note: a "connect_from_prev" is completely unnecessary: it's
232 just nxt.connect_to_next(self)
234 res
= [nxt
.valid_i
.eq(self
.valid_o
),
235 self
.ready_i
.eq(nxt
.ready_o
)]
237 res
.append(nxt
.mask_i
.eq(self
.mask_o
))
239 res
.append(nxt
.stop_i
.eq(self
.stop_o
))
241 res
.append(nmoperator
.eq(nxt
.data_i
, self
.data_o
))
242 print ("connect to next", self
, self
.maskwid
, nxt
.data_i
, do_data
, do_stop
)
245 def _connect_out(self
, nxt
, direct
=False, fn
=None,
246 do_data
=True, do_stop
=True):
247 """ internal helper function to connect stage to an output source.
248 do not use to connect stage-to-stage!
250 ready_i
= nxt
.ready_i
if direct
else nxt
.ready_i_test
251 res
= [nxt
.valid_o
.eq(self
.valid_o
),
252 self
.ready_i
.eq(ready_i
)]
254 res
.append(nxt
.mask_o
.eq(self
.mask_o
))
256 res
.append(nxt
.stop_o
.eq(self
.stop_o
))
259 data_o
= fn(nxt
.data_o
) if fn
is not None else nxt
.data_o
260 return res
+ [nmoperator
.eq(data_o
, self
.data_o
)]
262 def elaborate(self
, platform
):
264 m
.d
.comb
+= self
.trigger
.eq(self
.ready_i_test
& self
.valid_o
)
273 if hasattr(self
.data_o
, "ports"):
274 yield from self
.data_o
.ports()
275 elif isinstance(self
.data_o
, Sequence
):
276 yield from self
.data_o