1 from collections
import OrderedDict
4 __all__
= ["Pins", "PinsN", "DiffPairs", "DiffPairsN",
5 "Attrs", "Clock", "Subsignal", "Resource", "Connector"]
9 def __init__(self
, names
, *, dir="io", conn
=None, assert_width
=None):
10 if not isinstance(names
, str):
11 raise TypeError("Names must be a whitespace-separated string, not {!r}"
16 conn_name
, conn_number
= conn
17 if not (isinstance(conn_name
, str) and isinstance(conn_number
, int)):
18 raise TypeError("Connector must be None or a pair of string and integer, not {!r}"
20 names
= ["{}_{}:{}".format(conn_name
, conn_number
, name
) for name
in names
]
22 if dir not in ("i", "o", "io", "oe"):
23 raise TypeError("Direction must be one of \"i\", \"o\", \"oe\", or \"io\", not {!r}"
26 if assert_width
is not None and len(names
) != assert_width
:
27 raise AssertionError("{} names are specified ({}), but {} names are expected"
28 .format(len(names
), " ".join(names
), assert_width
))
35 return len(self
.names
)
38 return iter(self
.names
)
40 def map_names(self
, mapping
, resource
):
42 for name
in self
.names
:
44 if name
not in mapping
:
45 raise NameError("Resource {!r} refers to nonexistent connector pin {}"
46 .format(resource
, name
))
48 mapped_names
.append(name
)
52 return "(pins{} {} {})".format("-n" if self
.invert
else "",
53 self
.dir, " ".join(self
.names
))
56 def PinsN(*args
, **kwargs
):
57 pins
= Pins(*args
, **kwargs
)
63 def __init__(self
, p
, n
, *, dir="io", conn
=None, assert_width
=None):
64 self
.p
= Pins(p
, dir=dir, conn
=conn
, assert_width
=assert_width
)
65 self
.n
= Pins(n
, dir=dir, conn
=conn
, assert_width
=assert_width
)
67 if len(self
.p
.names
) != len(self
.n
.names
):
68 raise TypeError("Positive and negative pins must have the same width, but {!r} "
70 .format(self
.p
, self
.n
))
76 return len(self
.p
.names
)
79 return zip(self
.p
.names
, self
.n
.names
)
82 return "(diffpairs{} {} (p {}) (n {}))".format("-n" if self
.invert
else "",
83 self
.dir, " ".join(self
.p
.names
), " ".join(self
.n
.names
))
86 def DiffPairsN(*args
, **kwargs
):
87 diff_pairs
= DiffPairs(*args
, **kwargs
)
88 diff_pairs
.invert
= True
92 class Attrs(OrderedDict
):
93 def __init__(self
, **attrs
):
94 for key
, value
in attrs
.items():
95 if not (value
is None or isinstance(value
, str) or hasattr(value
, "__call__")):
96 raise TypeError("Value of attribute {} must be None, str, or callable, not {!r}"
99 super().__init
__(**attrs
)
103 for key
, value
in self
.items():
105 items
.append("!" + key
)
106 elif hasattr(value
, "__call__"):
107 items
.append(key
+ "=" + repr(value
))
109 items
.append(key
+ "=" + value
)
110 return "(attrs {})".format(" ".join(items
))
114 def __init__(self
, frequency
):
115 if not isinstance(frequency
, (float, int)):
116 raise TypeError("Clock frequency must be a number")
118 self
.frequency
= float(frequency
)
122 return 1 / self
.frequency
125 return "(clock {})".format(self
.frequency
)
129 def __init__(self
, name
, *args
):
136 raise ValueError("Missing I/O constraints")
138 if isinstance(arg
, (Pins
, DiffPairs
)):
142 raise TypeError("Pins and DiffPairs are incompatible with other location or "
143 "subsignal constraints, but {!r} appears after {!r}"
144 .format(arg
, self
.ios
[-1]))
145 elif isinstance(arg
, Subsignal
):
146 if not self
.ios
or isinstance(self
.ios
[-1], Subsignal
):
149 raise TypeError("Subsignal is incompatible with location constraints, but "
150 "{!r} appears after {!r}"
151 .format(arg
, self
.ios
[-1]))
152 elif isinstance(arg
, Attrs
):
153 self
.attrs
.update(arg
)
154 elif isinstance(arg
, Clock
):
155 if self
.ios
and isinstance(self
.ios
[-1], (Pins
, DiffPairs
)):
156 if self
.clock
is None:
159 raise ValueError("Clock constraint can be applied only once")
161 raise TypeError("Clock constraint can only be applied to Pins or DiffPairs, "
163 .format(self
.ios
[-1]))
165 raise TypeError("Constraint must be one of Pins, DiffPairs, Subsignal, Attrs, "
169 def _content_repr(self
):
172 parts
.append(repr(io
))
173 if self
.clock
is not None:
174 parts
.append(repr(self
.clock
))
176 parts
.append(repr(self
.attrs
))
177 return " ".join(parts
)
180 return "(subsignal {} {})".format(self
.name
, self
._content
_repr
())
183 class Resource(Subsignal
):
185 def family(cls
, name_or_number
, number
=None, *, ios
, default_name
, name_suffix
=""):
186 # This constructor accepts two different forms:
187 # 1. Number-only form:
188 # Resource.family(0, default_name="name", ios=[Pins("A0 A1")])
189 # 2. Name-and-number (name override) form:
190 # Resource.family("override", 0, default_name="name", ios=...)
191 # This makes it easier to build abstractions for resources, e.g. an SPIResource abstraction
192 # could simply delegate to `Resource.family(*args, default_name="spi", ios=ios)`.
193 # The name_suffix argument is meant to support creating resources with
194 # similar names, such as spi_flash, spi_flash_2x, etc.
195 if name_suffix
: # Only add "_" if we actually have a suffix.
196 name_suffix
= "_" + name_suffix
198 if number
is None: # name_or_number is number
199 return cls(default_name
+ name_suffix
, name_or_number
, *ios
)
200 else: # name_or_number is name
201 return cls(name_or_number
+ name_suffix
, number
, *ios
)
203 def __init__(self
, name
, number
, *args
):
204 super().__init
__(name
, *args
)
209 return "(resource {} {} {})".format(self
.name
, self
.number
, self
._content
_repr
())
213 def __init__(self
, name
, number
, io
):
216 self
.mapping
= OrderedDict()
218 if isinstance(io
, dict):
219 for conn_pin
, plat_pin
in io
.items():
220 if not isinstance(conn_pin
, str):
221 raise TypeError("Connector pin name must be a string, not {!r}"
223 if not isinstance(plat_pin
, str):
224 raise TypeError("Platform pin name must be a string, not {!r}"
226 self
.mapping
[conn_pin
] = plat_pin
228 elif isinstance(io
, str):
229 for conn_pin
, plat_pin
in enumerate(io
.split(), start
=1):
232 self
.mapping
[str(conn_pin
)] = plat_pin
235 raise TypeError("Connector I/Os must be a dictionary or a string, not {!r}"
239 return "(connector {} {} {})".format(self
.name
, self
.number
,
240 " ".join("{}=>{}".format(conn
, plat
)
241 for conn
, plat
in self
.mapping
.items()))
244 return len(self
.mapping
)
247 for conn_pin
, plat_pin
in self
.mapping
.items():
248 yield "{}_{}:{}".format(self
.name
, self
.number
, conn_pin
), plat_pin