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. use with eq_from_execute1 (below) to
30 def __init__(self
, name
=None):
31 layout
= (('insn_type', InternalOp
),
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
37 ('rc', Layout((("rc", 1), ("rc_ok", 1)))),
38 ('oe', Layout((("oe", 1), ("oe_ok", 1)))),
41 ('input_carry', CryIn
),
50 Record
.__init
__(self
, Layout(layout
), name
=name
)
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
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
69 def eq_from_execute1(self
, other
):
70 """ use this to copy in from Decode2Execute1Type
73 for fname
, sig
in self
.fields
.items():
74 eqfrom
= other
.fields
[fname
]
75 res
.append(sig
.eq(eqfrom
))
79 return [self
.insn_type
,
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
)
103 def elaborate(self
, platform
):
105 with m
.If(self
.invert_a
):
106 m
.d
.comb
+= self
.o
.eq((~self
.a
) + self
.b
)
108 m
.d
.comb
+= self
.o
.eq(self
.a
+ self
.b
)
112 class Subtractor(Elaboratable
):
113 def __init__(self
, width
):
114 self
.a
= Signal(width
)
115 self
.b
= Signal(width
)
116 self
.o
= Signal(width
)
118 def elaborate(self
, platform
):
120 m
.d
.comb
+= self
.o
.eq(self
.a
- self
.b
)
124 class Multiplier(Elaboratable
):
125 def __init__(self
, width
):
126 self
.a
= Signal(width
)
127 self
.b
= Signal(width
)
128 self
.o
= Signal(width
)
130 def elaborate(self
, platform
):
132 m
.d
.comb
+= self
.o
.eq(self
.a
* self
.b
)
136 class Shifter(Elaboratable
):
137 def __init__(self
, width
):
139 self
.a
= Signal(width
)
140 self
.b
= Signal(width
)
141 self
.o
= Signal(width
)
143 def elaborate(self
, platform
):
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
)
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
)
164 def elaborate(self
, platform
):
166 add
= Adder(self
.width
)
167 mul
= Multiplier(self
.width
)
168 shf
= Shifter(self
.width
)
170 m
.submodules
.add
= add
171 m
.submodules
.mul
= mul
172 m
.submodules
.shf
= shf
174 # really should not activate absolutely all ALU inputs like this
175 for mod
in [add
, mul
, shf
]:
181 # pass invert (and carry later)
182 m
.d
.comb
+= add
.invert_a
.eq(self
.op
.invert_a
)
184 go_now
= Signal(reset_less
=True) # testing no-delay ALU
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)
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
)
201 # NOTE: all of these are fake, just something to test
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)
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
214 m
.d
.comb
+= go_now
.eq(1)
215 m
.d
.sync
+= self
.counter
.eq(1)
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)
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
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)
239 yield from self
.op
.ports()
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
)
255 def elaborate(self
, platform
):
257 m
.d
.comb
+= self
.o
.eq(Mux(self
.op(self
.a
, self
.b
), 1, 0))
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)
269 self
.a
= Signal(width
)
270 self
.b
= Signal(width
)
271 self
.o
= Signal(width
)
274 def elaborate(self
, platform
):
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
)
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
]:
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)
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
]):
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)
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)
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
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)
335 def run_op(dut
, a
, b
, op
, inv_a
=0):
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)
345 n_valid_o
= yield dut
.n_valid_o
351 yield dut
.p_valid_i
.eq(0)
352 yield dut
.n_ready_i
.eq(0)
359 result
= yield from run_op(dut
, 5, 3, InternalOp
.OP_ADD
)
360 print ("alu_sim add", result
)
363 result
= yield from run_op(dut
, 2, 3, InternalOp
.OP_MUL_L64
)
364 print ("alu_sim mul", result
)
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)
374 run_simulation(alu
, alu_sim(alu
), vcd_name
='test_alusim.vcd')
376 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
377 with
open("test_alu.il", "w") as f
:
381 if __name__
== "__main__":
384 alu
= BranchALU(width
=16)
385 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
386 with
open("test_branch_alu.il", "w") as f
: