|
|
@ -17,14 +17,20 @@ Modified: Apr 2000 - Paolo Nenzi |
|
|
* and traverses all of its instances. It then proceeds to any other |
|
|
* and traverses all of its instances. It then proceeds to any other |
|
|
* models on the linked list. The total output noise density |
|
|
* models on the linked list. The total output noise density |
|
|
* generated by all the resistors is summed in the variable "OnDens". |
|
|
* generated by all the resistors is summed in the variable "OnDens". |
|
|
|
|
|
* |
|
|
|
|
|
* Paolo Nenzi 2003: |
|
|
|
|
|
* Added flicker noise (Kf-Af) calculation to simulate |
|
|
|
|
|
* carbon resistors. |
|
|
|
|
|
* |
|
|
|
|
|
* Added "noisy" switch to simulate noiseless resistors. |
|
|
*/ |
|
|
*/ |
|
|
|
|
|
|
|
|
extern void NevalSrc(); |
|
|
|
|
|
|
|
|
extern void NevalSrc2(); |
|
|
extern double Nintegrate(); |
|
|
extern double Nintegrate(); |
|
|
|
|
|
|
|
|
int |
|
|
int |
|
|
RESnoise (int mode, int operation, GENmodel *genmodel, CKTcircuit *ckt, |
|
|
RESnoise (int mode, int operation, GENmodel *genmodel, CKTcircuit *ckt, |
|
|
Ndata *data, double *OnDens) |
|
|
|
|
|
|
|
|
Ndata *data, double *OnDens) |
|
|
{ |
|
|
{ |
|
|
RESmodel *firstModel = (RESmodel *) genmodel; |
|
|
RESmodel *firstModel = (RESmodel *) genmodel; |
|
|
RESmodel *model; |
|
|
RESmodel *model; |
|
|
@ -32,61 +38,87 @@ RESnoise (int mode, int operation, GENmodel *genmodel, CKTcircuit *ckt, |
|
|
char name[N_MXVLNTH]; |
|
|
char name[N_MXVLNTH]; |
|
|
double tempOutNoise; |
|
|
double tempOutNoise; |
|
|
double tempInNoise; |
|
|
double tempInNoise; |
|
|
double noizDens; |
|
|
|
|
|
double lnNdens; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (model=firstModel; model != NULL; model=model->RESnextModel) { |
|
|
|
|
|
for (inst=model->RESinstances; inst != NULL; inst=inst->RESnextInstance) { |
|
|
|
|
|
|
|
|
double noizDens[RESNSRCS]; |
|
|
|
|
|
double lnNdens[RESNSRCS]; |
|
|
|
|
|
int i; |
|
|
|
|
|
|
|
|
|
|
|
/* define the names of the noise sources */ |
|
|
|
|
|
|
|
|
|
|
|
static char *RESnNames[RESNSRCS] = { |
|
|
|
|
|
/* Note that we have to keep the order consistent with the |
|
|
|
|
|
* strchr definitions in RESdefs.h */ |
|
|
|
|
|
"_thermal", /* Thermal noise */ |
|
|
|
|
|
"_1overf", /* flicker (1/f) noise */ |
|
|
|
|
|
"" /* total resistor noise */ |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (model = firstModel; model != NULL; model = model->RESnextModel) { |
|
|
|
|
|
for (inst = model->RESinstances; inst != NULL; |
|
|
|
|
|
inst = inst->RESnextInstance) { |
|
|
|
|
|
|
|
|
if (inst->RESowner != ARCHme) continue; |
|
|
if (inst->RESowner != ARCHme) continue; |
|
|
|
|
|
|
|
|
|
|
|
if(!inst->RESnoisy) continue; /* Quiet resistors are skipped */ |
|
|
|
|
|
|
|
|
switch (operation) { |
|
|
switch (operation) { |
|
|
|
|
|
|
|
|
case N_OPEN: |
|
|
case N_OPEN: |
|
|
|
|
|
|
|
|
/* see if we have to to produce a summary report */ |
|
|
|
|
|
/* if so, name the noise generator */ |
|
|
|
|
|
|
|
|
/* |
|
|
|
|
|
* See if we have to to produce a summary report |
|
|
|
|
|
* if so, name the noise generator |
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
if (((NOISEAN*)ckt->CKTcurJob)->NStpsSm != 0) { |
|
|
if (((NOISEAN*)ckt->CKTcurJob)->NStpsSm != 0) { |
|
|
switch (mode) { |
|
|
switch (mode) { |
|
|
|
|
|
|
|
|
case N_DENS: |
|
|
case N_DENS: |
|
|
(void)sprintf(name,"onoise_%s",inst->RESname); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
data->namelist = (IFuid *)trealloc((char *)data->namelist,(data->numPlots + 1)*sizeof(IFuid)); |
|
|
|
|
|
if (!data->namelist) return(E_NOMEM); |
|
|
|
|
|
(*(SPfrontEnd->IFnewUid))(ckt, |
|
|
|
|
|
&(data->namelist[data->numPlots++]), |
|
|
|
|
|
(IFuid)NULL,name,UID_OTHER,(void **)NULL); |
|
|
|
|
|
|
|
|
for (i=0; i < RESNSRCS; i++) { |
|
|
|
|
|
(void)sprintf(name,"onoise_%s%s", |
|
|
|
|
|
inst->RESname, RESnNames[i]); |
|
|
|
|
|
|
|
|
|
|
|
data->namelist = (IFuid *) |
|
|
|
|
|
trealloc((char *)data->namelist, |
|
|
|
|
|
(data->numPlots + 1)*sizeof(IFuid)); |
|
|
|
|
|
if (!data->namelist) return(E_NOMEM); |
|
|
|
|
|
(*(SPfrontEnd->IFnewUid))(ckt, |
|
|
|
|
|
&(data->namelist[data->numPlots++]), |
|
|
|
|
|
(IFuid)NULL,name,UID_OTHER,(void **)NULL); |
|
|
/* we've added one more plot */ |
|
|
/* we've added one more plot */ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case INT_NOIZ: |
|
|
case INT_NOIZ: |
|
|
(void)sprintf(name,"onoise_total_%s",inst->RESname); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
data->namelist = (IFuid *)trealloc((char *)data->namelist,(data->numPlots + 1)*sizeof(IFuid)); |
|
|
|
|
|
if (!data->namelist) return(E_NOMEM); |
|
|
|
|
|
(*(SPfrontEnd->IFnewUid))(ckt, |
|
|
|
|
|
&(data->namelist[data->numPlots++]), |
|
|
|
|
|
(IFuid)NULL,name,UID_OTHER,(void **)NULL); |
|
|
|
|
|
|
|
|
for (i=0; i < RESNSRCS; i++) { |
|
|
|
|
|
(void)sprintf(name,"onoise_total_%s%s", |
|
|
|
|
|
inst->RESname, RESnNames[i]); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
data->namelist = (IFuid *) |
|
|
|
|
|
trealloc((char *)data->namelist, |
|
|
|
|
|
(data->numPlots + 1)*sizeof(IFuid)); |
|
|
|
|
|
if (!data->namelist) return(E_NOMEM); |
|
|
|
|
|
(*(SPfrontEnd->IFnewUid))(ckt, |
|
|
|
|
|
&(data->namelist[data->numPlots++]), |
|
|
|
|
|
(IFuid)NULL,name,UID_OTHER,(void **)NULL); |
|
|
/* we've added one more plot */ |
|
|
/* we've added one more plot */ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(void)sprintf(name,"inoise_total_%s",inst->RESname); |
|
|
|
|
|
|
|
|
(void)sprintf(name,"inoise_total_%s%s", |
|
|
|
|
|
inst->RESname,RESnNames[i]); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
data->namelist = (IFuid *)trealloc((char *)data->namelist,(data->numPlots + 1)*sizeof(IFuid)); |
|
|
|
|
|
if (!data->namelist) return(E_NOMEM); |
|
|
|
|
|
(*(SPfrontEnd->IFnewUid))(ckt, |
|
|
|
|
|
&(data->namelist[data->numPlots++]), |
|
|
|
|
|
(IFuid)NULL,name,UID_OTHER,(void **)NULL); |
|
|
|
|
|
|
|
|
data->namelist = (IFuid *) |
|
|
|
|
|
trealloc((char *)data->namelist, |
|
|
|
|
|
(data->numPlots + 1)*sizeof(IFuid)); |
|
|
|
|
|
if (!data->namelist) return(E_NOMEM); |
|
|
|
|
|
(*(SPfrontEnd->IFnewUid))(ckt, |
|
|
|
|
|
&(data->namelist[data->numPlots++]), |
|
|
|
|
|
(IFuid)NULL,name,UID_OTHER,(void **)NULL); |
|
|
/* we've added one more plot */ |
|
|
/* we've added one more plot */ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
break; |
|
|
break; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
@ -96,46 +128,86 @@ if (!data->namelist) return(E_NOMEM); |
|
|
switch (mode) { |
|
|
switch (mode) { |
|
|
|
|
|
|
|
|
case N_DENS: |
|
|
case N_DENS: |
|
|
NevalSrc(&noizDens,&lnNdens,ckt,THERMNOISE, |
|
|
|
|
|
inst->RESposNode,inst->RESnegNode,inst->RESconduct); |
|
|
|
|
|
|
|
|
|
|
|
*OnDens += noizDens; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NevalSrc2(&noizDens[RESTHNOIZ],&lnNdens[RESTHNOIZ], |
|
|
|
|
|
ckt,THERMNOISE, inst->RESposNode,inst->RESnegNode, |
|
|
|
|
|
inst->RESconduct * inst->RESm, inst->RESdtemp); |
|
|
|
|
|
|
|
|
|
|
|
NevalSrc2(&noizDens[RESFLNOIZ],(double*)NULL, ckt, |
|
|
|
|
|
N_GAIN,inst->RESposNode, inst->RESnegNode, |
|
|
|
|
|
(double)0.0, (double)0.0); |
|
|
|
|
|
|
|
|
|
|
|
#if 0 |
|
|
|
|
|
printf("DC current in resistor %s: %e\n",inst->RESname, inst->REScurrent); |
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
noizDens[RESFLNOIZ] *= inst->RESm * model->RESfNcoef * exp(model->RESfNexp * |
|
|
|
|
|
log(MAX(fabs(inst->REScurrent), |
|
|
|
|
|
N_MINLOG))) / data->freq; |
|
|
|
|
|
lnNdens[RESFLNOIZ] = log(MAX(noizDens[RESFLNOIZ],N_MINLOG)); |
|
|
|
|
|
|
|
|
|
|
|
noizDens[RESTOTNOIZ] = noizDens[RESTHNOIZ] + noizDens[RESFLNOIZ]; |
|
|
|
|
|
lnNdens[RESTOTNOIZ] = log(noizDens[RESTOTNOIZ]); |
|
|
|
|
|
|
|
|
|
|
|
*OnDens += noizDens[RESTOTNOIZ]; |
|
|
|
|
|
|
|
|
if (data->delFreq == 0.0) { |
|
|
if (data->delFreq == 0.0) { |
|
|
|
|
|
|
|
|
/* if we haven't done any previous integration, we need to */ |
|
|
/* if we haven't done any previous integration, we need to */ |
|
|
/* initialize our "history" variables */ |
|
|
/* initialize our "history" variables */ |
|
|
|
|
|
|
|
|
inst->RESnVar[LNLSTDENS] = lnNdens; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (i=0; i < RESNSRCS; i++) { |
|
|
|
|
|
inst->RESnVar[LNLSTDENS][i] = lnNdens[i]; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
/* clear out our integration variable if it's the first pass */ |
|
|
/* clear out our integration variable if it's the first pass */ |
|
|
|
|
|
|
|
|
if (data->freq == ((NOISEAN*)ckt->CKTcurJob)->NstartFreq) { |
|
|
if (data->freq == ((NOISEAN*)ckt->CKTcurJob)->NstartFreq) { |
|
|
inst->RESnVar[OUTNOIZ] = 0.0; |
|
|
|
|
|
inst->RESnVar[INNOIZ] = 0.0; /* Clear input noise */ |
|
|
|
|
|
|
|
|
for (i=0; i < RESNSRCS; i++) { |
|
|
|
|
|
inst->RESnVar[OUTNOIZ][i] = 0.0; /* Clear output noise */ |
|
|
|
|
|
inst->RESnVar[INNOIZ][i] = 0.0; /* Clear input noise */ |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} else { /* data->delFreq != 0.0 (we have to integrate) */ |
|
|
} else { /* data->delFreq != 0.0 (we have to integrate) */ |
|
|
tempOutNoise = Nintegrate(noizDens, lnNdens, |
|
|
|
|
|
inst->RESnVar[LNLSTDENS], data); |
|
|
|
|
|
tempInNoise = Nintegrate(noizDens * |
|
|
|
|
|
data->GainSqInv ,lnNdens + data->lnGainInv, |
|
|
|
|
|
inst->RESnVar[LNLSTDENS] + data->lnGainInv, |
|
|
|
|
|
data); |
|
|
|
|
|
inst->RESnVar[OUTNOIZ] += tempOutNoise; |
|
|
|
|
|
inst->RESnVar[INNOIZ] += tempInNoise; |
|
|
|
|
|
data->outNoiz += tempOutNoise; |
|
|
|
|
|
data->inNoise += tempInNoise; |
|
|
|
|
|
inst->RESnVar[LNLSTDENS] = lnNdens; |
|
|
|
|
|
} |
|
|
|
|
|
if (data->prtSummary) { |
|
|
|
|
|
data->outpVector[data->outNumber++] = noizDens; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* In order to get the best curve fit, we have to integrate each component separately */ |
|
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < RESNSRCS; i++) { |
|
|
|
|
|
if (i != RESTOTNOIZ) { |
|
|
|
|
|
tempOutNoise = Nintegrate(noizDens[i], lnNdens[i], |
|
|
|
|
|
inst->RESnVar[LNLSTDENS][i], data); |
|
|
|
|
|
tempInNoise = Nintegrate(noizDens[i] * |
|
|
|
|
|
data->GainSqInv ,lnNdens[i] |
|
|
|
|
|
+ data->lnGainInv, |
|
|
|
|
|
inst->RESnVar[LNLSTDENS][i] |
|
|
|
|
|
+ data->lnGainInv, |
|
|
|
|
|
data); |
|
|
|
|
|
inst->RESnVar[LNLSTDENS][i] = lnNdens[i]; |
|
|
|
|
|
data->outNoiz += tempOutNoise; |
|
|
|
|
|
data->inNoise += tempInNoise; |
|
|
|
|
|
if (((NOISEAN*)ckt->CKTcurJob)->NStpsSm != 0) { |
|
|
|
|
|
inst->RESnVar[OUTNOIZ][i] += tempOutNoise; |
|
|
|
|
|
inst->RESnVar[OUTNOIZ][RESTOTNOIZ] += tempOutNoise; |
|
|
|
|
|
inst->RESnVar[INNOIZ][i] += tempInNoise; |
|
|
|
|
|
inst->RESnVar[INNOIZ][RESTOTNOIZ] += tempInNoise; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
if (data->prtSummary) { |
|
|
|
|
|
for (i=0; i < RESNSRCS; i++) { /* print a summary report */ |
|
|
|
|
|
data->outpVector[data->outNumber++] = noizDens[i]; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case INT_NOIZ: /* already calculated, just output */ |
|
|
case INT_NOIZ: /* already calculated, just output */ |
|
|
if (((NOISEAN*)ckt->CKTcurJob)->NStpsSm != 0) { |
|
|
if (((NOISEAN*)ckt->CKTcurJob)->NStpsSm != 0) { |
|
|
data->outpVector[data->outNumber++] = inst->RESnVar[OUTNOIZ]; |
|
|
|
|
|
data->outpVector[data->outNumber++] = inst->RESnVar[INNOIZ]; |
|
|
|
|
|
|
|
|
for (i=0; i < RESNSRCS; i++) { |
|
|
|
|
|
data->outpVector[data->outNumber++] = inst->RESnVar[OUTNOIZ][i]; |
|
|
|
|
|
data->outpVector[data->outNumber++] = inst->RESnVar[INNOIZ][i]; |
|
|
|
|
|
} |
|
|
} /* if */ |
|
|
} /* if */ |
|
|
break; |
|
|
break; |
|
|
} /* switch (mode) */ |
|
|
} /* switch (mode) */ |
|
|
|