build.dsl: replace extras= with Attrs().
This change proved more tricky than expected due to downstream dependencies, so it also includes some secondary refactoring.
This commit is contained in:
parent
c52cd72d3e
commit
ab3f103e5a
|
@ -1,3 +1,3 @@
|
||||||
from .dsl import Pins, DiffPairs, Subsignal, Resource, Connector
|
from .dsl import Pins, DiffPairs, Attrs, Subsignal, Resource, Connector
|
||||||
from .res import ConstraintError
|
from .res import ResourceError
|
||||||
from .plat import Platform, TemplatedPlatform
|
from .plat import Platform, TemplatedPlatform
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["Pins", "DiffPairs", "Subsignal", "Resource", "Connector"]
|
__all__ = ["Pins", "DiffPairs", "Attrs", "Subsignal", "Resource", "Connector"]
|
||||||
|
|
||||||
|
|
||||||
class Pins:
|
class Pins:
|
||||||
|
@ -32,13 +32,15 @@ class Pins:
|
||||||
return iter(self.names)
|
return iter(self.names)
|
||||||
|
|
||||||
def map_names(self, mapping, resource):
|
def map_names(self, mapping, resource):
|
||||||
|
mapped_names = []
|
||||||
for name in self.names:
|
for name in self.names:
|
||||||
while ":" in name:
|
while ":" in name:
|
||||||
if name not in mapping:
|
if name not in mapping:
|
||||||
raise NameError("Resource {!r} refers to nonexistent connector pin {}"
|
raise NameError("Resource {!r} refers to nonexistent connector pin {}"
|
||||||
.format(resource, name))
|
.format(resource, name))
|
||||||
name = mapping[name]
|
name = mapping[name]
|
||||||
yield name
|
mapped_names.append(name)
|
||||||
|
return mapped_names
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "(pins {} {})".format(self.dir, " ".join(self.names))
|
return "(pins {} {})".format(self.dir, " ".join(self.names))
|
||||||
|
@ -67,65 +69,66 @@ class DiffPairs:
|
||||||
self.dir, " ".join(self.p.names), " ".join(self.n.names))
|
self.dir, " ".join(self.p.names), " ".join(self.n.names))
|
||||||
|
|
||||||
|
|
||||||
|
class Attrs(OrderedDict):
|
||||||
|
def __init__(self, **attrs):
|
||||||
|
for attr_key, attr_value in attrs.items():
|
||||||
|
if not isinstance(attr_value, str):
|
||||||
|
raise TypeError("Attribute value must be a string, not {!r}"
|
||||||
|
.format(attr_value))
|
||||||
|
|
||||||
|
super().__init__(**attrs)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "(attrs {})".format(" ".join("{}={}".format(k, v)
|
||||||
|
for k, v in self.items()))
|
||||||
|
|
||||||
|
|
||||||
class Subsignal:
|
class Subsignal:
|
||||||
def __init__(self, name, *io, extras=None):
|
def __init__(self, name, *args):
|
||||||
self.name = name
|
self.name = name
|
||||||
|
self.ios = []
|
||||||
|
self.attrs = Attrs()
|
||||||
|
|
||||||
if not io:
|
if not args:
|
||||||
raise TypeError("Missing I/O constraints")
|
raise ValueError("Missing I/O constraints")
|
||||||
for c in io:
|
for arg in args:
|
||||||
if not isinstance(c, (Pins, DiffPairs, Subsignal)):
|
if isinstance(arg, (Pins, DiffPairs)):
|
||||||
raise TypeError("I/O constraint must be one of Pins, DiffPairs or Subsignal, "
|
if not self.ios:
|
||||||
"not {!r}"
|
self.ios.append(arg)
|
||||||
.format(c))
|
else:
|
||||||
if isinstance(io[0], (Pins, DiffPairs)) and len(io) > 1:
|
raise TypeError("Pins and DiffPairs are incompatible with other location or "
|
||||||
raise TypeError("Pins and DiffPairs cannot be followed by more I/O constraints, but "
|
"subsignal constraints, but {!r} appears after {!r}"
|
||||||
"{!r} is followed by {!r}"
|
.format(arg, self.ios[-1]))
|
||||||
.format(io[0], io[1]))
|
elif isinstance(arg, Subsignal):
|
||||||
if isinstance(io[0], Subsignal):
|
if not self.ios or isinstance(self.ios[-1], Subsignal):
|
||||||
for c in io[1:]:
|
self.ios.append(arg)
|
||||||
if not isinstance(c, Subsignal):
|
else:
|
||||||
raise TypeError("A Subsignal can only be followed by more Subsignals, but "
|
raise TypeError("Subsignal is incompatible with location constraints, but "
|
||||||
"{!r} is followed by {!r}"
|
"{!r} appears after {!r}"
|
||||||
.format(io[0], c))
|
.format(arg, self.ios[-1]))
|
||||||
self.io = io
|
elif isinstance(arg, Attrs):
|
||||||
self.extras = {}
|
self.attrs.update(arg)
|
||||||
|
else:
|
||||||
if extras is not None:
|
raise TypeError("I/O constraint must be one of Pins, DiffPairs, Subsignal, "
|
||||||
if not isinstance(extras, dict):
|
"or Attrs, not {!r}"
|
||||||
raise TypeError("Extra constraints must be a dict, not {!r}"
|
.format(arg))
|
||||||
.format(extras))
|
|
||||||
for extra_key, extra_value in extras.items():
|
|
||||||
if not isinstance(extra_key, str):
|
|
||||||
raise TypeError("Extra constraint key must be a string, not {!r}"
|
|
||||||
.format(extra_key))
|
|
||||||
if not isinstance(extra_value, str):
|
|
||||||
raise TypeError("Extra constraint value must be a string, not {!r}"
|
|
||||||
.format(extra_value))
|
|
||||||
self.extras[extra_key] = extra_value
|
|
||||||
|
|
||||||
if isinstance(self.io[0], Subsignal):
|
|
||||||
for sub in self.io:
|
|
||||||
sub.extras.update(self.extras)
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "(subsignal {} {} {})".format(self.name,
|
return "(subsignal {} {} {})".format(self.name,
|
||||||
" ".join(map(repr, self.io)),
|
" ".join(map(repr, self.ios)),
|
||||||
" ".join("{}={}".format(k, v)
|
repr(self.attrs))
|
||||||
for k, v in self.extras.items()))
|
|
||||||
|
|
||||||
|
|
||||||
class Resource(Subsignal):
|
class Resource(Subsignal):
|
||||||
def __init__(self, name, number, *io, extras=None):
|
def __init__(self, name, number, *args):
|
||||||
super().__init__(name, *io, extras=extras)
|
super().__init__(name, *args)
|
||||||
|
|
||||||
self.number = number
|
self.number = number
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "(resource {} {} {} {})".format(self.name, self.number,
|
return "(resource {} {} {} {})".format(self.name, self.number,
|
||||||
" ".join(map(repr, self.io)),
|
" ".join(map(repr, self.ios)),
|
||||||
" ".join("{}={}".format(k, v)
|
repr(self.attrs))
|
||||||
for k, v in self.extras.items()))
|
|
||||||
|
|
||||||
|
|
||||||
class Connector:
|
class Connector:
|
||||||
|
|
|
@ -17,7 +17,7 @@ from .run import *
|
||||||
__all__ = ["Platform", "TemplatedPlatform"]
|
__all__ = ["Platform", "TemplatedPlatform"]
|
||||||
|
|
||||||
|
|
||||||
class Platform(ConstraintManager, metaclass=ABCMeta):
|
class Platform(ResourceManager, metaclass=ABCMeta):
|
||||||
resources = abstractproperty()
|
resources = abstractproperty()
|
||||||
connectors = abstractproperty()
|
connectors = abstractproperty()
|
||||||
clocks = abstractproperty()
|
clocks = abstractproperty()
|
||||||
|
@ -67,25 +67,25 @@ class Platform(ConstraintManager, metaclass=ABCMeta):
|
||||||
pin_fragment.flatten = True
|
pin_fragment.flatten = True
|
||||||
fragment.add_subfragment(pin_fragment, name="pin_{}".format(pin.name))
|
fragment.add_subfragment(pin_fragment, name="pin_{}".format(pin.name))
|
||||||
|
|
||||||
for pin, port, extras in self.iter_single_ended_pins():
|
for pin, port, attrs in self.iter_single_ended_pins():
|
||||||
if pin.dir == "i":
|
if pin.dir == "i":
|
||||||
add_pin_fragment(pin, self.get_input(pin, port, extras))
|
add_pin_fragment(pin, self.get_input(pin, port, attrs))
|
||||||
if pin.dir == "o":
|
if pin.dir == "o":
|
||||||
add_pin_fragment(pin, self.get_output(pin, port, extras))
|
add_pin_fragment(pin, self.get_output(pin, port, attrs))
|
||||||
if pin.dir == "oe":
|
if pin.dir == "oe":
|
||||||
add_pin_fragment(pin, self.get_tristate(pin, port, extras))
|
add_pin_fragment(pin, self.get_tristate(pin, port, attrs))
|
||||||
if pin.dir == "io":
|
if pin.dir == "io":
|
||||||
add_pin_fragment(pin, self.get_input_output(pin, port, extras))
|
add_pin_fragment(pin, self.get_input_output(pin, port, attrs))
|
||||||
|
|
||||||
for pin, p_port, n_port, extras in self.iter_differential_pins():
|
for pin, p_port, n_port, attrs in self.iter_differential_pins():
|
||||||
if pin.dir == "i":
|
if pin.dir == "i":
|
||||||
add_pin_fragment(pin, self.get_diff_input(pin, p_port, n_port, extras))
|
add_pin_fragment(pin, self.get_diff_input(pin, p_port, n_port, attrs))
|
||||||
if pin.dir == "o":
|
if pin.dir == "o":
|
||||||
add_pin_fragment(pin, self.get_diff_output(pin, p_port, n_port, extras))
|
add_pin_fragment(pin, self.get_diff_output(pin, p_port, n_port, attrs))
|
||||||
if pin.dir == "oe":
|
if pin.dir == "oe":
|
||||||
add_pin_fragment(pin, self.get_diff_tristate(pin, p_port, n_port, extras))
|
add_pin_fragment(pin, self.get_diff_tristate(pin, p_port, n_port, attrs))
|
||||||
if pin.dir == "io":
|
if pin.dir == "io":
|
||||||
add_pin_fragment(pin, self.get_diff_input_output(pin, p_port, n_port, extras))
|
add_pin_fragment(pin, self.get_diff_input_output(pin, p_port, n_port, attrs))
|
||||||
|
|
||||||
return self.toolchain_prepare(fragment, name, **kwargs)
|
return self.toolchain_prepare(fragment, name, **kwargs)
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ class Platform(ConstraintManager, metaclass=ABCMeta):
|
||||||
raise NotImplementedError("Platform {} does not support programming"
|
raise NotImplementedError("Platform {} does not support programming"
|
||||||
.format(self.__class__.__name__))
|
.format(self.__class__.__name__))
|
||||||
|
|
||||||
def _check_feature(self, feature, pin, extras, valid_xdrs, valid_extras):
|
def _check_feature(self, feature, pin, attrs, valid_xdrs, valid_attrs):
|
||||||
if not valid_xdrs:
|
if not valid_xdrs:
|
||||||
raise NotImplementedError("Platform {} does not support {}"
|
raise NotImplementedError("Platform {} does not support {}"
|
||||||
.format(self.__class__.__name__, feature))
|
.format(self.__class__.__name__, feature))
|
||||||
|
@ -112,29 +112,29 @@ class Platform(ConstraintManager, metaclass=ABCMeta):
|
||||||
raise NotImplementedError("Platform {} does not support {} for XDR {}"
|
raise NotImplementedError("Platform {} does not support {} for XDR {}"
|
||||||
.format(self.__class__.__name__, feature, pin.xdr))
|
.format(self.__class__.__name__, feature, pin.xdr))
|
||||||
|
|
||||||
if not valid_extras and extras:
|
if not valid_attrs and attrs:
|
||||||
raise NotImplementedError("Platform {} does not support extras for {}"
|
raise NotImplementedError("Platform {} does not support attributes for {}"
|
||||||
.format(self.__class__.__name__, feature))
|
.format(self.__class__.__name__, feature))
|
||||||
|
|
||||||
def get_input(self, pin, port, extras):
|
def get_input(self, pin, port, attrs):
|
||||||
self._check_feature("single-ended input", pin, extras,
|
self._check_feature("single-ended input", pin, attrs,
|
||||||
valid_xdrs=(0,), valid_extras=None)
|
valid_xdrs=(0,), valid_attrs=None)
|
||||||
|
|
||||||
m = Module()
|
m = Module()
|
||||||
m.d.comb += pin.i.eq(port)
|
m.d.comb += pin.i.eq(port)
|
||||||
return m
|
return m
|
||||||
|
|
||||||
def get_output(self, pin, port, extras):
|
def get_output(self, pin, port, attrs):
|
||||||
self._check_feature("single-ended output", pin, extras,
|
self._check_feature("single-ended output", pin, attrs,
|
||||||
valid_xdrs=(0,), valid_extras=None)
|
valid_xdrs=(0,), valid_attrs=None)
|
||||||
|
|
||||||
m = Module()
|
m = Module()
|
||||||
m.d.comb += port.eq(pin.o)
|
m.d.comb += port.eq(pin.o)
|
||||||
return m
|
return m
|
||||||
|
|
||||||
def get_tristate(self, pin, port, extras):
|
def get_tristate(self, pin, port, attrs):
|
||||||
self._check_feature("single-ended tristate", pin, extras,
|
self._check_feature("single-ended tristate", pin, attrs,
|
||||||
valid_xdrs=(0,), valid_extras=None)
|
valid_xdrs=(0,), valid_attrs=None)
|
||||||
|
|
||||||
m = Module()
|
m = Module()
|
||||||
m.submodules += Instance("$tribuf",
|
m.submodules += Instance("$tribuf",
|
||||||
|
@ -145,9 +145,9 @@ class Platform(ConstraintManager, metaclass=ABCMeta):
|
||||||
)
|
)
|
||||||
return m
|
return m
|
||||||
|
|
||||||
def get_input_output(self, pin, port, extras):
|
def get_input_output(self, pin, port, attrs):
|
||||||
self._check_feature("single-ended input/output", pin, extras,
|
self._check_feature("single-ended input/output", pin, attrs,
|
||||||
valid_xdrs=(0,), valid_extras=None)
|
valid_xdrs=(0,), valid_attrs=None)
|
||||||
|
|
||||||
m = Module()
|
m = Module()
|
||||||
m.submodules += Instance("$tribuf",
|
m.submodules += Instance("$tribuf",
|
||||||
|
@ -159,21 +159,21 @@ class Platform(ConstraintManager, metaclass=ABCMeta):
|
||||||
m.d.comb += pin.i.eq(port)
|
m.d.comb += pin.i.eq(port)
|
||||||
return m
|
return m
|
||||||
|
|
||||||
def get_diff_input(self, pin, p_port, n_port, extras):
|
def get_diff_input(self, pin, p_port, n_port, attrs):
|
||||||
self._check_feature("differential input", pin, extras,
|
self._check_feature("differential input", pin, attrs,
|
||||||
valid_xdrs=(), valid_extras=None)
|
valid_xdrs=(), valid_attrs=None)
|
||||||
|
|
||||||
def get_diff_output(self, pin, p_port, n_port, extras):
|
def get_diff_output(self, pin, p_port, n_port, attrs):
|
||||||
self._check_feature("differential output", pin, extras,
|
self._check_feature("differential output", pin, attrs,
|
||||||
valid_xdrs=(), valid_extras=None)
|
valid_xdrs=(), valid_attrs=None)
|
||||||
|
|
||||||
def get_diff_tristate(self, pin, p_port, n_port, extras):
|
def get_diff_tristate(self, pin, p_port, n_port, attrs):
|
||||||
self._check_feature("differential tristate", pin, extras,
|
self._check_feature("differential tristate", pin, attrs,
|
||||||
valid_xdrs=(), valid_extras=None)
|
valid_xdrs=(), valid_attrs=None)
|
||||||
|
|
||||||
def get_diff_input_output(self, pin, p_port, n_port, extras):
|
def get_diff_input_output(self, pin, p_port, n_port, attrs):
|
||||||
self._check_feature("differential input/output", pin, extras,
|
self._check_feature("differential input/output", pin, attrs,
|
||||||
valid_xdrs=(), valid_extras=None)
|
valid_xdrs=(), valid_attrs=None)
|
||||||
|
|
||||||
|
|
||||||
class TemplatedPlatform(Platform):
|
class TemplatedPlatform(Platform):
|
||||||
|
|
|
@ -7,21 +7,24 @@ from ..lib.io import *
|
||||||
from .dsl import *
|
from .dsl import *
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["ConstraintError", "ConstraintManager"]
|
__all__ = ["ResourceError", "ResourceManager"]
|
||||||
|
|
||||||
|
|
||||||
class ConstraintError(Exception):
|
class ResourceError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ConstraintManager:
|
class ResourceManager:
|
||||||
def __init__(self, resources, connectors, clocks):
|
def __init__(self, resources, connectors, clocks):
|
||||||
self.resources = OrderedDict()
|
self.resources = OrderedDict()
|
||||||
|
self._requested = OrderedDict()
|
||||||
|
|
||||||
self.connectors = OrderedDict()
|
self.connectors = OrderedDict()
|
||||||
|
self._conn_pins = OrderedDict()
|
||||||
|
|
||||||
self.clocks = OrderedDict()
|
self.clocks = OrderedDict()
|
||||||
|
|
||||||
self._mapping = OrderedDict()
|
# Constraint lists
|
||||||
self._requested = OrderedDict()
|
|
||||||
self._ports = []
|
self._ports = []
|
||||||
|
|
||||||
self.add_resources(resources)
|
self.add_resources(resources)
|
||||||
|
@ -50,36 +53,36 @@ class ConstraintManager:
|
||||||
self.connectors[conn.name, conn.number] = conn
|
self.connectors[conn.name, conn.number] = conn
|
||||||
|
|
||||||
for conn_pin, plat_pin in conn:
|
for conn_pin, plat_pin in conn:
|
||||||
assert conn_pin not in self._mapping
|
assert conn_pin not in self._conn_pins
|
||||||
self._mapping[conn_pin] = plat_pin
|
self._conn_pins[conn_pin] = plat_pin
|
||||||
|
|
||||||
def add_clock(self, name, number, frequency):
|
def add_clock(self, name, number, frequency):
|
||||||
resource = self.lookup(name, number)
|
resource = self.lookup(name, number)
|
||||||
if isinstance(resource.io[0], Subsignal):
|
if isinstance(resource.ios[0], Subsignal):
|
||||||
raise TypeError("Cannot constrain frequency of resource {}#{} because it has "
|
raise TypeError("Cannot constrain frequency of resource {}#{} because it has "
|
||||||
"subsignals"
|
"subsignals"
|
||||||
.format(resource.name, resource.number, frequency))
|
.format(resource.name, resource.number, frequency))
|
||||||
if (resource.name, resource.number) in self.clocks:
|
if (resource.name, resource.number) in self.clocks:
|
||||||
other = self.clocks[resource.name, resource.number]
|
other = self.clocks[resource.name, resource.number]
|
||||||
raise ConstraintError("Resource {}#{} is already constrained to a frequency of "
|
raise ResourceError("Resource {}#{} is already constrained to a frequency of "
|
||||||
"{:f} MHz"
|
"{:f} MHz"
|
||||||
.format(resource.name, resource.number, other / 1e6))
|
.format(resource.name, resource.number, other / 1e6))
|
||||||
self.clocks[resource.name, resource.number] = frequency
|
self.clocks[resource.name, resource.number] = frequency
|
||||||
|
|
||||||
def lookup(self, name, number=0):
|
def lookup(self, name, number=0):
|
||||||
if (name, number) not in self.resources:
|
if (name, number) not in self.resources:
|
||||||
raise ConstraintError("Resource {}#{} does not exist"
|
raise ResourceError("Resource {}#{} does not exist"
|
||||||
.format(name, number))
|
.format(name, number))
|
||||||
return self.resources[name, number]
|
return self.resources[name, number]
|
||||||
|
|
||||||
def request(self, name, number=0, *, dir=None, xdr=None):
|
def request(self, name, number=0, *, dir=None, xdr=None):
|
||||||
resource = self.lookup(name, number)
|
resource = self.lookup(name, number)
|
||||||
if (resource.name, resource.number) in self._requested:
|
if (resource.name, resource.number) in self._requested:
|
||||||
raise ConstraintError("Resource {}#{} has already been requested"
|
raise ResourceError("Resource {}#{} has already been requested"
|
||||||
.format(name, number))
|
.format(name, number))
|
||||||
|
|
||||||
def merge_options(subsignal, dir, xdr):
|
def merge_options(subsignal, dir, xdr):
|
||||||
if isinstance(subsignal.io[0], Subsignal):
|
if isinstance(subsignal.ios[0], Subsignal):
|
||||||
if dir is None:
|
if dir is None:
|
||||||
dir = dict()
|
dir = dict()
|
||||||
if xdr is None:
|
if xdr is None:
|
||||||
|
@ -92,52 +95,54 @@ class ConstraintManager:
|
||||||
raise TypeError("Data rate must be a dict, not {!r}, because {!r} "
|
raise TypeError("Data rate must be a dict, not {!r}, because {!r} "
|
||||||
"has subsignals"
|
"has subsignals"
|
||||||
.format(xdr, subsignal))
|
.format(xdr, subsignal))
|
||||||
for sub in subsignal.io:
|
for sub in subsignal.ios:
|
||||||
sub_dir = dir.get(sub.name, None)
|
sub_dir = dir.get(sub.name, None)
|
||||||
sub_xdr = xdr.get(sub.name, None)
|
sub_xdr = xdr.get(sub.name, None)
|
||||||
dir[sub.name], xdr[sub.name] = merge_options(sub, sub_dir, sub_xdr)
|
dir[sub.name], xdr[sub.name] = merge_options(sub, sub_dir, sub_xdr)
|
||||||
else:
|
else:
|
||||||
if dir is None:
|
if dir is None:
|
||||||
dir = subsignal.io[0].dir
|
dir = subsignal.ios[0].dir
|
||||||
if xdr is None:
|
if xdr is None:
|
||||||
xdr = 0
|
xdr = 0
|
||||||
if dir not in ("i", "o", "oe", "io", "-"):
|
if dir not in ("i", "o", "oe", "io", "-"):
|
||||||
raise TypeError("Direction must be one of \"i\", \"o\", \"oe\", \"io\", "
|
raise TypeError("Direction must be one of \"i\", \"o\", \"oe\", \"io\", "
|
||||||
"or \"-\", not {!r}"
|
"or \"-\", not {!r}"
|
||||||
.format(dir))
|
.format(dir))
|
||||||
if dir != subsignal.io[0].dir and not (subsignal.io[0].dir == "io" or dir == "-"):
|
if dir != subsignal.ios[0].dir and \
|
||||||
|
not (subsignal.ios[0].dir == "io" or dir == "-"):
|
||||||
raise ValueError("Direction of {!r} cannot be changed from \"{}\" to \"{}\"; "
|
raise ValueError("Direction of {!r} cannot be changed from \"{}\" to \"{}\"; "
|
||||||
"direction can be changed from \"io\" to \"i\", \"o\", or "
|
"direction can be changed from \"io\" to \"i\", \"o\", or "
|
||||||
"\"oe\", or from anything to \"-\""
|
"\"oe\", or from anything to \"-\""
|
||||||
.format(subsignal.io[0], subsignal.io[0].dir, dir))
|
.format(subsignal.ios[0], subsignal.ios[0].dir, dir))
|
||||||
if not isinstance(xdr, int) or xdr < 0:
|
if not isinstance(xdr, int) or xdr < 0:
|
||||||
raise ValueError("Data rate of {!r} must be a non-negative integer, not {!r}"
|
raise ValueError("Data rate of {!r} must be a non-negative integer, not {!r}"
|
||||||
.format(subsignal.io[0], xdr))
|
.format(subsignal.ios[0], xdr))
|
||||||
return dir, xdr
|
return dir, xdr
|
||||||
|
|
||||||
def resolve(subsignal, dir, xdr, name):
|
def resolve(resource, dir, xdr, name, attrs):
|
||||||
if isinstance(subsignal.io[0], Subsignal):
|
if isinstance(resource.ios[0], Subsignal):
|
||||||
fields = OrderedDict()
|
fields = OrderedDict()
|
||||||
for sub in subsignal.io:
|
for sub in resource.ios:
|
||||||
fields[sub.name] = resolve(sub, dir[sub.name], xdr[sub.name],
|
fields[sub.name] = resolve(sub, dir[sub.name], xdr[sub.name],
|
||||||
name="{}__{}".format(name, sub.name))
|
name="{}__{}".format(name, sub.name),
|
||||||
|
attrs={**attrs, **sub.attrs})
|
||||||
return Record([
|
return Record([
|
||||||
(f_name, f.layout) for (f_name, f) in fields.items()
|
(f_name, f.layout) for (f_name, f) in fields.items()
|
||||||
], fields=fields, name=name)
|
], fields=fields, name=name)
|
||||||
|
|
||||||
elif isinstance(subsignal.io[0], (Pins, DiffPairs)):
|
elif isinstance(resource.ios[0], (Pins, DiffPairs)):
|
||||||
phys = subsignal.io[0]
|
phys = resource.ios[0]
|
||||||
if isinstance(phys, Pins):
|
if isinstance(phys, Pins):
|
||||||
port = Record([("io", len(phys))], name=name)
|
port = Record([("io", len(phys))], name=name)
|
||||||
if isinstance(phys, DiffPairs):
|
if isinstance(phys, DiffPairs):
|
||||||
port = Record([("p", len(phys)),
|
port = Record([("p", len(phys)),
|
||||||
("n", len(phys))], name=name)
|
("n", len(phys))], name=name)
|
||||||
if dir == "-":
|
if dir == "-":
|
||||||
self._ports.append((subsignal, None, port))
|
self._ports.append((resource, None, port, attrs))
|
||||||
return port
|
return port
|
||||||
else:
|
else:
|
||||||
pin = Pin(len(phys), dir, xdr, name=name)
|
pin = Pin(len(phys), dir, xdr, name=name)
|
||||||
self._ports.append((subsignal, pin, port))
|
self._ports.append((resource, pin, port, attrs))
|
||||||
return pin
|
return pin
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
@ -145,51 +150,61 @@ class ConstraintManager:
|
||||||
|
|
||||||
value = resolve(resource,
|
value = resolve(resource,
|
||||||
*merge_options(resource, dir, xdr),
|
*merge_options(resource, dir, xdr),
|
||||||
name="{}_{}".format(resource.name, resource.number))
|
name="{}_{}".format(resource.name, resource.number),
|
||||||
|
attrs=resource.attrs)
|
||||||
self._requested[resource.name, resource.number] = value
|
self._requested[resource.name, resource.number] = value
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def iter_single_ended_pins(self):
|
def iter_single_ended_pins(self):
|
||||||
for res, pin, port in self._ports:
|
for res, pin, port, attrs in self._ports:
|
||||||
if pin is None:
|
if pin is None:
|
||||||
continue
|
continue
|
||||||
if isinstance(res.io[0], Pins):
|
if isinstance(res.ios[0], Pins):
|
||||||
yield pin, port.io, res.extras
|
yield pin, port.io, attrs
|
||||||
|
|
||||||
def iter_differential_pins(self):
|
def iter_differential_pins(self):
|
||||||
for res, pin, port in self._ports:
|
for res, pin, port, attrs in self._ports:
|
||||||
if pin is None:
|
if pin is None:
|
||||||
continue
|
continue
|
||||||
if isinstance(res.io[0], DiffPairs):
|
if isinstance(res.ios[0], DiffPairs):
|
||||||
yield pin, port.p, port.n, res.extras
|
yield pin, port.p, port.n, attrs
|
||||||
|
|
||||||
|
def should_skip_port_component(self, port, attrs, component):
|
||||||
|
return False
|
||||||
|
|
||||||
def iter_ports(self):
|
def iter_ports(self):
|
||||||
for res, pin, port in self._ports:
|
for res, pin, port, attrs in self._ports:
|
||||||
if isinstance(res.io[0], Pins):
|
if isinstance(res.ios[0], Pins):
|
||||||
yield port.io
|
if not self.should_skip_port_component(port, attrs, "io"):
|
||||||
elif isinstance(res.io[0], DiffPairs):
|
yield port.io
|
||||||
yield port.p
|
elif isinstance(res.ios[0], DiffPairs):
|
||||||
yield port.n
|
if not self.should_skip_port_component(port, attrs, "p"):
|
||||||
|
yield port.p
|
||||||
|
if not self.should_skip_port_component(port, attrs, "n"):
|
||||||
|
yield port.n
|
||||||
else:
|
else:
|
||||||
assert False
|
assert False
|
||||||
|
|
||||||
def iter_port_constraints(self):
|
def iter_port_constraints(self):
|
||||||
for res, pin, port in self._ports:
|
for res, pin, port, attrs in self._ports:
|
||||||
if isinstance(res.io[0], Pins):
|
if isinstance(res.ios[0], Pins):
|
||||||
yield port.io.name, list(res.io[0].map_names(self._mapping, res)), res.extras
|
if not self.should_skip_port_component(port, attrs, "io"):
|
||||||
elif isinstance(res.io[0], DiffPairs):
|
yield port.io.name, res.ios[0].map_names(self._conn_pins, res), attrs
|
||||||
yield port.p.name, list(res.io[0].p.map_names(self._mapping, res)), res.extras
|
elif isinstance(res.ios[0], DiffPairs):
|
||||||
yield port.n.name, list(res.io[0].n.map_names(self._mapping, res)), res.extras
|
if not self.should_skip_port_component(port, attrs, "p"):
|
||||||
|
yield port.p.name, res.ios[0].p.map_names(self._conn_pins, res), attrs
|
||||||
|
if not self.should_skip_port_component(port, attrs, "n"):
|
||||||
|
yield port.n.name, res.ios[0].n.map_names(self._conn_pins, res), attrs
|
||||||
else:
|
else:
|
||||||
assert False
|
assert False
|
||||||
|
|
||||||
def iter_port_constraints_bits(self):
|
def iter_port_constraints_bits(self):
|
||||||
for port_name, pin_names, extras in self.iter_port_constraints():
|
for port_name, pin_names, attrs in self.iter_port_constraints():
|
||||||
if len(pin_names) == 1:
|
if len(pin_names) == 1:
|
||||||
yield port_name, pin_names[0], extras
|
yield port_name, pin_names[0], attrs
|
||||||
else:
|
else:
|
||||||
for bit, pin_name in enumerate(pin_names):
|
for bit, pin_name in enumerate(pin_names):
|
||||||
yield "{}[{}]".format(port_name, bit), pin_name, extras
|
yield "{}[{}]".format(port_name, bit), pin_name, attrs
|
||||||
|
|
||||||
def iter_clock_constraints(self):
|
def iter_clock_constraints(self):
|
||||||
for name, number in self.clocks.keys() & self._requested.keys():
|
for name, number in self.clocks.keys() & self._requested.keys():
|
||||||
|
@ -197,12 +212,12 @@ class ConstraintManager:
|
||||||
period = self.clocks[name, number]
|
period = self.clocks[name, number]
|
||||||
pin = self._requested[name, number]
|
pin = self._requested[name, number]
|
||||||
if pin.dir == "io":
|
if pin.dir == "io":
|
||||||
raise ConstraintError("Cannot constrain frequency of resource {}#{} because "
|
raise ResourceError("Cannot constrain frequency of resource {}#{} because "
|
||||||
"it has been requested as a tristate buffer"
|
"it has been requested as a tristate buffer"
|
||||||
.format(name, number))
|
.format(name, number))
|
||||||
if isinstance(resource.io[0], Pins):
|
if isinstance(resource.ios[0], Pins):
|
||||||
port_name = "{}__io".format(pin.name)
|
port_name = "{}__io".format(pin.name)
|
||||||
elif isinstance(resource.io[0], DiffPairs):
|
elif isinstance(resource.ios[0], DiffPairs):
|
||||||
port_name = "{}__p".format(pin.name)
|
port_name = "{}__p".format(pin.name)
|
||||||
else:
|
else:
|
||||||
assert False
|
assert False
|
||||||
|
|
|
@ -23,7 +23,7 @@ class PinsTestCase(FHDLTestCase):
|
||||||
"pmod_0:1": "A1",
|
"pmod_0:1": "A1",
|
||||||
"pmod_0:2": "A2",
|
"pmod_0:2": "A2",
|
||||||
}
|
}
|
||||||
self.assertEqual(list(p.map_names(mapping, p)), ["A0", "A1", "A2"])
|
self.assertEqual(p.map_names(mapping, p), ["A0", "A1", "A2"])
|
||||||
|
|
||||||
def test_map_names_recur(self):
|
def test_map_names_recur(self):
|
||||||
p = Pins("0", conn=("pmod", 0))
|
p = Pins("0", conn=("pmod", 0))
|
||||||
|
@ -31,7 +31,7 @@ class PinsTestCase(FHDLTestCase):
|
||||||
"pmod_0:0": "ext_0:1",
|
"pmod_0:0": "ext_0:1",
|
||||||
"ext_0:1": "A1",
|
"ext_0:1": "A1",
|
||||||
}
|
}
|
||||||
self.assertEqual(list(p.map_names(mapping, p)), ["A1"])
|
self.assertEqual(p.map_names(mapping, p), ["A1"])
|
||||||
|
|
||||||
def test_wrong_names(self):
|
def test_wrong_names(self):
|
||||||
with self.assertRaises(TypeError,
|
with self.assertRaises(TypeError,
|
||||||
|
@ -51,7 +51,7 @@ class PinsTestCase(FHDLTestCase):
|
||||||
with self.assertRaises(NameError,
|
with self.assertRaises(NameError,
|
||||||
msg="Resource (pins io pmod_0:0 pmod_0:1 pmod_0:2) refers to nonexistent "
|
msg="Resource (pins io pmod_0:0 pmod_0:1 pmod_0:2) refers to nonexistent "
|
||||||
"connector pin pmod_0:1"):
|
"connector pin pmod_0:1"):
|
||||||
list(p.map_names(mapping, p))
|
p.map_names(mapping, p)
|
||||||
|
|
||||||
|
|
||||||
class DiffPairsTestCase(FHDLTestCase):
|
class DiffPairsTestCase(FHDLTestCase):
|
||||||
|
@ -84,81 +84,90 @@ class DiffPairsTestCase(FHDLTestCase):
|
||||||
dp = DiffPairs("A0", "B0 B1")
|
dp = DiffPairs("A0", "B0 B1")
|
||||||
|
|
||||||
|
|
||||||
|
class AttrsTestCase(FHDLTestCase):
|
||||||
|
def test_basic(self):
|
||||||
|
a = Attrs(IO_STANDARD="LVCMOS33", PULLUP="1")
|
||||||
|
self.assertEqual(a["IO_STANDARD"], "LVCMOS33")
|
||||||
|
self.assertEqual(repr(a), "(attrs IO_STANDARD=LVCMOS33 PULLUP=1)")
|
||||||
|
|
||||||
|
def test_wrong_value(self):
|
||||||
|
with self.assertRaises(TypeError,
|
||||||
|
msg="Attribute value must be a string, not 1"):
|
||||||
|
a = Attrs(FOO=1)
|
||||||
|
|
||||||
|
|
||||||
class SubsignalTestCase(FHDLTestCase):
|
class SubsignalTestCase(FHDLTestCase):
|
||||||
def test_basic_pins(self):
|
def test_basic_pins(self):
|
||||||
s = Subsignal("a", Pins("A0"), extras={"IOSTANDARD": "LVCMOS33"})
|
s = Subsignal("a", Pins("A0"), Attrs(IOSTANDARD="LVCMOS33"))
|
||||||
self.assertEqual(repr(s), "(subsignal a (pins io A0) IOSTANDARD=LVCMOS33)")
|
self.assertEqual(repr(s),
|
||||||
|
"(subsignal a (pins io A0) (attrs IOSTANDARD=LVCMOS33))")
|
||||||
|
|
||||||
def test_basic_diffpairs(self):
|
def test_basic_diffpairs(self):
|
||||||
s = Subsignal("a", DiffPairs("A0", "B0"))
|
s = Subsignal("a", DiffPairs("A0", "B0"))
|
||||||
self.assertEqual(repr(s), "(subsignal a (diffpairs io (p A0) (n B0)) )")
|
self.assertEqual(repr(s),
|
||||||
|
"(subsignal a (diffpairs io (p A0) (n B0)) (attrs ))")
|
||||||
|
|
||||||
def test_basic_subsignals(self):
|
def test_basic_subsignals(self):
|
||||||
s = Subsignal("a",
|
s = Subsignal("a",
|
||||||
Subsignal("b", Pins("A0")),
|
Subsignal("b", Pins("A0")),
|
||||||
Subsignal("c", Pins("A1")))
|
Subsignal("c", Pins("A1")))
|
||||||
self.assertEqual(repr(s),
|
self.assertEqual(repr(s),
|
||||||
"(subsignal a (subsignal b (pins io A0) ) (subsignal c (pins io A1) ) )")
|
"(subsignal a (subsignal b (pins io A0) (attrs )) "
|
||||||
|
"(subsignal c (pins io A1) (attrs )) (attrs ))")
|
||||||
|
|
||||||
def test_extras(self):
|
def test_attrs(self):
|
||||||
s = Subsignal("a",
|
s = Subsignal("a",
|
||||||
Subsignal("b", Pins("A0")),
|
Subsignal("b", Pins("A0")),
|
||||||
Subsignal("c", Pins("A0"), extras={"SLEW": "FAST"}),
|
Subsignal("c", Pins("A0"), Attrs(SLEW="FAST")),
|
||||||
extras={"IOSTANDARD": "LVCMOS33"})
|
Attrs(IOSTANDARD="LVCMOS33"))
|
||||||
self.assertEqual(s.extras, {"IOSTANDARD": "LVCMOS33"})
|
self.assertEqual(s.attrs, {"IOSTANDARD": "LVCMOS33"})
|
||||||
self.assertEqual(s.io[0].extras, {"IOSTANDARD": "LVCMOS33"})
|
self.assertEqual(s.ios[0].attrs, {})
|
||||||
self.assertEqual(s.io[1].extras, {"SLEW": "FAST", "IOSTANDARD": "LVCMOS33"})
|
self.assertEqual(s.ios[1].attrs, {"SLEW": "FAST"})
|
||||||
|
|
||||||
def test_empty_io(self):
|
def test_attrs_many(self):
|
||||||
with self.assertRaises(TypeError, msg="Missing I/O constraints"):
|
s = Subsignal("a", Pins("A0"), Attrs(SLEW="FAST"), Attrs(PULLUP="1"))
|
||||||
|
self.assertEqual(s.attrs, {"SLEW": "FAST", "PULLUP": "1"})
|
||||||
|
|
||||||
|
def test_wrong_empty_io(self):
|
||||||
|
with self.assertRaises(ValueError, msg="Missing I/O constraints"):
|
||||||
s = Subsignal("a")
|
s = Subsignal("a")
|
||||||
|
|
||||||
def test_wrong_io(self):
|
def test_wrong_io(self):
|
||||||
with self.assertRaises(TypeError,
|
with self.assertRaises(TypeError,
|
||||||
msg="I/O constraint must be one of Pins, DiffPairs or Subsignal, not 'wrong'"):
|
msg="I/O constraint must be one of Pins, DiffPairs, Subsignal, or Attrs, "
|
||||||
|
"not 'wrong'"):
|
||||||
s = Subsignal("a", "wrong")
|
s = Subsignal("a", "wrong")
|
||||||
|
|
||||||
def test_wrong_pins(self):
|
def test_wrong_pins(self):
|
||||||
with self.assertRaises(TypeError,
|
with self.assertRaises(TypeError,
|
||||||
msg="Pins and DiffPairs cannot be followed by more I/O constraints, but "
|
msg="Pins and DiffPairs are incompatible with other location or subsignal "
|
||||||
"(pins io A0) is followed by (pins io A1)"):
|
"constraints, but (pins io A1) appears after (pins io A0)"):
|
||||||
s = Subsignal("a", Pins("A0"), Pins("A1"))
|
s = Subsignal("a", Pins("A0"), Pins("A1"))
|
||||||
|
|
||||||
def test_wrong_diffpairs(self):
|
def test_wrong_diffpairs(self):
|
||||||
with self.assertRaises(TypeError,
|
with self.assertRaises(TypeError,
|
||||||
msg="Pins and DiffPairs cannot be followed by more I/O constraints, but "
|
msg="Pins and DiffPairs are incompatible with other location or subsignal "
|
||||||
"(diffpairs io (p A0) (n B0)) is followed by "
|
"constraints, but (pins io A1) appears after (diffpairs io (p A0) (n B0))"):
|
||||||
"(pins io A1)"):
|
|
||||||
s = Subsignal("a", DiffPairs("A0", "B0"), Pins("A1"))
|
s = Subsignal("a", DiffPairs("A0", "B0"), Pins("A1"))
|
||||||
|
|
||||||
def test_wrong_subsignals(self):
|
def test_wrong_subsignals(self):
|
||||||
with self.assertRaises(TypeError,
|
with self.assertRaises(TypeError,
|
||||||
msg="A Subsignal can only be followed by more Subsignals, but "
|
msg="Pins and DiffPairs are incompatible with other location or subsignal "
|
||||||
"(subsignal b (pins io A0) ) is followed by (pins io B0)"):
|
"constraints, but (pins io B0) appears after (subsignal b (pins io A0) "
|
||||||
|
"(attrs ))"):
|
||||||
s = Subsignal("a", Subsignal("b", Pins("A0")), Pins("B0"))
|
s = Subsignal("a", Subsignal("b", Pins("A0")), Pins("B0"))
|
||||||
|
|
||||||
def test_wrong_extras(self):
|
|
||||||
with self.assertRaises(TypeError,
|
|
||||||
msg="Extra constraints must be a dict, not [(pins io B0)]"):
|
|
||||||
s = Subsignal("a", Pins("A0"), extras=[Pins("B0")])
|
|
||||||
with self.assertRaises(TypeError,
|
|
||||||
msg="Extra constraint key must be a string, not 1"):
|
|
||||||
s = Subsignal("a", Pins("A0"), extras={1: 2})
|
|
||||||
with self.assertRaises(TypeError,
|
|
||||||
msg="Extra constraint value must be a string, not 2"):
|
|
||||||
s = Subsignal("a", Pins("A0"), extras={"1": 2})
|
|
||||||
|
|
||||||
|
|
||||||
class ResourceTestCase(FHDLTestCase):
|
class ResourceTestCase(FHDLTestCase):
|
||||||
def test_basic(self):
|
def test_basic(self):
|
||||||
r = Resource("serial", 0,
|
r = Resource("serial", 0,
|
||||||
Subsignal("tx", Pins("A0", dir="o")),
|
Subsignal("tx", Pins("A0", dir="o")),
|
||||||
Subsignal("rx", Pins("A1", dir="i")),
|
Subsignal("rx", Pins("A1", dir="i")),
|
||||||
extras={"IOSTANDARD": "LVCMOS33"})
|
Attrs(IOSTANDARD="LVCMOS33"))
|
||||||
self.assertEqual(repr(r), "(resource serial 0"
|
self.assertEqual(repr(r), "(resource serial 0"
|
||||||
" (subsignal tx (pins o A0) IOSTANDARD=LVCMOS33)"
|
" (subsignal tx (pins o A0) (attrs ))"
|
||||||
" (subsignal rx (pins i A1) IOSTANDARD=LVCMOS33)"
|
" (subsignal rx (pins i A1) (attrs ))"
|
||||||
" IOSTANDARD=LVCMOS33)")
|
" (attrs IOSTANDARD=LVCMOS33))")
|
||||||
|
|
||||||
|
|
||||||
class ConnectorTestCase(FHDLTestCase):
|
class ConnectorTestCase(FHDLTestCase):
|
||||||
|
|
|
@ -6,7 +6,7 @@ from ..build.res import *
|
||||||
from .tools import *
|
from .tools import *
|
||||||
|
|
||||||
|
|
||||||
class ConstraintManagerTestCase(FHDLTestCase):
|
class ResourceManagerTestCase(FHDLTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.resources = [
|
self.resources = [
|
||||||
Resource("clk100", 0, DiffPairs("H1", "H2", dir="i")),
|
Resource("clk100", 0, DiffPairs("H1", "H2", dir="i")),
|
||||||
|
@ -20,14 +20,14 @@ class ConstraintManagerTestCase(FHDLTestCase):
|
||||||
self.connectors = [
|
self.connectors = [
|
||||||
Connector("pmod", 0, "B0 B1 B2 B3 - -"),
|
Connector("pmod", 0, "B0 B1 B2 B3 - -"),
|
||||||
]
|
]
|
||||||
self.cm = ConstraintManager(self.resources, self.connectors, [])
|
self.cm = ResourceManager(self.resources, self.connectors, [])
|
||||||
|
|
||||||
def test_basic(self):
|
def test_basic(self):
|
||||||
self.clocks = [
|
self.clocks = [
|
||||||
("clk100", 100),
|
("clk100", 100),
|
||||||
(("clk50", 0), 50),
|
(("clk50", 0), 50),
|
||||||
]
|
]
|
||||||
self.cm = ConstraintManager(self.resources, self.connectors, self.clocks)
|
self.cm = ResourceManager(self.resources, self.connectors, self.clocks)
|
||||||
self.assertEqual(self.cm.resources, {
|
self.assertEqual(self.cm.resources, {
|
||||||
("clk100", 0): self.resources[0],
|
("clk100", 0): self.resources[0],
|
||||||
("clk50", 0): self.resources[1],
|
("clk50", 0): self.resources[1],
|
||||||
|
@ -177,8 +177,8 @@ class ConstraintManagerTestCase(FHDLTestCase):
|
||||||
|
|
||||||
def test_wrong_resources_duplicate(self):
|
def test_wrong_resources_duplicate(self):
|
||||||
with self.assertRaises(NameError,
|
with self.assertRaises(NameError,
|
||||||
msg="Trying to add (resource user_led 0 (pins o A1) ), but "
|
msg="Trying to add (resource user_led 0 (pins o A1) (attrs )), but "
|
||||||
"(resource user_led 0 (pins o A0) ) has the same name and number"):
|
"(resource user_led 0 (pins o A0) (attrs )) has the same name and number"):
|
||||||
self.cm.add_resources([Resource("user_led", 0, Pins("A1", dir="o"))])
|
self.cm.add_resources([Resource("user_led", 0, Pins("A1", dir="o"))])
|
||||||
|
|
||||||
def test_wrong_connectors(self):
|
def test_wrong_connectors(self):
|
||||||
|
@ -192,7 +192,7 @@ class ConstraintManagerTestCase(FHDLTestCase):
|
||||||
self.cm.add_connectors([Connector("pmod", 0, "1 2")])
|
self.cm.add_connectors([Connector("pmod", 0, "1 2")])
|
||||||
|
|
||||||
def test_wrong_lookup(self):
|
def test_wrong_lookup(self):
|
||||||
with self.assertRaises(ConstraintError,
|
with self.assertRaises(ResourceError,
|
||||||
msg="Resource user_led#1 does not exist"):
|
msg="Resource user_led#1 does not exist"):
|
||||||
r = self.cm.lookup("user_led", 1)
|
r = self.cm.lookup("user_led", 1)
|
||||||
|
|
||||||
|
@ -203,7 +203,7 @@ class ConstraintManagerTestCase(FHDLTestCase):
|
||||||
self.cm.add_clock("i2c", 0, 10e6)
|
self.cm.add_clock("i2c", 0, 10e6)
|
||||||
|
|
||||||
def test_wrong_frequency_tristate(self):
|
def test_wrong_frequency_tristate(self):
|
||||||
with self.assertRaises(ConstraintError,
|
with self.assertRaises(ResourceError,
|
||||||
msg="Cannot constrain frequency of resource clk50#0 because "
|
msg="Cannot constrain frequency of resource clk50#0 because "
|
||||||
"it has been requested as a tristate buffer"):
|
"it has been requested as a tristate buffer"):
|
||||||
self.cm.add_clock("clk50", 0, 20e6)
|
self.cm.add_clock("clk50", 0, 20e6)
|
||||||
|
@ -211,13 +211,13 @@ class ConstraintManagerTestCase(FHDLTestCase):
|
||||||
list(self.cm.iter_clock_constraints())
|
list(self.cm.iter_clock_constraints())
|
||||||
|
|
||||||
def test_wrong_frequency_duplicate(self):
|
def test_wrong_frequency_duplicate(self):
|
||||||
with self.assertRaises(ConstraintError,
|
with self.assertRaises(ResourceError,
|
||||||
msg="Resource clk100#0 is already constrained to a frequency of 10.000000 MHz"):
|
msg="Resource clk100#0 is already constrained to a frequency of 10.000000 MHz"):
|
||||||
self.cm.add_clock("clk100", 0, 10e6)
|
self.cm.add_clock("clk100", 0, 10e6)
|
||||||
self.cm.add_clock("clk100", 0, 5e6)
|
self.cm.add_clock("clk100", 0, 5e6)
|
||||||
|
|
||||||
def test_wrong_request_duplicate(self):
|
def test_wrong_request_duplicate(self):
|
||||||
with self.assertRaises(ConstraintError,
|
with self.assertRaises(ResourceError,
|
||||||
msg="Resource user_led#0 has already been requested"):
|
msg="Resource user_led#0 has already been requested"):
|
||||||
self.cm.request("user_led", 0)
|
self.cm.request("user_led", 0)
|
||||||
self.cm.request("user_led", 0)
|
self.cm.request("user_led", 0)
|
||||||
|
@ -238,7 +238,8 @@ class ConstraintManagerTestCase(FHDLTestCase):
|
||||||
def test_wrong_request_with_dir_dict(self):
|
def test_wrong_request_with_dir_dict(self):
|
||||||
with self.assertRaises(TypeError,
|
with self.assertRaises(TypeError,
|
||||||
msg="Directions must be a dict, not 'i', because (resource i2c 0 (subsignal scl "
|
msg="Directions must be a dict, not 'i', because (resource i2c 0 (subsignal scl "
|
||||||
"(pins o N10) ) (subsignal sda (pins io N11) ) ) has subsignals"):
|
"(pins o N10) (attrs )) (subsignal sda (pins io N11) (attrs )) (attrs )) "
|
||||||
|
"has subsignals"):
|
||||||
i2c = self.cm.request("i2c", 0, dir="i")
|
i2c = self.cm.request("i2c", 0, dir="i")
|
||||||
|
|
||||||
def test_wrong_request_with_wrong_xdr(self):
|
def test_wrong_request_with_wrong_xdr(self):
|
||||||
|
@ -249,5 +250,6 @@ class ConstraintManagerTestCase(FHDLTestCase):
|
||||||
def test_wrong_request_with_xdr_dict(self):
|
def test_wrong_request_with_xdr_dict(self):
|
||||||
with self.assertRaises(TypeError,
|
with self.assertRaises(TypeError,
|
||||||
msg="Data rate must be a dict, not 2, because (resource i2c 0 (subsignal scl "
|
msg="Data rate must be a dict, not 2, because (resource i2c 0 (subsignal scl "
|
||||||
"(pins o N10) ) (subsignal sda (pins io N11) ) ) has subsignals"):
|
"(pins o N10) (attrs )) (subsignal sda (pins io N11) (attrs )) (attrs )) "
|
||||||
|
"has subsignals"):
|
||||||
i2c = self.cm.request("i2c", 0, xdr=2)
|
i2c = self.cm.request("i2c", 0, xdr=2)
|
||||||
|
|
102
nmigen/vendor/lattice_ice40.py
vendored
102
nmigen/vendor/lattice_ice40.py
vendored
|
@ -70,7 +70,7 @@ class LatticeICE40Platform(TemplatedPlatform):
|
||||||
""",
|
""",
|
||||||
"{{name}}.pcf": r"""
|
"{{name}}.pcf": r"""
|
||||||
# {{autogenerated}}
|
# {{autogenerated}}
|
||||||
{% for port_name, pin_name, extras in platform.iter_port_constraints_bits() -%}
|
{% for port_name, pin_name, attrs in platform.iter_port_constraints_bits() -%}
|
||||||
set_io {{port_name}} {{pin_name}}
|
set_io {{port_name}} {{pin_name}}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
""",
|
""",
|
||||||
|
@ -110,33 +110,17 @@ class LatticeICE40Platform(TemplatedPlatform):
|
||||||
"""
|
"""
|
||||||
]
|
]
|
||||||
|
|
||||||
def iter_ports(self):
|
def should_skip_port_component(self, port, attrs, component):
|
||||||
for res, pin, port in self._ports:
|
# On iCE40, a differential input is placed by only instantiating an SB_IO primitive for
|
||||||
if isinstance(res.io[0], Pins):
|
# the pin with z=0, which is the non-inverting pin. The pinout unfortunately differs
|
||||||
yield port.io
|
# between LP/HX and UP series:
|
||||||
elif isinstance(res.io[0], DiffPairs):
|
# * for LP/HX, z=0 is DPxxB (B is non-inverting, A is inverting)
|
||||||
if res.extras.get("IO_STANDARD", "SB_LVCMOS") == "SB_LVDS_INPUT":
|
# * for UP, z=0 is IOB_xxA (A is non-inverting, B is inverting)
|
||||||
yield port.p
|
if attrs.get("IO_STANDARD", "SB_LVCMOS") == "SB_LVDS_INPUT" and component == "n":
|
||||||
else:
|
return True
|
||||||
yield port.p
|
return False
|
||||||
yield port.n
|
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
|
|
||||||
def iter_port_constraints(self):
|
def _get_io_buffer(self, m, pin, port, attrs, o_invert=None):
|
||||||
for res, pin, port in self._ports:
|
|
||||||
if isinstance(res.io[0], Pins):
|
|
||||||
yield port.io.name, list(res.io[0].map_names(self._mapping, res)), res.extras
|
|
||||||
elif isinstance(res.io[0], DiffPairs):
|
|
||||||
if res.extras.get("IO_STANDARD", "SB_LVCMOS") == "SB_LVDS_INPUT":
|
|
||||||
yield port.p.name, list(res.io[0].p.map_names(self._mapping, res)), res.extras
|
|
||||||
else:
|
|
||||||
yield port.p.name, list(res.io[0].p.map_names(self._mapping, res)), res.extras
|
|
||||||
yield port.n.name, list(res.io[0].n.map_names(self._mapping, res)), res.extras
|
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
|
|
||||||
def _get_io_buffer(self, m, pin, port, extras, o_invert=None):
|
|
||||||
def _get_dff(clk, d, q):
|
def _get_dff(clk, d, q):
|
||||||
m.submodules += Instance("$dff",
|
m.submodules += Instance("$dff",
|
||||||
p_CLK_POLARITY=0,
|
p_CLK_POLARITY=0,
|
||||||
|
@ -160,9 +144,9 @@ class LatticeICE40Platform(TemplatedPlatform):
|
||||||
o_O=y[bit])
|
o_O=y[bit])
|
||||||
return y
|
return y
|
||||||
|
|
||||||
if "GLOBAL" in extras:
|
if "GLOBAL" in attrs:
|
||||||
is_global_input = bool(extras["GLOBAL"])
|
is_global_input = bool(attrs["GLOBAL"])
|
||||||
del extras["GLOBAL"]
|
del attrs["GLOBAL"]
|
||||||
else:
|
else:
|
||||||
is_global_input = False
|
is_global_input = False
|
||||||
|
|
||||||
|
@ -185,7 +169,7 @@ class LatticeICE40Platform(TemplatedPlatform):
|
||||||
for bit in range(len(port)):
|
for bit in range(len(port)):
|
||||||
io_args = [
|
io_args = [
|
||||||
("io", "PACKAGE_PIN", port[bit]),
|
("io", "PACKAGE_PIN", port[bit]),
|
||||||
*(("p", key, value) for key, value in extras.items()),
|
*(("p", key, value) for key, value in attrs.items()),
|
||||||
]
|
]
|
||||||
|
|
||||||
if "i" not in pin.dir:
|
if "i" not in pin.dir:
|
||||||
|
@ -242,56 +226,52 @@ class LatticeICE40Platform(TemplatedPlatform):
|
||||||
else:
|
else:
|
||||||
m.submodules += Instance("SB_IO", *io_args)
|
m.submodules += Instance("SB_IO", *io_args)
|
||||||
|
|
||||||
def get_input(self, pin, port, extras):
|
def get_input(self, pin, port, attrs):
|
||||||
self._check_feature("single-ended input", pin, extras,
|
self._check_feature("single-ended input", pin, attrs,
|
||||||
valid_xdrs=(0, 1, 2), valid_extras=True)
|
valid_xdrs=(0, 1, 2), valid_attrs=True)
|
||||||
m = Module()
|
m = Module()
|
||||||
self._get_io_buffer(m, pin, port, extras)
|
self._get_io_buffer(m, pin, port, attrs)
|
||||||
return m
|
return m
|
||||||
|
|
||||||
def get_output(self, pin, port, extras):
|
def get_output(self, pin, port, attrs):
|
||||||
self._check_feature("single-ended output", pin, extras,
|
self._check_feature("single-ended output", pin, attrs,
|
||||||
valid_xdrs=(0, 1, 2), valid_extras=True)
|
valid_xdrs=(0, 1, 2), valid_attrs=True)
|
||||||
m = Module()
|
m = Module()
|
||||||
self._get_io_buffer(m, pin, port, extras)
|
self._get_io_buffer(m, pin, port, attrs)
|
||||||
return m
|
return m
|
||||||
|
|
||||||
def get_tristate(self, pin, port, extras):
|
def get_tristate(self, pin, port, attrs):
|
||||||
self._check_feature("single-ended tristate", pin, extras,
|
self._check_feature("single-ended tristate", pin, attrs,
|
||||||
valid_xdrs=(0, 1, 2), valid_extras=True)
|
valid_xdrs=(0, 1, 2), valid_attrs=True)
|
||||||
m = Module()
|
m = Module()
|
||||||
self._get_io_buffer(m, pin, port, extras)
|
self._get_io_buffer(m, pin, port, attrs)
|
||||||
return m
|
return m
|
||||||
|
|
||||||
def get_input_output(self, pin, port, extras):
|
def get_input_output(self, pin, port, attrs):
|
||||||
self._check_feature("single-ended input/output", pin, extras,
|
self._check_feature("single-ended input/output", pin, attrs,
|
||||||
valid_xdrs=(0, 1, 2), valid_extras=True)
|
valid_xdrs=(0, 1, 2), valid_attrs=True)
|
||||||
m = Module()
|
m = Module()
|
||||||
self._get_io_buffer(m, pin, port, extras)
|
self._get_io_buffer(m, pin, port, attrs)
|
||||||
return m
|
return m
|
||||||
|
|
||||||
def get_diff_input(self, pin, p_port, n_port, extras):
|
def get_diff_input(self, pin, p_port, n_port, attrs):
|
||||||
self._check_feature("differential input", pin, extras,
|
self._check_feature("differential input", pin, attrs,
|
||||||
valid_xdrs=(0, 1, 2), valid_extras=True)
|
valid_xdrs=(0, 1, 2), valid_attrs=True)
|
||||||
# On iCE40, a differential input is placed by only instantiating an SB_IO primitive for
|
|
||||||
# the pin with z=0, which is the non-inverting pin. The pinout unfortunately differs
|
|
||||||
# between LP/HX and UP series:
|
|
||||||
# * for LP/HX, z=0 is DPxxB (B is non-inverting, A is inverting)
|
|
||||||
# * for UP, z=0 is IOB_xxA (A is non-inverting, B is inverting)
|
|
||||||
m = Module()
|
m = Module()
|
||||||
self._get_io_buffer(m, pin, p_port, extras)
|
# See comment in should_skip_port_component above.
|
||||||
|
self._get_io_buffer(m, pin, p_port, attrs)
|
||||||
return m
|
return m
|
||||||
|
|
||||||
def get_diff_output(self, pin, p_port, n_port, extras):
|
def get_diff_output(self, pin, p_port, n_port, attrs):
|
||||||
self._check_feature("differential output", pin, extras,
|
self._check_feature("differential output", pin, attrs,
|
||||||
valid_xdrs=(0, 1, 2), valid_extras=True)
|
valid_xdrs=(0, 1, 2), valid_attrs=True)
|
||||||
m = Module()
|
m = Module()
|
||||||
# Note that the non-inverting output pin is not driven the same way as a regular
|
# Note that the non-inverting output pin is not driven the same way as a regular
|
||||||
# output pin. The inverter introduces a delay, so for a non-inverting output pin,
|
# output pin. The inverter introduces a delay, so for a non-inverting output pin,
|
||||||
# an identical delay is introduced by instantiating a LUT. This makes the waveform
|
# an identical delay is introduced by instantiating a LUT. This makes the waveform
|
||||||
# perfectly symmetric in the xdr=0 case.
|
# perfectly symmetric in the xdr=0 case.
|
||||||
self._get_io_buffer(m, pin, p_port, extras, o_invert=False)
|
self._get_io_buffer(m, pin, p_port, attrs, o_invert=False)
|
||||||
self._get_io_buffer(m, pin, n_port, extras, o_invert=True)
|
self._get_io_buffer(m, pin, n_port, attrs, o_invert=True)
|
||||||
return m
|
return m
|
||||||
|
|
||||||
# Tristate and bidirectional buffers are not supported on iCE40 because it requires external
|
# Tristate and bidirectional buffers are not supported on iCE40 because it requires external
|
||||||
|
|
Loading…
Reference in a new issue