From c7d48f754b3c29552366d3baa4f96407f9e927fb Mon Sep 17 00:00:00 2001 From: whitequark Date: Mon, 14 Jan 2019 15:38:16 +0000 Subject: [PATCH] hdl: make ClockSignal and ResetSignal usable on LHS. Fixes #8. --- examples/por.py | 19 +++++++++++++++++++ nmigen/hdl/ast.py | 24 ++++++++++++++++++++---- nmigen/hdl/xfrm.py | 4 ++++ nmigen/test/test_hdl_dsl.py | 18 ++++++++++++++++++ nmigen/test/test_hdl_xfrm.py | 12 ++++++++++++ 5 files changed, 73 insertions(+), 4 deletions(-) create mode 100644 examples/por.py diff --git a/examples/por.py b/examples/por.py new file mode 100644 index 0000000..6c3c40f --- /dev/null +++ b/examples/por.py @@ -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]) diff --git a/nmigen/hdl/ast.py b/nmigen/hdl/ast.py index c6dee03..6e95611 100644 --- a/nmigen/hdl/ast.py +++ b/nmigen/hdl/ast.py @@ -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) diff --git a/nmigen/hdl/xfrm.py b/nmigen/hdl/xfrm.py index d7c142a..20a325c 100644 --- a/nmigen/hdl/xfrm.py +++ b/nmigen/hdl/xfrm.py @@ -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 diff --git a/nmigen/test/test_hdl_dsl.py b/nmigen/test/test_hdl_dsl.py index 31753fd..bbcae25 100644 --- a/nmigen/test/test_hdl_dsl.py +++ b/nmigen/test/test_hdl_dsl.py @@ -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): diff --git a/nmigen/test/test_hdl_xfrm.py b/nmigen/test/test_hdl_xfrm.py index 88bd78f..4483e7c 100644 --- a/nmigen/test/test_hdl_xfrm.py +++ b/nmigen/test/test_hdl_xfrm.py @@ -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() -- 2.30.2