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_
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
14 * SET mode: issue_i HI, go_i LO, reg_i HI - register is captured
15 - FWD is DISABLED (~issue_i)
17 * QRY mode: issue_i LO, go_i LO, haz_i HI - FWD is ASSERTED
19 * GO mode : issue_i LO, go_i HI - RSEL is ASSERTED
20 haz_i HI - FWD still can be ASSERTED
22 FWD assertion (hazard protection) therefore still occurs in both
23 Query and Go Modes, for this cycle, due to the cq register
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.
30 cancel_mode: individual bit-array of cancels rather than a global one
32 def __init__(self
, n_reg
, n_src
, n_dest
, cancel_mode
=False):
33 self
.cancel_mode
= cancel_mode
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))
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))
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)
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)
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)
71 self
.go_die_i
= Signal(n_reg
, reset_less
=True) # Go Die in (left)
73 self
.go_die_i
= Signal(reset_less
=True) # Go Die in (left)
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)
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)
83 def elaborate(self
, platform
):
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
)
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
)
100 # connect go_rd / go_wr (dest->wr, src->rd)
102 go_die
= self
.go_die_i
104 go_die
= Repl(self
.go_die_i
, self
.n_reg
)
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
)
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
)
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
])
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
])
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
)
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
)
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
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()
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()
155 yield from self
.dest_i
156 yield from self
.src_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
173 yield dut
.dest_i
.eq(1)
174 yield dut
.issue_i
.eq(1)
176 yield dut
.issue_i
.eq(0)
178 yield dut
.src1_i
.eq(1)
179 yield dut
.issue_i
.eq(1)
183 yield dut
.issue_i
.eq(0)
185 yield dut
.go_rd_i
.eq(1)
187 yield dut
.go_rd_i
.eq(0)
189 yield dut
.go_wr_i
.eq(1)
191 yield dut
.go_wr_i
.eq(0)
195 dut
= DependencyRow(4, 3, 2, True)
196 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
197 with
open("test_drow.il", "w") as f
:
200 run_simulation(dut
, dcell_sim(dut
), vcd_name
='test_dcell.vcd')
202 if __name__
== '__main__':