1 from collections
import OrderedDict
3 from ..hdl
.ast
import *
4 from ..hdl
.rec
import *
10 __all__
= ["ResourceError", "ResourceManager"]
13 class ResourceError(Exception):
17 class ResourceManager
:
18 def __init__(self
, resources
, connectors
):
19 self
.resources
= OrderedDict()
20 self
._requested
= OrderedDict()
21 self
._phys
_reqd
= OrderedDict()
23 self
.connectors
= OrderedDict()
24 self
._conn
_pins
= OrderedDict()
28 self
._clocks
= SignalDict()
30 self
.add_resources(resources
)
31 self
.add_connectors(connectors
)
33 def add_resources(self
, resources
):
35 if not isinstance(res
, Resource
):
36 raise TypeError("Object {!r} is not a Resource".format(res
))
37 if (res
.name
, res
.number
) in self
.resources
:
38 raise NameError("Trying to add {!r}, but {!r} has the same name and number"
39 .format(res
, self
.resources
[res
.name
, res
.number
]))
40 self
.resources
[res
.name
, res
.number
] = res
42 def add_connectors(self
, connectors
):
43 for conn
in connectors
:
44 if not isinstance(conn
, Connector
):
45 raise TypeError("Object {!r} is not a Connector".format(conn
))
46 if (conn
.name
, conn
.number
) in self
.connectors
:
47 raise NameError("Trying to add {!r}, but {!r} has the same name and number"
48 .format(conn
, self
.connectors
[conn
.name
, conn
.number
]))
49 self
.connectors
[conn
.name
, conn
.number
] = conn
51 for conn_pin
, plat_pin
in conn
:
52 assert conn_pin
not in self
._conn
_pins
53 self
._conn
_pins
[conn_pin
] = plat_pin
55 def lookup(self
, name
, number
=0):
56 if (name
, number
) not in self
.resources
:
57 raise ResourceError("Resource {}#{} does not exist"
58 .format(name
, number
))
59 return self
.resources
[name
, number
]
61 def request(self
, name
, number
=0, *, dir=None, xdr
=None):
62 resource
= self
.lookup(name
, number
)
63 if (resource
.name
, resource
.number
) in self
._requested
:
64 raise ResourceError("Resource {}#{} has already been requested"
65 .format(name
, number
))
67 def merge_options(subsignal
, dir, xdr
):
68 if isinstance(subsignal
.ios
[0], Subsignal
):
73 if not isinstance(dir, dict):
74 raise TypeError("Directions must be a dict, not {!r}, because {!r} "
76 .format(dir, subsignal
))
77 if not isinstance(xdr
, dict):
78 raise TypeError("Data rate must be a dict, not {!r}, because {!r} "
80 .format(xdr
, subsignal
))
81 for sub
in subsignal
.ios
:
82 sub_dir
= dir.get(sub
.name
, None)
83 sub_xdr
= xdr
.get(sub
.name
, None)
84 dir[sub
.name
], xdr
[sub
.name
] = merge_options(sub
, sub_dir
, sub_xdr
)
87 dir = subsignal
.ios
[0].dir
90 if dir not in ("i", "o", "oe", "io", "-"):
91 raise TypeError("Direction must be one of \"i\", \"o\", \"oe\", \"io\", "
94 if dir != subsignal
.ios
[0].dir and \
95 not (subsignal
.ios
[0].dir == "io" or dir == "-"):
96 raise ValueError("Direction of {!r} cannot be changed from \"{}\" to \"{}\"; "
97 "direction can be changed from \"io\" to \"i\", \"o\", or "
98 "\"oe\", or from anything to \"-\""
99 .format(subsignal
.ios
[0], subsignal
.ios
[0].dir, dir))
100 if not isinstance(xdr
, int) or xdr
< 0:
101 raise ValueError("Data rate of {!r} must be a non-negative integer, not {!r}"
102 .format(subsignal
.ios
[0], xdr
))
105 def resolve(resource
, dir, xdr
, name
, attrs
):
106 for attr_key
, attr_value
in attrs
.items():
107 if hasattr(attr_value
, "__call__"):
108 attr_value
= attr_value(self
)
109 assert attr_value
is None or isinstance(attr_value
, str)
110 if attr_value
is None:
113 attrs
[attr_key
] = attr_value
115 if isinstance(resource
.ios
[0], Subsignal
):
116 fields
= OrderedDict()
117 for sub
in resource
.ios
:
118 fields
[sub
.name
] = resolve(sub
, dir[sub
.name
], xdr
[sub
.name
],
119 name
="{}__{}".format(name
, sub
.name
),
120 attrs
={**attrs
, **sub
.attrs
})
122 (f_name
, f
.layout
) for (f_name
, f
) in fields
.items()
123 ], fields
=fields
, name
=name
)
125 elif isinstance(resource
.ios
[0], (Pins
, DiffPairs
)):
126 phys
= resource
.ios
[0]
127 if isinstance(phys
, Pins
):
128 phys_names
= phys
.names
129 port
= Record([("io", len(phys
))], name
=name
)
130 if isinstance(phys
, DiffPairs
):
133 if not self
.should_skip_port_component(None, attrs
, "p"):
134 phys_names
+= phys
.p
.names
135 record_fields
.append(("p", len(phys
)))
136 if not self
.should_skip_port_component(None, attrs
, "n"):
137 phys_names
+= phys
.n
.names
138 record_fields
.append(("n", len(phys
)))
139 port
= Record(record_fields
, name
=name
)
143 pin
= Pin(len(phys
), dir, xdr
=xdr
, name
=name
)
145 for phys_name
in phys_names
:
146 if phys_name
in self
._phys
_reqd
:
147 raise ResourceError("Resource component {} uses physical pin {}, but it "
148 "is already used by resource component {} that was "
150 .format(name
, phys_name
, self
._phys
_reqd
[phys_name
]))
151 self
._phys
_reqd
[phys_name
] = name
153 self
._ports
.append((resource
, pin
, port
, attrs
))
155 if pin
is not None and resource
.clock
is not None:
156 self
.add_clock_constraint(pin
.i
, resource
.clock
.frequency
)
158 return pin
if pin
is not None else port
161 assert False # :nocov:
163 value
= resolve(resource
,
164 *merge_options(resource
, dir, xdr
),
165 name
="{}_{}".format(resource
.name
, resource
.number
),
166 attrs
=resource
.attrs
)
167 self
._requested
[resource
.name
, resource
.number
] = value
170 def iter_single_ended_pins(self
):
171 for res
, pin
, port
, attrs
in self
._ports
:
174 if isinstance(res
.ios
[0], Pins
):
175 yield pin
, port
, attrs
, res
.ios
[0].invert
177 def iter_differential_pins(self
):
178 for res
, pin
, port
, attrs
in self
._ports
:
181 if isinstance(res
.ios
[0], DiffPairs
):
182 yield pin
, port
, attrs
, res
.ios
[0].invert
184 def should_skip_port_component(self
, port
, attrs
, component
):
187 def iter_ports(self
):
188 for res
, pin
, port
, attrs
in self
._ports
:
189 if isinstance(res
.ios
[0], Pins
):
190 if not self
.should_skip_port_component(port
, attrs
, "io"):
192 elif isinstance(res
.ios
[0], DiffPairs
):
193 if not self
.should_skip_port_component(port
, attrs
, "p"):
195 if not self
.should_skip_port_component(port
, attrs
, "n"):
200 def iter_port_constraints(self
):
201 for res
, pin
, port
, attrs
in self
._ports
:
202 if isinstance(res
.ios
[0], Pins
):
203 if not self
.should_skip_port_component(port
, attrs
, "io"):
204 yield port
.io
.name
, res
.ios
[0].map_names(self
._conn
_pins
, res
), attrs
205 elif isinstance(res
.ios
[0], DiffPairs
):
206 if not self
.should_skip_port_component(port
, attrs
, "p"):
207 yield port
.p
.name
, res
.ios
[0].p
.map_names(self
._conn
_pins
, res
), attrs
208 if not self
.should_skip_port_component(port
, attrs
, "n"):
209 yield port
.n
.name
, res
.ios
[0].n
.map_names(self
._conn
_pins
, res
), attrs
213 def iter_port_constraints_bits(self
):
214 for port_name
, pin_names
, attrs
in self
.iter_port_constraints():
215 if len(pin_names
) == 1:
216 yield port_name
, pin_names
[0], attrs
218 for bit
, pin_name
in enumerate(pin_names
):
219 yield "{}[{}]".format(port_name
, bit
), pin_name
, attrs
221 def add_clock_constraint(self
, clock
, frequency
):
222 if not isinstance(clock
, Signal
):
223 raise TypeError("Object {!r} is not a Signal".format(clock
))
224 if not isinstance(frequency
, (int, float)):
225 raise TypeError("Frequency must be a number, not {!r}".format(frequency
))
227 if clock
in self
._clocks
:
228 raise ValueError("Cannot add clock constraint on {!r}, which is already constrained "
230 .format(clock
, self
._clocks
[clock
]))
232 self
._clocks
[clock
] = float(frequency
)
234 def iter_clock_constraints(self
):
235 # Back-propagate constraints through the input buffer. For clock constraints on pins
236 # (the majority of cases), toolchains work better if the constraint is defined on the pin
237 # and not on the buffered internal net; and if the toolchain is advanced enough that
238 # it considers clock phase and delay of the input buffer, it is *necessary* to define
239 # the constraint on the pin to match the designer's expectation of phase being referenced
242 # Constraints on nets with no corresponding input pin (e.g. PLL or SERDES outputs) are not
244 pin_i_to_port
= SignalDict()
245 for res
, pin
, port
, attrs
in self
._ports
:
246 if hasattr(pin
, "i"):
247 if isinstance(res
.ios
[0], Pins
):
248 pin_i_to_port
[pin
.i
] = port
.io
249 elif isinstance(res
.ios
[0], DiffPairs
):
250 pin_i_to_port
[pin
.i
] = port
.p
254 for net_signal
, frequency
in self
._clocks
.items():
255 port_signal
= pin_i_to_port
.get(net_signal
)
256 yield net_signal
, port_signal
, frequency