Cider simulator (simulator routines) Import.

This commit is contained in:
pnenzi 2003-08-11 19:25:28 +00:00
parent dd8929457e
commit f5f8602d3a
87 changed files with 24864 additions and 0 deletions

7
src/ciderlib/Makefile.am Normal file
View File

@ -0,0 +1,7 @@
## Process this file with automake
EXTRA_DIST = notes
SUBDIRS = input support oned twod
MAINTAINERCLEANFILES = Makefile.in

View File

@ -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

View File

@ -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 );
}

View File

@ -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);
}

View File

@ -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,
&REGNinfo,
&OPTNinfo,
&OUTPinfo
};
int INPnumCards = sizeof(INPcardTab)/sizeof(IFcardInfo*);

View File

@ -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);
}

View File

@ -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 );
}

133
src/ciderlib/input/domain.c Normal file
View File

@ -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);
}

View File

@ -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 );
}

286
src/ciderlib/input/doping.c Normal file
View File

@ -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);
}

393
src/ciderlib/input/dopset.c Normal file
View File

@ -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 );
}

View File

@ -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 );
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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 );
}

148
src/ciderlib/input/mesh.c Normal file
View File

@ -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);
}

1302
src/ciderlib/input/meshset.c Normal file

File diff suppressed because it is too large Load Diff

114
src/ciderlib/input/method.c Normal file
View File

@ -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);
}

View File

@ -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);
}

157
src/ciderlib/input/mobset.c Normal file
View File

@ -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 );
}

133
src/ciderlib/input/models.c Normal file
View File

@ -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);
}

View File

@ -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 );
}

View File

@ -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);
}

View File

@ -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 );
}

241
src/ciderlib/input/output.c Normal file
View File

@ -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);
}

View File

@ -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.

9
src/ciderlib/notes Normal file
View File

@ -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.

View File

@ -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

36
src/ciderlib/oned/notes Normal file
View File

@ -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)

View File

@ -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);
}

172
src/ciderlib/oned/oneaval.c Normal file
View File

@ -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);
}

245
src/ciderlib/oned/onecond.c Normal file
View File

@ -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;
}

657
src/ciderlib/oned/onecont.c Normal file
View File

@ -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;
}
}
}

View File

@ -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

View File

@ -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);
}

121
src/ciderlib/oned/onedext.h Normal file
View File

@ -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

View File

@ -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;
}
}
}
}
}
}
}
}

View File

@ -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;
}
}

371
src/ciderlib/oned/onemesh.c Normal file
View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}

348
src/ciderlib/oned/oneproj.c Normal file
View File

@ -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;
*/
}

View File

@ -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);
}

View File

@ -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;
}
}
}
}

1084
src/ciderlib/oned/onesolve.c Normal file

File diff suppressed because it is too large Load Diff

13
src/ciderlib/oned/readme Normal file
View File

@ -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 '_'.

View File

@ -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

View File

@ -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. */
}
}

View File

@ -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;
}
}

View File

@ -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 );
}
}

View File

@ -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;
}

View File

@ -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 ] );
}
}
*/

View File

@ -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 );
}

View File

@ -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;
}
}

View File

@ -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;
}

161
src/ciderlib/support/misc.c Normal file
View File

@ -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 );
}

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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" );
}
}
*/

View File

@ -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;
}

View File

@ -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

15
src/ciderlib/twod/readme Normal file
View File

@ -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'.

1316
src/ciderlib/twod/twoadmit.c Normal file

File diff suppressed because it is too large Load Diff

208
src/ciderlib/twod/twoaval.c Normal file
View File

@ -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 );
}

612
src/ciderlib/twod/twocond.c Normal file
View File

@ -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;
}

1052
src/ciderlib/twod/twocont.c Normal file

File diff suppressed because it is too large Load Diff

183
src/ciderlib/twod/twocurr.c Normal file
View File

@ -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;
}
}
}

View File

@ -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

View File

@ -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 );
}

169
src/ciderlib/twod/twodext.h Normal file
View File

@ -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

View File

@ -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;
}
}
}
}
}
}
}
}

View File

@ -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;
}

View File

@ -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;
}
}

626
src/ciderlib/twod/twomesh.c Normal file
View File

@ -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;
}
}
}

1399
src/ciderlib/twod/twomobdv.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}
}
}
}
}
}

View File

@ -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;
}
}
}
}
}
}

View File

@ -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;
}
}
}
}
}

View File

@ -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);
}

679
src/ciderlib/twod/twoproj.c Normal file
View File

@ -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;
}
}
}
}
}
}
}

116
src/ciderlib/twod/tworead.c Normal file
View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}
}

1197
src/ciderlib/twod/twosolve.c Normal file

File diff suppressed because it is too large Load Diff