📄 twopcont.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 values and * store direct pointers with the nodes */void TWOPjacBuild(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, pEqn; /* scratch for deref'd eqn numbers */ int psiEqnTL, pEqnTL; int psiEqnTR, pEqnTR; int psiEqnBR, pEqnBR; int psiEqnBL, pEqnBL; 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 */ pEqn = pNode->pEqn; pNode->nEqn = 0; /* pointers for additional terms */ pNode->fPsiP = spGetElement( matrix, psiEqn, pEqn ); pNode->fPPsi = spGetElement( matrix, pEqn, psiEqn ); pNode->fPP = spGetElement( matrix, pEqn, pEqn ); } else { pEqn = 0; } /* save equation indices */ switch ( nIndex ) { case 0: /* TL Node */ psiEqnTL = psiEqn; pEqnTL = pEqn; break; case 1: /* TR Node */ psiEqnTR = psiEqn; pEqnTR = pEqn; break; case 2: /* BR Node */ psiEqnBR = psiEqn; pEqnBR = pEqn; break; case 3: /* BL Node */ psiEqnBL = psiEqn; pEqnBL = pEqn; 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->fPPsiiP1 = spGetElement( matrix, pEqnTL, psiEqnTR ); pNode->fPPiP1 = spGetElement( matrix, pEqnTL, pEqnTR ); pNode->fPPsijP1 = spGetElement( matrix, pEqnTL, psiEqnBL ); pNode->fPPjP1 = spGetElement( matrix, pEqnTL, pEqnBL ); /* Surface Mobility Model depends on diagonal node values */ if ( MobDeriv AND SurfaceMobility AND pElem->channel ) { pNode->fPPsiiP1jP1 = spGetElement( matrix, pEqnTL, psiEqnBR ); pNode->fPPiP1jP1 = spGetElement( matrix, pEqnTL, pEqnBR ); } } pNode = pElem->pTRNode; pNode->fPsiPsiiM1 = spGetElement(matrix, psiEqnTR, psiEqnTL ); pNode->fPsiPsijP1 = spGetElement(matrix, psiEqnTR, psiEqnBR ); if ( pElem->elemType IS SEMICON ) { /* continuity equation pointers */ pNode->fPPsiiM1 = spGetElement( matrix, pEqnTR, psiEqnTL ); pNode->fPPiM1 = spGetElement( matrix, pEqnTR, pEqnTL ); pNode->fPPsijP1 = spGetElement( matrix, pEqnTR, psiEqnBR ); pNode->fPPjP1 = spGetElement( matrix, pEqnTR, pEqnBR ); /* Surface Mobility Model depends on diagonal node values */ if ( MobDeriv AND SurfaceMobility AND pElem->channel ) { pNode->fPPsiiM1jP1 = spGetElement( matrix, pEqnTR, psiEqnBL ); pNode->fPPiM1jP1 = spGetElement( matrix, pEqnTR, pEqnBL ); } } pNode = pElem->pBRNode; pNode->fPsiPsiiM1 = spGetElement(matrix, psiEqnBR, psiEqnBL ); pNode->fPsiPsijM1 = spGetElement(matrix, psiEqnBR, psiEqnTR ); if ( pElem->elemType IS SEMICON ) { /* continuity equation pointers */ pNode->fPPsiiM1 = spGetElement( matrix, pEqnBR, psiEqnBL ); pNode->fPPiM1 = spGetElement( matrix, pEqnBR, pEqnBL ); pNode->fPPsijM1 = spGetElement( matrix, pEqnBR, psiEqnTR ); pNode->fPPjM1 = spGetElement( matrix, pEqnBR, pEqnTR ); /* Surface Mobility Model depends on diagonal node values */ if ( MobDeriv AND SurfaceMobility AND pElem->channel ) { pNode->fPPsiiM1jM1 = spGetElement( matrix, pEqnBR, psiEqnTL ); pNode->fPPiM1jM1 = spGetElement( matrix, pEqnBR, pEqnTL ); } } pNode = pElem->pBLNode; pNode->fPsiPsiiP1 = spGetElement(matrix, psiEqnBL, psiEqnBR ); pNode->fPsiPsijM1 = spGetElement(matrix, psiEqnBL, psiEqnTL ); if ( pElem->elemType IS SEMICON ) { /* continuity equation pointers */ pNode->fPPsiiP1 = spGetElement( matrix, pEqnBL, psiEqnBR ); pNode->fPPiP1 = spGetElement( matrix, pEqnBL, pEqnBR ); pNode->fPPsijM1 = spGetElement( matrix, pEqnBL, psiEqnTL ); pNode->fPPjM1 = spGetElement( matrix, pEqnBL, pEqnTL ); /* Surface Mobility Model depends on diagonal node values */ if ( MobDeriv AND SurfaceMobility AND pElem->channel ) { pNode->fPPsiiP1jM1 = spGetElement( matrix, pEqnBL, psiEqnTR ); pNode->fPPiP1jM1 = spGetElement( matrix, pEqnBL, pEqnTR ); } } } /* * 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; pEqn = pNode->pEqn; if ( pCh->type % 2 == 0 ) { /* Vertical Slice */ if ( nIndex IS 0 OR nIndex IS 3 ) { /* Left Side */ pNode->fPPsiIn = spGetElement( matrix, pEqn, psiEqnInM ); pNode->fPPsiInP1 = spGetElement( matrix, pEqn, psiEqnInP ); pNode->fPPsiOx = spGetElement( matrix, pEqn, psiEqnOxM ); pNode->fPPsiOxP1 = spGetElement( matrix, pEqn, psiEqnOxP ); } else { /* Right Side */ pNode->fPPsiInM1 = spGetElement( matrix, pEqn, psiEqnInM ); pNode->fPPsiIn = spGetElement( matrix, pEqn, psiEqnInP ); pNode->fPPsiOxM1 = spGetElement( matrix, pEqn, psiEqnOxM ); pNode->fPPsiOx = spGetElement( matrix, pEqn, psiEqnOxP ); } } else { /* Horizontal Slice */ if ( nIndex <= 1 ) { /* Top Side */ pNode->fPPsiIn = spGetElement( matrix, pEqn, psiEqnInM ); pNode->fPPsiInP1 = spGetElement( matrix, pEqn, psiEqnInP ); pNode->fPPsiOx = spGetElement( matrix, pEqn, psiEqnOxM ); pNode->fPPsiOxP1 = spGetElement( matrix, pEqn, psiEqnOxP ); } else { /* Bottom Side */ pNode->fPPsiInM1 = spGetElement( matrix, pEqn, psiEqnInM ); pNode->fPPsiIn = spGetElement( matrix, pEqn, psiEqnInP ); pNode->fPPsiOxM1 = spGetElement( matrix, pEqn, psiEqnOxM ); pNode->fPPsiOx = spGetElement( matrix, pEqn, 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 TWOPsysLoad( 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 rhsP; double nConc, pConc; double perTime; void spClear(), TWOPcommonTerms(); /* first compute the currents and derivatives */ TWOPcommonTerms( 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 ) { 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; *(pNode->fPsiPsi) += dyOverDx + dxOverDy; if ( pElem->elemType IS SEMICON ) { nConc = *(pDevice->devState0 + pNode->nodeN); pConc = *(pDevice->devState0 + pNode->nodeP); *(pNode->fPsiPsi) += dxdy * nConc; *(pNode->fPsiP) -= dxdy; *(pNode->fPPsi) -= dy * pHEdge->dJpDpsiP1 + dx * pVEdge->dJpDpsiP1; pRhs[ pNode->psiEqn ] += dxdy * (pNode->netConc + pConc - nConc); /* Handle generation terms */ *(pNode->fPP) += dxdy * pNode->dUdP; *(pNode->fPPsi) += dxdy * pNode->dUdN * nConc; rhsP = dxdy * pNode->uNet; pRhs[ pNode->pEqn ] -= rhsP; /* Handle dXdT continuity terms */ if ( tranAnalysis ) { *(pNode->fPP) += dxdy * perTime; pRhs[ pNode->pEqn ] -= dxdy * pNode->dPdT; } } } } /* 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->pEqn ] -= dy * pTEdge->jp + dx * pLEdge->jp; *(pNode->fPP) += dy * pTEdge->dJpDp + dx * pLEdge->dJpDp; *(pNode->fPPsiiP1) += dy * pTEdge->dJpDpsiP1; *(pNode->fPPiP1) += dy * pTEdge->dJpDpP1; *(pNode->fPPsijP1) += dx * pLEdge->dJpDpsiP1; *(pNode->fPPjP1) += dx * pLEdge->dJpDpP1; } } 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->pEqn ] -= -dy * pTEdge->jp + dx * pREdge->jp; *(pNode->fPP) += -dy * pTEdge->dJpDpP1 + dx * pREdge->dJpDp; *(pNode->fPPsiiM1) += dy * pTEdge->dJpDpsiP1; *(pNode->fPPiM1) -= dy * pTEdge->dJpDp; *(pNode->fPPsijP1) += dx * pREdge->dJpDpsiP1; *(pNode->fPPjP1) += dx * pREdge->dJpDpP1; } } 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->pEqn ] -= -dy * pBEdge->jp - dx * pREdge->jp; *(pNode->fPP) += -dy * pBEdge->dJpDpP1 - dx * pREdge->dJpDpP1; *(pNode->fPPsiiM1) += dy * pBEdge->dJpDpsiP1; *(pNode->fPPiM1) -= dy * pBEdge->dJpDp; *(pNode->fPPsijM1) += dx * pREdge->dJpDpsiP1; *(pNode->fPPjM1) -= dx * pREdge->dJpDp; } } 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->pEqn ] -= dy * pBEdge->jp - dx * pLEdge->jp; *(pNode->fPP) += dy * pBEdge->dJpDp - dx * pLEdge->dJpDpP1; *(pNode->fPPsiiP1) += dy * pBEdge->dJpDpsiP1; *(pNode->fPPiP1) += dy * pBEdge->dJpDpP1; *(pNode->fPPsijM1) += dx * pLEdge->dJpDpsiP1; *(pNode->fPPjM1) -= dx * pLEdge->dJpDp; } } } /* 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) { TWOPmobDeriv( 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 TWOPjacLoad( 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 nConc; void spClear(), TWOPcommonTerms(); /* first compute the currents and derivatives */ TWOPcommonTerms( 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; pBEdge = pElem->pBotEdge;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -