034f34fc4ffc558f562fe4b29c4ef9b1a504166c
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, syncreset
=False):
23 self
.shadow_wid
= shadow_wid
24 self
.syncreset
= syncreset
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)
34 self
.go_die_o
= Signal(reset_less
=True)
35 self
.shadown_o
= Signal(reset_less
=True)
37 # outputs when no shadowing needed
38 self
.shadown_o
= Const(1)
39 self
.go_die_o
= Const(0)
41 def elaborate(self
, platform
):
44 for i
in range(self
.shadow_wid
):
45 sh
= ShadowFn(self
.syncreset
)
46 setattr(m
.submodules
, "shadow%d" % i
, sh
)
49 # shadow / recover (optional: shadow_wid > 0)
58 # get list of latch signals. really must be a better way to do this
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())
91 class ShadowMatrix(Elaboratable
):
92 """ Matrix of Shadow Functions. One per FU.
95 * :n_fus: register file width
96 * :shadow_wid: number of shadow/fail/good/go_die sets
100 * Shadow enable/fail/good are all connected to all Shadow Functions
101 (incoming at the top)
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.
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...)
111 * go_die_o must be connected to *both* the Computation Unit's
112 src-operand and result-operand latch resets, causing both
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"
119 def __init__(self
, n_fus
, shadow_wid
=0, syncreset
=False):
120 self
.syncreset
= syncreset
122 self
.shadow_wid
= shadow_wid
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
))
134 self
.go_die_o
= Signal(n_fus
, reset_less
=True)
135 self
.shadown_o
= Signal(n_fus
, reset_less
=True)
137 def elaborate(self
, platform
):
140 for i
in range(self
.n_fus
):
141 sh
= Shadow(self
.shadow_wid
, self
.syncreset
)
142 setattr(m
.submodules
, "sh%d" % i
, 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
])
150 # connect all shadow outputs and issue input
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
))
170 yield from self
.shadow_i
171 yield from self
.s_fail_i
172 yield from self
.s_good_i
180 class BranchSpeculationRecord(Elaboratable
):
181 """ A record of which function units will be cancelled and which
182 allowed to proceed, on a branch.
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
190 therefore, use "good|~fail" and "fail|~good" respectively as
194 def __init__(self
, n_fus
):
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)
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)
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)
210 def elaborate(self
, platform
):
213 # registers to record *expected* status
214 good_r
= Signal(self
.n_fus
)
215 fail_r
= Signal(self
.n_fus
)
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
250 class WaWGrid(Elaboratable
):
251 """ An NxM grid-selector which raises a 2D bit selected by N and M
254 def __init__(self
, n_fus
, shadow_wid
):
256 self
.shadow_wid
= shadow_wid
258 self
.shadow_i
= Signal(shadow_wid
, reset_less
=True)
259 self
.fu_i
= Signal(n_fus
, reset_less
=True)
261 self
.waw_o
= Array(Signal(shadow_wid
, name
="waw_o", reset_less
=True) \
262 for f
in range(n_fus
))
264 def elaborate(self
, platform
):
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
)
273 yield dut
.dest_i
.eq(1)
274 yield dut
.issue_i
.eq(1)
276 yield dut
.issue_i
.eq(0)
278 yield dut
.src1_i
.eq(1)
279 yield dut
.issue_i
.eq(1)
283 yield dut
.issue_i
.eq(0)
285 yield dut
.go_rd_i
.eq(1)
287 yield dut
.go_rd_i
.eq(0)
289 yield dut
.go_wr_i
.eq(1)
291 yield dut
.go_wr_i
.eq(0)
295 dut
= ShadowMatrix(4, 2)
296 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
297 with
open("test_shadow.il", "w") as f
:
300 dut
= BranchSpeculationRecord(4)
301 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
302 with
open("test_branchspecrecord.il", "w") as f
:
305 run_simulation(dut
, shadow_sim(dut
), vcd_name
='test_shadow.vcd')
307 if __name__
== '__main__':