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