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