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 #include "ngspice/ngspice.h"
8 #include "ngspice/numglobs.h"
9 #include "ngspice/numenum.h"
10 #include "ngspice/twomesh.h"
11 #include "ngspice/twodev.h"
12 #include "ngspice/profile.h"
13 #include "ngspice/macros.h"
14 #include "ngspice/bool.h"
15 #include "twoddefs.h"
16 #include "twodext.h"
17 #include "ngspice/cidersupt.h"
18
19 /* functions in this file are used to calculate the conc */
20
21 double
TWOdopingValue(DOPprofile * pProfile,DOPtable * pTable,double x,double y)22 TWOdopingValue(DOPprofile *pProfile, DOPtable *pTable, double x,
23 double y)
24 {
25 double argX, argY, argP, argL, value = 0.0;
26
27 /* Find the appropriate lookup table if necessary */
28 if (pProfile->type == LOOKUP) {
29 while ( pTable != NULL ) {
30 if (pTable->impId == pProfile->IMPID) {
31 /* Found it */
32 break;
33 } else {
34 pTable = pTable->next;
35 }
36 }
37 if ( pTable == NULL ) {
38 fprintf( stderr, "Error: unknown impurity profile %d\n",
39 ((int)pProfile->IMPID) );
40 controlled_exit(1);
41 }
42 }
43 /* Find distances */
44 if ( pProfile->Y_LOW > y ) {
45 argY = pProfile->Y_LOW - y;
46 } else if ( y > pProfile->Y_HIGH ) {
47 argY = y - pProfile->Y_HIGH;
48 } else {
49 argY = 0.0;
50 }
51 if ( pProfile->X_LOW > x ) {
52 argX = pProfile->X_LOW - x;
53 } else if ( x > pProfile->X_HIGH ) {
54 argX = x - pProfile->X_HIGH;
55 } else {
56 argX = 0.0;
57 }
58
59 if ( pProfile->DIRECTION == Y ) {
60 argP = argY;
61 argL = argX / pProfile->LAT_RATIO;
62 }
63 else {
64 argP = argX;
65 argL = argY / pProfile->LAT_RATIO;
66 }
67 if ( pProfile->rotate ) {
68 argP = hypot(argP, argL);
69 argL = 0.0;
70 }
71
72 /* Transform to coordinates of profile peak */
73 argP -= pProfile->LOCATION;
74 argP /= pProfile->CHAR_LENGTH;
75 argL -= pProfile->LOCATION;
76 argL /= pProfile->CHAR_LENGTH;
77
78 switch (pProfile->type) {
79 case UNIF:
80 if (argP > 0.0) {
81 value = 0.0;
82 } else {
83 value = pProfile->CONC;
84 }
85 break;
86 case LIN:
87 argP = ABS(argP);
88 if (argP > 1.0) {
89 value = 0.0;
90 } else {
91 value = pProfile->CONC * ( 1.0 - argP );
92 }
93 break;
94 case GAUSS:
95 argP *= argP;
96 if ( argP > 80.0 ) {
97 value = 0.0;
98 } else {
99 value = pProfile->PEAK_CONC * exp( -argP );
100 }
101 break;
102 case EXP:
103 argP = ABS(argP);
104 if ( argP > 80.0 ) {
105 value = 0.0;
106 } else {
107 value = pProfile->PEAK_CONC * exp( -argP );
108 }
109 break;
110 case ERRFC:
111 argP = ABS(argP);
112 if ( argP > 10.0 ) {
113 value = 0.0;
114 } else {
115 value = pProfile->PEAK_CONC * erfc( argP );
116 }
117 break;
118 case LOOKUP:
119 argP = ABS(argP);
120 value = lookup( pTable->dopData, argP );
121 break;
122 default:
123 break;
124 }
125 if (!pProfile->rotate) { /* Tensor product in lateral direction */
126 switch (pProfile->latType) {
127 case UNIF:
128 if (argL > 0.0) {
129 value = 0.0;
130 }
131 break;
132 case LIN:
133 argL = ABS(argL);
134 if (argL > 1.0) {
135 value = 0.0;
136 } else {
137 value *= ( 1.0 - argL );
138 }
139 break;
140 case GAUSS:
141 argL *= argL;
142 if ( argL > 80.0 ) {
143 value = 0.0;
144 } else {
145 value *= exp( -argL );
146 }
147 break;
148 case EXP:
149 argL = ABS(argL);
150 if ( argL > 80.0 ) {
151 value = 0.0;
152 } else {
153 value *= exp( -argL );
154 }
155 break;
156 case ERRFC:
157 argL = ABS(argL);
158 if ( argP > 10.0 ) {
159 value = 0.0;
160 } else {
161 value *= erfc( argL );
162 }
163 break;
164 case LOOKUP:
165 argL = ABS(argL);
166 value *= lookup( pTable->dopData, argL ) / lookup( pTable->dopData, 0.0 );
167 break;
168 default:
169 break;
170 }
171 } /* end: not rotated */
172 return( value );
173 }
174
175 void
TWOsetDoping(TWOdevice * pDevice,DOPprofile * pProfile,DOPtable * pTable)176 TWOsetDoping(TWOdevice *pDevice, DOPprofile *pProfile, DOPtable *pTable)
177 {
178 TWOnode *pNode;
179 TWOelem *pElem;
180 DOPprofile *pP;
181 double conc;
182 int index, eIndex;
183 BOOLEAN dopeMe;
184
185 /* Clear doping info for all nodes. */
186 for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
187 pElem = pDevice->elements[ eIndex ];
188 for (index = 0; index <= 3; index++) {
189 if (pElem->evalNodes[index]) {
190 pNode = pElem->pNodes[index];
191 pNode->na = 0.0;
192 pNode->nd = 0.0;
193 pNode->netConc = 0.0;
194 pNode->totalConc = 0.0;
195 }
196 }
197 }
198 /* Now compute the contribution to the total doping from each profile. */
199 for ( pP = pProfile; pP != NULL; pP = pP->next ) {
200 for ( eIndex = 1; eIndex <= pDevice->numElems; eIndex++ ) {
201 pElem = pDevice->elements[ eIndex ];
202 if ( pElem->elemType == SEMICON ) {
203 if ( pP->numDomains > 0 ) {
204 dopeMe = FALSE;
205 for ( index = 0; index < pP->numDomains; index++ ) {
206 if ( pElem->domain == pP->domains[ index ] ) {
207 dopeMe = TRUE;
208 break;
209 }
210 }
211 } else { /* domains not given, so dope all */
212 dopeMe = TRUE;
213 }
214 if ( dopeMe ) {
215 for ( index = 0; index <= 3; index++ ) {
216 if ( pElem->evalNodes[ index ] ) {
217 pNode = pElem->pNodes[ index ];
218 conc = TWOdopingValue( pP, pTable,
219 pDevice->xScale[ pNode->nodeI ],
220 pDevice->yScale[ pNode->nodeJ ] );
221 pNode->netConc += conc;
222 if ( conc < 0.0 ) {
223 pNode->totalConc -= conc;
224 pNode->na -= conc;
225 }
226 else {
227 pNode->totalConc += conc;
228 pNode->nd += conc;
229 }
230 }
231 }
232 }
233 }
234 }
235 }
236 }
237