missing NULL terminator in set_config_x
[geda-gaf.git] / xorn / src / gaf / netlist / netlist.py
blob2cd24e7ea69c3d4307300dc95aa1f7e35bcaa03c
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.netlist
21 ## Main entry point for netlist generation.
23 # See the class Netlist for details.
25 import os, sys
26 from gettext import gettext as _
27 import gaf.attrib
28 import gaf.read
29 import gaf.netlist.blueprint
30 import gaf.netlist.instance
31 import gaf.netlist.net
32 import gaf.netlist.package
33 import gaf.netlist.pp_graphical
34 import gaf.netlist.pp_hierarchy
35 import gaf.netlist.pp_netattrib
36 import gaf.netlist.pp_power
37 import gaf.netlist.pp_slotting
38 import gaf.netlist.slib
40 ## Global netlist object representing the result of a netlister run.
42 class Netlist:
43 ## Create a netlist.
45 # This is the main function which creates a netlist. The most
46 # important argument is \a toplevel_filenames; it contains the
47 # filenames of the schematic pages which should be traversed.
48 # Other schematic pages are loaded as necessary if the \a
49 # traverse_hierarchy argument is set.
51 # \param [in] toplevel_filenames
52 # list of filenames for the toplevel schematics, as given on
53 # the command line
55 # \param [in] traverse_hierarchy
56 # whether to descend into sub-schematics
58 # \param [in] verbose_mode
59 # whether to print "Loading schematic" messages
61 # \param [in] prefer_netname_attribute
62 # whether to prefer net names set via a net segment's \c
63 # netname= attribute over net names set via a pin's \c net=
64 # attribute
66 # \param [in] flat_package_namespace
67 # whether to use a common package namespace for all subsheets
69 # \param [in] flat_netname_namespace
70 # whether to use a common \c netname= namespace for all subsheets
72 # \param [in] flat_netattrib_namespace
73 # whether to use a common \c net= namespace for all subsheets
75 # \param [in] refdes_mangle_func
76 # function for mangling package/component refdes's
78 # \param [in] netname_mangle_func
79 # function for mangling net names
81 # \param [in] default_net_name
82 # naming template for unnamed nets
84 # \param [in] default_bus_name
85 # naming template for unnamed buses
87 def __init__(self, toplevel_filenames,
88 traverse_hierarchy,
89 verbose_mode = False,
90 prefer_netname_attribute = False,
91 flat_package_namespace = False,
92 flat_netname_namespace = False,
93 flat_netattrib_namespace = False,
94 refdes_mangle_func = NotImplemented,
95 netname_mangle_func = NotImplemented,
96 default_net_name = 'unnamed_net',
97 default_bus_name = 'unnamed_bus',
98 show_error_coordinates = False):
99 ## Aggregated list of all components in the netlist.
100 self.components = []
101 ## List of sheets for the schematics named on the command line.
102 self.toplevel_sheets = []
103 ## List of sheets.
104 self.sheets = []
106 ## Whether an error has occurred.
107 self.failed = False
109 ## List of nets.
111 # Populated by gaf.netlist.net.
112 self.nets = None
114 ## List of packages.
116 # Populated by gaf.netlist.package.
117 self.packages = None
119 ## Convenience dictionary for looking up packages by their refdes.
120 self.packages_by_refdes = None
122 ## Convenience dictionary for looking up nets by their name.
123 self.nets_by_name = None
125 ## List of schematic blueprints.
126 self.schematics = []
127 ## Dictionary mapping filenames to schematic blueprints.
128 self.schematics_by_filename = {}
130 ## Whether a common package namespace for all subsheets was used.
131 self.flat_package_namespace = flat_package_namespace
132 ## Function which was used for mangling package/component refdes's.
133 self.refdes_mangle_func = refdes_mangle_func
135 ## Whether to print coordinate hints for errors.
136 self.show_error_coordinates = show_error_coordinates
138 def load_schematic(filename):
139 if filename in self.schematics_by_filename:
140 return
142 self.schematics_by_filename[filename] = None
144 if verbose_mode:
145 sys.stderr.write(_("Loading schematic [%s]\n") % filename)
147 try:
148 rev = gaf.read.read(filename, load_symbols = True)
149 except Exception as e:
150 if str(e):
151 sys.stderr.write(_("ERROR: Failed to load '%s': %s\n")
152 % (filename, e))
153 else:
154 sys.stderr.write(_("ERROR: Failed to load '%s'\n")
155 % filename)
156 sys.exit(2)
158 rev.finalize()
159 schematic = gaf.netlist.blueprint.Schematic(
160 rev, filename, self)
161 self.schematics.append(schematic)
162 self.schematics_by_filename[filename] = schematic
164 # Check if the component object represents a subsheet (i.e.,
165 # has a "source=" attribute), and if so, get the filenames.
167 for component in schematic.components:
168 component.composite_sources = []
170 for value in component.get_attributes('source'):
171 for filename in value.split(','):
172 if filename.startswith(' '):
173 component.warn(
174 _("leading spaces in source names "
175 "are deprecated"))
176 filename = filename.lstrip(' ')
178 full_filename = \
179 gaf.netlist.slib.s_slib_search_single(
180 filename)
181 if full_filename is None:
182 component.error(
183 _("failed to load subcircuit '%s': "
184 "schematic not found in source library")
185 % filename)
186 continue
188 load_schematic(full_filename)
189 component.composite_sources.append(
190 self.schematics_by_filename[full_filename])
192 for filename in toplevel_filenames:
193 load_schematic(filename)
195 gaf.netlist.pp_power.postproc_blueprints(self)
196 gaf.netlist.pp_hierarchy.postproc_blueprints(self)
197 gaf.netlist.pp_slotting.postproc_blueprints(self)
198 gaf.netlist.pp_netattrib.postproc_blueprints(self)
199 gaf.netlist.pp_graphical.postproc_blueprints(self)
200 gaf.netlist.package.postproc_blueprints(self)
202 # look for component type conflicts
203 for schematic in self.schematics:
204 for component in schematic.components:
205 if component.composite_sources and component.is_graphical:
206 # Do not bother traversing the hierarchy if the symbol
207 # has an graphical attribute attached to it.
208 component.warn(_("source= is set for graphical component"))
209 component.composite_sources = []
211 if component.has_netname_attrib and \
212 component.has_portname_attrib:
213 component.error(_("netname= and portname= attributes "
214 "are mutually exclusive"))
216 if component.has_netname_attrib and \
217 component.composite_sources:
218 component.error(_("power symbol can't be a subschematic"))
219 component.composite_sources = []
220 if component.has_portname_attrib and \
221 component.composite_sources:
222 component.error(_("I/O symbol can't be a subschematic"))
223 component.composite_sources = []
225 if component.has_netname_attrib and component.is_graphical:
226 component.error(_("power symbol can't be graphical"))
227 if component.has_portname_attrib and component.is_graphical:
228 component.error(_("I/O symbol can't be graphical"))
230 # collect parameters
231 for schematic in self.schematics:
232 for component in schematic.components:
233 component.parameters = {}
235 for func in [gaf.attrib.search_inherited,
236 gaf.attrib.search_attached]:
237 names = set()
238 for val in func(component.ob, 'param'):
239 try:
240 name, value = gaf.attrib.parse_string(val)
241 except gaf.attrib.MalformedAttributeError:
242 component.error(
243 _("malformed param= attribute: %s") % val)
244 continue
246 if name in names:
247 component.error(
248 _("duplicate param= attribute: %s") % name)
249 continue
251 component.parameters[name] = value
252 names.add(name)
254 # Traverse the schematic files and create the component objects
255 # accordingly.
257 def s_traverse_sheet1(sheet):
258 for component in sheet.components:
259 # now you need to traverse any underlying schematics
261 # Check if the component object represents a subsheet (i.e.,
262 # has a "source=" attribute), and if so, traverse that sheet.
264 for subschematic in component.blueprint.composite_sources:
265 # can't do the following, don't know why... HACK TODO
266 #component.hierarchy_tag = u_basic_strdup(refdes)
267 subsheet = gaf.netlist.instance.Sheet(
268 component.sheet.netlist, subschematic, component)
269 s_traverse_sheet1(subsheet)
271 for filename in toplevel_filenames:
272 sheet = gaf.netlist.instance.Sheet(
273 self, self.schematics_by_filename[filename], None)
274 self.toplevel_sheets.append(sheet)
275 if traverse_hierarchy:
276 s_traverse_sheet1(sheet)
278 # now that all the sheets have been read, go through and do the
279 # post processing work
281 # List the components in the same order as the old gnetlist code.
283 def collect_components(sheet):
284 for component in sheet.components:
285 sheet.netlist.components.append(component)
286 for subsheet in component.subsheets:
287 collect_components(subsheet)
289 for sheet in self.toplevel_sheets:
290 collect_components(sheet)
292 # create net objects
293 gaf.netlist.net.postproc_instances(
294 self, { False: flat_netname_namespace,
295 True: flat_netattrib_namespace },
296 prefer_netname_attribute,
297 default_net_name, default_bus_name)
299 # assign net names
300 for net in self.nets:
301 net.name = netname_mangle_func(
302 net.unmangled_name, net.namespace)
304 # assign component pins
305 for component in self.components:
306 for cpin in component.cpins:
307 if (component.sheet, cpin.blueprint.net) \
308 not in cpin.local_net.net.sheets_and_net_blueprints:
309 cpin.local_net.net.sheets_and_net_blueprints.append(
310 (component.sheet, cpin.blueprint.net))
312 for net in self.nets:
313 for sheet, net_blueprint in net.sheets_and_net_blueprints:
314 for cpin_blueprint in net_blueprint.pins:
315 if cpin_blueprint.ob is not None:
316 assert cpin_blueprint.ob.data().is_pin
318 cpin = sheet \
319 .components_by_blueprint[cpin_blueprint.component] \
320 .cpins_by_blueprint[cpin_blueprint]
322 assert cpin not in net.component_pins
323 net.component_pins.append(cpin)
325 # Resolve hierarchy
326 gaf.netlist.pp_hierarchy.postproc_instances(self)
328 gaf.netlist.pp_graphical.postproc_instances(self)
330 # group components into packages
331 gaf.netlist.package.postproc_instances(
332 self, flat_package_namespace)
334 # see if any unconnected subsheet pins are connected to
335 # multiple I/O ports; these need to be preserved or the
336 # internal connections in the subsheet will be lost
337 for net in self.nets:
338 if net.is_unconnected_pin and len(net.connections) > 1:
339 net.is_unconnected_pin = False
341 # remove nets for unconnected pins
342 self.nets = [net for net in self.nets if not net.is_unconnected_pin]
345 # assign component refdes
346 for component in self.components:
347 if component.blueprint.refdes is not None:
348 component.refdes = refdes_mangle_func(
349 component.blueprint.refdes,
350 None if flat_package_namespace
351 else component.sheet.namespace)
353 # assign package refdes
354 for package in self.packages:
355 if package.namespace is not None:
356 package.refdes = refdes_mangle_func(
357 package.unmangled_refdes, package.namespace)
358 else:
359 # If refdes mangling is disabled, packages don't have
360 # a sheet attribute, so just use the unmangled refdes.
361 package.refdes = package.unmangled_refdes
363 # compile convenience hashes, checking for cross-page name clashes
364 self.packages_by_refdes = {}
365 for package in self.packages:
366 if package.refdes in self.packages_by_refdes:
367 other_package = self.packages_by_refdes[package.refdes]
368 self.error(_("refdes conflict across hierarchy: "
369 "refdes `%s' is used by package `%s' on page "
370 "`%s' and by package `%s' on page `%s'") % (
371 package.refdes,
372 other_package.unmangled_refdes,
373 refdes_mangle_func('', other_package.namespace),
374 package.unmangled_refdes,
375 refdes_mangle_func('', package.namespace)))
376 self.packages_by_refdes[package.refdes] = package
378 self.nets_by_name = {}
379 for net in self.nets:
380 if net.name in self.nets_by_name:
381 other_net = self.nets_by_name[net.name]
382 self.error(_("net name conflict across hierarchy: "
383 "net name `%s' is used by net `%s' on page "
384 "`%s' and by net `%s' on page `%s'") % (
385 net.name,
386 other_net.unmangled_name,
387 netname_mangle_func('', other_net.namespace),
388 net.unmangled_name,
389 netname_mangle_func('', net.namespace)))
390 self.nets_by_name[net.name] = net
392 ## Return the value of a toplevel attribute.
394 # Searches for an floating attribute with the name \a name in the
395 # schematic files listed on the command line. Calls \ref error if
396 # multiple attributes with different values are found.
398 # Traditionally, this function returned <tt>'not found'</tt> when
399 # no such attribute existed in the toplevel schematic.
401 # \throws ValueError if no matching attribute was found and no \a
402 # default was given
404 def get_toplevel_attribute(self, name, default = KeyError):
405 if not isinstance(name, basestring):
406 raise ValueError
408 values = []
409 for sheet in self.toplevel_sheets:
410 values += gaf.attrib.search_floating(
411 sheet.blueprint.rev, name)
413 if values:
414 for value in values[1:]:
415 if value != values[0]:
416 self.error(
417 _("inconsistent values for toplevel attribute "
418 "\"%s\": %s") % (
419 name, _(" vs. ").join(_("\"%s\"") % value
420 for value in values)))
421 return values[0]
422 return values[0]
424 if default is not KeyError:
425 return default
426 raise KeyError
428 ## Print an error message and mark the netlist as failed.
430 def error(self, msg):
431 sys.stderr.write(_("error: %s\n" % msg))
432 self.failed = True
434 ## Print a warning message.
436 def warn(self, msg):
437 sys.stderr.write(_("warning: %s\n" % msg))