provide array-selector on inputs
[ieee754fpu.git] / src / scoreboard / 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, Array, Const, 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 FnUnit(Elaboratable):
11 """ implements 11.4.8 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 * 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)
26 """
27 def __init__(self, wid, shadow_wid=0, n_dests=1):
28 self.reg_width = wid
29 self.n_dests = n_dests
30 self.shadow_wid = shadow_wid
31
32 # inputs
33 if n_dests > 1:
34 self.rfile_sel_i = Signal(max=n_dests, reset_less=True)
35 else:
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)
41
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)
45
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)
48
49 if shadow_wid:
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)
54
55 # outputs
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)
60
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)
64
65 def elaborate(self, platform):
66 m = Module()
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)
72 s_latches = []
73 for i in range(self.shadow_wid):
74 sh = ShadowFn()
75 setattr(m.submodules, "shadow%d" % i, sh)
76 s_latches.append(sh)
77
78 # shadow / recover (optional: shadow_wid > 0)
79 if self.shadow_wid:
80 recover = self.go_die_o
81 shadown = Signal(reset_less=True)
82 i_l = []
83 fail_l = []
84 good_l = []
85 shi_l = []
86 sho_l = []
87 rec_l = []
88 # get list of latch signals. really must be a better way to do this
89 for l in s_latches:
90 i_l.append(l.issue_i)
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())
102 else:
103 shadown = Const(1)
104 recover = Const(0)
105
106 # selector
107 wr_pend_o = self.wr_pend_o[self.rfile_sel_i]
108 xxxxable_o = self.xxxxable_o[self.rfile_sel_i]
109
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)
113
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)
117
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)
125
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)
132
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())
137
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)
144
145 return m
146
147 def __iter__(self):
148 yield self.dest_i
149 yield self.src1_i
150 yield self.src2_i
151 yield self.issue_i
152 yield self.go_write_i
153 yield self.go_read_i
154 yield self.req_rel_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
159 yield self.rd_pend_o
160 yield from self.wr_pend_o
161
162 def ports(self):
163 return list(self)
164
165
166 def int_fn_unit_sim(dut):
167 yield dut.dest_i.eq(1)
168 yield dut.issue_i.eq(1)
169 yield
170 yield dut.issue_i.eq(0)
171 yield
172 yield dut.src1_i.eq(1)
173 yield dut.issue_i.eq(1)
174 yield
175 yield
176 yield
177 yield dut.issue_i.eq(0)
178 yield
179 yield dut.go_read_i.eq(1)
180 yield
181 yield dut.go_read_i.eq(0)
182 yield
183 yield dut.go_write_i.eq(1)
184 yield
185 yield dut.go_write_i.eq(0)
186 yield
187
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:
192 f.write(vl)
193
194 run_simulation(dut, int_fn_unit_sim(dut), vcd_name='test_fn_unit.vcd')
195
196 if __name__ == '__main__':
197 test_int_fn_unit()