3 ############################################################################
4 # tragesym - create gEDA symbols out of structured textfiles
6 # copyright : (C) 2001,2002,2003,2004,2006,2007, 2008 by Werner Hoch
7 # email : werner.ho@gmx.de
8 ############################################################################
10 # This program is free software; you can redistribute it and/or modify #
11 # it under the terms of the GNU General Public License as published by #
12 # the Free Software Foundation; either version 2 of the License, or #
13 # (at your option) any later version. #
15 ############################################################################
18 # - create pins and their elements
19 # - sort pins alphabetical
20 # - swap words of the pinlabels
21 # - negation lines if label is in "_","\" is for escape
22 # - rotate top and bottom pinlabels if wished
23 # - if the symbol's width specified is 0, then tragesym calculates the
24 # symbol width based on the greater number of pins at the top or at the
25 # bottom of the symbol
27 import getopt
, os
.path
, re
, string
, sys
29 ##################### GLOBALS ############################################
33 preset_options
= {"wordswap":"yes",
36 "generate_pinseq":"yes",
38 "pinwidthvertikal":"400",
39 "pinwidthvertical":"400",
40 "pinwidthhorizontal":"400"}
41 official_attr
= ["version", "name", "device", "refdes", "footprint", "numslots",
42 "slot", "slotdef","description", "comment", "author",
43 "documentation","value","dist-license", "use-license"]
44 single_attr_warning
= ["device", "footprint", "author", "documentation",
45 "description", "numslots","dist-license", "use-license"]
46 single_attr
= ["slot"]
47 multiple_attr
= ["slotdef", "comment"]
48 stylelist
= ["line","dot","clk","dotclk","spacer","none"]
49 poslist
= ["l","r","t","b",""]
50 typelist
= ["in","out","io","oc","oe","pas","tp","tri","clk","pwr"]
51 translate_pintype
= {"i/o":"io", "i":"in", "o":"out", "p":"pas"}
52 P_NR
, P_SEQ
, P_TYPE
, P_STYLE
, P_POS
, P_NET
, P_LABEL
= 0,1,2,3,4,5,6
53 re_section_header
= re
.compile("^\s*\[(?P<name>.+)]\s*$")
55 ################################## CLASSES ###############################
58 '''Encapsulation for all data related to a pin.'''
59 def __init__(self
, element
):
61 element
.extend(('', '', '', '', '', '', ''))
62 self
.nr
= element
[P_NR
].strip()
63 self
.seq
= element
[P_SEQ
].strip()
64 pintype
= element
[P_TYPE
].lower().strip()
65 self
.type = translate_pintype
.get(pintype
, pintype
)
66 self
.style
= element
[P_STYLE
].lower().strip()
67 self
.pos
= element
[P_POS
].lower().strip()
68 self
.net
= element
[P_NET
].strip()
69 self
.label
= element
[P_LABEL
].strip()
72 str = "Pin object (nr:" + self
.nr
+ " seq:" + self
.seq
+ " type:" + self
.type
73 str += " style:" + self
.style
+ " pos:" + self
.pos
+ " net:" + self
.net
74 str += " label:" + self
.label
+ ")"
77 def __cmp__(self
, other
):
79 Comparison function for the pin.
80 * The sorting rule is to place the nets first.
81 * The pin position is the second sorting key
82 * The pin label is the third sorting key
84 if not isinstance(other
, Pin
):
86 ret
= cmp(self
.net
, other
.net
)
89 ret
= cmp(other
.pos
, self
.pos
)
92 return cmp(splitspecial(parselabel(self
.label
)),
93 splitspecial(parselabel(other
.label
)))
97 if self
.style
=="spacer":
99 print "Error: there must be a position with a spacer.\n"
101 if self
.pos
not in poslist
:
102 print "Error: position is not allowed: \n", self
105 if self
.style
!= "none":
106 if self
.seq
.isdigit():
107 string
.atoi(self
.seq
)
109 print "pinseq needs to be a number: \n", self
111 if self
.type not in typelist
:
112 print "Pintype not allowed: \n", self
114 if self
.style
not in stylelist
:
115 print "Style is not allowed: \n", self
117 if self
.pos
not in poslist
:
118 print "Position is not allowed: \n", self
120 if self
.pos
== "" and self
.net
== "":
121 print "There must be either position or a netlabel: \n", self
125 ################################# FUNCTIONS ##############################
128 '''Print a usage message.'''
129 print "tragesym version " + VERSION
130 print "(C) 2001,2002,2003,2004,2006,2007 by Werner Hoch <werner.ho@gmx.de>"
131 print "Usage is: ", sys
.argv
[0] ,"<infile> <outfile>"
135 '''returns a stripped label without overbar markers "\_"'''
139 if letter
== '\\' and slash
== 0:
141 elif slash
== 1 and letter
== '_':
148 textout
=textout
+letter
151 if slash
== 1 or neg
== 1:
152 print '''unbalanced overbars or escapesequence: ''', str
153 print '''the overbar starts and ends with "\_" example: \"\_enable\_'''
154 print '''to write a "\" use "\\"'''
158 ## round *unsigned* integer x to closest r
159 def round_closest(x
,r
):
160 return x
-(x
+r
/2)%r+r/2
162 ## returns the words in reverse order
164 list=string
.split(str," ")
170 ## split a string at the first tab or equal char
171 def split_tab_equal(str,n
=1):
172 list_tab
=string
.split(str,'\t',n
)
173 list_equal
=string
.split(str,'=',n
)
174 if len(list_tab
[0]) < len(list_equal
[0]):
179 ## returns 2 dicts: (options, attr) and 2 arrays: (devices, pins)
180 def readsrc(filename
):
184 f
= open(filename
,"r")
185 content
= f
.readlines()
188 for lineraw
in content
:
189 line
= lineraw
.rstrip()
193 match
= re_section_header
.match(line
)
194 if match
: # find a section
195 section
=match
.group('name')
197 elif section
=="" or line
[0]=="#" \
198 or len(string
.strip(line
)) == 0: # comment, empty line or no section
200 if section
=="options":
201 element
=split_tab_equal(line
,1)
203 options
[string
.strip(element
[0])]=string
.strip(element
[1])
204 elif section
=="geda_attr":
205 element
=split_tab_equal(line
,1)
206 if len(element
) < 2 or len(element
[1].strip()) == 0:
207 print 'Warning: Empty attribute "%s" in the geda_attr section' % element
[0]
208 print ' The incomplete attribute will be dropped'
211 while geda_attr
.has_key((element
[0],nr
)):
213 geda_attr
[(string
.strip(element
[0]),nr
)]=string
.strip(element
[1])
214 elif section
=="pins":
215 element
=string
.split(line
,"\t")
217 pins
.append(Pin(element
))
219 print linenr
, ": illegal section name: ", section
221 return options
, geda_attr
, pins
224 def splitspecial(str):
226 makes a list out of a string:
227 "3abc345x?" --> ["",3,"abc",345,"x?"]
233 if letter
not in string
.digits
:
237 list.append(int(current
))
248 list.append(int(current
))
253 def writesym(filename
,options
,attr
,pins
):
254 o_symwidth
=string
.atoi(options
["sym_width"])
255 o_hdist
=string
.atoi(options
["pinwidthhorizontal"])
257 # If pinwidthvertikal was defined, use it, else use pinwidthvertical
258 # This keeps compatibility with older versions, while fixing the spell
260 if options
["pinwidthvertikal"] != preset_options
["pinwidthvertikal"]:
261 o_vdist
=string
.atoi(options
["pinwidthvertikal"])
263 o_vdist
=string
.atoi(options
["pinwidthvertical"])
265 o_wordswap
=options
["wordswap"]
266 o_rotate
=options
["rotate_labels"]
267 o_sort
=options
["sort_labels"]
271 ### Count the number of pins in each side
278 if pin
.pos
== "l": # left pin
280 elif pin
.pos
== "r": #right pin
281 numpright
=numpright
+1
282 elif pin
.pos
== "b": #right pin
283 numpbottom
=numpbottom
+1
284 elif pin
.pos
== "t": #right pin
287 # Calculate the position of the pins in the left and right side.
288 plefty
, prighty
= 0, 0
289 if numpleft
> numpright
:
290 plefty
=plefty
+(numpleft
-1)*o_vdist
293 prighty
=prighty
+(numpright
-1)*o_vdist
296 # Calculate the bottom left of the box
297 bottomleftx
, bottomlefty
= pinlength
+ 100, 100
299 bottomlefty
+= pinlength
301 # Calculate the minimum symwidth and increase it if necessary
302 calculated_top_symwidth
=(numptop
-1)*o_hdist
+2*o_hdist
303 calculated_bottom_symwidth
=(numpbottom
-1)*o_hdist
+2*o_hdist
305 calculated_symwidth
= max(calculated_bottom_symwidth
,
306 calculated_top_symwidth
)
308 if (numptop
+ numpbottom
> 0):
309 print "Note: use sym_width to adjust symbol width if texts overlap."
312 o_symwidth
= calculated_symwidth
314 # Calculate the symbol's high
315 if numpleft
< numpright
:
316 high
=(numpright
+1)*o_vdist
318 high
=(numpleft
+1)*o_vdist
319 topy
= bottomlefty
+ high
321 # Calculate the position of several items.
322 prightx
, prighty
= bottomleftx
+ pinlength
+ o_symwidth
, prighty
+ bottomlefty
+ o_vdist
323 pleftx
, plefty
= bottomleftx
- pinlength
, plefty
+ bottomlefty
+ o_vdist
324 ptopx
, ptopy
= bottomleftx
+ o_hdist
, bottomlefty
+ high
+ pinlength
325 pbottomx
, pbottomy
= bottomleftx
+ o_hdist
, bottomlefty
- pinlength
327 # Lets add some pad if sym_width was defined
328 ptopx
= ptopx
+ (o_symwidth
- calculated_top_symwidth
) / 2
329 pbottomx
= pbottomx
+ (o_symwidth
- calculated_bottom_symwidth
) / 2
331 ptopx
= round_closest(ptopx
, 100)
332 pbottomx
= round_closest(pbottomx
, 100)
334 f
= open(filename
, "w")
336 ### Draw the symbol version
337 if attr
.has_key(("version",1)):
338 value
=attr
[("version",1)]
339 if re
.match("[0-9]{8}$", value
):
340 f
.write("v " + value
+ " 1\n")
341 elif re
.match("[0-9]{8} 1$", value
):
342 f
.write("v " + value
+ "\n")
344 print "error: version string format invalid: [%s]" % value
347 print "error: version attribut missing"
354 if pin
.style
== "none": #
356 if pin
.style
=="spacer":
358 print "Warning: spacers are not supported when sorting labels"
360 elif pin
.pos
== "l": #left pin
361 plefty
=plefty
- o_vdist
#where to draw the _next_ pin
362 elif pin
.pos
== "r": #right pin
363 prighty
=prighty
- o_vdist
364 elif pin
.pos
== "b": # bottom pin
365 pbottomx
=pbottomx
+ o_hdist
366 elif pin
.pos
== "t": # top pin
367 ptopx
=ptopx
+ o_hdist
370 ### decide which pindirection to use
371 ## TODO: put all constants into a dictionary
372 if pin
.pos
== "l": #left pin
373 basex
, basey
= pleftx
, plefty
#where to draw this pin
374 xf
, yf
= 1, 0 # orientation factors
375 pint
=(200,50,6,0) # dx, dy, alignment, angle
376 pinl
=(350,0,0,0) # """"
377 pina
=(350,0,2,0) # """"
378 pinq
=(200,-50,8,0) # """"
379 swap
=0 # swap words in label ?
380 plefty
=plefty
- o_vdist
#where to draw the _next_ pin
381 elif pin
.pos
== "r": #right pin
382 basex
, basey
= prightx
, prighty
389 prighty
=prighty
- o_vdist
390 elif pin
.pos
== "b": # bottom pin
391 basex
, basey
=pbottomx
, pbottomy
393 if o_rotate
== "yes": # bottom pin with 90° text
404 pbottomx
=pbottomx
+ o_hdist
405 elif pin
.pos
== "t": # top pin
406 basex
, basey
=ptopx
, ptopy
408 if o_rotate
== "yes": # with 90° text
420 ptopx
=ptopx
+ o_hdist
422 if (pin
.style
=="dot" or #short pin and dot?
423 pin
.style
=="dotclk"):
429 f
.write("P %i"%basex
+" %i"%basey
+" %i"%x + " %i"%y
+ " 1 0 0\n")
432 pintx
, pinty
, pinta
, pintr
=pint
435 f
.write("T %i"%x+" %i"%y
+" 5 8 1 1 %i"%pintr
+" %i 1\n"%pinta
)
436 f
.write("pinnumber="+pin
.nr
+"\n")
438 pintx
, pinty
, pinta
, pintr
=pinq
441 f
.write("T %i"%x+" %i"%y
+" 5 8 0 1 %i"%pintr
+" %i 1\n"%pinta
)
442 f
.write("pinseq="+pin
.seq
+"\n")
443 ### draw pinlabel and pintype
444 pinlx
, pinly
, pinla
, pinlr
=pinl
445 pinax
, pinay
, pinaa
, pinar
=pina
446 if (pin
.style
=="clk" or #move label if clocksign
447 pin
.style
=="dotclk"):
456 if o_wordswap
=="yes" and swap
==1:
457 label
=swapwords(pin
.label
)
460 f
.write("T %i"%pinlx
+" %i"%pinly
+" 9 8 1 1 %i"%pinlr
+" %i 1\n"%pinla
)
461 f
.write("pinlabel="+label
+"\n")
462 f
.write("T %i"%pinax
+" %i"%pinay
+" 5 8 0 1 %i"%pinar
+" %i 1\n"%pinaa
)
463 f
.write("pintype="+pin
.type+"\n")
465 ### draw the negation bubble
466 if (pin
.style
=="dot" or pin
.style
=="dotclk"):
469 f
.write("V %i"%x+" %i"%y
+" 50 6 0 0 0 -1 -1 0 -1 -1 -1 -1 -1\n")
470 ### draw the clocksign
471 if (pin
.style
=="clk" or
472 pin
.style
=="dotclk"):
479 f
.write("L %i"%x1+" %i"%y1
+" %i"%x2+" %i"%y2
+ " 3 0 0 0 -1 -1\n")
480 f
.write("L %i"%x1+" %i"%y1
+" %i"%x3+" %i"%y3
+ " 3 0 0 0 -1 -1\n")
482 f
.write("B %i"%bottomleftx
+" %i"%bottomlefty
+" %i"%o_symwidth
+" %i"%high
+
483 " 3 0 0 0 -1 -1 0 -1 -1 -1 -1 -1\n")
485 ### draw the attributes
486 urefx
, urefy
= bottomleftx
+o_symwidth
, bottomlefty
+ high
+ 100
488 # Center name if we have top pins
490 namex
, namey
= (bottomleftx
+ o_symwidth
) / 2, (bottomlefty
+ high
) / 2 + 100
492 namex
, namey
= bottomleftx
, bottomlefty
+high
+100
499 ## special attribute format
500 if attr
.has_key(("refdes",1)):
501 f
.write("T %i"% urefx
+" %i"% urefy
+" 8 10 1 1 0 6 1\n")
502 f
.write("refdes=" + attr
[("refdes",1)] + "\n")
504 print "Warning: refdes attribut missing"
506 if attr
.has_key(("name",1)):
507 f
.write("T %i" %namex
+ " %i"% namey
+ " 9 10 1 0 0 0 1\n")
508 f
.write(attr
[("name",1)] + "\n")
510 print "Warning: name attribut missing"
512 ## attributes with same format and warnings
513 for a
in single_attr_warning
:
514 if attr
.has_key((a
,1)):
515 f
.write("T %i" %textx
+ " %i"% texty
+ " 5 10 0 0 0 0 1\n")
516 f
.write(a
+ "=" + attr
[(a
,1)] + "\n")
519 print "Warning: " + a
+ " attribut missing"
521 ## attributes without warning
522 for a
in single_attr
:
523 if attr
.has_key((a
,1)):
524 f
.write("T %i" %textx
+ " %i"% texty
+ " 5 10 0 0 0 0 1\n")
525 f
.write(a
+ "=" + attr
[(a
,1)] + "\n")
528 ## attributes with more than one equal name
529 for a
in multiple_attr
:
531 while attr
.has_key((a
,i
)):
532 f
.write("T %i" %textx
+ " %i"% texty
+ " 5 10 0 0 0 0 1\n")
533 f
.write(a
+ "=" + attr
[(a
,i
)] + "\n")
537 ## unknown attributes
538 for (name
, number
),value
in attr
.items():
539 if name
not in official_attr
:
540 f
.write("T %i" %textx
+ " %i"% texty
+ " 5 10 0 0 0 0 1\n")
541 f
.write(name
+ "=" + value
+ "\n")
543 print 'Warning: The attribute "%s=%s" is not official' %(name
, value
)
547 if pin
.style
== "none":
548 if not nets
.has_key(pin
.net
):
549 nets
[pin
.net
] = pin
.nr
551 nets
[pin
.net
] = nets
[pin
.net
] + ","+ pin
.nr
552 for key
,value
in nets
.items():
553 f
.write("T %i" %textx
+ " %i"% texty
+ " 5 10 0 0 0 0 1\n")
554 f
.write("net=" + key
+ ":" + value
+ "\n")
559 def mergeoptions(source_opt
,pre_opt
):
561 for item
in source_opt
.keys():
562 if ret
.has_key(item
):
563 ret
[item
]=source_opt
[item
]
565 print "This option is not allowed:", item
569 def generate_pinseq(pins
):
571 for nr
in xrange(len(pins
)):
572 if pins
[nr
].style
not in ["none","spacer"]:
573 pins
[nr
].seq
= "%i"%seq
577 ###################### MAIN #################################################
579 ## parse command line options
581 opts
, args
= getopt
.getopt(sys
.argv
[1:], "h", ["help"])
586 ## handle command line options
588 if o
in ("-h", "--help"):
599 if not os
.path
.exists(file_in
):
600 print "Input file " + file_in
+ " not found."
604 opts
,attr
,pins
=readsrc(file_in
)
606 options
=mergeoptions(opts
,preset_options
)
608 if options
["generate_pinseq"] == "yes":
609 pins
=generate_pinseq(pins
)
614 writesym(file_out
,options
,attr
,pins
)