2fdda7184e2b636a9cea91711fc752dd4581a19f
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
):
86 for resource
in description
:
87 if resource
[0] == name
and (number
is None or resource
[1] == number
):
89 raise ConstraintError("Resource not found: {}:{}".format(name
, number
))
92 def _resource_type(resource
):
95 for element
in resource
[2:]:
96 if isinstance(element
, Pins
):
98 t
= len(element
.identifiers
)
99 elif isinstance(element
, Subsignal
):
105 assert(isinstance(t
, list))
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
):
115 t
.append((element
.name
, n_bits
))
116 i
.append((element
.name
, inverted
))
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):
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]
135 raise ValueError("Unsupported pin list type {} for connector"
136 " {}".format(type(connector
[1]), conn_name
))
137 if conn_name
in self
.connector_table
:
139 "Connector specified more than once: {}".format(conn_name
))
141 self
.connector_table
[conn_name
] = pin_list
143 def resolve_identifiers(self
, identifiers
):
145 for identifier
in identifiers
:
146 if ":" in identifier
:
147 conn
, pn
= identifier
.split(":")
151 r
.append(self
.connector_table
[conn
][pn
])
158 def _separate_pins(constraints
):
161 for c
in constraints
:
162 if isinstance(c
, Pins
):
171 class ConstraintManager
:
172 def __init__(self
, io
, connectors
):
173 self
.available
= list(io
)
175 self
.platform_commands
= []
176 self
.connector_manager
= ConnectorManager(connectors
)
178 def add_extension(self
, io
):
179 self
.available
.extend(io
)
181 def request(self
, name
, number
=None):
182 resource
= _lookup(self
.available
, name
, number
)
183 rt
, ri
= _resource_type(resource
)
187 resource_name
= name
+ str(number
)
188 if isinstance(rt
, int):
189 obj
= Signal(rt
, name_override
=resource_name
)
191 obj
= Record(rt
, name
=resource_name
)
192 for name
, inverted
in ri
:
194 getattr(obj
, name
).inverted
= True
196 for element
in resource
[2:]:
197 if isinstance(element
, Inverted
):
198 if isinstance(obj
, Signal
):
200 if isinstance(element
, PlatformInfo
):
201 obj
.platform_info
= element
.info
204 self
.available
.remove(resource
)
205 self
.matched
.append((resource
, obj
))
208 def lookup_request(self
, name
, number
=None, loose
=False):
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
)
222 raise ConstraintError("Resource not found: {}:{}".format(name
, number
))
224 def add_platform_command(self
, command
, **signals
):
225 self
.platform_commands
.append((command
, signals
))
227 def get_io_signals(self
):
229 for resource
, obj
in self
.matched
:
230 if isinstance(obj
, Signal
):
233 r
.update(obj
.flatten())
237 def get_sig_constraints(self
):
239 for resource
, obj
in self
.matched
:
242 has_subsignals
= False
244 for element
in resource
[2:]:
245 if isinstance(element
, Subsignal
):
246 has_subsignals
= True
248 top_constraints
.append(element
)
251 for element
in resource
[2:]:
252 if isinstance(element
, Subsignal
):
253 sig
= getattr(obj
, element
.name
)
254 pins
, others
= _separate_pins(top_constraints
+
256 pins
= self
.connector_manager
.resolve_identifiers(pins
)
257 r
.append((sig
, pins
, others
,
258 (name
, number
, element
.name
)))
260 pins
, others
= _separate_pins(top_constraints
)
261 pins
= self
.connector_manager
.resolve_identifiers(pins
)
262 r
.append((obj
, pins
, others
, (name
, number
, None)))
266 def get_platform_commands(self
):
267 return self
.platform_commands
270 class GenericPlatform
:
271 def __init__(self
, device
, io
, connectors
=[], name
=None):
273 self
.constraint_manager
= ConstraintManager(io
, connectors
)
275 name
= self
.__module
__.split(".")[-1]
278 self
.verilog_include_paths
= []
279 self
.output_dir
= None
280 self
.finalized
= False
281 self
.use_default_clk
= False
283 def request(self
, *args
, **kwargs
):
284 return self
.constraint_manager
.request(*args
, **kwargs
)
286 def lookup_request(self
, *args
, **kwargs
):
287 return self
.constraint_manager
.lookup_request(*args
, **kwargs
)
289 def add_period_constraint(self
, clk
, period
):
290 raise NotImplementedError
292 def add_false_path_constraint(self
, from_
, to
):
293 raise NotImplementedError
295 def add_false_path_constraints(self
, *clk
):
299 self
.add_false_path_constraint(a
, b
)
301 def add_platform_command(self
, *args
, **kwargs
):
302 return self
.constraint_manager
.add_platform_command(*args
, **kwargs
)
304 def add_extension(self
, *args
, **kwargs
):
305 return self
.constraint_manager
.add_extension(*args
, **kwargs
)
307 def finalize(self
, fragment
, *args
, **kwargs
):
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
319 self
.do_finalize(fragment
, *args
, **kwargs
)
320 self
.finalized
= True
322 def do_finalize(self
, fragment
, *args
, **kwargs
):
323 """overload this and e.g. add_platform_command()'s after the modules
325 if self
.use_default_clk
:
327 self
.add_period_constraint(
328 self
.lookup_request(self
.default_clk_name
),
329 self
.default_clk_period
)
330 except ConstraintError
:
333 def add_source(self
, filename
, language
=None, library
=None):
335 language
= tools
.language_by_filename(filename
)
338 for f
, _
, _
in self
.sources
:
341 self
.sources
.append((os
.path
.abspath(filename
), language
, library
))
343 def add_sources(self
, path
, *filenames
, language
=None, library
=None):
345 self
.add_source(os
.path
.join(path
, f
), language
, library
)
347 def add_source_dir(self
, path
, recursive
=True, language
=None, library
=None):
350 for root
, dirs
, files
in os
.walk(path
):
351 for filename
in files
:
352 dir_files
.append(os
.path
.join(root
, filename
))
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
:
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
)
364 def add_verilog_include_path(self
, path
):
365 self
.verilog_include_paths
.append(os
.path
.abspath(path
))
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()
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
))
379 return named_sc
, named_pc
381 def get_verilog(self
, fragment
, **kwargs
):
382 return verilog
.convert(
384 self
.constraint_manager
.get_io_signals(),
385 create_clock_domains
=False, **kwargs
)
387 def get_edif(self
, fragment
, cell_library
, vendor
, device
, **kwargs
):
390 self
.constraint_manager
.get_io_signals(),
391 cell_library
, vendor
, device
, **kwargs
)
393 def build(self
, fragment
):
394 raise NotImplementedError("GenericPlatform.build must be overloaded")
396 def create_programmer(self
):
397 raise NotImplementedError