ngspice/src/ciderlib/support/mobil.c

423 lines
11 KiB
C

/**********
Copyright 1991 Regents of the University of California. All rights reserved.
Author: 1992 David A. Gates, U. C. Berkeley CAD Group
**********/
#include "ngspice/ngspice.h"
#include "ngspice/numglobs.h"
#include "ngspice/numconst.h"
#include "ngspice/numenum.h"
#include "ngspice/macros.h"
#include "ngspice/material.h"
#include "ngspice/cidersupt.h"
void MOBdefaults(MaterialInfo *info , int carrier, int type,
int concmodel, int fieldmodel )
{
switch (concmodel) {
case CT:
info->concModel = CT;
if (carrier == ELEC) {
info->muMax[ELEC][type] = CT_MUMAX_N;
info->muMin[ELEC][type] = CT_MUMIN_N;
info->ntRef[ELEC][type] = CT_NTREF_N;
info->ntExp[ELEC][type] = CT_NTEXP_N;
} else {
info->muMax[HOLE][type] = CT_MUMAX_P;
info->muMin[HOLE][type] = CT_MUMIN_P;
info->ntRef[HOLE][type] = CT_NTREF_P;
info->ntExp[HOLE][type] = CT_NTEXP_P;
}
break;
case AR:
info->concModel = AR;
if (carrier == ELEC) {
info->muMax[ELEC][type] = AR_MUMAX_N;
info->muMin[ELEC][type] = AR_MUMIN_N;
info->ntRef[ELEC][type] = AR_NTREF_N;
info->ntExp[ELEC][type] = AR_NTEXP_N;
} else {
info->muMax[HOLE][type] = AR_MUMAX_P;
info->muMin[HOLE][type] = AR_MUMIN_P;
info->ntRef[HOLE][type] = AR_NTREF_P;
info->ntExp[HOLE][type] = AR_NTEXP_P;
}
break;
case UF:
info->concModel = UF;
if (carrier == ELEC) {
info->muMax[ELEC][type] = UF_MUMAX_N;
info->muMin[ELEC][type] = UF_MUMIN_N;
info->ntRef[ELEC][type] = UF_NTREF_N;
info->ntExp[ELEC][type] = UF_NTEXP_N;
} else {
info->muMax[HOLE][type] = UF_MUMAX_P;
info->muMin[HOLE][type] = UF_MUMIN_P;
info->ntRef[HOLE][type] = UF_NTREF_P;
info->ntExp[HOLE][type] = UF_NTEXP_P;
}
break;
case GA:
info->concModel = GA;
if (carrier == ELEC) {
info->muMax[ELEC][type] = GA_MUMAX_N;
info->muMin[ELEC][type] = GA_MUMIN_N;
info->ntRef[ELEC][type] = GA_NTREF_N;
info->ntExp[ELEC][type] = GA_NTEXP_N;
} else {
info->muMax[HOLE][type] = GA_MUMAX_P;
info->muMin[HOLE][type] = GA_MUMIN_P;
info->ntRef[HOLE][type] = GA_NTREF_P;
info->ntExp[HOLE][type] = GA_NTEXP_P;
}
break;
case SG:
default:
info->concModel = SG;
if (carrier == ELEC) {
info->muMax[ELEC][type] = SG_MUMAX_N;
info->muMin[ELEC][type] = SG_MUMIN_N;
info->ntRef[ELEC][type] = SG_NTREF_N;
info->ntExp[ELEC][type] = SG_NTEXP_N;
} else {
info->muMax[HOLE][type] = SG_MUMAX_P;
info->muMin[HOLE][type] = SG_MUMIN_P;
info->ntRef[HOLE][type] = SG_NTREF_P;
info->ntExp[HOLE][type] = SG_NTEXP_P;
}
break;
}
if (type == MAJOR) {
switch (fieldmodel) {
case CT:
info->fieldModel = CT;
if (carrier == ELEC) {
info->vSat[ELEC] = CT_VSAT_N;
} else {
info->vSat[HOLE] = CT_VSAT_P;
}
break;
case AR:
case UF:
info->fieldModel = AR;
if (carrier == ELEC) {
info->vSat[ELEC] = AR_VSAT_N;
} else {
info->vSat[HOLE] = AR_VSAT_P;
}
break;
case GA:
info->fieldModel = GA;
if (carrier == ELEC) {
info->vSat[ELEC] = GA_VSAT_N;
info->vWarm[ELEC] = GA_VWARM_N;
} else {
info->vSat[HOLE] = GA_VSAT_P;
info->vWarm[HOLE] = GA_VWARM_P;
}
break;
case SG:
default:
info->fieldModel = SG;
if (carrier == ELEC) {
info->vSat[ELEC] = SG_VSAT_N;
info->vWarm[ELEC] = SG_VWARM_N;
} else {
info->vSat[HOLE] = SG_VSAT_P;
info->vWarm[HOLE] = SG_VWARM_P;
}
break;
}
}
}
void
MOBtempDep (MaterialInfo *info, double temp)
{
double relTemp = temp / 300.0;
double factor, muMin, mu0;
/* Modify if necessary. */
if (TempDepMobility)
{
/* Do concentration dependence parameters */
muMin = info->muMin[ELEC][MAJOR];
mu0 = info->muMax[ELEC][MAJOR] - muMin;
factor = pow(relTemp, TD_EXPMUMIN_N);
muMin *= factor;
factor = pow(relTemp, TD_EXPMUMAX_N);
mu0 *= factor;
info->muMin[ELEC][MAJOR] = muMin;
info->muMax[ELEC][MAJOR] = mu0 + muMin;
factor = pow(relTemp, TD_EXPNTREF_N);
info->ntRef[ELEC][MAJOR] *= factor;
factor = pow(relTemp, TD_EXPNTEXP_N);
info->ntExp[ELEC][MAJOR] *= factor;
muMin = info->muMin[ELEC][MINOR];
mu0 = info->muMax[ELEC][MINOR] - muMin;
factor = pow(relTemp, TD_EXPMUMIN_N);
muMin *= factor;
factor = pow(relTemp, TD_EXPMUMAX_N);
mu0 *= factor;
info->muMin[ELEC][MINOR] = muMin;
info->muMax[ELEC][MINOR] = mu0 + muMin;
factor = pow(relTemp, TD_EXPNTREF_N);
info->ntRef[ELEC][MINOR] *= factor;
factor = pow(relTemp, TD_EXPNTEXP_N);
info->ntExp[ELEC][MINOR] *= factor;
muMin = info->muMin[HOLE][MAJOR];
mu0 = info->muMax[HOLE][MAJOR] - muMin;
factor = pow(relTemp, TD_EXPMUMIN_P);
muMin *= factor;
factor = pow(relTemp, TD_EXPMUMAX_P);
mu0 *= factor;
info->muMin[HOLE][MAJOR] = muMin;
info->muMax[HOLE][MAJOR] = mu0 + muMin;
factor = pow(relTemp, TD_EXPNTREF_P);
info->ntRef[HOLE][MAJOR] *= factor;
factor = pow(relTemp, TD_EXPNTEXP_P);
info->ntExp[HOLE][MAJOR] *= factor;
muMin = info->muMin[HOLE][MINOR];
mu0 = info->muMax[HOLE][MINOR] - muMin;
factor = pow(relTemp, TD_EXPMUMIN_P);
muMin *= factor;
factor = pow(relTemp, TD_EXPMUMAX_P);
mu0 *= factor;
info->muMin[HOLE][MINOR] = muMin;
info->muMax[HOLE][MINOR] = mu0 + muMin;
factor = pow(relTemp, TD_EXPNTREF_P);
info->ntRef[HOLE][MINOR] *= factor;
factor = pow(relTemp, TD_EXPNTEXP_P);
info->ntExp[HOLE][MINOR] *= factor;
/* Modify field dependence parameters */
/* Assume warm carrier reference velocity has same temperature dep. */
factor = sqrt( tanh( TD_TREFVS_N / Temp ) );
info->vSat[ELEC] *= factor;
info->vWarm[ELEC] *= factor;
factor = sqrt( tanh( TD_TREFVS_P / Temp ) );
info->vSat[HOLE] *= factor;
info->vWarm[HOLE] *= factor;
}
}
void
MOBconcDep (MaterialInfo *info, double conc, double *pMun, double *pMup)
{
double s;
/* We have to check sign of conc even when concentration dependence
* is not used because it affects whether carriers are majority or
* minority carriers. Ideally, the minority/majority carrier models
* should agree at 0.0 concentration, but often they'll be inconsistent.
*/
if (conc >= 0.0)
{ /* N type */
if (ConcDepMobility)
{
switch (info->concModel)
{
case CT:
case AR:
case UF:
case GA:
*pMun = info->muMin[ELEC][MAJOR] +
(info->muMax[ELEC][MAJOR] - info->muMin[ELEC][MAJOR]) /
(1.0 + pow(conc / info->ntRef[ELEC][MAJOR],
info->ntExp[ELEC][MAJOR]));
*pMup = info->muMin[HOLE][MINOR] +
(info->muMax[HOLE][MINOR] - info->muMin[HOLE][MINOR]) /
(1.0 + pow(conc / info->ntRef[HOLE][MINOR],
info->ntExp[HOLE][MINOR]));
break;
case SG:
default:
s = info->muMax[ELEC][MAJOR] / info->muMin[ELEC][MAJOR];
s = pow(s, 1.0 / info->ntExp[ELEC][MAJOR]) - 1;
*pMun = info->muMax[ELEC][MAJOR] /
pow(1.0 + conc / (conc / s + info->ntRef[ELEC][MAJOR]),
info->ntExp[ELEC][MAJOR]);
s = info->muMax[HOLE][MINOR] / info->muMin[HOLE][MINOR];
s = pow(s, 1.0 / info->ntExp[HOLE][MINOR]) - 1;
*pMup = info->muMax[HOLE][MINOR] /
pow(1.0 + conc / (conc / s + info->ntRef[HOLE][MINOR]),
info->ntExp[HOLE][MINOR]);
break;
}
}
else
{
*pMun = info->muMax[ELEC][MAJOR];
*pMup = info->muMax[HOLE][MINOR];
}
}
else
{ /* P type */
if (ConcDepMobility)
{
conc = -conc; /* Take absolute value. */
switch (info->concModel)
{
case CT:
case AR:
case UF:
case GA:
*pMun = info->muMin[ELEC][MINOR] +
(info->muMax[ELEC][MINOR] - info->muMin[ELEC][MINOR]) /
(1.0 + pow(conc / info->ntRef[ELEC][MINOR],
info->ntExp[ELEC][MINOR]));
*pMup = info->muMin[HOLE][MAJOR] +
(info->muMax[HOLE][MAJOR] - info->muMin[HOLE][MAJOR]) /
(1.0 + pow(conc / info->ntRef[HOLE][MAJOR],
info->ntExp[HOLE][MAJOR]));
break;
case SG:
default:
s = info->muMax[ELEC][MINOR] / info->muMin[ELEC][MINOR];
s = pow(s, 1.0 / info->ntExp[ELEC][MINOR]) - 1;
*pMun = info->muMax[ELEC][MINOR] /
pow(1.0 + conc / (conc / s + info->ntRef[ELEC][MINOR]),
info->ntExp[ELEC][MINOR]);
s = info->muMax[HOLE][MAJOR] / info->muMin[HOLE][MAJOR];
s = pow(s, 1.0 / info->ntExp[HOLE][MAJOR]) - 1;
*pMup = info->muMax[HOLE][MAJOR] /
pow(1.0 + conc / (conc / s + info->ntRef[HOLE][MAJOR]),
info->ntExp[HOLE][MAJOR]);
break;
}
}
else
{
*pMun = info->muMax[ELEC][MINOR];
*pMup = info->muMax[HOLE][MAJOR];
}
}
return;
}
void
MOBfieldDep (MaterialInfo *info, int carrier, double field, double *pMu,
double *pDMu)
{
double eLateral, mu;
double sgnL;
double temp1, temp2, temp3, temp4, temp5, temp6;
double dMuDEl; /* Lateral Field Derivative */
/* Quick check to make sure we really belong here. */
if (!FieldDepMobility) /* XXX Global */
return;
sgnL = SGN (field);
eLateral = ABS (field);
mu = *pMu; /* Grab temp. and conc.-dep. mobility */
if (carrier == ELEC)
{
switch (info->fieldModel)
{
case CT:
case AR:
case UF:
temp1 = mu / info->vSat[ELEC];
temp2 = temp1 * eLateral;
temp3 = 1.0 / (1.0 + temp2 * temp2);
mu *= sqrt(temp3);
dMuDEl = -sgnL * mu * temp3 * temp2 * temp1;
break;
case GA:
temp1 = info->vSat[ELEC] / info->vWarm[ELEC]; /* Vsat / Vwarm */
temp2 = mu / info->vWarm[ELEC];
temp3 = temp2 * eLateral; /* Vdrift / Vwarm */
temp4 = temp3 * temp3 * temp3;
temp5 = 1.0 + temp1 * temp4;
temp6 = 1.0 / (1.0 + temp3 * temp4);
mu *= temp5 * temp6;
dMuDEl = - sgnL * mu * temp2 *
(4.0 * temp4 * temp6 - 3.0 * temp1 * temp3 * temp3 / temp5 );
/*
dMuDEl = 0.0;
*/
break;
case SG:
default:
temp1 = mu / info->vSat[ELEC];
temp2 = temp1 * eLateral;/* Vdrift / Vsat */
temp3 = mu / info->vWarm[ELEC];
temp4 = temp3 * eLateral;/* Vdrift / Vwarm */
temp5 = temp4 / (temp4 + SG_FIT_N);
temp6 = 1.0 / (1.0 + temp4 * temp5 + temp2 * temp2);
mu *= sqrt(temp6);
dMuDEl = -sgnL * 0.5 * mu * temp6 *
(temp5 * (2.0 - temp5) * temp3 + (2.0 * temp2 * temp1));
break;
}
}
else
{ /* Hole Mobility */
switch (info->fieldModel)
{
case CT:
case AR:
case UF:
temp1 = mu / info->vSat[HOLE];
temp2 = temp1 * eLateral;
temp3 = 1.0 / (1.0 + temp2);
mu *= temp3;
dMuDEl = -sgnL * mu * temp3 * temp1;
break;
case GA:
temp1 = info->vSat[HOLE] / info->vWarm[HOLE]; /* Vsat / Vwarm */
temp2 = mu / info->vWarm[HOLE];
temp3 = temp2 * eLateral; /* Vdrift / Vwarm */
temp4 = temp3 * temp3 * temp3;
temp5 = 1.0 + temp1 * temp4;
temp6 = 1.0 / (1.0 + temp3 * temp4);
mu *= temp5 * temp6;
dMuDEl = - sgnL * mu * temp2 *
(4.0 * temp4 * temp6 - 3.0 * temp1 * temp3 * temp3 / temp5 );
/*
dMuDEl = 0.0;
*/
break;
case SG:
default:
temp1 = mu / info->vSat[HOLE];
temp2 = temp1 * eLateral;/* Vdrift / Vsat */
temp3 = mu / info->vWarm[HOLE];
temp4 = temp3 * eLateral;/* Vdrift / Vwarm */
temp5 = temp4 / (temp4 + SG_FIT_P);
temp6 = 1.0 / (1.0 + temp4 * temp5 + temp2 * temp2);
mu *= sqrt(temp6);
dMuDEl = -sgnL * 0.5 * mu * temp6 *
(temp5 * (2.0 - temp5) * temp3 + (2.0 * temp2 * temp1));
break;
}
}
*pMu = mu;
*pDMu = dMuDEl;
return;
}