inverted global write pend vector, on creation of readable signal,
[soc.git] / src / scoreboard / fn_unit.py
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
5
6 from nmutil.latch import SRLatch, latchregister
7
8 from .shadow_fn import ShadowFn
9
10
11 class FnUnit(Elaboratable):
12 """ implements 11.4.8 function unit, p31
13 also implements optional shadowing 11.5.1, p55
14
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)
18
19 Inputs
20
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
26
27 notes:
28
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
37 """
38 def __init__(self, wid, shadow_wid=0, n_dests=1, wr_pend=False):
39 self.reg_width = wid
40 self.n_dests = n_dests
41 self.shadow_wid = shadow_wid
42 self.wr_pend = wr_pend
43
44 # inputs
45 if n_dests > 1:
46 self.rfile_sel_i = Signal(max=n_dests, reset_less=True)
47 else:
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)
53
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)
57
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)
61
62 if shadow_wid:
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)
67
68 # outputs
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)
73
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)
79
80 def elaborate(self, platform):
81 m = Module()
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)
87 s_latches = []
88 for i in range(self.shadow_wid):
89 sh = ShadowFn()
90 setattr(m.submodules, "shadow%d" % i, sh)
91 s_latches.append(sh)
92
93 # shadow / recover (optional: shadow_wid > 0)
94 if self.shadow_wid:
95 recover = self.go_die_o
96 shadown = Signal(reset_less=True)
97 i_l = []
98 fail_l = []
99 good_l = []
100 shi_l = []
101 sho_l = []
102 rec_l = []
103 # get list of latch signals. really must be a better way to do this
104 for l in s_latches:
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())
117 else:
118 shadown = Const(1)
119 recover = Const(0)
120
121 # selector
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]
125
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
130 # go_wr latch: reset on go_wr HI, set on issue
131 m.d.comb += wr_l.s.eq(self.issue_i)
132 m.d.comb += wr_l.r.eq(self.go_wr_i | recover)
133
134 # src1 latch: reset on go_rd HI, set on issue
135 m.d.comb += rd_l.s.eq(self.issue_i)
136 m.d.comb += rd_l.r.eq(self.go_rd_i | recover)
137
138 # latch/registers for dest / src1 / src2
139 dest_r = Signal(max=self.reg_width, reset_less=True)
140 src1_r = Signal(max=self.reg_width, reset_less=True)
141 src2_r = Signal(max=self.reg_width, reset_less=True)
142 # XXX latch based on *issue* rather than !latch (as in book)
143 latchregister(m, self.dest_i, dest_r, self.issue_i) #wr_l.qn)
144 latchregister(m, self.src1_i, src1_r, self.issue_i) #wr_l.qn)
145 latchregister(m, self.src2_i, src2_r, self.issue_i) #wr_l.qn)
146
147 # dest decoder (use dest reg as input): write-pending out
148 m.d.comb += dest_d.i.eq(dest_r)
149 m.d.comb += dest_d.n.eq(wr_l.qn) # decode is inverted
150 m.d.comb += self.busy_o.eq(wr_l.q) # busy if set
151 m.d.comb += xx_pend_o.eq(dest_d.o)
152
153 # src1/src2 decoder (use src1/2 regs as input): read-pending out
154 m.d.comb += src1_d.i.eq(src1_r)
155 m.d.comb += src1_d.n.eq(rd_l.qn) # decode is inverted
156 m.d.comb += src2_d.i.eq(src2_r)
157 m.d.comb += src2_d.n.eq(rd_l.qn) # decode is inverted
158 m.d.comb += self.src1_pend_o.eq(src1_d.o)
159 m.d.comb += self.src2_pend_o.eq(src2_d.o)
160 m.d.comb += self.rd_pend_o.eq(src1_d.o | src2_d.o)
161
162 # readable output signal
163 g_rd = Signal(self.reg_width, reset_less=True)
164 m.d.comb += g_rd.eq((~self.g_wr_pend_i) & self.rd_pend_o)
165 m.d.comb += self.readable_o.eq(g_rd.bool())
166
167 # writable output signal
168 g_wr_v = Signal(self.reg_width, reset_less=True)
169 g_wr = Signal(reset_less=True)
170 wo = Signal(reset_less=True)
171 m.d.comb += g_wr_v.eq(g_pend_i & xx_pend_o)
172 m.d.comb += g_wr.eq(~g_wr_v.bool())
173 m.d.comb += wo.eq(g_wr & rd_l.qn & self.req_rel_i & shadown)
174 m.d.comb += writable_o.eq(wo)
175
176 return m
177
178 def __iter__(self):
179 yield self.dest_i
180 yield self.src1_i
181 yield self.src2_i
182 yield self.issue_i
183 yield self.go_wr_i
184 yield self.go_rd_i
185 yield self.req_rel_i
186 yield from self.g_xx_pend_i
187 yield self.g_wr_pend_i
188 yield self.readable_o
189 yield from self.writable_o
190 yield self.rd_pend_o
191 yield from self.xx_pend_o
192
193 def ports(self):
194 return list(self)
195
196 ############# ###############
197 # --- --- #
198 # --- renamed / redirected from base class --- #
199 # --- --- #
200 # --- below are convenience classes which match the names --- #
201 # --- of the various mitch alsup book chapter gate diagrams --- #
202 # --- --- #
203 ############# ###############
204
205
206 class IntFnUnit(FnUnit):
207 def __init__(self, wid, shadow_wid=0):
208 FnUnit.__init__(self, wid, shadow_wid)
209 self.int_rd_pend_o = self.rd_pend_o
210 self.int_wr_pend_o = self.xx_pend_o[0]
211 self.g_int_wr_pend_i = self.g_wr_pend_i
212 self.g_int_rd_pend_i = self.g_xx_pend_i[0]
213 self.int_readable_o = self.readable_o
214 self.int_writable_o = self.writable_o[0]
215
216 self.int_rd_pend_o.name = "int_rd_pend_o"
217 self.int_wr_pend_o.name = "int_wr_pend_o"
218 self.g_int_rd_pend_i.name = "g_int_rd_pend_i"
219 self.g_int_wr_pend_i.name = "g_int_wr_pend_i"
220 self.int_readable_o.name = "int_readable_o"
221 self.int_writable_o.name = "int_writable_o"
222
223
224 class FPFnUnit(FnUnit):
225 def __init__(self, wid, shadow_wid=0):
226 FnUnit.__init__(self, wid, shadow_wid)
227 self.fp_rd_pend_o = self.rd_pend_o
228 self.fp_wr_pend_o = self.xx_pend_o[0]
229 self.g_fp_wr_pend_i = self.g_wr_pend_i
230 self.g_fp_rd_pend_i = self.g_xx_pend_i[0]
231 self.fp_writable_o = self.writable_o[0]
232 self.fp_readable_o = self.readable_o
233
234 self.fp_rd_pend_o.name = "fp_rd_pend_o"
235 self.fp_wr_pend_o.name = "fp_wr_pend_o"
236 self.g_fp_rd_pend_i.name = "g_fp_rd_pend_i"
237 self.g_fp_wr_pend_i.name = "g_fp_wr_pend_i"
238 self.fp_writable_o.name = "fp_writable_o"
239 self.fp_readable_o.name = "fp_readable_o"
240
241
242 class LDFnUnit(FnUnit):
243 """ number of dest selectors: 2. assumes len(int_regfile) == len(fp_regfile)
244 * when rfile_sel_i == 0, int_wr_pend_o is set
245 * when rfile_sel_i == 1, fp_wr_pend_o is set
246 """
247 def __init__(self, wid, shadow_wid=0):
248 FnUnit.__init__(self, wid, shadow_wid, n_dests=2)
249 self.int_rd_pend_o = self.rd_pend_o
250 self.int_wr_pend_o = self.xx_pend_o[0]
251 self.fp_wr_pend_o = self.xx_pend_o[1]
252 self.g_int_wr_pend_i = self.g_wr_pend_i
253 self.g_int_rd_pend_i = self.g_xx_pend_i[0]
254 self.g_fp_rd_pend_i = self.g_xx_pend_i[1]
255 self.int_readable_o = self.readable_o
256 self.int_writable_o = self.writable_o[0]
257 self.fp_writable_o = self.writable_o[1]
258
259 self.int_rd_pend_o.name = "int_rd_pend_o"
260 self.int_wr_pend_o.name = "int_wr_pend_o"
261 self.fp_wr_pend_o.name = "fp_wr_pend_o"
262 self.g_int_wr_pend_i.name = "g_int_wr_pend_i"
263 self.g_int_rd_pend_i.name = "g_int_rd_pend_i"
264 self.g_fp_rd_pend_i.name = "g_fp_rd_pend_i"
265 self.int_readable_o.name = "int_readable_o"
266 self.int_writable_o.name = "int_writable_o"
267 self.fp_writable_o.name = "fp_writable_o"
268
269
270 class STFnUnit(FnUnit):
271 """ number of dest selectors: 2. assumes len(int_regfile) == len(fp_regfile)
272 * wr_pend=False indicates to observe global fp write pending
273 * when rfile_sel_i == 0, int_wr_pend_o is set
274 * when rfile_sel_i == 1, fp_wr_pend_o is set
275 *
276 """
277 def __init__(self, wid, shadow_wid=0):
278 FnUnit.__init__(self, wid, shadow_wid, n_dests=2, wr_pend=True)
279 self.int_rd_pend_o = self.rd_pend_o # 1st int read-pending vector
280 self.int2_rd_pend_o = self.xx_pend_o[0] # 2nd int read-pending vector
281 self.fp_rd_pend_o = self.xx_pend_o[1] # 1x FP read-pending vector
282 # yes overwrite FnUnit base class g_wr_pend_i vector
283 self.g_int_wr_pend_i = self.g_wr_pend_i = self.g_xx_pend_i[0]
284 self.g_fp_wr_pend_i = self.g_xx_pend_i[1]
285 self.int_readable_o = self.readable_o
286 self.int_writable_o = self.writable_o[0]
287 self.fp_writable_o = self.writable_o[1]
288
289 self.int_rd_pend_o.name = "int_rd_pend_o"
290 self.int2_rd_pend_o.name = "int2_rd_pend_o"
291 self.fp_rd_pend_o.name = "fp_rd_pend_o"
292 self.g_int_wr_pend_i.name = "g_int_wr_pend_i"
293 self.g_fp_wr_pend_i.name = "g_fp_wr_pend_i"
294 self.int_readable_o.name = "int_readable_o"
295 self.int_writable_o.name = "int_writable_o"
296 self.fp_writable_o.name = "fp_writable_o"
297
298
299
300 def int_fn_unit_sim(dut):
301 yield dut.dest_i.eq(1)
302 yield dut.issue_i.eq(1)
303 yield
304 yield dut.issue_i.eq(0)
305 yield
306 yield dut.src1_i.eq(1)
307 yield dut.issue_i.eq(1)
308 yield
309 yield
310 yield
311 yield dut.issue_i.eq(0)
312 yield
313 yield dut.go_rd_i.eq(1)
314 yield
315 yield dut.go_rd_i.eq(0)
316 yield
317 yield dut.go_wr_i.eq(1)
318 yield
319 yield dut.go_wr_i.eq(0)
320 yield
321
322 def test_int_fn_unit():
323 dut = FnUnit(32, 2, 2)
324 vl = rtlil.convert(dut, ports=dut.ports())
325 with open("test_fn_unit.il", "w") as f:
326 f.write(vl)
327
328 dut = LDFnUnit(32, 2)
329 vl = rtlil.convert(dut, ports=dut.ports())
330 with open("test_ld_fn_unit.il", "w") as f:
331 f.write(vl)
332
333 dut = STFnUnit(32, 0)
334 vl = rtlil.convert(dut, ports=dut.ports())
335 with open("test_st_fn_unit.il", "w") as f:
336 f.write(vl)
337
338 run_simulation(dut, int_fn_unit_sim(dut), vcd_name='test_fn_unit.vcd')
339
340 if __name__ == '__main__':
341 test_int_fn_unit()