2004-06-22 Stefan Jones <stefan.jones@multigig.com>

* configure.in:
	Change to version 17

2004-06-22 Adrian Dawe <adrian.dawe@multigig.com>

	* src/pkgIndex.tcl.in  src/spicepp.pl  src/spicepp.tcl  src/spicewish
	src/Makefile.am:
	Added spicepp integration and spicewish command line options.
This commit is contained in:
stefanjones 2004-06-22 19:41:34 +00:00
parent f798049285
commit dd677f2a10
7 changed files with 1147 additions and 49 deletions

View File

@ -1,3 +1,14 @@
2004-06-22 Stefan Jones <stefan.jones@multigig.com>
* configure.in:
Change to version 17
2004-06-22 Adrian Dawe <adrian.dawe@multigig.com>
* 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 <stefan.jones@multigig.com>
* include/ngspice.h:

View File

@ -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,

View File

@ -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)

View File

@ -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]

855
src/spicepp.pl Executable file
View File

@ -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 <john@reptechnic.com.au>
#
# 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 (<INCLUDE>) {
#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

170
src/spicepp.tcl Normal file
View File

@ -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
}
}

View File

@ -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