make shadow inputs/good/fail arrays (actual matrix now)
[soc.git] / src / 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, Array, Const, Elaboratable
4 from nmigen.lib.coding import Decoder
5
6 from nmutil.latch import SRLatch, latchregister
7
8 from scoreboard.shadow_fn import ShadowFn
9
10
11 class Shadow(Elaboratable):
12 """ implements shadowing 11.5.1, p55
13
14 shadowing can be used for branches as well as exceptions (interrupts),
15 load/store hold (exceptions again), and vector-element predication
16 (once the predicate is known, which it may not be at instruction issue)
17
18 Inputs
19 * :shadow_wid: number of shadow/fail/good/go_die sets
20
21 notes:
22 * when shadow_wid = 0, recover and shadown are Consts (i.e. do nothing)
23 """
24 def __init__(self, shadow_wid=0):
25 self.shadow_wid = shadow_wid
26
27 if shadow_wid:
28 self.issue_i = Signal(reset_less=True)
29 self.shadow_i = Signal(shadow_wid, reset_less=True)
30 self.s_fail_i = Signal(shadow_wid, reset_less=True)
31 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)
34 else:
35 self.shadown_o = Const(1)
36 self.go_die_o = Const(0)
37
38 def elaborate(self, platform):
39 m = Module()
40 s_latches = []
41 for i in range(self.shadow_wid):
42 sh = ShadowFn()
43 setattr(m.submodules, "shadow%d" % i, sh)
44 s_latches.append(sh)
45
46 # shadow / recover (optional: shadow_wid > 0)
47 if self.shadow_wid:
48 i_l = []
49 fail_l = []
50 good_l = []
51 shi_l = []
52 sho_l = []
53 rec_l = []
54 # get list of latch signals. really must be a better way to do this
55 for l in s_latches:
56 i_l.append(l.issue_i)
57 shi_l.append(l.shadow_i)
58 fail_l.append(l.s_fail_i)
59 good_l.append(l.s_good_i)
60 sho_l.append(l.shadow_o)
61 rec_l.append(l.recover_o)
62 m.d.comb += Cat(*i_l).eq(self.issue_i)
63 m.d.comb += Cat(*fail_l).eq(self.s_fail_i)
64 m.d.comb += Cat(*good_l).eq(self.s_good_i)
65 m.d.comb += Cat(*shi_l).eq(self.shadow_i)
66 m.d.comb += self.shadown_o.eq(~(Cat(*sho_l).bool()))
67 m.d.comb += self.go_die_o.eq(Cat(*rec_l).bool())
68
69 return m
70
71 def __iter__(self):
72 if self.shadow_wid:
73 yield self.issue_i
74 yield self.shadow_i
75 yield self.s_fail_i
76 yield self.s_good_i
77 yield self.go_die_o
78 yield self.shadown_o
79
80 def ports(self):
81 return list(self)
82
83
84 class ShadowMatrix(Elaboratable):
85 """ Matrix of Shadow Functions. One per FU.
86
87 Inputs
88 * :n_fus: register file width
89 * :shadow_wid: number of shadow/fail/good/go_die sets
90
91 Notes:
92
93 * Shadow enable/fail/good are all connected to all Shadow Functions
94 (incoming at the top)
95
96 * Output is an array of "shadow active" (schroedinger wires: neither
97 alive nor dead) and an array of "go die" signals, one per FU.
98
99 * the shadown must be connected to the Computation Unit's
100 write release request, preventing it (ANDing) from firing
101 (and thus preventing Writable. this by the way being the
102 whole point of having the Shadow Matrix...)
103
104 * go_die_o must be connected to *both* the Computation Unit's
105 src-operand and result-operand latch resets, causing both
106 of them to reset.
107
108 * go_die_o also needs to be wired into the Dependency and Function
109 Unit Matrices by way of over-enabling (ORing) into Go_Read and
110 Go_Write, resetting every cell that is required to "die"
111 """
112 def __init__(self, n_fus, shadow_wid=0):
113 self.n_fus = n_fus
114 self.shadow_wid = shadow_wid
115
116 # inputs
117 self.issue_i = Signal(n_fus, reset_less=True)
118 self.shadow_i = Array(Signal(shadow_wid, name="sh_i", reset_less=True) \
119 for f in range(n_fus))
120 self.s_fail_i = Array(Signal(shadow_wid, name="f_i", reset_less=True) \
121 for f in range(n_fus))
122 self.s_good_i = Array(Signal(shadow_wid, name="g_i", reset_less=True) \
123 for f in range(n_fus))
124
125 # outputs
126 self.go_die_o = Signal(n_fus, reset_less=True)
127 self.shadown_o = Signal(n_fus, reset_less=True)
128
129 def elaborate(self, platform):
130 m = Module()
131 shadows = []
132 for i in range(self.n_fus):
133 sh = Shadow(self.shadow_wid)
134 setattr(m.submodules, "sh%d" % i, sh)
135 shadows.append(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 m.d.comb += sh.shadow_i.eq(self.shadow_i[i])
140
141 # connect all shadow outputs and issue input
142 issue_l = []
143 sho_l = []
144 rec_l = []
145 for l in shadows:
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))
152
153 return m
154
155 def __iter__(self):
156 yield self.issue_i
157 yield from self.shadow_i
158 yield from self.s_fail_i
159 yield from self.s_good_i
160 yield self.go_die_o
161 yield self.shadown_o
162
163 def ports(self):
164 return list(self)
165
166
167 def shadow_sim(dut):
168 yield dut.dest_i.eq(1)
169 yield dut.issue_i.eq(1)
170 yield
171 yield dut.issue_i.eq(0)
172 yield
173 yield dut.src1_i.eq(1)
174 yield dut.issue_i.eq(1)
175 yield
176 yield
177 yield
178 yield dut.issue_i.eq(0)
179 yield
180 yield dut.go_rd_i.eq(1)
181 yield
182 yield dut.go_rd_i.eq(0)
183 yield
184 yield dut.go_wr_i.eq(1)
185 yield
186 yield dut.go_wr_i.eq(0)
187 yield
188
189 def test_shadow():
190 dut = ShadowMatrix(4, 2)
191 vl = rtlil.convert(dut, ports=dut.ports())
192 with open("test_shadow.il", "w") as f:
193 f.write(vl)
194
195 run_simulation(dut, shadow_sim(dut), vcd_name='test_shadow.vcd')
196
197 if __name__ == '__main__':
198 test_shadow()