1 /**********
2 Copyright 1991 Regents of the University of California.  All rights reserved.
3 Author:	1991 David A. Gates, U. C. Berkeley CAD Group
4 Modifed: 2001 Paolo Nenzi
5 **********/
6 
7 #include "ngspice/carddefs.h"
8 #include "ngspice/cidersupt.h"
9 #include "ngspice/cktdefs.h"
10 #include "ngspice/ciderinp.h"
11 #include "ngspice/cpextern.h"
12 #include "ngspice/dopdefs.h"
13 #include "ngspice/meshext.h"
14 #include "ngspice/ngspice.h"
15 #include "ngspice/numenum.h"
16 #include "ngspice/profile.h"
17 #include "ngspice/gendev.h"
18 #include "ngspice/sperror.h"
19 #include "ngspice/suffix.h"
20 
21 
22 /*
23  * Name:	DOPcheck
24  * Purpose:	checks a list of DOPcards for input errors
25  * Formals:	cardList: the list to check
26  * Returns:	OK/E_PRIVATE
27  * Users:	 numerical device setup routines
28  * Calls:	error message handler
29  */
30 int
DOPcheck(DOPcard * cardList,MESHcoord * xMeshList,MESHcoord * yMeshList)31 DOPcheck(DOPcard *cardList, MESHcoord *xMeshList, MESHcoord *yMeshList)
32 {
33   DOPcard *card;
34   int cardNum = 0;
35   int error = OK;
36 
37   for ( card = cardList; card != NULL; card = card->DOPnextCard ) {
38     cardNum++;
39     if (!card->DOPdomainsGiven) {
40       card->DOPnumDomains = 0;
41       card->DOPdomains = NULL;
42     }
43     if (!card->DOPprofileTypeGiven) {
44       SPfrontEnd->IFerrorf( ERR_WARNING, "doping card %d does not specify profile type", cardNum );
45       error = E_PRIVATE;
46     } else switch (card->DOPprofileType) {
47       case DOP_UNIF:
48 	if (!card->DOPconcGiven) {
49 	  SPfrontEnd->IFerrorf( ERR_WARNING, "doping card %d needs conc of uniform distribution", cardNum );
50 	  error = E_PRIVATE;
51 	}
52 	break;
53       case DOP_LINEAR:
54 	if (!card->DOPconcGiven) {
55 	  SPfrontEnd->IFerrorf( ERR_WARNING, "doping card %d needs peak conc of linear distribution", cardNum );
56 	  error = E_PRIVATE;
57 	}
58 	break;
59       case DOP_GAUSS:
60 	if (!card->DOPconcGiven) {
61 	  SPfrontEnd->IFerrorf( ERR_WARNING, "doping card %d needs peak conc of gaussian distribution", cardNum );
62 	  error = E_PRIVATE;
63 	}
64 	break;
65       case DOP_ERFC:
66 	if (!card->DOPconcGiven) {
67 	  SPfrontEnd->IFerrorf( ERR_WARNING, "doping card %d needs peak conc of error-function distribution", cardNum );
68 	  error = E_PRIVATE;
69 	}
70 	break;
71       case DOP_EXP:
72 	if (!card->DOPconcGiven) {
73 	  SPfrontEnd->IFerrorf( ERR_WARNING, "doping card %d needs peak conc of exponential distribution", cardNum );
74 	  error = E_PRIVATE;
75 	}
76 	break;
77       case DOP_SUPREM3:
78       case DOP_SUPASCII:
79 	if (!card->DOPinFileGiven) {
80 	  SPfrontEnd->IFerrorf( ERR_WARNING, "doping card %d needs input-file name of suprem3 data", cardNum );
81 	  error = E_PRIVATE;
82 	}
83 	break;
84       case DOP_ASCII:
85 	if (!card->DOPinFileGiven) {
86 	  SPfrontEnd->IFerrorf( ERR_WARNING, "doping card %d needs input-file name of ascii data", cardNum );
87 	  error = E_PRIVATE;
88 	}
89 	break;
90       default:
91 	SPfrontEnd->IFerrorf( ERR_FATAL, "doping card %d has unrecognized profile type", cardNum );
92 	error = E_NOTFOUND;
93 	break;
94     }
95     if (!card->DOProtateLatGiven) {
96       card->DOProtateLat = FALSE;
97     }
98     if (!card->DOPlatProfileTypeGiven || card->DOProtateLat) {
99       card->DOPlatProfileType = card->DOPprofileType;
100     }
101     if (!card->DOPratioLatGiven) {
102       card->DOPratioLat = 1.0;
103     }
104     if (!card->DOPcharLenGiven) {
105       card->DOPcharLen = 1.0e-4; 	/* 1um in centimeters */
106     }
107     if (!card->DOPlocationGiven) {
108       card->DOPlocation = 0.0;
109     }
110     if (!card->DOPimpurityTypeGiven) {
111       card->DOPimpurityType = IMP_N_TYPE;
112     } else switch (card->DOPimpurityType) {
113       case DOP_BORON:
114 	card->DOPimpurityType = IMP_BORON;
115 	break;
116       case DOP_PHOSP:
117 	card->DOPimpurityType = IMP_PHOSPHORUS;
118 	break;
119       case DOP_ARSEN:
120 	card->DOPimpurityType = IMP_ARSENIC;
121 	break;
122       case DOP_ANTIM:
123 	card->DOPimpurityType = IMP_ANTIMONY;
124 	break;
125       case DOP_N_TYPE:
126 	card->DOPimpurityType = IMP_N_TYPE;
127 	break;
128       case DOP_P_TYPE:
129 	card->DOPimpurityType = IMP_P_TYPE;
130 	break;
131       default:
132 	break;
133     }
134 
135     if (!card->DOPaxisTypeGiven) {
136       if ( xMeshList && yMeshList ) { /* both lists are non-empty */
137 	card->DOPaxisType = DOP_Y_AXIS;
138       } else if ( xMeshList ) { /* x-mesh list is non-empty */
139 	card->DOPaxisType = DOP_X_AXIS;
140       } else if ( yMeshList ) { /* y-mesh list is non-empty */
141 	card->DOPaxisType = DOP_Y_AXIS;
142       }
143     }
144 
145 /* Return now if anything has failed */
146     if (error) return(error);
147   }
148   return(OK);
149 }
150 
151 
152 
153 /*
154  * Name:	DOPsetup
155  * Purpose:	convert a list of DOPcard's to DOPprofile's
156  * Formals:	cardList: list of cards to setup
157  *		profileList: returns the list of DOPprofile's
158  *		xMeshList: list of coordinates in the x mesh
159  *		yMeshList: list of coordinates in the y mesh
160  * Returns:	OK/E_PRIVATE
161  * Users:	 numerical devices
162  * Calls:	DOPcheck
163  */
164 int
DOPsetup(DOPcard * cardList,DOPprofile ** profileList,DOPtable ** tableList,MESHcoord * xMeshList,MESHcoord * yMeshList)165 DOPsetup(DOPcard *cardList, DOPprofile **profileList, DOPtable **tableList,
166          MESHcoord *xMeshList, MESHcoord *yMeshList)
167 {
168   DOPcard *card;
169   DOPprofile *newProfile = NULL, *endProfile;
170   int impurityId = 0;
171   double xMin, xMax, yMin, yMax;
172   double sign;
173   int error, xProfUnif, yProfUnif;
174 
175 /* Initialize list of profiles */
176   *profileList = endProfile = NULL;
177 
178 /* Check the card list */
179   if ((error = DOPcheck( cardList, xMeshList, yMeshList )) != 0) return( error );
180 
181 /* Find the limits on locations */
182   MESHlBounds( xMeshList, &xMin, &xMax );
183   MESHlBounds( yMeshList, &yMin, &yMax );
184 
185   for ( card = cardList; card != NULL; card = card->DOPnextCard ) {
186 
187     if (*profileList == NULL) {
188       RALLOC( newProfile, DOPprofile, 1 );
189       *profileList = newProfile;
190     }
191     else {
192       RALLOC( newProfile->next, DOPprofile, 1 );
193       newProfile = newProfile->next;
194     }
195     newProfile->next = NULL;
196 
197     newProfile->numDomains = card->DOPnumDomains;
198     if ( newProfile->numDomains > 0 ) {
199       int i;
200       RALLOC( newProfile->domains, int, newProfile->numDomains );
201       for ( i=0; i < newProfile->numDomains; i++ ) {
202 	newProfile->domains[i] = card->DOPdomains[i];
203       }
204     }
205     else {
206       newProfile->domains = NULL;
207     }
208 
209     if ( card->DOPimpurityType == IMP_P_TYPE ) {
210       sign = -1.0;
211     }
212     else {
213       sign = 1.0;
214     }
215     switch( card->DOPprofileType ) {
216       case DOP_UNIF:
217 	newProfile->type = UNIF;
218 	newProfile->CONC = sign * card->DOPconc;
219 	break;
220       case DOP_LINEAR:
221 	newProfile->type = LIN;
222 	newProfile->CONC = sign * card->DOPconc;
223 	break;
224       case DOP_GAUSS:
225 	newProfile->type = GAUSS;
226 	newProfile->CONC = sign * card->DOPconc;
227 	break;
228       case DOP_ERFC:
229 	newProfile->type = ERRFC;
230 	newProfile->CONC = sign * card->DOPconc;
231 	break;
232       case DOP_EXP:
233 	newProfile->type = EXP;
234 	newProfile->CONC = sign * card->DOPconc;
235 	break;
236       case DOP_SUPREM3:
237 	newProfile->type = LOOKUP;
238 	if (readSupremData( card->DOPinFile, 0, card->DOPimpurityType,
239             tableList) != 0) {
240         (void) fprintf(cp_err, "Doping setup failed.\n");
241         return -1;
242     }
243 	newProfile->IMPID = ++impurityId;
244 	break;
245       case DOP_SUPASCII:
246 	newProfile->type = LOOKUP;
247     if (readSupremData( card->DOPinFile, 1, card->DOPimpurityType,
248             tableList) != 0) {
249         (void) fprintf(cp_err, "Doping setup failed.\n");
250         return -1;
251     }
252 	newProfile->IMPID = ++impurityId;
253 	break;
254       case DOP_ASCII:
255 	newProfile->type = LOOKUP;
256     if (readAsciiData(card->DOPinFile, card->DOPimpurityType,
257             tableList) != 0) {
258         (void) fprintf(cp_err, "Doping setup failed.\n");
259         return -1;
260     }
261 	newProfile->IMPID = ++impurityId;
262 	break;
263       default:
264 	break;
265     }
266     switch( card->DOPlatProfileType ) {
267       case DOP_UNIF:
268 	newProfile->latType = UNIF;
269 	break;
270       case DOP_LINEAR:
271 	newProfile->latType = LIN;
272 	break;
273       case DOP_GAUSS:
274 	newProfile->latType = GAUSS;
275 	break;
276       case DOP_ERFC:
277 	newProfile->latType = ERRFC;
278 	break;
279       case DOP_EXP:
280 	newProfile->latType = EXP;
281 	break;
282       case DOP_SUPREM3:
283       case DOP_SUPASCII:
284 	newProfile->latType = LOOKUP;
285 	break;
286       case DOP_ASCII:
287 	newProfile->latType = LOOKUP;
288 	break;
289       default:
290 	break;
291     }
292     newProfile->rotate = card->DOProtateLat;
293     newProfile->LOCATION = card->DOPlocation;
294     newProfile->CHAR_LENGTH = card->DOPcharLen;
295     newProfile->LAT_RATIO = card->DOPratioLat;
296 
297     xProfUnif = yProfUnif = FALSE;
298     if (card->DOPaxisType == DOP_X_AXIS) {
299       newProfile->DIRECTION = X;
300       if (newProfile->type == UNIF) xProfUnif = TRUE;
301       if (newProfile->latType == UNIF) yProfUnif = TRUE;
302     }
303     else {
304       newProfile->DIRECTION = Y;
305       if (newProfile->type == UNIF) yProfUnif = TRUE;
306       if (newProfile->latType == UNIF) xProfUnif = TRUE;
307     }
308 
309 /* Fill in x coordinates.  Use defaults if necessary */
310     if (card->DOPxLowGiven && card->DOPxHighGiven) {
311       newProfile->X_LOW = card->DOPxLow;
312       newProfile->X_HIGH = card->DOPxHigh;
313     }
314     else if (card->DOPxLowGiven) {
315       newProfile->X_LOW = card->DOPxLow;
316       if (xProfUnif) {
317 	newProfile->X_HIGH = xMax;
318       }
319       else {
320 	newProfile->X_HIGH = newProfile->X_LOW;
321       }
322     }
323     else if (card->DOPxHighGiven) {
324       newProfile->X_HIGH = card->DOPxHigh;
325       if (xProfUnif) {
326 	newProfile->X_LOW = xMin;
327       }
328       else {
329 	newProfile->X_LOW = newProfile->X_HIGH;
330       }
331     }
332     else {
333       if (xProfUnif) {
334 	newProfile->X_LOW = xMin;
335 	newProfile->X_HIGH = xMax;
336       }
337       else {
338 	newProfile->X_LOW = 0.5 * (xMin + xMax);
339 	newProfile->X_HIGH = 0.5 * (xMin + xMax);
340       }
341     }
342 
343 /* Fill in y coordinates.  Use defaults if necessary */
344     if (card->DOPyLowGiven && card->DOPyHighGiven) {
345       newProfile->Y_LOW = card->DOPyLow;
346       newProfile->Y_HIGH = card->DOPyHigh;
347     }
348     else if (card->DOPyLowGiven) {
349       newProfile->Y_LOW = card->DOPyLow;
350       if (yProfUnif) {
351 	newProfile->Y_HIGH = yMax;
352       }
353       else {
354 	newProfile->Y_HIGH = newProfile->Y_LOW;
355       }
356     }
357     else if (card->DOPyHighGiven) {
358       newProfile->Y_HIGH = card->DOPyHigh;
359       if (xProfUnif) {
360 	newProfile->Y_LOW = yMin;
361       }
362       else {
363 	newProfile->Y_LOW = newProfile->Y_HIGH;
364       }
365     }
366     else {
367       if (yProfUnif) {
368 	newProfile->Y_LOW = yMin;
369 	newProfile->Y_HIGH = yMax;
370       }
371       else {
372 	newProfile->Y_LOW = 0.5 * (yMin + yMax);
373 	newProfile->Y_HIGH = 0.5 * (yMin + yMax);
374       }
375     }
376   }
377   return( OK );
378 }
379