# # File: xschem.tcl # # This file is part of XSCHEM, # a schematic capture and Spice/Vhdl/Verilog netlisting tool for circuit # simulation. # Copyright (C) 1998-2023 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 if { ![string compare $filename ""] } then { tk_messageBox -type ok -message "Please give a file name as argument" return } toplevel .inutile wm title .inutile "(IN)UTILE (Stefan Schippers, sschippe)" wm iconname .inutile "(IN)UTILE" set utile_path $XSCHEM_SHAREDIR/utile 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}\] {}\]} { inutile_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 } } # $execute(id) is an integer identifying the last running pipeline # set id $execute(id) to get the current subprocess id in variable 'id' # $execute(status,$id) contains the status argument as given in proc execute # $execute(pipe,$id) contains the channel descriptor to read or write as specified in status # $execute(data,$id) contains the stdout of the pipeline (output data) # $execute(cmd,$id) contains the pipeline command # when subprocess ends all execute(...,$id) data is cleared # # The following post-mortem data is available for last finished process: # execute(cmd,last) : the command # execute(data,last) : the data # execute(error,last) : the errors (stderr) # execute(status,last) : the status argument as was given when calling proc execute # execute(exitcode,last): exit code of last finished process # # execute service function proc execute_fileevent {id} { global execute OS append execute(data,$id) [read $execute(pipe,$id) 1024] if { $OS != {Windows} } { set eof [eof $execute(pipe,$id)] # handle processes that close stdout. Read pipe will go into eof condition # but process is still running. Doing a close operation in blocking mode # will block execution until process exits. # In this situation we avoid setting pipe to blocking mode and do an # asynchronous close. We lose exit status and stderr though, but # avoid the program to freeze waiting for process to exit. set lastproc [lindex [pid $execute(pipe,$id)] end] set ps_status [exec ps -o state= -p $lastproc] set finished [regexp Z $ps_status] ;# if zombie consider process to be finished. } else { set eof [eof $execute(pipe,$id)] set finished 1 } if {$eof} { set report [regexp {1} $execute(status,$id)] fileevent $execute(pipe,$id) readable "" # setting pipe to blocking before closing allows to get pipeline exit status # do not ask status for processes that close stdout/stderr, as eof might # occur before process ends and following close blocks until process terminates. if {$finished} {fconfigure $execute(pipe,$id) -blocking 1} set exit_status 0 set catch_return [eval catch [list {close $execute(pipe,$id)} err] ] if {$catch_return} { global errorCode if {"CHILDSTATUS" == [lindex $errorCode 0]} { set exit_status [lindex $errorCode 2] } if {$report} {viewdata "Failed: $execute(cmd,$id)\nstderr:\n$err\ndata:\n$execute(data,$id)"} } else { if {$report} {viewdata "Completed: $execute(cmd,$id)\ndata:\n$execute(data,$id)"} } if {[info exists execute(callback,$id)] && $execute(callback,$id) ne {}} { uplevel #0 "eval $execute(callback,$id)" } catch {unset execute(callback,$id)} set execute(cmd,last) $execute(cmd,$id) set execute(data,last) $execute(data,$id) if { ![info exists err] } { set err {} } set execute(error,last) $err set execute(status,last) $execute(status,$id) if { ![info exists exit_status] } { set exit_status 0 } set execute(exitcode,last) $exit_status unset execute(pipe,$id) unset execute(data,$id) unset execute(status,$id) unset execute(cmd,$id) Y # apply a delay, process does not disappear immediately. if {[winfo exists .processlist]} { after 250 {insert_running_cmds .processlist.f2.lb}} } } 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. # status: # rw open pipe in 'r+' (read write) mode instead of 'r' # line set line buffering mode of channel # none set no channel buffering. # 1 get status report at process end # 0 no status report # # These options can be combined as in '1rwline' of '1rnone' # 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) } set mode r if {[regexp {rw} $status]} { set mode r+ } if { [catch {open "|$args" $mode} 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) "" # Apply a delay to catch the new process. if {[winfo exists .processlist]} { after 250 {insert_running_cmds .processlist.f2.lb}} fconfigure $pipe -blocking 0 if {[regexp {line} $status]} { fconfigure $pipe -buffering line } if {[regexp {none} $status]} { fconfigure $pipe -buffering none } fileevent $pipe readable "execute_fileevent $id" return $id } # kill selected sub-processes by looking up their command strings # into all running sub-processes, killing the matching ones # with the supplied 'sig'. proc kill_running_cmds {lb sig} { global execute set selected [$lb curselection] foreach idx $selected { set cmd1 [$lb get $idx] foreach {id pid cmd2} [get_running_cmds] { # puts "$cmd1\n$cmd2 \n$pid" if { $cmd1 eq $cmd2 } { exec kill $sig $pid break } } } # apply a delay, after a kill command process does not disappear # immediately. after 250 insert_running_cmds $lb } # refresh list of running commands in dialog box proc insert_running_cmds {lb} { $lb delete 0 end foreach {id pid cmd} [get_running_cmds] { # puts "inserting $cmd" $lb insert end $cmd } } # display stdout of selected sub-process proc view_process_status {lb} { global execute set exists 0 if { [winfo exists .pstat] } { .pstat.text delete 1.0 end set exists 1 } set selected [$lb curselection] if {$selected ne {} && [llength $selected] == 1} { set idx $selected set cmd1 [$lb get $idx] foreach {id pid cmd2} [get_running_cmds] { if { $cmd1 eq $cmd2 } { if {[catch { set t $execute(data,$id) } err]} { set t $err } if {$exists == 0} { viewdata $t ro .pstat } else { .pstat.text insert 1.0 $t } .pstat.text yview moveto 1 break } } } } # top level dialog displaying running sub-processes proc list_running_cmds {} { set top .processlist toplevel $top set frame1 $top.f1 set frame2 $top.f2 set frame3 $top.f3 frame $frame1 frame $frame2 frame $frame3 set lb $frame2.lb listbox $lb -width 70 -height 8 -selectmode extended \ -yscrollcommand "$frame2.yscroll set" \ -xscrollcommand "$frame2.xscroll set" scrollbar $frame2.yscroll -command "$lb yview" scrollbar $frame2.xscroll -orient horiz -command "$lb xview" pack $frame2.yscroll -side right -fill y pack $frame2.xscroll -side bottom -fill x pack $lb -side bottom -fill both -expand true button $frame3.b1 -width 16 -text {Terminate selected} -command "kill_running_cmds $lb -15" -bg yellow button $frame3.b2 -width 16 -text {Kill selected} -command "kill_running_cmds $lb -9" -bg red button $frame3.b3 -width 16 -text {View status} -command "view_process_status $lb" -bg PaleGreen button $frame3.b4 -width 16 -text {Dismiss} -command "destroy $top" -bg PaleGreen pack $frame3.b1 $frame3.b2 $frame3.b3 $frame3.b4 -side left -fill x -expand 1 pack $frame1 -fill x -expand 0 pack $frame2 -fill both -expand 1 pack $frame3 -fill x -expand 0 insert_running_cmds $lb } # for each running sub-process return a list of three elements per process: # the integer id, the process PID, the command string. proc get_running_cmds {} { global execute set ret {} foreach i [array names execute *pipe*] { set id [lindex [split $i ,] 1] lappend ret $id [pid $execute($i)] $execute(cmd,$id) } return $ret } # pause for $del_ms milliseconds, keep event loop responsive proc delay {del_ms} { global delay_flag after $del_ms {set delay_flag 1} vwait delay_flag unset delay_flag } #### Scrollable frame proc sframeyview {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 "sframeyview $container" ;# sframeyview 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 ## convert engineering form to number proc from_eng {i} { set str {} scan $i "%g%s" n str set str [string tolower $str] if { [regexp {^meg} $str] } { set str {meg} } else { set suffix [string index $str 0] } set mult [switch $suffix { a { expr {1e-18}} f { expr {1e-15}} p { expr {1e-12}} n { expr { 1e-9}} u { expr {1e-6}} m { expr {1e-3}} k { expr {1e3}} meg { expr {1e6}} g { expr {1e9}} t { expr {1e12}} default { expr {1.0}} }] return [expr {$n * $mult}] } ## convert number to engineering form proc to_eng {i} { set suffix {} set absi [expr {abs($i)}] if {$absi == 0.0} { set mult 1 ; set suffix {} } elseif {$absi >=1e12} { set mult 1e-12; set suffix T } elseif {$absi >=1e9} { set mult 1e-9 ; set suffix G } elseif {$absi >=1e6} { set mult 1e-6 ; set suffix M } elseif {$absi >=1e3} { set mult 1e-3 ; set suffix k } elseif {$absi >=0.1} { set mult 1 ; set suffix {} } elseif {$absi >=1e-3} { set mult 1e3 ; set suffix m } elseif {$absi >=1e-6} { set mult 1e6 ; set suffix u } elseif {$absi >=1e-9} { set mult 1e9 ; set suffix n } elseif {$absi >=1e-12} { set mult 1e12 ; set suffix p } elseif {$absi >=1e-15} { set mult 1e15 ; set suffix f } else { set mult 1e18 ; set suffix a} if {$suffix ne {}} { set i [expr {$i * $mult}] set s [format {%.5g%s} $i $suffix] } else { set s [format {%.5g} $i] } return $s } ## 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 $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 { { topwin {} } } { global recentfile $topwin.menubar.file.menu.recent delete 0 9 set i 0 if { [info exists recentfile] } { foreach i $recentfile { $topwin.menubar.file.menu.recent add command \ -command "xschem load {$i} gui" \ -label [file tail $i] } } } ## ngspice:: raw file access functions namespace eval ngspice { # Create a variable inside the namespace variable ngspice_data variable op_point_read } proc ngspice::get_current {n} { set raw_level [xschem get raw_level] set path [string range [xschem get sch_path] 1 end] # skip hierarchy components above the level where raw file has been loaded. # node path names to look up in raw file begin from there. set skip 0 while { $skip < $raw_level } { regsub {^[^.]*\.} $path {} path incr skip } set n [string tolower $n] set prefix $n # if xm1.rd is given get prefix (r) by removing path components (xm1.) regsub {.*\.} $prefix {} prefix set prefix [string range $prefix 0 0] # puts "ngspice::get_current: path=$path n=$n prefix=$prefix" set n $path$n set currname i if { ![sim_is_xyce] } { ;# ngspice if {$path ne {} } { set n $prefix.$n } if { ![regexp $prefix {[ve]}] } { set n @$n } set n i($n) } else { ;# xyce if { [regexp {\[i[bcedgsb]\]$} $n] } { regexp {\[(i[bcesdgb])\]$} $n curr1 currname if { $prefix == {d} } {set currname i} regsub {\[(i[bcesdgb])\]$} $n {} n } regsub {\[i\]} $n {} n set n $currname\($n\) } # puts "ngspice::get_current --> $n" set err [catch {set ngspice::ngspice_data($n)} res] if { $err } { set res {?} } # puts "$n --> $res" return $res } proc ngspice::get_diff_voltage {n m} { set raw_level [xschem get raw_level] set path [string range [xschem get sch_path] 1 end] # skip hierarchy components above the level where raw file has been loaded. # node path names to look up in raw file begin from there. set skip 0 while { $skip < $raw_level } { regsub {^[^.]*\.} $path {} path incr skip } set n [string tolower $n] set m [string tolower $m] set nn $path$n set mm $path$m set errn [catch {set ngspice::ngspice_data($nn)} resn] if {$errn} { set nn v(${path}${n}) set errn [catch {set ngspice::ngspice_data($nn)} resn] } set errm [catch {set ngspice::ngspice_data($mm)} resm] if {$errm} { set mm v(${path}${m}) set errm [catch {set ngspice::ngspice_data($mm)} resm] } if { $errn || $errm} { set res {?} } return $res } proc ngspice::get_voltage {n} { set raw_level [xschem get raw_level] set path [string range [xschem get sch_path] 1 end] # skip hierarchy components above the level where raw file has been loaded. # node path names to look up in raw file begin from there. set skip 0 while { $skip < $raw_level } { regsub {^[^.]*\.} $path {} path incr skip } set n [string tolower $n] # puts "ngspice::get_voltage: path=$path n=$n" set node $path$n # puts "ngspice::get_voltage: trying $node" set err [catch {set ngspice::ngspice_data($node)} res] if {$err} { set node v(${path}${n}) # puts "ngspice::get_voltage: trying $node" set err [catch {set ngspice::ngspice_data($node)} res] } if { $err } { set res {?} } return $res } proc update_schematic_header {} { global retval rcode set retval [xschem get header_text] text_line {Header/License text:} 0 if { $rcode ne {}} { xschem set header_text $retval } } proc ngspice::get_node {n} { set raw_level [xschem get raw_level] set path [string range [xschem get sch_path] 1 end] # skip hierarchy components above the level where raw file has been loaded. # node path names to look up in raw file begin from there. set skip 0 while { $skip < $raw_level } { regsub {^[^.]*\.} $path {} path incr skip } set n [string tolower $n] # n may contain $path, so substitute its value set n [ subst -nocommand $n ] set err [catch {set ngspice::ngspice_data($n)} res] if { $err } { set res {?} } return $res } ## end ngspice:: functions # test if currently set simulator is ngspice 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 } # test if currently set simulator is Xyce 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 } # tests if file f exists. One level of global scope 'subst' is done on f # to expand global variables / commands catching errors. # example: # % set b {$env(HOME)/.bashrc} # $env(HOME)/.bashrc # % file_exists $b # 1 # % # % # % set b {$env(HOMExx)/.bashrc} # $env(HOMExx)/.bashrc # % file_exists $b # 0 # % proc file_exists {f} { set ret 0 set r [catch "uplevel #0 {subst $f}" res] if {$r == 0} { if {[file exists $res]} { set ret 1} } return $ret } # 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 { [info tclversion] > 8.4 } { if { [string is list $res]} { return $res } else { return [split $res] } } else { if {![catch {llength $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 { [info tclversion] > 8.4 } { if { [string is list $s]} { return $s } else { return [split $s] } } else { if {![catch {llength $s}]} { return $s } else { return [split $s] } } } # Initialize the tcl sim array variable (if not already set) # setting up simulator / wave viewer commands 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"} 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 # A server communicating with bespice wave was set up in the function setup_tcp_bespice(). # This server is listening on port $bespice_listen_port. 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) {sh -c "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 {sframeyview .sim.topf} bind .sim { set simconf_default_geometry [wm geometry .sim] } bind .sim { sframeyview .sim.topf scroll -0.2} bind .sim { sframeyview .sim.topf scroll 0.2} sframeyview .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 tclcmd_puts debug_var while {1} { if {[gets $sock line] < 0} { break } else { append xschem_server_getdata(line,$sock) $line \n } } if {$debug_var<=-1} {puts "tcp<-- $xschem_server_getdata(line,$sock)"} # xschem command must be executed at global scope... redef_puts uplevel #0 [list catch $xschem_server_getdata(line,$sock) tclcmd_puts] rename puts {} rename ::tcl::puts puts if {$debug_var<=-1} {puts "tcp--> $tclcmd_puts"} set xschem_server_getdata(res,$sock) "$tclcmd_puts" puts -nonewline $sock "$xschem_server_getdata(res,$sock)" flush $sock close $sock ;# server closes if {$debug_var<=-1} {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) } # this function is called as soon as bespice wave connects to the communication server listening on $bespice_listen_port # it makes sure the communication over the socket connection is possible 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] # this informs bespice wave that it receives it's instructions from xschem. Some features will be adjusted for that. puts $bespice_server_getdata(sock) "set_customer_specialization xschem" } } proc xschem_server {sock addr port} { global xschem_server_getdata debug_var if {$debug_var<=-1} {puts "Accept $sock from $addr port $port"} fconfigure $sock -buffering line -blocking 0 set xschem_server_getdata(addr,$sock) [list $addr $port] set xschem_server_getdata(line,$sock) {} fileevent $sock readable [list xschem_getdata $sock] } proc list_hierarchy {} { set s [xschem list_hierarchy] set r {} foreach {a b} [lsort -decreasing -dictionary -index 0 -stride 2 $s] { append r $a { } $b \n } return $r } ## 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 1 # 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 {} { return [rel_sym_path [find_file_first 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 } ## $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) or netlist_name if given ## $S : schematic name full path (/home/schippes/.xschem/xschem_library/opamp.sch) ## $d : netlist directory proc sim_cmd {cmd} { global netlist_dir terminal set tool [xschem get netlist_type] set d ${netlist_dir} 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} } # puts "N=$N\nn=$n\ns=$s\nS=$S\nd=$d" return [subst $cmd] } ## $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) or netlist_name if given ## $S : schematic name full path (/home/schippes/.xschem/xschem_library/opamp.sch) ## $d : netlist directory proc simulate {{callback {}}} { global netlist_dir terminal sim env global execute XSCHEM_SHAREDIR has_x OS simuldir set_sim_defaults set netlist_type [xschem get netlist_type] if { [set_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 -nobackslashes $sim($tool,$def,cmd)] set save [pwd] cd $netlist_dir if {$OS == "Windows"} { # $cmd cannot be surrounded by {} as exec will change forward slash to backward slash if { $callback ne {} } { uplevel #0 "eval $callback" } #eval exec {cmd /V /C "cd $netlist_dir&&$cmd} eval exec $cmd & set id 0 } else { set execute(callback) $callback # puts $cmd set id [eval $fg $st $cmd] puts "Simulation started: execution ID: $id" } cd $save 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) or netlist_name if given ## $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 { [set_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 save [pwd] cd $netlist_dir set cmd [subst -nobackslashes $sim($tool,$def,cmd)] eval $fg $st $cmd cd $save } } # ============================================================ proc graph_push_undo {} { global graph_change_done if {$graph_change_done == 0} { xschem push_undo set graph_change_done 1 } } # 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 cadlayers 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 < $cadlayers} {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} .... # used in hilight_net() proc graph_add_nodes_from_list {nodelist} { global graph_bus graph_selected graph_schname if {$graph_bus} { set sep , } else { set sep \n } if { [winfo exists .graphdialog] } { set sel {} 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 "\\\\&" node_quoted xschem setprop rect 2 $graph_selected node $node_quoted fast xschem draw_graph $graph_selected } } } else { set sel {} set change_done 0 set first 0 set col [xschem getprop rect 2 [xschem get graph_lastsel] color] set nnn [xschem getprop rect 2 [xschem get graph_lastsel] node] 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} { xschem setprop rect 2 [xschem get graph_lastsel] color $col fastundo if {[string length $nnn] > 0 && ![regexp "\n$" $nnn]} { append nnn "\n" } append nnn $sel regsub -all {[\\"]} $nnn "\\\\&" node_quoted 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" if { [info tclversion] > 8.4} { .graphdialog.center.right.text1 tag configure t$n -background $b -selectbackground grey40 } else { .graphdialog.center.right.text1 tag configure t$n -background $b } 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 graph_fill_listbox {} { global graph_selected set retval [.graphdialog.top.search get] set rawfile [uplevel #0 {subst [xschem getprop rect 2 $graph_selected rawfile 2]}] set sim_type [uplevel #0 {subst [xschem getprop rect 2 $graph_selected sim_type 2]}] # puts "graph_fill_listbox: $rawfile $sim_type" if {$rawfile ne {}} { set res [xschem raw read $rawfile $sim_type] if {$res} { set retval [graph_get_signal_list [xschem raw_query list] $retval] } else { set retval {} } # puts "switch back" xschem raw switch_back } else { 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 graph_update_node {node} { global graph_selected graph_update_nodelist regsub -all {[\\"]} $node "\\\\&" node_quoted graph_push_undo xschem setprop rect 2 $graph_selected node $node_quoted fast xschem draw_graph $graph_selected } proc graph_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 } graph_push_undo xschem setprop rect 2 $graph_selected $div $divis xschem draw_graph $graph_selected } proc graph_set_linewidth {graph_sel} { global graph_linewidth_mult set custom_lw [.graphdialog.top.lwe get] if {[regexp {^[ \t]*$} $custom_lw]} { set custom_lw $graph_linewidth_mult } graph_push_undo xschem setprop rect 2 $graph_sel linewidth_mult $custom_lw } 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 cadlayers graph_rainbow global graph_linewidth_mult graph_change_done set graph_change_done 0 set geom {} if { [winfo exists .graphdialog]} { set geom [winfo geometry .graphdialog] } catch {destroy .graphdialog} toplevel .graphdialog ;# -width 1 -height 1 wm withdraw .graphdialog update idletasks set graph_selected $n set graph_schname [xschem get schname] set_ne graph_sel_color 4 set_ne graph_sort 0 set graph_rainbow 0 if {[xschem getprop rect 2 $n rainbow] == 1} {set graph_rainbow 1} 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 } if { [info tclversion] > 8.4} { ttk::combobox .graphdialog.center.right.list -values {dc ac tran op sp noise} -width 4 } else { entry .graphdialog.center.right.list -width 4 } if { [info tclversion] > 8.4} { bind .graphdialog.center.right.list <> { xschem setprop rect 2 $graph_selected sim_type [.graphdialog.center.right.list get] fast if {[file_exists [.graphdialog.center.right.rawentry get]]} { graph_fill_listbox } } if { [xschem getprop rect 2 $graph_selected sim_type 2] ne {}} { .graphdialog.center.right.list set [xschem getprop rect 2 $graph_selected sim_type 2] } else { .graphdialog.center.right.list set tran } } else { .graphdialog.center.right.list delete 0 end if { [xschem getprop rect 2 $graph_selected sim_type 2] ne {}} { .graphdialog.center.right.list insert 0 [xschem getprop rect 2 $graph_selected sim_type 2] } else { .graphdialog.center.right.list insert 0 tran } } bind .graphdialog.center.right.list { xschem setprop rect 2 $graph_selected sim_type [.graphdialog.center.right.list get] fast if {[file_exists [.graphdialog.center.right.rawentry get]]} { graph_fill_listbox } } label .graphdialog.center.right.rawlab -text { Raw file: } entry .graphdialog.center.right.rawentry -width 30 bind .graphdialog.center.right.rawentry { xschem setprop rect 2 $graph_selected rawfile [.graphdialog.center.right.rawentry get] fast if {[file_exists [.graphdialog.center.right.rawentry get]]} { graph_fill_listbox } } .graphdialog.center.right.rawentry insert 0 [xschem getprop rect 2 $graph_selected rawfile 2] .graphdialog.center.right.rawentry xview moveto 1 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 .graphdialog.center.right.list .graphdialog.center.right.rawlab \ .graphdialog.center.right.rawentry - grid configure .graphdialog.center.right.rawentry -sticky ew grid .graphdialog.center.right.text1 - - - .graphdialog.center.right.yscroll -sticky nsew grid .graphdialog.center.right.xscroll - - - - -sticky ew grid rowconfig .graphdialog.center.right 0 -weight 0 grid rowconfig .graphdialog.center.right 1 -weight 1 -minsize 3c grid rowconfig .graphdialog.center.right 2 -weight 0 grid columnconfig .graphdialog.center.right 0 -weight 0 grid columnconfig .graphdialog.center.right 1 -weight 0 grid columnconfig .graphdialog.center.right 2 -weight 0 grid columnconfig .graphdialog.center.right 3 -weight 1 grid columnconfig .graphdialog.center.right 4 -weight 0 # 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 } { graph_push_undo graph_update_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.ymin get] fast xschem setprop rect 2 $graph_selected y2 [.graphdialog.top3.ymax 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 {} } button .graphdialog.bottom.apply -text Apply -command { if { [xschem get schname] eq $graph_schname } { graph_push_undo graph_update_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.ymin get] fast xschem setprop rect 2 $graph_selected y2 [.graphdialog.top3.ymax 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 < $cadlayers} {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 { graph_push_undo 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 { graph_push_undo 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 { graph_update_div $graph_selected divx } label .graphdialog.top2.labdivy -text { Y div.} entry .graphdialog.top2.divy -width 2 bind .graphdialog.top2.divy { graph_update_div $graph_selected divy } label .graphdialog.top2.labsubdivx -text { X subdiv.} entry .graphdialog.top2.subdivx -width 2 bind .graphdialog.top2.subdivx { graph_push_undo 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 { graph_push_undo 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 { graph_push_undo 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 { # graph_push_undo # xschem setprop rect 2 $graph_selected sweep [.graphdialog.top2.sweep get] # xschem draw_graph $graph_selected # } bind .graphdialog.top2.sweep { graph_push_undo 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 -side left pack .graphdialog.top2.sweep -side left -fill x -expand yes # 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 graph_fill_listbox checkbutton .graphdialog.top.rainbow -text {Rainbow col.} -variable graph_rainbow \ -command { if { [xschem get schname] eq $graph_schname } { graph_push_undo xschem setprop rect 2 $graph_selected rainbow $graph_rainbow fast xschem draw_graph $graph_selected } } label .graphdialog.top.lw -text " Line width:" entry .graphdialog.top.lwe -width 4 bind .graphdialog.top.lwe { graph_set_linewidth $graph_selected xschem draw_graph $graph_selected } set custom_lw [xschem getprop rect 2 $n linewidth_mult] if {[regexp {^[ \t]*$} $custom_lw]} { .graphdialog.top.lwe insert 0 $graph_linewidth_mult } else { .graphdialog.top.lwe insert 0 $custom_lw } checkbutton .graphdialog.top.unlocked -text {Unlock. X axis} -variable graph_unlocked checkbutton .graphdialog.top.dig -text {Digital} -variable graph_digital -indicatoron 1 \ -command { if { [xschem get schname] eq $graph_schname } { graph_push_undo 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 { graph_push_undo 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 { graph_push_undo xschem setprop rect 2 $graph_selected x2 [.graphdialog.top3.xmax get] xschem draw_graph $graph_selected } label .graphdialog.top3.ylabmin -text { Y min:} entry .graphdialog.top3.ymin -width 7 bind .graphdialog.top3.ymin { graph_push_undo xschem setprop rect 2 $graph_selected y1 [.graphdialog.top3.ymin get] xschem draw_graph $graph_selected } label .graphdialog.top3.ylabmax -text { Y max:} entry .graphdialog.top3.ymax -width 7 bind .graphdialog.top3.ymax { graph_push_undo xschem setprop rect 2 $graph_selected y2 [.graphdialog.top3.ymax get] xschem draw_graph $graph_selected } label .graphdialog.top3.xlabmag -text { X/Y lab mag:} entry .graphdialog.top3.xmag -width 4 bind .graphdialog.top3.xmag { graph_push_undo xschem setprop rect 2 $graph_selected xlabmag [.graphdialog.top3.xmag get] xschem draw_graph $graph_selected } label .graphdialog.top3.ylabmag -text { } entry .graphdialog.top3.ymag -width 4 bind .graphdialog.top3.ymag { graph_push_undo xschem setprop rect 2 $graph_selected ylabmag [.graphdialog.top3.ymag get] xschem draw_graph $graph_selected } button .graphdialog.top.clear -text Clear -padx 2 -command { .graphdialog.top.search delete 0 end graph_fill_listbox } pack .graphdialog.top.labsearch -side left pack .graphdialog.top.search -side left -expand yes -fill x 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 pack .graphdialog.top.rainbow -side left pack .graphdialog.top.lw -side left pack .graphdialog.top.lwe -side left .graphdialog.top3.ymin insert 0 [xschem getprop rect 2 $graph_selected y1] .graphdialog.top3.ymax 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] .graphdialog.top3.xmag insert 0 [xschem getprop rect 2 $graph_selected xlabmag] .graphdialog.top3.ymag insert 0 [xschem getprop rect 2 $graph_selected ylabmag] # top3 frame set graph_rainbow [xschem getprop rect 2 $graph_selected rainbow] set graph_logx [xschem getprop rect 2 $graph_selected logx] set graph_logy [xschem getprop rect 2 $graph_selected logy] if { $graph_rainbow eq {} } { set graph_rainbow 0 } 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} -variable graph_logx \ -command { if { [xschem get schname] eq $graph_schname } { graph_push_undo xschem setprop rect 2 $graph_selected logx $graph_logx fast if { $graph_logx eq 1} { graph_push_undo xschem setprop rect 2 $graph_selected subdivx 8 fast .graphdialog.top2.subdivx delete 0 end .graphdialog.top2.subdivx insert 0 8 } else { graph_push_undo 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} -variable graph_logy \ -command { if { [xschem get schname] eq $graph_schname } { graph_push_undo xschem setprop rect 2 $graph_selected logy $graph_logy fast if { $graph_logy eq 1} { graph_push_undo xschem setprop rect 2 $graph_selected subdivy 8 fast .graphdialog.top2.subdivy delete 0 end .graphdialog.top2.subdivy insert 0 8 } else { graph_push_undo 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 \ .graphdialog.top3.xlabmin .graphdialog.top3.xmin .graphdialog.top3.xlabmax .graphdialog.top3.xmax \ .graphdialog.top3.ylabmin .graphdialog.top3.ymin .graphdialog.top3.ylabmax .graphdialog.top3.ymax \ .graphdialog.top3.xlabmag .graphdialog.top3.xmag .graphdialog.top3.ylabmag .graphdialog.top3.ymag \ -fill x -expand yes -side left # binding bind .graphdialog.top.search { graph_fill_listbox } bind .graphdialog.center.left.list1 { graph_add_nodes if { [xschem get schname] eq $graph_schname } { graph_update_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 } { graph_update_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 graph_fill_listbox # fill data in right textbox set plotted_nodes [xschem getprop rect 2 $n node 0] 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 wm deiconify .graphdialog if {$geom ne {}} { wm geometry .graphdialog $geom} } 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}] }] } # launch a terminal shell, if 'curpath' is given set path to 'curpath' proc get_shell { {curpath {}} } { global netlist_dir debug_var global terminal set save [pwd] if { $curpath ne {} } { cd $curpath eval execute 0 $terminal } else { eval execute 0 $terminal } cd $save } 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 { [set_netlist_dir 0] ne "" } { set save [pwd] cd $netlist_dir if {$OS == "Windows"} { set cmd "$editor \"${netlist}\"" eval exec $cmd & } else { eval execute 0 $editor $ftype \"${netlist}\" } cd $save } 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} { if { ![file exists $f] } { return 0 } elseif { [file isdirectory $f] } { return 0 } set a [catch {open "$f" r} fd] set ret 0 set score 0 set instances 0 set nline 0 set generator 0 if {$a} { puts stderr "Can not open file $f" } else { fconfigure $fd -translation binary while { [gets $fd line] >=0 } { # this is a script. not an xschem file if { $nline == 0 && [regexp {^#!} $line] } { #### too dangerous executing an arbitrary script... # close $fd # set fd [open "|$f"] set generator 1 # continue } 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 } incr nline } if { $score > 4 } { set ret 1} ;# Heuristic decision :-) if {$generator eq {1}} { set ret GENERATOR } elseif { $ret ne {0}} { if { $instances} { set ret SCHEMATIC } else { set ret SYMBOL } } close $fd } # puts "ret=$ret score=$score" return $ret } # "xschem hash_string" in scheduler.c is faster 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) [xschem 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 maxlen 0 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) set len [string length [regexp -inline $pattern $name]] if { $len > $maxlen } { .load.l.paneleft.list itemconfigure $i -foreground $color -selectforeground $color set maxlen $len } } } } 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 maxlen 0 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) set len [string length [regexp -inline $pattern $dir1]] puts "len=$len\npattern=$pattern\nname=$name\n\n\n" if { $len > $maxlen } { .load.l.paneright.list itemconfigure $i -foreground $color -selectforeground $color set maxlen $len } } } 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 myload_globfilter myload_files2 OS set myload_files2 [lsort [glob -nocomplain -directory $dir -tails -type d .* *]] if { $myload_globfilter eq {*}} { set myload_files2 ${myload_files2}\ [lsort [ glob -nocomplain -directory $dir -tails -type {f} .* $myload_globfilter]] } else { if {$OS == "Windows"} { regsub {:} $myload_globfilter {\:} myload_globfilter } set myload_files2 ${myload_files2}\ [lsort [ glob -nocomplain -directory $dir -tails -type {f} $myload_globfilter]] } } proc load_file_dialog_mkdir {dir} { global myload_dir1 has_x if { $dir ne {} } { if {[catch {file mkdir "${myload_dir1}/$dir"} err]} { puts $err if {$has_x} { tk_messageBox -message "$err" -icon error -parent [xschem get topwindow] -type ok } } 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 has_x if { $myload_retval ne {}} { if { [regexp {^https?://} $myload_retval] } { set fname $myload_retval } elseif { [regexp {^/} $myload_retval]} { set fname $myload_retval } else { set fname "$myload_dir1/$myload_retval" } if {![file exists "$fname"] } { return "$fname" } if { $loadfile == 0 } { if {[file exists "$fname"]} { if {$confirm_overwrt == 1 } { set answer [alert_ "Overwrite $fname?" {} 0 1] } else { set answer 1 } if {$answer eq {1}} { return "$fname" } else { set myload_retval {} return {} } } } set type [is_xschem_file "$fname"] if { $type eq {0} || $type eq {GENERATOR} } { if { $type eq {0} } { set answer [alert_ "$fname does not seem to be an xschem file...\nContinue?" {} 0 1] } else { ;# $type == GENERATOR set answer 1 } if { $answer eq {0}} { set myload_retval {} return {} } else { ;# $answer == 1 if { $type eq {GENERATOR} } { return "${fname}" } # $type == 0 but $answer==1 so return selected filename return "$fname" } # $type == SYMBOL or SCHEMATIC } elseif { $type ne {SYMBOL} && ($myload_ext eq {*.sym}) } { ;# SCHEMATIC set answer [ alert_ "$fname does not seem to be a SYMBOL file...\nContinue?" {} 0 1] if { $answer eq {0}} { set myload_retval {} return {} } else { return "$fname" } } else { ;# SYMBOL return "$fname" } } else { return {} } } proc myload_place_symbol {} { global myload_retval set entry [.load.buttons_bot.entry get] # puts "entry=$entry, myload_retval=$myload_retval" set myload_retval $entry set sym [myload_getresult 2 0] # puts "sym=$sym myload_dir1=$myload_dir1 myload_dir2=$myload_dir2" xschem abort_operation if {$sym ne {}} { xschem place_symbol "$sym" } } proc myload_display_preview {f} { set type [is_xschem_file $f] if { $type ne {0} && $type ne {GENERATOR} } { 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 myload_globfilter myload_dir2 global myload_save_initialfile myload_loadfile myload_ext if { [winfo exists .load] } { .load.buttons_bot.cancel invoke } set myload_loadfile $loadfile if {$ext ne {}} {set myload_ext $ext} set myload_globfilter $myload_ext set myload_save_initialfile $initialf 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 #### avoid clearing search entry and resetting glob filter #### when changing directory in left listbox # set myload_globfilter $myload_ext # if {$myload_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 $global_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 $global_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 { $myload_save_initialfile ne {} } { .load.buttons_bot.entry insert 0 $myload_save_initialfile } bind .load.buttons_bot.entry { if {$myload_save_initialfile eq {} } { set myload_globfilter *[.load.buttons_bot.entry get]* if { $myload_globfilter eq {**} } { set myload_globfilter * } setglob $myload_dir1 } # set to something different to any file to force a new placement in myload_place_symbol set myload_retval { } } bind .load.buttons_bot.entry { # set to something different to any file to force a new placement in myload_place_symbol set myload_retval { } } radiobutton .load.buttons_bot.all -text All -variable myload_globfilter -value {*} \ -command { set myload_ext $myload_globfilter; setglob $myload_dir1 } radiobutton .load.buttons_bot.sym -text .sym -variable myload_globfilter -value {*.sym} \ -command { set myload_ext $myload_globfilter; setglob $myload_dir1 } radiobutton .load.buttons_bot.sch -text .sch -variable myload_globfilter -value {*.sch} \ -command { set myload_ext $myload_globfilter; 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 $global_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 $global_initdir \"\$myload_dir1\" } " } bind .load " set myload_retval {} destroy .load if {\$myload_loadfile == 2} {xschem abort_operation} xschem preview_window destroy {} {} set $global_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 if {$myload_loadfile == 2} { bind .load { if { {%W} eq {.load} && $myload_retval ne {} && [.load.buttons_bot.entry get] ne $myload_retval} { myload_place_symbol } } } 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 { .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 { } } };# 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_user_xschemrc {} { global USER_CONF_DIR XSCHEM_SHAREDIR if {![file exists $USER_CONF_DIR/xschemrc]} { file copy $XSCHEM_SHAREDIR/xschemrc $USER_CONF_DIR/xschemrc puts stderr "copied system $XSCHEM_SHAREDIR/xschemrc to $USER_CONF_DIR/xschemrc" puts stderr "Please review the file and make your changes, then restart xschem" } else { puts stderr "$USER_CONF_DIR/xschemrc already exists, will not overwrite." } } 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] set dirprefix [file dirname [rel_sym_path [find_file_first ipin.sym]]] if {$dirprefix == {.}} { set dirprefix {}} else {append dirprefix {/}} # viewdata $retval set pcnt 0 set y 0 set fd [open $USER_CONF_DIR/.clipboard.sch "w"] foreach i $lines { puts $fd "C \{${dirprefix}[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 set dirprefix [file dirname [rel_sym_path [find_file_first ipin.sym]]] if {$dirprefix == {.}} { set dirprefix {}} else {append dirprefix {/}} 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 { puts $fd "C \{${dirprefix}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 set dirprefix [file dirname [rel_sym_path [find_file_first ipin.sym]]] if {$dirprefix == {.}} { set dirprefix {}} else {append dirprefix {/}} 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 { puts $fd "C \{${dirprefix}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 {ask {no}} } { global XSCHEM_SHAREDIR symbol_width set name [abs_sym_path $name ] set symname [abs_sym_path $name .sym] if { $ask eq {no} && [file exists $symname] } { set answer [tk_messageBox -message "Warning: symbol $symname already exists. Overwrite?" \ -icon warning -parent [xschem get topwindow] -type okcancel] if {$answer ne {ok}} { return {}} } # 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 has_x if { $local_netlist_dir == 1 } { set simdir [xschem get current_dirname]/simulation if {[catch {file mkdir "$simdir"} err]} { puts $err if {$has_x} { tk_messageBox -message "$err" -icon error -parent [xschem get topwindow] -type ok } } set netlist_dir $simdir return $netlist_dir } return {} } # # force==0: force creation of $netlist_dir (if netlist_dir variable not empty) # and return current setting. # if netlist_dir variable empty: # if 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 # # Return current netlist directory # proc set_netlist_dir { force {dir {} }} { global netlist_dir env OS has_x if { ( $force == 0 ) && ( $netlist_dir ne {} ) } { if {![file exist $netlist_dir]} { if {[catch {file mkdir "$netlist_dir"} err]} { puts $err if {$has_x} { tk_messageBox -message "$err" -icon error -parent [xschem get topwindow] -type ok } } } 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]} { if {[catch {file mkdir "$new_dir"} err]} { puts $err if {$has_x} { tk_messageBox -message "$err" -icon error -parent [xschem get topwindow] -type ok } } } 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 props 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.hsize frame .dialog.edit.vsize frame .dialog.edit.props pack .dialog.edit.hsize -side bottom -expand yes -fill x pack .dialog.edit.vsize -side bottom -expand yes -fill x pack .dialog.edit.props -side bottom -expand yes -fill x pack .dialog.edit -side top -fill x if {$has_cairo } { entry .dialog.edit.hsize.hsize -relief sunken -textvariable vsize -width 20 } else { entry .dialog.edit.hsize.hsize -relief sunken -textvariable hsize -width 20 } entry .dialog.edit.vsize.vsize -relief sunken -textvariable vsize -width 20 text .dialog.edit.props.props -width 70 -height 3 .dialog.edit.props.props insert 1.0 $props label .dialog.edit.hsize.hlab -text "hsize:" label .dialog.edit.vsize.vlab -text "vsize:" label .dialog.edit.props.proplab -text "props:" pack .dialog.edit.hsize.hlab -side left pack .dialog.edit.hsize.hsize -side left -fill x -expand yes pack .dialog.edit.vsize.vlab -side left pack .dialog.edit.vsize.vsize -side left -fill x -expand yes pack .dialog.edit.props.proplab -side left pack .dialog.edit.props.props -side left -fill x -expand yes frame .dialog.buttons button .dialog.buttons.ok -text "OK" -command \ { set props [.dialog.edit.props.props get 1.0 {end - 1 chars}] 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.txt {return_release %W; .dialog.buttons.ok invoke} #grab set .dialog tkwait window .dialog return $retval } # will redefine puts to output into tclcmd_puts proc redef_puts {} { global tclcmd_puts if ![llength [info command ::tcl::puts]] { rename puts ::tcl::puts proc puts args { set la [llength $args] if {$la<1 || $la>3} { error "usage: puts ?-nonewline? ?channel? string" } set nl \n if {[lindex $args 0]=="-nonewline"} { set nl "" set args [lrange $args 1 end] } if {[llength $args]==1} { set args [list stdout [join $args]] ;# (2) } foreach {channel s} $args break #set s [join $s] ;# (1) prevent braces at leading/tailing spaces if {$channel=="stdout" || $channel=="stderr"} { append tclcmd_puts $s$nl } else { set cmd ::tcl::puts if {$nl==""} {lappend cmd -nonewline} lappend cmd $channel $s eval $cmd } };# puts } } # return key release, used to remove last entered character # when binding close text-widget window to Shift-return or Control-return. proc return_release {window} { set curs [$window index insert] $window delete "$curs - 1 chars" $curs } proc tclcmd_ok_button {} { global tclcmd_txt tclcmd_puts set tclcmd_txt [.tclcmd.t get 1.0 end] redef_puts catch {uplevel #0 $tclcmd_txt} tclcmd_puts rename puts {} rename ::tcl::puts puts if {$tclcmd_puts != {} && [string index $tclcmd_puts end] != "\n"} { append tclcmd_puts "\n" } .tclcmd.r.r insert end $tclcmd_puts .tclcmd.r.r yview moveto 1 } # evaluate a tcl command from GUI proc tclcmd {} { global tclcmd_txt if {[winfo exists .tclcmd]} { destroy .tclcmd } toplevel .tclcmd -class dialog label .tclcmd.txtlab -text {Enter TCL expression. Shift-Return will evaluate} panedwindow .tclcmd.p -orient vert text .tclcmd.t -width 100 -height 3 frame .tclcmd.r text .tclcmd.r.r -width 100 -height 8 -yscrollcommand ".tclcmd.r.yscroll set" scrollbar .tclcmd.r.yscroll -command ".tclcmd.r.r yview" .tclcmd.p add .tclcmd.t .tclcmd.r .tclcmd.t insert 1.0 $tclcmd_txt frame .tclcmd.b button .tclcmd.b.clear -text Clear -command { .tclcmd.r.r delete 1.0 end } button .tclcmd.b.close -text Close -command { set tclcmd_txt [.tclcmd.t get 1.0 {end - 1 chars}] destroy .tclcmd } button .tclcmd.b.ok -text Evaluate -command {tclcmd_ok_button} # bind .tclcmd.t { .tclcmd.b.ok invoke } bind .tclcmd.t {return_release %W; .tclcmd.b.ok invoke } pack .tclcmd.txtlab -side top -fill x pack .tclcmd.b -side bottom -fill x pack .tclcmd.p -side top -fill both -expand yes pack .tclcmd.r.yscroll -side right -fill y pack .tclcmd.r.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 pack .tclcmd.b.clear -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}] } # show xschem about dialog 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 button .about.link4 -text {Local XSCHEM Manual} -font Underline-Font -fg blue -relief flat label .about.copyright -text "\n Copyright (C) 1998-2023 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.link4 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} bind .about.link4 {execute 0 xdg-open file://$XSCHEM_SHAREDIR/../doc/xschem/xschem_man/xschem_man.html} } proc property_search {} { global search_value search_found global search_exact search_case 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 $search_case] } else { set search_found [xschem searchmenu regex $search_select $custom_token $search_value $search_case] } 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 checkbutton .dialog.but.sub -text {Exact search} -variable search_exact checkbutton .dialog.but.case -text {Match case} -variable search_case 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.case -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} wm protocol .dialog WM_DELETE_WINDOW {.dialog.but.cancel 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(