separate out go_die from go_rd/go_wr to stop reg read/write triggering
[soc.git] / src / scoreboard / dependence_cell.py
1 from nmigen.compat.sim import run_simulation
2 from nmigen.cli import verilog, rtlil
3 from nmigen import Module, Signal, Elaboratable, Array, Cat
4 from nmutil.latch import SRLatch
5
6
7 class DepCell(Elaboratable):
8 """ implements 11.4.7 mitch alsup dependence cell, p27
9 adjusted to be clock-sync'd on rising edge only.
10 mitch design (as does 6600) requires alternating rising/falling clock
11
12 * SET mode: issue_i HI, go_i LO, reg_i HI - register is captured
13 - FWD is DISABLED (~issue_i)
14 - RSEL DISABLED
15 * QRY mode: issue_i LO, go_i LO, haz_i HI - FWD is ASSERTED
16 reg_i HI - ignored
17 * GO mode : issue_i LO, go_i HI - RSEL is ASSERTED
18 haz_i HI - FWD still can be ASSERTED
19
20 FWD assertion (hazard protection) therefore still occurs in both
21 Query and Go Modes, for this cycle, due to the cq register
22
23 GO mode works for one cycle, again due to the cq register capturing
24 the latch output. Without the cq register, the SR Latch (which is
25 asynchronous) would be reset at the exact moment that GO was requested,
26 and the RSEL would be garbage.
27 """
28 def __init__(self):
29 # inputs
30 self.reg_i = Signal(reset_less=True) # reg bit in (top)
31 self.issue_i = Signal(reset_less=True) # Issue in (top)
32 self.hazard_i = Signal(reset_less=True) # to check hazard
33 self.go_i = Signal(reset_less=True) # Go read/write in (left)
34 self.die_i = Signal(reset_less=True) # Die in (left)
35 self.q_o = Signal(reset_less=True) # Latch out (register active)
36
37 # for Register File Select Lines (vertical)
38 self.rsel_o = Signal(reset_less=True) # reg sel (bottom)
39 # for Function Unit "forward progress" (horizontal)
40 self.fwd_o = Signal(reset_less=True) # FU forard progress (right)
41
42 def elaborate(self, platform):
43 m = Module()
44 m.submodules.l = l = SRLatch(sync=False) # async latch
45
46 # reset on go HI, set on dest and issue
47 m.d.comb += l.s.eq(self.issue_i & self.reg_i)
48 m.d.comb += l.r.eq(self.go_i | self.die_i)
49
50 # Function Unit "Forward Progress".
51 m.d.comb += self.fwd_o.eq((l.q) & self.hazard_i) # & ~self.issue_i)
52
53 # Register Select. Activated on go read/write and *current* latch set
54 m.d.comb += self.q_o.eq(l.qlq)
55 m.d.comb += self.rsel_o.eq(l.qlq & self.go_i)
56
57 return m
58
59 def __iter__(self):
60 yield self.reg_i
61 yield self.hazard_i
62 yield self.issue_i
63 yield self.go_i
64 yield self.die_i
65 yield self.q_o
66 yield self.rsel_o
67 yield self.fwd_o
68
69 def ports(self):
70 return list(self)
71
72
73 class DependenceCell(Elaboratable):
74 """ implements 11.4.7 mitch alsup dependence cell, p27
75 """
76 def __init__(self):
77 # inputs
78 self.dest_i = Signal(reset_less=True) # Dest in (top)
79 self.src1_i = Signal(reset_less=True) # oper1 in (top)
80 self.src2_i = Signal(reset_less=True) # oper2 in (top)
81 self.issue_i = Signal(reset_less=True) # Issue in (top)
82
83 self.rd_pend_i = Signal(reset_less=True) # Read pending in (top)
84 self.wr_pend_i = Signal(reset_less=True) # Write pending in (top)
85 self.rd_rsel_o = Signal(reset_less=True) # Read pending out (bottom)
86 self.wr_rsel_o = Signal(reset_less=True) # Write pending out (bottom)
87
88 self.go_wr_i = Signal(reset_less=True) # Go Write in (left)
89 self.go_rd_i = Signal(reset_less=True) # Go Read in (left)
90 self.go_die_i = Signal(reset_less=True) # Go Die in (left)
91
92 # for Register File Select Lines (vertical)
93 self.dest_rsel_o = Signal(reset_less=True) # dest reg sel (bottom)
94 self.src1_rsel_o = Signal(reset_less=True) # src1 reg sel (bottom)
95 self.src2_rsel_o = Signal(reset_less=True) # src2 reg sel (bottom)
96
97 # for Function Unit "forward progress" (horizontal)
98 self.dest_fwd_o = Signal(reset_less=True) # dest FU fw (right)
99 self.src1_fwd_o = Signal(reset_less=True) # src1 FU fw (right)
100 self.src2_fwd_o = Signal(reset_less=True) # src2 FU fw (right)
101
102 def elaborate(self, platform):
103 m = Module()
104 m.submodules.dest_c = dest_c = DepCell()
105 m.submodules.src1_c = src1_c = DepCell()
106 m.submodules.src2_c = src2_c = DepCell()
107
108 # connect issue and die
109 for c in [dest_c, src1_c, src2_c]:
110 m.d.comb += c.issue_i.eq(self.issue_i)
111 m.d.comb += c.die_i.eq(self.go_die_i)
112
113 # connect go_rd / go_wr (dest->wr, src->rd)
114 m.d.comb += dest_c.go_i.eq(self.go_wr_i)
115 m.d.comb += src1_c.go_i.eq(self.go_rd_i)
116 m.d.comb += src2_c.go_i.eq(self.go_rd_i)
117
118 # connect input reg bit (unary)
119 for c, reg in [(dest_c, self.dest_i),
120 (src1_c, self.src1_i),
121 (src2_c, self.src2_i)]:
122 m.d.comb += c.reg_i.eq(reg)
123
124 # wark-wark: yes, writing to the same reg you are reading is *NOT*
125 # a write-after-read hazard.
126 selfhazard = Signal(reset_less=False)
127 m.d.comb += selfhazard.eq((self.dest_i & self.src1_i) |
128 (self.dest_i & self.src2_i))
129
130 # connect up hazard checks: read-after-write and write-after-read
131 m.d.comb += dest_c.hazard_i.eq(self.rd_pend_i) # read-after-write
132 m.d.comb += src1_c.hazard_i.eq(self.wr_pend_i) # write-after-read
133 m.d.comb += src2_c.hazard_i.eq(self.wr_pend_i) # write-after-read
134
135 # connect fwd / reg-sel outputs
136 for c, fwd, rsel in [(dest_c, self.dest_fwd_o, self.dest_rsel_o),
137 (src1_c, self.src1_fwd_o, self.src1_rsel_o),
138 (src2_c, self.src2_fwd_o, self.src2_rsel_o)]:
139 m.d.comb += fwd.eq(c.fwd_o)
140 m.d.comb += rsel.eq(c.rsel_o)
141
142 # to be accumulated to indicate if register is in use (globally)
143 # after ORing, is fed back in to rd_pend_i / wr_pend_i
144 m.d.comb += self.rd_rsel_o.eq(src1_c.q_o | src2_c.q_o)
145 #with m.If(~selfhazard):
146 m.d.comb += self.wr_rsel_o.eq(dest_c.q_o)
147
148 return m
149
150 def __iter__(self):
151 yield self.dest_i
152 yield self.src1_i
153 yield self.src2_i
154 yield self.rd_pend_i
155 yield self.wr_pend_i
156 yield self.issue_i
157 yield self.go_wr_i
158 yield self.go_rd_i
159 yield self.go_die_i
160 yield self.dest_rsel_o
161 yield self.src1_rsel_o
162 yield self.src2_rsel_o
163 yield self.dest_fwd_o
164 yield self.src1_fwd_o
165 yield self.src2_fwd_o
166
167 def ports(self):
168 return list(self)
169
170
171 class DependencyRow(Elaboratable):
172 def __init__(self, n_reg_col):
173 self.n_reg_col = n_reg_col
174
175 # ----
176 # fields all match DependencyCell precisely
177
178 self.dest_i = Signal(n_reg_col, reset_less=True)
179 self.src1_i = Signal(n_reg_col, reset_less=True)
180 self.src2_i = Signal(n_reg_col, reset_less=True)
181
182 self.rd_pend_i = Signal(n_reg_col, reset_less=True)
183 self.wr_pend_i = Signal(n_reg_col, reset_less=True)
184
185 self.rd_rsel_o = Signal(n_reg_col, reset_less=True)
186 self.wr_rsel_o = Signal(n_reg_col, reset_less=True)
187
188 self.issue_i = Signal(reset_less=True)
189 self.go_wr_i = Signal(reset_less=True)
190 self.go_rd_i = Signal(reset_less=True)
191 self.go_die_i = Signal(reset_less=True)
192
193 self.dest_rsel_o = Signal(n_reg_col, reset_less=True)
194 self.src1_rsel_o = Signal(n_reg_col, reset_less=True)
195 self.src2_rsel_o = Signal(n_reg_col, reset_less=True)
196
197 self.dest_fwd_o = Signal(n_reg_col, reset_less=True)
198 self.src1_fwd_o = Signal(n_reg_col, reset_less=True)
199 self.src2_fwd_o = Signal(n_reg_col, reset_less=True)
200
201 def elaborate(self, platform):
202 m = Module()
203 rcell = Array(DependenceCell() for f in range(self.n_reg_col))
204 for rn in range(self.n_reg_col):
205 setattr(m.submodules, "dm_r%d" % rn, rcell[rn])
206
207 # ---
208 # connect Dep dest/src to module dest/src
209 # ---
210 rd_pend_i = []
211 wr_pend_i = []
212 rd_rsel_o = []
213 wr_rsel_o = []
214 dest_i = []
215 src1_i = []
216 src2_i = []
217 for rn in range(self.n_reg_col):
218 dc = rcell[rn]
219 # accumulate cell inputs dest/src1/src2
220 rd_pend_i.append(dc.rd_pend_i)
221 wr_pend_i.append(dc.wr_pend_i)
222 rd_rsel_o.append(dc.rd_rsel_o)
223 wr_rsel_o.append(dc.wr_rsel_o)
224 dest_i.append(dc.dest_i)
225 src1_i.append(dc.src1_i)
226 src2_i.append(dc.src2_i)
227 # wire up inputs from module to row cell inputs (Cat is gooood)
228 m.d.comb += [Cat(*dest_i).eq(self.dest_i),
229 Cat(*src1_i).eq(self.src1_i),
230 Cat(*src2_i).eq(self.src2_i),
231 Cat(*rd_pend_i).eq(self.rd_pend_i),
232 Cat(*wr_pend_i).eq(self.wr_pend_i),
233 self.rd_rsel_o.eq(Cat(*rd_rsel_o)),
234 self.wr_rsel_o.eq(Cat(*wr_rsel_o)),
235 ]
236
237 # ---
238 # connect Dep die/issue_i/go_rd_i/go_wr_i to module issue_i/go_rd/go_wr
239 # ---
240 for rn in range(self.n_reg_col):
241 dc = rcell[rn]
242 m.d.comb += [dc.go_rd_i.eq(self.go_rd_i),
243 dc.go_wr_i.eq(self.go_wr_i),
244 dc.issue_i.eq(self.issue_i),
245 dc.go_die_i.eq(self.go_die_i),
246 ]
247
248 # ---
249 # connect Function Unit vector
250 # ---
251 dest_fwd_o = []
252 src1_fwd_o = []
253 src2_fwd_o = []
254 for rn in range(self.n_reg_col):
255 dc = rcell[rn]
256 # accumulate cell fwd outputs for dest/src1/src2
257 dest_fwd_o.append(dc.dest_fwd_o)
258 src1_fwd_o.append(dc.src1_fwd_o)
259 src2_fwd_o.append(dc.src2_fwd_o)
260 # connect cell fwd outputs to FU Vector Out [Cat is gooood]
261 m.d.comb += [self.dest_fwd_o.eq(Cat(*dest_fwd_o)),
262 self.src1_fwd_o.eq(Cat(*src1_fwd_o)),
263 self.src2_fwd_o.eq(Cat(*src2_fwd_o))
264 ]
265
266 # ---
267 # connect Reg Selection vector
268 # ---
269 dest_rsel_o = []
270 src1_rsel_o = []
271 src2_rsel_o = []
272 for rn in range(self.n_reg_col):
273 dc = rcell[rn]
274 # accumulate cell reg-select outputs dest/src1/src2
275 dest_rsel_o.append(dc.dest_rsel_o)
276 src1_rsel_o.append(dc.src1_rsel_o)
277 src2_rsel_o.append(dc.src2_rsel_o)
278 # connect cell reg-select outputs to Reg Vector Out
279 m.d.comb += self.dest_rsel_o.eq(Cat(*dest_rsel_o))
280 m.d.comb += self.src1_rsel_o.eq(Cat(*src1_rsel_o))
281 m.d.comb += self.src2_rsel_o.eq(Cat(*src2_rsel_o))
282
283 return m
284
285 def __iter__(self):
286 yield self.dest_i
287 yield self.src1_i
288 yield self.src2_i
289 yield self.issue_i
290 yield self.go_wr_i
291 yield self.go_rd_i
292 yield self.go_die_i
293 yield self.dest_rsel_o
294 yield self.src1_rsel_o
295 yield self.src2_rsel_o
296 yield self.dest_fwd_o
297 yield self.src1_fwd_o
298 yield self.src2_fwd_o
299
300 def ports(self):
301 return list(self)
302
303
304 def dcell_sim(dut):
305 yield dut.dest_i.eq(1)
306 yield dut.issue_i.eq(1)
307 yield
308 yield dut.issue_i.eq(0)
309 yield
310 yield dut.src1_i.eq(1)
311 yield dut.issue_i.eq(1)
312 yield
313 yield
314 yield
315 yield dut.issue_i.eq(0)
316 yield
317 yield dut.go_rd_i.eq(1)
318 yield
319 yield dut.go_rd_i.eq(0)
320 yield
321 yield dut.go_wr_i.eq(1)
322 yield
323 yield dut.go_wr_i.eq(0)
324 yield
325
326 def test_dcell():
327 dut = DependencyRow(4)
328 vl = rtlil.convert(dut, ports=dut.ports())
329 with open("test_drow.il", "w") as f:
330 f.write(vl)
331
332 dut = DependenceCell()
333 vl = rtlil.convert(dut, ports=dut.ports())
334 with open("test_dcell.il", "w") as f:
335 f.write(vl)
336
337 run_simulation(dut, dcell_sim(dut), vcd_name='test_dcell.vcd')
338
339 if __name__ == '__main__':
340 test_dcell()