1 from nmigen
.compat
.sim
import run_simulation
2 from nmigen
.cli
import verilog
, rtlil
3 from nmigen
import Module
, Signal
, Cat
, Array
, Const
, Elaboratable
4 from nmutil
.latch
import SRLatch
5 from nmigen
.lib
.coding
import Decoder
7 from shadow_fn
import ShadowFn
10 class FnUnit(Elaboratable
):
11 """ implements 11.4.8 function unit, p31
12 also implements optional shadowing 11.5.1, p55
14 shadowing can be used for branches as well as exceptions (interrupts),
15 load/store hold (exceptions again), and vector-element predication
16 (once the predicate is known, which it may not be at instruction issue)
20 * dest_i / src1_i / src2_i are in *binary*, whereas
21 * g_rd_pend_i / g_wr_pend_i and rd_pend_o / wr_pend_o are UNARY vectors
22 * req_rel_i (request release) is the direct equivalent of pipeline
23 "output valid" (valid_o)
24 * recover is a local python variable (actually go_die_o)
25 * when shadow_wid = 0, recover and shadown are Consts (i.e. do nothing)
27 def __init__(self
, wid
, shadow_wid
=0, n_dests
=1):
29 self
.n_dests
= n_dests
30 self
.shadow_wid
= shadow_wid
34 self
.rfile_sel_i
= Signal(max=n_dests
, reset_less
=True)
36 self
.rfile_sel_i
= Const(0) # no selection. gets Array[0]
37 self
.dest_i
= Signal(max=wid
, reset_less
=True) # Dest R# in (top)
38 self
.src1_i
= Signal(max=wid
, reset_less
=True) # oper1 R# in (top)
39 self
.src2_i
= Signal(max=wid
, reset_less
=True) # oper2 R# in (top)
40 self
.issue_i
= Signal(reset_less
=True) # Issue in (top)
42 self
.go_write_i
= Signal(reset_less
=True) # Go Write in (left)
43 self
.go_read_i
= Signal(reset_less
=True) # Go Read in (left)
44 self
.req_rel_i
= Signal(reset_less
=True) # request release (left)
46 self
.g_rd_pend_i
= Signal(wid
, reset_less
=True) # global rd (right)
47 self
.g_wr_pend_i
= Signal(wid
, reset_less
=True) # global wr (right)
50 self
.shadow_i
= Signal(shadow_wid
, reset_less
=True)
51 self
.s_fail_i
= Signal(shadow_wid
, reset_less
=True)
52 self
.s_good_i
= Signal(shadow_wid
, reset_less
=True)
53 self
.go_die_o
= Signal(reset_less
=True)
56 self
.readable_o
= Signal(reset_less
=True) # Readable out (right)
57 self
.xxxxable_o
= Array(Signal(reset_less
=True) \
58 for i
in range(n_dests
)) # XXXXable out (right)
59 self
.busy_o
= Signal(reset_less
=True) # busy out (left)
61 self
.rd_pend_o
= Signal(wid
, reset_less
=True) # rd pending (right)
62 self
.wr_pend_o
= Array(Signal(wid
, reset_less
=True) \
63 for i
in range(n_dests
))# wr pending (right)
65 def elaborate(self
, platform
):
67 m
.submodules
.rd_l
= rd_l
= SRLatch(sync
=False)
68 m
.submodules
.wr_l
= wr_l
= SRLatch(sync
=False)
69 m
.submodules
.dest_d
= dest_d
= Decoder(self
.reg_width
)
70 m
.submodules
.src1_d
= src1_d
= Decoder(self
.reg_width
)
71 m
.submodules
.src2_d
= src2_d
= Decoder(self
.reg_width
)
73 for i
in range(self
.shadow_wid
):
75 setattr(m
.submodules
, "shadow%d" % i
, sh
)
78 # shadow / recover (optional: shadow_wid > 0)
80 recover
= self
.go_die_o
81 shadown
= Signal(reset_less
=True)
88 # get list of latch signals. really must be a better way to do this
91 shi_l
.append(l
.shadow_i
)
92 fail_l
.append(l
.s_fail_i
)
93 good_l
.append(l
.s_good_i
)
94 sho_l
.append(l
.shadow_o
)
95 rec_l
.append(l
.recover_o
)
96 m
.d
.comb
+= Cat(*i_l
).eq(self
.issue_i
)
97 m
.d
.comb
+= Cat(*fail_l
).eq(self
.s_fail_i
)
98 m
.d
.comb
+= Cat(*good_l
).eq(self
.s_good_i
)
99 m
.d
.comb
+= Cat(*shi_l
).eq(self
.shadow_i
)
100 m
.d
.comb
+= shadown
.eq(~
(Cat(*sho_l
).bool()))
101 m
.d
.comb
+= recover
.eq(Cat(*rec_l
).bool())
107 wr_pend_o
= self
.wr_pend_o
[self
.rfile_sel_i
]
108 xxxxable_o
= self
.xxxxable_o
[self
.rfile_sel_i
]
110 # go_write latch: reset on go_write HI, set on issue
111 m
.d
.comb
+= wr_l
.s
.eq(self
.issue_i
)
112 m
.d
.comb
+= wr_l
.r
.eq(self
.go_write_i | recover
)
114 # src1 latch: reset on go_read HI, set on issue
115 m
.d
.comb
+= rd_l
.s
.eq(self
.issue_i
)
116 m
.d
.comb
+= rd_l
.r
.eq(self
.go_read_i | recover
)
118 # dest decoder: write-pending out
119 m
.d
.comb
+= dest_d
.i
.eq(self
.dest_i
)
120 m
.d
.comb
+= dest_d
.n
.eq(wr_l
.qn
) # decode is inverted
121 m
.d
.comb
+= self
.busy_o
.eq(wr_l
.q
) # busy if set
122 for i
in range(self
.n_dests
):
123 m
.d
.comb
+= self
.wr_pend_o
[i
].eq(0)
124 m
.d
.comb
+= wr_pend_o
.eq(dest_d
.o
)
126 # src1/src2 decoder: read-pending out
127 m
.d
.comb
+= src1_d
.i
.eq(self
.src1_i
)
128 m
.d
.comb
+= src1_d
.n
.eq(rd_l
.qn
) # decode is inverted
129 m
.d
.comb
+= src2_d
.i
.eq(self
.src2_i
)
130 m
.d
.comb
+= src2_d
.n
.eq(rd_l
.qn
) # decode is inverted
131 m
.d
.comb
+= self
.rd_pend_o
.eq(src1_d
.o | src2_d
.o
)
133 # readable output signal
134 int_g_wr
= Signal(self
.reg_width
, reset_less
=True)
135 m
.d
.comb
+= int_g_wr
.eq(self
.g_wr_pend_i
& self
.rd_pend_o
)
136 m
.d
.comb
+= self
.readable_o
.eq(int_g_wr
.bool())
138 # xxxxable output signal
139 int_g_rw
= Signal(self
.reg_width
, reset_less
=True)
140 g_rw
= Signal(reset_less
=True)
141 m
.d
.comb
+= int_g_rw
.eq(self
.g_rd_pend_i
& wr_pend_o
)
142 m
.d
.comb
+= g_rw
.eq(~int_g_rw
.bool())
143 m
.d
.comb
+= xxxxable_o
.eq(g_rw
& rd_l
.q
& self
.req_rel_i
& shadown
)
152 yield self
.go_write_i
155 yield self
.g_rd_pend_i
156 yield self
.g_wr_pend_i
157 yield self
.readable_o
158 yield from self
.xxxxable_o
160 yield from self
.wr_pend_o
166 def int_fn_unit_sim(dut
):
167 yield dut
.dest_i
.eq(1)
168 yield dut
.issue_i
.eq(1)
170 yield dut
.issue_i
.eq(0)
172 yield dut
.src1_i
.eq(1)
173 yield dut
.issue_i
.eq(1)
177 yield dut
.issue_i
.eq(0)
179 yield dut
.go_read_i
.eq(1)
181 yield dut
.go_read_i
.eq(0)
183 yield dut
.go_write_i
.eq(1)
185 yield dut
.go_write_i
.eq(0)
188 def test_int_fn_unit():
189 dut
= FnUnit(32, 2, 2)
190 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
191 with
open("test_fn_unit.il", "w") as f
:
194 run_simulation(dut
, int_fn_unit_sim(dut
), vcd_name
='test_fn_unit.vcd')
196 if __name__
== '__main__':