1 /**********
2 Copyright 1992 Regents of the University of California. All rights reserved.
3 Author: 1987 Kartikeya Mayaram, U. C. Berkeley CAD Group
4 **********/
5
6 #include "ngspice/ngspice.h"
7 #include "ngspice/numglobs.h"
8 #include "ngspice/numenum.h"
9 #include "ngspice/onemesh.h"
10 #include "ngspice/onedev.h"
11 #include "ngspice/macros.h"
12 #include "onedext.h"
13 #include "oneddefs.h"
14
15
16 /* Forward Declarations */
17 /* static void ONEresetEvalFlag(ONEdevice *); sjb - not used */
18
19 void
ONEbuildMesh(ONEdevice * pDevice,ONEcoord * pCoord,ONEdomain * pDomain,ONEmaterial * pMaterial)20 ONEbuildMesh(ONEdevice *pDevice, ONEcoord *pCoord, ONEdomain *pDomain,
21 ONEmaterial *pMaterial)
22 {
23 int index, i;
24 int elemType;
25 double xPos;
26 ONEcoord *pC;
27 ONEnode *pNode;
28 ONEdomain *pD;
29 ONEelem *pElem;
30 ONEmaterial *pM;
31 int poiEqn, numEqn;
32 ONEedge *pEdge;
33 ONEnode **nodeArray=NULL;
34 BOOLEAN error = FALSE;
35
36
37 /* generate the work array for setting up nodes and elements */
38 XCALLOC(nodeArray, ONEnode *, 1 + pDevice->numNodes);
39
40 for (pC = pCoord; pC != NULL; pC = pC->next) {
41 xPos = pC->location;
42 XCALLOC(pNode, ONEnode, 1);
43 pNode->x = xPos;
44 pNode->nodeI = pC->number;
45 nodeArray[pNode->nodeI] = pNode;
46 }
47
48 /* mark the domain info on the nodes */
49 if (pDomain == NULL) {
50 fprintf(stderr, "Error: domains not defined for device\n");
51 exit(-1);
52 }
53 for (pD = pDomain; pD != NULL; pD = pD->next) {
54 for (pM = pMaterial; pM != NULL; pM = pM->next) {
55 if (pD->material == pM->id) {
56 break;
57 }
58 }
59 elemType = pM->type;
60 for (index = pD->ixLo; index <= pD->ixHi; index++) {
61 pNode = nodeArray[index];
62 if (!pNode->nodeType) {
63 pNode->nodeType = elemType;
64 }
65 }
66 }
67
68 /*
69 * check to see if a domain has been defined for all nodes. if not flag an
70 * error message
71 */
72 for (index = 2; index < pDevice->numNodes; index++) {
73 pNode = nodeArray[index];
74 if (!pNode->nodeType) {
75 printf("Error: No domain defined for node %d\n", pNode->nodeI);
76 error = TRUE;
77 }
78 }
79 if (error) {
80 /* nodes with undefined domains -- exit */
81 exit(-1);
82 }
83 /* mark the first and last nodes to be contact nodes */
84 nodeArray[1]->nodeType = CONTACT;
85 nodeArray[pDevice->numNodes]->nodeType = CONTACT;
86
87
88 /* generate the elements and the edges */
89 for (index = 1; index < pDevice->numNodes; index++) {
90 XCALLOC(pElem, ONEelem, 1);
91 XCALLOC(pEdge, ONEedge, 1);
92 pElem->pEdge = pEdge;
93 pElem->pLeftNode = nodeArray[index];
94 pElem->pRightNode = nodeArray[index + 1];
95 pDevice->elemArray[index] = pElem;
96 }
97
98 /* now setup the nodes to which an element belongs */
99 for (index = 1; index < pDevice->numNodes; index++) {
100 pElem = pDevice->elemArray[index];
101 pElem->pLeftNode->pRightElem = pElem;
102 pElem->pRightNode->pLeftElem = pElem;
103 if (index > 1) {
104 pElem->pLeftElem = pDevice->elemArray[index - 1];
105 }
106 if (index < pDevice->numNodes - 1) {
107 pElem->pRightElem = pDevice->elemArray[index + 1];
108 }
109 }
110
111 /* mark the domain info on the elements */
112 for (pD = pDomain; pD != NULL; pD = pD->next) {
113 for (pM = pMaterial; pM != NULL; pM = pM->next) {
114 if (pD->material == pM->id) {
115 break;
116 }
117 }
118 elemType = pM->type;
119 for (index = pD->ixLo; index < pD->ixHi; index++) {
120 pElem = pDevice->elemArray[index];
121 pElem->domain = pD->id;
122 pElem->elemType = elemType;
123 pElem->matlInfo = pM;
124 }
125 }
126
127 /* identify the interface nodes */
128 for (index = 2; index < pDevice->numNodes; index++) {
129 pNode = nodeArray[index];
130 if (pNode->pLeftElem->elemType !=
131 pNode->pRightElem->elemType) {
132 /* an interface node */
133 pNode->nodeType = INTERFACE;
134 }
135 }
136
137 /* now mark the nodes to be evaluated */
138 /* all interface nodes marked in silicon elements */
139
140 for (index = 1; index < pDevice->numNodes; index++) {
141 pElem = pDevice->elemArray[index];
142 pElem->dx = pElem->pRightNode->x - pElem->pLeftNode->x;
143 for (i = 0; i <= 1; i++) {
144 pNode = pElem->pNodes[i];
145 pElem->evalNodes[i] = FALSE;
146 if (pElem->elemType == INSULATOR) {
147 if ((!pNode->evaluated) &&
148 (pNode->nodeType != INTERFACE)) {
149 /* a non interface node in oxide domain */
150 pNode->evaluated = TRUE;
151 pElem->evalNodes[i] = TRUE;
152 }
153 }
154 if (pElem->elemType == SEMICON) {
155 if (!pNode->evaluated) {
156 pNode->evaluated = TRUE;
157 pElem->evalNodes[i] = TRUE;
158 }
159 }
160 }
161 }
162
163 /* set up the equation number for the nodes */
164 poiEqn = numEqn = 1;
165 for (index = 1; index < pDevice->numNodes; index++) {
166 pElem = pDevice->elemArray[index];
167 for (i = 0; i <= 1; i++) {
168 if (pElem->evalNodes[i]) {
169 pNode = pElem->pNodes[i];
170 if (pNode->nodeType != CONTACT) {
171 pNode->poiEqn = poiEqn++;
172
173 pNode->psiEqn = numEqn;
174 if (pElem->elemType == INSULATOR) {
175 numEqn += 1; /* only poisson's equation */
176 } else {
177 pNode->nEqn = numEqn + 1;
178 pNode->pEqn = numEqn + 2;
179 numEqn += 3;
180 }
181 } else { /* this is a contact node */
182 pNode->poiEqn = 0;
183 pNode->psiEqn = 0;
184 pNode->nEqn = 0;
185 pNode->pEqn = 0;
186 }
187 /*
188 * fprintf(stdout,"NODE: %d %d\n",pNode->nodeI,pNode->poiEqn);
189 */
190 }
191 }
192 }
193 pDevice->dimEquil = poiEqn;
194 pDevice->dimBias = numEqn;
195
196 /*
197 * ONEprnMesh( pDevice );
198 */
199 }
200
201 /*
202 * We have a separate function for this, so that the setup routines can reset
203 * the state pointers without rebuilding the entire mesh.
204 */
205 void
ONEgetStatePointers(ONEdevice * pDevice,int * numStates)206 ONEgetStatePointers(ONEdevice *pDevice, int *numStates)
207 {
208 int eIndex, nIndex;
209 ONEelem *pElem;
210 ONEnode *pNode;
211
212
213 for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) {
214 pElem = pDevice->elemArray[eIndex];
215 for (nIndex = 0; nIndex <= 1; nIndex++) {
216 if (pElem->evalNodes[nIndex]) {
217 pNode = pElem->pNodes[nIndex];
218 pNode->nodeState = *numStates;
219 *numStates += ONEnumNodeStates;
220 }
221 }
222 pElem->pEdge->edgeState = *numStates;
223 *numStates += ONEnumEdgeStates;
224 }
225 }
226
227 /* adjust the base contact to be the position of maximum density */
228 /* index EB and BC are the indexes for the eb, bc junctions */
229 void
adjustBaseContact(ONEdevice * pDevice,int indexEB,int indexBC)230 adjustBaseContact(ONEdevice *pDevice, int indexEB, int indexBC)
231 {
232 int index, i, newBaseIndex, midPoint;
233 double maxDensity;
234 ONEnode *pNode;
235 ONEelem *pElem;
236 ONEnode *pBaseNode = pDevice->elemArray[pDevice->baseIndex]->pNodes[0];
237
238
239 /* Initialize the base contact to be the center of the two junctions */
240 /* This should take care of uniform dopings. */
241
242 midPoint = (indexEB + indexBC) / 2;
243 newBaseIndex = midPoint;
244
245 if (pBaseNode->baseType == P_TYPE) {
246 maxDensity = pDevice->elemArray[midPoint]->pNodes[0]->pConc;
247 for (index = indexEB; index < indexBC; index++) {
248 pElem = pDevice->elemArray[index];
249 for (i = 0; i <= 1; i++) {
250 pNode = pElem->pNodes[i];
251 if (pNode->pConc > maxDensity) {
252 maxDensity = pNode->pConc;
253 newBaseIndex = index;
254 }
255 }
256 }
257 } else if (pBaseNode->baseType == N_TYPE) {
258 maxDensity = pDevice->elemArray[midPoint]->pNodes[0]->nConc;
259 for (index = indexEB; index < indexBC; index++) {
260 pElem = pDevice->elemArray[index];
261 for (i = 0; i <= 1; i++) {
262 pNode = pElem->pNodes[i];
263 if (pNode->nConc > maxDensity) {
264 maxDensity = pNode->nConc;
265 newBaseIndex = index;
266 }
267 }
268 }
269 } else {
270 printf("adjustBaseContact: unknown base type %d\n", pBaseNode->baseType);
271 }
272 /* at the conclusion of this loop have the point of max density */
273 if (pDevice->baseIndex != newBaseIndex) {
274 /* so change the position */
275 pNode = pDevice->elemArray[newBaseIndex]->pNodes[0];
276 pNode->baseType = pBaseNode->baseType;
277 pNode->vbe = pBaseNode->vbe;
278 pBaseNode->baseType = FALSE;
279 pBaseNode->vbe = 0.0;
280 pDevice->baseIndex = newBaseIndex;
281 }
282 }
283
284 void
NBJTjunctions(ONEdevice * pDevice,int * indexEB,int * indexBC)285 NBJTjunctions(ONEdevice *pDevice, int *indexEB, int *indexBC)
286 {
287 int index;
288 double conc1, conc2;
289 BOOLEAN findFirstJunction = TRUE;
290 BOOLEAN notFound = TRUE;
291
292 for (index = 1; (index < pDevice->numNodes) && (notFound); index++) {
293 conc1 = pDevice->elemArray[index]->pNodes[0]->netConc;
294 conc2 = pDevice->elemArray[index]->pNodes[1]->netConc;
295
296 if ((conc1 * conc2 < 0.0) && (findFirstJunction)) {
297 *indexEB = index;
298 findFirstJunction = FALSE;
299 } else if ((conc1 * conc2 < 0.0) && (!findFirstJunction)) {
300 *indexBC = index;
301 notFound = FALSE;
302 }
303 }
304
305 if (notFound) {
306 fprintf(stderr, "BJT: Device does not have two junctions!\n");
307 exit(-1);
308 }
309 }
310
311 void
ONEprnMesh(ONEdevice * pDevice)312 ONEprnMesh(ONEdevice *pDevice)
313 {
314 int eIndex, index;
315 ONEelem *pElem;
316 ONEnode *pNode;
317 char *name;
318
319
320 for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) {
321 pElem = pDevice->elemArray[eIndex];
322 fprintf(stderr, "elem %5d:\n", eIndex);
323 for (index = 0; index <= 1; index++) {
324 if (pElem->evalNodes[index]) {
325 pNode = pElem->pNodes[index];
326 switch (pNode->nodeType) {
327 case SEMICON:
328 name = "semiconductor";
329 break;
330 case INSULATOR:
331 name = "insulator";
332 break;
333 case CONTACT:
334 name = "contact";
335 break;
336 case SCHOTTKY:
337 name = "schottky";
338 break;
339 case INTERFACE:
340 name = "interface";
341 break;
342 default:
343 name = "unknown";
344 break;
345 }
346
347
348 fprintf(stderr, "node %5d: %s %5d\n", index, name,
349 pNode->nodeI);
350
351 }
352 }
353 }
354 }
355
356 /* sjb - not used
357 static void
358 ONEresetEvalFlag(ONEdevice *pDevice)
359 {
360 int index, eIndex;
361 ONEelem *pElem;
362
363
364 for (eIndex = 1; eIndex < pDevice->numNodes; eIndex++) {
365 pElem = pDevice->elemArray[eIndex];
366 for (index = 0; index <= 1; index++) {
367 pElem->pNodes[index]->evaluated = FALSE;
368 }
369 pElem->pEdge->evaluated = FALSE;
370 }
371 }
372 */
373