-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
from collections import OrderedDict
-__all__ = ["Pins", "DiffPairs", "Subsignal", "Resource", "Connector"]
+__all__ = ["Pins", "DiffPairs", "Attrs", "Subsignal", "Resource", "Connector"]
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))
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
-
- 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)
+ def __init__(self, name, *args):
+ self.name = name
+ self.ios = []
+ self.attrs = Attrs()
+
+ 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:
__all__ = ["Platform", "TemplatedPlatform"]
-class Platform(ConstraintManager, metaclass=ABCMeta):
+class Platform(ResourceManager, metaclass=ABCMeta):
resources = abstractproperty()
connectors = abstractproperty()
clocks = abstractproperty()
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)
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))
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",
)
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",
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):
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)
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:
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:
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():
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
"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))
"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,
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):
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"})
-
- def test_empty_io(self):
- with self.assertRaises(TypeError, msg="Missing I/O constraints"):
+ 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_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):
from .tools import *
-class ConstraintManagerTestCase(FHDLTestCase):
+class ResourceManagerTestCase(FHDLTestCase):
def setUp(self):
self.resources = [
Resource("clk100", 0, DiffPairs("H1", "H2", dir="i")),
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],
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):
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)
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)
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)
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):
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)
""",
"{{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 %}
""",
"""
]
- 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 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 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 _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,
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
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:
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