import collections from itertools import combinations from migen.fhdl.structure import * from migen.fhdl.specials import Special from migen.fhdl.tools import flat_iteration, rename_clock_domain class FinalizeError(Exception): pass def _flat_list(e): if isinstance(e, collections.Iterable): return flat_iteration(e) else: return [e] class _ModuleProxy: def __init__(self, fm): object.__setattr__(self, "_fm", fm) class _ModuleComb(_ModuleProxy): def __iadd__(self, other): self._fm._fragment.comb += _flat_list(other) return self def _cd_append(d, key, statements): try: l = d[key] except KeyError: l = [] d[key] = l l += _flat_list(statements) class _ModuleSyncCD: def __init__(self, fm, cd): self._fm = fm self._cd = cd def __iadd__(self, other): _cd_append(self._fm._fragment.sync, self._cd, other) return self class _ModuleSync(_ModuleProxy): def __iadd__(self, other): _cd_append(self._fm._fragment.sync, "sys", other) return self def __getattr__(self, name): return _ModuleSyncCD(self._fm, name) def __setattr__(self, name, value): if not isinstance(value, _ModuleSyncCD): raise AttributeError("Attempted to assign sync property - use += instead") # _ModuleForwardAttr enables user classes to do e.g.: # self.subm.foobar = SomeModule() # and then access the submodule with self.foobar. class _ModuleForwardAttr: def __setattr__(self, name, value): self.__iadd__(value) setattr(self._fm, name, value) class _ModuleSpecials(_ModuleProxy, _ModuleForwardAttr): def __iadd__(self, other): self._fm._fragment.specials |= set(_flat_list(other)) return self class _ModuleSubmodules(_ModuleProxy): def __setattr__(self, name, value): self._fm._submodules += [(name, e, dict()) for e in _flat_list(value)] setattr(self._fm, name, value) def __iadd__(self, other): self._fm._submodules += [(None, e, dict()) 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: def get_fragment(self): assert(not self._get_fragment_called) self._get_fragment_called = True self.finalize() return self._fragment def __getattr__(self, name): if name == "comb": return _ModuleComb(self) elif name == "sync": return _ModuleSync(self) elif name == "specials": 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) elif name == "finalized": self.finalized = False return self.finalized elif name == "_fragment": try: sim = [self.do_simulation] except AttributeError: sim = [] self._fragment = Fragment(sim=sim) return self._fragment 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 else: raise AttributeError("'"+self.__class__.__name__+"' object has no attribute '"+name+"'") def __setattr__(self, name, value): 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 add_submodule(self, submodule, cd_remapping=dict(), name=None): if isinstance(cd_remapping, str): cd_remapping = {"sys": cd_remapping} if name is not None: setattr(self, name, submodule) self._submodules.append((name, submodule, cd_remapping)) def _collect_submodules(self): r = [] for name, submodule, cd_remapping in self._submodules: f = submodule.get_fragment() for old, new in cd_remapping.items(): rename_clock_domain(f, old, new) r.append((name, f)) self._submodules = [] return r def finalize(self): if not self.finalized: self.finalized = True # finalize existing submodules before finalizing us subfragments = self._collect_submodules() self.do_finalize() # 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: rename_clock_domain(f, cd.name, mod_name + "_" + cd.name) # sum subfragments for mod_name, f in subfragments: self._fragment += f def do_finalize(self): pass