af373ca3c9905bfac4b87dd686619f0aa714e3e5
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
.n
= Dummy() # make look like nmutil pipeline API
88 self
.p
.valid_i
= Signal()
89 self
.p
.ready_o
= Signal()
90 self
.n
.ready_i
= Signal()
91 self
.n
.valid_o
= Signal()
92 self
.counter
= Signal(4)
93 self
.op
= CompALUOpSubset()
95 i
.append(Signal(width
, name
="i1"))
96 i
.append(Signal(width
, name
="i2"))
98 self
.a
, self
.b
= i
[0], i
[1]
99 self
.out
= Array([Signal(width
)])
103 def elaborate(self
, platform
):
105 add
= Adder(self
.width
)
106 mul
= Multiplier(self
.width
)
107 shf
= Shifter(self
.width
)
109 m
.submodules
.add
= add
110 m
.submodules
.mul
= mul
111 m
.submodules
.shf
= shf
113 # really should not activate absolutely all ALU inputs like this
114 for mod
in [add
, mul
, shf
]:
120 # pass invert (and carry later)
121 m
.d
.comb
+= add
.invert_a
.eq(self
.op
.invert_a
)
123 go_now
= Signal(reset_less
=True) # testing no-delay ALU
125 with m
.If(self
.p
.valid_i
):
126 # input is valid. next check, if we already said "ready" or not
127 with m
.If(~self
.p
.ready_o
):
128 # we didn't say "ready" yet, so say so and initialise
129 m
.d
.sync
+= self
.p
.ready_o
.eq(1)
131 # as this is a "fake" pipeline, just grab the output right now
132 with m
.If(self
.op
.insn_type
== InternalOp
.OP_ADD
):
133 m
.d
.sync
+= self
.o
.eq(add
.o
)
134 with m
.Elif(self
.op
.insn_type
== InternalOp
.OP_MUL_L64
):
135 m
.d
.sync
+= self
.o
.eq(mul
.o
)
136 with m
.Elif(self
.op
.insn_type
== InternalOp
.OP_SHR
):
137 m
.d
.sync
+= self
.o
.eq(shf
.o
)
140 # NOTE: all of these are fake, just something to test
142 # MUL, to take 5 instructions
143 with m
.If(self
.op
.insn_type
== InternalOp
.OP_MUL_L64
):
144 m
.d
.sync
+= self
.counter
.eq(5)
146 with m
.Elif(self
.op
.insn_type
== InternalOp
.OP_SHR
):
147 m
.d
.sync
+= self
.counter
.eq(7)
148 # ADD/SUB to take 2, straight away
149 with m
.If(self
.op
.insn_type
== InternalOp
.OP_ADD
):
150 m
.d
.sync
+= self
.counter
.eq(3)
151 # others to take 1, straight away
153 m
.d
.comb
+= go_now
.eq(1)
154 m
.d
.sync
+= self
.counter
.eq(1)
157 # input says no longer valid, so drop ready as well.
158 # a "proper" ALU would have had to sync in the opcode and a/b ops
159 m
.d
.sync
+= self
.p
.ready_o
.eq(0)
161 # ok so the counter's running: when it gets to 1, fire the output
162 with m
.If((self
.counter
== 1) | go_now
):
163 # set the output as valid if the recipient is ready for it
164 m
.d
.sync
+= self
.n
.valid_o
.eq(1)
165 with m
.If(self
.n
.ready_i
& self
.n
.valid_o
):
166 m
.d
.sync
+= self
.n
.valid_o
.eq(0)
167 # recipient said it was ready: reset back to known-good.
168 m
.d
.sync
+= self
.counter
.eq(0) # reset the counter
169 m
.d
.sync
+= self
.o
.eq(0) # clear the output for tidiness sake
171 # countdown to 1 (transition from 1 to 0 only on acknowledgement)
172 with m
.If(self
.counter
> 1):
173 m
.d
.sync
+= self
.counter
.eq(self
.counter
- 1)
178 yield from self
.op
.ports()
187 class BranchOp(Elaboratable
):
188 def __init__(self
, width
, op
):
189 self
.a
= Signal(width
)
190 self
.b
= Signal(width
)
191 self
.o
= Signal(width
)
194 def elaborate(self
, platform
):
196 m
.d
.comb
+= self
.o
.eq(Mux(self
.op(self
.a
, self
.b
), 1, 0))
200 class BranchALU(Elaboratable
):
201 def __init__(self
, width
):
202 self
.p
.valid_i
= Signal()
203 self
.p
.ready_o
= Signal()
204 self
.n
.ready_i
= Signal()
205 self
.n
.valid_o
= Signal()
206 self
.counter
= Signal(4)
209 i
.append(Signal(width
, name
="i1"))
210 i
.append(Signal(width
, name
="i2"))
212 self
.a
, self
.b
= i
[0], i
[1]
213 self
.out
= Array([Signal(width
)])
217 def elaborate(self
, platform
):
219 bgt
= BranchOp(self
.width
, operator
.gt
)
220 blt
= BranchOp(self
.width
, operator
.lt
)
221 beq
= BranchOp(self
.width
, operator
.eq
)
222 bne
= BranchOp(self
.width
, operator
.ne
)
224 m
.submodules
.bgt
= bgt
225 m
.submodules
.blt
= blt
226 m
.submodules
.beq
= beq
227 m
.submodules
.bne
= bne
228 for mod
in [bgt
, blt
, beq
, bne
]:
234 go_now
= Signal(reset_less
=True) # testing no-delay ALU
235 with m
.If(self
.p
.valid_i
):
236 # input is valid. next check, if we already said "ready" or not
237 with m
.If(~self
.p
.ready_o
):
238 # we didn't say "ready" yet, so say so and initialise
239 m
.d
.sync
+= self
.p
.ready_o
.eq(1)
241 # as this is a "fake" pipeline, just grab the output right now
242 with m
.Switch(self
.op
):
243 for i
, mod
in enumerate([bgt
, blt
, beq
, bne
]):
245 m
.d
.sync
+= self
.o
.eq(mod
.o
)
246 m
.d
.sync
+= self
.counter
.eq(5) # branch to take 5 cycles (fake)
247 #m.d.comb += go_now.eq(1)
249 # input says no longer valid, so drop ready as well.
250 # a "proper" ALU would have had to sync in the opcode and a/b ops
251 m
.d
.sync
+= self
.p
.ready_o
.eq(0)
253 # ok so the counter's running: when it gets to 1, fire the output
254 with m
.If((self
.counter
== 1) | go_now
):
255 # set the output as valid if the recipient is ready for it
256 m
.d
.sync
+= self
.n
.valid_o
.eq(1)
257 with m
.If(self
.n
.ready_i
& self
.n
.valid_o
):
258 m
.d
.sync
+= self
.n
.valid_o
.eq(0)
259 # recipient said it was ready: reset back to known-good.
260 m
.d
.sync
+= self
.counter
.eq(0) # reset the counter
261 m
.d
.sync
+= self
.o
.eq(0) # clear the output for tidiness sake
263 # countdown to 1 (transition from 1 to 0 only on acknowledgement)
264 with m
.If(self
.counter
> 1):
265 m
.d
.sync
+= self
.counter
.eq(self
.counter
- 1)
278 def run_op(dut
, a
, b
, op
, inv_a
=0):
281 yield dut
.op
.insn_type
.eq(op
)
282 yield dut
.op
.invert_a
.eq(inv_a
)
283 yield dut
.n
.ready_i
.eq(0)
284 yield dut
.p
.valid_i
.eq(1)
288 n
.valid_o
= yield dut
.n
.valid_o
294 yield dut
.p
.valid_i
.eq(0)
295 yield dut
.n
.ready_i
.eq(0)
302 result
= yield from run_op(dut
, 5, 3, InternalOp
.OP_ADD
)
303 print ("alu_sim add", result
)
306 result
= yield from run_op(dut
, 2, 3, InternalOp
.OP_MUL_L64
)
307 print ("alu_sim mul", result
)
310 result
= yield from run_op(dut
, 5, 3, InternalOp
.OP_ADD
, inv_a
=1)
311 print ("alu_sim add-inv", result
)
312 assert (result
== 65533)
317 run_simulation(alu
, alu_sim(alu
), vcd_name
='test_alusim.vcd')
319 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
320 with
open("test_alu.il", "w") as f
:
324 if __name__
== "__main__":
327 alu
= BranchALU(width
=16)
328 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
329 with
open("test_branch_alu.il", "w") as f
: