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>
8 from migen
.fhdl
.structure
import Signal
9 from migen
.genlib
.record
import Record
11 from litex
.gen
.fhdl
import verilog
13 from litex
.build
.io
import CRG
14 from litex
.build
import tools
17 class ConstraintError(Exception):
22 def __init__(self
, *identifiers
):
25 if isinstance(i
, int):
26 self
.identifiers
+= ["X"]*i
28 self
.identifiers
+= i
.split()
31 return "{}('{}')".format(self
.__class
__.__name
__,
32 " ".join(self
.identifiers
))
36 def __init__(self
, name
):
40 return "{}('{}')".format(self
.__class
__.__name
__, self
.name
)
44 def __init__(self
, strength
):
45 self
.strength
= strength
48 return "{}('{}')".format(self
.__class
__.__name
__, self
.strength
)
52 def __init__(self
, misc
):
56 return "{}({})".format(self
.__class
__.__name
__, repr(self
.misc
))
61 return "{}()".format(self
.__class
__.__name
__)
66 def __init__(self
, name
, *constraints
):
68 self
.constraints
= list(constraints
)
71 return "{}('{}', {})".format(
72 self
.__class
__.__name
__,
74 ", ".join([repr(constr
) for constr
in self
.constraints
]))
78 def __init__(self
, info
):
82 return "{}({})".format(self
.__class
__.__name
__, repr(self
.info
))
85 def _lookup(description
, name
, number
, loose
=True):
86 for resource
in description
:
87 if resource
[0] == name
and (number
is None or resource
[1] == number
):
92 raise ConstraintError("Resource not found: {}:{}".format(name
, number
))
95 def _resource_type(resource
):
98 for element
in resource
[2:]:
99 if isinstance(element
, Pins
):
101 t
= len(element
.identifiers
)
102 elif isinstance(element
, Subsignal
):
108 assert(isinstance(t
, list))
111 for c
in element
.constraints
:
112 if isinstance(c
, Pins
):
113 assert(n_bits
is None)
114 n_bits
= len(c
.identifiers
)
115 if isinstance(c
, Inverted
):
118 t
.append((element
.name
, n_bits
))
119 i
.append((element
.name
, inverted
))
124 class ConnectorManager
:
125 def __init__(self
, connectors
):
126 self
.connector_table
= dict()
127 for connector
in connectors
:
128 cit
= iter(connector
)
129 conn_name
= next(cit
)
130 if isinstance(connector
[1], str):
133 pin_list
+= pins
.split()
134 pin_list
= [None if pin
== "None" else pin
for pin
in pin_list
]
135 elif isinstance(connector
[1], dict):
136 pin_list
= connector
[1]
138 raise ValueError("Unsupported pin list type {} for connector"
139 " {}".format(type(connector
[1]), conn_name
))
140 if conn_name
in self
.connector_table
:
142 "Connector specified more than once: {}".format(conn_name
))
144 self
.connector_table
[conn_name
] = pin_list
146 def resolve_identifiers(self
, identifiers
):
148 for identifier
in identifiers
:
149 if ":" in identifier
:
150 conn
, pn
= identifier
.split(":")
154 r
.append(self
.connector_table
[conn
][pn
])
161 def _separate_pins(constraints
):
164 for c
in constraints
:
165 if isinstance(c
, Pins
):
174 class ConstraintManager
:
175 def __init__(self
, io
, connectors
):
176 self
.available
= list(io
)
178 self
.platform_commands
= []
179 self
.connector_manager
= ConnectorManager(connectors
)
181 def add_extension(self
, io
):
182 self
.available
.extend(io
)
184 def request(self
, name
, number
=None, loose
=False):
185 resource
= _lookup(self
.available
, name
, number
, loose
)
188 rt
, ri
= _resource_type(resource
)
192 resource_name
= name
+ str(number
)
193 if isinstance(rt
, int):
194 obj
= Signal(rt
, name_override
=resource_name
)
196 obj
= Record(rt
, name
=resource_name
)
197 for name
, inverted
in ri
:
199 getattr(obj
, name
).inverted
= True
201 for element
in resource
[2:]:
202 if isinstance(element
, Inverted
):
203 if isinstance(obj
, Signal
):
205 if isinstance(element
, PlatformInfo
):
206 obj
.platform_info
= element
.info
209 self
.available
.remove(resource
)
210 self
.matched
.append((resource
, obj
))
213 def lookup_request(self
, name
, number
=None, loose
=False):
215 if ":" in name
: name
, subname
= name
.split(":")
216 for resource
, obj
in self
.matched
:
217 if resource
[0] == name
and (number
is None or
218 resource
[1] == number
):
219 if subname
is not None:
220 return getattr(obj
, subname
)
227 raise ConstraintError("Resource not found: {}:{}".format(name
, number
))
229 def add_platform_command(self
, command
, **signals
):
230 self
.platform_commands
.append((command
, signals
))
232 def get_io_signals(self
):
234 for resource
, obj
in self
.matched
:
235 if isinstance(obj
, Signal
):
238 r
.update(obj
.flatten())
242 def get_sig_constraints(self
):
244 for resource
, obj
in self
.matched
:
247 has_subsignals
= False
249 for element
in resource
[2:]:
250 if isinstance(element
, Subsignal
):
251 has_subsignals
= True
253 top_constraints
.append(element
)
256 for element
in resource
[2:]:
257 if isinstance(element
, Subsignal
):
258 sig
= getattr(obj
, element
.name
)
259 pins
, others
= _separate_pins(top_constraints
+
261 pins
= self
.connector_manager
.resolve_identifiers(pins
)
262 r
.append((sig
, pins
, others
,
263 (name
, number
, element
.name
)))
265 pins
, others
= _separate_pins(top_constraints
)
266 pins
= self
.connector_manager
.resolve_identifiers(pins
)
267 r
.append((obj
, pins
, others
, (name
, number
, None)))
271 def get_platform_commands(self
):
272 return self
.platform_commands
275 class GenericPlatform
:
276 def __init__(self
, device
, io
, connectors
=[], name
=None):
278 self
.constraint_manager
= ConstraintManager(io
, connectors
)
280 name
= self
.__module
__.split(".")[-1]
283 self
.verilog_include_paths
= []
284 self
.output_dir
= None
285 self
.finalized
= False
286 self
.use_default_clk
= False
288 def request(self
, *args
, **kwargs
):
289 return self
.constraint_manager
.request(*args
, **kwargs
)
291 def lookup_request(self
, *args
, **kwargs
):
292 return self
.constraint_manager
.lookup_request(*args
, **kwargs
)
294 def add_period_constraint(self
, clk
, period
):
295 raise NotImplementedError
297 def add_false_path_constraint(self
, from_
, to
):
298 raise NotImplementedError
300 def add_false_path_constraints(self
, *clk
):
304 self
.add_false_path_constraint(a
, b
)
306 def add_platform_command(self
, *args
, **kwargs
):
307 return self
.constraint_manager
.add_platform_command(*args
, **kwargs
)
309 def add_extension(self
, *args
, **kwargs
):
310 return self
.constraint_manager
.add_extension(*args
, **kwargs
)
312 def finalize(self
, fragment
, *args
, **kwargs
):
314 raise ConstraintError("Already finalized")
315 # if none exists, create a default clock domain and drive it
316 if not fragment
.clock_domains
:
317 if not hasattr(self
, "default_clk_name"):
318 raise NotImplementedError(
319 "No default clock and no clock domain defined")
320 crg
= CRG(self
.request(self
.default_clk_name
))
321 fragment
+= crg
.get_fragment()
322 self
.user_default_clk
= True
324 self
.do_finalize(fragment
, *args
, **kwargs
)
325 self
.finalized
= True
327 def do_finalize(self
, fragment
, *args
, **kwargs
):
328 """overload this and e.g. add_platform_command()'s after the modules
330 if self
.use_default_clk
:
332 self
.add_period_constraint(
333 self
.lookup_request(self
.default_clk_name
),
334 self
.default_clk_period
)
335 except ConstraintError
:
338 def add_source(self
, filename
, language
=None, library
=None):
340 language
= tools
.language_by_filename(filename
)
343 for f
, _
, _
in self
.sources
:
346 self
.sources
.append((os
.path
.abspath(filename
), language
, library
))
348 def add_sources(self
, path
, *filenames
, language
=None, library
=None):
350 self
.add_source(os
.path
.join(path
, f
), language
, library
)
352 def add_source_dir(self
, path
, recursive
=True, language
=None, library
=None):
355 for root
, dirs
, files
in os
.walk(path
):
356 for filename
in files
:
357 dir_files
.append(os
.path
.join(root
, filename
))
359 for item
in os
.listdir(path
):
360 if os
.path
.isfile(os
.path
.join(path
, item
)):
361 dir_files
.append(os
.path
.join(path
, item
))
362 for filename
in dir_files
:
364 if _language
is None:
365 _language
= tools
.language_by_filename(filename
)
366 if _language
is not None:
367 self
.add_source(filename
, _language
, library
)
369 def add_verilog_include_path(self
, path
):
370 self
.verilog_include_paths
.append(os
.path
.abspath(path
))
372 def resolve_signals(self
, vns
):
373 # resolve signal names in constraints
374 sc
= self
.constraint_manager
.get_sig_constraints()
375 named_sc
= [(vns
.get_name(sig
), pins
, others
, resource
)
376 for sig
, pins
, others
, resource
in sc
]
377 # resolve signal names in platform commands
378 pc
= self
.constraint_manager
.get_platform_commands()
380 for template
, args
in pc
:
381 name_dict
= dict((k
, vns
.get_name(sig
)) for k
, sig
in args
.items())
382 named_pc
.append(template
.format(**name_dict
))
384 return named_sc
, named_pc
386 def get_verilog(self
, fragment
, **kwargs
):
387 return verilog
.convert(
389 self
.constraint_manager
.get_io_signals(),
390 create_clock_domains
=False, **kwargs
)
392 def get_edif(self
, fragment
, cell_library
, vendor
, device
, **kwargs
):
395 self
.constraint_manager
.get_io_signals(),
396 cell_library
, vendor
, device
, **kwargs
)
398 def build(self
, fragment
):
399 raise NotImplementedError("GenericPlatform.build must be overloaded")
401 def create_programmer(self
):
402 raise NotImplementedError