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