back.pysim: implement most operators and add tests.
authorwhitequark <cz@m-labs.hk>
Fri, 14 Dec 2018 14:21:22 +0000 (14:21 +0000)
committerwhitequark <cz@m-labs.hk>
Fri, 14 Dec 2018 14:21:22 +0000 (14:21 +0000)
.coveragerc
nmigen/back/pysim.py
nmigen/fhdl/ast.py
nmigen/test/test_sim.py [new file with mode: 0644]

index c5fad9ad461be42e88e33c32d1c8ad7248a3d537..e70df5002a16597198ff5ea864f12f068d43d1dd 100644 (file)
@@ -1,5 +1,7 @@
 [run]
 branch = True
+include =
+  nmigen/*
 omit =
   nmigen/test/*
   */__init__.py
index d6df00a8130344764a9cd182348ab1c7a4ba7550..984de7ce425e94fd1f0e06b5e1872441f2592593 100644 (file)
@@ -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)
index f97a0c913ad01d29b46bb8b21ae7da9569414d8e..3455b38835d09d35f3ca16763c0451649ea3e221 100644 (file)
@@ -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 (file)
index 0000000..c0bec55
--- /dev/null
@@ -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))