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
6 from scoreboard
.shadow_fn
import ShadowFn
9 class Shadow(Elaboratable
):
10 """ implements shadowing 11.5.1, p55
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)
17 * :shadow_wid: number of shadow/fail/good/go_die sets
20 * when shadow_wid = 0, recover and shadown are Consts (i.e. do nothing)
22 def __init__(self
, shadow_wid
=0):
23 self
.shadow_wid
= shadow_wid
27 self
.issue_i
= Signal(reset_less
=True)
28 self
.shadow_i
= Signal(shadow_wid
, reset_less
=True)
29 self
.s_fail_i
= Signal(shadow_wid
, reset_less
=True)
30 self
.s_good_i
= Signal(shadow_wid
, reset_less
=True)
32 self
.go_die_o
= Signal(reset_less
=True)
33 self
.shadown_o
= Signal(reset_less
=True)
35 # outputs when no shadowing needed
36 self
.shadown_o
= Const(1)
37 self
.go_die_o
= Const(0)
39 def elaborate(self
, platform
):
42 for i
in range(self
.shadow_wid
):
44 setattr(m
.submodules
, "shadow%d" % i
, sh
)
47 # shadow / recover (optional: shadow_wid > 0)
55 # get list of latch signals. really must be a better way to do this
58 shi_l
.append(l
.shadow_i
)
59 fail_l
.append(l
.s_fail_i
)
60 good_l
.append(l
.s_good_i
)
61 sho_l
.append(l
.shadow_o
)
62 rec_l
.append(l
.recover_o
)
63 m
.d
.comb
+= Cat(*i_l
).eq(Repl(self
.issue_i
, self
.shadow_wid
))
64 m
.d
.comb
+= Cat(*fail_l
).eq(self
.s_fail_i
)
65 m
.d
.comb
+= Cat(*good_l
).eq(self
.s_good_i
)
66 m
.d
.comb
+= Cat(*shi_l
).eq(self
.shadow_i
)
67 m
.d
.comb
+= self
.shadown_o
.eq(~
(Cat(*sho_l
).bool()))
68 m
.d
.comb
+= self
.go_die_o
.eq(Cat(*rec_l
).bool())
85 class ShadowMatrix(Elaboratable
):
86 """ Matrix of Shadow Functions. One per FU.
89 * :n_fus: register file width
90 * :shadow_wid: number of shadow/fail/good/go_die sets
94 * Shadow enable/fail/good are all connected to all Shadow Functions
97 * Output is an array of "shadow active" (schroedinger wires: neither
98 alive nor dead) and an array of "go die" signals, one per FU.
100 * the shadown must be connected to the Computation Unit's
101 write release request, preventing it (ANDing) from firing
102 (and thus preventing Writable. this by the way being the
103 whole point of having the Shadow Matrix...)
105 * go_die_o must be connected to *both* the Computation Unit's
106 src-operand and result-operand latch resets, causing both
109 * go_die_o also needs to be wired into the Dependency and Function
110 Unit Matrices by way of over-enabling (ORing) into Go_Read and
111 Go_Write, resetting every cell that is required to "die"
113 def __init__(self
, n_fus
, shadow_wid
=0):
115 self
.shadow_wid
= shadow_wid
118 self
.issue_i
= Signal(n_fus
, reset_less
=True)
119 self
.shadow_i
= Array(Signal(shadow_wid
, name
="sh_i", reset_less
=True) \
120 for f
in range(n_fus
))
121 self
.s_fail_i
= Array(Signal(shadow_wid
, name
="fl_i", reset_less
=True) \
122 for f
in range(n_fus
))
123 self
.s_good_i
= Array(Signal(shadow_wid
, name
="gd_i", reset_less
=True) \
124 for f
in range(n_fus
))
126 self
.go_die_o
= Signal(n_fus
, reset_less
=True)
127 self
.shadown_o
= Signal(n_fus
, reset_less
=True)
129 def elaborate(self
, platform
):
132 for i
in range(self
.n_fus
):
133 sh
= Shadow(self
.shadow_wid
)
134 setattr(m
.submodules
, "sh%d" % i
, sh
)
136 # connect shadow/fail/good to all shadows
137 m
.d
.comb
+= sh
.s_fail_i
.eq(self
.s_fail_i
[i
])
138 m
.d
.comb
+= sh
.s_good_i
.eq(self
.s_good_i
[i
])
139 # this one is the matrix (shadow enables)
140 m
.d
.comb
+= sh
.shadow_i
.eq(self
.shadow_i
[i
])
142 # connect all shadow outputs and issue input
147 issue_l
.append(l
.issue_i
)
148 sho_l
.append(l
.shadown_o
)
149 rec_l
.append(l
.go_die_o
)
150 m
.d
.comb
+= Cat(*issue_l
).eq(self
.issue_i
)
151 m
.d
.comb
+= self
.shadown_o
.eq(Cat(*sho_l
))
152 m
.d
.comb
+= self
.go_die_o
.eq(Cat(*rec_l
))
158 yield from self
.shadow_i
159 yield from self
.s_fail_i
160 yield from self
.s_good_i
168 class BranchSpeculationRecord(Elaboratable
):
169 """ A record of which function units will be cancelled and which
170 allowed to proceed, on a branch.
172 Whilst the input is a pair that says whether the instruction is
173 under the "success" branch shadow (good_i) or the "fail" shadow
174 (fail_i path), when the branch result is known, the "good" path
175 must be cancelled if "fail" occurred, and the "fail" path cancelled
178 therefore, use "good|~fail" and "fail|~good" respectively as
182 def __init__(self
, n_fus
):
185 # inputs: record *expected* status
186 self
.active_i
= Signal(reset_less
=True)
187 self
.good_i
= Signal(n_fus
, reset_less
=True)
188 self
.fail_i
= Signal(n_fus
, reset_less
=True)
190 # inputs: status of branch (when result was known)
191 self
.br_i
= Signal(reset_less
=True)
192 self
.br_ok_i
= Signal(reset_less
=True)
194 # outputs: true if the *expected* outcome matched the *actual* outcome
195 self
.match_f_o
= Signal(n_fus
, reset_less
=True)
196 self
.match_g_o
= Signal(n_fus
, reset_less
=True)
198 def elaborate(self
, platform
):
201 # registers to record *expected* status
202 good_r
= Signal(self
.n_fus
)
203 fail_r
= Signal(self
.n_fus
)
205 for i
in range(self
.n_fus
):
206 with m
.If(self
.active_i
):
207 m
.d
.sync
+= good_r
[i
].eq(good_r
[i
] | self
.good_i
[i
])
208 m
.d
.sync
+= fail_r
[i
].eq(fail_r
[i
] | self
.fail_i
[i
])
209 with m
.If(self
.br_i
):
210 # we expected fail, return OK that fail was EXPECTED... OR...
211 # we expected good, return OK that good was EXPECTED
212 good
= Signal(reset_less
=True)
213 fail
= Signal(reset_less
=True)
214 with m
.If(self
.br_ok_i
):
215 m
.d
.comb
+= good
.eq(good_r
[i
])
216 m
.d
.comb
+= fail
.eq(fail_r
[i
])
218 m
.d
.comb
+= good
.eq(~good_r
[i
])
219 m
.d
.comb
+= fail
.eq(~fail_r
[i
])
220 # ... but only set these where a good or fail *is* expected...
221 with m
.If(good_r
[i
]):
222 m
.d
.comb
+= self
.match_g_o
[i
].eq(self
.br_ok_i
)
223 m
.d
.comb
+= self
.match_f_o
[i
].eq(~self
.br_ok_i
)
224 with m
.If(fail_r
[i
]):
225 m
.d
.comb
+= self
.match_f_o
[i
].eq(~self
.br_ok_i
)
226 m
.d
.comb
+= self
.match_g_o
[i
].eq(self
.br_ok_i
)
227 m
.d
.sync
+= good_r
[i
].eq(0) # might be set if issue set as well
228 m
.d
.sync
+= fail_r
[i
].eq(0) # might be set if issue set as well
247 class WaWGrid(Elaboratable
):
248 """ An NxM grid-selector which raises a 2D bit selected by N and M
251 def __init__(self
, n_fus
, shadow_wid
):
253 self
.shadow_wid
= shadow_wid
255 self
.shadow_i
= Signal(shadow_wid
, reset_less
=True)
256 self
.fu_i
= Signal(n_fus
, reset_less
=True)
258 self
.waw_o
= Array(Signal(shadow_wid
, name
="waw_o", reset_less
=True) \
259 for f
in range(n_fus
))
261 def elaborate(self
, platform
):
263 for i
in range(self
.n_fus
):
264 v
= Repl(self
.fu_i
[i
], self
.shadow_wid
)
265 m
.d
.comb
+= self
.waw_o
[i
].eq(v
& self
.shadow_i
)
270 yield dut
.dest_i
.eq(1)
271 yield dut
.issue_i
.eq(1)
273 yield dut
.issue_i
.eq(0)
275 yield dut
.src1_i
.eq(1)
276 yield dut
.issue_i
.eq(1)
280 yield dut
.issue_i
.eq(0)
282 yield dut
.go_rd_i
.eq(1)
284 yield dut
.go_rd_i
.eq(0)
286 yield dut
.go_wr_i
.eq(1)
288 yield dut
.go_wr_i
.eq(0)
292 dut
= ShadowMatrix(4, 2)
293 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
294 with
open("test_shadow.il", "w") as f
:
297 dut
= BranchSpeculationRecord(4)
298 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
299 with
open("test_branchspecrecord.il", "w") as f
:
302 run_simulation(dut
, shadow_sim(dut
), vcd_name
='test_shadow.vcd')
304 if __name__
== '__main__':