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 attr_key
, attr_value
in attrs
.items():
95 if not (attr_value
is None or isinstance(attr_value
, str)):
96 raise TypeError("Attribute value must be None or str, not {!r}"
99 super().__init
__(**attrs
)
103 for key
, value
in self
.items():
105 items
.append("!" + key
)
107 items
.append(key
+ "=" + value
)
108 return "(attrs {})".format(" ".join(items
))
112 def __init__(self
, frequency
):
113 if not isinstance(frequency
, (float, int)):
114 raise TypeError("Clock frequency must be a number")
116 self
.frequency
= float(frequency
)
120 return 1 / self
.frequency
123 return "(clock {})".format(self
.frequency
)
127 def __init__(self
, name
, *args
):
134 raise ValueError("Missing I/O constraints")
136 if isinstance(arg
, (Pins
, DiffPairs
)):
140 raise TypeError("Pins and DiffPairs are incompatible with other location or "
141 "subsignal constraints, but {!r} appears after {!r}"
142 .format(arg
, self
.ios
[-1]))
143 elif isinstance(arg
, Subsignal
):
144 if not self
.ios
or isinstance(self
.ios
[-1], Subsignal
):
147 raise TypeError("Subsignal is incompatible with location constraints, but "
148 "{!r} appears after {!r}"
149 .format(arg
, self
.ios
[-1]))
150 elif isinstance(arg
, Attrs
):
151 self
.attrs
.update(arg
)
152 elif isinstance(arg
, Clock
):
153 if self
.ios
and isinstance(self
.ios
[-1], (Pins
, DiffPairs
)):
154 if self
.clock
is None:
157 raise ValueError("Clock constraint can be applied only once")
159 raise TypeError("Clock constraint can only be applied to Pins or DiffPairs, "
161 .format(self
.ios
[-1]))
163 raise TypeError("Constraint must be one of Pins, DiffPairs, Subsignal, Attrs, "
167 def _content_repr(self
):
170 parts
.append(repr(io
))
171 if self
.clock
is not None:
172 parts
.append(repr(self
.clock
))
174 parts
.append(repr(self
.attrs
))
175 return " ".join(parts
)
178 return "(subsignal {} {})".format(self
.name
, self
._content
_repr
())
181 class Resource(Subsignal
):
182 def __init__(self
, name
, number
, *args
):
183 super().__init
__(name
, *args
)
188 return "(resource {} {} {})".format(self
.name
, self
.number
, self
._content
_repr
())
192 def __init__(self
, name
, number
, io
):
195 self
.mapping
= OrderedDict()
197 if isinstance(io
, dict):
198 for conn_pin
, plat_pin
in io
.items():
199 if not isinstance(conn_pin
, str):
200 raise TypeError("Connector pin name must be a string, not {!r}"
202 if not isinstance(plat_pin
, str):
203 raise TypeError("Platform pin name must be a string, not {!r}"
205 self
.mapping
[conn_pin
] = plat_pin
207 elif isinstance(io
, str):
208 for conn_pin
, plat_pin
in enumerate(io
.split(), start
=1):
211 self
.mapping
[str(conn_pin
)] = plat_pin
214 raise TypeError("Connector I/Os must be a dictionary or a string, not {!r}"
218 return "(connector {} {} {})".format(self
.name
, self
.number
,
219 " ".join("{}=>{}".format(conn
, plat
)
220 for conn
, plat
in self
.mapping
.items()))
223 return len(self
.mapping
)
226 for conn_pin
, plat_pin
in self
.mapping
.items():
227 yield "{}_{}:{}".format(self
.name
, self
.number
, conn_pin
), plat_pin