Allow the formal engine to perform a same-cycle result in the ALU
[soc.git] / src / soc / 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, Const, Elaboratable, Repl
4 from nmigen.lib.coding import Decoder
5
6 from soc.scoreboard.shadow_fn import ShadowFn
7
8
9 class ShadowMatrix(Elaboratable):
10 """ Matrix of Shadow Functions. One per FU.
11
12 Inputs
13 * :n_fus: register file width
14 * :shadow_wid: number of shadow/fail/good/go_die sets
15
16 Notes:
17
18 * Shadow enable/fail/good are all connected to all Shadow Functions
19 (incoming at the top)
20
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.
23
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...)
28
29 * go_die_o must be connected to *both* the Computation Unit's
30 src-operand and result-operand latch resets, causing both
31 of them to reset.
32
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"
36 """
37 def __init__(self, n_fus, shadow_wid=0, syncreset=False):
38 self.syncreset = syncreset
39 self.n_fus = n_fus
40 self.shadow_wid = shadow_wid
41
42 # inputs
43 self.issue_i = Signal(n_fus, reset_less=True)
44 self.reset_i = Signal(n_fus, reset_less=True)
45 self.shadow_i = tuple(Signal(shadow_wid, name="sh_i", reset_less=True) \
46 for f in range(n_fus))
47 self.s_fail_i = tuple(Signal(shadow_wid, name="fl_i", reset_less=True) \
48 for f in range(n_fus))
49 self.s_good_i = tuple(Signal(shadow_wid, name="gd_i", reset_less=True) \
50 for f in range(n_fus))
51 # outputs
52 self.go_die_o = Signal(n_fus, reset_less=True)
53 self.shadown_o = Signal(n_fus, reset_less=True)
54
55 def elaborate(self, platform):
56 m = Module()
57 shadows = []
58 for i in range(self.n_fus):
59 sh = ShadowFn(self.shadow_wid, self.syncreset)
60 setattr(m.submodules, "sh%d" % i, sh)
61 shadows.append(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])
67
68 # connect all shadow outputs and issue input
69 issue_l = []
70 reset_l = []
71 sho_l = []
72 rec_l = []
73 for l in shadows:
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))
82
83 return m
84
85 def __iter__(self):
86 yield self.issue_i
87 yield self.reset_i
88 yield from self.shadow_i
89 yield from self.s_fail_i
90 yield from self.s_good_i
91 yield self.go_die_o
92 yield self.shadown_o
93
94 def ports(self):
95 return list(self)
96
97
98 class BranchSpeculationRecord(Elaboratable):
99 """ A record of which function units will be cancelled and which
100 allowed to proceed, on a branch.
101
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
106 if "good" occurred.
107
108 therefore, use "good|~fail" and "fail|~good" respectively as
109 output.
110 """
111
112 def __init__(self, n_fus):
113 self.n_fus = n_fus
114
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)
119
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)
123
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)
127
128 def elaborate(self, platform):
129 m = Module()
130
131 # registers to record *expected* status
132 good_r = Signal(self.n_fus)
133 fail_r = Signal(self.n_fus)
134
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
150
151 return m
152
153 def __iter__(self):
154 yield self.active_i
155 yield self.good_i
156 yield self.fail_i
157 yield self.br_i
158 yield self.br_good_i
159 yield self.br_fail_i
160 yield self.good_o
161 yield self.fail_o
162
163 def ports(self):
164 return list(self)
165
166
167
168 class WaWGrid(Elaboratable):
169 """ An NxM grid-selector which raises a 2D bit selected by N and M
170 """
171
172 def __init__(self, n_fus, shadow_wid):
173 self.n_fus = n_fus
174 self.shadow_wid = shadow_wid
175
176 self.shadow_i = Signal(shadow_wid, reset_less=True)
177 self.fu_i = Signal(n_fus, reset_less=True)
178
179 self.waw_o = tuple(Signal(shadow_wid, name="waw_o", reset_less=True) \
180 for f in range(n_fus))
181
182 def elaborate(self, platform):
183 m = Module()
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)
187 return m
188
189
190 def shadow_sim(dut):
191 yield dut.dest_i.eq(1)
192 yield dut.issue_i.eq(1)
193 yield
194 yield dut.issue_i.eq(0)
195 yield
196 yield dut.src1_i.eq(1)
197 yield dut.issue_i.eq(1)
198 yield
199 yield
200 yield
201 yield dut.issue_i.eq(0)
202 yield
203 yield dut.go_rd_i.eq(1)
204 yield
205 yield dut.go_rd_i.eq(0)
206 yield
207 yield dut.go_wr_i.eq(1)
208 yield
209 yield dut.go_wr_i.eq(0)
210 yield
211
212 def test_shadow():
213 dut = ShadowMatrix(4, 2)
214 vl = rtlil.convert(dut, ports=dut.ports())
215 with open("test_shadow.il", "w") as f:
216 f.write(vl)
217
218 dut = BranchSpeculationRecord(4)
219 vl = rtlil.convert(dut, ports=dut.ports())
220 with open("test_branchspecrecord.il", "w") as f:
221 f.write(vl)
222
223 run_simulation(dut, shadow_sim(dut), vcd_name='test_shadow.vcd')
224
225 if __name__ == '__main__':
226 test_shadow()