87 changed files with 24864 additions and 0 deletions
-
7src/ciderlib/Makefile.am
-
33src/ciderlib/input/Makefile.am
-
208src/ciderlib/input/bdryset.c
-
157src/ciderlib/input/boundary.c
-
46src/ciderlib/input/cards.c
-
130src/ciderlib/input/contact.c
-
87src/ciderlib/input/contset.c
-
133src/ciderlib/input/domain.c
-
207src/ciderlib/input/domnset.c
-
286src/ciderlib/input/doping.c
-
393src/ciderlib/input/dopset.c
-
177src/ciderlib/input/elctset.c
-
118src/ciderlib/input/electrod.c
-
305src/ciderlib/input/material.c
-
180src/ciderlib/input/matlset.c
-
148src/ciderlib/input/mesh.c
-
1302src/ciderlib/input/meshset.c
-
114src/ciderlib/input/method.c
-
210src/ciderlib/input/mobility.c
-
157src/ciderlib/input/mobset.c
-
133src/ciderlib/input/models.c
-
92src/ciderlib/input/modlset.c
-
163src/ciderlib/input/options.c
-
148src/ciderlib/input/outpset.c
-
241src/ciderlib/input/output.c
-
6src/ciderlib/input/readme
-
9src/ciderlib/notes
-
23src/ciderlib/oned/Makefile.am
-
36src/ciderlib/oned/notes
-
742src/ciderlib/oned/oneadmit.c
-
172src/ciderlib/oned/oneaval.c
-
245src/ciderlib/oned/onecond.c
-
657src/ciderlib/oned/onecont.c
-
29src/ciderlib/oned/oneddefs.h
-
74src/ciderlib/oned/onedest.c
-
121src/ciderlib/oned/onedext.h
-
166src/ciderlib/oned/onedopng.c
-
121src/ciderlib/oned/onefreez.c
-
371src/ciderlib/oned/onemesh.c
-
192src/ciderlib/oned/onepoiss.c
-
534src/ciderlib/oned/oneprint.c
-
348src/ciderlib/oned/oneproj.c
-
94src/ciderlib/oned/oneread.c
-
233src/ciderlib/oned/onesetup.c
-
1084src/ciderlib/oned/onesolve.c
-
13src/ciderlib/oned/readme
-
22src/ciderlib/support/Makefile.am
-
72src/ciderlib/support/database.c
-
74src/ciderlib/support/devprint.c
-
130src/ciderlib/support/geominfo.c
-
161src/ciderlib/support/globals.c
-
155src/ciderlib/support/integset.c
-
302src/ciderlib/support/integuse.c
-
41src/ciderlib/support/logfile.c
-
404src/ciderlib/support/mater.c
-
161src/ciderlib/support/misc.c
-
421src/ciderlib/support/mobil.c
-
9src/ciderlib/support/readme
-
50src/ciderlib/support/recomb.c
-
210src/ciderlib/support/suprem.c
-
357src/ciderlib/support/suprmitf.c
-
31src/ciderlib/twod/Makefile.am
-
15src/ciderlib/twod/readme
-
1316src/ciderlib/twod/twoadmit.c
-
208src/ciderlib/twod/twoaval.c
-
612src/ciderlib/twod/twocond.c
-
1052src/ciderlib/twod/twocont.c
-
183src/ciderlib/twod/twocurr.c
-
57src/ciderlib/twod/twoddefs.h
-
76src/ciderlib/twod/twodest.c
-
169src/ciderlib/twod/twodext.h
-
234src/ciderlib/twod/twodopng.c
-
180src/ciderlib/twod/twoelect.c
-
125src/ciderlib/twod/twofield.c
-
626src/ciderlib/twod/twomesh.c
-
1399src/ciderlib/twod/twomobdv.c
-
411src/ciderlib/twod/twomobfn.c
-
131src/ciderlib/twod/twomobil.c
-
889src/ciderlib/twod/twoncont.c
-
888src/ciderlib/twod/twopcont.c
-
283src/ciderlib/twod/twopoiss.c
-
561src/ciderlib/twod/twoprint.c
-
679src/ciderlib/twod/twoproj.c
-
116src/ciderlib/twod/tworead.c
-
94src/ciderlib/twod/twosetbc.c
-
318src/ciderlib/twod/twosetup.c
-
1197src/ciderlib/twod/twosolve.c
@ -0,0 +1,7 @@ |
|||
## Process this file with automake
|
|||
|
|||
EXTRA_DIST = notes |
|||
|
|||
SUBDIRS = input support oned twod |
|||
|
|||
MAINTAINERCLEANFILES = Makefile.in |
|||
@ -0,0 +1,33 @@ |
|||
## Process this file with automake to produce Makefile.in
|
|||
|
|||
noinst_LIBRARIES = libciderinput.a |
|||
|
|||
libciderinput_a_SOURCES = \
|
|||
bdryset.c \
|
|||
boundary.c \
|
|||
cards.c \
|
|||
contact.c \
|
|||
contset.c \
|
|||
domain.c \
|
|||
domnset.c \
|
|||
doping.c \
|
|||
dopset.c \
|
|||
elctset.c \
|
|||
electrod.c \
|
|||
material.c \
|
|||
matlset.c \
|
|||
mesh.c \
|
|||
meshset.c \
|
|||
method.c \
|
|||
mobility.c \
|
|||
mobset.c \
|
|||
models.c \
|
|||
modlset.c \
|
|||
options.c \
|
|||
outpset.c \
|
|||
output.c |
|||
|
|||
|
|||
EXTRA_DIST = readme |
|||
INCLUDES = -I$(top_srcdir)/src/include |
|||
MAINTAINERCLEANFILES = Makefile.in |
|||
@ -0,0 +1,208 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group |
|||
Modified: 2001 Paolo Nenzi |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "cktdefs.h" |
|||
#include "bdrydefs.h" |
|||
#include "meshext.h" |
|||
#include "gendev.h" |
|||
#include "sperror.h" |
|||
|
|||
extern int BDRYcheck( BDRYcard *, DOMNdomain * ); |
|||
extern int BDRYsetup( BDRYcard *, MESHcoord *, MESHcoord *, DOMNdomain * ); |
|||
|
|||
|
|||
/* |
|||
* Name: BDRYcheck |
|||
* Purpose: checks a list of BDRYcards for input errors |
|||
* Formals: cardList: the list to check |
|||
* Returns: OK/E_PRIVATE |
|||
* Users: numerical device setup routines |
|||
* Calls: error message handler |
|||
*/ |
|||
int |
|||
BDRYcheck(BDRYcard *cardList, DOMNdomain *domnList) |
|||
{ |
|||
BDRYcard *card; |
|||
DOMNdomain *domn; |
|||
int cardNum = 0; |
|||
int error = OK; |
|||
char ebuf[512]; /* error message buffer */ |
|||
|
|||
for ( card = cardList; card != NIL(BDRYcard); card = card->BDRYnextCard ) { |
|||
cardNum++; |
|||
if (card->BDRYxLowGiven && card->BDRYixLowGiven) { |
|||
sprintf( ebuf, |
|||
"boundary card %d uses both location and index - location ignored", |
|||
cardNum ); |
|||
SPfrontEnd->IFerror( ERR_INFO, ebuf, NIL(IFuid) ); |
|||
card->BDRYxLowGiven = FALSE; |
|||
} |
|||
if (card->BDRYxHighGiven && card->BDRYixHighGiven) { |
|||
sprintf( ebuf, |
|||
"boundary card %d uses both location and index - location ignored", |
|||
cardNum ); |
|||
SPfrontEnd->IFerror( ERR_INFO, ebuf, NIL(IFuid) ); |
|||
card->BDRYxHighGiven = FALSE; |
|||
} |
|||
if (card->BDRYyLowGiven && card->BDRYiyLowGiven) { |
|||
sprintf( ebuf, |
|||
"boundary card %d uses both location and index - location ignored", |
|||
cardNum ); |
|||
SPfrontEnd->IFerror( ERR_INFO, ebuf, NIL(IFuid) ); |
|||
card->BDRYyLowGiven = FALSE; |
|||
} |
|||
if (card->BDRYyHighGiven && card->BDRYiyHighGiven) { |
|||
sprintf( ebuf, |
|||
"boundary card %d uses both location and index - location ignored", |
|||
cardNum ); |
|||
SPfrontEnd->IFerror( ERR_INFO, ebuf, NIL(IFuid) ); |
|||
card->BDRYyHighGiven = FALSE; |
|||
} |
|||
if (!card->BDRYdomainGiven) { |
|||
sprintf( ebuf, |
|||
"boundary card %d is missing a domain index", |
|||
cardNum ); |
|||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) ); |
|||
error = E_PRIVATE; |
|||
} else { |
|||
/* Make sure the domain exists */ |
|||
for ( domn = domnList; domn != NIL(DOMNdomain); domn = domn->next ) { |
|||
if ( card->BDRYdomain == domn->id ) { |
|||
break; |
|||
} |
|||
} |
|||
if (domn == NIL(DOMNdomain)) { |
|||
sprintf( ebuf, |
|||
"boundary card %d specifies a non-existent domain", |
|||
cardNum ); |
|||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) ); |
|||
error = E_PRIVATE; |
|||
} |
|||
} |
|||
|
|||
if (!card->BDRYneighborGiven) { |
|||
card->BDRYneighbor = card->BDRYdomain; |
|||
} else { |
|||
/* Make sure the neighbor exists */ |
|||
for ( domn = domnList; domn != NIL(DOMNdomain); domn = domn->next ) { |
|||
if ( card->BDRYneighbor == domn->id ) { |
|||
break; |
|||
} |
|||
} |
|||
if (domn == NIL(DOMNdomain)) { |
|||
sprintf( ebuf, |
|||
"interface card %d specifies a non-existent domain", |
|||
cardNum ); |
|||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) ); |
|||
error = E_PRIVATE; |
|||
} |
|||
} |
|||
|
|||
if (!card->BDRYqfGiven) { |
|||
card->BDRYqf = 0.0; |
|||
} |
|||
if (!card->BDRYsnGiven) { |
|||
card->BDRYsn = 0.0; |
|||
} |
|||
if (!card->BDRYspGiven) { |
|||
card->BDRYsp = 0.0; |
|||
} |
|||
if (!card->BDRYlayerGiven) { |
|||
card->BDRYlayer = 0.0; |
|||
} |
|||
|
|||
/* Return now if anything has failed */ |
|||
if (error) return(error); |
|||
} |
|||
return(OK); |
|||
} |
|||
|
|||
|
|||
|
|||
/* |
|||
* Name: BDRYsetup |
|||
* Purpose: Checks BDRY cards and then sets the indices |
|||
* Formals: cardList: list of cards to setup, returns with indices set |
|||
* xMeshList: list of coordinates in the x mesh |
|||
* yMeshList: list of coordinates in the y mesh |
|||
* Returns: OK/E_PRIVATE |
|||
* Users: numerical devices |
|||
* Calls: BDRYcheck |
|||
*/ |
|||
int |
|||
BDRYsetup(BDRYcard *cardList, MESHcoord *xMeshList, MESHcoord *yMeshList,DOMNdomain *domnList) |
|||
{ |
|||
BDRYcard *card; |
|||
int ixMin, ixMax, iyMin, iyMax; |
|||
int cardNum = 0; |
|||
int error; |
|||
char ebuf[512]; /* error message buffer */ |
|||
|
|||
/* Check the card list */ |
|||
if ((error = BDRYcheck( cardList, domnList ))) return( error ); |
|||
|
|||
/* Find the limits on the indices */ |
|||
MESHiBounds( xMeshList, &ixMin, &ixMax ); |
|||
MESHiBounds( yMeshList, &iyMin, &iyMax ); |
|||
|
|||
error = OK; |
|||
for ( card = cardList; card != NIL(BDRYcard); card = card->BDRYnextCard ) { |
|||
cardNum++; |
|||
|
|||
if (card->BDRYixLowGiven) { |
|||
card->BDRYixLow = MAX(card->BDRYixLow, ixMin); |
|||
} |
|||
else if (card->BDRYxLowGiven) { |
|||
card->BDRYixLow = MESHlocate( xMeshList, card->BDRYxLow ); |
|||
} |
|||
else { |
|||
card->BDRYixLow = ixMin; |
|||
} |
|||
if (card->BDRYixHighGiven) { |
|||
card->BDRYixHigh = MIN(card->BDRYixHigh, ixMax); |
|||
} |
|||
else if (card->BDRYxHighGiven) { |
|||
card->BDRYixHigh = MESHlocate( xMeshList, card->BDRYxHigh ); |
|||
} |
|||
else { |
|||
card->BDRYixHigh = ixMax; |
|||
} |
|||
if (card->BDRYixLow > card->BDRYixHigh) { |
|||
sprintf( ebuf, |
|||
"boundary card %d has low x index (%d) > high x index (%d)", |
|||
cardNum, card->BDRYixHigh, card->BDRYixLow ); |
|||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) ); |
|||
error = E_PRIVATE; |
|||
} |
|||
if (card->BDRYiyLowGiven) { |
|||
card->BDRYiyLow = MAX(card->BDRYiyLow, iyMin); |
|||
} |
|||
else if (card->BDRYyLowGiven) { |
|||
card->BDRYiyLow = MESHlocate( yMeshList, card->BDRYyLow ); |
|||
} |
|||
else { |
|||
card->BDRYiyLow = iyMin; |
|||
} |
|||
if (card->BDRYiyHighGiven) { |
|||
card->BDRYiyHigh = MIN(card->BDRYiyHigh, iyMax); |
|||
} |
|||
else if (card->BDRYyHighGiven) { |
|||
card->BDRYiyHigh = MESHlocate( yMeshList, card->BDRYyHigh ); |
|||
} |
|||
else { |
|||
card->BDRYiyHigh = iyMax; |
|||
} |
|||
if (card->BDRYiyLow > card->BDRYiyHigh) { |
|||
sprintf( ebuf, |
|||
"boundary card %d has low y index (%d) > high y index (%d)", |
|||
cardNum, card->BDRYiyHigh, card->BDRYiyLow ); |
|||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) ); |
|||
error = E_PRIVATE; |
|||
} |
|||
} |
|||
return( error ); |
|||
} |
|||
@ -0,0 +1,157 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group |
|||
Modified: 2001 Paolo Nenzi |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numcards.h" |
|||
#include "numgen.h" |
|||
#include "bdrydefs.h" |
|||
#include "devdefs.h" |
|||
#include "sperror.h" |
|||
#include "suffix.h" |
|||
|
|||
#define UM_TO_CM 1.0e-4 |
|||
|
|||
extern int BDRYnewCard(void**,void*); |
|||
extern int BDRYparam(int,IFvalue*,void*); |
|||
|
|||
IFparm BDRYpTable[] = { |
|||
IP("domain", BDRY_DOMAIN, IF_INTEGER, "Primary domain"), |
|||
IP("neighbor",BDRY_NEIGHBOR, IF_INTEGER, "Neighboring domain"), |
|||
IP("x.low", BDRY_X_LOW, IF_REAL, "Location of left edge"), |
|||
IP("x.high", BDRY_X_HIGH, IF_REAL, "Location of right edge"), |
|||
IP("y.low", BDRY_Y_LOW, IF_REAL, "Location of top edge"), |
|||
IP("y.high", BDRY_Y_HIGH, IF_REAL, "Location of bottom edge"), |
|||
IP("ix.low", BDRY_IX_LOW, IF_INTEGER, "Index of left edge"), |
|||
IP("ix.high", BDRY_IX_HIGH, IF_INTEGER, "Index of right edge"), |
|||
IP("iy.low", BDRY_IY_LOW, IF_INTEGER, "Index of top edge"), |
|||
IP("iy.high", BDRY_IY_HIGH, IF_INTEGER, "Index of bottom edge"), |
|||
IP("nss", BDRY_QF, IF_REAL, "Fixed charge"), |
|||
IP("qss", BDRY_QF, IF_REAL, "Fixed charge"), |
|||
IP("qf", BDRY_QF, IF_REAL, "Fixed charge"), |
|||
IP("sn", BDRY_SN, IF_REAL, "Electron recomb velocity"), |
|||
IP("srvn", BDRY_SN, IF_REAL, "Electron recomb velocity"), |
|||
IP("vsrfn", BDRY_SN, IF_REAL, "Electron recomb velocity"), |
|||
IP("sp", BDRY_SP, IF_REAL, "Hole recomb velocity"), |
|||
IP("srvp", BDRY_SP, IF_REAL, "Hole recomb velocity"), |
|||
IP("vsrfp", BDRY_SP, IF_REAL, "Hole recomb velocity"), |
|||
IP("layer.width",BDRY_LAYER, IF_REAL, "Width of surface charge layer") |
|||
}; |
|||
|
|||
IFcardInfo BDRYinfo = { |
|||
"boundary", |
|||
"Specify properties of a domain boundary", |
|||
NUMELEMS(BDRYpTable), |
|||
BDRYpTable, |
|||
|
|||
BDRYnewCard, |
|||
BDRYparam, |
|||
NULL |
|||
}; |
|||
IFcardInfo INTFinfo = { |
|||
"interface", |
|||
"Specify properties of an interface between two domains", |
|||
NUMELEMS(BDRYpTable), |
|||
BDRYpTable, |
|||
|
|||
BDRYnewCard, |
|||
BDRYparam, |
|||
NULL |
|||
}; |
|||
|
|||
int |
|||
BDRYnewCard(void **inCard, void *inModel) |
|||
{ |
|||
BDRYcard *tmpCard, *newCard; |
|||
GENnumModel *model = (GENnumModel *)inModel; |
|||
|
|||
newCard = NEW( BDRYcard ); |
|||
if (!newCard) { |
|||
*inCard = (void *)NULL; |
|||
return(E_NOMEM); |
|||
} |
|||
newCard->BDRYnextCard = (BDRYcard *)NULL; |
|||
*inCard = (void *)newCard; |
|||
|
|||
tmpCard = model->GENboundaries; |
|||
if (!tmpCard) { /* First in list */ |
|||
model->GENboundaries = newCard; |
|||
} else { |
|||
/* Go to end of list */ |
|||
while (tmpCard->BDRYnextCard) tmpCard = tmpCard->BDRYnextCard; |
|||
/* And add new card */ |
|||
tmpCard->BDRYnextCard = newCard; |
|||
} |
|||
return(OK); |
|||
} |
|||
|
|||
int |
|||
BDRYparam(int param, IFvalue *value, void *inCard) |
|||
{ |
|||
BDRYcard *card = (BDRYcard *)inCard; |
|||
|
|||
switch (param) { |
|||
case BDRY_DOMAIN: |
|||
card->BDRYdomain = value->iValue; |
|||
card->BDRYdomainGiven = TRUE; |
|||
break; |
|||
case BDRY_NEIGHBOR: |
|||
card->BDRYneighbor = value->iValue; |
|||
card->BDRYneighborGiven = TRUE; |
|||
break; |
|||
case BDRY_X_LOW: |
|||
card->BDRYxLow = value->rValue * UM_TO_CM; |
|||
card->BDRYxLowGiven = TRUE; |
|||
break; |
|||
case BDRY_X_HIGH: |
|||
card->BDRYxHigh = value->rValue * UM_TO_CM; |
|||
card->BDRYxHighGiven = TRUE; |
|||
break; |
|||
case BDRY_Y_LOW: |
|||
card->BDRYyLow = value->rValue * UM_TO_CM; |
|||
card->BDRYyLowGiven = TRUE; |
|||
break; |
|||
case BDRY_Y_HIGH: |
|||
card->BDRYyHigh = value->rValue * UM_TO_CM; |
|||
card->BDRYyHighGiven = TRUE; |
|||
break; |
|||
case BDRY_IX_LOW: |
|||
card->BDRYixLow = value->iValue; |
|||
card->BDRYixLowGiven = TRUE; |
|||
break; |
|||
case BDRY_IX_HIGH: |
|||
card->BDRYixHigh = value->iValue; |
|||
card->BDRYixHighGiven = TRUE; |
|||
break; |
|||
case BDRY_IY_LOW: |
|||
card->BDRYiyLow = value->iValue; |
|||
card->BDRYiyLowGiven = TRUE; |
|||
break; |
|||
case BDRY_IY_HIGH: |
|||
card->BDRYiyHigh = value->iValue; |
|||
card->BDRYiyHighGiven = TRUE; |
|||
break; |
|||
case BDRY_QF: |
|||
card->BDRYqf = value->rValue; |
|||
card->BDRYqfGiven = TRUE; |
|||
break; |
|||
case BDRY_SN: |
|||
card->BDRYsn = value->rValue; |
|||
card->BDRYsnGiven = TRUE; |
|||
break; |
|||
case BDRY_SP: |
|||
card->BDRYsp = value->rValue; |
|||
card->BDRYspGiven = TRUE; |
|||
break; |
|||
case BDRY_LAYER: |
|||
card->BDRYlayer = value->rValue; |
|||
card->BDRYlayerGiven = TRUE; |
|||
break; |
|||
default: |
|||
return(E_BADPARM); |
|||
break; |
|||
} |
|||
return(OK); |
|||
} |
|||
@ -0,0 +1,46 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group |
|||
Modified: 2001 Paolo Nenzi |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numcards.h" |
|||
|
|||
extern IFcardInfo CONTinfo; |
|||
extern IFcardInfo DOPinfo; |
|||
extern IFcardInfo ELCTinfo; |
|||
extern IFcardInfo BDRYinfo; |
|||
extern IFcardInfo INTFinfo; |
|||
extern IFcardInfo XMSHinfo; |
|||
extern IFcardInfo YMSHinfo; |
|||
extern IFcardInfo METHinfo; |
|||
extern IFcardInfo MOBinfo; |
|||
extern IFcardInfo MODLinfo; |
|||
extern IFcardInfo PHYSinfo; |
|||
extern IFcardInfo MATLinfo; |
|||
extern IFcardInfo DOMNinfo; |
|||
extern IFcardInfo REGNinfo; |
|||
extern IFcardInfo OPTNinfo; |
|||
extern IFcardInfo OUTPinfo; |
|||
|
|||
IFcardInfo *INPcardTab[] = { |
|||
&CONTinfo, |
|||
&DOPinfo, |
|||
&ELCTinfo, |
|||
&BDRYinfo, |
|||
&INTFinfo, |
|||
&XMSHinfo, |
|||
&YMSHinfo, |
|||
&METHinfo, |
|||
&MOBinfo, |
|||
&MODLinfo, |
|||
&PHYSinfo, |
|||
&MATLinfo, |
|||
&DOMNinfo, |
|||
®Ninfo, |
|||
&OPTNinfo, |
|||
&OUTPinfo |
|||
}; |
|||
|
|||
int INPnumCards = sizeof(INPcardTab)/sizeof(IFcardInfo*); |
|||
@ -0,0 +1,130 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group |
|||
Modified: 2001 Paolo Nenzi |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numcards.h" |
|||
#include "numgen.h" |
|||
#include "contdefs.h" |
|||
#include "devdefs.h" |
|||
#include "sperror.h" |
|||
#include "suffix.h" |
|||
|
|||
extern int CONTnewCard(void**,void*); |
|||
extern int CONTparam(int,IFvalue*,void*); |
|||
|
|||
|
|||
IFparm CONTpTable[] = { |
|||
IP("neutral", CONT_NEUTRAL, IF_FLAG, "Charge neutral"), |
|||
IP("aluminum", CONT_ALUMINUM, IF_FLAG, "Aluminum contact"), |
|||
IP("p.polysilicon", CONT_P_POLY, IF_FLAG, "P poly contact"), |
|||
IP("n.polysilicon", CONT_N_POLY, IF_FLAG, "N poly contact"), |
|||
IP("workfunction", CONT_WORKFUN, IF_REAL, "Metal work function"), |
|||
IP("number", CONT_NUMBER, IF_INTEGER, "Electrode ID number") |
|||
}; |
|||
|
|||
IFcardInfo CONTinfo = { |
|||
"contact", |
|||
"Properties of a contact to the device", |
|||
NUMELEMS(CONTpTable), |
|||
CONTpTable, |
|||
|
|||
CONTnewCard, |
|||
CONTparam, |
|||
NULL |
|||
}; |
|||
|
|||
int |
|||
CONTnewCard(void **inCard, void *inModel) |
|||
{ |
|||
CONTcard *tmpCard, *newCard; |
|||
GENnumModel *model = (GENnumModel *)inModel; |
|||
|
|||
newCard = NEW( CONTcard ); |
|||
if (!newCard) { |
|||
*inCard = (void *)NULL; |
|||
return(E_NOMEM); |
|||
} |
|||
newCard->CONTnextCard = (CONTcard *)NULL; |
|||
*inCard = (void *)newCard; |
|||
|
|||
tmpCard = model->GENcontacts; |
|||
if (!tmpCard) { /* First in list */ |
|||
model->GENcontacts = newCard; |
|||
} else { |
|||
/* Go to end of list */ |
|||
while (tmpCard->CONTnextCard) tmpCard = tmpCard->CONTnextCard; |
|||
/* And add new card */ |
|||
tmpCard->CONTnextCard = newCard; |
|||
} |
|||
return(OK); |
|||
} |
|||
|
|||
int |
|||
CONTparam(int param, IFvalue *value, void *inCard) |
|||
{ |
|||
CONTcard *card = (CONTcard *)inCard; |
|||
|
|||
switch (param) { |
|||
case CONT_NEUTRAL: |
|||
if ( value->iValue ) { |
|||
card->CONTtype = CONT_NEUTRAL; |
|||
card->CONTtypeGiven = TRUE; |
|||
} else { |
|||
if ( card->CONTtype == CONT_NEUTRAL ) { |
|||
card->CONTtype = -1; |
|||
card->CONTtypeGiven = FALSE; |
|||
} |
|||
} |
|||
break; |
|||
case CONT_ALUMINUM: |
|||
if ( value->iValue ) { |
|||
card->CONTtype = CONT_ALUMINUM; |
|||
card->CONTtypeGiven = TRUE; |
|||
} else { |
|||
if ( card->CONTtype == CONT_ALUMINUM ) { |
|||
card->CONTtype = -1; |
|||
card->CONTtypeGiven = FALSE; |
|||
} |
|||
} |
|||
break; |
|||
case CONT_P_POLY: |
|||
if ( value->iValue ) { |
|||
card->CONTtype = CONT_P_POLY; |
|||
card->CONTtypeGiven = TRUE; |
|||
} else { |
|||
if ( card->CONTtype == CONT_P_POLY ) { |
|||
card->CONTtype = -1; |
|||
card->CONTtypeGiven = FALSE; |
|||
} |
|||
} |
|||
break; |
|||
case CONT_N_POLY: |
|||
if ( value->iValue ) { |
|||
card->CONTtype = CONT_N_POLY; |
|||
card->CONTtypeGiven = TRUE; |
|||
} else { |
|||
if ( card->CONTtype == CONT_N_POLY ) { |
|||
card->CONTtype = -1; |
|||
card->CONTtypeGiven = FALSE; |
|||
} |
|||
} |
|||
break; |
|||
case CONT_WORKFUN: |
|||
card->CONTtype = CONT_WORKFUN; |
|||
card->CONTtypeGiven = TRUE; |
|||
card->CONTworkfun = value->rValue; |
|||
card->CONTworkfunGiven = TRUE; |
|||
break; |
|||
case CONT_NUMBER: |
|||
card->CONTnumber = value->iValue; |
|||
card->CONTnumberGiven = TRUE; |
|||
break; |
|||
default: |
|||
return(E_BADPARM); |
|||
break; |
|||
} |
|||
return(OK); |
|||
} |
|||
@ -0,0 +1,87 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group |
|||
Modified: 2001 Paolo Nenzi |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "cktdefs.h" |
|||
#include "contdefs.h" |
|||
#include "meshext.h" |
|||
#include "gendev.h" |
|||
#include "sperror.h" |
|||
#include "suffix.h" |
|||
|
|||
extern int CONTcheck( CONTcard * ); |
|||
extern int CONTsetup( CONTcard *, ELCTelectrode * ); |
|||
|
|||
|
|||
/* |
|||
* Name: CONTcheck |
|||
* Purpose: checks a list of CONTcards for input errors |
|||
* Formals: cardList: the list to check |
|||
* Returns: OK/E_PRIVATE |
|||
* Users: numerical device setup routines |
|||
* Calls: error message handler |
|||
*/ |
|||
int |
|||
CONTcheck(CONTcard *cardList) |
|||
{ |
|||
CONTcard *card; |
|||
int cardNum = 0; |
|||
int error = OK; |
|||
char ebuf[512]; /* error message buffer */ |
|||
|
|||
for ( card = cardList; card != NIL(CONTcard); card = card->CONTnextCard ) { |
|||
cardNum++; |
|||
if (!card->CONTnumberGiven) { |
|||
sprintf( ebuf, |
|||
"contact card %d is missing an electrode index", |
|||
cardNum ); |
|||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) ); |
|||
error = E_PRIVATE; |
|||
} |
|||
|
|||
/* Return now if anything has failed */ |
|||
if (error) return(error); |
|||
} |
|||
return(OK); |
|||
} |
|||
|
|||
|
|||
|
|||
/* |
|||
* Name: CONTsetup |
|||
* Purpose: copies information from list of CONTcard's to ELCTelectrode's |
|||
* Formals: cardList: list of cards to setup |
|||
* electrodeList: previously built list of ELCTelectrode's |
|||
* Returns: OK/E_PRIVATE |
|||
* Users: numerical devices |
|||
* Calls: CONTcheck |
|||
*/ |
|||
int |
|||
CONTsetup(CONTcard *cardList, ELCTelectrode *electrodeList) |
|||
{ |
|||
CONTcard *card; |
|||
ELCTelectrode *electrode; |
|||
int error; |
|||
|
|||
/* Check the card list */ |
|||
if ((error = CONTcheck( cardList ))) return( error ); |
|||
|
|||
for ( card = cardList; card != NIL(CONTcard); card = card->CONTnextCard ) { |
|||
|
|||
/* Copy workfunction to all matching electrodes */ |
|||
for ( electrode = electrodeList; electrode != NIL(ELCTelectrode); |
|||
electrode = electrode->next ) { |
|||
if ( card->CONTnumber == electrode->id ) { |
|||
if ( card->CONTworkfunGiven ) { |
|||
electrode->workf = card->CONTworkfun; |
|||
} else { |
|||
electrode->workf = 4.10 /* electron volts */; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
return( OK ); |
|||
} |
|||
@ -0,0 +1,133 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group\ |
|||
Modified: 2001 Paolo Nenzi |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numcards.h" |
|||
#include "numgen.h" |
|||
#include "numenum.h" |
|||
#include "domndefs.h" |
|||
#include "devdefs.h" |
|||
#include "sperror.h" |
|||
#include "suffix.h" |
|||
|
|||
#define UM_TO_CM 1.0e-4 |
|||
|
|||
extern int DOMNnewCard(void**,void*); |
|||
extern int DOMNparam(int,IFvalue*,void*); |
|||
|
|||
|
|||
IFparm DOMNpTable[] = { |
|||
IP("x.low", DOMN_X_LOW, IF_REAL, "Location of left edge"), |
|||
IP("x.high", DOMN_X_HIGH, IF_REAL, "Location of right edge"), |
|||
IP("y.low", DOMN_Y_LOW, IF_REAL, "Location of top edge"), |
|||
IP("y.high", DOMN_Y_HIGH, IF_REAL, "Location of bottom edge"), |
|||
IP("ix.low", DOMN_IX_LOW, IF_INTEGER, "Index of left edge"), |
|||
IP("ix.high", DOMN_IX_HIGH, IF_INTEGER, "Index of right edge"), |
|||
IP("iy.low", DOMN_IY_LOW, IF_INTEGER, "Index of top edge"), |
|||
IP("iy.high", DOMN_IY_HIGH, IF_INTEGER, "Index of bottom edge"), |
|||
IP("number", DOMN_NUMBER, IF_INTEGER, "Domain ID number"), |
|||
IP("material",DOMN_MATERIAL, IF_INTEGER, "Material ID number") |
|||
}; |
|||
|
|||
IFcardInfo DOMNinfo = { |
|||
"domain", |
|||
"Identify material-type for portion of a device", |
|||
NUMELEMS(DOMNpTable), |
|||
DOMNpTable, |
|||
|
|||
DOMNnewCard, |
|||
DOMNparam, |
|||
NULL |
|||
}; |
|||
IFcardInfo REGNinfo = { |
|||
"region", |
|||
"Identify material-type for portion of a device", |
|||
NUMELEMS(DOMNpTable), |
|||
DOMNpTable, |
|||
|
|||
DOMNnewCard, |
|||
DOMNparam, |
|||
NULL |
|||
}; |
|||
|
|||
int |
|||
DOMNnewCard(void **inCard, void *inModel) |
|||
{ |
|||
DOMNcard *tmpCard, *newCard; |
|||
GENnumModel *model = (GENnumModel *)inModel; |
|||
|
|||
newCard = NEW( DOMNcard ); |
|||
if (!newCard) { |
|||
*inCard = (void *)NULL; |
|||
return(E_NOMEM); |
|||
} |
|||
newCard->DOMNnextCard = (DOMNcard *)NULL; |
|||
*inCard = (void *)newCard; |
|||
|
|||
tmpCard = model->GENdomains; |
|||
if (!tmpCard) { /* First in list */ |
|||
model->GENdomains = newCard; |
|||
} else { |
|||
/* Go to end of list */ |
|||
while (tmpCard->DOMNnextCard) tmpCard = tmpCard->DOMNnextCard; |
|||
/* And add new card */ |
|||
tmpCard->DOMNnextCard = newCard; |
|||
} |
|||
return(OK); |
|||
} |
|||
|
|||
int |
|||
DOMNparam(int param, IFvalue *value, void *inCard) |
|||
{ |
|||
DOMNcard *card = (DOMNcard *)inCard; |
|||
|
|||
switch (param) { |
|||
case DOMN_X_LOW: |
|||
card->DOMNxLow = value->rValue * UM_TO_CM; |
|||
card->DOMNxLowGiven = TRUE; |
|||
break; |
|||
case DOMN_X_HIGH: |
|||
card->DOMNxHigh = value->rValue * UM_TO_CM; |
|||
card->DOMNxHighGiven = TRUE; |
|||
break; |
|||
case DOMN_Y_LOW: |
|||
card->DOMNyLow = value->rValue * UM_TO_CM; |
|||
card->DOMNyLowGiven = TRUE; |
|||
break; |
|||
case DOMN_Y_HIGH: |
|||
card->DOMNyHigh = value->rValue * UM_TO_CM; |
|||
card->DOMNyHighGiven = TRUE; |
|||
break; |
|||
case DOMN_IX_LOW: |
|||
card->DOMNixLow = value->iValue; |
|||
card->DOMNixLowGiven = TRUE; |
|||
break; |
|||
case DOMN_IX_HIGH: |
|||
card->DOMNixHigh = value->iValue; |
|||
card->DOMNixHighGiven = TRUE; |
|||
break; |
|||
case DOMN_IY_LOW: |
|||
card->DOMNiyLow = value->iValue; |
|||
card->DOMNiyLowGiven = TRUE; |
|||
break; |
|||
case DOMN_IY_HIGH: |
|||
card->DOMNiyHigh = value->iValue; |
|||
card->DOMNiyHighGiven = TRUE; |
|||
break; |
|||
case DOMN_NUMBER: |
|||
card->DOMNnumber = value->iValue; |
|||
card->DOMNnumberGiven = TRUE; |
|||
break; |
|||
case DOMN_MATERIAL: |
|||
card->DOMNmaterial = value->iValue; |
|||
card->DOMNmaterialGiven = TRUE; |
|||
break; |
|||
default: |
|||
return(E_BADPARM); |
|||
break; |
|||
} |
|||
return(OK); |
|||
} |
|||
@ -0,0 +1,207 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group |
|||
Modifed: 2001 Paolo Nenzi |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "cktdefs.h" |
|||
#include "numenum.h" |
|||
#include "domndefs.h" |
|||
#include "material.h" |
|||
#include "meshext.h" |
|||
#include "gendev.h" |
|||
#include "sperror.h" |
|||
#include "suffix.h" |
|||
|
|||
|
|||
extern int DOMNcheck( DOMNcard *, MATLmaterial * ); |
|||
extern int DOMNsetup( DOMNcard *, DOMNdomain **, MESHcoord *, MESHcoord *, |
|||
MATLmaterial * ); |
|||
|
|||
|
|||
/* |
|||
* Name: DOMNcheck |
|||
* Purpose: checks a list of DOMNcards for input errors |
|||
* Formals: cardList: the list to check |
|||
* Returns: OK/E_PRIVATE |
|||
* Users: numerical device setup routines |
|||
* Calls: error message handler |
|||
*/ |
|||
int |
|||
DOMNcheck(DOMNcard *cardList, MaterialInfo *matlList) |
|||
{ |
|||
DOMNcard *card; |
|||
MATLmaterial *matl; |
|||
int cardNum = 0; |
|||
int error = OK; |
|||
char ebuf[512]; /* error message buffer */ |
|||
|
|||
for ( card = cardList; card != NIL(DOMNcard); card = card->DOMNnextCard ) { |
|||
cardNum++; |
|||
if (card->DOMNxLowGiven && card->DOMNixLowGiven) { |
|||
sprintf( ebuf, |
|||
"domain card %d uses both location and index - location ignored", |
|||
cardNum ); |
|||
SPfrontEnd->IFerror( ERR_INFO, ebuf, NIL(IFuid) ); |
|||
card->DOMNxLowGiven = FALSE; |
|||
} |
|||
if (card->DOMNxHighGiven && card->DOMNixHighGiven) { |
|||
sprintf( ebuf, |
|||
"domain card %d uses both location and index - location ignored", |
|||
cardNum ); |
|||
SPfrontEnd->IFerror( ERR_INFO, ebuf, NIL(IFuid) ); |
|||
card->DOMNxHighGiven = FALSE; |
|||
} |
|||
if (card->DOMNyLowGiven && card->DOMNiyLowGiven) { |
|||
sprintf( ebuf, |
|||
"domain card %d uses both location and index - location ignored", |
|||
cardNum ); |
|||
SPfrontEnd->IFerror( ERR_INFO, ebuf, NIL(IFuid) ); |
|||
card->DOMNyLowGiven = FALSE; |
|||
} |
|||
if (card->DOMNyHighGiven && card->DOMNiyHighGiven) { |
|||
sprintf( ebuf, |
|||
"domain card %d uses both location and index - location ignored", |
|||
cardNum ); |
|||
SPfrontEnd->IFerror( ERR_INFO, ebuf, NIL(IFuid) ); |
|||
card->DOMNyHighGiven = FALSE; |
|||
} |
|||
if (!card->DOMNmaterialGiven) { |
|||
sprintf( ebuf, |
|||
"domain card %d is missing a material index", |
|||
cardNum ); |
|||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) ); |
|||
error = E_PRIVATE; |
|||
} else { |
|||
/* Make sure the material exists */ |
|||
for ( matl = matlList; matl != NIL(MATLmaterial); matl = matl->next ) { |
|||
if ( card->DOMNmaterial == matl->id ) { |
|||
break; |
|||
} |
|||
} |
|||
if (matl == NIL(MATLmaterial)) { |
|||
sprintf( ebuf, |
|||
"domain card %d specifies a non-existent material", |
|||
cardNum ); |
|||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) ); |
|||
error = E_PRIVATE; |
|||
} |
|||
} |
|||
if (!card->DOMNnumberGiven) { |
|||
sprintf( ebuf, |
|||
"domain card %d is missing an ID number", |
|||
cardNum ); |
|||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) ); |
|||
error = E_PRIVATE; |
|||
} |
|||
|
|||
/* Return now if anything has failed */ |
|||
if (error) return(error); |
|||
} |
|||
return(OK); |
|||
} |
|||
|
|||
|
|||
|
|||
/* |
|||
* Name: DOMNsetup |
|||
* Purpose: convert a list of DOMNcard's to DOMNdomain's |
|||
* Formals: cardList: list of cards to setup |
|||
* domainList: returns the list of DOMNdomain's |
|||
* xMeshList: list of coordinates in the x mesh |
|||
* yMeshList: list of coordinates in the y mesh |
|||
* Returns: OK/E_PRIVATE |
|||
* Users: numerical devices |
|||
* Calls: DOMNcheck |
|||
*/ |
|||
int |
|||
DOMNsetup(DOMNcard *cardList, DOMNdomain **domainList, MESHcoord *xMeshList, |
|||
MESHcoord *yMeshList, MaterialInfo *materialList) |
|||
{ |
|||
DOMNcard *card; |
|||
DOMNdomain *newDomain = NULL; |
|||
int ixMin, ixMax, iyMin, iyMax; |
|||
int cardNum = 0; |
|||
int error; |
|||
char ebuf[512]; /* error message buffer */ |
|||
|
|||
/* Initialize list of domains */ |
|||
*domainList = NIL(DOMNdomain); |
|||
|
|||
/* Check the card list */ |
|||
if ((error = DOMNcheck( cardList, materialList ))) return( error ); |
|||
|
|||
/* Find the limits on the indices */ |
|||
MESHiBounds( xMeshList, &ixMin, &ixMax ); |
|||
MESHiBounds( yMeshList, &iyMin, &iyMax ); |
|||
|
|||
error = OK; |
|||
for ( card = cardList; card != NIL(DOMNcard); card = card->DOMNnextCard ) { |
|||
cardNum++; |
|||
|
|||
if (*domainList == NIL(DOMNdomain)) { |
|||
RALLOC( newDomain, DOMNdomain, 1 ); |
|||
*domainList = newDomain; |
|||
} else { |
|||
RALLOC( newDomain->next, DOMNdomain, 1 ); |
|||
newDomain = newDomain->next; |
|||
} |
|||
|
|||
newDomain->id = card->DOMNnumber; |
|||
newDomain->material = card->DOMNmaterial; |
|||
newDomain->next = NIL(DOMNdomain); |
|||
|
|||
if (card->DOMNixLowGiven) { |
|||
newDomain->ixLo = MAX(card->DOMNixLow, ixMin); |
|||
} |
|||
else if (card->DOMNxLowGiven) { |
|||
newDomain->ixLo = MESHlocate( xMeshList, card->DOMNxLow ); |
|||
} |
|||
else { |
|||
newDomain->ixLo = ixMin; |
|||
} |
|||
if (card->DOMNixHighGiven) { |
|||
newDomain->ixHi = MIN(card->DOMNixHigh, ixMax); |
|||
} |
|||
else if (card->DOMNxHighGiven) { |
|||
newDomain->ixHi = MESHlocate( xMeshList, card->DOMNxHigh ); |
|||
} |
|||
else { |
|||
newDomain->ixHi = ixMax; |
|||
} |
|||
if (newDomain->ixLo > newDomain->ixHi) { |
|||
sprintf( ebuf, |
|||
"domain card %d has low x index (%d) > high x index (%d)", |
|||
cardNum, newDomain->ixLo, newDomain->ixHi ); |
|||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) ); |
|||
error = E_PRIVATE; |
|||
} |
|||
if (card->DOMNiyLowGiven) { |
|||
newDomain->iyLo = MAX(card->DOMNiyLow, iyMin); |
|||
} |
|||
else if (card->DOMNyLowGiven) { |
|||
newDomain->iyLo = MESHlocate( yMeshList, card->DOMNyLow ); |
|||
} |
|||
else { |
|||
newDomain->iyLo = iyMin; |
|||
} |
|||
if (card->DOMNiyHighGiven) { |
|||
newDomain->iyHi = MIN(card->DOMNiyHigh, iyMax); |
|||
} |
|||
else if (card->DOMNyHighGiven) { |
|||
newDomain->iyHi = MESHlocate( yMeshList, card->DOMNyHigh ); |
|||
} |
|||
else { |
|||
newDomain->iyHi = iyMax; |
|||
} |
|||
if (newDomain->iyLo > newDomain->iyHi) { |
|||
sprintf( ebuf, |
|||
"domain card %d has low y index (%d) > high y index (%d)", |
|||
cardNum, newDomain->iyLo, newDomain->iyHi ); |
|||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) ); |
|||
error = E_PRIVATE; |
|||
} |
|||
} |
|||
return( error ); |
|||
} |
|||
@ -0,0 +1,286 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group |
|||
Modified: 2001 Paolo Nenzi |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numcards.h" |
|||
#include "numgen.h" |
|||
#include "dopdefs.h" |
|||
#include "devdefs.h" |
|||
#include "sperror.h" |
|||
#include "suffix.h" |
|||
|
|||
#define UM_TO_CM 1.0e-4 |
|||
|
|||
extern int DOPnewCard(void**,void*); |
|||
extern int DOPparam(int,IFvalue*,void*); |
|||
|
|||
|
|||
IFparm DOPpTable[] = { |
|||
IP("domains", DOP_DOMAIN, IF_INTVEC, "Domain(s) to dope"), |
|||
IP("uniform", DOP_UNIF, IF_FLAG, "Uniform doping"), |
|||
IP("linear", DOP_LINEAR, IF_FLAG, "Linear graded doping"), |
|||
IP("gaussian",DOP_GAUSS, IF_FLAG, "Gaussian doping"), |
|||
IP("gdiff", DOP_GAUSS, IF_FLAG, "Gaussian diffusion"), |
|||
IP("gimp", DOP_GAUSS, IF_FLAG, "Gaussian implantation"), |
|||
IP("erfc", DOP_ERFC, IF_FLAG, "Error-function doping"), |
|||
IP("errfc", DOP_ERFC, IF_FLAG, "Error-function doping"), |
|||
IP("exponential",DOP_EXP, IF_FLAG, "Exponential doping"), |
|||
IP("suprem3", DOP_SUPREM3, IF_FLAG, "Suprem3 doping"), |
|||
IP("ascii", DOP_ASCII, IF_FLAG, "Ascii-input doping"), |
|||
IP("infile", DOP_INFILE, IF_STRING, "Input-file name"), |
|||
IP("lat.rotate",DOP_ROTATE_LAT,IF_FLAG, "Rotate principal profile"), |
|||
IP("lat.unif",DOP_UNIF_LAT, IF_FLAG, "Uniform lateral falloff"), |
|||
IP("lat.linf",DOP_LINEAR_LAT, IF_FLAG, "Linear lateral falloff"), |
|||
IP("lat.gauss",DOP_GAUSS_LAT, IF_FLAG, "Gaussian lateral falloff"), |
|||
IP("lat.erfc",DOP_ERFC_LAT, IF_FLAG, "Erfc lateral falloff"), |
|||
IP("lat.exp", DOP_EXP_LAT, IF_FLAG, "Exponential lateral falloff"), |
|||
IP("boron", DOP_BORON, IF_FLAG, "Boron impurities"), |
|||
IP("phosphorus",DOP_PHOSP, IF_FLAG, "Phosphorus impurities"), |
|||
IP("arsenic", DOP_ARSEN, IF_FLAG, "Arsenic impurities"), |
|||
IP("antimony",DOP_ANTIM, IF_FLAG, "Antimony impurities"), |
|||
IP("p.type", DOP_P_TYPE, IF_FLAG, "P-type impurities"), |
|||
IP("acceptor",DOP_P_TYPE, IF_FLAG, "Acceptor impurities"), |
|||
IP("n.type", DOP_N_TYPE, IF_FLAG, "N-type impurities"), |
|||
IP("donor", DOP_N_TYPE, IF_FLAG, "Donor impurities"), |
|||
IP("x.axis", DOP_X_AXIS, IF_FLAG, "X principal axis"), |
|||
IP("y.axis", DOP_Y_AXIS, IF_FLAG, "Y principal axis"), |
|||
IP("x.low", DOP_X_LOW, IF_REAL, "Low X edge"), |
|||
IP("x.high", DOP_X_HIGH, IF_REAL, "High X edge"), |
|||
IP("y.low", DOP_Y_LOW, IF_REAL, "Low Y edge"), |
|||
IP("y.high", DOP_Y_HIGH, IF_REAL, "High Y edge"), |
|||
IP("conc", DOP_CONC, IF_REAL, "Concentration"), |
|||
IP("peak.conc",DOP_CONC, IF_REAL, "Peak concentration"), |
|||
IP("location",DOP_LOCATION, IF_REAL, "Peak location"), |
|||
IP("range", DOP_LOCATION, IF_REAL, "Peak location"), |
|||
IP("char.length",DOP_CHAR_LEN,IF_REAL, "Characteristic length"), |
|||
IP("ratio.lat",DOP_RATIO_LAT, IF_REAL, "Lateral ratio") |
|||
}; |
|||
|
|||
IFcardInfo DOPinfo = { |
|||
"doping", |
|||
"Add dopant to domains of a device", |
|||
NUMELEMS(DOPpTable), |
|||
DOPpTable, |
|||
|
|||
DOPnewCard, |
|||
DOPparam, |
|||
NULL |
|||
}; |
|||
|
|||
int |
|||
DOPnewCard(void **inCard, void *inModel) |
|||
{ |
|||
DOPcard *tmpCard, *newCard; |
|||
GENnumModel *model = (GENnumModel *)inModel; |
|||
|
|||
newCard = NEW( DOPcard ); |
|||
if (!newCard) { |
|||
*inCard = (void *)NULL; |
|||
return(E_NOMEM); |
|||
} |
|||
newCard->DOPnextCard = (DOPcard *)NULL; |
|||
*inCard = (void *)newCard; |
|||
|
|||
tmpCard = model->GENdopings; |
|||
if (!tmpCard) { /* First in list */ |
|||
model->GENdopings = newCard; |
|||
} else { |
|||
/* Go to end of list */ |
|||
while (tmpCard->DOPnextCard) tmpCard = tmpCard->DOPnextCard; |
|||
/* And add new card */ |
|||
tmpCard->DOPnextCard = newCard; |
|||
} |
|||
return(OK); |
|||
} |
|||
|
|||
|
|||
int |
|||
DOPparam(int param, IFvalue *value, void *inCard) |
|||
{ |
|||
int i; |
|||
DOPcard *card = (DOPcard *)inCard; |
|||
|
|||
switch (param) { |
|||
case DOP_DOMAIN: |
|||
if ( !card->DOPdomainsGiven ) { |
|||
card->DOPnumDomains = value->v.numValue; |
|||
card->DOPdomains = (int *)xmalloc(value->v.numValue * sizeof(int)); |
|||
for ( i=0; i < card->DOPnumDomains; i++ ) { |
|||
card->DOPdomains[i] = value->v.vec.iVec[i]; |
|||
} |
|||
card->DOPdomainsGiven = TRUE; |
|||
} /* else: do nothing, can only define domains once */ |
|||
break; |
|||
case DOP_ROTATE_LAT: |
|||
card->DOProtateLat = TRUE; |
|||
card->DOProtateLatGiven = TRUE; |
|||
break; |
|||
case DOP_UNIF: |
|||
if ( !card->DOPprofileTypeGiven ) { |
|||
card->DOPprofileType = DOP_UNIF; |
|||
card->DOPprofileTypeGiven = TRUE; |
|||
} |
|||
break; |
|||
case DOP_UNIF_LAT: |
|||
if ( !card->DOPlatProfileTypeGiven ) { |
|||
card->DOPlatProfileType = DOP_UNIF; |
|||
card->DOPlatProfileTypeGiven = TRUE; |
|||
} |
|||
break; |
|||
case DOP_LINEAR: |
|||
if ( !card->DOPprofileTypeGiven ) { |
|||
card->DOPprofileType = DOP_LINEAR; |
|||
card->DOPprofileTypeGiven = TRUE; |
|||
} |
|||
break; |
|||
case DOP_LINEAR_LAT: |
|||
if ( !card->DOPlatProfileTypeGiven ) { |
|||
card->DOPlatProfileType = DOP_LINEAR_LAT; |
|||
card->DOPlatProfileTypeGiven = TRUE; |
|||
} |
|||
break; |
|||
case DOP_GAUSS: |
|||
if ( !card->DOPprofileTypeGiven ) { |
|||
card->DOPprofileType = DOP_GAUSS; |
|||
card->DOPprofileTypeGiven = TRUE; |
|||
} |
|||
break; |
|||
case DOP_GAUSS_LAT: |
|||
if ( !card->DOPlatProfileTypeGiven ) { |
|||
card->DOPlatProfileType = DOP_GAUSS; |
|||
card->DOPlatProfileTypeGiven = TRUE; |
|||
} |
|||
break; |
|||
case DOP_ERFC: |
|||
if ( !card->DOPprofileTypeGiven ) { |
|||
card->DOPprofileType = DOP_ERFC; |
|||
card->DOPprofileTypeGiven = TRUE; |
|||
} |
|||
break; |
|||
case DOP_ERFC_LAT: |
|||
if ( !card->DOPlatProfileTypeGiven ) { |
|||
card->DOPlatProfileType = DOP_ERFC; |
|||
card->DOPlatProfileTypeGiven = TRUE; |
|||
} |
|||
break; |
|||
case DOP_EXP: |
|||
if ( !card->DOPprofileTypeGiven ) { |
|||
card->DOPprofileType = DOP_EXP; |
|||
card->DOPprofileTypeGiven = TRUE; |
|||
} |
|||
break; |
|||
case DOP_EXP_LAT: |
|||
if ( !card->DOPlatProfileTypeGiven ) { |
|||
card->DOPlatProfileType = DOP_EXP; |
|||
card->DOPlatProfileTypeGiven = TRUE; |
|||
} |
|||
break; |
|||
case DOP_SUPREM3: |
|||
if ( !card->DOPprofileTypeGiven ) { |
|||
card->DOPprofileType = DOP_SUPREM3; |
|||
card->DOPprofileTypeGiven = TRUE; |
|||
} else if ( card->DOPprofileType == DOP_ASCII ) { |
|||
card->DOPprofileType = DOP_SUPASCII; |
|||
} |
|||
break; |
|||
case DOP_ASCII: |
|||
if ( !card->DOPprofileTypeGiven ) { |
|||
card->DOPprofileType = DOP_ASCII; |
|||
card->DOPprofileTypeGiven = TRUE; |
|||
} else if ( card->DOPprofileType == DOP_SUPREM3 ) { |
|||
card->DOPprofileType = DOP_SUPASCII; |
|||
} |
|||
break; |
|||
case DOP_BORON: |
|||
if ( !card->DOPimpurityTypeGiven ) { |
|||
card->DOPimpurityType = DOP_BORON; |
|||
card->DOPimpurityTypeGiven = TRUE; |
|||
} |
|||
break; |
|||
case DOP_PHOSP: |
|||
if ( !card->DOPimpurityTypeGiven ) { |
|||
card->DOPimpurityType = DOP_PHOSP; |
|||
card->DOPimpurityTypeGiven = TRUE; |
|||
} |
|||
break; |
|||
case DOP_ARSEN: |
|||
if ( !card->DOPimpurityTypeGiven ) { |
|||
card->DOPimpurityType = DOP_ARSEN; |
|||
card->DOPimpurityTypeGiven = TRUE; |
|||
} |
|||
break; |
|||
case DOP_ANTIM: |
|||
if ( !card->DOPimpurityTypeGiven ) { |
|||
card->DOPimpurityType = DOP_ANTIM; |
|||
card->DOPimpurityTypeGiven = TRUE; |
|||
} |
|||
break; |
|||
case DOP_P_TYPE: |
|||
if ( !card->DOPimpurityTypeGiven ) { |
|||
card->DOPimpurityType = DOP_P_TYPE; |
|||
card->DOPimpurityTypeGiven = TRUE; |
|||
} |
|||
break; |
|||
case DOP_N_TYPE: |
|||
if ( !card->DOPimpurityTypeGiven ) { |
|||
card->DOPimpurityType = DOP_N_TYPE; |
|||
card->DOPimpurityTypeGiven = TRUE; |
|||
} |
|||
break; |
|||
case DOP_X_AXIS: |
|||
if ( !card->DOPaxisTypeGiven ) { |
|||
card->DOPaxisType = DOP_X_AXIS; |
|||
card->DOPaxisTypeGiven = TRUE; |
|||
} |
|||
break; |
|||
case DOP_Y_AXIS: |
|||
if ( !card->DOPaxisTypeGiven ) { |
|||
card->DOPaxisType = DOP_Y_AXIS; |
|||
card->DOPaxisTypeGiven = TRUE; |
|||
} |
|||
break; |
|||
case DOP_INFILE: |
|||
card->DOPinFile = value->sValue; |
|||
card->DOPinFileGiven = TRUE; |
|||
break; |
|||
case DOP_X_LOW: |
|||
card->DOPxLow = value->rValue * UM_TO_CM; |
|||
card->DOPxLowGiven = TRUE; |
|||
break; |
|||
case DOP_X_HIGH: |
|||
card->DOPxHigh = value->rValue * UM_TO_CM; |
|||
card->DOPxHighGiven = TRUE; |
|||
break; |
|||
case DOP_Y_LOW: |
|||
card->DOPyLow = value->rValue * UM_TO_CM; |
|||
card->DOPyLowGiven = TRUE; |
|||
break; |
|||
case DOP_Y_HIGH: |
|||
card->DOPyHigh = value->rValue * UM_TO_CM; |
|||
card->DOPyHighGiven = TRUE; |
|||
break; |
|||
case DOP_CONC: |
|||
card->DOPconc = fabs(value->rValue); |
|||
card->DOPconcGiven = TRUE; |
|||
break; |
|||
case DOP_LOCATION: |
|||
card->DOPlocation = value->rValue * UM_TO_CM; |
|||
card->DOPlocationGiven = TRUE; |
|||
break; |
|||
case DOP_CHAR_LEN: |
|||
card->DOPcharLen = value->rValue * UM_TO_CM; |
|||
card->DOPcharLenGiven = TRUE; |
|||
break; |
|||
case DOP_RATIO_LAT: |
|||
card->DOPratioLat = value->rValue; |
|||
card->DOPratioLatGiven = TRUE; |
|||
break; |
|||
default: |
|||
return(E_BADPARM); |
|||
break; |
|||
} |
|||
return(OK); |
|||
} |
|||
@ -0,0 +1,393 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group |
|||
Modifed: 2001 Paolo Nenzi |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "cktdefs.h" |
|||
#include "numenum.h" |
|||
#include "dopdefs.h" |
|||
#include "meshext.h" |
|||
#include "profile.h" |
|||
#include "gendev.h" |
|||
#include "sperror.h" |
|||
#include "suffix.h" |
|||
|
|||
extern int DOPnewCard(void**,void*); |
|||
extern int DOPparam(int,IFvalue*,void*); |
|||
|
|||
|
|||
/* |
|||
* Name: DOPcheck |
|||
* Purpose: checks a list of DOPcards for input errors |
|||
* Formals: cardList: the list to check |
|||
* Returns: OK/E_PRIVATE |
|||
* Users: numerical device setup routines |
|||
* Calls: error message handler |
|||
*/ |
|||
int |
|||
DOPcheck(DOPcard *cardList, MESHcoord *xMeshList, MESHcoord *yMeshList) |
|||
{ |
|||
DOPcard *card; |
|||
int cardNum = 0; |
|||
int error = OK; |
|||
char ebuf[512]; /* error message buffer */ |
|||
|
|||
for ( card = cardList; card != NIL(DOPcard); card = card->DOPnextCard ) { |
|||
cardNum++; |
|||
if (!card->DOPdomainsGiven) { |
|||
card->DOPnumDomains = 0; |
|||
card->DOPdomains = NIL(int); |
|||
} |
|||
if (!card->DOPprofileTypeGiven) { |
|||
sprintf( ebuf, |
|||
"doping card %d does not specify profile type", |
|||
cardNum ); |
|||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) ); |
|||
error = E_PRIVATE; |
|||
} else switch (card->DOPprofileType) { |
|||
case DOP_UNIF: |
|||
if (!card->DOPconcGiven) { |
|||
sprintf( ebuf, |
|||
"doping card %d needs conc of uniform distribution", |
|||
cardNum ); |
|||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) ); |
|||
error = E_PRIVATE; |
|||
} |
|||
break; |
|||
case DOP_LINEAR: |
|||
if (!card->DOPconcGiven) { |
|||
sprintf( ebuf, |
|||
"doping card %d needs peak conc of linear distribution", |
|||
cardNum ); |
|||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) ); |
|||
error = E_PRIVATE; |
|||
} |
|||
break; |
|||
case DOP_GAUSS: |
|||
if (!card->DOPconcGiven) { |
|||
sprintf( ebuf, |
|||
"doping card %d needs peak conc of gaussian distribution", |
|||
cardNum ); |
|||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) ); |
|||
error = E_PRIVATE; |
|||
} |
|||
break; |
|||
case DOP_ERFC: |
|||
if (!card->DOPconcGiven) { |
|||
sprintf( ebuf, |
|||
"doping card %d needs peak conc of error-function distribution", |
|||
cardNum ); |
|||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) ); |
|||
error = E_PRIVATE; |
|||
} |
|||
break; |
|||
case DOP_EXP: |
|||
if (!card->DOPconcGiven) { |
|||
sprintf( ebuf, |
|||
"doping card %d needs peak conc of exponential distribution", |
|||
cardNum ); |
|||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) ); |
|||
error = E_PRIVATE; |
|||
} |
|||
break; |
|||
case DOP_SUPREM3: |
|||
case DOP_SUPASCII: |
|||
if (!card->DOPinFileGiven) { |
|||
sprintf( ebuf, |
|||
"doping card %d needs input-file name of suprem3 data", |
|||
cardNum ); |
|||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) ); |
|||
error = E_PRIVATE; |
|||
} |
|||
break; |
|||
case DOP_ASCII: |
|||
if (!card->DOPinFileGiven) { |
|||
sprintf( ebuf, |
|||
"doping card %d needs input-file name of ascii data", |
|||
cardNum ); |
|||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) ); |
|||
error = E_PRIVATE; |
|||
} |
|||
break; |
|||
default: |
|||
sprintf( ebuf, |
|||
"doping card %d has unrecognized profile type", |
|||
cardNum ); |
|||
SPfrontEnd->IFerror( ERR_FATAL, ebuf, NIL(IFuid) ); |
|||
error = E_NOTFOUND; |
|||
break; |
|||
} |
|||
if (!card->DOProtateLatGiven) { |
|||
card->DOProtateLat = FALSE; |
|||
} |
|||
if (!card->DOPlatProfileTypeGiven || card->DOProtateLat) { |
|||
card->DOPlatProfileType = card->DOPprofileType; |
|||
} |
|||
if (!card->DOPratioLatGiven) { |
|||
card->DOPratioLat = 1.0; |
|||
} |
|||
if (!card->DOPcharLenGiven) { |
|||
card->DOPcharLen = 1.0e-4; /* 1um in centimeters */ |
|||
} |
|||
if (!card->DOPlocationGiven) { |
|||
card->DOPlocation = 0.0; |
|||
} |
|||
if (!card->DOPimpurityTypeGiven) { |
|||
card->DOPimpurityType = IMP_N_TYPE; |
|||
} else switch (card->DOPimpurityType) { |
|||
case DOP_BORON: |
|||
card->DOPimpurityType = IMP_BORON; |
|||
break; |
|||
case DOP_PHOSP: |
|||
card->DOPimpurityType = IMP_PHOSPHORUS; |
|||
break; |
|||
case DOP_ARSEN: |
|||
card->DOPimpurityType = IMP_ARSENIC; |
|||
break; |
|||
case DOP_ANTIM: |
|||
card->DOPimpurityType = IMP_ANTIMONY; |
|||
break; |
|||
case DOP_N_TYPE: |
|||
card->DOPimpurityType = IMP_N_TYPE; |
|||
break; |
|||
case DOP_P_TYPE: |
|||
card->DOPimpurityType = IMP_P_TYPE; |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
|
|||
if (!card->DOPaxisTypeGiven) { |
|||
if ( xMeshList && yMeshList ) { /* both lists are non-empty */ |
|||
card->DOPaxisType = DOP_Y_AXIS; |
|||
} else if ( xMeshList ) { /* x-mesh list is non-empty */ |
|||
card->DOPaxisType = DOP_X_AXIS; |
|||
} else if ( yMeshList ) { /* y-mesh list is non-empty */ |
|||
card->DOPaxisType = DOP_Y_AXIS; |
|||
} |
|||
} |
|||
|
|||
/* Return now if anything has failed */ |
|||
if (error) return(error); |
|||
} |
|||
return(OK); |
|||
} |
|||
|
|||
|
|||
|
|||
/* |
|||
* Name: DOPsetup |
|||
* Purpose: convert a list of DOPcard's to DOPprofile's |
|||
* Formals: cardList: list of cards to setup |
|||
* profileList: returns the list of DOPprofile's |
|||
* xMeshList: list of coordinates in the x mesh |
|||
* yMeshList: list of coordinates in the y mesh |
|||
* Returns: OK/E_PRIVATE |
|||
* Users: numerical devices |
|||
* Calls: DOPcheck |
|||
*/ |
|||
int |
|||
DOPsetup(DOPcard *cardList, DOPprofile **profileList, DOPtable **tableList, |
|||
MESHcoord *xMeshList, MESHcoord *yMeshList) |
|||
{ |
|||
DOPcard *card; |
|||
DOPprofile *newProfile = NULL, *endProfile; |
|||
int impurityId = 0; |
|||
double xMin, xMax, yMin, yMax; |
|||
double sign; |
|||
int error, xProfUnif, yProfUnif; |
|||
|
|||
/* Initialize list of profiles */ |
|||
*profileList = endProfile = NIL(DOPprofile); |
|||
|
|||
/* Check the card list */ |
|||
if ((error = DOPcheck( cardList, xMeshList, yMeshList ))) return( error ); |
|||
|
|||
/* Find the limits on locations */ |
|||
MESHlBounds( xMeshList, &xMin, &xMax ); |
|||
MESHlBounds( yMeshList, &yMin, &yMax ); |
|||
|
|||
for ( card = cardList; card != NIL(DOPcard); card = card->DOPnextCard ) { |
|||
|
|||
if (*profileList == NIL(DOPprofile)) { |
|||
RALLOC( newProfile, DOPprofile, 1 ); |
|||
*profileList = newProfile; |
|||
} |
|||
else { |
|||
RALLOC( newProfile->next, DOPprofile, 1 ); |
|||
newProfile = newProfile->next; |
|||
} |
|||
newProfile->next = NIL(DOPprofile); |
|||
|
|||
newProfile->numDomains = card->DOPnumDomains; |
|||
if ( newProfile->numDomains > 0 ) { |
|||
int i; |
|||
RALLOC( newProfile->domains, int, newProfile->numDomains ); |
|||
for ( i=0; i < newProfile->numDomains; i++ ) { |
|||
newProfile->domains[i] = card->DOPdomains[i]; |
|||
} |
|||
} |
|||
else { |
|||
newProfile->domains = NIL(int); |
|||
} |
|||
|
|||
if ( card->DOPimpurityType == IMP_P_TYPE ) { |
|||
sign = -1.0; |
|||
} |
|||
else { |
|||
sign = 1.0; |
|||
} |
|||
switch( card->DOPprofileType ) { |
|||
case DOP_UNIF: |
|||
newProfile->type = UNIF; |
|||
newProfile->CONC = sign * card->DOPconc; |
|||
break; |
|||
case DOP_LINEAR: |
|||
newProfile->type = LIN; |
|||
newProfile->CONC = sign * card->DOPconc; |
|||
break; |
|||
case DOP_GAUSS: |
|||
newProfile->type = GAUSS; |
|||
newProfile->CONC = sign * card->DOPconc; |
|||
break; |
|||
case DOP_ERFC: |
|||
newProfile->type = ERRFC; |
|||
newProfile->CONC = sign * card->DOPconc; |
|||
break; |
|||
case DOP_EXP: |
|||
newProfile->type = EXP; |
|||
newProfile->CONC = sign * card->DOPconc; |
|||
break; |
|||
case DOP_SUPREM3: |
|||
newProfile->type = LOOKUP; |
|||
readSupremData( card->DOPinFile, 0, card->DOPimpurityType, tableList ); |
|||
newProfile->IMPID = ++impurityId; |
|||
break; |
|||
case DOP_SUPASCII: |
|||
newProfile->type = LOOKUP; |
|||
readSupremData( card->DOPinFile, 1, card->DOPimpurityType, tableList ); |
|||
newProfile->IMPID = ++impurityId; |
|||
break; |
|||
case DOP_ASCII: |
|||
newProfile->type = LOOKUP; |
|||
readAsciiData( card->DOPinFile, card->DOPimpurityType, tableList ); |
|||
newProfile->IMPID = ++impurityId; |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
switch( card->DOPlatProfileType ) { |
|||
case DOP_UNIF: |
|||
newProfile->latType = UNIF; |
|||
break; |
|||
case DOP_LINEAR: |
|||
newProfile->latType = LIN; |
|||
break; |
|||
case DOP_GAUSS: |
|||
newProfile->latType = GAUSS; |
|||
break; |
|||
case DOP_ERFC: |
|||
newProfile->latType = ERRFC; |
|||
break; |
|||
case DOP_EXP: |
|||
newProfile->latType = EXP; |
|||
break; |
|||
case DOP_SUPREM3: |
|||
case DOP_SUPASCII: |
|||
newProfile->latType = LOOKUP; |
|||
break; |
|||
case DOP_ASCII: |
|||
newProfile->latType = LOOKUP; |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
newProfile->rotate = card->DOProtateLat; |
|||
newProfile->LOCATION = card->DOPlocation; |
|||
newProfile->CHAR_LENGTH = card->DOPcharLen; |
|||
newProfile->LAT_RATIO = card->DOPratioLat; |
|||
|
|||
xProfUnif = yProfUnif = FALSE; |
|||
if (card->DOPaxisType == DOP_X_AXIS) { |
|||
newProfile->DIRECTION = X; |
|||
if (newProfile->type == UNIF) xProfUnif = TRUE; |
|||
if (newProfile->latType == UNIF) yProfUnif = TRUE; |
|||
} |
|||
else { |
|||
newProfile->DIRECTION = Y; |
|||
if (newProfile->type == UNIF) yProfUnif = TRUE; |
|||
if (newProfile->latType == UNIF) xProfUnif = TRUE; |
|||
} |
|||
|
|||
/* Fill in x coordinates. Use defaults if necessary */ |
|||
if (card->DOPxLowGiven && card->DOPxHighGiven) { |
|||
newProfile->X_LOW = card->DOPxLow; |
|||
newProfile->X_HIGH = card->DOPxHigh; |
|||
} |
|||
else if (card->DOPxLowGiven) { |
|||
newProfile->X_LOW = card->DOPxLow; |
|||
if (xProfUnif) { |
|||
newProfile->X_HIGH = xMax; |
|||
} |
|||
else { |
|||
newProfile->X_HIGH = newProfile->X_LOW; |
|||
} |
|||
} |
|||
else if (card->DOPxHighGiven) { |
|||
newProfile->X_HIGH = card->DOPxHigh; |
|||
if (xProfUnif) { |
|||
newProfile->X_LOW = xMin; |
|||
} |
|||
else { |
|||
newProfile->X_LOW = newProfile->X_HIGH; |
|||
} |
|||
} |
|||
else { |
|||
if (xProfUnif) { |
|||
newProfile->X_LOW = xMin; |
|||
newProfile->X_HIGH = xMax; |
|||
} |
|||
else { |
|||
newProfile->X_LOW = 0.5 * (xMin + xMax); |
|||
newProfile->X_HIGH = 0.5 * (xMin + xMax); |
|||
} |
|||
} |
|||
|
|||
/* Fill in y coordinates. Use defaults if necessary */ |
|||
if (card->DOPyLowGiven && card->DOPyHighGiven) { |
|||
newProfile->Y_LOW = card->DOPyLow; |
|||
newProfile->Y_HIGH = card->DOPyHigh; |
|||
} |
|||
else if (card->DOPyLowGiven) { |
|||
newProfile->Y_LOW = card->DOPyLow; |
|||
if (yProfUnif) { |
|||
newProfile->Y_HIGH = yMax; |
|||
} |
|||
else { |
|||
newProfile->Y_HIGH = newProfile->Y_LOW; |
|||
} |
|||
} |
|||
else if (card->DOPyHighGiven) { |
|||
newProfile->Y_HIGH = card->DOPyHigh; |
|||
if (xProfUnif) { |
|||
newProfile->Y_LOW = yMin; |
|||
} |
|||
else { |
|||
newProfile->Y_LOW = newProfile->Y_HIGH; |
|||
} |
|||
} |
|||
else { |
|||
if (yProfUnif) { |
|||
newProfile->Y_LOW = yMin; |
|||
newProfile->Y_HIGH = yMax; |
|||
} |
|||
else { |
|||
newProfile->Y_LOW = 0.5 * (yMin + yMax); |
|||
newProfile->Y_HIGH = 0.5 * (yMin + yMax); |
|||
} |
|||
} |
|||
} |
|||
return( OK ); |
|||
} |
|||
@ -0,0 +1,177 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group |
|||
Modified: 2001 Paolo Nenzi |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "cktdefs.h" |
|||
#include "elctdefs.h" |
|||
#include "meshext.h" |
|||
#include "twomesh.h" |
|||
#include "gendev.h" |
|||
#include "sperror.h" |
|||
#include "suffix.h" |
|||
|
|||
|
|||
extern int ELCTcheck( ELCTcard * ); |
|||
extern int ELCTsetup( ELCTcard *, ELCTelectrode **, MESHcoord *, MESHcoord * ); |
|||
|
|||
|
|||
/* |
|||
* Name: ELCTcheck |
|||
* Purpose: checks a list of ELCTcards for input errors |
|||
* Formals: cardList: the list to check |
|||
* Returns: OK/E_PRIVATE |
|||
* Users: numerical device setup routines |
|||
* Calls: error message handler |
|||
*/ |
|||
int |
|||
ELCTcheck(ELCTcard *cardList) |
|||
{ |
|||
ELCTcard *card; |
|||
int cardNum = 0; |
|||
int error = OK; |
|||
char ebuf[512]; /* error message buffer */ |
|||
|
|||
for ( card = cardList; card != NIL(ELCTcard); card = card->ELCTnextCard ) { |
|||
cardNum++; |
|||
if (card->ELCTxLowGiven && card->ELCTixLowGiven) { |
|||
sprintf( ebuf, |
|||
"electrode card %d uses both location and index - location ignored", |
|||
cardNum ); |
|||
SPfrontEnd->IFerror( ERR_INFO, ebuf, NIL(IFuid) ); |
|||
card->ELCTxLowGiven = FALSE; |
|||
} |
|||
if (card->ELCTxHighGiven && card->ELCTixHighGiven) { |
|||
sprintf( ebuf, |
|||
"electrode card %d uses both location and index - location ignored", |
|||
cardNum ); |
|||
SPfrontEnd->IFerror( ERR_INFO, ebuf, NIL(IFuid) ); |
|||
card->ELCTxHighGiven = FALSE; |
|||
} |
|||
if (card->ELCTyLowGiven && card->ELCTiyLowGiven) { |
|||
sprintf( ebuf, |
|||
"electrode card %d uses both location and index - location ignored", |
|||
cardNum ); |
|||
SPfrontEnd->IFerror( ERR_INFO, ebuf, NIL(IFuid) ); |
|||
card->ELCTyLowGiven = FALSE; |
|||
} |
|||
if (card->ELCTyHighGiven && card->ELCTiyHighGiven) { |
|||
sprintf( ebuf, |
|||
"electrode card %d uses both location and index - location ignored", |
|||
cardNum ); |
|||
SPfrontEnd->IFerror( ERR_INFO, ebuf, NIL(IFuid) ); |
|||
card->ELCTyHighGiven = FALSE; |
|||
} |
|||
if (!card->ELCTnumberGiven) { |
|||
card->ELCTnumber = -1; |
|||
} |
|||
|
|||
/* Return now if anything has failed */ |
|||
if (error) return(error); |
|||
} |
|||
return(OK); |
|||
} |
|||
|
|||
|
|||
/* |
|||
* Name: ELCTsetup |
|||
* Purpose: convert a list of ELCTcard's to ELCTelectrode's |
|||
* Formals: cardList: list of cards to setup |
|||
* electrodeList: returns the list of ELCTelectrode's |
|||
* xMeshList: list of coordinates in the x mesh |
|||
* yMeshList: list of coordinates in the y mesh |
|||
* Returns: OK/E_PRIVATE |
|||
* Users: numerical devices |
|||
* Calls: ELCTcheck |
|||
*/ |
|||
int |
|||
ELCTsetup(ELCTcard *cardList, ELCTelectrode **electrodeList, |
|||
MESHcoord *xMeshList, MESHcoord *yMeshList) |
|||
{ |
|||
ELCTcard *card; |
|||
ELCTelectrode *newElectrode = NULL; |
|||
int ixMin, ixMax, iyMin, iyMax; |
|||
int cardNum = 0; |
|||
int error; |
|||
char ebuf[512]; /* error message buffer */ |
|||
|
|||
/* Initialize list of electrodes */ |
|||
*electrodeList = NIL(ELCTelectrode); |
|||
|
|||
/* Check the card list */ |
|||
if ((error = ELCTcheck( cardList ))) return( error ); |
|||
|
|||
/* Find the limits on the indices */ |
|||
MESHiBounds( xMeshList, &ixMin, &ixMax ); |
|||
MESHiBounds( yMeshList, &iyMin, &iyMax ); |
|||
|
|||
error = OK; |
|||
for ( card = cardList; card != NIL(ELCTcard); card = card->ELCTnextCard ) { |
|||
cardNum++; |
|||
|
|||
if (*electrodeList == NIL(ELCTelectrode)) { |
|||
RALLOC( newElectrode, ELCTelectrode, 1 ); |
|||
*electrodeList = newElectrode; |
|||
} else { |
|||
RALLOC( newElectrode->next, ELCTelectrode, 1 ); |
|||
newElectrode = newElectrode->next; |
|||
} |
|||
newElectrode->next = NIL(ELCTelectrode); |
|||
newElectrode->id = card->ELCTnumber; |
|||
newElectrode->workf = 4.10 /* electron volts */; |
|||
|
|||
if (card->ELCTixLowGiven) { |
|||
newElectrode->ixLo = MAX(card->ELCTixLow, ixMin); |
|||
} |
|||
else if (card->ELCTxLowGiven) { |
|||
newElectrode->ixLo = MESHlocate( xMeshList, card->ELCTxLow ); |
|||
} |
|||
else { |
|||
newElectrode->ixLo = ixMin; |
|||
} |
|||
if (card->ELCTixHighGiven) { |
|||
newElectrode->ixHi = MIN(card->ELCTixHigh, ixMax); |
|||
} |
|||
else if (card->ELCTxHighGiven) { |
|||
newElectrode->ixHi = MESHlocate( xMeshList, card->ELCTxHigh ); |
|||
} |
|||
else { |
|||
newElectrode->ixHi = ixMax; |
|||
} |
|||
if (newElectrode->ixLo > newElectrode->ixHi) { |
|||
sprintf( ebuf, |
|||
"electrode card %d has low x index (%d) > high x index (%d)", |
|||
cardNum, newElectrode->ixLo, newElectrode->ixHi ); |
|||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) ); |
|||
error = E_PRIVATE; |
|||
} |
|||
if (card->ELCTiyLowGiven) { |
|||
newElectrode->iyLo = MAX(card->ELCTiyLow, iyMin); |
|||
} |
|||
else if (card->ELCTyLowGiven) { |
|||
newElectrode->iyLo = MESHlocate( yMeshList, card->ELCTyLow ); |
|||
} |
|||
else { |
|||
newElectrode->iyLo = iyMin; |
|||
} |
|||
if (card->ELCTiyHighGiven) { |
|||
newElectrode->iyHi = MIN(card->ELCTiyHigh, iyMax); |
|||
} |
|||
else if (card->ELCTyHighGiven) { |
|||
newElectrode->iyHi = MESHlocate( yMeshList, card->ELCTyHigh ); |
|||
} |
|||
else { |
|||
newElectrode->iyHi = iyMax; |
|||
} |
|||
if (newElectrode->iyLo > newElectrode->iyHi) { |
|||
sprintf( ebuf, |
|||
"electrode card %d has low y index (%d) > high y index (%d)", |
|||
cardNum, newElectrode->iyLo, newElectrode->iyHi ); |
|||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) ); |
|||
error = E_PRIVATE; |
|||
} |
|||
} |
|||
return( error ); |
|||
} |
|||
@ -0,0 +1,118 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group |
|||
Modified: 2001 Paolo Nenzi |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numcards.h" |
|||
#include "numgen.h" |
|||
#include "elctdefs.h" |
|||
#include "devdefs.h" |
|||
#include "sperror.h" |
|||
#include "suffix.h" |
|||
|
|||
#define UM_TO_CM 1.0e-4 |
|||
|
|||
extern int ELCTnewCard(void**,void*); |
|||
extern int ELCTparam(int,IFvalue*,void*); |
|||
|
|||
|
|||
IFparm ELCTpTable[] = { |
|||
IP("x.low", ELCT_X_LOW, IF_REAL, "Location of left edge"), |
|||
IP("x.high", ELCT_X_HIGH, IF_REAL, "Location of right edge"), |
|||
IP("y.low", ELCT_Y_LOW, IF_REAL, "Location of top edge"), |
|||
IP("y.high", ELCT_Y_HIGH, IF_REAL, "Location of bottom edge"), |
|||
IP("ix.low", ELCT_IX_LOW, IF_INTEGER, "Index of left edge"), |
|||
IP("ix.high", ELCT_IX_HIGH, IF_INTEGER, "Index of right edge"), |
|||
IP("iy.low", ELCT_IY_LOW, IF_INTEGER, "Index of top edge"), |
|||
IP("iy.high", ELCT_IY_HIGH, IF_INTEGER, "Index of bottom edge"), |
|||
IP("number", ELCT_NUMBER, IF_INTEGER, "Electrode ID number") |
|||
}; |
|||
|
|||
IFcardInfo ELCTinfo = { |
|||
"electrode", |
|||
"Location of a contact to the device", |
|||
NUMELEMS(ELCTpTable), |
|||
ELCTpTable, |
|||
|
|||
ELCTnewCard, |
|||
ELCTparam, |
|||
NULL |
|||
}; |
|||
|
|||
int |
|||
ELCTnewCard(void **inCard, void *inModel) |
|||
{ |
|||
ELCTcard *tmpCard, *newCard; |
|||
GENnumModel *model = (GENnumModel *)inModel; |
|||
|
|||
newCard = NEW( ELCTcard ); |
|||
if (!newCard) { |
|||
*inCard = (void *)NULL; |
|||
return(E_NOMEM); |
|||
} |
|||
newCard->ELCTnextCard = (ELCTcard *)NULL; |
|||
*inCard = (void *)newCard; |
|||
|
|||
tmpCard = model->GENelectrodes; |
|||
if (!tmpCard) { /* First in list */ |
|||
model->GENelectrodes = newCard; |
|||
} else { |
|||
/* Go to end of list */ |
|||
while (tmpCard->ELCTnextCard) tmpCard = tmpCard->ELCTnextCard; |
|||
/* And add new card */ |
|||
tmpCard->ELCTnextCard = newCard; |
|||
} |
|||
return(OK); |
|||
} |
|||
|
|||
|
|||
int |
|||
ELCTparam(int param, IFvalue *value, void *inCard) |
|||
{ |
|||
ELCTcard *card = (ELCTcard *)inCard; |
|||
|
|||
switch (param) { |
|||
case ELCT_X_LOW: |
|||
card->ELCTxLow = value->rValue * UM_TO_CM; |
|||
card->ELCTxLowGiven = TRUE; |
|||
break; |
|||
case ELCT_X_HIGH: |
|||
card->ELCTxHigh = value->rValue * UM_TO_CM; |
|||
card->ELCTxHighGiven = TRUE; |
|||
break; |
|||
case ELCT_Y_LOW: |
|||
card->ELCTyLow = value->rValue * UM_TO_CM; |
|||
card->ELCTyLowGiven = TRUE; |
|||
break; |
|||
case ELCT_Y_HIGH: |
|||
card->ELCTyHigh = value->rValue * UM_TO_CM; |
|||
card->ELCTyHighGiven = TRUE; |
|||
break; |
|||
case ELCT_IX_LOW: |
|||
card->ELCTixLow = value->iValue; |
|||
card->ELCTixLowGiven = TRUE; |
|||
break; |
|||
case ELCT_IX_HIGH: |
|||
card->ELCTixHigh = value->iValue; |
|||
card->ELCTixHighGiven = TRUE; |
|||
break; |
|||
case ELCT_IY_LOW: |
|||
card->ELCTiyLow = value->iValue; |
|||
card->ELCTiyLowGiven = TRUE; |
|||
break; |
|||
case ELCT_IY_HIGH: |
|||
card->ELCTiyHigh = value->iValue; |
|||
card->ELCTiyHighGiven = TRUE; |
|||
break; |
|||
case ELCT_NUMBER: |
|||
card->ELCTnumber = value->iValue; |
|||
card->ELCTnumberGiven = TRUE; |
|||
break; |
|||
default: |
|||
return(E_BADPARM); |
|||
break; |
|||
} |
|||
return(OK); |
|||
} |
|||
@ -0,0 +1,305 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group |
|||
Modified: 2001 Paolo Nenzi |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numcards.h" |
|||
#include "numgen.h" |
|||
#include "numenum.h" |
|||
#include "matldefs.h" |
|||
#include "devdefs.h" |
|||
#include "sperror.h" |
|||
#include "suffix.h" |
|||
|
|||
|
|||
extern int MATLnewCard(void**,void *); |
|||
extern int MATLparam(int,IFvalue*,void *); |
|||
|
|||
|
|||
IFparm MATLpTable[] = { |
|||
IP("number", MATL_NUMBER, IF_INTEGER, "Material ID number"), |
|||
IP("insulator",MATL_INSULATOR,IF_FLAG, "Insulator"), |
|||
IP("oxide", MATL_OXIDE, IF_FLAG, "Oxide"), |
|||
IP("sio2", MATL_OXIDE, IF_FLAG, "Oxide"), |
|||
IP("nitride", MATL_NITRIDE, IF_FLAG, "Nitride"), |
|||
IP("si3n4", MATL_NITRIDE, IF_FLAG, "Nitride"), |
|||
IP("semiconductor",MATL_SEMICON,IF_FLAG, "Semiconductor"), |
|||
IP("silicon", MATL_SILICON, IF_FLAG, "Silicon"), |
|||
IP("polysilicon",MATL_POLYSIL,IF_FLAG, "Polysilicon"), |
|||
IP("gaas", MATL_GAAS, IF_FLAG, "Gallium-Arsenide"), |
|||
IP("nc", MATL_NC0, IF_REAL, "Conduction band density"), |
|||
IP("nc0", MATL_NC0, IF_REAL, "Conduction band density"), |
|||
IP("nc300", MATL_NC0, IF_REAL, "Conduction band density"), |
|||
IP("nv", MATL_NV0, IF_REAL, "Valence band density"), |
|||
IP("nv0", MATL_NV0, IF_REAL, "Valence band density"), |
|||
IP("nv300", MATL_NV0, IF_REAL, "Valence band density"), |
|||
IP("eg", MATL_EG0, IF_REAL, "Energy gap"), |
|||
IP("eg0", MATL_EG0, IF_REAL, "Energy gap"), |
|||
IP("eg300", MATL_EG0, IF_REAL, "Energy gap"), |
|||
IP("deg.dt", MATL_DEGDT, IF_REAL, "Bandgap narrowing w/ temp"), |
|||
IP("egalpha", MATL_DEGDT, IF_REAL, "Bandgap narrowing w/ temp"), |
|||
IP("eg.tref", MATL_TREF_EG, IF_REAL, "E-gap reference temperature"), |
|||
IP("egbeta", MATL_TREF_EG, IF_REAL, "E-gap reference temperature"), |
|||
IP("deg.dc", MATL_DEGDC, IF_REAL, "Bandgap narrowing w/ N&P doping"), |
|||
IP("eg.cref", MATL_CREF_EG, IF_REAL, "E-gap reference conc (N&P type)"), |
|||
IP("nbgn", MATL_CREF_EG, IF_REAL, "E-gap reference conc (N&P type)"), |
|||
IP("deg.dn", MATL_DEGDN, IF_REAL, "Bandgap narrowing w/ N doping"), |
|||
IP("eg.nref", MATL_NREF_EG, IF_REAL, "E-gap reference conc (N type)"), |
|||
IP("nbgnn", MATL_NREF_EG, IF_REAL, "E-gap reference conc (N type)"), |
|||
IP("deg.dp", MATL_DEGDP, IF_REAL, "Bandgap narrowing w/ P doping"), |
|||
IP("eg.pref", MATL_PREF_EG, IF_REAL, "E-gap reference conc (P type)"), |
|||
IP("nbgnp", MATL_PREF_EG, IF_REAL, "E-gap reference conc (P type)"), |
|||
IP("affinity",MATL_AFFIN, IF_REAL, "Electron affinity"), |
|||
IP("permittivity",MATL_PERMIT,IF_REAL, "Dielectric permittivity"), |
|||
IP("epsilon", MATL_PERMIT, IF_REAL, "Dielectric permittivity"), |
|||
IP("tn", MATL_TAUN0, IF_REAL, "SRH electron lifetime"), |
|||
IP("tn0", MATL_TAUN0, IF_REAL, "SRH electron lifetime"), |
|||
IP("taun0", MATL_TAUN0, IF_REAL, "SRH electron lifetime"), |
|||
IP("tp", MATL_TAUP0, IF_REAL, "SRH hole lifetime"), |
|||
IP("tp0", MATL_TAUP0, IF_REAL, "SRH hole lifetime"), |
|||
IP("taup0", MATL_TAUP0, IF_REAL, "SRH hole lifetime"), |
|||
IP("nsrhn", MATL_NSRHN, IF_REAL, "SRH reference conc (electrons)"), |
|||
IP("srh.nref",MATL_NSRHN, IF_REAL, "SRH reference conc (electrons)"), |
|||
IP("nsrhp", MATL_NSRHP, IF_REAL, "SRH reference conc (holes)"), |
|||
IP("srh.pref",MATL_NSRHP, IF_REAL, "SRH reference conc (holes)"), |
|||
IP("cn", MATL_CNAUG, IF_REAL, "Auger coefficient (electrons)"), |
|||
IP("cnaug", MATL_CNAUG, IF_REAL, "Auger coefficient (electrons)"), |
|||
IP("augn", MATL_CNAUG, IF_REAL, "Auger coefficient (electrons)"), |
|||
IP("cp", MATL_CPAUG, IF_REAL, "Auger coefficient (holes)"), |
|||
IP("cpaug", MATL_CPAUG, IF_REAL, "Auger coefficient (holes)"), |
|||
IP("augp", MATL_CPAUG, IF_REAL, "Auger coefficient (holes)"), |
|||
IP("arichn", MATL_ARICHN, IF_REAL, "Richardson constant (electrons)"), |
|||
IP("arichp", MATL_ARICHP, IF_REAL, "Richardson constant (holes)") |
|||
}; |
|||
|
|||
IFcardInfo MATLinfo = { |
|||
"material", |
|||
"Specify physical properties of a material", |
|||
NUMELEMS(MATLpTable), |
|||
MATLpTable, |
|||
|
|||
MATLnewCard, |
|||
MATLparam, |
|||
NULL |
|||
}; |
|||
|
|||
IFcardInfo PHYSinfo = { |
|||
"physics", |
|||
"Specify physical properties of a material", |
|||
NUMELEMS(MATLpTable), |
|||
MATLpTable, |
|||
|
|||
MATLnewCard, |
|||
MATLparam, |
|||
NULL |
|||
}; |
|||
|
|||
int |
|||
MATLnewCard(void **inCard, void *inModel) |
|||
{ |
|||
MATLcard *tmpCard, *newCard; |
|||
GENnumModel *model = (GENnumModel *)inModel; |
|||
|
|||
newCard = NEW( MATLcard ); |
|||
if (!newCard) { |
|||
*inCard = (void *)NULL; |
|||
return(E_NOMEM); |
|||
} |
|||
newCard->MATLnextCard = (MATLcard *)NULL; |
|||
*inCard = (void *)newCard; |
|||
|
|||
tmpCard = model->GENmaterials; |
|||
if (!tmpCard) { /* First in list */ |
|||
model->GENmaterials = newCard; |
|||
} else { |
|||
/* Go to end of list */ |
|||
while (tmpCard->MATLnextCard) tmpCard = tmpCard->MATLnextCard; |
|||
/* And add new card */ |
|||
tmpCard->MATLnextCard = newCard; |
|||
} |
|||
return(OK); |
|||
} |
|||
|
|||
int |
|||
MATLparam(int param, IFvalue *value, void *inCard) |
|||
{ |
|||
MATLcard *card = (MATLcard *)inCard; |
|||
|
|||
switch (param) { |
|||
case MATL_NUMBER: |
|||
card->MATLnumber = value->iValue; |
|||
card->MATLnumberGiven = TRUE; |
|||
break; |
|||
case MATL_NC0: |
|||
card->MATLnc0 = value->rValue; |
|||
card->MATLnc0Given = TRUE; |
|||
break; |
|||
case MATL_NV0: |
|||
card->MATLnv0 = value->rValue; |
|||
card->MATLnv0Given = TRUE; |
|||
break; |
|||
case MATL_EG0: |
|||
card->MATLeg0 = value->rValue; |
|||
card->MATLeg0Given = TRUE; |
|||
break; |
|||
case MATL_DEGDT: |
|||
card->MATLdEgdT = value->rValue; |
|||
card->MATLdEgdTGiven = TRUE; |
|||
break; |
|||
case MATL_TREF_EG: |
|||
card->MATLtrefEg = value->rValue; |
|||
card->MATLtrefEgGiven = TRUE; |
|||
break; |
|||
case MATL_DEGDC: |
|||
card->MATLdEgdN = value->rValue; |
|||
card->MATLdEgdNGiven = TRUE; |
|||
card->MATLdEgdP = value->rValue; |
|||
card->MATLdEgdPGiven = TRUE; |
|||
break; |
|||
case MATL_CREF_EG: |
|||
card->MATLnrefEg = value->rValue; |
|||
card->MATLnrefEgGiven = TRUE; |
|||
card->MATLprefEg = value->rValue; |
|||
card->MATLprefEgGiven = TRUE; |
|||
break; |
|||
case MATL_DEGDN: |
|||
card->MATLdEgdN = value->rValue; |
|||
card->MATLdEgdNGiven = TRUE; |
|||
break; |
|||
case MATL_NREF_EG: |
|||
card->MATLnrefEg = value->rValue; |
|||
card->MATLnrefEgGiven = TRUE; |
|||
break; |
|||
case MATL_DEGDP: |
|||
card->MATLdEgdP = value->rValue; |
|||
card->MATLdEgdPGiven = TRUE; |
|||
break; |
|||
case MATL_PREF_EG: |
|||
card->MATLprefEg = value->rValue; |
|||
card->MATLprefEgGiven = TRUE; |
|||
break; |
|||
case MATL_AFFIN: |
|||
card->MATLaffinity = value->rValue; |
|||
card->MATLaffinityGiven = TRUE; |
|||
break; |
|||
case MATL_PERMIT: |
|||
card->MATLpermittivity = value->rValue; |
|||
card->MATLpermittivityGiven = TRUE; |
|||
break; |
|||
case MATL_TAUN0: |
|||
card->MATLtaun0 = value->rValue; |
|||
card->MATLtaun0Given = TRUE; |
|||
break; |
|||
case MATL_TAUP0: |
|||
card->MATLtaup0 = value->rValue; |
|||
card->MATLtaup0Given = TRUE; |
|||
break; |
|||
case MATL_NSRHN: |
|||
card->MATLnrefSRHn = value->rValue; |
|||
card->MATLnrefSRHnGiven = TRUE; |
|||
break; |
|||
case MATL_NSRHP: |
|||
card->MATLnrefSRHp = value->rValue; |
|||
card->MATLnrefSRHpGiven = TRUE; |
|||
break; |
|||
case MATL_CNAUG: |
|||
card->MATLcnAug = value->rValue; |
|||
card->MATLcnAugGiven = TRUE; |
|||
break; |
|||
case MATL_CPAUG: |
|||
card->MATLcpAug = value->rValue; |
|||
card->MATLcpAugGiven = TRUE; |
|||
break; |
|||
case MATL_ARICHN: |
|||
card->MATLaRichN = value->rValue; |
|||
card->MATLaRichNGiven = TRUE; |
|||
break; |
|||
case MATL_ARICHP: |
|||
card->MATLaRichP = value->rValue; |
|||
card->MATLaRichPGiven = TRUE; |
|||
break; |
|||
case MATL_INSULATOR: |
|||
if ( value->iValue ) { |
|||
card->MATLmaterial = INSULATOR; |
|||
card->MATLmaterialGiven = TRUE; |
|||
} else { |
|||
if ( card->MATLmaterial == INSULATOR ) { |
|||
card->MATLmaterial = -1; |
|||
card->MATLmaterialGiven = FALSE; |
|||
} |
|||
} |
|||
break; |
|||
case MATL_OXIDE: |
|||
if ( value->iValue ) { |
|||
card->MATLmaterial = OXIDE; |
|||
card->MATLmaterialGiven = TRUE; |
|||
} else { |
|||
if ( card->MATLmaterial == OXIDE ) { |
|||
card->MATLmaterial = -1; |
|||
card->MATLmaterialGiven = FALSE; |
|||
} |
|||
} |
|||
break; |
|||
case MATL_NITRIDE: |
|||
if ( value->iValue ) { |
|||
card->MATLmaterial = NITRIDE; |
|||
card->MATLmaterialGiven = TRUE; |
|||
} else { |
|||
if ( card->MATLmaterial == NITRIDE ) { |
|||
card->MATLmaterial = -1; |
|||
card->MATLmaterialGiven = FALSE; |
|||
} |
|||
} |
|||
break; |
|||
case MATL_SEMICON: |
|||
if ( value->iValue ) { |
|||
card->MATLmaterial = SEMICON; |
|||
card->MATLmaterialGiven = TRUE; |
|||
} else { |
|||
if ( card->MATLmaterial == SEMICON ) { |
|||
card->MATLmaterial = -1; |
|||
card->MATLmaterialGiven = FALSE; |
|||
} |
|||
} |
|||
break; |
|||
case MATL_SILICON: |
|||
if ( value->iValue ) { |
|||
card->MATLmaterial = SILICON; |
|||
card->MATLmaterialGiven = TRUE; |
|||
} else { |
|||
if ( card->MATLmaterial == SILICON ) { |
|||
card->MATLmaterial = -1; |
|||
card->MATLmaterialGiven = FALSE; |
|||
} |
|||
} |
|||
break; |
|||
case MATL_POLYSIL: |
|||
if ( value->iValue ) { |
|||
card->MATLmaterial = POLYSILICON; |
|||
card->MATLmaterialGiven = TRUE; |
|||
} else { |
|||
if ( card->MATLmaterial == POLYSILICON ) { |
|||
card->MATLmaterial = -1; |
|||
card->MATLmaterialGiven = FALSE; |
|||
} |
|||
} |
|||
break; |
|||
case MATL_GAAS: |
|||
if ( value->iValue ) { |
|||
card->MATLmaterial = GAAS; |
|||
card->MATLmaterialGiven = TRUE; |
|||
} else { |
|||
if ( card->MATLmaterial == GAAS ) { |
|||
card->MATLmaterial = -1; |
|||
card->MATLmaterialGiven = FALSE; |
|||
} |
|||
} |
|||
break; |
|||
default: |
|||
return(E_BADPARM); |
|||
break; |
|||
} |
|||
return(OK); |
|||
} |
|||
@ -0,0 +1,180 @@ |
|||
/********** |
|||
Copyright 1992 Regents of the University of California. All rights reserved. |
|||
Author: 1992 David A. Gates, U. C. Berkeley CAD Group |
|||
Modified: 2001 Paolo Nenzi |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "cktdefs.h" |
|||
#include "numconst.h" |
|||
#include "numenum.h" |
|||
#include "matldefs.h" |
|||
#include "material.h" |
|||
#include "sperror.h" |
|||
#include "suffix.h" |
|||
|
|||
|
|||
extern int MATLcheck( MATLcard * ); |
|||
extern int MATLsetup( MATLcard *, MaterialInfo ** ); |
|||
|
|||
/* |
|||
* Name: MATLcheck |
|||
* Purpose: checks a list of MATLcards for input errors |
|||
* Formals: cardList: the list to check |
|||
* Returns: OK/E_PRIVATE |
|||
* Users: numerical device setup routines |
|||
* Calls: error message handler |
|||
*/ |
|||
int |
|||
MATLcheck(MATLcard *cardList) |
|||
{ |
|||
MATLcard *card, *card2; |
|||
int cardNum = 0, cardNum2; |
|||
int error = OK; |
|||
char ebuf[512]; /* error message buffer */ |
|||
|
|||
for ( card = cardList; card != NIL(MATLcard); card = card->MATLnextCard ) { |
|||
cardNum++; |
|||
|
|||
if( !card->MATLmaterialGiven ) { |
|||
card->MATLmaterial = SILICON; |
|||
} |
|||
if (!card->MATLnumberGiven) { |
|||
sprintf( ebuf, |
|||
"material card %d is missing an id number", |
|||
cardNum ); |
|||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) ); |
|||
error = E_PRIVATE; |
|||
} |
|||
|
|||
/* Return now if anything has failed */ |
|||
if (error) return(error); |
|||
|
|||
/* Make sure this id is different from all previous ones. */ |
|||
cardNum2 = 0; |
|||
for ( card2 = cardList; card2 != card; card2 = card2->MATLnextCard ) { |
|||
cardNum2++; |
|||
if (card2->MATLnumber == card->MATLnumber) { |
|||
sprintf( ebuf, |
|||
"material cards %d and %d use same id %d", |
|||
cardNum2, cardNum, card->MATLnumber ); |
|||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) ); |
|||
error = E_PRIVATE; |
|||
} |
|||
} |
|||
|
|||
/* Return now if anything has failed */ |
|||
if (error) return(error); |
|||
} |
|||
return(OK); |
|||
} |
|||
|
|||
|
|||
|
|||
/* |
|||
* Name: MATLsetup |
|||
* Purpose: setup the physical model parameters |
|||
* Formals: cardList: list of cards to setup |
|||
* Returns: OK/E_PRIVATE |
|||
* Users: numerical devices |
|||
* Calls: MATLcheck |
|||
*/ |
|||
int |
|||
MATLsetup(MATLcard *cardList, MaterialInfo **materialList) |
|||
{ |
|||
MATLcard *card; |
|||
MATLmaterial *newMaterial = NULL; |
|||
int error; |
|||
|
|||
/* Initialize list of electrodes */ |
|||
*materialList = NIL(MATLmaterial); |
|||
|
|||
/* Check the card list */ |
|||
if ((error = MATLcheck( cardList ))) return( error ); |
|||
|
|||
for ( card = cardList; card != NIL(MATLcard); card = card->MATLnextCard ) { |
|||
|
|||
if (*materialList == NIL(MATLmaterial)) { |
|||
RALLOC( newMaterial, MATLmaterial, 1 ); |
|||
*materialList = newMaterial; |
|||
} |
|||
else { |
|||
RALLOC( newMaterial->next, MATLmaterial, 1 ); |
|||
newMaterial = newMaterial->next; |
|||
} |
|||
newMaterial->next = NIL(MATLmaterial); |
|||
newMaterial->id = card->MATLnumber; |
|||
newMaterial->material = card->MATLmaterial; |
|||
|
|||
/* Fill in default values */ |
|||
MATLdefaults( newMaterial ); |
|||
|
|||
/* Now override with parameters set on the card */ |
|||
if ( card->MATLpermittivityGiven ) { |
|||
newMaterial->eps = card->MATLpermittivity; |
|||
/* Multiply by permittivity of free space if relative epsilon given. */ |
|||
if (newMaterial->eps > 0.1) { |
|||
newMaterial->eps *= EPS0; |
|||
} |
|||
} |
|||
if ( card->MATLaffinityGiven ) { |
|||
newMaterial->affin = card->MATLaffinity; |
|||
} |
|||
if ( card->MATLnc0Given ) { |
|||
newMaterial->nc0 = card->MATLnc0; |
|||
} |
|||
if ( card->MATLnv0Given ) { |
|||
newMaterial->nv0 = card->MATLnv0; |
|||
} |
|||
if ( card->MATLeg0Given ) { |
|||
newMaterial->eg0 = card->MATLeg0; |
|||
} |
|||
if ( card->MATLdEgdTGiven ) { |
|||
newMaterial->dEgDt = card->MATLdEgdT; |
|||
} |
|||
if ( card->MATLtrefEgGiven ) { |
|||
newMaterial->trefBGN = card->MATLtrefEg; |
|||
} |
|||
if ( card->MATLdEgdNGiven ) { |
|||
newMaterial->dEgDn[ELEC] = card->MATLdEgdN; |
|||
} |
|||
if ( card->MATLnrefEgGiven ) { |
|||
newMaterial->nrefBGN[ELEC] = card->MATLnrefEg; |
|||
} |
|||
if ( card->MATLdEgdPGiven ) { |
|||
newMaterial->dEgDn[HOLE] = card->MATLdEgdP; |
|||
} |
|||
if ( card->MATLprefEgGiven ) { |
|||
newMaterial->nrefBGN[HOLE] = card->MATLprefEg; |
|||
} |
|||
if ( card->MATLtaup0Given ) { |
|||
newMaterial->tau0[HOLE] = card->MATLtaup0; |
|||
} |
|||
if ( card->MATLtaun0Given ) { |
|||
newMaterial->tau0[ELEC] = card->MATLtaun0; |
|||
} |
|||
if ( card->MATLtaup0Given ) { |
|||
newMaterial->tau0[HOLE] = card->MATLtaup0; |
|||
} |
|||
if ( card->MATLnrefSRHnGiven ) { |
|||
newMaterial->nrefSRH[ELEC] = card->MATLnrefSRHn; |
|||
} |
|||
if ( card->MATLnrefSRHpGiven ) { |
|||
newMaterial->nrefSRH[HOLE] = card->MATLnrefSRHp; |
|||
} |
|||
if ( card->MATLcnAugGiven ) { |
|||
newMaterial->cAug[ELEC] = card->MATLcnAug; |
|||
} |
|||
if ( card->MATLcpAugGiven ) { |
|||
newMaterial->cAug[HOLE] = card->MATLcpAug; |
|||
} |
|||
if ( card->MATLaRichNGiven ) { |
|||
newMaterial->aRich[ELEC] = card->MATLaRichN; |
|||
} |
|||
if ( card->MATLaRichPGiven ) { |
|||
newMaterial->aRich[HOLE] = card->MATLaRichP; |
|||
} |
|||
|
|||
} |
|||
return( OK ); |
|||
} |
|||
@ -0,0 +1,148 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group |
|||
Modified: 2001 Paolo Nenzi |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numcards.h" |
|||
#include "numgen.h" |
|||
#include "meshdefs.h" |
|||
#include "devdefs.h" |
|||
#include "sperror.h" |
|||
#include "suffix.h" |
|||
|
|||
extern int XMSHnewCard(void**,void*); |
|||
extern int YMSHnewCard(void**,void*); |
|||
extern int MESHparam(int,IFvalue*,void*); |
|||
|
|||
|
|||
|
|||
IFparm MESHpTable[] = { |
|||
IP("location",MESH_LOCATION, IF_REAL, "Meshline location"), |
|||
IP("width", MESH_WIDTH, IF_REAL, "Distance to next line"), |
|||
IP("number", MESH_NUMBER, IF_INTEGER, "Meshline number"), |
|||
IP("node", MESH_NUMBER, IF_INTEGER, "Meshline number"), |
|||
IP("ratio", MESH_RATIO, IF_REAL, "Suggested spacing ratio"), |
|||
IP("h.start", MESH_H_START, IF_REAL, "Spacing at start of interval"), |
|||
IP("h1", MESH_H_START, IF_REAL, "Spacing at start of interVal"), |
|||
IP("h.end", MESH_H_END, IF_REAL, "Spacing at end of interval"), |
|||
IP("h2", MESH_H_END, IF_REAL, "Spacing at end of interval"), |
|||
IP("h.max", MESH_H_MAX, IF_REAL, "Max spacing during interval"), |
|||
IP("h3", MESH_H_MAX, IF_REAL, "Max spacing during interval") |
|||
}; |
|||
|
|||
IFcardInfo XMSHinfo = { |
|||
"x.mesh", |
|||
"Location of mesh lines", |
|||
NUMELEMS(MESHpTable), |
|||
MESHpTable, |
|||
|
|||
XMSHnewCard, |
|||
MESHparam, |
|||
NULL |
|||
}; |
|||
|
|||
IFcardInfo YMSHinfo = { |
|||
"y.mesh", |
|||
"Location of mesh lines", |
|||
NUMELEMS(MESHpTable), |
|||
MESHpTable, |
|||
|
|||
YMSHnewCard, |
|||
MESHparam, |
|||
NULL |
|||
}; |
|||
|
|||
int |
|||
XMSHnewCard(void **inCard, void *inModel) |
|||
{ |
|||
MESHcard *tmpCard, *newCard; |
|||
GENnumModel *model = (GENnumModel *)inModel; |
|||
|
|||
newCard = NEW( MESHcard ); |
|||
if (!newCard) { |
|||
*inCard = (void *)NULL; |
|||
return(E_NOMEM); |
|||
} |
|||
newCard->MESHnextCard = (MESHcard *)NULL; |
|||
*inCard = (void *)newCard; |
|||
|
|||
tmpCard = model->GENxMeshes; |
|||
if (!tmpCard) { /* First in list */ |
|||
model->GENxMeshes = newCard; |
|||
} else { |
|||
/* Go to end of list */ |
|||
while (tmpCard->MESHnextCard) tmpCard = tmpCard->MESHnextCard; |
|||
/* And add new card */ |
|||
tmpCard->MESHnextCard = newCard; |
|||
} |
|||
return(OK); |
|||
} |
|||
|
|||
int |
|||
YMSHnewCard(void **inCard, void *inModel) |
|||
{ |
|||
MESHcard *tmpCard, *newCard; |
|||
GENnumModel *model = (GENnumModel *)inModel; |
|||
|
|||
newCard = NEW( MESHcard ); |
|||
if (!newCard) { |
|||
*inCard = (void *)NULL; |
|||
return(E_NOMEM); |
|||
} |
|||
newCard->MESHnextCard = (MESHcard *)NULL; |
|||
*inCard = (void *)newCard; |
|||
|
|||
tmpCard = model->GENyMeshes; |
|||
if (!tmpCard) { /* First in list */ |
|||
model->GENyMeshes = newCard; |
|||
} else { |
|||
/* Go to end of list */ |
|||
while (tmpCard->MESHnextCard) tmpCard = tmpCard->MESHnextCard; |
|||
/* And add new card */ |
|||
tmpCard->MESHnextCard = newCard; |
|||
} |
|||
return(OK); |
|||
} |
|||
|
|||
int |
|||
MESHparam(int param, IFvalue *value, void *inCard) |
|||
{ |
|||
MESHcard *card = (MESHcard *)inCard; |
|||
|
|||
switch (param) { |
|||
case MESH_LOCATION: |
|||
card->MESHlocation = value->rValue; |
|||
card->MESHlocationGiven = TRUE; |
|||
break; |
|||
case MESH_WIDTH: |
|||
card->MESHwidth = value->rValue; |
|||
card->MESHwidthGiven = TRUE; |
|||
break; |
|||
case MESH_H_START: |
|||
card->MESHhStart = value->rValue; |
|||
card->MESHhStartGiven = TRUE; |
|||
break; |
|||
case MESH_H_END: |
|||
card->MESHhEnd = value->rValue; |
|||
card->MESHhEndGiven = TRUE; |
|||
break; |
|||
case MESH_H_MAX: |
|||
card->MESHhMax = value->rValue; |
|||
card->MESHhMaxGiven = TRUE; |
|||
break; |
|||
case MESH_RATIO: |
|||
card->MESHratio = value->rValue; |
|||
card->MESHratioGiven = TRUE; |
|||
break; |
|||
case MESH_NUMBER: |
|||
card->MESHnumber = value->iValue; |
|||
card->MESHnumberGiven = TRUE; |
|||
break; |
|||
default: |
|||
return(E_BADPARM); |
|||
break; |
|||
} |
|||
return(OK); |
|||
} |
|||
1302
src/ciderlib/input/meshset.c
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
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); |
|||
} |
|||
@ -0,0 +1,210 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group |
|||
Modified: 2001 Paolo Nenzi |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numcards.h" |
|||
#include "numgen.h" |
|||
#include "numenum.h" |
|||
#include "mobdefs.h" |
|||
#include "sperror.h" |
|||
#include "devdefs.h" |
|||
#include "suffix.h" |
|||
|
|||
extern int MOBnewCard(void**,void*); |
|||
extern int MOBparam(int,IFvalue*,void*); |
|||
|
|||
|
|||
|
|||
IFparm MOBpTable[] = { |
|||
IP("material",MOB_MATERIAL, IF_INTEGER, "Material index"), |
|||
IP("electron",MOB_ELEC, IF_FLAG, "Electron mobility flag"), |
|||
IP("hole", MOB_HOLE, IF_FLAG, "Hole mobility flag"), |
|||
IP("majority",MOB_MAJOR, IF_FLAG, "Majority carrier flag"), |
|||
IP("minority",MOB_MINOR, IF_FLAG, "Minority carrier flag"), |
|||
IP("mumax", MOB_MUMAX, IF_REAL, "Maximum bulk mobility"), |
|||
IP("mumin", MOB_MUMIN, IF_REAL, "Minimum bulk mobility"), |
|||
IP("ntref", MOB_NTREF, IF_REAL, "Ionized impurity ref. conc."), |
|||
IP("ntexp", MOB_NTEXP, IF_REAL, "Ionized impurity exponent"), |
|||
IP("vsat", MOB_VSAT, IF_REAL, "Saturation velocity"), |
|||
IP("vwarm", MOB_VWARM, IF_REAL, "Warm carrier ref. velocity"), |
|||
IP("mus", MOB_MUS, IF_REAL, "Initial surface mobility"), |
|||
IP("ec.a", MOB_EC_A, IF_REAL, "Surf. mobility critical field"), |
|||
IP("ec.b", MOB_EC_B, IF_REAL, "Surf. mobility critical field #2"), |
|||
IP("concmodel",MOB_CONC_MOD, IF_STRING, "Concentration dep. model"), |
|||
IP("fieldmodel",MOB_FIELD_MOD,IF_STRING, "Driving field dep. model"), |
|||
IP("init", MOB_INIT, IF_FLAG, "Initialize with defaults") |
|||
}; |
|||
|
|||
IFcardInfo MOBinfo = { |
|||
"mobility", |
|||
"Specify parameters and types of mobility models", |
|||
NUMELEMS(MOBpTable), |
|||
MOBpTable, |
|||
|
|||
MOBnewCard, |
|||
MOBparam, |
|||
NULL |
|||
}; |
|||
|
|||
int |
|||
MOBnewCard(void **inCard, void *inModel) |
|||
{ |
|||
MOBcard *tmpCard, *newCard; |
|||
GENnumModel *model = (GENnumModel *)inModel; |
|||
|
|||
newCard = NEW( MOBcard ); |
|||
if (!newCard) { |
|||
*inCard = (void *)NULL; |
|||
return(E_NOMEM); |
|||
} |
|||
newCard->MOBnextCard = (MOBcard *)NULL; |
|||
*inCard = (void *)newCard; |
|||
|
|||
tmpCard = model->GENmobility; |
|||
if (!tmpCard) { /* First in list */ |
|||
model->GENmobility = newCard; |
|||
} else { |
|||
/* Go to end of list */ |
|||
while (tmpCard->MOBnextCard) tmpCard = tmpCard->MOBnextCard; |
|||
/* And add new card */ |
|||
tmpCard->MOBnextCard = newCard; |
|||
} |
|||
return(OK); |
|||
} |
|||
|
|||
int |
|||
MOBparam(int param, IFvalue *value, void *inCard) |
|||
{ |
|||
MOBcard *card = (MOBcard *)inCard; |
|||
|
|||
switch (param) { |
|||
case MOB_MATERIAL: |
|||
card->MOBmaterial = value->iValue; |
|||
card->MOBmaterialGiven = TRUE; |
|||
break; |
|||
case MOB_ELEC: |
|||
if ( value->iValue ) { |
|||
card->MOBcarrier = ELEC; |
|||
card->MOBcarrierGiven = TRUE; |
|||
} else { |
|||
if ( card->MOBcarrier == ELEC ) { |
|||
card->MOBcarrier = -1; |
|||
card->MOBcarrierGiven = FALSE; |
|||
} |
|||
} |
|||
break; |
|||
case MOB_HOLE: |
|||
if ( value->iValue ) { |
|||
card->MOBcarrier = HOLE; |
|||
card->MOBcarrierGiven = TRUE; |
|||
} else { |
|||
if ( card->MOBcarrier == HOLE ) { |
|||
card->MOBcarrier = -1; |
|||
card->MOBcarrierGiven = FALSE; |
|||
} |
|||
} |
|||
break; |
|||
case MOB_MAJOR: |
|||
if ( value->iValue ) { |
|||
card->MOBcarrType = MAJOR; |
|||
card->MOBcarrTypeGiven = TRUE; |
|||
} else { |
|||
if ( card->MOBcarrType == MAJOR ) { |
|||
card->MOBcarrType = -1; |
|||
card->MOBcarrTypeGiven = FALSE; |
|||
} |
|||
} |
|||
break; |
|||
case MOB_MINOR: |
|||
if ( value->iValue ) { |
|||
card->MOBcarrType = MINOR; |
|||
card->MOBcarrTypeGiven = TRUE; |
|||
} else { |
|||
if ( card->MOBcarrType == MINOR ) { |
|||
card->MOBcarrType = -1; |
|||
card->MOBcarrTypeGiven = FALSE; |
|||
} |
|||
} |
|||
break; |
|||
case MOB_MUMAX: |
|||
card->MOBmuMax = value->rValue; |
|||
card->MOBmuMaxGiven = TRUE; |
|||
break; |
|||
case MOB_MUMIN: |
|||
card->MOBmuMin = value->rValue; |
|||
card->MOBmuMinGiven = TRUE; |
|||
break; |
|||
case MOB_NTREF: |
|||
card->MOBntRef = value->rValue; |
|||
card->MOBntRefGiven = TRUE; |
|||
break; |
|||
case MOB_NTEXP: |
|||
card->MOBntExp = value->rValue; |
|||
card->MOBntExpGiven = TRUE; |
|||
break; |
|||
case MOB_VSAT: |
|||
card->MOBvSat = value->rValue; |
|||
card->MOBvSatGiven = TRUE; |
|||
break; |
|||
case MOB_VWARM: |
|||
card->MOBvWarm = value->rValue; |
|||
card->MOBvWarmGiven = TRUE; |
|||
break; |
|||
case MOB_MUS: |
|||
card->MOBmus = value->rValue; |
|||
card->MOBmusGiven = TRUE; |
|||
break; |
|||
case MOB_EC_A: |
|||
card->MOBecA = value->rValue; |
|||
card->MOBecAGiven = TRUE; |
|||
break; |
|||
case MOB_EC_B: |
|||
card->MOBecB = value->rValue; |
|||
card->MOBecBGiven = TRUE; |
|||
break; |
|||
case MOB_CONC_MOD: |
|||
if ( cinprefix( value->sValue, "ct", 1 ) ) { |
|||
card->MOBconcModel = CT; |
|||
card->MOBconcModelGiven = TRUE; |
|||
} else if ( cinprefix( value->sValue, "ar", 1 ) ) { |
|||
card->MOBconcModel = AR; |
|||
card->MOBconcModelGiven = TRUE; |
|||
} else if ( cinprefix( value->sValue, "uf", 1 ) ) { |
|||
card->MOBconcModel = UF; |
|||
card->MOBconcModelGiven = TRUE; |
|||
} else if ( cinprefix( value->sValue, "sg", 1 ) ) { |
|||
card->MOBconcModel = SG; |
|||
card->MOBconcModelGiven = TRUE; |
|||
} else if ( cinprefix( value->sValue, "ga", 1 ) ) { |
|||
card->MOBconcModel = GA; |
|||
card->MOBconcModelGiven = TRUE; |
|||
} |
|||
break; |
|||
case MOB_FIELD_MOD: |
|||
if ( cinprefix( value->sValue, "ct", 1 ) ) { |
|||
card->MOBfieldModel = CT; |
|||
card->MOBfieldModelGiven = TRUE; |
|||
} else if ( cinprefix( value->sValue, "ar", 1 ) ) { |
|||
card->MOBfieldModel = AR; |
|||
card->MOBfieldModelGiven = TRUE; |
|||
} else if ( cinprefix( value->sValue, "sg", 1 ) ) { |
|||
card->MOBfieldModel = SG; |
|||
card->MOBfieldModelGiven = TRUE; |
|||
} else if ( cinprefix( value->sValue, "ga", 1 ) ) { |
|||
card->MOBfieldModel = GA; |
|||
card->MOBfieldModelGiven = TRUE; |
|||
} |
|||
break; |
|||
case MOB_INIT: |
|||
card->MOBinit = value->iValue; |
|||
card->MOBinitGiven = TRUE; |
|||
break; |
|||
default: |
|||
return(E_BADPARM); |
|||
break; |
|||
} |
|||
return(OK); |
|||
} |
|||
@ -0,0 +1,157 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group |
|||
Modified: 2001 Paolo Nenzi |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "cktdefs.h" |
|||
#include "numconst.h" |
|||
#include "numenum.h" |
|||
#include "mobdefs.h" |
|||
#include "material.h" |
|||
#include "sperror.h" |
|||
#include "suffix.h" |
|||
|
|||
extern int MOBcheck( MOBcard *, MaterialInfo * ); |
|||
extern int MOBsetup( MOBcard *, MaterialInfo * ); |
|||
|
|||
/* |
|||
* Name: MOBcheck |
|||
* Purpose: checks a list of MOBcards for input errors |
|||
* Formals: cardList: the list to check |
|||
* Returns: OK/E_PRIVATE |
|||
* Users: numerical device setup routines |
|||
* Calls: error message handler |
|||
*/ |
|||
int |
|||
MOBcheck(MOBcard *cardList, MaterialInfo *matlList) |
|||
{ |
|||
MOBcard *card; |
|||
MATLmaterial *matl; |
|||
int cardNum = 0; |
|||
int error = OK; |
|||
char ebuf[512]; /* error message buffer */ |
|||
|
|||
for ( card = cardList; card != NIL(MOBcard); card = card->MOBnextCard ) { |
|||
cardNum++; |
|||
if (!card->MOBmaterialGiven) { |
|||
sprintf( ebuf, |
|||
"mobility card %d is missing a material index", |
|||
cardNum ); |
|||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) ); |
|||
error = E_PRIVATE; |
|||
} else { |
|||
/* Make sure the material exists */ |
|||
for ( matl = matlList; matl != NIL(MATLmaterial); matl = matl->next ) { |
|||
if ( card->MOBmaterial == matl->id ) { |
|||
break; |
|||
} |
|||
} |
|||
if (matl == NIL(MATLmaterial)) { |
|||
sprintf( ebuf, |
|||
"mobility card %d specifies a non-existent material", |
|||
cardNum ); |
|||
SPfrontEnd->IFerror( ERR_WARNING, ebuf, NIL(IFuid) ); |
|||
error = E_PRIVATE; |
|||
} |
|||
} |
|||
if (!card->MOBcarrierGiven) { |
|||
card->MOBcarrier = ELEC; |
|||
} |
|||
if (!card->MOBcarrTypeGiven) { |
|||
card->MOBcarrType = MAJOR; |
|||
} |
|||
if (!card->MOBinitGiven) { |
|||
card->MOBinit = FALSE; |
|||
} |
|||
|
|||
/* Return now if anything has failed */ |
|||
if (error) return(error); |
|||
|
|||
} |
|||
return(OK); |
|||
} |
|||
|
|||
|
|||
|
|||
/* |
|||
* Name: MOBsetup |
|||
* Purpose: setup the mobility model parameters |
|||
* Formals: cardList: list of cards to setup |
|||
* Returns: OK/E_PRIVATE |
|||
* Users: numerical devices |
|||
* Calls: MOBcheck |
|||
*/ |
|||
int |
|||
MOBsetup(MOBcard *cardList, MaterialInfo *materialList) |
|||
{ |
|||
MOBcard *card; |
|||
MATLmaterial *matl; |
|||
int error; |
|||
|
|||
/* Check the card list */ |
|||
if ((error = MOBcheck( cardList, materialList ))) return( error ); |
|||
|
|||
for ( card = cardList; card != NIL(MOBcard); card = card->MOBnextCard ) { |
|||
|
|||
/* Find the right material */ |
|||
for ( matl = materialList; matl != NIL(MATLmaterial); matl = matl->next ) { |
|||
if ( card->MOBmaterial == matl->id ) { |
|||
break; |
|||
} |
|||
} |
|||
|
|||
/* Default models depend on previous value */ |
|||
if (!card->MOBconcModelGiven) { |
|||
card->MOBconcModel = matl->concModel; |
|||
} |
|||
if (!card->MOBfieldModelGiven) { |
|||
card->MOBfieldModel = matl->fieldModel; |
|||
} |
|||
|
|||
/* Load in default values if desired */ |
|||
if ( card->MOBinitGiven ) { |
|||
MOBdefaults( matl, card->MOBcarrier, card->MOBcarrType, |
|||
card->MOBconcModel, card->MOBfieldModel ); |
|||
} |
|||
|
|||
/* Override defaults */ |
|||
if ( card->MOBconcModelGiven ) { |
|||
matl->concModel = card->MOBconcModel; |
|||
} |
|||
if ( card->MOBfieldModelGiven ) { |
|||
matl->fieldModel = card->MOBfieldModel; |
|||
} |
|||
if ( card->MOBmuMaxGiven ) { |
|||
matl->muMax[card->MOBcarrier][card->MOBcarrType] = card->MOBmuMax; |
|||
} |
|||
if ( card->MOBmuMinGiven ) { |
|||
matl->muMin[card->MOBcarrier][card->MOBcarrType] = card->MOBmuMin; |
|||
} |
|||
if ( card->MOBntRefGiven ) { |
|||
matl->ntRef[card->MOBcarrier][card->MOBcarrType] = card->MOBntRef; |
|||
} |
|||
if ( card->MOBntExpGiven ) { |
|||
matl->ntExp[card->MOBcarrier][card->MOBcarrType] = card->MOBntExp; |
|||
} |
|||
if ( card->MOBvSatGiven ) { |
|||
matl->vSat[card->MOBcarrier] = card->MOBvSat; |
|||
} |
|||
if ( card->MOBvWarmGiven ) { |
|||
matl->vWarm[card->MOBcarrier] = card->MOBvWarm; |
|||
} |
|||
if ( card->MOBmusGiven ) { |
|||
matl->mus[card->MOBcarrier] = card->MOBmus; |
|||
} |
|||
if ( card->MOBecAGiven ) { |
|||
matl->thetaA[card->MOBcarrier] = 1.0 / MAX( card->MOBecA, 1e-20 ); |
|||
} |
|||
if ( card->MOBecBGiven ) { |
|||
matl->thetaB[card->MOBcarrier] = 1.0 / MAX( ABS(card->MOBecB), 1e-20 ); |
|||
matl->thetaB[card->MOBcarrier] *= matl->thetaB[card->MOBcarrier]; |
|||
matl->thetaB[card->MOBcarrier] *= SGN( card->MOBecB ); |
|||
} |
|||
} |
|||
return( OK ); |
|||
} |
|||
@ -0,0 +1,133 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group |
|||
Modified: 2001 Paolo Nenzi |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numcards.h" |
|||
#include "numgen.h" |
|||
#include "modldefs.h" |
|||
#include "devdefs.h" |
|||
#include "sperror.h" |
|||
#include "suffix.h" |
|||
|
|||
extern int MODLnewCard(void**,void*); |
|||
extern int MODLparam(int,IFvalue*,void*); |
|||
|
|||
|
|||
IFparm MODLpTable[] = { |
|||
IP("bgn", MODL_BGNW, IF_FLAG, "Bandgap narrowing"), |
|||
IP("bgnw", MODL_BGNW, IF_FLAG, "Bandgap narrowing"), |
|||
IP("tmpmob", MODL_TEMPMOB, IF_FLAG, "Temp-dependent mobility"), |
|||
IP("tempmob", MODL_TEMPMOB, IF_FLAG, "Temp-dependent mobility"), |
|||
IP("conmob", MODL_CONCMOB, IF_FLAG, "Conc-dependent mobility"), |
|||
IP("concmob", MODL_CONCMOB, IF_FLAG, "Conc-dependent mobility"), |
|||
IP("fldmob", MODL_FIELDMOB, IF_FLAG, |
|||
"Lateral-field-dependent mobility"), |
|||
IP("fieldmob",MODL_FIELDMOB, IF_FLAG, |
|||
"Lateral-field-dependent mobility"), |
|||
IP("trfmob",MODL_TRANSMOB, IF_FLAG, |
|||
"Transverse-field-dependent surface mobility"), |
|||
IP("transmob",MODL_TRANSMOB, IF_FLAG, |
|||
"Transverse-field-dependent surface mobility"), |
|||
IP("srfmob", MODL_SURFMOB, IF_FLAG, "Activate surface mobility"), |
|||
IP("surfmob", MODL_SURFMOB, IF_FLAG, "Activate surface mobility"), |
|||
IP("matchmob",MODL_MATCHMOB, IF_FLAG, |
|||
"Matching low-field surface/bulk mobilities"), |
|||
IP("srh", MODL_SRH, IF_FLAG, "SRH recombination"), |
|||
IP("consrh", MODL_CONCTAU, IF_FLAG, "Conc-dependent SRH recomb"), |
|||
IP("conctau", MODL_CONCTAU, IF_FLAG, "Conc-dependent SRH recomb"), |
|||
IP("auger", MODL_AUGER, IF_FLAG, "Auger recombination"), |
|||
IP("avalanche",MODL_AVAL, IF_FLAG, "Local avalanche generation") |
|||
}; |
|||
|
|||
IFcardInfo MODLinfo = { |
|||
"models", |
|||
"Specify which physical models should be simulated", |
|||
NUMELEMS(MODLpTable), |
|||
MODLpTable, |
|||
|
|||
MODLnewCard, |
|||
MODLparam, |
|||
NULL |
|||
}; |
|||
|
|||
int |
|||
MODLnewCard(void **inCard, void *inModel) |
|||
{ |
|||
MODLcard *tmpCard, *newCard; |
|||
GENnumModel *model = (GENnumModel *)inModel; |
|||
|
|||
tmpCard = model->GENmodels; |
|||
if (!tmpCard) { /* First in list */ |
|||
newCard = NEW( MODLcard ); |
|||
if (!newCard) { |
|||
*inCard = (void *)NULL; |
|||
return(E_NOMEM); |
|||
} |
|||
newCard->MODLnextCard = (MODLcard *)NULL; |
|||
*inCard = (void *)newCard; |
|||
model->GENmodels = newCard; |
|||
} else { /* Only one card of this type allowed */ |
|||
*inCard = (void *)tmpCard; |
|||
} |
|||
return(OK); |
|||
} |
|||
|
|||
int |
|||
MODLparam(int param, IFvalue *value, void *inCard) |
|||
{ |
|||
MODLcard *card = (MODLcard *)inCard; |
|||
|
|||
switch (param) { |
|||
case MODL_BGNW: |
|||
card->MODLbandGapNarrowing = value->iValue; |
|||
card->MODLbandGapNarrowingGiven = TRUE; |
|||
break; |
|||
case MODL_TEMPMOB: |
|||
card->MODLtempDepMobility = value->iValue; |
|||
card->MODLtempDepMobilityGiven = TRUE; |
|||
break; |
|||
case MODL_CONCMOB: |
|||
card->MODLconcDepMobility = value->iValue; |
|||
card->MODLconcDepMobilityGiven = TRUE; |
|||
break; |
|||
case MODL_TRANSMOB: |
|||
card->MODLtransDepMobility = value->iValue; |
|||
card->MODLtransDepMobilityGiven = TRUE; |
|||
break; |
|||
case MODL_FIELDMOB: |
|||
card->MODLfieldDepMobility = value->iValue; |
|||
card->MODLfieldDepMobilityGiven = TRUE; |
|||
break; |
|||
case MODL_SURFMOB: |
|||
card->MODLsurfaceMobility = value->iValue; |
|||
card->MODLsurfaceMobilityGiven = TRUE; |
|||
break; |
|||
case MODL_MATCHMOB: |
|||
card->MODLmatchingMobility = value->iValue; |
|||
card->MODLmatchingMobilityGiven = TRUE; |
|||
break; |
|||
case MODL_SRH: |
|||
card->MODLsrh = value->iValue; |
|||
card->MODLsrhGiven = TRUE; |
|||
break; |
|||
case MODL_CONCTAU: |
|||
card->MODLconcDepLifetime = value->iValue; |
|||
card->MODLconcDepLifetimeGiven = TRUE; |
|||
break; |
|||
case MODL_AUGER: |
|||
card->MODLauger = value->iValue; |
|||
card->MODLaugerGiven = TRUE; |
|||
break; |
|||
case MODL_AVAL: |
|||
card->MODLavalancheGen = value->iValue; |
|||
card->MODLavalancheGenGiven = TRUE; |
|||
break; |
|||
default: |
|||
return(E_BADPARM); |
|||
break; |
|||
} |
|||
return(OK); |
|||
} |
|||
@ -0,0 +1,92 @@ |
|||
/********** |
|||
Copyright 1992 Regents of the University of California. All rights reserved. |
|||
Author: 1992 David A. Gates, U. C. Berkeley CAD Group |
|||
Modified: 2001 Paolo Nenzi |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "cktdefs.h" |
|||
#include "modldefs.h" |
|||
#include "sperror.h" |
|||
#include "suffix.h" |
|||
|
|||
extern int MODLcheck( MODLcard * ); |
|||
extern int MODLsetup( MODLcard * ); |
|||
|
|||
/* |
|||
* Name: MODLcheck |
|||
* Purpose: checks a list of MODLcards for input errors, sets defaults |
|||
* Formals: cardList: the list to check |
|||
* Returns: OK/E_PRIVATE |
|||
* Users: numerical device setup routines |
|||
* Calls: error message handler |
|||
*/ |
|||
int |
|||
MODLcheck(MODLcard *cardList) |
|||
{ |
|||
MODLcard *card, *card2; |
|||
int cardNum = 0, cardNum2; |
|||
int error = OK; |
|||
char ebuf[512]; /* error message buffer */ |
|||
|
|||
for ( card = cardList; card != NIL(MODLcard); card = card->MODLnextCard ) { |
|||
cardNum++; |
|||
|
|||
if ( !card->MODLbandGapNarrowingGiven ) { |
|||
card->MODLbandGapNarrowing = FALSE; |
|||
} |
|||
if ( !card->MODLtempDepMobilityGiven ) { |
|||
card->MODLtempDepMobility = FALSE; |
|||
} |
|||
if ( !card->MODLconcDepMobilityGiven ) { |
|||
card->MODLconcDepMobility = FALSE; |
|||
} |
|||
if ( !card->MODLfieldDepMobilityGiven ) { |
|||
card->MODLfieldDepMobility = FALSE; |
|||
} |
|||
if ( !card->MODLtransDepMobilityGiven ) { |
|||
card->MODLtransDepMobility = FALSE; |
|||
} |
|||
if ( !card->MODLsurfaceMobilityGiven ) { |
|||
card->MODLsurfaceMobility = FALSE; |
|||
} |
|||
if ( !card->MODLmatchingMobilityGiven ) { |
|||
card->MODLmatchingMobility = FALSE; |
|||
} |
|||
if ( !card->MODLsrhGiven ) { |
|||
card->MODLsrh = FALSE; |
|||
} |
|||
if ( !card->MODLconcDepLifetimeGiven ) { |
|||
card->MODLconcDepLifetime = FALSE; |
|||
} |
|||
if ( !card->MODLaugerGiven ) { |
|||
card->MODLauger = FALSE; |
|||
} |
|||
if ( !card->MODLavalancheGenGiven ) { |
|||
card->MODLavalancheGen = FALSE; |
|||
} |
|||
} |
|||
return(OK); |
|||
} |
|||
|
|||
|
|||
|
|||
/* |
|||
* Name: MODLsetup |
|||
* Purpose: setup the physical models used |
|||
* Formals: cardList: list of cards to setup |
|||
* Returns: OK/E_PRIVATE |
|||
* Users: numerical devices |
|||
* Calls: MODLcheck |
|||
*/ |
|||
int |
|||
MODLsetup(MODLcard *cardList) |
|||
{ |
|||
int error; |
|||
|
|||
/* Check the card list */ |
|||
if ((error = MODLcheck( cardList ))) return( error ); |
|||
|
|||
/* Nothing else to do. */ |
|||
return( OK ); |
|||
} |
|||
@ -0,0 +1,163 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group |
|||
Modified: 2001 Paolo Nenzi |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numcards.h" |
|||
#include "numgen.h" |
|||
#include "optndefs.h" |
|||
#include "devdefs.h" |
|||
#include "sperror.h" |
|||
#include "suffix.h" |
|||
|
|||
#define M_TO_CM 1.0e2 |
|||
#define M2_TO_CM2 (M_TO_CM * M_TO_CM) |
|||
#define UM_TO_CM 1.0e-4 |
|||
#define UM2_TO_CM2 (UM_TO_CM * UM_TO_CM) |
|||
|
|||
extern int OPTNnewCard(void**,void*); |
|||
extern int OPTNparam(int,IFvalue*,void*); |
|||
|
|||
|
|||
|
|||
IFparm OPTNpTable[] = { |
|||
/* Supported Types of Devices. Ideally should be automatically extracted */ |
|||
IP("resistor",OPTN_RESISTOR, IF_FLAG, "Resistor"), |
|||
IP("capacitor",OPTN_CAPACITOR, IF_FLAG, "Capacitor"), |
|||
IP("diode", OPTN_DIODE, IF_FLAG, "Diode"), |
|||
IP("bipolar", OPTN_BIPOLAR, IF_FLAG, "Bipolar Transistor"), |
|||
IP("bjt", OPTN_BIPOLAR, IF_FLAG, "Bipolar Transistor"), |
|||
IP("soibjt", OPTN_SOIBJT, IF_FLAG, "SOI Bipolar"), |
|||
IP("moscap", OPTN_MOSCAP, IF_FLAG, "MOS Capacitor"), |
|||
IP("mosfet", OPTN_MOSFET, IF_FLAG, "MOSFET"), |
|||
IP("soimos", OPTN_SOIMOS, IF_FLAG, "SOI MOSFET"), |
|||
IP("jfet", OPTN_JFET, IF_FLAG, "Junction FET"), |
|||
IP("mesfet", OPTN_MESFET, IF_FLAG, "MESFET"), |
|||
/* Various layout dimensions */ |
|||
IP("defa", OPTN_DEFA, IF_REAL, "Default Mask Area"), |
|||
IP("defw", OPTN_DEFW, IF_REAL, "Default Mask Width"), |
|||
IP("defl", OPTN_DEFL, IF_REAL, "Default Mask Length"), |
|||
IP("base.area",OPTN_BASE_AREA, IF_REAL, "1D BJT Base Area"), |
|||
IP("base.length",OPTN_BASE_LENGTH,IF_REAL, "1D BJT Base Length"), |
|||
IP("base.depth",OPTN_BASE_DEPTH, IF_REAL, "1D BJT Base Depth"), |
|||
/* Values */ |
|||
IP("tnom", OPTN_TNOM, IF_REAL, "Nominal Temperature"), |
|||
/* Device Initial Condition File */ |
|||
IP("ic.file", OPTN_IC_FILE, IF_STRING, "Initial condition file"), |
|||
IP("unique", OPTN_UNIQUE, IF_FLAG, "Generate unique filename") |
|||
}; |
|||
|
|||
IFcardInfo OPTNinfo = { |
|||
"options", |
|||
"Provide optional information and hints", |
|||
NUMELEMS(OPTNpTable), |
|||
OPTNpTable, |
|||
|
|||
OPTNnewCard, |
|||
OPTNparam, |
|||
NULL |
|||
}; |
|||
|
|||
int |
|||
OPTNnewCard(void **inCard, void *inModel) |
|||
{ |
|||
OPTNcard *tmpCard, *newCard; |
|||
GENnumModel *model = (GENnumModel *)inModel; |
|||
|
|||
tmpCard = model->GENoptions; |
|||
if (!tmpCard) { /* First in list */ |
|||
newCard = NEW( OPTNcard ); |
|||
if (!newCard) { |
|||
*inCard = (void *)NULL; |
|||
return(E_NOMEM); |
|||
} |
|||
newCard->OPTNnextCard = (OPTNcard *)NULL; |
|||
*inCard = (void *)newCard; |
|||
model->GENoptions = newCard; |
|||
} else { /* Only one card of this type allowed */ |
|||
*inCard = (void *)tmpCard; |
|||
} |
|||
return(OK); |
|||
} |
|||
|
|||
int |
|||
OPTNparam(int param, IFvalue *value, void *inCard) |
|||
{ |
|||
OPTNcard *card = (OPTNcard *)inCard; |
|||
|
|||
switch (param) { |
|||
case OPTN_RESISTOR: |
|||
card->OPTNdeviceType = OPTN_RESISTOR; |
|||
card->OPTNdeviceTypeGiven = TRUE; |
|||
break; |
|||
case OPTN_CAPACITOR: |
|||
card->OPTNdeviceType = OPTN_CAPACITOR; |
|||
card->OPTNdeviceTypeGiven = TRUE; |
|||
break; |
|||
case OPTN_DIODE: |
|||
card->OPTNdeviceType = OPTN_DIODE; |
|||
card->OPTNdeviceTypeGiven = TRUE; |
|||
break; |
|||
case OPTN_MOSCAP: |
|||
card->OPTNdeviceType = OPTN_MOSCAP; |
|||
card->OPTNdeviceTypeGiven = TRUE; |
|||
break; |
|||
case OPTN_BIPOLAR: |
|||
case OPTN_SOIBJT: /* XXX Treat SOI as normal */ |
|||
card->OPTNdeviceType = OPTN_BIPOLAR; |
|||
card->OPTNdeviceTypeGiven = TRUE; |
|||
break; |
|||
case OPTN_MOSFET: |
|||
case OPTN_SOIMOS: /* XXX Treat SOI as normal */ |
|||
card->OPTNdeviceType = OPTN_MOSFET; |
|||
card->OPTNdeviceTypeGiven = TRUE; |
|||
break; |
|||
case OPTN_JFET: |
|||
case OPTN_MESFET: /* XXX Treat MES as junction */ |
|||
card->OPTNdeviceType = OPTN_JFET; |
|||
card->OPTNdeviceTypeGiven = TRUE; |
|||
break; |
|||
case OPTN_DEFA: |
|||
card->OPTNdefa = value->rValue * M2_TO_CM2; |
|||
card->OPTNdefaGiven = TRUE; |
|||
break; |
|||
case OPTN_DEFW: |
|||
card->OPTNdefw = value->rValue * M_TO_CM; |
|||
card->OPTNdefwGiven = TRUE; |
|||
break; |
|||
case OPTN_DEFL: |
|||
card->OPTNdefl = value->rValue * M_TO_CM; |
|||
card->OPTNdeflGiven = TRUE; |
|||
break; |
|||
case OPTN_BASE_AREA: |
|||
card->OPTNbaseArea = value->rValue; |
|||
card->OPTNbaseAreaGiven = TRUE; |
|||
break; |
|||
case OPTN_BASE_LENGTH: |
|||
card->OPTNbaseLength = value->rValue * UM_TO_CM; |
|||
card->OPTNbaseLengthGiven = TRUE; |
|||
break; |
|||
case OPTN_BASE_DEPTH: |
|||
card->OPTNbaseDepth = value->rValue * UM_TO_CM; |
|||
card->OPTNbaseDepthGiven = TRUE; |
|||
break; |
|||
case OPTN_TNOM: |
|||
card->OPTNtnom = value->rValue; |
|||
card->OPTNtnomGiven = TRUE; |
|||
break; |
|||
case OPTN_IC_FILE: |
|||
card->OPTNicFile = value->sValue; |
|||
card->OPTNicFileGiven = TRUE; |
|||
break; |
|||
case OPTN_UNIQUE: |
|||
card->OPTNunique = value->iValue; |
|||
card->OPTNuniqueGiven = TRUE; |
|||
break; |
|||
default: |
|||
return(E_BADPARM); |
|||
break; |
|||
} |
|||
return(OK); |
|||
} |
|||
@ -0,0 +1,148 @@ |
|||
/********** |
|||
Copyright 1992 Regents of the University of California. All rights reserved. |
|||
Author: 1992 David A. Gates, U. C. Berkeley CAD Group |
|||
Modified: 2001 Paolo Nenzi |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "cktdefs.h" |
|||
#include "numenum.h" |
|||
#include "outpdefs.h" |
|||
#include "sperror.h" |
|||
#include "suffix.h" |
|||
|
|||
extern int OUTPcheck( OUTPcard * ); |
|||
extern int OUTPsetup( OUTPcard * ); |
|||
|
|||
|
|||
/* |
|||
* Name: OUTPcheck |
|||
* Purpose: checks a list of OUTPcards for input errors, sets defaults |
|||
* Formals: cardList: the list to check |
|||
* Returns: OK/E_PRIVATE |
|||
* Users: numerical device setup routines, output routines |
|||
* Calls: error message handler |
|||
*/ |
|||
int |
|||
OUTPcheck(OUTPcard *cardList) |
|||
{ |
|||
OUTPcard *card, *card2; |
|||
int cardNum = 0, cardNum2; |
|||
int error = OK; |
|||
char ebuf[512]; /* error message buffer */ |
|||
|
|||
for ( card = cardList; card != NIL(OUTPcard); card = card->OUTPnextCard ) { |
|||
cardNum++; |
|||
|
|||
card->OUTPnumVars = -1; |
|||
|
|||
if ( !card->OUTPdcDebugGiven ) { |
|||
card->OUTPdcDebug = FALSE; |
|||
} |
|||
if ( !card->OUTPtranDebugGiven ) { |
|||
card->OUTPtranDebug = FALSE; |
|||
} |
|||
if ( !card->OUTPacDebugGiven ) { |
|||
card->OUTPacDebug = FALSE; |
|||
} |
|||
if ( !card->OUTPgeomGiven ) { |
|||
card->OUTPgeom = FALSE; |
|||
} |
|||
if ( !card->OUTPmeshGiven ) { |
|||
card->OUTPmesh = FALSE; |
|||
} |
|||
if ( !card->OUTPmaterialGiven ) { |
|||
card->OUTPmaterial = FALSE; |
|||
} |
|||
if ( !card->OUTPglobalsGiven ) { |
|||
card->OUTPglobals = FALSE; |
|||
} |
|||
if ( !card->OUTPstatsGiven ) { |
|||
card->OUTPstats = TRUE; |
|||
} |
|||
if ( !card->OUTProotFileGiven ) { |
|||
card->OUTProotFile = copy(""); |
|||
} |
|||
if ( !card->OUTPfileTypeGiven ) { |
|||
card->OUTPfileType = RAWFILE; |
|||
} |
|||
if ( !card->OUTPdopingGiven ) { |
|||
card->OUTPdoping = TRUE; |
|||
} |
|||
if ( !card->OUTPpsiGiven ) { |
|||
card->OUTPpsi = TRUE; |
|||
} |
|||
if ( !card->OUTPequPsiGiven ) { |
|||
card->OUTPequPsi = FALSE; |
|||
} |
|||
if ( !card->OUTPvacPsiGiven ) { |
|||
card->OUTPvacPsi = FALSE; |
|||
} |
|||
if ( !card->OUTPnConcGiven ) { |
|||
card->OUTPnConc = TRUE; |
|||
} |
|||
if ( !card->OUTPpConcGiven ) { |
|||
card->OUTPpConc = TRUE; |
|||
} |
|||
if ( !card->OUTPphinGiven ) { |
|||
card->OUTPphin = FALSE; |
|||
} |
|||
if ( !card->OUTPphipGiven ) { |
|||
card->OUTPphip = FALSE; |
|||
} |
|||
if ( !card->OUTPphicGiven ) { |
|||
card->OUTPphic = FALSE; |
|||
} |
|||
if ( !card->OUTPphivGiven ) { |
|||
card->OUTPphiv = FALSE; |
|||
} |
|||
if ( !card->OUTPeFieldGiven ) { |
|||
card->OUTPeField = TRUE; |
|||
} |
|||
if ( !card->OUTPjcGiven ) { |
|||
card->OUTPjc = FALSE; |
|||
} |
|||
if ( !card->OUTPjdGiven ) { |
|||
card->OUTPjd = TRUE; |
|||
} |
|||
if ( !card->OUTPjnGiven ) { |
|||
card->OUTPjn = TRUE; |
|||
} |
|||
if ( !card->OUTPjpGiven ) { |
|||
card->OUTPjp = TRUE; |
|||
} |
|||
if ( !card->OUTPjtGiven ) { |
|||
card->OUTPjt = FALSE; |
|||
} |
|||
if ( !card->OUTPuNetGiven ) { |
|||
card->OUTPuNet = FALSE; |
|||
} |
|||
if ( !card->OUTPmunGiven ) { |
|||
card->OUTPmun = FALSE; |
|||
} |
|||
if ( !card->OUTPmupGiven ) { |
|||
card->OUTPmup = FALSE; |
|||
} |
|||
} |
|||
return(OK); |
|||
} |
|||
|
|||
|
|||
/* |
|||
* Name: OUTPsetup |
|||
* Purpose: setup the output card |
|||
* Formals: cardList: list of cards to setup |
|||
* Returns: OK/E_PRIVATE |
|||
* Users: numerical devices |
|||
* Calls: OUTPcheck |
|||
*/ |
|||
int |
|||
OUTPsetup(OUTPcard *cardList) |
|||
{ |
|||
int error; |
|||
|
|||
/* Check the card list */ |
|||
if ((error = OUTPcheck( cardList ))) return( error ); |
|||
|
|||
return( OK ); |
|||
} |
|||
@ -0,0 +1,241 @@ |
|||
/********** |
|||
Copyright 1992 Regents of the University of California. All rights reserved. |
|||
Author: 1992 David A. Gates, U. C. Berkeley CAD Group |
|||
Modified: 2001 Paolo Nenzi |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numcards.h" |
|||
#include "numgen.h" |
|||
#include "numenum.h" |
|||
#include "outpdefs.h" |
|||
#include "devdefs.h" |
|||
#include "sperror.h" |
|||
#include "suffix.h" |
|||
|
|||
extern int OUTPnewCard(void**,void*); |
|||
extern int OUTPparam(int,IFvalue*,void*); |
|||
|
|||
|
|||
IFparm OUTPpTable[] = { |
|||
/* Debugging Flags */ |
|||
IP("all.debug",OUTP_ALL_DEBUG,IF_FLAG, "Debug All Analyses"), |
|||
IP("op.debug",OUTP_DC_DEBUG, IF_FLAG, "Debug DC/OP Analyses"), |
|||
IP("dc.debug",OUTP_DC_DEBUG, IF_FLAG, "Debug DC/OP Analyses"), |
|||
IP("tran.debug",OUTP_TRAN_DEBUG,IF_FLAG, "Debug TRAN Analysis"), |
|||
IP("ac.debug",OUTP_AC_DEBUG, IF_FLAG, "Debug AC/PZ Analyses"), |
|||
IP("pz.debug",OUTP_AC_DEBUG, IF_FLAG, "Debug AC/PZ Analyses"), |
|||
/* General Information */ |
|||
IP("geometry",OUTP_GEOM, IF_FLAG, "Geometric information"), |
|||
IP("mesh", OUTP_MESH, IF_FLAG, "Mesh information"), |
|||
IP("material",OUTP_MATERIAL, IF_FLAG, "Material information"), |
|||
IP("globals", OUTP_GLOBALS, IF_FLAG, "Global information"), |
|||
IP("statistics", OUTP_STATS, IF_FLAG, "Resource usage information"), |
|||
IP("resources", OUTP_STATS, IF_FLAG, "Resource usage information"), |
|||
/* Solution Information */ |
|||
IP("rootfile", OUTP_ROOTFILE, IF_STRING, "Root of output file names"), |
|||
IP("rawfile", OUTP_RAWFILE, IF_FLAG, "SPICE rawfile data format"), |
|||
IP("hdf", OUTP_HDF, IF_FLAG, "HDF data format"), |
|||
IP("doping", OUTP_DOPING, IF_FLAG, "Net doping"), |
|||
IP("psi", OUTP_PSI, IF_FLAG, "Potential"), |
|||
IP("equ.psi", OUTP_EQU_PSI, IF_FLAG, "Equilibrium potential"), |
|||
IP("vac.psi", OUTP_VAC_PSI, IF_FLAG, "Vacuum potential"), |
|||
IP("n.conc", OUTP_N_CONC, IF_FLAG, "Electron concentration"), |
|||
IP("electrons", OUTP_N_CONC, IF_FLAG, "Electron concentration"), |
|||
IP("p.conc", OUTP_P_CONC, IF_FLAG, "Hole concentration"), |
|||
IP("holes", OUTP_P_CONC, IF_FLAG, "Hole concentration"), |
|||
IP("phin", OUTP_PHIN, IF_FLAG, "Electron quasi-fermi potential"), |
|||
IP("qfn", OUTP_PHIN, IF_FLAG, "Electron quasi-fermi potential"), |
|||
IP("phip", OUTP_PHIP, IF_FLAG, "Hole quasi-fermi potential"), |
|||
IP("qfp", OUTP_PHIP, IF_FLAG, "Hole quasi-fermi potential"), |
|||
IP("phic", OUTP_PHIC, IF_FLAG, "Conduction band potential"), |
|||
IP("band.con",OUTP_PHIC, IF_FLAG, "Conduction band potential"), |
|||
IP("phiv", OUTP_PHIV, IF_FLAG, "Valence band potential"), |
|||
IP("band.val",OUTP_PHIV, IF_FLAG, "Valence band potential"), |
|||
IP("e.field", OUTP_E_FIELD, IF_FLAG, "Electric field"), |
|||
IP("jc", OUTP_J_C, IF_FLAG, "Conduction current density"), |
|||
IP("j.conduc",OUTP_J_C, IF_FLAG, "Conduction current density"), |
|||
IP("jd", OUTP_J_D, IF_FLAG, "Displacement current density"), |
|||
IP("j.disp", OUTP_J_D, IF_FLAG, "Displacement current density"), |
|||
IP("jn", OUTP_J_N, IF_FLAG, "Electron current density"), |
|||
IP("j.electr",OUTP_J_N, IF_FLAG, "Electron current density"), |
|||
IP("jp", OUTP_J_P, IF_FLAG, "Hole current density"), |
|||
IP("j.hole", OUTP_J_P, IF_FLAG, "Hole current density"), |
|||
IP("jt", OUTP_J_T, IF_FLAG, "Total current density"), |
|||
IP("j.total", OUTP_J_T, IF_FLAG, "Total current density"), |
|||
IP("unet", OUTP_U_NET, IF_FLAG, "Net recombination"), |
|||
IP("recomb", OUTP_U_NET, IF_FLAG, "Net recombination"), |
|||
IP("mun", OUTP_MUN, IF_FLAG, "Elctron mobility"), |
|||
IP("mob.elec",OUTP_MUN, IF_FLAG, "Electron mobility"), |
|||
IP("mup", OUTP_MUP, IF_FLAG, "Hole mobility"), |
|||
IP("mob.hole",OUTP_MUP, IF_FLAG, "Hole mobility") |
|||
}; |
|||
|
|||
IFcardInfo OUTPinfo = { |
|||
"output", |
|||
"Identify information to be output to user", |
|||
NUMELEMS(OUTPpTable), |
|||
OUTPpTable, |
|||
|
|||
OUTPnewCard, |
|||
OUTPparam, |
|||
NULL |
|||
}; |
|||
|
|||
int |
|||
OUTPnewCard(void **inCard, void *inModel) |
|||
{ |
|||
OUTPcard *tmpCard, *newCard; |
|||
GENnumModel *model = (GENnumModel *)inModel; |
|||
|
|||
tmpCard = model->GENoutputs; |
|||
if (!tmpCard) { /* First in list */ |
|||
newCard = NEW( OUTPcard ); |
|||
if (!newCard) { |
|||
*inCard = (void *)NULL; |
|||
return(E_NOMEM); |
|||
} |
|||
newCard->OUTPnextCard = (OUTPcard *)NULL; |
|||
*inCard = (void *)newCard; |
|||
model->GENoutputs = newCard; |
|||
} else { /* Only one card of this type allowed */ |
|||
*inCard = (void *)tmpCard; |
|||
} |
|||
return(OK); |
|||
} |
|||
|
|||
int |
|||
OUTPparam(int param, IFvalue *value, void *inCard) |
|||
{ |
|||
OUTPcard *card = (OUTPcard *)inCard; |
|||
|
|||
switch (param) { |
|||
case OUTP_ALL_DEBUG: |
|||
card->OUTPdcDebug = value->iValue; |
|||
card->OUTPdcDebugGiven = TRUE; |
|||
card->OUTPtranDebug = value->iValue; |
|||
card->OUTPtranDebugGiven = TRUE; |
|||
card->OUTPacDebug = value->iValue; |
|||
card->OUTPacDebugGiven = TRUE; |
|||
break; |
|||
case OUTP_DC_DEBUG: |
|||
card->OUTPdcDebug = value->iValue; |
|||
card->OUTPdcDebugGiven = TRUE; |
|||
break; |
|||
case OUTP_TRAN_DEBUG: |
|||
card->OUTPtranDebug = value->iValue; |
|||
card->OUTPtranDebugGiven = TRUE; |
|||
break; |
|||
case OUTP_AC_DEBUG: |
|||
card->OUTPacDebug = value->iValue; |
|||
card->OUTPacDebugGiven = TRUE; |
|||
break; |
|||
case OUTP_GEOM: |
|||
card->OUTPgeom = value->iValue; |
|||
card->OUTPgeomGiven = TRUE; |
|||
break; |
|||
case OUTP_MESH: |
|||
card->OUTPmesh = value->iValue; |
|||
card->OUTPmeshGiven = TRUE; |
|||
break; |
|||
case OUTP_MATERIAL: |
|||
card->OUTPmaterial = value->iValue; |
|||
card->OUTPmaterialGiven = TRUE; |
|||
break; |
|||
case OUTP_GLOBALS: |
|||
card->OUTPglobals = value->iValue; |
|||
card->OUTPglobalsGiven = TRUE; |
|||
break; |
|||
case OUTP_STATS: |
|||
card->OUTPstats = value->iValue; |
|||
card->OUTPstatsGiven = TRUE; |
|||
break; |
|||
case OUTP_ROOTFILE: |
|||
card->OUTProotFile = tilde_expand(value->sValue); |
|||
card->OUTProotFileGiven = TRUE; |
|||
break; |
|||
case OUTP_RAWFILE: |
|||
card->OUTPfileType = RAWFILE; |
|||
card->OUTPfileTypeGiven = TRUE; |
|||
break; |
|||
case OUTP_HDF: |
|||
return(E_UNSUPP); |
|||
break; |
|||
case OUTP_DOPING: |
|||
card->OUTPdoping = value->iValue; |
|||
card->OUTPdopingGiven = TRUE; |
|||
break; |
|||
case OUTP_PSI: |
|||
card->OUTPpsi = value->iValue; |
|||
card->OUTPpsiGiven = TRUE; |
|||
break; |
|||
case OUTP_EQU_PSI: |
|||
card->OUTPequPsi = value->iValue; |
|||
card->OUTPequPsiGiven = TRUE; |
|||
break; |
|||
case OUTP_VAC_PSI: |
|||
card->OUTPvacPsi = value->iValue; |
|||
card->OUTPvacPsiGiven = TRUE; |
|||
break; |
|||
case OUTP_N_CONC: |
|||
card->OUTPnConc = value->iValue; |
|||
card->OUTPnConcGiven = TRUE; |
|||
break; |
|||
case OUTP_P_CONC: |
|||
card->OUTPpConc = value->iValue; |
|||
card->OUTPpConcGiven = TRUE; |
|||
break; |
|||
case OUTP_PHIN: |
|||
card->OUTPphin = value->iValue; |
|||
card->OUTPphinGiven = TRUE; |
|||
break; |
|||
case OUTP_PHIP: |
|||
card->OUTPphip = value->iValue; |
|||
card->OUTPphipGiven = TRUE; |
|||
break; |
|||
case OUTP_PHIC: |
|||
card->OUTPphic = value->iValue; |
|||
card->OUTPphicGiven = TRUE; |
|||
break; |
|||
case OUTP_PHIV: |
|||
card->OUTPphiv = value->iValue; |
|||
card->OUTPphivGiven = TRUE; |
|||
break; |
|||
case OUTP_J_C: |
|||
card->OUTPjc = value->iValue; |
|||
card->OUTPjcGiven = TRUE; |
|||
break; |
|||
case OUTP_J_D: |
|||
card->OUTPjd = value->iValue; |
|||
card->OUTPjdGiven = TRUE; |
|||
break; |
|||
case OUTP_J_N: |
|||
card->OUTPjn = value->iValue; |
|||
card->OUTPjnGiven = TRUE; |
|||
break; |
|||
case OUTP_J_P: |
|||
card->OUTPjp = value->iValue; |
|||
card->OUTPjpGiven = TRUE; |
|||
break; |
|||
case OUTP_J_T: |
|||
card->OUTPjt = value->iValue; |
|||
card->OUTPjtGiven = TRUE; |
|||
break; |
|||
case OUTP_U_NET: |
|||
card->OUTPuNet = value->iValue; |
|||
card->OUTPuNetGiven = TRUE; |
|||
break; |
|||
case OUTP_MUN: |
|||
card->OUTPmun = value->iValue; |
|||
card->OUTPmunGiven = TRUE; |
|||
break; |
|||
case OUTP_MUP: |
|||
card->OUTPmup = value->iValue; |
|||
card->OUTPmupGiven = TRUE; |
|||
break; |
|||
default: |
|||
return(E_BADPARM); |
|||
break; |
|||
} |
|||
return(OK); |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
Directory: input |
|||
---------------- |
|||
The files in this directory serve two purposes: |
|||
1. Serve as collection point for numerical model parameters. |
|||
2. Translate data structures containing raw input into |
|||
a form usable by the one- and two-dimensional device simulators. |
|||
@ -0,0 +1,9 @@ |
|||
1. Crucial Difference between Spice3f1 and Spice3e2: |
|||
The 'states' field in the ***instance structured has been moved. |
|||
Belongs below the node id's in Spice3e2. |
|||
2. Crucial Differences between Spice3f2 and Spice3f1: |
|||
Addition of DEVunSetup call to ***itf.h files |
|||
The 3f2 input parser can't handle arithmetic operators in strings. |
|||
A special call has been added in spiceitf/inpgval.c to specially |
|||
parse strings. Quotes (either "foobar" or 'foobar') are stripped |
|||
from the string. |
|||
@ -0,0 +1,23 @@ |
|||
## Process this file with automake to produce Makefile.in
|
|||
|
|||
noinst_LIBRARIES = libcideroned.a |
|||
|
|||
libcideroned_a_SOURCES = \
|
|||
oneadmit.c \
|
|||
oneaval.c \
|
|||
onecond.c \
|
|||
onecont.c \
|
|||
onedest.c \
|
|||
onedopng.c \
|
|||
onefreez.c \
|
|||
onemesh.c \
|
|||
onepoiss.c \
|
|||
oneprint.c \
|
|||
oneproj.c \
|
|||
oneread.c \
|
|||
onesetup.c \
|
|||
onesolve.c |
|||
|
|||
EXTRA_DIST = notes readme |
|||
INCLUDES = -I$(top_srcdir)/src/include |
|||
MAINTAINERCLEANFILES = Makefile.in |
|||
@ -0,0 +1,36 @@ |
|||
Some comments on keeping track of where the solution is: |
|||
|
|||
There are 3 places that the internal state variables can be hiding: |
|||
1. In the (psi,n,p) triple of a node (pNode). |
|||
2. In the solution vectors (dcSolution, copiedSolution). |
|||
3. In the state tables (pDevice->devStates[]). |
|||
|
|||
To access node variables use: |
|||
pNode->psi |
|||
pNode->nConc |
|||
pNode->pConc |
|||
|
|||
To access solution vectors use: |
|||
solution[ pNode->psiEqn ] |
|||
solution[ pNode->nEqn ] |
|||
solution[ pNode->pEqn ] |
|||
|
|||
To access state vector X (0,1,etc.) use: |
|||
*(pDevice->devStateX + pNode->nodePsi) |
|||
*(pDevice->devStateX + pNode->nodeN) |
|||
*(pDevice->devStateX + pNode->nodeP) |
|||
|
|||
There are several functions that copy from one form of these |
|||
to another: |
|||
storeInitialGuess: dcSolution <- nodevars |
|||
biasSolve: nodevars <- dcSolution (DC) |
|||
state0 <- nodevars <- dcSolution (Transient) |
|||
saveState: nodevars <- state1 (Transient) |
|||
|
|||
In addition the main device-level Newton iteration copies during |
|||
the computation of currents and derivatives: |
|||
state0 <- dcSolution |
|||
|
|||
and the function predict() does a forward prediction step using |
|||
the previous state: |
|||
nodevars <- predict(state1) (Transient) |
|||
@ -0,0 +1,742 @@ |
|||
/********** |
|||
Copyright 1992 Regents of the University of California. All rights reserved. |
|||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
/* Functions to compute small-signal parameters of 1D devices */ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numglobs.h" |
|||
#include "numenum.h" |
|||
#include "numconst.h" |
|||
#include "onedev.h" |
|||
#include "onemesh.h" |
|||
#include "complex.h" |
|||
#include "spMatrix.h" |
|||
#include "ifsim.h" |
|||
|
|||
#include "onedext.h" |
|||
#include "oneddefs.h" |
|||
#include "cidersupt.h" |
|||
|
|||
|
|||
extern IFfrontEnd *SPfrontEnd; |
|||
|
|||
|
|||
/* |
|||
* mmhhh this may cause troubles |
|||
* Paolo Nenzi 2002 |
|||
*/ |
|||
SPcomplex yAc; |
|||
|
|||
|
|||
/* Forward Declarations */ |
|||
SPcomplex *computeAdmittance(ONEnode *, BOOLEAN, double *, |
|||
double *, SPcomplex *); |
|||
BOOLEAN ONEsorSolve(ONEdevice *pDevice, double *xReal, |
|||
double *xImag, double omega); |
|||
|
|||
int |
|||
NUMDadmittance(ONEdevice *pDevice, double omega, SPcomplex *yd) |
|||
{ |
|||
ONEnode *pNode; |
|||
ONEelem *pElem; |
|||
ONEedge *pEdge; |
|||
int index, i; |
|||
double yReal, yImag; |
|||
double *solutionReal, *solutionImag; |
|||
SPcomplex yAc, cOmega; |
|||
SPcomplex *y; |
|||
BOOLEAN SORFailed; |
|||
double startTime; |
|||
|
|||
|
|||
/* Each time we call this counts as one AC iteration. */ |
|||
pDevice->pStats->numIters[STAT_AC] += 1; |
|||
|
|||
/* |
|||
* Change context names of solution vectors for ac analysis. |
|||
* dcDeltaSolution stores the real part and copiedSolution stores the |
|||
* imaginary part of the ac solution vector. |
|||
*/ |
|||
solutionReal = pDevice->dcDeltaSolution; |
|||
solutionImag = pDevice->copiedSolution; |
|||
pDevice->solverType = SLV_SMSIG; |
|||
|
|||
/* use a normalized radian frequency */ |
|||
omega *= TNorm; |
|||
CMPLX_ASSIGN_VALUE(cOmega, 0.0, omega); |
|||
yReal = 0.0; |
|||
yImag = 0.0; |
|||
|
|||
if ((AcAnalysisMethod == SOR) || (AcAnalysisMethod == SOR_ONLY)) { |
|||
/* LOAD */ |
|||
startTime = SPfrontEnd->IFseconds(); |
|||
/* zero the rhs before loading in the new rhs */ |
|||
for (index = 1; index <= pDevice->numEqns; index++) { |
|||
pDevice->rhs[index] = 0.0; |
|||
pDevice->rhsImag[index] = 0.0; |
|||
} |
|||
/* store the new rhs vector */ |
|||
pElem = pDevice->elemArray[pDevice->numNodes - 1]; |
|||
pNode = pElem->pLeftNode; |
|||
pDevice->rhs[pNode->psiEqn] = pElem->epsRel * pElem->rDx; |
|||
if (pElem->elemType == SEMICON) { |
|||
pEdge = pElem->pEdge; |
|||
pDevice->rhs[pNode->nEqn] -= pEdge->dJnDpsiP1; |
|||
pDevice->rhs[pNode->pEqn] -= pEdge->dJpDpsiP1; |
|||
} |
|||
pDevice->pStats->loadTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime; |
|||
|
|||
/* SOLVE */ |
|||
startTime = SPfrontEnd->IFseconds(); |
|||
SORFailed = ONEsorSolve(pDevice, solutionReal, solutionImag, omega); |
|||
pDevice->pStats->solveTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime; |
|||
|
|||
if (SORFailed && AcAnalysisMethod == SOR) { |
|||
AcAnalysisMethod = DIRECT; |
|||
printf("SOR failed at %g Hz, switching to direct-method ac analysis.\n", |
|||
omega / (TWO_PI * TNorm) ); |
|||
} else if (SORFailed) { /* Told to only do SOR, so give up. */ |
|||
printf("SOR failed at %g Hz, returning null admittance.\n", |
|||
omega / (TWO_PI * TNorm) ); |
|||
CMPLX_ASSIGN_VALUE(*yd, 0.0, 0.0); |
|||
return (AcAnalysisMethod); |
|||
} |
|||
} |
|||
if (AcAnalysisMethod == DIRECT) { |
|||
/* LOAD */ |
|||
startTime = SPfrontEnd->IFseconds(); |
|||
/* solve the system of equations directly */ |
|||
for (index = 1; index <= pDevice->numEqns; index++) { |
|||
pDevice->rhs[index] = 0.0; |
|||
pDevice->rhsImag[index] = 0.0; |
|||
} |
|||
pElem = pDevice->elemArray[pDevice->numNodes - 1]; |
|||
pNode = pElem->pLeftNode; |
|||
pDevice->rhs[pNode->psiEqn] = pElem->epsRel * pElem->rDx; |
|||
if (pElem->elemType == SEMICON) { |
|||
pEdge = pElem->pEdge; |
|||
pDevice->rhs[pNode->nEqn] -= pEdge->dJnDpsiP1; |
|||
pDevice->rhs[pNode->pEqn] -= pEdge->dJpDpsiP1; |
|||
} |
|||
ONE_jacLoad(pDevice); |
|||
spSetComplex(pDevice->matrix); |
|||
for (index = 1; index < pDevice->numNodes; index++) { |
|||
pElem = pDevice->elemArray[index]; |
|||
if (pElem->elemType == SEMICON) { |
|||
for (i = 0; i <= 1; i++) { |
|||
pNode = pElem->pNodes[i]; |
|||
if (pNode->nodeType != CONTACT) { |
|||
spADD_COMPLEX_ELEMENT(pNode->fNN, 0.0, -0.5 * pElem->dx * omega); |
|||
spADD_COMPLEX_ELEMENT(pNode->fPP, 0.0, 0.5 * pElem->dx * omega); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
pDevice->pStats->loadTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime; |
|||
|
|||
/* FACTOR */ |
|||
startTime = SPfrontEnd->IFseconds(); |
|||
spFactor(pDevice->matrix); |
|||
pDevice->pStats->factorTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime; |
|||
|
|||
/* SOLVE */ |
|||
startTime = SPfrontEnd->IFseconds(); |
|||
spSolve(pDevice->matrix, pDevice->rhs, solutionReal, |
|||
pDevice->rhsImag, solutionImag); |
|||
pDevice->pStats->solveTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime; |
|||
} |
|||
/* MISC */ |
|||
startTime = SPfrontEnd->IFseconds(); |
|||
pNode = pDevice->elemArray[1]->pLeftNode; |
|||
y = computeAdmittance(pNode, FALSE, solutionReal, solutionImag, &cOmega); |
|||
CMPLX_ASSIGN_VALUE(yAc, -y->real, -y->imag); |
|||
CMPLX_ASSIGN(*yd, yAc); |
|||
CMPLX_MULT_SELF_SCALAR(*yd, GNorm * pDevice->area); |
|||
pDevice->pStats->miscTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime; |
|||
|
|||
return (AcAnalysisMethod); |
|||
} |
|||
|
|||
|
|||
int |
|||
NBJTadmittance(ONEdevice *pDevice, double omega, SPcomplex *yIeVce, |
|||
SPcomplex *yIcVce, SPcomplex *yIeVbe, SPcomplex *yIcVbe) |
|||
{ |
|||
ONEelem *pCollElem = pDevice->elemArray[pDevice->numNodes - 1]; |
|||
ONEelem *pBaseElem = pDevice->elemArray[pDevice->baseIndex - 1]; |
|||
ONEelem *pElem; |
|||
ONEedge *pEdge; |
|||
ONEnode *pNode; |
|||
int index, i; |
|||
double area = pDevice->area; |
|||
double *solutionReal, *solutionImag; |
|||
BOOLEAN SORFailed; |
|||
SPcomplex *y; |
|||
SPcomplex cOmega, pIeVce, pIcVce, pIeVbe, pIcVbe; |
|||
double startTime; |
|||
|
|||
/* Each time we call this counts as one AC iteration. */ |
|||
pDevice->pStats->numIters[STAT_AC] += 1; |
|||
|
|||
/* |
|||
* change context names of solution vectors for ac analysis dcDeltaSolution |
|||
* stores the real part and copiedSolution stores the imaginary part of the |
|||
* ac solution vector |
|||
*/ |
|||
solutionReal = pDevice->dcDeltaSolution; |
|||
solutionImag = pDevice->copiedSolution; |
|||
pDevice->solverType = SLV_SMSIG; |
|||
|
|||
/* use a normalized radian frequency */ |
|||
omega *= TNorm; |
|||
CMPLX_ASSIGN_VALUE(cOmega, 0.0, omega); |
|||
|
|||
if ((AcAnalysisMethod == SOR) || (AcAnalysisMethod == SOR_ONLY)) { |
|||
/* LOAD */ |
|||
startTime = SPfrontEnd->IFseconds(); |
|||
/* zero the rhs before loading in the new rhs */ |
|||
for (index = 1; index <= pDevice->numEqns; index++) { |
|||
pDevice->rhs[index] = 0.0; |
|||
pDevice->rhsImag[index] = 0.0; |
|||
} |
|||
/* store the new rhs vector */ |
|||
pNode = pCollElem->pLeftNode; |
|||
pDevice->rhs[pNode->psiEqn] = pCollElem->epsRel * pCollElem->rDx; |
|||
if (pCollElem->elemType == SEMICON) { |
|||
pEdge = pCollElem->pEdge; |
|||
pDevice->rhs[pNode->nEqn] -= pEdge->dJnDpsiP1; |
|||
pDevice->rhs[pNode->pEqn] -= pEdge->dJpDpsiP1; |
|||
} |
|||
pDevice->pStats->loadTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime; |
|||
|
|||
/* SOLVE */ |
|||
startTime = SPfrontEnd->IFseconds(); |
|||
SORFailed = ONEsorSolve(pDevice, solutionReal, solutionImag, omega); |
|||
pDevice->pStats->solveTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime; |
|||
if (SORFailed && (AcAnalysisMethod == SOR)) { |
|||
AcAnalysisMethod = DIRECT; |
|||
printf("SOR failed at %g Hz, switching to direct-method ac analysis.\n", |
|||
omega / (TWO_PI * TNorm) ); |
|||
} else if (SORFailed) { /* Told to only do SOR, so give up. */ |
|||
printf("SOR failed at %g Hz, returning null admittance.\n", |
|||
omega / (TWO_PI * TNorm) ); |
|||
CMPLX_ASSIGN_VALUE(*yIeVce, 0.0, 0.0); |
|||
CMPLX_ASSIGN_VALUE(*yIcVce, 0.0, 0.0); |
|||
CMPLX_ASSIGN_VALUE(*yIeVbe, 0.0, 0.0); |
|||
CMPLX_ASSIGN_VALUE(*yIcVbe, 0.0, 0.0); |
|||
return (AcAnalysisMethod); |
|||
} else { |
|||
/* MISC */ |
|||
startTime = SPfrontEnd->IFseconds(); |
|||
pElem = pDevice->elemArray[1]; |
|||
pNode = pElem->pLeftNode; |
|||
y = computeAdmittance(pNode, FALSE, solutionReal, solutionImag, &cOmega); |
|||
CMPLX_ASSIGN_VALUE(pIeVce, -y->real, -y->imag); |
|||
pNode = pCollElem->pRightNode; |
|||
y = computeAdmittance(pNode, TRUE, solutionReal, solutionImag, &cOmega); |
|||
CMPLX_ASSIGN_VALUE(pIcVce, -y->real, -y->imag); |
|||
pDevice->pStats->miscTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime; |
|||
|
|||
/* LOAD */ |
|||
startTime = SPfrontEnd->IFseconds(); |
|||
/* load in the base contribution to the rhs */ |
|||
for (index = 1; index <= pDevice->numEqns; index++) { |
|||
pDevice->rhs[index] = 0.0; |
|||
} |
|||
pNode = pBaseElem->pRightNode; |
|||
if (pNode->baseType == N_TYPE) { |
|||
pDevice->rhs[pNode->nEqn] = pNode->nConc * pNode->eg; |
|||
} else if (pNode->baseType == P_TYPE) { |
|||
pDevice->rhs[pNode->pEqn] = pNode->pConc * pNode->eg; |
|||
} else { |
|||
printf("projectBJTsolution: unknown base type\n"); |
|||
} |
|||
pDevice->pStats->loadTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime; |
|||
|
|||
/* SOLVE */ |
|||
startTime = SPfrontEnd->IFseconds(); |
|||
SORFailed = ONEsorSolve(pDevice, solutionReal, solutionImag, omega); |
|||
pDevice->pStats->solveTime[STAT_AC] += |
|||
SPfrontEnd->IFseconds() - startTime; |
|||
if (SORFailed && (AcAnalysisMethod == SOR)) { |
|||
AcAnalysisMethod = DIRECT; |
|||
printf("SOR failed at %g Hz, switching to direct-method ac analysis.\n", |
|||
omega / (TWO_PI * TNorm) ); |
|||
} else if (SORFailed) { /* Told to only do SOR, so give up. */ |
|||
printf("SOR failed at %g Hz, returning null admittance.\n", |
|||
omega / (TWO_PI * TNorm) ); |
|||
CMPLX_ASSIGN_VALUE(*yIeVce, 0.0, 0.0); |
|||
CMPLX_ASSIGN_VALUE(*yIcVce, 0.0, 0.0); |
|||
CMPLX_ASSIGN_VALUE(*yIeVbe, 0.0, 0.0); |
|||
CMPLX_ASSIGN_VALUE(*yIcVbe, 0.0, 0.0); |
|||
return (AcAnalysisMethod); |
|||
} |
|||
} |
|||
} |
|||
if (AcAnalysisMethod == DIRECT) { |
|||
/* LOAD */ |
|||
startTime = SPfrontEnd->IFseconds(); |
|||
for (index = 1; index <= pDevice->numEqns; index++) { |
|||
pDevice->rhs[index] = 0.0; |
|||
pDevice->rhsImag[index] = 0.0; |
|||
} |
|||
/* solve the system of equations directly */ |
|||
ONE_jacLoad(pDevice); |
|||
pNode = pCollElem->pLeftNode; |
|||
pDevice->rhs[pNode->psiEqn] = pCollElem->epsRel * pCollElem->rDx; |
|||
if (pCollElem->elemType == SEMICON) { |
|||
pEdge = pCollElem->pEdge; |
|||
pDevice->rhs[pNode->nEqn] -= pEdge->dJnDpsiP1; |
|||
pDevice->rhs[pNode->pEqn] -= pEdge->dJpDpsiP1; |
|||
} |
|||
spSetComplex(pDevice->matrix); |
|||
for (index = 1; index < pDevice->numNodes; index++) { |
|||
pElem = pDevice->elemArray[index]; |
|||
if (pElem->elemType == SEMICON) { |
|||
for (i = 0; i <= 1; i++) { |
|||
pNode = pElem->pNodes[i]; |
|||
if (pNode->nodeType != CONTACT) { |
|||
spADD_COMPLEX_ELEMENT(pNode->fNN, 0.0, -0.5 * pElem->dx * omega); |
|||
spADD_COMPLEX_ELEMENT(pNode->fPP, 0.0, 0.5 * pElem->dx * omega); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
pDevice->pStats->loadTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime; |
|||
|
|||
/* FACTOR */ |
|||
startTime = SPfrontEnd->IFseconds(); |
|||
spFactor(pDevice->matrix); |
|||
pDevice->pStats->factorTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime; |
|||
|
|||
/* SOLVE */ |
|||
startTime = SPfrontEnd->IFseconds(); |
|||
spSolve(pDevice->matrix, pDevice->rhs, solutionReal, |
|||
pDevice->rhsImag, solutionImag); |
|||
pDevice->pStats->solveTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime; |
|||
|
|||
/* MISC */ |
|||
startTime = SPfrontEnd->IFseconds(); |
|||
pElem = pDevice->elemArray[1]; |
|||
pNode = pElem->pLeftNode; |
|||
y = computeAdmittance(pNode, FALSE, solutionReal, solutionImag, &cOmega); |
|||
CMPLX_ASSIGN_VALUE(pIeVce, -y->real, -y->imag); |
|||
pNode = pCollElem->pRightNode; |
|||
y = computeAdmittance(pNode, TRUE, solutionReal, solutionImag, &cOmega); |
|||
CMPLX_ASSIGN_VALUE(pIcVce, -y->real, -y->imag); |
|||
pDevice->pStats->miscTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime; |
|||
|
|||
/* LOAD */ |
|||
startTime = SPfrontEnd->IFseconds(); |
|||
/* load in the base contribution in the rhs */ |
|||
for (index = 1; index <= pDevice->numEqns; index++) { |
|||
pDevice->rhs[index] = 0.0; |
|||
} |
|||
pNode = pBaseElem->pRightNode; |
|||
if (pNode->baseType == N_TYPE) { |
|||
pDevice->rhs[pNode->nEqn] = pNode->nConc * pNode->eg; |
|||
} else if (pNode->baseType == P_TYPE) { |
|||
pDevice->rhs[pNode->pEqn] = pNode->pConc * pNode->eg; |
|||
} else { |
|||
printf("\n BJTadmittance: unknown base type"); |
|||
} |
|||
pDevice->pStats->loadTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime; |
|||
|
|||
/* FACTOR: already done, no need to repeat. */ |
|||
|
|||
/* SOLVE */ |
|||
startTime = SPfrontEnd->IFseconds(); |
|||
spSolve(pDevice->matrix, pDevice->rhs, solutionReal, |
|||
pDevice->rhsImag, solutionImag); |
|||
pDevice->pStats->solveTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime; |
|||
} |
|||
/* MISC */ |
|||
startTime = SPfrontEnd->IFseconds(); |
|||
pElem = pDevice->elemArray[1]; |
|||
pNode = pElem->pLeftNode; |
|||
y = computeAdmittance(pNode, FALSE, solutionReal, solutionImag, &cOmega); |
|||
CMPLX_ASSIGN_VALUE(pIeVbe, -y->real, -y->imag); |
|||
pNode = pCollElem->pRightNode; |
|||
y = computeAdmittance(pNode, FALSE, solutionReal, solutionImag, &cOmega); |
|||
CMPLX_ASSIGN_VALUE(pIcVbe, -y->real, -y->imag); |
|||
|
|||
CMPLX_ASSIGN(*yIeVce, pIeVce); |
|||
CMPLX_ASSIGN(*yIcVce, pIcVce); |
|||
CMPLX_ASSIGN(*yIeVbe, pIeVbe); |
|||
CMPLX_ASSIGN(*yIcVbe, pIcVbe); |
|||
CMPLX_MULT_SELF_SCALAR(*yIeVce, GNorm * area); |
|||
CMPLX_MULT_SELF_SCALAR(*yIeVbe, GNorm * area); |
|||
CMPLX_MULT_SELF_SCALAR(*yIcVce, GNorm * area); |
|||
CMPLX_MULT_SELF_SCALAR(*yIcVbe, GNorm * area); |
|||
pDevice->pStats->miscTime[STAT_AC] += SPfrontEnd->IFseconds() - startTime; |
|||
|
|||
return (AcAnalysisMethod); |
|||
} |
|||
|
|||
BOOLEAN |
|||
ONEsorSolve(ONEdevice *pDevice, double *xReal, double *xImag, double omega) |
|||
{ |
|||
ONEnode *pNode; |
|||
ONEelem *pElem; |
|||
double wRelax = 1.0; /* SOR relaxation parameter */ |
|||
double *rhsSOR = pDevice->rhsImag; |
|||
int numEqns = pDevice->numEqns; |
|||
int numNodes = pDevice->numNodes; |
|||
BOOLEAN SORConverged = FALSE; |
|||
BOOLEAN SORFailed = FALSE; |
|||
int i, index, indexN, indexP, iterationNum; |
|||
double dx; |
|||
|
|||
|
|||
/* clear xReal, xImag arrays */ |
|||
for (index = 1; index <= numEqns; index++) { |
|||
xReal[index] = 0.0; |
|||
xImag[index] = 0.0; |
|||
} |
|||
|
|||
iterationNum = 1; |
|||
for (; (!SORConverged) &&(!SORFailed); iterationNum++) { |
|||
/* first setup the rhs for the real part */ |
|||
for (index = 1; index <= numEqns; index++) { |
|||
rhsSOR[index] = 0.0; |
|||
} |
|||
for (index = 1; index < numNodes; index++) { |
|||
pElem = pDevice->elemArray[index]; |
|||
dx = 0.5 * pElem->dx; |
|||
for (i = 0; i <= 1; i++) { |
|||
pNode = pElem->pNodes[i]; |
|||
if ((pNode->nodeType != CONTACT) |
|||
&& (pElem->elemType == SEMICON)) { |
|||
indexN = pNode->nEqn; |
|||
indexP = pNode->pEqn; |
|||
rhsSOR[indexN] -= dx * omega * xImag[indexN]; |
|||
rhsSOR[indexP] += dx * omega * xImag[indexP]; |
|||
} |
|||
} |
|||
} |
|||
/* now setup the rhs for the SOR equations */ |
|||
for (index = 1; index <= numEqns; index++) { |
|||
rhsSOR[index] += pDevice->rhs[index]; |
|||
} |
|||
/* compute xReal(k+1). solution stored in rhsSOR */ |
|||
spSolve(pDevice->matrix, rhsSOR, rhsSOR, NIL(spREAL), NIL(spREAL)); |
|||
|
|||
/* modify solution when wRelax is not 1 */ |
|||
if (wRelax != 1) { |
|||
for (index = 1; index <= numEqns; index++) { |
|||
rhsSOR[index] = (1 - wRelax) * xReal[index] + |
|||
wRelax * rhsSOR[index]; |
|||
} |
|||
} |
|||
if (iterationNum > 1) { |
|||
SORConverged = hasSORConverged(xReal, rhsSOR, numEqns); |
|||
} |
|||
/* copy real solution into xReal */ |
|||
for (index = 1; index <= numEqns; index++) { |
|||
xReal[index] = rhsSOR[index]; |
|||
} |
|||
|
|||
/* now compute the imaginary part of the solution, xImag */ |
|||
for (index = 1; index <= numEqns; index++) { |
|||
rhsSOR[index] = 0.0; |
|||
} |
|||
for (index = 1; index < numNodes; index++) { |
|||
pElem = pDevice->elemArray[index]; |
|||
dx = 0.5 * pElem->dx; |
|||
for (i = 0; i <= 1; i++) { |
|||
pNode = pElem->pNodes[i]; |
|||
if ((pNode->nodeType != CONTACT) |
|||
&& (pElem->elemType == SEMICON)) { |
|||
indexN = pNode->nEqn; |
|||
indexP = pNode->pEqn; |
|||
rhsSOR[indexN] += dx * omega * xReal[indexN]; |
|||
rhsSOR[indexP] -= dx * omega * xReal[indexP]; |
|||
} |
|||
} |
|||
} |
|||
/* compute xImag(k+1) */ |
|||
spSolve(pDevice->matrix, rhsSOR, rhsSOR, |
|||
NIL(spREAL), NIL(spREAL)); |
|||
/* modify solution when wRelax is not 1 */ |
|||
if (wRelax != 1) { |
|||
for (index = 1; index <= numEqns; index++) { |
|||
rhsSOR[index] = (1 - wRelax) * xImag[index] + |
|||
wRelax * rhsSOR[index]; |
|||
} |
|||
} |
|||
if (iterationNum > 1) { |
|||
SORConverged = SORConverged && hasSORConverged(xImag, rhsSOR, numEqns); |
|||
} |
|||
/* copy imag solution into xImag */ |
|||
for (index = 1; index <= numEqns; index++) { |
|||
xImag[index] = rhsSOR[index]; |
|||
} |
|||
if (ONEacDebug) |
|||
printf("SOR iteration number = %d\n", iterationNum); |
|||
if (iterationNum > 4) { |
|||
SORFailed = TRUE; |
|||
} |
|||
} |
|||
return (SORFailed); |
|||
} |
|||
|
|||
void |
|||
NUMDys(ONEdevice *pDevice, SPcomplex *s, SPcomplex *yd) |
|||
{ |
|||
ONEnode *pNode; |
|||
ONEelem *pElem; |
|||
ONEedge *pEdge; |
|||
int index, i; |
|||
double *solutionReal, *solutionImag; |
|||
SPcomplex temp, cOmega; |
|||
SPcomplex *y; |
|||
|
|||
|
|||
/* |
|||
* change context names of solution vectors for ac analysis dcDeltaSolution |
|||
* stores the real part and copiedSolution stores the imaginary part of the |
|||
* ac solution vector |
|||
*/ |
|||
solutionReal = pDevice->dcDeltaSolution; |
|||
solutionImag = pDevice->copiedSolution; |
|||
|
|||
/* use a normalized radian frequency */ |
|||
CMPLX_MULT_SCALAR(cOmega, *s, TNorm); |
|||
|
|||
/* solve the system of equations directly */ |
|||
for (index = 1; index <= pDevice->numEqns; index++) { |
|||
pDevice->rhs[index] = 0.0; |
|||
pDevice->rhsImag[index] = 0.0; |
|||
} |
|||
ONE_jacLoad(pDevice); |
|||
pElem = pDevice->elemArray[pDevice->numNodes - 1]; |
|||
pNode = pElem->pLeftNode; |
|||
pDevice->rhs[pNode->psiEqn] = pElem->epsRel * pElem->rDx; |
|||
if (pElem->elemType == SEMICON) { |
|||
pEdge = pElem->pEdge; |
|||
pDevice->rhs[pNode->nEqn] -= pEdge->dJnDpsiP1; |
|||
pDevice->rhs[pNode->pEqn] -= pEdge->dJpDpsiP1; |
|||
} |
|||
spSetComplex(pDevice->matrix); |
|||
for (index = 1; index < pDevice->numNodes; index++) { |
|||
pElem = pDevice->elemArray[index]; |
|||
if (pElem->elemType == SEMICON) { |
|||
for (i = 0; i <= 1; i++) { |
|||
pNode = pElem->pNodes[i]; |
|||
if (pNode->nodeType != CONTACT) { |
|||
CMPLX_MULT_SCALAR(temp, cOmega, 0.5 * pElem->dx); |
|||
spADD_COMPLEX_ELEMENT(pNode->fNN, -temp.real, -temp.imag); |
|||
spADD_COMPLEX_ELEMENT(pNode->fPP, temp.real, temp.imag); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
spFactor(pDevice->matrix); |
|||
spSolve(pDevice->matrix, pDevice->rhs, solutionReal, |
|||
pDevice->rhsImag, solutionImag); |
|||
|
|||
pElem = pDevice->elemArray[1]; |
|||
pNode = pElem->pLeftNode; |
|||
y = computeAdmittance(pNode, FALSE, solutionReal, solutionImag, &cOmega); |
|||
CMPLX_ASSIGN_VALUE(*yd, -y->real, -y->imag); |
|||
CMPLX_MULT_SELF_SCALAR(*yd, GNorm * pDevice->area); |
|||
} |
|||
|
|||
|
|||
void |
|||
NBJTys(ONEdevice *pDevice, SPcomplex *s, SPcomplex *yIeVce, |
|||
SPcomplex *yIcVce, SPcomplex *yIeVbe, SPcomplex *yIcVbe) |
|||
{ |
|||
ONEelem *pCollElem = pDevice->elemArray[pDevice->numNodes - 1]; |
|||
ONEelem *pBaseElem = pDevice->elemArray[pDevice->baseIndex - 1]; |
|||
ONEelem *pElem; |
|||
ONEnode *pNode; |
|||
ONEedge *pEdge; |
|||
int index, i; |
|||
SPcomplex *y; |
|||
double area = pDevice->area; |
|||
double *solutionReal, *solutionImag; |
|||
SPcomplex temp, cOmega; |
|||
SPcomplex pIeVce, pIcVce, pIeVbe, pIcVbe; |
|||
|
|||
/* |
|||
* change context names of solution vectors for ac analysis dcDeltaSolution |
|||
* stores the real part and copiedSolution stores the imaginary part of the |
|||
* ac solution vector |
|||
*/ |
|||
|
|||
solutionReal = pDevice->dcDeltaSolution; |
|||
solutionImag = pDevice->copiedSolution; |
|||
|
|||
/* use a normalized radian frequency */ |
|||
CMPLX_MULT_SCALAR(cOmega, *s, TNorm); |
|||
for (index = 1; index <= pDevice->numEqns; index++) { |
|||
pDevice->rhs[index] = 0.0; |
|||
pDevice->rhsImag[index] = 0.0; |
|||
} |
|||
/* solve the system of equations directly */ |
|||
ONE_jacLoad(pDevice); |
|||
pNode = pCollElem->pLeftNode; |
|||
pDevice->rhs[pNode->psiEqn] = pCollElem->epsRel * pCollElem->rDx; |
|||
if (pCollElem->elemType == SEMICON) { |
|||
pEdge = pCollElem->pEdge; |
|||
pDevice->rhs[pNode->nEqn] -= pEdge->dJnDpsiP1; |
|||
pDevice->rhs[pNode->pEqn] -= pEdge->dJpDpsiP1; |
|||
} |
|||
spSetComplex(pDevice->matrix); |
|||
for (index = 1; index < pDevice->numNodes; index++) { |
|||
pElem = pDevice->elemArray[index]; |
|||
if (pElem->elemType == SEMICON) { |
|||
for (i = 0; i <= 1; i++) { |
|||
pNode = pElem->pNodes[i]; |
|||
if (pNode->nodeType != CONTACT) { |
|||
CMPLX_MULT_SCALAR(temp, cOmega, 0.5 * pElem->dx); |
|||
spADD_COMPLEX_ELEMENT(pNode->fNN, -temp.real, -temp.imag); |
|||
spADD_COMPLEX_ELEMENT(pNode->fPP, temp.real, temp.imag); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
spFactor(pDevice->matrix); |
|||
spSolve(pDevice->matrix, pDevice->rhs, solutionReal, |
|||
pDevice->rhsImag, solutionImag); |
|||
pElem = pDevice->elemArray[1]; |
|||
pNode = pElem->pLeftNode; |
|||
y = computeAdmittance(pNode, FALSE, solutionReal, solutionImag, &cOmega); |
|||
CMPLX_ASSIGN_VALUE(pIeVce, -y->real, -y->imag); |
|||
pNode = pCollElem->pRightNode; |
|||
y = computeAdmittance(pNode, TRUE, solutionReal, solutionImag, &cOmega); |
|||
CMPLX_ASSIGN_VALUE(pIcVce, -y->real, -y->imag); |
|||
|
|||
/* load in the base contribution in the rhs */ |
|||
for (index = 1; index <= pDevice->numEqns; index++) { |
|||
pDevice->rhs[index] = 0.0; |
|||
} |
|||
pNode = pBaseElem->pRightNode; |
|||
if (pNode->baseType == N_TYPE) { |
|||
pDevice->rhs[pNode->nEqn] = pNode->nConc * pNode->eg; |
|||
} else if (pNode->baseType == P_TYPE) { |
|||
pDevice->rhs[pNode->pEqn] = pNode->pConc * pNode->eg; |
|||
} else { |
|||
printf("\n BJTadmittance: unknown base type"); |
|||
} |
|||
|
|||
/* don't need to LU factor the jacobian since it exists */ |
|||
spSolve(pDevice->matrix, pDevice->rhs, solutionReal, |
|||
pDevice->rhsImag, solutionImag); |
|||
pElem = pDevice->elemArray[1]; |
|||
pNode = pElem->pLeftNode; |
|||
y = computeAdmittance(pNode, FALSE, solutionReal, solutionImag, &cOmega); |
|||
CMPLX_ASSIGN_VALUE(pIeVbe, -y->real, -y->imag); |
|||
pNode = pCollElem->pRightNode; |
|||
y = computeAdmittance(pNode, FALSE, solutionReal, solutionImag, &cOmega); |
|||
CMPLX_ASSIGN_VALUE(pIcVbe, -y->real, -y->imag); |
|||
|
|||
|
|||
CMPLX_ASSIGN(*yIeVce, pIeVce); |
|||
CMPLX_ASSIGN(*yIcVce, pIcVce); |
|||
CMPLX_ASSIGN(*yIeVbe, pIeVbe); |
|||
CMPLX_ASSIGN(*yIcVbe, pIcVbe); |
|||
CMPLX_MULT_SELF_SCALAR(*yIeVce, GNorm * area); |
|||
CMPLX_MULT_SELF_SCALAR(*yIeVbe, GNorm * area); |
|||
CMPLX_MULT_SELF_SCALAR(*yIcVce, GNorm * area); |
|||
CMPLX_MULT_SELF_SCALAR(*yIcVbe, GNorm * area); |
|||
} |
|||
|
|||
/* function to compute the admittance of a one-D device */ |
|||
/* cOmega is the complex frequency */ |
|||
|
|||
SPcomplex * |
|||
computeAdmittance(ONEnode *pNode, BOOLEAN delVContact, double *xReal, |
|||
double *xImag, SPcomplex *cOmega) |
|||
{ |
|||
ONEnode *pHNode; |
|||
ONEedge *pEdge; |
|||
ONEelem *pElem; |
|||
SPcomplex psi, n, p; |
|||
SPcomplex sum, prod1, prod2; |
|||
/* SPcomplex yAc; */ |
|||
double temp; |
|||
int i; |
|||
|
|||
|
|||
CMPLX_ASSIGN_VALUE(yAc, 0.0, 0.0); |
|||
|
|||
for (i = 0; i <= 1; i++) { |
|||
pElem = pNode->pElems[i]; |
|||
if (pElem != NIL(ONEelem)) { |
|||
switch (i) { |
|||
case 0: |
|||
/* the right node of the element */ |
|||
pHNode = pElem->pLeftNode; |
|||
pEdge = pElem->pEdge; |
|||
CMPLX_ASSIGN_VALUE(psi, xReal[pHNode->psiEqn], |
|||
xImag[pHNode->psiEqn]); |
|||
if (pElem->elemType == SEMICON) { |
|||
CMPLX_ASSIGN_VALUE(n, xReal[pHNode->nEqn], |
|||
xImag[pHNode->nEqn]); |
|||
CMPLX_ASSIGN_VALUE(p, xReal[pHNode->pEqn], |
|||
xImag[pHNode->pEqn]); |
|||
|
|||
CMPLX_MULT_SCALAR(prod1, psi, -pEdge->dJnDpsiP1); |
|||
CMPLX_MULT_SCALAR(prod2, n, pEdge->dJnDn); |
|||
CMPLX_ADD(yAc, prod1, prod2); |
|||
CMPLX_MULT_SCALAR(prod1, psi, -pEdge->dJpDpsiP1); |
|||
CMPLX_MULT_SCALAR(prod2, p, pEdge->dJpDp); |
|||
CMPLX_ADD(sum, prod1, prod2); |
|||
CMPLX_ADD_ASSIGN(yAc, sum); |
|||
if (delVContact) { |
|||
CMPLX_ADD_SELF_SCALAR(yAc, pEdge->dJnDpsiP1 + pEdge->dJpDpsiP1); |
|||
} |
|||
} |
|||
CMPLX_MULT_SCALAR(prod1, *cOmega, pElem->epsRel * pElem->rDx); |
|||
CMPLX_MULT(prod2, prod1, psi) |
|||
CMPLX_ADD_ASSIGN(yAc, prod2); |
|||
if (delVContact) { |
|||
CMPLX_SUBT_ASSIGN(yAc, prod1); |
|||
} |
|||
break; |
|||
|
|||
case 1: |
|||
/* the left node of the element */ |
|||
pHNode = pElem->pRightNode; |
|||
pEdge = pElem->pEdge; |
|||
CMPLX_ASSIGN_VALUE(psi, xReal[pHNode->psiEqn], |
|||
xImag[pHNode->psiEqn]); |
|||
if (pElem->elemType == SEMICON) { |
|||
CMPLX_ASSIGN_VALUE(n, xReal[pHNode->nEqn], |
|||
xImag[pHNode->nEqn]); |
|||
CMPLX_ASSIGN_VALUE(p, xReal[pHNode->pEqn], |
|||
xImag[pHNode->pEqn]); |
|||
CMPLX_MULT_SCALAR(prod1, psi, pEdge->dJnDpsiP1); |
|||
CMPLX_MULT_SCALAR(prod2, n, pEdge->dJnDnP1); |
|||
CMPLX_ADD(yAc, prod1, prod2); |
|||
CMPLX_MULT_SCALAR(prod1, psi, pEdge->dJpDpsiP1); |
|||
CMPLX_MULT_SCALAR(prod2, p, pEdge->dJpDpP1); |
|||
CMPLX_ADD(sum, prod1, prod2); |
|||
CMPLX_ADD_ASSIGN(yAc, sum); |
|||
|
|||
if (delVContact) { |
|||
CMPLX_ADD_SELF_SCALAR(yAc, -(pEdge->dJnDpsiP1 + pEdge->dJpDpsiP1)); |
|||
} |
|||
} |
|||
CMPLX_MULT_SCALAR(prod1, *cOmega, pElem->epsRel * pElem->rDx); |
|||
CMPLX_MULT(prod2, prod1, psi); |
|||
CMPLX_SUBT_ASSIGN(yAc, prod2); |
|||
if (delVContact) { |
|||
CMPLX_ADD_ASSIGN(yAc, prod1); |
|||
} |
|||
break; |
|||
default: |
|||
/* should never be here. Error */ |
|||
printf("computeAdmittance: Error - unknown element\n"); |
|||
} |
|||
} |
|||
} |
|||
return (&yAc); |
|||
} |
|||
@ -0,0 +1,172 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numglobs.h" |
|||
#include "onemesh.h" |
|||
#include "onedev.h" |
|||
#include "macros.h" |
|||
|
|||
#include "onedext.h" |
|||
#include "oneddefs.h" |
|||
|
|||
double |
|||
ONEavalanche(BOOLEAN rhsOnly, ONEdevice *pDevice, ONEnode *pNode) |
|||
{ |
|||
ONEelem *pLElem, *pRElem; |
|||
ONEedge *pLEdge, *pREdge; |
|||
int numNodes = pDevice->numNodes; |
|||
double dJnDpsiPrev, dJpDpsiPrev; |
|||
double eField, temp, jn, jp; |
|||
double signE, signN, signP, coeffR, coeffL, alphaN, alphaP; |
|||
double generation = 0.0; |
|||
double dAlphaNDpsiM1, dAlphaNDpsi, dAlphaNDpsiP1; |
|||
double dAlphaPDpsiM1, dAlphaPDpsi, dAlphaPDpsiP1; |
|||
ONEmaterial *info; |
|||
|
|||
|
|||
pRElem = pNode->pRightElem; |
|||
pLElem = pNode->pLeftElem; |
|||
|
|||
if (pRElem->evalNodes[0]) { |
|||
info = pRElem->matlInfo; |
|||
} else { |
|||
info = pLElem->matlInfo; |
|||
} |
|||
|
|||
pREdge = pRElem->pEdge; |
|||
pLEdge = pLElem->pEdge; |
|||
dJnDpsiPrev = pLEdge->dJnDpsiP1; |
|||
dJpDpsiPrev = pLEdge->dJpDpsiP1; |
|||
|
|||
temp = pRElem->dx + pLElem->dx; |
|||
coeffR = pLElem->dx / temp; |
|||
coeffL = pRElem->dx / temp; |
|||
|
|||
eField = -(coeffR * pREdge->dPsi * pRElem->rDx + |
|||
coeffL * pLEdge->dPsi * pLElem->rDx); |
|||
jn = coeffR * pREdge->jn + coeffL * pLEdge->jn; |
|||
jp = coeffR * pREdge->jp + coeffL * pLEdge->jp; |
|||
|
|||
signE = SGN(eField); |
|||
eField = ABS(eField); |
|||
if (eField == 0.0) { |
|||
return (0.0); |
|||
} |
|||
signN = SGN(jn); |
|||
if (signN * signE > 0.0) { |
|||
/* field accelerates the carriers, hence avalanche */ |
|||
if (info->bii[ELEC] / eField > 80.0) { |
|||
alphaN = 0.0; |
|||
} else { |
|||
alphaN = info->aii[ELEC] * exp(-info->bii[ELEC] / eField); |
|||
} |
|||
} else { |
|||
alphaN = 0.0; |
|||
} |
|||
signP = SGN(jp); |
|||
if (signP * signE > 0.0) { |
|||
/* field accelerates the carriers, hence avalanche */ |
|||
if (info->bii[HOLE] / eField > 80.0) { |
|||
alphaP = 0.0; |
|||
} else { |
|||
alphaP = info->aii[HOLE] * exp(-info->bii[HOLE] / eField); |
|||
} |
|||
} else { |
|||
alphaP = 0.0; |
|||
} |
|||
|
|||
if ((alphaN == 0.0) && (alphaP == 0.0)) { |
|||
return (generation); |
|||
} |
|||
generation = (alphaN * ABS(jn) + alphaP * ABS(jp)) * |
|||
0.5 * (pRElem->dx + pLElem->dx); |
|||
if (rhsOnly) { |
|||
return (generation); |
|||
} |
|||
if (alphaN == 0.0) { |
|||
dAlphaNDpsiM1 = 0.0; |
|||
dAlphaNDpsiP1 = 0.0; |
|||
dAlphaNDpsi = 0.0; |
|||
} else { |
|||
temp = alphaN * info->bii[ELEC] / (eField * eField); |
|||
dAlphaNDpsiM1 = signE * temp * (coeffL * pLElem->rDx); |
|||
dAlphaNDpsiP1 = -signE * temp * (coeffR * pRElem->rDx); |
|||
dAlphaNDpsi = -(dAlphaNDpsiM1 + dAlphaNDpsiP1); |
|||
} |
|||
|
|||
if (alphaP == 0.0) { |
|||
dAlphaPDpsiM1 = 0.0; |
|||
dAlphaPDpsiP1 = 0.0; |
|||
dAlphaPDpsi = 0.0; |
|||
} else { |
|||
temp = alphaP * info->bii[HOLE] / (eField * eField); |
|||
dAlphaPDpsiM1 = signE * temp * (coeffL * pLElem->rDx); |
|||
dAlphaPDpsiP1 = -signE * temp * (coeffR * pRElem->rDx); |
|||
dAlphaPDpsi = -(dAlphaPDpsiM1 + dAlphaPDpsiP1); |
|||
} |
|||
|
|||
coeffR = 0.5 * pLElem->dx; |
|||
coeffL = 0.5 * pRElem->dx; |
|||
|
|||
if (pNode->nodeI != 2) { |
|||
*(pNode->fNPsiiM1) += |
|||
signN * (-alphaN * coeffL * dJnDpsiPrev + |
|||
coeffL * pLEdge->jn * dAlphaNDpsiM1) + |
|||
signP * (-alphaP * coeffL * dJpDpsiPrev + |
|||
coeffL * pLEdge->jp * dAlphaPDpsiM1); |
|||
*(pNode->fNNiM1) += signN * alphaN * coeffL * pLEdge->dJnDn; |
|||
*(pNode->fNPiM1) += signP * alphaP * coeffL * pLEdge->dJpDp; |
|||
|
|||
*(pNode->fPPsiiM1) -= |
|||
signN * (-alphaN * coeffL * dJnDpsiPrev + |
|||
coeffL * pLEdge->jn * dAlphaNDpsiM1) + |
|||
signP * (-alphaP * coeffL * dJpDpsiPrev + |
|||
coeffL * pLEdge->jp * dAlphaPDpsiM1); |
|||
*(pNode->fPPiM1) -= signP * alphaP * coeffL * pLEdge->dJpDp; |
|||
*(pNode->fPNiM1) -= signN * alphaN * coeffL * pLEdge->dJnDn; |
|||
} |
|||
if (pNode->nodeI != numNodes - 1) { |
|||
*(pNode->fNPsiiP1) += |
|||
signN * (alphaN * coeffR * pREdge->dJnDpsiP1 + |
|||
coeffR * pREdge->jn * dAlphaNDpsiP1) + |
|||
signP * (alphaP * coeffR * pREdge->dJpDpsiP1 + |
|||
coeffR * pREdge->jp * dAlphaPDpsiP1); |
|||
*(pNode->fNNiP1) += signN * alphaN * coeffR * pREdge->dJnDnP1; |
|||
*(pNode->fNPiP1) += signP * alphaP * coeffR * pREdge->dJpDpP1; |
|||
*(pNode->fPPsiiP1) -= |
|||
signN * (alphaN * coeffR * pREdge->dJnDpsiP1 + |
|||
coeffR * pREdge->jn * dAlphaNDpsiP1) + |
|||
signP * (alphaP * coeffR * pREdge->dJpDpsiP1 + |
|||
coeffR * pREdge->jp * dAlphaPDpsiP1); |
|||
*(pNode->fPPiP1) -= signP * alphaP * coeffR * pREdge->dJpDpP1; |
|||
*(pNode->fPNiP1) -= signN * alphaN * coeffR * pREdge->dJnDnP1; |
|||
} |
|||
*(pNode->fNPsi) += |
|||
signN * (alphaN * (-coeffR * pREdge->dJnDpsiP1 + |
|||
coeffL * dJnDpsiPrev) + (coeffR * pREdge->jn + |
|||
coeffL * pLEdge->jn) * dAlphaNDpsi) + |
|||
signP * (alphaP * (-coeffR * pREdge->dJpDpsiP1 + |
|||
coeffL * dJpDpsiPrev) + (coeffR * pREdge->jp + |
|||
coeffL * pLEdge->jp) * dAlphaPDpsi); |
|||
*(pNode->fNN) += signN * alphaN * (coeffR * pREdge->dJnDn + |
|||
coeffL * pLEdge->dJnDnP1); |
|||
*(pNode->fNP) += signP * alphaP * (coeffR * pREdge->dJpDp + |
|||
coeffL * pLEdge->dJpDpP1); |
|||
|
|||
*(pNode->fPPsi) -= |
|||
signN * (alphaN * (-coeffR * pREdge->dJnDpsiP1 + |
|||
coeffL * dJnDpsiPrev) + (coeffR * pREdge->jn + |
|||
coeffL * pLEdge->jn) * dAlphaNDpsi) + |
|||
signP * (alphaP * (-coeffR * pREdge->dJpDpsiP1 + |
|||
coeffL * dJpDpsiPrev) + (coeffR * pREdge->jp + |
|||
coeffL * pLEdge->jp) * dAlphaPDpsi); |
|||
*(pNode->fPN) -= signN * alphaN * (coeffR * pREdge->dJnDn + |
|||
coeffL * pLEdge->dJnDnP1); |
|||
*(pNode->fPP) -= signP * alphaP * (coeffR * pREdge->dJpDp + |
|||
coeffL * pLEdge->dJpDpP1); |
|||
|
|||
return (generation); |
|||
} |
|||
@ -0,0 +1,245 @@ |
|||
/********** |
|||
Copyright 1992 Regents of the University of California. All rights reserved. |
|||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
/* Functions to compute device conductances and currents */ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numglobs.h" |
|||
#include "numenum.h" |
|||
#include "onemesh.h" |
|||
#include "onedev.h" |
|||
#include "macros.h" |
|||
#include "spMatrix.h" |
|||
|
|||
#include "onedext.h" |
|||
#include "oneddefs.h" |
|||
|
|||
void |
|||
NUMDconductance(ONEdevice *pDevice, BOOLEAN tranAnalysis, |
|||
double *intCoeff, double *gd) |
|||
{ |
|||
ONEelem *pElem = pDevice->elemArray[pDevice->numNodes - 1]; |
|||
ONEnode *pNode; |
|||
ONEedge *pEdge; |
|||
int index; |
|||
double dPsiDv, dNDv, dPDv, *incVpn; |
|||
|
|||
|
|||
*gd = 0.0; |
|||
|
|||
/* zero the rhs before loading in the new rhs */ |
|||
for (index = 1; index <= pDevice->numEqns; index++) { |
|||
pDevice->rhs[index] = 0.0; |
|||
} |
|||
/* compute incremental changes due to N contact */ |
|||
pNode = pElem->pLeftNode; |
|||
pDevice->rhs[pNode->psiEqn] = pElem->epsRel * pElem->rDx; |
|||
if (pElem->elemType == SEMICON) { |
|||
pEdge = pElem->pEdge; |
|||
pDevice->rhs[pNode->nEqn] = -pEdge->dJnDpsiP1; |
|||
pDevice->rhs[pNode->pEqn] = -pEdge->dJpDpsiP1; |
|||
} |
|||
incVpn = pDevice->dcDeltaSolution; |
|||
spSolve(pDevice->matrix, pDevice->rhs, incVpn, NIL(spREAL), NIL(spREAL)); |
|||
|
|||
pElem = pDevice->elemArray[1]; |
|||
pNode = pElem->pRightNode; |
|||
pEdge = pElem->pEdge; |
|||
dPsiDv = incVpn[pNode->psiEqn]; |
|||
if (pElem->elemType == SEMICON) { |
|||
dNDv = incVpn[pNode->nEqn]; |
|||
dPDv = incVpn[pNode->pEqn]; |
|||
*gd += pEdge->dJnDpsiP1 * dPsiDv + pEdge->dJnDnP1 * dNDv + |
|||
pEdge->dJpDpsiP1 * dPsiDv + pEdge->dJpDpP1 * dPDv; |
|||
} |
|||
/* For transient analysis, add the displacement term */ |
|||
if (tranAnalysis) { |
|||
*gd -= intCoeff[0] * pElem->epsRel * pElem->rDx * dPsiDv; |
|||
} |
|||
*gd *= -GNorm * pDevice->area; |
|||
|
|||
} |
|||
|
|||
void |
|||
NBJTconductance(ONEdevice *pDevice, BOOLEAN tranAnalysis, double *intCoeff, |
|||
double *dIeDVce, double *dIcDVce, double *dIeDVbe, double *dIcDVbe) |
|||
{ |
|||
ONEelem *pLastElem = pDevice->elemArray[pDevice->numNodes - 1]; |
|||
ONEelem *pBaseElem = pDevice->elemArray[pDevice->baseIndex - 1]; |
|||
ONEelem *pElem; |
|||
ONEnode *pNode; |
|||
ONEedge *pEdge; |
|||
int index; |
|||
double dPsiDVce, dPsiDVbe, dNDVce, dNDVbe, dPDVce, dPDVbe; |
|||
double *incVce, *incVbe; |
|||
double nConc, pConc; |
|||
double area = pDevice->area; |
|||
|
|||
*dIeDVce = 0.0; |
|||
*dIcDVce = 0.0; |
|||
*dIeDVbe = 0.0; |
|||
*dIcDVbe = 0.0; |
|||
|
|||
/* zero the rhs before loading in the new rhs */ |
|||
for (index = 1; index <= pDevice->numEqns; index++) { |
|||
pDevice->rhs[index] = 0.0; |
|||
} |
|||
/* store the new rhs for computing CE incremental quantities */ |
|||
pNode = pLastElem->pLeftNode; |
|||
pDevice->rhs[pNode->psiEqn] = pLastElem->epsRel * pLastElem->rDx; |
|||
if (pLastElem->elemType == SEMICON) { |
|||
pEdge = pLastElem->pEdge; |
|||
pDevice->rhs[pNode->nEqn] = -pEdge->dJnDpsiP1; |
|||
pDevice->rhs[pNode->pEqn] = -pEdge->dJpDpsiP1; |
|||
} |
|||
incVce = pDevice->dcDeltaSolution; |
|||
spSolve(pDevice->matrix, pDevice->rhs, incVce, NIL(spREAL), NIL(spREAL)); |
|||
|
|||
/* zero the rhs before loading in the new rhs base contribution */ |
|||
for (index = 1; index <= pDevice->numEqns; index++) { |
|||
pDevice->rhs[index] = 0.0; |
|||
} |
|||
pNode = pBaseElem->pRightNode; |
|||
|
|||
if (pNode->baseType == N_TYPE) { |
|||
nConc = *(pDevice->devState0 + pNode->nodeN); |
|||
pDevice->rhs[pNode->nEqn] = nConc * pNode->eg; |
|||
} else if (pNode->baseType == P_TYPE) { |
|||
pConc = *(pDevice->devState0 + pNode->nodeP); |
|||
pDevice->rhs[pNode->pEqn] = pConc * pNode->eg; |
|||
} else { |
|||
printf("NBJTconductance: unknown base type\n"); |
|||
} |
|||
|
|||
incVbe = pDevice->copiedSolution; |
|||
spSolve(pDevice->matrix, pDevice->rhs, incVbe, NIL(spREAL), NIL(spREAL)); |
|||
|
|||
pElem = pDevice->elemArray[1];/* first element */ |
|||
pEdge = pElem->pEdge; |
|||
pNode = pElem->pRightNode; |
|||
|
|||
dPsiDVce = incVce[pNode->psiEqn]; |
|||
dPsiDVbe = incVbe[pNode->psiEqn]; |
|||
if (pElem->elemType == SEMICON) { |
|||
dNDVce = incVce[pNode->nEqn]; |
|||
dPDVce = incVce[pNode->pEqn]; |
|||
dNDVbe = incVbe[pNode->nEqn]; |
|||
dPDVbe = incVbe[pNode->pEqn]; |
|||
*dIeDVce += pEdge->dJnDpsiP1 * dPsiDVce + pEdge->dJnDnP1 * dNDVce + |
|||
pEdge->dJpDpsiP1 * dPsiDVce + pEdge->dJpDpP1 * dPDVce; |
|||
*dIeDVbe += pEdge->dJnDpsiP1 * dPsiDVbe + pEdge->dJnDnP1 * dNDVbe + |
|||
pEdge->dJpDpsiP1 * dPsiDVbe + pEdge->dJpDpP1 * dPDVbe; |
|||
} |
|||
/* For transient analysis add the displacement term */ |
|||
if (tranAnalysis) { |
|||
*dIeDVce -= intCoeff[0] * pElem->epsRel * dPsiDVce * pElem->rDx; |
|||
*dIeDVbe -= intCoeff[0] * pElem->epsRel * dPsiDVbe * pElem->rDx; |
|||
} |
|||
pElem = pDevice->elemArray[pDevice->numNodes - 1]; /* last element */ |
|||
pEdge = pElem->pEdge; |
|||
pNode = pElem->pLeftNode; |
|||
dPsiDVce = incVce[pNode->psiEqn]; |
|||
dPsiDVbe = incVbe[pNode->psiEqn]; |
|||
if (pElem->elemType == SEMICON) { |
|||
dNDVce = incVce[pNode->nEqn]; |
|||
dPDVce = incVce[pNode->pEqn]; |
|||
dNDVbe = incVbe[pNode->nEqn]; |
|||
dPDVbe = incVbe[pNode->pEqn]; |
|||
*dIcDVce += -pEdge->dJnDpsiP1 * dPsiDVce + pEdge->dJnDn * dNDVce + |
|||
-pEdge->dJpDpsiP1 * dPsiDVce + pEdge->dJpDp * dPDVce + |
|||
/* add terms since adjacent to boundary */ |
|||
pEdge->dJnDpsiP1 + pEdge->dJpDpsiP1; |
|||
*dIcDVbe += -pEdge->dJnDpsiP1 * dPsiDVbe + pEdge->dJnDn * dNDVbe + |
|||
-pEdge->dJpDpsiP1 * dPsiDVbe + pEdge->dJpDp * dPDVbe; |
|||
} |
|||
if (tranAnalysis) { |
|||
*dIcDVce += intCoeff[0] * pElem->epsRel * (dPsiDVce - 1.0) * pElem->rDx; |
|||
*dIcDVbe += intCoeff[0] * pElem->epsRel * dPsiDVbe * pElem->rDx; |
|||
} |
|||
*dIeDVce *= -GNorm * area; |
|||
*dIcDVce *= -GNorm * area; |
|||
*dIeDVbe *= -GNorm * area; |
|||
*dIcDVbe *= -GNorm * area; |
|||
} |
|||
|
|||
void |
|||
NUMDcurrent(ONEdevice *pDevice, BOOLEAN tranAnalysis, double *intCoeff, |
|||
double *id) |
|||
{ |
|||
ONEnode *pNode; |
|||
ONEelem *pElem; |
|||
ONEedge *pEdge; |
|||
int index; |
|||
double *delta = pDevice->dcDeltaSolution; |
|||
double dPsi, dN, dP, *solution; |
|||
|
|||
|
|||
*id = 0.0; |
|||
|
|||
pElem = pDevice->elemArray[1]; |
|||
pNode = pElem->pRightNode; |
|||
pEdge = pElem->pEdge; |
|||
dPsi = delta[pNode->psiEqn]; |
|||
*id = pEdge->jn + pEdge->jp + pElem->epsRel * pEdge->jd; |
|||
if (pElem->elemType == SEMICON) { |
|||
dN = delta[pNode->nEqn]; |
|||
dP = delta[pNode->pEqn]; |
|||
*id += pEdge->dJnDpsiP1 * dPsi + pEdge->dJnDnP1 * dN + |
|||
pEdge->dJpDpsiP1 * dPsi + pEdge->dJpDpP1 * dP; |
|||
} |
|||
/* for transient analysis add the displacement term */ |
|||
if (tranAnalysis) { |
|||
*id -= intCoeff[0] * pElem->epsRel * pElem->rDx * dPsi; |
|||
} |
|||
*id *= JNorm * pDevice->area; |
|||
} |
|||
|
|||
void |
|||
NBJTcurrent(ONEdevice *pDevice, BOOLEAN tranAnalysis, double *intCoeff, |
|||
double *ie, double *ic) |
|||
{ |
|||
ONEnode *pNode; |
|||
ONEelem *pElem; |
|||
ONEedge *pEdge; |
|||
int index; |
|||
double dPsi, dN, dP; |
|||
double *solution; |
|||
|
|||
solution = pDevice->dcDeltaSolution; |
|||
|
|||
/* first edge for calculating ie */ |
|||
pElem = pDevice->elemArray[1]; |
|||
pNode = pElem->pRightNode; |
|||
pEdge = pElem->pEdge; |
|||
dPsi = solution[pNode->psiEqn]; |
|||
*ie = pEdge->jn + pEdge->jp + pElem->epsRel * pEdge->jd; |
|||
if (pElem->elemType == SEMICON) { |
|||
dN = solution[pNode->nEqn]; |
|||
dP = solution[pNode->pEqn]; |
|||
*ie += pEdge->dJnDpsiP1 * dPsi + pEdge->dJnDnP1 * dN + |
|||
pEdge->dJpDpsiP1 * dPsi + pEdge->dJpDpP1 * dP; |
|||
} |
|||
/* for transient analysis add the displacement term */ |
|||
if (tranAnalysis) { |
|||
*ie -= intCoeff[0] * pElem->epsRel * dPsi * pElem->rDx; |
|||
} |
|||
/* last edge for calculating ic */ |
|||
pElem = pDevice->elemArray[pDevice->numNodes - 1]; |
|||
pNode = pElem->pLeftNode; |
|||
pEdge = pElem->pEdge; |
|||
dPsi = solution[pNode->psiEqn]; |
|||
*ic = pEdge->jn + pEdge->jp + pElem->epsRel * pEdge->jd; |
|||
if (pElem->elemType == SEMICON) { |
|||
dN = solution[pNode->nEqn]; |
|||
dP = solution[pNode->pEqn]; |
|||
*ic += -pEdge->dJnDpsiP1 * dPsi + pEdge->dJnDn * dN + |
|||
-pEdge->dJpDpsiP1 * dPsi + pEdge->dJpDp * dP; |
|||
} |
|||
if (tranAnalysis) { |
|||
*ic += intCoeff[0] * pElem->epsRel * dPsi * pElem->rDx; |
|||
} |
|||
*ic *= -JNorm * pDevice->area; |
|||
*ie *= -JNorm * pDevice->area; |
|||
} |
|||
@ -0,0 +1,657 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numglobs.h" |
|||
#include "numenum.h" |
|||
#include "onemesh.h" |
|||
#include "onedev.h" |
|||
#include "spMatrix.h" |
|||
#include "macros.h" |
|||
#include "onedext.h" |
|||
#include "oneddefs.h" |
|||
#include "cidersupt.h" |
|||
#include "../../maths/misc/bernoull.h" |
|||
|
|||
|
|||
/* Forward Declarations */ |
|||
|
|||
void ONE_commonTerms( ONEdevice *, BOOLEAN, BOOLEAN, ONEtranInfo *); |
|||
|
|||
|
|||
/* functions to setup and solve the continuity equations */ |
|||
/* Both continuity equations are solved */ |
|||
|
|||
|
|||
void |
|||
ONE_jacBuild(ONEdevice *pDevice) |
|||
{ |
|||
char *matrix = pDevice->matrix; |
|||
ONEelem *pElem; |
|||
ONEnode *pNode, *pNode1; |
|||
int index, eIndex; |
|||
int psiEqn, nEqn, pEqn; /* scratch for deref'd eqn numbers */ |
|||
int psiEqnL=0, nEqnL=0, pEqnL=0; |
|||
int psiEqnR=0, nEqnR=0, pEqnR=0; |
|||
|
|||
|
|||
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) { |
|||
pElem = pDevice->elemArray[eIndex]; |
|||
/* first the self terms */ |
|||
for (index = 0; index <= 1; index++) { |
|||
pNode = pElem->pNodes[index]; |
|||
/* get poisson pointer */ |
|||
psiEqn = pNode->psiEqn; |
|||
pNode->fPsiPsi = spGetElement(matrix, psiEqn, psiEqn); |
|||
|
|||
if (pElem->elemType == SEMICON) { |
|||
/* get continuity-coupling terms */ |
|||
nEqn = pNode->nEqn; |
|||
pEqn = pNode->pEqn; |
|||
/* pointers for additional terms */ |
|||
pNode->fPsiN = spGetElement(matrix, psiEqn, nEqn); |
|||
pNode->fPsiP = spGetElement(matrix, psiEqn, pEqn); |
|||
pNode->fNPsi = spGetElement(matrix, nEqn, psiEqn); |
|||
pNode->fNN = spGetElement(matrix, nEqn, nEqn); |
|||
pNode->fNP = spGetElement(matrix, nEqn, pEqn); |
|||
pNode->fPPsi = spGetElement(matrix, pEqn, psiEqn); |
|||
pNode->fPP = spGetElement(matrix, pEqn, pEqn); |
|||
pNode->fPN = spGetElement(matrix, pEqn, nEqn); |
|||
} else { |
|||
nEqn = 0; |
|||
pEqn = 0; |
|||
} |
|||
|
|||
/* save indices */ |
|||
if (index == 0) { /* left node */ |
|||
psiEqnL = psiEqn; |
|||
nEqnL = nEqn; |
|||
pEqnL = pEqn; |
|||
} else { |
|||
psiEqnR = psiEqn; |
|||
nEqnR = nEqn; |
|||
pEqnR = pEqn; |
|||
} |
|||
} |
|||
|
|||
/* now terms to couple to adjacent nodes */ |
|||
pNode = pElem->pLeftNode; |
|||
pNode->fPsiPsiiP1 = spGetElement(matrix, psiEqnL, psiEqnR); |
|||
if (pElem->elemType == SEMICON) { |
|||
/* pointers for additional terms */ |
|||
pNode->fNPsiiP1 = spGetElement(matrix, nEqnL, psiEqnR); |
|||
pNode->fNNiP1 = spGetElement(matrix, nEqnL, nEqnR); |
|||
pNode->fPPsiiP1 = spGetElement(matrix, pEqnL, psiEqnR); |
|||
pNode->fPPiP1 = spGetElement(matrix, pEqnL, pEqnR); |
|||
if (AvalancheGen) { |
|||
pNode->fNPiP1 = spGetElement(matrix, nEqnL, pEqnR); |
|||
pNode->fPNiP1 = spGetElement(matrix, pEqnL, nEqnR); |
|||
} |
|||
} |
|||
pNode = pElem->pRightNode; |
|||
pNode->fPsiPsiiM1 = spGetElement(matrix, psiEqnR, psiEqnL); |
|||
if (pElem->elemType == SEMICON) { |
|||
/* pointers for additional terms */ |
|||
pNode->fNPsiiM1 = spGetElement(matrix, nEqnR, psiEqnL); |
|||
pNode->fNNiM1 = spGetElement(matrix, nEqnR, nEqnL); |
|||
pNode->fPPsiiM1 = spGetElement(matrix, pEqnR, psiEqnL); |
|||
pNode->fPPiM1 = spGetElement(matrix, pEqnR, pEqnL); |
|||
if (AvalancheGen) { |
|||
pNode->fNPiM1 = spGetElement(matrix, nEqnR, pEqnL); |
|||
pNode->fPNiM1 = spGetElement(matrix, pEqnR, nEqnL); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
void |
|||
ONE_sysLoad(ONEdevice *pDevice, BOOLEAN tranAnalysis, |
|||
ONEtranInfo *info) |
|||
{ |
|||
ONEelem *pElem; |
|||
ONEnode *pNode, *pNode1; |
|||
ONEedge *pEdge; |
|||
int index, eIndex; |
|||
double *pRhs = pDevice->rhs; |
|||
double dx, rDx, dPsi; |
|||
double rhsN, rhsP, generation; |
|||
double perTime = 0.0; |
|||
double fNd, fNa, fdNd, fdNa; |
|||
double netConc, dNd, dNa, psi, nConc, pConc; |
|||
|
|||
|
|||
/* first compute the currents and their derivatives */ |
|||
ONE_commonTerms(pDevice, FALSE, tranAnalysis, info); |
|||
|
|||
/* find reciprocal timestep */ |
|||
if (tranAnalysis) { |
|||
perTime = info->intCoeff[0]; |
|||
} |
|||
/* zero the rhs vector */ |
|||
for (index = 1; index <= pDevice->numEqns; index++) { |
|||
pRhs[index] = 0.0; |
|||
} |
|||
|
|||
/* zero the matrix */ |
|||
spClear(pDevice->matrix); |
|||
|
|||
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) { |
|||
pElem = pDevice->elemArray[eIndex]; |
|||
dx = 0.5 * pElem->dx; |
|||
rDx = pElem->epsRel * pElem->rDx; |
|||
|
|||
/* load for all i */ |
|||
for (index = 0; index <= 1; index++) { |
|||
pNode = pElem->pNodes[index]; |
|||
if (pNode->nodeType != CONTACT) { |
|||
*(pNode->fPsiPsi) += rDx; |
|||
pRhs[pNode->psiEqn] += pNode->qf; |
|||
if (pElem->elemType == SEMICON) { |
|||
pEdge = pElem->pEdge; |
|||
netConc = pNode->netConc; |
|||
dNd = 0.0; |
|||
dNa = 0.0; |
|||
psi = *(pDevice->devState0 + pNode->nodePsi); |
|||
nConc = *(pDevice->devState0 + pNode->nodeN); |
|||
pConc = *(pDevice->devState0 + pNode->nodeP); |
|||
|
|||
|
|||
if (FreezeOut) { |
|||
ONE_freezeOut(pNode, nConc, pConc, &fNd, &fNa, &fdNd, &fdNa); |
|||
netConc = pNode->nd * fNd - pNode->na * fNa; |
|||
dNd = pNode->nd * fdNd; |
|||
dNa = pNode->na * fdNa; |
|||
} |
|||
*(pNode->fPsiN) += dx * (1.0 - dNd); |
|||
*(pNode->fPsiP) -= dx * (1.0 - dNa); |
|||
*(pNode->fNPsi) -= pEdge->dJnDpsiP1; |
|||
*(pNode->fPPsi) -= pEdge->dJpDpsiP1; |
|||
|
|||
pRhs[pNode->psiEqn] += dx * (netConc + pConc - nConc); |
|||
|
|||
/* Handle generation terms */ |
|||
*(pNode->fNN) -= dx * pNode->dUdN; |
|||
*(pNode->fNP) -= dx * pNode->dUdP; |
|||
*(pNode->fPP) += dx * pNode->dUdP; |
|||
*(pNode->fPN) += dx * pNode->dUdN; |
|||
pRhs[pNode->nEqn] -= -dx * pNode->uNet; |
|||
pRhs[pNode->pEqn] -= dx * pNode->uNet; |
|||
|
|||
/* Handle dXdT continuity terms */ |
|||
if (tranAnalysis) { |
|||
*(pNode->fNN) -= dx * perTime; |
|||
*(pNode->fPP) += dx * perTime; |
|||
pRhs[pNode->nEqn] += dx * pNode->dNdT; |
|||
pRhs[pNode->pEqn] -= dx * pNode->dPdT; |
|||
} |
|||
/* Take care of base contact if necessary */ |
|||
/* eg holds the base edge mu/dx */ |
|||
if (pNode->baseType == N_TYPE) { |
|||
pRhs[pNode->nEqn] += 0.5 * pNode->eg * nConc * |
|||
(pNode->vbe - psi + log(nConc / pNode->nie)); |
|||
*(pNode->fNPsi) += 0.5 * pNode->eg * nConc; |
|||
*(pNode->fNN) -= 0.5 * pNode->eg * |
|||
(pNode->vbe - psi + log(nConc / pNode->nie) + 1.0); |
|||
} else if (pNode->baseType == P_TYPE) { |
|||
pRhs[pNode->pEqn] += 0.5 * pNode->eg * pConc * |
|||
(pNode->vbe - psi - log(pConc / pNode->nie)); |
|||
*(pNode->fPPsi) += 0.5 * pNode->eg * pConc; |
|||
*(pNode->fPP) -= 0.5 * pNode->eg * |
|||
(pNode->vbe - psi - log(pConc / pNode->nie) - 1.0); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
pEdge = pElem->pEdge; |
|||
dPsi = pEdge->dPsi; |
|||
|
|||
pNode = pElem->pLeftNode; |
|||
if (pNode->nodeType != CONTACT) { |
|||
pRhs[pNode->psiEqn] += rDx * dPsi; |
|||
*(pNode->fPsiPsiiP1) -= rDx; |
|||
if (pElem->elemType == SEMICON) { |
|||
pRhs[pNode->nEqn] -= pEdge->jn; |
|||
pRhs[pNode->pEqn] -= pEdge->jp; |
|||
|
|||
*(pNode->fNN) += pEdge->dJnDn; |
|||
*(pNode->fPP) += pEdge->dJpDp; |
|||
*(pNode->fNPsiiP1) += pEdge->dJnDpsiP1; |
|||
*(pNode->fNNiP1) += pEdge->dJnDnP1; |
|||
*(pNode->fPPsiiP1) += pEdge->dJpDpsiP1; |
|||
*(pNode->fPPiP1) += pEdge->dJpDpP1; |
|||
} |
|||
} |
|||
pNode = pElem->pRightNode; |
|||
if (pNode->nodeType != CONTACT) { |
|||
pRhs[pNode->psiEqn] -= rDx * dPsi; |
|||
*(pNode->fPsiPsiiM1) -= rDx; |
|||
if (pElem->elemType == SEMICON) { |
|||
pRhs[pNode->nEqn] += pEdge->jn; |
|||
pRhs[pNode->pEqn] += pEdge->jp; |
|||
|
|||
|
|||
*(pNode->fNN) -= pEdge->dJnDnP1; |
|||
*(pNode->fPP) -= pEdge->dJpDpP1; |
|||
*(pNode->fNPsiiM1) += pEdge->dJnDpsiP1; |
|||
*(pNode->fNNiM1) -= pEdge->dJnDn; |
|||
*(pNode->fPPsiiM1) += pEdge->dJpDpsiP1; |
|||
*(pNode->fPPiM1) -= pEdge->dJpDp; |
|||
} |
|||
} |
|||
} |
|||
if (AvalancheGen) { |
|||
/* add the generation terms */ |
|||
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) { |
|||
pElem = pDevice->elemArray[eIndex]; |
|||
for (index = 0; index <= 1; index++) { |
|||
if (pElem->evalNodes[index]) { |
|||
pNode = pElem->pNodes[index]; |
|||
if ((pNode->nodeType != CONTACT) && (pElem->elemType == SEMICON)) { |
|||
generation = ONEavalanche(FALSE, pDevice, pNode); |
|||
pRhs[pNode->nEqn] -= generation; |
|||
pRhs[pNode->pEqn] += generation; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
void |
|||
ONE_jacLoad(ONEdevice *pDevice) |
|||
{ |
|||
/* used only for ac analysis */ |
|||
ONEelem *pElem; |
|||
ONEnode *pNode, *pNode1; |
|||
ONEedge *pEdge; |
|||
int index, eIndex; |
|||
double dx, rDx, dPsi; |
|||
double rhsN, rhsP, generation; |
|||
double fNd, fNa, fdNd, fdNa; |
|||
double netConc, dNd, dNa, psi, nConc, pConc; |
|||
|
|||
|
|||
/* first compute the currents and their derivatives */ |
|||
ONE_commonTerms(pDevice, FALSE, FALSE, NIL(ONEtranInfo)); |
|||
|
|||
/* zero the matrix */ |
|||
spClear(pDevice->matrix); |
|||
|
|||
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) { |
|||
pElem = pDevice->elemArray[eIndex]; |
|||
dx = 0.5 * pElem->dx; |
|||
rDx = pElem->epsRel * pElem->rDx; |
|||
/* load for all i */ |
|||
for (index = 0; index <= 1; index++) { |
|||
pNode = pElem->pNodes[index]; |
|||
if (pNode->nodeType != CONTACT) { |
|||
*(pNode->fPsiPsi) += rDx; |
|||
if (pElem->elemType == SEMICON) { |
|||
pEdge = pElem->pEdge; |
|||
dNd = 0.0; |
|||
dNa = 0.0; |
|||
psi = *(pDevice->devState0 + pNode->nodePsi); |
|||
nConc = *(pDevice->devState0 + pNode->nodeN); |
|||
pConc = *(pDevice->devState0 + pNode->nodeP); |
|||
if (FreezeOut) { |
|||
ONE_freezeOut(pNode, nConc, pConc, &fNd, &fNa, &fdNd, &fdNa); |
|||
dNd = pNode->nd * fdNd; |
|||
dNa = pNode->na * fdNa; |
|||
} |
|||
*(pNode->fPsiN) += dx * (1.0 - dNd); |
|||
*(pNode->fPsiP) -= dx * (1.0 - dNa); |
|||
*(pNode->fNPsi) -= pEdge->dJnDpsiP1; |
|||
*(pNode->fPPsi) -= pEdge->dJpDpsiP1; |
|||
|
|||
if (pNode->baseType == N_TYPE) { |
|||
*(pNode->fNPsi) += 0.5 * nConc * pNode->eg; |
|||
*(pNode->fNN) -= 0.5 * pNode->eg |
|||
* (pNode->vbe - psi + log(nConc / pNode->nie) + 1.0); |
|||
} |
|||
if (pNode->baseType == P_TYPE) { |
|||
*(pNode->fPPsi) += 0.5 * pConc * pNode->eg; |
|||
*(pNode->fPP) -= 0.5 * pNode->eg |
|||
* (pNode->vbe - psi - log(pConc / pNode->nie) - 1.0); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
pNode = pElem->pLeftNode; |
|||
if (pNode->nodeType != CONTACT) { |
|||
pEdge = pElem->pEdge; |
|||
dPsi = pEdge->dPsi; |
|||
if (pElem->elemType == SEMICON) { |
|||
*(pNode->fNN) += pEdge->dJnDn - dx * pNode->dUdN; |
|||
*(pNode->fNP) -= dx * pNode->dUdP; |
|||
*(pNode->fPP) += pEdge->dJpDp + dx * pNode->dUdP; |
|||
*(pNode->fPN) += dx * pNode->dUdN; |
|||
} |
|||
pNode1 = pElem->pRightNode; |
|||
if (pNode1->nodeType != CONTACT) { |
|||
*(pNode->fPsiPsiiP1) -= rDx; |
|||
if (pElem->elemType == SEMICON) { |
|||
*(pNode->fNPsiiP1) += pEdge->dJnDpsiP1; |
|||
*(pNode->fNNiP1) += pEdge->dJnDnP1; |
|||
*(pNode->fPPsiiP1) += pEdge->dJpDpsiP1; |
|||
*(pNode->fPPiP1) += pEdge->dJpDpP1; |
|||
} |
|||
} |
|||
} |
|||
pNode = pElem->pRightNode; |
|||
if (pNode->nodeType != CONTACT) { |
|||
pEdge = pElem->pEdge; |
|||
dPsi = pEdge->dPsi; |
|||
if (pElem->elemType == SEMICON) { |
|||
*(pNode->fNN) += -pEdge->dJnDnP1 - dx * pNode->dUdN; |
|||
*(pNode->fNP) -= dx * pNode->dUdP; |
|||
*(pNode->fPP) += -pEdge->dJpDpP1 + dx * pNode->dUdP; |
|||
*(pNode->fPN) += dx * pNode->dUdN; |
|||
} |
|||
pNode1 = pElem->pLeftNode; |
|||
if (pNode1->nodeType != CONTACT) { |
|||
*(pNode->fPsiPsiiM1) -= rDx; |
|||
if (pElem->elemType == SEMICON) { |
|||
*(pNode->fNPsiiM1) += pEdge->dJnDpsiP1; |
|||
*(pNode->fNNiM1) -= pEdge->dJnDn; |
|||
*(pNode->fPPsiiM1) += pEdge->dJpDpsiP1; |
|||
*(pNode->fPPiM1) -= pEdge->dJpDp; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
if (AvalancheGen) { |
|||
/* add the generation terms */ |
|||
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) { |
|||
pElem = pDevice->elemArray[eIndex]; |
|||
for (index = 0; index <= 1; index++) { |
|||
if (pElem->evalNodes[index]) { |
|||
pNode = pElem->pNodes[index]; |
|||
if ((pNode->nodeType != CONTACT) && (pElem->elemType == SEMICON)) { |
|||
generation = ONEavalanche(FALSE, pDevice, pNode); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
void |
|||
ONE_rhsLoad(ONEdevice *pDevice, BOOLEAN tranAnalysis, |
|||
ONEtranInfo *info) |
|||
{ |
|||
ONEelem *pElem; |
|||
ONEnode *pNode, *pNode1; |
|||
ONEedge *pEdge; |
|||
int index, eIndex; |
|||
double *pRhs = pDevice->rhs; |
|||
double dx, rDx, dPsi; |
|||
double rhsN, rhsP, generation; |
|||
double perTime; |
|||
double fNd, fNa, fdNd, fdNa; |
|||
double netConc, dNd, dNa, psi, nConc, pConc; |
|||
|
|||
/* first compute the currents and their derivatives */ |
|||
ONE_commonTerms(pDevice, FALSE, tranAnalysis, info); |
|||
|
|||
/* find reciprocal timestep */ |
|||
if (tranAnalysis) { |
|||
perTime = info->intCoeff[0]; |
|||
} |
|||
/* zero the rhs vector */ |
|||
for (index = 1; index <= pDevice->numEqns; index++) { |
|||
pRhs[index] = 0.0; |
|||
} |
|||
|
|||
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) { |
|||
pElem = pDevice->elemArray[eIndex]; |
|||
|
|||
dx = 0.5 * pElem->dx; |
|||
rDx = pElem->epsRel * pElem->rDx; |
|||
|
|||
/* load for all i */ |
|||
for (index = 0; index <= 1; index++) { |
|||
pNode = pElem->pNodes[index]; |
|||
if (pNode->nodeType != CONTACT) { |
|||
pRhs[pNode->psiEqn] += pNode->qf; |
|||
if (pElem->elemType == SEMICON) { |
|||
pEdge = pElem->pEdge; |
|||
netConc = pNode->netConc; |
|||
dNd = 0.0; |
|||
dNa = 0.0; |
|||
psi = *(pDevice->devState0 + pNode->nodePsi); |
|||
nConc = *(pDevice->devState0 + pNode->nodeN); |
|||
pConc = *(pDevice->devState0 + pNode->nodeP); |
|||
if (FreezeOut) { |
|||
ONE_freezeOut(pNode, nConc, pConc, &fNd, &fNa, &fdNd, &fdNa); |
|||
netConc = pNode->nd * fNd - pNode->na * fNa; |
|||
dNd = pNode->nd * fdNd; |
|||
dNa = pNode->na * fdNa; |
|||
} |
|||
pRhs[pNode->psiEqn] += dx * (netConc + pConc - nConc); |
|||
|
|||
/* Handle generation terms */ |
|||
pRhs[pNode->nEqn] -= -dx * pNode->uNet; |
|||
pRhs[pNode->pEqn] -= dx * pNode->uNet; |
|||
|
|||
/* Handle dXdT continuity terms */ |
|||
if (tranAnalysis) { |
|||
pRhs[pNode->nEqn] += dx * pNode->dNdT; |
|||
pRhs[pNode->pEqn] -= dx * pNode->dPdT; |
|||
} |
|||
/* Take care of base contact if necessary */ |
|||
/* eg holds the base edge mu/dx */ |
|||
if (pNode->baseType == N_TYPE) { |
|||
pRhs[pNode->nEqn] += 0.5 * pNode->eg * nConc * |
|||
(pNode->vbe - psi + log(nConc / pNode->nie)); |
|||
} else if (pNode->baseType == P_TYPE) { |
|||
pRhs[pNode->pEqn] += 0.5 * pNode->eg * pConc * |
|||
(pNode->vbe - psi - log(pConc / pNode->nie)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
pEdge = pElem->pEdge; |
|||
dPsi = pEdge->dPsi; |
|||
|
|||
pNode = pElem->pLeftNode; |
|||
if (pNode->nodeType != CONTACT) { |
|||
pRhs[pNode->psiEqn] += rDx * dPsi; |
|||
if (pElem->elemType == SEMICON) { |
|||
pRhs[pNode->nEqn] -= pEdge->jn; |
|||
pRhs[pNode->pEqn] -= pEdge->jp; |
|||
} |
|||
} |
|||
pNode = pElem->pRightNode; |
|||
if (pNode->nodeType != CONTACT) { |
|||
pRhs[pNode->psiEqn] -= rDx * dPsi; |
|||
if (pElem->elemType == SEMICON) { |
|||
pRhs[pNode->nEqn] += pEdge->jn; |
|||
pRhs[pNode->pEqn] += pEdge->jp; |
|||
} |
|||
} |
|||
} |
|||
if (AvalancheGen) { |
|||
/* add the generation terms */ |
|||
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) { |
|||
pElem = pDevice->elemArray[eIndex]; |
|||
for (index = 0; index <= 1; index++) { |
|||
if (pElem->evalNodes[index]) { |
|||
pNode = pElem->pNodes[index]; |
|||
if ((pNode->nodeType != CONTACT) && (pElem->elemType == SEMICON)) { |
|||
generation = ONEavalanche(TRUE, pDevice, pNode); |
|||
pRhs[pNode->nEqn] -= generation; |
|||
pRhs[pNode->pEqn] += generation; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
void |
|||
ONE_commonTerms(ONEdevice *pDevice, BOOLEAN currentOnly, |
|||
BOOLEAN tranAnalysis, ONEtranInfo *info) |
|||
{ |
|||
ONEelem *pElem; |
|||
ONEedge *pEdge; |
|||
ONEnode *pNode, *pNode1; |
|||
int index, eIndex; |
|||
double psi1, psi2, psi, nConc=0.0, pConc=0.0, nC, pC, nP1, pP1; |
|||
double dPsiN, dPsiP; |
|||
double bPsiN, dbPsiN, bMPsiN, dbMPsiN; |
|||
double bPsiP, dbPsiP, bMPsiP, dbMPsiP; |
|||
double mun, dMun, mup, dMup, rDx; |
|||
double conc1, conc2; |
|||
double cnAug, cpAug; |
|||
|
|||
|
|||
/* evaluate all node (including recombination) and edge quantities */ |
|||
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) { |
|||
pElem = pDevice->elemArray[eIndex]; |
|||
cnAug = pElem->matlInfo->cAug[ELEC]; |
|||
cpAug = pElem->matlInfo->cAug[HOLE]; |
|||
for (index = 0; index <= 1; index++) { |
|||
if (pElem->evalNodes[index]) { |
|||
pNode = pElem->pNodes[index]; |
|||
if (pNode->nodeType != CONTACT) { |
|||
psi = pDevice->dcSolution[pNode->psiEqn]; |
|||
if (pElem->elemType == SEMICON) { |
|||
nConc = pDevice->dcSolution[pNode->nEqn]; |
|||
pConc = pDevice->dcSolution[pNode->pEqn]; |
|||
if (Srh) { |
|||
recomb(nConc, pConc, |
|||
pNode->tn, pNode->tp, cnAug, cpAug, pNode->nie, |
|||
&pNode->uNet, &pNode->dUdN, &pNode->dUdP); |
|||
} else { |
|||
pNode->uNet = 0.0; |
|||
pNode->dUdN = 0.0; |
|||
pNode->dUdP = 0.0; |
|||
} |
|||
if (pNode->baseType == P_TYPE && pConc <= 0.0) { |
|||
pConc = pNode->na; |
|||
} else if (pNode->baseType == N_TYPE && nConc <= 0.0) { |
|||
nConc = pNode->nd; |
|||
} |
|||
} |
|||
} else { |
|||
/* a contact node */ |
|||
psi = pNode->psi; |
|||
if (pElem->elemType == SEMICON) { |
|||
nConc = pNode->nConc; |
|||
pConc = pNode->pConc; |
|||
} |
|||
} |
|||
/* store info in the state tables */ |
|||
*(pDevice->devState0 + pNode->nodePsi) = psi; |
|||
if (pElem->elemType == SEMICON) { |
|||
*(pDevice->devState0 + pNode->nodeN) = nConc; |
|||
*(pDevice->devState0 + pNode->nodeP) = pConc; |
|||
if (tranAnalysis && pNode->nodeType != CONTACT) { |
|||
pNode->dNdT = integrate(pDevice->devStates, info, pNode->nodeN); |
|||
pNode->dPdT = integrate(pDevice->devStates, info, pNode->nodeP); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
pEdge = pElem->pEdge; |
|||
pNode = pElem->pLeftNode; |
|||
if (pNode->nodeType != CONTACT) { |
|||
psi1 = pDevice->dcSolution[pNode->psiEqn]; |
|||
} else { |
|||
psi1 = pNode->psi; |
|||
} |
|||
pNode = pElem->pRightNode; |
|||
if (pNode->nodeType != CONTACT) { |
|||
psi2 = pDevice->dcSolution[pNode->psiEqn]; |
|||
} else { |
|||
psi2 = pNode->psi; |
|||
} |
|||
pEdge->dPsi = psi2 - psi1; |
|||
*(pDevice->devState0 + pEdge->edgeDpsi) = pEdge->dPsi; |
|||
} |
|||
/* calculate the current densities and mobility values */ |
|||
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) { |
|||
pElem = pDevice->elemArray[eIndex]; |
|||
pEdge = pElem->pEdge; |
|||
if (pElem->elemType == SEMICON) { |
|||
dPsiN = pEdge->dPsi + pEdge->dCBand; |
|||
dPsiP = pEdge->dPsi - pEdge->dVBand; |
|||
bernoulli(dPsiN, &bPsiN, &dbPsiN, &bMPsiN, &dbMPsiN, !currentOnly); |
|||
bernoulli(dPsiP, &bPsiP, &dbPsiP, &bMPsiP, &dbMPsiP, !currentOnly); |
|||
nC = *(pDevice->devState0 + pElem->pLeftNode->nodeN); |
|||
nP1 = *(pDevice->devState0 + pElem->pRightNode->nodeN); |
|||
pC = *(pDevice->devState0 + pElem->pLeftNode->nodeP); |
|||
pP1 = *(pDevice->devState0 + pElem->pRightNode->nodeP); |
|||
conc1 = pElem->pLeftNode->totalConc; |
|||
conc2 = pElem->pRightNode->totalConc; |
|||
pEdge->jn = (bPsiN * nP1 - bMPsiN * nC); |
|||
pEdge->jp = (bPsiP * pC - bMPsiP * pP1); |
|||
|
|||
|
|||
mun = pEdge->mun; |
|||
dMun = 0.0; |
|||
mup = pEdge->mup; |
|||
dMup = 0.0; |
|||
MOBfieldDep(pElem->matlInfo, ELEC, dPsiN * pElem->rDx, &mun, &dMun); |
|||
MOBfieldDep(pElem->matlInfo, HOLE, dPsiP * pElem->rDx, &mup, &dMup); |
|||
|
|||
|
|||
mun *= pElem->rDx; |
|||
dMun *= pElem->rDx * pElem->rDx; |
|||
mup *= pElem->rDx; |
|||
dMup *= pElem->rDx * pElem->rDx; |
|||
|
|||
|
|||
|
|||
/* |
|||
* The base continuity equation makes use of mu/dx in eg. The base |
|||
* length has already been calculated and converted to normalized, |
|||
* reciprocal form during setup. The name should be changed, but that's |
|||
* a big hassle. |
|||
*/ |
|||
for (index = 0; index <= 1; index++) { |
|||
if (pElem->evalNodes[index]) { |
|||
pNode = pElem->pNodes[index]; |
|||
if (pNode->baseType == N_TYPE) { |
|||
pNode->eg = pEdge->mun * pDevice->baseLength; |
|||
} else if (pNode->baseType == P_TYPE) { |
|||
pNode->eg = pEdge->mup * pDevice->baseLength; |
|||
} |
|||
} |
|||
} |
|||
|
|||
pEdge->jn *= mun; |
|||
pEdge->jp *= mup; |
|||
|
|||
|
|||
if (!currentOnly) { |
|||
if (dMun == 0.0) { |
|||
pEdge->dJnDpsiP1 = mun * (dbPsiN * nP1 - dbMPsiN * nC); |
|||
} else { |
|||
pEdge->dJnDpsiP1 = dMun * (bPsiN * nP1 - bMPsiN * nC) |
|||
+ mun * (dbPsiN * nP1 - dbMPsiN * nC); |
|||
} |
|||
pEdge->dJnDn = -mun * bMPsiN; |
|||
pEdge->dJnDnP1 = mun * bPsiN; |
|||
if (dMup == 0.0) { |
|||
pEdge->dJpDpsiP1 = mup * (dbPsiP * pC - dbMPsiP * pP1); |
|||
} else { |
|||
pEdge->dJpDpsiP1 = dMup * (bPsiP * pC - bMPsiP * pP1) |
|||
+ mup * (dbPsiP * pC - dbMPsiP * pP1); |
|||
} |
|||
pEdge->dJpDp = mup * bPsiP; |
|||
pEdge->dJpDpP1 = -mup * bMPsiP; |
|||
} |
|||
} |
|||
if (tranAnalysis) { |
|||
pEdge->jd = -integrate(pDevice->devStates, info, |
|||
pEdge->edgeDpsi) * pElem->rDx; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
/* |
|||
* 2001 Paolo Nenzi |
|||
*/ |
|||
|
|||
#ifndef _ONEDDEFS_H |
|||
#define _ONEDDEFS_H |
|||
|
|||
/* Debug statements */ |
|||
|
|||
extern BOOLEAN ONEacDebug; |
|||
extern BOOLEAN ONEdcDebug; |
|||
extern BOOLEAN ONEtranDebug; |
|||
extern BOOLEAN ONEjacDebug; |
|||
|
|||
/* Now some defines for the one dimensional simulator |
|||
* library. |
|||
* Theese defines were gathered from all the code in |
|||
* oned directory. |
|||
*/ |
|||
|
|||
#define LEVEL_ALPHA_SI 3.1e-8 /* From de Graaf & Klaasen, pg. 12 */ |
|||
#define MIN_DELV 1e-3 |
|||
#define NORM_RED_MAXITERS 10 |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
#endif |
|||
@ -0,0 +1,74 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numglobs.h" |
|||
#include "numenum.h" |
|||
#include "onedev.h" |
|||
#include "onemesh.h" |
|||
#include "spMatrix.h" |
|||
#include "onedext.h" |
|||
#include "oneddefs.h" |
|||
|
|||
void |
|||
ONEdestroy(ONEdevice *pDevice) |
|||
{ |
|||
int index, eIndex; |
|||
ONEelem *pElem; |
|||
ONEnode *pNode; |
|||
ONEedge *pEdge; |
|||
|
|||
|
|||
if (!pDevice) |
|||
return; |
|||
|
|||
switch (pDevice->solverType) { |
|||
case SLV_SMSIG: |
|||
case SLV_BIAS: |
|||
/* free up memory allocated for the bias solution */ |
|||
FREE(pDevice->dcSolution); |
|||
FREE(pDevice->dcDeltaSolution); |
|||
FREE(pDevice->copiedSolution); |
|||
FREE(pDevice->rhs); |
|||
FREE(pDevice->rhsImag); |
|||
spDestroy(pDevice->matrix); |
|||
break; |
|||
case SLV_EQUIL: |
|||
/* free up the vectors allocated in the equilibrium solution */ |
|||
FREE(pDevice->dcSolution); |
|||
FREE(pDevice->dcDeltaSolution); |
|||
FREE(pDevice->copiedSolution); |
|||
FREE(pDevice->rhs); |
|||
spDestroy(pDevice->matrix); |
|||
break; |
|||
case SLV_NONE: |
|||
break; |
|||
default: |
|||
fprintf(stderr, "Panic: Unknown solver type in ONEdestroy.\n"); |
|||
exit(-1); |
|||
break; |
|||
} |
|||
|
|||
/* destroy the mesh */ |
|||
if (pDevice->elemArray) { |
|||
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) { |
|||
pElem = pDevice->elemArray[eIndex]; |
|||
for (index = 0; index <= 2; index++) { |
|||
if (pElem->evalNodes[index]) { |
|||
pNode = pElem->pNodes[index]; |
|||
FREE(pNode); |
|||
} |
|||
pEdge = pElem->pEdge; |
|||
FREE(pEdge); |
|||
} |
|||
FREE(pElem); |
|||
} |
|||
FREE(pDevice->elemArray); |
|||
} |
|||
/* destroy any other lists */ |
|||
/* NOT IMPLEMENTED */ |
|||
|
|||
FREE(pDevice); |
|||
} |
|||
@ -0,0 +1,121 @@ |
|||
/* |
|||
* 2001 Paolo Nenzi |
|||
*/ |
|||
|
|||
/* External symbols for ONE Dimensional simulator */ |
|||
|
|||
#ifndef _ONEDEXT_H |
|||
#define _ONEDEXT_H |
|||
|
|||
#include "profile.h" |
|||
#include "onemesh.h" |
|||
#include "onedev.h" |
|||
#include "carddefs.h" |
|||
#include "bool.h" |
|||
#include "complex.h" |
|||
|
|||
/* oneadmit.c */ |
|||
extern int NUMDadmittance(ONEdevice *, double, SPcomplex *); |
|||
extern int NBJTadmittance(ONEdevice *, double, SPcomplex *, |
|||
SPcomplex *, SPcomplex *, |
|||
SPcomplex *); |
|||
extern BOOLEAN ONEsorSolve(ONEdevice *, double *, double *, double omega); |
|||
extern void NUMDys(ONEdevice *, SPcomplex *, SPcomplex *); |
|||
extern void NBJTys(ONEdevice *,SPcomplex *, SPcomplex *, SPcomplex *, |
|||
SPcomplex *, SPcomplex *); |
|||
|
|||
extern SPcomplex *computeAdmittance(ONEnode *, BOOLEAN, double *xReal, |
|||
double *,SPcomplex *); |
|||
|
|||
/* oneaval.c */ |
|||
extern double ONEavalanche(BOOLEAN, ONEdevice *, ONEnode *); |
|||
|
|||
/* onecond.c */ |
|||
extern void NUMDconductance(ONEdevice *, BOOLEAN, double *, double *); |
|||
extern void NBJTconductance(ONEdevice *, BOOLEAN, double *, double *, |
|||
double *, double *, double *); |
|||
extern void NUMDcurrent(ONEdevice *, BOOLEAN, double *, double *); |
|||
extern void NBJTcurrent(ONEdevice *, BOOLEAN, double *, double *, |
|||
double *); |
|||
|
|||
|
|||
/* onecont */ |
|||
extern void ONE_jacBuild(ONEdevice *); |
|||
extern void ONE_sysLoad(ONEdevice *, BOOLEAN, ONEtranInfo *); |
|||
extern void ONE_jacLoad(ONEdevice *); |
|||
extern void ONE_rhsLoad(ONEdevice *, BOOLEAN, ONEtranInfo *); |
|||
extern void ONE_commonTerms(ONEdevice *, BOOLEAN, BOOLEAN, ONEtranInfo *); |
|||
|
|||
|
|||
/* onedest */ |
|||
extern void ONEdestroy(ONEdevice *); |
|||
|
|||
/* onedopng.c */ |
|||
extern double ONEdopingValue(DOPprofile *, DOPtable *, double ); |
|||
extern void ONEsetDoping(ONEdevice *, DOPprofile *, DOPtable *); |
|||
|
|||
/* onefreez.c */ |
|||
extern void ONEQfreezeOut(ONEnode *, double *, double *, double *, |
|||
double *); |
|||
extern void ONE_freezeOut(ONEnode *, double, double, double *, |
|||
double*, double *, double *); |
|||
|
|||
|
|||
/* onemesh.c */ |
|||
extern void ONEbuildMesh(ONEdevice *, ONEcoord *, ONEdomain *, |
|||
ONEmaterial *); |
|||
extern void ONEgetStatePointers(ONEdevice *, int *); |
|||
extern void adjustBaseContact(ONEdevice *, int, int); |
|||
extern void NBJTjunctions(ONEdevice *, int *, int *); |
|||
extern void ONEprnMesh(ONEdevice *); |
|||
|
|||
/* onepoiss.c */ |
|||
extern void ONEQjacBuild(ONEdevice *); |
|||
extern void ONEQsysLoad(ONEdevice *); |
|||
extern void ONEQrhsLoad(ONEdevice *); |
|||
extern void ONEQcommonTerms(ONEdevice *); |
|||
|
|||
/*oneprint.c */ |
|||
extern void ONEprnSolution(FILE *, ONEdevice *, OUTPcard *); |
|||
extern void ONEmemStats(FILE *, ONEdevice *); |
|||
extern void ONEcpuStats(FILE *f, ONEdevice *); |
|||
|
|||
|
|||
/* oneproj.c */ |
|||
extern void NUMDproject(ONEdevice *, double); |
|||
extern void NBJTproject(ONEdevice *, double, double, double); |
|||
extern void NUMDupdate(ONEdevice *,double, BOOLEAN); |
|||
extern void NBJTupdate(ONEdevice *, double, double, double, BOOLEAN); |
|||
extern void NUMDsetBCs(ONEdevice *, double); |
|||
extern void NBJTsetBCs(ONEdevice *, double, double); |
|||
|
|||
/* oneread.c */ |
|||
extern int ONEreadState(ONEdevice *, char *, int, double *, double *); |
|||
|
|||
/*onesetup.c */ |
|||
extern void ONEsetup(ONEdevice *); |
|||
extern void ONEsetBCparams(ONEdevice *, BDRYcard *, CONTcard *); |
|||
extern void ONEnormalize(ONEdevice *); |
|||
|
|||
/* onesolve.c */ |
|||
extern void ONEdcSolve(ONEdevice *, int, BOOLEAN, BOOLEAN, ONEtranInfo *); |
|||
extern BOOLEAN ONEpsiDeltaConverged(ONEdevice *, int *); |
|||
extern BOOLEAN ONEdeltaConverged(ONEdevice *); |
|||
extern BOOLEAN ONEdeltaConverged(ONEdevice *); |
|||
extern void ONEresetJacobian(ONEdevice *); |
|||
extern void ONEstoreNeutralGuess(ONEdevice *); |
|||
extern void ONEequilSolve(ONEdevice *); |
|||
extern void ONEbiasSolve(ONEdevice *, int, BOOLEAN, ONEtranInfo *); |
|||
extern void ONEstoreEquilibGuess(ONEdevice *); |
|||
extern void ONEstoreInitialGuess(ONEdevice *); |
|||
extern int ONEnewDelta(ONEdevice *, BOOLEAN, ONEtranInfo *); |
|||
extern int ONEnewDelta(ONEdevice *, BOOLEAN, ONEtranInfo *); |
|||
extern double ONEtrunc(ONEdevice *, ONEtranInfo *, double); |
|||
extern void ONEsaveState(ONEdevice *); |
|||
extern double ONEnuNorm(ONEdevice *); |
|||
extern void ONEjacCheck(ONEdevice *, BOOLEAN, ONEtranInfo *); |
|||
extern void ONEpredict(ONEdevice *, ONEtranInfo *); |
|||
extern BOOLEAN ONEdeviceConverged(ONEdevice *); |
|||
|
|||
|
|||
#endif |
|||
@ -0,0 +1,166 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group |
|||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numglobs.h" |
|||
#include "numenum.h" |
|||
#include "onemesh.h" |
|||
#include "onedev.h" |
|||
#include "profile.h" |
|||
#include "macros.h" |
|||
#include "onedext.h" |
|||
#include "oneddefs.h" |
|||
#include "cidersupt.h" |
|||
|
|||
/* functions in this file are used to calculate the conc */ |
|||
|
|||
double |
|||
ONEdopingValue(DOPprofile *pProfile, DOPtable *pTable, double x) |
|||
{ |
|||
double argX, argP, value=0.0; |
|||
|
|||
|
|||
/* Find the appropriate lookup table if necessary */ |
|||
if (pProfile->type == LOOKUP) { |
|||
while (pTable != NIL(DOPtable)) { |
|||
if (pTable->impId == pProfile->IMPID) { |
|||
/* Found it */ |
|||
break; |
|||
} else { |
|||
pTable = pTable->next; |
|||
} |
|||
} |
|||
if (pTable == NIL(DOPtable)) { |
|||
fprintf(stderr, "Error: unknown impurity profile %d\n", |
|||
((int)pProfile->IMPID)); |
|||
exit(1); |
|||
} |
|||
} |
|||
/* Find distances */ |
|||
if (pProfile->X_LOW > x) { |
|||
argX = pProfile->X_LOW - x; |
|||
} else if (x > pProfile->X_HIGH) { |
|||
argX = x - pProfile->X_HIGH; |
|||
} else { |
|||
argX = 0.0; |
|||
} |
|||
|
|||
argP = argX; |
|||
|
|||
/* Transform to coordinates of profile peak */ |
|||
argP -= pProfile->LOCATION; |
|||
argP /= pProfile->CHAR_LENGTH; |
|||
|
|||
switch (pProfile->type) { |
|||
case UNIF: |
|||
if (argP > 0.0) { |
|||
value = 0.0; |
|||
} else { |
|||
value = pProfile->CONC; |
|||
} |
|||
break; |
|||
case LIN: |
|||
argP = ABS(argP); |
|||
if (argP > 1.0) { |
|||
value = 0.0; |
|||
} else { |
|||
value = pProfile->CONC * (1.0 - argP); |
|||
} |
|||
break; |
|||
case GAUSS: |
|||
argP *= argP; |
|||
if (argP > 80.0) { |
|||
value = 0.0; |
|||
} else { |
|||
value = pProfile->PEAK_CONC * exp(-argP); |
|||
} |
|||
case EXP: |
|||
argP = ABS(argP); |
|||
if (argP > 80.0) { |
|||
value = 0.0; |
|||
} else { |
|||
value = pProfile->PEAK_CONC * exp(-argP); |
|||
} |
|||
break; |
|||
case ERRFC: |
|||
argP = ABS(argP); |
|||
if (argP > 10.0) { |
|||
value = 0.0; |
|||
} else { |
|||
value = pProfile->PEAK_CONC * erfc(-argP); |
|||
} |
|||
break; |
|||
case LOOKUP: |
|||
argP = ABS(argP); |
|||
value = lookup(pTable->dopData, argP); |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
return (value); |
|||
} |
|||
|
|||
|
|||
void |
|||
ONEsetDoping(ONEdevice *pDevice, DOPprofile *pProfile, DOPtable *pTable) |
|||
{ |
|||
ONEnode *pNode; |
|||
ONEelem *pElem; |
|||
DOPprofile *pP; |
|||
double conc; |
|||
int index, eIndex; |
|||
BOOLEAN dopeMe; |
|||
|
|||
|
|||
/* Clear doping info for all nodes. */ |
|||
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) { |
|||
pElem = pDevice->elemArray[eIndex]; |
|||
for (index = 0; index <= 1; index++) { |
|||
if (pElem->evalNodes[index]) { |
|||
pNode = pElem->pNodes[index]; |
|||
pNode->na = 0.0; |
|||
pNode->nd = 0.0; |
|||
pNode->netConc = 0.0; |
|||
pNode->totalConc = 0.0; |
|||
} |
|||
} |
|||
} |
|||
/* Now compute the contribution to the total doping from each profile. */ |
|||
for (pP = pProfile; pP != NIL(DOPprofile); pP = pP->next) { |
|||
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) { |
|||
pElem = pDevice->elemArray[eIndex]; |
|||
if (pElem->elemType == SEMICON) { |
|||
if (pP->numDomains > 0) { |
|||
dopeMe = FALSE; |
|||
for (index = 0; index < pP->numDomains; index++) { |
|||
if (pElem->domain == pP->domains[index]) { |
|||
dopeMe = TRUE; |
|||
break; |
|||
} |
|||
} |
|||
} else { /* domains not given, so dope all */ |
|||
dopeMe = TRUE; |
|||
} |
|||
if (dopeMe) { |
|||
for (index = 0; index <= 1; index++) { |
|||
if (pElem->evalNodes[index]) { |
|||
pNode = pElem->pNodes[index]; |
|||
conc = ONEdopingValue(pP, pTable, pNode->x); |
|||
pNode->netConc += conc; |
|||
if (conc < 0.0) { |
|||
pNode->totalConc -= conc; |
|||
pNode->na -= conc; |
|||
} else { |
|||
pNode->totalConc += conc; |
|||
pNode->nd += conc; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,121 @@ |
|||
/********** |
|||
Copyright 1992 Regents of the University of California. All rights reserved. |
|||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numglobs.h" |
|||
#include "numconst.h" |
|||
#include "onemesh.h" |
|||
#include "../../maths/misc/accuracy.h" |
|||
#include "onedext.h" |
|||
#include "oneddefs.h" |
|||
|
|||
|
|||
void |
|||
ONEQfreezeOut(ONEnode *pNode, double *ndFac, double *naFac, double *dNdFac, |
|||
double *dNaFac) |
|||
{ |
|||
double temp1, temp2; |
|||
double eLev; |
|||
ONEmaterial *info; |
|||
|
|||
|
|||
if (pNode->pRightElem && pNode->pRightElem->evalNodes[0]) { |
|||
info = pNode->pRightElem->matlInfo; |
|||
} else { |
|||
info = pNode->pLeftElem->matlInfo; |
|||
} |
|||
|
|||
eLev = info->eDon; |
|||
if (info->material != GAAS) { |
|||
eLev -= LEVEL_ALPHA_SI * pow(pNode->nd * NNorm, 1.0 / 3.0); |
|||
if (eLev < 0.0) |
|||
eLev = 0.0; |
|||
} |
|||
if (eLev >= ExpLim) { |
|||
*ndFac = 0.0; |
|||
*dNdFac = 0.0; |
|||
} else if (eLev <= -ExpLim) { |
|||
*ndFac = 1.0; |
|||
*dNdFac = 0.0; |
|||
} else { |
|||
temp1 = info->gDon * pNode->nConc * NNorm * exp(eLev) / info->nc0; |
|||
temp2 = 1.0 / (1.0 + temp1); |
|||
*ndFac = temp2; |
|||
*dNdFac = -temp2 * temp2 * temp1; |
|||
} |
|||
|
|||
eLev = info->eAcc; |
|||
if (info->material != GAAS) { |
|||
eLev -= LEVEL_ALPHA_SI * pow(pNode->na * NNorm, 1.0 / 3.0); |
|||
if (eLev < 0.0) |
|||
eLev = 0.0; |
|||
} |
|||
if (eLev >= ExpLim) { |
|||
*naFac = 0.0; |
|||
*dNaFac = 0.0; |
|||
} else if (eLev <= -ExpLim) { |
|||
*naFac = 1.0; |
|||
*dNaFac = 0.0; |
|||
} else { |
|||
temp1 = info->gAcc * pNode->pConc * NNorm * exp(eLev) / info->nv0; |
|||
temp2 = 1.0 / (1.0 + temp1); |
|||
*naFac = temp2; |
|||
*dNaFac = temp2 * temp2 * temp1; |
|||
} |
|||
} |
|||
|
|||
void |
|||
ONE_freezeOut(ONEnode *pNode, double nConc, double pConc, double *ndFac, |
|||
double *naFac, double *dNdFac, double *dNaFac) |
|||
{ |
|||
double temp1, temp2; |
|||
double eLev; |
|||
ONEmaterial *info; |
|||
|
|||
|
|||
if (pNode->pRightElem && pNode->pRightElem->evalNodes[0]) { |
|||
info = pNode->pRightElem->matlInfo; |
|||
} else { |
|||
info = pNode->pLeftElem->matlInfo; |
|||
} |
|||
|
|||
eLev = info->eDon; |
|||
if (info->material != GAAS) { |
|||
eLev -= LEVEL_ALPHA_SI * pow(pNode->nd * NNorm, 1.0 / 3.0); |
|||
if (eLev < 0.0) |
|||
eLev = 0.0; |
|||
} |
|||
if (eLev >= ExpLim) { |
|||
*ndFac = 0.0; |
|||
*dNdFac = 0.0; |
|||
} else if (eLev <= -ExpLim) { |
|||
*ndFac = 1.0; |
|||
*dNdFac = 0.0; |
|||
} else { |
|||
temp1 = info->gDon * exp(eLev) * NNorm / info->nc0; |
|||
temp2 = 1.0 / (1.0 + nConc * temp1); |
|||
*ndFac = temp2; |
|||
*dNdFac = -temp2 * temp2 * temp1; |
|||
} |
|||
|
|||
eLev = info->eAcc; |
|||
if (info->material != GAAS) { |
|||
eLev -= LEVEL_ALPHA_SI * pow(pNode->na * NNorm, 1.0 / 3.0); |
|||
if (eLev < 0.0) |
|||
eLev = 0.0; |
|||
} |
|||
if (eLev >= ExpLim) { |
|||
*naFac = 0.0; |
|||
*dNaFac = 0.0; |
|||
} else if (eLev <= -ExpLim) { |
|||
*naFac = 1.0; |
|||
*dNaFac = 0.0; |
|||
} else { |
|||
temp1 = info->gAcc * exp(eLev) * NNorm / info->nv0; |
|||
temp2 = 1.0 / (1.0 + pConc * temp1); |
|||
*naFac = temp2; |
|||
*dNaFac = -temp2 * temp2 * temp1; |
|||
} |
|||
} |
|||
@ -0,0 +1,371 @@ |
|||
/********** |
|||
Copyright 1992 Regents of the University of California. All rights reserved. |
|||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numglobs.h" |
|||
#include "numenum.h" |
|||
#include "onemesh.h" |
|||
#include "onedev.h" |
|||
#include "macros.h" |
|||
#include "onedext.h" |
|||
#include "oneddefs.h" |
|||
|
|||
|
|||
/* Forward Declarations */ |
|||
static void ONEresetEvalFlag(ONEdevice *); |
|||
|
|||
void |
|||
ONEbuildMesh(ONEdevice *pDevice, ONEcoord *pCoord, ONEdomain *pDomain, |
|||
ONEmaterial *pMaterial) |
|||
{ |
|||
int index, i; |
|||
int elemType; |
|||
double xPos; |
|||
ONEcoord *pC; |
|||
ONEnode *pNode, *pNextNode; |
|||
ONEdomain *pD; |
|||
ONEelem *pElem; |
|||
ONEmaterial *pM; |
|||
int poiEqn, numEqn; |
|||
ONEedge *pEdge; |
|||
ONEnode **nodeArray=NULL; |
|||
BOOLEAN error = FALSE; |
|||
|
|||
|
|||
/* generate the work array for setting up nodes and elements */ |
|||
XCALLOC(nodeArray, ONEnode *, 1 + pDevice->numNodes); |
|||
|
|||
for (pC = pCoord; pC != NIL(ONEcoord); pC = pC->next) { |
|||
xPos = pC->location; |
|||
XCALLOC(pNode, ONEnode, 1); |
|||
pNode->x = xPos; |
|||
pNode->nodeI = pC->number; |
|||
nodeArray[pNode->nodeI] = pNode; |
|||
} |
|||
|
|||
/* mark the domain info on the nodes */ |
|||
if (pDomain == NIL(ONEdomain)) { |
|||
fprintf(stderr, "Error: domains not defined for device\n"); |
|||
exit(-1); |
|||
} |
|||
for (pD = pDomain; pD != NIL(ONEdomain); pD = pD->next) { |
|||
for (pM = pMaterial; pM != NIL(ONEmaterial); pM = pM->next) { |
|||
if (pD->material == pM->id) { |
|||
break; |
|||
} |
|||
} |
|||
elemType = pM->type; |
|||
for (index = pD->ixLo; index <= pD->ixHi; index++) { |
|||
pNode = nodeArray[index]; |
|||
if (!pNode->nodeType) { |
|||
pNode->nodeType = elemType; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* |
|||
* check to see if a domain has been defined for all nodes. if not flag an |
|||
* error message |
|||
*/ |
|||
for (index = 2; index < pDevice->numNodes; index++) { |
|||
pNode = nodeArray[index]; |
|||
if (!pNode->nodeType) { |
|||
printf("Error: No domain defined for node %d\n", pNode->nodeI); |
|||
error = TRUE; |
|||
} |
|||
} |
|||
if (error) { |
|||
/* nodes with undefined domains -- exit */ |
|||
exit(-1); |
|||
} |
|||
/* mark the first and last nodes to be contact nodes */ |
|||
nodeArray[1]->nodeType = CONTACT; |
|||
nodeArray[pDevice->numNodes]->nodeType = CONTACT; |
|||
|
|||
|
|||
/* generate the elements and the edges */ |
|||
for (index = 1; index < pDevice->numNodes; index++) { |
|||
XCALLOC(pElem, ONEelem, 1); |
|||
XCALLOC(pEdge, ONEedge, 1); |
|||
pElem->pEdge = pEdge; |
|||
pElem->pLeftNode = nodeArray[index]; |
|||
pElem->pRightNode = nodeArray[index + 1]; |
|||
pDevice->elemArray[index] = pElem; |
|||
} |
|||
|
|||
/* now setup the nodes to which an element belongs */ |
|||
for (index = 1; index < pDevice->numNodes; index++) { |
|||
pElem = pDevice->elemArray[index]; |
|||
pElem->pLeftNode->pRightElem = pElem; |
|||
pElem->pRightNode->pLeftElem = pElem; |
|||
if (index > 1) { |
|||
pElem->pLeftElem = pDevice->elemArray[index - 1]; |
|||
} |
|||
if (index < pDevice->numNodes - 1) { |
|||
pElem->pRightElem = pDevice->elemArray[index + 1]; |
|||
} |
|||
} |
|||
|
|||
/* mark the domain info on the elements */ |
|||
for (pD = pDomain; pD != NIL(ONEdomain); pD = pD->next) { |
|||
for (pM = pMaterial; pM != NIL(ONEmaterial); pM = pM->next) { |
|||
if (pD->material == pM->id) { |
|||
break; |
|||
} |
|||
} |
|||
elemType = pM->type; |
|||
for (index = pD->ixLo; index < pD->ixHi; index++) { |
|||
pElem = pDevice->elemArray[index]; |
|||
pElem->domain = pD->id; |
|||
pElem->elemType = elemType; |
|||
pElem->matlInfo = pM; |
|||
} |
|||
} |
|||
|
|||
/* identify the interface nodes */ |
|||
for (index = 2; index < pDevice->numNodes; index++) { |
|||
pNode = nodeArray[index]; |
|||
if (pNode->pLeftElem->elemType != |
|||
pNode->pRightElem->elemType) { |
|||
/* an interface node */ |
|||
pNode->nodeType = INTERFACE; |
|||
} |
|||
} |
|||
|
|||
/* now mark the nodes to be evaluated */ |
|||
/* all interface nodes marked in silicon elements */ |
|||
|
|||
for (index = 1; index < pDevice->numNodes; index++) { |
|||
pElem = pDevice->elemArray[index]; |
|||
pElem->dx = pElem->pRightNode->x - pElem->pLeftNode->x; |
|||
for (i = 0; i <= 1; i++) { |
|||
pNode = pElem->pNodes[i]; |
|||
pElem->evalNodes[i] = FALSE; |
|||
if (pElem->elemType == INSULATOR) { |
|||
if ((!pNode->evaluated) && |
|||
(pNode->nodeType != INTERFACE)) { |
|||
/* a non interface node in oxide domain */ |
|||
pNode->evaluated = TRUE; |
|||
pElem->evalNodes[i] = TRUE; |
|||
} |
|||
} |
|||
if (pElem->elemType == SEMICON) { |
|||
if (!pNode->evaluated) { |
|||
pNode->evaluated = TRUE; |
|||
pElem->evalNodes[i] = TRUE; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* set up the equation number for the nodes */ |
|||
poiEqn = numEqn = 1; |
|||
for (index = 1; index < pDevice->numNodes; index++) { |
|||
pElem = pDevice->elemArray[index]; |
|||
for (i = 0; i <= 1; i++) { |
|||
if (pElem->evalNodes[i]) { |
|||
pNode = pElem->pNodes[i]; |
|||
if (pNode->nodeType != CONTACT) { |
|||
pNode->poiEqn = poiEqn++; |
|||
|
|||
pNode->psiEqn = numEqn; |
|||
if (pElem->elemType == INSULATOR) { |
|||
numEqn += 1; /* only poisson's equation */ |
|||
} else { |
|||
pNode->nEqn = numEqn + 1; |
|||
pNode->pEqn = numEqn + 2; |
|||
numEqn += 3; |
|||
} |
|||
} else { /* this is a contact node */ |
|||
pNode->poiEqn = 0; |
|||
pNode->psiEqn = 0; |
|||
pNode->nEqn = 0; |
|||
pNode->pEqn = 0; |
|||
} |
|||
/* |
|||
* fprintf(stdout,"NODE: %d %d\n",pNode->nodeI,pNode->poiEqn); |
|||
*/ |
|||
} |
|||
} |
|||
} |
|||
pDevice->dimEquil = poiEqn; |
|||
pDevice->dimBias = numEqn; |
|||
|
|||
/* |
|||
* ONEprnMesh( pDevice ); |
|||
*/ |
|||
} |
|||
|
|||
/* |
|||
* We have a separate function for this, so that the setup routines can reset |
|||
* the state pointers without rebuilding the entire mesh. |
|||
*/ |
|||
void |
|||
ONEgetStatePointers(ONEdevice *pDevice, int *numStates) |
|||
{ |
|||
int eIndex, nIndex; |
|||
ONEelem *pElem; |
|||
ONEnode *pNode; |
|||
|
|||
|
|||
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) { |
|||
pElem = pDevice->elemArray[eIndex]; |
|||
for (nIndex = 0; nIndex <= 1; nIndex++) { |
|||
if (pElem->evalNodes[nIndex]) { |
|||
pNode = pElem->pNodes[nIndex]; |
|||
pNode->nodeState = *numStates; |
|||
*numStates += ONEnumNodeStates; |
|||
} |
|||
} |
|||
pElem->pEdge->edgeState = *numStates; |
|||
*numStates += ONEnumEdgeStates; |
|||
} |
|||
} |
|||
|
|||
/* adjust the base contact to be the position of maximum density */ |
|||
/* index EB and BC are the indexes for the eb, bc junctions */ |
|||
void |
|||
adjustBaseContact(ONEdevice *pDevice, int indexEB, int indexBC) |
|||
{ |
|||
int index, i, newBaseIndex, midPoint; |
|||
double maxDensity; |
|||
ONEnode *pNode; |
|||
ONEelem *pElem; |
|||
ONEnode *pBaseNode = pDevice->elemArray[pDevice->baseIndex]->pNodes[0]; |
|||
|
|||
|
|||
/* Initialize the base contact to be the center of the two junctions */ |
|||
/* This should take care of uniform dopings. */ |
|||
|
|||
midPoint = 0.5 * (indexEB + indexBC); |
|||
newBaseIndex = midPoint; |
|||
|
|||
if (pBaseNode->baseType == P_TYPE) { |
|||
maxDensity = pDevice->elemArray[midPoint]->pNodes[0]->pConc; |
|||
for (index = indexEB; index < indexBC; index++) { |
|||
pElem = pDevice->elemArray[index]; |
|||
for (i = 0; i <= 1; i++) { |
|||
pNode = pElem->pNodes[i]; |
|||
if (pNode->pConc > maxDensity) { |
|||
maxDensity = pNode->pConc; |
|||
newBaseIndex = index; |
|||
} |
|||
} |
|||
} |
|||
} else if (pBaseNode->baseType == N_TYPE) { |
|||
maxDensity = pDevice->elemArray[midPoint]->pNodes[0]->nConc; |
|||
for (index = indexEB; index < indexBC; index++) { |
|||
pElem = pDevice->elemArray[index]; |
|||
for (i = 0; i <= 1; i++) { |
|||
pNode = pElem->pNodes[i]; |
|||
if (pNode->nConc > maxDensity) { |
|||
maxDensity = pNode->nConc; |
|||
newBaseIndex = index; |
|||
} |
|||
} |
|||
} |
|||
} else { |
|||
printf("adjustBaseContact: unknown base type %d\n", pBaseNode->baseType); |
|||
} |
|||
/* at the conclusion of this loop have the point of max density */ |
|||
if (pDevice->baseIndex != newBaseIndex) { |
|||
/* so change the position */ |
|||
pNode = pDevice->elemArray[newBaseIndex]->pNodes[0]; |
|||
pNode->baseType = pBaseNode->baseType; |
|||
pNode->vbe = pBaseNode->vbe; |
|||
pBaseNode->baseType = FALSE; |
|||
pBaseNode->vbe = 0.0; |
|||
pDevice->baseIndex = newBaseIndex; |
|||
} |
|||
} |
|||
|
|||
void |
|||
NBJTjunctions(ONEdevice *pDevice, int *indexEB, int *indexBC) |
|||
{ |
|||
int index; |
|||
double conc1, conc2; |
|||
BOOLEAN findFirstJunction = TRUE; |
|||
BOOLEAN notFound = TRUE; |
|||
|
|||
for (index = 1; (index < pDevice->numNodes) && (notFound); index++) { |
|||
conc1 = pDevice->elemArray[index]->pNodes[0]->netConc; |
|||
conc2 = pDevice->elemArray[index]->pNodes[1]->netConc; |
|||
|
|||
if ((conc1 * conc2 < 0.0) && (findFirstJunction)) { |
|||
*indexEB = index; |
|||
findFirstJunction = FALSE; |
|||
} else if ((conc1 * conc2 < 0.0) && (!findFirstJunction)) { |
|||
*indexBC = index; |
|||
notFound = FALSE; |
|||
} |
|||
} |
|||
|
|||
if (notFound) { |
|||
fprintf(stderr, "BJT: Device does not have two junctions!\n"); |
|||
exit(-1); |
|||
} |
|||
} |
|||
|
|||
void |
|||
ONEprnMesh(ONEdevice *pDevice) |
|||
{ |
|||
int eIndex, index; |
|||
ONEelem *pElem; |
|||
ONEnode *pNode; |
|||
ONEedge *pEdge; |
|||
char *name; |
|||
|
|||
|
|||
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) { |
|||
pElem = pDevice->elemArray[eIndex]; |
|||
fprintf(stderr, "elem %5d:\n", eIndex); |
|||
for (index = 0; index <= 1; index++) { |
|||
if (pElem->evalNodes[index]) { |
|||
pNode = pElem->pNodes[index]; |
|||
switch (pNode->nodeType) { |
|||
case SEMICON: |
|||
name = "semiconductor"; |
|||
break; |
|||
case INSULATOR: |
|||
name = "insulator"; |
|||
break; |
|||
case CONTACT: |
|||
name = "contact"; |
|||
break; |
|||
case SCHOTTKY: |
|||
name = "schottky"; |
|||
break; |
|||
case INTERFACE: |
|||
name = "interface"; |
|||
break; |
|||
default: |
|||
name = "unknown"; |
|||
break; |
|||
} |
|||
|
|||
|
|||
fprintf(stderr, "node %5d: %s %5d\n", index, name, |
|||
pNode->nodeI); |
|||
|
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
static void |
|||
ONEresetEvalFlag(ONEdevice *pDevice) |
|||
{ |
|||
int index, eIndex; |
|||
ONEelem *pElem; |
|||
|
|||
|
|||
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) { |
|||
pElem = pDevice->elemArray[eIndex]; |
|||
for (index = 0; index <= 1; index++) { |
|||
pElem->pNodes[index]->evaluated = FALSE; |
|||
} |
|||
pElem->pEdge->evaluated = FALSE; |
|||
} |
|||
} |
|||
@ -0,0 +1,192 @@ |
|||
/********** |
|||
Copyright 1992 Regents of the University of California. All rights reserved. |
|||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numglobs.h" |
|||
#include "numenum.h" |
|||
#include "onemesh.h" |
|||
#include "onedev.h" |
|||
#include "onedext.h" |
|||
#include "oneddefs.h" |
|||
#include "spMatrix.h" |
|||
|
|||
/* Functions to setup and solve the 1D poisson equation. */ |
|||
|
|||
|
|||
/* Forward Declarations */ |
|||
void ONEQcommonTerms(ONEdevice *pDevice); |
|||
|
|||
void |
|||
ONEQjacBuild(ONEdevice *pDevice) |
|||
{ |
|||
char *matrix = pDevice->matrix; |
|||
ONEelem *pElem; |
|||
ONEnode *pNode, *pNode1; |
|||
int index; |
|||
|
|||
|
|||
for (index = 1; index < pDevice->numNodes; index++) { |
|||
pElem = pDevice->elemArray[index]; |
|||
pNode = pElem->pLeftNode; |
|||
pNode->fPsiPsi = spGetElement(matrix, pNode->poiEqn, pNode->poiEqn); |
|||
pNode1 = pElem->pRightNode; |
|||
pNode->fPsiPsiiP1 = spGetElement(matrix, pNode->poiEqn, pNode1->poiEqn); |
|||
pNode = pElem->pRightNode; |
|||
pNode->fPsiPsi = spGetElement(matrix, pNode->poiEqn, pNode->poiEqn); |
|||
pNode1 = pElem->pLeftNode; |
|||
pNode->fPsiPsiiM1 = spGetElement(matrix, pNode->poiEqn, pNode1->poiEqn); |
|||
} |
|||
} |
|||
|
|||
|
|||
void |
|||
ONEQsysLoad(ONEdevice *pDevice) |
|||
{ |
|||
ONEelem *pElem; |
|||
ONEnode *pNode, *pNode1; |
|||
int index, i; |
|||
double *pRhs = pDevice->rhs; |
|||
double rDx, dPsi; |
|||
double netConc, dNetConc; |
|||
double fNd, fdNd, fNa, fdNa; |
|||
|
|||
|
|||
ONEQcommonTerms(pDevice); |
|||
|
|||
/* zero the rhs vector */ |
|||
for (index = 1; index <= pDevice->numEqns; index++) { |
|||
pRhs[index] = 0.0; |
|||
} |
|||
|
|||
/* zero the matrix */ |
|||
spClear(pDevice->matrix); |
|||
|
|||
for (index = 1; index < pDevice->numNodes; index++) { |
|||
pElem = pDevice->elemArray[index]; |
|||
rDx = pElem->epsRel * pElem->rDx; |
|||
for (i = 0; i <= 1; i++) { |
|||
pNode = pElem->pNodes[i]; |
|||
if (pNode->nodeType != CONTACT) { |
|||
*(pNode->fPsiPsi) += rDx; |
|||
pRhs[pNode->poiEqn] += pNode->qf; |
|||
if (pElem->elemType == SEMICON) { |
|||
netConc = pNode->netConc; |
|||
dNetConc = 0.0; |
|||
if (FreezeOut) { |
|||
ONEQfreezeOut(pNode, &fNd, &fNa, &fdNd, &fdNa); |
|||
netConc = pNode->nd * fNd - pNode->na * fNa; |
|||
dNetConc = pNode->nd * fdNd - pNode->na * fdNa; |
|||
} |
|||
*(pNode->fPsiPsi) += 0.5 * pElem->dx * |
|||
(pNode->nConc + pNode->pConc - dNetConc); |
|||
pRhs[pNode->poiEqn] += 0.5 * pElem->dx * |
|||
(netConc + pNode->pConc - pNode->nConc); |
|||
} |
|||
} |
|||
} |
|||
|
|||
dPsi = pElem->pEdge->dPsi; |
|||
|
|||
pNode = pElem->pLeftNode; |
|||
pRhs[pNode->poiEqn] += rDx * dPsi; |
|||
*(pNode->fPsiPsiiP1) -= rDx; |
|||
|
|||
pNode = pElem->pRightNode; |
|||
pRhs[pNode->poiEqn] -= rDx * dPsi; |
|||
*(pNode->fPsiPsiiM1) -= rDx; |
|||
} |
|||
} |
|||
|
|||
|
|||
void |
|||
ONEQrhsLoad(ONEdevice *pDevice) |
|||
{ |
|||
ONEelem *pElem; |
|||
ONEnode *pNode; |
|||
int index, i; |
|||
double *pRhs = pDevice->rhs; |
|||
double rDx, dPsi; |
|||
double fNd, fNa, fdNd, fdNa; |
|||
double netConc; |
|||
|
|||
|
|||
ONEQcommonTerms(pDevice); |
|||
|
|||
/* zero the rhs vector */ |
|||
for (index = 1; index <= pDevice->numEqns; index++) { |
|||
pRhs[index] = 0.0; |
|||
} |
|||
|
|||
for (index = 1; index < pDevice->numNodes; index++) { |
|||
pElem = pDevice->elemArray[index]; |
|||
rDx = pElem->epsRel * pElem->rDx; |
|||
for (i = 0; i <= 1; i++) { |
|||
pNode = pElem->pNodes[i]; |
|||
if (pNode->nodeType != CONTACT) { |
|||
pRhs[pNode->poiEqn] += pNode->qf; |
|||
if (pElem->elemType == SEMICON) { |
|||
netConc = pNode->netConc; |
|||
if (FreezeOut) { |
|||
ONEQfreezeOut(pNode, &fNd, &fNa, &fdNd, &fdNa); |
|||
netConc = pNode->nd * fNd - pNode->na * fNa; |
|||
} |
|||
pRhs[pNode->poiEqn] += 0.5 * pElem->dx * |
|||
(netConc + pNode->pConc - pNode->nConc); |
|||
} |
|||
} |
|||
} |
|||
|
|||
dPsi = pElem->pEdge->dPsi; |
|||
|
|||
pNode = pElem->pLeftNode; |
|||
pRhs[pNode->poiEqn] += rDx * dPsi; |
|||
|
|||
pNode = pElem->pRightNode; |
|||
pRhs[pNode->poiEqn] -= rDx * dPsi; |
|||
} |
|||
} |
|||
|
|||
|
|||
void |
|||
ONEQcommonTerms(ONEdevice *pDevice) |
|||
{ |
|||
ONEelem *pElem; |
|||
ONEedge *pEdge; |
|||
ONEnode *pNode, *pNode1; |
|||
int i, index; |
|||
double psi1, psi2, refPsi; |
|||
|
|||
|
|||
for (index = 1; index < pDevice->numNodes; index++) { |
|||
pElem = pDevice->elemArray[index]; |
|||
refPsi = pElem->matlInfo->refPsi; |
|||
for (i = 0; i <= 1; i++) { |
|||
if (pElem->evalNodes[i]) { |
|||
pNode = pElem->pNodes[i]; |
|||
if (pNode->nodeType != CONTACT) { |
|||
pNode->psi = pDevice->dcSolution[pNode->poiEqn]; |
|||
if (pElem->elemType == SEMICON) { |
|||
pNode->nConc = pNode->nie * exp(pNode->psi - refPsi); |
|||
pNode->pConc = pNode->nie * exp(-pNode->psi + refPsi); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
pEdge = pElem->pEdge; |
|||
pNode = pElem->pNodes[0]; |
|||
if (pNode->nodeType != CONTACT) { |
|||
psi1 = pDevice->dcSolution[pNode->poiEqn]; |
|||
} else { |
|||
psi1 = pNode->psi; |
|||
} |
|||
pNode = pElem->pNodes[1]; |
|||
if (pNode->nodeType != CONTACT) { |
|||
psi2 = pDevice->dcSolution[pNode->poiEqn]; |
|||
} else { |
|||
psi2 = pNode->psi; |
|||
} |
|||
pEdge->dPsi = psi2 - psi1; |
|||
} |
|||
} |
|||
@ -0,0 +1,534 @@ |
|||
/********** |
|||
Copyright 1992 Regents of the University of California. All rights reserved. |
|||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group |
|||
Author: 1992 David A. Gates, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numglobs.h" |
|||
#include "numconst.h" |
|||
#include "numenum.h" |
|||
#include "onemesh.h" |
|||
#include "onedev.h" |
|||
#include "carddefs.h" |
|||
#include "spMatrix.h" |
|||
#include "onedext.h" |
|||
#include "oneddefs.h" |
|||
|
|||
void |
|||
ONEprnSolution(FILE *file, ONEdevice *pDevice, OUTPcard *output) |
|||
{ |
|||
int index, i; |
|||
int numVars = 0; |
|||
ONEnode **nodeArray=NULL; |
|||
ONEnode *pNode; |
|||
ONEelem *pElem, *pPrevElem; |
|||
ONEmaterial *info=NULL; |
|||
double data[50]; |
|||
double eField, refPsi = 0.0, eGap, dGap; |
|||
double mun, mup, jc, jd, jn, jp, jt; |
|||
double coeff1, coeff2; |
|||
|
|||
|
|||
if (output->OUTPnumVars == -1) { |
|||
/* First pass. Need to count number of variables in output. */ |
|||
numVars++; /* For the X scale */ |
|||
if (output->OUTPdoping) { |
|||
numVars++; |
|||
} |
|||
if (output->OUTPpsi) { |
|||
numVars++; |
|||
} |
|||
if (output->OUTPequPsi) { |
|||
numVars++; |
|||
} |
|||
if (output->OUTPvacPsi) { |
|||
numVars++; |
|||
} |
|||
if (output->OUTPnConc) { |
|||
numVars++; |
|||
} |
|||
if (output->OUTPpConc) { |
|||
numVars++; |
|||
} |
|||
if (output->OUTPphin) { |
|||
numVars++; |
|||
} |
|||
if (output->OUTPphip) { |
|||
numVars++; |
|||
} |
|||
if (output->OUTPphic) { |
|||
numVars++; |
|||
} |
|||
if (output->OUTPphiv) { |
|||
numVars++; |
|||
} |
|||
if (output->OUTPeField) { |
|||
numVars++; |
|||
} |
|||
if (output->OUTPjc) { |
|||
numVars++; |
|||
} |
|||
if (output->OUTPjd) { |
|||
numVars++; |
|||
} |
|||
if (output->OUTPjn) { |
|||
numVars++; |
|||
} |
|||
if (output->OUTPjp) { |
|||
numVars++; |
|||
} |
|||
if (output->OUTPjt) { |
|||
numVars++; |
|||
} |
|||
if (output->OUTPuNet) { |
|||
numVars++; |
|||
} |
|||
if (output->OUTPmun) { |
|||
numVars++; |
|||
} |
|||
if (output->OUTPmup) { |
|||
numVars++; |
|||
} |
|||
output->OUTPnumVars = numVars; |
|||
} |
|||
/* generate the work array for printing node info */ |
|||
XCALLOC(nodeArray, ONEnode *, 1 + pDevice->numNodes); |
|||
|
|||
/* store the nodes in this work array and print out later */ |
|||
for (index = 1; index < pDevice->numNodes; index++) { |
|||
pElem = pDevice->elemArray[index]; |
|||
if (refPsi == 0.0 && pElem->matlInfo->type == SEMICON) { |
|||
refPsi = pElem->matlInfo->refPsi; |
|||
} |
|||
for (i = 0; i <= 1; i++) { |
|||
if (pElem->evalNodes[i]) { |
|||
pNode = pElem->pNodes[i]; |
|||
nodeArray[pNode->nodeI] = pNode; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Initialize rawfile */ |
|||
numVars = output->OUTPnumVars; |
|||
fprintf(file, "Title: Device %s internal state\n", pDevice->name); |
|||
fprintf(file, "Plotname: Device Cross Section\n"); |
|||
fprintf(file, "Flags: real\n"); |
|||
fprintf(file, "Command: deftype p xs cross\n"); |
|||
fprintf(file, "Command: deftype v distance m\n"); |
|||
fprintf(file, "Command: deftype v concentration cm^-3\n"); |
|||
fprintf(file, "Command: deftype v electric_field V/cm\n"); |
|||
fprintf(file, "Command: deftype v current_density A/cm^2\n"); |
|||
fprintf(file, "Command: deftype v concentration/time cm^-3/s\n"); |
|||
fprintf(file, "Command: deftype v mobility cm^2/Vs\n"); |
|||
fprintf(file, "No. Variables: %d\n", numVars); |
|||
fprintf(file, "No. Points: %d\n", pDevice->numNodes); |
|||
numVars = 0; |
|||
fprintf(file, "Variables:\n"); |
|||
fprintf(file, "\t%d x distance\n", numVars++); |
|||
if (output->OUTPpsi) { |
|||
fprintf(file, "\t%d psi voltage\n", numVars++); |
|||
} |
|||
if (output->OUTPequPsi) { |
|||
fprintf(file, "\t%d equ.psi voltage\n", numVars++); |
|||
} |
|||
if (output->OUTPvacPsi) { |
|||
fprintf(file, "\t%d vac.psi voltage\n", numVars++); |
|||
} |
|||
if (output->OUTPphin) { |
|||
fprintf(file, "\t%d phin voltage\n", numVars++); |
|||
} |
|||
if (output->OUTPphip) { |
|||
fprintf(file, "\t%d phip voltage\n", numVars++); |
|||
} |
|||
if (output->OUTPphic) { |
|||
fprintf(file, "\t%d phic voltage\n", numVars++); |
|||
} |
|||
if (output->OUTPphiv) { |
|||
fprintf(file, "\t%d phiv voltage\n", numVars++); |
|||
} |
|||
if (output->OUTPdoping) { |
|||
fprintf(file, "\t%d dop concentration\n", numVars++); |
|||
} |
|||
if (output->OUTPnConc) { |
|||
fprintf(file, "\t%d n concentration\n", numVars++); |
|||
} |
|||
if (output->OUTPpConc) { |
|||
fprintf(file, "\t%d p concentration\n", numVars++); |
|||
} |
|||
if (output->OUTPeField) { |
|||
fprintf(file, "\t%d e electric_field\n", numVars++); |
|||
} |
|||
if (output->OUTPjc) { |
|||
fprintf(file, "\t%d jc current_density\n", numVars++); |
|||
} |
|||
if (output->OUTPjd) { |
|||
fprintf(file, "\t%d jd current_density\n", numVars++); |
|||
} |
|||
if (output->OUTPjn) { |
|||
fprintf(file, "\t%d jn current_density\n", numVars++); |
|||
} |
|||
if (output->OUTPjp) { |
|||
fprintf(file, "\t%d jp current_density\n", numVars++); |
|||
} |
|||
if (output->OUTPjt) { |
|||
fprintf(file, "\t%d jt current_density\n", numVars++); |
|||
} |
|||
if (output->OUTPuNet) { |
|||
fprintf(file, "\t%d unet concentration/time\n", numVars++); |
|||
} |
|||
if (output->OUTPmun) { |
|||
fprintf(file, "\t%d mun mobility\n", numVars++); |
|||
} |
|||
if (output->OUTPmup) { |
|||
fprintf(file, "\t%d mup mobility\n", numVars++); |
|||
} |
|||
fprintf(file, "Binary:\n"); |
|||
|
|||
for (index = 1; index <= pDevice->numNodes; index++) { |
|||
pNode = nodeArray[index]; |
|||
if ((index > 1) && (index < pDevice->numNodes)) { |
|||
pElem = pNode->pRightElem; |
|||
pPrevElem = pNode->pLeftElem; |
|||
if (pElem->evalNodes[0]) { |
|||
info = pElem->matlInfo; |
|||
} else if (pPrevElem->evalNodes[1]) { |
|||
info = pPrevElem->matlInfo; |
|||
} |
|||
coeff1 = pPrevElem->dx / (pPrevElem->dx + pElem->dx); |
|||
coeff2 = pElem->dx / (pPrevElem->dx + pElem->dx); |
|||
eField = -coeff1 * pElem->pEdge->dPsi * pElem->rDx |
|||
- coeff2 * pPrevElem->pEdge->dPsi * pPrevElem->rDx; |
|||
mun = coeff1 * pElem->pEdge->mun + coeff2 * pPrevElem->pEdge->mun; |
|||
mup = coeff1 * pElem->pEdge->mup + coeff2 * pPrevElem->pEdge->mup; |
|||
jn = coeff1 * pElem->pEdge->jn + coeff2 * pPrevElem->pEdge->jn; |
|||
jp = coeff1 * pElem->pEdge->jp + coeff2 * pPrevElem->pEdge->jp; |
|||
jd = coeff1 * pElem->pEdge->jd + coeff2 * pPrevElem->pEdge->jd; |
|||
} else if (index == 1) { |
|||
info = pNode->pRightElem->matlInfo; |
|||
eField = 0.0; |
|||
mun = pNode->pRightElem->pEdge->mun; |
|||
mup = pNode->pRightElem->pEdge->mup; |
|||
jn = pNode->pRightElem->pEdge->jn; |
|||
jp = pNode->pRightElem->pEdge->jp; |
|||
jd = pNode->pRightElem->pEdge->jd; |
|||
} else { |
|||
info = pNode->pLeftElem->matlInfo; |
|||
eField = 0.0; |
|||
mun = pNode->pLeftElem->pEdge->mun; |
|||
mup = pNode->pLeftElem->pEdge->mup; |
|||
jn = pNode->pLeftElem->pEdge->jn; |
|||
jp = pNode->pLeftElem->pEdge->jp; |
|||
jd = pNode->pLeftElem->pEdge->jd; |
|||
} |
|||
jc = jn + jp; |
|||
jt = jc + jd; |
|||
/* Crude hack to get around the fact that the base node wipes out 'eg' */ |
|||
if (index == pDevice->baseIndex) { |
|||
eGap = info->eg0; |
|||
dGap = 0.0; |
|||
} else { |
|||
eGap = pNode->eg * VNorm; |
|||
dGap = 0.5 * (info->eg0 - eGap); |
|||
} |
|||
|
|||
/* Now fill in the data array */ |
|||
numVars = 0; |
|||
data[numVars++] = pNode->x * 1e-2; |
|||
if (output->OUTPpsi) { |
|||
data[numVars++] = (pNode->psi - refPsi) * VNorm; |
|||
} |
|||
if (output->OUTPequPsi) { |
|||
data[numVars++] = (pNode->psi0 - refPsi) * VNorm; |
|||
} |
|||
if (output->OUTPvacPsi) { |
|||
data[numVars++] = pNode->psi * VNorm; |
|||
} |
|||
if (output->OUTPphin) { |
|||
if (info->type != INSULATOR) { |
|||
data[numVars++] = (pNode->psi - refPsi - log(pNode->nConc / pNode->nie)) |
|||
* VNorm; |
|||
} else { |
|||
data[numVars++] = 0.0; |
|||
} |
|||
} |
|||
if (output->OUTPphip) { |
|||
if (info->type != INSULATOR) { |
|||
data[numVars++] = (pNode->psi - refPsi + log(pNode->pConc / pNode->nie)) |
|||
* VNorm; |
|||
} else { |
|||
data[numVars++] = 0.0; |
|||
} |
|||
} |
|||
if (output->OUTPphic) { |
|||
data[numVars++] = (pNode->psi + pNode->eaff) * VNorm + dGap; |
|||
} |
|||
if (output->OUTPphiv) { |
|||
data[numVars++] = (pNode->psi + pNode->eaff) * VNorm + dGap + eGap; |
|||
} |
|||
if (output->OUTPdoping) { |
|||
data[numVars++] = pNode->netConc * NNorm; |
|||
} |
|||
if (output->OUTPnConc) { |
|||
data[numVars++] = pNode->nConc * NNorm; |
|||
} |
|||
if (output->OUTPpConc) { |
|||
data[numVars++] = pNode->pConc * NNorm; |
|||
} |
|||
if (output->OUTPeField) { |
|||
data[numVars++] = eField * ENorm; |
|||
} |
|||
if (output->OUTPjc) { |
|||
data[numVars++] = jc * JNorm; |
|||
} |
|||
if (output->OUTPjd) { |
|||
data[numVars++] = jd * JNorm; |
|||
} |
|||
if (output->OUTPjn) { |
|||
data[numVars++] = jn * JNorm; |
|||
} |
|||
if (output->OUTPjp) { |
|||
data[numVars++] = jp * JNorm; |
|||
} |
|||
if (output->OUTPjt) { |
|||
data[numVars++] = jt * JNorm; |
|||
} |
|||
if (output->OUTPuNet) { |
|||
data[numVars++] = pNode->uNet * NNorm / TNorm; |
|||
} |
|||
if (output->OUTPmun) { |
|||
data[numVars++] = mun; |
|||
} |
|||
if (output->OUTPmup) { |
|||
data[numVars++] = mup; |
|||
} |
|||
fwrite((char *) data, sizeof(double), numVars, file); |
|||
} |
|||
FREE(nodeArray); |
|||
} |
|||
|
|||
/* |
|||
* XXX This is what the SPARSE element structure looks like. We can't take it |
|||
* from its definition because the include file redefines all sorts of |
|||
* things. Note that we are violating data encapsulation to find out the |
|||
* size of this thing. |
|||
*/ |
|||
struct MatrixElement { |
|||
spREAL Real; |
|||
spREAL Imag; |
|||
int Row; |
|||
int Col; |
|||
struct MatrixElement *NextInRow; |
|||
struct MatrixElement *NextInCol; |
|||
}; |
|||
|
|||
void |
|||
ONEmemStats(FILE *file, ONEdevice *pDevice) |
|||
{ |
|||
static char *memFormat = "%-20s%10d%10d\n"; |
|||
static char *sumFormat = "%20s %-10d\n"; |
|||
unsigned int size; |
|||
unsigned int memory; |
|||
ONEmaterial *pMaterial; |
|||
ONEcontact *pContact; |
|||
int numContactNodes; |
|||
|
|||
|
|||
fprintf(file, "----------------------------------------\n"); |
|||
fprintf(file, "Device %s Memory Usage:\n", pDevice->name); |
|||
fprintf(file, "Item Count Bytes\n"); |
|||
fprintf(file, "----------------------------------------\n"); |
|||
|
|||
size = 1; |
|||
memory = size * sizeof(ONEdevice); |
|||
fprintf(file, memFormat, "Device", size, memory); |
|||
size = pDevice->numNodes - 1; |
|||
memory = size * sizeof(ONEelem); |
|||
fprintf(file, memFormat, "Elements", size, memory); |
|||
size = pDevice->numNodes; |
|||
memory = size * sizeof(ONEnode); |
|||
fprintf(file, memFormat, "Nodes", size, memory); |
|||
size = pDevice->numNodes - 1; |
|||
memory = size * sizeof(ONEedge); |
|||
fprintf(file, memFormat, "Edges", size, memory); |
|||
|
|||
size = pDevice->numNodes; |
|||
memory = size * sizeof(ONEelem *); |
|||
size = 0; |
|||
for (pMaterial = pDevice->pMaterials; pMaterial; pMaterial = pMaterial->next) |
|||
size++; |
|||
memory += size * sizeof(ONEmaterial); |
|||
size = numContactNodes = 0; |
|||
for (pContact = pDevice->pFirstContact; pContact; pContact = pContact->next) { |
|||
numContactNodes += pContact->numNodes; |
|||
size++; |
|||
} |
|||
memory += size * sizeof(ONEcontact); |
|||
size = numContactNodes; |
|||
memory += size * sizeof(ONEnode *); |
|||
size = 0; |
|||
fprintf(file, "%-20s%10s%10d\n", "Misc Mesh", "n/a", memory); |
|||
|
|||
size = pDevice->numOrigEquil; |
|||
memory = size * sizeof(struct MatrixElement); |
|||
fprintf(file, memFormat, "Equil Orig NZ", size, memory); |
|||
size = pDevice->numFillEquil; |
|||
memory = size * sizeof(struct MatrixElement); |
|||
fprintf(file, memFormat, "Equil Fill NZ", size, memory); |
|||
size = pDevice->numOrigEquil + pDevice->numFillEquil; |
|||
memory = size * sizeof(struct MatrixElement); |
|||
fprintf(file, memFormat, "Equil Tot NZ", size, memory); |
|||
size = pDevice->dimEquil; |
|||
memory = size * 4 * sizeof(double); |
|||
fprintf(file, memFormat, "Equil Vectors", size, memory); |
|||
|
|||
size = pDevice->numOrigBias; |
|||
memory = size * sizeof(struct MatrixElement); |
|||
fprintf(file, memFormat, "Bias Orig NZ", size, memory); |
|||
size = pDevice->numFillBias; |
|||
memory = size * sizeof(struct MatrixElement); |
|||
fprintf(file, memFormat, "Bias Fill NZ", size, memory); |
|||
size = pDevice->numOrigBias + pDevice->numFillBias; |
|||
memory = size * sizeof(struct MatrixElement); |
|||
fprintf(file, memFormat, "Bias Tot NZ", size, memory); |
|||
size = pDevice->dimBias; |
|||
memory = size * 5 * sizeof(double); |
|||
fprintf(file, memFormat, "Bias Vectors", size, memory); |
|||
|
|||
size = (pDevice->numNodes - 1) * ONEnumEdgeStates + |
|||
pDevice->numNodes * ONEnumNodeStates; |
|||
memory = size * sizeof(double); |
|||
fprintf(file, memFormat, "State Vector", size, memory); |
|||
} |
|||
|
|||
void |
|||
ONEcpuStats(FILE *file, ONEdevice *pDevice) |
|||
{ |
|||
static char *cpuFormat = "%-20s%10g%10g%10g%10g%10g\n"; |
|||
ONEstats *pStats = pDevice->pStats; |
|||
double total; |
|||
int iTotal; |
|||
|
|||
fprintf(file, |
|||
"----------------------------------------------------------------------\n"); |
|||
fprintf(file, |
|||
"Device %s Time Usage:\n", pDevice->name); |
|||
fprintf(file, |
|||
"Item SETUP DC TRAN AC TOTAL\n"); |
|||
fprintf(file, |
|||
"----------------------------------------------------------------------\n"); |
|||
|
|||
total = pStats->setupTime[STAT_SETUP] + |
|||
pStats->setupTime[STAT_DC] + |
|||
pStats->setupTime[STAT_TRAN] + |
|||
pStats->setupTime[STAT_AC]; |
|||
fprintf(file, cpuFormat, "Setup Time", |
|||
pStats->setupTime[STAT_SETUP], |
|||
pStats->setupTime[STAT_DC], |
|||
pStats->setupTime[STAT_TRAN], |
|||
pStats->setupTime[STAT_AC], |
|||
total); |
|||
|
|||
total = pStats->loadTime[STAT_SETUP] + |
|||
pStats->loadTime[STAT_DC] + |
|||
pStats->loadTime[STAT_TRAN] + |
|||
pStats->loadTime[STAT_AC]; |
|||
fprintf(file, cpuFormat, "Load Time", |
|||
pStats->loadTime[STAT_SETUP], |
|||
pStats->loadTime[STAT_DC], |
|||
pStats->loadTime[STAT_TRAN], |
|||
pStats->loadTime[STAT_AC], |
|||
total); |
|||
|
|||
total = pStats->orderTime[STAT_SETUP] + |
|||
pStats->orderTime[STAT_DC] + |
|||
pStats->orderTime[STAT_TRAN] + |
|||
pStats->orderTime[STAT_AC]; |
|||
fprintf(file, cpuFormat, "Order Time", |
|||
pStats->orderTime[STAT_SETUP], |
|||
pStats->orderTime[STAT_DC], |
|||
pStats->orderTime[STAT_TRAN], |
|||
pStats->orderTime[STAT_AC], |
|||
total); |
|||
|
|||
total = pStats->factorTime[STAT_SETUP] + |
|||
pStats->factorTime[STAT_DC] + |
|||
pStats->factorTime[STAT_TRAN] + |
|||
pStats->factorTime[STAT_AC]; |
|||
fprintf(file, cpuFormat, "Factor Time", |
|||
pStats->factorTime[STAT_SETUP], |
|||
pStats->factorTime[STAT_DC], |
|||
pStats->factorTime[STAT_TRAN], |
|||
pStats->factorTime[STAT_AC], |
|||
total); |
|||
|
|||
total = pStats->solveTime[STAT_SETUP] + |
|||
pStats->solveTime[STAT_DC] + |
|||
pStats->solveTime[STAT_TRAN] + |
|||
pStats->solveTime[STAT_AC]; |
|||
fprintf(file, cpuFormat, "Solve Time", |
|||
pStats->solveTime[STAT_SETUP], |
|||
pStats->solveTime[STAT_DC], |
|||
pStats->solveTime[STAT_TRAN], |
|||
pStats->solveTime[STAT_AC], |
|||
total); |
|||
|
|||
total = pStats->updateTime[STAT_SETUP] + |
|||
pStats->updateTime[STAT_DC] + |
|||
pStats->updateTime[STAT_TRAN] + |
|||
pStats->updateTime[STAT_AC]; |
|||
fprintf(file, cpuFormat, "Update Time", |
|||
pStats->updateTime[STAT_SETUP], |
|||
pStats->updateTime[STAT_DC], |
|||
pStats->updateTime[STAT_TRAN], |
|||
pStats->updateTime[STAT_AC], |
|||
total); |
|||
|
|||
total = pStats->checkTime[STAT_SETUP] + |
|||
pStats->checkTime[STAT_DC] + |
|||
pStats->checkTime[STAT_TRAN] + |
|||
pStats->checkTime[STAT_AC]; |
|||
fprintf(file, cpuFormat, "Check Time", |
|||
pStats->checkTime[STAT_SETUP], |
|||
pStats->checkTime[STAT_DC], |
|||
pStats->checkTime[STAT_TRAN], |
|||
pStats->checkTime[STAT_AC], |
|||
total); |
|||
|
|||
total = pStats->setupTime[STAT_SETUP] + |
|||
pStats->setupTime[STAT_DC] + |
|||
pStats->setupTime[STAT_TRAN] + |
|||
pStats->setupTime[STAT_AC]; |
|||
fprintf(file, cpuFormat, "Misc Time", |
|||
pStats->miscTime[STAT_SETUP], |
|||
pStats->miscTime[STAT_DC], |
|||
pStats->miscTime[STAT_TRAN], |
|||
pStats->miscTime[STAT_AC], |
|||
total); |
|||
|
|||
fprintf(file, "%-40s%10g%10s%10g\n", "LTE Time", |
|||
pStats->lteTime, |
|||
"", pStats->lteTime); |
|||
|
|||
total = pStats->totalTime[STAT_SETUP] + |
|||
pStats->totalTime[STAT_DC] + |
|||
pStats->totalTime[STAT_TRAN] + |
|||
pStats->totalTime[STAT_AC]; |
|||
fprintf(file, cpuFormat, "Total Time", |
|||
pStats->totalTime[STAT_SETUP], |
|||
pStats->totalTime[STAT_DC], |
|||
pStats->totalTime[STAT_TRAN], |
|||
pStats->totalTime[STAT_AC], |
|||
total); |
|||
|
|||
iTotal = pStats->numIters[STAT_SETUP] + |
|||
pStats->numIters[STAT_DC] + |
|||
pStats->numIters[STAT_TRAN] + |
|||
pStats->numIters[STAT_AC]; |
|||
fprintf(file, "%-20s%10d%10d%10d%10d%10d\n", "Iterations", |
|||
pStats->numIters[STAT_SETUP], |
|||
pStats->numIters[STAT_DC], |
|||
pStats->numIters[STAT_TRAN], |
|||
pStats->numIters[STAT_AC], |
|||
iTotal); |
|||
} |
|||
@ -0,0 +1,348 @@ |
|||
/********** |
|||
Copyright 1992 Regents of the University of California. All rights reserved. |
|||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numglobs.h" |
|||
#include "numenum.h" |
|||
#include "onemesh.h" |
|||
#include "onedev.h" |
|||
#include "macros.h" |
|||
#include "spMatrix.h" |
|||
#include "bool.h" |
|||
#include "onedext.h" |
|||
#include "oneddefs.h" |
|||
#include "cidersupt.h" |
|||
|
|||
|
|||
/* |
|||
* Functions for projecting the next solution point for modified two-level |
|||
* newton scheme |
|||
*/ |
|||
|
|||
void |
|||
NUMDproject(ONEdevice *pDevice, double delV) |
|||
{ |
|||
ONEelem *pElem = pDevice->elemArray[pDevice->numNodes - 1]; |
|||
ONEnode *pNode; |
|||
ONEedge *pEdge; |
|||
double delPsi, delN, delP, newN, newP; |
|||
double *incVpn; |
|||
int i, index; |
|||
|
|||
|
|||
delV = - delV / VNorm; |
|||
pElem->pRightNode->psi += delV; |
|||
|
|||
if (ABS(delV) < MIN_DELV) { |
|||
ONEstoreInitialGuess(pDevice); |
|||
return; |
|||
} |
|||
/* zero the rhs before loading in the new rhs */ |
|||
for (index = 1; index <= pDevice->numEqns; index++) { |
|||
pDevice->rhs[index] = 0.0; |
|||
} |
|||
/* compute incremental changes due to N contact */ |
|||
pNode = pElem->pLeftNode; |
|||
pDevice->rhs[pNode->psiEqn] = pElem->epsRel * pElem->rDx; |
|||
if (pElem->elemType == SEMICON) { |
|||
pEdge = pElem->pEdge; |
|||
pDevice->rhs[pNode->nEqn] = -pEdge->dJnDpsiP1; |
|||
pDevice->rhs[pNode->pEqn] = -pEdge->dJpDpsiP1; |
|||
} |
|||
incVpn = pDevice->dcDeltaSolution; |
|||
spSolve(pDevice->matrix, pDevice->rhs, incVpn, NIL(spREAL), NIL(spREAL)); |
|||
|
|||
for (index = 1; index < pDevice->numNodes; index++) { |
|||
pElem = pDevice->elemArray[index]; |
|||
for (i = 0; i <= 1; i++) { |
|||
if (pElem->evalNodes[i]) { |
|||
pNode = pElem->pNodes[i]; |
|||
if (pNode->nodeType != CONTACT) { |
|||
delPsi = incVpn[pNode->psiEqn] * delV; |
|||
pDevice->dcSolution[pNode->psiEqn] = pNode->psi + delPsi; |
|||
if (pElem->elemType == SEMICON) { |
|||
delN = incVpn[pNode->nEqn] * delV; |
|||
delP = incVpn[pNode->pEqn] * delV; |
|||
newN = pNode->nConc + delN; |
|||
newP = pNode->pConc + delP; |
|||
/* if new conc less than 0.0 then use a fraction of the guess */ |
|||
if (newN <= 0.0) { |
|||
pDevice->dcSolution[pNode->nEqn] = |
|||
guessNewConc(pNode->nConc, delN); |
|||
} else { |
|||
pDevice->dcSolution[pNode->nEqn] = newN; |
|||
} |
|||
if (newP <= 0.0) { |
|||
pDevice->dcSolution[pNode->pEqn] = |
|||
guessNewConc(pNode->pConc, delP); |
|||
} else { |
|||
pDevice->dcSolution[pNode->pEqn] = newP; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
void |
|||
NBJTproject(ONEdevice *pDevice, double delVce, double delVbe, |
|||
double vbe) |
|||
{ |
|||
ONEelem *pLastElem = pDevice->elemArray[pDevice->numNodes - 1]; |
|||
ONEelem *pBaseElem = pDevice->elemArray[pDevice->baseIndex - 1]; |
|||
ONEelem *pElem; |
|||
ONEnode *pNode; |
|||
ONEedge *pEdge; |
|||
double delPsi, delN, delP, newN, newP, *incVce, *incVbe; |
|||
double baseConc=0.0; |
|||
int i, index; |
|||
double nConc, pConc; |
|||
|
|||
/* normalize the voltages for calculations */ |
|||
delVce = delVce / VNorm; |
|||
delVbe = delVbe / VNorm; |
|||
pLastElem->pRightNode->psi += delVce; |
|||
pBaseElem->pRightNode->vbe = vbe / VNorm + pBaseElem->matlInfo->refPsi; |
|||
pNode = pBaseElem->pRightNode; |
|||
if (pNode->baseType == N_TYPE) { |
|||
baseConc = pNode->nConc; |
|||
} else if (pNode->baseType == P_TYPE) { |
|||
baseConc = pNode->pConc; |
|||
} |
|||
if (ABS(delVce) > MIN_DELV) { |
|||
|
|||
/* zero the rhs before loading in the new rhs */ |
|||
for (index = 1; index <= pDevice->numEqns; index++) { |
|||
pDevice->rhs[index] = 0.0; |
|||
} |
|||
/* store the new rhs for computing the incremental quantities */ |
|||
pNode = pLastElem->pLeftNode; |
|||
pDevice->rhs[pNode->psiEqn] = pLastElem->epsRel * pLastElem->rDx; |
|||
if (pLastElem->elemType == SEMICON) { |
|||
pEdge = pLastElem->pEdge; |
|||
pDevice->rhs[pNode->nEqn] = -pEdge->dJnDpsiP1; |
|||
pDevice->rhs[pNode->pEqn] = -pEdge->dJpDpsiP1; |
|||
} |
|||
incVce = pDevice->dcDeltaSolution; |
|||
spSolve(pDevice->matrix, pDevice->rhs, incVce, NIL(spREAL), NIL(spREAL)); |
|||
|
|||
for (index = 1; index < pDevice->numNodes; index++) { |
|||
pElem = pDevice->elemArray[index]; |
|||
for (i = 0; i <= 1; i++) { |
|||
if (pElem->evalNodes[i]) { |
|||
pNode = pElem->pNodes[i]; |
|||
if (pNode->nodeType != CONTACT) { |
|||
delPsi = incVce[pNode->psiEqn] * delVce; |
|||
pDevice->dcSolution[pNode->psiEqn] = pNode->psi + delPsi; |
|||
if (pElem->elemType == SEMICON) { |
|||
delN = incVce[pNode->nEqn] * delVce; |
|||
delP = incVce[pNode->pEqn] * delVce; |
|||
newN = pNode->nConc + delN; |
|||
newP = pNode->pConc + delP; |
|||
/* if new conc less than 0.0 then use a fraction of the guess */ |
|||
if (newN <= 0.0) { |
|||
pDevice->dcSolution[pNode->nEqn] = |
|||
guessNewConc(pNode->nConc, delN); |
|||
} else { |
|||
pDevice->dcSolution[pNode->nEqn] = newN; |
|||
} |
|||
if (newP <= 0.0) { |
|||
pDevice->dcSolution[pNode->pEqn] = |
|||
guessNewConc(pNode->pConc, delP); |
|||
} else { |
|||
pDevice->dcSolution[pNode->pEqn] = newP; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} else { |
|||
ONEstoreInitialGuess(pDevice); |
|||
} |
|||
|
|||
if (ABS(delVbe) > MIN_DELV) { |
|||
|
|||
/* zero the rhs before loading in the new rhs base contribution */ |
|||
for (index = 1; index <= pDevice->numEqns; index++) { |
|||
pDevice->rhs[index] = 0.0; |
|||
} |
|||
pNode = pBaseElem->pRightNode; |
|||
if (pNode->baseType == N_TYPE) { |
|||
pDevice->rhs[pNode->nEqn] = baseConc * pNode->eg; |
|||
} else if (pNode->baseType == P_TYPE) { |
|||
pDevice->rhs[pNode->pEqn] = baseConc * pNode->eg; |
|||
} |
|||
|
|||
incVbe = pDevice->copiedSolution; |
|||
spSolve(pDevice->matrix, pDevice->rhs, incVbe, NIL(spREAL), NIL(spREAL)); |
|||
|
|||
for (index = 1; index < pDevice->numNodes; index++) { |
|||
pElem = pDevice->elemArray[index]; |
|||
for (i = 0; i <= 1; i++) { |
|||
if (pElem->evalNodes[i]) { |
|||
pNode = pElem->pNodes[i]; |
|||
if (pNode->nodeType != CONTACT) { |
|||
delPsi = incVbe[pNode->psiEqn] * delVbe; |
|||
pDevice->dcSolution[pNode->psiEqn] += delPsi; |
|||
if (pElem->elemType == SEMICON) { |
|||
delN = incVbe[pNode->nEqn] * delVbe; |
|||
delP = incVbe[pNode->pEqn] * delVbe; |
|||
nConc = pDevice->dcSolution[pNode->nEqn]; |
|||
pConc = pDevice->dcSolution[pNode->pEqn]; |
|||
newN = nConc + delN; |
|||
newP = pConc + delP; |
|||
/* if new conc less than 0.0 then use a fraction of the guess */ |
|||
if (newN <= 0.0) { |
|||
pDevice->dcSolution[pNode->nEqn] = |
|||
guessNewConc(nConc, delN); |
|||
} else { |
|||
pDevice->dcSolution[pNode->nEqn] = newN; |
|||
} |
|||
if (newP <= 0.0) { |
|||
pDevice->dcSolution[pNode->pEqn] = |
|||
guessNewConc(pConc, delP); |
|||
} else { |
|||
pDevice->dcSolution[pNode->pEqn] = newP; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* functions to update device states for full-LU algorithm */ |
|||
|
|||
void |
|||
NUMDupdate(ONEdevice *pDevice, double delV, BOOLEAN updateBoundary) |
|||
{ |
|||
ONEelem *pElem = pDevice->elemArray[pDevice->numNodes - 1]; |
|||
ONEnode *pNode; |
|||
ONEedge *pEdge; |
|||
double delPsi, delN, delP; |
|||
int i, index; |
|||
|
|||
|
|||
delV = - delV / VNorm; |
|||
if (updateBoundary) { |
|||
pElem->pRightNode->psi += delV; |
|||
} |
|||
for (index = 1; index < pDevice->numNodes; index++) { |
|||
pElem = pDevice->elemArray[index]; |
|||
for (i = 0; i <= 1; i++) { |
|||
if (pElem->evalNodes[i]) { |
|||
pNode = pElem->pNodes[i]; |
|||
if (pNode->nodeType != CONTACT) { |
|||
delPsi = pDevice->dcDeltaSolution[pNode->psiEqn] * delV; |
|||
pDevice->dcSolution[pNode->psiEqn] = pNode->psi + delPsi; |
|||
if (pElem->elemType == SEMICON) { |
|||
delN = pDevice->dcDeltaSolution[pNode->nEqn] * delV; |
|||
delP = pDevice->dcDeltaSolution[pNode->pEqn] * delV; |
|||
pDevice->dcSolution[pNode->nEqn] = pNode->nConc + delN; |
|||
pDevice->dcSolution[pNode->pEqn] = pNode->pConc + delP; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
void |
|||
NBJTupdate(ONEdevice *pDevice, double delVce, double delVbe, |
|||
double vbe, BOOLEAN updateBoundary) |
|||
{ |
|||
ONEelem *pLastElem = pDevice->elemArray[pDevice->numNodes - 1]; |
|||
ONEelem *pBaseElem = pDevice->elemArray[pDevice->baseIndex - 1]; |
|||
ONEelem *pElem; |
|||
ONEnode *pNode; |
|||
double delPsi, delN, delP, *incVce, *incVbe; |
|||
int i, index; |
|||
|
|||
/* normalize the voltages for calculations */ |
|||
delVce = delVce / VNorm; |
|||
delVbe = delVbe / VNorm; |
|||
if (updateBoundary) { |
|||
pLastElem->pRightNode->psi += delVce; |
|||
pBaseElem->pRightNode->vbe = vbe / VNorm + pBaseElem->matlInfo->refPsi; |
|||
} |
|||
/* |
|||
* The incremental quantities are available from NBJTconductance. incVce |
|||
* (dcDeltaSolution) and incVbe (copiedSolution) are used to store the |
|||
* incremental quantities associated with Vce and Vbe |
|||
*/ |
|||
|
|||
/* set incVce = dcDeltaSolution; incVbe = copiedSolution */ |
|||
incVce = pDevice->dcDeltaSolution; |
|||
incVbe = pDevice->copiedSolution; |
|||
|
|||
for (index = 1; index < pDevice->numNodes; index++) { |
|||
pElem = pDevice->elemArray[index]; |
|||
for (i = 0; i <= 1; i++) { |
|||
if (pElem->evalNodes[i]) { |
|||
pNode = pElem->pNodes[i]; |
|||
if (pNode->nodeType != CONTACT) { |
|||
delPsi = incVce[pNode->psiEqn] * delVce |
|||
+ incVbe[pNode->psiEqn] * delVbe; |
|||
pDevice->dcSolution[pNode->psiEqn] = pNode->psi + delPsi; |
|||
if (pElem->elemType == SEMICON) { |
|||
delN = incVce[pNode->nEqn] * delVce |
|||
+ incVbe[pNode->nEqn] * delVbe; |
|||
delP = incVce[pNode->pEqn] * delVce |
|||
+ incVbe[pNode->pEqn] * delVbe; |
|||
pDevice->dcSolution[pNode->nEqn] = pNode->nConc + delN; |
|||
pDevice->dcSolution[pNode->pEqn] = pNode->pConc + delP; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
void |
|||
NUMDsetBCs(ONEdevice *pDevice, double vpn) |
|||
{ |
|||
ONEelem *pElem = pDevice->elemArray[pDevice->numNodes - 1]; |
|||
|
|||
|
|||
/* normalize the voltage */ |
|||
vpn = - vpn / VNorm; |
|||
|
|||
/* set the boundary conditions */ |
|||
pElem->pRightNode->psi = vpn + pElem->pRightNode->psi0; |
|||
} |
|||
|
|||
void |
|||
NBJTsetBCs(ONEdevice *pDevice, double vce, double vbe) |
|||
{ |
|||
ONEelem *pLastElem = pDevice->elemArray[pDevice->numNodes - 1]; |
|||
ONEelem *pBaseElem = pDevice->elemArray[pDevice->baseIndex - 1]; |
|||
ONEnode *pNode; |
|||
double psi, conc, sign, absConc; |
|||
double nie, ni, pi; |
|||
|
|||
/* normalize the voltages */ |
|||
vce = vce / VNorm; |
|||
vbe = vbe / VNorm; |
|||
|
|||
/* set the boundary conditions */ |
|||
pBaseElem->pRightNode->vbe = vbe + pBaseElem->matlInfo->refPsi; |
|||
pLastElem->pRightNode->psi = vce + pLastElem->pRightNode->psi0; |
|||
|
|||
/* |
|||
* if (pLastElem->elemType IS INSULATOR) { pNode->psi = RefPsi - |
|||
* pNode->eaff; pNode->nConc = 0.0; pNode->pConc = 0.0; } else if |
|||
* (pLastElem->elemType IS SEMICON) { nie = pNode->nie; conc = |
|||
* pNode->netConc / pNode->nie; psi = 0.0; ni = nie; pi = nie; sign = SGN( |
|||
* conc ); absConc = ABS( conc ); if ( conc ISNOT 0.0 ) { psi = sign * log( |
|||
* 0.5 * absConc + sqrt( 1.0 + 0.25*absConc*absConc )); ni = nie * exp( psi |
|||
* ); pi = nie * exp( -psi ); } pNode->psi = pLastElem->matlInfo->refPsi + |
|||
* psi; pNode->nConc = ni; pNode->pConc = pi; } pNode->psi = pNode->psi0; |
|||
* pNode->psi += vce; |
|||
*/ |
|||
} |
|||
@ -0,0 +1,94 @@ |
|||
/********** |
|||
Copyright 1992 Regents of the University of California. All rights reserved. |
|||
Author: 1992 David A. Gates, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
/* |
|||
* Functions needed to read solutions for 1D devices. |
|||
*/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numglobs.h" |
|||
#include "numenum.h" |
|||
#include "onedev.h" |
|||
#include "onemesh.h" |
|||
#include "plot.h" |
|||
#include "onedext.h" |
|||
#include "oneddefs.h" |
|||
#include "cidersupt.h" |
|||
|
|||
int |
|||
ONEreadState(ONEdevice *pDevice, char *fileName, int numVolts, |
|||
double *pV1, double *pV2 ) |
|||
/* fileName | File containing raw data */ |
|||
/* int numVolts | Number of voltage differences */ |
|||
/* double *pV1, *pV2 | Pointer to return them in */ |
|||
{ |
|||
int dataLength; |
|||
int index, i; |
|||
ONEnode **nodeArray=NULL; |
|||
ONEnode *pNode; |
|||
ONEelem *pElem; |
|||
ONEmaterial *info; |
|||
double refPsi = 0.0; |
|||
double *psiData, *nData, *pData; |
|||
double *vData[2]; |
|||
struct plot *stateDB; |
|||
struct plot *voltsDB; |
|||
char voltName[80]; |
|||
|
|||
|
|||
stateDB = DBread( fileName ); |
|||
if (stateDB == NULL) return (-1); |
|||
voltsDB = stateDB->pl_next; |
|||
if (voltsDB == NULL) return (-1); |
|||
|
|||
for (i=0; i < numVolts; i++ ) { |
|||
sprintf( voltName, "v%d%d", i+1, numVolts+1 ); |
|||
vData[i] = DBgetData( voltsDB, voltName, 1 ); |
|||
if (vData[i] == NULL) return (-1); |
|||
} |
|||
dataLength = pDevice->numNodes; |
|||
psiData = DBgetData( stateDB, "psi", dataLength ); |
|||
nData = DBgetData( stateDB, "n", dataLength ); |
|||
pData = DBgetData( stateDB, "p", dataLength ); |
|||
if (psiData == NULL || nData == NULL || pData == NULL) return (-1); |
|||
|
|||
if (pV1 != NULL) { |
|||
*pV1 = vData[0][0]; |
|||
FREE( vData[0] ); |
|||
} |
|||
if (pV2 != NULL) { |
|||
*pV2 = vData[1][0]; |
|||
FREE( vData[1] ); |
|||
} |
|||
|
|||
/* generate the work array for copying node info */ |
|||
XCALLOC(nodeArray, ONEnode *, 1 + pDevice->numNodes); |
|||
|
|||
/* store the nodes in this work array and print out later */ |
|||
for (index = 1; index < pDevice->numNodes; index++) { |
|||
pElem = pDevice->elemArray[index]; |
|||
if (refPsi == 0.0 && pElem->matlInfo->type == SEMICON) { |
|||
refPsi = pElem->matlInfo->refPsi; |
|||
} |
|||
for (i = 0; i <= 1; i++) { |
|||
if (pElem->evalNodes[i]) { |
|||
pNode = pElem->pNodes[i]; |
|||
nodeArray[pNode->nodeI] = pNode; |
|||
} |
|||
} |
|||
} |
|||
for (index = 1; index <= pDevice->numNodes; index++) { |
|||
pNode = nodeArray[index]; |
|||
pNode->psi = psiData[index-1]/VNorm + refPsi; |
|||
pNode->nConc = nData[index-1]/NNorm; |
|||
pNode->pConc = pData[index-1]/NNorm; |
|||
} |
|||
FREE(nodeArray); |
|||
FREE(psiData); |
|||
FREE(nData); |
|||
FREE(pData); |
|||
|
|||
return (0); |
|||
} |
|||
@ -0,0 +1,233 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group |
|||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
/********** |
|||
One-Dimensional Numerical Device Setup Routines |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numglobs.h" |
|||
#include "numconst.h" |
|||
#include "numenum.h" |
|||
#include "onemesh.h" |
|||
#include "onedev.h" |
|||
#include "carddefs.h" /* XXX Not really modular if we need this. */ |
|||
/* #include "material.h" */ |
|||
#include "onedext.h" |
|||
#include "oneddefs.h" |
|||
#include "cidersupt.h" |
|||
|
|||
/* compute node parameters */ |
|||
void |
|||
ONEsetup(ONEdevice *pDevice) |
|||
{ |
|||
double temp1, deltaEg, avgConc, totalConc, absNetConc; |
|||
double ncv0, dBand, dNie, psiBand[2]; |
|||
int index, eIndex; |
|||
int numContactNodes; |
|||
ONEnode *pNode; |
|||
ONEelem *pElem; |
|||
ONEedge *pEdge; |
|||
ONEmaterial *info; |
|||
|
|||
|
|||
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) { |
|||
pElem = pDevice->elemArray[eIndex]; |
|||
info = pElem->matlInfo; |
|||
|
|||
pElem->dx = pElem->pRightNode->x - pElem->pLeftNode->x; |
|||
pElem->epsRel = info->eps; |
|||
|
|||
if (pElem->elemType == INSULATOR) { |
|||
for (index = 0; index <= 1; index++) { |
|||
if (pElem->evalNodes[index]) { |
|||
pNode = pElem->pNodes[index]; |
|||
if (pNode->nodeType == CONTACT) { |
|||
pNode->eaff = PHI_METAL; |
|||
pNode->eg = 0.0; |
|||
} else { |
|||
pNode->eaff = info->affin; |
|||
pNode->eg = info->eg0; |
|||
} |
|||
} |
|||
} |
|||
} else if (pElem->elemType == SEMICON) { |
|||
ncv0 = sqrt(info->nc0) * sqrt(info->nv0); |
|||
for (index = 0; index <= 1; index++) { |
|||
if (pElem->evalNodes[index]) { |
|||
pNode = pElem->pNodes[index]; |
|||
|
|||
/* Fixed Interface Charge */ |
|||
pNode->qf = 0.0; |
|||
|
|||
/* Narrowing of Energy Band-Gap */ |
|||
if (BandGapNarrowing) { |
|||
absNetConc = ABS(pNode->netConc); |
|||
if (pNode->netConc > 0.0) { |
|||
temp1 = log(absNetConc / info->nrefBGN[ELEC]); |
|||
deltaEg = -info->dEgDn[ELEC] * (temp1 + sqrt(temp1 * temp1 + 0.5)); |
|||
pNode->eg = info->eg0 + deltaEg; |
|||
} else if (pNode->netConc < 0.0) { |
|||
temp1 = log(absNetConc / info->nrefBGN[HOLE]); |
|||
deltaEg = -info->dEgDn[HOLE] * (temp1 + sqrt(temp1 * temp1 + 0.5)); |
|||
pNode->eg = info->eg0 + deltaEg; |
|||
} else { |
|||
pNode->eg = info->eg0; |
|||
} |
|||
} else { |
|||
pNode->eg = info->eg0; |
|||
} |
|||
pNode->nie = ncv0 * exp(-0.5 * pNode->eg / Vt); |
|||
pNode->eaff = info->affin; |
|||
/* Save band structure parameter. */ |
|||
psiBand[index] = -info->refPsi; |
|||
|
|||
/* Ionized-Impurity-Scattering Reduction of Carrier Lifetime */ |
|||
if (ConcDepLifetime) { |
|||
totalConc = pNode->totalConc; |
|||
temp1 = 1.0 / (1.0 + totalConc / info->nrefSRH[ELEC]); |
|||
pNode->tn = info->tau0[ELEC] * temp1; |
|||
temp1 = 1.0 / (1.0 + totalConc / info->nrefSRH[HOLE]); |
|||
pNode->tp = info->tau0[HOLE] * temp1; |
|||
} else { |
|||
pNode->tn = info->tau0[ELEC]; |
|||
pNode->tp = info->tau0[HOLE]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
pEdge = pElem->pEdge; |
|||
|
|||
/* Variable Band Built-In Potential */ |
|||
dBand = psiBand[1] - psiBand[0]; |
|||
dNie = log(pElem->pNodes[1]->nie / pElem->pNodes[0]->nie); |
|||
pEdge->dCBand = dBand + dNie; |
|||
pEdge->dVBand = -dBand + dNie; |
|||
|
|||
/* Evaluate conc.-dep. mobility. */ |
|||
avgConc = 0.5 * (pElem->pRightNode->totalConc + pElem->pLeftNode->totalConc); |
|||
MOBconcDep(info, avgConc, &pEdge->mun, &pEdge->mup); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Transfer BC info from bdry to nodes and edges. */ |
|||
static void |
|||
ONEcopyBCinfo(ONEdevice *pDevice, ONEelem *pElem, BDRYcard *bdry, int index) |
|||
{ |
|||
ONEnode *pNode; |
|||
ONEelem *pNElem; |
|||
int eIndex, nIndex; |
|||
double length; |
|||
|
|||
|
|||
/* First add fixed charge. */ |
|||
pNode = pElem->pNodes[index]; |
|||
pNode->qf += bdry->BDRYqf; |
|||
|
|||
/* Now add surface recombination. */ |
|||
/* Compute semiconductor length around this node. */ |
|||
length = 0.0; |
|||
for (eIndex = 0; eIndex <= 3; eIndex++) { |
|||
pNElem = pNode->pElems[eIndex]; |
|||
if ((pNElem != NIL(ONEelem)) && (pElem->elemType == SEMICON)) { |
|||
length += 0.5 * pElem->dx; |
|||
} |
|||
} |
|||
if (bdry->BDRYsnGiven) { |
|||
pNode->tn = pNode->tn / |
|||
(1.0 + ((bdry->BDRYsn * TNorm) * pNode->tn) / length); |
|||
} |
|||
if (bdry->BDRYspGiven) { |
|||
pNode->tp = pNode->tp / |
|||
(1.0 + ((bdry->BDRYsp * TNorm) * pNode->tp) / length); |
|||
} |
|||
/* Finally, surface layer is irrelevant for 1d devices. */ |
|||
} |
|||
|
|||
|
|||
/* Compute boundary condition parameters. */ |
|||
void |
|||
ONEsetBCparams(ONEdevice *pDevice, BDRYcard *bdryList, CONTcard *contList) |
|||
{ |
|||
int index, xIndex; |
|||
ONEnode *pNode; |
|||
ONEelem *pElem, *pNElem; |
|||
BDRYcard *bdry; |
|||
CONTcard *cont; |
|||
|
|||
|
|||
|
|||
for (bdry = bdryList; bdry != NIL(BDRYcard); bdry = bdry->BDRYnextCard) { |
|||
for (xIndex = bdry->BDRYixLow; xIndex < bdry->BDRYixHigh; xIndex++) { |
|||
pElem = pDevice->elemArray[xIndex]; |
|||
if (pElem != NIL(ONEelem)) { |
|||
if (pElem->domain == bdry->BDRYdomain) { |
|||
for (index = 0; index <= 1; index++) { |
|||
if (pElem->evalNodes[index]) { |
|||
pNElem = pElem->pElems[index]; |
|||
if (bdry->BDRYneighborGiven) { |
|||
if (pNElem && (pNElem->domain == bdry->BDRYneighbor)) { |
|||
/* Found an interface node. */ |
|||
ONEcopyBCinfo(pDevice, pElem, bdry, index); |
|||
} |
|||
} else { |
|||
if ((!pNElem) || (pNElem->domain != pElem->domain)) { |
|||
/* Found a boundary node. */ |
|||
ONEcopyBCinfo(pDevice, pElem, bdry, index); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
for (cont = contList; cont != NIL(CONTcard); cont = cont->CONTnextCard) { |
|||
if (!cont->CONTworkfunGiven) { |
|||
cont->CONTworkfun = PHI_METAL; |
|||
} |
|||
/* |
|||
* XXX This won't work right if someone tries to change the 1d BJT base |
|||
* contact workfunction and doesn't want to change the emitter. But no |
|||
* one will probably try to do that. |
|||
*/ |
|||
if (cont->CONTnumber == 1) { |
|||
pDevice->elemArray[1]->pNodes[0]->eaff = cont->CONTworkfun; |
|||
} else if ((cont->CONTnumber == 2) || (cont->CONTnumber == 3)) { |
|||
pDevice->elemArray[pDevice->numNodes - 1]->pNodes[1]->eaff = |
|||
cont->CONTworkfun; |
|||
} |
|||
} |
|||
} |
|||
|
|||
void |
|||
ONEnormalize(ONEdevice *pDevice) |
|||
{ |
|||
int index, eIndex; |
|||
ONEelem *pElem; |
|||
ONEnode *pNode; |
|||
|
|||
for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) { |
|||
pElem = pDevice->elemArray[eIndex]; |
|||
|
|||
pElem->dx /= LNorm; |
|||
pElem->rDx = 1.0 / pElem->dx; |
|||
pElem->epsRel /= EpsNorm; |
|||
for (index = 0; index <= 1; index++) { |
|||
if (pElem->evalNodes[index]) { |
|||
pNode = pElem->pNodes[index]; |
|||
pNode->netConc /= NNorm; |
|||
pNode->nd /= NNorm; |
|||
pNode->na /= NNorm; |
|||
pNode->qf /= (NNorm * LNorm); |
|||
pNode->nie /= NNorm; |
|||
pNode->eg /= VNorm; |
|||
pNode->eaff /= VNorm; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
1084
src/ciderlib/oned/onesolve.c
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
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 '_'. |
|||
@ -0,0 +1,22 @@ |
|||
## Process this file with automake to produce Makefile.in
|
|||
|
|||
noinst_LIBRARIES = libcidersuprt.a |
|||
|
|||
libcidersuprt_a_SOURCES = \
|
|||
database.c \
|
|||
devprint.c \
|
|||
geominfo.c \
|
|||
globals.c \
|
|||
integset.c \
|
|||
integuse.c \
|
|||
logfile.c \
|
|||
mater.c \
|
|||
misc.c \
|
|||
mobil.c \
|
|||
recomb.c \
|
|||
suprem.c \
|
|||
suprmitf.c |
|||
|
|||
EXTRA_DIST = makefile |
|||
INCLUDES = -I$(top_srcdir)/src/include |
|||
MAINTAINERCLEANFILES = Makefile.in |
|||
@ -0,0 +1,72 @@ |
|||
/********** |
|||
Copyright 1992 Regents of the University of California. All rights reserved. |
|||
Author: 1992 David A. Gates, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "fteext.h" |
|||
/* #include "ftedata.h" */ |
|||
|
|||
struct plot * |
|||
DBread( fileName ) |
|||
{ |
|||
struct plot *plot; |
|||
|
|||
plot = raw_read( fileName ); |
|||
|
|||
return(plot); |
|||
} |
|||
|
|||
double * |
|||
DBgetData( plot, name, lengthWanted ) |
|||
struct plot *plot; |
|||
char *name; |
|||
int lengthWanted; |
|||
{ |
|||
struct dvec *v; |
|||
double *data; |
|||
int i; |
|||
|
|||
v = vec_fromplot(name,plot); |
|||
|
|||
if (!v) { |
|||
fprintf( stderr, "Error: cannot locate variable '%s'\n", name ); |
|||
return(NULL); |
|||
} |
|||
if (v->v_length != lengthWanted ) { |
|||
fprintf( stderr, "Error: vector '%s' has incorrect length\n", name ); |
|||
return(NULL); |
|||
} |
|||
|
|||
data = (double *) malloc(sizeof (double) * v->v_length); |
|||
if (isreal(v)) { |
|||
bcopy((char *) v->v_realdata, (char *) data, sizeof (double) * v->v_length); |
|||
} else { |
|||
for (i=0; i < v->v_length; i++) { |
|||
data[i] = realpart(&v->v_compdata[i]); |
|||
} |
|||
} |
|||
return(data); |
|||
} |
|||
|
|||
void |
|||
DBfree( plot ) |
|||
struct plot *plot; |
|||
{ |
|||
struct dvec *v, *nextv; |
|||
struct plot *pl, *nextpl; |
|||
|
|||
for (pl = plot; pl; pl = nextpl) { |
|||
nextpl = pl->pl_next; |
|||
tfree( pl->pl_title ); |
|||
tfree( pl->pl_date ); |
|||
tfree( pl->pl_name ); |
|||
tfree( pl->pl_typename ); |
|||
for (v = pl->pl_dvecs; v; v = nextv) { |
|||
nextv = v->v_next; |
|||
vec_free( v ); |
|||
} |
|||
wl_free( pl->pl_commands ); |
|||
/* XXX Environment variables (pl->pl_env) will leak. */ |
|||
} |
|||
} |
|||
@ -0,0 +1,74 @@ |
|||
/********** |
|||
Copyright 1992 Regents of the University of California. All rights reserved. |
|||
Author: 1992 David A. Gates, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
/* Device-type Dependent Printing Routines */ |
|||
|
|||
#include "ngspice.h" |
|||
#include "optndefs.h" |
|||
|
|||
void |
|||
printVoltages(FILE *file, char *mName, char *iName, int devType, |
|||
int numVolt, double v1, double delV1, double v2, |
|||
double delV2, double v3, double delV3 ) |
|||
/* FILE *file output filestream */ |
|||
/* char *mName name of model */ |
|||
/* char *iName name of instance */ |
|||
{ |
|||
fprintf( file, "\n" ); |
|||
switch ( devType ) { |
|||
case OPTN_RESISTOR: |
|||
fprintf( file, "RES %s:%s voltage:\n", mName, iName ); |
|||
fprintf( file, " Vpn =% .4e delVpn =% .4e\n", v1, delV1 ); |
|||
break; |
|||
case OPTN_CAPACITOR: |
|||
fprintf( file, "CAP %s:%s voltage:\n", mName, iName ); |
|||
fprintf( file, " Vpn =% .4e delVpn =% .4e\n", v1, delV1 ); |
|||
break; |
|||
case OPTN_DIODE: |
|||
fprintf( file, "DIO %s:%s voltage:\n", mName, iName ); |
|||
fprintf( file, " Vpn =% .4e delVpn =% .4e\n", v1, delV1 ); |
|||
break; |
|||
case OPTN_MOSCAP: |
|||
fprintf( file, "MOS %s:%s voltage:\n", mName, iName ); |
|||
fprintf( file, " Vgb =% .4e delVgb =% .4e\n", v1, delV1 ); |
|||
break; |
|||
case OPTN_BIPOLAR: |
|||
fprintf( file, "BJT %s:%s voltages:\n", mName, iName ); |
|||
if ( numVolt == 3 ) { |
|||
fprintf( file, " Vce =% .4e delVce =% .4e\n", |
|||
v1 - v3, delV1 - delV3 ); |
|||
fprintf( file, " Vbe =% .4e delVbe =% .4e\n", |
|||
v2 - v3, delV2 - delV3 ); |
|||
fprintf( file, " Vcs =% .4e delVcs =% .4e\n", v1, delV1 ); |
|||
} else { |
|||
fprintf( file, " Vce =% .4e delVce =% .4e\n", v1, delV1 ); |
|||
fprintf( file, " Vbe =% .4e delVbe =% .4e\n", v2, delV2 ); |
|||
} |
|||
break; |
|||
case OPTN_MOSFET: |
|||
fprintf( file, "MOS %s:%s voltages:\n", mName, iName ); |
|||
fprintf( file, " Vdb =% .4e delVdb =% .4e\n", v1, delV1 ); |
|||
fprintf( file, " Vgb =% .4e delVgb =% .4e\n", v2, delV2 ); |
|||
fprintf( file, " Vsb =% .4e delVsb =% .4e\n", v3, delV3 ); |
|||
break; |
|||
case OPTN_JFET: |
|||
if ( numVolt == 3 ) { |
|||
fprintf( file, "JFET %s:%s voltages:\n", mName, iName ); |
|||
fprintf( file, " Vdb =% .4e delVdb =% .4e\n", v1, delV1 ); |
|||
fprintf( file, " Vgb =% .4e delVgb =% .4e\n", v2, delV2 ); |
|||
fprintf( file, " Vsb =% .4e delVsb =% .4e\n", v3, delV3 ); |
|||
} else { |
|||
fprintf( file, "JFET %s:%s voltages:\n", mName, iName ); |
|||
fprintf( file, " Vds =% .4e delVds =% .4e\n", v1, delV1 ); |
|||
fprintf( file, " Vgs =% .4e delVgs =% .4e\n", v2, delV2 ); |
|||
} |
|||
break; |
|||
case OPTN_SOIBJT: |
|||
case OPTN_SOIMOS: |
|||
case OPTN_MESFET: |
|||
default: |
|||
break; |
|||
} |
|||
} |
|||
@ -0,0 +1,130 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group |
|||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "gendev.h" |
|||
#include "macros.h" |
|||
#include "memory.h" |
|||
|
|||
void printCoordInfo(CoordInfo *pFirstCoord) |
|||
{ |
|||
CoordInfo *pCoord; |
|||
|
|||
for ( pCoord = pFirstCoord; pCoord != NIL(CoordInfo); |
|||
pCoord = pCoord->next ) { |
|||
fprintf(stderr, "mesh number=%4d location=%11.4e\n", |
|||
pCoord->number, pCoord->location ); |
|||
} |
|||
} |
|||
|
|||
void killCoordInfo(CoordInfo *pFirstCoord) |
|||
{ |
|||
CoordInfo *pCoord, *pKill; |
|||
|
|||
for ( pCoord = pFirstCoord; pCoord != NIL(CoordInfo); ) { |
|||
pKill = pCoord; |
|||
pCoord = pCoord->next; |
|||
FREE( pKill ); |
|||
} |
|||
} |
|||
|
|||
void ONEprintDomainInfo(DomainInfo *pFirstDomain) |
|||
{ |
|||
DomainInfo *pDomain; |
|||
|
|||
for ( pDomain = pFirstDomain; pDomain != NIL(DomainInfo); |
|||
pDomain = pDomain->next ) { |
|||
fprintf( stderr, "domain id=%4d mat=%4d ixLo=%4d ixHi=%4d\n", |
|||
pDomain->id, pDomain->material, pDomain->ixLo, pDomain->ixHi ); |
|||
} |
|||
} |
|||
|
|||
void TWOprintDomainInfo(DomainInfo *pFirstDomain) |
|||
{ |
|||
DomainInfo *pDomain; |
|||
|
|||
for ( pDomain = pFirstDomain; pDomain != NIL(DomainInfo); |
|||
pDomain = pDomain->next ) { |
|||
fprintf( stderr, |
|||
"domain id=%4d mat=%4d ixLo=%4d ixHi=%4d iyLo=%4d iyHi=%4d\n", |
|||
pDomain->id, pDomain->material, |
|||
pDomain->ixLo, pDomain->ixHi, |
|||
pDomain->iyLo, pDomain->iyHi); |
|||
} |
|||
} |
|||
|
|||
void killDomainInfo(DomainInfo *pFirstDomain) |
|||
{ |
|||
DomainInfo *pDomain, *pKill; |
|||
|
|||
for ( pDomain = pFirstDomain; pDomain != NIL(DomainInfo); ) { |
|||
pKill = pDomain; |
|||
pDomain = pDomain->next; |
|||
FREE( pKill ); |
|||
} |
|||
} |
|||
|
|||
void ONEprintBoundaryInfo(BoundaryInfo *pFirstBoundary) |
|||
{ |
|||
BoundaryInfo *pBoundary; |
|||
|
|||
for ( pBoundary = pFirstBoundary; pBoundary != NIL(BoundaryInfo); |
|||
pBoundary = pBoundary->next ) { |
|||
fprintf( stderr, |
|||
"boundary dom=%4d nbr=%4d ixLo=%4d ixHi=%4d\n", |
|||
pBoundary->domain, pBoundary->neighbor, |
|||
pBoundary->ixLo, pBoundary->ixHi ); |
|||
} |
|||
} |
|||
|
|||
void TWOprintBoundaryInfo(BoundaryInfo *pFirstBoundary) |
|||
{ |
|||
BoundaryInfo *pBoundary; |
|||
|
|||
for ( pBoundary = pFirstBoundary; pBoundary != NIL(BoundaryInfo); |
|||
pBoundary = pBoundary->next ) { |
|||
fprintf( stderr, |
|||
"boundary dom=%4d nbr=%4d ixLo=%4d ixHi=%4d iyLo=%4d iyHi=%4d\n", |
|||
pBoundary->domain, pBoundary->neighbor, |
|||
pBoundary->ixLo, pBoundary->ixHi, |
|||
pBoundary->iyLo, pBoundary->iyHi); |
|||
} |
|||
} |
|||
|
|||
void killBoundaryInfo(BoundaryInfo *pFirstBoundary) |
|||
{ |
|||
BoundaryInfo *pBoundary, *pKill; |
|||
|
|||
for ( pBoundary = pFirstBoundary; pBoundary != NIL(BoundaryInfo); ) { |
|||
pKill = pBoundary; |
|||
pBoundary = pBoundary->next; |
|||
FREE( pKill ); |
|||
} |
|||
} |
|||
|
|||
void TWOprintElectrodeInfo(ElectrodeInfo *pFirstElectrode) |
|||
{ |
|||
ElectrodeInfo *pElectrode; |
|||
|
|||
for ( pElectrode = pFirstElectrode; pElectrode != NIL(ElectrodeInfo); |
|||
pElectrode = pElectrode->next ) { |
|||
fprintf( stderr, |
|||
"electrode id=%4d ixLo=%4d ixHi=%4d iyLo=%4d iyHi=%4d\n", |
|||
pElectrode->id, pElectrode->ixLo, pElectrode->ixHi, |
|||
pElectrode->iyLo, pElectrode->iyHi ); |
|||
} |
|||
} |
|||
|
|||
void killElectrodeInfo(ElectrodeInfo *pFirstElectrode) |
|||
{ |
|||
ElectrodeInfo *pElectrode, *pKill; |
|||
|
|||
for ( pElectrode = pFirstElectrode; pElectrode != NIL(ElectrodeInfo); ) { |
|||
pKill = pElectrode; |
|||
pElectrode = pElectrode->next; |
|||
FREE( pKill ); |
|||
} |
|||
} |
|||
@ -0,0 +1,161 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group |
|||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numglobs.h" |
|||
#include "numconst.h" |
|||
#include "numenum.h" |
|||
|
|||
/* Forward Declarations */ |
|||
void GLOBputGlobals(GLOBvalues *); |
|||
void GLOBgetGlobals(GLOBvalues *); |
|||
void GLOBprnGlobals(FILE *, GLOBvalues *); |
|||
|
|||
/* Global Variable Declarations |
|||
char *LogFileName = "cider.log"; |
|||
|
|||
int BandGapNarrowing; |
|||
int TempDepMobility, ConcDepMobility, FieldDepMobility, TransDepMobility; |
|||
int SurfaceMobility, MatchingMobility, MobDeriv; |
|||
int CCScattering; |
|||
int Srh, Auger, ConcDepLifetime, AvalancheGen; |
|||
int FreezeOut = FALSE; |
|||
int OneCarrier; |
|||
|
|||
int MaxIterations = 100; |
|||
int AcAnalysisMethod = DIRECT; |
|||
|
|||
double Temp, RelTemp, Vt, RefPsi; |
|||
double EpsNorm, VNorm, NNorm, LNorm, TNorm, JNorm, GNorm, ENorm; |
|||
RefPsi is the potential at Infinity */ |
|||
|
|||
/* |
|||
* Compute global values for this device. |
|||
*/ |
|||
void GLOBcomputeGlobals(GLOBvalues *pGlobals, double temp) |
|||
/* GLOBvalues *pGlobals Global Parameter Data Structure */ |
|||
/* double temp Instance Temperature */ |
|||
{ |
|||
double tmp1; |
|||
double mnSi, mpSi; /* electron and hole conduction mass */ |
|||
double eg0; /* band gap */ |
|||
double nc0, nv0; /* conduction/valence band states */ |
|||
|
|||
/* compute temp. dependent global parameters */ |
|||
Temp = temp; |
|||
RelTemp = Temp / 300.0; |
|||
tmp1 = pow( RelTemp, 1.5 ); |
|||
|
|||
Vt = BOLTZMANN_CONSTANT * Temp / CHARGE; |
|||
eg0 = EGAP300_SI + DGAPDT_SI * ( (300.0 * 300.0) / (300.0 + TREF_EG_SI) |
|||
- (Temp * Temp) / (Temp + TREF_EG_SI) ); |
|||
mnSi = 1.039 + 5.477e-4 * Temp - 2.326e-7 * Temp * Temp; |
|||
mpSi = 0.262 * log( 0.259 * Temp ); |
|||
nc0 = NCV_NOM * pow( mnSi, 1.5 ) * tmp1; |
|||
nv0 = NCV_NOM * pow( mpSi, 1.5 ) * tmp1; |
|||
RefPsi = 0.0; |
|||
|
|||
/* set up the normalization factors */ |
|||
EpsNorm = EPS_SI; |
|||
VNorm = Vt; |
|||
NNorm = sqrt( nc0 ) * sqrt( nv0 ); /* this way no overflow */ |
|||
LNorm = sqrt( ( VNorm * EpsNorm ) / ( CHARGE * NNorm ) ); |
|||
TNorm = LNorm * LNorm / VNorm; |
|||
JNorm = CHARGE * NNorm * VNorm / LNorm; |
|||
GNorm = JNorm / VNorm; |
|||
ENorm = VNorm / LNorm; |
|||
|
|||
RefPsi /= VNorm; |
|||
|
|||
/* Save Globals */ |
|||
GLOBputGlobals( pGlobals ); |
|||
/* |
|||
* GLOBprnGlobals( stdout, pGlobals ); |
|||
*/ |
|||
|
|||
} |
|||
|
|||
void GLOBputGlobals(GLOBvalues *values) |
|||
{ |
|||
if ( values == NIL(GLOBvalues) ) { |
|||
fprintf( stderr, "Error: tried to get from NIL GLOBvalues\n"); |
|||
exit(-1); |
|||
} |
|||
|
|||
/* Temperature-related globals */ |
|||
values->Temp = Temp; |
|||
values->RelTemp = RelTemp; |
|||
values->Vt = Vt; |
|||
values->RefPsi = RefPsi; |
|||
|
|||
/* Normalization Factors */ |
|||
values->EpsNorm = EpsNorm; |
|||
values->VNorm = VNorm; |
|||
values->NNorm = NNorm; |
|||
values->LNorm = LNorm; |
|||
values->TNorm = TNorm; |
|||
values->JNorm = JNorm; |
|||
values->GNorm = GNorm; |
|||
values->ENorm = ENorm; |
|||
|
|||
return; |
|||
} |
|||
|
|||
/* |
|||
* Reload all globals needed during DEV loading routines |
|||
* and DEV output routines |
|||
*/ |
|||
void GLOBgetGlobals(GLOBvalues *values) |
|||
{ |
|||
if ( values == NIL(GLOBvalues) ) { |
|||
fprintf( stderr, "Error: tried to get from NIL GLOBvalues\n"); |
|||
exit(-1); |
|||
} |
|||
|
|||
/* Temperature-related globals */ |
|||
Temp = values->Temp; |
|||
RelTemp = values->RelTemp; |
|||
Vt = values->Vt; |
|||
RefPsi = values->RefPsi; |
|||
|
|||
/* Normalization Factors */ |
|||
EpsNorm = values->EpsNorm; |
|||
VNorm = values->VNorm; |
|||
NNorm = values->NNorm; |
|||
LNorm = values->LNorm; |
|||
TNorm = values->TNorm; |
|||
JNorm = values->JNorm; |
|||
GNorm = values->GNorm; |
|||
ENorm = values->ENorm; |
|||
|
|||
return; |
|||
} |
|||
|
|||
void GLOBprnGlobals(FILE *file, GLOBvalues *values) |
|||
{ |
|||
static char *tabformat = "%12s: % .4e %-12s\t"; |
|||
static char *newformat = "%12s: % .4e %-12s\n"; |
|||
|
|||
if ( values == NIL( GLOBvalues ) ) { |
|||
fprintf( stderr, "Error: tried to print NIL GLOBvalues\n"); |
|||
exit(-1); |
|||
} |
|||
fprintf( file, "*** GLOBAL PARAMETERS AT %g deg K\n", values->Temp ); |
|||
fprintf( file, "****** Temperature-Dependent Voltages\n" ); |
|||
fprintf( file, tabformat, "Vt", values->Vt, "V" ); |
|||
fprintf( file, newformat, "RefPsi", values->RefPsi * values->VNorm, "V" ); |
|||
fprintf( file, "****** Normalization Factors\n" ); |
|||
fprintf( file, newformat, "EpsNorm", values->EpsNorm, "F/cm" ); |
|||
fprintf( file, newformat, "VNorm", values->VNorm, "V" ); |
|||
fprintf( file, newformat, "NNorm", values->NNorm, "/cm^3" ); |
|||
fprintf( file, newformat, "LNorm", values->LNorm, "cm" ); |
|||
fprintf( file, newformat, "TNorm", values->TNorm, "s" ); |
|||
fprintf( file, newformat, "JNorm", values->JNorm, "A/cm^2" ); |
|||
fprintf( file, newformat, "GNorm", values->GNorm, "A/V" ); |
|||
fprintf( file, newformat, "ENorm", values->ENorm, "V/cm" ); |
|||
|
|||
return; |
|||
} |
|||
@ -0,0 +1,155 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numenum.h" |
|||
|
|||
/* compute the coefficient for the integration and predictor methods */ |
|||
/* based on the Lagrange polynomial method in Liniger et. al. */ |
|||
|
|||
void |
|||
computeIntegCoeff(int method, int order, double *intCoeff, double *delta) |
|||
{ |
|||
int i, j, k; |
|||
double sum, temp, preMult; |
|||
double num, denom, prod; |
|||
|
|||
switch( method ) { |
|||
case BDF: |
|||
/* determine coeff[0] first */ |
|||
|
|||
sum = 0.0; |
|||
temp = 0.0; |
|||
for( j = 0 ; j < order; j++ ) { |
|||
temp += delta[ j ]; |
|||
sum += 1.0 / temp; |
|||
} |
|||
intCoeff[ 0 ] = sum; |
|||
|
|||
/* now compute the higher order coefficients */ |
|||
for( j = 1; j <= order ; j++ ) { |
|||
/* compute the pre multiplier */ |
|||
temp = 0.0; |
|||
for( i = 0; i < j; i++ ) { |
|||
temp += delta[ i ]; |
|||
} |
|||
preMult = 1.0 / temp; |
|||
prod = 1.0; |
|||
/* now compute the product */ |
|||
for( i = 1; i <= order; i++ ) { |
|||
/* product loop */ |
|||
if( i != j ) { |
|||
num = 0.0; |
|||
for( k = 0; k < i; k++ ) { |
|||
/* first the numerator */ |
|||
num += delta[ k ]; |
|||
} |
|||
if( i > j ) { |
|||
/* denom is positive */ |
|||
denom = 0.0; |
|||
for( k = j; k < i; k++ ) { |
|||
denom += delta[ k ]; |
|||
} |
|||
} |
|||
else { |
|||
/* i < j */ |
|||
denom = 0.0; |
|||
for( k = i; k < j; k++ ) { |
|||
denom += delta[ k ]; |
|||
} |
|||
denom = -denom; |
|||
} |
|||
prod *= num / denom ; |
|||
} |
|||
} |
|||
intCoeff[ j ] = -preMult * prod; |
|||
} |
|||
break; |
|||
case TRAPEZOIDAL: |
|||
default: |
|||
switch( order ) { |
|||
case 1: |
|||
temp = 1.0 / delta[ 0 ]; |
|||
intCoeff[ 0 ] = temp; |
|||
intCoeff[ 1 ] = -temp; |
|||
break; |
|||
case 2: |
|||
temp = 2.0 / delta[ 0 ]; |
|||
intCoeff[ 0 ] = temp; |
|||
intCoeff[ 1 ] = -temp; |
|||
intCoeff[ 2 ] = -1.0; |
|||
break; |
|||
} |
|||
break; |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
void |
|||
computePredCoeff(int method, int order, double *predCoeff, double *delta) |
|||
{ |
|||
int i, j, k; |
|||
double num, denom, prod, temp; |
|||
|
|||
if( method == TRAPEZOIDAL && order > 2 ) { |
|||
printf("\n computePredCoeff: order > 2 for trapezoidal"); |
|||
exit( -1 ); |
|||
} |
|||
for( j = 1; j <= order+1 ; j++ ) { |
|||
prod = 1.0; |
|||
/* now compute the product */ |
|||
for( i = 1; i <= order+1; i++ ) { |
|||
/* product loop */ |
|||
if( i != j ) { |
|||
num = 0.0; |
|||
for( k = 0; k < i; k++ ) { |
|||
/* first the numerator */ |
|||
num += delta[ k ]; |
|||
} |
|||
if( i > j ) { |
|||
/* denom is positive */ |
|||
denom = 0.0; |
|||
for( k = j; k < i; k++ ) { |
|||
denom += delta[ k ]; |
|||
} |
|||
} |
|||
else { |
|||
/* i < j */ |
|||
denom = 0.0; |
|||
for( k = i; k < j; k++ ) { |
|||
denom += delta[ k ]; |
|||
} |
|||
denom = -denom; |
|||
} |
|||
prod *= num / denom ; |
|||
} |
|||
} |
|||
predCoeff[ j - 1 ] = prod; |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
/* main program to check the coefficients |
|||
main() |
|||
{ |
|||
double intCoeff[ 7 ], predCoeff[ 7 ]; |
|||
double delta[ 7 ]; |
|||
int order = 1; |
|||
int i; |
|||
|
|||
for( i = 0; i <= 6; i++ ) { |
|||
delta[ i ] = 1.0; |
|||
} |
|||
|
|||
computeIntegCoeff(TRAPEZOIDAL, order, intCoeff, delta ); |
|||
computePredCoeff(TRAPEZOIDAL, order, predCoeff, delta ); |
|||
|
|||
for(i = 0; i <= order; i++ ) { |
|||
printf("\n IntCoeff[ %d ] = %e PredCoeff[ %d ] = %e ", i, intCoeff[ i ], i, predCoeff[ i ] ); |
|||
} |
|||
} |
|||
*/ |
|||
@ -0,0 +1,302 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numglobs.h" |
|||
#include "numenum.h" |
|||
#include "gendev.h" |
|||
|
|||
/* function to compute the integrated variables discretization */ |
|||
|
|||
#define ccap qcap+1 |
|||
|
|||
double |
|||
integrate(double **devStates, TranInfo *info, int qcap ) |
|||
{ |
|||
double value; |
|||
double *coeff = info->intCoeff; |
|||
|
|||
switch ( info->method ) { |
|||
case BDF: |
|||
switch( info->order ) { |
|||
case 1: |
|||
value = coeff[0] * (*(devStates[0]+qcap)) + |
|||
coeff[1] * (*(devStates[1]+qcap)); |
|||
break; |
|||
case 2: |
|||
value = coeff[0] * (*(devStates[0]+qcap)) + |
|||
coeff[1] * (*(devStates[1]+qcap)) + |
|||
coeff[2] * (*(devStates[2]+qcap)); |
|||
break; |
|||
case 3: |
|||
value = coeff[0] * (*(devStates[0]+qcap)) + |
|||
coeff[1] * (*(devStates[1]+qcap)) + |
|||
coeff[2] * (*(devStates[2]+qcap)) + |
|||
coeff[3] * (*(devStates[3]+qcap)); |
|||
break; |
|||
case 4: |
|||
value = coeff[0] * (*(devStates[0]+qcap)) + |
|||
coeff[1] * (*(devStates[1]+qcap)) + |
|||
coeff[2] * (*(devStates[2]+qcap)) + |
|||
coeff[3] * (*(devStates[3]+qcap)) + |
|||
coeff[4] * (*(devStates[4]+qcap)); |
|||
break; |
|||
case 5: |
|||
value = coeff[0] * (*(devStates[0]+qcap)) + |
|||
coeff[1] * (*(devStates[1]+qcap)) + |
|||
coeff[2] * (*(devStates[2]+qcap)) + |
|||
coeff[3] * (*(devStates[3]+qcap)) + |
|||
coeff[4] * (*(devStates[4]+qcap)) + |
|||
coeff[5] * (*(devStates[5]+qcap)); |
|||
break; |
|||
case 6: |
|||
value = coeff[0] * (*(devStates[0]+qcap)) + |
|||
coeff[1] * (*(devStates[1]+qcap)) + |
|||
coeff[2] * (*(devStates[2]+qcap)) + |
|||
coeff[3] * (*(devStates[3]+qcap)) + |
|||
coeff[4] * (*(devStates[4]+qcap)) + |
|||
coeff[5] * (*(devStates[5]+qcap)) + |
|||
coeff[6] * (*(devStates[6]+qcap)); |
|||
break; |
|||
default: |
|||
printf( "\n integration order %d !! STOP \n", info->order ); |
|||
exit( 0 ); |
|||
} |
|||
break; |
|||
case TRAPEZOIDAL: |
|||
default: |
|||
switch( info->order ) { |
|||
case 1: |
|||
value = coeff[0] * (*(devStates[0]+qcap)) + |
|||
coeff[1] * (*(devStates[1]+qcap)); |
|||
*(devStates[0]+ccap) = value; |
|||
break; |
|||
case 2: |
|||
value = coeff[0] * (*(devStates[0]+qcap)) + |
|||
coeff[1] * (*(devStates[1]+qcap)) + |
|||
coeff[2] * (*(devStates[1]+ccap)); |
|||
*(devStates[0]+ccap) = value; |
|||
break; |
|||
default: |
|||
printf( "\n integration order %d !! STOP \n", info->order ); |
|||
exit( 0 ); |
|||
} |
|||
break; |
|||
} |
|||
|
|||
return( value ); |
|||
} |
|||
|
|||
/* function to predict the value of the variables */ |
|||
|
|||
double |
|||
predict(double **devStates, TranInfo *info, int qcap ) |
|||
{ |
|||
double value; |
|||
double *coeff = info->predCoeff; |
|||
|
|||
switch ( info->method ) { |
|||
case BDF: |
|||
switch( info->order ) { |
|||
case 1: |
|||
value = coeff[0] * (*(devStates[1]+qcap)) + |
|||
coeff[1] * (*(devStates[2]+qcap)); |
|||
break; |
|||
case 2: |
|||
value = coeff[0] * (*(devStates[1]+qcap)) + |
|||
coeff[1] * (*(devStates[2]+qcap)) + |
|||
coeff[2] * (*(devStates[3]+qcap)); |
|||
break; |
|||
case 3: |
|||
value = coeff[0] * (*(devStates[1]+qcap)) + |
|||
coeff[1] * (*(devStates[2]+qcap)) + |
|||
coeff[2] * (*(devStates[3]+qcap)) + |
|||
coeff[3] * (*(devStates[4]+qcap)); |
|||
break; |
|||
case 4: |
|||
value = coeff[0] * (*(devStates[1]+qcap)) + |
|||
coeff[1] * (*(devStates[2]+qcap)) + |
|||
coeff[2] * (*(devStates[3]+qcap)) + |
|||
coeff[3] * (*(devStates[4]+qcap)) + |
|||
coeff[4] * (*(devStates[5]+qcap)); |
|||
break; |
|||
case 5: |
|||
value = coeff[0] * (*(devStates[1]+qcap)) + |
|||
coeff[1] * (*(devStates[2]+qcap)) + |
|||
coeff[2] * (*(devStates[3]+qcap)) + |
|||
coeff[3] * (*(devStates[4]+qcap)) + |
|||
coeff[4] * (*(devStates[5]+qcap)) + |
|||
coeff[5] * (*(devStates[6]+qcap)); |
|||
break; |
|||
case 6: |
|||
value = coeff[0] * (*(devStates[1]+qcap)) + |
|||
coeff[1] * (*(devStates[2]+qcap)) + |
|||
coeff[2] * (*(devStates[3]+qcap)) + |
|||
coeff[3] * (*(devStates[4]+qcap)) + |
|||
coeff[4] * (*(devStates[5]+qcap)) + |
|||
coeff[5] * (*(devStates[6]+qcap)) + |
|||
coeff[6] * (*(devStates[7]+qcap)); |
|||
break; |
|||
default: |
|||
printf( "\n prediction order %d !! STOP \n", info->order ); |
|||
exit( 0 ); |
|||
} |
|||
break; |
|||
case TRAPEZOIDAL: |
|||
default: |
|||
switch( info->order ) { |
|||
case 1: |
|||
value = coeff[0] * (*(devStates[1]+qcap)) + |
|||
coeff[1] * (*(devStates[2]+qcap)); |
|||
break; |
|||
case 2: |
|||
value = coeff[0] * (*(devStates[1]+qcap)) + |
|||
coeff[1] * (*(devStates[2]+qcap)) + |
|||
coeff[2] * (*(devStates[3]+qcap)); |
|||
/* |
|||
value = coeff[0] * (*(devStates[1]+qcap)) + |
|||
coeff[1] * (*(devStates[2]+qcap)) + |
|||
coeff[2] * (*(devStates[1]+ccap)); |
|||
*/ |
|||
break; |
|||
default: |
|||
printf( "\n prediction order %d !! STOP \n", info->order ); |
|||
exit( 0 ); |
|||
} |
|||
break; |
|||
} |
|||
|
|||
return( value ); |
|||
} |
|||
|
|||
double |
|||
computeLTECoeff( TranInfo *info ) |
|||
{ |
|||
double *delta = info->delta; |
|||
double temp, denom, lteCoeff; |
|||
|
|||
switch ( info->method ) { |
|||
case BDF: |
|||
switch( info->order ) { |
|||
case 1: |
|||
denom = delta[ 0 ] + delta[ 1 ]; |
|||
break; |
|||
case 2: |
|||
denom = delta[ 0 ] + delta[ 1 ] + delta[ 2 ]; |
|||
break; |
|||
case 3: |
|||
denom = delta[ 0 ] + delta[ 1 ] + delta[ 2 ] + |
|||
delta[ 3 ]; |
|||
break; |
|||
case 4: |
|||
denom = delta[ 0 ] + delta[ 1 ] + delta[ 2 ] + |
|||
delta[ 3 ] + delta[ 4 ]; |
|||
break; |
|||
case 5: |
|||
denom = delta[ 0 ] + delta[ 1 ] + delta[ 2 ] + |
|||
delta[ 3 ] + delta[ 4 ] + delta[ 5 ]; |
|||
break; |
|||
case 6: |
|||
denom = delta[ 0 ] + delta[ 1 ] + delta[ 2 ] + |
|||
delta[ 3 ] + delta[ 4 ] + delta[ 5 ] + delta[ 6 ]; |
|||
break; |
|||
default: |
|||
printf( "\n integration order %d !! STOP \n", info->order ); |
|||
exit( 0 ); |
|||
break; |
|||
} |
|||
break; |
|||
case TRAPEZOIDAL: |
|||
default: |
|||
switch( info->order ) { |
|||
case 1: |
|||
denom = delta[ 0 ] + delta[ 1 ]; |
|||
break; |
|||
case 2: |
|||
/* |
|||
denom = delta[ 0 ] + delta[ 1 ]; |
|||
*/ |
|||
temp = delta[ 0 ] + delta [ 1 ]; |
|||
denom = 2.0 * temp * (temp + delta[ 2 ]) / delta[ 0 ]; |
|||
break; |
|||
default: |
|||
printf( "\n integration order %d !! STOP \n", info->order ); |
|||
exit( 0 ); |
|||
break; |
|||
} |
|||
break; |
|||
} |
|||
lteCoeff = delta[ 0 ] / denom; |
|||
return( lteCoeff ); |
|||
} |
|||
|
|||
/* function to integrate a linear DAE */ |
|||
double |
|||
integrateLin(double **devStates, TranInfo *info, int qcap ) |
|||
{ |
|||
double value; |
|||
double *coeff = info->intCoeff; |
|||
|
|||
switch ( info->method ) { |
|||
case BDF: |
|||
switch( info->order ) { |
|||
case 1: |
|||
value = coeff[1] * (*(devStates[1]+qcap)); |
|||
break; |
|||
case 2: |
|||
value = coeff[1] * (*(devStates[1]+qcap)) + |
|||
coeff[2] * (*(devStates[2]+qcap)); |
|||
break; |
|||
case 3: |
|||
value = coeff[1] * (*(devStates[1]+qcap)) + |
|||
coeff[2] * (*(devStates[2]+qcap)) + |
|||
coeff[3] * (*(devStates[3]+qcap)); |
|||
break; |
|||
case 4: |
|||
value = coeff[1] * (*(devStates[1]+qcap)) + |
|||
coeff[2] * (*(devStates[2]+qcap)) + |
|||
coeff[3] * (*(devStates[3]+qcap)) + |
|||
coeff[4] * (*(devStates[4]+qcap)); |
|||
break; |
|||
case 5: |
|||
value = coeff[1] * (*(devStates[1]+qcap)) + |
|||
coeff[2] * (*(devStates[2]+qcap)) + |
|||
coeff[3] * (*(devStates[3]+qcap)) + |
|||
coeff[4] * (*(devStates[4]+qcap)) + |
|||
coeff[5] * (*(devStates[5]+qcap)); |
|||
break; |
|||
case 6: |
|||
value = coeff[1] * (*(devStates[1]+qcap)) + |
|||
coeff[2] * (*(devStates[2]+qcap)) + |
|||
coeff[3] * (*(devStates[3]+qcap)) + |
|||
coeff[4] * (*(devStates[4]+qcap)) + |
|||
coeff[5] * (*(devStates[5]+qcap)) + |
|||
coeff[6] * (*(devStates[6]+qcap)); |
|||
break; |
|||
default: |
|||
printf( "\n integration order %d !! STOP \n", info->order ); |
|||
exit( 0 ); |
|||
} |
|||
break; |
|||
case TRAPEZOIDAL: |
|||
default: |
|||
switch( info->order ) { |
|||
case 1: |
|||
value = coeff[1] * (*(devStates[1]+qcap)); |
|||
break; |
|||
case 2: |
|||
value = coeff[1] * (*(devStates[1]+qcap)) + |
|||
coeff[2] * (*(devStates[1]+ccap)); |
|||
break; |
|||
default: |
|||
printf( "\n integration order %d !! STOP \n", info->order ); |
|||
exit( 0 ); |
|||
} |
|||
break; |
|||
} |
|||
|
|||
return( value ); |
|||
} |
|||
|
|||
@ -0,0 +1,41 @@ |
|||
/********** |
|||
Copyright 1992 Regents of the University of California. All rights reserved. |
|||
Author: 1992 David A. Gates, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
|
|||
static char *LogFileName = "cider.log"; |
|||
static int LogError = 0; |
|||
|
|||
void |
|||
LOGmakeEntry(name, description) |
|||
char *name; |
|||
char *description; |
|||
{ |
|||
int procStamp; |
|||
FILE *fpLog; |
|||
|
|||
#ifdef HAS_GETPID |
|||
procStamp = getpid(); |
|||
#else |
|||
procStamp = 0; |
|||
#endif |
|||
|
|||
/* Want to make sure that multiple processes can access the log file |
|||
* without stepping on each other. |
|||
*/ |
|||
#ifdef ultrix |
|||
if (!(fpLog = fopen(LogFileName, "A"))) { |
|||
#else |
|||
if (!(fpLog = fopen(LogFileName, "a"))) { |
|||
#endif |
|||
if (!LogError) |
|||
perror(LogFileName); |
|||
LogError = 1; |
|||
} else { |
|||
fprintf(fpLog, "<%05d> %s: %s\n", procStamp, name, description); |
|||
fclose(fpLog); |
|||
LogError = 0; |
|||
} |
|||
} |
|||
@ -0,0 +1,404 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group |
|||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numglobs.h" |
|||
#include "numconst.h" |
|||
#include "numenum.h" |
|||
#include "material.h" |
|||
|
|||
/* Forward Declarations */ |
|||
void printMaterialInfo(MaterialInfo *info); |
|||
|
|||
/* External Symbols */ |
|||
extern void MOBtempDep (MaterialInfo *, double); |
|||
|
|||
|
|||
/* |
|||
* Set material info values to their defaults. |
|||
*/ |
|||
void |
|||
MATLdefaults(MaterialInfo *info) |
|||
{ |
|||
if ((info->material == OXIDE) || (info->material == INSULATOR)) { |
|||
info->type = INSULATOR; |
|||
info->eps = EPS_OX; |
|||
info->affin = AFFIN_OX; |
|||
info->eg0 = EGAP300_OX; |
|||
} else if (info->material == NITRIDE) { |
|||
info->type = INSULATOR; |
|||
info->eps = EPS_NI; |
|||
info->affin = AFFIN_NI; |
|||
info->eg0 = EGAP300_NI; |
|||
} else if (info->material == POLYSILICON) { |
|||
info->type = SEMICON; |
|||
info->eps = EPS_SI; |
|||
info->affin = AFFIN_SI; |
|||
info->nc0 = 0.0; |
|||
info->nv0 = 0.0; |
|||
info->eg0 = EGAP300_SI; |
|||
info->dEgDt = DGAPDT_SI; |
|||
info->trefBGN = TREF_EG_SI; |
|||
info->dEgDn[ELEC] = DGAPDN_N; |
|||
info->dEgDn[HOLE] = DGAPDN_P; |
|||
info->nrefBGN[ELEC] = NBGN_N; |
|||
info->nrefBGN[HOLE] = NBGN_P; |
|||
info->tau0[ELEC] = TAU0_N_SI; |
|||
info->tau0[HOLE] = TAU0_P_SI; |
|||
info->nrefSRH[ELEC] = NSRH_N_SI; |
|||
info->nrefSRH[HOLE] = NSRH_P_SI; |
|||
info->cAug[ELEC] = C_AUG_N_SI; |
|||
info->cAug[HOLE] = C_AUG_P_SI; |
|||
info->aRich[ELEC] = A_RICH_N_SI; |
|||
info->aRich[HOLE] = A_RICH_P_SI; |
|||
info->eDon = E_DON_SI; |
|||
info->eAcc = E_ACC_SI; |
|||
info->gDon = G_DON_SI; |
|||
info->gAcc = G_ACC_SI; |
|||
info->concModel = CT; |
|||
info->muMax[ELEC][MAJOR] = 0.07 * AR_MUMAX_N; |
|||
info->muMin[ELEC][MAJOR] = 0.07 * AR_MUMIN_N; |
|||
info->ntRef[ELEC][MAJOR] = AR_NTREF_N; |
|||
info->ntExp[ELEC][MAJOR] = AR_NTEXP_N; |
|||
info->muMax[HOLE][MAJOR] = 0.07 * AR_MUMAX_P; |
|||
info->muMin[HOLE][MAJOR] = 0.07 * AR_MUMIN_P; |
|||
info->ntRef[HOLE][MAJOR] = AR_NTREF_P; |
|||
info->ntExp[HOLE][MAJOR] = AR_NTEXP_P; |
|||
info->muMax[ELEC][MINOR] = 0.07 * UF_MUMAX_N; |
|||
info->muMin[ELEC][MINOR] = 0.07 * UF_MUMIN_N; |
|||
info->ntRef[ELEC][MINOR] = UF_NTREF_N; |
|||
info->ntExp[ELEC][MINOR] = UF_NTEXP_N; |
|||
info->muMax[HOLE][MINOR] = 0.07 * UF_MUMAX_P; |
|||
info->muMin[HOLE][MINOR] = 0.07 * UF_MUMIN_P; |
|||
info->ntRef[HOLE][MINOR] = UF_NTREF_P; |
|||
info->ntExp[HOLE][MINOR] = UF_NTEXP_P; |
|||
info->fieldModel = CT; |
|||
info->vSat[ELEC] = AR_VSAT_N; |
|||
info->vSat[HOLE] = AR_VSAT_P; |
|||
info->vWarm[ELEC] = SG_VWARM_N; |
|||
info->vWarm[HOLE] = SG_VWARM_P; |
|||
info->mus[ELEC] = 0.07 * MUS_N; |
|||
info->thetaA[ELEC] = THETAA_N; |
|||
info->thetaB[ELEC] = THETAB_N; |
|||
info->mus[HOLE] = 0.07 * MUS_P; |
|||
info->thetaA[HOLE] = THETAA_P; |
|||
info->thetaB[HOLE] = THETAB_P; |
|||
} else if ((info->material == SILICON) || (info->material == SEMICON)) { |
|||
info->type = SEMICON; |
|||
info->eps = EPS_SI; |
|||
info->affin = AFFIN_SI; |
|||
info->nc0 = 0.0; |
|||
info->nv0 = 0.0; |
|||
info->eg0 = EGAP300_SI; |
|||
info->dEgDt = DGAPDT_SI; |
|||
info->trefBGN = TREF_EG_SI; |
|||
info->dEgDn[ELEC] = DGAPDN_N; |
|||
info->dEgDn[HOLE] = DGAPDN_P; |
|||
info->nrefBGN[ELEC] = NBGN_N; |
|||
info->nrefBGN[HOLE] = NBGN_P; |
|||
info->tau0[ELEC] = TAU0_N_SI; |
|||
info->tau0[HOLE] = TAU0_P_SI; |
|||
info->nrefSRH[ELEC] = NSRH_N_SI; |
|||
info->nrefSRH[HOLE] = NSRH_P_SI; |
|||
info->cAug[ELEC] = C_AUG_N_SI; |
|||
info->cAug[HOLE] = C_AUG_P_SI; |
|||
info->aRich[ELEC] = A_RICH_N_SI; |
|||
info->aRich[HOLE] = A_RICH_P_SI; |
|||
info->eDon = E_DON_SI; |
|||
info->eAcc = E_ACC_SI; |
|||
info->gDon = G_DON_SI; |
|||
info->gAcc = G_ACC_SI; |
|||
info->concModel = CT; |
|||
info->muMax[ELEC][MAJOR] = AR_MUMAX_N; |
|||
info->muMin[ELEC][MAJOR] = AR_MUMIN_N; |
|||
info->ntRef[ELEC][MAJOR] = AR_NTREF_N; |
|||
info->ntExp[ELEC][MAJOR] = AR_NTEXP_N; |
|||
info->muMax[HOLE][MAJOR] = AR_MUMAX_P; |
|||
info->muMin[HOLE][MAJOR] = AR_MUMIN_P; |
|||
info->ntRef[HOLE][MAJOR] = AR_NTREF_P; |
|||
info->ntExp[HOLE][MAJOR] = AR_NTEXP_P; |
|||
info->muMax[ELEC][MINOR] = UF_MUMAX_N; |
|||
info->muMin[ELEC][MINOR] = UF_MUMIN_N; |
|||
info->ntRef[ELEC][MINOR] = UF_NTREF_N; |
|||
info->ntExp[ELEC][MINOR] = UF_NTEXP_N; |
|||
info->muMax[HOLE][MINOR] = UF_MUMAX_P; |
|||
info->muMin[HOLE][MINOR] = UF_MUMIN_P; |
|||
info->ntRef[HOLE][MINOR] = UF_NTREF_P; |
|||
info->ntExp[HOLE][MINOR] = UF_NTEXP_P; |
|||
info->fieldModel = CT; |
|||
info->vSat[ELEC] = AR_VSAT_N; |
|||
info->vSat[HOLE] = AR_VSAT_P; |
|||
info->vWarm[ELEC] = SG_VWARM_N; |
|||
info->vWarm[HOLE] = SG_VWARM_P; |
|||
info->mus[ELEC] = MUS_N; |
|||
info->thetaA[ELEC] = THETAA_N; |
|||
info->thetaB[ELEC] = THETAB_N; |
|||
info->mus[HOLE] = MUS_P; |
|||
info->thetaA[HOLE] = THETAA_P; |
|||
info->thetaB[HOLE] = THETAB_P; |
|||
} else if (info->material == GAAS) { |
|||
info->type = SEMICON; |
|||
info->eps = EPS_GA; |
|||
info->affin = AFFIN_GA; |
|||
info->nc0 = NCV_NOM * pow(M_N_GA, 1.5); |
|||
info->nv0 = NCV_NOM * pow(M_P_GA, 1.5); |
|||
info->eg0 = EGAP300_GA; |
|||
info->dEgDt = DGAPDT_GA; |
|||
info->trefBGN = TREF_EG_GA; |
|||
info->dEgDn[ELEC] = DGAPDN_N; |
|||
info->dEgDn[HOLE] = DGAPDN_P; |
|||
info->nrefBGN[ELEC] = NBGN_N; |
|||
info->nrefBGN[HOLE] = NBGN_P; |
|||
info->tau0[ELEC] = TAU0_N_GA; |
|||
info->tau0[HOLE] = TAU0_P_GA; |
|||
info->nrefSRH[ELEC] = NSRH_N_GA; |
|||
info->nrefSRH[HOLE] = NSRH_P_GA; |
|||
info->cAug[ELEC] = C_AUG_N_GA; |
|||
info->cAug[HOLE] = C_AUG_P_GA; |
|||
info->aRich[ELEC] = A_RICH_N_GA; |
|||
info->aRich[HOLE] = A_RICH_P_GA; |
|||
info->eDon = E_DON_GA; |
|||
info->eAcc = E_ACC_GA; |
|||
info->gDon = G_DON_GA; |
|||
info->gAcc = G_ACC_GA; |
|||
info->concModel = GA; |
|||
info->muMax[ELEC][MAJOR] = GA_MUMAX_N; |
|||
info->muMin[ELEC][MAJOR] = GA_MUMIN_N; |
|||
info->ntRef[ELEC][MAJOR] = GA_NTREF_N; |
|||
info->ntExp[ELEC][MAJOR] = GA_NTEXP_N; |
|||
info->muMax[HOLE][MAJOR] = GA_MUMAX_P; |
|||
info->muMin[HOLE][MAJOR] = GA_MUMIN_P; |
|||
info->ntRef[HOLE][MAJOR] = GA_NTREF_P; |
|||
info->ntExp[HOLE][MAJOR] = GA_NTEXP_P; |
|||
info->muMax[ELEC][MINOR] = GA_MUMAX_N; |
|||
info->muMin[ELEC][MINOR] = GA_MUMIN_N; |
|||
info->ntRef[ELEC][MINOR] = GA_NTREF_N; |
|||
info->ntExp[ELEC][MINOR] = GA_NTEXP_N; |
|||
info->muMax[HOLE][MINOR] = GA_MUMAX_P; |
|||
info->muMin[HOLE][MINOR] = GA_MUMIN_P; |
|||
info->ntRef[HOLE][MINOR] = GA_NTREF_P; |
|||
info->ntExp[HOLE][MINOR] = GA_NTEXP_P; |
|||
info->fieldModel = GA; |
|||
info->vSat[ELEC] = GA_VSAT_N; |
|||
info->vSat[HOLE] = GA_VSAT_P; |
|||
info->vWarm[ELEC] = GA_VWARM_N; |
|||
info->vWarm[HOLE] = GA_VWARM_P; |
|||
info->mus[ELEC] = MUS_N; |
|||
info->thetaA[ELEC] = THETAA_N; |
|||
info->thetaB[ELEC] = THETAB_N; |
|||
info->mus[HOLE] = MUS_P; |
|||
info->thetaA[HOLE] = THETAA_P; |
|||
info->thetaB[HOLE] = THETAB_P; |
|||
} |
|||
} |
|||
|
|||
/* |
|||
* Compute the temperature-dependent physical parameters of materials |
|||
* Normalize physical constants Actual Instance Temperature is passed in thru |
|||
* the global var 'Temp' |
|||
*/ |
|||
void |
|||
MATLtempDep(MaterialInfo *info, double tnom) |
|||
/* double tnom Nominal Parameter Temperature */ |
|||
{ |
|||
double tmp1; |
|||
double relTemp, perRelTemp; |
|||
double eg0; |
|||
|
|||
if (info->type == INSULATOR) { |
|||
info->refPsi = RefPsi - (info->affin + 0.5 * info->eg0) / VNorm; |
|||
} else if (info->type == SEMICON) { |
|||
|
|||
/* compute temperature dependent semiconductor parameters */ |
|||
relTemp = Temp / tnom; |
|||
perRelTemp = 1.0 / relTemp; |
|||
tmp1 = pow(relTemp, 1.5); |
|||
|
|||
/* Bandgap and intrinsic concentration */ |
|||
eg0 = info->eg0 + (info->dEgDt * tnom * tnom) / (tnom + info->trefBGN); |
|||
info->eg0 = eg0 - (info->dEgDt * Temp * Temp) / (Temp + info->trefBGN); |
|||
if (info->nc0 > 0.0) { |
|||
info->mass[ELEC] = pow(info->nc0 / NCV_NOM / tmp1, 2.0 / 3.0); |
|||
} else { |
|||
info->mass[ELEC] = 1.039 + 5.477e-4 * Temp - 2.326e-7 * Temp * Temp; |
|||
} |
|||
if (info->nv0 > 0.0) { |
|||
info->mass[HOLE] = pow(info->nv0 / NCV_NOM / tmp1, 2.0 / 3.0); |
|||
} else { |
|||
info->mass[HOLE] = 0.262 * log(0.259 * Temp); |
|||
} |
|||
info->nc0 = NCV_NOM * pow(info->mass[ELEC], 1.5) * tmp1; |
|||
info->nv0 = NCV_NOM * pow(info->mass[HOLE], 1.5) * tmp1; |
|||
info->ni0 = sqrt(info->nc0) * sqrt(info->nv0) * |
|||
exp(-0.5 * info->eg0 / Vt); |
|||
info->refPsi = RefPsi - (info->affin |
|||
+ 0.5 * (info->eg0 + Vt * log(info->nc0 / info->nv0))) / VNorm; |
|||
|
|||
/* Impurity energies */ |
|||
info->eDon /= VNorm; |
|||
info->eAcc /= VNorm; |
|||
|
|||
/* SRH lifetimes */ |
|||
tmp1 = sqrt(perRelTemp) * exp(3.8667 * (perRelTemp - 1.0)); |
|||
info->tau0[ELEC] *= tmp1 / TNorm; |
|||
info->tau0[HOLE] *= tmp1 / TNorm; |
|||
|
|||
/* Auger recombination coefficients */ |
|||
info->cAug[ELEC] *= pow(relTemp, 0.14) * NNorm * NNorm * TNorm; |
|||
info->cAug[HOLE] *= pow(relTemp, 0.18) * NNorm * NNorm * TNorm; |
|||
|
|||
/* Avalanche generation parameters */ |
|||
info->aii[ELEC] = AII_N * LNorm; |
|||
info->bii[ELEC] = BII_N / ENorm; |
|||
info->aii[HOLE] = AII_P * LNorm; |
|||
info->bii[HOLE] = BII_P / ENorm; |
|||
|
|||
/* Effective recombination velocities */ |
|||
info->vRich[ELEC] = info->aRich[ELEC] * Temp * Temp / |
|||
(CHARGE * info->nc0 * ENorm); |
|||
info->vRich[HOLE] = info->aRich[HOLE] * Temp * Temp / |
|||
(CHARGE * info->nv0 * ENorm); |
|||
|
|||
/* Mobility Temperature Dependence */ |
|||
MOBtempDep(info, Temp); |
|||
|
|||
/* Velocity Saturation Parameters */ |
|||
info->vSat[ELEC] /= ENorm; |
|||
info->vWarm[ELEC] /= ENorm; |
|||
info->vSat[HOLE] /= ENorm; |
|||
info->vWarm[HOLE] /= ENorm; |
|||
|
|||
/* Normal Field Mobility Degradation Parameters */ |
|||
info->thetaA[ELEC] *= ENorm; |
|||
info->thetaB[ELEC] *= ENorm * ENorm; |
|||
info->thetaA[HOLE] *= ENorm; |
|||
info->thetaB[HOLE] *= ENorm * ENorm; |
|||
} |
|||
} |
|||
|
|||
void |
|||
printMaterialInfo(MaterialInfo *info) |
|||
{ |
|||
static char *tabformat = "%12s: % .4e %-12s\t"; |
|||
static char *newformat = "%12s: % .4e %-12s\n"; |
|||
|
|||
char *name; |
|||
|
|||
|
|||
if (info == NIL(MaterialInfo)) { |
|||
fprintf(stderr, "Error: tried to print NIL MaterialInfo\n"); |
|||
exit(-1); |
|||
} |
|||
/* Find material name. */ |
|||
switch (info->material) { |
|||
case OXIDE: |
|||
name = "OXIDE"; |
|||
break; |
|||
case NITRIDE: |
|||
name = "NITRIDE"; |
|||
break; |
|||
case INSULATOR: |
|||
name = "INSULATOR"; |
|||
break; |
|||
case SILICON: |
|||
name = "SILICON"; |
|||
break; |
|||
case POLYSILICON: |
|||
name = "POLYSILICON"; |
|||
break; |
|||
case GAAS: |
|||
name = "GAAS"; |
|||
break; |
|||
case SEMICON: |
|||
name = "SEMICONDUCTOR"; |
|||
break; |
|||
default: |
|||
name = "MATERIAL"; |
|||
break; |
|||
} |
|||
if (info->type == INSULATOR) { |
|||
fprintf(stdout, "***** %s PARAMETERS AT %g deg K\n", name, Temp); |
|||
fprintf(stdout, "*** Poisson Equation Parameters -\n"); |
|||
fprintf(stdout, tabformat, "Eps", info->eps, "F/cm"); |
|||
fprintf(stdout, newformat, "Affin", info->affin, "eV"); |
|||
fprintf(stdout, tabformat, "Egap", info->eg0, "eV"); |
|||
fprintf(stdout, newformat, "PsiB", -info->refPsi * VNorm, "V"); |
|||
} else if (info->type == SEMICON) { |
|||
fprintf(stdout, "***** %s PARAMETERS AT %g deg K\n", name, Temp); |
|||
fprintf(stdout, "*** Poisson Equation\n"); |
|||
fprintf(stdout, tabformat, "Eps", info->eps, "F/cm"); |
|||
fprintf(stdout, newformat, "Affin", info->affin, "eV"); |
|||
fprintf(stdout, tabformat, "Vt", Vt, "V"); |
|||
fprintf(stdout, newformat, "Ni", info->ni0, "/cm^3"); |
|||
fprintf(stdout, tabformat, "Nc", info->nc0, "/cm^3"); |
|||
fprintf(stdout, newformat, "Nv", info->nv0, "/cm^3"); |
|||
fprintf(stdout, tabformat, "MnSi", info->mass[ELEC], "*m0 kg"); |
|||
fprintf(stdout, newformat, "MpSi", info->mass[HOLE], "*m0 kg"); |
|||
fprintf(stdout, tabformat, "Egap", info->eg0, "eV"); |
|||
fprintf(stdout, newformat, "PsiB", -info->refPsi * VNorm, "V"); |
|||
fprintf(stdout, tabformat, "dEg/dT", info->dEgDt, "eV"); |
|||
fprintf(stdout, newformat, "Tref", info->trefBGN, "deg K"); |
|||
fprintf(stdout, tabformat, "dEg/dN", info->dEgDn[ELEC], "eV"); |
|||
fprintf(stdout, newformat, "Nref", info->nrefBGN[ELEC], "/cm^3"); |
|||
fprintf(stdout, tabformat, "dEg/dP", info->dEgDn[HOLE], "eV"); |
|||
fprintf(stdout, newformat, "Pref", info->nrefBGN[HOLE], "/cm^3"); |
|||
fprintf(stdout, tabformat, "Edon", info->eDon * VNorm, "eV"); |
|||
fprintf(stdout, newformat, "Eacc", info->eAcc * VNorm, "eV"); |
|||
fprintf(stdout, tabformat, "Gdon", info->gDon, ""); |
|||
fprintf(stdout, newformat, "Gacc", info->gAcc, ""); |
|||
fprintf(stdout, "*** Generation - Recombination\n"); |
|||
fprintf(stdout, tabformat, "Tn0", info->tau0[ELEC] * TNorm, "s"); |
|||
fprintf(stdout, newformat, "Tp0", info->tau0[HOLE] * TNorm, "s"); |
|||
fprintf(stdout, tabformat, "CnAug", |
|||
info->cAug[ELEC] / (NNorm * NNorm * TNorm), "cm^6/s"); |
|||
fprintf(stdout, newformat, "CpAug", |
|||
info->cAug[HOLE] / (NNorm * NNorm * TNorm), "cm^6/s"); |
|||
fprintf(stdout, tabformat, "Aiin", info->aii[ELEC] / LNorm, "/cm"); |
|||
fprintf(stdout, newformat, "Aiip", info->aii[HOLE] / LNorm, "/cm"); |
|||
fprintf(stdout, tabformat, "Biin", info->bii[ELEC] * ENorm, "V/cm"); |
|||
fprintf(stdout, newformat, "Biip", info->bii[HOLE] * ENorm, "V/cm"); |
|||
fprintf(stdout, "*** Thermionic Emission\n"); |
|||
fprintf(stdout, tabformat, "Arichn", info->aRich[ELEC], "A/cm^2/oK^2"); |
|||
fprintf(stdout, newformat, "Arichp", info->aRich[HOLE], "A/cm^2/oK^2"); |
|||
fprintf(stdout, tabformat, "Vrichn", info->vRich[ELEC] * ENorm, "cm/s"); |
|||
fprintf(stdout, newformat, "Vrichp", info->vRich[HOLE] * ENorm, "cm/s"); |
|||
fprintf(stdout, "*** Majority Carrier Mobility\n"); |
|||
fprintf(stdout, tabformat, "MunMax", |
|||
info->muMax[ELEC][MAJOR], "cm^2/V-s"); |
|||
fprintf(stdout, newformat, "MupMax", |
|||
info->muMax[HOLE][MAJOR], "cm^2/V-s"); |
|||
fprintf(stdout, tabformat, "MunMin", |
|||
info->muMin[ELEC][MAJOR], "cm^2/V-s"); |
|||
fprintf(stdout, newformat, "MupMin", |
|||
info->muMin[HOLE][MAJOR], "cm^2/V-s"); |
|||
fprintf(stdout, "*** Minority Carrier Mobility\n"); |
|||
fprintf(stdout, tabformat, "MunMax", |
|||
info->muMax[ELEC][MINOR], "cm^2/V-s"); |
|||
fprintf(stdout, newformat, "MupMax", |
|||
info->muMax[HOLE][MINOR], "cm^2/V-s"); |
|||
fprintf(stdout, tabformat, "MunMin", |
|||
info->muMin[ELEC][MINOR], "cm^2/V-s"); |
|||
fprintf(stdout, newformat, "MupMin", |
|||
info->muMin[HOLE][MINOR], "cm^2/V-s"); |
|||
fprintf(stdout, "*** Surface Mobility\n"); |
|||
fprintf(stdout, tabformat, "Muns", info->mus[ELEC], "cm^2/V-s"); |
|||
fprintf(stdout, newformat, "Mups", info->mus[HOLE], "cm^2/V-s"); |
|||
fprintf(stdout, tabformat, "ThetaAN", info->thetaA[ELEC] / ENorm, "cm/V"); |
|||
fprintf(stdout, newformat, "ThetaAP", info->thetaA[HOLE] / ENorm, "cm/V"); |
|||
fprintf(stdout, tabformat, "ThetaBN", |
|||
info->thetaB[ELEC] / ENorm / ENorm, "cm^2/V^2"); |
|||
fprintf(stdout, newformat, "ThetaBP", |
|||
info->thetaB[HOLE] / ENorm / ENorm, "cm^2/V^2"); |
|||
fprintf(stdout, "*** Velocity Saturation\n"); |
|||
fprintf(stdout, tabformat, "VsatN", info->vSat[ELEC] * ENorm, "cm/s"); |
|||
fprintf(stdout, newformat, "VsatP", info->vSat[HOLE] * ENorm, "cm/s"); |
|||
if (info->fieldModel == SG || info->fieldModel == GA) { |
|||
fprintf(stdout, tabformat, "VwarmN", info->vWarm[ELEC] * ENorm, "cm/s"); |
|||
fprintf(stdout, newformat, "VwarmP", info->vWarm[HOLE] * ENorm, "cm/s"); |
|||
} |
|||
} |
|||
return; |
|||
} |
|||
@ -0,0 +1,161 @@ |
|||
/********** |
|||
Copyright 1992 Regents of the University of California. All rights reserved. |
|||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group |
|||
Author: 1992 David A. Gates, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
/* |
|||
* Miscellaneous routines culled from the oned directory so that the twod |
|||
* code will run without having to compile the oned code |
|||
*/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numglobs.h" |
|||
#include "numenum.h" |
|||
#include "spMatrix.h" |
|||
|
|||
|
|||
/* Used in Solution Projection Calculations */ |
|||
double guessNewConc(double conc, double delta) |
|||
{ |
|||
BOOLEAN acceptable = FALSE; |
|||
double fib, newConc, lambda, fibn, fibp; |
|||
lambda = 1.0; |
|||
fibn = 1.0; |
|||
fibp = 1.0; |
|||
for ( ; !acceptable ; ) { |
|||
fib = fibp; |
|||
fibp = fibn; |
|||
fibn += fib; |
|||
lambda *= fibp / fibn; |
|||
newConc = conc + delta * lambda; |
|||
if( newConc > 0.0 ) { |
|||
acceptable = TRUE; |
|||
} |
|||
else { |
|||
/* newConc is still negative but fibp and fibn are large */ |
|||
if ( (fibp > 1e6) || (fibn > 1e6) ) { |
|||
acceptable = TRUE; |
|||
newConc = conc; |
|||
} |
|||
} |
|||
} |
|||
return( newConc ); |
|||
} |
|||
|
|||
/* Used in Doping Calculation */ |
|||
/* compute the concentration at x given an array of data. |
|||
* The data is stored in a two-dimensional array x, N(x). |
|||
* x is assumed to be in ascending order. given an x |
|||
* a search is performed to determine the data points closest |
|||
* to it and linear interpolation is performed to generate N(x) |
|||
*/ |
|||
|
|||
/* |
|||
#define LOGSUPREM |
|||
*/ |
|||
double lookup(double **dataTable, double x) |
|||
{ |
|||
double conc=0.0, x0, x1, y0, y1; |
|||
#ifdef LOGSUPREM |
|||
double lnconc, lny0, lny1; |
|||
#endif |
|||
int index, numPoints; |
|||
BOOLEAN done = FALSE; |
|||
|
|||
numPoints = dataTable[ 0 ][ 0 ]; |
|||
for( index = 2; index <= numPoints && (!done); index++ ) { |
|||
x1 = dataTable[ 0 ][ index ]; |
|||
/* check if x1 > x */ |
|||
if( x1 >= x ) { |
|||
/* found an x1 larger than x, so linear interpolate */ |
|||
x0 = dataTable[ 0 ][ index - 1 ]; |
|||
y0 = dataTable[ 1 ][ index - 1 ]; |
|||
y1 = dataTable[ 1 ][ index ]; |
|||
#ifdef LOGSUPREM |
|||
/* Ignore concentrations below 1.0 */ |
|||
if ( ABS(y0) < 1.0 ) |
|||
lny0 = 0.0; |
|||
else |
|||
lny0 = SGN(y0) * log( ABS(y0) ); |
|||
if ( ABS(y1) < 1.0 ) |
|||
lny1 = 0.0; |
|||
else |
|||
lny1 = SGN(y0) * log( ABS(y0) ); |
|||
lnconc = lny0 + (lny1 - lny0) * (x - x0) / (x1 - x0); |
|||
conc = SGN(lnconc) * exp( ABS(lnconc) ); |
|||
#else |
|||
conc = y0 + (y1 - y0) * (x - x0) / (x1 - x0); |
|||
#endif /* LOGSUPREM */ |
|||
done = TRUE; |
|||
} else { |
|||
if( index == numPoints ) { |
|||
/* set to concentration of last node - due to roundoff errors */ |
|||
conc = dataTable[ 1 ][ numPoints ]; |
|||
} |
|||
} |
|||
} |
|||
return ( conc ); |
|||
} |
|||
|
|||
/* Used in admittance calculations */ |
|||
/* this function returns TRUE is SOR iteration converges otherwise FALSE */ |
|||
|
|||
BOOLEAN hasSORConverged(double *oldSolution, double *newSolution, |
|||
int numEqns) |
|||
{ |
|||
BOOLEAN converged = TRUE; |
|||
int index; |
|||
double xOld, xNew, tol; |
|||
double absTol = 1e-12; |
|||
double relTol = 1e-3; |
|||
for( index = 1 ; index <= numEqns ; index++ ) { |
|||
xOld = oldSolution[ index ]; |
|||
xNew = newSolution[ index ]; |
|||
tol = absTol + relTol * MAX( ABS( xOld ), ABS( xNew )); |
|||
if( ABS( xOld - xNew ) > tol ) { |
|||
converged = FALSE; |
|||
printf("hasSORconverged failed\n"); |
|||
break; |
|||
} |
|||
} |
|||
return( converged ); |
|||
} |
|||
|
|||
/* Used to Check Sparse Matrix Errors */ |
|||
BOOLEAN |
|||
foundError(int error) |
|||
{ |
|||
BOOLEAN matrixError; |
|||
|
|||
switch( error ) { |
|||
/* Removed for Spice3e1 Compatibility |
|||
case spSMALL_PIVOT: |
|||
printf( "Warning: LU Decomposition Problem - SMALL PIVOT\n" ); |
|||
matrixError = FALSE; |
|||
break; |
|||
*/ |
|||
case spPANIC: |
|||
printf( "Error: LU Decomposition Failed - PANIC\n" ); |
|||
matrixError = TRUE; |
|||
break; |
|||
case spSINGULAR: |
|||
printf( "Error: LU Decomposition Failed - SINGULAR\n" ); |
|||
matrixError = TRUE; |
|||
break; |
|||
/* Removed for Spice3e1 Compatibility |
|||
case spZERO_DIAG: |
|||
printf( "Error: LU Decomposition Failed - ZERO PIVOT\n" ); |
|||
matrixError = TRUE; |
|||
break; |
|||
*/ |
|||
case spNO_MEMORY: |
|||
printf( "Error: LU Decomposition Failed - NO MEMORY\n" ); |
|||
matrixError = TRUE; |
|||
break; |
|||
default: |
|||
matrixError = FALSE; |
|||
break; |
|||
} |
|||
return( matrixError ); |
|||
} |
|||
@ -0,0 +1,421 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1992 David A. Gates, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numglobs.h" |
|||
#include "numconst.h" |
|||
#include "numenum.h" |
|||
#include "macros.h" |
|||
#include "material.h" |
|||
|
|||
void MOBdefaults(MaterialInfo *info , int carrier, int type, |
|||
int concmodel, int fieldmodel ) |
|||
{ |
|||
switch (concmodel) { |
|||
case CT: |
|||
info->concModel = CT; |
|||
if (carrier == ELEC) { |
|||
info->muMax[ELEC][type] = CT_MUMAX_N; |
|||
info->muMin[ELEC][type] = CT_MUMIN_N; |
|||
info->ntRef[ELEC][type] = CT_NTREF_N; |
|||
info->ntExp[ELEC][type] = CT_NTEXP_N; |
|||
} else { |
|||
info->muMax[HOLE][type] = CT_MUMAX_P; |
|||
info->muMin[HOLE][type] = CT_MUMIN_P; |
|||
info->ntRef[HOLE][type] = CT_NTREF_P; |
|||
info->ntExp[HOLE][type] = CT_NTEXP_P; |
|||
} |
|||
break; |
|||
case AR: |
|||
info->concModel = AR; |
|||
if (carrier == ELEC) { |
|||
info->muMax[ELEC][type] = AR_MUMAX_N; |
|||
info->muMin[ELEC][type] = AR_MUMIN_N; |
|||
info->ntRef[ELEC][type] = AR_NTREF_N; |
|||
info->ntExp[ELEC][type] = AR_NTEXP_N; |
|||
} else { |
|||
info->muMax[HOLE][type] = AR_MUMAX_P; |
|||
info->muMin[HOLE][type] = AR_MUMIN_P; |
|||
info->ntRef[HOLE][type] = AR_NTREF_P; |
|||
info->ntExp[HOLE][type] = AR_NTEXP_P; |
|||
} |
|||
break; |
|||
case UF: |
|||
info->concModel = UF; |
|||
if (carrier == ELEC) { |
|||
info->muMax[ELEC][type] = UF_MUMAX_N; |
|||
info->muMin[ELEC][type] = UF_MUMIN_N; |
|||
info->ntRef[ELEC][type] = UF_NTREF_N; |
|||
info->ntExp[ELEC][type] = UF_NTEXP_N; |
|||
} else { |
|||
info->muMax[HOLE][type] = UF_MUMAX_P; |
|||
info->muMin[HOLE][type] = UF_MUMIN_P; |
|||
info->ntRef[HOLE][type] = UF_NTREF_P; |
|||
info->ntExp[HOLE][type] = UF_NTEXP_P; |
|||
} |
|||
break; |
|||
case GA: |
|||
info->concModel = GA; |
|||
if (carrier == ELEC) { |
|||
info->muMax[ELEC][type] = GA_MUMAX_N; |
|||
info->muMin[ELEC][type] = GA_MUMIN_N; |
|||
info->ntRef[ELEC][type] = GA_NTREF_N; |
|||
info->ntExp[ELEC][type] = GA_NTEXP_N; |
|||
} else { |
|||
info->muMax[HOLE][type] = GA_MUMAX_P; |
|||
info->muMin[HOLE][type] = GA_MUMIN_P; |
|||
info->ntRef[HOLE][type] = GA_NTREF_P; |
|||
info->ntExp[HOLE][type] = GA_NTEXP_P; |
|||
} |
|||
break; |
|||
case SG: |
|||
default: |
|||
info->concModel = SG; |
|||
if (carrier == ELEC) { |
|||
info->muMax[ELEC][type] = SG_MUMAX_N; |
|||
info->muMin[ELEC][type] = SG_MUMIN_N; |
|||
info->ntRef[ELEC][type] = SG_NTREF_N; |
|||
info->ntExp[ELEC][type] = SG_NTEXP_N; |
|||
} else { |
|||
info->muMax[HOLE][type] = SG_MUMAX_P; |
|||
info->muMin[HOLE][type] = SG_MUMIN_P; |
|||
info->ntRef[HOLE][type] = SG_NTREF_P; |
|||
info->ntExp[HOLE][type] = SG_NTEXP_P; |
|||
} |
|||
break; |
|||
} |
|||
if (type == MAJOR) { |
|||
switch (fieldmodel) { |
|||
case CT: |
|||
info->fieldModel = CT; |
|||
if (carrier == ELEC) { |
|||
info->vSat[ELEC] = CT_VSAT_N; |
|||
} else { |
|||
info->vSat[HOLE] = CT_VSAT_P; |
|||
} |
|||
break; |
|||
case AR: |
|||
case UF: |
|||
info->fieldModel = AR; |
|||
if (carrier == ELEC) { |
|||
info->vSat[ELEC] = AR_VSAT_N; |
|||
} else { |
|||
info->vSat[HOLE] = AR_VSAT_P; |
|||
} |
|||
break; |
|||
case GA: |
|||
info->fieldModel = GA; |
|||
if (carrier == ELEC) { |
|||
info->vSat[ELEC] = GA_VSAT_N; |
|||
info->vWarm[ELEC] = GA_VWARM_N; |
|||
} else { |
|||
info->vSat[HOLE] = GA_VSAT_P; |
|||
info->vWarm[HOLE] = GA_VWARM_P; |
|||
} |
|||
break; |
|||
case SG: |
|||
default: |
|||
info->fieldModel = SG; |
|||
if (carrier == ELEC) { |
|||
info->vSat[ELEC] = SG_VSAT_N; |
|||
info->vWarm[ELEC] = SG_VWARM_N; |
|||
} else { |
|||
info->vSat[HOLE] = SG_VSAT_P; |
|||
info->vWarm[HOLE] = SG_VWARM_P; |
|||
} |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
void |
|||
MOBtempDep (MaterialInfo *info, double temp) |
|||
{ |
|||
double relTemp = temp / 300.0; |
|||
double factor, muMin, muMax, mu0; |
|||
|
|||
/* Modify if necessary. */ |
|||
if (TempDepMobility) |
|||
{ |
|||
/* Do concentration dependence parameters */ |
|||
muMin = info->muMin[ELEC][MAJOR]; |
|||
mu0 = info->muMax[ELEC][MAJOR] - muMin; |
|||
factor = pow(relTemp, TD_EXPMUMIN_N); |
|||
muMin *= factor; |
|||
factor = pow(relTemp, TD_EXPMUMAX_N); |
|||
mu0 *= factor; |
|||
info->muMin[ELEC][MAJOR] = muMin; |
|||
info->muMax[ELEC][MAJOR] = mu0 + muMin; |
|||
factor = pow(relTemp, TD_EXPNTREF_N); |
|||
info->ntRef[ELEC][MAJOR] *= factor; |
|||
factor = pow(relTemp, TD_EXPNTEXP_N); |
|||
info->ntExp[ELEC][MAJOR] *= factor; |
|||
|
|||
muMin = info->muMin[ELEC][MINOR]; |
|||
mu0 = info->muMax[ELEC][MINOR] - muMin; |
|||
factor = pow(relTemp, TD_EXPMUMIN_N); |
|||
muMin *= factor; |
|||
factor = pow(relTemp, TD_EXPMUMAX_N); |
|||
mu0 *= factor; |
|||
info->muMin[ELEC][MINOR] = muMin; |
|||
info->muMax[ELEC][MINOR] = mu0 + muMin; |
|||
factor = pow(relTemp, TD_EXPNTREF_N); |
|||
info->ntRef[ELEC][MINOR] *= factor; |
|||
factor = pow(relTemp, TD_EXPNTEXP_N); |
|||
info->ntExp[ELEC][MINOR] *= factor; |
|||
|
|||
muMin = info->muMin[HOLE][MAJOR]; |
|||
mu0 = info->muMax[HOLE][MAJOR] - muMin; |
|||
factor = pow(relTemp, TD_EXPMUMIN_P); |
|||
muMin *= factor; |
|||
factor = pow(relTemp, TD_EXPMUMAX_P); |
|||
mu0 *= factor; |
|||
info->muMin[HOLE][MAJOR] = muMin; |
|||
info->muMax[HOLE][MAJOR] = mu0 + muMin; |
|||
factor = pow(relTemp, TD_EXPNTREF_P); |
|||
info->ntRef[HOLE][MAJOR] *= factor; |
|||
factor = pow(relTemp, TD_EXPNTEXP_P); |
|||
info->ntExp[HOLE][MAJOR] *= factor; |
|||
|
|||
muMin = info->muMin[HOLE][MINOR]; |
|||
mu0 = info->muMax[HOLE][MINOR] - muMin; |
|||
factor = pow(relTemp, TD_EXPMUMIN_P); |
|||
muMin *= factor; |
|||
factor = pow(relTemp, TD_EXPMUMAX_P); |
|||
mu0 *= factor; |
|||
info->muMin[HOLE][MINOR] = muMin; |
|||
info->muMax[HOLE][MINOR] = mu0 + muMin; |
|||
factor = pow(relTemp, TD_EXPNTREF_P); |
|||
info->ntRef[HOLE][MINOR] *= factor; |
|||
factor = pow(relTemp, TD_EXPNTEXP_P); |
|||
info->ntExp[HOLE][MINOR] *= factor; |
|||
|
|||
/* Modify field dependence parameters */ |
|||
/* Assume warm carrier reference velocity has same temperature dep. */ |
|||
factor = sqrt( tanh( TD_TREFVS_N / Temp ) ); |
|||
info->vSat[ELEC] *= factor; |
|||
info->vWarm[ELEC] *= factor; |
|||
factor = sqrt( tanh( TD_TREFVS_P / Temp ) ); |
|||
info->vSat[HOLE] *= factor; |
|||
info->vWarm[HOLE] *= factor; |
|||
} |
|||
} |
|||
|
|||
void |
|||
MOBconcDep (MaterialInfo *info, double conc, double *pMun, double *pMup) |
|||
{ |
|||
double s; |
|||
|
|||
/* We have to check sign of conc even when concentration dependence |
|||
* is not used because it affects whether carriers are majority or |
|||
* minority carriers. Ideally, the minority/majority carrier models |
|||
* should agree at 0.0 concentration, but often they'll be inconsistent. |
|||
*/ |
|||
|
|||
if (conc >= 0.0) |
|||
{ /* N type */ |
|||
if (ConcDepMobility) |
|||
{ |
|||
switch (info->concModel) |
|||
{ |
|||
case CT: |
|||
case AR: |
|||
case UF: |
|||
case GA: |
|||
*pMun = info->muMin[ELEC][MAJOR] + |
|||
(info->muMax[ELEC][MAJOR] - info->muMin[ELEC][MAJOR]) / |
|||
(1.0 + pow(conc / info->ntRef[ELEC][MAJOR], |
|||
info->ntExp[ELEC][MAJOR])); |
|||
|
|||
*pMup = info->muMin[HOLE][MINOR] + |
|||
(info->muMax[HOLE][MINOR] - info->muMin[HOLE][MINOR]) / |
|||
(1.0 + pow(conc / info->ntRef[HOLE][MINOR], |
|||
info->ntExp[HOLE][MINOR])); |
|||
break; |
|||
case SG: |
|||
default: |
|||
s = info->muMax[ELEC][MAJOR] / info->muMin[ELEC][MAJOR]; |
|||
s = pow(s, 1.0 / info->ntExp[ELEC][MAJOR]) - 1; |
|||
*pMun = info->muMax[ELEC][MAJOR] / |
|||
pow(1.0 + conc / (conc / s + info->ntRef[ELEC][MAJOR]), |
|||
info->ntExp[ELEC][MAJOR]); |
|||
|
|||
s = info->muMax[HOLE][MINOR] / info->muMin[HOLE][MINOR]; |
|||
s = pow(s, 1.0 / info->ntExp[HOLE][MINOR]) - 1; |
|||
*pMup = info->muMax[HOLE][MINOR] / |
|||
pow(1.0 + conc / (conc / s + info->ntRef[HOLE][MINOR]), |
|||
info->ntExp[HOLE][MINOR]); |
|||
break; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
*pMun = info->muMax[ELEC][MAJOR]; |
|||
*pMup = info->muMax[HOLE][MINOR]; |
|||
} |
|||
} |
|||
else |
|||
{ /* P type */ |
|||
if (ConcDepMobility) |
|||
{ |
|||
conc = -conc; /* Take absolute value. */ |
|||
switch (info->concModel) |
|||
{ |
|||
case CT: |
|||
case AR: |
|||
case UF: |
|||
case GA: |
|||
*pMun = info->muMin[ELEC][MINOR] + |
|||
(info->muMax[ELEC][MINOR] - info->muMin[ELEC][MINOR]) / |
|||
(1.0 + pow(conc / info->ntRef[ELEC][MINOR], |
|||
info->ntExp[ELEC][MINOR])); |
|||
|
|||
*pMup = info->muMin[HOLE][MAJOR] + |
|||
(info->muMax[HOLE][MAJOR] - info->muMin[HOLE][MAJOR]) / |
|||
(1.0 + pow(conc / info->ntRef[HOLE][MAJOR], |
|||
info->ntExp[HOLE][MAJOR])); |
|||
break; |
|||
case SG: |
|||
default: |
|||
s = info->muMax[ELEC][MINOR] / info->muMin[ELEC][MINOR]; |
|||
s = pow(s, 1.0 / info->ntExp[ELEC][MINOR]) - 1; |
|||
*pMun = info->muMax[ELEC][MINOR] / |
|||
pow(1.0 + conc / (conc / s + info->ntRef[ELEC][MINOR]), |
|||
info->ntExp[ELEC][MINOR]); |
|||
|
|||
s = info->muMax[HOLE][MAJOR] / info->muMin[HOLE][MAJOR]; |
|||
s = pow(s, 1.0 / info->ntExp[HOLE][MAJOR]) - 1; |
|||
*pMup = info->muMax[HOLE][MAJOR] / |
|||
pow(1.0 + conc / (conc / s + info->ntRef[HOLE][MAJOR]), |
|||
info->ntExp[HOLE][MAJOR]); |
|||
break; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
*pMun = info->muMax[ELEC][MINOR]; |
|||
*pMup = info->muMax[HOLE][MAJOR]; |
|||
} |
|||
} |
|||
return; |
|||
} |
|||
|
|||
|
|||
void |
|||
MOBfieldDep (MaterialInfo *info, int carrier, double field, double *pMu, |
|||
double *pDMu) |
|||
{ |
|||
double eLateral, mu; |
|||
double sgnL; |
|||
double temp1, temp2, temp3, temp4, temp5, temp6; |
|||
double dMuDEl; /* Lateral Field Derivative */ |
|||
|
|||
/* Quick check to make sure we really belong here. */ |
|||
if (!FieldDepMobility) /* XXX Global */ |
|||
return; |
|||
|
|||
sgnL = SGN (field); |
|||
eLateral = ABS (field); |
|||
mu = *pMu; /* Grab temp. and conc.-dep. mobility */ |
|||
|
|||
|
|||
if (carrier == ELEC) |
|||
{ |
|||
switch (info->fieldModel) |
|||
{ |
|||
case CT: |
|||
case AR: |
|||
case UF: |
|||
temp1 = mu / info->vSat[ELEC]; |
|||
temp2 = temp1 * eLateral; |
|||
temp3 = 1.0 / (1.0 + temp2 * temp2); |
|||
mu *= sqrt(temp3); |
|||
dMuDEl = -sgnL * mu * temp3 * temp2 * temp1; |
|||
|
|||
|
|||
break; |
|||
case GA: |
|||
temp1 = info->vSat[ELEC] / info->vWarm[ELEC]; /* Vsat / Vwarm */ |
|||
temp2 = mu / info->vWarm[ELEC]; |
|||
temp3 = temp2 * eLateral; /* Vdrift / Vwarm */ |
|||
temp4 = temp3 * temp3 * temp3; |
|||
temp5 = 1.0 + temp1 * temp4; |
|||
temp6 = 1.0 / (1.0 + temp3 * temp4); |
|||
mu *= temp5 * temp6; |
|||
dMuDEl = - sgnL * mu * temp2 * |
|||
(4.0 * temp4 * temp6 - 3.0 * temp1 * temp3 * temp3 / temp5 ); |
|||
|
|||
/* |
|||
dMuDEl = 0.0; |
|||
*/ |
|||
break; |
|||
case SG: |
|||
default: |
|||
temp1 = mu / info->vSat[ELEC]; |
|||
temp2 = temp1 * eLateral;/* Vdrift / Vsat */ |
|||
temp3 = mu / info->vWarm[ELEC]; |
|||
temp4 = temp3 * eLateral;/* Vdrift / Vwarm */ |
|||
temp5 = temp4 / (temp4 + SG_FIT_N); |
|||
temp6 = 1.0 / (1.0 + temp4 * temp5 + temp2 * temp2); |
|||
mu *= sqrt(temp6); |
|||
dMuDEl = -sgnL * 0.5 * mu * temp6 * |
|||
(temp5 * (2.0 - temp5) * temp3 + (2.0 * temp2 * temp1)); |
|||
|
|||
|
|||
break; |
|||
} |
|||
} |
|||
else |
|||
{ /* Hole Mobility */ |
|||
switch (info->fieldModel) |
|||
{ |
|||
case CT: |
|||
case AR: |
|||
case UF: |
|||
temp1 = mu / info->vSat[HOLE]; |
|||
temp2 = temp1 * eLateral; |
|||
temp3 = 1.0 / (1.0 + temp2); |
|||
mu *= temp3; |
|||
dMuDEl = -sgnL * mu * temp3 * temp1; |
|||
|
|||
break; |
|||
case GA: |
|||
temp1 = info->vSat[HOLE] / info->vWarm[HOLE]; /* Vsat / Vwarm */ |
|||
temp2 = mu / info->vWarm[HOLE]; |
|||
temp3 = temp2 * eLateral; /* Vdrift / Vwarm */ |
|||
temp4 = temp3 * temp3 * temp3; |
|||
temp5 = 1.0 + temp1 * temp4; |
|||
temp6 = 1.0 / (1.0 + temp3 * temp4); |
|||
mu *= temp5 * temp6; |
|||
dMuDEl = - sgnL * mu * temp2 * |
|||
(4.0 * temp4 * temp6 - 3.0 * temp1 * temp3 * temp3 / temp5 ); |
|||
|
|||
|
|||
/* |
|||
dMuDEl = 0.0; |
|||
*/ |
|||
break; |
|||
case SG: |
|||
default: |
|||
temp1 = mu / info->vSat[HOLE]; |
|||
temp2 = temp1 * eLateral;/* Vdrift / Vsat */ |
|||
temp3 = mu / info->vWarm[HOLE]; |
|||
temp4 = temp3 * eLateral;/* Vdrift / Vwarm */ |
|||
temp5 = temp4 / (temp4 + SG_FIT_P); |
|||
temp6 = 1.0 / (1.0 + temp4 * temp5 + temp2 * temp2); |
|||
mu *= sqrt(temp6); |
|||
dMuDEl = -sgnL * 0.5 * mu * temp6 * |
|||
(temp5 * (2.0 - temp5) * temp3 + (2.0 * temp2 * temp1)); |
|||
|
|||
|
|||
break; |
|||
} |
|||
} |
|||
|
|||
*pMu = mu; |
|||
*pDMu = dMuDEl; |
|||
|
|||
return; |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
Directory: support |
|||
------------------ |
|||
This directory contains the files that support the device simulators and |
|||
their interfaces to the circuit simulators. They fall into four basic |
|||
areas: |
|||
1. Numerical algorithms: integration, accuracy limits, special functions |
|||
2. Semiconductor device physics: material properties, recombination, mobility |
|||
3. File access for input: doping profiles from SUPREMIII, saved states |
|||
4. Miscellaneous: diagnstic I/O, string routines |
|||
@ -0,0 +1,50 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numglobs.h" |
|||
|
|||
/* |
|||
* function recomb calculates the recobination rates and the |
|||
* derivatives with respect to n and p. SRH recombination is |
|||
* always assumed. Auger recombination is specified by the flag |
|||
* 'Auger' which by default is false. |
|||
*/ |
|||
|
|||
void |
|||
recomb (double ni, double pi, double tn, double tp, double cn, double cp, |
|||
double nie, double *pUnet, double *pDuDn, double *pDuDp) |
|||
{ |
|||
double uSrh, uSrhNum, uSrhDen, perUdenSq, duSrhDn, duSrhDp; |
|||
double cncp, uAug, duAugDn, duAugDp, uNet, duDn, duDp; |
|||
|
|||
uSrhNum = ni * pi - nie * nie; |
|||
uSrhDen = tp * (ni + nie) + tn * (pi + nie); |
|||
uSrh = uSrhNum / uSrhDen; |
|||
perUdenSq = 1.0 / (uSrhDen * uSrhDen); |
|||
duSrhDn = (pi * uSrhDen - uSrhNum * tp) * perUdenSq; |
|||
duSrhDp = (ni * uSrhDen - uSrhNum * tn) * perUdenSq; |
|||
|
|||
|
|||
if (Auger && uSrhNum >= 0.0) { /* XXX Auger Global */ |
|||
cncp = cn * ni + cp * pi; |
|||
uAug = cncp * uSrhNum; |
|||
duAugDn = cn * uSrhNum + cncp * pi; |
|||
duAugDp = cp * uSrhNum + cncp * ni; |
|||
uNet = uSrh + uAug; |
|||
duDn = duSrhDn + duAugDn; |
|||
duDp = duSrhDp + duAugDp; |
|||
} |
|||
else { |
|||
uNet = uSrh; |
|||
duDn = duSrhDn; |
|||
duDp = duSrhDp; |
|||
} |
|||
|
|||
/* return uNet duDn duDp */ |
|||
*pUnet = uNet; |
|||
*pDuDn = duDn; |
|||
*pDuDp = duDp; |
|||
} |
|||
@ -0,0 +1,210 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group |
|||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
/* Functions to read SUPREM (Binary or Ascii) & ASCII input files */ |
|||
|
|||
#include "ngspice.h" |
|||
#include "profile.h" |
|||
|
|||
void |
|||
readAsciiData( char *fileName, int impType, DOPtable **ppTable ) |
|||
{ |
|||
FILE *fpAscii; |
|||
int index, i; |
|||
double x, y; |
|||
int numPoints; |
|||
DOPtable *tmpTable; |
|||
double **profileData; |
|||
double sign; |
|||
|
|||
/* Open Input File */ |
|||
if (!(fpAscii = fopen( fileName, "r" ))) { |
|||
perror( fileName ); |
|||
exit(-1); |
|||
} |
|||
|
|||
/* Get sign of concentrations */ |
|||
if (impType == IMP_P_TYPE) { |
|||
sign = -1.0; |
|||
} else { |
|||
sign = 1.0; |
|||
} |
|||
|
|||
/* read the number of points */ |
|||
fscanf( fpAscii, "%d", &numPoints ); |
|||
|
|||
/* allocate 2-D array to read in data of x-coordinate and N(x) */ |
|||
XCALLOC( profileData, double *, 2 ); |
|||
for( index = 0; index <= 1; index++ ) { |
|||
XCALLOC( profileData[ index ], double, 1 + numPoints ); |
|||
} |
|||
/* the number of points is stored as profileData[0][0] */ |
|||
profileData[0][0] = numPoints; |
|||
|
|||
for( index = 1; index <= numPoints; index++ ) { |
|||
fscanf( fpAscii, "%f %f ", &x, &y ); |
|||
profileData[ 0 ][ index ] = x; |
|||
profileData[ 1 ][ index ] = sign * ABS(y); |
|||
} |
|||
|
|||
/* Now create a new lookup table */ |
|||
XCALLOC( tmpTable, DOPtable, 1 ); |
|||
if ( *ppTable == NIL(DOPtable) ) { |
|||
/* First Entry */ |
|||
tmpTable->impId = 1; |
|||
tmpTable->dopData = profileData; |
|||
tmpTable->next = NIL(DOPtable); |
|||
*ppTable = tmpTable; |
|||
} else { |
|||
tmpTable->impId = (*ppTable)->impId + 1; |
|||
tmpTable->dopData = profileData; |
|||
tmpTable->next = *ppTable; |
|||
*ppTable = tmpTable; |
|||
} |
|||
|
|||
/* for debugging print the data that has been just read */ |
|||
/* |
|||
for( index = 1; index <= numPoints; index++ ) { |
|||
printf("\n %e %e", profileData[ 0 ][ index ], profileData[ 1 ][ index ]); |
|||
} |
|||
*/ |
|||
fclose(fpAscii); |
|||
return; |
|||
} |
|||
|
|||
/* interface routine based on notes provided by Steve Hansen of Stanford */ |
|||
|
|||
/* |
|||
* The material types are: |
|||
* 1 = single crystal silicon |
|||
* 2 = silicon dioxide |
|||
* 3 = poly-crystalline silicon |
|||
* 4 = silicon nitride |
|||
* 5 = aluminum |
|||
|
|||
* The impurity types are: |
|||
* 1 = boron |
|||
* 2 = phosphorus |
|||
* 3 = arsenic |
|||
* 4 = antimony |
|||
|
|||
* The crystalline orientations are: |
|||
* 1 = <111> |
|||
* 2 = <100> |
|||
* 3 = <110> |
|||
|
|||
* The layer thinkness, poly-crystalline grain size, node spacing and |
|||
* distance from the surface are all in microns. |
|||
|
|||
* The integrated dopant concentration and the phophorus implant dose are |
|||
* in atoms per square centimeter. |
|||
|
|||
* The interior polycrystalline grain concentration and the impurity |
|||
* concentrations at each node are in atoms per cubic centimeter. |
|||
*/ |
|||
|
|||
|
|||
void |
|||
readSupremData( fileName, fileType, impType, ppTable ) |
|||
char *fileName; |
|||
int fileType; |
|||
int impType; |
|||
DOPtable **ppTable; |
|||
{ |
|||
#define MAX_GRID 500 |
|||
float x[ MAX_GRID ], conc[ MAX_GRID ]; |
|||
|
|||
int index, i, j; |
|||
DOPtable *tmpTable; |
|||
double **profileData; |
|||
int numNodes; |
|||
char *colon, *impName; |
|||
|
|||
/* read the Suprem data file */ |
|||
if ( fileType == 0 ) { /* BINARY FILE */ |
|||
SUPbinRead( fileName, x, conc, &impType, &numNodes ); |
|||
} |
|||
else { |
|||
SUPascRead( fileName, x, conc, &impType, &numNodes ); |
|||
} |
|||
|
|||
/* allocate 2-D array to read in data of x-coordinate and N(x) */ |
|||
XCALLOC( profileData, double *, 2 ); |
|||
for( index = 0; index <= 1; index++ ) { |
|||
XCALLOC( profileData[ index ], double, 1 + numNodes ); |
|||
} |
|||
/* the number of points is stored as profileData[0][0] */ |
|||
profileData[0][0] = numNodes; |
|||
|
|||
for( index = 1; index <= numNodes; index++ ) { |
|||
profileData[ 0 ][ index ] = x[ index ]; |
|||
profileData[ 1 ][ index ] = conc[ index ]; |
|||
} |
|||
|
|||
/* Now create a new lookup table */ |
|||
XCALLOC( tmpTable, DOPtable, 1 ); |
|||
if ( *ppTable == NIL(DOPtable) ) { |
|||
/* First Entry */ |
|||
tmpTable->impId = 1; |
|||
tmpTable->dopData = profileData; |
|||
tmpTable->next = NIL(DOPtable); |
|||
*ppTable = tmpTable; |
|||
} else { |
|||
tmpTable->impId = (*ppTable)->impId + 1; |
|||
tmpTable->dopData = profileData; |
|||
tmpTable->next = *ppTable; |
|||
*ppTable = tmpTable; |
|||
} |
|||
|
|||
/* for debugging print the data that has been just read */ |
|||
/* |
|||
for( index = 1; index <= numNodes; index++ ) { |
|||
printf("%e %e\n", profileData[ 0 ][ index ], profileData[ 1 ][ index ]); |
|||
} |
|||
*/ |
|||
return; |
|||
} |
|||
|
|||
|
|||
/* main program to debug readSupremData */ |
|||
|
|||
/* |
|||
main(ac, av) |
|||
char **av; |
|||
{ |
|||
void readSupremData(); |
|||
DOPtable *supTable = NIL(DOPtable); |
|||
double **supInput; |
|||
int numPoints, index; |
|||
char *impName; |
|||
int impType; |
|||
|
|||
switch (ac) { |
|||
case 1: |
|||
printf( "Usage: %s suprem-file ...\n", av[0] ); |
|||
exit(-1); |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
|
|||
for ( index = 1; index < ac; index++ ) { |
|||
for ( impType=1; impType <= 4; impType++ ) { |
|||
readSupremData( av[index], 1, impType, &supTable ); |
|||
} |
|||
} |
|||
for ( ; supTable ISNOT NIL(DOPtable); supTable = supTable->next ) { |
|||
fprintf( stdout, "\"Impurity Number: %d\n", supTable->impId ); |
|||
supInput = supTable->dopData; |
|||
numPoints = supInput[0][0]; |
|||
for( index = 1; index <= numPoints; index++ ) { |
|||
printf("%e\t%e\n", |
|||
supInput[ 0 ][ index ], ABS(supInput[ 1 ][ index ]) + 1e-20 ); |
|||
} |
|||
fprintf( stdout, "\n" ); |
|||
} |
|||
} |
|||
*/ |
|||
@ -0,0 +1,357 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
/* |
|||
* Translated FORTRAN subroutine to read the SUPREM-3 binary file |
|||
* the data is read and stored in two arrays x and conc |
|||
*/ |
|||
|
|||
|
|||
#include "ngspice.h" |
|||
|
|||
#define MAXMAT 10 |
|||
#define MAXIMP 4 |
|||
#define MAXLAYER 10 |
|||
#define MAXGRID 500 |
|||
|
|||
#define GFREAD( fp, ptr, type, num ) if (num && (fread( ptr,\ |
|||
sizeof(type), (unsigned)num, fp ) != (unsigned)num)) {\ |
|||
return;\ |
|||
} |
|||
|
|||
#define DEBUG if (0) |
|||
|
|||
void |
|||
SUPbinRead( inFile, x, conc, impId, numNod ) |
|||
char *inFile; |
|||
float *x, *conc; |
|||
int *impId, *numNod; |
|||
{ |
|||
int idata, recordMark; |
|||
int ldata; |
|||
int i, j; |
|||
float rdata; |
|||
char cdata[21]; |
|||
int numLay, numImp, numGrid; |
|||
int impTyp[4], matTyp[10], topNod[10], siIndex, offset; |
|||
float xStart; |
|||
float layerTh[10]; |
|||
float con[500]; |
|||
FILE *fpSuprem; |
|||
|
|||
/* Clear Concentration Array */ |
|||
for ( i=0; i < MAXGRID; i++ ) { |
|||
conc[i] = 0.0; |
|||
} |
|||
|
|||
/* Open Input File */ |
|||
if (!(fpSuprem = fopen( inFile, "r" ))) { |
|||
perror(inFile); |
|||
return; |
|||
} |
|||
|
|||
/* |
|||
* The first record contains the number of layers (I4), the number of |
|||
* impurities (I4), and the number of nodes (I4) present in the structure. |
|||
*/ |
|||
GFREAD( fpSuprem, &recordMark, int, 1 ); |
|||
GFREAD( fpSuprem, &numLay, int, 1 ); |
|||
GFREAD( fpSuprem, &numImp, int, 1 ); |
|||
GFREAD( fpSuprem, &numGrid, int, 1 ); |
|||
DEBUG fprintf(stderr,"rec 1: %d %d %d\n", numLay, numImp, numGrid); |
|||
GFREAD( fpSuprem, &recordMark, int, 1 ); |
|||
|
|||
/* |
|||
* The second record contains, for each layer, the material type (I4), the |
|||
* layer thickness (R4), and the pointer to the top node of the layer (I4). |
|||
*/ |
|||
GFREAD( fpSuprem, &recordMark, int, 1 ); |
|||
for ( i=0; i < numLay; i++ ) { |
|||
GFREAD( fpSuprem, &matTyp[i], int, 1 ); |
|||
GFREAD( fpSuprem, &layerTh[i], float, 1 ); |
|||
GFREAD( fpSuprem, &topNod[i], int, 1 ); |
|||
DEBUG fprintf(stderr,"rec 2: %d %f %d\n", matTyp[i], layerTh[i], topNod[i] ); |
|||
} |
|||
GFREAD( fpSuprem, &recordMark, int, 1 ); |
|||
|
|||
/* |
|||
* The third record contains, for each layer, the material name (A20). |
|||
*/ |
|||
/* Put a null at the end */ |
|||
cdata[20] = '\0'; |
|||
GFREAD( fpSuprem, &recordMark, int, 1 ); |
|||
for ( i=0; i < numLay; i++ ) { |
|||
GFREAD( fpSuprem, cdata, char, 20 ); |
|||
DEBUG fprintf(stderr,"rec 3: %s\n", cdata ); |
|||
} |
|||
GFREAD( fpSuprem, &recordMark, int, 1 ); |
|||
|
|||
/* |
|||
* The fourth record contains, for each layer, the crystalline orientation |
|||
* (I4), and the poly-crystalline grain size in microns (R4). |
|||
*/ |
|||
GFREAD( fpSuprem, &recordMark, int, 1 ); |
|||
for ( i=0; i < numLay; i++ ) { |
|||
GFREAD( fpSuprem, &idata, int, 1 ); |
|||
GFREAD( fpSuprem, &rdata, float, 1 ); |
|||
DEBUG fprintf(stderr,"rec 4: %d %f\n", idata, rdata ); |
|||
} |
|||
GFREAD( fpSuprem, &recordMark, int, 1 ); |
|||
|
|||
/* |
|||
* The fifth record contains, for each impurity, the type of impurity (I4). |
|||
*/ |
|||
GFREAD( fpSuprem, &recordMark, int, 1 ); |
|||
for ( i=0; i < numImp; i++ ) { |
|||
GFREAD( fpSuprem, &impTyp[i], int, 1 ); |
|||
DEBUG fprintf(stderr,"rec 5: %d\n", impTyp[i] ); |
|||
} |
|||
GFREAD( fpSuprem, &recordMark, int, 1 ); |
|||
|
|||
/* |
|||
* The sixth record contains, for each impurity, the impurity name (A20). |
|||
*/ |
|||
GFREAD( fpSuprem, &recordMark, int, 1 ); |
|||
for ( i=0; i < numImp; i++ ) { |
|||
GFREAD( fpSuprem, cdata, char, 20 ); |
|||
DEBUG fprintf(stderr,"rec 6: %s\n", cdata ); |
|||
} |
|||
GFREAD( fpSuprem, &recordMark, int, 1 ); |
|||
|
|||
/* |
|||
* The seventh record contains, for each layer by each impurity, the |
|||
* integrated dopant (R4), and the interior concentration of the |
|||
* polysilicon grains (R4). |
|||
*/ |
|||
GFREAD( fpSuprem, &recordMark, int, 1 ); |
|||
for ( j=0; j < numLay; j++ ) { |
|||
for ( i=0; i < numImp; i++ ) { |
|||
GFREAD( fpSuprem, &rdata, float, 1 ); |
|||
DEBUG fprintf(stderr,"rec 7: %e", rdata ); |
|||
GFREAD( fpSuprem, &rdata, float, 1 ); |
|||
DEBUG fprintf(stderr," %e\n", rdata ); |
|||
} |
|||
} |
|||
GFREAD( fpSuprem, &recordMark, int, 1 ); |
|||
|
|||
/* |
|||
* The eighth record contains, for each node in the structure, the distance |
|||
* to the next deepest node (R4). |
|||
*/ |
|||
GFREAD( fpSuprem, &recordMark, int, 1 ); |
|||
for ( i=0; i < numGrid; i++ ) { |
|||
GFREAD( fpSuprem, &rdata, float, 1 ); |
|||
} |
|||
DEBUG fprintf(stderr,"rec 8: %f\n", rdata ); |
|||
GFREAD( fpSuprem, &recordMark, int, 1 ); |
|||
|
|||
/* |
|||
* The ninth record contains, for each node in the structure, the distance |
|||
* from the surface (R4). |
|||
*/ |
|||
GFREAD( fpSuprem, &recordMark, int, 1 ); |
|||
GFREAD( fpSuprem, &x[1], float, numGrid ); |
|||
DEBUG fprintf(stderr,"rec 9: %f\n", x[1] ); |
|||
GFREAD( fpSuprem, &recordMark, int, 1 ); |
|||
|
|||
/* |
|||
* Next, for each impurity there is a record containing the impurity's |
|||
* chemical concentration at each node (R4) and a record containing the |
|||
* impurity's active concentration at each node (R4). |
|||
*/ |
|||
|
|||
for ( j=0; j < numImp; j++ ) { |
|||
/* chemical concentration - not required */ |
|||
GFREAD( fpSuprem, &recordMark, int, 1 ); |
|||
GFREAD( fpSuprem, &con[1], float, numGrid ); |
|||
DEBUG fprintf(stderr,"rec 10: %e\n", con[1] ); |
|||
GFREAD( fpSuprem, &recordMark, int, 1 ); |
|||
|
|||
/* store active concentration */ |
|||
GFREAD( fpSuprem, &recordMark, int, 1 ); |
|||
GFREAD( fpSuprem, &con[1], float, numGrid ); |
|||
DEBUG fprintf(stderr,"rec 11: %e\n", con[1] ); |
|||
GFREAD( fpSuprem, &recordMark, int, 1 ); |
|||
|
|||
if (impTyp[j] == *impId) { |
|||
/*...Boron...*/ |
|||
if (impTyp[j] == 1) { |
|||
for ( i=1; i <= numGrid; i++ ) conc[i] = - con[i]; |
|||
} else { |
|||
/*...All Other Impurities: P, As, Sb ...*/ |
|||
for ( i=1; i <= numGrid; i++ ) conc[i] = con[i]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* |
|||
* The last record in the file contains some random stuff that might be |
|||
* useful to some people, the temperature in degrees Kelvin of the last |
|||
* diffusion step (R4), the phosphorus implant dose (R4), the arsenic |
|||
* implant flag (L4). |
|||
*/ |
|||
GFREAD( fpSuprem, &recordMark, int, 1 ); |
|||
GFREAD( fpSuprem, &rdata, float, 1 ); |
|||
DEBUG fprintf(stderr,"rec 12: %f", rdata ); |
|||
GFREAD( fpSuprem, &rdata, float, 1 ); |
|||
DEBUG fprintf(stderr," %e", rdata ); |
|||
GFREAD( fpSuprem, &ldata, int, 1 ); |
|||
DEBUG fprintf(stderr," %d\n", ldata ); |
|||
GFREAD( fpSuprem, &recordMark, int, 1 ); |
|||
|
|||
fclose( fpSuprem ); |
|||
|
|||
/* shift silicon layer to beginning of array */ |
|||
for ( j=0; j < numLay; j++ ) { |
|||
if (matTyp[ j ] == 1) { |
|||
siIndex = j; |
|||
} |
|||
} |
|||
offset = topNod[ siIndex ] - 1; |
|||
numGrid -= offset; |
|||
xStart = x[1 + offset]; |
|||
for ( i=1; i <= numGrid; i++ ) { |
|||
x[i] = x[i + offset] - xStart; |
|||
conc[i] = conc[i + offset]; |
|||
} |
|||
|
|||
/* Store number of valid nodes using pointer */ |
|||
*numNod = numGrid; |
|||
return; |
|||
} |
|||
|
|||
void |
|||
SUPascRead( inFile, x, conc, impId, numNod ) |
|||
char *inFile; |
|||
float *x, *conc; |
|||
int *impId, *numNod; |
|||
{ |
|||
int idata; |
|||
int ldata; |
|||
int i, j; |
|||
float rdata; |
|||
char cdata[21]; |
|||
int numLay, numImp, numGrid; |
|||
int impTyp[4], matTyp[10], topNod[10], siIndex, offset; |
|||
float xStart; |
|||
float layerTh[10]; |
|||
float con[500]; |
|||
FILE *fpSuprem; |
|||
|
|||
/* Clear Concentration Array */ |
|||
for ( i=0; i < MAXGRID; i++ ) { |
|||
conc[i] = 0.0; |
|||
} |
|||
|
|||
/* Open Input File */ |
|||
if (!(fpSuprem = fopen( inFile, "r" ))) { |
|||
perror(inFile); |
|||
return; |
|||
} |
|||
|
|||
/* |
|||
* The first line contains the number of layers (I4), the number of |
|||
* impurities (I4), and the number of nodes (I4) present in the structure. |
|||
*/ |
|||
fscanf( fpSuprem, "%d %d %d\n", &numLay, &numImp, &numGrid ); |
|||
DEBUG fprintf( stderr, "set 1: %d %d %d\n", numLay, numImp, numGrid); |
|||
|
|||
/* |
|||
* The second set of lines contains, for each layer, the material name (A20), |
|||
* the material type (I4), the layer thickness (R4), and the pointer to |
|||
* the top node of the layer (I4), and an unknown int and unknown float. |
|||
* The material type code: |
|||
* 1 - Si |
|||
* 2 - SiO2 |
|||
* 3 - Poly |
|||
* 4 - Si3N4 |
|||
* 5 - Alum |
|||
*/ |
|||
for ( i=0; i < numLay; i++ ) { |
|||
fscanf( fpSuprem, "%s\n %d %e %d %d %e\n", |
|||
cdata, &matTyp[i], &layerTh[i], &topNod[i], &idata, &rdata ); |
|||
DEBUG fprintf(stderr,"set 2: %s: %d %f %d\n", |
|||
cdata, matTyp[i], layerTh[i], topNod[i] ); |
|||
} |
|||
|
|||
/* |
|||
* The third set of lines contains, for each impurity, the name of the |
|||
* impurity (A20) and the type of impurity (I4). |
|||
*/ |
|||
for ( i=0; i < numImp; i++ ) { |
|||
fscanf( fpSuprem, "%s\n %d\n", cdata, &impTyp[i] ); |
|||
DEBUG fprintf(stderr,"set 3: %s: %d\n", cdata, impTyp[i] ); |
|||
} |
|||
|
|||
/* |
|||
* The fourth set of lines contains, for each layer by each impurity, the |
|||
* integrated dopant (R4), and the interior concentration of the |
|||
* polysilicon grains (R4). |
|||
*/ |
|||
for ( j=0; j < numLay; j++ ) { |
|||
for ( i=0; i < numImp; i++ ) { |
|||
fscanf( fpSuprem, "%e", &rdata ); |
|||
fscanf( fpSuprem, "%e", &rdata ); |
|||
} |
|||
} |
|||
DEBUG fprintf(stderr,"set 4:\n" ); |
|||
|
|||
/* |
|||
* The fifth set of lines contains, for each node in the structure, |
|||
* the distance to the next deepest node (R4), the distance from the |
|||
* surface (R4), and, for each impurity type, the impurity's |
|||
* chemical concentration (R4) and the impurity's active concentration (R4). |
|||
*/ |
|||
for ( i=1; i <= numGrid; i++ ) { |
|||
fscanf( fpSuprem, "%e %e", &rdata, &x[i] ); |
|||
|
|||
for ( j=0; j < numImp; j++ ) { |
|||
/* chemical concentration - not required */ |
|||
fscanf( fpSuprem, "%e", &con[i] ); |
|||
|
|||
/* store active concentration */ |
|||
fscanf( fpSuprem, "%e", &con[i] ); |
|||
|
|||
/* orient sign properly */ |
|||
if (impTyp[j] == *impId) { |
|||
/*...Boron...*/ |
|||
if (impTyp[j] == 1) { |
|||
conc[i] = - con[i]; |
|||
} else { |
|||
/*...All Other Impurities: P, As, Sb ...*/ |
|||
conc[i] = con[i]; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
DEBUG fprintf( stderr, "set 5: %e %e\n", x[1], conc[1] ); |
|||
|
|||
/* |
|||
* The last line in the file contains some random stuff that might be |
|||
* useful to some people, the temperature in degrees Kelvin of the last |
|||
* diffusion step (R4), the phosphorus implant dose (R4), the arsenic |
|||
* implant flag (L4). However, we can just ignore that stuff. |
|||
*/ |
|||
fclose( fpSuprem ); |
|||
|
|||
|
|||
/* shift silicon layer to beginning of array */ |
|||
for ( j=0; j < numLay; j++ ) { |
|||
if (matTyp[ j ] == 1) { |
|||
siIndex = j; |
|||
} |
|||
} |
|||
offset = topNod[ siIndex ] - 1; |
|||
numGrid -= offset; |
|||
xStart = x[1 + offset]; |
|||
for ( i=1; i <= numGrid; i++ ) { |
|||
x[i] = x[i + offset] - xStart; |
|||
conc[i] = conc[i + offset]; |
|||
} |
|||
|
|||
/* Store number of valid nodes using pointer */ |
|||
*numNod = numGrid; |
|||
return; |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
## Process this file with automake to produce Makefile.in
|
|||
|
|||
noinst_LIBRARIES = libcidertwod.a |
|||
|
|||
libcidertwod_a_SOURCES = \
|
|||
twoadmit.c \
|
|||
twoaval.c \
|
|||
twocond.c \
|
|||
twocont.c \
|
|||
twocurr.c \
|
|||
twodest.c \
|
|||
twodopng.c \
|
|||
twoelect.c \
|
|||
twofield.c \
|
|||
twomesh.c \
|
|||
twomobdv.c \
|
|||
twomobfn.c \
|
|||
twomobil.c \
|
|||
twoncont.c \
|
|||
twopcont.c \
|
|||
twopoiss.c \
|
|||
twoprint.c \
|
|||
twoproj.c \
|
|||
tworead.c \
|
|||
twosetbc.c \
|
|||
twosetup.c \
|
|||
twosolve.c |
|||
|
|||
EXTRA_DIST = readme |
|||
INCLUDES = -I$(top_srcdir)/src/include |
|||
MAINTAINERCLEANFILES = Makefile.in |
|||
@ -0,0 +1,15 @@ |
|||
Directory: twod |
|||
--------------- |
|||
This directory contains the files that are primarily responsible for |
|||
implementing the 2D device simulator. It also contains files that help |
|||
interface the circuit simulator to the device simulator. Most functions |
|||
that are common to all 2D device simulations start with the prefix TWO, |
|||
e.g. TWObiasSolve. The device-specific routines start with either NUMD2, |
|||
NBJT2 or NUMOS, e.g. NUMD2admittance, NBJTproject or NUMOSconductance. The |
|||
simulator contains a Poisson Solver for equilibrium, and a Two-carrier |
|||
solver and One-carrier solvers for bias solutions. An attempt has been |
|||
made to keep the function names parallel in the four portions. Poisson |
|||
routines are identified with a 'Q' (for charge only) after the TWO, Full |
|||
solver routines are identified with an underscore '_', |
|||
Electron-current-only routines are identified with an 'N', and |
|||
Hole-current-only routines are identified with a 'P'. |
|||
1316
src/ciderlib/twod/twoadmit.c
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
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 ); |
|||
} |
|||
@ -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
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
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; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,57 @@ |
|||
/* |
|||
* 2001 Paolo Nenzi |
|||
*/ |
|||
|
|||
#ifndef _TWODDEFS_H |
|||
#define _TWODDEFS_H |
|||
|
|||
/* Debug statements */ |
|||
|
|||
extern BOOLEAN TWOacDebug; |
|||
extern BOOLEAN TWOdcDebug; |
|||
extern BOOLEAN TWOtranDebug; |
|||
extern BOOLEAN TWOjacDebug; |
|||
|
|||
/* Now some defines for the two dimensional simulator |
|||
* library. |
|||
* Theese defines were gathered from all the code in |
|||
* oned directory. |
|||
*/ |
|||
|
|||
|
|||
/* Temporary hack to remove NUMOS gate special case */ |
|||
#ifdef NORMAL_GATE |
|||
#define GateTypeAdmittance oxideAdmittance |
|||
#else |
|||
#define GateTypeAdmittance contactAdmittance |
|||
#endif /* NORMAL_GATE */ |
|||
|
|||
/* Temporary hack to remove NUMOS gate special case */ |
|||
#ifdef NORMAL_GATE |
|||
#define GateTypeConductance oxideConductance |
|||
#define GateTypeCurrent oxideCurrent |
|||
#else |
|||
#define GateTypeConductance contactConductance |
|||
#define GateTypeCurrent contactCurrent |
|||
#endif /* NORMAL_GATE */ |
|||
|
|||
/* This structure was moved up from twoadmit.c */ |
|||
struct mosAdmittances { |
|||
SPcomplex yIdVdb; |
|||
SPcomplex yIdVsb; |
|||
SPcomplex yIdVgb; |
|||
SPcomplex yIsVdb; |
|||
SPcomplex yIsVsb; |
|||
SPcomplex yIsVgb; |
|||
SPcomplex yIgVdb; |
|||
SPcomplex yIgVsb; |
|||
SPcomplex yIgVgb; |
|||
}; |
|||
|
|||
#define MAXTERMINALS 5 /* One more than max number of terminals */ |
|||
#define ELCT_ID poiEqn |
|||
|
|||
#define MIN_DELV 1e-3 |
|||
#define NORM_RED_MAXITERS 10 |
|||
|
|||
#endif |
|||
@ -0,0 +1,76 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numglobs.h" |
|||
#include "numenum.h" |
|||
#include "twodev.h" |
|||
#include "twomesh.h" |
|||
#include "spMatrix.h" |
|||
#include "twoddefs.h" |
|||
#include "twodext.h" |
|||
|
|||
void |
|||
TWOdestroy(TWOdevice *pDevice) |
|||
{ |
|||
int index, eIndex; |
|||
TWOelem *pElem; |
|||
TWOnode *pNode; |
|||
TWOedge *pEdge; |
|||
|
|||
if ( !pDevice ) return; |
|||
|
|||
switch ( pDevice->solverType ) { |
|||
case SLV_SMSIG: |
|||
case SLV_BIAS: |
|||
/* free up memory allocated for the bias solution */ |
|||
FREE( pDevice->dcSolution ); |
|||
FREE( pDevice->dcDeltaSolution ); |
|||
FREE( pDevice->copiedSolution ); |
|||
FREE( pDevice->rhs ); |
|||
FREE( pDevice->rhsImag ); |
|||
spDestroy( pDevice->matrix ); |
|||
break; |
|||
case SLV_EQUIL: |
|||
/* free up the vectors allocated in the equilibrium solution */ |
|||
FREE( pDevice->dcSolution ); |
|||
FREE( pDevice->dcDeltaSolution ); |
|||
FREE( pDevice->copiedSolution ); |
|||
FREE( pDevice->rhs ); |
|||
spDestroy( pDevice->matrix ); |
|||
break; |
|||
case SLV_NONE: |
|||
break; |
|||
default: |
|||
fprintf( stderr, "Panic: Unknown solver type in TWOdestroy.\n" ); |
|||
exit( -1 ); |
|||
break; |
|||
} |
|||
|
|||
/* destroy the mesh */ |
|||
if ( pDevice->elements ) { |
|||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) { |
|||
pElem = pDevice->elements[ eIndex ]; |
|||
for ( index = 0; index <= 3; index++ ) { |
|||
if ( pElem->evalNodes[ index ] ) { |
|||
pNode = pElem->pNodes[ index ]; |
|||
FREE( pNode ); |
|||
} |
|||
if ( pElem->evalEdges[ index ] ) { |
|||
pEdge = pElem->pEdges[ index ]; |
|||
FREE( pEdge ); |
|||
} |
|||
} |
|||
FREE( pElem ); |
|||
} |
|||
FREE( pDevice->elements ); |
|||
FREE( pDevice->elemArray ); |
|||
} |
|||
|
|||
/* destroy the contacts & channels */ |
|||
/* NOT IMPLEMENTED */ |
|||
|
|||
FREE( pDevice ); |
|||
} |
|||
@ -0,0 +1,169 @@ |
|||
/* |
|||
* 2001 Paolo Nenzi |
|||
*/ |
|||
|
|||
/* External symbols for Two Dimensional simulator */ |
|||
|
|||
#ifndef _TWODEXT_H |
|||
#define _TWODEXT_H |
|||
|
|||
#include "profile.h" |
|||
#include "twomesh.h" |
|||
#include "twodev.h" |
|||
#include "carddefs.h" |
|||
#include "bool.h" |
|||
#include "complex.h" |
|||
|
|||
/* twoadmit.c */ |
|||
extern int NUMD2admittance(TWOdevice *, double, SPcomplex *); |
|||
extern int NBJT2admittance(TWOdevice *, double, SPcomplex *, |
|||
SPcomplex *, SPcomplex *, SPcomplex *); |
|||
extern int NUMOSadmittance(TWOdevice *, double, struct mosAdmittances *); |
|||
extern BOOLEAN TWOsorSolve(TWOdevice *, double *, double *, double); |
|||
extern SPcomplex *contactAdmittance(TWOdevice *, TWOcontact *, BOOLEAN, |
|||
double *, double *, SPcomplex *); |
|||
extern SPcomplex *oxideAdmittance(TWOdevice *, TWOcontact *,BOOLEAN, |
|||
double *, double *, SPcomplex *); |
|||
|
|||
extern void NUMD2ys(TWOdevice *, SPcomplex *, SPcomplex *); |
|||
extern void NBJT2ys(TWOdevice *,SPcomplex *, SPcomplex *, SPcomplex *, |
|||
SPcomplex *, SPcomplex *); |
|||
extern void NUMOSys(TWOdevice *, SPcomplex *, struct mosAdmittances *); |
|||
|
|||
/* twoaval.c */ |
|||
extern double TWOavalanche(TWOelem *, TWOnode *); |
|||
|
|||
/* twocond.c */ |
|||
extern void NUMD2conductance(TWOdevice *, BOOLEAN, double *, double *); |
|||
extern void NBJT2conductance(TWOdevice *, BOOLEAN, double *, double *, |
|||
double *, double *, double *); |
|||
extern void NUMOSconductance(TWOdevice *, BOOLEAN, double *, |
|||
struct mosConductances *); |
|||
extern double contactCurrent(TWOdevice *, TWOcontact *); |
|||
extern double oxideCurrent(TWOdevice *, TWOcontact *, BOOLEAN); |
|||
extern double contactConductance(TWOdevice *, TWOcontact *, BOOLEAN, |
|||
double *, BOOLEAN, double *); |
|||
extern double oxideConductance(TWOdevice *, TWOcontact *, BOOLEAN, |
|||
double *, BOOLEAN, double *); |
|||
extern void NUMD2current(TWOdevice *, BOOLEAN, double *, double *); |
|||
extern void NBJT2current(TWOdevice *, BOOLEAN, double *, double *, |
|||
double *); |
|||
extern void NUMOScurrent(TWOdevice *, BOOLEAN , double *, double *, double *, |
|||
double *); |
|||
|
|||
/* twocont */ |
|||
extern void TWO_jacBuild(TWOdevice *); |
|||
extern void TWO_sysLoad(TWOdevice *, BOOLEAN, TWOtranInfo *); |
|||
extern void TWO_jacLoad(TWOdevice *); |
|||
extern void TWO_rhsLoad(TWOdevice *, BOOLEAN, TWOtranInfo *); |
|||
extern void TWO_commonTerms(TWOdevice *, BOOLEAN, BOOLEAN, TWOtranInfo *); |
|||
|
|||
/* twocurr.c */ |
|||
extern void nodeCurrents(TWOelem *, TWOnode *, double *, double *, |
|||
double *, double *, double *, double *, double *, double *); |
|||
|
|||
/* twodest */ |
|||
extern void TWOdestroy(TWOdevice *); |
|||
|
|||
/* twodopng.c */ |
|||
extern double TWOdopingValue(DOPprofile *, DOPtable *, double, double); |
|||
extern void TWOsetDoping(TWOdevice *, DOPprofile *, DOPtable *); |
|||
|
|||
/* twoelect.c */ |
|||
extern int TWOcmpElectrode(TWOelectrode *, TWOelectrode *); |
|||
extern void checkElectrodes(TWOelectrode *, int); |
|||
extern void setupContacts(TWOdevice *, TWOelectrode *, TWOnode ***); |
|||
|
|||
/* twofield.c */ |
|||
extern void nodeFields(TWOelem *, TWOnode *, double *, double *); |
|||
|
|||
/* twomesh.c */ |
|||
extern void TWObuildMesh(TWOdevice *, TWOdomain *, TWOelectrode *, |
|||
TWOmaterial *); |
|||
extern void TWOprnMesh(TWOdevice *); |
|||
extern void TWOgetStatePointers(TWOdevice *, int *); |
|||
|
|||
/* twomobdv.c */ |
|||
extern void TWO_mobDeriv(TWOelem *, int, double); |
|||
extern void TWONmobDeriv(TWOelem *, int, double); |
|||
extern void TWOPmobDeriv(TWOelem *, int, double); |
|||
|
|||
/* twomobfn.c */ |
|||
extern void MOBsurfElec(TWOmaterial *, TWOelem *, double, double, |
|||
double, double, double, double); |
|||
extern void MOBsurfHole(TWOmaterial *, TWOelem *, double, double, |
|||
double, double, double, double); |
|||
|
|||
/* twomobil.c */ |
|||
extern void TWO_mobility(TWOelem *, double); |
|||
extern void TWONmobility(TWOelem *, double); |
|||
extern void TWOPmobility(TWOelem *, double); |
|||
|
|||
/* twoncont.c */ |
|||
extern void TWONjacBuild(TWOdevice *); |
|||
extern void TWONsysLoad(TWOdevice *, BOOLEAN, TWOtranInfo *); |
|||
extern void TWONjacLoad(TWOdevice *); |
|||
extern void TWONrhsLoad(TWOdevice *, BOOLEAN, TWOtranInfo *); |
|||
extern void TWONcommonTerms(TWOdevice *, BOOLEAN, BOOLEAN, TWOtranInfo *); |
|||
|
|||
/* twopcont.c */ |
|||
extern void TWOPjacBuild(TWOdevice *); |
|||
extern void TWOPsysLoad(TWOdevice *, BOOLEAN, TWOtranInfo *); |
|||
extern void TWOPjacLoad(TWOdevice *); |
|||
extern void TWOPrhsLoad(TWOdevice *, BOOLEAN, TWOtranInfo *); |
|||
extern void TWOPcommonTerms(TWOdevice *, BOOLEAN, BOOLEAN, TWOtranInfo *); |
|||
|
|||
/* twopoiss.c */ |
|||
extern void TWOQjacBuild(TWOdevice *); |
|||
extern void TWOQsysLoad(TWOdevice *); |
|||
extern void TWOQrhsLoad(TWOdevice *); |
|||
extern void TWOQcommonTerms(TWOdevice *); |
|||
|
|||
/*twoprint.c */ |
|||
extern void TWOprnSolution(FILE *, TWOdevice *, OUTPcard *); |
|||
extern void TWOmemStats(FILE *, TWOdevice *); |
|||
extern void TWOcpuStats(FILE *, TWOdevice *); |
|||
|
|||
|
|||
/* twoproj.c */ |
|||
extern void NUMD2project(TWOdevice *, double); |
|||
extern void NBJT2project(TWOdevice *, double, double); |
|||
extern void NUMOSproject(TWOdevice *, double, double, double); |
|||
extern void NUMD2update(TWOdevice *,double, BOOLEAN); |
|||
extern void NBJT2update(TWOdevice *, double, double, BOOLEAN); |
|||
extern void NUMOSupdate(TWOdevice *, double, double, double, BOOLEAN); |
|||
extern void storeNewRhs(TWOdevice *, TWOcontact *); |
|||
|
|||
/* tworead.c */ |
|||
extern int TWOreadState(TWOdevice *, char *, int, double *, double *, double *); |
|||
|
|||
/*twosetbc.c */ |
|||
extern void NUMD2setBCs(TWOdevice *, double); |
|||
extern void NBJT2setBCs(TWOdevice *, double, double); |
|||
extern void NUMOSsetBCs(TWOdevice *, double, double, double); |
|||
|
|||
/*twosetup.c */ |
|||
extern void TWOsetup(TWOdevice *); |
|||
extern void TWOsetBCparams(TWOdevice *, BDRYcard *); |
|||
extern void TWOnormalize(TWOdevice *); |
|||
|
|||
/* twosolve.c */ |
|||
extern void TWOdcSolve(TWOdevice *, int, BOOLEAN, BOOLEAN, TWOtranInfo *); |
|||
extern BOOLEAN TWOdeltaConverged(TWOdevice *); |
|||
extern BOOLEAN TWOdeviceConverged(TWOdevice *); |
|||
extern void TWOresetJacobian(TWOdevice *); |
|||
extern void TWOstoreNeutralGuess(TWOdevice *); |
|||
extern void TWOequilSolve(TWOdevice *); |
|||
extern void TWObiasSolve(TWOdevice *, int, BOOLEAN, TWOtranInfo *); |
|||
extern void TWOstoreEquilibGuess(TWOdevice *); |
|||
extern void TWOstoreInitialGuess(TWOdevice *); |
|||
extern void oldTWOnewDelta(TWOdevice *, BOOLEAN, TWOtranInfo *); |
|||
extern int TWOnewDelta(TWOdevice *, BOOLEAN, TWOtranInfo *); |
|||
extern void TWOpredict(TWOdevice *, TWOtranInfo *); |
|||
extern double TWOtrunc(TWOdevice *, TWOtranInfo *, double); |
|||
extern void TWOsaveState(TWOdevice *); |
|||
extern BOOLEAN TWOpsiDeltaConverged(TWOdevice *); |
|||
extern double TWOnuNorm(TWOdevice *); |
|||
extern void TWOjacCheck(TWOdevice *, BOOLEAN, TWOtranInfo *); |
|||
|
|||
#endif |
|||
@ -0,0 +1,234 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group |
|||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numglobs.h" |
|||
#include "numenum.h" |
|||
#include "twomesh.h" |
|||
#include "twodev.h" |
|||
#include "profile.h" |
|||
#include "macros.h" |
|||
#include "bool.h" |
|||
#include "twoddefs.h" |
|||
#include "twodext.h" |
|||
#include "cidersupt.h" |
|||
|
|||
/* functions in this file are used to calculate the conc */ |
|||
|
|||
double |
|||
TWOdopingValue(DOPprofile *pProfile, DOPtable *pTable, double x, |
|||
double y) |
|||
{ |
|||
double argX, argY, argP, argL, value = 0.0; |
|||
|
|||
/* Find the appropriate lookup table if necessary */ |
|||
if (pProfile->type == LOOKUP) { |
|||
while ( pTable != NIL(DOPtable) ) { |
|||
if (pTable->impId == pProfile->IMPID) { |
|||
/* Found it */ |
|||
break; |
|||
} else { |
|||
pTable = pTable->next; |
|||
} |
|||
} |
|||
if ( pTable == NIL(DOPtable) ) { |
|||
fprintf( stderr, "Error: unknown impurity profile %d\n", |
|||
((int)pProfile->IMPID) ); |
|||
exit(1); |
|||
} |
|||
} |
|||
/* Find distances */ |
|||
if ( pProfile->Y_LOW > y ) { |
|||
argY = pProfile->Y_LOW - y; |
|||
} else if ( y > pProfile->Y_HIGH ) { |
|||
argY = y - pProfile->Y_HIGH; |
|||
} else { |
|||
argY = 0.0; |
|||
} |
|||
if ( pProfile->X_LOW > x ) { |
|||
argX = pProfile->X_LOW - x; |
|||
} else if ( x > pProfile->X_HIGH ) { |
|||
argX = x - pProfile->X_HIGH; |
|||
} else { |
|||
argX = 0.0; |
|||
} |
|||
|
|||
if ( pProfile->DIRECTION == Y ) { |
|||
argP = argY; |
|||
argL = argX / pProfile->LAT_RATIO; |
|||
} |
|||
else { |
|||
argP = argX; |
|||
argL = argY / pProfile->LAT_RATIO; |
|||
} |
|||
if ( pProfile->rotate ) { |
|||
argP = sqrt(argP*argP + argL*argL); |
|||
argL = 0.0; |
|||
} |
|||
|
|||
/* Transform to coordinates of profile peak */ |
|||
argP -= pProfile->LOCATION; |
|||
argP /= pProfile->CHAR_LENGTH; |
|||
argL -= pProfile->LOCATION; |
|||
argL /= pProfile->CHAR_LENGTH; |
|||
|
|||
switch (pProfile->type) { |
|||
case UNIF: |
|||
if (argP > 0.0) { |
|||
value = 0.0; |
|||
} else { |
|||
value = pProfile->CONC; |
|||
} |
|||
break; |
|||
case LIN: |
|||
argP = ABS(argP); |
|||
if (argP > 1.0) { |
|||
value = 0.0; |
|||
} else { |
|||
value = pProfile->CONC * ( 1.0 - argP ); |
|||
} |
|||
break; |
|||
case GAUSS: |
|||
argP *= argP; |
|||
if ( argP > 80.0 ) { |
|||
value = 0.0; |
|||
} else { |
|||
value = pProfile->PEAK_CONC * exp( -argP ); |
|||
} |
|||
case EXP: |
|||
argP = ABS(argP); |
|||
if ( argP > 80.0 ) { |
|||
value = 0.0; |
|||
} else { |
|||
value = pProfile->PEAK_CONC * exp( -argP ); |
|||
} |
|||
break; |
|||
case ERRFC: |
|||
argP = ABS(argP); |
|||
if ( argP > 10.0 ) { |
|||
value = 0.0; |
|||
} else { |
|||
value = pProfile->PEAK_CONC * erfc( -argP ); |
|||
} |
|||
break; |
|||
case LOOKUP: |
|||
argP = ABS(argP); |
|||
value = lookup( pTable->dopData, argP ); |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
if (!pProfile->rotate) { /* Tensor product in lateral direction */ |
|||
switch (pProfile->latType) { |
|||
case UNIF: |
|||
if (argL > 0.0) { |
|||
value = 0.0; |
|||
} |
|||
break; |
|||
case LIN: |
|||
argL = ABS(argL); |
|||
if (argL > 1.0) { |
|||
value = 0.0; |
|||
} else { |
|||
value *= ( 1.0 - argL ); |
|||
} |
|||
break; |
|||
case GAUSS: |
|||
argL *= argL; |
|||
if ( argL > 80.0 ) { |
|||
value = 0.0; |
|||
} else { |
|||
value *= exp( -argL ); |
|||
} |
|||
case EXP: |
|||
argL = ABS(argL); |
|||
if ( argL > 80.0 ) { |
|||
value = 0.0; |
|||
} else { |
|||
value *= exp( -argL ); |
|||
} |
|||
break; |
|||
case ERRFC: |
|||
argL = ABS(argL); |
|||
if ( argP > 10.0 ) { |
|||
value = 0.0; |
|||
} else { |
|||
value *= erfc( -argL ); |
|||
} |
|||
break; |
|||
case LOOKUP: |
|||
argL = ABS(argL); |
|||
value *= lookup( pTable->dopData, argL ) / lookup( pTable->dopData, 0.0 ); |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
} /* end: not rotated */ |
|||
return( value ); |
|||
} |
|||
|
|||
void |
|||
TWOsetDoping(TWOdevice *pDevice, DOPprofile *pProfile, DOPtable *pTable) |
|||
{ |
|||
TWOnode *pNode; |
|||
TWOelem *pElem; |
|||
DOPprofile *pP; |
|||
double conc; |
|||
int index, eIndex; |
|||
BOOLEAN dopeMe; |
|||
|
|||
/* Clear doping info for all nodes. */ |
|||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) { |
|||
pElem = pDevice->elements[ eIndex ]; |
|||
for (index = 0; index <= 3; index++) { |
|||
if (pElem->evalNodes[index]) { |
|||
pNode = pElem->pNodes[index]; |
|||
pNode->na = 0.0; |
|||
pNode->nd = 0.0; |
|||
pNode->netConc = 0.0; |
|||
pNode->totalConc = 0.0; |
|||
} |
|||
} |
|||
} |
|||
/* Now compute the contribution to the total doping from each profile. */ |
|||
for ( pP = pProfile; pP != NIL(DOPprofile); pP = pP->next ) { |
|||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) { |
|||
pElem = pDevice->elements[ eIndex ]; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
if ( pP->numDomains > 0 ) { |
|||
dopeMe = FALSE; |
|||
for ( index = 0; index < pP->numDomains; index++ ) { |
|||
if ( pElem->domain == pP->domains[ index ] ) { |
|||
dopeMe = TRUE; |
|||
break; |
|||
} |
|||
} |
|||
} else { /* domains not given, so dope all */ |
|||
dopeMe = TRUE; |
|||
} |
|||
if ( dopeMe ) { |
|||
for ( index = 0; index <= 3; index++ ) { |
|||
if ( pElem->evalNodes[ index ] ) { |
|||
pNode = pElem->pNodes[ index ]; |
|||
conc = TWOdopingValue( pP, pTable, |
|||
pDevice->xScale[ pNode->nodeI ], |
|||
pDevice->yScale[ pNode->nodeJ ] ); |
|||
pNode->netConc += conc; |
|||
if ( conc < 0.0 ) { |
|||
pNode->totalConc -= conc; |
|||
pNode->na -= conc; |
|||
} |
|||
else { |
|||
pNode->totalConc += conc; |
|||
pNode->nd += conc; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,180 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group |
|||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numglobs.h" |
|||
#include "numconst.h" |
|||
#include "numenum.h" |
|||
#include "twomesh.h" |
|||
#include "twodev.h" |
|||
#include "bool.h" |
|||
#include "twoddefs.h" |
|||
#include "twodext.h" |
|||
|
|||
/* Use list-sorting header file to create electrode sorter */ |
|||
#define TYPE TWOelectrode |
|||
#define NEXT next |
|||
#define SORT TWOssortElectrodes |
|||
#define SORT1 TWOsortElectrodes |
|||
#include "lsort.h" |
|||
|
|||
#define ARG_MIN(a,b,c) ((a) > (b) ? 1 : ((a) < (b) ? -1 : (c))) |
|||
|
|||
int |
|||
TWOcmpElectrode(TWOelectrode *a, TWOelectrode *b) |
|||
{ |
|||
return ARG_MIN(a->id, b->id, 0); |
|||
} |
|||
|
|||
/* |
|||
* checkElectrodes |
|||
* debug list of electrodes for errors, exit on any error: |
|||
* 1. list doesn't have only contiguous id's from 1 to idHigh |
|||
* 2. electrodes with box shapes |
|||
* 3. order of node numbers is correct |
|||
*/ |
|||
void |
|||
checkElectrodes(TWOelectrode *pElectrode, int idHigh) |
|||
{ |
|||
TWOelectrode *pE; |
|||
int id; |
|||
BOOLEAN error = FALSE; |
|||
|
|||
/* |
|||
* order the electrodes |
|||
* assign electrode numbers to uninitialized electrodes ( id == -1 ) |
|||
* uninit electrodes are in reverse order from order in input file |
|||
* resort electrodes |
|||
*/ |
|||
pElectrode = TWOsortElectrodes( pElectrode, TWOcmpElectrode ); |
|||
id = 1; |
|||
for (pE = pElectrode; pE != NIL(TWOelectrode); pE = pE->next) { |
|||
if (pE->id == -1) pE->id = id++; |
|||
} |
|||
pElectrode = TWOsortElectrodes( pElectrode, TWOcmpElectrode ); |
|||
|
|||
for (pE = pElectrode, id = 1; pE != NIL(TWOelectrode); pE = pE->next) { |
|||
/* Check id's */ |
|||
if ( pE->id < 1 || pE->id > idHigh) { |
|||
fprintf(stderr, "Error: electrode %d out of range\n",pE->id); |
|||
error = TRUE; |
|||
} else if ( pE->id != id ) { |
|||
if ( pE->id != ++id ) { |
|||
fprintf(stderr, "Error: electrode(s) %d to %d missing\n",id,pE->id-1); |
|||
id = pE->id; |
|||
error = TRUE; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Make sure total number of distinct electrodes is correct */ |
|||
if ( id != idHigh ) { |
|||
fprintf(stderr, "Error: %d electrode%s not equal to %d required\n", |
|||
id, (id != 1) ? "s are" : " is", idHigh); |
|||
error = TRUE; |
|||
} |
|||
|
|||
if (error) { |
|||
exit(-1); |
|||
} |
|||
} |
|||
|
|||
/* |
|||
* setupContacts |
|||
* go through each set of electrode segments, find its size and find nodes |
|||
*/ |
|||
|
|||
|
|||
void |
|||
setupContacts(TWOdevice *pDevice, TWOelectrode *pElectrode, |
|||
TWOnode ***nodeArray) |
|||
{ |
|||
TWOelectrode *pE; |
|||
TWOcontact *pNew = NULL, *pTail; |
|||
TWOnode *pNode; |
|||
int xIndex, yIndex, nIndex = 0; |
|||
int id = 0, electrodeSize[MAXTERMINALS], electrodeType; |
|||
int error = FALSE; |
|||
|
|||
/* INITIALIZATION |
|||
* 0. Assume ELCT_ID's are initialized to 0 before setup is called |
|||
* 1. Add a node only once to only one electrode |
|||
* 2. Compute number of nodes in each electrode |
|||
* 3. Overwrite SEMICON or INSULATOR nodeType at electrodes |
|||
*/ |
|||
for ( pE = pElectrode; pE != NIL(TWOelectrode); pE = pE->next ) { |
|||
if (pE->id != id) { /* Found the next electrode */ |
|||
id = pE->id; |
|||
electrodeSize[id] = 0; |
|||
electrodeType = 0; |
|||
} |
|||
for ( xIndex = pE->ixLo; xIndex <= pE->ixHi; xIndex++ ) { |
|||
for ( yIndex = pE->iyLo; yIndex <= pE->iyHi; yIndex++ ) { |
|||
pNode = nodeArray[ xIndex ][ yIndex ]; |
|||
if ( pNode != NIL( TWOnode ) ) { |
|||
pNode->nodeType = CONTACT; |
|||
|
|||
/* Assign each node to an electrode only once */ |
|||
if (pNode->ELCT_ID == 0) { /* Is this a new node? */ |
|||
pNode->ELCT_ID = id; /* Mark electrode ownership */ |
|||
electrodeSize[id]++; /* Increase electrode size */ |
|||
} else if (pNode->ELCT_ID != id) { |
|||
/* Node already owned by another electrode */ |
|||
fprintf(stderr, "Error: electrodes %d and %d overlap at (%d,%d)\n", |
|||
pNode->ELCT_ID, id, xIndex, yIndex); |
|||
error = TRUE; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
if (error) { |
|||
exit(-1); |
|||
} |
|||
|
|||
/* ALLOCATION AND NODE GRABBING |
|||
* 1. For each electrode, create a new contact structure for the electrode. |
|||
* 2. Fill in entries of contact structure |
|||
* 3. Update First and Last Contact pointers of device |
|||
*/ |
|||
/* |
|||
printElectrodes( pDevice->pFirstContact ); |
|||
*/ |
|||
id = 0; |
|||
pDevice->pFirstContact = pTail = NIL(TWOcontact); |
|||
for ( pE = pElectrode; pE != NIL(TWOelectrode); pE = pE->next ) { |
|||
if (pE->id != id) { /* Found the next electrode */ |
|||
if ( pDevice->pFirstContact == NIL(TWOcontact) ) { |
|||
XCALLOC( pNew, TWOcontact, 1 ); |
|||
pDevice->pFirstContact = pTail = pNew; |
|||
} else { |
|||
XCALLOC( pNew->next, TWOcontact, 1 ); |
|||
pTail = pNew = pNew->next; |
|||
} |
|||
pNew->next = NIL(TWOcontact); |
|||
id = pNew->id = pE->id; |
|||
pNew->workf = pE->workf; |
|||
pNew->numNodes = electrodeSize[id]; |
|||
nIndex = 0; |
|||
XCALLOC( pNew->pNodes, TWOnode *, electrodeSize[id] ); |
|||
} |
|||
for ( xIndex = pE->ixLo; xIndex <= pE->ixHi; xIndex++ ) { |
|||
for ( yIndex = pE->iyLo; yIndex <= pE->iyHi; yIndex++ ) { |
|||
pNode = nodeArray[ xIndex ][ yIndex ]; |
|||
if ( pNode != NIL( TWOnode ) ) { |
|||
/* Make sure node belongs to this electrode, then |
|||
* clear ELCT_ID so that it is not grabbed again. |
|||
*/ |
|||
if (pNode->ELCT_ID == id) { |
|||
pNew->pNodes[ nIndex++ ] = pNode; |
|||
pNode->ELCT_ID = 0; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
pDevice->pLastContact = pTail; |
|||
} |
|||
@ -0,0 +1,125 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group |
|||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numglobs.h" |
|||
#include "numenum.h" |
|||
#include "twomesh.h" |
|||
#include "twoddefs.h" |
|||
#include "twodext.h" |
|||
|
|||
void |
|||
nodeFields(TWOelem *pElem, TWOnode *pNode, double *ex, double *ey) |
|||
{ |
|||
|
|||
TWOelem *pElemTL, *pElemTR, *pElemBL, *pElemBR; |
|||
TWOedge *pEdgeT, *pEdgeB, *pEdgeL, *pEdgeR; |
|||
int materT, materB, materL, materR; |
|||
double dxL = 0.0, dxR = 0.0, dyT = 0.0, dyB = 0.0; |
|||
double ef1, ef2, coeff1, coeff2; |
|||
|
|||
/* Find all four neighboring elements */ |
|||
pElemTL = pNode->pTLElem; |
|||
pElemTR = pNode->pTRElem; |
|||
pElemBL = pNode->pBLElem; |
|||
pElemBR = pNode->pBRElem; |
|||
|
|||
/* Null edge pointers */ |
|||
pEdgeT = pEdgeB = pEdgeL = pEdgeR = NIL(TWOedge); |
|||
|
|||
/* Find edges next to node */ |
|||
if (pElemTL != NIL(TWOelem)) { |
|||
if (pElemTL->evalEdges[1]) { |
|||
pEdgeT = pElemTL->pRightEdge; |
|||
materT = pElemTL->elemType; |
|||
dyT = pElemTL->dy; |
|||
} |
|||
if (pElemTL->evalEdges[2]) { |
|||
pEdgeL = pElemTL->pBotEdge; |
|||
materL = pElemTL->elemType; |
|||
dxL = pElemTL->dx; |
|||
} |
|||
} |
|||
if (pElemTR != NIL(TWOelem)) { |
|||
if (pElemTR->evalEdges[3]) { |
|||
pEdgeT = pElemTR->pLeftEdge; |
|||
materT = pElemTR->elemType; |
|||
dyT = pElemTR->dy; |
|||
} |
|||
if (pElemTR->evalEdges[2]) { |
|||
pEdgeR = pElemTR->pBotEdge; |
|||
materR = pElemTR->elemType; |
|||
dxR = pElemTR->dx; |
|||
} |
|||
} |
|||
if (pElemBR != NIL(TWOelem)) { |
|||
if (pElemBR->evalEdges[3]) { |
|||
pEdgeB = pElemBR->pLeftEdge; |
|||
materB = pElemBR->elemType; |
|||
dyB = pElemBR->dy; |
|||
} |
|||
if (pElemBR->evalEdges[0]) { |
|||
pEdgeR = pElemBR->pTopEdge; |
|||
materR = pElemBR->elemType; |
|||
dxR = pElemBR->dx; |
|||
} |
|||
} |
|||
if (pElemBL != NIL(TWOelem)) { |
|||
if (pElemBL->evalEdges[1]) { |
|||
pEdgeB = pElemBL->pRightEdge; |
|||
materB = pElemBL->elemType; |
|||
dyB = pElemBL->dy; |
|||
} |
|||
if (pElemBL->evalEdges[0]) { |
|||
pEdgeL = pElemBL->pTopEdge; |
|||
materL = pElemBL->elemType; |
|||
dxL = pElemBL->dx; |
|||
} |
|||
} |
|||
/* compute horizontal vector components */ |
|||
/* No more than one of Left Edge or Right Edge is absent */ |
|||
if (pEdgeL == NIL(TWOedge)) { |
|||
if (pNode->nodeType == CONTACT) { |
|||
*ex = -pEdgeR->dPsi / dxR; |
|||
} else { |
|||
*ex = 0.0; |
|||
} |
|||
} else if (pEdgeR == NIL(TWOedge)) { |
|||
if (pNode->nodeType == CONTACT) { |
|||
*ex = -pEdgeL->dPsi / dxL; |
|||
} else { |
|||
*ex = 0.0; |
|||
} |
|||
} else { /* Both edges are present */ |
|||
coeff1 = dxL / (dxL + dxR); |
|||
coeff2 = dxR / (dxL + dxR); |
|||
ef1 = -pEdgeL->dPsi / dxL; |
|||
ef2 = -pEdgeR->dPsi / dxR; |
|||
*ex = coeff2 * ef1 + coeff1 * ef2; |
|||
} |
|||
|
|||
/* compute vertical vector components */ |
|||
/* No more than one of Top Edge or Bottom Edge is absent */ |
|||
if (pEdgeT == NIL(TWOedge)) { |
|||
if (pNode->nodeType == CONTACT) { |
|||
*ey = -pEdgeB->dPsi / dyB; |
|||
} else { |
|||
*ey = 0.0; |
|||
} |
|||
} else if (pEdgeB == NIL(TWOedge)) { |
|||
if (pNode->nodeType == CONTACT) { |
|||
*ey = -pEdgeT->dPsi / dyT; |
|||
} else { |
|||
*ey = 0.0; |
|||
} |
|||
} else { /* Both edges are present */ |
|||
coeff1 = dyT / (dyT + dyB); |
|||
coeff2 = dyB / (dyT + dyB); |
|||
ef1 = -pEdgeT->dPsi / dyT; |
|||
ef2 = -pEdgeB->dPsi / dyB; |
|||
*ey = coeff2 * ef1 + coeff1 * ef2; |
|||
} |
|||
} |
|||
@ -0,0 +1,626 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group |
|||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numglobs.h" |
|||
#include "numconst.h" |
|||
#include "numenum.h" |
|||
#include "twomesh.h" |
|||
#include "twodev.h" |
|||
#include "bool.h" |
|||
#include "twoddefs.h" |
|||
#include "twodext.h" |
|||
|
|||
|
|||
|
|||
static void doMobCoeffs(TWOelem *, int); |
|||
static void resetEvalFlag(TWOdevice *pDevice); |
|||
|
|||
|
|||
void |
|||
TWObuildMesh(TWOdevice *pDevice, TWOdomain *pDomain, |
|||
TWOelectrode *pElectrode, TWOmaterial *pMaterial) |
|||
{ |
|||
int xIndex, yIndex, eIndex, index; |
|||
int elemType; |
|||
TWOcoord *pX, *pY; |
|||
TWOelem *pElem, *pElem1; |
|||
TWOnode *pNode, *pNode1, *pNextHNode, *pNextVNode, *pNextDNode; |
|||
TWOnode ***nodeArray = NULL; |
|||
TWOedge *pEdge; |
|||
TWOedge ***edgeArrayH = NULL, ***edgeArrayV = NULL; |
|||
TWOdomain *pD; |
|||
TWOelectrode *pE; |
|||
TWOmaterial *pM; |
|||
BOOLEAN error = FALSE; |
|||
BOOLEAN interiorNode; |
|||
int poiEqn, numEqn, numElem, numNodes, numEdges; |
|||
int numXNodes = pDevice->numXNodes; |
|||
int numYNodes = pDevice->numYNodes; |
|||
double *xScale = pDevice->xScale; |
|||
double *yScale = pDevice->yScale; |
|||
FILE *meshFile; |
|||
|
|||
/* Generate work arrays. */ |
|||
XCALLOC(nodeArray, TWOnode **, 1 + numXNodes); |
|||
for (xIndex = 1; xIndex <= numXNodes; xIndex++) { |
|||
XCALLOC(nodeArray[xIndex], TWOnode *, 1 + numYNodes); |
|||
} |
|||
|
|||
/* Generate the nodes. */ |
|||
for (xIndex = 1; xIndex <= numXNodes; xIndex++) { |
|||
for (yIndex = 1; yIndex <= numYNodes; yIndex++) { |
|||
XCALLOC(pNode, TWOnode, 1); |
|||
pNode->nodeI = xIndex; |
|||
pNode->nodeJ = yIndex; |
|||
pNode->poiEqn = 0; |
|||
nodeArray[xIndex][yIndex] = pNode; |
|||
} |
|||
} |
|||
|
|||
/* Mark the semiconductor/insulator domains. */ |
|||
if (pDomain == NIL(TWOdomain)) { |
|||
fprintf(stderr, "Error: domains not defined for device\n"); |
|||
exit(-1); |
|||
} |
|||
for (pD = pDomain; pD != NIL(TWOdomain); pD = pD->next) { |
|||
for (pM = pMaterial; pM != NIL(TWOmaterial); pM = pM->next) { |
|||
if (pD->material == pM->id) { |
|||
break; |
|||
} |
|||
} |
|||
elemType = pM->type; |
|||
for (xIndex = pD->ixLo; xIndex <= pD->ixHi; xIndex++) { |
|||
for (yIndex = pD->iyLo; yIndex <= pD->iyHi; yIndex++) { |
|||
pNode = nodeArray[xIndex][yIndex]; |
|||
pNode->nodeType = elemType; |
|||
} |
|||
} |
|||
} |
|||
/* Now mark all the metallic domains */ |
|||
for (pE = pElectrode; pE != NIL(TWOelectrode); pE = pE->next) { |
|||
for (xIndex = pE->ixLo; xIndex <= pE->ixHi; xIndex++) { |
|||
for (yIndex = pE->iyLo; yIndex <= pE->iyHi; yIndex++) { |
|||
pNode = nodeArray[xIndex][yIndex]; |
|||
pNode->nodeType = CONTACT; |
|||
} |
|||
} |
|||
} |
|||
/* |
|||
* Now mark as NULL any node in the interior of an electrode, i.e. none of |
|||
* its neighbors is a different material. |
|||
*/ |
|||
for (xIndex = 1; xIndex <= numXNodes; xIndex++) { |
|||
for (yIndex = 1; yIndex <= numYNodes; yIndex++) { |
|||
pNode = nodeArray[xIndex][yIndex]; |
|||
if (pNode->nodeType == CONTACT) { |
|||
interiorNode = TRUE; |
|||
if (xIndex > 1) { |
|||
pNode1 = nodeArray[xIndex - 1][yIndex]; |
|||
if (pNode1->nodeType != NULL |
|||
&& pNode1->nodeType != CONTACT) { |
|||
interiorNode = FALSE; |
|||
} |
|||
} |
|||
if (interiorNode && xIndex < numXNodes) { |
|||
pNode1 = nodeArray[xIndex + 1][yIndex]; |
|||
if (pNode1->nodeType != NULL |
|||
&& pNode1->nodeType != CONTACT) { |
|||
interiorNode = FALSE; |
|||
} |
|||
} |
|||
if (interiorNode && yIndex > 1) { |
|||
pNode1 = nodeArray[xIndex][yIndex - 1]; |
|||
if (pNode1->nodeType != NULL |
|||
&& pNode1->nodeType != CONTACT) { |
|||
interiorNode = FALSE; |
|||
} |
|||
} |
|||
if (interiorNode && yIndex < numYNodes) { |
|||
pNode1 = nodeArray[xIndex][yIndex + 1]; |
|||
if (pNode1->nodeType != NULL |
|||
&& pNode1->nodeType != CONTACT) { |
|||
interiorNode = FALSE; |
|||
} |
|||
} |
|||
if (interiorNode) { |
|||
pNode->nodeType = NULL; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Delete nodes that do not belong to any domain. */ |
|||
numNodes = 0; |
|||
for (yIndex = 1; yIndex <= numYNodes; yIndex++) { |
|||
for (xIndex = 1; xIndex <= numXNodes; xIndex++) { |
|||
pNode = nodeArray[xIndex][yIndex]; |
|||
if (pNode->nodeType == NULL) { |
|||
/* This node doesn't belong to a domain so delete it. */ |
|||
nodeArray[xIndex][yIndex] = NIL(TWOnode); |
|||
FREE(pNode); |
|||
} else { |
|||
numNodes++; |
|||
} |
|||
} |
|||
} |
|||
pDevice->numNodes = numNodes; |
|||
|
|||
/* Now relabel any remaining nodes that are part of electrodes. */ |
|||
setupContacts(pDevice, pElectrode, nodeArray); |
|||
|
|||
/* Done with the nodes. Now construct the elements and the edges. */ |
|||
numEdges = 0; |
|||
|
|||
/* Generate the horizontal edges and store in a work array. */ |
|||
XCALLOC(edgeArrayH, TWOedge **, numXNodes); |
|||
for (xIndex = 1; xIndex < numXNodes; xIndex++) { |
|||
XCALLOC(edgeArrayH[xIndex], TWOedge *, 1 + numYNodes); |
|||
} |
|||
for (yIndex = 1; yIndex <= numYNodes; yIndex++) { |
|||
for (xIndex = 1; xIndex < numXNodes; xIndex++) { |
|||
pNode = nodeArray[xIndex][yIndex]; |
|||
pNextHNode = nodeArray[xIndex + 1][yIndex]; |
|||
if (pNode != NIL(TWOnode) && |
|||
pNextHNode != NIL(TWOnode)) { |
|||
XCALLOC(pEdge, TWOedge, 1); |
|||
numEdges++; |
|||
edgeArrayH[xIndex][yIndex] = pEdge; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Generate the vertical edges and store in a work array. */ |
|||
XCALLOC(edgeArrayV, TWOedge **, 1 + numXNodes); |
|||
for (xIndex = 1; xIndex <= numXNodes; xIndex++) { |
|||
XCALLOC(edgeArrayV[xIndex], TWOedge *, numYNodes); |
|||
} |
|||
for (xIndex = 1; xIndex <= numXNodes; xIndex++) { |
|||
for (yIndex = 1; yIndex < numYNodes; yIndex++) { |
|||
pNode = nodeArray[xIndex][yIndex]; |
|||
pNextVNode = nodeArray[xIndex][yIndex + 1]; |
|||
if (pNode != NIL(TWOnode) && |
|||
pNextVNode != NIL(TWOnode)) { |
|||
XCALLOC(pEdge, TWOedge, 1); |
|||
numEdges++; |
|||
edgeArrayV[xIndex][yIndex] = pEdge; |
|||
} |
|||
} |
|||
} |
|||
pDevice->numEdges = numEdges; |
|||
|
|||
/* Now construct and count the elements and store the node/edge pointers. */ |
|||
numElem = 1; |
|||
for (yIndex = 1; yIndex < numYNodes; yIndex++) { |
|||
for (xIndex = 1; xIndex < numXNodes; xIndex++) { |
|||
pNode = nodeArray[xIndex][yIndex]; |
|||
pNextHNode = nodeArray[xIndex + 1][yIndex]; |
|||
pNextVNode = nodeArray[xIndex][yIndex + 1]; |
|||
pNextDNode = nodeArray[xIndex + 1][yIndex + 1]; |
|||
if (pNode != NIL(TWOnode) && |
|||
pNextHNode != NIL(TWOnode) && |
|||
pNextVNode != NIL(TWOnode) && |
|||
pNextDNode != NIL(TWOnode)) { |
|||
numElem++; |
|||
XCALLOC(pElem, TWOelem, 1); |
|||
pElem->pTLNode = pNode; |
|||
pElem->pTRNode = pNextHNode; |
|||
pElem->pBLNode = pNextVNode; |
|||
pElem->pBRNode = pNextDNode; |
|||
pElem->pTopEdge = edgeArrayH[xIndex][yIndex]; |
|||
pElem->pBotEdge = edgeArrayH[xIndex][yIndex + 1]; |
|||
pElem->pLeftEdge = edgeArrayV[xIndex][yIndex]; |
|||
pElem->pRightEdge = edgeArrayV[xIndex + 1][yIndex]; |
|||
pDevice->elemArray[xIndex][yIndex] = pElem; |
|||
} else { |
|||
pDevice->elemArray[xIndex][yIndex] = NIL(TWOelem); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Create and pack the 1D element array. */ |
|||
pDevice->numElems = numElem - 1; |
|||
XCALLOC(pDevice->elements, TWOelem *, 1 + numElem); |
|||
numElem = 1; |
|||
for (yIndex = 1; yIndex < numYNodes; yIndex++) { |
|||
for (xIndex = 1; xIndex < numXNodes; xIndex++) { |
|||
pElem = pDevice->elemArray[xIndex][yIndex]; |
|||
if (pElem != NIL(TWOelem)) { |
|||
pDevice->elements[numElem++] = pElem; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Now create back links from elements to nodes. */ |
|||
for (yIndex = 1; yIndex < numYNodes; yIndex++) { |
|||
for (xIndex = 1; xIndex < numXNodes; xIndex++) { |
|||
pElem = pDevice->elemArray[xIndex][yIndex]; |
|||
if (pElem != NIL(TWOelem)) { |
|||
pElem->pTLNode->pBRElem = pElem; |
|||
pElem->pTRNode->pBLElem = pElem; |
|||
pElem->pBLNode->pTRElem = pElem; |
|||
pElem->pBRNode->pTLElem = pElem; |
|||
if (xIndex > 1) { |
|||
pElem->pLeftElem = pDevice->elemArray[xIndex-1][yIndex]; |
|||
} |
|||
if (xIndex < numXNodes - 1) { |
|||
pElem->pRightElem = pDevice->elemArray[xIndex+1][yIndex]; |
|||
} |
|||
if (yIndex > 1) { |
|||
pElem->pTopElem = pDevice->elemArray[xIndex][yIndex-1]; |
|||
} |
|||
if (yIndex < numYNodes - 1) { |
|||
pElem->pBotElem = pDevice->elemArray[xIndex][yIndex+1]; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Establish the element types using domain info. */ |
|||
for (pD = pDomain; pD != NIL(TWOdomain); pD = pD->next) { |
|||
for (pM = pMaterial; pM != NIL(TWOmaterial); pM = pM->next) { |
|||
if (pD->material == pM->id) { |
|||
break; |
|||
} |
|||
} |
|||
elemType = pM->type; |
|||
for (yIndex = pD->iyLo; yIndex < pD->iyHi; yIndex++) { |
|||
for (xIndex = pD->ixLo; xIndex < pD->ixHi; xIndex++) { |
|||
pElem = pDevice->elemArray[xIndex][yIndex]; |
|||
if (pElem != NIL(TWOelem)) { |
|||
pElem->domain = pD->id; |
|||
pElem->elemType = elemType; |
|||
pElem->matlInfo = pM; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Establish the edge types. */ |
|||
for (yIndex = 1; yIndex < numYNodes; yIndex++) { |
|||
for (xIndex = 1; xIndex < numXNodes; xIndex++) { |
|||
pElem = pDevice->elemArray[xIndex][yIndex]; |
|||
if (pElem != NIL(TWOelem)) { |
|||
for (index = 0; index <= 3; index++) { |
|||
pEdge = pElem->pEdges[index]; |
|||
pNode = pElem->pNodes[index]; |
|||
pNode1 = pElem->pNodes[(index + 1) % 4]; |
|||
pElem1 = pNode1->pElems[index]; |
|||
|
|||
if (pNode->nodeType == CONTACT && |
|||
pNode1->nodeType == CONTACT) { |
|||
/* Contact edge */ |
|||
pEdge->edgeType = CONTACT; |
|||
} else if (pNode->nodeType == SCHOTTKY && |
|||
pNode1->nodeType == SCHOTTKY) { |
|||
/* Schottky edge */ |
|||
pEdge->edgeType = SCHOTTKY; |
|||
} else if (pElem1 == NIL(TWOelem)) { |
|||
/* Neumann edge */ |
|||
pEdge->edgeType = pElem->elemType; |
|||
} else if (pElem->elemType != pElem1->elemType) { |
|||
/* Interface edge */ |
|||
pEdge->edgeType = INTERFACE; |
|||
} else { /* Ignore heterojnxns for now */ |
|||
/* Heterojunction or Homojunction edge */ |
|||
pEdge->edgeType = pElem->elemType; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
resetEvalFlag(pDevice); |
|||
/* Set evaluation flags on nodes and edges. */ |
|||
/* Assign the dx and dy terms in the elements while we're at it. */ |
|||
for (yIndex = 1; yIndex < numYNodes; yIndex++) { |
|||
for (xIndex = 1; xIndex < numXNodes; xIndex++) { |
|||
pElem = pDevice->elemArray[xIndex][yIndex]; |
|||
if (pElem != NIL(TWOelem)) { |
|||
pElem->dx = xScale[xIndex + 1] - xScale[xIndex]; |
|||
pElem->dy = yScale[yIndex + 1] - yScale[yIndex]; |
|||
pElem->dxOverDy = pElem->dx / pElem->dy; |
|||
pElem->dyOverDx = pElem->dy / pElem->dx; |
|||
|
|||
/* |
|||
* Semiconductor elements take precedence over Insulator elements, so |
|||
* set them up first. |
|||
*/ |
|||
for (index = 0; index <= 3; index++) { |
|||
if (pElem->elemType == SEMICON) { |
|||
pNode = pElem->pNodes[index]; |
|||
if (!pNode->evaluated) { |
|||
pNode->evaluated = TRUE; |
|||
pElem->evalNodes[index] = TRUE; |
|||
} else { |
|||
pElem->evalNodes[index] = FALSE; |
|||
} |
|||
pEdge = pElem->pEdges[index]; |
|||
if (!pEdge->evaluated) { |
|||
pEdge->evaluated = TRUE; |
|||
pElem->evalEdges[index] = TRUE; |
|||
} else { |
|||
pElem->evalEdges[index] = FALSE; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Do a second setup pass for Insulator elements */ |
|||
/* Do mobility coefficients now, because we set up dx and dy |
|||
* in the previous pass |
|||
*/ |
|||
for (yIndex = 1; yIndex < numYNodes; yIndex++) { |
|||
for (xIndex = 1; xIndex < numXNodes; xIndex++) { |
|||
pElem = pDevice->elemArray[xIndex][yIndex]; |
|||
if (pElem != NIL(TWOelem)) { |
|||
pElem->direction = 0; |
|||
pElem->channel = 0; |
|||
pElem->surface = FALSE; |
|||
for (index = 0; index <= 3; index++) { |
|||
if (pElem->elemType == SEMICON) { |
|||
doMobCoeffs( pElem, index ); |
|||
} else if (pElem->elemType == INSULATOR) { |
|||
pNode = pElem->pNodes[index]; |
|||
if (!pNode->evaluated) { |
|||
pNode->evaluated = TRUE; |
|||
pElem->evalNodes[index] = TRUE; |
|||
} else { |
|||
pElem->evalNodes[index] = FALSE; |
|||
} |
|||
pEdge = pElem->pEdges[index]; |
|||
if (!pEdge->evaluated) { |
|||
pEdge->evaluated = TRUE; |
|||
pElem->evalEdges[index] = TRUE; |
|||
} else { |
|||
pElem->evalEdges[index] = FALSE; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Set up the equation numbers for nodes. */ |
|||
poiEqn = numEqn = 1; |
|||
for (eIndex = 1; eIndex <= pDevice->numElems; eIndex++) { |
|||
pElem = pDevice->elements[eIndex]; |
|||
for (index = 0; index <= 3; index++) { |
|||
if (pElem->evalNodes[index]) { |
|||
pNode = pElem->pNodes[index]; |
|||
if (pNode->nodeType != CONTACT) { |
|||
/* First assign potential equation numbers */ |
|||
if (pNode->nodeType != SCHOTTKY) { |
|||
pNode->poiEqn = poiEqn++; |
|||
pNode->psiEqn = numEqn++; |
|||
} |
|||
/* Now assign carrier-concentration equation numbers */ |
|||
if (pElem->elemType == INSULATOR) { |
|||
pNode->nEqn = 0; |
|||
pNode->pEqn = 0; |
|||
} else { |
|||
if (OneCarrier) { |
|||
/* n and p get same number */ |
|||
pNode->nEqn = numEqn; |
|||
pNode->pEqn = numEqn++; |
|||
} else { |
|||
pNode->nEqn = numEqn++; |
|||
pNode->pEqn = numEqn++; |
|||
} |
|||
} |
|||
} else { /* This is a contact node. */ |
|||
pNode->poiEqn = 0; |
|||
pNode->psiEqn = 0; |
|||
pNode->nEqn = 0; |
|||
pNode->pEqn = 0; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
pDevice->dimEquil = poiEqn; |
|||
pDevice->dimBias = numEqn; |
|||
|
|||
/* Open and Print Mesh Output File for Debugging */ |
|||
/* Nuked from release version */ |
|||
#ifdef NOTDEF |
|||
if (!(meshFile = fopen("mesh.out", "w"))) { |
|||
perror("mesh.out"); |
|||
exit(-1); |
|||
} |
|||
for (eIndex = 1; eIndex <= pDevice->numElems; eIndex++) { |
|||
pElem = pDevice->elements[eIndex]; |
|||
for (index = 0; index <= 3; index++) { |
|||
if (pElem->evalNodes[index]) { |
|||
pNode = pElem->pNodes[index]; |
|||
fprintf(meshFile, "node: %5d %5d %5d %5d\n", pNode->nodeI, |
|||
pNode->nodeJ, pNode->poiEqn, pNode->psiEqn); |
|||
} |
|||
} |
|||
} |
|||
fflush(meshFile); |
|||
fclose(meshFile); |
|||
#endif |
|||
|
|||
/* Delete work arrays. */ |
|||
for (xIndex = 1; xIndex <= numXNodes; xIndex++) { |
|||
FREE(nodeArray[xIndex]); |
|||
FREE(edgeArrayV[xIndex]); |
|||
} |
|||
for (xIndex = 1; xIndex < numXNodes; xIndex++) { |
|||
FREE(edgeArrayH[xIndex]); |
|||
} |
|||
FREE(nodeArray); |
|||
FREE(edgeArrayV); |
|||
FREE(edgeArrayH); |
|||
|
|||
/* |
|||
* TWOprnMesh( pDevice ); |
|||
*/ |
|||
} |
|||
|
|||
void |
|||
TWOprnMesh(TWOdevice *pDevice) |
|||
{ |
|||
int eIndex, index; |
|||
TWOelem *pElem; |
|||
TWOnode *pNode; |
|||
TWOedge *pEdge; |
|||
char *name; |
|||
|
|||
for (eIndex = 1; eIndex <= pDevice->numElems; eIndex++) { |
|||
pElem = pDevice->elements[eIndex]; |
|||
fprintf(stderr, "elem %5d:\n", eIndex); |
|||
for (index = 0; index <= 3; index++) { |
|||
if (pElem->evalNodes[index]) { |
|||
pNode = pElem->pNodes[index]; |
|||
switch (pNode->nodeType) { |
|||
case SEMICON: |
|||
name = "semiconductor"; |
|||
break; |
|||
case INSULATOR: |
|||
name = "insulator"; |
|||
break; |
|||
case CONTACT: |
|||
name = "contact"; |
|||
break; |
|||
case SCHOTTKY: |
|||
name = "schottky"; |
|||
break; |
|||
case INTERFACE: |
|||
name = "interface"; |
|||
break; |
|||
default: |
|||
name = "unknown"; |
|||
break; |
|||
} |
|||
fprintf(stderr, "node %5d: %s %5d %5d\n", index, name, |
|||
pNode->nodeI, pNode->nodeJ); |
|||
} |
|||
if (pElem->evalEdges[index]) { |
|||
pEdge = pElem->pEdges[index]; |
|||
switch (pEdge->edgeType) { |
|||
case SEMICON: |
|||
name = "semiconductor"; |
|||
break; |
|||
case INSULATOR: |
|||
name = "insulator"; |
|||
break; |
|||
case CONTACT: |
|||
name = "contact"; |
|||
break; |
|||
case SCHOTTKY: |
|||
name = "schottky"; |
|||
break; |
|||
case INTERFACE: |
|||
name = "interface"; |
|||
break; |
|||
default: |
|||
name = "unknown"; |
|||
break; |
|||
} |
|||
fprintf(stderr, "edge %5d: %s\n", index, name); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* |
|||
* We have a separate function for this, so that the setup routines can |
|||
* reset the state pointers without rebuilding the entire mesh. |
|||
*/ |
|||
void |
|||
TWOgetStatePointers(TWOdevice *pDevice, int *numStates) |
|||
{ |
|||
int eIndex, index; |
|||
TWOelem *pElem; |
|||
TWOnode *pNode; |
|||
TWOedge *pEdge; |
|||
|
|||
for (eIndex = 1; eIndex <= pDevice->numElems; eIndex++) { |
|||
pElem = pDevice->elements[eIndex]; |
|||
for (index = 0; index <= 3; index++) { |
|||
if (pElem->evalNodes[index]) { |
|||
pNode = pElem->pNodes[index]; |
|||
pNode->nodeState = *numStates; |
|||
*numStates += TWOnumNodeStates; |
|||
} |
|||
if (pElem->evalEdges[index]) { |
|||
pEdge = pElem->pEdges[index]; |
|||
pEdge->edgeState = *numStates; |
|||
*numStates += TWOnumEdgeStates; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* |
|||
* This function computes the percentages of the total semiconductor |
|||
* width of an edge on the negative and positive sides of the edge |
|||
*/ |
|||
static void |
|||
doMobCoeffs(TWOelem *pElem, int index) |
|||
{ |
|||
TWOelem *pNElem; |
|||
TWOedge *pEdge; |
|||
double dl1 = 0.0, dl2 = 0.0; |
|||
|
|||
pNElem = pElem->pElems[ index ]; |
|||
pEdge = pElem->pEdges[ index ]; |
|||
|
|||
/* If neighbor is not SEMICON, assign and return */ |
|||
if ( (pNElem == NIL(TWOelem)) || (pNElem->elemType == INSULATOR) ) { |
|||
if ( index == 0 || index == 3 ) { |
|||
pEdge->kNeg = 0.0; |
|||
pEdge->kPos = 1.0; |
|||
} else { |
|||
pEdge->kNeg = 1.0; |
|||
pEdge->kPos = 0.0; |
|||
} |
|||
return; |
|||
} |
|||
|
|||
/* Find appropriate dimensions of the elements */ |
|||
switch ( index ) { |
|||
case 0: |
|||
dl1 = pNElem->dy; |
|||
dl2 = pElem->dy; |
|||
break; |
|||
case 1: |
|||
dl1 = pElem->dx; |
|||
dl2 = pNElem->dx; |
|||
break; |
|||
case 2: |
|||
dl1 = pElem->dy; |
|||
dl2 = pNElem->dy; |
|||
break; |
|||
case 3: |
|||
dl1 = pNElem->dx; |
|||
dl2 = pElem->dx; |
|||
break; |
|||
} |
|||
|
|||
/* Assign coefficients */ |
|||
pEdge->kNeg = dl1 / (dl1 + dl2); |
|||
pEdge->kPos = dl2 / (dl1 + dl2); |
|||
|
|||
} |
|||
|
|||
static void |
|||
resetEvalFlag(TWOdevice *pDevice) |
|||
{ |
|||
int index, eIndex; |
|||
TWOelem *pElem; |
|||
|
|||
for (eIndex = 1; eIndex <= pDevice->numElems; eIndex++) { |
|||
pElem = pDevice->elements[eIndex]; |
|||
for (index = 0; index <= 3; index++) { |
|||
pElem->pNodes[index]->evaluated = FALSE; |
|||
pElem->pEdges[index]->evaluated = FALSE; |
|||
} |
|||
} |
|||
} |
|||
1399
src/ciderlib/twod/twomobdv.c
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
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; |
|||
} |
|||
@ -0,0 +1,131 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1990 David A. Gates, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numglobs.h" |
|||
#include "twomesh.h" |
|||
#include "twoddefs.h" |
|||
#include "twodext.h" |
|||
|
|||
/* |
|||
* Compute the 2-D field-dependent mobility at the center of an element. |
|||
* It is known a priori that the element belongs to a semiconductor domain. |
|||
*/ |
|||
|
|||
void |
|||
TWO_mobility(TWOelem *pElem, double eSurf) |
|||
{ |
|||
|
|||
TWOedge *pTEdge, *pBEdge, *pLEdge, *pREdge; |
|||
double dx, dy, rDx, rDy; |
|||
double enx, eny, wnx, wny, concav; |
|||
double epx, epy, wpx, wpy; |
|||
|
|||
/* Initialize various quantities */ |
|||
dx = pElem->dx; |
|||
dy = pElem->dy; |
|||
rDx = 0.5 / dx; /* Includes averaging factor of 0.5 */ |
|||
rDy = 0.5 / dy; /* Includes averaging factor of 0.5 */ |
|||
|
|||
/* Get pointers to element's edges */ |
|||
pTEdge = pElem->pTopEdge; |
|||
pBEdge = pElem->pBotEdge; |
|||
pLEdge = pElem->pLeftEdge; |
|||
pREdge = pElem->pRightEdge; |
|||
|
|||
/* Calculate electric field at element center */ |
|||
enx = -rDx *(pTEdge->dPsi + pTEdge->dCBand + pBEdge->dPsi + pBEdge->dCBand); |
|||
epx = -rDx *(pTEdge->dPsi - pTEdge->dVBand + pBEdge->dPsi - pBEdge->dVBand); |
|||
eny = -rDy *(pLEdge->dPsi + pLEdge->dCBand + pREdge->dPsi + pREdge->dCBand); |
|||
epy = -rDy *(pLEdge->dPsi - pLEdge->dVBand + pREdge->dPsi - pREdge->dVBand); |
|||
|
|||
/* Calculate weighted carrier driving force at element center */ |
|||
wnx = rDx * (pTEdge->wdfn + pBEdge->wdfn); |
|||
wpx = rDx * (pTEdge->wdfp + pBEdge->wdfp); |
|||
wny = rDy * (pLEdge->wdfn + pREdge->wdfn); |
|||
wpy = rDy * (pLEdge->wdfp + pREdge->wdfp); |
|||
|
|||
/* compute the mobility for the element */ |
|||
/* Average concentrations at the four corners */ |
|||
concav = 0.25 * ( pElem->pTLNode->totalConc + pElem->pTRNode->totalConc + |
|||
pElem->pBLNode->totalConc + pElem->pBRNode->totalConc ); |
|||
MOBsurfElec(pElem->matlInfo, pElem, enx, eny, eSurf, wnx, wny, concav); |
|||
MOBsurfHole(pElem->matlInfo, pElem, epx, epy, eSurf, wpx, wpy, concav); |
|||
return; |
|||
} |
|||
|
|||
void |
|||
TWONmobility(TWOelem *pElem, double eSurf) |
|||
{ |
|||
|
|||
TWOedge *pTEdge, *pBEdge, *pLEdge, *pREdge; |
|||
double dx, dy, rDx, rDy; |
|||
double enx, eny, wnx, wny, concav; |
|||
|
|||
/* Initialize various quantities */ |
|||
dx = pElem->dx; |
|||
dy = pElem->dy; |
|||
rDx = 0.5 / dx; /* Includes averaging factor of 0.5 */ |
|||
rDy = 0.5 / dy; /* Includes averaging factor of 0.5 */ |
|||
|
|||
/* Get pointers to element's edges */ |
|||
pTEdge = pElem->pTopEdge; |
|||
pBEdge = pElem->pBotEdge; |
|||
pLEdge = pElem->pLeftEdge; |
|||
pREdge = pElem->pRightEdge; |
|||
|
|||
/* Calculate electric field at element center */ |
|||
enx = -rDx *(pTEdge->dPsi + pTEdge->dCBand + pBEdge->dPsi + pBEdge->dCBand); |
|||
eny = -rDy *(pLEdge->dPsi + pLEdge->dCBand + pREdge->dPsi + pREdge->dCBand); |
|||
|
|||
/* Calculate weighted carrier driving force at element center */ |
|||
wnx = rDx * (pTEdge->wdfn + pBEdge->wdfn); |
|||
wny = rDy * (pLEdge->wdfn + pREdge->wdfn); |
|||
|
|||
/* compute the mobility for the element */ |
|||
/* Average concentrations at the four corners */ |
|||
concav = 0.25 * ( pElem->pTLNode->totalConc + pElem->pTRNode->totalConc + |
|||
pElem->pBLNode->totalConc + pElem->pBRNode->totalConc ); |
|||
MOBsurfElec(pElem->matlInfo, pElem, enx, eny, eSurf, wnx, wny, concav); |
|||
|
|||
return; |
|||
} |
|||
|
|||
void |
|||
TWOPmobility(TWOelem *pElem, double eSurf) |
|||
{ |
|||
|
|||
TWOedge *pTEdge, *pBEdge, *pLEdge, *pREdge; |
|||
double dx, dy, rDx, rDy; |
|||
double epx, epy, wpx, wpy, concav; |
|||
|
|||
/* Initialize various quantities */ |
|||
dx = pElem->dx; |
|||
dy = pElem->dy; |
|||
rDx = 0.5 / dx; /* Includes averaging factor of 0.5 */ |
|||
rDy = 0.5 / dy; /* Includes averaging factor of 0.5 */ |
|||
|
|||
/* Get pointers to element's edges */ |
|||
pTEdge = pElem->pTopEdge; |
|||
pBEdge = pElem->pBotEdge; |
|||
pLEdge = pElem->pLeftEdge; |
|||
pREdge = pElem->pRightEdge; |
|||
|
|||
/* Calculate electric field at element center */ |
|||
epx = -rDx *(pTEdge->dPsi - pTEdge->dVBand + pBEdge->dPsi - pBEdge->dVBand); |
|||
epy = -rDy *(pLEdge->dPsi - pLEdge->dVBand + pREdge->dPsi - pREdge->dVBand); |
|||
|
|||
/* Calculate weighted carrier driving force at element center */ |
|||
wpx = rDx * (pTEdge->wdfp + pBEdge->wdfp); |
|||
wpy = rDy * (pLEdge->wdfp + pREdge->wdfp); |
|||
|
|||
/* compute the mobility for the element */ |
|||
/* Average concentrations at the four corners */ |
|||
concav = 0.25 * ( pElem->pTLNode->totalConc + pElem->pTRNode->totalConc + |
|||
pElem->pBLNode->totalConc + pElem->pBRNode->totalConc ); |
|||
MOBsurfHole(pElem->matlInfo, pElem, epx, epy, eSurf, wpx, wpy, concav); |
|||
|
|||
return; |
|||
} |
|||
@ -0,0 +1,889 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group |
|||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numglobs.h" |
|||
#include "numenum.h" |
|||
#include "twomesh.h" |
|||
#include "twodev.h" |
|||
#include "bool.h" |
|||
#include "spMatrix.h" |
|||
#include "twoddefs.h" |
|||
#include "twodext.h" |
|||
#include "cidersupt.h" |
|||
#include "../../maths/misc/bernoull.h" |
|||
|
|||
|
|||
/* |
|||
* Functions to setup and solve the continuity equations. |
|||
* Both continuity equations are solved. |
|||
* Separate functions are used for one continuity equation. |
|||
*/ |
|||
|
|||
|
|||
/* |
|||
* Setup matrix pointers to Jacobian entries and |
|||
* store direct pointers with the nodes. |
|||
*/ |
|||
|
|||
void |
|||
TWONjacBuild(TWOdevice *pDevice) |
|||
{ |
|||
char *matrix = pDevice->matrix; |
|||
TWOelem *pElem; |
|||
TWOnode *pNode; |
|||
TWOchannel *pCh; |
|||
int eIndex, nIndex; |
|||
int nextIndex; /* index of node to find next element */ |
|||
int psiEqn, nEqn; /* scratch for deref'd eqn numbers */ |
|||
int psiEqnTL = 0, nEqnTL = 0; |
|||
int psiEqnTR = 0, nEqnTR = 0; |
|||
int psiEqnBR = 0, nEqnBR = 0; |
|||
int psiEqnBL = 0, nEqnBL = 0; |
|||
int psiEqnInM = 0, psiEqnInP = 0; /* scratch for deref'd surface eqns */ |
|||
int psiEqnOxM = 0, psiEqnOxP = 0; /* M= more negative, P= more positive */ |
|||
|
|||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) { |
|||
pElem = pDevice->elements[ eIndex ]; |
|||
|
|||
/* first the self terms */ |
|||
for ( nIndex = 0; nIndex <= 3; nIndex++ ) { |
|||
pNode = pElem->pNodes[ nIndex ]; |
|||
/* get poisson-only pointer */ |
|||
psiEqn = pNode->psiEqn; |
|||
pNode->fPsiPsi = spGetElement( matrix, psiEqn, psiEqn ); |
|||
|
|||
if ( pElem->elemType == SEMICON ) { |
|||
/* get continuity-coupling terms */ |
|||
nEqn = pNode->nEqn; |
|||
pNode->pEqn = 0; /* Throw pEqn number into garbage. */ |
|||
/* pointers for additional terms */ |
|||
pNode->fPsiN = spGetElement( matrix, psiEqn, nEqn ); |
|||
pNode->fNPsi = spGetElement( matrix, nEqn, psiEqn ); |
|||
pNode->fNN = spGetElement( matrix, nEqn, nEqn ); |
|||
} else { |
|||
nEqn = 0; |
|||
} |
|||
/* save equation indices */ |
|||
switch ( nIndex ) { |
|||
case 0: /* TL Node */ |
|||
psiEqnTL = psiEqn; |
|||
nEqnTL = nEqn; |
|||
break; |
|||
case 1: /* TR Node */ |
|||
psiEqnTR = psiEqn; |
|||
nEqnTR = nEqn; |
|||
break; |
|||
case 2: /* BR Node */ |
|||
psiEqnBR = psiEqn; |
|||
nEqnBR = nEqn; |
|||
break; |
|||
case 3: /* BL Node */ |
|||
psiEqnBL = psiEqn; |
|||
nEqnBL = nEqn; |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
} |
|||
|
|||
/* now terms to couple to adjacent nodes */ |
|||
pNode = pElem->pTLNode; |
|||
pNode->fPsiPsiiP1 = spGetElement(matrix, psiEqnTL, psiEqnTR ); |
|||
pNode->fPsiPsijP1 = spGetElement(matrix, psiEqnTL, psiEqnBL ); |
|||
if ( pElem->elemType == SEMICON ) { |
|||
/* continuity equation pointers */ |
|||
pNode->fNPsiiP1 = spGetElement( matrix, nEqnTL, psiEqnTR ); |
|||
pNode->fNNiP1 = spGetElement( matrix, nEqnTL, nEqnTR ); |
|||
pNode->fNPsijP1 = spGetElement( matrix, nEqnTL, psiEqnBL ); |
|||
pNode->fNNjP1 = spGetElement( matrix, nEqnTL, nEqnBL ); |
|||
/* Surface Mobility Model depends on diagonal node values */ |
|||
if ( MobDeriv && SurfaceMobility && pElem->channel ) { |
|||
pNode->fNPsiiP1jP1 = spGetElement( matrix, nEqnTL, psiEqnBR ); |
|||
pNode->fNNiP1jP1 = spGetElement( matrix, nEqnTL, nEqnBR ); |
|||
} |
|||
} |
|||
|
|||
pNode = pElem->pTRNode; |
|||
pNode->fPsiPsiiM1 = spGetElement(matrix, psiEqnTR, psiEqnTL ); |
|||
pNode->fPsiPsijP1 = spGetElement(matrix, psiEqnTR, psiEqnBR ); |
|||
if ( pElem->elemType == SEMICON ) { |
|||
/* continuity equation pointers */ |
|||
pNode->fNPsiiM1 = spGetElement( matrix, nEqnTR, psiEqnTL ); |
|||
pNode->fNNiM1 = spGetElement( matrix, nEqnTR, nEqnTL ); |
|||
pNode->fNPsijP1 = spGetElement( matrix, nEqnTR, psiEqnBR ); |
|||
pNode->fNNjP1 = spGetElement( matrix, nEqnTR, nEqnBR ); |
|||
/* Surface Mobility Model depends on diagonal node values */ |
|||
if ( MobDeriv && SurfaceMobility && pElem->channel ) { |
|||
pNode->fNPsiiM1jP1 = spGetElement( matrix, nEqnTR, psiEqnBL ); |
|||
pNode->fNNiM1jP1 = spGetElement( matrix, nEqnTR, nEqnBL ); |
|||
} |
|||
} |
|||
|
|||
pNode = pElem->pBRNode; |
|||
pNode->fPsiPsiiM1 = spGetElement(matrix, psiEqnBR, psiEqnBL ); |
|||
pNode->fPsiPsijM1 = spGetElement(matrix, psiEqnBR, psiEqnTR ); |
|||
if ( pElem->elemType == SEMICON ) { |
|||
/* continuity equation pointers */ |
|||
pNode->fNPsiiM1 = spGetElement( matrix, nEqnBR, psiEqnBL ); |
|||
pNode->fNNiM1 = spGetElement( matrix, nEqnBR, nEqnBL ); |
|||
pNode->fNPsijM1 = spGetElement( matrix, nEqnBR, psiEqnTR ); |
|||
pNode->fNNjM1 = spGetElement( matrix, nEqnBR, nEqnTR ); |
|||
/* Surface Mobility Model depends on diagonal node values */ |
|||
if ( MobDeriv && SurfaceMobility && pElem->channel ) { |
|||
pNode->fNPsiiM1jM1 = spGetElement( matrix, nEqnBR, psiEqnTL ); |
|||
pNode->fNNiM1jM1 = spGetElement( matrix, nEqnBR, nEqnTL ); |
|||
} |
|||
} |
|||
|
|||
pNode = pElem->pBLNode; |
|||
pNode->fPsiPsiiP1 = spGetElement(matrix, psiEqnBL, psiEqnBR ); |
|||
pNode->fPsiPsijM1 = spGetElement(matrix, psiEqnBL, psiEqnTL ); |
|||
if ( pElem->elemType == SEMICON ) { |
|||
/* continuity equation pointers */ |
|||
pNode->fNPsiiP1 = spGetElement( matrix, nEqnBL, psiEqnBR ); |
|||
pNode->fNNiP1 = spGetElement( matrix, nEqnBL, nEqnBR ); |
|||
pNode->fNPsijM1 = spGetElement( matrix, nEqnBL, psiEqnTL ); |
|||
pNode->fNNjM1 = spGetElement( matrix, nEqnBL, nEqnTL ); |
|||
/* Surface Mobility Model depends on diagonal node values */ |
|||
if ( MobDeriv && SurfaceMobility && pElem->channel ) { |
|||
pNode->fNPsiiP1jM1 = spGetElement( matrix, nEqnBL, psiEqnTR ); |
|||
pNode->fNNiP1jM1 = spGetElement( matrix, nEqnBL, nEqnTR ); |
|||
} |
|||
} |
|||
} |
|||
/* |
|||
* Add terms for surface-field of inversion-layer mobility model. |
|||
* Elements MUST be made from silicon for this to work. |
|||
* No empty elements are allowed. |
|||
* Don't need these pointers if SurfaceMobility isn't set. |
|||
*/ |
|||
if ( MobDeriv && SurfaceMobility ) { |
|||
for ( pCh = pDevice->pChannel; pCh != NIL(TWOchannel); |
|||
pCh = pCh->next ) { |
|||
pElem = pCh->pNElem; |
|||
switch (pCh->type) { |
|||
case 0: |
|||
psiEqnInM = pElem->pBLNode->psiEqn; |
|||
psiEqnInP = pElem->pBRNode->psiEqn; |
|||
psiEqnOxM = pElem->pTLNode->psiEqn; |
|||
psiEqnOxP = pElem->pTRNode->psiEqn; |
|||
break; |
|||
case 1: |
|||
psiEqnInM = pElem->pTLNode->psiEqn; |
|||
psiEqnInP = pElem->pBLNode->psiEqn; |
|||
psiEqnOxM = pElem->pTRNode->psiEqn; |
|||
psiEqnOxP = pElem->pBRNode->psiEqn; |
|||
break; |
|||
case 2: |
|||
psiEqnInM = pElem->pTLNode->psiEqn; |
|||
psiEqnInP = pElem->pTRNode->psiEqn; |
|||
psiEqnOxM = pElem->pBLNode->psiEqn; |
|||
psiEqnOxP = pElem->pBRNode->psiEqn; |
|||
break; |
|||
case 3: |
|||
psiEqnInM = pElem->pTRNode->psiEqn; |
|||
psiEqnInP = pElem->pBRNode->psiEqn; |
|||
psiEqnOxM = pElem->pTLNode->psiEqn; |
|||
psiEqnOxP = pElem->pBLNode->psiEqn; |
|||
break; |
|||
} |
|||
pElem = pCh->pSeed; |
|||
nextIndex = (pCh->type + 2)%4; |
|||
while (pElem && pElem->channel == pCh->id) { |
|||
for ( nIndex = 0; nIndex <= 3; nIndex++ ) { |
|||
pNode = pElem->pNodes[ nIndex ]; |
|||
psiEqn = pNode->psiEqn; |
|||
nEqn = pNode->nEqn; |
|||
if ( pCh->type % 2 == 0 ) { /* Vertical Slice */ |
|||
if ( nIndex == 0 || nIndex == 3 ) { /* Left Side */ |
|||
pNode->fNPsiIn = spGetElement( matrix, nEqn, psiEqnInM ); |
|||
pNode->fNPsiInP1 = spGetElement( matrix, nEqn, psiEqnInP ); |
|||
pNode->fNPsiOx = spGetElement( matrix, nEqn, psiEqnOxM ); |
|||
pNode->fNPsiOxP1 = spGetElement( matrix, nEqn, psiEqnOxP ); |
|||
} else { /* Right Side */ |
|||
pNode->fNPsiInM1 = spGetElement( matrix, nEqn, psiEqnInM ); |
|||
pNode->fNPsiIn = spGetElement( matrix, nEqn, psiEqnInP ); |
|||
pNode->fNPsiOxM1 = spGetElement( matrix, nEqn, psiEqnOxM ); |
|||
pNode->fNPsiOx = spGetElement( matrix, nEqn, psiEqnOxP ); |
|||
} |
|||
} else { /* Horizontal Slice */ |
|||
if ( nIndex <= 1 ) { /* Top Side */ |
|||
pNode->fNPsiIn = spGetElement( matrix, nEqn, psiEqnInM ); |
|||
pNode->fNPsiInP1 = spGetElement( matrix, nEqn, psiEqnInP ); |
|||
pNode->fNPsiOx = spGetElement( matrix, nEqn, psiEqnOxM ); |
|||
pNode->fNPsiOxP1 = spGetElement( matrix, nEqn, psiEqnOxP ); |
|||
} else { /* Bottom Side */ |
|||
pNode->fNPsiInM1 = spGetElement( matrix, nEqn, psiEqnInM ); |
|||
pNode->fNPsiIn = spGetElement( matrix, nEqn, psiEqnInP ); |
|||
pNode->fNPsiOxM1 = spGetElement( matrix, nEqn, psiEqnOxM ); |
|||
pNode->fNPsiOx = spGetElement( matrix, nEqn, psiEqnOxP ); |
|||
} |
|||
} |
|||
} /* endfor nIndex */ |
|||
pElem = pElem->pElems[ nextIndex ]; |
|||
} /* endwhile pElem */ |
|||
} /* endfor pCh */ |
|||
} /* endif SurfaceMobility */ |
|||
} |
|||
|
|||
|
|||
/* |
|||
* The Jacobian and Rhs are loaded by the following function. |
|||
* Inputs are the transient analysis flag and the transient |
|||
* information structure |
|||
*/ |
|||
|
|||
void |
|||
TWONsysLoad(TWOdevice *pDevice, BOOLEAN tranAnalysis, TWOtranInfo *info) |
|||
{ |
|||
TWOelem *pElem; |
|||
TWOnode *pNode; |
|||
TWOedge *pHEdge, *pVEdge; |
|||
TWOedge *pTEdge, *pBEdge, *pLEdge, *pREdge; |
|||
TWOchannel *pCh; |
|||
int index, eIndex; |
|||
int nextIndex; /* index of node to find next element */ |
|||
double *pRhs = pDevice->rhs; |
|||
double dx, dy, dxdy, dyOverDx, dxOverDy; |
|||
double ds; |
|||
double dPsiT, dPsiB, dPsiL, dPsiR; |
|||
double rhsN; |
|||
double nConc, pConc; |
|||
double perTime = 0.0; |
|||
|
|||
/* first compute the currents and derivatives */ |
|||
TWONcommonTerms( pDevice, FALSE, tranAnalysis, info ); |
|||
|
|||
/* find reciprocal timestep */ |
|||
if ( tranAnalysis ) { |
|||
perTime = info->intCoeff[0]; |
|||
} |
|||
|
|||
/* zero the rhs vector */ |
|||
for ( index = 1 ; index <= pDevice->numEqns ; index++ ) { |
|||
pRhs[ index ] = 0.0; |
|||
} |
|||
|
|||
/* zero the matrix */ |
|||
spClear( pDevice->matrix ); |
|||
|
|||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) { |
|||
pElem = pDevice->elements[ eIndex ]; |
|||
|
|||
dx = 0.5 * pElem->dx; |
|||
dy = 0.5 * pElem->dy; |
|||
dxdy = dx * dy; |
|||
dxOverDy = 0.5 * pElem->epsRel * pElem->dxOverDy; |
|||
dyOverDx = 0.5 * pElem->epsRel * pElem->dyOverDx; |
|||
|
|||
pTEdge = pElem->pTopEdge; |
|||
pBEdge = pElem->pBotEdge; |
|||
pLEdge = pElem->pLeftEdge; |
|||
pREdge = pElem->pRightEdge; |
|||
dPsiT = pTEdge->dPsi; |
|||
dPsiB = pBEdge->dPsi; |
|||
dPsiL = pLEdge->dPsi; |
|||
dPsiR = pREdge->dPsi; |
|||
|
|||
/* load for all i,j */ |
|||
for ( index = 0; index <= 3; index++ ) { |
|||
pNode = pElem->pNodes[ index ]; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
*(pNode->fPsiPsi) += dyOverDx + dxOverDy; |
|||
if ( index <= 1 ) { |
|||
pHEdge = pTEdge; |
|||
} else { |
|||
pHEdge = pBEdge; |
|||
} |
|||
if ( index == 0 || index == 3 ) { |
|||
pVEdge = pLEdge; |
|||
} else { |
|||
pVEdge = pREdge; |
|||
} |
|||
/* Add surface state charges. */ |
|||
pRhs[ pNode->psiEqn ] += dx * pHEdge->qf; |
|||
pRhs[ pNode->psiEqn ] += dy * pVEdge->qf; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
nConc = *(pDevice->devState0 + pNode->nodeN); |
|||
pConc = *(pDevice->devState0 + pNode->nodeP); |
|||
|
|||
*(pNode->fPsiN) += dxdy; |
|||
*(pNode->fPsiPsi) += dxdy * pConc; |
|||
*(pNode->fNPsi) -= dy * pHEdge->dJnDpsiP1 + dx * pVEdge->dJnDpsiP1; |
|||
pRhs[ pNode->psiEqn ] += dxdy * (pNode->netConc + pConc - nConc); |
|||
|
|||
/* Handle generation terms */ |
|||
*(pNode->fNN) -= dxdy * pNode->dUdN; |
|||
*(pNode->fNPsi) += dxdy * pNode->dUdP * pConc; |
|||
rhsN = - dxdy * pNode->uNet; |
|||
pRhs[ pNode->nEqn ] -= rhsN; |
|||
|
|||
/* Handle dXdT continuity terms */ |
|||
if ( tranAnalysis ) { |
|||
*(pNode->fNN) -= dxdy * perTime; |
|||
pRhs[ pNode->nEqn ] += dxdy * pNode->dNdT; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Handle neighbor and edge dependent terms */ |
|||
pNode = pElem->pTLNode; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
pRhs[ pNode->psiEqn ] -= -dyOverDx * dPsiT - dxOverDy * dPsiL; |
|||
*(pNode->fPsiPsiiP1) -= dyOverDx; |
|||
*(pNode->fPsiPsijP1) -= dxOverDy; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
pRhs[ pNode->nEqn ] -= dy * pTEdge->jn + dx * pLEdge->jn; |
|||
*(pNode->fNN) += dy * pTEdge->dJnDn + dx * pLEdge->dJnDn; |
|||
*(pNode->fNPsiiP1) += dy * pTEdge->dJnDpsiP1; |
|||
*(pNode->fNNiP1) += dy * pTEdge->dJnDnP1; |
|||
*(pNode->fNPsijP1) += dx * pLEdge->dJnDpsiP1; |
|||
*(pNode->fNNjP1) += dx * pLEdge->dJnDnP1; |
|||
} |
|||
} |
|||
pNode = pElem->pTRNode; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
pRhs[ pNode->psiEqn ] -= dyOverDx * dPsiT - dxOverDy * dPsiR; |
|||
*(pNode->fPsiPsiiM1) -= dyOverDx; |
|||
*(pNode->fPsiPsijP1) -= dxOverDy; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
pRhs[ pNode->nEqn ] -= -dy * pTEdge->jn + dx * pREdge->jn; |
|||
*(pNode->fNN) += -dy * pTEdge->dJnDnP1 + dx * pREdge->dJnDn; |
|||
*(pNode->fNPsiiM1) += dy * pTEdge->dJnDpsiP1; |
|||
*(pNode->fNNiM1) -= dy * pTEdge->dJnDn; |
|||
*(pNode->fNPsijP1) += dx * pREdge->dJnDpsiP1; |
|||
*(pNode->fNNjP1) += dx * pREdge->dJnDnP1; |
|||
} |
|||
} |
|||
pNode = pElem->pBRNode; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
pRhs[ pNode->psiEqn ] -= dyOverDx * dPsiB + dxOverDy * dPsiR; |
|||
*(pNode->fPsiPsiiM1) -= dyOverDx; |
|||
*(pNode->fPsiPsijM1) -= dxOverDy; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
pRhs[ pNode->nEqn ] -= -dy * pBEdge->jn - dx * pREdge->jn; |
|||
*(pNode->fNN) += -dy * pBEdge->dJnDnP1 - dx * pREdge->dJnDnP1; |
|||
*(pNode->fNPsiiM1) += dy * pBEdge->dJnDpsiP1; |
|||
*(pNode->fNNiM1) -= dy * pBEdge->dJnDn; |
|||
*(pNode->fNPsijM1) += dx * pREdge->dJnDpsiP1; |
|||
*(pNode->fNNjM1) -= dx * pREdge->dJnDn; |
|||
} |
|||
} |
|||
pNode = pElem->pBLNode; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
pRhs[ pNode->psiEqn ] -= -dyOverDx * dPsiB + dxOverDy * dPsiL; |
|||
*(pNode->fPsiPsiiP1) -= dyOverDx; |
|||
*(pNode->fPsiPsijM1) -= dxOverDy; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
pRhs[ pNode->nEqn ] -= dy * pBEdge->jn - dx * pLEdge->jn; |
|||
*(pNode->fNN) += dy * pBEdge->dJnDn - dx * pLEdge->dJnDnP1; |
|||
*(pNode->fNPsiiP1) += dy * pBEdge->dJnDpsiP1; |
|||
*(pNode->fNNiP1) += dy * pBEdge->dJnDnP1; |
|||
*(pNode->fNPsijM1) += dx * pLEdge->dJnDpsiP1; |
|||
*(pNode->fNNjM1) -= dx * pLEdge->dJnDn; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Calculate the Inversion-Layer Mobility Dependent Terms in Jac. */ |
|||
if ( MobDeriv && SurfaceMobility ) { |
|||
for ( pCh = pDevice->pChannel; pCh != NIL(TWOchannel); |
|||
pCh = pCh->next ) { |
|||
/* Find effective height of oxide element at interface. */ |
|||
if ( pCh->type%2 == 0 ) { /* Vertical slice */ |
|||
ds = pCh->pNElem->dy / pCh->pNElem->epsRel; |
|||
} else { /* Horizontal slice */ |
|||
ds = pCh->pNElem->dx / pCh->pNElem->epsRel; |
|||
} |
|||
pElem = pCh->pSeed; |
|||
nextIndex = (pCh->type + 2)%4; |
|||
while (pElem && pElem->channel == pCh->id) { |
|||
TWONmobDeriv( pElem, pCh->type, ds ); |
|||
pElem = pElem->pElems[ nextIndex ]; |
|||
} |
|||
} /* endfor pCh != NIL */ |
|||
} /* endif MobDeriv and SurfaceMobility */ |
|||
} |
|||
|
|||
|
|||
/* |
|||
* This function used only for direct method ac analysis. |
|||
* Used to load only the dc Jacobian matrix. Rhs is unaffected |
|||
*/ |
|||
|
|||
void |
|||
TWONjacLoad(TWOdevice *pDevice) |
|||
{ |
|||
TWOelem *pElem; |
|||
TWOnode *pNode; |
|||
TWOedge *pHEdge, *pVEdge; |
|||
TWOedge *pTEdge, *pBEdge, *pLEdge, *pREdge; |
|||
TWOchannel *pCh; |
|||
int index, eIndex; |
|||
int nextIndex; /* index of node to find next element */ |
|||
double dx, dy, dxdy, dyOverDx, dxOverDy; |
|||
double ds; |
|||
double pConc; |
|||
|
|||
/* first compute the currents and derivatives */ |
|||
TWONcommonTerms( pDevice, FALSE, FALSE, NIL(TWOtranInfo) ); |
|||
|
|||
/* zero the matrix */ |
|||
spClear( pDevice->matrix ); |
|||
|
|||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) { |
|||
pElem = pDevice->elements[ eIndex ]; |
|||
dx = 0.5 * pElem->dx; |
|||
dy = 0.5 * pElem->dy; |
|||
dxdy = dx * dy; |
|||
dxOverDy = 0.5 * pElem->epsRel * pElem->dxOverDy; |
|||
dyOverDx = 0.5 * pElem->epsRel * pElem->dyOverDx; |
|||
|
|||
pTEdge = pElem->pTopEdge; |
|||
pBEdge = pElem->pBotEdge; |
|||
pLEdge = pElem->pLeftEdge; |
|||
pREdge = pElem->pRightEdge; |
|||
|
|||
/* load for all i,j */ |
|||
for ( index = 0; index <= 3; index++ ) { |
|||
pNode = pElem->pNodes[ index ]; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
*(pNode->fPsiPsi) += dyOverDx + dxOverDy; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
if ( index <= 1 ) { |
|||
pHEdge = pTEdge; |
|||
} else { |
|||
pHEdge = pBEdge; |
|||
} |
|||
if ( index == 0 || index == 3 ) { |
|||
pVEdge = pLEdge; |
|||
} else { |
|||
pVEdge = pREdge; |
|||
} |
|||
pConc = *(pDevice->devState0 + pNode->nodeP); |
|||
*(pNode->fPsiN) += dxdy; |
|||
*(pNode->fPsiPsi) += dxdy * pConc; |
|||
*(pNode->fNPsi) -= dy * pHEdge->dJnDpsiP1 + dx * pVEdge->dJnDpsiP1; |
|||
|
|||
/* Handle generation terms */ |
|||
*(pNode->fNN) -= dxdy * pNode->dUdN; |
|||
*(pNode->fNPsi) += dxdy * pNode->dUdP * pConc; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Handle neighbor and edge dependent terms */ |
|||
pNode = pElem->pTLNode; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
*(pNode->fPsiPsiiP1) -= dyOverDx; |
|||
*(pNode->fPsiPsijP1) -= dxOverDy; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
*(pNode->fNN) += dy * pTEdge->dJnDn + dx * pLEdge->dJnDn; |
|||
*(pNode->fNPsiiP1) += dy * pTEdge->dJnDpsiP1; |
|||
*(pNode->fNNiP1) += dy * pTEdge->dJnDnP1; |
|||
*(pNode->fNPsijP1) += dx * pLEdge->dJnDpsiP1; |
|||
*(pNode->fNNjP1) += dx * pLEdge->dJnDnP1; |
|||
} |
|||
} |
|||
pNode = pElem->pTRNode; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
*(pNode->fPsiPsiiM1) -= dyOverDx; |
|||
*(pNode->fPsiPsijP1) -= dxOverDy; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
*(pNode->fNN) += -dy * pTEdge->dJnDnP1 + dx * pREdge->dJnDn; |
|||
*(pNode->fNPsiiM1) += dy * pTEdge->dJnDpsiP1; |
|||
*(pNode->fNNiM1) -= dy * pTEdge->dJnDn; |
|||
*(pNode->fNPsijP1) += dx * pREdge->dJnDpsiP1; |
|||
*(pNode->fNNjP1) += dx * pREdge->dJnDnP1; |
|||
} |
|||
} |
|||
pNode = pElem->pBRNode; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
*(pNode->fPsiPsiiM1) -= dyOverDx; |
|||
*(pNode->fPsiPsijM1) -= dxOverDy; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
*(pNode->fNN) += -dy * pBEdge->dJnDnP1 - dx * pREdge->dJnDnP1; |
|||
*(pNode->fNPsiiM1) += dy * pBEdge->dJnDpsiP1; |
|||
*(pNode->fNNiM1) -= dy * pBEdge->dJnDn; |
|||
*(pNode->fNPsijM1) += dx * pREdge->dJnDpsiP1; |
|||
*(pNode->fNNjM1) -= dx * pREdge->dJnDn; |
|||
} |
|||
} |
|||
pNode = pElem->pBLNode; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
*(pNode->fPsiPsiiP1) -= dyOverDx; |
|||
*(pNode->fPsiPsijM1) -= dxOverDy; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
*(pNode->fNN) += dy * pBEdge->dJnDn - dx * pLEdge->dJnDnP1; |
|||
*(pNode->fNPsiiP1) += dy * pBEdge->dJnDpsiP1; |
|||
*(pNode->fNNiP1) += dy * pBEdge->dJnDnP1; |
|||
*(pNode->fNPsijM1) += dx * pLEdge->dJnDpsiP1; |
|||
*(pNode->fNNjM1) -= dx * pLEdge->dJnDn; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Calculate the Inversion-Layer Mobility Dependent Terms in Jac. */ |
|||
if ( MobDeriv && SurfaceMobility ) { |
|||
for ( pCh = pDevice->pChannel; pCh != NIL(TWOchannel); |
|||
pCh = pCh->next ) { |
|||
/* Find effective height of oxide element at interface. */ |
|||
if ( pCh->type%2 == 0 ) { /* Vertical slice */ |
|||
ds = pCh->pNElem->dy / pCh->pNElem->epsRel; |
|||
} else { /* Horizontal slice */ |
|||
ds = pCh->pNElem->dx / pCh->pNElem->epsRel; |
|||
} |
|||
pElem = pCh->pSeed; |
|||
nextIndex = (pCh->type + 2)%4; |
|||
while (pElem && pElem->channel == pCh->id) { |
|||
TWONmobDeriv( pElem, pCh->type, ds ); |
|||
pElem = pElem->pElems[ nextIndex ]; |
|||
} |
|||
} /* endfor pCh != NIL */ |
|||
} /* endif MobDeriv and SurfaceMobility */ |
|||
} |
|||
|
|||
/* load only the Rhs vector */ |
|||
void |
|||
TWONrhsLoad(TWOdevice *pDevice, BOOLEAN tranAnalysis, TWOtranInfo *info) |
|||
{ |
|||
TWOelem *pElem; |
|||
TWOnode *pNode; |
|||
TWOedge *pHEdge, *pVEdge; |
|||
TWOedge *pTEdge, *pBEdge, *pLEdge, *pREdge; |
|||
TWOchannel *pCh; |
|||
int index, eIndex; |
|||
double *pRhs = pDevice->rhs; |
|||
double dx, dy, dxdy, dyOverDx, dxOverDy; |
|||
double dPsiT, dPsiB, dPsiL, dPsiR; |
|||
double rhsN; |
|||
double nConc, pConc; |
|||
double perTime; |
|||
|
|||
/* first compute the currents */ |
|||
TWONcommonTerms( pDevice, TRUE, tranAnalysis, info ); |
|||
|
|||
/* find reciprocal timestep */ |
|||
if ( tranAnalysis ) { |
|||
perTime = info->intCoeff[0]; |
|||
} |
|||
|
|||
/* zero the rhs vector */ |
|||
for ( index = 1 ; index <= pDevice->numEqns ; index++ ) { |
|||
pRhs[ index ] = 0.0; |
|||
} |
|||
|
|||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) { |
|||
pElem = pDevice->elements[ eIndex ]; |
|||
|
|||
dx = 0.5 * pElem->dx; |
|||
dy = 0.5 * pElem->dy; |
|||
dxdy = dx * dy; |
|||
dxOverDy = 0.5 * pElem->epsRel * pElem->dxOverDy; |
|||
dyOverDx = 0.5 * pElem->epsRel * pElem->dyOverDx; |
|||
|
|||
pTEdge = pElem->pTopEdge; |
|||
pBEdge = pElem->pBotEdge; |
|||
pLEdge = pElem->pLeftEdge; |
|||
pREdge = pElem->pRightEdge; |
|||
dPsiT = pTEdge->dPsi; |
|||
dPsiB = pBEdge->dPsi; |
|||
dPsiL = pLEdge->dPsi; |
|||
dPsiR = pREdge->dPsi; |
|||
|
|||
/* load for all i,j */ |
|||
for ( index = 0; index <= 3; index++ ) { |
|||
pNode = pElem->pNodes[ index ]; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
if ( index <= 1 ) { |
|||
pHEdge = pTEdge; |
|||
} else { |
|||
pHEdge = pBEdge; |
|||
} |
|||
if ( index == 0 || index == 3 ) { |
|||
pVEdge = pLEdge; |
|||
} else { |
|||
pVEdge = pREdge; |
|||
} |
|||
/* Add surface state charges. */ |
|||
pRhs[ pNode->psiEqn ] += dx * pHEdge->qf; |
|||
pRhs[ pNode->psiEqn ] += dy * pVEdge->qf; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
nConc = *(pDevice->devState0 + pNode->nodeN); |
|||
pConc = *(pDevice->devState0 + pNode->nodeP); |
|||
pRhs[ pNode->psiEqn ] += dxdy * (pNode->netConc + pConc - nConc); |
|||
|
|||
/* Handle generation terms */ |
|||
rhsN = - dxdy * pNode->uNet; |
|||
pRhs[ pNode->nEqn ] -= rhsN; |
|||
|
|||
/* Handle dXdT continuity terms */ |
|||
if ( tranAnalysis ) { |
|||
pRhs[ pNode->nEqn ] += dxdy * pNode->dNdT; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Handle neighbor and edge dependent terms */ |
|||
pNode = pElem->pTLNode; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
pRhs[ pNode->psiEqn ] -= -dyOverDx * dPsiT - dxOverDy * dPsiL; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
pRhs[ pNode->nEqn ] -= dy * pTEdge->jn + dx * pLEdge->jn; |
|||
} |
|||
} |
|||
pNode = pElem->pTRNode; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
pRhs[ pNode->psiEqn ] -= dyOverDx * dPsiT - dxOverDy * dPsiR; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
pRhs[ pNode->nEqn ] -= -dy * pTEdge->jn + dx * pREdge->jn; |
|||
} |
|||
} |
|||
pNode = pElem->pBRNode; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
pRhs[ pNode->psiEqn ] -= dyOverDx * dPsiB + dxOverDy * dPsiR; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
pRhs[ pNode->nEqn ] -= -dy * pBEdge->jn - dx * pREdge->jn; |
|||
} |
|||
} |
|||
pNode = pElem->pBLNode; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
pRhs[ pNode->psiEqn ] -= -dyOverDx * dPsiB + dxOverDy * dPsiL; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
pRhs[ pNode->nEqn ] -= dy * pBEdge->jn - dx * pLEdge->jn; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* |
|||
* computation of current densities, recombination rates, |
|||
* mobilities and their derivatives |
|||
*/ |
|||
void |
|||
TWONcommonTerms(TWOdevice *pDevice, BOOLEAN currentOnly, |
|||
BOOLEAN tranAnalysis, TWOtranInfo *info) |
|||
{ |
|||
TWOelem *pElem, *pElem1; |
|||
TWOedge *pEdge; |
|||
TWOnode *pNode; |
|||
int index, eIndex; |
|||
int nextIndex; /* index of node to find next element */ |
|||
double psi1, psi2, refPsi, nC, nP1; |
|||
double dPsiN; |
|||
double bPsiN, dbPsiN, bMPsiN, dbMPsiN; |
|||
double muN, dMuN, rDx, rDy; |
|||
double psi, nConc = 0.0, pConc = 0.0; |
|||
double cnAug, cpAug; |
|||
double eSurf = 0.0; /* For channel mobilities */ |
|||
double qInt = 0.0; |
|||
TWOchannel *pCh; |
|||
|
|||
/* evaluate all node (including recombination) and edge quantities */ |
|||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) { |
|||
pElem = pDevice->elements[ eIndex ]; |
|||
refPsi = pElem->matlInfo->refPsi; |
|||
cnAug = pElem->matlInfo->cAug[ELEC]; |
|||
cpAug = pElem->matlInfo->cAug[HOLE]; |
|||
for ( index = 0; index <= 3; index++ ) { |
|||
if ( pElem->evalNodes[ index ] ) { |
|||
pNode = pElem->pNodes[ index ]; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
psi = pDevice->dcSolution[ pNode->psiEqn ]; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
nConc = pDevice->dcSolution[ pNode->nEqn ]; |
|||
pConc = pNode->nie * exp( - psi + refPsi ); |
|||
if ( Srh ) { |
|||
recomb(nConc, pConc, |
|||
pNode->tn, pNode->tp, cnAug, cpAug, pNode->nie, |
|||
&pNode->uNet, &pNode->dUdN, &pNode->dUdP); |
|||
} else { |
|||
pNode->uNet = 0.0; |
|||
pNode->dUdN = 0.0; |
|||
pNode->dUdP = 0.0; |
|||
} |
|||
} |
|||
} else { |
|||
/* a contact node */ |
|||
psi = pNode->psi; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
nConc = pNode->nConc; |
|||
pConc = pNode->pConc; |
|||
} |
|||
} |
|||
|
|||
/* store info in the state tables */ |
|||
*(pDevice->devState0 + pNode->nodePsi) = psi; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
*(pDevice->devState0 + pNode->nodeN) = nConc; |
|||
*(pDevice->devState0 + pNode->nodeP) = pConc; |
|||
if ( tranAnalysis && pNode->nodeType != CONTACT ) { |
|||
pNode->dNdT = integrate( pDevice->devStates, info, pNode->nodeN ); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
for ( index = 0; index <= 3; index++ ) { |
|||
if ( pElem->evalEdges[ index ] ) { |
|||
pEdge = pElem->pEdges[ index ]; |
|||
pNode = pElem->pNodes[ index ]; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
psi1 = pDevice->dcSolution[pNode->psiEqn]; |
|||
} else { |
|||
psi1 = pNode->psi; |
|||
} |
|||
pNode = pElem->pNodes[ (index + 1) % 4 ]; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
psi2 = pDevice->dcSolution[pNode->psiEqn]; |
|||
} else { |
|||
psi2 = pNode->psi; |
|||
} |
|||
if ( index <= 1 ) { |
|||
pEdge->dPsi = psi2 - psi1; |
|||
} else { |
|||
pEdge->dPsi = psi1 - psi2; |
|||
} |
|||
*(pDevice->devState0 + pEdge->edgeDpsi) = pEdge->dPsi; |
|||
|
|||
if ( pElem->elemType == SEMICON ) { |
|||
/* Calculate weighted driving forces - wdfn & wdfp for the edge */ |
|||
dPsiN = pEdge->dPsi + pEdge->dCBand; |
|||
bernoulli( dPsiN, &bPsiN, &dbPsiN, |
|||
&bMPsiN, &dbMPsiN, !currentOnly ); |
|||
if ( index <= 1 ) { |
|||
nC = *(pDevice->devState0 + pElem->pNodes[ index ]->nodeN); |
|||
nP1 = *(pDevice->devState0 + pElem->pNodes[ index+1 ]->nodeN); |
|||
} else { |
|||
nC = *(pDevice->devState0 + pElem->pNodes[(index+1)%4]->nodeN); |
|||
nP1 = *(pDevice->devState0 + pElem->pNodes[ index ]->nodeN); |
|||
} |
|||
pEdge->wdfn = bPsiN * nP1 - bMPsiN * nC; |
|||
pEdge->jn = 0.0; |
|||
if ( !currentOnly ) { |
|||
pEdge->dWnDpsiP1 = dbPsiN * nP1 - dbMPsiN * nC; |
|||
pEdge->dWnDn = - bMPsiN; |
|||
pEdge->dWnDnP1 = bPsiN; |
|||
pEdge->dJnDpsiP1 = 0.0; |
|||
pEdge->dJnDn = 0.0; |
|||
pEdge->dJnDnP1 = 0.0; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* DAG: calculate mobilities for channel elems */ |
|||
if ( SurfaceMobility ) { |
|||
for ( pCh = pDevice->pChannel; |
|||
pCh != NIL(TWOchannel); pCh = pCh->next ) { |
|||
pElem = pCh->pNElem; |
|||
switch (pCh->type) { |
|||
case 0: |
|||
eSurf = - 0.5 * (pElem->pLeftEdge->dPsi + pElem->pRightEdge->dPsi ) |
|||
* pElem->epsRel / pElem->dy; |
|||
qInt = 0.5 * pElem->pBotEdge->qf; |
|||
break; |
|||
case 1: |
|||
eSurf = - 0.5 * (pElem->pTopEdge->dPsi + pElem->pBotEdge->dPsi ) |
|||
* pElem->epsRel / pElem->dx; |
|||
qInt = 0.5 * pElem->pLeftEdge->qf; |
|||
break; |
|||
case 2: |
|||
eSurf = - 0.5 * (pElem->pLeftEdge->dPsi + pElem->pRightEdge->dPsi ) |
|||
* pElem->epsRel / pElem->dy; |
|||
qInt = 0.5 * pElem->pTopEdge->qf; |
|||
break; |
|||
case 3: |
|||
eSurf = - 0.5 * (pElem->pTopEdge->dPsi + pElem->pBotEdge->dPsi ) |
|||
* pElem->epsRel / pElem->dx; |
|||
qInt = 0.5 * pElem->pRightEdge->qf; |
|||
break; |
|||
} |
|||
eSurf += qInt; |
|||
pElem = pCh->pSeed; |
|||
nextIndex = (pCh->type + 2)%4; |
|||
while (pElem && pElem->channel == pCh->id) { |
|||
TWONmobility( pElem, eSurf ); |
|||
pElem = pElem->pElems[ nextIndex ]; |
|||
} |
|||
} /* endfor pCH != NIL */ |
|||
} /* endif SurfaceMobility */ |
|||
|
|||
/* calculate the current densities assuming mobility value depend on Ex,Ey*/ |
|||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) { |
|||
pElem = pDevice->elements[ eIndex ]; |
|||
|
|||
rDx = 1.0 / pElem->dx; |
|||
rDy = 1.0 / pElem->dy; |
|||
for ( index = 0; index <= 3; index++ ) { |
|||
pEdge = pElem->pEdges[ index ]; |
|||
/* calculate conductive currents */ |
|||
if ( pElem->elemType == SEMICON ) { |
|||
/* get mobility for this edge */ |
|||
if ( !pElem->channel ) { |
|||
/* Calculate mobility for non-channel elements */ |
|||
muN = pElem->mun0; |
|||
dMuN = 0.0; |
|||
dPsiN = pEdge->dPsi + pEdge->dCBand; |
|||
if ( index%2 == 0 ) { |
|||
MOBfieldDep( pElem->matlInfo, ELEC, - dPsiN * rDx, &muN, &dMuN ); |
|||
} else { |
|||
MOBfieldDep( pElem->matlInfo, ELEC, - dPsiN * rDy, &muN, &dMuN ); |
|||
} |
|||
} else { |
|||
/* Retrieve previously calculated value. */ |
|||
muN = pElem->mun; |
|||
dMuN = 0.0; |
|||
} |
|||
switch ( index ) { |
|||
case 0: |
|||
muN *= pEdge->kPos * rDx; |
|||
dMuN *= pEdge->kPos * rDx * rDx; |
|||
break; |
|||
case 1: |
|||
muN *= pEdge->kNeg * rDy; |
|||
dMuN *= pEdge->kNeg * rDy * rDy; |
|||
break; |
|||
case 2: |
|||
muN *= pEdge->kNeg * rDx; |
|||
dMuN *= pEdge->kNeg * rDx * rDx; |
|||
break; |
|||
case 3: |
|||
muN *= pEdge->kPos * rDy; |
|||
dMuN *= pEdge->kPos * rDy * rDy; |
|||
break; |
|||
} |
|||
/* Now that the mobility for this edge is known, do current */ |
|||
pEdge->jn += muN * pEdge->wdfn; |
|||
if ( !currentOnly ) { |
|||
pEdge->dJnDpsiP1 += muN * pEdge->dWnDpsiP1; |
|||
pEdge->dJnDn += muN * pEdge->dWnDn; |
|||
pEdge->dJnDnP1 += muN * pEdge->dWnDnP1; |
|||
if ( MobDeriv && (!pElem->channel) ) { |
|||
pEdge->dJnDpsiP1 -= dMuN * pEdge->wdfn; |
|||
} |
|||
} |
|||
} |
|||
/* calculate displacement current only once */ |
|||
if ( pElem->evalEdges[ index ] ) { |
|||
if ( tranAnalysis ) { |
|||
if ( index == 0 || index == 2 ) { |
|||
/* horizontal edges */ |
|||
pEdge->jd = -integrate(pDevice->devStates, info, |
|||
pEdge->edgeDpsi) * rDx; |
|||
} else { |
|||
/* vertical edges */ |
|||
pEdge->jd = -integrate(pDevice->devStates, info, |
|||
pEdge->edgeDpsi) * rDy; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,888 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group |
|||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numglobs.h" |
|||
#include "numenum.h" |
|||
#include "twomesh.h" |
|||
#include "twodev.h" |
|||
#include "bool.h" |
|||
#include "spMatrix.h" |
|||
#include "twoddefs.h" |
|||
#include "twodext.h" |
|||
#include "cidersupt.h" |
|||
#include "../../maths/misc/bernoull.h" |
|||
|
|||
/* |
|||
* Functions to setup and solve the continuity equations. |
|||
* Both continuity equations are solved. |
|||
* Separate functions are used for one continuity equation. |
|||
*/ |
|||
|
|||
|
|||
/* |
|||
* setup matrix pointers to Jacobian values and |
|||
* store direct pointers with the nodes |
|||
*/ |
|||
|
|||
void |
|||
TWOPjacBuild(TWOdevice *pDevice) |
|||
{ |
|||
char *matrix = pDevice->matrix; |
|||
TWOelem *pElem; |
|||
TWOnode *pNode; |
|||
TWOchannel *pCh; |
|||
int eIndex, nIndex; |
|||
int nextIndex; /* index of node to find next element */ |
|||
int psiEqn, pEqn; /* scratch for deref'd eqn numbers */ |
|||
int psiEqnTL = 0, pEqnTL = 0; |
|||
int psiEqnTR = 0, pEqnTR = 0; |
|||
int psiEqnBR = 0, pEqnBR = 0; |
|||
int psiEqnBL = 0, pEqnBL = 0; |
|||
int psiEqnInM = 0, psiEqnInP = 0; /* scratch for deref'd surface eqns */ |
|||
int psiEqnOxM = 0, psiEqnOxP = 0; /* M= more negative, P= more positive */ |
|||
|
|||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) { |
|||
pElem = pDevice->elements[ eIndex ]; |
|||
|
|||
/* first the self terms */ |
|||
for ( nIndex = 0; nIndex <= 3; nIndex++ ) { |
|||
pNode = pElem->pNodes[ nIndex ]; |
|||
/* get poisson-only pointer */ |
|||
psiEqn = pNode->psiEqn; |
|||
pNode->fPsiPsi = spGetElement( matrix, psiEqn, psiEqn ); |
|||
|
|||
if ( pElem->elemType == SEMICON ) { |
|||
/* get continuity-coupling terms */ |
|||
pEqn = pNode->pEqn; |
|||
pNode->nEqn = 0; |
|||
/* pointers for additional terms */ |
|||
pNode->fPsiP = spGetElement( matrix, psiEqn, pEqn ); |
|||
pNode->fPPsi = spGetElement( matrix, pEqn, psiEqn ); |
|||
pNode->fPP = spGetElement( matrix, pEqn, pEqn ); |
|||
} else { |
|||
pEqn = 0; |
|||
} |
|||
/* save equation indices */ |
|||
switch ( nIndex ) { |
|||
case 0: /* TL Node */ |
|||
psiEqnTL = psiEqn; |
|||
pEqnTL = pEqn; |
|||
break; |
|||
case 1: /* TR Node */ |
|||
psiEqnTR = psiEqn; |
|||
pEqnTR = pEqn; |
|||
break; |
|||
case 2: /* BR Node */ |
|||
psiEqnBR = psiEqn; |
|||
pEqnBR = pEqn; |
|||
break; |
|||
case 3: /* BL Node */ |
|||
psiEqnBL = psiEqn; |
|||
pEqnBL = pEqn; |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
} |
|||
|
|||
/* now terms to couple to adjacent nodes */ |
|||
pNode = pElem->pTLNode; |
|||
pNode->fPsiPsiiP1 = spGetElement(matrix, psiEqnTL, psiEqnTR ); |
|||
pNode->fPsiPsijP1 = spGetElement(matrix, psiEqnTL, psiEqnBL ); |
|||
if ( pElem->elemType == SEMICON ) { |
|||
/* continuity equation pointers */ |
|||
pNode->fPPsiiP1 = spGetElement( matrix, pEqnTL, psiEqnTR ); |
|||
pNode->fPPiP1 = spGetElement( matrix, pEqnTL, pEqnTR ); |
|||
pNode->fPPsijP1 = spGetElement( matrix, pEqnTL, psiEqnBL ); |
|||
pNode->fPPjP1 = spGetElement( matrix, pEqnTL, pEqnBL ); |
|||
/* Surface Mobility Model depends on diagonal node values */ |
|||
if ( MobDeriv && SurfaceMobility && pElem->channel ) { |
|||
pNode->fPPsiiP1jP1 = spGetElement( matrix, pEqnTL, psiEqnBR ); |
|||
pNode->fPPiP1jP1 = spGetElement( matrix, pEqnTL, pEqnBR ); |
|||
} |
|||
} |
|||
|
|||
pNode = pElem->pTRNode; |
|||
pNode->fPsiPsiiM1 = spGetElement(matrix, psiEqnTR, psiEqnTL ); |
|||
pNode->fPsiPsijP1 = spGetElement(matrix, psiEqnTR, psiEqnBR ); |
|||
if ( pElem->elemType == SEMICON ) { |
|||
/* continuity equation pointers */ |
|||
pNode->fPPsiiM1 = spGetElement( matrix, pEqnTR, psiEqnTL ); |
|||
pNode->fPPiM1 = spGetElement( matrix, pEqnTR, pEqnTL ); |
|||
pNode->fPPsijP1 = spGetElement( matrix, pEqnTR, psiEqnBR ); |
|||
pNode->fPPjP1 = spGetElement( matrix, pEqnTR, pEqnBR ); |
|||
/* Surface Mobility Model depends on diagonal node values */ |
|||
if ( MobDeriv && SurfaceMobility && pElem->channel ) { |
|||
pNode->fPPsiiM1jP1 = spGetElement( matrix, pEqnTR, psiEqnBL ); |
|||
pNode->fPPiM1jP1 = spGetElement( matrix, pEqnTR, pEqnBL ); |
|||
} |
|||
} |
|||
|
|||
pNode = pElem->pBRNode; |
|||
pNode->fPsiPsiiM1 = spGetElement(matrix, psiEqnBR, psiEqnBL ); |
|||
pNode->fPsiPsijM1 = spGetElement(matrix, psiEqnBR, psiEqnTR ); |
|||
if ( pElem->elemType == SEMICON ) { |
|||
/* continuity equation pointers */ |
|||
pNode->fPPsiiM1 = spGetElement( matrix, pEqnBR, psiEqnBL ); |
|||
pNode->fPPiM1 = spGetElement( matrix, pEqnBR, pEqnBL ); |
|||
pNode->fPPsijM1 = spGetElement( matrix, pEqnBR, psiEqnTR ); |
|||
pNode->fPPjM1 = spGetElement( matrix, pEqnBR, pEqnTR ); |
|||
/* Surface Mobility Model depends on diagonal node values */ |
|||
if ( MobDeriv && SurfaceMobility && pElem->channel ) { |
|||
pNode->fPPsiiM1jM1 = spGetElement( matrix, pEqnBR, psiEqnTL ); |
|||
pNode->fPPiM1jM1 = spGetElement( matrix, pEqnBR, pEqnTL ); |
|||
} |
|||
} |
|||
|
|||
pNode = pElem->pBLNode; |
|||
pNode->fPsiPsiiP1 = spGetElement(matrix, psiEqnBL, psiEqnBR ); |
|||
pNode->fPsiPsijM1 = spGetElement(matrix, psiEqnBL, psiEqnTL ); |
|||
if ( pElem->elemType == SEMICON ) { |
|||
/* continuity equation pointers */ |
|||
pNode->fPPsiiP1 = spGetElement( matrix, pEqnBL, psiEqnBR ); |
|||
pNode->fPPiP1 = spGetElement( matrix, pEqnBL, pEqnBR ); |
|||
pNode->fPPsijM1 = spGetElement( matrix, pEqnBL, psiEqnTL ); |
|||
pNode->fPPjM1 = spGetElement( matrix, pEqnBL, pEqnTL ); |
|||
/* Surface Mobility Model depends on diagonal node values */ |
|||
if ( MobDeriv && SurfaceMobility && pElem->channel ) { |
|||
pNode->fPPsiiP1jM1 = spGetElement( matrix, pEqnBL, psiEqnTR ); |
|||
pNode->fPPiP1jM1 = spGetElement( matrix, pEqnBL, pEqnTR ); |
|||
} |
|||
} |
|||
} |
|||
/* |
|||
* Add terms for surface-field of inversion-layer mobility model. |
|||
* Elements MUST be made from silicon for this to work. |
|||
* No empty elements are allowed. |
|||
* Don't need these pointers if SurfaceMobility isn't set. |
|||
*/ |
|||
if ( MobDeriv && SurfaceMobility ) { |
|||
for ( pCh = pDevice->pChannel; pCh != NIL(TWOchannel); |
|||
pCh = pCh->next ) { |
|||
pElem = pCh->pNElem; |
|||
switch (pCh->type) { |
|||
case 0: |
|||
psiEqnInM = pElem->pBLNode->psiEqn; |
|||
psiEqnInP = pElem->pBRNode->psiEqn; |
|||
psiEqnOxM = pElem->pTLNode->psiEqn; |
|||
psiEqnOxP = pElem->pTRNode->psiEqn; |
|||
break; |
|||
case 1: |
|||
psiEqnInM = pElem->pTLNode->psiEqn; |
|||
psiEqnInP = pElem->pBLNode->psiEqn; |
|||
psiEqnOxM = pElem->pTRNode->psiEqn; |
|||
psiEqnOxP = pElem->pBRNode->psiEqn; |
|||
break; |
|||
case 2: |
|||
psiEqnInM = pElem->pTLNode->psiEqn; |
|||
psiEqnInP = pElem->pTRNode->psiEqn; |
|||
psiEqnOxM = pElem->pBLNode->psiEqn; |
|||
psiEqnOxP = pElem->pBRNode->psiEqn; |
|||
break; |
|||
case 3: |
|||
psiEqnInM = pElem->pTRNode->psiEqn; |
|||
psiEqnInP = pElem->pBRNode->psiEqn; |
|||
psiEqnOxM = pElem->pTLNode->psiEqn; |
|||
psiEqnOxP = pElem->pBLNode->psiEqn; |
|||
break; |
|||
} |
|||
pElem = pCh->pSeed; |
|||
nextIndex = (pCh->type + 2)%4; |
|||
while (pElem && pElem->channel == pCh->id) { |
|||
for ( nIndex = 0; nIndex <= 3; nIndex++ ) { |
|||
pNode = pElem->pNodes[ nIndex ]; |
|||
psiEqn = pNode->psiEqn; |
|||
pEqn = pNode->pEqn; |
|||
if ( pCh->type % 2 == 0 ) { /* Vertical Slice */ |
|||
if ( nIndex == 0 || nIndex == 3 ) { /* Left Side */ |
|||
pNode->fPPsiIn = spGetElement( matrix, pEqn, psiEqnInM ); |
|||
pNode->fPPsiInP1 = spGetElement( matrix, pEqn, psiEqnInP ); |
|||
pNode->fPPsiOx = spGetElement( matrix, pEqn, psiEqnOxM ); |
|||
pNode->fPPsiOxP1 = spGetElement( matrix, pEqn, psiEqnOxP ); |
|||
} else { /* Right Side */ |
|||
pNode->fPPsiInM1 = spGetElement( matrix, pEqn, psiEqnInM ); |
|||
pNode->fPPsiIn = spGetElement( matrix, pEqn, psiEqnInP ); |
|||
pNode->fPPsiOxM1 = spGetElement( matrix, pEqn, psiEqnOxM ); |
|||
pNode->fPPsiOx = spGetElement( matrix, pEqn, psiEqnOxP ); |
|||
} |
|||
} else { /* Horizontal Slice */ |
|||
if ( nIndex <= 1 ) { /* Top Side */ |
|||
pNode->fPPsiIn = spGetElement( matrix, pEqn, psiEqnInM ); |
|||
pNode->fPPsiInP1 = spGetElement( matrix, pEqn, psiEqnInP ); |
|||
pNode->fPPsiOx = spGetElement( matrix, pEqn, psiEqnOxM ); |
|||
pNode->fPPsiOxP1 = spGetElement( matrix, pEqn, psiEqnOxP ); |
|||
} else { /* Bottom Side */ |
|||
pNode->fPPsiInM1 = spGetElement( matrix, pEqn, psiEqnInM ); |
|||
pNode->fPPsiIn = spGetElement( matrix, pEqn, psiEqnInP ); |
|||
pNode->fPPsiOxM1 = spGetElement( matrix, pEqn, psiEqnOxM ); |
|||
pNode->fPPsiOx = spGetElement( matrix, pEqn, psiEqnOxP ); |
|||
} |
|||
} |
|||
} /* endfor nIndex */ |
|||
pElem = pElem->pElems[ nextIndex ]; |
|||
} /* endwhile pElem */ |
|||
} /* endfor pCh */ |
|||
} /* endif SurfaceMobility */ |
|||
} |
|||
|
|||
|
|||
/* |
|||
* The Jacobian and Rhs are loaded by the following function. |
|||
* Inputs are the transient analysis flag and the transient |
|||
* information structure |
|||
*/ |
|||
|
|||
void |
|||
TWOPsysLoad(TWOdevice *pDevice, BOOLEAN tranAnalysis, TWOtranInfo *info) |
|||
{ |
|||
TWOelem *pElem; |
|||
TWOnode *pNode; |
|||
TWOedge *pHEdge, *pVEdge; |
|||
TWOedge *pTEdge, *pBEdge, *pLEdge, *pREdge; |
|||
TWOchannel *pCh; |
|||
int index, eIndex; |
|||
int nextIndex; /* index of node to find next element */ |
|||
double *pRhs = pDevice->rhs; |
|||
double dx, dy, dxdy, dyOverDx, dxOverDy; |
|||
double ds; |
|||
double dPsiT, dPsiB, dPsiL, dPsiR; |
|||
double rhsP; |
|||
double nConc, pConc; |
|||
double perTime = 0.0; |
|||
|
|||
/* first compute the currents and derivatives */ |
|||
TWOPcommonTerms( pDevice, FALSE, tranAnalysis, info ); |
|||
|
|||
/* find reciprocal timestep */ |
|||
if ( tranAnalysis ) { |
|||
perTime = info->intCoeff[0]; |
|||
} |
|||
|
|||
/* zero the rhs vector */ |
|||
for ( index = 1 ; index <= pDevice->numEqns ; index++ ) { |
|||
pRhs[ index ] = 0.0; |
|||
} |
|||
|
|||
/* zero the matrix */ |
|||
spClear( pDevice->matrix ); |
|||
|
|||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) { |
|||
pElem = pDevice->elements[ eIndex ]; |
|||
|
|||
dx = 0.5 * pElem->dx; |
|||
dy = 0.5 * pElem->dy; |
|||
dxdy = dx * dy; |
|||
dxOverDy = 0.5 * pElem->epsRel * pElem->dxOverDy; |
|||
dyOverDx = 0.5 * pElem->epsRel * pElem->dyOverDx; |
|||
|
|||
pTEdge = pElem->pTopEdge; |
|||
pBEdge = pElem->pBotEdge; |
|||
pLEdge = pElem->pLeftEdge; |
|||
pREdge = pElem->pRightEdge; |
|||
dPsiT = pTEdge->dPsi; |
|||
dPsiB = pBEdge->dPsi; |
|||
dPsiL = pLEdge->dPsi; |
|||
dPsiR = pREdge->dPsi; |
|||
|
|||
/* load for all i,j */ |
|||
for ( index = 0; index <= 3; index++ ) { |
|||
pNode = pElem->pNodes[ index ]; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
if ( index <= 1 ) { |
|||
pHEdge = pTEdge; |
|||
} else { |
|||
pHEdge = pBEdge; |
|||
} |
|||
if ( index == 0 || index == 3 ) { |
|||
pVEdge = pLEdge; |
|||
} else { |
|||
pVEdge = pREdge; |
|||
} |
|||
/* Add surface state charges. */ |
|||
pRhs[ pNode->psiEqn ] += dx * pHEdge->qf; |
|||
pRhs[ pNode->psiEqn ] += dy * pVEdge->qf; |
|||
*(pNode->fPsiPsi) += dyOverDx + dxOverDy; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
nConc = *(pDevice->devState0 + pNode->nodeN); |
|||
pConc = *(pDevice->devState0 + pNode->nodeP); |
|||
|
|||
*(pNode->fPsiPsi) += dxdy * nConc; |
|||
*(pNode->fPsiP) -= dxdy; |
|||
*(pNode->fPPsi) -= dy * pHEdge->dJpDpsiP1 + dx * pVEdge->dJpDpsiP1; |
|||
pRhs[ pNode->psiEqn ] += dxdy * (pNode->netConc + pConc - nConc); |
|||
|
|||
/* Handle generation terms */ |
|||
*(pNode->fPP) += dxdy * pNode->dUdP; |
|||
*(pNode->fPPsi) += dxdy * pNode->dUdN * nConc; |
|||
rhsP = dxdy * pNode->uNet; |
|||
pRhs[ pNode->pEqn ] -= rhsP; |
|||
|
|||
/* Handle dXdT continuity terms */ |
|||
if ( tranAnalysis ) { |
|||
*(pNode->fPP) += dxdy * perTime; |
|||
pRhs[ pNode->pEqn ] -= dxdy * pNode->dPdT; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Handle neighbor and edge dependent terms */ |
|||
pNode = pElem->pTLNode; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
pRhs[ pNode->psiEqn ] -= -dyOverDx * dPsiT - dxOverDy * dPsiL; |
|||
*(pNode->fPsiPsiiP1) -= dyOverDx; |
|||
*(pNode->fPsiPsijP1) -= dxOverDy; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
pRhs[ pNode->pEqn ] -= dy * pTEdge->jp + dx * pLEdge->jp; |
|||
*(pNode->fPP) += dy * pTEdge->dJpDp + dx * pLEdge->dJpDp; |
|||
*(pNode->fPPsiiP1) += dy * pTEdge->dJpDpsiP1; |
|||
*(pNode->fPPiP1) += dy * pTEdge->dJpDpP1; |
|||
*(pNode->fPPsijP1) += dx * pLEdge->dJpDpsiP1; |
|||
*(pNode->fPPjP1) += dx * pLEdge->dJpDpP1; |
|||
} |
|||
} |
|||
pNode = pElem->pTRNode; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
pRhs[ pNode->psiEqn ] -= dyOverDx * dPsiT - dxOverDy * dPsiR; |
|||
*(pNode->fPsiPsiiM1) -= dyOverDx; |
|||
*(pNode->fPsiPsijP1) -= dxOverDy; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
pRhs[ pNode->pEqn ] -= -dy * pTEdge->jp + dx * pREdge->jp; |
|||
*(pNode->fPP) += -dy * pTEdge->dJpDpP1 + dx * pREdge->dJpDp; |
|||
*(pNode->fPPsiiM1) += dy * pTEdge->dJpDpsiP1; |
|||
*(pNode->fPPiM1) -= dy * pTEdge->dJpDp; |
|||
*(pNode->fPPsijP1) += dx * pREdge->dJpDpsiP1; |
|||
*(pNode->fPPjP1) += dx * pREdge->dJpDpP1; |
|||
} |
|||
} |
|||
pNode = pElem->pBRNode; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
pRhs[ pNode->psiEqn ] -= dyOverDx * dPsiB + dxOverDy * dPsiR; |
|||
*(pNode->fPsiPsiiM1) -= dyOverDx; |
|||
*(pNode->fPsiPsijM1) -= dxOverDy; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
pRhs[ pNode->pEqn ] -= -dy * pBEdge->jp - dx * pREdge->jp; |
|||
*(pNode->fPP) += -dy * pBEdge->dJpDpP1 - dx * pREdge->dJpDpP1; |
|||
*(pNode->fPPsiiM1) += dy * pBEdge->dJpDpsiP1; |
|||
*(pNode->fPPiM1) -= dy * pBEdge->dJpDp; |
|||
*(pNode->fPPsijM1) += dx * pREdge->dJpDpsiP1; |
|||
*(pNode->fPPjM1) -= dx * pREdge->dJpDp; |
|||
} |
|||
} |
|||
pNode = pElem->pBLNode; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
pRhs[ pNode->psiEqn ] -= -dyOverDx * dPsiB + dxOverDy * dPsiL; |
|||
*(pNode->fPsiPsiiP1) -= dyOverDx; |
|||
*(pNode->fPsiPsijM1) -= dxOverDy; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
pRhs[ pNode->pEqn ] -= dy * pBEdge->jp - dx * pLEdge->jp; |
|||
*(pNode->fPP) += dy * pBEdge->dJpDp - dx * pLEdge->dJpDpP1; |
|||
*(pNode->fPPsiiP1) += dy * pBEdge->dJpDpsiP1; |
|||
*(pNode->fPPiP1) += dy * pBEdge->dJpDpP1; |
|||
*(pNode->fPPsijM1) += dx * pLEdge->dJpDpsiP1; |
|||
*(pNode->fPPjM1) -= dx * pLEdge->dJpDp; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Calculate the Inversion-Layer Mobility Dependent Terms in Jac. */ |
|||
if ( MobDeriv && SurfaceMobility ) { |
|||
for ( pCh = pDevice->pChannel; pCh != NIL(TWOchannel); |
|||
pCh = pCh->next ) { |
|||
/* Find effective height of oxide element at interface. */ |
|||
if ( pCh->type%2 == 0 ) { /* Vertical slice */ |
|||
ds = pCh->pNElem->dy / pCh->pNElem->epsRel; |
|||
} else { /* Horizontal slice */ |
|||
ds = pCh->pNElem->dx / pCh->pNElem->epsRel; |
|||
} |
|||
pElem = pCh->pSeed; |
|||
nextIndex = (pCh->type + 2)%4; |
|||
while (pElem && pElem->channel == pCh->id) { |
|||
TWOPmobDeriv( pElem, pCh->type, ds ); |
|||
pElem = pElem->pElems[ nextIndex ]; |
|||
} |
|||
} /* endfor pCh != NIL */ |
|||
} /* endif MobDeriv and SurfaceMobility */ |
|||
} |
|||
|
|||
|
|||
/* this function used only for direct method ac analysis |
|||
Used to load only the dc Jacobian matrix. Rhs is unaffected |
|||
*/ |
|||
|
|||
void |
|||
TWOPjacLoad(TWOdevice *pDevice) |
|||
{ |
|||
TWOelem *pElem; |
|||
TWOnode *pNode; |
|||
TWOedge *pHEdge, *pVEdge; |
|||
TWOedge *pTEdge, *pBEdge, *pLEdge, *pREdge; |
|||
TWOchannel *pCh; |
|||
int index, eIndex; |
|||
int nextIndex; /* index of node to find next element */ |
|||
double dx, dy, dxdy, dyOverDx, dxOverDy; |
|||
double ds; |
|||
double nConc; |
|||
|
|||
/* first compute the currents and derivatives */ |
|||
TWOPcommonTerms( pDevice, FALSE, FALSE, NIL(TWOtranInfo) ); |
|||
|
|||
/* zero the matrix */ |
|||
spClear( pDevice->matrix ); |
|||
|
|||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) { |
|||
pElem = pDevice->elements[ eIndex ]; |
|||
dx = 0.5 * pElem->dx; |
|||
dy = 0.5 * pElem->dy; |
|||
dxdy = dx * dy; |
|||
dxOverDy = 0.5 * pElem->epsRel * pElem->dxOverDy; |
|||
dyOverDx = 0.5 * pElem->epsRel * pElem->dyOverDx; |
|||
|
|||
pTEdge = pElem->pTopEdge; |
|||
pBEdge = pElem->pBotEdge; |
|||
pLEdge = pElem->pLeftEdge; |
|||
pREdge = pElem->pRightEdge; |
|||
|
|||
/* load for all i,j */ |
|||
for ( index = 0; index <= 3; index++ ) { |
|||
pNode = pElem->pNodes[ index ]; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
*(pNode->fPsiPsi) += dyOverDx + dxOverDy; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
if ( index <= 1 ) { |
|||
pHEdge = pTEdge; |
|||
} else { |
|||
pHEdge = pBEdge; |
|||
} |
|||
if ( index == 0 || index == 3 ) { |
|||
pVEdge = pLEdge; |
|||
} else { |
|||
pVEdge = pREdge; |
|||
} |
|||
nConc = *(pDevice->devState0 + pNode->nodeN); |
|||
*(pNode->fPsiPsi) += dxdy * nConc; |
|||
*(pNode->fPsiP) -= dxdy; |
|||
*(pNode->fPPsi) -= dy * pHEdge->dJpDpsiP1 + dx * pVEdge->dJpDpsiP1; |
|||
|
|||
/* Handle generation terms */ |
|||
*(pNode->fPP) += dxdy * pNode->dUdP; |
|||
*(pNode->fPPsi) += dxdy * pNode->dUdN * nConc; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Handle neighbor and edge dependent terms */ |
|||
pNode = pElem->pTLNode; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
*(pNode->fPsiPsiiP1) -= dyOverDx; |
|||
*(pNode->fPsiPsijP1) -= dxOverDy; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
*(pNode->fPP) += dy * pTEdge->dJpDp + dx * pLEdge->dJpDp; |
|||
*(pNode->fPPsiiP1) += dy * pTEdge->dJpDpsiP1; |
|||
*(pNode->fPPiP1) += dy * pTEdge->dJpDpP1; |
|||
*(pNode->fPPsijP1) += dx * pLEdge->dJpDpsiP1; |
|||
*(pNode->fPPjP1) += dx * pLEdge->dJpDpP1; |
|||
} |
|||
} |
|||
pNode = pElem->pTRNode; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
*(pNode->fPsiPsiiM1) -= dyOverDx; |
|||
*(pNode->fPsiPsijP1) -= dxOverDy; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
*(pNode->fPP) += -dy * pTEdge->dJpDpP1 + dx * pREdge->dJpDp; |
|||
*(pNode->fPPsiiM1) += dy * pTEdge->dJpDpsiP1; |
|||
*(pNode->fPPiM1) -= dy * pTEdge->dJpDp; |
|||
*(pNode->fPPsijP1) += dx * pREdge->dJpDpsiP1; |
|||
*(pNode->fPPjP1) += dx * pREdge->dJpDpP1; |
|||
} |
|||
} |
|||
pNode = pElem->pBRNode; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
*(pNode->fPsiPsiiM1) -= dyOverDx; |
|||
*(pNode->fPsiPsijM1) -= dxOverDy; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
*(pNode->fPP) += -dy * pBEdge->dJpDpP1 - dx * pREdge->dJpDpP1; |
|||
*(pNode->fPPsiiM1) += dy * pBEdge->dJpDpsiP1; |
|||
*(pNode->fPPiM1) -= dy * pBEdge->dJpDp; |
|||
*(pNode->fPPsijM1) += dx * pREdge->dJpDpsiP1; |
|||
*(pNode->fPPjM1) -= dx * pREdge->dJpDp; |
|||
} |
|||
} |
|||
pNode = pElem->pBLNode; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
*(pNode->fPsiPsiiP1) -= dyOverDx; |
|||
*(pNode->fPsiPsijM1) -= dxOverDy; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
*(pNode->fPP) += dy * pBEdge->dJpDp - dx * pLEdge->dJpDpP1; |
|||
*(pNode->fPPsiiP1) += dy * pBEdge->dJpDpsiP1; |
|||
*(pNode->fPPiP1) += dy * pBEdge->dJpDpP1; |
|||
*(pNode->fPPsijM1) += dx * pLEdge->dJpDpsiP1; |
|||
*(pNode->fPPjM1) -= dx * pLEdge->dJpDp; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Calculate the Inversion-Layer Mobility Dependent Terms in Jac. */ |
|||
if ( MobDeriv && SurfaceMobility ) { |
|||
for ( pCh = pDevice->pChannel; pCh != NIL(TWOchannel); |
|||
pCh = pCh->next ) { |
|||
/* Find effective height of oxide element at interface. */ |
|||
if ( pCh->type%2 == 0 ) { /* Vertical slice */ |
|||
ds = pCh->pNElem->dy / pCh->pNElem->epsRel; |
|||
} else { /* Horizontal slice */ |
|||
ds = pCh->pNElem->dx / pCh->pNElem->epsRel; |
|||
} |
|||
pElem = pCh->pSeed; |
|||
nextIndex = (pCh->type + 2)%4; |
|||
while (pElem && pElem->channel == pCh->id) { |
|||
TWOPmobDeriv( pElem, pCh->type, ds ); |
|||
pElem = pElem->pElems[ nextIndex ]; |
|||
} |
|||
} /* endfor pCh != NIL */ |
|||
} /* endif MobDeriv and SurfaceMobility */ |
|||
} |
|||
|
|||
/* load only the Rhs vector */ |
|||
void |
|||
TWOPrhsLoad(TWOdevice *pDevice, BOOLEAN tranAnalysis, TWOtranInfo *info) |
|||
{ |
|||
TWOelem *pElem; |
|||
TWOnode *pNode; |
|||
TWOedge *pHEdge, *pVEdge; |
|||
TWOedge *pTEdge, *pBEdge, *pLEdge, *pREdge; |
|||
TWOchannel *pCh; |
|||
int index, eIndex; |
|||
double *pRhs = pDevice->rhs; |
|||
double dx, dy, dxdy, dyOverDx, dxOverDy; |
|||
double dPsiT, dPsiB, dPsiL, dPsiR; |
|||
double rhsP; |
|||
double nConc, pConc; |
|||
double perTime; |
|||
|
|||
/* first compute the currents */ |
|||
TWOPcommonTerms( pDevice, TRUE, tranAnalysis, info ); |
|||
|
|||
/* find reciprocal timestep */ |
|||
if ( tranAnalysis ) { |
|||
perTime = info->intCoeff[0]; |
|||
} |
|||
|
|||
/* zero the rhs vector */ |
|||
for ( index = 1 ; index <= pDevice->numEqns ; index++ ) { |
|||
pRhs[ index ] = 0.0; |
|||
} |
|||
|
|||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) { |
|||
pElem = pDevice->elements[ eIndex ]; |
|||
|
|||
dx = 0.5 * pElem->dx; |
|||
dy = 0.5 * pElem->dy; |
|||
dxdy = dx * dy; |
|||
dxOverDy = 0.5 * pElem->epsRel * pElem->dxOverDy; |
|||
dyOverDx = 0.5 * pElem->epsRel * pElem->dyOverDx; |
|||
|
|||
pTEdge = pElem->pTopEdge; |
|||
pBEdge = pElem->pBotEdge; |
|||
pLEdge = pElem->pLeftEdge; |
|||
pREdge = pElem->pRightEdge; |
|||
dPsiT = pTEdge->dPsi; |
|||
dPsiB = pBEdge->dPsi; |
|||
dPsiL = pLEdge->dPsi; |
|||
dPsiR = pREdge->dPsi; |
|||
|
|||
/* load for all i,j */ |
|||
for ( index = 0; index <= 3; index++ ) { |
|||
pNode = pElem->pNodes[ index ]; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
if ( index <= 1 ) { |
|||
pHEdge = pTEdge; |
|||
} else { |
|||
pHEdge = pBEdge; |
|||
} |
|||
if ( index == 0 || index == 3 ) { |
|||
pVEdge = pLEdge; |
|||
} else { |
|||
pVEdge = pREdge; |
|||
} |
|||
/* Add surface state charges. */ |
|||
pRhs[ pNode->psiEqn ] += dx * pHEdge->qf; |
|||
pRhs[ pNode->psiEqn ] += dy * pVEdge->qf; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
nConc = *(pDevice->devState0 + pNode->nodeN); |
|||
pConc = *(pDevice->devState0 + pNode->nodeP); |
|||
pRhs[ pNode->psiEqn ] += dxdy * (pNode->netConc + pConc - nConc); |
|||
|
|||
/* Handle generation terms */ |
|||
rhsP = dxdy * pNode->uNet; |
|||
pRhs[ pNode->pEqn ] -= rhsP; |
|||
|
|||
/* Handle dXdT continuity terms */ |
|||
if ( tranAnalysis ) { |
|||
pRhs[ pNode->pEqn ] -= dxdy * pNode->dPdT; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Handle neighbor and edge dependent terms */ |
|||
pNode = pElem->pTLNode; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
pRhs[ pNode->psiEqn ] -= -dyOverDx * dPsiT - dxOverDy * dPsiL; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
pRhs[ pNode->pEqn ] -= dy * pTEdge->jp + dx * pLEdge->jp; |
|||
} |
|||
} |
|||
pNode = pElem->pTRNode; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
pRhs[ pNode->psiEqn ] -= dyOverDx * dPsiT - dxOverDy * dPsiR; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
pRhs[ pNode->pEqn ] -= -dy * pTEdge->jp + dx * pREdge->jp; |
|||
} |
|||
} |
|||
pNode = pElem->pBRNode; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
pRhs[ pNode->psiEqn ] -= dyOverDx * dPsiB + dxOverDy * dPsiR; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
pRhs[ pNode->pEqn ] -= -dy * pBEdge->jp - dx * pREdge->jp; |
|||
} |
|||
} |
|||
pNode = pElem->pBLNode; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
pRhs[ pNode->psiEqn ] -= -dyOverDx * dPsiB + dxOverDy * dPsiL; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
pRhs[ pNode->pEqn ] -= dy * pBEdge->jp - dx * pLEdge->jp; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* |
|||
* computation of current densities, recombination rates, |
|||
* mobilities and their derivatives |
|||
*/ |
|||
void |
|||
TWOPcommonTerms(TWOdevice *pDevice, BOOLEAN currentOnly, |
|||
BOOLEAN tranAnalysis, TWOtranInfo *info) |
|||
{ |
|||
TWOelem *pElem, *pElem1; |
|||
TWOedge *pEdge; |
|||
TWOnode *pNode; |
|||
int index, eIndex; |
|||
int nextIndex; /* index of node to find next element */ |
|||
double psi1, psi2, refPsi, pC, pP1; |
|||
double dPsiP; |
|||
double bPsiP, dbPsiP, bMPsiP, dbMPsiP; |
|||
double muP, dMuP, rDx, rDy; |
|||
double psi, nConc = 0.0, pConc = 0.0; |
|||
double cnAug, cpAug; |
|||
double eSurf = 0.0; /* For channel mobilities */ |
|||
double qInt = 0.0; |
|||
TWOchannel *pCh; |
|||
|
|||
/* evaluate all node (including recombination) and edge quantities */ |
|||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) { |
|||
pElem = pDevice->elements[ eIndex ]; |
|||
refPsi = pElem->matlInfo->refPsi; |
|||
cnAug = pElem->matlInfo->cAug[ELEC]; |
|||
cpAug = pElem->matlInfo->cAug[HOLE]; |
|||
for ( index = 0; index <= 3; index++ ) { |
|||
if ( pElem->evalNodes[ index ] ) { |
|||
pNode = pElem->pNodes[ index ]; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
psi = pDevice->dcSolution[ pNode->psiEqn ]; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
nConc = pNode->nie * exp( psi - refPsi ); |
|||
pConc = pDevice->dcSolution[ pNode->pEqn ]; |
|||
if ( Srh ) { |
|||
recomb(nConc, pConc, |
|||
pNode->tn, pNode->tp, cnAug, cpAug, pNode->nie, |
|||
&pNode->uNet, &pNode->dUdN, &pNode->dUdP); |
|||
} else { |
|||
pNode->uNet = 0.0; |
|||
pNode->dUdN = 0.0; |
|||
pNode->dUdP = 0.0; |
|||
} |
|||
} |
|||
} else { |
|||
/* a contact node */ |
|||
psi = pNode->psi; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
nConc = pNode->nConc; |
|||
pConc = pNode->pConc; |
|||
} |
|||
} |
|||
|
|||
/* store info in the state tables */ |
|||
*(pDevice->devState0 + pNode->nodePsi) = psi; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
*(pDevice->devState0 + pNode->nodeN) = nConc; |
|||
*(pDevice->devState0 + pNode->nodeP) = pConc; |
|||
if ( tranAnalysis && pNode->nodeType != CONTACT ) { |
|||
pNode->dNdT = integrate( pDevice->devStates, info, pNode->nodeN ); |
|||
pNode->dPdT = integrate( pDevice->devStates, info, pNode->nodeP ); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
for ( index = 0; index <= 3; index++ ) { |
|||
if ( pElem->evalEdges[ index ] ) { |
|||
pEdge = pElem->pEdges[ index ]; |
|||
pNode = pElem->pNodes[ index ]; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
psi1 = pDevice->dcSolution[pNode->psiEqn]; |
|||
} else { |
|||
psi1 = pNode->psi; |
|||
} |
|||
pNode = pElem->pNodes[ (index + 1) % 4 ]; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
psi2 = pDevice->dcSolution[pNode->psiEqn]; |
|||
} else { |
|||
psi2 = pNode->psi; |
|||
} |
|||
if ( index <= 1 ) { |
|||
pEdge->dPsi = psi2 - psi1; |
|||
} else { |
|||
pEdge->dPsi = psi1 - psi2; |
|||
} |
|||
*(pDevice->devState0 + pEdge->edgeDpsi) = pEdge->dPsi; |
|||
|
|||
if ( pElem->elemType == SEMICON ) { |
|||
/* Calculate weighted driving forces - wdfn & wdfp for the edge */ |
|||
dPsiP = pEdge->dPsi - pEdge->dVBand; |
|||
bernoulli( dPsiP, &bPsiP, &dbPsiP, |
|||
&bMPsiP, &dbMPsiP, !currentOnly); |
|||
if ( index <= 1 ) { |
|||
pC = *(pDevice->devState0 + pElem->pNodes[ index ]->nodeP); |
|||
pP1 = *(pDevice->devState0 + pElem->pNodes[ index+1 ]->nodeP); |
|||
} else { |
|||
pC = *(pDevice->devState0 + pElem->pNodes[(index+1)%4]->nodeP); |
|||
pP1 = *(pDevice->devState0 + pElem->pNodes[ index ]->nodeP); |
|||
} |
|||
pEdge->wdfp = bPsiP * pC - bMPsiP * pP1; |
|||
pEdge->jp = 0.0; |
|||
if ( !currentOnly ) { |
|||
pEdge->dWpDpsiP1 = dbPsiP * pC - dbMPsiP * pP1; |
|||
pEdge->dWpDp = bPsiP; |
|||
pEdge->dWpDpP1 = - bMPsiP; |
|||
pEdge->dJpDpsiP1 = 0.0; |
|||
pEdge->dJpDp = 0.0; |
|||
pEdge->dJpDpP1 = 0.0; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* DAG: calculate mobilities for channel elems */ |
|||
if ( SurfaceMobility ) { |
|||
for ( pCh = pDevice->pChannel; |
|||
pCh != NIL(TWOchannel); pCh = pCh->next ) { |
|||
pElem = pCh->pNElem; |
|||
switch (pCh->type) { |
|||
case 0: |
|||
eSurf = - 0.5 * (pElem->pLeftEdge->dPsi + pElem->pRightEdge->dPsi ) |
|||
* pElem->epsRel / pElem->dy; |
|||
qInt = 0.5 * pElem->pBotEdge->qf; |
|||
break; |
|||
case 1: |
|||
eSurf = - 0.5 * (pElem->pTopEdge->dPsi + pElem->pBotEdge->dPsi ) |
|||
* pElem->epsRel / pElem->dx; |
|||
qInt = 0.5 * pElem->pLeftEdge->qf; |
|||
break; |
|||
case 2: |
|||
eSurf = - 0.5 * (pElem->pLeftEdge->dPsi + pElem->pRightEdge->dPsi ) |
|||
* pElem->epsRel / pElem->dy; |
|||
qInt = 0.5 * pElem->pTopEdge->qf; |
|||
break; |
|||
case 3: |
|||
eSurf = - 0.5 * (pElem->pTopEdge->dPsi + pElem->pBotEdge->dPsi ) |
|||
* pElem->epsRel / pElem->dx; |
|||
qInt = 0.5 * pElem->pRightEdge->qf; |
|||
break; |
|||
} |
|||
eSurf += qInt; |
|||
pElem = pCh->pSeed; |
|||
nextIndex = (pCh->type + 2)%4; |
|||
while (pElem && pElem->channel == pCh->id) { |
|||
TWOPmobility( pElem, eSurf ); |
|||
pElem = pElem->pElems[ nextIndex ]; |
|||
} |
|||
} /* endfor pCH != NIL */ |
|||
} /* endif SurfaceMobility */ |
|||
|
|||
/* calculate the current densities assuming mobility value depend on Ex,Ey*/ |
|||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) { |
|||
pElem = pDevice->elements[ eIndex ]; |
|||
|
|||
rDx = 1.0 / pElem->dx; |
|||
rDy = 1.0 / pElem->dy; |
|||
for ( index = 0; index <= 3; index++ ) { |
|||
pEdge = pElem->pEdges[ index ]; |
|||
/* calculate conductive currents */ |
|||
if ( pElem->elemType == SEMICON ) { |
|||
/* get mobility for this edge */ |
|||
if ( !pElem->channel ) { |
|||
/* Calculate mobility for non-channel elements */ |
|||
muP = pElem->mup0; |
|||
dMuP = 0.0; |
|||
dPsiP = pEdge->dPsi - pEdge->dVBand; |
|||
if ( index%2 == 0 ) { |
|||
MOBfieldDep( pElem->matlInfo, HOLE, - dPsiP * rDx, &muP, &dMuP ); |
|||
} else { |
|||
MOBfieldDep( pElem->matlInfo, HOLE, - dPsiP * rDy, &muP, &dMuP ); |
|||
} |
|||
} else { |
|||
/* Retrieve previously calculated value. */ |
|||
muP = pElem->mup; |
|||
dMuP = 0.0; |
|||
} |
|||
switch ( index ) { |
|||
case 0: |
|||
muP *= pEdge->kPos * rDx; |
|||
dMuP *= pEdge->kPos * rDx * rDx; |
|||
break; |
|||
case 1: |
|||
muP *= pEdge->kNeg * rDy; |
|||
dMuP *= pEdge->kNeg * rDy * rDy; |
|||
break; |
|||
case 2: |
|||
muP *= pEdge->kNeg * rDx; |
|||
dMuP *= pEdge->kNeg * rDx * rDx; |
|||
break; |
|||
case 3: |
|||
muP *= pEdge->kPos * rDy; |
|||
dMuP *= pEdge->kPos * rDy * rDy; |
|||
break; |
|||
} |
|||
/* Now that the mobility for this edge is known, do current */ |
|||
pEdge->jp += muP * pEdge->wdfp; |
|||
if ( !currentOnly ) { |
|||
pEdge->dJpDpsiP1 += muP * pEdge->dWpDpsiP1; |
|||
pEdge->dJpDp += muP * pEdge->dWpDp; |
|||
pEdge->dJpDpP1 += muP * pEdge->dWpDpP1; |
|||
if ( MobDeriv && (!pElem->channel) ) { |
|||
pEdge->dJpDpsiP1 -= dMuP * pEdge->wdfp; |
|||
} |
|||
} |
|||
} |
|||
/* calculate displacement current only once */ |
|||
if ( pElem->evalEdges[ index ] ) { |
|||
if ( tranAnalysis ) { |
|||
if ( index == 0 || index == 2 ) { |
|||
/* horizontal edges */ |
|||
pEdge->jd = -integrate(pDevice->devStates, info, |
|||
pEdge->edgeDpsi) * rDx; |
|||
} else { |
|||
/* vertical edges */ |
|||
pEdge->jd = -integrate(pDevice->devStates, info, |
|||
pEdge->edgeDpsi) * rDy; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,283 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group |
|||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numglobs.h" |
|||
#include "numenum.h" |
|||
#include "twomesh.h" |
|||
#include "twodev.h" |
|||
#include "spMatrix.h" |
|||
#include "twoddefs.h" |
|||
#include "twodext.h" |
|||
|
|||
|
|||
/* functions to setup and solve the 2D poisson equation */ |
|||
|
|||
void |
|||
TWOQjacBuild(TWOdevice *pDevice) |
|||
{ |
|||
char *matrix = pDevice->matrix; |
|||
TWOelem *pElem; |
|||
TWOnode *pNode, *pNode1; |
|||
int eIndex, nIndex; |
|||
|
|||
/* set up matrix pointers */ |
|||
/* establish main diagonal first */ |
|||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) { |
|||
pElem = pDevice->elements[ eIndex ]; |
|||
for ( nIndex = 0; nIndex <= 3; nIndex++ ) { |
|||
if ( pElem->evalNodes[ nIndex ] ) { |
|||
pNode = pElem->pNodes[ nIndex ]; |
|||
pNode->fPsiPsi = spGetElement( matrix, pNode->poiEqn, pNode->poiEqn ); |
|||
} |
|||
} |
|||
} |
|||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) { |
|||
pElem = pDevice->elements[ eIndex ]; |
|||
|
|||
pNode = pElem->pTLNode; |
|||
pNode1 = pElem->pTRNode; |
|||
pNode->fPsiPsiiP1 = spGetElement(matrix, pNode->poiEqn, pNode1->poiEqn ); |
|||
pNode1 = pElem->pBLNode; |
|||
pNode->fPsiPsijP1 = spGetElement(matrix, pNode->poiEqn, pNode1->poiEqn ); |
|||
pNode = pElem->pTRNode; |
|||
pNode1 = pElem->pTLNode; |
|||
pNode->fPsiPsiiM1 = spGetElement(matrix, pNode->poiEqn, pNode1->poiEqn ); |
|||
pNode1 = pElem->pBRNode; |
|||
pNode->fPsiPsijP1 = spGetElement(matrix, pNode->poiEqn, pNode1->poiEqn ); |
|||
pNode = pElem->pBRNode; |
|||
pNode1 = pElem->pBLNode; |
|||
pNode->fPsiPsiiM1 = spGetElement(matrix, pNode->poiEqn, pNode1->poiEqn ); |
|||
pNode1 = pElem->pTRNode; |
|||
pNode->fPsiPsijM1 = spGetElement(matrix, pNode->poiEqn, pNode1->poiEqn ); |
|||
pNode = pElem->pBLNode; |
|||
pNode1 = pElem->pBRNode; |
|||
pNode->fPsiPsiiP1 = spGetElement(matrix, pNode->poiEqn, pNode1->poiEqn ); |
|||
pNode1 = pElem->pTLNode; |
|||
pNode->fPsiPsijM1 = spGetElement(matrix, pNode->poiEqn, pNode1->poiEqn ); |
|||
} |
|||
/* |
|||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) { |
|||
pElem = pDevice->elements[ eIndex ]; |
|||
|
|||
pNode = pElem->pTLNode; |
|||
pNode->fPsiPsi = spGetElement( matrix, pNode->poiEqn, pNode->poiEqn ); |
|||
pNode1 = pElem->pTRNode; |
|||
pNode->fPsiPsiiP1 = spGetElement(matrix, pNode->poiEqn, pNode1->poiEqn ); |
|||
pNode1 = pElem->pBLNode; |
|||
pNode->fPsiPsijP1 = spGetElement(matrix, pNode->poiEqn, pNode1->poiEqn ); |
|||
pNode = pElem->pTRNode; |
|||
pNode->fPsiPsi = spGetElement( matrix, pNode->poiEqn, pNode->poiEqn ); |
|||
pNode1 = pElem->pTLNode; |
|||
pNode->fPsiPsiiM1 = spGetElement(matrix, pNode->poiEqn, pNode1->poiEqn ); |
|||
pNode1 = pElem->pBRNode; |
|||
pNode->fPsiPsijP1 = spGetElement(matrix, pNode->poiEqn, pNode1->poiEqn ); |
|||
pNode = pElem->pBRNode; |
|||
pNode->fPsiPsi = spGetElement( matrix, pNode->poiEqn, pNode->poiEqn ); |
|||
pNode1 = pElem->pBLNode; |
|||
pNode->fPsiPsiiM1 = spGetElement(matrix, pNode->poiEqn, pNode1->poiEqn ); |
|||
pNode1 = pElem->pTRNode; |
|||
pNode->fPsiPsijM1 = spGetElement(matrix, pNode->poiEqn, pNode1->poiEqn ); |
|||
pNode = pElem->pBLNode; |
|||
pNode->fPsiPsi = spGetElement( matrix, pNode->poiEqn, pNode->poiEqn ); |
|||
pNode1 = pElem->pBRNode; |
|||
pNode->fPsiPsiiP1 = spGetElement(matrix, pNode->poiEqn, pNode1->poiEqn ); |
|||
pNode1 = pElem->pTLNode; |
|||
pNode->fPsiPsijM1 = spGetElement(matrix, pNode->poiEqn, pNode1->poiEqn ); |
|||
} |
|||
*/ |
|||
} |
|||
|
|||
void |
|||
TWOQsysLoad(TWOdevice *pDevice) |
|||
{ |
|||
TWOelem *pElem; |
|||
TWOnode *pNode, *pNode1; |
|||
TWOedge *pHEdge, *pVEdge; |
|||
int index, eIndex; |
|||
double *pRhs = pDevice->rhs; |
|||
double dyOverDx, dxOverDy, dPsiT, dPsiB, dPsiL, dPsiR; |
|||
|
|||
TWOQcommonTerms( pDevice ); |
|||
|
|||
/* zero the rhs vector */ |
|||
for ( index = 1 ; index <= pDevice->numEqns ; index++ ) { |
|||
pRhs[ index ] = 0.0; |
|||
} |
|||
|
|||
/* zero the matrix */ |
|||
spClear( pDevice->matrix ); |
|||
|
|||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) { |
|||
pElem = pDevice->elements[ eIndex ]; |
|||
|
|||
dxOverDy = 0.5 * pElem->epsRel * pElem->dxOverDy; |
|||
dyOverDx = 0.5 * pElem->epsRel * pElem->dyOverDx; |
|||
dPsiT = pElem->pTopEdge->dPsi; |
|||
dPsiB = pElem->pBotEdge->dPsi; |
|||
dPsiL = pElem->pLeftEdge->dPsi; |
|||
dPsiR = pElem->pRightEdge->dPsi; |
|||
|
|||
/* load for all i,j */ |
|||
for ( index = 0; index <= 3; index++ ) { |
|||
pNode = pElem->pNodes[ index ]; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
*(pNode->fPsiPsi) += dyOverDx + dxOverDy; |
|||
if ( index <= 1 ) { |
|||
pHEdge = pElem->pTopEdge; |
|||
} else { |
|||
pHEdge = pElem->pBotEdge; |
|||
} |
|||
if ( index == 0 || index == 3 ) { |
|||
pVEdge = pElem->pLeftEdge; |
|||
} else { |
|||
pVEdge = pElem->pRightEdge; |
|||
} |
|||
/* add surface state charges */ |
|||
pRhs[ pNode->poiEqn ] += 0.5 * pElem->dx * pHEdge->qf; |
|||
pRhs[ pNode->poiEqn ] += 0.5 * pElem->dy * pVEdge->qf; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
*(pNode->fPsiPsi) += 0.25 * pElem->dx * pElem->dy * |
|||
(pNode->nConc + pNode->pConc); |
|||
pRhs[ pNode->poiEqn ] += 0.25 * pElem->dx * pElem->dy * |
|||
(pNode->netConc + pNode->pConc - pNode->nConc); |
|||
} |
|||
} |
|||
} |
|||
|
|||
pNode = pElem->pTLNode; |
|||
pRhs[ pNode->poiEqn ] -= -dyOverDx * dPsiT - dxOverDy * dPsiL; |
|||
*(pNode->fPsiPsiiP1) -= dyOverDx; |
|||
*(pNode->fPsiPsijP1) -= dxOverDy; |
|||
|
|||
pNode = pElem->pTRNode; |
|||
pRhs[ pNode->poiEqn ] -= dyOverDx * dPsiT - dxOverDy * dPsiR; |
|||
*(pNode->fPsiPsiiM1) -= dyOverDx; |
|||
*(pNode->fPsiPsijP1) -= dxOverDy; |
|||
|
|||
pNode = pElem->pBRNode; |
|||
pRhs[ pNode->poiEqn ] -= dyOverDx * dPsiB + dxOverDy * dPsiR; |
|||
*(pNode->fPsiPsiiM1) -= dyOverDx; |
|||
*(pNode->fPsiPsijM1) -= dxOverDy; |
|||
|
|||
pNode = pElem->pBLNode; |
|||
pRhs[ pNode->poiEqn ] -= -dyOverDx * dPsiB + dxOverDy * dPsiL; |
|||
*(pNode->fPsiPsiiP1) -= dyOverDx; |
|||
*(pNode->fPsiPsijM1) -= dxOverDy; |
|||
} |
|||
} |
|||
|
|||
|
|||
void |
|||
TWOQrhsLoad(TWOdevice *pDevice) |
|||
{ |
|||
TWOelem *pElem; |
|||
TWOnode *pNode, *pNode1; |
|||
TWOedge *pHEdge, *pVEdge; |
|||
int index, eIndex; |
|||
double *pRhs = pDevice->rhs; |
|||
double dyOverDx, dxOverDy, dPsiT, dPsiB, dPsiL, dPsiR; |
|||
|
|||
TWOQcommonTerms( pDevice ); |
|||
|
|||
/* zero the rhs vector */ |
|||
for ( index = 1 ; index <= pDevice->numEqns ; index++ ) { |
|||
pRhs[ index ] = 0.0; |
|||
} |
|||
|
|||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) { |
|||
pElem = pDevice->elements[ eIndex ]; |
|||
|
|||
dxOverDy = 0.5 * pElem->epsRel * pElem->dxOverDy; |
|||
dyOverDx = 0.5 * pElem->epsRel * pElem->dyOverDx; |
|||
dPsiT = pElem->pTopEdge->dPsi; |
|||
dPsiB = pElem->pBotEdge->dPsi; |
|||
dPsiL = pElem->pLeftEdge->dPsi; |
|||
dPsiR = pElem->pRightEdge->dPsi; |
|||
|
|||
/* load nodal terms */ |
|||
for ( index = 0; index <= 3; index++ ) { |
|||
pNode = pElem->pNodes[ index ]; |
|||
if ( (pNode->nodeType != CONTACT) && (pElem->elemType == SEMICON) ) { |
|||
pRhs[ pNode->poiEqn ] += 0.25 * pElem->dx * pElem->dy * |
|||
(pNode->netConc + pNode->pConc - pNode->nConc); |
|||
} |
|||
if ( index <= 1 ) { |
|||
pHEdge = pElem->pTopEdge; |
|||
} else { |
|||
pHEdge = pElem->pBotEdge; |
|||
} |
|||
if ( index == 0 || index == 3 ) { |
|||
pVEdge = pElem->pLeftEdge; |
|||
} else { |
|||
pVEdge = pElem->pRightEdge; |
|||
} |
|||
/* add surface state charges */ |
|||
pRhs[ pNode->poiEqn ] += 0.5 * pElem->dx * pHEdge->qf; |
|||
pRhs[ pNode->poiEqn ] += 0.5 * pElem->dy * pVEdge->qf; |
|||
} |
|||
|
|||
/* load edge terms */ |
|||
pNode = pElem->pTLNode; |
|||
pRhs[ pNode->poiEqn ] -= -dyOverDx * dPsiT - dxOverDy * dPsiL; |
|||
|
|||
pNode = pElem->pTRNode; |
|||
pRhs[ pNode->poiEqn ] -= dyOverDx * dPsiT - dxOverDy * dPsiR; |
|||
|
|||
pNode = pElem->pBRNode; |
|||
pRhs[ pNode->poiEqn ] -= dyOverDx * dPsiB + dxOverDy * dPsiR; |
|||
|
|||
pNode = pElem->pBLNode; |
|||
pRhs[ pNode->poiEqn ] -= -dyOverDx * dPsiB + dxOverDy * dPsiL; |
|||
} |
|||
} |
|||
|
|||
void |
|||
TWOQcommonTerms(TWOdevice *pDevice) |
|||
{ |
|||
TWOelem *pElem; |
|||
TWOedge *pEdge; |
|||
TWOnode *pNode, *pNode1; |
|||
int index, eIndex; |
|||
double psi1, psi2, refPsi; |
|||
|
|||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) { |
|||
pElem = pDevice->elements[ eIndex ]; |
|||
refPsi = pElem->matlInfo->refPsi; |
|||
for ( index = 0; index <= 3; index++ ) { |
|||
if ( pElem->evalNodes[ index ] ) { |
|||
pNode = pElem->pNodes[ index ]; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
pNode->psi = pDevice->dcSolution[ pNode->poiEqn ]; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
pNode->nConc = pNode->nie * exp( pNode->psi - refPsi ); |
|||
pNode->pConc = pNode->nie * exp( - pNode->psi + refPsi ); |
|||
} |
|||
} |
|||
} |
|||
if ( pElem->evalEdges[ index ] ) { |
|||
pEdge = pElem->pEdges[ index ]; |
|||
pNode = pElem->pNodes[ index ]; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
psi1 = pDevice->dcSolution[pNode->poiEqn]; |
|||
} else { |
|||
psi1 = pNode->psi; |
|||
} |
|||
pNode = pElem->pNodes[ (index + 1) % 4 ]; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
psi2 = pDevice->dcSolution[pNode->poiEqn]; |
|||
} else { |
|||
psi2 = pNode->psi; |
|||
} |
|||
if ( index <= 1 ) { |
|||
pEdge->dPsi = psi2 - psi1; |
|||
} else { |
|||
pEdge->dPsi = psi1 - psi2; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,561 @@ |
|||
/********** |
|||
Copyright 1992 Regents of the University of California. All rights reserved. |
|||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group |
|||
Author: 1992 David A. Gates, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numglobs.h" |
|||
#include "numconst.h" |
|||
#include "numenum.h" |
|||
#include "twomesh.h" |
|||
#include "twodev.h" |
|||
#include "carddefs.h" |
|||
#include "spMatrix.h" |
|||
#include "bool.h" |
|||
#include "twoddefs.h" |
|||
#include "twodext.h" |
|||
|
|||
void |
|||
TWOprnSolution(FILE *file, TWOdevice *pDevice, OUTPcard *output) |
|||
{ |
|||
int i, index, xIndex, yIndex; |
|||
int numVars = 0; |
|||
TWOnode ***nodeArray = NULL; |
|||
TWOnode *pNode; |
|||
TWOelem *pElem, *pNextElem; |
|||
TWOmaterial *info; |
|||
double data[50]; |
|||
double ex, ey, refPsi = 0.0, eGap, dGap; |
|||
double mun, mup; |
|||
double jcx, jdx, jnx, jpx, jtx; |
|||
double jcy, jdy, jny, jpy, jty; |
|||
double *xScale = pDevice->xScale; |
|||
double *yScale = pDevice->yScale; |
|||
BOOLEAN foundElem; |
|||
|
|||
if (output->OUTPnumVars == -1) { |
|||
/* First pass. Need to count number of variables in output. */ |
|||
numVars += 2; /* For the X & Y scales */ |
|||
if (output->OUTPdoping) { |
|||
numVars++; |
|||
} |
|||
if (output->OUTPpsi) { |
|||
numVars++; |
|||
} |
|||
if (output->OUTPequPsi) { |
|||
numVars++; |
|||
} |
|||
if (output->OUTPvacPsi) { |
|||
numVars++; |
|||
} |
|||
if (output->OUTPnConc) { |
|||
numVars++; |
|||
} |
|||
if (output->OUTPpConc) { |
|||
numVars++; |
|||
} |
|||
if (output->OUTPphin) { |
|||
numVars++; |
|||
} |
|||
if (output->OUTPphip) { |
|||
numVars++; |
|||
} |
|||
if (output->OUTPphic) { |
|||
numVars++; |
|||
} |
|||
if (output->OUTPphiv) { |
|||
numVars++; |
|||
} |
|||
if (output->OUTPeField) { |
|||
numVars += 2; |
|||
} |
|||
if (output->OUTPjc) { |
|||
numVars += 2; |
|||
} |
|||
if (output->OUTPjd) { |
|||
numVars += 2; |
|||
} |
|||
if (output->OUTPjn) { |
|||
numVars += 2; |
|||
} |
|||
if (output->OUTPjp) { |
|||
numVars += 2; |
|||
} |
|||
if (output->OUTPjt) { |
|||
numVars += 2; |
|||
} |
|||
if (output->OUTPuNet) { |
|||
numVars++; |
|||
} |
|||
if (output->OUTPmun) { |
|||
numVars++; |
|||
} |
|||
if (output->OUTPmup) { |
|||
numVars++; |
|||
} |
|||
output->OUTPnumVars = numVars; |
|||
} |
|||
/* generate the work array for printing node info */ |
|||
XCALLOC(nodeArray, TWOnode **, 1 + pDevice->numXNodes); |
|||
for (xIndex = 1; xIndex <= pDevice->numXNodes; xIndex++) { |
|||
XCALLOC(nodeArray[xIndex], TWOnode *, 1 + pDevice->numYNodes); |
|||
} |
|||
|
|||
/* store the nodes in this work array and print out later */ |
|||
for (xIndex = 1; xIndex < pDevice->numXNodes; xIndex++) { |
|||
for (yIndex = 1; yIndex < pDevice->numYNodes; yIndex++) { |
|||
pElem = pDevice->elemArray[xIndex][yIndex]; |
|||
if (pElem != NIL(TWOelem)) { |
|||
if (refPsi == 0.0 && pElem->matlInfo->type == SEMICON) { |
|||
refPsi = pElem->matlInfo->refPsi; |
|||
} |
|||
for (index = 0; index <= 3; index++) { |
|||
if (pElem->evalNodes[index]) { |
|||
pNode = pElem->pNodes[index]; |
|||
nodeArray[pNode->nodeI][pNode->nodeJ] = pNode; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Initialize rawfile */ |
|||
numVars = output->OUTPnumVars; |
|||
fprintf(file, "Title: Device %s internal state\n", pDevice->name); |
|||
fprintf(file, "Plotname: Device Cross Section\n"); |
|||
fprintf(file, "Flags: real\n"); |
|||
fprintf(file, "Command: deftype p xs cross\n"); |
|||
fprintf(file, "Command: deftype v distance m\n"); |
|||
fprintf(file, "Command: deftype v concentration cm^-3\n"); |
|||
fprintf(file, "Command: deftype v electric_field V/cm\n"); |
|||
fprintf(file, "Command: deftype v current_density A/cm^2\n"); |
|||
fprintf(file, "Command: deftype v concentration/time cm^-3/s\n"); |
|||
fprintf(file, "Command: deftype v mobility cm^2/Vs\n"); |
|||
fprintf(file, "No. Variables: %d\n", numVars); |
|||
fprintf(file, "No. Points: %d\n", |
|||
pDevice->numXNodes * pDevice->numYNodes); |
|||
fprintf(file, "Dimensions: %d,%d\n", |
|||
pDevice->numXNodes, pDevice->numYNodes); |
|||
numVars = 0; |
|||
fprintf(file, "Variables:\n"); |
|||
fprintf(file, "\t%d y distance\n", numVars++); |
|||
fprintf(file, "\t%d x distance\n", numVars++); |
|||
if (output->OUTPpsi) { |
|||
fprintf(file, "\t%d psi voltage\n", numVars++); |
|||
} |
|||
if (output->OUTPequPsi) { |
|||
fprintf(file, "\t%d equ.psi voltage\n", numVars++); |
|||
} |
|||
if (output->OUTPvacPsi) { |
|||
fprintf(file, "\t%d vac.psi voltage\n", numVars++); |
|||
} |
|||
if (output->OUTPphin) { |
|||
fprintf(file, "\t%d phin voltage\n", numVars++); |
|||
} |
|||
if (output->OUTPphip) { |
|||
fprintf(file, "\t%d phip voltage\n", numVars++); |
|||
} |
|||
if (output->OUTPphic) { |
|||
fprintf(file, "\t%d phic voltage\n", numVars++); |
|||
} |
|||
if (output->OUTPphiv) { |
|||
fprintf(file, "\t%d phiv voltage\n", numVars++); |
|||
} |
|||
if (output->OUTPdoping) { |
|||
fprintf(file, "\t%d dop concentration\n", numVars++); |
|||
} |
|||
if (output->OUTPnConc) { |
|||
fprintf(file, "\t%d n concentration\n", numVars++); |
|||
} |
|||
if (output->OUTPpConc) { |
|||
fprintf(file, "\t%d p concentration\n", numVars++); |
|||
} |
|||
if (output->OUTPeField) { |
|||
fprintf(file, "\t%d ex electric_field\n", numVars++); |
|||
fprintf(file, "\t%d ey electric_field\n", numVars++); |
|||
} |
|||
if (output->OUTPjc) { |
|||
fprintf(file, "\t%d jcx current_density\n", numVars++); |
|||
fprintf(file, "\t%d jcy current_density\n", numVars++); |
|||
} |
|||
if (output->OUTPjd) { |
|||
fprintf(file, "\t%d jdx current_density\n", numVars++); |
|||
fprintf(file, "\t%d jdy current_density\n", numVars++); |
|||
} |
|||
if (output->OUTPjn) { |
|||
fprintf(file, "\t%d jnx current_density\n", numVars++); |
|||
fprintf(file, "\t%d jny current_density\n", numVars++); |
|||
} |
|||
if (output->OUTPjp) { |
|||
fprintf(file, "\t%d jpx current_density\n", numVars++); |
|||
fprintf(file, "\t%d jpy current_density\n", numVars++); |
|||
} |
|||
if (output->OUTPjt) { |
|||
fprintf(file, "\t%d jtx current_density\n", numVars++); |
|||
fprintf(file, "\t%d jty current_density\n", numVars++); |
|||
} |
|||
if (output->OUTPuNet) { |
|||
fprintf(file, "\t%d unet concentration/time\n", numVars++); |
|||
} |
|||
if (output->OUTPmun) { |
|||
fprintf(file, "\t%d mun mobility\n", numVars++); |
|||
} |
|||
if (output->OUTPmup) { |
|||
fprintf(file, "\t%d mup mobility\n", numVars++); |
|||
} |
|||
fprintf(file, "Binary:\n"); |
|||
|
|||
for (xIndex = 1; xIndex <= pDevice->numXNodes; xIndex++) { |
|||
for (yIndex = 1; yIndex <= pDevice->numYNodes; yIndex++) { |
|||
pNode = nodeArray[xIndex][yIndex]; |
|||
if (pNode != NIL(TWOnode)) { |
|||
/* Find the element to which this node belongs. */ |
|||
foundElem = FALSE; |
|||
for (index = 0; (index <= 3) && (!foundElem); index++) { |
|||
pElem = pNode->pElems[index]; |
|||
if (pElem != NIL(TWOelem) && pElem->evalNodes[(index + 2) % 4]) { |
|||
foundElem = TRUE; |
|||
} |
|||
} |
|||
nodeFields(pElem, pNode, &ex, &ey); |
|||
nodeCurrents(pElem, pNode, &mun, &mup, |
|||
&jnx, &jny, &jpx, &jpy, &jdx, &jdy); |
|||
jcx = jnx + jpx; |
|||
jcy = jny + jpy; |
|||
jtx = jcx + jdx; |
|||
jty = jcy + jdy; |
|||
|
|||
info = pElem->matlInfo; |
|||
eGap = pNode->eg * VNorm; |
|||
dGap = 0.5 * (info->eg0 - eGap); |
|||
|
|||
/* Now fill in the data array */ |
|||
numVars = 0; |
|||
data[numVars++] = yScale[yIndex] * 1e-2; |
|||
data[numVars++] = xScale[xIndex] * 1e-2; |
|||
if (output->OUTPpsi) { |
|||
data[numVars++] = (pNode->psi - refPsi) * VNorm; |
|||
} |
|||
if (output->OUTPequPsi) { |
|||
data[numVars++] = (pNode->psi0 - refPsi) * VNorm; |
|||
} |
|||
if (output->OUTPvacPsi) { |
|||
data[numVars++] = pNode->psi * VNorm; |
|||
} |
|||
if (output->OUTPphin) { |
|||
if (info->type != INSULATOR) { |
|||
data[numVars++] = (pNode->psi - refPsi |
|||
- log(pNode->nConc / pNode->nie)) * VNorm; |
|||
} else { |
|||
data[numVars++] = 0.0; |
|||
} |
|||
} |
|||
if (output->OUTPphip) { |
|||
if (info->type != INSULATOR) { |
|||
data[numVars++] = (pNode->psi - refPsi |
|||
+ log(pNode->pConc / pNode->nie)) * VNorm; |
|||
} else { |
|||
data[numVars++] = 0.0; |
|||
} |
|||
} |
|||
if (output->OUTPphic) { |
|||
data[numVars++] = (pNode->psi + pNode->eaff) * VNorm + dGap; |
|||
} |
|||
if (output->OUTPphiv) { |
|||
data[numVars++] = (pNode->psi + pNode->eaff) * VNorm + dGap + eGap; |
|||
} |
|||
if (output->OUTPdoping) { |
|||
data[numVars++] = pNode->netConc * NNorm; |
|||
} |
|||
if (output->OUTPnConc) { |
|||
data[numVars++] = pNode->nConc * NNorm; |
|||
} |
|||
if (output->OUTPpConc) { |
|||
data[numVars++] = pNode->pConc * NNorm; |
|||
} |
|||
if (output->OUTPeField) { |
|||
data[numVars++] = ex * ENorm; |
|||
data[numVars++] = ey * ENorm; |
|||
} |
|||
if (output->OUTPjc) { |
|||
data[numVars++] = jcx * JNorm; |
|||
data[numVars++] = jcy * JNorm; |
|||
} |
|||
if (output->OUTPjd) { |
|||
data[numVars++] = jdx * JNorm; |
|||
data[numVars++] = jdy * JNorm; |
|||
} |
|||
if (output->OUTPjn) { |
|||
data[numVars++] = jnx * JNorm; |
|||
data[numVars++] = jny * JNorm; |
|||
} |
|||
if (output->OUTPjp) { |
|||
data[numVars++] = jpx * JNorm; |
|||
data[numVars++] = jpy * JNorm; |
|||
} |
|||
if (output->OUTPjt) { |
|||
data[numVars++] = jtx * JNorm; |
|||
data[numVars++] = jty * JNorm; |
|||
} |
|||
if (output->OUTPuNet) { |
|||
data[numVars++] = pNode->uNet * NNorm / TNorm; |
|||
} |
|||
if (output->OUTPmun) { |
|||
data[numVars++] = mun; |
|||
} |
|||
if (output->OUTPmup) { |
|||
data[numVars++] = mup; |
|||
} |
|||
fwrite((char *) data, sizeof(double), numVars, file); |
|||
} else { |
|||
for (index = 0; index < output->OUTPnumVars; index++) { |
|||
data[index] = 0.0; |
|||
} |
|||
data[0] = yScale[yIndex] * 1e-2; |
|||
data[1] = xScale[xIndex] * 1e-2; |
|||
fwrite((char *) data, sizeof(double), numVars, file); |
|||
} |
|||
} |
|||
} |
|||
/* Delete work array. */ |
|||
for (xIndex = 1; xIndex <= pDevice->numXNodes; xIndex++) { |
|||
FREE(nodeArray[xIndex]); |
|||
} |
|||
FREE(nodeArray); |
|||
} |
|||
|
|||
/* XXX This is what the SPARSE element structure looks like. |
|||
* We can't take it from its definition because the include |
|||
* file redefines all sorts of things. Note that we are |
|||
* violating data encapsulation to find out the size of this |
|||
* thing. |
|||
*/ |
|||
struct MatrixElement |
|||
{ spREAL Real; |
|||
spREAL Imag; |
|||
int Row; |
|||
int Col; |
|||
struct MatrixElement *NextInRow; |
|||
struct MatrixElement *NextInCol; |
|||
}; |
|||
|
|||
void |
|||
TWOmemStats(FILE *file, TWOdevice *pDevice) |
|||
{ |
|||
static char *memFormat = "%-20s%10d%10d\n"; |
|||
static char *sumFormat = "%20s %-10d\n"; |
|||
unsigned int size; |
|||
unsigned int memory; |
|||
TWOmaterial *pMaterial; |
|||
TWOcontact *pContact; |
|||
TWOchannel *pChannel; |
|||
int numContactNodes; |
|||
|
|||
fprintf(file, "----------------------------------------\n"); |
|||
fprintf(file, "Device %s Memory Usage:\n", pDevice->name ); |
|||
fprintf(file, "Item Count Bytes\n"); |
|||
fprintf(file, "----------------------------------------\n"); |
|||
|
|||
size = 1; |
|||
memory = size * sizeof(TWOdevice); |
|||
fprintf( file, memFormat, "Device", size, memory ); |
|||
size = pDevice->numElems; |
|||
memory = size * sizeof(TWOelem); |
|||
fprintf( file, memFormat, "Elements", size, memory ); |
|||
size = pDevice->numNodes; |
|||
memory = size * sizeof(TWOnode); |
|||
fprintf( file, memFormat, "Nodes", size, memory ); |
|||
size = pDevice->numEdges; |
|||
memory = size * sizeof(TWOedge); |
|||
fprintf( file, memFormat, "Edges", size, memory ); |
|||
|
|||
size = pDevice->numXNodes; |
|||
memory = size * sizeof(TWOelem **); |
|||
size = (pDevice->numXNodes-1) * pDevice->numYNodes; |
|||
memory += size * sizeof(TWOelem *); |
|||
size = pDevice->numElems + 1; |
|||
memory += size * sizeof(TWOelem *); |
|||
size = pDevice->numXNodes + pDevice->numYNodes; |
|||
memory += size * sizeof(double); |
|||
size = 0; |
|||
for (pMaterial = pDevice->pMaterials; pMaterial; pMaterial = pMaterial->next) |
|||
size++; |
|||
memory += size * sizeof(TWOmaterial); |
|||
size = numContactNodes = 0; |
|||
for (pContact = pDevice->pFirstContact; pContact; pContact = pContact->next) { |
|||
numContactNodes += pContact->numNodes; |
|||
size++; |
|||
} |
|||
memory += size * sizeof(TWOcontact); |
|||
size = numContactNodes; |
|||
memory += size * sizeof(TWOnode *); |
|||
size = 0; |
|||
for (pChannel = pDevice->pChannel; pChannel; pChannel = pChannel->next) |
|||
size++; |
|||
memory += size * sizeof(TWOchannel); |
|||
fprintf(file, "%-20s%10s%10d\n", "Misc Mesh", "n/a", memory); |
|||
|
|||
size = pDevice->numOrigEquil; |
|||
memory = size * sizeof(struct MatrixElement); |
|||
fprintf( file, memFormat, "Equil Orig NZ", size, memory ); |
|||
size = pDevice->numFillEquil; |
|||
memory = size * sizeof(struct MatrixElement); |
|||
fprintf( file, memFormat, "Equil Fill NZ", size, memory ); |
|||
size = pDevice->numOrigEquil + pDevice->numFillEquil; |
|||
memory = size * sizeof(struct MatrixElement); |
|||
fprintf( file, memFormat, "Equil Tot NZ", size, memory ); |
|||
size = pDevice->dimEquil; |
|||
memory = size * 4 * sizeof(double); |
|||
fprintf( file, memFormat, "Equil Vectors", size, memory ); |
|||
|
|||
size = pDevice->numOrigBias; |
|||
memory = size * sizeof(struct MatrixElement); |
|||
fprintf( file, memFormat, "Bias Orig NZ", size, memory ); |
|||
size = pDevice->numFillBias; |
|||
memory = size * sizeof(struct MatrixElement); |
|||
fprintf( file, memFormat, "Bias Fill NZ", size, memory ); |
|||
size = pDevice->numOrigBias + pDevice->numFillBias; |
|||
memory = size * sizeof(struct MatrixElement); |
|||
fprintf( file, memFormat, "Bias Tot NZ", size, memory ); |
|||
size = pDevice->dimBias; |
|||
memory = size * 5 * sizeof(double); |
|||
fprintf( file, memFormat, "Bias Vectors", size, memory ); |
|||
|
|||
size = pDevice->numEdges * TWOnumEdgeStates + |
|||
pDevice->numNodes * TWOnumNodeStates; |
|||
memory = size * sizeof(double); |
|||
fprintf( file, memFormat, "State Vector", size, memory ); |
|||
} |
|||
|
|||
void |
|||
TWOcpuStats(FILE *file, TWOdevice *pDevice) |
|||
{ |
|||
static char *cpuFormat = "%-20s%10g%10g%10g%10g%10g\n"; |
|||
TWOstats *pStats = pDevice->pStats; |
|||
double total; |
|||
int iTotal; |
|||
|
|||
fprintf(file, |
|||
"----------------------------------------------------------------------\n"); |
|||
fprintf(file, |
|||
"Device %s Time Usage:\n", pDevice->name); |
|||
fprintf(file, |
|||
"Item SETUP DC TRAN AC TOTAL\n"); |
|||
fprintf(file, |
|||
"----------------------------------------------------------------------\n"); |
|||
|
|||
total = pStats->setupTime[STAT_SETUP] + |
|||
pStats->setupTime[STAT_DC] + |
|||
pStats->setupTime[STAT_TRAN] + |
|||
pStats->setupTime[STAT_AC]; |
|||
fprintf(file, cpuFormat, "Setup Time", |
|||
pStats->setupTime[STAT_SETUP], |
|||
pStats->setupTime[STAT_DC], |
|||
pStats->setupTime[STAT_TRAN], |
|||
pStats->setupTime[STAT_AC], |
|||
total); |
|||
|
|||
total = pStats->loadTime[STAT_SETUP] + |
|||
pStats->loadTime[STAT_DC] + |
|||
pStats->loadTime[STAT_TRAN] + |
|||
pStats->loadTime[STAT_AC]; |
|||
fprintf(file, cpuFormat, "Load Time", |
|||
pStats->loadTime[STAT_SETUP], |
|||
pStats->loadTime[STAT_DC], |
|||
pStats->loadTime[STAT_TRAN], |
|||
pStats->loadTime[STAT_AC], |
|||
total); |
|||
|
|||
total = pStats->orderTime[STAT_SETUP] + |
|||
pStats->orderTime[STAT_DC] + |
|||
pStats->orderTime[STAT_TRAN] + |
|||
pStats->orderTime[STAT_AC]; |
|||
fprintf(file, cpuFormat, "Order Time", |
|||
pStats->orderTime[STAT_SETUP], |
|||
pStats->orderTime[STAT_DC], |
|||
pStats->orderTime[STAT_TRAN], |
|||
pStats->orderTime[STAT_AC], |
|||
total); |
|||
|
|||
total = pStats->factorTime[STAT_SETUP] + |
|||
pStats->factorTime[STAT_DC] + |
|||
pStats->factorTime[STAT_TRAN] + |
|||
pStats->factorTime[STAT_AC]; |
|||
fprintf(file, cpuFormat, "Factor Time", |
|||
pStats->factorTime[STAT_SETUP], |
|||
pStats->factorTime[STAT_DC], |
|||
pStats->factorTime[STAT_TRAN], |
|||
pStats->factorTime[STAT_AC], |
|||
total); |
|||
|
|||
total = pStats->solveTime[STAT_SETUP] + |
|||
pStats->solveTime[STAT_DC] + |
|||
pStats->solveTime[STAT_TRAN] + |
|||
pStats->solveTime[STAT_AC]; |
|||
fprintf(file, cpuFormat, "Solve Time", |
|||
pStats->solveTime[STAT_SETUP], |
|||
pStats->solveTime[STAT_DC], |
|||
pStats->solveTime[STAT_TRAN], |
|||
pStats->solveTime[STAT_AC], |
|||
total); |
|||
|
|||
total = pStats->updateTime[STAT_SETUP] + |
|||
pStats->updateTime[STAT_DC] + |
|||
pStats->updateTime[STAT_TRAN] + |
|||
pStats->updateTime[STAT_AC]; |
|||
fprintf(file, cpuFormat, "Update Time", |
|||
pStats->updateTime[STAT_SETUP], |
|||
pStats->updateTime[STAT_DC], |
|||
pStats->updateTime[STAT_TRAN], |
|||
pStats->updateTime[STAT_AC], |
|||
total); |
|||
|
|||
total = pStats->checkTime[STAT_SETUP] + |
|||
pStats->checkTime[STAT_DC] + |
|||
pStats->checkTime[STAT_TRAN] + |
|||
pStats->checkTime[STAT_AC]; |
|||
fprintf(file, cpuFormat, "Check Time", |
|||
pStats->checkTime[STAT_SETUP], |
|||
pStats->checkTime[STAT_DC], |
|||
pStats->checkTime[STAT_TRAN], |
|||
pStats->checkTime[STAT_AC], |
|||
total); |
|||
|
|||
total = pStats->setupTime[STAT_SETUP] + |
|||
pStats->setupTime[STAT_DC] + |
|||
pStats->setupTime[STAT_TRAN] + |
|||
pStats->setupTime[STAT_AC]; |
|||
fprintf(file, cpuFormat, "Misc Time", |
|||
pStats->miscTime[STAT_SETUP], |
|||
pStats->miscTime[STAT_DC], |
|||
pStats->miscTime[STAT_TRAN], |
|||
pStats->miscTime[STAT_AC], |
|||
total); |
|||
|
|||
fprintf(file, "%-40s%10g%10s%10g\n", "LTE Time", |
|||
pStats->lteTime, |
|||
"", pStats->lteTime); |
|||
|
|||
total = pStats->totalTime[STAT_SETUP] + |
|||
pStats->totalTime[STAT_DC] + |
|||
pStats->totalTime[STAT_TRAN] + |
|||
pStats->totalTime[STAT_AC]; |
|||
fprintf(file, cpuFormat, "Total Time", |
|||
pStats->totalTime[STAT_SETUP], |
|||
pStats->totalTime[STAT_DC], |
|||
pStats->totalTime[STAT_TRAN], |
|||
pStats->totalTime[STAT_AC], |
|||
total); |
|||
|
|||
iTotal = pStats->numIters[STAT_SETUP] + |
|||
pStats->numIters[STAT_DC] + |
|||
pStats->numIters[STAT_TRAN] + |
|||
pStats->numIters[STAT_AC]; |
|||
fprintf(file, "%-20s%10d%10d%10d%10d%10d\n", "Iterations", |
|||
pStats->numIters[STAT_SETUP], |
|||
pStats->numIters[STAT_DC], |
|||
pStats->numIters[STAT_TRAN], |
|||
pStats->numIters[STAT_AC], |
|||
iTotal); |
|||
} |
|||
@ -0,0 +1,679 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group |
|||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
/* |
|||
* Functions for projecting the next solution point for use with the modified |
|||
* two-level Newton scheme |
|||
*/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numglobs.h" |
|||
#include "numenum.h" |
|||
#include "twomesh.h" |
|||
#include "twodev.h" |
|||
#include "bool.h" |
|||
#include "spMatrix.h" |
|||
#include "twoddefs.h" |
|||
#include "twodext.h" |
|||
#include "cidersupt.h" |
|||
|
|||
|
|||
/* Forward Declarations */ |
|||
|
|||
|
|||
void NUMD2project(TWOdevice *pDevice, double delV) |
|||
{ |
|||
TWOnode *pNode; |
|||
TWOelem *pElem; |
|||
int index, eIndex, numContactNodes; |
|||
TWOcontact *pContact = pDevice->pLastContact; |
|||
double *incVpn, *solution = pDevice->dcSolution; |
|||
double delPsi, delN, delP, newN, newP; |
|||
|
|||
delV = -delV / VNorm; |
|||
/* update the boundary condition for the last contact */ |
|||
numContactNodes = pContact->numNodes; |
|||
for ( index = 0; index < numContactNodes; index++ ) { |
|||
pNode = pContact->pNodes[ index ]; |
|||
pNode->psi += delV; |
|||
} |
|||
|
|||
/* |
|||
* store the new rhs for computing the incremental quantities |
|||
* with the second to last node. solve the system of equations |
|||
*/ |
|||
|
|||
if ( ABS(delV) < MIN_DELV ) { |
|||
TWOstoreInitialGuess( pDevice ); |
|||
return; |
|||
} |
|||
incVpn = pDevice->dcDeltaSolution; |
|||
storeNewRhs( pDevice, pContact ); |
|||
spSolve( pDevice->matrix, pDevice->rhs, incVpn, NIL(spREAL), NIL(spREAL) ); |
|||
|
|||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) { |
|||
pElem = pDevice->elements[ eIndex ]; |
|||
for ( index = 0; index <= 3; index++ ) { |
|||
if ( pElem->evalNodes[ index ] ) { |
|||
pNode = pElem->pNodes[ index ]; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
delPsi = incVpn[ pNode->psiEqn ] * delV; |
|||
solution[ pNode->psiEqn ] = pNode->psi + delPsi; |
|||
if ( pElem->elemType == SEMICON |
|||
&& (!OneCarrier || (OneCarrier == N_TYPE)) ) { |
|||
delN = incVpn[ pNode->nEqn ] * delV; |
|||
newN = pNode->nConc + delN; |
|||
if ( newN <= 0.0 ) { |
|||
solution[ pNode->nEqn ] = guessNewConc( pNode->nConc, delN ); |
|||
} |
|||
else { |
|||
solution[ pNode->nEqn ] = newN; |
|||
} |
|||
} |
|||
if ( pElem->elemType == SEMICON |
|||
&& (!OneCarrier || (OneCarrier == P_TYPE)) ) { |
|||
delP = incVpn[ pNode->pEqn ] * delV; |
|||
newP = pNode->pConc + delP; |
|||
if ( newP <= 0.0 ) { |
|||
solution[ pNode->pEqn ] = guessNewConc( pNode->pConc, delP ); |
|||
} |
|||
else { |
|||
solution[ pNode->pEqn ] = newP; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
void NBJT2project(TWOdevice *pDevice, double delVce, double delVbe) |
|||
{ |
|||
TWOnode *pNode; |
|||
TWOelem *pElem; |
|||
int index, eIndex, numContactNodes; |
|||
TWOcontact *pColContact = pDevice->pFirstContact; |
|||
TWOcontact *pBaseContact = pDevice->pFirstContact->next; |
|||
double *incVce, *incVbe, *solution = pDevice->dcSolution; |
|||
double delPsi, delN, delP, newN, newP; |
|||
double nConc, pConc; |
|||
|
|||
/* Normalize the voltages for calculations. */ |
|||
if ( delVce != 0.0 ) { |
|||
delVce = delVce / VNorm; |
|||
numContactNodes = pColContact->numNodes; |
|||
for ( index = 0; index < numContactNodes; index++ ) { |
|||
pNode = pColContact->pNodes[ index ]; |
|||
pNode->psi += delVce; |
|||
} |
|||
} |
|||
if ( delVbe != 0.0 ) { |
|||
delVbe = delVbe / VNorm; |
|||
numContactNodes = pBaseContact->numNodes; |
|||
for ( index = 0; index < numContactNodes; index++ ) { |
|||
pNode = pBaseContact->pNodes[ index ]; |
|||
pNode->psi += delVbe; |
|||
} |
|||
} |
|||
|
|||
/* |
|||
* store the new rhs for computing the incremental quantities |
|||
* incVce (dcDeltaSolution) and incVbe (copiedSolution) are used to |
|||
* store the incremental quantities associated with Vce and Vbe |
|||
*/ |
|||
|
|||
/* set incVce = dcDeltaSolution; incVbe = copiedSolution */ |
|||
|
|||
if ( ABS( delVce ) > MIN_DELV ) { |
|||
incVce = pDevice->dcDeltaSolution; |
|||
storeNewRhs( pDevice, pColContact ); |
|||
spSolve( pDevice->matrix, pDevice->rhs, incVce, NIL(spREAL), NIL(spREAL)); |
|||
|
|||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) { |
|||
pElem = pDevice->elements[ eIndex ]; |
|||
for ( index = 0; index <= 3; index++ ) { |
|||
if ( pElem->evalNodes[ index ] ) { |
|||
pNode = pElem->pNodes[ index ]; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
delPsi = incVce[ pNode->psiEqn ] * delVce; |
|||
solution[ pNode->psiEqn ] = pNode->psi + delPsi; |
|||
if ( pElem->elemType == SEMICON |
|||
&& (!OneCarrier || (OneCarrier == N_TYPE)) ) { |
|||
delN = incVce[ pNode->nEqn ] * delVce; |
|||
newN = pNode->nConc + delN; |
|||
if ( newN <= 0.0 ) { |
|||
solution[ pNode->nEqn ] = guessNewConc( pNode->nConc, delN ); |
|||
} |
|||
else { |
|||
solution[ pNode->nEqn ] = newN; |
|||
} |
|||
} |
|||
if ( pElem->elemType == SEMICON |
|||
&& (!OneCarrier | (OneCarrier == P_TYPE)) ) { |
|||
delP = incVce[ pNode->pEqn ] * delVce; |
|||
newP = pNode->pConc + delP; |
|||
if ( newP <= 0.0 ) { |
|||
solution[ pNode->pEqn ] = guessNewConc( pNode->pConc, delP ); |
|||
} |
|||
else { |
|||
solution[ pNode->pEqn ] = newP; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
else { |
|||
TWOstoreInitialGuess( pDevice ); |
|||
} |
|||
|
|||
if ( ABS( delVbe ) > MIN_DELV ) { |
|||
incVbe = pDevice->copiedSolution; |
|||
storeNewRhs( pDevice, pBaseContact ); |
|||
spSolve( pDevice->matrix, pDevice->rhs, incVbe, NIL(spREAL), NIL(spREAL)); |
|||
|
|||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) { |
|||
pElem = pDevice->elements[ eIndex ]; |
|||
for ( index = 0; index <= 3; index++ ) { |
|||
if ( pElem->evalNodes[ index ] ) { |
|||
pNode = pElem->pNodes[ index ]; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
delPsi = incVbe[ pNode->psiEqn ] * delVbe; |
|||
solution[ pNode->psiEqn ] += delPsi; |
|||
if ( pElem->elemType == SEMICON |
|||
&& (!OneCarrier || (OneCarrier == N_TYPE)) ) { |
|||
delN = incVbe[ pNode->nEqn ] * delVbe; |
|||
nConc = solution[ pNode->nEqn ]; |
|||
newN = nConc + delN; |
|||
if ( newN <= 0.0 ) { |
|||
solution[ pNode->nEqn ] = guessNewConc( nConc, delN ); |
|||
} |
|||
else { |
|||
solution[ pNode->nEqn ] = newN; |
|||
} |
|||
} |
|||
if ( pElem->elemType == SEMICON |
|||
&& (!OneCarrier || (OneCarrier == P_TYPE)) ) { |
|||
delP = incVbe[ pNode->pEqn ] * delVbe; |
|||
pConc = solution[ pNode->pEqn ]; |
|||
newP = pConc + delP; |
|||
if ( newP <= 0.0 ) { |
|||
solution[ pNode->pEqn ] = guessNewConc( pConc, delP ); |
|||
} |
|||
else { |
|||
solution[ pNode->pEqn ] = newP; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
void NUMOSproject(TWOdevice *pDevice, double delVdb, double delVsb, |
|||
double delVgb) |
|||
{ |
|||
TWOnode *pNode; |
|||
TWOelem *pElem; |
|||
int index, eIndex, numContactNodes; |
|||
TWOcontact *pDContact = pDevice->pFirstContact; |
|||
TWOcontact *pGContact = pDevice->pFirstContact->next; |
|||
TWOcontact *pSContact = pDevice->pFirstContact->next->next; |
|||
double *incVdb, *incVsb, *incVgb, *solution = pDevice->dcSolution; |
|||
double delPsi, delN, delP, newN, newP; |
|||
double nConc, pConc; |
|||
|
|||
/* normalize the voltages for calculations */ |
|||
if ( delVdb != 0.0 ) { |
|||
delVdb = delVdb / VNorm; |
|||
numContactNodes = pDContact->numNodes; |
|||
for ( index = 0; index < numContactNodes; index++ ) { |
|||
pNode = pDContact->pNodes[ index ]; |
|||
pNode->psi += delVdb; |
|||
} |
|||
} |
|||
if ( delVsb != 0.0 ) { |
|||
delVsb = delVsb / VNorm; |
|||
numContactNodes = pSContact->numNodes; |
|||
for ( index = 0; index < numContactNodes; index++ ) { |
|||
pNode = pSContact->pNodes[ index ]; |
|||
pNode->psi += delVsb; |
|||
} |
|||
} |
|||
if ( delVgb != 0.0 ) { |
|||
delVgb = delVgb / VNorm; |
|||
numContactNodes = pGContact->numNodes; |
|||
for ( index = 0; index < numContactNodes; index++ ) { |
|||
pNode = pGContact->pNodes[ index ]; |
|||
pNode->psi += delVgb; |
|||
} |
|||
} |
|||
|
|||
/* |
|||
* store the new rhs for computing the incremental quantities |
|||
* incVdb (dcDeltaSolution), incVsb, incVgb |
|||
*/ |
|||
|
|||
if ( ABS( delVdb ) > MIN_DELV ) { |
|||
|
|||
incVdb = pDevice->dcDeltaSolution; |
|||
storeNewRhs( pDevice, pDContact ); |
|||
spSolve( pDevice->matrix, pDevice->rhs, incVdb, NIL(spREAL), NIL(spREAL)); |
|||
|
|||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) { |
|||
pElem = pDevice->elements[ eIndex ]; |
|||
for ( index = 0; index <= 3; index++ ) { |
|||
if ( pElem->evalNodes[ index ] ) { |
|||
pNode = pElem->pNodes[ index ]; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
delPsi = incVdb[ pNode->psiEqn ] * delVdb; |
|||
solution[ pNode->psiEqn ] = pNode->psi + delPsi; |
|||
if ( pElem->elemType == SEMICON |
|||
&& (!OneCarrier || (OneCarrier == N_TYPE)) ) { |
|||
delN = incVdb[ pNode->nEqn ] * delVdb; |
|||
newN = pNode->nConc + delN; |
|||
if ( newN <= 0.0 ) { |
|||
solution[ pNode->nEqn ] = guessNewConc( pNode->nConc, delN ); |
|||
} |
|||
else { |
|||
solution[ pNode->nEqn ] = newN; |
|||
} |
|||
} |
|||
if ( pElem->elemType == SEMICON |
|||
&& (!OneCarrier || (OneCarrier == P_TYPE)) ) { |
|||
delP = incVdb[ pNode->pEqn ] * delVdb; |
|||
newP = pNode->pConc + delP; |
|||
if ( newP <= 0.0 ) { |
|||
solution[ pNode->pEqn ] = guessNewConc( pNode->pConc, delP ); |
|||
} |
|||
else { |
|||
solution[ pNode->pEqn ] = newP; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
else { |
|||
TWOstoreInitialGuess( pDevice ); |
|||
} |
|||
|
|||
if ( ABS( delVsb ) > MIN_DELV ) { |
|||
|
|||
incVsb = pDevice->dcDeltaSolution; |
|||
storeNewRhs( pDevice, pSContact ); |
|||
spSolve( pDevice->matrix, pDevice->rhs, incVsb, NIL(spREAL), NIL(spREAL)); |
|||
|
|||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) { |
|||
pElem = pDevice->elements[ eIndex ]; |
|||
for ( index = 0; index <= 3; index++ ) { |
|||
if ( pElem->evalNodes[ index ] ) { |
|||
pNode = pElem->pNodes[ index ]; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
delPsi = incVsb[ pNode->psiEqn ] * delVsb; |
|||
solution[ pNode->psiEqn ] += delPsi; |
|||
if ( pElem->elemType == SEMICON |
|||
&& (!OneCarrier || (OneCarrier == N_TYPE)) ) { |
|||
delN = incVsb[ pNode->nEqn ] * delVsb; |
|||
nConc = solution[ pNode->nEqn ]; |
|||
newN = nConc + delN; |
|||
if ( newN <= 0.0 ) { |
|||
solution[ pNode->nEqn ] = guessNewConc( nConc, delN ); |
|||
} |
|||
else { |
|||
solution[ pNode->nEqn ] = newN; |
|||
} |
|||
} |
|||
if ( pElem->elemType == SEMICON |
|||
&& (!OneCarrier || (OneCarrier == P_TYPE)) ) { |
|||
delP = incVsb[ pNode->pEqn ] * delVsb; |
|||
pConc = solution[ pNode->pEqn ]; |
|||
newP = pConc + delP; |
|||
if ( newP <= 0.0 ) { |
|||
solution[ pNode->pEqn ] = guessNewConc( pConc, delP ); |
|||
} |
|||
else { |
|||
solution[ pNode->pEqn ] = newP; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
if ( ABS( delVgb ) > MIN_DELV ) { |
|||
|
|||
incVgb = pDevice->dcDeltaSolution; |
|||
storeNewRhs( pDevice, pGContact ); |
|||
spSolve( pDevice->matrix, pDevice->rhs, incVgb, NIL(spREAL), NIL(spREAL)); |
|||
|
|||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) { |
|||
pElem = pDevice->elements[ eIndex ]; |
|||
for ( index = 0; index <= 3; index++ ) { |
|||
if ( pElem->evalNodes[ index ] ) { |
|||
pNode = pElem->pNodes[ index ]; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
delPsi = incVgb[ pNode->psiEqn ] * delVgb; |
|||
solution[ pNode->psiEqn ] += delPsi; |
|||
if ( pElem->elemType == SEMICON |
|||
&& (!OneCarrier || (OneCarrier == N_TYPE)) ) { |
|||
delN = incVgb[ pNode->nEqn ] * delVgb; |
|||
nConc = solution[ pNode->nEqn ]; |
|||
newN = nConc + delN; |
|||
if ( newN <= 0.0 ) { |
|||
solution[ pNode->nEqn ] = guessNewConc( nConc, delN ); |
|||
} |
|||
else { |
|||
solution[ pNode->nEqn ] = newN; |
|||
} |
|||
} |
|||
if ( pElem->elemType == SEMICON |
|||
&& (!OneCarrier || (OneCarrier == P_TYPE)) ) { |
|||
delP = incVgb[ pNode->pEqn ] * delVgb; |
|||
pConc = solution[ pNode->pEqn ]; |
|||
newP = pConc + delP; |
|||
if ( newP <= 0.0 ) { |
|||
solution[ pNode->pEqn ] = guessNewConc( pConc, delP ); |
|||
} |
|||
else { |
|||
solution[ pNode->pEqn ] = newP; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* functions to update the solution for the full-LU and |
|||
modified two-level Newton methods |
|||
*/ |
|||
|
|||
void NUMD2update(TWOdevice *pDevice, double delV, BOOLEAN updateBoundary) |
|||
{ |
|||
TWOnode *pNode; |
|||
TWOelem *pElem; |
|||
int index, eIndex, numContactNodes; |
|||
TWOcontact *pContact = pDevice->pLastContact; |
|||
double delPsi, delN, delP, *incVpn, *solution = pDevice->dcSolution; |
|||
|
|||
delV = -delV / VNorm; |
|||
if ( updateBoundary ) { |
|||
/* update the boundary condition for the last contact */ |
|||
numContactNodes = pContact->numNodes; |
|||
for ( index = 0; index < numContactNodes; index++ ) { |
|||
pNode = pContact->pNodes[ index ]; |
|||
pNode->psi += delV; |
|||
} |
|||
} |
|||
|
|||
/* the equations have been solved while computing the conductances */ |
|||
/* solution is in dcDeltaSolution, so use it */ |
|||
|
|||
incVpn = pDevice->dcDeltaSolution; |
|||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) { |
|||
pElem = pDevice->elements[ eIndex ]; |
|||
for ( index = 0; index <= 3; index++ ) { |
|||
if ( pElem->evalNodes[ index ] ) { |
|||
pNode = pElem->pNodes[ index ]; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
delPsi = incVpn[ pNode->psiEqn ] * delV; |
|||
solution[ pNode->psiEqn ] = pNode->psi + delPsi; |
|||
if ( pElem->elemType == SEMICON |
|||
&& (!OneCarrier || (OneCarrier == N_TYPE)) ) { |
|||
delN = incVpn[ pNode->nEqn ] * delV; |
|||
solution[ pNode->nEqn ] = pNode->nConc + delN; |
|||
} |
|||
if ( pElem->elemType == SEMICON |
|||
&& (!OneCarrier || (OneCarrier == P_TYPE)) ) { |
|||
delP = incVpn[ pNode->pEqn ] * delV; |
|||
solution[ pNode->pEqn ] = pNode->pConc + delP; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
void NBJT2update(TWOdevice *pDevice, double delVce, double delVbe, |
|||
BOOLEAN updateBoundary) |
|||
{ |
|||
TWOnode *pNode; |
|||
TWOelem *pElem; |
|||
int index, eIndex, numContactNodes; |
|||
TWOcontact *pColContact = pDevice->pFirstContact; |
|||
TWOcontact *pBaseContact = pDevice->pFirstContact->next; |
|||
double delPsi, delN, delP, *incVce, *incVbe, *solution = pDevice->dcSolution; |
|||
|
|||
/* normalize the voltages for calculations */ |
|||
|
|||
if ( delVce != 0.0 ) { |
|||
delVce = delVce / VNorm; |
|||
if ( updateBoundary ) { |
|||
numContactNodes = pColContact->numNodes; |
|||
for ( index = 0; index < numContactNodes; index++ ) { |
|||
pNode = pColContact->pNodes[ index ]; |
|||
pNode->psi += delVce; |
|||
} |
|||
} |
|||
} |
|||
if ( delVbe != 0.0 ) { |
|||
delVbe = delVbe / VNorm; |
|||
if ( updateBoundary ) { |
|||
numContactNodes = pBaseContact->numNodes; |
|||
for ( index = 0; index < numContactNodes; index++ ) { |
|||
pNode = pBaseContact->pNodes[ index ]; |
|||
pNode->psi += delVbe; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* use solution from computeConductance to do update */ |
|||
/* set incVce = dcDeltaSolution; incVbe = copiedSolution */ |
|||
|
|||
incVce = pDevice->dcDeltaSolution; |
|||
incVbe = pDevice->copiedSolution; |
|||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) { |
|||
pElem = pDevice->elements[ eIndex ]; |
|||
for ( index = 0; index <= 3; index++ ) { |
|||
if ( pElem->evalNodes[ index ] ) { |
|||
pNode = pElem->pNodes[ index ]; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
delPsi = (incVce[ pNode->psiEqn ] * delVce |
|||
+ incVbe[ pNode->psiEqn ] * delVbe); |
|||
solution[ pNode->psiEqn ] = pNode->psi + delPsi; |
|||
if ( pElem->elemType == SEMICON |
|||
&& (!OneCarrier || (OneCarrier == N_TYPE)) ) { |
|||
delN = (incVce[ pNode->nEqn ] * delVce |
|||
+ incVbe[ pNode->nEqn ] * delVbe); |
|||
solution[ pNode->nEqn ] = pNode->nConc + delN; |
|||
} |
|||
if ( pElem->elemType == SEMICON |
|||
&& (!OneCarrier || (OneCarrier == P_TYPE)) ) { |
|||
delP = (incVce[ pNode->pEqn ] * delVce |
|||
+ incVbe[ pNode->pEqn ] * delVbe); |
|||
solution[ pNode->pEqn ] = pNode->pConc + delP; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
void NUMOSupdate(TWOdevice *pDevice, double delVdb, double delVsb, |
|||
double delVgb, BOOLEAN updateBoundary) |
|||
{ |
|||
TWOnode *pNode; |
|||
TWOelem *pElem; |
|||
int index, eIndex, numContactNodes; |
|||
TWOcontact *pDContact = pDevice->pFirstContact; |
|||
TWOcontact *pGContact = pDevice->pFirstContact->next; |
|||
TWOcontact *pSContact = pDevice->pFirstContact->next->next; |
|||
double delPsi, delN, delP; |
|||
double *incVdb, *incVsb, *incVgb, *solution = pDevice->dcSolution; |
|||
|
|||
/* normalize the voltages for calculations */ |
|||
if ( delVdb != 0.0 ) { |
|||
delVdb = delVdb / VNorm; |
|||
if ( updateBoundary ) { |
|||
numContactNodes = pDContact->numNodes; |
|||
for ( index = 0; index < numContactNodes; index++ ) { |
|||
pNode = pDContact->pNodes[ index ]; |
|||
pNode->psi += delVdb; |
|||
} |
|||
} |
|||
} |
|||
if ( delVsb != 0.0 ) { |
|||
delVsb = delVsb / VNorm; |
|||
if ( updateBoundary ) { |
|||
numContactNodes = pSContact->numNodes; |
|||
for ( index = 0; index < numContactNodes; index++ ) { |
|||
pNode = pSContact->pNodes[ index ]; |
|||
pNode->psi += delVsb; |
|||
} |
|||
} |
|||
} |
|||
if ( delVgb != 0.0 ) { |
|||
delVgb = delVgb / VNorm; |
|||
if ( updateBoundary ) { |
|||
numContactNodes = pGContact->numNodes; |
|||
for ( index = 0; index < numContactNodes; index++ ) { |
|||
pNode = pGContact->pNodes[ index ]; |
|||
pNode->psi += delVgb; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* use solution from computeConductance to do update */ |
|||
|
|||
incVdb = pDevice->dcDeltaSolution; |
|||
incVsb = pDevice->copiedSolution; |
|||
incVgb = pDevice->rhsImag; |
|||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) { |
|||
pElem = pDevice->elements[ eIndex ]; |
|||
for ( index = 0; index <= 3; index++ ) { |
|||
if ( pElem->evalNodes[ index ] ) { |
|||
pNode = pElem->pNodes[ index ]; |
|||
if ( pNode->nodeType != CONTACT ) { |
|||
delPsi = (incVdb[ pNode->psiEqn ] * delVdb |
|||
+ incVsb[ pNode->psiEqn ] * delVsb |
|||
+ incVgb[ pNode->psiEqn ] * delVgb); |
|||
solution[ pNode->psiEqn ] = pNode->psi + delPsi; |
|||
if ( pElem->elemType == SEMICON |
|||
&& (!OneCarrier || (OneCarrier == N_TYPE)) ) { |
|||
delN = (incVdb[ pNode->nEqn ] * delVdb |
|||
+ incVsb[ pNode->nEqn ] * delVsb |
|||
+ incVgb[ pNode->nEqn ] * delVgb); |
|||
solution[ pNode->nEqn ] = pNode->nConc + delN; |
|||
} |
|||
if ( pElem->elemType == SEMICON |
|||
&& (!OneCarrier || (OneCarrier == P_TYPE)) ) { |
|||
delP = (incVdb[ pNode->pEqn ] * delVdb |
|||
+ incVsb[ pNode->pEqn ] * delVsb |
|||
+ incVgb[ pNode->pEqn ] * delVgb); |
|||
solution[ pNode->pEqn ] = pNode->pConc + delP; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
void storeNewRhs(TWOdevice *pDevice, TWOcontact *pContact) |
|||
{ |
|||
int index, i, numContactNodes; |
|||
TWOelem *pElem; |
|||
TWOnode *pNode, *pHNode = NULL, *pVNode = NULL; |
|||
TWOedge *pHEdge = NULL, *pVEdge = NULL; |
|||
double *rhs = pDevice->rhs; |
|||
|
|||
/* zero the rhs before loading in the new rhs */ |
|||
for ( index = 1; index <= pDevice->numEqns ; index++ ) { |
|||
rhs[ index ] = 0.0; |
|||
} |
|||
|
|||
numContactNodes = pContact->numNodes; |
|||
for ( index = 0; index < numContactNodes; index++ ) { |
|||
pNode = pContact->pNodes[ index ]; |
|||
for ( i = 0; i <= 3; i++ ) { |
|||
pElem = pNode->pElems[ i ]; |
|||
if ( pElem != NIL(TWOelem)) { |
|||
/* found an element to which this node belongs */ |
|||
switch ( i ) { |
|||
case 0: |
|||
/* the TL element of this node */ |
|||
pHNode = pElem->pBLNode; |
|||
pVNode = pElem->pTRNode; |
|||
pHEdge = pElem->pBotEdge; |
|||
pVEdge = pElem->pRightEdge; |
|||
break; |
|||
case 1: |
|||
/* the TR element */ |
|||
pHNode = pElem->pBRNode; |
|||
pVNode = pElem->pTLNode; |
|||
pHEdge = pElem->pBotEdge; |
|||
pVEdge = pElem->pLeftEdge; |
|||
break; |
|||
case 2: |
|||
/* the BR element */ |
|||
pHNode = pElem->pTRNode; |
|||
pVNode = pElem->pBLNode; |
|||
pHEdge = pElem->pTopEdge; |
|||
pVEdge = pElem->pLeftEdge; |
|||
break; |
|||
case 3: |
|||
/* the BL element */ |
|||
pHNode = pElem->pTLNode; |
|||
pVNode = pElem->pBRNode; |
|||
pHEdge = pElem->pTopEdge; |
|||
pVEdge = pElem->pRightEdge; |
|||
break; |
|||
default: |
|||
printf( "storeNewRhs: shouldn't be here\n"); |
|||
break; |
|||
} |
|||
if ( pHNode->nodeType != CONTACT ) { |
|||
/* contribution to the x nodes */ |
|||
rhs[ pHNode->psiEqn ] += pElem->epsRel * 0.5 * pElem->dyOverDx; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
if ( !OneCarrier ) { |
|||
rhs[ pHNode->nEqn ] -= 0.5 * pElem->dy * pHEdge->dJnDpsiP1; |
|||
rhs[ pHNode->pEqn ] -= 0.5 * pElem->dy * pHEdge->dJpDpsiP1; |
|||
} else if ( OneCarrier == N_TYPE ) { |
|||
rhs[ pHNode->nEqn ] -= 0.5 * pElem->dy * pHEdge->dJnDpsiP1; |
|||
} else if ( OneCarrier == P_TYPE ) { |
|||
rhs[ pHNode->pEqn ] -= 0.5 * pElem->dy * pHEdge->dJpDpsiP1; |
|||
} |
|||
} |
|||
} |
|||
if ( pVNode->nodeType != CONTACT ) { |
|||
/* contribution to the y nodes */ |
|||
rhs[ pVNode->psiEqn ] += pElem->epsRel * 0.5 * pElem->dxOverDy; |
|||
if ( pElem->elemType == SEMICON ) { |
|||
if ( !OneCarrier ) { |
|||
rhs[ pVNode->nEqn ] -= 0.5 * pElem->dx * pVEdge->dJnDpsiP1; |
|||
rhs[ pVNode->pEqn ] -= 0.5 * pElem->dx * pVEdge->dJpDpsiP1; |
|||
} else if ( OneCarrier == N_TYPE ) { |
|||
rhs[ pVNode->nEqn ] -= 0.5 * pElem->dx * pVEdge->dJnDpsiP1; |
|||
} else if ( OneCarrier == P_TYPE ) { |
|||
rhs[ pVNode->pEqn ] -= 0.5 * pElem->dx * pVEdge->dJpDpsiP1; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,116 @@ |
|||
/********** |
|||
Copyright 1992 Regents of the University of California. All rights reserved. |
|||
Author: 1992 David A. Gates, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
/* |
|||
* Functions needed to read solutions for 2D devices. |
|||
*/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "plot.h" |
|||
#include "numglobs.h" |
|||
#include "numenum.h" |
|||
#include "twodev.h" |
|||
#include "twomesh.h" |
|||
#include "twoddefs.h" |
|||
#include "twodext.h" |
|||
#include "cidersupt.h" |
|||
|
|||
|
|||
int |
|||
TWOreadState(TWOdevice *pDevice, char *fileName, int numVolts, double *pV1, |
|||
double *pV2, double *pV3) |
|||
/* char *fileName: File containing raw data */ |
|||
/* int numVolts: Number of voltage differences */ |
|||
/* double *pV1, *pV2, *pV3: Pointer to return them in */ |
|||
{ |
|||
int dataLength; |
|||
int i, index, xIndex, yIndex; |
|||
TWOnode ***nodeArray = NULL; |
|||
TWOnode *pNode; |
|||
TWOelem *pElem; |
|||
TWOmaterial *info; |
|||
double refPsi = 0.0; |
|||
double *psiData, *nData, *pData; |
|||
double *vData[3]; |
|||
struct plot *stateDB; |
|||
struct plot *voltsDB; |
|||
char voltName[80]; |
|||
|
|||
stateDB = DBread( fileName ); |
|||
if (stateDB == NULL) return (-1); |
|||
voltsDB = stateDB->pl_next; |
|||
if (voltsDB == NULL) return (-1); |
|||
|
|||
for (i=0; i < numVolts; i++ ) { |
|||
sprintf( voltName, "v%d%d", i+1, numVolts+1 ); |
|||
vData[i] = DBgetData( voltsDB, voltName, 1 ); |
|||
if (vData[i] == NULL) return (-1); |
|||
} |
|||
dataLength = pDevice->numXNodes * pDevice->numYNodes; |
|||
psiData = DBgetData( stateDB, "psi", dataLength ); |
|||
nData = DBgetData( stateDB, "n", dataLength ); |
|||
pData = DBgetData( stateDB, "p", dataLength ); |
|||
if (psiData == NULL || nData == NULL || pData == NULL) return (-1); |
|||
|
|||
if (pV1 != NULL) { |
|||
*pV1 = vData[0][0]; |
|||
FREE( vData[0] ); |
|||
} |
|||
if (pV2 != NULL) { |
|||
*pV2 = vData[1][0]; |
|||
FREE( vData[1] ); |
|||
} |
|||
if (pV3 != NULL) { |
|||
*pV3 = vData[2][0]; |
|||
FREE( vData[2] ); |
|||
} |
|||
|
|||
/* generate the work array for copying node info */ |
|||
XCALLOC(nodeArray, TWOnode **, 1 + pDevice->numXNodes); |
|||
for (xIndex = 1; xIndex <= pDevice->numXNodes; xIndex++) { |
|||
XCALLOC(nodeArray[xIndex], TWOnode *, 1 + pDevice->numYNodes); |
|||
} |
|||
|
|||
/* store the nodes in this work array and use later */ |
|||
for (xIndex = 1; xIndex < pDevice->numXNodes; xIndex++) { |
|||
for (yIndex = 1; yIndex < pDevice->numYNodes; yIndex++) { |
|||
pElem = pDevice->elemArray[xIndex][yIndex]; |
|||
if (pElem != NIL(TWOelem)) { |
|||
if (refPsi == 0.0 && pElem->matlInfo->type == SEMICON) { |
|||
refPsi = pElem->matlInfo->refPsi; |
|||
} |
|||
for (index = 0; index <= 3; index++) { |
|||
if (pElem->evalNodes[index]) { |
|||
pNode = pElem->pNodes[index]; |
|||
nodeArray[pNode->nodeI][pNode->nodeJ] = pNode; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
index = 0; |
|||
for (xIndex = 1; xIndex <= pDevice->numXNodes; xIndex++) { |
|||
for (yIndex = 1; yIndex <= pDevice->numYNodes; yIndex++) { |
|||
pNode = nodeArray[xIndex][yIndex]; |
|||
index++; |
|||
if (pNode != NIL(TWOnode)) { |
|||
pNode->psi = psiData[index-1]/VNorm + refPsi; |
|||
pNode->nConc = nData[index-1]/NNorm; |
|||
pNode->pConc = pData[index-1]/NNorm; |
|||
} |
|||
} |
|||
} |
|||
/* Delete work array. */ |
|||
for (xIndex = 1; xIndex <= pDevice->numXNodes; xIndex++) { |
|||
FREE(nodeArray[xIndex]); |
|||
} |
|||
FREE(nodeArray); |
|||
|
|||
FREE(psiData); |
|||
FREE(nData); |
|||
FREE(pData); |
|||
|
|||
return (0); |
|||
} |
|||
@ -0,0 +1,94 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numglobs.h" |
|||
#include "numenum.h" |
|||
#include "twomesh.h" |
|||
#include "twodev.h" |
|||
#include "twoddefs.h" |
|||
#include "twodext.h" |
|||
|
|||
/* Forward declarations */ |
|||
static void setDirichlet(TWOcontact *, double); |
|||
|
|||
|
|||
void NUMD2setBCs(TWOdevice *pDevice, double vd) |
|||
{ |
|||
TWOcontact *pContact = pDevice->pLastContact; |
|||
|
|||
setDirichlet( pContact, - vd ); |
|||
} |
|||
|
|||
void NBJT2setBCs(TWOdevice *pDevice, double vce, double vbe) |
|||
{ |
|||
TWOcontact *pCollContact = pDevice->pFirstContact; |
|||
TWOcontact *pBaseContact = pDevice->pFirstContact->next; |
|||
|
|||
setDirichlet( pCollContact, vce ); |
|||
setDirichlet( pBaseContact, vbe ); |
|||
} |
|||
|
|||
void NUMOSsetBCs(TWOdevice *pDevice, double vdb, double vsb, double vgb) |
|||
{ |
|||
TWOcontact *pDContact = pDevice->pFirstContact; |
|||
TWOcontact *pGContact = pDevice->pFirstContact->next; |
|||
TWOcontact *pSContact = pDevice->pFirstContact->next->next; |
|||
|
|||
setDirichlet( pDContact, vdb ); |
|||
setDirichlet( pSContact, vsb ); |
|||
setDirichlet( pGContact, vgb ); |
|||
} |
|||
|
|||
static void |
|||
setDirichlet(TWOcontact *pContact, double voltage) |
|||
{ |
|||
int index, numContactNodes, i; |
|||
TWOelem *pElem = NULL; |
|||
TWOnode *pNode; |
|||
double psi, ni, pi, nie; |
|||
double conc, sign, absConc; |
|||
|
|||
voltage /= VNorm; |
|||
|
|||
numContactNodes = pContact->numNodes; |
|||
for ( index = 0; index < numContactNodes; index++ ) { |
|||
pNode = pContact->pNodes[ index ]; |
|||
|
|||
/* Find this node's owner element. */ |
|||
for ( i = 0; i <= 3; i++ ) { |
|||
pElem = pNode->pElems[ i ]; |
|||
if ( pElem != NIL(TWOelem) && pElem->evalNodes[ (i+2)%4 ] ) { |
|||
break; /* got it */ |
|||
} |
|||
} |
|||
|
|||
if (pElem->elemType == INSULATOR) { |
|||
pNode->psi = RefPsi - pNode->eaff; |
|||
pNode->nConc = 0.0; |
|||
pNode->pConc = 0.0; |
|||
} |
|||
else if (pElem->elemType == SEMICON) { |
|||
nie = pNode->nie; |
|||
conc = pNode->netConc / nie; |
|||
sign = SGN( conc ); |
|||
absConc = ABS( conc ); |
|||
if ( conc != 0.0 ) { |
|||
psi = sign * log( 0.5 * absConc + sqrt( 1.0 + 0.25*absConc*absConc )); |
|||
ni = nie * exp( psi ); |
|||
pi = nie * exp( - psi ); |
|||
} |
|||
else { |
|||
psi = 0.0; |
|||
ni = nie; |
|||
pi = nie; |
|||
} |
|||
pNode->psi = pElem->matlInfo->refPsi + psi; |
|||
pNode->nConc = ni; |
|||
pNode->pConc = pi; |
|||
} |
|||
pNode->psi += voltage; |
|||
} |
|||
} |
|||
@ -0,0 +1,318 @@ |
|||
/********** |
|||
Copyright 1991 Regents of the University of California. All rights reserved. |
|||
Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group |
|||
Author: 1991 David A. Gates, U. C. Berkeley CAD Group |
|||
**********/ |
|||
|
|||
/********** |
|||
Two-Dimensional Numerical Device Setup Routines |
|||
**********/ |
|||
|
|||
#include "ngspice.h" |
|||
#include "numglobs.h" |
|||
#include "numconst.h" |
|||
#include "numenum.h" |
|||
#include "twomesh.h" |
|||
#include "twodev.h" |
|||
#include "carddefs.h" /* XXX Not really modular if we need this. */ |
|||
#include "twoddefs.h" |
|||
#include "twodext.h" |
|||
#include "cidersupt.h" |
|||
|
|||
|
|||
/* compute node parameters */ |
|||
void TWOsetup(TWOdevice *pDevice) |
|||
{ |
|||
double temp1, deltaEg, avgConc, totalConc, absNetConc; |
|||
double ncv0, dBand, dNie, psiBand[4]; |
|||
double *xScale = pDevice->xScale; |
|||
double *yScale = pDevice->yScale; |
|||
int index, eIndex; |
|||
int numContactNodes; |
|||
TWOnode *pNode; |
|||
TWOelem *pElem; |
|||
TWOedge *pEdge; |
|||
TWOcontact *pC; |
|||
TWOmaterial *info; |
|||
|
|||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) { |
|||
pElem = pDevice->elements[ eIndex ]; |
|||
info = pElem->matlInfo; |
|||
|
|||
pElem->dx = xScale[ pElem->pTRNode->nodeI ]-xScale[ pElem->pTLNode->nodeI ]; |
|||
pElem->dy = yScale[ pElem->pBLNode->nodeJ ]-yScale[ pElem->pTLNode->nodeJ ]; |
|||
pElem->epsRel = info->eps; |
|||
|
|||
if ( pElem->elemType == INSULATOR ) { |
|||
for ( index = 0; index <= 3; index++ ) { |
|||
if ( pElem->evalNodes[ index ] ) { |
|||
pNode = pElem->pNodes[ index ]; |
|||
if (pNode->nodeType == CONTACT) { |
|||
pNode->eaff = PHI_METAL; |
|||
pNode->eg = 0.0; |
|||
} else { |
|||
pNode->eaff = info->affin; |
|||
pNode->eg = info->eg0; |
|||
} |
|||
} |
|||
} |
|||
} else if ( pElem->elemType == SEMICON ) { |
|||
ncv0 = sqrt( info->nc0 ) * sqrt( info->nv0 ); |
|||
for ( index = 0; index <= 3; index++ ) { |
|||
if ( pElem->evalNodes[ index ] ) { |
|||
pNode = pElem->pNodes[ index ]; |
|||
|
|||
/* Narrowing of Energy-Band Gap */ |
|||
if (BandGapNarrowing) { |
|||
absNetConc = ABS( pNode->netConc ); |
|||
if ( pNode->netConc > 0.0 ) { |
|||
temp1 = log( absNetConc / info->nrefBGN[ELEC] ); |
|||
deltaEg = - info->dEgDn[ELEC] * (temp1 + sqrt(temp1*temp1 + 0.5)); |
|||
pNode->eg = info->eg0 + deltaEg; |
|||
} else if ( pNode->netConc < 0.0 ) { |
|||
temp1 = log( absNetConc / info->nrefBGN[HOLE] ); |
|||
deltaEg = - info->dEgDn[HOLE] * (temp1 + sqrt(temp1*temp1 + 0.5)); |
|||
pNode->eg = info->eg0 + deltaEg; |
|||
} else { |
|||
pNode->eg = info->eg0; |
|||
} |
|||
} else { |
|||
pNode->eg = info->eg0; |
|||
} |
|||
pNode->nie = ncv0 * exp ( -0.5 * pNode->eg / Vt ); |
|||
pNode->eaff = info->affin; |
|||
/* Save band structure parameter. */ |
|||
psiBand[ index ] = - info->refPsi; |
|||
|
|||
/* Ionized-Impurity-Scattering Reduction of Carrier Lifetime */ |
|||
if (ConcDepLifetime) { |
|||
totalConc = pNode->totalConc; |
|||
temp1 = 1.0 / ( 1.0 + totalConc / info->nrefSRH[ELEC] ); |
|||
pNode->tn = info->tau0[ELEC] * temp1; |
|||
temp1 = 1.0 / ( 1.0 + totalConc / info->nrefSRH[HOLE] ); |
|||
pNode->tp = info->tau0[HOLE] * temp1; |
|||
} else { |
|||
pNode->tn = info->tau0[ELEC]; |
|||
pNode->tp = info->tau0[HOLE]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
for ( index = 0; index <= 3; index++ ) { |
|||
if ( pElem->evalEdges[ index ] ) { |
|||
pEdge = pElem->pEdges[ index ]; |
|||
|
|||
/* Fixed Interface Charge */ |
|||
pEdge->qf = 0.0; |
|||
|
|||
/* Variable Band Built-In Potential */ |
|||
if ( index <= 1 ) { |
|||
dBand = psiBand[index+1] - psiBand[index]; |
|||
dNie = log( pElem->pNodes[index+1]->nie / |
|||
pElem->pNodes[index]->nie ); |
|||
pEdge->dCBand = dBand + dNie; |
|||
pEdge->dVBand = - dBand + dNie; |
|||
} else { |
|||
dBand = psiBand[index] - psiBand[(index+1)%4]; |
|||
dNie = log( pElem->pNodes[index]->nie / |
|||
pElem->pNodes[(index+1)%4]->nie ); |
|||
pEdge->dCBand = dBand + dNie; |
|||
pEdge->dVBand = - dBand + dNie; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Evaluate conc.-dep. mobility. */ |
|||
/* Average conc of all four nodes. */ |
|||
avgConc = 0.25*(pElem->pTLNode->totalConc + pElem->pTRNode->totalConc + |
|||
pElem->pBRNode->totalConc + pElem->pBLNode->totalConc); |
|||
MOBconcDep( info, avgConc, &pElem->mun0, &pElem->mup0 ); |
|||
} |
|||
} |
|||
|
|||
for ( pC = pDevice->pFirstContact; pC != NIL(TWOcontact); pC = pC->next ) { |
|||
numContactNodes = pC->numNodes; |
|||
for ( index = 0; index < numContactNodes; index++ ) { |
|||
pNode = pC->pNodes[ index ]; |
|||
pNode->eaff = pC->workf; /* Affinity aka work function */ |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Transfer BC info from card to nodes and edges. */ |
|||
static void TWOcopyBCinfo( pDevice, pElem, card, index ) |
|||
TWOdevice *pDevice; |
|||
TWOelem *pElem; |
|||
BDRYcard *card; |
|||
int index; |
|||
{ |
|||
TWOnode *pNode; |
|||
TWOelem *pNElem; |
|||
TWOedge *pEdge; |
|||
TWOmaterial *info; |
|||
TWOchannel *newChannel; |
|||
int eIndex, nIndex; |
|||
int direction = index%2; |
|||
double length, area, width, layerWidth; |
|||
double dop, na = 0.0, nd = 0.0; |
|||
|
|||
/* First add fixed charge. */ |
|||
pEdge = pElem->pEdges[index]; |
|||
pEdge->qf += card->BDRYqf; |
|||
|
|||
/* Now add surface recombination. */ |
|||
if ( direction == 0 ) { /* Horizontal Edge */ |
|||
length = 0.5 * pElem->dx; |
|||
} else { |
|||
length = 0.5 * pElem->dy; |
|||
} |
|||
for (nIndex = 0; nIndex <= 1; nIndex++) { |
|||
pNode = pElem->pNodes[ (index+nIndex)%4 ]; |
|||
/* Compute semiconductor area around this node. */ |
|||
area = 0.0; |
|||
for (eIndex = 0; eIndex <= 3; eIndex++) { |
|||
pNElem = pNode->pElems[eIndex]; |
|||
if (pNElem != NIL(TWOelem) && pElem->elemType == SEMICON) { |
|||
area += 0.25 * pElem->dx * pElem->dy; |
|||
} |
|||
} |
|||
if (card->BDRYsnGiven) { |
|||
pNode->tn = pNode->tn / |
|||
(1.0 + ((card->BDRYsn * TNorm)*length*pNode->tn) / area); |
|||
} |
|||
if (card->BDRYspGiven) { |
|||
pNode->tp = pNode->tp / |
|||
(1.0 + ((card->BDRYsp * TNorm)*length*pNode->tp) / area); |
|||
} |
|||
/* Compute doping just in case we need it later. */ |
|||
na += 0.5 * pNode->na; |
|||
nd += 0.5 * pNode->nd; |
|||
} |
|||
|
|||
/* Finally do surface layer. */ |
|||
pNElem = pElem->pElems[index]; |
|||
if (card->BDRYlayerGiven && SurfaceMobility && pElem->elemType == SEMICON |
|||
&& pElem->channel == 0 && pNElem && pNElem->elemType == INSULATOR |
|||
&& pElem->pNodes[index]->nodeType != CONTACT && |
|||
pElem->pNodes[(index+1)%4]->nodeType != CONTACT ) { |
|||
/* Find the layer width. */ |
|||
layerWidth = card->BDRYlayer; |
|||
if (card->BDRYlayer <= 0.0) { /* Need to compute extrinsic Debye length */ |
|||
info = pElem->matlInfo; |
|||
dop = MAX(MAX(na,nd),info->ni0); |
|||
layerWidth = sqrt((Vt * info->eps) / (CHARGE * dop)); |
|||
} |
|||
|
|||
/* Add a channel to the list of channels. */ |
|||
XCALLOC( newChannel, TWOchannel, 1); |
|||
newChannel->pSeed = pElem; |
|||
newChannel->pNElem = pNElem; |
|||
newChannel->type = index; |
|||
if (pDevice->pChannel != NIL(TWOchannel)) { |
|||
newChannel->id = pDevice->pChannel->id + 1; |
|||
newChannel->next = pDevice->pChannel; |
|||
} else { |
|||
newChannel->id = 1; |
|||
newChannel->next = NIL(TWOchannel); |
|||
} |
|||
pDevice->pChannel = newChannel; |
|||
|
|||
/* Now add elements to channel until we're more than layerWidth away |
|||
* from the interface. If we encounter a missing element or an |
|||
* element that's already part of a different channel, quit. |
|||
* The seed element is at the surface. |
|||
*/ |
|||
width = 0.0; |
|||
eIndex = (index+2)%4; |
|||
pElem->surface = TRUE; |
|||
while (width < layerWidth && pElem && pElem->channel == 0) { |
|||
pElem->channel = newChannel->id; |
|||
pElem->direction = direction; |
|||
/* |
|||
* Surface mobility is normally concentration-independent in |
|||
* the default model. Overwrite concentration-dependent value |
|||
* calculated earlier unless matching of low-field surface |
|||
* and bulk mobilities is requested. |
|||
*/ |
|||
if (!MatchingMobility) { |
|||
pElem->mun0 = pElem->matlInfo->mus[ELEC]; |
|||
pElem->mup0 = pElem->matlInfo->mus[HOLE]; |
|||
} |
|||
if ( direction == 0 ) { |
|||
width += pElem->dy; |
|||
} else { |
|||
width += pElem->dx; |
|||
} |
|||
pElem = pElem->pElems[ eIndex ]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Compute boundary condition parameters. */ |
|||
void TWOsetBCparams(TWOdevice *pDevice, BDRYcard *cardList) |
|||
{ |
|||
int index, xIndex, yIndex; /* Need to access in X/Y order. */ |
|||
TWOnode *pNode; |
|||
TWOelem *pElem, *pNElem; |
|||
TWOedge *pEdge; |
|||
BDRYcard *card; |
|||
|
|||
for ( card = cardList; card != NIL(BDRYcard); card = card->BDRYnextCard ) { |
|||
for (xIndex = card->BDRYixLow; xIndex < card->BDRYixHigh; xIndex++) { |
|||
for (yIndex = card->BDRYiyLow; yIndex < card->BDRYiyHigh; yIndex++) { |
|||
pElem = pDevice->elemArray[ xIndex ][ yIndex ]; |
|||
if (pElem != NIL(TWOelem)) { |
|||
if (pElem->domain == card->BDRYdomain) { |
|||
for (index = 0; index <= 3; index++) { |
|||
if (pElem->evalEdges[index]) { |
|||
pNElem = pElem->pElems[index]; |
|||
if (card->BDRYneighborGiven) { |
|||
if (pNElem && pNElem->domain == card->BDRYneighbor) { |
|||
/* Found an interface edge. */ |
|||
TWOcopyBCinfo( pDevice, pElem, card, index ); |
|||
} |
|||
} else { |
|||
if (!pNElem || pNElem->domain != pElem->domain) { |
|||
/* Found a boundary edge. */ |
|||
TWOcopyBCinfo( pDevice, pElem, card, index ); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
void TWOnormalize(TWOdevice *pDevice) |
|||
{ |
|||
int index, eIndex; |
|||
TWOelem *pElem; |
|||
TWOnode *pNode; |
|||
TWOedge *pEdge; |
|||
|
|||
for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) { |
|||
pElem = pDevice->elements[ eIndex ]; |
|||
|
|||
pElem->dx /= LNorm; |
|||
pElem->dy /= LNorm; |
|||
pElem->epsRel /= EpsNorm; |
|||
for ( index = 0; index <= 3; index++ ) { |
|||
if ( pElem->evalNodes[ index ] ) { |
|||
pNode = pElem->pNodes[ index ]; |
|||
pNode->netConc /= NNorm; |
|||
pNode->nd /= NNorm; |
|||
pNode->na /= NNorm; |
|||
pNode->nie /= NNorm; |
|||
pNode->eg /= VNorm; |
|||
pNode->eaff /= VNorm; |
|||
} |
|||
if ( pElem->evalEdges[ index ] ) { |
|||
pEdge = pElem->pEdges[ index ]; |
|||
pEdge->qf /= NNorm*LNorm; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
1197
src/ciderlib/twod/twosolve.c
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
Write
Preview
Loading…
Cancel
Save
Reference in new issue