CMC test suite

This commit is contained in:
dwarning
2011-05-28 19:08:04 +00:00
parent aeef26ecd4
commit c96b5afd18
79 changed files with 21325 additions and 0 deletions
+247
View File
@@ -0,0 +1,247 @@
#!/bin/sh
eval 'exec perl -S -x -w $0 ${1+"$@"}'
#!perl
#
# compareSimulationResults.pl: program to do a toleranced comparison of compact model simulation results
#
# Rel Date Who Comments
# ==== ========== ============= ========
# 1.0 04/13/06 Colin McAndrew Initial version
#
sub usage() {
print "
$prog: compare simulation results between two files
Usage: $prog [options] refFile simFile
Files:
refFile reference results file
simFile simulated results file
Options:
-c CLIP match numbers n1 and n2 with abs(n1)<CLIP and abs(n2)<CLIP
-n NDIGIT match numbers n1 and n2 if they are within 1 of the NDIGITth digit
-r REL match any numbers n1 and n2 with abs(n1-n2)/(0.5*(abs(n1)+abs(n2)+abs(n1-n2)))<REL
-d debug mode
-h print this help message
-i print info on file formats and structure
-v verbose mode
";
} # End of usage
sub info() {
print "
This program numerically compares simulation results and returns a string
that indicates the result of the comparision. Possible return values are:
ERROR: cannot open file nameOfFile
FAIL (probably from some simulation failure)
FAIL (simulation output quantities differ)
FAIL (number of results is different)
FAIL (non-numeric results)
DIFFER (max rel error is relErr)
MATCH (within specified tolerances)
MATCH (exact)
It is expected that each file is a columnar list of simulation results,
with the first line being a title line that indicates column contents,
and every other line being numerical simulation results.
The comparisons are done in the order
exact
clip
nDigits
relErr
and passing one test means the results are considered to be the same.
Different tolerancing should be used for different types of simulations,
and because of this mixing different types of simulation results in
the one file is not recommended. Reasonable tolerances are:
Quantity Clip nDigtis relTol
DC current 1.0e-13 6 1.0e-06
AC conductance 1.0e-20 6 1.0e-06
capacitance 1.0e-20 6 1.0e-06
noise 1.0e-30 5 1.0e-05
Note that these numbers should be adjusted based on the precision
to which the numbers to be compared are printed.
If no tolerances are specified, then the refFile name must contain
one of the strings \"Dc\", \"Ac\", or \"Noise\" and the above values
are used as default tolerances.
The UNIX utilities spiff and ndiff so toleranced numerical comparisons,
but they seem not generally available now. Hence this simplified
numerical comparison program is provided for verifying simulation
results against reference test results. Also, clipping and comparison
to a certain number of digits are more relevant for comparison of
numbers printed in output (as compared to results held in memory),
and these are not considered in other toleranced numerical comparisons.
";
} # End of info
#
# Set program names and variables
#
$\="\n";
$,=" ";
$debug=0;
$verbose=0;
@prog=split("/",$0);
$prog=$prog[$#prog];
$number='[+-]?\d+[\.]?\d*[eE][+-]?\d+|[+-]?[\.]\d+[eE][+-]?\d+|[+-]?\d+[\.]?\d*|[+-]?[\.]\d+';
for (;;) {
if (!defined($ARGV[0])) {
last;
} elsif ($ARGV[0] =~ /^-c/i) {
shift(@ARGV);
die("ERROR: no clip value specified for -c option, stopped") if ($#ARGV < 0);
$clip=$ARGV[0];
die("ERROR: clip must be a positive number, stopped") if ($clip !~ /^$number$/ || $clip <= 0);
} elsif ($ARGV[0] =~ /^-n/i) {
shift(@ARGV);
die("ERROR: no number of digits value specified for -n option, stopped") if ($#ARGV < 0);
$nDigits=$ARGV[0];
die("ERROR: nDigits must be a positive integer, stopped") if ($nDigits !~ /^[1-9][0-9]*$/);
} elsif ($ARGV[0] =~ /^-r/i) {
shift(@ARGV);
die("ERROR: no relTol value specified for -r option, stopped") if ($#ARGV < 0);
$relTol=$ARGV[0];
die("ERROR: relTol must be a positive number, stopped") if ($relTol !~ /^$number$/ || $relTol <= 0);
} elsif ($ARGV[0] =~ /^-d/i) {
$debug=1;$verbose=1;
} elsif ($ARGV[0] =~ /^-h/i) {
&usage();exit(0);
} elsif ($ARGV[0] =~ /^-i/i) {
&usage();&info();exit(0);
} elsif ($ARGV[0] =~ /^-v/i) {
$verbose=1;
} elsif ($ARGV[0] =~ /^-/) {
&usage();
die("ERROR: unknown flag $ARGV[0], stopped");
} else {
last;
}
shift(@ARGV);
}
if ($#ARGV<1) {
&usage();exit(0);
}
if (!defined($clip)) {
if ($ARGV[0] =~ /Dc/i) {
$clip=1.0e-13;
} elsif ($ARGV[0] =~ /Ac/i) {
$clip=1.0e-20;
} elsif ($ARGV[0] =~ /Noise/i) {
$clip=1.0e-30;
} else {
die("ERROR: must specify -c CLIP value if file is not Dc, Ac, or noise, stopped");
}
}
if (!defined($relTol)) {
if ($ARGV[0] =~ /Dc/i) {
$relTol=1.0e-6;
} elsif ($ARGV[0] =~ /Ac/i) {
$relTol=1.0e-6;
} elsif ($ARGV[0] =~ /Noise/i) {
$relTol=1.0e-5;
} else {
die("ERROR: must specify -r RELTOL value if file is not Dc, Ac, or noise, stopped");
}
}
if (!defined($nDigits)) {
if ($ARGV[0] =~ /Dc/i) {
$nDigits=6;
} elsif ($ARGV[0] =~ /Ac/i) {
$nDigits=6;
} elsif ($ARGV[0] =~ /Noise/i) {
$nDigits=5;
} else {
die("ERROR: must specify -n NDIGITS value if file is not Dc, Ac, or noise, stopped");
}
}
if ($ARGV[0] =~ /reference/i) {
$reference="reference";
} else {
($reference=$ARGV[0])=~s/^.*\.//;
}
($variant=$ARGV[1])=~s/^.*\.//;
$result=&compareResults($ARGV[0],$ARGV[1],$clip,$nDigits,$relTol);
printf(" variant: %-20s(compared to: %-9s) %s\n",$variant,$reference,$result);
sub compareResults {
use strict;
my($refFile,$simFile,$clip,$nDigits,$relTol)=@_;
my(@Ref,@Sim,$i,$j,$relErr,$maxRelErr,$absErr,$maxAbsErr);
my(@RefRes,@SimRes,$matchType,$mag,$lo,$hi);
return("ERROR: cannot open file $refFile") if (!open(IF,"$refFile"));
while (<IF>) {
s/\s+$//;
push(@Ref,$_);
}
close(IF);
return("ERROR: cannot open file $simFile") if (!open(IF,"$simFile"));
while (<IF>) {
s/\s+$//;
push(@Sim,$_);
}
close(IF);
return("FAIL (probably from some simulation failure)") if ($#Ref != $#Sim || $#Sim<1);
return("FAIL (simulation output quantities differ)") if ($Ref[0] ne $Sim[0]);
$maxAbsErr=0;$maxRelErr=0;$matchType=0;
for ($j=1;$j<=$#Ref;++$j) {
@RefRes=split(/\s+/,$Ref[$j]);
@SimRes=split(/\s+/,$Sim[$j]);
return("FAIL (number of quantities simulated are different") if ($#RefRes != $#SimRes);
for ($i=1;$i<=$#RefRes;++$i) { # ignore first column, this is the sweep variable
if ($RefRes[$i] !~ /^$main::number$/ || $SimRes[$i] !~ /^$main::number$/) {
return("FAIL (non-numeric results");
}
next if ($RefRes[$i] == $SimRes[$i]);
$matchType=1 if ($matchType<1);
#next if (abs($RefRes[$i]) < $clip && abs($SimRes[$i]) < $clip);
next if (abs($RefRes[$i]) < $clip || abs($SimRes[$i]) < $clip);
if ($RefRes[$i]*$SimRes[$i] <= 0.0) {
$matchType=2 if ($matchType<2);
$absErr=abs($RefRes[$i]-$SimRes[$i]);
$relErr=$absErr/(0.5*(abs($RefRes[$i])+abs($SimRes[$i])+$absErr));
$maxRelErr=$relErr if ($relErr > $maxRelErr);
if ($main::verbose) {print STDERR $RefRes[$i],$SimRes[$i],100*$relErr."\%"}
next;
}
$lo=abs($RefRes[$i]);
if (abs($SimRes[$i]) < $lo) {
$hi=$lo;
$lo=abs($SimRes[$i]);
} else {
$hi=abs($SimRes[$i]);
}
$mag=int(log($lo)/log(10))+1;
if ($lo < 1) {$mag-=1}
$lo=int(0.5+$lo*10**($nDigits+1-$mag));
$hi=int(0.5+$hi*10**($nDigits+1-$mag));
next if (abs($lo-$hi)<=10);
$absErr=abs($RefRes[$i]-$SimRes[$i]);
$relErr=$absErr/(0.5*(abs($RefRes[$i])+abs($SimRes[$i])+$absErr));
next if ($relErr<$relTol);
if ($main::verbose) {print STDERR $RefRes[$i],$SimRes[$i],100*$relErr."\%"}
$matchType=2 if ($matchType<2);
$maxRelErr=$relErr if ($relErr > $maxRelErr);
}
}
if ($matchType==0) {
return("MATCH (exact)");
} elsif ($matchType==1) {
return("MATCH (within specified tolerances)");
} elsif ($matchType==2) {
$mag=int(log($maxRelErr)/log(10));
$i=$maxRelErr/10**$mag;
$maxRelErr=100*10**$mag*int(0.5+1e3*$i)/1e3;
return("DIFFER (max rel error is $maxRelErr\%)");
}
}
+704
View File
@@ -0,0 +1,704 @@
#
# perl module with subroutines used as part of compact model QA (runQaTests.pl)
#
#
# Rel Date Who Comments
# ==== ========== ============= ========
# 1.2 06/30/06 Colin McAndrew Floating node support added
# Noise simulation added
# Other general cleanup
# 1.0 04/13/06 Colin McAndrew Initial version
#
package modelQa;
#use strict; # hirearchical read cannot be done while "strict refs" is in use
#
# This subroutine processes the generic (not test specific) setup information.
# It sets the information in global variables.
#
sub processSetup {
my(@Setup)=@_;
my(@Field,$pin,$temperature);
undef(%main::isLinearScale);
undef(%main::isAreaScale);
undef(%main::DefaultTemperature);
$main::doMfactorTest=0;
$main::doScaleTest =0;
$main::doShrinkTest =0;
$main::doPinFlipTest=0;
$main::doPNFlipTest =0;
@main::Pin=();
foreach (@Setup) {
@Field=split(/[\s,]+/,$_);
if (s/^keyLetter\s+//i) {
if ($_ !~ /^[a-zA-Z]$/) {
die("ERROR: bad keyLetter specification, stopped");
}
$main::keyLetter=$_;
next;
}
if (s/^verilogaFile\s+//i) {
$main::verilogaFile=$_;
if (! -f $main::verilogaFile) {
die("ERROR: cannot find file $main::verilogaFile, stopped");
}
next;
}
if (s/^(pins|terminals)\s+//i) {
push(@main::Pin,@Field[1..$#Field]);
foreach $pin (@main::Pin) {
$main::isPin{$pin}=1;
if ($pin !~ /^[a-zA-Z][a-zA-Z0-9]*$/) { # underscores are not allowed
die("ERROR: bad pin name specification $pin, stopped");
}
}
next;
}
if (/^symmetric(Pins|Terminals)/i) {
if ($#Field != 2) {
die("ERROR: bad symmetricPins specification, stopped");
}
$main::isSymmetryPin{$Field[1]}=1;
$main::isSymmetryPin{$Field[2]}=1;
$main::flipPin{$Field[1]}=$Field[2];
$main::flipPin{$Field[2]}=$Field[1];
$main::doPinFlipTest=1;
next;
}
if (s/^pTypeSelectionArguments\s+//i) {
s/\s*=\s*/=/g;
$main::pTypeSelectionArguments=$_;
next;
}
if (s/^(nType|type|model)SelectionArguments\s+//i) {
s/\s*=\s*/=/g;
$main::nTypeSelectionArguments=$_;
next;
}
if (/^checkPolarity/i) {
if ($#Field<1 || ($Field[1] !~ /^[yn01]/i)) {
die("ERROR: bad checkPolarity specification, stopped");
}
if ($Field[1] =~ /^[y1]/i) {
$main::doPNFlipTest=1;
} else {
$main::doPNFlipTest=0;
}
next;
}
if (/^scaleParameters/i) {
foreach (@Field[1..$#Field]) {
if (/^m$/i) {$main::doMfactorTest=1}
if (/^scale$/i) {$main::doScaleTest =1}
if (/^shrink$/i) {$main::doShrinkTest =1}
}
next;
}
if (/^linearScale/i) {
foreach (@Field[1..$#Field]) {
$main::isLinearScale{$_}=1;
}
next;
}
if (/^areaScale/i) {
foreach (@Field[1..$#Field]) {
$main::isAreaScale{$_}=1;
}
next;
}
if (/^temperature/i) {
push(@main::DefaultTemperature,@Field[1..$#Field]);
next;
}
if (/^float/i) {
foreach (@Field[1..$#Field]) {
$main::isGeneralFloatingPin{$_}=1;
}
next;
}
die("ERROR: unknown setup directive $Field[0], stopped");
}
if ($#main::Pin < 1) {
die("ERROR: there must be two or more device pins, stopped");
}
foreach $pin (keys(%main::isSymmetryPin)) {
if (!$main::isPin{$pin}) {
die("ERROR: symmetry pin $pin is not a specified device pin, stopped");
}
}
foreach $pin (keys(%main::isGeneralFloatingPin)) {
if (!$main::isPin{$pin}) {
die("ERROR: floating pin $pin is not a specified device pin, stopped");
}
}
if (!defined(@main::DefaultTemperature)) {
@main::DefaultTemperature=(27);
}
foreach $temperature (@main::DefaultTemperature) {
if ($temperature!~/^$main::number$/) {
die("ERROR: bad temperature value specified, stopped");
}
}
if ($main::simulatorName =~ /mica|ads/i && defined($main::verilogaFile)) {
$main::keyLetter="a";
}
if ($main::simulatorName =~ /hspice/i && defined($main::verilogaFile)) {
$main::keyLetter="X";
}
if ($main::simulatorName =~ /spectre/i && !defined($main::keyLetter)) {
$main::keyLetter="x";
}
if (!defined($main::keyLetter)) {
die("ERROR: no keyLetter specified, stopped");
}
if (!defined($main::nTypeSelectionArguments)) {
die("ERROR: no model selection arguments specified, stopped");
}
@main::Variants=("standard");
if ($main::doPinFlipTest&&$main::doPNFlipTest) {
push(@main::Variants,"Flip_N");
push(@main::Variants,"noFlip_P");
push(@main::Variants,"Flip_P");
if (!defined($main::pTypeSelectionArguments)) {
die("ERROR: no pType model selection arguments specified, stopped");
}
} elsif ($main::doPinFlipTest) {
push(@main::Variants,"Flip_N");
} elsif ($main::doPNFlipTest) {
push(@main::Variants,"noFlip_P");
if (!defined($main::pTypeSelectionArguments)) {
die("ERROR: no pType model selection arguments specified, stopped");
}
}
if ($main::doShrinkTest ) {push(@main::Variants,"shrink")}
if ($main::doScaleTest ) {push(@main::Variants,"scale")}
if ($main::doMfactorTest) {push(@main::Variants,"m")}
}
#
# This subroutine processes test specific setup information.
# It sets the information in global variables.
#
sub processTestSpec {
my(@Spec)=@_;
my($i,$arg,$temperature,$bias,$pin,@Field,$oneOverTwoPi,$name,$value,%isAnalysisPin,%AlreadyHave,%IndexFor);
$main::outputDc=0;$main::outputAc=0;$main::outputNoise=0;
undef($main::biasSweepPin);undef($main::biasSweepSpec);undef(@main::BiasSweepList);
undef($main::biasListPin);undef($main::biasListSpec);
undef($main::frequencySpec);
undef($main::Temperature);
@main::InstanceParameters=();
@main::Outputs=();
@main::ModelParameters=();
undef(%main::BiasFor);
if (defined(%main::isGeneralFloatingPin)) {
%main::isFloatingPin=%main::isGeneralFloatingPin;
} else {
undef(%main::isFloatingPin);
}
undef(%isAnalysisPin);
undef(@main::Temperature);
foreach (@Spec) {
if (s/^output[s]?\s+//i) {
s/\(/ /g;s/\)//g;
@Field=split(/[\s,]+/,$_);
for ($i=0;$i<=$#Field;++$i) {
if ($Field[$i] =~ /^[IV]$/) {
$main::outputDc=1;
++$i;
if (!$main::isPin{$Field[$i]}) {
die("ERROR: pin $Field[$i] listed for DC output is not a specified pin, stopped");
}
push(@main::Outputs,$Field[$i]);
}
if ($Field[$i] =~ /^[CG]$/) {
$main::outputAc=1;
push(@main::Outputs,lc($Field[$i]));
++$i;
if (!$main::isPin{$Field[$i]}) {
die("ERROR: pin $Field[$i] listed for AC output is not a specified pin, stopped");
}
$main::Outputs[$#main::Outputs].=" $Field[$i]";
$isAnalysisPin{$Field[$i]}=1;
++$i;
if (!$main::isPin{$Field[$i]}) {
die("ERROR: pin $Field[$i] listed for AC output is not a specified pin, stopped");
}
$main::Outputs[$#main::Outputs].=" $Field[$i]";
$isAnalysisPin{$Field[$i]}=1;
}
if ($Field[$i] =~ /^N$/) {
$main::outputNoise=1;
++$i;
if (!$main::isPin{$Field[$i]}) {
die("ERROR: pin $Field[$i] listed for noise output is not a specified pin, stopped");
}
if ($#main::Outputs==0) {
die("ERROR: can only specify one pin for noise output, stopped");
}
push(@main::Outputs,$Field[$i]);
$isAnalysisPin{$Field[$i]}=1;
}
}
next;
}
if (/^biases\s+/i) {
@Field=split(/[\s,]+/,$_);
for ($i=1;$i<=$#Field;++$i) {
if ($Field[$i] !~ /=/) {
die("ERROR: biases specifications must be V(pin)=number, stopped");
}
$Field[$i]=~s/V\s*\(\s*//;$Field[$i]=~s/\s*\)//;
($pin,$bias)=split("=",$Field[$i]);
if ($bias !~ /^$main::number$/) {
die("ERROR: biases specifications must be V(pin)=number, stopped");
}
$main::BiasFor{$pin}=$bias;
}
next;
}
if (s/^(biasSweep|sweepBias)\s+//i) {
if (defined($main::biasSweepSpec)) {
die("ERROR: can only have one biasSweep specification, stopped");
}
s/V\s*\(\s*//i;s/\s*\)//;
@Field=split(/[=,\s]+/,$_);
if ($#Field!=3) {
die("ERROR: biasSweep specification must be V(pin)=start,stop,step, stopped");
}
$main::biasSweepPin=$Field[0];
if (($Field[1] !~ /^$main::number$/) || ($Field[2] !~ /^$main::number$/) || ($Field[3] !~ /^$main::number$/)) {
die("ERROR: biasSweep start,stop,step must be numbers, stopped");
}
if ($Field[1] == $Field[2]) {
die("ERROR: biasSweep start and stop must be different, stopped");
}
if ($Field[3] == 0.0) {
die("ERROR: biasStep must be non-zero, stopped");
}
@main::BiasSweepList=();
if ($Field[2] > $Field[1]) {
$Field[3]=abs($Field[3]);
for ($bias=$Field[1];$bias<=$Field[2]+0.1*$Field[3];$bias+=$Field[3]) {
push(@main::BiasSweepList,$bias);
}
} else {
$Field[3]=-1.0*abs($Field[3]);
for ($bias=$Field[1];$bias>=$Field[2]+0.1*$Field[3];$bias+=$Field[3]) {
push(@main::BiasSweepList,$bias);
}
}
$main::biasSweepSpec=join(" ",@Field[1..3]);
$main::BiasFor{$main::biasSweepPin}=$Field[1];
next;
}
if (s/^(biasList|listBias)\s+//i) {
if (defined($main::biasListSpec)) {
die("ERROR: can only have one biasList specification, stopped");
}
s/V\s*\(\s*//i;s/\s*\)//;
@Field=split(/[=,\s]+/,$_);
if ($#Field < 2) {
die("ERROR: biasList specification must be V(pin)=val1,val2,..., stopped");
}
$main::biasListPin=$Field[0];
for ($i=1;$i<=$#Field;++$i) {
if ($Field[$i] !~ /^$main::number$/) {
die("ERROR: biasList values must be numbers, stopped");
}
}
$main::biasListSpec=join(" ",@Field[1..$#Field]);
$main::BiasFor{$main::biasListPin}=$Field[1];
next;
}
if (s/^instanceParameters\s+//i) {
foreach $arg (split(/\s+/,$_)) {
if ($arg !~ /.=./) {
die("ERROR: instance parameters must be name=value pairs, stopped");
}
($name,$value)=split(/=/,$arg);
$value=~s/\(//;$value=~s/\)//; # get rid of possible parens
if ($value !~ /^$main::number$/) {
die("ERROR: instance parameter value in $arg is not a number, stopped");
}
push(@main::InstanceParameters,$arg);
}
next;
}
if (s/^modelParameters\s+//i) {
foreach $arg (split(/\s+/,$_)) {
if ($arg !~ /.=./ && ! -r $arg) {
die("ERROR: model parameters must be name=value pairs or a file name, stopped");
}
if (-r $arg) {
if (!open(IF,"$arg")) {
die("ERROR: cannot open file $arg, stopped");
}
while (<IF>) {
chomp;s/\s*=\s*/=/g;
s/^\+\s*//;s/^\s+//;s/\s+$//;
($name,$value)=split(/=/,$_);
$value=~s/\(//;$value=~s/\)//; # get rid of possible parens
if ($value !~ /^$main::number$/) {
die("ERROR: model parameter value in $_ is not a number, stopped");
}
if (!defined($AlreadyHave{$name})) {
push(@main::ModelParameters,"$name=$value");
$AlreadyHave{$name}=1;
$IndexFor{$name}=$#main::ModelParameters;
} else {
if ($AlreadyHave{$name} == 1 && $main::printWarnings) {
printf("WARNING: parameter $name defined more than once, last value specified will be used\n");
}
$AlreadyHave{$name}=2;
$main::ModelParameters[$IndexFor{$name}]="$name=$value";
}
}
close(IF);
} else {
($name,$value)=split(/=/,$arg);
$value=~s/\(//;$value=~s/\)//; # get rid of possible parens
if ($value !~ /^$main::number$/) {
die("ERROR: model parameter value in $arg is not a number, stopped");
}
if (!defined($AlreadyHave{$name})) {
push(@main::ModelParameters,"$name=$value");
$AlreadyHave{$name}=1;
$IndexFor{$name}=$#main::ModelParameters;
} else {
if ($AlreadyHave{$name} == 1 && $main::printWarnings) {
printf("WARNING: parameter $name defined more than once, last value specified will be used\n");
}
$AlreadyHave{$name}=2;
$main::ModelParameters[$IndexFor{$name}]="$name=$value";
}
}
}
next;
}
if (s/^freq[uency]*\s+//i) {
if (!/^(lin|oct|dec)\s+(\d+)\s+($main::number)\s+($main::number)$/) {
die("ERROR: bad frequency sweep specification, stopped");
}
$main::fType=$1;$main::fSteps=$2;$main::fMin=$3;$main::fMax=$4;
$main::frequencySpec=$_;
next;
}
if (s/^temperature\s+//i) {
push(@main::Temperature,split(/[,\s]+/,$_));
foreach $temperature (@main::Temperature) {
if ($temperature !~ /^$main::number$/) {
die("ERROR: bad temperature value specified, stopped");
}
}
next;
}
if (s/^float[ingpinode]*\s+//i) {
@Field=split(/[\s,]+/,$_);
if ($#Field < 0) {
die("ERROR: bad floating pin specification, stopped");
}
foreach (@Field) {
if (!$main::isPin{$_}) {
die("ERROR: floating pin $_ is not a specified device pin, stopped");
}
$main::isFloatingPin{$_}=1;
}
next;
}
die("ERROR: unknown test directive\n$_\nstopped");
}
if (!defined(@main::Temperature)) {
@main::Temperature=@main::DefaultTemperature;
}
if (abs($main::outputDc+$main::outputAc+$main::outputNoise-1) > 0.001) {
die("ERROR: outputs specified must be one of DC, AC or noise, stopped");
}
if ($main::outputDc && !defined($main::biasSweepSpec)) {
die("ERROR: no bias sweep spec defined for DC testing, stopped");
}
if ($main::outputNoise && !defined($main::frequencySpec)) {
die("ERROR: no frequency spec defined for noise testing, stopped");
}
if ($main::outputAc && !defined($main::frequencySpec)) { # default for AC is omega=1
$oneOverTwoPi=1.0/(8.0*atan2(1.0,1.0));
$main::frequencySpec="lin 1 $oneOverTwoPi $oneOverTwoPi";
$main::fType="lin";$main::fSteps=1;$main::fMin=$oneOverTwoPi;$main::fMax=$oneOverTwoPi;
}
if ($main::outputAc && ($main::frequencySpec eq "lin") && ($main::simulatorName =~ /hspice|ads/i)) {
# AC spec is number of points, not number of steps, for hspice and ads
++$main::fSteps if ($main::fMin != $main::fMax);
$main::frequencySpec="$main::fType $main::fSteps $main::fMin $main::fMax";
}
foreach $pin (@main::Pin) {
if (!defined($main::BiasFor{$pin}) && !defined($main::isFloatingPin{$pin})) {
die("ERROR: a bias must be specified for all non-floating pins, stopped");
}
if ($main::isSymmetryPin{$pin} && $main::isFloatingPin{$pin}) {
die("ERROR: a floating pin cannot be specified as a symmetry pin, stopped");
}
if ($isAnalysisPin{$pin} && $main::isFloatingPin{$pin}) {
die("ERROR: a floating pin can only have its voltage measured in DC analyses, stopped");
}
}
if (!defined($main::biasListPin)) { # if not specified make a dummy bias list, to simplify processing later
$main::biasListPin="dummyPinNameThatIsNeverUsed";
$main::biasListSpec="0";
} elsif (defined($main::isFloatingPin{$main::biasListPin})) {
die("ERROR: a bias list cannot be specified for a floating pin, stopped");
}
if (!defined($main::biasSweepPin)) { # if not specified make a dummy bias sweep, to simplify processing later
if($main::biasListPin eq $main::Pin[0]) {
$main::biasSweepPin=$main::Pin[1];
} else {
$main::biasSweepPin=$main::Pin[0];
}
$main::biasSweepSpec="$main::BiasFor{$main::biasSweepPin} $main::BiasFor{$main::biasSweepPin} 0";
@main::BiasSweepList=($main::BiasFor{$main::biasSweepPin});
} elsif (defined($main::isFloatingPin{$main::biasSweepPin})) {
die("ERROR: a bias sweep cannot be specified for a floating pin, stopped");
}
}
#
# This subroutine reads in a test specification file.
# It cleans up the syntax by getting rid of comments
# and continutation lines, processing conditionals,
# and splitting up the contents of the file into
# global specifications and individual test specifications.
#
# On call:
# $main::qaSpecFile must be set to the name of the file that
# contains the qaSpec information
# On return:
# @main::Setup contains general, test-nonspecific information
# @main::Test contains a list of the test defined in the qaSpec file
# %main::TestSpec contains each test specification, hash keys are @main::Test elements
#
sub readQaSpecFile {
my(@File,@RawFile,@Field);
@RawFile=&readHierarchicalFile($main::qaSpecFile);
foreach (@RawFile) {
s%\s*//.*%%; # eliminate C++ style comments
s/^\s+//;s/\s+$//; # eliminate leading and trailing white space
next if (/^$/); # ignore blank lines
s/\s*=\s*/=/g; # eliminate space around "=" in name=value pairs
if (/^\+/) { # process a continuation line
s/^\+\s*//; # eliminate continuation "+" and any following whitespace
$File[$#File]=~s/\s*\\$//; # get rid of possible additional continuation on previous line
$File[$#File].=" $_"; # add to previous line
next;
}
if (($#File >= 0) && ($File[$#File] =~ /\\$/)) { # add to previous line if that had an end-of-line continuation
$File[$#File]=~s/\s*\\$//;
$File[$#File].=" $_";
} else {
push(@File,$_);
}
}
@File=&processIfdefs(\%main::Defined,@File); # process ifdef's to get conditional-free qaSpec
@main::Test=();@main::Setup=();
foreach (@File) { # process the qaSpec
if (/^test(name)?\s+/i) {
@Field=split;
if ($#Field < 1) {
die("ERROR: no test name specified for a test directive, stopped");
}
push(@main::Test,$Field[1]);
@{$main::TestSpec{$main::Test[$#main::Test]}}=();
next;
}
if ($#main::Test >= 0) {
push(@{$main::TestSpec{$main::Test[$#main::Test]}},$_);
} else {
push(@main::Setup,$_);
}
}
}
sub readHierarchicalFile {
my($fileName,$hierarchyLevel)=@_;
my(@File,$FH,$includeFileName);
if (!defined($hierarchyLevel)) {
$hierarchyLevel=0;
}
$FH="file".$hierarchyLevel;
if (!open($FH,$fileName)) {
die("ERROR: cannot open file $fileName, stopped");
}
@File=();
while (<$FH>) {
chomp;
if (/^\s*\`include\s+/) {
($includeFileName=$')=~s/"//g;
++$hierarchyLevel;
push(@File,&readHierarchicalFile($includeFileName,$hierarchyLevel));
--$hierarchyLevel;
} else {
push(@File,$_);
}
}
close($FH);
return(@File);
}
#
# This subroutine processes the `ifdef statements (recursively, so nested `ifdef's are handled)
# and returns the test specification with the appropriate blocks included and excluded.
# Note that the simulator name is defined, so that
# simulator specific directives are automatically included.
#
sub processIfdefs {
my($defRef,@Input)=@_;
my(%Defined,$i,$block,@Field,$start,$middle,$end,$ifdefLevel,$maxIfdefLevel);
my(@Insert);
%Defined=%$defRef;
for ($i=0;$i<=$#Input;++$i) {
if ($Input[$i] =~ /^`(define|undef)/) {
@Field=split(/\s+/,$Input[$i]);
if ($#Field > 0) {
if ($Input[$i] =~ /^`define/) {
$Defined{$Field[1]}=1;
} else {
$Defined{$Field[1]}=0;
}
}
splice(@Input,$i,1);
--$i;
next;
}
if ($Input[$i] =~ /^`ifdef\s+/) {
$start=$i;
$ifdefLevel=1;$maxIfdefLevel=1;
undef($middle);
for ($end=$start+1;$end<=$#Input;++$end) {
if ($Input[$end] =~ /^`ifdef/) {
++$ifdefLevel;
if ($ifdefLevel > $maxIfdefLevel) {$maxIfdefLevel=$ifdefLevel}
}
if ($Input[$end] =~ /^`end/) {--$ifdefLevel}
if ($Input[$end] =~ /^`else/) {$middle=$end}
last if ($ifdefLevel == 0);
}
if (($end > $#Input) && ($ifdefLevel > 0)) {
die("ERROR: `ifdef not terminated, stopped");
}
($block=$Input[$i])=~s/^`ifdef\s+//;
if ($maxIfdefLevel > 1) {
if (!defined($middle)) {$middle=$end}
@Insert=();
if ($Defined{$block}) {
if ($start+1 <= $middle-1) {
@Insert=&processIfdefs(\%Defined,@Input[$start+1..$middle-1]);
}
} else {
if ($middle+1 <= $end-1) {
@Insert=&processIfdefs(\%Defined,@Input[$middle+1..$end-1]);
}
}
splice(@Input,$start,$end-$start+1,@Insert);
} else {
if (!defined($middle)) {$middle=$end}
@Insert=();
if ($Defined{$block}) {
if ($start+1 <= $middle-1) {
@Insert=@Input[$start+1..$middle-1]
}
} else {
if ($middle+1 <= $end-1) {
@Insert=@Input[$middle+1..$end-1];
}
}
splice(@Input,$start,$end-$start+1,@Insert);
}
--$i;
next;
}
if ($Input[$i] =~ /^`/) {die("ERROR: bad directive\n$Input[$i]\nstopped")}
}
return(@Input);
}
sub unScale {
#
# call: $Result=&unScale($Scalar);
#
# If $Scalar is a SPICE-like scaled number then $Result is the value
# of that number, else $Result is just $Scalar.
#
my($String)=@_;
my($Result);
$Result=$String;
if ($String =~ /^([+-]?[0-9]+[.]?[0-9]*|[+-]?[.][0-9]+)T/i) {
$Result=$1*1e12;
} elsif ($String =~ /^([+-]?[0-9]+[.]?[0-9]*|[+-]?[.][0-9]+)G/i) {
$Result=$1*1e9;
} elsif ($String =~ /^([+-]?[0-9]+[.]?[0-9]*|[+-]?[.][0-9]+)(M|meg|x)/) {
$Result=$1*1e6;
} elsif ($String =~ /^([+-]?[0-9]+[.]?[0-9]*|[+-]?[.][0-9]+)K/i) {
$Result=$1*1e3;
} elsif ($String =~ /^([+-]?[0-9]+[.]?[0-9]*|[+-]?[.][0-9]+)m/) {
$Result=$1*1e-3;
} if ($String =~ /^([+-]?[0-9]+[.]?[0-9]*|[+-]?[.][0-9]+)u/) {
$Result=$1*1e-6;
} elsif ($String =~ /^([+-]?[0-9]+[.]?[0-9]*|[+-]?[.][0-9]+)n/) {
$Result=$1*1e-9;
} elsif ($String =~ /^([+-]?[0-9]+[.]?[0-9]*|[+-]?[.][0-9]+)p/) {
$Result=$1*1e-12;
} elsif ($String =~ /^([+-]?[0-9]+[.]?[0-9]*|[+-]?[.][0-9]+)f/) {
$Result=$1*1e-15;
} elsif ($String =~ /^([+-]?[0-9]+[.]?[0-9]*|[+-]?[.][0-9]+)a/) {
$Result=$1*1e-18;
}
return($Result);
}
sub platform {
#
# This subroutines returns a string that includes the processor
# type, OS name, and OS version. This string is used as one level
# of the directory hierarchy for storing test results, because
# simulation results can vary with processor and OS.
#
# The UNIX uname command is used to get the appropriate information.
# If the system appears to be Windows, then the perl Config module
# information is used instead. However this information is generated
# as part of the Perl build, and so may not relate to the machine
# on which it is being run.
#
use Config;
my($osName,$osVer,$archName)=($modelQa::Config{osname},$modelQa::Config{osvers},$modelQa::Config{archname});
my($platform);
if ($osName !~ /win/i) {
open(UNAME,"uname -p|") or die("ERROR: cannot determine processore and OS information, stopped");
chomp($archName=<UNAME>);close(UNAME);
if ($archName eq "unknown") {
open(UNAME,"uname -m|");chomp($archName=<UNAME>);close(UNAME);
}
open(UNAME,"uname -s|");chomp($osName=<UNAME>);close(UNAME);
open(UNAME,"uname -r|");chomp($osVer =<UNAME>);close(UNAME);
}
$platform = "${archName}_${osName}_${osVer}";
$platform =~ s/\(//;
$platform =~ s/\)//;
return($platform);
}
1;
+474
View File
@@ -0,0 +1,474 @@
#
# ngspice DC, AC and noise test routines
#
#
# Rel Date Who Comments
# ==== ========== ============= ========
# 1.0 05/13/11 Dietmar Warning Initial version
#
package simulate;
$simulatorCommand="ngspice";
$netlistFile="ngspiceCkt";
use strict;
sub version {
return("22"); # the version only seems to be printed in interactive mode
}
sub runNoiseTest {
my($variant,$outputFile)=@_;
my($arg,$name,$value,$type,$pin,$noisePin);
my(@BiasList,$i,@Field);
my(@X,@Noise,$temperature,$biasVoltage,$sweepVoltage,$inData);
#
# Make up the netlist, using a subckt to encapsulate the
# instance. This simplifies handling of the variants as
# the actual instance is driven by voltage-controlled
# voltage sources from the subckt pins, and the currents
# are fed back to the subckt pins using current-controlled
# current sources. Pin swapping, polarity reversal, and
# m-factor scaling can all be handled by simple modifications
# of this subckt.
#
@X=();@Noise=();
$noisePin=$main::Outputs[0];
if ($main::fMin == $main::fMax) {
$main::frequencySpec="lin 0 $main::fMin ".(10*$main::fMin); # spice3f5 bug workaround
}
foreach $temperature (@main::Temperature) {
foreach $biasVoltage (split(/\s+/,$main::biasListSpec)) {
if ($main::fMin == $main::fMax) {
push(@X,@main::BiasSweepList);
}
foreach $sweepVoltage (@main::BiasSweepList) {
if (!open(OF,">$simulate::netlistFile")) {
die("ERROR: cannot open file $simulate::netlistFile, stopped");
}
print OF "* Noise simulation for $main::simulatorName";
&generateCommonNetlistInfo($variant,$temperature);
print OF "vin dummy 0 0 ac 1";
print OF "rin dummy 0 1";
foreach $pin (@main::Pin) {
if ($main::isFloatingPin{$pin}) {
print OF "i_$pin $pin 0 0";
} elsif ($pin eq $main::biasListPin) {
print OF "v_$pin $pin 0 $biasVoltage";
} elsif ($pin eq $main::biasSweepPin) {
print OF "v_$pin $pin 0 $sweepVoltage";
} else {
print OF "v_$pin $pin 0 $main::BiasFor{$pin}";
}
}
print OF "x1 ".join(" ",@main::Pin)." mysub";
print OF "hn 0 n_$noisePin v_$noisePin 1";
print OF ".noise v(n_$noisePin) vin $main::frequencySpec";
print OF ".print noise all";
print OF ".end";
close(OF);
#
# Run simulations and get the results
#
if (!open(SIMULATE,"$simulate::simulatorCommand < $simulate::netlistFile 2>/dev/null|")) {
die("ERROR: cannot run $main::simulatorName, stopped");
}
$inData=0;
while (<SIMULATE>) {
chomp;s/^\s+//;s/\s+$//;s/,/ /g;
if (/Index\s+frequency\s+inoise_spectrum\s+onoise_spectrum/i) {
$inData=1;<SIMULATE>;next;
}
@Field=split;
if (/\*/ || ($#Field != 3)) {$inData=0}
next if (!$inData);
if ($main::fMin == $main::fMax) {
push(@Noise,1*$Field[3]);$inData=0;next; # spice3f5 bug workaround
}
push(@X,1*$Field[1]);
push(@Noise,1*$Field[3]);
}
close(SIMULATE);
}
}
}
#
# Write the results to a file
#
if (!open(OF,">$outputFile")) {
die("ERROR: cannot open file $outputFile, stopped");
}
if ($main::fMin == $main::fMax) {
printf OF ("V($main::biasSweepPin)");
} else {
printf OF ("Freq");
}
foreach (@main::Outputs) {
printf OF (" N($_)");
}
printf OF ("\n");
for ($i=0;$i<=$#X;++$i) {
if (defined($Noise[$i])) {printf OF ("$X[$i] $Noise[$i]\n")}
}
close(OF);
#
# Clean up, unless the debug flag was specified
#
if (! $main::debug) {
unlink($simulate::netlistFile);
unlink("$simulate::netlistFile.st0");
if (!opendir(DIRQA,".")) {
die("ERROR: cannot open directory ., stopped");
}
foreach (grep(/^$simulate::netlistFile\.ic/,readdir(DIRQA))) {unlink($_)}
closedir(DIRQA);
}
}
sub runAcTest {
my($variant,$outputFile)=@_;
my($arg,$name,$value,$type,$pin,$mPin,$fPin,%NextPin);
my(@BiasList,$acStim,$i,@Field);
my(@X,$omega,$twoPi,%g,%c,$temperature,$biasVoltage,$sweepVoltage,$inData,$outputLine);
$twoPi=8.0*atan2(1.0,1.0);
#
# Make up the netlist, using a subckt to encapsulate the
# instance. This simplifies handling of the variants as
# the actual instance is driven by voltage-controlled
# voltage sources from the subckt pins, and the currents
# are fed back to the subckt pins using current-controlled
# current sources. Pin swapping, polarity reversal, and
# m-factor scaling can all be handled by simple modifications
# of this subckt.
#
foreach $mPin (@main::Pin) {
foreach $fPin (@main::Pin) {
@{$g{$mPin,$fPin}}=();
@{$c{$mPin,$fPin}}=();
}
}
@X=();
foreach $temperature (@main::Temperature) {
foreach $biasVoltage (split(/\s+/,$main::biasListSpec)) {
if ($main::fMin == $main::fMax) {
push(@X,@main::BiasSweepList);
}
foreach $sweepVoltage (@main::BiasSweepList) {
if (!open(OF,">$simulate::netlistFile")) {
die("ERROR: cannot open file $simulate::netlistFile, stopped");
}
print OF "* AC simulation for $main::simulatorName";
&generateCommonNetlistInfo($variant,$temperature);
foreach $fPin (@main::Pin) {
foreach $mPin (@main::Pin) {
if ($mPin eq $fPin) {
$acStim=" ac 1";
} else {
$acStim="";
}
if ($main::isFloatingPin{$mPin}) {
print OF "i_${mPin}_$fPin ${mPin}_$fPin 0 0";
} elsif ($mPin eq $main::biasListPin) {
print OF "v_${mPin}_$fPin ${mPin}_$fPin 0 $biasVoltage$acStim";
} elsif ($mPin eq $main::biasSweepPin) {
print OF "v_${mPin}_$fPin ${mPin}_$fPin 0 $sweepVoltage$acStim";
} else {
print OF "v_${mPin}_$fPin ${mPin}_$fPin 0 $main::BiasFor{$mPin}$acStim";
}
}
print OF "x_$fPin ".join("_$fPin ",@main::Pin)."_$fPin mysub";
}
print OF ".ac $main::frequencySpec";
foreach $mPin (@main::Pin) {
foreach $fPin (@main::Pin) {
print OF ".print ac i(v_${mPin}_$fPin)";
}
}
print OF ".end";
close(OF);
#
# Run simulations and get the results
#
if (!open(SIMULATE,"$simulate::simulatorCommand < $simulate::netlistFile 2>/dev/null|")) {
die("ERROR: cannot run $main::simulatorName, stopped");
}
$inData=0;
while (<SIMULATE>) {
chomp;s/^\s+//;s/\s+$//;s/,/ /g;
if (/^Index\s+frequency\s+v_([a-zA-Z][a-zA-Z0-9]*)_([a-zA-Z][a-zA-Z0-9]*)#branch/i) {
$mPin=$1;$fPin=$2;<SIMULATE>;$inData=1;next;
}
@Field=split;
if (/^\*/ || ($#Field != 3)) {$inData=0;}
next if (!$inData);
if (($main::fMin != $main::fMax) && ($mPin eq $fPin) && ($mPin eq $main::Pin[0])) {
push(@X,1*$Field[1]);
}
push(@{$g{$mPin,$fPin}},$Field[2]);
$omega=$twoPi*$Field[1];
if ($mPin eq $fPin) {
push(@{$c{$mPin,$fPin}},$Field[3]/$omega);
} else {
push(@{$c{$mPin,$fPin}},-1*$Field[3]/$omega);
}
}
close(SIMULATE);
}
}
}
#
# Write the results to a file
#
if (!open(OF,">$outputFile")) {
die("ERROR: cannot open file $outputFile, stopped");
}
if ($main::fMin == $main::fMax) {
printf OF ("V($main::biasSweepPin)");
} else {
printf OF ("Freq");
}
foreach (@main::Outputs) {
($type,$mPin,$fPin)=split(/\s+/,$_);
printf OF (" $type($mPin,$fPin)");
}
printf OF ("\n");
for ($i=0;$i<=$#X;++$i) {
$outputLine="$X[$i]";
foreach (@main::Outputs) {
($type,$mPin,$fPin)=split(/\s+/,$_);
if ($type eq "g") {
if (defined(${$g{$mPin,$fPin}}[$i])) {
$outputLine.=" ${$g{$mPin,$fPin}}[$i]";
} else {
undef($outputLine);last;
}
} else {
if (defined(${$c{$mPin,$fPin}}[$i])) {
$outputLine.=" ${$c{$mPin,$fPin}}[$i]";
} else {
undef($outputLine);last;
}
}
}
if (defined($outputLine)) {printf OF ("$outputLine\n")}
}
close(OF);
#
# Clean up, unless the debug flag was specified
#
if (! $main::debug) {
unlink($simulate::netlistFile);
unlink("$simulate::netlistFile.st0");
if (!opendir(DIRQA,".")) {
die("ERROR: cannot open directory ., stopped");
}
foreach (grep(/^$simulate::netlistFile\.ic/,readdir(DIRQA))) {unlink($_)}
closedir(DIRQA);
}
}
sub runDcTest {
my($variant,$outputFile)=@_;
my($arg,$name,$value,$i,@Field,$pin);
my($start,$stop,$step);
my(@V,%DC,$temperature,$biasVoltage);
my($inData,$inResults);
#
# Make up the netlist, using a subckt to encapsulate the
# instance. This simplifies handling of the variants as
# the actual instance is driven by voltage-controlled
# voltage sources from the subckt pins, and the currents
# are fed back to the subckt pins using current-controlled
# current sources. Pin swapping, polarity reversal, and
# m-factor scaling can all be handled by simple modifications
# of this subckt.
#
@V=();
foreach $pin (@main::Outputs) {@{$DC{$pin}}=()}
($start,$stop,$step)=split(/\s+/,$main::biasSweepSpec);
$start-=$step;
foreach $temperature (@main::Temperature) {
foreach $biasVoltage (split(/\s+/,$main::biasListSpec)) {
if (!open(OF,">$simulate::netlistFile")) {
die("ERROR: cannot open file $simulate::netlistFile, stopped");
}
print OF "* DC simulation for $main::simulatorName";
&generateCommonNetlistInfo($variant,$temperature);
foreach $pin (@main::Pin) {
if ($main::isFloatingPin{$pin}) {
print OF "i_$pin $pin 0 0";
} elsif ($pin eq $main::biasListPin) {
print OF "v_$pin $pin 0 $biasVoltage";
} elsif ($pin eq $main::biasSweepPin) {
print OF "v_$pin $pin 0 $start";
} else {
print OF "v_$pin $pin 0 $main::BiasFor{$pin}";
}
}
print OF "x1 ".join(" ",@main::Pin)." mysub";
print OF ".dc v_$main::biasSweepPin $main::biasSweepSpec";
foreach $pin (@main::Outputs) {
if ($main::isFloatingPin{$pin}) {
print OF ".print dc v($pin)";
} else {
print OF ".print dc i(v_$pin)";
}
}
print OF ".end";
close(OF);
#
# Run simulations and get the results
#
if (!open(SIMULATE,"$simulate::simulatorCommand < $simulate::netlistFile 2>/dev/null|")) {
die("ERROR: cannot run $main::simulatorName, stopped");
}
$inResults=0;
while (<SIMULATE>) {
chomp;s/^\s+//;s/\s+$//;s/#branch//;s/\(/_/;s/\)//;
if (/^Index\s+v-sweep\s+v_/i) {$inResults=1;($pin=$');<SIMULATE>;next}
@Field=split;
if ($#Field != 2) {$inResults=0}
next if (!$inResults);
if ($pin eq $main::Outputs[0]) {
push(@V,$Field[1]);
}
push(@{$DC{$pin}},$Field[2]);
}
close(SIMULATE);
}
}
#
# Write the results to a file
#
if (!open(OF,">$outputFile")) {
die("ERROR: cannot open file $outputFile, stopped");
}
printf OF ("V($main::biasSweepPin)");
foreach $pin (@main::Outputs) {
if ($main::isFloatingPin{$pin}) {
printf OF (" V($pin)");
} else {
printf OF (" I($pin)");
}
}
printf OF ("\n");
for ($i=0;$i<=$#V;++$i) {
next if (abs($V[$i]-$start) < abs(0.1*$step)); # this is dummy first bias point
printf OF ("$V[$i]");
foreach $pin (@main::Outputs) {printf OF (" ${$DC{$pin}}[$i]")}
printf OF ("\n");
}
close(OF);
#
# Clean up, unless the debug flag was specified
#
if (! $main::debug) {
unlink($simulate::netlistFile);
unlink("$simulate::netlistFile.st0");
if (!opendir(DIRQA,".")) {
die("ERROR: cannot open directory ., stopped");
}
foreach (grep(/^$simulate::netlistFile\.ic/,readdir(DIRQA))) {unlink($_)}
closedir(DIRQA);
}
}
sub generateCommonNetlistInfo {
my($variant,$temperature)=@_;
my(@Pin_x,$arg,$name,$value,$eFactor,$fFactor,$pin);
foreach $pin (@main::Pin) {push(@Pin_x,"${pin}_x")}
print OF ".options temp=$temperature gmin=1e-15 abstol=1e-14 reltol=1e-8";
if ($variant=~/^scale$/) {
die("ERROR: there is no scale or shrink option for ngspice, stopped");
}
if ($variant=~/^shrink$/) {
die("ERROR: there is no scale or shrink option for ngspice, stopped");
}
if ($variant=~/_P/) {
$eFactor=-1;$fFactor=1;
} else {
$eFactor=1;$fFactor=-1;
}
if ($variant=~/^m$/) {
if ($main::outputNoise) {
$fFactor/=sqrt($main::mFactor);
} else {
$fFactor/=$main::mFactor;
}
}
if (defined($main::verilogaFile)) {
die("ERROR: Verilog-A model support is not implemented for ngspice, stopped");
}
print OF ".subckt mysub ".join(" ",@Pin_x);
foreach $pin (@main::Pin) {
if ($main::isFloatingPin{$pin}) { # assumed "dt" thermal pin, no scaling sign change
print OF "v_$pin ${pin} ${pin}_x 0";
} elsif ($variant=~/^Flip/ && defined($main::flipPin{$pin})) {
print OF "e_$pin ${pin}_v 0 $main::flipPin{$pin}_x 0 $eFactor";
print OF "v_$pin ${pin}_v ${pin} 0";
print OF "f_$pin $main::flipPin{$pin}_x 0 v_$pin $fFactor";
} else {
print OF "e_$pin ${pin}_v 0 ${pin}_x 0 $eFactor";
print OF "v_$pin ${pin}_v ${pin} 0";
print OF "f_$pin ${pin}_x 0 v_$pin $fFactor";
}
}
print OF "${main::keyLetter}1 ".join(" ",@main::Pin)." mymodel";
foreach $arg (@main::InstanceParameters) {
($name,$value)=split(/=/,$arg);
if ($variant=~/^scale$/) {
if ($main::isLinearScale{$name}) {
$value/=$main::scaleFactor;
} elsif ($main::isAreaScale{$name}) {
$value/=$main::scaleFactor**2;
}
}
if ($variant=~/^shrink$/) {
if ($main::isLinearScale{$name}) {
$value/=(1.0-$main::shrinkPercent*0.01);
} elsif ($main::isAreaScale{$name}) {
$value/=(1.0-$main::shrinkPercent*0.01)**2;
}
}
print OF "+ $name=$value";
}
if ($variant eq "m") {
print OF "+ m=$main::mFactor";
}
if ($variant=~/_P/) {
print OF ".model mymodel $main::pTypeSelectionArguments";
} else {
print OF ".model mymodel $main::nTypeSelectionArguments";
}
foreach $arg (@main::ModelParameters) {
print OF "+ $arg";
}
print OF ".ends";
}
1;
+335
View File
@@ -0,0 +1,335 @@
#!/bin/sh
eval 'exec perl -S -x -w $0 ${1+"$@"}'
#!perl
#
# runQaTests.pl: program to run automated QA tests on compact models
#
# Rel Date Who Comments
# ==== ========== ============= ========
# 1.2 06/30/06 Colin McAndrew Floating node support added
# 1.0 04/13/06 Colin McAndrew Initial version
#
sub usage() {
print "
$prog: run model QA tests
Usage: $prog [options] -s simulatorName qaSpecificationFile
Files:
qaSpecificationFile file with specifications for QA tests
simulatorName name of simulator to be tested
Options:
-c version platform do not try to simulate, only compare results for version and platform
-d debug mode (leave intermediate files around)
-h print this help message
-i print info on file formats and structure
-l list tests and variants that are defined
-lt list tests that are defined
-lv list test variants that are defined
-nw do not print warning messages
-platform prints the hardware platform and operating system version
-p plot results (limited, only standard test variant)
-P plot results (complete, for all test variants)
-r re-use previously simulated results if they exist
(default is to resimulate, even if results exist)
-sv prints the simulator version being run
-t TEST only run test TEST (can be a comma delimited list)
-var VAR only run variant VAR (can be a comma delimited list)
-v verbose mode
-V really verbose mode, print out each difference detected
";
} # End of usage
sub info() {
print "
This program runs automated QA tests on a model.
The test specifications are defined in the qaSpecificationFile
Each test is run by setting up a netlist, running this is in
the simulator in which the implementation of a model is being
tested, and then collating the simulation results. Because the
netlist formats, simulator commands, and output formats
vary between simulators, a specific set of routines that
run the tests must be provided for each simulator.
Please see the documentation for more details.
";
} # End of info
#
# Set program names and variables
#
$\="\n";
$,=" ";
undef($number);
$number='[+-]?\d+[\.]?\d*[eE][+-]?\d+|[+-]?[\.]\d+[eE][+-]?\d+|[+-]?\d+[\.]?\d*|[+-]?[\.]\d+';
undef($qaSpecFile);
undef(@Setup);
undef(@Test);
undef(@Variants);
$debug=0;
$verbose=0;
$reallyVerbose=0;
$doPlot=0;
$listTests=0;
$listVariants=0;
$onlyDoSimulatorVersion=0;
$onlyDoPlatformVersion=0;
$onlyDoComparison=0;
$forceSimulation=1;
$printWarnings=1;
@prog=split("/",$0);
$programDirectory=join("/",@prog[0..$#prog-1]);
$prog=$prog[$#prog];
#
# These variables are only defined once in this file,
# and so generate unsightly warnings from the -w option
# to perl, these undef's stop those warnings.
#
undef($dcClip);undef($dcNdigit);undef($dcRelTol);
undef($acClip);undef($acNdigit);undef($acRelTol);
undef($noiseClip);undef($noiseNdigit);undef($noiseRelTol);
undef($mFactor);undef($shrinkPercent);undef($scaleFactor);undef(%TestSpec);
undef($refrnceDirectory);
#
# These are the tolerances used to compare results
#
$dcClip=1.0e-13;
$dcNdigit=6;
$dcRelTol=1.0e-6;
$acClip=1.0e-20;
$acNdigit=6;
$acRelTol=1.0e-6;
$noiseClip=1.0e-30;
$noiseNdigit=5;
$noiseRelTol=1.0e-5;
#
# These are the values used to test shrink, scale, and m
# (if they are requested to be tested).
#
$scaleFactor=1.0e-6;
$shrinkPercent=50;
$sqrt_mFactor=10;
$mFactor=$sqrt_mFactor*$sqrt_mFactor;
#
# Parse the command line arguments
#
for (;;) {
if (!defined($ARGV[0])) {
last;
} elsif ($ARGV[0] =~ /^-c/) {
shift(@ARGV);
if ($#ARGV<0) {die("ERROR: no simulator version specified for -c option, stopped")}
$version=$ARGV[0];
shift(@ARGV);
if ($#ARGV<0) {die("ERROR: no platform specified for -c option, stopped")}
$platform=$ARGV[0];
$onlyDoComparison=1;
} elsif ($ARGV[0] =~ /^-d/i) {
$debug=1;$verbose=1;
} elsif ($ARGV[0] =~ /^-h/i) {
&usage();exit(0);
} elsif ($ARGV[0] =~ /^-i/i) {
&usage();&info();exit(0);
} elsif ($ARGV[0] =~ /^-lv/i) {
$listVariants=1;
} elsif ($ARGV[0] =~ /^-lt/i) {
$listTests=1;
} elsif ($ARGV[0] =~ /^-l/i) {
$listTests=1;$listVariants=1;
} elsif ($ARGV[0] =~ /^-platform/i) {
$onlyDoPlatformVersion=1;
} elsif ($ARGV[0] =~ /^-nw/i) {
$printWarnings=0;
} elsif ($ARGV[0] =~ /^-p/i) {
$doPlot=1;
} elsif ($ARGV[0] =~ /^-r/i) {
$forceSimulation=0;
} elsif ($ARGV[0] =~ /^-sv/i) {
$onlyDoSimulatorVersion=1;
} elsif ($ARGV[0] =~ /^-s/) {
shift(@ARGV);
if ($#ARGV<0) {die("ERROR: no simulator specified for -s option, stopped")}
$simulatorName=$ARGV[0];
} elsif ($ARGV[0] =~ /^-t/) {
shift(@ARGV);
if ($#ARGV<0) {die("ERROR: no test(s) specified for -t option, stopped")}
foreach (split(/,/,$ARGV[0])) {$doTest{$_}=1}
} elsif ($ARGV[0] =~ /^-var/) {
shift(@ARGV);
if ($#ARGV<0) {die("ERROR: no variant(s) specified for -var option, stopped")}
foreach (split(/,/,$ARGV[0])) {$doVariant{$_}=1}
} elsif ($ARGV[0] =~ /^-v/) {
$verbose=1;
} elsif ($ARGV[0] =~ /^-V/) {
$verbose=1;$reallyVerbose=1;
} elsif ($ARGV[0] =~ /^-/) {
&usage();
die("ERROR: unknown flag $ARGV[0], stopped");
} else {
last;
}
shift(@ARGV);
}
if ($onlyDoSimulatorVersion && !defined($simulatorName) && defined($ARGV[0])) {
$simulatorName=$ARGV[0]; # assume -sv simulatorName was specified
}
if ($#ARGV<0 && !$onlyDoPlatformVersion && !($onlyDoSimulatorVersion && defined($simulatorName))) {
&usage();exit(0);
}
if (!$onlyDoPlatformVersion && !defined($simulatorName)) {
&usage();exit(0);
}
#
# Source perl modules with subroutines that are called to do all the work
#
if (! require "$programDirectory/modelQaTestRoutines.pm") {
die("ERROR: problem sourcing modelQaTestRoutines.pm, stopped");
}
if (!$onlyDoComparison) {
$platform=&modelQa::platform();
if ($onlyDoPlatformVersion) {
print $platform;exit(0);
}
if (! -r "$programDirectory/$simulatorName.pm") {
die("ERROR: there is no test routine Perl module for simulator $simulatorName, stopped");
}
if (! require "$programDirectory/$simulatorName.pm") {
die("ERROR: problem sourcing test routine Perl module for simulator $simulatorName, stopped");
}
}
#
# Initial processing, set up directory names and process the QA specification file
#
if (!$onlyDoComparison) {
$version=&simulate::version();
if ($onlyDoSimulatorVersion) {
print $version;exit(0);
}
}
$qaSpecFile=$ARGV[0];
$resultsDirectory="results";
$refrnceDirectory="reference";
undef(%Defined);
$Defined{$simulatorName}=1; # any `ifdef's in the QA spec file for $simulatorName are automatically inlcuded
&modelQa::readQaSpecFile();
&modelQa::processSetup(@Setup);
#
# List tests and variants, if that was all that was requested
# (note that the Makefile uses output from -lt and -lv
# options to loop over the tests and variants individually,
# so the output for those needs to be on a single line)
#
if ($listTests || $listVariants) {
if ($listTests && $listVariants) {
print "\nTests:";
foreach (@Test) {print " ".$_}
print "\nVariants:";
foreach (@Variants) {print " ".$_}
} elsif ($listTests) {
print @Test;
} else {
print @Variants;
}
exit(0);
}
#
# Loop over and run all tests
# Note that the "standard" variant test is compared
# to the reference results, whereas the other variant
# tests are compared to the "standard" variant result.
# This is because there may be some slight differences
# between implementations, which get flagged when standard
# is compared to reference, however for the other variants
# this would generate a sequence of identical and in-exact
# comparison messages. Each variant should *exactly* match
# the standard, hence this is checked, and gives cleaner
# looking output when the standard differs from the reference.
#
if (!$onlyDoComparison) {
if (! -d $resultsDirectory) {mkdir($resultsDirectory,0775)}
$resultsDirectory.="/$simulatorName";
if (! -d $resultsDirectory) {mkdir($resultsDirectory,0775)}
$resultsDirectory.="/$version";
if (! -d $resultsDirectory) {mkdir($resultsDirectory,0775)}
$resultsDirectory.="/$platform";
if (! -d $resultsDirectory) {mkdir($resultsDirectory,0775)}
} else {
$resultsDirectory.="/$simulatorName/$version/$platform";
}
if ($reallyVerbose) {
$flag="-v";
} else {
$flag="";
}
foreach $test (@Test) {
next if (defined(%doTest) && !$doTest{$test});
if ($verbose) {print "\n****** Running test ($simulatorName): $test"}
undef($outputDc);
undef($outputAc);
undef($outputNoise);
&modelQa::processTestSpec(@{$TestSpec{$test}});
foreach $variant (@Variants) {
if ($variant eq "standard") {
$refFile="$refrnceDirectory/$test.standard";
} else {
$refFile="$resultsDirectory/$test.standard";
}
next if (defined(%doVariant) && !$doVariant{$variant});
$simFile="$resultsDirectory/$test.$variant";
if ($outputDc) {
if (($forceSimulation || ! -r $simFile) && !$onlyDoComparison) {
&simulate::runDcTest($variant,$simFile);
}
$clip=$dcClip;$relTol=$dcRelTol;$ndigit=$dcNdigit;
}
if ($outputAc) {
if (($forceSimulation || ! -r $simFile) && !$onlyDoComparison) {
&simulate::runAcTest($variant,$simFile);
}
$clip=$acClip;$relTol=$acRelTol;$ndigit=$acNdigit;
}
if ($outputNoise) {
if (($forceSimulation || ! -r $simFile) && !$onlyDoComparison) {
&simulate::runNoiseTest($variant,$simFile);
}
$clip=$noiseClip;$relTol=$noiseRelTol;$ndigit=$noiseNdigit;
}
if (-r $refFile && -r $simFile) {
$message=sprintf(" variant: %-20s************************ comparison failed\n",$variant);
if (open(IF,"$programDirectory/compareSimulationResults.pl $flag -c $clip -r $relTol -n $ndigit $refFile $simFile|")) {
while (<IF>) {chomp;$message=$_;}
close(IF);
}
print $message;
} else {
printf(" variant: %-20s************************ no results to compare to\n",$variant);
}
}
}
+476
View File
@@ -0,0 +1,476 @@
#
# spice3f5 DC, AC and noise test routines
#
#
# Rel Date Who Comments
# ==== ========== ============= ========
# 1.2 06/30/06 Colin McAndrew Floating node support added
# Noise simulation added
# 1.0 04/13/06 Colin McAndrew Initial version
#
package simulate;
$simulatorCommand="spice3";
$netlistFile="spiceCkt";
use strict;
sub version {
return("3f5"); # the version only seems to be printed in interactive mode
}
sub runNoiseTest {
my($variant,$outputFile)=@_;
my($arg,$name,$value,$type,$pin,$noisePin);
my(@BiasList,$i,@Field);
my(@X,@Noise,$temperature,$biasVoltage,$sweepVoltage,$inData);
#
# Make up the netlist, using a subckt to encapsulate the
# instance. This simplifies handling of the variants as
# the actual instance is driven by voltage-controlled
# voltage sources from the subckt pins, and the currents
# are fed back to the subckt pins using current-controlled
# current sources. Pin swapping, polarity reversal, and
# m-factor scaling can all be handled by simple modifications
# of this subckt.
#
@X=();@Noise=();
$noisePin=$main::Outputs[0];
if ($main::fMin == $main::fMax) {
$main::frequencySpec="lin 0 $main::fMin ".(10*$main::fMin); # spice3f5 bug workaround
}
foreach $temperature (@main::Temperature) {
foreach $biasVoltage (split(/\s+/,$main::biasListSpec)) {
if ($main::fMin == $main::fMax) {
push(@X,@main::BiasSweepList);
}
foreach $sweepVoltage (@main::BiasSweepList) {
if (!open(OF,">$simulate::netlistFile")) {
die("ERROR: cannot open file $simulate::netlistFile, stopped");
}
print OF "* Noise simulation for $main::simulatorName";
&generateCommonNetlistInfo($variant,$temperature);
print OF "vin dummy 0 0 ac 1";
print OF "rin dummy 0 1";
foreach $pin (@main::Pin) {
if ($main::isFloatingPin{$pin}) {
print OF "i_$pin $pin 0 0";
} elsif ($pin eq $main::biasListPin) {
print OF "v_$pin $pin 0 $biasVoltage";
} elsif ($pin eq $main::biasSweepPin) {
print OF "v_$pin $pin 0 $sweepVoltage";
} else {
print OF "v_$pin $pin 0 $main::BiasFor{$pin}";
}
}
print OF "x1 ".join(" ",@main::Pin)." mysub";
print OF "hn 0 n_$noisePin v_$noisePin 1";
print OF ".noise v(n_$noisePin) vin $main::frequencySpec";
print OF ".print noise all";
print OF ".end";
close(OF);
#
# Run simulations and get the results
#
if (!open(SIMULATE,"$simulate::simulatorCommand < $simulate::netlistFile 2>/dev/null|")) {
die("ERROR: cannot run $main::simulatorName, stopped");
}
$inData=0;
while (<SIMULATE>) {
chomp;s/^\s+//;s/\s+$//;s/,/ /g;
if (/Index\s+frequency\s+inoise_spectrum\s+onoise_spectrum/i) {
$inData=1;<SIMULATE>;next;
}
@Field=split;
if (/\*/ || ($#Field != 3)) {$inData=0}
next if (!$inData);
if ($main::fMin == $main::fMax) {
push(@Noise,1*$Field[3]);$inData=0;next; # spice3f5 bug workaround
}
push(@X,1*$Field[1]);
push(@Noise,1*$Field[3]);
}
close(SIMULATE);
}
}
}
#
# Write the results to a file
#
if (!open(OF,">$outputFile")) {
die("ERROR: cannot open file $outputFile, stopped");
}
if ($main::fMin == $main::fMax) {
printf OF ("V($main::biasSweepPin)");
} else {
printf OF ("Freq");
}
foreach (@main::Outputs) {
printf OF (" N($_)");
}
printf OF ("\n");
for ($i=0;$i<=$#X;++$i) {
if (defined($Noise[$i])) {printf OF ("$X[$i] $Noise[$i]\n")}
}
close(OF);
#
# Clean up, unless the debug flag was specified
#
if (! $main::debug) {
unlink($simulate::netlistFile);
unlink("$simulate::netlistFile.st0");
if (!opendir(DIRQA,".")) {
die("ERROR: cannot open directory ., stopped");
}
foreach (grep(/^$simulate::netlistFile\.ic/,readdir(DIRQA))) {unlink($_)}
closedir(DIRQA);
}
}
sub runAcTest {
my($variant,$outputFile)=@_;
my($arg,$name,$value,$type,$pin,$mPin,$fPin,%NextPin);
my(@BiasList,$acStim,$i,@Field);
my(@X,$omega,$twoPi,%g,%c,$temperature,$biasVoltage,$sweepVoltage,$inData,$outputLine);
$twoPi=8.0*atan2(1.0,1.0);
#
# Make up the netlist, using a subckt to encapsulate the
# instance. This simplifies handling of the variants as
# the actual instance is driven by voltage-controlled
# voltage sources from the subckt pins, and the currents
# are fed back to the subckt pins using current-controlled
# current sources. Pin swapping, polarity reversal, and
# m-factor scaling can all be handled by simple modifications
# of this subckt.
#
foreach $mPin (@main::Pin) {
foreach $fPin (@main::Pin) {
@{$g{$mPin,$fPin}}=();
@{$c{$mPin,$fPin}}=();
}
}
@X=();
foreach $temperature (@main::Temperature) {
foreach $biasVoltage (split(/\s+/,$main::biasListSpec)) {
if ($main::fMin == $main::fMax) {
push(@X,@main::BiasSweepList);
}
foreach $sweepVoltage (@main::BiasSweepList) {
if (!open(OF,">$simulate::netlistFile")) {
die("ERROR: cannot open file $simulate::netlistFile, stopped");
}
print OF "* AC simulation for $main::simulatorName";
&generateCommonNetlistInfo($variant,$temperature);
foreach $fPin (@main::Pin) {
foreach $mPin (@main::Pin) {
if ($mPin eq $fPin) {
$acStim=" ac 1";
} else {
$acStim="";
}
if ($main::isFloatingPin{$mPin}) {
print OF "i_${mPin}_$fPin ${mPin}_$fPin 0 0";
} elsif ($mPin eq $main::biasListPin) {
print OF "v_${mPin}_$fPin ${mPin}_$fPin 0 $biasVoltage$acStim";
} elsif ($mPin eq $main::biasSweepPin) {
print OF "v_${mPin}_$fPin ${mPin}_$fPin 0 $sweepVoltage$acStim";
} else {
print OF "v_${mPin}_$fPin ${mPin}_$fPin 0 $main::BiasFor{$mPin}$acStim";
}
}
print OF "x_$fPin ".join("_$fPin ",@main::Pin)."_$fPin mysub";
}
print OF ".ac $main::frequencySpec";
foreach $mPin (@main::Pin) {
foreach $fPin (@main::Pin) {
print OF ".print ac i(v_${mPin}_$fPin)";
}
}
print OF ".end";
close(OF);
#
# Run simulations and get the results
#
if (!open(SIMULATE,"$simulate::simulatorCommand < $simulate::netlistFile 2>/dev/null|")) {
die("ERROR: cannot run $main::simulatorName, stopped");
}
$inData=0;
while (<SIMULATE>) {
chomp;s/^\s+//;s/\s+$//;s/,/ /g;
if (/^Index\s+frequency\s+v_([a-zA-z][a-zA-Z0-9]*)_([a-zA-z][a-zA-Z0-9]*)#branch/i) {
$mPin=$1;$fPin=$2;<SIMULATE>;$inData=1;next;
}
@Field=split;
if (/^\*/ || ($#Field != 4)) {$inData=0}
next if (!$inData);
if (($main::fMin != $main::fMax) && ($mPin eq $fPin) && ($mPin eq $main::Pin[0])) {
push(@X,1*$Field[1]);
}
push(@{$g{$mPin,$fPin}},$Field[3]);
$omega=$twoPi*$Field[1];
if ($mPin eq $fPin) {
push(@{$c{$mPin,$fPin}},$Field[4]/$omega);
} else {
push(@{$c{$mPin,$fPin}},-1*$Field[4]/$omega);
}
}
close(SIMULATE);
}
}
}
#
# Write the results to a file
#
if (!open(OF,">$outputFile")) {
die("ERROR: cannot open file $outputFile, stopped");
}
if ($main::fMin == $main::fMax) {
printf OF ("V($main::biasSweepPin)");
} else {
printf OF ("Freq");
}
foreach (@main::Outputs) {
($type,$mPin,$fPin)=split(/\s+/,$_);
printf OF (" $type($mPin,$fPin)");
}
printf OF ("\n");
for ($i=0;$i<=$#X;++$i) {
$outputLine="$X[$i]";
foreach (@main::Outputs) {
($type,$mPin,$fPin)=split(/\s+/,$_);
if ($type eq "g") {
if (defined(${$g{$mPin,$fPin}}[$i])) {
$outputLine.=" ${$g{$mPin,$fPin}}[$i]";
} else {
undef($outputLine);last;
}
} else {
if (defined(${$c{$mPin,$fPin}}[$i])) {
$outputLine.=" ${$c{$mPin,$fPin}}[$i]";
} else {
undef($outputLine);last;
}
}
}
if (defined($outputLine)) {printf OF ("$outputLine\n")}
}
close(OF);
#
# Clean up, unless the debug flag was specified
#
if (! $main::debug) {
unlink($simulate::netlistFile);
unlink("$simulate::netlistFile.st0");
if (!opendir(DIRQA,".")) {
die("ERROR: cannot open directory ., stopped");
}
foreach (grep(/^$simulate::netlistFile\.ic/,readdir(DIRQA))) {unlink($_)}
closedir(DIRQA);
}
}
sub runDcTest {
my($variant,$outputFile)=@_;
my($arg,$name,$value,$i,@Field,$pin);
my($start,$stop,$step);
my(@V,%DC,$temperature,$biasVoltage);
my($inData,$inResults);
#
# Make up the netlist, using a subckt to encapsulate the
# instance. This simplifies handling of the variants as
# the actual instance is driven by voltage-controlled
# voltage sources from the subckt pins, and the currents
# are fed back to the subckt pins using current-controlled
# current sources. Pin swapping, polarity reversal, and
# m-factor scaling can all be handled by simple modifications
# of this subckt.
#
@V=();
foreach $pin (@main::Outputs) {@{$DC{$pin}}=()}
($start,$stop,$step)=split(/\s+/,$main::biasSweepSpec);
$start-=$step;
foreach $temperature (@main::Temperature) {
foreach $biasVoltage (split(/\s+/,$main::biasListSpec)) {
if (!open(OF,">$simulate::netlistFile")) {
die("ERROR: cannot open file $simulate::netlistFile, stopped");
}
print OF "* DC simulation for $main::simulatorName";
&generateCommonNetlistInfo($variant,$temperature);
foreach $pin (@main::Pin) {
if ($main::isFloatingPin{$pin}) {
print OF "i_$pin $pin 0 0";
} elsif ($pin eq $main::biasListPin) {
print OF "v_$pin $pin 0 $biasVoltage";
} elsif ($pin eq $main::biasSweepPin) {
print OF "v_$pin $pin 0 $start";
} else {
print OF "v_$pin $pin 0 $main::BiasFor{$pin}";
}
}
print OF "x1 ".join(" ",@main::Pin)." mysub";
print OF ".dc v_$main::biasSweepPin $main::biasSweepSpec";
foreach $pin (@main::Outputs) {
if ($main::isFloatingPin{$pin}) {
print OF ".print dc v($pin)";
} else {
print OF ".print dc i(v_$pin)";
}
}
print OF ".end";
close(OF);
#
# Run simulations and get the results
#
if (!open(SIMULATE,"$simulate::simulatorCommand < $simulate::netlistFile 2>/dev/null|")) {
die("ERROR: cannot run $main::simulatorName, stopped");
}
$inResults=0;
while (<SIMULATE>) {
chomp;s/^\s+//;s/\s+$//;s/#branch//;s/\(/_/;s/\)//;
if (/^Index\s+sweep\s+v_/i) {$inResults=1;($pin=$');<SIMULATE>;next}
@Field=split;
if ($#Field != 2) {$inResults=0}
next if (!$inResults);
if ($pin eq $main::Outputs[0]) {
push(@V,$Field[1]);
}
push(@{$DC{$pin}},$Field[2]);
}
close(SIMULATE);
}
}
#
# Write the results to a file
#
if (!open(OF,">$outputFile")) {
die("ERROR: cannot open file $outputFile, stopped");
}
printf OF ("V($main::biasSweepPin)");
foreach $pin (@main::Outputs) {
if ($main::isFloatingPin{$pin}) {
printf OF (" V($pin)");
} else {
printf OF (" I($pin)");
}
}
printf OF ("\n");
for ($i=0;$i<=$#V;++$i) {
next if (abs($V[$i]-$start) < abs(0.1*$step)); # this is dummy first bias point
printf OF ("$V[$i]");
foreach $pin (@main::Outputs) {printf OF (" ${$DC{$pin}}[$i]")}
printf OF ("\n");
}
close(OF);
#
# Clean up, unless the debug flag was specified
#
if (! $main::debug) {
unlink($simulate::netlistFile);
unlink("$simulate::netlistFile.st0");
if (!opendir(DIRQA,".")) {
die("ERROR: cannot open directory ., stopped");
}
foreach (grep(/^$simulate::netlistFile\.ic/,readdir(DIRQA))) {unlink($_)}
closedir(DIRQA);
}
}
sub generateCommonNetlistInfo {
my($variant,$temperature)=@_;
my(@Pin_x,$arg,$name,$value,$eFactor,$fFactor,$pin);
foreach $pin (@main::Pin) {push(@Pin_x,"${pin}_x")}
print OF ".options temp=$temperature gmin=1e-15 abstol=1e-14 reltol=1e-8";
if ($variant=~/^scale$/) {
die("ERROR: there is no scale or shrink option for spice, stopped");
}
if ($variant=~/^shrink$/) {
die("ERROR: there is no scale or shrink option for spice, stopped");
}
if ($variant=~/_P/) {
$eFactor=-1;$fFactor=1;
} else {
$eFactor=1;$fFactor=-1;
}
if ($variant=~/^m$/) {
if ($main::outputNoise) {
$fFactor/=sqrt($main::mFactor);
} else {
$fFactor/=$main::mFactor;
}
}
if (defined($main::verilogaFile)) {
die("ERROR: Verilog-A model support is not implemented for spice, stopped");
}
print OF ".subckt mysub ".join(" ",@Pin_x);
foreach $pin (@main::Pin) {
if ($main::isFloatingPin{$pin}) { # assumed "dt" thermal pin, no scaling sign change
print OF "v_$pin ${pin} ${pin}_x 0";
} elsif ($variant=~/^Flip/ && defined($main::flipPin{$pin})) {
print OF "e_$pin ${pin}_v 0 $main::flipPin{$pin}_x 0 $eFactor";
print OF "v_$pin ${pin}_v ${pin} 0";
print OF "f_$pin $main::flipPin{$pin}_x 0 v_$pin $fFactor";
} else {
print OF "e_$pin ${pin}_v 0 ${pin}_x 0 $eFactor";
print OF "v_$pin ${pin}_v ${pin} 0";
print OF "f_$pin ${pin}_x 0 v_$pin $fFactor";
}
}
print OF "${main::keyLetter}1 ".join(" ",@main::Pin)." mymodel";
foreach $arg (@main::InstanceParameters) {
($name,$value)=split(/=/,$arg);
if ($variant=~/^scale$/) {
if ($main::isLinearScale{$name}) {
$value/=$main::scaleFactor;
} elsif ($main::isAreaScale{$name}) {
$value/=$main::scaleFactor**2;
}
}
if ($variant=~/^shrink$/) {
if ($main::isLinearScale{$name}) {
$value/=(1.0-$main::shrinkPercent*0.01);
} elsif ($main::isAreaScale{$name}) {
$value/=(1.0-$main::shrinkPercent*0.01)**2;
}
}
print OF "+ $name=$value";
}
if ($variant eq "m") {
print OF "+ m=$main::mFactor";
}
if ($variant=~/_P/) {
print OF ".model mymodel $main::pTypeSelectionArguments";
} else {
print OF ".model mymodel $main::nTypeSelectionArguments";
}
foreach $arg (@main::ModelParameters) {
print OF "+ $arg";
}
print OF ".ends";
}
1;