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))