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 .res import ConstraintError
|
||||
from .dsl import Pins, DiffPairs, Attrs, Subsignal, Resource, Connector
|
||||
from .res import ResourceError
|
||||
from .plat import Platform, TemplatedPlatform
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from collections import OrderedDict
|
||||
|
||||
|
||||
__all__ = ["Pins", "DiffPairs", "Subsignal", "Resource", "Connector"]
|
||||
__all__ = ["Pins", "DiffPairs", "Attrs", "Subsignal", "Resource", "Connector"]
|
||||
|
||||
|
||||
class Pins:
|
||||
|
@ -32,13 +32,15 @@ class Pins:
|
|||
return iter(self.names)
|
||||
|
||||
def map_names(self, mapping, resource):
|
||||
mapped_names = []
|
||||
for name in self.names:
|
||||
while ":" in name:
|
||||
if name not in mapping:
|
||||
raise NameError("Resource {!r} refers to nonexistent connector pin {}"
|
||||
.format(resource, name))
|
||||
name = mapping[name]
|
||||
yield name
|
||||
mapped_names.append(name)
|
||||
return mapped_names
|
||||
|
||||
def __repr__(self):
|
||||
return "(pins {} {})".format(self.dir, " ".join(self.names))
|
||||
|
@ -67,65 +69,66 @@ class DiffPairs:
|
|||
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:
|
||||
def __init__(self, name, *io, extras=None):
|
||||
self.name = name
|
||||
def __init__(self, name, *args):
|
||||
self.name = name
|
||||
self.ios = []
|
||||
self.attrs = Attrs()
|
||||
|
||||
if not io:
|
||||
raise TypeError("Missing I/O constraints")
|
||||
for c in io:
|
||||
if not isinstance(c, (Pins, DiffPairs, Subsignal)):
|
||||
raise TypeError("I/O constraint must be one of Pins, DiffPairs or Subsignal, "
|
||||
"not {!r}"
|
||||
.format(c))
|
||||
if isinstance(io[0], (Pins, DiffPairs)) and len(io) > 1:
|
||||
raise TypeError("Pins and DiffPairs cannot be followed by more I/O constraints, but "
|
||||
"{!r} is followed by {!r}"
|
||||
.format(io[0], io[1]))
|
||||
if isinstance(io[0], Subsignal):
|
||||
for c in io[1:]:
|
||||
if not isinstance(c, Subsignal):
|
||||
raise TypeError("A Subsignal can only be followed by more Subsignals, but "
|
||||
"{!r} is followed by {!r}"
|
||||
.format(io[0], c))
|
||||
self.io = io
|
||||
self.extras = {}
|
||||
|
||||
if extras is not None:
|
||||
if not isinstance(extras, dict):
|
||||
raise TypeError("Extra constraints must be a dict, not {!r}"
|
||||
.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)
|
||||
if not args:
|
||||
raise ValueError("Missing I/O constraints")
|
||||
for arg in args:
|
||||
if isinstance(arg, (Pins, DiffPairs)):
|
||||
if not self.ios:
|
||||
self.ios.append(arg)
|
||||
else:
|
||||
raise TypeError("Pins and DiffPairs are incompatible with other location or "
|
||||
"subsignal constraints, but {!r} appears after {!r}"
|
||||
.format(arg, self.ios[-1]))
|
||||
elif isinstance(arg, Subsignal):
|
||||
if not self.ios or isinstance(self.ios[-1], Subsignal):
|
||||
self.ios.append(arg)
|
||||
else:
|
||||
raise TypeError("Subsignal is incompatible with location constraints, but "
|
||||
"{!r} appears after {!r}"
|
||||
.format(arg, self.ios[-1]))
|
||||
elif isinstance(arg, Attrs):
|
||||
self.attrs.update(arg)
|
||||
else:
|
||||
raise TypeError("I/O constraint must be one of Pins, DiffPairs, Subsignal, "
|
||||
"or Attrs, not {!r}"
|
||||
.format(arg))
|
||||
|
||||
def __repr__(self):
|
||||
return "(subsignal {} {} {})".format(self.name,
|
||||
" ".join(map(repr, self.io)),
|
||||
" ".join("{}={}".format(k, v)
|
||||
for k, v in self.extras.items()))
|
||||
" ".join(map(repr, self.ios)),
|
||||
repr(self.attrs))
|
||||
|
||||
|
||||
class Resource(Subsignal):
|
||||
def __init__(self, name, number, *io, extras=None):
|
||||
super().__init__(name, *io, extras=extras)
|
||||
def __init__(self, name, number, *args):
|
||||
super().__init__(name, *args)
|
||||
|
||||
self.number = number
|
||||
|
||||
def __repr__(self):
|
||||
return "(resource {} {} {} {})".format(self.name, self.number,
|
||||
" ".join(map(repr, self.io)),
|
||||
" ".join("{}={}".format(k, v)
|
||||
for k, v in self.extras.items()))
|
||||
" ".join(map(repr, self.ios)),
|
||||
repr(self.attrs))
|
||||
|
||||
|
||||
class Connector:
|
||||
|
|
|
@ -17,7 +17,7 @@ from .run import *
|
|||
__all__ = ["Platform", "TemplatedPlatform"]
|
||||
|
||||
|
||||
class Platform(ConstraintManager, metaclass=ABCMeta):
|
||||
class Platform(ResourceManager, metaclass=ABCMeta):
|
||||
resources = abstractproperty()
|
||||
connectors = abstractproperty()
|
||||
clocks = abstractproperty()
|
||||
|
@ -67,25 +67,25 @@ class Platform(ConstraintManager, metaclass=ABCMeta):
|
|||
pin_fragment.flatten = True
|
||||
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":
|
||||
add_pin_fragment(pin, self.get_input(pin, port, extras))
|
||||
add_pin_fragment(pin, self.get_input(pin, port, attrs))
|
||||
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":
|
||||
add_pin_fragment(pin, self.get_tristate(pin, port, extras))
|
||||
add_pin_fragment(pin, self.get_tristate(pin, port, attrs))
|
||||
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":
|
||||
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":
|
||||
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":
|
||||
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":
|
||||
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)
|
||||
|
||||
|
@ -104,7 +104,7 @@ class Platform(ConstraintManager, metaclass=ABCMeta):
|
|||
raise NotImplementedError("Platform {} does not support programming"
|
||||
.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:
|
||||
raise NotImplementedError("Platform {} does not support {}"
|
||||
.format(self.__class__.__name__, feature))
|
||||
|
@ -112,29 +112,29 @@ class Platform(ConstraintManager, metaclass=ABCMeta):
|
|||
raise NotImplementedError("Platform {} does not support {} for XDR {}"
|
||||
.format(self.__class__.__name__, feature, pin.xdr))
|
||||
|
||||
if not valid_extras and extras:
|
||||
raise NotImplementedError("Platform {} does not support extras for {}"
|
||||
if not valid_attrs and attrs:
|
||||
raise NotImplementedError("Platform {} does not support attributes for {}"
|
||||
.format(self.__class__.__name__, feature))
|
||||
|
||||
def get_input(self, pin, port, extras):
|
||||
self._check_feature("single-ended input", pin, extras,
|
||||
valid_xdrs=(0,), valid_extras=None)
|
||||
def get_input(self, pin, port, attrs):
|
||||
self._check_feature("single-ended input", pin, attrs,
|
||||
valid_xdrs=(0,), valid_attrs=None)
|
||||
|
||||
m = Module()
|
||||
m.d.comb += pin.i.eq(port)
|
||||
return m
|
||||
|
||||
def get_output(self, pin, port, extras):
|
||||
self._check_feature("single-ended output", pin, extras,
|
||||
valid_xdrs=(0,), valid_extras=None)
|
||||
def get_output(self, pin, port, attrs):
|
||||
self._check_feature("single-ended output", pin, attrs,
|
||||
valid_xdrs=(0,), valid_attrs=None)
|
||||
|
||||
m = Module()
|
||||
m.d.comb += port.eq(pin.o)
|
||||
return m
|
||||
|
||||
def get_tristate(self, pin, port, extras):
|
||||
self._check_feature("single-ended tristate", pin, extras,
|
||||
valid_xdrs=(0,), valid_extras=None)
|
||||
def get_tristate(self, pin, port, attrs):
|
||||
self._check_feature("single-ended tristate", pin, attrs,
|
||||
valid_xdrs=(0,), valid_attrs=None)
|
||||
|
||||
m = Module()
|
||||
m.submodules += Instance("$tribuf",
|
||||
|
@ -145,9 +145,9 @@ class Platform(ConstraintManager, metaclass=ABCMeta):
|
|||
)
|
||||
return m
|
||||
|
||||
def get_input_output(self, pin, port, extras):
|
||||
self._check_feature("single-ended input/output", pin, extras,
|
||||
valid_xdrs=(0,), valid_extras=None)
|
||||
def get_input_output(self, pin, port, attrs):
|
||||
self._check_feature("single-ended input/output", pin, attrs,
|
||||
valid_xdrs=(0,), valid_attrs=None)
|
||||
|
||||
m = Module()
|
||||
m.submodules += Instance("$tribuf",
|
||||
|
@ -159,21 +159,21 @@ class Platform(ConstraintManager, metaclass=ABCMeta):
|
|||
m.d.comb += pin.i.eq(port)
|
||||
return m
|
||||
|
||||
def get_diff_input(self, pin, p_port, n_port, extras):
|
||||
self._check_feature("differential input", pin, extras,
|
||||
valid_xdrs=(), valid_extras=None)
|
||||
def get_diff_input(self, pin, p_port, n_port, attrs):
|
||||
self._check_feature("differential input", pin, attrs,
|
||||
valid_xdrs=(), valid_attrs=None)
|
||||
|
||||
def get_diff_output(self, pin, p_port, n_port, extras):
|
||||
self._check_feature("differential output", pin, extras,
|
||||
valid_xdrs=(), valid_extras=None)
|
||||
def get_diff_output(self, pin, p_port, n_port, attrs):
|
||||
self._check_feature("differential output", pin, attrs,
|
||||
valid_xdrs=(), valid_attrs=None)
|
||||
|
||||
def get_diff_tristate(self, pin, p_port, n_port, extras):
|
||||
self._check_feature("differential tristate", pin, extras,
|
||||
valid_xdrs=(), valid_extras=None)
|
||||
def get_diff_tristate(self, pin, p_port, n_port, attrs):
|
||||
self._check_feature("differential tristate", pin, attrs,
|
||||
valid_xdrs=(), valid_attrs=None)
|
||||
|
||||
def get_diff_input_output(self, pin, p_port, n_port, extras):
|
||||
self._check_feature("differential input/output", pin, extras,
|
||||
valid_xdrs=(), valid_extras=None)
|
||||
def get_diff_input_output(self, pin, p_port, n_port, attrs):
|
||||
self._check_feature("differential input/output", pin, attrs,
|
||||
valid_xdrs=(), valid_attrs=None)
|
||||
|
||||
|
||||
class TemplatedPlatform(Platform):
|
||||
|
|
|
@ -7,21 +7,24 @@ from ..lib.io import *
|
|||
from .dsl import *
|
||||
|
||||
|
||||
__all__ = ["ConstraintError", "ConstraintManager"]
|
||||
__all__ = ["ResourceError", "ResourceManager"]
|
||||
|
||||
|
||||
class ConstraintError(Exception):
|
||||
class ResourceError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class ConstraintManager:
|
||||
class ResourceManager:
|
||||
def __init__(self, resources, connectors, clocks):
|
||||
self.resources = OrderedDict()
|
||||
self._requested = OrderedDict()
|
||||
|
||||
self.connectors = OrderedDict()
|
||||
self._conn_pins = OrderedDict()
|
||||
|
||||
self.clocks = OrderedDict()
|
||||
|
||||
self._mapping = OrderedDict()
|
||||
self._requested = OrderedDict()
|
||||
# Constraint lists
|
||||
self._ports = []
|
||||
|
||||
self.add_resources(resources)
|
||||
|
@ -50,36 +53,36 @@ class ConstraintManager:
|
|||
self.connectors[conn.name, conn.number] = conn
|
||||
|
||||
for conn_pin, plat_pin in conn:
|
||||
assert conn_pin not in self._mapping
|
||||
self._mapping[conn_pin] = plat_pin
|
||||
assert conn_pin not in self._conn_pins
|
||||
self._conn_pins[conn_pin] = plat_pin
|
||||
|
||||
def add_clock(self, name, number, frequency):
|
||||
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 "
|
||||
"subsignals"
|
||||
.format(resource.name, resource.number, frequency))
|
||||
if (resource.name, resource.number) in self.clocks:
|
||||
other = self.clocks[resource.name, resource.number]
|
||||
raise ConstraintError("Resource {}#{} is already constrained to a frequency of "
|
||||
"{:f} MHz"
|
||||
.format(resource.name, resource.number, other / 1e6))
|
||||
raise ResourceError("Resource {}#{} is already constrained to a frequency of "
|
||||
"{:f} MHz"
|
||||
.format(resource.name, resource.number, other / 1e6))
|
||||
self.clocks[resource.name, resource.number] = frequency
|
||||
|
||||
def lookup(self, name, number=0):
|
||||
if (name, number) not in self.resources:
|
||||
raise ConstraintError("Resource {}#{} does not exist"
|
||||
raise ResourceError("Resource {}#{} does not exist"
|
||||
.format(name, number))
|
||||
return self.resources[name, number]
|
||||
|
||||
def request(self, name, number=0, *, dir=None, xdr=None):
|
||||
resource = self.lookup(name, number)
|
||||
if (resource.name, resource.number) in self._requested:
|
||||
raise ConstraintError("Resource {}#{} has already been requested"
|
||||
.format(name, number))
|
||||
raise ResourceError("Resource {}#{} has already been requested"
|
||||
.format(name, number))
|
||||
|
||||
def merge_options(subsignal, dir, xdr):
|
||||
if isinstance(subsignal.io[0], Subsignal):
|
||||
if isinstance(subsignal.ios[0], Subsignal):
|
||||
if dir is None:
|
||||
dir = dict()
|
||||
if xdr is None:
|
||||
|
@ -92,52 +95,54 @@ class ConstraintManager:
|
|||
raise TypeError("Data rate must be a dict, not {!r}, because {!r} "
|
||||
"has subsignals"
|
||||
.format(xdr, subsignal))
|
||||
for sub in subsignal.io:
|
||||
for sub in subsignal.ios:
|
||||
sub_dir = dir.get(sub.name, None)
|
||||
sub_xdr = xdr.get(sub.name, None)
|
||||
dir[sub.name], xdr[sub.name] = merge_options(sub, sub_dir, sub_xdr)
|
||||
else:
|
||||
if dir is None:
|
||||
dir = subsignal.io[0].dir
|
||||
dir = subsignal.ios[0].dir
|
||||
if xdr is None:
|
||||
xdr = 0
|
||||
if dir not in ("i", "o", "oe", "io", "-"):
|
||||
raise TypeError("Direction must be one of \"i\", \"o\", \"oe\", \"io\", "
|
||||
"or \"-\", not {!r}"
|
||||
.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 \"{}\"; "
|
||||
"direction can be changed from \"io\" to \"i\", \"o\", or "
|
||||
"\"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:
|
||||
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
|
||||
|
||||
def resolve(subsignal, dir, xdr, name):
|
||||
if isinstance(subsignal.io[0], Subsignal):
|
||||
def resolve(resource, dir, xdr, name, attrs):
|
||||
if isinstance(resource.ios[0], Subsignal):
|
||||
fields = OrderedDict()
|
||||
for sub in subsignal.io:
|
||||
for sub in resource.ios:
|
||||
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([
|
||||
(f_name, f.layout) for (f_name, f) in fields.items()
|
||||
], fields=fields, name=name)
|
||||
|
||||
elif isinstance(subsignal.io[0], (Pins, DiffPairs)):
|
||||
phys = subsignal.io[0]
|
||||
elif isinstance(resource.ios[0], (Pins, DiffPairs)):
|
||||
phys = resource.ios[0]
|
||||
if isinstance(phys, Pins):
|
||||
port = Record([("io", len(phys))], name=name)
|
||||
if isinstance(phys, DiffPairs):
|
||||
port = Record([("p", len(phys)),
|
||||
("n", len(phys))], name=name)
|
||||
if dir == "-":
|
||||
self._ports.append((subsignal, None, port))
|
||||
self._ports.append((resource, None, port, attrs))
|
||||
return port
|
||||
else:
|
||||
pin = Pin(len(phys), dir, xdr, name=name)
|
||||
self._ports.append((subsignal, pin, port))
|
||||
self._ports.append((resource, pin, port, attrs))
|
||||
return pin
|
||||
|
||||
else:
|
||||
|
@ -145,51 +150,61 @@ class ConstraintManager:
|
|||
|
||||
value = resolve(resource,
|
||||
*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
|
||||
return value
|
||||
|
||||
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:
|
||||
continue
|
||||
if isinstance(res.io[0], Pins):
|
||||
yield pin, port.io, res.extras
|
||||
if isinstance(res.ios[0], Pins):
|
||||
yield pin, port.io, attrs
|
||||
|
||||
def iter_differential_pins(self):
|
||||
for res, pin, port in self._ports:
|
||||
for res, pin, port, attrs in self._ports:
|
||||
if pin is None:
|
||||
continue
|
||||
if isinstance(res.io[0], DiffPairs):
|
||||
yield pin, port.p, port.n, res.extras
|
||||
if isinstance(res.ios[0], DiffPairs):
|
||||
yield pin, port.p, port.n, attrs
|
||||
|
||||
def should_skip_port_component(self, port, attrs, component):
|
||||
return False
|
||||
|
||||
def iter_ports(self):
|
||||
for res, pin, port in self._ports:
|
||||
if isinstance(res.io[0], Pins):
|
||||
yield port.io
|
||||
elif isinstance(res.io[0], DiffPairs):
|
||||
yield port.p
|
||||
yield port.n
|
||||
for res, pin, port, attrs in self._ports:
|
||||
if isinstance(res.ios[0], Pins):
|
||||
if not self.should_skip_port_component(port, attrs, "io"):
|
||||
yield port.io
|
||||
elif isinstance(res.ios[0], DiffPairs):
|
||||
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:
|
||||
assert False
|
||||
|
||||
def iter_port_constraints(self):
|
||||
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):
|
||||
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
|
||||
for res, pin, port, attrs in self._ports:
|
||||
if isinstance(res.ios[0], Pins):
|
||||
if not self.should_skip_port_component(port, attrs, "io"):
|
||||
yield port.io.name, res.ios[0].map_names(self._conn_pins, res), attrs
|
||||
elif isinstance(res.ios[0], DiffPairs):
|
||||
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:
|
||||
assert False
|
||||
|
||||
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:
|
||||
yield port_name, pin_names[0], extras
|
||||
yield port_name, pin_names[0], attrs
|
||||
else:
|
||||
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):
|
||||
for name, number in self.clocks.keys() & self._requested.keys():
|
||||
|
@ -197,12 +212,12 @@ class ConstraintManager:
|
|||
period = self.clocks[name, number]
|
||||
pin = self._requested[name, number]
|
||||
if pin.dir == "io":
|
||||
raise ConstraintError("Cannot constrain frequency of resource {}#{} because "
|
||||
"it has been requested as a tristate buffer"
|
||||
.format(name, number))
|
||||
if isinstance(resource.io[0], Pins):
|
||||
raise ResourceError("Cannot constrain frequency of resource {}#{} because "
|
||||
"it has been requested as a tristate buffer"
|
||||
.format(name, number))
|
||||
if isinstance(resource.ios[0], Pins):
|
||||
port_name = "{}__io".format(pin.name)
|
||||
elif isinstance(resource.io[0], DiffPairs):
|
||||
elif isinstance(resource.ios[0], DiffPairs):
|
||||
port_name = "{}__p".format(pin.name)
|
||||
else:
|
||||
assert False
|
||||
|
|
|
@ -23,7 +23,7 @@ class PinsTestCase(FHDLTestCase):
|
|||
"pmod_0:1": "A1",
|
||||
"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):
|
||||
p = Pins("0", conn=("pmod", 0))
|
||||
|
@ -31,7 +31,7 @@ class PinsTestCase(FHDLTestCase):
|
|||
"pmod_0:0": "ext_0:1",
|
||||
"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):
|
||||
with self.assertRaises(TypeError,
|
||||
|
@ -51,7 +51,7 @@ class PinsTestCase(FHDLTestCase):
|
|||
with self.assertRaises(NameError,
|
||||
msg="Resource (pins io pmod_0:0 pmod_0:1 pmod_0:2) refers to nonexistent "
|
||||
"connector pin pmod_0:1"):
|
||||
list(p.map_names(mapping, p))
|
||||
p.map_names(mapping, p)
|
||||
|
||||
|
||||
class DiffPairsTestCase(FHDLTestCase):
|
||||
|
@ -84,81 +84,90 @@ class DiffPairsTestCase(FHDLTestCase):
|
|||
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):
|
||||
def test_basic_pins(self):
|
||||
s = Subsignal("a", Pins("A0"), extras={"IOSTANDARD": "LVCMOS33"})
|
||||
self.assertEqual(repr(s), "(subsignal a (pins io A0) IOSTANDARD=LVCMOS33)")
|
||||
s = Subsignal("a", Pins("A0"), Attrs(IOSTANDARD="LVCMOS33"))
|
||||
self.assertEqual(repr(s),
|
||||
"(subsignal a (pins io A0) (attrs IOSTANDARD=LVCMOS33))")
|
||||
|
||||
def test_basic_diffpairs(self):
|
||||
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):
|
||||
s = Subsignal("a",
|
||||
Subsignal("b", Pins("A0")),
|
||||
Subsignal("c", Pins("A1")))
|
||||
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",
|
||||
Subsignal("b", Pins("A0")),
|
||||
Subsignal("c", Pins("A0"), extras={"SLEW": "FAST"}),
|
||||
extras={"IOSTANDARD": "LVCMOS33"})
|
||||
self.assertEqual(s.extras, {"IOSTANDARD": "LVCMOS33"})
|
||||
self.assertEqual(s.io[0].extras, {"IOSTANDARD": "LVCMOS33"})
|
||||
self.assertEqual(s.io[1].extras, {"SLEW": "FAST", "IOSTANDARD": "LVCMOS33"})
|
||||
Subsignal("c", Pins("A0"), Attrs(SLEW="FAST")),
|
||||
Attrs(IOSTANDARD="LVCMOS33"))
|
||||
self.assertEqual(s.attrs, {"IOSTANDARD": "LVCMOS33"})
|
||||
self.assertEqual(s.ios[0].attrs, {})
|
||||
self.assertEqual(s.ios[1].attrs, {"SLEW": "FAST"})
|
||||
|
||||
def test_empty_io(self):
|
||||
with self.assertRaises(TypeError, msg="Missing I/O constraints"):
|
||||
def test_attrs_many(self):
|
||||
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")
|
||||
|
||||
def test_wrong_io(self):
|
||||
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")
|
||||
|
||||
def test_wrong_pins(self):
|
||||
with self.assertRaises(TypeError,
|
||||
msg="Pins and DiffPairs cannot be followed by more I/O constraints, but "
|
||||
"(pins io A0) is followed by (pins io A1)"):
|
||||
msg="Pins and DiffPairs are incompatible with other location or subsignal "
|
||||
"constraints, but (pins io A1) appears after (pins io A0)"):
|
||||
s = Subsignal("a", Pins("A0"), Pins("A1"))
|
||||
|
||||
def test_wrong_diffpairs(self):
|
||||
with self.assertRaises(TypeError,
|
||||
msg="Pins and DiffPairs cannot be followed by more I/O constraints, but "
|
||||
"(diffpairs io (p A0) (n B0)) is followed by "
|
||||
"(pins io A1)"):
|
||||
msg="Pins and DiffPairs are incompatible with other location or subsignal "
|
||||
"constraints, but (pins io A1) appears after (diffpairs io (p A0) (n B0))"):
|
||||
s = Subsignal("a", DiffPairs("A0", "B0"), Pins("A1"))
|
||||
|
||||
def test_wrong_subsignals(self):
|
||||
with self.assertRaises(TypeError,
|
||||
msg="A Subsignal can only be followed by more Subsignals, but "
|
||||
"(subsignal b (pins io A0) ) is followed by (pins io B0)"):
|
||||
msg="Pins and DiffPairs are incompatible with other location or subsignal "
|
||||
"constraints, but (pins io B0) appears after (subsignal b (pins io A0) "
|
||||
"(attrs ))"):
|
||||
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):
|
||||
def test_basic(self):
|
||||
r = Resource("serial", 0,
|
||||
Subsignal("tx", Pins("A0", dir="o")),
|
||||
Subsignal("rx", Pins("A1", dir="i")),
|
||||
extras={"IOSTANDARD": "LVCMOS33"})
|
||||
Attrs(IOSTANDARD="LVCMOS33"))
|
||||
self.assertEqual(repr(r), "(resource serial 0"
|
||||
" (subsignal tx (pins o A0) IOSTANDARD=LVCMOS33)"
|
||||
" (subsignal rx (pins i A1) IOSTANDARD=LVCMOS33)"
|
||||
" IOSTANDARD=LVCMOS33)")
|
||||
" (subsignal tx (pins o A0) (attrs ))"
|
||||
" (subsignal rx (pins i A1) (attrs ))"
|
||||
" (attrs IOSTANDARD=LVCMOS33))")
|
||||
|
||||
|
||||
class ConnectorTestCase(FHDLTestCase):
|
||||
|
|
|
@ -6,7 +6,7 @@ from ..build.res import *
|
|||
from .tools import *
|
||||
|
||||
|
||||
class ConstraintManagerTestCase(FHDLTestCase):
|
||||
class ResourceManagerTestCase(FHDLTestCase):
|
||||
def setUp(self):
|
||||
self.resources = [
|
||||
Resource("clk100", 0, DiffPairs("H1", "H2", dir="i")),
|
||||
|
@ -20,14 +20,14 @@ class ConstraintManagerTestCase(FHDLTestCase):
|
|||
self.connectors = [
|
||||
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):
|
||||
self.clocks = [
|
||||
("clk100", 100),
|
||||
(("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, {
|
||||
("clk100", 0): self.resources[0],
|
||||
("clk50", 0): self.resources[1],
|
||||
|
@ -177,8 +177,8 @@ class ConstraintManagerTestCase(FHDLTestCase):
|
|||
|
||||
def test_wrong_resources_duplicate(self):
|
||||
with self.assertRaises(NameError,
|
||||
msg="Trying to add (resource user_led 0 (pins o A1) ), but "
|
||||
"(resource user_led 0 (pins o A0) ) has the same name and number"):
|
||||
msg="Trying to add (resource user_led 0 (pins o A1) (attrs )), but "
|
||||
"(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"))])
|
||||
|
||||
def test_wrong_connectors(self):
|
||||
|
@ -192,7 +192,7 @@ class ConstraintManagerTestCase(FHDLTestCase):
|
|||
self.cm.add_connectors([Connector("pmod", 0, "1 2")])
|
||||
|
||||
def test_wrong_lookup(self):
|
||||
with self.assertRaises(ConstraintError,
|
||||
with self.assertRaises(ResourceError,
|
||||
msg="Resource user_led#1 does not exist"):
|
||||
r = self.cm.lookup("user_led", 1)
|
||||
|
||||
|
@ -203,7 +203,7 @@ class ConstraintManagerTestCase(FHDLTestCase):
|
|||
self.cm.add_clock("i2c", 0, 10e6)
|
||||
|
||||
def test_wrong_frequency_tristate(self):
|
||||
with self.assertRaises(ConstraintError,
|
||||
with self.assertRaises(ResourceError,
|
||||
msg="Cannot constrain frequency of resource clk50#0 because "
|
||||
"it has been requested as a tristate buffer"):
|
||||
self.cm.add_clock("clk50", 0, 20e6)
|
||||
|
@ -211,13 +211,13 @@ class ConstraintManagerTestCase(FHDLTestCase):
|
|||
list(self.cm.iter_clock_constraints())
|
||||
|
||||
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"):
|
||||
self.cm.add_clock("clk100", 0, 10e6)
|
||||
self.cm.add_clock("clk100", 0, 5e6)
|
||||
|
||||
def test_wrong_request_duplicate(self):
|
||||
with self.assertRaises(ConstraintError,
|
||||
with self.assertRaises(ResourceError,
|
||||
msg="Resource user_led#0 has already been requested"):
|
||||
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):
|
||||
with self.assertRaises(TypeError,
|
||||
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")
|
||||
|
||||
def test_wrong_request_with_wrong_xdr(self):
|
||||
|
@ -249,5 +250,6 @@ class ConstraintManagerTestCase(FHDLTestCase):
|
|||
def test_wrong_request_with_xdr_dict(self):
|
||||
with self.assertRaises(TypeError,
|
||||
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)
|
||||
|
|
102
nmigen/vendor/lattice_ice40.py
vendored
102
nmigen/vendor/lattice_ice40.py
vendored
|
@ -70,7 +70,7 @@ class LatticeICE40Platform(TemplatedPlatform):
|
|||
""",
|
||||
"{{name}}.pcf": r"""
|
||||
# {{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}}
|
||||
{% endfor %}
|
||||
""",
|
||||
|
@ -110,33 +110,17 @@ class LatticeICE40Platform(TemplatedPlatform):
|
|||
"""
|
||||
]
|
||||
|
||||
def iter_ports(self):
|
||||
for res, pin, port in self._ports:
|
||||
if isinstance(res.io[0], Pins):
|
||||
yield port.io
|
||||
elif isinstance(res.io[0], DiffPairs):
|
||||
if res.extras.get("IO_STANDARD", "SB_LVCMOS") == "SB_LVDS_INPUT":
|
||||
yield port.p
|
||||
else:
|
||||
yield port.p
|
||||
yield port.n
|
||||
else:
|
||||
assert False
|
||||
def should_skip_port_component(self, port, attrs, component):
|
||||
# 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)
|
||||
if attrs.get("IO_STANDARD", "SB_LVCMOS") == "SB_LVDS_INPUT" and component == "n":
|
||||
return True
|
||||
return False
|
||||
|
||||
def iter_port_constraints(self):
|
||||
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_io_buffer(self, m, pin, port, attrs, o_invert=None):
|
||||
def _get_dff(clk, d, q):
|
||||
m.submodules += Instance("$dff",
|
||||
p_CLK_POLARITY=0,
|
||||
|
@ -160,9 +144,9 @@ class LatticeICE40Platform(TemplatedPlatform):
|
|||
o_O=y[bit])
|
||||
return y
|
||||
|
||||
if "GLOBAL" in extras:
|
||||
is_global_input = bool(extras["GLOBAL"])
|
||||
del extras["GLOBAL"]
|
||||
if "GLOBAL" in attrs:
|
||||
is_global_input = bool(attrs["GLOBAL"])
|
||||
del attrs["GLOBAL"]
|
||||
else:
|
||||
is_global_input = False
|
||||
|
||||
|
@ -185,7 +169,7 @@ class LatticeICE40Platform(TemplatedPlatform):
|
|||
for bit in range(len(port)):
|
||||
io_args = [
|
||||
("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:
|
||||
|
@ -242,56 +226,52 @@ class LatticeICE40Platform(TemplatedPlatform):
|
|||
else:
|
||||
m.submodules += Instance("SB_IO", *io_args)
|
||||
|
||||
def get_input(self, pin, port, extras):
|
||||
self._check_feature("single-ended input", pin, extras,
|
||||
valid_xdrs=(0, 1, 2), valid_extras=True)
|
||||
def get_input(self, pin, port, attrs):
|
||||
self._check_feature("single-ended input", pin, attrs,
|
||||
valid_xdrs=(0, 1, 2), valid_attrs=True)
|
||||
m = Module()
|
||||
self._get_io_buffer(m, pin, port, extras)
|
||||
self._get_io_buffer(m, pin, port, attrs)
|
||||
return m
|
||||
|
||||
def get_output(self, pin, port, extras):
|
||||
self._check_feature("single-ended output", pin, extras,
|
||||
valid_xdrs=(0, 1, 2), valid_extras=True)
|
||||
def get_output(self, pin, port, attrs):
|
||||
self._check_feature("single-ended output", pin, attrs,
|
||||
valid_xdrs=(0, 1, 2), valid_attrs=True)
|
||||
m = Module()
|
||||
self._get_io_buffer(m, pin, port, extras)
|
||||
self._get_io_buffer(m, pin, port, attrs)
|
||||
return m
|
||||
|
||||
def get_tristate(self, pin, port, extras):
|
||||
self._check_feature("single-ended tristate", pin, extras,
|
||||
valid_xdrs=(0, 1, 2), valid_extras=True)
|
||||
def get_tristate(self, pin, port, attrs):
|
||||
self._check_feature("single-ended tristate", pin, attrs,
|
||||
valid_xdrs=(0, 1, 2), valid_attrs=True)
|
||||
m = Module()
|
||||
self._get_io_buffer(m, pin, port, extras)
|
||||
self._get_io_buffer(m, pin, port, attrs)
|
||||
return m
|
||||
|
||||
def get_input_output(self, pin, port, extras):
|
||||
self._check_feature("single-ended input/output", pin, extras,
|
||||
valid_xdrs=(0, 1, 2), valid_extras=True)
|
||||
def get_input_output(self, pin, port, attrs):
|
||||
self._check_feature("single-ended input/output", pin, attrs,
|
||||
valid_xdrs=(0, 1, 2), valid_attrs=True)
|
||||
m = Module()
|
||||
self._get_io_buffer(m, pin, port, extras)
|
||||
self._get_io_buffer(m, pin, port, attrs)
|
||||
return m
|
||||
|
||||
def get_diff_input(self, pin, p_port, n_port, extras):
|
||||
self._check_feature("differential input", pin, extras,
|
||||
valid_xdrs=(0, 1, 2), valid_extras=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)
|
||||
def get_diff_input(self, pin, p_port, n_port, attrs):
|
||||
self._check_feature("differential input", pin, attrs,
|
||||
valid_xdrs=(0, 1, 2), valid_attrs=True)
|
||||
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
|
||||
|
||||
def get_diff_output(self, pin, p_port, n_port, extras):
|
||||
self._check_feature("differential output", pin, extras,
|
||||
valid_xdrs=(0, 1, 2), valid_extras=True)
|
||||
def get_diff_output(self, pin, p_port, n_port, attrs):
|
||||
self._check_feature("differential output", pin, attrs,
|
||||
valid_xdrs=(0, 1, 2), valid_attrs=True)
|
||||
m = Module()
|
||||
# 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,
|
||||
# an identical delay is introduced by instantiating a LUT. This makes the waveform
|
||||
# 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, n_port, extras, o_invert=True)
|
||||
self._get_io_buffer(m, pin, p_port, attrs, o_invert=False)
|
||||
self._get_io_buffer(m, pin, n_port, attrs, o_invert=True)
|
||||
return m
|
||||
|
||||
# Tristate and bidirectional buffers are not supported on iCE40 because it requires external
|
||||
|
|
Loading…
Reference in a new issue