#--------------------------------------------------------------------------- # symbol.tcl --- # # xcircuit Tcl script for generating # a canonical circuit symbol # # Tim Edwards 12/1/05 for MultiGiG #--------------------------------------------------------------------------- #--------------------------------------------------------------------------- # Get the info label line declaring the text subcircuit, if one exists. #--------------------------------------------------------------------------- proc xcircuit::getsubckttext {name} { global XCOps set curhandle [object handle] set handle [page handle $name] if {$curhandle != $handle} {schematic goto} set pageparts [object $handle parts] set itext {} foreach j $pageparts { set etype [element $j type] if {$etype == "Label"} { set ltype [label $j type] if {$ltype == "info"} { set itext [label $j text] if {[string first ".subckt" $itext] >= 0} { if {$curhandle != $handle} {symbol goto} return $itext } } } } return "" } #--------------------------------------------------------------------------- # Create a matching symbol for a subcircuit page or SPICE file. # # This is a replacement for the procedure makesymbol in wrapper.tcl. # We make a sub-widget to list, change, and reorder the symbol pins, # similar to the "addliblist" procedure in wrapper.tcl # # Modified 1/5/06: If "filename" is non-null, then it points to a SPICE # netlist containing a subcircuit. A symbol is made to correspond to the # subcircuit definition, with a link to include the file. # # Modified 1/7/06: If "orderedpins" is non-null, then it contains the # list of pins in proper order. #--------------------------------------------------------------------------- proc xcircuit::makesymbol {{filename ""} {orderedpins ""}} { global XCOps config suspend true ;# suspend graphics and change count set techname [.dialog.techself.techselect cget -text] set symbolname [.dialog.textent.txt get] if {[string length $symbolname] == 0} { set symbolname [page label] if {[string length $symbolname] == 0 || [string first "Page " $symbolname] >= 0} { puts stderr "Symbol/Schematic has no name!" consoleup return } } if {[string first :: $symbolname] >= 0} { ;# symbolname has tech name embedded set techname "" } elseif {[string first ( $techname] >= 0} { ;# "(user)" specified set techname "::" } elseif {[string first :: $techname] < 0} { set techname "${techname}::" } # If "filename" is specified then we have a netlist, not a schematic. # Therefore, create an object but don't use xcircuit::symbol if {$filename == ""} { xcircuit::symbol make ${techname}${symbolname} $XCOps(library); set schematicname [schematic get] set noschem 0 } else { set schematicname $symbolname set noschem 1 } set pinspace 64 set halfspace [expr $pinspace / 2] set qtrspace [expr $pinspace / 4] # remove the old pin labels set oldpinlabels [object parts] foreach j $oldpinlabels { delete $j } set leftpins [.dialog.pinself.left.list index end] set toppins [.dialog.pinself.top.list index end] set rightpins [.dialog.pinself.right.list index end] set botpins [.dialog.pinself.bottom.list index end] set hpins $leftpins if {$rightpins > $leftpins} {set hpins $rightpins} set vpins $toppins if {$botpins > $toppins} {set vpins $botpins} set boxwidth [expr ($vpins + 1) * $pinspace] set boxheight [expr ($hpins + 1) * $pinspace] set hwidth [expr $boxwidth / 2] if {$hwidth < 256} {set hwidth 256} set hheight [expr $boxheight / 2] if {$hheight < 256} {set hheight 256} set sbox [polygon make box "-$hwidth -$hheight" "$hwidth $hheight"] set pinlabels {} # If we didn't make a symbol using xcircuit::symbol, now is the time # to generate the object. if {$noschem == 1} { select $sbox set handle [object make ${techname}${symbolname} $XCOps(library)] push $handle } # Ordered right->left->bottom->top, on logical grounds. set x [expr $hwidth + $qtrspace] set y [expr -($rightpins - 1) * $halfspace] for {set j 0} {$j < $rightpins} {incr j} { set tabx [expr $x - $qtrspace] polygon make 2 "$x $y" "$tabx $y" set pintext [.dialog.pinself.right.list get $j] lappend pinlabels $pintext set tlab [label make pin "$pintext" "$x $y"] label $tlab justify left label $tlab justify middle set nlab [element $tlab copy relative "-$halfspace 0"] label $nlab type normal label $nlab justify right incr y $pinspace deselect selected } set x [expr -$hwidth - $qtrspace] set y [expr -($leftpins - 1) * $halfspace] for {set j 0} {$j < $leftpins} {incr j} { set tabx [expr $x + $qtrspace] polygon make 2 "$x $y" "$tabx $y" set pintext [.dialog.pinself.left.list get $j] lappend pinlabels $pintext set tlab [label make pin "$pintext" "$x $y"] label $tlab justify right label $tlab justify middle set nlab [element $tlab copy relative "$halfspace 0"] label $nlab type normal label $nlab justify left incr y $pinspace deselect selected } set y [expr -$hheight -$qtrspace] set x [expr -($botpins - 1) * $halfspace] for {set j 0} {$j < $botpins} {incr j} { set taby [expr $y + $qtrspace] polygon make 2 "$x $y" "$x $taby" set pintext [.dialog.pinself.bottom.list get $j] lappend pinlabels $pintext set tlab [label make pin "$pintext" "$x $y"] rotate $tlab 270 label $tlab justify right label $tlab justify middle set nlab [element $tlab copy relative "0 $halfspace"] label $nlab type normal label $nlab justify left incr x $pinspace deselect selected } set y [expr $hheight + $qtrspace] set x [expr -($toppins - 1) * $halfspace] for {set j 0} {$j < $toppins} {incr j} { set taby [expr $y - $qtrspace] polygon make 2 "$x $y" "$x $taby" set pintext [.dialog.pinself.top.list get $j] lappend pinlabels $pintext set tlab [label make pin "$pintext" "$x $y"] rotate $tlab 90 label $tlab justify right label $tlab justify middle set nlab [element $tlab copy relative "0 -$halfspace"] label $nlab type normal label $nlab justify left incr x $pinspace deselect selected } deselect selected set nlab [label make "$symbolname" {0 0}] label $nlab justify middle label $nlab justify center element $nlab color set blue deselect selected parameter make substring index "?" parameter make substring class "X" if {$schematicname == $symbolname} { parameter make substring link "%n" } else { parameter make substring link "$schematicname" } set nlab [label make "{Parameter class} {Parameter index}" "0 -$pinspace"] label $nlab justify center element $nlab color set blue deselect selected # Determine if the schematic already has a "subckt" line. If so, # attempt to arrange the pin ordering from it. If not, create one. if {$noschem == 1} { set subckttext "" set pinlabels $orderedpins } else { set subckttext [xcircuit::getsubckttext $schematicname] } if {$subckttext == ""} { set subckttext [list {Text "spice1:.subckt %n"}] set pstring "" foreach j $pinlabels { if {[string length $pstring] > 60} { lappend subckttext [subst {Text "$pstring"}] lappend subckttext {Return} set pstring "+" } set pstring [join [list $pstring "$j"]] ;# preserves whitespace } lappend subckttext [subst {Text "$pstring"}] if {$noschem == 0} { schematic goto set bbox [join [page bbox all]] set x [expr ([lindex $bbox 2] + [lindex $bbox 0]) / 2] set y [expr [lindex $bbox 1] - $pinspace] set nlab [label make info "$subckttext" "$x $y"] label $nlab justify center deselect selected set y [expr $y - $pinspace] set nlab [label make info "spice-1:.ends" "$x $y"] symbol goto } set itext [list {Text "spice:"} {Parameter class} {Parameter index}] set pstring "" foreach j $pinlabels { if {[string length $pstring] > 60} { lappend itext [subst {Text "$pstring"}] lappend itext {Return} set pstring "+" } set pstring [join [list $pstring "%p$j"]] ;# preserves whitespace } set pstring [join [list $pstring "%n"]] ;# preserves whitespace lappend itext [subst {Text "$pstring"}] } else { set itext [list {Text "spice:"} {Parameter class} {Parameter index}] set pstring "" foreach j [lrange [lindex $subckttext 0] 2 end] { if {[string length $pstring] > 60} { lappend itext [subst {Text "$pstring"}] lappend itext {Return} set pstring "+" } set pstring [join [list $pstring "%p$j"]] ;# preserves whitespace } set pstring [join [list $pstring "%n"]] ;# preserves whitespace lappend itext [subst {Text "$pstring"}] } set y [expr -$hheight - 3 * $pinspace] deselect selected set nlab [label make info "$itext" "0 $y"] label $nlab justify center if {$noschem == 1} { deselect selected if {[string index $filename 0] != "/"} { set filename [join [concat [pwd] $filename] "/"] } set y [expr -$hheight - 4 * $pinspace] set itext [list {Text "spice@1:%F"}] lappend itext [subst {Text "$filename"}] set nlab [label make info "$itext" "0 $y"] label $nlab justify center deselect selected pop } else { library $XCOps(library) compose deselect selected symbol goto zoom view } config suspend false ;# unlocked state } #--------------------------------------------------------------------------- # Get the list of pins for the object. If the schematic has a "subckt" # line, then we use the pin names from it, in order. If not, then we # compile a list of all unique pin labels and arrange them in dictionary # alphabetical order. #--------------------------------------------------------------------------- proc xcircuit::getpinlist {schematicname} { global XCOps set subckttext [xcircuit::getsubckttext $schematicname] if {$subckttext == {}} { set pinlist {} deselect selected set objlist [object parts] foreach j $objlist { set etype [element $j type] if {$etype == "Label"} { set ltype [label $j type] if {$ltype == "local" || $ltype == "global"} { # Avoid netlist-generated pins by rejecting labels that # don't start with a font specifier. set subtype [lindex [lindex [lindex [label $j list] 0] 0] 0] if {$subtype == "Font"} { lappend pinlist [label $j text] } } } } set pinlist [lsort -unique -dictionary $pinlist] } else { set pinlist [lrange [lindex $subckttext 0] 2 end] } deselect selected return $pinlist } #--------------------------------------------------------------------------- # Figure out which list has the selection #--------------------------------------------------------------------------- proc xcircuit::getselectedpinwidget {} { set w .dialog.pinself.left.list set result [$w curselection] if {$result != {}} {return $w} set w .dialog.pinself.top.list set result [$w curselection] if {$result != {}} {return $w} set w .dialog.pinself.right.list set result [$w curselection] if {$result != {}} {return $w} set w .dialog.pinself.bottom.list set result [$w curselection] if {$result != {}} {return $w} } #--------------------------------------------------------------------------- # Remove a pin from the pin list #--------------------------------------------------------------------------- proc xcircuit::removeselectedpin {} { set w [xcircuit::getselectedpinwidget] if {$w != {}} { set idx [$w curselection] $w delete $idx } } #--------------------------------------------------------------------------- # Move a pin to the left side of the symbol #--------------------------------------------------------------------------- proc xcircuit::movepinleft {} { set w [xcircuit::getselectedpinwidget] if {$w != {}} { set idx [$w curselection] set pinname [$w get $idx] $w delete $idx .dialog.pinself.left.list insert end $pinname $w selection set $idx } } #--------------------------------------------------------------------------- # Move a pin to the top side of the symbol #--------------------------------------------------------------------------- proc xcircuit::movepintop {} { set w [xcircuit::getselectedpinwidget] if {$w != {}} { set idx [$w curselection] set pinname [$w get $idx] $w delete $idx .dialog.pinself.top.list insert end $pinname $w selection set $idx } } #--------------------------------------------------------------------------- # Move a pin to the right side of the symbol #--------------------------------------------------------------------------- proc xcircuit::movepinright {} { set w [xcircuit::getselectedpinwidget] if {$w != {}} { set idx [$w curselection] set pinname [$w get $idx] $w delete $idx .dialog.pinself.right.list insert end $pinname $w selection set $idx } } #--------------------------------------------------------------------------- # Move a pin to the bottom side of the symbol #--------------------------------------------------------------------------- proc xcircuit::movepinbottom {} { set w [xcircuit::getselectedpinwidget] if {$w != {}} { set idx [$w curselection] set pinname [$w get $idx] $w delete $idx .dialog.pinself.bottom.list insert end $pinname $w selection set $idx } } #--------------------------------------------------------------------------- # Create the pin arranger widget and add it to the dialog box. #--------------------------------------------------------------------------- proc xcircuit::addpinarranger {w {pinlist {}}} { frame ${w}.pinself frame ${w}.pinself.left frame ${w}.pinself.top frame ${w}.pinself.right frame ${w}.pinself.bottom label ${w}.pinself.left.title -text "Left Pins" label ${w}.pinself.top.title -text "Top Pins" label ${w}.pinself.right.title -text "Right Pins" label ${w}.pinself.bottom.title -text "Bottom Pins" listbox ${w}.pinself.left.list listbox ${w}.pinself.top.list listbox ${w}.pinself.right.list listbox ${w}.pinself.bottom.list pack ${w}.pinself.left.title -side top pack ${w}.pinself.left.list -side top -fill y -expand true pack ${w}.pinself.top.title -side top pack ${w}.pinself.top.list -side top -fill y -expand true pack ${w}.pinself.right.title -side top pack ${w}.pinself.right.list -side top -fill y -expand true pack ${w}.pinself.bottom.title -side top pack ${w}.pinself.bottom.list -side top -fill y -expand true grid ${w}.pinself.left -row 0 -column 0 -sticky news -padx 1 -pady 1 grid ${w}.pinself.top -row 0 -column 1 -sticky news -padx 1 -pady 1 grid ${w}.pinself.right -row 0 -column 2 -sticky news -padx 1 -pady 1 grid ${w}.pinself.bottom -row 0 -column 3 -sticky news -padx 1 -pady 1 grid columnconfigure ${w}.pinself 0 -weight 1 -minsize 50 grid columnconfigure ${w}.pinself 1 -weight 1 -minsize 50 grid columnconfigure ${w}.pinself 2 -weight 1 -minsize 50 grid columnconfigure ${w}.pinself 3 -weight 1 -minsize 50 grid rowconfigure ${w}.pinself 0 -weight 1 -minsize 50 # Determine if the pinlist is fixed by either being taken from a "subckt" # line in a schematic, or being taken from a "subckt" line in a SPICE deck. # If so, we pass the ordered list to the symbol construction routine, and # we also prevent symbol pins from being deleted. if {$pinlist == {}} { set pinlist [xcircuit::getpinlist [page label]] if {[xcircuit::getsubckttext [page label]] != {}} { set orderedpins 1 } else { set orderedpins 0 } } else { set orderedpins 1 } # Break the pinlist up into 4 parts set rightpins [expr [llength $pinlist] / 2] set bottompins [expr [llength $pinlist] - $rightpins] set leftpins [expr $rightpins / 2] set rightpins [expr $rightpins - $leftpins] set toppins [expr $bottompins / 2] set bottompins [expr $bottompins - $toppins] incr leftpins $rightpins incr bottompins $leftpins incr toppins $bottompins for {set k 0} {$k < $rightpins} {incr k} { ${w}.pinself.right.list insert end [lindex $pinlist $k] } for {} {$k < $leftpins} {incr k} { ${w}.pinself.left.list insert end [lindex $pinlist $k] } for {} {$k < $bottompins} {incr k} { ${w}.pinself.bottom.list insert end [lindex $pinlist $k] } for {} {$k < $toppins} {incr k} { ${w}.pinself.top.list insert end [lindex $pinlist $k] } pack ${w}.pinself -side top -anchor w -padx 20 -pady 5 -fill y -expand true catch { if {$orderedpins == 0} { button ${w}.bbar.remove -text "Remove Pin" -bg beige -command \ {xcircuit::removeselectedpin} } button ${w}.bbar.moveleft -text "Move Left" -bg beige -command \ {xcircuit::movepinleft} button ${w}.bbar.movetop -text "Move Top" -bg beige -command \ {xcircuit::movepintop} button ${w}.bbar.moveright -text "Move Right" -bg beige -command \ {xcircuit::movepinright} button ${w}.bbar.movebottom -text "Move Bottom" -bg beige -command \ {xcircuit::movepinbottom} } if {$orderedpins == 0} { pack ${w}.bbar.remove -side left -ipadx 10 } pack ${w}.bbar.moveleft -side left -ipadx 10 pack ${w}.bbar.movetop -side left -ipadx 10 pack ${w}.bbar.moveright -side left -ipadx 10 pack ${w}.bbar.movebottom -side left -ipadx 10 } #--------------------------------------------------------------------------- # Remove the pin arranger widget from the dialog box. #--------------------------------------------------------------------------- proc xcircuit::removepinarranger {w} { catch { pack forget ${w}.pinself destroy ${w}.pinself pack forget ${w}.bbar.movebottom pack forget ${w}.bbar.moveright pack forget ${w}.bbar.movetop pack forget ${w}.bbar.moveleft pack forget ${w}.bbar.remove } } #--------------------------------------------------------------------------- # Redefine popupdialog to remove the pin arranger any time it is invoked. #--------------------------------------------------------------------------- proc xcircuit::popupdialog {} { xcircuit::removepinarranger .dialog xcircuit::removelists .dialog wm deiconify .dialog focus .dialog.textent.txt } #--------------------------------------------------------------------------- # Redefine the procedure for the "Make Matching Symbol" menu button. #--------------------------------------------------------------------------- proc xcircuit::promptmakesymbol {{name ""}} { global XCOps .dialog.bbar.okay configure -command \ {if {[string first "Page " [page label]] >= 0} { \ page label [.dialog.textent.txt get]}; \ xcircuit::makesymbol; \ wm withdraw .dialog} .dialog.textent.title.field configure -text "Confirm symbol name:" .dialog.textent.txt delete 0 end if {[string length $name] == 0 && [string first "Page " [page label]] < 0} { set name [page label]} .dialog.textent.txt insert 0 $name xcircuit::popupdialog xcircuit::addtechlist .dialog "Technology: " xcircuit::addliblist .dialog "Place in: " xcircuit::addpinarranger .dialog } #--------------------------------------------------------------------------- # Routine which parses a spice file for the first .subckt line and # generates a symbol to match, with a "%F" escape pointing to the # spice file to include. #--------------------------------------------------------------------------- proc xcircuit::spice2symbol {filename {subcktname ""}} { global XCOps set f [open $filename] set infolabel "" while {[gets $f line] >= 0} { set dnline [string tolower $line] if {[string first .subckt $dnline] == 0} { while {[gets $f nextline] >= 0} { if {[string first + $nextline] != 0} {break} append line [string range $nextline 1 end] } set infolabel $line if {$subcktname == ""} {break} if {[string compare $subcktname [lindex $infolabel 1]] == 0} {break} } } close $f if {[string length $infolabel] == 0} {return} set pinlabels [lrange $infolabel 2 end] .dialog.bbar.okay configure -command \ "if {[string first {Page } [page label]] >= 0} { \ page label [.dialog.textent.txt get]}; \ xcircuit::makesymbol $filename [list $pinlabels]; \ wm withdraw .dialog" .dialog.textent.title.field configure -text "Confirm symbol name:" .dialog.textent.txt delete 0 end .dialog.textent.txt insert 0 [lindex $infolabel 1] xcircuit::popupdialog xcircuit::addliblist .dialog "Place in: " xcircuit::addpinarranger .dialog $pinlabels } #--------------------------------------------------------------------------- # Procedure that creates the dialog to find a spice file to parse and # calls spice2symbol. #--------------------------------------------------------------------------- proc xcircuit::promptspicesymbol {} { global XCOps .filelist.bbar.okay configure -command \ {xcircuit::spice2symbol [.filelist.textent.txt get] ; \ wm withdraw .filelist} .filelist.listwin.win configure -data "cir" .filelist.textent.title.field configure -text "Select spice file to parse:" .filelist.textent.txt delete 0 end xcircuit::popupfilelist xcircuit::removelists .filelist } #--------------------------------------------------------------------------- # Add a menu item to invoke promptspicesymbol. #--------------------------------------------------------------------------- set m .xcircuit.menubar.netlistbutton.netlistmenu $m insert 7 command -label "SPICE to symbol" -command {xcircuit::promptspicesymbol} unset m #---------------------------------------------------------------------------