netlist: Fix namespaces for subsheet packages
[geda-gaf.git] / src / gaf / netlist / package.py
blob299dfd69bdff9e7ca9d7306d430ad1e1ccc2a6f9
1 # gaf.netlist - gEDA Netlist Extraction and Generation
2 # Copyright (C) 1998-2010 Ales Hvezda
3 # Copyright (C) 1998-2010 gEDA Contributors (see ChangeLog for details)
4 # Copyright (C) 2013-2020 Roland Lutz
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software Foundation,
18 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 ## \namespace gaf.netlist.package
21 ## Grouping components with the same refdes into a package.
23 import sys
24 from gettext import gettext as _
26 class Package:
27 def __init__(self, netlist, namespace, unmangled_refdes):
28 self.netlist = netlist
29 self.namespace = namespace
30 self.unmangled_refdes = unmangled_refdes
31 self.refdes = None # set by netlist ctor
32 self.components = []
33 self.pins = []
34 self.pins_by_number = {}
36 ## Get attribute value(s) from a package with given refdes.
38 # This function returns the values of a specific attribute type
39 # attached to the symbol instances with this package's refdes.
41 # For each symbol instance, the found attribute value is added to
42 # the return list. \c None is added if the instance has no such
43 # attribute.
45 # \note The order of the values in the return list is the order of
46 # symbol instances within the netlist (the first element is
47 # the value associated with the first symbol instance).
49 # \returns a list of attribute values as strings and \c None
51 def get_all_attributes(self, name):
52 if not isinstance(name, basestring):
53 raise ValueError
55 # search for refdes instances and through the entire list
56 l = []
57 for component in self.components:
58 try:
59 value = component.blueprint.get_attribute(name)
60 except KeyError:
61 l.append(None)
62 else:
63 if component.sheet.instantiating_component is not None:
64 for param_name, param_value in \
65 component.sheet.instantiating_component \
66 .blueprint.parameters.iteritems():
67 value = value.replace(
68 '$(%s)' % param_name, param_value)
69 l.append(value)
70 return l
72 ## Return the value associated with attribute \a name on the package.
74 # It actually computes a single value from the full list of values
75 # produced by \ref get_all_attributes.
77 # Returns the value associated with the first symbol instance for
78 # \a refdes which has a matching attribute. If all instances of
79 # \a refdes do not have the same value for \a name and \a name is
80 # not \c "slot", raises an error.
82 def get_attribute(self, name, default = KeyError):
83 values = []
84 for value in self.get_all_attributes(name):
85 if value is not None and value not in values:
86 values.append(value)
88 if len(values) > 1:
89 self.error(_("attribute conflict for \"%s\": %s") % (
90 name, _(" vs. ").join(_("\"%s\"") % value
91 for value in values)))
92 values = False
94 if not values:
95 if default is not KeyError:
96 return default
97 raise KeyError
99 assert isinstance(values[0], basestring)
100 return values[0]
102 ## Takes a pinseq string and returns that pinseq pin of this package.
104 def get_pin_by_pinseq(self, pinseq):
105 if not isinstance(pinseq, int):
106 raise ValueError
108 for component in self.components:
109 try:
110 pin_blueprint = component.blueprint.pins_by_pinseq[pinseq]
111 except KeyError:
112 continue
113 return self.pins_by_number[pin_blueprint.number]
115 raise KeyError
117 def get_attribute_names(self, search_inherited):
118 # search outside the symbol (attached attributes only)
119 l = []
120 for component in self.components:
121 for name in \
122 component.blueprint.get_attribute_names(search_inherited):
123 if name not in l:
124 l.append(name)
125 return l
127 def error(self, msg):
128 sys.stderr.write(_("package `%s': error: %s\n") % (self.refdes, msg))
129 self.netlist.failed = True
131 def warn(self, msg):
132 sys.stderr.write(_("package `%s': warning: %s\n") % (self.refdes, msg))
134 # ============================================================================
136 class PackagePin:
137 def __init__(self, package, number):
138 self.package = package
139 self.number = number
140 self.cpins = []
142 ## Returns the appropriate attribute values on this pin.
144 # This function returns the values of a specific attribute type
145 # attached to the instances of this pin. For each instance, the
146 # found attribute value is added to the return list. \c None is
147 # added if the instance has no such attribute.
149 # \note The order of the values in the return list is the order of
150 # pin instances within the netlist (the first element is the
151 # value associated with the first pin instance).
153 # \returns a list of attribute values as strings and \c None
155 def get_all_attributes(self, name):
156 if not isinstance(name, basestring):
157 raise ValueError
159 l = []
160 for cpin in self.cpins:
161 try:
162 value = cpin.blueprint.get_attribute(name)
163 except KeyError:
164 l.append(None)
165 else:
166 l.append(value)
167 return l
169 ## Return the value associated with attribute \a name on the pin.
171 # It actually computes a single value from the full list of values
172 # produced by \ref get_all_attributes.
174 # If all instances do not have the same value for \a name, raises
175 # an error.
177 def get_attribute(self, name, default = KeyError):
178 # Treat "pinnumber" specially: return the value of self.number
179 # which recognizes slotting. For backwards compatibility,
180 # artificial pins do not have a pinnumber.
182 if name == 'pinnumber':
183 has_real_pins = False
184 for cpin in self.cpins:
185 if cpin.blueprint.ob is not None:
186 has_real_pins = True
188 if has_real_pins:
189 return self.number
190 else:
191 if default is not KeyError:
192 return default
193 raise KeyError
195 values = []
196 for value in self.get_all_attributes(name):
197 if value is not None and value not in values:
198 values.append(value)
200 if len(values) > 1:
201 self.error(_("attribute conflict for \"%s\": %s") % (
202 name, _(" vs. ").join(_("\"%s\"") % value
203 for value in values)))
204 values = False
206 if not values:
207 if default is not KeyError:
208 return default
209 raise KeyError
211 assert isinstance(values[0], basestring)
212 return values[0]
214 def error(self, msg):
215 if self.package.refdes is not None:
216 sys.stderr.write(
217 _("package `%s', pin `%s': error: %s\n") % (
218 self.package.refdes, self.number, msg))
219 else:
220 sys.stderr.write(
221 _("package `%s' (unmangled), pin `%s': error: %s\n") % (
222 self.package.unmangled_refdes, self.number, msg))
224 self.package.netlist.failed = True
226 def warn(self, msg):
227 if self.package.refdes is not None:
228 sys.stderr.write(
229 _("package `%s', pin `%s': warning: %s\n") % (
230 self.package.refdes, self.number, msg))
231 else:
232 sys.stderr.write(
233 _("package `%s' (unmangled), pin `%s': warning: %s\n") % (
234 self.package.unmangled_refdes, self.number, msg))
236 # ============================================================================
238 def postproc_blueprints(netlist):
239 # find components without a refdes
240 for schematic in netlist.schematics:
241 for component in schematic.components:
242 if component.refdes is not None:
243 # component has a refdes -> ok
244 continue
245 if component.is_graphical:
246 # graphical components don't need a refdes -> ok
247 continue
248 if component.has_netname_attrib or component.has_portname_attrib:
249 # component is a power symbol or port -> ok
250 continue
252 # Maybe the symbol isn't a component but a power/gnd symbol?
254 if not component.pins:
255 component.error(_("component has neither refdes nor pins"))
256 continue
258 if not next((True for pin in component.pins
259 if pin.has_netattrib), False):
260 # pin is missing a net= attribute
261 component.error(_(
262 "could not find refdes on component and "
263 "could not find net= attribute on pin"))
265 def postproc_instances(netlist, flat_namespace):
266 netlist.packages = []
267 pkg_dict = {}
269 for component in netlist.components:
270 if component.blueprint.refdes is None:
271 continue
273 if flat_namespace:
274 namespace = None
275 else:
276 namespace = component.sheet.namespace
278 try:
279 package = pkg_dict[namespace, component.blueprint.refdes]
280 except KeyError:
281 package = Package(netlist, namespace, component.blueprint.refdes)
282 netlist.packages.append(package)
283 pkg_dict[namespace, component.blueprint.refdes] = package
285 package.components.append(component)
287 for cpin in component.cpins:
288 try:
289 ppin = package.pins_by_number[cpin.blueprint.number]
290 except KeyError:
291 ppin = PackagePin(package, cpin.blueprint.number)
292 package.pins.append(ppin)
293 package.pins_by_number[cpin.blueprint.number] = ppin
294 ppin.cpins.append(cpin)
296 for package in netlist.packages:
297 for ppin in package.pins:
298 nets = []
299 for cpin in ppin.cpins:
300 if cpin.local_net.net not in nets:
301 nets.append(cpin.local_net.net)
302 assert nets
303 if len(nets) > 1:
304 ppin.error(_("multiple nets connected to pin: %s")
305 % _(" vs. ").join(_("\"%s\"") % net.name
306 for net in nets))
307 ppin.net = nets[0]
309 for net in netlist.nets:
310 # walk through the list of components, and through the list
311 # of individual pins on each, adding net names to the list
312 # being careful to ignore duplicates, and unconnected pins
313 net.connections = []
315 # add the net name to the list
316 for cpin in net.component_pins:
317 if cpin.component.blueprint.refdes is None:
318 continue
319 if flat_namespace:
320 namespace = None
321 else:
322 namespace = cpin.component.sheet.namespace
323 ppin = pkg_dict[namespace, cpin.component.blueprint.refdes] \
324 .pins_by_number[cpin.blueprint.number]
325 if ppin not in net.connections:
326 net.connections.append(ppin)