b2ef9468a243fa89adc9e3736d00ff16324e19ec
[soc.git] / 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 nmutil.latch import SRLatch
5 from nmigen.lib.coding import Decoder
6
7 from shadow_fn import ShadowFn
8
9
10 class FnUnit(Elaboratable):
11 """ implements 11.4.8 function unit, p31
12 also implements optional shadowing 11.5.1, p55
13
14 shadowing can be used for branches as well as exceptions (interrupts),
15 load/store hold (exceptions again), and vector-element predication
16 (once the predicate is known, which it may not be at instruction issue)
17
18 Inputs
19
20 * :wid: register file width
21 * :shadow_wid: number of shadow/fail/good/go_die sets
22 * :n_dests: number of destination regfile(s) (index: rfile_sel_i)
23 * :wr_pend: if true, writable observes the g_wr_pend_i vector
24 otherwise observes g_rd_pend_i
25
26 notes:
27
28 * dest_i / src1_i / src2_i are in *binary*, whereas...
29 * ...g_rd_pend_i / g_wr_pend_i and rd_pend_o / wr_pend_o are UNARY
30 * req_rel_i (request release) is the direct equivalent of pipeline
31 "output valid" (valid_o)
32 * recover is a local python variable (actually go_die_o)
33 * when shadow_wid = 0, recover and shadown are Consts (i.e. do nothing)
34 * wr_pend is set False for the majority of uses: however for
35 use in a STORE Function Unit it is set to True
36 """
37 def __init__(self, wid, shadow_wid=0, n_dests=1, wr_pend=False):
38 self.reg_width = wid
39 self.n_dests = n_dests
40 self.shadow_wid = shadow_wid
41 self.wr_pend = wr_pend
42
43 # inputs
44 if n_dests > 1:
45 self.rfile_sel_i = Signal(max=n_dests, reset_less=True)
46 else:
47 self.rfile_sel_i = Const(0) # no selection. gets Array[0]
48 self.dest_i = Signal(max=wid, reset_less=True) # Dest R# in (top)
49 self.src1_i = Signal(max=wid, reset_less=True) # oper1 R# in (top)
50 self.src2_i = Signal(max=wid, reset_less=True) # oper2 R# in (top)
51 self.issue_i = Signal(reset_less=True) # Issue in (top)
52
53 self.go_write_i = Signal(reset_less=True) # Go Write in (left)
54 self.go_read_i = Signal(reset_less=True) # Go Read in (left)
55 self.req_rel_i = Signal(reset_less=True) # request release (left)
56
57 self.g_xx_pend_i = Array(Signal(wid, reset_less=True, name="g_pend_i") \
58 for i in range(n_dests)) # global rd (right)
59 self.g_wr_pend_i = Signal(wid, reset_less=True) # global wr (right)
60
61 if shadow_wid:
62 self.shadow_i = Signal(shadow_wid, reset_less=True)
63 self.s_fail_i = Signal(shadow_wid, reset_less=True)
64 self.s_good_i = Signal(shadow_wid, reset_less=True)
65 self.go_die_o = Signal(reset_less=True)
66
67 # outputs
68 self.readable_o = Signal(reset_less=True) # Readable out (right)
69 self.writable_o = Array(Signal(reset_less=True, name="writable_o") \
70 for i in range(n_dests)) # writable out (right)
71 self.busy_o = Signal(reset_less=True) # busy out (left)
72
73 self.rd_pend_o = Signal(wid, reset_less=True) # rd pending (right)
74 self.xx_pend_o = Array(Signal(wid, reset_less=True, name="pend_o") \
75 for i in range(n_dests))# wr pending (right)
76
77 def elaborate(self, platform):
78 m = Module()
79 m.submodules.rd_l = rd_l = SRLatch(sync=False)
80 m.submodules.wr_l = wr_l = SRLatch(sync=False)
81 m.submodules.dest_d = dest_d = Decoder(self.reg_width)
82 m.submodules.src1_d = src1_d = Decoder(self.reg_width)
83 m.submodules.src2_d = src2_d = Decoder(self.reg_width)
84 s_latches = []
85 for i in range(self.shadow_wid):
86 sh = ShadowFn()
87 setattr(m.submodules, "shadow%d" % i, sh)
88 s_latches.append(sh)
89
90 # shadow / recover (optional: shadow_wid > 0)
91 if self.shadow_wid:
92 recover = self.go_die_o
93 shadown = Signal(reset_less=True)
94 i_l = []
95 fail_l = []
96 good_l = []
97 shi_l = []
98 sho_l = []
99 rec_l = []
100 # get list of latch signals. really must be a better way to do this
101 for l in s_latches:
102 i_l.append(l.issue_i)
103 shi_l.append(l.shadow_i)
104 fail_l.append(l.s_fail_i)
105 good_l.append(l.s_good_i)
106 sho_l.append(l.shadow_o)
107 rec_l.append(l.recover_o)
108 m.d.comb += Cat(*i_l).eq(self.issue_i)
109 m.d.comb += Cat(*fail_l).eq(self.s_fail_i)
110 m.d.comb += Cat(*good_l).eq(self.s_good_i)
111 m.d.comb += Cat(*shi_l).eq(self.shadow_i)
112 m.d.comb += shadown.eq(~(Cat(*sho_l).bool()))
113 m.d.comb += recover.eq(Cat(*rec_l).bool())
114 else:
115 shadown = Const(1)
116 recover = Const(0)
117
118 # selector
119 xx_pend_o = self.xx_pend_o[self.rfile_sel_i]
120 writable_o = self.writable_o[self.rfile_sel_i]
121 g_pend_i = self.g_xx_pend_i[self.rfile_sel_i]
122
123 for i in range(self.n_dests):
124 m.d.comb += self.xx_pend_o[i].eq(0) # initialise all array
125 m.d.comb += self.writable_o[i].eq(0) # to zero
126
127 # go_write latch: reset on go_write HI, set on issue
128 m.d.comb += wr_l.s.eq(self.issue_i)
129 m.d.comb += wr_l.r.eq(self.go_write_i | recover)
130
131 # src1 latch: reset on go_read HI, set on issue
132 m.d.comb += rd_l.s.eq(self.issue_i)
133 m.d.comb += rd_l.r.eq(self.go_read_i | recover)
134
135 # dest decoder: write-pending out
136 m.d.comb += dest_d.i.eq(self.dest_i)
137 m.d.comb += dest_d.n.eq(wr_l.qn) # decode is inverted
138 m.d.comb += self.busy_o.eq(wr_l.q) # busy if set
139 m.d.comb += xx_pend_o.eq(dest_d.o)
140
141 # src1/src2 decoder: read-pending out
142 m.d.comb += src1_d.i.eq(self.src1_i)
143 m.d.comb += src1_d.n.eq(rd_l.qn) # decode is inverted
144 m.d.comb += src2_d.i.eq(self.src2_i)
145 m.d.comb += src2_d.n.eq(rd_l.qn) # decode is inverted
146 m.d.comb += self.rd_pend_o.eq(src1_d.o | src2_d.o)
147
148 # readable output signal
149 g_rd = Signal(self.reg_width, reset_less=True)
150 m.d.comb += g_rd.eq(self.g_wr_pend_i & self.rd_pend_o)
151 m.d.comb += self.readable_o.eq(g_rd.bool())
152
153 # writable output signal
154 g_wr_v = Signal(self.reg_width, reset_less=True)
155 g_wr = Signal(reset_less=True)
156 wo = Signal(reset_less=True)
157 m.d.comb += g_wr_v.eq(g_pend_i & xx_pend_o)
158 m.d.comb += g_wr.eq(~g_wr_v.bool())
159 m.d.comb += wo.eq(g_wr & rd_l.q & self.req_rel_i & shadown)
160 m.d.comb += writable_o.eq(wo)
161
162 return m
163
164 def __iter__(self):
165 yield self.dest_i
166 yield self.src1_i
167 yield self.src2_i
168 yield self.issue_i
169 yield self.go_write_i
170 yield self.go_read_i
171 yield self.req_rel_i
172 yield from self.g_xx_pend_i
173 yield self.g_wr_pend_i
174 yield self.readable_o
175 yield from self.writable_o
176 yield self.rd_pend_o
177 yield from self.xx_pend_o
178
179 def ports(self):
180 return list(self)
181
182 ############# ###############
183 # --- --- #
184 # --- renamed / redirected from base class --- #
185 # --- --- #
186 # --- below are convenience classes which match the names --- #
187 # --- of the various mitch alsup book chapter gate diagrams --- #
188 # --- --- #
189 ############# ###############
190
191
192 class IntFnUnit(FnUnit):
193 def __init__(self, wid, shadow_wid=0):
194 FnUnit.__init__(self, wid, shadow_wid)
195 self.int_rd_pend_o = self.rd_pend_o
196 self.int_wr_pend_o = self.xx_pend_o[0]
197 self.g_int_wr_pend_i = self.g_wr_pend_i
198 self.g_int_rd_pend_i = self.g_xx_pend_i[0]
199 self.int_readable_o = self.readable_o
200 self.int_writable_o = self.writable_o[0]
201
202 self.int_rd_pend_o.name = "int_rd_pend_o"
203 self.int_wr_pend_o.name = "int_wr_pend_o"
204 self.g_int_rd_pend_i.name = "g_int_rd_pend_i"
205 self.g_int_wr_pend_i.name = "g_int_wr_pend_i"
206 self.int_readable_o.name = "int_readable_o"
207 self.int_writable_o.name = "int_writable_o"
208
209
210 class FPFnUnit(FnUnit):
211 def __init__(self, wid, shadow_wid=0):
212 FnUnit.__init__(self, wid, shadow_wid)
213 self.fp_rd_pend_o = self.rd_pend_o
214 self.fp_wr_pend_o = self.xx_pend_o[0]
215 self.g_fp_wr_pend_i = self.g_wr_pend_i
216 self.g_fp_rd_pend_i = self.g_xx_pend_i[0]
217 self.fp_writable_o = self.writable_o[0]
218 self.fp_readable_o = self.readable_o
219
220 self.fp_rd_pend_o.name = "fp_rd_pend_o"
221 self.fp_wr_pend_o.name = "fp_wr_pend_o"
222 self.g_fp_rd_pend_i.name = "g_fp_rd_pend_i"
223 self.g_fp_wr_pend_i.name = "g_fp_wr_pend_i"
224 self.fp_writable_o.name = "fp_writable_o"
225 self.fp_readable_o.name = "fp_readable_o"
226
227
228 class LDFnUnit(FnUnit):
229 """ number of dest selectors: 2. assumes len(int_regfile) == len(fp_regfile)
230 * when rfile_sel_i == 0, int_wr_pend_o is set
231 * when rfile_sel_i == 1, fp_wr_pend_o is set
232 """
233 def __init__(self, wid, shadow_wid=0):
234 FnUnit.__init__(self, wid, shadow_wid, n_dests=2)
235 self.int_rd_pend_o = self.rd_pend_o
236 self.int_wr_pend_o = self.xx_pend_o[0]
237 self.fp_wr_pend_o = self.xx_pend_o[1]
238 self.g_int_wr_pend_i = self.g_wr_pend_i
239 self.g_int_rd_pend_i = self.g_xx_pend_i[0]
240 self.g_fp_rd_pend_i = self.g_xx_pend_i[1]
241 self.int_readable_o = self.readable_o
242 self.int_writable_o = self.writable_o[0]
243 self.fp_writable_o = self.writable_o[1]
244
245 self.int_rd_pend_o.name = "int_rd_pend_o"
246 self.int_wr_pend_o.name = "int_wr_pend_o"
247 self.fp_wr_pend_o.name = "fp_wr_pend_o"
248 self.g_int_wr_pend_i.name = "g_int_wr_pend_i"
249 self.g_int_rd_pend_i.name = "g_int_rd_pend_i"
250 self.g_fp_rd_pend_i.name = "g_fp_rd_pend_i"
251 self.int_readable_o.name = "int_readable_o"
252 self.int_writable_o.name = "int_writable_o"
253 self.fp_writable_o.name = "fp_writable_o"
254
255
256 class STFnUnit(FnUnit):
257 """ number of dest selectors: 2. assumes len(int_regfile) == len(fp_regfile)
258 * wr_pend=False indicates to observe global fp write pending
259 * when rfile_sel_i == 0, int_wr_pend_o is set
260 * when rfile_sel_i == 1, fp_wr_pend_o is set
261 *
262 """
263 def __init__(self, wid, shadow_wid=0):
264 FnUnit.__init__(self, wid, shadow_wid, n_dests=2, wr_pend=True)
265 self.int_rd_pend_o = self.rd_pend_o # 1st int read-pending vector
266 self.int2_rd_pend_o = self.xx_pend_o[0] # 2nd int read-pending vector
267 self.fp_rd_pend_o = self.xx_pend_o[1] # 1x FP read-pending vector
268 # yes overwrite FnUnit base class g_wr_pend_i vector
269 self.g_int_wr_pend_i = self.g_wr_pend_i = self.g_xx_pend_i[0]
270 self.g_fp_wr_pend_i = self.g_xx_pend_i[1]
271 self.int_readable_o = self.readable_o
272 self.int_writable_o = self.writable_o[0]
273 self.fp_writable_o = self.writable_o[1]
274
275 self.int_rd_pend_o.name = "int_rd_pend_o"
276 self.int2_rd_pend_o.name = "int2_rd_pend_o"
277 self.fp_rd_pend_o.name = "fp_rd_pend_o"
278 self.g_int_wr_pend_i.name = "g_int_wr_pend_i"
279 self.g_fp_wr_pend_i.name = "g_fp_wr_pend_i"
280 self.int_readable_o.name = "int_readable_o"
281 self.int_writable_o.name = "int_writable_o"
282 self.fp_writable_o.name = "fp_writable_o"
283
284
285
286 def int_fn_unit_sim(dut):
287 yield dut.dest_i.eq(1)
288 yield dut.issue_i.eq(1)
289 yield
290 yield dut.issue_i.eq(0)
291 yield
292 yield dut.src1_i.eq(1)
293 yield dut.issue_i.eq(1)
294 yield
295 yield
296 yield
297 yield dut.issue_i.eq(0)
298 yield
299 yield dut.go_read_i.eq(1)
300 yield
301 yield dut.go_read_i.eq(0)
302 yield
303 yield dut.go_write_i.eq(1)
304 yield
305 yield dut.go_write_i.eq(0)
306 yield
307
308 def test_int_fn_unit():
309 dut = FnUnit(32, 2, 2)
310 vl = rtlil.convert(dut, ports=dut.ports())
311 with open("test_fn_unit.il", "w") as f:
312 f.write(vl)
313
314 dut = LDFnUnit(32, 2)
315 vl = rtlil.convert(dut, ports=dut.ports())
316 with open("test_ld_fn_unit.il", "w") as f:
317 f.write(vl)
318
319 dut = STFnUnit(32, 0)
320 vl = rtlil.convert(dut, ports=dut.ports())
321 with open("test_st_fn_unit.il", "w") as f:
322 f.write(vl)
323
324 run_simulation(dut, int_fn_unit_sim(dut), vcd_name='test_fn_unit.vcd')
325
326 if __name__ == '__main__':
327 test_int_fn_unit()