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
),
51 Record
.__init
__(self
, Layout(layout
), name
=name
)
53 # grrr. Record does not have kwargs
54 self
.insn_type
.reset_less
= True
55 self
.fn_unit
.reset_less
= True
56 self
.nia
.reset_less
= True
57 #self.cr = Signal(32, reset_less = True
59 self
.lk
.reset_less
= True
60 self
.invert_a
.reset_less
= True
61 self
.invert_out
.reset_less
= True
62 self
.input_carry
.reset_less
= True
63 self
.output_carry
.reset_less
= True
64 self
.input_cr
.reset_less
= True
65 self
.output_cr
.reset_less
= True
66 self
.is_32bit
.reset_less
= True
67 self
.is_signed
.reset_less
= True
68 self
.byte_reverse
.reset_less
= True
69 self
.sign_extend
.reset_less
= True
71 def eq_from_execute1(self
, other
):
72 """ use this to copy in from Decode2Execute1Type
75 for fname
, sig
in self
.fields
.items():
76 eqfrom
= other
.fields
[fname
]
77 res
.append(sig
.eq(eqfrom
))
81 return [self
.insn_type
,
99 class Adder(Elaboratable
):
100 def __init__(self
, width
):
101 self
.invert_a
= Signal()
102 self
.a
= Signal(width
)
103 self
.b
= Signal(width
)
104 self
.o
= Signal(width
)
106 def elaborate(self
, platform
):
108 with m
.If(self
.invert_a
):
109 m
.d
.comb
+= self
.o
.eq((~self
.a
) + self
.b
)
111 m
.d
.comb
+= self
.o
.eq(self
.a
+ self
.b
)
115 class Subtractor(Elaboratable
):
116 def __init__(self
, width
):
117 self
.a
= Signal(width
)
118 self
.b
= Signal(width
)
119 self
.o
= Signal(width
)
121 def elaborate(self
, platform
):
123 m
.d
.comb
+= self
.o
.eq(self
.a
- self
.b
)
127 class Multiplier(Elaboratable
):
128 def __init__(self
, width
):
129 self
.a
= Signal(width
)
130 self
.b
= Signal(width
)
131 self
.o
= Signal(width
)
133 def elaborate(self
, platform
):
135 m
.d
.comb
+= self
.o
.eq(self
.a
* self
.b
)
139 class Shifter(Elaboratable
):
140 def __init__(self
, width
):
142 self
.a
= Signal(width
)
143 self
.b
= Signal(width
)
144 self
.o
= Signal(width
)
146 def elaborate(self
, platform
):
148 btrunc
= Signal(self
.width
)
149 m
.d
.comb
+= btrunc
.eq(self
.b
& Const((1<<self
.width
)-1))
150 m
.d
.comb
+= self
.o
.eq(self
.a
>> btrunc
)
154 class ALU(Elaboratable
):
155 def __init__(self
, width
):
156 self
.p_valid_i
= Signal()
157 self
.p_ready_o
= Signal()
158 self
.n_ready_i
= Signal()
159 self
.n_valid_o
= Signal()
160 self
.counter
= Signal(4)
161 self
.op
= CompALUOpSubset()
163 i
.append(Signal(width
, name
="i1"))
164 i
.append(Signal(width
, name
="i2"))
166 self
.a
, self
.b
= i
[0], i
[1]
167 self
.out
= Array([Signal(width
)])
171 def elaborate(self
, platform
):
173 add
= Adder(self
.width
)
174 mul
= Multiplier(self
.width
)
175 shf
= Shifter(self
.width
)
177 m
.submodules
.add
= add
178 m
.submodules
.mul
= mul
179 m
.submodules
.shf
= shf
181 # really should not activate absolutely all ALU inputs like this
182 for mod
in [add
, mul
, shf
]:
188 # pass invert (and carry later)
189 m
.d
.comb
+= add
.invert_a
.eq(self
.op
.invert_a
)
191 go_now
= Signal(reset_less
=True) # testing no-delay ALU
193 with m
.If(self
.p_valid_i
):
194 # input is valid. next check, if we already said "ready" or not
195 with m
.If(~self
.p_ready_o
):
196 # we didn't say "ready" yet, so say so and initialise
197 m
.d
.sync
+= self
.p_ready_o
.eq(1)
199 # as this is a "fake" pipeline, just grab the output right now
200 with m
.If(self
.op
.insn_type
== InternalOp
.OP_ADD
):
201 m
.d
.sync
+= self
.o
.eq(add
.o
)
202 with m
.Elif(self
.op
.insn_type
== InternalOp
.OP_MUL_L64
):
203 m
.d
.sync
+= self
.o
.eq(mul
.o
)
204 with m
.Elif(self
.op
.insn_type
== InternalOp
.OP_SHR
):
205 m
.d
.sync
+= self
.o
.eq(shf
.o
)
208 # NOTE: all of these are fake, just something to test
210 # MUL, to take 5 instructions
211 with m
.If(self
.op
.insn_type
== InternalOp
.OP_MUL_L64
):
212 m
.d
.sync
+= self
.counter
.eq(5)
214 with m
.Elif(self
.op
.insn_type
== InternalOp
.OP_SHR
):
215 m
.d
.sync
+= self
.counter
.eq(7)
216 # ADD/SUB to take 2, straight away
217 with m
.If(self
.op
.insn_type
== InternalOp
.OP_ADD
):
218 m
.d
.sync
+= self
.counter
.eq(3)
219 # others to take 1, straight away
221 m
.d
.comb
+= go_now
.eq(1)
222 m
.d
.sync
+= self
.counter
.eq(1)
225 # input says no longer valid, so drop ready as well.
226 # a "proper" ALU would have had to sync in the opcode and a/b ops
227 m
.d
.sync
+= self
.p_ready_o
.eq(0)
229 # ok so the counter's running: when it gets to 1, fire the output
230 with m
.If((self
.counter
== 1) | go_now
):
231 # set the output as valid if the recipient is ready for it
232 m
.d
.sync
+= self
.n_valid_o
.eq(1)
233 with m
.If(self
.n_ready_i
& self
.n_valid_o
):
234 m
.d
.sync
+= self
.n_valid_o
.eq(0)
235 # recipient said it was ready: reset back to known-good.
236 m
.d
.sync
+= self
.counter
.eq(0) # reset the counter
237 m
.d
.sync
+= self
.o
.eq(0) # clear the output for tidiness sake
239 # countdown to 1 (transition from 1 to 0 only on acknowledgement)
240 with m
.If(self
.counter
> 1):
241 m
.d
.sync
+= self
.counter
.eq(self
.counter
- 1)
246 yield from self
.op
.ports()
255 class BranchOp(Elaboratable
):
256 def __init__(self
, width
, op
):
257 self
.a
= Signal(width
)
258 self
.b
= Signal(width
)
259 self
.o
= Signal(width
)
262 def elaborate(self
, platform
):
264 m
.d
.comb
+= self
.o
.eq(Mux(self
.op(self
.a
, self
.b
), 1, 0))
268 class BranchALU(Elaboratable
):
269 def __init__(self
, width
):
270 self
.p_valid_i
= Signal()
271 self
.p_ready_o
= Signal()
272 self
.n_ready_i
= Signal()
273 self
.n_valid_o
= Signal()
274 self
.counter
= Signal(4)
277 i
.append(Signal(width
, name
="i1"))
278 i
.append(Signal(width
, name
="i2"))
280 self
.a
, self
.b
= i
[0], i
[1]
281 self
.out
= Array([Signal(width
)])
285 def elaborate(self
, platform
):
287 bgt
= BranchOp(self
.width
, operator
.gt
)
288 blt
= BranchOp(self
.width
, operator
.lt
)
289 beq
= BranchOp(self
.width
, operator
.eq
)
290 bne
= BranchOp(self
.width
, operator
.ne
)
292 m
.submodules
.bgt
= bgt
293 m
.submodules
.blt
= blt
294 m
.submodules
.beq
= beq
295 m
.submodules
.bne
= bne
296 for mod
in [bgt
, blt
, beq
, bne
]:
302 go_now
= Signal(reset_less
=True) # testing no-delay ALU
303 with m
.If(self
.p_valid_i
):
304 # input is valid. next check, if we already said "ready" or not
305 with m
.If(~self
.p_ready_o
):
306 # we didn't say "ready" yet, so say so and initialise
307 m
.d
.sync
+= self
.p_ready_o
.eq(1)
309 # as this is a "fake" pipeline, just grab the output right now
310 with m
.Switch(self
.op
):
311 for i
, mod
in enumerate([bgt
, blt
, beq
, bne
]):
313 m
.d
.sync
+= self
.o
.eq(mod
.o
)
314 m
.d
.sync
+= self
.counter
.eq(5) # branch to take 5 cycles (fake)
315 #m.d.comb += go_now.eq(1)
317 # input says no longer valid, so drop ready as well.
318 # a "proper" ALU would have had to sync in the opcode and a/b ops
319 m
.d
.sync
+= self
.p_ready_o
.eq(0)
321 # ok so the counter's running: when it gets to 1, fire the output
322 with m
.If((self
.counter
== 1) | go_now
):
323 # set the output as valid if the recipient is ready for it
324 m
.d
.sync
+= self
.n_valid_o
.eq(1)
325 with m
.If(self
.n_ready_i
& self
.n_valid_o
):
326 m
.d
.sync
+= self
.n_valid_o
.eq(0)
327 # recipient said it was ready: reset back to known-good.
328 m
.d
.sync
+= self
.counter
.eq(0) # reset the counter
329 m
.d
.sync
+= self
.o
.eq(0) # clear the output for tidiness sake
331 # countdown to 1 (transition from 1 to 0 only on acknowledgement)
332 with m
.If(self
.counter
> 1):
333 m
.d
.sync
+= self
.counter
.eq(self
.counter
- 1)
346 def run_op(dut
, a
, b
, op
, inv_a
=0):
349 yield dut
.op
.insn_type
.eq(op
)
350 yield dut
.op
.invert_a
.eq(inv_a
)
351 yield dut
.n_ready_i
.eq(0)
352 yield dut
.p_valid_i
.eq(1)
356 n_valid_o
= yield dut
.n_valid_o
362 yield dut
.p_valid_i
.eq(0)
363 yield dut
.n_ready_i
.eq(0)
370 result
= yield from run_op(dut
, 5, 3, InternalOp
.OP_ADD
)
371 print ("alu_sim add", result
)
374 result
= yield from run_op(dut
, 2, 3, InternalOp
.OP_MUL_L64
)
375 print ("alu_sim mul", result
)
378 result
= yield from run_op(dut
, 5, 3, InternalOp
.OP_ADD
, inv_a
=1)
379 print ("alu_sim add-inv", result
)
380 assert (result
== 65533)
385 run_simulation(alu
, alu_sim(alu
), vcd_name
='test_alusim.vcd')
387 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
388 with
open("test_alu.il", "w") as f
:
392 if __name__
== "__main__":
395 alu
= BranchALU(width
=16)
396 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
397 with
open("test_branch_alu.il", "w") as f
: