5163d9227590e9e33326fc969a07027ac9330e24
[litex.git] / litex / gen / migen / build / generic_platform.py
1 import os
2
3 from migen.fhdl.structure import Signal
4 from migen.genlib.record import Record
5 from migen.genlib.io import CRG
6 from migen.fhdl import verilog, edif
7 from migen.build import tools
8
9
10 class ConstraintError(Exception):
11 pass
12
13
14 class Pins:
15 def __init__(self, *identifiers):
16 self.identifiers = []
17 for i in identifiers:
18 self.identifiers += i.split()
19
20 def __repr__(self):
21 return "{}('{}')".format(self.__class__.__name__,
22 " ".join(self.identifiers))
23
24
25 class IOStandard:
26 def __init__(self, name):
27 self.name = name
28
29 def __repr__(self):
30 return "{}('{}')".format(self.__class__.__name__, self.name)
31
32
33 class Drive:
34 def __init__(self, strength):
35 self.strength = strength
36
37 def __repr__(self):
38 return "{}('{}')".format(self.__class__.__name__, self.strength)
39
40
41 class Misc:
42 def __init__(self, misc):
43 self.misc = misc
44
45 def __repr__(self):
46 return "{}({})".format(self.__class__.__name__, repr(self.misc))
47
48
49 class Subsignal:
50 def __init__(self, name, *constraints):
51 self.name = name
52 self.constraints = list(constraints)
53
54 def __repr__(self):
55 return "{}('{}', {})".format(
56 self.__class__.__name__,
57 self.name,
58 ", ".join([repr(constr) for constr in self.constraints]))
59
60
61 class PlatformInfo:
62 def __init__(self, info):
63 self.info = info
64
65 def __repr__(self):
66 return "{}({})".format(self.__class__.__name__, repr(self.info))
67
68
69 def _lookup(description, name, number):
70 for resource in description:
71 if resource[0] == name and (number is None or resource[1] == number):
72 return resource
73 raise ConstraintError("Resource not found: {}:{}".format(name, number))
74
75
76 def _resource_type(resource):
77 t = None
78 for element in resource[2:]:
79 if isinstance(element, Pins):
80 assert(t is None)
81 t = len(element.identifiers)
82 elif isinstance(element, Subsignal):
83 if t is None:
84 t = []
85
86 assert(isinstance(t, list))
87 n_bits = None
88 for c in element.constraints:
89 if isinstance(c, Pins):
90 assert(n_bits is None)
91 n_bits = len(c.identifiers)
92
93 t.append((element.name, n_bits))
94
95 return t
96
97
98 class ConnectorManager:
99 def __init__(self, connectors):
100 self.connector_table = dict()
101 for connector in connectors:
102 cit = iter(connector)
103 conn_name = next(cit)
104 if isinstance(connector[1], str):
105 pin_list = []
106 for pins in cit:
107 pin_list += pins.split()
108 pin_list = [None if pin == "None" else pin for pin in pin_list]
109 elif isinstance(connector[1], dict):
110 pin_list = connector[1]
111 else:
112 raise ValueError("Unsupported pin list type {} for connector"
113 " {}".format(type(connector[1]), conn_name))
114 if conn_name in self.connector_table:
115 raise ValueError(
116 "Connector specified more than once: {}".format(conn_name))
117
118 self.connector_table[conn_name] = pin_list
119
120 def resolve_identifiers(self, identifiers):
121 r = []
122 for identifier in identifiers:
123 if ":" in identifier:
124 conn, pn = identifier.split(":")
125 if pn.isdigit():
126 pn = int(pn)
127
128 r.append(self.connector_table[conn][pn])
129 else:
130 r.append(identifier)
131
132 return r
133
134
135 def _separate_pins(constraints):
136 pins = None
137 others = []
138 for c in constraints:
139 if isinstance(c, Pins):
140 assert(pins is None)
141 pins = c.identifiers
142 else:
143 others.append(c)
144
145 return pins, others
146
147
148 class ConstraintManager:
149 def __init__(self, io, connectors):
150 self.available = list(io)
151 self.matched = []
152 self.platform_commands = []
153 self.connector_manager = ConnectorManager(connectors)
154
155 def add_extension(self, io):
156 self.available.extend(io)
157
158 def request(self, name, number=None):
159 resource = _lookup(self.available, name, number)
160 rt = _resource_type(resource)
161 if isinstance(rt, int):
162 obj = Signal(rt, name_override=resource[0])
163 else:
164 obj = Record(rt, name=resource[0])
165
166 for element in resource[2:]:
167 if isinstance(element, PlatformInfo):
168 obj.platform_info = element.info
169 break
170
171 self.available.remove(resource)
172 self.matched.append((resource, obj))
173 return obj
174
175 def lookup_request(self, name, number=None):
176 for resource, obj in self.matched:
177 if resource[0] == name and (number is None or
178 resource[1] == number):
179 return obj
180
181 raise ConstraintError("Resource not found: {}:{}".format(name, number))
182
183 def add_platform_command(self, command, **signals):
184 self.platform_commands.append((command, signals))
185
186 def get_io_signals(self):
187 r = set()
188 for resource, obj in self.matched:
189 if isinstance(obj, Signal):
190 r.add(obj)
191 else:
192 r.update(obj.flatten())
193
194 return r
195
196 def get_sig_constraints(self):
197 r = []
198 for resource, obj in self.matched:
199 name = resource[0]
200 number = resource[1]
201 has_subsignals = False
202 top_constraints = []
203 for element in resource[2:]:
204 if isinstance(element, Subsignal):
205 has_subsignals = True
206 else:
207 top_constraints.append(element)
208
209 if has_subsignals:
210 for element in resource[2:]:
211 if isinstance(element, Subsignal):
212 sig = getattr(obj, element.name)
213 pins, others = _separate_pins(top_constraints +
214 element.constraints)
215 pins = self.connector_manager.resolve_identifiers(pins)
216 r.append((sig, pins, others,
217 (name, number, element.name)))
218 else:
219 pins, others = _separate_pins(top_constraints)
220 pins = self.connector_manager.resolve_identifiers(pins)
221 r.append((obj, pins, others, (name, number, None)))
222
223 return r
224
225 def get_platform_commands(self):
226 return self.platform_commands
227
228
229 class GenericPlatform:
230 def __init__(self, device, io, connectors=[], name=None):
231 self.device = device
232 self.constraint_manager = ConstraintManager(io, connectors)
233 if name is None:
234 name = self.__module__.split(".")[-1]
235 self.name = name
236 self.sources = set()
237 self.verilog_include_paths = set()
238 self.finalized = False
239
240 def request(self, *args, **kwargs):
241 return self.constraint_manager.request(*args, **kwargs)
242
243 def lookup_request(self, *args, **kwargs):
244 return self.constraint_manager.lookup_request(*args, **kwargs)
245
246 def add_period_constraint(self, clk, period):
247 raise NotImplementedError
248
249 def add_platform_command(self, *args, **kwargs):
250 return self.constraint_manager.add_platform_command(*args, **kwargs)
251
252 def add_extension(self, *args, **kwargs):
253 return self.constraint_manager.add_extension(*args, **kwargs)
254
255 def finalize(self, fragment, *args, **kwargs):
256 if self.finalized:
257 raise ConstraintError("Already finalized")
258 # if none exists, create a default clock domain and drive it
259 if not fragment.clock_domains:
260 if not hasattr(self, "default_clk_name"):
261 raise NotImplementedError(
262 "No default clock and no clock domain defined")
263 crg = CRG(self.request(self.default_clk_name))
264 fragment += crg.get_fragment()
265
266 self.do_finalize(fragment, *args, **kwargs)
267 self.finalized = True
268
269 def do_finalize(self, fragment, *args, **kwargs):
270 """overload this and e.g. add_platform_command()'s after the modules
271 had their say"""
272 if hasattr(self, "default_clk_period"):
273 try:
274 self.add_period_constraint(
275 self.lookup_request(self.default_clk_name),
276 self.default_clk_period)
277 except ConstraintError:
278 pass
279
280 def add_source(self, filename, language=None, library=None):
281 if language is None:
282 language = tools.language_by_filename(filename)
283 if language is None:
284 language = "verilog"
285
286 if library is None:
287 library = "work"
288
289 self.sources.add((os.path.abspath(filename), language, library))
290
291 def add_sources(self, path, *filenames, language=None, library=None):
292 for f in filenames:
293 self.add_source(os.path.join(path, f), language, library)
294
295 def add_source_dir(self, path, recursive=True, library=None):
296 dir_files = []
297 if recursive:
298 for root, dirs, files in os.walk(path):
299 for filename in files:
300 dir_files.append(os.path.join(root, filename))
301 else:
302 for item in os.listdir(path):
303 if os.path.isfile(os.path.join(path, item)):
304 dir_files.append(os.path.join(path, item))
305 for filename in dir_files:
306 language = tools.language_by_filename(filename)
307 if language is not None:
308 self.add_source(filename, language, library)
309
310 def add_verilog_include_path(self, path):
311 self.verilog_include_paths.add(os.path.abspath(path))
312
313 def resolve_signals(self, vns):
314 # resolve signal names in constraints
315 sc = self.constraint_manager.get_sig_constraints()
316 named_sc = [(vns.get_name(sig), pins, others, resource)
317 for sig, pins, others, resource in sc]
318 # resolve signal names in platform commands
319 pc = self.constraint_manager.get_platform_commands()
320 named_pc = []
321 for template, args in pc:
322 name_dict = dict((k, vns.get_name(sig)) for k, sig in args.items())
323 named_pc.append(template.format(**name_dict))
324
325 return named_sc, named_pc
326
327 def get_verilog(self, fragment, **kwargs):
328 return verilog.convert(
329 fragment,
330 self.constraint_manager.get_io_signals(),
331 create_clock_domains=False, **kwargs)
332
333 def get_edif(self, fragment, cell_library, vendor, device, **kwargs):
334 return edif.convert(
335 fragment,
336 self.constraint_manager.get_io_signals(),
337 cell_library, vendor, device, **kwargs)
338
339 def build(self, fragment):
340 raise NotImplementedError("GenericPlatform.build must be overloaded")
341
342 def create_programmer(self):
343 raise NotImplementedError