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