4c2e1347adc29a4d2b05d018746bf6fff1a458a0
[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 combinatorial zero-delay operation
468 # In the test ALU, any operation other than ADD, MUL, EXTS or SHR
469 # is zero-delay, and do a subtraction.
470 # 5 - 2 = 3
471 yield from op.issue([5, 2], MicrOp.OP_CMP, [3, 0],
472 wrmask=[0, 1],
473 src_delays=[0, 1], dest_delays=[2, 0])
474 # test all combinations of masked input ports
475 # NOP does not make any request nor response
476 yield from op.issue([5, 2], MicrOp.OP_NOP, [0, 0],
477 rdmaskn=[1, 1], wrmask=[1, 1],
478 src_delays=[1, 2], dest_delays=[1, 0])
479 # sign_extend(0x80) = 0xFF80
480 yield from op.issue([0x80, 2], MicrOp.OP_EXTS, [0xFF80, 0],
481 rdmaskn=[0, 1], wrmask=[0, 1],
482 src_delays=[2, 1], dest_delays=[0, 0])
483 # sign_extend(0x80) = 0xFF80
484 yield from op.issue([2, 0x80], MicrOp.OP_EXTSWSLI, [0xFF80, 0],
485 rdmaskn=[1, 0], wrmask=[0, 1],
486 src_delays=[1, 2], dest_delays=[1, 0])
487 # test with rc=1, so expect results on the CR output port
488 # 5 + 2 = 7
489 # 7 > 0 => CR = 0b100
490 yield from op.issue([5, 2], MicrOp.OP_ADD, [7, 0b100],
491 rc=1,
492 src_delays=[1, 1], dest_delays=[1, 0])
493 # sign_extend(0x80) = 0xFF80
494 # -128 < 0 => CR = 0b010
495 yield from op.issue([0x80, 2], MicrOp.OP_EXTS, [0xFF80, 0b010],
496 rc=1, rdmaskn=[0, 1],
497 src_delays=[2, 1], dest_delays=[0, 2])
498 # 5 - 5 = 0
499 # 0 == 0 => CR = 0b001
500 yield from op.issue([5, 2], MicrOp.OP_CMP, [0, 0b001],
501 imm=5, imm_ok=1, rc=1,
502 src_delays=[0, 1], dest_delays=[2, 1])
503
504
505 def test_compunit_fsm():
506 style = {
507 'in': {'color': 'orange'},
508 'out': {'color': 'yellow'},
509 }
510 traces = [
511 'clk',
512 ('operation port', {'color': 'red'}, [
513 'cu_issue_i', 'cu_busy_o',
514 {'comment': 'operation'},
515 'oper_i_None__sdir']),
516 ('operand 1 port', 'in', [
517 ('cu_rd__rel_o[1:0]', {'bit': 1}),
518 ('cu_rd__go_i[1:0]', {'bit': 1}),
519 'src1_i[7:0]']),
520 ('operand 2 port', 'in', [
521 ('cu_rd__rel_o[1:0]', {'bit': 0}),
522 ('cu_rd__go_i[1:0]', {'bit': 0}),
523 'src2_i[7:0]']),
524 ('result port', 'out', [
525 'cu_wr__rel_o', 'cu_wr__go_i', 'dest1_o[7:0]']),
526 ('alu', {'submodule': 'alu'}, [
527 ('prev port', 'in', [
528 'op__sdir', 'p_data_i[7:0]', 'p_shift_i[7:0]',
529 ({'submodule': 'p'},
530 ['p_i_valid', 'p_o_ready'])]),
531 ('next port', 'out', [
532 'n_data_o[7:0]',
533 ({'submodule': 'n'},
534 ['n_o_valid', 'n_i_ready'])])]),
535 ('debug', {'module': 'top'},
536 ['src1_count[7:0]', 'src2_count[7:0]', 'dest1_count[7:0]'])]
537
538 write_gtkw(
539 "test_compunit_fsm1.gtkw",
540 "test_compunit_fsm1.vcd",
541 traces, style,
542 module='top.cu'
543 )
544 m = Module()
545 alu = Shifter(8)
546 dut = MultiCompUnit(8, alu, CompFSMOpSubset)
547 m.submodules.cu = dut
548
549 vl = rtlil.convert(dut, ports=dut.ports())
550 with open("test_compunit_fsm1.il", "w") as f:
551 f.write(vl)
552
553 sim = Simulator(m)
554 sim.add_clock(1e-6)
555
556 # create one operand producer for each input port
557 prod_a = OperandProducer(sim, dut, 0)
558 prod_b = OperandProducer(sim, dut, 1)
559 # create an result consumer for the output port
560 cons = ResultConsumer(sim, dut, 0)
561 sim.add_sync_process(wrap(scoreboard_sim_fsm(dut,
562 [prod_a, prod_b],
563 [cons])))
564 sim_writer = sim.write_vcd('test_compunit_fsm1.vcd',
565 traces=[prod_a.count,
566 prod_b.count,
567 cons.count])
568 with sim_writer:
569 sim.run()
570
571
572 def test_compunit():
573
574 m = Module()
575 alu = ALU(16)
576 dut = MultiCompUnit(16, alu, CompALUOpSubset, n_dst=2)
577 m.submodules.cu = dut
578
579 vl = rtlil.convert(dut, ports=dut.ports())
580 with open("test_compunit1.il", "w") as f:
581 f.write(vl)
582
583 sim = Simulator(m)
584 sim.add_clock(1e-6)
585
586 # create an operation issuer
587 op = OpSim(dut, sim)
588 sim.add_sync_process(wrap(scoreboard_sim(op)))
589 sim_writer = sim.write_vcd('test_compunit1.vcd')
590 with sim_writer:
591 sim.run()
592
593
594 def test_compunit_regspec2_fsm():
595
596 inspec = [('INT', 'data', '0:15'),
597 ('INT', 'shift', '0:15')]
598 outspec = [('INT', 'data', '0:15')]
599
600 regspec = (inspec, outspec)
601
602 m = Module()
603 alu = Shifter(8)
604 dut = MultiCompUnit(regspec, alu, CompFSMOpSubset)
605 m.submodules.cu = dut
606
607 sim = Simulator(m)
608 sim.add_clock(1e-6)
609
610 # create one operand producer for each input port
611 prod_a = OperandProducer(sim, dut, 0)
612 prod_b = OperandProducer(sim, dut, 1)
613 # create an result consumer for the output port
614 cons = ResultConsumer(sim, dut, 0)
615 sim.add_sync_process(wrap(scoreboard_sim_fsm(dut,
616 [prod_a, prod_b],
617 [cons])))
618 sim_writer = sim.write_vcd('test_compunit_regspec2_fsm.vcd',
619 traces=[prod_a.count,
620 prod_b.count,
621 cons.count])
622 with sim_writer:
623 sim.run()
624
625
626 def test_compunit_regspec3():
627
628 style = {
629 'in': {'color': 'orange'},
630 'out': {'color': 'yellow'},
631 }
632 traces = [
633 'clk',
634 ('operation port', {'color': 'red'}, [
635 'cu_issue_i', 'cu_busy_o',
636 {'comment': 'operation'},
637 ('oper_i_None__insn_type'
638 + ('' if is_engine_pysim() else '[6:0]'),
639 {'display': 'insn_type'})]),
640 ('operand 1 port', 'in', [
641 ('cu_rdmaskn_i[2:0]', {'bit': 2}),
642 ('cu_rd__rel_o[2:0]', {'bit': 2}),
643 ('cu_rd__go_i[2:0]', {'bit': 2}),
644 'src1_i[15:0]']),
645 ('operand 2 port', 'in', [
646 ('cu_rdmaskn_i[2:0]', {'bit': 1}),
647 ('cu_rd__rel_o[2:0]', {'bit': 1}),
648 ('cu_rd__go_i[2:0]', {'bit': 1}),
649 'src2_i[15:0]']),
650 ('operand 3 port', 'in', [
651 ('cu_rdmaskn_i[2:0]', {'bit': 0}),
652 ('cu_rd__rel_o[2:0]', {'bit': 0}),
653 ('cu_rd__go_i[2:0]', {'bit': 0}),
654 'src1_i[15:0]']),
655 ('result port', 'out', [
656 'cu_wrmask_o', 'cu_wr__rel_o', 'cu_wr__go_i', 'dest1_o[15:0]']),
657 ('alu', {'submodule': 'alu'}, [
658 ('prev port', 'in', [
659 'oper_i_None__insn_type', 'i1[15:0]',
660 'i_valid', 'o_ready']),
661 ('next port', 'out', [
662 'alu_o[15:0]', 'o_valid', 'i_ready'])])]
663
664 write_gtkw("test_compunit_regspec3.gtkw",
665 "test_compunit_regspec3.vcd",
666 traces, style,
667 clk_period=1e-6,
668 module='top.cu')
669
670 inspec = [('INT', 'a', '0:15'),
671 ('INT', 'b', '0:15'),
672 ('INT', 'c', '0:15')]
673 outspec = [('INT', 'o', '0:15')]
674
675 regspec = (inspec, outspec)
676
677 m = Module()
678 alu = DummyALU(16)
679 dut = MultiCompUnit(regspec, alu, CompCROpSubset)
680 m.submodules.cu = dut
681
682 sim = Simulator(m)
683 sim.add_clock(1e-6)
684
685 # create an operation issuer
686 op = OpSim(dut, sim)
687 sim.add_sync_process(wrap(scoreboard_sim_dummy(op)))
688 sim_writer = sim.write_vcd('test_compunit_regspec3.vcd')
689 with sim_writer:
690 sim.run()
691
692
693 def test_compunit_regspec1():
694
695 style = {
696 'in': {'color': 'orange'},
697 'out': {'color': 'yellow'},
698 }
699 traces = [
700 'clk',
701 ('operation port', {'color': 'red'}, [
702 'cu_issue_i', 'cu_busy_o',
703 {'comment': 'operation'},
704 ('oper_i_None__insn_type'
705 + ('' if is_engine_pysim() else '[6:0]'),
706 {'display': 'insn_type'}),
707 ('oper_i_None__invert_in', {'display': 'invert_in'}),
708 ('oper_i_None__imm_data__data[63:0]', {'display': 'data[63:0]'}),
709 ('oper_i_None__imm_data__ok', {'display': 'imm_ok'}),
710 ('oper_i_None__zero_a', {'display': 'zero_a'}),
711 ('oper_i_None__rc__rc', {'display': 'rc'})]),
712 ('operand 1 port', 'in', [
713 ('cu_rdmaskn_i[1:0]', {'bit': 1}),
714 ('cu_rd__rel_o[1:0]', {'bit': 1}),
715 ('cu_rd__go_i[1:0]', {'bit': 1}),
716 'src1_i[15:0]']),
717 ('operand 2 port', 'in', [
718 ('cu_rdmaskn_i[1:0]', {'bit': 0}),
719 ('cu_rd__rel_o[1:0]', {'bit': 0}),
720 ('cu_rd__go_i[1:0]', {'bit': 0}),
721 'src2_i[15:0]']),
722 ('result port', 'out', [
723 ('cu_wrmask_o[1:0]', {'bit': 1}),
724 ('cu_wr__rel_o[1:0]', {'bit': 1}),
725 ('cu_wr__go_i[1:0]', {'bit': 1}),
726 'dest1_o[15:0]']),
727 ('cr port', 'out', [
728 ('cu_wrmask_o[1:0]', {'bit': 0}),
729 ('cu_wr__rel_o[1:0]', {'bit': 0}),
730 ('cu_wr__go_i[1:0]', {'bit': 0}),
731 'dest2_o[15:0]']),
732 ('alu', {'submodule': 'alu'}, [
733 ('prev port', 'in', [
734 'op__insn_type', 'op__invert_in', 'a[15:0]', 'b[15:0]',
735 'i_valid', 'o_ready']),
736 ('next port', 'out', [
737 'alu_o[15:0]', 'o_valid', 'i_ready',
738 'alu_o_ok', 'alu_cr_ok'])]),
739 ('debug', {'module': 'top'},
740 ['src1_count[7:0]', 'src2_count[7:0]', 'dest1_count[7:0]'])]
741
742 write_gtkw("test_compunit_regspec1.gtkw",
743 "test_compunit_regspec1.vcd",
744 traces, style,
745 clk_period=1e-6,
746 module='top.cu')
747
748 inspec = [('INT', 'a', '0:15'),
749 ('INT', 'b', '0:15')]
750 outspec = [('INT', 'o', '0:15'),
751 ('INT', 'cr', '0:15')]
752
753 regspec = (inspec, outspec)
754
755 m = Module()
756 alu = ALU(16)
757 dut = MultiCompUnit(regspec, alu, CompALUOpSubset)
758 m.submodules.cu = dut
759
760 vl = rtlil.convert(dut, ports=dut.ports())
761 with open("test_compunit_regspec1.il", "w") as f:
762 f.write(vl)
763
764 sim = Simulator(m)
765 sim.add_clock(1e-6)
766
767 # create an operation issuer
768 op = OpSim(dut, sim)
769 sim.add_sync_process(wrap(scoreboard_sim(op)))
770 sim_writer = sim.write_vcd('test_compunit_regspec1.vcd',
771 traces=[op.producers[0].count,
772 op.producers[1].count,
773 op.consumers[0].count])
774 with sim_writer:
775 sim.run()
776
777
778 if __name__ == '__main__':
779 test_compunit()
780 test_compunit_fsm()
781 test_compunit_regspec1()
782 test_compunit_regspec2_fsm()
783 test_compunit_regspec3()