do dependency row as multi-bit SRLatch
[soc.git] / src / scoreboard / 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
6
7 class DepCell(Elaboratable):
8 """ implements 11.4.7 mitch alsup dependence cell, p27
9 adjusted to be clock-sync'd on rising edge only.
10 mitch design (as does 6600) requires alternating rising/falling clock
11
12 * SET mode: issue_i HI, go_i LO, reg_i HI - register is captured
13 - FWD is DISABLED (~issue_i)
14 - RSEL DISABLED
15 * QRY mode: issue_i LO, go_i LO, haz_i HI - FWD is ASSERTED
16 reg_i HI - ignored
17 * GO mode : issue_i LO, go_i HI - RSEL is ASSERTED
18 haz_i HI - FWD still can be ASSERTED
19
20 FWD assertion (hazard protection) therefore still occurs in both
21 Query and Go Modes, for this cycle, due to the cq register
22
23 GO mode works for one cycle, again due to the cq register capturing
24 the latch output. Without the cq register, the SR Latch (which is
25 asynchronous) would be reset at the exact moment that GO was requested,
26 and the RSEL would be garbage.
27 """
28 def __init__(self, llen):
29 self.llen = llen
30 # inputs
31 self.reg_i = Signal(llen, reset_less=True) # reg bit in (top)
32 self.issue_i = Signal(reset_less=True) # Issue in (top)
33 self.hazard_i = Signal(llen, reset_less=True) # to check hazard
34 self.go_i = Signal(reset_less=True) # Go read/write in (left)
35 self.die_i = Signal(reset_less=True) # Die in (left)
36 self.q_o = Signal(llen, reset_less=True) # Latch out (reg active)
37
38 # for Register File Select Lines (vertical)
39 self.rsel_o = Signal(llen, reset_less=True) # reg sel (bottom)
40 # for Function Unit "forward progress" (horizontal)
41 self.fwd_o = Signal(llen, reset_less=True) # FU forard progress (right)
42
43 def elaborate(self, platform):
44 m = Module()
45 m.submodules.l = l = SRLatch(sync=False, llen=self.llen) # async latch
46
47 # reset on go HI, set on dest and issue
48 m.d.comb += l.s.eq(Repl(self.issue_i, self.llen) & self.reg_i)
49 m.d.comb += l.r.eq(Repl(self.go_i | self.die_i, self.llen))
50
51 # Function Unit "Forward Progress".
52 m.d.comb += self.fwd_o.eq((l.q) & self.hazard_i) # & ~self.issue_i)
53
54 # Register Select. Activated on go read/write and *current* latch set
55 m.d.comb += self.q_o.eq(l.qlq)
56 m.d.comb += self.rsel_o.eq(l.qlq & Repl(self.go_i, self.llen))
57
58 return m
59
60 def __iter__(self):
61 yield self.reg_i
62 yield self.hazard_i
63 yield self.issue_i
64 yield self.go_i
65 yield self.die_i
66 yield self.q_o
67 yield self.rsel_o
68 yield self.fwd_o
69
70 def ports(self):
71 return list(self)
72
73
74 class DependencyRow(Elaboratable):
75 """ implements 11.4.7 mitch alsup dependence cell, p27
76 """
77 def __init__(self, n_reg):
78 self.n_reg = n_reg
79 # inputs
80 self.dest_i = Signal(n_reg, reset_less=True) # Dest in (top)
81 self.src1_i = Signal(n_reg, reset_less=True) # oper1 in (top)
82 self.src2_i = Signal(n_reg, reset_less=True) # oper2 in (top)
83 self.issue_i = Signal(reset_less=True) # Issue in (top)
84
85 self.rd_pend_i = Signal(n_reg, reset_less=True) # Read pend in (top)
86 self.wr_pend_i = Signal(n_reg, reset_less=True) # Write pend in (top)
87 self.rd_rsel_o = Signal(n_reg, reset_less=True) # Read pend out (bot)
88 self.wr_rsel_o = Signal(n_reg, reset_less=True) # Write pend out (bot)
89
90 self.go_wr_i = Signal(reset_less=True) # Go Write in (left)
91 self.go_rd_i = Signal(reset_less=True) # Go Read in (left)
92 self.go_die_i = Signal(reset_less=True) # Go Die in (left)
93
94 # for Register File Select Lines (vertical)
95 self.dest_rsel_o = Signal(n_reg, reset_less=True) # dest reg sel (bot)
96 self.src1_rsel_o = Signal(n_reg, reset_less=True) # src1 reg sel (bot)
97 self.src2_rsel_o = Signal(n_reg, reset_less=True) # src2 reg sel (bot)
98
99 # for Function Unit "forward progress" (horizontal)
100 self.dest_fwd_o = Signal(n_reg, reset_less=True) # dest FU fw (right)
101 self.src1_fwd_o = Signal(n_reg, reset_less=True) # src1 FU fw (right)
102 self.src2_fwd_o = Signal(n_reg, reset_less=True) # src2 FU fw (right)
103
104 def elaborate(self, platform):
105 m = Module()
106 m.submodules.dest_c = dest_c = DepCell(self.n_reg)
107 m.submodules.src1_c = src1_c = DepCell(self.n_reg)
108 m.submodules.src2_c = src2_c = DepCell(self.n_reg)
109
110 # connect issue and die
111 for c in [dest_c, src1_c, src2_c]:
112 m.d.comb += c.issue_i.eq(self.issue_i)
113 m.d.comb += c.die_i.eq(self.go_die_i)
114
115 # connect go_rd / go_wr (dest->wr, src->rd)
116 m.d.comb += dest_c.go_i.eq(self.go_wr_i)
117 m.d.comb += src1_c.go_i.eq(self.go_rd_i)
118 m.d.comb += src2_c.go_i.eq(self.go_rd_i)
119
120 # connect input reg bit (unary)
121 for c, reg in [(dest_c, self.dest_i),
122 (src1_c, self.src1_i),
123 (src2_c, self.src2_i)]:
124 m.d.comb += c.reg_i.eq(reg)
125
126 # wark-wark: yes, writing to the same reg you are reading is *NOT*
127 # a write-after-read hazard.
128 selfhazard = Signal(self.n_reg, reset_less=False)
129 m.d.comb += selfhazard.eq((self.dest_i & self.src1_i) |
130 (self.dest_i & self.src2_i))
131
132 # connect up hazard checks: read-after-write and write-after-read
133 m.d.comb += dest_c.hazard_i.eq(self.rd_pend_i) # read-after-write
134 m.d.comb += src1_c.hazard_i.eq(self.wr_pend_i) # write-after-read
135 m.d.comb += src2_c.hazard_i.eq(self.wr_pend_i) # write-after-read
136
137 # connect fwd / reg-sel outputs
138 for c, fwd, rsel in [(dest_c, self.dest_fwd_o, self.dest_rsel_o),
139 (src1_c, self.src1_fwd_o, self.src1_rsel_o),
140 (src2_c, self.src2_fwd_o, self.src2_rsel_o)]:
141 m.d.comb += fwd.eq(c.fwd_o)
142 m.d.comb += rsel.eq(c.rsel_o)
143
144 # to be accumulated to indicate if register is in use (globally)
145 # after ORing, is fed back in to rd_pend_i / wr_pend_i
146 m.d.comb += self.rd_rsel_o.eq(src1_c.q_o | src2_c.q_o)
147 #with m.If(~selfhazard):
148 m.d.comb += self.wr_rsel_o.eq(dest_c.q_o)
149
150 return m
151
152 def __iter__(self):
153 yield self.dest_i
154 yield self.src1_i
155 yield self.src2_i
156 yield self.rd_pend_i
157 yield self.wr_pend_i
158 yield self.issue_i
159 yield self.go_wr_i
160 yield self.go_rd_i
161 yield self.go_die_i
162 yield self.dest_rsel_o
163 yield self.src1_rsel_o
164 yield self.src2_rsel_o
165 yield self.dest_fwd_o
166 yield self.src1_fwd_o
167 yield self.src2_fwd_o
168
169 def ports(self):
170 return list(self)
171
172
173 def dcell_sim(dut):
174 yield dut.dest_i.eq(1)
175 yield dut.issue_i.eq(1)
176 yield
177 yield dut.issue_i.eq(0)
178 yield
179 yield dut.src1_i.eq(1)
180 yield dut.issue_i.eq(1)
181 yield
182 yield
183 yield
184 yield dut.issue_i.eq(0)
185 yield
186 yield dut.go_rd_i.eq(1)
187 yield
188 yield dut.go_rd_i.eq(0)
189 yield
190 yield dut.go_wr_i.eq(1)
191 yield
192 yield dut.go_wr_i.eq(0)
193 yield
194
195 def test_dcell():
196 dut = DependencyRow(4)
197 vl = rtlil.convert(dut, ports=dut.ports())
198 with open("test_drow.il", "w") as f:
199 f.write(vl)
200
201 run_simulation(dut, dcell_sim(dut), vcd_name='test_dcell.vcd')
202
203 if __name__ == '__main__':
204 test_dcell()