1 from nmigen
.compat
.sim
import run_simulation
2 from nmigen
.cli
import verilog
, rtlil
3 from nmigen
import Module
, Signal
, Cat
, Array
, Const
, Elaboratable
4 from nmigen
.lib
.coding
import Decoder
6 from nmutil
.latch
import SRLatch
, latchregister
8 from .shadow_fn
import ShadowFn
11 class FnUnit(Elaboratable
):
12 """ implements 11.4.8 function unit, p31
13 also implements optional shadowing 11.5.1, p55
15 shadowing can be used for branches as well as exceptions (interrupts),
16 load/store hold (exceptions again), and vector-element predication
17 (once the predicate is known, which it may not be at instruction issue)
21 * :wid: register file width
22 * :shadow_wid: number of shadow/fail/good/go_die sets
23 * :n_dests: number of destination regfile(s) (index: rfile_sel_i)
24 * :wr_pend: if true, writable observes the g_wr_pend_i vector
25 otherwise observes g_rd_pend_i
29 * dest_i / src1_i / src2_i are in *binary*, whereas...
30 * ...g_rd_pend_i / g_wr_pend_i and rd_pend_o / wr_pend_o are UNARY
31 * req_rel_i (request release) is the direct equivalent of pipeline
32 "output valid" (valid_o)
33 * recover is a local python variable (actually go_die_o)
34 * when shadow_wid = 0, recover and shadown are Consts (i.e. do nothing)
35 * wr_pend is set False for the majority of uses: however for
36 use in a STORE Function Unit it is set to True
38 def __init__(self
, wid
, shadow_wid
=0, n_dests
=1, wr_pend
=False):
40 self
.n_dests
= n_dests
41 self
.shadow_wid
= shadow_wid
42 self
.wr_pend
= wr_pend
46 self
.rfile_sel_i
= Signal(max=n_dests
, reset_less
=True)
48 self
.rfile_sel_i
= Const(0) # no selection. gets Array[0]
49 self
.dest_i
= Signal(max=wid
, reset_less
=True) # Dest R# in (top)
50 self
.src1_i
= Signal(max=wid
, reset_less
=True) # oper1 R# in (top)
51 self
.src2_i
= Signal(max=wid
, reset_less
=True) # oper2 R# in (top)
52 self
.issue_i
= Signal(reset_less
=True) # Issue in (top)
54 self
.go_wr_i
= Signal(reset_less
=True) # Go Write in (left)
55 self
.go_rd_i
= Signal(reset_less
=True) # Go Read in (left)
56 self
.req_rel_i
= Signal(reset_less
=True) # request release (left)
58 self
.g_xx_pend_i
= Array(Signal(wid
, reset_less
=True, name
="g_pend_i") \
59 for i
in range(n_dests
)) # global rd (right)
60 self
.g_wr_pend_i
= Signal(wid
, reset_less
=True) # global wr (right)
63 self
.shadow_i
= Signal(shadow_wid
, reset_less
=True)
64 self
.s_fail_i
= Signal(shadow_wid
, reset_less
=True)
65 self
.s_good_i
= Signal(shadow_wid
, reset_less
=True)
66 self
.go_die_o
= Signal(reset_less
=True)
69 self
.readable_o
= Signal(reset_less
=True) # Readable out (right)
70 self
.writable_o
= Array(Signal(reset_less
=True, name
="writable_o") \
71 for i
in range(n_dests
)) # writable out (right)
72 self
.busy_o
= Signal(reset_less
=True) # busy out (left)
74 self
.src1_pend_o
= Signal(wid
, reset_less
=True) # src1 pending
75 self
.src2_pend_o
= Signal(wid
, reset_less
=True) # src1 pending
76 self
.rd_pend_o
= Signal(wid
, reset_less
=True) # rd pending (right)
77 self
.xx_pend_o
= Array(Signal(wid
, reset_less
=True, name
="pend_o") \
78 for i
in range(n_dests
))# wr pending (right)
80 def elaborate(self
, platform
):
82 m
.submodules
.rd_l
= rd_l
= SRLatch(sync
=False)
83 m
.submodules
.wr_l
= wr_l
= SRLatch(sync
=False)
84 m
.submodules
.dest_d
= dest_d
= Decoder(self
.reg_width
)
85 m
.submodules
.src1_d
= src1_d
= Decoder(self
.reg_width
)
86 m
.submodules
.src2_d
= src2_d
= Decoder(self
.reg_width
)
88 for i
in range(self
.shadow_wid
):
90 setattr(m
.submodules
, "shadow%d" % i
, sh
)
93 # shadow / recover (optional: shadow_wid > 0)
95 recover
= self
.go_die_o
96 shadown
= Signal(reset_less
=True)
103 # get list of latch signals. really must be a better way to do this
105 i_l
.append(l
.issue_i
)
106 shi_l
.append(l
.shadow_i
)
107 fail_l
.append(l
.s_fail_i
)
108 good_l
.append(l
.s_good_i
)
109 sho_l
.append(l
.shadow_o
)
110 rec_l
.append(l
.recover_o
)
111 m
.d
.comb
+= Cat(*i_l
).eq(self
.issue_i
)
112 m
.d
.comb
+= Cat(*fail_l
).eq(self
.s_fail_i
)
113 m
.d
.comb
+= Cat(*good_l
).eq(self
.s_good_i
)
114 m
.d
.comb
+= Cat(*shi_l
).eq(self
.shadow_i
)
115 m
.d
.comb
+= shadown
.eq(~
(Cat(*sho_l
).bool()))
116 m
.d
.comb
+= recover
.eq(Cat(*rec_l
).bool())
122 xx_pend_o
= self
.xx_pend_o
[self
.rfile_sel_i
]
123 writable_o
= self
.writable_o
[self
.rfile_sel_i
]
124 g_pend_i
= self
.g_xx_pend_i
[self
.rfile_sel_i
]
126 for i
in range(self
.n_dests
):
127 m
.d
.comb
+= self
.xx_pend_o
[i
].eq(0) # initialise all array
128 m
.d
.comb
+= self
.writable_o
[i
].eq(0) # to zero
129 m
.d
.comb
+= self
.readable_o
[i
].eq(0) # to zero
131 # go_wr latch: reset on go_wr HI, set on issue
132 m
.d
.comb
+= wr_l
.s
.eq(self
.issue_i
)
133 m
.d
.comb
+= wr_l
.r
.eq(self
.go_wr_i | recover
)
135 # src1 latch: reset on go_rd HI, set on issue
136 m
.d
.comb
+= rd_l
.s
.eq(self
.issue_i
)
137 m
.d
.comb
+= rd_l
.r
.eq(self
.go_rd_i | recover
)
139 # latch/registers for dest / src1 / src2
140 dest_r
= Signal(max=self
.reg_width
, reset_less
=True)
141 src1_r
= Signal(max=self
.reg_width
, reset_less
=True)
142 src2_r
= Signal(max=self
.reg_width
, reset_less
=True)
143 # XXX latch based on *issue* rather than !latch (as in book)
144 latchregister(m
, self
.dest_i
, dest_r
, self
.issue_i
) #wr_l.qn)
145 latchregister(m
, self
.src1_i
, src1_r
, self
.issue_i
) #wr_l.qn)
146 latchregister(m
, self
.src2_i
, src2_r
, self
.issue_i
) #wr_l.qn)
148 # dest decoder (use dest reg as input): write-pending out
149 m
.d
.comb
+= dest_d
.i
.eq(dest_r
)
150 m
.d
.comb
+= dest_d
.n
.eq(wr_l
.qn
) # decode is inverted
151 m
.d
.comb
+= self
.busy_o
.eq(wr_l
.q
) # busy if set
152 m
.d
.comb
+= xx_pend_o
.eq(dest_d
.o
)
154 # src1/src2 decoder (use src1/2 regs as input): read-pending out
155 m
.d
.comb
+= src1_d
.i
.eq(src1_r
)
156 m
.d
.comb
+= src1_d
.n
.eq(rd_l
.qn
) # decode is inverted
157 m
.d
.comb
+= src2_d
.i
.eq(src2_r
)
158 m
.d
.comb
+= src2_d
.n
.eq(rd_l
.qn
) # decode is inverted
159 m
.d
.comb
+= self
.src1_pend_o
.eq(src1_d
.o
)
160 m
.d
.comb
+= self
.src2_pend_o
.eq(src2_d
.o
)
161 m
.d
.comb
+= self
.rd_pend_o
.eq(src1_d
.o | src2_d
.o
)
163 # readable output signal
164 g_rd
= Signal(self
.reg_width
, reset_less
=True)
165 ro
= Signal(reset_less
=True)
166 m
.d
.comb
+= g_rd
.eq(self
.g_wr_pend_i
& self
.rd_pend_o
)
167 m
.d
.comb
+= ro
.eq(~g_rd
.bool())
168 m
.d
.comb
+= self
.readable_o
.eq(ro
)
170 # writable output signal
171 g_wr_v
= Signal(self
.reg_width
, reset_less
=True)
172 g_wr
= Signal(reset_less
=True)
173 wo
= Signal(reset_less
=True)
174 m
.d
.comb
+= g_wr_v
.eq(g_pend_i
& xx_pend_o
)
175 m
.d
.comb
+= g_wr
.eq(~g_wr_v
.bool())
176 m
.d
.comb
+= wo
.eq(g_wr
& rd_l
.qn
& self
.req_rel_i
& shadown
)
177 m
.d
.comb
+= writable_o
.eq(wo
)
189 yield from self
.g_xx_pend_i
190 yield self
.g_wr_pend_i
191 yield self
.readable_o
192 yield from self
.writable_o
194 yield from self
.xx_pend_o
199 ############# ###############
201 # --- renamed / redirected from base class --- #
203 # --- below are convenience classes which match the names --- #
204 # --- of the various mitch alsup book chapter gate diagrams --- #
206 ############# ###############
209 class IntFnUnit(FnUnit
):
210 def __init__(self
, wid
, shadow_wid
=0):
211 FnUnit
.__init
__(self
, wid
, shadow_wid
)
212 self
.int_rd_pend_o
= self
.rd_pend_o
213 self
.int_wr_pend_o
= self
.xx_pend_o
[0]
214 self
.g_int_wr_pend_i
= self
.g_wr_pend_i
215 self
.g_int_rd_pend_i
= self
.g_xx_pend_i
[0]
216 self
.int_readable_o
= self
.readable_o
217 self
.int_writable_o
= self
.writable_o
[0]
219 self
.int_rd_pend_o
.name
= "int_rd_pend_o"
220 self
.int_wr_pend_o
.name
= "int_wr_pend_o"
221 self
.g_int_rd_pend_i
.name
= "g_int_rd_pend_i"
222 self
.g_int_wr_pend_i
.name
= "g_int_wr_pend_i"
223 self
.int_readable_o
.name
= "int_readable_o"
224 self
.int_writable_o
.name
= "int_writable_o"
227 class FPFnUnit(FnUnit
):
228 def __init__(self
, wid
, shadow_wid
=0):
229 FnUnit
.__init
__(self
, wid
, shadow_wid
)
230 self
.fp_rd_pend_o
= self
.rd_pend_o
231 self
.fp_wr_pend_o
= self
.xx_pend_o
[0]
232 self
.g_fp_wr_pend_i
= self
.g_wr_pend_i
233 self
.g_fp_rd_pend_i
= self
.g_xx_pend_i
[0]
234 self
.fp_writable_o
= self
.writable_o
[0]
235 self
.fp_readable_o
= self
.readable_o
237 self
.fp_rd_pend_o
.name
= "fp_rd_pend_o"
238 self
.fp_wr_pend_o
.name
= "fp_wr_pend_o"
239 self
.g_fp_rd_pend_i
.name
= "g_fp_rd_pend_i"
240 self
.g_fp_wr_pend_i
.name
= "g_fp_wr_pend_i"
241 self
.fp_writable_o
.name
= "fp_writable_o"
242 self
.fp_readable_o
.name
= "fp_readable_o"
245 class LDFnUnit(FnUnit
):
246 """ number of dest selectors: 2. assumes len(int_regfile) == len(fp_regfile)
247 * when rfile_sel_i == 0, int_wr_pend_o is set
248 * when rfile_sel_i == 1, fp_wr_pend_o is set
250 def __init__(self
, wid
, shadow_wid
=0):
251 FnUnit
.__init
__(self
, wid
, shadow_wid
, n_dests
=2)
252 self
.int_rd_pend_o
= self
.rd_pend_o
253 self
.int_wr_pend_o
= self
.xx_pend_o
[0]
254 self
.fp_wr_pend_o
= self
.xx_pend_o
[1]
255 self
.g_int_wr_pend_i
= self
.g_wr_pend_i
256 self
.g_int_rd_pend_i
= self
.g_xx_pend_i
[0]
257 self
.g_fp_rd_pend_i
= self
.g_xx_pend_i
[1]
258 self
.int_readable_o
= self
.readable_o
259 self
.int_writable_o
= self
.writable_o
[0]
260 self
.fp_writable_o
= self
.writable_o
[1]
262 self
.int_rd_pend_o
.name
= "int_rd_pend_o"
263 self
.int_wr_pend_o
.name
= "int_wr_pend_o"
264 self
.fp_wr_pend_o
.name
= "fp_wr_pend_o"
265 self
.g_int_wr_pend_i
.name
= "g_int_wr_pend_i"
266 self
.g_int_rd_pend_i
.name
= "g_int_rd_pend_i"
267 self
.g_fp_rd_pend_i
.name
= "g_fp_rd_pend_i"
268 self
.int_readable_o
.name
= "int_readable_o"
269 self
.int_writable_o
.name
= "int_writable_o"
270 self
.fp_writable_o
.name
= "fp_writable_o"
273 class STFnUnit(FnUnit
):
274 """ number of dest selectors: 2. assumes len(int_regfile) == len(fp_regfile)
275 * wr_pend=False indicates to observe global fp write pending
276 * when rfile_sel_i == 0, int_wr_pend_o is set
277 * when rfile_sel_i == 1, fp_wr_pend_o is set
280 def __init__(self
, wid
, shadow_wid
=0):
281 FnUnit
.__init
__(self
, wid
, shadow_wid
, n_dests
=2, wr_pend
=True)
282 self
.int_rd_pend_o
= self
.rd_pend_o
# 1st int read-pending vector
283 self
.int2_rd_pend_o
= self
.xx_pend_o
[0] # 2nd int read-pending vector
284 self
.fp_rd_pend_o
= self
.xx_pend_o
[1] # 1x FP read-pending vector
285 # yes overwrite FnUnit base class g_wr_pend_i vector
286 self
.g_int_wr_pend_i
= self
.g_wr_pend_i
= self
.g_xx_pend_i
[0]
287 self
.g_fp_wr_pend_i
= self
.g_xx_pend_i
[1]
288 self
.int_readable_o
= self
.readable_o
289 self
.int_writable_o
= self
.writable_o
[0]
290 self
.fp_writable_o
= self
.writable_o
[1]
292 self
.int_rd_pend_o
.name
= "int_rd_pend_o"
293 self
.int2_rd_pend_o
.name
= "int2_rd_pend_o"
294 self
.fp_rd_pend_o
.name
= "fp_rd_pend_o"
295 self
.g_int_wr_pend_i
.name
= "g_int_wr_pend_i"
296 self
.g_fp_wr_pend_i
.name
= "g_fp_wr_pend_i"
297 self
.int_readable_o
.name
= "int_readable_o"
298 self
.int_writable_o
.name
= "int_writable_o"
299 self
.fp_writable_o
.name
= "fp_writable_o"
303 def int_fn_unit_sim(dut
):
304 yield dut
.dest_i
.eq(1)
305 yield dut
.issue_i
.eq(1)
307 yield dut
.issue_i
.eq(0)
309 yield dut
.src1_i
.eq(1)
310 yield dut
.issue_i
.eq(1)
314 yield dut
.issue_i
.eq(0)
316 yield dut
.go_rd_i
.eq(1)
318 yield dut
.go_rd_i
.eq(0)
320 yield dut
.go_wr_i
.eq(1)
322 yield dut
.go_wr_i
.eq(0)
325 def test_int_fn_unit():
326 dut
= FnUnit(32, 2, 2)
327 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
328 with
open("test_fn_unit.il", "w") as f
:
331 dut
= LDFnUnit(32, 2)
332 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
333 with
open("test_ld_fn_unit.il", "w") as f
:
336 dut
= STFnUnit(32, 0)
337 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
338 with
open("test_st_fn_unit.il", "w") as f
:
341 run_simulation(dut
, int_fn_unit_sim(dut
), vcd_name
='test_fn_unit.vcd')
343 if __name__
== '__main__':