hdl.cd: add negedge clock domains.
authorwhitequark <cz@m-labs.hk>
Sat, 31 Aug 2019 22:05:48 +0000 (22:05 +0000)
committerwhitequark <cz@m-labs.hk>
Sat, 31 Aug 2019 22:05:48 +0000 (22:05 +0000)
Fixes #185.

nmigen/back/pysim.py
nmigen/back/rtlil.py
nmigen/hdl/cd.py
nmigen/test/test_hdl_cd.py

index 3c73eaee9ebcc4e3ee629f5e5ed5917b9908f6a7..25b65a70e100f2fe9a0da4a4d1663438b532ed49 100644 (file)
@@ -364,6 +364,7 @@ class Simulator:
         self._slot_signals    = list()        # int/slot -> Signal
 
         self._domains         = list()        # [ClockDomain]
+        self._clk_edges       = dict()        # ClockDomain -> int/edge
         self._domain_triggers = list()        # int/slot -> ClockDomain
 
         self._signals         = SignalSet()   # {Signal}
@@ -488,6 +489,7 @@ class Simulator:
                     add_fragment(subfragment, (*scope, name))
         add_fragment(root_fragment, scope=("top",))
         self._domains = list(domains)
+        self._clk_edges = {domain: 1 if domain.clk_edge == "pos" else 0 for domain in domains}
 
         def add_signal(signal):
             if signal not in self._signals:
@@ -642,7 +644,8 @@ class Simulator:
             return
 
         # If the signal is a clock that triggers synchronous logic, record that fact.
-        if new == 1 and self._domain_triggers[signal_slot] is not None:
+        if (self._domain_triggers[signal_slot] is not None and
+                self._clk_edges[self._domain_triggers[signal_slot]] == new):
             domains.add(self._domain_triggers[signal_slot])
 
         if self._vcd_writer:
index 73b05f33a3d73fd74cb523601d29cf64798191a3..f0c156367d769c57b22570abdda205dfcbaa87f2 100644 (file)
@@ -884,8 +884,8 @@ def _convert_fragment(builder, fragment, hierarchy):
 
                 # For every signal in every sync domain, assign \sig to \sig$next. The sensitivity
                 # list, however, differs between domains: for domains with sync reset, it is
-                # `posedge clk`, for sync domains with async reset it is `posedge clk or
-                # posedge rst`.
+                # `[pos|neg]edge clk`, for sync domains with async reset it is `[pos|neg]edge clk
+                # or posedge rst`.
                 for domain, signals in fragment.drivers.items():
                     if domain is None:
                         continue
@@ -897,7 +897,7 @@ def _convert_fragment(builder, fragment, hierarchy):
                     cd = fragment.domains[domain]
 
                     triggers = []
-                    triggers.append(("posedge", compiler_state.resolve_curr(cd.clk)))
+                    triggers.append((cd.clk_edge + "edge", compiler_state.resolve_curr(cd.clk)))
                     if cd.async_reset:
                         triggers.append(("posedge", compiler_state.resolve_curr(cd.rst)))
 
index 6813a9d69474f9107b7ebe7f87185ce04ca80d01..0f43d6db55c0a296e9643c8847536923f298d231 100644 (file)
@@ -45,7 +45,8 @@ class ClockDomain:
         else:
             return "{}_{}".format(domain_name, signal_name)
 
-    def __init__(self, name=None, reset_less=False, async_reset=False, local=False):
+    def __init__(self, name=None, *, clk_edge="pos", reset_less=False, async_reset=False,
+                 local=False):
         if name is None:
             try:
                 name = tracer.get_var_name()
@@ -55,9 +56,16 @@ class ClockDomain:
             name = name[3:]
         if name == "comb":
             raise ValueError("Domain '{}' may not be clocked".format(name))
+
+        if clk_edge not in ("pos", "neg"):
+            raise ValueError("Domain clock edge must be one of 'pos' or 'neg', not {!r}"
+                             .format(clk_edge))
+
         self.name = name
 
         self.clk = Signal(name=self._name_for(name, "clk"), src_loc_at=1)
+        self.clk_edge = clk_edge
+
         if reset_less:
             self.rst = None
         else:
index da04e2c5a504b1a59a102430622e36ba20a57f81..60edc3b79343effda07c85e0c34a58835144981e 100644 (file)
@@ -23,6 +23,19 @@ class ClockDomainTestCase(FHDLTestCase):
         cd_reset = ClockDomain(local=True)
         self.assertEqual(cd_reset.local, True)
 
+    def test_edge(self):
+        sync = ClockDomain()
+        self.assertEqual(sync.clk_edge, "pos")
+        sync = ClockDomain(clk_edge="pos")
+        self.assertEqual(sync.clk_edge, "pos")
+        sync = ClockDomain(clk_edge="neg")
+        self.assertEqual(sync.clk_edge, "neg")
+
+    def test_edge_wrong(self):
+        with self.assertRaises(ValueError,
+                msg="Domain clock edge must be one of 'pos' or 'neg', not 'xxx'"):
+            ClockDomain("sync", clk_edge="xxx")
+
     def test_with_reset(self):
         pix = ClockDomain()
         self.assertIsNotNone(pix.clk)