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.
233 lines
6.3 KiB
233 lines
6.3 KiB
/**********
|
|
Copyright 1991 Regents of the University of California. All rights reserved.
|
|
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group
|
|
Author: 1991 David A. Gates, U. C. Berkeley CAD Group
|
|
**********/
|
|
|
|
/**********
|
|
One-Dimensional Numerical Device Setup Routines
|
|
**********/
|
|
|
|
#include "ngspice.h"
|
|
#include "numglobs.h"
|
|
#include "numconst.h"
|
|
#include "numenum.h"
|
|
#include "onemesh.h"
|
|
#include "onedev.h"
|
|
#include "carddefs.h" /* XXX Not really modular if we need this. */
|
|
/* #include "material.h" */
|
|
#include "onedext.h"
|
|
#include "oneddefs.h"
|
|
#include "cidersupt.h"
|
|
|
|
/* compute node parameters */
|
|
void
|
|
ONEsetup(ONEdevice *pDevice)
|
|
{
|
|
double temp1, deltaEg, avgConc, totalConc, absNetConc;
|
|
double ncv0, dBand, dNie, psiBand[2];
|
|
int index, eIndex;
|
|
int numContactNodes;
|
|
ONEnode *pNode;
|
|
ONEelem *pElem;
|
|
ONEedge *pEdge;
|
|
ONEmaterial *info;
|
|
|
|
|
|
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) {
|
|
pElem = pDevice->elemArray[eIndex];
|
|
info = pElem->matlInfo;
|
|
|
|
pElem->dx = pElem->pRightNode->x - pElem->pLeftNode->x;
|
|
pElem->epsRel = info->eps;
|
|
|
|
if (pElem->elemType == INSULATOR) {
|
|
for (index = 0; index <= 1; index++) {
|
|
if (pElem->evalNodes[index]) {
|
|
pNode = pElem->pNodes[index];
|
|
if (pNode->nodeType == CONTACT) {
|
|
pNode->eaff = PHI_METAL;
|
|
pNode->eg = 0.0;
|
|
} else {
|
|
pNode->eaff = info->affin;
|
|
pNode->eg = info->eg0;
|
|
}
|
|
}
|
|
}
|
|
} else if (pElem->elemType == SEMICON) {
|
|
ncv0 = sqrt(info->nc0) * sqrt(info->nv0);
|
|
for (index = 0; index <= 1; index++) {
|
|
if (pElem->evalNodes[index]) {
|
|
pNode = pElem->pNodes[index];
|
|
|
|
/* Fixed Interface Charge */
|
|
pNode->qf = 0.0;
|
|
|
|
/* Narrowing of Energy Band-Gap */
|
|
if (BandGapNarrowing) {
|
|
absNetConc = ABS(pNode->netConc);
|
|
if (pNode->netConc > 0.0) {
|
|
temp1 = log(absNetConc / info->nrefBGN[ELEC]);
|
|
deltaEg = -info->dEgDn[ELEC] * (temp1 + sqrt(temp1 * temp1 + 0.5));
|
|
pNode->eg = info->eg0 + deltaEg;
|
|
} else if (pNode->netConc < 0.0) {
|
|
temp1 = log(absNetConc / info->nrefBGN[HOLE]);
|
|
deltaEg = -info->dEgDn[HOLE] * (temp1 + sqrt(temp1 * temp1 + 0.5));
|
|
pNode->eg = info->eg0 + deltaEg;
|
|
} else {
|
|
pNode->eg = info->eg0;
|
|
}
|
|
} else {
|
|
pNode->eg = info->eg0;
|
|
}
|
|
pNode->nie = ncv0 * exp(-0.5 * pNode->eg / Vt);
|
|
pNode->eaff = info->affin;
|
|
/* Save band structure parameter. */
|
|
psiBand[index] = -info->refPsi;
|
|
|
|
/* Ionized-Impurity-Scattering Reduction of Carrier Lifetime */
|
|
if (ConcDepLifetime) {
|
|
totalConc = pNode->totalConc;
|
|
temp1 = 1.0 / (1.0 + totalConc / info->nrefSRH[ELEC]);
|
|
pNode->tn = info->tau0[ELEC] * temp1;
|
|
temp1 = 1.0 / (1.0 + totalConc / info->nrefSRH[HOLE]);
|
|
pNode->tp = info->tau0[HOLE] * temp1;
|
|
} else {
|
|
pNode->tn = info->tau0[ELEC];
|
|
pNode->tp = info->tau0[HOLE];
|
|
}
|
|
}
|
|
}
|
|
|
|
pEdge = pElem->pEdge;
|
|
|
|
/* Variable Band Built-In Potential */
|
|
dBand = psiBand[1] - psiBand[0];
|
|
dNie = log(pElem->pNodes[1]->nie / pElem->pNodes[0]->nie);
|
|
pEdge->dCBand = dBand + dNie;
|
|
pEdge->dVBand = -dBand + dNie;
|
|
|
|
/* Evaluate conc.-dep. mobility. */
|
|
avgConc = 0.5 * (pElem->pRightNode->totalConc + pElem->pLeftNode->totalConc);
|
|
MOBconcDep(info, avgConc, &pEdge->mun, &pEdge->mup);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Transfer BC info from bdry to nodes and edges. */
|
|
static void
|
|
ONEcopyBCinfo(ONEdevice *pDevice, ONEelem *pElem, BDRYcard *bdry, int index)
|
|
{
|
|
ONEnode *pNode;
|
|
ONEelem *pNElem;
|
|
int eIndex, nIndex;
|
|
double length;
|
|
|
|
|
|
/* First add fixed charge. */
|
|
pNode = pElem->pNodes[index];
|
|
pNode->qf += bdry->BDRYqf;
|
|
|
|
/* Now add surface recombination. */
|
|
/* Compute semiconductor length around this node. */
|
|
length = 0.0;
|
|
for (eIndex = 0; eIndex <= 3; eIndex++) {
|
|
pNElem = pNode->pElems[eIndex];
|
|
if ((pNElem != NIL(ONEelem)) && (pElem->elemType == SEMICON)) {
|
|
length += 0.5 * pElem->dx;
|
|
}
|
|
}
|
|
if (bdry->BDRYsnGiven) {
|
|
pNode->tn = pNode->tn /
|
|
(1.0 + ((bdry->BDRYsn * TNorm) * pNode->tn) / length);
|
|
}
|
|
if (bdry->BDRYspGiven) {
|
|
pNode->tp = pNode->tp /
|
|
(1.0 + ((bdry->BDRYsp * TNorm) * pNode->tp) / length);
|
|
}
|
|
/* Finally, surface layer is irrelevant for 1d devices. */
|
|
}
|
|
|
|
|
|
/* Compute boundary condition parameters. */
|
|
void
|
|
ONEsetBCparams(ONEdevice *pDevice, BDRYcard *bdryList, CONTcard *contList)
|
|
{
|
|
int index, xIndex;
|
|
ONEnode *pNode;
|
|
ONEelem *pElem, *pNElem;
|
|
BDRYcard *bdry;
|
|
CONTcard *cont;
|
|
|
|
|
|
|
|
for (bdry = bdryList; bdry != NIL(BDRYcard); bdry = bdry->BDRYnextCard) {
|
|
for (xIndex = bdry->BDRYixLow; xIndex < bdry->BDRYixHigh; xIndex++) {
|
|
pElem = pDevice->elemArray[xIndex];
|
|
if (pElem != NIL(ONEelem)) {
|
|
if (pElem->domain == bdry->BDRYdomain) {
|
|
for (index = 0; index <= 1; index++) {
|
|
if (pElem->evalNodes[index]) {
|
|
pNElem = pElem->pElems[index];
|
|
if (bdry->BDRYneighborGiven) {
|
|
if (pNElem && (pNElem->domain == bdry->BDRYneighbor)) {
|
|
/* Found an interface node. */
|
|
ONEcopyBCinfo(pDevice, pElem, bdry, index);
|
|
}
|
|
} else {
|
|
if ((!pNElem) || (pNElem->domain != pElem->domain)) {
|
|
/* Found a boundary node. */
|
|
ONEcopyBCinfo(pDevice, pElem, bdry, index);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (cont = contList; cont != NIL(CONTcard); cont = cont->CONTnextCard) {
|
|
if (!cont->CONTworkfunGiven) {
|
|
cont->CONTworkfun = PHI_METAL;
|
|
}
|
|
/*
|
|
* XXX This won't work right if someone tries to change the 1d BJT base
|
|
* contact workfunction and doesn't want to change the emitter. But no
|
|
* one will probably try to do that.
|
|
*/
|
|
if (cont->CONTnumber == 1) {
|
|
pDevice->elemArray[1]->pNodes[0]->eaff = cont->CONTworkfun;
|
|
} else if ((cont->CONTnumber == 2) || (cont->CONTnumber == 3)) {
|
|
pDevice->elemArray[pDevice->numNodes - 1]->pNodes[1]->eaff =
|
|
cont->CONTworkfun;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
ONEnormalize(ONEdevice *pDevice)
|
|
{
|
|
int index, eIndex;
|
|
ONEelem *pElem;
|
|
ONEnode *pNode;
|
|
|
|
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) {
|
|
pElem = pDevice->elemArray[eIndex];
|
|
|
|
pElem->dx /= LNorm;
|
|
pElem->rDx = 1.0 / pElem->dx;
|
|
pElem->epsRel /= EpsNorm;
|
|
for (index = 0; index <= 1; index++) {
|
|
if (pElem->evalNodes[index]) {
|
|
pNode = pElem->pNodes[index];
|
|
pNode->netConc /= NNorm;
|
|
pNode->nd /= NNorm;
|
|
pNode->na /= NNorm;
|
|
pNode->qf /= (NNorm * LNorm);
|
|
pNode->nie /= NNorm;
|
|
pNode->eg /= VNorm;
|
|
pNode->eaff /= VNorm;
|
|
}
|
|
}
|
|
}
|
|
}
|