use shadow submodule instead of in fn unit
[ieee754fpu.git] / src / scoreboard / int_fn_unit.py
1 from nmigen.compat.sim import run_simulation
2 from nmigen.cli import verilog, rtlil
3 from nmigen import Module, Signal, Cat, Elaboratable
4 from nmutil.latch import SRLatch
5 from nmigen.lib.coding import Decoder
6
7 from shadow_fn import ShadowFn
8
9
10 class IntFnUnit(Elaboratable):
11 """ implements 11.4.8 integer function unit, p31
12 also implements optional shadowing 11.5.1, p55
13
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)
17
18 notes:
19
20 * req_rel_i (request release) is the direct equivalent of pipeline
21 "output valid"
22 * recover is a local python variable (actually go_die_o)
23 * when shadow_wid = 0, recover and shadown are Consts (i.e. do nothing)
24 """
25 def __init__(self, wid, shadow_wid=0):
26 self.reg_width = wid
27 self.shadow_wid = shadow_wid
28
29 # inputs
30 self.dest_i = Signal(wid, reset_less=True) # Dest in (top)
31 self.src1_i = Signal(wid, reset_less=True) # oper1 in (top)
32 self.src2_i = Signal(wid, reset_less=True) # oper2 in (top)
33 self.issue_i = Signal(reset_less=True) # Issue in (top)
34
35 self.go_write_i = Signal(reset_less=True) # Go Write in (left)
36 self.go_read_i = Signal(reset_less=True) # Go Read in (left)
37 self.req_rel_i = Signal(wid, reset_less=True) # request release (left)
38
39 self.g_rd_pend_i = Signal(wid, reset_less=True) # global rd (right)
40 self.g_wr_pend_i = Signal(wid, reset_less=True) # global wr (right)
41
42 if shadow_wid:
43 self.shadow_i = Signal(shadow_wid, reset_less=True)
44 self.s_fail_i = Signal(shadow_wid, reset_less=True)
45 self.s_good_i = Signal(shadow_wid, reset_less=True)
46 self.go_die_o = Signal(reset_less=True)
47
48 # outputs
49 self.readable_o = Signal(reset_less=True) # Readable out (right)
50 self.writable_o = Signal(reset_less=True) # Writable out (right)
51 self.busy_o = Signal(reset_less=True) # busy out (left)
52
53 self.rd_pend_o = Signal(wid, reset_less=True) # rd pending (right)
54 self.wr_pend_o = Signal(wid, reset_less=True) # wr pending (right)
55
56 def elaborate(self, platform):
57 m = Module()
58 m.submodules.rd_l = rd_l = SRLatch(sync=False)
59 m.submodules.wr_l = wr_l = SRLatch(sync=False)
60 m.submodules.dest_d = dest_d = Decoder(self.reg_width)
61 m.submodules.src1_d = src1_d = Decoder(self.reg_width)
62 m.submodules.src2_d = src2_d = Decoder(self.reg_width)
63 s_latches = []
64 for i in range(self.shadow_wid):
65 sh = ShadowFn()
66 setattr(m.submodules, "shadow%d" % i, sh)
67 s_latches.append(sh)
68
69 # shadow / recover (optional: shadow_wid > 0)
70 if self.shadow_wid:
71 recover = self.go_die_o
72 shadown = Signal(reset_less=True)
73 i_l = []
74 fail_l = []
75 good_l = []
76 shi_l = []
77 sho_l = []
78 rec_l = []
79 # get list of latch signals. really must be a better way to do this
80 for l in s_latches:
81 i_l.append(l.issue_i)
82 shi_l.append(l.shadow_i)
83 fail_l.append(l.s_fail_i)
84 good_l.append(l.s_good_i)
85 sho_l.append(l.shadow_o)
86 rec_l.append(l.recover_o)
87 m.d.comb += Cat(*i_l).eq(self.issue_i)
88 m.d.comb += Cat(*fail_l).eq(self.s_fail_i)
89 m.d.comb += Cat(*good_l).eq(self.s_good_i)
90 m.d.comb += Cat(*shi_l).eq(self.shadow_i)
91 m.d.comb += shadown.eq(~(Cat(*sho_l).bool()))
92 m.d.comb += recover.eq(Cat(*rec_l).bool())
93 else:
94 shadown = Const(1)
95 recover = Const(0)
96
97 # go_write latch: reset on go_write HI, set on issue
98 m.d.comb += wr_l.s.eq(self.issue_i)
99 m.d.comb += wr_l.r.eq(self.go_write_i | recover)
100
101 # src1 latch: reset on go_read HI, set on issue
102 m.d.comb += rd_l.s.eq(self.issue_i)
103 m.d.comb += rd_l.r.eq(self.go_read_i | recover)
104
105 # dest decoder: write-pending out
106 m.d.comb += dest_d.i.eq(self.dest_i)
107 m.d.comb += dest_d.n.eq(wr_l.qn) # decode is inverted
108 m.d.comb += self.busy_o.eq(wr_l.q) # busy if set
109 m.d.comb += self.wr_pend_o.eq(dest_d.o)
110
111 # src1/src2 decoder: read-pending out
112 m.d.comb += src1_d.i.eq(self.src1_i)
113 m.d.comb += src1_d.n.eq(rd_l.qn) # decode is inverted
114 m.d.comb += src2_d.i.eq(self.src2_i)
115 m.d.comb += src2_d.n.eq(rd_l.qn) # decode is inverted
116 m.d.comb += self.rd_pend_o.eq(src1_d.o | src2_d.o)
117
118 # readable output signal
119 int_g_wr = Signal(self.reg_width, reset_less=True)
120 m.d.comb += int_g_wr.eq(self.g_wr_pend_i & self.rd_pend_o)
121 m.d.comb += self.readable_o.eq(int_g_wr.bool())
122
123 # writable output signal
124 int_g_rw = Signal(self.reg_width, reset_less=True)
125 g_rw = Signal(reset_less=True)
126 m.d.comb += int_g_rw.eq(self.g_rd_pend_i & self.wr_pend_o)
127 m.d.comb += g_rw.eq(~int_g_rw.bool())
128 m.d.comb += self.writable_o.eq(g_rw & rd_l.q & self.req_rel_i & shadown)
129
130 return m
131
132 def __iter__(self):
133 yield self.dest_i
134 yield self.src1_i
135 yield self.src2_i
136 yield self.issue_i
137 yield self.go_write_i
138 yield self.go_read_i
139 yield self.req_rel_i
140 yield self.g_rd_pend_i
141 yield self.g_wr_pend_i
142 yield self.readable_o
143 yield self.writable_o
144 yield self.rd_pend_o
145 yield self.wr_pend_o
146
147 def ports(self):
148 return list(self)
149
150
151 def int_fn_unit_sim(dut):
152 yield dut.dest_i.eq(1)
153 yield dut.issue_i.eq(1)
154 yield
155 yield dut.issue_i.eq(0)
156 yield
157 yield dut.src1_i.eq(1)
158 yield dut.issue_i.eq(1)
159 yield
160 yield
161 yield
162 yield dut.issue_i.eq(0)
163 yield
164 yield dut.go_read_i.eq(1)
165 yield
166 yield dut.go_read_i.eq(0)
167 yield
168 yield dut.go_write_i.eq(1)
169 yield
170 yield dut.go_write_i.eq(0)
171 yield
172
173 def test_int_fn_unit():
174 dut = IntFnUnit(32, 2)
175 vl = rtlil.convert(dut, ports=dut.ports())
176 with open("test_int_fn_unit.il", "w") as f:
177 f.write(vl)
178
179 run_simulation(dut, int_fn_unit_sim(dut), vcd_name='test_int_fn_unit.vcd')
180
181 if __name__ == '__main__':
182 test_int_fn_unit()