mirror of
https://github.com/enjoy-digital/litex.git
synced 2025-01-04 09:52:26 -05:00
actorlib: generator-based generic simulation actor
This commit is contained in:
parent
b145f9e5e2
commit
910c7806cf
2 changed files with 103 additions and 0 deletions
33
examples/dataflow_sim.py
Normal file
33
examples/dataflow_sim.py
Normal file
|
@ -0,0 +1,33 @@
|
|||
import networkx as nx
|
||||
|
||||
from migen.fhdl.structure import *
|
||||
from migen.flow.actor import *
|
||||
from migen.flow.network import *
|
||||
from migen.actorlib.sim import *
|
||||
from migen.sim.generic import Simulator
|
||||
from migen.sim.icarus import Runner
|
||||
|
||||
def source_gen():
|
||||
for i in range(10):
|
||||
print("Sending: " + str(i))
|
||||
yield Token("source", {"value": i})
|
||||
|
||||
def sink_gen():
|
||||
while True:
|
||||
t = Token("sink")
|
||||
yield t
|
||||
print("Received: " + str(t.value["value"]))
|
||||
|
||||
def main():
|
||||
source = SimActor(source_gen(), ("source", Source, [("value", BV(32))]))
|
||||
sink = SimActor(sink_gen(), ("sink", Sink, [("value", BV(32))]))
|
||||
g = nx.MultiDiGraph()
|
||||
add_connection(g, source, sink)
|
||||
comp = CompositeActor(g)
|
||||
def end_simulation(s):
|
||||
s.interrupt = source.done
|
||||
fragment = comp.get_fragment() + Fragment(sim=[end_simulation])
|
||||
sim = Simulator(fragment, Runner())
|
||||
sim.run()
|
||||
|
||||
main()
|
70
migen/actorlib/sim.py
Normal file
70
migen/actorlib/sim.py
Normal file
|
@ -0,0 +1,70 @@
|
|||
from migen.fhdl.structure import *
|
||||
from migen.flow.actor import *
|
||||
|
||||
class Token:
|
||||
def __init__(self, endpoint, value=None):
|
||||
self.endpoint = endpoint
|
||||
self.value = value
|
||||
|
||||
# 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 SimActor(Actor):
|
||||
def __init__(self, generator, *endpoint_descriptions, **misc):
|
||||
self.generator = generator
|
||||
self.active = [] # TODO: use set
|
||||
self.done = False
|
||||
super().__init__(*endpoint_descriptions, **misc)
|
||||
|
||||
def _process_transactions(self, s):
|
||||
completed = []
|
||||
for token in self.active:
|
||||
ep = self.endpoints[token.endpoint]
|
||||
if isinstance(ep, Sink):
|
||||
if s.rd(ep.ack):
|
||||
if s.rd(ep.stb):
|
||||
token.value = s.multiread(ep.token)
|
||||
completed.append(token)
|
||||
s.wr(ep.ack, 0)
|
||||
else:
|
||||
s.wr(ep.ack, 1)
|
||||
elif isinstance(ep, Source):
|
||||
if s.rd(ep.stb):
|
||||
if s.rd(ep.ack):
|
||||
completed.append(token)
|
||||
s.wr(ep.stb, 0)
|
||||
else:
|
||||
s.wr(ep.stb, 1)
|
||||
s.multiwrite(ep.token, token.value)
|
||||
else:
|
||||
raise TypeError
|
||||
for token in completed: # XXX
|
||||
self.active.remove(token)
|
||||
|
||||
def _next_transactions(self):
|
||||
try:
|
||||
transactions = next(self.generator)
|
||||
except StopIteration:
|
||||
self.done = True
|
||||
transactions = None
|
||||
if isinstance(transactions, Token):
|
||||
self.active = [transactions]
|
||||
elif isinstance(transactions, tuple) or isinstance(transactions, list):
|
||||
self.active = list(transactions)
|
||||
elif transactions is None:
|
||||
self.active = []
|
||||
else:
|
||||
raise TypeError
|
||||
|
||||
def do_simulation(self, s):
|
||||
if not self.done:
|
||||
if self.active:
|
||||
self._process_transactions(s)
|
||||
else:
|
||||
self._next_transactions()
|
||||
|
||||
def get_fragment(self):
|
||||
return Fragment(sim=[self.do_simulation])
|
Loading…
Reference in a new issue