Xorn
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Pages
blueprint.py
Go to the documentation of this file.
1 # xorn.geda.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-2016 Roland Lutz
5 #
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.
10 #
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.
15 #
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.
19 
20 ## \namespace xorn.geda.netlist.blueprint
21 ## Netlists for individual schematic files.
22 #
23 # This module pre-processes a schematic as far as possible without
24 # knowing anything about the rest of the hierarchy.
25 
26 import sys
27 from gettext import gettext as _
28 import xorn.proxy
29 import xorn.storage
30 import xorn.geda.attrib
32 
33 def traverse_net(cmap, instance, netsY_by_instance, netY):
34  if instance in netsY_by_instance:
35  return
36 
37  netY.append(instance)
38  netsY_by_instance[instance] = netY
39 
40  for other_instance in cmap.connected_to(instance):
41  traverse_net(cmap, other_instance, netsY_by_instance, netY)
42 
43 ## A netlist for a single schematic.
44 
45 class Schematic:
46  def __init__(self, rev, filename, netlister_run):
47  if rev.is_transient():
48  raise ValueError
49 
50  self.rev = rev
51  self.filename = filename
52  self.netlister_run = netlister_run
53  self.components = []
54  self.components_by_ob = {}
55  self.nets = []
56 
57  for ob in rev.toplevel_objects():
58  data = ob.data()
59  if not isinstance(data, xorn.storage.Component):
60  continue
61 
62  if data.symbol.prim_objs is None:
64  ob, _("%s: symbol not found") % data.symbol.basename)
65  continue
66 
67  Component(self, ob)
68 
70  netsY = []
71  netsY_by_instance = {}
72 
73  for instance in \
75  if instance not in netsY_by_instance:
76  netY = []
77  netsY.append(netY)
78  traverse_net(cmap, instance, netsY_by_instance, netY)
79 
80  for netY in netsY:
81  net = Net(self, netY)
82  for pin in net.pins:
83  pin.net = net
84 
85  def error(self, msg):
86  sys.stderr.write(_("%s: error: %s\n") % (self.filename, msg))
87  self.netlister_run.failed = True
88 
89  def warn(self, msg):
90  sys.stderr.write(_("%s: warning: %s\n") % (self.filename, msg))
91 
92  def error_object(self, ob, msg):
93  data = ob.data()
94  sys.stderr.write(_("%s:%sx%s: error: %s\n") % (
95  self.filename, format_coord(data.x), format_coord(data.y), msg))
96  self.netlister_run.failed = True
97 
98  def warn_object(self, ob, msg):
99  data = ob.data()
100  sys.stderr.write(_("%s:%sx%s: warning: %s\n") % (
101  self.filename, format_coord(data.x), format_coord(data.y), msg))
102 
103 ## Format an integer coordinate for printing in an error message.
104 #
105 # Divides the coordinate value by 100 and returns it as a string,
106 # keeping as many decimal digits as necessary.
107 #
108 # gEDA uses a coordinate space where two adjacent grid points have a
109 # distance of 100, so most coordinates are multiples of 100.
110 
111 def format_coord(coord):
112  coord = int(coord)
113  if coord % 100 == 0:
114  return '%d' % (coord / 100, )
115  if coord % 10 == 0:
116  return '%d.%d' % (coord / 100, (coord % 100) / 10)
117  return '%d.%02d' % (coord / 100, coord % 100)
118 
119 ## %Component in a single schematic's netlist.
120 
121 class Component:
122  def __init__(self, schematic, ob):
123  self.schematic = schematic
124  self.ob = ob
125  self.pins = []
126  self.pins_by_ob = {}
127 
128  # populated by schematic loader
129  self.composite_sources = None
130 
131  # set by xorn.geda.netlist.pp_graphical
132  self.is_graphical = False
133 
134  # populated by xorn.geda.netlist.pp_slotting
135  self.pins_by_pinseq = None
136  self.pins_by_number = None
137  self.slotdef = None
138 
139  self.error = lambda msg: self.schematic.error_object(self.ob, msg)
140  self.warn = lambda msg: self.schematic.warn_object(self.ob, msg)
141 
142  schematic.components.append(self)
143  schematic.components_by_ob[ob] = self
144 
145  ## Determine the refdes to use for a particular object.
146  self.refdes = None
147 
148  # check refdes, then uref, then return None.
149  try:
150  self.refdes = self.get_attribute(
151  'refdes', search_inherited = False)
152  except KeyError:
153  try:
154  self.refdes = self.get_attribute(
155  'uref', search_inherited = False)
156  except KeyError:
157  self.refdes = None
158  else:
159  self.warn(_("Found uref=%s. uref= is deprecated, please use "
160  "refdes=%s") % (self.refdes, self.refdes))
161 
162  # collect pins
163 
164  data = ob.data()
165  assert isinstance(data, xorn.storage.Component)
166  assert data.symbol.prim_objs is not None
167 
168  for pin_ob in xorn.proxy.RevisionProxy(
169  data.symbol.prim_objs).toplevel_objects():
170  pin_data = pin_ob.data()
171  if not isinstance(pin_data, xorn.storage.Net):
172  continue
173 
174  assert pin_data.is_pin
175  assert pin_ob not in self.pins_by_ob
176 
177  pin = Pin(self, pin_ob)
178  self.pins_by_ob[pin_ob] = pin
179 
180 
181  ## Get all attribute values for a given attribute name.
182  #
183  # Searches the attributes attached to this component for
184  # attributes with the name \a name and returns a list with their
185  # values. If no matching attributes are attached and \a
186  # search_inherited is not \c False, searches the attributes
187  # inherited from the symbol instead.
188 
189  def get_attributes(self, name, search_inherited = True):
190  # look outside first
191  values = xorn.geda.attrib.search_attached(self.ob, name)
192 
193  if not values and search_inherited:
194  # okay we were looking outside and didn't find anything,
195  # so now we need to look inside the symbol
196  values = xorn.geda.attrib.search_inherited(self.ob, name)
197 
198  return values
199 
200  ## Get the value of an attached or inherited attribute.
201  #
202  # Returns the value of the attribute with the name \a name, or
203  # raises a \a KeyError if the attribute doesn't exist. If \a
204  # default is given, returns that value instead.
205  #
206  # Searches the attributes attached to the component first. If no
207  # matching attributes are found and \a search_inherited is not \c
208  # False, searches the attributes inherited from the symbol. It is
209  # an error for the component to contain multiple attached or
210  # inherited values with different values.
211  #
212  # If an attribute has the value \c unknown, it is treated as if it
213  # didn't exist. This can be used to un-set an attribute inherited
214  # from the symbol.
215 
216  def get_attribute(self, name, default = KeyError, search_inherited = True):
217  raw_values = self.get_attributes(name, search_inherited)
218  values = []
219  for value in raw_values:
220  if value not in values:
221  values.append(value)
222 
223  if len(values) > 1:
224  self.error(_("inconsistent values for \"%s\": %s") % (
225  name, _(" vs. ").join(_("\"%s\"") % value
226  for value in values)))
227  values = False
228  elif len(raw_values) > 1:
229  self.warn(_("multiple definitions of \"%s=%s\"")
230  % (name, values[0]))
231 
232  if not values or values[0] == 'unknown':
233  if default != KeyError:
234  return default
235  raise KeyError
236 
237  return values[0]
238 
239  def get_attribute_names(self, search_inherited):
240  attribs = self.ob.attached_objects()
241  if search_inherited:
242  attribs = \
244 
245  l = []
246  for attrib in attribs:
247  data = attrib.data()
248  assert isinstance(data, xorn.storage.Text)
249  try:
250  found_name, found_value = \
252  except xorn.geda.attrib.MalformedAttributeException:
253  pass
254  else:
255  if found_value != 'unknown':
256  if found_name not in l:
257  l.append(found_name)
258  else:
259  if found_name in l:
260  l.remove(found_name)
261  return l
262 
263 ## %Pin in a single schematic's netlist.
264 
265 class Pin:
266  def __init__(self, component, ob):
267  self.component = component
268  self.net = None
269  self.ob = ob
270 
271  ## The "identifier" of the pin.
272  #
273  # set by xorn.geda.netlist.pp_slotting and
274  # xorn.geda.netlist.pp_netattrib
275  self.number = None
276 
277  # set by xorn.geda.netlist.pp_netattrib
278  self.has_netattrib = False
279 
280  component.pins.append(self)
281 
282  ## Get all attribute values for a given attribute name.
283  #
284  # Searches the attributes attached to this pin for attributes with
285  # the name \a name and returns a list with their values.
286 
287  def get_attributes(self, name):
288  if self.ob is None:
289  return []
290  return xorn.geda.attrib.search_attached(self.ob, name)
291 
292  ## Get the value of an attribute.
293  #
294  # Returns the value of the attribute with the name \a name, or
295  # raises a \a KeyError if the attribute doesn't exist. If \a
296  # default is given, returns that value instead.
297  #
298  # It is an error for the pin to contain multiple attributes with
299  # the same name and different values.
300  #
301  # An attribute with the value \c unknown isn't treated specially
302  # in any way. Since pin attributes can't be overridden, there
303  # wouldn't be a use case for this.
304 
305  def get_attribute(self, name, default = KeyError):
306  raw_values = self.get_attributes(name)
307  values = []
308  for value in raw_values:
309  if value not in values:
310  values.append(value)
311 
312  if len(values) > 1:
313  self.error(_("inconsistent values for \"%s\": %s") % (
314  name, _(" vs. ").join(_("\"%s\"") % value
315  for value in values)))
316  values = False
317  elif len(raw_values) > 1:
318  self.warn(_("multiple definitions of \"%s=%s\"")
319  % (name, values[0]))
320 
321  if not values:
322  if self.ob is None and name == 'pintype':
323  # supply pintype 'pwr' for artificial pin
324  return 'pwr'
325  if default != KeyError:
326  return default
327  raise KeyError
328 
329  return values[0]
330 
331  def error(self, msg):
332  # TODO: calculate pin coordinates
333  refdes = self.component.refdes
334  if refdes is None:
335  refdes = '<no refdes>'
336  data = self.component.ob.data()
337  sys.stderr.write(_("%s:%s-%s(%sx%s): error: %s\n") % (
338  self.component.schematic.filename, refdes, self.number,
339  format_coord(data.x), format_coord(data.y), msg))
340  self.component.schematic.netlister_run.failed = True
341 
342  def warn(self, msg):
343  # TODO: calculate pin coordinates
344  refdes = self.component.refdes
345  if refdes is None:
346  refdes = '<no refdes>'
347  data = self.component.ob.data()
348  sys.stderr.write(_("%s:%s-%s(%sx%s): warning: %s\n") % (
349  self.component.schematic.filename, refdes, self.number,
350  format_coord(data.x), format_coord(data.y), msg))
351 
352 ## Visually connected net piece in a single schematic's netlist.
353 
354 class Net:
355  def __init__(self, schematic, instances):
356  self.schematic = schematic
357  self.net_segments = []
358  self.names = []
359  self.pins = []
360  self.is_bus = instances and instances[0][1].data().is_bus
362 
363  for path, ob in instances:
364  assert ob.data().is_bus == self.is_bus
365  if not path:
366  self.net_segments.append(ob)
367  else:
368  self.pins.append(
369  schematic.components_by_ob[path[0]].pins_by_ob[ob])
370 
371  # sort net segments and pins to achieve a stable output order
372  self.net_segments.sort(key = lambda ob: ob.location()[1])
373  self.pins.sort(key = lambda pin: (pin.component.ob.location()[1],
374  pin.ob.location()[1]))
375 
376  for ob in self.net_segments:
377  self.names += xorn.geda.attrib.search_attached(ob, 'netname')
378 
379  # search for the old label= attribute on nets
380  values = xorn.geda.attrib.search_attached(ob, 'label')
381  if values:
382  self.schematic.warn_object(ob,
383  _("label= is deprecated, please use netname="))
384  self.names += values
385 
386  if len(self.names) > 1:
387  self.schematic.warn_object(self.net_segments[0],
388  _("multiple names on a single net: %s")
389  % _(" vs. ").join(_("\"%s\"") % name for name in self.names))
390 
391  schematic.nets.append(self)
Attribute parsing and lookup.
Definition: attrib.py:1
Find and track the connections between net segments.
Definition: conn.py:1
Schematic net segment, bus segment, or pin.
Definition: storage.py:549
Pin in a single schematic's netlist.
Definition: blueprint.py:265
Visually connected net piece in a single schematic's netlist.
Definition: blueprint.py:354
High-level revision proxy class.
Definition: proxy.py:24
refdes
Determine the refdes to use for a particular object.
Definition: blueprint.py:146
def get_attribute
Get the value of an attached or inherited attribute.
Definition: blueprint.py:216
def find_inherited_attribs
Return the attributes inherited by a component via its symbol.
Definition: attrib.py:111
def format_coord
Format an integer coordinate for printing in an error message.
Definition: blueprint.py:111
def search_inherited
Search attributes inherited by a component for an attribute name and return matching values...
Definition: attrib.py:173
Schematic component.
Definition: storage.py:527
def search_attached
Search attributes attached to a net or component for an attribute name and return matching values...
Definition: attrib.py:162
number
The "identifier" of the pin.
Definition: blueprint.py:275
Schematic text or attribute.
Definition: storage.py:583
High-level proxy classes for the storage backend.
Definition: proxy.py:1
A netlist for a single schematic.
Definition: blueprint.py:45
Tracks the connections of the net segments in a revision at a time.
Definition: conn.py:269
def get_attributes
Get all attribute values for a given attribute name.
Definition: blueprint.py:189
def get_attributes
Get all attribute values for a given attribute name.
Definition: blueprint.py:287
Xorn storage backend.
Definition: storage.py:1
def all_net_instances_in_revision
Returns all net instances in a given revision.
Definition: conn.py:109
Component in a single schematic's netlist.
Definition: blueprint.py:121
def parse_string
Parse an attribute string of the form name=value into its name and value parts.
Definition: attrib.py:55
def get_attribute
Get the value of an attribute.
Definition: blueprint.py:305