hdl: make ClockSignal and ResetSignal usable on LHS.
authorwhitequark <cz@m-labs.hk>
Mon, 14 Jan 2019 15:38:16 +0000 (15:38 +0000)
committerwhitequark <cz@m-labs.hk>
Mon, 14 Jan 2019 15:38:16 +0000 (15:38 +0000)
Fixes #8.

examples/por.py [new file with mode: 0644]
nmigen/hdl/ast.py
nmigen/hdl/xfrm.py
nmigen/test/test_hdl_dsl.py
nmigen/test/test_hdl_xfrm.py

diff --git a/examples/por.py b/examples/por.py
new file mode 100644 (file)
index 0000000..6c3c40f
--- /dev/null
@@ -0,0 +1,19 @@
+from nmigen import *
+from nmigen.cli import main
+
+
+m = Module()
+cd_por  = ClockDomain(reset_less=True)
+cd_sync = ClockDomain()
+m.domains += cd_por, cd_sync
+
+delay = Signal(max=255, reset=255)
+with m.If(delay != 0):
+    m.d.por += delay.eq(delay - 1)
+m.d.comb += [
+    ClockSignal().eq(cd_por.clk),
+    ResetSignal().eq(delay == 0),
+]
+
+if __name__ == "__main__":
+    main(m.lower(platform=None), ports=[cd_por.clk])
index c6dee0303ef6dde398cde12e2026621008d6ddd7..6e95611a11886715e5061e9b7e8b98ce42e243e4 100644 (file)
@@ -635,6 +635,9 @@ class ClockSignal(Value):
     def shape(self):
         return 1, False
 
+    def _lhs_signals(self):
+        return ValueSet((self,))
+
     def _rhs_signals(self):
         raise NotImplementedError("ClockSignal must be lowered to a concrete signal") # :nocov:
 
@@ -666,6 +669,9 @@ class ResetSignal(Value):
     def shape(self):
         return 1, False
 
+    def _lhs_signals(self):
+        return ValueSet((self,))
+
     def _rhs_signals(self):
         raise NotImplementedError("ResetSignal must be lowered to a concrete signal") # :nocov:
 
@@ -1038,6 +1044,8 @@ class ValueKey:
             return hash(self.value.value)
         elif isinstance(self.value, Signal):
             return hash(self.value.duid)
+        elif isinstance(self.value, (ClockSignal, ResetSignal)):
+            return hash(self.value.domain)
         elif isinstance(self.value, Operator):
             return hash((self.value.op, tuple(ValueKey(o) for o in self.value.operands)))
         elif isinstance(self.value, Slice):
@@ -1064,6 +1072,8 @@ class ValueKey:
             return self.value.value == other.value.value
         elif isinstance(self.value, Signal):
             return self.value is other.value
+        elif isinstance(self.value, (ClockSignal, ResetSignal)):
+            return self.value.domain == other.value.domain
         elif isinstance(self.value, Operator):
             return (self.value.op == other.value.op and
                     len(self.value.operands) == len(other.value.operands) and
@@ -1123,22 +1133,28 @@ class ValueSet(_MappedKeySet):
 
 class SignalKey:
     def __init__(self, signal):
-        if type(signal) is not Signal:
+        if type(signal) is Signal:
+            self._intern = (0, signal.duid)
+        elif type(signal) is ClockSignal:
+            self._intern = (1, signal.domain)
+        elif type(signal) is ResetSignal:
+            self._intern = (2, signal.domain)
+        else:
             raise TypeError("Object '{!r}' is not an nMigen signal".format(signal))
         self.signal = signal
 
     def __hash__(self):
-        return hash(self.signal.duid)
+        return hash(self._intern)
 
     def __eq__(self, other):
         if type(other) is not SignalKey:
             return False
-        return self.signal is other.signal
+        return self._intern == other._intern
 
     def __lt__(self, other):
         if type(other) is not SignalKey:
             raise TypeError("Object '{!r}' cannot be compared to a SignalKey".format(signal))
-        return self.signal.duid < other.signal.duid
+        return self._intern < other._intern
 
     def __repr__(self):
         return "<{}.SignalKey {!r}>".format(__name__, self.signal)
index d7c142a48acbf696cd7e8da1e31bf6dad0b4b779..20a325ce5fe830be2e127e2b58b8743c14858916 100644 (file)
@@ -293,6 +293,10 @@ class DomainLowerer(FragmentTransformer, ValueTransformer, StatementTransformer)
                               .format(context, domain))
         return self.domains[domain]
 
+    def map_drivers(self, fragment, new_fragment):
+        for domain, signal in fragment.iter_drivers():
+            new_fragment.add_driver(self.on_value(signal), domain)
+
     def on_ClockSignal(self, value):
         cd = self._resolve(value.domain, value)
         return cd.clk
index 31753fd046f3eb363709ef172c0d1745fdb849b2..bbcae25426ca66729a6f7594e8e9b7dcdf00e2ef 100644 (file)
@@ -95,6 +95,24 @@ class DSLTestCase(FHDLTestCase):
                 msg="'Module' object has no attribute 'nonexistentattr'"):
             m.nonexistentattr
 
+    def test_clock_signal(self):
+        m = Module()
+        m.d.comb += ClockSignal("pix").eq(ClockSignal())
+        self.assertRepr(m._statements, """
+        (
+            (eq (clk pix) (clk sync))
+        )
+        """)
+
+    def test_reset_signal(self):
+        m = Module()
+        m.d.comb += ResetSignal("pix").eq(1)
+        self.assertRepr(m._statements, """
+        (
+            (eq (rst pix) (const 1'd1))
+        )
+        """)
+
     def test_If(self):
         m = Module()
         with m.If(self.s1):
index 88bd78f7852e19003329648fe57ab8ffe5b030fa..4483e7cb46fe017d3ae0626b4517a5b8f9bb1860 100644 (file)
@@ -135,6 +135,18 @@ class DomainLowererTestCase(FHDLTestCase):
         )
         """)
 
+    def test_lower_drivers(self):
+        pix = ClockDomain()
+        f = Fragment()
+        f.add_driver(ClockSignal("pix"), None)
+        f.add_driver(ResetSignal("pix"), "sync")
+
+        f = DomainLowerer({"pix": pix})(f)
+        self.assertEqual(f.drivers, {
+            None: SignalSet((pix.clk,)),
+            "sync": SignalSet((pix.rst,))
+        })
+
     def test_lower_wrong_domain(self):
         sync = ClockDomain()
         f = Fragment()