📄 twoncont.c
字号:
/**********Copyright 1991 Regents of the University of California. All rights reserved.Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD GroupAuthor: 1991 David A. Gates, U. C. Berkeley CAD Group**********/#include <math.h>#include "numglobs.h"#include "numenum.h"#include "nummacs.h"#include "twomesh.h"#include "twodev.h"/* * Functions to setup and solve the continuity equations. * Both continuity equations are solved. * Separate functions are used for one continuity equation. */double integrate();/* * Setup matrix pointers to Jacobian entries and * store direct pointers with the nodes. */void TWONjacBuild(pDevice)TWOdevice *pDevice;{ char *matrix = pDevice->matrix; double *spGetElement(); TWOelem *pElem; TWOnode *pNode; TWOchannel *pCh; int eIndex, nIndex; int nextIndex; /* index of node to find next element */ int psiEqn, nEqn; /* scratch for deref'd eqn numbers */ int psiEqnTL, nEqnTL; int psiEqnTR, nEqnTR; int psiEqnBR, nEqnBR; int psiEqnBL, nEqnBL; int psiEqnInM, psiEqnInP; /* scratch for deref'd surface eqns */ int psiEqnOxM, psiEqnOxP; /* M= more negative, P= more positive */ for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) { pElem = pDevice->elements[ eIndex ]; /* first the self terms */ for ( nIndex = 0; nIndex <= 3; nIndex++ ) { pNode = pElem->pNodes[ nIndex ]; /* get poisson-only pointer */ psiEqn = pNode->psiEqn; pNode->fPsiPsi = spGetElement( matrix, psiEqn, psiEqn ); if ( pElem->elemType IS SEMICON ) { /* get continuity-coupling terms */ nEqn = pNode->nEqn; pNode->pEqn = 0; /* Throw pEqn number into garbage. */ /* pointers for additional terms */ pNode->fPsiN = spGetElement( matrix, psiEqn, nEqn ); pNode->fNPsi = spGetElement( matrix, nEqn, psiEqn ); pNode->fNN = spGetElement( matrix, nEqn, nEqn ); } else { nEqn = 0; } /* save equation indices */ switch ( nIndex ) { case 0: /* TL Node */ psiEqnTL = psiEqn; nEqnTL = nEqn; break; case 1: /* TR Node */ psiEqnTR = psiEqn; nEqnTR = nEqn; break; case 2: /* BR Node */ psiEqnBR = psiEqn; nEqnBR = nEqn; break; case 3: /* BL Node */ psiEqnBL = psiEqn; nEqnBL = nEqn; break; default: break; } } /* now terms to couple to adjacent nodes */ pNode = pElem->pTLNode; pNode->fPsiPsiiP1 = spGetElement(matrix, psiEqnTL, psiEqnTR ); pNode->fPsiPsijP1 = spGetElement(matrix, psiEqnTL, psiEqnBL ); if ( pElem->elemType IS SEMICON ) { /* continuity equation pointers */ pNode->fNPsiiP1 = spGetElement( matrix, nEqnTL, psiEqnTR ); pNode->fNNiP1 = spGetElement( matrix, nEqnTL, nEqnTR ); pNode->fNPsijP1 = spGetElement( matrix, nEqnTL, psiEqnBL ); pNode->fNNjP1 = spGetElement( matrix, nEqnTL, nEqnBL ); /* Surface Mobility Model depends on diagonal node values */ if ( MobDeriv AND SurfaceMobility AND pElem->channel ) { pNode->fNPsiiP1jP1 = spGetElement( matrix, nEqnTL, psiEqnBR ); pNode->fNNiP1jP1 = spGetElement( matrix, nEqnTL, nEqnBR ); } } pNode = pElem->pTRNode; pNode->fPsiPsiiM1 = spGetElement(matrix, psiEqnTR, psiEqnTL ); pNode->fPsiPsijP1 = spGetElement(matrix, psiEqnTR, psiEqnBR ); if ( pElem->elemType IS SEMICON ) { /* continuity equation pointers */ pNode->fNPsiiM1 = spGetElement( matrix, nEqnTR, psiEqnTL ); pNode->fNNiM1 = spGetElement( matrix, nEqnTR, nEqnTL ); pNode->fNPsijP1 = spGetElement( matrix, nEqnTR, psiEqnBR ); pNode->fNNjP1 = spGetElement( matrix, nEqnTR, nEqnBR ); /* Surface Mobility Model depends on diagonal node values */ if ( MobDeriv AND SurfaceMobility AND pElem->channel ) { pNode->fNPsiiM1jP1 = spGetElement( matrix, nEqnTR, psiEqnBL ); pNode->fNNiM1jP1 = spGetElement( matrix, nEqnTR, nEqnBL ); } } pNode = pElem->pBRNode; pNode->fPsiPsiiM1 = spGetElement(matrix, psiEqnBR, psiEqnBL ); pNode->fPsiPsijM1 = spGetElement(matrix, psiEqnBR, psiEqnTR ); if ( pElem->elemType IS SEMICON ) { /* continuity equation pointers */ pNode->fNPsiiM1 = spGetElement( matrix, nEqnBR, psiEqnBL ); pNode->fNNiM1 = spGetElement( matrix, nEqnBR, nEqnBL ); pNode->fNPsijM1 = spGetElement( matrix, nEqnBR, psiEqnTR ); pNode->fNNjM1 = spGetElement( matrix, nEqnBR, nEqnTR ); /* Surface Mobility Model depends on diagonal node values */ if ( MobDeriv AND SurfaceMobility AND pElem->channel ) { pNode->fNPsiiM1jM1 = spGetElement( matrix, nEqnBR, psiEqnTL ); pNode->fNNiM1jM1 = spGetElement( matrix, nEqnBR, nEqnTL ); } } pNode = pElem->pBLNode; pNode->fPsiPsiiP1 = spGetElement(matrix, psiEqnBL, psiEqnBR ); pNode->fPsiPsijM1 = spGetElement(matrix, psiEqnBL, psiEqnTL ); if ( pElem->elemType IS SEMICON ) { /* continuity equation pointers */ pNode->fNPsiiP1 = spGetElement( matrix, nEqnBL, psiEqnBR ); pNode->fNNiP1 = spGetElement( matrix, nEqnBL, nEqnBR ); pNode->fNPsijM1 = spGetElement( matrix, nEqnBL, psiEqnTL ); pNode->fNNjM1 = spGetElement( matrix, nEqnBL, nEqnTL ); /* Surface Mobility Model depends on diagonal node values */ if ( MobDeriv AND SurfaceMobility AND pElem->channel ) { pNode->fNPsiiP1jM1 = spGetElement( matrix, nEqnBL, psiEqnTR ); pNode->fNNiP1jM1 = spGetElement( matrix, nEqnBL, nEqnTR ); } } } /* * Add terms for surface-field of inversion-layer mobility model. * Elements MUST be made from silicon for this to work. * No empty elements are allowed. * Don't need these pointers if SurfaceMobility isn't set. */ if ( MobDeriv AND SurfaceMobility ) { for ( pCh = pDevice->pChannel; pCh ISNOT NIL(TWOchannel); pCh = pCh->next ) { pElem = pCh->pNElem; switch (pCh->type) { case 0: psiEqnInM = pElem->pBLNode->psiEqn; psiEqnInP = pElem->pBRNode->psiEqn; psiEqnOxM = pElem->pTLNode->psiEqn; psiEqnOxP = pElem->pTRNode->psiEqn; break; case 1: psiEqnInM = pElem->pTLNode->psiEqn; psiEqnInP = pElem->pBLNode->psiEqn; psiEqnOxM = pElem->pTRNode->psiEqn; psiEqnOxP = pElem->pBRNode->psiEqn; break; case 2: psiEqnInM = pElem->pTLNode->psiEqn; psiEqnInP = pElem->pTRNode->psiEqn; psiEqnOxM = pElem->pBLNode->psiEqn; psiEqnOxP = pElem->pBRNode->psiEqn; break; case 3: psiEqnInM = pElem->pTRNode->psiEqn; psiEqnInP = pElem->pBRNode->psiEqn; psiEqnOxM = pElem->pTLNode->psiEqn; psiEqnOxP = pElem->pBLNode->psiEqn; break; } pElem = pCh->pSeed; nextIndex = (pCh->type + 2)%4; while (pElem && pElem->channel == pCh->id) { for ( nIndex = 0; nIndex <= 3; nIndex++ ) { pNode = pElem->pNodes[ nIndex ]; psiEqn = pNode->psiEqn; nEqn = pNode->nEqn; if ( pCh->type % 2 == 0 ) { /* Vertical Slice */ if ( nIndex IS 0 OR nIndex IS 3 ) { /* Left Side */ pNode->fNPsiIn = spGetElement( matrix, nEqn, psiEqnInM ); pNode->fNPsiInP1 = spGetElement( matrix, nEqn, psiEqnInP ); pNode->fNPsiOx = spGetElement( matrix, nEqn, psiEqnOxM ); pNode->fNPsiOxP1 = spGetElement( matrix, nEqn, psiEqnOxP ); } else { /* Right Side */ pNode->fNPsiInM1 = spGetElement( matrix, nEqn, psiEqnInM ); pNode->fNPsiIn = spGetElement( matrix, nEqn, psiEqnInP ); pNode->fNPsiOxM1 = spGetElement( matrix, nEqn, psiEqnOxM ); pNode->fNPsiOx = spGetElement( matrix, nEqn, psiEqnOxP ); } } else { /* Horizontal Slice */ if ( nIndex <= 1 ) { /* Top Side */ pNode->fNPsiIn = spGetElement( matrix, nEqn, psiEqnInM ); pNode->fNPsiInP1 = spGetElement( matrix, nEqn, psiEqnInP ); pNode->fNPsiOx = spGetElement( matrix, nEqn, psiEqnOxM ); pNode->fNPsiOxP1 = spGetElement( matrix, nEqn, psiEqnOxP ); } else { /* Bottom Side */ pNode->fNPsiInM1 = spGetElement( matrix, nEqn, psiEqnInM ); pNode->fNPsiIn = spGetElement( matrix, nEqn, psiEqnInP ); pNode->fNPsiOxM1 = spGetElement( matrix, nEqn, psiEqnOxM ); pNode->fNPsiOx = spGetElement( matrix, nEqn, psiEqnOxP ); } } } /* endfor nIndex */ pElem = pElem->pElems[ nextIndex ]; } /* endwhile pElem */ } /* endfor pCh */ } /* endif SurfaceMobility */}/* * The Jacobian and Rhs are loaded by the following function. * Inputs are the transient analysis flag and the transient * information structure */void TWONsysLoad( pDevice, tranAnalysis, info ) TWOdevice *pDevice;BOOLEAN tranAnalysis;TWOtranInfo *info;{ TWOelem *pElem; TWOnode *pNode; TWOedge *pHEdge, *pVEdge; TWOedge *pTEdge, *pBEdge, *pLEdge, *pREdge; TWOchannel *pCh; int index, eIndex; int nextIndex; /* index of node to find next element */ double *pRhs = pDevice->rhs; double dx, dy, dxdy, dyOverDx, dxOverDy; double ds; double dPsiT, dPsiB, dPsiL, dPsiR; double rhsN; double nConc, pConc; double perTime; void spClear(), TWONcommonTerms(); /* first compute the currents and derivatives */ TWONcommonTerms( pDevice, FALSE, tranAnalysis, info ); /* find reciprocal timestep */ if ( tranAnalysis ) { perTime = info->intCoeff[0]; } /* zero the rhs vector */ for ( index = 1 ; index <= pDevice->numEqns ; index++ ) { pRhs[ index ] = 0.0; } /* zero the matrix */ spClear( pDevice->matrix ); for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) { pElem = pDevice->elements[ eIndex ]; dx = 0.5 * pElem->dx; dy = 0.5 * pElem->dy; dxdy = dx * dy; dxOverDy = 0.5 * pElem->epsRel * pElem->dxOverDy; dyOverDx = 0.5 * pElem->epsRel * pElem->dyOverDx; pTEdge = pElem->pTopEdge; pBEdge = pElem->pBotEdge; pLEdge = pElem->pLeftEdge; pREdge = pElem->pRightEdge; dPsiT = pTEdge->dPsi; dPsiB = pBEdge->dPsi; dPsiL = pLEdge->dPsi; dPsiR = pREdge->dPsi; /* load for all i,j */ for ( index = 0; index <= 3; index++ ) { pNode = pElem->pNodes[ index ]; if ( pNode->nodeType ISNOT CONTACT ) { *(pNode->fPsiPsi) += dyOverDx + dxOverDy; if ( index <= 1 ) { pHEdge = pTEdge; } else { pHEdge = pBEdge; } if ( index IS 0 OR index IS 3 ) { pVEdge = pLEdge; } else { pVEdge = pREdge; } /* Add surface state charges. */ pRhs[ pNode->psiEqn ] += dx * pHEdge->qf; pRhs[ pNode->psiEqn ] += dy * pVEdge->qf; if ( pElem->elemType IS SEMICON ) { nConc = *(pDevice->devState0 + pNode->nodeN); pConc = *(pDevice->devState0 + pNode->nodeP); *(pNode->fPsiN) += dxdy; *(pNode->fPsiPsi) += dxdy * pConc; *(pNode->fNPsi) -= dy * pHEdge->dJnDpsiP1 + dx * pVEdge->dJnDpsiP1; pRhs[ pNode->psiEqn ] += dxdy * (pNode->netConc + pConc - nConc); /* Handle generation terms */ *(pNode->fNN) -= dxdy * pNode->dUdN; *(pNode->fNPsi) += dxdy * pNode->dUdP * pConc; rhsN = - dxdy * pNode->uNet; pRhs[ pNode->nEqn ] -= rhsN; /* Handle dXdT continuity terms */ if ( tranAnalysis ) { *(pNode->fNN) -= dxdy * perTime; pRhs[ pNode->nEqn ] += dxdy * pNode->dNdT; } } } } /* Handle neighbor and edge dependent terms */ pNode = pElem->pTLNode; if ( pNode->nodeType ISNOT CONTACT ) { pRhs[ pNode->psiEqn ] -= -dyOverDx * dPsiT - dxOverDy * dPsiL; *(pNode->fPsiPsiiP1) -= dyOverDx; *(pNode->fPsiPsijP1) -= dxOverDy; if ( pElem->elemType IS SEMICON ) { pRhs[ pNode->nEqn ] -= dy * pTEdge->jn + dx * pLEdge->jn; *(pNode->fNN) += dy * pTEdge->dJnDn + dx * pLEdge->dJnDn; *(pNode->fNPsiiP1) += dy * pTEdge->dJnDpsiP1; *(pNode->fNNiP1) += dy * pTEdge->dJnDnP1; *(pNode->fNPsijP1) += dx * pLEdge->dJnDpsiP1; *(pNode->fNNjP1) += dx * pLEdge->dJnDnP1; } } pNode = pElem->pTRNode; if ( pNode->nodeType ISNOT CONTACT ) { pRhs[ pNode->psiEqn ] -= dyOverDx * dPsiT - dxOverDy * dPsiR; *(pNode->fPsiPsiiM1) -= dyOverDx; *(pNode->fPsiPsijP1) -= dxOverDy; if ( pElem->elemType IS SEMICON ) { pRhs[ pNode->nEqn ] -= -dy * pTEdge->jn + dx * pREdge->jn; *(pNode->fNN) += -dy * pTEdge->dJnDnP1 + dx * pREdge->dJnDn; *(pNode->fNPsiiM1) += dy * pTEdge->dJnDpsiP1; *(pNode->fNNiM1) -= dy * pTEdge->dJnDn; *(pNode->fNPsijP1) += dx * pREdge->dJnDpsiP1; *(pNode->fNNjP1) += dx * pREdge->dJnDnP1; } } pNode = pElem->pBRNode; if ( pNode->nodeType ISNOT CONTACT ) { pRhs[ pNode->psiEqn ] -= dyOverDx * dPsiB + dxOverDy * dPsiR; *(pNode->fPsiPsiiM1) -= dyOverDx; *(pNode->fPsiPsijM1) -= dxOverDy; if ( pElem->elemType IS SEMICON ) { pRhs[ pNode->nEqn ] -= -dy * pBEdge->jn - dx * pREdge->jn; *(pNode->fNN) += -dy * pBEdge->dJnDnP1 - dx * pREdge->dJnDnP1; *(pNode->fNPsiiM1) += dy * pBEdge->dJnDpsiP1; *(pNode->fNNiM1) -= dy * pBEdge->dJnDn; *(pNode->fNPsijM1) += dx * pREdge->dJnDpsiP1; *(pNode->fNNjM1) -= dx * pREdge->dJnDn; } } pNode = pElem->pBLNode; if ( pNode->nodeType ISNOT CONTACT ) { pRhs[ pNode->psiEqn ] -= -dyOverDx * dPsiB + dxOverDy * dPsiL; *(pNode->fPsiPsiiP1) -= dyOverDx; *(pNode->fPsiPsijM1) -= dxOverDy; if ( pElem->elemType IS SEMICON ) { pRhs[ pNode->nEqn ] -= dy * pBEdge->jn - dx * pLEdge->jn; *(pNode->fNN) += dy * pBEdge->dJnDn - dx * pLEdge->dJnDnP1; *(pNode->fNPsiiP1) += dy * pBEdge->dJnDpsiP1; *(pNode->fNNiP1) += dy * pBEdge->dJnDnP1; *(pNode->fNPsijM1) += dx * pLEdge->dJnDpsiP1; *(pNode->fNNjM1) -= dx * pLEdge->dJnDn; } } } /* Calculate the Inversion-Layer Mobility Dependent Terms in Jac. */ if ( MobDeriv AND SurfaceMobility ) { for ( pCh = pDevice->pChannel; pCh ISNOT NIL(TWOchannel); pCh = pCh->next ) { /* Find effective height of oxide element at interface. */ if ( pCh->type%2 == 0 ) { /* Vertical slice */ ds = pCh->pNElem->dy / pCh->pNElem->epsRel; } else { /* Horizontal slice */ ds = pCh->pNElem->dx / pCh->pNElem->epsRel; } pElem = pCh->pSeed; nextIndex = (pCh->type + 2)%4; while (pElem && pElem->channel == pCh->id) { TWONmobDeriv( pElem, pCh->type, ds ); pElem = pElem->pElems[ nextIndex ]; } } /* endfor pCh ISNOT NIL */ } /* endif MobDeriv and SurfaceMobility */}/* * This function used only for direct method ac analysis. * Used to load only the dc Jacobian matrix. Rhs is unaffected */void TWONjacLoad( pDevice ) TWOdevice *pDevice;{ TWOelem *pElem; TWOnode *pNode; TWOedge *pHEdge, *pVEdge; TWOedge *pTEdge, *pBEdge, *pLEdge, *pREdge; TWOchannel *pCh; int index, eIndex; int nextIndex; /* index of node to find next element */ double dx, dy, dxdy, dyOverDx, dxOverDy; double ds; double pConc; void spClear(), TWONcommonTerms(); /* first compute the currents and derivatives */ TWONcommonTerms( pDevice, FALSE, FALSE, NIL(TWOtranInfo) ); /* zero the matrix */ spClear( pDevice->matrix ); for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) { pElem = pDevice->elements[ eIndex ]; dx = 0.5 * pElem->dx; dy = 0.5 * pElem->dy; dxdy = dx * dy; dxOverDy = 0.5 * pElem->epsRel * pElem->dxOverDy; dyOverDx = 0.5 * pElem->epsRel * pElem->dyOverDx; pTEdge = pElem->pTopEdge;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -