From 4ed147b1bc9b4e17fe0e6e0142cb57cc60606baf Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 14 Dec 2018 14:21:22 +0000 Subject: [PATCH] back.pysim: implement most operators and add tests. --- .coveragerc | 2 + nmigen/back/pysim.py | 29 ++++++----- nmigen/fhdl/ast.py | 2 +- nmigen/test/test_sim.py | 113 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 131 insertions(+), 15 deletions(-) create mode 100644 nmigen/test/test_sim.py diff --git a/.coveragerc b/.coveragerc index c5fad9a..e70df50 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,5 +1,7 @@ [run] branch = True +include = + nmigen/* omit = nmigen/test/* */__init__.py diff --git a/nmigen/back/pysim.py b/nmigen/back/pysim.py index d6df00a..984de7c 100644 --- a/nmigen/back/pysim.py +++ b/nmigen/back/pysim.py @@ -27,13 +27,7 @@ class _State: def get(self, signal): return self.curr[signal] - def set_curr(self, signal, value): - assert isinstance(value, int) - if self.curr[signal] != value: - self.curr_dirty.add(signal) - self.curr[signal] = value - - def set_next(self, signal, value): + def set(self, signal, value): assert isinstance(value, int) if self.next[signal] != value: self.next_dirty.add(signal) @@ -48,11 +42,6 @@ class _State: new_value = self.curr[signal] return old_value, new_value - def iter_dirty(self): - dirty, self.dirty = self.dirty, ValueSet() - for signal in dirty: - yield signal, self.curr[signal], self.next[signal] - normalize = Const.normalize @@ -82,6 +71,8 @@ class _RHSValueCompiler(ValueTransformer): return lambda state: normalize(~arg(state), shape) if value.op == "-": return lambda state: normalize(-arg(state), shape) + if value.op == "b": + return lambda state: normalize(bool(arg(state)), shape) elif len(value.operands) == 2: lhs, rhs = map(self, value.operands) if value.op == "+": @@ -96,11 +87,21 @@ class _RHSValueCompiler(ValueTransformer): return lambda state: normalize(lhs(state) ^ rhs(state), shape) if value.op == "==": return lambda state: normalize(lhs(state) == rhs(state), shape) + if value.op == "!=": + return lambda state: normalize(lhs(state) != rhs(state), shape) + if value.op == "<": + return lambda state: normalize(lhs(state) < rhs(state), shape) + if value.op == "<=": + return lambda state: normalize(lhs(state) <= rhs(state), shape) + if value.op == ">": + return lambda state: normalize(lhs(state) > rhs(state), shape) + if value.op == ">=": + return lambda state: normalize(lhs(state) >= rhs(state), shape) elif len(value.operands) == 3: if value.op == "m": sel, val1, val0 = map(self, value.operands) return lambda state: val1(state) if sel(state) else val0(state) - raise NotImplementedError("Operator '{}' not implemented".format(value.op)) + raise NotImplementedError("Operator '{!r}' not implemented".format(value.op)) # :nocov: def on_Slice(self, value): shape = value.shape() @@ -148,7 +149,7 @@ class _StatementCompiler(StatementTransformer): def lhs_compiler(self, value): # TODO - return lambda state, arg: state.set_next(value, arg) + return lambda state, arg: state.set(value, arg) def on_Assign(self, stmt): assert isinstance(stmt.lhs, Signal) diff --git a/nmigen/fhdl/ast.py b/nmigen/fhdl/ast.py index f97a0c9..3455b38 100644 --- a/nmigen/fhdl/ast.py +++ b/nmigen/fhdl/ast.py @@ -8,7 +8,7 @@ from ..tools import * __all__ = [ - "Value", "Const", "Operator", "Mux", "Part", "Slice", "Cat", "Repl", + "Value", "Const", "C", "Operator", "Mux", "Part", "Slice", "Cat", "Repl", "Signal", "ClockSignal", "ResetSignal", "Statement", "Assign", "Switch", "Delay", "Tick", "Passive", "ValueKey", "ValueDict", "ValueSet", diff --git a/nmigen/test/test_sim.py b/nmigen/test/test_sim.py new file mode 100644 index 0000000..c0bec55 --- /dev/null +++ b/nmigen/test/test_sim.py @@ -0,0 +1,113 @@ +from .tools import * +from ..fhdl.ast import * +from ..fhdl.ir import * +from ..back.pysim import * + + +class SimulatorUnitTestCase(FHDLTestCase): + def assertOperator(self, stmt, inputs, output): + inputs = [Value.wrap(i) for i in inputs] + output = Value.wrap(output) + + isigs = [Signal(i.shape(), name=n) for i, n in zip(inputs, "abcd")] + osig = Signal(output.shape(), name="y") + + frag = Fragment() + frag.add_statements(osig.eq(stmt(*isigs))) + frag.drive(osig) + + with Simulator(frag, + vcd_file =open("test.vcd", "w"), + gtkw_file=open("test.gtkw", "w"), + gtkw_signals=[*isigs, osig]) as sim: + def process(): + for isig, input in zip(isigs, inputs): + yield isig.eq(input) + yield Delay() + self.assertEqual((yield osig), output.value) + sim.add_process(process) + sim.run() + + def test_invert(self): + stmt = lambda a: ~a + self.assertOperator(stmt, [C(0b0000, 4)], C(0b1111, 4)) + self.assertOperator(stmt, [C(0b1010, 4)], C(0b0101, 4)) + self.assertOperator(stmt, [C(0, 4)], C(-1, 4)) + + def test_neg(self): + stmt = lambda a: -a + self.assertOperator(stmt, [C(0b0000, 4)], C(0b0000, 4)) + self.assertOperator(stmt, [C(0b0001, 4)], C(0b1111, 4)) + self.assertOperator(stmt, [C(0b1010, 4)], C(0b0110, 4)) + self.assertOperator(stmt, [C(1, 4)], C(-1, 4)) + self.assertOperator(stmt, [C(5, 4)], C(-5, 4)) + + def test_bool(self): + stmt = lambda a: a.bool() + self.assertOperator(stmt, [C(0, 4)], C(0)) + self.assertOperator(stmt, [C(1, 4)], C(1)) + self.assertOperator(stmt, [C(2, 4)], C(1)) + + def test_add(self): + stmt = lambda a, b: a + b + self.assertOperator(stmt, [C(0, 4), C(1, 4)], C(1, 4)) + self.assertOperator(stmt, [C(-5, 4), C(-5, 4)], C(-10, 5)) + + def test_sub(self): + stmt = lambda a, b: a - b + self.assertOperator(stmt, [C(2, 4), C(1, 4)], C(1, 4)) + self.assertOperator(stmt, [C(0, 4), C(1, 4)], C(-1, 4)) + self.assertOperator(stmt, [C(0, 4), C(10, 4)], C(-10, 5)) + + def test_and(self): + stmt = lambda a, b: a & b + self.assertOperator(stmt, [C(0b1100, 4), C(0b1010, 4)], C(0b1000, 4)) + + def test_or(self): + stmt = lambda a, b: a | b + self.assertOperator(stmt, [C(0b1100, 4), C(0b1010, 4)], C(0b1110, 4)) + + def test_xor(self): + stmt = lambda a, b: a ^ b + self.assertOperator(stmt, [C(0b1100, 4), C(0b1010, 4)], C(0b0110, 4)) + + def test_eq(self): + stmt = lambda a, b: a == b + self.assertOperator(stmt, [C(0, 4), C(0, 4)], C(1)) + self.assertOperator(stmt, [C(0, 4), C(1, 4)], C(0)) + self.assertOperator(stmt, [C(1, 4), C(0, 4)], C(0)) + + def test_ne(self): + stmt = lambda a, b: a != b + self.assertOperator(stmt, [C(0, 4), C(0, 4)], C(0)) + self.assertOperator(stmt, [C(0, 4), C(1, 4)], C(1)) + self.assertOperator(stmt, [C(1, 4), C(0, 4)], C(1)) + + def test_lt(self): + stmt = lambda a, b: a < b + self.assertOperator(stmt, [C(0, 4), C(0, 4)], C(0)) + self.assertOperator(stmt, [C(0, 4), C(1, 4)], C(1)) + self.assertOperator(stmt, [C(1, 4), C(0, 4)], C(0)) + + def test_ge(self): + stmt = lambda a, b: a >= b + self.assertOperator(stmt, [C(0, 4), C(0, 4)], C(1)) + self.assertOperator(stmt, [C(0, 4), C(1, 4)], C(0)) + self.assertOperator(stmt, [C(1, 4), C(0, 4)], C(1)) + + def test_gt(self): + stmt = lambda a, b: a > b + self.assertOperator(stmt, [C(0, 4), C(0, 4)], C(0)) + self.assertOperator(stmt, [C(0, 4), C(1, 4)], C(0)) + self.assertOperator(stmt, [C(1, 4), C(0, 4)], C(1)) + + def test_le(self): + stmt = lambda a, b: a <= b + self.assertOperator(stmt, [C(0, 4), C(0, 4)], C(1)) + self.assertOperator(stmt, [C(0, 4), C(1, 4)], C(1)) + self.assertOperator(stmt, [C(1, 4), C(0, 4)], C(0)) + + def test_mux(self): + stmt = lambda a, b, c: Mux(c, a, b) + self.assertOperator(stmt, [C(2, 4), C(3, 4), C(0)], C(3, 4)) + self.assertOperator(stmt, [C(2, 4), C(3, 4), C(1)], C(2, 4)) -- 2.30.2