2004-07-19 Adrian Dawe <adrian.dawe@multigig.com>

* src/{Makefile.am,controls.tcl,engUnits.tcl,gnuplot.tcl,
	measure.tcl,nodeDialog.tcl,pkgIndex.tcl.in,readline.tcl,
	spicewish,spicewish.tcl,tclspice_source.tcl,viewer.tcl}:
	New and much improved tclspice CLI
This commit is contained in:
stefanjones 2004-07-19 23:20:11 +00:00
parent c89ae6e9f5
commit d913383038
13 changed files with 1361 additions and 2984 deletions

View File

@ -1,3 +1,10 @@
2004-07-19 Adrian Dawe <adrian.dawe@multigig.com>
* src/{Makefile.am,controls.tcl,engUnits.tcl,gnuplot.tcl,
measure.tcl,nodeDialog.tcl,pkgIndex.tcl.in,readline.tcl,
spicewish,spicewish.tcl,tclspice_source.tcl,viewer.tcl}:
New and much improved tclspice CLI
2004-07-12 Stefan Jones <stefan.jones@multigig.com>
* src/tclspice.c:

View File

@ -171,8 +171,11 @@ TCL_PKG_PATH = @TCL_PACKAGE_PATH@
TCLSPICE_VERSION = @VERSION@
TCL_FILES = libspice.so pkgIndex.tcl measure_spice.pl spicepp.pl \
tclspice_source.tcl spicepp.tcl
TCL_SOURCE = controls.tcl measure.tcl spicepp.tcl \
engUnits.tcl nodeDialog.tcl spicewish.tcl viewer.tcl \
gnuplot.tcl readline.tcl pkgIndex.tcl
TCL_FILES = libspice.so measure_spice.pl spicepp.pl $(TCL_SOURCE)
LIBSPICE_OBJS = tclspice.o
@ -195,15 +198,16 @@ install-tcl-recursive:
|| case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \
done;
install-tclspice: ${TCL_FILES}
install-tclspice: $(TCL_FILES)
$(mkinstalldirs) $(DESTDIR)$(TCL_PKG_PATH)
$(INSTALL_DATA) libspice.so $(DESTDIR)$(TCL_PKG_PATH)
$(mkinstalldirs) $(DESTDIR)$(TCL_PKG_PATH)/spice
$(INSTALL_DATA) pkgIndex.tcl $(DESTDIR)$(TCL_PKG_PATH)/spice
$(INSTALL_DATA) tclspice_source.tcl $(DESTDIR)$(TCL_PKG_PATH)/spice
$(INSTALL_DATA) spicepp.tcl $(DESTDIR)$(TCL_PKG_PATH)/spice
$(INSTALL_PROGRAM) spicepp.pl $(DESTDIR)$(TCL_PKG_PATH)/spice
$(INSTALL_PROGRAM) measure_spice.pl $(DESTDIR)$(TCL_PKG_PATH)/spice
@for f in $(TCL_SOURCE) ; do \
echo $(INSTALL_DATA) $$f $(DESTDIR)$(TCL_PKG_PATH)/spice; \
$(INSTALL_DATA) $$f $(DESTDIR)$(TCL_PKG_PATH)/spice; \
done
$(mkinstalldirs) $(DESTDIR)$(bindir)
$(INSTALL_PROGRAM) spicewish $(DESTDIR)$(bindir)
@ -225,4 +229,4 @@ tcl-recursive:
(cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) all) \
|| case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \
done;
make ${TCL_FILES}
make $(TCL_FILES)

43
src/controls.tcl Normal file
View File

@ -0,0 +1,43 @@
namespace eval controls {
variable wName ".controls"
proc create { } {
variable wName
if {[winfo exists $wName]} {
wm deiconify $wName
raise $wName
return
}
set w [toplevel $wName]
pack [button $w.bRun -text "Run" -command "spicewish::run" -width 8] -fill x
pack [button $w.bHalt -text "Halt" -command "spicewish::halt"] -fill x
pack [button $w.bPlot -text "Plot" -command "spicewish::plot"] -fill x
pack [button $w.bExit -text "Exit" -command "[namespace current]::Exit"] -fill x
pack [label $w.lFake -height 1] -fill x
}
proc Exit { } {
set widget_list [winfo children .]
# test if tk console ( .tkcon ) is being used
if [info exists ::CAD_HOME] {
# kill only the spicewish based windows
foreach winname $widget_list {
# viewer windows
if {[regexp {.tclspice[0-9]+} $winname]} {
wm withdraw $winname
}
if { $winname == ".controls" } {
wm withdraw ".controls"
}
}
} else { exit }
}
}

86
src/engUnits.tcl Normal file
View File

@ -0,0 +1,86 @@
namespace eval eng {
proc float_eng { num {forceunit ""} } {
set significant_digits 4 ;#- mininum signficant digits to show - might show more e.g. 125 will be shown no matter what
#lookup hashes
set table_eng_float_MSC(T) 1e+12
set table_eng_float_MSC(G) 1e+9
set table_eng_float_MSC(M) 1e+6
set table_eng_float_MSC(K) 1e+3
set table_eng_float_MSC(\n) 1; # Will need to change?
set table_eng_float_MSC(m) 1e-3
set table_eng_float_MSC(u) 1e-6
set table_eng_float_MSC(\u03BC) 1e-6
set table_eng_float_MSC(n) 1e-9
set table_eng_float_MSC(p) 1e-12
set table_eng_float_MSC(f) 1e-15
set table_eng_float_MSC(a) 1e-18
#inverse hash
set table_float_eng_MSC(1e+12)" T"
set table_float_eng_MSC(1e+9) " G"
set table_float_eng_MSC(1e+6) " M"
set table_float_eng_MSC(1e+3) " K"
set table_float_eng_MSC(1) " "
set table_float_eng_MSC(1e-3) " m"
set table_float_eng_MSC(1e-6) " \u03BC"
set table_float_eng_MSC(1e-9) " n"
set table_float_eng_MSC(1e-12) " p"
set table_float_eng_MSC(1e-15) " f"
set table_float_eng_MSC(1e-18) " a"
set indexes [lsort -real -decreasing "[array names table_float_eng_MSC]"]
set out {}; # If no suffix found, return number as is
if "$num != 0" {
foreach index $indexes {
set newnum "[expr {$num / $index}]"
set whole 0
set fraction 0; # In case of integer input
# regexp {^(\d+)} $newnum whole; # Messy!
# Uses stupid variable to save having to do two regexp's
regexp {^(\d+)\.*(\d*)} $newnum stupid whole fraction
if {[expr {[string match {*e*} $newnum] == 0}] && \
[expr {$whole >0 }] } {
set suf "$table_float_eng_MSC($index)"
#work out how many decimal places to show - fixed to 4 significant digits
set decimal_length_to_show [expr (($significant_digits - 1) - [string length $whole])]
if {$decimal_length_to_show >= 0} {
if { $fraction == "" } {
set newnm $whole
} else {
#need some decimal places to give the resolution required
set newnum "$whole.[string range $fraction 0 [expr ($decimal_length_to_show)]]"
}
} else {
#no decimal places
set newnum $whole ;#- just use the whole, no decimals
}
set out "$newnum$suf"
return "$out"
break
} else {
#puts "[expr {fmod($newnum,1)}] is not big enough"
}
}
}
if {[string equal $out {}]} {
return $num
}
}
}

63
src/gnuplot.tcl Normal file
View File

@ -0,0 +1,63 @@
namespace eval gnuplot {
proc writeDataFile { plotNum args } {
set fileName "gnuplot.dat"
set fileId [open $fileName "w"]
set plot_list "[spicewish::vectors::defaultScale $plotNum] $args"
puts $fileId "// $plot_list"
for {set dPoint 0} {$dPoint < [spice::plot_datapoints $plotNum]} {incr dPoint} {
for {set i 0} {$i < [llength $plot_list]} {incr i} {
set vecName [lindex $plot_list $i]
puts -nonewline $fileId "[spice::plot_get_value $vecName $plotNum $dPoint]\t"
}
puts $fileId ""
}
close $fileId
}
proc writeCommandFile { plotNum args } {
set fileName "gnuplot.cmd"
set fileId [open $fileName "w"]
puts $fileId "set title \"[spice::plot_title $plotNum]\""
puts $fileId "set xlabel \"[spicewish::vectors::defaultScale $plotNum]\""
puts $fileId "set ylabel \" \""
puts $fileId "plot \\"
for {set pCnt 0} {$pCnt < [llength $args]} {incr pCnt} {
set plotName [lindex $args $pCnt]
if {$pCnt ==[expr [llength $args] -1]} {
puts $fileId "\"gnuplot.dat\" using 1:[expr $pCnt +2] with lines title \"$plotName\""
} else {
puts $fileId "\"gnuplot.dat\" using 1:[expr $pCnt +2] with lines title \"$plotName\", \\"
}
}
puts $fileId "pause -1 \"Hit return to exit\""
close $fileId
}
proc plot { args } {
set plotNum 0
eval "set validVecs \[spicewish::vectors::validVectorList $plotNum $args \]"
if {$validVecs == ""} {
puts "Error: no valid vectors"
return
}
eval writeCommandFile $plotNum $validVecs
eval writeDataFile $plotNum $validVecs
}
}

315
src/measure.tcl Normal file
View File

@ -0,0 +1,315 @@
namespace eval meas_dialog {
variable measCnt 1
variable nName{} ""
variable cycleCnt{} ""
variable freq_d{} ""
variable riseTime_d{} ""
variable _propDelay{} ""
variable _riseTime_20{} ""
variable VDD 2.5
proc create_widget { } {
variable measCnt
variable nName
set w ".meas${measCnt}"
toplevel $w
pack [label $w.lNode -textvariable [namespace current]::nName($measCnt) -background "grey" -width 20]
pack [frame $w.fFreq] -fill x
pack [label $w.fFreq.l -text "Freq :"] -side left
pack [label $w.fFreq.v -textvariable spicewish::meas_dialog::freq_d($measCnt)] -side right
pack [frame $w.fRiseTime] -fill x
pack [label $w.fRiseTime.l -text "RiseTime :"] -side left
pack [label $w.fRiseTime.v -textvariable [namespace current]::riseTime_d($measCnt)] -side right
pack [frame $w.fCycleCnt] -fill x
pack [label $w.fCycleCnt.l -text "CycleCnt :"] -side left
pack [label $w.fCycleCnt.v -textvariable [namespace current]::cycleCnt($measCnt)] -side right
pack [button $w.bExit -text "Close"]
bind $w.bExit <ButtonPress-1> { spicewish::meas_dialog::destroy_widget [winfo parent %W] }
}
proc destroy_widget { w } {
if {![scan $w ".meas%i" index]} {return}
# Remove all associated triggers
catch { spice::unregisterTrigger ${index}_propDelay }
catch { spice::unregisterTrigger ${index}_riseTime_20 }
catch { spice::unregisterTrigger ${index}_riseTime_80 }
after 10 destroy $w ;# can't destroy widget beacuse of bind to button
}
proc TriggerCallBack { args } {
variable nName
variable freq_d
variable cycleCnt
variable riseTime_d
variable _propDelay
variable _riseTime_20
# set nName [lindex $args 0] ;# - NodeName
set tTime [lindex $args 1] ;# - Trigger Time
set sStep [expr [lindex $args 2] -1] ;# - pop trigger returns steps completed not spice step
set mName [lindex $args 5] ;# - MarkerName
if {![scan $mName "%i_" index]} { return }
if {$mName == "${index}_propDelay"} {
set freq_d($index) "[spicewish::eng::float_eng [expr 1/($tTime - $_propDelay($index))]]Hz"
set _propDelay($index) $tTime
incr cycleCnt($index)
}
if {$mName == "${index}_riseTime_20"} {
set _riseTime_20($index) $tTime
}
if {$mName == "${index}_riseTime_80"} {
set riseTime_d($index) "[spicewish::eng::float_eng [expr $tTime - $_riseTime_20($index)]]s"
}
}
proc stepCallBack { } {
}
proc registerTrigger { } {
variable measCnt
variable nName
variable VDD
spice::registerTrigger $nName($measCnt) [expr $VDD *0.5] [expr $VDD *0.5] 1 ${measCnt}_propDelay
spice::registerTrigger $nName($measCnt) [expr $VDD *0.2] [expr $VDD *0.2] 1 ${measCnt}_riseTime_20
spice::registerTrigger $nName($measCnt) [expr $VDD *0.8] [expr $VDD *0.8] 1 ${measCnt}_riseTime_80
}
proc add { nodeName } {
variable measCnt
variable nName
variable cycleCnt
variable freq_d
variable riseTime_d
variable _propDelay
variable _riseTime_20
if {$spice::steps_completed == 0 } {
return
}
set nName(${measCnt}) $nodeName
set freq_d(${measCnt}) 0
set cycleCnt(${measCnt}) 0
set riseTime_d(${measCnt}) 0
set _propDelay(${measCnt}) 0
set _riseTime_20(${measCnt}) 0
create_widget
registerTrigger
incr measCnt
}
spice::registerTriggerCallback [namespace current]::TriggerCallBack
}
namespace eval meas_markers {
variable VDD 2.5
proc preciseTrigPoint { trig_stepV trig_stepT prev_stepV prev_stepT Vtrigger} {
set spiceTimeStep [expr $trig_stepT - $prev_stepT]
set Vrate_through_trig [expr ($trig_stepV - $prev_stepV) / $spiceTimeStep]
set trigger_delta_time [expr (0 - (($trig_stepV - $Vtrigger) / $Vrate_through_trig))]
set tTrigger [expr $trig_stepT + $trigger_delta_time ]
return $tTrigger
}
proc riseTime { w x y } {
variable VDD
set index [marker_position $w $x $y]
if {$index == -1} { return }
# find the time when crosses 20% of VDD
for {set i $index} {$i < [nodeData length]} {incr i} {
set cur_V [nodeData index $i]
set cur_T [timeData index $i]
set prev_V [nodeData index [expr $i -1]]
set prev_T [timeData index [expr $i -1]]
if {$cur_V > [expr $VDD *0.2]} {
set x1 [preciseTrigPoint $cur_V $cur_T $prev_V $prev_T [expr $VDD *0.2]]
set y1 [expr $VDD *0.2]
break
}
}
# find the time when crosses 80% of VDD
for {set i $index} {$i < [nodeData length]} {incr i} {
set cur_V [nodeData index $i]
set cur_T [timeData index $i]
set prev_V [nodeData index [expr $i -1]]
set prev_T [timeData index [expr $i -1]]
if {$cur_V > [expr $VDD * 0.8]} {
set x2 [ preciseTrigPoint $cur_V $cur_T $prev_V $prev_T [expr $VDD *0.8]]
set y2 [expr $VDD *0.8]
break
}
}
$w line create "m_riseTime" -xdata {$x1 $x2} \
-ydata {$y1 $y2} \
-color "black" \
-label "" \
-symbol "cross"
set RiseTime [expr $x2 - $x1]
set xd [expr $x1 + (($x2 - $x1) /2)]
set yd [expr $y1 + (($y2 - $y1) /2)]
$w marker create text -text "[spicewish::eng::float_eng $RiseTime]s" \
-coords {$xd $yd} \
-name "m_riseTime_t" \
-anchor "nw"
blt::vector destroy nodeData
blt::vector destroy timeData
}
proc propDelay { w x y } {
variable VDD
set index [marker_position $w $x $y]
if {$index == -1} { return}
# find the time when crosses 50% of VDD
for {set i $index} {$i < [nodeData length]} {incr i} {
set cur_V [nodeData index $i]
set cur_T [timeData index $i]
set prev_V [nodeData index [expr $i -1]]
set prev_T [timeData index [expr $i -1]]
if {$cur_V > [expr $VDD * 0.5]} {
set x1 [preciseTrigPoint $cur_V $cur_T $prev_V $prev_T [expr $VDD *0.5]]
set y1 [expr $VDD * 0.5]
set index $i
break
}
}
# second crossing of 50% VDD
set state 0
for {set i $index} {$i < [nodeData length]} {incr i} {
set cur_V [nodeData index $i]
set cur_T [timeData index $i]
set prev_V [nodeData index [expr $i -1]]
set prev_T [timeData index [expr $i -1]]
if {$state && ($cur_V > [expr $VDD * 0.5])} {
set x2 [preciseTrigPoint $cur_V $cur_T $prev_V $prev_T [expr $VDD * 0.5]]
set y2 [expr $VDD * 0.5]
set state 0
break
} elseif {(!$state) && ($cur_V < [expr $VDD * 0.5])} {
set state 1
}
}
$w line create "m_riseTime" -xdata {$x1 $x2} \
-ydata {$y1 $y2} \
-color "black" \
-label "" \
-symbol "cross"
set Freq [expr 1/($x2 - $x1)]
set xd [expr $x1 + (($x2 - $x1) /2)]
set yd [expr $y1 + (($y2 - $y1) /2)]
$w marker create text -text "[spicewish::eng::float_eng $Freq]Hz" \
-coords {$xd $yd} \
-name "m_riseTime_t" \
-anchor "nw" \
-xoffset 10 -yoffset 10
blt::vector destroy nodeData
blt::vector destroy timeData
}
proc marker_position { w x y } {
variable VDD
set VDD [spice::get_value vdd [ expr $::spice::steps_completed -1]]
catch { $w element delete "m_riseTime" }
catch { $w marker delete "m_riseTime_t" }
set x [expr $x - 10]
set y [expr $y - 28]
set currentNode [$w element get current]
if {$currentNode == "" } { return -1 }
set xaxis [$w xaxis invtransform $x]
set yaxis [$w yaxis invtransform $y]
# get blt vectors
blt::vector create nodeData
blt::vector create timeData
spice::spicetoblt time timeData
spice::spicetoblt $currentNode nodeData
if {![$w element closest $x $y closestData -halo 5.0i]} {return -1}
set prev_V [nodeData index $closestData(index)]
# test if rising edge 20-80%
if {($prev_V < [expr $VDD *0.2]) || ($prev_V >[expr $VDD * 0.8])} {
return -1
}
# test next voltage is greater (rising edge)
if {$prev_V > [nodeData index [expr $closestData(index) +1]]} {
return -1
}
# back track index until rise of waveform
for {set index $closestData(index)} { $index > 0} {incr index -1} {
set cur_V [nodeData index $index]
if {$cur_V > $prev_V} { break }
set prev_V [nodeData index $index]
}
return $index
}
proc release { w x y } {
catch { $w element delete "m_riseTime" }
$w marker delete "m_riseTime_t"
}
proc bindings { w } {
bind $w <Key-F1> { spicewish::meas_markers::release %W.g %x %y }
bind $w <Key-F2> { spicewish::meas_markers::riseTime %W.g %x %y }
bind $w <Key-F3> { spicewish::meas_markers::propDelay %W.g %x %y}
}
}

172
src/nodeDialog.tcl Normal file
View File

@ -0,0 +1,172 @@
namespace eval nodeDialog {
variable w ".nodeSelect"
variable plot_title
variable selection
variable selectionCnt
proc create { } {
variable w
if {[winfo exists $w]} {
raise $w
return
}
toplevel $w
pack [frame $w.plots] -fill x
pack [listbox $w.plots.lst -height 3 \
-width 50 \
-yscrollcommand {.nodeSelect.plots.sx set} \
] -fill x -expand 1 -side left
pack [scrollbar $w.plots.sx -orient vertical \
-width 9 \
-command {.nodeSelect.plots.lst yview}\
] -fill y -side right
pack [frame $w.nodes] -fill both -expand 1
pack [blt::hierbox $w.nodes.hb -hideroot 1 \
-yscrollcommand {.nodeSelect.nodes.sx set} \
-selectbackground "#e6e6e6" \
] -fill both -expand 1 -side left
pack [scrollbar $w.nodes.sx -orient vertical \
-width 9 \
-command {.nodeSelect.nodes.hb yview} \
] -fill y -side right
$w.nodes.hb bind all <Button-1> {
set hierbox_path %W
set index [$hierbox_path index current]
set nodeName [$hierbox_path entry cget $index -label]
set plotTypeName [spice::plot_typename [.nodeSelect.plots.lst curselection]]
set listPos [lsearch $::spicewish::nodeDialog::selection($plotTypeName) $nodeName]
if { [ $hierbox_path entry cget $index -labelcolor] == "black"} {
$hierbox_path entry configure $index -labelcolor "blue"
$hierbox_path configure -selectforeground blue
if {$listPos == -1 } {
lappend spicewish::nodeDialog::selection($plotTypeName) $nodeName
}
} else {
$hierbox_path entry configure $index -labelcolor "black"
$hierbox_path configure -selectforeground black
set spicewish::nodeDialog::selection($plotTypeName) [lreplace $::spicewish::nodeDialog::selection($plotTypeName) $listPos $listPos]
}
set spicewish::nodeDialog::selectionCnt [llength $::spicewish::nodeDialog::selection($plotTypeName)]
}
pack [frame $w.fcont] -fill x
pack [button $w.fcont.bNodeCnt -textvariable spicewish::nodeDialog::selectionCnt \
-padx 1m \
-command spicewish::nodeDialog::clearSelection \
] -side left
pack [button $w.fcont.bPlot -text "Plot" \
-command spicewish::nodeDialog::plotSelection ]
bind $w.plots.lst <ButtonRelease-1> { spicewish::nodeDialog::selectPlot %W }
wm protocol $w WM_TAKE_FOCUS { spicewish::nodeDialog::currentPlots }
currentPlots
}
proc plotSelection { } {
variable selection
set plotNum [.nodeSelect.plots.lst curselection]
set plotTypeName [spice::plot_typename $plotNum]
eval "spicewish::viewer::oplot $plotNum $selection($plotTypeName)"
}
proc loadSelection { } {
variable selection
variable selectionCnt
set plotTypeName [spice::plot_typename [.nodeSelect.plots.lst curselection]]
set w_hb ".nodeSelect.nodes.hb"
for {set index 1} { $index <= [$w_hb index end] } {incr index} {
if {[$w_hb index $index] == "" } {continue}
set name [$w_hb entry cget $index -label]
set listPos [lsearch $selection($plotTypeName) $name]
if {$listPos != -1} {
$w_hb entry configure $index -labelcolor "blue"
}
}
set selectionCnt [llength $selection($plotTypeName)]
}
proc clearSelection { } {
variable selection
set plotTypeName [spice::plot_typename [.nodeSelect.plots.lst curselection]]
set selection($plotTypeName) ""
populateTree [.nodeSelect.plots.lst curselection]
}
proc currentPlots { } {
variable plot_title
variable selection
set index 0
while { ![catch {set t [spice::plot_name $index]} ] } {
set p_title [spice::plot_title $index]
set p_typeName [spice::plot_typename $index]
set plot_title($index) "$p_typeName :$p_title"
if {![info exists selection($p_typeName)]} {
set selection($p_typeName) ""
}
incr index
}
.nodeSelect.plots.lst delete 0 end
for {set i 0} {$i < [array size plot_title]} {incr i} {
.nodeSelect.plots.lst insert end $plot_title($i)
}
.nodeSelect.plots.lst selection set 0
populateTree 0
}
proc populateTree { index } {
set w ".nodeSelect.nodes.hb"
$w delete 0
set defaultScale [spice::plot_defaultscale $index]
foreach node [spice::plot_variables $index] {
set text ": [spicewish::vectors::type $index $node], real, [spice::plot_datapoints $index]"
if {$node == $defaultScale} {
set text "$text \[default scale\]"
}
$w insert -at 0 end $node -labelfont {times 9} -text $text -labelcolor "black"
}
loadSelection
}
proc selectPlot { w } {
set index [$w curselection]
populateTree $index
}
}

View File

@ -72,13 +72,47 @@ proc Loadspice { version dir } {
puts "DefineLinestyle $linestyleid $mask"
}
proc spice_init_gui { spicefile } {
if { ! [ winfo exists .controls ] } {
source "%LIB_DIR%/spice/tclspice_source.tcl" ;#- only load procs when gui needed
}
spicewish::init_gui $spicefile
# proc spice_init_gui { spicefile } {
# if { ! [ winfo exists .controls ] } {
# source "/usr/lib/spice/tclspice_source.tcl" ;#- only load procs when gui needed
# }
# spicewish::init_gui $spicefile
# }
proc spice_init_gui { fileName {gui 0} {batchMode 0}} {
# source tclcode
if {[info procs spicewish::plot] == ""} {
source [file join "spice/spicewish.tcl"]
}
if {!$batchMode} { spice::version }
if {$fileName != ""} {
switch [file extension $fileName] {
".tcl" {
source $fileName
}
".raw" {
spice::load $fileName
}
default {
spice::source $fileName
if {$batchMode} {
spice::run
spice::write out.raw
exit
}
}
}
}
if {$gui == 0} {
wm withdraw .
}
}
set ::spice_version $version
set ::spice_library $dir
}

45
src/readline.tcl Normal file
View File

@ -0,0 +1,45 @@
namespace eval readline {
proc completer { word start end line } {
if {[info procs ::tclreadline::TryFromList] == ""} {
::tclreadline::ScriptCompleter $word $start $end $line
}
set command [lindex $line 0]
switch $command {
"spicewish\:\:source" - "spice\:\:source" - "source" {
return [::tclreadline::TryFromList $word [glob *]]
}
"spicewish\:\:load" - "spice\:\:load" - "load" {
return [::tclreadline::TryFromList $word [glob *]]
}
"spicewish\:\:plot" - "plot" {
return [::tclreadline::TryFromList $word [spice::plot_variables 0]]
}
"spicewish\:\:iplot" - "iplot" {
return [::tclreadline::TryFromList $word [spice::plot_variables 0]]
}
"spicewish\:\:measDialog" - "measDialog" {
return [::tclreadline::TryFromList $word [spice::plot_variables 0]]
}
"spicewish\:\:write2gnuplot" - "write2gnuplot" {
return [::tclreadline::TryFromList $word [spice::plot_variables 0]]
}
"spicewish\:\:run" - "run" {
return ""
}
"spicewish\:\:halt" - "halt" {
return ""
}
}
::tclreadline::ScriptCompleter $word $start $end $line
}
::tclreadline::readline customcompleter "spicewish::readline::completer"
}

View File

@ -4,124 +4,126 @@
# the next line starts SpiceWish interactively with the given file name \
exec wish "$0" "$@"
namespace eval spicewish {
# load tclspice
package require spice
package require spice
package require tclreadline
proc usage { } {
puts "usage: spicewish \[options ...\] \[inputfile ...\]"
puts " -b Run in batch mode."
puts " -pp Execute spicePP on given file."
puts " -cm load Xspice codemodel."
puts " -version Display version number."
puts ""
exit
}
proc spicewish_usage { } {
puts "usage: spicewish \[options ...\] \[inputfile ...\]"
puts " -b Run in batch mode."
puts " -pp Execute spicePP on given file."
puts " -version Display version number."
puts ""
exit
}
proc version { } {
puts " $::spice_version\n"
exit
}
proc run_spicepp { fileName } {
proc run_spicepp { fileName } {
# load tcl code to run spicePP (perl)
source [file join $::spice_library "spice/spicepp.tcl"]
# load tcl code to run spicePP (perl)
source [file join $::spice_library "spice/spicepp.tcl"]
puts ""
# check if file contains Hspice statemenst
if {[spicepp::checkHSpiceFormat $fileName]} {
puts ""
# check if file contains Hspice statemenst
if {[spicepp::checkHSpiceFormat $fileName]} {
set ppFileName [spicepp::convert $fileName]
if {$ppFileName == ""} {
puts "spicepp: Error translating file '$fileName'."
exit
}
puts "spicewish: sourcing '$ppFileName'."
return $ppFileName
} else {
puts "spicepp: '$fileName' contains no Hspice statements."
puts "spicewish: sourcing '$fileName'."
return $fileName
}
}
proc load { argv argc } {
set fileName ""
set mode_batch 0
set mode_spicePP 0
set codeModels {}
for {set i 0} {$i < $argc} {incr i} {
set arg [lindex $argv $i]
if {[string match {-*} $arg]} {
set val [lindex $argv [expr $i + 1]]
switch -glob -- $arg {
-- - -argv {
set argv [concat -- [lrange $argv $i end]]
set argc [llength $argv]
break
}
-h - -help { usage }
-b { set mode_batch 1 }
-version { version }
-pp { set mode_spicePP 1 }
-cm {
set val [lindex $argv [incr i]]
lappend codeModels $val
}
default { usage }
}
} elseif {[file isfile $arg]} {
set fileName $arg
}
}
if {$fileName == "" } { usage }
if {[llength $codeModels] != 0} {
foreach codeModel $codeModels {
spice::codemodel $codeModel
}
}
if {$mode_spicePP} {
set fileName [ run_spicepp $fileName ]
}
if { [ file extension $fileName ] == ".tcl" } {
source $fileName
} else {
spice_init_gui $fileName
if {$mode_batch} {
# remove control widget
wm withdraw .
}
}
set ::spicewish_mode "standalone"
}
proc tclreadline { } {
# readline with prompt set to "tclspice > "
if {$::tcl_interactive} {
package require tclreadline
proc ::tclreadline::prompt1 { } { return "tclspice > " }
::tclreadline::Loop
set ppFileName [spicepp::convert $fileName]
if {$ppFileName == ""} {
puts "spicepp: Error translating file '$fileName'."
exit
}
puts "spicewish: sourcing '$ppFileName'."
return $ppFileName
} else {
puts "spicepp: '$fileName' contains no Hspice statements."
puts "spicewish: sourcing '$fileName'."
return $fileName
}
}
proc spicewish_load { argv argc } {
set fileName ""
set mode_batch 0
set mode_spicePP 0
for {set i 0} {$i < $argc} {incr i} {
set arg [lindex $argv $i]
if {[string match {-*} $arg]} {
set val [lindex $argv [expr $i + 1]]
switch -glob -- $arg {
-- - -argv {
set argv [concat -- [lrange $argv $i end]]
set argc [llength $argv]
break
}
-h - -help { spicewish_usage }
-b { set ::mode_batch 1 }
-version { spice::version }
-pp { set mode_spicePP 1 }
default { spicewish_usage }
}
} else {
set fileName $arg
}
}
if {$fileName == "" } {
spice_init_gui "" 0 $mode_batch
return
}
if {![file isfile $fileName]} { spicewish_usage }
if {$mode_spicePP} {
set fileName [ run_spicepp $fileName ]
}
spice_init_gui $fileName 0 $mode_batch
}
proc Tclreadline { } {
# readline with prompt set to "tclspice > "
if {$::tcl_interactive} {
proc ::tclreadline::prompt1 { } { return "tclspice -> " }
puts "tclspice ->"
puts "tclspice ->"
::tclreadline::Loop .tclspice-history
}
}
proc spicewish_import_namespaces { } {
# spice::
namespace eval spice {
namespace export ac help save alias sens alter altermod setcirc asciiplot jobs setplot aspice setscale bg let settype linearize shell bug listing shift show cdump maxstep showmod compose newhelp noise spec cross oldhelp spice dc op spice_data define spice_header deftype state delete plot_datapoints status delta plot_date step plot_get_value stop diff plot_name strcmp display plot_nvars tf disto plot_title dowhile plot_variables tran dump print transpose echo pz tutorial edit quit unalias else rehash undefine end repeat unlet reset fourier reshape version spicetoblt resume where get_output rspice get_param write goto running xgraph hardcopy rusage steps_completed blt_vnum codemodel loadsnap savesnap
}
namespace import -force spice::*
# spicewish::
rename ::source ::tclSource
rename ::load ::tclLoad
namespace eval spicewish {
set ::nameSpaceList [namespace export]
}
namespace import -force spicewish::*
foreach procName $::nameSpaceList {
tclreadline::readline add ${procName}
}
rename spicewish_import_namespaces ""
}
set ::tcl_interactive 1
source [file join $::spice_library spice/spicewish.tcl]
spicewish::load $argv $argc
spicewish_import_namespaces
spicewish_load $::argv $::argc
namespace import -force spice::*
namespace import -force spicewish::*
spicewish::tclreadline
Tclreadline

89
src/spicewish.tcl Normal file
View File

@ -0,0 +1,89 @@
#package require spice
namespace eval spicewish {
set dir [file join $::spice_library spice]
variable stepCallBack_list ""
source [file join $dir viewer.tcl ]
source [file join $dir controls.tcl ]
source [file join $dir engUnits.tcl ]
source [file join $dir nodeDialog.tcl ]
source [file join $dir readline.tcl ]
source [file join $dir measure.tcl ]
source [file join $dir gnuplot.tcl]
proc plot {args} {
# no plots passed load selection dialog
if {$args == ""} {
spicewish::nodeDialog::create
return
}
eval spicewish::viewer::plot $args
}
proc gui { } {
spicewish::controls::create
}
proc iplot {args} {
eval spicewish::viewer::iplot $args
}
proc source { fileName } {
if {![file exists $fileName]} {
puts "$fileName: No such file"
return
}
if {[string tolower [file extension $fileName]] == ".tcl" } {
uplevel 1 ::tclSource $fileName
return
}
spice::source $fileName
}
proc load { fileName } {
if {[string tolower [file extension $fileName]] == ".raw" } {
spice::load $fileName
return
}
uplevel 1 ::tclLoad $fileName
}
proc run { } {
if {$::spice::steps_completed == 0} {
spice::bg run
} else {
spice::bg resume
}
}
proc halt { } {
spice::halt
spicewish::viewer::Update
}
proc measDialog { node } {
spicewish::meas_dialog::add $node
}
proc write2gnuplot { args } {
eval spicewish::gnuplot::plot $args
}
proc stepCallBack { } {
variable stepCallBack_list
foreach callBack $stepCallBack_list {
eval $callBack
}
}
spice::registerStepCallback spicewish::stepCallBack 1 1
namespace export plot gui iplot source run halt load measDialog write2gnuplot
}

File diff suppressed because it is too large Load Diff

379
src/viewer.tcl Normal file
View File

@ -0,0 +1,379 @@
namespace eval viewer {
variable wNames ".tclspice"
variable viewCnt 1
variable viewTraces
variable viewTraceScale
variable view_w
variable view_traces
variable view_type
variable view_plotName
variable meas_dx
variable meas_dy
variable meas_x1
variable meas_y1
variable meas_x2
variable meas_y2
proc create { plotNum } {
variable wNames
variable viewCnt
variable view_w
set w [toplevel "${wNames}${viewCnt}"]
set plotName [spicewish::vectors::plotTypeName $plotNum]
wm title $w "\[$w\] - $plotName - [spice::plot_title $plotNum]"
# measurments
frame $w.meas
blt::graph $w.g -width 1000 -height 250
eval "$w.g axis configure x -scrollcommand {$w.x set}"
eval "$w.g axis configure y -scrollcommand {$w.y set}"
eval "scrollbar $w.x -orient horizontal -command {$w.g axis view x} -width 9"
eval "scrollbar $w.y -command {$w.g axis view y} -width 9"
# x-axis title
$w.g axis configure x -title [spicewish::vectors::defaultScale $plotNum]
create_measurments $w
# table
blt::table $w \
0,0 $w.meas -fill x -cspan 3 \
1,1 $w.g -fill both -rspan 2 \
1,2 $w.y -fill y -rspan 2 \
3,1 $w.x -fill x
blt::table configure $w r0 r2 c0 c2 r3 -resize none
# graph settings
Blt_ZoomStack $w.g
Blt_Crosshairs $w.g
$w.g grid on
$w.g axis configure x -command { spicewish::viewer::axis_callback } -subdivisions 2
set view_w($viewCnt) "$w"
# freq/risetime measurments
spicewish::meas_markers::bindings $w
eval "wm protocol $w WM_DELETE_WINDOW {spicewish::viewer::Destroy $w}"
incr viewCnt
return [expr $viewCnt -1]
}
proc create_measurments { w } {
variable meas_dx
variable meas_dy
variable meas_x1
variable meas_y1
variable meas_x2
variable meas_y2
set meas_dx($w.g) 0.00000000000000
set meas_dy($w.g) 0.00000000000000
set meas_x1($w.g) 0.00000000000000
set meas_y1($w.g) 0.00000000000000
set meas_x2($w.g) 0.00000000000000
set meas_y2($w.g) 0.00000000000000
set w_meas "$w.meas"
# measurements
pack [label $w_meas.dyv -textvariable spicewish::viewer::meas_dy($w.g) -background "lightgrey" -width 16] -side right
pack [label $w_meas.dy -text "dy :" -background "lightgrey" ] -side right
bind $w_meas.dyv <ButtonPress-1> { regexp {(.[0-9A-z]+)} %W w; puts "dy : $::spicewish::viewer::meas_dy($w.g)"}
pack [label $w_meas.dxv -textvariable spicewish::viewer::meas_dx($w.g) -background "grey" -width 16] -side right
pack [label $w_meas.dx -text "dx : " -background "grey"] -side right
bind $w_meas.dxv <ButtonPress-1> { regexp {(.[0-9A-z]+)} %W w; puts "dx : $::spicewish::viewer::meas_dx($w.g)"}
bind $w_meas.dxv <ButtonPress-2> {
regexp {(.[0-9A-z]+)} %W w
if { $::spicewish::viewer::meas_dx($w.g) != 0 } {
puts "freq : [spicewish::eng::float_eng [expr 1 / $::spicewish::viewer::meas_dx($w.g)]] Hz"
}
}
pack [ label $w_meas.y2v -textvariable spicewish::viewer::meas_y2($w.g) -background "lightgrey" -width 16 ] -side right
pack [ label $w_meas.y2 -text "y2 :" -background "lightgrey"] -side right
bind $w_meas.y2v <ButtonPress-1> { regexp {(.[0-9A-z]+)} %W w; puts "y2 : $::spicewish::viewer::meas_y2($w.g)"}
pack [ label $w_meas.y1v -textvariable spicewish::viewer::meas_y1($w.g) -background "grey" -width 16] -side right
pack [ label $w_meas.y1 -text "y1 :" -background "grey" ] -side right
bind $w_meas.y1v <ButtonPress-1> { regexp {(.[0-9A-z]+)} %W w; puts "y1 : $::spicewish::viewer::meas_y1($w.g)"}
pack [ label $w_meas.x2v -textvariable spicewish::viewer::meas_x2($w.g) -background "lightgrey" -width 16 ] -side right
pack [ label $w_meas.x2 -text "x2 :" -background "lightgrey" ] -side right
bind $w_meas.x2v <ButtonPress-1> { regexp {(.[0-9A-z]+)} %W w; puts "x2 : $::spicewish::viewer::meas_x2($w.g)"}
pack [ label $w_meas.x1v -textvariable spicewish::viewer::meas_x1($w.g) -background "grey" -width 16 ] -side right
pack [ label $w_meas.x1 -text "x1 :" -background "grey" ] -side right
bind $w_meas.x1v <ButtonPress-1> { regexp {(.[0-9A-z]+)} %W w; puts "x1 : $::spicewish::viewer::meas_x1($w.g)"}
# cursor motion bindings
bind $w.g <Motion> {
if { $::zoomInfo(%W,B,x) != "" } {
set ::spicewish::viewer::meas_x2(%W) [%W xaxis invtransform $::zoomInfo(%W,B,x)]
set ::spicewish::viewer::meas_y2(%W) [%W yaxis invtransform $::zoomInfo(%W,B,y)]
set ::spicewish::viewer::meas_x1(%W) [%W xaxis invtransform $::zoomInfo(%W,A,x)]
set ::spicewish::viewer::meas_y1(%W) [%W yaxis invtransform $::zoomInfo(%W,A,y)]
set ::spicewish::viewer::meas_dx(%W) [expr ($::spicewish::viewer::meas_x2(%W) - $::spicewish::viewer::meas_x1(%W))]
set ::spicewish::viewer::meas_dy(%W) [expr ($::spicewish::viewer::meas_y2(%W) - $::spicewish::viewer::meas_y1(%W))]
}
}
}
proc names { } {
variable view_w
return [array names view_w]
}
proc plot { args } {
eval "oplot 0 $args"
}
proc oplot { plotNum args } {
variable view_w
variable view_traces
variable view_type
variable view_plotName
eval "set args \[spicewish::vectors::validVectorList $plotNum $args\]"
if {$args == ""} {return}
# create a new viewer
set viewNum [create $plotNum]
set w $view_w($viewNum)
set view_traces($viewNum) ""
set view_type($viewNum) "plot"
set view_plotName($viewNum) [spicewish::vectors::plotTypeName $plotNum]
# add/update default scale
set defBLTvec [spicewish::vectors::create $plotNum [spicewish::vectors::defaultScale $plotNum]]
if {$defBLTvec == ""} {
puts "Error: plotting '[spicewish::vectors::defaultScale $plotNum]'"
return
}
set traceCnt 0
#loop for all vectors
foreach vectorName $args {
# don't allow duplicate traces
set BLTvec [spicewish::vectors::create $plotNum "$vectorName"]
if {$BLTvec == ""} {
puts "Error: plotting '$vectorName'"
continue
}
lappend view_traces($viewNum) $vectorName
$w.g element create "$vectorName" \
-xdata $defBLTvec \
-ydata $BLTvec \
-symbol "circle" \
-linewidth 2 \
-pixels 3 \
-color [trace_colour $traceCnt]
incr traceCnt
}
return $w
}
proc iplot { args } {
variable view_type
eval "set view_w [plot $args]"
# register callBack
if {[lsearch $spicewish::stepCallBack_list "spicewish::viewer::stepCallBack"] == -1} {
lappend spicewish::stepCallBack_list "spicewish::viewer::stepCallBack"
}
set viewNum [get_index $view_w]
set view_type($viewNum) "iplot"
}
proc get_index { name } {
variable view_w
foreach viewNum [array names view_w] {
if { $name == $view_w($viewNum) } {
return $viewNum
}
}
return -1
}
proc get_plotNum { plotTypeName } {
for {set i 0} {$i < [spicewish::vectors::avaliable_plots]} {incr i} {
if {[spicewish::vectors::plotTypeName $i] == $plotTypeName} {
return $i
}
}
return -1
}
proc axis_callback { widget value } {
return [ spicewish::eng::float_eng $value]
}
proc trace_colour { index } {
set def_colours "red blue orange green magenta brown"
if {$index < [llength $def_colours]} {
return [lindex $def_colours $index]
} else {
# creates a random colour
set randred [format "%03x" [expr {int (rand() * 4095)}]]
set randgreen [format "%03x" [expr {int (rand() * 4095)}]]
set randblue [format "%03x" [expr {int (rand() * 4095)}]]
return "#$randred$randgreen$randblue"
}
}
proc stepCallBack { } {
set currentPlotName [spicewish::vectors::plotTypeName 0]
#puts $::spice::steps_completed
# hack !!!
spicewish::vectors::create 0 "time"
spicewish::vectors::create 0 "a0_x1_y0"
}
proc Update { } {
variable view_traces
variable view_plotName
foreach index [names] {
set plotTypeName $view_plotName($index)
set plotNum [get_plotNum $plotTypeName]
# default scale
spicewish::vectors::create $plotNum [spicewish::vectors::defaultScale $plotNum]
foreach trace $view_traces($index) {
spicewish::vectors::create $plotNum $trace
}
}
}
proc Destroy { w } {
variable view_traces
variable view_w
foreach index [names ] {
if {[string match $view_w($index) $w]} {
unset view_w($index)
catch {unset view_traces($index)}
}
}
destroy $w
}
}
namespace eval vectors {
proc names { {plot 0} } {
# traces for current plot
return [spice::plot_variables $plot]
}
proc exists { plot name } {
set list [names $plot]
if {[lsearch -exact $list $name] != -1} {
return $name
} else {
return ""
}
}
proc validVectorList { plotNum args } {
set validList ""
foreach vector $args {
set vector [string tolower $vector]
if {[exists $plotNum $vector] != ""} {
lappend validList $vector
}
}
return $validList
}
proc plotTypeName { plot } {
# plot type eg tran1, dc1
return [spice::plot_typename $plot]
}
proc type { plot name } {
set spice_data [spice::spice_data $plot] ;# "{a0 voltage}{time time}"
set name [string tolower $name]
set spice_data [string tolower $spice_data]
foreach plot $spice_data {
set p_name [lindex $plot 0] ;# node name
set p_type [lindex $plot 1] ;# voltage/current/time
if {$p_name == $name} {
return $p_type
}
}
return ""
}
proc avaliable_plots { } {
set cnt 0
while {![catch { spice::plot_name $cnt }]} {
incr cnt
}
return $cnt
}
proc defaultScale { plot } {
return [spice::plot_defaultscale $plot]
}
proc create { plot name } {
# valid vector
if {[exists $plot $name] == ""} {return ""}
if { ![info exists "::[plotTypeName $plot]:v:${name}"]} {
set ::[plotTypeName $plot]:v:${name} "[blt::vector #auto]"
}
eval "set tmp \${::[plotTypeName $plot]:v:${name}}"
eval spice::plot_getvector $plot $name $tmp
return $tmp
}
}