diff --git a/ChangeLog b/ChangeLog index 2a98512fb..cb483886a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2004-06-22 Stefan Jones + + * configure.in: + Change to version 17 + +2004-06-22 Adrian Dawe + + * src/pkgIndex.tcl.in src/spicepp.pl src/spicepp.tcl src/spicewish + src/Makefile.am: + Added spicepp integration and spicewish command line options. + 2004-06-10 Stefan Jones * include/ngspice.h: diff --git a/configure.in b/configure.in index 6f99611e2..aeee40ffb 100644 --- a/configure.in +++ b/configure.in @@ -5,7 +5,7 @@ dnl Create a configuration header AM_CONFIG_HEADER(config.h) dnl Initialize automake stuff -AM_INIT_AUTOMAKE(tclspice,0.2.16) +AM_INIT_AUTOMAKE(tclspice,0.2.17) dnl --enable-ftedebug : enable frontend debug macros AC_ARG_ENABLE(ftedebug, diff --git a/src/Makefile.am b/src/Makefile.am index 00ce1f32e..ab1952cf0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -195,6 +195,8 @@ install-tclspice: ${TCL_FILES} $(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 $(mkinstalldirs) $(DESTDIR)$(bindir) $(INSTALL_PROGRAM) spicewish $(DESTDIR)$(bindir) diff --git a/src/pkgIndex.tcl.in b/src/pkgIndex.tcl.in index cdb218fa4..21eb91c45 100755 --- a/src/pkgIndex.tcl.in +++ b/src/pkgIndex.tcl.in @@ -79,7 +79,8 @@ proc Loadspice { version dir } { spicewish::init_gui $spicefile } - set ::spice_version $version + set ::spice_version $version + set ::spice_library $dir } proc q { } { exit } @@ -88,8 +89,5 @@ proc q { } { exit } set version "%VERSION%" set libdir "%LIB_DIR%" -#set version "0.2.7" -#set libdir "/usr/local/lib" - package ifneeded spice $version [list Loadspice $version $libdir] diff --git a/src/spicepp.pl b/src/spicepp.pl new file mode 100755 index 000000000..7376b438f --- /dev/null +++ b/src/spicepp.pl @@ -0,0 +1,855 @@ +eval '(exit $?0)' && + eval 'exec perl -S $0 ${1+"$@"}' && + eval 'exec perl -S $0 $argv:q' + if 0; +# Copyright 2000 by John Sheahan +# +# This program is free software; you may redistribute and/or modify it under +# the terms of the GNU General Public License Version 2 as published by the +# Free Software Foundation. +# +# 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 complete details. +# + +$ver= '$Id$ '; + +require "getopts.pl"; +&Getopts("hi"); +(defined($opt_i)) ? ($interactive=1) : ($interactive=0) ; +if (defined($opt_h)) { + print "$0 $ver\n"; + print " -i interactive\n"; + + print " hspice-like spice preprocessor for berkeley spice\n"; + die; +} +# preprocess a hspice-like deck for berkeley spice 3f5 +# expand .lib .param .temp .global +# expoand .meas +# support $ for comment to lineend + +# $debug=1; + +# first pass read in deck, +# inline continuation lines, remove comment lines + +$_=<>;chop;@deck=" " . $_; # heading/comment line passthrough + +while (<>) { + chop; + # dont lowercase stuff in quotes + if (/^(.*)([\'\"])(.*)([\"\'])(.*)/) { + $_=lc($1) . $2 . $3 . $4 . lc($5); + } + else { $_ = lc($_); } + s/\$.*//; # no trailing comments; + s/^\*.*//; # no comments; + s/^\s*$//; # no bl lines + s/^\s+(.*)/$1/g; #get rid of blankspace + if (/^\s*\+(.*)/) { # continuation + $_ = pop (@deck) . " " . $1; + } + push @deck,$_ if (length($_) >0); +} + +while(&read_libs){} + +&read_parameters; +&eval_parameters; +&expand_parameters; + +&expand_eqns; + +#&remove_vbracket; + +&read_globals; +&expand_global_subs; +&expand_global_calls; + +&fix_temp; + +&expand_linearize; +&expand_meas; +&expand_control; + +&process_models; + +&printdeck; + + +if ($debug) { + foreach $param (sort keys %param) { + print STDERR "param $param = $param{$param}\n"; + } + foreach $global (sort keys %global) { + print STDERR " global $global\n"; + } +} + + + +################# subroutines ############# + +# include the specified model from the library. complain if not found. +sub libinclude { + local ($file,$model,$found,@lib ); + $file=$_[0]; + $model=$_[1]; + @lib=(); + open (INCLUDE, "$file") || die "include file $file cannot be opened"; + $found=0; + while () { + #get rid of blankspace, also allows you to optimise matches below + s/^\s+(.*)/$1/g; + last if ($found && /^\.endl/i); + if ($found) { + chop; + # dont lowercase stuff in quotes + if (/^(.*)([\'\"])(.*)([\"\'])(.*)/) { + $_=lc($1) . $2 . $3 . $4 . lc($5); + } + else { $_ = lc($_); } + s/\$.*//; # no trailing comments; + s/^\*.*//; # no comments; + s/\s\s+/ /g; # shrink multiple whitespaces + s/\s*=\s*/=/g; # compress around = + if (/^\+(.*)/) { # continuation + $_ = pop (@lib) . " " . $1; + } + push @lib,$_ if (length($_)>0); + } + if (/^\.lib\s+$model\b/i) { $found=1} + } + print STDERR "did not find model $model in library $file\n" unless ($found); + return @lib; +} +################## &read_libs ############ +sub read_libs { + local ($i,$file,$model,$flag); + $flag=0; + for ($i=0;$i<@deck;$i++) { + $_=$deck[$i]; + if (/^\.lib\s+[\'\"](.*)[\'\"]\s+(\S+)/i) { + $flag=1; + $file=$1; + $model=$2; + @lib=&libinclude($file,$model); + $_=@lib; + splice(@deck,$i,1,@lib); + } } + return $flag; +} + + +############## read globals ############ +# record a hash of all global signals. kill the line. +sub read_globals { + local ($i,$file,$model,@kill); + for ($i=0;$i<@deck;$i++) { + if ($deck[$i] =~ /^\.globals?\s+(.*)/) { + for (split(" ",$1)) { $global{$_}=1 } + push @kill,$i; + } + } + &zapdeck(@kill); +} + +############## kill line(s) in deck ################ +sub zapdeck { + # give it a list of lines to remove in increasing order!!! + while ($_=pop(@_)) { + splice @deck,$_,1; + } +} +######### parameters ################# +# find parameter define lines, remeber and delete them +sub read_parameters { + local ($i,$j,@kill); + for ($i=0;$i<@deck;$i++) { + if ($deck[$i] =~/^\.param\s+(.*)/) { # record param/value pairs +# print STDERR "found param line $1\n"; + push @kill,$i; + $_=$1; + $done=0; + until ($done) { + if (/^\s*(\S+)\s*\=\s*\'([^\']*)\'(.*)/) { +# print STDERR "PARAM $1 = $2\n"; + $param{$1}=&unit($2); + $_=$3; + } + elsif (/^\s*(\S+)\s*\=\s*(\S+)(.*)/) { +# print STDERR "PARAM(2) $1 = $2\n"; + $param{$1}=&unit($2); +# print STDERR "PARAM(2) $1 = $param{$1}\n"; + $_=$3; + } + else { $done=1; } + } +# @param=split(" ",$1); +# for ($j=0;$j<@param;$j++) { +# if ($param[$j] =~ /(\S*)\s*=\s*(\S+)/) { +# $param{$1}=$2; +# } +# else { +# print STDERR "parameter parse error in line $_\n"; + } } # } } + &zapdeck(@kill); +} +# replace parameters with defined string +# if the line starts with . - sub most places +# if not ., sub only last item +sub expand_parameters { + local ($i,$j,$nline,$skip,$tmptoken); + for ($i=0;$i<@deck;$i++) { + $skip=&skipnumber($deck[$i]); + if ($i==0) {$skip=0} # replace the first line +# print STDERR "skipping $skip fields $i $deck[$i]\n"; + $nline=''; + $_=$deck[$i]; + s/\(/ \( /g; # delimit openbracket + s/\)/ \) /g; # delimit closebracket + s/=/ = /g; + @_=split; + for ($j=0;$j<$skip;$j++) { + $nline .= $_[$j] . " "; + } + for ($j=$skip;$j<@_;$j++) { + $tmptoken = $_[$j]; + while (defined($param{$tmptoken})) { + $tmptoken = $param{$tmptoken}; + } + $nline .= $tmptoken . " "; +# print STDERR "first $_[$j]\n" if ($j == $skip); + + } +# print STDERR "$nline \n" if ($nline =~ /pwl/); + $_=$nline; + # undelimit openbracket for v(??) only + s/([vi]) \(\s*([^\)\s]*)\s*\)/$1\($2\)/g; + s/ = /=/g; # + s/ $//; # trailing space + $deck[$i]=$_; +} } + +################## add globals to subckt IO line ###################### + +# accept subroutine data +# add used global signals to the io defn line +# remember them in $globalsub{sub} to fix other instantiate lines +sub expand_global_subs { + local ($i,$subroutine,$extras,%nodes,$subckt); + for ($i=0;$i<@deck;$i++) { + $_=$deck[$i]; + if (defined($subckt)) { # remember nodes if in a subckt + @_=split; + for ($j=1;$j<@_-1;$j++) { # not first or last + $nodes{$_[$j]}++ if (defined($global{$_[$j]})); + } + } + if (/^\.subckt\s+(\S+)/) { + $subckt=$1; + $line=$i; + } + elsif (/^\.ends/) { + $extras=join(" ", keys %nodes); + $deck[$line] =~ s/^(.subckt.*)$/$1 $extras/; + $globalsub{$subckt}=$extras; + undef $subckt; +} } } + +sub expand_global_calls { + local ($i); + for ($i=0;$i<@deck;$i++) { + $_=$deck[$i]; + if (/^(x.*)\s+(\S+)\s*$/ && defined($globalsub{$2})) { + $deck[$i]="$1 $globalsub{$2} $2\n"; +} } } +############ measure #################### +# appears you have to linearize it all the first time +sub expand_linearize { + local ($i,@xtra,$first); + for ($i=0;$i<@deck;$i++) { + $_=$deck[$i]; + if (/^\.(tran|ac|dc)/) { + $analysis=$1; + } + elsif (/^\.measu?r?e?\s+(\S+)\s+trig(.*)/) { + $_=".meas $analysis $1 trig$2"; + $deck[$i]=$_; + # fill in the missing type so I don't have to think later + } + if (/^\.measu?r?e? (tran|dc|ac)\s+(\S+)\s+trig\s+(\S+) .* targ\s+(\S+) .*/) { + $first=$i unless (defined($first)); + $linearize{$3}=1; + $linearize{$4}=1; + } + } + if (defined($first)) { + push(@xtra, ".control"); + $list=join(" ", keys %linearize); + push(@xtra, "linearize time $list"); + push(@xtra, ".endc"); + splice(@deck,$first,0,@xtra); + @xtra=(); + } +} +sub expand_meas { + local ($i,@xtra); + for ($i=0;$i<@deck;$i++) { + $_=$deck[$i]; + if (/^\.measu?r?e? (tran|dc|ac)\s+(\S+)\s+trig\s+(\S+) .* targ\s+(\S+) .*/) { + $name=$2; + $trig=$3; + $targ=$4; + push(@xtra, ".control"); + push(@xtra, "print time $trig $targ > meas.data"); + push(@xtra, "echo $_ > meas.ctl"); + push(@xtra, "shell measure_spice $name"); + push(@xtra, ".endc"); + splice(@deck,$i,1,@xtra); + @xtra=(); + } + } +} +####################### control codes ############# +sub expand_control { + local ($i,@xtra); + for ($i=0;$i<@deck;$i++) { + $_=$deck[$i]; + if (/^\.(plot|print|tran|end)\b/) { + $cmd=$1; + s/^\.//; # drop initial dot + s/\stran\s/ /; + s/start=\S+//; # starts annow it + push(@xtra, ".control"); + if ($cmd eq "end") { + push(@xtra, "destroy all" ) unless ($interactive); + push(@xtra, "quit") unless ($interactive); + } + else { + push(@xtra, $_); + } + push(@xtra, ".endc"); + splice(@deck,$i,1,@xtra); + @xtra=(); + } + } +} + +################# translate model level ####### +# bsim3 (v3) +sub fix_temp { + for ($i=0;$i<@deck;$i++) { + if ($deck[$i] =~ /^\.tempe?r?a?t?u?r?e?\s*\=\s*(\S+)/) { + $deck[$i] = ".options temp=$1"; +} } } + +sub printdeck { + for (@deck) { + print &wrapline(70,"","+","\n"," ",split(" ",$_)); + } +} + +######################### wrapline ############### + +# wrapline( linelen, header, header2, trailer, +# separator, list ) +# where +# linelen max number of chars per line eg 70 +# header is a string stuck on to the beginning +# header2 is a string to add to the second and all subsequent lines, +# {if they exist] +# trailer is a string to tack on to the end of the structure +# separator is put between multiple elements in the list (NOT last) +# list +# returns string + +sub wrapline { + local($maxlen, $header, $header2, $trailer, + $separator, $linelen, @list, $output, $term, $last_term); + + ($maxlen, $header, $header2, $trailer, $separator, @list)=@_; + $output=$header; + $linelen=length($output); + $last_term=pop(@list); + foreach $term (@list) { + if ((length($term)+$linelen+length($separator))>$maxlen) { + $output .= "\n$header2" ; + $linelen = length($header2); + } + $output .= "$term$separator"; + $linelen += length($term)+ length($separator); + } + if ((length($last_term)+$linelen+length($trailer))>$maxlen) { + $output .= "\n$header2" ; + } + $output .= "$last_term$trailer"; +return $output; +} + +######################### equation support ######## +sub isnumber { + if (($_[0] =~ /\s*[\+\-]?[0-9\.]+\s*/) || + ($_[0] =~ /\s*[\+\-]?[0-9\.]+e[\+\-]?\d+/)) { + return 1; + } + else { + return 0; + } +} + +sub process { +# print STDERR "mapped $_[0] "; + local ($nline,$r,$i,$tmptoken); + $_=$_[0]; + s/([\(\)\*\/])/ $1 /g; # space delimit operators + s/([^eE])([\+\-])/$1 $2 /g; # special case for 1e-10 etc. + s/\^\^/ ^^ /g; # space delimit exponent +# print STDERR " delim $_ "; + @_=split; + for ($i=0;$i<@_;$i++) { + $tmptoken = $_[$i]; + while (defined($param{$tmptoken})) { + $tmptoken = $param{$tmptoken}; + } + $nline .= $tmptoken . " "; + } +# print STDERR " $nline " ; + $r=eval($nline); + print STDERR $@ if ($@ ne ''); +# print STDERR "to $r\n"; + return $r; +} + +# probably only one level deep.. +sub eval_parameters { + local ($val, $nval, $param); + for $param (keys %param) { +# print STDERR "checking param $param \n"; + $val=$param{$param}; + if ($val =~ /[\'\"](.*)[\'\"]/) { + $nval=&process($1); +# print STDERR "eval: processed $val to $nval\n"; + $param{$param}=$nval; + } + else { + $nval = &unit($val); +# print STDERR "eval: united $val to $nval\n"; + $param{$param}=$nval; + } + } +} +sub expand_eqns { + local ($i,$head,$tail,$p); + for ($i=0;$i<@deck;$i++) { + $_=$deck[$i]; + while (/(.*)\'([^\']*)\'(.*)/) { + $head=$1; $tail=$3; + $p=&process($2); + $_=$head . " $p " . $tail; + s/= /=/g; +# print STDERR "translated $deck[$i]\n to $_\n"; + } + $deck[$i]=$_; + } +} +# if it looks like v(pin) replace with pin +sub remove_vbracket { + local ($i,$nline); + for ($i=0;$i<@deck;$i++) { + $_=$deck[$i]; + $nline=''; + for (split) { + if (/^v\((.*)\)$/) { +# print STDERR "found vbracket $_ to $1 line $i\n"; + $_=$1; +} + $nline .= $_ . " "; + } + $deck[$i]=$nline; +} } + + + + +sub unit { + if ($_[0] =~ /^([0-9e\+\-\.]+)(t|g|meg|k|mil|m|u|n|p|f)?(v|a|s)?$/) { + if ($2 eq 't') { $mult = 1e12 } + elsif ($2 eq 'g') { $mult = 1e9 } + elsif ($2 eq 'meg') { $mult = 1e6 } + elsif ($2 eq 'k') { $mult = 1e3 } + elsif ($2 eq 'm') { $mult = 1e-3 } + elsif ($2 eq 'u') { $mult = 1e-6 } + elsif ($2 eq 'n') { $mult = 1e-9 } + elsif ($2 eq 'p') { $mult = 1e-12 } + elsif ($2 eq 'f') { $mult = 1e-15 } + elsif ($2 eq 'mil') { $mult = 25.4e-6} + else { $mult = 1 } +# print STDERR "unit $_[0] mult $mult val $1 letter $2 unit $3\n"; + return $1 * $mult; + } + return $_[0]; # maybe perl does it better?? +} + + +# when substituting parameters , skip this number from the start +sub skipnumber { + local ($in,$f); + $in=$_[0]; + $_=substr($in,0,1); # get the first char in the line + $f=$_; + # apart from the bjt - which I treat as a 3node device - all these + # have a fixed number of nodes + $n=tr/cdefghijklmoqrstuvw/2244442322443244424/; # element nodes +# print STDERR "first char $f matched $_ skip $_ \n"; + if ($n == 1) { + return $_+1; # add the name of the instance to the number of nodes + } + @_=split(" ",$in); + $_=@_; + if ($f eq 'x') { # only the last on ein a subckt call + return $_-1; + } + $first=shift(@_); + if ($first =~ /^\.meas/) { return 1 } # process this line + elsif ($first =~ /^\.tran/) { return 1 } # process this line + elsif ($first =~ /^\.lib/) { return 2 } # process last one + elsif ($first =~ /^\.model/) { return 3 } + else { return $_ } # skip the line +} + +################Fix all model incompatabilites ##################### + +sub process_models { + $spice_scale = 1; + + &change_models; + &change_instances; +} + +################# reads all avalible models #################### +######### Translate the hspice levels to ones for ng-spice ########## +# The wise men at multigig have decided the following: +#if level= 28 then use level=4 +# if level=49 or 53 +# Check BSIM version, +# if version = 3.2 use level 50 (3v3.2) +# if version = 3.1 use level 49 (3v3.1) +# if version = 4.2 use level 14 (4v2.1) +# else use level 8 +# (most up to date model, 3v3.2.2 at the moment) +# The parameters are also modified as follows: +#always remove the following: +#hspver, acm, calcasm, ldif, hdif, rd, rs, rsc, rdc (not used) +# Remove: NQSMOD if version >= 3v3.2 + +sub change_models { + local ($i,$model,@params,%default,$bin,$p); + %default = (acm => 0, calcacm => 0, lmlt => 1, wmlt => 1, xl => 0, + xw => 0, php => 1, ldif => 0, hdif => 0, lmin => 0, + lmax => 0, wmin => 0, wmax => 0, hspver => 0, + rd => 0, rs => 0, rsc => 0, rdc => 0, geo => 0, + tlev => 0, nlev => 0, n => 0, tt => 0, binflag => 1, + # There are from Bsim4.3 which ngspice does not have + stimod => -1, sa0 => 0, sb0 => 0, kvsat => 0, wlod => 0, + tku0 => 0, ku0 => 0, llodku0 => 0, wlodku0 => 0, lku0 => 0, wku0 => 0, + pku0 => 0, kvth0 => 0, stk2 => 0, steta0 => 0, lkvth0 => 0, wkvth0 => 0, + pkvth0 => 0, llodvth => 0, wlodvth => 0, lodk2 => 1, lodeta0 => 1); + for ($i=0;$i<@deck;$i++) { + $_ = $deck[$i]; + if(/^\.model\s+(\S+)\s+[np]mos/) { +# print STDERR "got ".$_."\n"; + + split /\./, $1 ; + $model = $_[0]; + $bin = $1; + push @$model, $bin; + + #First read level and version info and set the level + #and put acm to default value + + $_ = $deck[$i]; + /\blevel\s*=\s*([0-9]+)\b/; + + if($1 == 49){ + $$bin{"acm"} = 0;} + elsif($1 == 53){ + $$bin{"acm"} = 10;} + else{ + $$bin{"acm"} = 0; } + + if(/\bversion\s*=\s*([0-9\.]+)\b/){ + + if($1 eq "3.2"){ + $deck[$i] =~ s/\blevel\s*=\s*[0-9]+\b/level=50/; + $deck[$i] =~ s/(.*)\bnqsmod\s*=\s*\S*\b(.*)/$1$2/;} + + elsif($1 eq "3.1"){ + $deck[$i] =~ s/\blevel\s*=\s*[0-9]+\b/level=49/;} + + elsif($1 eq "4.2"){ + $deck[$i] =~ s/\blevel\s*=\s*[0-9]+\b/level=14/; + $deck[$i] =~ s/\bversion\s*=\s*[0-9\.]+\b/version=4.2.1/; + } else { + $deck[$i] =~ s/\blevel\s*=\s*[0-9]+\b/level=8/; + $deck[$i] =~ s/(.*)\bnqsmod\s*=\s*\S*\b(.*)/$1$2/;} + } + + + # get rid of hspice only variables, and store them + foreach $p (keys %default){ + if ($p ne "acm"){ + $$bin{$p} = $default{$p};} + } + $newline = ""; + @list = split /[\s\(\)]/,$deck[$i]; + # zap ".model name type" + $newline = shift @list; + $newline .= " " . shift @list; + $newline .= " " . shift @list; + foreach $item (@list) { + ($name,$value) = split /=/,$item; + if(exists($default{$name})){ + $$bin{$name} = $value; + } else { + $newline .= " " . $item; + } + } + $deck[$i] = $newline; + + #translate some vars + $deck[$i] =~ s/(\b)tref(\s*=)/${1}tnom$2/; + + #HDIFeff needed not HDIF + $$bin{"hdif"} *= $$bin{"wmlt"}; + } +#end if mosfet model + if (/\.opt\S*\s+scale=(\S+)/) { + $spice_scale = &unit($1); + } + } +#end for +} +#end sub + +#### selects the correct model and does l, w scaling and acm##### + + +sub change_instances { + local($i,$model,$l,$w,$p,$m,$ad,$as,$pd,$ps); + for ($i=0;$i<@deck;$i++) { + $_ = $deck[$i]; + if(/^m\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+(\S+)/) { + + #print STDERR "processing ".$_."\n"; + $model = $1; + + + #Model binning + if(/.*\bl\s*=\s*(\S+)\b.*/){ + $l = &unit($1); + $l *= $spice_scale; + }else {print STDERR "no l found\n";die;} + + if(/.*\bw\s*=\s*(\S+)\b.*/){ + $w = &unit($1); + $w *= $spice_scale; + }else {print STDERR "no w found\n";die;} + + #Loops though all bins and selects one + foreach $p (@$model){ + if( + $l <= $$p{'lmax'} && + $l >= $$p{'lmin'} && + $w <= $$p{'wmax'} && + $w >= $$p{'wmin'} + ){ + $deck[$i] =~ + s/(^\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+)(\S+)/$1$p/; + $model = $p; + goto model_found; + } + } + print STDERR "Model or bin not found for model=$model, w=$w, l=$l\n"; + next; + model_found: + #W & L mangling and acm stuff + #Get m + if($deck[$i] =~ s/(.*)\bm\s*=\s*([0-9]+)\b(.*)/$1$3/){ + $m = $2;}else {$m = 1;} + + #get and remove pd, ps, ad, as + if($deck[$i] =~ s/(.*)\bpd\s*=\s*(\S+)\b(.*)/$1$3/){ + $pd = &unit($2); + $pd *= $spice_scale; + }else{ $pd = 0;} + + if($deck[$i] =~ s/(.*)\bps\s*=\s*(\S+)\b(.*)/$1$3/){ + $ps = &unit($2); + $ps *= $spice_scale; + }else{ $ps = 0;} + + if($deck[$i] =~ s/(.*)\bas\s*=\s*(\S+)\b(.*)/$1$3/){ + $as = &unit($2); + $as *= $spice_scale * $spice_scale; + }else{ $as = 0;} + + if($deck[$i] =~ s/(.*)\bad\s*=\s*(\S+)\b(.*)/$1$3/){ + $ad = &unit($2); + $ad *= $spice_scale * $spice_scale; + }else{ $ad = 0;} + + #Calculate Leff and Weff + $w = $m * ($w * $$model{"wmlt"} + $$model{"xw"}); + $l = $l * $$model{"lmlt"} + $$model{"xl"}; + + #Now do the ACM stuff...... + #gathered from Hspice manual release 1999.4 December 1999 + + #Now the perimeters and areas + #calculated using hspice methods.. + if( $$model{"acm"} == 0){ + #pg 869, 19-33 + $ad = $m * $ad * $$model{"wmlt"} * $$model{"wmlt"}; + $as = $m * $as * $$model{"wmlt"} * $$model{"wmlt"}; + $pd = $m * $pd * $$model{"wmlt"}; + $ps = $m * $ps * $$model{"wmlt"}; + + } + + elsif( $$model{"acm"} == 1){ + #pg 871, 19-35 + $as = $ad = $$model{"wmlt"} * $w; + $ps = $pd = $w; + } + + elsif( $$model{"acm"} == 2){ + #pg 874, 19-39 + if($ad){ + $ad = $ad * $m * $$model{"wmlt"} * $$model{"wmlt"}; + }else { + $ad = 2 * $$model{"hdif"} * $w;} + + if($as){ + $as = $m * $as * $$model{"wmlt"} * $$model{"wmlt"}; + }else{ + $as = 2 * $$model{"hdif"} * $w;} + + if($pd){ + $pd = $m * $pd * $$model{"wmlt"}; + }else { + $pd = 4 * $$model{"hdif"} + 2 * $w; + } + + if($ps){ + $ps = $m * $ps * $$model{"wmlt"}; + }else{ + $ps = 4 * $$model{"hdif"} + 2 * $w; + } + } + + elsif( $$model{"acm"} == 3){ + #pg 879, 19-43 + if($ad){ + $ad = $m * $ad * $$model{"wmlt"} * $$model{"wmlt"};} + elsif($$model{"geo"} == 0 || $$model{"geo"} == 2){ + $ad = 2 * $$model{"hdif"} * $w; + }else{ + $ad = $$model{"hdif"} * $w; + } + + if($as){ + $as = $m * $as * $$model{"wmlt"} * $$model{"wmlt"};} + elsif($$model{"geo"} == 0 || $$model{"geo"} == 1){ + $as = 2 * $$model{"hdif"} * $w; + }else{ + $as = $$model{"hdif"} * $w; + } + + if($pd){ + $pd = $m * $pd * $$model{"wmlt"};} + elsif($$model{"geo"} == 0 || $$model{"geo"} == 2){ + $pd = 4 * $$model{"hdif"} + $w; + }else{ + $pd = 2 * $$model{"hdif"}; + } + + if($ps){ + $ps = $m * $ps * $$model{"wmlt"};} + elsif($$model{"geo"} == 0 || $$model{"geo"} == 1){ + $ps = 4 * $$model{"hdif"} + $w; + }else{ + $ps = 2 * $$model{"hdif"}; + } + } + + #following acm's from pg 1198, 26-246 + + elsif( $$model{"acm"} == 10){ + + } + + elsif( $$model{"acm"} == 11){ + + } + + elsif( $$model{"acm"} == 12 ){ + if( $$model{"calcacm"} == 1 ) { + if($ad){ + $ad = $ad * $$model{"wmlt"} * $$model{"wmlt"}; + }else{ + $ad = 2 * $$model{"hdif"} * $w; + } + + if($as){ + $as = $as * $$model{"wmlt"} * $$model{"wmlt"}; + }else{ + $as = 2 * $$model{"hdif"} * $w; + } + + if($ps){ + $ps = $ps * $$model{"wmlt"}; + }else{ + $ps = 4 * $$model{"hdif"} + 2 * $w; + } + + if($pd){ + $pd = $pd * $$model{"wmlt"}; + }else{ + $pd = 4 * $$model{"hdif"} + 2 * $w; + } + } + } + + elsif( $$model{"acm"} == 13){ + + } + else { + print STDERR "Bad acm number in ".$model." ".$acm."\n"; + die; + } + + #Now write the values into the file...... + $l /= $spice_scale; + $w /= $spice_scale; + + $pd /= $spice_scale; + $ps /= $spice_scale; + + $as /= $spice_scale * $spice_scale; + $ad /= $spice_scale * $spice_scale; + + $deck[$i] =~ s/\b(l\s*=\s*)(\S+)\b/$1$l/; + + $deck[$i] =~ s/\b(w\s*=\s*)(\S+)\b/$1$w/; + + $deck[$i] .= " pd=".$pd." ps=".$ps." as=".$as." ad=".$ad; + } + #end if mosfet + } + #end for +} +#end of sub diff --git a/src/spicepp.tcl b/src/spicepp.tcl new file mode 100644 index 000000000..8ddee2384 --- /dev/null +++ b/src/spicepp.tcl @@ -0,0 +1,170 @@ +# Script to run Spicepp from Tcl +# 07/06/04 - ad +# +# version 0.1.2 +# +# note: +# edit variable spicepp_dir to for dir contaning spicepp code +# spicepp avaliable at http://sourceforge.net/projects/tclspice +# + +namespace eval spicepp { + + variable version 0.1.3 + variable spicepp_dir $::spice_library/spice + variable outFile_extension ".pp-cir" + variable timeStamp_prefix "* spicepp:" + variable Hspice_structures ".meas .param .lib .globals" + + #------------------------------------------------------ + # + # returns + # - empty string if an error + # - name of passed file if in spice3f5 syntax + # - or name of translated file + # + proc convert { fileName } { + + variable outFile_extension + variable spicepp_dir + + if {![file exists $fileName]} { + puts "spicepp: '${fileName}' dosen't exist" + return "" + } + + # output fileName + set strLen [expr [string length $fileName] - [string length [file extension $fileName]] -1] + set outFile [string range $fileName 0 $strLen] + set outFile "${outFile}${outFile_extension}" + + # check that input file contains Hspice statements + if {![checkHSpiceFormat $fileName]} { + puts "spicepp: '$fileName' doesn't contain any Hspice statements." + return $fileName + } + + # check modified time stamp in previous generated output files + # against modified time of input file + if {![checkTimeStamp $fileName $outFile]} { + puts "spicepp: '$fileName' hasn't been modified since last translation" + return $outFile + } + + # conversion Start Time + set sTime [clock seconds] + + # execute Spicepp on given file + puts "spicepp: converting '$fileName' > '$outFile'" + + if {[catch {exec ${spicepp_dir}/spicepp.pl $fileName > $outFile} errResult]} { + puts "spicepp couldn't convert '$fileName'" + puts $errResult + return "" + } + + # add time stamp to output file + addTimeStamp $fileName $outFile + + # Conversion Time + #puts "Spicepp : [expr [clock seconds] - $sTime]s" + + return $outFile + } + + #----------------------------------------- + # adds the modified time of orginal spice deck to second line + # of output file + # + proc addTimeStamp { inpFile outFile } { + + variable timeStamp_prefix + + set mTime [file mtime $inpFile] + + set fileId [open $outFile "r"] + set fileData [read $fileId] + set fileData [split $fileData "\n"] + close $fileId + + set fileId [open $outFile "w"] + set lineCnt 0 + + foreach lineData $fileData { + + if {$lineCnt ==1} { + puts $fileId "$timeStamp_prefix $mTime" + } + puts $fileId $lineData + incr lineCnt + } + close $fileId + } + + #------------------------------------------- + # Checks modified time stamp in output file against + # modified time of input file + proc checkTimeStamp { inpFile outFile } { + + variable timeStamp_prefix + + # check output file exists + if {![file exists $outFile]} { + return 1 + } + + set fileId [open $outFile "r"] + set fileData [read $fileId] + set fileData [split $fileData "\n"] + close $fileId + + set timeStampData [lindex $fileData 1] + set timeStampData [string range $timeStampData [string length $timeStamp_prefix] end] + # remove spaces + eval set timeStampData \"$timeStampData\" + + if {![string is integer $timeStampData]} { + return 1 + } + + # compare modified times + if { $timeStampData == [file mtime $inpFile] } { + return 0 + } + return 1 + } + + #--------------------------------------------- + # parses the spice file for any Hspice stuctures + # .meas .lib etc + proc checkHSpiceFormat { fileName } { + + variable Hspice_structures + + set fileId [open $fileName "r"] + set fileData [read $fileId] + set fileData [split $fileData "\n"] + close $fileId + + for {set lineCnt 0} {$lineCnt < [llength $fileData]} {incr lineCnt} { + set line [lindex $fileData $lineCnt] + + # get line structre + set line [string range $line 0 [expr [string first " " $line] -1]] + + # lowercase + set line [string tolower $line] + + # skip line if '*' as lsearch returns a 0 + if {$line == "*"} { continue } + + # check line structure against Hspice list + if {[lsearch $Hspice_structures $line] != -1} { + # puts "match found line $lineCnt : '$line'" + return 1 + } + } + return 0 + } + +} diff --git a/src/spicewish b/src/spicewish index c9a3fedfc..9a4dcfb9a 100755 --- a/src/spicewish +++ b/src/spicewish @@ -1,52 +1,114 @@ -#!/bin/sh +# +# 22/6/04 -ad # the next line starts SpiceWish interactively with the given file name \ exec wish "$0" "$@" + +namespace eval spicewish { + + # load tclspice + package require spice + + proc 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 } { + + # 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]} { + + 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 + + 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 } + default { usage } + } + } elseif {[file isfile $arg]} { + set fileName $arg + } + } + + if {$fileName == "" } { usage } + + 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 . + } + } + } + + proc tclreadline { } { + # readline with prompt set to "tclspice > " + if {$::tcl_interactive} { + package require tclreadline + proc ::tclreadline::prompt1 { } { return "tclspice > " } + ::tclreadline::Loop + } + } +} + set ::tcl_interactive 1 -if { [ llength $argv ] != 1 } { - puts " usage: spicewish filename.cir \n" - wm withdraw . - exit -} else { - set fileName $argv -} - - -# check file exists -if {![ file exists $fileName ]} { - puts " file '${fileName}' doesn't exists \n" - exit -} - -if { [ file extension $fileName ] == ".tcl" } { - source $fileName - -} else { - package require spice - spice_init_gui $fileName - - # import the spice namespace - namespace import spice::* - - # import the spicewish namespace - namespace import -force spicewish::* - - set ::spicewish_mode "standalone" -} - - - - -# readline with prompt set to "tclspice > " -if {$tcl_interactive} { - package require tclreadline - proc ::tclreadline::prompt1 { } { return "tclspice > " } - - - ::tclreadline::Loop -} - +spicewish::load $argv $argc +namespace import -force spice::* +namespace import -force spicewish::* +spicewish::tclreadline