3 Associated development bugs:
4 * http://bugs.libre-riscv.org/show_bug.cgi?id=64
5 * http://bugs.libre-riscv.org/show_bug.cgi?id=57
7 Important: see Stage API (stageapi.py) in combination with below
9 Main classes: PrevControl and NextControl.
11 These classes manage the data and the synchronisation state
12 to the previous and next stage, respectively. ready/valid
13 signals are used by the Pipeline classes to tell if data
14 may be safely passed from stage to stage.
16 The connection from one stage to the next is carried out with
17 NextControl.connect_to_next. It is *not* necessary to have
18 a PrevControl.connect_to_prev because it is functionally
19 directly equivalent to prev->next->connect_to_next.
22 from nmigen
import Signal
, Cat
, Const
, Module
, Value
, Elaboratable
23 from nmigen
.cli
import verilog
, rtlil
24 from nmigen
.hdl
.rec
import Record
26 from collections
.abc
import Sequence
, Iterable
27 from collections
import OrderedDict
29 from nmutil
import nmoperator
34 self
.fields
= OrderedDict()
36 def __setattr__(self
, k
, v
):
38 if (k
.startswith('_') or k
in ["fields", "name", "src_loc"] or
39 k
in dir(Object
) or "fields" not in self
.__dict
__):
40 return object.__setattr
__(self
, k
, v
)
43 def __getattr__(self
, k
):
44 if k
in self
.__dict
__:
45 return object.__getattr
__(self
, k
)
49 raise AttributeError(e
)
52 for x
in self
.fields
.values(): # OrderedDict so order is preserved
53 if isinstance(x
, Iterable
):
60 for (k
, o
) in self
.fields
.items():
64 if isinstance(rres
, Sequence
):
71 def ports(self
): # being called "keys" would be much better
75 class RecordObject(Record
):
76 def __init__(self
, layout
=None, name
=None):
77 Record
.__init
__(self
, layout
=layout
or [], name
=name
)
79 def __setattr__(self
, k
, v
):
81 if (k
.startswith('_') or k
in ["fields", "name", "src_loc"] or
82 k
in dir(Record
) or "fields" not in self
.__dict
__):
83 return object.__setattr
__(self
, k
, v
)
85 #print ("RecordObject setattr", k, v)
86 if isinstance(v
, Record
):
87 newlayout
= {k
: (k
, v
.layout
)}
88 elif isinstance(v
, Value
):
89 newlayout
= {k
: (k
, v
.shape())}
91 newlayout
= {k
: (k
, nmoperator
.shape(v
))}
92 self
.layout
.fields
.update(newlayout
)
95 for x
in self
.fields
.values(): # remember: fields is an OrderedDict
96 if isinstance(x
, Iterable
):
97 yield from x
# a bit like flatten (nmigen.tools)
101 def ports(self
): # would be better being called "keys"
105 class PrevControl(Elaboratable
):
106 """ contains signals that come *from* the previous stage (both in and out)
107 * valid_i: previous stage indicating all incoming data is valid.
108 may be a multi-bit signal, where all bits are required
109 to be asserted to indicate "valid".
110 * ready_o: output to next stage indicating readiness to accept data
111 * data_i : an input - MUST be added by the USER of this class
114 def __init__(self
, i_width
=1, stage_ctl
=False, maskwid
=0, offs
=0):
115 self
.stage_ctl
= stage_ctl
116 self
.maskwid
= maskwid
118 self
.mask_i
= Signal(maskwid
) # prev >>in self
119 self
.stop_i
= Signal(maskwid
) # prev >>in self
120 self
.valid_i
= Signal(i_width
, name
="p_valid_i") # prev >>in self
121 self
._ready
_o
= Signal(name
="p_ready_o") # prev <<out self
122 self
.data_i
= None # XXX MUST BE ADDED BY USER
124 self
.s_ready_o
= Signal(name
="p_s_o_rdy") # prev <<out self
125 self
.trigger
= Signal(reset_less
=True)
129 """ public-facing API: indicates (externally) that stage is ready
132 return self
.s_ready_o
# set dynamically by stage
133 return self
._ready
_o
# return this when not under dynamic control
135 def _connect_in(self
, prev
, direct
=False, fn
=None,
136 do_data
=True, do_stop
=True):
137 """ internal helper function to connect stage to an input source.
138 do not use to connect stage-to-stage!
140 valid_i
= prev
.valid_i
if direct
else prev
.valid_i_test
141 res
= [self
.valid_i
.eq(valid_i
),
142 prev
.ready_o
.eq(self
.ready_o
)]
144 res
.append(self
.mask_i
.eq(prev
.mask_i
))
146 res
.append(self
.stop_i
.eq(prev
.stop_i
))
149 data_i
= fn(prev
.data_i
) if fn
is not None else prev
.data_i
150 return res
+ [nmoperator
.eq(self
.data_i
, data_i
)]
153 def valid_i_test(self
):
154 vlen
= len(self
.valid_i
)
156 # multi-bit case: valid only when valid_i is all 1s
157 all1s
= Const(-1, (len(self
.valid_i
), False))
158 valid_i
= (self
.valid_i
== all1s
)
160 # single-bit valid_i case
161 valid_i
= self
.valid_i
163 # when stage indicates not ready, incoming data
164 # must "appear" to be not ready too
166 valid_i
= valid_i
& self
.s_ready_o
170 def elaborate(self
, platform
):
172 m
.d
.comb
+= self
.trigger
.eq(self
.valid_i_test
& self
.ready_o
)
176 res
= [nmoperator
.eq(self
.data_i
, i
.data_i
),
177 self
.ready_o
.eq(i
.ready_o
),
178 self
.valid_i
.eq(i
.valid_i
)]
180 res
.append(self
.mask_i
.eq(i
.mask_i
))
189 if hasattr(self
.data_i
, "ports"):
190 yield from self
.data_i
.ports()
191 elif isinstance(self
.data_i
, Sequence
):
192 yield from self
.data_i
200 class NextControl(Elaboratable
):
201 """ contains the signals that go *to* the next stage (both in and out)
202 * valid_o: output indicating to next stage that data is valid
203 * ready_i: input from next stage indicating that it can accept data
204 * data_o : an output - MUST be added by the USER of this class
206 def __init__(self
, stage_ctl
=False, maskwid
=0):
207 self
.stage_ctl
= stage_ctl
208 self
.maskwid
= maskwid
210 self
.mask_o
= Signal(maskwid
) # self out>> next
211 self
.stop_o
= Signal(maskwid
) # self out>> next
212 self
.valid_o
= Signal(name
="n_valid_o") # self out>> next
213 self
.ready_i
= Signal(name
="n_ready_i") # self <<in next
214 self
.data_o
= None # XXX MUST BE ADDED BY USER
216 self
.d_valid
= Signal(reset
=1) # INTERNAL (data valid)
217 self
.trigger
= Signal(reset_less
=True)
220 def ready_i_test(self
):
222 return self
.ready_i
& self
.d_valid
225 def connect_to_next(self
, nxt
, do_data
=True, do_stop
=True):
226 """ helper function to connect to the next stage data/valid/ready.
227 data/valid is passed *TO* nxt, and ready comes *IN* from nxt.
228 use this when connecting stage-to-stage
230 note: a "connect_from_prev" is completely unnecessary: it's
231 just nxt.connect_to_next(self)
233 res
= [nxt
.valid_i
.eq(self
.valid_o
),
234 self
.ready_i
.eq(nxt
.ready_o
)]
236 res
.append(nxt
.mask_i
.eq(self
.mask_o
))
238 res
.append(nxt
.stop_i
.eq(self
.stop_o
))
240 res
.append(nmoperator
.eq(nxt
.data_i
, self
.data_o
))
241 print ("connect to next", self
, self
.maskwid
, nxt
.data_i
, do_data
, do_stop
)
244 def _connect_out(self
, nxt
, direct
=False, fn
=None,
245 do_data
=True, do_stop
=True):
246 """ internal helper function to connect stage to an output source.
247 do not use to connect stage-to-stage!
249 ready_i
= nxt
.ready_i
if direct
else nxt
.ready_i_test
250 res
= [nxt
.valid_o
.eq(self
.valid_o
),
251 self
.ready_i
.eq(ready_i
)]
253 res
.append(nxt
.mask_o
.eq(self
.mask_o
))
255 res
.append(nxt
.stop_o
.eq(self
.stop_o
))
258 data_o
= fn(nxt
.data_o
) if fn
is not None else nxt
.data_o
259 return res
+ [nmoperator
.eq(data_o
, self
.data_o
)]
261 def elaborate(self
, platform
):
263 m
.d
.comb
+= self
.trigger
.eq(self
.ready_i_test
& self
.valid_o
)
272 if hasattr(self
.data_o
, "ports"):
273 yield from self
.data_o
.ports()
274 elif isinstance(self
.data_o
, Sequence
):
275 yield from self
.data_o