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