diff --git a/migen/fhdl/module.py b/migen/fhdl/module.py index 4bc42f1ff..2d52eba93 100644 --- a/migen/fhdl/module.py +++ b/migen/fhdl/module.py @@ -1,4 +1,5 @@ import collections +from itertools import combinations from migen.fhdl.structure import * from migen.fhdl.specials import Special @@ -64,9 +65,18 @@ class _ModuleSpecials(_ModuleProxy, _ModuleForwardAttr): self._fm._fragment.specials |= set(_flat_list(other)) return self -class _ModuleSubmodules(_ModuleProxy, _ModuleForwardAttr): +class _ModuleSubmodules(_ModuleProxy): + def __setattr__(self, name, value): + self._fm._submodules += [(name, e) for e in _flat_list(value)] + setattr(self._fm, name, value) + def __iadd__(self, other): - self._fm._submodules += _flat_list(other) + self._fm._submodules += [(None, e) for e in _flat_list(other)] + return self + +class _ModuleClockDomains(_ModuleProxy, _ModuleForwardAttr): + def __iadd__(self, other): + self._fm._fragment.clock_domains += _flat_list(other) return self class Module: @@ -85,6 +95,8 @@ class Module: return _ModuleSpecials(self) elif name == "submodules": return _ModuleSubmodules(self) + elif name == "clock_domains": + return _ModuleClockDomains(self) # hack to have initialized regular attributes without using __init__ # (which would require derived classes to call it) @@ -101,6 +113,9 @@ class Module: elif name == "_submodules": self._submodules = [] return self._submodules + elif name == "_clock_domains": + self._clock_domains = [] + return self._clock_domains elif name == "_get_fragment_called": self._get_fragment_called = False return self._get_fragment_called @@ -109,21 +124,44 @@ class Module: raise AttributeError("'"+self.__class__.__name__+"' object has no attribute '"+name+"'") def __setattr__(self, name, value): - if name in ["comb", "sync", "specials", "submodules"]: + if name in ["comb", "sync", "specials", "submodules", "clock_domains"]: if not isinstance(value, _ModuleProxy): raise AttributeError("Attempted to assign special Module property - use += instead") else: object.__setattr__(self, name, value) + def _collect_submodules(self): + r = [(name, submodule.get_fragment()) for name, submodule in self._submodules] + self._submodules = [] + return r + def finalize(self): if not self.finalized: self.finalized = True - for submodule in self._submodules: - self._fragment += submodule.get_fragment() - self._submodules = [] + # finalize existing submodules before finalizing us + subfragments = self._collect_submodules() self.do_finalize() - for submodule in self._submodules: - self._fragment += submodule.get_fragment() + # finalize submodules created by do_finalize + subfragments += self._collect_submodules() + # resolve clock domain name conflicts + needs_renaming = set() + for (mod_name1, f1), (mod_name2, f2) in combinations(subfragments, 2): + f1_names = set(cd.name for cd in f1.clock_domains) + f2_names = set(cd.name for cd in f2.clock_domains) + common_names = f1_names & f2_names + if common_names: + if mod_name1 is None or mod_name2 is None: + raise ValueError("Multiple submodules with local clock domains cannot be anonymous") + if mod_name1 == mod_name2: + raise ValueError("Multiple submodules with local clock domains cannot have the same name") + needs_renaming |= common_names + for mod_name, f in subfragments: + for cd in f.clock_domains: + if cd.name in needs_renaming: + f.rename_clock_domain(cd.name, mod_name + "_" + cd.name) + # sum subfragments + for mod_name, f in subfragments: + self._fragment += f def do_finalize(self): pass diff --git a/migen/fhdl/structure.py b/migen/fhdl/structure.py index 8052fdffb..056d602d8 100644 --- a/migen/fhdl/structure.py +++ b/migen/fhdl/structure.py @@ -229,11 +229,37 @@ class Array(list): else: return list.__getitem__(self, key) +class ClockDomain: + def __init__(self, name=None): + self.name = tracer.get_obj_var_name(name) + if self.name is None: + raise ValueError("Cannot extract clock domain name from code, need to specify.") + if len(self.name) > 3 and self.name[:3] == "cd_": + self.name = self.name[3:] + self.clk = Signal(name_override=self.name + "_clk") + self.rst = Signal(name_override=self.name + "_rst") + + def rename(self, new_name): + self.name = new_name + self.clk.name_override = new_name + "_clk" + self.rst.name_override = new_name + "_rst" + +class _ClockDomainList(list): + def __getitem__(self, key): + if isinstance(key, str): + for cd in self: + if cd.name == key: + return cd + raise KeyError(key) + else: + return list.__getitem__(self, key) + class Fragment: - def __init__(self, comb=None, sync=None, specials=None, sim=None): + def __init__(self, comb=None, sync=None, specials=None, clock_domains=None, sim=None): if comb is None: comb = [] if sync is None: sync = dict() if specials is None: specials = set() + if clock_domains is None: clock_domains = _ClockDomainList() if sim is None: sim = [] if isinstance(sync, list): @@ -242,6 +268,7 @@ class Fragment: self.comb = comb self.sync = sync self.specials = set(specials) + self.clock_domains = _ClockDomainList(clock_domains) self.sim = sim def __add__(self, other): @@ -252,27 +279,22 @@ class Fragment: newsync[k].extend(v) return Fragment(self.comb + other.comb, newsync, self.specials | other.specials, + self.clock_domains + other.clock_domains, self.sim + other.sim) def rename_clock_domain(self, old, new): - self.sync["new"] = self.sync["old"] - del self.sync["old"] + self.sync[new] = self.sync[old] + del self.sync[old] for special in self.specials: special.rename_clock_domain(old, new) + try: + cd = self.clock_domains[old] + except KeyError: + pass + else: + cd.rename(new) def call_sim(self, simulator): for s in self.sim: if simulator.cycle_counter >= 0 or (hasattr(s, "initialize") and s.initialize): s(simulator) - -class ClockDomain: - def __init__(self, n1, n2=None): - self.name = n1 - if n2 is None: - n_clk = n1 + "_clk" - n_rst = n1 + "_rst" - else: - n_clk = n1 - n_rst = n2 - self.clk = Signal(name_override=n_clk) - self.rst = Signal(name_override=n_rst) diff --git a/migen/fhdl/tools.py b/migen/fhdl/tools.py index f7b50fd7d..1dd1ba57e 100644 --- a/migen/fhdl/tools.py +++ b/migen/fhdl/tools.py @@ -68,6 +68,8 @@ def list_clock_domains(f): r = set(f.sync.keys()) for special in f.specials: r |= special.get_clock_domains() + for cd in f.clock_domains: + r.add(cd.name) return r def is_variable(node): diff --git a/migen/fhdl/verilog.py b/migen/fhdl/verilog.py index 795fcf006..7d2f5c55a 100644 --- a/migen/fhdl/verilog.py +++ b/migen/fhdl/verilog.py @@ -200,16 +200,16 @@ def _printcomb(f, ns, display_run): r += "\n" return r -def _insert_resets(f, clock_domains): +def _insert_resets(f): newsync = dict() for k, v in f.sync.items(): - newsync[k] = insert_reset(clock_domains[k].rst, v) + newsync[k] = insert_reset(f.clock_domains[k].rst, v) f.sync = newsync -def _printsync(f, ns, clock_domains): +def _printsync(f, ns): r = "" for k, v in sorted(f.sync.items(), key=itemgetter(0)): - r += "always @(posedge " + ns.get_name(clock_domains[k].clk) + ") begin\n" + r += "always @(posedge " + ns.get_name(f.clock_domains[k].clk) + ") begin\n" r += _printnode(ns, _AT_SIGNAL, 1, v) r += "end\n\n" return r @@ -256,7 +256,6 @@ def _printinit(f, ios, ns): return r def convert(f, ios=None, name="top", - clock_domains=None, return_ns=False, special_overrides=dict(), display_run=False): @@ -264,18 +263,18 @@ def convert(f, ios=None, name="top", f = f.get_fragment() if ios is None: ios = set() - if clock_domains is None: - clock_domains = dict() - for d in list_clock_domains(f): - cd = ClockDomain(d) - clock_domains[d] = cd - ios.add(cd.clk) - ios.add(cd.rst) - f = lower_arrays(f) + f = lower_arrays(f) # this also copies f fs, lowered_specials = _lower_specials(special_overrides, f.specials) f += fs - _insert_resets(f, clock_domains) + for cd_name in list_clock_domains(f): + try: + f.clock_domains[cd_name] + except KeyError: + cd = ClockDomain(cd_name) + f.clock_domains.append(cd) + ios |= {cd.clk, cd.rst} + _insert_resets(f) ns = build_namespace(list_signals(f) \ | list_special_ios(f, True, True, True) \ @@ -284,8 +283,8 @@ def convert(f, ios=None, name="top", r = "/* Machine-generated using Migen */\n" r += _printheader(f, ios, name, ns) r += _printcomb(f, ns, display_run) - r += _printsync(f, ns, clock_domains) - r += _printspecials(special_overrides, f.specials - lowered_specials, ns, clock_domains) + r += _printsync(f, ns) + r += _printspecials(special_overrides, f.specials - lowered_specials, ns, f.clock_domains) r += _printinit(f, ios, ns) r += "endmodule\n"