hdl.ir: call back from Fragment.prepare if a clock domain is missing.
authorwhitequark <whitequark@whitequark.org>
Sat, 3 Aug 2019 14:54:20 +0000 (14:54 +0000)
committerwhitequark <whitequark@whitequark.org>
Sat, 3 Aug 2019 14:54:20 +0000 (14:54 +0000)
See #57.

nmigen/build/plat.py
nmigen/compat/fhdl/verilog.py
nmigen/compat/sim/__init__.py
nmigen/hdl/ir.py
nmigen/hdl/xfrm.py
nmigen/test/test_hdl_ir.py
nmigen/test/test_sim.py

index 9a21bad6a099bcd107012036e2a8620b3b80578d..f4c5cd5cc88ac615bc9d0e0e42a75fe59417fd60 100644 (file)
@@ -211,7 +211,7 @@ class TemplatedPlatform(Platform):
         def emit_design(backend):
             return {"rtlil": rtlil, "verilog": verilog}[backend].convert(
                 fragment, name=name, platform=self, ports=list(self.iter_ports()),
-                ensure_sync_exists=False)
+                missing_domain=lambda name: None)
 
         def emit_commands(format):
             commands = []
index ad2f588b0e2d5e8e0d8c40cdc89294859396eadb..29e5daa41da4c71b61a22973889b5a48937064a7 100644 (file)
@@ -1,6 +1,7 @@
 import warnings
 
 from ...hdl.ir import Fragment
+from ...hdl.cd import ClockDomain
 from ...back import verilog
 from .conv_output import ConvOutput
 
@@ -16,11 +17,14 @@ def convert(fi, ios=None, name="top", special_overrides=dict(),
                       DeprecationWarning, stacklevel=1)
     # TODO: attr_translate
 
+    def missing_domain(name):
+        if create_clock_domains:
+            return ClockDomain(name)
     v_output = verilog.convert(
         fragment=Fragment.get(fi.get_fragment(), platform=None),
         name=name,
         ports=ios or (),
-        ensure_sync_exists=create_clock_domains
+        missing_domain=missing_domain
     )
     output = ConvOutput()
     output.set_main_source(v_output)
index 009492f3631b87ec4bc058a2b9abde86cf7aaaf2..9a3a5aa20dcfddb9c09969cce06476089671d1d6 100644 (file)
@@ -1,6 +1,7 @@
 import functools
 import inspect
 from collections.abc import Iterable
+from ...hdl.cd import ClockDomain
 from ...back.pysim import *
 
 
@@ -18,6 +19,7 @@ def run_simulation(fragment_or_module, generators, clocks={"sync": 10}, vcd_name
 
     if not isinstance(generators, dict):
         generators = {"sync": generators}
+        fragment.domains += ClockDomain("sync")
 
     with Simulator(fragment, vcd_file=open(vcd_name, "w") if vcd_name else None) as sim:
         for domain, period in clocks.items():
index bb2cd0ec936465064624127ac712312c46d9f88b..805c31c4f90a37059c561a480687a2bea93e894f 100644 (file)
@@ -351,14 +351,19 @@ class Fragment:
 
             subfrag._propagate_domains_down()
 
-    def _propagate_domains(self, ensure_sync_exists):
+    def _propagate_domains(self, missing_domain):
+        from .xfrm import DomainCollector
+
         self._propagate_domains_up()
-        if ensure_sync_exists and not self.domains:
-            cd_sync = ClockDomain()
-            self.add_domains(cd_sync)
-            new_domains = (cd_sync,)
-        else:
-            new_domains = ()
+        new_domains = []
+        for domain_name in DomainCollector()(self):
+            if domain_name is None:
+                continue
+            if domain_name not in self.domains:
+                domain = missing_domain(domain_name)
+                if domain is not None:
+                    self.add_domains(domain)
+                    new_domains.append(domain)
         self._propagate_domains_down()
         return new_domains
 
@@ -513,11 +518,11 @@ class Fragment:
             else:
                 self.add_ports(sig, dir="i")
 
-    def prepare(self, ports=None, ensure_sync_exists=True):
+    def prepare(self, ports=None, missing_domain=lambda name: ClockDomain(name)):
         from .xfrm import SampleLowerer
 
         fragment = SampleLowerer()(self)
-        new_domains = fragment._propagate_domains(ensure_sync_exists)
+        new_domains = fragment._propagate_domains(missing_domain)
         fragment._resolve_hierarchy_conflicts()
         fragment = fragment._insert_domain_resets()
         fragment = fragment._lower_domain_signals()
index 25f282566f3139b58b7c774f2aee1a0e3f7487f1..8d853a6ef4969070833558446de1091389708954 100644 (file)
@@ -15,7 +15,7 @@ __all__ = ["ValueVisitor", "ValueTransformer",
            "StatementVisitor", "StatementTransformer",
            "FragmentTransformer",
            "TransformedElaboratable",
-           "DomainRenamer", "DomainLowerer",
+           "DomainCollector", "DomainRenamer", "DomainLowerer",
            "SampleDomainInjector", "SampleLowerer",
            "SwitchCleaner", "LHSGroupAnalyzer", "LHSGroupFilter",
            "ResetInserter", "CEInserter"]
@@ -325,6 +325,85 @@ class TransformedElaboratable(Elaboratable):
         return fragment
 
 
+class DomainCollector(ValueVisitor, StatementVisitor):
+    def __init__(self):
+        self.domains = set()
+
+    def on_ignore(self, value):
+        pass
+
+    on_Const = on_ignore
+    on_AnyConst = on_ignore
+    on_AnySeq = on_ignore
+    on_Signal = on_ignore
+
+    def on_ClockSignal(self, value):
+        self.domains.add(value.domain)
+
+    def on_ResetSignal(self, value):
+        self.domains.add(value.domain)
+
+    on_Record = on_ignore
+
+    def on_Operator(self, value):
+        for o in value.operands:
+            self.on_value(o)
+
+    def on_Slice(self, value):
+        self.on_value(value.value)
+
+    def on_Part(self, value):
+        self.on_value(value.value)
+        self.on_value(value.offset)
+
+    def on_Cat(self, value):
+        for o in value.parts:
+            self.on_value(o)
+
+    def on_Repl(self, value):
+        self.on_value(value.value)
+
+    def on_ArrayProxy(self, value):
+        for elem in value._iter_as_values():
+            self.on_value(elem)
+        self.on_value(value.index)
+
+    def on_Sample(self, value):
+        self.on_value(value.value)
+
+    def on_Assign(self, stmt):
+        self.on_value(stmt.lhs)
+        self.on_value(stmt.rhs)
+
+    def on_Assert(self, stmt):
+        self.on_value(stmt.test)
+
+    def on_Assume(self, stmt):
+        self.on_value(stmt.test)
+
+    def on_Switch(self, stmt):
+        self.on_value(stmt.test)
+        for stmts in stmt.cases.values():
+            self.on_statement(stmts)
+
+    def on_statements(self, stmts):
+        for stmt in stmts:
+            self.on_statement(stmt)
+
+    def on_fragment(self, fragment):
+        if isinstance(fragment, Instance):
+            for name, (value, dir) in fragment.named_ports.items():
+                self.on_value(value)
+        self.on_statements(fragment.statements)
+        self.domains.update(fragment.drivers.keys())
+        for subfragment, name in fragment.subfragments:
+            self.on_fragment(subfragment)
+
+    def __call__(self, fragment):
+        self.on_fragment(fragment)
+        return self.domains
+
+
 class DomainRenamer(FragmentTransformer, ValueTransformer, StatementTransformer):
     def __init__(self, domain_map):
         if isinstance(domain_map, str):
index 439b791c9819f5a44c61c7115daf3ebb1332dd8d..f8efd8c34af479e4730b25fa786666e67360f88c 100644 (file)
@@ -376,16 +376,18 @@ class FragmentDomainsTestCase(FHDLTestCase):
         f1.add_domains(cd)
         f1.add_subfragment(f2)
 
-        f1._propagate_domains(ensure_sync_exists=False)
+        f1._propagate_domains(missing_domain=lambda name: None)
         self.assertEqual(f1.domains, {"cd": cd})
         self.assertEqual(f2.domains, {"cd": cd})
 
-    def test_propagate_ensure_sync(self):
+    def test_propagate_create_missing(self):
+        s1 = Signal()
         f1 = Fragment()
+        f1.add_driver(s1, "sync")
         f2 = Fragment()
         f1.add_subfragment(f2)
 
-        f1._propagate_domains(ensure_sync_exists=True)
+        f1._propagate_domains(missing_domain=lambda name: ClockDomain(name))
         self.assertEqual(f1.domains.keys(), {"sync"})
         self.assertEqual(f2.domains.keys(), {"sync"})
         self.assertEqual(f1.domains["sync"], f2.domains["sync"])
@@ -661,7 +663,7 @@ class InstanceTestCase(FHDLTestCase):
         f = Fragment()
         f.add_subfragment(Instance("foo", o_O=s[0]))
         f.add_subfragment(Instance("foo", o_O=s[1]))
-        fp = f.prepare(ports=[s], ensure_sync_exists=False)
+        fp = f.prepare(ports=[s], missing_domain=lambda name: None)
         self.assertEqual(fp.ports, SignalDict([
             (s, "o"),
         ]))
index 2a3d93d34ab45f1c1f76471017de32a5774234a8..d796e8817909279b720e1f3a9adc6813ac65bb4b 100644 (file)
@@ -385,7 +385,10 @@ class SimulatorIntegrationTestCase(FHDLTestCase):
             sim.add_process(process)
 
     def test_run_until(self):
-        with self.assertSimulation(Module(), deadline=100e-6) as sim:
+        m = Module()
+        s = Signal()
+        m.d.sync += s.eq(0)
+        with self.assertSimulation(m, deadline=100e-6) as sim:
             sim.add_clock(1e-6)
             def process():
                 for _ in range(101):
@@ -401,7 +404,10 @@ class SimulatorIntegrationTestCase(FHDLTestCase):
                 sim.add_process(1)
 
     def test_add_clock_wrong(self):
-        with self.assertSimulation(Module()) as sim:
+        m = Module()
+        s = Signal()
+        m.d.sync += s.eq(0)
+        with self.assertSimulation(m) as sim:
             sim.add_clock(1)
             with self.assertRaises(ValueError,
                     msg="Domain 'sync' already has a clock driving it"):