PADS-PCB converter: generate a .map file, listing short->long name mapping, for debug...
[trinary.git] / bb / pads.py
blob2815f1686a6ab0b1e5b40c61df0861fd1999bc69
1 #!/usr/bin/env python
2 # Created:20080412
3 # By Jeff Connelly
5 # Created PADS-PCB netlist file, for FreePCB at http://www.freepcb.com/ ,
6 # or any other PCB layout program that supports the PADS-PCB format.
8 import sys, os
10 PROGRAM_NAME = "pads.py"
12 footprint_map = {
13 "CD4007": "14DIP300",
14 "CD4016": "14DIP300",
15 "R": "RC07", # 1/4 W resistor
16 "MDP1403-12K": "14DIP300", # 7 x resistor network, all 12k
17 "V": "1X2HDR-100-40", # header, **with 40 mil holes**
18 "sp3t-1": "SS14MDP2", # NKK switch, in position 1
19 "sp3t-2": "SS14MDP2", # NKK switch, in position 2
20 "sp3t-3": "SS14MDP2", # NKK switch, in position 3
21 #"1N4148": "DO-35", # Diode
22 "D": "DO-35", # All diodes (note: should really be specific!)
25 # TODO: Add bypass capacitor? Might be better done
26 # manually, on back of board, directly between 7 and 14.
28 # TODO: Connect not-connected pins? Probably a good idea but
29 # be careful about shorting something. Could also be done
30 # manually, with copious amounts of solder.
32 # Append/prepend this to all part and net names (note, prefix may
33 # cause problems with footprint mapping, if can't be recognized.)
34 UNIVERSAL_SUFFIX = ""
35 UNIVERSAL_PREFIX = ""
37 # Append/prepend to all net names
38 NETNAME_SUFFIX = os.environ.get("JC_NETNAME_SUFFIX", "")
39 NETNAME_PREFIX = ""
41 # If enabled, name resistors numerically R1, R2, ... instead of hierarchically.
42 SERIAL_RESISTORS = True
43 RESISTOR_SERIAL_START = int(os.environ.get("JC_RESISTOR_SERIAL_START", 0))
45 # If enabled, anything containing Vdd or Vss will be named Vdd or Vss--
46 # effectively disabling hierarchy for these power sources. **Note that if
47 # there are multiple Vdd and Vss, this will cause duplicate parts!**
48 SHORT_VDD_VSS = True
50 # If enabled, RX$Xflipflop$XX<n>$Xinv$R{P,N} will be mapped to ${P,N}<n>.
51 # Useful for laying out a single dtflop-ms_test, but may cause serious
52 # errors otherwise!
53 SHORT_DTFLOP_RP_RN = False
55 # TODO: remove these limitations on FreePCB, so that
56 # USE_SHORT_NAMES and BREAK_LONG_LINES can be turned off!
58 # If true, reference designators and node names will be assigned
59 # sequentially if needed, instead of using their hierarchical name
60 # from the net2 file. Some PCB programs have length limits
61 # on reference designators (FreePCB).
62 USE_SHORT_NAMES = True
64 # Split long nets over multiple lines.
65 BREAK_LONG_LINES = True
67 # If not false, nodes with only one connection will have a
68 # testpoint attached, using the given footprint.
69 #NAKED_NODE_FOOTPRINT = "HOLE_100_RND_200"
70 NAKED_NODE_FOOTPRINT = None
73 def usage():
74 print """usage: %s input-filename [output-filename]
76 input-filename A chip-level SPICE netlist (.net2)
77 output-filename PADS-PCB layout netlist (.pads)
79 If output-filename is omitted, input-filename is used but
80 with a .pads extension instead of .net2, or .pads appended.
82 Either filenames can be "-" for stdin or stdout, respectively.
83 """ % (PROGRAM_NAME, )
84 raise SystemExit
86 # MAX_NET_NAME_SIZE and FMAX_PIN_NAME_SIZE from
87 # svn://svn.berlios.de/freepcb/trunk/Shape.h and Netlist.h
88 # (TODO: remove these arbitrary limits in FreePCB)
89 MAX_SIZE = 39
91 long2short = {
92 # Names to preserve, if USE_SHORT_NAMES is True.
93 # Would be nice to preserve all/more net names, but
94 # some are just too long and hierarchical.
95 "$G_Vss": "$G_Vss",
96 "$G_Vdd": "$G_Vdd",
97 "0": "0",
99 next_serial = {
100 "R": 1,
101 "NX": 1,
102 "X": 1,
105 resistor_serial = RESISTOR_SERIAL_START
107 short2long_map = {}
109 def shorten(long, is_netname):
110 global short2long_map
112 short = UNIVERSAL_PREFIX + shorten_2(long, is_netname) + UNIVERSAL_SUFFIX
113 if is_netname:
114 short = NETNAME_PREFIX + short + NETNAME_SUFFIX
116 # Save short -> long for future reference
117 short2long_map[short] = long
119 return short
121 def shorten_2(long, is_netname):
122 global resistor_serial
124 if SHORT_VDD_VSS:
125 if "Vdd" in long:
126 return "Vdd"
127 if "Vss" in long:
128 return "Vss"
130 if long.startswith("R") and SERIAL_RESISTORS:
131 resistor_serial += 1
132 return "R%d" % (resistor_serial,)
134 if not USE_SHORT_NAMES:
135 return long
137 if SHORT_DTFLOP_RP_RN and long.startswith("RX$Xflipflop$XX"):
138 short = long[len("RX$Xflipflop$XX"):][0]
139 if long.endswith("RP"):
140 short = "RP" + short
141 elif long.endswith("RN"):
142 short = "RN" + short
143 else:
144 sys.stderr.write("dtflop RP/RN mapping enabled, but not RP/RN!")
145 raise SystemExit
146 return short
148 if len(long) <= MAX_SIZE:
149 # We got away with it this time.
150 return long
152 # Resistors are what you have to watch our for
153 if long2short.has_key(long):
154 short = long2short[long]
155 else:
156 # id = first letter, 'R', etc.
157 # Net names get a prefix of "NX" (for node extended;
158 # the name was too long so it was shortened)
159 if is_netname:
160 id = "NX"
161 else:
162 id = long[0]
165 #sys.stderr.write("%s\n" % (long,))
166 short = "%s%d" % (id, next_serial[id])
167 long2short[long] = short
168 next_serial[id] += 1
170 return short
172 if len(sys.argv) < 2:
173 usage()
174 filename = sys.argv[1]
176 # Redirect stdout to output file
177 if len(sys.argv) > 2:
178 output_filename = sys.argv[2]
179 else:
180 output_filename = filename.replace(".net2", ".pads")
181 if output_filename == filename:
182 output_filename = filename + ".pads"
183 if output_filename != "-":
184 sys.stdout = file(output_filename, "wt")
186 print "*PADS-PCB*"
187 print "*%s*" % (filename, )
189 parts = []
190 nets = {}
191 if filename == "-":
192 infile = sys.stdin
193 else:
194 infile = file(filename, "rt")
195 for line in infile.readlines():
196 if line[0] == '*':
197 continue
199 line = line.strip()
200 if len(line) == 0:
201 continue
203 words = line.split()
204 long_refdesg = words[0]
206 refdesg = shorten(long_refdesg, is_netname=False)
208 args = words[1:-1]
209 if refdesg[0] == 'V':
210 # Voltage sources only have two pins, other arguments
211 # may be PWL or other values (really one argument, but
212 # separated by spaces, so we'll see it as more than one).
213 args = [args[0], args[1]]
215 # Make nets list
216 for i, long_arg in enumerate(args):
217 if long_arg.startswith("NC_"):
218 continue
220 #if arg == "0":
221 # arg = "GND"
223 #sys.stderr.write("-> %s" % (line,))
224 arg = shorten(long_arg, is_netname=True)
226 if arg not in nets.keys():
227 nets[arg] = []
229 nets[arg].append("%s.%s" % (refdesg, i + 1))
231 model = words[-1]
233 # Make parts list
234 parts.append((refdesg, model))
237 # Map models to footprints
238 print "*PART*"
239 for part in parts:
240 refdesg, model = part
241 if footprint_map.has_key(model):
242 package = footprint_map[model]
243 elif footprint_map.has_key(refdesg[0]):
244 package = footprint_map[refdesg[0]]
245 else:
246 raise "Part name %s, model %s unknown, couldn't find in map: %s" % (refdesg, model, footprint_map)
247 print "%s %s" % (refdesg, package)
249 # Nets with only one node get testpoints for free (also appends to parts list)
250 if NAKED_NODE_FOOTPRINT:
251 for signal_name, nodes in nets.iteritems():
252 if len(nodes) == 1:
253 testpoint = "%s_tp" % (signal_name,)
254 print "%s %s" % (testpoint, NAKED_NODE_FOOTPRINT)
255 nets[signal_name].append(testpoint)
258 # Dump all nets
259 print "*NET*"
260 for signal, pins in nets.iteritems():
261 print "*SIGNAL* %s" % (signal,)
262 if BREAK_LONG_LINES:
263 i = 0
264 for p in pins:
265 print p,
266 # Break lines every so many pins
267 # (would be nice if didn't have to do this)
268 if i % 5 == 0:
269 print
270 i += 1
271 print
272 else:
273 print " ".join(pins)
275 # Print a newline at the end for picky layout programs (ExpressPCB)
276 print
279 # Save short->long map so that abbreviated part names
280 # (such as R1 or NX1) can be mapped to the full, hierarchical
281 # name for debugging purposes.
282 mapfilename = filename.replace(".net2", ".map")
283 if mapfilename == filename:
284 mapfilename = filename + ".map"
285 mapfile = file(mapfilename, "wt")
286 for short, long in short2long_map.items():
287 mapfile.write("%s,%s\n" % (short, long))
288 mapfile.close()