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