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.
26 from gettext
import gettext
as _
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.
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
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=
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
,
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.
101 ## List of sheets for the schematics named on the command line.
102 self
.toplevel_sheets
= []
106 ## Whether an error has occurred.
111 # Populated by gaf.netlist.net.
116 # Populated by gaf.netlist.package.
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.
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
:
142 self
.schematics_by_filename
[filename
] = None
145 sys
.stderr
.write(_("Loading schematic [%s]\n") % filename
)
148 rev
= gaf
.read
.read(filename
, load_symbols
= True)
149 except Exception as e
:
151 sys
.stderr
.write(_("ERROR: Failed to load '%s': %s\n")
154 sys
.stderr
.write(_("ERROR: Failed to load '%s'\n")
159 schematic
= gaf
.netlist
.blueprint
.Schematic(
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 warn(_("leading spaces in source names "
175 filename
= filename
.lstrip(' ')
178 gaf
.netlist
.slib
.s_slib_search_single(
180 if full_filename
is None:
182 _("failed to load subcircuit '%s': "
183 "schematic not found in source library")
187 load_schematic(full_filename
)
188 component
.composite_sources
.append(
189 self
.schematics_by_filename
[full_filename
])
191 for filename
in toplevel_filenames
:
192 load_schematic(filename
)
194 gaf
.netlist
.pp_power
.postproc_blueprints(self
)
195 gaf
.netlist
.pp_hierarchy
.postproc_blueprints(self
)
196 gaf
.netlist
.pp_slotting
.postproc_blueprints(self
)
197 gaf
.netlist
.pp_netattrib
.postproc_blueprints(self
)
198 gaf
.netlist
.pp_graphical
.postproc_blueprints(self
)
199 gaf
.netlist
.package
.postproc_blueprints(self
)
201 # look for component type conflicts
202 for schematic
in self
.schematics
:
203 for component
in schematic
.components
:
204 if component
.composite_sources
and component
.is_graphical
:
205 # Do not bother traversing the hierarchy if the symbol
206 # has an graphical attribute attached to it.
207 component
.warn(_("source= is set for graphical component"))
208 component
.composite_sources
= []
210 if component
.has_netname_attrib
and \
211 component
.has_portname_attrib
:
212 component
.error(_("netname= and portname= attributes "
213 "are mutually exclusive"))
215 if component
.has_netname_attrib
and \
216 component
.composite_sources
:
217 component
.error(_("power symbol can't be a subschematic"))
218 component
.composite_sources
= []
219 if component
.has_portname_attrib
and \
220 component
.composite_sources
:
221 component
.error(_("I/O symbol can't be a subschematic"))
222 component
.composite_sources
= []
224 if component
.has_netname_attrib
and component
.is_graphical
:
225 component
.error(_("power symbol can't be graphical"))
226 if component
.has_portname_attrib
and component
.is_graphical
:
227 component
.error(_("I/O symbol can't be graphical"))
230 for schematic
in self
.schematics
:
231 for component
in schematic
.components
:
232 component
.parameters
= {}
234 for func
in [gaf
.attrib
.search_inherited
,
235 gaf
.attrib
.search_attached
]:
237 for val
in func(component
.ob
, 'param'):
239 name
, value
= gaf
.attrib
.parse_string(val
)
240 except gaf
.attrib
.MalformedAttributeError
:
242 _("malformed param= attribute: %s") % val
)
247 _("duplicate param= attribute: %s") % name
)
250 component
.parameters
[name
] = value
253 # Traverse the schematic files and create the component objects
256 def s_traverse_sheet1(sheet
):
257 for component
in sheet
.components
:
258 # now you need to traverse any underlying schematics
260 # Check if the component object represents a subsheet (i.e.,
261 # has a "source=" attribute), and if so, traverse that sheet.
263 for subschematic
in component
.blueprint
.composite_sources
:
264 # can't do the following, don't know why... HACK TODO
265 #component.hierarchy_tag = u_basic_strdup(refdes)
266 subsheet
= gaf
.netlist
.instance
.Sheet(
267 component
.sheet
.netlist
, subschematic
, component
)
268 s_traverse_sheet1(subsheet
)
270 for filename
in toplevel_filenames
:
271 sheet
= gaf
.netlist
.instance
.Sheet(
272 self
, self
.schematics_by_filename
[filename
], None)
273 self
.toplevel_sheets
.append(sheet
)
274 if traverse_hierarchy
:
275 s_traverse_sheet1(sheet
)
277 # now that all the sheets have been read, go through and do the
278 # post processing work
280 # List the components in the same order as the old gnetlist code.
282 def collect_components(sheet
):
283 for component
in sheet
.components
:
284 sheet
.netlist
.components
.append(component
)
285 for subsheet
in component
.subsheets
:
286 collect_components(subsheet
)
288 for sheet
in self
.toplevel_sheets
:
289 collect_components(sheet
)
292 gaf
.netlist
.net
.postproc_instances(
293 self
, { False: flat_netname_namespace
,
294 True: flat_netattrib_namespace
},
295 prefer_netname_attribute
,
296 default_net_name
, default_bus_name
)
299 for net
in self
.nets
:
300 net
.name
= netname_mangle_func(
301 net
.unmangled_name
, net
.namespace
)
303 # assign component pins
304 for component
in self
.components
:
305 for cpin
in component
.cpins
:
306 if (component
.sheet
, cpin
.blueprint
.net
) \
307 not in cpin
.local_net
.net
.sheets_and_net_blueprints
:
308 cpin
.local_net
.net
.sheets_and_net_blueprints
.append(
309 (component
.sheet
, cpin
.blueprint
.net
))
311 for net
in self
.nets
:
312 for sheet
, net_blueprint
in net
.sheets_and_net_blueprints
:
313 for cpin_blueprint
in net_blueprint
.pins
:
314 if cpin_blueprint
.ob
is not None:
315 assert cpin_blueprint
.ob
.data().is_pin
318 .components_by_blueprint
[cpin_blueprint
.component
] \
319 .cpins_by_blueprint
[cpin_blueprint
]
321 assert cpin
not in net
.component_pins
322 net
.component_pins
.append(cpin
)
325 gaf
.netlist
.pp_hierarchy
.postproc_instances(self
)
327 gaf
.netlist
.pp_graphical
.postproc_instances(self
)
329 # group components into packages
330 gaf
.netlist
.package
.postproc_instances(
331 self
, flat_package_namespace
)
333 # see if any unconnected subsheet pins are connected to
334 # multiple I/O ports; these need to be preserved or the
335 # internal connections in the subsheet will be lost
336 for net
in self
.nets
:
337 if net
.is_unconnected_pin
and len(net
.connections
) > 1:
338 net
.is_unconnected_pin
= False
340 # remove nets for unconnected pins
341 self
.nets
= [net
for net
in self
.nets
if not net
.is_unconnected_pin
]
344 # assign component refdes
345 for component
in self
.components
:
346 if component
.blueprint
.refdes
is not None:
347 component
.refdes
= refdes_mangle_func(
348 component
.blueprint
.refdes
,
349 component
.sheet
.instantiating_component
)
351 # assign package refdes
352 for package
in self
.packages
:
353 if package
.namespace
is not None:
354 package
.refdes
= refdes_mangle_func(
355 package
.unmangled_refdes
, package
.namespace
)
357 # If refdes mangling is disabled, packages don't have
358 # a sheet attribute, so just use the unmangled refdes.
359 package
.refdes
= package
.unmangled_refdes
361 # compile convenience hashes, checking for cross-page name clashes
362 self
.packages_by_refdes
= {}
363 for package
in self
.packages
:
364 if package
.refdes
in self
.packages_by_refdes
:
365 other_package
= self
.packages_by_refdes
[package
.refdes
]
366 self
.error(_("refdes conflict across hierarchy: "
367 "refdes `%s' is used by package `%s' on page "
368 "`%s' and by package `%s' on page `%s'") % (
370 other_package
.unmangled_refdes
,
371 refdes_mangle_func('', other_package
.namespace
),
372 package
.unmangled_refdes
,
373 refdes_mangle_func('', package
.namespace
)))
374 self
.packages_by_refdes
[package
.refdes
] = package
376 self
.nets_by_name
= {}
377 for net
in self
.nets
:
378 if net
.name
in self
.nets_by_name
:
379 other_net
= self
.nets_by_name
[net
.name
]
380 self
.error(_("net name conflict across hierarchy: "
381 "net name `%s' is used by net `%s' on page "
382 "`%s' and by net `%s' on page `%s'") % (
384 other_net
.unmangled_name
,
385 netname_mangle_func('', other_net
.namespace
),
387 netname_mangle_func('', net
.namespace
)))
388 self
.nets_by_name
[net
.name
] = net
390 ## Return the value of a toplevel attribute.
392 # Searches for an floating attribute with the name \a name in the
393 # schematic files listed on the command line. Calls \ref error if
394 # multiple attributes with different values are found.
396 # Traditionally, this function returned <tt>'not found'</tt> when
397 # no such attribute existed in the toplevel schematic.
399 # \throws ValueError if no matching attribute was found and no \a
402 def get_toplevel_attribute(self
, name
, default
= KeyError):
403 if not isinstance(name
, basestring
):
407 for sheet
in self
.toplevel_sheets
:
408 values
+= gaf
.attrib
.search_floating(
409 sheet
.blueprint
.rev
, name
)
412 for value
in values
[1:]:
413 if value
!= values
[0]:
415 _("inconsistent values for toplevel attribute "
417 name
, _(" vs. ").join(_("\"%s\"") % value
418 for value
in values
)))
422 if default
is not KeyError:
426 ## Print an error message and mark the netlist as failed.
428 def error(self
, msg
):
429 sys
.stderr
.write(_("error: %s\n" % msg
))
432 ## Print a warning message.
435 sys
.stderr
.write(_("warning: %s\n" % msg
))