litex/mibuild/generic_platform.py

364 lines
12 KiB
Python
Raw Normal View History

2015-04-13 14:54:19 -04:00
import os
import sys
2013-02-07 16:07:30 -05:00
2015-04-20 04:03:08 -04:00
from migen.fhdl.std import Signal
2013-02-23 13:37:27 -05:00
from migen.genlib.record import Record
from migen.genlib.io import CRG
2013-08-02 11:10:33 -04:00
from migen.fhdl import verilog, edif
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
2015-04-13 14:45:35 -04:00
2013-02-07 16:07:30 -05:00
class ConstraintError(Exception):
pass
2014-10-17 05:08:37 -04:00
2015-04-13 14:45:35 -04:00
2013-02-07 16:07:30 -05:00
class Pins:
def __init__(self, *identifiers):
self.identifiers = []
for i in identifiers:
self.identifiers += i.split()
2013-02-07 16:07:30 -05:00
2015-04-20 04:03:08 -04:00
def __repr__(self):
return "{}('{}')".format(self.__class__.__name__,
" ".join(self.identifiers))
2015-04-13 14:45:35 -04:00
2013-02-07 16:07:30 -05:00
class IOStandard:
def __init__(self, name):
self.name = name
2013-02-07 16:07:30 -05:00
2015-04-20 04:03:08 -04:00
def __repr__(self):
return "{}('{}')".format(self.__class__.__name__, self.name)
2015-04-13 14:45:35 -04:00
2013-02-07 16:07:30 -05:00
class Drive:
def __init__(self, strength):
self.strength = strength
2013-02-07 16:07:30 -05:00
2015-04-20 04:03:08 -04:00
def __repr__(self):
return "{}('{}')".format(self.__class__.__name__, self.strength)
2015-04-13 14:45:35 -04:00
2013-02-07 16:07:30 -05:00
class Misc:
def __init__(self, misc):
self.misc = misc
2013-02-07 16:07:30 -05:00
2015-04-20 04:03:08 -04:00
def __repr__(self):
return "{}({})".format(self.__class__.__name__, repr(self.misc))
2015-04-13 14:45:35 -04:00
2013-02-07 16:07:30 -05:00
class Subsignal:
def __init__(self, name, *constraints):
self.name = name
self.constraints = list(constraints)
2013-02-07 16:07:30 -05:00
2015-04-20 04:03:08 -04:00
def __repr__(self):
return "{}('{}', {})".format(
self.__class__.__name__,
self.name,
", ".join([repr(constr) for constr in self.constraints]))
2015-04-13 14:45:35 -04:00
2013-03-26 14:17:35 -04:00
class PlatformInfo:
def __init__(self, info):
self.info = info
2013-03-26 14:17:35 -04:00
2015-04-20 04:03:08 -04:00
def __repr__(self):
return "{}({})".format(self.__class__.__name__, repr(self.info))
2015-04-13 14:45:35 -04:00
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
2015-04-20 04:03:08 -04:00
raise ConstraintError("Resource not found: {}:{}".format(name, number))
2014-10-17 05:08:37 -04:00
2015-04-13 14:45:35 -04:00
def _resource_type(resource):
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 = []
2015-04-20 04:03:08 -04:00
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)
2015-04-20 04:03:08 -04:00
t.append((element.name, n_bits))
2015-04-20 04:03:08 -04:00
return t
2013-02-07 16:07:30 -05:00
2015-04-13 14:45:35 -04:00
class ConnectorManager:
def __init__(self, connectors):
self.connector_table = dict()
for connector in connectors:
cit = iter(connector)
conn_name = next(cit)
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"
2015-04-20 04:03:08 -04:00
" {}".format(type(connector[1]), conn_name))
if conn_name in self.connector_table:
2015-04-20 04:03:08 -04:00
raise ValueError(
"Connector specified more than once: {}".format(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(":")
if pn.isdigit():
pn = int(pn)
2015-04-20 04:03:08 -04:00
r.append(self.connector_table[conn][pn])
else:
r.append(identifier)
2015-04-20 04:03:08 -04:00
return r
2015-04-13 14:45:35 -04:00
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)
2015-04-20 04:03:08 -04:00
return pins, others
2015-04-13 14:45:35 -04:00
2013-02-07 16:07:30 -05:00
class ConstraintManager:
def __init__(self, io, connectors):
self.available = list(io)
self.matched = []
self.platform_commands = []
self.connector_manager = ConnectorManager(connectors)
def add_extension(self, io):
self.available.extend(io)
def request(self, name, number=None):
resource = _lookup(self.available, name, number)
rt = _resource_type(resource)
if isinstance(rt, int):
obj = Signal(rt, name_override=resource[0])
else:
obj = Record(rt, name=resource[0])
2015-04-20 04:03:08 -04:00
for element in resource[2:]:
if isinstance(element, PlatformInfo):
obj.platform_info = element.info
break
2015-04-20 04:03:08 -04:00
self.available.remove(resource)
self.matched.append((resource, obj))
return obj
def lookup_request(self, name, number=None):
for resource, obj in self.matched:
2015-04-20 04:03:08 -04:00
if resource[0] == name and (number is None or
resource[1] == number):
return obj
2015-04-20 04:03:08 -04:00
raise ConstraintError("Resource not found: {}:{}".format(name, number))
def add_platform_command(self, command, **signals):
self.platform_commands.append((command, signals))
def get_io_signals(self):
r = set()
for resource, obj in self.matched:
if isinstance(obj, Signal):
r.add(obj)
else:
r.update(obj.flatten())
2015-04-20 04:03:08 -04:00
return r
def get_sig_constraints(self):
r = []
for resource, obj in self.matched:
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)
2015-04-20 04:03:08 -04:00
if has_subsignals:
for element in resource[2:]:
if isinstance(element, Subsignal):
sig = getattr(obj, element.name)
2015-04-20 04:03:08 -04:00
pins, others = _separate_pins(top_constraints +
element.constraints)
pins = self.connector_manager.resolve_identifiers(pins)
2015-04-20 04:03:08 -04:00
r.append((sig, pins, others,
(name, number, element.name)))
else:
pins, others = _separate_pins(top_constraints)
pins = self.connector_manager.resolve_identifiers(pins)
r.append((obj, pins, others, (name, number, None)))
2015-04-20 04:03:08 -04:00
return r
def get_platform_commands(self):
return self.platform_commands
2013-02-07 16:07:30 -05:00
2015-04-13 14:45:35 -04:00
2013-02-07 16:07:30 -05:00
class GenericPlatform:
def __init__(self, device, io, connectors=[], name=None):
self.device = device
self.constraint_manager = ConstraintManager(io, connectors)
if name is None:
name = self.__module__.split(".")[-1]
self.name = name
self.sources = set()
self.verilog_include_paths = set()
self.finalized = False
def request(self, *args, **kwargs):
return self.constraint_manager.request(*args, **kwargs)
def lookup_request(self, *args, **kwargs):
return self.constraint_manager.lookup_request(*args, **kwargs)
def add_period_constraint(self, clk, period):
raise NotImplementedError
def add_platform_command(self, *args, **kwargs):
return self.constraint_manager.add_platform_command(*args, **kwargs)
def add_extension(self, *args, **kwargs):
return self.constraint_manager.add_extension(*args, **kwargs)
def finalize(self, fragment, *args, **kwargs):
if self.finalized:
raise ConstraintError("Already finalized")
# if none exists, create a default clock domain and drive it
if not fragment.clock_domains:
if not hasattr(self, "default_clk_name"):
2015-04-20 04:03:08 -04:00
raise NotImplementedError(
"No default clock and no clock domain defined")
crg = CRG(self.request(self.default_clk_name))
fragment += crg.get_fragment()
2015-04-20 04:03:08 -04:00
self.do_finalize(fragment, *args, **kwargs)
self.finalized = True
def do_finalize(self, fragment, *args, **kwargs):
2015-04-20 04:03: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:
2015-04-20 04:03:08 -04:00
self.add_period_constraint(
self.lookup_request(self.default_clk_name),
self.default_clk_period)
except ConstraintError:
pass
def add_source(self, filename, language=None, library=None):
if language is None:
language = tools.language_by_filename(filename)
2015-04-20 04:03:08 -04:00
if language is None:
2015-04-13 15:21:30 -04:00
language = "verilog" # default to Verilog
2015-04-20 04:03:08 -04:00
if library is None:
library = "work" # default to work
2015-04-20 04:03:08 -04:00
filename = os.path.abspath(filename)
if sys.platform == "win32" or sys.platform == "cygwin":
filename = filename.replace("\\", "/")
self.sources.add((filename, language, library))
def add_sources(self, path, *filenames, language=None, library=None):
for f in filenames:
self.add_source(os.path.join(path, f), language, library)
def add_source_dir(self, path, recursive=True, library=None):
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, library)
def add_verilog_include_path(self, path):
path = os.path.abspath(path)
if sys.platform == "win32" or sys.platform == "cygwin":
path = path.replace("\\", "/")
self.verilog_include_paths.add(path)
def resolve_signals(self, vns):
# resolve signal names in constraints
sc = self.constraint_manager.get_sig_constraints()
2015-04-20 04:03:08 -04:00
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))
2015-04-20 04:03:08 -04:00
return named_sc, named_pc
def get_verilog(self, fragment, **kwargs):
2015-04-20 04:03:08 -04:00
return verilog.convert(
fragment,
self.constraint_manager.get_io_signals(),
create_clock_domains=False, **kwargs)
def get_edif(self, fragment, cell_library, vendor, device, **kwargs):
2015-04-20 04:03:08 -04:00
return edif.convert(
fragment,
self.constraint_manager.get_io_signals(),
cell_library, vendor, device, **kwargs)
def build(self, fragment):
raise NotImplementedError("GenericPlatform.build must be overloaded")
def build_cmdline(self, *args, **kwargs):
arg = sys.argv[1:]
if len(arg) % 2:
2015-04-20 04:03:08 -04:00
print("Missing value for option: {}".format(sys.argv[-1]))
sys.exit(1)
2015-04-20 04:03:08 -04:00
argdict = dict((k, autotype(v)) for k, v in zip(*[iter(arg)] * 2))
kwargs.update(argdict)
self.build(*args, **kwargs)
def create_programmer(self):
raise NotImplementedError