litex/migen/actorlib/sim.py

117 lines
3.2 KiB
Python

from migen.fhdl.std import *
from migen.flow.actor import *
from migen.flow.transactions import *
from migen.util.misc import xdir
def _sim_multiread(sim, obj):
if isinstance(obj, Signal):
return sim.rd(obj)
else:
r = {}
for k, v in xdir(obj, True):
rd = _sim_multiread(sim, v)
if isinstance(rd, int) or rd:
r[k] = rd
return r
def _sim_multiwrite(sim, obj, value):
if isinstance(obj, Signal):
sim.wr(obj, value)
else:
for k, v in value.items():
_sim_multiwrite(sim, getattr(obj, k), v)
# Generators yield None or a tuple of Tokens.
# Tokens for Sink endpoints are pulled and the "value" field filled in.
# Tokens for Source endpoints are pushed according to their "value" field.
#
# NB: the possibility to push several tokens at once is important to interact
# with actors that only accept a group of tokens when all of them are available.
class TokenExchanger(Module):
def __init__(self, generator, actor):
self.generator = generator
self.actor = actor
self.active = set()
self.busy = True
self.done = False
def _process_transactions(self, selfp):
completed = set()
for token in self.active:
ep = getattr(self.actor, token.endpoint)
if isinstance(ep, Sink):
if selfp.simulator.rd(ep.ack) and selfp.simulator.rd(ep.stb):
token.value = _sim_multiread(selfp.simulator, ep.payload)
completed.add(token)
selfp.simulator.wr(ep.ack, 0)
elif isinstance(ep, Source):
if selfp.simulator.rd(ep.ack) and selfp.simulator.rd(ep.stb):
completed.add(token)
selfp.simulator.wr(ep.stb, 0)
else:
raise TypeError
self.active -= completed
if not self.active:
self.busy = True
def _update_control_signals(self, selfp):
for token in self.active:
ep = getattr(self.actor, token.endpoint)
if isinstance(ep, Sink):
selfp.simulator.wr(ep.ack, 1)
elif isinstance(ep, Source):
_sim_multiwrite(selfp.simulator, ep.payload, token.value)
selfp.simulator.wr(ep.stb, 1)
else:
raise TypeError
def _next_transactions(self):
try:
transactions = next(self.generator)
except StopIteration:
self.busy = False
self.done = True
raise StopSimulation
if isinstance(transactions, Token):
self.active = {transactions}
elif isinstance(transactions, (tuple, list, set)):
self.active = set(transactions)
elif transactions is None:
self.active = set()
else:
raise TypeError
if self.active and all(transaction.idle_wait for transaction in self.active):
self.busy = False
def do_simulation(self, selfp):
if self.active:
self._process_transactions(selfp)
if not self.active:
self._next_transactions()
self._update_control_signals(selfp)
do_simulation.passive = True
class SimActor(Module):
def __init__(self, generator):
self.busy = Signal()
self.submodules.token_exchanger = TokenExchanger(generator, self)
def do_simulation(self, selfp):
selfp.busy = self.token_exchanger.busy
do_simulation.passive = True
def _dumper_gen(prefix):
while True:
t = Token("result")
yield t
if len(t.value) > 1:
s = str(t.value)
else:
s = str(list(t.value.values())[0])
print(prefix + s)
class Dumper(SimActor):
def __init__(self, layout, prefix=""):
self.result = Sink(layout)
SimActor.__init__(self, _dumper_gen(prefix))