Convert yet another few tests to be able to use latest cxxsim
[soc.git] / src / soc / experiment / test / test_compalu_multi.py
1 """Computation Unit (aka "ALU Manager").
2
3 Manages a Pipeline or FSM, ensuring that the start and end time are 100%
4 monitored. At no time may the ALU proceed without this module notifying
5 the Dependency Matrices. At no time is a result production "abandoned".
6 This module blocks (indicates busy) starting from when it first receives
7 an opcode until it receives notification that
8 its result(s) have been successfully stored in the regfile(s)
9
10 Documented at http://libre-soc.org/3d_gpu/architecture/compunit
11 """
12
13 from soc.experiment.alu_fsm import Shifter, CompFSMOpSubset
14 from soc.fu.alu.alu_input_record import CompALUOpSubset
15 from soc.experiment.alu_hier import ALU, DummyALU
16 from soc.experiment.compalu_multi import MultiCompUnit
17 from soc.decoder.power_enums import MicrOp
18 from nmigen import Module
19 from nmigen.cli import rtlil
20
21 # NOTE: to use cxxsim, export NMIGEN_SIM_MODE=cxxsim from the shell
22 # Also, check out the cxxsim nmigen branch, and latest yosys from git
23 from nmutil.sim_tmp_alternative import Simulator, Settle
24
25
26 def wrap(process):
27 def wrapper():
28 yield from process
29 return wrapper
30
31
32 def op_sim_fsm(dut, a, b, direction):
33 print("op_sim_fsm", a, b, direction)
34 yield dut.issue_i.eq(0)
35 yield
36 yield dut.src_i[0].eq(a)
37 yield dut.src_i[1].eq(b)
38 yield dut.oper_i.sdir.eq(direction)
39 yield dut.issue_i.eq(1)
40 yield
41 yield dut.issue_i.eq(0)
42 yield
43
44 yield dut.rd.go_i.eq(0b11)
45 while True:
46 yield
47 rd_rel_o = yield dut.rd.rel_o
48 print("rd_rel", rd_rel_o)
49 if rd_rel_o:
50 break
51 yield dut.rd.go_i.eq(0)
52
53 req_rel_o = yield dut.wr.rel_o
54 result = yield dut.data_o
55 print("req_rel", req_rel_o, result)
56 while True:
57 req_rel_o = yield dut.wr.rel_o
58 result = yield dut.data_o
59 print("req_rel", req_rel_o, result)
60 if req_rel_o:
61 break
62 yield
63 yield dut.wr.go_i[0].eq(1)
64 yield Settle()
65 result = yield dut.data_o
66 yield
67 print("result", result)
68 yield dut.wr.go_i[0].eq(0)
69 yield
70 return result
71
72
73 def op_sim(dut, a, b, op, inv_a=0, imm=0, imm_ok=0, zero_a=0):
74 yield dut.issue_i.eq(0)
75 yield
76 yield dut.src_i[0].eq(a)
77 yield dut.src_i[1].eq(b)
78 yield dut.oper_i.insn_type.eq(op)
79 yield dut.oper_i.invert_in.eq(inv_a)
80 yield dut.oper_i.imm_data.data.eq(imm)
81 yield dut.oper_i.imm_data.ok.eq(imm_ok)
82 yield dut.oper_i.zero_a.eq(zero_a)
83 yield dut.issue_i.eq(1)
84 yield
85 yield dut.issue_i.eq(0)
86 yield
87 if not imm_ok or not zero_a:
88 yield dut.rd.go_i.eq(0b11)
89 while True:
90 yield
91 rd_rel_o = yield dut.rd.rel_o
92 print("rd_rel", rd_rel_o)
93 if rd_rel_o:
94 break
95 yield dut.rd.go_i.eq(0)
96 else:
97 print("no go rd")
98
99 if len(dut.src_i) == 3:
100 yield dut.rd.go_i.eq(0b100)
101 while True:
102 yield
103 rd_rel_o = yield dut.rd.rel_o
104 print("rd_rel", rd_rel_o)
105 if rd_rel_o:
106 break
107 yield dut.rd.go_i.eq(0)
108 else:
109 print("no 3rd rd")
110
111 req_rel_o = yield dut.wr.rel_o
112 result = yield dut.data_o
113 print("req_rel", req_rel_o, result)
114 while True:
115 req_rel_o = yield dut.wr.rel_o
116 result = yield dut.data_o
117 print("req_rel", req_rel_o, result)
118 if req_rel_o:
119 break
120 yield
121 yield dut.wr.go_i[0].eq(1)
122 yield Settle()
123 result = yield dut.data_o
124 yield
125 print("result", result)
126 yield dut.wr.go_i[0].eq(0)
127 yield
128 return result
129
130
131 def scoreboard_sim_fsm(dut):
132 result = yield from op_sim_fsm(dut, 13, 2, 1)
133 assert result == 3, result
134
135 result = yield from op_sim_fsm(dut, 3, 4, 0)
136 assert result == 48, result
137
138 result = yield from op_sim_fsm(dut, 21, 0, 0)
139 assert result == 21, result
140
141
142 def scoreboard_sim_dummy(dut):
143 result = yield from op_sim(dut, 5, 2, MicrOp.OP_NOP, inv_a=0,
144 imm=8, imm_ok=1)
145 assert result == 5, result
146
147 result = yield from op_sim(dut, 9, 2, MicrOp.OP_NOP, inv_a=0,
148 imm=8, imm_ok=1)
149 assert result == 9, result
150
151
152 def scoreboard_sim(dut):
153 # zero (no) input operands test
154 result = yield from op_sim(dut, 5, 2, MicrOp.OP_ADD, zero_a=1,
155 imm=8, imm_ok=1)
156 assert result == 8
157
158 result = yield from op_sim(dut, 5, 2, MicrOp.OP_ADD, inv_a=0,
159 imm=8, imm_ok=1)
160 assert result == 13
161
162 result = yield from op_sim(dut, 5, 2, MicrOp.OP_ADD)
163 assert result == 7
164
165 result = yield from op_sim(dut, 5, 2, MicrOp.OP_ADD, inv_a=1)
166 assert result == 65532
167
168 result = yield from op_sim(dut, 5, 2, MicrOp.OP_ADD, zero_a=1)
169 assert result == 2
170
171 # test combinatorial zero-delay operation
172 # In the test ALU, any operation other than ADD, MUL or SHR
173 # is zero-delay, and do a subtraction.
174 result = yield from op_sim(dut, 5, 2, MicrOp.OP_NOP)
175 assert result == 3
176
177
178 def test_compunit_fsm():
179
180 m = Module()
181 alu = Shifter(8)
182 dut = MultiCompUnit(8, alu, CompFSMOpSubset)
183 m.submodules.cu = dut
184
185 vl = rtlil.convert(dut, ports=dut.ports())
186 with open("test_compunit_fsm1.il", "w") as f:
187 f.write(vl)
188
189 sim = Simulator(m)
190 sim.add_clock(1e-6)
191
192 sim.add_sync_process(wrap(scoreboard_sim_fsm(dut)))
193 sim_writer = sim.write_vcd('test_compunit_fsm1.vcd')
194 with sim_writer:
195 sim.run()
196
197
198 def test_compunit():
199
200 m = Module()
201 alu = ALU(16)
202 dut = MultiCompUnit(16, alu, CompALUOpSubset)
203 m.submodules.cu = dut
204
205 vl = rtlil.convert(dut, ports=dut.ports())
206 with open("test_compunit1.il", "w") as f:
207 f.write(vl)
208
209 sim = Simulator(m)
210 sim.add_clock(1e-6)
211
212 sim.add_sync_process(wrap(scoreboard_sim(dut)))
213 sim_writer = sim.write_vcd('test_compunit1.vcd')
214 with sim_writer:
215 sim.run()
216
217
218 class CompUnitParallelTest:
219 def __init__(self, dut):
220 self.dut = dut
221
222 # Operation cycle should not take longer than this:
223 self.MAX_BUSY_WAIT = 50
224
225 # Minimum duration in which issue_i will be kept inactive,
226 # during which busy_o must remain low.
227 self.MIN_BUSY_LOW = 5
228
229 # Number of cycles to stall until the assertion of go.
230 # One value, for each port. Can be zero, for no delay.
231 self.RD_GO_DELAY = [0, 3]
232
233 # store common data for the input operation of the processes
234 # input operation:
235 self.op = 0
236 self.inv_a = self.zero_a = 0
237 self.imm = self.imm_ok = 0
238 self.imm_control = (0, 0)
239 self.rdmaskn = (0, 0)
240 # input data:
241 self.operands = (0, 0)
242
243 # Indicates completion of the sub-processes
244 self.rd_complete = [False, False]
245
246 def driver(self):
247 print("Begin parallel test.")
248 yield from self.operation(5, 2, MicrOp.OP_ADD)
249
250 def operation(self, a, b, op, inv_a=0, imm=0, imm_ok=0, zero_a=0,
251 rdmaskn=(0, 0)):
252 # store data for the operation
253 self.operands = (a, b)
254 self.op = op
255 self.inv_a = inv_a
256 self.imm = imm
257 self.imm_ok = imm_ok
258 self.zero_a = zero_a
259 self.imm_control = (zero_a, imm_ok)
260 self.rdmaskn = rdmaskn
261
262 # Initialize completion flags
263 self.rd_complete = [False, False]
264
265 # trigger operation cycle
266 yield from self.issue()
267
268 # check that the sub-processes completed, before the busy_o cycle ended
269 for completion in self.rd_complete:
270 assert completion
271
272 def issue(self):
273 # issue_i starts inactive
274 yield self.dut.issue_i.eq(0)
275
276 for n in range(self.MIN_BUSY_LOW):
277 yield
278 # busy_o must remain inactive. It cannot rise on its own.
279 busy_o = yield self.dut.busy_o
280 assert not busy_o
281
282 # activate issue_i to begin the operation cycle
283 yield self.dut.issue_i.eq(1)
284
285 # at the same time, present the operation
286 yield self.dut.oper_i.insn_type.eq(self.op)
287 yield self.dut.oper_i.invert_in.eq(self.inv_a)
288 yield self.dut.oper_i.imm_data.data.eq(self.imm)
289 yield self.dut.oper_i.imm_data.ok.eq(self.imm_ok)
290 yield self.dut.oper_i.zero_a.eq(self.zero_a)
291 rdmaskn = self.rdmaskn[0] | (self.rdmaskn[1] << 1)
292 yield self.dut.rdmaskn.eq(rdmaskn)
293
294 # give one cycle for the CompUnit to latch the data
295 yield
296
297 # busy_o must keep being low in this cycle, because issue_i was
298 # low on the previous cycle.
299 # It cannot rise on its own.
300 # Also, busy_o and issue_i must never be active at the same time, ever.
301 busy_o = yield self.dut.busy_o
302 assert not busy_o
303
304 # Lower issue_i
305 yield self.dut.issue_i.eq(0)
306
307 # deactivate inputs along with issue_i, so we can be sure the data
308 # was latched at the correct cycle
309 # note: rdmaskn must be held, while busy_o is active
310 # TODO: deactivate rdmaskn when the busy_o cycle ends
311 yield self.dut.oper_i.insn_type.eq(0)
312 yield self.dut.oper_i.invert_in.eq(0)
313 yield self.dut.oper_i.imm_data.data.eq(0)
314 yield self.dut.oper_i.imm_data.ok.eq(0)
315 yield self.dut.oper_i.zero_a.eq(0)
316 yield
317
318 # wait for busy_o to lower
319 # timeout after self.MAX_BUSY_WAIT cycles
320 for n in range(self.MAX_BUSY_WAIT):
321 # sample busy_o in the current cycle
322 busy_o = yield self.dut.busy_o
323 if not busy_o:
324 # operation cycle ends when busy_o becomes inactive
325 break
326 yield
327
328 # if busy_o is still active, a timeout has occurred
329 # TODO: Uncomment this, once the test is complete:
330 # assert not busy_o
331
332 if busy_o:
333 print("If you are reading this, "
334 "it's because the above test failed, as expected,\n"
335 "with a timeout. It must pass, once the test is complete.")
336 return
337
338 print("If you are reading this, "
339 "it's because the above test unexpectedly passed.")
340
341 def rd(self, rd_idx):
342 # wait for issue_i to rise
343 while True:
344 issue_i = yield self.dut.issue_i
345 if issue_i:
346 break
347 # issue_i has not risen yet, so rd must keep low
348 rel = yield self.dut.rd.rel_o[rd_idx]
349 assert not rel
350 yield
351
352 # we do not want rd to rise on an immediate operand
353 # if it is immediate, exit the process
354 # likewise, if the read mask is active
355 # TODO: don't exit the process, monitor rd instead to ensure it
356 # doesn't rise on its own
357 if self.rdmaskn[rd_idx] or self.imm_control[rd_idx]:
358 self.rd_complete[rd_idx] = True
359 return
360
361 # issue_i has risen. rel must rise on the next cycle
362 rel = yield self.dut.rd.rel_o[rd_idx]
363 assert not rel
364
365 # stall for additional cycles. Check that rel doesn't fall on its own
366 for n in range(self.RD_GO_DELAY[rd_idx]):
367 yield
368 rel = yield self.dut.rd.rel_o[rd_idx]
369 assert rel
370
371 # Before asserting "go", make sure "rel" has risen.
372 # The use of Settle allows "go" to be set combinatorially,
373 # rising on the same cycle as "rel".
374 yield Settle()
375 rel = yield self.dut.rd.rel_o[rd_idx]
376 assert rel
377
378 # assert go for one cycle, passing along the operand value
379 yield self.dut.rd.go_i[rd_idx].eq(1)
380 yield self.dut.src_i[rd_idx].eq(self.operands[rd_idx])
381 # check that the operand was sent to the alu
382 # TODO: Properly check the alu protocol
383 yield Settle()
384 alu_input = yield self.dut.get_in(rd_idx)
385 assert alu_input == self.operands[rd_idx]
386 yield
387
388 # rel must keep high, since go was inactive in the last cycle
389 rel = yield self.dut.rd.rel_o[rd_idx]
390 assert rel
391
392 # finish the go one-clock pulse
393 yield self.dut.rd.go_i[rd_idx].eq(0)
394 yield self.dut.src_i[rd_idx].eq(0)
395 yield
396
397 # rel must have gone low in response to go being high
398 # on the previous cycle
399 rel = yield self.dut.rd.rel_o[rd_idx]
400 assert not rel
401
402 self.rd_complete[rd_idx] = True
403
404 # TODO: check that rel doesn't rise again until the end of the
405 # busy_o cycle
406
407 def wr(self, wr_idx):
408 # monitor self.dut.wr.req[rd_idx] and sets dut.wr.go[idx] for one cycle
409 yield
410 # TODO: also when dut.wr.go is set, check the output against the
411 # self.expected_o and assert. use dut.get_out(wr_idx) to do so.
412
413 def run_simulation(self, vcd_name):
414 m = Module()
415 m.submodules.cu = self.dut
416 sim = Simulator(m)
417 sim.add_clock(1e-6)
418
419 sim.add_sync_process(wrap(self.driver()))
420 sim.add_sync_process(wrap(self.rd(0)))
421 sim.add_sync_process(wrap(self.rd(1)))
422 sim.add_sync_process(wrap(self.wr(0)))
423 sim_writer = sim.write_vcd(vcd_name)
424 with sim_writer:
425 sim.run()
426
427
428 def test_compunit_regspec2_fsm():
429
430 inspec = [('INT', 'a', '0:15'),
431 ('INT', 'b', '0:15'),
432 ]
433 outspec = [('INT', 'o', '0:15'),
434 ]
435
436 regspec = (inspec, outspec)
437
438 m = Module()
439 alu = Shifter(8)
440 dut = MultiCompUnit(regspec, alu, CompFSMOpSubset)
441 m.submodules.cu = dut
442
443 sim = Simulator(m)
444 sim.add_clock(1e-6)
445
446 sim.add_sync_process(wrap(scoreboard_sim_fsm(dut)))
447 sim_writer = sim.write_vcd('test_compunit_regspec2_fsm.vcd')
448 with sim_writer:
449 sim.run()
450
451
452 def test_compunit_regspec3():
453
454 inspec = [('INT', 'a', '0:15'),
455 ('INT', 'b', '0:15'),
456 ('INT', 'c', '0:15')]
457 outspec = [('INT', 'o', '0:15'),
458 ]
459
460 regspec = (inspec, outspec)
461
462 m = Module()
463 alu = DummyALU(16)
464 dut = MultiCompUnit(regspec, alu, CompALUOpSubset)
465 m.submodules.cu = dut
466
467 sim = Simulator(m)
468 sim.add_clock(1e-6)
469
470 sim.add_sync_process(wrap(scoreboard_sim_dummy(dut)))
471 sim_writer = sim.write_vcd('test_compunit_regspec3.vcd')
472 with sim_writer:
473 sim.run()
474
475
476 def test_compunit_regspec1():
477
478 inspec = [('INT', 'a', '0:15'),
479 ('INT', 'b', '0:15')]
480 outspec = [('INT', 'o', '0:15'),
481 ]
482
483 regspec = (inspec, outspec)
484
485 m = Module()
486 alu = ALU(16)
487 dut = MultiCompUnit(regspec, alu, CompALUOpSubset)
488 m.submodules.cu = dut
489
490 vl = rtlil.convert(dut, ports=dut.ports())
491 with open("test_compunit_regspec1.il", "w") as f:
492 f.write(vl)
493
494 sim = Simulator(m)
495 sim.add_clock(1e-6)
496
497 sim.add_sync_process(wrap(scoreboard_sim(dut)))
498 sim_writer = sim.write_vcd('test_compunit_regspec1.vcd')
499 with sim_writer:
500 sim.run()
501
502 test = CompUnitParallelTest(dut)
503 test.run_simulation("test_compunit_parallel.vcd")
504
505
506 if __name__ == '__main__':
507 test_compunit()
508 test_compunit_fsm()
509 test_compunit_regspec1()
510 test_compunit_regspec3()