dependence cell to use arrays
[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 DependencyRow(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, n_reg, n_src):
29 self.n_reg = n_reg
30 self.n_src = n_src
31 # arrays
32 src = []
33 rsel = []
34 fwd = []
35 for i in range(n_src):
36 j = i + 1 # name numbering to match src1/src2
37 src.append(Signal(n_reg, name="src%d" % j, reset_less=True))
38 rsel.append(Signal(n_reg, name="src%d_rsel_o" % j, reset_less=True))
39 fwd.append(Signal(n_reg, name="src%d_fwd_o" % j, reset_less=True))
40
41 # inputs
42 self.dest_i = Signal(n_reg, reset_less=True) # Dest in (top)
43 self.src_i = Array(src) # operands in (top)
44 self.issue_i = Signal(reset_less=True) # Issue in (top)
45
46 self.rd_pend_i = Signal(n_reg, reset_less=True) # Read pend in (top)
47 self.wr_pend_i = Signal(n_reg, reset_less=True) # Write pend in (top)
48 self.v_rd_rsel_o = Signal(n_reg, reset_less=True) # Read pend out (bot)
49 self.v_wr_rsel_o = Signal(n_reg, reset_less=True) # Write pend out (bot)
50
51 self.go_wr_i = Signal(reset_less=True) # Go Write in (left)
52 self.go_rd_i = Signal(reset_less=True) # Go Read in (left)
53 self.go_die_i = Signal(reset_less=True) # Go Die in (left)
54
55 # for Register File Select Lines (vertical)
56 self.dest_rsel_o = Signal(n_reg, reset_less=True) # dest reg sel (bot)
57 self.src_rsel_o = Array(rsel) # src reg sel (bot)
58 self.src2_rsel_o = Signal(n_reg, reset_less=True) # src2 reg sel (bot)
59
60 # for Function Unit "forward progress" (horizontal)
61 self.dest_fwd_o = Signal(n_reg, reset_less=True) # dest FU fw (right)
62 self.src_fwd_o = Array(fwd) # src FU fw (right)
63
64 def elaborate(self, platform):
65 m = Module()
66 m.submodules.dest_c = dest_c = SRLatch(sync=False, llen=self.n_reg)
67 src_c = []
68 for i in range(self.n_src):
69 src_l = SRLatch(sync=False, llen=self.n_reg)
70 setattr(m.submodules, "src%d_c" % (i+1), src_l)
71 src_c.append(src_l)
72
73 # connect go_rd / go_wr (dest->wr, src->rd)
74 wr_die = Signal(reset_less=True)
75 rd_die = Signal(reset_less=True)
76 m.d.comb += wr_die.eq(self.go_wr_i | self.go_die_i)
77 m.d.comb += rd_die.eq(self.go_rd_i | self.go_die_i)
78 m.d.comb += dest_c.r.eq(Repl(wr_die, self.n_reg))
79 for i in range(self.n_src):
80 m.d.comb += src_c[i].r.eq(Repl(rd_die, self.n_reg))
81
82 # connect input reg bit (unary)
83 i_ext = Repl(self.issue_i, self.n_reg)
84 m.d.comb += dest_c.s.eq(i_ext & self.dest_i)
85 for i in range(self.n_src):
86 m.d.comb += src_c[i].s.eq(i_ext & self.src_i[i])
87
88 # connect up hazard checks: read-after-write and write-after-read
89 m.d.comb += self.dest_fwd_o.eq(dest_c.q & self.rd_pend_i)
90 for i in range(self.n_src):
91 m.d.comb += self.src_fwd_o[i].eq(src_c[i].q & self.wr_pend_i)
92
93 # connect reg-sel outputs
94 rd_ext = Repl(self.go_rd_i, self.n_reg)
95 wr_ext = Repl(self.go_wr_i, self.n_reg)
96 m.d.comb += self.dest_rsel_o.eq(dest_c.qlq & wr_ext)
97 for i in range(self.n_src):
98 m.d.comb += self.src_rsel_o[i].eq(src_c[i].qlq & rd_ext)
99
100 # to be accumulated to indicate if register is in use (globally)
101 # after ORing, is fed back in to rd_pend_i / wr_pend_i
102 src_q = []
103 for i in range(self.n_src):
104 src_q.append(src_c[i].qlq)
105 m.d.comb += self.v_rd_rsel_o.eq(Cat(*src_q).bool())
106 m.d.comb += self.v_wr_rsel_o.eq(dest_c.qlq)
107
108 return m
109
110 def __iter__(self):
111 yield self.dest_i
112 yield from self.src_i
113 yield self.rd_pend_i
114 yield self.wr_pend_i
115 yield self.issue_i
116 yield self.go_wr_i
117 yield self.go_rd_i
118 yield self.go_die_i
119 yield self.dest_rsel_o
120 yield from self.src_rsel_o
121 yield self.dest_fwd_o
122 yield from self.src_fwd_o
123
124 def ports(self):
125 return list(self)
126
127
128 def dcell_sim(dut):
129 yield dut.dest_i.eq(1)
130 yield dut.issue_i.eq(1)
131 yield
132 yield dut.issue_i.eq(0)
133 yield
134 yield dut.src1_i.eq(1)
135 yield dut.issue_i.eq(1)
136 yield
137 yield
138 yield
139 yield dut.issue_i.eq(0)
140 yield
141 yield dut.go_rd_i.eq(1)
142 yield
143 yield dut.go_rd_i.eq(0)
144 yield
145 yield dut.go_wr_i.eq(1)
146 yield
147 yield dut.go_wr_i.eq(0)
148 yield
149
150 def test_dcell():
151 dut = DependencyRow(4, 2)
152 vl = rtlil.convert(dut, ports=dut.ports())
153 with open("test_drow.il", "w") as f:
154 f.write(vl)
155
156 run_simulation(dut, dcell_sim(dut), vcd_name='test_dcell.vcd')
157
158 if __name__ == '__main__':
159 test_dcell()