1 # (DO NOT REMOVE THESE NOTICES)
2 # SPDX-License-Identifier: LGPLv3+
3 # Copyright (C) 2019, 2020, 2021 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
4 # Part of the Libre-SOC Project.
5 # Sponsored by NLnet EU Grant No: 825310 and 825322
6 # Sponsored by NGI POINTER EU Grant No: 871528
8 from nmigen
.compat
.sim
import run_simulation
9 from nmigen
.cli
import verilog
, rtlil
10 from nmigen
import Module
, Signal
, Elaboratable
, Cat
, Repl
11 from nmutil
.latch
import SRLatch
14 class DependencyRow(Elaboratable
):
15 """ implements 11.4.7 mitch alsup dependence cell, p27
16 adjusted to be clock-sync'd on rising edge only.
17 mitch design (as does 6600) requires alternating rising/falling clock
19 * SET mode: issue_i HI, go_i LO, reg_i HI - register is captured
20 - FWD is DISABLED (~issue_i)
22 * QRY mode: issue_i LO, go_i LO, haz_i HI - FWD is ASSERTED
24 * GO mode : issue_i LO, go_i HI - RSEL is ASSERTED
25 haz_i HI - FWD still can be ASSERTED
27 FWD assertion (hazard protection) therefore still occurs in both
28 Query and Go Modes, for this cycle, due to the cq register
30 GO mode works for one cycle, again due to the cq register capturing
31 the latch output. Without the cq register, the SR Latch (which is
32 asynchronous) would be reset at the exact moment that GO was requested,
33 and the RSEL would be garbage.
35 def __init__(self
, n_reg
, n_src
, n_dst
, cancel_mode
=False):
36 self
.cancel_mode
= cancel_mode
44 for i
in range(n_src
):
45 j
= i
+ 1 # name numbering to match src1/src2
46 src
.append(Signal(n_reg
, name
="src%d" % j
, reset_less
=True))
47 rsel
.append(Signal(n_reg
, name
="src%d_rsel_o" % j
, reset_less
=True))
48 fwd
.append(Signal(n_reg
, name
="src%d_fwd_o" % j
, reset_less
=True))
52 for i
in range(n_dst
):
53 j
= i
+ 1 # name numbering to match src1/src2
54 dst
.append(Signal(n_reg
, name
="dst%d" % j
, reset_less
=True))
55 dsel
.append(Signal(n_reg
, name
="dst%d_rsel_o" % j
, reset_less
=True))
56 dfwd
.append(Signal(n_reg
, name
="dst%d_fwd_o" % j
, reset_less
=True))
59 self
.dst_i
= tuple(dst
) # Dest in (top)
60 self
.src_i
= tuple(src
) # operands in (top)
61 self
.issue_i
= Signal(reset_less
=True) # Issue in (top)
63 self
.rd_pend_i
= Signal(n_reg
, reset_less
=True) # Read pend in (top)
64 self
.wr_pend_i
= Signal(n_reg
, reset_less
=True) # Write pend in (top)
65 self
.v_rd_rsel_o
= Signal(n_reg
, reset_less
=True) # Read pend out (bot)
66 self
.v_wr_rsel_o
= Signal(n_reg
, reset_less
=True) # Write pend out (bot)
68 self
.go_wr_i
= Signal(reset_less
=True) # Go Write in (left)
69 self
.go_rd_i
= Signal(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
.dst_rsel_o
= tuple(dsel
) # dest reg sel (bot)
77 self
.src_rsel_o
= tuple(rsel
) # src reg sel (bot)
79 # for Function Unit "forward progress" (horizontal)
80 self
.dst_fwd_o
= tuple(dfwd
) # dest FU fw (right)
81 self
.src_fwd_o
= tuple(fwd
) # src FU fw (right)
83 # for temporary (transitional) compatibility with old API
84 # number of dests used to be 1 (one) - increasing to n_dst
85 self
.dest_i
= self
.dst_i
[0]
86 self
.dest_rsel_o
= self
.dst_rsel_o
[0]
87 self
.dest_fwd_o
= self
.dst_fwd_o
[0]
89 def elaborate(self
, platform
):
91 # create source and dest SRLatches
93 for i
in range(self
.n_dst
):
94 dst_l
= SRLatch(sync
=False, llen
=self
.n_reg
)
95 m
.submodules
["dst%d_c" % (i
+1)] = dst_l
99 for i
in range(self
.n_src
):
100 src_l
= SRLatch(sync
=False, llen
=self
.n_reg
)
101 m
.submodules
["src%d_c" % (i
+1)] = src_l
104 # connect go_rd / go_wr (dest->wr, src->rd)
105 wr_die
= Signal(self
.n_reg
, reset_less
=True)
106 rd_die
= Signal(self
.n_reg
, reset_less
=True)
108 go_die
= self
.go_die_i
110 go_die
= Repl(self
.go_die_i
, self
.n_reg
)
111 m
.d
.comb
+= wr_die
.eq(Repl(self
.go_wr_i
, self
.n_reg
) | go_die
)
112 m
.d
.comb
+= rd_die
.eq(Repl(self
.go_rd_i
, self
.n_reg
) | go_die
)
113 for i
in range(self
.n_dst
):
114 m
.d
.comb
+= dst_c
[i
].r
.eq(wr_die
)
115 for i
in range(self
.n_src
):
116 m
.d
.comb
+= src_c
[i
].r
.eq(rd_die
)
118 # connect input reg bit (unary)
119 i_ext
= Repl(self
.issue_i
, self
.n_reg
)
120 for i
in range(self
.n_dst
):
121 m
.d
.comb
+= dst_c
[i
].s
.eq(i_ext
& self
.dst_i
[i
])
122 for i
in range(self
.n_src
):
123 m
.d
.comb
+= src_c
[i
].s
.eq(i_ext
& self
.src_i
[i
])
125 # connect up hazard checks: read-after-write and write-after-read
126 for i
in range(self
.n_dst
):
127 m
.d
.comb
+= self
.dst_fwd_o
[i
].eq(dst_c
[i
].q
& self
.rd_pend_i
)
128 for i
in range(self
.n_src
):
129 m
.d
.comb
+= self
.src_fwd_o
[i
].eq(src_c
[i
].q
& self
.wr_pend_i
)
131 # connect reg-sel outputs
132 rd_ext
= Repl(self
.go_rd_i
, self
.n_reg
)
133 wr_ext
= Repl(self
.go_wr_i
, self
.n_reg
)
134 for i
in range(self
.n_dst
):
135 m
.d
.comb
+= self
.dst_rsel_o
[i
].eq(dst_c
[i
].qlq
& wr_ext
)
136 for i
in range(self
.n_src
):
137 m
.d
.comb
+= self
.src_rsel_o
[i
].eq(src_c
[i
].qlq
& rd_ext
)
139 # to be accumulated to indicate if register is in use (globally)
140 # after ORing, is fed back in to rd_pend_i / wr_pend_i
142 for i
in range(self
.n_src
):
143 src_q
.append(src_c
[i
].qlq
)
144 m
.d
.comb
+= self
.v_rd_rsel_o
.eq(Cat(*src_q
).bool())
146 for i
in range(self
.n_dst
):
147 dst_q
.append(dst_c
[i
].qlq
)
148 m
.d
.comb
+= self
.v_wr_rsel_o
.eq(Cat(*dst_q
).bool())
153 yield from self
.dst_i
154 yield from self
.src_i
161 yield from self
.dst_rsel_o
162 yield from self
.src_rsel_o
163 yield from self
.dst_fwd_o
164 yield from self
.src_fwd_o
170 # XXX not up-to-date but hey
172 yield dut
.dest_i
.eq(1)
173 yield dut
.issue_i
.eq(1)
175 yield dut
.issue_i
.eq(0)
177 yield dut
.src_i
[0].eq(1)
178 yield dut
.issue_i
.eq(1)
182 yield dut
.issue_i
.eq(0)
184 yield dut
.go_rd_i
.eq(1)
186 yield dut
.go_rd_i
.eq(0)
188 yield dut
.go_wr_i
.eq(1)
190 yield dut
.go_wr_i
.eq(0)
194 dut
= DependencyRow(4, 2, 2, True)
195 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
196 with
open("test_drow.il", "w") as f
:
199 run_simulation(dut
, dcell_sim(dut
), vcd_name
='test_dcell.vcd')
201 if __name__
== '__main__':