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
15 from soc
.decoder
.power_enums
import InternalOp
20 class Adder(Elaboratable
):
21 def __init__(self
, width
):
22 self
.a
= Signal(width
)
23 self
.b
= Signal(width
)
24 self
.o
= Signal(width
)
26 def elaborate(self
, platform
):
28 m
.d
.comb
+= self
.o
.eq(self
.a
+ self
.b
)
32 class Subtractor(Elaboratable
):
33 def __init__(self
, width
):
34 self
.a
= Signal(width
)
35 self
.b
= Signal(width
)
36 self
.o
= Signal(width
)
38 def elaborate(self
, platform
):
40 m
.d
.comb
+= self
.o
.eq(self
.a
- self
.b
)
44 class Multiplier(Elaboratable
):
45 def __init__(self
, width
):
46 self
.a
= Signal(width
)
47 self
.b
= Signal(width
)
48 self
.o
= Signal(width
)
50 def elaborate(self
, platform
):
52 m
.d
.comb
+= self
.o
.eq(self
.a
* self
.b
)
56 class Shifter(Elaboratable
):
57 def __init__(self
, width
):
59 self
.a
= Signal(width
)
60 self
.b
= Signal(width
)
61 self
.o
= Signal(width
)
63 def elaborate(self
, platform
):
65 btrunc
= Signal(self
.width
)
66 m
.d
.comb
+= btrunc
.eq(self
.b
& Const((1<<self
.width
)-1))
67 m
.d
.comb
+= self
.o
.eq(self
.a
>> btrunc
)
71 class ALU(Elaboratable
):
72 def __init__(self
, width
):
73 self
.p_valid_i
= Signal()
74 self
.p_ready_o
= Signal()
75 self
.n_ready_i
= Signal()
76 self
.n_valid_o
= Signal()
77 self
.counter
= Signal(4)
78 self
.op
= Signal(InternalOp
)
79 self
.a
= Signal(width
)
80 self
.b
= Signal(width
)
81 self
.o
= Signal(width
)
84 def elaborate(self
, platform
):
86 add
= Adder(self
.width
)
87 sub
= Subtractor(self
.width
)
88 mul
= Multiplier(self
.width
)
89 shf
= Shifter(self
.width
)
91 m
.submodules
.add
= add
92 m
.submodules
.sub
= sub
93 m
.submodules
.mul
= mul
94 m
.submodules
.shf
= shf
96 # really should not activate absolutely all ALU inputs like this
97 for mod
in [add
, sub
, mul
, shf
]:
102 go_now
= Signal(reset_less
=True) # testing no-delay ALU
104 with m
.If(self
.p_valid_i
):
105 # input is valid. next check, if we already said "ready" or not
106 with m
.If(~self
.p_ready_o
):
107 # we didn't say "ready" yet, so say so and initialise
108 m
.d
.sync
+= self
.p_ready_o
.eq(1)
110 # as this is a "fake" pipeline, just grab the output right now
111 with m
.If(self
.op
== InternalOp
.OP_ADD
):
112 m
.d
.sync
+= self
.o
.eq(add
.o
)
113 with m
.Elif(self
.op
== InternalOp
.OP_MUL_L64
):
114 m
.d
.sync
+= self
.o
.eq(mul
.o
)
115 with m
.Elif(self
.op
== InternalOp
.OP_SHR
):
116 m
.d
.sync
+= self
.o
.eq(shf
.o
)
119 with m
.Switch(self
.op
):
120 for i
, mod
in enumerate([add
, sub
, mul
, shf
]):
122 m
.d
.sync
+= self
.o
.eq(mod
.o
)
123 with m
.If(self
.op
== 2): # MUL, to take 5 instructions
124 m
.d
.sync
+= self
.counter
.eq(5)
125 with m
.Elif(self
.op
== 3): # SHIFT to take 7
126 m
.d
.sync
+= self
.counter
.eq(7)
127 with m
.Elif(self
.op
== 1): # SUB to take 1, straight away
128 m
.d
.sync
+= self
.counter
.eq(1)
129 m
.d
.comb
+= go_now
.eq(1)
130 with m
.Else(): # ADD to take 2
131 m
.d
.sync
+= self
.counter
.eq(2)
133 # input says no longer valid, so drop ready as well.
134 # a "proper" ALU would have had to sync in the opcode and a/b ops
135 m
.d
.sync
+= self
.p_ready_o
.eq(0)
137 # ok so the counter's running: when it gets to 1, fire the output
138 with m
.If((self
.counter
== 1) | go_now
):
139 # set the output as valid if the recipient is ready for it
140 m
.d
.sync
+= self
.n_valid_o
.eq(1)
141 with m
.If(self
.n_ready_i
& self
.n_valid_o
):
142 m
.d
.sync
+= self
.n_valid_o
.eq(0)
143 # recipient said it was ready: reset back to known-good.
144 m
.d
.sync
+= self
.counter
.eq(0) # reset the counter
145 m
.d
.sync
+= self
.o
.eq(0) # clear the output for tidiness sake
147 # countdown to 1 (transition from 1 to 0 only on acknowledgement)
148 with m
.If(self
.counter
> 1):
149 m
.d
.sync
+= self
.counter
.eq(self
.counter
- 1)
163 class BranchOp(Elaboratable
):
164 def __init__(self
, width
, op
):
165 self
.a
= Signal(width
)
166 self
.b
= Signal(width
)
167 self
.o
= Signal(width
)
170 def elaborate(self
, platform
):
172 m
.d
.comb
+= self
.o
.eq(Mux(self
.op(self
.a
, self
.b
), 1, 0))
176 class BranchALU(Elaboratable
):
177 def __init__(self
, width
):
178 self
.p_valid_i
= Signal()
179 self
.p_ready_o
= Signal()
180 self
.n_ready_i
= Signal()
181 self
.n_valid_o
= Signal()
182 self
.counter
= Signal(4)
184 self
.a
= Signal(width
)
185 self
.b
= Signal(width
)
186 self
.o
= Signal(width
)
189 def elaborate(self
, platform
):
191 bgt
= BranchOp(self
.width
, operator
.gt
)
192 blt
= BranchOp(self
.width
, operator
.lt
)
193 beq
= BranchOp(self
.width
, operator
.eq
)
194 bne
= BranchOp(self
.width
, operator
.ne
)
196 m
.submodules
.bgt
= bgt
197 m
.submodules
.blt
= blt
198 m
.submodules
.beq
= beq
199 m
.submodules
.bne
= bne
200 for mod
in [bgt
, blt
, beq
, bne
]:
206 go_now
= Signal(reset_less
=True) # testing no-delay ALU
207 with m
.If(self
.p_valid_i
):
208 # input is valid. next check, if we already said "ready" or not
209 with m
.If(~self
.p_ready_o
):
210 # we didn't say "ready" yet, so say so and initialise
211 m
.d
.sync
+= self
.p_ready_o
.eq(1)
213 # as this is a "fake" pipeline, just grab the output right now
214 with m
.Switch(self
.op
):
215 for i
, mod
in enumerate([bgt
, blt
, beq
, bne
]):
217 m
.d
.sync
+= self
.o
.eq(mod
.o
)
218 m
.d
.sync
+= self
.counter
.eq(5) # branch to take 5 cycles (fake)
219 #m.d.comb += go_now.eq(1)
221 # input says no longer valid, so drop ready as well.
222 # a "proper" ALU would have had to sync in the opcode and a/b ops
223 m
.d
.sync
+= self
.p_ready_o
.eq(0)
225 # ok so the counter's running: when it gets to 1, fire the output
226 with m
.If((self
.counter
== 1) | go_now
):
227 # set the output as valid if the recipient is ready for it
228 m
.d
.sync
+= self
.n_valid_o
.eq(1)
229 with m
.If(self
.n_ready_i
& self
.n_valid_o
):
230 m
.d
.sync
+= self
.n_valid_o
.eq(0)
231 # recipient said it was ready: reset back to known-good.
232 m
.d
.sync
+= self
.counter
.eq(0) # reset the counter
233 m
.d
.sync
+= self
.o
.eq(0) # clear the output for tidiness sake
235 # countdown to 1 (transition from 1 to 0 only on acknowledgement)
236 with m
.If(self
.counter
> 1):
237 m
.d
.sync
+= self
.counter
.eq(self
.counter
- 1)
251 if __name__
== "__main__":
253 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
254 with
open("test_alu.il", "w") as f
:
257 alu
= BranchALU(width
=16)
258 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
259 with
open("test_branch_alu.il", "w") as f
: