1317 lines
44 KiB
C
1317 lines
44 KiB
C
/**********
|
|
Copyright 1991 Regents of the University of California. All rights reserved.
|
|
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group
|
|
**********/
|
|
|
|
/* Functions to compute the ac admittances of a device. */
|
|
|
|
#include "ngspice.h"
|
|
#include "numglobs.h"
|
|
#include "numenum.h"
|
|
#include "numconst.h"
|
|
#include "twodev.h"
|
|
#include "twomesh.h"
|
|
#include "complex.h"
|
|
#include "spMatrix.h"
|
|
#include "bool.h"
|
|
#include "macros.h"
|
|
#include "ifsim.h"
|
|
#include "twoddefs.h"
|
|
#include "twodext.h"
|
|
#include "cidersupt.h"
|
|
|
|
extern IFfrontEnd *SPfrontEnd;
|
|
|
|
/*
|
|
* mmhhh this may cause troubles
|
|
* Paolo Nenzi 2002
|
|
*/
|
|
SPcomplex yTotal;
|
|
|
|
int
|
|
NUMD2admittance(TWOdevice *pDevice, double omega, SPcomplex *yd)
|
|
{
|
|
TWOnode *pNode;
|
|
TWOelem *pElem;
|
|
int index, eIndex;
|
|
double dxdy;
|
|
double *solnReal, *solnImag;
|
|
double *rhsReal, *rhsImag;
|
|
SPcomplex yAc, cOmega, *y;
|
|
BOOLEAN deltaVContact = FALSE;
|
|
BOOLEAN SORFailed;
|
|
double startTime;
|
|
|
|
/* Each time we call this counts as one AC iteration. */
|
|
pDevice->pStats->numIters[STAT_AC] += 1;
|
|
|
|
/*
|
|
* change context names of solution vectors for ac analysis dcDeltaSolution
|
|
* stores the real part and copiedSolution stores the imaginary part of the
|
|
* ac solution vector
|
|
*/
|
|
pDevice->solverType = SLV_SMSIG;
|
|
rhsReal = pDevice->rhs;
|
|
rhsImag = pDevice->rhsImag;
|
|
solnReal = pDevice->dcDeltaSolution;
|
|
solnImag = pDevice->copiedSolution;
|
|
|
|
/* use a normalized radian frequency */
|
|
omega *= TNorm;
|
|
CMPLX_ASSIGN_VALUE(cOmega, 0.0, omega);
|
|
|
|
if ((AcAnalysisMethod == SOR) || (AcAnalysisMethod == SOR_ONLY)) {
|
|
/* LOAD */
|
|
startTime = SPfrontEnd->IFseconds();
|
|
/* zero the rhsImag */
|
|
for (index = 1; index <= pDevice->numEqns; index++) {
|
|
rhsImag[index] = 0.0;
|
|
}
|
|
/* store the new rhs vector */
|
|
storeNewRhs(pDevice, pDevice->pLastContact);
|
|
pDevice->pStats->loadTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
|
|
|
/* SOLVE */
|
|
startTime = SPfrontEnd->IFseconds();
|
|
SORFailed = TWOsorSolve(pDevice, solnReal, solnImag, omega);
|
|
pDevice->pStats->solveTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
|
if (SORFailed && AcAnalysisMethod == SOR) {
|
|
AcAnalysisMethod = DIRECT;
|
|
printf("SOR failed at %g Hz, switching to direct-method ac analysis.\n",
|
|
omega / (TWO_PI * TNorm) );
|
|
} else if (SORFailed) { /* Told to only do SOR, so give up. */
|
|
printf("SOR failed at %g Hz, returning null admittance.\n",
|
|
omega / (TWO_PI * TNorm) );
|
|
CMPLX_ASSIGN_VALUE(*yd, 0.0, 0.0);
|
|
return (AcAnalysisMethod);
|
|
}
|
|
}
|
|
if (AcAnalysisMethod == DIRECT) {
|
|
/* LOAD */
|
|
startTime = SPfrontEnd->IFseconds();
|
|
for (index = 1; index <= pDevice->numEqns; index++) {
|
|
rhsImag[index] = 0.0;
|
|
}
|
|
/* solve the system of equations directly */
|
|
if (!OneCarrier) {
|
|
TWO_jacLoad(pDevice);
|
|
} else if (OneCarrier == N_TYPE) {
|
|
TWONjacLoad(pDevice);
|
|
} else if (OneCarrier == P_TYPE) {
|
|
TWOPjacLoad(pDevice);
|
|
}
|
|
storeNewRhs(pDevice, pDevice->pLastContact);
|
|
|
|
spSetComplex(pDevice->matrix);
|
|
for (eIndex = 1; eIndex <= pDevice->numElems; eIndex++) {
|
|
pElem = pDevice->elements[eIndex];
|
|
if (pElem->elemType == SEMICON) {
|
|
dxdy = 0.25 * pElem->dx * pElem->dy;
|
|
for (index = 0; index <= 3; index++) {
|
|
pNode = pElem->pNodes[index];
|
|
if (pNode->nodeType != CONTACT) {
|
|
if (!OneCarrier) {
|
|
spADD_COMPLEX_ELEMENT(pNode->fNN, 0.0, -dxdy * omega);
|
|
spADD_COMPLEX_ELEMENT(pNode->fPP, 0.0, dxdy * omega);
|
|
} else if (OneCarrier == N_TYPE) {
|
|
spADD_COMPLEX_ELEMENT(pNode->fNN, 0.0, -dxdy * omega);
|
|
} else if (OneCarrier == P_TYPE) {
|
|
spADD_COMPLEX_ELEMENT(pNode->fPP, 0.0, dxdy * omega);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
pDevice->pStats->loadTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
|
|
|
/* FACTOR */
|
|
startTime = SPfrontEnd->IFseconds();
|
|
spFactor(pDevice->matrix);
|
|
pDevice->pStats->factorTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
|
|
|
/* SOLVE */
|
|
startTime = SPfrontEnd->IFseconds();
|
|
spSolve(pDevice->matrix, rhsReal, solnReal, rhsImag, solnImag);
|
|
pDevice->pStats->solveTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
|
}
|
|
/* MISC */
|
|
startTime = SPfrontEnd->IFseconds();
|
|
y = contactAdmittance(pDevice, pDevice->pFirstContact, deltaVContact,
|
|
solnReal, solnImag, &cOmega);
|
|
CMPLX_ASSIGN_VALUE(yAc, -y->real, -y->imag);
|
|
CMPLX_ASSIGN(*yd, yAc);
|
|
CMPLX_MULT_SELF_SCALAR(*yd, GNorm * pDevice->width * LNorm);
|
|
pDevice->pStats->miscTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
|
|
|
return (AcAnalysisMethod);
|
|
}
|
|
|
|
|
|
int
|
|
NBJT2admittance(TWOdevice *pDevice, double omega, SPcomplex *yIeVce,
|
|
SPcomplex *yIcVce, SPcomplex *yIeVbe, SPcomplex *yIcVbe)
|
|
{
|
|
TWOcontact *pEmitContact = pDevice->pLastContact;
|
|
TWOcontact *pColContact = pDevice->pFirstContact;
|
|
TWOcontact *pBaseContact = pDevice->pFirstContact->next;
|
|
TWOnode *pNode;
|
|
TWOelem *pElem;
|
|
int index, eIndex;
|
|
double width = pDevice->width;
|
|
double dxdy;
|
|
double *solnReal, *solnImag;
|
|
double *rhsReal, *rhsImag;
|
|
BOOLEAN SORFailed;
|
|
SPcomplex *y;
|
|
SPcomplex pIeVce, pIcVce, pIeVbe, pIcVbe;
|
|
SPcomplex cOmega;
|
|
double startTime;
|
|
|
|
/* Each time we call this counts as one AC iteration. */
|
|
pDevice->pStats->numIters[STAT_AC] += 1;
|
|
|
|
pDevice->solverType = SLV_SMSIG;
|
|
rhsReal = pDevice->rhs;
|
|
rhsImag = pDevice->rhsImag;
|
|
solnReal = pDevice->dcDeltaSolution;
|
|
solnImag = pDevice->copiedSolution;
|
|
|
|
/* use a normalized radian frequency */
|
|
omega *= TNorm;
|
|
CMPLX_ASSIGN_VALUE(cOmega, 0.0, omega);
|
|
|
|
if ((AcAnalysisMethod == SOR) || (AcAnalysisMethod == SOR_ONLY)) {
|
|
/* LOAD */
|
|
startTime = SPfrontEnd->IFseconds();
|
|
/* zero the rhs before loading in the new rhs */
|
|
for (index = 1; index <= pDevice->numEqns; index++) {
|
|
rhsImag[index] = 0.0;
|
|
}
|
|
storeNewRhs(pDevice, pColContact);
|
|
pDevice->pStats->loadTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
|
|
|
/* SOLVE */
|
|
startTime = SPfrontEnd->IFseconds();
|
|
SORFailed = TWOsorSolve(pDevice, solnReal, solnImag, omega);
|
|
pDevice->pStats->solveTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
|
if (SORFailed && AcAnalysisMethod == SOR) {
|
|
AcAnalysisMethod = DIRECT;
|
|
printf("SOR failed at %g Hz, switching to direct-method ac analysis.\n",
|
|
omega / (TWO_PI * TNorm) );
|
|
} else if (SORFailed) { /* Told to only do SOR, so give up. */
|
|
printf("SOR failed at %g Hz, returning null admittance.\n",
|
|
omega / (TWO_PI * TNorm) );
|
|
CMPLX_ASSIGN_VALUE(*yIeVce, 0.0, 0.0);
|
|
CMPLX_ASSIGN_VALUE(*yIcVce, 0.0, 0.0);
|
|
CMPLX_ASSIGN_VALUE(*yIeVbe, 0.0, 0.0);
|
|
CMPLX_ASSIGN_VALUE(*yIcVbe, 0.0, 0.0);
|
|
return (AcAnalysisMethod);
|
|
} else {
|
|
/* MISC */
|
|
startTime = SPfrontEnd->IFseconds();
|
|
y = contactAdmittance(pDevice, pEmitContact, FALSE,
|
|
solnReal, solnImag, &cOmega);
|
|
CMPLX_ASSIGN_VALUE(pIeVce, y->real, y->imag);
|
|
y = contactAdmittance(pDevice, pColContact, TRUE,
|
|
solnReal, solnImag, &cOmega);
|
|
CMPLX_ASSIGN_VALUE(pIcVce, y->real, y->imag);
|
|
pDevice->pStats->miscTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
|
|
|
/* LOAD */
|
|
startTime = SPfrontEnd->IFseconds();
|
|
/* load in the base contribution to the rhs */
|
|
for (index = 1; index <= pDevice->numEqns; index++) {
|
|
rhsImag[index] = 0.0;
|
|
}
|
|
storeNewRhs(pDevice, pBaseContact);
|
|
pDevice->pStats->loadTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
|
|
|
/* SOLVE */
|
|
startTime = SPfrontEnd->IFseconds();
|
|
SORFailed = TWOsorSolve(pDevice, solnReal, solnImag, omega);
|
|
pDevice->pStats->solveTime[STAT_AC] +=
|
|
SPfrontEnd->IFseconds() - startTime;
|
|
if (SORFailed && AcAnalysisMethod == SOR) {
|
|
AcAnalysisMethod = DIRECT;
|
|
printf("SOR failed at %g Hz, switching to direct-method ac analysis.\n",
|
|
omega / (TWO_PI * TNorm) );
|
|
} else if (SORFailed) { /* Told to only do SOR, so give up. */
|
|
printf("SOR failed at %g Hz, returning null admittance.\n",
|
|
omega / (TWO_PI * TNorm) );
|
|
CMPLX_ASSIGN_VALUE(*yIeVce, 0.0, 0.0);
|
|
CMPLX_ASSIGN_VALUE(*yIcVce, 0.0, 0.0);
|
|
CMPLX_ASSIGN_VALUE(*yIeVbe, 0.0, 0.0);
|
|
CMPLX_ASSIGN_VALUE(*yIcVbe, 0.0, 0.0);
|
|
return (AcAnalysisMethod);
|
|
}
|
|
}
|
|
}
|
|
if (AcAnalysisMethod == DIRECT) {
|
|
/* LOAD */
|
|
startTime = SPfrontEnd->IFseconds();
|
|
for (index = 1; index <= pDevice->numEqns; index++) {
|
|
rhsImag[index] = 0.0;
|
|
}
|
|
/* solve the system of equations directly */
|
|
if (!OneCarrier) {
|
|
TWO_jacLoad(pDevice);
|
|
} else if (OneCarrier == N_TYPE) {
|
|
TWONjacLoad(pDevice);
|
|
} else if (OneCarrier == P_TYPE) {
|
|
TWOPjacLoad(pDevice);
|
|
}
|
|
storeNewRhs(pDevice, pColContact);
|
|
spSetComplex(pDevice->matrix);
|
|
for (eIndex = 1; eIndex <= pDevice->numElems; eIndex++) {
|
|
pElem = pDevice->elements[eIndex];
|
|
if (pElem->elemType == SEMICON) {
|
|
dxdy = 0.25 * pElem->dx * pElem->dy;
|
|
for (index = 0; index <= 3; index++) {
|
|
pNode = pElem->pNodes[index];
|
|
if (pNode->nodeType != CONTACT) {
|
|
if (!OneCarrier) {
|
|
spADD_COMPLEX_ELEMENT(pNode->fNN, 0.0, -dxdy * omega);
|
|
spADD_COMPLEX_ELEMENT(pNode->fPP, 0.0, dxdy * omega);
|
|
} else if (OneCarrier == N_TYPE) {
|
|
spADD_COMPLEX_ELEMENT(pNode->fNN, 0.0, -dxdy * omega);
|
|
} else if (OneCarrier == P_TYPE) {
|
|
spADD_COMPLEX_ELEMENT(pNode->fPP, 0.0, dxdy * omega);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
pDevice->pStats->loadTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
|
|
|
/* FACTOR */
|
|
startTime = SPfrontEnd->IFseconds();
|
|
spFactor(pDevice->matrix);
|
|
pDevice->pStats->factorTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
|
|
|
/* SOLVE */
|
|
startTime = SPfrontEnd->IFseconds();
|
|
spSolve(pDevice->matrix, rhsReal, solnReal, rhsImag, solnImag);
|
|
pDevice->pStats->solveTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
|
|
|
/* MISC */
|
|
startTime = SPfrontEnd->IFseconds();
|
|
y = contactAdmittance(pDevice, pEmitContact, FALSE,
|
|
solnReal, solnImag, &cOmega);
|
|
CMPLX_ASSIGN_VALUE(pIeVce, y->real, y->imag);
|
|
y = contactAdmittance(pDevice, pColContact, TRUE,
|
|
solnReal, solnImag, &cOmega);
|
|
CMPLX_ASSIGN_VALUE(pIcVce, y->real, y->imag);
|
|
pDevice->pStats->miscTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
|
|
|
/* LOAD */
|
|
startTime = SPfrontEnd->IFseconds();
|
|
for (index = 1; index <= pDevice->numEqns; index++) {
|
|
rhsImag[index] = 0.0;
|
|
}
|
|
storeNewRhs(pDevice, pBaseContact);
|
|
pDevice->pStats->loadTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
|
|
|
/* FACTOR: already done, no need to repeat. */
|
|
|
|
/* SOLVE */
|
|
startTime = SPfrontEnd->IFseconds();
|
|
spSolve(pDevice->matrix, rhsReal, solnReal, rhsImag, solnImag);
|
|
pDevice->pStats->solveTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
|
}
|
|
/* MISC */
|
|
startTime = SPfrontEnd->IFseconds();
|
|
y = contactAdmittance(pDevice, pEmitContact, FALSE,
|
|
solnReal, solnImag, &cOmega);
|
|
CMPLX_ASSIGN_VALUE(pIeVbe, y->real, y->imag);
|
|
y = contactAdmittance(pDevice, pColContact, FALSE,
|
|
solnReal, solnImag, &cOmega);
|
|
CMPLX_ASSIGN_VALUE(pIcVbe, y->real, y->imag);
|
|
|
|
CMPLX_ASSIGN(*yIeVce, pIeVce);
|
|
CMPLX_ASSIGN(*yIeVbe, pIeVbe);
|
|
CMPLX_ASSIGN(*yIcVce, pIcVce);
|
|
CMPLX_ASSIGN(*yIcVbe, pIcVbe);
|
|
CMPLX_MULT_SELF_SCALAR(*yIeVce, GNorm * width * LNorm);
|
|
CMPLX_MULT_SELF_SCALAR(*yIeVbe, GNorm * width * LNorm);
|
|
CMPLX_MULT_SELF_SCALAR(*yIcVce, GNorm * width * LNorm);
|
|
CMPLX_MULT_SELF_SCALAR(*yIcVbe, GNorm * width * LNorm);
|
|
pDevice->pStats->miscTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
|
|
|
return (AcAnalysisMethod);
|
|
}
|
|
|
|
int
|
|
NUMOSadmittance(TWOdevice *pDevice, double omega, struct mosAdmittances *yAc)
|
|
{
|
|
TWOcontact *pDContact = pDevice->pFirstContact;
|
|
TWOcontact *pGContact = pDevice->pFirstContact->next;
|
|
TWOcontact *pSContact = pDevice->pFirstContact->next->next;
|
|
TWOcontact *pBContact = pDevice->pLastContact;
|
|
TWOnode *pNode;
|
|
TWOelem *pElem;
|
|
int index, eIndex;
|
|
double width = pDevice->width;
|
|
double dxdy;
|
|
double *solnReal, *solnImag;
|
|
double *rhsReal, *rhsImag;
|
|
BOOLEAN SORFailed;
|
|
SPcomplex *y, cOmega;
|
|
double startTime;
|
|
|
|
/* Each time we call this counts as one AC iteration. */
|
|
pDevice->pStats->numIters[STAT_AC] += 1;
|
|
|
|
pDevice->solverType = SLV_SMSIG;
|
|
rhsReal = pDevice->rhs;
|
|
rhsImag = pDevice->rhsImag;
|
|
solnReal = pDevice->dcDeltaSolution;
|
|
solnImag = pDevice->copiedSolution;
|
|
|
|
/* use a normalized radian frequency */
|
|
omega *= TNorm;
|
|
CMPLX_ASSIGN_VALUE(cOmega, 0.0, omega);
|
|
|
|
if ((AcAnalysisMethod == SOR) || (AcAnalysisMethod == SOR_ONLY)) {
|
|
/* LOAD */
|
|
startTime = SPfrontEnd->IFseconds();
|
|
/* zero the rhs before loading in the new rhs */
|
|
for (index = 1; index <= pDevice->numEqns; index++) {
|
|
rhsImag[index] = 0.0;
|
|
}
|
|
storeNewRhs(pDevice, pDContact);
|
|
pDevice->pStats->loadTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
|
|
|
/* SOLVE */
|
|
startTime = SPfrontEnd->IFseconds();
|
|
SORFailed = TWOsorSolve(pDevice, solnReal, solnImag, omega);
|
|
pDevice->pStats->solveTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
|
if (SORFailed && AcAnalysisMethod == SOR) {
|
|
AcAnalysisMethod = DIRECT;
|
|
printf("SOR failed at %g Hz, switching to direct-method ac analysis.\n",
|
|
omega / (TWO_PI * TNorm) );
|
|
} else if (SORFailed) { /* Told to only do SOR, so give up. */
|
|
printf("SOR failed at %g Hz, returning null admittance.\n",
|
|
omega / (TWO_PI * TNorm) );
|
|
CMPLX_ASSIGN_VALUE(yAc->yIdVdb, 0.0, 0.0);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIdVsb, 0.0, 0.0);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIdVgb, 0.0, 0.0);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIsVdb, 0.0, 0.0);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIsVsb, 0.0, 0.0);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIsVgb, 0.0, 0.0);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIgVdb, 0.0, 0.0);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIgVsb, 0.0, 0.0);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIgVgb, 0.0, 0.0);
|
|
return (AcAnalysisMethod);
|
|
} else {
|
|
/* MISC */
|
|
startTime = SPfrontEnd->IFseconds();
|
|
y = contactAdmittance(pDevice, pDContact, TRUE,
|
|
solnReal, solnImag, &cOmega);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIdVdb, y->real, y->imag);
|
|
y = contactAdmittance(pDevice, pSContact, FALSE,
|
|
solnReal, solnImag, &cOmega);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIsVdb, y->real, y->imag);
|
|
y = GateTypeAdmittance(pDevice, pGContact, FALSE,
|
|
solnReal, solnImag, &cOmega);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIgVdb, y->real, y->imag);
|
|
pDevice->pStats->miscTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
|
|
|
/* LOAD */
|
|
startTime = SPfrontEnd->IFseconds();
|
|
/* load in the source contribution to the rhs */
|
|
for (index = 1; index <= pDevice->numEqns; index++) {
|
|
rhsImag[index] = 0.0;
|
|
}
|
|
storeNewRhs(pDevice, pSContact);
|
|
pDevice->pStats->loadTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
|
|
|
/* SOLVE */
|
|
startTime = SPfrontEnd->IFseconds();
|
|
SORFailed = TWOsorSolve(pDevice, solnReal, solnImag, omega);
|
|
pDevice->pStats->solveTime[STAT_AC] +=
|
|
SPfrontEnd->IFseconds() - startTime;
|
|
if (SORFailed && AcAnalysisMethod == SOR) {
|
|
AcAnalysisMethod = DIRECT;
|
|
printf("SOR failed at %g Hz, switching to direct-method ac analysis.\n",
|
|
omega / (TWO_PI * TNorm) );
|
|
} else if (SORFailed) { /* Told to only do SOR, so give up. */
|
|
printf("SOR failed at %g Hz, returning null admittance.\n",
|
|
omega / (TWO_PI * TNorm) );
|
|
CMPLX_ASSIGN_VALUE(yAc->yIdVdb, 0.0, 0.0);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIdVsb, 0.0, 0.0);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIdVgb, 0.0, 0.0);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIsVdb, 0.0, 0.0);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIsVsb, 0.0, 0.0);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIsVgb, 0.0, 0.0);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIgVdb, 0.0, 0.0);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIgVsb, 0.0, 0.0);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIgVgb, 0.0, 0.0);
|
|
return (AcAnalysisMethod);
|
|
} else {
|
|
/* MISC */
|
|
startTime = SPfrontEnd->IFseconds();
|
|
y = contactAdmittance(pDevice, pDContact, FALSE,
|
|
solnReal, solnImag, &cOmega);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIdVsb, y->real, y->imag);
|
|
y = contactAdmittance(pDevice, pSContact, TRUE,
|
|
solnReal, solnImag, &cOmega);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIsVsb, y->real, y->imag);
|
|
y = GateTypeAdmittance(pDevice, pGContact, FALSE,
|
|
solnReal, solnImag, &cOmega);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIgVsb, y->real, y->imag);
|
|
pDevice->pStats->miscTime[STAT_AC] +=
|
|
SPfrontEnd->IFseconds() - startTime;
|
|
|
|
/* LOAD */
|
|
startTime = SPfrontEnd->IFseconds();
|
|
/* load in the gate contribution to the rhs */
|
|
for (index = 1; index <= pDevice->numEqns; index++) {
|
|
rhsImag[index] = 0.0;
|
|
}
|
|
storeNewRhs(pDevice, pGContact);
|
|
pDevice->pStats->loadTime[STAT_AC] +=
|
|
SPfrontEnd->IFseconds() - startTime;
|
|
|
|
/* SOLVE */
|
|
startTime = SPfrontEnd->IFseconds();
|
|
SORFailed = TWOsorSolve(pDevice, solnReal, solnImag, omega);
|
|
pDevice->pStats->solveTime[STAT_AC] +=
|
|
SPfrontEnd->IFseconds() - startTime;
|
|
if (SORFailed && AcAnalysisMethod == SOR) {
|
|
AcAnalysisMethod = DIRECT;
|
|
printf("SOR failed at %g Hz, switching to direct-method ac analysis.\n",
|
|
omega / (TWO_PI * TNorm) );
|
|
} else if (SORFailed) { /* Told to only do SOR, so give up. */
|
|
printf("SOR failed at %g Hz, returning null admittance.\n",
|
|
omega / (TWO_PI * TNorm) );
|
|
CMPLX_ASSIGN_VALUE(yAc->yIdVdb, 0.0, 0.0);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIdVsb, 0.0, 0.0);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIdVgb, 0.0, 0.0);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIsVdb, 0.0, 0.0);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIsVsb, 0.0, 0.0);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIsVgb, 0.0, 0.0);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIgVdb, 0.0, 0.0);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIgVsb, 0.0, 0.0);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIgVgb, 0.0, 0.0);
|
|
return (AcAnalysisMethod);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (AcAnalysisMethod == DIRECT) {
|
|
/* solve the system of equations directly */
|
|
/* LOAD */
|
|
startTime = SPfrontEnd->IFseconds();
|
|
for (index = 1; index <= pDevice->numEqns; index++) {
|
|
rhsImag[index] = 0.0;
|
|
}
|
|
storeNewRhs(pDevice, pDContact);
|
|
|
|
/* Need to load & factor jacobian once. */
|
|
if (!OneCarrier) {
|
|
TWO_jacLoad(pDevice);
|
|
} else if (OneCarrier == N_TYPE) {
|
|
TWONjacLoad(pDevice);
|
|
} else if (OneCarrier == P_TYPE) {
|
|
TWOPjacLoad(pDevice);
|
|
}
|
|
spSetComplex(pDevice->matrix);
|
|
for (eIndex = 1; eIndex <= pDevice->numElems; eIndex++) {
|
|
pElem = pDevice->elements[eIndex];
|
|
if (pElem->elemType == SEMICON) {
|
|
dxdy = 0.25 * pElem->dx * pElem->dy;
|
|
for (index = 0; index <= 3; index++) {
|
|
pNode = pElem->pNodes[index];
|
|
if (pNode->nodeType != CONTACT) {
|
|
if (!OneCarrier) {
|
|
spADD_COMPLEX_ELEMENT(pNode->fNN, 0.0, -dxdy * omega);
|
|
spADD_COMPLEX_ELEMENT(pNode->fPP, 0.0, dxdy * omega);
|
|
} else if (OneCarrier == N_TYPE) {
|
|
spADD_COMPLEX_ELEMENT(pNode->fNN, 0.0, -dxdy * omega);
|
|
} else if (OneCarrier == P_TYPE) {
|
|
spADD_COMPLEX_ELEMENT(pNode->fPP, 0.0, dxdy * omega);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
pDevice->pStats->loadTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
|
|
|
/* FACTOR */
|
|
startTime = SPfrontEnd->IFseconds();
|
|
spFactor(pDevice->matrix);
|
|
pDevice->pStats->factorTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
|
|
|
/* SOLVE */
|
|
startTime = SPfrontEnd->IFseconds();
|
|
spSolve(pDevice->matrix, rhsReal, solnReal, rhsImag, solnImag);
|
|
pDevice->pStats->solveTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
|
|
|
/* MISC */
|
|
startTime = SPfrontEnd->IFseconds();
|
|
y = contactAdmittance(pDevice, pDContact, TRUE,
|
|
solnReal, solnImag, &cOmega);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIdVdb, y->real, y->imag);
|
|
y = contactAdmittance(pDevice, pSContact, FALSE,
|
|
solnReal, solnImag, &cOmega);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIsVdb, y->real, y->imag);
|
|
y = GateTypeAdmittance(pDevice, pGContact, FALSE,
|
|
solnReal, solnImag, &cOmega);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIgVdb, y->real, y->imag);
|
|
pDevice->pStats->miscTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
|
|
|
/* LOAD */
|
|
startTime = SPfrontEnd->IFseconds();
|
|
for (index = 1; index <= pDevice->numEqns; index++) {
|
|
rhsImag[index] = 0.0;
|
|
}
|
|
storeNewRhs(pDevice, pSContact);
|
|
pDevice->pStats->loadTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
|
|
|
/* FACTOR: already done, no need to repeat. */
|
|
|
|
/* SOLVE */
|
|
startTime = SPfrontEnd->IFseconds();
|
|
spSolve(pDevice->matrix, rhsReal, solnReal, rhsImag, solnImag);
|
|
pDevice->pStats->solveTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
|
|
|
/* MISC */
|
|
startTime = SPfrontEnd->IFseconds();
|
|
y = contactAdmittance(pDevice, pDContact, FALSE,
|
|
solnReal, solnImag, &cOmega);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIdVsb, y->real, y->imag);
|
|
y = contactAdmittance(pDevice, pSContact, TRUE,
|
|
solnReal, solnImag, &cOmega);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIsVsb, y->real, y->imag);
|
|
y = GateTypeAdmittance(pDevice, pGContact, FALSE,
|
|
solnReal, solnImag, &cOmega);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIgVsb, y->real, y->imag);
|
|
pDevice->pStats->miscTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
|
|
|
/* LOAD */
|
|
startTime = SPfrontEnd->IFseconds();
|
|
for (index = 1; index <= pDevice->numEqns; index++) {
|
|
rhsImag[index] = 0.0;
|
|
}
|
|
storeNewRhs(pDevice, pGContact);
|
|
pDevice->pStats->loadTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
|
|
|
/* FACTOR: already done, no need to repeat. */
|
|
|
|
/* SOLVE */
|
|
startTime = SPfrontEnd->IFseconds();
|
|
spSolve(pDevice->matrix, rhsReal, solnReal, rhsImag, solnImag);
|
|
pDevice->pStats->solveTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
|
}
|
|
/* MISC */
|
|
startTime = SPfrontEnd->IFseconds();
|
|
y = contactAdmittance(pDevice, pDContact, FALSE,
|
|
solnReal, solnImag, &cOmega);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIdVgb, y->real, y->imag);
|
|
y = contactAdmittance(pDevice, pSContact, FALSE,
|
|
solnReal, solnImag, &cOmega);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIsVgb, y->real, y->imag);
|
|
y = GateTypeAdmittance(pDevice, pGContact, TRUE,
|
|
solnReal, solnImag, &cOmega);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIgVgb, y->real, y->imag);
|
|
|
|
CMPLX_MULT_SELF_SCALAR(yAc->yIdVdb, GNorm * width * LNorm);
|
|
CMPLX_MULT_SELF_SCALAR(yAc->yIdVsb, GNorm * width * LNorm);
|
|
CMPLX_MULT_SELF_SCALAR(yAc->yIdVgb, GNorm * width * LNorm);
|
|
CMPLX_MULT_SELF_SCALAR(yAc->yIsVdb, GNorm * width * LNorm);
|
|
CMPLX_MULT_SELF_SCALAR(yAc->yIsVsb, GNorm * width * LNorm);
|
|
CMPLX_MULT_SELF_SCALAR(yAc->yIsVgb, GNorm * width * LNorm);
|
|
CMPLX_MULT_SELF_SCALAR(yAc->yIgVdb, GNorm * width * LNorm);
|
|
CMPLX_MULT_SELF_SCALAR(yAc->yIgVsb, GNorm * width * LNorm);
|
|
CMPLX_MULT_SELF_SCALAR(yAc->yIgVgb, GNorm * width * LNorm);
|
|
pDevice->pStats->miscTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
|
|
|
return (AcAnalysisMethod);
|
|
}
|
|
|
|
BOOLEAN
|
|
TWOsorSolve(TWOdevice *pDevice, double *xReal, double *xImag,
|
|
double omega)
|
|
{
|
|
double dxdy;
|
|
double wRelax = 1.0; /* SOR relaxation parameter */
|
|
double *rhsReal = pDevice->rhs;
|
|
double *rhsSOR = pDevice->rhsImag;
|
|
BOOLEAN SORConverged = FALSE;
|
|
BOOLEAN SORFailed = FALSE;
|
|
int numEqns = pDevice->numEqns;
|
|
int iterationNum;
|
|
int indexN, indexP;
|
|
int index, eIndex;
|
|
TWOnode *pNode;
|
|
TWOelem *pElem;
|
|
|
|
/* clear xReal and xImag arrays */
|
|
for (index = 1; index <= numEqns; index++) {
|
|
xReal[index] = 0.0;
|
|
xImag[index] = 0.0;
|
|
}
|
|
|
|
iterationNum = 1;
|
|
for (; (!SORConverged) &&(!SORFailed); iterationNum++) {
|
|
for (index = 1; index <= numEqns; index++) {
|
|
rhsSOR[index] = 0.0;
|
|
}
|
|
for (eIndex = 1; eIndex <= pDevice->numElems; eIndex++) {
|
|
pElem = pDevice->elements[eIndex];
|
|
dxdy = 0.25 * pElem->dx * pElem->dy;
|
|
for (index = 0; index <= 3; index++) {
|
|
pNode = pElem->pNodes[index];
|
|
if ((pNode->nodeType != CONTACT) && (pElem->elemType == SEMICON)) {
|
|
if (!OneCarrier) {
|
|
indexN = pNode->nEqn;
|
|
indexP = pNode->pEqn;
|
|
rhsSOR[indexN] -= dxdy * omega * xImag[indexN];
|
|
rhsSOR[indexP] += dxdy * omega * xImag[indexP];
|
|
} else if (OneCarrier == N_TYPE) {
|
|
indexN = pNode->nEqn;
|
|
rhsSOR[indexN] -= dxdy * omega * xImag[indexN];
|
|
} else if (OneCarrier == P_TYPE) {
|
|
indexP = pNode->pEqn;
|
|
rhsSOR[indexP] += dxdy * omega * xImag[indexP];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* now add the terms from rhs to rhsImag */
|
|
for (index = 1; index <= numEqns; index++) {
|
|
rhsSOR[index] += rhsReal[index];
|
|
}
|
|
|
|
/* compute xReal(k+1). solution stored in rhsImag */
|
|
spSolve(pDevice->matrix, rhsSOR, rhsSOR, NIL(spREAL), NIL(spREAL));
|
|
/* modify solution when wRelax is not 1 */
|
|
if (wRelax != 1) {
|
|
for (index = 1; index <= numEqns; index++) {
|
|
rhsSOR[index] = (1 - wRelax) * xReal[index] +
|
|
wRelax * rhsSOR[index];
|
|
}
|
|
}
|
|
if (iterationNum > 1) {
|
|
SORConverged = hasSORConverged(xReal, rhsSOR, numEqns);
|
|
}
|
|
/* copy real solution into xReal */
|
|
for (index = 1; index <= numEqns; index++) {
|
|
xReal[index] = rhsSOR[index];
|
|
}
|
|
|
|
/* now compute the imaginary part of the solution xImag */
|
|
for (index = 1; index <= numEqns; index++) {
|
|
rhsSOR[index] = 0.0;
|
|
}
|
|
for (eIndex = 1; eIndex <= pDevice->numElems; eIndex++) {
|
|
pElem = pDevice->elements[eIndex];
|
|
dxdy = 0.25 * pElem->dx * pElem->dy;
|
|
for (index = 0; index <= 3; index++) {
|
|
pNode = pElem->pNodes[index];
|
|
if ((pNode->nodeType != CONTACT) && (pElem->elemType == SEMICON)) {
|
|
if (!OneCarrier) {
|
|
indexN = pNode->nEqn;
|
|
indexP = pNode->pEqn;
|
|
rhsSOR[indexN] += dxdy * omega * xReal[indexN];
|
|
rhsSOR[indexP] -= dxdy * omega * xReal[indexP];
|
|
} else if (OneCarrier == N_TYPE) {
|
|
indexN = pNode->nEqn;
|
|
rhsSOR[indexN] += dxdy * omega * xReal[indexN];
|
|
} else if (OneCarrier == P_TYPE) {
|
|
indexP = pNode->pEqn;
|
|
rhsSOR[indexP] -= dxdy * omega * xReal[indexP];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* compute xImag(k+1) */
|
|
spSolve(pDevice->matrix, rhsSOR, rhsSOR, NIL(spREAL), NIL(spREAL));
|
|
/* modify solution when wRelax is not 1 */
|
|
if (wRelax != 1) {
|
|
for (index = 1; index <= numEqns; index++) {
|
|
rhsSOR[index] = (1 - wRelax) * xImag[index] +
|
|
wRelax * rhsSOR[index];
|
|
}
|
|
}
|
|
if (iterationNum > 1) {
|
|
SORConverged = SORConverged && hasSORConverged(xImag, rhsSOR, numEqns);
|
|
}
|
|
/* copy imag solution into xImag */
|
|
for (index = 1; index <= numEqns; index++) {
|
|
xImag[index] = rhsSOR[index];
|
|
}
|
|
if ((iterationNum > 4) && !SORConverged) {
|
|
SORFailed = TRUE;
|
|
}
|
|
if (TWOacDebug)
|
|
printf("SOR iteration number = %d\n", iterationNum);
|
|
}
|
|
return (SORFailed);
|
|
}
|
|
|
|
|
|
SPcomplex *
|
|
contactAdmittance(TWOdevice *pDevice, TWOcontact *pContact, BOOLEAN delVContact,
|
|
double *xReal, double *xImag, SPcomplex *cOmega)
|
|
{
|
|
TWOnode *pNode, *pHNode = NULL, *pVNode = NULL;
|
|
TWOedge *pHEdge = NULL, *pVEdge = NULL;
|
|
int index, i, indexPsi, indexN, indexP, numContactNodes;
|
|
TWOelem *pElem;
|
|
SPcomplex psiAc, nAc, pAc;
|
|
SPcomplex prod1, prod2, sum;
|
|
double yReal, yImag;
|
|
double temp;
|
|
|
|
CMPLX_ASSIGN_VALUE(yTotal, 0.0, 0.0);
|
|
|
|
numContactNodes = pContact->numNodes;
|
|
for (index = 0; index < numContactNodes; index++) {
|
|
pNode = pContact->pNodes[index];
|
|
for (i = 0; i <= 3; i++) {
|
|
pElem = pNode->pElems[i];
|
|
if (pElem != NIL(TWOelem)) {
|
|
switch (i) {
|
|
case 0:
|
|
/* the TL element */
|
|
pHNode = pElem->pBLNode;
|
|
pVNode = pElem->pTRNode;
|
|
pHEdge = pElem->pBotEdge;
|
|
pVEdge = pElem->pRightEdge;
|
|
if (pElem->elemType == SEMICON) {
|
|
/* compute the derivatives with n,p */
|
|
if (pHNode->nodeType != CONTACT) {
|
|
indexN = pHNode->nEqn;
|
|
indexP = pHNode->pEqn;
|
|
CMPLX_ASSIGN_VALUE(nAc, xReal[indexN], xImag[indexN]);
|
|
CMPLX_ASSIGN_VALUE(pAc, xReal[indexP], xImag[indexP]);
|
|
CMPLX_MULT_SCALAR(prod1, nAc, pHEdge->dJnDn);
|
|
CMPLX_MULT_SCALAR(prod2, pAc, pHEdge->dJpDp);
|
|
CMPLX_ADD(sum, prod1, prod2);
|
|
CMPLX_MULT_SCALAR(prod1, sum, 0.5 * pElem->dy);
|
|
CMPLX_SUBT_ASSIGN(yTotal, prod1);
|
|
}
|
|
if (pVNode->nodeType != CONTACT) {
|
|
indexN = pVNode->nEqn;
|
|
indexP = pVNode->pEqn;
|
|
CMPLX_ASSIGN_VALUE(nAc, xReal[indexN], xImag[indexN]);
|
|
CMPLX_ASSIGN_VALUE(pAc, xReal[indexP], xImag[indexP]);
|
|
CMPLX_MULT_SCALAR(prod1, nAc, pVEdge->dJnDn);
|
|
CMPLX_MULT_SCALAR(prod2, pAc, pVEdge->dJpDp);
|
|
CMPLX_ADD(sum, prod1, prod2);
|
|
CMPLX_MULT_SCALAR(prod1, sum, 0.5 * pElem->dx);
|
|
CMPLX_SUBT_ASSIGN(yTotal, prod1);
|
|
}
|
|
}
|
|
break;
|
|
case 1:
|
|
/* the TR element */
|
|
pHNode = pElem->pBRNode;
|
|
pVNode = pElem->pTLNode;
|
|
pHEdge = pElem->pBotEdge;
|
|
pVEdge = pElem->pLeftEdge;
|
|
if (pElem->elemType == SEMICON) {
|
|
/* compute the derivatives with n,p */
|
|
if (pHNode->nodeType != CONTACT) {
|
|
indexN = pHNode->nEqn;
|
|
indexP = pHNode->pEqn;
|
|
CMPLX_ASSIGN_VALUE(nAc, xReal[indexN], xImag[indexN]);
|
|
CMPLX_ASSIGN_VALUE(pAc, xReal[indexP], xImag[indexP]);
|
|
CMPLX_MULT_SCALAR(prod1, nAc, pHEdge->dJnDnP1);
|
|
CMPLX_MULT_SCALAR(prod2, pAc, pHEdge->dJpDpP1);
|
|
CMPLX_ADD(sum, prod1, prod2);
|
|
CMPLX_MULT_SCALAR(prod1, sum, 0.5 * pElem->dy);
|
|
CMPLX_ADD_ASSIGN(yTotal, prod1);
|
|
}
|
|
if (pVNode->nodeType != CONTACT) {
|
|
indexN = pVNode->nEqn;
|
|
indexP = pVNode->pEqn;
|
|
CMPLX_ASSIGN_VALUE(nAc, xReal[indexN], xImag[indexN]);
|
|
CMPLX_ASSIGN_VALUE(pAc, xReal[indexP], xImag[indexP]);
|
|
CMPLX_MULT_SCALAR(prod1, nAc, pVEdge->dJnDn);
|
|
CMPLX_MULT_SCALAR(prod2, pAc, pVEdge->dJpDp);
|
|
CMPLX_ADD(sum, prod1, prod2);
|
|
CMPLX_MULT_SCALAR(prod1, sum, 0.5 * pElem->dx);
|
|
CMPLX_SUBT_ASSIGN(yTotal, prod1);
|
|
}
|
|
}
|
|
break;
|
|
case 2:
|
|
/* the BR element */
|
|
pHNode = pElem->pTRNode;
|
|
pVNode = pElem->pBLNode;
|
|
pHEdge = pElem->pTopEdge;
|
|
pVEdge = pElem->pLeftEdge;
|
|
if (pElem->elemType == SEMICON) {
|
|
/* compute the derivatives with n,p */
|
|
if (pHNode->nodeType != CONTACT) {
|
|
indexN = pHNode->nEqn;
|
|
indexP = pHNode->pEqn;
|
|
CMPLX_ASSIGN_VALUE(nAc, xReal[indexN], xImag[indexN]);
|
|
CMPLX_ASSIGN_VALUE(pAc, xReal[indexP], xImag[indexP]);
|
|
CMPLX_MULT_SCALAR(prod1, nAc, pHEdge->dJnDnP1);
|
|
CMPLX_MULT_SCALAR(prod2, pAc, pHEdge->dJpDpP1);
|
|
CMPLX_ADD(sum, prod1, prod2);
|
|
CMPLX_MULT_SCALAR(prod1, sum, 0.5 * pElem->dy);
|
|
CMPLX_ADD_ASSIGN(yTotal, prod1);
|
|
}
|
|
if (pVNode->nodeType != CONTACT) {
|
|
indexN = pVNode->nEqn;
|
|
indexP = pVNode->pEqn;
|
|
CMPLX_ASSIGN_VALUE(nAc, xReal[indexN], xImag[indexN]);
|
|
CMPLX_ASSIGN_VALUE(pAc, xReal[indexP], xImag[indexP]);
|
|
CMPLX_MULT_SCALAR(prod1, nAc, pVEdge->dJnDnP1);
|
|
CMPLX_MULT_SCALAR(prod2, pAc, pVEdge->dJpDpP1);
|
|
CMPLX_ADD(sum, prod1, prod2);
|
|
CMPLX_MULT_SCALAR(prod1, sum, 0.5 * pElem->dx);
|
|
CMPLX_ADD_ASSIGN(yTotal, prod1);
|
|
}
|
|
}
|
|
break;
|
|
case 3:
|
|
/* the BL element */
|
|
pHNode = pElem->pTLNode;
|
|
pVNode = pElem->pBRNode;
|
|
pHEdge = pElem->pTopEdge;
|
|
pVEdge = pElem->pRightEdge;
|
|
if (pElem->elemType == SEMICON) {
|
|
/* compute the derivatives with n,p */
|
|
if (pHNode->nodeType != CONTACT) {
|
|
indexN = pHNode->nEqn;
|
|
indexP = pHNode->pEqn;
|
|
CMPLX_ASSIGN_VALUE(nAc, xReal[indexN], xImag[indexN]);
|
|
CMPLX_ASSIGN_VALUE(pAc, xReal[indexP], xImag[indexP]);
|
|
CMPLX_MULT_SCALAR(prod1, nAc, pHEdge->dJnDn);
|
|
CMPLX_MULT_SCALAR(prod2, pAc, pHEdge->dJpDp);
|
|
CMPLX_ADD(sum, prod1, prod2);
|
|
CMPLX_MULT_SCALAR(prod1, sum, 0.5 * pElem->dy);
|
|
CMPLX_SUBT_ASSIGN(yTotal, prod1);
|
|
}
|
|
if (pVNode->nodeType != CONTACT) {
|
|
indexN = pVNode->nEqn;
|
|
indexP = pVNode->pEqn;
|
|
CMPLX_ASSIGN_VALUE(nAc, xReal[indexN], xImag[indexN]);
|
|
CMPLX_ASSIGN_VALUE(pAc, xReal[indexP], xImag[indexP]);
|
|
CMPLX_MULT_SCALAR(prod1, nAc, pVEdge->dJnDnP1);
|
|
CMPLX_MULT_SCALAR(prod2, pAc, pVEdge->dJpDpP1);
|
|
CMPLX_ADD(sum, prod1, prod2);
|
|
CMPLX_MULT_SCALAR(prod1, sum, 0.5 * pElem->dx);
|
|
CMPLX_ADD_ASSIGN(yTotal, prod1);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
if (pElem->elemType == SEMICON) {
|
|
if (pHNode->nodeType != CONTACT) {
|
|
indexPsi = pHNode->psiEqn;
|
|
CMPLX_ASSIGN_VALUE(psiAc, xReal[indexPsi], xImag[indexPsi]);
|
|
temp = 0.5 * pElem->dy * (pHEdge->dJnDpsiP1 + pHEdge->dJpDpsiP1);
|
|
CMPLX_MULT_SCALAR(prod1, psiAc, temp);
|
|
CMPLX_ADD_ASSIGN(yTotal, prod1);
|
|
if (delVContact) {
|
|
CMPLX_ADD_SELF_SCALAR(yTotal, -temp);
|
|
}
|
|
}
|
|
if (pVNode->nodeType != CONTACT) {
|
|
indexPsi = pVNode->psiEqn;
|
|
CMPLX_ASSIGN_VALUE(psiAc, xReal[indexPsi], xImag[indexPsi]);
|
|
temp = 0.5 * pElem->dx * (pVEdge->dJnDpsiP1 + pVEdge->dJpDpsiP1);
|
|
CMPLX_MULT_SCALAR(prod1, psiAc, temp);
|
|
CMPLX_ADD_ASSIGN(yTotal, prod1);
|
|
if (delVContact) {
|
|
CMPLX_ADD_SELF_SCALAR(yTotal, -temp);
|
|
}
|
|
}
|
|
}
|
|
/* displacement current terms */
|
|
if (pHNode->nodeType != CONTACT) {
|
|
indexPsi = pHNode->psiEqn;
|
|
CMPLX_ASSIGN_VALUE(psiAc, xReal[indexPsi], xImag[indexPsi]);
|
|
CMPLX_MULT_SCALAR(prod1, *cOmega, pElem->epsRel * 0.5 * pElem->dyOverDx);
|
|
CMPLX_MULT(prod2, prod1, psiAc);
|
|
CMPLX_SUBT_ASSIGN(yTotal, prod2);
|
|
if (delVContact) {
|
|
CMPLX_ADD_ASSIGN(yTotal, prod1);
|
|
}
|
|
}
|
|
if (pVNode->nodeType != CONTACT) {
|
|
indexPsi = pVNode->psiEqn;
|
|
CMPLX_ASSIGN_VALUE(psiAc, xReal[indexPsi], xImag[indexPsi]);
|
|
CMPLX_MULT_SCALAR(prod1, *cOmega, pElem->epsRel * 0.5 * pElem->dxOverDy);
|
|
CMPLX_MULT(prod2, prod1, psiAc);
|
|
CMPLX_SUBT_ASSIGN(yTotal, prod2);
|
|
if (delVContact) {
|
|
CMPLX_ADD_ASSIGN(yTotal, prod1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return (&yTotal); /* XXX */
|
|
}
|
|
|
|
|
|
SPcomplex *
|
|
oxideAdmittance(TWOdevice *pDevice, TWOcontact *pContact, BOOLEAN delVContact,
|
|
double *xReal, double *xImag, SPcomplex *cOmega)
|
|
{
|
|
TWOnode *pNode, *pHNode = NULL, *pVNode = NULL;
|
|
TWOedge *pHEdge, *pVEdge;
|
|
int index, i, indexPsi, numContactNodes;
|
|
TWOelem *pElem;
|
|
SPcomplex psiAc, nAc, pAc;
|
|
SPcomplex prod1, prod2, sum;
|
|
double yReal, yImag;
|
|
|
|
CMPLX_ASSIGN_VALUE(yTotal, 0.0, 0.0);
|
|
|
|
numContactNodes = pContact->numNodes;
|
|
for (index = 0; index < numContactNodes; index++) {
|
|
pNode = pContact->pNodes[index];
|
|
for (i = 0; i <= 3; i++) {
|
|
pElem = pNode->pElems[i];
|
|
if (pElem != NIL(TWOelem)) {
|
|
switch (i) {
|
|
case 0:
|
|
/* the TL element */
|
|
pHNode = pElem->pBLNode;
|
|
pVNode = pElem->pTRNode;
|
|
pHEdge = pElem->pBotEdge;
|
|
pVEdge = pElem->pRightEdge;
|
|
break;
|
|
case 1:
|
|
/* the TR element */
|
|
pHNode = pElem->pBRNode;
|
|
pVNode = pElem->pTLNode;
|
|
pHEdge = pElem->pBotEdge;
|
|
pVEdge = pElem->pLeftEdge;
|
|
break;
|
|
case 2:
|
|
/* the BR element */
|
|
pHNode = pElem->pTRNode;
|
|
pVNode = pElem->pBLNode;
|
|
pHEdge = pElem->pTopEdge;
|
|
pVEdge = pElem->pLeftEdge;
|
|
break;
|
|
case 3:
|
|
/* the BL element */
|
|
pHNode = pElem->pTLNode;
|
|
pVNode = pElem->pBRNode;
|
|
pHEdge = pElem->pTopEdge;
|
|
pVEdge = pElem->pRightEdge;
|
|
break;
|
|
}
|
|
/* displacement current terms */
|
|
if (pHNode->nodeType != CONTACT) {
|
|
indexPsi = pHNode->psiEqn;
|
|
CMPLX_ASSIGN_VALUE(psiAc, xReal[indexPsi], xImag[indexPsi]);
|
|
CMPLX_MULT_SCALAR(prod1, *cOmega, pElem->epsRel * 0.5 * pElem->dyOverDx);
|
|
CMPLX_MULT(prod2, prod1, psiAc);
|
|
CMPLX_SUBT_ASSIGN(yTotal, prod2);
|
|
if (delVContact) {
|
|
CMPLX_ADD_ASSIGN(yTotal, prod1);
|
|
}
|
|
}
|
|
if (pVNode->nodeType != CONTACT) {
|
|
indexPsi = pVNode->psiEqn;
|
|
CMPLX_ASSIGN_VALUE(psiAc, xReal[indexPsi], xImag[indexPsi]);
|
|
CMPLX_MULT_SCALAR(prod1, *cOmega, pElem->epsRel * 0.5 * pElem->dxOverDy);
|
|
CMPLX_MULT(prod2, prod1, psiAc);
|
|
CMPLX_SUBT_ASSIGN(yTotal, prod2);
|
|
if (delVContact) {
|
|
CMPLX_ADD_ASSIGN(yTotal, prod1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return (&yTotal);
|
|
}
|
|
|
|
void
|
|
NUMD2ys(TWOdevice *pDevice, SPcomplex *s, SPcomplex *yIn)
|
|
{
|
|
TWOnode *pNode;
|
|
TWOelem *pElem;
|
|
int index, eIndex;
|
|
double dxdy;
|
|
double *solnReal, *solnImag;
|
|
double *rhsReal, *rhsImag;
|
|
SPcomplex yAc, *y;
|
|
BOOLEAN deltaVContact = FALSE;
|
|
SPcomplex temp, cOmega;
|
|
|
|
/*
|
|
* change context names of solution vectors for ac analysis dcDeltaSolution
|
|
* stores the real part and copiedSolution stores the imaginary part of the
|
|
* ac solution vector
|
|
*/
|
|
pDevice->solverType = SLV_SMSIG;
|
|
rhsReal = pDevice->rhs;
|
|
rhsImag = pDevice->rhsImag;
|
|
solnReal = pDevice->dcDeltaSolution;
|
|
solnImag = pDevice->copiedSolution;
|
|
|
|
/* use a normalized radian frequency */
|
|
CMPLX_MULT_SCALAR(cOmega, *s, TNorm);
|
|
for (index = 1; index <= pDevice->numEqns; index++) {
|
|
rhsImag[index] = 0.0;
|
|
}
|
|
/* solve the system of equations directly */
|
|
if (!OneCarrier) {
|
|
TWO_jacLoad(pDevice);
|
|
} else if (OneCarrier == N_TYPE) {
|
|
TWONjacLoad(pDevice);
|
|
} else if (OneCarrier == P_TYPE) {
|
|
TWOPjacLoad(pDevice);
|
|
}
|
|
storeNewRhs(pDevice, pDevice->pLastContact);
|
|
|
|
spSetComplex(pDevice->matrix);
|
|
for (eIndex = 1; eIndex <= pDevice->numElems; eIndex++) {
|
|
pElem = pDevice->elements[eIndex];
|
|
if (pElem->elemType == SEMICON) {
|
|
dxdy = 0.25 * pElem->dx * pElem->dy;
|
|
for (index = 0; index <= 3; index++) {
|
|
pNode = pElem->pNodes[index];
|
|
if (pNode->nodeType != CONTACT) {
|
|
if (!OneCarrier) {
|
|
CMPLX_MULT_SCALAR(temp, cOmega, dxdy);
|
|
spADD_COMPLEX_ELEMENT(pNode->fNN, -temp.real, -temp.imag);
|
|
spADD_COMPLEX_ELEMENT(pNode->fPP, temp.real, temp.imag);
|
|
} else if (OneCarrier == N_TYPE) {
|
|
CMPLX_MULT_SCALAR(temp, cOmega, dxdy);
|
|
spADD_COMPLEX_ELEMENT(pNode->fNN, -temp.real, -temp.imag);
|
|
} else if (OneCarrier == P_TYPE) {
|
|
CMPLX_MULT_SCALAR(temp, cOmega, dxdy);
|
|
spADD_COMPLEX_ELEMENT(pNode->fPP, temp.real, temp.imag);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
spFactor(pDevice->matrix);
|
|
spSolve(pDevice->matrix, rhsReal, solnReal, rhsImag, solnImag);
|
|
y = contactAdmittance(pDevice, pDevice->pFirstContact, deltaVContact,
|
|
solnReal, solnImag, &cOmega);
|
|
CMPLX_ASSIGN_VALUE(yAc, y->real, y->imag);
|
|
CMPLX_ASSIGN(*yIn, yAc);
|
|
CMPLX_NEGATE_SELF(*yIn);
|
|
CMPLX_MULT_SELF_SCALAR(*yIn, GNorm * pDevice->width * LNorm);
|
|
}
|
|
|
|
|
|
void
|
|
NBJT2ys(TWOdevice *pDevice, SPcomplex *s, SPcomplex *yIeVce, SPcomplex *yIcVce,
|
|
SPcomplex *yIeVbe, SPcomplex *yIcVbe)
|
|
{
|
|
TWOcontact *pEmitContact = pDevice->pLastContact;
|
|
TWOcontact *pColContact = pDevice->pFirstContact;
|
|
TWOcontact *pBaseContact = pDevice->pFirstContact->next;
|
|
TWOnode *pNode;
|
|
TWOelem *pElem;
|
|
int index, eIndex;
|
|
double width = pDevice->width;
|
|
double dxdy;
|
|
double *solnReal, *solnImag;
|
|
double *rhsReal, *rhsImag;
|
|
SPcomplex *y;
|
|
SPcomplex pIeVce, pIcVce, pIeVbe, pIcVbe;
|
|
SPcomplex temp, cOmega;
|
|
|
|
pDevice->solverType = SLV_SMSIG;
|
|
rhsReal = pDevice->rhs;
|
|
rhsImag = pDevice->rhsImag;
|
|
solnReal = pDevice->dcDeltaSolution;
|
|
solnImag = pDevice->copiedSolution;
|
|
|
|
/* use a normalized radian frequency */
|
|
CMPLX_MULT_SCALAR(cOmega, *s, TNorm);
|
|
|
|
for (index = 1; index <= pDevice->numEqns; index++) {
|
|
rhsImag[index] = 0.0;
|
|
}
|
|
/* solve the system of equations directly */
|
|
if (!OneCarrier) {
|
|
TWO_jacLoad(pDevice);
|
|
} else if (OneCarrier == N_TYPE) {
|
|
TWONjacLoad(pDevice);
|
|
} else if (OneCarrier == P_TYPE) {
|
|
TWOPjacLoad(pDevice);
|
|
}
|
|
storeNewRhs(pDevice, pColContact);
|
|
spSetComplex(pDevice->matrix);
|
|
for (eIndex = 1; eIndex <= pDevice->numElems; eIndex++) {
|
|
pElem = pDevice->elements[eIndex];
|
|
if (pElem->elemType == SEMICON) {
|
|
dxdy = 0.25 * pElem->dx * pElem->dy;
|
|
for (index = 0; index <= 3; index++) {
|
|
pNode = pElem->pNodes[index];
|
|
if (pNode->nodeType != CONTACT) {
|
|
if (!OneCarrier) {
|
|
CMPLX_MULT_SCALAR(temp, cOmega, dxdy);
|
|
spADD_COMPLEX_ELEMENT(pNode->fNN, -temp.real, -temp.imag);
|
|
spADD_COMPLEX_ELEMENT(pNode->fPP, temp.real, temp.imag);
|
|
} else if (OneCarrier == N_TYPE) {
|
|
CMPLX_MULT_SCALAR(temp, cOmega, dxdy);
|
|
spADD_COMPLEX_ELEMENT(pNode->fNN, -temp.real, -temp.imag);
|
|
} else if (OneCarrier == P_TYPE) {
|
|
CMPLX_MULT_SCALAR(temp, cOmega, dxdy);
|
|
spADD_COMPLEX_ELEMENT(pNode->fPP, temp.real, temp.imag);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
spFactor(pDevice->matrix);
|
|
spSolve(pDevice->matrix, rhsReal, solnReal, rhsImag, solnImag);
|
|
|
|
y = contactAdmittance(pDevice, pEmitContact, FALSE,
|
|
solnReal, solnImag, &cOmega);
|
|
CMPLX_ASSIGN_VALUE(pIeVce, y->real, y->imag);
|
|
y = contactAdmittance(pDevice, pColContact, TRUE,
|
|
solnReal, solnImag, &cOmega);
|
|
CMPLX_ASSIGN_VALUE(pIcVce, y->real, y->imag);
|
|
for (index = 1; index <= pDevice->numEqns; index++) {
|
|
rhsImag[index] = 0.0;
|
|
}
|
|
storeNewRhs(pDevice, pBaseContact);
|
|
/* don't need to LU factor the jacobian since it exists */
|
|
spSolve(pDevice->matrix, rhsReal, solnReal, rhsImag, solnImag);
|
|
y = contactAdmittance(pDevice, pEmitContact, FALSE,
|
|
solnReal, solnImag, &cOmega);
|
|
CMPLX_ASSIGN_VALUE(pIeVbe, y->real, y->imag);
|
|
y = contactAdmittance(pDevice, pColContact, FALSE,
|
|
solnReal, solnImag, &cOmega);
|
|
CMPLX_ASSIGN_VALUE(pIcVbe, y->real, y->imag);
|
|
|
|
|
|
CMPLX_ASSIGN(*yIeVce, pIeVce);
|
|
CMPLX_ASSIGN(*yIeVbe, pIeVbe);
|
|
CMPLX_ASSIGN(*yIcVce, pIcVce);
|
|
CMPLX_ASSIGN(*yIcVbe, pIcVbe);
|
|
CMPLX_MULT_SELF_SCALAR(*yIeVce, GNorm * width * LNorm);
|
|
CMPLX_MULT_SELF_SCALAR(*yIeVbe, GNorm * width * LNorm);
|
|
CMPLX_MULT_SELF_SCALAR(*yIcVce, GNorm * width * LNorm);
|
|
CMPLX_MULT_SELF_SCALAR(*yIcVbe, GNorm * width * LNorm);
|
|
}
|
|
|
|
void
|
|
NUMOSys(TWOdevice *pDevice, SPcomplex *s, struct mosAdmittances *yAc)
|
|
{
|
|
TWOcontact *pDContact = pDevice->pFirstContact;
|
|
TWOcontact *pGContact = pDevice->pFirstContact->next;
|
|
TWOcontact *pSContact = pDevice->pFirstContact->next->next;
|
|
TWOcontact *pBContact = pDevice->pLastContact;
|
|
TWOnode *pNode;
|
|
TWOelem *pElem;
|
|
int index, eIndex;
|
|
double width = pDevice->width;
|
|
double dxdy;
|
|
double *rhsReal, *rhsImag;
|
|
double *solnReal, *solnImag;
|
|
SPcomplex *y;
|
|
SPcomplex temp, cOmega;
|
|
|
|
pDevice->solverType = SLV_SMSIG;
|
|
rhsReal = pDevice->rhs;
|
|
rhsImag = pDevice->rhsImag;
|
|
solnReal = pDevice->dcDeltaSolution;
|
|
solnImag = pDevice->copiedSolution;
|
|
|
|
/* use a normalized radian frequency */
|
|
CMPLX_MULT_SCALAR(cOmega, *s, TNorm);
|
|
for (index = 1; index <= pDevice->numEqns; index++) {
|
|
rhsImag[index] = 0.0;
|
|
}
|
|
/* solve the system of equations directly */
|
|
if (!OneCarrier) {
|
|
TWO_jacLoad(pDevice);
|
|
} else if (OneCarrier == N_TYPE) {
|
|
TWONjacLoad(pDevice);
|
|
} else if (OneCarrier == P_TYPE) {
|
|
TWOPjacLoad(pDevice);
|
|
}
|
|
storeNewRhs(pDevice, pDContact);
|
|
spSetComplex(pDevice->matrix);
|
|
|
|
for (eIndex = 1; eIndex <= pDevice->numElems; eIndex++) {
|
|
pElem = pDevice->elements[eIndex];
|
|
if (pElem->elemType == SEMICON) {
|
|
dxdy = 0.25 * pElem->dx * pElem->dy;
|
|
for (index = 0; index <= 3; index++) {
|
|
pNode = pElem->pNodes[index];
|
|
if (pNode->nodeType != CONTACT) {
|
|
if (!OneCarrier) {
|
|
CMPLX_MULT_SCALAR(temp, cOmega, dxdy);
|
|
spADD_COMPLEX_ELEMENT(pNode->fNN, -temp.real, -temp.imag);
|
|
spADD_COMPLEX_ELEMENT(pNode->fPP, temp.real, temp.imag);
|
|
} else if (OneCarrier == N_TYPE) {
|
|
CMPLX_MULT_SCALAR(temp, cOmega, dxdy);
|
|
spADD_COMPLEX_ELEMENT(pNode->fNN, -temp.real, -temp.imag);
|
|
} else if (OneCarrier == P_TYPE) {
|
|
CMPLX_MULT_SCALAR(temp, cOmega, dxdy);
|
|
spADD_COMPLEX_ELEMENT(pNode->fPP, temp.real, temp.imag);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
spFactor(pDevice->matrix);
|
|
spSolve(pDevice->matrix, rhsReal, solnReal, rhsImag, solnImag);
|
|
|
|
y = contactAdmittance(pDevice, pDContact, TRUE,
|
|
solnReal, solnImag, &cOmega);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIdVdb, y->real, y->imag);
|
|
y = contactAdmittance(pDevice, pSContact, FALSE,
|
|
solnReal, solnImag, &cOmega);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIsVdb, y->real, y->imag);
|
|
y = GateTypeAdmittance(pDevice, pGContact, FALSE,
|
|
solnReal, solnImag, &cOmega);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIgVdb, y->real, y->imag);
|
|
|
|
for (index = 1; index <= pDevice->numEqns; index++) {
|
|
rhsImag[index] = 0.0;
|
|
}
|
|
storeNewRhs(pDevice, pSContact);
|
|
/* don't need to LU factor the jacobian since it exists */
|
|
spSolve(pDevice->matrix, rhsReal, solnReal, rhsImag, solnImag);
|
|
y = contactAdmittance(pDevice, pDContact, FALSE,
|
|
solnReal, solnImag, &cOmega);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIdVsb, y->real, y->imag);
|
|
y = contactAdmittance(pDevice, pSContact, TRUE,
|
|
solnReal, solnImag, &cOmega);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIsVsb, y->real, y->imag);
|
|
y = GateTypeAdmittance(pDevice, pGContact, FALSE,
|
|
solnReal, solnImag, &cOmega);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIgVsb, y->real, y->imag);
|
|
for (index = 1; index <= pDevice->numEqns; index++) {
|
|
rhsImag[index] = 0.0;
|
|
}
|
|
storeNewRhs(pDevice, pGContact);
|
|
spSolve(pDevice->matrix, rhsReal, solnReal, rhsImag, solnImag);
|
|
y = contactAdmittance(pDevice, pDContact, FALSE,
|
|
solnReal, solnImag, &cOmega);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIdVgb, y->real, y->imag);
|
|
y = contactAdmittance(pDevice, pSContact, FALSE,
|
|
solnReal, solnImag, &cOmega);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIsVgb, y->real, y->imag);
|
|
y = GateTypeAdmittance(pDevice, pGContact, TRUE,
|
|
solnReal, solnImag, &cOmega);
|
|
CMPLX_ASSIGN_VALUE(yAc->yIgVgb, y->real, y->imag);
|
|
|
|
CMPLX_MULT_SELF_SCALAR(yAc->yIdVdb, GNorm * width * LNorm);
|
|
CMPLX_MULT_SELF_SCALAR(yAc->yIdVsb, GNorm * width * LNorm);
|
|
CMPLX_MULT_SELF_SCALAR(yAc->yIdVgb, GNorm * width * LNorm);
|
|
CMPLX_MULT_SELF_SCALAR(yAc->yIsVdb, GNorm * width * LNorm);
|
|
CMPLX_MULT_SELF_SCALAR(yAc->yIsVsb, GNorm * width * LNorm);
|
|
CMPLX_MULT_SELF_SCALAR(yAc->yIsVgb, GNorm * width * LNorm);
|
|
CMPLX_MULT_SELF_SCALAR(yAc->yIgVdb, GNorm * width * LNorm);
|
|
CMPLX_MULT_SELF_SCALAR(yAc->yIgVsb, GNorm * width * LNorm);
|
|
CMPLX_MULT_SELF_SCALAR(yAc->yIgVgb, GNorm * width * LNorm);
|
|
}
|