Update schedule after http://jeff.tk/wiki/Trinary/Meeting_Notes_20080810
[trinary.git] / bb / bb.py
blobc12004e09a37f3915e9a4c5ef422f2bb3eb0897d
1 #!/usr/bin/env python
2 # Created:20080411
3 # By Jeff Connelly
5 # Circuit pin layout program, for mapping sub-chip (or partially sub-chip)
6 # subcircuits to actual integrated circuit chips plus optional extra components,
7 # outside the chip. For example, the 'tinv' subcircuit has a complementary pair
8 # of MOSFETs, which this program maps to part of CD4007, assigning the pins,
9 # and mapping the other pair inside the CD4007 if another 'tinv' comes along.
11 import copy
12 import types
13 import sys
15 import tg
16 import tinv
17 import tnor
18 import tnand
19 import os
21 PROGRAM_NAME = "bb.py"
23 # Start chip numbering at this value.
24 CHIP_NO_START = int(os.environ.get("JC_CHIP_START", 1))
26 # Subcircuits to map that should be mapped physical ICs
27 SUBCIRCUITS_TO_MAP = ('tg', 'tinv', 'tnor', 'tnor3', 'tnand', 'tnand3', 'sp3t-1', 'sp3t-2', 'sp3t-3')
28 SUBCIRCUITS_CAN_MAP = ('tg', 'tinv', 'tnor', 'tnand') # subcircuits we actually can map to ICs, as of yet
29 SUBCIRCUITS_PASS = ('sp3t-1', 'sp3t-2', 'sp3t-3') # pass unchanged to pads.py
31 # If False, use discrete resistors
32 # If True, use the MDP140 resistor network
33 USE_RESISTOR_CHIP = bool(int(os.environ.get("JC_USE_RESISTOR_CHIP", False)))
34 RESISTOR_CHIP = "MDP1403" # Vishay isolated 7-resistor network
36 KNOWN_CHIPS = (
37 "CD4007", # dual complementary MOSFETs + binary inverter
38 "CD4016", # quad transmission gates
39 #RESISTOR_CHIP # handled above
42 # Pairs of pins
43 RESISTOR_NETWORK_PINS = ((1, 14), (2, 13), (3, 12), (4, 11), (5, 10), (6, 9), (7,8))
45 def combine_dicts(dict1, dict2):
46 """Combine two dictionaries; dict2 takes priority over dict1."""
47 ret = copy.deepcopy(dict1)
48 ret.update(dict2)
49 return ret
51 def read_netlist(filename):
52 """Read a SPICE input deck, returning subcircuit nodes and definitions."""
54 if filename == "-":
55 f = sys.stdin
56 else:
57 f = file(filename, "rt")
58 subckt_nodes = {}
59 subckt_defns = {}
60 toplevel = []
61 name = None
62 while True:
63 line = f.readline()
64 if len(line) == 0:
65 break
67 line = line.strip()
69 if line.startswith(".subckt"):
70 words = line.split()
71 name = words[1]
72 nodes = words[2:]
74 subckt_nodes[name] = nodes
75 elif line.startswith(".ends"):
76 name = None
77 else:
78 if name is not None:
79 if not subckt_defns.has_key(name):
80 subckt_defns[name] = []
81 subckt_defns[name].append(line)
82 else:
83 # Top-level elements, skip blank lines, commands and comments
84 if len(line.strip()) != 0 and line[0] not in ('.', '*'):
85 toplevel.append(line)
87 return subckt_nodes, subckt_defns, toplevel
89 def rewrite_subckt(subckt_defns, s):
90 """Partially rewrite subcircuit 's', using rules from a module of the same name.
92 Removes parts in mod.parts_consumed, keeps parts in mod.parts_kept.
94 Does not generate any new parts."""
96 mod = globals()[s]
97 lines = []
98 for line in subckt_defns[s]:
99 words = line.split()
100 name = words[0]
101 args = words[1:]
103 if name in mod.parts_consumed:
104 # Skip this line
105 pass
106 elif name in mod.parts_kept:
107 lines.append(line)
108 else:
109 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)
111 # Map node positions in arguments list (subckt invocation, X..), to node names
112 pos2node = {}
113 for i in range(0, len(mod.nodes)):
114 pos2node[mod.nodes[i]] = i
116 # Rewrite
117 subckt_defns[s] = lines
119 return mod, subckt_defns, pos2node
121 next_serial = -1
122 def get_serial():
123 """Get a unique, increasing number."""
124 global next_serial
126 next_serial += 1
127 return next_serial
129 def get_floating(n=None):
130 """Return a unique disconnected (floating) node name.
132 If n is given, return a dict of n floating node names. Otherwise returns one in a string."""
133 if n is not None:
134 return get_floating_n(n)
136 return "NC__%s" % (get_serial(), )
138 def get_floating_n(n):
139 """Get n floating nodes, see get_floating()."""
140 fs = {}
141 for x in range(1, n + 1):
142 fs[x] = get_floating()
143 return fs
145 def is_floating(node_name):
146 """Return whether the given node name is probably not connected;
147 generated from get_floating()."""
148 return node_name.startswith("NC_")
150 def chip_has_pins_available(pins_needed, pins):
151 """Return whether 'pins' has not-connected pins, all those required in pins_needed."""
152 for p in pins_needed:
153 if not is_floating(pins[p]):
154 return False
155 return True
157 # This program doesn't thoroughly parse SPICE cards, so you must
158 # give it parameters that are not nodes, to not map at all.
159 # For example, '12k' for the 12k resistors. TODO: better parsing
160 def find_chip(chips, model_needed, pins_needed_options):
161 """Return an index into the chips array, and what pins, with the given model and the pins free.
163 pins_needed_options is a list of lists, of any acceptable set of pins to use.
165 A new chip is added if none are found. """
167 result = find_chip_no_add(chips, model_needed, pins_needed_options)
168 if result is not None:
169 return result
171 # No chips found with model model_needed and with pins free. Need more chips.
172 if model_needed not in KNOWN_CHIPS and not model_needed.startswith(RESISTOR_CHIP):
173 raise "Model %s not known, it is not in %s, not recognized!" % (model_needed, KNOWN_CHIPS)
175 # Add a new chip!
176 # Assume all are 14-pin chips
177 chips.append((model_needed, get_floating(14)))
179 result = find_chip_no_add(chips, model_needed, pins_needed_options)
180 if result is not None:
181 return result
183 raise "Tried to find model %s with pins %s free, then added a new chip but couldn't find it!" % (
184 model_needed, pins_needed_options)
186 def find_chip_no_add(chips, model_needed, pins_needed_options):
187 """Find chip to use (see find_chip), but return None if not found instead of adding."""
188 for i, chip in enumerate(chips):
189 model, pins = chip
190 if model != model_needed:
191 continue
193 for option_num, option in enumerate(pins_needed_options):
194 # Note: I do not yet check if the chip has pins that are used, but are assigned to
195 # the same node that is required. The pins must be unused.
196 if chip_has_pins_available(option, pins):
197 #print "* Found model %s with pins %s free: chip #%s" % (model_needed, option, i)
198 return i, option_num
199 return None
201 def find_pins_needed(pins):
202 """From a mod.pins[x] dict, return the pins needed for each SPICE model, for find_chip()"""
203 need = {}
204 for x in pins.values():
205 if type(x) == types.TupleType:
206 model, pin = x
207 if not need.has_key(model):
208 need[model] = []
210 need[model].append(pin)
211 elif type(x) == types.ListType:
212 for mp in x:
213 model, pin = mp
214 if not need.has_key(model):
215 need[model] = []
217 need[model].append(pin)
219 return need
221 def assign_part(chips, subckt_defns, extra, model_name, external_nodes, refdesg):
222 """Assign a given model to a physical chip, using the names of the external nodes.
224 chips: array of existing physical chips that can be assigned from
225 mod: logical model to map to, like 'tinv'
226 external_nodes: dict mapping internal nodes in the model, to external nodes in the world
229 mod = globals()[model_name]
230 subckt_lines = subckt_defns[model_name]
233 # Store new pin assignments
234 assignments = {}
235 for p in mod.parts_generated:
236 assignments[p] = {}
238 need_options = []
239 the_model = None
240 for p in mod.pins:
241 need = find_pins_needed(p)
242 if len(need) > 1:
243 raise "Sorry, more than one model is needed: %s, but only one is supported right now." % (need,)
244 if the_model is None:
245 the_model = need.keys()[0]
246 else:
247 if the_model != need.keys()[0]:
248 raise "Sorry, different models are needed: %s, but already decided on %s earlier. This is not yet supported." % (the_model, need[0])
250 need_options.append(need.values()[0])
252 #print "* Searching for model %s with one of pins: %s" % (the_model, need_options)
253 chip_num, option_num = find_chip(chips, the_model, need_options)
254 #print "* FOUND CHIP:",chip_num
255 #print "* WITH PINS (option #%s):" % (option_num,), mod.pins[option_num]
257 findings = []
258 for node_pin in combine_dicts(mod.global_pins, mod.pins[option_num]).iteritems():
259 node, pin = node_pin
261 if type(pin) == types.TupleType:
262 part, pin = pin
263 findings.append((node, part, pin))
264 elif type(pin) == types.ListType:
265 for pp in pin:
266 part, p = pp
267 findings.append((node, part, p))
268 else:
269 part = None
270 findings.append((node, part, pin))
272 for node, part, pin in findings:
273 #print "* %s -> %s:%s" % (node, part, pin)
275 if part is not None:
276 if node.startswith("$G_") or node == "0":
277 new_node = node # global node (LTspice)
278 elif external_nodes.has_key(node):
279 #sys.stderr.write("Mapping external node %s, map = %s\n" % (node, external_nodes))
280 new_node = external_nodes[node] # map internal to external node
281 else:
282 #sys.stderr.write("Mapping internal-only node %s\n" % (node,))
283 # 'refdesg' here is prefixed with X, for a subcircuit instantation,
284 # but we only need the subcircuit prefix for a node, without the
285 # X prefix.
286 assert refdesg[0:2] == 'X$', "Assumed refdesg %s began with X$, but it didn't" % (refdesg,)
287 refdesg_without_letter = refdesg[2:]
288 new_node = rewrite_node("", refdesg_without_letter, node)
289 #sys.stderr.write("Rewriting to node = %s\n" % (new_node,))
291 #sys.stderr.write("Adding to chips: %s\n" % (new_node,))
292 chips[chip_num][1][pin] = new_node
294 internal_only_nodes = {}
296 # Now place any ++additional parts++ (resistors, etc.) within the subcircuit model
297 # that connect to the chip, but are not part of the chip.
298 for line in subckt_lines:
299 words = line.split()
300 new_words = []
302 if words[0][0] == "M":
303 raise ("This line:\t%s\nIn the subcircuit '%s', has a MOSFET left " +
304 "over that wasn't converted. Probably it was meant to be converted to an IC? " +
305 "Comment out this line in %s if you are sure you want this, otherwise " +
306 "double-check the model definition for '%s', specifically, " +
307 "parts_consumed and parts_kept.\nAlso, check if the model was rewritten!") % (line, model_name, PROGRAM_NAME, model_name)
310 #name = "%s_%s_%s_%s" % (words[0], model_name, chip_num, refdesg)
311 name = "%s%s$%s" % (words[0][0], refdesg, words[0])
313 new_words.append(name)
315 args = words[1:-1]
316 model_name = words[-1]
318 # Replace internal nodes with external nodes.
319 for w in args:
320 if w in external_nodes.keys():
321 new_words.append(external_nodes[w])
322 elif is_floating(w):
323 # TODO ???
324 new_words.append(get_floating())
325 else:
326 if not internal_only_nodes.has_key(w):
327 #internal_only_nodes[w] = "%s$%s$%s" % (w[0], refdesg, w)
328 assert refdesg[0:2] == 'X$', "Reference designator %s expected to begin with X$, but didn't" % (refdesg,)
329 refdesg_without_letter = refdesg[2:]
330 internal_only_nodes[w] = rewrite_node("", refdesg_without_letter, w)
331 new_words.append(internal_only_nodes[w])
333 # TODO: comment this out, if the above works
334 #raise "Could not map argument '%s' in subcircuit line %s, not found in %s" % (w, line, external_nodes)
336 new_words.append(model_name)
338 if words[0][0] == "R" and USE_RESISTOR_CHIP:
339 resistor_value = words[3]
340 # Find resistor chip, named after resistor value.
341 chip_num, option_num = find_chip(chips, RESISTOR_CHIP + "-" + resistor_value.upper(), RESISTOR_NETWORK_PINS)
342 pins = RESISTOR_NETWORK_PINS[option_num]
344 # Assign pins on resistor network to nodes
345 # Order doesn't matter for electrical purposes, but
346 # for RN, swap pins since often RP and RN are back-to-back.
347 # Doesn't affect electrical connections, though better
348 # for routing purposes.
349 if words[0].endswith("RN"):
350 chips[chip_num][1][pins[0]] = new_words[2]
351 chips[chip_num][1][pins[1]] = new_words[1]
352 else:
353 chips[chip_num][1][pins[0]] = new_words[1]
354 chips[chip_num][1][pins[1]] = new_words[2]
356 else:
357 extra.append(" ".join(new_words))
359 return chips, extra
361 def any_pins_used(pins):
362 """Return whether any of the pins on a chip are used. If False, chip isn't used."""
363 for p in pins.values():
364 if not is_floating(p):
365 return True
366 return False
368 def dump_chips(chips):
369 """Show the current chips and their pin connections."""
370 for i, c in enumerate(chips):
371 m, p = c
373 if not any_pins_used(p):
374 print "* Chip #%s - %s no pins used, skipping" % (i + CHIP_NO_START, m)
375 continue
377 print "* Chip #%s - %s pinout:" % (i + CHIP_NO_START, m)
378 for k, v in p.iteritems():
379 print "* \t%s: %s" % (k, v)
381 print "IC_%s_%s" % (m, i + CHIP_NO_START),
382 # Assumes all chips are 14-pin, arguments from 1 to 14 positional
383 for k in range(1, 14+1):
384 print p[k],
385 print m
386 print
388 def dump_extra(extra):
389 """Shows the extra, supporting subcircuit parts that support the IC and are part of the subcircuit."""
390 print "* Parts to support subcircuits:"
391 for e in extra:
392 print e
394 def make_node_mapping(internal, external):
395 """Make a node mapping dictionary from internal to external nodes.
396 Keys of the returned dictionary are internal nodes (of subcircuit), values are external."""
397 d = {}
398 d.update(zip(internal, external))
399 return d
401 def rewrite_refdesg(original, prefix):
402 """Prefix a reference designator, preserving the first character."""
403 new_refdesg = "%s%s$%s" % (original[0], prefix, original)
405 #sys.stderr.write("@ rewrite_refdesg = %s\n" % (new_refdesg,))
406 return new_refdesg
408 def rewrite_node(prefix, circuit_inside, original_node_name):
409 """Rewrite a node name inside a subcircuit, prefixing it with
410 prefix and the name of the circuit that it is inside (both
411 are optional)."""
413 # Globals never rewritten
414 if original_node_name.startswith("$G_") or original_node_name == "0":
415 return original_node_name
417 new_name = original_node_name
418 if circuit_inside:
419 new_name = "%s$%s" % (circuit_inside, new_name)
421 if prefix:
422 new_name = "%s$%s" % (prefix, new_name)
424 # Nodes don't need to begin with spurious '$'s (can happen if no prefix)
425 if new_name[0] == '$':
426 new_name = new_name[1:]
428 #sys.stderr.write("@@ rewrite_node = %s\n" % (new_name,))
429 return new_name
431 def is_expandable_subcircuit(refdesg, model):
432 """Return whether the SPICE reference designator and model is
433 a) a subcircuit, b) _and_ it can be hierarchically expanded further."""
434 return refdesg[0] == 'X' and model not in SUBCIRCUITS_TO_MAP
436 def expand(subckt_defns, subckt_nodes, line, prefix, outer_nodes, outer_prefixes):
437 """Recursively expand a subcircuit instantiation if needed."""
438 words = line.split()
439 outer_refdesg = words[0]
440 outer_model = words[-1]
441 outer_args = words[1:-1]
443 #sys.stderr.write("expand(%s,%s,%s,%s)\n" % (line, prefix, outer_nodes, outer_prefixes))
444 if is_expandable_subcircuit(outer_refdesg, outer_model):
445 nodes = make_node_mapping(subckt_nodes[outer_model], outer_args)
446 new_lines = []
447 new_lines.append(("* %s: Instance of subcircuit %s: %s" % (outer_refdesg, outer_model, " ".join(outer_args))))
449 # Notes that are internal to the subcircuit, not exposed in any ports
450 internal_only_nodes = {}
452 for sline in subckt_defns[outer_model]:
453 swords = sline.split()
454 inner_refdesg = swords[0]
455 inner_model = swords[-1]
456 inner_args = swords[1:-1]
458 if is_expandable_subcircuit(inner_refdesg, inner_model):
459 # Recursively expand subcircuits, to transistor-level subcircuits (SUBCIRCUITS_TO_MAP)
460 nodes_to_pass = {}
461 prefixes_to_pass = {}
462 for n in nodes:
463 if outer_nodes.has_key(nodes[n]):
464 nodes_to_pass[n] = outer_nodes[nodes[n]]
465 prefixes_to_pass[nodes_to_pass[n]] = outer_prefixes[nodes_to_pass[n]]
466 else:
467 nodes_to_pass[n] = nodes[n]
468 prefixes_to_pass[nodes_to_pass[n]] = prefix
469 # Only append separator if not empty
470 if prefix:
471 prefixes_to_pass[nodes_to_pass[n]] += "$"
473 # Chop '$' if begins with it
474 if len(prefixes_to_pass[nodes_to_pass[n]]) >= 1 and prefixes_to_pass[nodes_to_pass[n]][0] == '$':
475 prefixes_to_pass[nodes_to_pass[n]] = prefixes_to_pass[nodes_to_pass[n]][1:]
476 #sys.stderr.write("PASSING NODES: %s (outer=%s, inner=%s), outer_refdesg=%s, prefix=%s\n" % (nodes_to_pass, outer_nodes, nodes, outer_refdesg, prefix))
477 #sys.stderr.write("\tPASSING PREFIXES: %s (outer=%s)\n" % (prefixes_to_pass, outer_prefixes))
478 new_lines.extend(expand(subckt_defns, subckt_nodes, sline, prefix +
479 "$" + outer_refdesg, nodes_to_pass, prefixes_to_pass))
480 else:
481 new_words = []
482 # Nest reference designator
483 new_words.append(rewrite_refdesg(inner_refdesg, prefix + "$" + outer_refdesg))
485 # Map internal to external nodes
486 for w in inner_args:
487 #print "****", word
488 if w in nodes.keys():
489 # Follow up the hierarchy. Without doing this, leads to:
490 # incomplete nets. For example, dtflop-ms_test.net maps:
492 # In nodes {'Q': 'Q', 'C': 'CLK', 'D': 'D'}, rewrite C -> CLK, prefix [correct]
493 # In nodes {'Q': 'Q', 'C': 'CLK', 'D': 'D'}, rewrite C -> CLK, prefix [correct]
495 # but because the 'nodes' dict is only for the parent, it
496 # doesn't map this correctly, leading to an unconnected node:
498 # In nodes {'OUT': '_C', 'IN': 'C'}, rewrite IN -> C, prefix Xflipflop [wrong]
500 # It should be CLK, not Xflipflop$C. These problems will occur
501 # with more nesting of subcircuits
502 if nodes[w] in outer_nodes:
503 # This is a port of this subcircuit, ascends hierarchy
504 new_words.append(outer_prefixes[outer_nodes[nodes[w]]] + outer_nodes[nodes[w]])
505 #new_words.append(outer_nodes[nodes[w]])
506 #sys.stderr.write("Node %s -> %s -> %s (outer nodes=%s, prefixes=%s) (prefix=%s, refdesgs=%s,%s)\n" %
507 # (w, nodes[w], outer_nodes[nodes[w]], outer_nodes, outer_prefixes, prefix,
508 # outer_refdesg, inner_refdesg))
509 else:
510 new_words.append(rewrite_node(prefix, "", nodes[w]))
512 elif is_floating(w):
513 # This is a port, but that is not connected on the outside, but
514 # still may be internally-connected so it needs a node name.
515 # Name it what it is called inside, hierarchically nested.
516 inner_node_map = make_node_mapping(inner_args, subckt_nodes[inner_model])
517 new_words.append(rewrite_node(prefix, outer_refdesg, inner_node_map[w]))
518 #print "Floating:",w," now=",new_words,"node map=",inner_node_map
519 else:
520 # A signal only connected within this subcircuit, but not a port.
521 # Make a new node name for it and replace it.
522 if not internal_only_nodes.has_key(w):
523 internal_only_nodes[w] = rewrite_node(prefix, outer_refdesg, w)
524 #print "* sline: %s, Subcircuit %s, mapping internal-only node %s -> %s" % (sline, outer_model, w, internal_only_nodes[w])
526 new_words.append(internal_only_nodes[w])
527 #new_words.append(w)
528 #raise "Expanding subcircuit line '%s' (for line '%s'), couldn't map word %s, nodes=%s" % (sline, line, w, nodes)
530 new_words.append(inner_model)
532 new_lines.append(" ".join(new_words))
533 #new_lines.append("")
534 else:
535 new_lines = [line]
537 return new_lines
539 def test_flatten():
540 """Demonstrate flattening of a hierarchical subcircuit."""
541 if os.access("testcases", os.R_OK | os.X_OK):
542 testdir = "testcases"
543 else:
544 testdir = "."
546 i = 0
547 for case_in in sorted(os.listdir(testdir)):
548 if ".in" not in case_in or ".swp" in case_in:
549 continue
550 case = case_in.replace(".in", "")
552 subckt_nodes, subckt_defns, toplevel = read_netlist("%s/%s.in" % (testdir, case))
554 actual_out = []
555 for line in toplevel:
556 actual_out.extend(expand(subckt_defns, subckt_nodes, line, "", {}, {}))
558 outfile = file("%s/%s.act" % (testdir, case), "wt")
559 for line in actual_out:
560 if line[0] == '*':
561 continue
562 outfile.write("%s\n" % (line,))
563 outfile.close()
565 if os.system("diff -u %s/%s.exp %s/%s.act" % (testdir, case, testdir, case)) != 0:
566 print "%2d. FAILED: %s" % (i, case)
567 raise SystemExit
568 else:
569 print "%2d. Passed: %s" % (i, case)
570 print "-" * 70
571 i += 1
573 def test_assignment():
574 """Demonstrate subcircuit assignment."""
575 subckt_nodes, subckt_defns, toplevel = read_netlist("mux3-1_test.net")
577 mod_tinv, subckt_defns, pos2node_tinv = rewrite_subckt(subckt_defns, "tinv")
578 tg_tinv, subckt_defns, pos2node_tg = rewrite_subckt(subckt_defns, "tg")
581 # Available chips
582 chips = [
583 ("CD4007", get_floating(14) ),
584 ("CD4016", get_floating(14) ),
585 #("CD4007", get_floating(14) )
588 extra = []
589 chips, extra = assign_part(chips, subckt_defns, extra, "tinv",
591 "Vin": "IN_1",
592 "PTI_Out": "PTI_Out_1",
593 "NTI_Out": "NTI_Out_1",
594 "STI_Out": "STI_Out_1",
597 chips, extra = assign_part(chips, subckt_defns, extra, "tinv",
599 "Vin": "IN_2",
600 "PTI_Out": "PTI_Out_2",
601 "NTI_Out": "NTI_Out_2",
602 "STI_Out": "STI_Out_2",
605 chips, extra = assign_part(chips, subckt_defns, extra, "tg",
607 "IN_OUT": "IN_1",
608 "OUT_IN": "OUT_1",
609 "CONTROL": "CTRL_1",
611 chips, extra = assign_part(chips, subckt_defns, extra, "tg",
613 "IN_OUT": "IN_2",
614 "OUT_IN": "OUT_2",
615 "CONTROL": "CTRL_2",
617 chips, extra = assign_part(chips, subckt_defns, extra, "tg",
619 "IN_OUT": "IN_3",
620 "OUT_IN": "OUT_3",
621 "CONTROL": "CTRL_3",
623 chips, extra = assign_part(chips, subckt_defns, extra, "tg",
625 "IN_OUT": "IN_4",
626 "OUT_IN": "OUT_4",
627 "CONTROL": "CTRL_4",
630 dump_chips(chips)
631 dump_extra(extra)
633 def usage():
634 print """usage: %s input-filename [output-filename | -p]
636 input-filename A transistor-level SPICE netlist (.net)
637 output-filename Chip-level SPICE netlist (.net2)
639 If output-filename is omitted, input-filename is used but
640 with '2' appended, so .net becomes .net2 by convention.
642 Either filenames can be "-" for stdin or stdout, respectively.
644 The -p flag, instead of an output filename will also run pads.py
645 so that both .net2 and .pads files will be generated.
646 """ % (PROGRAM_NAME, )
647 raise SystemExit
649 def main():
650 if len(sys.argv) < 2:
651 usage()
652 input_filename = sys.argv[1]
654 generate_pads = len(sys.argv) > 2 and sys.argv[2] == "-p"
656 subckt_nodes, subckt_defns, toplevel = read_netlist(input_filename)
658 # Redirect stdout to output file
659 if len(sys.argv) > 2 and sys.argv[2] != "-p":
660 output_filename = sys.argv[2]
661 else:
662 output_filename = input_filename + "2"
664 if output_filename != "-":
665 sys.stdout = file(output_filename, "wt")
667 print "* Chip-level netlist, converted from %s by %s" % (input_filename, PROGRAM_NAME)
669 # Circuits to rewrite need to be loaded first. Any transistor-level circuits
670 # you want to replace with ICs, are loaded here.
671 modules = pos2node = {}
672 for s in SUBCIRCUITS_CAN_MAP:
673 if subckt_defns.has_key(s):
674 modules[s], subckt_defns, pos2node[s] = rewrite_subckt(subckt_defns, s)
676 # First semi-flatten the circuit
677 flat_toplevel = []
678 for line in toplevel:
679 flat_toplevel.extend((expand(subckt_defns, subckt_nodes, line, "", {}, {})))
681 print "* Flattened top-level, before part assignment:"
682 for f in flat_toplevel:
683 print "** %s" % (f,)
684 print "* Begin converted circuit"
685 print
687 # Available chips
688 chips = []
690 extra = []
691 for line in flat_toplevel:
692 words = line.split()
693 if words[0][0] == 'X':
694 refdesg = words[0]
695 model = words[-1]
696 args = words[1:-1]
698 #print "MODEL=%s, args=%s" % (model, args)
700 #print subckt_defns[model]
701 #print subckt_nodes[model]
703 if model in SUBCIRCUITS_CAN_MAP:
704 nodes = make_node_mapping(subckt_nodes[model], args)
705 #print nodes
706 chips, extra = assign_part(chips, subckt_defns, extra, model, nodes, refdesg)
707 elif model in SUBCIRCUITS_PASS:
708 print line
709 else:
710 raise "Cannot synthesize model: %s, line: %s" % (model, line)
711 else:
712 print line
714 dump_chips(chips)
715 dump_extra(extra)
717 sys.stdout = sys.stderr
719 if generate_pads:
720 import os
721 os.system("python pads.py %s" % (output_filename,))
723 if __name__ == "__main__":
724 if len(sys.argv) > 1 and sys.argv[1] == "-t":
725 #test_assignment()
726 test_flatten()
727 raise SystemExit
728 main()