Cider simulator (simulator routines) Import.
This commit is contained in:
parent
dd8929457e
commit
f5f8602d3a
|
|
@ -0,0 +1,7 @@
|
|||
## Process this file with automake
|
||||
|
||||
EXTRA_DIST = notes
|
||||
|
||||
SUBDIRS = input support oned twod
|
||||
|
||||
MAINTAINERCLEANFILES = Makefile.in
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
## Process this file with automake to produce Makefile.in
|
||||
|
||||
noinst_LIBRARIES = libciderinput.a
|
||||
|
||||
libciderinput_a_SOURCES = \
|
||||
bdryset.c \
|
||||
boundary.c \
|
||||
cards.c \
|
||||
contact.c \
|
||||
contset.c \
|
||||
domain.c \
|
||||
domnset.c \
|
||||
doping.c \
|
||||
dopset.c \
|
||||
elctset.c \
|
||||
electrod.c \
|
||||
material.c \
|
||||
matlset.c \
|
||||
mesh.c \
|
||||
meshset.c \
|
||||
method.c \
|
||||
mobility.c \
|
||||
mobset.c \
|
||||
models.c \
|
||||
modlset.c \
|
||||
options.c \
|
||||
outpset.c \
|
||||
output.c
|
||||
|
||||
|
||||
EXTRA_DIST = readme
|
||||
INCLUDES = -I$(top_srcdir)/src/include
|
||||
MAINTAINERCLEANFILES = Makefile.in
|
||||
|
|
@ -0,0 +1,208 @@
|
|||
/**********
|
||||
Copyright 1991 Regents of the University of California. All rights reserved.
|
||||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group
|
||||
Modified: 2001 Paolo Nenzi
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "cktdefs.h"
|
||||
#include "bdrydefs.h"
|
||||
#include "meshext.h"
|
||||
#include "gendev.h"
|
||||
#include "sperror.h"
|
||||
|
||||
extern int BDRYcheck( BDRYcard *, DOMNdomain * );
|
||||
extern int BDRYsetup( BDRYcard *, MESHcoord *, MESHcoord *, DOMNdomain * );
|
||||
|
||||
|
||||
/*
|
||||
* Name: BDRYcheck
|
||||
* Purpose: checks a list of BDRYcards for input errors
|
||||
* Formals: cardList: the list to check
|
||||
* Returns: OK/E_PRIVATE
|
||||
* Users: numerical device setup routines
|
||||
* Calls: error message handler
|
||||
*/
|
||||
int
|
||||
BDRYcheck(BDRYcard *cardList, DOMNdomain *domnList)
|
||||
{
|
||||
BDRYcard *card;
|
||||
DOMNdomain *domn;
|
||||
int cardNum = 0;
|
||||
int error = OK;
|
||||
char ebuf[512]; /* error message buffer */
|
||||
|
||||
for ( card = cardList; card != NIL(BDRYcard); card = card->BDRYnextCard ) {
|
||||
cardNum++;
|
||||
if (card->BDRYxLowGiven && card->BDRYixLowGiven) {
|
||||
sprintf( ebuf,
|
||||
"boundary card %d uses both location and index - location ignored",
|
||||
cardNum );
|
||||
SPfrontEnd->IFerror( ERR_INFO, ebuf, NIL(IFuid) );
|
||||
card->BDRYxLowGiven = FALSE;
|
||||
}
|
||||
if (card->BDRYxHighGiven && card->BDRYixHighGiven) {
|
||||
sprintf( ebuf,
|
||||
"boundary card %d uses both location and index - location ignored",
|
||||
cardNum );
|
||||
SPfrontEnd->IFerror( ERR_INFO, ebuf, NIL(IFuid) );
|
||||
card->BDRYxHighGiven = FALSE;
|
||||
}
|
||||
if (card->BDRYyLowGiven && card->BDRYiyLowGiven) {
|
||||
sprintf( ebuf,
|
||||
"boundary card %d uses both location and index - location ignored",
|
||||
cardNum );
|
||||
SPfrontEnd->IFerror( ERR_INFO, ebuf, NIL(IFuid) );
|
||||
card->BDRYyLowGiven = FALSE;
|
||||
}
|
||||
if (card->BDRYyHighGiven && card->BDRYiyHighGiven) {
|
||||
sprintf( ebuf,
|
||||
"boundary card %d uses both location and index - location ignored",
|
||||
cardNum );
|
||||
SPfrontEnd->IFerror( ERR_INFO, ebuf, NIL(IFuid) );
|
||||
card->BDRYyHighGiven = FALSE;
|
||||
}
|
||||
if (!card->BDRYdomainGiven) {
|
||||
sprintf( ebuf,
|
||||
"boundary card %d is missing a domain index",
|
||||
cardNum );
|
||||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) );
|
||||
error = E_PRIVATE;
|
||||
} else {
|
||||
/* Make sure the domain exists */
|
||||
for ( domn = domnList; domn != NIL(DOMNdomain); domn = domn->next ) {
|
||||
if ( card->BDRYdomain == domn->id ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (domn == NIL(DOMNdomain)) {
|
||||
sprintf( ebuf,
|
||||
"boundary card %d specifies a non-existent domain",
|
||||
cardNum );
|
||||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) );
|
||||
error = E_PRIVATE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!card->BDRYneighborGiven) {
|
||||
card->BDRYneighbor = card->BDRYdomain;
|
||||
} else {
|
||||
/* Make sure the neighbor exists */
|
||||
for ( domn = domnList; domn != NIL(DOMNdomain); domn = domn->next ) {
|
||||
if ( card->BDRYneighbor == domn->id ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (domn == NIL(DOMNdomain)) {
|
||||
sprintf( ebuf,
|
||||
"interface card %d specifies a non-existent domain",
|
||||
cardNum );
|
||||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) );
|
||||
error = E_PRIVATE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!card->BDRYqfGiven) {
|
||||
card->BDRYqf = 0.0;
|
||||
}
|
||||
if (!card->BDRYsnGiven) {
|
||||
card->BDRYsn = 0.0;
|
||||
}
|
||||
if (!card->BDRYspGiven) {
|
||||
card->BDRYsp = 0.0;
|
||||
}
|
||||
if (!card->BDRYlayerGiven) {
|
||||
card->BDRYlayer = 0.0;
|
||||
}
|
||||
|
||||
/* Return now if anything has failed */
|
||||
if (error) return(error);
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Name: BDRYsetup
|
||||
* Purpose: Checks BDRY cards and then sets the indices
|
||||
* Formals: cardList: list of cards to setup, returns with indices set
|
||||
* xMeshList: list of coordinates in the x mesh
|
||||
* yMeshList: list of coordinates in the y mesh
|
||||
* Returns: OK/E_PRIVATE
|
||||
* Users: numerical devices
|
||||
* Calls: BDRYcheck
|
||||
*/
|
||||
int
|
||||
BDRYsetup(BDRYcard *cardList, MESHcoord *xMeshList, MESHcoord *yMeshList,DOMNdomain *domnList)
|
||||
{
|
||||
BDRYcard *card;
|
||||
int ixMin, ixMax, iyMin, iyMax;
|
||||
int cardNum = 0;
|
||||
int error;
|
||||
char ebuf[512]; /* error message buffer */
|
||||
|
||||
/* Check the card list */
|
||||
if ((error = BDRYcheck( cardList, domnList ))) return( error );
|
||||
|
||||
/* Find the limits on the indices */
|
||||
MESHiBounds( xMeshList, &ixMin, &ixMax );
|
||||
MESHiBounds( yMeshList, &iyMin, &iyMax );
|
||||
|
||||
error = OK;
|
||||
for ( card = cardList; card != NIL(BDRYcard); card = card->BDRYnextCard ) {
|
||||
cardNum++;
|
||||
|
||||
if (card->BDRYixLowGiven) {
|
||||
card->BDRYixLow = MAX(card->BDRYixLow, ixMin);
|
||||
}
|
||||
else if (card->BDRYxLowGiven) {
|
||||
card->BDRYixLow = MESHlocate( xMeshList, card->BDRYxLow );
|
||||
}
|
||||
else {
|
||||
card->BDRYixLow = ixMin;
|
||||
}
|
||||
if (card->BDRYixHighGiven) {
|
||||
card->BDRYixHigh = MIN(card->BDRYixHigh, ixMax);
|
||||
}
|
||||
else if (card->BDRYxHighGiven) {
|
||||
card->BDRYixHigh = MESHlocate( xMeshList, card->BDRYxHigh );
|
||||
}
|
||||
else {
|
||||
card->BDRYixHigh = ixMax;
|
||||
}
|
||||
if (card->BDRYixLow > card->BDRYixHigh) {
|
||||
sprintf( ebuf,
|
||||
"boundary card %d has low x index (%d) > high x index (%d)",
|
||||
cardNum, card->BDRYixHigh, card->BDRYixLow );
|
||||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) );
|
||||
error = E_PRIVATE;
|
||||
}
|
||||
if (card->BDRYiyLowGiven) {
|
||||
card->BDRYiyLow = MAX(card->BDRYiyLow, iyMin);
|
||||
}
|
||||
else if (card->BDRYyLowGiven) {
|
||||
card->BDRYiyLow = MESHlocate( yMeshList, card->BDRYyLow );
|
||||
}
|
||||
else {
|
||||
card->BDRYiyLow = iyMin;
|
||||
}
|
||||
if (card->BDRYiyHighGiven) {
|
||||
card->BDRYiyHigh = MIN(card->BDRYiyHigh, iyMax);
|
||||
}
|
||||
else if (card->BDRYyHighGiven) {
|
||||
card->BDRYiyHigh = MESHlocate( yMeshList, card->BDRYyHigh );
|
||||
}
|
||||
else {
|
||||
card->BDRYiyHigh = iyMax;
|
||||
}
|
||||
if (card->BDRYiyLow > card->BDRYiyHigh) {
|
||||
sprintf( ebuf,
|
||||
"boundary card %d has low y index (%d) > high y index (%d)",
|
||||
cardNum, card->BDRYiyHigh, card->BDRYiyLow );
|
||||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) );
|
||||
error = E_PRIVATE;
|
||||
}
|
||||
}
|
||||
return( error );
|
||||
}
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
/**********
|
||||
Copyright 1991 Regents of the University of California. All rights reserved.
|
||||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group
|
||||
Modified: 2001 Paolo Nenzi
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numcards.h"
|
||||
#include "numgen.h"
|
||||
#include "bdrydefs.h"
|
||||
#include "devdefs.h"
|
||||
#include "sperror.h"
|
||||
#include "suffix.h"
|
||||
|
||||
#define UM_TO_CM 1.0e-4
|
||||
|
||||
extern int BDRYnewCard(void**,void*);
|
||||
extern int BDRYparam(int,IFvalue*,void*);
|
||||
|
||||
IFparm BDRYpTable[] = {
|
||||
IP("domain", BDRY_DOMAIN, IF_INTEGER, "Primary domain"),
|
||||
IP("neighbor",BDRY_NEIGHBOR, IF_INTEGER, "Neighboring domain"),
|
||||
IP("x.low", BDRY_X_LOW, IF_REAL, "Location of left edge"),
|
||||
IP("x.high", BDRY_X_HIGH, IF_REAL, "Location of right edge"),
|
||||
IP("y.low", BDRY_Y_LOW, IF_REAL, "Location of top edge"),
|
||||
IP("y.high", BDRY_Y_HIGH, IF_REAL, "Location of bottom edge"),
|
||||
IP("ix.low", BDRY_IX_LOW, IF_INTEGER, "Index of left edge"),
|
||||
IP("ix.high", BDRY_IX_HIGH, IF_INTEGER, "Index of right edge"),
|
||||
IP("iy.low", BDRY_IY_LOW, IF_INTEGER, "Index of top edge"),
|
||||
IP("iy.high", BDRY_IY_HIGH, IF_INTEGER, "Index of bottom edge"),
|
||||
IP("nss", BDRY_QF, IF_REAL, "Fixed charge"),
|
||||
IP("qss", BDRY_QF, IF_REAL, "Fixed charge"),
|
||||
IP("qf", BDRY_QF, IF_REAL, "Fixed charge"),
|
||||
IP("sn", BDRY_SN, IF_REAL, "Electron recomb velocity"),
|
||||
IP("srvn", BDRY_SN, IF_REAL, "Electron recomb velocity"),
|
||||
IP("vsrfn", BDRY_SN, IF_REAL, "Electron recomb velocity"),
|
||||
IP("sp", BDRY_SP, IF_REAL, "Hole recomb velocity"),
|
||||
IP("srvp", BDRY_SP, IF_REAL, "Hole recomb velocity"),
|
||||
IP("vsrfp", BDRY_SP, IF_REAL, "Hole recomb velocity"),
|
||||
IP("layer.width",BDRY_LAYER, IF_REAL, "Width of surface charge layer")
|
||||
};
|
||||
|
||||
IFcardInfo BDRYinfo = {
|
||||
"boundary",
|
||||
"Specify properties of a domain boundary",
|
||||
NUMELEMS(BDRYpTable),
|
||||
BDRYpTable,
|
||||
|
||||
BDRYnewCard,
|
||||
BDRYparam,
|
||||
NULL
|
||||
};
|
||||
IFcardInfo INTFinfo = {
|
||||
"interface",
|
||||
"Specify properties of an interface between two domains",
|
||||
NUMELEMS(BDRYpTable),
|
||||
BDRYpTable,
|
||||
|
||||
BDRYnewCard,
|
||||
BDRYparam,
|
||||
NULL
|
||||
};
|
||||
|
||||
int
|
||||
BDRYnewCard(void **inCard, void *inModel)
|
||||
{
|
||||
BDRYcard *tmpCard, *newCard;
|
||||
GENnumModel *model = (GENnumModel *)inModel;
|
||||
|
||||
newCard = NEW( BDRYcard );
|
||||
if (!newCard) {
|
||||
*inCard = (void *)NULL;
|
||||
return(E_NOMEM);
|
||||
}
|
||||
newCard->BDRYnextCard = (BDRYcard *)NULL;
|
||||
*inCard = (void *)newCard;
|
||||
|
||||
tmpCard = model->GENboundaries;
|
||||
if (!tmpCard) { /* First in list */
|
||||
model->GENboundaries = newCard;
|
||||
} else {
|
||||
/* Go to end of list */
|
||||
while (tmpCard->BDRYnextCard) tmpCard = tmpCard->BDRYnextCard;
|
||||
/* And add new card */
|
||||
tmpCard->BDRYnextCard = newCard;
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
||||
int
|
||||
BDRYparam(int param, IFvalue *value, void *inCard)
|
||||
{
|
||||
BDRYcard *card = (BDRYcard *)inCard;
|
||||
|
||||
switch (param) {
|
||||
case BDRY_DOMAIN:
|
||||
card->BDRYdomain = value->iValue;
|
||||
card->BDRYdomainGiven = TRUE;
|
||||
break;
|
||||
case BDRY_NEIGHBOR:
|
||||
card->BDRYneighbor = value->iValue;
|
||||
card->BDRYneighborGiven = TRUE;
|
||||
break;
|
||||
case BDRY_X_LOW:
|
||||
card->BDRYxLow = value->rValue * UM_TO_CM;
|
||||
card->BDRYxLowGiven = TRUE;
|
||||
break;
|
||||
case BDRY_X_HIGH:
|
||||
card->BDRYxHigh = value->rValue * UM_TO_CM;
|
||||
card->BDRYxHighGiven = TRUE;
|
||||
break;
|
||||
case BDRY_Y_LOW:
|
||||
card->BDRYyLow = value->rValue * UM_TO_CM;
|
||||
card->BDRYyLowGiven = TRUE;
|
||||
break;
|
||||
case BDRY_Y_HIGH:
|
||||
card->BDRYyHigh = value->rValue * UM_TO_CM;
|
||||
card->BDRYyHighGiven = TRUE;
|
||||
break;
|
||||
case BDRY_IX_LOW:
|
||||
card->BDRYixLow = value->iValue;
|
||||
card->BDRYixLowGiven = TRUE;
|
||||
break;
|
||||
case BDRY_IX_HIGH:
|
||||
card->BDRYixHigh = value->iValue;
|
||||
card->BDRYixHighGiven = TRUE;
|
||||
break;
|
||||
case BDRY_IY_LOW:
|
||||
card->BDRYiyLow = value->iValue;
|
||||
card->BDRYiyLowGiven = TRUE;
|
||||
break;
|
||||
case BDRY_IY_HIGH:
|
||||
card->BDRYiyHigh = value->iValue;
|
||||
card->BDRYiyHighGiven = TRUE;
|
||||
break;
|
||||
case BDRY_QF:
|
||||
card->BDRYqf = value->rValue;
|
||||
card->BDRYqfGiven = TRUE;
|
||||
break;
|
||||
case BDRY_SN:
|
||||
card->BDRYsn = value->rValue;
|
||||
card->BDRYsnGiven = TRUE;
|
||||
break;
|
||||
case BDRY_SP:
|
||||
card->BDRYsp = value->rValue;
|
||||
card->BDRYspGiven = TRUE;
|
||||
break;
|
||||
case BDRY_LAYER:
|
||||
card->BDRYlayer = value->rValue;
|
||||
card->BDRYlayerGiven = TRUE;
|
||||
break;
|
||||
default:
|
||||
return(E_BADPARM);
|
||||
break;
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/**********
|
||||
Copyright 1991 Regents of the University of California. All rights reserved.
|
||||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group
|
||||
Modified: 2001 Paolo Nenzi
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numcards.h"
|
||||
|
||||
extern IFcardInfo CONTinfo;
|
||||
extern IFcardInfo DOPinfo;
|
||||
extern IFcardInfo ELCTinfo;
|
||||
extern IFcardInfo BDRYinfo;
|
||||
extern IFcardInfo INTFinfo;
|
||||
extern IFcardInfo XMSHinfo;
|
||||
extern IFcardInfo YMSHinfo;
|
||||
extern IFcardInfo METHinfo;
|
||||
extern IFcardInfo MOBinfo;
|
||||
extern IFcardInfo MODLinfo;
|
||||
extern IFcardInfo PHYSinfo;
|
||||
extern IFcardInfo MATLinfo;
|
||||
extern IFcardInfo DOMNinfo;
|
||||
extern IFcardInfo REGNinfo;
|
||||
extern IFcardInfo OPTNinfo;
|
||||
extern IFcardInfo OUTPinfo;
|
||||
|
||||
IFcardInfo *INPcardTab[] = {
|
||||
&CONTinfo,
|
||||
&DOPinfo,
|
||||
&ELCTinfo,
|
||||
&BDRYinfo,
|
||||
&INTFinfo,
|
||||
&XMSHinfo,
|
||||
&YMSHinfo,
|
||||
&METHinfo,
|
||||
&MOBinfo,
|
||||
&MODLinfo,
|
||||
&PHYSinfo,
|
||||
&MATLinfo,
|
||||
&DOMNinfo,
|
||||
®Ninfo,
|
||||
&OPTNinfo,
|
||||
&OUTPinfo
|
||||
};
|
||||
|
||||
int INPnumCards = sizeof(INPcardTab)/sizeof(IFcardInfo*);
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
/**********
|
||||
Copyright 1991 Regents of the University of California. All rights reserved.
|
||||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group
|
||||
Modified: 2001 Paolo Nenzi
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numcards.h"
|
||||
#include "numgen.h"
|
||||
#include "contdefs.h"
|
||||
#include "devdefs.h"
|
||||
#include "sperror.h"
|
||||
#include "suffix.h"
|
||||
|
||||
extern int CONTnewCard(void**,void*);
|
||||
extern int CONTparam(int,IFvalue*,void*);
|
||||
|
||||
|
||||
IFparm CONTpTable[] = {
|
||||
IP("neutral", CONT_NEUTRAL, IF_FLAG, "Charge neutral"),
|
||||
IP("aluminum", CONT_ALUMINUM, IF_FLAG, "Aluminum contact"),
|
||||
IP("p.polysilicon", CONT_P_POLY, IF_FLAG, "P poly contact"),
|
||||
IP("n.polysilicon", CONT_N_POLY, IF_FLAG, "N poly contact"),
|
||||
IP("workfunction", CONT_WORKFUN, IF_REAL, "Metal work function"),
|
||||
IP("number", CONT_NUMBER, IF_INTEGER, "Electrode ID number")
|
||||
};
|
||||
|
||||
IFcardInfo CONTinfo = {
|
||||
"contact",
|
||||
"Properties of a contact to the device",
|
||||
NUMELEMS(CONTpTable),
|
||||
CONTpTable,
|
||||
|
||||
CONTnewCard,
|
||||
CONTparam,
|
||||
NULL
|
||||
};
|
||||
|
||||
int
|
||||
CONTnewCard(void **inCard, void *inModel)
|
||||
{
|
||||
CONTcard *tmpCard, *newCard;
|
||||
GENnumModel *model = (GENnumModel *)inModel;
|
||||
|
||||
newCard = NEW( CONTcard );
|
||||
if (!newCard) {
|
||||
*inCard = (void *)NULL;
|
||||
return(E_NOMEM);
|
||||
}
|
||||
newCard->CONTnextCard = (CONTcard *)NULL;
|
||||
*inCard = (void *)newCard;
|
||||
|
||||
tmpCard = model->GENcontacts;
|
||||
if (!tmpCard) { /* First in list */
|
||||
model->GENcontacts = newCard;
|
||||
} else {
|
||||
/* Go to end of list */
|
||||
while (tmpCard->CONTnextCard) tmpCard = tmpCard->CONTnextCard;
|
||||
/* And add new card */
|
||||
tmpCard->CONTnextCard = newCard;
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
||||
int
|
||||
CONTparam(int param, IFvalue *value, void *inCard)
|
||||
{
|
||||
CONTcard *card = (CONTcard *)inCard;
|
||||
|
||||
switch (param) {
|
||||
case CONT_NEUTRAL:
|
||||
if ( value->iValue ) {
|
||||
card->CONTtype = CONT_NEUTRAL;
|
||||
card->CONTtypeGiven = TRUE;
|
||||
} else {
|
||||
if ( card->CONTtype == CONT_NEUTRAL ) {
|
||||
card->CONTtype = -1;
|
||||
card->CONTtypeGiven = FALSE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CONT_ALUMINUM:
|
||||
if ( value->iValue ) {
|
||||
card->CONTtype = CONT_ALUMINUM;
|
||||
card->CONTtypeGiven = TRUE;
|
||||
} else {
|
||||
if ( card->CONTtype == CONT_ALUMINUM ) {
|
||||
card->CONTtype = -1;
|
||||
card->CONTtypeGiven = FALSE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CONT_P_POLY:
|
||||
if ( value->iValue ) {
|
||||
card->CONTtype = CONT_P_POLY;
|
||||
card->CONTtypeGiven = TRUE;
|
||||
} else {
|
||||
if ( card->CONTtype == CONT_P_POLY ) {
|
||||
card->CONTtype = -1;
|
||||
card->CONTtypeGiven = FALSE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CONT_N_POLY:
|
||||
if ( value->iValue ) {
|
||||
card->CONTtype = CONT_N_POLY;
|
||||
card->CONTtypeGiven = TRUE;
|
||||
} else {
|
||||
if ( card->CONTtype == CONT_N_POLY ) {
|
||||
card->CONTtype = -1;
|
||||
card->CONTtypeGiven = FALSE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CONT_WORKFUN:
|
||||
card->CONTtype = CONT_WORKFUN;
|
||||
card->CONTtypeGiven = TRUE;
|
||||
card->CONTworkfun = value->rValue;
|
||||
card->CONTworkfunGiven = TRUE;
|
||||
break;
|
||||
case CONT_NUMBER:
|
||||
card->CONTnumber = value->iValue;
|
||||
card->CONTnumberGiven = TRUE;
|
||||
break;
|
||||
default:
|
||||
return(E_BADPARM);
|
||||
break;
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
/**********
|
||||
Copyright 1991 Regents of the University of California. All rights reserved.
|
||||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group
|
||||
Modified: 2001 Paolo Nenzi
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "cktdefs.h"
|
||||
#include "contdefs.h"
|
||||
#include "meshext.h"
|
||||
#include "gendev.h"
|
||||
#include "sperror.h"
|
||||
#include "suffix.h"
|
||||
|
||||
extern int CONTcheck( CONTcard * );
|
||||
extern int CONTsetup( CONTcard *, ELCTelectrode * );
|
||||
|
||||
|
||||
/*
|
||||
* Name: CONTcheck
|
||||
* Purpose: checks a list of CONTcards for input errors
|
||||
* Formals: cardList: the list to check
|
||||
* Returns: OK/E_PRIVATE
|
||||
* Users: numerical device setup routines
|
||||
* Calls: error message handler
|
||||
*/
|
||||
int
|
||||
CONTcheck(CONTcard *cardList)
|
||||
{
|
||||
CONTcard *card;
|
||||
int cardNum = 0;
|
||||
int error = OK;
|
||||
char ebuf[512]; /* error message buffer */
|
||||
|
||||
for ( card = cardList; card != NIL(CONTcard); card = card->CONTnextCard ) {
|
||||
cardNum++;
|
||||
if (!card->CONTnumberGiven) {
|
||||
sprintf( ebuf,
|
||||
"contact card %d is missing an electrode index",
|
||||
cardNum );
|
||||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) );
|
||||
error = E_PRIVATE;
|
||||
}
|
||||
|
||||
/* Return now if anything has failed */
|
||||
if (error) return(error);
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Name: CONTsetup
|
||||
* Purpose: copies information from list of CONTcard's to ELCTelectrode's
|
||||
* Formals: cardList: list of cards to setup
|
||||
* electrodeList: previously built list of ELCTelectrode's
|
||||
* Returns: OK/E_PRIVATE
|
||||
* Users: numerical devices
|
||||
* Calls: CONTcheck
|
||||
*/
|
||||
int
|
||||
CONTsetup(CONTcard *cardList, ELCTelectrode *electrodeList)
|
||||
{
|
||||
CONTcard *card;
|
||||
ELCTelectrode *electrode;
|
||||
int error;
|
||||
|
||||
/* Check the card list */
|
||||
if ((error = CONTcheck( cardList ))) return( error );
|
||||
|
||||
for ( card = cardList; card != NIL(CONTcard); card = card->CONTnextCard ) {
|
||||
|
||||
/* Copy workfunction to all matching electrodes */
|
||||
for ( electrode = electrodeList; electrode != NIL(ELCTelectrode);
|
||||
electrode = electrode->next ) {
|
||||
if ( card->CONTnumber == electrode->id ) {
|
||||
if ( card->CONTworkfunGiven ) {
|
||||
electrode->workf = card->CONTworkfun;
|
||||
} else {
|
||||
electrode->workf = 4.10 /* electron volts */;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return( OK );
|
||||
}
|
||||
|
|
@ -0,0 +1,133 @@
|
|||
/**********
|
||||
Copyright 1991 Regents of the University of California. All rights reserved.
|
||||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group\
|
||||
Modified: 2001 Paolo Nenzi
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numcards.h"
|
||||
#include "numgen.h"
|
||||
#include "numenum.h"
|
||||
#include "domndefs.h"
|
||||
#include "devdefs.h"
|
||||
#include "sperror.h"
|
||||
#include "suffix.h"
|
||||
|
||||
#define UM_TO_CM 1.0e-4
|
||||
|
||||
extern int DOMNnewCard(void**,void*);
|
||||
extern int DOMNparam(int,IFvalue*,void*);
|
||||
|
||||
|
||||
IFparm DOMNpTable[] = {
|
||||
IP("x.low", DOMN_X_LOW, IF_REAL, "Location of left edge"),
|
||||
IP("x.high", DOMN_X_HIGH, IF_REAL, "Location of right edge"),
|
||||
IP("y.low", DOMN_Y_LOW, IF_REAL, "Location of top edge"),
|
||||
IP("y.high", DOMN_Y_HIGH, IF_REAL, "Location of bottom edge"),
|
||||
IP("ix.low", DOMN_IX_LOW, IF_INTEGER, "Index of left edge"),
|
||||
IP("ix.high", DOMN_IX_HIGH, IF_INTEGER, "Index of right edge"),
|
||||
IP("iy.low", DOMN_IY_LOW, IF_INTEGER, "Index of top edge"),
|
||||
IP("iy.high", DOMN_IY_HIGH, IF_INTEGER, "Index of bottom edge"),
|
||||
IP("number", DOMN_NUMBER, IF_INTEGER, "Domain ID number"),
|
||||
IP("material",DOMN_MATERIAL, IF_INTEGER, "Material ID number")
|
||||
};
|
||||
|
||||
IFcardInfo DOMNinfo = {
|
||||
"domain",
|
||||
"Identify material-type for portion of a device",
|
||||
NUMELEMS(DOMNpTable),
|
||||
DOMNpTable,
|
||||
|
||||
DOMNnewCard,
|
||||
DOMNparam,
|
||||
NULL
|
||||
};
|
||||
IFcardInfo REGNinfo = {
|
||||
"region",
|
||||
"Identify material-type for portion of a device",
|
||||
NUMELEMS(DOMNpTable),
|
||||
DOMNpTable,
|
||||
|
||||
DOMNnewCard,
|
||||
DOMNparam,
|
||||
NULL
|
||||
};
|
||||
|
||||
int
|
||||
DOMNnewCard(void **inCard, void *inModel)
|
||||
{
|
||||
DOMNcard *tmpCard, *newCard;
|
||||
GENnumModel *model = (GENnumModel *)inModel;
|
||||
|
||||
newCard = NEW( DOMNcard );
|
||||
if (!newCard) {
|
||||
*inCard = (void *)NULL;
|
||||
return(E_NOMEM);
|
||||
}
|
||||
newCard->DOMNnextCard = (DOMNcard *)NULL;
|
||||
*inCard = (void *)newCard;
|
||||
|
||||
tmpCard = model->GENdomains;
|
||||
if (!tmpCard) { /* First in list */
|
||||
model->GENdomains = newCard;
|
||||
} else {
|
||||
/* Go to end of list */
|
||||
while (tmpCard->DOMNnextCard) tmpCard = tmpCard->DOMNnextCard;
|
||||
/* And add new card */
|
||||
tmpCard->DOMNnextCard = newCard;
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
||||
int
|
||||
DOMNparam(int param, IFvalue *value, void *inCard)
|
||||
{
|
||||
DOMNcard *card = (DOMNcard *)inCard;
|
||||
|
||||
switch (param) {
|
||||
case DOMN_X_LOW:
|
||||
card->DOMNxLow = value->rValue * UM_TO_CM;
|
||||
card->DOMNxLowGiven = TRUE;
|
||||
break;
|
||||
case DOMN_X_HIGH:
|
||||
card->DOMNxHigh = value->rValue * UM_TO_CM;
|
||||
card->DOMNxHighGiven = TRUE;
|
||||
break;
|
||||
case DOMN_Y_LOW:
|
||||
card->DOMNyLow = value->rValue * UM_TO_CM;
|
||||
card->DOMNyLowGiven = TRUE;
|
||||
break;
|
||||
case DOMN_Y_HIGH:
|
||||
card->DOMNyHigh = value->rValue * UM_TO_CM;
|
||||
card->DOMNyHighGiven = TRUE;
|
||||
break;
|
||||
case DOMN_IX_LOW:
|
||||
card->DOMNixLow = value->iValue;
|
||||
card->DOMNixLowGiven = TRUE;
|
||||
break;
|
||||
case DOMN_IX_HIGH:
|
||||
card->DOMNixHigh = value->iValue;
|
||||
card->DOMNixHighGiven = TRUE;
|
||||
break;
|
||||
case DOMN_IY_LOW:
|
||||
card->DOMNiyLow = value->iValue;
|
||||
card->DOMNiyLowGiven = TRUE;
|
||||
break;
|
||||
case DOMN_IY_HIGH:
|
||||
card->DOMNiyHigh = value->iValue;
|
||||
card->DOMNiyHighGiven = TRUE;
|
||||
break;
|
||||
case DOMN_NUMBER:
|
||||
card->DOMNnumber = value->iValue;
|
||||
card->DOMNnumberGiven = TRUE;
|
||||
break;
|
||||
case DOMN_MATERIAL:
|
||||
card->DOMNmaterial = value->iValue;
|
||||
card->DOMNmaterialGiven = TRUE;
|
||||
break;
|
||||
default:
|
||||
return(E_BADPARM);
|
||||
break;
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
|
@ -0,0 +1,207 @@
|
|||
/**********
|
||||
Copyright 1991 Regents of the University of California. All rights reserved.
|
||||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group
|
||||
Modifed: 2001 Paolo Nenzi
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "cktdefs.h"
|
||||
#include "numenum.h"
|
||||
#include "domndefs.h"
|
||||
#include "material.h"
|
||||
#include "meshext.h"
|
||||
#include "gendev.h"
|
||||
#include "sperror.h"
|
||||
#include "suffix.h"
|
||||
|
||||
|
||||
extern int DOMNcheck( DOMNcard *, MATLmaterial * );
|
||||
extern int DOMNsetup( DOMNcard *, DOMNdomain **, MESHcoord *, MESHcoord *,
|
||||
MATLmaterial * );
|
||||
|
||||
|
||||
/*
|
||||
* Name: DOMNcheck
|
||||
* Purpose: checks a list of DOMNcards for input errors
|
||||
* Formals: cardList: the list to check
|
||||
* Returns: OK/E_PRIVATE
|
||||
* Users: numerical device setup routines
|
||||
* Calls: error message handler
|
||||
*/
|
||||
int
|
||||
DOMNcheck(DOMNcard *cardList, MaterialInfo *matlList)
|
||||
{
|
||||
DOMNcard *card;
|
||||
MATLmaterial *matl;
|
||||
int cardNum = 0;
|
||||
int error = OK;
|
||||
char ebuf[512]; /* error message buffer */
|
||||
|
||||
for ( card = cardList; card != NIL(DOMNcard); card = card->DOMNnextCard ) {
|
||||
cardNum++;
|
||||
if (card->DOMNxLowGiven && card->DOMNixLowGiven) {
|
||||
sprintf( ebuf,
|
||||
"domain card %d uses both location and index - location ignored",
|
||||
cardNum );
|
||||
SPfrontEnd->IFerror( ERR_INFO, ebuf, NIL(IFuid) );
|
||||
card->DOMNxLowGiven = FALSE;
|
||||
}
|
||||
if (card->DOMNxHighGiven && card->DOMNixHighGiven) {
|
||||
sprintf( ebuf,
|
||||
"domain card %d uses both location and index - location ignored",
|
||||
cardNum );
|
||||
SPfrontEnd->IFerror( ERR_INFO, ebuf, NIL(IFuid) );
|
||||
card->DOMNxHighGiven = FALSE;
|
||||
}
|
||||
if (card->DOMNyLowGiven && card->DOMNiyLowGiven) {
|
||||
sprintf( ebuf,
|
||||
"domain card %d uses both location and index - location ignored",
|
||||
cardNum );
|
||||
SPfrontEnd->IFerror( ERR_INFO, ebuf, NIL(IFuid) );
|
||||
card->DOMNyLowGiven = FALSE;
|
||||
}
|
||||
if (card->DOMNyHighGiven && card->DOMNiyHighGiven) {
|
||||
sprintf( ebuf,
|
||||
"domain card %d uses both location and index - location ignored",
|
||||
cardNum );
|
||||
SPfrontEnd->IFerror( ERR_INFO, ebuf, NIL(IFuid) );
|
||||
card->DOMNyHighGiven = FALSE;
|
||||
}
|
||||
if (!card->DOMNmaterialGiven) {
|
||||
sprintf( ebuf,
|
||||
"domain card %d is missing a material index",
|
||||
cardNum );
|
||||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) );
|
||||
error = E_PRIVATE;
|
||||
} else {
|
||||
/* Make sure the material exists */
|
||||
for ( matl = matlList; matl != NIL(MATLmaterial); matl = matl->next ) {
|
||||
if ( card->DOMNmaterial == matl->id ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (matl == NIL(MATLmaterial)) {
|
||||
sprintf( ebuf,
|
||||
"domain card %d specifies a non-existent material",
|
||||
cardNum );
|
||||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) );
|
||||
error = E_PRIVATE;
|
||||
}
|
||||
}
|
||||
if (!card->DOMNnumberGiven) {
|
||||
sprintf( ebuf,
|
||||
"domain card %d is missing an ID number",
|
||||
cardNum );
|
||||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) );
|
||||
error = E_PRIVATE;
|
||||
}
|
||||
|
||||
/* Return now if anything has failed */
|
||||
if (error) return(error);
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Name: DOMNsetup
|
||||
* Purpose: convert a list of DOMNcard's to DOMNdomain's
|
||||
* Formals: cardList: list of cards to setup
|
||||
* domainList: returns the list of DOMNdomain's
|
||||
* xMeshList: list of coordinates in the x mesh
|
||||
* yMeshList: list of coordinates in the y mesh
|
||||
* Returns: OK/E_PRIVATE
|
||||
* Users: numerical devices
|
||||
* Calls: DOMNcheck
|
||||
*/
|
||||
int
|
||||
DOMNsetup(DOMNcard *cardList, DOMNdomain **domainList, MESHcoord *xMeshList,
|
||||
MESHcoord *yMeshList, MaterialInfo *materialList)
|
||||
{
|
||||
DOMNcard *card;
|
||||
DOMNdomain *newDomain = NULL;
|
||||
int ixMin, ixMax, iyMin, iyMax;
|
||||
int cardNum = 0;
|
||||
int error;
|
||||
char ebuf[512]; /* error message buffer */
|
||||
|
||||
/* Initialize list of domains */
|
||||
*domainList = NIL(DOMNdomain);
|
||||
|
||||
/* Check the card list */
|
||||
if ((error = DOMNcheck( cardList, materialList ))) return( error );
|
||||
|
||||
/* Find the limits on the indices */
|
||||
MESHiBounds( xMeshList, &ixMin, &ixMax );
|
||||
MESHiBounds( yMeshList, &iyMin, &iyMax );
|
||||
|
||||
error = OK;
|
||||
for ( card = cardList; card != NIL(DOMNcard); card = card->DOMNnextCard ) {
|
||||
cardNum++;
|
||||
|
||||
if (*domainList == NIL(DOMNdomain)) {
|
||||
RALLOC( newDomain, DOMNdomain, 1 );
|
||||
*domainList = newDomain;
|
||||
} else {
|
||||
RALLOC( newDomain->next, DOMNdomain, 1 );
|
||||
newDomain = newDomain->next;
|
||||
}
|
||||
|
||||
newDomain->id = card->DOMNnumber;
|
||||
newDomain->material = card->DOMNmaterial;
|
||||
newDomain->next = NIL(DOMNdomain);
|
||||
|
||||
if (card->DOMNixLowGiven) {
|
||||
newDomain->ixLo = MAX(card->DOMNixLow, ixMin);
|
||||
}
|
||||
else if (card->DOMNxLowGiven) {
|
||||
newDomain->ixLo = MESHlocate( xMeshList, card->DOMNxLow );
|
||||
}
|
||||
else {
|
||||
newDomain->ixLo = ixMin;
|
||||
}
|
||||
if (card->DOMNixHighGiven) {
|
||||
newDomain->ixHi = MIN(card->DOMNixHigh, ixMax);
|
||||
}
|
||||
else if (card->DOMNxHighGiven) {
|
||||
newDomain->ixHi = MESHlocate( xMeshList, card->DOMNxHigh );
|
||||
}
|
||||
else {
|
||||
newDomain->ixHi = ixMax;
|
||||
}
|
||||
if (newDomain->ixLo > newDomain->ixHi) {
|
||||
sprintf( ebuf,
|
||||
"domain card %d has low x index (%d) > high x index (%d)",
|
||||
cardNum, newDomain->ixLo, newDomain->ixHi );
|
||||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) );
|
||||
error = E_PRIVATE;
|
||||
}
|
||||
if (card->DOMNiyLowGiven) {
|
||||
newDomain->iyLo = MAX(card->DOMNiyLow, iyMin);
|
||||
}
|
||||
else if (card->DOMNyLowGiven) {
|
||||
newDomain->iyLo = MESHlocate( yMeshList, card->DOMNyLow );
|
||||
}
|
||||
else {
|
||||
newDomain->iyLo = iyMin;
|
||||
}
|
||||
if (card->DOMNiyHighGiven) {
|
||||
newDomain->iyHi = MIN(card->DOMNiyHigh, iyMax);
|
||||
}
|
||||
else if (card->DOMNyHighGiven) {
|
||||
newDomain->iyHi = MESHlocate( yMeshList, card->DOMNyHigh );
|
||||
}
|
||||
else {
|
||||
newDomain->iyHi = iyMax;
|
||||
}
|
||||
if (newDomain->iyLo > newDomain->iyHi) {
|
||||
sprintf( ebuf,
|
||||
"domain card %d has low y index (%d) > high y index (%d)",
|
||||
cardNum, newDomain->iyLo, newDomain->iyHi );
|
||||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) );
|
||||
error = E_PRIVATE;
|
||||
}
|
||||
}
|
||||
return( error );
|
||||
}
|
||||
|
|
@ -0,0 +1,286 @@
|
|||
/**********
|
||||
Copyright 1991 Regents of the University of California. All rights reserved.
|
||||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group
|
||||
Modified: 2001 Paolo Nenzi
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numcards.h"
|
||||
#include "numgen.h"
|
||||
#include "dopdefs.h"
|
||||
#include "devdefs.h"
|
||||
#include "sperror.h"
|
||||
#include "suffix.h"
|
||||
|
||||
#define UM_TO_CM 1.0e-4
|
||||
|
||||
extern int DOPnewCard(void**,void*);
|
||||
extern int DOPparam(int,IFvalue*,void*);
|
||||
|
||||
|
||||
IFparm DOPpTable[] = {
|
||||
IP("domains", DOP_DOMAIN, IF_INTVEC, "Domain(s) to dope"),
|
||||
IP("uniform", DOP_UNIF, IF_FLAG, "Uniform doping"),
|
||||
IP("linear", DOP_LINEAR, IF_FLAG, "Linear graded doping"),
|
||||
IP("gaussian",DOP_GAUSS, IF_FLAG, "Gaussian doping"),
|
||||
IP("gdiff", DOP_GAUSS, IF_FLAG, "Gaussian diffusion"),
|
||||
IP("gimp", DOP_GAUSS, IF_FLAG, "Gaussian implantation"),
|
||||
IP("erfc", DOP_ERFC, IF_FLAG, "Error-function doping"),
|
||||
IP("errfc", DOP_ERFC, IF_FLAG, "Error-function doping"),
|
||||
IP("exponential",DOP_EXP, IF_FLAG, "Exponential doping"),
|
||||
IP("suprem3", DOP_SUPREM3, IF_FLAG, "Suprem3 doping"),
|
||||
IP("ascii", DOP_ASCII, IF_FLAG, "Ascii-input doping"),
|
||||
IP("infile", DOP_INFILE, IF_STRING, "Input-file name"),
|
||||
IP("lat.rotate",DOP_ROTATE_LAT,IF_FLAG, "Rotate principal profile"),
|
||||
IP("lat.unif",DOP_UNIF_LAT, IF_FLAG, "Uniform lateral falloff"),
|
||||
IP("lat.linf",DOP_LINEAR_LAT, IF_FLAG, "Linear lateral falloff"),
|
||||
IP("lat.gauss",DOP_GAUSS_LAT, IF_FLAG, "Gaussian lateral falloff"),
|
||||
IP("lat.erfc",DOP_ERFC_LAT, IF_FLAG, "Erfc lateral falloff"),
|
||||
IP("lat.exp", DOP_EXP_LAT, IF_FLAG, "Exponential lateral falloff"),
|
||||
IP("boron", DOP_BORON, IF_FLAG, "Boron impurities"),
|
||||
IP("phosphorus",DOP_PHOSP, IF_FLAG, "Phosphorus impurities"),
|
||||
IP("arsenic", DOP_ARSEN, IF_FLAG, "Arsenic impurities"),
|
||||
IP("antimony",DOP_ANTIM, IF_FLAG, "Antimony impurities"),
|
||||
IP("p.type", DOP_P_TYPE, IF_FLAG, "P-type impurities"),
|
||||
IP("acceptor",DOP_P_TYPE, IF_FLAG, "Acceptor impurities"),
|
||||
IP("n.type", DOP_N_TYPE, IF_FLAG, "N-type impurities"),
|
||||
IP("donor", DOP_N_TYPE, IF_FLAG, "Donor impurities"),
|
||||
IP("x.axis", DOP_X_AXIS, IF_FLAG, "X principal axis"),
|
||||
IP("y.axis", DOP_Y_AXIS, IF_FLAG, "Y principal axis"),
|
||||
IP("x.low", DOP_X_LOW, IF_REAL, "Low X edge"),
|
||||
IP("x.high", DOP_X_HIGH, IF_REAL, "High X edge"),
|
||||
IP("y.low", DOP_Y_LOW, IF_REAL, "Low Y edge"),
|
||||
IP("y.high", DOP_Y_HIGH, IF_REAL, "High Y edge"),
|
||||
IP("conc", DOP_CONC, IF_REAL, "Concentration"),
|
||||
IP("peak.conc",DOP_CONC, IF_REAL, "Peak concentration"),
|
||||
IP("location",DOP_LOCATION, IF_REAL, "Peak location"),
|
||||
IP("range", DOP_LOCATION, IF_REAL, "Peak location"),
|
||||
IP("char.length",DOP_CHAR_LEN,IF_REAL, "Characteristic length"),
|
||||
IP("ratio.lat",DOP_RATIO_LAT, IF_REAL, "Lateral ratio")
|
||||
};
|
||||
|
||||
IFcardInfo DOPinfo = {
|
||||
"doping",
|
||||
"Add dopant to domains of a device",
|
||||
NUMELEMS(DOPpTable),
|
||||
DOPpTable,
|
||||
|
||||
DOPnewCard,
|
||||
DOPparam,
|
||||
NULL
|
||||
};
|
||||
|
||||
int
|
||||
DOPnewCard(void **inCard, void *inModel)
|
||||
{
|
||||
DOPcard *tmpCard, *newCard;
|
||||
GENnumModel *model = (GENnumModel *)inModel;
|
||||
|
||||
newCard = NEW( DOPcard );
|
||||
if (!newCard) {
|
||||
*inCard = (void *)NULL;
|
||||
return(E_NOMEM);
|
||||
}
|
||||
newCard->DOPnextCard = (DOPcard *)NULL;
|
||||
*inCard = (void *)newCard;
|
||||
|
||||
tmpCard = model->GENdopings;
|
||||
if (!tmpCard) { /* First in list */
|
||||
model->GENdopings = newCard;
|
||||
} else {
|
||||
/* Go to end of list */
|
||||
while (tmpCard->DOPnextCard) tmpCard = tmpCard->DOPnextCard;
|
||||
/* And add new card */
|
||||
tmpCard->DOPnextCard = newCard;
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
DOPparam(int param, IFvalue *value, void *inCard)
|
||||
{
|
||||
int i;
|
||||
DOPcard *card = (DOPcard *)inCard;
|
||||
|
||||
switch (param) {
|
||||
case DOP_DOMAIN:
|
||||
if ( !card->DOPdomainsGiven ) {
|
||||
card->DOPnumDomains = value->v.numValue;
|
||||
card->DOPdomains = (int *)xmalloc(value->v.numValue * sizeof(int));
|
||||
for ( i=0; i < card->DOPnumDomains; i++ ) {
|
||||
card->DOPdomains[i] = value->v.vec.iVec[i];
|
||||
}
|
||||
card->DOPdomainsGiven = TRUE;
|
||||
} /* else: do nothing, can only define domains once */
|
||||
break;
|
||||
case DOP_ROTATE_LAT:
|
||||
card->DOProtateLat = TRUE;
|
||||
card->DOProtateLatGiven = TRUE;
|
||||
break;
|
||||
case DOP_UNIF:
|
||||
if ( !card->DOPprofileTypeGiven ) {
|
||||
card->DOPprofileType = DOP_UNIF;
|
||||
card->DOPprofileTypeGiven = TRUE;
|
||||
}
|
||||
break;
|
||||
case DOP_UNIF_LAT:
|
||||
if ( !card->DOPlatProfileTypeGiven ) {
|
||||
card->DOPlatProfileType = DOP_UNIF;
|
||||
card->DOPlatProfileTypeGiven = TRUE;
|
||||
}
|
||||
break;
|
||||
case DOP_LINEAR:
|
||||
if ( !card->DOPprofileTypeGiven ) {
|
||||
card->DOPprofileType = DOP_LINEAR;
|
||||
card->DOPprofileTypeGiven = TRUE;
|
||||
}
|
||||
break;
|
||||
case DOP_LINEAR_LAT:
|
||||
if ( !card->DOPlatProfileTypeGiven ) {
|
||||
card->DOPlatProfileType = DOP_LINEAR_LAT;
|
||||
card->DOPlatProfileTypeGiven = TRUE;
|
||||
}
|
||||
break;
|
||||
case DOP_GAUSS:
|
||||
if ( !card->DOPprofileTypeGiven ) {
|
||||
card->DOPprofileType = DOP_GAUSS;
|
||||
card->DOPprofileTypeGiven = TRUE;
|
||||
}
|
||||
break;
|
||||
case DOP_GAUSS_LAT:
|
||||
if ( !card->DOPlatProfileTypeGiven ) {
|
||||
card->DOPlatProfileType = DOP_GAUSS;
|
||||
card->DOPlatProfileTypeGiven = TRUE;
|
||||
}
|
||||
break;
|
||||
case DOP_ERFC:
|
||||
if ( !card->DOPprofileTypeGiven ) {
|
||||
card->DOPprofileType = DOP_ERFC;
|
||||
card->DOPprofileTypeGiven = TRUE;
|
||||
}
|
||||
break;
|
||||
case DOP_ERFC_LAT:
|
||||
if ( !card->DOPlatProfileTypeGiven ) {
|
||||
card->DOPlatProfileType = DOP_ERFC;
|
||||
card->DOPlatProfileTypeGiven = TRUE;
|
||||
}
|
||||
break;
|
||||
case DOP_EXP:
|
||||
if ( !card->DOPprofileTypeGiven ) {
|
||||
card->DOPprofileType = DOP_EXP;
|
||||
card->DOPprofileTypeGiven = TRUE;
|
||||
}
|
||||
break;
|
||||
case DOP_EXP_LAT:
|
||||
if ( !card->DOPlatProfileTypeGiven ) {
|
||||
card->DOPlatProfileType = DOP_EXP;
|
||||
card->DOPlatProfileTypeGiven = TRUE;
|
||||
}
|
||||
break;
|
||||
case DOP_SUPREM3:
|
||||
if ( !card->DOPprofileTypeGiven ) {
|
||||
card->DOPprofileType = DOP_SUPREM3;
|
||||
card->DOPprofileTypeGiven = TRUE;
|
||||
} else if ( card->DOPprofileType == DOP_ASCII ) {
|
||||
card->DOPprofileType = DOP_SUPASCII;
|
||||
}
|
||||
break;
|
||||
case DOP_ASCII:
|
||||
if ( !card->DOPprofileTypeGiven ) {
|
||||
card->DOPprofileType = DOP_ASCII;
|
||||
card->DOPprofileTypeGiven = TRUE;
|
||||
} else if ( card->DOPprofileType == DOP_SUPREM3 ) {
|
||||
card->DOPprofileType = DOP_SUPASCII;
|
||||
}
|
||||
break;
|
||||
case DOP_BORON:
|
||||
if ( !card->DOPimpurityTypeGiven ) {
|
||||
card->DOPimpurityType = DOP_BORON;
|
||||
card->DOPimpurityTypeGiven = TRUE;
|
||||
}
|
||||
break;
|
||||
case DOP_PHOSP:
|
||||
if ( !card->DOPimpurityTypeGiven ) {
|
||||
card->DOPimpurityType = DOP_PHOSP;
|
||||
card->DOPimpurityTypeGiven = TRUE;
|
||||
}
|
||||
break;
|
||||
case DOP_ARSEN:
|
||||
if ( !card->DOPimpurityTypeGiven ) {
|
||||
card->DOPimpurityType = DOP_ARSEN;
|
||||
card->DOPimpurityTypeGiven = TRUE;
|
||||
}
|
||||
break;
|
||||
case DOP_ANTIM:
|
||||
if ( !card->DOPimpurityTypeGiven ) {
|
||||
card->DOPimpurityType = DOP_ANTIM;
|
||||
card->DOPimpurityTypeGiven = TRUE;
|
||||
}
|
||||
break;
|
||||
case DOP_P_TYPE:
|
||||
if ( !card->DOPimpurityTypeGiven ) {
|
||||
card->DOPimpurityType = DOP_P_TYPE;
|
||||
card->DOPimpurityTypeGiven = TRUE;
|
||||
}
|
||||
break;
|
||||
case DOP_N_TYPE:
|
||||
if ( !card->DOPimpurityTypeGiven ) {
|
||||
card->DOPimpurityType = DOP_N_TYPE;
|
||||
card->DOPimpurityTypeGiven = TRUE;
|
||||
}
|
||||
break;
|
||||
case DOP_X_AXIS:
|
||||
if ( !card->DOPaxisTypeGiven ) {
|
||||
card->DOPaxisType = DOP_X_AXIS;
|
||||
card->DOPaxisTypeGiven = TRUE;
|
||||
}
|
||||
break;
|
||||
case DOP_Y_AXIS:
|
||||
if ( !card->DOPaxisTypeGiven ) {
|
||||
card->DOPaxisType = DOP_Y_AXIS;
|
||||
card->DOPaxisTypeGiven = TRUE;
|
||||
}
|
||||
break;
|
||||
case DOP_INFILE:
|
||||
card->DOPinFile = value->sValue;
|
||||
card->DOPinFileGiven = TRUE;
|
||||
break;
|
||||
case DOP_X_LOW:
|
||||
card->DOPxLow = value->rValue * UM_TO_CM;
|
||||
card->DOPxLowGiven = TRUE;
|
||||
break;
|
||||
case DOP_X_HIGH:
|
||||
card->DOPxHigh = value->rValue * UM_TO_CM;
|
||||
card->DOPxHighGiven = TRUE;
|
||||
break;
|
||||
case DOP_Y_LOW:
|
||||
card->DOPyLow = value->rValue * UM_TO_CM;
|
||||
card->DOPyLowGiven = TRUE;
|
||||
break;
|
||||
case DOP_Y_HIGH:
|
||||
card->DOPyHigh = value->rValue * UM_TO_CM;
|
||||
card->DOPyHighGiven = TRUE;
|
||||
break;
|
||||
case DOP_CONC:
|
||||
card->DOPconc = fabs(value->rValue);
|
||||
card->DOPconcGiven = TRUE;
|
||||
break;
|
||||
case DOP_LOCATION:
|
||||
card->DOPlocation = value->rValue * UM_TO_CM;
|
||||
card->DOPlocationGiven = TRUE;
|
||||
break;
|
||||
case DOP_CHAR_LEN:
|
||||
card->DOPcharLen = value->rValue * UM_TO_CM;
|
||||
card->DOPcharLenGiven = TRUE;
|
||||
break;
|
||||
case DOP_RATIO_LAT:
|
||||
card->DOPratioLat = value->rValue;
|
||||
card->DOPratioLatGiven = TRUE;
|
||||
break;
|
||||
default:
|
||||
return(E_BADPARM);
|
||||
break;
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
|
@ -0,0 +1,393 @@
|
|||
/**********
|
||||
Copyright 1991 Regents of the University of California. All rights reserved.
|
||||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group
|
||||
Modifed: 2001 Paolo Nenzi
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "cktdefs.h"
|
||||
#include "numenum.h"
|
||||
#include "dopdefs.h"
|
||||
#include "meshext.h"
|
||||
#include "profile.h"
|
||||
#include "gendev.h"
|
||||
#include "sperror.h"
|
||||
#include "suffix.h"
|
||||
|
||||
extern int DOPnewCard(void**,void*);
|
||||
extern int DOPparam(int,IFvalue*,void*);
|
||||
|
||||
|
||||
/*
|
||||
* Name: DOPcheck
|
||||
* Purpose: checks a list of DOPcards for input errors
|
||||
* Formals: cardList: the list to check
|
||||
* Returns: OK/E_PRIVATE
|
||||
* Users: numerical device setup routines
|
||||
* Calls: error message handler
|
||||
*/
|
||||
int
|
||||
DOPcheck(DOPcard *cardList, MESHcoord *xMeshList, MESHcoord *yMeshList)
|
||||
{
|
||||
DOPcard *card;
|
||||
int cardNum = 0;
|
||||
int error = OK;
|
||||
char ebuf[512]; /* error message buffer */
|
||||
|
||||
for ( card = cardList; card != NIL(DOPcard); card = card->DOPnextCard ) {
|
||||
cardNum++;
|
||||
if (!card->DOPdomainsGiven) {
|
||||
card->DOPnumDomains = 0;
|
||||
card->DOPdomains = NIL(int);
|
||||
}
|
||||
if (!card->DOPprofileTypeGiven) {
|
||||
sprintf( ebuf,
|
||||
"doping card %d does not specify profile type",
|
||||
cardNum );
|
||||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) );
|
||||
error = E_PRIVATE;
|
||||
} else switch (card->DOPprofileType) {
|
||||
case DOP_UNIF:
|
||||
if (!card->DOPconcGiven) {
|
||||
sprintf( ebuf,
|
||||
"doping card %d needs conc of uniform distribution",
|
||||
cardNum );
|
||||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) );
|
||||
error = E_PRIVATE;
|
||||
}
|
||||
break;
|
||||
case DOP_LINEAR:
|
||||
if (!card->DOPconcGiven) {
|
||||
sprintf( ebuf,
|
||||
"doping card %d needs peak conc of linear distribution",
|
||||
cardNum );
|
||||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) );
|
||||
error = E_PRIVATE;
|
||||
}
|
||||
break;
|
||||
case DOP_GAUSS:
|
||||
if (!card->DOPconcGiven) {
|
||||
sprintf( ebuf,
|
||||
"doping card %d needs peak conc of gaussian distribution",
|
||||
cardNum );
|
||||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) );
|
||||
error = E_PRIVATE;
|
||||
}
|
||||
break;
|
||||
case DOP_ERFC:
|
||||
if (!card->DOPconcGiven) {
|
||||
sprintf( ebuf,
|
||||
"doping card %d needs peak conc of error-function distribution",
|
||||
cardNum );
|
||||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) );
|
||||
error = E_PRIVATE;
|
||||
}
|
||||
break;
|
||||
case DOP_EXP:
|
||||
if (!card->DOPconcGiven) {
|
||||
sprintf( ebuf,
|
||||
"doping card %d needs peak conc of exponential distribution",
|
||||
cardNum );
|
||||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) );
|
||||
error = E_PRIVATE;
|
||||
}
|
||||
break;
|
||||
case DOP_SUPREM3:
|
||||
case DOP_SUPASCII:
|
||||
if (!card->DOPinFileGiven) {
|
||||
sprintf( ebuf,
|
||||
"doping card %d needs input-file name of suprem3 data",
|
||||
cardNum );
|
||||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) );
|
||||
error = E_PRIVATE;
|
||||
}
|
||||
break;
|
||||
case DOP_ASCII:
|
||||
if (!card->DOPinFileGiven) {
|
||||
sprintf( ebuf,
|
||||
"doping card %d needs input-file name of ascii data",
|
||||
cardNum );
|
||||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) );
|
||||
error = E_PRIVATE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
sprintf( ebuf,
|
||||
"doping card %d has unrecognized profile type",
|
||||
cardNum );
|
||||
SPfrontEnd->IFerror( ERR_FATAL, ebuf, NIL(IFuid) );
|
||||
error = E_NOTFOUND;
|
||||
break;
|
||||
}
|
||||
if (!card->DOProtateLatGiven) {
|
||||
card->DOProtateLat = FALSE;
|
||||
}
|
||||
if (!card->DOPlatProfileTypeGiven || card->DOProtateLat) {
|
||||
card->DOPlatProfileType = card->DOPprofileType;
|
||||
}
|
||||
if (!card->DOPratioLatGiven) {
|
||||
card->DOPratioLat = 1.0;
|
||||
}
|
||||
if (!card->DOPcharLenGiven) {
|
||||
card->DOPcharLen = 1.0e-4; /* 1um in centimeters */
|
||||
}
|
||||
if (!card->DOPlocationGiven) {
|
||||
card->DOPlocation = 0.0;
|
||||
}
|
||||
if (!card->DOPimpurityTypeGiven) {
|
||||
card->DOPimpurityType = IMP_N_TYPE;
|
||||
} else switch (card->DOPimpurityType) {
|
||||
case DOP_BORON:
|
||||
card->DOPimpurityType = IMP_BORON;
|
||||
break;
|
||||
case DOP_PHOSP:
|
||||
card->DOPimpurityType = IMP_PHOSPHORUS;
|
||||
break;
|
||||
case DOP_ARSEN:
|
||||
card->DOPimpurityType = IMP_ARSENIC;
|
||||
break;
|
||||
case DOP_ANTIM:
|
||||
card->DOPimpurityType = IMP_ANTIMONY;
|
||||
break;
|
||||
case DOP_N_TYPE:
|
||||
card->DOPimpurityType = IMP_N_TYPE;
|
||||
break;
|
||||
case DOP_P_TYPE:
|
||||
card->DOPimpurityType = IMP_P_TYPE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!card->DOPaxisTypeGiven) {
|
||||
if ( xMeshList && yMeshList ) { /* both lists are non-empty */
|
||||
card->DOPaxisType = DOP_Y_AXIS;
|
||||
} else if ( xMeshList ) { /* x-mesh list is non-empty */
|
||||
card->DOPaxisType = DOP_X_AXIS;
|
||||
} else if ( yMeshList ) { /* y-mesh list is non-empty */
|
||||
card->DOPaxisType = DOP_Y_AXIS;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return now if anything has failed */
|
||||
if (error) return(error);
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Name: DOPsetup
|
||||
* Purpose: convert a list of DOPcard's to DOPprofile's
|
||||
* Formals: cardList: list of cards to setup
|
||||
* profileList: returns the list of DOPprofile's
|
||||
* xMeshList: list of coordinates in the x mesh
|
||||
* yMeshList: list of coordinates in the y mesh
|
||||
* Returns: OK/E_PRIVATE
|
||||
* Users: numerical devices
|
||||
* Calls: DOPcheck
|
||||
*/
|
||||
int
|
||||
DOPsetup(DOPcard *cardList, DOPprofile **profileList, DOPtable **tableList,
|
||||
MESHcoord *xMeshList, MESHcoord *yMeshList)
|
||||
{
|
||||
DOPcard *card;
|
||||
DOPprofile *newProfile = NULL, *endProfile;
|
||||
int impurityId = 0;
|
||||
double xMin, xMax, yMin, yMax;
|
||||
double sign;
|
||||
int error, xProfUnif, yProfUnif;
|
||||
|
||||
/* Initialize list of profiles */
|
||||
*profileList = endProfile = NIL(DOPprofile);
|
||||
|
||||
/* Check the card list */
|
||||
if ((error = DOPcheck( cardList, xMeshList, yMeshList ))) return( error );
|
||||
|
||||
/* Find the limits on locations */
|
||||
MESHlBounds( xMeshList, &xMin, &xMax );
|
||||
MESHlBounds( yMeshList, &yMin, &yMax );
|
||||
|
||||
for ( card = cardList; card != NIL(DOPcard); card = card->DOPnextCard ) {
|
||||
|
||||
if (*profileList == NIL(DOPprofile)) {
|
||||
RALLOC( newProfile, DOPprofile, 1 );
|
||||
*profileList = newProfile;
|
||||
}
|
||||
else {
|
||||
RALLOC( newProfile->next, DOPprofile, 1 );
|
||||
newProfile = newProfile->next;
|
||||
}
|
||||
newProfile->next = NIL(DOPprofile);
|
||||
|
||||
newProfile->numDomains = card->DOPnumDomains;
|
||||
if ( newProfile->numDomains > 0 ) {
|
||||
int i;
|
||||
RALLOC( newProfile->domains, int, newProfile->numDomains );
|
||||
for ( i=0; i < newProfile->numDomains; i++ ) {
|
||||
newProfile->domains[i] = card->DOPdomains[i];
|
||||
}
|
||||
}
|
||||
else {
|
||||
newProfile->domains = NIL(int);
|
||||
}
|
||||
|
||||
if ( card->DOPimpurityType == IMP_P_TYPE ) {
|
||||
sign = -1.0;
|
||||
}
|
||||
else {
|
||||
sign = 1.0;
|
||||
}
|
||||
switch( card->DOPprofileType ) {
|
||||
case DOP_UNIF:
|
||||
newProfile->type = UNIF;
|
||||
newProfile->CONC = sign * card->DOPconc;
|
||||
break;
|
||||
case DOP_LINEAR:
|
||||
newProfile->type = LIN;
|
||||
newProfile->CONC = sign * card->DOPconc;
|
||||
break;
|
||||
case DOP_GAUSS:
|
||||
newProfile->type = GAUSS;
|
||||
newProfile->CONC = sign * card->DOPconc;
|
||||
break;
|
||||
case DOP_ERFC:
|
||||
newProfile->type = ERRFC;
|
||||
newProfile->CONC = sign * card->DOPconc;
|
||||
break;
|
||||
case DOP_EXP:
|
||||
newProfile->type = EXP;
|
||||
newProfile->CONC = sign * card->DOPconc;
|
||||
break;
|
||||
case DOP_SUPREM3:
|
||||
newProfile->type = LOOKUP;
|
||||
readSupremData( card->DOPinFile, 0, card->DOPimpurityType, tableList );
|
||||
newProfile->IMPID = ++impurityId;
|
||||
break;
|
||||
case DOP_SUPASCII:
|
||||
newProfile->type = LOOKUP;
|
||||
readSupremData( card->DOPinFile, 1, card->DOPimpurityType, tableList );
|
||||
newProfile->IMPID = ++impurityId;
|
||||
break;
|
||||
case DOP_ASCII:
|
||||
newProfile->type = LOOKUP;
|
||||
readAsciiData( card->DOPinFile, card->DOPimpurityType, tableList );
|
||||
newProfile->IMPID = ++impurityId;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch( card->DOPlatProfileType ) {
|
||||
case DOP_UNIF:
|
||||
newProfile->latType = UNIF;
|
||||
break;
|
||||
case DOP_LINEAR:
|
||||
newProfile->latType = LIN;
|
||||
break;
|
||||
case DOP_GAUSS:
|
||||
newProfile->latType = GAUSS;
|
||||
break;
|
||||
case DOP_ERFC:
|
||||
newProfile->latType = ERRFC;
|
||||
break;
|
||||
case DOP_EXP:
|
||||
newProfile->latType = EXP;
|
||||
break;
|
||||
case DOP_SUPREM3:
|
||||
case DOP_SUPASCII:
|
||||
newProfile->latType = LOOKUP;
|
||||
break;
|
||||
case DOP_ASCII:
|
||||
newProfile->latType = LOOKUP;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
newProfile->rotate = card->DOProtateLat;
|
||||
newProfile->LOCATION = card->DOPlocation;
|
||||
newProfile->CHAR_LENGTH = card->DOPcharLen;
|
||||
newProfile->LAT_RATIO = card->DOPratioLat;
|
||||
|
||||
xProfUnif = yProfUnif = FALSE;
|
||||
if (card->DOPaxisType == DOP_X_AXIS) {
|
||||
newProfile->DIRECTION = X;
|
||||
if (newProfile->type == UNIF) xProfUnif = TRUE;
|
||||
if (newProfile->latType == UNIF) yProfUnif = TRUE;
|
||||
}
|
||||
else {
|
||||
newProfile->DIRECTION = Y;
|
||||
if (newProfile->type == UNIF) yProfUnif = TRUE;
|
||||
if (newProfile->latType == UNIF) xProfUnif = TRUE;
|
||||
}
|
||||
|
||||
/* Fill in x coordinates. Use defaults if necessary */
|
||||
if (card->DOPxLowGiven && card->DOPxHighGiven) {
|
||||
newProfile->X_LOW = card->DOPxLow;
|
||||
newProfile->X_HIGH = card->DOPxHigh;
|
||||
}
|
||||
else if (card->DOPxLowGiven) {
|
||||
newProfile->X_LOW = card->DOPxLow;
|
||||
if (xProfUnif) {
|
||||
newProfile->X_HIGH = xMax;
|
||||
}
|
||||
else {
|
||||
newProfile->X_HIGH = newProfile->X_LOW;
|
||||
}
|
||||
}
|
||||
else if (card->DOPxHighGiven) {
|
||||
newProfile->X_HIGH = card->DOPxHigh;
|
||||
if (xProfUnif) {
|
||||
newProfile->X_LOW = xMin;
|
||||
}
|
||||
else {
|
||||
newProfile->X_LOW = newProfile->X_HIGH;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (xProfUnif) {
|
||||
newProfile->X_LOW = xMin;
|
||||
newProfile->X_HIGH = xMax;
|
||||
}
|
||||
else {
|
||||
newProfile->X_LOW = 0.5 * (xMin + xMax);
|
||||
newProfile->X_HIGH = 0.5 * (xMin + xMax);
|
||||
}
|
||||
}
|
||||
|
||||
/* Fill in y coordinates. Use defaults if necessary */
|
||||
if (card->DOPyLowGiven && card->DOPyHighGiven) {
|
||||
newProfile->Y_LOW = card->DOPyLow;
|
||||
newProfile->Y_HIGH = card->DOPyHigh;
|
||||
}
|
||||
else if (card->DOPyLowGiven) {
|
||||
newProfile->Y_LOW = card->DOPyLow;
|
||||
if (yProfUnif) {
|
||||
newProfile->Y_HIGH = yMax;
|
||||
}
|
||||
else {
|
||||
newProfile->Y_HIGH = newProfile->Y_LOW;
|
||||
}
|
||||
}
|
||||
else if (card->DOPyHighGiven) {
|
||||
newProfile->Y_HIGH = card->DOPyHigh;
|
||||
if (xProfUnif) {
|
||||
newProfile->Y_LOW = yMin;
|
||||
}
|
||||
else {
|
||||
newProfile->Y_LOW = newProfile->Y_HIGH;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (yProfUnif) {
|
||||
newProfile->Y_LOW = yMin;
|
||||
newProfile->Y_HIGH = yMax;
|
||||
}
|
||||
else {
|
||||
newProfile->Y_LOW = 0.5 * (yMin + yMax);
|
||||
newProfile->Y_HIGH = 0.5 * (yMin + yMax);
|
||||
}
|
||||
}
|
||||
}
|
||||
return( OK );
|
||||
}
|
||||
|
|
@ -0,0 +1,177 @@
|
|||
/**********
|
||||
Copyright 1991 Regents of the University of California. All rights reserved.
|
||||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group
|
||||
Modified: 2001 Paolo Nenzi
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "cktdefs.h"
|
||||
#include "elctdefs.h"
|
||||
#include "meshext.h"
|
||||
#include "twomesh.h"
|
||||
#include "gendev.h"
|
||||
#include "sperror.h"
|
||||
#include "suffix.h"
|
||||
|
||||
|
||||
extern int ELCTcheck( ELCTcard * );
|
||||
extern int ELCTsetup( ELCTcard *, ELCTelectrode **, MESHcoord *, MESHcoord * );
|
||||
|
||||
|
||||
/*
|
||||
* Name: ELCTcheck
|
||||
* Purpose: checks a list of ELCTcards for input errors
|
||||
* Formals: cardList: the list to check
|
||||
* Returns: OK/E_PRIVATE
|
||||
* Users: numerical device setup routines
|
||||
* Calls: error message handler
|
||||
*/
|
||||
int
|
||||
ELCTcheck(ELCTcard *cardList)
|
||||
{
|
||||
ELCTcard *card;
|
||||
int cardNum = 0;
|
||||
int error = OK;
|
||||
char ebuf[512]; /* error message buffer */
|
||||
|
||||
for ( card = cardList; card != NIL(ELCTcard); card = card->ELCTnextCard ) {
|
||||
cardNum++;
|
||||
if (card->ELCTxLowGiven && card->ELCTixLowGiven) {
|
||||
sprintf( ebuf,
|
||||
"electrode card %d uses both location and index - location ignored",
|
||||
cardNum );
|
||||
SPfrontEnd->IFerror( ERR_INFO, ebuf, NIL(IFuid) );
|
||||
card->ELCTxLowGiven = FALSE;
|
||||
}
|
||||
if (card->ELCTxHighGiven && card->ELCTixHighGiven) {
|
||||
sprintf( ebuf,
|
||||
"electrode card %d uses both location and index - location ignored",
|
||||
cardNum );
|
||||
SPfrontEnd->IFerror( ERR_INFO, ebuf, NIL(IFuid) );
|
||||
card->ELCTxHighGiven = FALSE;
|
||||
}
|
||||
if (card->ELCTyLowGiven && card->ELCTiyLowGiven) {
|
||||
sprintf( ebuf,
|
||||
"electrode card %d uses both location and index - location ignored",
|
||||
cardNum );
|
||||
SPfrontEnd->IFerror( ERR_INFO, ebuf, NIL(IFuid) );
|
||||
card->ELCTyLowGiven = FALSE;
|
||||
}
|
||||
if (card->ELCTyHighGiven && card->ELCTiyHighGiven) {
|
||||
sprintf( ebuf,
|
||||
"electrode card %d uses both location and index - location ignored",
|
||||
cardNum );
|
||||
SPfrontEnd->IFerror( ERR_INFO, ebuf, NIL(IFuid) );
|
||||
card->ELCTyHighGiven = FALSE;
|
||||
}
|
||||
if (!card->ELCTnumberGiven) {
|
||||
card->ELCTnumber = -1;
|
||||
}
|
||||
|
||||
/* Return now if anything has failed */
|
||||
if (error) return(error);
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Name: ELCTsetup
|
||||
* Purpose: convert a list of ELCTcard's to ELCTelectrode's
|
||||
* Formals: cardList: list of cards to setup
|
||||
* electrodeList: returns the list of ELCTelectrode's
|
||||
* xMeshList: list of coordinates in the x mesh
|
||||
* yMeshList: list of coordinates in the y mesh
|
||||
* Returns: OK/E_PRIVATE
|
||||
* Users: numerical devices
|
||||
* Calls: ELCTcheck
|
||||
*/
|
||||
int
|
||||
ELCTsetup(ELCTcard *cardList, ELCTelectrode **electrodeList,
|
||||
MESHcoord *xMeshList, MESHcoord *yMeshList)
|
||||
{
|
||||
ELCTcard *card;
|
||||
ELCTelectrode *newElectrode = NULL;
|
||||
int ixMin, ixMax, iyMin, iyMax;
|
||||
int cardNum = 0;
|
||||
int error;
|
||||
char ebuf[512]; /* error message buffer */
|
||||
|
||||
/* Initialize list of electrodes */
|
||||
*electrodeList = NIL(ELCTelectrode);
|
||||
|
||||
/* Check the card list */
|
||||
if ((error = ELCTcheck( cardList ))) return( error );
|
||||
|
||||
/* Find the limits on the indices */
|
||||
MESHiBounds( xMeshList, &ixMin, &ixMax );
|
||||
MESHiBounds( yMeshList, &iyMin, &iyMax );
|
||||
|
||||
error = OK;
|
||||
for ( card = cardList; card != NIL(ELCTcard); card = card->ELCTnextCard ) {
|
||||
cardNum++;
|
||||
|
||||
if (*electrodeList == NIL(ELCTelectrode)) {
|
||||
RALLOC( newElectrode, ELCTelectrode, 1 );
|
||||
*electrodeList = newElectrode;
|
||||
} else {
|
||||
RALLOC( newElectrode->next, ELCTelectrode, 1 );
|
||||
newElectrode = newElectrode->next;
|
||||
}
|
||||
newElectrode->next = NIL(ELCTelectrode);
|
||||
newElectrode->id = card->ELCTnumber;
|
||||
newElectrode->workf = 4.10 /* electron volts */;
|
||||
|
||||
if (card->ELCTixLowGiven) {
|
||||
newElectrode->ixLo = MAX(card->ELCTixLow, ixMin);
|
||||
}
|
||||
else if (card->ELCTxLowGiven) {
|
||||
newElectrode->ixLo = MESHlocate( xMeshList, card->ELCTxLow );
|
||||
}
|
||||
else {
|
||||
newElectrode->ixLo = ixMin;
|
||||
}
|
||||
if (card->ELCTixHighGiven) {
|
||||
newElectrode->ixHi = MIN(card->ELCTixHigh, ixMax);
|
||||
}
|
||||
else if (card->ELCTxHighGiven) {
|
||||
newElectrode->ixHi = MESHlocate( xMeshList, card->ELCTxHigh );
|
||||
}
|
||||
else {
|
||||
newElectrode->ixHi = ixMax;
|
||||
}
|
||||
if (newElectrode->ixLo > newElectrode->ixHi) {
|
||||
sprintf( ebuf,
|
||||
"electrode card %d has low x index (%d) > high x index (%d)",
|
||||
cardNum, newElectrode->ixLo, newElectrode->ixHi );
|
||||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) );
|
||||
error = E_PRIVATE;
|
||||
}
|
||||
if (card->ELCTiyLowGiven) {
|
||||
newElectrode->iyLo = MAX(card->ELCTiyLow, iyMin);
|
||||
}
|
||||
else if (card->ELCTyLowGiven) {
|
||||
newElectrode->iyLo = MESHlocate( yMeshList, card->ELCTyLow );
|
||||
}
|
||||
else {
|
||||
newElectrode->iyLo = iyMin;
|
||||
}
|
||||
if (card->ELCTiyHighGiven) {
|
||||
newElectrode->iyHi = MIN(card->ELCTiyHigh, iyMax);
|
||||
}
|
||||
else if (card->ELCTyHighGiven) {
|
||||
newElectrode->iyHi = MESHlocate( yMeshList, card->ELCTyHigh );
|
||||
}
|
||||
else {
|
||||
newElectrode->iyHi = iyMax;
|
||||
}
|
||||
if (newElectrode->iyLo > newElectrode->iyHi) {
|
||||
sprintf( ebuf,
|
||||
"electrode card %d has low y index (%d) > high y index (%d)",
|
||||
cardNum, newElectrode->iyLo, newElectrode->iyHi );
|
||||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) );
|
||||
error = E_PRIVATE;
|
||||
}
|
||||
}
|
||||
return( error );
|
||||
}
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
/**********
|
||||
Copyright 1991 Regents of the University of California. All rights reserved.
|
||||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group
|
||||
Modified: 2001 Paolo Nenzi
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numcards.h"
|
||||
#include "numgen.h"
|
||||
#include "elctdefs.h"
|
||||
#include "devdefs.h"
|
||||
#include "sperror.h"
|
||||
#include "suffix.h"
|
||||
|
||||
#define UM_TO_CM 1.0e-4
|
||||
|
||||
extern int ELCTnewCard(void**,void*);
|
||||
extern int ELCTparam(int,IFvalue*,void*);
|
||||
|
||||
|
||||
IFparm ELCTpTable[] = {
|
||||
IP("x.low", ELCT_X_LOW, IF_REAL, "Location of left edge"),
|
||||
IP("x.high", ELCT_X_HIGH, IF_REAL, "Location of right edge"),
|
||||
IP("y.low", ELCT_Y_LOW, IF_REAL, "Location of top edge"),
|
||||
IP("y.high", ELCT_Y_HIGH, IF_REAL, "Location of bottom edge"),
|
||||
IP("ix.low", ELCT_IX_LOW, IF_INTEGER, "Index of left edge"),
|
||||
IP("ix.high", ELCT_IX_HIGH, IF_INTEGER, "Index of right edge"),
|
||||
IP("iy.low", ELCT_IY_LOW, IF_INTEGER, "Index of top edge"),
|
||||
IP("iy.high", ELCT_IY_HIGH, IF_INTEGER, "Index of bottom edge"),
|
||||
IP("number", ELCT_NUMBER, IF_INTEGER, "Electrode ID number")
|
||||
};
|
||||
|
||||
IFcardInfo ELCTinfo = {
|
||||
"electrode",
|
||||
"Location of a contact to the device",
|
||||
NUMELEMS(ELCTpTable),
|
||||
ELCTpTable,
|
||||
|
||||
ELCTnewCard,
|
||||
ELCTparam,
|
||||
NULL
|
||||
};
|
||||
|
||||
int
|
||||
ELCTnewCard(void **inCard, void *inModel)
|
||||
{
|
||||
ELCTcard *tmpCard, *newCard;
|
||||
GENnumModel *model = (GENnumModel *)inModel;
|
||||
|
||||
newCard = NEW( ELCTcard );
|
||||
if (!newCard) {
|
||||
*inCard = (void *)NULL;
|
||||
return(E_NOMEM);
|
||||
}
|
||||
newCard->ELCTnextCard = (ELCTcard *)NULL;
|
||||
*inCard = (void *)newCard;
|
||||
|
||||
tmpCard = model->GENelectrodes;
|
||||
if (!tmpCard) { /* First in list */
|
||||
model->GENelectrodes = newCard;
|
||||
} else {
|
||||
/* Go to end of list */
|
||||
while (tmpCard->ELCTnextCard) tmpCard = tmpCard->ELCTnextCard;
|
||||
/* And add new card */
|
||||
tmpCard->ELCTnextCard = newCard;
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
ELCTparam(int param, IFvalue *value, void *inCard)
|
||||
{
|
||||
ELCTcard *card = (ELCTcard *)inCard;
|
||||
|
||||
switch (param) {
|
||||
case ELCT_X_LOW:
|
||||
card->ELCTxLow = value->rValue * UM_TO_CM;
|
||||
card->ELCTxLowGiven = TRUE;
|
||||
break;
|
||||
case ELCT_X_HIGH:
|
||||
card->ELCTxHigh = value->rValue * UM_TO_CM;
|
||||
card->ELCTxHighGiven = TRUE;
|
||||
break;
|
||||
case ELCT_Y_LOW:
|
||||
card->ELCTyLow = value->rValue * UM_TO_CM;
|
||||
card->ELCTyLowGiven = TRUE;
|
||||
break;
|
||||
case ELCT_Y_HIGH:
|
||||
card->ELCTyHigh = value->rValue * UM_TO_CM;
|
||||
card->ELCTyHighGiven = TRUE;
|
||||
break;
|
||||
case ELCT_IX_LOW:
|
||||
card->ELCTixLow = value->iValue;
|
||||
card->ELCTixLowGiven = TRUE;
|
||||
break;
|
||||
case ELCT_IX_HIGH:
|
||||
card->ELCTixHigh = value->iValue;
|
||||
card->ELCTixHighGiven = TRUE;
|
||||
break;
|
||||
case ELCT_IY_LOW:
|
||||
card->ELCTiyLow = value->iValue;
|
||||
card->ELCTiyLowGiven = TRUE;
|
||||
break;
|
||||
case ELCT_IY_HIGH:
|
||||
card->ELCTiyHigh = value->iValue;
|
||||
card->ELCTiyHighGiven = TRUE;
|
||||
break;
|
||||
case ELCT_NUMBER:
|
||||
card->ELCTnumber = value->iValue;
|
||||
card->ELCTnumberGiven = TRUE;
|
||||
break;
|
||||
default:
|
||||
return(E_BADPARM);
|
||||
break;
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
|
@ -0,0 +1,305 @@
|
|||
/**********
|
||||
Copyright 1991 Regents of the University of California. All rights reserved.
|
||||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group
|
||||
Modified: 2001 Paolo Nenzi
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numcards.h"
|
||||
#include "numgen.h"
|
||||
#include "numenum.h"
|
||||
#include "matldefs.h"
|
||||
#include "devdefs.h"
|
||||
#include "sperror.h"
|
||||
#include "suffix.h"
|
||||
|
||||
|
||||
extern int MATLnewCard(void**,void *);
|
||||
extern int MATLparam(int,IFvalue*,void *);
|
||||
|
||||
|
||||
IFparm MATLpTable[] = {
|
||||
IP("number", MATL_NUMBER, IF_INTEGER, "Material ID number"),
|
||||
IP("insulator",MATL_INSULATOR,IF_FLAG, "Insulator"),
|
||||
IP("oxide", MATL_OXIDE, IF_FLAG, "Oxide"),
|
||||
IP("sio2", MATL_OXIDE, IF_FLAG, "Oxide"),
|
||||
IP("nitride", MATL_NITRIDE, IF_FLAG, "Nitride"),
|
||||
IP("si3n4", MATL_NITRIDE, IF_FLAG, "Nitride"),
|
||||
IP("semiconductor",MATL_SEMICON,IF_FLAG, "Semiconductor"),
|
||||
IP("silicon", MATL_SILICON, IF_FLAG, "Silicon"),
|
||||
IP("polysilicon",MATL_POLYSIL,IF_FLAG, "Polysilicon"),
|
||||
IP("gaas", MATL_GAAS, IF_FLAG, "Gallium-Arsenide"),
|
||||
IP("nc", MATL_NC0, IF_REAL, "Conduction band density"),
|
||||
IP("nc0", MATL_NC0, IF_REAL, "Conduction band density"),
|
||||
IP("nc300", MATL_NC0, IF_REAL, "Conduction band density"),
|
||||
IP("nv", MATL_NV0, IF_REAL, "Valence band density"),
|
||||
IP("nv0", MATL_NV0, IF_REAL, "Valence band density"),
|
||||
IP("nv300", MATL_NV0, IF_REAL, "Valence band density"),
|
||||
IP("eg", MATL_EG0, IF_REAL, "Energy gap"),
|
||||
IP("eg0", MATL_EG0, IF_REAL, "Energy gap"),
|
||||
IP("eg300", MATL_EG0, IF_REAL, "Energy gap"),
|
||||
IP("deg.dt", MATL_DEGDT, IF_REAL, "Bandgap narrowing w/ temp"),
|
||||
IP("egalpha", MATL_DEGDT, IF_REAL, "Bandgap narrowing w/ temp"),
|
||||
IP("eg.tref", MATL_TREF_EG, IF_REAL, "E-gap reference temperature"),
|
||||
IP("egbeta", MATL_TREF_EG, IF_REAL, "E-gap reference temperature"),
|
||||
IP("deg.dc", MATL_DEGDC, IF_REAL, "Bandgap narrowing w/ N&P doping"),
|
||||
IP("eg.cref", MATL_CREF_EG, IF_REAL, "E-gap reference conc (N&P type)"),
|
||||
IP("nbgn", MATL_CREF_EG, IF_REAL, "E-gap reference conc (N&P type)"),
|
||||
IP("deg.dn", MATL_DEGDN, IF_REAL, "Bandgap narrowing w/ N doping"),
|
||||
IP("eg.nref", MATL_NREF_EG, IF_REAL, "E-gap reference conc (N type)"),
|
||||
IP("nbgnn", MATL_NREF_EG, IF_REAL, "E-gap reference conc (N type)"),
|
||||
IP("deg.dp", MATL_DEGDP, IF_REAL, "Bandgap narrowing w/ P doping"),
|
||||
IP("eg.pref", MATL_PREF_EG, IF_REAL, "E-gap reference conc (P type)"),
|
||||
IP("nbgnp", MATL_PREF_EG, IF_REAL, "E-gap reference conc (P type)"),
|
||||
IP("affinity",MATL_AFFIN, IF_REAL, "Electron affinity"),
|
||||
IP("permittivity",MATL_PERMIT,IF_REAL, "Dielectric permittivity"),
|
||||
IP("epsilon", MATL_PERMIT, IF_REAL, "Dielectric permittivity"),
|
||||
IP("tn", MATL_TAUN0, IF_REAL, "SRH electron lifetime"),
|
||||
IP("tn0", MATL_TAUN0, IF_REAL, "SRH electron lifetime"),
|
||||
IP("taun0", MATL_TAUN0, IF_REAL, "SRH electron lifetime"),
|
||||
IP("tp", MATL_TAUP0, IF_REAL, "SRH hole lifetime"),
|
||||
IP("tp0", MATL_TAUP0, IF_REAL, "SRH hole lifetime"),
|
||||
IP("taup0", MATL_TAUP0, IF_REAL, "SRH hole lifetime"),
|
||||
IP("nsrhn", MATL_NSRHN, IF_REAL, "SRH reference conc (electrons)"),
|
||||
IP("srh.nref",MATL_NSRHN, IF_REAL, "SRH reference conc (electrons)"),
|
||||
IP("nsrhp", MATL_NSRHP, IF_REAL, "SRH reference conc (holes)"),
|
||||
IP("srh.pref",MATL_NSRHP, IF_REAL, "SRH reference conc (holes)"),
|
||||
IP("cn", MATL_CNAUG, IF_REAL, "Auger coefficient (electrons)"),
|
||||
IP("cnaug", MATL_CNAUG, IF_REAL, "Auger coefficient (electrons)"),
|
||||
IP("augn", MATL_CNAUG, IF_REAL, "Auger coefficient (electrons)"),
|
||||
IP("cp", MATL_CPAUG, IF_REAL, "Auger coefficient (holes)"),
|
||||
IP("cpaug", MATL_CPAUG, IF_REAL, "Auger coefficient (holes)"),
|
||||
IP("augp", MATL_CPAUG, IF_REAL, "Auger coefficient (holes)"),
|
||||
IP("arichn", MATL_ARICHN, IF_REAL, "Richardson constant (electrons)"),
|
||||
IP("arichp", MATL_ARICHP, IF_REAL, "Richardson constant (holes)")
|
||||
};
|
||||
|
||||
IFcardInfo MATLinfo = {
|
||||
"material",
|
||||
"Specify physical properties of a material",
|
||||
NUMELEMS(MATLpTable),
|
||||
MATLpTable,
|
||||
|
||||
MATLnewCard,
|
||||
MATLparam,
|
||||
NULL
|
||||
};
|
||||
|
||||
IFcardInfo PHYSinfo = {
|
||||
"physics",
|
||||
"Specify physical properties of a material",
|
||||
NUMELEMS(MATLpTable),
|
||||
MATLpTable,
|
||||
|
||||
MATLnewCard,
|
||||
MATLparam,
|
||||
NULL
|
||||
};
|
||||
|
||||
int
|
||||
MATLnewCard(void **inCard, void *inModel)
|
||||
{
|
||||
MATLcard *tmpCard, *newCard;
|
||||
GENnumModel *model = (GENnumModel *)inModel;
|
||||
|
||||
newCard = NEW( MATLcard );
|
||||
if (!newCard) {
|
||||
*inCard = (void *)NULL;
|
||||
return(E_NOMEM);
|
||||
}
|
||||
newCard->MATLnextCard = (MATLcard *)NULL;
|
||||
*inCard = (void *)newCard;
|
||||
|
||||
tmpCard = model->GENmaterials;
|
||||
if (!tmpCard) { /* First in list */
|
||||
model->GENmaterials = newCard;
|
||||
} else {
|
||||
/* Go to end of list */
|
||||
while (tmpCard->MATLnextCard) tmpCard = tmpCard->MATLnextCard;
|
||||
/* And add new card */
|
||||
tmpCard->MATLnextCard = newCard;
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
||||
int
|
||||
MATLparam(int param, IFvalue *value, void *inCard)
|
||||
{
|
||||
MATLcard *card = (MATLcard *)inCard;
|
||||
|
||||
switch (param) {
|
||||
case MATL_NUMBER:
|
||||
card->MATLnumber = value->iValue;
|
||||
card->MATLnumberGiven = TRUE;
|
||||
break;
|
||||
case MATL_NC0:
|
||||
card->MATLnc0 = value->rValue;
|
||||
card->MATLnc0Given = TRUE;
|
||||
break;
|
||||
case MATL_NV0:
|
||||
card->MATLnv0 = value->rValue;
|
||||
card->MATLnv0Given = TRUE;
|
||||
break;
|
||||
case MATL_EG0:
|
||||
card->MATLeg0 = value->rValue;
|
||||
card->MATLeg0Given = TRUE;
|
||||
break;
|
||||
case MATL_DEGDT:
|
||||
card->MATLdEgdT = value->rValue;
|
||||
card->MATLdEgdTGiven = TRUE;
|
||||
break;
|
||||
case MATL_TREF_EG:
|
||||
card->MATLtrefEg = value->rValue;
|
||||
card->MATLtrefEgGiven = TRUE;
|
||||
break;
|
||||
case MATL_DEGDC:
|
||||
card->MATLdEgdN = value->rValue;
|
||||
card->MATLdEgdNGiven = TRUE;
|
||||
card->MATLdEgdP = value->rValue;
|
||||
card->MATLdEgdPGiven = TRUE;
|
||||
break;
|
||||
case MATL_CREF_EG:
|
||||
card->MATLnrefEg = value->rValue;
|
||||
card->MATLnrefEgGiven = TRUE;
|
||||
card->MATLprefEg = value->rValue;
|
||||
card->MATLprefEgGiven = TRUE;
|
||||
break;
|
||||
case MATL_DEGDN:
|
||||
card->MATLdEgdN = value->rValue;
|
||||
card->MATLdEgdNGiven = TRUE;
|
||||
break;
|
||||
case MATL_NREF_EG:
|
||||
card->MATLnrefEg = value->rValue;
|
||||
card->MATLnrefEgGiven = TRUE;
|
||||
break;
|
||||
case MATL_DEGDP:
|
||||
card->MATLdEgdP = value->rValue;
|
||||
card->MATLdEgdPGiven = TRUE;
|
||||
break;
|
||||
case MATL_PREF_EG:
|
||||
card->MATLprefEg = value->rValue;
|
||||
card->MATLprefEgGiven = TRUE;
|
||||
break;
|
||||
case MATL_AFFIN:
|
||||
card->MATLaffinity = value->rValue;
|
||||
card->MATLaffinityGiven = TRUE;
|
||||
break;
|
||||
case MATL_PERMIT:
|
||||
card->MATLpermittivity = value->rValue;
|
||||
card->MATLpermittivityGiven = TRUE;
|
||||
break;
|
||||
case MATL_TAUN0:
|
||||
card->MATLtaun0 = value->rValue;
|
||||
card->MATLtaun0Given = TRUE;
|
||||
break;
|
||||
case MATL_TAUP0:
|
||||
card->MATLtaup0 = value->rValue;
|
||||
card->MATLtaup0Given = TRUE;
|
||||
break;
|
||||
case MATL_NSRHN:
|
||||
card->MATLnrefSRHn = value->rValue;
|
||||
card->MATLnrefSRHnGiven = TRUE;
|
||||
break;
|
||||
case MATL_NSRHP:
|
||||
card->MATLnrefSRHp = value->rValue;
|
||||
card->MATLnrefSRHpGiven = TRUE;
|
||||
break;
|
||||
case MATL_CNAUG:
|
||||
card->MATLcnAug = value->rValue;
|
||||
card->MATLcnAugGiven = TRUE;
|
||||
break;
|
||||
case MATL_CPAUG:
|
||||
card->MATLcpAug = value->rValue;
|
||||
card->MATLcpAugGiven = TRUE;
|
||||
break;
|
||||
case MATL_ARICHN:
|
||||
card->MATLaRichN = value->rValue;
|
||||
card->MATLaRichNGiven = TRUE;
|
||||
break;
|
||||
case MATL_ARICHP:
|
||||
card->MATLaRichP = value->rValue;
|
||||
card->MATLaRichPGiven = TRUE;
|
||||
break;
|
||||
case MATL_INSULATOR:
|
||||
if ( value->iValue ) {
|
||||
card->MATLmaterial = INSULATOR;
|
||||
card->MATLmaterialGiven = TRUE;
|
||||
} else {
|
||||
if ( card->MATLmaterial == INSULATOR ) {
|
||||
card->MATLmaterial = -1;
|
||||
card->MATLmaterialGiven = FALSE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MATL_OXIDE:
|
||||
if ( value->iValue ) {
|
||||
card->MATLmaterial = OXIDE;
|
||||
card->MATLmaterialGiven = TRUE;
|
||||
} else {
|
||||
if ( card->MATLmaterial == OXIDE ) {
|
||||
card->MATLmaterial = -1;
|
||||
card->MATLmaterialGiven = FALSE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MATL_NITRIDE:
|
||||
if ( value->iValue ) {
|
||||
card->MATLmaterial = NITRIDE;
|
||||
card->MATLmaterialGiven = TRUE;
|
||||
} else {
|
||||
if ( card->MATLmaterial == NITRIDE ) {
|
||||
card->MATLmaterial = -1;
|
||||
card->MATLmaterialGiven = FALSE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MATL_SEMICON:
|
||||
if ( value->iValue ) {
|
||||
card->MATLmaterial = SEMICON;
|
||||
card->MATLmaterialGiven = TRUE;
|
||||
} else {
|
||||
if ( card->MATLmaterial == SEMICON ) {
|
||||
card->MATLmaterial = -1;
|
||||
card->MATLmaterialGiven = FALSE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MATL_SILICON:
|
||||
if ( value->iValue ) {
|
||||
card->MATLmaterial = SILICON;
|
||||
card->MATLmaterialGiven = TRUE;
|
||||
} else {
|
||||
if ( card->MATLmaterial == SILICON ) {
|
||||
card->MATLmaterial = -1;
|
||||
card->MATLmaterialGiven = FALSE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MATL_POLYSIL:
|
||||
if ( value->iValue ) {
|
||||
card->MATLmaterial = POLYSILICON;
|
||||
card->MATLmaterialGiven = TRUE;
|
||||
} else {
|
||||
if ( card->MATLmaterial == POLYSILICON ) {
|
||||
card->MATLmaterial = -1;
|
||||
card->MATLmaterialGiven = FALSE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MATL_GAAS:
|
||||
if ( value->iValue ) {
|
||||
card->MATLmaterial = GAAS;
|
||||
card->MATLmaterialGiven = TRUE;
|
||||
} else {
|
||||
if ( card->MATLmaterial == GAAS ) {
|
||||
card->MATLmaterial = -1;
|
||||
card->MATLmaterialGiven = FALSE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return(E_BADPARM);
|
||||
break;
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
|
@ -0,0 +1,180 @@
|
|||
/**********
|
||||
Copyright 1992 Regents of the University of California. All rights reserved.
|
||||
Author: 1992 David A. Gates, U. C. Berkeley CAD Group
|
||||
Modified: 2001 Paolo Nenzi
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "cktdefs.h"
|
||||
#include "numconst.h"
|
||||
#include "numenum.h"
|
||||
#include "matldefs.h"
|
||||
#include "material.h"
|
||||
#include "sperror.h"
|
||||
#include "suffix.h"
|
||||
|
||||
|
||||
extern int MATLcheck( MATLcard * );
|
||||
extern int MATLsetup( MATLcard *, MaterialInfo ** );
|
||||
|
||||
/*
|
||||
* Name: MATLcheck
|
||||
* Purpose: checks a list of MATLcards for input errors
|
||||
* Formals: cardList: the list to check
|
||||
* Returns: OK/E_PRIVATE
|
||||
* Users: numerical device setup routines
|
||||
* Calls: error message handler
|
||||
*/
|
||||
int
|
||||
MATLcheck(MATLcard *cardList)
|
||||
{
|
||||
MATLcard *card, *card2;
|
||||
int cardNum = 0, cardNum2;
|
||||
int error = OK;
|
||||
char ebuf[512]; /* error message buffer */
|
||||
|
||||
for ( card = cardList; card != NIL(MATLcard); card = card->MATLnextCard ) {
|
||||
cardNum++;
|
||||
|
||||
if( !card->MATLmaterialGiven ) {
|
||||
card->MATLmaterial = SILICON;
|
||||
}
|
||||
if (!card->MATLnumberGiven) {
|
||||
sprintf( ebuf,
|
||||
"material card %d is missing an id number",
|
||||
cardNum );
|
||||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) );
|
||||
error = E_PRIVATE;
|
||||
}
|
||||
|
||||
/* Return now if anything has failed */
|
||||
if (error) return(error);
|
||||
|
||||
/* Make sure this id is different from all previous ones. */
|
||||
cardNum2 = 0;
|
||||
for ( card2 = cardList; card2 != card; card2 = card2->MATLnextCard ) {
|
||||
cardNum2++;
|
||||
if (card2->MATLnumber == card->MATLnumber) {
|
||||
sprintf( ebuf,
|
||||
"material cards %d and %d use same id %d",
|
||||
cardNum2, cardNum, card->MATLnumber );
|
||||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) );
|
||||
error = E_PRIVATE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return now if anything has failed */
|
||||
if (error) return(error);
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Name: MATLsetup
|
||||
* Purpose: setup the physical model parameters
|
||||
* Formals: cardList: list of cards to setup
|
||||
* Returns: OK/E_PRIVATE
|
||||
* Users: numerical devices
|
||||
* Calls: MATLcheck
|
||||
*/
|
||||
int
|
||||
MATLsetup(MATLcard *cardList, MaterialInfo **materialList)
|
||||
{
|
||||
MATLcard *card;
|
||||
MATLmaterial *newMaterial = NULL;
|
||||
int error;
|
||||
|
||||
/* Initialize list of electrodes */
|
||||
*materialList = NIL(MATLmaterial);
|
||||
|
||||
/* Check the card list */
|
||||
if ((error = MATLcheck( cardList ))) return( error );
|
||||
|
||||
for ( card = cardList; card != NIL(MATLcard); card = card->MATLnextCard ) {
|
||||
|
||||
if (*materialList == NIL(MATLmaterial)) {
|
||||
RALLOC( newMaterial, MATLmaterial, 1 );
|
||||
*materialList = newMaterial;
|
||||
}
|
||||
else {
|
||||
RALLOC( newMaterial->next, MATLmaterial, 1 );
|
||||
newMaterial = newMaterial->next;
|
||||
}
|
||||
newMaterial->next = NIL(MATLmaterial);
|
||||
newMaterial->id = card->MATLnumber;
|
||||
newMaterial->material = card->MATLmaterial;
|
||||
|
||||
/* Fill in default values */
|
||||
MATLdefaults( newMaterial );
|
||||
|
||||
/* Now override with parameters set on the card */
|
||||
if ( card->MATLpermittivityGiven ) {
|
||||
newMaterial->eps = card->MATLpermittivity;
|
||||
/* Multiply by permittivity of free space if relative epsilon given. */
|
||||
if (newMaterial->eps > 0.1) {
|
||||
newMaterial->eps *= EPS0;
|
||||
}
|
||||
}
|
||||
if ( card->MATLaffinityGiven ) {
|
||||
newMaterial->affin = card->MATLaffinity;
|
||||
}
|
||||
if ( card->MATLnc0Given ) {
|
||||
newMaterial->nc0 = card->MATLnc0;
|
||||
}
|
||||
if ( card->MATLnv0Given ) {
|
||||
newMaterial->nv0 = card->MATLnv0;
|
||||
}
|
||||
if ( card->MATLeg0Given ) {
|
||||
newMaterial->eg0 = card->MATLeg0;
|
||||
}
|
||||
if ( card->MATLdEgdTGiven ) {
|
||||
newMaterial->dEgDt = card->MATLdEgdT;
|
||||
}
|
||||
if ( card->MATLtrefEgGiven ) {
|
||||
newMaterial->trefBGN = card->MATLtrefEg;
|
||||
}
|
||||
if ( card->MATLdEgdNGiven ) {
|
||||
newMaterial->dEgDn[ELEC] = card->MATLdEgdN;
|
||||
}
|
||||
if ( card->MATLnrefEgGiven ) {
|
||||
newMaterial->nrefBGN[ELEC] = card->MATLnrefEg;
|
||||
}
|
||||
if ( card->MATLdEgdPGiven ) {
|
||||
newMaterial->dEgDn[HOLE] = card->MATLdEgdP;
|
||||
}
|
||||
if ( card->MATLprefEgGiven ) {
|
||||
newMaterial->nrefBGN[HOLE] = card->MATLprefEg;
|
||||
}
|
||||
if ( card->MATLtaup0Given ) {
|
||||
newMaterial->tau0[HOLE] = card->MATLtaup0;
|
||||
}
|
||||
if ( card->MATLtaun0Given ) {
|
||||
newMaterial->tau0[ELEC] = card->MATLtaun0;
|
||||
}
|
||||
if ( card->MATLtaup0Given ) {
|
||||
newMaterial->tau0[HOLE] = card->MATLtaup0;
|
||||
}
|
||||
if ( card->MATLnrefSRHnGiven ) {
|
||||
newMaterial->nrefSRH[ELEC] = card->MATLnrefSRHn;
|
||||
}
|
||||
if ( card->MATLnrefSRHpGiven ) {
|
||||
newMaterial->nrefSRH[HOLE] = card->MATLnrefSRHp;
|
||||
}
|
||||
if ( card->MATLcnAugGiven ) {
|
||||
newMaterial->cAug[ELEC] = card->MATLcnAug;
|
||||
}
|
||||
if ( card->MATLcpAugGiven ) {
|
||||
newMaterial->cAug[HOLE] = card->MATLcpAug;
|
||||
}
|
||||
if ( card->MATLaRichNGiven ) {
|
||||
newMaterial->aRich[ELEC] = card->MATLaRichN;
|
||||
}
|
||||
if ( card->MATLaRichPGiven ) {
|
||||
newMaterial->aRich[HOLE] = card->MATLaRichP;
|
||||
}
|
||||
|
||||
}
|
||||
return( OK );
|
||||
}
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
/**********
|
||||
Copyright 1991 Regents of the University of California. All rights reserved.
|
||||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group
|
||||
Modified: 2001 Paolo Nenzi
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numcards.h"
|
||||
#include "numgen.h"
|
||||
#include "meshdefs.h"
|
||||
#include "devdefs.h"
|
||||
#include "sperror.h"
|
||||
#include "suffix.h"
|
||||
|
||||
extern int XMSHnewCard(void**,void*);
|
||||
extern int YMSHnewCard(void**,void*);
|
||||
extern int MESHparam(int,IFvalue*,void*);
|
||||
|
||||
|
||||
|
||||
IFparm MESHpTable[] = {
|
||||
IP("location",MESH_LOCATION, IF_REAL, "Meshline location"),
|
||||
IP("width", MESH_WIDTH, IF_REAL, "Distance to next line"),
|
||||
IP("number", MESH_NUMBER, IF_INTEGER, "Meshline number"),
|
||||
IP("node", MESH_NUMBER, IF_INTEGER, "Meshline number"),
|
||||
IP("ratio", MESH_RATIO, IF_REAL, "Suggested spacing ratio"),
|
||||
IP("h.start", MESH_H_START, IF_REAL, "Spacing at start of interval"),
|
||||
IP("h1", MESH_H_START, IF_REAL, "Spacing at start of interVal"),
|
||||
IP("h.end", MESH_H_END, IF_REAL, "Spacing at end of interval"),
|
||||
IP("h2", MESH_H_END, IF_REAL, "Spacing at end of interval"),
|
||||
IP("h.max", MESH_H_MAX, IF_REAL, "Max spacing during interval"),
|
||||
IP("h3", MESH_H_MAX, IF_REAL, "Max spacing during interval")
|
||||
};
|
||||
|
||||
IFcardInfo XMSHinfo = {
|
||||
"x.mesh",
|
||||
"Location of mesh lines",
|
||||
NUMELEMS(MESHpTable),
|
||||
MESHpTable,
|
||||
|
||||
XMSHnewCard,
|
||||
MESHparam,
|
||||
NULL
|
||||
};
|
||||
|
||||
IFcardInfo YMSHinfo = {
|
||||
"y.mesh",
|
||||
"Location of mesh lines",
|
||||
NUMELEMS(MESHpTable),
|
||||
MESHpTable,
|
||||
|
||||
YMSHnewCard,
|
||||
MESHparam,
|
||||
NULL
|
||||
};
|
||||
|
||||
int
|
||||
XMSHnewCard(void **inCard, void *inModel)
|
||||
{
|
||||
MESHcard *tmpCard, *newCard;
|
||||
GENnumModel *model = (GENnumModel *)inModel;
|
||||
|
||||
newCard = NEW( MESHcard );
|
||||
if (!newCard) {
|
||||
*inCard = (void *)NULL;
|
||||
return(E_NOMEM);
|
||||
}
|
||||
newCard->MESHnextCard = (MESHcard *)NULL;
|
||||
*inCard = (void *)newCard;
|
||||
|
||||
tmpCard = model->GENxMeshes;
|
||||
if (!tmpCard) { /* First in list */
|
||||
model->GENxMeshes = newCard;
|
||||
} else {
|
||||
/* Go to end of list */
|
||||
while (tmpCard->MESHnextCard) tmpCard = tmpCard->MESHnextCard;
|
||||
/* And add new card */
|
||||
tmpCard->MESHnextCard = newCard;
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
||||
int
|
||||
YMSHnewCard(void **inCard, void *inModel)
|
||||
{
|
||||
MESHcard *tmpCard, *newCard;
|
||||
GENnumModel *model = (GENnumModel *)inModel;
|
||||
|
||||
newCard = NEW( MESHcard );
|
||||
if (!newCard) {
|
||||
*inCard = (void *)NULL;
|
||||
return(E_NOMEM);
|
||||
}
|
||||
newCard->MESHnextCard = (MESHcard *)NULL;
|
||||
*inCard = (void *)newCard;
|
||||
|
||||
tmpCard = model->GENyMeshes;
|
||||
if (!tmpCard) { /* First in list */
|
||||
model->GENyMeshes = newCard;
|
||||
} else {
|
||||
/* Go to end of list */
|
||||
while (tmpCard->MESHnextCard) tmpCard = tmpCard->MESHnextCard;
|
||||
/* And add new card */
|
||||
tmpCard->MESHnextCard = newCard;
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
||||
int
|
||||
MESHparam(int param, IFvalue *value, void *inCard)
|
||||
{
|
||||
MESHcard *card = (MESHcard *)inCard;
|
||||
|
||||
switch (param) {
|
||||
case MESH_LOCATION:
|
||||
card->MESHlocation = value->rValue;
|
||||
card->MESHlocationGiven = TRUE;
|
||||
break;
|
||||
case MESH_WIDTH:
|
||||
card->MESHwidth = value->rValue;
|
||||
card->MESHwidthGiven = TRUE;
|
||||
break;
|
||||
case MESH_H_START:
|
||||
card->MESHhStart = value->rValue;
|
||||
card->MESHhStartGiven = TRUE;
|
||||
break;
|
||||
case MESH_H_END:
|
||||
card->MESHhEnd = value->rValue;
|
||||
card->MESHhEndGiven = TRUE;
|
||||
break;
|
||||
case MESH_H_MAX:
|
||||
card->MESHhMax = value->rValue;
|
||||
card->MESHhMaxGiven = TRUE;
|
||||
break;
|
||||
case MESH_RATIO:
|
||||
card->MESHratio = value->rValue;
|
||||
card->MESHratioGiven = TRUE;
|
||||
break;
|
||||
case MESH_NUMBER:
|
||||
card->MESHnumber = value->iValue;
|
||||
card->MESHnumberGiven = TRUE;
|
||||
break;
|
||||
default:
|
||||
return(E_BADPARM);
|
||||
break;
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,114 @@
|
|||
/**********
|
||||
Copyright 1991 Regents of the University of California. All rights reserved.
|
||||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group
|
||||
Modified: 2001 Paolo Nenzi
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numcards.h"
|
||||
#include "numgen.h"
|
||||
#include "numenum.h"
|
||||
#include "methdefs.h"
|
||||
#include "sperror.h"
|
||||
#include "devdefs.h"
|
||||
#include "suffix.h"
|
||||
|
||||
|
||||
extern int METHnewCard(void**,void*);
|
||||
extern int METHparam(int,IFvalue*,void*);
|
||||
|
||||
|
||||
IFparm METHpTable[] = {
|
||||
IP("devtol", METH_DABSTOL, IF_REAL, "Absolute tolerance on device equations"),
|
||||
IP("dabstol", METH_DABSTOL, IF_REAL, "Absolute tolerance on device equations"),
|
||||
IP("dreltol", METH_DRELTOL, IF_REAL, "Relative tolerance on device equations"),
|
||||
IP("onecarrier",METH_ONEC, IF_FLAG, "Solve for majority carriers only"),
|
||||
IP("ac.analysis",METH_ACANAL, IF_STRING, "AC solution technique"),
|
||||
IP("frequency",METH_OMEGA, IF_REAL, "AC default frequency"),
|
||||
IP("nomobderiv",METH_NOMOBDERIV,IF_FLAG, "Ignore mobility derivatives"),
|
||||
IP("itlim", METH_ITLIM, IF_INTEGER, "Iteration limit"),
|
||||
IP("voltpred",METH_VOLTPRED, IF_FLAG, "Perform DC voltage prediction")
|
||||
};
|
||||
|
||||
IFcardInfo METHinfo = {
|
||||
"method",
|
||||
"Specify parameters and types of simulation methods",
|
||||
NUMELEMS(METHpTable),
|
||||
METHpTable,
|
||||
|
||||
METHnewCard,
|
||||
METHparam,
|
||||
NULL
|
||||
};
|
||||
|
||||
int
|
||||
METHnewCard(void **inCard, void *inModel)
|
||||
{
|
||||
METHcard *tmpCard, *newCard;
|
||||
GENnumModel *model = (GENnumModel *)inModel;
|
||||
|
||||
tmpCard = model->GENmethods;
|
||||
if (!tmpCard) { /* First in list */
|
||||
newCard = NEW( METHcard );
|
||||
if (!newCard) {
|
||||
*inCard = (void *)NULL;
|
||||
return(E_NOMEM);
|
||||
}
|
||||
newCard->METHnextCard = (METHcard *)NULL;
|
||||
*inCard = (void *)newCard;
|
||||
model->GENmethods = newCard;
|
||||
} else { /* Only one card of this type allowed */
|
||||
*inCard = (void *)tmpCard;
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
||||
int
|
||||
METHparam(int param, IFvalue *value, void *inCard)
|
||||
{
|
||||
METHcard *card = (METHcard *)inCard;
|
||||
|
||||
switch (param) {
|
||||
case METH_DABSTOL:
|
||||
card->METHdabstol = value->rValue;
|
||||
card->METHdabstolGiven = TRUE;
|
||||
break;
|
||||
case METH_DRELTOL:
|
||||
card->METHdreltol = value->rValue;
|
||||
card->METHdreltolGiven = TRUE;
|
||||
break;
|
||||
case METH_OMEGA:
|
||||
card->METHomega = 2.0 * M_PI * value->rValue;
|
||||
card->METHomegaGiven = TRUE;
|
||||
break;
|
||||
case METH_ONEC:
|
||||
card->METHoneCarrier = value->iValue;
|
||||
card->METHoneCarrierGiven = TRUE;
|
||||
break;
|
||||
case METH_NOMOBDERIV:
|
||||
card->METHmobDeriv = !(value->iValue);
|
||||
card->METHmobDerivGiven = TRUE;
|
||||
break;
|
||||
case METH_ACANAL:
|
||||
if ( cinprefix( value->sValue, "direct", 1 ) ) {
|
||||
card->METHacAnalysisMethod = DIRECT;
|
||||
card->METHacAnalysisMethodGiven = TRUE;
|
||||
} else if ( cinprefix( value->sValue, "sor", 1 ) ) {
|
||||
card->METHacAnalysisMethod = SOR;
|
||||
card->METHacAnalysisMethodGiven = TRUE;
|
||||
}
|
||||
break;
|
||||
case METH_ITLIM:
|
||||
card->METHitLim = value->iValue;
|
||||
card->METHitLimGiven = TRUE;
|
||||
break;
|
||||
case METH_VOLTPRED:
|
||||
card->METHvoltPred = value->iValue;
|
||||
card->METHvoltPredGiven = TRUE;
|
||||
break;
|
||||
default:
|
||||
return(E_BADPARM);
|
||||
break;
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
|
@ -0,0 +1,210 @@
|
|||
/**********
|
||||
Copyright 1991 Regents of the University of California. All rights reserved.
|
||||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group
|
||||
Modified: 2001 Paolo Nenzi
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numcards.h"
|
||||
#include "numgen.h"
|
||||
#include "numenum.h"
|
||||
#include "mobdefs.h"
|
||||
#include "sperror.h"
|
||||
#include "devdefs.h"
|
||||
#include "suffix.h"
|
||||
|
||||
extern int MOBnewCard(void**,void*);
|
||||
extern int MOBparam(int,IFvalue*,void*);
|
||||
|
||||
|
||||
|
||||
IFparm MOBpTable[] = {
|
||||
IP("material",MOB_MATERIAL, IF_INTEGER, "Material index"),
|
||||
IP("electron",MOB_ELEC, IF_FLAG, "Electron mobility flag"),
|
||||
IP("hole", MOB_HOLE, IF_FLAG, "Hole mobility flag"),
|
||||
IP("majority",MOB_MAJOR, IF_FLAG, "Majority carrier flag"),
|
||||
IP("minority",MOB_MINOR, IF_FLAG, "Minority carrier flag"),
|
||||
IP("mumax", MOB_MUMAX, IF_REAL, "Maximum bulk mobility"),
|
||||
IP("mumin", MOB_MUMIN, IF_REAL, "Minimum bulk mobility"),
|
||||
IP("ntref", MOB_NTREF, IF_REAL, "Ionized impurity ref. conc."),
|
||||
IP("ntexp", MOB_NTEXP, IF_REAL, "Ionized impurity exponent"),
|
||||
IP("vsat", MOB_VSAT, IF_REAL, "Saturation velocity"),
|
||||
IP("vwarm", MOB_VWARM, IF_REAL, "Warm carrier ref. velocity"),
|
||||
IP("mus", MOB_MUS, IF_REAL, "Initial surface mobility"),
|
||||
IP("ec.a", MOB_EC_A, IF_REAL, "Surf. mobility critical field"),
|
||||
IP("ec.b", MOB_EC_B, IF_REAL, "Surf. mobility critical field #2"),
|
||||
IP("concmodel",MOB_CONC_MOD, IF_STRING, "Concentration dep. model"),
|
||||
IP("fieldmodel",MOB_FIELD_MOD,IF_STRING, "Driving field dep. model"),
|
||||
IP("init", MOB_INIT, IF_FLAG, "Initialize with defaults")
|
||||
};
|
||||
|
||||
IFcardInfo MOBinfo = {
|
||||
"mobility",
|
||||
"Specify parameters and types of mobility models",
|
||||
NUMELEMS(MOBpTable),
|
||||
MOBpTable,
|
||||
|
||||
MOBnewCard,
|
||||
MOBparam,
|
||||
NULL
|
||||
};
|
||||
|
||||
int
|
||||
MOBnewCard(void **inCard, void *inModel)
|
||||
{
|
||||
MOBcard *tmpCard, *newCard;
|
||||
GENnumModel *model = (GENnumModel *)inModel;
|
||||
|
||||
newCard = NEW( MOBcard );
|
||||
if (!newCard) {
|
||||
*inCard = (void *)NULL;
|
||||
return(E_NOMEM);
|
||||
}
|
||||
newCard->MOBnextCard = (MOBcard *)NULL;
|
||||
*inCard = (void *)newCard;
|
||||
|
||||
tmpCard = model->GENmobility;
|
||||
if (!tmpCard) { /* First in list */
|
||||
model->GENmobility = newCard;
|
||||
} else {
|
||||
/* Go to end of list */
|
||||
while (tmpCard->MOBnextCard) tmpCard = tmpCard->MOBnextCard;
|
||||
/* And add new card */
|
||||
tmpCard->MOBnextCard = newCard;
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
||||
int
|
||||
MOBparam(int param, IFvalue *value, void *inCard)
|
||||
{
|
||||
MOBcard *card = (MOBcard *)inCard;
|
||||
|
||||
switch (param) {
|
||||
case MOB_MATERIAL:
|
||||
card->MOBmaterial = value->iValue;
|
||||
card->MOBmaterialGiven = TRUE;
|
||||
break;
|
||||
case MOB_ELEC:
|
||||
if ( value->iValue ) {
|
||||
card->MOBcarrier = ELEC;
|
||||
card->MOBcarrierGiven = TRUE;
|
||||
} else {
|
||||
if ( card->MOBcarrier == ELEC ) {
|
||||
card->MOBcarrier = -1;
|
||||
card->MOBcarrierGiven = FALSE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MOB_HOLE:
|
||||
if ( value->iValue ) {
|
||||
card->MOBcarrier = HOLE;
|
||||
card->MOBcarrierGiven = TRUE;
|
||||
} else {
|
||||
if ( card->MOBcarrier == HOLE ) {
|
||||
card->MOBcarrier = -1;
|
||||
card->MOBcarrierGiven = FALSE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MOB_MAJOR:
|
||||
if ( value->iValue ) {
|
||||
card->MOBcarrType = MAJOR;
|
||||
card->MOBcarrTypeGiven = TRUE;
|
||||
} else {
|
||||
if ( card->MOBcarrType == MAJOR ) {
|
||||
card->MOBcarrType = -1;
|
||||
card->MOBcarrTypeGiven = FALSE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MOB_MINOR:
|
||||
if ( value->iValue ) {
|
||||
card->MOBcarrType = MINOR;
|
||||
card->MOBcarrTypeGiven = TRUE;
|
||||
} else {
|
||||
if ( card->MOBcarrType == MINOR ) {
|
||||
card->MOBcarrType = -1;
|
||||
card->MOBcarrTypeGiven = FALSE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MOB_MUMAX:
|
||||
card->MOBmuMax = value->rValue;
|
||||
card->MOBmuMaxGiven = TRUE;
|
||||
break;
|
||||
case MOB_MUMIN:
|
||||
card->MOBmuMin = value->rValue;
|
||||
card->MOBmuMinGiven = TRUE;
|
||||
break;
|
||||
case MOB_NTREF:
|
||||
card->MOBntRef = value->rValue;
|
||||
card->MOBntRefGiven = TRUE;
|
||||
break;
|
||||
case MOB_NTEXP:
|
||||
card->MOBntExp = value->rValue;
|
||||
card->MOBntExpGiven = TRUE;
|
||||
break;
|
||||
case MOB_VSAT:
|
||||
card->MOBvSat = value->rValue;
|
||||
card->MOBvSatGiven = TRUE;
|
||||
break;
|
||||
case MOB_VWARM:
|
||||
card->MOBvWarm = value->rValue;
|
||||
card->MOBvWarmGiven = TRUE;
|
||||
break;
|
||||
case MOB_MUS:
|
||||
card->MOBmus = value->rValue;
|
||||
card->MOBmusGiven = TRUE;
|
||||
break;
|
||||
case MOB_EC_A:
|
||||
card->MOBecA = value->rValue;
|
||||
card->MOBecAGiven = TRUE;
|
||||
break;
|
||||
case MOB_EC_B:
|
||||
card->MOBecB = value->rValue;
|
||||
card->MOBecBGiven = TRUE;
|
||||
break;
|
||||
case MOB_CONC_MOD:
|
||||
if ( cinprefix( value->sValue, "ct", 1 ) ) {
|
||||
card->MOBconcModel = CT;
|
||||
card->MOBconcModelGiven = TRUE;
|
||||
} else if ( cinprefix( value->sValue, "ar", 1 ) ) {
|
||||
card->MOBconcModel = AR;
|
||||
card->MOBconcModelGiven = TRUE;
|
||||
} else if ( cinprefix( value->sValue, "uf", 1 ) ) {
|
||||
card->MOBconcModel = UF;
|
||||
card->MOBconcModelGiven = TRUE;
|
||||
} else if ( cinprefix( value->sValue, "sg", 1 ) ) {
|
||||
card->MOBconcModel = SG;
|
||||
card->MOBconcModelGiven = TRUE;
|
||||
} else if ( cinprefix( value->sValue, "ga", 1 ) ) {
|
||||
card->MOBconcModel = GA;
|
||||
card->MOBconcModelGiven = TRUE;
|
||||
}
|
||||
break;
|
||||
case MOB_FIELD_MOD:
|
||||
if ( cinprefix( value->sValue, "ct", 1 ) ) {
|
||||
card->MOBfieldModel = CT;
|
||||
card->MOBfieldModelGiven = TRUE;
|
||||
} else if ( cinprefix( value->sValue, "ar", 1 ) ) {
|
||||
card->MOBfieldModel = AR;
|
||||
card->MOBfieldModelGiven = TRUE;
|
||||
} else if ( cinprefix( value->sValue, "sg", 1 ) ) {
|
||||
card->MOBfieldModel = SG;
|
||||
card->MOBfieldModelGiven = TRUE;
|
||||
} else if ( cinprefix( value->sValue, "ga", 1 ) ) {
|
||||
card->MOBfieldModel = GA;
|
||||
card->MOBfieldModelGiven = TRUE;
|
||||
}
|
||||
break;
|
||||
case MOB_INIT:
|
||||
card->MOBinit = value->iValue;
|
||||
card->MOBinitGiven = TRUE;
|
||||
break;
|
||||
default:
|
||||
return(E_BADPARM);
|
||||
break;
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
/**********
|
||||
Copyright 1991 Regents of the University of California. All rights reserved.
|
||||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group
|
||||
Modified: 2001 Paolo Nenzi
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "cktdefs.h"
|
||||
#include "numconst.h"
|
||||
#include "numenum.h"
|
||||
#include "mobdefs.h"
|
||||
#include "material.h"
|
||||
#include "sperror.h"
|
||||
#include "suffix.h"
|
||||
|
||||
extern int MOBcheck( MOBcard *, MaterialInfo * );
|
||||
extern int MOBsetup( MOBcard *, MaterialInfo * );
|
||||
|
||||
/*
|
||||
* Name: MOBcheck
|
||||
* Purpose: checks a list of MOBcards for input errors
|
||||
* Formals: cardList: the list to check
|
||||
* Returns: OK/E_PRIVATE
|
||||
* Users: numerical device setup routines
|
||||
* Calls: error message handler
|
||||
*/
|
||||
int
|
||||
MOBcheck(MOBcard *cardList, MaterialInfo *matlList)
|
||||
{
|
||||
MOBcard *card;
|
||||
MATLmaterial *matl;
|
||||
int cardNum = 0;
|
||||
int error = OK;
|
||||
char ebuf[512]; /* error message buffer */
|
||||
|
||||
for ( card = cardList; card != NIL(MOBcard); card = card->MOBnextCard ) {
|
||||
cardNum++;
|
||||
if (!card->MOBmaterialGiven) {
|
||||
sprintf( ebuf,
|
||||
"mobility card %d is missing a material index",
|
||||
cardNum );
|
||||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) );
|
||||
error = E_PRIVATE;
|
||||
} else {
|
||||
/* Make sure the material exists */
|
||||
for ( matl = matlList; matl != NIL(MATLmaterial); matl = matl->next ) {
|
||||
if ( card->MOBmaterial == matl->id ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (matl == NIL(MATLmaterial)) {
|
||||
sprintf( ebuf,
|
||||
"mobility card %d specifies a non-existent material",
|
||||
cardNum );
|
||||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) );
|
||||
error = E_PRIVATE;
|
||||
}
|
||||
}
|
||||
if (!card->MOBcarrierGiven) {
|
||||
card->MOBcarrier = ELEC;
|
||||
}
|
||||
if (!card->MOBcarrTypeGiven) {
|
||||
card->MOBcarrType = MAJOR;
|
||||
}
|
||||
if (!card->MOBinitGiven) {
|
||||
card->MOBinit = FALSE;
|
||||
}
|
||||
|
||||
/* Return now if anything has failed */
|
||||
if (error) return(error);
|
||||
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Name: MOBsetup
|
||||
* Purpose: setup the mobility model parameters
|
||||
* Formals: cardList: list of cards to setup
|
||||
* Returns: OK/E_PRIVATE
|
||||
* Users: numerical devices
|
||||
* Calls: MOBcheck
|
||||
*/
|
||||
int
|
||||
MOBsetup(MOBcard *cardList, MaterialInfo *materialList)
|
||||
{
|
||||
MOBcard *card;
|
||||
MATLmaterial *matl;
|
||||
int error;
|
||||
|
||||
/* Check the card list */
|
||||
if ((error = MOBcheck( cardList, materialList ))) return( error );
|
||||
|
||||
for ( card = cardList; card != NIL(MOBcard); card = card->MOBnextCard ) {
|
||||
|
||||
/* Find the right material */
|
||||
for ( matl = materialList; matl != NIL(MATLmaterial); matl = matl->next ) {
|
||||
if ( card->MOBmaterial == matl->id ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Default models depend on previous value */
|
||||
if (!card->MOBconcModelGiven) {
|
||||
card->MOBconcModel = matl->concModel;
|
||||
}
|
||||
if (!card->MOBfieldModelGiven) {
|
||||
card->MOBfieldModel = matl->fieldModel;
|
||||
}
|
||||
|
||||
/* Load in default values if desired */
|
||||
if ( card->MOBinitGiven ) {
|
||||
MOBdefaults( matl, card->MOBcarrier, card->MOBcarrType,
|
||||
card->MOBconcModel, card->MOBfieldModel );
|
||||
}
|
||||
|
||||
/* Override defaults */
|
||||
if ( card->MOBconcModelGiven ) {
|
||||
matl->concModel = card->MOBconcModel;
|
||||
}
|
||||
if ( card->MOBfieldModelGiven ) {
|
||||
matl->fieldModel = card->MOBfieldModel;
|
||||
}
|
||||
if ( card->MOBmuMaxGiven ) {
|
||||
matl->muMax[card->MOBcarrier][card->MOBcarrType] = card->MOBmuMax;
|
||||
}
|
||||
if ( card->MOBmuMinGiven ) {
|
||||
matl->muMin[card->MOBcarrier][card->MOBcarrType] = card->MOBmuMin;
|
||||
}
|
||||
if ( card->MOBntRefGiven ) {
|
||||
matl->ntRef[card->MOBcarrier][card->MOBcarrType] = card->MOBntRef;
|
||||
}
|
||||
if ( card->MOBntExpGiven ) {
|
||||
matl->ntExp[card->MOBcarrier][card->MOBcarrType] = card->MOBntExp;
|
||||
}
|
||||
if ( card->MOBvSatGiven ) {
|
||||
matl->vSat[card->MOBcarrier] = card->MOBvSat;
|
||||
}
|
||||
if ( card->MOBvWarmGiven ) {
|
||||
matl->vWarm[card->MOBcarrier] = card->MOBvWarm;
|
||||
}
|
||||
if ( card->MOBmusGiven ) {
|
||||
matl->mus[card->MOBcarrier] = card->MOBmus;
|
||||
}
|
||||
if ( card->MOBecAGiven ) {
|
||||
matl->thetaA[card->MOBcarrier] = 1.0 / MAX( card->MOBecA, 1e-20 );
|
||||
}
|
||||
if ( card->MOBecBGiven ) {
|
||||
matl->thetaB[card->MOBcarrier] = 1.0 / MAX( ABS(card->MOBecB), 1e-20 );
|
||||
matl->thetaB[card->MOBcarrier] *= matl->thetaB[card->MOBcarrier];
|
||||
matl->thetaB[card->MOBcarrier] *= SGN( card->MOBecB );
|
||||
}
|
||||
}
|
||||
return( OK );
|
||||
}
|
||||
|
|
@ -0,0 +1,133 @@
|
|||
/**********
|
||||
Copyright 1991 Regents of the University of California. All rights reserved.
|
||||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group
|
||||
Modified: 2001 Paolo Nenzi
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numcards.h"
|
||||
#include "numgen.h"
|
||||
#include "modldefs.h"
|
||||
#include "devdefs.h"
|
||||
#include "sperror.h"
|
||||
#include "suffix.h"
|
||||
|
||||
extern int MODLnewCard(void**,void*);
|
||||
extern int MODLparam(int,IFvalue*,void*);
|
||||
|
||||
|
||||
IFparm MODLpTable[] = {
|
||||
IP("bgn", MODL_BGNW, IF_FLAG, "Bandgap narrowing"),
|
||||
IP("bgnw", MODL_BGNW, IF_FLAG, "Bandgap narrowing"),
|
||||
IP("tmpmob", MODL_TEMPMOB, IF_FLAG, "Temp-dependent mobility"),
|
||||
IP("tempmob", MODL_TEMPMOB, IF_FLAG, "Temp-dependent mobility"),
|
||||
IP("conmob", MODL_CONCMOB, IF_FLAG, "Conc-dependent mobility"),
|
||||
IP("concmob", MODL_CONCMOB, IF_FLAG, "Conc-dependent mobility"),
|
||||
IP("fldmob", MODL_FIELDMOB, IF_FLAG,
|
||||
"Lateral-field-dependent mobility"),
|
||||
IP("fieldmob",MODL_FIELDMOB, IF_FLAG,
|
||||
"Lateral-field-dependent mobility"),
|
||||
IP("trfmob",MODL_TRANSMOB, IF_FLAG,
|
||||
"Transverse-field-dependent surface mobility"),
|
||||
IP("transmob",MODL_TRANSMOB, IF_FLAG,
|
||||
"Transverse-field-dependent surface mobility"),
|
||||
IP("srfmob", MODL_SURFMOB, IF_FLAG, "Activate surface mobility"),
|
||||
IP("surfmob", MODL_SURFMOB, IF_FLAG, "Activate surface mobility"),
|
||||
IP("matchmob",MODL_MATCHMOB, IF_FLAG,
|
||||
"Matching low-field surface/bulk mobilities"),
|
||||
IP("srh", MODL_SRH, IF_FLAG, "SRH recombination"),
|
||||
IP("consrh", MODL_CONCTAU, IF_FLAG, "Conc-dependent SRH recomb"),
|
||||
IP("conctau", MODL_CONCTAU, IF_FLAG, "Conc-dependent SRH recomb"),
|
||||
IP("auger", MODL_AUGER, IF_FLAG, "Auger recombination"),
|
||||
IP("avalanche",MODL_AVAL, IF_FLAG, "Local avalanche generation")
|
||||
};
|
||||
|
||||
IFcardInfo MODLinfo = {
|
||||
"models",
|
||||
"Specify which physical models should be simulated",
|
||||
NUMELEMS(MODLpTable),
|
||||
MODLpTable,
|
||||
|
||||
MODLnewCard,
|
||||
MODLparam,
|
||||
NULL
|
||||
};
|
||||
|
||||
int
|
||||
MODLnewCard(void **inCard, void *inModel)
|
||||
{
|
||||
MODLcard *tmpCard, *newCard;
|
||||
GENnumModel *model = (GENnumModel *)inModel;
|
||||
|
||||
tmpCard = model->GENmodels;
|
||||
if (!tmpCard) { /* First in list */
|
||||
newCard = NEW( MODLcard );
|
||||
if (!newCard) {
|
||||
*inCard = (void *)NULL;
|
||||
return(E_NOMEM);
|
||||
}
|
||||
newCard->MODLnextCard = (MODLcard *)NULL;
|
||||
*inCard = (void *)newCard;
|
||||
model->GENmodels = newCard;
|
||||
} else { /* Only one card of this type allowed */
|
||||
*inCard = (void *)tmpCard;
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
||||
int
|
||||
MODLparam(int param, IFvalue *value, void *inCard)
|
||||
{
|
||||
MODLcard *card = (MODLcard *)inCard;
|
||||
|
||||
switch (param) {
|
||||
case MODL_BGNW:
|
||||
card->MODLbandGapNarrowing = value->iValue;
|
||||
card->MODLbandGapNarrowingGiven = TRUE;
|
||||
break;
|
||||
case MODL_TEMPMOB:
|
||||
card->MODLtempDepMobility = value->iValue;
|
||||
card->MODLtempDepMobilityGiven = TRUE;
|
||||
break;
|
||||
case MODL_CONCMOB:
|
||||
card->MODLconcDepMobility = value->iValue;
|
||||
card->MODLconcDepMobilityGiven = TRUE;
|
||||
break;
|
||||
case MODL_TRANSMOB:
|
||||
card->MODLtransDepMobility = value->iValue;
|
||||
card->MODLtransDepMobilityGiven = TRUE;
|
||||
break;
|
||||
case MODL_FIELDMOB:
|
||||
card->MODLfieldDepMobility = value->iValue;
|
||||
card->MODLfieldDepMobilityGiven = TRUE;
|
||||
break;
|
||||
case MODL_SURFMOB:
|
||||
card->MODLsurfaceMobility = value->iValue;
|
||||
card->MODLsurfaceMobilityGiven = TRUE;
|
||||
break;
|
||||
case MODL_MATCHMOB:
|
||||
card->MODLmatchingMobility = value->iValue;
|
||||
card->MODLmatchingMobilityGiven = TRUE;
|
||||
break;
|
||||
case MODL_SRH:
|
||||
card->MODLsrh = value->iValue;
|
||||
card->MODLsrhGiven = TRUE;
|
||||
break;
|
||||
case MODL_CONCTAU:
|
||||
card->MODLconcDepLifetime = value->iValue;
|
||||
card->MODLconcDepLifetimeGiven = TRUE;
|
||||
break;
|
||||
case MODL_AUGER:
|
||||
card->MODLauger = value->iValue;
|
||||
card->MODLaugerGiven = TRUE;
|
||||
break;
|
||||
case MODL_AVAL:
|
||||
card->MODLavalancheGen = value->iValue;
|
||||
card->MODLavalancheGenGiven = TRUE;
|
||||
break;
|
||||
default:
|
||||
return(E_BADPARM);
|
||||
break;
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
/**********
|
||||
Copyright 1992 Regents of the University of California. All rights reserved.
|
||||
Author: 1992 David A. Gates, U. C. Berkeley CAD Group
|
||||
Modified: 2001 Paolo Nenzi
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "cktdefs.h"
|
||||
#include "modldefs.h"
|
||||
#include "sperror.h"
|
||||
#include "suffix.h"
|
||||
|
||||
extern int MODLcheck( MODLcard * );
|
||||
extern int MODLsetup( MODLcard * );
|
||||
|
||||
/*
|
||||
* Name: MODLcheck
|
||||
* Purpose: checks a list of MODLcards for input errors, sets defaults
|
||||
* Formals: cardList: the list to check
|
||||
* Returns: OK/E_PRIVATE
|
||||
* Users: numerical device setup routines
|
||||
* Calls: error message handler
|
||||
*/
|
||||
int
|
||||
MODLcheck(MODLcard *cardList)
|
||||
{
|
||||
MODLcard *card, *card2;
|
||||
int cardNum = 0, cardNum2;
|
||||
int error = OK;
|
||||
char ebuf[512]; /* error message buffer */
|
||||
|
||||
for ( card = cardList; card != NIL(MODLcard); card = card->MODLnextCard ) {
|
||||
cardNum++;
|
||||
|
||||
if ( !card->MODLbandGapNarrowingGiven ) {
|
||||
card->MODLbandGapNarrowing = FALSE;
|
||||
}
|
||||
if ( !card->MODLtempDepMobilityGiven ) {
|
||||
card->MODLtempDepMobility = FALSE;
|
||||
}
|
||||
if ( !card->MODLconcDepMobilityGiven ) {
|
||||
card->MODLconcDepMobility = FALSE;
|
||||
}
|
||||
if ( !card->MODLfieldDepMobilityGiven ) {
|
||||
card->MODLfieldDepMobility = FALSE;
|
||||
}
|
||||
if ( !card->MODLtransDepMobilityGiven ) {
|
||||
card->MODLtransDepMobility = FALSE;
|
||||
}
|
||||
if ( !card->MODLsurfaceMobilityGiven ) {
|
||||
card->MODLsurfaceMobility = FALSE;
|
||||
}
|
||||
if ( !card->MODLmatchingMobilityGiven ) {
|
||||
card->MODLmatchingMobility = FALSE;
|
||||
}
|
||||
if ( !card->MODLsrhGiven ) {
|
||||
card->MODLsrh = FALSE;
|
||||
}
|
||||
if ( !card->MODLconcDepLifetimeGiven ) {
|
||||
card->MODLconcDepLifetime = FALSE;
|
||||
}
|
||||
if ( !card->MODLaugerGiven ) {
|
||||
card->MODLauger = FALSE;
|
||||
}
|
||||
if ( !card->MODLavalancheGenGiven ) {
|
||||
card->MODLavalancheGen = FALSE;
|
||||
}
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Name: MODLsetup
|
||||
* Purpose: setup the physical models used
|
||||
* Formals: cardList: list of cards to setup
|
||||
* Returns: OK/E_PRIVATE
|
||||
* Users: numerical devices
|
||||
* Calls: MODLcheck
|
||||
*/
|
||||
int
|
||||
MODLsetup(MODLcard *cardList)
|
||||
{
|
||||
int error;
|
||||
|
||||
/* Check the card list */
|
||||
if ((error = MODLcheck( cardList ))) return( error );
|
||||
|
||||
/* Nothing else to do. */
|
||||
return( OK );
|
||||
}
|
||||
|
|
@ -0,0 +1,163 @@
|
|||
/**********
|
||||
Copyright 1991 Regents of the University of California. All rights reserved.
|
||||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group
|
||||
Modified: 2001 Paolo Nenzi
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numcards.h"
|
||||
#include "numgen.h"
|
||||
#include "optndefs.h"
|
||||
#include "devdefs.h"
|
||||
#include "sperror.h"
|
||||
#include "suffix.h"
|
||||
|
||||
#define M_TO_CM 1.0e2
|
||||
#define M2_TO_CM2 (M_TO_CM * M_TO_CM)
|
||||
#define UM_TO_CM 1.0e-4
|
||||
#define UM2_TO_CM2 (UM_TO_CM * UM_TO_CM)
|
||||
|
||||
extern int OPTNnewCard(void**,void*);
|
||||
extern int OPTNparam(int,IFvalue*,void*);
|
||||
|
||||
|
||||
|
||||
IFparm OPTNpTable[] = {
|
||||
/* Supported Types of Devices. Ideally should be automatically extracted */
|
||||
IP("resistor",OPTN_RESISTOR, IF_FLAG, "Resistor"),
|
||||
IP("capacitor",OPTN_CAPACITOR, IF_FLAG, "Capacitor"),
|
||||
IP("diode", OPTN_DIODE, IF_FLAG, "Diode"),
|
||||
IP("bipolar", OPTN_BIPOLAR, IF_FLAG, "Bipolar Transistor"),
|
||||
IP("bjt", OPTN_BIPOLAR, IF_FLAG, "Bipolar Transistor"),
|
||||
IP("soibjt", OPTN_SOIBJT, IF_FLAG, "SOI Bipolar"),
|
||||
IP("moscap", OPTN_MOSCAP, IF_FLAG, "MOS Capacitor"),
|
||||
IP("mosfet", OPTN_MOSFET, IF_FLAG, "MOSFET"),
|
||||
IP("soimos", OPTN_SOIMOS, IF_FLAG, "SOI MOSFET"),
|
||||
IP("jfet", OPTN_JFET, IF_FLAG, "Junction FET"),
|
||||
IP("mesfet", OPTN_MESFET, IF_FLAG, "MESFET"),
|
||||
/* Various layout dimensions */
|
||||
IP("defa", OPTN_DEFA, IF_REAL, "Default Mask Area"),
|
||||
IP("defw", OPTN_DEFW, IF_REAL, "Default Mask Width"),
|
||||
IP("defl", OPTN_DEFL, IF_REAL, "Default Mask Length"),
|
||||
IP("base.area",OPTN_BASE_AREA, IF_REAL, "1D BJT Base Area"),
|
||||
IP("base.length",OPTN_BASE_LENGTH,IF_REAL, "1D BJT Base Length"),
|
||||
IP("base.depth",OPTN_BASE_DEPTH, IF_REAL, "1D BJT Base Depth"),
|
||||
/* Values */
|
||||
IP("tnom", OPTN_TNOM, IF_REAL, "Nominal Temperature"),
|
||||
/* Device Initial Condition File */
|
||||
IP("ic.file", OPTN_IC_FILE, IF_STRING, "Initial condition file"),
|
||||
IP("unique", OPTN_UNIQUE, IF_FLAG, "Generate unique filename")
|
||||
};
|
||||
|
||||
IFcardInfo OPTNinfo = {
|
||||
"options",
|
||||
"Provide optional information and hints",
|
||||
NUMELEMS(OPTNpTable),
|
||||
OPTNpTable,
|
||||
|
||||
OPTNnewCard,
|
||||
OPTNparam,
|
||||
NULL
|
||||
};
|
||||
|
||||
int
|
||||
OPTNnewCard(void **inCard, void *inModel)
|
||||
{
|
||||
OPTNcard *tmpCard, *newCard;
|
||||
GENnumModel *model = (GENnumModel *)inModel;
|
||||
|
||||
tmpCard = model->GENoptions;
|
||||
if (!tmpCard) { /* First in list */
|
||||
newCard = NEW( OPTNcard );
|
||||
if (!newCard) {
|
||||
*inCard = (void *)NULL;
|
||||
return(E_NOMEM);
|
||||
}
|
||||
newCard->OPTNnextCard = (OPTNcard *)NULL;
|
||||
*inCard = (void *)newCard;
|
||||
model->GENoptions = newCard;
|
||||
} else { /* Only one card of this type allowed */
|
||||
*inCard = (void *)tmpCard;
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
||||
int
|
||||
OPTNparam(int param, IFvalue *value, void *inCard)
|
||||
{
|
||||
OPTNcard *card = (OPTNcard *)inCard;
|
||||
|
||||
switch (param) {
|
||||
case OPTN_RESISTOR:
|
||||
card->OPTNdeviceType = OPTN_RESISTOR;
|
||||
card->OPTNdeviceTypeGiven = TRUE;
|
||||
break;
|
||||
case OPTN_CAPACITOR:
|
||||
card->OPTNdeviceType = OPTN_CAPACITOR;
|
||||
card->OPTNdeviceTypeGiven = TRUE;
|
||||
break;
|
||||
case OPTN_DIODE:
|
||||
card->OPTNdeviceType = OPTN_DIODE;
|
||||
card->OPTNdeviceTypeGiven = TRUE;
|
||||
break;
|
||||
case OPTN_MOSCAP:
|
||||
card->OPTNdeviceType = OPTN_MOSCAP;
|
||||
card->OPTNdeviceTypeGiven = TRUE;
|
||||
break;
|
||||
case OPTN_BIPOLAR:
|
||||
case OPTN_SOIBJT: /* XXX Treat SOI as normal */
|
||||
card->OPTNdeviceType = OPTN_BIPOLAR;
|
||||
card->OPTNdeviceTypeGiven = TRUE;
|
||||
break;
|
||||
case OPTN_MOSFET:
|
||||
case OPTN_SOIMOS: /* XXX Treat SOI as normal */
|
||||
card->OPTNdeviceType = OPTN_MOSFET;
|
||||
card->OPTNdeviceTypeGiven = TRUE;
|
||||
break;
|
||||
case OPTN_JFET:
|
||||
case OPTN_MESFET: /* XXX Treat MES as junction */
|
||||
card->OPTNdeviceType = OPTN_JFET;
|
||||
card->OPTNdeviceTypeGiven = TRUE;
|
||||
break;
|
||||
case OPTN_DEFA:
|
||||
card->OPTNdefa = value->rValue * M2_TO_CM2;
|
||||
card->OPTNdefaGiven = TRUE;
|
||||
break;
|
||||
case OPTN_DEFW:
|
||||
card->OPTNdefw = value->rValue * M_TO_CM;
|
||||
card->OPTNdefwGiven = TRUE;
|
||||
break;
|
||||
case OPTN_DEFL:
|
||||
card->OPTNdefl = value->rValue * M_TO_CM;
|
||||
card->OPTNdeflGiven = TRUE;
|
||||
break;
|
||||
case OPTN_BASE_AREA:
|
||||
card->OPTNbaseArea = value->rValue;
|
||||
card->OPTNbaseAreaGiven = TRUE;
|
||||
break;
|
||||
case OPTN_BASE_LENGTH:
|
||||
card->OPTNbaseLength = value->rValue * UM_TO_CM;
|
||||
card->OPTNbaseLengthGiven = TRUE;
|
||||
break;
|
||||
case OPTN_BASE_DEPTH:
|
||||
card->OPTNbaseDepth = value->rValue * UM_TO_CM;
|
||||
card->OPTNbaseDepthGiven = TRUE;
|
||||
break;
|
||||
case OPTN_TNOM:
|
||||
card->OPTNtnom = value->rValue;
|
||||
card->OPTNtnomGiven = TRUE;
|
||||
break;
|
||||
case OPTN_IC_FILE:
|
||||
card->OPTNicFile = value->sValue;
|
||||
card->OPTNicFileGiven = TRUE;
|
||||
break;
|
||||
case OPTN_UNIQUE:
|
||||
card->OPTNunique = value->iValue;
|
||||
card->OPTNuniqueGiven = TRUE;
|
||||
break;
|
||||
default:
|
||||
return(E_BADPARM);
|
||||
break;
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
/**********
|
||||
Copyright 1992 Regents of the University of California. All rights reserved.
|
||||
Author: 1992 David A. Gates, U. C. Berkeley CAD Group
|
||||
Modified: 2001 Paolo Nenzi
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "cktdefs.h"
|
||||
#include "numenum.h"
|
||||
#include "outpdefs.h"
|
||||
#include "sperror.h"
|
||||
#include "suffix.h"
|
||||
|
||||
extern int OUTPcheck( OUTPcard * );
|
||||
extern int OUTPsetup( OUTPcard * );
|
||||
|
||||
|
||||
/*
|
||||
* Name: OUTPcheck
|
||||
* Purpose: checks a list of OUTPcards for input errors, sets defaults
|
||||
* Formals: cardList: the list to check
|
||||
* Returns: OK/E_PRIVATE
|
||||
* Users: numerical device setup routines, output routines
|
||||
* Calls: error message handler
|
||||
*/
|
||||
int
|
||||
OUTPcheck(OUTPcard *cardList)
|
||||
{
|
||||
OUTPcard *card, *card2;
|
||||
int cardNum = 0, cardNum2;
|
||||
int error = OK;
|
||||
char ebuf[512]; /* error message buffer */
|
||||
|
||||
for ( card = cardList; card != NIL(OUTPcard); card = card->OUTPnextCard ) {
|
||||
cardNum++;
|
||||
|
||||
card->OUTPnumVars = -1;
|
||||
|
||||
if ( !card->OUTPdcDebugGiven ) {
|
||||
card->OUTPdcDebug = FALSE;
|
||||
}
|
||||
if ( !card->OUTPtranDebugGiven ) {
|
||||
card->OUTPtranDebug = FALSE;
|
||||
}
|
||||
if ( !card->OUTPacDebugGiven ) {
|
||||
card->OUTPacDebug = FALSE;
|
||||
}
|
||||
if ( !card->OUTPgeomGiven ) {
|
||||
card->OUTPgeom = FALSE;
|
||||
}
|
||||
if ( !card->OUTPmeshGiven ) {
|
||||
card->OUTPmesh = FALSE;
|
||||
}
|
||||
if ( !card->OUTPmaterialGiven ) {
|
||||
card->OUTPmaterial = FALSE;
|
||||
}
|
||||
if ( !card->OUTPglobalsGiven ) {
|
||||
card->OUTPglobals = FALSE;
|
||||
}
|
||||
if ( !card->OUTPstatsGiven ) {
|
||||
card->OUTPstats = TRUE;
|
||||
}
|
||||
if ( !card->OUTProotFileGiven ) {
|
||||
card->OUTProotFile = copy("");
|
||||
}
|
||||
if ( !card->OUTPfileTypeGiven ) {
|
||||
card->OUTPfileType = RAWFILE;
|
||||
}
|
||||
if ( !card->OUTPdopingGiven ) {
|
||||
card->OUTPdoping = TRUE;
|
||||
}
|
||||
if ( !card->OUTPpsiGiven ) {
|
||||
card->OUTPpsi = TRUE;
|
||||
}
|
||||
if ( !card->OUTPequPsiGiven ) {
|
||||
card->OUTPequPsi = FALSE;
|
||||
}
|
||||
if ( !card->OUTPvacPsiGiven ) {
|
||||
card->OUTPvacPsi = FALSE;
|
||||
}
|
||||
if ( !card->OUTPnConcGiven ) {
|
||||
card->OUTPnConc = TRUE;
|
||||
}
|
||||
if ( !card->OUTPpConcGiven ) {
|
||||
card->OUTPpConc = TRUE;
|
||||
}
|
||||
if ( !card->OUTPphinGiven ) {
|
||||
card->OUTPphin = FALSE;
|
||||
}
|
||||
if ( !card->OUTPphipGiven ) {
|
||||
card->OUTPphip = FALSE;
|
||||
}
|
||||
if ( !card->OUTPphicGiven ) {
|
||||
card->OUTPphic = FALSE;
|
||||
}
|
||||
if ( !card->OUTPphivGiven ) {
|
||||
card->OUTPphiv = FALSE;
|
||||
}
|
||||
if ( !card->OUTPeFieldGiven ) {
|
||||
card->OUTPeField = TRUE;
|
||||
}
|
||||
if ( !card->OUTPjcGiven ) {
|
||||
card->OUTPjc = FALSE;
|
||||
}
|
||||
if ( !card->OUTPjdGiven ) {
|
||||
card->OUTPjd = TRUE;
|
||||
}
|
||||
if ( !card->OUTPjnGiven ) {
|
||||
card->OUTPjn = TRUE;
|
||||
}
|
||||
if ( !card->OUTPjpGiven ) {
|
||||
card->OUTPjp = TRUE;
|
||||
}
|
||||
if ( !card->OUTPjtGiven ) {
|
||||
card->OUTPjt = FALSE;
|
||||
}
|
||||
if ( !card->OUTPuNetGiven ) {
|
||||
card->OUTPuNet = FALSE;
|
||||
}
|
||||
if ( !card->OUTPmunGiven ) {
|
||||
card->OUTPmun = FALSE;
|
||||
}
|
||||
if ( !card->OUTPmupGiven ) {
|
||||
card->OUTPmup = FALSE;
|
||||
}
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Name: OUTPsetup
|
||||
* Purpose: setup the output card
|
||||
* Formals: cardList: list of cards to setup
|
||||
* Returns: OK/E_PRIVATE
|
||||
* Users: numerical devices
|
||||
* Calls: OUTPcheck
|
||||
*/
|
||||
int
|
||||
OUTPsetup(OUTPcard *cardList)
|
||||
{
|
||||
int error;
|
||||
|
||||
/* Check the card list */
|
||||
if ((error = OUTPcheck( cardList ))) return( error );
|
||||
|
||||
return( OK );
|
||||
}
|
||||
|
|
@ -0,0 +1,241 @@
|
|||
/**********
|
||||
Copyright 1992 Regents of the University of California. All rights reserved.
|
||||
Author: 1992 David A. Gates, U. C. Berkeley CAD Group
|
||||
Modified: 2001 Paolo Nenzi
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numcards.h"
|
||||
#include "numgen.h"
|
||||
#include "numenum.h"
|
||||
#include "outpdefs.h"
|
||||
#include "devdefs.h"
|
||||
#include "sperror.h"
|
||||
#include "suffix.h"
|
||||
|
||||
extern int OUTPnewCard(void**,void*);
|
||||
extern int OUTPparam(int,IFvalue*,void*);
|
||||
|
||||
|
||||
IFparm OUTPpTable[] = {
|
||||
/* Debugging Flags */
|
||||
IP("all.debug",OUTP_ALL_DEBUG,IF_FLAG, "Debug All Analyses"),
|
||||
IP("op.debug",OUTP_DC_DEBUG, IF_FLAG, "Debug DC/OP Analyses"),
|
||||
IP("dc.debug",OUTP_DC_DEBUG, IF_FLAG, "Debug DC/OP Analyses"),
|
||||
IP("tran.debug",OUTP_TRAN_DEBUG,IF_FLAG, "Debug TRAN Analysis"),
|
||||
IP("ac.debug",OUTP_AC_DEBUG, IF_FLAG, "Debug AC/PZ Analyses"),
|
||||
IP("pz.debug",OUTP_AC_DEBUG, IF_FLAG, "Debug AC/PZ Analyses"),
|
||||
/* General Information */
|
||||
IP("geometry",OUTP_GEOM, IF_FLAG, "Geometric information"),
|
||||
IP("mesh", OUTP_MESH, IF_FLAG, "Mesh information"),
|
||||
IP("material",OUTP_MATERIAL, IF_FLAG, "Material information"),
|
||||
IP("globals", OUTP_GLOBALS, IF_FLAG, "Global information"),
|
||||
IP("statistics", OUTP_STATS, IF_FLAG, "Resource usage information"),
|
||||
IP("resources", OUTP_STATS, IF_FLAG, "Resource usage information"),
|
||||
/* Solution Information */
|
||||
IP("rootfile", OUTP_ROOTFILE, IF_STRING, "Root of output file names"),
|
||||
IP("rawfile", OUTP_RAWFILE, IF_FLAG, "SPICE rawfile data format"),
|
||||
IP("hdf", OUTP_HDF, IF_FLAG, "HDF data format"),
|
||||
IP("doping", OUTP_DOPING, IF_FLAG, "Net doping"),
|
||||
IP("psi", OUTP_PSI, IF_FLAG, "Potential"),
|
||||
IP("equ.psi", OUTP_EQU_PSI, IF_FLAG, "Equilibrium potential"),
|
||||
IP("vac.psi", OUTP_VAC_PSI, IF_FLAG, "Vacuum potential"),
|
||||
IP("n.conc", OUTP_N_CONC, IF_FLAG, "Electron concentration"),
|
||||
IP("electrons", OUTP_N_CONC, IF_FLAG, "Electron concentration"),
|
||||
IP("p.conc", OUTP_P_CONC, IF_FLAG, "Hole concentration"),
|
||||
IP("holes", OUTP_P_CONC, IF_FLAG, "Hole concentration"),
|
||||
IP("phin", OUTP_PHIN, IF_FLAG, "Electron quasi-fermi potential"),
|
||||
IP("qfn", OUTP_PHIN, IF_FLAG, "Electron quasi-fermi potential"),
|
||||
IP("phip", OUTP_PHIP, IF_FLAG, "Hole quasi-fermi potential"),
|
||||
IP("qfp", OUTP_PHIP, IF_FLAG, "Hole quasi-fermi potential"),
|
||||
IP("phic", OUTP_PHIC, IF_FLAG, "Conduction band potential"),
|
||||
IP("band.con",OUTP_PHIC, IF_FLAG, "Conduction band potential"),
|
||||
IP("phiv", OUTP_PHIV, IF_FLAG, "Valence band potential"),
|
||||
IP("band.val",OUTP_PHIV, IF_FLAG, "Valence band potential"),
|
||||
IP("e.field", OUTP_E_FIELD, IF_FLAG, "Electric field"),
|
||||
IP("jc", OUTP_J_C, IF_FLAG, "Conduction current density"),
|
||||
IP("j.conduc",OUTP_J_C, IF_FLAG, "Conduction current density"),
|
||||
IP("jd", OUTP_J_D, IF_FLAG, "Displacement current density"),
|
||||
IP("j.disp", OUTP_J_D, IF_FLAG, "Displacement current density"),
|
||||
IP("jn", OUTP_J_N, IF_FLAG, "Electron current density"),
|
||||
IP("j.electr",OUTP_J_N, IF_FLAG, "Electron current density"),
|
||||
IP("jp", OUTP_J_P, IF_FLAG, "Hole current density"),
|
||||
IP("j.hole", OUTP_J_P, IF_FLAG, "Hole current density"),
|
||||
IP("jt", OUTP_J_T, IF_FLAG, "Total current density"),
|
||||
IP("j.total", OUTP_J_T, IF_FLAG, "Total current density"),
|
||||
IP("unet", OUTP_U_NET, IF_FLAG, "Net recombination"),
|
||||
IP("recomb", OUTP_U_NET, IF_FLAG, "Net recombination"),
|
||||
IP("mun", OUTP_MUN, IF_FLAG, "Elctron mobility"),
|
||||
IP("mob.elec",OUTP_MUN, IF_FLAG, "Electron mobility"),
|
||||
IP("mup", OUTP_MUP, IF_FLAG, "Hole mobility"),
|
||||
IP("mob.hole",OUTP_MUP, IF_FLAG, "Hole mobility")
|
||||
};
|
||||
|
||||
IFcardInfo OUTPinfo = {
|
||||
"output",
|
||||
"Identify information to be output to user",
|
||||
NUMELEMS(OUTPpTable),
|
||||
OUTPpTable,
|
||||
|
||||
OUTPnewCard,
|
||||
OUTPparam,
|
||||
NULL
|
||||
};
|
||||
|
||||
int
|
||||
OUTPnewCard(void **inCard, void *inModel)
|
||||
{
|
||||
OUTPcard *tmpCard, *newCard;
|
||||
GENnumModel *model = (GENnumModel *)inModel;
|
||||
|
||||
tmpCard = model->GENoutputs;
|
||||
if (!tmpCard) { /* First in list */
|
||||
newCard = NEW( OUTPcard );
|
||||
if (!newCard) {
|
||||
*inCard = (void *)NULL;
|
||||
return(E_NOMEM);
|
||||
}
|
||||
newCard->OUTPnextCard = (OUTPcard *)NULL;
|
||||
*inCard = (void *)newCard;
|
||||
model->GENoutputs = newCard;
|
||||
} else { /* Only one card of this type allowed */
|
||||
*inCard = (void *)tmpCard;
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
||||
int
|
||||
OUTPparam(int param, IFvalue *value, void *inCard)
|
||||
{
|
||||
OUTPcard *card = (OUTPcard *)inCard;
|
||||
|
||||
switch (param) {
|
||||
case OUTP_ALL_DEBUG:
|
||||
card->OUTPdcDebug = value->iValue;
|
||||
card->OUTPdcDebugGiven = TRUE;
|
||||
card->OUTPtranDebug = value->iValue;
|
||||
card->OUTPtranDebugGiven = TRUE;
|
||||
card->OUTPacDebug = value->iValue;
|
||||
card->OUTPacDebugGiven = TRUE;
|
||||
break;
|
||||
case OUTP_DC_DEBUG:
|
||||
card->OUTPdcDebug = value->iValue;
|
||||
card->OUTPdcDebugGiven = TRUE;
|
||||
break;
|
||||
case OUTP_TRAN_DEBUG:
|
||||
card->OUTPtranDebug = value->iValue;
|
||||
card->OUTPtranDebugGiven = TRUE;
|
||||
break;
|
||||
case OUTP_AC_DEBUG:
|
||||
card->OUTPacDebug = value->iValue;
|
||||
card->OUTPacDebugGiven = TRUE;
|
||||
break;
|
||||
case OUTP_GEOM:
|
||||
card->OUTPgeom = value->iValue;
|
||||
card->OUTPgeomGiven = TRUE;
|
||||
break;
|
||||
case OUTP_MESH:
|
||||
card->OUTPmesh = value->iValue;
|
||||
card->OUTPmeshGiven = TRUE;
|
||||
break;
|
||||
case OUTP_MATERIAL:
|
||||
card->OUTPmaterial = value->iValue;
|
||||
card->OUTPmaterialGiven = TRUE;
|
||||
break;
|
||||
case OUTP_GLOBALS:
|
||||
card->OUTPglobals = value->iValue;
|
||||
card->OUTPglobalsGiven = TRUE;
|
||||
break;
|
||||
case OUTP_STATS:
|
||||
card->OUTPstats = value->iValue;
|
||||
card->OUTPstatsGiven = TRUE;
|
||||
break;
|
||||
case OUTP_ROOTFILE:
|
||||
card->OUTProotFile = tilde_expand(value->sValue);
|
||||
card->OUTProotFileGiven = TRUE;
|
||||
break;
|
||||
case OUTP_RAWFILE:
|
||||
card->OUTPfileType = RAWFILE;
|
||||
card->OUTPfileTypeGiven = TRUE;
|
||||
break;
|
||||
case OUTP_HDF:
|
||||
return(E_UNSUPP);
|
||||
break;
|
||||
case OUTP_DOPING:
|
||||
card->OUTPdoping = value->iValue;
|
||||
card->OUTPdopingGiven = TRUE;
|
||||
break;
|
||||
case OUTP_PSI:
|
||||
card->OUTPpsi = value->iValue;
|
||||
card->OUTPpsiGiven = TRUE;
|
||||
break;
|
||||
case OUTP_EQU_PSI:
|
||||
card->OUTPequPsi = value->iValue;
|
||||
card->OUTPequPsiGiven = TRUE;
|
||||
break;
|
||||
case OUTP_VAC_PSI:
|
||||
card->OUTPvacPsi = value->iValue;
|
||||
card->OUTPvacPsiGiven = TRUE;
|
||||
break;
|
||||
case OUTP_N_CONC:
|
||||
card->OUTPnConc = value->iValue;
|
||||
card->OUTPnConcGiven = TRUE;
|
||||
break;
|
||||
case OUTP_P_CONC:
|
||||
card->OUTPpConc = value->iValue;
|
||||
card->OUTPpConcGiven = TRUE;
|
||||
break;
|
||||
case OUTP_PHIN:
|
||||
card->OUTPphin = value->iValue;
|
||||
card->OUTPphinGiven = TRUE;
|
||||
break;
|
||||
case OUTP_PHIP:
|
||||
card->OUTPphip = value->iValue;
|
||||
card->OUTPphipGiven = TRUE;
|
||||
break;
|
||||
case OUTP_PHIC:
|
||||
card->OUTPphic = value->iValue;
|
||||
card->OUTPphicGiven = TRUE;
|
||||
break;
|
||||
case OUTP_PHIV:
|
||||
card->OUTPphiv = value->iValue;
|
||||
card->OUTPphivGiven = TRUE;
|
||||
break;
|
||||
case OUTP_J_C:
|
||||
card->OUTPjc = value->iValue;
|
||||
card->OUTPjcGiven = TRUE;
|
||||
break;
|
||||
case OUTP_J_D:
|
||||
card->OUTPjd = value->iValue;
|
||||
card->OUTPjdGiven = TRUE;
|
||||
break;
|
||||
case OUTP_J_N:
|
||||
card->OUTPjn = value->iValue;
|
||||
card->OUTPjnGiven = TRUE;
|
||||
break;
|
||||
case OUTP_J_P:
|
||||
card->OUTPjp = value->iValue;
|
||||
card->OUTPjpGiven = TRUE;
|
||||
break;
|
||||
case OUTP_J_T:
|
||||
card->OUTPjt = value->iValue;
|
||||
card->OUTPjtGiven = TRUE;
|
||||
break;
|
||||
case OUTP_U_NET:
|
||||
card->OUTPuNet = value->iValue;
|
||||
card->OUTPuNetGiven = TRUE;
|
||||
break;
|
||||
case OUTP_MUN:
|
||||
card->OUTPmun = value->iValue;
|
||||
card->OUTPmunGiven = TRUE;
|
||||
break;
|
||||
case OUTP_MUP:
|
||||
card->OUTPmup = value->iValue;
|
||||
card->OUTPmupGiven = TRUE;
|
||||
break;
|
||||
default:
|
||||
return(E_BADPARM);
|
||||
break;
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
Directory: input
|
||||
----------------
|
||||
The files in this directory serve two purposes:
|
||||
1. Serve as collection point for numerical model parameters.
|
||||
2. Translate data structures containing raw input into
|
||||
a form usable by the one- and two-dimensional device simulators.
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
1. Crucial Difference between Spice3f1 and Spice3e2:
|
||||
The 'states' field in the ***instance structured has been moved.
|
||||
Belongs below the node id's in Spice3e2.
|
||||
2. Crucial Differences between Spice3f2 and Spice3f1:
|
||||
Addition of DEVunSetup call to ***itf.h files
|
||||
The 3f2 input parser can't handle arithmetic operators in strings.
|
||||
A special call has been added in spiceitf/inpgval.c to specially
|
||||
parse strings. Quotes (either "foobar" or 'foobar') are stripped
|
||||
from the string.
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
## Process this file with automake to produce Makefile.in
|
||||
|
||||
noinst_LIBRARIES = libcideroned.a
|
||||
|
||||
libcideroned_a_SOURCES = \
|
||||
oneadmit.c \
|
||||
oneaval.c \
|
||||
onecond.c \
|
||||
onecont.c \
|
||||
onedest.c \
|
||||
onedopng.c \
|
||||
onefreez.c \
|
||||
onemesh.c \
|
||||
onepoiss.c \
|
||||
oneprint.c \
|
||||
oneproj.c \
|
||||
oneread.c \
|
||||
onesetup.c \
|
||||
onesolve.c
|
||||
|
||||
EXTRA_DIST = notes readme
|
||||
INCLUDES = -I$(top_srcdir)/src/include
|
||||
MAINTAINERCLEANFILES = Makefile.in
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
Some comments on keeping track of where the solution is:
|
||||
|
||||
There are 3 places that the internal state variables can be hiding:
|
||||
1. In the (psi,n,p) triple of a node (pNode).
|
||||
2. In the solution vectors (dcSolution, copiedSolution).
|
||||
3. In the state tables (pDevice->devStates[]).
|
||||
|
||||
To access node variables use:
|
||||
pNode->psi
|
||||
pNode->nConc
|
||||
pNode->pConc
|
||||
|
||||
To access solution vectors use:
|
||||
solution[ pNode->psiEqn ]
|
||||
solution[ pNode->nEqn ]
|
||||
solution[ pNode->pEqn ]
|
||||
|
||||
To access state vector X (0,1,etc.) use:
|
||||
*(pDevice->devStateX + pNode->nodePsi)
|
||||
*(pDevice->devStateX + pNode->nodeN)
|
||||
*(pDevice->devStateX + pNode->nodeP)
|
||||
|
||||
There are several functions that copy from one form of these
|
||||
to another:
|
||||
storeInitialGuess: dcSolution <- nodevars
|
||||
biasSolve: nodevars <- dcSolution (DC)
|
||||
state0 <- nodevars <- dcSolution (Transient)
|
||||
saveState: nodevars <- state1 (Transient)
|
||||
|
||||
In addition the main device-level Newton iteration copies during
|
||||
the computation of currents and derivatives:
|
||||
state0 <- dcSolution
|
||||
|
||||
and the function predict() does a forward prediction step using
|
||||
the previous state:
|
||||
nodevars <- predict(state1) (Transient)
|
||||
|
|
@ -0,0 +1,742 @@
|
|||
/**********
|
||||
Copyright 1992 Regents of the University of California. All rights reserved.
|
||||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group
|
||||
**********/
|
||||
|
||||
/* Functions to compute small-signal parameters of 1D devices */
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numglobs.h"
|
||||
#include "numenum.h"
|
||||
#include "numconst.h"
|
||||
#include "onedev.h"
|
||||
#include "onemesh.h"
|
||||
#include "complex.h"
|
||||
#include "spMatrix.h"
|
||||
#include "ifsim.h"
|
||||
|
||||
#include "onedext.h"
|
||||
#include "oneddefs.h"
|
||||
#include "cidersupt.h"
|
||||
|
||||
|
||||
extern IFfrontEnd *SPfrontEnd;
|
||||
|
||||
|
||||
/*
|
||||
* mmhhh this may cause troubles
|
||||
* Paolo Nenzi 2002
|
||||
*/
|
||||
SPcomplex yAc;
|
||||
|
||||
|
||||
/* Forward Declarations */
|
||||
SPcomplex *computeAdmittance(ONEnode *, BOOLEAN, double *,
|
||||
double *, SPcomplex *);
|
||||
BOOLEAN ONEsorSolve(ONEdevice *pDevice, double *xReal,
|
||||
double *xImag, double omega);
|
||||
|
||||
int
|
||||
NUMDadmittance(ONEdevice *pDevice, double omega, SPcomplex *yd)
|
||||
{
|
||||
ONEnode *pNode;
|
||||
ONEelem *pElem;
|
||||
ONEedge *pEdge;
|
||||
int index, i;
|
||||
double yReal, yImag;
|
||||
double *solutionReal, *solutionImag;
|
||||
SPcomplex yAc, cOmega;
|
||||
SPcomplex *y;
|
||||
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.
|
||||
*/
|
||||
solutionReal = pDevice->dcDeltaSolution;
|
||||
solutionImag = pDevice->copiedSolution;
|
||||
pDevice->solverType = SLV_SMSIG;
|
||||
|
||||
/* use a normalized radian frequency */
|
||||
omega *= TNorm;
|
||||
CMPLX_ASSIGN_VALUE(cOmega, 0.0, omega);
|
||||
yReal = 0.0;
|
||||
yImag = 0.0;
|
||||
|
||||
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++) {
|
||||
pDevice->rhs[index] = 0.0;
|
||||
pDevice->rhsImag[index] = 0.0;
|
||||
}
|
||||
/* store the new rhs vector */
|
||||
pElem = pDevice->elemArray[pDevice->numNodes - 1];
|
||||
pNode = pElem->pLeftNode;
|
||||
pDevice->rhs[pNode->psiEqn] = pElem->epsRel * pElem->rDx;
|
||||
if (pElem->elemType == SEMICON) {
|
||||
pEdge = pElem->pEdge;
|
||||
pDevice->rhs[pNode->nEqn] -= pEdge->dJnDpsiP1;
|
||||
pDevice->rhs[pNode->pEqn] -= pEdge->dJpDpsiP1;
|
||||
}
|
||||
pDevice->pStats->loadTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
||||
|
||||
/* SOLVE */
|
||||
startTime = SPfrontEnd->IFseconds();
|
||||
SORFailed = ONEsorSolve(pDevice, solutionReal, solutionImag, 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();
|
||||
/* solve the system of equations directly */
|
||||
for (index = 1; index <= pDevice->numEqns; index++) {
|
||||
pDevice->rhs[index] = 0.0;
|
||||
pDevice->rhsImag[index] = 0.0;
|
||||
}
|
||||
pElem = pDevice->elemArray[pDevice->numNodes - 1];
|
||||
pNode = pElem->pLeftNode;
|
||||
pDevice->rhs[pNode->psiEqn] = pElem->epsRel * pElem->rDx;
|
||||
if (pElem->elemType == SEMICON) {
|
||||
pEdge = pElem->pEdge;
|
||||
pDevice->rhs[pNode->nEqn] -= pEdge->dJnDpsiP1;
|
||||
pDevice->rhs[pNode->pEqn] -= pEdge->dJpDpsiP1;
|
||||
}
|
||||
ONE_jacLoad(pDevice);
|
||||
spSetComplex(pDevice->matrix);
|
||||
for (index = 1; index < pDevice->numNodes; index++) {
|
||||
pElem = pDevice->elemArray[index];
|
||||
if (pElem->elemType == SEMICON) {
|
||||
for (i = 0; i <= 1; i++) {
|
||||
pNode = pElem->pNodes[i];
|
||||
if (pNode->nodeType != CONTACT) {
|
||||
spADD_COMPLEX_ELEMENT(pNode->fNN, 0.0, -0.5 * pElem->dx * omega);
|
||||
spADD_COMPLEX_ELEMENT(pNode->fPP, 0.0, 0.5 * pElem->dx * 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, pDevice->rhs, solutionReal,
|
||||
pDevice->rhsImag, solutionImag);
|
||||
pDevice->pStats->solveTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
||||
}
|
||||
/* MISC */
|
||||
startTime = SPfrontEnd->IFseconds();
|
||||
pNode = pDevice->elemArray[1]->pLeftNode;
|
||||
y = computeAdmittance(pNode, FALSE, solutionReal, solutionImag, &cOmega);
|
||||
CMPLX_ASSIGN_VALUE(yAc, -y->real, -y->imag);
|
||||
CMPLX_ASSIGN(*yd, yAc);
|
||||
CMPLX_MULT_SELF_SCALAR(*yd, GNorm * pDevice->area);
|
||||
pDevice->pStats->miscTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
||||
|
||||
return (AcAnalysisMethod);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
NBJTadmittance(ONEdevice *pDevice, double omega, SPcomplex *yIeVce,
|
||||
SPcomplex *yIcVce, SPcomplex *yIeVbe, SPcomplex *yIcVbe)
|
||||
{
|
||||
ONEelem *pCollElem = pDevice->elemArray[pDevice->numNodes - 1];
|
||||
ONEelem *pBaseElem = pDevice->elemArray[pDevice->baseIndex - 1];
|
||||
ONEelem *pElem;
|
||||
ONEedge *pEdge;
|
||||
ONEnode *pNode;
|
||||
int index, i;
|
||||
double area = pDevice->area;
|
||||
double *solutionReal, *solutionImag;
|
||||
BOOLEAN SORFailed;
|
||||
SPcomplex *y;
|
||||
SPcomplex cOmega, pIeVce, pIcVce, pIeVbe, pIcVbe;
|
||||
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
|
||||
*/
|
||||
solutionReal = pDevice->dcDeltaSolution;
|
||||
solutionImag = pDevice->copiedSolution;
|
||||
pDevice->solverType = SLV_SMSIG;
|
||||
|
||||
/* 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++) {
|
||||
pDevice->rhs[index] = 0.0;
|
||||
pDevice->rhsImag[index] = 0.0;
|
||||
}
|
||||
/* store the new rhs vector */
|
||||
pNode = pCollElem->pLeftNode;
|
||||
pDevice->rhs[pNode->psiEqn] = pCollElem->epsRel * pCollElem->rDx;
|
||||
if (pCollElem->elemType == SEMICON) {
|
||||
pEdge = pCollElem->pEdge;
|
||||
pDevice->rhs[pNode->nEqn] -= pEdge->dJnDpsiP1;
|
||||
pDevice->rhs[pNode->pEqn] -= pEdge->dJpDpsiP1;
|
||||
}
|
||||
pDevice->pStats->loadTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
||||
|
||||
/* SOLVE */
|
||||
startTime = SPfrontEnd->IFseconds();
|
||||
SORFailed = ONEsorSolve(pDevice, solutionReal, solutionImag, 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();
|
||||
pElem = pDevice->elemArray[1];
|
||||
pNode = pElem->pLeftNode;
|
||||
y = computeAdmittance(pNode, FALSE, solutionReal, solutionImag, &cOmega);
|
||||
CMPLX_ASSIGN_VALUE(pIeVce, -y->real, -y->imag);
|
||||
pNode = pCollElem->pRightNode;
|
||||
y = computeAdmittance(pNode, TRUE, solutionReal, solutionImag, &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++) {
|
||||
pDevice->rhs[index] = 0.0;
|
||||
}
|
||||
pNode = pBaseElem->pRightNode;
|
||||
if (pNode->baseType == N_TYPE) {
|
||||
pDevice->rhs[pNode->nEqn] = pNode->nConc * pNode->eg;
|
||||
} else if (pNode->baseType == P_TYPE) {
|
||||
pDevice->rhs[pNode->pEqn] = pNode->pConc * pNode->eg;
|
||||
} else {
|
||||
printf("projectBJTsolution: unknown base type\n");
|
||||
}
|
||||
pDevice->pStats->loadTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
||||
|
||||
/* SOLVE */
|
||||
startTime = SPfrontEnd->IFseconds();
|
||||
SORFailed = ONEsorSolve(pDevice, solutionReal, solutionImag, 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++) {
|
||||
pDevice->rhs[index] = 0.0;
|
||||
pDevice->rhsImag[index] = 0.0;
|
||||
}
|
||||
/* solve the system of equations directly */
|
||||
ONE_jacLoad(pDevice);
|
||||
pNode = pCollElem->pLeftNode;
|
||||
pDevice->rhs[pNode->psiEqn] = pCollElem->epsRel * pCollElem->rDx;
|
||||
if (pCollElem->elemType == SEMICON) {
|
||||
pEdge = pCollElem->pEdge;
|
||||
pDevice->rhs[pNode->nEqn] -= pEdge->dJnDpsiP1;
|
||||
pDevice->rhs[pNode->pEqn] -= pEdge->dJpDpsiP1;
|
||||
}
|
||||
spSetComplex(pDevice->matrix);
|
||||
for (index = 1; index < pDevice->numNodes; index++) {
|
||||
pElem = pDevice->elemArray[index];
|
||||
if (pElem->elemType == SEMICON) {
|
||||
for (i = 0; i <= 1; i++) {
|
||||
pNode = pElem->pNodes[i];
|
||||
if (pNode->nodeType != CONTACT) {
|
||||
spADD_COMPLEX_ELEMENT(pNode->fNN, 0.0, -0.5 * pElem->dx * omega);
|
||||
spADD_COMPLEX_ELEMENT(pNode->fPP, 0.0, 0.5 * pElem->dx * 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, pDevice->rhs, solutionReal,
|
||||
pDevice->rhsImag, solutionImag);
|
||||
pDevice->pStats->solveTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
||||
|
||||
/* MISC */
|
||||
startTime = SPfrontEnd->IFseconds();
|
||||
pElem = pDevice->elemArray[1];
|
||||
pNode = pElem->pLeftNode;
|
||||
y = computeAdmittance(pNode, FALSE, solutionReal, solutionImag, &cOmega);
|
||||
CMPLX_ASSIGN_VALUE(pIeVce, -y->real, -y->imag);
|
||||
pNode = pCollElem->pRightNode;
|
||||
y = computeAdmittance(pNode, TRUE, solutionReal, solutionImag, &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 in the rhs */
|
||||
for (index = 1; index <= pDevice->numEqns; index++) {
|
||||
pDevice->rhs[index] = 0.0;
|
||||
}
|
||||
pNode = pBaseElem->pRightNode;
|
||||
if (pNode->baseType == N_TYPE) {
|
||||
pDevice->rhs[pNode->nEqn] = pNode->nConc * pNode->eg;
|
||||
} else if (pNode->baseType == P_TYPE) {
|
||||
pDevice->rhs[pNode->pEqn] = pNode->pConc * pNode->eg;
|
||||
} else {
|
||||
printf("\n BJTadmittance: unknown base type");
|
||||
}
|
||||
pDevice->pStats->loadTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
||||
|
||||
/* FACTOR: already done, no need to repeat. */
|
||||
|
||||
/* SOLVE */
|
||||
startTime = SPfrontEnd->IFseconds();
|
||||
spSolve(pDevice->matrix, pDevice->rhs, solutionReal,
|
||||
pDevice->rhsImag, solutionImag);
|
||||
pDevice->pStats->solveTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
||||
}
|
||||
/* MISC */
|
||||
startTime = SPfrontEnd->IFseconds();
|
||||
pElem = pDevice->elemArray[1];
|
||||
pNode = pElem->pLeftNode;
|
||||
y = computeAdmittance(pNode, FALSE, solutionReal, solutionImag, &cOmega);
|
||||
CMPLX_ASSIGN_VALUE(pIeVbe, -y->real, -y->imag);
|
||||
pNode = pCollElem->pRightNode;
|
||||
y = computeAdmittance(pNode, FALSE, solutionReal, solutionImag, &cOmega);
|
||||
CMPLX_ASSIGN_VALUE(pIcVbe, -y->real, -y->imag);
|
||||
|
||||
CMPLX_ASSIGN(*yIeVce, pIeVce);
|
||||
CMPLX_ASSIGN(*yIcVce, pIcVce);
|
||||
CMPLX_ASSIGN(*yIeVbe, pIeVbe);
|
||||
CMPLX_ASSIGN(*yIcVbe, pIcVbe);
|
||||
CMPLX_MULT_SELF_SCALAR(*yIeVce, GNorm * area);
|
||||
CMPLX_MULT_SELF_SCALAR(*yIeVbe, GNorm * area);
|
||||
CMPLX_MULT_SELF_SCALAR(*yIcVce, GNorm * area);
|
||||
CMPLX_MULT_SELF_SCALAR(*yIcVbe, GNorm * area);
|
||||
pDevice->pStats->miscTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime;
|
||||
|
||||
return (AcAnalysisMethod);
|
||||
}
|
||||
|
||||
BOOLEAN
|
||||
ONEsorSolve(ONEdevice *pDevice, double *xReal, double *xImag, double omega)
|
||||
{
|
||||
ONEnode *pNode;
|
||||
ONEelem *pElem;
|
||||
double wRelax = 1.0; /* SOR relaxation parameter */
|
||||
double *rhsSOR = pDevice->rhsImag;
|
||||
int numEqns = pDevice->numEqns;
|
||||
int numNodes = pDevice->numNodes;
|
||||
BOOLEAN SORConverged = FALSE;
|
||||
BOOLEAN SORFailed = FALSE;
|
||||
int i, index, indexN, indexP, iterationNum;
|
||||
double dx;
|
||||
|
||||
|
||||
/* clear xReal, xImag arrays */
|
||||
for (index = 1; index <= numEqns; index++) {
|
||||
xReal[index] = 0.0;
|
||||
xImag[index] = 0.0;
|
||||
}
|
||||
|
||||
iterationNum = 1;
|
||||
for (; (!SORConverged) &&(!SORFailed); iterationNum++) {
|
||||
/* first setup the rhs for the real part */
|
||||
for (index = 1; index <= numEqns; index++) {
|
||||
rhsSOR[index] = 0.0;
|
||||
}
|
||||
for (index = 1; index < numNodes; index++) {
|
||||
pElem = pDevice->elemArray[index];
|
||||
dx = 0.5 * pElem->dx;
|
||||
for (i = 0; i <= 1; i++) {
|
||||
pNode = pElem->pNodes[i];
|
||||
if ((pNode->nodeType != CONTACT)
|
||||
&& (pElem->elemType == SEMICON)) {
|
||||
indexN = pNode->nEqn;
|
||||
indexP = pNode->pEqn;
|
||||
rhsSOR[indexN] -= dx * omega * xImag[indexN];
|
||||
rhsSOR[indexP] += dx * omega * xImag[indexP];
|
||||
}
|
||||
}
|
||||
}
|
||||
/* now setup the rhs for the SOR equations */
|
||||
for (index = 1; index <= numEqns; index++) {
|
||||
rhsSOR[index] += pDevice->rhs[index];
|
||||
}
|
||||
/* compute xReal(k+1). solution stored in rhsSOR */
|
||||
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 (index = 1; index < numNodes; index++) {
|
||||
pElem = pDevice->elemArray[index];
|
||||
dx = 0.5 * pElem->dx;
|
||||
for (i = 0; i <= 1; i++) {
|
||||
pNode = pElem->pNodes[i];
|
||||
if ((pNode->nodeType != CONTACT)
|
||||
&& (pElem->elemType == SEMICON)) {
|
||||
indexN = pNode->nEqn;
|
||||
indexP = pNode->pEqn;
|
||||
rhsSOR[indexN] += dx * omega * xReal[indexN];
|
||||
rhsSOR[indexP] -= dx * 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 (ONEacDebug)
|
||||
printf("SOR iteration number = %d\n", iterationNum);
|
||||
if (iterationNum > 4) {
|
||||
SORFailed = TRUE;
|
||||
}
|
||||
}
|
||||
return (SORFailed);
|
||||
}
|
||||
|
||||
void
|
||||
NUMDys(ONEdevice *pDevice, SPcomplex *s, SPcomplex *yd)
|
||||
{
|
||||
ONEnode *pNode;
|
||||
ONEelem *pElem;
|
||||
ONEedge *pEdge;
|
||||
int index, i;
|
||||
double *solutionReal, *solutionImag;
|
||||
SPcomplex temp, cOmega;
|
||||
SPcomplex *y;
|
||||
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
solutionReal = pDevice->dcDeltaSolution;
|
||||
solutionImag = pDevice->copiedSolution;
|
||||
|
||||
/* use a normalized radian frequency */
|
||||
CMPLX_MULT_SCALAR(cOmega, *s, TNorm);
|
||||
|
||||
/* solve the system of equations directly */
|
||||
for (index = 1; index <= pDevice->numEqns; index++) {
|
||||
pDevice->rhs[index] = 0.0;
|
||||
pDevice->rhsImag[index] = 0.0;
|
||||
}
|
||||
ONE_jacLoad(pDevice);
|
||||
pElem = pDevice->elemArray[pDevice->numNodes - 1];
|
||||
pNode = pElem->pLeftNode;
|
||||
pDevice->rhs[pNode->psiEqn] = pElem->epsRel * pElem->rDx;
|
||||
if (pElem->elemType == SEMICON) {
|
||||
pEdge = pElem->pEdge;
|
||||
pDevice->rhs[pNode->nEqn] -= pEdge->dJnDpsiP1;
|
||||
pDevice->rhs[pNode->pEqn] -= pEdge->dJpDpsiP1;
|
||||
}
|
||||
spSetComplex(pDevice->matrix);
|
||||
for (index = 1; index < pDevice->numNodes; index++) {
|
||||
pElem = pDevice->elemArray[index];
|
||||
if (pElem->elemType == SEMICON) {
|
||||
for (i = 0; i <= 1; i++) {
|
||||
pNode = pElem->pNodes[i];
|
||||
if (pNode->nodeType != CONTACT) {
|
||||
CMPLX_MULT_SCALAR(temp, cOmega, 0.5 * pElem->dx);
|
||||
spADD_COMPLEX_ELEMENT(pNode->fNN, -temp.real, -temp.imag);
|
||||
spADD_COMPLEX_ELEMENT(pNode->fPP, temp.real, temp.imag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
spFactor(pDevice->matrix);
|
||||
spSolve(pDevice->matrix, pDevice->rhs, solutionReal,
|
||||
pDevice->rhsImag, solutionImag);
|
||||
|
||||
pElem = pDevice->elemArray[1];
|
||||
pNode = pElem->pLeftNode;
|
||||
y = computeAdmittance(pNode, FALSE, solutionReal, solutionImag, &cOmega);
|
||||
CMPLX_ASSIGN_VALUE(*yd, -y->real, -y->imag);
|
||||
CMPLX_MULT_SELF_SCALAR(*yd, GNorm * pDevice->area);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
NBJTys(ONEdevice *pDevice, SPcomplex *s, SPcomplex *yIeVce,
|
||||
SPcomplex *yIcVce, SPcomplex *yIeVbe, SPcomplex *yIcVbe)
|
||||
{
|
||||
ONEelem *pCollElem = pDevice->elemArray[pDevice->numNodes - 1];
|
||||
ONEelem *pBaseElem = pDevice->elemArray[pDevice->baseIndex - 1];
|
||||
ONEelem *pElem;
|
||||
ONEnode *pNode;
|
||||
ONEedge *pEdge;
|
||||
int index, i;
|
||||
SPcomplex *y;
|
||||
double area = pDevice->area;
|
||||
double *solutionReal, *solutionImag;
|
||||
SPcomplex temp, cOmega;
|
||||
SPcomplex pIeVce, pIcVce, pIeVbe, pIcVbe;
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
solutionReal = pDevice->dcDeltaSolution;
|
||||
solutionImag = pDevice->copiedSolution;
|
||||
|
||||
/* use a normalized radian frequency */
|
||||
CMPLX_MULT_SCALAR(cOmega, *s, TNorm);
|
||||
for (index = 1; index <= pDevice->numEqns; index++) {
|
||||
pDevice->rhs[index] = 0.0;
|
||||
pDevice->rhsImag[index] = 0.0;
|
||||
}
|
||||
/* solve the system of equations directly */
|
||||
ONE_jacLoad(pDevice);
|
||||
pNode = pCollElem->pLeftNode;
|
||||
pDevice->rhs[pNode->psiEqn] = pCollElem->epsRel * pCollElem->rDx;
|
||||
if (pCollElem->elemType == SEMICON) {
|
||||
pEdge = pCollElem->pEdge;
|
||||
pDevice->rhs[pNode->nEqn] -= pEdge->dJnDpsiP1;
|
||||
pDevice->rhs[pNode->pEqn] -= pEdge->dJpDpsiP1;
|
||||
}
|
||||
spSetComplex(pDevice->matrix);
|
||||
for (index = 1; index < pDevice->numNodes; index++) {
|
||||
pElem = pDevice->elemArray[index];
|
||||
if (pElem->elemType == SEMICON) {
|
||||
for (i = 0; i <= 1; i++) {
|
||||
pNode = pElem->pNodes[i];
|
||||
if (pNode->nodeType != CONTACT) {
|
||||
CMPLX_MULT_SCALAR(temp, cOmega, 0.5 * pElem->dx);
|
||||
spADD_COMPLEX_ELEMENT(pNode->fNN, -temp.real, -temp.imag);
|
||||
spADD_COMPLEX_ELEMENT(pNode->fPP, temp.real, temp.imag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
spFactor(pDevice->matrix);
|
||||
spSolve(pDevice->matrix, pDevice->rhs, solutionReal,
|
||||
pDevice->rhsImag, solutionImag);
|
||||
pElem = pDevice->elemArray[1];
|
||||
pNode = pElem->pLeftNode;
|
||||
y = computeAdmittance(pNode, FALSE, solutionReal, solutionImag, &cOmega);
|
||||
CMPLX_ASSIGN_VALUE(pIeVce, -y->real, -y->imag);
|
||||
pNode = pCollElem->pRightNode;
|
||||
y = computeAdmittance(pNode, TRUE, solutionReal, solutionImag, &cOmega);
|
||||
CMPLX_ASSIGN_VALUE(pIcVce, -y->real, -y->imag);
|
||||
|
||||
/* load in the base contribution in the rhs */
|
||||
for (index = 1; index <= pDevice->numEqns; index++) {
|
||||
pDevice->rhs[index] = 0.0;
|
||||
}
|
||||
pNode = pBaseElem->pRightNode;
|
||||
if (pNode->baseType == N_TYPE) {
|
||||
pDevice->rhs[pNode->nEqn] = pNode->nConc * pNode->eg;
|
||||
} else if (pNode->baseType == P_TYPE) {
|
||||
pDevice->rhs[pNode->pEqn] = pNode->pConc * pNode->eg;
|
||||
} else {
|
||||
printf("\n BJTadmittance: unknown base type");
|
||||
}
|
||||
|
||||
/* don't need to LU factor the jacobian since it exists */
|
||||
spSolve(pDevice->matrix, pDevice->rhs, solutionReal,
|
||||
pDevice->rhsImag, solutionImag);
|
||||
pElem = pDevice->elemArray[1];
|
||||
pNode = pElem->pLeftNode;
|
||||
y = computeAdmittance(pNode, FALSE, solutionReal, solutionImag, &cOmega);
|
||||
CMPLX_ASSIGN_VALUE(pIeVbe, -y->real, -y->imag);
|
||||
pNode = pCollElem->pRightNode;
|
||||
y = computeAdmittance(pNode, FALSE, solutionReal, solutionImag, &cOmega);
|
||||
CMPLX_ASSIGN_VALUE(pIcVbe, -y->real, -y->imag);
|
||||
|
||||
|
||||
CMPLX_ASSIGN(*yIeVce, pIeVce);
|
||||
CMPLX_ASSIGN(*yIcVce, pIcVce);
|
||||
CMPLX_ASSIGN(*yIeVbe, pIeVbe);
|
||||
CMPLX_ASSIGN(*yIcVbe, pIcVbe);
|
||||
CMPLX_MULT_SELF_SCALAR(*yIeVce, GNorm * area);
|
||||
CMPLX_MULT_SELF_SCALAR(*yIeVbe, GNorm * area);
|
||||
CMPLX_MULT_SELF_SCALAR(*yIcVce, GNorm * area);
|
||||
CMPLX_MULT_SELF_SCALAR(*yIcVbe, GNorm * area);
|
||||
}
|
||||
|
||||
/* function to compute the admittance of a one-D device */
|
||||
/* cOmega is the complex frequency */
|
||||
|
||||
SPcomplex *
|
||||
computeAdmittance(ONEnode *pNode, BOOLEAN delVContact, double *xReal,
|
||||
double *xImag, SPcomplex *cOmega)
|
||||
{
|
||||
ONEnode *pHNode;
|
||||
ONEedge *pEdge;
|
||||
ONEelem *pElem;
|
||||
SPcomplex psi, n, p;
|
||||
SPcomplex sum, prod1, prod2;
|
||||
/* SPcomplex yAc; */
|
||||
double temp;
|
||||
int i;
|
||||
|
||||
|
||||
CMPLX_ASSIGN_VALUE(yAc, 0.0, 0.0);
|
||||
|
||||
for (i = 0; i <= 1; i++) {
|
||||
pElem = pNode->pElems[i];
|
||||
if (pElem != NIL(ONEelem)) {
|
||||
switch (i) {
|
||||
case 0:
|
||||
/* the right node of the element */
|
||||
pHNode = pElem->pLeftNode;
|
||||
pEdge = pElem->pEdge;
|
||||
CMPLX_ASSIGN_VALUE(psi, xReal[pHNode->psiEqn],
|
||||
xImag[pHNode->psiEqn]);
|
||||
if (pElem->elemType == SEMICON) {
|
||||
CMPLX_ASSIGN_VALUE(n, xReal[pHNode->nEqn],
|
||||
xImag[pHNode->nEqn]);
|
||||
CMPLX_ASSIGN_VALUE(p, xReal[pHNode->pEqn],
|
||||
xImag[pHNode->pEqn]);
|
||||
|
||||
CMPLX_MULT_SCALAR(prod1, psi, -pEdge->dJnDpsiP1);
|
||||
CMPLX_MULT_SCALAR(prod2, n, pEdge->dJnDn);
|
||||
CMPLX_ADD(yAc, prod1, prod2);
|
||||
CMPLX_MULT_SCALAR(prod1, psi, -pEdge->dJpDpsiP1);
|
||||
CMPLX_MULT_SCALAR(prod2, p, pEdge->dJpDp);
|
||||
CMPLX_ADD(sum, prod1, prod2);
|
||||
CMPLX_ADD_ASSIGN(yAc, sum);
|
||||
if (delVContact) {
|
||||
CMPLX_ADD_SELF_SCALAR(yAc, pEdge->dJnDpsiP1 + pEdge->dJpDpsiP1);
|
||||
}
|
||||
}
|
||||
CMPLX_MULT_SCALAR(prod1, *cOmega, pElem->epsRel * pElem->rDx);
|
||||
CMPLX_MULT(prod2, prod1, psi)
|
||||
CMPLX_ADD_ASSIGN(yAc, prod2);
|
||||
if (delVContact) {
|
||||
CMPLX_SUBT_ASSIGN(yAc, prod1);
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
/* the left node of the element */
|
||||
pHNode = pElem->pRightNode;
|
||||
pEdge = pElem->pEdge;
|
||||
CMPLX_ASSIGN_VALUE(psi, xReal[pHNode->psiEqn],
|
||||
xImag[pHNode->psiEqn]);
|
||||
if (pElem->elemType == SEMICON) {
|
||||
CMPLX_ASSIGN_VALUE(n, xReal[pHNode->nEqn],
|
||||
xImag[pHNode->nEqn]);
|
||||
CMPLX_ASSIGN_VALUE(p, xReal[pHNode->pEqn],
|
||||
xImag[pHNode->pEqn]);
|
||||
CMPLX_MULT_SCALAR(prod1, psi, pEdge->dJnDpsiP1);
|
||||
CMPLX_MULT_SCALAR(prod2, n, pEdge->dJnDnP1);
|
||||
CMPLX_ADD(yAc, prod1, prod2);
|
||||
CMPLX_MULT_SCALAR(prod1, psi, pEdge->dJpDpsiP1);
|
||||
CMPLX_MULT_SCALAR(prod2, p, pEdge->dJpDpP1);
|
||||
CMPLX_ADD(sum, prod1, prod2);
|
||||
CMPLX_ADD_ASSIGN(yAc, sum);
|
||||
|
||||
if (delVContact) {
|
||||
CMPLX_ADD_SELF_SCALAR(yAc, -(pEdge->dJnDpsiP1 + pEdge->dJpDpsiP1));
|
||||
}
|
||||
}
|
||||
CMPLX_MULT_SCALAR(prod1, *cOmega, pElem->epsRel * pElem->rDx);
|
||||
CMPLX_MULT(prod2, prod1, psi);
|
||||
CMPLX_SUBT_ASSIGN(yAc, prod2);
|
||||
if (delVContact) {
|
||||
CMPLX_ADD_ASSIGN(yAc, prod1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* should never be here. Error */
|
||||
printf("computeAdmittance: Error - unknown element\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
return (&yAc);
|
||||
}
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
/**********
|
||||
Copyright 1991 Regents of the University of California. All rights reserved.
|
||||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numglobs.h"
|
||||
#include "onemesh.h"
|
||||
#include "onedev.h"
|
||||
#include "macros.h"
|
||||
|
||||
#include "onedext.h"
|
||||
#include "oneddefs.h"
|
||||
|
||||
double
|
||||
ONEavalanche(BOOLEAN rhsOnly, ONEdevice *pDevice, ONEnode *pNode)
|
||||
{
|
||||
ONEelem *pLElem, *pRElem;
|
||||
ONEedge *pLEdge, *pREdge;
|
||||
int numNodes = pDevice->numNodes;
|
||||
double dJnDpsiPrev, dJpDpsiPrev;
|
||||
double eField, temp, jn, jp;
|
||||
double signE, signN, signP, coeffR, coeffL, alphaN, alphaP;
|
||||
double generation = 0.0;
|
||||
double dAlphaNDpsiM1, dAlphaNDpsi, dAlphaNDpsiP1;
|
||||
double dAlphaPDpsiM1, dAlphaPDpsi, dAlphaPDpsiP1;
|
||||
ONEmaterial *info;
|
||||
|
||||
|
||||
pRElem = pNode->pRightElem;
|
||||
pLElem = pNode->pLeftElem;
|
||||
|
||||
if (pRElem->evalNodes[0]) {
|
||||
info = pRElem->matlInfo;
|
||||
} else {
|
||||
info = pLElem->matlInfo;
|
||||
}
|
||||
|
||||
pREdge = pRElem->pEdge;
|
||||
pLEdge = pLElem->pEdge;
|
||||
dJnDpsiPrev = pLEdge->dJnDpsiP1;
|
||||
dJpDpsiPrev = pLEdge->dJpDpsiP1;
|
||||
|
||||
temp = pRElem->dx + pLElem->dx;
|
||||
coeffR = pLElem->dx / temp;
|
||||
coeffL = pRElem->dx / temp;
|
||||
|
||||
eField = -(coeffR * pREdge->dPsi * pRElem->rDx +
|
||||
coeffL * pLEdge->dPsi * pLElem->rDx);
|
||||
jn = coeffR * pREdge->jn + coeffL * pLEdge->jn;
|
||||
jp = coeffR * pREdge->jp + coeffL * pLEdge->jp;
|
||||
|
||||
signE = SGN(eField);
|
||||
eField = ABS(eField);
|
||||
if (eField == 0.0) {
|
||||
return (0.0);
|
||||
}
|
||||
signN = SGN(jn);
|
||||
if (signN * signE > 0.0) {
|
||||
/* field accelerates the carriers, hence avalanche */
|
||||
if (info->bii[ELEC] / eField > 80.0) {
|
||||
alphaN = 0.0;
|
||||
} else {
|
||||
alphaN = info->aii[ELEC] * exp(-info->bii[ELEC] / eField);
|
||||
}
|
||||
} else {
|
||||
alphaN = 0.0;
|
||||
}
|
||||
signP = SGN(jp);
|
||||
if (signP * signE > 0.0) {
|
||||
/* field accelerates the carriers, hence avalanche */
|
||||
if (info->bii[HOLE] / eField > 80.0) {
|
||||
alphaP = 0.0;
|
||||
} else {
|
||||
alphaP = info->aii[HOLE] * exp(-info->bii[HOLE] / eField);
|
||||
}
|
||||
} else {
|
||||
alphaP = 0.0;
|
||||
}
|
||||
|
||||
if ((alphaN == 0.0) && (alphaP == 0.0)) {
|
||||
return (generation);
|
||||
}
|
||||
generation = (alphaN * ABS(jn) + alphaP * ABS(jp)) *
|
||||
0.5 * (pRElem->dx + pLElem->dx);
|
||||
if (rhsOnly) {
|
||||
return (generation);
|
||||
}
|
||||
if (alphaN == 0.0) {
|
||||
dAlphaNDpsiM1 = 0.0;
|
||||
dAlphaNDpsiP1 = 0.0;
|
||||
dAlphaNDpsi = 0.0;
|
||||
} else {
|
||||
temp = alphaN * info->bii[ELEC] / (eField * eField);
|
||||
dAlphaNDpsiM1 = signE * temp * (coeffL * pLElem->rDx);
|
||||
dAlphaNDpsiP1 = -signE * temp * (coeffR * pRElem->rDx);
|
||||
dAlphaNDpsi = -(dAlphaNDpsiM1 + dAlphaNDpsiP1);
|
||||
}
|
||||
|
||||
if (alphaP == 0.0) {
|
||||
dAlphaPDpsiM1 = 0.0;
|
||||
dAlphaPDpsiP1 = 0.0;
|
||||
dAlphaPDpsi = 0.0;
|
||||
} else {
|
||||
temp = alphaP * info->bii[HOLE] / (eField * eField);
|
||||
dAlphaPDpsiM1 = signE * temp * (coeffL * pLElem->rDx);
|
||||
dAlphaPDpsiP1 = -signE * temp * (coeffR * pRElem->rDx);
|
||||
dAlphaPDpsi = -(dAlphaPDpsiM1 + dAlphaPDpsiP1);
|
||||
}
|
||||
|
||||
coeffR = 0.5 * pLElem->dx;
|
||||
coeffL = 0.5 * pRElem->dx;
|
||||
|
||||
if (pNode->nodeI != 2) {
|
||||
*(pNode->fNPsiiM1) +=
|
||||
signN * (-alphaN * coeffL * dJnDpsiPrev +
|
||||
coeffL * pLEdge->jn * dAlphaNDpsiM1) +
|
||||
signP * (-alphaP * coeffL * dJpDpsiPrev +
|
||||
coeffL * pLEdge->jp * dAlphaPDpsiM1);
|
||||
*(pNode->fNNiM1) += signN * alphaN * coeffL * pLEdge->dJnDn;
|
||||
*(pNode->fNPiM1) += signP * alphaP * coeffL * pLEdge->dJpDp;
|
||||
|
||||
*(pNode->fPPsiiM1) -=
|
||||
signN * (-alphaN * coeffL * dJnDpsiPrev +
|
||||
coeffL * pLEdge->jn * dAlphaNDpsiM1) +
|
||||
signP * (-alphaP * coeffL * dJpDpsiPrev +
|
||||
coeffL * pLEdge->jp * dAlphaPDpsiM1);
|
||||
*(pNode->fPPiM1) -= signP * alphaP * coeffL * pLEdge->dJpDp;
|
||||
*(pNode->fPNiM1) -= signN * alphaN * coeffL * pLEdge->dJnDn;
|
||||
}
|
||||
if (pNode->nodeI != numNodes - 1) {
|
||||
*(pNode->fNPsiiP1) +=
|
||||
signN * (alphaN * coeffR * pREdge->dJnDpsiP1 +
|
||||
coeffR * pREdge->jn * dAlphaNDpsiP1) +
|
||||
signP * (alphaP * coeffR * pREdge->dJpDpsiP1 +
|
||||
coeffR * pREdge->jp * dAlphaPDpsiP1);
|
||||
*(pNode->fNNiP1) += signN * alphaN * coeffR * pREdge->dJnDnP1;
|
||||
*(pNode->fNPiP1) += signP * alphaP * coeffR * pREdge->dJpDpP1;
|
||||
*(pNode->fPPsiiP1) -=
|
||||
signN * (alphaN * coeffR * pREdge->dJnDpsiP1 +
|
||||
coeffR * pREdge->jn * dAlphaNDpsiP1) +
|
||||
signP * (alphaP * coeffR * pREdge->dJpDpsiP1 +
|
||||
coeffR * pREdge->jp * dAlphaPDpsiP1);
|
||||
*(pNode->fPPiP1) -= signP * alphaP * coeffR * pREdge->dJpDpP1;
|
||||
*(pNode->fPNiP1) -= signN * alphaN * coeffR * pREdge->dJnDnP1;
|
||||
}
|
||||
*(pNode->fNPsi) +=
|
||||
signN * (alphaN * (-coeffR * pREdge->dJnDpsiP1 +
|
||||
coeffL * dJnDpsiPrev) + (coeffR * pREdge->jn +
|
||||
coeffL * pLEdge->jn) * dAlphaNDpsi) +
|
||||
signP * (alphaP * (-coeffR * pREdge->dJpDpsiP1 +
|
||||
coeffL * dJpDpsiPrev) + (coeffR * pREdge->jp +
|
||||
coeffL * pLEdge->jp) * dAlphaPDpsi);
|
||||
*(pNode->fNN) += signN * alphaN * (coeffR * pREdge->dJnDn +
|
||||
coeffL * pLEdge->dJnDnP1);
|
||||
*(pNode->fNP) += signP * alphaP * (coeffR * pREdge->dJpDp +
|
||||
coeffL * pLEdge->dJpDpP1);
|
||||
|
||||
*(pNode->fPPsi) -=
|
||||
signN * (alphaN * (-coeffR * pREdge->dJnDpsiP1 +
|
||||
coeffL * dJnDpsiPrev) + (coeffR * pREdge->jn +
|
||||
coeffL * pLEdge->jn) * dAlphaNDpsi) +
|
||||
signP * (alphaP * (-coeffR * pREdge->dJpDpsiP1 +
|
||||
coeffL * dJpDpsiPrev) + (coeffR * pREdge->jp +
|
||||
coeffL * pLEdge->jp) * dAlphaPDpsi);
|
||||
*(pNode->fPN) -= signN * alphaN * (coeffR * pREdge->dJnDn +
|
||||
coeffL * pLEdge->dJnDnP1);
|
||||
*(pNode->fPP) -= signP * alphaP * (coeffR * pREdge->dJpDp +
|
||||
coeffL * pLEdge->dJpDpP1);
|
||||
|
||||
return (generation);
|
||||
}
|
||||
|
|
@ -0,0 +1,245 @@
|
|||
/**********
|
||||
Copyright 1992 Regents of the University of California. All rights reserved.
|
||||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group
|
||||
**********/
|
||||
|
||||
/* Functions to compute device conductances and currents */
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numglobs.h"
|
||||
#include "numenum.h"
|
||||
#include "onemesh.h"
|
||||
#include "onedev.h"
|
||||
#include "macros.h"
|
||||
#include "spMatrix.h"
|
||||
|
||||
#include "onedext.h"
|
||||
#include "oneddefs.h"
|
||||
|
||||
void
|
||||
NUMDconductance(ONEdevice *pDevice, BOOLEAN tranAnalysis,
|
||||
double *intCoeff, double *gd)
|
||||
{
|
||||
ONEelem *pElem = pDevice->elemArray[pDevice->numNodes - 1];
|
||||
ONEnode *pNode;
|
||||
ONEedge *pEdge;
|
||||
int index;
|
||||
double dPsiDv, dNDv, dPDv, *incVpn;
|
||||
|
||||
|
||||
*gd = 0.0;
|
||||
|
||||
/* zero the rhs before loading in the new rhs */
|
||||
for (index = 1; index <= pDevice->numEqns; index++) {
|
||||
pDevice->rhs[index] = 0.0;
|
||||
}
|
||||
/* compute incremental changes due to N contact */
|
||||
pNode = pElem->pLeftNode;
|
||||
pDevice->rhs[pNode->psiEqn] = pElem->epsRel * pElem->rDx;
|
||||
if (pElem->elemType == SEMICON) {
|
||||
pEdge = pElem->pEdge;
|
||||
pDevice->rhs[pNode->nEqn] = -pEdge->dJnDpsiP1;
|
||||
pDevice->rhs[pNode->pEqn] = -pEdge->dJpDpsiP1;
|
||||
}
|
||||
incVpn = pDevice->dcDeltaSolution;
|
||||
spSolve(pDevice->matrix, pDevice->rhs, incVpn, NIL(spREAL), NIL(spREAL));
|
||||
|
||||
pElem = pDevice->elemArray[1];
|
||||
pNode = pElem->pRightNode;
|
||||
pEdge = pElem->pEdge;
|
||||
dPsiDv = incVpn[pNode->psiEqn];
|
||||
if (pElem->elemType == SEMICON) {
|
||||
dNDv = incVpn[pNode->nEqn];
|
||||
dPDv = incVpn[pNode->pEqn];
|
||||
*gd += pEdge->dJnDpsiP1 * dPsiDv + pEdge->dJnDnP1 * dNDv +
|
||||
pEdge->dJpDpsiP1 * dPsiDv + pEdge->dJpDpP1 * dPDv;
|
||||
}
|
||||
/* For transient analysis, add the displacement term */
|
||||
if (tranAnalysis) {
|
||||
*gd -= intCoeff[0] * pElem->epsRel * pElem->rDx * dPsiDv;
|
||||
}
|
||||
*gd *= -GNorm * pDevice->area;
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
NBJTconductance(ONEdevice *pDevice, BOOLEAN tranAnalysis, double *intCoeff,
|
||||
double *dIeDVce, double *dIcDVce, double *dIeDVbe, double *dIcDVbe)
|
||||
{
|
||||
ONEelem *pLastElem = pDevice->elemArray[pDevice->numNodes - 1];
|
||||
ONEelem *pBaseElem = pDevice->elemArray[pDevice->baseIndex - 1];
|
||||
ONEelem *pElem;
|
||||
ONEnode *pNode;
|
||||
ONEedge *pEdge;
|
||||
int index;
|
||||
double dPsiDVce, dPsiDVbe, dNDVce, dNDVbe, dPDVce, dPDVbe;
|
||||
double *incVce, *incVbe;
|
||||
double nConc, pConc;
|
||||
double area = pDevice->area;
|
||||
|
||||
*dIeDVce = 0.0;
|
||||
*dIcDVce = 0.0;
|
||||
*dIeDVbe = 0.0;
|
||||
*dIcDVbe = 0.0;
|
||||
|
||||
/* zero the rhs before loading in the new rhs */
|
||||
for (index = 1; index <= pDevice->numEqns; index++) {
|
||||
pDevice->rhs[index] = 0.0;
|
||||
}
|
||||
/* store the new rhs for computing CE incremental quantities */
|
||||
pNode = pLastElem->pLeftNode;
|
||||
pDevice->rhs[pNode->psiEqn] = pLastElem->epsRel * pLastElem->rDx;
|
||||
if (pLastElem->elemType == SEMICON) {
|
||||
pEdge = pLastElem->pEdge;
|
||||
pDevice->rhs[pNode->nEqn] = -pEdge->dJnDpsiP1;
|
||||
pDevice->rhs[pNode->pEqn] = -pEdge->dJpDpsiP1;
|
||||
}
|
||||
incVce = pDevice->dcDeltaSolution;
|
||||
spSolve(pDevice->matrix, pDevice->rhs, incVce, NIL(spREAL), NIL(spREAL));
|
||||
|
||||
/* zero the rhs before loading in the new rhs base contribution */
|
||||
for (index = 1; index <= pDevice->numEqns; index++) {
|
||||
pDevice->rhs[index] = 0.0;
|
||||
}
|
||||
pNode = pBaseElem->pRightNode;
|
||||
|
||||
if (pNode->baseType == N_TYPE) {
|
||||
nConc = *(pDevice->devState0 + pNode->nodeN);
|
||||
pDevice->rhs[pNode->nEqn] = nConc * pNode->eg;
|
||||
} else if (pNode->baseType == P_TYPE) {
|
||||
pConc = *(pDevice->devState0 + pNode->nodeP);
|
||||
pDevice->rhs[pNode->pEqn] = pConc * pNode->eg;
|
||||
} else {
|
||||
printf("NBJTconductance: unknown base type\n");
|
||||
}
|
||||
|
||||
incVbe = pDevice->copiedSolution;
|
||||
spSolve(pDevice->matrix, pDevice->rhs, incVbe, NIL(spREAL), NIL(spREAL));
|
||||
|
||||
pElem = pDevice->elemArray[1];/* first element */
|
||||
pEdge = pElem->pEdge;
|
||||
pNode = pElem->pRightNode;
|
||||
|
||||
dPsiDVce = incVce[pNode->psiEqn];
|
||||
dPsiDVbe = incVbe[pNode->psiEqn];
|
||||
if (pElem->elemType == SEMICON) {
|
||||
dNDVce = incVce[pNode->nEqn];
|
||||
dPDVce = incVce[pNode->pEqn];
|
||||
dNDVbe = incVbe[pNode->nEqn];
|
||||
dPDVbe = incVbe[pNode->pEqn];
|
||||
*dIeDVce += pEdge->dJnDpsiP1 * dPsiDVce + pEdge->dJnDnP1 * dNDVce +
|
||||
pEdge->dJpDpsiP1 * dPsiDVce + pEdge->dJpDpP1 * dPDVce;
|
||||
*dIeDVbe += pEdge->dJnDpsiP1 * dPsiDVbe + pEdge->dJnDnP1 * dNDVbe +
|
||||
pEdge->dJpDpsiP1 * dPsiDVbe + pEdge->dJpDpP1 * dPDVbe;
|
||||
}
|
||||
/* For transient analysis add the displacement term */
|
||||
if (tranAnalysis) {
|
||||
*dIeDVce -= intCoeff[0] * pElem->epsRel * dPsiDVce * pElem->rDx;
|
||||
*dIeDVbe -= intCoeff[0] * pElem->epsRel * dPsiDVbe * pElem->rDx;
|
||||
}
|
||||
pElem = pDevice->elemArray[pDevice->numNodes - 1]; /* last element */
|
||||
pEdge = pElem->pEdge;
|
||||
pNode = pElem->pLeftNode;
|
||||
dPsiDVce = incVce[pNode->psiEqn];
|
||||
dPsiDVbe = incVbe[pNode->psiEqn];
|
||||
if (pElem->elemType == SEMICON) {
|
||||
dNDVce = incVce[pNode->nEqn];
|
||||
dPDVce = incVce[pNode->pEqn];
|
||||
dNDVbe = incVbe[pNode->nEqn];
|
||||
dPDVbe = incVbe[pNode->pEqn];
|
||||
*dIcDVce += -pEdge->dJnDpsiP1 * dPsiDVce + pEdge->dJnDn * dNDVce +
|
||||
-pEdge->dJpDpsiP1 * dPsiDVce + pEdge->dJpDp * dPDVce +
|
||||
/* add terms since adjacent to boundary */
|
||||
pEdge->dJnDpsiP1 + pEdge->dJpDpsiP1;
|
||||
*dIcDVbe += -pEdge->dJnDpsiP1 * dPsiDVbe + pEdge->dJnDn * dNDVbe +
|
||||
-pEdge->dJpDpsiP1 * dPsiDVbe + pEdge->dJpDp * dPDVbe;
|
||||
}
|
||||
if (tranAnalysis) {
|
||||
*dIcDVce += intCoeff[0] * pElem->epsRel * (dPsiDVce - 1.0) * pElem->rDx;
|
||||
*dIcDVbe += intCoeff[0] * pElem->epsRel * dPsiDVbe * pElem->rDx;
|
||||
}
|
||||
*dIeDVce *= -GNorm * area;
|
||||
*dIcDVce *= -GNorm * area;
|
||||
*dIeDVbe *= -GNorm * area;
|
||||
*dIcDVbe *= -GNorm * area;
|
||||
}
|
||||
|
||||
void
|
||||
NUMDcurrent(ONEdevice *pDevice, BOOLEAN tranAnalysis, double *intCoeff,
|
||||
double *id)
|
||||
{
|
||||
ONEnode *pNode;
|
||||
ONEelem *pElem;
|
||||
ONEedge *pEdge;
|
||||
int index;
|
||||
double *delta = pDevice->dcDeltaSolution;
|
||||
double dPsi, dN, dP, *solution;
|
||||
|
||||
|
||||
*id = 0.0;
|
||||
|
||||
pElem = pDevice->elemArray[1];
|
||||
pNode = pElem->pRightNode;
|
||||
pEdge = pElem->pEdge;
|
||||
dPsi = delta[pNode->psiEqn];
|
||||
*id = pEdge->jn + pEdge->jp + pElem->epsRel * pEdge->jd;
|
||||
if (pElem->elemType == SEMICON) {
|
||||
dN = delta[pNode->nEqn];
|
||||
dP = delta[pNode->pEqn];
|
||||
*id += pEdge->dJnDpsiP1 * dPsi + pEdge->dJnDnP1 * dN +
|
||||
pEdge->dJpDpsiP1 * dPsi + pEdge->dJpDpP1 * dP;
|
||||
}
|
||||
/* for transient analysis add the displacement term */
|
||||
if (tranAnalysis) {
|
||||
*id -= intCoeff[0] * pElem->epsRel * pElem->rDx * dPsi;
|
||||
}
|
||||
*id *= JNorm * pDevice->area;
|
||||
}
|
||||
|
||||
void
|
||||
NBJTcurrent(ONEdevice *pDevice, BOOLEAN tranAnalysis, double *intCoeff,
|
||||
double *ie, double *ic)
|
||||
{
|
||||
ONEnode *pNode;
|
||||
ONEelem *pElem;
|
||||
ONEedge *pEdge;
|
||||
int index;
|
||||
double dPsi, dN, dP;
|
||||
double *solution;
|
||||
|
||||
solution = pDevice->dcDeltaSolution;
|
||||
|
||||
/* first edge for calculating ie */
|
||||
pElem = pDevice->elemArray[1];
|
||||
pNode = pElem->pRightNode;
|
||||
pEdge = pElem->pEdge;
|
||||
dPsi = solution[pNode->psiEqn];
|
||||
*ie = pEdge->jn + pEdge->jp + pElem->epsRel * pEdge->jd;
|
||||
if (pElem->elemType == SEMICON) {
|
||||
dN = solution[pNode->nEqn];
|
||||
dP = solution[pNode->pEqn];
|
||||
*ie += pEdge->dJnDpsiP1 * dPsi + pEdge->dJnDnP1 * dN +
|
||||
pEdge->dJpDpsiP1 * dPsi + pEdge->dJpDpP1 * dP;
|
||||
}
|
||||
/* for transient analysis add the displacement term */
|
||||
if (tranAnalysis) {
|
||||
*ie -= intCoeff[0] * pElem->epsRel * dPsi * pElem->rDx;
|
||||
}
|
||||
/* last edge for calculating ic */
|
||||
pElem = pDevice->elemArray[pDevice->numNodes - 1];
|
||||
pNode = pElem->pLeftNode;
|
||||
pEdge = pElem->pEdge;
|
||||
dPsi = solution[pNode->psiEqn];
|
||||
*ic = pEdge->jn + pEdge->jp + pElem->epsRel * pEdge->jd;
|
||||
if (pElem->elemType == SEMICON) {
|
||||
dN = solution[pNode->nEqn];
|
||||
dP = solution[pNode->pEqn];
|
||||
*ic += -pEdge->dJnDpsiP1 * dPsi + pEdge->dJnDn * dN +
|
||||
-pEdge->dJpDpsiP1 * dPsi + pEdge->dJpDp * dP;
|
||||
}
|
||||
if (tranAnalysis) {
|
||||
*ic += intCoeff[0] * pElem->epsRel * dPsi * pElem->rDx;
|
||||
}
|
||||
*ic *= -JNorm * pDevice->area;
|
||||
*ie *= -JNorm * pDevice->area;
|
||||
}
|
||||
|
|
@ -0,0 +1,657 @@
|
|||
/**********
|
||||
Copyright 1991 Regents of the University of California. All rights reserved.
|
||||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numglobs.h"
|
||||
#include "numenum.h"
|
||||
#include "onemesh.h"
|
||||
#include "onedev.h"
|
||||
#include "spMatrix.h"
|
||||
#include "macros.h"
|
||||
#include "onedext.h"
|
||||
#include "oneddefs.h"
|
||||
#include "cidersupt.h"
|
||||
#include "../../maths/misc/bernoull.h"
|
||||
|
||||
|
||||
/* Forward Declarations */
|
||||
|
||||
void ONE_commonTerms( ONEdevice *, BOOLEAN, BOOLEAN, ONEtranInfo *);
|
||||
|
||||
|
||||
/* functions to setup and solve the continuity equations */
|
||||
/* Both continuity equations are solved */
|
||||
|
||||
|
||||
void
|
||||
ONE_jacBuild(ONEdevice *pDevice)
|
||||
{
|
||||
char *matrix = pDevice->matrix;
|
||||
ONEelem *pElem;
|
||||
ONEnode *pNode, *pNode1;
|
||||
int index, eIndex;
|
||||
int psiEqn, nEqn, pEqn; /* scratch for deref'd eqn numbers */
|
||||
int psiEqnL=0, nEqnL=0, pEqnL=0;
|
||||
int psiEqnR=0, nEqnR=0, pEqnR=0;
|
||||
|
||||
|
||||
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) {
|
||||
pElem = pDevice->elemArray[eIndex];
|
||||
/* first the self terms */
|
||||
for (index = 0; index <= 1; index++) {
|
||||
pNode = pElem->pNodes[index];
|
||||
/* get poisson pointer */
|
||||
psiEqn = pNode->psiEqn;
|
||||
pNode->fPsiPsi = spGetElement(matrix, psiEqn, psiEqn);
|
||||
|
||||
if (pElem->elemType == SEMICON) {
|
||||
/* get continuity-coupling terms */
|
||||
nEqn = pNode->nEqn;
|
||||
pEqn = pNode->pEqn;
|
||||
/* pointers for additional terms */
|
||||
pNode->fPsiN = spGetElement(matrix, psiEqn, nEqn);
|
||||
pNode->fPsiP = spGetElement(matrix, psiEqn, pEqn);
|
||||
pNode->fNPsi = spGetElement(matrix, nEqn, psiEqn);
|
||||
pNode->fNN = spGetElement(matrix, nEqn, nEqn);
|
||||
pNode->fNP = spGetElement(matrix, nEqn, pEqn);
|
||||
pNode->fPPsi = spGetElement(matrix, pEqn, psiEqn);
|
||||
pNode->fPP = spGetElement(matrix, pEqn, pEqn);
|
||||
pNode->fPN = spGetElement(matrix, pEqn, nEqn);
|
||||
} else {
|
||||
nEqn = 0;
|
||||
pEqn = 0;
|
||||
}
|
||||
|
||||
/* save indices */
|
||||
if (index == 0) { /* left node */
|
||||
psiEqnL = psiEqn;
|
||||
nEqnL = nEqn;
|
||||
pEqnL = pEqn;
|
||||
} else {
|
||||
psiEqnR = psiEqn;
|
||||
nEqnR = nEqn;
|
||||
pEqnR = pEqn;
|
||||
}
|
||||
}
|
||||
|
||||
/* now terms to couple to adjacent nodes */
|
||||
pNode = pElem->pLeftNode;
|
||||
pNode->fPsiPsiiP1 = spGetElement(matrix, psiEqnL, psiEqnR);
|
||||
if (pElem->elemType == SEMICON) {
|
||||
/* pointers for additional terms */
|
||||
pNode->fNPsiiP1 = spGetElement(matrix, nEqnL, psiEqnR);
|
||||
pNode->fNNiP1 = spGetElement(matrix, nEqnL, nEqnR);
|
||||
pNode->fPPsiiP1 = spGetElement(matrix, pEqnL, psiEqnR);
|
||||
pNode->fPPiP1 = spGetElement(matrix, pEqnL, pEqnR);
|
||||
if (AvalancheGen) {
|
||||
pNode->fNPiP1 = spGetElement(matrix, nEqnL, pEqnR);
|
||||
pNode->fPNiP1 = spGetElement(matrix, pEqnL, nEqnR);
|
||||
}
|
||||
}
|
||||
pNode = pElem->pRightNode;
|
||||
pNode->fPsiPsiiM1 = spGetElement(matrix, psiEqnR, psiEqnL);
|
||||
if (pElem->elemType == SEMICON) {
|
||||
/* pointers for additional terms */
|
||||
pNode->fNPsiiM1 = spGetElement(matrix, nEqnR, psiEqnL);
|
||||
pNode->fNNiM1 = spGetElement(matrix, nEqnR, nEqnL);
|
||||
pNode->fPPsiiM1 = spGetElement(matrix, pEqnR, psiEqnL);
|
||||
pNode->fPPiM1 = spGetElement(matrix, pEqnR, pEqnL);
|
||||
if (AvalancheGen) {
|
||||
pNode->fNPiM1 = spGetElement(matrix, nEqnR, pEqnL);
|
||||
pNode->fPNiM1 = spGetElement(matrix, pEqnR, nEqnL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ONE_sysLoad(ONEdevice *pDevice, BOOLEAN tranAnalysis,
|
||||
ONEtranInfo *info)
|
||||
{
|
||||
ONEelem *pElem;
|
||||
ONEnode *pNode, *pNode1;
|
||||
ONEedge *pEdge;
|
||||
int index, eIndex;
|
||||
double *pRhs = pDevice->rhs;
|
||||
double dx, rDx, dPsi;
|
||||
double rhsN, rhsP, generation;
|
||||
double perTime = 0.0;
|
||||
double fNd, fNa, fdNd, fdNa;
|
||||
double netConc, dNd, dNa, psi, nConc, pConc;
|
||||
|
||||
|
||||
/* first compute the currents and their derivatives */
|
||||
ONE_commonTerms(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->numNodes; eIndex++) {
|
||||
pElem = pDevice->elemArray[eIndex];
|
||||
dx = 0.5 * pElem->dx;
|
||||
rDx = pElem->epsRel * pElem->rDx;
|
||||
|
||||
/* load for all i */
|
||||
for (index = 0; index <= 1; index++) {
|
||||
pNode = pElem->pNodes[index];
|
||||
if (pNode->nodeType != CONTACT) {
|
||||
*(pNode->fPsiPsi) += rDx;
|
||||
pRhs[pNode->psiEqn] += pNode->qf;
|
||||
if (pElem->elemType == SEMICON) {
|
||||
pEdge = pElem->pEdge;
|
||||
netConc = pNode->netConc;
|
||||
dNd = 0.0;
|
||||
dNa = 0.0;
|
||||
psi = *(pDevice->devState0 + pNode->nodePsi);
|
||||
nConc = *(pDevice->devState0 + pNode->nodeN);
|
||||
pConc = *(pDevice->devState0 + pNode->nodeP);
|
||||
|
||||
|
||||
if (FreezeOut) {
|
||||
ONE_freezeOut(pNode, nConc, pConc, &fNd, &fNa, &fdNd, &fdNa);
|
||||
netConc = pNode->nd * fNd - pNode->na * fNa;
|
||||
dNd = pNode->nd * fdNd;
|
||||
dNa = pNode->na * fdNa;
|
||||
}
|
||||
*(pNode->fPsiN) += dx * (1.0 - dNd);
|
||||
*(pNode->fPsiP) -= dx * (1.0 - dNa);
|
||||
*(pNode->fNPsi) -= pEdge->dJnDpsiP1;
|
||||
*(pNode->fPPsi) -= pEdge->dJpDpsiP1;
|
||||
|
||||
pRhs[pNode->psiEqn] += dx * (netConc + pConc - nConc);
|
||||
|
||||
/* Handle generation terms */
|
||||
*(pNode->fNN) -= dx * pNode->dUdN;
|
||||
*(pNode->fNP) -= dx * pNode->dUdP;
|
||||
*(pNode->fPP) += dx * pNode->dUdP;
|
||||
*(pNode->fPN) += dx * pNode->dUdN;
|
||||
pRhs[pNode->nEqn] -= -dx * pNode->uNet;
|
||||
pRhs[pNode->pEqn] -= dx * pNode->uNet;
|
||||
|
||||
/* Handle dXdT continuity terms */
|
||||
if (tranAnalysis) {
|
||||
*(pNode->fNN) -= dx * perTime;
|
||||
*(pNode->fPP) += dx * perTime;
|
||||
pRhs[pNode->nEqn] += dx * pNode->dNdT;
|
||||
pRhs[pNode->pEqn] -= dx * pNode->dPdT;
|
||||
}
|
||||
/* Take care of base contact if necessary */
|
||||
/* eg holds the base edge mu/dx */
|
||||
if (pNode->baseType == N_TYPE) {
|
||||
pRhs[pNode->nEqn] += 0.5 * pNode->eg * nConc *
|
||||
(pNode->vbe - psi + log(nConc / pNode->nie));
|
||||
*(pNode->fNPsi) += 0.5 * pNode->eg * nConc;
|
||||
*(pNode->fNN) -= 0.5 * pNode->eg *
|
||||
(pNode->vbe - psi + log(nConc / pNode->nie) + 1.0);
|
||||
} else if (pNode->baseType == P_TYPE) {
|
||||
pRhs[pNode->pEqn] += 0.5 * pNode->eg * pConc *
|
||||
(pNode->vbe - psi - log(pConc / pNode->nie));
|
||||
*(pNode->fPPsi) += 0.5 * pNode->eg * pConc;
|
||||
*(pNode->fPP) -= 0.5 * pNode->eg *
|
||||
(pNode->vbe - psi - log(pConc / pNode->nie) - 1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pEdge = pElem->pEdge;
|
||||
dPsi = pEdge->dPsi;
|
||||
|
||||
pNode = pElem->pLeftNode;
|
||||
if (pNode->nodeType != CONTACT) {
|
||||
pRhs[pNode->psiEqn] += rDx * dPsi;
|
||||
*(pNode->fPsiPsiiP1) -= rDx;
|
||||
if (pElem->elemType == SEMICON) {
|
||||
pRhs[pNode->nEqn] -= pEdge->jn;
|
||||
pRhs[pNode->pEqn] -= pEdge->jp;
|
||||
|
||||
*(pNode->fNN) += pEdge->dJnDn;
|
||||
*(pNode->fPP) += pEdge->dJpDp;
|
||||
*(pNode->fNPsiiP1) += pEdge->dJnDpsiP1;
|
||||
*(pNode->fNNiP1) += pEdge->dJnDnP1;
|
||||
*(pNode->fPPsiiP1) += pEdge->dJpDpsiP1;
|
||||
*(pNode->fPPiP1) += pEdge->dJpDpP1;
|
||||
}
|
||||
}
|
||||
pNode = pElem->pRightNode;
|
||||
if (pNode->nodeType != CONTACT) {
|
||||
pRhs[pNode->psiEqn] -= rDx * dPsi;
|
||||
*(pNode->fPsiPsiiM1) -= rDx;
|
||||
if (pElem->elemType == SEMICON) {
|
||||
pRhs[pNode->nEqn] += pEdge->jn;
|
||||
pRhs[pNode->pEqn] += pEdge->jp;
|
||||
|
||||
|
||||
*(pNode->fNN) -= pEdge->dJnDnP1;
|
||||
*(pNode->fPP) -= pEdge->dJpDpP1;
|
||||
*(pNode->fNPsiiM1) += pEdge->dJnDpsiP1;
|
||||
*(pNode->fNNiM1) -= pEdge->dJnDn;
|
||||
*(pNode->fPPsiiM1) += pEdge->dJpDpsiP1;
|
||||
*(pNode->fPPiM1) -= pEdge->dJpDp;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (AvalancheGen) {
|
||||
/* add the generation terms */
|
||||
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) {
|
||||
pElem = pDevice->elemArray[eIndex];
|
||||
for (index = 0; index <= 1; index++) {
|
||||
if (pElem->evalNodes[index]) {
|
||||
pNode = pElem->pNodes[index];
|
||||
if ((pNode->nodeType != CONTACT) && (pElem->elemType == SEMICON)) {
|
||||
generation = ONEavalanche(FALSE, pDevice, pNode);
|
||||
pRhs[pNode->nEqn] -= generation;
|
||||
pRhs[pNode->pEqn] += generation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ONE_jacLoad(ONEdevice *pDevice)
|
||||
{
|
||||
/* used only for ac analysis */
|
||||
ONEelem *pElem;
|
||||
ONEnode *pNode, *pNode1;
|
||||
ONEedge *pEdge;
|
||||
int index, eIndex;
|
||||
double dx, rDx, dPsi;
|
||||
double rhsN, rhsP, generation;
|
||||
double fNd, fNa, fdNd, fdNa;
|
||||
double netConc, dNd, dNa, psi, nConc, pConc;
|
||||
|
||||
|
||||
/* first compute the currents and their derivatives */
|
||||
ONE_commonTerms(pDevice, FALSE, FALSE, NIL(ONEtranInfo));
|
||||
|
||||
/* zero the matrix */
|
||||
spClear(pDevice->matrix);
|
||||
|
||||
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) {
|
||||
pElem = pDevice->elemArray[eIndex];
|
||||
dx = 0.5 * pElem->dx;
|
||||
rDx = pElem->epsRel * pElem->rDx;
|
||||
/* load for all i */
|
||||
for (index = 0; index <= 1; index++) {
|
||||
pNode = pElem->pNodes[index];
|
||||
if (pNode->nodeType != CONTACT) {
|
||||
*(pNode->fPsiPsi) += rDx;
|
||||
if (pElem->elemType == SEMICON) {
|
||||
pEdge = pElem->pEdge;
|
||||
dNd = 0.0;
|
||||
dNa = 0.0;
|
||||
psi = *(pDevice->devState0 + pNode->nodePsi);
|
||||
nConc = *(pDevice->devState0 + pNode->nodeN);
|
||||
pConc = *(pDevice->devState0 + pNode->nodeP);
|
||||
if (FreezeOut) {
|
||||
ONE_freezeOut(pNode, nConc, pConc, &fNd, &fNa, &fdNd, &fdNa);
|
||||
dNd = pNode->nd * fdNd;
|
||||
dNa = pNode->na * fdNa;
|
||||
}
|
||||
*(pNode->fPsiN) += dx * (1.0 - dNd);
|
||||
*(pNode->fPsiP) -= dx * (1.0 - dNa);
|
||||
*(pNode->fNPsi) -= pEdge->dJnDpsiP1;
|
||||
*(pNode->fPPsi) -= pEdge->dJpDpsiP1;
|
||||
|
||||
if (pNode->baseType == N_TYPE) {
|
||||
*(pNode->fNPsi) += 0.5 * nConc * pNode->eg;
|
||||
*(pNode->fNN) -= 0.5 * pNode->eg
|
||||
* (pNode->vbe - psi + log(nConc / pNode->nie) + 1.0);
|
||||
}
|
||||
if (pNode->baseType == P_TYPE) {
|
||||
*(pNode->fPPsi) += 0.5 * pConc * pNode->eg;
|
||||
*(pNode->fPP) -= 0.5 * pNode->eg
|
||||
* (pNode->vbe - psi - log(pConc / pNode->nie) - 1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pNode = pElem->pLeftNode;
|
||||
if (pNode->nodeType != CONTACT) {
|
||||
pEdge = pElem->pEdge;
|
||||
dPsi = pEdge->dPsi;
|
||||
if (pElem->elemType == SEMICON) {
|
||||
*(pNode->fNN) += pEdge->dJnDn - dx * pNode->dUdN;
|
||||
*(pNode->fNP) -= dx * pNode->dUdP;
|
||||
*(pNode->fPP) += pEdge->dJpDp + dx * pNode->dUdP;
|
||||
*(pNode->fPN) += dx * pNode->dUdN;
|
||||
}
|
||||
pNode1 = pElem->pRightNode;
|
||||
if (pNode1->nodeType != CONTACT) {
|
||||
*(pNode->fPsiPsiiP1) -= rDx;
|
||||
if (pElem->elemType == SEMICON) {
|
||||
*(pNode->fNPsiiP1) += pEdge->dJnDpsiP1;
|
||||
*(pNode->fNNiP1) += pEdge->dJnDnP1;
|
||||
*(pNode->fPPsiiP1) += pEdge->dJpDpsiP1;
|
||||
*(pNode->fPPiP1) += pEdge->dJpDpP1;
|
||||
}
|
||||
}
|
||||
}
|
||||
pNode = pElem->pRightNode;
|
||||
if (pNode->nodeType != CONTACT) {
|
||||
pEdge = pElem->pEdge;
|
||||
dPsi = pEdge->dPsi;
|
||||
if (pElem->elemType == SEMICON) {
|
||||
*(pNode->fNN) += -pEdge->dJnDnP1 - dx * pNode->dUdN;
|
||||
*(pNode->fNP) -= dx * pNode->dUdP;
|
||||
*(pNode->fPP) += -pEdge->dJpDpP1 + dx * pNode->dUdP;
|
||||
*(pNode->fPN) += dx * pNode->dUdN;
|
||||
}
|
||||
pNode1 = pElem->pLeftNode;
|
||||
if (pNode1->nodeType != CONTACT) {
|
||||
*(pNode->fPsiPsiiM1) -= rDx;
|
||||
if (pElem->elemType == SEMICON) {
|
||||
*(pNode->fNPsiiM1) += pEdge->dJnDpsiP1;
|
||||
*(pNode->fNNiM1) -= pEdge->dJnDn;
|
||||
*(pNode->fPPsiiM1) += pEdge->dJpDpsiP1;
|
||||
*(pNode->fPPiM1) -= pEdge->dJpDp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (AvalancheGen) {
|
||||
/* add the generation terms */
|
||||
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) {
|
||||
pElem = pDevice->elemArray[eIndex];
|
||||
for (index = 0; index <= 1; index++) {
|
||||
if (pElem->evalNodes[index]) {
|
||||
pNode = pElem->pNodes[index];
|
||||
if ((pNode->nodeType != CONTACT) && (pElem->elemType == SEMICON)) {
|
||||
generation = ONEavalanche(FALSE, pDevice, pNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ONE_rhsLoad(ONEdevice *pDevice, BOOLEAN tranAnalysis,
|
||||
ONEtranInfo *info)
|
||||
{
|
||||
ONEelem *pElem;
|
||||
ONEnode *pNode, *pNode1;
|
||||
ONEedge *pEdge;
|
||||
int index, eIndex;
|
||||
double *pRhs = pDevice->rhs;
|
||||
double dx, rDx, dPsi;
|
||||
double rhsN, rhsP, generation;
|
||||
double perTime;
|
||||
double fNd, fNa, fdNd, fdNa;
|
||||
double netConc, dNd, dNa, psi, nConc, pConc;
|
||||
|
||||
/* first compute the currents and their derivatives */
|
||||
ONE_commonTerms(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;
|
||||
}
|
||||
|
||||
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) {
|
||||
pElem = pDevice->elemArray[eIndex];
|
||||
|
||||
dx = 0.5 * pElem->dx;
|
||||
rDx = pElem->epsRel * pElem->rDx;
|
||||
|
||||
/* load for all i */
|
||||
for (index = 0; index <= 1; index++) {
|
||||
pNode = pElem->pNodes[index];
|
||||
if (pNode->nodeType != CONTACT) {
|
||||
pRhs[pNode->psiEqn] += pNode->qf;
|
||||
if (pElem->elemType == SEMICON) {
|
||||
pEdge = pElem->pEdge;
|
||||
netConc = pNode->netConc;
|
||||
dNd = 0.0;
|
||||
dNa = 0.0;
|
||||
psi = *(pDevice->devState0 + pNode->nodePsi);
|
||||
nConc = *(pDevice->devState0 + pNode->nodeN);
|
||||
pConc = *(pDevice->devState0 + pNode->nodeP);
|
||||
if (FreezeOut) {
|
||||
ONE_freezeOut(pNode, nConc, pConc, &fNd, &fNa, &fdNd, &fdNa);
|
||||
netConc = pNode->nd * fNd - pNode->na * fNa;
|
||||
dNd = pNode->nd * fdNd;
|
||||
dNa = pNode->na * fdNa;
|
||||
}
|
||||
pRhs[pNode->psiEqn] += dx * (netConc + pConc - nConc);
|
||||
|
||||
/* Handle generation terms */
|
||||
pRhs[pNode->nEqn] -= -dx * pNode->uNet;
|
||||
pRhs[pNode->pEqn] -= dx * pNode->uNet;
|
||||
|
||||
/* Handle dXdT continuity terms */
|
||||
if (tranAnalysis) {
|
||||
pRhs[pNode->nEqn] += dx * pNode->dNdT;
|
||||
pRhs[pNode->pEqn] -= dx * pNode->dPdT;
|
||||
}
|
||||
/* Take care of base contact if necessary */
|
||||
/* eg holds the base edge mu/dx */
|
||||
if (pNode->baseType == N_TYPE) {
|
||||
pRhs[pNode->nEqn] += 0.5 * pNode->eg * nConc *
|
||||
(pNode->vbe - psi + log(nConc / pNode->nie));
|
||||
} else if (pNode->baseType == P_TYPE) {
|
||||
pRhs[pNode->pEqn] += 0.5 * pNode->eg * pConc *
|
||||
(pNode->vbe - psi - log(pConc / pNode->nie));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pEdge = pElem->pEdge;
|
||||
dPsi = pEdge->dPsi;
|
||||
|
||||
pNode = pElem->pLeftNode;
|
||||
if (pNode->nodeType != CONTACT) {
|
||||
pRhs[pNode->psiEqn] += rDx * dPsi;
|
||||
if (pElem->elemType == SEMICON) {
|
||||
pRhs[pNode->nEqn] -= pEdge->jn;
|
||||
pRhs[pNode->pEqn] -= pEdge->jp;
|
||||
}
|
||||
}
|
||||
pNode = pElem->pRightNode;
|
||||
if (pNode->nodeType != CONTACT) {
|
||||
pRhs[pNode->psiEqn] -= rDx * dPsi;
|
||||
if (pElem->elemType == SEMICON) {
|
||||
pRhs[pNode->nEqn] += pEdge->jn;
|
||||
pRhs[pNode->pEqn] += pEdge->jp;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (AvalancheGen) {
|
||||
/* add the generation terms */
|
||||
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) {
|
||||
pElem = pDevice->elemArray[eIndex];
|
||||
for (index = 0; index <= 1; index++) {
|
||||
if (pElem->evalNodes[index]) {
|
||||
pNode = pElem->pNodes[index];
|
||||
if ((pNode->nodeType != CONTACT) && (pElem->elemType == SEMICON)) {
|
||||
generation = ONEavalanche(TRUE, pDevice, pNode);
|
||||
pRhs[pNode->nEqn] -= generation;
|
||||
pRhs[pNode->pEqn] += generation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ONE_commonTerms(ONEdevice *pDevice, BOOLEAN currentOnly,
|
||||
BOOLEAN tranAnalysis, ONEtranInfo *info)
|
||||
{
|
||||
ONEelem *pElem;
|
||||
ONEedge *pEdge;
|
||||
ONEnode *pNode, *pNode1;
|
||||
int index, eIndex;
|
||||
double psi1, psi2, psi, nConc=0.0, pConc=0.0, nC, pC, nP1, pP1;
|
||||
double dPsiN, dPsiP;
|
||||
double bPsiN, dbPsiN, bMPsiN, dbMPsiN;
|
||||
double bPsiP, dbPsiP, bMPsiP, dbMPsiP;
|
||||
double mun, dMun, mup, dMup, rDx;
|
||||
double conc1, conc2;
|
||||
double cnAug, cpAug;
|
||||
|
||||
|
||||
/* evaluate all node (including recombination) and edge quantities */
|
||||
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) {
|
||||
pElem = pDevice->elemArray[eIndex];
|
||||
cnAug = pElem->matlInfo->cAug[ELEC];
|
||||
cpAug = pElem->matlInfo->cAug[HOLE];
|
||||
for (index = 0; index <= 1; index++) {
|
||||
if (pElem->evalNodes[index]) {
|
||||
pNode = pElem->pNodes[index];
|
||||
if (pNode->nodeType != CONTACT) {
|
||||
psi = pDevice->dcSolution[pNode->psiEqn];
|
||||
if (pElem->elemType == SEMICON) {
|
||||
nConc = pDevice->dcSolution[pNode->nEqn];
|
||||
pConc = pDevice->dcSolution[pNode->pEqn];
|
||||
if (Srh) {
|
||||
recomb(nConc, pConc,
|
||||
pNode->tn, pNode->tp, cnAug, cpAug, pNode->nie,
|
||||
&pNode->uNet, &pNode->dUdN, &pNode->dUdP);
|
||||
} else {
|
||||
pNode->uNet = 0.0;
|
||||
pNode->dUdN = 0.0;
|
||||
pNode->dUdP = 0.0;
|
||||
}
|
||||
if (pNode->baseType == P_TYPE && pConc <= 0.0) {
|
||||
pConc = pNode->na;
|
||||
} else if (pNode->baseType == N_TYPE && nConc <= 0.0) {
|
||||
nConc = pNode->nd;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* a contact node */
|
||||
psi = pNode->psi;
|
||||
if (pElem->elemType == SEMICON) {
|
||||
nConc = pNode->nConc;
|
||||
pConc = pNode->pConc;
|
||||
}
|
||||
}
|
||||
/* store info in the state tables */
|
||||
*(pDevice->devState0 + pNode->nodePsi) = psi;
|
||||
if (pElem->elemType == SEMICON) {
|
||||
*(pDevice->devState0 + pNode->nodeN) = nConc;
|
||||
*(pDevice->devState0 + pNode->nodeP) = pConc;
|
||||
if (tranAnalysis && pNode->nodeType != CONTACT) {
|
||||
pNode->dNdT = integrate(pDevice->devStates, info, pNode->nodeN);
|
||||
pNode->dPdT = integrate(pDevice->devStates, info, pNode->nodeP);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pEdge = pElem->pEdge;
|
||||
pNode = pElem->pLeftNode;
|
||||
if (pNode->nodeType != CONTACT) {
|
||||
psi1 = pDevice->dcSolution[pNode->psiEqn];
|
||||
} else {
|
||||
psi1 = pNode->psi;
|
||||
}
|
||||
pNode = pElem->pRightNode;
|
||||
if (pNode->nodeType != CONTACT) {
|
||||
psi2 = pDevice->dcSolution[pNode->psiEqn];
|
||||
} else {
|
||||
psi2 = pNode->psi;
|
||||
}
|
||||
pEdge->dPsi = psi2 - psi1;
|
||||
*(pDevice->devState0 + pEdge->edgeDpsi) = pEdge->dPsi;
|
||||
}
|
||||
/* calculate the current densities and mobility values */
|
||||
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) {
|
||||
pElem = pDevice->elemArray[eIndex];
|
||||
pEdge = pElem->pEdge;
|
||||
if (pElem->elemType == SEMICON) {
|
||||
dPsiN = pEdge->dPsi + pEdge->dCBand;
|
||||
dPsiP = pEdge->dPsi - pEdge->dVBand;
|
||||
bernoulli(dPsiN, &bPsiN, &dbPsiN, &bMPsiN, &dbMPsiN, !currentOnly);
|
||||
bernoulli(dPsiP, &bPsiP, &dbPsiP, &bMPsiP, &dbMPsiP, !currentOnly);
|
||||
nC = *(pDevice->devState0 + pElem->pLeftNode->nodeN);
|
||||
nP1 = *(pDevice->devState0 + pElem->pRightNode->nodeN);
|
||||
pC = *(pDevice->devState0 + pElem->pLeftNode->nodeP);
|
||||
pP1 = *(pDevice->devState0 + pElem->pRightNode->nodeP);
|
||||
conc1 = pElem->pLeftNode->totalConc;
|
||||
conc2 = pElem->pRightNode->totalConc;
|
||||
pEdge->jn = (bPsiN * nP1 - bMPsiN * nC);
|
||||
pEdge->jp = (bPsiP * pC - bMPsiP * pP1);
|
||||
|
||||
|
||||
mun = pEdge->mun;
|
||||
dMun = 0.0;
|
||||
mup = pEdge->mup;
|
||||
dMup = 0.0;
|
||||
MOBfieldDep(pElem->matlInfo, ELEC, dPsiN * pElem->rDx, &mun, &dMun);
|
||||
MOBfieldDep(pElem->matlInfo, HOLE, dPsiP * pElem->rDx, &mup, &dMup);
|
||||
|
||||
|
||||
mun *= pElem->rDx;
|
||||
dMun *= pElem->rDx * pElem->rDx;
|
||||
mup *= pElem->rDx;
|
||||
dMup *= pElem->rDx * pElem->rDx;
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* The base continuity equation makes use of mu/dx in eg. The base
|
||||
* length has already been calculated and converted to normalized,
|
||||
* reciprocal form during setup. The name should be changed, but that's
|
||||
* a big hassle.
|
||||
*/
|
||||
for (index = 0; index <= 1; index++) {
|
||||
if (pElem->evalNodes[index]) {
|
||||
pNode = pElem->pNodes[index];
|
||||
if (pNode->baseType == N_TYPE) {
|
||||
pNode->eg = pEdge->mun * pDevice->baseLength;
|
||||
} else if (pNode->baseType == P_TYPE) {
|
||||
pNode->eg = pEdge->mup * pDevice->baseLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pEdge->jn *= mun;
|
||||
pEdge->jp *= mup;
|
||||
|
||||
|
||||
if (!currentOnly) {
|
||||
if (dMun == 0.0) {
|
||||
pEdge->dJnDpsiP1 = mun * (dbPsiN * nP1 - dbMPsiN * nC);
|
||||
} else {
|
||||
pEdge->dJnDpsiP1 = dMun * (bPsiN * nP1 - bMPsiN * nC)
|
||||
+ mun * (dbPsiN * nP1 - dbMPsiN * nC);
|
||||
}
|
||||
pEdge->dJnDn = -mun * bMPsiN;
|
||||
pEdge->dJnDnP1 = mun * bPsiN;
|
||||
if (dMup == 0.0) {
|
||||
pEdge->dJpDpsiP1 = mup * (dbPsiP * pC - dbMPsiP * pP1);
|
||||
} else {
|
||||
pEdge->dJpDpsiP1 = dMup * (bPsiP * pC - bMPsiP * pP1)
|
||||
+ mup * (dbPsiP * pC - dbMPsiP * pP1);
|
||||
}
|
||||
pEdge->dJpDp = mup * bPsiP;
|
||||
pEdge->dJpDpP1 = -mup * bMPsiP;
|
||||
}
|
||||
}
|
||||
if (tranAnalysis) {
|
||||
pEdge->jd = -integrate(pDevice->devStates, info,
|
||||
pEdge->edgeDpsi) * pElem->rDx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 2001 Paolo Nenzi
|
||||
*/
|
||||
|
||||
#ifndef _ONEDDEFS_H
|
||||
#define _ONEDDEFS_H
|
||||
|
||||
/* Debug statements */
|
||||
|
||||
extern BOOLEAN ONEacDebug;
|
||||
extern BOOLEAN ONEdcDebug;
|
||||
extern BOOLEAN ONEtranDebug;
|
||||
extern BOOLEAN ONEjacDebug;
|
||||
|
||||
/* Now some defines for the one dimensional simulator
|
||||
* library.
|
||||
* Theese defines were gathered from all the code in
|
||||
* oned directory.
|
||||
*/
|
||||
|
||||
#define LEVEL_ALPHA_SI 3.1e-8 /* From de Graaf & Klaasen, pg. 12 */
|
||||
#define MIN_DELV 1e-3
|
||||
#define NORM_RED_MAXITERS 10
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
/**********
|
||||
Copyright 1991 Regents of the University of California. All rights reserved.
|
||||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numglobs.h"
|
||||
#include "numenum.h"
|
||||
#include "onedev.h"
|
||||
#include "onemesh.h"
|
||||
#include "spMatrix.h"
|
||||
#include "onedext.h"
|
||||
#include "oneddefs.h"
|
||||
|
||||
void
|
||||
ONEdestroy(ONEdevice *pDevice)
|
||||
{
|
||||
int index, eIndex;
|
||||
ONEelem *pElem;
|
||||
ONEnode *pNode;
|
||||
ONEedge *pEdge;
|
||||
|
||||
|
||||
if (!pDevice)
|
||||
return;
|
||||
|
||||
switch (pDevice->solverType) {
|
||||
case SLV_SMSIG:
|
||||
case SLV_BIAS:
|
||||
/* free up memory allocated for the bias solution */
|
||||
FREE(pDevice->dcSolution);
|
||||
FREE(pDevice->dcDeltaSolution);
|
||||
FREE(pDevice->copiedSolution);
|
||||
FREE(pDevice->rhs);
|
||||
FREE(pDevice->rhsImag);
|
||||
spDestroy(pDevice->matrix);
|
||||
break;
|
||||
case SLV_EQUIL:
|
||||
/* free up the vectors allocated in the equilibrium solution */
|
||||
FREE(pDevice->dcSolution);
|
||||
FREE(pDevice->dcDeltaSolution);
|
||||
FREE(pDevice->copiedSolution);
|
||||
FREE(pDevice->rhs);
|
||||
spDestroy(pDevice->matrix);
|
||||
break;
|
||||
case SLV_NONE:
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Panic: Unknown solver type in ONEdestroy.\n");
|
||||
exit(-1);
|
||||
break;
|
||||
}
|
||||
|
||||
/* destroy the mesh */
|
||||
if (pDevice->elemArray) {
|
||||
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) {
|
||||
pElem = pDevice->elemArray[eIndex];
|
||||
for (index = 0; index <= 2; index++) {
|
||||
if (pElem->evalNodes[index]) {
|
||||
pNode = pElem->pNodes[index];
|
||||
FREE(pNode);
|
||||
}
|
||||
pEdge = pElem->pEdge;
|
||||
FREE(pEdge);
|
||||
}
|
||||
FREE(pElem);
|
||||
}
|
||||
FREE(pDevice->elemArray);
|
||||
}
|
||||
/* destroy any other lists */
|
||||
/* NOT IMPLEMENTED */
|
||||
|
||||
FREE(pDevice);
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* 2001 Paolo Nenzi
|
||||
*/
|
||||
|
||||
/* External symbols for ONE Dimensional simulator */
|
||||
|
||||
#ifndef _ONEDEXT_H
|
||||
#define _ONEDEXT_H
|
||||
|
||||
#include "profile.h"
|
||||
#include "onemesh.h"
|
||||
#include "onedev.h"
|
||||
#include "carddefs.h"
|
||||
#include "bool.h"
|
||||
#include "complex.h"
|
||||
|
||||
/* oneadmit.c */
|
||||
extern int NUMDadmittance(ONEdevice *, double, SPcomplex *);
|
||||
extern int NBJTadmittance(ONEdevice *, double, SPcomplex *,
|
||||
SPcomplex *, SPcomplex *,
|
||||
SPcomplex *);
|
||||
extern BOOLEAN ONEsorSolve(ONEdevice *, double *, double *, double omega);
|
||||
extern void NUMDys(ONEdevice *, SPcomplex *, SPcomplex *);
|
||||
extern void NBJTys(ONEdevice *,SPcomplex *, SPcomplex *, SPcomplex *,
|
||||
SPcomplex *, SPcomplex *);
|
||||
|
||||
extern SPcomplex *computeAdmittance(ONEnode *, BOOLEAN, double *xReal,
|
||||
double *,SPcomplex *);
|
||||
|
||||
/* oneaval.c */
|
||||
extern double ONEavalanche(BOOLEAN, ONEdevice *, ONEnode *);
|
||||
|
||||
/* onecond.c */
|
||||
extern void NUMDconductance(ONEdevice *, BOOLEAN, double *, double *);
|
||||
extern void NBJTconductance(ONEdevice *, BOOLEAN, double *, double *,
|
||||
double *, double *, double *);
|
||||
extern void NUMDcurrent(ONEdevice *, BOOLEAN, double *, double *);
|
||||
extern void NBJTcurrent(ONEdevice *, BOOLEAN, double *, double *,
|
||||
double *);
|
||||
|
||||
|
||||
/* onecont */
|
||||
extern void ONE_jacBuild(ONEdevice *);
|
||||
extern void ONE_sysLoad(ONEdevice *, BOOLEAN, ONEtranInfo *);
|
||||
extern void ONE_jacLoad(ONEdevice *);
|
||||
extern void ONE_rhsLoad(ONEdevice *, BOOLEAN, ONEtranInfo *);
|
||||
extern void ONE_commonTerms(ONEdevice *, BOOLEAN, BOOLEAN, ONEtranInfo *);
|
||||
|
||||
|
||||
/* onedest */
|
||||
extern void ONEdestroy(ONEdevice *);
|
||||
|
||||
/* onedopng.c */
|
||||
extern double ONEdopingValue(DOPprofile *, DOPtable *, double );
|
||||
extern void ONEsetDoping(ONEdevice *, DOPprofile *, DOPtable *);
|
||||
|
||||
/* onefreez.c */
|
||||
extern void ONEQfreezeOut(ONEnode *, double *, double *, double *,
|
||||
double *);
|
||||
extern void ONE_freezeOut(ONEnode *, double, double, double *,
|
||||
double*, double *, double *);
|
||||
|
||||
|
||||
/* onemesh.c */
|
||||
extern void ONEbuildMesh(ONEdevice *, ONEcoord *, ONEdomain *,
|
||||
ONEmaterial *);
|
||||
extern void ONEgetStatePointers(ONEdevice *, int *);
|
||||
extern void adjustBaseContact(ONEdevice *, int, int);
|
||||
extern void NBJTjunctions(ONEdevice *, int *, int *);
|
||||
extern void ONEprnMesh(ONEdevice *);
|
||||
|
||||
/* onepoiss.c */
|
||||
extern void ONEQjacBuild(ONEdevice *);
|
||||
extern void ONEQsysLoad(ONEdevice *);
|
||||
extern void ONEQrhsLoad(ONEdevice *);
|
||||
extern void ONEQcommonTerms(ONEdevice *);
|
||||
|
||||
/*oneprint.c */
|
||||
extern void ONEprnSolution(FILE *, ONEdevice *, OUTPcard *);
|
||||
extern void ONEmemStats(FILE *, ONEdevice *);
|
||||
extern void ONEcpuStats(FILE *f, ONEdevice *);
|
||||
|
||||
|
||||
/* oneproj.c */
|
||||
extern void NUMDproject(ONEdevice *, double);
|
||||
extern void NBJTproject(ONEdevice *, double, double, double);
|
||||
extern void NUMDupdate(ONEdevice *,double, BOOLEAN);
|
||||
extern void NBJTupdate(ONEdevice *, double, double, double, BOOLEAN);
|
||||
extern void NUMDsetBCs(ONEdevice *, double);
|
||||
extern void NBJTsetBCs(ONEdevice *, double, double);
|
||||
|
||||
/* oneread.c */
|
||||
extern int ONEreadState(ONEdevice *, char *, int, double *, double *);
|
||||
|
||||
/*onesetup.c */
|
||||
extern void ONEsetup(ONEdevice *);
|
||||
extern void ONEsetBCparams(ONEdevice *, BDRYcard *, CONTcard *);
|
||||
extern void ONEnormalize(ONEdevice *);
|
||||
|
||||
/* onesolve.c */
|
||||
extern void ONEdcSolve(ONEdevice *, int, BOOLEAN, BOOLEAN, ONEtranInfo *);
|
||||
extern BOOLEAN ONEpsiDeltaConverged(ONEdevice *, int *);
|
||||
extern BOOLEAN ONEdeltaConverged(ONEdevice *);
|
||||
extern BOOLEAN ONEdeltaConverged(ONEdevice *);
|
||||
extern void ONEresetJacobian(ONEdevice *);
|
||||
extern void ONEstoreNeutralGuess(ONEdevice *);
|
||||
extern void ONEequilSolve(ONEdevice *);
|
||||
extern void ONEbiasSolve(ONEdevice *, int, BOOLEAN, ONEtranInfo *);
|
||||
extern void ONEstoreEquilibGuess(ONEdevice *);
|
||||
extern void ONEstoreInitialGuess(ONEdevice *);
|
||||
extern int ONEnewDelta(ONEdevice *, BOOLEAN, ONEtranInfo *);
|
||||
extern int ONEnewDelta(ONEdevice *, BOOLEAN, ONEtranInfo *);
|
||||
extern double ONEtrunc(ONEdevice *, ONEtranInfo *, double);
|
||||
extern void ONEsaveState(ONEdevice *);
|
||||
extern double ONEnuNorm(ONEdevice *);
|
||||
extern void ONEjacCheck(ONEdevice *, BOOLEAN, ONEtranInfo *);
|
||||
extern void ONEpredict(ONEdevice *, ONEtranInfo *);
|
||||
extern BOOLEAN ONEdeviceConverged(ONEdevice *);
|
||||
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
/**********
|
||||
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
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numglobs.h"
|
||||
#include "numenum.h"
|
||||
#include "onemesh.h"
|
||||
#include "onedev.h"
|
||||
#include "profile.h"
|
||||
#include "macros.h"
|
||||
#include "onedext.h"
|
||||
#include "oneddefs.h"
|
||||
#include "cidersupt.h"
|
||||
|
||||
/* functions in this file are used to calculate the conc */
|
||||
|
||||
double
|
||||
ONEdopingValue(DOPprofile *pProfile, DOPtable *pTable, double x)
|
||||
{
|
||||
double argX, argP, value=0.0;
|
||||
|
||||
|
||||
/* Find the appropriate lookup table if necessary */
|
||||
if (pProfile->type == LOOKUP) {
|
||||
while (pTable != NIL(DOPtable)) {
|
||||
if (pTable->impId == pProfile->IMPID) {
|
||||
/* Found it */
|
||||
break;
|
||||
} else {
|
||||
pTable = pTable->next;
|
||||
}
|
||||
}
|
||||
if (pTable == NIL(DOPtable)) {
|
||||
fprintf(stderr, "Error: unknown impurity profile %d\n",
|
||||
((int)pProfile->IMPID));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
/* Find distances */
|
||||
if (pProfile->X_LOW > x) {
|
||||
argX = pProfile->X_LOW - x;
|
||||
} else if (x > pProfile->X_HIGH) {
|
||||
argX = x - pProfile->X_HIGH;
|
||||
} else {
|
||||
argX = 0.0;
|
||||
}
|
||||
|
||||
argP = argX;
|
||||
|
||||
/* Transform to coordinates of profile peak */
|
||||
argP -= pProfile->LOCATION;
|
||||
argP /= pProfile->CHAR_LENGTH;
|
||||
|
||||
switch (pProfile->type) {
|
||||
case UNIF:
|
||||
if (argP > 0.0) {
|
||||
value = 0.0;
|
||||
} else {
|
||||
value = pProfile->CONC;
|
||||
}
|
||||
break;
|
||||
case LIN:
|
||||
argP = ABS(argP);
|
||||
if (argP > 1.0) {
|
||||
value = 0.0;
|
||||
} else {
|
||||
value = pProfile->CONC * (1.0 - argP);
|
||||
}
|
||||
break;
|
||||
case GAUSS:
|
||||
argP *= argP;
|
||||
if (argP > 80.0) {
|
||||
value = 0.0;
|
||||
} else {
|
||||
value = pProfile->PEAK_CONC * exp(-argP);
|
||||
}
|
||||
case EXP:
|
||||
argP = ABS(argP);
|
||||
if (argP > 80.0) {
|
||||
value = 0.0;
|
||||
} else {
|
||||
value = pProfile->PEAK_CONC * exp(-argP);
|
||||
}
|
||||
break;
|
||||
case ERRFC:
|
||||
argP = ABS(argP);
|
||||
if (argP > 10.0) {
|
||||
value = 0.0;
|
||||
} else {
|
||||
value = pProfile->PEAK_CONC * erfc(-argP);
|
||||
}
|
||||
break;
|
||||
case LOOKUP:
|
||||
argP = ABS(argP);
|
||||
value = lookup(pTable->dopData, argP);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return (value);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ONEsetDoping(ONEdevice *pDevice, DOPprofile *pProfile, DOPtable *pTable)
|
||||
{
|
||||
ONEnode *pNode;
|
||||
ONEelem *pElem;
|
||||
DOPprofile *pP;
|
||||
double conc;
|
||||
int index, eIndex;
|
||||
BOOLEAN dopeMe;
|
||||
|
||||
|
||||
/* Clear doping info for all nodes. */
|
||||
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) {
|
||||
pElem = pDevice->elemArray[eIndex];
|
||||
for (index = 0; index <= 1; index++) {
|
||||
if (pElem->evalNodes[index]) {
|
||||
pNode = pElem->pNodes[index];
|
||||
pNode->na = 0.0;
|
||||
pNode->nd = 0.0;
|
||||
pNode->netConc = 0.0;
|
||||
pNode->totalConc = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Now compute the contribution to the total doping from each profile. */
|
||||
for (pP = pProfile; pP != NIL(DOPprofile); pP = pP->next) {
|
||||
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) {
|
||||
pElem = pDevice->elemArray[eIndex];
|
||||
if (pElem->elemType == SEMICON) {
|
||||
if (pP->numDomains > 0) {
|
||||
dopeMe = FALSE;
|
||||
for (index = 0; index < pP->numDomains; index++) {
|
||||
if (pElem->domain == pP->domains[index]) {
|
||||
dopeMe = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else { /* domains not given, so dope all */
|
||||
dopeMe = TRUE;
|
||||
}
|
||||
if (dopeMe) {
|
||||
for (index = 0; index <= 1; index++) {
|
||||
if (pElem->evalNodes[index]) {
|
||||
pNode = pElem->pNodes[index];
|
||||
conc = ONEdopingValue(pP, pTable, pNode->x);
|
||||
pNode->netConc += conc;
|
||||
if (conc < 0.0) {
|
||||
pNode->totalConc -= conc;
|
||||
pNode->na -= conc;
|
||||
} else {
|
||||
pNode->totalConc += conc;
|
||||
pNode->nd += conc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
/**********
|
||||
Copyright 1992 Regents of the University of California. All rights reserved.
|
||||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numglobs.h"
|
||||
#include "numconst.h"
|
||||
#include "onemesh.h"
|
||||
#include "../../maths/misc/accuracy.h"
|
||||
#include "onedext.h"
|
||||
#include "oneddefs.h"
|
||||
|
||||
|
||||
void
|
||||
ONEQfreezeOut(ONEnode *pNode, double *ndFac, double *naFac, double *dNdFac,
|
||||
double *dNaFac)
|
||||
{
|
||||
double temp1, temp2;
|
||||
double eLev;
|
||||
ONEmaterial *info;
|
||||
|
||||
|
||||
if (pNode->pRightElem && pNode->pRightElem->evalNodes[0]) {
|
||||
info = pNode->pRightElem->matlInfo;
|
||||
} else {
|
||||
info = pNode->pLeftElem->matlInfo;
|
||||
}
|
||||
|
||||
eLev = info->eDon;
|
||||
if (info->material != GAAS) {
|
||||
eLev -= LEVEL_ALPHA_SI * pow(pNode->nd * NNorm, 1.0 / 3.0);
|
||||
if (eLev < 0.0)
|
||||
eLev = 0.0;
|
||||
}
|
||||
if (eLev >= ExpLim) {
|
||||
*ndFac = 0.0;
|
||||
*dNdFac = 0.0;
|
||||
} else if (eLev <= -ExpLim) {
|
||||
*ndFac = 1.0;
|
||||
*dNdFac = 0.0;
|
||||
} else {
|
||||
temp1 = info->gDon * pNode->nConc * NNorm * exp(eLev) / info->nc0;
|
||||
temp2 = 1.0 / (1.0 + temp1);
|
||||
*ndFac = temp2;
|
||||
*dNdFac = -temp2 * temp2 * temp1;
|
||||
}
|
||||
|
||||
eLev = info->eAcc;
|
||||
if (info->material != GAAS) {
|
||||
eLev -= LEVEL_ALPHA_SI * pow(pNode->na * NNorm, 1.0 / 3.0);
|
||||
if (eLev < 0.0)
|
||||
eLev = 0.0;
|
||||
}
|
||||
if (eLev >= ExpLim) {
|
||||
*naFac = 0.0;
|
||||
*dNaFac = 0.0;
|
||||
} else if (eLev <= -ExpLim) {
|
||||
*naFac = 1.0;
|
||||
*dNaFac = 0.0;
|
||||
} else {
|
||||
temp1 = info->gAcc * pNode->pConc * NNorm * exp(eLev) / info->nv0;
|
||||
temp2 = 1.0 / (1.0 + temp1);
|
||||
*naFac = temp2;
|
||||
*dNaFac = temp2 * temp2 * temp1;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ONE_freezeOut(ONEnode *pNode, double nConc, double pConc, double *ndFac,
|
||||
double *naFac, double *dNdFac, double *dNaFac)
|
||||
{
|
||||
double temp1, temp2;
|
||||
double eLev;
|
||||
ONEmaterial *info;
|
||||
|
||||
|
||||
if (pNode->pRightElem && pNode->pRightElem->evalNodes[0]) {
|
||||
info = pNode->pRightElem->matlInfo;
|
||||
} else {
|
||||
info = pNode->pLeftElem->matlInfo;
|
||||
}
|
||||
|
||||
eLev = info->eDon;
|
||||
if (info->material != GAAS) {
|
||||
eLev -= LEVEL_ALPHA_SI * pow(pNode->nd * NNorm, 1.0 / 3.0);
|
||||
if (eLev < 0.0)
|
||||
eLev = 0.0;
|
||||
}
|
||||
if (eLev >= ExpLim) {
|
||||
*ndFac = 0.0;
|
||||
*dNdFac = 0.0;
|
||||
} else if (eLev <= -ExpLim) {
|
||||
*ndFac = 1.0;
|
||||
*dNdFac = 0.0;
|
||||
} else {
|
||||
temp1 = info->gDon * exp(eLev) * NNorm / info->nc0;
|
||||
temp2 = 1.0 / (1.0 + nConc * temp1);
|
||||
*ndFac = temp2;
|
||||
*dNdFac = -temp2 * temp2 * temp1;
|
||||
}
|
||||
|
||||
eLev = info->eAcc;
|
||||
if (info->material != GAAS) {
|
||||
eLev -= LEVEL_ALPHA_SI * pow(pNode->na * NNorm, 1.0 / 3.0);
|
||||
if (eLev < 0.0)
|
||||
eLev = 0.0;
|
||||
}
|
||||
if (eLev >= ExpLim) {
|
||||
*naFac = 0.0;
|
||||
*dNaFac = 0.0;
|
||||
} else if (eLev <= -ExpLim) {
|
||||
*naFac = 1.0;
|
||||
*dNaFac = 0.0;
|
||||
} else {
|
||||
temp1 = info->gAcc * exp(eLev) * NNorm / info->nv0;
|
||||
temp2 = 1.0 / (1.0 + pConc * temp1);
|
||||
*naFac = temp2;
|
||||
*dNaFac = -temp2 * temp2 * temp1;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,371 @@
|
|||
/**********
|
||||
Copyright 1992 Regents of the University of California. All rights reserved.
|
||||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numglobs.h"
|
||||
#include "numenum.h"
|
||||
#include "onemesh.h"
|
||||
#include "onedev.h"
|
||||
#include "macros.h"
|
||||
#include "onedext.h"
|
||||
#include "oneddefs.h"
|
||||
|
||||
|
||||
/* Forward Declarations */
|
||||
static void ONEresetEvalFlag(ONEdevice *);
|
||||
|
||||
void
|
||||
ONEbuildMesh(ONEdevice *pDevice, ONEcoord *pCoord, ONEdomain *pDomain,
|
||||
ONEmaterial *pMaterial)
|
||||
{
|
||||
int index, i;
|
||||
int elemType;
|
||||
double xPos;
|
||||
ONEcoord *pC;
|
||||
ONEnode *pNode, *pNextNode;
|
||||
ONEdomain *pD;
|
||||
ONEelem *pElem;
|
||||
ONEmaterial *pM;
|
||||
int poiEqn, numEqn;
|
||||
ONEedge *pEdge;
|
||||
ONEnode **nodeArray=NULL;
|
||||
BOOLEAN error = FALSE;
|
||||
|
||||
|
||||
/* generate the work array for setting up nodes and elements */
|
||||
XCALLOC(nodeArray, ONEnode *, 1 + pDevice->numNodes);
|
||||
|
||||
for (pC = pCoord; pC != NIL(ONEcoord); 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 == NIL(ONEdomain)) {
|
||||
fprintf(stderr, "Error: domains not defined for device\n");
|
||||
exit(-1);
|
||||
}
|
||||
for (pD = pDomain; pD != NIL(ONEdomain); pD = pD->next) {
|
||||
for (pM = pMaterial; pM != NIL(ONEmaterial); 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) {
|
||||
printf("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 != NIL(ONEdomain); pD = pD->next) {
|
||||
for (pM = pMaterial; pM != NIL(ONEmaterial); 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;
|
||||
|
||||
/*
|
||||
* 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 = 0.5 * (indexEB + indexBC);
|
||||
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;
|
||||
BOOLEAN findFirstJunction = TRUE;
|
||||
BOOLEAN 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;
|
||||
ONEedge *pEdge;
|
||||
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);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,192 @@
|
|||
/**********
|
||||
Copyright 1992 Regents of the University of California. All rights reserved.
|
||||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numglobs.h"
|
||||
#include "numenum.h"
|
||||
#include "onemesh.h"
|
||||
#include "onedev.h"
|
||||
#include "onedext.h"
|
||||
#include "oneddefs.h"
|
||||
#include "spMatrix.h"
|
||||
|
||||
/* Functions to setup and solve the 1D poisson equation. */
|
||||
|
||||
|
||||
/* Forward Declarations */
|
||||
void ONEQcommonTerms(ONEdevice *pDevice);
|
||||
|
||||
void
|
||||
ONEQjacBuild(ONEdevice *pDevice)
|
||||
{
|
||||
char *matrix = pDevice->matrix;
|
||||
ONEelem *pElem;
|
||||
ONEnode *pNode, *pNode1;
|
||||
int index;
|
||||
|
||||
|
||||
for (index = 1; index < pDevice->numNodes; index++) {
|
||||
pElem = pDevice->elemArray[index];
|
||||
pNode = pElem->pLeftNode;
|
||||
pNode->fPsiPsi = spGetElement(matrix, pNode->poiEqn, pNode->poiEqn);
|
||||
pNode1 = pElem->pRightNode;
|
||||
pNode->fPsiPsiiP1 = spGetElement(matrix, pNode->poiEqn, pNode1->poiEqn);
|
||||
pNode = pElem->pRightNode;
|
||||
pNode->fPsiPsi = spGetElement(matrix, pNode->poiEqn, pNode->poiEqn);
|
||||
pNode1 = pElem->pLeftNode;
|
||||
pNode->fPsiPsiiM1 = spGetElement(matrix, pNode->poiEqn, pNode1->poiEqn);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ONEQsysLoad(ONEdevice *pDevice)
|
||||
{
|
||||
ONEelem *pElem;
|
||||
ONEnode *pNode, *pNode1;
|
||||
int index, i;
|
||||
double *pRhs = pDevice->rhs;
|
||||
double rDx, dPsi;
|
||||
double netConc, dNetConc;
|
||||
double fNd, fdNd, fNa, fdNa;
|
||||
|
||||
|
||||
ONEQcommonTerms(pDevice);
|
||||
|
||||
/* zero the rhs vector */
|
||||
for (index = 1; index <= pDevice->numEqns; index++) {
|
||||
pRhs[index] = 0.0;
|
||||
}
|
||||
|
||||
/* zero the matrix */
|
||||
spClear(pDevice->matrix);
|
||||
|
||||
for (index = 1; index < pDevice->numNodes; index++) {
|
||||
pElem = pDevice->elemArray[index];
|
||||
rDx = pElem->epsRel * pElem->rDx;
|
||||
for (i = 0; i <= 1; i++) {
|
||||
pNode = pElem->pNodes[i];
|
||||
if (pNode->nodeType != CONTACT) {
|
||||
*(pNode->fPsiPsi) += rDx;
|
||||
pRhs[pNode->poiEqn] += pNode->qf;
|
||||
if (pElem->elemType == SEMICON) {
|
||||
netConc = pNode->netConc;
|
||||
dNetConc = 0.0;
|
||||
if (FreezeOut) {
|
||||
ONEQfreezeOut(pNode, &fNd, &fNa, &fdNd, &fdNa);
|
||||
netConc = pNode->nd * fNd - pNode->na * fNa;
|
||||
dNetConc = pNode->nd * fdNd - pNode->na * fdNa;
|
||||
}
|
||||
*(pNode->fPsiPsi) += 0.5 * pElem->dx *
|
||||
(pNode->nConc + pNode->pConc - dNetConc);
|
||||
pRhs[pNode->poiEqn] += 0.5 * pElem->dx *
|
||||
(netConc + pNode->pConc - pNode->nConc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dPsi = pElem->pEdge->dPsi;
|
||||
|
||||
pNode = pElem->pLeftNode;
|
||||
pRhs[pNode->poiEqn] += rDx * dPsi;
|
||||
*(pNode->fPsiPsiiP1) -= rDx;
|
||||
|
||||
pNode = pElem->pRightNode;
|
||||
pRhs[pNode->poiEqn] -= rDx * dPsi;
|
||||
*(pNode->fPsiPsiiM1) -= rDx;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ONEQrhsLoad(ONEdevice *pDevice)
|
||||
{
|
||||
ONEelem *pElem;
|
||||
ONEnode *pNode;
|
||||
int index, i;
|
||||
double *pRhs = pDevice->rhs;
|
||||
double rDx, dPsi;
|
||||
double fNd, fNa, fdNd, fdNa;
|
||||
double netConc;
|
||||
|
||||
|
||||
ONEQcommonTerms(pDevice);
|
||||
|
||||
/* zero the rhs vector */
|
||||
for (index = 1; index <= pDevice->numEqns; index++) {
|
||||
pRhs[index] = 0.0;
|
||||
}
|
||||
|
||||
for (index = 1; index < pDevice->numNodes; index++) {
|
||||
pElem = pDevice->elemArray[index];
|
||||
rDx = pElem->epsRel * pElem->rDx;
|
||||
for (i = 0; i <= 1; i++) {
|
||||
pNode = pElem->pNodes[i];
|
||||
if (pNode->nodeType != CONTACT) {
|
||||
pRhs[pNode->poiEqn] += pNode->qf;
|
||||
if (pElem->elemType == SEMICON) {
|
||||
netConc = pNode->netConc;
|
||||
if (FreezeOut) {
|
||||
ONEQfreezeOut(pNode, &fNd, &fNa, &fdNd, &fdNa);
|
||||
netConc = pNode->nd * fNd - pNode->na * fNa;
|
||||
}
|
||||
pRhs[pNode->poiEqn] += 0.5 * pElem->dx *
|
||||
(netConc + pNode->pConc - pNode->nConc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dPsi = pElem->pEdge->dPsi;
|
||||
|
||||
pNode = pElem->pLeftNode;
|
||||
pRhs[pNode->poiEqn] += rDx * dPsi;
|
||||
|
||||
pNode = pElem->pRightNode;
|
||||
pRhs[pNode->poiEqn] -= rDx * dPsi;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ONEQcommonTerms(ONEdevice *pDevice)
|
||||
{
|
||||
ONEelem *pElem;
|
||||
ONEedge *pEdge;
|
||||
ONEnode *pNode, *pNode1;
|
||||
int i, index;
|
||||
double psi1, psi2, refPsi;
|
||||
|
||||
|
||||
for (index = 1; index < pDevice->numNodes; index++) {
|
||||
pElem = pDevice->elemArray[index];
|
||||
refPsi = pElem->matlInfo->refPsi;
|
||||
for (i = 0; i <= 1; i++) {
|
||||
if (pElem->evalNodes[i]) {
|
||||
pNode = pElem->pNodes[i];
|
||||
if (pNode->nodeType != CONTACT) {
|
||||
pNode->psi = pDevice->dcSolution[pNode->poiEqn];
|
||||
if (pElem->elemType == SEMICON) {
|
||||
pNode->nConc = pNode->nie * exp(pNode->psi - refPsi);
|
||||
pNode->pConc = pNode->nie * exp(-pNode->psi + refPsi);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pEdge = pElem->pEdge;
|
||||
pNode = pElem->pNodes[0];
|
||||
if (pNode->nodeType != CONTACT) {
|
||||
psi1 = pDevice->dcSolution[pNode->poiEqn];
|
||||
} else {
|
||||
psi1 = pNode->psi;
|
||||
}
|
||||
pNode = pElem->pNodes[1];
|
||||
if (pNode->nodeType != CONTACT) {
|
||||
psi2 = pDevice->dcSolution[pNode->poiEqn];
|
||||
} else {
|
||||
psi2 = pNode->psi;
|
||||
}
|
||||
pEdge->dPsi = psi2 - psi1;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,534 @@
|
|||
/**********
|
||||
Copyright 1992 Regents of the University of California. All rights reserved.
|
||||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group
|
||||
Author: 1992 David A. Gates, U. C. Berkeley CAD Group
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numglobs.h"
|
||||
#include "numconst.h"
|
||||
#include "numenum.h"
|
||||
#include "onemesh.h"
|
||||
#include "onedev.h"
|
||||
#include "carddefs.h"
|
||||
#include "spMatrix.h"
|
||||
#include "onedext.h"
|
||||
#include "oneddefs.h"
|
||||
|
||||
void
|
||||
ONEprnSolution(FILE *file, ONEdevice *pDevice, OUTPcard *output)
|
||||
{
|
||||
int index, i;
|
||||
int numVars = 0;
|
||||
ONEnode **nodeArray=NULL;
|
||||
ONEnode *pNode;
|
||||
ONEelem *pElem, *pPrevElem;
|
||||
ONEmaterial *info=NULL;
|
||||
double data[50];
|
||||
double eField, refPsi = 0.0, eGap, dGap;
|
||||
double mun, mup, jc, jd, jn, jp, jt;
|
||||
double coeff1, coeff2;
|
||||
|
||||
|
||||
if (output->OUTPnumVars == -1) {
|
||||
/* First pass. Need to count number of variables in output. */
|
||||
numVars++; /* For the X scale */
|
||||
if (output->OUTPdoping) {
|
||||
numVars++;
|
||||
}
|
||||
if (output->OUTPpsi) {
|
||||
numVars++;
|
||||
}
|
||||
if (output->OUTPequPsi) {
|
||||
numVars++;
|
||||
}
|
||||
if (output->OUTPvacPsi) {
|
||||
numVars++;
|
||||
}
|
||||
if (output->OUTPnConc) {
|
||||
numVars++;
|
||||
}
|
||||
if (output->OUTPpConc) {
|
||||
numVars++;
|
||||
}
|
||||
if (output->OUTPphin) {
|
||||
numVars++;
|
||||
}
|
||||
if (output->OUTPphip) {
|
||||
numVars++;
|
||||
}
|
||||
if (output->OUTPphic) {
|
||||
numVars++;
|
||||
}
|
||||
if (output->OUTPphiv) {
|
||||
numVars++;
|
||||
}
|
||||
if (output->OUTPeField) {
|
||||
numVars++;
|
||||
}
|
||||
if (output->OUTPjc) {
|
||||
numVars++;
|
||||
}
|
||||
if (output->OUTPjd) {
|
||||
numVars++;
|
||||
}
|
||||
if (output->OUTPjn) {
|
||||
numVars++;
|
||||
}
|
||||
if (output->OUTPjp) {
|
||||
numVars++;
|
||||
}
|
||||
if (output->OUTPjt) {
|
||||
numVars++;
|
||||
}
|
||||
if (output->OUTPuNet) {
|
||||
numVars++;
|
||||
}
|
||||
if (output->OUTPmun) {
|
||||
numVars++;
|
||||
}
|
||||
if (output->OUTPmup) {
|
||||
numVars++;
|
||||
}
|
||||
output->OUTPnumVars = numVars;
|
||||
}
|
||||
/* generate the work array for printing node info */
|
||||
XCALLOC(nodeArray, ONEnode *, 1 + pDevice->numNodes);
|
||||
|
||||
/* store the nodes in this work array and print out later */
|
||||
for (index = 1; index < pDevice->numNodes; index++) {
|
||||
pElem = pDevice->elemArray[index];
|
||||
if (refPsi == 0.0 && pElem->matlInfo->type == SEMICON) {
|
||||
refPsi = pElem->matlInfo->refPsi;
|
||||
}
|
||||
for (i = 0; i <= 1; i++) {
|
||||
if (pElem->evalNodes[i]) {
|
||||
pNode = pElem->pNodes[i];
|
||||
nodeArray[pNode->nodeI] = pNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize rawfile */
|
||||
numVars = output->OUTPnumVars;
|
||||
fprintf(file, "Title: Device %s internal state\n", pDevice->name);
|
||||
fprintf(file, "Plotname: Device Cross Section\n");
|
||||
fprintf(file, "Flags: real\n");
|
||||
fprintf(file, "Command: deftype p xs cross\n");
|
||||
fprintf(file, "Command: deftype v distance m\n");
|
||||
fprintf(file, "Command: deftype v concentration cm^-3\n");
|
||||
fprintf(file, "Command: deftype v electric_field V/cm\n");
|
||||
fprintf(file, "Command: deftype v current_density A/cm^2\n");
|
||||
fprintf(file, "Command: deftype v concentration/time cm^-3/s\n");
|
||||
fprintf(file, "Command: deftype v mobility cm^2/Vs\n");
|
||||
fprintf(file, "No. Variables: %d\n", numVars);
|
||||
fprintf(file, "No. Points: %d\n", pDevice->numNodes);
|
||||
numVars = 0;
|
||||
fprintf(file, "Variables:\n");
|
||||
fprintf(file, "\t%d x distance\n", numVars++);
|
||||
if (output->OUTPpsi) {
|
||||
fprintf(file, "\t%d psi voltage\n", numVars++);
|
||||
}
|
||||
if (output->OUTPequPsi) {
|
||||
fprintf(file, "\t%d equ.psi voltage\n", numVars++);
|
||||
}
|
||||
if (output->OUTPvacPsi) {
|
||||
fprintf(file, "\t%d vac.psi voltage\n", numVars++);
|
||||
}
|
||||
if (output->OUTPphin) {
|
||||
fprintf(file, "\t%d phin voltage\n", numVars++);
|
||||
}
|
||||
if (output->OUTPphip) {
|
||||
fprintf(file, "\t%d phip voltage\n", numVars++);
|
||||
}
|
||||
if (output->OUTPphic) {
|
||||
fprintf(file, "\t%d phic voltage\n", numVars++);
|
||||
}
|
||||
if (output->OUTPphiv) {
|
||||
fprintf(file, "\t%d phiv voltage\n", numVars++);
|
||||
}
|
||||
if (output->OUTPdoping) {
|
||||
fprintf(file, "\t%d dop concentration\n", numVars++);
|
||||
}
|
||||
if (output->OUTPnConc) {
|
||||
fprintf(file, "\t%d n concentration\n", numVars++);
|
||||
}
|
||||
if (output->OUTPpConc) {
|
||||
fprintf(file, "\t%d p concentration\n", numVars++);
|
||||
}
|
||||
if (output->OUTPeField) {
|
||||
fprintf(file, "\t%d e electric_field\n", numVars++);
|
||||
}
|
||||
if (output->OUTPjc) {
|
||||
fprintf(file, "\t%d jc current_density\n", numVars++);
|
||||
}
|
||||
if (output->OUTPjd) {
|
||||
fprintf(file, "\t%d jd current_density\n", numVars++);
|
||||
}
|
||||
if (output->OUTPjn) {
|
||||
fprintf(file, "\t%d jn current_density\n", numVars++);
|
||||
}
|
||||
if (output->OUTPjp) {
|
||||
fprintf(file, "\t%d jp current_density\n", numVars++);
|
||||
}
|
||||
if (output->OUTPjt) {
|
||||
fprintf(file, "\t%d jt current_density\n", numVars++);
|
||||
}
|
||||
if (output->OUTPuNet) {
|
||||
fprintf(file, "\t%d unet concentration/time\n", numVars++);
|
||||
}
|
||||
if (output->OUTPmun) {
|
||||
fprintf(file, "\t%d mun mobility\n", numVars++);
|
||||
}
|
||||
if (output->OUTPmup) {
|
||||
fprintf(file, "\t%d mup mobility\n", numVars++);
|
||||
}
|
||||
fprintf(file, "Binary:\n");
|
||||
|
||||
for (index = 1; index <= pDevice->numNodes; index++) {
|
||||
pNode = nodeArray[index];
|
||||
if ((index > 1) && (index < pDevice->numNodes)) {
|
||||
pElem = pNode->pRightElem;
|
||||
pPrevElem = pNode->pLeftElem;
|
||||
if (pElem->evalNodes[0]) {
|
||||
info = pElem->matlInfo;
|
||||
} else if (pPrevElem->evalNodes[1]) {
|
||||
info = pPrevElem->matlInfo;
|
||||
}
|
||||
coeff1 = pPrevElem->dx / (pPrevElem->dx + pElem->dx);
|
||||
coeff2 = pElem->dx / (pPrevElem->dx + pElem->dx);
|
||||
eField = -coeff1 * pElem->pEdge->dPsi * pElem->rDx
|
||||
- coeff2 * pPrevElem->pEdge->dPsi * pPrevElem->rDx;
|
||||
mun = coeff1 * pElem->pEdge->mun + coeff2 * pPrevElem->pEdge->mun;
|
||||
mup = coeff1 * pElem->pEdge->mup + coeff2 * pPrevElem->pEdge->mup;
|
||||
jn = coeff1 * pElem->pEdge->jn + coeff2 * pPrevElem->pEdge->jn;
|
||||
jp = coeff1 * pElem->pEdge->jp + coeff2 * pPrevElem->pEdge->jp;
|
||||
jd = coeff1 * pElem->pEdge->jd + coeff2 * pPrevElem->pEdge->jd;
|
||||
} else if (index == 1) {
|
||||
info = pNode->pRightElem->matlInfo;
|
||||
eField = 0.0;
|
||||
mun = pNode->pRightElem->pEdge->mun;
|
||||
mup = pNode->pRightElem->pEdge->mup;
|
||||
jn = pNode->pRightElem->pEdge->jn;
|
||||
jp = pNode->pRightElem->pEdge->jp;
|
||||
jd = pNode->pRightElem->pEdge->jd;
|
||||
} else {
|
||||
info = pNode->pLeftElem->matlInfo;
|
||||
eField = 0.0;
|
||||
mun = pNode->pLeftElem->pEdge->mun;
|
||||
mup = pNode->pLeftElem->pEdge->mup;
|
||||
jn = pNode->pLeftElem->pEdge->jn;
|
||||
jp = pNode->pLeftElem->pEdge->jp;
|
||||
jd = pNode->pLeftElem->pEdge->jd;
|
||||
}
|
||||
jc = jn + jp;
|
||||
jt = jc + jd;
|
||||
/* Crude hack to get around the fact that the base node wipes out 'eg' */
|
||||
if (index == pDevice->baseIndex) {
|
||||
eGap = info->eg0;
|
||||
dGap = 0.0;
|
||||
} else {
|
||||
eGap = pNode->eg * VNorm;
|
||||
dGap = 0.5 * (info->eg0 - eGap);
|
||||
}
|
||||
|
||||
/* Now fill in the data array */
|
||||
numVars = 0;
|
||||
data[numVars++] = pNode->x * 1e-2;
|
||||
if (output->OUTPpsi) {
|
||||
data[numVars++] = (pNode->psi - refPsi) * VNorm;
|
||||
}
|
||||
if (output->OUTPequPsi) {
|
||||
data[numVars++] = (pNode->psi0 - refPsi) * VNorm;
|
||||
}
|
||||
if (output->OUTPvacPsi) {
|
||||
data[numVars++] = pNode->psi * VNorm;
|
||||
}
|
||||
if (output->OUTPphin) {
|
||||
if (info->type != INSULATOR) {
|
||||
data[numVars++] = (pNode->psi - refPsi - log(pNode->nConc / pNode->nie))
|
||||
* VNorm;
|
||||
} else {
|
||||
data[numVars++] = 0.0;
|
||||
}
|
||||
}
|
||||
if (output->OUTPphip) {
|
||||
if (info->type != INSULATOR) {
|
||||
data[numVars++] = (pNode->psi - refPsi + log(pNode->pConc / pNode->nie))
|
||||
* VNorm;
|
||||
} else {
|
||||
data[numVars++] = 0.0;
|
||||
}
|
||||
}
|
||||
if (output->OUTPphic) {
|
||||
data[numVars++] = (pNode->psi + pNode->eaff) * VNorm + dGap;
|
||||
}
|
||||
if (output->OUTPphiv) {
|
||||
data[numVars++] = (pNode->psi + pNode->eaff) * VNorm + dGap + eGap;
|
||||
}
|
||||
if (output->OUTPdoping) {
|
||||
data[numVars++] = pNode->netConc * NNorm;
|
||||
}
|
||||
if (output->OUTPnConc) {
|
||||
data[numVars++] = pNode->nConc * NNorm;
|
||||
}
|
||||
if (output->OUTPpConc) {
|
||||
data[numVars++] = pNode->pConc * NNorm;
|
||||
}
|
||||
if (output->OUTPeField) {
|
||||
data[numVars++] = eField * ENorm;
|
||||
}
|
||||
if (output->OUTPjc) {
|
||||
data[numVars++] = jc * JNorm;
|
||||
}
|
||||
if (output->OUTPjd) {
|
||||
data[numVars++] = jd * JNorm;
|
||||
}
|
||||
if (output->OUTPjn) {
|
||||
data[numVars++] = jn * JNorm;
|
||||
}
|
||||
if (output->OUTPjp) {
|
||||
data[numVars++] = jp * JNorm;
|
||||
}
|
||||
if (output->OUTPjt) {
|
||||
data[numVars++] = jt * JNorm;
|
||||
}
|
||||
if (output->OUTPuNet) {
|
||||
data[numVars++] = pNode->uNet * NNorm / TNorm;
|
||||
}
|
||||
if (output->OUTPmun) {
|
||||
data[numVars++] = mun;
|
||||
}
|
||||
if (output->OUTPmup) {
|
||||
data[numVars++] = mup;
|
||||
}
|
||||
fwrite((char *) data, sizeof(double), numVars, file);
|
||||
}
|
||||
FREE(nodeArray);
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX This is what the SPARSE element structure looks like. We can't take it
|
||||
* from its definition because the include file redefines all sorts of
|
||||
* things. Note that we are violating data encapsulation to find out the
|
||||
* size of this thing.
|
||||
*/
|
||||
struct MatrixElement {
|
||||
spREAL Real;
|
||||
spREAL Imag;
|
||||
int Row;
|
||||
int Col;
|
||||
struct MatrixElement *NextInRow;
|
||||
struct MatrixElement *NextInCol;
|
||||
};
|
||||
|
||||
void
|
||||
ONEmemStats(FILE *file, ONEdevice *pDevice)
|
||||
{
|
||||
static char *memFormat = "%-20s%10d%10d\n";
|
||||
static char *sumFormat = "%20s %-10d\n";
|
||||
unsigned int size;
|
||||
unsigned int memory;
|
||||
ONEmaterial *pMaterial;
|
||||
ONEcontact *pContact;
|
||||
int numContactNodes;
|
||||
|
||||
|
||||
fprintf(file, "----------------------------------------\n");
|
||||
fprintf(file, "Device %s Memory Usage:\n", pDevice->name);
|
||||
fprintf(file, "Item Count Bytes\n");
|
||||
fprintf(file, "----------------------------------------\n");
|
||||
|
||||
size = 1;
|
||||
memory = size * sizeof(ONEdevice);
|
||||
fprintf(file, memFormat, "Device", size, memory);
|
||||
size = pDevice->numNodes - 1;
|
||||
memory = size * sizeof(ONEelem);
|
||||
fprintf(file, memFormat, "Elements", size, memory);
|
||||
size = pDevice->numNodes;
|
||||
memory = size * sizeof(ONEnode);
|
||||
fprintf(file, memFormat, "Nodes", size, memory);
|
||||
size = pDevice->numNodes - 1;
|
||||
memory = size * sizeof(ONEedge);
|
||||
fprintf(file, memFormat, "Edges", size, memory);
|
||||
|
||||
size = pDevice->numNodes;
|
||||
memory = size * sizeof(ONEelem *);
|
||||
size = 0;
|
||||
for (pMaterial = pDevice->pMaterials; pMaterial; pMaterial = pMaterial->next)
|
||||
size++;
|
||||
memory += size * sizeof(ONEmaterial);
|
||||
size = numContactNodes = 0;
|
||||
for (pContact = pDevice->pFirstContact; pContact; pContact = pContact->next) {
|
||||
numContactNodes += pContact->numNodes;
|
||||
size++;
|
||||
}
|
||||
memory += size * sizeof(ONEcontact);
|
||||
size = numContactNodes;
|
||||
memory += size * sizeof(ONEnode *);
|
||||
size = 0;
|
||||
fprintf(file, "%-20s%10s%10d\n", "Misc Mesh", "n/a", memory);
|
||||
|
||||
size = pDevice->numOrigEquil;
|
||||
memory = size * sizeof(struct MatrixElement);
|
||||
fprintf(file, memFormat, "Equil Orig NZ", size, memory);
|
||||
size = pDevice->numFillEquil;
|
||||
memory = size * sizeof(struct MatrixElement);
|
||||
fprintf(file, memFormat, "Equil Fill NZ", size, memory);
|
||||
size = pDevice->numOrigEquil + pDevice->numFillEquil;
|
||||
memory = size * sizeof(struct MatrixElement);
|
||||
fprintf(file, memFormat, "Equil Tot NZ", size, memory);
|
||||
size = pDevice->dimEquil;
|
||||
memory = size * 4 * sizeof(double);
|
||||
fprintf(file, memFormat, "Equil Vectors", size, memory);
|
||||
|
||||
size = pDevice->numOrigBias;
|
||||
memory = size * sizeof(struct MatrixElement);
|
||||
fprintf(file, memFormat, "Bias Orig NZ", size, memory);
|
||||
size = pDevice->numFillBias;
|
||||
memory = size * sizeof(struct MatrixElement);
|
||||
fprintf(file, memFormat, "Bias Fill NZ", size, memory);
|
||||
size = pDevice->numOrigBias + pDevice->numFillBias;
|
||||
memory = size * sizeof(struct MatrixElement);
|
||||
fprintf(file, memFormat, "Bias Tot NZ", size, memory);
|
||||
size = pDevice->dimBias;
|
||||
memory = size * 5 * sizeof(double);
|
||||
fprintf(file, memFormat, "Bias Vectors", size, memory);
|
||||
|
||||
size = (pDevice->numNodes - 1) * ONEnumEdgeStates +
|
||||
pDevice->numNodes * ONEnumNodeStates;
|
||||
memory = size * sizeof(double);
|
||||
fprintf(file, memFormat, "State Vector", size, memory);
|
||||
}
|
||||
|
||||
void
|
||||
ONEcpuStats(FILE *file, ONEdevice *pDevice)
|
||||
{
|
||||
static char *cpuFormat = "%-20s%10g%10g%10g%10g%10g\n";
|
||||
ONEstats *pStats = pDevice->pStats;
|
||||
double total;
|
||||
int iTotal;
|
||||
|
||||
fprintf(file,
|
||||
"----------------------------------------------------------------------\n");
|
||||
fprintf(file,
|
||||
"Device %s Time Usage:\n", pDevice->name);
|
||||
fprintf(file,
|
||||
"Item SETUP DC TRAN AC TOTAL\n");
|
||||
fprintf(file,
|
||||
"----------------------------------------------------------------------\n");
|
||||
|
||||
total = pStats->setupTime[STAT_SETUP] +
|
||||
pStats->setupTime[STAT_DC] +
|
||||
pStats->setupTime[STAT_TRAN] +
|
||||
pStats->setupTime[STAT_AC];
|
||||
fprintf(file, cpuFormat, "Setup Time",
|
||||
pStats->setupTime[STAT_SETUP],
|
||||
pStats->setupTime[STAT_DC],
|
||||
pStats->setupTime[STAT_TRAN],
|
||||
pStats->setupTime[STAT_AC],
|
||||
total);
|
||||
|
||||
total = pStats->loadTime[STAT_SETUP] +
|
||||
pStats->loadTime[STAT_DC] +
|
||||
pStats->loadTime[STAT_TRAN] +
|
||||
pStats->loadTime[STAT_AC];
|
||||
fprintf(file, cpuFormat, "Load Time",
|
||||
pStats->loadTime[STAT_SETUP],
|
||||
pStats->loadTime[STAT_DC],
|
||||
pStats->loadTime[STAT_TRAN],
|
||||
pStats->loadTime[STAT_AC],
|
||||
total);
|
||||
|
||||
total = pStats->orderTime[STAT_SETUP] +
|
||||
pStats->orderTime[STAT_DC] +
|
||||
pStats->orderTime[STAT_TRAN] +
|
||||
pStats->orderTime[STAT_AC];
|
||||
fprintf(file, cpuFormat, "Order Time",
|
||||
pStats->orderTime[STAT_SETUP],
|
||||
pStats->orderTime[STAT_DC],
|
||||
pStats->orderTime[STAT_TRAN],
|
||||
pStats->orderTime[STAT_AC],
|
||||
total);
|
||||
|
||||
total = pStats->factorTime[STAT_SETUP] +
|
||||
pStats->factorTime[STAT_DC] +
|
||||
pStats->factorTime[STAT_TRAN] +
|
||||
pStats->factorTime[STAT_AC];
|
||||
fprintf(file, cpuFormat, "Factor Time",
|
||||
pStats->factorTime[STAT_SETUP],
|
||||
pStats->factorTime[STAT_DC],
|
||||
pStats->factorTime[STAT_TRAN],
|
||||
pStats->factorTime[STAT_AC],
|
||||
total);
|
||||
|
||||
total = pStats->solveTime[STAT_SETUP] +
|
||||
pStats->solveTime[STAT_DC] +
|
||||
pStats->solveTime[STAT_TRAN] +
|
||||
pStats->solveTime[STAT_AC];
|
||||
fprintf(file, cpuFormat, "Solve Time",
|
||||
pStats->solveTime[STAT_SETUP],
|
||||
pStats->solveTime[STAT_DC],
|
||||
pStats->solveTime[STAT_TRAN],
|
||||
pStats->solveTime[STAT_AC],
|
||||
total);
|
||||
|
||||
total = pStats->updateTime[STAT_SETUP] +
|
||||
pStats->updateTime[STAT_DC] +
|
||||
pStats->updateTime[STAT_TRAN] +
|
||||
pStats->updateTime[STAT_AC];
|
||||
fprintf(file, cpuFormat, "Update Time",
|
||||
pStats->updateTime[STAT_SETUP],
|
||||
pStats->updateTime[STAT_DC],
|
||||
pStats->updateTime[STAT_TRAN],
|
||||
pStats->updateTime[STAT_AC],
|
||||
total);
|
||||
|
||||
total = pStats->checkTime[STAT_SETUP] +
|
||||
pStats->checkTime[STAT_DC] +
|
||||
pStats->checkTime[STAT_TRAN] +
|
||||
pStats->checkTime[STAT_AC];
|
||||
fprintf(file, cpuFormat, "Check Time",
|
||||
pStats->checkTime[STAT_SETUP],
|
||||
pStats->checkTime[STAT_DC],
|
||||
pStats->checkTime[STAT_TRAN],
|
||||
pStats->checkTime[STAT_AC],
|
||||
total);
|
||||
|
||||
total = pStats->setupTime[STAT_SETUP] +
|
||||
pStats->setupTime[STAT_DC] +
|
||||
pStats->setupTime[STAT_TRAN] +
|
||||
pStats->setupTime[STAT_AC];
|
||||
fprintf(file, cpuFormat, "Misc Time",
|
||||
pStats->miscTime[STAT_SETUP],
|
||||
pStats->miscTime[STAT_DC],
|
||||
pStats->miscTime[STAT_TRAN],
|
||||
pStats->miscTime[STAT_AC],
|
||||
total);
|
||||
|
||||
fprintf(file, "%-40s%10g%10s%10g\n", "LTE Time",
|
||||
pStats->lteTime,
|
||||
"", pStats->lteTime);
|
||||
|
||||
total = pStats->totalTime[STAT_SETUP] +
|
||||
pStats->totalTime[STAT_DC] +
|
||||
pStats->totalTime[STAT_TRAN] +
|
||||
pStats->totalTime[STAT_AC];
|
||||
fprintf(file, cpuFormat, "Total Time",
|
||||
pStats->totalTime[STAT_SETUP],
|
||||
pStats->totalTime[STAT_DC],
|
||||
pStats->totalTime[STAT_TRAN],
|
||||
pStats->totalTime[STAT_AC],
|
||||
total);
|
||||
|
||||
iTotal = pStats->numIters[STAT_SETUP] +
|
||||
pStats->numIters[STAT_DC] +
|
||||
pStats->numIters[STAT_TRAN] +
|
||||
pStats->numIters[STAT_AC];
|
||||
fprintf(file, "%-20s%10d%10d%10d%10d%10d\n", "Iterations",
|
||||
pStats->numIters[STAT_SETUP],
|
||||
pStats->numIters[STAT_DC],
|
||||
pStats->numIters[STAT_TRAN],
|
||||
pStats->numIters[STAT_AC],
|
||||
iTotal);
|
||||
}
|
||||
|
|
@ -0,0 +1,348 @@
|
|||
/**********
|
||||
Copyright 1992 Regents of the University of California. All rights reserved.
|
||||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numglobs.h"
|
||||
#include "numenum.h"
|
||||
#include "onemesh.h"
|
||||
#include "onedev.h"
|
||||
#include "macros.h"
|
||||
#include "spMatrix.h"
|
||||
#include "bool.h"
|
||||
#include "onedext.h"
|
||||
#include "oneddefs.h"
|
||||
#include "cidersupt.h"
|
||||
|
||||
|
||||
/*
|
||||
* Functions for projecting the next solution point for modified two-level
|
||||
* newton scheme
|
||||
*/
|
||||
|
||||
void
|
||||
NUMDproject(ONEdevice *pDevice, double delV)
|
||||
{
|
||||
ONEelem *pElem = pDevice->elemArray[pDevice->numNodes - 1];
|
||||
ONEnode *pNode;
|
||||
ONEedge *pEdge;
|
||||
double delPsi, delN, delP, newN, newP;
|
||||
double *incVpn;
|
||||
int i, index;
|
||||
|
||||
|
||||
delV = - delV / VNorm;
|
||||
pElem->pRightNode->psi += delV;
|
||||
|
||||
if (ABS(delV) < MIN_DELV) {
|
||||
ONEstoreInitialGuess(pDevice);
|
||||
return;
|
||||
}
|
||||
/* zero the rhs before loading in the new rhs */
|
||||
for (index = 1; index <= pDevice->numEqns; index++) {
|
||||
pDevice->rhs[index] = 0.0;
|
||||
}
|
||||
/* compute incremental changes due to N contact */
|
||||
pNode = pElem->pLeftNode;
|
||||
pDevice->rhs[pNode->psiEqn] = pElem->epsRel * pElem->rDx;
|
||||
if (pElem->elemType == SEMICON) {
|
||||
pEdge = pElem->pEdge;
|
||||
pDevice->rhs[pNode->nEqn] = -pEdge->dJnDpsiP1;
|
||||
pDevice->rhs[pNode->pEqn] = -pEdge->dJpDpsiP1;
|
||||
}
|
||||
incVpn = pDevice->dcDeltaSolution;
|
||||
spSolve(pDevice->matrix, pDevice->rhs, incVpn, NIL(spREAL), NIL(spREAL));
|
||||
|
||||
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) {
|
||||
delPsi = incVpn[pNode->psiEqn] * delV;
|
||||
pDevice->dcSolution[pNode->psiEqn] = pNode->psi + delPsi;
|
||||
if (pElem->elemType == SEMICON) {
|
||||
delN = incVpn[pNode->nEqn] * delV;
|
||||
delP = incVpn[pNode->pEqn] * delV;
|
||||
newN = pNode->nConc + delN;
|
||||
newP = pNode->pConc + delP;
|
||||
/* if new conc less than 0.0 then use a fraction of the guess */
|
||||
if (newN <= 0.0) {
|
||||
pDevice->dcSolution[pNode->nEqn] =
|
||||
guessNewConc(pNode->nConc, delN);
|
||||
} else {
|
||||
pDevice->dcSolution[pNode->nEqn] = newN;
|
||||
}
|
||||
if (newP <= 0.0) {
|
||||
pDevice->dcSolution[pNode->pEqn] =
|
||||
guessNewConc(pNode->pConc, delP);
|
||||
} else {
|
||||
pDevice->dcSolution[pNode->pEqn] = newP;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
NBJTproject(ONEdevice *pDevice, double delVce, double delVbe,
|
||||
double vbe)
|
||||
{
|
||||
ONEelem *pLastElem = pDevice->elemArray[pDevice->numNodes - 1];
|
||||
ONEelem *pBaseElem = pDevice->elemArray[pDevice->baseIndex - 1];
|
||||
ONEelem *pElem;
|
||||
ONEnode *pNode;
|
||||
ONEedge *pEdge;
|
||||
double delPsi, delN, delP, newN, newP, *incVce, *incVbe;
|
||||
double baseConc=0.0;
|
||||
int i, index;
|
||||
double nConc, pConc;
|
||||
|
||||
/* normalize the voltages for calculations */
|
||||
delVce = delVce / VNorm;
|
||||
delVbe = delVbe / VNorm;
|
||||
pLastElem->pRightNode->psi += delVce;
|
||||
pBaseElem->pRightNode->vbe = vbe / VNorm + pBaseElem->matlInfo->refPsi;
|
||||
pNode = pBaseElem->pRightNode;
|
||||
if (pNode->baseType == N_TYPE) {
|
||||
baseConc = pNode->nConc;
|
||||
} else if (pNode->baseType == P_TYPE) {
|
||||
baseConc = pNode->pConc;
|
||||
}
|
||||
if (ABS(delVce) > MIN_DELV) {
|
||||
|
||||
/* zero the rhs before loading in the new rhs */
|
||||
for (index = 1; index <= pDevice->numEqns; index++) {
|
||||
pDevice->rhs[index] = 0.0;
|
||||
}
|
||||
/* store the new rhs for computing the incremental quantities */
|
||||
pNode = pLastElem->pLeftNode;
|
||||
pDevice->rhs[pNode->psiEqn] = pLastElem->epsRel * pLastElem->rDx;
|
||||
if (pLastElem->elemType == SEMICON) {
|
||||
pEdge = pLastElem->pEdge;
|
||||
pDevice->rhs[pNode->nEqn] = -pEdge->dJnDpsiP1;
|
||||
pDevice->rhs[pNode->pEqn] = -pEdge->dJpDpsiP1;
|
||||
}
|
||||
incVce = pDevice->dcDeltaSolution;
|
||||
spSolve(pDevice->matrix, pDevice->rhs, incVce, NIL(spREAL), NIL(spREAL));
|
||||
|
||||
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) {
|
||||
delPsi = incVce[pNode->psiEqn] * delVce;
|
||||
pDevice->dcSolution[pNode->psiEqn] = pNode->psi + delPsi;
|
||||
if (pElem->elemType == SEMICON) {
|
||||
delN = incVce[pNode->nEqn] * delVce;
|
||||
delP = incVce[pNode->pEqn] * delVce;
|
||||
newN = pNode->nConc + delN;
|
||||
newP = pNode->pConc + delP;
|
||||
/* if new conc less than 0.0 then use a fraction of the guess */
|
||||
if (newN <= 0.0) {
|
||||
pDevice->dcSolution[pNode->nEqn] =
|
||||
guessNewConc(pNode->nConc, delN);
|
||||
} else {
|
||||
pDevice->dcSolution[pNode->nEqn] = newN;
|
||||
}
|
||||
if (newP <= 0.0) {
|
||||
pDevice->dcSolution[pNode->pEqn] =
|
||||
guessNewConc(pNode->pConc, delP);
|
||||
} else {
|
||||
pDevice->dcSolution[pNode->pEqn] = newP;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ONEstoreInitialGuess(pDevice);
|
||||
}
|
||||
|
||||
if (ABS(delVbe) > MIN_DELV) {
|
||||
|
||||
/* zero the rhs before loading in the new rhs base contribution */
|
||||
for (index = 1; index <= pDevice->numEqns; index++) {
|
||||
pDevice->rhs[index] = 0.0;
|
||||
}
|
||||
pNode = pBaseElem->pRightNode;
|
||||
if (pNode->baseType == N_TYPE) {
|
||||
pDevice->rhs[pNode->nEqn] = baseConc * pNode->eg;
|
||||
} else if (pNode->baseType == P_TYPE) {
|
||||
pDevice->rhs[pNode->pEqn] = baseConc * pNode->eg;
|
||||
}
|
||||
|
||||
incVbe = pDevice->copiedSolution;
|
||||
spSolve(pDevice->matrix, pDevice->rhs, incVbe, NIL(spREAL), NIL(spREAL));
|
||||
|
||||
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) {
|
||||
delPsi = incVbe[pNode->psiEqn] * delVbe;
|
||||
pDevice->dcSolution[pNode->psiEqn] += delPsi;
|
||||
if (pElem->elemType == SEMICON) {
|
||||
delN = incVbe[pNode->nEqn] * delVbe;
|
||||
delP = incVbe[pNode->pEqn] * delVbe;
|
||||
nConc = pDevice->dcSolution[pNode->nEqn];
|
||||
pConc = pDevice->dcSolution[pNode->pEqn];
|
||||
newN = nConc + delN;
|
||||
newP = pConc + delP;
|
||||
/* if new conc less than 0.0 then use a fraction of the guess */
|
||||
if (newN <= 0.0) {
|
||||
pDevice->dcSolution[pNode->nEqn] =
|
||||
guessNewConc(nConc, delN);
|
||||
} else {
|
||||
pDevice->dcSolution[pNode->nEqn] = newN;
|
||||
}
|
||||
if (newP <= 0.0) {
|
||||
pDevice->dcSolution[pNode->pEqn] =
|
||||
guessNewConc(pConc, delP);
|
||||
} else {
|
||||
pDevice->dcSolution[pNode->pEqn] = newP;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* functions to update device states for full-LU algorithm */
|
||||
|
||||
void
|
||||
NUMDupdate(ONEdevice *pDevice, double delV, BOOLEAN updateBoundary)
|
||||
{
|
||||
ONEelem *pElem = pDevice->elemArray[pDevice->numNodes - 1];
|
||||
ONEnode *pNode;
|
||||
ONEedge *pEdge;
|
||||
double delPsi, delN, delP;
|
||||
int i, index;
|
||||
|
||||
|
||||
delV = - delV / VNorm;
|
||||
if (updateBoundary) {
|
||||
pElem->pRightNode->psi += delV;
|
||||
}
|
||||
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) {
|
||||
delPsi = pDevice->dcDeltaSolution[pNode->psiEqn] * delV;
|
||||
pDevice->dcSolution[pNode->psiEqn] = pNode->psi + delPsi;
|
||||
if (pElem->elemType == SEMICON) {
|
||||
delN = pDevice->dcDeltaSolution[pNode->nEqn] * delV;
|
||||
delP = pDevice->dcDeltaSolution[pNode->pEqn] * delV;
|
||||
pDevice->dcSolution[pNode->nEqn] = pNode->nConc + delN;
|
||||
pDevice->dcSolution[pNode->pEqn] = pNode->pConc + delP;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NBJTupdate(ONEdevice *pDevice, double delVce, double delVbe,
|
||||
double vbe, BOOLEAN updateBoundary)
|
||||
{
|
||||
ONEelem *pLastElem = pDevice->elemArray[pDevice->numNodes - 1];
|
||||
ONEelem *pBaseElem = pDevice->elemArray[pDevice->baseIndex - 1];
|
||||
ONEelem *pElem;
|
||||
ONEnode *pNode;
|
||||
double delPsi, delN, delP, *incVce, *incVbe;
|
||||
int i, index;
|
||||
|
||||
/* normalize the voltages for calculations */
|
||||
delVce = delVce / VNorm;
|
||||
delVbe = delVbe / VNorm;
|
||||
if (updateBoundary) {
|
||||
pLastElem->pRightNode->psi += delVce;
|
||||
pBaseElem->pRightNode->vbe = vbe / VNorm + pBaseElem->matlInfo->refPsi;
|
||||
}
|
||||
/*
|
||||
* The incremental quantities are available from NBJTconductance. incVce
|
||||
* (dcDeltaSolution) and incVbe (copiedSolution) are used to store the
|
||||
* incremental quantities associated with Vce and Vbe
|
||||
*/
|
||||
|
||||
/* set incVce = dcDeltaSolution; incVbe = copiedSolution */
|
||||
incVce = pDevice->dcDeltaSolution;
|
||||
incVbe = pDevice->copiedSolution;
|
||||
|
||||
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) {
|
||||
delPsi = incVce[pNode->psiEqn] * delVce
|
||||
+ incVbe[pNode->psiEqn] * delVbe;
|
||||
pDevice->dcSolution[pNode->psiEqn] = pNode->psi + delPsi;
|
||||
if (pElem->elemType == SEMICON) {
|
||||
delN = incVce[pNode->nEqn] * delVce
|
||||
+ incVbe[pNode->nEqn] * delVbe;
|
||||
delP = incVce[pNode->pEqn] * delVce
|
||||
+ incVbe[pNode->pEqn] * delVbe;
|
||||
pDevice->dcSolution[pNode->nEqn] = pNode->nConc + delN;
|
||||
pDevice->dcSolution[pNode->pEqn] = pNode->pConc + delP;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NUMDsetBCs(ONEdevice *pDevice, double vpn)
|
||||
{
|
||||
ONEelem *pElem = pDevice->elemArray[pDevice->numNodes - 1];
|
||||
|
||||
|
||||
/* normalize the voltage */
|
||||
vpn = - vpn / VNorm;
|
||||
|
||||
/* set the boundary conditions */
|
||||
pElem->pRightNode->psi = vpn + pElem->pRightNode->psi0;
|
||||
}
|
||||
|
||||
void
|
||||
NBJTsetBCs(ONEdevice *pDevice, double vce, double vbe)
|
||||
{
|
||||
ONEelem *pLastElem = pDevice->elemArray[pDevice->numNodes - 1];
|
||||
ONEelem *pBaseElem = pDevice->elemArray[pDevice->baseIndex - 1];
|
||||
ONEnode *pNode;
|
||||
double psi, conc, sign, absConc;
|
||||
double nie, ni, pi;
|
||||
|
||||
/* normalize the voltages */
|
||||
vce = vce / VNorm;
|
||||
vbe = vbe / VNorm;
|
||||
|
||||
/* set the boundary conditions */
|
||||
pBaseElem->pRightNode->vbe = vbe + pBaseElem->matlInfo->refPsi;
|
||||
pLastElem->pRightNode->psi = vce + pLastElem->pRightNode->psi0;
|
||||
|
||||
/*
|
||||
* if (pLastElem->elemType IS INSULATOR) { pNode->psi = RefPsi -
|
||||
* pNode->eaff; pNode->nConc = 0.0; pNode->pConc = 0.0; } else if
|
||||
* (pLastElem->elemType IS SEMICON) { nie = pNode->nie; conc =
|
||||
* pNode->netConc / pNode->nie; psi = 0.0; ni = nie; pi = nie; sign = SGN(
|
||||
* conc ); absConc = ABS( conc ); if ( conc ISNOT 0.0 ) { psi = sign * log(
|
||||
* 0.5 * absConc + sqrt( 1.0 + 0.25*absConc*absConc )); ni = nie * exp( psi
|
||||
* ); pi = nie * exp( -psi ); } pNode->psi = pLastElem->matlInfo->refPsi +
|
||||
* psi; pNode->nConc = ni; pNode->pConc = pi; } pNode->psi = pNode->psi0;
|
||||
* pNode->psi += vce;
|
||||
*/
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
/**********
|
||||
Copyright 1992 Regents of the University of California. All rights reserved.
|
||||
Author: 1992 David A. Gates, U. C. Berkeley CAD Group
|
||||
**********/
|
||||
|
||||
/*
|
||||
* Functions needed to read solutions for 1D devices.
|
||||
*/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numglobs.h"
|
||||
#include "numenum.h"
|
||||
#include "onedev.h"
|
||||
#include "onemesh.h"
|
||||
#include "plot.h"
|
||||
#include "onedext.h"
|
||||
#include "oneddefs.h"
|
||||
#include "cidersupt.h"
|
||||
|
||||
int
|
||||
ONEreadState(ONEdevice *pDevice, char *fileName, int numVolts,
|
||||
double *pV1, double *pV2 )
|
||||
/* fileName | File containing raw data */
|
||||
/* int numVolts | Number of voltage differences */
|
||||
/* double *pV1, *pV2 | Pointer to return them in */
|
||||
{
|
||||
int dataLength;
|
||||
int index, i;
|
||||
ONEnode **nodeArray=NULL;
|
||||
ONEnode *pNode;
|
||||
ONEelem *pElem;
|
||||
ONEmaterial *info;
|
||||
double refPsi = 0.0;
|
||||
double *psiData, *nData, *pData;
|
||||
double *vData[2];
|
||||
struct plot *stateDB;
|
||||
struct plot *voltsDB;
|
||||
char voltName[80];
|
||||
|
||||
|
||||
stateDB = DBread( fileName );
|
||||
if (stateDB == NULL) return (-1);
|
||||
voltsDB = stateDB->pl_next;
|
||||
if (voltsDB == NULL) return (-1);
|
||||
|
||||
for (i=0; i < numVolts; i++ ) {
|
||||
sprintf( voltName, "v%d%d", i+1, numVolts+1 );
|
||||
vData[i] = DBgetData( voltsDB, voltName, 1 );
|
||||
if (vData[i] == NULL) return (-1);
|
||||
}
|
||||
dataLength = pDevice->numNodes;
|
||||
psiData = DBgetData( stateDB, "psi", dataLength );
|
||||
nData = DBgetData( stateDB, "n", dataLength );
|
||||
pData = DBgetData( stateDB, "p", dataLength );
|
||||
if (psiData == NULL || nData == NULL || pData == NULL) return (-1);
|
||||
|
||||
if (pV1 != NULL) {
|
||||
*pV1 = vData[0][0];
|
||||
FREE( vData[0] );
|
||||
}
|
||||
if (pV2 != NULL) {
|
||||
*pV2 = vData[1][0];
|
||||
FREE( vData[1] );
|
||||
}
|
||||
|
||||
/* generate the work array for copying node info */
|
||||
XCALLOC(nodeArray, ONEnode *, 1 + pDevice->numNodes);
|
||||
|
||||
/* store the nodes in this work array and print out later */
|
||||
for (index = 1; index < pDevice->numNodes; index++) {
|
||||
pElem = pDevice->elemArray[index];
|
||||
if (refPsi == 0.0 && pElem->matlInfo->type == SEMICON) {
|
||||
refPsi = pElem->matlInfo->refPsi;
|
||||
}
|
||||
for (i = 0; i <= 1; i++) {
|
||||
if (pElem->evalNodes[i]) {
|
||||
pNode = pElem->pNodes[i];
|
||||
nodeArray[pNode->nodeI] = pNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (index = 1; index <= pDevice->numNodes; index++) {
|
||||
pNode = nodeArray[index];
|
||||
pNode->psi = psiData[index-1]/VNorm + refPsi;
|
||||
pNode->nConc = nData[index-1]/NNorm;
|
||||
pNode->pConc = pData[index-1]/NNorm;
|
||||
}
|
||||
FREE(nodeArray);
|
||||
FREE(psiData);
|
||||
FREE(nData);
|
||||
FREE(pData);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
|
@ -0,0 +1,233 @@
|
|||
/**********
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,13 @@
|
|||
Directory: oned
|
||||
---------------
|
||||
This directory contains the files that are primarily responsible for
|
||||
implementing the 1D device simulator. It also contains files that help
|
||||
interface the circuit simulator to the device simulator. Most functions
|
||||
that are common to all 1D device simulations start with the prefix ONE,
|
||||
e.g. ONEbiasSolve. The device-specific routines start with either
|
||||
NUMD or NBJT, e.g. NUMDadmittance or NBJTproject. The simulator contains
|
||||
both a Poisson Solver for equilibrium and a three-equation solver for bias
|
||||
solutions. An attempt has been made to keep the function names parallel
|
||||
in the two portions. Poisson routines are identified with a 'Q' (for charge
|
||||
only) after the ONE, and Full solver routines are identified with an
|
||||
underscore '_'.
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
## Process this file with automake to produce Makefile.in
|
||||
|
||||
noinst_LIBRARIES = libcidersuprt.a
|
||||
|
||||
libcidersuprt_a_SOURCES = \
|
||||
database.c \
|
||||
devprint.c \
|
||||
geominfo.c \
|
||||
globals.c \
|
||||
integset.c \
|
||||
integuse.c \
|
||||
logfile.c \
|
||||
mater.c \
|
||||
misc.c \
|
||||
mobil.c \
|
||||
recomb.c \
|
||||
suprem.c \
|
||||
suprmitf.c
|
||||
|
||||
EXTRA_DIST = makefile
|
||||
INCLUDES = -I$(top_srcdir)/src/include
|
||||
MAINTAINERCLEANFILES = Makefile.in
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
/**********
|
||||
Copyright 1992 Regents of the University of California. All rights reserved.
|
||||
Author: 1992 David A. Gates, U. C. Berkeley CAD Group
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "fteext.h"
|
||||
/* #include "ftedata.h" */
|
||||
|
||||
struct plot *
|
||||
DBread( fileName )
|
||||
{
|
||||
struct plot *plot;
|
||||
|
||||
plot = raw_read( fileName );
|
||||
|
||||
return(plot);
|
||||
}
|
||||
|
||||
double *
|
||||
DBgetData( plot, name, lengthWanted )
|
||||
struct plot *plot;
|
||||
char *name;
|
||||
int lengthWanted;
|
||||
{
|
||||
struct dvec *v;
|
||||
double *data;
|
||||
int i;
|
||||
|
||||
v = vec_fromplot(name,plot);
|
||||
|
||||
if (!v) {
|
||||
fprintf( stderr, "Error: cannot locate variable '%s'\n", name );
|
||||
return(NULL);
|
||||
}
|
||||
if (v->v_length != lengthWanted ) {
|
||||
fprintf( stderr, "Error: vector '%s' has incorrect length\n", name );
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
data = (double *) malloc(sizeof (double) * v->v_length);
|
||||
if (isreal(v)) {
|
||||
bcopy((char *) v->v_realdata, (char *) data, sizeof (double) * v->v_length);
|
||||
} else {
|
||||
for (i=0; i < v->v_length; i++) {
|
||||
data[i] = realpart(&v->v_compdata[i]);
|
||||
}
|
||||
}
|
||||
return(data);
|
||||
}
|
||||
|
||||
void
|
||||
DBfree( plot )
|
||||
struct plot *plot;
|
||||
{
|
||||
struct dvec *v, *nextv;
|
||||
struct plot *pl, *nextpl;
|
||||
|
||||
for (pl = plot; pl; pl = nextpl) {
|
||||
nextpl = pl->pl_next;
|
||||
tfree( pl->pl_title );
|
||||
tfree( pl->pl_date );
|
||||
tfree( pl->pl_name );
|
||||
tfree( pl->pl_typename );
|
||||
for (v = pl->pl_dvecs; v; v = nextv) {
|
||||
nextv = v->v_next;
|
||||
vec_free( v );
|
||||
}
|
||||
wl_free( pl->pl_commands );
|
||||
/* XXX Environment variables (pl->pl_env) will leak. */
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
/**********
|
||||
Copyright 1992 Regents of the University of California. All rights reserved.
|
||||
Author: 1992 David A. Gates, U. C. Berkeley CAD Group
|
||||
**********/
|
||||
|
||||
/* Device-type Dependent Printing Routines */
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "optndefs.h"
|
||||
|
||||
void
|
||||
printVoltages(FILE *file, char *mName, char *iName, int devType,
|
||||
int numVolt, double v1, double delV1, double v2,
|
||||
double delV2, double v3, double delV3 )
|
||||
/* FILE *file output filestream */
|
||||
/* char *mName name of model */
|
||||
/* char *iName name of instance */
|
||||
{
|
||||
fprintf( file, "\n" );
|
||||
switch ( devType ) {
|
||||
case OPTN_RESISTOR:
|
||||
fprintf( file, "RES %s:%s voltage:\n", mName, iName );
|
||||
fprintf( file, " Vpn =% .4e delVpn =% .4e\n", v1, delV1 );
|
||||
break;
|
||||
case OPTN_CAPACITOR:
|
||||
fprintf( file, "CAP %s:%s voltage:\n", mName, iName );
|
||||
fprintf( file, " Vpn =% .4e delVpn =% .4e\n", v1, delV1 );
|
||||
break;
|
||||
case OPTN_DIODE:
|
||||
fprintf( file, "DIO %s:%s voltage:\n", mName, iName );
|
||||
fprintf( file, " Vpn =% .4e delVpn =% .4e\n", v1, delV1 );
|
||||
break;
|
||||
case OPTN_MOSCAP:
|
||||
fprintf( file, "MOS %s:%s voltage:\n", mName, iName );
|
||||
fprintf( file, " Vgb =% .4e delVgb =% .4e\n", v1, delV1 );
|
||||
break;
|
||||
case OPTN_BIPOLAR:
|
||||
fprintf( file, "BJT %s:%s voltages:\n", mName, iName );
|
||||
if ( numVolt == 3 ) {
|
||||
fprintf( file, " Vce =% .4e delVce =% .4e\n",
|
||||
v1 - v3, delV1 - delV3 );
|
||||
fprintf( file, " Vbe =% .4e delVbe =% .4e\n",
|
||||
v2 - v3, delV2 - delV3 );
|
||||
fprintf( file, " Vcs =% .4e delVcs =% .4e\n", v1, delV1 );
|
||||
} else {
|
||||
fprintf( file, " Vce =% .4e delVce =% .4e\n", v1, delV1 );
|
||||
fprintf( file, " Vbe =% .4e delVbe =% .4e\n", v2, delV2 );
|
||||
}
|
||||
break;
|
||||
case OPTN_MOSFET:
|
||||
fprintf( file, "MOS %s:%s voltages:\n", mName, iName );
|
||||
fprintf( file, " Vdb =% .4e delVdb =% .4e\n", v1, delV1 );
|
||||
fprintf( file, " Vgb =% .4e delVgb =% .4e\n", v2, delV2 );
|
||||
fprintf( file, " Vsb =% .4e delVsb =% .4e\n", v3, delV3 );
|
||||
break;
|
||||
case OPTN_JFET:
|
||||
if ( numVolt == 3 ) {
|
||||
fprintf( file, "JFET %s:%s voltages:\n", mName, iName );
|
||||
fprintf( file, " Vdb =% .4e delVdb =% .4e\n", v1, delV1 );
|
||||
fprintf( file, " Vgb =% .4e delVgb =% .4e\n", v2, delV2 );
|
||||
fprintf( file, " Vsb =% .4e delVsb =% .4e\n", v3, delV3 );
|
||||
} else {
|
||||
fprintf( file, "JFET %s:%s voltages:\n", mName, iName );
|
||||
fprintf( file, " Vds =% .4e delVds =% .4e\n", v1, delV1 );
|
||||
fprintf( file, " Vgs =% .4e delVgs =% .4e\n", v2, delV2 );
|
||||
}
|
||||
break;
|
||||
case OPTN_SOIBJT:
|
||||
case OPTN_SOIMOS:
|
||||
case OPTN_MESFET:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
/**********
|
||||
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
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "gendev.h"
|
||||
#include "macros.h"
|
||||
#include "memory.h"
|
||||
|
||||
void printCoordInfo(CoordInfo *pFirstCoord)
|
||||
{
|
||||
CoordInfo *pCoord;
|
||||
|
||||
for ( pCoord = pFirstCoord; pCoord != NIL(CoordInfo);
|
||||
pCoord = pCoord->next ) {
|
||||
fprintf(stderr, "mesh number=%4d location=%11.4e\n",
|
||||
pCoord->number, pCoord->location );
|
||||
}
|
||||
}
|
||||
|
||||
void killCoordInfo(CoordInfo *pFirstCoord)
|
||||
{
|
||||
CoordInfo *pCoord, *pKill;
|
||||
|
||||
for ( pCoord = pFirstCoord; pCoord != NIL(CoordInfo); ) {
|
||||
pKill = pCoord;
|
||||
pCoord = pCoord->next;
|
||||
FREE( pKill );
|
||||
}
|
||||
}
|
||||
|
||||
void ONEprintDomainInfo(DomainInfo *pFirstDomain)
|
||||
{
|
||||
DomainInfo *pDomain;
|
||||
|
||||
for ( pDomain = pFirstDomain; pDomain != NIL(DomainInfo);
|
||||
pDomain = pDomain->next ) {
|
||||
fprintf( stderr, "domain id=%4d mat=%4d ixLo=%4d ixHi=%4d\n",
|
||||
pDomain->id, pDomain->material, pDomain->ixLo, pDomain->ixHi );
|
||||
}
|
||||
}
|
||||
|
||||
void TWOprintDomainInfo(DomainInfo *pFirstDomain)
|
||||
{
|
||||
DomainInfo *pDomain;
|
||||
|
||||
for ( pDomain = pFirstDomain; pDomain != NIL(DomainInfo);
|
||||
pDomain = pDomain->next ) {
|
||||
fprintf( stderr,
|
||||
"domain id=%4d mat=%4d ixLo=%4d ixHi=%4d iyLo=%4d iyHi=%4d\n",
|
||||
pDomain->id, pDomain->material,
|
||||
pDomain->ixLo, pDomain->ixHi,
|
||||
pDomain->iyLo, pDomain->iyHi);
|
||||
}
|
||||
}
|
||||
|
||||
void killDomainInfo(DomainInfo *pFirstDomain)
|
||||
{
|
||||
DomainInfo *pDomain, *pKill;
|
||||
|
||||
for ( pDomain = pFirstDomain; pDomain != NIL(DomainInfo); ) {
|
||||
pKill = pDomain;
|
||||
pDomain = pDomain->next;
|
||||
FREE( pKill );
|
||||
}
|
||||
}
|
||||
|
||||
void ONEprintBoundaryInfo(BoundaryInfo *pFirstBoundary)
|
||||
{
|
||||
BoundaryInfo *pBoundary;
|
||||
|
||||
for ( pBoundary = pFirstBoundary; pBoundary != NIL(BoundaryInfo);
|
||||
pBoundary = pBoundary->next ) {
|
||||
fprintf( stderr,
|
||||
"boundary dom=%4d nbr=%4d ixLo=%4d ixHi=%4d\n",
|
||||
pBoundary->domain, pBoundary->neighbor,
|
||||
pBoundary->ixLo, pBoundary->ixHi );
|
||||
}
|
||||
}
|
||||
|
||||
void TWOprintBoundaryInfo(BoundaryInfo *pFirstBoundary)
|
||||
{
|
||||
BoundaryInfo *pBoundary;
|
||||
|
||||
for ( pBoundary = pFirstBoundary; pBoundary != NIL(BoundaryInfo);
|
||||
pBoundary = pBoundary->next ) {
|
||||
fprintf( stderr,
|
||||
"boundary dom=%4d nbr=%4d ixLo=%4d ixHi=%4d iyLo=%4d iyHi=%4d\n",
|
||||
pBoundary->domain, pBoundary->neighbor,
|
||||
pBoundary->ixLo, pBoundary->ixHi,
|
||||
pBoundary->iyLo, pBoundary->iyHi);
|
||||
}
|
||||
}
|
||||
|
||||
void killBoundaryInfo(BoundaryInfo *pFirstBoundary)
|
||||
{
|
||||
BoundaryInfo *pBoundary, *pKill;
|
||||
|
||||
for ( pBoundary = pFirstBoundary; pBoundary != NIL(BoundaryInfo); ) {
|
||||
pKill = pBoundary;
|
||||
pBoundary = pBoundary->next;
|
||||
FREE( pKill );
|
||||
}
|
||||
}
|
||||
|
||||
void TWOprintElectrodeInfo(ElectrodeInfo *pFirstElectrode)
|
||||
{
|
||||
ElectrodeInfo *pElectrode;
|
||||
|
||||
for ( pElectrode = pFirstElectrode; pElectrode != NIL(ElectrodeInfo);
|
||||
pElectrode = pElectrode->next ) {
|
||||
fprintf( stderr,
|
||||
"electrode id=%4d ixLo=%4d ixHi=%4d iyLo=%4d iyHi=%4d\n",
|
||||
pElectrode->id, pElectrode->ixLo, pElectrode->ixHi,
|
||||
pElectrode->iyLo, pElectrode->iyHi );
|
||||
}
|
||||
}
|
||||
|
||||
void killElectrodeInfo(ElectrodeInfo *pFirstElectrode)
|
||||
{
|
||||
ElectrodeInfo *pElectrode, *pKill;
|
||||
|
||||
for ( pElectrode = pFirstElectrode; pElectrode != NIL(ElectrodeInfo); ) {
|
||||
pKill = pElectrode;
|
||||
pElectrode = pElectrode->next;
|
||||
FREE( pKill );
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
/**********
|
||||
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
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numglobs.h"
|
||||
#include "numconst.h"
|
||||
#include "numenum.h"
|
||||
|
||||
/* Forward Declarations */
|
||||
void GLOBputGlobals(GLOBvalues *);
|
||||
void GLOBgetGlobals(GLOBvalues *);
|
||||
void GLOBprnGlobals(FILE *, GLOBvalues *);
|
||||
|
||||
/* Global Variable Declarations
|
||||
char *LogFileName = "cider.log";
|
||||
|
||||
int BandGapNarrowing;
|
||||
int TempDepMobility, ConcDepMobility, FieldDepMobility, TransDepMobility;
|
||||
int SurfaceMobility, MatchingMobility, MobDeriv;
|
||||
int CCScattering;
|
||||
int Srh, Auger, ConcDepLifetime, AvalancheGen;
|
||||
int FreezeOut = FALSE;
|
||||
int OneCarrier;
|
||||
|
||||
int MaxIterations = 100;
|
||||
int AcAnalysisMethod = DIRECT;
|
||||
|
||||
double Temp, RelTemp, Vt, RefPsi;
|
||||
double EpsNorm, VNorm, NNorm, LNorm, TNorm, JNorm, GNorm, ENorm;
|
||||
RefPsi is the potential at Infinity */
|
||||
|
||||
/*
|
||||
* Compute global values for this device.
|
||||
*/
|
||||
void GLOBcomputeGlobals(GLOBvalues *pGlobals, double temp)
|
||||
/* GLOBvalues *pGlobals Global Parameter Data Structure */
|
||||
/* double temp Instance Temperature */
|
||||
{
|
||||
double tmp1;
|
||||
double mnSi, mpSi; /* electron and hole conduction mass */
|
||||
double eg0; /* band gap */
|
||||
double nc0, nv0; /* conduction/valence band states */
|
||||
|
||||
/* compute temp. dependent global parameters */
|
||||
Temp = temp;
|
||||
RelTemp = Temp / 300.0;
|
||||
tmp1 = pow( RelTemp, 1.5 );
|
||||
|
||||
Vt = BOLTZMANN_CONSTANT * Temp / CHARGE;
|
||||
eg0 = EGAP300_SI + DGAPDT_SI * ( (300.0 * 300.0) / (300.0 + TREF_EG_SI)
|
||||
- (Temp * Temp) / (Temp + TREF_EG_SI) );
|
||||
mnSi = 1.039 + 5.477e-4 * Temp - 2.326e-7 * Temp * Temp;
|
||||
mpSi = 0.262 * log( 0.259 * Temp );
|
||||
nc0 = NCV_NOM * pow( mnSi, 1.5 ) * tmp1;
|
||||
nv0 = NCV_NOM * pow( mpSi, 1.5 ) * tmp1;
|
||||
RefPsi = 0.0;
|
||||
|
||||
/* set up the normalization factors */
|
||||
EpsNorm = EPS_SI;
|
||||
VNorm = Vt;
|
||||
NNorm = sqrt( nc0 ) * sqrt( nv0 ); /* this way no overflow */
|
||||
LNorm = sqrt( ( VNorm * EpsNorm ) / ( CHARGE * NNorm ) );
|
||||
TNorm = LNorm * LNorm / VNorm;
|
||||
JNorm = CHARGE * NNorm * VNorm / LNorm;
|
||||
GNorm = JNorm / VNorm;
|
||||
ENorm = VNorm / LNorm;
|
||||
|
||||
RefPsi /= VNorm;
|
||||
|
||||
/* Save Globals */
|
||||
GLOBputGlobals( pGlobals );
|
||||
/*
|
||||
* GLOBprnGlobals( stdout, pGlobals );
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
void GLOBputGlobals(GLOBvalues *values)
|
||||
{
|
||||
if ( values == NIL(GLOBvalues) ) {
|
||||
fprintf( stderr, "Error: tried to get from NIL GLOBvalues\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
/* Temperature-related globals */
|
||||
values->Temp = Temp;
|
||||
values->RelTemp = RelTemp;
|
||||
values->Vt = Vt;
|
||||
values->RefPsi = RefPsi;
|
||||
|
||||
/* Normalization Factors */
|
||||
values->EpsNorm = EpsNorm;
|
||||
values->VNorm = VNorm;
|
||||
values->NNorm = NNorm;
|
||||
values->LNorm = LNorm;
|
||||
values->TNorm = TNorm;
|
||||
values->JNorm = JNorm;
|
||||
values->GNorm = GNorm;
|
||||
values->ENorm = ENorm;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reload all globals needed during DEV loading routines
|
||||
* and DEV output routines
|
||||
*/
|
||||
void GLOBgetGlobals(GLOBvalues *values)
|
||||
{
|
||||
if ( values == NIL(GLOBvalues) ) {
|
||||
fprintf( stderr, "Error: tried to get from NIL GLOBvalues\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
/* Temperature-related globals */
|
||||
Temp = values->Temp;
|
||||
RelTemp = values->RelTemp;
|
||||
Vt = values->Vt;
|
||||
RefPsi = values->RefPsi;
|
||||
|
||||
/* Normalization Factors */
|
||||
EpsNorm = values->EpsNorm;
|
||||
VNorm = values->VNorm;
|
||||
NNorm = values->NNorm;
|
||||
LNorm = values->LNorm;
|
||||
TNorm = values->TNorm;
|
||||
JNorm = values->JNorm;
|
||||
GNorm = values->GNorm;
|
||||
ENorm = values->ENorm;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void GLOBprnGlobals(FILE *file, GLOBvalues *values)
|
||||
{
|
||||
static char *tabformat = "%12s: % .4e %-12s\t";
|
||||
static char *newformat = "%12s: % .4e %-12s\n";
|
||||
|
||||
if ( values == NIL( GLOBvalues ) ) {
|
||||
fprintf( stderr, "Error: tried to print NIL GLOBvalues\n");
|
||||
exit(-1);
|
||||
}
|
||||
fprintf( file, "*** GLOBAL PARAMETERS AT %g deg K\n", values->Temp );
|
||||
fprintf( file, "****** Temperature-Dependent Voltages\n" );
|
||||
fprintf( file, tabformat, "Vt", values->Vt, "V" );
|
||||
fprintf( file, newformat, "RefPsi", values->RefPsi * values->VNorm, "V" );
|
||||
fprintf( file, "****** Normalization Factors\n" );
|
||||
fprintf( file, newformat, "EpsNorm", values->EpsNorm, "F/cm" );
|
||||
fprintf( file, newformat, "VNorm", values->VNorm, "V" );
|
||||
fprintf( file, newformat, "NNorm", values->NNorm, "/cm^3" );
|
||||
fprintf( file, newformat, "LNorm", values->LNorm, "cm" );
|
||||
fprintf( file, newformat, "TNorm", values->TNorm, "s" );
|
||||
fprintf( file, newformat, "JNorm", values->JNorm, "A/cm^2" );
|
||||
fprintf( file, newformat, "GNorm", values->GNorm, "A/V" );
|
||||
fprintf( file, newformat, "ENorm", values->ENorm, "V/cm" );
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
/**********
|
||||
Copyright 1991 Regents of the University of California. All rights reserved.
|
||||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numenum.h"
|
||||
|
||||
/* compute the coefficient for the integration and predictor methods */
|
||||
/* based on the Lagrange polynomial method in Liniger et. al. */
|
||||
|
||||
void
|
||||
computeIntegCoeff(int method, int order, double *intCoeff, double *delta)
|
||||
{
|
||||
int i, j, k;
|
||||
double sum, temp, preMult;
|
||||
double num, denom, prod;
|
||||
|
||||
switch( method ) {
|
||||
case BDF:
|
||||
/* determine coeff[0] first */
|
||||
|
||||
sum = 0.0;
|
||||
temp = 0.0;
|
||||
for( j = 0 ; j < order; j++ ) {
|
||||
temp += delta[ j ];
|
||||
sum += 1.0 / temp;
|
||||
}
|
||||
intCoeff[ 0 ] = sum;
|
||||
|
||||
/* now compute the higher order coefficients */
|
||||
for( j = 1; j <= order ; j++ ) {
|
||||
/* compute the pre multiplier */
|
||||
temp = 0.0;
|
||||
for( i = 0; i < j; i++ ) {
|
||||
temp += delta[ i ];
|
||||
}
|
||||
preMult = 1.0 / temp;
|
||||
prod = 1.0;
|
||||
/* now compute the product */
|
||||
for( i = 1; i <= order; i++ ) {
|
||||
/* product loop */
|
||||
if( i != j ) {
|
||||
num = 0.0;
|
||||
for( k = 0; k < i; k++ ) {
|
||||
/* first the numerator */
|
||||
num += delta[ k ];
|
||||
}
|
||||
if( i > j ) {
|
||||
/* denom is positive */
|
||||
denom = 0.0;
|
||||
for( k = j; k < i; k++ ) {
|
||||
denom += delta[ k ];
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* i < j */
|
||||
denom = 0.0;
|
||||
for( k = i; k < j; k++ ) {
|
||||
denom += delta[ k ];
|
||||
}
|
||||
denom = -denom;
|
||||
}
|
||||
prod *= num / denom ;
|
||||
}
|
||||
}
|
||||
intCoeff[ j ] = -preMult * prod;
|
||||
}
|
||||
break;
|
||||
case TRAPEZOIDAL:
|
||||
default:
|
||||
switch( order ) {
|
||||
case 1:
|
||||
temp = 1.0 / delta[ 0 ];
|
||||
intCoeff[ 0 ] = temp;
|
||||
intCoeff[ 1 ] = -temp;
|
||||
break;
|
||||
case 2:
|
||||
temp = 2.0 / delta[ 0 ];
|
||||
intCoeff[ 0 ] = temp;
|
||||
intCoeff[ 1 ] = -temp;
|
||||
intCoeff[ 2 ] = -1.0;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
computePredCoeff(int method, int order, double *predCoeff, double *delta)
|
||||
{
|
||||
int i, j, k;
|
||||
double num, denom, prod, temp;
|
||||
|
||||
if( method == TRAPEZOIDAL && order > 2 ) {
|
||||
printf("\n computePredCoeff: order > 2 for trapezoidal");
|
||||
exit( -1 );
|
||||
}
|
||||
for( j = 1; j <= order+1 ; j++ ) {
|
||||
prod = 1.0;
|
||||
/* now compute the product */
|
||||
for( i = 1; i <= order+1; i++ ) {
|
||||
/* product loop */
|
||||
if( i != j ) {
|
||||
num = 0.0;
|
||||
for( k = 0; k < i; k++ ) {
|
||||
/* first the numerator */
|
||||
num += delta[ k ];
|
||||
}
|
||||
if( i > j ) {
|
||||
/* denom is positive */
|
||||
denom = 0.0;
|
||||
for( k = j; k < i; k++ ) {
|
||||
denom += delta[ k ];
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* i < j */
|
||||
denom = 0.0;
|
||||
for( k = i; k < j; k++ ) {
|
||||
denom += delta[ k ];
|
||||
}
|
||||
denom = -denom;
|
||||
}
|
||||
prod *= num / denom ;
|
||||
}
|
||||
}
|
||||
predCoeff[ j - 1 ] = prod;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* main program to check the coefficients
|
||||
main()
|
||||
{
|
||||
double intCoeff[ 7 ], predCoeff[ 7 ];
|
||||
double delta[ 7 ];
|
||||
int order = 1;
|
||||
int i;
|
||||
|
||||
for( i = 0; i <= 6; i++ ) {
|
||||
delta[ i ] = 1.0;
|
||||
}
|
||||
|
||||
computeIntegCoeff(TRAPEZOIDAL, order, intCoeff, delta );
|
||||
computePredCoeff(TRAPEZOIDAL, order, predCoeff, delta );
|
||||
|
||||
for(i = 0; i <= order; i++ ) {
|
||||
printf("\n IntCoeff[ %d ] = %e PredCoeff[ %d ] = %e ", i, intCoeff[ i ], i, predCoeff[ i ] );
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
@ -0,0 +1,302 @@
|
|||
/**********
|
||||
Copyright 1991 Regents of the University of California. All rights reserved.
|
||||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numglobs.h"
|
||||
#include "numenum.h"
|
||||
#include "gendev.h"
|
||||
|
||||
/* function to compute the integrated variables discretization */
|
||||
|
||||
#define ccap qcap+1
|
||||
|
||||
double
|
||||
integrate(double **devStates, TranInfo *info, int qcap )
|
||||
{
|
||||
double value;
|
||||
double *coeff = info->intCoeff;
|
||||
|
||||
switch ( info->method ) {
|
||||
case BDF:
|
||||
switch( info->order ) {
|
||||
case 1:
|
||||
value = coeff[0] * (*(devStates[0]+qcap)) +
|
||||
coeff[1] * (*(devStates[1]+qcap));
|
||||
break;
|
||||
case 2:
|
||||
value = coeff[0] * (*(devStates[0]+qcap)) +
|
||||
coeff[1] * (*(devStates[1]+qcap)) +
|
||||
coeff[2] * (*(devStates[2]+qcap));
|
||||
break;
|
||||
case 3:
|
||||
value = coeff[0] * (*(devStates[0]+qcap)) +
|
||||
coeff[1] * (*(devStates[1]+qcap)) +
|
||||
coeff[2] * (*(devStates[2]+qcap)) +
|
||||
coeff[3] * (*(devStates[3]+qcap));
|
||||
break;
|
||||
case 4:
|
||||
value = coeff[0] * (*(devStates[0]+qcap)) +
|
||||
coeff[1] * (*(devStates[1]+qcap)) +
|
||||
coeff[2] * (*(devStates[2]+qcap)) +
|
||||
coeff[3] * (*(devStates[3]+qcap)) +
|
||||
coeff[4] * (*(devStates[4]+qcap));
|
||||
break;
|
||||
case 5:
|
||||
value = coeff[0] * (*(devStates[0]+qcap)) +
|
||||
coeff[1] * (*(devStates[1]+qcap)) +
|
||||
coeff[2] * (*(devStates[2]+qcap)) +
|
||||
coeff[3] * (*(devStates[3]+qcap)) +
|
||||
coeff[4] * (*(devStates[4]+qcap)) +
|
||||
coeff[5] * (*(devStates[5]+qcap));
|
||||
break;
|
||||
case 6:
|
||||
value = coeff[0] * (*(devStates[0]+qcap)) +
|
||||
coeff[1] * (*(devStates[1]+qcap)) +
|
||||
coeff[2] * (*(devStates[2]+qcap)) +
|
||||
coeff[3] * (*(devStates[3]+qcap)) +
|
||||
coeff[4] * (*(devStates[4]+qcap)) +
|
||||
coeff[5] * (*(devStates[5]+qcap)) +
|
||||
coeff[6] * (*(devStates[6]+qcap));
|
||||
break;
|
||||
default:
|
||||
printf( "\n integration order %d !! STOP \n", info->order );
|
||||
exit( 0 );
|
||||
}
|
||||
break;
|
||||
case TRAPEZOIDAL:
|
||||
default:
|
||||
switch( info->order ) {
|
||||
case 1:
|
||||
value = coeff[0] * (*(devStates[0]+qcap)) +
|
||||
coeff[1] * (*(devStates[1]+qcap));
|
||||
*(devStates[0]+ccap) = value;
|
||||
break;
|
||||
case 2:
|
||||
value = coeff[0] * (*(devStates[0]+qcap)) +
|
||||
coeff[1] * (*(devStates[1]+qcap)) +
|
||||
coeff[2] * (*(devStates[1]+ccap));
|
||||
*(devStates[0]+ccap) = value;
|
||||
break;
|
||||
default:
|
||||
printf( "\n integration order %d !! STOP \n", info->order );
|
||||
exit( 0 );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return( value );
|
||||
}
|
||||
|
||||
/* function to predict the value of the variables */
|
||||
|
||||
double
|
||||
predict(double **devStates, TranInfo *info, int qcap )
|
||||
{
|
||||
double value;
|
||||
double *coeff = info->predCoeff;
|
||||
|
||||
switch ( info->method ) {
|
||||
case BDF:
|
||||
switch( info->order ) {
|
||||
case 1:
|
||||
value = coeff[0] * (*(devStates[1]+qcap)) +
|
||||
coeff[1] * (*(devStates[2]+qcap));
|
||||
break;
|
||||
case 2:
|
||||
value = coeff[0] * (*(devStates[1]+qcap)) +
|
||||
coeff[1] * (*(devStates[2]+qcap)) +
|
||||
coeff[2] * (*(devStates[3]+qcap));
|
||||
break;
|
||||
case 3:
|
||||
value = coeff[0] * (*(devStates[1]+qcap)) +
|
||||
coeff[1] * (*(devStates[2]+qcap)) +
|
||||
coeff[2] * (*(devStates[3]+qcap)) +
|
||||
coeff[3] * (*(devStates[4]+qcap));
|
||||
break;
|
||||
case 4:
|
||||
value = coeff[0] * (*(devStates[1]+qcap)) +
|
||||
coeff[1] * (*(devStates[2]+qcap)) +
|
||||
coeff[2] * (*(devStates[3]+qcap)) +
|
||||
coeff[3] * (*(devStates[4]+qcap)) +
|
||||
coeff[4] * (*(devStates[5]+qcap));
|
||||
break;
|
||||
case 5:
|
||||
value = coeff[0] * (*(devStates[1]+qcap)) +
|
||||
coeff[1] * (*(devStates[2]+qcap)) +
|
||||
coeff[2] * (*(devStates[3]+qcap)) +
|
||||
coeff[3] * (*(devStates[4]+qcap)) +
|
||||
coeff[4] * (*(devStates[5]+qcap)) +
|
||||
coeff[5] * (*(devStates[6]+qcap));
|
||||
break;
|
||||
case 6:
|
||||
value = coeff[0] * (*(devStates[1]+qcap)) +
|
||||
coeff[1] * (*(devStates[2]+qcap)) +
|
||||
coeff[2] * (*(devStates[3]+qcap)) +
|
||||
coeff[3] * (*(devStates[4]+qcap)) +
|
||||
coeff[4] * (*(devStates[5]+qcap)) +
|
||||
coeff[5] * (*(devStates[6]+qcap)) +
|
||||
coeff[6] * (*(devStates[7]+qcap));
|
||||
break;
|
||||
default:
|
||||
printf( "\n prediction order %d !! STOP \n", info->order );
|
||||
exit( 0 );
|
||||
}
|
||||
break;
|
||||
case TRAPEZOIDAL:
|
||||
default:
|
||||
switch( info->order ) {
|
||||
case 1:
|
||||
value = coeff[0] * (*(devStates[1]+qcap)) +
|
||||
coeff[1] * (*(devStates[2]+qcap));
|
||||
break;
|
||||
case 2:
|
||||
value = coeff[0] * (*(devStates[1]+qcap)) +
|
||||
coeff[1] * (*(devStates[2]+qcap)) +
|
||||
coeff[2] * (*(devStates[3]+qcap));
|
||||
/*
|
||||
value = coeff[0] * (*(devStates[1]+qcap)) +
|
||||
coeff[1] * (*(devStates[2]+qcap)) +
|
||||
coeff[2] * (*(devStates[1]+ccap));
|
||||
*/
|
||||
break;
|
||||
default:
|
||||
printf( "\n prediction order %d !! STOP \n", info->order );
|
||||
exit( 0 );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return( value );
|
||||
}
|
||||
|
||||
double
|
||||
computeLTECoeff( TranInfo *info )
|
||||
{
|
||||
double *delta = info->delta;
|
||||
double temp, denom, lteCoeff;
|
||||
|
||||
switch ( info->method ) {
|
||||
case BDF:
|
||||
switch( info->order ) {
|
||||
case 1:
|
||||
denom = delta[ 0 ] + delta[ 1 ];
|
||||
break;
|
||||
case 2:
|
||||
denom = delta[ 0 ] + delta[ 1 ] + delta[ 2 ];
|
||||
break;
|
||||
case 3:
|
||||
denom = delta[ 0 ] + delta[ 1 ] + delta[ 2 ] +
|
||||
delta[ 3 ];
|
||||
break;
|
||||
case 4:
|
||||
denom = delta[ 0 ] + delta[ 1 ] + delta[ 2 ] +
|
||||
delta[ 3 ] + delta[ 4 ];
|
||||
break;
|
||||
case 5:
|
||||
denom = delta[ 0 ] + delta[ 1 ] + delta[ 2 ] +
|
||||
delta[ 3 ] + delta[ 4 ] + delta[ 5 ];
|
||||
break;
|
||||
case 6:
|
||||
denom = delta[ 0 ] + delta[ 1 ] + delta[ 2 ] +
|
||||
delta[ 3 ] + delta[ 4 ] + delta[ 5 ] + delta[ 6 ];
|
||||
break;
|
||||
default:
|
||||
printf( "\n integration order %d !! STOP \n", info->order );
|
||||
exit( 0 );
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case TRAPEZOIDAL:
|
||||
default:
|
||||
switch( info->order ) {
|
||||
case 1:
|
||||
denom = delta[ 0 ] + delta[ 1 ];
|
||||
break;
|
||||
case 2:
|
||||
/*
|
||||
denom = delta[ 0 ] + delta[ 1 ];
|
||||
*/
|
||||
temp = delta[ 0 ] + delta [ 1 ];
|
||||
denom = 2.0 * temp * (temp + delta[ 2 ]) / delta[ 0 ];
|
||||
break;
|
||||
default:
|
||||
printf( "\n integration order %d !! STOP \n", info->order );
|
||||
exit( 0 );
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
lteCoeff = delta[ 0 ] / denom;
|
||||
return( lteCoeff );
|
||||
}
|
||||
|
||||
/* function to integrate a linear DAE */
|
||||
double
|
||||
integrateLin(double **devStates, TranInfo *info, int qcap )
|
||||
{
|
||||
double value;
|
||||
double *coeff = info->intCoeff;
|
||||
|
||||
switch ( info->method ) {
|
||||
case BDF:
|
||||
switch( info->order ) {
|
||||
case 1:
|
||||
value = coeff[1] * (*(devStates[1]+qcap));
|
||||
break;
|
||||
case 2:
|
||||
value = coeff[1] * (*(devStates[1]+qcap)) +
|
||||
coeff[2] * (*(devStates[2]+qcap));
|
||||
break;
|
||||
case 3:
|
||||
value = coeff[1] * (*(devStates[1]+qcap)) +
|
||||
coeff[2] * (*(devStates[2]+qcap)) +
|
||||
coeff[3] * (*(devStates[3]+qcap));
|
||||
break;
|
||||
case 4:
|
||||
value = coeff[1] * (*(devStates[1]+qcap)) +
|
||||
coeff[2] * (*(devStates[2]+qcap)) +
|
||||
coeff[3] * (*(devStates[3]+qcap)) +
|
||||
coeff[4] * (*(devStates[4]+qcap));
|
||||
break;
|
||||
case 5:
|
||||
value = coeff[1] * (*(devStates[1]+qcap)) +
|
||||
coeff[2] * (*(devStates[2]+qcap)) +
|
||||
coeff[3] * (*(devStates[3]+qcap)) +
|
||||
coeff[4] * (*(devStates[4]+qcap)) +
|
||||
coeff[5] * (*(devStates[5]+qcap));
|
||||
break;
|
||||
case 6:
|
||||
value = coeff[1] * (*(devStates[1]+qcap)) +
|
||||
coeff[2] * (*(devStates[2]+qcap)) +
|
||||
coeff[3] * (*(devStates[3]+qcap)) +
|
||||
coeff[4] * (*(devStates[4]+qcap)) +
|
||||
coeff[5] * (*(devStates[5]+qcap)) +
|
||||
coeff[6] * (*(devStates[6]+qcap));
|
||||
break;
|
||||
default:
|
||||
printf( "\n integration order %d !! STOP \n", info->order );
|
||||
exit( 0 );
|
||||
}
|
||||
break;
|
||||
case TRAPEZOIDAL:
|
||||
default:
|
||||
switch( info->order ) {
|
||||
case 1:
|
||||
value = coeff[1] * (*(devStates[1]+qcap));
|
||||
break;
|
||||
case 2:
|
||||
value = coeff[1] * (*(devStates[1]+qcap)) +
|
||||
coeff[2] * (*(devStates[1]+ccap));
|
||||
break;
|
||||
default:
|
||||
printf( "\n integration order %d !! STOP \n", info->order );
|
||||
exit( 0 );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return( value );
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
/**********
|
||||
Copyright 1992 Regents of the University of California. All rights reserved.
|
||||
Author: 1992 David A. Gates, U. C. Berkeley CAD Group
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
|
||||
static char *LogFileName = "cider.log";
|
||||
static int LogError = 0;
|
||||
|
||||
void
|
||||
LOGmakeEntry(name, description)
|
||||
char *name;
|
||||
char *description;
|
||||
{
|
||||
int procStamp;
|
||||
FILE *fpLog;
|
||||
|
||||
#ifdef HAS_GETPID
|
||||
procStamp = getpid();
|
||||
#else
|
||||
procStamp = 0;
|
||||
#endif
|
||||
|
||||
/* Want to make sure that multiple processes can access the log file
|
||||
* without stepping on each other.
|
||||
*/
|
||||
#ifdef ultrix
|
||||
if (!(fpLog = fopen(LogFileName, "A"))) {
|
||||
#else
|
||||
if (!(fpLog = fopen(LogFileName, "a"))) {
|
||||
#endif
|
||||
if (!LogError)
|
||||
perror(LogFileName);
|
||||
LogError = 1;
|
||||
} else {
|
||||
fprintf(fpLog, "<%05d> %s: %s\n", procStamp, name, description);
|
||||
fclose(fpLog);
|
||||
LogError = 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,404 @@
|
|||
/**********
|
||||
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
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numglobs.h"
|
||||
#include "numconst.h"
|
||||
#include "numenum.h"
|
||||
#include "material.h"
|
||||
|
||||
/* Forward Declarations */
|
||||
void printMaterialInfo(MaterialInfo *info);
|
||||
|
||||
/* External Symbols */
|
||||
extern void MOBtempDep (MaterialInfo *, double);
|
||||
|
||||
|
||||
/*
|
||||
* Set material info values to their defaults.
|
||||
*/
|
||||
void
|
||||
MATLdefaults(MaterialInfo *info)
|
||||
{
|
||||
if ((info->material == OXIDE) || (info->material == INSULATOR)) {
|
||||
info->type = INSULATOR;
|
||||
info->eps = EPS_OX;
|
||||
info->affin = AFFIN_OX;
|
||||
info->eg0 = EGAP300_OX;
|
||||
} else if (info->material == NITRIDE) {
|
||||
info->type = INSULATOR;
|
||||
info->eps = EPS_NI;
|
||||
info->affin = AFFIN_NI;
|
||||
info->eg0 = EGAP300_NI;
|
||||
} else if (info->material == POLYSILICON) {
|
||||
info->type = SEMICON;
|
||||
info->eps = EPS_SI;
|
||||
info->affin = AFFIN_SI;
|
||||
info->nc0 = 0.0;
|
||||
info->nv0 = 0.0;
|
||||
info->eg0 = EGAP300_SI;
|
||||
info->dEgDt = DGAPDT_SI;
|
||||
info->trefBGN = TREF_EG_SI;
|
||||
info->dEgDn[ELEC] = DGAPDN_N;
|
||||
info->dEgDn[HOLE] = DGAPDN_P;
|
||||
info->nrefBGN[ELEC] = NBGN_N;
|
||||
info->nrefBGN[HOLE] = NBGN_P;
|
||||
info->tau0[ELEC] = TAU0_N_SI;
|
||||
info->tau0[HOLE] = TAU0_P_SI;
|
||||
info->nrefSRH[ELEC] = NSRH_N_SI;
|
||||
info->nrefSRH[HOLE] = NSRH_P_SI;
|
||||
info->cAug[ELEC] = C_AUG_N_SI;
|
||||
info->cAug[HOLE] = C_AUG_P_SI;
|
||||
info->aRich[ELEC] = A_RICH_N_SI;
|
||||
info->aRich[HOLE] = A_RICH_P_SI;
|
||||
info->eDon = E_DON_SI;
|
||||
info->eAcc = E_ACC_SI;
|
||||
info->gDon = G_DON_SI;
|
||||
info->gAcc = G_ACC_SI;
|
||||
info->concModel = CT;
|
||||
info->muMax[ELEC][MAJOR] = 0.07 * AR_MUMAX_N;
|
||||
info->muMin[ELEC][MAJOR] = 0.07 * AR_MUMIN_N;
|
||||
info->ntRef[ELEC][MAJOR] = AR_NTREF_N;
|
||||
info->ntExp[ELEC][MAJOR] = AR_NTEXP_N;
|
||||
info->muMax[HOLE][MAJOR] = 0.07 * AR_MUMAX_P;
|
||||
info->muMin[HOLE][MAJOR] = 0.07 * AR_MUMIN_P;
|
||||
info->ntRef[HOLE][MAJOR] = AR_NTREF_P;
|
||||
info->ntExp[HOLE][MAJOR] = AR_NTEXP_P;
|
||||
info->muMax[ELEC][MINOR] = 0.07 * UF_MUMAX_N;
|
||||
info->muMin[ELEC][MINOR] = 0.07 * UF_MUMIN_N;
|
||||
info->ntRef[ELEC][MINOR] = UF_NTREF_N;
|
||||
info->ntExp[ELEC][MINOR] = UF_NTEXP_N;
|
||||
info->muMax[HOLE][MINOR] = 0.07 * UF_MUMAX_P;
|
||||
info->muMin[HOLE][MINOR] = 0.07 * UF_MUMIN_P;
|
||||
info->ntRef[HOLE][MINOR] = UF_NTREF_P;
|
||||
info->ntExp[HOLE][MINOR] = UF_NTEXP_P;
|
||||
info->fieldModel = CT;
|
||||
info->vSat[ELEC] = AR_VSAT_N;
|
||||
info->vSat[HOLE] = AR_VSAT_P;
|
||||
info->vWarm[ELEC] = SG_VWARM_N;
|
||||
info->vWarm[HOLE] = SG_VWARM_P;
|
||||
info->mus[ELEC] = 0.07 * MUS_N;
|
||||
info->thetaA[ELEC] = THETAA_N;
|
||||
info->thetaB[ELEC] = THETAB_N;
|
||||
info->mus[HOLE] = 0.07 * MUS_P;
|
||||
info->thetaA[HOLE] = THETAA_P;
|
||||
info->thetaB[HOLE] = THETAB_P;
|
||||
} else if ((info->material == SILICON) || (info->material == SEMICON)) {
|
||||
info->type = SEMICON;
|
||||
info->eps = EPS_SI;
|
||||
info->affin = AFFIN_SI;
|
||||
info->nc0 = 0.0;
|
||||
info->nv0 = 0.0;
|
||||
info->eg0 = EGAP300_SI;
|
||||
info->dEgDt = DGAPDT_SI;
|
||||
info->trefBGN = TREF_EG_SI;
|
||||
info->dEgDn[ELEC] = DGAPDN_N;
|
||||
info->dEgDn[HOLE] = DGAPDN_P;
|
||||
info->nrefBGN[ELEC] = NBGN_N;
|
||||
info->nrefBGN[HOLE] = NBGN_P;
|
||||
info->tau0[ELEC] = TAU0_N_SI;
|
||||
info->tau0[HOLE] = TAU0_P_SI;
|
||||
info->nrefSRH[ELEC] = NSRH_N_SI;
|
||||
info->nrefSRH[HOLE] = NSRH_P_SI;
|
||||
info->cAug[ELEC] = C_AUG_N_SI;
|
||||
info->cAug[HOLE] = C_AUG_P_SI;
|
||||
info->aRich[ELEC] = A_RICH_N_SI;
|
||||
info->aRich[HOLE] = A_RICH_P_SI;
|
||||
info->eDon = E_DON_SI;
|
||||
info->eAcc = E_ACC_SI;
|
||||
info->gDon = G_DON_SI;
|
||||
info->gAcc = G_ACC_SI;
|
||||
info->concModel = CT;
|
||||
info->muMax[ELEC][MAJOR] = AR_MUMAX_N;
|
||||
info->muMin[ELEC][MAJOR] = AR_MUMIN_N;
|
||||
info->ntRef[ELEC][MAJOR] = AR_NTREF_N;
|
||||
info->ntExp[ELEC][MAJOR] = AR_NTEXP_N;
|
||||
info->muMax[HOLE][MAJOR] = AR_MUMAX_P;
|
||||
info->muMin[HOLE][MAJOR] = AR_MUMIN_P;
|
||||
info->ntRef[HOLE][MAJOR] = AR_NTREF_P;
|
||||
info->ntExp[HOLE][MAJOR] = AR_NTEXP_P;
|
||||
info->muMax[ELEC][MINOR] = UF_MUMAX_N;
|
||||
info->muMin[ELEC][MINOR] = UF_MUMIN_N;
|
||||
info->ntRef[ELEC][MINOR] = UF_NTREF_N;
|
||||
info->ntExp[ELEC][MINOR] = UF_NTEXP_N;
|
||||
info->muMax[HOLE][MINOR] = UF_MUMAX_P;
|
||||
info->muMin[HOLE][MINOR] = UF_MUMIN_P;
|
||||
info->ntRef[HOLE][MINOR] = UF_NTREF_P;
|
||||
info->ntExp[HOLE][MINOR] = UF_NTEXP_P;
|
||||
info->fieldModel = CT;
|
||||
info->vSat[ELEC] = AR_VSAT_N;
|
||||
info->vSat[HOLE] = AR_VSAT_P;
|
||||
info->vWarm[ELEC] = SG_VWARM_N;
|
||||
info->vWarm[HOLE] = SG_VWARM_P;
|
||||
info->mus[ELEC] = MUS_N;
|
||||
info->thetaA[ELEC] = THETAA_N;
|
||||
info->thetaB[ELEC] = THETAB_N;
|
||||
info->mus[HOLE] = MUS_P;
|
||||
info->thetaA[HOLE] = THETAA_P;
|
||||
info->thetaB[HOLE] = THETAB_P;
|
||||
} else if (info->material == GAAS) {
|
||||
info->type = SEMICON;
|
||||
info->eps = EPS_GA;
|
||||
info->affin = AFFIN_GA;
|
||||
info->nc0 = NCV_NOM * pow(M_N_GA, 1.5);
|
||||
info->nv0 = NCV_NOM * pow(M_P_GA, 1.5);
|
||||
info->eg0 = EGAP300_GA;
|
||||
info->dEgDt = DGAPDT_GA;
|
||||
info->trefBGN = TREF_EG_GA;
|
||||
info->dEgDn[ELEC] = DGAPDN_N;
|
||||
info->dEgDn[HOLE] = DGAPDN_P;
|
||||
info->nrefBGN[ELEC] = NBGN_N;
|
||||
info->nrefBGN[HOLE] = NBGN_P;
|
||||
info->tau0[ELEC] = TAU0_N_GA;
|
||||
info->tau0[HOLE] = TAU0_P_GA;
|
||||
info->nrefSRH[ELEC] = NSRH_N_GA;
|
||||
info->nrefSRH[HOLE] = NSRH_P_GA;
|
||||
info->cAug[ELEC] = C_AUG_N_GA;
|
||||
info->cAug[HOLE] = C_AUG_P_GA;
|
||||
info->aRich[ELEC] = A_RICH_N_GA;
|
||||
info->aRich[HOLE] = A_RICH_P_GA;
|
||||
info->eDon = E_DON_GA;
|
||||
info->eAcc = E_ACC_GA;
|
||||
info->gDon = G_DON_GA;
|
||||
info->gAcc = G_ACC_GA;
|
||||
info->concModel = GA;
|
||||
info->muMax[ELEC][MAJOR] = GA_MUMAX_N;
|
||||
info->muMin[ELEC][MAJOR] = GA_MUMIN_N;
|
||||
info->ntRef[ELEC][MAJOR] = GA_NTREF_N;
|
||||
info->ntExp[ELEC][MAJOR] = GA_NTEXP_N;
|
||||
info->muMax[HOLE][MAJOR] = GA_MUMAX_P;
|
||||
info->muMin[HOLE][MAJOR] = GA_MUMIN_P;
|
||||
info->ntRef[HOLE][MAJOR] = GA_NTREF_P;
|
||||
info->ntExp[HOLE][MAJOR] = GA_NTEXP_P;
|
||||
info->muMax[ELEC][MINOR] = GA_MUMAX_N;
|
||||
info->muMin[ELEC][MINOR] = GA_MUMIN_N;
|
||||
info->ntRef[ELEC][MINOR] = GA_NTREF_N;
|
||||
info->ntExp[ELEC][MINOR] = GA_NTEXP_N;
|
||||
info->muMax[HOLE][MINOR] = GA_MUMAX_P;
|
||||
info->muMin[HOLE][MINOR] = GA_MUMIN_P;
|
||||
info->ntRef[HOLE][MINOR] = GA_NTREF_P;
|
||||
info->ntExp[HOLE][MINOR] = GA_NTEXP_P;
|
||||
info->fieldModel = GA;
|
||||
info->vSat[ELEC] = GA_VSAT_N;
|
||||
info->vSat[HOLE] = GA_VSAT_P;
|
||||
info->vWarm[ELEC] = GA_VWARM_N;
|
||||
info->vWarm[HOLE] = GA_VWARM_P;
|
||||
info->mus[ELEC] = MUS_N;
|
||||
info->thetaA[ELEC] = THETAA_N;
|
||||
info->thetaB[ELEC] = THETAB_N;
|
||||
info->mus[HOLE] = MUS_P;
|
||||
info->thetaA[HOLE] = THETAA_P;
|
||||
info->thetaB[HOLE] = THETAB_P;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute the temperature-dependent physical parameters of materials
|
||||
* Normalize physical constants Actual Instance Temperature is passed in thru
|
||||
* the global var 'Temp'
|
||||
*/
|
||||
void
|
||||
MATLtempDep(MaterialInfo *info, double tnom)
|
||||
/* double tnom Nominal Parameter Temperature */
|
||||
{
|
||||
double tmp1;
|
||||
double relTemp, perRelTemp;
|
||||
double eg0;
|
||||
|
||||
if (info->type == INSULATOR) {
|
||||
info->refPsi = RefPsi - (info->affin + 0.5 * info->eg0) / VNorm;
|
||||
} else if (info->type == SEMICON) {
|
||||
|
||||
/* compute temperature dependent semiconductor parameters */
|
||||
relTemp = Temp / tnom;
|
||||
perRelTemp = 1.0 / relTemp;
|
||||
tmp1 = pow(relTemp, 1.5);
|
||||
|
||||
/* Bandgap and intrinsic concentration */
|
||||
eg0 = info->eg0 + (info->dEgDt * tnom * tnom) / (tnom + info->trefBGN);
|
||||
info->eg0 = eg0 - (info->dEgDt * Temp * Temp) / (Temp + info->trefBGN);
|
||||
if (info->nc0 > 0.0) {
|
||||
info->mass[ELEC] = pow(info->nc0 / NCV_NOM / tmp1, 2.0 / 3.0);
|
||||
} else {
|
||||
info->mass[ELEC] = 1.039 + 5.477e-4 * Temp - 2.326e-7 * Temp * Temp;
|
||||
}
|
||||
if (info->nv0 > 0.0) {
|
||||
info->mass[HOLE] = pow(info->nv0 / NCV_NOM / tmp1, 2.0 / 3.0);
|
||||
} else {
|
||||
info->mass[HOLE] = 0.262 * log(0.259 * Temp);
|
||||
}
|
||||
info->nc0 = NCV_NOM * pow(info->mass[ELEC], 1.5) * tmp1;
|
||||
info->nv0 = NCV_NOM * pow(info->mass[HOLE], 1.5) * tmp1;
|
||||
info->ni0 = sqrt(info->nc0) * sqrt(info->nv0) *
|
||||
exp(-0.5 * info->eg0 / Vt);
|
||||
info->refPsi = RefPsi - (info->affin
|
||||
+ 0.5 * (info->eg0 + Vt * log(info->nc0 / info->nv0))) / VNorm;
|
||||
|
||||
/* Impurity energies */
|
||||
info->eDon /= VNorm;
|
||||
info->eAcc /= VNorm;
|
||||
|
||||
/* SRH lifetimes */
|
||||
tmp1 = sqrt(perRelTemp) * exp(3.8667 * (perRelTemp - 1.0));
|
||||
info->tau0[ELEC] *= tmp1 / TNorm;
|
||||
info->tau0[HOLE] *= tmp1 / TNorm;
|
||||
|
||||
/* Auger recombination coefficients */
|
||||
info->cAug[ELEC] *= pow(relTemp, 0.14) * NNorm * NNorm * TNorm;
|
||||
info->cAug[HOLE] *= pow(relTemp, 0.18) * NNorm * NNorm * TNorm;
|
||||
|
||||
/* Avalanche generation parameters */
|
||||
info->aii[ELEC] = AII_N * LNorm;
|
||||
info->bii[ELEC] = BII_N / ENorm;
|
||||
info->aii[HOLE] = AII_P * LNorm;
|
||||
info->bii[HOLE] = BII_P / ENorm;
|
||||
|
||||
/* Effective recombination velocities */
|
||||
info->vRich[ELEC] = info->aRich[ELEC] * Temp * Temp /
|
||||
(CHARGE * info->nc0 * ENorm);
|
||||
info->vRich[HOLE] = info->aRich[HOLE] * Temp * Temp /
|
||||
(CHARGE * info->nv0 * ENorm);
|
||||
|
||||
/* Mobility Temperature Dependence */
|
||||
MOBtempDep(info, Temp);
|
||||
|
||||
/* Velocity Saturation Parameters */
|
||||
info->vSat[ELEC] /= ENorm;
|
||||
info->vWarm[ELEC] /= ENorm;
|
||||
info->vSat[HOLE] /= ENorm;
|
||||
info->vWarm[HOLE] /= ENorm;
|
||||
|
||||
/* Normal Field Mobility Degradation Parameters */
|
||||
info->thetaA[ELEC] *= ENorm;
|
||||
info->thetaB[ELEC] *= ENorm * ENorm;
|
||||
info->thetaA[HOLE] *= ENorm;
|
||||
info->thetaB[HOLE] *= ENorm * ENorm;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
printMaterialInfo(MaterialInfo *info)
|
||||
{
|
||||
static char *tabformat = "%12s: % .4e %-12s\t";
|
||||
static char *newformat = "%12s: % .4e %-12s\n";
|
||||
|
||||
char *name;
|
||||
|
||||
|
||||
if (info == NIL(MaterialInfo)) {
|
||||
fprintf(stderr, "Error: tried to print NIL MaterialInfo\n");
|
||||
exit(-1);
|
||||
}
|
||||
/* Find material name. */
|
||||
switch (info->material) {
|
||||
case OXIDE:
|
||||
name = "OXIDE";
|
||||
break;
|
||||
case NITRIDE:
|
||||
name = "NITRIDE";
|
||||
break;
|
||||
case INSULATOR:
|
||||
name = "INSULATOR";
|
||||
break;
|
||||
case SILICON:
|
||||
name = "SILICON";
|
||||
break;
|
||||
case POLYSILICON:
|
||||
name = "POLYSILICON";
|
||||
break;
|
||||
case GAAS:
|
||||
name = "GAAS";
|
||||
break;
|
||||
case SEMICON:
|
||||
name = "SEMICONDUCTOR";
|
||||
break;
|
||||
default:
|
||||
name = "MATERIAL";
|
||||
break;
|
||||
}
|
||||
if (info->type == INSULATOR) {
|
||||
fprintf(stdout, "***** %s PARAMETERS AT %g deg K\n", name, Temp);
|
||||
fprintf(stdout, "*** Poisson Equation Parameters -\n");
|
||||
fprintf(stdout, tabformat, "Eps", info->eps, "F/cm");
|
||||
fprintf(stdout, newformat, "Affin", info->affin, "eV");
|
||||
fprintf(stdout, tabformat, "Egap", info->eg0, "eV");
|
||||
fprintf(stdout, newformat, "PsiB", -info->refPsi * VNorm, "V");
|
||||
} else if (info->type == SEMICON) {
|
||||
fprintf(stdout, "***** %s PARAMETERS AT %g deg K\n", name, Temp);
|
||||
fprintf(stdout, "*** Poisson Equation\n");
|
||||
fprintf(stdout, tabformat, "Eps", info->eps, "F/cm");
|
||||
fprintf(stdout, newformat, "Affin", info->affin, "eV");
|
||||
fprintf(stdout, tabformat, "Vt", Vt, "V");
|
||||
fprintf(stdout, newformat, "Ni", info->ni0, "/cm^3");
|
||||
fprintf(stdout, tabformat, "Nc", info->nc0, "/cm^3");
|
||||
fprintf(stdout, newformat, "Nv", info->nv0, "/cm^3");
|
||||
fprintf(stdout, tabformat, "MnSi", info->mass[ELEC], "*m0 kg");
|
||||
fprintf(stdout, newformat, "MpSi", info->mass[HOLE], "*m0 kg");
|
||||
fprintf(stdout, tabformat, "Egap", info->eg0, "eV");
|
||||
fprintf(stdout, newformat, "PsiB", -info->refPsi * VNorm, "V");
|
||||
fprintf(stdout, tabformat, "dEg/dT", info->dEgDt, "eV");
|
||||
fprintf(stdout, newformat, "Tref", info->trefBGN, "deg K");
|
||||
fprintf(stdout, tabformat, "dEg/dN", info->dEgDn[ELEC], "eV");
|
||||
fprintf(stdout, newformat, "Nref", info->nrefBGN[ELEC], "/cm^3");
|
||||
fprintf(stdout, tabformat, "dEg/dP", info->dEgDn[HOLE], "eV");
|
||||
fprintf(stdout, newformat, "Pref", info->nrefBGN[HOLE], "/cm^3");
|
||||
fprintf(stdout, tabformat, "Edon", info->eDon * VNorm, "eV");
|
||||
fprintf(stdout, newformat, "Eacc", info->eAcc * VNorm, "eV");
|
||||
fprintf(stdout, tabformat, "Gdon", info->gDon, "");
|
||||
fprintf(stdout, newformat, "Gacc", info->gAcc, "");
|
||||
fprintf(stdout, "*** Generation - Recombination\n");
|
||||
fprintf(stdout, tabformat, "Tn0", info->tau0[ELEC] * TNorm, "s");
|
||||
fprintf(stdout, newformat, "Tp0", info->tau0[HOLE] * TNorm, "s");
|
||||
fprintf(stdout, tabformat, "CnAug",
|
||||
info->cAug[ELEC] / (NNorm * NNorm * TNorm), "cm^6/s");
|
||||
fprintf(stdout, newformat, "CpAug",
|
||||
info->cAug[HOLE] / (NNorm * NNorm * TNorm), "cm^6/s");
|
||||
fprintf(stdout, tabformat, "Aiin", info->aii[ELEC] / LNorm, "/cm");
|
||||
fprintf(stdout, newformat, "Aiip", info->aii[HOLE] / LNorm, "/cm");
|
||||
fprintf(stdout, tabformat, "Biin", info->bii[ELEC] * ENorm, "V/cm");
|
||||
fprintf(stdout, newformat, "Biip", info->bii[HOLE] * ENorm, "V/cm");
|
||||
fprintf(stdout, "*** Thermionic Emission\n");
|
||||
fprintf(stdout, tabformat, "Arichn", info->aRich[ELEC], "A/cm^2/oK^2");
|
||||
fprintf(stdout, newformat, "Arichp", info->aRich[HOLE], "A/cm^2/oK^2");
|
||||
fprintf(stdout, tabformat, "Vrichn", info->vRich[ELEC] * ENorm, "cm/s");
|
||||
fprintf(stdout, newformat, "Vrichp", info->vRich[HOLE] * ENorm, "cm/s");
|
||||
fprintf(stdout, "*** Majority Carrier Mobility\n");
|
||||
fprintf(stdout, tabformat, "MunMax",
|
||||
info->muMax[ELEC][MAJOR], "cm^2/V-s");
|
||||
fprintf(stdout, newformat, "MupMax",
|
||||
info->muMax[HOLE][MAJOR], "cm^2/V-s");
|
||||
fprintf(stdout, tabformat, "MunMin",
|
||||
info->muMin[ELEC][MAJOR], "cm^2/V-s");
|
||||
fprintf(stdout, newformat, "MupMin",
|
||||
info->muMin[HOLE][MAJOR], "cm^2/V-s");
|
||||
fprintf(stdout, "*** Minority Carrier Mobility\n");
|
||||
fprintf(stdout, tabformat, "MunMax",
|
||||
info->muMax[ELEC][MINOR], "cm^2/V-s");
|
||||
fprintf(stdout, newformat, "MupMax",
|
||||
info->muMax[HOLE][MINOR], "cm^2/V-s");
|
||||
fprintf(stdout, tabformat, "MunMin",
|
||||
info->muMin[ELEC][MINOR], "cm^2/V-s");
|
||||
fprintf(stdout, newformat, "MupMin",
|
||||
info->muMin[HOLE][MINOR], "cm^2/V-s");
|
||||
fprintf(stdout, "*** Surface Mobility\n");
|
||||
fprintf(stdout, tabformat, "Muns", info->mus[ELEC], "cm^2/V-s");
|
||||
fprintf(stdout, newformat, "Mups", info->mus[HOLE], "cm^2/V-s");
|
||||
fprintf(stdout, tabformat, "ThetaAN", info->thetaA[ELEC] / ENorm, "cm/V");
|
||||
fprintf(stdout, newformat, "ThetaAP", info->thetaA[HOLE] / ENorm, "cm/V");
|
||||
fprintf(stdout, tabformat, "ThetaBN",
|
||||
info->thetaB[ELEC] / ENorm / ENorm, "cm^2/V^2");
|
||||
fprintf(stdout, newformat, "ThetaBP",
|
||||
info->thetaB[HOLE] / ENorm / ENorm, "cm^2/V^2");
|
||||
fprintf(stdout, "*** Velocity Saturation\n");
|
||||
fprintf(stdout, tabformat, "VsatN", info->vSat[ELEC] * ENorm, "cm/s");
|
||||
fprintf(stdout, newformat, "VsatP", info->vSat[HOLE] * ENorm, "cm/s");
|
||||
if (info->fieldModel == SG || info->fieldModel == GA) {
|
||||
fprintf(stdout, tabformat, "VwarmN", info->vWarm[ELEC] * ENorm, "cm/s");
|
||||
fprintf(stdout, newformat, "VwarmP", info->vWarm[HOLE] * ENorm, "cm/s");
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
/**********
|
||||
Copyright 1992 Regents of the University of California. All rights reserved.
|
||||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group
|
||||
Author: 1992 David A. Gates, U. C. Berkeley CAD Group
|
||||
**********/
|
||||
|
||||
/*
|
||||
* Miscellaneous routines culled from the oned directory so that the twod
|
||||
* code will run without having to compile the oned code
|
||||
*/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numglobs.h"
|
||||
#include "numenum.h"
|
||||
#include "spMatrix.h"
|
||||
|
||||
|
||||
/* Used in Solution Projection Calculations */
|
||||
double guessNewConc(double conc, double delta)
|
||||
{
|
||||
BOOLEAN acceptable = FALSE;
|
||||
double fib, newConc, lambda, fibn, fibp;
|
||||
lambda = 1.0;
|
||||
fibn = 1.0;
|
||||
fibp = 1.0;
|
||||
for ( ; !acceptable ; ) {
|
||||
fib = fibp;
|
||||
fibp = fibn;
|
||||
fibn += fib;
|
||||
lambda *= fibp / fibn;
|
||||
newConc = conc + delta * lambda;
|
||||
if( newConc > 0.0 ) {
|
||||
acceptable = TRUE;
|
||||
}
|
||||
else {
|
||||
/* newConc is still negative but fibp and fibn are large */
|
||||
if ( (fibp > 1e6) || (fibn > 1e6) ) {
|
||||
acceptable = TRUE;
|
||||
newConc = conc;
|
||||
}
|
||||
}
|
||||
}
|
||||
return( newConc );
|
||||
}
|
||||
|
||||
/* Used in Doping Calculation */
|
||||
/* compute the concentration at x given an array of data.
|
||||
* The data is stored in a two-dimensional array x, N(x).
|
||||
* x is assumed to be in ascending order. given an x
|
||||
* a search is performed to determine the data points closest
|
||||
* to it and linear interpolation is performed to generate N(x)
|
||||
*/
|
||||
|
||||
/*
|
||||
#define LOGSUPREM
|
||||
*/
|
||||
double lookup(double **dataTable, double x)
|
||||
{
|
||||
double conc=0.0, x0, x1, y0, y1;
|
||||
#ifdef LOGSUPREM
|
||||
double lnconc, lny0, lny1;
|
||||
#endif
|
||||
int index, numPoints;
|
||||
BOOLEAN done = FALSE;
|
||||
|
||||
numPoints = dataTable[ 0 ][ 0 ];
|
||||
for( index = 2; index <= numPoints && (!done); index++ ) {
|
||||
x1 = dataTable[ 0 ][ index ];
|
||||
/* check if x1 > x */
|
||||
if( x1 >= x ) {
|
||||
/* found an x1 larger than x, so linear interpolate */
|
||||
x0 = dataTable[ 0 ][ index - 1 ];
|
||||
y0 = dataTable[ 1 ][ index - 1 ];
|
||||
y1 = dataTable[ 1 ][ index ];
|
||||
#ifdef LOGSUPREM
|
||||
/* Ignore concentrations below 1.0 */
|
||||
if ( ABS(y0) < 1.0 )
|
||||
lny0 = 0.0;
|
||||
else
|
||||
lny0 = SGN(y0) * log( ABS(y0) );
|
||||
if ( ABS(y1) < 1.0 )
|
||||
lny1 = 0.0;
|
||||
else
|
||||
lny1 = SGN(y0) * log( ABS(y0) );
|
||||
lnconc = lny0 + (lny1 - lny0) * (x - x0) / (x1 - x0);
|
||||
conc = SGN(lnconc) * exp( ABS(lnconc) );
|
||||
#else
|
||||
conc = y0 + (y1 - y0) * (x - x0) / (x1 - x0);
|
||||
#endif /* LOGSUPREM */
|
||||
done = TRUE;
|
||||
} else {
|
||||
if( index == numPoints ) {
|
||||
/* set to concentration of last node - due to roundoff errors */
|
||||
conc = dataTable[ 1 ][ numPoints ];
|
||||
}
|
||||
}
|
||||
}
|
||||
return ( conc );
|
||||
}
|
||||
|
||||
/* Used in admittance calculations */
|
||||
/* this function returns TRUE is SOR iteration converges otherwise FALSE */
|
||||
|
||||
BOOLEAN hasSORConverged(double *oldSolution, double *newSolution,
|
||||
int numEqns)
|
||||
{
|
||||
BOOLEAN converged = TRUE;
|
||||
int index;
|
||||
double xOld, xNew, tol;
|
||||
double absTol = 1e-12;
|
||||
double relTol = 1e-3;
|
||||
for( index = 1 ; index <= numEqns ; index++ ) {
|
||||
xOld = oldSolution[ index ];
|
||||
xNew = newSolution[ index ];
|
||||
tol = absTol + relTol * MAX( ABS( xOld ), ABS( xNew ));
|
||||
if( ABS( xOld - xNew ) > tol ) {
|
||||
converged = FALSE;
|
||||
printf("hasSORconverged failed\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return( converged );
|
||||
}
|
||||
|
||||
/* Used to Check Sparse Matrix Errors */
|
||||
BOOLEAN
|
||||
foundError(int error)
|
||||
{
|
||||
BOOLEAN matrixError;
|
||||
|
||||
switch( error ) {
|
||||
/* Removed for Spice3e1 Compatibility
|
||||
case spSMALL_PIVOT:
|
||||
printf( "Warning: LU Decomposition Problem - SMALL PIVOT\n" );
|
||||
matrixError = FALSE;
|
||||
break;
|
||||
*/
|
||||
case spPANIC:
|
||||
printf( "Error: LU Decomposition Failed - PANIC\n" );
|
||||
matrixError = TRUE;
|
||||
break;
|
||||
case spSINGULAR:
|
||||
printf( "Error: LU Decomposition Failed - SINGULAR\n" );
|
||||
matrixError = TRUE;
|
||||
break;
|
||||
/* Removed for Spice3e1 Compatibility
|
||||
case spZERO_DIAG:
|
||||
printf( "Error: LU Decomposition Failed - ZERO PIVOT\n" );
|
||||
matrixError = TRUE;
|
||||
break;
|
||||
*/
|
||||
case spNO_MEMORY:
|
||||
printf( "Error: LU Decomposition Failed - NO MEMORY\n" );
|
||||
matrixError = TRUE;
|
||||
break;
|
||||
default:
|
||||
matrixError = FALSE;
|
||||
break;
|
||||
}
|
||||
return( matrixError );
|
||||
}
|
||||
|
|
@ -0,0 +1,421 @@
|
|||
/**********
|
||||
Copyright 1991 Regents of the University of California. All rights reserved.
|
||||
Author: 1992 David A. Gates, U. C. Berkeley CAD Group
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numglobs.h"
|
||||
#include "numconst.h"
|
||||
#include "numenum.h"
|
||||
#include "macros.h"
|
||||
#include "material.h"
|
||||
|
||||
void MOBdefaults(MaterialInfo *info , int carrier, int type,
|
||||
int concmodel, int fieldmodel )
|
||||
{
|
||||
switch (concmodel) {
|
||||
case CT:
|
||||
info->concModel = CT;
|
||||
if (carrier == ELEC) {
|
||||
info->muMax[ELEC][type] = CT_MUMAX_N;
|
||||
info->muMin[ELEC][type] = CT_MUMIN_N;
|
||||
info->ntRef[ELEC][type] = CT_NTREF_N;
|
||||
info->ntExp[ELEC][type] = CT_NTEXP_N;
|
||||
} else {
|
||||
info->muMax[HOLE][type] = CT_MUMAX_P;
|
||||
info->muMin[HOLE][type] = CT_MUMIN_P;
|
||||
info->ntRef[HOLE][type] = CT_NTREF_P;
|
||||
info->ntExp[HOLE][type] = CT_NTEXP_P;
|
||||
}
|
||||
break;
|
||||
case AR:
|
||||
info->concModel = AR;
|
||||
if (carrier == ELEC) {
|
||||
info->muMax[ELEC][type] = AR_MUMAX_N;
|
||||
info->muMin[ELEC][type] = AR_MUMIN_N;
|
||||
info->ntRef[ELEC][type] = AR_NTREF_N;
|
||||
info->ntExp[ELEC][type] = AR_NTEXP_N;
|
||||
} else {
|
||||
info->muMax[HOLE][type] = AR_MUMAX_P;
|
||||
info->muMin[HOLE][type] = AR_MUMIN_P;
|
||||
info->ntRef[HOLE][type] = AR_NTREF_P;
|
||||
info->ntExp[HOLE][type] = AR_NTEXP_P;
|
||||
}
|
||||
break;
|
||||
case UF:
|
||||
info->concModel = UF;
|
||||
if (carrier == ELEC) {
|
||||
info->muMax[ELEC][type] = UF_MUMAX_N;
|
||||
info->muMin[ELEC][type] = UF_MUMIN_N;
|
||||
info->ntRef[ELEC][type] = UF_NTREF_N;
|
||||
info->ntExp[ELEC][type] = UF_NTEXP_N;
|
||||
} else {
|
||||
info->muMax[HOLE][type] = UF_MUMAX_P;
|
||||
info->muMin[HOLE][type] = UF_MUMIN_P;
|
||||
info->ntRef[HOLE][type] = UF_NTREF_P;
|
||||
info->ntExp[HOLE][type] = UF_NTEXP_P;
|
||||
}
|
||||
break;
|
||||
case GA:
|
||||
info->concModel = GA;
|
||||
if (carrier == ELEC) {
|
||||
info->muMax[ELEC][type] = GA_MUMAX_N;
|
||||
info->muMin[ELEC][type] = GA_MUMIN_N;
|
||||
info->ntRef[ELEC][type] = GA_NTREF_N;
|
||||
info->ntExp[ELEC][type] = GA_NTEXP_N;
|
||||
} else {
|
||||
info->muMax[HOLE][type] = GA_MUMAX_P;
|
||||
info->muMin[HOLE][type] = GA_MUMIN_P;
|
||||
info->ntRef[HOLE][type] = GA_NTREF_P;
|
||||
info->ntExp[HOLE][type] = GA_NTEXP_P;
|
||||
}
|
||||
break;
|
||||
case SG:
|
||||
default:
|
||||
info->concModel = SG;
|
||||
if (carrier == ELEC) {
|
||||
info->muMax[ELEC][type] = SG_MUMAX_N;
|
||||
info->muMin[ELEC][type] = SG_MUMIN_N;
|
||||
info->ntRef[ELEC][type] = SG_NTREF_N;
|
||||
info->ntExp[ELEC][type] = SG_NTEXP_N;
|
||||
} else {
|
||||
info->muMax[HOLE][type] = SG_MUMAX_P;
|
||||
info->muMin[HOLE][type] = SG_MUMIN_P;
|
||||
info->ntRef[HOLE][type] = SG_NTREF_P;
|
||||
info->ntExp[HOLE][type] = SG_NTEXP_P;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (type == MAJOR) {
|
||||
switch (fieldmodel) {
|
||||
case CT:
|
||||
info->fieldModel = CT;
|
||||
if (carrier == ELEC) {
|
||||
info->vSat[ELEC] = CT_VSAT_N;
|
||||
} else {
|
||||
info->vSat[HOLE] = CT_VSAT_P;
|
||||
}
|
||||
break;
|
||||
case AR:
|
||||
case UF:
|
||||
info->fieldModel = AR;
|
||||
if (carrier == ELEC) {
|
||||
info->vSat[ELEC] = AR_VSAT_N;
|
||||
} else {
|
||||
info->vSat[HOLE] = AR_VSAT_P;
|
||||
}
|
||||
break;
|
||||
case GA:
|
||||
info->fieldModel = GA;
|
||||
if (carrier == ELEC) {
|
||||
info->vSat[ELEC] = GA_VSAT_N;
|
||||
info->vWarm[ELEC] = GA_VWARM_N;
|
||||
} else {
|
||||
info->vSat[HOLE] = GA_VSAT_P;
|
||||
info->vWarm[HOLE] = GA_VWARM_P;
|
||||
}
|
||||
break;
|
||||
case SG:
|
||||
default:
|
||||
info->fieldModel = SG;
|
||||
if (carrier == ELEC) {
|
||||
info->vSat[ELEC] = SG_VSAT_N;
|
||||
info->vWarm[ELEC] = SG_VWARM_N;
|
||||
} else {
|
||||
info->vSat[HOLE] = SG_VSAT_P;
|
||||
info->vWarm[HOLE] = SG_VWARM_P;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MOBtempDep (MaterialInfo *info, double temp)
|
||||
{
|
||||
double relTemp = temp / 300.0;
|
||||
double factor, muMin, muMax, mu0;
|
||||
|
||||
/* Modify if necessary. */
|
||||
if (TempDepMobility)
|
||||
{
|
||||
/* Do concentration dependence parameters */
|
||||
muMin = info->muMin[ELEC][MAJOR];
|
||||
mu0 = info->muMax[ELEC][MAJOR] - muMin;
|
||||
factor = pow(relTemp, TD_EXPMUMIN_N);
|
||||
muMin *= factor;
|
||||
factor = pow(relTemp, TD_EXPMUMAX_N);
|
||||
mu0 *= factor;
|
||||
info->muMin[ELEC][MAJOR] = muMin;
|
||||
info->muMax[ELEC][MAJOR] = mu0 + muMin;
|
||||
factor = pow(relTemp, TD_EXPNTREF_N);
|
||||
info->ntRef[ELEC][MAJOR] *= factor;
|
||||
factor = pow(relTemp, TD_EXPNTEXP_N);
|
||||
info->ntExp[ELEC][MAJOR] *= factor;
|
||||
|
||||
muMin = info->muMin[ELEC][MINOR];
|
||||
mu0 = info->muMax[ELEC][MINOR] - muMin;
|
||||
factor = pow(relTemp, TD_EXPMUMIN_N);
|
||||
muMin *= factor;
|
||||
factor = pow(relTemp, TD_EXPMUMAX_N);
|
||||
mu0 *= factor;
|
||||
info->muMin[ELEC][MINOR] = muMin;
|
||||
info->muMax[ELEC][MINOR] = mu0 + muMin;
|
||||
factor = pow(relTemp, TD_EXPNTREF_N);
|
||||
info->ntRef[ELEC][MINOR] *= factor;
|
||||
factor = pow(relTemp, TD_EXPNTEXP_N);
|
||||
info->ntExp[ELEC][MINOR] *= factor;
|
||||
|
||||
muMin = info->muMin[HOLE][MAJOR];
|
||||
mu0 = info->muMax[HOLE][MAJOR] - muMin;
|
||||
factor = pow(relTemp, TD_EXPMUMIN_P);
|
||||
muMin *= factor;
|
||||
factor = pow(relTemp, TD_EXPMUMAX_P);
|
||||
mu0 *= factor;
|
||||
info->muMin[HOLE][MAJOR] = muMin;
|
||||
info->muMax[HOLE][MAJOR] = mu0 + muMin;
|
||||
factor = pow(relTemp, TD_EXPNTREF_P);
|
||||
info->ntRef[HOLE][MAJOR] *= factor;
|
||||
factor = pow(relTemp, TD_EXPNTEXP_P);
|
||||
info->ntExp[HOLE][MAJOR] *= factor;
|
||||
|
||||
muMin = info->muMin[HOLE][MINOR];
|
||||
mu0 = info->muMax[HOLE][MINOR] - muMin;
|
||||
factor = pow(relTemp, TD_EXPMUMIN_P);
|
||||
muMin *= factor;
|
||||
factor = pow(relTemp, TD_EXPMUMAX_P);
|
||||
mu0 *= factor;
|
||||
info->muMin[HOLE][MINOR] = muMin;
|
||||
info->muMax[HOLE][MINOR] = mu0 + muMin;
|
||||
factor = pow(relTemp, TD_EXPNTREF_P);
|
||||
info->ntRef[HOLE][MINOR] *= factor;
|
||||
factor = pow(relTemp, TD_EXPNTEXP_P);
|
||||
info->ntExp[HOLE][MINOR] *= factor;
|
||||
|
||||
/* Modify field dependence parameters */
|
||||
/* Assume warm carrier reference velocity has same temperature dep. */
|
||||
factor = sqrt( tanh( TD_TREFVS_N / Temp ) );
|
||||
info->vSat[ELEC] *= factor;
|
||||
info->vWarm[ELEC] *= factor;
|
||||
factor = sqrt( tanh( TD_TREFVS_P / Temp ) );
|
||||
info->vSat[HOLE] *= factor;
|
||||
info->vWarm[HOLE] *= factor;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MOBconcDep (MaterialInfo *info, double conc, double *pMun, double *pMup)
|
||||
{
|
||||
double s;
|
||||
|
||||
/* We have to check sign of conc even when concentration dependence
|
||||
* is not used because it affects whether carriers are majority or
|
||||
* minority carriers. Ideally, the minority/majority carrier models
|
||||
* should agree at 0.0 concentration, but often they'll be inconsistent.
|
||||
*/
|
||||
|
||||
if (conc >= 0.0)
|
||||
{ /* N type */
|
||||
if (ConcDepMobility)
|
||||
{
|
||||
switch (info->concModel)
|
||||
{
|
||||
case CT:
|
||||
case AR:
|
||||
case UF:
|
||||
case GA:
|
||||
*pMun = info->muMin[ELEC][MAJOR] +
|
||||
(info->muMax[ELEC][MAJOR] - info->muMin[ELEC][MAJOR]) /
|
||||
(1.0 + pow(conc / info->ntRef[ELEC][MAJOR],
|
||||
info->ntExp[ELEC][MAJOR]));
|
||||
|
||||
*pMup = info->muMin[HOLE][MINOR] +
|
||||
(info->muMax[HOLE][MINOR] - info->muMin[HOLE][MINOR]) /
|
||||
(1.0 + pow(conc / info->ntRef[HOLE][MINOR],
|
||||
info->ntExp[HOLE][MINOR]));
|
||||
break;
|
||||
case SG:
|
||||
default:
|
||||
s = info->muMax[ELEC][MAJOR] / info->muMin[ELEC][MAJOR];
|
||||
s = pow(s, 1.0 / info->ntExp[ELEC][MAJOR]) - 1;
|
||||
*pMun = info->muMax[ELEC][MAJOR] /
|
||||
pow(1.0 + conc / (conc / s + info->ntRef[ELEC][MAJOR]),
|
||||
info->ntExp[ELEC][MAJOR]);
|
||||
|
||||
s = info->muMax[HOLE][MINOR] / info->muMin[HOLE][MINOR];
|
||||
s = pow(s, 1.0 / info->ntExp[HOLE][MINOR]) - 1;
|
||||
*pMup = info->muMax[HOLE][MINOR] /
|
||||
pow(1.0 + conc / (conc / s + info->ntRef[HOLE][MINOR]),
|
||||
info->ntExp[HOLE][MINOR]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*pMun = info->muMax[ELEC][MAJOR];
|
||||
*pMup = info->muMax[HOLE][MINOR];
|
||||
}
|
||||
}
|
||||
else
|
||||
{ /* P type */
|
||||
if (ConcDepMobility)
|
||||
{
|
||||
conc = -conc; /* Take absolute value. */
|
||||
switch (info->concModel)
|
||||
{
|
||||
case CT:
|
||||
case AR:
|
||||
case UF:
|
||||
case GA:
|
||||
*pMun = info->muMin[ELEC][MINOR] +
|
||||
(info->muMax[ELEC][MINOR] - info->muMin[ELEC][MINOR]) /
|
||||
(1.0 + pow(conc / info->ntRef[ELEC][MINOR],
|
||||
info->ntExp[ELEC][MINOR]));
|
||||
|
||||
*pMup = info->muMin[HOLE][MAJOR] +
|
||||
(info->muMax[HOLE][MAJOR] - info->muMin[HOLE][MAJOR]) /
|
||||
(1.0 + pow(conc / info->ntRef[HOLE][MAJOR],
|
||||
info->ntExp[HOLE][MAJOR]));
|
||||
break;
|
||||
case SG:
|
||||
default:
|
||||
s = info->muMax[ELEC][MINOR] / info->muMin[ELEC][MINOR];
|
||||
s = pow(s, 1.0 / info->ntExp[ELEC][MINOR]) - 1;
|
||||
*pMun = info->muMax[ELEC][MINOR] /
|
||||
pow(1.0 + conc / (conc / s + info->ntRef[ELEC][MINOR]),
|
||||
info->ntExp[ELEC][MINOR]);
|
||||
|
||||
s = info->muMax[HOLE][MAJOR] / info->muMin[HOLE][MAJOR];
|
||||
s = pow(s, 1.0 / info->ntExp[HOLE][MAJOR]) - 1;
|
||||
*pMup = info->muMax[HOLE][MAJOR] /
|
||||
pow(1.0 + conc / (conc / s + info->ntRef[HOLE][MAJOR]),
|
||||
info->ntExp[HOLE][MAJOR]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*pMun = info->muMax[ELEC][MINOR];
|
||||
*pMup = info->muMax[HOLE][MAJOR];
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
MOBfieldDep (MaterialInfo *info, int carrier, double field, double *pMu,
|
||||
double *pDMu)
|
||||
{
|
||||
double eLateral, mu;
|
||||
double sgnL;
|
||||
double temp1, temp2, temp3, temp4, temp5, temp6;
|
||||
double dMuDEl; /* Lateral Field Derivative */
|
||||
|
||||
/* Quick check to make sure we really belong here. */
|
||||
if (!FieldDepMobility) /* XXX Global */
|
||||
return;
|
||||
|
||||
sgnL = SGN (field);
|
||||
eLateral = ABS (field);
|
||||
mu = *pMu; /* Grab temp. and conc.-dep. mobility */
|
||||
|
||||
|
||||
if (carrier == ELEC)
|
||||
{
|
||||
switch (info->fieldModel)
|
||||
{
|
||||
case CT:
|
||||
case AR:
|
||||
case UF:
|
||||
temp1 = mu / info->vSat[ELEC];
|
||||
temp2 = temp1 * eLateral;
|
||||
temp3 = 1.0 / (1.0 + temp2 * temp2);
|
||||
mu *= sqrt(temp3);
|
||||
dMuDEl = -sgnL * mu * temp3 * temp2 * temp1;
|
||||
|
||||
|
||||
break;
|
||||
case GA:
|
||||
temp1 = info->vSat[ELEC] / info->vWarm[ELEC]; /* Vsat / Vwarm */
|
||||
temp2 = mu / info->vWarm[ELEC];
|
||||
temp3 = temp2 * eLateral; /* Vdrift / Vwarm */
|
||||
temp4 = temp3 * temp3 * temp3;
|
||||
temp5 = 1.0 + temp1 * temp4;
|
||||
temp6 = 1.0 / (1.0 + temp3 * temp4);
|
||||
mu *= temp5 * temp6;
|
||||
dMuDEl = - sgnL * mu * temp2 *
|
||||
(4.0 * temp4 * temp6 - 3.0 * temp1 * temp3 * temp3 / temp5 );
|
||||
|
||||
/*
|
||||
dMuDEl = 0.0;
|
||||
*/
|
||||
break;
|
||||
case SG:
|
||||
default:
|
||||
temp1 = mu / info->vSat[ELEC];
|
||||
temp2 = temp1 * eLateral;/* Vdrift / Vsat */
|
||||
temp3 = mu / info->vWarm[ELEC];
|
||||
temp4 = temp3 * eLateral;/* Vdrift / Vwarm */
|
||||
temp5 = temp4 / (temp4 + SG_FIT_N);
|
||||
temp6 = 1.0 / (1.0 + temp4 * temp5 + temp2 * temp2);
|
||||
mu *= sqrt(temp6);
|
||||
dMuDEl = -sgnL * 0.5 * mu * temp6 *
|
||||
(temp5 * (2.0 - temp5) * temp3 + (2.0 * temp2 * temp1));
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ /* Hole Mobility */
|
||||
switch (info->fieldModel)
|
||||
{
|
||||
case CT:
|
||||
case AR:
|
||||
case UF:
|
||||
temp1 = mu / info->vSat[HOLE];
|
||||
temp2 = temp1 * eLateral;
|
||||
temp3 = 1.0 / (1.0 + temp2);
|
||||
mu *= temp3;
|
||||
dMuDEl = -sgnL * mu * temp3 * temp1;
|
||||
|
||||
break;
|
||||
case GA:
|
||||
temp1 = info->vSat[HOLE] / info->vWarm[HOLE]; /* Vsat / Vwarm */
|
||||
temp2 = mu / info->vWarm[HOLE];
|
||||
temp3 = temp2 * eLateral; /* Vdrift / Vwarm */
|
||||
temp4 = temp3 * temp3 * temp3;
|
||||
temp5 = 1.0 + temp1 * temp4;
|
||||
temp6 = 1.0 / (1.0 + temp3 * temp4);
|
||||
mu *= temp5 * temp6;
|
||||
dMuDEl = - sgnL * mu * temp2 *
|
||||
(4.0 * temp4 * temp6 - 3.0 * temp1 * temp3 * temp3 / temp5 );
|
||||
|
||||
|
||||
/*
|
||||
dMuDEl = 0.0;
|
||||
*/
|
||||
break;
|
||||
case SG:
|
||||
default:
|
||||
temp1 = mu / info->vSat[HOLE];
|
||||
temp2 = temp1 * eLateral;/* Vdrift / Vsat */
|
||||
temp3 = mu / info->vWarm[HOLE];
|
||||
temp4 = temp3 * eLateral;/* Vdrift / Vwarm */
|
||||
temp5 = temp4 / (temp4 + SG_FIT_P);
|
||||
temp6 = 1.0 / (1.0 + temp4 * temp5 + temp2 * temp2);
|
||||
mu *= sqrt(temp6);
|
||||
dMuDEl = -sgnL * 0.5 * mu * temp6 *
|
||||
(temp5 * (2.0 - temp5) * temp3 + (2.0 * temp2 * temp1));
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*pMu = mu;
|
||||
*pDMu = dMuDEl;
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
Directory: support
|
||||
------------------
|
||||
This directory contains the files that support the device simulators and
|
||||
their interfaces to the circuit simulators. They fall into four basic
|
||||
areas:
|
||||
1. Numerical algorithms: integration, accuracy limits, special functions
|
||||
2. Semiconductor device physics: material properties, recombination, mobility
|
||||
3. File access for input: doping profiles from SUPREMIII, saved states
|
||||
4. Miscellaneous: diagnstic I/O, string routines
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
/**********
|
||||
Copyright 1991 Regents of the University of California. All rights reserved.
|
||||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numglobs.h"
|
||||
|
||||
/*
|
||||
* function recomb calculates the recobination rates and the
|
||||
* derivatives with respect to n and p. SRH recombination is
|
||||
* always assumed. Auger recombination is specified by the flag
|
||||
* 'Auger' which by default is false.
|
||||
*/
|
||||
|
||||
void
|
||||
recomb (double ni, double pi, double tn, double tp, double cn, double cp,
|
||||
double nie, double *pUnet, double *pDuDn, double *pDuDp)
|
||||
{
|
||||
double uSrh, uSrhNum, uSrhDen, perUdenSq, duSrhDn, duSrhDp;
|
||||
double cncp, uAug, duAugDn, duAugDp, uNet, duDn, duDp;
|
||||
|
||||
uSrhNum = ni * pi - nie * nie;
|
||||
uSrhDen = tp * (ni + nie) + tn * (pi + nie);
|
||||
uSrh = uSrhNum / uSrhDen;
|
||||
perUdenSq = 1.0 / (uSrhDen * uSrhDen);
|
||||
duSrhDn = (pi * uSrhDen - uSrhNum * tp) * perUdenSq;
|
||||
duSrhDp = (ni * uSrhDen - uSrhNum * tn) * perUdenSq;
|
||||
|
||||
|
||||
if (Auger && uSrhNum >= 0.0) { /* XXX Auger Global */
|
||||
cncp = cn * ni + cp * pi;
|
||||
uAug = cncp * uSrhNum;
|
||||
duAugDn = cn * uSrhNum + cncp * pi;
|
||||
duAugDp = cp * uSrhNum + cncp * ni;
|
||||
uNet = uSrh + uAug;
|
||||
duDn = duSrhDn + duAugDn;
|
||||
duDp = duSrhDp + duAugDp;
|
||||
}
|
||||
else {
|
||||
uNet = uSrh;
|
||||
duDn = duSrhDn;
|
||||
duDp = duSrhDp;
|
||||
}
|
||||
|
||||
/* return uNet duDn duDp */
|
||||
*pUnet = uNet;
|
||||
*pDuDn = duDn;
|
||||
*pDuDp = duDp;
|
||||
}
|
||||
|
|
@ -0,0 +1,210 @@
|
|||
/**********
|
||||
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
|
||||
**********/
|
||||
|
||||
/* Functions to read SUPREM (Binary or Ascii) & ASCII input files */
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "profile.h"
|
||||
|
||||
void
|
||||
readAsciiData( char *fileName, int impType, DOPtable **ppTable )
|
||||
{
|
||||
FILE *fpAscii;
|
||||
int index, i;
|
||||
double x, y;
|
||||
int numPoints;
|
||||
DOPtable *tmpTable;
|
||||
double **profileData;
|
||||
double sign;
|
||||
|
||||
/* Open Input File */
|
||||
if (!(fpAscii = fopen( fileName, "r" ))) {
|
||||
perror( fileName );
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
/* Get sign of concentrations */
|
||||
if (impType == IMP_P_TYPE) {
|
||||
sign = -1.0;
|
||||
} else {
|
||||
sign = 1.0;
|
||||
}
|
||||
|
||||
/* read the number of points */
|
||||
fscanf( fpAscii, "%d", &numPoints );
|
||||
|
||||
/* allocate 2-D array to read in data of x-coordinate and N(x) */
|
||||
XCALLOC( profileData, double *, 2 );
|
||||
for( index = 0; index <= 1; index++ ) {
|
||||
XCALLOC( profileData[ index ], double, 1 + numPoints );
|
||||
}
|
||||
/* the number of points is stored as profileData[0][0] */
|
||||
profileData[0][0] = numPoints;
|
||||
|
||||
for( index = 1; index <= numPoints; index++ ) {
|
||||
fscanf( fpAscii, "%f %f ", &x, &y );
|
||||
profileData[ 0 ][ index ] = x;
|
||||
profileData[ 1 ][ index ] = sign * ABS(y);
|
||||
}
|
||||
|
||||
/* Now create a new lookup table */
|
||||
XCALLOC( tmpTable, DOPtable, 1 );
|
||||
if ( *ppTable == NIL(DOPtable) ) {
|
||||
/* First Entry */
|
||||
tmpTable->impId = 1;
|
||||
tmpTable->dopData = profileData;
|
||||
tmpTable->next = NIL(DOPtable);
|
||||
*ppTable = tmpTable;
|
||||
} else {
|
||||
tmpTable->impId = (*ppTable)->impId + 1;
|
||||
tmpTable->dopData = profileData;
|
||||
tmpTable->next = *ppTable;
|
||||
*ppTable = tmpTable;
|
||||
}
|
||||
|
||||
/* for debugging print the data that has been just read */
|
||||
/*
|
||||
for( index = 1; index <= numPoints; index++ ) {
|
||||
printf("\n %e %e", profileData[ 0 ][ index ], profileData[ 1 ][ index ]);
|
||||
}
|
||||
*/
|
||||
fclose(fpAscii);
|
||||
return;
|
||||
}
|
||||
|
||||
/* interface routine based on notes provided by Steve Hansen of Stanford */
|
||||
|
||||
/*
|
||||
* The material types are:
|
||||
* 1 = single crystal silicon
|
||||
* 2 = silicon dioxide
|
||||
* 3 = poly-crystalline silicon
|
||||
* 4 = silicon nitride
|
||||
* 5 = aluminum
|
||||
|
||||
* The impurity types are:
|
||||
* 1 = boron
|
||||
* 2 = phosphorus
|
||||
* 3 = arsenic
|
||||
* 4 = antimony
|
||||
|
||||
* The crystalline orientations are:
|
||||
* 1 = <111>
|
||||
* 2 = <100>
|
||||
* 3 = <110>
|
||||
|
||||
* The layer thinkness, poly-crystalline grain size, node spacing and
|
||||
* distance from the surface are all in microns.
|
||||
|
||||
* The integrated dopant concentration and the phophorus implant dose are
|
||||
* in atoms per square centimeter.
|
||||
|
||||
* The interior polycrystalline grain concentration and the impurity
|
||||
* concentrations at each node are in atoms per cubic centimeter.
|
||||
*/
|
||||
|
||||
|
||||
void
|
||||
readSupremData( fileName, fileType, impType, ppTable )
|
||||
char *fileName;
|
||||
int fileType;
|
||||
int impType;
|
||||
DOPtable **ppTable;
|
||||
{
|
||||
#define MAX_GRID 500
|
||||
float x[ MAX_GRID ], conc[ MAX_GRID ];
|
||||
|
||||
int index, i, j;
|
||||
DOPtable *tmpTable;
|
||||
double **profileData;
|
||||
int numNodes;
|
||||
char *colon, *impName;
|
||||
|
||||
/* read the Suprem data file */
|
||||
if ( fileType == 0 ) { /* BINARY FILE */
|
||||
SUPbinRead( fileName, x, conc, &impType, &numNodes );
|
||||
}
|
||||
else {
|
||||
SUPascRead( fileName, x, conc, &impType, &numNodes );
|
||||
}
|
||||
|
||||
/* allocate 2-D array to read in data of x-coordinate and N(x) */
|
||||
XCALLOC( profileData, double *, 2 );
|
||||
for( index = 0; index <= 1; index++ ) {
|
||||
XCALLOC( profileData[ index ], double, 1 + numNodes );
|
||||
}
|
||||
/* the number of points is stored as profileData[0][0] */
|
||||
profileData[0][0] = numNodes;
|
||||
|
||||
for( index = 1; index <= numNodes; index++ ) {
|
||||
profileData[ 0 ][ index ] = x[ index ];
|
||||
profileData[ 1 ][ index ] = conc[ index ];
|
||||
}
|
||||
|
||||
/* Now create a new lookup table */
|
||||
XCALLOC( tmpTable, DOPtable, 1 );
|
||||
if ( *ppTable == NIL(DOPtable) ) {
|
||||
/* First Entry */
|
||||
tmpTable->impId = 1;
|
||||
tmpTable->dopData = profileData;
|
||||
tmpTable->next = NIL(DOPtable);
|
||||
*ppTable = tmpTable;
|
||||
} else {
|
||||
tmpTable->impId = (*ppTable)->impId + 1;
|
||||
tmpTable->dopData = profileData;
|
||||
tmpTable->next = *ppTable;
|
||||
*ppTable = tmpTable;
|
||||
}
|
||||
|
||||
/* for debugging print the data that has been just read */
|
||||
/*
|
||||
for( index = 1; index <= numNodes; index++ ) {
|
||||
printf("%e %e\n", profileData[ 0 ][ index ], profileData[ 1 ][ index ]);
|
||||
}
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/* main program to debug readSupremData */
|
||||
|
||||
/*
|
||||
main(ac, av)
|
||||
char **av;
|
||||
{
|
||||
void readSupremData();
|
||||
DOPtable *supTable = NIL(DOPtable);
|
||||
double **supInput;
|
||||
int numPoints, index;
|
||||
char *impName;
|
||||
int impType;
|
||||
|
||||
switch (ac) {
|
||||
case 1:
|
||||
printf( "Usage: %s suprem-file ...\n", av[0] );
|
||||
exit(-1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
for ( index = 1; index < ac; index++ ) {
|
||||
for ( impType=1; impType <= 4; impType++ ) {
|
||||
readSupremData( av[index], 1, impType, &supTable );
|
||||
}
|
||||
}
|
||||
for ( ; supTable ISNOT NIL(DOPtable); supTable = supTable->next ) {
|
||||
fprintf( stdout, "\"Impurity Number: %d\n", supTable->impId );
|
||||
supInput = supTable->dopData;
|
||||
numPoints = supInput[0][0];
|
||||
for( index = 1; index <= numPoints; index++ ) {
|
||||
printf("%e\t%e\n",
|
||||
supInput[ 0 ][ index ], ABS(supInput[ 1 ][ index ]) + 1e-20 );
|
||||
}
|
||||
fprintf( stdout, "\n" );
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
@ -0,0 +1,357 @@
|
|||
/**********
|
||||
Copyright 1991 Regents of the University of California. All rights reserved.
|
||||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group
|
||||
**********/
|
||||
|
||||
/*
|
||||
* Translated FORTRAN subroutine to read the SUPREM-3 binary file
|
||||
* the data is read and stored in two arrays x and conc
|
||||
*/
|
||||
|
||||
|
||||
#include "ngspice.h"
|
||||
|
||||
#define MAXMAT 10
|
||||
#define MAXIMP 4
|
||||
#define MAXLAYER 10
|
||||
#define MAXGRID 500
|
||||
|
||||
#define GFREAD( fp, ptr, type, num ) if (num && (fread( ptr,\
|
||||
sizeof(type), (unsigned)num, fp ) != (unsigned)num)) {\
|
||||
return;\
|
||||
}
|
||||
|
||||
#define DEBUG if (0)
|
||||
|
||||
void
|
||||
SUPbinRead( inFile, x, conc, impId, numNod )
|
||||
char *inFile;
|
||||
float *x, *conc;
|
||||
int *impId, *numNod;
|
||||
{
|
||||
int idata, recordMark;
|
||||
int ldata;
|
||||
int i, j;
|
||||
float rdata;
|
||||
char cdata[21];
|
||||
int numLay, numImp, numGrid;
|
||||
int impTyp[4], matTyp[10], topNod[10], siIndex, offset;
|
||||
float xStart;
|
||||
float layerTh[10];
|
||||
float con[500];
|
||||
FILE *fpSuprem;
|
||||
|
||||
/* Clear Concentration Array */
|
||||
for ( i=0; i < MAXGRID; i++ ) {
|
||||
conc[i] = 0.0;
|
||||
}
|
||||
|
||||
/* Open Input File */
|
||||
if (!(fpSuprem = fopen( inFile, "r" ))) {
|
||||
perror(inFile);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The first record contains the number of layers (I4), the number of
|
||||
* impurities (I4), and the number of nodes (I4) present in the structure.
|
||||
*/
|
||||
GFREAD( fpSuprem, &recordMark, int, 1 );
|
||||
GFREAD( fpSuprem, &numLay, int, 1 );
|
||||
GFREAD( fpSuprem, &numImp, int, 1 );
|
||||
GFREAD( fpSuprem, &numGrid, int, 1 );
|
||||
DEBUG fprintf(stderr,"rec 1: %d %d %d\n", numLay, numImp, numGrid);
|
||||
GFREAD( fpSuprem, &recordMark, int, 1 );
|
||||
|
||||
/*
|
||||
* The second record contains, for each layer, the material type (I4), the
|
||||
* layer thickness (R4), and the pointer to the top node of the layer (I4).
|
||||
*/
|
||||
GFREAD( fpSuprem, &recordMark, int, 1 );
|
||||
for ( i=0; i < numLay; i++ ) {
|
||||
GFREAD( fpSuprem, &matTyp[i], int, 1 );
|
||||
GFREAD( fpSuprem, &layerTh[i], float, 1 );
|
||||
GFREAD( fpSuprem, &topNod[i], int, 1 );
|
||||
DEBUG fprintf(stderr,"rec 2: %d %f %d\n", matTyp[i], layerTh[i], topNod[i] );
|
||||
}
|
||||
GFREAD( fpSuprem, &recordMark, int, 1 );
|
||||
|
||||
/*
|
||||
* The third record contains, for each layer, the material name (A20).
|
||||
*/
|
||||
/* Put a null at the end */
|
||||
cdata[20] = '\0';
|
||||
GFREAD( fpSuprem, &recordMark, int, 1 );
|
||||
for ( i=0; i < numLay; i++ ) {
|
||||
GFREAD( fpSuprem, cdata, char, 20 );
|
||||
DEBUG fprintf(stderr,"rec 3: %s\n", cdata );
|
||||
}
|
||||
GFREAD( fpSuprem, &recordMark, int, 1 );
|
||||
|
||||
/*
|
||||
* The fourth record contains, for each layer, the crystalline orientation
|
||||
* (I4), and the poly-crystalline grain size in microns (R4).
|
||||
*/
|
||||
GFREAD( fpSuprem, &recordMark, int, 1 );
|
||||
for ( i=0; i < numLay; i++ ) {
|
||||
GFREAD( fpSuprem, &idata, int, 1 );
|
||||
GFREAD( fpSuprem, &rdata, float, 1 );
|
||||
DEBUG fprintf(stderr,"rec 4: %d %f\n", idata, rdata );
|
||||
}
|
||||
GFREAD( fpSuprem, &recordMark, int, 1 );
|
||||
|
||||
/*
|
||||
* The fifth record contains, for each impurity, the type of impurity (I4).
|
||||
*/
|
||||
GFREAD( fpSuprem, &recordMark, int, 1 );
|
||||
for ( i=0; i < numImp; i++ ) {
|
||||
GFREAD( fpSuprem, &impTyp[i], int, 1 );
|
||||
DEBUG fprintf(stderr,"rec 5: %d\n", impTyp[i] );
|
||||
}
|
||||
GFREAD( fpSuprem, &recordMark, int, 1 );
|
||||
|
||||
/*
|
||||
* The sixth record contains, for each impurity, the impurity name (A20).
|
||||
*/
|
||||
GFREAD( fpSuprem, &recordMark, int, 1 );
|
||||
for ( i=0; i < numImp; i++ ) {
|
||||
GFREAD( fpSuprem, cdata, char, 20 );
|
||||
DEBUG fprintf(stderr,"rec 6: %s\n", cdata );
|
||||
}
|
||||
GFREAD( fpSuprem, &recordMark, int, 1 );
|
||||
|
||||
/*
|
||||
* The seventh record contains, for each layer by each impurity, the
|
||||
* integrated dopant (R4), and the interior concentration of the
|
||||
* polysilicon grains (R4).
|
||||
*/
|
||||
GFREAD( fpSuprem, &recordMark, int, 1 );
|
||||
for ( j=0; j < numLay; j++ ) {
|
||||
for ( i=0; i < numImp; i++ ) {
|
||||
GFREAD( fpSuprem, &rdata, float, 1 );
|
||||
DEBUG fprintf(stderr,"rec 7: %e", rdata );
|
||||
GFREAD( fpSuprem, &rdata, float, 1 );
|
||||
DEBUG fprintf(stderr," %e\n", rdata );
|
||||
}
|
||||
}
|
||||
GFREAD( fpSuprem, &recordMark, int, 1 );
|
||||
|
||||
/*
|
||||
* The eighth record contains, for each node in the structure, the distance
|
||||
* to the next deepest node (R4).
|
||||
*/
|
||||
GFREAD( fpSuprem, &recordMark, int, 1 );
|
||||
for ( i=0; i < numGrid; i++ ) {
|
||||
GFREAD( fpSuprem, &rdata, float, 1 );
|
||||
}
|
||||
DEBUG fprintf(stderr,"rec 8: %f\n", rdata );
|
||||
GFREAD( fpSuprem, &recordMark, int, 1 );
|
||||
|
||||
/*
|
||||
* The ninth record contains, for each node in the structure, the distance
|
||||
* from the surface (R4).
|
||||
*/
|
||||
GFREAD( fpSuprem, &recordMark, int, 1 );
|
||||
GFREAD( fpSuprem, &x[1], float, numGrid );
|
||||
DEBUG fprintf(stderr,"rec 9: %f\n", x[1] );
|
||||
GFREAD( fpSuprem, &recordMark, int, 1 );
|
||||
|
||||
/*
|
||||
* Next, for each impurity there is a record containing the impurity's
|
||||
* chemical concentration at each node (R4) and a record containing the
|
||||
* impurity's active concentration at each node (R4).
|
||||
*/
|
||||
|
||||
for ( j=0; j < numImp; j++ ) {
|
||||
/* chemical concentration - not required */
|
||||
GFREAD( fpSuprem, &recordMark, int, 1 );
|
||||
GFREAD( fpSuprem, &con[1], float, numGrid );
|
||||
DEBUG fprintf(stderr,"rec 10: %e\n", con[1] );
|
||||
GFREAD( fpSuprem, &recordMark, int, 1 );
|
||||
|
||||
/* store active concentration */
|
||||
GFREAD( fpSuprem, &recordMark, int, 1 );
|
||||
GFREAD( fpSuprem, &con[1], float, numGrid );
|
||||
DEBUG fprintf(stderr,"rec 11: %e\n", con[1] );
|
||||
GFREAD( fpSuprem, &recordMark, int, 1 );
|
||||
|
||||
if (impTyp[j] == *impId) {
|
||||
/*...Boron...*/
|
||||
if (impTyp[j] == 1) {
|
||||
for ( i=1; i <= numGrid; i++ ) conc[i] = - con[i];
|
||||
} else {
|
||||
/*...All Other Impurities: P, As, Sb ...*/
|
||||
for ( i=1; i <= numGrid; i++ ) conc[i] = con[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The last record in the file contains some random stuff that might be
|
||||
* useful to some people, the temperature in degrees Kelvin of the last
|
||||
* diffusion step (R4), the phosphorus implant dose (R4), the arsenic
|
||||
* implant flag (L4).
|
||||
*/
|
||||
GFREAD( fpSuprem, &recordMark, int, 1 );
|
||||
GFREAD( fpSuprem, &rdata, float, 1 );
|
||||
DEBUG fprintf(stderr,"rec 12: %f", rdata );
|
||||
GFREAD( fpSuprem, &rdata, float, 1 );
|
||||
DEBUG fprintf(stderr," %e", rdata );
|
||||
GFREAD( fpSuprem, &ldata, int, 1 );
|
||||
DEBUG fprintf(stderr," %d\n", ldata );
|
||||
GFREAD( fpSuprem, &recordMark, int, 1 );
|
||||
|
||||
fclose( fpSuprem );
|
||||
|
||||
/* shift silicon layer to beginning of array */
|
||||
for ( j=0; j < numLay; j++ ) {
|
||||
if (matTyp[ j ] == 1) {
|
||||
siIndex = j;
|
||||
}
|
||||
}
|
||||
offset = topNod[ siIndex ] - 1;
|
||||
numGrid -= offset;
|
||||
xStart = x[1 + offset];
|
||||
for ( i=1; i <= numGrid; i++ ) {
|
||||
x[i] = x[i + offset] - xStart;
|
||||
conc[i] = conc[i + offset];
|
||||
}
|
||||
|
||||
/* Store number of valid nodes using pointer */
|
||||
*numNod = numGrid;
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
SUPascRead( inFile, x, conc, impId, numNod )
|
||||
char *inFile;
|
||||
float *x, *conc;
|
||||
int *impId, *numNod;
|
||||
{
|
||||
int idata;
|
||||
int ldata;
|
||||
int i, j;
|
||||
float rdata;
|
||||
char cdata[21];
|
||||
int numLay, numImp, numGrid;
|
||||
int impTyp[4], matTyp[10], topNod[10], siIndex, offset;
|
||||
float xStart;
|
||||
float layerTh[10];
|
||||
float con[500];
|
||||
FILE *fpSuprem;
|
||||
|
||||
/* Clear Concentration Array */
|
||||
for ( i=0; i < MAXGRID; i++ ) {
|
||||
conc[i] = 0.0;
|
||||
}
|
||||
|
||||
/* Open Input File */
|
||||
if (!(fpSuprem = fopen( inFile, "r" ))) {
|
||||
perror(inFile);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The first line contains the number of layers (I4), the number of
|
||||
* impurities (I4), and the number of nodes (I4) present in the structure.
|
||||
*/
|
||||
fscanf( fpSuprem, "%d %d %d\n", &numLay, &numImp, &numGrid );
|
||||
DEBUG fprintf( stderr, "set 1: %d %d %d\n", numLay, numImp, numGrid);
|
||||
|
||||
/*
|
||||
* The second set of lines contains, for each layer, the material name (A20),
|
||||
* the material type (I4), the layer thickness (R4), and the pointer to
|
||||
* the top node of the layer (I4), and an unknown int and unknown float.
|
||||
* The material type code:
|
||||
* 1 - Si
|
||||
* 2 - SiO2
|
||||
* 3 - Poly
|
||||
* 4 - Si3N4
|
||||
* 5 - Alum
|
||||
*/
|
||||
for ( i=0; i < numLay; i++ ) {
|
||||
fscanf( fpSuprem, "%s\n %d %e %d %d %e\n",
|
||||
cdata, &matTyp[i], &layerTh[i], &topNod[i], &idata, &rdata );
|
||||
DEBUG fprintf(stderr,"set 2: %s: %d %f %d\n",
|
||||
cdata, matTyp[i], layerTh[i], topNod[i] );
|
||||
}
|
||||
|
||||
/*
|
||||
* The third set of lines contains, for each impurity, the name of the
|
||||
* impurity (A20) and the type of impurity (I4).
|
||||
*/
|
||||
for ( i=0; i < numImp; i++ ) {
|
||||
fscanf( fpSuprem, "%s\n %d\n", cdata, &impTyp[i] );
|
||||
DEBUG fprintf(stderr,"set 3: %s: %d\n", cdata, impTyp[i] );
|
||||
}
|
||||
|
||||
/*
|
||||
* The fourth set of lines contains, for each layer by each impurity, the
|
||||
* integrated dopant (R4), and the interior concentration of the
|
||||
* polysilicon grains (R4).
|
||||
*/
|
||||
for ( j=0; j < numLay; j++ ) {
|
||||
for ( i=0; i < numImp; i++ ) {
|
||||
fscanf( fpSuprem, "%e", &rdata );
|
||||
fscanf( fpSuprem, "%e", &rdata );
|
||||
}
|
||||
}
|
||||
DEBUG fprintf(stderr,"set 4:\n" );
|
||||
|
||||
/*
|
||||
* The fifth set of lines contains, for each node in the structure,
|
||||
* the distance to the next deepest node (R4), the distance from the
|
||||
* surface (R4), and, for each impurity type, the impurity's
|
||||
* chemical concentration (R4) and the impurity's active concentration (R4).
|
||||
*/
|
||||
for ( i=1; i <= numGrid; i++ ) {
|
||||
fscanf( fpSuprem, "%e %e", &rdata, &x[i] );
|
||||
|
||||
for ( j=0; j < numImp; j++ ) {
|
||||
/* chemical concentration - not required */
|
||||
fscanf( fpSuprem, "%e", &con[i] );
|
||||
|
||||
/* store active concentration */
|
||||
fscanf( fpSuprem, "%e", &con[i] );
|
||||
|
||||
/* orient sign properly */
|
||||
if (impTyp[j] == *impId) {
|
||||
/*...Boron...*/
|
||||
if (impTyp[j] == 1) {
|
||||
conc[i] = - con[i];
|
||||
} else {
|
||||
/*...All Other Impurities: P, As, Sb ...*/
|
||||
conc[i] = con[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
DEBUG fprintf( stderr, "set 5: %e %e\n", x[1], conc[1] );
|
||||
|
||||
/*
|
||||
* The last line in the file contains some random stuff that might be
|
||||
* useful to some people, the temperature in degrees Kelvin of the last
|
||||
* diffusion step (R4), the phosphorus implant dose (R4), the arsenic
|
||||
* implant flag (L4). However, we can just ignore that stuff.
|
||||
*/
|
||||
fclose( fpSuprem );
|
||||
|
||||
|
||||
/* shift silicon layer to beginning of array */
|
||||
for ( j=0; j < numLay; j++ ) {
|
||||
if (matTyp[ j ] == 1) {
|
||||
siIndex = j;
|
||||
}
|
||||
}
|
||||
offset = topNod[ siIndex ] - 1;
|
||||
numGrid -= offset;
|
||||
xStart = x[1 + offset];
|
||||
for ( i=1; i <= numGrid; i++ ) {
|
||||
x[i] = x[i + offset] - xStart;
|
||||
conc[i] = conc[i + offset];
|
||||
}
|
||||
|
||||
/* Store number of valid nodes using pointer */
|
||||
*numNod = numGrid;
|
||||
return;
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
## Process this file with automake to produce Makefile.in
|
||||
|
||||
noinst_LIBRARIES = libcidertwod.a
|
||||
|
||||
libcidertwod_a_SOURCES = \
|
||||
twoadmit.c \
|
||||
twoaval.c \
|
||||
twocond.c \
|
||||
twocont.c \
|
||||
twocurr.c \
|
||||
twodest.c \
|
||||
twodopng.c \
|
||||
twoelect.c \
|
||||
twofield.c \
|
||||
twomesh.c \
|
||||
twomobdv.c \
|
||||
twomobfn.c \
|
||||
twomobil.c \
|
||||
twoncont.c \
|
||||
twopcont.c \
|
||||
twopoiss.c \
|
||||
twoprint.c \
|
||||
twoproj.c \
|
||||
tworead.c \
|
||||
twosetbc.c \
|
||||
twosetup.c \
|
||||
twosolve.c
|
||||
|
||||
EXTRA_DIST = readme
|
||||
INCLUDES = -I$(top_srcdir)/src/include
|
||||
MAINTAINERCLEANFILES = Makefile.in
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
Directory: twod
|
||||
---------------
|
||||
This directory contains the files that are primarily responsible for
|
||||
implementing the 2D device simulator. It also contains files that help
|
||||
interface the circuit simulator to the device simulator. Most functions
|
||||
that are common to all 2D device simulations start with the prefix TWO,
|
||||
e.g. TWObiasSolve. The device-specific routines start with either NUMD2,
|
||||
NBJT2 or NUMOS, e.g. NUMD2admittance, NBJTproject or NUMOSconductance. The
|
||||
simulator contains a Poisson Solver for equilibrium, and a Two-carrier
|
||||
solver and One-carrier solvers for bias solutions. An attempt has been
|
||||
made to keep the function names parallel in the four portions. Poisson
|
||||
routines are identified with a 'Q' (for charge only) after the TWO, Full
|
||||
solver routines are identified with an underscore '_',
|
||||
Electron-current-only routines are identified with an 'N', and
|
||||
Hole-current-only routines are identified with a 'P'.
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,208 @@
|
|||
/**********
|
||||
Copyright 1991 Regents of the University of California. All rights reserved.
|
||||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group
|
||||
Author: 1990 David A. Gates, U. C. Berkeley CAD Group
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numglobs.h"
|
||||
#include "numenum.h"
|
||||
#include "twomesh.h"
|
||||
#include "twoddefs.h"
|
||||
#include "twodext.h"
|
||||
|
||||
|
||||
double
|
||||
TWOavalanche(TWOelem *pElem, TWOnode *pNode)
|
||||
{
|
||||
|
||||
TWOelem *pElemTL, *pElemTR, *pElemBL, *pElemBR;
|
||||
TWOedge *pEdgeT, *pEdgeB, *pEdgeL, *pEdgeR;
|
||||
int materT = 0 , materB = 0, materL = 0 , materR = 0;
|
||||
double dxL = 0.0, dxR = 0.0, dyT = 0.0, dyB = 0.0;
|
||||
double ef1, ef2, coeff1, coeff2;
|
||||
double enx, eny, epx, epy, jnx, jny, jpx, jpy;
|
||||
double current, eField;
|
||||
double generation = 0.0;
|
||||
double eiip2 = 4.0e5 / ENorm;
|
||||
double aiip2 = 6.71e5 * LNorm;
|
||||
double biip2 = 1.693e6 / ENorm;
|
||||
TWOmaterial *info = pElem->matlInfo;
|
||||
|
||||
/* Find all four neighboring elements */
|
||||
pElemTL = pNode->pTLElem;
|
||||
pElemTR = pNode->pTRElem;
|
||||
pElemBL = pNode->pBLElem;
|
||||
pElemBR = pNode->pBRElem;
|
||||
|
||||
/* Null edge pointers */
|
||||
pEdgeT = pEdgeB = pEdgeL = pEdgeR = NIL(TWOedge);
|
||||
|
||||
/* Find edges next to node */
|
||||
if ( pElemTL != NIL(TWOelem) ) {
|
||||
if (pElemTL->evalEdges[1]) {
|
||||
pEdgeT = pElemTL->pRightEdge;
|
||||
materT = pElemTL->elemType;
|
||||
dyT = pElemTL->dy;
|
||||
}
|
||||
if (pElemTL->evalEdges[2]) {
|
||||
pEdgeL = pElemTL->pBotEdge;
|
||||
materL = pElemTL->elemType;
|
||||
dxL = pElemTL->dx;
|
||||
}
|
||||
}
|
||||
if ( pElemTR != NIL(TWOelem) ) {
|
||||
if (pElemTR->evalEdges[3]) {
|
||||
pEdgeT = pElemTR->pLeftEdge;
|
||||
materT = pElemTR->elemType;
|
||||
dyT = pElemTR->dy;
|
||||
}
|
||||
if (pElemTR->evalEdges[2]) {
|
||||
pEdgeR = pElemTR->pBotEdge;
|
||||
materR = pElemTR->elemType;
|
||||
dxR = pElemTR->dx;
|
||||
}
|
||||
}
|
||||
if ( pElemBR != NIL(TWOelem) ) {
|
||||
if (pElemBR->evalEdges[3]) {
|
||||
pEdgeB = pElemBR->pLeftEdge;
|
||||
materB = pElemBR->elemType;
|
||||
dyB = pElemBR->dy;
|
||||
}
|
||||
if (pElemBR->evalEdges[0]) {
|
||||
pEdgeR = pElemBR->pTopEdge;
|
||||
materR = pElemBR->elemType;
|
||||
dxR = pElemBR->dx;
|
||||
}
|
||||
}
|
||||
if ( pElemBL != NIL(TWOelem) ) {
|
||||
if (pElemBL->evalEdges[1]) {
|
||||
pEdgeB = pElemBL->pRightEdge;
|
||||
materB = pElemBL->elemType;
|
||||
dyB = pElemBL->dy;
|
||||
}
|
||||
if (pElemBL->evalEdges[0]) {
|
||||
pEdgeL = pElemBL->pTopEdge;
|
||||
materL = pElemBL->elemType;
|
||||
dxL = pElemBL->dx;
|
||||
}
|
||||
}
|
||||
|
||||
/* compute horizontal vector components */
|
||||
/* No more than one of Left Edge or Right Edge is absent */
|
||||
/* If one is absent the other is guaranteed to be from silicon */
|
||||
if (pEdgeL == NIL(TWOedge)) {
|
||||
if ( pNode->nodeType == CONTACT ) {
|
||||
enx = -(pEdgeR->dPsi + pEdgeR->dCBand) / dxR;
|
||||
epx = -(pEdgeR->dPsi - pEdgeR->dVBand) / dxR;
|
||||
jnx = pEdgeR->jn;
|
||||
jpx = pEdgeR->jp;
|
||||
} else {
|
||||
enx = 0.0;
|
||||
epx = 0.0;
|
||||
jnx = 0.0;
|
||||
jpx = 0.0;
|
||||
}
|
||||
} else if (pEdgeR == NIL(TWOedge)) {
|
||||
if ( pNode->nodeType == CONTACT ) {
|
||||
enx = -(pEdgeL->dPsi + pEdgeL->dCBand) / dxL;
|
||||
epx = -(pEdgeL->dPsi - pEdgeL->dVBand) / dxL;
|
||||
jnx = pEdgeL->jn;
|
||||
jpx = pEdgeL->jp;
|
||||
} else {
|
||||
enx = 0.0;
|
||||
epx = 0.0;
|
||||
jnx = 0.0;
|
||||
jpx = 0.0;
|
||||
}
|
||||
} else { /* Both edges are present */
|
||||
coeff1 = dxL / (dxL + dxR);
|
||||
coeff2 = dxR / (dxL + dxR);
|
||||
ef1 = -(pEdgeL->dPsi + pEdgeL->dCBand) / dxL;
|
||||
ef2 = -(pEdgeR->dPsi + pEdgeR->dCBand) / dxR;
|
||||
enx = coeff2 * ef1 + coeff1 * ef2;
|
||||
ef1 = -(pEdgeL->dPsi - pEdgeL->dVBand) / dxL;
|
||||
ef2 = -(pEdgeR->dPsi - pEdgeR->dVBand) / dxR;
|
||||
epx = coeff2 * ef1 + coeff1 * ef2;
|
||||
if ( (materL == INSULATOR) || (materR == INSULATOR) ) {
|
||||
jnx = 0.0;
|
||||
jpx = 0.0;
|
||||
} else {
|
||||
jnx = coeff2 * pEdgeL->jn + coeff1 * pEdgeR->jn;
|
||||
jpx = coeff2 * pEdgeL->jp + coeff1 * pEdgeR->jp;
|
||||
}
|
||||
}
|
||||
|
||||
/* compute vertical vector components */
|
||||
/* No more than one of Top Edge or Bottom Edge is absent */
|
||||
/* If one is absent the other is guaranteed to be from silicon */
|
||||
if (pEdgeT == NIL(TWOedge)) {
|
||||
if ( pNode->nodeType == CONTACT ) {
|
||||
eny = -(pEdgeB->dPsi + pEdgeB->dCBand) / dyB;
|
||||
epy = -(pEdgeB->dPsi - pEdgeB->dVBand) / dyB;
|
||||
jny = pEdgeB->jn;
|
||||
jpy = pEdgeB->jp;
|
||||
} else {
|
||||
eny = 0.0;
|
||||
epy = 0.0;
|
||||
jny = 0.0;
|
||||
jpy = 0.0;
|
||||
}
|
||||
} else if (pEdgeB == NIL(TWOedge)) {
|
||||
if ( pNode->nodeType == CONTACT ) {
|
||||
eny = -(pEdgeT->dPsi + pEdgeT->dCBand) / dyT;
|
||||
epy = -(pEdgeT->dPsi - pEdgeT->dVBand) / dyT;
|
||||
jny = pEdgeT->jn;
|
||||
jpy = pEdgeT->jp;
|
||||
} else {
|
||||
eny = 0.0;
|
||||
epy = 0.0;
|
||||
jny = 0.0;
|
||||
jpy = 0.0;
|
||||
}
|
||||
} else { /* Both edges are present */
|
||||
coeff1 = dyT / (dyT + dyB);
|
||||
coeff2 = dyB / (dyT + dyB);
|
||||
ef1 = -(pEdgeT->dPsi + pEdgeT->dCBand) / dyT;
|
||||
ef2 = -(pEdgeB->dPsi + pEdgeB->dCBand) / dyB;
|
||||
eny = coeff2 * ef1 + coeff1 * ef2;
|
||||
ef1 = -(pEdgeT->dPsi - pEdgeT->dVBand) / dyT;
|
||||
ef2 = -(pEdgeB->dPsi - pEdgeB->dVBand) / dyB;
|
||||
epy = coeff2 * ef1 + coeff1 * ef2;
|
||||
if ( (materT == INSULATOR) || (materB == INSULATOR) ) {
|
||||
jny = 0.0;
|
||||
jpy = 0.0;
|
||||
} else {
|
||||
jny = coeff2 * pEdgeT->jn + coeff1 * pEdgeB->jn;
|
||||
jpy = coeff2 * pEdgeT->jp + coeff1 * pEdgeB->jp;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
fprintf(stderr,"%% en = (%9.2e,%9.2e), jn = (%9.2e,%9.2e)\n",
|
||||
enx,eny,jnx,jny);
|
||||
fprintf(stderr,"%% ep = (%9.2e,%9.2e), jp = (%9.2e,%9.2e)\n",
|
||||
epx,epy,jpx,jpy);
|
||||
*/
|
||||
|
||||
/* now calculate the avalanche generation rate */
|
||||
current = sqrt( jnx * jnx + jny * jny );
|
||||
if ( current != 0.0 ) {
|
||||
eField = (enx * jnx + eny * jny) / current;
|
||||
if ( (eField > 0) && ( info->bii[ELEC] / eField <= 80.0) ) {
|
||||
generation += current * info->aii[ELEC]
|
||||
* exp( - info->bii[ELEC] / eField );
|
||||
}
|
||||
}
|
||||
current = sqrt( jpx * jpx + jpy * jpy );
|
||||
if ( current != 0.0 ) {
|
||||
eField = (epx * jpx + epy * jpy) / current;
|
||||
if ( eField > eiip2 ) {
|
||||
generation += current * aiip2 * exp( - biip2 / eField );
|
||||
} else if ( (eField > 0) && ( info->bii[HOLE] / eField <= 80.0) ) {
|
||||
generation += current * info->aii[HOLE]
|
||||
* exp( - info->bii[HOLE] / eField );
|
||||
}
|
||||
}
|
||||
return( generation );
|
||||
}
|
||||
|
|
@ -0,0 +1,612 @@
|
|||
/**********
|
||||
Copyright 1991 Regents of the University of California. All rights reserved.
|
||||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group
|
||||
**********/
|
||||
|
||||
/* Functions to compute terminal conductances & currents. */
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numglobs.h"
|
||||
#include "numenum.h"
|
||||
#include "twomesh.h"
|
||||
#include "twodev.h"
|
||||
#include "bool.h"
|
||||
#include "spMatrix.h"
|
||||
#include "twoddefs.h"
|
||||
#include "twodext.h"
|
||||
|
||||
|
||||
void
|
||||
NUMD2conductance(TWOdevice *pDevice, BOOLEAN tranAnalysis,
|
||||
double *intCoeff, double *gd)
|
||||
{
|
||||
TWOcontact *pContact = pDevice->pFirstContact;
|
||||
int index;
|
||||
double *incVpn;
|
||||
BOOLEAN deltaVContact = FALSE;
|
||||
|
||||
/*
|
||||
* store the new rhs for computing the incremental quantities
|
||||
* with the second to last node. solve the system of equations
|
||||
*/
|
||||
incVpn = pDevice->dcDeltaSolution;
|
||||
storeNewRhs( pDevice, pDevice->pLastContact );
|
||||
spSolve( pDevice->matrix, pDevice->rhs, incVpn, NIL(spREAL), NIL(spREAL));
|
||||
|
||||
incVpn = pDevice->dcDeltaSolution;
|
||||
*gd = contactConductance( pDevice, pContact, deltaVContact, incVpn,
|
||||
tranAnalysis, intCoeff );
|
||||
*gd *= - GNorm * pDevice->width * LNorm;
|
||||
}
|
||||
|
||||
void
|
||||
NBJT2conductance(TWOdevice *pDevice, BOOLEAN tranAnalysis,
|
||||
double *intCoeff, double *dIeDVce, double *dIcDVce,
|
||||
double *dIeDVbe, double *dIcDVbe)
|
||||
{
|
||||
TWOcontact *pEmitContact = pDevice->pLastContact;
|
||||
TWOcontact *pColContact = pDevice->pFirstContact;
|
||||
TWOcontact *pBaseContact = pDevice->pFirstContact->next;
|
||||
int index;
|
||||
double width = pDevice->width;
|
||||
double *incVce, *incVbe;
|
||||
|
||||
/*
|
||||
* store the new rhs for computing the incremental quantities
|
||||
* incVce (dcDeltaSolution) and incVbe (copiedSolution) are used to
|
||||
* store the incremental quantities associated with Vce and Vbe
|
||||
*/
|
||||
|
||||
incVce = pDevice->dcDeltaSolution;
|
||||
incVbe = pDevice->copiedSolution;
|
||||
storeNewRhs( pDevice, pColContact );
|
||||
spSolve( pDevice->matrix, pDevice->rhs, incVce, NIL(spREAL), NIL(spREAL));
|
||||
storeNewRhs( pDevice, pBaseContact );
|
||||
spSolve( pDevice->matrix, pDevice->rhs, incVbe, NIL(spREAL), NIL(spREAL));
|
||||
|
||||
*dIeDVce = contactConductance( pDevice, pEmitContact, FALSE, incVce,
|
||||
tranAnalysis, intCoeff );
|
||||
*dIeDVbe = contactConductance( pDevice, pEmitContact, FALSE, incVbe,
|
||||
tranAnalysis, intCoeff );
|
||||
|
||||
*dIcDVce = contactConductance( pDevice, pColContact, TRUE, incVce,
|
||||
tranAnalysis, intCoeff );
|
||||
*dIcDVbe = contactConductance( pDevice, pColContact, FALSE, incVbe,
|
||||
tranAnalysis, intCoeff );
|
||||
*dIeDVce *= GNorm * width * LNorm;
|
||||
*dIcDVce *= GNorm * width * LNorm;
|
||||
*dIeDVbe *= GNorm * width * LNorm;
|
||||
*dIcDVbe *= GNorm * width * LNorm;
|
||||
}
|
||||
|
||||
void
|
||||
NUMOSconductance(TWOdevice *pDevice, BOOLEAN tranAnalysis, double *intCoeff,
|
||||
struct mosConductances *dIdV)
|
||||
{
|
||||
TWOcontact *pDContact = pDevice->pFirstContact;
|
||||
TWOcontact *pGContact = pDevice->pFirstContact->next;
|
||||
TWOcontact *pSContact = pDevice->pFirstContact->next->next;
|
||||
TWOcontact *pBContact = pDevice->pLastContact;
|
||||
int index;
|
||||
double width = pDevice->width;
|
||||
double *incVdb, *incVsb, *incVgb;
|
||||
|
||||
/*
|
||||
* store the new rhs for computing the incremental quantities
|
||||
* incVdb (dcDeltaSolution)
|
||||
*/
|
||||
|
||||
incVdb = pDevice->dcDeltaSolution;
|
||||
incVsb = pDevice->copiedSolution;
|
||||
incVgb = pDevice->rhsImag;
|
||||
storeNewRhs( pDevice, pDContact );
|
||||
spSolve( pDevice->matrix, pDevice->rhs, incVdb, NIL(spREAL), NIL(spREAL));
|
||||
storeNewRhs( pDevice, pSContact );
|
||||
spSolve( pDevice->matrix, pDevice->rhs, incVsb, NIL(spREAL), NIL(spREAL));
|
||||
storeNewRhs( pDevice, pGContact );
|
||||
spSolve( pDevice->matrix, pDevice->rhs, incVgb, NIL(spREAL), NIL(spREAL));
|
||||
|
||||
dIdV->dIdDVdb = contactConductance( pDevice, pDContact, TRUE,
|
||||
incVdb, tranAnalysis, intCoeff );
|
||||
dIdV->dIsDVdb = contactConductance( pDevice, pSContact, FALSE,
|
||||
incVdb, tranAnalysis, intCoeff );
|
||||
dIdV->dIgDVdb = GateTypeConductance( pDevice, pGContact, FALSE,
|
||||
incVdb, tranAnalysis, intCoeff );
|
||||
dIdV->dIdDVsb = contactConductance( pDevice, pDContact, FALSE,
|
||||
incVsb, tranAnalysis, intCoeff );
|
||||
dIdV->dIsDVsb = contactConductance( pDevice, pSContact, TRUE,
|
||||
incVsb, tranAnalysis, intCoeff );
|
||||
dIdV->dIgDVsb = GateTypeConductance( pDevice, pGContact, FALSE,
|
||||
incVsb, tranAnalysis, intCoeff );
|
||||
dIdV->dIdDVgb = contactConductance( pDevice, pDContact, FALSE,
|
||||
incVgb, tranAnalysis, intCoeff );
|
||||
dIdV->dIsDVgb = contactConductance( pDevice, pSContact, FALSE,
|
||||
incVgb, tranAnalysis, intCoeff );
|
||||
dIdV->dIgDVgb = GateTypeConductance( pDevice, pGContact, TRUE,
|
||||
incVgb, tranAnalysis, intCoeff );
|
||||
|
||||
dIdV->dIdDVdb *= GNorm * width * LNorm;
|
||||
dIdV->dIdDVsb *= GNorm * width * LNorm;
|
||||
dIdV->dIdDVgb *= GNorm * width * LNorm;
|
||||
dIdV->dIsDVdb *= GNorm * width * LNorm;
|
||||
dIdV->dIsDVsb *= GNorm * width * LNorm;
|
||||
dIdV->dIsDVgb *= GNorm * width * LNorm;
|
||||
dIdV->dIgDVdb *= GNorm * width * LNorm;
|
||||
dIdV->dIgDVsb *= GNorm * width * LNorm;
|
||||
dIdV->dIgDVgb *= GNorm * width * LNorm;
|
||||
|
||||
}
|
||||
|
||||
double
|
||||
contactCurrent(TWOdevice *pDevice, TWOcontact *pContact)
|
||||
{
|
||||
/* computes the current through the contact given in pContact */
|
||||
int index, i, numContactNodes;
|
||||
TWOnode *pNode;
|
||||
TWOelem *pElem;
|
||||
TWOedge *pHEdge, *pVEdge;
|
||||
double dx, dy;
|
||||
double jTotal = 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) ) {
|
||||
dx = 0.5 * pElem->dx;
|
||||
dy = 0.5 * pElem->dy;
|
||||
switch ( i ) {
|
||||
case 0:
|
||||
/* Bottom Right node */
|
||||
pHEdge = pElem->pBotEdge;
|
||||
pVEdge = pElem->pRightEdge;
|
||||
jTotal += pElem->epsRel * ( -dy * pHEdge->jd - dx * pVEdge->jd );
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
jTotal += -dy * (pHEdge->jn + pHEdge->jp)
|
||||
-dx * (pVEdge->jn + pVEdge->jp);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
/* Bottom Left node */
|
||||
pHEdge = pElem->pBotEdge;
|
||||
pVEdge = pElem->pLeftEdge;
|
||||
jTotal += pElem->epsRel * ( dy * pHEdge->jd - dx * pVEdge->jd );
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
jTotal += dy * (pHEdge->jn + pHEdge->jp)
|
||||
-dx * (pVEdge->jn + pVEdge->jp);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
/* Top Left node */
|
||||
pHEdge = pElem->pTopEdge;
|
||||
pVEdge = pElem->pLeftEdge;
|
||||
jTotal += pElem->epsRel * ( dy * pHEdge->jd + dx * pVEdge->jd );
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
jTotal += dy * (pHEdge->jn + pHEdge->jp)
|
||||
+ dx * (pVEdge->jn + pVEdge->jp);
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
/* Top Right Node */
|
||||
pHEdge = pElem->pTopEdge;
|
||||
pVEdge = pElem->pRightEdge;
|
||||
jTotal += pElem->epsRel * ( -dy * pHEdge->jd + dx * pVEdge->jd );
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
jTotal += -dy * (pHEdge->jn + pHEdge->jp)
|
||||
+ dx * (pVEdge->jn + pVEdge->jp);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return( jTotal * pDevice->width * LNorm * JNorm );
|
||||
}
|
||||
|
||||
double
|
||||
oxideCurrent(TWOdevice *pDevice, TWOcontact *pContact,
|
||||
BOOLEAN tranAnalysis)
|
||||
{
|
||||
/* computes the current through the contact given in pContact */
|
||||
int index, i, numContactNodes;
|
||||
TWOnode *pNode;
|
||||
TWOelem *pElem;
|
||||
TWOedge *pHEdge, *pVEdge;
|
||||
double dx, dy;
|
||||
double jTotal = 0.0;
|
||||
|
||||
if ( !tranAnalysis ) {
|
||||
return( jTotal );
|
||||
}
|
||||
|
||||
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) ) {
|
||||
dx = 0.5 * pElem->dx;
|
||||
dy = 0.5 * pElem->dy;
|
||||
switch ( i ) {
|
||||
case 0:
|
||||
/* Bottom Right node */
|
||||
pHEdge = pElem->pBotEdge;
|
||||
pVEdge = pElem->pRightEdge;
|
||||
jTotal += pElem->epsRel * ( -dy * pHEdge->jd - dx * pVEdge->jd );
|
||||
break;
|
||||
case 1:
|
||||
/* Bottom Left node */
|
||||
pHEdge = pElem->pBotEdge;
|
||||
pVEdge = pElem->pLeftEdge;
|
||||
jTotal += pElem->epsRel * ( dy * pHEdge->jd - dx * pVEdge->jd );
|
||||
break;
|
||||
case 2:
|
||||
/* Top Left node */
|
||||
pHEdge = pElem->pTopEdge;
|
||||
pVEdge = pElem->pLeftEdge;
|
||||
jTotal += pElem->epsRel * ( dy * pHEdge->jd + dx * pVEdge->jd );
|
||||
break;
|
||||
case 3:
|
||||
/* Top Right Node */
|
||||
pHEdge = pElem->pTopEdge;
|
||||
pVEdge = pElem->pRightEdge;
|
||||
jTotal += pElem->epsRel * ( -dy * pHEdge->jd + dx * pVEdge->jd );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return( jTotal * pDevice->width * LNorm * JNorm );
|
||||
}
|
||||
|
||||
|
||||
double
|
||||
contactConductance(TWOdevice *pDevice, TWOcontact *pContact,
|
||||
BOOLEAN delVContact, double *dxDv,
|
||||
BOOLEAN tranAnalysis, double *intCoeff)
|
||||
{
|
||||
/* computes the conductance of the contact given in pContact */
|
||||
int index, i, numContactNodes;
|
||||
TWOnode *pNode, *pHNode = NULL, *pVNode = NULL;
|
||||
TWOelem *pElem;
|
||||
TWOedge *pHEdge = NULL, *pVEdge = NULL;
|
||||
double dPsiDv, dnDv, dpDv;
|
||||
double gTotal = 0.0;
|
||||
int nInc, pInc;
|
||||
|
||||
/* for one carrier the rest of this code relies on appropriate
|
||||
current derivative term to be zero */
|
||||
if ( !OneCarrier ) {
|
||||
nInc = 1;
|
||||
pInc = 2;
|
||||
} else {
|
||||
nInc = 1;
|
||||
pInc = 1;
|
||||
}
|
||||
|
||||
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 ) {
|
||||
dnDv = dxDv[ pHNode->nEqn ];
|
||||
dpDv = dxDv[ pHNode->pEqn ];
|
||||
gTotal -= 0.5 * pElem->dy * (pHEdge->dJnDn * dnDv
|
||||
+ pHEdge->dJpDp * dpDv);
|
||||
}
|
||||
if ( pVNode->nodeType != CONTACT ) {
|
||||
dnDv = dxDv[ pVNode->nEqn ];
|
||||
dpDv = dxDv[ pVNode->pEqn ];
|
||||
gTotal -= 0.5 * pElem->dx * (pVEdge->dJnDn * dnDv
|
||||
+ pVEdge->dJpDp * dpDv);
|
||||
}
|
||||
}
|
||||
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 ) {
|
||||
dnDv = dxDv[ pHNode->nEqn ];
|
||||
dpDv = dxDv[ pHNode->pEqn ];
|
||||
gTotal += 0.5 * pElem->dy * (pHEdge->dJnDnP1 * dnDv
|
||||
+ pHEdge->dJpDpP1 * dpDv);
|
||||
}
|
||||
if ( pVNode->nodeType != CONTACT ) {
|
||||
dnDv = dxDv[ pVNode->nEqn ];
|
||||
dpDv = dxDv[ pVNode->pEqn ];
|
||||
gTotal -= 0.5 * pElem->dx * (pVEdge->dJnDn * dnDv
|
||||
+ pVEdge->dJpDp * dpDv);
|
||||
}
|
||||
}
|
||||
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 ) {
|
||||
dnDv = dxDv[ pHNode->nEqn ];
|
||||
dpDv = dxDv[ pHNode->pEqn ];
|
||||
gTotal += 0.5 * pElem->dy * (pHEdge->dJnDnP1 * dnDv
|
||||
+ pHEdge->dJpDpP1 * dpDv);
|
||||
}
|
||||
if ( pVNode->nodeType != CONTACT ) {
|
||||
dnDv = dxDv[ pVNode->nEqn ];
|
||||
dpDv = dxDv[ pVNode->pEqn ];
|
||||
gTotal += 0.5 * pElem->dx * (pVEdge->dJnDnP1 * dnDv
|
||||
+ pVEdge->dJpDpP1 * dpDv);
|
||||
}
|
||||
}
|
||||
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 ) {
|
||||
dnDv = dxDv[ pHNode->nEqn ];
|
||||
dpDv = dxDv[ pHNode->pEqn ];
|
||||
gTotal -= 0.5 * pElem->dy * (pHEdge->dJnDn * dnDv
|
||||
+ pHEdge->dJpDp * dpDv);
|
||||
}
|
||||
if ( pVNode->nodeType != CONTACT ) {
|
||||
dnDv = dxDv[ pVNode->nEqn ];
|
||||
dpDv = dxDv[ pVNode->pEqn ];
|
||||
gTotal += 0.5 * pElem->dx * (pVEdge->dJnDnP1 * dnDv
|
||||
+ pVEdge->dJpDpP1 * dpDv);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
if ( pHNode->nodeType != CONTACT ) {
|
||||
dPsiDv = dxDv[ pHNode->psiEqn ];
|
||||
gTotal += 0.5 * pElem->dy * dPsiDv * (pHEdge->dJnDpsiP1 + pHEdge->dJpDpsiP1 );
|
||||
if ( delVContact ) {
|
||||
gTotal -= 0.5 * pElem->dy * (pHEdge->dJnDpsiP1 + pHEdge->dJpDpsiP1 );
|
||||
}
|
||||
}
|
||||
if ( pVNode->nodeType != CONTACT ) {
|
||||
dPsiDv = dxDv[ pVNode->psiEqn ];
|
||||
gTotal += 0.5 * pElem->dx * dPsiDv * (pVEdge->dJnDpsiP1 + pVEdge->dJpDpsiP1 );
|
||||
if ( delVContact ) {
|
||||
gTotal -= 0.5 * pElem->dx * (pVEdge->dJnDpsiP1 + pVEdge->dJpDpsiP1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( tranAnalysis ) {
|
||||
/* add the displacement current terms */
|
||||
if ( pHNode->nodeType != CONTACT ) {
|
||||
dPsiDv = dxDv[ pHNode->psiEqn ];
|
||||
gTotal -= intCoeff[0] * pElem->epsRel * 0.5 * pElem->dyOverDx * dPsiDv;
|
||||
if ( delVContact ) {
|
||||
gTotal += intCoeff[0] * pElem->epsRel * 0.5 * pElem->dyOverDx;
|
||||
}
|
||||
}
|
||||
if ( pVNode->nodeType != CONTACT ) {
|
||||
dPsiDv = dxDv[ pVNode->psiEqn ];
|
||||
gTotal -= intCoeff[0] * pElem->epsRel * 0.5 * pElem->dxOverDy * dPsiDv;
|
||||
if ( delVContact ) {
|
||||
gTotal += intCoeff[0] * pElem->epsRel * 0.5 * pElem->dxOverDy;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return( gTotal );
|
||||
}
|
||||
|
||||
|
||||
double
|
||||
oxideConductance(TWOdevice *pDevice, TWOcontact *pContact,
|
||||
BOOLEAN delVContact, double *dxDv,
|
||||
BOOLEAN tranAnalysis, double *intCoeff)
|
||||
{
|
||||
/* computes the conductance of the contact given in pContact */
|
||||
int index, i, numContactNodes;
|
||||
TWOnode *pNode, *pHNode = NULL, *pVNode = NULL;
|
||||
TWOelem *pElem;
|
||||
double dPsiDv, dnDv, dpDv;
|
||||
double gTotal = 0.0;
|
||||
|
||||
if ( !tranAnalysis ) {
|
||||
return( gTotal );
|
||||
}
|
||||
|
||||
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;
|
||||
break;
|
||||
case 1:
|
||||
/* the TR element */
|
||||
pHNode = pElem->pBRNode;
|
||||
pVNode = pElem->pTLNode;
|
||||
break;
|
||||
case 2:
|
||||
/* the BR element*/
|
||||
pHNode = pElem->pTRNode;
|
||||
pVNode = pElem->pBLNode;
|
||||
break;
|
||||
case 3:
|
||||
/* the BL element */
|
||||
pHNode = pElem->pTLNode;
|
||||
pVNode = pElem->pBRNode;
|
||||
break;
|
||||
}
|
||||
/* add the displacement current terms */
|
||||
if ( pHNode->nodeType != CONTACT ) {
|
||||
dPsiDv = dxDv[ pHNode->psiEqn ];
|
||||
gTotal -= intCoeff[0] * pElem->epsRel * 0.5 * pElem->dyOverDx * dPsiDv;
|
||||
if ( delVContact ) {
|
||||
gTotal += intCoeff[0] * pElem->epsRel * 0.5 * pElem->dyOverDx;
|
||||
}
|
||||
}
|
||||
if ( pVNode->nodeType != CONTACT ) {
|
||||
dPsiDv = dxDv[ pVNode->psiEqn ];
|
||||
gTotal -= intCoeff[0] * pElem->epsRel * 0.5 * pElem->dxOverDy * dPsiDv;
|
||||
if ( delVContact ) {
|
||||
gTotal += intCoeff[0] * pElem->epsRel * 0.5 * pElem->dxOverDy;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return( gTotal );
|
||||
}
|
||||
|
||||
/* these functions are used for solving the complete system of
|
||||
* equations directly using LU decomposition 1/22/88
|
||||
*/
|
||||
|
||||
void
|
||||
NUMD2current(TWOdevice *pDevice, BOOLEAN tranAnalysis,
|
||||
double *intCoeff, double *id)
|
||||
{
|
||||
TWOcontact *pPContact = pDevice->pFirstContact;
|
||||
TWOcontact *pNContact = pDevice->pLastContact;
|
||||
int index;
|
||||
double ip, ipPrime, *solution;
|
||||
double in;
|
||||
BOOLEAN deltaVContact = FALSE;
|
||||
|
||||
solution = pDevice->dcDeltaSolution;
|
||||
ip = contactCurrent( pDevice, pPContact );
|
||||
/*
|
||||
in = contactCurrent( pDevice, pNContact );
|
||||
fprintf(stdout, "DIO current: ( %11.4e error )\n", ip+in );
|
||||
fprintf(stdout, " Ip = %11.4e In = %11.4e\n", ip, in );
|
||||
*/
|
||||
|
||||
/*
|
||||
* for the additional contribution to id will make use of
|
||||
* contactConductance. This function will be called
|
||||
* with the dcDeltaSolution vector instead of the incremental quantities
|
||||
*/
|
||||
ipPrime = contactConductance( pDevice, pPContact, deltaVContact,
|
||||
solution, tranAnalysis, intCoeff );
|
||||
|
||||
ipPrime *= JNorm * pDevice->width * LNorm;
|
||||
ip += ipPrime;
|
||||
*id = ip;
|
||||
}
|
||||
|
||||
void
|
||||
NBJT2current(TWOdevice *pDevice, BOOLEAN tranAnalysis, double *intCoeff,
|
||||
double *ie, double *ic)
|
||||
{
|
||||
TWOcontact *pEmitContact = pDevice->pLastContact;
|
||||
TWOcontact *pColContact = pDevice->pFirstContact;
|
||||
TWOcontact *pBaseContact = pDevice->pFirstContact->next;
|
||||
double *solution, iePrime, icPrime;
|
||||
double ib;
|
||||
|
||||
solution = pDevice->dcDeltaSolution;
|
||||
|
||||
*ie = contactCurrent( pDevice, pEmitContact );
|
||||
*ic = contactCurrent( pDevice, pColContact );
|
||||
/*
|
||||
ib = contactCurrent( pDevice, pBaseContact );
|
||||
fprintf(stdout, "BJT current: ( %11.4e error )\n", *ic+ib+*ie );
|
||||
fprintf(stdout, " Ic = %11.4e Ib = %11.4e\n", *ic, ib );
|
||||
fprintf(stdout, " Ie = %11.4e\n", *ie );
|
||||
*/
|
||||
|
||||
iePrime = contactConductance( pDevice, pEmitContact, FALSE, solution,
|
||||
tranAnalysis, intCoeff );
|
||||
|
||||
icPrime = contactConductance( pDevice, pColContact, FALSE, solution,
|
||||
tranAnalysis, intCoeff );
|
||||
|
||||
iePrime *= JNorm * pDevice->width * LNorm;
|
||||
icPrime *= JNorm * pDevice->width * LNorm;
|
||||
|
||||
*ie += iePrime;
|
||||
*ic += icPrime;
|
||||
}
|
||||
|
||||
void
|
||||
NUMOScurrent(TWOdevice *pDevice, BOOLEAN tranAnalysis, double *intCoeff,
|
||||
double *id, double *is, double *ig)
|
||||
{
|
||||
TWOcontact *pDContact = pDevice->pFirstContact;
|
||||
TWOcontact *pGContact = pDevice->pFirstContact->next;
|
||||
TWOcontact *pSContact = pDevice->pFirstContact->next->next;
|
||||
TWOcontact *pBContact = pDevice->pLastContact;
|
||||
double *solution, idPrime, isPrime, igPrime;
|
||||
double ib;
|
||||
|
||||
solution = pDevice->dcDeltaSolution;
|
||||
|
||||
*id = contactCurrent( pDevice, pDContact );
|
||||
|
||||
/*
|
||||
* This is a terrible hack
|
||||
*/
|
||||
|
||||
#ifdef NORMAL_GATE
|
||||
*ig = GateTypeCurrent( pDevice, pGContact, tranAnalysis );
|
||||
#else
|
||||
*ig = GateTypeCurrent( pDevice, pGContact);
|
||||
#endif
|
||||
|
||||
*is = contactCurrent( pDevice, pSContact );
|
||||
/*
|
||||
ib = contactCurrent( pDevice, pBContact );
|
||||
fprintf(stdout, "MOS current: ( %11.4e error )\n", *id+*ig+*is+ib );
|
||||
fprintf(stdout, " Id = %11.4e Is = %11.4e\n", *id, *is );
|
||||
fprintf(stdout, " Ig = %11.4e Ib = %11.4e\n", *ig, ib );
|
||||
*/
|
||||
|
||||
idPrime = contactConductance( pDevice, pDContact, FALSE,
|
||||
solution, tranAnalysis, intCoeff );
|
||||
|
||||
isPrime = contactConductance( pDevice, pSContact, FALSE,
|
||||
solution, tranAnalysis, intCoeff );
|
||||
|
||||
igPrime = GateTypeConductance( pDevice, pGContact, FALSE,
|
||||
solution, tranAnalysis, intCoeff );
|
||||
|
||||
idPrime *= JNorm * pDevice->width * LNorm;
|
||||
isPrime *= JNorm * pDevice->width * LNorm;
|
||||
igPrime *= JNorm * pDevice->width * LNorm;
|
||||
|
||||
*id += idPrime;
|
||||
*is += isPrime;
|
||||
*ig += igPrime;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,183 @@
|
|||
/**********
|
||||
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
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numglobs.h"
|
||||
#include "numenum.h"
|
||||
#include "twomesh.h"
|
||||
#include "twoddefs.h"
|
||||
#include "twodext.h"
|
||||
|
||||
void
|
||||
nodeCurrents(TWOelem *pElem, TWOnode *pNode, double *mun, double *mup,
|
||||
double *jnx, double *jny, double *jpx, double *jpy,
|
||||
double *jdx, double *jdy)
|
||||
{
|
||||
|
||||
TWOelem *pElemTL, *pElemTR, *pElemBL, *pElemBR;
|
||||
TWOedge *pEdgeT, *pEdgeB, *pEdgeL, *pEdgeR;
|
||||
int materT = 0, materB = 0, materL = 0, materR = 0;
|
||||
int numFound = 0;
|
||||
double dxL = 0.0, dxR = 0.0, dyT = 0.0, dyB = 0.0;
|
||||
double epsL = 0.0, epsR = 0.0, epsT = 0.0, epsB = 0.0;
|
||||
double coeff1, coeff2;
|
||||
|
||||
/* Find all four neighboring elements */
|
||||
pElemTL = pNode->pTLElem;
|
||||
pElemTR = pNode->pTRElem;
|
||||
pElemBL = pNode->pBLElem;
|
||||
pElemBR = pNode->pBRElem;
|
||||
|
||||
/* Null edge pointers */
|
||||
pEdgeT = pEdgeB = pEdgeL = pEdgeR = NIL(TWOedge);
|
||||
|
||||
/* Zero mobilities */
|
||||
*mun = *mup = 0.0;
|
||||
|
||||
/* Find edges next to node */
|
||||
if (pElemTL != NIL(TWOelem)) {
|
||||
numFound++;
|
||||
*mun += pElemTL->mun0;
|
||||
*mup += pElemTL->mup0;
|
||||
if (pElemTL->evalEdges[1]) {
|
||||
pEdgeT = pElemTL->pRightEdge;
|
||||
materT = pElemTL->elemType;
|
||||
dyT = pElemTL->dy;
|
||||
epsT = pElemTL->epsRel;
|
||||
}
|
||||
if (pElemTL->evalEdges[2]) {
|
||||
pEdgeL = pElemTL->pBotEdge;
|
||||
materL = pElemTL->elemType;
|
||||
dxL = pElemTL->dx;
|
||||
epsL = pElemTL->epsRel;
|
||||
}
|
||||
}
|
||||
if (pElemTR != NIL(TWOelem)) {
|
||||
numFound++;
|
||||
*mun += pElemTR->mun0;
|
||||
*mup += pElemTR->mup0;
|
||||
if (pElemTR->evalEdges[3]) {
|
||||
pEdgeT = pElemTR->pLeftEdge;
|
||||
materT = pElemTR->elemType;
|
||||
epsT = pElemTR->epsRel;
|
||||
}
|
||||
if (pElemTR->evalEdges[2]) {
|
||||
pEdgeR = pElemTR->pBotEdge;
|
||||
materR = pElemTR->elemType;
|
||||
dxR = pElemTR->dx;
|
||||
epsR = pElemTR->epsRel;
|
||||
}
|
||||
}
|
||||
if (pElemBR != NIL(TWOelem)) {
|
||||
numFound++;
|
||||
*mun += pElemBR->mun0;
|
||||
*mup += pElemBR->mup0;
|
||||
if (pElemBR->evalEdges[3]) {
|
||||
pEdgeB = pElemBR->pLeftEdge;
|
||||
materB = pElemBR->elemType;
|
||||
dyB = pElemBR->dy;
|
||||
epsB = pElemBR->epsRel;
|
||||
}
|
||||
if (pElemBR->evalEdges[0]) {
|
||||
pEdgeR = pElemBR->pTopEdge;
|
||||
materR = pElemBR->elemType;
|
||||
dxR = pElemBR->dx;
|
||||
epsR = pElemBR->epsRel;
|
||||
}
|
||||
}
|
||||
if (pElemBL != NIL(TWOelem)) {
|
||||
numFound++;
|
||||
*mun += pElemBL->mun0;
|
||||
*mup += pElemBL->mup0;
|
||||
if (pElemBL->evalEdges[1]) {
|
||||
pEdgeB = pElemBL->pRightEdge;
|
||||
materB = pElemBL->elemType;
|
||||
dyB = pElemBL->dy;
|
||||
epsB = pElemBL->epsRel;
|
||||
}
|
||||
if (pElemBL->evalEdges[0]) {
|
||||
pEdgeL = pElemBL->pTopEdge;
|
||||
materL = pElemBL->elemType;
|
||||
dxL = pElemBL->dx;
|
||||
epsL = pElemBL->epsRel;
|
||||
}
|
||||
}
|
||||
*mun /= (double) numFound;
|
||||
*mup /= (double) numFound;
|
||||
/* compute horizontal vector components */
|
||||
/* No more than one of Left Edge or Right Edge is absent */
|
||||
/* If one is absent the other is guaranteed to be from silicon */
|
||||
if (pEdgeL == NIL(TWOedge)) {
|
||||
if (pNode->nodeType == CONTACT) {
|
||||
*jnx = pEdgeR->jn;
|
||||
*jpx = pEdgeR->jp;
|
||||
*jdx = pEdgeR->jd;
|
||||
} else {
|
||||
*jnx = 0.0;
|
||||
*jpx = 0.0;
|
||||
*jdx = 0.0;
|
||||
}
|
||||
} else if (pEdgeR == NIL(TWOedge)) {
|
||||
if (pNode->nodeType == CONTACT) {
|
||||
*jnx = pEdgeL->jn;
|
||||
*jpx = pEdgeL->jp;
|
||||
*jdx = pEdgeL->jd;
|
||||
} else {
|
||||
*jnx = 0.0;
|
||||
*jpx = 0.0;
|
||||
*jdx = 0.0;
|
||||
}
|
||||
} else { /* Both edges are present */
|
||||
coeff1 = dxL / (dxL + dxR);
|
||||
coeff2 = dxR / (dxL + dxR);
|
||||
if ((materL == INSULATOR) || (materR == INSULATOR)) {
|
||||
*jnx = 0.0;
|
||||
*jpx = 0.0;
|
||||
*jdx = coeff2 * epsL * pEdgeL->jd + coeff1 * epsR * pEdgeR->jd;
|
||||
} else {
|
||||
*jnx = coeff2 * pEdgeL->jn + coeff1 * pEdgeR->jn;
|
||||
*jpx = coeff2 * pEdgeL->jp + coeff1 * pEdgeR->jp;
|
||||
*jdx = coeff2 * pEdgeL->jd + coeff1 * pEdgeR->jd;
|
||||
}
|
||||
}
|
||||
|
||||
/* compute vertical vector components */
|
||||
/* No more than one of Top Edge or Bottom Edge is absent */
|
||||
/* If one is absent the other is guaranteed to be from silicon */
|
||||
if (pEdgeT == NIL(TWOedge)) {
|
||||
if (pNode->nodeType == CONTACT) {
|
||||
*jny = pEdgeB->jn;
|
||||
*jpy = pEdgeB->jp;
|
||||
*jdy = pEdgeB->jd;
|
||||
} else {
|
||||
*jny = 0.0;
|
||||
*jpy = 0.0;
|
||||
*jdy = 0.0;
|
||||
}
|
||||
} else if (pEdgeB == NIL(TWOedge)) {
|
||||
if (pNode->nodeType == CONTACT) {
|
||||
*jny = pEdgeT->jn;
|
||||
*jpy = pEdgeT->jp;
|
||||
*jdy = pEdgeT->jd;
|
||||
} else {
|
||||
*jny = 0.0;
|
||||
*jpy = 0.0;
|
||||
*jdy = 0.0;
|
||||
}
|
||||
} else { /* Both edges are present */
|
||||
coeff1 = dyT / (dyT + dyB);
|
||||
coeff2 = dyB / (dyT + dyB);
|
||||
if ((materT == INSULATOR) || (materB == INSULATOR)) {
|
||||
*jny = 0.0;
|
||||
*jpy = 0.0;
|
||||
*jdy = coeff2 * epsT * pEdgeT->jd + coeff1 * epsB * pEdgeB->jd;
|
||||
} else {
|
||||
*jny = coeff2 * pEdgeT->jn + coeff1 * pEdgeB->jn;
|
||||
*jpy = coeff2 * pEdgeT->jp + coeff1 * pEdgeB->jp;
|
||||
*jdy = coeff2 * pEdgeT->jd + coeff1 * pEdgeB->jd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 2001 Paolo Nenzi
|
||||
*/
|
||||
|
||||
#ifndef _TWODDEFS_H
|
||||
#define _TWODDEFS_H
|
||||
|
||||
/* Debug statements */
|
||||
|
||||
extern BOOLEAN TWOacDebug;
|
||||
extern BOOLEAN TWOdcDebug;
|
||||
extern BOOLEAN TWOtranDebug;
|
||||
extern BOOLEAN TWOjacDebug;
|
||||
|
||||
/* Now some defines for the two dimensional simulator
|
||||
* library.
|
||||
* Theese defines were gathered from all the code in
|
||||
* oned directory.
|
||||
*/
|
||||
|
||||
|
||||
/* Temporary hack to remove NUMOS gate special case */
|
||||
#ifdef NORMAL_GATE
|
||||
#define GateTypeAdmittance oxideAdmittance
|
||||
#else
|
||||
#define GateTypeAdmittance contactAdmittance
|
||||
#endif /* NORMAL_GATE */
|
||||
|
||||
/* Temporary hack to remove NUMOS gate special case */
|
||||
#ifdef NORMAL_GATE
|
||||
#define GateTypeConductance oxideConductance
|
||||
#define GateTypeCurrent oxideCurrent
|
||||
#else
|
||||
#define GateTypeConductance contactConductance
|
||||
#define GateTypeCurrent contactCurrent
|
||||
#endif /* NORMAL_GATE */
|
||||
|
||||
/* This structure was moved up from twoadmit.c */
|
||||
struct mosAdmittances {
|
||||
SPcomplex yIdVdb;
|
||||
SPcomplex yIdVsb;
|
||||
SPcomplex yIdVgb;
|
||||
SPcomplex yIsVdb;
|
||||
SPcomplex yIsVsb;
|
||||
SPcomplex yIsVgb;
|
||||
SPcomplex yIgVdb;
|
||||
SPcomplex yIgVsb;
|
||||
SPcomplex yIgVgb;
|
||||
};
|
||||
|
||||
#define MAXTERMINALS 5 /* One more than max number of terminals */
|
||||
#define ELCT_ID poiEqn
|
||||
|
||||
#define MIN_DELV 1e-3
|
||||
#define NORM_RED_MAXITERS 10
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
/**********
|
||||
Copyright 1991 Regents of the University of California. All rights reserved.
|
||||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numglobs.h"
|
||||
#include "numenum.h"
|
||||
#include "twodev.h"
|
||||
#include "twomesh.h"
|
||||
#include "spMatrix.h"
|
||||
#include "twoddefs.h"
|
||||
#include "twodext.h"
|
||||
|
||||
void
|
||||
TWOdestroy(TWOdevice *pDevice)
|
||||
{
|
||||
int index, eIndex;
|
||||
TWOelem *pElem;
|
||||
TWOnode *pNode;
|
||||
TWOedge *pEdge;
|
||||
|
||||
if ( !pDevice ) return;
|
||||
|
||||
switch ( pDevice->solverType ) {
|
||||
case SLV_SMSIG:
|
||||
case SLV_BIAS:
|
||||
/* free up memory allocated for the bias solution */
|
||||
FREE( pDevice->dcSolution );
|
||||
FREE( pDevice->dcDeltaSolution );
|
||||
FREE( pDevice->copiedSolution );
|
||||
FREE( pDevice->rhs );
|
||||
FREE( pDevice->rhsImag );
|
||||
spDestroy( pDevice->matrix );
|
||||
break;
|
||||
case SLV_EQUIL:
|
||||
/* free up the vectors allocated in the equilibrium solution */
|
||||
FREE( pDevice->dcSolution );
|
||||
FREE( pDevice->dcDeltaSolution );
|
||||
FREE( pDevice->copiedSolution );
|
||||
FREE( pDevice->rhs );
|
||||
spDestroy( pDevice->matrix );
|
||||
break;
|
||||
case SLV_NONE:
|
||||
break;
|
||||
default:
|
||||
fprintf( stderr, "Panic: Unknown solver type in TWOdestroy.\n" );
|
||||
exit( -1 );
|
||||
break;
|
||||
}
|
||||
|
||||
/* destroy the mesh */
|
||||
if ( pDevice->elements ) {
|
||||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
|
||||
pElem = pDevice->elements[ eIndex ];
|
||||
for ( index = 0; index <= 3; index++ ) {
|
||||
if ( pElem->evalNodes[ index ] ) {
|
||||
pNode = pElem->pNodes[ index ];
|
||||
FREE( pNode );
|
||||
}
|
||||
if ( pElem->evalEdges[ index ] ) {
|
||||
pEdge = pElem->pEdges[ index ];
|
||||
FREE( pEdge );
|
||||
}
|
||||
}
|
||||
FREE( pElem );
|
||||
}
|
||||
FREE( pDevice->elements );
|
||||
FREE( pDevice->elemArray );
|
||||
}
|
||||
|
||||
/* destroy the contacts & channels */
|
||||
/* NOT IMPLEMENTED */
|
||||
|
||||
FREE( pDevice );
|
||||
}
|
||||
|
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* 2001 Paolo Nenzi
|
||||
*/
|
||||
|
||||
/* External symbols for Two Dimensional simulator */
|
||||
|
||||
#ifndef _TWODEXT_H
|
||||
#define _TWODEXT_H
|
||||
|
||||
#include "profile.h"
|
||||
#include "twomesh.h"
|
||||
#include "twodev.h"
|
||||
#include "carddefs.h"
|
||||
#include "bool.h"
|
||||
#include "complex.h"
|
||||
|
||||
/* twoadmit.c */
|
||||
extern int NUMD2admittance(TWOdevice *, double, SPcomplex *);
|
||||
extern int NBJT2admittance(TWOdevice *, double, SPcomplex *,
|
||||
SPcomplex *, SPcomplex *, SPcomplex *);
|
||||
extern int NUMOSadmittance(TWOdevice *, double, struct mosAdmittances *);
|
||||
extern BOOLEAN TWOsorSolve(TWOdevice *, double *, double *, double);
|
||||
extern SPcomplex *contactAdmittance(TWOdevice *, TWOcontact *, BOOLEAN,
|
||||
double *, double *, SPcomplex *);
|
||||
extern SPcomplex *oxideAdmittance(TWOdevice *, TWOcontact *,BOOLEAN,
|
||||
double *, double *, SPcomplex *);
|
||||
|
||||
extern void NUMD2ys(TWOdevice *, SPcomplex *, SPcomplex *);
|
||||
extern void NBJT2ys(TWOdevice *,SPcomplex *, SPcomplex *, SPcomplex *,
|
||||
SPcomplex *, SPcomplex *);
|
||||
extern void NUMOSys(TWOdevice *, SPcomplex *, struct mosAdmittances *);
|
||||
|
||||
/* twoaval.c */
|
||||
extern double TWOavalanche(TWOelem *, TWOnode *);
|
||||
|
||||
/* twocond.c */
|
||||
extern void NUMD2conductance(TWOdevice *, BOOLEAN, double *, double *);
|
||||
extern void NBJT2conductance(TWOdevice *, BOOLEAN, double *, double *,
|
||||
double *, double *, double *);
|
||||
extern void NUMOSconductance(TWOdevice *, BOOLEAN, double *,
|
||||
struct mosConductances *);
|
||||
extern double contactCurrent(TWOdevice *, TWOcontact *);
|
||||
extern double oxideCurrent(TWOdevice *, TWOcontact *, BOOLEAN);
|
||||
extern double contactConductance(TWOdevice *, TWOcontact *, BOOLEAN,
|
||||
double *, BOOLEAN, double *);
|
||||
extern double oxideConductance(TWOdevice *, TWOcontact *, BOOLEAN,
|
||||
double *, BOOLEAN, double *);
|
||||
extern void NUMD2current(TWOdevice *, BOOLEAN, double *, double *);
|
||||
extern void NBJT2current(TWOdevice *, BOOLEAN, double *, double *,
|
||||
double *);
|
||||
extern void NUMOScurrent(TWOdevice *, BOOLEAN , double *, double *, double *,
|
||||
double *);
|
||||
|
||||
/* twocont */
|
||||
extern void TWO_jacBuild(TWOdevice *);
|
||||
extern void TWO_sysLoad(TWOdevice *, BOOLEAN, TWOtranInfo *);
|
||||
extern void TWO_jacLoad(TWOdevice *);
|
||||
extern void TWO_rhsLoad(TWOdevice *, BOOLEAN, TWOtranInfo *);
|
||||
extern void TWO_commonTerms(TWOdevice *, BOOLEAN, BOOLEAN, TWOtranInfo *);
|
||||
|
||||
/* twocurr.c */
|
||||
extern void nodeCurrents(TWOelem *, TWOnode *, double *, double *,
|
||||
double *, double *, double *, double *, double *, double *);
|
||||
|
||||
/* twodest */
|
||||
extern void TWOdestroy(TWOdevice *);
|
||||
|
||||
/* twodopng.c */
|
||||
extern double TWOdopingValue(DOPprofile *, DOPtable *, double, double);
|
||||
extern void TWOsetDoping(TWOdevice *, DOPprofile *, DOPtable *);
|
||||
|
||||
/* twoelect.c */
|
||||
extern int TWOcmpElectrode(TWOelectrode *, TWOelectrode *);
|
||||
extern void checkElectrodes(TWOelectrode *, int);
|
||||
extern void setupContacts(TWOdevice *, TWOelectrode *, TWOnode ***);
|
||||
|
||||
/* twofield.c */
|
||||
extern void nodeFields(TWOelem *, TWOnode *, double *, double *);
|
||||
|
||||
/* twomesh.c */
|
||||
extern void TWObuildMesh(TWOdevice *, TWOdomain *, TWOelectrode *,
|
||||
TWOmaterial *);
|
||||
extern void TWOprnMesh(TWOdevice *);
|
||||
extern void TWOgetStatePointers(TWOdevice *, int *);
|
||||
|
||||
/* twomobdv.c */
|
||||
extern void TWO_mobDeriv(TWOelem *, int, double);
|
||||
extern void TWONmobDeriv(TWOelem *, int, double);
|
||||
extern void TWOPmobDeriv(TWOelem *, int, double);
|
||||
|
||||
/* twomobfn.c */
|
||||
extern void MOBsurfElec(TWOmaterial *, TWOelem *, double, double,
|
||||
double, double, double, double);
|
||||
extern void MOBsurfHole(TWOmaterial *, TWOelem *, double, double,
|
||||
double, double, double, double);
|
||||
|
||||
/* twomobil.c */
|
||||
extern void TWO_mobility(TWOelem *, double);
|
||||
extern void TWONmobility(TWOelem *, double);
|
||||
extern void TWOPmobility(TWOelem *, double);
|
||||
|
||||
/* twoncont.c */
|
||||
extern void TWONjacBuild(TWOdevice *);
|
||||
extern void TWONsysLoad(TWOdevice *, BOOLEAN, TWOtranInfo *);
|
||||
extern void TWONjacLoad(TWOdevice *);
|
||||
extern void TWONrhsLoad(TWOdevice *, BOOLEAN, TWOtranInfo *);
|
||||
extern void TWONcommonTerms(TWOdevice *, BOOLEAN, BOOLEAN, TWOtranInfo *);
|
||||
|
||||
/* twopcont.c */
|
||||
extern void TWOPjacBuild(TWOdevice *);
|
||||
extern void TWOPsysLoad(TWOdevice *, BOOLEAN, TWOtranInfo *);
|
||||
extern void TWOPjacLoad(TWOdevice *);
|
||||
extern void TWOPrhsLoad(TWOdevice *, BOOLEAN, TWOtranInfo *);
|
||||
extern void TWOPcommonTerms(TWOdevice *, BOOLEAN, BOOLEAN, TWOtranInfo *);
|
||||
|
||||
/* twopoiss.c */
|
||||
extern void TWOQjacBuild(TWOdevice *);
|
||||
extern void TWOQsysLoad(TWOdevice *);
|
||||
extern void TWOQrhsLoad(TWOdevice *);
|
||||
extern void TWOQcommonTerms(TWOdevice *);
|
||||
|
||||
/*twoprint.c */
|
||||
extern void TWOprnSolution(FILE *, TWOdevice *, OUTPcard *);
|
||||
extern void TWOmemStats(FILE *, TWOdevice *);
|
||||
extern void TWOcpuStats(FILE *, TWOdevice *);
|
||||
|
||||
|
||||
/* twoproj.c */
|
||||
extern void NUMD2project(TWOdevice *, double);
|
||||
extern void NBJT2project(TWOdevice *, double, double);
|
||||
extern void NUMOSproject(TWOdevice *, double, double, double);
|
||||
extern void NUMD2update(TWOdevice *,double, BOOLEAN);
|
||||
extern void NBJT2update(TWOdevice *, double, double, BOOLEAN);
|
||||
extern void NUMOSupdate(TWOdevice *, double, double, double, BOOLEAN);
|
||||
extern void storeNewRhs(TWOdevice *, TWOcontact *);
|
||||
|
||||
/* tworead.c */
|
||||
extern int TWOreadState(TWOdevice *, char *, int, double *, double *, double *);
|
||||
|
||||
/*twosetbc.c */
|
||||
extern void NUMD2setBCs(TWOdevice *, double);
|
||||
extern void NBJT2setBCs(TWOdevice *, double, double);
|
||||
extern void NUMOSsetBCs(TWOdevice *, double, double, double);
|
||||
|
||||
/*twosetup.c */
|
||||
extern void TWOsetup(TWOdevice *);
|
||||
extern void TWOsetBCparams(TWOdevice *, BDRYcard *);
|
||||
extern void TWOnormalize(TWOdevice *);
|
||||
|
||||
/* twosolve.c */
|
||||
extern void TWOdcSolve(TWOdevice *, int, BOOLEAN, BOOLEAN, TWOtranInfo *);
|
||||
extern BOOLEAN TWOdeltaConverged(TWOdevice *);
|
||||
extern BOOLEAN TWOdeviceConverged(TWOdevice *);
|
||||
extern void TWOresetJacobian(TWOdevice *);
|
||||
extern void TWOstoreNeutralGuess(TWOdevice *);
|
||||
extern void TWOequilSolve(TWOdevice *);
|
||||
extern void TWObiasSolve(TWOdevice *, int, BOOLEAN, TWOtranInfo *);
|
||||
extern void TWOstoreEquilibGuess(TWOdevice *);
|
||||
extern void TWOstoreInitialGuess(TWOdevice *);
|
||||
extern void oldTWOnewDelta(TWOdevice *, BOOLEAN, TWOtranInfo *);
|
||||
extern int TWOnewDelta(TWOdevice *, BOOLEAN, TWOtranInfo *);
|
||||
extern void TWOpredict(TWOdevice *, TWOtranInfo *);
|
||||
extern double TWOtrunc(TWOdevice *, TWOtranInfo *, double);
|
||||
extern void TWOsaveState(TWOdevice *);
|
||||
extern BOOLEAN TWOpsiDeltaConverged(TWOdevice *);
|
||||
extern double TWOnuNorm(TWOdevice *);
|
||||
extern void TWOjacCheck(TWOdevice *, BOOLEAN, TWOtranInfo *);
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,234 @@
|
|||
/**********
|
||||
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
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numglobs.h"
|
||||
#include "numenum.h"
|
||||
#include "twomesh.h"
|
||||
#include "twodev.h"
|
||||
#include "profile.h"
|
||||
#include "macros.h"
|
||||
#include "bool.h"
|
||||
#include "twoddefs.h"
|
||||
#include "twodext.h"
|
||||
#include "cidersupt.h"
|
||||
|
||||
/* functions in this file are used to calculate the conc */
|
||||
|
||||
double
|
||||
TWOdopingValue(DOPprofile *pProfile, DOPtable *pTable, double x,
|
||||
double y)
|
||||
{
|
||||
double argX, argY, argP, argL, value = 0.0;
|
||||
|
||||
/* Find the appropriate lookup table if necessary */
|
||||
if (pProfile->type == LOOKUP) {
|
||||
while ( pTable != NIL(DOPtable) ) {
|
||||
if (pTable->impId == pProfile->IMPID) {
|
||||
/* Found it */
|
||||
break;
|
||||
} else {
|
||||
pTable = pTable->next;
|
||||
}
|
||||
}
|
||||
if ( pTable == NIL(DOPtable) ) {
|
||||
fprintf( stderr, "Error: unknown impurity profile %d\n",
|
||||
((int)pProfile->IMPID) );
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
/* Find distances */
|
||||
if ( pProfile->Y_LOW > y ) {
|
||||
argY = pProfile->Y_LOW - y;
|
||||
} else if ( y > pProfile->Y_HIGH ) {
|
||||
argY = y - pProfile->Y_HIGH;
|
||||
} else {
|
||||
argY = 0.0;
|
||||
}
|
||||
if ( pProfile->X_LOW > x ) {
|
||||
argX = pProfile->X_LOW - x;
|
||||
} else if ( x > pProfile->X_HIGH ) {
|
||||
argX = x - pProfile->X_HIGH;
|
||||
} else {
|
||||
argX = 0.0;
|
||||
}
|
||||
|
||||
if ( pProfile->DIRECTION == Y ) {
|
||||
argP = argY;
|
||||
argL = argX / pProfile->LAT_RATIO;
|
||||
}
|
||||
else {
|
||||
argP = argX;
|
||||
argL = argY / pProfile->LAT_RATIO;
|
||||
}
|
||||
if ( pProfile->rotate ) {
|
||||
argP = sqrt(argP*argP + argL*argL);
|
||||
argL = 0.0;
|
||||
}
|
||||
|
||||
/* Transform to coordinates of profile peak */
|
||||
argP -= pProfile->LOCATION;
|
||||
argP /= pProfile->CHAR_LENGTH;
|
||||
argL -= pProfile->LOCATION;
|
||||
argL /= pProfile->CHAR_LENGTH;
|
||||
|
||||
switch (pProfile->type) {
|
||||
case UNIF:
|
||||
if (argP > 0.0) {
|
||||
value = 0.0;
|
||||
} else {
|
||||
value = pProfile->CONC;
|
||||
}
|
||||
break;
|
||||
case LIN:
|
||||
argP = ABS(argP);
|
||||
if (argP > 1.0) {
|
||||
value = 0.0;
|
||||
} else {
|
||||
value = pProfile->CONC * ( 1.0 - argP );
|
||||
}
|
||||
break;
|
||||
case GAUSS:
|
||||
argP *= argP;
|
||||
if ( argP > 80.0 ) {
|
||||
value = 0.0;
|
||||
} else {
|
||||
value = pProfile->PEAK_CONC * exp( -argP );
|
||||
}
|
||||
case EXP:
|
||||
argP = ABS(argP);
|
||||
if ( argP > 80.0 ) {
|
||||
value = 0.0;
|
||||
} else {
|
||||
value = pProfile->PEAK_CONC * exp( -argP );
|
||||
}
|
||||
break;
|
||||
case ERRFC:
|
||||
argP = ABS(argP);
|
||||
if ( argP > 10.0 ) {
|
||||
value = 0.0;
|
||||
} else {
|
||||
value = pProfile->PEAK_CONC * erfc( -argP );
|
||||
}
|
||||
break;
|
||||
case LOOKUP:
|
||||
argP = ABS(argP);
|
||||
value = lookup( pTable->dopData, argP );
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!pProfile->rotate) { /* Tensor product in lateral direction */
|
||||
switch (pProfile->latType) {
|
||||
case UNIF:
|
||||
if (argL > 0.0) {
|
||||
value = 0.0;
|
||||
}
|
||||
break;
|
||||
case LIN:
|
||||
argL = ABS(argL);
|
||||
if (argL > 1.0) {
|
||||
value = 0.0;
|
||||
} else {
|
||||
value *= ( 1.0 - argL );
|
||||
}
|
||||
break;
|
||||
case GAUSS:
|
||||
argL *= argL;
|
||||
if ( argL > 80.0 ) {
|
||||
value = 0.0;
|
||||
} else {
|
||||
value *= exp( -argL );
|
||||
}
|
||||
case EXP:
|
||||
argL = ABS(argL);
|
||||
if ( argL > 80.0 ) {
|
||||
value = 0.0;
|
||||
} else {
|
||||
value *= exp( -argL );
|
||||
}
|
||||
break;
|
||||
case ERRFC:
|
||||
argL = ABS(argL);
|
||||
if ( argP > 10.0 ) {
|
||||
value = 0.0;
|
||||
} else {
|
||||
value *= erfc( -argL );
|
||||
}
|
||||
break;
|
||||
case LOOKUP:
|
||||
argL = ABS(argL);
|
||||
value *= lookup( pTable->dopData, argL ) / lookup( pTable->dopData, 0.0 );
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} /* end: not rotated */
|
||||
return( value );
|
||||
}
|
||||
|
||||
void
|
||||
TWOsetDoping(TWOdevice *pDevice, DOPprofile *pProfile, DOPtable *pTable)
|
||||
{
|
||||
TWOnode *pNode;
|
||||
TWOelem *pElem;
|
||||
DOPprofile *pP;
|
||||
double conc;
|
||||
int index, eIndex;
|
||||
BOOLEAN dopeMe;
|
||||
|
||||
/* Clear doping info for all nodes. */
|
||||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
|
||||
pElem = pDevice->elements[ eIndex ];
|
||||
for (index = 0; index <= 3; index++) {
|
||||
if (pElem->evalNodes[index]) {
|
||||
pNode = pElem->pNodes[index];
|
||||
pNode->na = 0.0;
|
||||
pNode->nd = 0.0;
|
||||
pNode->netConc = 0.0;
|
||||
pNode->totalConc = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Now compute the contribution to the total doping from each profile. */
|
||||
for ( pP = pProfile; pP != NIL(DOPprofile); pP = pP->next ) {
|
||||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
|
||||
pElem = pDevice->elements[ eIndex ];
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
if ( pP->numDomains > 0 ) {
|
||||
dopeMe = FALSE;
|
||||
for ( index = 0; index < pP->numDomains; index++ ) {
|
||||
if ( pElem->domain == pP->domains[ index ] ) {
|
||||
dopeMe = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else { /* domains not given, so dope all */
|
||||
dopeMe = TRUE;
|
||||
}
|
||||
if ( dopeMe ) {
|
||||
for ( index = 0; index <= 3; index++ ) {
|
||||
if ( pElem->evalNodes[ index ] ) {
|
||||
pNode = pElem->pNodes[ index ];
|
||||
conc = TWOdopingValue( pP, pTable,
|
||||
pDevice->xScale[ pNode->nodeI ],
|
||||
pDevice->yScale[ pNode->nodeJ ] );
|
||||
pNode->netConc += conc;
|
||||
if ( conc < 0.0 ) {
|
||||
pNode->totalConc -= conc;
|
||||
pNode->na -= conc;
|
||||
}
|
||||
else {
|
||||
pNode->totalConc += conc;
|
||||
pNode->nd += conc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,180 @@
|
|||
/**********
|
||||
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
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numglobs.h"
|
||||
#include "numconst.h"
|
||||
#include "numenum.h"
|
||||
#include "twomesh.h"
|
||||
#include "twodev.h"
|
||||
#include "bool.h"
|
||||
#include "twoddefs.h"
|
||||
#include "twodext.h"
|
||||
|
||||
/* Use list-sorting header file to create electrode sorter */
|
||||
#define TYPE TWOelectrode
|
||||
#define NEXT next
|
||||
#define SORT TWOssortElectrodes
|
||||
#define SORT1 TWOsortElectrodes
|
||||
#include "lsort.h"
|
||||
|
||||
#define ARG_MIN(a,b,c) ((a) > (b) ? 1 : ((a) < (b) ? -1 : (c)))
|
||||
|
||||
int
|
||||
TWOcmpElectrode(TWOelectrode *a, TWOelectrode *b)
|
||||
{
|
||||
return ARG_MIN(a->id, b->id, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* checkElectrodes
|
||||
* debug list of electrodes for errors, exit on any error:
|
||||
* 1. list doesn't have only contiguous id's from 1 to idHigh
|
||||
* 2. electrodes with box shapes
|
||||
* 3. order of node numbers is correct
|
||||
*/
|
||||
void
|
||||
checkElectrodes(TWOelectrode *pElectrode, int idHigh)
|
||||
{
|
||||
TWOelectrode *pE;
|
||||
int id;
|
||||
BOOLEAN error = FALSE;
|
||||
|
||||
/*
|
||||
* order the electrodes
|
||||
* assign electrode numbers to uninitialized electrodes ( id == -1 )
|
||||
* uninit electrodes are in reverse order from order in input file
|
||||
* resort electrodes
|
||||
*/
|
||||
pElectrode = TWOsortElectrodes( pElectrode, TWOcmpElectrode );
|
||||
id = 1;
|
||||
for (pE = pElectrode; pE != NIL(TWOelectrode); pE = pE->next) {
|
||||
if (pE->id == -1) pE->id = id++;
|
||||
}
|
||||
pElectrode = TWOsortElectrodes( pElectrode, TWOcmpElectrode );
|
||||
|
||||
for (pE = pElectrode, id = 1; pE != NIL(TWOelectrode); pE = pE->next) {
|
||||
/* Check id's */
|
||||
if ( pE->id < 1 || pE->id > idHigh) {
|
||||
fprintf(stderr, "Error: electrode %d out of range\n",pE->id);
|
||||
error = TRUE;
|
||||
} else if ( pE->id != id ) {
|
||||
if ( pE->id != ++id ) {
|
||||
fprintf(stderr, "Error: electrode(s) %d to %d missing\n",id,pE->id-1);
|
||||
id = pE->id;
|
||||
error = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Make sure total number of distinct electrodes is correct */
|
||||
if ( id != idHigh ) {
|
||||
fprintf(stderr, "Error: %d electrode%s not equal to %d required\n",
|
||||
id, (id != 1) ? "s are" : " is", idHigh);
|
||||
error = TRUE;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* setupContacts
|
||||
* go through each set of electrode segments, find its size and find nodes
|
||||
*/
|
||||
|
||||
|
||||
void
|
||||
setupContacts(TWOdevice *pDevice, TWOelectrode *pElectrode,
|
||||
TWOnode ***nodeArray)
|
||||
{
|
||||
TWOelectrode *pE;
|
||||
TWOcontact *pNew = NULL, *pTail;
|
||||
TWOnode *pNode;
|
||||
int xIndex, yIndex, nIndex = 0;
|
||||
int id = 0, electrodeSize[MAXTERMINALS], electrodeType;
|
||||
int error = FALSE;
|
||||
|
||||
/* INITIALIZATION
|
||||
* 0. Assume ELCT_ID's are initialized to 0 before setup is called
|
||||
* 1. Add a node only once to only one electrode
|
||||
* 2. Compute number of nodes in each electrode
|
||||
* 3. Overwrite SEMICON or INSULATOR nodeType at electrodes
|
||||
*/
|
||||
for ( pE = pElectrode; pE != NIL(TWOelectrode); pE = pE->next ) {
|
||||
if (pE->id != id) { /* Found the next electrode */
|
||||
id = pE->id;
|
||||
electrodeSize[id] = 0;
|
||||
electrodeType = 0;
|
||||
}
|
||||
for ( xIndex = pE->ixLo; xIndex <= pE->ixHi; xIndex++ ) {
|
||||
for ( yIndex = pE->iyLo; yIndex <= pE->iyHi; yIndex++ ) {
|
||||
pNode = nodeArray[ xIndex ][ yIndex ];
|
||||
if ( pNode != NIL( TWOnode ) ) {
|
||||
pNode->nodeType = CONTACT;
|
||||
|
||||
/* Assign each node to an electrode only once */
|
||||
if (pNode->ELCT_ID == 0) { /* Is this a new node? */
|
||||
pNode->ELCT_ID = id; /* Mark electrode ownership */
|
||||
electrodeSize[id]++; /* Increase electrode size */
|
||||
} else if (pNode->ELCT_ID != id) {
|
||||
/* Node already owned by another electrode */
|
||||
fprintf(stderr, "Error: electrodes %d and %d overlap at (%d,%d)\n",
|
||||
pNode->ELCT_ID, id, xIndex, yIndex);
|
||||
error = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (error) {
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
/* ALLOCATION AND NODE GRABBING
|
||||
* 1. For each electrode, create a new contact structure for the electrode.
|
||||
* 2. Fill in entries of contact structure
|
||||
* 3. Update First and Last Contact pointers of device
|
||||
*/
|
||||
/*
|
||||
printElectrodes( pDevice->pFirstContact );
|
||||
*/
|
||||
id = 0;
|
||||
pDevice->pFirstContact = pTail = NIL(TWOcontact);
|
||||
for ( pE = pElectrode; pE != NIL(TWOelectrode); pE = pE->next ) {
|
||||
if (pE->id != id) { /* Found the next electrode */
|
||||
if ( pDevice->pFirstContact == NIL(TWOcontact) ) {
|
||||
XCALLOC( pNew, TWOcontact, 1 );
|
||||
pDevice->pFirstContact = pTail = pNew;
|
||||
} else {
|
||||
XCALLOC( pNew->next, TWOcontact, 1 );
|
||||
pTail = pNew = pNew->next;
|
||||
}
|
||||
pNew->next = NIL(TWOcontact);
|
||||
id = pNew->id = pE->id;
|
||||
pNew->workf = pE->workf;
|
||||
pNew->numNodes = electrodeSize[id];
|
||||
nIndex = 0;
|
||||
XCALLOC( pNew->pNodes, TWOnode *, electrodeSize[id] );
|
||||
}
|
||||
for ( xIndex = pE->ixLo; xIndex <= pE->ixHi; xIndex++ ) {
|
||||
for ( yIndex = pE->iyLo; yIndex <= pE->iyHi; yIndex++ ) {
|
||||
pNode = nodeArray[ xIndex ][ yIndex ];
|
||||
if ( pNode != NIL( TWOnode ) ) {
|
||||
/* Make sure node belongs to this electrode, then
|
||||
* clear ELCT_ID so that it is not grabbed again.
|
||||
*/
|
||||
if (pNode->ELCT_ID == id) {
|
||||
pNew->pNodes[ nIndex++ ] = pNode;
|
||||
pNode->ELCT_ID = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pDevice->pLastContact = pTail;
|
||||
}
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
/**********
|
||||
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
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numglobs.h"
|
||||
#include "numenum.h"
|
||||
#include "twomesh.h"
|
||||
#include "twoddefs.h"
|
||||
#include "twodext.h"
|
||||
|
||||
void
|
||||
nodeFields(TWOelem *pElem, TWOnode *pNode, double *ex, double *ey)
|
||||
{
|
||||
|
||||
TWOelem *pElemTL, *pElemTR, *pElemBL, *pElemBR;
|
||||
TWOedge *pEdgeT, *pEdgeB, *pEdgeL, *pEdgeR;
|
||||
int materT, materB, materL, materR;
|
||||
double dxL = 0.0, dxR = 0.0, dyT = 0.0, dyB = 0.0;
|
||||
double ef1, ef2, coeff1, coeff2;
|
||||
|
||||
/* Find all four neighboring elements */
|
||||
pElemTL = pNode->pTLElem;
|
||||
pElemTR = pNode->pTRElem;
|
||||
pElemBL = pNode->pBLElem;
|
||||
pElemBR = pNode->pBRElem;
|
||||
|
||||
/* Null edge pointers */
|
||||
pEdgeT = pEdgeB = pEdgeL = pEdgeR = NIL(TWOedge);
|
||||
|
||||
/* Find edges next to node */
|
||||
if (pElemTL != NIL(TWOelem)) {
|
||||
if (pElemTL->evalEdges[1]) {
|
||||
pEdgeT = pElemTL->pRightEdge;
|
||||
materT = pElemTL->elemType;
|
||||
dyT = pElemTL->dy;
|
||||
}
|
||||
if (pElemTL->evalEdges[2]) {
|
||||
pEdgeL = pElemTL->pBotEdge;
|
||||
materL = pElemTL->elemType;
|
||||
dxL = pElemTL->dx;
|
||||
}
|
||||
}
|
||||
if (pElemTR != NIL(TWOelem)) {
|
||||
if (pElemTR->evalEdges[3]) {
|
||||
pEdgeT = pElemTR->pLeftEdge;
|
||||
materT = pElemTR->elemType;
|
||||
dyT = pElemTR->dy;
|
||||
}
|
||||
if (pElemTR->evalEdges[2]) {
|
||||
pEdgeR = pElemTR->pBotEdge;
|
||||
materR = pElemTR->elemType;
|
||||
dxR = pElemTR->dx;
|
||||
}
|
||||
}
|
||||
if (pElemBR != NIL(TWOelem)) {
|
||||
if (pElemBR->evalEdges[3]) {
|
||||
pEdgeB = pElemBR->pLeftEdge;
|
||||
materB = pElemBR->elemType;
|
||||
dyB = pElemBR->dy;
|
||||
}
|
||||
if (pElemBR->evalEdges[0]) {
|
||||
pEdgeR = pElemBR->pTopEdge;
|
||||
materR = pElemBR->elemType;
|
||||
dxR = pElemBR->dx;
|
||||
}
|
||||
}
|
||||
if (pElemBL != NIL(TWOelem)) {
|
||||
if (pElemBL->evalEdges[1]) {
|
||||
pEdgeB = pElemBL->pRightEdge;
|
||||
materB = pElemBL->elemType;
|
||||
dyB = pElemBL->dy;
|
||||
}
|
||||
if (pElemBL->evalEdges[0]) {
|
||||
pEdgeL = pElemBL->pTopEdge;
|
||||
materL = pElemBL->elemType;
|
||||
dxL = pElemBL->dx;
|
||||
}
|
||||
}
|
||||
/* compute horizontal vector components */
|
||||
/* No more than one of Left Edge or Right Edge is absent */
|
||||
if (pEdgeL == NIL(TWOedge)) {
|
||||
if (pNode->nodeType == CONTACT) {
|
||||
*ex = -pEdgeR->dPsi / dxR;
|
||||
} else {
|
||||
*ex = 0.0;
|
||||
}
|
||||
} else if (pEdgeR == NIL(TWOedge)) {
|
||||
if (pNode->nodeType == CONTACT) {
|
||||
*ex = -pEdgeL->dPsi / dxL;
|
||||
} else {
|
||||
*ex = 0.0;
|
||||
}
|
||||
} else { /* Both edges are present */
|
||||
coeff1 = dxL / (dxL + dxR);
|
||||
coeff2 = dxR / (dxL + dxR);
|
||||
ef1 = -pEdgeL->dPsi / dxL;
|
||||
ef2 = -pEdgeR->dPsi / dxR;
|
||||
*ex = coeff2 * ef1 + coeff1 * ef2;
|
||||
}
|
||||
|
||||
/* compute vertical vector components */
|
||||
/* No more than one of Top Edge or Bottom Edge is absent */
|
||||
if (pEdgeT == NIL(TWOedge)) {
|
||||
if (pNode->nodeType == CONTACT) {
|
||||
*ey = -pEdgeB->dPsi / dyB;
|
||||
} else {
|
||||
*ey = 0.0;
|
||||
}
|
||||
} else if (pEdgeB == NIL(TWOedge)) {
|
||||
if (pNode->nodeType == CONTACT) {
|
||||
*ey = -pEdgeT->dPsi / dyT;
|
||||
} else {
|
||||
*ey = 0.0;
|
||||
}
|
||||
} else { /* Both edges are present */
|
||||
coeff1 = dyT / (dyT + dyB);
|
||||
coeff2 = dyB / (dyT + dyB);
|
||||
ef1 = -pEdgeT->dPsi / dyT;
|
||||
ef2 = -pEdgeB->dPsi / dyB;
|
||||
*ey = coeff2 * ef1 + coeff1 * ef2;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,626 @@
|
|||
/**********
|
||||
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
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numglobs.h"
|
||||
#include "numconst.h"
|
||||
#include "numenum.h"
|
||||
#include "twomesh.h"
|
||||
#include "twodev.h"
|
||||
#include "bool.h"
|
||||
#include "twoddefs.h"
|
||||
#include "twodext.h"
|
||||
|
||||
|
||||
|
||||
static void doMobCoeffs(TWOelem *, int);
|
||||
static void resetEvalFlag(TWOdevice *pDevice);
|
||||
|
||||
|
||||
void
|
||||
TWObuildMesh(TWOdevice *pDevice, TWOdomain *pDomain,
|
||||
TWOelectrode *pElectrode, TWOmaterial *pMaterial)
|
||||
{
|
||||
int xIndex, yIndex, eIndex, index;
|
||||
int elemType;
|
||||
TWOcoord *pX, *pY;
|
||||
TWOelem *pElem, *pElem1;
|
||||
TWOnode *pNode, *pNode1, *pNextHNode, *pNextVNode, *pNextDNode;
|
||||
TWOnode ***nodeArray = NULL;
|
||||
TWOedge *pEdge;
|
||||
TWOedge ***edgeArrayH = NULL, ***edgeArrayV = NULL;
|
||||
TWOdomain *pD;
|
||||
TWOelectrode *pE;
|
||||
TWOmaterial *pM;
|
||||
BOOLEAN error = FALSE;
|
||||
BOOLEAN interiorNode;
|
||||
int poiEqn, numEqn, numElem, numNodes, numEdges;
|
||||
int numXNodes = pDevice->numXNodes;
|
||||
int numYNodes = pDevice->numYNodes;
|
||||
double *xScale = pDevice->xScale;
|
||||
double *yScale = pDevice->yScale;
|
||||
FILE *meshFile;
|
||||
|
||||
/* Generate work arrays. */
|
||||
XCALLOC(nodeArray, TWOnode **, 1 + numXNodes);
|
||||
for (xIndex = 1; xIndex <= numXNodes; xIndex++) {
|
||||
XCALLOC(nodeArray[xIndex], TWOnode *, 1 + numYNodes);
|
||||
}
|
||||
|
||||
/* Generate the nodes. */
|
||||
for (xIndex = 1; xIndex <= numXNodes; xIndex++) {
|
||||
for (yIndex = 1; yIndex <= numYNodes; yIndex++) {
|
||||
XCALLOC(pNode, TWOnode, 1);
|
||||
pNode->nodeI = xIndex;
|
||||
pNode->nodeJ = yIndex;
|
||||
pNode->poiEqn = 0;
|
||||
nodeArray[xIndex][yIndex] = pNode;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mark the semiconductor/insulator domains. */
|
||||
if (pDomain == NIL(TWOdomain)) {
|
||||
fprintf(stderr, "Error: domains not defined for device\n");
|
||||
exit(-1);
|
||||
}
|
||||
for (pD = pDomain; pD != NIL(TWOdomain); pD = pD->next) {
|
||||
for (pM = pMaterial; pM != NIL(TWOmaterial); pM = pM->next) {
|
||||
if (pD->material == pM->id) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
elemType = pM->type;
|
||||
for (xIndex = pD->ixLo; xIndex <= pD->ixHi; xIndex++) {
|
||||
for (yIndex = pD->iyLo; yIndex <= pD->iyHi; yIndex++) {
|
||||
pNode = nodeArray[xIndex][yIndex];
|
||||
pNode->nodeType = elemType;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Now mark all the metallic domains */
|
||||
for (pE = pElectrode; pE != NIL(TWOelectrode); pE = pE->next) {
|
||||
for (xIndex = pE->ixLo; xIndex <= pE->ixHi; xIndex++) {
|
||||
for (yIndex = pE->iyLo; yIndex <= pE->iyHi; yIndex++) {
|
||||
pNode = nodeArray[xIndex][yIndex];
|
||||
pNode->nodeType = CONTACT;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Now mark as NULL any node in the interior of an electrode, i.e. none of
|
||||
* its neighbors is a different material.
|
||||
*/
|
||||
for (xIndex = 1; xIndex <= numXNodes; xIndex++) {
|
||||
for (yIndex = 1; yIndex <= numYNodes; yIndex++) {
|
||||
pNode = nodeArray[xIndex][yIndex];
|
||||
if (pNode->nodeType == CONTACT) {
|
||||
interiorNode = TRUE;
|
||||
if (xIndex > 1) {
|
||||
pNode1 = nodeArray[xIndex - 1][yIndex];
|
||||
if (pNode1->nodeType != NULL
|
||||
&& pNode1->nodeType != CONTACT) {
|
||||
interiorNode = FALSE;
|
||||
}
|
||||
}
|
||||
if (interiorNode && xIndex < numXNodes) {
|
||||
pNode1 = nodeArray[xIndex + 1][yIndex];
|
||||
if (pNode1->nodeType != NULL
|
||||
&& pNode1->nodeType != CONTACT) {
|
||||
interiorNode = FALSE;
|
||||
}
|
||||
}
|
||||
if (interiorNode && yIndex > 1) {
|
||||
pNode1 = nodeArray[xIndex][yIndex - 1];
|
||||
if (pNode1->nodeType != NULL
|
||||
&& pNode1->nodeType != CONTACT) {
|
||||
interiorNode = FALSE;
|
||||
}
|
||||
}
|
||||
if (interiorNode && yIndex < numYNodes) {
|
||||
pNode1 = nodeArray[xIndex][yIndex + 1];
|
||||
if (pNode1->nodeType != NULL
|
||||
&& pNode1->nodeType != CONTACT) {
|
||||
interiorNode = FALSE;
|
||||
}
|
||||
}
|
||||
if (interiorNode) {
|
||||
pNode->nodeType = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Delete nodes that do not belong to any domain. */
|
||||
numNodes = 0;
|
||||
for (yIndex = 1; yIndex <= numYNodes; yIndex++) {
|
||||
for (xIndex = 1; xIndex <= numXNodes; xIndex++) {
|
||||
pNode = nodeArray[xIndex][yIndex];
|
||||
if (pNode->nodeType == NULL) {
|
||||
/* This node doesn't belong to a domain so delete it. */
|
||||
nodeArray[xIndex][yIndex] = NIL(TWOnode);
|
||||
FREE(pNode);
|
||||
} else {
|
||||
numNodes++;
|
||||
}
|
||||
}
|
||||
}
|
||||
pDevice->numNodes = numNodes;
|
||||
|
||||
/* Now relabel any remaining nodes that are part of electrodes. */
|
||||
setupContacts(pDevice, pElectrode, nodeArray);
|
||||
|
||||
/* Done with the nodes. Now construct the elements and the edges. */
|
||||
numEdges = 0;
|
||||
|
||||
/* Generate the horizontal edges and store in a work array. */
|
||||
XCALLOC(edgeArrayH, TWOedge **, numXNodes);
|
||||
for (xIndex = 1; xIndex < numXNodes; xIndex++) {
|
||||
XCALLOC(edgeArrayH[xIndex], TWOedge *, 1 + numYNodes);
|
||||
}
|
||||
for (yIndex = 1; yIndex <= numYNodes; yIndex++) {
|
||||
for (xIndex = 1; xIndex < numXNodes; xIndex++) {
|
||||
pNode = nodeArray[xIndex][yIndex];
|
||||
pNextHNode = nodeArray[xIndex + 1][yIndex];
|
||||
if (pNode != NIL(TWOnode) &&
|
||||
pNextHNode != NIL(TWOnode)) {
|
||||
XCALLOC(pEdge, TWOedge, 1);
|
||||
numEdges++;
|
||||
edgeArrayH[xIndex][yIndex] = pEdge;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Generate the vertical edges and store in a work array. */
|
||||
XCALLOC(edgeArrayV, TWOedge **, 1 + numXNodes);
|
||||
for (xIndex = 1; xIndex <= numXNodes; xIndex++) {
|
||||
XCALLOC(edgeArrayV[xIndex], TWOedge *, numYNodes);
|
||||
}
|
||||
for (xIndex = 1; xIndex <= numXNodes; xIndex++) {
|
||||
for (yIndex = 1; yIndex < numYNodes; yIndex++) {
|
||||
pNode = nodeArray[xIndex][yIndex];
|
||||
pNextVNode = nodeArray[xIndex][yIndex + 1];
|
||||
if (pNode != NIL(TWOnode) &&
|
||||
pNextVNode != NIL(TWOnode)) {
|
||||
XCALLOC(pEdge, TWOedge, 1);
|
||||
numEdges++;
|
||||
edgeArrayV[xIndex][yIndex] = pEdge;
|
||||
}
|
||||
}
|
||||
}
|
||||
pDevice->numEdges = numEdges;
|
||||
|
||||
/* Now construct and count the elements and store the node/edge pointers. */
|
||||
numElem = 1;
|
||||
for (yIndex = 1; yIndex < numYNodes; yIndex++) {
|
||||
for (xIndex = 1; xIndex < numXNodes; xIndex++) {
|
||||
pNode = nodeArray[xIndex][yIndex];
|
||||
pNextHNode = nodeArray[xIndex + 1][yIndex];
|
||||
pNextVNode = nodeArray[xIndex][yIndex + 1];
|
||||
pNextDNode = nodeArray[xIndex + 1][yIndex + 1];
|
||||
if (pNode != NIL(TWOnode) &&
|
||||
pNextHNode != NIL(TWOnode) &&
|
||||
pNextVNode != NIL(TWOnode) &&
|
||||
pNextDNode != NIL(TWOnode)) {
|
||||
numElem++;
|
||||
XCALLOC(pElem, TWOelem, 1);
|
||||
pElem->pTLNode = pNode;
|
||||
pElem->pTRNode = pNextHNode;
|
||||
pElem->pBLNode = pNextVNode;
|
||||
pElem->pBRNode = pNextDNode;
|
||||
pElem->pTopEdge = edgeArrayH[xIndex][yIndex];
|
||||
pElem->pBotEdge = edgeArrayH[xIndex][yIndex + 1];
|
||||
pElem->pLeftEdge = edgeArrayV[xIndex][yIndex];
|
||||
pElem->pRightEdge = edgeArrayV[xIndex + 1][yIndex];
|
||||
pDevice->elemArray[xIndex][yIndex] = pElem;
|
||||
} else {
|
||||
pDevice->elemArray[xIndex][yIndex] = NIL(TWOelem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Create and pack the 1D element array. */
|
||||
pDevice->numElems = numElem - 1;
|
||||
XCALLOC(pDevice->elements, TWOelem *, 1 + numElem);
|
||||
numElem = 1;
|
||||
for (yIndex = 1; yIndex < numYNodes; yIndex++) {
|
||||
for (xIndex = 1; xIndex < numXNodes; xIndex++) {
|
||||
pElem = pDevice->elemArray[xIndex][yIndex];
|
||||
if (pElem != NIL(TWOelem)) {
|
||||
pDevice->elements[numElem++] = pElem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Now create back links from elements to nodes. */
|
||||
for (yIndex = 1; yIndex < numYNodes; yIndex++) {
|
||||
for (xIndex = 1; xIndex < numXNodes; xIndex++) {
|
||||
pElem = pDevice->elemArray[xIndex][yIndex];
|
||||
if (pElem != NIL(TWOelem)) {
|
||||
pElem->pTLNode->pBRElem = pElem;
|
||||
pElem->pTRNode->pBLElem = pElem;
|
||||
pElem->pBLNode->pTRElem = pElem;
|
||||
pElem->pBRNode->pTLElem = pElem;
|
||||
if (xIndex > 1) {
|
||||
pElem->pLeftElem = pDevice->elemArray[xIndex-1][yIndex];
|
||||
}
|
||||
if (xIndex < numXNodes - 1) {
|
||||
pElem->pRightElem = pDevice->elemArray[xIndex+1][yIndex];
|
||||
}
|
||||
if (yIndex > 1) {
|
||||
pElem->pTopElem = pDevice->elemArray[xIndex][yIndex-1];
|
||||
}
|
||||
if (yIndex < numYNodes - 1) {
|
||||
pElem->pBotElem = pDevice->elemArray[xIndex][yIndex+1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Establish the element types using domain info. */
|
||||
for (pD = pDomain; pD != NIL(TWOdomain); pD = pD->next) {
|
||||
for (pM = pMaterial; pM != NIL(TWOmaterial); pM = pM->next) {
|
||||
if (pD->material == pM->id) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
elemType = pM->type;
|
||||
for (yIndex = pD->iyLo; yIndex < pD->iyHi; yIndex++) {
|
||||
for (xIndex = pD->ixLo; xIndex < pD->ixHi; xIndex++) {
|
||||
pElem = pDevice->elemArray[xIndex][yIndex];
|
||||
if (pElem != NIL(TWOelem)) {
|
||||
pElem->domain = pD->id;
|
||||
pElem->elemType = elemType;
|
||||
pElem->matlInfo = pM;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Establish the edge types. */
|
||||
for (yIndex = 1; yIndex < numYNodes; yIndex++) {
|
||||
for (xIndex = 1; xIndex < numXNodes; xIndex++) {
|
||||
pElem = pDevice->elemArray[xIndex][yIndex];
|
||||
if (pElem != NIL(TWOelem)) {
|
||||
for (index = 0; index <= 3; index++) {
|
||||
pEdge = pElem->pEdges[index];
|
||||
pNode = pElem->pNodes[index];
|
||||
pNode1 = pElem->pNodes[(index + 1) % 4];
|
||||
pElem1 = pNode1->pElems[index];
|
||||
|
||||
if (pNode->nodeType == CONTACT &&
|
||||
pNode1->nodeType == CONTACT) {
|
||||
/* Contact edge */
|
||||
pEdge->edgeType = CONTACT;
|
||||
} else if (pNode->nodeType == SCHOTTKY &&
|
||||
pNode1->nodeType == SCHOTTKY) {
|
||||
/* Schottky edge */
|
||||
pEdge->edgeType = SCHOTTKY;
|
||||
} else if (pElem1 == NIL(TWOelem)) {
|
||||
/* Neumann edge */
|
||||
pEdge->edgeType = pElem->elemType;
|
||||
} else if (pElem->elemType != pElem1->elemType) {
|
||||
/* Interface edge */
|
||||
pEdge->edgeType = INTERFACE;
|
||||
} else { /* Ignore heterojnxns for now */
|
||||
/* Heterojunction or Homojunction edge */
|
||||
pEdge->edgeType = pElem->elemType;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resetEvalFlag(pDevice);
|
||||
/* Set evaluation flags on nodes and edges. */
|
||||
/* Assign the dx and dy terms in the elements while we're at it. */
|
||||
for (yIndex = 1; yIndex < numYNodes; yIndex++) {
|
||||
for (xIndex = 1; xIndex < numXNodes; xIndex++) {
|
||||
pElem = pDevice->elemArray[xIndex][yIndex];
|
||||
if (pElem != NIL(TWOelem)) {
|
||||
pElem->dx = xScale[xIndex + 1] - xScale[xIndex];
|
||||
pElem->dy = yScale[yIndex + 1] - yScale[yIndex];
|
||||
pElem->dxOverDy = pElem->dx / pElem->dy;
|
||||
pElem->dyOverDx = pElem->dy / pElem->dx;
|
||||
|
||||
/*
|
||||
* Semiconductor elements take precedence over Insulator elements, so
|
||||
* set them up first.
|
||||
*/
|
||||
for (index = 0; index <= 3; index++) {
|
||||
if (pElem->elemType == SEMICON) {
|
||||
pNode = pElem->pNodes[index];
|
||||
if (!pNode->evaluated) {
|
||||
pNode->evaluated = TRUE;
|
||||
pElem->evalNodes[index] = TRUE;
|
||||
} else {
|
||||
pElem->evalNodes[index] = FALSE;
|
||||
}
|
||||
pEdge = pElem->pEdges[index];
|
||||
if (!pEdge->evaluated) {
|
||||
pEdge->evaluated = TRUE;
|
||||
pElem->evalEdges[index] = TRUE;
|
||||
} else {
|
||||
pElem->evalEdges[index] = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Do a second setup pass for Insulator elements */
|
||||
/* Do mobility coefficients now, because we set up dx and dy
|
||||
* in the previous pass
|
||||
*/
|
||||
for (yIndex = 1; yIndex < numYNodes; yIndex++) {
|
||||
for (xIndex = 1; xIndex < numXNodes; xIndex++) {
|
||||
pElem = pDevice->elemArray[xIndex][yIndex];
|
||||
if (pElem != NIL(TWOelem)) {
|
||||
pElem->direction = 0;
|
||||
pElem->channel = 0;
|
||||
pElem->surface = FALSE;
|
||||
for (index = 0; index <= 3; index++) {
|
||||
if (pElem->elemType == SEMICON) {
|
||||
doMobCoeffs( pElem, index );
|
||||
} else if (pElem->elemType == INSULATOR) {
|
||||
pNode = pElem->pNodes[index];
|
||||
if (!pNode->evaluated) {
|
||||
pNode->evaluated = TRUE;
|
||||
pElem->evalNodes[index] = TRUE;
|
||||
} else {
|
||||
pElem->evalNodes[index] = FALSE;
|
||||
}
|
||||
pEdge = pElem->pEdges[index];
|
||||
if (!pEdge->evaluated) {
|
||||
pEdge->evaluated = TRUE;
|
||||
pElem->evalEdges[index] = TRUE;
|
||||
} else {
|
||||
pElem->evalEdges[index] = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Set up the equation numbers for nodes. */
|
||||
poiEqn = numEqn = 1;
|
||||
for (eIndex = 1; eIndex <= pDevice->numElems; eIndex++) {
|
||||
pElem = pDevice->elements[eIndex];
|
||||
for (index = 0; index <= 3; index++) {
|
||||
if (pElem->evalNodes[index]) {
|
||||
pNode = pElem->pNodes[index];
|
||||
if (pNode->nodeType != CONTACT) {
|
||||
/* First assign potential equation numbers */
|
||||
if (pNode->nodeType != SCHOTTKY) {
|
||||
pNode->poiEqn = poiEqn++;
|
||||
pNode->psiEqn = numEqn++;
|
||||
}
|
||||
/* Now assign carrier-concentration equation numbers */
|
||||
if (pElem->elemType == INSULATOR) {
|
||||
pNode->nEqn = 0;
|
||||
pNode->pEqn = 0;
|
||||
} else {
|
||||
if (OneCarrier) {
|
||||
/* n and p get same number */
|
||||
pNode->nEqn = numEqn;
|
||||
pNode->pEqn = numEqn++;
|
||||
} else {
|
||||
pNode->nEqn = numEqn++;
|
||||
pNode->pEqn = numEqn++;
|
||||
}
|
||||
}
|
||||
} else { /* This is a contact node. */
|
||||
pNode->poiEqn = 0;
|
||||
pNode->psiEqn = 0;
|
||||
pNode->nEqn = 0;
|
||||
pNode->pEqn = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pDevice->dimEquil = poiEqn;
|
||||
pDevice->dimBias = numEqn;
|
||||
|
||||
/* Open and Print Mesh Output File for Debugging */
|
||||
/* Nuked from release version */
|
||||
#ifdef NOTDEF
|
||||
if (!(meshFile = fopen("mesh.out", "w"))) {
|
||||
perror("mesh.out");
|
||||
exit(-1);
|
||||
}
|
||||
for (eIndex = 1; eIndex <= pDevice->numElems; eIndex++) {
|
||||
pElem = pDevice->elements[eIndex];
|
||||
for (index = 0; index <= 3; index++) {
|
||||
if (pElem->evalNodes[index]) {
|
||||
pNode = pElem->pNodes[index];
|
||||
fprintf(meshFile, "node: %5d %5d %5d %5d\n", pNode->nodeI,
|
||||
pNode->nodeJ, pNode->poiEqn, pNode->psiEqn);
|
||||
}
|
||||
}
|
||||
}
|
||||
fflush(meshFile);
|
||||
fclose(meshFile);
|
||||
#endif
|
||||
|
||||
/* Delete work arrays. */
|
||||
for (xIndex = 1; xIndex <= numXNodes; xIndex++) {
|
||||
FREE(nodeArray[xIndex]);
|
||||
FREE(edgeArrayV[xIndex]);
|
||||
}
|
||||
for (xIndex = 1; xIndex < numXNodes; xIndex++) {
|
||||
FREE(edgeArrayH[xIndex]);
|
||||
}
|
||||
FREE(nodeArray);
|
||||
FREE(edgeArrayV);
|
||||
FREE(edgeArrayH);
|
||||
|
||||
/*
|
||||
* TWOprnMesh( pDevice );
|
||||
*/
|
||||
}
|
||||
|
||||
void
|
||||
TWOprnMesh(TWOdevice *pDevice)
|
||||
{
|
||||
int eIndex, index;
|
||||
TWOelem *pElem;
|
||||
TWOnode *pNode;
|
||||
TWOedge *pEdge;
|
||||
char *name;
|
||||
|
||||
for (eIndex = 1; eIndex <= pDevice->numElems; eIndex++) {
|
||||
pElem = pDevice->elements[eIndex];
|
||||
fprintf(stderr, "elem %5d:\n", eIndex);
|
||||
for (index = 0; index <= 3; 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 %5d\n", index, name,
|
||||
pNode->nodeI, pNode->nodeJ);
|
||||
}
|
||||
if (pElem->evalEdges[index]) {
|
||||
pEdge = pElem->pEdges[index];
|
||||
switch (pEdge->edgeType) {
|
||||
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, "edge %5d: %s\n", index, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We have a separate function for this, so that the setup routines can
|
||||
* reset the state pointers without rebuilding the entire mesh.
|
||||
*/
|
||||
void
|
||||
TWOgetStatePointers(TWOdevice *pDevice, int *numStates)
|
||||
{
|
||||
int eIndex, index;
|
||||
TWOelem *pElem;
|
||||
TWOnode *pNode;
|
||||
TWOedge *pEdge;
|
||||
|
||||
for (eIndex = 1; eIndex <= pDevice->numElems; eIndex++) {
|
||||
pElem = pDevice->elements[eIndex];
|
||||
for (index = 0; index <= 3; index++) {
|
||||
if (pElem->evalNodes[index]) {
|
||||
pNode = pElem->pNodes[index];
|
||||
pNode->nodeState = *numStates;
|
||||
*numStates += TWOnumNodeStates;
|
||||
}
|
||||
if (pElem->evalEdges[index]) {
|
||||
pEdge = pElem->pEdges[index];
|
||||
pEdge->edgeState = *numStates;
|
||||
*numStates += TWOnumEdgeStates;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function computes the percentages of the total semiconductor
|
||||
* width of an edge on the negative and positive sides of the edge
|
||||
*/
|
||||
static void
|
||||
doMobCoeffs(TWOelem *pElem, int index)
|
||||
{
|
||||
TWOelem *pNElem;
|
||||
TWOedge *pEdge;
|
||||
double dl1 = 0.0, dl2 = 0.0;
|
||||
|
||||
pNElem = pElem->pElems[ index ];
|
||||
pEdge = pElem->pEdges[ index ];
|
||||
|
||||
/* If neighbor is not SEMICON, assign and return */
|
||||
if ( (pNElem == NIL(TWOelem)) || (pNElem->elemType == INSULATOR) ) {
|
||||
if ( index == 0 || index == 3 ) {
|
||||
pEdge->kNeg = 0.0;
|
||||
pEdge->kPos = 1.0;
|
||||
} else {
|
||||
pEdge->kNeg = 1.0;
|
||||
pEdge->kPos = 0.0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Find appropriate dimensions of the elements */
|
||||
switch ( index ) {
|
||||
case 0:
|
||||
dl1 = pNElem->dy;
|
||||
dl2 = pElem->dy;
|
||||
break;
|
||||
case 1:
|
||||
dl1 = pElem->dx;
|
||||
dl2 = pNElem->dx;
|
||||
break;
|
||||
case 2:
|
||||
dl1 = pElem->dy;
|
||||
dl2 = pNElem->dy;
|
||||
break;
|
||||
case 3:
|
||||
dl1 = pNElem->dx;
|
||||
dl2 = pElem->dx;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Assign coefficients */
|
||||
pEdge->kNeg = dl1 / (dl1 + dl2);
|
||||
pEdge->kPos = dl2 / (dl1 + dl2);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
resetEvalFlag(TWOdevice *pDevice)
|
||||
{
|
||||
int index, eIndex;
|
||||
TWOelem *pElem;
|
||||
|
||||
for (eIndex = 1; eIndex <= pDevice->numElems; eIndex++) {
|
||||
pElem = pDevice->elements[eIndex];
|
||||
for (index = 0; index <= 3; index++) {
|
||||
pElem->pNodes[index]->evaluated = FALSE;
|
||||
pElem->pEdges[index]->evaluated = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,411 @@
|
|||
/**********
|
||||
Copyright 1991 Regents of the University of California. All rights reserved.
|
||||
Author: 1990 David A. Gates, U. C. Berkeley CAD Group
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numglobs.h"
|
||||
#include "numconst.h"
|
||||
#include "numenum.h"
|
||||
#include "twomesh.h"
|
||||
#include "material.h"
|
||||
#include "twoddefs.h"
|
||||
#include "twodext.h"
|
||||
|
||||
/*
|
||||
* These functions calculate the variable-dependence
|
||||
* of the surface mobilities
|
||||
*/
|
||||
|
||||
void
|
||||
MOBsurfElec(TWOmaterial *info, TWOelem *pElem, double ex, double ey,
|
||||
double es, double wx, double wy, double totalConc)
|
||||
{
|
||||
double thetaA = info->thetaA[ELEC];
|
||||
double thetaB = info->thetaB[ELEC];
|
||||
double eL, eN, eD, e0, mun;
|
||||
double temp1, temp2, temp3, temp4, temp5;
|
||||
double temp6, temp7, temp8, temp9, temp10;
|
||||
double sgnN, sgnL;
|
||||
double dMunDEs; /* Surface Field Derivative */
|
||||
double dMunDEn; /* (Local) Normal Field Derivative */
|
||||
double dMunDEl; /* Tangent Field Derivative */
|
||||
double muHC, muSR, muLV;
|
||||
double dMuSRDEn;
|
||||
double d2MuSRDEn2;
|
||||
double dMuHCDEl;
|
||||
double dMuHCDMuSR;
|
||||
double d2MuHCDMuSR2;
|
||||
double d2MuHCDElDMuSR;
|
||||
double dEnDEx; /* Normal Derivative x Component */
|
||||
double dEnDEy; /* Normal Derivative y Component */
|
||||
double dEnDWx; /* Normal Derivative x Component */
|
||||
double dEnDWy; /* Normal Derivative y Component */
|
||||
double dElDEx; /* Lateral Derivative x Component */
|
||||
double dElDEy; /* Lateral Derivative y Component */
|
||||
double dElDWx; /* Lateral Derivative x Component */
|
||||
double dElDWy; /* Lateral Derivative y Component */
|
||||
|
||||
if ( pElem->surface ) { /* replace one field component with surface field */
|
||||
if ( pElem->direction == 0 ) {
|
||||
ey = es;
|
||||
} else {
|
||||
ex = es;
|
||||
}
|
||||
}
|
||||
|
||||
e0 = 1.0 / ENorm;
|
||||
if ( pElem->direction == 0 ) {
|
||||
eN = ABS( SALPHA_N*ey + SBETA_N*es );
|
||||
sgnN = SGN( SALPHA_N*ey + SBETA_N*es );
|
||||
eD = SALPHA_N*( es - ey );
|
||||
dEnDEx = 0.0;
|
||||
dEnDEy = 1.0;
|
||||
dEnDWx = 0.0;
|
||||
dEnDWy = 0.0;
|
||||
eL = ABS( ex );
|
||||
sgnL = SGN( ex );
|
||||
dElDEx = 1.0;
|
||||
dElDEy = 0.0;
|
||||
dElDWx = 0.0;
|
||||
dElDWy = 0.0;
|
||||
} else { /* pElem->direction == Y */
|
||||
eN = ABS( SALPHA_N*ex + SBETA_N*es );
|
||||
sgnN = SGN( SALPHA_N*ex + SBETA_N*es );
|
||||
eD = SALPHA_N*( es - ex );
|
||||
dEnDEx = 1.0;
|
||||
dEnDEy = 0.0;
|
||||
dEnDWx = 0.0;
|
||||
dEnDWy = 0.0;
|
||||
eL = ABS( ey );
|
||||
sgnL = SGN( ey );
|
||||
dElDEx = 0.0;
|
||||
dElDEy = 1.0;
|
||||
dElDWx = 0.0;
|
||||
dElDWy = 0.0;
|
||||
}
|
||||
/*
|
||||
fprintf(stderr,"En = %e, Ep = %e, Ey = %e, Es= %e\n",eN,eL,ey,es);
|
||||
*/
|
||||
|
||||
muLV = pElem->mun0;
|
||||
if ( TransDepMobility ) {
|
||||
/* Compute various partial derivatives of muSR */
|
||||
temp1 = 1.0 / ( 1.0 + thetaA*eN + thetaB*eN*eN );
|
||||
temp2 = (thetaA + 2.0*thetaB*eN);
|
||||
muSR = muLV * temp1;
|
||||
dMuSRDEn = - muSR * temp1 * temp2;
|
||||
d2MuSRDEn2 = - 2.0 * (dMuSRDEn * temp1 * temp2 + muSR * temp1 * thetaB);
|
||||
if ( FieldDepMobility ) {
|
||||
/* Compute various partial derivatives of muHC */
|
||||
switch ( info->fieldModel ) {
|
||||
case CT:
|
||||
case AR:
|
||||
case UF:
|
||||
temp1 = 1.0 / info->vSat[ELEC];
|
||||
temp2 = muSR * temp1;
|
||||
temp3 = eL * temp1;
|
||||
temp4 = eL * temp2;
|
||||
temp5 = 1.0 / ( 1.0 + temp4 * temp4 );
|
||||
temp6 = sqrt( temp5 );
|
||||
muHC = muSR * temp6;
|
||||
dMuHCDMuSR = temp5 * temp6;
|
||||
temp7 = temp4 * dMuHCDMuSR;
|
||||
temp8 = - 3.0 * temp7 * temp5;
|
||||
dMuHCDEl = - muSR * temp7 * temp2;
|
||||
d2MuHCDMuSR2 = temp8 * temp3;
|
||||
d2MuHCDElDMuSR = temp8 * temp2;
|
||||
break;
|
||||
case SG:
|
||||
default:
|
||||
temp1 = 1.0 / info->vSat[ELEC];
|
||||
temp2 = muSR * eL * temp1; /* Vdrift / Vsat */
|
||||
temp3 = 1.0 / info->vWarm[ELEC];
|
||||
temp4 = muSR * eL * temp3; /* Vdrift / Vwarm */
|
||||
temp5 = temp4 / (temp4 + SG_FIT_N);
|
||||
temp6 = 1.0 / (1.0 + temp5*temp4 + temp2*temp2);
|
||||
temp7 = sqrt(temp6);
|
||||
muHC = muSR * temp7;
|
||||
temp7 *= temp6;
|
||||
temp8 = (2.0 - temp5)*temp5*temp3 + 2.0*temp2*temp1;
|
||||
dMuHCDEl = - 0.5*muSR*temp7*temp8 * muSR;
|
||||
temp9 = temp5*temp5;
|
||||
dMuHCDMuSR = (1.0 + 0.5*temp9*temp4) * temp7;
|
||||
temp9 = (1.5 - temp5)*temp9*temp3 * temp7;
|
||||
temp9 -= 1.5 * dMuHCDMuSR * temp6 * temp8;
|
||||
d2MuHCDMuSR2 = temp9 * eL;
|
||||
d2MuHCDElDMuSR = temp9 * muSR;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Now compute total derivatives */
|
||||
temp1 = dMuHCDMuSR * dMuSRDEn * sgnN;
|
||||
temp2 = d2MuHCDMuSR2 * dMuSRDEn * dMuSRDEn + dMuHCDMuSR * d2MuSRDEn2;
|
||||
temp3 = temp1 - temp2 * eD;
|
||||
mun = muHC - temp1 * eD;
|
||||
dMunDEn = (temp3 + temp1) * SALPHA_N;
|
||||
dMunDEs = temp3 * SBETA_N - temp1 * SALPHA_N;
|
||||
dMunDEl = (dMuHCDEl - d2MuHCDElDMuSR * dMuSRDEn * sgnN * eD) * sgnL;
|
||||
} else {
|
||||
/* Now compute total derivatives */
|
||||
temp1 = dMuSRDEn * sgnN;
|
||||
temp3 = temp1 - d2MuSRDEn2 * eD;
|
||||
mun = muSR - temp1 * eD;
|
||||
dMunDEn = (temp3 + temp1) * SALPHA_N;
|
||||
dMunDEs = temp3 * SBETA_N - temp1 * SALPHA_N;
|
||||
dMunDEl = 0.0;
|
||||
}
|
||||
} else {
|
||||
if ( FieldDepMobility ) {
|
||||
/* Compute various partial derivatives of muHC */
|
||||
switch ( info->fieldModel ) {
|
||||
case CT:
|
||||
case AR:
|
||||
case UF:
|
||||
temp1 = muLV / info->vSat[ELEC];
|
||||
temp2 = eL * temp1;
|
||||
temp3 = 1.0 / ( 1.0 + temp2 * temp2 );
|
||||
temp4 = sqrt( temp3 );
|
||||
muHC = muLV * temp4;
|
||||
dMuHCDEl = - muHC*temp2*temp3 * temp1;
|
||||
break;
|
||||
case SG:
|
||||
default:
|
||||
temp1 = 1.0 / info->vSat[ELEC];
|
||||
temp2 = muLV * eL * temp1; /* Vdrift / Vsat */
|
||||
temp3 = 1.0 / info->vWarm[ELEC];
|
||||
temp4 = muLV * eL * temp3; /* Vdrift / Vwarm */
|
||||
temp5 = temp4 / (temp4 + SG_FIT_N);
|
||||
temp6 = 1.0 / (1.0 + temp5*temp4 + temp2*temp2);
|
||||
temp7 = sqrt(temp6);
|
||||
muHC = muLV * temp7;
|
||||
temp8 = (2.0 - temp5)*temp5*temp3 + 2.0*temp2*temp1;
|
||||
dMuHCDEl = - 0.5*muHC*temp6*temp8 * muLV;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Now compute total derivatives */
|
||||
mun = muHC;
|
||||
dMunDEn = 0.0;
|
||||
dMunDEs = 0.0;
|
||||
dMunDEl = dMuHCDEl * sgnL;
|
||||
} else {
|
||||
mun = muLV;
|
||||
dMunDEn = 0.0;
|
||||
dMunDEs = 0.0;
|
||||
dMunDEl = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
pElem->mun = mun;
|
||||
pElem->dMunDEs = dMunDEs;
|
||||
pElem->dMunDEx = dMunDEn * dEnDEx + dMunDEl * dElDEx;
|
||||
pElem->dMunDEy = dMunDEn * dEnDEy + dMunDEl * dElDEy;
|
||||
pElem->dMunDWx = dMunDEn * dEnDWx + dMunDEl * dElDWx;
|
||||
pElem->dMunDWy = dMunDEn * dEnDWy + dMunDEl * dElDWy;
|
||||
|
||||
if ( pElem->surface ) { /* replace one field component with surface field */
|
||||
if ( pElem->direction == 0 ) {
|
||||
pElem->dMunDEs += pElem->dMunDEy;
|
||||
pElem->dMunDEy = 0.0;
|
||||
} else {
|
||||
pElem->dMunDEs += pElem->dMunDEx;
|
||||
pElem->dMunDEx = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
MOBsurfHole(TWOmaterial *info, TWOelem *pElem, double ex, double ey,
|
||||
double es, double wx, double wy, double totalConc)
|
||||
{
|
||||
double thetaA = info->thetaA[HOLE];
|
||||
double thetaB = info->thetaB[HOLE];
|
||||
double eL, eN, eD, mup;
|
||||
double temp1, temp2, temp3, temp4, temp5;
|
||||
double temp6, temp7, temp8, temp9, temp10;
|
||||
double sgnN, sgnL;
|
||||
double dMupDEs; /* Surface Field Derivative */
|
||||
double dMupDEn; /* (Local) Normal Field Derivative */
|
||||
double dMupDEl; /* Tangent Field Derivative */
|
||||
double muHC, muSR, muLV;
|
||||
double dMuSRDEn;
|
||||
double d2MuSRDEn2;
|
||||
double dMuHCDEl;
|
||||
double dMuHCDMuSR;
|
||||
double d2MuHCDMuSR2;
|
||||
double d2MuHCDElDMuSR;
|
||||
double dEnDEx; /* Normal Derivative x Component */
|
||||
double dEnDEy; /* Normal Derivative y Component */
|
||||
double dEnDWx; /* Normal Derivative x Component */
|
||||
double dEnDWy; /* Normal Derivative y Component */
|
||||
double dElDEx; /* Lateral Derivative x Component */
|
||||
double dElDEy; /* Lateral Derivative y Component */
|
||||
double dElDWx; /* Lateral Derivative x Component */
|
||||
double dElDWy; /* Lateral Derivative y Component */
|
||||
|
||||
if ( pElem->surface ) { /* replace one field component with surface field */
|
||||
if ( pElem->direction == 0 ) {
|
||||
ey = es;
|
||||
} else {
|
||||
ex = es;
|
||||
}
|
||||
}
|
||||
|
||||
if ( pElem->direction == 0 ) {
|
||||
eN = ABS( SALPHA_P*ey + SBETA_P*es );
|
||||
sgnN = SGN( SALPHA_P*ey + SBETA_P*es );
|
||||
eD = SALPHA_P*( es - ey );
|
||||
dEnDEx = 0.0;
|
||||
dEnDEy = 1.0;
|
||||
dEnDWx = 0.0;
|
||||
dEnDWy = 0.0;
|
||||
eL = ABS( ex );
|
||||
sgnL = SGN( ex );
|
||||
dElDEx = 1.0;
|
||||
dElDEy = 0.0;
|
||||
dElDWx = 0.0;
|
||||
dElDWy = 0.0;
|
||||
} else { /* pElem->direction == Y */
|
||||
eN = ABS( SALPHA_P*ex + SBETA_P*es );
|
||||
sgnN = SGN( SALPHA_P*ex + SBETA_P*es );
|
||||
eD = SALPHA_P*( es - ex );
|
||||
dEnDEx = 1.0;
|
||||
dEnDEy = 0.0;
|
||||
dEnDWx = 0.0;
|
||||
dEnDWy = 0.0;
|
||||
eL = ABS( ey );
|
||||
sgnL = SGN( ey );
|
||||
dElDEx = 0.0;
|
||||
dElDEy = 1.0;
|
||||
dElDWx = 0.0;
|
||||
dElDWy = 0.0;
|
||||
}
|
||||
|
||||
muLV = pElem->mup0;
|
||||
if ( TransDepMobility ) {
|
||||
/* Compute various partial derivatives of muSR */
|
||||
temp1 = 1.0 / ( 1.0 + thetaA*eN + thetaB*eN*eN );
|
||||
temp2 = thetaA + 2.0*thetaB*eN;
|
||||
muSR = muLV * temp1;
|
||||
dMuSRDEn = - muSR * temp1 * temp2;
|
||||
d2MuSRDEn2 = - 2.0 * (dMuSRDEn * temp1 * temp2 + muSR * temp1 * thetaB);
|
||||
if ( FieldDepMobility ) {
|
||||
/* Compute various partial derivatives of muHC */
|
||||
switch ( info->fieldModel ) {
|
||||
case CT:
|
||||
case AR:
|
||||
case UF:
|
||||
temp1 = 1.0 / info->vSat[HOLE];
|
||||
temp2 = muSR * temp1;
|
||||
temp3 = eL * temp1;
|
||||
temp4 = eL * temp2;
|
||||
temp5 = 1.0 / ( 1.0 + temp4 );
|
||||
muHC = muSR * temp5;
|
||||
dMuHCDMuSR = temp5 * temp5;
|
||||
dMuHCDEl = - muSR * dMuHCDMuSR * temp2;
|
||||
temp6 = - 2.0 * dMuHCDMuSR * temp5;
|
||||
d2MuHCDMuSR2 = temp6 * temp3;
|
||||
d2MuHCDElDMuSR = temp6 * temp2;
|
||||
break;
|
||||
case SG:
|
||||
default:
|
||||
temp1 = 1.0 / info->vSat[HOLE];
|
||||
temp2 = muSR * eL * temp1; /* Vdrift / Vsat */
|
||||
temp3 = 1.0 / info->vWarm[HOLE];
|
||||
temp4 = muSR * eL * temp3; /* Vdrift / Vwarm */
|
||||
temp5 = temp4 / (temp4 + SG_FIT_P);
|
||||
temp6 = 1.0 / (1.0 + temp5*temp4 + temp2*temp2);
|
||||
temp7 = sqrt(temp6);
|
||||
muHC = muSR * temp7;
|
||||
temp7 *= temp6;
|
||||
temp8 = (2.0 - temp5)*temp5*temp3 + 2.0*temp2*temp1;
|
||||
dMuHCDEl = - 0.5*muSR*temp7*temp8 * muSR;
|
||||
temp9 = temp5*temp5;
|
||||
dMuHCDMuSR = (1.0 + 0.5*temp9*temp4) * temp7;
|
||||
temp9 = (1.5 - temp5)*temp9*temp3 * temp7;
|
||||
temp9 -= 1.5 * dMuHCDMuSR * temp6 * temp8;
|
||||
d2MuHCDMuSR2 = temp9 * eL;
|
||||
d2MuHCDElDMuSR = temp9 * muSR;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Now compute total derivatives */
|
||||
temp1 = dMuHCDMuSR * dMuSRDEn * sgnN;
|
||||
temp2 = d2MuHCDMuSR2 * dMuSRDEn * dMuSRDEn + dMuHCDMuSR * d2MuSRDEn2;
|
||||
temp3 = temp1 - temp2 * eD;
|
||||
mup = muHC - temp1 * eD;
|
||||
dMupDEn = (temp3 + temp1) * SALPHA_P;
|
||||
dMupDEs = temp3 * SBETA_P - temp1 * SALPHA_P;
|
||||
dMupDEl = (dMuHCDEl - d2MuHCDElDMuSR * dMuSRDEn * sgnN * eD ) * sgnL;
|
||||
} else {
|
||||
/* Now compute total derivatives */
|
||||
temp1 = dMuSRDEn * sgnN;
|
||||
temp3 = temp1 - d2MuSRDEn2 * eD;
|
||||
mup = muSR - temp1 * eD;
|
||||
dMupDEn = (temp3 + temp1) * SALPHA_P;
|
||||
dMupDEs = temp3 * SBETA_P - temp1 * SALPHA_P;
|
||||
dMupDEl = 0.0;
|
||||
}
|
||||
} else {
|
||||
if ( FieldDepMobility ) {
|
||||
/* Compute various partial derivatives of muHC */
|
||||
switch ( info->fieldModel ) {
|
||||
case CT:
|
||||
case AR:
|
||||
case UF:
|
||||
temp1 = muLV / info->vSat[HOLE];
|
||||
temp2 = eL * temp1;
|
||||
temp3 = 1.0 / ( 1.0 + temp2 );
|
||||
muHC = muLV * temp3;
|
||||
dMuHCDEl = - muHC * temp3 * temp1;
|
||||
break;
|
||||
case SG:
|
||||
default:
|
||||
temp1 = 1.0 / info->vSat[HOLE];
|
||||
temp2 = muLV * eL * temp1; /* Vdrift / Vsat */
|
||||
temp3 = 1.0 / info->vWarm[HOLE];
|
||||
temp4 = muLV * eL * temp3; /* Vdrift / Vwarm */
|
||||
temp5 = temp4 / (temp4 + SG_FIT_P);
|
||||
temp6 = 1.0 / (1.0 + temp5*temp4 + temp2*temp2);
|
||||
temp7 = sqrt(temp6);
|
||||
muHC = muLV * temp7;
|
||||
temp8 = (2.0 - temp5)*temp5*temp3 + 2.0*temp2*temp1;
|
||||
dMuHCDEl = - 0.5*muHC*temp6*temp8 * muLV;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Now compute total derivatives */
|
||||
mup = muHC;
|
||||
dMupDEn = 0.0;
|
||||
dMupDEs = 0.0;
|
||||
dMupDEl = dMuHCDEl * sgnL;
|
||||
} else {
|
||||
mup = muLV;
|
||||
dMupDEn = 0.0;
|
||||
dMupDEs = 0.0;
|
||||
dMupDEl = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
pElem->mup = mup;
|
||||
pElem->dMupDEs = dMupDEs;
|
||||
pElem->dMupDEx = dMupDEn * dEnDEx + dMupDEl * dElDEx;
|
||||
pElem->dMupDEy = dMupDEn * dEnDEy + dMupDEl * dElDEy;
|
||||
pElem->dMupDWx = dMupDEn * dEnDWx + dMupDEl * dElDWx;
|
||||
pElem->dMupDWy = dMupDEn * dEnDWy + dMupDEl * dElDWy;
|
||||
|
||||
if ( pElem->surface ) { /* replace one field component with surface field */
|
||||
if ( pElem->direction == 0 ) {
|
||||
pElem->dMupDEs += pElem->dMupDEy;
|
||||
pElem->dMupDEy = 0.0;
|
||||
} else {
|
||||
pElem->dMupDEs += pElem->dMupDEx;
|
||||
pElem->dMupDEx = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
/**********
|
||||
Copyright 1991 Regents of the University of California. All rights reserved.
|
||||
Author: 1990 David A. Gates, U. C. Berkeley CAD Group
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numglobs.h"
|
||||
#include "twomesh.h"
|
||||
#include "twoddefs.h"
|
||||
#include "twodext.h"
|
||||
|
||||
/*
|
||||
* Compute the 2-D field-dependent mobility at the center of an element.
|
||||
* It is known a priori that the element belongs to a semiconductor domain.
|
||||
*/
|
||||
|
||||
void
|
||||
TWO_mobility(TWOelem *pElem, double eSurf)
|
||||
{
|
||||
|
||||
TWOedge *pTEdge, *pBEdge, *pLEdge, *pREdge;
|
||||
double dx, dy, rDx, rDy;
|
||||
double enx, eny, wnx, wny, concav;
|
||||
double epx, epy, wpx, wpy;
|
||||
|
||||
/* Initialize various quantities */
|
||||
dx = pElem->dx;
|
||||
dy = pElem->dy;
|
||||
rDx = 0.5 / dx; /* Includes averaging factor of 0.5 */
|
||||
rDy = 0.5 / dy; /* Includes averaging factor of 0.5 */
|
||||
|
||||
/* Get pointers to element's edges */
|
||||
pTEdge = pElem->pTopEdge;
|
||||
pBEdge = pElem->pBotEdge;
|
||||
pLEdge = pElem->pLeftEdge;
|
||||
pREdge = pElem->pRightEdge;
|
||||
|
||||
/* Calculate electric field at element center */
|
||||
enx = -rDx *(pTEdge->dPsi + pTEdge->dCBand + pBEdge->dPsi + pBEdge->dCBand);
|
||||
epx = -rDx *(pTEdge->dPsi - pTEdge->dVBand + pBEdge->dPsi - pBEdge->dVBand);
|
||||
eny = -rDy *(pLEdge->dPsi + pLEdge->dCBand + pREdge->dPsi + pREdge->dCBand);
|
||||
epy = -rDy *(pLEdge->dPsi - pLEdge->dVBand + pREdge->dPsi - pREdge->dVBand);
|
||||
|
||||
/* Calculate weighted carrier driving force at element center */
|
||||
wnx = rDx * (pTEdge->wdfn + pBEdge->wdfn);
|
||||
wpx = rDx * (pTEdge->wdfp + pBEdge->wdfp);
|
||||
wny = rDy * (pLEdge->wdfn + pREdge->wdfn);
|
||||
wpy = rDy * (pLEdge->wdfp + pREdge->wdfp);
|
||||
|
||||
/* compute the mobility for the element */
|
||||
/* Average concentrations at the four corners */
|
||||
concav = 0.25 * ( pElem->pTLNode->totalConc + pElem->pTRNode->totalConc +
|
||||
pElem->pBLNode->totalConc + pElem->pBRNode->totalConc );
|
||||
MOBsurfElec(pElem->matlInfo, pElem, enx, eny, eSurf, wnx, wny, concav);
|
||||
MOBsurfHole(pElem->matlInfo, pElem, epx, epy, eSurf, wpx, wpy, concav);
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
TWONmobility(TWOelem *pElem, double eSurf)
|
||||
{
|
||||
|
||||
TWOedge *pTEdge, *pBEdge, *pLEdge, *pREdge;
|
||||
double dx, dy, rDx, rDy;
|
||||
double enx, eny, wnx, wny, concav;
|
||||
|
||||
/* Initialize various quantities */
|
||||
dx = pElem->dx;
|
||||
dy = pElem->dy;
|
||||
rDx = 0.5 / dx; /* Includes averaging factor of 0.5 */
|
||||
rDy = 0.5 / dy; /* Includes averaging factor of 0.5 */
|
||||
|
||||
/* Get pointers to element's edges */
|
||||
pTEdge = pElem->pTopEdge;
|
||||
pBEdge = pElem->pBotEdge;
|
||||
pLEdge = pElem->pLeftEdge;
|
||||
pREdge = pElem->pRightEdge;
|
||||
|
||||
/* Calculate electric field at element center */
|
||||
enx = -rDx *(pTEdge->dPsi + pTEdge->dCBand + pBEdge->dPsi + pBEdge->dCBand);
|
||||
eny = -rDy *(pLEdge->dPsi + pLEdge->dCBand + pREdge->dPsi + pREdge->dCBand);
|
||||
|
||||
/* Calculate weighted carrier driving force at element center */
|
||||
wnx = rDx * (pTEdge->wdfn + pBEdge->wdfn);
|
||||
wny = rDy * (pLEdge->wdfn + pREdge->wdfn);
|
||||
|
||||
/* compute the mobility for the element */
|
||||
/* Average concentrations at the four corners */
|
||||
concav = 0.25 * ( pElem->pTLNode->totalConc + pElem->pTRNode->totalConc +
|
||||
pElem->pBLNode->totalConc + pElem->pBRNode->totalConc );
|
||||
MOBsurfElec(pElem->matlInfo, pElem, enx, eny, eSurf, wnx, wny, concav);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
TWOPmobility(TWOelem *pElem, double eSurf)
|
||||
{
|
||||
|
||||
TWOedge *pTEdge, *pBEdge, *pLEdge, *pREdge;
|
||||
double dx, dy, rDx, rDy;
|
||||
double epx, epy, wpx, wpy, concav;
|
||||
|
||||
/* Initialize various quantities */
|
||||
dx = pElem->dx;
|
||||
dy = pElem->dy;
|
||||
rDx = 0.5 / dx; /* Includes averaging factor of 0.5 */
|
||||
rDy = 0.5 / dy; /* Includes averaging factor of 0.5 */
|
||||
|
||||
/* Get pointers to element's edges */
|
||||
pTEdge = pElem->pTopEdge;
|
||||
pBEdge = pElem->pBotEdge;
|
||||
pLEdge = pElem->pLeftEdge;
|
||||
pREdge = pElem->pRightEdge;
|
||||
|
||||
/* Calculate electric field at element center */
|
||||
epx = -rDx *(pTEdge->dPsi - pTEdge->dVBand + pBEdge->dPsi - pBEdge->dVBand);
|
||||
epy = -rDy *(pLEdge->dPsi - pLEdge->dVBand + pREdge->dPsi - pREdge->dVBand);
|
||||
|
||||
/* Calculate weighted carrier driving force at element center */
|
||||
wpx = rDx * (pTEdge->wdfp + pBEdge->wdfp);
|
||||
wpy = rDy * (pLEdge->wdfp + pREdge->wdfp);
|
||||
|
||||
/* compute the mobility for the element */
|
||||
/* Average concentrations at the four corners */
|
||||
concav = 0.25 * ( pElem->pTLNode->totalConc + pElem->pTRNode->totalConc +
|
||||
pElem->pBLNode->totalConc + pElem->pBRNode->totalConc );
|
||||
MOBsurfHole(pElem->matlInfo, pElem, epx, epy, eSurf, wpx, wpy, concav);
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
@ -0,0 +1,889 @@
|
|||
/**********
|
||||
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
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numglobs.h"
|
||||
#include "numenum.h"
|
||||
#include "twomesh.h"
|
||||
#include "twodev.h"
|
||||
#include "bool.h"
|
||||
#include "spMatrix.h"
|
||||
#include "twoddefs.h"
|
||||
#include "twodext.h"
|
||||
#include "cidersupt.h"
|
||||
#include "../../maths/misc/bernoull.h"
|
||||
|
||||
|
||||
/*
|
||||
* Functions to setup and solve the continuity equations.
|
||||
* Both continuity equations are solved.
|
||||
* Separate functions are used for one continuity equation.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Setup matrix pointers to Jacobian entries and
|
||||
* store direct pointers with the nodes.
|
||||
*/
|
||||
|
||||
void
|
||||
TWONjacBuild(TWOdevice *pDevice)
|
||||
{
|
||||
char *matrix = pDevice->matrix;
|
||||
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 = 0, nEqnTL = 0;
|
||||
int psiEqnTR = 0, nEqnTR = 0;
|
||||
int psiEqnBR = 0, nEqnBR = 0;
|
||||
int psiEqnBL = 0, nEqnBL = 0;
|
||||
int psiEqnInM = 0, psiEqnInP = 0; /* scratch for deref'd surface eqns */
|
||||
int psiEqnOxM = 0, psiEqnOxP = 0; /* 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 == 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 == 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 && SurfaceMobility && 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 == 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 && SurfaceMobility && 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 == 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 && SurfaceMobility && 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 == 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 && SurfaceMobility && 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 && SurfaceMobility ) {
|
||||
for ( pCh = pDevice->pChannel; pCh != 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 == 0 || nIndex == 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(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 = 0.0;
|
||||
|
||||
/* 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 != CONTACT ) {
|
||||
*(pNode->fPsiPsi) += dyOverDx + dxOverDy;
|
||||
if ( index <= 1 ) {
|
||||
pHEdge = pTEdge;
|
||||
} else {
|
||||
pHEdge = pBEdge;
|
||||
}
|
||||
if ( index == 0 || index == 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 == 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 != CONTACT ) {
|
||||
pRhs[ pNode->psiEqn ] -= -dyOverDx * dPsiT - dxOverDy * dPsiL;
|
||||
*(pNode->fPsiPsiiP1) -= dyOverDx;
|
||||
*(pNode->fPsiPsijP1) -= dxOverDy;
|
||||
if ( pElem->elemType == 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 != CONTACT ) {
|
||||
pRhs[ pNode->psiEqn ] -= dyOverDx * dPsiT - dxOverDy * dPsiR;
|
||||
*(pNode->fPsiPsiiM1) -= dyOverDx;
|
||||
*(pNode->fPsiPsijP1) -= dxOverDy;
|
||||
if ( pElem->elemType == 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 != CONTACT ) {
|
||||
pRhs[ pNode->psiEqn ] -= dyOverDx * dPsiB + dxOverDy * dPsiR;
|
||||
*(pNode->fPsiPsiiM1) -= dyOverDx;
|
||||
*(pNode->fPsiPsijM1) -= dxOverDy;
|
||||
if ( pElem->elemType == 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 != CONTACT ) {
|
||||
pRhs[ pNode->psiEqn ] -= -dyOverDx * dPsiB + dxOverDy * dPsiL;
|
||||
*(pNode->fPsiPsiiP1) -= dyOverDx;
|
||||
*(pNode->fPsiPsijM1) -= dxOverDy;
|
||||
if ( pElem->elemType == 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 && SurfaceMobility ) {
|
||||
for ( pCh = pDevice->pChannel; pCh != 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 != 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(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;
|
||||
|
||||
/* 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;
|
||||
pBEdge = pElem->pBotEdge;
|
||||
pLEdge = pElem->pLeftEdge;
|
||||
pREdge = pElem->pRightEdge;
|
||||
|
||||
/* load for all i,j */
|
||||
for ( index = 0; index <= 3; index++ ) {
|
||||
pNode = pElem->pNodes[ index ];
|
||||
if ( pNode->nodeType != CONTACT ) {
|
||||
*(pNode->fPsiPsi) += dyOverDx + dxOverDy;
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
if ( index <= 1 ) {
|
||||
pHEdge = pTEdge;
|
||||
} else {
|
||||
pHEdge = pBEdge;
|
||||
}
|
||||
if ( index == 0 || index == 3 ) {
|
||||
pVEdge = pLEdge;
|
||||
} else {
|
||||
pVEdge = pREdge;
|
||||
}
|
||||
pConc = *(pDevice->devState0 + pNode->nodeP);
|
||||
*(pNode->fPsiN) += dxdy;
|
||||
*(pNode->fPsiPsi) += dxdy * pConc;
|
||||
*(pNode->fNPsi) -= dy * pHEdge->dJnDpsiP1 + dx * pVEdge->dJnDpsiP1;
|
||||
|
||||
/* Handle generation terms */
|
||||
*(pNode->fNN) -= dxdy * pNode->dUdN;
|
||||
*(pNode->fNPsi) += dxdy * pNode->dUdP * pConc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle neighbor and edge dependent terms */
|
||||
pNode = pElem->pTLNode;
|
||||
if ( pNode->nodeType != CONTACT ) {
|
||||
*(pNode->fPsiPsiiP1) -= dyOverDx;
|
||||
*(pNode->fPsiPsijP1) -= dxOverDy;
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
*(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 != CONTACT ) {
|
||||
*(pNode->fPsiPsiiM1) -= dyOverDx;
|
||||
*(pNode->fPsiPsijP1) -= dxOverDy;
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
*(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 != CONTACT ) {
|
||||
*(pNode->fPsiPsiiM1) -= dyOverDx;
|
||||
*(pNode->fPsiPsijM1) -= dxOverDy;
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
*(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 != CONTACT ) {
|
||||
*(pNode->fPsiPsiiP1) -= dyOverDx;
|
||||
*(pNode->fPsiPsijM1) -= dxOverDy;
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
*(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 && SurfaceMobility ) {
|
||||
for ( pCh = pDevice->pChannel; pCh != 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 != NIL */
|
||||
} /* endif MobDeriv and SurfaceMobility */
|
||||
}
|
||||
|
||||
/* load only the Rhs vector */
|
||||
void
|
||||
TWONrhsLoad(TWOdevice *pDevice, BOOLEAN tranAnalysis, TWOtranInfo *info)
|
||||
{
|
||||
TWOelem *pElem;
|
||||
TWOnode *pNode;
|
||||
TWOedge *pHEdge, *pVEdge;
|
||||
TWOedge *pTEdge, *pBEdge, *pLEdge, *pREdge;
|
||||
TWOchannel *pCh;
|
||||
int index, eIndex;
|
||||
double *pRhs = pDevice->rhs;
|
||||
double dx, dy, dxdy, dyOverDx, dxOverDy;
|
||||
double dPsiT, dPsiB, dPsiL, dPsiR;
|
||||
double rhsN;
|
||||
double nConc, pConc;
|
||||
double perTime;
|
||||
|
||||
/* first compute the currents */
|
||||
TWONcommonTerms( pDevice, TRUE, 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;
|
||||
}
|
||||
|
||||
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 != CONTACT ) {
|
||||
if ( index <= 1 ) {
|
||||
pHEdge = pTEdge;
|
||||
} else {
|
||||
pHEdge = pBEdge;
|
||||
}
|
||||
if ( index == 0 || index == 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 == SEMICON ) {
|
||||
nConc = *(pDevice->devState0 + pNode->nodeN);
|
||||
pConc = *(pDevice->devState0 + pNode->nodeP);
|
||||
pRhs[ pNode->psiEqn ] += dxdy * (pNode->netConc + pConc - nConc);
|
||||
|
||||
/* Handle generation terms */
|
||||
rhsN = - dxdy * pNode->uNet;
|
||||
pRhs[ pNode->nEqn ] -= rhsN;
|
||||
|
||||
/* Handle dXdT continuity terms */
|
||||
if ( tranAnalysis ) {
|
||||
pRhs[ pNode->nEqn ] += dxdy * pNode->dNdT;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle neighbor and edge dependent terms */
|
||||
pNode = pElem->pTLNode;
|
||||
if ( pNode->nodeType != CONTACT ) {
|
||||
pRhs[ pNode->psiEqn ] -= -dyOverDx * dPsiT - dxOverDy * dPsiL;
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
pRhs[ pNode->nEqn ] -= dy * pTEdge->jn + dx * pLEdge->jn;
|
||||
}
|
||||
}
|
||||
pNode = pElem->pTRNode;
|
||||
if ( pNode->nodeType != CONTACT ) {
|
||||
pRhs[ pNode->psiEqn ] -= dyOverDx * dPsiT - dxOverDy * dPsiR;
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
pRhs[ pNode->nEqn ] -= -dy * pTEdge->jn + dx * pREdge->jn;
|
||||
}
|
||||
}
|
||||
pNode = pElem->pBRNode;
|
||||
if ( pNode->nodeType != CONTACT ) {
|
||||
pRhs[ pNode->psiEqn ] -= dyOverDx * dPsiB + dxOverDy * dPsiR;
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
pRhs[ pNode->nEqn ] -= -dy * pBEdge->jn - dx * pREdge->jn;
|
||||
}
|
||||
}
|
||||
pNode = pElem->pBLNode;
|
||||
if ( pNode->nodeType != CONTACT ) {
|
||||
pRhs[ pNode->psiEqn ] -= -dyOverDx * dPsiB + dxOverDy * dPsiL;
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
pRhs[ pNode->nEqn ] -= dy * pBEdge->jn - dx * pLEdge->jn;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* computation of current densities, recombination rates,
|
||||
* mobilities and their derivatives
|
||||
*/
|
||||
void
|
||||
TWONcommonTerms(TWOdevice *pDevice, BOOLEAN currentOnly,
|
||||
BOOLEAN tranAnalysis, TWOtranInfo *info)
|
||||
{
|
||||
TWOelem *pElem, *pElem1;
|
||||
TWOedge *pEdge;
|
||||
TWOnode *pNode;
|
||||
int index, eIndex;
|
||||
int nextIndex; /* index of node to find next element */
|
||||
double psi1, psi2, refPsi, nC, nP1;
|
||||
double dPsiN;
|
||||
double bPsiN, dbPsiN, bMPsiN, dbMPsiN;
|
||||
double muN, dMuN, rDx, rDy;
|
||||
double psi, nConc = 0.0, pConc = 0.0;
|
||||
double cnAug, cpAug;
|
||||
double eSurf = 0.0; /* For channel mobilities */
|
||||
double qInt = 0.0;
|
||||
TWOchannel *pCh;
|
||||
|
||||
/* evaluate all node (including recombination) and edge quantities */
|
||||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
|
||||
pElem = pDevice->elements[ eIndex ];
|
||||
refPsi = pElem->matlInfo->refPsi;
|
||||
cnAug = pElem->matlInfo->cAug[ELEC];
|
||||
cpAug = pElem->matlInfo->cAug[HOLE];
|
||||
for ( index = 0; index <= 3; index++ ) {
|
||||
if ( pElem->evalNodes[ index ] ) {
|
||||
pNode = pElem->pNodes[ index ];
|
||||
if ( pNode->nodeType != CONTACT ) {
|
||||
psi = pDevice->dcSolution[ pNode->psiEqn ];
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
nConc = pDevice->dcSolution[ pNode->nEqn ];
|
||||
pConc = pNode->nie * exp( - psi + refPsi );
|
||||
if ( Srh ) {
|
||||
recomb(nConc, pConc,
|
||||
pNode->tn, pNode->tp, cnAug, cpAug, pNode->nie,
|
||||
&pNode->uNet, &pNode->dUdN, &pNode->dUdP);
|
||||
} else {
|
||||
pNode->uNet = 0.0;
|
||||
pNode->dUdN = 0.0;
|
||||
pNode->dUdP = 0.0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* a contact node */
|
||||
psi = pNode->psi;
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
nConc = pNode->nConc;
|
||||
pConc = pNode->pConc;
|
||||
}
|
||||
}
|
||||
|
||||
/* store info in the state tables */
|
||||
*(pDevice->devState0 + pNode->nodePsi) = psi;
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
*(pDevice->devState0 + pNode->nodeN) = nConc;
|
||||
*(pDevice->devState0 + pNode->nodeP) = pConc;
|
||||
if ( tranAnalysis && pNode->nodeType != CONTACT ) {
|
||||
pNode->dNdT = integrate( pDevice->devStates, info, pNode->nodeN );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for ( index = 0; index <= 3; index++ ) {
|
||||
if ( pElem->evalEdges[ index ] ) {
|
||||
pEdge = pElem->pEdges[ index ];
|
||||
pNode = pElem->pNodes[ index ];
|
||||
if ( pNode->nodeType != CONTACT ) {
|
||||
psi1 = pDevice->dcSolution[pNode->psiEqn];
|
||||
} else {
|
||||
psi1 = pNode->psi;
|
||||
}
|
||||
pNode = pElem->pNodes[ (index + 1) % 4 ];
|
||||
if ( pNode->nodeType != CONTACT ) {
|
||||
psi2 = pDevice->dcSolution[pNode->psiEqn];
|
||||
} else {
|
||||
psi2 = pNode->psi;
|
||||
}
|
||||
if ( index <= 1 ) {
|
||||
pEdge->dPsi = psi2 - psi1;
|
||||
} else {
|
||||
pEdge->dPsi = psi1 - psi2;
|
||||
}
|
||||
*(pDevice->devState0 + pEdge->edgeDpsi) = pEdge->dPsi;
|
||||
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
/* Calculate weighted driving forces - wdfn & wdfp for the edge */
|
||||
dPsiN = pEdge->dPsi + pEdge->dCBand;
|
||||
bernoulli( dPsiN, &bPsiN, &dbPsiN,
|
||||
&bMPsiN, &dbMPsiN, !currentOnly );
|
||||
if ( index <= 1 ) {
|
||||
nC = *(pDevice->devState0 + pElem->pNodes[ index ]->nodeN);
|
||||
nP1 = *(pDevice->devState0 + pElem->pNodes[ index+1 ]->nodeN);
|
||||
} else {
|
||||
nC = *(pDevice->devState0 + pElem->pNodes[(index+1)%4]->nodeN);
|
||||
nP1 = *(pDevice->devState0 + pElem->pNodes[ index ]->nodeN);
|
||||
}
|
||||
pEdge->wdfn = bPsiN * nP1 - bMPsiN * nC;
|
||||
pEdge->jn = 0.0;
|
||||
if ( !currentOnly ) {
|
||||
pEdge->dWnDpsiP1 = dbPsiN * nP1 - dbMPsiN * nC;
|
||||
pEdge->dWnDn = - bMPsiN;
|
||||
pEdge->dWnDnP1 = bPsiN;
|
||||
pEdge->dJnDpsiP1 = 0.0;
|
||||
pEdge->dJnDn = 0.0;
|
||||
pEdge->dJnDnP1 = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* DAG: calculate mobilities for channel elems */
|
||||
if ( SurfaceMobility ) {
|
||||
for ( pCh = pDevice->pChannel;
|
||||
pCh != NIL(TWOchannel); pCh = pCh->next ) {
|
||||
pElem = pCh->pNElem;
|
||||
switch (pCh->type) {
|
||||
case 0:
|
||||
eSurf = - 0.5 * (pElem->pLeftEdge->dPsi + pElem->pRightEdge->dPsi )
|
||||
* pElem->epsRel / pElem->dy;
|
||||
qInt = 0.5 * pElem->pBotEdge->qf;
|
||||
break;
|
||||
case 1:
|
||||
eSurf = - 0.5 * (pElem->pTopEdge->dPsi + pElem->pBotEdge->dPsi )
|
||||
* pElem->epsRel / pElem->dx;
|
||||
qInt = 0.5 * pElem->pLeftEdge->qf;
|
||||
break;
|
||||
case 2:
|
||||
eSurf = - 0.5 * (pElem->pLeftEdge->dPsi + pElem->pRightEdge->dPsi )
|
||||
* pElem->epsRel / pElem->dy;
|
||||
qInt = 0.5 * pElem->pTopEdge->qf;
|
||||
break;
|
||||
case 3:
|
||||
eSurf = - 0.5 * (pElem->pTopEdge->dPsi + pElem->pBotEdge->dPsi )
|
||||
* pElem->epsRel / pElem->dx;
|
||||
qInt = 0.5 * pElem->pRightEdge->qf;
|
||||
break;
|
||||
}
|
||||
eSurf += qInt;
|
||||
pElem = pCh->pSeed;
|
||||
nextIndex = (pCh->type + 2)%4;
|
||||
while (pElem && pElem->channel == pCh->id) {
|
||||
TWONmobility( pElem, eSurf );
|
||||
pElem = pElem->pElems[ nextIndex ];
|
||||
}
|
||||
} /* endfor pCH != NIL */
|
||||
} /* endif SurfaceMobility */
|
||||
|
||||
/* calculate the current densities assuming mobility value depend on Ex,Ey*/
|
||||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
|
||||
pElem = pDevice->elements[ eIndex ];
|
||||
|
||||
rDx = 1.0 / pElem->dx;
|
||||
rDy = 1.0 / pElem->dy;
|
||||
for ( index = 0; index <= 3; index++ ) {
|
||||
pEdge = pElem->pEdges[ index ];
|
||||
/* calculate conductive currents */
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
/* get mobility for this edge */
|
||||
if ( !pElem->channel ) {
|
||||
/* Calculate mobility for non-channel elements */
|
||||
muN = pElem->mun0;
|
||||
dMuN = 0.0;
|
||||
dPsiN = pEdge->dPsi + pEdge->dCBand;
|
||||
if ( index%2 == 0 ) {
|
||||
MOBfieldDep( pElem->matlInfo, ELEC, - dPsiN * rDx, &muN, &dMuN );
|
||||
} else {
|
||||
MOBfieldDep( pElem->matlInfo, ELEC, - dPsiN * rDy, &muN, &dMuN );
|
||||
}
|
||||
} else {
|
||||
/* Retrieve previously calculated value. */
|
||||
muN = pElem->mun;
|
||||
dMuN = 0.0;
|
||||
}
|
||||
switch ( index ) {
|
||||
case 0:
|
||||
muN *= pEdge->kPos * rDx;
|
||||
dMuN *= pEdge->kPos * rDx * rDx;
|
||||
break;
|
||||
case 1:
|
||||
muN *= pEdge->kNeg * rDy;
|
||||
dMuN *= pEdge->kNeg * rDy * rDy;
|
||||
break;
|
||||
case 2:
|
||||
muN *= pEdge->kNeg * rDx;
|
||||
dMuN *= pEdge->kNeg * rDx * rDx;
|
||||
break;
|
||||
case 3:
|
||||
muN *= pEdge->kPos * rDy;
|
||||
dMuN *= pEdge->kPos * rDy * rDy;
|
||||
break;
|
||||
}
|
||||
/* Now that the mobility for this edge is known, do current */
|
||||
pEdge->jn += muN * pEdge->wdfn;
|
||||
if ( !currentOnly ) {
|
||||
pEdge->dJnDpsiP1 += muN * pEdge->dWnDpsiP1;
|
||||
pEdge->dJnDn += muN * pEdge->dWnDn;
|
||||
pEdge->dJnDnP1 += muN * pEdge->dWnDnP1;
|
||||
if ( MobDeriv && (!pElem->channel) ) {
|
||||
pEdge->dJnDpsiP1 -= dMuN * pEdge->wdfn;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* calculate displacement current only once */
|
||||
if ( pElem->evalEdges[ index ] ) {
|
||||
if ( tranAnalysis ) {
|
||||
if ( index == 0 || index == 2 ) {
|
||||
/* horizontal edges */
|
||||
pEdge->jd = -integrate(pDevice->devStates, info,
|
||||
pEdge->edgeDpsi) * rDx;
|
||||
} else {
|
||||
/* vertical edges */
|
||||
pEdge->jd = -integrate(pDevice->devStates, info,
|
||||
pEdge->edgeDpsi) * rDy;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,888 @@
|
|||
/**********
|
||||
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
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numglobs.h"
|
||||
#include "numenum.h"
|
||||
#include "twomesh.h"
|
||||
#include "twodev.h"
|
||||
#include "bool.h"
|
||||
#include "spMatrix.h"
|
||||
#include "twoddefs.h"
|
||||
#include "twodext.h"
|
||||
#include "cidersupt.h"
|
||||
#include "../../maths/misc/bernoull.h"
|
||||
|
||||
/*
|
||||
* Functions to setup and solve the continuity equations.
|
||||
* Both continuity equations are solved.
|
||||
* Separate functions are used for one continuity equation.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* setup matrix pointers to Jacobian values and
|
||||
* store direct pointers with the nodes
|
||||
*/
|
||||
|
||||
void
|
||||
TWOPjacBuild(TWOdevice *pDevice)
|
||||
{
|
||||
char *matrix = pDevice->matrix;
|
||||
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 = 0, pEqnTL = 0;
|
||||
int psiEqnTR = 0, pEqnTR = 0;
|
||||
int psiEqnBR = 0, pEqnBR = 0;
|
||||
int psiEqnBL = 0, pEqnBL = 0;
|
||||
int psiEqnInM = 0, psiEqnInP = 0; /* scratch for deref'd surface eqns */
|
||||
int psiEqnOxM = 0, psiEqnOxP = 0; /* 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 == 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 == 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 && SurfaceMobility && 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 == 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 && SurfaceMobility && 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 == 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 && SurfaceMobility && 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 == 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 && SurfaceMobility && 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 && SurfaceMobility ) {
|
||||
for ( pCh = pDevice->pChannel; pCh != 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 == 0 || nIndex == 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(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 = 0.0;
|
||||
|
||||
/* 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 != CONTACT ) {
|
||||
if ( index <= 1 ) {
|
||||
pHEdge = pTEdge;
|
||||
} else {
|
||||
pHEdge = pBEdge;
|
||||
}
|
||||
if ( index == 0 || index == 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 == 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 != CONTACT ) {
|
||||
pRhs[ pNode->psiEqn ] -= -dyOverDx * dPsiT - dxOverDy * dPsiL;
|
||||
*(pNode->fPsiPsiiP1) -= dyOverDx;
|
||||
*(pNode->fPsiPsijP1) -= dxOverDy;
|
||||
if ( pElem->elemType == 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 != CONTACT ) {
|
||||
pRhs[ pNode->psiEqn ] -= dyOverDx * dPsiT - dxOverDy * dPsiR;
|
||||
*(pNode->fPsiPsiiM1) -= dyOverDx;
|
||||
*(pNode->fPsiPsijP1) -= dxOverDy;
|
||||
if ( pElem->elemType == 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 != CONTACT ) {
|
||||
pRhs[ pNode->psiEqn ] -= dyOverDx * dPsiB + dxOverDy * dPsiR;
|
||||
*(pNode->fPsiPsiiM1) -= dyOverDx;
|
||||
*(pNode->fPsiPsijM1) -= dxOverDy;
|
||||
if ( pElem->elemType == 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 != CONTACT ) {
|
||||
pRhs[ pNode->psiEqn ] -= -dyOverDx * dPsiB + dxOverDy * dPsiL;
|
||||
*(pNode->fPsiPsiiP1) -= dyOverDx;
|
||||
*(pNode->fPsiPsijM1) -= dxOverDy;
|
||||
if ( pElem->elemType == 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 && SurfaceMobility ) {
|
||||
for ( pCh = pDevice->pChannel; pCh != 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 != 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(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;
|
||||
|
||||
/* 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;
|
||||
pLEdge = pElem->pLeftEdge;
|
||||
pREdge = pElem->pRightEdge;
|
||||
|
||||
/* load for all i,j */
|
||||
for ( index = 0; index <= 3; index++ ) {
|
||||
pNode = pElem->pNodes[ index ];
|
||||
if ( pNode->nodeType != CONTACT ) {
|
||||
*(pNode->fPsiPsi) += dyOverDx + dxOverDy;
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
if ( index <= 1 ) {
|
||||
pHEdge = pTEdge;
|
||||
} else {
|
||||
pHEdge = pBEdge;
|
||||
}
|
||||
if ( index == 0 || index == 3 ) {
|
||||
pVEdge = pLEdge;
|
||||
} else {
|
||||
pVEdge = pREdge;
|
||||
}
|
||||
nConc = *(pDevice->devState0 + pNode->nodeN);
|
||||
*(pNode->fPsiPsi) += dxdy * nConc;
|
||||
*(pNode->fPsiP) -= dxdy;
|
||||
*(pNode->fPPsi) -= dy * pHEdge->dJpDpsiP1 + dx * pVEdge->dJpDpsiP1;
|
||||
|
||||
/* Handle generation terms */
|
||||
*(pNode->fPP) += dxdy * pNode->dUdP;
|
||||
*(pNode->fPPsi) += dxdy * pNode->dUdN * nConc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle neighbor and edge dependent terms */
|
||||
pNode = pElem->pTLNode;
|
||||
if ( pNode->nodeType != CONTACT ) {
|
||||
*(pNode->fPsiPsiiP1) -= dyOverDx;
|
||||
*(pNode->fPsiPsijP1) -= dxOverDy;
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
*(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 != CONTACT ) {
|
||||
*(pNode->fPsiPsiiM1) -= dyOverDx;
|
||||
*(pNode->fPsiPsijP1) -= dxOverDy;
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
*(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 != CONTACT ) {
|
||||
*(pNode->fPsiPsiiM1) -= dyOverDx;
|
||||
*(pNode->fPsiPsijM1) -= dxOverDy;
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
*(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 != CONTACT ) {
|
||||
*(pNode->fPsiPsiiP1) -= dyOverDx;
|
||||
*(pNode->fPsiPsijM1) -= dxOverDy;
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
*(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 && SurfaceMobility ) {
|
||||
for ( pCh = pDevice->pChannel; pCh != 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 != NIL */
|
||||
} /* endif MobDeriv and SurfaceMobility */
|
||||
}
|
||||
|
||||
/* load only the Rhs vector */
|
||||
void
|
||||
TWOPrhsLoad(TWOdevice *pDevice, BOOLEAN tranAnalysis, TWOtranInfo *info)
|
||||
{
|
||||
TWOelem *pElem;
|
||||
TWOnode *pNode;
|
||||
TWOedge *pHEdge, *pVEdge;
|
||||
TWOedge *pTEdge, *pBEdge, *pLEdge, *pREdge;
|
||||
TWOchannel *pCh;
|
||||
int index, eIndex;
|
||||
double *pRhs = pDevice->rhs;
|
||||
double dx, dy, dxdy, dyOverDx, dxOverDy;
|
||||
double dPsiT, dPsiB, dPsiL, dPsiR;
|
||||
double rhsP;
|
||||
double nConc, pConc;
|
||||
double perTime;
|
||||
|
||||
/* first compute the currents */
|
||||
TWOPcommonTerms( pDevice, TRUE, 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;
|
||||
}
|
||||
|
||||
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 != CONTACT ) {
|
||||
if ( index <= 1 ) {
|
||||
pHEdge = pTEdge;
|
||||
} else {
|
||||
pHEdge = pBEdge;
|
||||
}
|
||||
if ( index == 0 || index == 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 == SEMICON ) {
|
||||
nConc = *(pDevice->devState0 + pNode->nodeN);
|
||||
pConc = *(pDevice->devState0 + pNode->nodeP);
|
||||
pRhs[ pNode->psiEqn ] += dxdy * (pNode->netConc + pConc - nConc);
|
||||
|
||||
/* Handle generation terms */
|
||||
rhsP = dxdy * pNode->uNet;
|
||||
pRhs[ pNode->pEqn ] -= rhsP;
|
||||
|
||||
/* Handle dXdT continuity terms */
|
||||
if ( tranAnalysis ) {
|
||||
pRhs[ pNode->pEqn ] -= dxdy * pNode->dPdT;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle neighbor and edge dependent terms */
|
||||
pNode = pElem->pTLNode;
|
||||
if ( pNode->nodeType != CONTACT ) {
|
||||
pRhs[ pNode->psiEqn ] -= -dyOverDx * dPsiT - dxOverDy * dPsiL;
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
pRhs[ pNode->pEqn ] -= dy * pTEdge->jp + dx * pLEdge->jp;
|
||||
}
|
||||
}
|
||||
pNode = pElem->pTRNode;
|
||||
if ( pNode->nodeType != CONTACT ) {
|
||||
pRhs[ pNode->psiEqn ] -= dyOverDx * dPsiT - dxOverDy * dPsiR;
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
pRhs[ pNode->pEqn ] -= -dy * pTEdge->jp + dx * pREdge->jp;
|
||||
}
|
||||
}
|
||||
pNode = pElem->pBRNode;
|
||||
if ( pNode->nodeType != CONTACT ) {
|
||||
pRhs[ pNode->psiEqn ] -= dyOverDx * dPsiB + dxOverDy * dPsiR;
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
pRhs[ pNode->pEqn ] -= -dy * pBEdge->jp - dx * pREdge->jp;
|
||||
}
|
||||
}
|
||||
pNode = pElem->pBLNode;
|
||||
if ( pNode->nodeType != CONTACT ) {
|
||||
pRhs[ pNode->psiEqn ] -= -dyOverDx * dPsiB + dxOverDy * dPsiL;
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
pRhs[ pNode->pEqn ] -= dy * pBEdge->jp - dx * pLEdge->jp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* computation of current densities, recombination rates,
|
||||
* mobilities and their derivatives
|
||||
*/
|
||||
void
|
||||
TWOPcommonTerms(TWOdevice *pDevice, BOOLEAN currentOnly,
|
||||
BOOLEAN tranAnalysis, TWOtranInfo *info)
|
||||
{
|
||||
TWOelem *pElem, *pElem1;
|
||||
TWOedge *pEdge;
|
||||
TWOnode *pNode;
|
||||
int index, eIndex;
|
||||
int nextIndex; /* index of node to find next element */
|
||||
double psi1, psi2, refPsi, pC, pP1;
|
||||
double dPsiP;
|
||||
double bPsiP, dbPsiP, bMPsiP, dbMPsiP;
|
||||
double muP, dMuP, rDx, rDy;
|
||||
double psi, nConc = 0.0, pConc = 0.0;
|
||||
double cnAug, cpAug;
|
||||
double eSurf = 0.0; /* For channel mobilities */
|
||||
double qInt = 0.0;
|
||||
TWOchannel *pCh;
|
||||
|
||||
/* evaluate all node (including recombination) and edge quantities */
|
||||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
|
||||
pElem = pDevice->elements[ eIndex ];
|
||||
refPsi = pElem->matlInfo->refPsi;
|
||||
cnAug = pElem->matlInfo->cAug[ELEC];
|
||||
cpAug = pElem->matlInfo->cAug[HOLE];
|
||||
for ( index = 0; index <= 3; index++ ) {
|
||||
if ( pElem->evalNodes[ index ] ) {
|
||||
pNode = pElem->pNodes[ index ];
|
||||
if ( pNode->nodeType != CONTACT ) {
|
||||
psi = pDevice->dcSolution[ pNode->psiEqn ];
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
nConc = pNode->nie * exp( psi - refPsi );
|
||||
pConc = pDevice->dcSolution[ pNode->pEqn ];
|
||||
if ( Srh ) {
|
||||
recomb(nConc, pConc,
|
||||
pNode->tn, pNode->tp, cnAug, cpAug, pNode->nie,
|
||||
&pNode->uNet, &pNode->dUdN, &pNode->dUdP);
|
||||
} else {
|
||||
pNode->uNet = 0.0;
|
||||
pNode->dUdN = 0.0;
|
||||
pNode->dUdP = 0.0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* a contact node */
|
||||
psi = pNode->psi;
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
nConc = pNode->nConc;
|
||||
pConc = pNode->pConc;
|
||||
}
|
||||
}
|
||||
|
||||
/* store info in the state tables */
|
||||
*(pDevice->devState0 + pNode->nodePsi) = psi;
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
*(pDevice->devState0 + pNode->nodeN) = nConc;
|
||||
*(pDevice->devState0 + pNode->nodeP) = pConc;
|
||||
if ( tranAnalysis && pNode->nodeType != CONTACT ) {
|
||||
pNode->dNdT = integrate( pDevice->devStates, info, pNode->nodeN );
|
||||
pNode->dPdT = integrate( pDevice->devStates, info, pNode->nodeP );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for ( index = 0; index <= 3; index++ ) {
|
||||
if ( pElem->evalEdges[ index ] ) {
|
||||
pEdge = pElem->pEdges[ index ];
|
||||
pNode = pElem->pNodes[ index ];
|
||||
if ( pNode->nodeType != CONTACT ) {
|
||||
psi1 = pDevice->dcSolution[pNode->psiEqn];
|
||||
} else {
|
||||
psi1 = pNode->psi;
|
||||
}
|
||||
pNode = pElem->pNodes[ (index + 1) % 4 ];
|
||||
if ( pNode->nodeType != CONTACT ) {
|
||||
psi2 = pDevice->dcSolution[pNode->psiEqn];
|
||||
} else {
|
||||
psi2 = pNode->psi;
|
||||
}
|
||||
if ( index <= 1 ) {
|
||||
pEdge->dPsi = psi2 - psi1;
|
||||
} else {
|
||||
pEdge->dPsi = psi1 - psi2;
|
||||
}
|
||||
*(pDevice->devState0 + pEdge->edgeDpsi) = pEdge->dPsi;
|
||||
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
/* Calculate weighted driving forces - wdfn & wdfp for the edge */
|
||||
dPsiP = pEdge->dPsi - pEdge->dVBand;
|
||||
bernoulli( dPsiP, &bPsiP, &dbPsiP,
|
||||
&bMPsiP, &dbMPsiP, !currentOnly);
|
||||
if ( index <= 1 ) {
|
||||
pC = *(pDevice->devState0 + pElem->pNodes[ index ]->nodeP);
|
||||
pP1 = *(pDevice->devState0 + pElem->pNodes[ index+1 ]->nodeP);
|
||||
} else {
|
||||
pC = *(pDevice->devState0 + pElem->pNodes[(index+1)%4]->nodeP);
|
||||
pP1 = *(pDevice->devState0 + pElem->pNodes[ index ]->nodeP);
|
||||
}
|
||||
pEdge->wdfp = bPsiP * pC - bMPsiP * pP1;
|
||||
pEdge->jp = 0.0;
|
||||
if ( !currentOnly ) {
|
||||
pEdge->dWpDpsiP1 = dbPsiP * pC - dbMPsiP * pP1;
|
||||
pEdge->dWpDp = bPsiP;
|
||||
pEdge->dWpDpP1 = - bMPsiP;
|
||||
pEdge->dJpDpsiP1 = 0.0;
|
||||
pEdge->dJpDp = 0.0;
|
||||
pEdge->dJpDpP1 = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* DAG: calculate mobilities for channel elems */
|
||||
if ( SurfaceMobility ) {
|
||||
for ( pCh = pDevice->pChannel;
|
||||
pCh != NIL(TWOchannel); pCh = pCh->next ) {
|
||||
pElem = pCh->pNElem;
|
||||
switch (pCh->type) {
|
||||
case 0:
|
||||
eSurf = - 0.5 * (pElem->pLeftEdge->dPsi + pElem->pRightEdge->dPsi )
|
||||
* pElem->epsRel / pElem->dy;
|
||||
qInt = 0.5 * pElem->pBotEdge->qf;
|
||||
break;
|
||||
case 1:
|
||||
eSurf = - 0.5 * (pElem->pTopEdge->dPsi + pElem->pBotEdge->dPsi )
|
||||
* pElem->epsRel / pElem->dx;
|
||||
qInt = 0.5 * pElem->pLeftEdge->qf;
|
||||
break;
|
||||
case 2:
|
||||
eSurf = - 0.5 * (pElem->pLeftEdge->dPsi + pElem->pRightEdge->dPsi )
|
||||
* pElem->epsRel / pElem->dy;
|
||||
qInt = 0.5 * pElem->pTopEdge->qf;
|
||||
break;
|
||||
case 3:
|
||||
eSurf = - 0.5 * (pElem->pTopEdge->dPsi + pElem->pBotEdge->dPsi )
|
||||
* pElem->epsRel / pElem->dx;
|
||||
qInt = 0.5 * pElem->pRightEdge->qf;
|
||||
break;
|
||||
}
|
||||
eSurf += qInt;
|
||||
pElem = pCh->pSeed;
|
||||
nextIndex = (pCh->type + 2)%4;
|
||||
while (pElem && pElem->channel == pCh->id) {
|
||||
TWOPmobility( pElem, eSurf );
|
||||
pElem = pElem->pElems[ nextIndex ];
|
||||
}
|
||||
} /* endfor pCH != NIL */
|
||||
} /* endif SurfaceMobility */
|
||||
|
||||
/* calculate the current densities assuming mobility value depend on Ex,Ey*/
|
||||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
|
||||
pElem = pDevice->elements[ eIndex ];
|
||||
|
||||
rDx = 1.0 / pElem->dx;
|
||||
rDy = 1.0 / pElem->dy;
|
||||
for ( index = 0; index <= 3; index++ ) {
|
||||
pEdge = pElem->pEdges[ index ];
|
||||
/* calculate conductive currents */
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
/* get mobility for this edge */
|
||||
if ( !pElem->channel ) {
|
||||
/* Calculate mobility for non-channel elements */
|
||||
muP = pElem->mup0;
|
||||
dMuP = 0.0;
|
||||
dPsiP = pEdge->dPsi - pEdge->dVBand;
|
||||
if ( index%2 == 0 ) {
|
||||
MOBfieldDep( pElem->matlInfo, HOLE, - dPsiP * rDx, &muP, &dMuP );
|
||||
} else {
|
||||
MOBfieldDep( pElem->matlInfo, HOLE, - dPsiP * rDy, &muP, &dMuP );
|
||||
}
|
||||
} else {
|
||||
/* Retrieve previously calculated value. */
|
||||
muP = pElem->mup;
|
||||
dMuP = 0.0;
|
||||
}
|
||||
switch ( index ) {
|
||||
case 0:
|
||||
muP *= pEdge->kPos * rDx;
|
||||
dMuP *= pEdge->kPos * rDx * rDx;
|
||||
break;
|
||||
case 1:
|
||||
muP *= pEdge->kNeg * rDy;
|
||||
dMuP *= pEdge->kNeg * rDy * rDy;
|
||||
break;
|
||||
case 2:
|
||||
muP *= pEdge->kNeg * rDx;
|
||||
dMuP *= pEdge->kNeg * rDx * rDx;
|
||||
break;
|
||||
case 3:
|
||||
muP *= pEdge->kPos * rDy;
|
||||
dMuP *= pEdge->kPos * rDy * rDy;
|
||||
break;
|
||||
}
|
||||
/* Now that the mobility for this edge is known, do current */
|
||||
pEdge->jp += muP * pEdge->wdfp;
|
||||
if ( !currentOnly ) {
|
||||
pEdge->dJpDpsiP1 += muP * pEdge->dWpDpsiP1;
|
||||
pEdge->dJpDp += muP * pEdge->dWpDp;
|
||||
pEdge->dJpDpP1 += muP * pEdge->dWpDpP1;
|
||||
if ( MobDeriv && (!pElem->channel) ) {
|
||||
pEdge->dJpDpsiP1 -= dMuP * pEdge->wdfp;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* calculate displacement current only once */
|
||||
if ( pElem->evalEdges[ index ] ) {
|
||||
if ( tranAnalysis ) {
|
||||
if ( index == 0 || index == 2 ) {
|
||||
/* horizontal edges */
|
||||
pEdge->jd = -integrate(pDevice->devStates, info,
|
||||
pEdge->edgeDpsi) * rDx;
|
||||
} else {
|
||||
/* vertical edges */
|
||||
pEdge->jd = -integrate(pDevice->devStates, info,
|
||||
pEdge->edgeDpsi) * rDy;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,283 @@
|
|||
/**********
|
||||
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
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numglobs.h"
|
||||
#include "numenum.h"
|
||||
#include "twomesh.h"
|
||||
#include "twodev.h"
|
||||
#include "spMatrix.h"
|
||||
#include "twoddefs.h"
|
||||
#include "twodext.h"
|
||||
|
||||
|
||||
/* functions to setup and solve the 2D poisson equation */
|
||||
|
||||
void
|
||||
TWOQjacBuild(TWOdevice *pDevice)
|
||||
{
|
||||
char *matrix = pDevice->matrix;
|
||||
TWOelem *pElem;
|
||||
TWOnode *pNode, *pNode1;
|
||||
int eIndex, nIndex;
|
||||
|
||||
/* set up matrix pointers */
|
||||
/* establish main diagonal first */
|
||||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
|
||||
pElem = pDevice->elements[ eIndex ];
|
||||
for ( nIndex = 0; nIndex <= 3; nIndex++ ) {
|
||||
if ( pElem->evalNodes[ nIndex ] ) {
|
||||
pNode = pElem->pNodes[ nIndex ];
|
||||
pNode->fPsiPsi = spGetElement( matrix, pNode->poiEqn, pNode->poiEqn );
|
||||
}
|
||||
}
|
||||
}
|
||||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
|
||||
pElem = pDevice->elements[ eIndex ];
|
||||
|
||||
pNode = pElem->pTLNode;
|
||||
pNode1 = pElem->pTRNode;
|
||||
pNode->fPsiPsiiP1 = spGetElement(matrix, pNode->poiEqn, pNode1->poiEqn );
|
||||
pNode1 = pElem->pBLNode;
|
||||
pNode->fPsiPsijP1 = spGetElement(matrix, pNode->poiEqn, pNode1->poiEqn );
|
||||
pNode = pElem->pTRNode;
|
||||
pNode1 = pElem->pTLNode;
|
||||
pNode->fPsiPsiiM1 = spGetElement(matrix, pNode->poiEqn, pNode1->poiEqn );
|
||||
pNode1 = pElem->pBRNode;
|
||||
pNode->fPsiPsijP1 = spGetElement(matrix, pNode->poiEqn, pNode1->poiEqn );
|
||||
pNode = pElem->pBRNode;
|
||||
pNode1 = pElem->pBLNode;
|
||||
pNode->fPsiPsiiM1 = spGetElement(matrix, pNode->poiEqn, pNode1->poiEqn );
|
||||
pNode1 = pElem->pTRNode;
|
||||
pNode->fPsiPsijM1 = spGetElement(matrix, pNode->poiEqn, pNode1->poiEqn );
|
||||
pNode = pElem->pBLNode;
|
||||
pNode1 = pElem->pBRNode;
|
||||
pNode->fPsiPsiiP1 = spGetElement(matrix, pNode->poiEqn, pNode1->poiEqn );
|
||||
pNode1 = pElem->pTLNode;
|
||||
pNode->fPsiPsijM1 = spGetElement(matrix, pNode->poiEqn, pNode1->poiEqn );
|
||||
}
|
||||
/*
|
||||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
|
||||
pElem = pDevice->elements[ eIndex ];
|
||||
|
||||
pNode = pElem->pTLNode;
|
||||
pNode->fPsiPsi = spGetElement( matrix, pNode->poiEqn, pNode->poiEqn );
|
||||
pNode1 = pElem->pTRNode;
|
||||
pNode->fPsiPsiiP1 = spGetElement(matrix, pNode->poiEqn, pNode1->poiEqn );
|
||||
pNode1 = pElem->pBLNode;
|
||||
pNode->fPsiPsijP1 = spGetElement(matrix, pNode->poiEqn, pNode1->poiEqn );
|
||||
pNode = pElem->pTRNode;
|
||||
pNode->fPsiPsi = spGetElement( matrix, pNode->poiEqn, pNode->poiEqn );
|
||||
pNode1 = pElem->pTLNode;
|
||||
pNode->fPsiPsiiM1 = spGetElement(matrix, pNode->poiEqn, pNode1->poiEqn );
|
||||
pNode1 = pElem->pBRNode;
|
||||
pNode->fPsiPsijP1 = spGetElement(matrix, pNode->poiEqn, pNode1->poiEqn );
|
||||
pNode = pElem->pBRNode;
|
||||
pNode->fPsiPsi = spGetElement( matrix, pNode->poiEqn, pNode->poiEqn );
|
||||
pNode1 = pElem->pBLNode;
|
||||
pNode->fPsiPsiiM1 = spGetElement(matrix, pNode->poiEqn, pNode1->poiEqn );
|
||||
pNode1 = pElem->pTRNode;
|
||||
pNode->fPsiPsijM1 = spGetElement(matrix, pNode->poiEqn, pNode1->poiEqn );
|
||||
pNode = pElem->pBLNode;
|
||||
pNode->fPsiPsi = spGetElement( matrix, pNode->poiEqn, pNode->poiEqn );
|
||||
pNode1 = pElem->pBRNode;
|
||||
pNode->fPsiPsiiP1 = spGetElement(matrix, pNode->poiEqn, pNode1->poiEqn );
|
||||
pNode1 = pElem->pTLNode;
|
||||
pNode->fPsiPsijM1 = spGetElement(matrix, pNode->poiEqn, pNode1->poiEqn );
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void
|
||||
TWOQsysLoad(TWOdevice *pDevice)
|
||||
{
|
||||
TWOelem *pElem;
|
||||
TWOnode *pNode, *pNode1;
|
||||
TWOedge *pHEdge, *pVEdge;
|
||||
int index, eIndex;
|
||||
double *pRhs = pDevice->rhs;
|
||||
double dyOverDx, dxOverDy, dPsiT, dPsiB, dPsiL, dPsiR;
|
||||
|
||||
TWOQcommonTerms( pDevice );
|
||||
|
||||
/* 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 ];
|
||||
|
||||
dxOverDy = 0.5 * pElem->epsRel * pElem->dxOverDy;
|
||||
dyOverDx = 0.5 * pElem->epsRel * pElem->dyOverDx;
|
||||
dPsiT = pElem->pTopEdge->dPsi;
|
||||
dPsiB = pElem->pBotEdge->dPsi;
|
||||
dPsiL = pElem->pLeftEdge->dPsi;
|
||||
dPsiR = pElem->pRightEdge->dPsi;
|
||||
|
||||
/* load for all i,j */
|
||||
for ( index = 0; index <= 3; index++ ) {
|
||||
pNode = pElem->pNodes[ index ];
|
||||
if ( pNode->nodeType != CONTACT ) {
|
||||
*(pNode->fPsiPsi) += dyOverDx + dxOverDy;
|
||||
if ( index <= 1 ) {
|
||||
pHEdge = pElem->pTopEdge;
|
||||
} else {
|
||||
pHEdge = pElem->pBotEdge;
|
||||
}
|
||||
if ( index == 0 || index == 3 ) {
|
||||
pVEdge = pElem->pLeftEdge;
|
||||
} else {
|
||||
pVEdge = pElem->pRightEdge;
|
||||
}
|
||||
/* add surface state charges */
|
||||
pRhs[ pNode->poiEqn ] += 0.5 * pElem->dx * pHEdge->qf;
|
||||
pRhs[ pNode->poiEqn ] += 0.5 * pElem->dy * pVEdge->qf;
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
*(pNode->fPsiPsi) += 0.25 * pElem->dx * pElem->dy *
|
||||
(pNode->nConc + pNode->pConc);
|
||||
pRhs[ pNode->poiEqn ] += 0.25 * pElem->dx * pElem->dy *
|
||||
(pNode->netConc + pNode->pConc - pNode->nConc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pNode = pElem->pTLNode;
|
||||
pRhs[ pNode->poiEqn ] -= -dyOverDx * dPsiT - dxOverDy * dPsiL;
|
||||
*(pNode->fPsiPsiiP1) -= dyOverDx;
|
||||
*(pNode->fPsiPsijP1) -= dxOverDy;
|
||||
|
||||
pNode = pElem->pTRNode;
|
||||
pRhs[ pNode->poiEqn ] -= dyOverDx * dPsiT - dxOverDy * dPsiR;
|
||||
*(pNode->fPsiPsiiM1) -= dyOverDx;
|
||||
*(pNode->fPsiPsijP1) -= dxOverDy;
|
||||
|
||||
pNode = pElem->pBRNode;
|
||||
pRhs[ pNode->poiEqn ] -= dyOverDx * dPsiB + dxOverDy * dPsiR;
|
||||
*(pNode->fPsiPsiiM1) -= dyOverDx;
|
||||
*(pNode->fPsiPsijM1) -= dxOverDy;
|
||||
|
||||
pNode = pElem->pBLNode;
|
||||
pRhs[ pNode->poiEqn ] -= -dyOverDx * dPsiB + dxOverDy * dPsiL;
|
||||
*(pNode->fPsiPsiiP1) -= dyOverDx;
|
||||
*(pNode->fPsiPsijM1) -= dxOverDy;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TWOQrhsLoad(TWOdevice *pDevice)
|
||||
{
|
||||
TWOelem *pElem;
|
||||
TWOnode *pNode, *pNode1;
|
||||
TWOedge *pHEdge, *pVEdge;
|
||||
int index, eIndex;
|
||||
double *pRhs = pDevice->rhs;
|
||||
double dyOverDx, dxOverDy, dPsiT, dPsiB, dPsiL, dPsiR;
|
||||
|
||||
TWOQcommonTerms( pDevice );
|
||||
|
||||
/* zero the rhs vector */
|
||||
for ( index = 1 ; index <= pDevice->numEqns ; index++ ) {
|
||||
pRhs[ index ] = 0.0;
|
||||
}
|
||||
|
||||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
|
||||
pElem = pDevice->elements[ eIndex ];
|
||||
|
||||
dxOverDy = 0.5 * pElem->epsRel * pElem->dxOverDy;
|
||||
dyOverDx = 0.5 * pElem->epsRel * pElem->dyOverDx;
|
||||
dPsiT = pElem->pTopEdge->dPsi;
|
||||
dPsiB = pElem->pBotEdge->dPsi;
|
||||
dPsiL = pElem->pLeftEdge->dPsi;
|
||||
dPsiR = pElem->pRightEdge->dPsi;
|
||||
|
||||
/* load nodal terms */
|
||||
for ( index = 0; index <= 3; index++ ) {
|
||||
pNode = pElem->pNodes[ index ];
|
||||
if ( (pNode->nodeType != CONTACT) && (pElem->elemType == SEMICON) ) {
|
||||
pRhs[ pNode->poiEqn ] += 0.25 * pElem->dx * pElem->dy *
|
||||
(pNode->netConc + pNode->pConc - pNode->nConc);
|
||||
}
|
||||
if ( index <= 1 ) {
|
||||
pHEdge = pElem->pTopEdge;
|
||||
} else {
|
||||
pHEdge = pElem->pBotEdge;
|
||||
}
|
||||
if ( index == 0 || index == 3 ) {
|
||||
pVEdge = pElem->pLeftEdge;
|
||||
} else {
|
||||
pVEdge = pElem->pRightEdge;
|
||||
}
|
||||
/* add surface state charges */
|
||||
pRhs[ pNode->poiEqn ] += 0.5 * pElem->dx * pHEdge->qf;
|
||||
pRhs[ pNode->poiEqn ] += 0.5 * pElem->dy * pVEdge->qf;
|
||||
}
|
||||
|
||||
/* load edge terms */
|
||||
pNode = pElem->pTLNode;
|
||||
pRhs[ pNode->poiEqn ] -= -dyOverDx * dPsiT - dxOverDy * dPsiL;
|
||||
|
||||
pNode = pElem->pTRNode;
|
||||
pRhs[ pNode->poiEqn ] -= dyOverDx * dPsiT - dxOverDy * dPsiR;
|
||||
|
||||
pNode = pElem->pBRNode;
|
||||
pRhs[ pNode->poiEqn ] -= dyOverDx * dPsiB + dxOverDy * dPsiR;
|
||||
|
||||
pNode = pElem->pBLNode;
|
||||
pRhs[ pNode->poiEqn ] -= -dyOverDx * dPsiB + dxOverDy * dPsiL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TWOQcommonTerms(TWOdevice *pDevice)
|
||||
{
|
||||
TWOelem *pElem;
|
||||
TWOedge *pEdge;
|
||||
TWOnode *pNode, *pNode1;
|
||||
int index, eIndex;
|
||||
double psi1, psi2, refPsi;
|
||||
|
||||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
|
||||
pElem = pDevice->elements[ eIndex ];
|
||||
refPsi = pElem->matlInfo->refPsi;
|
||||
for ( index = 0; index <= 3; index++ ) {
|
||||
if ( pElem->evalNodes[ index ] ) {
|
||||
pNode = pElem->pNodes[ index ];
|
||||
if ( pNode->nodeType != CONTACT ) {
|
||||
pNode->psi = pDevice->dcSolution[ pNode->poiEqn ];
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
pNode->nConc = pNode->nie * exp( pNode->psi - refPsi );
|
||||
pNode->pConc = pNode->nie * exp( - pNode->psi + refPsi );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( pElem->evalEdges[ index ] ) {
|
||||
pEdge = pElem->pEdges[ index ];
|
||||
pNode = pElem->pNodes[ index ];
|
||||
if ( pNode->nodeType != CONTACT ) {
|
||||
psi1 = pDevice->dcSolution[pNode->poiEqn];
|
||||
} else {
|
||||
psi1 = pNode->psi;
|
||||
}
|
||||
pNode = pElem->pNodes[ (index + 1) % 4 ];
|
||||
if ( pNode->nodeType != CONTACT ) {
|
||||
psi2 = pDevice->dcSolution[pNode->poiEqn];
|
||||
} else {
|
||||
psi2 = pNode->psi;
|
||||
}
|
||||
if ( index <= 1 ) {
|
||||
pEdge->dPsi = psi2 - psi1;
|
||||
} else {
|
||||
pEdge->dPsi = psi1 - psi2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,561 @@
|
|||
/**********
|
||||
Copyright 1992 Regents of the University of California. All rights reserved.
|
||||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group
|
||||
Author: 1992 David A. Gates, U. C. Berkeley CAD Group
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numglobs.h"
|
||||
#include "numconst.h"
|
||||
#include "numenum.h"
|
||||
#include "twomesh.h"
|
||||
#include "twodev.h"
|
||||
#include "carddefs.h"
|
||||
#include "spMatrix.h"
|
||||
#include "bool.h"
|
||||
#include "twoddefs.h"
|
||||
#include "twodext.h"
|
||||
|
||||
void
|
||||
TWOprnSolution(FILE *file, TWOdevice *pDevice, OUTPcard *output)
|
||||
{
|
||||
int i, index, xIndex, yIndex;
|
||||
int numVars = 0;
|
||||
TWOnode ***nodeArray = NULL;
|
||||
TWOnode *pNode;
|
||||
TWOelem *pElem, *pNextElem;
|
||||
TWOmaterial *info;
|
||||
double data[50];
|
||||
double ex, ey, refPsi = 0.0, eGap, dGap;
|
||||
double mun, mup;
|
||||
double jcx, jdx, jnx, jpx, jtx;
|
||||
double jcy, jdy, jny, jpy, jty;
|
||||
double *xScale = pDevice->xScale;
|
||||
double *yScale = pDevice->yScale;
|
||||
BOOLEAN foundElem;
|
||||
|
||||
if (output->OUTPnumVars == -1) {
|
||||
/* First pass. Need to count number of variables in output. */
|
||||
numVars += 2; /* For the X & Y scales */
|
||||
if (output->OUTPdoping) {
|
||||
numVars++;
|
||||
}
|
||||
if (output->OUTPpsi) {
|
||||
numVars++;
|
||||
}
|
||||
if (output->OUTPequPsi) {
|
||||
numVars++;
|
||||
}
|
||||
if (output->OUTPvacPsi) {
|
||||
numVars++;
|
||||
}
|
||||
if (output->OUTPnConc) {
|
||||
numVars++;
|
||||
}
|
||||
if (output->OUTPpConc) {
|
||||
numVars++;
|
||||
}
|
||||
if (output->OUTPphin) {
|
||||
numVars++;
|
||||
}
|
||||
if (output->OUTPphip) {
|
||||
numVars++;
|
||||
}
|
||||
if (output->OUTPphic) {
|
||||
numVars++;
|
||||
}
|
||||
if (output->OUTPphiv) {
|
||||
numVars++;
|
||||
}
|
||||
if (output->OUTPeField) {
|
||||
numVars += 2;
|
||||
}
|
||||
if (output->OUTPjc) {
|
||||
numVars += 2;
|
||||
}
|
||||
if (output->OUTPjd) {
|
||||
numVars += 2;
|
||||
}
|
||||
if (output->OUTPjn) {
|
||||
numVars += 2;
|
||||
}
|
||||
if (output->OUTPjp) {
|
||||
numVars += 2;
|
||||
}
|
||||
if (output->OUTPjt) {
|
||||
numVars += 2;
|
||||
}
|
||||
if (output->OUTPuNet) {
|
||||
numVars++;
|
||||
}
|
||||
if (output->OUTPmun) {
|
||||
numVars++;
|
||||
}
|
||||
if (output->OUTPmup) {
|
||||
numVars++;
|
||||
}
|
||||
output->OUTPnumVars = numVars;
|
||||
}
|
||||
/* generate the work array for printing node info */
|
||||
XCALLOC(nodeArray, TWOnode **, 1 + pDevice->numXNodes);
|
||||
for (xIndex = 1; xIndex <= pDevice->numXNodes; xIndex++) {
|
||||
XCALLOC(nodeArray[xIndex], TWOnode *, 1 + pDevice->numYNodes);
|
||||
}
|
||||
|
||||
/* store the nodes in this work array and print out later */
|
||||
for (xIndex = 1; xIndex < pDevice->numXNodes; xIndex++) {
|
||||
for (yIndex = 1; yIndex < pDevice->numYNodes; yIndex++) {
|
||||
pElem = pDevice->elemArray[xIndex][yIndex];
|
||||
if (pElem != NIL(TWOelem)) {
|
||||
if (refPsi == 0.0 && pElem->matlInfo->type == SEMICON) {
|
||||
refPsi = pElem->matlInfo->refPsi;
|
||||
}
|
||||
for (index = 0; index <= 3; index++) {
|
||||
if (pElem->evalNodes[index]) {
|
||||
pNode = pElem->pNodes[index];
|
||||
nodeArray[pNode->nodeI][pNode->nodeJ] = pNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize rawfile */
|
||||
numVars = output->OUTPnumVars;
|
||||
fprintf(file, "Title: Device %s internal state\n", pDevice->name);
|
||||
fprintf(file, "Plotname: Device Cross Section\n");
|
||||
fprintf(file, "Flags: real\n");
|
||||
fprintf(file, "Command: deftype p xs cross\n");
|
||||
fprintf(file, "Command: deftype v distance m\n");
|
||||
fprintf(file, "Command: deftype v concentration cm^-3\n");
|
||||
fprintf(file, "Command: deftype v electric_field V/cm\n");
|
||||
fprintf(file, "Command: deftype v current_density A/cm^2\n");
|
||||
fprintf(file, "Command: deftype v concentration/time cm^-3/s\n");
|
||||
fprintf(file, "Command: deftype v mobility cm^2/Vs\n");
|
||||
fprintf(file, "No. Variables: %d\n", numVars);
|
||||
fprintf(file, "No. Points: %d\n",
|
||||
pDevice->numXNodes * pDevice->numYNodes);
|
||||
fprintf(file, "Dimensions: %d,%d\n",
|
||||
pDevice->numXNodes, pDevice->numYNodes);
|
||||
numVars = 0;
|
||||
fprintf(file, "Variables:\n");
|
||||
fprintf(file, "\t%d y distance\n", numVars++);
|
||||
fprintf(file, "\t%d x distance\n", numVars++);
|
||||
if (output->OUTPpsi) {
|
||||
fprintf(file, "\t%d psi voltage\n", numVars++);
|
||||
}
|
||||
if (output->OUTPequPsi) {
|
||||
fprintf(file, "\t%d equ.psi voltage\n", numVars++);
|
||||
}
|
||||
if (output->OUTPvacPsi) {
|
||||
fprintf(file, "\t%d vac.psi voltage\n", numVars++);
|
||||
}
|
||||
if (output->OUTPphin) {
|
||||
fprintf(file, "\t%d phin voltage\n", numVars++);
|
||||
}
|
||||
if (output->OUTPphip) {
|
||||
fprintf(file, "\t%d phip voltage\n", numVars++);
|
||||
}
|
||||
if (output->OUTPphic) {
|
||||
fprintf(file, "\t%d phic voltage\n", numVars++);
|
||||
}
|
||||
if (output->OUTPphiv) {
|
||||
fprintf(file, "\t%d phiv voltage\n", numVars++);
|
||||
}
|
||||
if (output->OUTPdoping) {
|
||||
fprintf(file, "\t%d dop concentration\n", numVars++);
|
||||
}
|
||||
if (output->OUTPnConc) {
|
||||
fprintf(file, "\t%d n concentration\n", numVars++);
|
||||
}
|
||||
if (output->OUTPpConc) {
|
||||
fprintf(file, "\t%d p concentration\n", numVars++);
|
||||
}
|
||||
if (output->OUTPeField) {
|
||||
fprintf(file, "\t%d ex electric_field\n", numVars++);
|
||||
fprintf(file, "\t%d ey electric_field\n", numVars++);
|
||||
}
|
||||
if (output->OUTPjc) {
|
||||
fprintf(file, "\t%d jcx current_density\n", numVars++);
|
||||
fprintf(file, "\t%d jcy current_density\n", numVars++);
|
||||
}
|
||||
if (output->OUTPjd) {
|
||||
fprintf(file, "\t%d jdx current_density\n", numVars++);
|
||||
fprintf(file, "\t%d jdy current_density\n", numVars++);
|
||||
}
|
||||
if (output->OUTPjn) {
|
||||
fprintf(file, "\t%d jnx current_density\n", numVars++);
|
||||
fprintf(file, "\t%d jny current_density\n", numVars++);
|
||||
}
|
||||
if (output->OUTPjp) {
|
||||
fprintf(file, "\t%d jpx current_density\n", numVars++);
|
||||
fprintf(file, "\t%d jpy current_density\n", numVars++);
|
||||
}
|
||||
if (output->OUTPjt) {
|
||||
fprintf(file, "\t%d jtx current_density\n", numVars++);
|
||||
fprintf(file, "\t%d jty current_density\n", numVars++);
|
||||
}
|
||||
if (output->OUTPuNet) {
|
||||
fprintf(file, "\t%d unet concentration/time\n", numVars++);
|
||||
}
|
||||
if (output->OUTPmun) {
|
||||
fprintf(file, "\t%d mun mobility\n", numVars++);
|
||||
}
|
||||
if (output->OUTPmup) {
|
||||
fprintf(file, "\t%d mup mobility\n", numVars++);
|
||||
}
|
||||
fprintf(file, "Binary:\n");
|
||||
|
||||
for (xIndex = 1; xIndex <= pDevice->numXNodes; xIndex++) {
|
||||
for (yIndex = 1; yIndex <= pDevice->numYNodes; yIndex++) {
|
||||
pNode = nodeArray[xIndex][yIndex];
|
||||
if (pNode != NIL(TWOnode)) {
|
||||
/* Find the element to which this node belongs. */
|
||||
foundElem = FALSE;
|
||||
for (index = 0; (index <= 3) && (!foundElem); index++) {
|
||||
pElem = pNode->pElems[index];
|
||||
if (pElem != NIL(TWOelem) && pElem->evalNodes[(index + 2) % 4]) {
|
||||
foundElem = TRUE;
|
||||
}
|
||||
}
|
||||
nodeFields(pElem, pNode, &ex, &ey);
|
||||
nodeCurrents(pElem, pNode, &mun, &mup,
|
||||
&jnx, &jny, &jpx, &jpy, &jdx, &jdy);
|
||||
jcx = jnx + jpx;
|
||||
jcy = jny + jpy;
|
||||
jtx = jcx + jdx;
|
||||
jty = jcy + jdy;
|
||||
|
||||
info = pElem->matlInfo;
|
||||
eGap = pNode->eg * VNorm;
|
||||
dGap = 0.5 * (info->eg0 - eGap);
|
||||
|
||||
/* Now fill in the data array */
|
||||
numVars = 0;
|
||||
data[numVars++] = yScale[yIndex] * 1e-2;
|
||||
data[numVars++] = xScale[xIndex] * 1e-2;
|
||||
if (output->OUTPpsi) {
|
||||
data[numVars++] = (pNode->psi - refPsi) * VNorm;
|
||||
}
|
||||
if (output->OUTPequPsi) {
|
||||
data[numVars++] = (pNode->psi0 - refPsi) * VNorm;
|
||||
}
|
||||
if (output->OUTPvacPsi) {
|
||||
data[numVars++] = pNode->psi * VNorm;
|
||||
}
|
||||
if (output->OUTPphin) {
|
||||
if (info->type != INSULATOR) {
|
||||
data[numVars++] = (pNode->psi - refPsi
|
||||
- log(pNode->nConc / pNode->nie)) * VNorm;
|
||||
} else {
|
||||
data[numVars++] = 0.0;
|
||||
}
|
||||
}
|
||||
if (output->OUTPphip) {
|
||||
if (info->type != INSULATOR) {
|
||||
data[numVars++] = (pNode->psi - refPsi
|
||||
+ log(pNode->pConc / pNode->nie)) * VNorm;
|
||||
} else {
|
||||
data[numVars++] = 0.0;
|
||||
}
|
||||
}
|
||||
if (output->OUTPphic) {
|
||||
data[numVars++] = (pNode->psi + pNode->eaff) * VNorm + dGap;
|
||||
}
|
||||
if (output->OUTPphiv) {
|
||||
data[numVars++] = (pNode->psi + pNode->eaff) * VNorm + dGap + eGap;
|
||||
}
|
||||
if (output->OUTPdoping) {
|
||||
data[numVars++] = pNode->netConc * NNorm;
|
||||
}
|
||||
if (output->OUTPnConc) {
|
||||
data[numVars++] = pNode->nConc * NNorm;
|
||||
}
|
||||
if (output->OUTPpConc) {
|
||||
data[numVars++] = pNode->pConc * NNorm;
|
||||
}
|
||||
if (output->OUTPeField) {
|
||||
data[numVars++] = ex * ENorm;
|
||||
data[numVars++] = ey * ENorm;
|
||||
}
|
||||
if (output->OUTPjc) {
|
||||
data[numVars++] = jcx * JNorm;
|
||||
data[numVars++] = jcy * JNorm;
|
||||
}
|
||||
if (output->OUTPjd) {
|
||||
data[numVars++] = jdx * JNorm;
|
||||
data[numVars++] = jdy * JNorm;
|
||||
}
|
||||
if (output->OUTPjn) {
|
||||
data[numVars++] = jnx * JNorm;
|
||||
data[numVars++] = jny * JNorm;
|
||||
}
|
||||
if (output->OUTPjp) {
|
||||
data[numVars++] = jpx * JNorm;
|
||||
data[numVars++] = jpy * JNorm;
|
||||
}
|
||||
if (output->OUTPjt) {
|
||||
data[numVars++] = jtx * JNorm;
|
||||
data[numVars++] = jty * JNorm;
|
||||
}
|
||||
if (output->OUTPuNet) {
|
||||
data[numVars++] = pNode->uNet * NNorm / TNorm;
|
||||
}
|
||||
if (output->OUTPmun) {
|
||||
data[numVars++] = mun;
|
||||
}
|
||||
if (output->OUTPmup) {
|
||||
data[numVars++] = mup;
|
||||
}
|
||||
fwrite((char *) data, sizeof(double), numVars, file);
|
||||
} else {
|
||||
for (index = 0; index < output->OUTPnumVars; index++) {
|
||||
data[index] = 0.0;
|
||||
}
|
||||
data[0] = yScale[yIndex] * 1e-2;
|
||||
data[1] = xScale[xIndex] * 1e-2;
|
||||
fwrite((char *) data, sizeof(double), numVars, file);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Delete work array. */
|
||||
for (xIndex = 1; xIndex <= pDevice->numXNodes; xIndex++) {
|
||||
FREE(nodeArray[xIndex]);
|
||||
}
|
||||
FREE(nodeArray);
|
||||
}
|
||||
|
||||
/* XXX This is what the SPARSE element structure looks like.
|
||||
* We can't take it from its definition because the include
|
||||
* file redefines all sorts of things. Note that we are
|
||||
* violating data encapsulation to find out the size of this
|
||||
* thing.
|
||||
*/
|
||||
struct MatrixElement
|
||||
{ spREAL Real;
|
||||
spREAL Imag;
|
||||
int Row;
|
||||
int Col;
|
||||
struct MatrixElement *NextInRow;
|
||||
struct MatrixElement *NextInCol;
|
||||
};
|
||||
|
||||
void
|
||||
TWOmemStats(FILE *file, TWOdevice *pDevice)
|
||||
{
|
||||
static char *memFormat = "%-20s%10d%10d\n";
|
||||
static char *sumFormat = "%20s %-10d\n";
|
||||
unsigned int size;
|
||||
unsigned int memory;
|
||||
TWOmaterial *pMaterial;
|
||||
TWOcontact *pContact;
|
||||
TWOchannel *pChannel;
|
||||
int numContactNodes;
|
||||
|
||||
fprintf(file, "----------------------------------------\n");
|
||||
fprintf(file, "Device %s Memory Usage:\n", pDevice->name );
|
||||
fprintf(file, "Item Count Bytes\n");
|
||||
fprintf(file, "----------------------------------------\n");
|
||||
|
||||
size = 1;
|
||||
memory = size * sizeof(TWOdevice);
|
||||
fprintf( file, memFormat, "Device", size, memory );
|
||||
size = pDevice->numElems;
|
||||
memory = size * sizeof(TWOelem);
|
||||
fprintf( file, memFormat, "Elements", size, memory );
|
||||
size = pDevice->numNodes;
|
||||
memory = size * sizeof(TWOnode);
|
||||
fprintf( file, memFormat, "Nodes", size, memory );
|
||||
size = pDevice->numEdges;
|
||||
memory = size * sizeof(TWOedge);
|
||||
fprintf( file, memFormat, "Edges", size, memory );
|
||||
|
||||
size = pDevice->numXNodes;
|
||||
memory = size * sizeof(TWOelem **);
|
||||
size = (pDevice->numXNodes-1) * pDevice->numYNodes;
|
||||
memory += size * sizeof(TWOelem *);
|
||||
size = pDevice->numElems + 1;
|
||||
memory += size * sizeof(TWOelem *);
|
||||
size = pDevice->numXNodes + pDevice->numYNodes;
|
||||
memory += size * sizeof(double);
|
||||
size = 0;
|
||||
for (pMaterial = pDevice->pMaterials; pMaterial; pMaterial = pMaterial->next)
|
||||
size++;
|
||||
memory += size * sizeof(TWOmaterial);
|
||||
size = numContactNodes = 0;
|
||||
for (pContact = pDevice->pFirstContact; pContact; pContact = pContact->next) {
|
||||
numContactNodes += pContact->numNodes;
|
||||
size++;
|
||||
}
|
||||
memory += size * sizeof(TWOcontact);
|
||||
size = numContactNodes;
|
||||
memory += size * sizeof(TWOnode *);
|
||||
size = 0;
|
||||
for (pChannel = pDevice->pChannel; pChannel; pChannel = pChannel->next)
|
||||
size++;
|
||||
memory += size * sizeof(TWOchannel);
|
||||
fprintf(file, "%-20s%10s%10d\n", "Misc Mesh", "n/a", memory);
|
||||
|
||||
size = pDevice->numOrigEquil;
|
||||
memory = size * sizeof(struct MatrixElement);
|
||||
fprintf( file, memFormat, "Equil Orig NZ", size, memory );
|
||||
size = pDevice->numFillEquil;
|
||||
memory = size * sizeof(struct MatrixElement);
|
||||
fprintf( file, memFormat, "Equil Fill NZ", size, memory );
|
||||
size = pDevice->numOrigEquil + pDevice->numFillEquil;
|
||||
memory = size * sizeof(struct MatrixElement);
|
||||
fprintf( file, memFormat, "Equil Tot NZ", size, memory );
|
||||
size = pDevice->dimEquil;
|
||||
memory = size * 4 * sizeof(double);
|
||||
fprintf( file, memFormat, "Equil Vectors", size, memory );
|
||||
|
||||
size = pDevice->numOrigBias;
|
||||
memory = size * sizeof(struct MatrixElement);
|
||||
fprintf( file, memFormat, "Bias Orig NZ", size, memory );
|
||||
size = pDevice->numFillBias;
|
||||
memory = size * sizeof(struct MatrixElement);
|
||||
fprintf( file, memFormat, "Bias Fill NZ", size, memory );
|
||||
size = pDevice->numOrigBias + pDevice->numFillBias;
|
||||
memory = size * sizeof(struct MatrixElement);
|
||||
fprintf( file, memFormat, "Bias Tot NZ", size, memory );
|
||||
size = pDevice->dimBias;
|
||||
memory = size * 5 * sizeof(double);
|
||||
fprintf( file, memFormat, "Bias Vectors", size, memory );
|
||||
|
||||
size = pDevice->numEdges * TWOnumEdgeStates +
|
||||
pDevice->numNodes * TWOnumNodeStates;
|
||||
memory = size * sizeof(double);
|
||||
fprintf( file, memFormat, "State Vector", size, memory );
|
||||
}
|
||||
|
||||
void
|
||||
TWOcpuStats(FILE *file, TWOdevice *pDevice)
|
||||
{
|
||||
static char *cpuFormat = "%-20s%10g%10g%10g%10g%10g\n";
|
||||
TWOstats *pStats = pDevice->pStats;
|
||||
double total;
|
||||
int iTotal;
|
||||
|
||||
fprintf(file,
|
||||
"----------------------------------------------------------------------\n");
|
||||
fprintf(file,
|
||||
"Device %s Time Usage:\n", pDevice->name);
|
||||
fprintf(file,
|
||||
"Item SETUP DC TRAN AC TOTAL\n");
|
||||
fprintf(file,
|
||||
"----------------------------------------------------------------------\n");
|
||||
|
||||
total = pStats->setupTime[STAT_SETUP] +
|
||||
pStats->setupTime[STAT_DC] +
|
||||
pStats->setupTime[STAT_TRAN] +
|
||||
pStats->setupTime[STAT_AC];
|
||||
fprintf(file, cpuFormat, "Setup Time",
|
||||
pStats->setupTime[STAT_SETUP],
|
||||
pStats->setupTime[STAT_DC],
|
||||
pStats->setupTime[STAT_TRAN],
|
||||
pStats->setupTime[STAT_AC],
|
||||
total);
|
||||
|
||||
total = pStats->loadTime[STAT_SETUP] +
|
||||
pStats->loadTime[STAT_DC] +
|
||||
pStats->loadTime[STAT_TRAN] +
|
||||
pStats->loadTime[STAT_AC];
|
||||
fprintf(file, cpuFormat, "Load Time",
|
||||
pStats->loadTime[STAT_SETUP],
|
||||
pStats->loadTime[STAT_DC],
|
||||
pStats->loadTime[STAT_TRAN],
|
||||
pStats->loadTime[STAT_AC],
|
||||
total);
|
||||
|
||||
total = pStats->orderTime[STAT_SETUP] +
|
||||
pStats->orderTime[STAT_DC] +
|
||||
pStats->orderTime[STAT_TRAN] +
|
||||
pStats->orderTime[STAT_AC];
|
||||
fprintf(file, cpuFormat, "Order Time",
|
||||
pStats->orderTime[STAT_SETUP],
|
||||
pStats->orderTime[STAT_DC],
|
||||
pStats->orderTime[STAT_TRAN],
|
||||
pStats->orderTime[STAT_AC],
|
||||
total);
|
||||
|
||||
total = pStats->factorTime[STAT_SETUP] +
|
||||
pStats->factorTime[STAT_DC] +
|
||||
pStats->factorTime[STAT_TRAN] +
|
||||
pStats->factorTime[STAT_AC];
|
||||
fprintf(file, cpuFormat, "Factor Time",
|
||||
pStats->factorTime[STAT_SETUP],
|
||||
pStats->factorTime[STAT_DC],
|
||||
pStats->factorTime[STAT_TRAN],
|
||||
pStats->factorTime[STAT_AC],
|
||||
total);
|
||||
|
||||
total = pStats->solveTime[STAT_SETUP] +
|
||||
pStats->solveTime[STAT_DC] +
|
||||
pStats->solveTime[STAT_TRAN] +
|
||||
pStats->solveTime[STAT_AC];
|
||||
fprintf(file, cpuFormat, "Solve Time",
|
||||
pStats->solveTime[STAT_SETUP],
|
||||
pStats->solveTime[STAT_DC],
|
||||
pStats->solveTime[STAT_TRAN],
|
||||
pStats->solveTime[STAT_AC],
|
||||
total);
|
||||
|
||||
total = pStats->updateTime[STAT_SETUP] +
|
||||
pStats->updateTime[STAT_DC] +
|
||||
pStats->updateTime[STAT_TRAN] +
|
||||
pStats->updateTime[STAT_AC];
|
||||
fprintf(file, cpuFormat, "Update Time",
|
||||
pStats->updateTime[STAT_SETUP],
|
||||
pStats->updateTime[STAT_DC],
|
||||
pStats->updateTime[STAT_TRAN],
|
||||
pStats->updateTime[STAT_AC],
|
||||
total);
|
||||
|
||||
total = pStats->checkTime[STAT_SETUP] +
|
||||
pStats->checkTime[STAT_DC] +
|
||||
pStats->checkTime[STAT_TRAN] +
|
||||
pStats->checkTime[STAT_AC];
|
||||
fprintf(file, cpuFormat, "Check Time",
|
||||
pStats->checkTime[STAT_SETUP],
|
||||
pStats->checkTime[STAT_DC],
|
||||
pStats->checkTime[STAT_TRAN],
|
||||
pStats->checkTime[STAT_AC],
|
||||
total);
|
||||
|
||||
total = pStats->setupTime[STAT_SETUP] +
|
||||
pStats->setupTime[STAT_DC] +
|
||||
pStats->setupTime[STAT_TRAN] +
|
||||
pStats->setupTime[STAT_AC];
|
||||
fprintf(file, cpuFormat, "Misc Time",
|
||||
pStats->miscTime[STAT_SETUP],
|
||||
pStats->miscTime[STAT_DC],
|
||||
pStats->miscTime[STAT_TRAN],
|
||||
pStats->miscTime[STAT_AC],
|
||||
total);
|
||||
|
||||
fprintf(file, "%-40s%10g%10s%10g\n", "LTE Time",
|
||||
pStats->lteTime,
|
||||
"", pStats->lteTime);
|
||||
|
||||
total = pStats->totalTime[STAT_SETUP] +
|
||||
pStats->totalTime[STAT_DC] +
|
||||
pStats->totalTime[STAT_TRAN] +
|
||||
pStats->totalTime[STAT_AC];
|
||||
fprintf(file, cpuFormat, "Total Time",
|
||||
pStats->totalTime[STAT_SETUP],
|
||||
pStats->totalTime[STAT_DC],
|
||||
pStats->totalTime[STAT_TRAN],
|
||||
pStats->totalTime[STAT_AC],
|
||||
total);
|
||||
|
||||
iTotal = pStats->numIters[STAT_SETUP] +
|
||||
pStats->numIters[STAT_DC] +
|
||||
pStats->numIters[STAT_TRAN] +
|
||||
pStats->numIters[STAT_AC];
|
||||
fprintf(file, "%-20s%10d%10d%10d%10d%10d\n", "Iterations",
|
||||
pStats->numIters[STAT_SETUP],
|
||||
pStats->numIters[STAT_DC],
|
||||
pStats->numIters[STAT_TRAN],
|
||||
pStats->numIters[STAT_AC],
|
||||
iTotal);
|
||||
}
|
||||
|
|
@ -0,0 +1,679 @@
|
|||
/**********
|
||||
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
|
||||
**********/
|
||||
|
||||
/*
|
||||
* Functions for projecting the next solution point for use with the modified
|
||||
* two-level Newton scheme
|
||||
*/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numglobs.h"
|
||||
#include "numenum.h"
|
||||
#include "twomesh.h"
|
||||
#include "twodev.h"
|
||||
#include "bool.h"
|
||||
#include "spMatrix.h"
|
||||
#include "twoddefs.h"
|
||||
#include "twodext.h"
|
||||
#include "cidersupt.h"
|
||||
|
||||
|
||||
/* Forward Declarations */
|
||||
|
||||
|
||||
void NUMD2project(TWOdevice *pDevice, double delV)
|
||||
{
|
||||
TWOnode *pNode;
|
||||
TWOelem *pElem;
|
||||
int index, eIndex, numContactNodes;
|
||||
TWOcontact *pContact = pDevice->pLastContact;
|
||||
double *incVpn, *solution = pDevice->dcSolution;
|
||||
double delPsi, delN, delP, newN, newP;
|
||||
|
||||
delV = -delV / VNorm;
|
||||
/* update the boundary condition for the last contact */
|
||||
numContactNodes = pContact->numNodes;
|
||||
for ( index = 0; index < numContactNodes; index++ ) {
|
||||
pNode = pContact->pNodes[ index ];
|
||||
pNode->psi += delV;
|
||||
}
|
||||
|
||||
/*
|
||||
* store the new rhs for computing the incremental quantities
|
||||
* with the second to last node. solve the system of equations
|
||||
*/
|
||||
|
||||
if ( ABS(delV) < MIN_DELV ) {
|
||||
TWOstoreInitialGuess( pDevice );
|
||||
return;
|
||||
}
|
||||
incVpn = pDevice->dcDeltaSolution;
|
||||
storeNewRhs( pDevice, pContact );
|
||||
spSolve( pDevice->matrix, pDevice->rhs, incVpn, NIL(spREAL), NIL(spREAL) );
|
||||
|
||||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
|
||||
pElem = pDevice->elements[ eIndex ];
|
||||
for ( index = 0; index <= 3; index++ ) {
|
||||
if ( pElem->evalNodes[ index ] ) {
|
||||
pNode = pElem->pNodes[ index ];
|
||||
if ( pNode->nodeType != CONTACT ) {
|
||||
delPsi = incVpn[ pNode->psiEqn ] * delV;
|
||||
solution[ pNode->psiEqn ] = pNode->psi + delPsi;
|
||||
if ( pElem->elemType == SEMICON
|
||||
&& (!OneCarrier || (OneCarrier == N_TYPE)) ) {
|
||||
delN = incVpn[ pNode->nEqn ] * delV;
|
||||
newN = pNode->nConc + delN;
|
||||
if ( newN <= 0.0 ) {
|
||||
solution[ pNode->nEqn ] = guessNewConc( pNode->nConc, delN );
|
||||
}
|
||||
else {
|
||||
solution[ pNode->nEqn ] = newN;
|
||||
}
|
||||
}
|
||||
if ( pElem->elemType == SEMICON
|
||||
&& (!OneCarrier || (OneCarrier == P_TYPE)) ) {
|
||||
delP = incVpn[ pNode->pEqn ] * delV;
|
||||
newP = pNode->pConc + delP;
|
||||
if ( newP <= 0.0 ) {
|
||||
solution[ pNode->pEqn ] = guessNewConc( pNode->pConc, delP );
|
||||
}
|
||||
else {
|
||||
solution[ pNode->pEqn ] = newP;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void NBJT2project(TWOdevice *pDevice, double delVce, double delVbe)
|
||||
{
|
||||
TWOnode *pNode;
|
||||
TWOelem *pElem;
|
||||
int index, eIndex, numContactNodes;
|
||||
TWOcontact *pColContact = pDevice->pFirstContact;
|
||||
TWOcontact *pBaseContact = pDevice->pFirstContact->next;
|
||||
double *incVce, *incVbe, *solution = pDevice->dcSolution;
|
||||
double delPsi, delN, delP, newN, newP;
|
||||
double nConc, pConc;
|
||||
|
||||
/* Normalize the voltages for calculations. */
|
||||
if ( delVce != 0.0 ) {
|
||||
delVce = delVce / VNorm;
|
||||
numContactNodes = pColContact->numNodes;
|
||||
for ( index = 0; index < numContactNodes; index++ ) {
|
||||
pNode = pColContact->pNodes[ index ];
|
||||
pNode->psi += delVce;
|
||||
}
|
||||
}
|
||||
if ( delVbe != 0.0 ) {
|
||||
delVbe = delVbe / VNorm;
|
||||
numContactNodes = pBaseContact->numNodes;
|
||||
for ( index = 0; index < numContactNodes; index++ ) {
|
||||
pNode = pBaseContact->pNodes[ index ];
|
||||
pNode->psi += delVbe;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* store the new rhs for computing the incremental quantities
|
||||
* incVce (dcDeltaSolution) and incVbe (copiedSolution) are used to
|
||||
* store the incremental quantities associated with Vce and Vbe
|
||||
*/
|
||||
|
||||
/* set incVce = dcDeltaSolution; incVbe = copiedSolution */
|
||||
|
||||
if ( ABS( delVce ) > MIN_DELV ) {
|
||||
incVce = pDevice->dcDeltaSolution;
|
||||
storeNewRhs( pDevice, pColContact );
|
||||
spSolve( pDevice->matrix, pDevice->rhs, incVce, NIL(spREAL), NIL(spREAL));
|
||||
|
||||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
|
||||
pElem = pDevice->elements[ eIndex ];
|
||||
for ( index = 0; index <= 3; index++ ) {
|
||||
if ( pElem->evalNodes[ index ] ) {
|
||||
pNode = pElem->pNodes[ index ];
|
||||
if ( pNode->nodeType != CONTACT ) {
|
||||
delPsi = incVce[ pNode->psiEqn ] * delVce;
|
||||
solution[ pNode->psiEqn ] = pNode->psi + delPsi;
|
||||
if ( pElem->elemType == SEMICON
|
||||
&& (!OneCarrier || (OneCarrier == N_TYPE)) ) {
|
||||
delN = incVce[ pNode->nEqn ] * delVce;
|
||||
newN = pNode->nConc + delN;
|
||||
if ( newN <= 0.0 ) {
|
||||
solution[ pNode->nEqn ] = guessNewConc( pNode->nConc, delN );
|
||||
}
|
||||
else {
|
||||
solution[ pNode->nEqn ] = newN;
|
||||
}
|
||||
}
|
||||
if ( pElem->elemType == SEMICON
|
||||
&& (!OneCarrier | (OneCarrier == P_TYPE)) ) {
|
||||
delP = incVce[ pNode->pEqn ] * delVce;
|
||||
newP = pNode->pConc + delP;
|
||||
if ( newP <= 0.0 ) {
|
||||
solution[ pNode->pEqn ] = guessNewConc( pNode->pConc, delP );
|
||||
}
|
||||
else {
|
||||
solution[ pNode->pEqn ] = newP;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
TWOstoreInitialGuess( pDevice );
|
||||
}
|
||||
|
||||
if ( ABS( delVbe ) > MIN_DELV ) {
|
||||
incVbe = pDevice->copiedSolution;
|
||||
storeNewRhs( pDevice, pBaseContact );
|
||||
spSolve( pDevice->matrix, pDevice->rhs, incVbe, NIL(spREAL), NIL(spREAL));
|
||||
|
||||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
|
||||
pElem = pDevice->elements[ eIndex ];
|
||||
for ( index = 0; index <= 3; index++ ) {
|
||||
if ( pElem->evalNodes[ index ] ) {
|
||||
pNode = pElem->pNodes[ index ];
|
||||
if ( pNode->nodeType != CONTACT ) {
|
||||
delPsi = incVbe[ pNode->psiEqn ] * delVbe;
|
||||
solution[ pNode->psiEqn ] += delPsi;
|
||||
if ( pElem->elemType == SEMICON
|
||||
&& (!OneCarrier || (OneCarrier == N_TYPE)) ) {
|
||||
delN = incVbe[ pNode->nEqn ] * delVbe;
|
||||
nConc = solution[ pNode->nEqn ];
|
||||
newN = nConc + delN;
|
||||
if ( newN <= 0.0 ) {
|
||||
solution[ pNode->nEqn ] = guessNewConc( nConc, delN );
|
||||
}
|
||||
else {
|
||||
solution[ pNode->nEqn ] = newN;
|
||||
}
|
||||
}
|
||||
if ( pElem->elemType == SEMICON
|
||||
&& (!OneCarrier || (OneCarrier == P_TYPE)) ) {
|
||||
delP = incVbe[ pNode->pEqn ] * delVbe;
|
||||
pConc = solution[ pNode->pEqn ];
|
||||
newP = pConc + delP;
|
||||
if ( newP <= 0.0 ) {
|
||||
solution[ pNode->pEqn ] = guessNewConc( pConc, delP );
|
||||
}
|
||||
else {
|
||||
solution[ pNode->pEqn ] = newP;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void NUMOSproject(TWOdevice *pDevice, double delVdb, double delVsb,
|
||||
double delVgb)
|
||||
{
|
||||
TWOnode *pNode;
|
||||
TWOelem *pElem;
|
||||
int index, eIndex, numContactNodes;
|
||||
TWOcontact *pDContact = pDevice->pFirstContact;
|
||||
TWOcontact *pGContact = pDevice->pFirstContact->next;
|
||||
TWOcontact *pSContact = pDevice->pFirstContact->next->next;
|
||||
double *incVdb, *incVsb, *incVgb, *solution = pDevice->dcSolution;
|
||||
double delPsi, delN, delP, newN, newP;
|
||||
double nConc, pConc;
|
||||
|
||||
/* normalize the voltages for calculations */
|
||||
if ( delVdb != 0.0 ) {
|
||||
delVdb = delVdb / VNorm;
|
||||
numContactNodes = pDContact->numNodes;
|
||||
for ( index = 0; index < numContactNodes; index++ ) {
|
||||
pNode = pDContact->pNodes[ index ];
|
||||
pNode->psi += delVdb;
|
||||
}
|
||||
}
|
||||
if ( delVsb != 0.0 ) {
|
||||
delVsb = delVsb / VNorm;
|
||||
numContactNodes = pSContact->numNodes;
|
||||
for ( index = 0; index < numContactNodes; index++ ) {
|
||||
pNode = pSContact->pNodes[ index ];
|
||||
pNode->psi += delVsb;
|
||||
}
|
||||
}
|
||||
if ( delVgb != 0.0 ) {
|
||||
delVgb = delVgb / VNorm;
|
||||
numContactNodes = pGContact->numNodes;
|
||||
for ( index = 0; index < numContactNodes; index++ ) {
|
||||
pNode = pGContact->pNodes[ index ];
|
||||
pNode->psi += delVgb;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* store the new rhs for computing the incremental quantities
|
||||
* incVdb (dcDeltaSolution), incVsb, incVgb
|
||||
*/
|
||||
|
||||
if ( ABS( delVdb ) > MIN_DELV ) {
|
||||
|
||||
incVdb = pDevice->dcDeltaSolution;
|
||||
storeNewRhs( pDevice, pDContact );
|
||||
spSolve( pDevice->matrix, pDevice->rhs, incVdb, NIL(spREAL), NIL(spREAL));
|
||||
|
||||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
|
||||
pElem = pDevice->elements[ eIndex ];
|
||||
for ( index = 0; index <= 3; index++ ) {
|
||||
if ( pElem->evalNodes[ index ] ) {
|
||||
pNode = pElem->pNodes[ index ];
|
||||
if ( pNode->nodeType != CONTACT ) {
|
||||
delPsi = incVdb[ pNode->psiEqn ] * delVdb;
|
||||
solution[ pNode->psiEqn ] = pNode->psi + delPsi;
|
||||
if ( pElem->elemType == SEMICON
|
||||
&& (!OneCarrier || (OneCarrier == N_TYPE)) ) {
|
||||
delN = incVdb[ pNode->nEqn ] * delVdb;
|
||||
newN = pNode->nConc + delN;
|
||||
if ( newN <= 0.0 ) {
|
||||
solution[ pNode->nEqn ] = guessNewConc( pNode->nConc, delN );
|
||||
}
|
||||
else {
|
||||
solution[ pNode->nEqn ] = newN;
|
||||
}
|
||||
}
|
||||
if ( pElem->elemType == SEMICON
|
||||
&& (!OneCarrier || (OneCarrier == P_TYPE)) ) {
|
||||
delP = incVdb[ pNode->pEqn ] * delVdb;
|
||||
newP = pNode->pConc + delP;
|
||||
if ( newP <= 0.0 ) {
|
||||
solution[ pNode->pEqn ] = guessNewConc( pNode->pConc, delP );
|
||||
}
|
||||
else {
|
||||
solution[ pNode->pEqn ] = newP;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
TWOstoreInitialGuess( pDevice );
|
||||
}
|
||||
|
||||
if ( ABS( delVsb ) > MIN_DELV ) {
|
||||
|
||||
incVsb = pDevice->dcDeltaSolution;
|
||||
storeNewRhs( pDevice, pSContact );
|
||||
spSolve( pDevice->matrix, pDevice->rhs, incVsb, NIL(spREAL), NIL(spREAL));
|
||||
|
||||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
|
||||
pElem = pDevice->elements[ eIndex ];
|
||||
for ( index = 0; index <= 3; index++ ) {
|
||||
if ( pElem->evalNodes[ index ] ) {
|
||||
pNode = pElem->pNodes[ index ];
|
||||
if ( pNode->nodeType != CONTACT ) {
|
||||
delPsi = incVsb[ pNode->psiEqn ] * delVsb;
|
||||
solution[ pNode->psiEqn ] += delPsi;
|
||||
if ( pElem->elemType == SEMICON
|
||||
&& (!OneCarrier || (OneCarrier == N_TYPE)) ) {
|
||||
delN = incVsb[ pNode->nEqn ] * delVsb;
|
||||
nConc = solution[ pNode->nEqn ];
|
||||
newN = nConc + delN;
|
||||
if ( newN <= 0.0 ) {
|
||||
solution[ pNode->nEqn ] = guessNewConc( nConc, delN );
|
||||
}
|
||||
else {
|
||||
solution[ pNode->nEqn ] = newN;
|
||||
}
|
||||
}
|
||||
if ( pElem->elemType == SEMICON
|
||||
&& (!OneCarrier || (OneCarrier == P_TYPE)) ) {
|
||||
delP = incVsb[ pNode->pEqn ] * delVsb;
|
||||
pConc = solution[ pNode->pEqn ];
|
||||
newP = pConc + delP;
|
||||
if ( newP <= 0.0 ) {
|
||||
solution[ pNode->pEqn ] = guessNewConc( pConc, delP );
|
||||
}
|
||||
else {
|
||||
solution[ pNode->pEqn ] = newP;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ABS( delVgb ) > MIN_DELV ) {
|
||||
|
||||
incVgb = pDevice->dcDeltaSolution;
|
||||
storeNewRhs( pDevice, pGContact );
|
||||
spSolve( pDevice->matrix, pDevice->rhs, incVgb, NIL(spREAL), NIL(spREAL));
|
||||
|
||||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
|
||||
pElem = pDevice->elements[ eIndex ];
|
||||
for ( index = 0; index <= 3; index++ ) {
|
||||
if ( pElem->evalNodes[ index ] ) {
|
||||
pNode = pElem->pNodes[ index ];
|
||||
if ( pNode->nodeType != CONTACT ) {
|
||||
delPsi = incVgb[ pNode->psiEqn ] * delVgb;
|
||||
solution[ pNode->psiEqn ] += delPsi;
|
||||
if ( pElem->elemType == SEMICON
|
||||
&& (!OneCarrier || (OneCarrier == N_TYPE)) ) {
|
||||
delN = incVgb[ pNode->nEqn ] * delVgb;
|
||||
nConc = solution[ pNode->nEqn ];
|
||||
newN = nConc + delN;
|
||||
if ( newN <= 0.0 ) {
|
||||
solution[ pNode->nEqn ] = guessNewConc( nConc, delN );
|
||||
}
|
||||
else {
|
||||
solution[ pNode->nEqn ] = newN;
|
||||
}
|
||||
}
|
||||
if ( pElem->elemType == SEMICON
|
||||
&& (!OneCarrier || (OneCarrier == P_TYPE)) ) {
|
||||
delP = incVgb[ pNode->pEqn ] * delVgb;
|
||||
pConc = solution[ pNode->pEqn ];
|
||||
newP = pConc + delP;
|
||||
if ( newP <= 0.0 ) {
|
||||
solution[ pNode->pEqn ] = guessNewConc( pConc, delP );
|
||||
}
|
||||
else {
|
||||
solution[ pNode->pEqn ] = newP;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* functions to update the solution for the full-LU and
|
||||
modified two-level Newton methods
|
||||
*/
|
||||
|
||||
void NUMD2update(TWOdevice *pDevice, double delV, BOOLEAN updateBoundary)
|
||||
{
|
||||
TWOnode *pNode;
|
||||
TWOelem *pElem;
|
||||
int index, eIndex, numContactNodes;
|
||||
TWOcontact *pContact = pDevice->pLastContact;
|
||||
double delPsi, delN, delP, *incVpn, *solution = pDevice->dcSolution;
|
||||
|
||||
delV = -delV / VNorm;
|
||||
if ( updateBoundary ) {
|
||||
/* update the boundary condition for the last contact */
|
||||
numContactNodes = pContact->numNodes;
|
||||
for ( index = 0; index < numContactNodes; index++ ) {
|
||||
pNode = pContact->pNodes[ index ];
|
||||
pNode->psi += delV;
|
||||
}
|
||||
}
|
||||
|
||||
/* the equations have been solved while computing the conductances */
|
||||
/* solution is in dcDeltaSolution, so use it */
|
||||
|
||||
incVpn = pDevice->dcDeltaSolution;
|
||||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
|
||||
pElem = pDevice->elements[ eIndex ];
|
||||
for ( index = 0; index <= 3; index++ ) {
|
||||
if ( pElem->evalNodes[ index ] ) {
|
||||
pNode = pElem->pNodes[ index ];
|
||||
if ( pNode->nodeType != CONTACT ) {
|
||||
delPsi = incVpn[ pNode->psiEqn ] * delV;
|
||||
solution[ pNode->psiEqn ] = pNode->psi + delPsi;
|
||||
if ( pElem->elemType == SEMICON
|
||||
&& (!OneCarrier || (OneCarrier == N_TYPE)) ) {
|
||||
delN = incVpn[ pNode->nEqn ] * delV;
|
||||
solution[ pNode->nEqn ] = pNode->nConc + delN;
|
||||
}
|
||||
if ( pElem->elemType == SEMICON
|
||||
&& (!OneCarrier || (OneCarrier == P_TYPE)) ) {
|
||||
delP = incVpn[ pNode->pEqn ] * delV;
|
||||
solution[ pNode->pEqn ] = pNode->pConc + delP;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void NBJT2update(TWOdevice *pDevice, double delVce, double delVbe,
|
||||
BOOLEAN updateBoundary)
|
||||
{
|
||||
TWOnode *pNode;
|
||||
TWOelem *pElem;
|
||||
int index, eIndex, numContactNodes;
|
||||
TWOcontact *pColContact = pDevice->pFirstContact;
|
||||
TWOcontact *pBaseContact = pDevice->pFirstContact->next;
|
||||
double delPsi, delN, delP, *incVce, *incVbe, *solution = pDevice->dcSolution;
|
||||
|
||||
/* normalize the voltages for calculations */
|
||||
|
||||
if ( delVce != 0.0 ) {
|
||||
delVce = delVce / VNorm;
|
||||
if ( updateBoundary ) {
|
||||
numContactNodes = pColContact->numNodes;
|
||||
for ( index = 0; index < numContactNodes; index++ ) {
|
||||
pNode = pColContact->pNodes[ index ];
|
||||
pNode->psi += delVce;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( delVbe != 0.0 ) {
|
||||
delVbe = delVbe / VNorm;
|
||||
if ( updateBoundary ) {
|
||||
numContactNodes = pBaseContact->numNodes;
|
||||
for ( index = 0; index < numContactNodes; index++ ) {
|
||||
pNode = pBaseContact->pNodes[ index ];
|
||||
pNode->psi += delVbe;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* use solution from computeConductance to do update */
|
||||
/* set incVce = dcDeltaSolution; incVbe = copiedSolution */
|
||||
|
||||
incVce = pDevice->dcDeltaSolution;
|
||||
incVbe = pDevice->copiedSolution;
|
||||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
|
||||
pElem = pDevice->elements[ eIndex ];
|
||||
for ( index = 0; index <= 3; index++ ) {
|
||||
if ( pElem->evalNodes[ index ] ) {
|
||||
pNode = pElem->pNodes[ index ];
|
||||
if ( pNode->nodeType != CONTACT ) {
|
||||
delPsi = (incVce[ pNode->psiEqn ] * delVce
|
||||
+ incVbe[ pNode->psiEqn ] * delVbe);
|
||||
solution[ pNode->psiEqn ] = pNode->psi + delPsi;
|
||||
if ( pElem->elemType == SEMICON
|
||||
&& (!OneCarrier || (OneCarrier == N_TYPE)) ) {
|
||||
delN = (incVce[ pNode->nEqn ] * delVce
|
||||
+ incVbe[ pNode->nEqn ] * delVbe);
|
||||
solution[ pNode->nEqn ] = pNode->nConc + delN;
|
||||
}
|
||||
if ( pElem->elemType == SEMICON
|
||||
&& (!OneCarrier || (OneCarrier == P_TYPE)) ) {
|
||||
delP = (incVce[ pNode->pEqn ] * delVce
|
||||
+ incVbe[ pNode->pEqn ] * delVbe);
|
||||
solution[ pNode->pEqn ] = pNode->pConc + delP;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void NUMOSupdate(TWOdevice *pDevice, double delVdb, double delVsb,
|
||||
double delVgb, BOOLEAN updateBoundary)
|
||||
{
|
||||
TWOnode *pNode;
|
||||
TWOelem *pElem;
|
||||
int index, eIndex, numContactNodes;
|
||||
TWOcontact *pDContact = pDevice->pFirstContact;
|
||||
TWOcontact *pGContact = pDevice->pFirstContact->next;
|
||||
TWOcontact *pSContact = pDevice->pFirstContact->next->next;
|
||||
double delPsi, delN, delP;
|
||||
double *incVdb, *incVsb, *incVgb, *solution = pDevice->dcSolution;
|
||||
|
||||
/* normalize the voltages for calculations */
|
||||
if ( delVdb != 0.0 ) {
|
||||
delVdb = delVdb / VNorm;
|
||||
if ( updateBoundary ) {
|
||||
numContactNodes = pDContact->numNodes;
|
||||
for ( index = 0; index < numContactNodes; index++ ) {
|
||||
pNode = pDContact->pNodes[ index ];
|
||||
pNode->psi += delVdb;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( delVsb != 0.0 ) {
|
||||
delVsb = delVsb / VNorm;
|
||||
if ( updateBoundary ) {
|
||||
numContactNodes = pSContact->numNodes;
|
||||
for ( index = 0; index < numContactNodes; index++ ) {
|
||||
pNode = pSContact->pNodes[ index ];
|
||||
pNode->psi += delVsb;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( delVgb != 0.0 ) {
|
||||
delVgb = delVgb / VNorm;
|
||||
if ( updateBoundary ) {
|
||||
numContactNodes = pGContact->numNodes;
|
||||
for ( index = 0; index < numContactNodes; index++ ) {
|
||||
pNode = pGContact->pNodes[ index ];
|
||||
pNode->psi += delVgb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* use solution from computeConductance to do update */
|
||||
|
||||
incVdb = pDevice->dcDeltaSolution;
|
||||
incVsb = pDevice->copiedSolution;
|
||||
incVgb = pDevice->rhsImag;
|
||||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
|
||||
pElem = pDevice->elements[ eIndex ];
|
||||
for ( index = 0; index <= 3; index++ ) {
|
||||
if ( pElem->evalNodes[ index ] ) {
|
||||
pNode = pElem->pNodes[ index ];
|
||||
if ( pNode->nodeType != CONTACT ) {
|
||||
delPsi = (incVdb[ pNode->psiEqn ] * delVdb
|
||||
+ incVsb[ pNode->psiEqn ] * delVsb
|
||||
+ incVgb[ pNode->psiEqn ] * delVgb);
|
||||
solution[ pNode->psiEqn ] = pNode->psi + delPsi;
|
||||
if ( pElem->elemType == SEMICON
|
||||
&& (!OneCarrier || (OneCarrier == N_TYPE)) ) {
|
||||
delN = (incVdb[ pNode->nEqn ] * delVdb
|
||||
+ incVsb[ pNode->nEqn ] * delVsb
|
||||
+ incVgb[ pNode->nEqn ] * delVgb);
|
||||
solution[ pNode->nEqn ] = pNode->nConc + delN;
|
||||
}
|
||||
if ( pElem->elemType == SEMICON
|
||||
&& (!OneCarrier || (OneCarrier == P_TYPE)) ) {
|
||||
delP = (incVdb[ pNode->pEqn ] * delVdb
|
||||
+ incVsb[ pNode->pEqn ] * delVsb
|
||||
+ incVgb[ pNode->pEqn ] * delVgb);
|
||||
solution[ pNode->pEqn ] = pNode->pConc + delP;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void storeNewRhs(TWOdevice *pDevice, TWOcontact *pContact)
|
||||
{
|
||||
int index, i, numContactNodes;
|
||||
TWOelem *pElem;
|
||||
TWOnode *pNode, *pHNode = NULL, *pVNode = NULL;
|
||||
TWOedge *pHEdge = NULL, *pVEdge = NULL;
|
||||
double *rhs = pDevice->rhs;
|
||||
|
||||
/* zero the rhs before loading in the new rhs */
|
||||
for ( index = 1; index <= pDevice->numEqns ; index++ ) {
|
||||
rhs[ index ] = 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)) {
|
||||
/* found an element to which this node belongs */
|
||||
switch ( i ) {
|
||||
case 0:
|
||||
/* the TL element of this node */
|
||||
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;
|
||||
default:
|
||||
printf( "storeNewRhs: shouldn't be here\n");
|
||||
break;
|
||||
}
|
||||
if ( pHNode->nodeType != CONTACT ) {
|
||||
/* contribution to the x nodes */
|
||||
rhs[ pHNode->psiEqn ] += pElem->epsRel * 0.5 * pElem->dyOverDx;
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
if ( !OneCarrier ) {
|
||||
rhs[ pHNode->nEqn ] -= 0.5 * pElem->dy * pHEdge->dJnDpsiP1;
|
||||
rhs[ pHNode->pEqn ] -= 0.5 * pElem->dy * pHEdge->dJpDpsiP1;
|
||||
} else if ( OneCarrier == N_TYPE ) {
|
||||
rhs[ pHNode->nEqn ] -= 0.5 * pElem->dy * pHEdge->dJnDpsiP1;
|
||||
} else if ( OneCarrier == P_TYPE ) {
|
||||
rhs[ pHNode->pEqn ] -= 0.5 * pElem->dy * pHEdge->dJpDpsiP1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( pVNode->nodeType != CONTACT ) {
|
||||
/* contribution to the y nodes */
|
||||
rhs[ pVNode->psiEqn ] += pElem->epsRel * 0.5 * pElem->dxOverDy;
|
||||
if ( pElem->elemType == SEMICON ) {
|
||||
if ( !OneCarrier ) {
|
||||
rhs[ pVNode->nEqn ] -= 0.5 * pElem->dx * pVEdge->dJnDpsiP1;
|
||||
rhs[ pVNode->pEqn ] -= 0.5 * pElem->dx * pVEdge->dJpDpsiP1;
|
||||
} else if ( OneCarrier == N_TYPE ) {
|
||||
rhs[ pVNode->nEqn ] -= 0.5 * pElem->dx * pVEdge->dJnDpsiP1;
|
||||
} else if ( OneCarrier == P_TYPE ) {
|
||||
rhs[ pVNode->pEqn ] -= 0.5 * pElem->dx * pVEdge->dJpDpsiP1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
/**********
|
||||
Copyright 1992 Regents of the University of California. All rights reserved.
|
||||
Author: 1992 David A. Gates, U. C. Berkeley CAD Group
|
||||
**********/
|
||||
|
||||
/*
|
||||
* Functions needed to read solutions for 2D devices.
|
||||
*/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "plot.h"
|
||||
#include "numglobs.h"
|
||||
#include "numenum.h"
|
||||
#include "twodev.h"
|
||||
#include "twomesh.h"
|
||||
#include "twoddefs.h"
|
||||
#include "twodext.h"
|
||||
#include "cidersupt.h"
|
||||
|
||||
|
||||
int
|
||||
TWOreadState(TWOdevice *pDevice, char *fileName, int numVolts, double *pV1,
|
||||
double *pV2, double *pV3)
|
||||
/* char *fileName: File containing raw data */
|
||||
/* int numVolts: Number of voltage differences */
|
||||
/* double *pV1, *pV2, *pV3: Pointer to return them in */
|
||||
{
|
||||
int dataLength;
|
||||
int i, index, xIndex, yIndex;
|
||||
TWOnode ***nodeArray = NULL;
|
||||
TWOnode *pNode;
|
||||
TWOelem *pElem;
|
||||
TWOmaterial *info;
|
||||
double refPsi = 0.0;
|
||||
double *psiData, *nData, *pData;
|
||||
double *vData[3];
|
||||
struct plot *stateDB;
|
||||
struct plot *voltsDB;
|
||||
char voltName[80];
|
||||
|
||||
stateDB = DBread( fileName );
|
||||
if (stateDB == NULL) return (-1);
|
||||
voltsDB = stateDB->pl_next;
|
||||
if (voltsDB == NULL) return (-1);
|
||||
|
||||
for (i=0; i < numVolts; i++ ) {
|
||||
sprintf( voltName, "v%d%d", i+1, numVolts+1 );
|
||||
vData[i] = DBgetData( voltsDB, voltName, 1 );
|
||||
if (vData[i] == NULL) return (-1);
|
||||
}
|
||||
dataLength = pDevice->numXNodes * pDevice->numYNodes;
|
||||
psiData = DBgetData( stateDB, "psi", dataLength );
|
||||
nData = DBgetData( stateDB, "n", dataLength );
|
||||
pData = DBgetData( stateDB, "p", dataLength );
|
||||
if (psiData == NULL || nData == NULL || pData == NULL) return (-1);
|
||||
|
||||
if (pV1 != NULL) {
|
||||
*pV1 = vData[0][0];
|
||||
FREE( vData[0] );
|
||||
}
|
||||
if (pV2 != NULL) {
|
||||
*pV2 = vData[1][0];
|
||||
FREE( vData[1] );
|
||||
}
|
||||
if (pV3 != NULL) {
|
||||
*pV3 = vData[2][0];
|
||||
FREE( vData[2] );
|
||||
}
|
||||
|
||||
/* generate the work array for copying node info */
|
||||
XCALLOC(nodeArray, TWOnode **, 1 + pDevice->numXNodes);
|
||||
for (xIndex = 1; xIndex <= pDevice->numXNodes; xIndex++) {
|
||||
XCALLOC(nodeArray[xIndex], TWOnode *, 1 + pDevice->numYNodes);
|
||||
}
|
||||
|
||||
/* store the nodes in this work array and use later */
|
||||
for (xIndex = 1; xIndex < pDevice->numXNodes; xIndex++) {
|
||||
for (yIndex = 1; yIndex < pDevice->numYNodes; yIndex++) {
|
||||
pElem = pDevice->elemArray[xIndex][yIndex];
|
||||
if (pElem != NIL(TWOelem)) {
|
||||
if (refPsi == 0.0 && pElem->matlInfo->type == SEMICON) {
|
||||
refPsi = pElem->matlInfo->refPsi;
|
||||
}
|
||||
for (index = 0; index <= 3; index++) {
|
||||
if (pElem->evalNodes[index]) {
|
||||
pNode = pElem->pNodes[index];
|
||||
nodeArray[pNode->nodeI][pNode->nodeJ] = pNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
index = 0;
|
||||
for (xIndex = 1; xIndex <= pDevice->numXNodes; xIndex++) {
|
||||
for (yIndex = 1; yIndex <= pDevice->numYNodes; yIndex++) {
|
||||
pNode = nodeArray[xIndex][yIndex];
|
||||
index++;
|
||||
if (pNode != NIL(TWOnode)) {
|
||||
pNode->psi = psiData[index-1]/VNorm + refPsi;
|
||||
pNode->nConc = nData[index-1]/NNorm;
|
||||
pNode->pConc = pData[index-1]/NNorm;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Delete work array. */
|
||||
for (xIndex = 1; xIndex <= pDevice->numXNodes; xIndex++) {
|
||||
FREE(nodeArray[xIndex]);
|
||||
}
|
||||
FREE(nodeArray);
|
||||
|
||||
FREE(psiData);
|
||||
FREE(nData);
|
||||
FREE(pData);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
/**********
|
||||
Copyright 1991 Regents of the University of California. All rights reserved.
|
||||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numglobs.h"
|
||||
#include "numenum.h"
|
||||
#include "twomesh.h"
|
||||
#include "twodev.h"
|
||||
#include "twoddefs.h"
|
||||
#include "twodext.h"
|
||||
|
||||
/* Forward declarations */
|
||||
static void setDirichlet(TWOcontact *, double);
|
||||
|
||||
|
||||
void NUMD2setBCs(TWOdevice *pDevice, double vd)
|
||||
{
|
||||
TWOcontact *pContact = pDevice->pLastContact;
|
||||
|
||||
setDirichlet( pContact, - vd );
|
||||
}
|
||||
|
||||
void NBJT2setBCs(TWOdevice *pDevice, double vce, double vbe)
|
||||
{
|
||||
TWOcontact *pCollContact = pDevice->pFirstContact;
|
||||
TWOcontact *pBaseContact = pDevice->pFirstContact->next;
|
||||
|
||||
setDirichlet( pCollContact, vce );
|
||||
setDirichlet( pBaseContact, vbe );
|
||||
}
|
||||
|
||||
void NUMOSsetBCs(TWOdevice *pDevice, double vdb, double vsb, double vgb)
|
||||
{
|
||||
TWOcontact *pDContact = pDevice->pFirstContact;
|
||||
TWOcontact *pGContact = pDevice->pFirstContact->next;
|
||||
TWOcontact *pSContact = pDevice->pFirstContact->next->next;
|
||||
|
||||
setDirichlet( pDContact, vdb );
|
||||
setDirichlet( pSContact, vsb );
|
||||
setDirichlet( pGContact, vgb );
|
||||
}
|
||||
|
||||
static void
|
||||
setDirichlet(TWOcontact *pContact, double voltage)
|
||||
{
|
||||
int index, numContactNodes, i;
|
||||
TWOelem *pElem = NULL;
|
||||
TWOnode *pNode;
|
||||
double psi, ni, pi, nie;
|
||||
double conc, sign, absConc;
|
||||
|
||||
voltage /= VNorm;
|
||||
|
||||
numContactNodes = pContact->numNodes;
|
||||
for ( index = 0; index < numContactNodes; index++ ) {
|
||||
pNode = pContact->pNodes[ index ];
|
||||
|
||||
/* Find this node's owner element. */
|
||||
for ( i = 0; i <= 3; i++ ) {
|
||||
pElem = pNode->pElems[ i ];
|
||||
if ( pElem != NIL(TWOelem) && pElem->evalNodes[ (i+2)%4 ] ) {
|
||||
break; /* got it */
|
||||
}
|
||||
}
|
||||
|
||||
if (pElem->elemType == INSULATOR) {
|
||||
pNode->psi = RefPsi - pNode->eaff;
|
||||
pNode->nConc = 0.0;
|
||||
pNode->pConc = 0.0;
|
||||
}
|
||||
else if (pElem->elemType == SEMICON) {
|
||||
nie = pNode->nie;
|
||||
conc = pNode->netConc / nie;
|
||||
sign = SGN( conc );
|
||||
absConc = ABS( conc );
|
||||
if ( conc != 0.0 ) {
|
||||
psi = sign * log( 0.5 * absConc + sqrt( 1.0 + 0.25*absConc*absConc ));
|
||||
ni = nie * exp( psi );
|
||||
pi = nie * exp( - psi );
|
||||
}
|
||||
else {
|
||||
psi = 0.0;
|
||||
ni = nie;
|
||||
pi = nie;
|
||||
}
|
||||
pNode->psi = pElem->matlInfo->refPsi + psi;
|
||||
pNode->nConc = ni;
|
||||
pNode->pConc = pi;
|
||||
}
|
||||
pNode->psi += voltage;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,318 @@
|
|||
/**********
|
||||
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
|
||||
**********/
|
||||
|
||||
/**********
|
||||
Two-Dimensional Numerical Device Setup Routines
|
||||
**********/
|
||||
|
||||
#include "ngspice.h"
|
||||
#include "numglobs.h"
|
||||
#include "numconst.h"
|
||||
#include "numenum.h"
|
||||
#include "twomesh.h"
|
||||
#include "twodev.h"
|
||||
#include "carddefs.h" /* XXX Not really modular if we need this. */
|
||||
#include "twoddefs.h"
|
||||
#include "twodext.h"
|
||||
#include "cidersupt.h"
|
||||
|
||||
|
||||
/* compute node parameters */
|
||||
void TWOsetup(TWOdevice *pDevice)
|
||||
{
|
||||
double temp1, deltaEg, avgConc, totalConc, absNetConc;
|
||||
double ncv0, dBand, dNie, psiBand[4];
|
||||
double *xScale = pDevice->xScale;
|
||||
double *yScale = pDevice->yScale;
|
||||
int index, eIndex;
|
||||
int numContactNodes;
|
||||
TWOnode *pNode;
|
||||
TWOelem *pElem;
|
||||
TWOedge *pEdge;
|
||||
TWOcontact *pC;
|
||||
TWOmaterial *info;
|
||||
|
||||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
|
||||
pElem = pDevice->elements[ eIndex ];
|
||||
info = pElem->matlInfo;
|
||||
|
||||
pElem->dx = xScale[ pElem->pTRNode->nodeI ]-xScale[ pElem->pTLNode->nodeI ];
|
||||
pElem->dy = yScale[ pElem->pBLNode->nodeJ ]-yScale[ pElem->pTLNode->nodeJ ];
|
||||
pElem->epsRel = info->eps;
|
||||
|
||||
if ( pElem->elemType == INSULATOR ) {
|
||||
for ( index = 0; index <= 3; 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 <= 3; index++ ) {
|
||||
if ( pElem->evalNodes[ index ] ) {
|
||||
pNode = pElem->pNodes[ index ];
|
||||
|
||||
/* 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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ( index = 0; index <= 3; index++ ) {
|
||||
if ( pElem->evalEdges[ index ] ) {
|
||||
pEdge = pElem->pEdges[ index ];
|
||||
|
||||
/* Fixed Interface Charge */
|
||||
pEdge->qf = 0.0;
|
||||
|
||||
/* Variable Band Built-In Potential */
|
||||
if ( index <= 1 ) {
|
||||
dBand = psiBand[index+1] - psiBand[index];
|
||||
dNie = log( pElem->pNodes[index+1]->nie /
|
||||
pElem->pNodes[index]->nie );
|
||||
pEdge->dCBand = dBand + dNie;
|
||||
pEdge->dVBand = - dBand + dNie;
|
||||
} else {
|
||||
dBand = psiBand[index] - psiBand[(index+1)%4];
|
||||
dNie = log( pElem->pNodes[index]->nie /
|
||||
pElem->pNodes[(index+1)%4]->nie );
|
||||
pEdge->dCBand = dBand + dNie;
|
||||
pEdge->dVBand = - dBand + dNie;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Evaluate conc.-dep. mobility. */
|
||||
/* Average conc of all four nodes. */
|
||||
avgConc = 0.25*(pElem->pTLNode->totalConc + pElem->pTRNode->totalConc +
|
||||
pElem->pBRNode->totalConc + pElem->pBLNode->totalConc);
|
||||
MOBconcDep( info, avgConc, &pElem->mun0, &pElem->mup0 );
|
||||
}
|
||||
}
|
||||
|
||||
for ( pC = pDevice->pFirstContact; pC != NIL(TWOcontact); pC = pC->next ) {
|
||||
numContactNodes = pC->numNodes;
|
||||
for ( index = 0; index < numContactNodes; index++ ) {
|
||||
pNode = pC->pNodes[ index ];
|
||||
pNode->eaff = pC->workf; /* Affinity aka work function */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Transfer BC info from card to nodes and edges. */
|
||||
static void TWOcopyBCinfo( pDevice, pElem, card, index )
|
||||
TWOdevice *pDevice;
|
||||
TWOelem *pElem;
|
||||
BDRYcard *card;
|
||||
int index;
|
||||
{
|
||||
TWOnode *pNode;
|
||||
TWOelem *pNElem;
|
||||
TWOedge *pEdge;
|
||||
TWOmaterial *info;
|
||||
TWOchannel *newChannel;
|
||||
int eIndex, nIndex;
|
||||
int direction = index%2;
|
||||
double length, area, width, layerWidth;
|
||||
double dop, na = 0.0, nd = 0.0;
|
||||
|
||||
/* First add fixed charge. */
|
||||
pEdge = pElem->pEdges[index];
|
||||
pEdge->qf += card->BDRYqf;
|
||||
|
||||
/* Now add surface recombination. */
|
||||
if ( direction == 0 ) { /* Horizontal Edge */
|
||||
length = 0.5 * pElem->dx;
|
||||
} else {
|
||||
length = 0.5 * pElem->dy;
|
||||
}
|
||||
for (nIndex = 0; nIndex <= 1; nIndex++) {
|
||||
pNode = pElem->pNodes[ (index+nIndex)%4 ];
|
||||
/* Compute semiconductor area around this node. */
|
||||
area = 0.0;
|
||||
for (eIndex = 0; eIndex <= 3; eIndex++) {
|
||||
pNElem = pNode->pElems[eIndex];
|
||||
if (pNElem != NIL(TWOelem) && pElem->elemType == SEMICON) {
|
||||
area += 0.25 * pElem->dx * pElem->dy;
|
||||
}
|
||||
}
|
||||
if (card->BDRYsnGiven) {
|
||||
pNode->tn = pNode->tn /
|
||||
(1.0 + ((card->BDRYsn * TNorm)*length*pNode->tn) / area);
|
||||
}
|
||||
if (card->BDRYspGiven) {
|
||||
pNode->tp = pNode->tp /
|
||||
(1.0 + ((card->BDRYsp * TNorm)*length*pNode->tp) / area);
|
||||
}
|
||||
/* Compute doping just in case we need it later. */
|
||||
na += 0.5 * pNode->na;
|
||||
nd += 0.5 * pNode->nd;
|
||||
}
|
||||
|
||||
/* Finally do surface layer. */
|
||||
pNElem = pElem->pElems[index];
|
||||
if (card->BDRYlayerGiven && SurfaceMobility && pElem->elemType == SEMICON
|
||||
&& pElem->channel == 0 && pNElem && pNElem->elemType == INSULATOR
|
||||
&& pElem->pNodes[index]->nodeType != CONTACT &&
|
||||
pElem->pNodes[(index+1)%4]->nodeType != CONTACT ) {
|
||||
/* Find the layer width. */
|
||||
layerWidth = card->BDRYlayer;
|
||||
if (card->BDRYlayer <= 0.0) { /* Need to compute extrinsic Debye length */
|
||||
info = pElem->matlInfo;
|
||||
dop = MAX(MAX(na,nd),info->ni0);
|
||||
layerWidth = sqrt((Vt * info->eps) / (CHARGE * dop));
|
||||
}
|
||||
|
||||
/* Add a channel to the list of channels. */
|
||||
XCALLOC( newChannel, TWOchannel, 1);
|
||||
newChannel->pSeed = pElem;
|
||||
newChannel->pNElem = pNElem;
|
||||
newChannel->type = index;
|
||||
if (pDevice->pChannel != NIL(TWOchannel)) {
|
||||
newChannel->id = pDevice->pChannel->id + 1;
|
||||
newChannel->next = pDevice->pChannel;
|
||||
} else {
|
||||
newChannel->id = 1;
|
||||
newChannel->next = NIL(TWOchannel);
|
||||
}
|
||||
pDevice->pChannel = newChannel;
|
||||
|
||||
/* Now add elements to channel until we're more than layerWidth away
|
||||
* from the interface. If we encounter a missing element or an
|
||||
* element that's already part of a different channel, quit.
|
||||
* The seed element is at the surface.
|
||||
*/
|
||||
width = 0.0;
|
||||
eIndex = (index+2)%4;
|
||||
pElem->surface = TRUE;
|
||||
while (width < layerWidth && pElem && pElem->channel == 0) {
|
||||
pElem->channel = newChannel->id;
|
||||
pElem->direction = direction;
|
||||
/*
|
||||
* Surface mobility is normally concentration-independent in
|
||||
* the default model. Overwrite concentration-dependent value
|
||||
* calculated earlier unless matching of low-field surface
|
||||
* and bulk mobilities is requested.
|
||||
*/
|
||||
if (!MatchingMobility) {
|
||||
pElem->mun0 = pElem->matlInfo->mus[ELEC];
|
||||
pElem->mup0 = pElem->matlInfo->mus[HOLE];
|
||||
}
|
||||
if ( direction == 0 ) {
|
||||
width += pElem->dy;
|
||||
} else {
|
||||
width += pElem->dx;
|
||||
}
|
||||
pElem = pElem->pElems[ eIndex ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Compute boundary condition parameters. */
|
||||
void TWOsetBCparams(TWOdevice *pDevice, BDRYcard *cardList)
|
||||
{
|
||||
int index, xIndex, yIndex; /* Need to access in X/Y order. */
|
||||
TWOnode *pNode;
|
||||
TWOelem *pElem, *pNElem;
|
||||
TWOedge *pEdge;
|
||||
BDRYcard *card;
|
||||
|
||||
for ( card = cardList; card != NIL(BDRYcard); card = card->BDRYnextCard ) {
|
||||
for (xIndex = card->BDRYixLow; xIndex < card->BDRYixHigh; xIndex++) {
|
||||
for (yIndex = card->BDRYiyLow; yIndex < card->BDRYiyHigh; yIndex++) {
|
||||
pElem = pDevice->elemArray[ xIndex ][ yIndex ];
|
||||
if (pElem != NIL(TWOelem)) {
|
||||
if (pElem->domain == card->BDRYdomain) {
|
||||
for (index = 0; index <= 3; index++) {
|
||||
if (pElem->evalEdges[index]) {
|
||||
pNElem = pElem->pElems[index];
|
||||
if (card->BDRYneighborGiven) {
|
||||
if (pNElem && pNElem->domain == card->BDRYneighbor) {
|
||||
/* Found an interface edge. */
|
||||
TWOcopyBCinfo( pDevice, pElem, card, index );
|
||||
}
|
||||
} else {
|
||||
if (!pNElem || pNElem->domain != pElem->domain) {
|
||||
/* Found a boundary edge. */
|
||||
TWOcopyBCinfo( pDevice, pElem, card, index );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TWOnormalize(TWOdevice *pDevice)
|
||||
{
|
||||
int index, eIndex;
|
||||
TWOelem *pElem;
|
||||
TWOnode *pNode;
|
||||
TWOedge *pEdge;
|
||||
|
||||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
|
||||
pElem = pDevice->elements[ eIndex ];
|
||||
|
||||
pElem->dx /= LNorm;
|
||||
pElem->dy /= LNorm;
|
||||
pElem->epsRel /= EpsNorm;
|
||||
for ( index = 0; index <= 3; index++ ) {
|
||||
if ( pElem->evalNodes[ index ] ) {
|
||||
pNode = pElem->pNodes[ index ];
|
||||
pNode->netConc /= NNorm;
|
||||
pNode->nd /= NNorm;
|
||||
pNode->na /= NNorm;
|
||||
pNode->nie /= NNorm;
|
||||
pNode->eg /= VNorm;
|
||||
pNode->eaff /= VNorm;
|
||||
}
|
||||
if ( pElem->evalEdges[ index ] ) {
|
||||
pEdge = pElem->pEdges[ index ];
|
||||
pEdge->qf /= NNorm*LNorm;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue