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 ShadowMatrix(Elaboratable
):
10 """ Matrix of Shadow Functions. One per FU.
13 * :n_fus: register file width
14 * :shadow_wid: number of shadow/fail/good/go_die sets
18 * Shadow enable/fail/good are all connected to all Shadow Functions
21 * Output is an array of "shadow active" (schroedinger wires: neither
22 alive nor dead) and an array of "go die" signals, one per FU.
24 * the shadown must be connected to the Computation Unit's
25 write release request, preventing it (ANDing) from firing
26 (and thus preventing Writable. this by the way being the
27 whole point of having the Shadow Matrix...)
29 * go_die_o must be connected to *both* the Computation Unit's
30 src-operand and result-operand latch resets, causing both
33 * go_die_o also needs to be wired into the Dependency and Function
34 Unit Matrices by way of over-enabling (ORing) into Go_Read and
35 Go_Write, resetting every cell that is required to "die"
37 def __init__(self
, n_fus
, shadow_wid
=0, syncreset
=False):
38 self
.syncreset
= syncreset
40 self
.shadow_wid
= shadow_wid
43 self
.issue_i
= Signal(n_fus
, reset_less
=True)
44 self
.reset_i
= Signal(n_fus
, reset_less
=True)
45 self
.shadow_i
= Array(Signal(shadow_wid
, name
="sh_i", reset_less
=True) \
46 for f
in range(n_fus
))
47 self
.s_fail_i
= Array(Signal(shadow_wid
, name
="fl_i", reset_less
=True) \
48 for f
in range(n_fus
))
49 self
.s_good_i
= Array(Signal(shadow_wid
, name
="gd_i", reset_less
=True) \
50 for f
in range(n_fus
))
52 self
.go_die_o
= Signal(n_fus
, reset_less
=True)
53 self
.shadown_o
= Signal(n_fus
, reset_less
=True)
55 def elaborate(self
, platform
):
58 for i
in range(self
.n_fus
):
59 sh
= ShadowFn(self
.shadow_wid
, self
.syncreset
)
60 setattr(m
.submodules
, "sh%d" % i
, sh
)
62 # connect shadow/fail/good to all shadows
63 m
.d
.comb
+= sh
.s_fail_i
.eq(self
.s_fail_i
[i
])
64 m
.d
.comb
+= sh
.s_good_i
.eq(self
.s_good_i
[i
])
65 # this one is the matrix (shadow enables)
66 m
.d
.comb
+= sh
.shadow_i
.eq(self
.shadow_i
[i
])
68 # connect all shadow outputs and issue input
74 issue_l
.append(l
.issue_i
)
75 reset_l
.append(l
.reset_i
)
76 sho_l
.append(l
.shadown_o
)
77 rec_l
.append(l
.go_die_o
)
78 m
.d
.comb
+= Cat(*issue_l
).eq(self
.issue_i
)
79 m
.d
.comb
+= Cat(*reset_l
).eq(self
.reset_i
)
80 m
.d
.comb
+= self
.shadown_o
.eq(Cat(*sho_l
))
81 m
.d
.comb
+= self
.go_die_o
.eq(Cat(*rec_l
))
88 yield from self
.shadow_i
89 yield from self
.s_fail_i
90 yield from self
.s_good_i
98 class BranchSpeculationRecord(Elaboratable
):
99 """ A record of which function units will be cancelled and which
100 allowed to proceed, on a branch.
102 Whilst the input is a pair that says whether the instruction is
103 under the "success" branch shadow (good_i) or the "fail" shadow
104 (fail_i path), when the branch result is known, the "good" path
105 must be cancelled if "fail" occurred, and the "fail" path cancelled
108 therefore, use "good|~fail" and "fail|~good" respectively as
112 def __init__(self
, n_fus
):
115 # inputs: record *expected* status
116 self
.active_i
= Signal(reset_less
=True)
117 self
.good_i
= Signal(n_fus
, reset_less
=True)
118 self
.fail_i
= Signal(n_fus
, reset_less
=True)
120 # inputs: status of branch (when result was known)
121 self
.br_i
= Signal(reset_less
=True)
122 self
.br_ok_i
= Signal(reset_less
=True)
124 # outputs: true if the *expected* outcome matched the *actual* outcome
125 self
.match_f_o
= Signal(n_fus
, reset_less
=True)
126 self
.match_g_o
= Signal(n_fus
, reset_less
=True)
128 def elaborate(self
, platform
):
131 # registers to record *expected* status
132 good_r
= Signal(self
.n_fus
)
133 fail_r
= Signal(self
.n_fus
)
135 for i
in range(self
.n_fus
):
136 with m
.If(self
.active_i
):
137 m
.d
.sync
+= good_r
[i
].eq(good_r
[i
] | self
.good_i
[i
])
138 m
.d
.sync
+= fail_r
[i
].eq(fail_r
[i
] | self
.fail_i
[i
])
139 with m
.If(self
.br_i
):
140 with m
.If(good_r
[i
]):
141 # we expected good, return OK that good was EXPECTED
142 m
.d
.comb
+= self
.match_g_o
[i
].eq(self
.br_ok_i
)
143 m
.d
.comb
+= self
.match_f_o
[i
].eq(~self
.br_ok_i
)
144 with m
.If(fail_r
[i
]):
145 # we expected fail, return OK that fail was EXPECTED
146 m
.d
.comb
+= self
.match_g_o
[i
].eq(~self
.br_ok_i
)
147 m
.d
.comb
+= self
.match_f_o
[i
].eq(self
.br_ok_i
)
148 m
.d
.sync
+= good_r
[i
].eq(0) # might be set if issue set as well
149 m
.d
.sync
+= fail_r
[i
].eq(0) # might be set if issue set as well
168 class WaWGrid(Elaboratable
):
169 """ An NxM grid-selector which raises a 2D bit selected by N and M
172 def __init__(self
, n_fus
, shadow_wid
):
174 self
.shadow_wid
= shadow_wid
176 self
.shadow_i
= Signal(shadow_wid
, reset_less
=True)
177 self
.fu_i
= Signal(n_fus
, reset_less
=True)
179 self
.waw_o
= Array(Signal(shadow_wid
, name
="waw_o", reset_less
=True) \
180 for f
in range(n_fus
))
182 def elaborate(self
, platform
):
184 for i
in range(self
.n_fus
):
185 v
= Repl(self
.fu_i
[i
], self
.shadow_wid
)
186 m
.d
.comb
+= self
.waw_o
[i
].eq(v
& self
.shadow_i
)
191 yield dut
.dest_i
.eq(1)
192 yield dut
.issue_i
.eq(1)
194 yield dut
.issue_i
.eq(0)
196 yield dut
.src1_i
.eq(1)
197 yield dut
.issue_i
.eq(1)
201 yield dut
.issue_i
.eq(0)
203 yield dut
.go_rd_i
.eq(1)
205 yield dut
.go_rd_i
.eq(0)
207 yield dut
.go_wr_i
.eq(1)
209 yield dut
.go_wr_i
.eq(0)
213 dut
= ShadowMatrix(4, 2)
214 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
215 with
open("test_shadow.il", "w") as f
:
218 dut
= BranchSpeculationRecord(4)
219 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
220 with
open("test_branchspecrecord.il", "w") as f
:
223 run_simulation(dut
, shadow_sim(dut
), vcd_name
='test_shadow.vcd')
225 if __name__
== '__main__':