5 # Breadboard/circuit pin layout program
14 PROGRAM_NAME
= "bb.py"
16 def combine_dicts(dict1
, dict2
):
17 """Combine two dictionaries; dict2 takes priority over dict1."""
18 ret
= copy
.deepcopy(dict1
)
22 def read_netlist(filename
):
23 """Read a SPICE input deck, returning subcircuit nodes and definitions."""
25 f
= file(filename
, "rt")
37 if line
.startswith(".subckt"):
42 subckt_nodes
[name
] = nodes
43 elif line
.startswith(".ends"):
47 if not subckt_defns
.has_key(name
):
48 subckt_defns
[name
] = []
49 subckt_defns
[name
].append(line
)
51 # Top-level elements, skip blank lines, commands and comments
52 if len(line
.strip()) != 0 and line
[0] not in ('.', '*'):
55 print "* Converted from netlist %s by %s on %s" % (filename
, PROGRAM_NAME
, time
.asctime())
56 return subckt_nodes
, subckt_defns
, toplevel
58 def rewrite_subckt(subckt_defns
, s
):
59 """Partially rewrite subcircuit 's', using rules from a module of the same name.
61 Removes parts in mod.parts_consumed, keeps parts in mod.parts_kept.
63 Does not generate any new parts."""
67 for line
in subckt_defns
[s
]:
72 if name
in mod
.parts_consumed
:
75 elif name
in mod
.parts_kept
:
78 raise "Subcircuit %s defined in module %s has parts consumed list: %s and parts kept list: %s, but the name '%s' is in neither. Add it to either." % (s
, name
, mod
.parts_consumed
, mod
.parts_kept
, name
)
80 # Map node positions in arguments list (subckt invocation, X..), to node names
82 for i
in range(0, len(mod
.nodes
)):
83 pos2node
[mod
.nodes
[i
]] = i
86 subckt_defns
[s
] = lines
88 return mod
, subckt_defns
, pos2node
92 """Get a unique, increasing number."""
98 def get_floating(n
=None):
99 """Return a unique disconnected (floating) node name.
101 If n is given, return a dict of n floating node names. Otherwise returns one in a string."""
103 return get_floating_n(n
)
105 return "NC_%s_" % (get_serial(), )
107 def get_floating_n(n
):
108 """Get n floating nodes, see get_floating()."""
110 for x
in range(1, n
+ 1):
111 fs
[x
] = get_floating()
114 def is_floating(node_name
):
115 """Return whether the given node name is probably not connected;
116 generated from get_floating()."""
117 return node_name
.startswith("NC_")
119 def chip_has_pins_available(pins_needed
, pins
):
120 """Return whether 'pins' has not-connected pins, all those required in pins_needed."""
121 for p
in pins_needed
:
122 if not is_floating(pins
[p
]):
126 def find_chip(chips
, model_needed
, pins_needed_options
):
127 """Return an index into the chips array, and what pins, with the given model and the pins free.
129 pins_needed_options is a list of lists, of any acceptable set of pins to use."""
130 for i
, chip
in enumerate(chips
):
132 if model
!= model_needed
:
135 for option_num
, option
in enumerate(pins_needed_options
):
136 # Note: I do not yet check if the chip has pins that are used, but are assigned to
137 # the same node that is required. The pins must be unused.
138 if chip_has_pins_available(option
, pins
):
139 #print "* Found model %s with pins %s free: chip #%s" % (model_needed, option, i)
142 raise "* No chips found with model %s and with pins %s free. Maybe you need more chips." % (model_needed
,
145 def find_pins_needed(pins
):
146 """From a mod.pins[x] dict, return the pins needed for each model, for find_chip()"""
148 for x
in pins
.values():
149 if type(x
) == types
.TupleType
:
151 if not need
.has_key(model
):
154 need
[model
].append(pin
)
158 def assign_part(chips
, subckt_defns
, extra
, model_name
, external_nodes
, refdesg
):
159 """Assign a given model to a physical chip, using the names of the external nodes.
161 chips: array of existing physical chips that can be assigned from
162 mod: logical model to map to, like 'tinv'
163 external_nodes: dict mapping internal nodes in the model, to external nodes in the world
166 mod
= globals()[model_name
]
167 subckt_lines
= subckt_defns
[model_name
]
170 # Store new pin assignments
172 for p
in mod
.parts_generated
:
178 need
= find_pins_needed(p
)
180 raise "Sorry, more than one model is needed: %s, but only one is supported right now." % (need
,)
181 if the_model
is None:
182 the_model
= need
.keys()[0]
184 if the_model
!= need
.keys()[0]:
185 raise "Sorry, different models are needed: %s, but already decided on %s earlier. This is not yet supported." % (the_model
, need
[0])
187 need_options
.append(need
.values()[0])
189 #print "* Searching for model %s with one of pins: %s" % (the_model, need_options)
190 chip_num
, option_num
= find_chip(chips
, the_model
, need_options
)
191 #print "* FOUND CHIP:",chip_num
192 #print "* WITH PINS (option #%s):" % (option_num,), mod.pins[option_num]
194 for node
, pin
in combine_dicts(mod
.global_pins
, mod
.pins
[option_num
]).iteritems():
195 if type(pin
) == types
.TupleType
:
200 #print "* %s -> %s:%s" % (node, part, pin)
203 if node
.startswith("$G_") or node
== "0":
204 external_node
= node
# global node (LTspice)
206 external_node
= external_nodes
[node
] # map internal to external node
208 chips
[chip_num
][1][pin
] = external_node
210 # Now place any additional parts (resistors, etc.) within the subcircuit model
211 # that connect to the chip, but are not part of the chip.
212 for line
in subckt_lines
:
216 #name = "%s_%s_%s_%s" % (words[0], model_name, chip_num, refdesg)
217 name
= "%s%s$%s" % (words
[0][0], refdesg
, words
[0])
219 new_words
.append(name
)
221 # Replace internal nodes with external nodes.
223 if w
in external_nodes
.keys():
224 new_words
.append(external_nodes
[w
])
227 new_words
.append(get_floating())
228 elif w
[0].isdigit(): # value
231 raise "Could not map argument '%s' in subcircuit line %s" % (w
, line
)
232 extra
.append(" ".join(new_words
))
233 # TODO: get new part names!!!!!!!!
237 def any_pins_used(pins
):
238 """Return whether any of the pins on a chip are used. If False, chip isn't used."""
239 for p
in pins
.values():
240 if not is_floating(p
):
244 def dump_chips(chips
):
245 """Show the current chips and their pin connections."""
246 for i
, c
in enumerate(chips
):
249 if not any_pins_used(p
):
250 print "* Chip #%s - %s no pins used, skipping" % (i
, m
)
253 print "* Chip #%s - %s pinout:" % (i
, m
)
254 for k
, v
in p
.iteritems():
255 print "* \t%s: %s" % (k
, v
)
257 print "X_IC_%s " % (i
, ),
258 # Assumes all chips are 14-pin, arguments from 1 to 14 positional
259 for k
in range(1, 14+1):
264 def dump_extra(extra
):
265 """Shows the extra, supporting subcircuit parts that support the IC and are part of the subcircuit."""
266 print "* Parts to support subcircuits:"
270 def make_node_mapping(internal
, external
):
271 """Make a node mapping dictionary from internal to external nodes.
272 Keys of the returned dictionary are internal nodes (of subcircuit), values are external."""
274 d
.update(zip(internal
, external
))
277 def rewrite_refdesg(original
, prefix
):
278 """Prefix a reference designator, preserving the first character."""
279 return "%s%s$%s" % (original
[0], prefix
, original
)
281 def expand(subckt_defns
, line
, prefix
):
282 """Recursively expand a subcircuit instantiation if needed."""
285 if words
[0][0] == 'X':
289 nodes
= make_node_mapping(subckt_defns
[model
], args
)
291 new_lines
.append(("* Instance of subcircuit %s: %s" % (model
, " ".join(args
))))
292 for sline
in subckt_defns
[model
]:
293 words
= sline
.split()
294 if words
[0][0] == 'X' and words
[-1] not in ('tg', 'tinv', 'tnor', 'tnor3', 'tnand', 'tnand3'):
295 new_lines
.extend(expand(subckt_defns
, sline
, "%s$" % (words
[0]),))
298 # Nest reference designator
299 new_words
.append("%s%s%s$%s" % (words
[0][0], prefix
, refdesg
, words
[0]))
300 #new_words.append(rewrite_refdesg(rewrite_refdesg(words[0], refdesg), prefix)) # XXX TODO
301 # Map internal to external nodes
302 for word
in words
[1:]:
304 if word
in nodes
.keys():
305 new_words
.append(nodes
[word
])
306 elif is_floating(word
):
307 new_words
.append(get_floating())
309 new_words
.append(word
)
310 new_lines
.append(" ".join(new_words
))
311 #new_lines.append("")
318 """Demonstrate flattening of a hierarchical subcircuit."""
319 subckt_nodes
, subckt_defns
, toplevel
= read_netlist("mux3-1_test.net")
321 for line
in toplevel
:
322 print "\n".join(expand(subckt_defns
, line
, ""))
325 subckt_nodes
, subckt_defns
, toplevel
= read_netlist("tinv_test.net")
327 mod_tinv
, subckt_defns
, pos2node_tinv
= rewrite_subckt(subckt_defns
, "tinv")
328 #tg_tinv, subckt_defns, pos2node_tg = rewrite_subckt(subckt_defns, "tg")
330 # First semi-flatten the circuit
332 for line
in toplevel
:
333 flat_toplevel
.extend((expand(subckt_defns
, line
, "")))
335 print "* Flattened top-level, before part assignment:"
336 for f
in flat_toplevel
:
338 print "* Begin converted circuit"
343 ("CD4007", get_floating(14) ),
344 ("CD4007", get_floating(14) ),
345 ("CD4007", get_floating(14) ),
346 ("CD4016", get_floating(14) ),
350 for line
in flat_toplevel
:
352 if words
[0][0] == 'X':
357 #print "MODEL=%s, args=%s" % (model, args)
359 #print subckt_defns[model]
360 #print subckt_nodes[model]
362 if model
in ('tg', 'tinv'):
363 nodes
= make_node_mapping(subckt_nodes
[model
], args
)
364 chips
, extra
= assign_part(chips
, subckt_defns
, extra
, model
, nodes
, refdesg
)
366 raise "Cannot synthesize model: %s, line: %s" % (model
, line
)
373 def test_assignment():
374 """Demonstrate subcircuit assignment."""
375 subckt_nodes
, subckt_defns
, toplevel
= read_netlist("mux3-1_test.net")
377 mod_tinv
, subckt_defns
, pos2node_tinv
= rewrite_subckt(subckt_defns
, "tinv")
378 tg_tinv
, subckt_defns
, pos2node_tg
= rewrite_subckt(subckt_defns
, "tg")
383 ("CD4007", get_floating(14) ),
384 ("CD4016", get_floating(14) ),
385 #("CD4007", get_floating(14) )
388 # TODO: parse from SPICE files, assigning nodes based on pos2node_*
390 # line = "XX1 IN NC_01 OUT NC_02 tinv"
393 chips
, extra
= assign_part(chips
, subckt_defns
, extra
, "tinv",
396 "PTI_Out": "PTI_Out_1",
397 "NTI_Out": "NTI_Out_1",
398 "STI_Out": "STI_Out_1",
401 chips
, extra
= assign_part(chips
, subckt_defns
, extra
, "tinv",
404 "PTI_Out": "PTI_Out_2",
405 "NTI_Out": "NTI_Out_2",
406 "STI_Out": "STI_Out_2",
409 chips
, extra
= assign_part(chips
, subckt_defns
, extra
, "tg",
415 chips
, extra
= assign_part(chips
, subckt_defns
, extra
, "tg",
421 chips
, extra
= assign_part(chips
, subckt_defns
, extra
, "tg",
427 chips
, extra
= assign_part(chips
, subckt_defns
, extra
, "tg",
437 if __name__
== "__main__":