get LDSTCompALU debugged a bit: ST functionality working
[soc.git] / src / soc / experiment / compldst.py
1 """ LOAD / STORE Computation Unit. Also capable of doing ADD and ADD immediate
2
3 This module runs a "revolving door" set of four latches, based on
4 * Issue
5 * Go_Read
6 * Go_Addr
7 * Go_Write *OR* Go_Store
8
9 (Note that opc_l has been inverted (and qn used), due to SRLatch
10 default reset state being "0" rather than "1")
11
12 Also note: the LD/ST Comp Unit can act as a *standard ALU* doing
13 add and subtract.
14
15 Stores are activated when Go_Store is enabled, and uses the ALU
16 to add the immediate (imm_i) to the address (src1_i), and then
17 when ready (go_st_i and the ALU ready) the operand (src2_i) is stored
18 in the computed address.
19 """
20
21 from nmigen.compat.sim import run_simulation
22 from nmigen.cli import verilog, rtlil
23 from nmigen import Module, Signal, Mux, Cat, Elaboratable
24
25 from nmutil.latch import SRLatch, latchregister
26
27 from testmem import TestMemory
28
29 # internal opcodes. hypothetically this could do more combinations.
30 # meanings:
31 # * bit 0: 0 = ADD , 1 = SUB
32 # * bit 1: 0 = src1, 1 = IMM
33 # * bit 2: 1 = LD
34 # * bit 3: 1 = ST
35 LDST_OP_ADD = 0b0000 # plain ADD (src1 + src2) - use this ALU as an ADD
36 LDST_OP_SUB = 0b0001 # plain SUB (src1 - src2) - use this ALU as a SUB
37 LDST_OP_ADDI = 0b0010 # immed ADD (imm + src1)
38 LDST_OP_SUBI = 0b0011 # immed SUB (imm - src1)
39 LDST_OP_ST = 0b0110 # immed ADD plus LD op. ADD result is address
40 LDST_OP_LD = 0b1010 # immed ADD plus ST op. ADD result is address
41
42
43 class LDSTCompUnit(Elaboratable):
44 """ LOAD / STORE / ADD / SUB Computation Unit
45
46 Inputs
47 ------
48
49 * :rwid: register width
50 * :alu: an ALU module
51 * :mem: a Memory Module (read-write capable)
52
53 Control Signals (In)
54 --------------------
55
56 * :oper_i: operation being carried out (LDST_OP_ADD, LDST_OP_LD)
57 * :issue_i: LD/ST is being "issued".
58 * :isalu_i: ADD/SUB is being "issued" (aka issue_alu_i)
59 * :shadown_i: Inverted-shadow is being held (stops STORE *and* WRITE)
60 * :go_rd_i: read is being actioned (latches in src regs)
61 * :go_wr_i: write mode (exactly like ALU CompUnit)
62 * :go_ad_i: address is being actioned (triggers actual mem LD)
63 * :go_st_i: store is being actioned (triggers actual mem STORE)
64 * :go_die_i: resets the unit back to "wait for issue"
65
66 Control Signals (Out)
67 ---------------------
68
69 * :busy_o: function unit is busy
70 * :rd_rel_o: request src1/src2
71 * :adr_rel_o: request address (from mem)
72 * :sto_rel_o: request store (to mem)
73 * :req_rel_o: request write (result)
74 * :load_mem_o: activate memory LOAD
75 * :stwd_mem_o: activate memory STORE
76
77 Note: load_mem_o, stwd_mem_o and req_rel_o MUST all be acknowledged
78 in a single cycle and the CompUnit set back to doing another op.
79 This means deasserting go_st_i, go_ad_i or go_wr_i as appropriate
80 depending on whether the operation is a STORE, LD, or a straight
81 ALU operation respectively.
82
83 Control Data (out)
84 ------------------
85 * :data_o: Dest out (LD or ALU)
86 * :addr_o: Address out (LD or ST)
87 """
88 def __init__(self, rwid, opwid, alu, mem):
89 self.opwid = opwid
90 self.rwid = rwid
91 self.alu = alu
92 self.mem = mem
93
94 self.counter = Signal(4)
95 self.go_rd_i = Signal(reset_less=True) # go read in
96 self.go_ad_i = Signal(reset_less=True) # go address in
97 self.go_wr_i = Signal(reset_less=True) # go write in
98 self.go_st_i = Signal(reset_less=True) # go store in
99 self.issue_i = Signal(reset_less=True) # fn issue in
100 self.isalu_i = Signal(reset_less=True) # fn issue as ALU in
101 self.shadown_i = Signal(reset=1) # shadow function, defaults to ON
102 self.go_die_i = Signal() # go die (reset)
103
104 self.oper_i = Signal(opwid, reset_less=True) # opcode in
105 self.imm_i = Signal(rwid, reset_less=True) # immediate in
106 self.src1_i = Signal(rwid, reset_less=True) # oper1 in
107 self.src2_i = Signal(rwid, reset_less=True) # oper2 in
108
109 self.busy_o = Signal(reset_less=True) # fn busy out
110 self.rd_rel_o = Signal(reset_less=True) # request src1/src2
111 self.adr_rel_o = Signal(reset_less=True) # request address (from mem)
112 self.sto_rel_o = Signal(reset_less=True) # request store (to mem)
113 self.req_rel_o = Signal(reset_less=True) # request write (result)
114 self.data_o = Signal(rwid, reset_less=True) # Dest out (LD or ALU)
115 self.addr_o = Signal(rwid, reset_less=True) # Address out (LD or ST)
116
117 # hmm... TODO... move these to outside of LDSTCompUnit?
118 self.load_mem_o = Signal(reset_less=True) # activate memory LOAD
119 self.stwd_mem_o = Signal(reset_less=True) # activate memory STORE
120 self.ld_o = Signal(reset_less=True) # operation is a LD
121 self.st_o = Signal(reset_less=True) # operation is a ST
122
123 def elaborate(self, platform):
124 m = Module()
125 comb = m.d.comb
126 sync = m.d.sync
127
128 m.submodules.alu = self.alu
129 #m.submodules.mem = self.mem
130 m.submodules.src_l = src_l = SRLatch(sync=False)
131 m.submodules.opc_l = opc_l = SRLatch(sync=False)
132 m.submodules.adr_l = adr_l = SRLatch(sync=False)
133 m.submodules.req_l = req_l = SRLatch(sync=False)
134 m.submodules.sto_l = sto_l = SRLatch(sync=False)
135
136 # shadow/go_die
137 reset_b = Signal(reset_less=True)
138 reset_w = Signal(reset_less=True)
139 reset_a = Signal(reset_less=True)
140 reset_s = Signal(reset_less=True)
141 reset_r = Signal(reset_less=True)
142 comb += reset_b.eq(self.go_st_i | self.go_wr_i | self.go_die_i)
143 comb += reset_w.eq(self.go_wr_i | self.go_die_i)
144 comb += reset_s.eq(self.go_st_i | self.go_die_i)
145 comb += reset_r.eq(self.go_rd_i | self.go_die_i)
146 # this one is slightly different, issue_alu_i selects go_wr_i)
147 a_sel = Mux(self.isalu_i, self.go_wr_i, self.go_ad_i)
148 comb += reset_a.eq(a_sel| self.go_die_i)
149
150 # opcode decode
151 op_alu = Signal(reset_less=True)
152 op_is_ld = Signal(reset_less=True)
153 op_is_st = Signal(reset_less=True)
154 op_ldst = Signal(reset_less=True)
155 op_is_imm = Signal(reset_less=True)
156
157 # src2 register
158 src2_r = Signal(self.rwid, reset_less=True)
159
160 # select immediate or src2 reg to add
161 src2_or_imm = Signal(self.rwid, reset_less=True)
162 src_sel = Signal(reset_less=True)
163
164 # issue can be either issue_i or issue_alu_i (isalu_i)
165 issue_i = Signal(reset_less=True)
166 comb += issue_i.eq(self.issue_i | self.isalu_i)
167
168 # Ripple-down the latches, each one set cancels the previous.
169 # NOTE: use sync to stop combinatorial loops.
170
171 # opcode latch - inverted so that busy resets to 0
172 sync += opc_l.s.eq(issue_i) # XXX NOTE: INVERTED FROM book!
173 sync += opc_l.r.eq(reset_b) # XXX NOTE: INVERTED FROM book!
174
175 # src operand latch
176 sync += src_l.s.eq(issue_i)
177 sync += src_l.r.eq(reset_r)
178
179 # addr latch
180 sync += adr_l.s.eq(self.go_rd_i)
181 sync += adr_l.r.eq(reset_a)
182
183 # dest operand latch
184 sync += req_l.s.eq(self.go_ad_i)
185 sync += req_l.r.eq(reset_w)
186
187 # store latch
188 sync += sto_l.s.eq(self.go_ad_i)
189 sync += sto_l.r.eq(reset_s)
190
191 # outputs: busy and release signals
192 busy_o = self.busy_o
193 comb += self.busy_o.eq(opc_l.q) # busy out
194 comb += self.rd_rel_o.eq(src_l.q & busy_o) # src1/src2 req rel
195 comb += self.sto_rel_o.eq(sto_l.q & busy_o & self.shadown_i & op_is_st)
196
197 # request release enabled based on if op is a LD/ST or a plain ALU
198 # if op is an ADD/SUB or a LD, req_rel activates.
199 wr_q = Signal(reset_less=True)
200 comb += wr_q.eq(req_l.q & (~op_ldst | op_is_ld))
201
202 alulatch = Signal(reset_less=True)
203 comb += alulatch.eq((op_ldst & self.adr_rel_o) | \
204 (~op_ldst & self.req_rel_o))
205
206 # select immediate if opcode says so. however also change the latch
207 # to trigger *from* the opcode latch instead.
208 comb += src_sel.eq(Mux(op_is_imm, opc_l.qn, src_l.q))
209 comb += src2_or_imm.eq(Mux(op_is_imm, self.imm_i, self.src2_i))
210
211 # create a latch/register for src1/src2 (include immediate select)
212 latchregister(m, self.src1_i, self.alu.a, src_l.q)
213 latchregister(m, self.src2_i, src2_r, src_l.q)
214 latchregister(m, src2_or_imm, self.alu.b, src_sel)
215
216 # create a latch/register for the operand
217 oper_r = Signal(self.opwid, reset_less=True) # Dest register
218 latchregister(m, self.oper_i, oper_r, self.issue_i)
219 alu_op = Cat(op_alu, 0, op_is_imm) # using alu_hier, here.
220 comb += self.alu.op.eq(alu_op)
221
222 # and one for the output from the ALU
223 data_r = Signal(self.rwid, reset_less=True) # Dest register
224 latchregister(m, self.alu.o, data_r, alulatch)
225
226 # decode bits of operand (latched)
227 comb += op_alu.eq(oper_r[0])
228 comb += op_is_imm.eq(oper_r[1])
229 comb += op_is_ld.eq(oper_r[2])
230 comb += op_is_st.eq(oper_r[3])
231 comb += op_ldst.eq(op_is_ld | op_is_st)
232 comb += self.load_mem_o.eq(op_is_ld & self.go_ad_i)
233 comb += self.stwd_mem_o.eq(op_is_st & self.go_st_i)
234 comb += self.ld_o.eq(op_is_ld)
235 comb += self.st_o.eq(op_is_st)
236
237 # on a go_read, tell the ALU we're accepting data.
238 # NOTE: this spells TROUBLE if the ALU isn't ready!
239 # go_read is only valid for one clock!
240 with m.If(self.go_rd_i): # src operands ready, GO!
241 with m.If(~self.alu.p_ready_o): # no ACK yet
242 m.d.comb += self.alu.p_valid_i.eq(1) # so indicate valid
243
244 # only proceed if ALU says its output is valid
245 with m.If(self.alu.n_valid_o):
246 # write req release out. waits until shadow is dropped.
247 comb += self.req_rel_o.eq(wr_q & busy_o & self.shadown_i)
248 # address release only happens on LD/ST, and is shadowed.
249 comb += self.adr_rel_o.eq(adr_l.q & op_ldst & busy_o & \
250 self.shadown_i)
251 # when output latch is ready, and ALU says ready, accept ALU output
252 with m.If(self.req_rel_o):
253 m.d.comb += self.alu.n_ready_i.eq(1) # tells ALU "thanks got it"
254
255 # put the register directly onto the output bus on a go_write
256 # this is "ALU mode". go_wr_i *must* be deasserted on next clock
257 with m.If(self.go_wr_i):
258 comb += self.data_o.eq(data_r)
259
260 # "LD/ST" mode: put the register directly onto the *address* bus
261 with m.If(self.go_ad_i | self.go_st_i):
262 comb += self.addr_o.eq(data_r)
263
264 # TODO: think about moving these to another module
265
266 # connect ST to memory. NOTE: unit *must* be set back
267 # to start again by dropping go_st_i on next clock
268 with m.If(self.stwd_mem_o):
269 wrport = self.mem.wrport
270 comb += wrport.addr.eq(self.addr_o)
271 comb += wrport.data.eq(src2_r)
272 comb += wrport.en.eq(1)
273
274 # connect LD to memory. NOTE: unit *must* be set back
275 # to start again by dropping go_ad_i on next clock
276 with m.If(self.load_mem_o):
277 rdport = self.mem.rdport
278 comb += rdport.addr.eq(self.addr_o)
279 comb += self.data_o.eq(rdport.data)
280 # comb += rdport.en.eq(1) # only when transparent=False
281
282 return m
283
284 def __iter__(self):
285 yield self.go_rd_i
286 yield self.go_ad_i
287 yield self.go_wr_i
288 yield self.go_st_i
289 yield self.issue_i
290 yield self.isalu_i
291 yield self.shadown_i
292 yield self.go_die_i
293 yield self.oper_i
294 yield self.imm_i
295 yield self.src1_i
296 yield self.src2_i
297 yield self.busy_o
298 yield self.rd_rel_o
299 yield self.adr_rel_o
300 yield self.sto_rel_o
301 yield self.req_rel_o
302 yield self.data_o
303 yield self.load_mem_o
304 yield self.stwd_mem_o
305
306 def ports(self):
307 return list(self)
308
309
310 def scoreboard_sim(dut):
311 yield dut.oper_i.eq(LDST_OP_LD)
312 yield dut.src1_i.eq(4)
313 yield dut.src2_i.eq(9)
314 yield dut.imm_i.eq(2)
315 yield dut.issue_i.eq(1)
316 yield
317 yield dut.issue_i.eq(0)
318 yield
319 yield dut.go_rd_i.eq(1)
320 yield
321 yield dut.go_rd_i.eq(0)
322 yield
323 #yield dut.go_ad_i.eq(1)
324 #yield
325 #yield dut.go_ad_i.eq(0)
326 yield
327 yield dut.go_st_i.eq(1)
328 yield
329 yield dut.go_st_i.eq(0)
330 yield
331 #yield dut.go_wr_i.eq(1)
332 #yield
333 #yield dut.go_wr_i.eq(0)
334 yield
335 yield dut.issue_i.eq(1)
336 yield
337 yield dut.issue_i.eq(0)
338 yield
339 x = yield dut.go_rd_i
340
341
342 class TestLDSTCompUnit(LDSTCompUnit):
343
344 def __init__(self, rwid, opwid):
345 from alu_hier import ALU
346 self.alu = alu = ALU(rwid)
347 self.mem = mem = TestMemory(rwid, 8)
348 LDSTCompUnit.__init__(self, rwid, opwid, alu, mem)
349
350 def elaborate(self, platform):
351 m = LDSTCompUnit.elaborate(self, platform)
352 m.submodules.mem = self.mem
353 return m
354
355
356 def test_scoreboard():
357
358 dut = TestLDSTCompUnit(16, 4)
359 vl = rtlil.convert(dut, ports=dut.ports())
360 with open("test_ldst_comp.il", "w") as f:
361 f.write(vl)
362
363 run_simulation(dut, scoreboard_sim(dut), vcd_name='test_ldst_comp.vcd')
364
365 if __name__ == '__main__':
366 test_scoreboard()