hdl.ast,back.rtlil: implement Cover.
authorwhitequark <cz@m-labs.hk>
Tue, 3 Sep 2019 01:32:24 +0000 (01:32 +0000)
committerwhitequark <cz@m-labs.hk>
Tue, 3 Sep 2019 01:32:24 +0000 (01:32 +0000)
Fixes #194.

nmigen/asserts.py
nmigen/back/pysim.py
nmigen/back/rtlil.py
nmigen/hdl/ast.py
nmigen/hdl/dsl.py
nmigen/hdl/xfrm.py
nmigen/test/test_hdl_dsl.py

index f374cf0e296cad14cbc07ba0747283e586f90494..b0e97b9cb0ea7ac950b3d091c244ed7423c7e9ee 100644 (file)
@@ -1,2 +1,2 @@
-from .hdl.ast import AnyConst, AnySeq, Assert, Assume
+from .hdl.ast import AnyConst, AnySeq, Assert, Assume, Cover
 from .hdl.ast import Past, Stable, Rose, Fell, Initial
index 25b65a70e100f2fe9a0da4a4d1663438b532ed49..82df6055cb64d05cecb1b3b700a9e15d4aa0d1b7 100644 (file)
@@ -320,6 +320,9 @@ class _StatementCompiler(StatementVisitor):
     def on_Assume(self, stmt):
         pass # :nocov:
 
+    def on_Cover(self, stmt):
+        raise NotImplementedError("Covers not yet implemented for Simulator backend.") # :nocov:
+
     def on_Switch(self, stmt):
         test  = self.rrhs_compiler(stmt.test)
         cases = []
index f0c156367d769c57b22570abdda205dfcbaa87f2..09788aa50c29ef8f00b370b2bd013396a7ae54b1 100644 (file)
@@ -654,27 +654,20 @@ class _StatementCompiler(xfrm.StatementVisitor):
         else:
             self._case.assign(self.lhs_compiler(stmt.lhs), rhs_sigspec)
 
-    def on_Assert(self, stmt):
+    def on_property(self, stmt):
         self(stmt._check.eq(stmt.test))
         self(stmt._en.eq(1))
 
         en_wire = self.rhs_compiler(stmt._en)
         check_wire = self.rhs_compiler(stmt._check)
-        self.state.rtlil.cell("$assert", ports={
+        self.state.rtlil.cell("$" + stmt._kind, ports={
             "\\A": check_wire,
             "\\EN": en_wire,
         }, src=src(stmt.src_loc))
 
-    def on_Assume(self, stmt):
-        self(stmt._check.eq(stmt.test))
-        self(stmt._en.eq(1))
-
-        en_wire = self.rhs_compiler(stmt._en)
-        check_wire = self.rhs_compiler(stmt._check)
-        self.state.rtlil.cell("$assume", ports={
-            "\\A": check_wire,
-            "\\EN": en_wire,
-        }, src=src(stmt.src_loc))
+    on_Assert = on_property
+    on_Assume = on_property
+    on_Cover  = on_property
 
     def on_Switch(self, stmt):
         self._check_rhs(stmt.test)
index c5376d0d02dfd475300cdfc8d6fdaeb2e3a65da2..a6839e2963e3eb4ab341946554114833b8ca3297 100644 (file)
@@ -15,7 +15,7 @@ __all__ = [
     "Signal", "ClockSignal", "ResetSignal",
     "UserValue",
     "Sample", "Past", "Stable", "Rose", "Fell", "Initial",
-    "Statement", "Assign", "Assert", "Assume", "Switch", "Delay", "Tick",
+    "Statement", "Assign", "Assert", "Assume", "Cover", "Switch", "Delay", "Tick",
     "Passive", "ValueKey", "ValueDict", "ValueSet", "SignalKey", "SignalDict",
     "SignalSet",
 ]
@@ -1080,6 +1080,11 @@ class Assume(Property):
     _kind = "assume"
 
 
+@final
+class Cover(Property):
+    _kind = "cover"
+
+
 # @final
 class Switch(Statement):
     def __init__(self, test, cases, *, src_loc=None, src_loc_at=0, case_src_locs={}):
index 9a7d598abf51173b56f79e15c50501dd5ca0a3e0..7f85f2d5b476786820384f59dbba8110ef472dc9 100644 (file)
@@ -417,9 +417,9 @@ class Module(_ModuleBuilderRoot, Elaboratable):
             self._pop_ctrl()
 
         for assign in Statement.wrap(assigns):
-            if not compat_mode and not isinstance(assign, (Assign, Assert, Assume)):
+            if not compat_mode and not isinstance(assign, (Assign, Assert, Assume, Cover)):
                 raise SyntaxError(
-                    "Only assignments, asserts, and assumes may be appended to d.{}"
+                    "Only assignments and property checks may be appended to d.{}"
                     .format(domain_name(domain)))
 
             assign = SampleDomainInjector(domain)(assign)
index 8793c5996d33ab544da82f2242366f1e0c21fd92..ab12ffdd3a46b914bdb5ca83ecf149758d583aa8 100644 (file)
@@ -196,6 +196,10 @@ class StatementVisitor(metaclass=ABCMeta):
     def on_Assume(self, stmt):
         pass # :nocov:
 
+    @abstractmethod
+    def on_Cover(self, stmt):
+        pass # :nocov:
+
     @abstractmethod
     def on_Switch(self, stmt):
         pass # :nocov:
@@ -217,6 +221,8 @@ class StatementVisitor(metaclass=ABCMeta):
             new_stmt = self.on_Assert(stmt)
         elif type(stmt) is Assume:
             new_stmt = self.on_Assume(stmt)
+        elif type(stmt) is Cover:
+            new_stmt = self.on_Cover(stmt)
         elif isinstance(stmt, Switch):
             # Uses `isinstance()` and not `type() is` because nmigen.compat requires it.
             new_stmt = self.on_Switch(stmt)
@@ -247,6 +253,9 @@ class StatementTransformer(StatementVisitor):
     def on_Assume(self, stmt):
         return Assume(self.on_value(stmt.test), _check=stmt._check, _en=stmt._en)
 
+    def on_Cover(self, stmt):
+        return Cover(self.on_value(stmt.test), _check=stmt._check, _en=stmt._en)
+
     def on_Switch(self, stmt):
         cases = OrderedDict((k, self.on_statement(s)) for k, s in stmt.cases.items())
         return Switch(self.on_value(stmt.test), cases)
@@ -396,11 +405,12 @@ class DomainCollector(ValueVisitor, StatementVisitor):
         self.on_value(stmt.lhs)
         self.on_value(stmt.rhs)
 
-    def on_Assert(self, stmt):
+    def on_property(self, stmt):
         self.on_value(stmt.test)
 
-    def on_Assume(self, stmt):
-        self.on_value(stmt.test)
+    on_Assert = on_property
+    on_Assume = on_property
+    on_Cover  = on_property
 
     def on_Switch(self, stmt):
         self.on_value(stmt.test)
@@ -598,12 +608,13 @@ class SampleLowerer(FragmentTransformer, ValueTransformer, StatementTransformer)
 
 
 class SwitchCleaner(StatementVisitor):
-    def on_Assign(self, stmt):
+    def on_ignore(self, stmt):
         return stmt
 
-    on_Assert = on_Assign
-
-    on_Assume = on_Assign
+    on_Assign = on_ignore
+    on_Assert = on_ignore
+    on_Assume = on_ignore
+    on_Cover  = on_ignore
 
     def on_Switch(self, stmt):
         cases = OrderedDict((k, self.on_statement(s)) for k, s in stmt.cases.items())
@@ -651,9 +662,14 @@ class LHSGroupAnalyzer(StatementVisitor):
         if lhs_signals:
             self.unify(*stmt._lhs_signals())
 
-    on_Assert = on_Assign
+    def on_property(self, stmt):
+        lhs_signals = stmt._lhs_signals()
+        if lhs_signals:
+            self.unify(*stmt._lhs_signals())
 
-    on_Assume = on_Assign
+    on_Assert = on_property
+    on_Assume = on_property
+    on_Cover  = on_property
 
     def on_Switch(self, stmt):
         for case_stmts in stmt.cases.values():
@@ -681,12 +697,14 @@ class LHSGroupFilter(SwitchCleaner):
             if any_lhs_signal in self.signals:
                 return stmt
 
-    def on_Assert(self, stmt):
+    def on_property(self, stmt):
         any_lhs_signal = next(iter(stmt._lhs_signals()))
         if any_lhs_signal in self.signals:
             return stmt
 
-    on_Assume = on_Assert
+    on_Assert = on_property
+    on_Assume = on_property
+    on_Cover  = on_property
 
 
 class _ControlInserter(FragmentTransformer):
index 1da5a6a2b2366d1032a1420ed7cecb2c6ca1cce2..02433d858d57d77e2bfe49d90db0314741b22d64 100644 (file)
@@ -74,7 +74,7 @@ class DSLTestCase(FHDLTestCase):
     def test_d_asgn_wrong(self):
         m = Module()
         with self.assertRaises(SyntaxError,
-                msg="Only assignments, asserts, and assumes may be appended to d.sync"):
+                msg="Only assignments and property checks may be appended to d.sync"):
             m.d.sync += Switch(self.s1, {})
 
     def test_comb_wrong(self):