Handle newer nMigen adding a "bench" hierarchy root in VCD files
[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.fu.cr.cr_input_record import CompCROpSubset
16 from soc.experiment.alu_hier import ALU, DummyALU
17 from soc.experiment.compalu_multi import MultiCompUnit
18 from openpower.decoder.power_enums import MicrOp
19 from nmutil.gtkw import write_gtkw
20 from nmigen import Module, Signal
21 from nmigen.cli import rtlil
22
23 # NOTE: to use cxxsim, export NMIGEN_SIM_MODE=cxxsim from the shell
24 # Also, check out the cxxsim nmigen branch, and latest yosys from git
25 from nmutil.sim_tmp_alternative import (Simulator, Settle, is_engine_pysim,
26 Passive)
27
28
29 def wrap(process):
30 def wrapper():
31 yield from process
32 return wrapper
33
34
35 class OperandProducer:
36 """
37 Produces an operand when requested by the Computation Unit
38 (`dut` parameter), using the `rel_o` / `go_i` handshake.
39
40 Attaches itself to the `dut` operand indexed by `op_index`.
41
42 Has a programmable delay between the assertion of `rel_o` and the
43 `go_i` pulse.
44
45 Data is presented only during the cycle in which `go_i` is active.
46
47 It adds itself as a passive process to the simulation (`sim` parameter).
48 Since it is passive, it will not hang the simulation, and does not need a
49 flag to terminate itself.
50 """
51 def __init__(self, sim, dut, op_index):
52 self.count = Signal(8, name=f"src{op_index + 1}_count")
53 """ transaction counter"""
54 # data and handshake signals from the DUT
55 self.port = dut.src_i[op_index]
56 self.go_i = dut.rd.go_i[op_index]
57 self.rel_o = dut.rd.rel_o[op_index]
58 # transaction parameters, passed via signals
59 self.delay = Signal(8)
60 self.data = Signal.like(self.port)
61 self.data_valid = False
62 # add ourselves to the simulation process list
63 sim.add_sync_process(self._process)
64
65 def _process(self):
66 yield Passive()
67 while True:
68 # Settle() is needed to give a quick response to
69 # the zero delay case
70 yield Settle()
71 # wait for rel_o to become active
72 while not (yield self.rel_o):
73 yield
74 yield Settle()
75 # read the transaction parameters
76 assert self.data_valid, "an unexpected operand was consumed"
77 delay = (yield self.delay)
78 data = (yield self.data)
79 # wait for `delay` cycles
80 for _ in range(delay):
81 yield
82 # activate go_i and present data, for one cycle
83 yield self.go_i.eq(1)
84 yield self.port.eq(data)
85 yield self.count.eq(self.count + 1)
86 yield
87 self.data_valid = False
88 yield self.go_i.eq(0)
89 yield self.port.eq(0)
90
91 def send(self, data, delay):
92 """
93 Schedules the module to send some `data`, counting `delay` cycles after
94 `rel_i` becomes active.
95
96 To be called from the main test-bench process,
97 it returns in the same cycle.
98
99 Communication with the worker process is done by means of
100 combinatorial simulation-only signals.
101
102 """
103 yield self.data.eq(data)
104 yield self.delay.eq(delay)
105 self.data_valid = True
106
107
108 class ResultConsumer:
109 """
110 Consumes a result when requested by the Computation Unit
111 (`dut` parameter), using the `rel_o` / `go_i` handshake.
112
113 Attaches itself to the `dut` result indexed by `op_index`.
114
115 Has a programmable delay between the assertion of `rel_o` and the
116 `go_i` pulse.
117
118 Data is retrieved only during the cycle in which `go_i` is active.
119
120 It adds itself as a passive process to the simulation (`sim` parameter).
121 Since it is passive, it will not hang the simulation, and does not need a
122 flag to terminate itself.
123 """
124 def __init__(self, sim, dut, op_index):
125 self.count = Signal(8, name=f"dest{op_index + 1}_count")
126 """ transaction counter"""
127 # data and handshake signals from the DUT
128 self.port = dut.dest[op_index]
129 self.go_i = dut.wr.go_i[op_index]
130 self.rel_o = dut.wr.rel_o[op_index]
131 # transaction parameters, passed via signals
132 self.delay = Signal(8)
133 self.expected = Signal.like(self.port)
134 self.expecting = False
135 # add ourselves to the simulation process list
136 sim.add_sync_process(self._process)
137
138 def _process(self):
139 yield Passive()
140 while True:
141 # Settle() is needed to give a quick response to
142 # the zero delay case
143 yield Settle()
144 # wait for rel_o to become active
145 while not (yield self.rel_o):
146 yield
147 yield Settle()
148 # read the transaction parameters
149 assert self.expecting, "an unexpected result was produced"
150 delay = (yield self.delay)
151 expected = (yield self.expected)
152 # wait for `delay` cycles
153 for _ in range(delay):
154 yield
155 # activate go_i for one cycle
156 yield self.go_i.eq(1)
157 yield self.count.eq(self.count + 1)
158 yield
159 # check received data against the expected value
160 result = (yield self.port)
161 assert result == expected,\
162 f"expected {expected}, received {result}"
163 yield self.go_i.eq(0)
164 yield self.port.eq(0)
165
166 def receive(self, expected, delay):
167 """
168 Schedules the module to receive some result,
169 counting `delay` cycles after `rel_i` becomes active.
170 As 'go_i' goes active, check the result with `expected`.
171
172 To be called from the main test-bench process,
173 it returns in the same cycle.
174
175 Communication with the worker process is done by means of
176 combinatorial simulation-only signals.
177 """
178 yield self.expected.eq(expected)
179 yield self.delay.eq(delay)
180 self.expecting = True
181
182
183 def op_sim(dut, a, b, op, inv_a=0, imm=0, imm_ok=0, zero_a=0):
184 yield dut.issue_i.eq(0)
185 yield
186 yield dut.src_i[0].eq(a)
187 yield dut.src_i[1].eq(b)
188 yield dut.oper_i.insn_type.eq(op)
189 yield dut.oper_i.invert_in.eq(inv_a)
190 yield dut.oper_i.imm_data.data.eq(imm)
191 yield dut.oper_i.imm_data.ok.eq(imm_ok)
192 yield dut.oper_i.zero_a.eq(zero_a)
193 yield dut.issue_i.eq(1)
194 yield
195 yield dut.issue_i.eq(0)
196 yield
197 if not imm_ok or not zero_a:
198 yield dut.rd.go_i.eq(0b11)
199 while True:
200 yield
201 rd_rel_o = yield dut.rd.rel_o
202 print("rd_rel", rd_rel_o)
203 if rd_rel_o:
204 break
205 yield dut.rd.go_i.eq(0)
206 else:
207 print("no go rd")
208
209 if len(dut.src_i) == 3:
210 yield dut.rd.go_i.eq(0b100)
211 while True:
212 yield
213 rd_rel_o = yield dut.rd.rel_o
214 print("rd_rel", rd_rel_o)
215 if rd_rel_o:
216 break
217 yield dut.rd.go_i.eq(0)
218 else:
219 print("no 3rd rd")
220
221 req_rel_o = yield dut.wr.rel_o
222 result = yield dut.data_o
223 print("req_rel", req_rel_o, result)
224 while True:
225 req_rel_o = yield dut.wr.rel_o
226 result = yield dut.data_o
227 print("req_rel", req_rel_o, result)
228 if req_rel_o:
229 break
230 yield
231 yield dut.wr.go_i[0].eq(1)
232 yield Settle()
233 result = yield dut.data_o
234 yield
235 print("result", result)
236 yield dut.wr.go_i[0].eq(0)
237 yield
238 return result
239
240
241 def scoreboard_sim_fsm(dut, producers, consumers):
242
243 # stores the operation count
244 op_count = 0
245
246 def op_sim_fsm(a, b, direction, expected, delays):
247 print("op_sim_fsm", a, b, direction, expected)
248 yield dut.issue_i.eq(0)
249 yield
250 # forward data and delays to the producers and consumers
251 yield from producers[0].send(a, delays[0])
252 yield from producers[1].send(b, delays[1])
253 yield from consumers[0].receive(expected, delays[2])
254 # submit operation, and assert issue_i for one cycle
255 yield dut.oper_i.sdir.eq(direction)
256 yield dut.issue_i.eq(1)
257 yield
258 yield dut.issue_i.eq(0)
259 # wait for busy to be negated
260 yield Settle()
261 while (yield dut.busy_o):
262 yield
263 yield Settle()
264 # update the operation count
265 nonlocal op_count
266 op_count = (op_count + 1) & 255
267 # check that producers and consumers have the same count
268 # this assures that no data was left unused or was lost
269 assert (yield producers[0].count) == op_count
270 assert (yield producers[1].count) == op_count
271 assert (yield consumers[0].count) == op_count
272
273 # 13 >> 2 = 3
274 # operand 1 arrives immediately
275 # operand 2 arrives after operand 1
276 # write data is accepted immediately
277 yield from op_sim_fsm(13, 2, 1, 3, [0, 2, 0])
278 # 3 << 4 = 48
279 # operand 2 arrives immediately
280 # operand 1 arrives after operand 2
281 # write data is accepted after some delay
282 yield from op_sim_fsm(3, 4, 0, 48, [2, 0, 2])
283 # 21 << 0 = 21
284 # operands 1 and 2 arrive at the same time
285 # write data is accepted after some delay
286 yield from op_sim_fsm(21, 0, 0, 21, [1, 1, 1])
287
288
289 def scoreboard_sim_dummy(op):
290 yield from op.issue([5, 2, 0], MicrOp.OP_NOP, [5],
291 src_delays=[0, 2, 1], dest_delays=[0])
292 yield from op.issue([9, 2, 0], MicrOp.OP_NOP, [9],
293 src_delays=[2, 1, 0], dest_delays=[2])
294 # test all combinations of masked input ports
295 yield from op.issue([5, 2, 0], MicrOp.OP_NOP, [0],
296 rdmaskn=[1, 0, 0],
297 src_delays=[0, 2, 1], dest_delays=[0])
298 yield from op.issue([9, 2, 0], MicrOp.OP_NOP, [9],
299 rdmaskn=[0, 1, 0],
300 src_delays=[2, 1, 0], dest_delays=[2])
301 yield from op.issue([5, 2, 0], MicrOp.OP_NOP, [5],
302 rdmaskn=[0, 0, 1],
303 src_delays=[2, 1, 0], dest_delays=[2])
304 yield from op.issue([9, 2, 0], MicrOp.OP_NOP, [9],
305 rdmaskn=[0, 1, 1],
306 src_delays=[2, 1, 0], dest_delays=[2])
307 yield from op.issue([9, 2, 0], MicrOp.OP_NOP, [0],
308 rdmaskn=[1, 1, 0],
309 src_delays=[2, 1, 0], dest_delays=[2])
310 yield from op.issue([9, 2, 0], MicrOp.OP_NOP, [0],
311 rdmaskn=[1, 1, 1],
312 src_delays=[2, 1, 0], dest_delays=[2])
313
314
315 class OpSim:
316 """ALU Operation issuer
317
318 Issues operations to the DUT"""
319 def __init__(self, dut, sim):
320 self.op_count = 0
321 self.zero_a_count = 0
322 self.imm_ok_count = 0
323 self.rdmaskn_count = [0] * len(dut.src_i)
324 self.wrmask_count = [0] * len(dut.dest)
325 self.dut = dut
326 # create one operand producer for each input port
327 self.producers = list()
328 for i in range(len(dut.src_i)):
329 self.producers.append(OperandProducer(sim, dut, i))
330 # create one result consumer for each output port
331 self.consumers = list()
332 for i in range(len(dut.dest)):
333 self.consumers.append(ResultConsumer(sim, dut, i))
334
335 def issue(self, src_i, op, expected, src_delays, dest_delays,
336 inv_a=0, imm=0, imm_ok=0, zero_a=0, rc=0,
337 rdmaskn=None, wrmask=None):
338 """Executes the issue operation"""
339 dut = self.dut
340 producers = self.producers
341 consumers = self.consumers
342 if rdmaskn is None:
343 rdmaskn = [0] * len(src_i)
344 if wrmask is None:
345 wrmask = [0] * len(expected)
346 yield dut.issue_i.eq(0)
347 yield
348 # forward data and delays to the producers and consumers
349 # first, send special cases (with zero_a and/or imm_ok)
350 if not zero_a:
351 yield from producers[0].send(src_i[0], src_delays[0])
352 if not imm_ok:
353 yield from producers[1].send(src_i[1], src_delays[1])
354 # then, send the rest (if any)
355 for i in range(2, len(producers)):
356 yield from producers[i].send(src_i[i], src_delays[i])
357 for i in range(len(consumers)):
358 yield from consumers[i].receive(expected[i], dest_delays[i])
359 # submit operation, and assert issue_i for one cycle
360 yield dut.oper_i.insn_type.eq(op)
361 if hasattr(dut.oper_i, "invert_in"):
362 yield dut.oper_i.invert_in.eq(inv_a)
363 if hasattr(dut.oper_i, "imm_data"):
364 yield dut.oper_i.imm_data.data.eq(imm)
365 yield dut.oper_i.imm_data.ok.eq(imm_ok)
366 if hasattr(dut.oper_i, "zero_a"):
367 yield dut.oper_i.zero_a.eq(zero_a)
368 if hasattr(dut.oper_i, "rc"):
369 yield dut.oper_i.rc.rc.eq(rc)
370 if hasattr(dut, "rdmaskn"):
371 rdmaskn_bits = 0
372 for i in range(len(rdmaskn)):
373 rdmaskn_bits |= rdmaskn[i] << i
374 yield dut.rdmaskn.eq(rdmaskn_bits)
375 yield dut.issue_i.eq(1)
376 yield
377 yield dut.issue_i.eq(0)
378 # deactivate decoder inputs along with issue_i, so we can be sure they
379 # were latched at the correct cycle
380 # note: rdmaskn is not latched, and must be held as long as
381 # busy_o is active
382 # See: https://bugs.libre-soc.org/show_bug.cgi?id=336#c44
383 yield self.dut.oper_i.insn_type.eq(0)
384 if hasattr(dut.oper_i, "invert_in"):
385 yield self.dut.oper_i.invert_in.eq(0)
386 if hasattr(dut.oper_i, "imm_data"):
387 yield self.dut.oper_i.imm_data.data.eq(0)
388 yield self.dut.oper_i.imm_data.ok.eq(0)
389 if hasattr(dut.oper_i, "zero_a"):
390 yield self.dut.oper_i.zero_a.eq(0)
391 if hasattr(dut.oper_i, "rc"):
392 yield dut.oper_i.rc.rc.eq(0)
393 # wait for busy to be negated
394 yield Settle()
395 while (yield dut.busy_o):
396 yield
397 yield Settle()
398 # now, deactivate rdmaskn
399 if hasattr(dut, "rdmaskn"):
400 yield dut.rdmaskn.eq(0)
401 # update the operation count
402 self.op_count = (self.op_count + 1) & 255
403 # On zero_a, imm_ok and rdmaskn executions, the producer counters will
404 # fall behind. But, by summing the following counts, the invariant is
405 # preserved.
406 if zero_a and not rdmaskn[0]:
407 self.zero_a_count += 1
408 if imm_ok and not rdmaskn[1]:
409 self.imm_ok_count += 1
410 for i in range(len(rdmaskn)):
411 if rdmaskn[i]:
412 self.rdmaskn_count[i] += 1
413 for i in range(len(wrmask)):
414 if wrmask[i]:
415 self.wrmask_count[i] += 1
416 # check that producers and consumers have the same count
417 # this assures that no data was left unused or was lost
418 # first, check special cases (zero_a and imm_ok)
419 port_a_cnt = \
420 (yield producers[0].count) \
421 + self.zero_a_count \
422 + self.rdmaskn_count[0]
423 port_b_cnt = \
424 (yield producers[1].count) \
425 + self.imm_ok_count \
426 + self.rdmaskn_count[1]
427 assert port_a_cnt == self.op_count
428 assert port_b_cnt == self.op_count
429 # then, check the rest (if any)
430 for i in range(2, len(producers)):
431 port_cnt = (yield producers[i].count) + self.rdmaskn_count[i]
432 assert port_cnt == self.op_count
433 # check write counter
434 for i in range(len(consumers)):
435 port_cnt = (yield consumers[i].count) + self.wrmask_count[i]
436 assert port_cnt == self.op_count
437
438
439 def scoreboard_sim(op):
440 # the following tests cases have rc=0, so no CR output is expected
441 # zero (no) input operands test
442 # 0 + 8 = 8
443 yield from op.issue([5, 2], MicrOp.OP_ADD, [8, 0],
444 zero_a=1, imm=8, imm_ok=1,
445 wrmask=[0, 1],
446 src_delays=[0, 2], dest_delays=[0, 0])
447 # 5 + 8 = 13
448 yield from op.issue([5, 2], MicrOp.OP_ADD, [13, 0],
449 inv_a=0, imm=8, imm_ok=1,
450 wrmask=[0, 1],
451 src_delays=[2, 0], dest_delays=[2, 0])
452 # 5 + 2 = 7
453 yield from op.issue([5, 2], MicrOp.OP_ADD, [7, 0],
454 wrmask=[0, 1],
455 src_delays=[1, 1], dest_delays=[1, 0])
456 # (-6) + 2 = (-4)
457 yield from op.issue([5, 2], MicrOp.OP_ADD, [65532, 0],
458 inv_a=1,
459 wrmask=[0, 1],
460 src_delays=[1, 2], dest_delays=[0, 0])
461 # 0 + 2 = 2
462 yield from op.issue([5, 2], MicrOp.OP_ADD, [2, 0],
463 zero_a=1,
464 wrmask=[0, 1],
465 src_delays=[2, 0], dest_delays=[1, 0])
466
467 # test all combinations of masked input ports
468 # NOP does not make any request nor response
469 yield from op.issue([5, 2], MicrOp.OP_NOP, [0, 0],
470 rdmaskn=[1, 1], wrmask=[1, 1],
471 src_delays=[1, 2], dest_delays=[1, 0])
472 # sign_extend(0x80) = 0xFF80
473 yield from op.issue([0x80, 2], MicrOp.OP_EXTS, [0xFF80, 0],
474 rdmaskn=[0, 1], wrmask=[0, 1],
475 src_delays=[2, 1], dest_delays=[0, 0])
476 # sign_extend(0x80) = 0xFF80
477 yield from op.issue([2, 0x80], MicrOp.OP_EXTSWSLI, [0xFF80, 0],
478 rdmaskn=[1, 0], wrmask=[0, 1],
479 src_delays=[1, 2], dest_delays=[1, 0])
480
481 # test combinatorial zero-delay operation
482 # In the test ALU, any operation other than ADD, MUL, EXTS or SHR
483 # is zero-delay, and do a subtraction.
484 # 5 - 2 = 3
485 yield from op.issue([5, 2], MicrOp.OP_CMP, [3, 0],
486 wrmask=[0, 1],
487 src_delays=[0, 1], dest_delays=[2, 0])
488
489 # test with rc=1, so expect results on the CR output port
490 # 5 + 2 = 7
491 # 7 > 0 => CR = 0b100
492 yield from op.issue([5, 2], MicrOp.OP_ADD, [7, 0b100],
493 rc=1,
494 src_delays=[1, 1], dest_delays=[1, 0])
495 # sign_extend(0x80) = 0xFF80
496 # -128 < 0 => CR = 0b010
497 yield from op.issue([0x80, 2], MicrOp.OP_EXTS, [0xFF80, 0b010],
498 rc=1, rdmaskn=[0, 1],
499 src_delays=[2, 1], dest_delays=[0, 2])
500 # 5 - 5 = 0
501 # 0 == 0 => CR = 0b001
502 yield from op.issue([5, 2], MicrOp.OP_CMP, [0, 0b001],
503 imm=5, imm_ok=1, rc=1,
504 src_delays=[0, 1], dest_delays=[2, 1])
505
506
507 def test_compunit_fsm():
508 style = {
509 'in': {'color': 'orange'},
510 'out': {'color': 'yellow'},
511 }
512 traces = [
513 'clk',
514 ('operation port', {'color': 'red'}, [
515 'cu_issue_i', 'cu_busy_o',
516 {'comment': 'operation'},
517 'oper_i_None__sdir']),
518 ('operand 1 port', 'in', [
519 ('cu_rd__rel_o[1:0]', {'bit': 1}),
520 ('cu_rd__go_i[1:0]', {'bit': 1}),
521 'src1_i[7:0]']),
522 ('operand 2 port', 'in', [
523 ('cu_rd__rel_o[1:0]', {'bit': 0}),
524 ('cu_rd__go_i[1:0]', {'bit': 0}),
525 'src2_i[7:0]']),
526 ('result port', 'out', [
527 'cu_wr__rel_o', 'cu_wr__go_i', 'dest1_o[7:0]']),
528 ('alu', {'submodule': 'alu'}, [
529 ('prev port', 'in', [
530 'op__sdir', 'p_data_i[7:0]', 'p_shift_i[7:0]',
531 ({'submodule': 'p'},
532 ['p_i_valid', 'p_o_ready'])]),
533 ('next port', 'out', [
534 'n_data_o[7:0]',
535 ({'submodule': 'n'},
536 ['n_o_valid', 'n_i_ready'])])]),
537 ('debug', {'module': 'bench'},
538 ['src1_count[7:0]', 'src2_count[7:0]', 'dest1_count[7:0]'])]
539
540 write_gtkw(
541 "test_compunit_fsm1.gtkw",
542 "test_compunit_fsm1.vcd",
543 traces, style,
544 module='bench.top.cu'
545 )
546 m = Module()
547 alu = Shifter(8)
548 dut = MultiCompUnit(8, alu, CompFSMOpSubset)
549 m.submodules.cu = dut
550
551 vl = rtlil.convert(dut, ports=dut.ports())
552 with open("test_compunit_fsm1.il", "w") as f:
553 f.write(vl)
554
555 sim = Simulator(m)
556 sim.add_clock(1e-6)
557
558 # create one operand producer for each input port
559 prod_a = OperandProducer(sim, dut, 0)
560 prod_b = OperandProducer(sim, dut, 1)
561 # create an result consumer for the output port
562 cons = ResultConsumer(sim, dut, 0)
563 sim.add_sync_process(wrap(scoreboard_sim_fsm(dut,
564 [prod_a, prod_b],
565 [cons])))
566 sim_writer = sim.write_vcd('test_compunit_fsm1.vcd',
567 traces=[prod_a.count,
568 prod_b.count,
569 cons.count])
570 with sim_writer:
571 sim.run()
572
573
574 def test_compunit():
575
576 m = Module()
577 alu = ALU(16)
578 dut = MultiCompUnit(16, alu, CompALUOpSubset, n_dst=2)
579 m.submodules.cu = dut
580
581 vl = rtlil.convert(dut, ports=dut.ports())
582 with open("test_compunit1.il", "w") as f:
583 f.write(vl)
584
585 sim = Simulator(m)
586 sim.add_clock(1e-6)
587
588 # create an operation issuer
589 op = OpSim(dut, sim)
590 sim.add_sync_process(wrap(scoreboard_sim(op)))
591 sim_writer = sim.write_vcd('test_compunit1.vcd')
592 with sim_writer:
593 sim.run()
594
595
596 def test_compunit_regspec2_fsm():
597
598 inspec = [('INT', 'data', '0:15'),
599 ('INT', 'shift', '0:15')]
600 outspec = [('INT', 'data', '0:15')]
601
602 regspec = (inspec, outspec)
603
604 m = Module()
605 alu = Shifter(8)
606 dut = MultiCompUnit(regspec, alu, CompFSMOpSubset)
607 m.submodules.cu = dut
608
609 sim = Simulator(m)
610 sim.add_clock(1e-6)
611
612 # create one operand producer for each input port
613 prod_a = OperandProducer(sim, dut, 0)
614 prod_b = OperandProducer(sim, dut, 1)
615 # create an result consumer for the output port
616 cons = ResultConsumer(sim, dut, 0)
617 sim.add_sync_process(wrap(scoreboard_sim_fsm(dut,
618 [prod_a, prod_b],
619 [cons])))
620 sim_writer = sim.write_vcd('test_compunit_regspec2_fsm.vcd',
621 traces=[prod_a.count,
622 prod_b.count,
623 cons.count])
624 with sim_writer:
625 sim.run()
626
627
628 def test_compunit_regspec3():
629
630 style = {
631 'in': {'color': 'orange'},
632 'out': {'color': 'yellow'},
633 }
634 traces = [
635 'clk',
636 ('operation port', {'color': 'red'}, [
637 'cu_issue_i', 'cu_busy_o',
638 {'comment': 'operation'},
639 ('oper_i_None__insn_type'
640 + ('' if is_engine_pysim() else '[6:0]'),
641 {'display': 'insn_type'})]),
642 ('operand 1 port', 'in', [
643 ('cu_rdmaskn_i[2:0]', {'bit': 2}),
644 ('cu_rd__rel_o[2:0]', {'bit': 2}),
645 ('cu_rd__go_i[2:0]', {'bit': 2}),
646 'src1_i[15:0]']),
647 ('operand 2 port', 'in', [
648 ('cu_rdmaskn_i[2:0]', {'bit': 1}),
649 ('cu_rd__rel_o[2:0]', {'bit': 1}),
650 ('cu_rd__go_i[2:0]', {'bit': 1}),
651 'src2_i[15:0]']),
652 ('operand 3 port', 'in', [
653 ('cu_rdmaskn_i[2:0]', {'bit': 0}),
654 ('cu_rd__rel_o[2:0]', {'bit': 0}),
655 ('cu_rd__go_i[2:0]', {'bit': 0}),
656 'src1_i[15:0]']),
657 ('result port', 'out', [
658 'cu_wrmask_o', 'cu_wr__rel_o', 'cu_wr__go_i', 'dest1_o[15:0]']),
659 ('alu', {'submodule': 'alu'}, [
660 ('prev port', 'in', [
661 'oper_i_None__insn_type', 'i1[15:0]',
662 'i_valid', 'o_ready']),
663 ('next port', 'out', [
664 'alu_o[15:0]', 'o_valid', 'i_ready'])])]
665
666 write_gtkw("test_compunit_regspec3.gtkw",
667 "test_compunit_regspec3.vcd",
668 traces, style,
669 clk_period=1e-6,
670 module='bench.top.cu')
671
672 inspec = [('INT', 'a', '0:15'),
673 ('INT', 'b', '0:15'),
674 ('INT', 'c', '0:15')]
675 outspec = [('INT', 'o', '0:15')]
676
677 regspec = (inspec, outspec)
678
679 m = Module()
680 alu = DummyALU(16)
681 dut = MultiCompUnit(regspec, alu, CompCROpSubset)
682 m.submodules.cu = dut
683
684 sim = Simulator(m)
685 sim.add_clock(1e-6)
686
687 # create an operation issuer
688 op = OpSim(dut, sim)
689 sim.add_sync_process(wrap(scoreboard_sim_dummy(op)))
690 sim_writer = sim.write_vcd('test_compunit_regspec3.vcd')
691 with sim_writer:
692 sim.run()
693
694
695 def test_compunit_regspec1():
696
697 style = {
698 'in': {'color': 'orange'},
699 'out': {'color': 'yellow'},
700 }
701 traces = [
702 'clk',
703 ('operation port', {'color': 'red'}, [
704 'cu_issue_i', 'cu_busy_o',
705 {'comment': 'operation'},
706 ('oper_i_None__insn_type'
707 + ('' if is_engine_pysim() else '[6:0]'),
708 {'display': 'insn_type'}),
709 ('oper_i_None__invert_in', {'display': 'invert_in'}),
710 ('oper_i_None__imm_data__data[63:0]', {'display': 'data[63:0]'}),
711 ('oper_i_None__imm_data__ok', {'display': 'imm_ok'}),
712 ('oper_i_None__zero_a', {'display': 'zero_a'}),
713 ('oper_i_None__rc__rc', {'display': 'rc'})]),
714 ('operand 1 port', 'in', [
715 ('cu_rdmaskn_i[1:0]', {'bit': 1}),
716 ('cu_rd__rel_o[1:0]', {'bit': 1}),
717 ('cu_rd__go_i[1:0]', {'bit': 1}),
718 'src1_i[15:0]']),
719 ('operand 2 port', 'in', [
720 ('cu_rdmaskn_i[1:0]', {'bit': 0}),
721 ('cu_rd__rel_o[1:0]', {'bit': 0}),
722 ('cu_rd__go_i[1:0]', {'bit': 0}),
723 'src2_i[15:0]']),
724 ('result port', 'out', [
725 ('cu_wrmask_o[1:0]', {'bit': 1}),
726 ('cu_wr__rel_o[1:0]', {'bit': 1}),
727 ('cu_wr__go_i[1:0]', {'bit': 1}),
728 'dest1_o[15:0]']),
729 ('cr port', 'out', [
730 ('cu_wrmask_o[1:0]', {'bit': 0}),
731 ('cu_wr__rel_o[1:0]', {'bit': 0}),
732 ('cu_wr__go_i[1:0]', {'bit': 0}),
733 'dest2_o[15:0]']),
734 ('alu', {'submodule': 'alu'}, [
735 ('prev port', 'in', [
736 'op__insn_type', 'op__invert_in', 'a[15:0]', 'b[15:0]',
737 'i_valid', 'o_ready']),
738 ('next port', 'out', [
739 'alu_o[15:0]', 'o_valid', 'i_ready',
740 'alu_o_ok', 'alu_cr_ok'])]),
741 ('debug', {'module': 'bench'},
742 ['src1_count[7:0]', 'src2_count[7:0]', 'dest1_count[7:0]'])]
743
744 write_gtkw("test_compunit_regspec1.gtkw",
745 "test_compunit_regspec1.vcd",
746 traces, style,
747 clk_period=1e-6,
748 module='bench.top.cu')
749
750 inspec = [('INT', 'a', '0:15'),
751 ('INT', 'b', '0:15')]
752 outspec = [('INT', 'o', '0:15'),
753 ('INT', 'cr', '0:15')]
754
755 regspec = (inspec, outspec)
756
757 m = Module()
758 alu = ALU(16)
759 dut = MultiCompUnit(regspec, alu, CompALUOpSubset)
760 m.submodules.cu = dut
761
762 vl = rtlil.convert(dut, ports=dut.ports())
763 with open("test_compunit_regspec1.il", "w") as f:
764 f.write(vl)
765
766 sim = Simulator(m)
767 sim.add_clock(1e-6)
768
769 # create an operation issuer
770 op = OpSim(dut, sim)
771 sim.add_sync_process(wrap(scoreboard_sim(op)))
772 sim_writer = sim.write_vcd('test_compunit_regspec1.vcd',
773 traces=[op.producers[0].count,
774 op.producers[1].count,
775 op.consumers[0].count])
776 with sim_writer:
777 sim.run()
778
779
780 if __name__ == '__main__':
781 test_compunit()
782 test_compunit_fsm()
783 test_compunit_regspec1()
784 test_compunit_regspec2_fsm()
785 test_compunit_regspec3()