fhdl/module: use r.append() in _collect_submodules
[litex.git] / migen / fhdl / module.py
1 import collections
2 from itertools import combinations
3
4 from migen.util.misc import flat_iteration
5 from migen.fhdl.structure import *
6 from migen.fhdl.structure import _Fragment
7 from migen.fhdl.tools import rename_clock_domain
8 from migen.sim.upper import gen_sim, proxy_sim
9
10 class FinalizeError(Exception):
11 pass
12
13 def _flat_list(e):
14 if isinstance(e, collections.Iterable):
15 return flat_iteration(e)
16 else:
17 return [e]
18
19 class _ModuleProxy:
20 def __init__(self, fm):
21 object.__setattr__(self, "_fm", fm)
22
23 class _ModuleComb(_ModuleProxy):
24 def __iadd__(self, other):
25 self._fm._fragment.comb += _flat_list(other)
26 return self
27
28 def _cd_append(d, key, statements):
29 try:
30 l = d[key]
31 except KeyError:
32 l = []
33 d[key] = l
34 l += _flat_list(statements)
35
36 class _ModuleSyncCD:
37 def __init__(self, fm, cd):
38 self._fm = fm
39 self._cd = cd
40
41 def __iadd__(self, other):
42 _cd_append(self._fm._fragment.sync, self._cd, other)
43 return self
44
45 class _ModuleSync(_ModuleProxy):
46 def __iadd__(self, other):
47 _cd_append(self._fm._fragment.sync, "sys", other)
48 return self
49
50 def __getattr__(self, name):
51 return _ModuleSyncCD(self._fm, name)
52
53 def __setattr__(self, name, value):
54 if not isinstance(value, _ModuleSyncCD):
55 raise AttributeError("Attempted to assign sync property - use += instead")
56
57 # _ModuleForwardAttr enables user classes to do e.g.:
58 # self.subm.foobar = SomeModule()
59 # and then access the submodule with self.foobar.
60 class _ModuleForwardAttr:
61 def __setattr__(self, name, value):
62 self.__iadd__(value)
63 setattr(self._fm, name, value)
64
65 class _ModuleSpecials(_ModuleProxy, _ModuleForwardAttr):
66 def __iadd__(self, other):
67 self._fm._fragment.specials |= set(_flat_list(other))
68 return self
69
70 class _ModuleSubmodules(_ModuleProxy):
71 def __setattr__(self, name, value):
72 self._fm._submodules += [(name, e) for e in _flat_list(value)]
73 setattr(self._fm, name, value)
74
75 def __iadd__(self, other):
76 self._fm._submodules += [(None, e) for e in _flat_list(other)]
77 return self
78
79 class _ModuleClockDomains(_ModuleProxy, _ModuleForwardAttr):
80 def __iadd__(self, other):
81 self._fm._fragment.clock_domains += _flat_list(other)
82 return self
83
84 class Module:
85 def get_fragment(self):
86 assert(not self._get_fragment_called)
87 self._get_fragment_called = True
88 self.finalize()
89 return self._fragment
90
91 def __getattr__(self, name):
92 if name == "comb":
93 return _ModuleComb(self)
94 elif name == "sync":
95 return _ModuleSync(self)
96 elif name == "specials":
97 return _ModuleSpecials(self)
98 elif name == "submodules":
99 return _ModuleSubmodules(self)
100 elif name == "clock_domains":
101 return _ModuleClockDomains(self)
102
103 # hack to have initialized regular attributes without using __init__
104 # (which would require derived classes to call it)
105 elif name == "finalized":
106 self.finalized = False
107 return self.finalized
108 elif name == "_fragment":
109 simf = None
110 try:
111 simf = self.do_simulation
112 except AttributeError:
113 try:
114 simg = self.gen_simulation
115 except AttributeError:
116 pass
117 else:
118 simf = gen_sim(simg)
119 if simf is not None:
120 simf = proxy_sim(self, simf)
121 sim = [] if simf is None else [simf]
122 self._fragment = _Fragment(sim=sim)
123 return self._fragment
124 elif name == "_submodules":
125 self._submodules = []
126 return self._submodules
127 elif name == "_clock_domains":
128 self._clock_domains = []
129 return self._clock_domains
130 elif name == "_get_fragment_called":
131 self._get_fragment_called = False
132 return self._get_fragment_called
133
134 else:
135 raise AttributeError("'"+self.__class__.__name__+"' object has no attribute '"+name+"'")
136
137 def __setattr__(self, name, value):
138 if name in ["comb", "sync", "specials", "submodules", "clock_domains"]:
139 if not isinstance(value, _ModuleProxy):
140 raise AttributeError("Attempted to assign special Module property - use += instead")
141 else:
142 object.__setattr__(self, name, value)
143
144 def _collect_submodules(self):
145 r = []
146 for name, submodule in self._submodules:
147 if not submodule._get_fragment_called:
148 r.append((name, submodule.get_fragment()))
149 return r
150
151 def finalize(self, *args, **kwargs):
152 if not self.finalized:
153 self.finalized = True
154 # finalize existing submodules before finalizing us
155 subfragments = self._collect_submodules()
156 self.do_finalize(*args, **kwargs)
157 # finalize submodules created by do_finalize
158 subfragments += self._collect_submodules()
159 # resolve clock domain name conflicts
160 needs_renaming = set()
161 for (mod_name1, f1), (mod_name2, f2) in combinations(subfragments, 2):
162 f1_names = set(cd.name for cd in f1.clock_domains)
163 f2_names = set(cd.name for cd in f2.clock_domains)
164 common_names = f1_names & f2_names
165 if common_names:
166 if mod_name1 is None or mod_name2 is None:
167 raise ValueError("Multiple submodules with local clock domains cannot be anonymous")
168 if mod_name1 == mod_name2:
169 raise ValueError("Multiple submodules with local clock domains cannot have the same name")
170 needs_renaming |= common_names
171 for mod_name, f in subfragments:
172 for cd in f.clock_domains:
173 if cd.name in needs_renaming:
174 rename_clock_domain(f, cd.name, mod_name + "_" + cd.name)
175 # sum subfragments
176 for mod_name, f in subfragments:
177 self._fragment += f
178
179 def do_finalize(self):
180 pass
181
182 def do_exit(self, *args, **kwargs):
183 for name, submodule in self._submodules:
184 submodule.do_exit(*args, **kwargs)