amaranth/nmigen/build/dsl.py
whitequark 6fae06aea9 build.{dsl,plat,res}: allow dir="oe".
Although a dir="oe" pin is generally equivalent to dir="io" pin with
the i* signal(s) disconnected, they are not equivalent, because some
pins may not be able to support input buffers at all, either because
there are no input buffers, or because the input buffers are consumed
by some other resource.

E.g. this can happen on iCE40 when the input buffer is consumed by
a PLL.
2019-06-03 04:42:55 +00:00

108 lines
4 KiB
Python

__all__ = ["Pins", "DiffPairs", "Subsignal", "Resource"]
class Pins:
def __init__(self, names, dir="io"):
if not isinstance(names, str):
raise TypeError("Names must be a whitespace-separated string, not {!r}"
.format(names))
self.names = names.split()
if dir not in ("i", "o", "io"):
raise TypeError("Direction must be one of \"i\", \"o\", \"oe\", or \"io\", not {!r}"
.format(dir))
self.dir = dir
def __len__(self):
return len(self.names)
def __iter__(self):
return iter(self.names)
def __repr__(self):
return "(pins {} {})".format(self.dir, " ".join(self.names))
class DiffPairs:
def __init__(self, p, n, dir="io"):
self.p = Pins(p, dir=dir)
self.n = Pins(n, dir=dir)
if len(self.p.names) != len(self.n.names):
raise TypeError("Positive and negative pins must have the same width, but {!r} "
"and {!r} do not"
.format(self.p, self.n))
self.dir = dir
def __len__(self):
return len(self.p.names)
def __iter__(self):
return zip(self.p.names, self.n.names)
def __repr__(self):
return "(diffpairs {} (p {}) (n {}))".format(
self.dir, " ".join(self.p.names), " ".join(self.n.names))
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 __repr__(self):
return "(subsignal {} {} {})".format(self.name,
" ".join(map(repr, self.io)),
" ".join("{}={}".format(k, v)
for k, v in self.extras.items()))
class Resource(Subsignal):
def __init__(self, name, number, *io, extras=None):
super().__init__(name, *io, extras=extras)
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()))