Make ClockDomains part of fragments
authorSebastien Bourdeauducq <sebastien@milkymist.org>
Fri, 15 Mar 2013 17:17:33 +0000 (18:17 +0100)
committerSebastien Bourdeauducq <sebastien@milkymist.org>
Fri, 15 Mar 2013 17:17:33 +0000 (18:17 +0100)
migen/fhdl/module.py
migen/fhdl/structure.py
migen/fhdl/tools.py
migen/fhdl/verilog.py

index 4bc42f1ffb07d45f2ed52d830b5a26f8e66bf9bc..2d52eba934fa9bfab3409d55162a0f2d037b52b7 100644 (file)
@@ -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
index 8052fdffbbc700e473247c8dab0f8ecd2643ec15..056d602d8c86dc5e2e744e66812491e5c6f07a9a 100644 (file)
@@ -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)
index f7b50fd7dbdade8172c33624d80125e6eba983be..1dd1ba57ea4a99c4ef782801d072bf63f8cfe54d 100644 (file)
@@ -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):
index 795fcf00627922096c5c24a784509938a454bfff..7d2f5c55a2cc014c5c81a83986813e2823e8717a 100644 (file)
@@ -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"