+class OperandProducer:
+ """
+ Produces an operand when requested by the Computation Unit
+ (`dut` parameter), using the `rel_o` / `go_i` handshake.
+
+ Attaches itself to the `dut` operand indexed by `op_index`.
+
+ Has a programmable delay between the assertion of `rel_o` and the
+ `go_i` pulse.
+
+ Data is presented only during the cycle in which `go_i` is active.
+
+ It adds itself as a passive process to the simulation (`sim` parameter).
+ Since it is passive, it will not hang the simulation, and does not need a
+ flag to terminate itself.
+ """
+ def __init__(self, sim, dut, op_index):
+ # data and handshake signals from the DUT
+ self.port = dut.src_i[op_index]
+ self.go_i = dut.rd.go_i[op_index]
+ self.rel_o = dut.rd.rel_o[op_index]
+ # transaction parameters, passed via signals
+ self.delay = Signal(8)
+ self.data = Signal.like(self.port)
+ # add ourselves to the simulation process list
+ sim.add_sync_process(self._process)
+
+ def _process(self):
+ yield Passive()
+ while True:
+ # Settle() is needed to give a quick response to
+ # the zero delay case
+ yield Settle()
+ # wait for rel_o to become active
+ while not (yield self.rel_o):
+ yield
+ yield Settle()
+ # read the transaction parameters
+ delay = (yield self.delay)
+ data = (yield self.data)
+ # wait for `delay` cycles
+ for _ in range(delay):
+ yield
+ # activate go_i and present data, for one cycle
+ yield self.go_i.eq(1)
+ yield self.port.eq(data)
+ yield
+ yield self.go_i.eq(0)
+ yield self.port.eq(0)
+
+ def send(self, data, delay):
+ """
+ Schedules the module to send some `data`, counting `delay` cycles after
+ `rel_i` becomes active.
+
+ To be called from the main test-bench process,
+ it returns in the same cycle.
+
+ Communication with the worker process is done by means of
+ combinatorial simulation-only signals.
+
+ """
+ yield self.data.eq(data)
+ yield self.delay.eq(delay)
+
+
+def op_sim_fsm(dut, a, b, direction, producers, delays):