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
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
, CryIn
23 class CompALUOpSubset(Record
):
26 a copy of the relevant subset information from Decode2Execute1Type
27 needed for ALU operations.
30 layout
= (('insn_type', InternalOp
),
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
36 ('rc', Layout((("rc", 1), ("rc_ok", 1)))),
37 ('oe', Layout((("oe", 1), ("oe_ok", 1)))),
40 ('input_carry', CryIn
),
49 Record
.__init
__(self
, Layout(layout
))
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
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
69 return [self
.insn_type
,
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
)
93 def elaborate(self
, platform
):
95 with m
.If(self
.invert_a
):
96 m
.d
.comb
+= self
.o
.eq((~self
.a
) + self
.b
)
98 m
.d
.comb
+= self
.o
.eq(self
.a
+ self
.b
)
102 class Subtractor(Elaboratable
):
103 def __init__(self
, width
):
104 self
.a
= Signal(width
)
105 self
.b
= Signal(width
)
106 self
.o
= Signal(width
)
108 def elaborate(self
, platform
):
110 m
.d
.comb
+= self
.o
.eq(self
.a
- self
.b
)
114 class Multiplier(Elaboratable
):
115 def __init__(self
, width
):
116 self
.a
= Signal(width
)
117 self
.b
= Signal(width
)
118 self
.o
= Signal(width
)
120 def elaborate(self
, platform
):
122 m
.d
.comb
+= self
.o
.eq(self
.a
* self
.b
)
126 class Shifter(Elaboratable
):
127 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 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
)
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
)
154 def elaborate(self
, platform
):
156 add
= Adder(self
.width
)
157 mul
= Multiplier(self
.width
)
158 shf
= Shifter(self
.width
)
160 m
.submodules
.add
= add
161 m
.submodules
.mul
= mul
162 m
.submodules
.shf
= shf
164 # really should not activate absolutely all ALU inputs like this
165 for mod
in [add
, mul
, shf
]:
171 # pass invert (and carry later)
172 m
.d
.comb
+= add
.invert_a
.eq(self
.op
.invert_a
)
174 go_now
= Signal(reset_less
=True) # testing no-delay ALU
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)
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
)
191 # NOTE: all of these are fake, just something to test
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)
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)
205 m
.d
.sync
+= self
.counter
.eq(2)
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)
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
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)
228 yield from self
.op
.ports()
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
)
244 def elaborate(self
, platform
):
246 m
.d
.comb
+= self
.o
.eq(Mux(self
.op(self
.a
, self
.b
), 1, 0))
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)
258 self
.a
= Signal(width
)
259 self
.b
= Signal(width
)
260 self
.o
= Signal(width
)
263 def elaborate(self
, platform
):
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
)
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
]:
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)
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
]):
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)
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)
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
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)
324 def run_op(dut
, a
, b
, op
, inv_a
=0):
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)
334 n_valid_o
= yield dut
.n_valid_o
340 yield dut
.p_valid_i
.eq(0)
341 yield dut
.n_ready_i
.eq(0)
348 result
= yield from run_op(dut
, 5, 3, InternalOp
.OP_ADD
)
349 print ("alu_sim add", result
)
352 result
= yield from run_op(dut
, 2, 3, InternalOp
.OP_MUL_L64
)
353 print ("alu_sim mul", result
)
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)
363 run_simulation(alu
, alu_sim(alu
), vcd_name
='test_alusim.vcd')
365 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
366 with
open("test_alu.il", "w") as f
:
370 if __name__
== "__main__":
373 alu
= BranchALU(width
=16)
374 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
375 with
open("test_branch_alu.il", "w") as f
: