Make ClockDomains part of fragments
This commit is contained in:
parent
5adab17efa
commit
bd8bbd9305
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
Loading…
Reference in New Issue