Allow the formal engine to perform a same-cycle result in the ALU
[soc.git] / src / soc / scoremulti / dependence_cell.py
1 from nmigen.compat.sim import run_simulation
2 from nmigen.cli import verilog, rtlil
3 from nmigen import Module, Signal, Elaboratable, Array, Cat, Repl
4 from nmutil.latch import SRLatch
5 from functools import reduce
6 from operator import or_
7
8
9 class DependencyRow(Elaboratable):
10 """ implements 11.4.7 mitch alsup dependence cell, p27
11 adjusted to be clock-sync'd on rising edge only.
12 mitch design (as does 6600) requires alternating rising/falling clock
13
14 * SET mode: issue_i HI, go_i LO, reg_i HI - register is captured
15 - FWD is DISABLED (~issue_i)
16 - RSEL DISABLED
17 * QRY mode: issue_i LO, go_i LO, haz_i HI - FWD is ASSERTED
18 reg_i HI - ignored
19 * GO mode : issue_i LO, go_i HI - RSEL is ASSERTED
20 haz_i HI - FWD still can be ASSERTED
21
22 FWD assertion (hazard protection) therefore still occurs in both
23 Query and Go Modes, for this cycle, due to the cq register
24
25 GO mode works for one cycle, again due to the cq register capturing
26 the latch output. Without the cq register, the SR Latch (which is
27 asynchronous) would be reset at the exact moment that GO was requested,
28 and the RSEL would be garbage.
29
30 cancel_mode: individual bit-array of cancels rather than a global one
31 """
32 def __init__(self, n_reg, n_src, n_dest, cancel_mode=False):
33 self.cancel_mode = cancel_mode
34 self.n_reg = n_reg
35 self.n_src = n_src
36 self.n_dest = n_dest
37 # src arrays
38 src = []
39 rsel = []
40 fwd = []
41 for i in range(n_src):
42 j = i + 1 # name numbering to match src1/src2
43 src.append(Signal(n_reg, name="src%d" % j, reset_less=True))
44 rsel.append(Signal(n_reg, name="src%d_rsel_o" % j, reset_less=True))
45 fwd.append(Signal(n_reg, name="src%d_fwd_o" % j, reset_less=True))
46
47 # dest arrays
48 dest = []
49 dsel = []
50 dfwd = []
51 for i in range(n_dest):
52 j = i + 1 # name numbering to match src1/src2
53 dest.append(Signal(n_reg, name="dst%d" % j, reset_less=True))
54 dsel.append(Signal(n_reg, name="dst%d_rsel_o" % j, reset_less=True))
55 dfwd.append(Signal(n_reg, name="dst%d_fwd_o" % j, reset_less=True))
56
57 # inputs
58 self.dest_i = Array(dest) # Dest in (top)
59 self.src_i = Array(src) # operands in (top)
60 self.issue_i = Signal(reset_less=True) # Issue in (top)
61
62 self.rd_pend_i = Signal(n_reg, reset_less=True) # Read pend in (top)
63 self.wr_pend_i = Signal(n_reg, reset_less=True) # Write pend in (top)
64 self.v_rd_rsel_o = Signal(n_reg, reset_less=True) # Read pend out (bot)
65 self.v_wr_rsel_o = Signal(n_reg, reset_less=True) # Write pend out (bot)
66
67 self.go_wr_i = Signal(n_dest, reset_less=True) # Go Write in (left)
68 self.go_rd_i = Signal(n_src, reset_less=True) # Go Read in (left)
69
70 if self.cancel_mode:
71 self.go_die_i = Signal(n_reg, reset_less=True) # Go Die in (left)
72 else:
73 self.go_die_i = Signal(reset_less=True) # Go Die in (left)
74
75 # for Register File Select Lines (vertical)
76 self.dest_rsel_o = Array(dsel) # dest reg sel (bot)
77 self.src_rsel_o = Array(rsel) # src reg sel (bot)
78
79 # for Function Unit "forward progress" (horizontal)
80 self.dest_fwd_o = Array(dfwd) # dest FU fw (right)
81 self.src_fwd_o = Array(fwd) # src FU fw (right)
82
83 def elaborate(self, platform):
84 m = Module()
85
86 # set up dest latches
87 dest_c = []
88 for i in range(self.n_dest):
89 dst_l = SRLatch(sync=False, llen=self.n_reg, name="dst%d" % i)
90 setattr(m.submodules, "dst%d_c" % (i+1), dst_l)
91 dest_c.append(dst_l)
92
93 # set up src latches
94 src_c = []
95 for i in range(self.n_src):
96 src_l = SRLatch(sync=False, llen=self.n_reg, name="src%d" % i)
97 setattr(m.submodules, "src%d_c" % (i+1), src_l)
98 src_c.append(src_l)
99
100 # connect go_rd / go_wr (dest->wr, src->rd)
101 if self.cancel_mode:
102 go_die = self.go_die_i
103 else:
104 go_die = Repl(self.go_die_i, self.n_reg)
105 wr_die = []
106 for i in range(self.n_dest):
107 wrd = Signal(self.n_reg, reset_less=True, name="wdi%d" % i)
108 m.d.comb += wrd.eq(Repl(self.go_wr_i[i], self.n_reg) | go_die)
109 wr_die.append(wrd)
110 rd_die = []
111 for i in range(self.n_src):
112 rdd = Signal(self.n_reg, reset_less=True, name="rdi%d" % i)
113 m.d.comb += rdd.eq(Repl(self.go_rd_i[i], self.n_reg) | go_die)
114 rd_die.append(rdd)
115 for i in range(self.n_src):
116 m.d.comb += src_c[i].r.eq(rd_die[i])
117 for i in range(self.n_dest):
118 m.d.comb += dest_c[i].r.eq(wr_die[i])
119
120 # connect input reg bit (unary)
121 i_ext = Repl(self.issue_i, self.n_reg)
122 for i in range(self.n_dest):
123 m.d.comb += dest_c[i].s.eq(i_ext & self.dest_i[i])
124 for i in range(self.n_src):
125 m.d.comb += src_c[i].s.eq(i_ext & self.src_i[i])
126
127 # connect up hazard checks: read-after-write and write-after-read
128 for i in range(self.n_dest):
129 m.d.comb += self.dest_fwd_o[i].eq(dest_c[i].q & self.rd_pend_i)
130 for i in range(self.n_src):
131 m.d.comb += self.src_fwd_o[i].eq(src_c[i].q & self.wr_pend_i)
132
133 # connect reg-sel outputs
134 for i in range(self.n_dest):
135 wr_ext = Repl(self.go_wr_i[i], self.n_reg)
136 m.d.comb += self.dest_rsel_o[i].eq(dest_c[i].qlq & wr_ext)
137 for i in range(self.n_src):
138 rd_ext = Repl(self.go_rd_i[i], self.n_reg)
139 m.d.comb += self.src_rsel_o[i].eq(src_c[i].qlq & rd_ext)
140
141 # to be accumulated to indicate if register is in use (globally)
142 # after ORing, is fed back in to rd_pend_i / wr_pend_i
143 src_q = []
144 for i in range(self.n_src):
145 src_q.append(src_c[i].qlq)
146 m.d.comb += self.v_rd_rsel_o.eq(reduce(or_, src_q)) # do not use bool()
147 dst_q = []
148 for i in range(self.n_dest):
149 dst_q.append(dest_c[i].qlq)
150 m.d.comb += self.v_wr_rsel_o.eq(reduce(or_, dst_q)) # do not use bool()
151
152 return m
153
154 def __iter__(self):
155 yield from self.dest_i
156 yield from self.src_i
157 yield self.rd_pend_i
158 yield self.wr_pend_i
159 yield self.issue_i
160 yield self.go_wr_i
161 yield self.go_rd_i
162 yield self.go_die_i
163 yield from self.dest_rsel_o
164 yield from self.src_rsel_o
165 yield from self.dest_fwd_o
166 yield from self.src_fwd_o
167
168 def ports(self):
169 return list(self)
170
171
172 def dcell_sim(dut):
173 yield dut.dest_i.eq(1)
174 yield dut.issue_i.eq(1)
175 yield
176 yield dut.issue_i.eq(0)
177 yield
178 yield dut.src1_i.eq(1)
179 yield dut.issue_i.eq(1)
180 yield
181 yield
182 yield
183 yield dut.issue_i.eq(0)
184 yield
185 yield dut.go_rd_i.eq(1)
186 yield
187 yield dut.go_rd_i.eq(0)
188 yield
189 yield dut.go_wr_i.eq(1)
190 yield
191 yield dut.go_wr_i.eq(0)
192 yield
193
194 def test_dcell():
195 dut = DependencyRow(4, 3, 2, True)
196 vl = rtlil.convert(dut, ports=dut.ports())
197 with open("test_drow.il", "w") as f:
198 f.write(vl)
199
200 run_simulation(dut, dcell_sim(dut), vcd_name='test_dcell.vcd')
201
202 if __name__ == '__main__':
203 test_dcell()