test.sim: add tests for sync functionality and errors.

This commit is contained in:
whitequark 2018-12-18 17:53:50 +00:00
parent 7fa82a70be
commit 07e9cfa939
3 changed files with 189 additions and 3 deletions

View file

@ -1,4 +1,4 @@
from .hdl.ast import Value, Const, Mux, Cat, Repl, Array, Signal, ClockSignal, ResetSignal from .hdl.ast import Value, Const, C, Mux, Cat, Repl, Array, Signal, ClockSignal, ResetSignal
from .hdl.dsl import Module from .hdl.dsl import Module
from .hdl.cd import ClockDomain from .hdl.cd import ClockDomain
from .hdl.ir import Fragment, Instance from .hdl.ir import Fragment, Instance

View file

@ -354,7 +354,7 @@ class Simulator:
if inspect.isgeneratorfunction(process): if inspect.isgeneratorfunction(process):
process = process() process = process()
if not inspect.isgenerator(process): if not inspect.isgenerator(process):
raise TypeError("Cannot add a process '{!r}' because it is not a generator or" raise TypeError("Cannot add a process '{!r}' because it is not a generator or "
"a generator function" "a generator function"
.format(process)) .format(process))
return process return process
@ -643,11 +643,11 @@ class Simulator:
elif type(cmd) is Assign: elif type(cmd) is Assign:
lhs_signals = cmd.lhs._lhs_signals() lhs_signals = cmd.lhs._lhs_signals()
for signal in lhs_signals: for signal in lhs_signals:
signal_slot = self._signal_slots[signal]
if not signal in self._signals: if not signal in self._signals:
raise ValueError("Process '{}' sent a request to set signal '{!r}', " raise ValueError("Process '{}' sent a request to set signal '{!r}', "
"which is not a part of simulation" "which is not a part of simulation"
.format(self._name_process(process), signal)) .format(self._name_process(process), signal))
signal_slot = self._signal_slots[signal]
if self._comb_signals[signal_slot]: if self._comb_signals[signal_slot]:
raise ValueError("Process '{}' sent a request to set signal '{!r}', " raise ValueError("Process '{}' sent a request to set signal '{!r}', "
"which is a part of combinatorial assignment in " "which is a part of combinatorial assignment in "

View file

@ -1,6 +1,10 @@
from contextlib import contextmanager
from .tools import * from .tools import *
from ..tools import flatten, union from ..tools import flatten, union
from ..hdl.ast import * from ..hdl.ast import *
from ..hdl.cd import *
from ..hdl.dsl import *
from ..hdl.ir import * from ..hdl.ir import *
from ..back.pysim import * from ..back.pysim import *
@ -198,3 +202,185 @@ class SimulatorUnitTestCase(FHDLTestCase):
stmt = lambda y, a: y.eq(array[a].p + array[a].n) stmt = lambda y, a: y.eq(array[a].p + array[a].n)
for i in range(10): for i in range(10):
self.assertStatement(stmt, [C(i)], C(0)) self.assertStatement(stmt, [C(i)], C(0))
class SimulatorIntegrationTestCase(FHDLTestCase):
@contextmanager
def assertSimulation(self, module, deadline=None):
with Simulator(module.lower(platform=None)) as sim:
yield sim
if deadline is None:
sim.run()
else:
sim.run_until(deadline)
def setUp_counter(self):
self.count = Signal(3, reset=4)
self.sync = ClockDomain()
self.m = Module()
self.m.d.sync += self.count.eq(self.count + 1)
self.m.domains += self.sync
def test_counter_process(self):
self.setUp_counter()
with self.assertSimulation(self.m) as sim:
def process():
self.assertEqual((yield self.count), 4)
yield Delay(1e-6)
self.assertEqual((yield self.count), 4)
yield self.sync.clk.eq(1)
self.assertEqual((yield self.count), 5)
yield Delay(1e-6)
self.assertEqual((yield self.count), 5)
yield self.sync.clk.eq(0)
self.assertEqual((yield self.count), 5)
for _ in range(3):
yield Delay(1e-6)
yield self.sync.clk.eq(1)
yield Delay(1e-6)
yield self.sync.clk.eq(0)
self.assertEqual((yield self.count), 0)
sim.add_process(process)
def test_counter_clock_and_sync_process(self):
self.setUp_counter()
with self.assertSimulation(self.m) as sim:
sim.add_clock(1e-6, domain="sync")
def process():
self.assertEqual((yield self.count), 4)
self.assertEqual((yield self.sync.clk), 0)
yield
self.assertEqual((yield self.count), 5)
self.assertEqual((yield self.sync.clk), 1)
for _ in range(3):
yield
self.assertEqual((yield self.count), 0)
sim.add_sync_process(process)
def setUp_alu(self):
self.a = Signal(8)
self.b = Signal(8)
self.o = Signal(8)
self.x = Signal(8)
self.s = Signal(2)
self.sync = ClockDomain(reset_less=True)
self.m = Module()
self.m.d.comb += self.x.eq(self.a ^ self.b)
with self.m.Switch(self.s):
with self.m.Case(0):
self.m.d.sync += self.o.eq(self.a + self.b)
with self.m.Case(1):
self.m.d.sync += self.o.eq(self.a - self.b)
with self.m.Case():
self.m.d.sync += self.o.eq(0)
self.m.domains += self.sync
def test_alu(self):
self.setUp_alu()
with self.assertSimulation(self.m) as sim:
sim.add_clock(1e-6)
def process():
yield self.a.eq(5)
yield self.b.eq(1)
yield
self.assertEqual((yield self.x), 4)
self.assertEqual((yield self.o), 6)
yield self.s.eq(1)
yield
self.assertEqual((yield self.o), 4)
yield self.s.eq(2)
yield
self.assertEqual((yield self.o), 0)
sim.add_sync_process(process)
def setUp_multiclock(self):
self.sys = ClockDomain()
self.pix = ClockDomain()
self.m = Module()
self.m.domains += self.sys, self.pix
def test_multiclock(self):
self.setUp_multiclock()
with self.assertSimulation(self.m) as sim:
sim.add_clock(1e-6, domain="sys")
sim.add_clock(0.3e-6, domain="pix")
def sys_process():
yield Passive()
yield
yield
self.fail()
def pix_process():
yield
yield
yield
sim.add_sync_process(sys_process, domain="sys")
sim.add_sync_process(pix_process, domain="pix")
def setUp_lhs_rhs(self):
self.i = Signal(8)
self.o = Signal(8)
self.m = Module()
self.m.d.comb += self.o.eq(self.i)
def test_complex_lhs_rhs(self):
self.setUp_lhs_rhs()
with self.assertSimulation(self.m) as sim:
def process():
yield self.i.eq(0b10101010)
yield self.i[:4].eq(-1)
yield Delay()
self.assertEqual((yield self.i[:4]), 0b1111)
self.assertEqual((yield self.i), 0b10101111)
sim.add_process(process)
def test_run_until(self):
with self.assertSimulation(Module(), deadline=100e-6) as sim:
sim.add_clock(1e-6)
def process():
for _ in range(100):
yield
self.fail()
def test_add_process_wrong(self):
with self.assertSimulation(Module()) as sim:
with self.assertRaises(TypeError,
msg="Cannot add a process '1' because it is not a generator or "
"a generator function"):
sim.add_process(1)
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):
with self.assertSimulation(Module()) as sim:
def process():
with self.assertRaisesRegex(TypeError,
regex=r"Received unsupported command '1' from process '.+?'"):
yield 1
yield Delay()
sim.add_process(process)