# # File: xschem.tcl # # This file is part of XSCHEM, # a schematic capture and Spice/Vhdl/Verilog netlisting tool for circuit # simulation. # Copyright (C) 1998-2022 Stefan Frederik Schippers # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ### INUTILE integration (spice stimuli generator from a higher level description language) proc inutile_line {txtlabel} { global retval toplevel .inutile_line -class Dialog set X [expr [winfo pointerx .inutile_line] - 60] set Y [expr [winfo pointery .inutile_line] - 35] wm geometry .inutile_line "+$X+$Y" label .inutile_line.l1 -text $txtlabel entry .inutile_line.e1 -width 60 .inutile_line.e1 delete 0 end .inutile_line.e1 insert 0 $retval button .inutile_line.b1 -text "OK" -command \ { set retval [.inutile_line.e1 get ] destroy .inutile_line } bind .inutile_line { set retval [.inutile_line.e1 get ] destroy .inutile_line } pack .inutile_line.l1 -side top -fill x pack .inutile_line.e1 -side top -fill both -expand yes pack .inutile_line.b1 -side top -fill x grab set .inutile_line focus .inutile_line.e1 tkwait window .inutile_line return $retval } proc inutile_write_data {w f} { set fid [open $f "w"] set t [$w get 0.0 {end - 1 chars}] puts -nonewline $fid $t close $fid } proc inutile_read_data {w f} { set fid [open $f "r"] set t [read $fid] $w delete 0.0 end $w insert 0.0 $t close $fid } proc inutile_template {w f} { set fid [open $f "r"] set t [read $fid] $w insert 0.0 $t close $fid } proc inutile_get_time {} { global netlist_dir set fileid [open "$netlist_dir/inutile.simulationtime" "RDONLY"] .inutile.buttons.time delete 0 end .inutile.buttons.time insert 0 [read -nonewline $fileid] close $fileid file delete "$netlist_dir/inutile.simulationtime" } proc inutile_alias_window {w filename} { catch {destroy $w} toplevel $w wm title $w "(IN)UTILE ALIAS FILE: $filename" wm iconname $w "ALIAS" set fileid [open $filename "RDONLY CREAT"] set testo [read $fileid] close $fileid frame $w.buttons pack $w.buttons -side bottom -fill x -pady 2m text $w.text -relief sunken -bd 2 -yscrollcommand "$w.scroll set" -setgrid 1 \ -height 30 scrollbar $w.scroll -command "$w.text yview" button $w.buttons.dismiss -text Dismiss -command "destroy $w" button $w.buttons.save -text Save -command "inutile_write_data $w.text \"$filename\"" button $w.buttons.load -text Reload -command "inutile_read_data $w.text \"$filename\"" pack $w.buttons.dismiss $w.buttons.save $w.buttons.load -side left -expand 1 pack $w.scroll -side right -fill y pack $w.text -expand yes -fill both $w.text insert 0.0 $testo } proc inutile_help_window {w filename} { catch {destroy $w} toplevel $w wm title $w "(IN)UTILE ALIAS FILE" wm iconname $w "ALIAS" frame $w.buttons pack $w.buttons -side bottom -fill x -pady 2m button $w.buttons.dismiss -text Dismiss -command "destroy $w" button $w.buttons.save -text Save -command "inutile_write_data $w.text \"$filename\"" pack $w.buttons.dismiss $w.buttons.save -side left -expand 1 text $w.text -relief sunken -bd 2 -yscrollcommand "$w.scroll set" -setgrid 1 \ -height 30 -width 90 scrollbar $w.scroll -command "$w.text yview" pack $w.scroll -side right -fill y pack $w.text -expand yes -fill both set fileid [open $filename "RDONLY CREAT"] $w.text insert 0.0 [read $fileid] close $fileid } proc inutile_translate {f} { global XSCHEM_SHAREDIR netlist_dir set p $XSCHEM_SHAREDIR/utile set savedir [pwd] cd $netlist_dir eval exec awk -f $p/preprocess.awk \"$f\" | awk -f $p/expand_alias.awk | awk -f $p/param.awk | awk -f $p/clock.awk | awk -f $p/stimuli.awk cd $savedir } proc inutile { {filename {}}} { global XSCHEM_SHAREDIR retval netlist_dir toplevel .inutile wm title .inutile "(IN)UTILE (Stefan Schippers, sschippe)" wm iconname .inutile "(IN)UTILE" set utile_path $XSCHEM_SHAREDIR/utile if { ![string compare $filename ""] } then { wm withdraw .inutile tk_messageBox -type ok -message "Please give a file name as argument" exit } set retval {} frame .inutile.buttons pack .inutile.buttons -side bottom -fill x -pady 2m button .inutile.buttons.translate -text Translate -command " inutile_write_data .inutile.text \"$filename\" inutile_translate \"$filename\" inutile_get_time" button .inutile.buttons.dismiss -text Dismiss -command "destroy .inutile" button .inutile.buttons.code -text "Help" -command "inutile_help_window .inutile.help $utile_path/utile.txt" text .inutile.text -relief sunken -bd 2 -yscrollcommand ".inutile.scroll set" -setgrid 1 -height 30 scrollbar .inutile.scroll -command {.inutile.text yview} button .inutile.buttons.save -text Save -command " set retval \"$filename\" set filename \[inutile_line {Filename}\] inutile_write_data .inutile.text \"$filename\"" button .inutile.buttons.load -text Reload -command " set retval \"$filename\" set filename \[inutile_line {Filename}\] inutile_read_data .inutile.text \"$filename\"" button .inutile.buttons.send -text "Template" -command " if { !\[string compare \[.inutile.text get 0.0 {end - 1 chars}\] {}\]} { template .inutile.text $utile_path/template.stimuli}" label .inutile.buttons.timelab -text "time:" entry .inutile.buttons.time -width 11 pack .inutile.buttons.dismiss .inutile.buttons.code \ .inutile.buttons.load .inutile.buttons.save .inutile.buttons.translate \ .inutile.buttons.send .inutile.buttons.timelab \ .inutile.buttons.time -side left -expand 1 pack .inutile.scroll -side right -fill y pack .inutile.text -expand yes -fill both if { [file exists $filename] } { set fileid [open $filename "RDONLY"] .inutile.text insert 0.0 [read $fileid] close $fileid } set tmp [.inutile.text index end] regsub {\..*$} $tmp {} lines for {set i 1} {$i <= $lines} {incr i} { set tmp [.inutile.text get $i.0 "$i.0 lineend"] if [regexp {^(include)|(\.include)} $tmp ] { inutile_alias_window .inutile.tw$i [lindex $tmp 1] } } } ### End INUTILE integration ### for tclreadline: disable customcompleters proc completer { text start end line } { return {}} ### ### set var with $val if var Not existing ### proc set_ne { var val } { upvar #0 $var v if { ![ info exists v ] } { set v $val } } ### ### Tk procedures ### # execute service function proc execute_fileevent {id} { global execute append execute(data,$id) [read $execute(pipe,$id) 1024] if {[eof $execute(pipe,$id)]} { fileevent $execute(pipe,$id) readable "" if { $execute(status,$id) } { # setting pipe to blocking before closing allows to see if pipeline failed # do not ask status for processes that close stdout/stderr, as eof might # occur before process ends and following close blocks until process terminates. fconfigure $execute(pipe,$id) -blocking 1 set status 0 if { [ info tclversion] > 8.4} { set catch_return [eval catch [list {close $execute(pipe,$id)} err options] ] } else { set catch_return [eval catch [list {close $execute(pipe,$id)} err] ] } if {$catch_return} { if {[info tclversion] > 8.4} { set details [dict get $options -errorcode] if {[lindex $details 0] eq "CHILDSTATUS"} { set status [lindex $details 2] viewdata "Failed: $execute(cmd,$id)\nstderr:\n$err\ndata:\n$execute(data,$id)" } else { set status 1 if {$execute(status,$id) } { viewdata "Completed: $execute(cmd,$id)\nstderr:\n$err\ndata:\n$execute(data,$id)" } } } else { set status 1 if {$execute(status,$id) } { viewdata "Completed: $execute(cmd,$id)\nstderr:\n$err\ndata:\n$execute(data,$id)" } } } if {$status == 0} { if {$execute(status,$id) } { viewdata "Completed: $execute(cmd,$id)\ndata:\n$execute(data,$id)" } } } else { # nonblocking close always succeed close $execute(pipe,$id) } if {[info exists execute(callback,$id)] && $execute(callback,$id) ne {}} { uplevel #0 "eval $execute(callback,$id)" unset execute(callback,$id) } unset execute(pipe,$id) unset execute(data,$id) unset execute(status,$id) unset execute(cmd,$id) } } proc execute_wait {status args} { global execute set id [eval execute $status $args] if {$id == -1} { return -1 } xschem set semaphore [expr {[xschem get semaphore] +1}] vwait execute(pipe,$id) xschem set semaphore [expr {[xschem get semaphore] -1}] return $id } # equivalent to the 'exec' tcl function but keeps the event loop # responding, so widgets get updated properly # while waiting for process to end. proc execute {status args} { global execute has_x if {![info exists execute(id)]} { set execute(id) 0 } else { incr execute(id) } set id $execute(id) if { [info exists execute(callback)] } { set execute(callback,$id) $execute(callback) unset execute(callback) } if { [catch {open "|$args" r} err] } { puts stderr "Proc execute error: $err" if { [info exists has_x]} { tk_messageBox -message \ "Can not execute '$args': ensure it is available on the system. Error: $err" \ -icon error -parent [xschem get topwindow] -type ok } return -1 } else { set pipe $err } set execute(status,$id) $status set execute(pipe,$id) $pipe set execute(cmd,$id) $args set execute(data,$id) "" fconfigure $pipe -blocking 0 fileevent $pipe readable "execute_fileevent $id" return $id } #### Scrollable frame proc scrollyview {container args} { global ${container}_vpos ;# global to remember scrollbar position set_ne ${container}_vpos 0 if {[lindex $args 0] eq {place}} { place ${container}.f.scrl -in $container.f -x 0 -y 0 -relwidth 1 update ;# without this vpos of scrollbar will not be remembered when reopening toplevel } set ht [winfo height $container.f] set hs [winfo height $container.f.scrl] set frac [expr {double($ht)/$hs}] if { [lindex $args 0] eq {scroll}} { ;# mouse wheel set ${container}_vpos [expr {[set ${container}_vpos] + [lindex $args 1] *(1.0/$frac)/5}] } elseif { [lindex $args 0] eq {moveto}} { ;# scrollbar slider set ${container}_vpos [lindex $args 1] } if { [set ${container}_vpos] < 0.0 } {set ${container}_vpos 0.0} if { [set ${container}_vpos] > 1.0 - $frac } {set ${container}_vpos [expr {1.0 - $frac}]} $container.vs set [set ${container}_vpos] [expr {[set ${container}_vpos] + $frac}] place $container.f.scrl -in $container.f -x 0 -y [expr {-$hs * [set ${container}_vpos]}] -relwidth 1.0 } # scrollable frame constructor proc sframe {container} { frame $container.f scrollbar $container.vs -command "scrollyview $container" ;# scrollyview moveto commands frame $container.f.scrl pack $container.f -expand yes -fill both -side left pack $container.vs -expand yes -fill y return $container.f.scrl } #### /Scrollable frame ## evaluate expression. if expression has errors or does not evaluate return expression as is proc ev {s} { if {![catch {expr $s} res]} { return [format %.4g $res] } else { return $s } } proc netlist {source_file show netlist_file} { global XSCHEM_SHAREDIR flat_netlist netlist_dir global verilog_2001 debug_var OS verilog_bitblast simuldir set netlist_type [xschem get netlist_type] if {$debug_var <= -1} { puts "netlist: source_file=$source_file, netlist_type=$netlist_type" } set dest $netlist_dir/$netlist_file if {$netlist_type eq {spice}} { if { [sim_is_xyce] } { set xyce {-xyce} } else { set xyce {} } set cmd ${XSCHEM_SHAREDIR}/spice.awk set brk ${XSCHEM_SHAREDIR}/break.awk set flatten ${XSCHEM_SHAREDIR}/flatten.awk if {$flat_netlist==0} { eval exec {awk -f $cmd -- $xyce $source_file | awk -f $brk > $dest} } else { eval exec {awk -f $cmd -- $xyce $source_file | awk -f $flatten | awk -f $brk > $dest} } if ![string compare $show "show"] { textwindow $dest } } if {$netlist_type eq {vhdl}} { set cmd $XSCHEM_SHAREDIR/vhdl.awk eval exec {awk -f $cmd $source_file > $dest} if ![string compare $show "show"] { textwindow $dest } } if {$netlist_type eq {tedax}} { set cmd1 $XSCHEM_SHAREDIR/tedax.awk set cmd2 $XSCHEM_SHAREDIR/flatten_tedax.awk if {[catch {eval exec {awk -f $cmd1 $source_file | awk -f $cmd2 > $dest} } err] } { puts stderr "tEDAx errors: $err" } if ![string compare $show "show"] { textwindow $dest } } if {$netlist_type eq {verilog}} { set cmd ${XSCHEM_SHAREDIR}/verilog.awk if { $verilog_bitblast == 1 } { eval exec {awk -f $cmd -- -bitblast $source_file > $dest} } else { eval exec {awk -f $cmd $source_file > $dest} } if { $verilog_2001==1 } { set cmd ${XSCHEM_SHAREDIR}/convert_to_verilog2001.awk set interm ${dest}[pid] eval exec {awk -f $cmd $dest > $interm} file rename -force $interm $dest } if ![string compare $show "show"] { textwindow "$dest" } } return {} } # 20161121 proc convert_to_pdf {filename dest} { global to_pdf OS if { [regexp -nocase {\.pdf$} $dest] } { set pdffile [file rootname $filename].pdf # puts "---> $to_pdf $filename $pdffile" set cmd "exec $to_pdf \$filename \$pdffile" if {$OS == "Windows"} { set cmd "exec $to_pdf \$pdffile \$filename" } if { ![catch {eval $cmd} msg] } { file rename -force $pdffile $dest # ps2pdf succeeded, so remove original .ps file if { ![xschem get debug_var] } { file delete $filename } } else { puts stderr "problems converting postscript to pdf: $msg" } } else { file rename -force $filename $dest } } # 20161121 proc convert_to_png {filename dest} { global to_png debug_var OS # puts "---> $to_png $filename $dest" set cmd "exec $to_png \$filename png:\$dest" if {$OS == "Windows"} { set cmd "exec $to_png \$dest \$filename" } if { ![catch {eval $cmd} msg] } { # conversion succeeded, so remove original .xpm file if { ![xschem get debug_var] } { file delete $filename } } else { puts stderr "problems converting xpm to png: $msg" } } # always specify Shift- modifier for capital letters # see tk 'man keysyms' for key names # example format for s, d: Control-Alt-Key-asterisk # Control-Shift-Key-A # Alt-Key-c # ButtonPress-4 # proc key_binding { s d { topwin {} } } { regsub {.*-} $d {} key switch $key { Insert { set keysym 65379 } Escape { set keysym 65307 } Return { set keysym 65293 } Delete { set keysym 65535 } F1 { set keysym 65470 } F2 { set keysym 65471 } F3 { set keysym 65472 } F4 { set keysym 65473 } F5 { set keysym 65474 } F6 { set keysym 65475 } F7 { set keysym 65476 } F8 { set keysym 65477 } F9 { set keysym 65478 } F10 { set keysym 65479 } F11 { set keysym 65480 } F12 { set keysym 65481 } BackSpace { set keysym 65288 } default { set keysym [scan "$key" %c] } } set state 0 # not found any portable way to get modifier constants ... if { [regexp {(Mod1|Alt)-} $d] } { set state [expr {$state +8}] } if { [regexp Control- $d] } { set state [expr {$state +4}] } if { [regexp Shift- $d] } { set state [expr {$state +1}] } if { [regexp ButtonPress-1 $d] } { set state [expr {$state +0x100}] } if { [regexp ButtonPress-2 $d] } { set state [expr {$state +0x200}] } if { [regexp ButtonPress-3 $d] } { set state [expr {$state +0x400}] } # puts "$state $key <${s}>" if {[regexp ButtonPress- $d]} { bind $topwin.drw "<${s}>" "xschem callback %W %T %x %y 0 $key 0 $state" } else { if {![string compare $d {} ] } { # puts "bind .drw <${s}> {}" bind $topwin.drw "<${s}>" {} } else { # puts "bind .drw <${s}> xschem callback %W %T %x %y $keysym 0 0 $state" bind $topwin.drw "<${s}>" "xschem callback %W %T %x %y $keysym 0 0 $state" } } } proc edit_file {filename} { global editor # since $editor can be an executable with options (gvim -f) I *need* to use eval eval execute 0 $editor $filename return {} } # ============================================================ # SIMULATION CONTROL # ============================================================ # ============================================================ # SIMCONF # ============================================================ ## $N : netlist file full path (/home/schippes/simulations/opamp.spice) ## $n : netlist file full path with extension chopped (/home/schippes/simulations/opamp) ## $s : schematic name (opamp) ## $d : netlist directory ## ## Other global vars: ## netlist_dir ## terminal ## netlist_type can be obtained with [xschem get netlist_type] proc save_sim_defaults {f} { global sim netlist_dir terminal set a [catch {open $f w} fd] if { $a } { puts "save_sim_defaults: error opening file $f: $fd" return } puts $fd {# set the list of tools known to xschem} puts $fd {# Note that no spaces are allowed around commas in array keys} puts $fd "set sim(tool_list) {$sim(tool_list)}" puts $fd {} foreach tool $sim(tool_list) { puts $fd "#Specify the number of configured $tool tools." puts $fd "set sim($tool,n) $sim($tool,n) ;# number of configured $tool tools" puts $fd "# Specify the default $tool tool to use (first=0)" puts $fd "set sim($tool,default) $sim($tool,default) ;# default $tool tool to launch" puts $fd {} for {set i 0} {$i < $sim($tool,n)} { incr i} { puts $fd "# specify tool command (cmd), name (name), if tool must run\ in foreground and if exit status must be reported" puts $fd "set sim($tool,$i,cmd) {$sim($tool,$i,cmd)}" puts $fd "set sim($tool,$i,name) {$sim($tool,$i,name)}" puts $fd "set sim($tool,$i,fg) $sim($tool,$i,fg)" puts $fd "set sim($tool,$i,st) $sim($tool,$i,st)" puts $fd {} } puts $fd {} } close $fd } proc load_recent_file {} { global USER_CONF_DIR recentfile has_x # recent files set recentfile {} if { [file exists $USER_CONF_DIR/recent_files] } { if {[catch { source $USER_CONF_DIR/recent_files } err] } { puts "Problems opening recent_files: $err" if {[info exists has_x]} { tk_messageBox -message "Problems opening recent_files: $err" \ -icon warning -parent . -type ok } } foreach i [info vars c_toolbar::c_t_*] { if {[set ${i}(w)] != $c_toolbar::c_t(w) || [set ${i}(n)] != $c_toolbar::c_t(n)} { array unset $i } } set hash $c_toolbar::c_t(hash) if { [info exists c_toolbar::c_t_$hash]} { array set c_toolbar::c_t [array get c_toolbar::c_t_$hash] } } } proc update_recent_file {f {topwin {} } } { global recentfile has_x # puts "update recent file, f=$f, topwin=$topwin" set old $recentfile set recentfile {} lappend recentfile $f foreach i $old { if {[abs_sym_path $i] ne [abs_sym_path $f]} { lappend recentfile [abs_sym_path $i] } } # tcl8.4 errors if using lreplace past the last element if { [llength $recentfile] > 10 } { set recentfile [lreplace $recentfile 10 end] } write_recent_file if { [info exists has_x] } {setup_recent_menu 0 $topwin} if { [info exists has_x] } {setup_recent_menu 1 $topwin} } proc write_recent_file {} { global recentfile USER_CONF_DIR # puts "write recent file recentfile=$recentfile" set a [catch {open $USER_CONF_DIR/recent_files w} fd] if { $a } { puts "write_recent_file: error opening file $f: $fd" return } puts $fd "set recentfile {$recentfile}" if {[info exists c_toolbar::c_t]} { set hash $c_toolbar::c_t(hash) set c_toolbar::c_t(time) [clock seconds] array set c_toolbar::c_t_$hash [array get c_toolbar::c_t] foreach i [info vars c_toolbar::c_t_*] { ## old (~3 months) recent components will expire if { [info exists [subst $i](time)]} { if { [clock seconds] - [set [subst $i](time)] < 7500000} { puts $fd "array set $i {[array get $i]}" } # puts "Age: [expr {[clock seconds] - [set [subst $i](time)]}]" } } } close $fd } proc setup_recent_menu { {in_new_window 0} { topwin {} } } { global recentfile # puts "setup recent menu in_new_window=$in_new_window" if {$in_new_window} { $topwin.menubar.file.menu.recent_new_window delete 0 9 } else { $topwin.menubar.file.menu.recent delete 0 9 } set i 0 if { [info exists recentfile] } { foreach i $recentfile { if {$in_new_window} { $topwin.menubar.file.menu.recent_new_window add command \ -command "xschem load_new_window {$i}" \ -label [file tail $i] } else { $topwin.menubar.file.menu.recent add command \ -command "xschem load {$i}" \ -label [file tail $i] } } } } proc sim_is_ngspice {} { global sim set_sim_defaults if { [info exists sim(spice,default)] } { set idx $sim(spice,default) if { [regexp {ngspice} $sim(spice,$idx,cmd)] } { return 1 } } return 0 } proc sim_is_Xyce {} { return [sim_is_xyce] } proc sim_is_xyce {} { global sim set_sim_defaults if { [info exists sim(spice,default)] } { set idx $sim(spice,default) if { [regexp {[xX]yce} $sim(spice,$idx,cmd)] } { return 1 } } return 0 } # wrapper to "xschem list_tokens" comand to handle non list results # usually as a result of malformed input strings proc list_tokens {s} { set res [xschem list_tokens $s 0] if {[string is list $res]} { return $res } else { return [split $res] } } # generates a proper list, trimming multiple separators proc tolist {s} { set s [string trim $s] regsub -all {[\t\n ]+} $s { } s if {[string is list $s] } { return $s } else { return [split $s] } } proc set_sim_defaults {{reset {}}} { global sim terminal USER_CONF_DIR has_x bespice_listen_port env OS if {$reset eq {reset} } { file delete ${USER_CONF_DIR}/simrc } if { $reset eq {} } { set failure 0 if { [info exists has_x] && [winfo exists .sim] } { foreach tool $sim(tool_list) { for {set i 0} {$i < $sim($tool,n)} { incr i} { set sim($tool,$i,cmd) [.sim.topf.f.scrl.center.$tool.r.$i.cmd get 1.0 {end - 1 chars}] } } } if { ![info exists sim] } { if { [file exists ${USER_CONF_DIR}/simrc] } { # get conf from simrc if { [catch {source ${USER_CONF_DIR}/simrc} err]} { puts "Problems opening simrc file: $err" if {[info exists has_x]} { tk_messageBox -message "Problems opening simrc file: $err" -icon warning \ -parent [xschem get topwindow] -type ok } set failure 1 } } } } if {( $reset eq {reset} ) || ![info exists sim] || $failure} { if {[info exists sim]} {unset sim} # no simrc, set a reasonable default set sim(tool_list) {spice spicewave verilog verilogwave vhdl vhdlwave} if {$OS == "Windows"} { set_ne sim(spice,0,cmd) {ngspice -i "$N" -a} } else { set_ne sim(spice,0,cmd) {$terminal -e 'ngspice -i "$N" -a || sh'} } # can not use set_ne as variables bound to entry widgets always exist if widget exists set sim(spice,0,name) {Ngspice} set_ne sim(spice,0,fg) 0 set_ne sim(spice,0,st) 0 set_ne sim(spice,1,cmd) {ngspice -b -r "$n.raw" -o "$n.out" "$N"} set sim(spice,1,name) {Ngspice batch} set_ne sim(spice,1,fg) 0 set_ne sim(spice,1,st) 1 set_ne sim(spice,2,cmd) "Xyce \"\$N\"\n# Add -r \"\$n.raw\" if you want all variables saved" set sim(spice,2,name) {Xyce batch} set_ne sim(spice,2,fg) 0 set_ne sim(spice,2,st) 1 set_ne sim(spice,3,cmd) {mpirun /path/to/parallel/Xyce "$N"} set sim(spice,3,name) {Xyce parallel batch} set_ne sim(spice,3,fg) 0 set_ne sim(spice,3,st) 1 # number of configured spice simulators, and default one set_ne sim(spice,n) 4 set_ne sim(spice,default) 0 ### spice wave view set_ne sim(spicewave,0,cmd) {gaw "$n.raw" } set sim(spicewave,0,name) {Gaw viewer} set_ne sim(spicewave,0,fg) 0 set_ne sim(spicewave,0,st) 0 set_ne sim(spicewave,1,cmd) {$terminal -e ngspice} set sim(spicewave,1,name) {Ngpice Viewer} set_ne sim(spicewave,1,fg) 0 set_ne sim(spicewave,1,st) 0 set_ne sim(spicewave,2,cmd) {rawtovcd -v 1.5 "$n.raw" > "$n.vcd" && gtkwave "$n.vcd" "$n.sav" 2>/dev/null} set sim(spicewave,2,name) {Rawtovcd} set_ne sim(spicewave,2,fg) 0 set_ne sim(spicewave,2,st) 0 set_ne sim(spicewave,3,cmd) {$env(HOME)/analog_flavor_eval/bin/bspwave --socket localhost $bespice_listen_port "$n.raw" } set sim(spicewave,3,name) {Bespice wave} set_ne sim(spicewave,3,fg) 0 set_ne sim(spicewave,3,st) 0 # number of configured spice wave viewers, and default one set_ne sim(spicewave,n) 4 set_ne sim(spicewave,default) 0 ### verilog set_ne sim(verilog,0,cmd) {iverilog -o .verilog_object -g2012 "$N" && vvp .verilog_object} set sim(verilog,0,name) {Icarus verilog} set_ne sim(verilog,0,fg) 0 set_ne sim(verilog,0,st) 1 # number of configured verilog simulators, and default one set_ne sim(verilog,n) 1 set_ne sim(verilog,default) 0 ### verilog wave view set_ne sim(verilogwave,0,cmd) {gtkwave dumpfile.vcd "$N.sav" 2>/dev/null} set sim(verilogwave,0,name) {Gtkwave} set_ne sim(verilogwave,0,fg) 0 set_ne sim(verilogwave,0,st) 0 # number of configured verilog wave viewers, and default one set_ne sim(verilogwave,n) 1 set_ne sim(verilogwave,default) 0 ### vhdl set_ne sim(vhdl,0,cmd) {ghdl -c --ieee=synopsys -fexplicit "$N" -r "$s" --wave="$n.ghw"} set sim(vhdl,0,name) {Ghdl} set_ne sim(vhdl,0,fg) 0 set_ne sim(vhdl,0,st) 1 # number of configured vhdl simulators, and default one set_ne sim(vhdl,n) 1 set_ne sim(vhdl,default) 0 ### vhdl wave view set_ne sim(vhdlwave,0,cmd) {gtkwave "$n.ghw" "$N.sav" 2>/dev/null} set sim(vhdlwave,0,name) {Gtkwave} set_ne sim(vhdlwave,0,fg) 0 set_ne sim(vhdlwave,0,st) 0 # number of configured vhdl wave viewers, and default one set_ne sim(vhdlwave,n) 1 set_ne sim(vhdlwave,default) 0 } } proc simconf_reset {} { global sim set answer [tk_messageBox -message "Warning: delete simulation configuration file and reset to default?" \ -icon warning -parent .sim -type okcancel] if { $answer eq {ok}} { set_sim_defaults reset foreach tool $sim(tool_list) { for {set i 0} { $i < $sim($tool,n)} {incr i} { .sim.topf.f.scrl.center.$tool.r.$i.cmd delete 1.0 end .sim.topf.f.scrl.center.$tool.r.$i.cmd insert 1.0 $sim($tool,$i,cmd) } } } } proc simconf_saveconf {scrollframe} { global sim USER_CONF_DIR foreach tool $sim(tool_list) { for {set i 0} { $i < $sim($tool,n)} {incr i} { set sim($tool,$i,cmd) [${scrollframe}.center.$tool.r.$i.cmd get 1.0 {end - 1 chars}] } } # destroy .sim # xschem set semaphore [expr {[xschem get semaphore] -1}] save_sim_defaults ${USER_CONF_DIR}/simrc # puts "saving simrc" } proc simconf {} { global sim USER_CONF_DIR simconf_default_geometry if {[winfo exists .sim]} { destroy .sim xschem set semaphore [expr {[xschem get semaphore] -1}] } xschem set semaphore [expr {[xschem get semaphore] +1}] set_sim_defaults toplevel .sim -class dialog wm title .sim {Simulation Configuration} wm geometry .sim 700x340 frame .sim.topf set scrollframe [sframe .sim.topf] frame ${scrollframe}.top frame ${scrollframe}.center frame .sim.bottom pack ${scrollframe}.top -fill x pack ${scrollframe}.center -fill both -expand yes set bg(0) {#dddddd} set bg(1) {#aaaaaa} set toggle 0 foreach tool $sim(tool_list) { frame ${scrollframe}.center.$tool label ${scrollframe}.center.$tool.l -width 12 -text $tool -bg $bg($toggle) frame ${scrollframe}.center.$tool.r pack ${scrollframe}.center.$tool -fill both -expand yes pack ${scrollframe}.center.$tool.l -fill y -side left pack ${scrollframe}.center.$tool.r -fill both -expand yes for {set i 0} { $i < $sim($tool,n)} {incr i} { frame ${scrollframe}.center.$tool.r.$i pack ${scrollframe}.center.$tool.r.$i -fill x -expand yes entry ${scrollframe}.center.$tool.r.$i.lab -textvariable sim($tool,$i,name) -width 18 -bg $bg($toggle) radiobutton ${scrollframe}.center.$tool.r.$i.radio -bg $bg($toggle) \ -variable sim($tool,default) -value $i text ${scrollframe}.center.$tool.r.$i.cmd -width 20 -height 3 -wrap none -bg $bg($toggle) ${scrollframe}.center.$tool.r.$i.cmd insert 1.0 $sim($tool,$i,cmd) checkbutton ${scrollframe}.center.$tool.r.$i.fg -text Fg -variable sim($tool,$i,fg) -bg $bg($toggle) checkbutton ${scrollframe}.center.$tool.r.$i.st -text Status -variable sim($tool,$i,st) -bg $bg($toggle) pack ${scrollframe}.center.$tool.r.$i.lab -side left -fill y pack ${scrollframe}.center.$tool.r.$i.radio -side left -fill y pack ${scrollframe}.center.$tool.r.$i.cmd -side left -fill x -expand yes pack ${scrollframe}.center.$tool.r.$i.fg -side left -fill y pack ${scrollframe}.center.$tool.r.$i.st -side left -fill y } incr toggle set toggle [expr {$toggle %2}] } button .sim.bottom.cancel -text Cancel -command { destroy .sim xschem set semaphore [expr {[xschem get semaphore] -1}] } button .sim.bottom.help -text Help -command { viewdata {In this dialog box you set the commands xschem uses to launch the various external tools. Xschem has 3 main netlisting modes (spice, verilog, vhdl) and for each netlisting mode some simulators and some viewers can be defined. The following variables are defined and will get substituted by XSCHEM before sending commands to the shell: - N: complete filename of netlist for current netlisting mode (example: /home/schippes/.xschem/simulations/opamp.spice for spice) (example: /home/schippes/.xschem/simulations/opamp.v for verilog) - n: complete filename of netlist as above but without extension (example: /home/schippes/.xschem/simulations/opamp) - S: full pathname of schematic being used (example: /home/schippes/.xschem/xschem_library/opamp.sch) - s: name of schematic being used (example: opamp) - d: simulation directory (example: /home/schippes/.xschem/simulations) - terminal: terminal to be used for applications that need to be executed in terminal (example: $terminal -e ngspice -i "$N" -a) If for a given tool there are multiple rows then the radiobutton tells which one will be called by xschem. Variables should be used with the usual substitution character $: $n, $N, etc. Foreground (Fg) checkbutton tells xschem to wait for child process to finish. Status checkbutton tells xschem to report a status dialog (stdout, stderr, exit status) when process finishes. Any changes made in the command or tool name entries will be saved in ~/.xschem/simrc when 'Save Configuration to file' button is pressed. If 'Accept and Close' is pressed then the changes are kept in memory and dialog is closed without writing to a file, if xschem is restarted changes will be lost. If no ~/.xschem/simrc is present then a minimal default setup is presented. To reset to default use the corresponding button or just delete the ~/.xschem/simrc file manually. } ro } button .sim.bottom.ok -text {Save Configuration to file} -command "simconf_saveconf $scrollframe" button .sim.bottom.reset -text {Reset to default} -command { simconf_reset } button .sim.bottom.close -text {Accept and Close} -command { set_sim_defaults destroy .sim xschem set semaphore [expr {[xschem get semaphore] -1}] } wm protocol .sim WM_DELETE_WINDOW { set_sim_defaults destroy .sim xschem set semaphore [expr {[xschem get semaphore] -1}] } pack .sim.bottom.cancel -side left -anchor w pack .sim.bottom.help -side left #foreach tool $sim(tool_list) { # button .sim.bottom.add${tool} -text +${tool} -command " # simconf_add $tool # destroy .sim # xschem set semaphore [expr {[xschem get semaphore] -1}] # save_sim_defaults ${USER_CONF_DIR}/simrc ## simconf # " # pack .sim.bottom.add${tool} -side left #} pack .sim.bottom.ok -side right -anchor e pack .sim.bottom.close -side right pack .sim.bottom.reset -side right pack .sim.topf -fill both -expand yes pack .sim.bottom -fill x if { [info exists simconf_default_geometry]} { wm geometry .sim "${simconf_default_geometry}" } bind .sim.topf.f {scrollyview .sim.topf} bind .sim { set simconf_default_geometry [wm geometry .sim] } bind .sim { scrollyview .sim.topf scroll -0.2} bind .sim { scrollyview .sim.topf scroll 0.2} scrollyview .sim.topf place set maxsize [expr {[winfo height ${scrollframe}] + [winfo height .sim.bottom]}] wm maxsize .sim 9999 $maxsize # tkwait window .sim } proc simconf_add {tool} { global sim set n $sim($tool,n) set sim($tool,$n,cmd) {} set sim($tool,$n,name) {} set sim($tool,$n,fg) 0 set sim($tool,$n,st) 0 incr sim($tool,n) } proc bespice_getdata {sock} { global bespice_server_getdata if {[eof $sock] || [catch {gets $sock bespice_server_getdata(line,$sock)}]} { close $sock puts "Close $bespice_server_getdata(addr,$sock)" unset bespice_server_getdata(addr,$sock) unset bespice_server_getdata(line,$sock) unset bespice_server_getdata(sock) } else { puts "bespice --> $bespice_server_getdata(line,$sock)" set bespice_server_getdata(last) $bespice_server_getdata(line,$sock) } } proc xschem_getdata {sock} { global xschem_server_getdata if {[eof $sock] || [catch {gets $sock xschem_server_getdata(line,$sock)}]} { close $sock puts "Close $xschem_server_getdata(addr,$sock)" unset xschem_server_getdata(addr,$sock) unset xschem_server_getdata(line,$sock) unset xschem_server_getdata(res,$sock) } else { puts "tcp--> $xschem_server_getdata(line,$sock)" # xschem command must be executed at global scope... uplevel #0 [list catch $xschem_server_getdata(line,$sock) xschem_server_getdata(res,$sock)] puts $sock "$xschem_server_getdata(res,$sock)" } } proc bespice_server {sock addr port} { global bespice_server_getdata if { ![info exists bespice_server_getdata(sock)] } { puts "Accept $sock from $addr port $port" fconfigure $sock -buffering line set bespice_server_getdata(addr,$sock) [list $addr $port] set bespice_server_getdata(sock) [list $sock] fileevent $sock readable [list bespice_getdata $sock] } } proc xschem_server {sock addr port} { global xschem_server_getdata puts "Accept $sock from $addr port $port" fconfigure $sock -buffering line set xschem_server_getdata(addr,$sock) [list $addr $port] fileevent $sock readable [list xschem_getdata $sock] } ## given a path (x1.x2.m4) descend into x1.x2 and return m4 whether m4 found or not proc descend_hierarchy {path {redraw 1}} { xschem set no_draw 1 # return to top level if not already there while { [xschem get currsch] } { xschem go_back } # recursively descend into sub-schematics while { [regexp {\.} $path] } { xschem unselect_all set inst $path regsub {\..*} $inst {} inst ;# take 1st path component: xlev1[3].xlev2.m3 -> xlev1[3] regsub {[^.]+\.} $path {} path ;# take remaining path: xlev1[3].xlev2.m3 -> xlev2.m3 xschem search exact 1 name $inst # handle vector instances: xlev1[3:0] -> xlev1[3],xlev1[2],xlev1[1],xlev1[0] # descend into the right one set inst_list [split [lindex [xschem expandlabel [lindex [xschem selected_set] 0 ] ] 0] {,}] set instnum [expr {[lsearch -exact $inst_list $inst] + 1}] xschem descend $instnum } xschem set no_draw 0 if {$redraw} {xschem redraw} return $path } ## given a hierarchical instname name (x1.xamp.m1) go down in the hierarchy and ## select the specified instance (m1). ## this search assumes it is given from the top of hierarchy proc select_inst {fullinst {redraw 1 } } { xschem set no_draw 1 set inst [descend_hierarchy $fullinst 0] set res [xschem select instance $inst] # if nothing found return to top if {!$res} { while { [xschem get currsch] } { xschem go_back } } xschem set no_draw 0 if {$redraw} {xschem redraw} if {$res} {return $inst} else { return {} } } proc pin_label {} { if { [file exists [abs_sym_path devices/lab_pin.sym]] } { return {devices/lab_pin.sym} } return {lab_pin.sym} } ## given a hierarchical net name x1.xamp.netname go down in the hierarchy and ## highlight the specified net. ## this search assumes it is given from the top of hierarchy proc probe_net {fullnet {redraw 1} } { xschem set no_draw 1 set net [descend_hierarchy $fullnet 0] set res [xschem hilight_netname $net] if {$res==0 && [regexp {^net[0-9]+$} $net]} { set net \#$net set res [xschem hilight_netname $net] } if {!$res} { while { [xschem get currsch] } { xschem go_back } } xschem set no_draw 0 if {$redraw} {xschem redraw} if {$res} {return $net} else { return {} } } # backannotate newnet to be connected to specified hierarchical instance name and pin. # places a label close to the instance pin to be re-routed. # actual reconnect is human assisted! proc reroute_inst {fullinst pinattr pinval newnet} { if { [regexp {\.} $fullinst] } { set hier 1 } else { set hier 0 } set res [descend_hierarchy $fullinst 0] if {$res ne {} } { set coord [xschem instance_pin_coord $res $pinattr $pinval] if { $coord eq {} } { while { [xschem get currsch] } { xschem go_back } return 0 } set pinname [lindex $coord 0] set x [expr {[lindex $coord 1] - 10} ] set y [expr {[lindex $coord 2] - 10} ] set oldnet [xschem instance_net $res $pinname] regsub {.*\.} $newnet {} newnet if { $oldnet eq $newnet } { while { [xschem get currsch] } { xschem go_back } puts "Warning: netlist patch already done? " return 0 } xschem instance [pin_label] $x $y 0 0 [list name=l1 lab=$newnet] xschem hilight_netname $newnet xschem select instance $res xschem hilight_netname $oldnet if {$hier} { xschem save} ;# save so we can process other reroute_inst without beink asked to save. xschem redraw return 1 } return 0 } ## put $new net labels close to pins on all elements connected to $old proc reroute_net {old new} { xschem push_undo xschem set no_undo 1 xschem unhilight probe_net $old set old_nopath [regsub {.*\.} $old {}] set new_nopath [regsub {.*\.} $new {}] set devlist [xschem instances_to_net $old_nopath] foreach i $devlist { set instname [lindex $i 0] set x [expr {[lindex $i 2] - 10}] set y [expr {[lindex $i 3] - 10}] xschem instance [pin_label] $x $y 0 0 [list name=l1 lab=$new_nopath] xschem select instance $instname } xschem hilight_netname $new_nopath xschem set no_undo 0 } proc simulate {{callback {}}} { ## $N : netlist file full path (/home/schippes/simulations/opamp.spice) ## $n : netlist file full path with extension chopped (/home/schippes/simulations/opamp) ## $s : schematic name (opamp) ## $S : schematic name full path (/home/schippes/.xschem/xschem_library/opamp.sch) ## $d : netlist directory global netlist_dir terminal sim global execute XSCHEM_SHAREDIR has_x OS simuldir set_sim_defaults set netlist_type [xschem get netlist_type] if { [select_netlist_dir 0] ne {}} { set d ${netlist_dir} set tool $netlist_type set S [xschem get schname] set custom_netlist_file [xschem get netlist_name] if {$custom_netlist_file ne {}} { set s [file rootname $custom_netlist_file] } else { set s [file tail [file rootname $S]] } set n ${netlist_dir}/${s} if {$tool eq {verilog}} { set N ${n}.v } else { set N ${n}.${tool} } if { ![info exists sim($tool,default)] } { if { [info exists has_x] } {alert_ "Warning: simulator for $tool is not configured"} puts "Warning: simulator for $tool is not configured" return -1 } set def $sim($tool,default) set fg $sim($tool,$def,fg) set st $sim($tool,$def,st) if {$fg} { set fg {execute_wait} } else { set fg {execute} } set cmd [subst $sim($tool,$def,cmd)] if {$OS == "Windows"} { # $cmd cannot be surrounded by {} as exec will change forward slash to backward slash if { $callback ne {} } { uplevel #0 "eval cd $netlist_dir; $callback" } #eval exec {cmd /V /C "cd $netlist_dir&&$cmd} eval exec $cmd & return -1 ;# no execute ID on windows } else { set execute(callback) $callback set id [$fg $st sh -c "cd $netlist_dir; $cmd"] puts "Simulation started: execution ID: $id" return $id } } else { return -1 } } proc gaw_echoline {} { global gaw_fd gets $gaw_fd line if {[eof $gaw_fd]} { puts "finishing connection from gaw" close $gaw_fd unset gaw_fd } else { # generate a variable event we can vwait for set gaw_fd $gaw_fd } puts "gaw -> $line" } proc setup_tcp_gaw {} { global gaw_fd gaw_tcp_address netlist_dir has_x if { [info exists gaw_fd] } { return 1; } simuldir set custom_netlist_file [xschem get netlist_name] if {$custom_netlist_file ne {}} { set s [file rootname $custom_netlist_file] } else { set s [file tail [file rootname [xschem get schname 0]]] } if { ![info exists gaw_fd] && [catch {eval socket $gaw_tcp_address} gaw_fd] } { puts "Problems opening socket to gaw on address $gaw_tcp_address" unset gaw_fd if {[info exists has_x]} { tk_messageBox -type ok -title {Tcp socket error} \ -message [concat "Problems opening socket to gaw on address $gaw_tcp_address. " \ "Ensure the following line is present uncommented in ~/.gaw/gawrc: up_listenPort = 2020." \ "If you recently closed gaw the port may be in a TIME_WAIT state for a minute or so ." \ "Close gaw, Wait a minute or two, then send waves to gaw again."] } return 0 } chan configure $gaw_fd -blocking 1 -buffering line -encoding binary -translation binary fileevent $gaw_fd readable gaw_echoline puts $gaw_fd "table_set $s.raw" return 1 } proc gaw_cmd {cmd} { global gaw_fd gaw_tcp_address netlist_dir has_x simuldir if { ![info exists gaw_fd] && [catch {eval socket $gaw_tcp_address} gaw_fd] } { puts "Problems opening socket to gaw on address $gaw_tcp_address" unset gaw_fd if {[info exists has_x]} { tk_messageBox -type ok -title {Tcp socket error} \ -message [concat "Problems opening socket to gaw on address $gaw_tcp_address. " \ "If you recently closed gaw the port may be in a TIME_WAIT state for a minute or so ." \ "Close gaw, Wait a minute or two, then send waves to gaw again."] } return } chan configure $gaw_fd -blocking 0 -buffering line -encoding binary -translation binary puts $gaw_fd "$cmd" set n [regexp -all \n $cmd] incr n puts "gaw command lines: $n" fileevent $gaw_fd readable gaw_echoline while { $n} { #timeout for abnormal deadlocks set wd [after 10000 set gaw_fd stalled] vwait gaw_fd if { $gaw_fd ne {stalled} } { after cancel $wd } else { puts "timeout waiting for gaw response.." break } incr n -1 } close $gaw_fd unset gaw_fd } proc waves {} { ## $N : netlist file full path (/home/schippes/simulations/opamp.spice) ## $n : netlist file full path with extension chopped (/home/schippes/simulations/opamp) ## $s : schematic name (opamp) ## $S : schematic name full path (/home/schippes/.xschem/xschem_library/opamp.sch) ## $d : netlist directory global netlist_dir terminal sim XSCHEM_SHAREDIR has_x global bespice_listen_port env simuldir set netlist_type [xschem get netlist_type] set_sim_defaults if { [select_netlist_dir 0] ne {}} { set d ${netlist_dir} set tool ${netlist_type} set S [xschem get schname] set custom_netlist_file [xschem get netlist_name] if {$custom_netlist_file ne {}} { set s [file rootname $custom_netlist_file] } else { set s [file tail [file rootname $S]] } set n ${netlist_dir}/${s} if {$tool eq {verilog}} { set N ${n}.v } else { set N ${n}.${tool} } set tool ${tool}wave if { ![info exists sim($tool,default)] } { if { [info exists has_x] } {alert_ "Warning: viewer for $tool is not configured"} puts "Warning: viewer for $tool is not configured" return } set def $sim($tool,default) set fg $sim($tool,$def,fg) set st $sim($tool,$def,st) if {$fg} { set fg {execute_wait} } else { set fg {execute} } set cmd [subst $sim($tool,$def,cmd)] $fg $st sh -c "cd $netlist_dir; $cmd" } } # ============================================================ # allow change color (via graph_change_wave_color) of double clicked wave proc graph_edit_wave {n n_wave} { global graph_sel_color graph_selected colors graph_sel_wave global graph_schname set graph_schname [xschem get schname] set_ne graph_sel_color 4 set graph_selected $n set graph_sel_wave $n_wave set col [xschem getprop rect 2 $graph_selected color] set node [xschem getprop rect 2 $graph_selected node] # add default colors if unspecified in col set i 0 foreach graph_node $node { if {[lindex $col $i] eq {}} { lappend col $graph_sel_color} incr i } # remove excess colors set col [lrange $col 0 [expr {$i - 1}]] set graph_sel_color [lindex $col $graph_sel_wave] xschem setprop rect 2 $graph_selected color $col fast xschem draw_graph $graph_selected toplevel .graphdialog frame .graphdialog.f button .graphdialog.ok -text OK -command {destroy .graphdialog} button .graphdialog.cancel -text Cancel -command {destroy .graphdialog} for {set i 4} {$i < 22} {incr i} { radiobutton .graphdialog.f.r$i -value $i -bg [lindex $colors $i] \ -variable graph_sel_color -command {graph_change_wave_color $graph_sel_wave } pack .graphdialog.f.r$i -side left -fill both -expand yes } grid .graphdialog.f - -sticky nsew grid .graphdialog.ok .graphdialog.cancel -sticky ew grid rowconfig .graphdialog 0 -weight 1 grid column .graphdialog 0 -weight 1 grid column .graphdialog 1 -weight 1 tkwait window .graphdialog set graph_schname {} } # get selected text from a text widget: # # .graphdialog.center.right.text1 get sel.first sel.last # # see if a selection is present: # .graphdialog.center.right.text1 tag ranges sel # # replace selected text: # .graphdialog.center.right.text1 replace sel.first sel.last BUS # # programmatically select text: # .graphdialog.center.right.text1 tag add sel 1.0 {end - 1 chars} # clear selection # .graphdialog.center.right.text1 tag remove sel 1.0 end # get position of cursor: # .graphdialog.center.right.text1 index insert # set cursor position: # .graphdialog.center.right.text1 mark set insert 2.18 # add nodes from provided list of {node color} .... proc graph_add_nodes_from_list {nodelist} { global graph_bus graph_selected graph_schname set sel {} if {$graph_bus} { set sep , } else { set sep \n } if { [winfo exists .graphdialog] } { set current_node_list [.graphdialog.center.right.text1 get 1.0 {end - 1 chars}] set col [xschem getprop rect 2 $graph_selected color] if {[string length $current_node_list] > 0 && ![regexp "\n$" $current_node_list]} { .graphdialog.center.right.text1 insert end \n } set change_done 0 set first 0 foreach {i c} $nodelist { if {$sel ne {}} {append sel $sep} if {!$first || !$graph_bus } { regsub {\[.*} $i {} busname lappend col $c } append sel $i set change_done 1 set first 1 } if {$change_done && $graph_bus} { set sel "[string toupper $busname],${sel}\n" } else { set sel "${sel}\n" } if {$change_done} { .graphdialog.center.right.text1 insert end $sel if { [xschem get schname] eq $graph_schname } { set node [string trim [.graphdialog.center.right.text1 get 1.0 {end - 1 chars}] " \n"] xschem setprop rect 2 $graph_selected color $col fastundo graph_update_nodelist regsub -all {\\?(["\\])} $node {\\\1} node_quoted ;#"4vim xschem setprop rect 2 $graph_selected node $node_quoted fast xschem draw_graph $graph_selected } } } else { set graph_bus 0 set nn {} set cc {} foreach {n c} $nodelist { if { $nn ne {}} {append nn \n} if { $cc ne {}} {append cc " "} append nn $n append cc $c } set nnn [xschem getprop rect 2 [xschem get graph_lastsel] node] set ccc [xschem getprop rect 2 [xschem get graph_lastsel] color] if { $nnn ne {}} {append nnn "\n"} if { $ccc ne {}} {append ccc " "} append nnn $nn append ccc $cc regsub -all {\\?(["\\])} $nnn {\\\1} node_quoted ;#"4vim xschem setprop rect 2 [xschem get graph_lastsel] color $ccc fastundo xschem setprop rect 2 [xschem get graph_lastsel] node $node_quoted fast xschem draw_graph [xschem get graph_lastsel] } } # add nodes from left listbox proc graph_add_nodes {} { global graph_bus set sel_idx [.graphdialog.center.left.list1 curselection] set sel {} if {$graph_bus} { set sep , } else { set sep \n } set current_node_list [.graphdialog.center.right.text1 get 1.0 {end - 1 chars}] if {[string length $current_node_list] > 0 && ![regexp "\n$" $current_node_list]} { .graphdialog.center.right.text1 insert end \n } set change_done 0 foreach i $sel_idx { set c [.graphdialog.center.left.list1 get $i] set c [regsub -all {([][])} $c {\\\1}] if { ![regexp "(^|\[ \t\n\])${c}($|\[ \t\n\])" $current_node_list]} { if {$sel ne {}} {append sel $sep} append sel [.graphdialog.center.left.list1 get $i] set change_done 1 } } if {$change_done && $graph_bus} { set sel "BUS_NAME,${sel}\n" } else { set sel "${sel}\n" } if {$change_done} { .graphdialog.center.right.text1 insert {insert lineend + 1 char} $sel } } proc graph_get_signal_list {siglist pattern } { global graph_sort set direction {-decreasing} if {$graph_sort} {set direction {-increasing}} set result {} set siglist [join [lsort $direction -dictionary $siglist] \n] # just check if pattern is a valid regexp set err [catch {regexp $pattern {12345}} res] if {$err} {set pattern {}} foreach i $siglist { regsub {^v\((.*)\)$} $i {\1} i if {[regexp $pattern $i] } { lappend result $i } } return $result } # change color of selected wave in text widget and redraw graph # OR # change color attribute of wave given as parameter, redraw graph proc graph_change_wave_color {{wave {}}} { global graph_sel_color graph_selected graph_schname if { [xschem get schname] ne $graph_schname } return # get tag the cursor is on: if { $wave eq {}} { set tag [.graphdialog.center.right.text1 tag names insert] if { [regexp {^t} $tag]} { set index [string range $tag 1 end] set col [xschem getprop rect 2 $graph_selected color] set col [lreplace $col $index $index $graph_sel_color] xschem setprop rect 2 $graph_selected color $col fast graph_update_nodelist xschem draw_graph $graph_selected } # wave to change provided as parameter } else { set col [xschem getprop rect 2 $graph_selected color] set col [lreplace $col $wave $wave $graph_sel_color] xschem setprop rect 2 $graph_selected color $col fast xschem draw_graph $graph_selected } } # tag nodes in text widget with assigned colors proc graph_update_nodelist {} { global graph_selected colors graph_sel_color graph_schname if { [xschem get schname] ne $graph_schname } return # delete old tags eval .graphdialog.center.right.text1 tag delete [ .graphdialog.center.right.text1 tag names] # tagging nodes in text widget: set col [xschem getprop rect 2 $graph_selected color] set col [string trim $col " \n"] set regx {(?:(tcleval\()?"[^"]+"\)?)|(?:(tcleval\()?[^\n \t]+\)?)} set txt [.graphdialog.center.right.text1 get 1.0 {end - 1 chars}] set tt {} set cc {} set start 0 while {[regexp -indices -start $start $regx $txt idx]} { lappend tt [lindex $idx 0] set start [expr {[lindex $idx 1] + 1}] lappend cc $start } set n 0 if { $tt ne {} } { foreach t $tt c $cc { set col_idx [lindex $col $n] # add missing colors if {$col_idx eq {}} { set col_idx $graph_sel_color lappend col $graph_sel_color } set b [lindex $colors $col_idx] .graphdialog.center.right.text1 tag add t$n "1.0 + $t chars" "1.0 + $c chars" .graphdialog.center.right.text1 tag configure t$n -background $b -selectbackground grey40 incr n } # remove excess colors set col [lrange $col 0 [expr {$n - 1}]] ## for debug # if { [llength $col] != [llength [tolist [.graphdialog.center.right.text1 get 1.0 {end - 1 chars}]]] } { # puts "PROBLEMS: colors and nodes of different length" # } } else { set col {} } xschem setprop rect 2 $graph_selected color $col fast } proc fill_graph_listbox {} { set retval [.graphdialog.top.search get] set retval [graph_get_signal_list [xschem raw_query list] $retval] .graphdialog.center.left.list1 delete 0 end eval .graphdialog.center.left.list1 insert 0 $retval } # called from event handlers (OK, KeyRelease, DoubleClick) in graph_edit_properties proc update_graph_node {node} { global graph_selected graph_update_nodelist regsub -all {\\?(["\\])} $node {\\\1} node_quoted ;#"4vim xschem setprop rect 2 $graph_selected node $node_quoted fast xschem draw_graph $graph_selected } proc update_div {graph_selected div} { set divis [.graphdialog.top2.$div get] if {[regexp {^[0-9]+$} $divis] && $divis < 1} { set divis 1 .graphdialog.top2.$div delete 0 end .graphdialog.top2.$div insert 0 $divis } xschem setprop rect 2 $graph_selected $div $divis xschem draw_graph $graph_selected } proc graph_edit_properties {n} { global graph_bus graph_sort graph_digital graph_selected colors graph_sel_color global graph_unlocked graph_schname graph_logx graph_logy xschem push_undo set geom {} if { [winfo exists .graphdialog]} { set geom [winfo geometry .graphdialog] } catch {destroy .graphdialog} toplevel .graphdialog -width 1 -height 1 update idletasks if {$geom ne {}} { wm geometry .graphdialog $geom} set graph_selected $n set graph_schname [xschem get schname] set_ne graph_sel_color 4 set_ne graph_sort 0 set graph_logx 0 if {[xschem getprop rect 2 $n logx] == 1} {set graph_logx 1} set graph_logy 0 if {[xschem getprop rect 2 $n logy] == 1} {set graph_logy 1} set graph_digital 0 if {[xschem getprop rect 2 $n digital] == 1} {set graph_digital 1} if {[regexp {unlocked} [xschem getprop rect 2 $n flags]]} { set graph_unlocked 1 } else { set graph_unlocked 0 } frame .graphdialog.top # another row of buttons frame .graphdialog.top2 frame .graphdialog.top3 panedwindow .graphdialog.center -orient horiz frame .graphdialog.bottom frame .graphdialog.center.left frame .graphdialog.center.right .graphdialog.center add .graphdialog.center.left .graphdialog.center.right pack .graphdialog.top -side top -fill x pack .graphdialog.top2 -side top -fill x pack .graphdialog.top3 -side top -fill x pack .graphdialog.center -side top -fill both -expand yes pack .graphdialog.bottom -side top -fill x # center-left frame label .graphdialog.center.left.lab1 -text {Signal list} button .graphdialog.center.left.add -text Add -command { graph_add_nodes; graph_update_nodelist } listbox .graphdialog.center.left.list1 -width 20 -height 5 -selectmode extended \ -yscrollcommand {.graphdialog.center.left.yscroll set} \ -xscrollcommand {.graphdialog.center.left.xscroll set} scrollbar .graphdialog.center.left.yscroll -command {.graphdialog.center.left.list1 yview} scrollbar .graphdialog.center.left.xscroll -orient horiz -command {.graphdialog.center.left.list1 xview} grid .graphdialog.center.left.lab1 .graphdialog.center.left.add grid .graphdialog.center.left.list1 - .graphdialog.center.left.yscroll -sticky nsew grid .graphdialog.center.left.xscroll - -sticky nsew grid rowconfig .graphdialog.center.left 0 -weight 0 grid rowconfig .graphdialog.center.left 1 -weight 1 -minsize 2c grid columnconfig .graphdialog.center.left 0 -weight 1 grid columnconfig .graphdialog.center.left 1 -weight 1 # center right frame label .graphdialog.center.right.lab1 -text {Signals in graph} text .graphdialog.center.right.text1 -wrap none -width 50 -height 5 -bg grey70 -fg black \ -insertbackground grey40 -exportselection 1 \ -yscrollcommand {.graphdialog.center.right.yscroll set} \ -xscrollcommand {.graphdialog.center.right.xscroll set} scrollbar .graphdialog.center.right.yscroll -command {.graphdialog.center.right.text1 yview} scrollbar .graphdialog.center.right.xscroll -orient horiz -command {.graphdialog.center.right.text1 xview} grid .graphdialog.center.right.lab1 grid .graphdialog.center.right.text1 - .graphdialog.center.right.yscroll -sticky nsew grid .graphdialog.center.right.xscroll - -sticky nsew grid rowconfig .graphdialog.center.right 0 -weight 0 grid rowconfig .graphdialog.center.right 1 -weight 1 -minsize 3c grid columnconfig .graphdialog.center.right 0 -weight 1 grid columnconfig .graphdialog.center.right 1 -weight 1 # bottom frame button .graphdialog.bottom.cancel -text Cancel -command { destroy .graphdialog set graph_selected {} set graph_schname {} } button .graphdialog.bottom.ok -text OK -command { if { [xschem get schname] eq $graph_schname } { update_graph_node [string trim [.graphdialog.center.right.text1 get 1.0 {end - 1 chars}] " \n"] xschem setprop rect 2 $graph_selected x1 [.graphdialog.top3.xmin get] fast xschem setprop rect 2 $graph_selected x2 [.graphdialog.top3.xmax get] fast xschem setprop rect 2 $graph_selected y1 [.graphdialog.top3.min get] fast xschem setprop rect 2 $graph_selected y2 [.graphdialog.top3.max get] fast if {$graph_unlocked} { xschem setprop rect 2 $graph_selected flags {graph,unlocked} fast } else { xschem setprop rect 2 $graph_selected flags {graph} fast } destroy .graphdialog set graph_selected {} set graph_schname {} } else { destroy .graphdialog set graph_selected {} set graph_schname {} } } button .graphdialog.bottom.apply -text Apply -command { if { [xschem get schname] eq $graph_schname } { update_graph_node [string trim [.graphdialog.center.right.text1 get 1.0 {end - 1 chars}] " \n"] xschem setprop rect 2 $graph_selected x1 [.graphdialog.top3.xmin get] fast xschem setprop rect 2 $graph_selected x2 [.graphdialog.top3.xmax get] fast xschem setprop rect 2 $graph_selected y1 [.graphdialog.top3.min get] fast xschem setprop rect 2 $graph_selected y2 [.graphdialog.top3.max get] fast if {$graph_unlocked} { xschem setprop rect 2 $graph_selected flags {graph,unlocked} fast } else { xschem setprop rect 2 $graph_selected flags {graph} fast } } } # top packs pack .graphdialog.bottom.ok -side left pack .graphdialog.bottom.apply -side left pack .graphdialog.bottom.cancel -side left for {set i 4} {$i < 22} {incr i} { radiobutton .graphdialog.bottom.r$i -value $i -bg [lindex $colors $i] \ -variable graph_sel_color -command graph_change_wave_color pack .graphdialog.bottom.r$i -side left } # top2 frame label .graphdialog.top2.labunitx -text {X units} spinbox .graphdialog.top2.unitx -values {f p n u m 1 k M G T} -width 2 \ -command { xschem setprop rect 2 $graph_selected unitx [.graphdialog.top2.unitx get] xschem draw_graph $graph_selected } label .graphdialog.top2.labunity -text { Y units} spinbox .graphdialog.top2.unity -values {f p n u m 1 k M G T} -width 2 \ -command { xschem setprop rect 2 $graph_selected unity [.graphdialog.top2.unity get] xschem draw_graph $graph_selected } label .graphdialog.top2.labdivx -text { X div.} entry .graphdialog.top2.divx -width 2 bind .graphdialog.top2.divx { update_div $graph_selected divx } label .graphdialog.top2.labdivy -text { Y div.} entry .graphdialog.top2.divy -width 2 bind .graphdialog.top2.divy { update_div $graph_selected divy } label .graphdialog.top2.labsubdivx -text { X subdiv.} entry .graphdialog.top2.subdivx -width 2 bind .graphdialog.top2.subdivx { xschem setprop rect 2 $graph_selected subdivx [.graphdialog.top2.subdivx get] xschem draw_graph $graph_selected } label .graphdialog.top2.labsubdivy -text { Y subdiv.} entry .graphdialog.top2.subdivy -width 2 bind .graphdialog.top2.subdivy { xschem setprop rect 2 $graph_selected subdivy [.graphdialog.top2.subdivy get] xschem draw_graph $graph_selected } label .graphdialog.top2.labdset -text { Dataset} entry .graphdialog.top2.dset -width 4 bind .graphdialog.top2.dset { xschem setprop rect 2 $graph_selected dataset [.graphdialog.top2.dset get] xschem draw_graph $graph_selected } .graphdialog.top2.dset insert 0 [xschem getprop rect 2 $graph_selected dataset] label .graphdialog.top2.labsweep -text { Sweep} entry .graphdialog.top2.sweep -width 10 bind .graphdialog.top2.sweep { xschem setprop rect 2 $graph_selected sweep [.graphdialog.top2.sweep get] xschem draw_graph $graph_selected } .graphdialog.top2.sweep insert 0 [xschem getprop rect 2 $graph_selected sweep] set graph_divx [xschem getprop rect 2 $graph_selected divx] if {$graph_divx eq {}} { set graph_divx 5} .graphdialog.top2.divx insert 0 $graph_divx set graph_divy [xschem getprop rect 2 $graph_selected divy] if {$graph_divy eq {}} { set graph_divy 5} .graphdialog.top2.divy insert 0 $graph_divy set graph_subdivx [xschem getprop rect 2 $graph_selected subdivx] if {$graph_subdivx eq {}} { set graph_subdivx 1} .graphdialog.top2.subdivx insert 0 $graph_subdivx set graph_subdivy [xschem getprop rect 2 $graph_selected subdivy] if {$graph_subdivy eq {}} { set graph_subdivy 1} .graphdialog.top2.subdivy insert 0 $graph_subdivy set graph_unitx [xschem getprop rect 2 $graph_selected unitx] if {$graph_unitx eq {}} { set graph_unitx 1} .graphdialog.top2.unitx set $graph_unitx set graph_unity [xschem getprop rect 2 $graph_selected unity] if {$graph_unity eq {}} { set graph_unity 1} .graphdialog.top2.unity set $graph_unity pack .graphdialog.top2.labunitx .graphdialog.top2.unitx \ .graphdialog.top2.labunity .graphdialog.top2.unity -side left pack .graphdialog.top2.labdivx .graphdialog.top2.divx \ .graphdialog.top2.labdivy .graphdialog.top2.divy \ .graphdialog.top2.labsubdivx .graphdialog.top2.subdivx \ .graphdialog.top2.labsubdivy .graphdialog.top2.subdivy \ .graphdialog.top2.labdset .graphdialog.top2.dset \ .graphdialog.top2.labsweep .graphdialog.top2.sweep -side left # top frame label .graphdialog.top.labsearch -text Search: entry .graphdialog.top.search -width 10 checkbutton .graphdialog.top.bus -text Bus -padx 2 -variable graph_bus checkbutton .graphdialog.top.incr -text {Incr. sort} -variable graph_sort -indicatoron 1 \ -command fill_graph_listbox checkbutton .graphdialog.top.unlocked -text {Unlocked X axis} -variable graph_unlocked checkbutton .graphdialog.top.dig -text {Digital} -variable graph_digital -indicatoron 1 \ -command { if { [xschem get schname] eq $graph_schname } { xschem setprop rect 2 $graph_selected digital $graph_digital fast xschem draw_graph $graph_selected } } label .graphdialog.top3.xlabmin -text { X min:} entry .graphdialog.top3.xmin -width 7 bind .graphdialog.top3.xmin { xschem setprop rect 2 $graph_selected x1 [.graphdialog.top3.xmin get] xschem draw_graph $graph_selected } label .graphdialog.top3.xlabmax -text { X max:} entry .graphdialog.top3.xmax -width 7 bind .graphdialog.top3.xmax { xschem setprop rect 2 $graph_selected x2 [.graphdialog.top3.xmax get] xschem draw_graph $graph_selected } label .graphdialog.top3.labmin -text { Y min:} entry .graphdialog.top3.min -width 7 bind .graphdialog.top3.min { xschem setprop rect 2 $graph_selected y1 [.graphdialog.top3.min get] xschem draw_graph $graph_selected } label .graphdialog.top3.labmax -text { Y max:} entry .graphdialog.top3.max -width 7 bind .graphdialog.top3.max { xschem setprop rect 2 $graph_selected y2 [.graphdialog.top3.max get] xschem draw_graph $graph_selected } button .graphdialog.top.clear -text Clear -padx 2 -command { .graphdialog.top.search delete 0 end fill_graph_listbox } pack .graphdialog.top.labsearch .graphdialog.top.search -side left pack .graphdialog.top.clear -side left pack .graphdialog.top.incr -side left pack .graphdialog.top.bus -side left pack .graphdialog.top.dig -side left pack .graphdialog.top.unlocked -side left .graphdialog.top3.min insert 0 [xschem getprop rect 2 $graph_selected y1] .graphdialog.top3.max insert 0 [xschem getprop rect 2 $graph_selected y2] .graphdialog.top3.xmin insert 0 [xschem getprop rect 2 $graph_selected x1] .graphdialog.top3.xmax insert 0 [xschem getprop rect 2 $graph_selected x2] # top3 frame set graph_logx [xschem getprop rect 2 $graph_selected logx] set graph_logy [xschem getprop rect 2 $graph_selected logy] if { $graph_logx eq {} } { set graph_logx 0 } if { $graph_logy eq {} } { set graph_logy 0 } checkbutton .graphdialog.top3.logx -padx 2 -text {Log X scale} -variable graph_logx \ -command { if { [xschem get schname] eq $graph_schname } { xschem setprop rect 2 $graph_selected logx $graph_logx fast if { $graph_logx eq 1} { xschem setprop rect 2 $graph_selected subdivx 8 fast .graphdialog.top2.subdivx delete 0 end .graphdialog.top2.subdivx insert 0 8 } else { xschem setprop rect 2 $graph_selected subdivx 4 fast .graphdialog.top2.subdivx delete 0 end .graphdialog.top2.subdivx insert 0 4 } xschem draw_graph $graph_selected } } checkbutton .graphdialog.top3.logy -text {Log Y scale} -variable graph_logy \ -command { if { [xschem get schname] eq $graph_schname } { xschem setprop rect 2 $graph_selected logy $graph_logy fast if { $graph_logy eq 1} { xschem setprop rect 2 $graph_selected subdivy 8 fast .graphdialog.top2.subdivy delete 0 end .graphdialog.top2.subdivy insert 0 8 } else { xschem setprop rect 2 $graph_selected subdivy 4 fast .graphdialog.top2.subdivy delete 0 end .graphdialog.top2.subdivy insert 0 4 } xschem draw_graph $graph_selected } } pack .graphdialog.top3.logx .graphdialog.top3.logy -side left pack .graphdialog.top3.xlabmin .graphdialog.top3.xmin .graphdialog.top3.xlabmax .graphdialog.top3.xmax -side left pack .graphdialog.top3.labmin .graphdialog.top3.min .graphdialog.top3.labmax .graphdialog.top3.max -side left # binding bind .graphdialog.top.search { fill_graph_listbox } bind .graphdialog.center.left.list1 { graph_add_nodes if { [xschem get schname] eq $graph_schname } { update_graph_node [string trim [.graphdialog.center.right.text1 get 1.0 {end - 1 chars}] " \n"] } } bind .graphdialog.center.right.text1 { if { [xschem get schname] eq $graph_schname } { update_graph_node [string trim [.graphdialog.center.right.text1 get 1.0 {end - 1 chars}] " \n"] } } bind .graphdialog { .graphdialog.bottom.ok invoke } bind .graphdialog { .graphdialog.bottom.cancel invoke } wm protocol .graphdialog WM_DELETE_WINDOW { .graphdialog.bottom.cancel invoke } # fill data in left listbox eval .graphdialog.center.left.list1 insert 0 [graph_get_signal_list [xschem raw_query list] {}] # fill data in right textbox set plotted_nodes [xschem getprop rect 2 $n node] if {[string length $plotted_nodes] > 0 && [string index $plotted_nodes end] ne "\n"} {append plotted_nodes \n} .graphdialog.center.right.text1 insert 1.0 $plotted_nodes graph_update_nodelist # add stuff in textbox at end of line + 1 char (after newline) # .graphdialog.center.right.text1 insert {insert lineend + 1 char} foo\n # tkwait window .graphdialog } proc graph_show_measure {{action show}} { global measure_id measure_text set_ne measure_text "y=\nx=" if { [info exists measure_id] } { after cancel $measure_id unset measure_id } destroy .measure if {$action eq {stop}} { return } set measure_id [after 400 { unset measure_id toplevel .measure -bg {} label .measure.lab -text $measure_text -bg black -fg yellow -justify left pack .measure.lab wm overrideredirect .measure 1 wm geometry .measure +[expr {[winfo pointerx .measure] +10}]+[expr {[winfo pointery .measure] -8}] }] } proc get_shell { curpath } { global netlist_dir debug_var global terminal simuldir execute 0 sh -c "cd $curpath && $terminal" } proc edit_netlist {netlist } { global netlist_dir debug_var global editor terminal OS simuldir set netlist_type [xschem get netlist_type] if { [regexp vim $editor] } { set ftype "-c \":set filetype=$netlist_type\"" } else { set ftype {} } if { [select_netlist_dir 0] ne "" } { if {$OS == "Windows"} { set cmd "$editor \"$netlist_dir/${netlist}\"" eval exec $cmd & } else { execute 0 sh -c "cd $netlist_dir && $editor $ftype \"${netlist}\"" } } return {} } # 20180926 # global_initdir should be set to: # INITIALLOADDIR for load # INITIALINSTDIR for instance placement # ext: .sch or .sym or .sch.sym or .sym.sch # proc save_file_dialog { msg ext global_initdir {initialf {}} {overwrt 1} } { upvar #0 $global_initdir initdir set temp $initdir if { $initialf ne {}} { set initialdir [file dirname $initialf] set initialf [file tail $initialf] } else { set initialdir $initdir set initialf {} } set initdir $initialdir set r [load_file_dialog $msg $ext $global_initdir 0 $overwrt $initialf] set initdir $temp return $r } proc is_xschem_file {f} { set a [catch {open "$f" r} fd] set ret 0 set score 0 set instances 0 if {$a} { puts stderr "Can not open file $f" } else { fconfigure $fd -translation binary while { [gets $fd line] >=0 } { if { [regexp {^[TKGVSE] \{} $line] } { incr score } if { [regexp {^[BL] +[0-9]+ +[-0-9.eE]+ +[-0-9.eE]+ +[-0-9.eE]+ +[-0-9.eE]+ +\{} $line] } {incr score} if { [regexp {^N +[-0-9.eE]+ +[-0-9.eE]+ +[-0-9.eE]+ +[-0-9.eE]+ +\{} $line] } {incr score} if { [regexp {^C +\{[^{}]+\} +[-0-9.eE]+ +[-0-9.eE]+ +[0-3]+ +[0-3]+ +\{} $line] } {incr instances; incr score} if { [regexp "^v\[ \t\]+\{xschem\[ \t\]+version\[ \t\]*=.*\[ \t\]+file_version\[ \t\]*=" $line] } { set ret 1 } } if { $score > 4 } { set ret 1} ;# Heuristic decision :-) if { $ret } { if { $instances} { set ret SCHEMATIC } else { set ret SYMBOL } } close $fd } # puts "score=$score" return $ret } proc hash_string {s} { set hash 5381 set len [string length $s] for {set i 0} { $i < $len} { incr i} { set c [string index $s $i] set ascii [scan $c %c] set hash [expr {($hash + ($hash << 5) + $ascii)%4294967296} ] } return $hash } ## Recent component toolbar namespace eval c_toolbar { # Create a variable inside the namespace variable c_t variable i set c_t(w) .load.l.recent set c_t(hash) [hash_string $XSCHEM_LIBRARY_PATH] set c_t(n) 25 set c_t(top) 0 for {set i 0} {$i < $c_t(n)} {incr i} { set c_t($i,text) {} set c_t($i,command) {} set c_t($i,file) {} } proc cleanup {} { variable c_t if {![info exists c_t(n)]} return set j 0 set n $c_t(n) set top $c_t(top) set i $top while {1} { set f [abs_sym_path $c_t($i,file)] if { $c_t($i,text) eq {} } {break} if { $j } { set k [expr {$i - $j}] if {$k < 0 } { set k [expr {$k + $n}]} set c_t($k,text) $c_t($i,text) set c_t($k,command) $c_t($i,command) set c_t($k,file) $c_t($i,file) set c_t($i,text) {} set c_t($i,command) {} set c_t($i,file) {} } if {$f ne {} && ![file exists $f]} { incr j } elseif {[array names files -exact $f] ne {}} { incr j } set files($f) 1 set i [expr {($i + 1) % $n} ] if {$i == $top} break } } proc display {} { variable c_t if { [winfo exists $c_t(w)]} { set w $c_t(w) set n $c_t(n) cleanup destroy $w.title for {set i 0} {$i < $n} {incr i} { destroy $w.b$i } set i $c_t(top) button $w.title -text Recent -pady 0 -padx 0 -width 7 -state disabled -disabledforeground black \ -background grey60 -highlightthickness 0 -borderwidth 0 -font {TkDefaultFont 12 bold} pack $w.title -side top -fill x while {1} { button $w.b$i -text $c_t($i,text) -pady 0 -padx 0 -command $c_t($i,command) -width 7 pack $w.b$i -side top -fill x set i [expr {($i + 1) % $n}] if { $i == $c_t(top) } break } } } proc add {f} { variable c_t for {set i 0} {$i < $c_t(n)} {incr i} { if { $c_t($i,file) eq $f } { return 0} } set ret 0 set ret 1 set i [expr { ($c_t(top)-1) % $c_t(n) } ];# last element set c_t($i,file) $f set c_t($i,command) "xschem abort_operation; myload_display_preview {$f}; xschem place_symbol {$f} " set c_t($i,text) [file tail [file rootname $f]] set c_t(top) $i if {$ret} {write_recent_file} return $ret } } ## end Recent component toolbar proc myload_set_colors1 {} { global myload_files1 dircolor for {set i 0} { $i< [.load.l.paneleft.list index end] } { incr i} { set name "[lindex $myload_files1 $i]" .load.l.paneleft.list itemconfigure $i -foreground black -selectforeground black foreach j [array names dircolor] { set pattern $j set color $dircolor($j) if { [regexp $pattern $name] } { .load.l.paneleft.list itemconfigure $i -foreground $color -selectforeground $color } } } } proc myload_set_colors2 {} { global myload_index1 myload_files2 dircolor set dir1 [abs_sym_path [.load.l.paneleft.list get $myload_index1]] for {set i 0} { $i< [.load.l.paneright.list index end] } { incr i} { set name "$dir1/[lindex $myload_files2 $i]" if {[ file isdirectory $name]} { .load.l.paneright.list itemconfigure $i -foreground blue foreach j [array names dircolor] { set pattern $j set color $dircolor($j) if { [regexp $pattern $name] } { .load.l.paneright.list itemconfigure $i -foreground $color -selectforeground $color } } } else { .load.l.paneright.list itemconfigure $i -foreground black } } } proc myload_set_home {dir} { global pathlist myload_files1 myload_index1 set curr_dirname [xschem get current_dirname] .load.l.paneleft.list selection clear 0 end if { $dir eq {.}} { set dir $curr_dirname} # puts "set home: dir=$dir, pathlist=$pathlist" set pl {} foreach path_elem $pathlist { if { ![string compare $path_elem .]} { set path_elem $curr_dirname } lappend pl $path_elem } set i [lsearch -exact $pl $dir] if { $i>=0 } { set myload_files1 $pathlist update myload_set_colors1 .load.l.paneleft.list xview moveto 1 set myload_index1 $i .load.l.paneleft.list selection set $myload_index1 } else { set myload_files1 [list $dir] update myload_set_colors1 .load.l.paneleft.list xview moveto 1 set myload_index1 0 .load.l.paneleft.list selection set 0 } } proc setglob {dir} { global globfilter myload_files2 set myload_files2 [lsort [glob -nocomplain -directory $dir -tails -type d .* *]] if { $globfilter eq {*}} { set myload_files2 ${myload_files2}\ [lsort [glob -nocomplain -directory $dir -tails -type {f} .* $globfilter]] } else { set myload_files2 ${myload_files2}\ [lsort [glob -nocomplain -directory $dir -tails -type {f} $globfilter]] } } proc load_file_dialog_mkdir {dir} { global myload_dir1 if { $dir ne {} } { file mkdir "${myload_dir1}/$dir" setglob ${myload_dir1} myload_set_colors2 } } proc load_file_dialog_up {dir} { global myload_dir1 bind .load.l.paneright.draw {} .load.l.paneright.draw configure -background white set d [file dirname $dir] if { [file isdirectory $d]} { myload_set_home $d setglob $d myload_set_colors2 set myload_dir1 $d } } proc myload_getresult {loadfile confirm_overwrt} { global myload_dir1 myload_retval myload_ext if { $myload_retval ne {}} { if {![file exists "$myload_dir1/$myload_retval"] } { return "$myload_dir1/$myload_retval" } if { $loadfile == 0 } { if {[file exists "$myload_dir1/$myload_retval"]} { if {$confirm_overwrt == 1 } { set answer [tk_messageBox -message "Overwrite $myload_dir1/${myload_retval}?" \ -icon warning -parent [xschem get topwindow] -type okcancel] } else { set answer ok } if {$answer eq {ok}} { return "$myload_dir1/$myload_retval" } else { return {} } } } set myload_type [is_xschem_file "$myload_dir1/$myload_retval"] if { $myload_type eq {0} } { set answer [ tk_messageBox -message "$myload_dir1/$myload_retval does not seem to be an xschem file...\nContinue?" \ -icon warning -parent [xschem get topwindow] -type yesno] if { $answer eq "no"} { set myload_retval {} return {} } else { return "$myload_dir1/$myload_retval" } } elseif { $myload_type ne {SYMBOL} && ($myload_ext eq {.sym}) } { set answer [ tk_messageBox -message "$myload_dir1/$myload_retval does not seem to be a SYMBOL file...\nContinue?" \ -icon warning -parent [xschem get topwindow] -type yesno] if { $answer eq "no"} { set myload_retval {} return {} } else { return "$myload_dir1/$myload_retval" } } else { return "$myload_dir1/$myload_retval" } } else { return {} } } proc myload_display_preview {f} { set myload_type [is_xschem_file $f] if { $myload_type ne {0} } { ### update if { [winfo exists .load] } { .load.l.paneright.draw configure -background {} xschem preview_window draw .load.l.paneright.draw "$f" bind .load.l.paneright.draw [subst {xschem preview_window draw .load.l.paneright.draw "$f"}] } } else { bind .load.l.paneright.draw {} .load.l.paneright.draw configure -background white } } # global_initdir: name of global variable containing the initial directory # loadfile: set to 0 if calling for saving instead of loading a file # set to 2 for non blocking operation (symbol insertion) # confirm_overwrt: ask before overwriting an existing file # initialf: fill the file entry box with this name (used when saving) # proc load_file_dialog {{msg {}} {ext {}} {global_initdir {INITIALINSTDIR}} {loadfile {1}} {confirm_overwrt {1}} {initialf {}}} { global myload_index1 myload_files2 myload_files1 myload_retval myload_dir1 pathlist OS global myload_default_geometry myload_sash_pos myload_yview tcl_version globfilter myload_dir2 global save_initialfile myload_loadfile myload_ext if { [winfo exists .load] } { .load.buttons_bot.cancel invoke } set myload_loadfile $loadfile set myload_ext $ext set save_initialfile $initialf set globfilter * set myload_retval {} upvar #0 $global_initdir initdir if { $loadfile != 2} {xschem set semaphore [expr {[xschem get semaphore] +1}]} toplevel .load -class dialog wm title .load $msg set_ne myload_index1 0 if { ![info exists myload_files1]} { set myload_files1 $pathlist set myload_index1 0 } set_ne myload_files2 {} panedwindow .load.l -orient horizontal -height 8c if { $loadfile == 2} {frame .load.l.recent} frame .load.l.paneleft eval [subst {listbox .load.l.paneleft.list -listvariable myload_files1 -width 20 -height 12 \ -yscrollcommand ".load.l.paneleft.yscroll set" -selectmode browse \ -xscrollcommand ".load.l.paneleft.xscroll set" -exportselection 0}] if { ![catch {.load.l.paneleft.list cget -justify}]} { .load.l.paneleft.list configure -justify right } myload_set_colors1 scrollbar .load.l.paneleft.yscroll -command ".load.l.paneleft.list yview" scrollbar .load.l.paneleft.xscroll -command ".load.l.paneleft.list xview" -orient horiz pack .load.l.paneleft.yscroll -side right -fill y pack .load.l.paneleft.xscroll -side bottom -fill x pack .load.l.paneleft.list -fill both -expand true bind .load.l.paneleft.list <> { # bind .load.l.paneright.draw {} # .load.l.paneright.draw configure -background white set myload_sel [.load.l.paneleft.list curselection] if { $myload_sel ne {} } { set myload_dir1 [abs_sym_path [.load.l.paneleft.list get $myload_sel]] set myload_index1 $myload_sel set globfilter * if {$save_initialfile eq {}} {.load.buttons_bot.entry delete 0 end} setglob $myload_dir1 myload_set_colors2 } } frame .load.l.paneright frame .load.l.paneright.draw -background white -height 3.8c listbox .load.l.paneright.list -listvariable myload_files2 -width 20 -height 12\ -yscrollcommand ".load.l.paneright.yscroll set" -selectmode browse \ -xscrollcommand ".load.l.paneright.xscroll set" -exportselection 0 scrollbar .load.l.paneright.yscroll -command ".load.l.paneright.list yview" scrollbar .load.l.paneright.xscroll -command ".load.l.paneright.list xview" -orient horiz pack .load.l.paneright.draw -side bottom -anchor s -fill x pack .load.l.paneright.yscroll -side right -fill y pack .load.l.paneright.xscroll -side bottom -fill x pack .load.l.paneright.list -side bottom -fill both -expand true if { $loadfile == 2} { .load.l add .load.l.recent -minsize 30 c_toolbar::display } .load.l add .load.l.paneleft -minsize 40 .load.l add .load.l.paneright -minsize 40 # .load.l paneconfigure .load.l.paneleft -stretch always # .load.l paneconfigure .load.l.paneright -stretch always frame .load.buttons frame .load.buttons_bot button .load.buttons_bot.ok -width 5 -text OK -command { set myload_retval [.load.buttons_bot.entry get] destroy .load xschem preview_window destroy {} {} set initdir "$myload_dir1" } button .load.buttons_bot.cancel -width 5 -text Cancel -command { set myload_retval {} destroy .load if {$myload_loadfile == 2} {xschem abort_operation} xschem preview_window destroy {} {} set initdir "$myload_dir1" } button .load.buttons.home -width 5 -text {Home} -command { bind .load.l.paneright.draw {} .load.l.paneright.draw configure -background white set myload_files1 $pathlist update myload_set_colors1 .load.l.paneleft.list xview moveto 1 set myload_index1 0 set myload_dir1 [abs_sym_path [.load.l.paneleft.list get $myload_index1]] setglob $myload_dir1 myload_set_colors2 .load.l.paneleft.list selection clear 0 end .load.l.paneright.list selection clear 0 end .load.l.paneleft.list selection set $myload_index1 } label .load.buttons_bot.label -text { File/Search:} entry .load.buttons_bot.entry if { $save_initialfile ne {} } { .load.buttons_bot.entry insert 0 $save_initialfile } bind .load.buttons_bot.entry { if {$save_initialfile eq {} } { set globfilter *[.load.buttons_bot.entry get]* if { $globfilter eq {**} } { set globfilter * } setglob $myload_dir1 } } radiobutton .load.buttons_bot.all -text All -variable globfilter -value {*} \ -command { setglob $myload_dir1 } radiobutton .load.buttons_bot.sym -text .sym -variable globfilter -value {*.sym} \ -command { setglob $myload_dir1 } radiobutton .load.buttons_bot.sch -text .sch -variable globfilter -value {*.sch} \ -command { setglob $myload_dir1 } button .load.buttons.up -width 5 -text Up -command {load_file_dialog_up $myload_dir1} label .load.buttons.mkdirlab -text { New dir: } -fg blue entry .load.buttons.newdir -width 16 button .load.buttons.mkdir -width 5 -text Create -fg blue -command { load_file_dialog_mkdir [.load.buttons.newdir get] } button .load.buttons.rmdir -width 5 -text Delete -fg blue -command { if { [.load.buttons.newdir get] ne {} } { file delete "${myload_dir1}/[.load.buttons.newdir get]" setglob ${myload_dir1} myload_set_colors2 } } button .load.buttons.pwd -text {Current dir} -command {load_file_dialog_up [xschem get schname]} pack .load.buttons.home .load.buttons.up .load.buttons.pwd -side left pack .load.buttons.mkdirlab -side left pack .load.buttons.newdir -expand true -fill x -side left pack .load.buttons.rmdir .load.buttons.mkdir -side right pack .load.buttons_bot.all .load.buttons_bot.sym .load.buttons_bot.sch -side left pack .load.buttons_bot.label -side left pack .load.buttons_bot.entry -side left -fill x -expand true pack .load.buttons_bot.cancel .load.buttons_bot.ok -side left pack .load.buttons_bot -side bottom -fill x pack .load.buttons -side bottom -fill x pack .load.l -expand true -fill both if { [info exists myload_default_geometry]} { wm geometry .load "${myload_default_geometry}" } myload_set_home $initdir if { $loadfile != 2} { bind .load { set myload_retval [.load.buttons_bot.entry get] if {$myload_retval ne {} } { destroy .load xschem preview_window destroy {} {} set initdir "$myload_dir1" } } bind .load.l.paneright.list { set myload_retval [.load.buttons_bot.entry get] if {$myload_retval ne {} && ![file isdirectory "$myload_dir1/[.load.l.paneright.list get $myload_sel]"]} { bind .load.l.paneright.draw {} destroy .load xschem preview_window destroy {} {} set initdir "$myload_dir1" } } } bind .load { set myload_retval {} destroy .load if {$myload_loadfile == 2} {xschem abort_operation} xschem preview_window destroy {} {} set initdir "$myload_dir1" } ### update if { [ info exists myload_sash_pos] } { eval .load.l sash mark 0 [.load.l sash coord 0] eval .load.l sash dragto 0 [subst $myload_sash_pos] } ### update .load.l.paneleft.list xview moveto 1 bind .load { set myload_sash_pos [.load.l sash coord 0] set myload_default_geometry [wm geometry .load] .load.l.paneleft.list xview moveto 1 # regsub {\+.*} $myload_default_geometry {} myload_default_geometry } bind .load.l.paneright.yscroll { set myload_yview [.load.l.paneright.list yview] } xschem preview_window create .load.l.paneright.draw {} set myload_dir1 [abs_sym_path [.load.l.paneleft.list get $myload_index1]] setglob $myload_dir1 myload_set_colors2 bind .load.l.paneright.list { set myload_yview [.load.l.paneright.list yview] } bind .load.l.paneright.list <> { set myload_yview [.load.l.paneright.list yview] set myload_sel [.load.l.paneright.list curselection] if { $myload_sel ne {} } { set myload_dir1 [abs_sym_path [.load.l.paneleft.list get $myload_index1]] set myload_dir2 [.load.l.paneright.list get $myload_sel] if {$myload_dir2 eq {..}} { set myload_d [file dirname $myload_dir1] } elseif { $myload_dir2 eq {.} } { set myload_d $myload_dir1 } else { if {$OS == "Windows"} { if {[regexp {^[A-Za-z]\:/$} $myload_dir1]} { set myload_d "$myload_dir1$myload_dir2" } else { set myload_d "$myload_dir1/$myload_dir2" } } else { if {$myload_dir1 eq "/"} { set myload_d "$myload_dir1$myload_dir2" } else { set myload_d "$myload_dir1/$myload_dir2" } } } if { [file isdirectory $myload_d]} { bind .load.l.paneright.draw {} .load.l.paneright.draw configure -background white myload_set_home $myload_d setglob $myload_d myload_set_colors2 set myload_dir1 $myload_d # .load.buttons_bot.entry delete 0 end } else { set globfilter * .load.buttons_bot.entry delete 0 end .load.buttons_bot.entry insert 0 $myload_dir2 myload_display_preview $myload_dir1/$myload_dir2 # puts "xschem preview_window draw .load.l.paneright.draw \"$myload_dir1/$myload_dir2\"" } } if {$myload_loadfile == 2} { set myload_retval [.load.buttons_bot.entry get] set r [myload_getresult 2 0] # puts "r=$r myload_dir1=$myload_dir1 myload_dir2=$myload_dir2" xschem abort_operation if {$r ne {}} { xschem place_symbol "$myload_dir1/$myload_dir2" } } };# bind .load.l.paneright.list <> if { [ info exists myload_yview]} { .load.l.paneright.list yview moveto [lindex $myload_yview 0] } if {$loadfile != 2} { tkwait window .load xschem set semaphore [expr {[xschem get semaphore] -1}] } return [myload_getresult $loadfile $confirm_overwrt] } # get last n path components: example , n=1 --> /aaa/bbb/ccc/ddd.sch -> ccc/ddd.sch proc get_cell {s n } { set slist [file split $s] set l [llength $slist] if { $n >= $l } {set n [expr {$l - 1}]} set p {} for {set i [expr {$l-1-$n}]} {$i < $l} { incr i } { append p [lindex $slist $i] if {$i < $l - 1} { append p {/} } } return $p } # chop last n path components from s proc path_head {s n } { set slist [file split $s] set l [llength $slist] if { $n < 0 } { set n 0 } set p {} for {set i 0} {$i < [expr {$l - $n}]} { incr i } { append p [lindex $slist $i] if {$i < $l -$n- 1 && ([lindex $slist $i] ne {/})} { append p {/} } } return $p } proc delete_files { dir } { if { [ info tclversion] >=8.4} { set x [tk_getOpenFile -title "DELETE FILES" -multiple 1 -initialdir [file dirname $dir] ] } else { set x [tk_getOpenFile -title "DELETE FILES" -initialdir [file dirname $dir] ] } foreach i $x { file delete $i } } proc create_pins {} { global env retval USER_CONF_DIR global filetmp set retval [ read_data_nonewline $filetmp ] regsub -all {<} $retval {[} retval regsub -all {>} $retval {]} retval set lines [split $retval \n] if { [file exists [abs_sym_path devices/ipin.sym]] } { set indirect 1 } else { set indirect 0 } # viewdata $retval set pcnt 0 set y 0 set fd [open $USER_CONF_DIR/.clipboard.sch "w"] foreach i $lines { if {$indirect} { puts $fd "C \{[rel_sym_path devices/[lindex $i 1].sym]\} 0 [set y [expr {$y-20}]] \ 0 0 \{ name=p[incr pcnt] lab=[lindex $i 0] \}" } else { puts $fd "C \{[rel_sym_path [lindex $i 1].sym]\} 0 [set y [expr {$y-20}]] \ 0 0 \{ name=p[incr pcnt] lab=[lindex $i 0] \}" } } close $fd xschem merge $USER_CONF_DIR/.clipboard.sch } proc rectorder {x1 y1 x2 y2} { if {$x2 < $x1} {set tmp $x1; set x1 $x2; set x2 $tmp} if {$y2 < $y1} {set tmp $y1; set y1 $y2; set y2 $tmp} return [list $x1 $y1 $x2 $y2] } proc order {x1 y1 x2 y2} { if {$x2 < $x1} {set tmp $x1; set x1 $x2; set x2 $tmp; set tmp $y1; set y1 $y2; set y2 $tmp } elseif {$x2==$x1 && $y2<$y1} {set tmp $y1; set y1 $y2; set y2 $tmp} return [list $x1 $y1 $x2 $y2] } proc rotation {x0 y0 x y rot flip} { set tmp [expr {$flip? 2*$x0-$x : $x}] if {$rot==0} {set rx $tmp; set ry $y } if {$rot==1} {set rx [expr {$x0 - $y +$y0}]; set ry [expr {$y0+$tmp-$x0}]} if {$rot==2} {set rx [expr {2*$x0-$tmp}]; set ry [expr {2*$y0-$y}]} if {$rot==3} {set rx [expr {$x0+$y-$y0}]; set ry [expr {$y0-$tmp+$x0}]} return [list $rx $ry] } proc schpins_to_sympins {} { global env USER_CONF_DIR set pinhsize 2.5 set first 1 xschem copy set clipboard [read_data_nonewline $USER_CONF_DIR/.clipboard.sch] set lines [split $clipboard \n] set fd [open $USER_CONF_DIR/.clipboard.sch "w"] foreach i $lines { set ii [split [regexp -all -inline {\S+} $i]] if {[regexp {^C \{.*(i|o|io)pin} $i ]} { if {[regexp {ipin} [lindex $ii 1]]} { set dir in } if {[regexp {opin} [lindex $ii 1]]} { set dir out } if {[regexp {iopin} [lindex $ii 1]]} { set dir inout } set rot [lindex $ii 4] set flip [lindex $ii 5] while {1} { if { [regexp {lab=} $i] } { regsub {^.*lab=} $i {} lab regsub {[\} ].*} $lab {} lab } if { [regexp {\}} $i]} { break} } set x0 [lindex $ii 2] set y0 [lindex $ii 3] if {$first} { puts $fd "G {$x0 $y0 }" set first 0 } set pinx1 [expr {$x0-$pinhsize}] set pinx2 [expr {$x0+$pinhsize}] set piny1 [expr {$y0-$pinhsize}] set piny2 [expr {$y0+$pinhsize}] if {![string compare $dir "out"] || ![string compare $dir "inout"] } { set linex1 [expr {$x0-20}] set liney1 $y0 set linex2 $x0 set liney2 $y0 set textx0 [expr {$x0-25}] set texty0 [expr {$y0-4}] set textflip [expr {!$flip}] } else { set linex1 [expr {$x0+20}] set liney1 $y0 set linex2 $x0 set liney2 $y0 set textx0 [expr {$x0+25}] set texty0 [expr {$y0-4}] set textflip [expr {$flip}] } lassign [rotation $x0 $y0 $linex1 $liney1 $rot $flip] linex1 liney1 lassign [rotation $x0 $y0 $linex2 $liney2 $rot $flip] linex2 liney2 lassign [order $linex1 $liney1 $linex2 $liney2] linex1 liney1 linex2 liney2 lassign [rotation $x0 $y0 $textx0 $texty0 $rot $flip] textx0 texty0 puts $fd "B 5 $pinx1 $piny1 $pinx2 $piny2 \{name=$lab dir=$dir\}" puts $fd "L 4 $linex1 $liney1 $linex2 $liney2 \{\}" puts $fd "T \{$lab\} $textx0 $texty0 $rot $textflip 0.2 0.2 \{\}" } } close $fd xschem paste } proc add_lab_no_prefix {} { global env retval USER_CONF_DIR global filetmp if { [file exists [abs_sym_path devices/ipin.sym]] } { set indirect 1 } else { set indirect 0 } set retval [ read_data_nonewline $filetmp ] regsub -all {<} $retval {[} retval regsub -all {>} $retval {]} retval set lines [split $retval \n] # viewdata $retval set pcnt 0 set y 0 set fd [open $USER_CONF_DIR/.clipboard.sch "w"] foreach i $lines { if {$indirect} { puts $fd "C \{devices/lab_pin.sym\} 0 [set y [expr {$y+20}]] \ 0 0 \{ name=p[incr pcnt] verilog_type=wire lab=[lindex $i 0] \}" } else { puts $fd "C \{lab_pin.sym\} 0 [set y [expr {$y+20}]] \ 0 0 \{ name=p[incr pcnt] verilog_type=wire lab=[lindex $i 0] \}" } } close $fd xschem merge $USER_CONF_DIR/.clipboard.sch } proc add_lab_prefix {} { global env retval USER_CONF_DIR global filetmp if { [file exists [abs_sym_path devices/ipin.sym]] } { set indirect 1 } else { set indirect 0 } set retval [ read_data_nonewline $filetmp ] regsub -all {<} $retval {[} retval regsub -all {>} $retval {]} retval set lines [split $retval \n] # viewdata $retval set pcnt 0 set y 0 set fd [open $USER_CONF_DIR/.clipboard.sch "w"] foreach i $lines { if {$indirect} { puts $fd "C \{devices/lab_pin.sym\} 0 [set y [expr {$y+20}]] \ 0 0 \{ name=p[incr pcnt] verilog_type=reg lab=i[lindex $i 0] \}" } else { puts $fd "C \{lab_pin.sym\} 0 [set y [expr {$y+20}]] \ 0 0 \{ name=p[incr pcnt] verilog_type=reg lab=i[lindex $i 0] \}" } } close $fd xschem merge $USER_CONF_DIR/.clipboard.sch } proc make_symbol {name} { global XSCHEM_SHAREDIR symbol_width set name [abs_sym_path $name ] # puts "make_symbol{}, executing: ${XSCHEM_SHAREDIR}/make_sym.awk $symbol_width ${name}" eval exec {awk -f ${XSCHEM_SHAREDIR}/make_sym.awk $symbol_width $name} return {} } proc make_symbol_lcc {name} { global XSCHEM_SHAREDIR set name [abs_sym_path $name] # puts "make_symbol{}, executing: ${XSCHEM_SHAREDIR}/make_sym_lcc.awk ${name}" eval exec {awk -f ${XSCHEM_SHAREDIR}/make_sym_lcc.awk $name} return {} } # create simulation dir 'simulation/' under current schematic directory proc simuldir {} { global netlist_dir local_netlist_dir if { $local_netlist_dir == 1 } { set simdir [xschem get current_dirname]/simulation file mkdir $simdir set netlist_dir $simdir return $netlist_dir } return {} } # # force==0: force creation of $netlist_dir (if not empty) # if netlist_dir empty and no dir given prompt user # else set netlist_dir to dir # # force==1: if no dir given prompt user # else set netlist_dir to dir # proc select_netlist_dir { force {dir {} }} { global netlist_dir env OS if { ( $force == 0 ) && ( $netlist_dir ne {} ) } { if {![file exist $netlist_dir]} { file mkdir $netlist_dir } regsub {^~/} $netlist_dir ${env(HOME)}/ netlist_dir return $netlist_dir } if { $dir eq {} } { if { $netlist_dir ne {} } { set initdir $netlist_dir } else { if {$OS == "Windows"} { set initdir $env(windir) } else { set initdir [pwd] } } # 20140409 do not change netlist_dir if user Cancels action set new_dir [tk_chooseDirectory -initialdir $initdir \ -parent [xschem get topwindow] -title {Select netlist DIR} -mustexist false] } else { set new_dir $dir } if {$new_dir ne {} } { if {![file exist $new_dir]} { file mkdir $new_dir } set netlist_dir $new_dir } regsub {^~/} $netlist_dir ${env(HOME)}/ netlist_dir return $netlist_dir } proc enter_text {textlabel {preserve_disabled disabled}} { global retval rcode has_cairo preserve_unchanged_attrs wm_fix set rcode {} toplevel .dialog -class Dialog wm title .dialog {Enter text} set X [expr {[winfo pointerx .dialog] - 30}] set Y [expr {[winfo pointery .dialog] - 25}] # 20100203 if { $wm_fix } { tkwait visibility .dialog } wm geometry .dialog "+$X+$Y" frame .dialog.f1 label .dialog.f1.txtlab -text $textlabel text .dialog.txt -width 100 -height 4 .dialog.txt delete 1.0 end .dialog.txt insert 1.0 $retval checkbutton .dialog.f1.l1 -text "preserve unchanged props" -variable preserve_unchanged_attrs \ -state $preserve_disabled pack .dialog.f1 -side top -fill x ;# -expand yes pack .dialog.f1.l1 -side left pack .dialog.f1.txtlab -side left -expand yes -fill x pack .dialog.txt -side top -fill both -expand yes frame .dialog.edit frame .dialog.edit.lab frame .dialog.edit.entries pack .dialog.edit.lab -side left pack .dialog.edit.entries -side left -fill x -expand yes pack .dialog.edit -side top -fill x if {$has_cairo } { entry .dialog.edit.entries.hsize -relief sunken -textvariable vsize -width 20 } else { entry .dialog.edit.entries.hsize -relief sunken -textvariable hsize -width 20 } entry .dialog.edit.entries.vsize -relief sunken -textvariable vsize -width 20 entry .dialog.edit.entries.props -relief sunken -textvariable props -width 20 pack .dialog.edit.entries.hsize .dialog.edit.entries.vsize \ .dialog.edit.entries.props -side top -fill x -expand yes label .dialog.edit.lab.hlab -text "hsize:" label .dialog.edit.lab.vlab -text "vsize:" label .dialog.edit.lab.proplab -text "props:" pack .dialog.edit.lab.hlab .dialog.edit.lab.vlab \ .dialog.edit.lab.proplab -side top frame .dialog.buttons button .dialog.buttons.ok -text "OK" -command \ { set retval [.dialog.txt get 1.0 {end - 1 chars}] if {$has_cairo} { set hsize $vsize } set rcode {ok} destroy .dialog } button .dialog.buttons.cancel -text "Cancel" -command \ { set retval {} set rcode {} destroy .dialog } button .dialog.buttons.b3 -text "Load" -command \ { global INITIALTEXTDIR if { ![info exists INITIALTEXTDIR] } { set INITIALTEXTDIR [xschem get current_dirname] } set a [tk_getOpenFile -parent .dialog -initialdir $INITIALTEXTDIR ] if [string compare $a ""] { set INITIALTEXTDIR [file dirname $a] read_data_window .dialog.txt $a } } button .dialog.buttons.b4 -text "Del" -command \ { .dialog.txt delete 1.0 end } pack .dialog.buttons.ok -side left -fill x -expand yes pack .dialog.buttons.cancel -side left -fill x -expand yes pack .dialog.buttons.b3 -side left -fill x -expand yes pack .dialog.buttons.b4 -side left -fill x -expand yes pack .dialog.buttons -side bottom -fill x bind .dialog { if ![string compare $retval [.dialog.txt get 1.0 {end - 1 chars}]] { .dialog.buttons.cancel invoke } } bind .dialog {.dialog.buttons.ok invoke} #grab set .dialog tkwait window .dialog return $retval } # evaluate a tcl command from GUI proc tclcmd {} { global tclcmd_txt if {[winfo exists .tclcmd]} { destroy .tclcmd xschem set semaphore [expr {[xschem get semaphore] -1}] } xschem set semaphore [expr {[xschem get semaphore] +1}] toplevel .tclcmd -class dialog label .tclcmd.txtlab -text {Enter TCL expression:} label .tclcmd.result -text {Result:} text .tclcmd.t -width 100 -height 8 text .tclcmd.r -width 100 -height 6 -yscrollcommand ".tclcmd.yscroll set" scrollbar .tclcmd.yscroll -command ".tclcmd.r yview" .tclcmd.t insert 1.0 $tclcmd_txt frame .tclcmd.b button .tclcmd.b.close -text Close -command { set tclcmd_txt [.tclcmd.t get 1.0 end] destroy .tclcmd xschem set semaphore [expr {[xschem get semaphore] -1}] } button .tclcmd.b.ok -text Evaluate -command { set tclcmd_txt [.tclcmd.t get 1.0 end] set res [eval $tclcmd_txt] .tclcmd.r delete 1.0 end .tclcmd.r insert 1.0 $res } pack .tclcmd.txtlab -side top -fill x pack .tclcmd.t -side top -fill both -expand yes pack .tclcmd.result -side top -fill x pack .tclcmd.b -side bottom -fill x pack .tclcmd.yscroll -side right -fill y pack .tclcmd.r -side top -fill both -expand yes pack .tclcmd.b.ok -side left -expand yes -fill x pack .tclcmd.b.close -side left -expand yes -fill x } proc select_layers {} { global dark_colorscheme colors enable_layer xschem set semaphore [expr {[xschem get semaphore] +1}] toplevel .sl -class dialog if { $dark_colorscheme == 1 } { set txt_color black } else { set txt_color white } set j 0 set f 0 frame .sl.f0 frame .sl.f1 pack .sl.f0 .sl.f1 -side top -fill x button .sl.f1.ok -text OK -command { destroy .sl} pack .sl.f1.ok -side left -expand yes -fill x frame .sl.f0.f$f pack .sl.f0.f$f -side left -fill y foreach i $colors { if { $dark_colorscheme == 1 } { set ind_bg white } else { set ind_bg black } if { $j == [xschem get pinlayer] } { set laylab [format %2d $j]-PIN set layfg $txt_color } elseif { $j == [xschem get wirelayer] } { set laylab [format %2d $j]-WIRE set layfg $txt_color } elseif { $j == [xschem get textlayer] } { set laylab [format %2d $j]-TEXT set layfg $txt_color } elseif { $j == [xschem get backlayer] } { set laylab [format %2d $j]-BG if { $dark_colorscheme == 1 } { set layfg white set ind_bg black } else { set layfg black set ind_bg white } } elseif { $j == [xschem get gridlayer] } { set laylab [format %2d $j]-GRID set layfg $txt_color } else { set laylab "[format %2d $j] " set layfg $txt_color } checkbutton .sl.f0.f$f.cb$j -text $laylab -variable enable_layer($j) -activeforeground $layfg \ -selectcolor $ind_bg -anchor w -foreground $layfg -background $i -activebackground $i \ -command { xschem enable_layers } pack .sl.f0.f$f.cb$j -side top -fill x incr j if { [expr {$j%10}] == 0 } { incr f frame .sl.f0.f$f pack .sl.f0.f$f -side left -fill y } } tkwait window .sl xschem set semaphore [expr {[xschem get semaphore] -1}] } proc color_dim {} { global dim_bg dim_value enable_dim_bg xschem set semaphore [expr {[xschem get semaphore] +1}] toplevel .dim -class dialog wm title .dim {Dim colors} checkbutton .dim.bg -text {Dim background} -variable enable_dim_bg # xschem color_dim sets also dim_value variable scale .dim.scale -digits 2 -label {Dim factor} -length 256 \ -showvalue 1 -command {xschem color_dim} -orient horizontal \ -from -5 -to 5 -resolution 0.1 button .dim.ok -text OK -command {destroy .dim} .dim.scale set $dim_value pack .dim.scale pack .dim.bg -side left pack .dim.ok -side right -anchor e tkwait window .dim xschem set semaphore [expr {[xschem get semaphore] -1}] } proc about {} { if [winfo exists .about] { bind .about.link {} bind .about.link2 {} destroy .about } toplevel .about -class dialog wm title .about {About XSCHEM} label .about.xschem -text "XSCHEM V[xschem get version]" -font {Sans 24 bold} label .about.descr -text "Schematic editor / netlister for VHDL, Verilog, SPICE, tEDAx" button .about.link -text {http://repo.hu/projects/xschem} -font Underline-Font -fg blue -relief flat button .about.link2 -text {https://github.com/StefanSchippers/xschem} -font Underline-Font -fg blue -relief flat button .about.link3 -text {Online XSCHEM Manual} -font Underline-Font -fg blue -relief flat label .about.copyright -text "\n Copyright 1998-2022 Stefan Schippers (stefan.schippers@gmail.com) \n This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE\n" button .about.close -text Close -command {destroy .about} -font {Sans 18} pack .about.xschem pack .about.link pack .about.link2 pack .about.link3 pack .about.descr pack .about.copyright pack .about.close bind .about.link { execute 0 xdg-open http://repo.hu/projects/xschem} bind .about.link2 { execute 0 xdg-open https://github.com/StefanSchippers/xschem} bind .about.link3 { execute 0 xdg-open http://repo.hu/projects/xschem/index.html} } proc property_search {} { global search_value search_found global search_exact global search_select global custom_token OS set search_found 0 while { !$search_found} { if { [winfo exists .dialog] } return xschem set semaphore [expr {[xschem get semaphore] +1}] toplevel .dialog -class Dialog wm title .dialog {Search} if { ![info exists X] } { set X [expr {[winfo pointerx .dialog] - 60}] set Y [expr {[winfo pointery .dialog] - 35}] } wm geometry .dialog "+$X+$Y" frame .dialog.custom label .dialog.custom.l -text "Token" entry .dialog.custom.e -width 32 .dialog.custom.e insert 0 $custom_token pack .dialog.custom.e .dialog.custom.l -side right frame .dialog.val label .dialog.val.l -text "Value" entry .dialog.val.e -width 32 .dialog.val.e insert 0 $search_value pack .dialog.val.e .dialog.val.l -side right frame .dialog.but button .dialog.but.ok -text OK -command { set search_value [.dialog.val.e get] set custom_token [.dialog.custom.e get] if {$debug_var<=-1} { puts stderr "|$custom_token|" } if { $search_exact==1 } { set search_found [xschem searchmenu exact $search_select $custom_token $search_value] } else { set search_found [xschem searchmenu regex $search_select $custom_token $search_value] } destroy .dialog } button .dialog.but.cancel -text Cancel -command { set search_found 1; destroy .dialog } # Window doesn't support regular expression, has to be exact match for now if {$OS == "Windows"} { set search_exact 1 checkbutton .dialog.but.sub -text Exact_search -variable search_exact -state disable } else { checkbutton .dialog.but.sub -text Exact_search -variable search_exact } radiobutton .dialog.but.nosel -text {Highlight} -variable search_select -value 0 radiobutton .dialog.but.sel -text {Select} -variable search_select -value 1 # 20171211 added unselect radiobutton .dialog.but.unsel -text {Unselect} -variable search_select -value -1 pack .dialog.but.ok -anchor w -side left pack .dialog.but.sub -side left pack .dialog.but.nosel -side left pack .dialog.but.sel -side left pack .dialog.but.unsel -side left pack .dialog.but.cancel -anchor e pack .dialog.custom -anchor e pack .dialog.val -anchor e pack .dialog.but -expand yes -fill x focus .dialog bind .dialog {.dialog.but.cancel invoke} bind .dialog {.dialog.but.ok invoke} bind .dialog {.dialog.but.ok invoke} grab set .dialog tkwait window .dialog xschem set semaphore [expr {[xschem get semaphore] -1}] } return {} } #20171029 # allows to call TCL hooks from 'format' strings during netlisting # example of symbol spice format definition: # format="@name @pinlist @symname @tcleval(