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
= Signal(shadow_wid
, reset_less
=True)
122 self
.s_good_i
= Signal(shadow_wid
, reset_less
=True)
125 self
.go_die_o
= Signal(n_fus
, reset_less
=True)
126 self
.shadown_o
= Signal(n_fus
, reset_less
=True)
128 def elaborate(self
, platform
):
131 for i
in range(self
.n_fus
):
132 sh
= Shadow(self
.shadow_wid
)
133 setattr(m
.submodules
, "sh%d" % i
, sh
)
135 # connect shadow/fail/good to all shadows
136 m
.d
.comb
+= sh
.s_fail_i
.eq(self
.s_fail_i
)
137 m
.d
.comb
+= sh
.s_good_i
.eq(self
.s_good_i
)
138 # this one is the matrix (shadow enables)
139 m
.d
.comb
+= sh
.shadow_i
.eq(self
.shadow_i
[i
])
141 # connect all shadow outputs and issue input
146 issue_l
.append(l
.issue_i
)
147 sho_l
.append(l
.shadown_o
)
148 rec_l
.append(l
.go_die_o
)
149 m
.d
.comb
+= Cat(*issue_l
).eq(self
.issue_i
)
150 m
.d
.comb
+= self
.shadown_o
.eq(Cat(*sho_l
))
151 m
.d
.comb
+= self
.go_die_o
.eq(Cat(*rec_l
))
157 yield from self
.shadow_i
167 class BranchSpeculationRecord(Elaboratable
):
168 """ A record of which function units will be cancelled and which
169 allowed to proceed, on a branch.
171 Whilst the input is a pair that says whether the instruction is
172 under the "success" branch shadow (good_i) or the "fail" shadow
173 (fail_i path), when the branch result is known, the "good" path
174 must be cancelled if "fail" occurred, and the "fail" path cancelled
177 therefore, use "good|~fail" and "fail|~good" respectively as
181 def __init__(self
, n_fus
):
184 # inputs: record *expected* status
185 self
.issue_i
= Signal(reset_less
=True)
186 self
.good_i
= Signal(reset_less
=True)
187 self
.fail_i
= Signal(reset_less
=True)
189 # inputs: status of branch (when result was known)
190 self
.br_i
= Signal(reset_less
=True)
191 self
.br_good_i
= Signal(reset_less
=True)
192 self
.br_fail_i
= Signal(reset_less
=True)
194 # outputs: true if the *expected* outcome matched the *actual* outcome
195 self
.matched_o
= Signal(reset_less
=True)
197 def elaborate(self
, platform
):
200 # registers to record *expected* status
204 with m
.If(self
.br_i
):
205 # we expected fail, return OK that fail was EXPECTED... OR...
206 # we expected good, return OK that good was EXPECTED
207 success
= Signal(reset_less
=True)
208 m
.d
.comb
+= success
.eq((good_r
& self
.br_good_i
) | \
209 (fail_r
& self
.br_fail_i
) )
210 # ... but only set these where a good or fail *is* expected...
211 with m
.If(good_r | fail_r
):
212 m
.d
.comb
+= self
.matched_o
.eq(success
)
213 m
.d
.sync
+= good_r
.eq(0) # might be set if issue set as well
214 m
.d
.sync
+= fail_r
.eq(0) # might be set if issue set as well
215 with m
.If(self
.issue_i
):
216 m
.d
.sync
+= good_r
.eq(good_r | self
.good_i
)
217 m
.d
.sync
+= fail_r
.eq(fail_r | self
.fail_i
)
236 class WaWGrid(Elaboratable
):
237 """ An NxM grid-selector which raises a 2D bit selected by N and M
240 def __init__(self
, n_fus
, shadow_wid
):
242 self
.shadow_wid
= shadow_wid
244 self
.shadow_i
= Signal(shadow_wid
, reset_less
=True)
245 self
.fu_i
= Signal(n_fus
, reset_less
=True)
247 self
.waw_o
= Array(Signal(shadow_wid
, name
="waw_o", reset_less
=True) \
248 for f
in range(n_fus
))
250 def elaborate(self
, platform
):
252 for i
in range(self
.n_fus
):
253 v
= Repl(self
.fu_i
[i
], self
.shadow_wid
)
254 m
.d
.comb
+= self
.waw_o
[i
].eq(v
& self
.shadow_i
)
259 yield dut
.dest_i
.eq(1)
260 yield dut
.issue_i
.eq(1)
262 yield dut
.issue_i
.eq(0)
264 yield dut
.src1_i
.eq(1)
265 yield dut
.issue_i
.eq(1)
269 yield dut
.issue_i
.eq(0)
271 yield dut
.go_rd_i
.eq(1)
273 yield dut
.go_rd_i
.eq(0)
275 yield dut
.go_wr_i
.eq(1)
277 yield dut
.go_wr_i
.eq(0)
281 dut
= ShadowMatrix(4, 2)
282 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
283 with
open("test_shadow.il", "w") as f
:
286 dut
= BranchSpeculationRecord(4)
287 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
288 with
open("test_branchspecrecord.il", "w") as f
:
291 run_simulation(dut
, shadow_sim(dut
), vcd_name
='test_shadow.vcd')
293 if __name__
== '__main__':