034f34fc4ffc558f562fe4b29c4ef9b1a504166c
[soc.git] / src / scoreboard / shadow.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, Repl
4 from nmigen.lib.coding import Decoder
5
6 from scoreboard.shadow_fn import ShadowFn
7
8
9 class Shadow(Elaboratable):
10 """ implements shadowing 11.5.1, p55
11
12 shadowing can be used for branches as well as exceptions (interrupts),
13 load/store hold (exceptions again), and vector-element predication
14 (once the predicate is known, which it may not be at instruction issue)
15
16 Inputs
17 * :shadow_wid: number of shadow/fail/good/go_die sets
18
19 notes:
20 * when shadow_wid = 0, recover and shadown are Consts (i.e. do nothing)
21 """
22 def __init__(self, shadow_wid=0, syncreset=False):
23 self.shadow_wid = shadow_wid
24 self.syncreset = syncreset
25
26 if shadow_wid:
27 # inputs
28 self.issue_i = Signal(reset_less=True)
29 self.reset_i = Signal(reset_less=True)
30 self.shadow_i = Signal(shadow_wid, reset_less=True)
31 self.s_fail_i = Signal(shadow_wid, reset_less=True)
32 self.s_good_i = Signal(shadow_wid, reset_less=True)
33 # outputs
34 self.go_die_o = Signal(reset_less=True)
35 self.shadown_o = Signal(reset_less=True)
36 else:
37 # outputs when no shadowing needed
38 self.shadown_o = Const(1)
39 self.go_die_o = Const(0)
40
41 def elaborate(self, platform):
42 m = Module()
43 s_latches = []
44 for i in range(self.shadow_wid):
45 sh = ShadowFn(self.syncreset)
46 setattr(m.submodules, "shadow%d" % i, sh)
47 s_latches.append(sh)
48
49 # shadow / recover (optional: shadow_wid > 0)
50 if self.shadow_wid:
51 i_l = []
52 d_l = []
53 fail_l = []
54 good_l = []
55 shi_l = []
56 sho_l = []
57 rec_l = []
58 # get list of latch signals. really must be a better way to do this
59 for l in s_latches:
60 i_l.append(l.issue_i)
61 d_l.append(l.reset_i)
62 shi_l.append(l.shadow_i)
63 fail_l.append(l.s_fail_i)
64 good_l.append(l.s_good_i)
65 sho_l.append(l.shadow_o)
66 rec_l.append(l.recover_o)
67 m.d.comb += Cat(*i_l).eq(Repl(self.issue_i, self.shadow_wid))
68 m.d.comb += Cat(*d_l).eq(Repl(self.reset_i, self.shadow_wid))
69 m.d.comb += Cat(*fail_l).eq(self.s_fail_i)
70 m.d.comb += Cat(*good_l).eq(self.s_good_i)
71 m.d.comb += Cat(*shi_l).eq(self.shadow_i)
72 m.d.comb += self.shadown_o.eq(~(Cat(*sho_l).bool()))
73 m.d.comb += self.go_die_o.eq(Cat(*rec_l).bool())
74
75 return m
76
77 def __iter__(self):
78 if self.shadow_wid:
79 yield self.reset_i
80 yield self.issue_i
81 yield self.shadow_i
82 yield self.s_fail_i
83 yield self.s_good_i
84 yield self.go_die_o
85 yield self.shadown_o
86
87 def ports(self):
88 return list(self)
89
90
91 class ShadowMatrix(Elaboratable):
92 """ Matrix of Shadow Functions. One per FU.
93
94 Inputs
95 * :n_fus: register file width
96 * :shadow_wid: number of shadow/fail/good/go_die sets
97
98 Notes:
99
100 * Shadow enable/fail/good are all connected to all Shadow Functions
101 (incoming at the top)
102
103 * Output is an array of "shadow active" (schroedinger wires: neither
104 alive nor dead) and an array of "go die" signals, one per FU.
105
106 * the shadown must be connected to the Computation Unit's
107 write release request, preventing it (ANDing) from firing
108 (and thus preventing Writable. this by the way being the
109 whole point of having the Shadow Matrix...)
110
111 * go_die_o must be connected to *both* the Computation Unit's
112 src-operand and result-operand latch resets, causing both
113 of them to reset.
114
115 * go_die_o also needs to be wired into the Dependency and Function
116 Unit Matrices by way of over-enabling (ORing) into Go_Read and
117 Go_Write, resetting every cell that is required to "die"
118 """
119 def __init__(self, n_fus, shadow_wid=0, syncreset=False):
120 self.syncreset = syncreset
121 self.n_fus = n_fus
122 self.shadow_wid = shadow_wid
123
124 # inputs
125 self.issue_i = Signal(n_fus, reset_less=True)
126 self.reset_i = Signal(n_fus, reset_less=True)
127 self.shadow_i = Array(Signal(shadow_wid, name="sh_i", reset_less=True) \
128 for f in range(n_fus))
129 self.s_fail_i = Array(Signal(shadow_wid, name="fl_i", reset_less=True) \
130 for f in range(n_fus))
131 self.s_good_i = Array(Signal(shadow_wid, name="gd_i", reset_less=True) \
132 for f in range(n_fus))
133 # outputs
134 self.go_die_o = Signal(n_fus, reset_less=True)
135 self.shadown_o = Signal(n_fus, reset_less=True)
136
137 def elaborate(self, platform):
138 m = Module()
139 shadows = []
140 for i in range(self.n_fus):
141 sh = Shadow(self.shadow_wid, self.syncreset)
142 setattr(m.submodules, "sh%d" % i, sh)
143 shadows.append(sh)
144 # connect shadow/fail/good to all shadows
145 m.d.comb += sh.s_fail_i.eq(self.s_fail_i[i])
146 m.d.comb += sh.s_good_i.eq(self.s_good_i[i])
147 # this one is the matrix (shadow enables)
148 m.d.comb += sh.shadow_i.eq(self.shadow_i[i])
149
150 # connect all shadow outputs and issue input
151 issue_l = []
152 reset_l = []
153 sho_l = []
154 rec_l = []
155 for l in shadows:
156 issue_l.append(l.issue_i)
157 reset_l.append(l.reset_i)
158 sho_l.append(l.shadown_o)
159 rec_l.append(l.go_die_o)
160 m.d.comb += Cat(*issue_l).eq(self.issue_i)
161 m.d.comb += Cat(*reset_l).eq(self.reset_i)
162 m.d.comb += self.shadown_o.eq(Cat(*sho_l))
163 m.d.comb += self.go_die_o.eq(Cat(*rec_l))
164
165 return m
166
167 def __iter__(self):
168 yield self.issue_i
169 yield self.reset_i
170 yield from self.shadow_i
171 yield from self.s_fail_i
172 yield from self.s_good_i
173 yield self.go_die_o
174 yield self.shadown_o
175
176 def ports(self):
177 return list(self)
178
179
180 class BranchSpeculationRecord(Elaboratable):
181 """ A record of which function units will be cancelled and which
182 allowed to proceed, on a branch.
183
184 Whilst the input is a pair that says whether the instruction is
185 under the "success" branch shadow (good_i) or the "fail" shadow
186 (fail_i path), when the branch result is known, the "good" path
187 must be cancelled if "fail" occurred, and the "fail" path cancelled
188 if "good" occurred.
189
190 therefore, use "good|~fail" and "fail|~good" respectively as
191 output.
192 """
193
194 def __init__(self, n_fus):
195 self.n_fus = n_fus
196
197 # inputs: record *expected* status
198 self.active_i = Signal(reset_less=True)
199 self.good_i = Signal(n_fus, reset_less=True)
200 self.fail_i = Signal(n_fus, reset_less=True)
201
202 # inputs: status of branch (when result was known)
203 self.br_i = Signal(reset_less=True)
204 self.br_ok_i = Signal(reset_less=True)
205
206 # outputs: true if the *expected* outcome matched the *actual* outcome
207 self.match_f_o = Signal(n_fus, reset_less=True)
208 self.match_g_o = Signal(n_fus, reset_less=True)
209
210 def elaborate(self, platform):
211 m = Module()
212
213 # registers to record *expected* status
214 good_r = Signal(self.n_fus)
215 fail_r = Signal(self.n_fus)
216
217 for i in range(self.n_fus):
218 with m.If(self.active_i):
219 m.d.sync += good_r[i].eq(good_r[i] | self.good_i[i])
220 m.d.sync += fail_r[i].eq(fail_r[i] | self.fail_i[i])
221 with m.If(self.br_i):
222 with m.If(good_r[i]):
223 # we expected good, return OK that good was EXPECTED
224 m.d.comb += self.match_g_o[i].eq(self.br_ok_i)
225 m.d.comb += self.match_f_o[i].eq(~self.br_ok_i)
226 with m.If(fail_r[i]):
227 # we expected fail, return OK that fail was EXPECTED
228 m.d.comb += self.match_g_o[i].eq(~self.br_ok_i)
229 m.d.comb += self.match_f_o[i].eq(self.br_ok_i)
230 m.d.sync += good_r[i].eq(0) # might be set if issue set as well
231 m.d.sync += fail_r[i].eq(0) # might be set if issue set as well
232
233 return m
234
235 def __iter__(self):
236 yield self.active_i
237 yield self.good_i
238 yield self.fail_i
239 yield self.br_i
240 yield self.br_good_i
241 yield self.br_fail_i
242 yield self.good_o
243 yield self.fail_o
244
245 def ports(self):
246 return list(self)
247
248
249
250 class WaWGrid(Elaboratable):
251 """ An NxM grid-selector which raises a 2D bit selected by N and M
252 """
253
254 def __init__(self, n_fus, shadow_wid):
255 self.n_fus = n_fus
256 self.shadow_wid = shadow_wid
257
258 self.shadow_i = Signal(shadow_wid, reset_less=True)
259 self.fu_i = Signal(n_fus, reset_less=True)
260
261 self.waw_o = Array(Signal(shadow_wid, name="waw_o", reset_less=True) \
262 for f in range(n_fus))
263
264 def elaborate(self, platform):
265 m = Module()
266 for i in range(self.n_fus):
267 v = Repl(self.fu_i[i], self.shadow_wid)
268 m.d.comb += self.waw_o[i].eq(v & self.shadow_i)
269 return m
270
271
272 def shadow_sim(dut):
273 yield dut.dest_i.eq(1)
274 yield dut.issue_i.eq(1)
275 yield
276 yield dut.issue_i.eq(0)
277 yield
278 yield dut.src1_i.eq(1)
279 yield dut.issue_i.eq(1)
280 yield
281 yield
282 yield
283 yield dut.issue_i.eq(0)
284 yield
285 yield dut.go_rd_i.eq(1)
286 yield
287 yield dut.go_rd_i.eq(0)
288 yield
289 yield dut.go_wr_i.eq(1)
290 yield
291 yield dut.go_wr_i.eq(0)
292 yield
293
294 def test_shadow():
295 dut = ShadowMatrix(4, 2)
296 vl = rtlil.convert(dut, ports=dut.ports())
297 with open("test_shadow.il", "w") as f:
298 f.write(vl)
299
300 dut = BranchSpeculationRecord(4)
301 vl = rtlil.convert(dut, ports=dut.ports())
302 with open("test_branchspecrecord.il", "w") as f:
303 f.write(vl)
304
305 run_simulation(dut, shadow_sim(dut), vcd_name='test_shadow.vcd')
306
307 if __name__ == '__main__':
308 test_shadow()