379 lines
9.3 KiB
C
379 lines
9.3 KiB
C
/**********
|
|
Copyright 1992 Regents of the University of California. All rights reserved.
|
|
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group
|
|
**********/
|
|
|
|
#include "ngspice/ngspice.h"
|
|
#include "ngspice/numglobs.h"
|
|
#include "ngspice/numenum.h"
|
|
#include "ngspice/onemesh.h"
|
|
#include "ngspice/onedev.h"
|
|
#include "ngspice/macros.h"
|
|
#include "onedext.h"
|
|
#include "oneddefs.h"
|
|
|
|
extern void CiderLoaded(int);
|
|
|
|
/* Forward Declarations */
|
|
/* static void ONEresetEvalFlag(ONEdevice *); sjb - not used */
|
|
|
|
void
|
|
ONEbuildMesh(ONEdevice *pDevice, ONEcoord *pCoord, ONEdomain *pDomain,
|
|
ONEmaterial *pMaterial)
|
|
{
|
|
int index, i;
|
|
int elemType;
|
|
double xPos;
|
|
ONEcoord *pC;
|
|
ONEnode *pNode;
|
|
ONEdomain *pD;
|
|
ONEelem *pElem;
|
|
ONEmaterial *pM;
|
|
int poiEqn, numEqn;
|
|
ONEedge *pEdge;
|
|
ONEnode **nodeArray=NULL;
|
|
bool error = FALSE;
|
|
|
|
|
|
/* generate the work array for setting up nodes and elements */
|
|
XCALLOC(nodeArray, ONEnode *, 1 + pDevice->numNodes);
|
|
|
|
for (pC = pCoord; pC != NULL; pC = pC->next) {
|
|
xPos = pC->location;
|
|
XCALLOC(pNode, ONEnode, 1);
|
|
pNode->x = xPos;
|
|
pNode->nodeI = pC->number;
|
|
nodeArray[pNode->nodeI] = pNode;
|
|
}
|
|
|
|
/* mark the domain info on the nodes */
|
|
if (pDomain == NULL) {
|
|
fprintf(stderr, "Error: domains not defined for device\n");
|
|
exit(-1);
|
|
}
|
|
for (pD = pDomain; pD != NULL; pD = pD->next) {
|
|
for (pM = pMaterial; pM != NULL; pM = pM->next) {
|
|
if (pD->material == pM->id) {
|
|
break;
|
|
}
|
|
}
|
|
elemType = pM->type;
|
|
for (index = pD->ixLo; index <= pD->ixHi; index++) {
|
|
pNode = nodeArray[index];
|
|
if (!pNode->nodeType) {
|
|
pNode->nodeType = elemType;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* check to see if a domain has been defined for all nodes. if not flag an
|
|
* error message
|
|
*/
|
|
for (index = 2; index < pDevice->numNodes; index++) {
|
|
pNode = nodeArray[index];
|
|
if (!pNode->nodeType) {
|
|
fprintf(stderr, "Error: No domain defined for node %d\n", pNode->nodeI);
|
|
error = TRUE;
|
|
}
|
|
}
|
|
if (error) {
|
|
/* nodes with undefined domains -- exit */
|
|
exit(-1);
|
|
}
|
|
/* mark the first and last nodes to be contact nodes */
|
|
nodeArray[1]->nodeType = CONTACT;
|
|
nodeArray[pDevice->numNodes]->nodeType = CONTACT;
|
|
|
|
|
|
/* generate the elements and the edges */
|
|
for (index = 1; index < pDevice->numNodes; index++) {
|
|
XCALLOC(pElem, ONEelem, 1);
|
|
XCALLOC(pEdge, ONEedge, 1);
|
|
pElem->pEdge = pEdge;
|
|
pElem->pLeftNode = nodeArray[index];
|
|
pElem->pRightNode = nodeArray[index + 1];
|
|
pDevice->elemArray[index] = pElem;
|
|
}
|
|
|
|
/* now setup the nodes to which an element belongs */
|
|
for (index = 1; index < pDevice->numNodes; index++) {
|
|
pElem = pDevice->elemArray[index];
|
|
pElem->pLeftNode->pRightElem = pElem;
|
|
pElem->pRightNode->pLeftElem = pElem;
|
|
if (index > 1) {
|
|
pElem->pLeftElem = pDevice->elemArray[index - 1];
|
|
}
|
|
if (index < pDevice->numNodes - 1) {
|
|
pElem->pRightElem = pDevice->elemArray[index + 1];
|
|
}
|
|
}
|
|
|
|
/* mark the domain info on the elements */
|
|
for (pD = pDomain; pD != NULL; pD = pD->next) {
|
|
for (pM = pMaterial; pM != NULL; pM = pM->next) {
|
|
if (pD->material == pM->id) {
|
|
break;
|
|
}
|
|
}
|
|
elemType = pM->type;
|
|
for (index = pD->ixLo; index < pD->ixHi; index++) {
|
|
pElem = pDevice->elemArray[index];
|
|
pElem->domain = pD->id;
|
|
pElem->elemType = elemType;
|
|
pElem->matlInfo = pM;
|
|
}
|
|
}
|
|
|
|
/* identify the interface nodes */
|
|
for (index = 2; index < pDevice->numNodes; index++) {
|
|
pNode = nodeArray[index];
|
|
if (pNode->pLeftElem->elemType !=
|
|
pNode->pRightElem->elemType) {
|
|
/* an interface node */
|
|
pNode->nodeType = INTERFACE;
|
|
}
|
|
}
|
|
|
|
/* now mark the nodes to be evaluated */
|
|
/* all interface nodes marked in silicon elements */
|
|
|
|
for (index = 1; index < pDevice->numNodes; index++) {
|
|
pElem = pDevice->elemArray[index];
|
|
pElem->dx = pElem->pRightNode->x - pElem->pLeftNode->x;
|
|
for (i = 0; i <= 1; i++) {
|
|
pNode = pElem->pNodes[i];
|
|
pElem->evalNodes[i] = FALSE;
|
|
if (pElem->elemType == INSULATOR) {
|
|
if ((!pNode->evaluated) &&
|
|
(pNode->nodeType != INTERFACE)) {
|
|
/* a non interface node in oxide domain */
|
|
pNode->evaluated = TRUE;
|
|
pElem->evalNodes[i] = TRUE;
|
|
}
|
|
}
|
|
if (pElem->elemType == SEMICON) {
|
|
if (!pNode->evaluated) {
|
|
pNode->evaluated = TRUE;
|
|
pElem->evalNodes[i] = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* set up the equation number for the nodes */
|
|
poiEqn = numEqn = 1;
|
|
for (index = 1; index < pDevice->numNodes; index++) {
|
|
pElem = pDevice->elemArray[index];
|
|
for (i = 0; i <= 1; i++) {
|
|
if (pElem->evalNodes[i]) {
|
|
pNode = pElem->pNodes[i];
|
|
if (pNode->nodeType != CONTACT) {
|
|
pNode->poiEqn = poiEqn++;
|
|
|
|
pNode->psiEqn = numEqn;
|
|
if (pElem->elemType == INSULATOR) {
|
|
numEqn += 1; /* only poisson's equation */
|
|
} else {
|
|
pNode->nEqn = numEqn + 1;
|
|
pNode->pEqn = numEqn + 2;
|
|
numEqn += 3;
|
|
}
|
|
} else { /* this is a contact node */
|
|
pNode->poiEqn = 0;
|
|
pNode->psiEqn = 0;
|
|
pNode->nEqn = 0;
|
|
pNode->pEqn = 0;
|
|
}
|
|
/*
|
|
* fprintf(stdout,"NODE: %d %d\n",pNode->nodeI,pNode->poiEqn);
|
|
*/
|
|
}
|
|
}
|
|
}
|
|
pDevice->dimEquil = poiEqn;
|
|
pDevice->dimBias = numEqn;
|
|
|
|
FREE(nodeArray);
|
|
{
|
|
CiderLoaded(1);
|
|
}
|
|
|
|
/*
|
|
* ONEprnMesh( pDevice );
|
|
*/
|
|
}
|
|
|
|
/*
|
|
* We have a separate function for this, so that the setup routines can reset
|
|
* the state pointers without rebuilding the entire mesh.
|
|
*/
|
|
void
|
|
ONEgetStatePointers(ONEdevice *pDevice, int *numStates)
|
|
{
|
|
int eIndex, nIndex;
|
|
ONEelem *pElem;
|
|
ONEnode *pNode;
|
|
|
|
|
|
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) {
|
|
pElem = pDevice->elemArray[eIndex];
|
|
for (nIndex = 0; nIndex <= 1; nIndex++) {
|
|
if (pElem->evalNodes[nIndex]) {
|
|
pNode = pElem->pNodes[nIndex];
|
|
pNode->nodeState = *numStates;
|
|
*numStates += ONEnumNodeStates;
|
|
}
|
|
}
|
|
pElem->pEdge->edgeState = *numStates;
|
|
*numStates += ONEnumEdgeStates;
|
|
}
|
|
}
|
|
|
|
/* adjust the base contact to be the position of maximum density */
|
|
/* index EB and BC are the indexes for the eb, bc junctions */
|
|
void
|
|
adjustBaseContact(ONEdevice *pDevice, int indexEB, int indexBC)
|
|
{
|
|
int index, i, newBaseIndex, midPoint;
|
|
double maxDensity;
|
|
ONEnode *pNode;
|
|
ONEelem *pElem;
|
|
ONEnode *pBaseNode = pDevice->elemArray[pDevice->baseIndex]->pNodes[0];
|
|
|
|
|
|
/* Initialize the base contact to be the center of the two junctions */
|
|
/* This should take care of uniform dopings. */
|
|
|
|
midPoint = (indexEB + indexBC) / 2;
|
|
newBaseIndex = midPoint;
|
|
|
|
if (pBaseNode->baseType == P_TYPE) {
|
|
maxDensity = pDevice->elemArray[midPoint]->pNodes[0]->pConc;
|
|
for (index = indexEB; index < indexBC; index++) {
|
|
pElem = pDevice->elemArray[index];
|
|
for (i = 0; i <= 1; i++) {
|
|
pNode = pElem->pNodes[i];
|
|
if (pNode->pConc > maxDensity) {
|
|
maxDensity = pNode->pConc;
|
|
newBaseIndex = index;
|
|
}
|
|
}
|
|
}
|
|
} else if (pBaseNode->baseType == N_TYPE) {
|
|
maxDensity = pDevice->elemArray[midPoint]->pNodes[0]->nConc;
|
|
for (index = indexEB; index < indexBC; index++) {
|
|
pElem = pDevice->elemArray[index];
|
|
for (i = 0; i <= 1; i++) {
|
|
pNode = pElem->pNodes[i];
|
|
if (pNode->nConc > maxDensity) {
|
|
maxDensity = pNode->nConc;
|
|
newBaseIndex = index;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
printf("adjustBaseContact: unknown base type %d\n", pBaseNode->baseType);
|
|
}
|
|
/* at the conclusion of this loop have the point of max density */
|
|
if (pDevice->baseIndex != newBaseIndex) {
|
|
/* so change the position */
|
|
pNode = pDevice->elemArray[newBaseIndex]->pNodes[0];
|
|
pNode->baseType = pBaseNode->baseType;
|
|
pNode->vbe = pBaseNode->vbe;
|
|
pBaseNode->baseType = FALSE;
|
|
pBaseNode->vbe = 0.0;
|
|
pDevice->baseIndex = newBaseIndex;
|
|
}
|
|
}
|
|
|
|
void
|
|
NBJTjunctions(ONEdevice *pDevice, int *indexEB, int *indexBC)
|
|
{
|
|
int index;
|
|
double conc1, conc2;
|
|
bool findFirstJunction = TRUE;
|
|
bool notFound = TRUE;
|
|
|
|
for (index = 1; (index < pDevice->numNodes) && (notFound); index++) {
|
|
conc1 = pDevice->elemArray[index]->pNodes[0]->netConc;
|
|
conc2 = pDevice->elemArray[index]->pNodes[1]->netConc;
|
|
|
|
if ((conc1 * conc2 < 0.0) && (findFirstJunction)) {
|
|
*indexEB = index;
|
|
findFirstJunction = FALSE;
|
|
} else if ((conc1 * conc2 < 0.0) && (!findFirstJunction)) {
|
|
*indexBC = index;
|
|
notFound = FALSE;
|
|
}
|
|
}
|
|
|
|
if (notFound) {
|
|
fprintf(stderr, "BJT: Device does not have two junctions!\n");
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
void
|
|
ONEprnMesh(ONEdevice *pDevice)
|
|
{
|
|
int eIndex, index;
|
|
ONEelem *pElem;
|
|
ONEnode *pNode;
|
|
char *name;
|
|
|
|
|
|
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) {
|
|
pElem = pDevice->elemArray[eIndex];
|
|
fprintf(stderr, "elem %5d:\n", eIndex);
|
|
for (index = 0; index <= 1; index++) {
|
|
if (pElem->evalNodes[index]) {
|
|
pNode = pElem->pNodes[index];
|
|
switch (pNode->nodeType) {
|
|
case SEMICON:
|
|
name = "semiconductor";
|
|
break;
|
|
case INSULATOR:
|
|
name = "insulator";
|
|
break;
|
|
case CONTACT:
|
|
name = "contact";
|
|
break;
|
|
case SCHOTTKY:
|
|
name = "schottky";
|
|
break;
|
|
case INTERFACE:
|
|
name = "interface";
|
|
break;
|
|
default:
|
|
name = "unknown";
|
|
break;
|
|
}
|
|
|
|
|
|
fprintf(stderr, "node %5d: %s %5d\n", index, name,
|
|
pNode->nodeI);
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* sjb - not used
|
|
static void
|
|
ONEresetEvalFlag(ONEdevice *pDevice)
|
|
{
|
|
int index, eIndex;
|
|
ONEelem *pElem;
|
|
|
|
|
|
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) {
|
|
pElem = pDevice->elemArray[eIndex];
|
|
for (index = 0; index <= 1; index++) {
|
|
pElem->pNodes[index]->evaluated = FALSE;
|
|
}
|
|
pElem->pEdge->evaluated = FALSE;
|
|
}
|
|
}
|
|
*/
|