add comment about SPRs CSV
[soc.git] / src / soc / experiment / alu_hier.py
1 """*Experimental* ALU: based on nmigen alu_hier.py, includes branch-compare ALU
2
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
7
8 A "real" integer ALU would place the answers onto the output bus after
9 only one cycle (sync)
10 """
11
12 from nmigen import Elaboratable, Signal, Module, Const, Mux
13 from nmigen.cli import main
14 from nmigen.cli import verilog, rtlil
15
16 import operator
17
18
19 class Adder(Elaboratable):
20 def __init__(self, width):
21 self.a = Signal(width)
22 self.b = Signal(width)
23 self.o = Signal(width)
24
25 def elaborate(self, platform):
26 m = Module()
27 m.d.comb += self.o.eq(self.a + self.b)
28 return m
29
30
31 class Subtractor(Elaboratable):
32 def __init__(self, width):
33 self.a = Signal(width)
34 self.b = Signal(width)
35 self.o = Signal(width)
36
37 def elaborate(self, platform):
38 m = Module()
39 m.d.comb += self.o.eq(self.a - self.b)
40 return m
41
42
43 class Multiplier(Elaboratable):
44 def __init__(self, width):
45 self.a = Signal(width)
46 self.b = Signal(width)
47 self.o = Signal(width)
48
49 def elaborate(self, platform):
50 m = Module()
51 m.d.comb += self.o.eq(self.a * self.b)
52 return m
53
54
55 class Shifter(Elaboratable):
56 def __init__(self, width):
57 self.width = width
58 self.a = Signal(width)
59 self.b = Signal(width)
60 self.o = Signal(width)
61
62 def elaborate(self, platform):
63 m = Module()
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)
67 return m
68
69
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)
77 self.op = Signal(2)
78 self.a = Signal(width)
79 self.b = Signal(width)
80 self.o = Signal(width)
81 self.width = width
82
83 def elaborate(self, platform):
84 m = Module()
85 add = Adder(self.width)
86 sub = Subtractor(self.width)
87 mul = Multiplier(self.width)
88 shf = Shifter(self.width)
89
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]:
95 m.d.comb += [
96 mod.a.eq(self.a),
97 mod.b.eq(self.b),
98 ]
99 go_now = Signal(reset_less=True) # testing no-delay ALU
100
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)
106
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]):
110 with m.Case(i):
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)
121 with m.Else():
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)
125
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
135
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)
139
140 return m
141
142 def __iter__(self):
143 yield self.op
144 yield self.a
145 yield self.b
146 yield self.o
147
148 def ports(self):
149 return list(self)
150
151
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)
157 self.op = op
158
159 def elaborate(self, platform):
160 m = Module()
161 m.d.comb += self.o.eq(Mux(self.op(self.a, self.b), 1, 0))
162 return m
163
164
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)
172 self.op = Signal(2)
173 self.a = Signal(width)
174 self.b = Signal(width)
175 self.o = Signal(width)
176 self.width = width
177
178 def elaborate(self, platform):
179 m = Module()
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)
184
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]:
190 m.d.comb += [
191 mod.a.eq(self.a),
192 mod.b.eq(self.b),
193 ]
194
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)
201
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]):
205 with m.Case(i):
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)
209 with m.Else():
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)
213
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
223
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)
227
228 return m
229
230 def __iter__(self):
231 yield self.op
232 yield self.a
233 yield self.b
234 yield self.o
235
236 def ports(self):
237 return list(self)
238
239
240 if __name__ == "__main__":
241 alu = ALU(width=16)
242 vl = rtlil.convert(alu, ports=alu.ports())
243 with open("test_alu.il", "w") as f:
244 f.write(vl)
245
246 alu = BranchALU(width=16)
247 vl = rtlil.convert(alu, ports=alu.ports())
248 with open("test_branch_alu.il", "w") as f:
249 f.write(vl)
250