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
20 from soc
.fu
.alu
.alu_input_record
import CompALUOpSubset
27 class Adder(Elaboratable
):
28 def __init__(self
, width
):
29 self
.invert_a
= Signal()
30 self
.a
= Signal(width
)
31 self
.b
= Signal(width
)
32 self
.o
= Signal(width
)
34 def elaborate(self
, platform
):
36 with m
.If(self
.invert_a
):
37 m
.d
.comb
+= self
.o
.eq((~self
.a
) + self
.b
)
39 m
.d
.comb
+= self
.o
.eq(self
.a
+ self
.b
)
43 class Subtractor(Elaboratable
):
44 def __init__(self
, width
):
45 self
.a
= Signal(width
)
46 self
.b
= Signal(width
)
47 self
.o
= Signal(width
)
49 def elaborate(self
, platform
):
51 m
.d
.comb
+= self
.o
.eq(self
.a
- self
.b
)
55 class Multiplier(Elaboratable
):
56 def __init__(self
, width
):
57 self
.a
= Signal(width
)
58 self
.b
= Signal(width
)
59 self
.o
= Signal(width
)
61 def elaborate(self
, platform
):
63 m
.d
.comb
+= self
.o
.eq(self
.a
* self
.b
)
67 class Shifter(Elaboratable
):
68 def __init__(self
, width
):
70 self
.a
= Signal(width
)
71 self
.b
= Signal(width
)
72 self
.o
= Signal(width
)
74 def elaborate(self
, platform
):
76 btrunc
= Signal(self
.width
)
77 m
.d
.comb
+= btrunc
.eq(self
.b
& Const((1<<self
.width
)-1))
78 m
.d
.comb
+= self
.o
.eq(self
.a
>> btrunc
)
84 class ALU(Elaboratable
):
85 def __init__(self
, width
):
86 self
.p
= Dummy() # make look like nmutil pipeline API
87 self
.p
.data_i
= Dummy()
88 self
.p
.data_i
.ctx
= Dummy()
89 self
.n
= Dummy() # make look like nmutil pipeline API
90 self
.n
.data_o
= Dummy()
91 self
.p
.valid_i
= Signal()
92 self
.p
.ready_o
= Signal()
93 self
.n
.ready_i
= Signal()
94 self
.n
.valid_o
= Signal()
95 self
.counter
= Signal(4)
96 self
.op
= CompALUOpSubset()
98 i
.append(Signal(width
, name
="i1"))
99 i
.append(Signal(width
, name
="i2"))
101 self
.a
, self
.b
= i
[0], i
[1]
102 self
.out
= Array([Signal(width
)])
105 # more "look like nmutil pipeline API"
106 self
.p
.data_i
.ctx
.op
= self
.op
107 self
.p
.data_i
.a
= self
.a
108 self
.p
.data_i
.b
= self
.b
109 self
.n
.data_o
.o
= self
.o
111 def elaborate(self
, platform
):
113 add
= Adder(self
.width
)
114 mul
= Multiplier(self
.width
)
115 shf
= Shifter(self
.width
)
117 m
.submodules
.add
= add
118 m
.submodules
.mul
= mul
119 m
.submodules
.shf
= shf
121 # really should not activate absolutely all ALU inputs like this
122 for mod
in [add
, mul
, shf
]:
128 # pass invert (and carry later)
129 m
.d
.comb
+= add
.invert_a
.eq(self
.op
.invert_a
)
131 go_now
= Signal(reset_less
=True) # testing no-delay ALU
133 with m
.If(self
.p
.valid_i
):
134 # input is valid. next check, if we already said "ready" or not
135 with m
.If(~self
.p
.ready_o
):
136 # we didn't say "ready" yet, so say so and initialise
137 m
.d
.sync
+= self
.p
.ready_o
.eq(1)
139 # as this is a "fake" pipeline, just grab the output right now
140 with m
.If(self
.op
.insn_type
== InternalOp
.OP_ADD
):
141 m
.d
.sync
+= self
.o
.eq(add
.o
)
142 with m
.Elif(self
.op
.insn_type
== InternalOp
.OP_MUL_L64
):
143 m
.d
.sync
+= self
.o
.eq(mul
.o
)
144 with m
.Elif(self
.op
.insn_type
== InternalOp
.OP_SHR
):
145 m
.d
.sync
+= self
.o
.eq(shf
.o
)
148 # NOTE: all of these are fake, just something to test
150 # MUL, to take 5 instructions
151 with m
.If(self
.op
.insn_type
== InternalOp
.OP_MUL_L64
):
152 m
.d
.sync
+= self
.counter
.eq(5)
154 with m
.Elif(self
.op
.insn_type
== InternalOp
.OP_SHR
):
155 m
.d
.sync
+= self
.counter
.eq(7)
156 # ADD/SUB to take 2, straight away
157 with m
.If(self
.op
.insn_type
== InternalOp
.OP_ADD
):
158 m
.d
.sync
+= self
.counter
.eq(3)
159 # others to take 1, straight away
161 m
.d
.comb
+= go_now
.eq(1)
162 m
.d
.sync
+= self
.counter
.eq(1)
165 # input says no longer valid, so drop ready as well.
166 # a "proper" ALU would have had to sync in the opcode and a/b ops
167 m
.d
.sync
+= self
.p
.ready_o
.eq(0)
169 # ok so the counter's running: when it gets to 1, fire the output
170 with m
.If((self
.counter
== 1) | go_now
):
171 # set the output as valid if the recipient is ready for it
172 m
.d
.sync
+= self
.n
.valid_o
.eq(1)
173 with m
.If(self
.n
.ready_i
& self
.n
.valid_o
):
174 m
.d
.sync
+= self
.n
.valid_o
.eq(0)
175 # recipient said it was ready: reset back to known-good.
176 m
.d
.sync
+= self
.counter
.eq(0) # reset the counter
177 m
.d
.sync
+= self
.o
.eq(0) # clear the output for tidiness sake
179 # countdown to 1 (transition from 1 to 0 only on acknowledgement)
180 with m
.If(self
.counter
> 1):
181 m
.d
.sync
+= self
.counter
.eq(self
.counter
- 1)
186 yield from self
.op
.ports()
195 class BranchOp(Elaboratable
):
196 def __init__(self
, width
, op
):
197 self
.a
= Signal(width
)
198 self
.b
= Signal(width
)
199 self
.o
= Signal(width
)
202 def elaborate(self
, platform
):
204 m
.d
.comb
+= self
.o
.eq(Mux(self
.op(self
.a
, self
.b
), 1, 0))
208 class BranchALU(Elaboratable
):
209 def __init__(self
, width
):
210 self
.p
.valid_i
= Signal()
211 self
.p
.ready_o
= Signal()
212 self
.n
.ready_i
= Signal()
213 self
.n
.valid_o
= Signal()
214 self
.counter
= Signal(4)
217 i
.append(Signal(width
, name
="i1"))
218 i
.append(Signal(width
, name
="i2"))
220 self
.a
, self
.b
= i
[0], i
[1]
221 self
.out
= Array([Signal(width
)])
225 def elaborate(self
, platform
):
227 bgt
= BranchOp(self
.width
, operator
.gt
)
228 blt
= BranchOp(self
.width
, operator
.lt
)
229 beq
= BranchOp(self
.width
, operator
.eq
)
230 bne
= BranchOp(self
.width
, operator
.ne
)
232 m
.submodules
.bgt
= bgt
233 m
.submodules
.blt
= blt
234 m
.submodules
.beq
= beq
235 m
.submodules
.bne
= bne
236 for mod
in [bgt
, blt
, beq
, bne
]:
242 go_now
= Signal(reset_less
=True) # testing no-delay ALU
243 with m
.If(self
.p
.valid_i
):
244 # input is valid. next check, if we already said "ready" or not
245 with m
.If(~self
.p
.ready_o
):
246 # we didn't say "ready" yet, so say so and initialise
247 m
.d
.sync
+= self
.p
.ready_o
.eq(1)
249 # as this is a "fake" pipeline, just grab the output right now
250 with m
.Switch(self
.op
):
251 for i
, mod
in enumerate([bgt
, blt
, beq
, bne
]):
253 m
.d
.sync
+= self
.o
.eq(mod
.o
)
254 m
.d
.sync
+= self
.counter
.eq(5) # branch to take 5 cycles (fake)
255 #m.d.comb += go_now.eq(1)
257 # input says no longer valid, so drop ready as well.
258 # a "proper" ALU would have had to sync in the opcode and a/b ops
259 m
.d
.sync
+= self
.p
.ready_o
.eq(0)
261 # ok so the counter's running: when it gets to 1, fire the output
262 with m
.If((self
.counter
== 1) | go_now
):
263 # set the output as valid if the recipient is ready for it
264 m
.d
.sync
+= self
.n
.valid_o
.eq(1)
265 with m
.If(self
.n
.ready_i
& self
.n
.valid_o
):
266 m
.d
.sync
+= self
.n
.valid_o
.eq(0)
267 # recipient said it was ready: reset back to known-good.
268 m
.d
.sync
+= self
.counter
.eq(0) # reset the counter
269 m
.d
.sync
+= self
.o
.eq(0) # clear the output for tidiness sake
271 # countdown to 1 (transition from 1 to 0 only on acknowledgement)
272 with m
.If(self
.counter
> 1):
273 m
.d
.sync
+= self
.counter
.eq(self
.counter
- 1)
286 def run_op(dut
, a
, b
, op
, inv_a
=0):
289 yield dut
.op
.insn_type
.eq(op
)
290 yield dut
.op
.invert_a
.eq(inv_a
)
291 yield dut
.n
.ready_i
.eq(0)
292 yield dut
.p
.valid_i
.eq(1)
296 n
.valid_o
= yield dut
.n
.valid_o
302 yield dut
.p
.valid_i
.eq(0)
303 yield dut
.n
.ready_i
.eq(0)
310 result
= yield from run_op(dut
, 5, 3, InternalOp
.OP_ADD
)
311 print ("alu_sim add", result
)
314 result
= yield from run_op(dut
, 2, 3, InternalOp
.OP_MUL_L64
)
315 print ("alu_sim mul", result
)
318 result
= yield from run_op(dut
, 5, 3, InternalOp
.OP_ADD
, inv_a
=1)
319 print ("alu_sim add-inv", result
)
320 assert (result
== 65533)
325 run_simulation(alu
, alu_sim(alu
), vcd_name
='test_alusim.vcd')
327 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
328 with
open("test_alu.il", "w") as f
:
332 if __name__
== "__main__":
335 alu
= BranchALU(width
=16)
336 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
337 with
open("test_branch_alu.il", "w") as f
: