2013-12-01 11:56:07 -05:00
|
|
|
import os, sys
|
2013-02-07 16:07:30 -05:00
|
|
|
|
2013-05-26 12:07:26 -04:00
|
|
|
from migen.fhdl.std import *
|
2013-07-26 09:13:24 -04:00
|
|
|
from migen.fhdl.structure import _Fragment
|
2013-02-23 13:37:27 -05:00
|
|
|
from migen.genlib.record import Record
|
2015-03-13 19:10:08 -04:00
|
|
|
from migen.genlib.io import CRG
|
2013-08-02 11:10:33 -04:00
|
|
|
from migen.fhdl import verilog, edif
|
2013-12-01 11:56:07 -05:00
|
|
|
from migen.util.misc import autotype
|
2013-02-07 16:07:30 -05:00
|
|
|
|
2013-02-08 14:25:20 -05:00
|
|
|
from mibuild import tools
|
|
|
|
|
2013-02-07 16:07:30 -05:00
|
|
|
class ConstraintError(Exception):
|
|
|
|
pass
|
2014-10-17 05:08:37 -04:00
|
|
|
|
2013-02-07 16:07:30 -05:00
|
|
|
class Pins:
|
|
|
|
def __init__(self, *identifiers):
|
2013-06-25 16:57:31 -04:00
|
|
|
self.identifiers = []
|
|
|
|
for i in identifiers:
|
|
|
|
self.identifiers += i.split()
|
2013-02-07 16:07:30 -05:00
|
|
|
|
|
|
|
class IOStandard:
|
|
|
|
def __init__(self, name):
|
|
|
|
self.name = name
|
|
|
|
|
|
|
|
class Drive:
|
|
|
|
def __init__(self, strength):
|
|
|
|
self.strength = strength
|
|
|
|
|
|
|
|
class Misc:
|
|
|
|
def __init__(self, misc):
|
|
|
|
self.misc = misc
|
|
|
|
|
|
|
|
class Subsignal:
|
|
|
|
def __init__(self, name, *constraints):
|
|
|
|
self.name = name
|
|
|
|
self.constraints = list(constraints)
|
|
|
|
|
2013-03-26 14:17:35 -04:00
|
|
|
class PlatformInfo:
|
|
|
|
def __init__(self, info):
|
|
|
|
self.info = info
|
|
|
|
|
2013-02-07 16:07:30 -05:00
|
|
|
def _lookup(description, name, number):
|
|
|
|
for resource in description:
|
|
|
|
if resource[0] == name and (number is None or resource[1] == number):
|
|
|
|
return resource
|
2013-02-13 17:29:33 -05:00
|
|
|
raise ConstraintError("Resource not found: " + name + ":" + str(number))
|
2014-10-17 05:08:37 -04:00
|
|
|
|
2013-03-26 12:56:53 -04:00
|
|
|
def _resource_type(resource):
|
2013-02-07 16:07:30 -05:00
|
|
|
t = None
|
|
|
|
for element in resource[2:]:
|
|
|
|
if isinstance(element, Pins):
|
|
|
|
assert(t is None)
|
|
|
|
t = len(element.identifiers)
|
|
|
|
elif isinstance(element, Subsignal):
|
|
|
|
if t is None:
|
|
|
|
t = []
|
|
|
|
assert(isinstance(t, list))
|
|
|
|
n_bits = None
|
|
|
|
for c in element.constraints:
|
|
|
|
if isinstance(c, Pins):
|
|
|
|
assert(n_bits is None)
|
|
|
|
n_bits = len(c.identifiers)
|
2013-03-26 12:56:53 -04:00
|
|
|
t.append((element.name, n_bits))
|
2013-02-07 16:07:30 -05:00
|
|
|
return t
|
|
|
|
|
2014-02-16 17:53:50 -05:00
|
|
|
class ConnectorManager:
|
|
|
|
def __init__(self, connectors):
|
|
|
|
self.connector_table = dict()
|
|
|
|
for connector in connectors:
|
|
|
|
cit = iter(connector)
|
|
|
|
conn_name = next(cit)
|
2015-02-18 10:32:15 -05:00
|
|
|
if isinstance(connector[1], str):
|
|
|
|
pin_list = []
|
|
|
|
for pins in cit:
|
|
|
|
pin_list += pins.split()
|
|
|
|
pin_list = [None if pin == "None" else pin for pin in pin_list]
|
|
|
|
elif isinstance(connector[1], dict):
|
|
|
|
pin_list = connector[1]
|
|
|
|
else:
|
|
|
|
raise ValueError("Unsupported pin list type {} for connector"
|
|
|
|
" {}".format(type(connector[1]), conn_name))
|
2014-02-16 17:53:50 -05:00
|
|
|
if conn_name in self.connector_table:
|
|
|
|
raise ValueError("Connector specified more than once: "+conn_name)
|
|
|
|
self.connector_table[conn_name] = pin_list
|
|
|
|
|
|
|
|
def resolve_identifiers(self, identifiers):
|
|
|
|
r = []
|
|
|
|
for identifier in identifiers:
|
|
|
|
if ":" in identifier:
|
|
|
|
conn, pn = identifier.split(":")
|
2015-02-18 10:32:15 -05:00
|
|
|
if pn.isdigit():
|
|
|
|
pn = int(pn)
|
2014-02-16 17:53:50 -05:00
|
|
|
r.append(self.connector_table[conn][pn])
|
|
|
|
else:
|
|
|
|
r.append(identifier)
|
|
|
|
return r
|
|
|
|
|
2013-02-07 16:07:30 -05:00
|
|
|
def _separate_pins(constraints):
|
|
|
|
pins = None
|
|
|
|
others = []
|
|
|
|
for c in constraints:
|
|
|
|
if isinstance(c, Pins):
|
|
|
|
assert(pins is None)
|
|
|
|
pins = c.identifiers
|
|
|
|
else:
|
|
|
|
others.append(c)
|
|
|
|
return pins, others
|
2014-02-16 17:53:50 -05:00
|
|
|
|
2013-02-07 16:07:30 -05:00
|
|
|
class ConstraintManager:
|
2014-02-16 17:53:50 -05:00
|
|
|
def __init__(self, io, connectors):
|
|
|
|
self.available = list(io)
|
2013-03-26 12:56:53 -04:00
|
|
|
self.matched = []
|
2013-02-07 16:07:30 -05:00
|
|
|
self.platform_commands = []
|
2014-02-16 17:53:50 -05:00
|
|
|
self.connector_manager = ConnectorManager(connectors)
|
|
|
|
|
|
|
|
def add_extension(self, io):
|
|
|
|
self.available.extend(io)
|
2014-10-17 05:08:37 -04:00
|
|
|
|
2013-03-26 12:56:53 -04:00
|
|
|
def request(self, name, number=None):
|
|
|
|
resource = _lookup(self.available, name, number)
|
2014-10-17 05:08:37 -04:00
|
|
|
rt = _resource_type(resource)
|
2013-03-26 12:56:53 -04:00
|
|
|
if isinstance(rt, int):
|
|
|
|
obj = Signal(rt, name_override=resource[0])
|
2013-02-07 16:07:30 -05:00
|
|
|
else:
|
2013-03-26 12:56:53 -04:00
|
|
|
obj = Record(rt, name=resource[0])
|
2013-03-26 14:17:35 -04:00
|
|
|
for element in resource[2:]:
|
|
|
|
if isinstance(element, PlatformInfo):
|
|
|
|
obj.platform_info = element.info
|
|
|
|
break
|
2013-03-26 12:56:53 -04:00
|
|
|
self.available.remove(resource)
|
|
|
|
self.matched.append((resource, obj))
|
2013-02-07 16:07:30 -05:00
|
|
|
return obj
|
2013-03-26 12:56:53 -04:00
|
|
|
|
|
|
|
def lookup_request(self, name, number=None):
|
|
|
|
for resource, obj in self.matched:
|
|
|
|
if resource[0] == name and (number is None or resource[1] == number):
|
|
|
|
return obj
|
|
|
|
raise ConstraintError("Resource not found: " + name + ":" + str(number))
|
2014-10-17 05:08:37 -04:00
|
|
|
|
2013-02-07 16:07:30 -05:00
|
|
|
def add_platform_command(self, command, **signals):
|
|
|
|
self.platform_commands.append((command, signals))
|
2014-10-17 05:08:37 -04:00
|
|
|
|
2013-02-07 16:07:30 -05:00
|
|
|
def get_io_signals(self):
|
2013-03-26 12:56:53 -04:00
|
|
|
r = set()
|
|
|
|
for resource, obj in self.matched:
|
|
|
|
if isinstance(obj, Signal):
|
|
|
|
r.add(obj)
|
|
|
|
else:
|
|
|
|
r.update(obj.flatten())
|
|
|
|
return r
|
2014-10-17 05:08:37 -04:00
|
|
|
|
2013-02-07 16:07:30 -05:00
|
|
|
def get_sig_constraints(self):
|
|
|
|
r = []
|
2013-03-26 12:56:53 -04:00
|
|
|
for resource, obj in self.matched:
|
2013-02-07 16:07:30 -05:00
|
|
|
name = resource[0]
|
|
|
|
number = resource[1]
|
|
|
|
has_subsignals = False
|
|
|
|
top_constraints = []
|
|
|
|
for element in resource[2:]:
|
|
|
|
if isinstance(element, Subsignal):
|
|
|
|
has_subsignals = True
|
|
|
|
else:
|
|
|
|
top_constraints.append(element)
|
|
|
|
if has_subsignals:
|
|
|
|
for element in resource[2:]:
|
|
|
|
if isinstance(element, Subsignal):
|
2013-03-26 12:56:53 -04:00
|
|
|
sig = getattr(obj, element.name)
|
2013-02-07 16:07:30 -05:00
|
|
|
pins, others = _separate_pins(top_constraints + element.constraints)
|
2014-02-16 17:53:50 -05:00
|
|
|
pins = self.connector_manager.resolve_identifiers(pins)
|
2013-02-07 16:07:30 -05:00
|
|
|
r.append((sig, pins, others, (name, number, element.name)))
|
|
|
|
else:
|
|
|
|
pins, others = _separate_pins(top_constraints)
|
2014-02-16 17:53:50 -05:00
|
|
|
pins = self.connector_manager.resolve_identifiers(pins)
|
2013-02-07 16:07:30 -05:00
|
|
|
r.append((obj, pins, others, (name, number, None)))
|
|
|
|
return r
|
|
|
|
|
|
|
|
def get_platform_commands(self):
|
|
|
|
return self.platform_commands
|
|
|
|
|
|
|
|
class GenericPlatform:
|
2015-03-13 19:10:08 -04:00
|
|
|
def __init__(self, device, io, connectors=[], name=None):
|
2013-02-07 16:07:30 -05:00
|
|
|
self.device = device
|
2014-02-16 17:53:50 -05:00
|
|
|
self.constraint_manager = ConstraintManager(io, connectors)
|
2013-02-14 14:02:35 -05:00
|
|
|
if name is None:
|
|
|
|
name = self.__module__.split(".")[-1]
|
|
|
|
self.name = name
|
2013-02-08 14:25:20 -05:00
|
|
|
self.sources = []
|
2013-12-12 17:17:51 -05:00
|
|
|
self.verilog_include_paths = []
|
2013-06-25 18:27:41 -04:00
|
|
|
self.finalized = False
|
2013-02-07 16:07:30 -05:00
|
|
|
|
|
|
|
def request(self, *args, **kwargs):
|
|
|
|
return self.constraint_manager.request(*args, **kwargs)
|
|
|
|
|
2013-03-26 12:56:53 -04:00
|
|
|
def lookup_request(self, *args, **kwargs):
|
|
|
|
return self.constraint_manager.lookup_request(*args, **kwargs)
|
|
|
|
|
2015-03-13 19:10:08 -04:00
|
|
|
def add_period_constraint(self, clk, period):
|
|
|
|
raise NotImplementedError
|
|
|
|
|
2013-02-07 16:07:30 -05:00
|
|
|
def add_platform_command(self, *args, **kwargs):
|
|
|
|
return self.constraint_manager.add_platform_command(*args, **kwargs)
|
|
|
|
|
2014-02-16 17:53:50 -05:00
|
|
|
def add_extension(self, *args, **kwargs):
|
|
|
|
return self.constraint_manager.add_extension(*args, **kwargs)
|
|
|
|
|
2013-06-25 18:27:41 -04:00
|
|
|
def finalize(self, fragment, *args, **kwargs):
|
|
|
|
if self.finalized:
|
|
|
|
raise ConstraintError("Already finalized")
|
2013-08-11 17:07:07 -04:00
|
|
|
# if none exists, create a default clock domain and drive it
|
|
|
|
if not fragment.clock_domains:
|
2015-03-13 19:10:08 -04:00
|
|
|
if not hasattr(self, "default_clk_name"):
|
|
|
|
raise NotImplementedError("No default clock and no clock domain defined")
|
|
|
|
crg = CRG(self.request(self.default_clk_name))
|
2013-08-11 17:07:07 -04:00
|
|
|
fragment += crg.get_fragment()
|
2013-06-25 18:27:41 -04:00
|
|
|
self.do_finalize(fragment, *args, **kwargs)
|
|
|
|
self.finalized = True
|
|
|
|
|
|
|
|
def do_finalize(self, fragment, *args, **kwargs):
|
2015-03-13 19:10:08 -04:00
|
|
|
"""overload this and e.g. add_platform_command()'s after the modules had their say"""
|
|
|
|
if hasattr(self, "default_clk_period"):
|
|
|
|
try:
|
|
|
|
self.add_period_constraint(self.lookup_request(self.default_clk_name), self.default_clk_period)
|
|
|
|
except ConstraintError:
|
|
|
|
pass
|
2013-06-25 18:27:41 -04:00
|
|
|
|
2013-02-08 14:25:20 -05:00
|
|
|
def add_source(self, filename, language=None):
|
|
|
|
if language is None:
|
|
|
|
language = tools.language_by_filename(filename)
|
|
|
|
if language is None:
|
|
|
|
language = "verilog" # default to Verilog
|
2013-02-12 13:16:00 -05:00
|
|
|
filename = os.path.abspath(filename)
|
2013-02-08 14:25:20 -05:00
|
|
|
self.sources.append((filename, language))
|
|
|
|
|
|
|
|
def add_sources(self, path, *filenames, language=None):
|
|
|
|
for f in filenames:
|
|
|
|
self.add_source(os.path.join(path, f), language)
|
|
|
|
|
2014-08-01 06:50:38 -04:00
|
|
|
def add_source_dir(self, path, recursive=True):
|
|
|
|
dir_files = []
|
|
|
|
if recursive:
|
|
|
|
for root, dirs, files in os.walk(path):
|
|
|
|
for filename in files:
|
|
|
|
dir_files.append(os.path.join(root, filename))
|
|
|
|
else:
|
|
|
|
for item in os.listdir(path):
|
|
|
|
if os.path.isfile(os.path.join(path, item)):
|
|
|
|
dir_files.append(os.path.join(path, item))
|
|
|
|
for filename in dir_files:
|
|
|
|
language = tools.language_by_filename(filename)
|
|
|
|
if language is not None:
|
|
|
|
self.add_source(filename, language)
|
2013-02-08 14:25:20 -05:00
|
|
|
|
2013-12-12 17:17:51 -05:00
|
|
|
def add_verilog_include_path(self, path):
|
|
|
|
self.verilog_include_paths.append(os.path.abspath(path))
|
|
|
|
|
2015-02-14 06:05:07 -05:00
|
|
|
def resolve_signals(self, vns):
|
2013-08-02 11:10:33 -04:00
|
|
|
# resolve signal names in constraints
|
|
|
|
sc = self.constraint_manager.get_sig_constraints()
|
|
|
|
named_sc = [(vns.get_name(sig), pins, others, resource) for sig, pins, others, resource in sc]
|
|
|
|
# resolve signal names in platform commands
|
|
|
|
pc = self.constraint_manager.get_platform_commands()
|
|
|
|
named_pc = []
|
|
|
|
for template, args in pc:
|
|
|
|
name_dict = dict((k, vns.get_name(sig)) for k, sig in args.items())
|
|
|
|
named_pc.append(template.format(**name_dict))
|
|
|
|
return named_sc, named_pc
|
|
|
|
|
2013-08-11 17:07:07 -04:00
|
|
|
def _get_source(self, fragment, gen_fn):
|
2013-07-26 09:13:24 -04:00
|
|
|
if not isinstance(fragment, _Fragment):
|
2013-03-12 11:14:13 -04:00
|
|
|
fragment = fragment.get_fragment()
|
2013-08-11 17:07:07 -04:00
|
|
|
# generate source
|
|
|
|
src, vns = gen_fn(fragment)
|
2015-02-12 17:28:41 -05:00
|
|
|
return src, vns
|
2013-08-11 17:07:07 -04:00
|
|
|
|
|
|
|
def get_verilog(self, fragment, **kwargs):
|
|
|
|
return self._get_source(fragment, lambda f: verilog.convert(f, self.constraint_manager.get_io_signals(),
|
2015-03-18 09:58:40 -04:00
|
|
|
return_ns=True, create_clock_domains=False, **kwargs))
|
2014-10-17 05:08:37 -04:00
|
|
|
|
2013-08-02 11:10:33 -04:00
|
|
|
def get_edif(self, fragment, cell_library, vendor, device, **kwargs):
|
2013-08-11 17:07:07 -04:00
|
|
|
return self._get_source(fragment, lambda f: edif.convert(f, self.constraint_manager.get_io_signals(),
|
2015-03-18 09:58:40 -04:00
|
|
|
cell_library, vendor, device, return_ns=True, **kwargs))
|
2013-08-02 11:10:33 -04:00
|
|
|
|
2013-03-15 13:46:11 -04:00
|
|
|
def build(self, fragment):
|
2013-02-07 16:07:30 -05:00
|
|
|
raise NotImplementedError("GenericPlatform.build must be overloaded")
|
2013-02-08 16:23:58 -05:00
|
|
|
|
|
|
|
def build_cmdline(self, *args, **kwargs):
|
2013-12-01 11:56:07 -05:00
|
|
|
arg = sys.argv[1:]
|
|
|
|
if len(arg) % 2:
|
|
|
|
print("Missing value for option: "+sys.argv[-1])
|
|
|
|
sys.exit(1)
|
|
|
|
argdict = dict((k, autotype(v)) for k, v in zip(*[iter(arg)]*2))
|
|
|
|
kwargs.update(argdict)
|
|
|
|
self.build(*args, **kwargs)
|
2015-03-13 18:17:45 -04:00
|
|
|
|
|
|
|
def create_programmer(self):
|
|
|
|
raise NotImplementedError
|