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