New simulation API

This commit is contained in:
Sebastien Bourdeauducq 2014-01-26 22:19:43 +01:00
parent 8f69d9b669
commit 63c1d7e4b7
29 changed files with 406 additions and 463 deletions

View file

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

View file

@ -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()
run_simulation(TBWishboneWriter())
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)
if __name__ == "__main__":
test_wb_reader()
test_wb_writer()

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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:
# <TWrite adr:0x0 dat:0x0>

View file

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

View file

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

View file

@ -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()
run_simulation(self)
del self.i[-1], self.o[0]
if self.i[0] != (0, 0, 0):
assert self.o[0] != (0, 0, 0)

View file

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

View file

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

View file

@ -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
raise StopSimulation
def main():
dut = Mem()
sim = Simulator(dut)
# No need for a cycle limit here, we use sim.interrupt instead.
sim.run()
main()
if __name__ == "__main__":
run_simulation(Mem())

View file

@ -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:

View file

@ -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:
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 selfp.simulator.cycle_counter == self.transaction_end:
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
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)
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)

View file

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

View file

@ -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:

View file

@ -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":

View file

@ -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 = []

View file

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

View file

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

99
migen/sim/upper.py Normal file
View file

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

View file

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

View file

@ -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<<i for i in range(8)])
tbp.dut.i = seq.pop(0)
if tbp.dut.n:
self.assertNotIn(tbp.dut.i, [1<<i for i in range(8)])
else:
o = s.rd(tb.dut.o)
self.assertEqual(i, 1<<o)
self.assertEqual(tbp.dut.i, 1<<tbp.dut.o)
self.run_with(cb, 256)
class PrioEncCase(SimCase, unittest.TestCase):
@ -41,15 +38,14 @@ class PrioEncCase(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):
tbp.dut.i = seq.pop(0)
i = tbp.dut.i
if tbp.dut.n:
self.assertEqual(i, 0)
else:
o = s.rd(tb.dut.o)
o = tbp.dut.o
if o > 0:
self.assertEqual(i & 1<<(o - 1), 0)
self.assertGreaterEqual(i, 1<<o)
@ -67,15 +63,14 @@ class DecCase(SimCase, unittest.TestCase):
def test_run_sequence(self):
seq = list(range(8*2))
cur = None
def cb(tb, s):
def cb(tb, tbp):
if seq:
i = seq.pop()
s.wr(tb.dut.i, i//2)
s.wr(tb.dut.n, i%2)
i = s.rd(tb.dut.i)
o = s.rd(tb.dut.o)
if s.rd(tb.dut.n):
tbp.dut.i = i//2
tbp.dut.n = i%2
i = tbp.dut.i
o = tbp.dut.o
if tbp.dut.n:
self.assertEqual(o, 0)
else:
self.assertEqual(o, 1<<i)

View file

@ -7,7 +7,6 @@ from migen.genlib.cordic import *
from migen.test.support import SimCase, SimBench
class CordicCase(SimCase, unittest.TestCase):
class TestBench(SimBench):
def __init__(self, **kwargs):
@ -23,22 +22,21 @@ class CordicCase(SimCase, unittest.TestCase):
zm = self.tb.dut.zmax
pipe = {}
genn = [gen() for i in range(n)]
def cb(tb, s):
if s.rd(tb.dut.new_in):
def cb(tb, tbp):
if tbp.dut.new_in:
if genn:
xi, yi, zi = genn.pop(0)
else:
s.interrupt = True
return
raise StopSimulation
xi = floor(xi*c/g)
yi = floor(yi*c/g)
zi = floor(zi*c/zm)
s.wr(tb.dut.xi, xi)
s.wr(tb.dut.yi, yi)
s.wr(tb.dut.zi, zi)
pipe[s.cycle_counter] = xi, yi, zi
if s.rd(tb.dut.new_out):
t = s.cycle_counter - tb.dut.latency - 1
tbp.dut.xi = xi
tbp.dut.yi = yi
tbp.dut.zi = zi
pipe[tbp.simulator.cycle_counter] = xi, yi, zi
if tbp.dut.new_out:
t = tbp.simulator.cycle_counter - tb.dut.latency - 1
if t < 1:
return
xi, yi, zi = pipe.pop(t)

View file

@ -1,7 +1,7 @@
import unittest
from migen.fhdl.std import *
from migen.genlib.fifo import SyncFIFO, AsyncFIFO
from migen.genlib.fifo import SyncFIFO
from migen.test.support import SimCase, SimBench
@ -23,13 +23,13 @@ class SyncFIFOCase(SimCase, unittest.TestCase):
def test_run_sequence(self):
seq = list(range(20))
def cb(tb, s):
def cb(tb, tbp):
# fire re and we at "random"
s.wr(tb.dut.we, s.cycle_counter % 2 == 0)
s.wr(tb.dut.re, s.cycle_counter % 3 == 0)
tbp.dut.we = tbp.simulator.cycle_counter % 2 == 0
tbp.dut.re = tbp.simulator.cycle_counter % 3 == 0
# the output if valid must be correct
if s.rd(tb.dut.readable) and s.rd(tb.dut.re):
if tbp.dut.readable and tbp.dut.re:
i = seq.pop(0)
self.assertEqual(s.rd(tb.dut.dout.a), i)
self.assertEqual(s.rd(tb.dut.dout.b), i*2)
self.assertEqual(tbp.dut.dout.a, i)
self.assertEqual(tbp.dut.dout.b, i*2)
self.run_with(cb, 20)

View file

@ -9,13 +9,13 @@ class SignedCase(SimCase, unittest.TestCase):
self.a = Signal((3, True))
self.b = Signal((4, True))
comps = [
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,
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))

View file

@ -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<<flen(i)))
i = [s.rd(i) for i in tb.dut.i]
o = [s.rd(o) for o in tb.dut.o]
self.assertEqual(sorted(i), o)
tbp.simulator.wr(i, randrange(1<<flen(i)))
self.assertEqual(sorted(list(tbp.dut.i)), list(tbp.dut.o))
self.run_with(cb, 20)