back.pysim: redesign the simulator.
The redesign introduces no fundamental incompatibilities, but it does involve minor breaking changes: * The simulator commands were moved from hdl.ast to back.pysim (instead of only being reexported from back.pysim). * back.pysim.DeadlineError was removed. Summary of changes: * The new simulator compiles HDL to Python code and is >6x faster. (The old one compiled HDL to lots of Python lambdas.) * The new simulator is a straightforward, rigorous implementation of the Synchronous Reactive Programming paradigm, instead of a pile of ad-hoc code with no particular design driving it. * The new simulator never raises DeadlineError, and there is no limit on the amount of delta cycles. * The new simulator robustly handles multiclock designs. * The new simulator can be reset, such that the compiled design can be reused, which can save significant runtime with large designs. * Generators can no longer be added as processes, since that would break reset(); only generator functions may be. If necessary, they may be added by wrapping them into a generator function; a deprecated fallback does just that. This workaround will raise an exception if the simulator is reset and restarted. * The new simulator does not depend on Python extensions. (The old one required bitarray, which did not provide wheels.) Fixes #28. Fixes #34. Fixes #160. Fixes #161. Fixes #215. Fixes #242. Fixes #262.
This commit is contained in:
parent
f8428ff505
commit
7df70059d1
|
@ -19,10 +19,7 @@ ctr = Counter(width=16)
|
||||||
|
|
||||||
print(verilog.convert(ctr, ports=[ctr.o, ctr.en]))
|
print(verilog.convert(ctr, ports=[ctr.o, ctr.en]))
|
||||||
|
|
||||||
with pysim.Simulator(ctr,
|
sim = pysim.Simulator(ctr)
|
||||||
vcd_file=open("ctrl.vcd", "w"),
|
|
||||||
gtkw_file=open("ctrl.gtkw", "w"),
|
|
||||||
traces=[ctr.en, ctr.v, ctr.o]) as sim:
|
|
||||||
sim.add_clock(1e-6)
|
sim.add_clock(1e-6)
|
||||||
def ce_proc():
|
def ce_proc():
|
||||||
yield; yield; yield
|
yield; yield; yield
|
||||||
|
@ -31,5 +28,6 @@ with pysim.Simulator(ctr,
|
||||||
yield ctr.en.eq(0)
|
yield ctr.en.eq(0)
|
||||||
yield; yield; yield
|
yield; yield; yield
|
||||||
yield ctr.en.eq(1)
|
yield ctr.en.eq(1)
|
||||||
sim.add_sync_process(ce_proc())
|
sim.add_sync_process(ce_proc)
|
||||||
|
with sim.write_vcd("ctrl.vcd", "ctrl.gtkw", traces=[ctr.en, ctr.v, ctr.o]):
|
||||||
sim.run_until(100e-6, run_passive=True)
|
sim.run_until(100e-6, run_passive=True)
|
||||||
|
|
1660
nmigen/back/pysim.py
1660
nmigen/back/pysim.py
File diff suppressed because it is too large
Load diff
|
@ -21,15 +21,24 @@ def run_simulation(fragment_or_module, generators, clocks={"sync": 10}, vcd_name
|
||||||
generators = {"sync": generators}
|
generators = {"sync": generators}
|
||||||
fragment.domains += ClockDomain("sync")
|
fragment.domains += ClockDomain("sync")
|
||||||
|
|
||||||
with Simulator(fragment, vcd_file=open(vcd_name, "w") if vcd_name else None) as sim:
|
sim = Simulator(fragment)
|
||||||
for domain, period in clocks.items():
|
for domain, period in clocks.items():
|
||||||
sim.add_clock(period / 1e9, domain=domain)
|
sim.add_clock(period / 1e9, domain=domain)
|
||||||
for domain, processes in generators.items():
|
for domain, processes in generators.items():
|
||||||
|
def wrap(process):
|
||||||
|
def wrapper():
|
||||||
|
yield from process
|
||||||
|
return wrapper
|
||||||
if isinstance(processes, Iterable) and not inspect.isgenerator(processes):
|
if isinstance(processes, Iterable) and not inspect.isgenerator(processes):
|
||||||
for process in processes:
|
for process in processes:
|
||||||
sim.add_sync_process(process, domain=domain)
|
sim.add_sync_process(wrap(process), domain=domain)
|
||||||
|
else:
|
||||||
|
sim.add_sync_process(wrap(processes), domain=domain)
|
||||||
|
|
||||||
|
if vcd_name is not None:
|
||||||
|
with sim.write_vcd(vcd_name):
|
||||||
|
sim.run()
|
||||||
else:
|
else:
|
||||||
sim.add_sync_process(processes, domain=domain)
|
|
||||||
sim.run()
|
sim.run()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,8 @@ class FFSynchronizerTestCase(FHDLTestCase):
|
||||||
i = Signal()
|
i = Signal()
|
||||||
o = Signal()
|
o = Signal()
|
||||||
frag = FFSynchronizer(i, o)
|
frag = FFSynchronizer(i, o)
|
||||||
with Simulator(frag) as sim:
|
|
||||||
|
sim = Simulator(frag)
|
||||||
sim.add_clock(1e-6)
|
sim.add_clock(1e-6)
|
||||||
def process():
|
def process():
|
||||||
self.assertEqual((yield o), 0)
|
self.assertEqual((yield o), 0)
|
||||||
|
@ -37,7 +38,8 @@ class FFSynchronizerTestCase(FHDLTestCase):
|
||||||
i = Signal(reset=1)
|
i = Signal(reset=1)
|
||||||
o = Signal()
|
o = Signal()
|
||||||
frag = FFSynchronizer(i, o, reset=1)
|
frag = FFSynchronizer(i, o, reset=1)
|
||||||
with Simulator(frag) as sim:
|
|
||||||
|
sim = Simulator(frag)
|
||||||
sim.add_clock(1e-6)
|
sim.add_clock(1e-6)
|
||||||
def process():
|
def process():
|
||||||
self.assertEqual((yield o), 1)
|
self.assertEqual((yield o), 1)
|
||||||
|
@ -69,7 +71,7 @@ class ResetSynchronizerTestCase(FHDLTestCase):
|
||||||
s = Signal(reset=1)
|
s = Signal(reset=1)
|
||||||
m.d.sync += s.eq(0)
|
m.d.sync += s.eq(0)
|
||||||
|
|
||||||
with Simulator(m, vcd_file=open("test.vcd", "w")) as sim:
|
sim = Simulator(m)
|
||||||
sim.add_clock(1e-6)
|
sim.add_clock(1e-6)
|
||||||
def process():
|
def process():
|
||||||
# initial reset
|
# initial reset
|
||||||
|
@ -84,7 +86,7 @@ class ResetSynchronizerTestCase(FHDLTestCase):
|
||||||
|
|
||||||
yield arst.eq(1)
|
yield arst.eq(1)
|
||||||
yield Delay(1e-8)
|
yield Delay(1e-8)
|
||||||
self.assertEqual((yield s), 1)
|
self.assertEqual((yield s), 0)
|
||||||
yield Tick(); yield Delay(1e-8)
|
yield Tick(); yield Delay(1e-8)
|
||||||
self.assertEqual((yield s), 1)
|
self.assertEqual((yield s), 1)
|
||||||
yield arst.eq(0)
|
yield arst.eq(0)
|
||||||
|
@ -96,4 +98,5 @@ class ResetSynchronizerTestCase(FHDLTestCase):
|
||||||
self.assertEqual((yield s), 0)
|
self.assertEqual((yield s), 0)
|
||||||
yield Tick(); yield Delay(1e-8)
|
yield Tick(); yield Delay(1e-8)
|
||||||
sim.add_process(process)
|
sim.add_process(process)
|
||||||
|
with sim.write_vcd("test.vcd"):
|
||||||
sim.run()
|
sim.run()
|
||||||
|
|
|
@ -8,26 +8,26 @@ from ..lib.coding import *
|
||||||
class EncoderTestCase(FHDLTestCase):
|
class EncoderTestCase(FHDLTestCase):
|
||||||
def test_basic(self):
|
def test_basic(self):
|
||||||
enc = Encoder(4)
|
enc = Encoder(4)
|
||||||
with Simulator(enc) as sim:
|
|
||||||
def process():
|
def process():
|
||||||
self.assertEqual((yield enc.n), 1)
|
self.assertEqual((yield enc.n), 1)
|
||||||
self.assertEqual((yield enc.o), 0)
|
self.assertEqual((yield enc.o), 0)
|
||||||
|
|
||||||
yield enc.i.eq(0b0001)
|
yield enc.i.eq(0b0001)
|
||||||
yield Delay()
|
yield Settle()
|
||||||
self.assertEqual((yield enc.n), 0)
|
self.assertEqual((yield enc.n), 0)
|
||||||
self.assertEqual((yield enc.o), 0)
|
self.assertEqual((yield enc.o), 0)
|
||||||
|
|
||||||
yield enc.i.eq(0b0100)
|
yield enc.i.eq(0b0100)
|
||||||
yield Delay()
|
yield Settle()
|
||||||
self.assertEqual((yield enc.n), 0)
|
self.assertEqual((yield enc.n), 0)
|
||||||
self.assertEqual((yield enc.o), 2)
|
self.assertEqual((yield enc.o), 2)
|
||||||
|
|
||||||
yield enc.i.eq(0b0110)
|
yield enc.i.eq(0b0110)
|
||||||
yield Delay()
|
yield Settle()
|
||||||
self.assertEqual((yield enc.n), 1)
|
self.assertEqual((yield enc.n), 1)
|
||||||
self.assertEqual((yield enc.o), 0)
|
self.assertEqual((yield enc.o), 0)
|
||||||
|
|
||||||
|
sim = Simulator(enc)
|
||||||
sim.add_process(process)
|
sim.add_process(process)
|
||||||
sim.run()
|
sim.run()
|
||||||
|
|
||||||
|
@ -35,26 +35,26 @@ class EncoderTestCase(FHDLTestCase):
|
||||||
class PriorityEncoderTestCase(FHDLTestCase):
|
class PriorityEncoderTestCase(FHDLTestCase):
|
||||||
def test_basic(self):
|
def test_basic(self):
|
||||||
enc = PriorityEncoder(4)
|
enc = PriorityEncoder(4)
|
||||||
with Simulator(enc) as sim:
|
|
||||||
def process():
|
def process():
|
||||||
self.assertEqual((yield enc.n), 1)
|
self.assertEqual((yield enc.n), 1)
|
||||||
self.assertEqual((yield enc.o), 0)
|
self.assertEqual((yield enc.o), 0)
|
||||||
|
|
||||||
yield enc.i.eq(0b0001)
|
yield enc.i.eq(0b0001)
|
||||||
yield Delay()
|
yield Settle()
|
||||||
self.assertEqual((yield enc.n), 0)
|
self.assertEqual((yield enc.n), 0)
|
||||||
self.assertEqual((yield enc.o), 0)
|
self.assertEqual((yield enc.o), 0)
|
||||||
|
|
||||||
yield enc.i.eq(0b0100)
|
yield enc.i.eq(0b0100)
|
||||||
yield Delay()
|
yield Settle()
|
||||||
self.assertEqual((yield enc.n), 0)
|
self.assertEqual((yield enc.n), 0)
|
||||||
self.assertEqual((yield enc.o), 2)
|
self.assertEqual((yield enc.o), 2)
|
||||||
|
|
||||||
yield enc.i.eq(0b0110)
|
yield enc.i.eq(0b0110)
|
||||||
yield Delay()
|
yield Settle()
|
||||||
self.assertEqual((yield enc.n), 0)
|
self.assertEqual((yield enc.n), 0)
|
||||||
self.assertEqual((yield enc.o), 1)
|
self.assertEqual((yield enc.o), 1)
|
||||||
|
|
||||||
|
sim = Simulator(enc)
|
||||||
sim.add_process(process)
|
sim.add_process(process)
|
||||||
sim.run()
|
sim.run()
|
||||||
|
|
||||||
|
@ -62,22 +62,22 @@ class PriorityEncoderTestCase(FHDLTestCase):
|
||||||
class DecoderTestCase(FHDLTestCase):
|
class DecoderTestCase(FHDLTestCase):
|
||||||
def test_basic(self):
|
def test_basic(self):
|
||||||
dec = Decoder(4)
|
dec = Decoder(4)
|
||||||
with Simulator(dec) as sim:
|
|
||||||
def process():
|
def process():
|
||||||
self.assertEqual((yield dec.o), 0b0001)
|
self.assertEqual((yield dec.o), 0b0001)
|
||||||
|
|
||||||
yield dec.i.eq(1)
|
yield dec.i.eq(1)
|
||||||
yield Delay()
|
yield Settle()
|
||||||
self.assertEqual((yield dec.o), 0b0010)
|
self.assertEqual((yield dec.o), 0b0010)
|
||||||
|
|
||||||
yield dec.i.eq(3)
|
yield dec.i.eq(3)
|
||||||
yield Delay()
|
yield Settle()
|
||||||
self.assertEqual((yield dec.o), 0b1000)
|
self.assertEqual((yield dec.o), 0b1000)
|
||||||
|
|
||||||
yield dec.n.eq(1)
|
yield dec.n.eq(1)
|
||||||
yield Delay()
|
yield Settle()
|
||||||
self.assertEqual((yield dec.o), 0b0000)
|
self.assertEqual((yield dec.o), 0b0000)
|
||||||
|
|
||||||
|
sim = Simulator(dec)
|
||||||
sim.add_process(process)
|
sim.add_process(process)
|
||||||
sim.run()
|
sim.run()
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import os
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
|
||||||
from .utils import *
|
from .utils import *
|
||||||
|
@ -25,16 +26,14 @@ class SimulatorUnitTestCase(FHDLTestCase):
|
||||||
for signal in flatten(s._lhs_signals() for s in Statement.cast(stmt)):
|
for signal in flatten(s._lhs_signals() for s in Statement.cast(stmt)):
|
||||||
frag.add_driver(signal)
|
frag.add_driver(signal)
|
||||||
|
|
||||||
with Simulator(frag,
|
sim = Simulator(frag)
|
||||||
vcd_file =open("test.vcd", "w"),
|
|
||||||
gtkw_file=open("test.gtkw", "w"),
|
|
||||||
traces=[*isigs, osig]) as sim:
|
|
||||||
def process():
|
def process():
|
||||||
for isig, input in zip(isigs, inputs):
|
for isig, input in zip(isigs, inputs):
|
||||||
yield isig.eq(input)
|
yield isig.eq(input)
|
||||||
yield Delay()
|
yield Settle()
|
||||||
self.assertEqual((yield osig), output.value)
|
self.assertEqual((yield osig), output.value)
|
||||||
sim.add_process(process)
|
sim.add_process(process)
|
||||||
|
with sim.write_vcd("test.vcd", "test.gtkw", traces=[*isigs, osig]):
|
||||||
sim.run()
|
sim.run()
|
||||||
|
|
||||||
def test_invert(self):
|
def test_invert(self):
|
||||||
|
@ -213,6 +212,13 @@ class SimulatorUnitTestCase(FHDLTestCase):
|
||||||
stmt = lambda y, a: [Cat(l, m, n).eq(a), y.eq(Cat(n, m, l))]
|
stmt = lambda y, a: [Cat(l, m, n).eq(a), y.eq(Cat(n, m, l))]
|
||||||
self.assertStatement(stmt, [C(0b100101110, 9)], C(0b110101100, 9))
|
self.assertStatement(stmt, [C(0b100101110, 9)], C(0b110101100, 9))
|
||||||
|
|
||||||
|
def test_nested_cat_lhs(self):
|
||||||
|
l = Signal(3)
|
||||||
|
m = Signal(3)
|
||||||
|
n = Signal(3)
|
||||||
|
stmt = lambda y, a: [Cat(Cat(l, Cat(m)), n).eq(a), y.eq(Cat(n, m, l))]
|
||||||
|
self.assertStatement(stmt, [C(0b100101110, 9)], C(0b110101100, 9))
|
||||||
|
|
||||||
def test_record(self):
|
def test_record(self):
|
||||||
rec = Record([
|
rec = Record([
|
||||||
("l", 1),
|
("l", 1),
|
||||||
|
@ -277,8 +283,9 @@ class SimulatorUnitTestCase(FHDLTestCase):
|
||||||
class SimulatorIntegrationTestCase(FHDLTestCase):
|
class SimulatorIntegrationTestCase(FHDLTestCase):
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def assertSimulation(self, module, deadline=None):
|
def assertSimulation(self, module, deadline=None):
|
||||||
with Simulator(module) as sim:
|
sim = Simulator(module)
|
||||||
yield sim
|
yield sim
|
||||||
|
with sim.write_vcd("test.vcd", "test.gtkw"):
|
||||||
if deadline is None:
|
if deadline is None:
|
||||||
sim.run()
|
sim.run()
|
||||||
else:
|
else:
|
||||||
|
@ -300,11 +307,15 @@ class SimulatorIntegrationTestCase(FHDLTestCase):
|
||||||
yield Delay(1e-6)
|
yield Delay(1e-6)
|
||||||
self.assertEqual((yield self.count), 4)
|
self.assertEqual((yield self.count), 4)
|
||||||
yield self.sync.clk.eq(1)
|
yield self.sync.clk.eq(1)
|
||||||
|
self.assertEqual((yield self.count), 4)
|
||||||
|
yield Settle()
|
||||||
self.assertEqual((yield self.count), 5)
|
self.assertEqual((yield self.count), 5)
|
||||||
yield Delay(1e-6)
|
yield Delay(1e-6)
|
||||||
self.assertEqual((yield self.count), 5)
|
self.assertEqual((yield self.count), 5)
|
||||||
yield self.sync.clk.eq(0)
|
yield self.sync.clk.eq(0)
|
||||||
self.assertEqual((yield self.count), 5)
|
self.assertEqual((yield self.count), 5)
|
||||||
|
yield Settle()
|
||||||
|
self.assertEqual((yield self.count), 5)
|
||||||
for _ in range(3):
|
for _ in range(3):
|
||||||
yield Delay(1e-6)
|
yield Delay(1e-6)
|
||||||
yield self.sync.clk.eq(1)
|
yield self.sync.clk.eq(1)
|
||||||
|
@ -328,6 +339,26 @@ class SimulatorIntegrationTestCase(FHDLTestCase):
|
||||||
self.assertEqual((yield self.count), 0)
|
self.assertEqual((yield self.count), 0)
|
||||||
sim.add_sync_process(process)
|
sim.add_sync_process(process)
|
||||||
|
|
||||||
|
def test_reset(self):
|
||||||
|
self.setUp_counter()
|
||||||
|
sim = Simulator(self.m)
|
||||||
|
sim.add_clock(1e-6)
|
||||||
|
times = 0
|
||||||
|
def process():
|
||||||
|
nonlocal times
|
||||||
|
self.assertEqual((yield self.count), 4)
|
||||||
|
yield
|
||||||
|
self.assertEqual((yield self.count), 5)
|
||||||
|
yield
|
||||||
|
self.assertEqual((yield self.count), 6)
|
||||||
|
yield
|
||||||
|
times += 1
|
||||||
|
sim.add_sync_process(process)
|
||||||
|
sim.run()
|
||||||
|
sim.reset()
|
||||||
|
sim.run()
|
||||||
|
self.assertEqual(times, 2)
|
||||||
|
|
||||||
def setUp_alu(self):
|
def setUp_alu(self):
|
||||||
self.a = Signal(8)
|
self.a = Signal(8)
|
||||||
self.b = Signal(8)
|
self.b = Signal(8)
|
||||||
|
@ -406,7 +437,7 @@ class SimulatorIntegrationTestCase(FHDLTestCase):
|
||||||
def process():
|
def process():
|
||||||
yield self.i.eq(0b10101010)
|
yield self.i.eq(0b10101010)
|
||||||
yield self.i[:4].eq(-1)
|
yield self.i[:4].eq(-1)
|
||||||
yield Delay()
|
yield Settle()
|
||||||
self.assertEqual((yield self.i[:4]), 0b1111)
|
self.assertEqual((yield self.i[:4]), 0b1111)
|
||||||
self.assertEqual((yield self.i), 0b10101111)
|
self.assertEqual((yield self.i), 0b10101111)
|
||||||
sim.add_process(process)
|
sim.add_process(process)
|
||||||
|
@ -426,10 +457,18 @@ class SimulatorIntegrationTestCase(FHDLTestCase):
|
||||||
def test_add_process_wrong(self):
|
def test_add_process_wrong(self):
|
||||||
with self.assertSimulation(Module()) as sim:
|
with self.assertSimulation(Module()) as sim:
|
||||||
with self.assertRaises(TypeError,
|
with self.assertRaises(TypeError,
|
||||||
msg="Cannot add a process 1 because it is not a generator or "
|
msg="Cannot add a process 1 because it is not a generator function"):
|
||||||
"a generator function"):
|
|
||||||
sim.add_process(1)
|
sim.add_process(1)
|
||||||
|
|
||||||
|
def test_add_process_wrong_generator(self):
|
||||||
|
with self.assertSimulation(Module()) as sim:
|
||||||
|
with self.assertWarns(DeprecationWarning,
|
||||||
|
msg="instead of generators, use generator functions as processes; "
|
||||||
|
"this allows the simulator to be repeatedly reset"):
|
||||||
|
def process():
|
||||||
|
yield Delay()
|
||||||
|
sim.add_process(process())
|
||||||
|
|
||||||
def test_add_clock_wrong_twice(self):
|
def test_add_clock_wrong_twice(self):
|
||||||
m = Module()
|
m = Module()
|
||||||
s = Signal()
|
s = Signal()
|
||||||
|
@ -452,37 +491,18 @@ class SimulatorIntegrationTestCase(FHDLTestCase):
|
||||||
with self.assertSimulation(m) as sim:
|
with self.assertSimulation(m) as sim:
|
||||||
sim.add_clock(1, if_exists=True)
|
sim.add_clock(1, if_exists=True)
|
||||||
|
|
||||||
def test_eq_signal_unused_wrong(self):
|
|
||||||
self.setUp_lhs_rhs()
|
|
||||||
self.s = Signal()
|
|
||||||
with self.assertSimulation(self.m) as sim:
|
|
||||||
def process():
|
|
||||||
with self.assertRaisesRegex(ValueError,
|
|
||||||
regex=r"Process .+? sent a request to set signal \(sig s\), "
|
|
||||||
r"which is not a part of simulation"):
|
|
||||||
yield self.s.eq(0)
|
|
||||||
yield Delay()
|
|
||||||
sim.add_process(process)
|
|
||||||
|
|
||||||
def test_eq_signal_comb_wrong(self):
|
|
||||||
self.setUp_lhs_rhs()
|
|
||||||
with self.assertSimulation(self.m) as sim:
|
|
||||||
def process():
|
|
||||||
with self.assertRaisesRegex(ValueError,
|
|
||||||
regex=r"Process .+? sent a request to set signal \(sig o\), "
|
|
||||||
r"which is a part of combinatorial assignment in simulation"):
|
|
||||||
yield self.o.eq(0)
|
|
||||||
yield Delay()
|
|
||||||
sim.add_process(process)
|
|
||||||
|
|
||||||
def test_command_wrong(self):
|
def test_command_wrong(self):
|
||||||
|
survived = False
|
||||||
with self.assertSimulation(Module()) as sim:
|
with self.assertSimulation(Module()) as sim:
|
||||||
def process():
|
def process():
|
||||||
|
nonlocal survived
|
||||||
with self.assertRaisesRegex(TypeError,
|
with self.assertRaisesRegex(TypeError,
|
||||||
regex=r"Received unsupported command 1 from process .+?"):
|
regex=r"Received unsupported command 1 from process .+?"):
|
||||||
yield 1
|
yield 1
|
||||||
yield Delay()
|
yield Settle()
|
||||||
|
survived = True
|
||||||
sim.add_process(process)
|
sim.add_process(process)
|
||||||
|
self.assertTrue(survived)
|
||||||
|
|
||||||
def setUp_memory(self, rd_synchronous=True, rd_transparent=True, wr_granularity=None):
|
def setUp_memory(self, rd_synchronous=True, rd_transparent=True, wr_granularity=None):
|
||||||
self.m = Module()
|
self.m = Module()
|
||||||
|
@ -558,7 +578,7 @@ class SimulatorIntegrationTestCase(FHDLTestCase):
|
||||||
self.assertEqual((yield self.rdport.data), 0xaa)
|
self.assertEqual((yield self.rdport.data), 0xaa)
|
||||||
yield
|
yield
|
||||||
self.assertEqual((yield self.rdport.data), 0xaa)
|
self.assertEqual((yield self.rdport.data), 0xaa)
|
||||||
yield Delay(1e-6) # let comb propagate
|
yield Settle()
|
||||||
self.assertEqual((yield self.rdport.data), 0x33)
|
self.assertEqual((yield self.rdport.data), 0x33)
|
||||||
sim.add_clock(1e-6)
|
sim.add_clock(1e-6)
|
||||||
sim.add_sync_process(process)
|
sim.add_sync_process(process)
|
||||||
|
@ -571,11 +591,11 @@ class SimulatorIntegrationTestCase(FHDLTestCase):
|
||||||
yield self.wrport.en.eq(1)
|
yield self.wrport.en.eq(1)
|
||||||
yield
|
yield
|
||||||
self.assertEqual((yield self.rdport.data), 0xaa)
|
self.assertEqual((yield self.rdport.data), 0xaa)
|
||||||
yield Delay(1e-6) # let comb propagate
|
yield Settle()
|
||||||
self.assertEqual((yield self.rdport.data), 0x33)
|
self.assertEqual((yield self.rdport.data), 0x33)
|
||||||
yield
|
yield
|
||||||
yield self.rdport.addr.eq(1)
|
yield self.rdport.addr.eq(1)
|
||||||
yield Delay(1e-6) # let comb propagate
|
yield Settle()
|
||||||
self.assertEqual((yield self.rdport.data), 0x33)
|
self.assertEqual((yield self.rdport.data), 0x33)
|
||||||
sim.add_clock(1e-6)
|
sim.add_clock(1e-6)
|
||||||
sim.add_sync_process(process)
|
sim.add_sync_process(process)
|
||||||
|
@ -585,10 +605,10 @@ class SimulatorIntegrationTestCase(FHDLTestCase):
|
||||||
with self.assertSimulation(self.m) as sim:
|
with self.assertSimulation(self.m) as sim:
|
||||||
def process():
|
def process():
|
||||||
yield self.rdport.addr.eq(0)
|
yield self.rdport.addr.eq(0)
|
||||||
yield Delay()
|
yield Settle()
|
||||||
self.assertEqual((yield self.rdport.data), 0xaa)
|
self.assertEqual((yield self.rdport.data), 0xaa)
|
||||||
yield self.rdport.addr.eq(1)
|
yield self.rdport.addr.eq(1)
|
||||||
yield Delay()
|
yield Settle()
|
||||||
self.assertEqual((yield self.rdport.data), 0x55)
|
self.assertEqual((yield self.rdport.data), 0x55)
|
||||||
yield self.rdport.addr.eq(0)
|
yield self.rdport.addr.eq(0)
|
||||||
yield self.wrport.addr.eq(0)
|
yield self.wrport.addr.eq(0)
|
||||||
|
@ -596,7 +616,7 @@ class SimulatorIntegrationTestCase(FHDLTestCase):
|
||||||
yield self.wrport.en.eq(1)
|
yield self.wrport.en.eq(1)
|
||||||
yield Tick("sync")
|
yield Tick("sync")
|
||||||
self.assertEqual((yield self.rdport.data), 0xaa)
|
self.assertEqual((yield self.rdport.data), 0xaa)
|
||||||
yield Delay(1e-6) # let comb propagate
|
yield Settle()
|
||||||
self.assertEqual((yield self.rdport.data), 0x33)
|
self.assertEqual((yield self.rdport.data), 0x33)
|
||||||
sim.add_clock(1e-6)
|
sim.add_clock(1e-6)
|
||||||
sim.add_process(process)
|
sim.add_process(process)
|
||||||
|
@ -661,8 +681,26 @@ class SimulatorIntegrationTestCase(FHDLTestCase):
|
||||||
sim.add_sync_process(process_gen)
|
sim.add_sync_process(process_gen)
|
||||||
sim.add_sync_process(process_check)
|
sim.add_sync_process(process_check)
|
||||||
|
|
||||||
def test_wrong_not_run(self):
|
def test_vcd_wrong_nonzero_time(self):
|
||||||
with self.assertWarns(UserWarning,
|
s = Signal()
|
||||||
msg="Simulation created, but not run"):
|
m = Module()
|
||||||
with Simulator(Fragment()) as sim:
|
m.d.sync += s.eq(s)
|
||||||
|
sim = Simulator(m)
|
||||||
|
sim.add_clock(1e-6)
|
||||||
|
sim.run_until(1e-5)
|
||||||
|
with self.assertRaisesRegex(ValueError,
|
||||||
|
regex=r"^Cannot start writing waveforms after advancing simulation time$"):
|
||||||
|
with sim.write_vcd(open(os.path.devnull, "wt")):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_vcd_wrong_twice(self):
|
||||||
|
s = Signal()
|
||||||
|
m = Module()
|
||||||
|
m.d.sync += s.eq(s)
|
||||||
|
sim = Simulator(m)
|
||||||
|
sim.add_clock(1e-6)
|
||||||
|
with self.assertRaisesRegex(ValueError,
|
||||||
|
regex=r"^Already writing waveforms to .+$"):
|
||||||
|
with sim.write_vcd(open(os.path.devnull, "wt")):
|
||||||
|
with sim.write_vcd(open(os.path.devnull, "wt")):
|
||||||
pass
|
pass
|
||||||
|
|
6
setup.py
6
setup.py
|
@ -24,7 +24,11 @@ setup(
|
||||||
license="BSD",
|
license="BSD",
|
||||||
python_requires="~=3.6",
|
python_requires="~=3.6",
|
||||||
setup_requires=["setuptools_scm"],
|
setup_requires=["setuptools_scm"],
|
||||||
install_requires=["setuptools", "pyvcd>=0.1.4", "bitarray", "Jinja2"],
|
install_requires=[
|
||||||
|
"setuptools",
|
||||||
|
"pyvcd~=0.1.4", # for nmigen.pysim
|
||||||
|
"Jinja2", # for nmigen.build
|
||||||
|
],
|
||||||
packages=find_packages(),
|
packages=find_packages(),
|
||||||
entry_points={
|
entry_points={
|
||||||
"console_scripts": [
|
"console_scripts": [
|
||||||
|
|
Loading…
Reference in a new issue