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