experiment morphing ALU to take subset of Decode2ToExecute1
[soc.git] / src / soc / experiment / alu_hier.py
1 """*Experimental* ALU: based on nmigen alu_hier.py, includes branch-compare ALU
2
3 This ALU is *deliberately* designed to add in (unnecessary) delays into
4 different operations so as to be able to test the 6600-style matrices
5 and the CompUnits. Countdown timers wait for (defined) periods before
6 indicating that the output is valid
7
8 A "real" integer ALU would place the answers onto the output bus after
9 only one cycle (sync)
10 """
11
12 from nmigen import Elaboratable, Signal, Module, Const, Mux
13 from nmigen.hdl.rec import Record, Layout
14 from nmigen.cli import main
15 from nmigen.cli import verilog, rtlil
16 from nmigen.compat.sim import run_simulation
17
18 from soc.decoder.power_enums import InternalOp, CryIn
19
20 import operator
21
22
23 class CompALUOpSubset(Record):
24 """CompALUOpSubset
25
26 a copy of the relevant subset information from Decode2Execute1Type
27 needed for ALU operations.
28 """
29 def __init__(self):
30 layout = (('insn_type', InternalOp),
31 ('nia', 64),
32 ('imm_data', Layout((("imm", 64), ("imm_ok", 1)))),
33 #'cr = Signal(32, reset_less=True) # NO: this is from the CR SPR
34 #'xerc = XerBits() # NO: this is from the XER SPR
35 ('lk', 1),
36 ('rc', Layout((("rc", 1), ("rc_ok", 1)))),
37 ('oe', Layout((("oe", 1), ("oe_ok", 1)))),
38 ('invert_a', 1),
39 ('invert_out', 1),
40 ('input_carry', CryIn),
41 ('output_carry', 1),
42 ('input_cr', 1),
43 ('output_cr', 1),
44 ('is_32bit', 1),
45 ('is_signed', 1),
46 ('byte_reverse', 1),
47 ('sign_extend', 1))
48
49 Record.__init__(self, Layout(layout))
50
51 # grrr. Record does not have kwargs
52 self.insn_type.reset_less = True
53 self.nia.reset_less = True
54 #self.cr = Signal(32, reset_less = True
55 #self.xerc = XerBits(
56 self.lk.reset_less = True
57 self.invert_a.reset_less = True
58 self.invert_out.reset_less = True
59 self.input_carry.reset_less = True
60 self.output_carry.reset_less = True
61 self.input_cr.reset_less = True
62 self.output_cr.reset_less = True
63 self.is_32bit.reset_less = True
64 self.is_signed.reset_less = True
65 self.byte_reverse.reset_less = True
66 self.sign_extend.reset_less = True
67
68 def ports(self):
69 return [self.insn_type,
70 self.nia,
71 #self.cr,
72 #self.xerc,
73 self.lk,
74 self.invert_a,
75 self.invert_out,
76 self.input_carry,
77 self.output_carry,
78 self.input_cr,
79 self.output_cr,
80 self.is_32bit,
81 self.is_signed,
82 self.byte_reverse,
83 self.sign_extend,
84 ]
85
86 class Adder(Elaboratable):
87 def __init__(self, width):
88 self.invert_a = Signal()
89 self.a = Signal(width)
90 self.b = Signal(width)
91 self.o = Signal(width)
92
93 def elaborate(self, platform):
94 m = Module()
95 with m.If(self.invert_a):
96 m.d.comb += self.o.eq((~self.a) + self.b)
97 with m.Else():
98 m.d.comb += self.o.eq(self.a + self.b)
99 return m
100
101
102 class Subtractor(Elaboratable):
103 def __init__(self, width):
104 self.a = Signal(width)
105 self.b = Signal(width)
106 self.o = Signal(width)
107
108 def elaborate(self, platform):
109 m = Module()
110 m.d.comb += self.o.eq(self.a - self.b)
111 return m
112
113
114 class Multiplier(Elaboratable):
115 def __init__(self, width):
116 self.a = Signal(width)
117 self.b = Signal(width)
118 self.o = Signal(width)
119
120 def elaborate(self, platform):
121 m = Module()
122 m.d.comb += self.o.eq(self.a * self.b)
123 return m
124
125
126 class Shifter(Elaboratable):
127 def __init__(self, width):
128 self.width = width
129 self.a = Signal(width)
130 self.b = Signal(width)
131 self.o = Signal(width)
132
133 def elaborate(self, platform):
134 m = Module()
135 btrunc = Signal(self.width)
136 m.d.comb += btrunc.eq(self.b & Const((1<<self.width)-1))
137 m.d.comb += self.o.eq(self.a >> btrunc)
138 return m
139
140
141 class ALU(Elaboratable):
142 def __init__(self, width):
143 self.p_valid_i = Signal()
144 self.p_ready_o = Signal()
145 self.n_ready_i = Signal()
146 self.n_valid_o = Signal()
147 self.counter = Signal(4)
148 self.op = CompALUOpSubset()
149 self.a = Signal(width)
150 self.b = Signal(width)
151 self.o = Signal(width)
152 self.width = width
153
154 def elaborate(self, platform):
155 m = Module()
156 add = Adder(self.width)
157 mul = Multiplier(self.width)
158 shf = Shifter(self.width)
159
160 m.submodules.add = add
161 m.submodules.mul = mul
162 m.submodules.shf = shf
163
164 # really should not activate absolutely all ALU inputs like this
165 for mod in [add, mul, shf]:
166 m.d.comb += [
167 mod.a.eq(self.a),
168 mod.b.eq(self.b),
169 ]
170
171 # pass invert (and carry later)
172 m.d.comb += add.invert_a.eq(self.op.invert_a)
173
174 go_now = Signal(reset_less=True) # testing no-delay ALU
175
176 with m.If(self.p_valid_i):
177 # input is valid. next check, if we already said "ready" or not
178 with m.If(~self.p_ready_o):
179 # we didn't say "ready" yet, so say so and initialise
180 m.d.sync += self.p_ready_o.eq(1)
181
182 # as this is a "fake" pipeline, just grab the output right now
183 with m.If(self.op.insn_type == InternalOp.OP_ADD):
184 m.d.sync += self.o.eq(add.o)
185 with m.Elif(self.op.insn_type == InternalOp.OP_MUL_L64):
186 m.d.sync += self.o.eq(mul.o)
187 with m.Elif(self.op.insn_type == InternalOp.OP_SHR):
188 m.d.sync += self.o.eq(shf.o)
189 # TODO: SUB
190
191 # NOTE: all of these are fake, just something to test
192
193 # MUL, to take 5 instructions
194 with m.If(self.op.insn_type == InternalOp.OP_MUL_L64.value):
195 m.d.sync += self.counter.eq(5)
196 # SHIFT to take 7
197 with m.Elif(self.op.insn_type == InternalOp.OP_SHR.value):
198 m.d.sync += self.counter.eq(7)
199 # SUB to take 1, straight away
200 with m.If(self.op.insn_type == InternalOp.OP_ADD.value):
201 m.d.sync += self.counter.eq(1)
202 m.d.comb += go_now.eq(1)
203 # ADD to take 2
204 with m.Else():
205 m.d.sync += self.counter.eq(2)
206 with m.Else():
207 # input says no longer valid, so drop ready as well.
208 # a "proper" ALU would have had to sync in the opcode and a/b ops
209 m.d.sync += self.p_ready_o.eq(0)
210
211 # ok so the counter's running: when it gets to 1, fire the output
212 with m.If((self.counter == 1) | go_now):
213 # set the output as valid if the recipient is ready for it
214 m.d.sync += self.n_valid_o.eq(1)
215 with m.If(self.n_ready_i & self.n_valid_o):
216 m.d.sync += self.n_valid_o.eq(0)
217 # recipient said it was ready: reset back to known-good.
218 m.d.sync += self.counter.eq(0) # reset the counter
219 m.d.sync += self.o.eq(0) # clear the output for tidiness sake
220
221 # countdown to 1 (transition from 1 to 0 only on acknowledgement)
222 with m.If(self.counter > 1):
223 m.d.sync += self.counter.eq(self.counter - 1)
224
225 return m
226
227 def __iter__(self):
228 yield from self.op.ports()
229 yield self.a
230 yield self.b
231 yield self.o
232
233 def ports(self):
234 return list(self)
235
236
237 class BranchOp(Elaboratable):
238 def __init__(self, width, op):
239 self.a = Signal(width)
240 self.b = Signal(width)
241 self.o = Signal(width)
242 self.op = op
243
244 def elaborate(self, platform):
245 m = Module()
246 m.d.comb += self.o.eq(Mux(self.op(self.a, self.b), 1, 0))
247 return m
248
249
250 class BranchALU(Elaboratable):
251 def __init__(self, width):
252 self.p_valid_i = Signal()
253 self.p_ready_o = Signal()
254 self.n_ready_i = Signal()
255 self.n_valid_o = Signal()
256 self.counter = Signal(4)
257 self.op = Signal(2)
258 self.a = Signal(width)
259 self.b = Signal(width)
260 self.o = Signal(width)
261 self.width = width
262
263 def elaborate(self, platform):
264 m = Module()
265 bgt = BranchOp(self.width, operator.gt)
266 blt = BranchOp(self.width, operator.lt)
267 beq = BranchOp(self.width, operator.eq)
268 bne = BranchOp(self.width, operator.ne)
269
270 m.submodules.bgt = bgt
271 m.submodules.blt = blt
272 m.submodules.beq = beq
273 m.submodules.bne = bne
274 for mod in [bgt, blt, beq, bne]:
275 m.d.comb += [
276 mod.a.eq(self.a),
277 mod.b.eq(self.b),
278 ]
279
280 go_now = Signal(reset_less=True) # testing no-delay ALU
281 with m.If(self.p_valid_i):
282 # input is valid. next check, if we already said "ready" or not
283 with m.If(~self.p_ready_o):
284 # we didn't say "ready" yet, so say so and initialise
285 m.d.sync += self.p_ready_o.eq(1)
286
287 # as this is a "fake" pipeline, just grab the output right now
288 with m.Switch(self.op):
289 for i, mod in enumerate([bgt, blt, beq, bne]):
290 with m.Case(i):
291 m.d.sync += self.o.eq(mod.o)
292 m.d.sync += self.counter.eq(5) # branch to take 5 cycles (fake)
293 #m.d.comb += go_now.eq(1)
294 with m.Else():
295 # input says no longer valid, so drop ready as well.
296 # a "proper" ALU would have had to sync in the opcode and a/b ops
297 m.d.sync += self.p_ready_o.eq(0)
298
299 # ok so the counter's running: when it gets to 1, fire the output
300 with m.If((self.counter == 1) | go_now):
301 # set the output as valid if the recipient is ready for it
302 m.d.sync += self.n_valid_o.eq(1)
303 with m.If(self.n_ready_i & self.n_valid_o):
304 m.d.sync += self.n_valid_o.eq(0)
305 # recipient said it was ready: reset back to known-good.
306 m.d.sync += self.counter.eq(0) # reset the counter
307 m.d.sync += self.o.eq(0) # clear the output for tidiness sake
308
309 # countdown to 1 (transition from 1 to 0 only on acknowledgement)
310 with m.If(self.counter > 1):
311 m.d.sync += self.counter.eq(self.counter - 1)
312
313 return m
314
315 def __iter__(self):
316 yield self.op
317 yield self.a
318 yield self.b
319 yield self.o
320
321 def ports(self):
322 return list(self)
323
324 def run_op(dut, a, b, op, inv_a=0):
325 yield dut.a.eq(a)
326 yield dut.b.eq(b)
327 yield dut.op.insn_type.eq(op)
328 yield dut.op.invert_a.eq(inv_a)
329 yield dut.n_ready_i.eq(0)
330 yield dut.p_valid_i.eq(1)
331 yield
332 while True:
333 yield
334 n_valid_o = yield dut.n_valid_o
335 if n_valid_o:
336 break
337 yield
338
339 result = yield dut.o
340 yield dut.p_valid_i.eq(0)
341 yield dut.n_ready_i.eq(0)
342 yield
343
344 return result
345
346
347 def alu_sim(dut):
348 result = yield from run_op(dut, 5, 3, InternalOp.OP_ADD)
349 print ("alu_sim add", result)
350 assert (result == 8)
351
352 result = yield from run_op(dut, 2, 3, InternalOp.OP_MUL_L64)
353 print ("alu_sim mul", result)
354 assert (result == 6)
355
356 result = yield from run_op(dut, 5, 3, InternalOp.OP_ADD, inv_a=1)
357 print ("alu_sim add-inv", result)
358 assert (result == 65533)
359
360
361 def test_alu():
362 alu = ALU(width=16)
363 run_simulation(alu, alu_sim(alu), vcd_name='test_alusim.vcd')
364
365 vl = rtlil.convert(alu, ports=alu.ports())
366 with open("test_alu.il", "w") as f:
367 f.write(vl)
368
369
370 if __name__ == "__main__":
371 test_alu()
372
373 alu = BranchALU(width=16)
374 vl = rtlil.convert(alu, ports=alu.ports())
375 with open("test_branch_alu.il", "w") as f:
376 f.write(vl)
377