You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
550 lines
17 KiB
550 lines
17 KiB
/**********
|
|
Copyright 1990 Regents of the University of California. All rights reserved.
|
|
Author: 1987 Gary W. Ng
|
|
Modified: 2001 AlansFixes
|
|
**********/
|
|
|
|
/* Patch to noisean.c by Richard D. McRoberts.
|
|
* Patched with modifications from Weidong Liu (2000)
|
|
* Patched with modifications ftom Weidong Liu
|
|
* in bsim4.1.0 code
|
|
*/
|
|
|
|
#include "ngspice/ngspice.h"
|
|
#include "ngspice/acdefs.h"
|
|
#include "ngspice/cktdefs.h"
|
|
#include "ngspice/iferrmsg.h"
|
|
#include "ngspice/cpextern.h"
|
|
#include "ngspice/noisedef.h"
|
|
#include "ngspice/sperror.h"
|
|
#include "ngspice/sim.h"
|
|
#include "vsrc/vsrcdefs.h"
|
|
#include "isrc/isrcdefs.h"
|
|
|
|
#ifdef XSPICE
|
|
#include "ngspice/evt.h"
|
|
#include "ngspice/enh.h"
|
|
/* gtri - add - wbk - 12/19/90 - Add headers */
|
|
#include "ngspice/mif.h"
|
|
#include "ngspice/evtproto.h"
|
|
#include "ngspice/ipctiein.h"
|
|
/* gtri - end - wbk */
|
|
#endif
|
|
|
|
#ifdef KLU
|
|
#include "ngspice/devdefs.h"
|
|
#endif
|
|
|
|
// fixme
|
|
// ugly hack to work around missing api to specify the "type" of signals
|
|
|
|
extern int fixme_onoise_type;
|
|
extern int fixme_inoise_type;
|
|
|
|
|
|
int
|
|
NOISEan(CKTcircuit* ckt, int restart)
|
|
{
|
|
/* variable must be static, for continuation of interrupted (Ctrl-C),
|
|
longer lasting noise anlysis */
|
|
static Ndata* data;
|
|
|
|
double realVal;
|
|
double imagVal;
|
|
int error;
|
|
int posOutNode;
|
|
int negOutNode;
|
|
int step;
|
|
IFuid freqUid;
|
|
double freqTol; /* tolerence parameter for finding final frequency; hack */
|
|
int i, src_type;
|
|
|
|
int numNames;
|
|
IFuid* nameList; /* va: tmalloc'ed list of names */
|
|
static runDesc* noiPlot = NULL;
|
|
runDesc* plot = NULL;
|
|
|
|
#ifdef XSPICE
|
|
/* Tell the code models what mode we're in */
|
|
g_mif_info.circuit.anal_type = MIF_DC;
|
|
g_mif_info.circuit.anal_init = MIF_TRUE;
|
|
#endif
|
|
|
|
#ifdef KLU
|
|
if (ckt->CKTkluMODE) {
|
|
fprintf(stderr, "Error: Noise simulation is not (yet) supported with 'option KLU'.\n");
|
|
fprintf(stderr, " Use 'option sparse' instead.\n");
|
|
return(E_UNSUPP);
|
|
}
|
|
#endif
|
|
|
|
NOISEAN* job = (NOISEAN*)ckt->CKTcurJob;
|
|
GENinstance* inst = CKTfndDev(ckt, job->input);
|
|
bool frequequal = AlmostEqualUlps(job->NstartFreq, job->NstopFreq, 3);
|
|
|
|
posOutNode = (job->output)->number;
|
|
negOutNode = (job->outputRef)->number;
|
|
|
|
if (job->NnumSteps < 1) {
|
|
SPfrontEnd->IFerrorf(ERR_WARNING,
|
|
"Number of steps for noise measurement has to be larger than 0,\n but currently is %d\n",
|
|
job->NnumSteps);
|
|
return(E_PARMVAL);
|
|
}
|
|
else if ((job->NnumSteps == 1) && (job->NstpType == LINEAR)) {
|
|
if (!frequequal) {
|
|
job->NstopFreq = job->NstartFreq;
|
|
SPfrontEnd->IFerrorf(ERR_WARNING,
|
|
"Noise measurement at a single frequency %g only!\n",
|
|
job->NstartFreq);
|
|
}
|
|
}
|
|
else {
|
|
if (frequequal) {
|
|
job->NstopFreq = job->NstartFreq;
|
|
job->NnumSteps = 1;
|
|
SPfrontEnd->IFerrorf(ERR_WARNING,
|
|
"Noise measurement at a single frequency %g only!\n",
|
|
job->NstartFreq);
|
|
}
|
|
}
|
|
/* see if the source specified is AC */
|
|
{
|
|
bool ac_given = FALSE;
|
|
|
|
if (!inst || inst->GENmodPtr->GENmodType < 0) {
|
|
SPfrontEnd->IFerrorf(ERR_WARNING,
|
|
"Noise input source %s not in circuit",
|
|
job->input);
|
|
return E_NOTFOUND;
|
|
}
|
|
|
|
if (inst->GENmodPtr->GENmodType == CKTtypelook("Vsource")) {
|
|
ac_given = ((VSRCinstance*)inst)->VSRCacGiven;
|
|
src_type = SV_VOLTAGE;
|
|
}
|
|
else if (inst->GENmodPtr->GENmodType == CKTtypelook("Isource")) {
|
|
ac_given = ((ISRCinstance*)inst)->ISRCacGiven;
|
|
src_type = SV_CURRENT;
|
|
}
|
|
else {
|
|
SPfrontEnd->IFerrorf(ERR_WARNING,
|
|
"Noise input source %s is not of proper type",
|
|
job->input);
|
|
return E_NOTFOUND;
|
|
}
|
|
|
|
if (!ac_given) {
|
|
SPfrontEnd->IFerrorf(ERR_WARNING,
|
|
"Noise input source %s has no AC value",
|
|
job->input);
|
|
return E_NOACINPUT;
|
|
}
|
|
}
|
|
|
|
if ((job->NsavFstp == 0.0) || restart) { /* va, NsavFstp is double */
|
|
switch (job->NstpType) {
|
|
|
|
|
|
case DECADE:
|
|
job->NfreqDelta = exp(log(10.0) /
|
|
job->NnumSteps);
|
|
break;
|
|
|
|
case OCTAVE:
|
|
job->NfreqDelta = exp(log(2.0) /
|
|
job->NnumSteps);
|
|
break;
|
|
|
|
case LINEAR:
|
|
if (job->NnumSteps == 1)
|
|
job->NfreqDelta = 0;
|
|
else
|
|
job->NfreqDelta = (job->NstopFreq -
|
|
job->NstartFreq) / (job->NnumSteps - 1);
|
|
break;
|
|
|
|
default:
|
|
return(E_BADPARM);
|
|
}
|
|
|
|
|
|
#ifdef XSPICE
|
|
/* gtri - begin - wbk - Call EVTop if event-driven instances exist */
|
|
|
|
if (ckt->evt->counts.num_insts != 0) {
|
|
error = EVTop(ckt,
|
|
(ckt->CKTmode & MODEUIC) | MODEDCOP | MODEINITJCT,
|
|
(ckt->CKTmode & MODEUIC) | MODEDCOP | MODEINITFLOAT,
|
|
ckt->CKTdcMaxIter,
|
|
MIF_TRUE);
|
|
EVTdump(ckt, IPC_ANAL_DCOP, 0.0);
|
|
EVTop_save(ckt, MIF_TRUE, 0.0);
|
|
}
|
|
else
|
|
{ // start of no XSPICE event-driven instances
|
|
#endif
|
|
|
|
#ifdef KLU
|
|
if (ckt->CKTmatrix->CKTkluMODE)
|
|
{
|
|
/* Conversion from Complex Matrix to Real Matrix */
|
|
for (i = 0 ; i < DEVmaxnum ; i++)
|
|
if (DEVices [i] && DEVices [i]->DEVbindCSCComplexToReal && ckt->CKThead [i])
|
|
DEVices [i]->DEVbindCSCComplexToReal (ckt->CKThead [i], ckt) ;
|
|
|
|
ckt->CKTmatrix->SMPkluMatrix->KLUmatrixIsComplex = KLUmatrixReal ;
|
|
}
|
|
#endif
|
|
|
|
/* If no event-driven instances, do what SPICE normally does */
|
|
if (!ckt->CKTnoopac) { /* skip OP if option NOOPAC is set and circuit is linear */
|
|
error = CKTop(ckt,
|
|
(ckt->CKTmode & MODEUIC) | MODEDCOP | MODEINITJCT,
|
|
(ckt->CKTmode & MODEUIC) | MODEDCOP | MODEINITFLOAT,
|
|
ckt->CKTdcMaxIter);
|
|
|
|
if (error) {
|
|
fprintf(stdout, "\nNOISE operating point failed -\n");
|
|
CKTncDump(ckt);
|
|
return(error);
|
|
}
|
|
}
|
|
else {
|
|
fprintf(stdout, "\n Linear circuit, option noopac given: no OP analysis\n");
|
|
}
|
|
} // end of no XSPICE event-driven instances
|
|
|
|
/* Patch to noisean.c by Richard D. McRoberts. */
|
|
ckt->CKTmode = (ckt->CKTmode & MODEUIC) | MODEDCOP | MODEINITSMSIG;
|
|
error = CKTload(ckt);
|
|
if (error) return(error);
|
|
|
|
if (ckt->CKTkeepOpInfo) {
|
|
error = CKTnames(ckt, &numNames, &nameList);
|
|
if (error) return(error);
|
|
/* Dump operating point. */
|
|
error = SPfrontEnd->OUTpBeginPlot(ckt, ckt->CKTcurJob,
|
|
"NOISE Operating Point",
|
|
NULL, IF_REAL,
|
|
numNames, nameList, IF_REAL,
|
|
&plot);
|
|
txfree(nameList);
|
|
if (error) return(error);
|
|
CKTdump(ckt, 0.0, plot);
|
|
SPfrontEnd->OUTendPlot(plot);
|
|
plot = NULL;
|
|
}
|
|
|
|
data = TMALLOC(Ndata, 1);
|
|
step = 0;
|
|
data->freq = job->NstartFreq;
|
|
data->outNoiz = 0.0;
|
|
data->inNoise = 0.0;
|
|
data->squared = cp_getvar("sqrnoise", CP_BOOL, NULL, 0) ? 1 : 0;
|
|
|
|
/* the current front-end needs the namelist to be fully
|
|
declared before an OUTpBeginplot */
|
|
|
|
SPfrontEnd->IFnewUid(ckt, &freqUid, NULL, "frequency", UID_OTHER, NULL);
|
|
|
|
data->numPlots = 0; /* we don't have any plots yet */
|
|
error = CKTnoise(ckt, N_DENS, N_OPEN, data);
|
|
if (error) return(error);
|
|
|
|
/*
|
|
* all names in the namelist have been declared. now start the
|
|
* plot
|
|
*/
|
|
|
|
if (src_type == SV_VOLTAGE)
|
|
fixme_inoise_type =
|
|
data->squared ? SV_SQR_VOLTAGE_DENSITY : SV_VOLTAGE_DENSITY;
|
|
else
|
|
fixme_inoise_type =
|
|
data->squared ? SV_SQR_CURRENT_DENSITY : SV_CURRENT_DENSITY;
|
|
|
|
fixme_onoise_type =
|
|
data->squared ? SV_SQR_VOLTAGE_DENSITY : SV_VOLTAGE_DENSITY;
|
|
|
|
if (!data->squared)
|
|
for (i = 0; i < data->numPlots; i++)
|
|
data->squared_value[i] =
|
|
ciprefix("inoise", data->namelist[i]) ||
|
|
ciprefix("onoise", data->namelist[i]);
|
|
|
|
error = SPfrontEnd->OUTpBeginPlot(ckt, ckt->CKTcurJob,
|
|
data->squared
|
|
? "Noise Spectral Density Curves - (V^2 or A^2)/Hz"
|
|
|
|
: "Noise Spectral Density Curves",
|
|
freqUid, IF_REAL,
|
|
data->numPlots, data->namelist, IF_REAL,
|
|
&(data->NplotPtr));
|
|
if (error) return(error);
|
|
|
|
if (job->NstpType != LINEAR) {
|
|
SPfrontEnd->OUTattributes(data->NplotPtr, NULL, OUT_SCALE_LOG, NULL);
|
|
}
|
|
|
|
}
|
|
else { /* we must have paused before. pick up where we left off */
|
|
step = (int)(job->NsavFstp);
|
|
switch (job->NstpType) {
|
|
|
|
case DECADE:
|
|
case OCTAVE:
|
|
data->freq = job->NstartFreq * exp(step *
|
|
log(job->NfreqDelta));
|
|
break;
|
|
|
|
case LINEAR:
|
|
data->freq = job->NstartFreq + step *
|
|
job->NfreqDelta;
|
|
break;
|
|
|
|
default:
|
|
return(E_BADPARM);
|
|
|
|
}
|
|
job->NsavFstp = 0;
|
|
data->outNoiz = job->NsavOnoise;
|
|
data->inNoise = job->NsavInoise;
|
|
/* saj resume rawfile fix*/
|
|
error = SPfrontEnd->OUTpBeginPlot(NULL, NULL,
|
|
NULL,
|
|
NULL, 0,
|
|
666, NULL, 666,
|
|
&(data->NplotPtr));
|
|
/*saj*/
|
|
}
|
|
|
|
switch (job->NstpType) {
|
|
case DECADE:
|
|
case OCTAVE:
|
|
freqTol = job->NfreqDelta * job->NstopFreq * ckt->CKTreltol;
|
|
break;
|
|
case LINEAR:
|
|
freqTol = job->NfreqDelta * ckt->CKTreltol;
|
|
break;
|
|
default:
|
|
return(E_BADPARM);
|
|
}
|
|
|
|
data->lstFreq = data->freq;
|
|
|
|
#ifdef XSPICE
|
|
/* gtri - add - wbk - 12/19/90 - Set anal_init and anal_type */
|
|
|
|
g_mif_info.circuit.anal_init = MIF_TRUE;
|
|
|
|
/* Tell the code models what mode we're in */
|
|
/* MIF_NOI is not yet supported by code models, so use their AC capabilities */
|
|
g_mif_info.circuit.anal_type = MIF_AC;
|
|
|
|
/* gtri - end - wbk */
|
|
#endif
|
|
|
|
#ifdef KLU
|
|
if (ckt->CKTmatrix->CKTkluMODE)
|
|
{
|
|
/* Conversion from Real Matrix to Complex Matrix */
|
|
if (!ckt->CKTmatrix->SMPkluMatrix->KLUmatrixIsComplex)
|
|
{
|
|
for (i = 0 ; i < DEVmaxnum ; i++)
|
|
if (DEVices [i] && DEVices [i]->DEVbindCSCComplex && ckt->CKThead [i])
|
|
DEVices [i]->DEVbindCSCComplex (ckt->CKThead [i], ckt) ;
|
|
|
|
ckt->CKTmatrix->SMPkluMatrix->KLUmatrixIsComplex = KLUMatrixComplex ;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* do the noise analysis over all frequencies */
|
|
|
|
while (data->freq <= job->NstopFreq + freqTol) {
|
|
if (SPfrontEnd->IFpauseTest()) {
|
|
job->NsavFstp = step; /* save our results */
|
|
job->NsavOnoise = data->outNoiz; /* up until now */
|
|
job->NsavInoise = data->inNoise;
|
|
return (E_PAUSE);
|
|
}
|
|
|
|
/* Update opertating point, if variable 'hertz' is given */
|
|
if (ckt->CKTvarHertz) {
|
|
|
|
#ifdef KLU
|
|
if (ckt->CKTmatrix->CKTkluMODE)
|
|
{
|
|
/* Conversion from Complex Matrix to Real Matrix */
|
|
for (i = 0; i < DEVmaxnum; i++)
|
|
if (DEVices[i] && DEVices[i]->DEVbindCSCComplexToReal && ckt->CKThead[i])
|
|
DEVices[i]->DEVbindCSCComplexToReal(ckt->CKThead[i], ckt);
|
|
|
|
ckt->CKTmatrix->SMPkluMatrix->KLUmatrixIsComplex = KLUmatrixReal;
|
|
}
|
|
#endif
|
|
|
|
#ifdef XSPICE
|
|
/* Call EVTop if event-driven instances exist */
|
|
|
|
if (ckt->evt->counts.num_insts != 0) {
|
|
error = EVTop(ckt,
|
|
(ckt->CKTmode & MODEUIC) | MODEDCOP | MODEINITJCT,
|
|
(ckt->CKTmode & MODEUIC) | MODEDCOP | MODEINITFLOAT,
|
|
ckt->CKTdcMaxIter,
|
|
MIF_TRUE);
|
|
EVTdump(ckt, IPC_ANAL_DCOP, 0.0);
|
|
EVTop_save(ckt, MIF_TRUE, 0.0);
|
|
}
|
|
else
|
|
#endif
|
|
// If no event-driven instances, do what SPICE normally does
|
|
error = CKTop(ckt,
|
|
(ckt->CKTmode & MODEUIC) | MODEDCOP | MODEINITJCT,
|
|
(ckt->CKTmode & MODEUIC) | MODEDCOP | MODEINITFLOAT,
|
|
ckt->CKTdcMaxIter);
|
|
|
|
if (error) {
|
|
fprintf(stderr, "\nError: AC operating point with variable 'Hertz' for noise sim failed -\n");
|
|
CKTncDump(ckt);
|
|
return(error);
|
|
}
|
|
ckt->CKTmode = (ckt->CKTmode & MODEUIC) | MODEDCOP | MODEINITSMSIG;
|
|
error = CKTload(ckt);
|
|
if (error) return(error);
|
|
|
|
#ifdef KLU
|
|
if (ckt->CKTmatrix->CKTkluMODE)
|
|
{
|
|
/* Conversion from Real Matrix to Complex Matrix */
|
|
for (i = 0; i < DEVmaxnum; i++)
|
|
if (DEVices[i] && DEVices[i]->DEVbindCSCComplex && ckt->CKThead[i])
|
|
DEVices[i]->DEVbindCSCComplex(ckt->CKThead[i], ckt);
|
|
|
|
ckt->CKTmatrix->SMPkluMatrix->KLUmatrixIsComplex = KLUMatrixComplex;
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
ckt->CKTomega = 2.0 * M_PI * data->freq;
|
|
ckt->CKTmode = (ckt->CKTmode & MODEUIC) | MODEAC | MODEACNOISE;
|
|
ckt->noise_input = inst;
|
|
|
|
/*
|
|
* solve the original AC system to get the transfer
|
|
* function between the input and output
|
|
*/
|
|
|
|
NIacIter(ckt);
|
|
realVal = ckt->CKTrhsOld[posOutNode]
|
|
- ckt->CKTrhsOld[negOutNode];
|
|
imagVal = ckt->CKTirhsOld[posOutNode]
|
|
- ckt->CKTirhsOld[negOutNode];
|
|
data->GainSqInv = 1.0 / MAX(((realVal * realVal)
|
|
+ (imagVal * imagVal)), N_MINGAIN);
|
|
data->lnGainInv = log(data->GainSqInv);
|
|
|
|
/* set up a block of "common" data so we don't have to
|
|
* recalculate it for every device
|
|
*/
|
|
|
|
data->delFreq = data->freq - data->lstFreq;
|
|
data->lnFreq = log(MAX(data->freq, N_MINLOG));
|
|
data->lnLastFreq = log(MAX(data->lstFreq, N_MINLOG));
|
|
data->delLnFreq = data->lnFreq - data->lnLastFreq;
|
|
|
|
if ((job->NStpsSm != 0) && ((step % (job->NStpsSm)) == 0)) {
|
|
data->prtSummary = TRUE;
|
|
}
|
|
else {
|
|
data->prtSummary = FALSE;
|
|
}
|
|
|
|
/*
|
|
data->outNumber = 1;
|
|
*/
|
|
|
|
data->outNumber = 0;
|
|
/* the frequency will NOT be stored in array[0] as before; instead,
|
|
* it will be given in refVal.rValue (see later)
|
|
*/
|
|
|
|
NInzIter(ckt, posOutNode, negOutNode); /* solve the adjoint system */
|
|
|
|
/* now we use the adjoint system to calculate the noise
|
|
* contributions of each generator in the circuit
|
|
*/
|
|
|
|
error = CKTnoise(ckt, N_DENS, N_CALC, data);
|
|
if (error) return(error);
|
|
data->lstFreq = data->freq;
|
|
|
|
/* update the frequency */
|
|
|
|
switch (job->NstpType) {
|
|
|
|
case DECADE:
|
|
case OCTAVE:
|
|
data->freq *= job->NfreqDelta;
|
|
break;
|
|
|
|
case LINEAR:
|
|
data->freq += job->NfreqDelta;
|
|
break;
|
|
|
|
default:
|
|
return(E_INTERN);
|
|
}
|
|
step++;
|
|
|
|
if ((job->NnumSteps == 1) && (job->NstpType == LINEAR))
|
|
break;
|
|
}
|
|
|
|
error = CKTnoise(ckt, N_DENS, N_CLOSE, data);
|
|
if (error) return(error);
|
|
|
|
data->numPlots = 0;
|
|
data->outNumber = 0;
|
|
|
|
if (job->NstartFreq != job->NstopFreq) {
|
|
error = CKTnoise(ckt, INT_NOIZ, N_OPEN, data);
|
|
|
|
if (error) return(error);
|
|
|
|
if (src_type == SV_VOLTAGE)
|
|
fixme_inoise_type =
|
|
data->squared ? SV_SQR_VOLTAGE : SV_VOLTAGE;
|
|
else
|
|
fixme_inoise_type =
|
|
data->squared ? SV_SQR_CURRENT : SV_CURRENT;
|
|
|
|
fixme_onoise_type =
|
|
data->squared ? SV_SQR_VOLTAGE : SV_VOLTAGE;
|
|
|
|
if (!data->squared)
|
|
for (i = 0; i < data->numPlots; i++)
|
|
data->squared_value[i] =
|
|
ciprefix("inoise", data->namelist[i]) ||
|
|
ciprefix("onoise", data->namelist[i]);
|
|
|
|
SPfrontEnd->OUTpBeginPlot(ckt, ckt->CKTcurJob,
|
|
data->squared
|
|
? "Integrated Noise - V^2 or A^2"
|
|
: "Integrated Noise",
|
|
NULL, 0,
|
|
data->numPlots, data->namelist, IF_REAL,
|
|
&(data->NplotPtr));
|
|
|
|
error = CKTnoise(ckt, INT_NOIZ, N_CALC, data);
|
|
if (error) return(error);
|
|
|
|
error = CKTnoise(ckt, INT_NOIZ, N_CLOSE, data);
|
|
if (error) return(error);
|
|
}
|
|
|
|
FREE(data);
|
|
return(OK);
|
|
}
|