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

* src/measure_spice.pl src/spicepp.pl.in Makefile.am:
	Add .measure statement support

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

	* src/spicewish:
	Added -cm switch to load a codemodel
This commit is contained in:
stefanjones 2004-06-22 23:13:45 +00:00
parent 34d93b64c9
commit a066cb7006
6 changed files with 1071 additions and 6 deletions

View File

@ -7,7 +7,11 @@
include <errno.h> for Tcl version of perror
* src/main.c:
Add dummy INPretrieve() symbol so ngnutmeg and co link with xspice enabled
Add dummy INPretrieve() symbol so ngnutmeg and co link with
xspice enabled.
* src/measure_spice.pl src/spicepp.pl.in Makefile.am:
Add .measure statement support
2004-06-22 Adrian Dawe <adrian.dawe@multigig.com>
@ -15,6 +19,9 @@
src/Makefile.am:
Added spicepp integration and spicewish command line options.
* src/spicewish:
Added -cm switch to load a codemodel
2004-06-10 Stefan Jones <stefan.jones@multigig.com>
* include/ngspice.h:

View File

@ -171,14 +171,20 @@ TCL_PKG_PATH = @TCL_PACKAGE_PATH@
TCLSPICE_VERSION = @VERSION@
TCL_FILES = libspice.so pkgIndex.tcl
TCL_FILES = libspice.so pkgIndex.tcl measure_spice.pl spicepp.pl \
tclspice_source.tcl spicepp.tcl
LIBSPICE_OBJS = tclspice.o
pkgIndex.tcl: pkgIndex.tcl.in
rm -f pkgIndex.tcl
sed -e 's;%LIB_DIR%;$(TCL_PKG_PATH);g' pkgIndex.tcl.in | \
sed -e 's;%VERSION%;$(TCLSPICE_VERSION);g' > pkgIndex.tcl
rm -f $@
sed -e 's;%LIB_DIR%;$(TCL_PKG_PATH);g' $< | \
sed -e 's;%VERSION%;$(TCLSPICE_VERSION);g' > $@
spicepp.pl: spicepp.pl.in
rm -f $@
sed -e 's;%LIB_DIR%;$(TCL_PKG_PATH);g' $< | \
sed -e 's;%VERSION%;$(TCLSPICE_VERSION);g' > $@
install-tcl: install-tcl-recursive install-tclspice install-data-am
@ -197,6 +203,7 @@ install-tclspice: ${TCL_FILES}
$(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
$(mkinstalldirs) $(DESTDIR)$(bindir)
$(INSTALL_PROGRAM) spicewish $(DESTDIR)$(bindir)

184
src/measure_spice.pl Executable file
View File

@ -0,0 +1,184 @@
eval '(exit $?0)' &&
eval 'exec perl -S $0 ${1+"$@"}' &&
eval 'exec perl -S $0 $argv:q'
if 0;
# $Id$
# 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.
#
# measure utility for spice
# call to 'meas' inserted into spicedeck by spicepp
# preceeded by write of a data and control file
# copyright John Sheahan taudelta@integritynet.com.au
$ver='$id$ ';
# data is in meas.data in pwd
############# control information if=nfo is in meas.ctl
# output is appended to spice.log
$data="meas.data";
$ctl="meas.ctl";
$log="spice.log";
$date = `date`; chop($date);
$user=$ENV{"USER"};
open CTL, $ctl || die "cannot open control file $ctl";
$_=<CTL>;
if (/^\.measu?r?e? (tran|dc|ac) (\S+) trig\s+(\S+) (.*)targ\s+(\S+)(.*)/) {
$name=$2;
$trig=$3;
$trigstr=$4;
$targ=$5;
$targstr=$6;
if ($trigstr =~ /\bval=(\S+)/) { $trigval=&unit($1)}
else { print "ERROR: no trigger value\n"; }
if ($trigstr =~ /\b(fall|rise|count)=(\S+)/) {$trigdrn=$1 ; $trigcount=&unit($2); }
else { print "ERROR: no trigger rise/ fall / cross\n"; }
if ($trigstr =~ /\btd=(\S+)/) {$trigdelay=&unit($1)}
else {$trigdelay=0}
if ($targstr =~ /\bval=(\S+)/) { $targval=&unit($1)}
else { print "ERROR: no target value\n"; }
if ($targstr =~ /\b(fall|rise|count)=(\S+)/) {$targdrn=$1 ; $targcount=&unit($2); }
else { print "ERROR: no target rise/ fall / cross\n"; }
if ($targstr =~ /\btd=(\S+)/) {$targdelay=&unit($1)}
else {$targdelay=0}
/^\.measu?r?e? (tran|dc|ac) \S+ (.*)/; $remainder=$2;
}
else {
print "ERROR cannot parse command line\n";
}
close CTL;
#($name, $a_level,$a_drn,$b_level,$b_drn)=split;
open DATA, $data || die "cannot open data file $data";
$title=<DATA>;
while (<DATA>) {
if (/^(\d+)\s+([0-9\.e\+\-]+)\s+([0-9\.e\+\-]+)\s+([0-9\.e\+\-]+)/) {
$time[$1]=$2;
$a[$1]=$3;
$b[$1]=$4;
}
}
close DATA;
$first=&get_index($trigdelay,@time);
if ($trigdrn eq 'rise') {
$aindex = &get_rise($trigval,$trigcount,$first,@a);
}
elsif ($trigdrn eq 'fall') {
$aindex = &get_fall($trigval,$trigcount,$first,@a);
}
else {
$aindex = &get_cross($trigval,$trigcount,$first,@a);
}
$atime = &get_time($aindex,@time);
#print "atime=$atime aindex=$aindex first=$first tdelay=$trigdelay count=$trigcount drn=$trigdrn\n";
$first=&get_index($targdelay,@time);
if ($targdrn eq 'rise') {
$bindex = &get_rise($targval,$targcount,$first,@b);
}
elsif ($targdrn eq 'fall') {
$bindex = &get_fall($targval,$targcount,$first,@b);
}
else {
$bindex = &get_cross($targval,$targcount,$first,@b);
}
$btime = &get_time($bindex,@time);
#print "btime=$btime bindex=$bindex\n";
#print "btime=$btime bindex=$bindex first=$first tdelay=$targdelay count=$targcount drn=$targdrn\n";
$ans=$btime-$atime;
open LOG, ">>$log" || die "cannot open logfile $log";
print LOG "$title";
print LOG "$user $date\n";
print LOG "$name=$ans trig=$atime targ=$btime $remainder\n\n";
print "$user $date\n";
print "$name=$ans trig=$atime targ=$btime $remainder\n";
close LOG;
################### get_fall ################
sub get_fall {
$level=shift(@_);
$count=shift(@_);
$first=shift(@_);
for ($i=$first;$i<@_-1;$i++) {
if (($_[$i] >= $level) && ($_[$i+1] < $level)) {
$count--;
return $i + (($_[$i]-$level)/($_[$i]-$_[$i+1])) if ($count==0);
}
}
return undef;
}
sub get_rise {
$level=shift(@_);
$count=shift(@_);
$first=shift(@_);
for ($i=$first;$i<@_-1;$i++) {
if (($_[$i] <= $level) && ($_[$i+1] > $level)) {
$count--;
return $i + (($level-$_[$i])/($_[$i+1]-$_[$i])) if ($count==0);
}
}
return undef;
}
sub get_cross {
$level=shift(@_);
$count=shift(@_);
$first=shift(@_);
for ($i=$first;$i<@_-1;$i++) {
if ((($_[$i] <= $level) && ($_[$i+1] > $level)) ||
(($_[$i] >= $level) && ($_[$i+1] < $level))) {
$count--;
return $i + (($level-$_[$i])/($_[$i+1]-$_[$i])) if ($count==0);
}
}
return undef;
}
# return the interpolated time corresponding to the (fractional) index
sub get_time {
$level=shift(@_);
$index=int($level);
$time = $_[$index] + (($_[$index+1] - $_[$index]) * ($level-$index));
return $time;
}
# return the first index at or after the given time
sub get_index {
$time=shift(@_);
for ($i=0;$i<@_-1;$i++) {
return $i if ($_[$i] >= $time)
}
return undef;
}
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 }
return $1 * $mult;
}
else {
print "ERROR: cannot result value $_[0]\n";
}
return $_; # maybe perl does it better??
}

View File

@ -307,7 +307,7 @@ sub expand_meas {
push(@xtra, ".control");
push(@xtra, "print time $trig $targ > meas.data");
push(@xtra, "echo $_ > meas.ctl");
push(@xtra, "shell measure_spice $name");
push(@xtra, "shell /usr/lib/spice/measure_spice.pl $name");
push(@xtra, ".endc");
splice(@deck,$i,1,@xtra);
@xtra=();

855
src/spicepp.pl.in 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 %LIB_DIR%/spice/measure_spice.pl $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

View File

@ -14,6 +14,7 @@ namespace eval spicewish {
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
@ -52,6 +53,7 @@ namespace eval spicewish {
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]
@ -69,6 +71,10 @@ namespace eval spicewish {
-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]} {
@ -78,6 +84,12 @@ namespace eval spicewish {
if {$fileName == "" } { usage }
if {[llength $codeModels] != 0} {
foreach codeModel $codeModels {
spice::codemodel $codeModel
}
}
if {$mode_spicePP} {
set fileName [ run_spicepp $fileName ]
}