1 """*Experimental* ALU: based on nmigen alu_hier.py, includes branch-compare ALU
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
8 A "real" integer ALU would place the answers onto the output bus after
12 from nmigen
import Elaboratable
, Signal
, Module
, Const
, Mux
, Array
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
18 from soc
.decoder
.power_enums
import InternalOp
, Function
, CryIn
23 class CompALUOpSubset(Record
):
26 a copy of the relevant subset information from Decode2Execute1Type
27 needed for ALU operations. use with eq_from_execute1 (below) to
30 def __init__(self
, name
=None):
31 layout
= (('insn_type', InternalOp
),
32 ('fn_unit', Function
),
34 ('imm_data', Layout((("imm", 64), ("imm_ok", 1)))),
35 #'cr = Signal(32, reset_less=True) # NO: this is from the CR SPR
36 #'xerc = XerBits() # NO: this is from the XER SPR
38 ('rc', Layout((("rc", 1), ("rc_ok", 1)))),
39 ('oe', Layout((("oe", 1), ("oe_ok", 1)))),
42 ('input_carry', CryIn
),
48 ('data_len', 4), # TODO: should be in separate CompLDSTSubset
52 Record
.__init
__(self
, Layout(layout
), name
=name
)
54 # grrr. Record does not have kwargs
55 self
.insn_type
.reset_less
= True
56 self
.fn_unit
.reset_less
= True
57 self
.nia
.reset_less
= True
58 #self.cr = Signal(32, reset_less = True
60 self
.lk
.reset_less
= True
61 self
.invert_a
.reset_less
= True
62 self
.invert_out
.reset_less
= True
63 self
.input_carry
.reset_less
= True
64 self
.output_carry
.reset_less
= True
65 self
.input_cr
.reset_less
= True
66 self
.output_cr
.reset_less
= True
67 self
.is_32bit
.reset_less
= True
68 self
.is_signed
.reset_less
= True
69 self
.data_len
.reset_less
= True
70 self
.byte_reverse
.reset_less
= True
71 self
.sign_extend
.reset_less
= True
73 def eq_from_execute1(self
, other
):
74 """ use this to copy in from Decode2Execute1Type
77 for fname
, sig
in self
.fields
.items():
78 eqfrom
= other
.fields
[fname
]
79 res
.append(sig
.eq(eqfrom
))
83 return [self
.insn_type
,
102 class Adder(Elaboratable
):
103 def __init__(self
, width
):
104 self
.invert_a
= Signal()
105 self
.a
= Signal(width
)
106 self
.b
= Signal(width
)
107 self
.o
= Signal(width
)
109 def elaborate(self
, platform
):
111 with m
.If(self
.invert_a
):
112 m
.d
.comb
+= self
.o
.eq((~self
.a
) + self
.b
)
114 m
.d
.comb
+= self
.o
.eq(self
.a
+ self
.b
)
118 class Subtractor(Elaboratable
):
119 def __init__(self
, width
):
120 self
.a
= Signal(width
)
121 self
.b
= Signal(width
)
122 self
.o
= Signal(width
)
124 def elaborate(self
, platform
):
126 m
.d
.comb
+= self
.o
.eq(self
.a
- self
.b
)
130 class Multiplier(Elaboratable
):
131 def __init__(self
, width
):
132 self
.a
= Signal(width
)
133 self
.b
= Signal(width
)
134 self
.o
= Signal(width
)
136 def elaborate(self
, platform
):
138 m
.d
.comb
+= self
.o
.eq(self
.a
* self
.b
)
142 class Shifter(Elaboratable
):
143 def __init__(self
, width
):
145 self
.a
= Signal(width
)
146 self
.b
= Signal(width
)
147 self
.o
= Signal(width
)
149 def elaborate(self
, platform
):
151 btrunc
= Signal(self
.width
)
152 m
.d
.comb
+= btrunc
.eq(self
.b
& Const((1<<self
.width
)-1))
153 m
.d
.comb
+= self
.o
.eq(self
.a
>> btrunc
)
157 class ALU(Elaboratable
):
158 def __init__(self
, width
):
159 self
.p_valid_i
= Signal()
160 self
.p_ready_o
= Signal()
161 self
.n_ready_i
= Signal()
162 self
.n_valid_o
= Signal()
163 self
.counter
= Signal(4)
164 self
.op
= CompALUOpSubset()
166 i
.append(Signal(width
, name
="i1"))
167 i
.append(Signal(width
, name
="i2"))
169 self
.a
, self
.b
= i
[0], i
[1]
170 self
.out
= Array([Signal(width
)])
174 def elaborate(self
, platform
):
176 add
= Adder(self
.width
)
177 mul
= Multiplier(self
.width
)
178 shf
= Shifter(self
.width
)
180 m
.submodules
.add
= add
181 m
.submodules
.mul
= mul
182 m
.submodules
.shf
= shf
184 # really should not activate absolutely all ALU inputs like this
185 for mod
in [add
, mul
, shf
]:
191 # pass invert (and carry later)
192 m
.d
.comb
+= add
.invert_a
.eq(self
.op
.invert_a
)
194 go_now
= Signal(reset_less
=True) # testing no-delay ALU
196 with m
.If(self
.p_valid_i
):
197 # input is valid. next check, if we already said "ready" or not
198 with m
.If(~self
.p_ready_o
):
199 # we didn't say "ready" yet, so say so and initialise
200 m
.d
.sync
+= self
.p_ready_o
.eq(1)
202 # as this is a "fake" pipeline, just grab the output right now
203 with m
.If(self
.op
.insn_type
== InternalOp
.OP_ADD
):
204 m
.d
.sync
+= self
.o
.eq(add
.o
)
205 with m
.Elif(self
.op
.insn_type
== InternalOp
.OP_MUL_L64
):
206 m
.d
.sync
+= self
.o
.eq(mul
.o
)
207 with m
.Elif(self
.op
.insn_type
== InternalOp
.OP_SHR
):
208 m
.d
.sync
+= self
.o
.eq(shf
.o
)
211 # NOTE: all of these are fake, just something to test
213 # MUL, to take 5 instructions
214 with m
.If(self
.op
.insn_type
== InternalOp
.OP_MUL_L64
):
215 m
.d
.sync
+= self
.counter
.eq(5)
217 with m
.Elif(self
.op
.insn_type
== InternalOp
.OP_SHR
):
218 m
.d
.sync
+= self
.counter
.eq(7)
219 # ADD/SUB to take 2, straight away
220 with m
.If(self
.op
.insn_type
== InternalOp
.OP_ADD
):
221 m
.d
.sync
+= self
.counter
.eq(3)
222 # others to take 1, straight away
224 m
.d
.comb
+= go_now
.eq(1)
225 m
.d
.sync
+= self
.counter
.eq(1)
228 # input says no longer valid, so drop ready as well.
229 # a "proper" ALU would have had to sync in the opcode and a/b ops
230 m
.d
.sync
+= self
.p_ready_o
.eq(0)
232 # ok so the counter's running: when it gets to 1, fire the output
233 with m
.If((self
.counter
== 1) | go_now
):
234 # set the output as valid if the recipient is ready for it
235 m
.d
.sync
+= self
.n_valid_o
.eq(1)
236 with m
.If(self
.n_ready_i
& self
.n_valid_o
):
237 m
.d
.sync
+= self
.n_valid_o
.eq(0)
238 # recipient said it was ready: reset back to known-good.
239 m
.d
.sync
+= self
.counter
.eq(0) # reset the counter
240 m
.d
.sync
+= self
.o
.eq(0) # clear the output for tidiness sake
242 # countdown to 1 (transition from 1 to 0 only on acknowledgement)
243 with m
.If(self
.counter
> 1):
244 m
.d
.sync
+= self
.counter
.eq(self
.counter
- 1)
249 yield from self
.op
.ports()
258 class BranchOp(Elaboratable
):
259 def __init__(self
, width
, op
):
260 self
.a
= Signal(width
)
261 self
.b
= Signal(width
)
262 self
.o
= Signal(width
)
265 def elaborate(self
, platform
):
267 m
.d
.comb
+= self
.o
.eq(Mux(self
.op(self
.a
, self
.b
), 1, 0))
271 class BranchALU(Elaboratable
):
272 def __init__(self
, width
):
273 self
.p_valid_i
= Signal()
274 self
.p_ready_o
= Signal()
275 self
.n_ready_i
= Signal()
276 self
.n_valid_o
= Signal()
277 self
.counter
= Signal(4)
280 i
.append(Signal(width
, name
="i1"))
281 i
.append(Signal(width
, name
="i2"))
283 self
.a
, self
.b
= i
[0], i
[1]
284 self
.out
= Array([Signal(width
)])
288 def elaborate(self
, platform
):
290 bgt
= BranchOp(self
.width
, operator
.gt
)
291 blt
= BranchOp(self
.width
, operator
.lt
)
292 beq
= BranchOp(self
.width
, operator
.eq
)
293 bne
= BranchOp(self
.width
, operator
.ne
)
295 m
.submodules
.bgt
= bgt
296 m
.submodules
.blt
= blt
297 m
.submodules
.beq
= beq
298 m
.submodules
.bne
= bne
299 for mod
in [bgt
, blt
, beq
, bne
]:
305 go_now
= Signal(reset_less
=True) # testing no-delay ALU
306 with m
.If(self
.p_valid_i
):
307 # input is valid. next check, if we already said "ready" or not
308 with m
.If(~self
.p_ready_o
):
309 # we didn't say "ready" yet, so say so and initialise
310 m
.d
.sync
+= self
.p_ready_o
.eq(1)
312 # as this is a "fake" pipeline, just grab the output right now
313 with m
.Switch(self
.op
):
314 for i
, mod
in enumerate([bgt
, blt
, beq
, bne
]):
316 m
.d
.sync
+= self
.o
.eq(mod
.o
)
317 m
.d
.sync
+= self
.counter
.eq(5) # branch to take 5 cycles (fake)
318 #m.d.comb += go_now.eq(1)
320 # input says no longer valid, so drop ready as well.
321 # a "proper" ALU would have had to sync in the opcode and a/b ops
322 m
.d
.sync
+= self
.p_ready_o
.eq(0)
324 # ok so the counter's running: when it gets to 1, fire the output
325 with m
.If((self
.counter
== 1) | go_now
):
326 # set the output as valid if the recipient is ready for it
327 m
.d
.sync
+= self
.n_valid_o
.eq(1)
328 with m
.If(self
.n_ready_i
& self
.n_valid_o
):
329 m
.d
.sync
+= self
.n_valid_o
.eq(0)
330 # recipient said it was ready: reset back to known-good.
331 m
.d
.sync
+= self
.counter
.eq(0) # reset the counter
332 m
.d
.sync
+= self
.o
.eq(0) # clear the output for tidiness sake
334 # countdown to 1 (transition from 1 to 0 only on acknowledgement)
335 with m
.If(self
.counter
> 1):
336 m
.d
.sync
+= self
.counter
.eq(self
.counter
- 1)
349 def run_op(dut
, a
, b
, op
, inv_a
=0):
352 yield dut
.op
.insn_type
.eq(op
)
353 yield dut
.op
.invert_a
.eq(inv_a
)
354 yield dut
.n_ready_i
.eq(0)
355 yield dut
.p_valid_i
.eq(1)
359 n_valid_o
= yield dut
.n_valid_o
365 yield dut
.p_valid_i
.eq(0)
366 yield dut
.n_ready_i
.eq(0)
373 result
= yield from run_op(dut
, 5, 3, InternalOp
.OP_ADD
)
374 print ("alu_sim add", result
)
377 result
= yield from run_op(dut
, 2, 3, InternalOp
.OP_MUL_L64
)
378 print ("alu_sim mul", result
)
381 result
= yield from run_op(dut
, 5, 3, InternalOp
.OP_ADD
, inv_a
=1)
382 print ("alu_sim add-inv", result
)
383 assert (result
== 65533)
388 run_simulation(alu
, alu_sim(alu
), vcd_name
='test_alusim.vcd')
390 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
391 with
open("test_alu.il", "w") as f
:
395 if __name__
== "__main__":
398 alu
= BranchALU(width
=16)
399 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
400 with
open("test_branch_alu.il", "w") as f
: