From 63c1d7e4b7d41f71da7cd25a47457f4868410559 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sun, 26 Jan 2014 22:19:43 +0100 Subject: [PATCH] New simulation API --- examples/basic/graycounter.py | 14 +-- examples/dataflow/dma.py | 75 ++-------------- examples/dataflow/misc.py | 9 +- examples/dataflow/structuring.py | 8 +- examples/pytholite/basic.py | 12 ++- examples/pytholite/uio.py | 12 ++- examples/sim/abstract_transactions_lasmi.py | 17 ++-- examples/sim/abstract_transactions_wb.py | 14 +-- examples/sim/basic1.py | 19 ++-- examples/sim/basic2.py | 48 ++++------ examples/sim/cordic_err.py | 26 ++---- examples/sim/dataflow.py | 8 +- examples/sim/fir.py | 21 ++--- examples/sim/memory.py | 23 ++--- migen/actorlib/sim.py | 62 ++++++++----- migen/bus/lasmibus.py | 90 +++++++++---------- migen/bus/memory.py | 26 +++--- migen/bus/wishbone.py | 74 ++++++++------- migen/fhdl/module.py | 19 +++- migen/fhdl/structure.py | 3 + migen/flow/hooks.py | 6 +- migen/sim/generic.py | 67 +++----------- migen/sim/upper.py | 99 +++++++++++++++++++++ migen/test/support.py | 12 ++- migen/test/test_coding.py | 37 ++++---- migen/test/test_cordic.py | 20 ++--- migen/test/test_fifo.py | 14 +-- migen/test/test_signed.py | 28 +++--- migen/test/test_sort.py | 8 +- 29 files changed, 407 insertions(+), 464 deletions(-) create mode 100644 migen/sim/upper.py diff --git a/examples/basic/graycounter.py b/examples/basic/graycounter.py index 3a543e5f..be1b1838 100644 --- a/examples/basic/graycounter.py +++ b/examples/basic/graycounter.py @@ -2,7 +2,7 @@ from random import Random from migen.fhdl.std import * from migen.genlib.cdc import GrayCounter -from migen.sim.generic import Simulator +from migen.sim.generic import run_simulation class TB(Module): def __init__(self, width=3): @@ -10,10 +10,10 @@ class TB(Module): self.submodules.gc = GrayCounter(self.width) self.prng = Random(7345) - def do_simulation(self, s): - print("{0:0{1}b} CE={2} bin={3}".format(s.rd(self.gc.q), - self.width, s.rd(self.gc.ce), s.rd(self.gc.q_binary))) - s.wr(self.gc.ce, self.prng.getrandbits(1)) + def do_simulation(self, selfp): + print("{0:0{1}b} CE={2} bin={3}".format(selfp.gc.q, + self.width, selfp.gc.ce, selfp.gc.q_binary)) + selfp.gc.ce = self.prng.getrandbits(1) -sim = Simulator(TB()) -sim.run(35) +if __name__ == "__main__": + run_simulation(TB(), ncycles=35) diff --git a/examples/dataflow/dma.py b/examples/dataflow/dma.py index 9ae85ab4..8bd27282 100644 --- a/examples/dataflow/dma.py +++ b/examples/dataflow/dma.py @@ -3,10 +3,10 @@ from random import Random from migen.fhdl.std import * from migen.flow.network import * from migen.flow.transactions import * -from migen.actorlib import dma_wishbone, dma_asmi +from migen.actorlib import dma_wishbone from migen.actorlib.sim import * -from migen.bus import wishbone, asmibus -from migen.sim.generic import Simulator +from migen.bus import wishbone +from migen.sim.generic import run_simulation class MyModel: def read(self, address): @@ -19,9 +19,6 @@ class MyModelWB(MyModel, wishbone.TargetModel): def can_ack(self, bus): return self.prng.randrange(0, 2) -class MyModelASMI(MyModel, asmibus.TargetModel): - pass - def adrgen_gen(): for i in range(10): print("Address: " + hex(i)) @@ -73,9 +70,6 @@ class TBWishboneReader(TBWishbone): self.submodules.comp = CompositeActor(g) TBWishbone.__init__(self, self.reader) - def do_simulation(self, s): - s.interrupt = self.adrgen.token_exchanger.done and not s.rd(self.comp.busy) - class TBWishboneWriter(TBWishbone): def __init__(self): self.trgen = SimTrGen(30) @@ -85,65 +79,14 @@ class TBWishboneWriter(TBWishbone): self.submodules.comp = CompositeActor(g) TBWishbone.__init__(self, self.writer) - def do_simulation(self, s): - s.interrupt = self.trgen.token_exchanger.done and not s.rd(self.comp.busy) - -class TBAsmi(Module): - def __init__(self, nslots): - self.submodules.hub = asmibus.Hub(32, 32) - self.port = self.hub.get_port(nslots) - self.hub.finalize() - - self.submodules.peripheral = asmibus.Target(MyModelASMI(), self.hub) - self.submodules.tap = asmibus.Tap(self.hub) - -class TBAsmiReader(TBAsmi): - def __init__(self, nslots): - TBAsmi.__init__(self, nslots) - - self.adrgen = SimAdrGen(32) - self.reader = dma_asmi.Reader(self.port) - self.dumper = SimDumper() - g = DataFlowGraph() - g.add_connection(self.adrgen, self.reader) - g.add_connection(self.reader, self.dumper) - self.submodules.comp = CompositeActor(g) - - def do_simulation(self, s): - s.interrupt = self.adrgen.token_exchanger.done and not s.rd(self.comp.busy) - -class TBAsmiWriter(TBAsmi): - def __init__(self, nslots): - TBAsmi.__init__(self, nslots) - - self.trgen = SimTrGen(32) - self.writer = dma_asmi.Writer(self.port) - g = DataFlowGraph() - g.add_connection(self.trgen, self.writer) - self.submodules.comp = CompositeActor(g) - - def do_simulation(self, s): - s.interrupt = self.trgen.token_exchanger.done and not s.rd(self.comp.busy) - def test_wb_reader(): print("*** Testing Wishbone reader") - Simulator(TBWishboneReader()).run() + run_simulation(TBWishboneReader()) def test_wb_writer(): print("*** Testing Wishbone writer") - Simulator(TBWishboneWriter()).run() - -def test_asmi_reader(nslots): - print("*** Testing ASMI reader (nslots={})".format(nslots)) - Simulator(TBAsmiReader(nslots)).run() - -def test_asmi_writer(nslots): - print("*** Testing ASMI writer (nslots={})".format(nslots)) - Simulator(TBAsmiWriter(nslots)).run() - -test_wb_reader() -test_wb_writer() -test_asmi_reader(1) -test_asmi_reader(2) -test_asmi_writer(1) -test_asmi_writer(2) + run_simulation(TBWishboneWriter()) + +if __name__ == "__main__": + test_wb_reader() + test_wb_writer() diff --git a/examples/dataflow/misc.py b/examples/dataflow/misc.py index bb1df5b4..9eda30d8 100644 --- a/examples/dataflow/misc.py +++ b/examples/dataflow/misc.py @@ -2,7 +2,7 @@ from migen.flow.network import * from migen.flow.transactions import * from migen.actorlib import misc from migen.actorlib.sim import * -from migen.sim.generic import Simulator +from migen.sim.generic import run_simulation def source_gen(): for i in range(10): @@ -26,7 +26,7 @@ class SimSink(SimActor): self.sink = Sink([("value", 32)]) SimActor.__init__(self, sink_gen()) -def main(): +if __name__ == "__main__": source = SimSource() loop = misc.IntSequence(32) sink = SimSink() @@ -34,7 +34,4 @@ def main(): g.add_connection(source, loop) g.add_connection(loop, sink) comp = CompositeActor(g) - sim = Simulator(comp) - sim.run(500) - -main() + run_simulation(comp, ncycles=500) diff --git a/examples/dataflow/structuring.py b/examples/dataflow/structuring.py index 7f301468..ea2cea99 100644 --- a/examples/dataflow/structuring.py +++ b/examples/dataflow/structuring.py @@ -7,8 +7,8 @@ from migen.flow.network import * from migen.flow.transactions import * from migen.actorlib import structuring from migen.actorlib.sim import * -from migen.sim.generic import Simulator from migen.flow import perftools +from migen.sim.generic import run_simulation pack_factor = 5 base_layout = [("value", 32)] @@ -55,13 +55,11 @@ class TB(Module): self.submodules.comp = CompositeActor(self.g) self.submodules.reporter = perftools.DFGReporter(self.g) -def main(): +if __name__ == "__main__": tb = TB() - sim = Simulator(tb).run(1000) + run_simulation(tb, ncycles=1000) g_layout = nx.spectral_layout(tb.g) nx.draw(tb.g, g_layout) nx.draw_networkx_edge_labels(tb.g, g_layout, tb.reporter.get_edge_labels()) plt.show() - -main() diff --git a/examples/pytholite/basic.py b/examples/pytholite/basic.py index 425cfe7f..fd59ca61 100644 --- a/examples/pytholite/basic.py +++ b/examples/pytholite/basic.py @@ -2,7 +2,7 @@ from migen.flow.network import * from migen.flow.transactions import * from migen.actorlib.sim import * from migen.pytholite.compiler import Pytholite -from migen.sim.generic import Simulator +from migen.sim.generic import run_simulation from migen.fhdl import verilog layout = [("r", 32)] @@ -16,15 +16,13 @@ class SimNumberGen(SimActor): self.result = Source(layout) SimActor.__init__(self, number_gen(5)) -def run_sim(ng): +def run_ng_sim(ng): g = DataFlowGraph() d = Dumper(layout) g.add_connection(ng, d) c = CompositeActor(g) - sim = Simulator(c) - sim.run(20) - del sim + run_simulation(c, ncycles=20) def make_ng_pytholite(): ng_pytholite = Pytholite(number_gen, 5) @@ -35,11 +33,11 @@ def make_ng_pytholite(): def main(): print("Simulating native Python:") ng_native = SimNumberGen() - run_sim(ng_native) + run_ng_sim(ng_native) print("Simulating Pytholite:") ng_pytholite = make_ng_pytholite() - run_sim(ng_pytholite) + run_ng_sim(ng_pytholite) print("Converting Pytholite to Verilog:") ng_pytholite = make_ng_pytholite() diff --git a/examples/pytholite/uio.py b/examples/pytholite/uio.py index c627a8ef..94152628 100644 --- a/examples/pytholite/uio.py +++ b/examples/pytholite/uio.py @@ -6,7 +6,7 @@ from migen.bus.transactions import * from migen.genlib.ioo import UnifiedIOSimulation from migen.pytholite.transel import Register from migen.pytholite.compiler import Pytholite -from migen.sim.generic import Simulator +from migen.sim.generic import run_simulation from migen.fhdl.std import * from migen.fhdl import verilog @@ -39,10 +39,8 @@ class TestBench(Module): self.submodules.intercon = wishbone.InterconnectPointToPoint(ng.wb, self.slave.bus) self.submodules.ca = CompositeActor(g) -def run_sim(ng): - sim = Simulator(TestBench(ng)) - sim.run(50) - del sim +def run_ng_sim(ng): + run_simulation(TestBench(ng), ncycles=50) def add_interfaces(obj): obj.result = Source(layout) @@ -54,12 +52,12 @@ def main(): print("Simulating native Python:") ng_native = UnifiedIOSimulation(gen()) add_interfaces(ng_native) - run_sim(ng_native) + run_ng_sim(ng_native) print("Simulating Pytholite:") ng_pytholite = Pytholite(gen) add_interfaces(ng_pytholite) - run_sim(ng_pytholite) + run_ng_sim(ng_pytholite) print("Converting Pytholite to Verilog:") ng_pytholite = Pytholite(gen) diff --git a/examples/sim/abstract_transactions_lasmi.py b/examples/sim/abstract_transactions_lasmi.py index e53ca7ca..7dbcfe29 100644 --- a/examples/sim/abstract_transactions_lasmi.py +++ b/examples/sim/abstract_transactions_lasmi.py @@ -1,7 +1,7 @@ from migen.fhdl.std import * from migen.bus.transactions import * from migen.bus import lasmibus -from migen.sim.generic import Simulator +from migen.sim.generic import run_simulation def my_generator(n): bank = n % 4 @@ -30,16 +30,9 @@ class TB(Module): def __init__(self): self.submodules.controller = lasmibus.Target(MyModel(), aw=4, dw=32, nbanks=4, req_queue_size=4, read_latency=4, write_latency=1) - self.submodules.xbar = lasmibus.Crossbar([self.controller.bus], 4, 2) - self.initiators = [lasmibus.Initiator(my_generator(n), bus) for n, bus in enumerate(self.xbar.masters)] + self.submodules.xbar = lasmibus.Crossbar([self.controller.bus], 2) + self.initiators = [lasmibus.Initiator(my_generator(n), self.xbar.get_master()) for n in range(4)] self.submodules += self.initiators - def do_simulation(self, s): - s.interrupt = all(m.done for m in self.initiators) - -def main(): - tb = TB() - sim = Simulator(tb) - sim.run() - -main() +if __name__ == "__main__": + run_simulation(TB()) diff --git a/examples/sim/abstract_transactions_wb.py b/examples/sim/abstract_transactions_wb.py index 813772eb..431a6460 100644 --- a/examples/sim/abstract_transactions_wb.py +++ b/examples/sim/abstract_transactions_wb.py @@ -3,7 +3,7 @@ from random import Random from migen.fhdl.std import * from migen.bus.transactions import * from migen.bus import wishbone -from migen.sim.generic import Simulator +from migen.sim.generic import run_simulation # Our bus master. # Python generators let us program bus transactions in an elegant sequential style. @@ -53,16 +53,8 @@ class TB(Module): # Connect the master to the slave. self.submodules.intercon = wishbone.InterconnectPointToPoint(self.master.bus, self.slave.bus) - def do_simulation(self, s): - # Terminate the simulation when the initiator is done (i.e. our generator is exhausted). - s.interrupt = self.master.done - -def main(): - tb = TB() - sim = Simulator(tb) - sim.run() - -main() +if __name__ == "__main__": + run_simulation(TB()) # Output: # diff --git a/examples/sim/basic1.py b/examples/sim/basic1.py index 17d65a1c..dfe74a26 100644 --- a/examples/sim/basic1.py +++ b/examples/sim/basic1.py @@ -1,8 +1,5 @@ -# Copyright (C) 2012 Vermeer Manufacturing Co. -# License: GPLv3 with additional permissions (see README). - from migen.fhdl.std import * -from migen.sim.generic import Simulator +from migen.sim.generic import run_simulation # Our simple counter, which increments at every cycle # and prints its current value in simulation. @@ -15,21 +12,17 @@ class Counter(Module): self.sync += self.count.eq(self.count + 1) # This function will be called at every cycle. - def do_simulation(self, s): + def do_simulation(self, selfp): # Simply read the count signal and print it. # The output is: # Count: 0 # Count: 1 # Count: 2 # ... - print("Count: " + str(s.rd(self.count))) + print("Count: " + str(selfp.count)) -def main(): +if __name__ == "__main__": dut = Counter() - # We do not specify a top-level nor runner object, and use the defaults. - sim = Simulator(dut) - # Since we do not use sim.interrupt, limit the simulation + # Since we do not use StopSimulation, limit the simulation # to some number of cycles. - sim.run(20) - -main() + run_simulation(dut, ncycles=20) diff --git a/examples/sim/basic2.py b/examples/sim/basic2.py index a20623f3..acba62b6 100644 --- a/examples/sim/basic2.py +++ b/examples/sim/basic2.py @@ -1,10 +1,7 @@ -# Copyright (C) 2012 Vermeer Manufacturing Co. -# License: GPLv3 with additional permissions (see README). - from migen.fhdl.std import * -from migen.sim.generic import Simulator, TopLevel +from migen.sim.generic import run_simulation -# A slightly improved counter. +# A slightly more elaborate counter. # Has a clock enable (CE) signal, counts on more bits # and resets with a negative number. class Counter(Module): @@ -15,34 +12,25 @@ class Counter(Module): self.sync += If(self.ce, self.count.eq(self.count + 1)) - def do_simulation(self, s): + def do_simulation(self, selfp): # Only assert CE every second cycle. # => each counter value is held for two cycles. - if s.cycle_counter % 2: - s.wr(self.ce, 0) # This is how you write to a signal. + if selfp.simulator.cycle_counter % 2: + selfp.ce = 0 # This is how you write to a signal. else: - s.wr(self.ce, 1) - print("Cycle: " + str(s.cycle_counter) + " Count: " + \ - str(s.rd(self.count))) - # Set the "initialize" property on our simulation function. - # The simulator will call it during the reset cycle, - # with s.cycle_counter == -1. - do_simulation.initialize = True + selfp.ce = 1 + print("Cycle: " + str(selfp.simulator.cycle_counter) + " Count: " + \ + str(selfp.count)) - # Output is: - # Cycle: -1 Count: 0 - # Cycle: 0 Count: -5 - # Cycle: 1 Count: -5 - # Cycle: 2 Count: -4 - # Cycle: 3 Count: -4 - # Cycle: 4 Count: -3 - # ... +# Output is: +# Cycle: 0 Count: -5 +# Cycle: 1 Count: -5 +# Cycle: 2 Count: -4 +# Cycle: 3 Count: -4 +# Cycle: 4 Count: -3 +# ... -def main(): +if __name__ == "__main__": dut = Counter() - # Instantiating the generic top-level ourselves lets us - # specify a VCD output file. - sim = Simulator(dut, TopLevel("my.vcd")) - sim.run(20) - -main() + # Demonstrate VCD output + run_simulation(dut, vcd_name="my.vcd", ncycles=20) diff --git a/examples/sim/cordic_err.py b/examples/sim/cordic_err.py index 98b2650b..bd22f250 100644 --- a/examples/sim/cordic_err.py +++ b/examples/sim/cordic_err.py @@ -6,7 +6,7 @@ import matplotlib.pyplot as plt from migen.fhdl.std import * from migen.fhdl import verilog from migen.genlib.cordic import Cordic -from migen.sim.generic import Simulator +from migen.sim.generic import run_simulation class TestBench(Module): def __init__(self, n=None, xmax=.98, i=None, **kwargs): @@ -23,25 +23,17 @@ class TestBench(Module): self.ii = iter(self.i) self.o = [] - def do_simulation(self, s): - if s.rd(self.cordic.new_in): + def do_simulation(self, selfp): + if selfp.cordic.new_in: try: - xi, yi, zi = next(self.ii) + selfp.cordic.xi, selfp.cordic.yi, selfp.cordic.zi = next(self.ii) except StopIteration: - s.interrupt = True - return - s.wr(self.cordic.xi, xi) - s.wr(self.cordic.yi, yi) - s.wr(self.cordic.zi, zi) - if s.rd(self.cordic.new_out): - xo = s.rd(self.cordic.xo) - yo = s.rd(self.cordic.yo) - zo = s.rd(self.cordic.zo) - self.o.append((xo, yo, zo)) + raise StopSimulation + if selfp.cordic.new_out: + self.o.append((selfp.cordic.xo, selfp.cordic.yo, selfp.cordic.zo)) - def run_io(self): - with Simulator(self) as sim: - sim.run() + def run_io(self): + run_simulation(self) del self.i[-1], self.o[0] if self.i[0] != (0, 0, 0): assert self.o[0] != (0, 0, 0) diff --git a/examples/sim/dataflow.py b/examples/sim/dataflow.py index ca142344..5a325440 100644 --- a/examples/sim/dataflow.py +++ b/examples/sim/dataflow.py @@ -3,7 +3,7 @@ from migen.flow.actor import * from migen.flow.transactions import * from migen.flow.network import * from migen.actorlib.sim import * -from migen.sim.generic import Simulator +from migen.sim.generic import run_simulation def source_gen(): for i in range(10): @@ -34,7 +34,5 @@ class TB(Module): g.add_connection(self.source, self.sink) self.submodules.comp = CompositeActor(g) - def do_simulation(self, s): - s.interrupt = self.source.token_exchanger.done - -Simulator(TB()).run() +if __name__ == "__main__": + run_simulation(TB()) diff --git a/examples/sim/fir.py b/examples/sim/fir.py index 0f939403..0a4a1919 100644 --- a/examples/sim/fir.py +++ b/examples/sim/fir.py @@ -1,6 +1,3 @@ -# Copyright (C) 2012 Vermeer Manufacturing Co. -# License: GPLv3 with additional permissions (see README). - from math import cos, pi from scipy import signal import matplotlib.pyplot as plt @@ -8,7 +5,7 @@ import matplotlib.pyplot as plt from migen.fhdl.std import * from migen.fhdl import verilog from migen.genlib.misc import optree -from migen.sim.generic import Simulator +from migen.sim.generic import run_simulation # A synthesizable FIR filter. class FIR(Module): @@ -41,14 +38,14 @@ class TB(Module): self.inputs = [] self.outputs = [] - def do_simulation(self, s): + def do_simulation(self, selfp): f = 2**(self.fir.wsize - 1) - v = 0.1*cos(2*pi*self.frequency*s.cycle_counter) - s.wr(self.fir.i, int(f*v)) + v = 0.1*cos(2*pi*self.frequency*selfp.simulator.cycle_counter) + selfp.fir.i = int(f*v) self.inputs.append(v) - self.outputs.append(s.rd(self.fir.o)/f) + self.outputs.append(selfp.fir.o/f) -def main(): +if __name__ == "__main__": # Compute filter coefficients with SciPy. coef = signal.remez(30, [0, 0.1, 0.2, 0.4, 0.45, 0.5], [0, 1, 0]) @@ -58,9 +55,7 @@ def main(): out_signals = [] for frequency in [0.05, 0.1, 0.25]: tb = TB(coef, frequency) - sim = Simulator(tb) - sim.run(200) - del sim + run_simulation(tb, ncycles=200) in_signals += tb.inputs out_signals += tb.outputs @@ -72,5 +67,3 @@ def main(): # Print the Verilog source for the filter. fir = FIR(coef) print(verilog.convert(fir, ios={fir.i, fir.o})) - -main() diff --git a/examples/sim/memory.py b/examples/sim/memory.py index 35f926db..2e55052f 100644 --- a/examples/sim/memory.py +++ b/examples/sim/memory.py @@ -1,8 +1,5 @@ -# Copyright (C) 2012 Vermeer Manufacturing Co. -# License: GPLv3 with additional permissions (see README). - from migen.fhdl.std import * -from migen.sim.generic import Simulator +from migen.sim.generic import run_simulation class Mem(Module): def __init__(self): @@ -10,23 +7,19 @@ class Mem(Module): # from 0 to 19. self.specials.mem = Memory(16, 2**12, init=list(range(20))) - def do_simulation(self, s): + def do_simulation(self, selfp): # Read the memory. Use the cycle counter as address. - value = s.rd(self.mem, s.cycle_counter) + value = selfp.mem[selfp.simulator.cycle_counter] # Print the result. Output is: # 0 # 1 # 2 # ... print(value) - # Demonstrate how to interrupt the simulator. + # Raising StopSimulation disables the current (and here, only one) + # simulation function. Simulator stops when all functions are disabled. if value == 10: - s.interrupt = True - -def main(): - dut = Mem() - sim = Simulator(dut) - # No need for a cycle limit here, we use sim.interrupt instead. - sim.run() + raise StopSimulation -main() +if __name__ == "__main__": + run_simulation(Mem()) diff --git a/migen/actorlib/sim.py b/migen/actorlib/sim.py index ac80a2f0..37dc1c31 100644 --- a/migen/actorlib/sim.py +++ b/migen/actorlib/sim.py @@ -1,6 +1,25 @@ 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. @@ -14,35 +33,34 @@ class TokenExchanger(Module): self.actor = actor self.active = set() self.busy = True - self.done = False - def _process_transactions(self, s): + def _process_transactions(self, selfp): completed = set() for token in self.active: ep = getattr(self.actor, token.endpoint) if isinstance(ep, Sink): - if s.rd(ep.ack) and s.rd(ep.stb): - token.value = s.multiread(ep.payload) + if selfp.simulator.rd(ep.ack) and selfp.simulator.rd(ep.stb): + token.value = _sim_multiread(selfp.simulator, ep.payload) completed.add(token) - s.wr(ep.ack, 0) + selfp.simulator.wr(ep.ack, 0) elif isinstance(ep, Source): - if s.rd(ep.ack) and s.rd(ep.stb): + if selfp.simulator.rd(ep.ack) and selfp.simulator.rd(ep.stb): completed.add(token) - s.wr(ep.stb, 0) + selfp.simulator.wr(ep.stb, 0) else: raise TypeError self.active -= completed if not self.active: self.busy = True - def _update_control_signals(self, s): + def _update_control_signals(self, selfp): for token in self.active: ep = getattr(self.actor, token.endpoint) if isinstance(ep, Sink): - s.wr(ep.ack, 1) + selfp.simulator.wr(ep.ack, 1) elif isinstance(ep, Source): - s.multiwrite(ep.payload, token.value) - s.wr(ep.stb, 1) + _sim_multiwrite(selfp.simulator, ep.payload, token.value) + selfp.simulator.wr(ep.stb, 1) else: raise TypeError @@ -50,9 +68,8 @@ class TokenExchanger(Module): try: transactions = next(self.generator) except StopIteration: - self.done = True self.busy = False - transactions = None + raise StopSimulation if isinstance(transactions, Token): self.active = {transactions} elif isinstance(transactions, (tuple, list, set)): @@ -64,23 +81,20 @@ class TokenExchanger(Module): if self.active and all(transaction.idle_wait for transaction in self.active): self.busy = False - def do_simulation(self, s): - if not self.done: - if self.active: - self._process_transactions(s) - if not self.active: - self._next_transactions() - self._update_control_signals(s) - - do_simulation.initialize = True + def do_simulation(self, selfp): + if self.active: + self._process_transactions(selfp) + if not self.active: + self._next_transactions() + self._update_control_signals(selfp) class SimActor(Module): def __init__(self, generator): self.busy = Signal() self.submodules.token_exchanger = TokenExchanger(generator, self) - def do_simulation(self, s): - s.wr(self.busy, self.token_exchanger.busy) + def do_simulation(self, selfp): + selfp.busy = self.token_exchanger.busy def _dumper_gen(prefix): while True: diff --git a/migen/bus/lasmibus.py b/migen/bus/lasmibus.py index 906fd2ca..d907581b 100644 --- a/migen/bus/lasmibus.py +++ b/migen/bus/lasmibus.py @@ -190,42 +190,40 @@ class Initiator(Module): self.transaction_start = 0 self.transaction = None self.transaction_end = None - self.done = False - def do_simulation(self, s): - s.wr(self.bus.dat_w, 0) - s.wr(self.bus.dat_we, 0) - if not self.done: - if self.transaction is not None: - if s.rd(self.bus.req_ack): - s.wr(self.bus.stb, 0) - if s.rd(self.bus.dat_ack): - if isinstance(self.transaction, TRead): - self.transaction_end = s.cycle_counter + self.bus.read_latency - else: - self.transaction_end = s.cycle_counter + self.bus.write_latency - 1 + def do_simulation(self, selfp): + selfp.bus.dat_w = 0 + selfp.bus.dat_we = 0 + + if self.transaction is not None: + if selfp.bus.req_ack: + selfp.bus.stb = 0 + if selfp.bus.dat_ack: + if isinstance(self.transaction, TRead): + self.transaction_end = selfp.simulator.cycle_counter + self.bus.read_latency + else: + self.transaction_end = selfp.simulator.cycle_counter + self.bus.write_latency - 1 - if self.transaction is None or s.cycle_counter == self.transaction_end: - if self.transaction is not None: - self.transaction.latency = s.cycle_counter - self.transaction_start - 1 - if isinstance(self.transaction, TRead): - self.transaction.data = s.rd(self.bus.dat_r) - else: - s.wr(self.bus.dat_w, self.transaction.data) - s.wr(self.bus.dat_we, self.transaction.sel) - try: - self.transaction = next(self.generator) - except StopIteration: - self.done = True - self.transaction = None - if self.transaction is not None: - self.transaction_start = s.cycle_counter - s.wr(self.bus.stb, 1) - s.wr(self.bus.adr, self.transaction.address) - if isinstance(self.transaction, TRead): - s.wr(self.bus.we, 0) - else: - s.wr(self.bus.we, 1) + if self.transaction is None or selfp.simulator.cycle_counter == self.transaction_end: + if self.transaction is not None: + self.transaction.latency = selfp.simulator.cycle_counter - self.transaction_start - 1 + if isinstance(self.transaction, TRead): + self.transaction.data = selfp.bus.dat_r + else: + selfp.bus.dat_w = self.transaction.data + selfp.bus.dat_we = self.transaction.sel + try: + self.transaction = next(self.generator) + except StopIteration: + raise StopSimulation + if self.transaction is not None: + self.transaction_start = selfp.simulator.cycle_counter + selfp.bus.stb = 1 + selfp.bus.adr = self.transaction.address + if isinstance(self.transaction, TRead): + selfp.bus.we = 0 + else: + selfp.bus.we = 1 class TargetModel: def __init__(self): @@ -254,14 +252,14 @@ class _ReqFIFO(Module): self.bank = bank self.contents = [] - def do_simulation(self, s): + def do_simulation(self, selfp): if len(self.contents) < self.req_queue_size: - if s.rd(self.bank.stb): - self.contents.append((s.rd(self.bank.we), s.rd(self.bank.adr))) - s.wr(self.bank.req_ack, 1) + if selfp.bank.stb: + self.contents.append((selfp.bank.we, selfp.bank.adr)) + selfp.bank.req_ack = 1 else: - s.wr(self.bank.req_ack, 0) - s.wr(self.bank.lock, bool(self.contents)) + selfp.bank.req_ack = 0 + selfp.bank.lock = bool(self.contents) class Target(Module): def __init__(self, model, *ifargs, **ifkwargs): @@ -273,7 +271,7 @@ class Target(Module): self.rd_pipeline = [None]*self.bus.read_latency self.wr_pipeline = [None]*(self.bus.write_latency + 1) - def do_simulation(self, s): + def do_simulation(self, selfp): # determine banks with pending requests pending_banks = set(nb for nb, rf in enumerate(self.req_fifos) if rf.contents) @@ -281,12 +279,12 @@ class Target(Module): selected_bank_n = self.model.select_bank(pending_banks) selected_transaction = None for nb in range(self.bus.nbanks): - bank = getattr(self.bus, "bank"+str(nb)) + bank = getattr(selfp.bus, "bank"+str(nb)) if nb == selected_bank_n: - s.wr(bank.dat_ack, 1) + bank.dat_ack = 1 selected_transaction = self.req_fifos[nb].contents.pop(0) else: - s.wr(bank.dat_ack, 0) + bank.dat_ack = 0 rd_transaction = None wr_transaction = None @@ -303,7 +301,7 @@ class Target(Module): done_rd_transaction = self.rd_pipeline.pop(0) done_wr_transaction = self.wr_pipeline.pop(0) if done_rd_transaction is not None: - s.wr(self.bus.dat_r, self.model.read(done_rd_transaction[0], done_rd_transaction[1])) + selfp.bus.dat_r = self.model.read(done_rd_transaction[0], done_rd_transaction[1]) if done_wr_transaction is not None: self.model.write(done_wr_transaction[0], done_wr_transaction[1], - s.rd(self.bus.dat_w), s.rd(self.bus.dat_we)) + selfp.bus.dat_w, selfp.bus.dat_we) diff --git a/migen/bus/memory.py b/migen/bus/memory.py index 77aeaedb..bd0b93e9 100644 --- a/migen/bus/memory.py +++ b/migen/bus/memory.py @@ -19,18 +19,16 @@ class Initiator(Module): def __init__(self, generator, mem): self.generator = generator self.mem = mem - self.done = False - def do_simulation(self, s): - if not self.done: - try: - transaction = next(self.generator) - except StopIteration: - self.done = True - transaction = None - if isinstance(transaction, TRead): - transaction.data = s.rd(self.mem, transaction.address) - elif isinstance(transaction, TWrite): - d = s.rd(self.mem, transaction.address) - d_mask = _byte_mask(d, transaction.data, transaction.sel) - s.wr(s.mem, d_mask, transaction.address) + def do_simulation(self, selfp): + try: + transaction = next(self.generator) + except StopIteration: + transaction = None + raise StopSimulation + if isinstance(transaction, TRead): + transaction.data = selfp.mem[transaction.address] + elif isinstance(transaction, TWrite): + d = selfp.mem[transaction.address] + d_mask = _byte_mask(d, transaction.data, transaction.sel) + selfp.mem[transaction.address] = d_mask diff --git a/migen/bus/wishbone.py b/migen/bus/wishbone.py index 6dd2a066..e2f95c40 100644 --- a/migen/bus/wishbone.py +++ b/migen/bus/wishbone.py @@ -4,7 +4,6 @@ from migen.genlib.record import * from migen.genlib.misc import optree, chooser from migen.genlib.fsm import FSM, NextState from migen.bus.transactions import * -from migen.sim.generic import Proxy _layout = [ ("adr", 30, DIR_M_TO_S), @@ -202,16 +201,16 @@ class Tap(Module): self.bus = bus self.handler = handler - def do_simulation(self, s): - if s.rd(self.bus.ack): - assert(s.rd(self.bus.cyc) and s.rd(self.bus.stb)) - if s.rd(self.bus.we): - transaction = TWrite(s.rd(self.bus.adr), - s.rd(self.bus.dat_w), - s.rd(self.bus.sel)) + def do_simulation(self, selfp): + if selfp.bus.ack: + assert(selfp.bus.cyc and selfp.bus.stb) + if selfp.bus.we: + transaction = TWrite(selfp.bus.adr, + selfp.bus.dat_w, + selfp.bus.sel) else: - transaction = TRead(s.rd(self.bus.adr), - s.rd(self.bus.dat_r)) + transaction = TRead(selfp.bus.adr, + selfp.bus.dat_r) self.handler(transaction) class Initiator(Module): @@ -222,34 +221,33 @@ class Initiator(Module): self.bus = bus self.transaction_start = 0 self.transaction = None - self.done = False - def do_simulation(self, s): - if not self.done: - if self.transaction is None or s.rd(self.bus.ack): - if self.transaction is not None: - self.transaction.latency = s.cycle_counter - self.transaction_start - 1 - if isinstance(self.transaction, TRead): - self.transaction.data = s.rd(self.bus.dat_r) - try: - self.transaction = next(self.generator) - except StopIteration: - self.done = True - self.transaction = None - if self.transaction is not None: - self.transaction_start = s.cycle_counter - s.wr(self.bus.cyc, 1) - s.wr(self.bus.stb, 1) - s.wr(self.bus.adr, self.transaction.address) - if isinstance(self.transaction, TWrite): - s.wr(self.bus.we, 1) - s.wr(self.bus.sel, self.transaction.sel) - s.wr(self.bus.dat_w, self.transaction.data) - else: - s.wr(self.bus.we, 0) + def do_simulation(self, selfp): + if self.transaction is None or selfp.bus.ack: + if self.transaction is not None: + self.transaction.latency = selfp.simulator.cycle_counter - self.transaction_start - 1 + if isinstance(self.transaction, TRead): + self.transaction.data = selfp.bus.dat_r + try: + self.transaction = next(self.generator) + except StopIteration: + selfp.bus.cyc = 0 + selfp.bus.stb = 0 + raise StopSimulation + if self.transaction is not None: + self.transaction_start = selfp.simulator.cycle_counter + selfp.bus.cyc = 1 + selfp.bus.stb = 1 + selfp.bus.adr = self.transaction.address + if isinstance(self.transaction, TWrite): + selfp.bus.we = 1 + selfp.bus.sel = self.transaction.sel + selfp.bus.dat_w = self.transaction.data else: - s.wr(self.bus.cyc, 0) - s.wr(self.bus.stb, 0) + selfp.bus.we = 0 + else: + selfp.bus.cyc = 0 + selfp.bus.stb = 0 class TargetModel: def read(self, address): @@ -268,8 +266,8 @@ class Target(Module): self.bus = bus self.model = model - def do_simulation(self, s): - bus = Proxy(s, self.bus) + def do_simulation(self, selfp): + bus = selfp.bus if not bus.ack: if self.model.can_ack(bus) and bus.cyc and bus.stb: if bus.we: diff --git a/migen/fhdl/module.py b/migen/fhdl/module.py index 30e065b6..eb236a94 100644 --- a/migen/fhdl/module.py +++ b/migen/fhdl/module.py @@ -1,11 +1,11 @@ import collections from itertools import combinations +from migen.util.misc import flat_iteration from migen.fhdl.structure import * from migen.fhdl.structure import _Fragment -from migen.fhdl.specials import Special from migen.fhdl.tools import rename_clock_domain -from migen.util.misc import flat_iteration +from migen.sim.upper import GenSim, ProxySim class FinalizeError(Exception): pass @@ -106,10 +106,21 @@ class Module: self.finalized = False return self.finalized elif name == "_fragment": + simf = None try: - sim = [self.do_simulation] + simf = self.do_simulation except AttributeError: - sim = [] + try: + simg = self.gen_simulation + except AttributeError: + pass + else: + gs = GenSim(simg) + simf = gs.do_simulation + if simf is not None: + ps = ProxySim(self, simf) + simf = ps.do_simulation + sim = [] if simf is None else [simf] self._fragment = _Fragment(sim=sim) return self._fragment elif name == "_submodules": diff --git a/migen/fhdl/structure.py b/migen/fhdl/structure.py index 9b31e696..c2268428 100644 --- a/migen/fhdl/structure.py +++ b/migen/fhdl/structure.py @@ -528,6 +528,9 @@ class _ClockDomainList(list): (SPECIAL_INPUT, SPECIAL_OUTPUT, SPECIAL_INOUT) = range(3) +class StopSimulation(Exception): + pass + class _Fragment: def __init__(self, comb=None, sync=None, specials=None, clock_domains=None, sim=None): if comb is None: comb = [] diff --git a/migen/flow/hooks.py b/migen/flow/hooks.py index 2c505558..190664e7 100644 --- a/migen/flow/hooks.py +++ b/migen/flow/hooks.py @@ -16,9 +16,9 @@ class EndpointSimHook(Module): def on_inactive(self): pass - def do_simulation(self, s): - if s.rd(self.endpoint.stb): - if s.rd(self.endpoint.ack): + def do_simulation(self, selfp): + if selfp.endpoint.stb: + if selfp.endpoint.ack: self.on_ack() else: self.on_nack() diff --git a/migen/sim/generic.py b/migen/sim/generic.py index 741ccfcf..02017e38 100644 --- a/migen/sim/generic.py +++ b/migen/sim/generic.py @@ -73,9 +73,14 @@ end return r def _call_sim(fragment, simulator): + del_list = [] for s in fragment.sim: - if simulator.cycle_counter >= 0 or (hasattr(s, "initialize") and s.initialize): + try: s(simulator) + except StopSimulation: + del_list.append(s) + for s in del_list: + fragment.sim.remove(s) class Simulator: def __init__(self, fragment, top_level=None, sim_runner=None, sockaddr="simsocket", **vopts): @@ -99,19 +104,16 @@ class Simulator: **vopts) self.cycle_counter = -1 - self.interrupt = False self.sim_runner = sim_runner self.sim_runner.start(c_top, c_fragment) self.ipc.accept() reply = self.ipc.recv() assert(isinstance(reply, MessageTick)) - _call_sim(self.fragment, self) - def run(self, ncycles=-1): - self.interrupt = False + def run(self, ncycles=None): counter = 0 - while not self.interrupt and (ncycles < 0 or counter < ncycles): + while self.fragment.sim and (ncycles is None or counter < ncycles): self.cycle_counter += 1 counter += 1 self.ipc.send(MessageGo()) @@ -150,34 +152,6 @@ class Simulator: assert(value >= 0 and value < 2**nbits) self.ipc.send(MessageWrite(name, Int32(index), value)) - def multiread(self, obj): - if isinstance(obj, Signal): - return self.rd(obj) - elif isinstance(obj, list): - r = [] - for item in obj: - rd = self.multiread(item) - if isinstance(item, Signal) or rd: - r.append(rd) - return r - elif hasattr(obj, "__dict__"): - r = {} - for k, v in obj.__dict__.items(): - rd = self.multiread(v) - if isinstance(v, Signal) or rd: - r[k] = rd - return r - - def multiwrite(self, obj, value): - if isinstance(obj, Signal): - self.wr(obj, value) - elif isinstance(obj, list): - for target, source in zip(obj, value): - self.multiwrite(target, source) - else: - for k, v in value.items(): - self.multiwrite(getattr(obj, k), v) - def __del__(self): if hasattr(self, "ipc"): warnings.warn("call Simulator.close() to clean up " @@ -193,26 +167,9 @@ class Simulator: def __enter__(self): return self - def __exit__(self, type, value, traceback): + def __exit__(self, type, value, traceback): self.close() -# Contrary to multiread/multiwrite, Proxy fetches the necessary signals only and -# immediately forwards writes into the simulation. -class Proxy: - def __init__(self, sim, obj): - self.__dict__["_sim"] = sim - self.__dict__["_obj"] = obj - - def __getattr__(self, name): - item = getattr(self._obj, name) - if isinstance(item, Signal): - return self._sim.rd(item) - elif isinstance(item, list): - return [Proxy(self._sim, si) for si in item] - else: - return Proxy(self._sim, item) - - def __setattr__(self, name, value): - item = getattr(self._obj, name) - assert(isinstance(item, Signal)) - self._sim.wr(item, value) +def run_simulation(fragment, ncycles=None, vcd_name=None, keep_files=False): + with Simulator(fragment, TopLevel(vcd_name), icarus.Runner(keep_files=keep_files)) as s: + s.run(ncycles) diff --git a/migen/sim/upper.py b/migen/sim/upper.py new file mode 100644 index 00000000..34b3e141 --- /dev/null +++ b/migen/sim/upper.py @@ -0,0 +1,99 @@ +from migen.fhdl.structure import Signal, StopSimulation +from migen.fhdl.specials import Memory + +class MemoryProxy: + def __init__(self, simulator, obj): + self.simulator = simulator + self._simproxy_obj = obj + + def __getitem__(self, key): + if isinstance(key, int): + return self.simulator.rd(self._simproxy_obj, key) + else: + start, stop, step = key.indices(self._simproxy_obj.depth) + return [self.simulator.rd(self._simproxy_obj, i) for i in range(start, stop, step)] + + def __setitem__(self, key, value): + if isinstance(key, int): + self.simulator.wr(self._simproxy_obj, key, value) + else: + start, stop, step = key.indices(self.__obj.depth) + if len(value) != (stop - start)//step: + raise ValueError + for i, v in zip(range(start, stop, step), value): + self.simulator.wr(self._simproxy_obj, i, v) + +class Proxy: + def __init__(self, simulator, obj): + object.__setattr__(self, "simulator", simulator) + object.__setattr__(self, "_simproxy_obj", obj) + + def __process_get(self, item): + if isinstance(item, Signal): + return self.simulator.rd(item) + elif isinstance(item, Memory): + return MemoryProxy(self.simulator, item) + else: + return Proxy(self.simulator, item) + + def __getattr__(self, name): + return self.__process_get(getattr(self._simproxy_obj, name)) + + def __setattr__(self, name, value): + item = getattr(self._simproxy_obj, name) + assert(isinstance(item, Signal)) + self.simulator.wr(item, value) + + def __getitem__(self, key): + return self.__process_get(self._simproxy_obj[key]) + + def __setitem__(self, key, value): + item = self._simproxy_obj[key] + assert(isinstance(item, Signal)) + self.simulator.wr(item, value) + +class GenSim: + def __init__(self, simg): + self.simg = simg + self.gens = dict() + self.resume_cycle = 0 + + def do_simulation(self, s): + if isinstance(s, Proxy): + simulator = s.simulator + else: + simulator = s + + if simulator.cycle_counter >= self.resume_cycle: + try: + gen = self.gens[simulator] + except KeyError: + gen = self.simg(s) + self.gens[simulator] = gen + try: + n = next(gen) + except StopIteration: + del self.gens[simulator] + raise StopSimulation + else: + if n is None: + n = 1 + self.resume_cycle = simulator.cycle_counter + n + +class ProxySim: + def __init__(self, target, simf): + self.target = target + self.simf = simf + self.proxies = dict() + + def do_simulation(self, simulator): + try: + proxy = self.proxies[simulator] + except KeyError: + proxy = Proxy(simulator, self.target) + self.proxies[simulator] = proxy + try: + self.simf(proxy) + except StopSimulation: + del self.proxies[simulator] + raise diff --git a/migen/test/support.py b/migen/test/support.py index e99cfb2c..394cc424 100644 --- a/migen/test/support.py +++ b/migen/test/support.py @@ -1,13 +1,12 @@ -import unittest from migen.fhdl.std import * -from migen.sim.generic import Simulator +from migen.sim.generic import run_simulation from migen.fhdl import verilog class SimBench(Module): callback = None - def do_simulation(self, s): + def do_simulation(self, selfp): if self.callback is not None: - return self.callback(self, s) + return self.callback(self, selfp) class SimCase: TestBench = SimBench @@ -18,7 +17,6 @@ class SimCase: def test_to_verilog(self): verilog.convert(self.tb) - def run_with(self, cb, cycles=-1): + def run_with(self, cb, ncycles=-1): self.tb.callback = cb - with Simulator(self.tb) as s: - s.run(cycles) + run_simulation(self.tb, ncycles=ncycles) diff --git a/migen/test/test_coding.py b/migen/test/test_coding.py index 2d0f53d9..1a61158a 100644 --- a/migen/test/test_coding.py +++ b/migen/test/test_coding.py @@ -17,16 +17,13 @@ class EncCase(SimCase, unittest.TestCase): def test_run_sequence(self): seq = list(range(1<<8)) - cur = None - def cb(tb, s): + def cb(tb, tbp): if seq: - s.wr(tb.dut.i, seq.pop(0)) - i = s.rd(tb.dut.i) - if s.rd(tb.dut.n): - self.assertNotIn(i, [1< 0: self.assertEqual(i & 1<<(o - 1), 0) self.assertGreaterEqual(i, 1< q, - lambda p, q: p >= q, - lambda p, q: p < q, - lambda p, q: p <= q, - lambda p, q: p == q, - lambda p, q: p != q, - ] + lambda p, q: p > q, + lambda p, q: p >= q, + lambda p, q: p < q, + lambda p, q: p <= q, + lambda p, q: p == q, + lambda p, q: p != q, + ] self.vals = [] for asign in 1, -1: for bsign in 1, -1: @@ -29,16 +29,16 @@ class SignedCase(SimCase, unittest.TestCase): values = range(-4, 4) agen = iter(values) bgen = iter(values) - def cb(tb, s): + def cb(tb, tbp): try: - s.wr(self.tb.a, next(agen)) - s.wr(self.tb.b, next(bgen)) + tbp.a = next(agen) + tbp.b = next(bgen) except StopIteration: - s.interrupt = True - a = s.rd(self.tb.a) - b = s.rd(self.tb.b) + raise StopSimulation + a = tbp.a + b = tbp.b for asign, bsign, f, r, op in self.tb.vals: - r, r0 = s.rd(r), f(asign*a, bsign*b) + r, r0 = tbp.simulator.rd(r), f(asign*a, bsign*b) self.assertEqual(r, int(r0), "got {}, want {}*{} {} {}*{} = {}".format( r, asign, a, op, bsign, b, r0)) diff --git a/migen/test/test_sort.py b/migen/test/test_sort.py index 99785ab0..163be8c2 100644 --- a/migen/test/test_sort.py +++ b/migen/test/test_sort.py @@ -19,10 +19,8 @@ class BitonicCase(SimCase, unittest.TestCase): self.assertEqual(flen(self.tb.dut.o[i]), 4) def test_sort(self): - def cb(tb, s): + def cb(tb, tbp): for i in tb.dut.i: - s.wr(i, randrange(1<