From 2e206220462c67aa6ae97f7515a2191440fd61b3 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sat, 31 Aug 2019 22:05:48 +0000 Subject: [PATCH] hdl.cd: add negedge clock domains. Fixes #185. --- nmigen/back/pysim.py | 5 ++++- nmigen/back/rtlil.py | 6 +++--- nmigen/hdl/cd.py | 10 +++++++++- nmigen/test/test_hdl_cd.py | 13 +++++++++++++ 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/nmigen/back/pysim.py b/nmigen/back/pysim.py index 3c73eae..25b65a7 100644 --- a/nmigen/back/pysim.py +++ b/nmigen/back/pysim.py @@ -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: diff --git a/nmigen/back/rtlil.py b/nmigen/back/rtlil.py index 73b05f3..f0c1563 100644 --- a/nmigen/back/rtlil.py +++ b/nmigen/back/rtlil.py @@ -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))) diff --git a/nmigen/hdl/cd.py b/nmigen/hdl/cd.py index 6813a9d..0f43d6d 100644 --- a/nmigen/hdl/cd.py +++ b/nmigen/hdl/cd.py @@ -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: diff --git a/nmigen/test/test_hdl_cd.py b/nmigen/test/test_hdl_cd.py index da04e2c..60edc3b 100644 --- a/nmigen/test/test_hdl_cd.py +++ b/nmigen/test/test_hdl_cd.py @@ -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) -- 2.30.2