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
.cli
import main
14 from nmigen
.cli
import verilog
, rtlil
19 class Adder(Elaboratable
):
20 def __init__(self
, width
):
21 self
.a
= Signal(width
)
22 self
.b
= Signal(width
)
23 self
.o
= Signal(width
)
25 def elaborate(self
, platform
):
27 m
.d
.comb
+= self
.o
.eq(self
.a
+ self
.b
)
31 class Subtractor(Elaboratable
):
32 def __init__(self
, width
):
33 self
.a
= Signal(width
)
34 self
.b
= Signal(width
)
35 self
.o
= Signal(width
)
37 def elaborate(self
, platform
):
39 m
.d
.comb
+= self
.o
.eq(self
.a
- self
.b
)
43 class Multiplier(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 Shifter(Elaboratable
):
56 def __init__(self
, width
):
58 self
.a
= Signal(width
)
59 self
.b
= Signal(width
)
60 self
.o
= Signal(width
)
62 def elaborate(self
, platform
):
64 btrunc
= Signal(self
.width
)
65 m
.d
.comb
+= btrunc
.eq(self
.b
& Const((1<<self
.width
)-1))
66 m
.d
.comb
+= self
.o
.eq(self
.a
>> btrunc
)
70 class ALU(Elaboratable
):
71 def __init__(self
, width
):
72 self
.p_valid_i
= Signal()
73 self
.p_ready_o
= Signal()
74 self
.n_ready_i
= Signal()
75 self
.n_valid_o
= Signal()
76 self
.counter
= Signal(4)
78 self
.a
= Signal(width
)
79 self
.b
= Signal(width
)
80 self
.o
= Signal(width
)
83 def elaborate(self
, platform
):
85 add
= Adder(self
.width
)
86 sub
= Subtractor(self
.width
)
87 mul
= Multiplier(self
.width
)
88 shf
= Shifter(self
.width
)
90 m
.submodules
.add
= add
91 m
.submodules
.sub
= sub
92 m
.submodules
.mul
= mul
93 m
.submodules
.shf
= shf
94 for mod
in [add
, sub
, mul
, shf
]:
99 go_now
= Signal(reset_less
=True) # testing no-delay ALU
101 with m
.If(self
.p_valid_i
):
102 # input is valid. next check, if we already said "ready" or not
103 with m
.If(~self
.p_ready_o
):
104 # we didn't say "ready" yet, so say so and initialise
105 m
.d
.sync
+= self
.p_ready_o
.eq(1)
107 # as this is a "fake" pipeline, just grab the output right now
108 with m
.Switch(self
.op
):
109 for i
, mod
in enumerate([add
, sub
, mul
, shf
]):
111 m
.d
.sync
+= self
.o
.eq(mod
.o
)
112 with m
.If(self
.op
== 2): # MUL, to take 5 instructions
113 m
.d
.sync
+= self
.counter
.eq(5)
114 with m
.Elif(self
.op
== 3): # SHIFT to take 7
115 m
.d
.sync
+= self
.counter
.eq(7)
116 with m
.Elif(self
.op
== 1): # SUB to take 1, straight away
117 m
.d
.sync
+= self
.counter
.eq(1)
118 m
.d
.comb
+= go_now
.eq(1)
119 with m
.Else(): # ADD to take 2
120 m
.d
.sync
+= self
.counter
.eq(2)
122 # input says no longer valid, so drop ready as well.
123 # a "proper" ALU would have had to sync in the opcode and a/b ops
124 m
.d
.sync
+= self
.p_ready_o
.eq(0)
126 # ok so the counter's running: when it gets to 1, fire the output
127 with m
.If((self
.counter
== 1) | go_now
):
128 # set the output as valid if the recipient is ready for it
129 m
.d
.sync
+= self
.n_valid_o
.eq(1)
130 with m
.If(self
.n_ready_i
& self
.n_valid_o
):
131 m
.d
.sync
+= self
.n_valid_o
.eq(0)
132 # recipient said it was ready: reset back to known-good.
133 m
.d
.sync
+= self
.counter
.eq(0) # reset the counter
134 m
.d
.sync
+= self
.o
.eq(0) # clear the output for tidiness sake
136 # countdown to 1 (transition from 1 to 0 only on acknowledgement)
137 with m
.If(self
.counter
> 1):
138 m
.d
.sync
+= self
.counter
.eq(self
.counter
- 1)
152 class BranchOp(Elaboratable
):
153 def __init__(self
, width
, op
):
154 self
.a
= Signal(width
)
155 self
.b
= Signal(width
)
156 self
.o
= Signal(width
)
159 def elaborate(self
, platform
):
161 m
.d
.comb
+= self
.o
.eq(Mux(self
.op(self
.a
, self
.b
), 1, 0))
165 class BranchALU(Elaboratable
):
166 def __init__(self
, width
):
167 self
.p_valid_i
= Signal()
168 self
.p_ready_o
= Signal()
169 self
.n_ready_i
= Signal()
170 self
.n_valid_o
= Signal()
171 self
.counter
= Signal(4)
173 self
.a
= Signal(width
)
174 self
.b
= Signal(width
)
175 self
.o
= Signal(width
)
178 def elaborate(self
, platform
):
180 bgt
= BranchOp(self
.width
, operator
.gt
)
181 blt
= BranchOp(self
.width
, operator
.lt
)
182 beq
= BranchOp(self
.width
, operator
.eq
)
183 bne
= BranchOp(self
.width
, operator
.ne
)
185 m
.submodules
.bgt
= bgt
186 m
.submodules
.blt
= blt
187 m
.submodules
.beq
= beq
188 m
.submodules
.bne
= bne
189 for mod
in [bgt
, blt
, beq
, bne
]:
195 go_now
= Signal(reset_less
=True) # testing no-delay ALU
196 with m
.If(self
.p_valid_i
):
197 # input is valid. next check, if we already said "ready" or not
198 with m
.If(~self
.p_ready_o
):
199 # we didn't say "ready" yet, so say so and initialise
200 m
.d
.sync
+= self
.p_ready_o
.eq(1)
202 # as this is a "fake" pipeline, just grab the output right now
203 with m
.Switch(self
.op
):
204 for i
, mod
in enumerate([bgt
, blt
, beq
, bne
]):
206 m
.d
.sync
+= self
.o
.eq(mod
.o
)
207 m
.d
.sync
+= self
.counter
.eq(5) # branch to take 5 cycles (fake)
208 #m.d.comb += go_now.eq(1)
210 # input says no longer valid, so drop ready as well.
211 # a "proper" ALU would have had to sync in the opcode and a/b ops
212 m
.d
.sync
+= self
.p_ready_o
.eq(0)
214 # ok so the counter's running: when it gets to 1, fire the output
215 with m
.If((self
.counter
== 1) | go_now
):
216 # set the output as valid if the recipient is ready for it
217 m
.d
.sync
+= self
.n_valid_o
.eq(1)
218 with m
.If(self
.n_ready_i
& self
.n_valid_o
):
219 m
.d
.sync
+= self
.n_valid_o
.eq(0)
220 # recipient said it was ready: reset back to known-good.
221 m
.d
.sync
+= self
.counter
.eq(0) # reset the counter
222 m
.d
.sync
+= self
.o
.eq(0) # clear the output for tidiness sake
224 # countdown to 1 (transition from 1 to 0 only on acknowledgement)
225 with m
.If(self
.counter
> 1):
226 m
.d
.sync
+= self
.counter
.eq(self
.counter
- 1)
240 if __name__
== "__main__":
242 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
243 with
open("test_alu.il", "w") as f
:
246 alu
= BranchALU(width
=16)
247 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
248 with
open("test_branch_alu.il", "w") as f
: