move scoreboard multi rd/wr to new folder
[soc.git] / src / soc / 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 soc.scoreboard.shadow import Shadow
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
39 def __init__(self, wid, shadow_wid=0, n_dests=1, wr_pend=False):
40 self.reg_width = wid
41 self.n_dests = n_dests
42 self.shadow_wid = shadow_wid
43 self.wr_pend = wr_pend
44
45 # inputs
46 if n_dests > 1:
47 self.rfile_sel_i = Signal(range(n_dests), reset_less=True)
48 else:
49 self.rfile_sel_i = Const(0) # no selection. gets Array[0]
50 self.dest_i = Signal(range(wid), reset_less=True) # Dest R# in (top)
51 self.src1_i = Signal(range(wid), reset_less=True) # oper1 R# in (top)
52 self.src2_i = Signal(range(wid), reset_less=True) # oper2 R# in (top)
53 self.issue_i = Signal(reset_less=True) # Issue in (top)
54
55 self.go_wr_i = Signal(reset_less=True) # Go Write in (left)
56 self.go_rd_i = Signal(reset_less=True) # Go Read in (left)
57 self.req_rel_i = Signal(reset_less=True) # request release (left)
58
59 self.g_xx_pend_i = Array(Signal(wid, reset_less=True, name="g_pend_i")
60 for i in range(n_dests)) # global rd (right)
61 self.g_wr_pend_i = Signal(wid, reset_less=True) # global wr (right)
62
63 if shadow_wid:
64 self.shadow_i = Signal(shadow_wid, reset_less=True)
65 self.s_fail_i = Signal(shadow_wid, reset_less=True)
66 self.s_good_i = Signal(shadow_wid, reset_less=True)
67 self.go_die_o = Signal(reset_less=True)
68
69 # outputs
70 self.readable_o = Signal(reset_less=True) # Readable out (right)
71 self.writable_o = Array(Signal(reset_less=True, name="writable_o")
72 for i in range(n_dests)) # writable out (right)
73 self.busy_o = Signal(reset_less=True) # busy out (left)
74
75 self.src1_pend_o = Signal(wid, reset_less=True) # src1 pending
76 self.src2_pend_o = Signal(wid, reset_less=True) # src1 pending
77 self.rd_pend_o = Signal(wid, reset_less=True) # rd pending (right)
78 self.xx_pend_o = Array(Signal(wid, reset_less=True, name="pend_o")
79 for i in range(n_dests)) # wr pending (right)
80
81 def elaborate(self, platform):
82 m = Module()
83 m.submodules.rd_l = rd_l = SRLatch(sync=False)
84 m.submodules.wr_l = wr_l = SRLatch(sync=False)
85 m.submodules.dest_d = dest_d = Decoder(self.reg_width)
86 m.submodules.src1_d = src1_d = Decoder(self.reg_width)
87 m.submodules.src2_d = src2_d = Decoder(self.reg_width)
88
89 # shadow / recover (optional: shadow_wid > 0)
90 m.submodules.shadow = shadow = Shadow(self.shadow_wid)
91 if self.shadow_wid:
92 m.d.comb += shadow.issue_i.eq(self.issue_i)
93 m.d.comb += shadow.s_fail_i.eq(self.s_fail_i)
94 m.d.comb += shadow.s_good_i.eq(self.s_good_i)
95 m.d.comb += shadow.shadow_i.eq(self.shadow_i)
96 shadown = shadow.shadown_o
97 recover = shadow.go_die_o
98
99 # selector
100 xx_pend_o = self.xx_pend_o[self.rfile_sel_i]
101 writable_o = self.writable_o[self.rfile_sel_i]
102 g_pend_i = self.g_xx_pend_i[self.rfile_sel_i]
103
104 for i in range(self.n_dests):
105 m.d.comb += self.xx_pend_o[i].eq(0) # initialise all array
106 m.d.comb += self.writable_o[i].eq(0) # to zero
107 m.d.comb += self.readable_o.eq(0) # to zero
108
109 # go_wr latch: reset on go_wr HI, set on issue
110 m.d.comb += wr_l.s.eq(self.issue_i)
111 m.d.comb += wr_l.r.eq(self.go_wr_i | recover)
112
113 # src1 latch: reset on go_rd HI, set on issue
114 m.d.comb += rd_l.s.eq(self.issue_i)
115 m.d.comb += rd_l.r.eq(self.go_rd_i | recover)
116
117 # latch/registers for dest / src1 / src2
118 dest_r = Signal(range(self.reg_width), reset_less=True)
119 src1_r = Signal(range(self.reg_width), reset_less=True)
120 src2_r = Signal(range(self.reg_width), reset_less=True)
121 # XXX latch based on *issue* rather than !latch (as in book)
122 latchregister(m, self.dest_i, dest_r, self.issue_i) # wr_l.qn)
123 latchregister(m, self.src1_i, src1_r, self.issue_i) # wr_l.qn)
124 latchregister(m, self.src2_i, src2_r, self.issue_i) # wr_l.qn)
125
126 # dest decoder (use dest reg as input): write-pending out
127 m.d.comb += dest_d.i.eq(dest_r)
128 m.d.comb += dest_d.n.eq(wr_l.qn) # decode is inverted
129 m.d.comb += self.busy_o.eq(wr_l.q) # busy if set
130 m.d.comb += xx_pend_o.eq(dest_d.o)
131
132 # src1/src2 decoder (use src1/2 regs as input): read-pending out
133 m.d.comb += src1_d.i.eq(src1_r)
134 m.d.comb += src1_d.n.eq(rd_l.qn) # decode is inverted
135 m.d.comb += src2_d.i.eq(src2_r)
136 m.d.comb += src2_d.n.eq(rd_l.qn) # decode is inverted
137 m.d.comb += self.src1_pend_o.eq(src1_d.o)
138 m.d.comb += self.src2_pend_o.eq(src2_d.o)
139 m.d.comb += self.rd_pend_o.eq(src1_d.o | src2_d.o)
140
141 # readable output signal
142 g_rd = Signal(self.reg_width, reset_less=True)
143 ro = Signal(reset_less=True)
144 m.d.comb += g_rd.eq(~self.g_wr_pend_i & self.rd_pend_o)
145 m.d.comb += ro.eq(~g_rd.bool())
146 m.d.comb += self.readable_o.eq(ro)
147
148 # writable output signal
149 g_wr_v = Signal(self.reg_width, reset_less=True)
150 g_wr = Signal(reset_less=True)
151 wo = Signal(reset_less=True)
152 m.d.comb += g_wr_v.eq(g_pend_i & xx_pend_o)
153 m.d.comb += g_wr.eq(~g_wr_v.bool())
154 m.d.comb += wo.eq(g_wr & rd_l.qn & self.req_rel_i & shadown)
155 m.d.comb += writable_o.eq(wo)
156
157 return m
158
159 def __iter__(self):
160 yield self.dest_i
161 yield self.src1_i
162 yield self.src2_i
163 yield self.issue_i
164 yield self.go_wr_i
165 yield self.go_rd_i
166 yield self.req_rel_i
167 yield from self.g_xx_pend_i
168 yield self.g_wr_pend_i
169 yield self.readable_o
170 yield from self.writable_o
171 yield self.rd_pend_o
172 yield from self.xx_pend_o
173
174 def ports(self):
175 return list(self)
176
177 ############# ###############
178 # --- --- #
179 # --- renamed / redirected from base class --- #
180 # --- --- #
181 # --- below are convenience classes which match the names --- #
182 # --- of the various mitch alsup book chapter gate diagrams --- #
183 # --- --- #
184 ############# ###############
185
186
187 class IntFnUnit(FnUnit):
188 def __init__(self, wid, shadow_wid=0):
189 FnUnit.__init__(self, wid, shadow_wid)
190 self.int_rd_pend_o = self.rd_pend_o
191 self.int_wr_pend_o = self.xx_pend_o[0]
192 self.g_int_wr_pend_i = self.g_wr_pend_i
193 self.g_int_rd_pend_i = self.g_xx_pend_i[0]
194 self.int_readable_o = self.readable_o
195 self.int_writable_o = self.writable_o[0]
196
197 self.int_rd_pend_o.name = "int_rd_pend_o"
198 self.int_wr_pend_o.name = "int_wr_pend_o"
199 self.g_int_rd_pend_i.name = "g_int_rd_pend_i"
200 self.g_int_wr_pend_i.name = "g_int_wr_pend_i"
201 self.int_readable_o.name = "int_readable_o"
202 self.int_writable_o.name = "int_writable_o"
203
204
205 class FPFnUnit(FnUnit):
206 def __init__(self, wid, shadow_wid=0):
207 FnUnit.__init__(self, wid, shadow_wid)
208 self.fp_rd_pend_o = self.rd_pend_o
209 self.fp_wr_pend_o = self.xx_pend_o[0]
210 self.g_fp_wr_pend_i = self.g_wr_pend_i
211 self.g_fp_rd_pend_i = self.g_xx_pend_i[0]
212 self.fp_writable_o = self.writable_o[0]
213 self.fp_readable_o = self.readable_o
214
215 self.fp_rd_pend_o.name = "fp_rd_pend_o"
216 self.fp_wr_pend_o.name = "fp_wr_pend_o"
217 self.g_fp_rd_pend_i.name = "g_fp_rd_pend_i"
218 self.g_fp_wr_pend_i.name = "g_fp_wr_pend_i"
219 self.fp_writable_o.name = "fp_writable_o"
220 self.fp_readable_o.name = "fp_readable_o"
221
222
223 class LDFnUnit(FnUnit):
224 """ number of dest selectors: 2. assumes len(int_regfile) == len(fp_regfile)
225 * when rfile_sel_i == 0, int_wr_pend_o is set
226 * when rfile_sel_i == 1, fp_wr_pend_o is set
227 """
228
229 def __init__(self, wid, shadow_wid=0):
230 FnUnit.__init__(self, wid, shadow_wid, n_dests=2)
231 self.int_rd_pend_o = self.rd_pend_o
232 self.int_wr_pend_o = self.xx_pend_o[0]
233 self.fp_wr_pend_o = self.xx_pend_o[1]
234 self.g_int_wr_pend_i = self.g_wr_pend_i
235 self.g_int_rd_pend_i = self.g_xx_pend_i[0]
236 self.g_fp_rd_pend_i = self.g_xx_pend_i[1]
237 self.int_readable_o = self.readable_o
238 self.int_writable_o = self.writable_o[0]
239 self.fp_writable_o = self.writable_o[1]
240
241 self.int_rd_pend_o.name = "int_rd_pend_o"
242 self.int_wr_pend_o.name = "int_wr_pend_o"
243 self.fp_wr_pend_o.name = "fp_wr_pend_o"
244 self.g_int_wr_pend_i.name = "g_int_wr_pend_i"
245 self.g_int_rd_pend_i.name = "g_int_rd_pend_i"
246 self.g_fp_rd_pend_i.name = "g_fp_rd_pend_i"
247 self.int_readable_o.name = "int_readable_o"
248 self.int_writable_o.name = "int_writable_o"
249 self.fp_writable_o.name = "fp_writable_o"
250
251
252 class STFnUnit(FnUnit):
253 """ number of dest selectors: 2. assumes len(int_regfile) == len(fp_regfile)
254 * wr_pend=False indicates to observe global fp write pending
255 * when rfile_sel_i == 0, int_wr_pend_o is set
256 * when rfile_sel_i == 1, fp_wr_pend_o is set
257 *
258 """
259
260 def __init__(self, wid, shadow_wid=0):
261 FnUnit.__init__(self, wid, shadow_wid, n_dests=2, wr_pend=True)
262 self.int_rd_pend_o = self.rd_pend_o # 1st int read-pending vector
263 self.int2_rd_pend_o = self.xx_pend_o[0] # 2nd int read-pending vector
264 self.fp_rd_pend_o = self.xx_pend_o[1] # 1x FP read-pending vector
265 # yes overwrite FnUnit base class g_wr_pend_i vector
266 self.g_int_wr_pend_i = self.g_wr_pend_i = self.g_xx_pend_i[0]
267 self.g_fp_wr_pend_i = self.g_xx_pend_i[1]
268 self.int_readable_o = self.readable_o
269 self.int_writable_o = self.writable_o[0]
270 self.fp_writable_o = self.writable_o[1]
271
272 self.int_rd_pend_o.name = "int_rd_pend_o"
273 self.int2_rd_pend_o.name = "int2_rd_pend_o"
274 self.fp_rd_pend_o.name = "fp_rd_pend_o"
275 self.g_int_wr_pend_i.name = "g_int_wr_pend_i"
276 self.g_fp_wr_pend_i.name = "g_fp_wr_pend_i"
277 self.int_readable_o.name = "int_readable_o"
278 self.int_writable_o.name = "int_writable_o"
279 self.fp_writable_o.name = "fp_writable_o"
280
281
282 def int_fn_unit_sim(dut):
283 yield dut.dest_i.eq(1)
284 yield dut.issue_i.eq(1)
285 yield
286 yield dut.issue_i.eq(0)
287 yield
288 yield dut.src1_i.eq(1)
289 yield dut.issue_i.eq(1)
290 yield
291 yield
292 yield
293 yield dut.issue_i.eq(0)
294 yield
295 yield dut.go_rd_i.eq(1)
296 yield
297 yield dut.go_rd_i.eq(0)
298 yield
299 yield dut.go_wr_i.eq(1)
300 yield
301 yield dut.go_wr_i.eq(0)
302 yield
303
304
305 def test_int_fn_unit():
306 dut = FnUnit(32, 2, 2)
307 vl = rtlil.convert(dut, ports=dut.ports())
308 with open("test_fn_unit.il", "w") as f:
309 f.write(vl)
310
311 dut = LDFnUnit(32, 2)
312 vl = rtlil.convert(dut, ports=dut.ports())
313 with open("test_ld_fn_unit.il", "w") as f:
314 f.write(vl)
315
316 dut = STFnUnit(32, 0)
317 vl = rtlil.convert(dut, ports=dut.ports())
318 with open("test_st_fn_unit.il", "w") as f:
319 f.write(vl)
320
321 run_simulation(dut, int_fn_unit_sim(dut), vcd_name='test_fn_unit.vcd')
322
323
324 if __name__ == '__main__':
325 test_int_fn_unit()