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.
384 lines
13 KiB
384 lines
13 KiB
/**********
|
|
Copyright 1990 Regents of the University of California. All rights reserved.
|
|
Author: 1995 Gary W. Ng and Min-Chie Jeng.
|
|
File: b3v2noi.c
|
|
**********/
|
|
|
|
#include "ngspice.h"
|
|
#include <stdio.h>
|
|
#include <math.h>
|
|
#include "bsim3v2def.h"
|
|
#include "cktdefs.h"
|
|
#include "iferrmsg.h"
|
|
#include "noisedef.h"
|
|
#include "suffix.h"
|
|
#include "const.h" /* jwan */
|
|
|
|
/*
|
|
* BSIM3V2noise (mode, operation, firstModel, ckt, data, OnDens)
|
|
* This routine names and evaluates all of the noise sources
|
|
* associated with MOSFET's. It starts with the model *firstModel and
|
|
* traverses all of its insts. It then proceeds to any other models
|
|
* on the linked list. The total output noise density generated by
|
|
* all of the MOSFET's is summed with the variable "OnDens".
|
|
*/
|
|
|
|
/*
|
|
Channel thermal and flicker noises are calculated based on the value
|
|
of model->BSIM3V2noiMod.
|
|
If model->BSIM3V2noiMod = 1,
|
|
Channel thermal noise = SPICE2 model
|
|
Flicker noise = SPICE2 model
|
|
If model->BSIM3V2noiMod = 2,
|
|
Channel thermal noise = BSIM3V2 model
|
|
Flicker noise = BSIM3V2 model
|
|
If model->BSIM3V2noiMod = 3,
|
|
Channel thermal noise = SPICE2 model
|
|
Flicker noise = BSIM3V2 model
|
|
If model->BSIM3V2noiMod = 4,
|
|
Channel thermal noise = BSIM3V2 model
|
|
Flicker noise = SPICE2 model
|
|
*/
|
|
|
|
extern void NevalSrc();
|
|
extern double Nintegrate();
|
|
|
|
double
|
|
BSIM3V2StrongInversionNoiseEval(vgs, vds, model, here, freq, temp)
|
|
double vgs, vds, freq, temp;
|
|
BSIM3V2model *model;
|
|
BSIM3V2instance *here;
|
|
{
|
|
struct BSIM3V2SizeDependParam *pParam;
|
|
double cd, esat, DelClm, EffFreq, N0, Nl, Vgst;
|
|
double T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, Ssi;
|
|
|
|
pParam = here->pParam;
|
|
cd = fabs(here->BSIM3V2cd);
|
|
if (vds > here->BSIM3V2vdsat)
|
|
{ esat = 2.0 * pParam->BSIM3V2vsattemp / here->BSIM3V2ueff;
|
|
T0 = ((((vds - here->BSIM3V2vdsat) / pParam->BSIM3V2litl) + model->BSIM3V2em)
|
|
/ esat);
|
|
DelClm = pParam->BSIM3V2litl * log (MAX(T0, N_MINLOG));
|
|
}
|
|
else
|
|
DelClm = 0.0;
|
|
EffFreq = pow(freq, model->BSIM3V2ef);
|
|
T1 = CHARGE * CHARGE * 8.62e-5 * cd * temp * here->BSIM3V2ueff;
|
|
T2 = 1.0e8 * EffFreq * model->BSIM3V2cox
|
|
* pParam->BSIM3V2leff * pParam->BSIM3V2leff;
|
|
Vgst = vgs - here->BSIM3V2von;
|
|
N0 = model->BSIM3V2cox * Vgst / CHARGE;
|
|
if (N0 < 0.0)
|
|
N0 = 0.0;
|
|
Nl = model->BSIM3V2cox * (Vgst - MIN(vds, here->BSIM3V2vdsat)) / CHARGE;
|
|
if (Nl < 0.0)
|
|
Nl = 0.0;
|
|
|
|
T3 = model->BSIM3V2oxideTrapDensityA
|
|
* log(MAX(((N0 + 2.0e14) / (Nl + 2.0e14)), N_MINLOG));
|
|
T4 = model->BSIM3V2oxideTrapDensityB * (N0 - Nl);
|
|
T5 = model->BSIM3V2oxideTrapDensityC * 0.5 * (N0 * N0 - Nl * Nl);
|
|
|
|
T6 = 8.62e-5 * temp * cd * cd;
|
|
T7 = 1.0e8 * EffFreq * pParam->BSIM3V2leff
|
|
* pParam->BSIM3V2leff * pParam->BSIM3V2weff;
|
|
T8 = model->BSIM3V2oxideTrapDensityA + model->BSIM3V2oxideTrapDensityB * Nl
|
|
+ model->BSIM3V2oxideTrapDensityC * Nl * Nl;
|
|
T9 = (Nl + 2.0e14) * (Nl + 2.0e14);
|
|
|
|
Ssi = T1 / T2 * (T3 + T4 + T5) + T6 / T7 * DelClm * T8 / T9;
|
|
return Ssi;
|
|
}
|
|
|
|
int
|
|
BSIM3V2noise (mode, operation, inModel, ckt, data, OnDens)
|
|
int mode, operation;
|
|
GENmodel *inModel;
|
|
CKTcircuit *ckt;
|
|
Ndata *data;
|
|
double *OnDens;
|
|
{
|
|
BSIM3V2model *model = (BSIM3V2model *)inModel;
|
|
BSIM3V2instance *here;
|
|
struct BSIM3V2SizeDependParam *pParam;
|
|
char name[N_MXVLNTH];
|
|
double tempOnoise;
|
|
double tempInoise;
|
|
double noizDens[BSIM3V2NSRCS];
|
|
double lnNdens[BSIM3V2NSRCS];
|
|
|
|
double vgs, vds, Slimit;
|
|
double N0, Nl;
|
|
double T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13;
|
|
double n, ExpArg, Ssi, Swi;
|
|
|
|
int error, i;
|
|
|
|
/* define the names of the noise sources */
|
|
static char *BSIM3V2nNames[BSIM3V2NSRCS] =
|
|
{ /* Note that we have to keep the order */
|
|
".rd", /* noise due to rd */
|
|
/* consistent with the index definitions */
|
|
".rs", /* noise due to rs */
|
|
/* in BSIM3V2defs.h */
|
|
".id", /* noise due to id */
|
|
".1overf", /* flicker (1/f) noise */
|
|
"" /* total transistor noise */
|
|
};
|
|
|
|
for (; model != NULL; model = model->BSIM3V2nextModel)
|
|
{ for (here = model->BSIM3V2instances; here != NULL;
|
|
here = here->BSIM3V2nextInstance)
|
|
{ pParam = here->pParam;
|
|
switch (operation)
|
|
{ case N_OPEN:
|
|
/* see if we have to to produce a summary report */
|
|
/* if so, name all the noise generators */
|
|
|
|
if (((NOISEAN*)ckt->CKTcurJob)->NStpsSm != 0)
|
|
{ switch (mode)
|
|
{ case N_DENS:
|
|
for (i = 0; i < BSIM3V2NSRCS; i++)
|
|
{ (void) sprintf(name, "onoise.%s%s",
|
|
here->BSIM3V2name,
|
|
BSIM3V2nNames[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 */
|
|
}
|
|
break;
|
|
case INT_NOIZ:
|
|
for (i = 0; i < BSIM3V2NSRCS; i++)
|
|
{ (void) sprintf(name, "onoise_total.%s%s",
|
|
here->BSIM3V2name,
|
|
BSIM3V2nNames[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 */
|
|
|
|
(void) sprintf(name, "inoise_total.%s%s",
|
|
here->BSIM3V2name,
|
|
BSIM3V2nNames[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 */
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case N_CALC:
|
|
switch (mode)
|
|
{ case N_DENS:
|
|
NevalSrc(&noizDens[BSIM3V2RDNOIZ],
|
|
&lnNdens[BSIM3V2RDNOIZ], ckt, THERMNOISE,
|
|
here->BSIM3V2dNodePrime, here->BSIM3V2dNode,
|
|
here->BSIM3V2drainConductance);
|
|
|
|
NevalSrc(&noizDens[BSIM3V2RSNOIZ],
|
|
&lnNdens[BSIM3V2RSNOIZ], ckt, THERMNOISE,
|
|
here->BSIM3V2sNodePrime, here->BSIM3V2sNode,
|
|
here->BSIM3V2sourceConductance);
|
|
|
|
switch( model->BSIM3V2noiMod )
|
|
{ case 1:
|
|
case 3:
|
|
NevalSrc(&noizDens[BSIM3V2IDNOIZ],
|
|
&lnNdens[BSIM3V2IDNOIZ], ckt,
|
|
THERMNOISE, here->BSIM3V2dNodePrime,
|
|
here->BSIM3V2sNodePrime,
|
|
(2.0 / 3.0 * fabs(here->BSIM3V2gm
|
|
+ here->BSIM3V2gds
|
|
+ here->BSIM3V2gmbs)));
|
|
break;
|
|
case 2:
|
|
case 4:
|
|
NevalSrc(&noizDens[BSIM3V2IDNOIZ],
|
|
&lnNdens[BSIM3V2IDNOIZ], ckt,
|
|
THERMNOISE, here->BSIM3V2dNodePrime,
|
|
here->BSIM3V2sNodePrime,
|
|
(here->BSIM3V2ueff
|
|
* fabs(here->BSIM3V2qinv
|
|
/ (pParam->BSIM3V2leff
|
|
* pParam->BSIM3V2leff))));
|
|
break;
|
|
}
|
|
NevalSrc(&noizDens[BSIM3V2FLNOIZ], (double*) NULL,
|
|
ckt, N_GAIN, here->BSIM3V2dNodePrime,
|
|
here->BSIM3V2sNodePrime, (double) 0.0);
|
|
|
|
switch( model->BSIM3V2noiMod )
|
|
{ case 1:
|
|
case 4:
|
|
noizDens[BSIM3V2FLNOIZ] *= model->BSIM3V2kf
|
|
* exp(model->BSIM3V2af
|
|
* log(MAX(fabs(here->BSIM3V2cd),
|
|
N_MINLOG)))
|
|
/ (pow(data->freq, model->BSIM3V2ef)
|
|
* pParam->BSIM3V2leff
|
|
* pParam->BSIM3V2leff
|
|
* model->BSIM3V2cox);
|
|
break;
|
|
case 2:
|
|
case 3:
|
|
vgs = *(ckt->CKTstates[0] + here->BSIM3V2vgs);
|
|
vds = *(ckt->CKTstates[0] + here->BSIM3V2vds);
|
|
if (vds < 0.0)
|
|
{ vds = -vds;
|
|
vgs = vgs + vds;
|
|
}
|
|
if (vgs >= here->BSIM3V2von + 0.1)
|
|
{ Ssi = BSIM3V2StrongInversionNoiseEval(vgs,
|
|
vds, model, here, data->freq,
|
|
ckt->CKTtemp);
|
|
noizDens[BSIM3V2FLNOIZ] *= Ssi;
|
|
}
|
|
else
|
|
{ pParam = here->pParam;
|
|
T10 = model->BSIM3V2oxideTrapDensityA
|
|
* 8.62e-5 * ckt->CKTtemp;
|
|
T11 = pParam->BSIM3V2weff
|
|
* pParam->BSIM3V2leff
|
|
* pow(data->freq, model->BSIM3V2ef)
|
|
* 4.0e36;
|
|
Swi = T10 / T11 * here->BSIM3V2cd
|
|
* here->BSIM3V2cd;
|
|
Slimit = BSIM3V2StrongInversionNoiseEval(
|
|
here->BSIM3V2von + 0.1, vds, model,
|
|
here, data->freq, ckt->CKTtemp);
|
|
T1 = Swi + Slimit;
|
|
if (T1 > 0.0)
|
|
noizDens[BSIM3V2FLNOIZ] *= (Slimit
|
|
* Swi) / T1;
|
|
else
|
|
noizDens[BSIM3V2FLNOIZ] *= 0.0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
lnNdens[BSIM3V2FLNOIZ] =
|
|
log(MAX(noizDens[BSIM3V2FLNOIZ], N_MINLOG));
|
|
|
|
noizDens[BSIM3V2TOTNOIZ] = noizDens[BSIM3V2RDNOIZ]
|
|
+ noizDens[BSIM3V2RSNOIZ]
|
|
+ noizDens[BSIM3V2IDNOIZ]
|
|
+ noizDens[BSIM3V2FLNOIZ];
|
|
lnNdens[BSIM3V2TOTNOIZ] =
|
|
log(MAX(noizDens[BSIM3V2TOTNOIZ], N_MINLOG));
|
|
|
|
*OnDens += noizDens[BSIM3V2TOTNOIZ];
|
|
|
|
if (data->delFreq == 0.0)
|
|
{ /* if we haven't done any previous
|
|
integration, we need to initialize our
|
|
"history" variables.
|
|
*/
|
|
|
|
for (i = 0; i < BSIM3V2NSRCS; i++)
|
|
{ here->BSIM3V2nVar[LNLSTDENS][i] =
|
|
lnNdens[i];
|
|
}
|
|
|
|
/* clear out our integration variables
|
|
if it's the first pass
|
|
*/
|
|
if (data->freq ==
|
|
((NOISEAN*) ckt->CKTcurJob)->NstartFreq)
|
|
{ for (i = 0; i < BSIM3V2NSRCS; i++)
|
|
{ here->BSIM3V2nVar[OUTNOIZ][i] = 0.0;
|
|
here->BSIM3V2nVar[INNOIZ][i] = 0.0;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{ /* data->delFreq != 0.0,
|
|
we have to integrate.
|
|
*/
|
|
for (i = 0; i < BSIM3V2NSRCS; i++)
|
|
{ if (i != BSIM3V2TOTNOIZ)
|
|
{ tempOnoise = Nintegrate(noizDens[i],
|
|
lnNdens[i],
|
|
here->BSIM3V2nVar[LNLSTDENS][i],
|
|
data);
|
|
tempInoise = Nintegrate(noizDens[i]
|
|
* data->GainSqInv, lnNdens[i]
|
|
+ data->lnGainInv,
|
|
here->BSIM3V2nVar[LNLSTDENS][i]
|
|
+ data->lnGainInv, data);
|
|
here->BSIM3V2nVar[LNLSTDENS][i] =
|
|
lnNdens[i];
|
|
data->outNoiz += tempOnoise;
|
|
data->inNoise += tempInoise;
|
|
if (((NOISEAN*)
|
|
ckt->CKTcurJob)->NStpsSm != 0)
|
|
{ here->BSIM3V2nVar[OUTNOIZ][i]
|
|
+= tempOnoise;
|
|
here->BSIM3V2nVar[OUTNOIZ][BSIM3V2TOTNOIZ]
|
|
+= tempOnoise;
|
|
here->BSIM3V2nVar[INNOIZ][i]
|
|
+= tempInoise;
|
|
here->BSIM3V2nVar[INNOIZ][BSIM3V2TOTNOIZ]
|
|
+= tempInoise;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (data->prtSummary)
|
|
{ for (i = 0; i < BSIM3V2NSRCS; i++)
|
|
{ /* print a summary report */
|
|
data->outpVector[data->outNumber++]
|
|
= noizDens[i];
|
|
}
|
|
}
|
|
break;
|
|
case INT_NOIZ:
|
|
/* already calculated, just output */
|
|
if (((NOISEAN*)ckt->CKTcurJob)->NStpsSm != 0)
|
|
{ for (i = 0; i < BSIM3V2NSRCS; i++)
|
|
{ data->outpVector[data->outNumber++]
|
|
= here->BSIM3V2nVar[OUTNOIZ][i];
|
|
data->outpVector[data->outNumber++]
|
|
= here->BSIM3V2nVar[INNOIZ][i];
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case N_CLOSE:
|
|
/* do nothing, the main calling routine will close */
|
|
return (OK);
|
|
break; /* the plots */
|
|
} /* switch (operation) */
|
|
} /* for here */
|
|
} /* for model */
|
|
|
|
return(OK);
|
|
}
|
|
|
|
|
|
|