1 /**********
2 Copyright 1991 Regents of the University of California.  All rights reserved.
3 Author:	1987 Kartikeya Mayaram, U. C. Berkeley CAD Group
4 Author:	1991 David A. Gates, U. C. Berkeley CAD Group
5 **********/
6 
7 /**********
8 Two-Dimensional Numerical Device Setup Routines
9 **********/
10 
11 #include "ngspice/ngspice.h"
12 #include "ngspice/numglobs.h"
13 #include "ngspice/numconst.h"
14 #include "ngspice/numenum.h"
15 #include "ngspice/twomesh.h"
16 #include "ngspice/twodev.h"
17 #include "ngspice/carddefs.h"		/* XXX Not really modular if we need this. */
18 #include "twoddefs.h"
19 #include "twodext.h"
20 #include "ngspice/cidersupt.h"
21 
22 
23 /* compute node parameters */
TWOsetup(TWOdevice * pDevice)24 void TWOsetup(TWOdevice *pDevice)
25 {
26   double temp1, deltaEg, avgConc, totalConc, absNetConc;
27   double ncv0, dBand, dNie, psiBand[4];
28   double *xScale = pDevice->xScale;
29   double *yScale = pDevice->yScale;
30   int index, eIndex;
31   int numContactNodes;
32   TWOnode *pNode;
33   TWOelem *pElem;
34   TWOedge *pEdge;
35   TWOcontact *pC;
36   TWOmaterial *info;
37 
38   for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
39     pElem = pDevice->elements[ eIndex ];
40     info = pElem->matlInfo;
41 
42     pElem->dx = xScale[ pElem->pTRNode->nodeI ]-xScale[ pElem->pTLNode->nodeI ];
43     pElem->dy = yScale[ pElem->pBLNode->nodeJ ]-yScale[ pElem->pTLNode->nodeJ ];
44     pElem->epsRel = info->eps;
45 
46     if ( pElem->elemType == INSULATOR ) {
47       for ( index = 0; index <= 3; index++ ) {
48 	if ( pElem->evalNodes[ index ] ) {
49 	  pNode = pElem->pNodes[ index ];
50 	  if (pNode->nodeType == CONTACT) {
51 	    pNode->eaff = PHI_METAL;
52 	    pNode->eg = 0.0;
53 	  } else {
54 	    pNode->eaff = info->affin;
55 	    pNode->eg = info->eg0;
56 	  }
57 	}
58       }
59     } else if ( pElem->elemType == SEMICON ) {
60       ncv0 = sqrt( info->nc0 ) * sqrt( info->nv0 );
61       for ( index = 0; index <= 3; index++ ) {
62 	if ( pElem->evalNodes[ index ] ) {
63 	  pNode = pElem->pNodes[ index ];
64 
65           /* Narrowing of Energy-Band Gap */
66 	  if (BandGapNarrowing) {
67 	    absNetConc = ABS( pNode->netConc );
68 	    if ( pNode->netConc > 0.0 ) {
69 	      temp1 = log( absNetConc / info->nrefBGN[ELEC] );
70 	      deltaEg = - info->dEgDn[ELEC] * (temp1 + sqrt(temp1*temp1 + 0.5));
71 	      pNode->eg = info->eg0 + deltaEg;
72 	    } else if ( pNode->netConc < 0.0 ) {
73 	      temp1 = log( absNetConc / info->nrefBGN[HOLE] );
74 	      deltaEg = - info->dEgDn[HOLE] * (temp1 + sqrt(temp1*temp1 + 0.5));
75 	      pNode->eg = info->eg0 + deltaEg;
76 	    } else {
77 	      pNode->eg = info->eg0;
78 	    }
79 	  } else {
80 	    pNode->eg = info->eg0;
81 	  }
82 	  pNode->nie = ncv0 * exp ( -0.5 * pNode->eg / Vt );
83 	  pNode->eaff = info->affin;
84 	  /* Save band structure parameter. */
85 	  psiBand[ index ] = - info->refPsi;
86 
87 	  /* Ionized-Impurity-Scattering Reduction of Carrier Lifetime */
88 	  if (ConcDepLifetime) {
89 	    totalConc = pNode->totalConc;
90 	    temp1 = 1.0 / ( 1.0 + totalConc / info->nrefSRH[ELEC] );
91 	    pNode->tn = info->tau0[ELEC] * temp1;
92 	    temp1 = 1.0 / ( 1.0 + totalConc / info->nrefSRH[HOLE] );
93 	    pNode->tp = info->tau0[HOLE] * temp1;
94 	  } else {
95 	    pNode->tn = info->tau0[ELEC];
96 	    pNode->tp = info->tau0[HOLE];
97 	  }
98 	}
99       }
100 
101       for ( index = 0; index <= 3; index++ ) {
102 	if ( pElem->evalEdges[ index ] ) {
103 	  pEdge = pElem->pEdges[ index ];
104 
105 	  /* Fixed Interface Charge */
106 	  pEdge->qf = 0.0;
107 
108           /* Variable Band Built-In Potential */
109 	  if ( index <= 1 ) {
110 	    dBand = psiBand[index+1] - psiBand[index];
111 	    dNie = log( pElem->pNodes[index+1]->nie /
112 	                pElem->pNodes[index]->nie );
113 	    pEdge->dCBand = dBand + dNie;
114 	    pEdge->dVBand = - dBand + dNie;
115 	  } else {
116 	    dBand = psiBand[index] - psiBand[(index+1)%4];
117 	    dNie = log( pElem->pNodes[index]->nie /
118 	                pElem->pNodes[(index+1)%4]->nie );
119 	    pEdge->dCBand = dBand + dNie;
120 	    pEdge->dVBand = - dBand + dNie;
121 	  }
122 	}
123       }
124 
125       /* Evaluate conc.-dep. mobility. */
126       /* Average conc of all four nodes. */
127       avgConc = 0.25*(pElem->pTLNode->totalConc + pElem->pTRNode->totalConc +
128 		      pElem->pBRNode->totalConc + pElem->pBLNode->totalConc);
129       MOBconcDep( info, avgConc, &pElem->mun0, &pElem->mup0 );
130     }
131   }
132 
133   for ( pC = pDevice->pFirstContact; pC != NULL; pC = pC->next ) {
134     numContactNodes = pC->numNodes;
135     for ( index = 0; index < numContactNodes; index++ ) {
136       pNode = pC->pNodes[ index ];
137       pNode->eaff = pC->workf;		/* Affinity aka work function */
138     }
139   }
140 }
141 
142 /* Transfer BC info from card to nodes and edges. */
143 static void
TWOcopyBCinfo(TWOdevice * pDevice,TWOelem * pElem,BDRYcard * card,int index)144 TWOcopyBCinfo(TWOdevice *pDevice, TWOelem *pElem, BDRYcard *card, int index )
145 {
146   TWOnode *pNode;
147   TWOelem *pNElem;
148   TWOedge *pEdge;
149   TWOmaterial *info;
150   TWOchannel *newChannel;
151   int eIndex, nIndex;
152   int direction = index%2;
153   double length, area, width, layerWidth;
154   double dop, na = 0.0, nd = 0.0;
155 
156   /* First add fixed charge. */
157   pEdge = pElem->pEdges[index];
158   pEdge->qf += card->BDRYqf;
159 
160   /* Now add surface recombination. */
161   if ( direction == 0 ) {	/* Horizontal Edge */
162     length = 0.5 * pElem->dx;
163   } else {
164     length = 0.5 * pElem->dy;
165   }
166   for (nIndex = 0; nIndex <= 1; nIndex++) {
167     pNode = pElem->pNodes[ (index+nIndex)%4 ];
168     /* Compute semiconductor area around this node. */
169     area = 0.0;
170     for (eIndex = 0; eIndex <= 3; eIndex++) {
171       pNElem = pNode->pElems[eIndex];
172       if (pNElem != NULL && pElem->elemType == SEMICON) {
173 	area += 0.25 * pElem->dx * pElem->dy;
174       }
175     }
176     if (card->BDRYsnGiven) {
177       pNode->tn = pNode->tn /
178 	(1.0 + ((card->BDRYsn * TNorm)*length*pNode->tn) / area);
179     }
180     if (card->BDRYspGiven) {
181       pNode->tp = pNode->tp /
182 	(1.0 + ((card->BDRYsp * TNorm)*length*pNode->tp) / area);
183     }
184     /* Compute doping just in case we need it later. */
185     na += 0.5 * pNode->na;
186     nd += 0.5 * pNode->nd;
187   }
188 
189   /* Finally do surface layer. */
190   pNElem = pElem->pElems[index];
191   if (card->BDRYlayerGiven && SurfaceMobility && pElem->elemType == SEMICON
192       && pElem->channel == 0 && pNElem && pNElem->elemType == INSULATOR
193       && pElem->pNodes[index]->nodeType != CONTACT &&
194       pElem->pNodes[(index+1)%4]->nodeType != CONTACT ) {
195     /* Find the layer width. */
196     layerWidth = card->BDRYlayer;
197     if (card->BDRYlayer <= 0.0) {  /* Need to compute extrinsic Debye length */
198       info = pElem->matlInfo;
199       dop = MAX(MAX(na,nd),info->ni0);
200       layerWidth = sqrt((Vt * info->eps) / (CHARGE * dop));
201     }
202 
203     /* Add a channel to the list of channels. */
204     XCALLOC( newChannel, TWOchannel, 1);
205     newChannel->pSeed = pElem;
206     newChannel->pNElem = pNElem;
207     newChannel->type = index;
208     if (pDevice->pChannel != NULL) {
209       newChannel->id = pDevice->pChannel->id + 1;
210       newChannel->next = pDevice->pChannel;
211     } else {
212       newChannel->id = 1;
213       newChannel->next = NULL;
214     }
215     pDevice->pChannel = newChannel;
216 
217     /* Now add elements to channel until we're more than layerWidth away
218      * from the interface.  If we encounter a missing element or an
219      * element that's already part of a different channel, quit.
220      * The seed element is at the surface.
221      */
222     width = 0.0;
223     eIndex = (index+2)%4;
224     pElem->surface = TRUE;
225     while (width < layerWidth && pElem && pElem->channel == 0) {
226       pElem->channel = newChannel->id;
227       pElem->direction = direction;
228       /*
229        * Surface mobility is normally concentration-independent in
230        * the default model. Overwrite concentration-dependent value
231        * calculated earlier unless matching of low-field surface
232        * and bulk mobilities is requested.
233        */
234       if (!MatchingMobility) {
235 	pElem->mun0 = pElem->matlInfo->mus[ELEC];
236 	pElem->mup0 = pElem->matlInfo->mus[HOLE];
237       }
238       if ( direction == 0 ) {
239 	width += pElem->dy;
240       } else {
241 	width += pElem->dx;
242       }
243       pElem = pElem->pElems[ eIndex ];
244     }
245   }
246 }
247 
248 /* Compute boundary condition parameters. */
TWOsetBCparams(TWOdevice * pDevice,BDRYcard * cardList)249 void TWOsetBCparams(TWOdevice *pDevice, BDRYcard *cardList)
250 {
251   int index, xIndex, yIndex;		/* Need to access in X/Y order. */
252   TWOelem *pElem, *pNElem;
253   BDRYcard *card;
254 
255   for ( card = cardList; card != NULL; card = card->BDRYnextCard ) {
256     for (xIndex = card->BDRYixLow; xIndex < card->BDRYixHigh; xIndex++) {
257       for (yIndex = card->BDRYiyLow; yIndex < card->BDRYiyHigh; yIndex++) {
258 	pElem = pDevice->elemArray[ xIndex ][ yIndex ];
259 	if (pElem != NULL) {
260 	  if (pElem->domain == card->BDRYdomain) {
261 	    for (index = 0; index <= 3; index++) {
262 	      if (pElem->evalEdges[index]) {
263 		pNElem = pElem->pElems[index];
264 		if (card->BDRYneighborGiven) {
265 		  if (pNElem && pNElem->domain == card->BDRYneighbor) {
266 		    /* Found an interface edge. */
267 		    TWOcopyBCinfo( pDevice, pElem, card, index );
268 		  }
269 		} else {
270 		  if (!pNElem || pNElem->domain != pElem->domain) {
271 		    /* Found a boundary edge. */
272 		    TWOcopyBCinfo( pDevice, pElem, card, index );
273 		  }
274 		}
275 	      }
276 	    }
277 	  }
278 	}
279       }
280     }
281   }
282 }
283 
TWOnormalize(TWOdevice * pDevice)284 void TWOnormalize(TWOdevice *pDevice)
285 {
286   int index, eIndex;
287   TWOelem *pElem;
288   TWOnode *pNode;
289   TWOedge *pEdge;
290 
291   for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
292     pElem = pDevice->elements[ eIndex ];
293 
294     pElem->dx /= LNorm;
295     pElem->dy /= LNorm;
296     pElem->epsRel /= EpsNorm;
297     for ( index = 0; index <= 3; index++ ) {
298       if ( pElem->evalNodes[ index ] ) {
299 	pNode = pElem->pNodes[ index ];
300 	pNode->netConc /= NNorm;
301 	pNode->nd /= NNorm;
302 	pNode->na /= NNorm;
303 	pNode->nie /= NNorm;
304 	pNode->eg /= VNorm;
305 	pNode->eaff /= VNorm;
306       }
307       if ( pElem->evalEdges[ index ] ) {
308 	pEdge = pElem->pEdges[ index ];
309 	pEdge->qf /= NNorm*LNorm;
310       }
311     }
312   }
313 }
314