1 /* -*- tab-width: 4 -*-
2  *
3  * Electric(tm) VLSI Design System
4  *
5  * File: simspice.cpp
6  * SPICE list generator: write a SPICE format file for the current cell
7  * Written by: Steven M. Rubin, Static Free Software
8  * Improved by: Sid Penstone, Queen's University
9  *
10  * Copyright (c) 2000 Static Free Software.
11  *
12  * Electric(tm) is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * Electric(tm) is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with Electric(tm); see the file COPYING.  If not, write to
24  * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
25  * Boston, Mass 02111-1307, USA.
26  *
27  * Static Free Software
28  * 4119 Alpine Road
29  * Portola Valley, California 94028
30  * info@staticfreesoft.com
31  */
32 
33 /*
34  * Modified to get areas correctly by Oct.6/87 S.R.Penstone, Queen's U.
35  * Pools diffusion areas (see notes below)
36  * Revision Nov.12-- fixed error that applied mask scale factor twice
37  * Revised Dec.2/87 SRP - separate out the diffusion and transistor types
38  * Revision Dec.30/87, to leave out the poly gate on a transistor
39  * Revision Mar.29/89 to declare atof() as double (SRP)
40  * Revised June 6/89 to ask for cell name during parse-output operation (QU)
41  * Revised June 6/89 to no longer look for X in 1st column of spice output (QU)
42  * Revised Aug 31/89 to verify cell name during parse-output operation (QU)
43  * Revised Aug 31/89 to look for layout view (QU)
44  * Revised Nov 27/89 merged with version in Electric 4.04 (QU)
45  * Revised Nov 28/89 merged with version from Chris Schneider at U. of Manitoba (QU)
46  * Revised Mar 89 to support the use of node names, external model files,
47  *    remote execution (UNIX), .NODESET and special sources, meters (fixed
48  *    bug), varargs (UNIX), interactive use with spice2 spice3 or hspice
49  *    simulators... by L. Swab (QU).  Some code by C. Schneider.
50  * Revised June 7/90 to check for prototype ground net when exporting
51  * subcircuit ports; if it is ground, do not export it. (SRP) This will
52  * be looked at again.
53  * Revised Dec.7/90 to start handling ARRAY types
54  * MAJOR CHANGES Dec/90 SRP:
55  * prepare to split source files into two files: simspice.c and simspicerun.c to
56  * separate execution from writing
57  * Create separate function to write 2 port types, so it can be called
58  * Do not write  two terminal elements that are shorted out 91-1-30 SRP
59  * Mar. 1991:  Include a substrate connection for bipolar transistors; use the
60  * subnet defined by a substrate connection or default to ground.
61  * Nov.18/91 Test the temp bits in the cell in case we
62  * are doing an extraction from icons...
63  * Nov. 29/91: Added option to write a trailer file, tech:~.SIM_spice_trailer_file
64  * modified comment header to describe capabilities
65  * Removed bug in writing substrate of bipolar transistors
66  * 920113: Created default ground net in cells; output only the first
67  * name of multiply-named networks; output a reference list of named arcs
68  * Do not complain about posnet in subcircuits
69  * SRP920603: Added option to include behavioral file for a cell, or a cell.
70  * The variable "SPICE_behave_file" can be attached to the cell (in case it is
71  * an icon, or to any cell in the cell on the nodeproto.
72  * Changed the name of the behavior file variable to "sim_spice_behave_file"
73  * SRP920604
74  * We should only count diffusion connections to the network, to calculate
75  * the drain and source areas correctly!
76  * Removed trapdoor in sim_spice_arcarea() for well layers SRP920618:
77  * Added function to look for diffusion arc function
78  * Added check for isolated diffusion that will not be modelled correctly
79  * Caught multiple polygons on different arc in sim_spice_evalpolygon()
80  * Changed call to mrgdonecell() and evalpolygon() to use (float) area
81  * Changed conditions for error messages about wells and substrates (SRP)
82  * Identify ground node even if the port is not flagged SRP920623
83  * SRP920712: Changed storage in nets for diffarea[] to float as well
84  *     "    Changed to write cell pins in numerical order
85  * Fixed bug in sim_spice_nodearea() that didi not traverse the arcs of
86  * a complex cell
87  * Wrote temporary local version of decsribenetwork() to get around bug
88  * in infinitstr() code
89  * RLW920716: modified sim_spice_writecell() to indicate the type of
90  *     unconnected diffusion on a node
91  *
92  * SRP921116: Changed warning messages in spice file to comments so that
93  *      simulator can test file
94  *
95  * TADR20000805 Added dependent sources CCVS CCCS VCVS VCCS
96  *
97  * To properly simulate a cell, it must have the following (many of these
98  * commands are found in the "spice.mac" command file):
99  *
100  * 1) Power and ground must be exports and explicitly connected to sources.
101  *    You can do this with a power source (the primitive node prototype
102  *    "schematic:source") which must be connected to power at the top and to
103  *    ground at the bottom.  The source should then be parameterized to
104  *    indicate the amount and whether it is voltage or current.  For example,
105  *    to make a 5 volt supply, create a source node and use:
106  *        setsource v "DC 5"
107  *
108  * 2) All input ports must be exports and connected to the positive side
109  *    of sources (again, the "schematic:source" node).  Time values may be
110  *    placed on the source nodes.  For example, if the source is to produce
111  *    values that are 0 volts from 0 to 10NS and then 5 volts, use:
112  *        setsource v "PWL(0NS 0 10NS 0 11NS 5)"
113  *    constant input values can use the same form as for power and ground.
114  *
115  *    A .NODESET source can be created by connecting the positive end of the
116  *    source to the appropriate node, and typing:
117  *        setsource n VALUE
118  *
119  *    Special sources such as VCVS's can be created with the "special" source.
120  *    The letter following the "s" in the parameterization will appear as
121  *    the first letter in the deck.  For example, to create a VCVS, create
122  *    a source and connect it to the approprite nodes.  Then parameterize it
123  *    with
124  *        setsource se "NC+ NC-"
125  *    Where NC+ and NC- are the names of ports which specify the control
126  *    voltages.  This would produce a spice card of the form:
127  *        EX N+ N- NC+ NC-
128  *    Where X is a unique name or number, and N+ and N- are the nodes which
129  *    are connected to the source.
130  *
131  * 3) All values that are being watched must be exports and have meters
132  *    placed on them.  The primitive nodeproto "schematic:meter" can be placed
133  *    anywhere and it should have the top and bottom ports connected
134  *    appropriately.  For the meter to watch voltage from 0 to 5 volts, use:
135  *        setmeter "(0 5)"
136  *    To watch the current through any voltage source, parameterize it in the
137  *    usual way, but add the "m" option, eg.
138  *        setsource vm ...
139  *        setsource vdm ...
140  *
141  * 4) Determine the level of simulation by saying:
142  *        setlevel 2
143  *    This will extract the appropriate header from the technology.
144  *    Alternately, you can specify a file to read the header info from
145  *    with the command:
146  *        variable set tech:~.SIM_spice_model_file FILENAME
147  *
148  * 5) Determine the type of analysis with another source node.  For transient
149  *    analysis that displays half NS intervals, runs for 20NS, starts at time
150  *    0, and uses internal steps of 0.1NS, create an unconnected source node
151  *    and issue this statement:
152  *        setsource vt ".5NS 20NS 0NS .1NS"
153  *    For DC analysis you must connect the power side of the source node to
154  *    the DC point and then use:
155  *        setsource vd "0V 5V .1V"
156  *    For AC analysis, create a source node and parameterize it with (eg.)
157  *        setsource va "DEC 10 1 10K"
158  *    There must be exactly one of these source nodes.
159  *
160  * 6) Define the spice format to use when producing the deck and parsing
161  *    the output:
162  *        telltool simulation spice format [2 | 3 | hspice | pspice | gnucap ]
163  *
164  * 7) Run spice with the command:
165  *        ontool simulation
166  *    This generates a deck and runs the simulator.  The results from .PRINT
167  *    commands will be converted to a cell in the current library that
168  *    contains a plot of the data.
169  *
170  * 8) You can also run SPICE on another machine (or otherwise outside of
171  *    Electric).  To do this, supress SPICE execution with:
172  *        telltool simulation not execute
173  *    which will cause deck generation only.  Then run SPICE externally
174  *    and convert the output listing to a plot with:
175  *        telltool simulation spice parse-output FILE
176  *
177  * 9) You can replace the internal spice header file that is part of the
178  *        technology by defining the variable "SIM_spice_model_file" on the
179  *        technology, which is the name of a file. You can add a trailer to the
180  *    spice deck by attaching a variable "SIM_spice_trailer_file" to the
181  *    technology, that is the name of a file. These variables are most
182  *    easily created in the Info.variables window, by clicking on
183  *    "Current Technology", then "New Attribute", defining "SIM_spice_model_file"
184  *    with the name of the file. Remember to click on "Set Attribute".
185  *    Include a full path if the file will not be in the current directory at
186  *    run time.
187  *
188  * 10) You can call up special bipolar and jfet models that are defined in
189  *    your header file by including an appropriate string in the variable
190  *    "SIM_spice_model" that is attached to transistors when they
191  *    are created. When the 'Transistor size' window appears, enter the
192  *    model name, ex: LARGE_NPN
193  *        The extractor will use the string in the call in the
194  *    spice file, ex:   Q1 3 4 0 0 LARGE_NPN
195  *    (in fact, any string that does not include the character '/' will
196  *    be used to describe the transistor in the spice file; you can
197  *    use this to define the attributes of individual transistors in your
198  *    circuit. The character '/' is reserved as a separator for length
199  *    and width values, and if present, causes the default type to be
200  *    invoked.)
201  *
202  * 11) You can use the contents of a file to replace the extracted description
203  *    of any cell, by attaching the name of the file as a variable
204  *    called "SIM_spice_behave_file" to the prototype.  The extractor will always
205  *    use the file, if found, instead of extracting the cell network, but it will
206  *    still extract any subcells in the cell. These in turn could also be described by
207  *    behavior files. If an icon or different view is used, it can have a
208  *    different behavior file than the other views.
209  *
210  */
211 
212 /*
213  * Extraction notes: Layers on arcs and nodes that overlap are
214  * merged into polygons, and the area and perimeter of the resulting polygon
215  * is computed. Overlapping areas are thus eliminated. The resultinmg areas
216  * are used to calculate the capacitance on the net, with the following special
217  * treatment: If the node or arc has multiple layers, the layer that gives the
218  * largest capacitance is left as the only active capacitance, and the other
219  * layers have an their area equal to their area on this port of the node
220  * removed.
221  * BUT, if the node or arc has a diffusion layer, that layer is always assumed
222  * dominant, and the area of the nondominant layers are subtracted from
223  * their accumulated area. This is not quite correct, when the diffusion area
224  * only partly covers the other areas.
225  * The NPCAPAC nodes (series capacitors) are defined to have a dominant
226  * diffusion layer, so that their nondiffusion layers are cancelled out. In
227  * order to cancel out the perimeter capacity of a top-plate layer, there
228  * should be an identical-sized layer with a nonzero area capacitance value,
229  * and a negative edge capacitance value equal to that of the layer to be
230  * cancelled out.
231  * The diffusion areas are gathered up according to whether they are declared
232  * as n-type, or p-type, or undefined. DMOS are assumed n-type. The number
233  * of n-transistors and p-transistors on the net are counted up, and the
234  * corresponding diffusion shared equally.
235  * It is assumed that the technology file has correctly used the bits that
236  * define layer functions.
237  * MOS Transistors must have a correct labelling of the source and drain, or
238  * there may be problems in the simulations if the wrong end is connected to
239  * the substrate. In this extractor, the order of extraction will be gate,
240  * source, gate, drain, based on the order of the ports in the technology file.
241  * This will be correct for PMOS with the Vdd at the top. The extracted values
242  * source and drain areas will correspond to this order.
243  * pMOS-transistors as taken from the technology file prototype will have their
244  * source at the top (more positive end), and nMOS-transistors taken in the
245  * prototype position will have to be rotated to have their drain at the top.
246  * Other device types will output collector, base, emitter, corresponding to
247  * extraction of the first three ports from the prototype in the tech file.
248  * Otherwise manual editing of the SPICE file is required.
249  */
250 
251 #include "config.h"
252 #if SIMTOOL
253 
254 #include "global.h"
255 #include "sim.h"
256 #include "eio.h"
257 #include "usr.h"
258 #include "network.h"
259 #include "efunction.h"
260 #include "tecschem.h"
261 #include "tecgen.h"
262 #include <math.h>
263 
264 #define DIFFTYPES       3	/* Types of diffusions & transistors plus 1 */
265 #define ISNONE	        0
266 #define ISNTYPE	        1
267 #define ISPTYPE	        2
268 
269 #define SPICELEGALCHARS        x_("!#$%*+-/<>[]_")
270 #define CDLNOBRACKETLEGALCHARS x_("!#$%*+-/<>_")
271 #define SPICEMAXLENSUBCKTNAME	70						/* maximum subcircuit name length (JKG) */
272 
273 #define sim_spice_puts(s,iscomment) sim_spice_xputs(s, sim_spice_file, iscomment)
274 
275 static CHAR       *sim_spicelegalchars;					/* legal characters */
276 static float       sim_spice_min_resist;				/* spice minimum resistance */
277 static float       sim_spice_min_capac;					/* spice minimum capacitance */
278 static CHAR       *sim_spice_ac;						/* AC analysis message */
279 static CHAR       *sim_spice_dc;						/* DC analysis message */
280 static CHAR       *sim_spice_tran;						/* Transient analysis message */
281 static POLYGON    *sim_polygonfree = NOPOLYGON;			/* list of free simulation polygons */
282 static float       sim_spice_mask_scale = 1.0;			/* Mask shrink factor (default =1) */
283 static float      *sim_spice_extra_area = 0;			/* Duplicated area on each layer */
284 static INTBIG      sim_spice_diffusion_index[DIFFTYPES];/* diffusion layers indices */
285 static INTBIG      sim_spice_layer_count;
286 static INTBIG      sim_spice_unnamednum;
287 static INTBIG      sim_spice_netindex;
288        INTBIG      sim_spice_levelkey;					/* key for "SIM_spice_level" */
289        INTBIG      sim_spice_statekey;					/* key for "SIM_spice_state" */
290        INTBIG      sim_spice_state;						/* value of "SIM_spice_state" */
291        INTBIG      sim_spice_nameuniqueid;				/* key for "SIM_spice_nameuniqueid" */
292 static INTBIG      sim_spice_machine;					/* Spice type: 2, 3, H, P */
293        INTBIG      sim_spice_listingfilekey;			/* key for "SIM_listingfile" */
294        INTBIG      sim_spice_runargskey;				/* key for "SIM_spice_runarguments" */
295 static CHAR       *sim_spice_printcard;					/* the .PRINT or .PLOT card */
296 static TECHNOLOGY *sim_spice_tech;						/* default technology to use */
297 static INTBIG      sim_spicewirelisttotal = 0;
298 static NETWORK   **sim_spicewirelist;
299 static INTBIG      sim_spice_card_key = 0;				/* key for "SPICE_card" */
300 static INTBIG      sim_spice_template_key = 0;			/* key for "ATTR_SPICE_template" */
301 static INTBIG      sim_spice_template_s2_key = 0;		/* key for "ATTR_SPICE_template_spice2" */
302 static INTBIG      sim_spice_template_s3_key = 0;		/* key for "ATTR_SPICE_template_spice3" */
303 static INTBIG      sim_spice_template_hs_key = 0;		/* key for "ATTR_SPICE_template_hspice" */
304 static INTBIG      sim_spice_template_ps_key = 0;		/* key for "ATTR_SPICE_template_pspice" */
305 static INTBIG      sim_spice_template_gc_key = 0;		/* key for "ATTR_SPICE_template_gnucap" */
306 static INTBIG      sim_spice_template_ss_key = 0;		/* key for "ATTR_SPICE_template_smartspice" */
307 static INTBIG      sim_spice_preferedkey;
308 static INTBIG      sim_spiceglobalnetcount;				/* number of global nets */
309 static INTBIG      sim_spiceglobalnettotal = 0;			/* size of global net array */
310 static CHAR      **sim_spiceglobalnets;					/* global net names */
311 static INTBIG      sim_spiceNameUniqueID;				/* for short unique subckt names (JKG) */
312 static FILE       *sim_spice_file;                      /* output stream */
313 static BOOLEAN     sim_spice_cdl;                       /* If "sim_spice_cdl" is true, put handle CDL format */
314 
315 /* working memory for "sim_spice_edgecapacitance()" */
316 static INTBIG *sim_spice_capacvalue = 0;
317 
318 /******************** SPICE NET QUEUE ********************/
319 
320 #define NOSPNET   ((SPNET *)-1)
321 
322 typedef struct Ispnet
323 {
324 	NETWORK       *network;					/* network object associated with this */
325 	INTBIG         netnumber;				/* internal unique net number */
326 	float          diffarea[DIFFTYPES];		/* area of diffusion */
327 	float          diffperim[DIFFTYPES];	/* perimeter of diffusion */
328 	float          resistance;				/* amount of resistance */
329 	float          capacitance;				/* amount of capacitance */
330 	INTBIG         components[DIFFTYPES];	/* number of components connected to net */
331 	SpiceNet      *spiceNet;                /* pointer to SpiceNet class */
332 	struct Ispnet *nextnet;					/* next in linked list */
333 } SPNET;
334 
335 static SPNET     *sim_spice_firstnet;				/* first in list of nets in this cell */
336 static SPNET     *sim_spice_netfree = NOSPNET;		/* list of free nets */
337 static SPNET     *sim_spice_cur_net;				/* for polygon merging */
338 
339 static NETWORK   *sim_spice_gnd;					/* net of ground */
340 static NETWORK   *sim_spice_vdd;					/* net of power */
341 
342 /* prototypes for local routines */
343 static void       sim_spice_addincludefile(CHAR *filename);
344 static POLYGON   *sim_spice_allocpolygon(void);
345 static SPNET     *sim_spice_allocspnet(void);
346 static void       sim_spice_arcarea(SPNET*, ARCINST*);
347 static INTBIG     sim_spice_arcisdiff(ARCINST*);
348 static float      sim_spice_capacitance(TECHNOLOGY*, INTBIG);
349 static CHAR      *sim_spice_cellname(NODEPROTO *np);
350 static CHAR      *sim_spice_describenetwork(NETWORK *net);
351 static void       sim_spice_dumpstringerror(void *infstr, SpiceInst *inst);
352 static float      sim_spice_edgecapacitance(TECHNOLOGY*, INTBIG);
353 static CHAR      *sim_spice_elementname(NODEINST*, CHAR, INTBIG*, CHAR*);
354 static void       sim_spice_evalpolygon(INTBIG, TECHNOLOGY*, INTBIG*, INTBIG*, INTBIG);
355 static void       sim_spice_freepolygon(POLYGON*);
356 static void       sim_spice_freespnet(SPNET*);
357 static void       sim_spice_gatherglobals(NODEPROTO *np);
358 static INTBIG     sim_spice_getexportednetworks(NODEPROTO *cell, NETWORK ***netlist,
359 					NETWORK **vdd, NETWORK **gnd, BOOLEAN cdl);
360 static SPNET     *sim_spice_getnet(NODEINST*, NETWORK*);
361 static INTBIG     sim_spice_layerisdiff(TECHNOLOGY*, INTBIG);
362 static CHAR      *sim_spice_legalname(CHAR *name);
363 static INTBIG	  sim_spice_markuniquenodeinsts(NODEPROTO*);
364 static CHAR      *sim_spice_netname(NETWORK *net, INTBIG bussize, INTBIG busindex);
365 static void       sim_spice_nodearea(SPNET*, NODEINST*, PORTPROTO*);
366 static CHAR      *sim_spice_nodename(SPNET*);
367 static int        sim_spice_sortnetworksbyname(const void *e1, const void *e2);
368 static void       sim_spice_storebox(TECHNOLOGY*, INTBIG, INTBIG, INTBIG, INTBIG, INTBIG);
369 static INTBIG     sim_spice_writecell(NODEPROTO*, BOOLEAN, CHAR*);
370 static void       sim_spice_writeheader(NODEPROTO*);
371 static void       sim_spice_writetrailer(NODEPROTO*);
372 static void       sim_spice_writetwoport(NODEINST*, INTBIG, CHAR*, SpiceCell*, INTBIG*, INTBIG);
373 static void       sim_spice_xputs(CHAR *s, FILE *stream, BOOLEAN iscomment);
374 
375 /*
376  * Routine to free all memory associated with this module.
377  */
sim_freespicememory(void)378 void sim_freespicememory(void)
379 {
380 	REGISTER POLYGON *poly;
381 	REGISTER SPNET *spnet;
382 	REGISTER INTBIG i;
383 
384 	while (sim_polygonfree != NOPOLYGON)
385 	{
386 		poly = sim_polygonfree;
387 		sim_polygonfree = sim_polygonfree->nextpolygon;
388 		freepolygon(poly);
389 	}
390 
391 	while (sim_spice_netfree != NOSPNET)
392 	{
393 		spnet = sim_spice_netfree;
394 		sim_spice_netfree = sim_spice_netfree->nextnet;
395 		efree((CHAR *)spnet);
396 	}
397 	if (sim_spice_capacvalue != 0) efree((CHAR *)sim_spice_capacvalue);
398 	if (sim_spicewirelisttotal > 0) efree((CHAR *)sim_spicewirelist);
399 	for(i=0; i<sim_spiceglobalnettotal; i++)
400 		if (sim_spiceglobalnets[i] != 0)
401 			efree((CHAR *)sim_spiceglobalnets[i]);
402 	if (sim_spiceglobalnettotal > 0)
403 		efree((CHAR *)sim_spiceglobalnets);
404 }
405 
406 /******************** SPICE DECK GENERATION ********************/
407 
408 /*
409  * Procedure to write a spice deck to describe cell "np".
410  * If "cdl" is true, put handle CDL format (no parameters, no top-level).
411  */
sim_writespice(NODEPROTO * np,BOOLEAN cdl)412 void sim_writespice(NODEPROTO *np, BOOLEAN cdl)
413 {
414 	REGISTER INTBIG retval, *curstate;
415 	REGISTER INTBIG analysiscards, i;
416 	CHAR deckfile[256], *pt;
417 	REGISTER VARIABLE *var;
418 	REGISTER LIBRARY *lib, *olib;
419 	REGISTER NODEPROTO *onp, *tnp, *gnp;
420 	REGISTER TECHNOLOGY *t;
421 	CHAR templatefile[256], *respar[2], *libname, *libpath, *prompt;
422 	extern TOOL *io_tool;
423 	REGISTER void *infstr;
424 	REGISTER BOOLEAN backannotate;
425 
426 	/* make sure network tool is on */
427 	if ((net_tool->toolstate&TOOLON) == 0)
428 	{
429 		ttyputerr(_("Network tool must be running...turning it on"));
430 		toolturnon(net_tool);
431 		ttyputerr(_("...now reissue the simulation command"));
432 		return;
433 	}
434 
435 	if (np == NONODEPROTO)
436 	{
437 		ttyputerr(_("Must have a cell to edit"));
438 		return;
439 	}
440 	sim_simnt = np;
441 
442 	/* determine technology to use */
443 	sim_spice_tech = defschematictechnology(np->tech);
444 	sim_spice_layer_count = sim_spice_tech->layercount;
445 
446 	/* get the SPICE state */
447 	var = getvalkey((INTBIG)sim_tool, VTOOL, VINTEGER, sim_spice_statekey);
448 	if (var != NOVARIABLE) sim_spice_state = var->addr; else sim_spice_state = 0;
449 	sim_spice_machine = sim_spice_state & SPICETYPE;
450 
451 	/*
452 	 * initialize the .PRINT or .PLOT card.
453 	 * As we find sources/meters, we will tack things onto this string
454 	 * and 'reallocstring' it.
455 	 * It is printed and then freed in sim_spice_writecell.
456 	 */
457 	if (allocstring(&sim_spice_printcard, x_(""), sim_tool->cluster)) return;
458 
459 	/* get the overall minimum resistance and capacitance */
460 	var = getval((INTBIG)sim_spice_tech, VTECHNOLOGY, VFLOAT, x_("SIM_spice_min_resistance"));
461 	if (var != NOVARIABLE) sim_spice_min_resist = castfloat(var->addr); else
462 		sim_spice_min_resist = 0.0;
463 	var = getval((INTBIG)sim_spice_tech, VTECHNOLOGY, VFLOAT, x_("SIM_spice_min_capacitance"));
464 	if (var != NOVARIABLE) sim_spice_min_capac = castfloat(var->addr); else
465 		sim_spice_min_capac = 0.0;
466 	var = getval((INTBIG)sim_spice_tech, VTECHNOLOGY, VFLOAT, x_("SIM_spice_mask_scale"));
467 	if (var != NOVARIABLE) sim_spice_mask_scale = castfloat(var->addr); else
468 		sim_spice_mask_scale = 1.0;
469 	sim_spice_extra_area = (float *)emalloc(sizeof(float) * sim_spice_layer_count,
470 		sim_tool->cluster);
471 
472 	/* get the layer resistance and capacitance arrays for each technology */
473 	for(t = el_technologies; t != NOTECHNOLOGY; t = t->nexttechnology)
474 	{
475 		var = getval((INTBIG)t, VTECHNOLOGY, VFLOAT|VISARRAY, x_("SIM_spice_resistance"));
476 		t->temp1 = (var == NOVARIABLE ? 0 : var->addr);
477 		var = getval((INTBIG)t, VTECHNOLOGY, VFLOAT|VISARRAY, x_("SIM_spice_capacitance"));
478 		t->temp2 = (var == NOVARIABLE ? 0 : var->addr);
479 	}
480 
481 	/*
482 	 * determine whether any cells have name clashes in other libraries
483 	 */
484 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
485 		for(onp = lib->firstnodeproto; onp != NONODEPROTO; onp = onp->nextnodeproto)
486 			onp->temp2 = 0;
487 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
488 	{
489 		if ((lib->userbits&HIDDENLIBRARY) != 0) continue;
490 		for(tnp = lib->firstnodeproto; tnp != NONODEPROTO; tnp = tnp->nextnodeproto)
491 		{
492 			if (tnp->temp2 != 0) continue;
493 			for(olib = lib->nextlibrary; olib != NOLIBRARY; olib = olib->nextlibrary)
494 			{
495 				if ((olib->userbits&HIDDENLIBRARY) != 0) continue;
496 				for(onp = olib->firstnodeproto; onp != NONODEPROTO; onp = onp->nextnodeproto)
497 					if (namesame(tnp->protoname, onp->protoname) == 0) break;
498 				if (onp != NONODEPROTO)
499 				{
500 					FOR_CELLGROUP(gnp, onp)
501 						gnp->temp2 = 1;
502 					FOR_CELLGROUP(gnp, tnp)
503 						gnp->temp2 = 1;
504 				}
505 			}
506 		}
507 	}
508 
509 	/* setup the legal characters */
510 	sim_spicelegalchars = SPICELEGALCHARS;
511 
512 	/* start writing the spice deck */
513 	sim_spice_cdl = cdl;
514 	if (cdl)
515 	{
516 		/* setup bracket conversion for CDL */
517 		curstate = io_getstatebits();
518 		if ((curstate[1]&CDLNOBRACKETS) != 0)
519 			sim_spicelegalchars = CDLNOBRACKETLEGALCHARS;
520 
521 		(void)estrcpy(deckfile, np->protoname);
522 		(void)estrcat(deckfile, x_(".cdl"));
523 		pt = deckfile;
524 		prompt = 0;
525 		if ((us_useroptions&NOPROMPTBEFOREWRITE) == 0) prompt = _("CDL File");
526 		sim_spice_file = xcreate(deckfile, sim_filetypecdl, prompt, &pt);
527 		if (pt != 0) (void)estrcpy(deckfile, pt);
528 		if (sim_spice_file == NULL)
529 		{
530 			ttyputerr(_("Cannot create CDL file: %s"), deckfile);
531 			return;
532 		}
533 		sim_spice_xprintf(sim_spice_file, TRUE, x_("* First line is ignored\n"));
534 	} else
535 	{
536 		(void)estrcpy(deckfile, np->protoname);
537 		(void)estrcat(deckfile, x_(".spi"));
538 		pt = deckfile;
539 		prompt = 0;
540 		if ((us_useroptions&NOPROMPTBEFOREWRITE) == 0) prompt = _("SPICE File");
541 		sim_spice_file = xcreate(deckfile, sim_filetypespice, prompt, &pt);
542 		if (pt != 0) (void)estrcpy(deckfile, pt);
543 		if (sim_spice_file == NULL)
544 		{
545 			ttyputerr(_("Cannot create SPICE file: %s"), deckfile);
546 			return;
547 		}
548 		(void)sim_spice_writeheader(np);
549 	}
550 
551 	/* gather all global signal names (HSPICE and PSPICE only) */
552 	sim_spiceglobalnetcount = 0;
553 	if ((sim_spice_state&SPICENODENAMES) != 0 && sim_spice_machine != SPICE3)
554 	{
555 		for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
556 			for(onp = lib->firstnodeproto; onp != NONODEPROTO; onp = onp->nextnodeproto)
557 				onp->temp1 = 0;
558 		sim_spice_gatherglobals(np);
559 		if (sim_spiceglobalnetcount > 0)
560 		{
561 			sim_spice_xprintf(sim_spice_file, FALSE, x_("\n"));
562 			infstr = initinfstr();
563 			addstringtoinfstr(infstr, x_(".global"));
564 			for(i=0; i<sim_spiceglobalnetcount; i++)
565 				formatinfstr(infstr, x_(" %s"), sim_spiceglobalnets[i]);
566 			sim_spice_xprintf(sim_spice_file, FALSE, x_("%s\n"), returninfstr(infstr));
567 		}
568 	}
569 
570 	/* initialize short names for subcircuits (JKG) */
571 	sim_spiceNameUniqueID = 0;
572 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
573 		for(onp = lib->firstnodeproto; onp != NONODEPROTO; onp = onp->nextnodeproto)
574 			onp->temp1 = 0;
575 	sim_spice_markuniquenodeinsts(np);
576 
577 	/* see if there are any resistors in this circuit */
578 	if (hasresistors(np))
579 	{
580 		/* has resistors: make sure they aren't being ignored */
581 		if (asktech(sch_tech, x_("ignoring-resistor-topology")) != 0)
582 		{
583 			/* must redo network topology to make resistors real */
584 			respar[0] = x_("resistors");
585 			respar[1] = x_("include");
586 			(void)telltool(net_tool, 2, respar);
587 		}
588 	}
589 
590 	/* make sure that all nodes have names on them */
591 	backannotate = FALSE;
592 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
593 		for(onp = lib->firstnodeproto; onp != NONODEPROTO; onp = onp->nextnodeproto)
594 	{
595 		if (asktool(net_tool, x_("name-nodes"), (INTBIG)onp) != 0) backannotate = TRUE;
596 		if (asktool(net_tool, x_("name-nets"), (INTBIG)onp) != 0) backannotate = TRUE;
597 	}
598 
599 	/* reset bits on all cells */
600 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
601 		for(onp = lib->firstnodeproto; onp != NONODEPROTO; onp = onp->nextnodeproto)
602 	{
603 		onp->temp1 = 0;
604 //		onp->cell->temp1 = 0;
605 	}
606 	sim_spice_unnamednum = 1;
607 
608 	/* initialize for parameterized cells */
609 	if (sim_spice_cdl || (sim_spice_state&SPICECELLPARAM) == 0) initparameterizedcells();
610 	begintraversehierarchy();
611 
612 	/* we don't know the type of analysis yet... */
613 	sim_spice_ac = sim_spice_dc = sim_spice_tran = NULL;
614 
615 	/* initialize the polygon merging system */
616 	mrginit();
617 
618 	/* initialize SpiceCell structures */
619 	SpiceCell::clearAll();
620 
621 	/* recursively write all cells below this one */
622 	retval = sim_spice_writecell(np, TRUE, 0);
623 	endtraversehierarchy();
624 	if (retval < 0) ttyputnomemory();
625 	if (backannotate)
626 		ttyputmsg(_("Back-annotation information has been added (library must be saved)"));
627 
628 	/* handle AC, DC, and TRAN analysis cards */
629 	analysiscards = 0;
630 	if (sim_spice_dc != NULL) analysiscards++;
631 	if (sim_spice_tran != NULL) analysiscards++;
632 	if (sim_spice_ac != NULL) analysiscards++;
633 	if (analysiscards > 1)
634 		ttyputerr(_("WARNING: can only have one DC, Transient or AC source node"));
635 	if (sim_spice_tran != NULL)
636 	{
637 		sim_spice_xprintf(sim_spice_file, FALSE, x_("%s\n"), sim_spice_tran);
638 		efree(sim_spice_tran);
639 	} else if (sim_spice_ac != NULL)
640 	{
641 		sim_spice_xprintf(sim_spice_file, FALSE, x_("%s\n"), sim_spice_ac);
642 		efree(sim_spice_ac);
643 	} else if (sim_spice_dc != NULL)
644 	{
645 		sim_spice_xprintf(sim_spice_file, FALSE, x_("%s\n"), sim_spice_dc);
646 		efree(sim_spice_dc);
647 	}
648 
649 	if (!sim_spice_cdl)
650 	{
651 		sim_spice_writetrailer(np);
652 		sim_spice_xprintf(sim_spice_file, FALSE, x_(".END\n"));
653 	}
654 	xclose(sim_spice_file);
655 	ttyputmsg(_("%s written"), deckfile);
656 	efree((CHAR *)sim_spice_extra_area);
657 
658 	/* finish polygon merging subsystem */
659 	mrgterm();
660 
661 	if (sim_spice_cdl)
662 	{
663 		/* write the control files */
664 		(void)estrcpy(templatefile, np->protoname);
665 		(void)estrcat(templatefile, x_(".cdltemplate"));
666 		sim_spice_file = xcreate(templatefile, sim_filetypectemp, _("CDL Template File"), &pt);
667 		if (pt != 0) (void)estrcpy(templatefile, pt);
668 		if (sim_spice_file == NULL)
669 		{
670 			ttyputerr(_("Cannot create CDL template file: %s"), templatefile);
671 			return;
672 		}
673 		for(i=estrlen(deckfile)-1; i>0; i--) if (deckfile[i] == DIRSEP) break;
674 		if (deckfile[i] == DIRSEP) deckfile[i++] = 0;
675 		var = getval((INTBIG)io_tool, VTOOL, VSTRING, x_("IO_cdl_library_name"));
676 		if (var == NOVARIABLE) libname = x_(""); else
677 			libname = (CHAR *)var->addr;
678 		var = getval((INTBIG)io_tool, VTOOL, VSTRING, x_("IO_cdl_library_path"));
679 		if (var == NOVARIABLE) libpath = x_(""); else
680 			libpath = (CHAR *)var->addr;
681 		xprintf(sim_spice_file, x_("cdlInKeys = list(nil\n"));
682 		xprintf(sim_spice_file, x_("    'searchPath             \"%s"), deckfile);
683 		if (libpath[0] != 0)
684 			xprintf(sim_spice_file, x_("\n                             %s"), libpath);
685 		xprintf(sim_spice_file, x_("\"\n"));
686 		xprintf(sim_spice_file, x_("    'cdlFile                \"%s\"\n"), &deckfile[i]);
687 		xprintf(sim_spice_file, x_("    'userSkillFile          \"\"\n"));
688 		xprintf(sim_spice_file, x_("    'opusLib                \"%s\"\n"), libname);
689 		xprintf(sim_spice_file, x_("    'primaryCell            \"%s\"\n"), sim_spice_cellname(np));
690 		xprintf(sim_spice_file, x_("    'caseSensitivity        \"preserve\"\n"));
691 		xprintf(sim_spice_file, x_("    'hierarchy              \"flatten\"\n"));
692 		xprintf(sim_spice_file, x_("    'cellTable              \"\"\n"));
693 		xprintf(sim_spice_file, x_("    'viewName               \"netlist\"\n"));
694 		xprintf(sim_spice_file, x_("    'viewType               \"\"\n"));
695 		xprintf(sim_spice_file, x_("    'pr                     nil\n"));
696 		xprintf(sim_spice_file, x_("    'skipDevice             nil\n"));
697 		xprintf(sim_spice_file, x_("    'schemaLib              \"sample\"\n"));
698 		xprintf(sim_spice_file, x_("    'refLib                 \"\"\n"));
699 		xprintf(sim_spice_file, x_("    'globalNodeExpand       \"full\"\n"));
700 		xprintf(sim_spice_file, x_(")\n"));
701 		xclose(sim_spice_file);
702 		ttyputmsg(_("%s written"), templatefile);
703 		ttyputmsg(x_("Now type: exec nino CDLIN %s &"), templatefile);
704 	}
705 
706 	/* run spice (if requested) */
707 	var = getvalkey((INTBIG)sim_tool, VTOOL, VINTEGER, sim_dontrunkey);
708 	if (var != NOVARIABLE && var->addr != SIMRUNNO)
709 	{
710 		ttyputmsg(_("Running SPICE..."));
711 		var = getvalkey((INTBIG)sim_tool, VTOOL, VSTRING, sim_spice_listingfilekey);
712 		if (var == NOVARIABLE) sim_spice_execute(deckfile, x_(""), np); else
713 			sim_spice_execute(deckfile, (CHAR *)var->addr, np);
714 	}
715 }
716 
717 /*
718  * routine to write cell "np" and its all referenced subnodeprotos in SPICE
719  * format to file "f".  If "top" is true, this is the top cell.  The
720  * spice level is "tool:sim.SIM_spice_level" and "sim_spice_min_resist" and
721  * "sim_spice_min_capac" are the minimum required resistance and capacitance.
722  * Returns negative on error, positive if back annotation was added.
723  */
sim_spice_writecell(NODEPROTO * np,BOOLEAN top,CHAR * paramname)724 INTBIG sim_spice_writecell(NODEPROTO *np, BOOLEAN top, CHAR *paramname)
725 {
726 	REGISTER NODEINST *ni;
727 	REGISTER NODEPROTO *subnt, *cnp;
728 	REGISTER PORTPROTO *pp, *cpp;
729 	PORTPROTO *biasport = NOPORTPROTO;
730 	PORTPROTO *gate, *source, *drain, *gatedummy;
731 	NETWORK **netlist, *subvdd, *subgnd;
732 	REGISTER PORTARCINST *pi;
733 	REGISTER PORTEXPINST *pe;
734 	REGISTER ARCINST *ai;
735 	REGISTER SPNET *spnet, *nspnet, *gaten, *sourcen, *drainn, *posnet, *negnet,
736 		*subnet, *biasn;
737 	float a, b, purevalue;
738 	REGISTER BOOLEAN backannotate;
739 	REGISTER INTBIG state, j, first, i, nodetype, retval, len, count,
740 		reallambda, nodelambda, netcount, err, sigcount, dumpcell, nodewidth, nindex;
741 	INTBIG subcellindex, nodeindex, resistnum, capacnum, inductnum, diodenum;
742 	REGISTER VARIABLE *var, *vartemplate, *nivar, *varl, *varw;
743 	REGISTER NETWORK *net, *rnet;
744 	CHAR *extra, *info, line[100], *shortname, *pname, **strings;
745 	REGISTER CHAR *pt, *start, save;
746 	INTBIG lx, ly, *indexlist, depth;
747 	INTBIG bipolars, nmostrans, pmostrans;
748 	NODEINST **hier;
749 	CHAR *uncon_diff_type, **nodenames;
750 	REGISTER void *infstr;
751 	SpiceCell *spCell;
752 	SpiceInst *spInst;
753 
754 	/* stop if requested */
755 	if (stopping(STOPREASONDECK)) return(-1);
756 
757 	/* make sure key is cached */
758 	if (sim_spice_template_key == 0)
759 		sim_spice_template_key = makekey(x_("ATTR_SPICE_template"));
760 	if (sim_spice_template_s2_key == 0)
761 		sim_spice_template_s2_key = makekey(x_("ATTR_SPICE_template_spice2"));
762 	if (sim_spice_template_s3_key == 0)
763 		sim_spice_template_s3_key = makekey(x_("ATTR_SPICE_template_spice3"));
764 	if (sim_spice_template_hs_key == 0)
765 		sim_spice_template_hs_key = makekey(x_("ATTR_SPICE_template_hspice"));
766 	if (sim_spice_template_ps_key == 0)
767 		sim_spice_template_ps_key = makekey(x_("ATTR_SPICE_template_pspice"));
768 	if (sim_spice_template_gc_key == 0)
769 		sim_spice_template_gc_key = makekey(x_("ATTR_SPICE_template_gnucap"));
770 	if (sim_spice_template_ss_key == 0)
771 		sim_spice_template_ss_key = makekey(x_("ATTR_SPICE_template_smartspice"));
772 	switch (sim_spice_state&SPICETYPE)
773 	{
774 		case SPICE2:          sim_spice_preferedkey = sim_spice_template_s2_key;   break;
775 		case SPICE3:          sim_spice_preferedkey = sim_spice_template_s3_key;   break;
776 		case SPICEHSPICE:     sim_spice_preferedkey = sim_spice_template_hs_key;   break;
777 		case SPICEPSPICE:     sim_spice_preferedkey = sim_spice_template_ps_key;   break;
778 		case SPICEGNUCAP:     sim_spice_preferedkey = sim_spice_template_gc_key;   break;
779 		case SPICESMARTSPICE: sim_spice_preferedkey = sim_spice_template_ss_key;   break;
780 	}
781 
782 	/* look for a model file on the current cell */
783 	var = getval((INTBIG)np, VNODEPROTO, VSTRING, x_("SIM_spice_behave_file"));
784 
785 	/* first pass through the node list */
786 	/* if there are any sub-cells that have not been written, write them */
787 	backannotate = FALSE;
788 	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
789 	{
790 		subnt = ni->proto;
791 		if (subnt->primindex != 0) continue;
792 
793 		/* ignore recursive references (showing icon in contents) */
794 		if (isiconof(subnt, np)) continue;
795 
796 		/* see if this cell has a template */
797 
798 		var = getvalkey((INTBIG)subnt, VNODEPROTO, VSTRING, sim_spice_preferedkey);
799 		if (var == NOVARIABLE)
800 			var = getvalkey((INTBIG)subnt, VNODEPROTO, VSTRING, sim_spice_template_key);
801 		if (var != NOVARIABLE) continue;
802 
803 		/* use the contents view if this is an icon */
804 		if (subnt->cellview == el_iconview)
805 		{
806 			cnp = contentsview(subnt);
807 			if (cnp != NONODEPROTO)
808 			{
809 				subnt = cnp;
810 
811 				/* see if this contents cell has a template */
812 				var = getvalkey((INTBIG)subnt, VNODEPROTO, VSTRING, sim_spice_preferedkey);
813 				if (var == NOVARIABLE)
814 					var = getvalkey((INTBIG)subnt, VNODEPROTO, VSTRING, sim_spice_template_key);
815 				if (var != NOVARIABLE) continue;
816 			}
817 		}
818 
819 		/* make sure the subcell hasn't already been written */
820 		dumpcell = 1;
821 		if (subnt->temp1 != 0) dumpcell = 0;
822 //		if (subnt->cell->temp1 != 0) dumpcell = 0;
823 
824 		pname = 0;
825 		if (sim_spice_cdl || (sim_spice_state&SPICECELLPARAM) == 0)
826 		{
827 			pname = parameterizedname(ni, sim_spice_cellname(ni->proto));
828 
829 			/* logical effort requires uniqueness for so marked insts, even if parameterized inst */
830 			var = getvalkey((INTBIG)ni, VNODEINST, VINTEGER, sim_spice_nameuniqueid);
831 			if (var != NOVARIABLE)
832 			{
833 				/* make unique name out of hierarchy */
834 				gettraversalpath(ni->parent, el_curwindowpart, &hier, &indexlist, &depth, 0);
835 				infstr = initinfstr();
836 				for(i=0; i<depth; i++)
837 					addstringtoinfstr(infstr, describenodeinst(hier[i]));
838 				addstringtoinfstr(infstr, describenodeinst(ni));
839 				(void)allocstring(&pname, returninfstr(infstr), el_tempcluster);
840 
841 				/* this ni has been marked to dump regardless of parameter state */
842 				dumpcell = 1;
843 			}
844 			if (pname != 0)
845 			{
846 				if (inparameterizedcells(subnt, pname) == 0)
847 				{
848 					dumpcell = 1;
849 
850 					/* JKG CHANGE - use shorter subckt names if subckt name too long */
851 					shortname = 0;
852 					if (estrlen(pname) > SPICEMAXLENSUBCKTNAME)
853 					{
854 						infstr = initinfstr();
855 						formatinfstr(infstr, x_("%s_ID%ld"), sim_spice_cellname(ni->proto), sim_spiceNameUniqueID);
856 						shortname = returninfstr(infstr);
857 						sim_spiceNameUniqueID++;
858 					}
859 					addtoparameterizedcells(subnt, pname, shortname);
860 					if (shortname != 0)
861 					{
862 						efree((CHAR *)pname);
863 						(void)allocstring(&pname, shortname, el_tempcluster);
864 					}
865 				} else
866 					dumpcell = 0;
867 			}
868 		}
869 
870 		if (dumpcell != 0)
871 		{
872 			downhierarchy(ni, subnt, 0);
873 			retval = sim_spice_writecell(subnt, FALSE, pname);
874 			uphierarchy();
875 			if (retval < 0) return(-1);
876 			if (retval > 0) backannotate = TRUE;
877 		}
878 		if (pname != 0) efree((CHAR *)pname);
879 	}
880 
881 	/* mark this cell as written */
882 	if (paramname == 0)
883 	{
884 		np->temp1 = 1;
885 //		np->cell->temp1 = 1;
886 	}
887 
888 	/* see if this cell has a disk file of SPICE associated with it */
889 	var = getval((INTBIG)np, VNODEPROTO, VSTRING, x_("SIM_spice_behave_file"));
890 	if (var != NOVARIABLE)
891 	{
892 		xprintf(sim_spice_file, x_("* Cell %s is described in this file:\n"), describenodeproto(np));
893 		sim_spice_addincludefile((CHAR *)var->addr);
894 		return(0);
895 	}
896 
897 	/* find all networks in this cell, and find power and ground */
898 	netcount = sim_spice_getexportednetworks(np, &netlist, &sim_spice_vdd, &sim_spice_gnd, sim_spice_cdl);
899 
900 	/* make sure power and ground appear at the top level */
901 	if (top && (sim_spice_state&SPICEGLOBALPG) == 0)
902 	{
903 		if (sim_spice_vdd == NONETWORK)
904 			ttyputerr(_("WARNING: cannot find power at top level of circuit"));
905 		if (sim_spice_gnd == NONETWORK)
906 			ttyputerr(_("WARNING: cannot find ground at top level of circuit"));
907 	}
908 
909 	/* zero the temp1 for arcs and nodes to compute area */
910 	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst) ai->temp1 = 0;
911 	for(net = np->firstnetwork; net != NONETWORK; net = net->nextnetwork) net->temp1 = 0;
912 	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
913 		ni->temp1 = ni->temp2 = 0;
914 	bipolars = nmostrans = pmostrans = 0;
915 
916 	/* create linked list of electrical nets in this cell */
917 	sim_spice_firstnet = NOSPNET;
918 
919 	/* must have a default node 0 in subcells */
920 	nodeindex = subcellindex = 1;
921 	sim_spice_netindex = 2;	   /* save 1 for the substrate */
922 
923 	/* look at all arcs in the cell */
924 	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
925 	{
926 		if (ai->temp1 != 0) continue;
927 
928 		/* don't count non-electrical arcs */
929 		if (((ai->proto->userbits&AFUNCTION)>>AFUNCTIONSH) == APNONELEC) continue;
930 
931 		/* ignore busses */
932 		if (ai->network->buswidth > 1) continue;
933 
934 		/* add this net to the list */
935 		if (ai->network->temp1 != 0)
936 		{
937 			spnet = (SPNET*)ai->network->temp1;
938 		} else
939 		{
940 			spnet = sim_spice_allocspnet();
941 			if (spnet == NOSPNET) return(-1);
942 			spnet->network = ai->network;
943 			ai->network->temp1 = (INTBIG)spnet;
944 			spnet->netnumber = sim_spice_netindex++;
945 			spnet->nextnet = sim_spice_firstnet;
946 			sim_spice_firstnet = spnet;
947 		}
948 
949 		/* reset */
950 		for (j = 0; j < sim_spice_layer_count; j++) sim_spice_extra_area[j] = 0.0;
951 		for (j = 0; j < DIFFTYPES; j++) sim_spice_diffusion_index[j] = -1;
952 
953 		sim_spice_arcarea(spnet, ai); /* builds areas, etc */
954 
955 		/* get merged polygons so far */
956 		sim_spice_cur_net = spnet;
957 		mrgdonecell(sim_spice_evalpolygon);
958 	}
959 
960 	/* now add any unwired but exported ports to the list of nets */
961 	for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
962 	{
963 		/* determine the net number of the export */
964 		if (pp->network->temp1 != 0) continue;
965 
966 		/* add this net to the list */
967 		spnet = sim_spice_allocspnet();
968 		if (spnet == NOSPNET) return(-1);
969 		spnet->network = pp->network;
970 		pp->network->temp1 = (INTBIG)spnet;
971 		spnet->netnumber = sim_spice_netindex++;
972 
973 		/* reset */
974 		ni = pp->subnodeinst;
975 		for (j = 0; j < sim_spice_layer_count; j++) sim_spice_extra_area[j] = 0.0;
976 		for (j = 0; j < DIFFTYPES; j++) sim_spice_diffusion_index[j] = -1;
977 		sim_spice_nodearea(spnet, ni, pp->subportproto);
978 
979 		/* get merged polygons so far */
980 		sim_spice_cur_net = spnet;
981 		mrgdonecell(sim_spice_evalpolygon);
982 
983 		/* count the number of components on the net */
984 		state = nodefunction(ni);
985 		switch (state)
986 		{
987 			case NPTRAEMES:
988 			case NPTRA4EMES:
989 			case NPTRADMES:
990 			case NPTRA4DMES:
991 			case NPTRADMOS:
992 			case NPTRA4DMOS:
993 			case NPTRANMOS:
994 			case NPTRA4NMOS: nodetype = ISNTYPE; break;
995 			case NPTRAPMOS:
996 			case NPTRA4PMOS: nodetype = ISPTYPE; break;
997 			default:         nodetype = ISNONE;  break;
998 		}
999 
1000 		/* only count drains and source connections in counting transistors */
1001 		if (nodetype == ISNONE)
1002 			spnet->components[nodetype]++; /* We don't use this, anyhow */
1003 		else
1004 		{
1005 			transistorports(ni, &gate, &gatedummy, &source, &drain);
1006 			if (pp->subportproto == source || pp->subportproto == drain)
1007 			{
1008 				spnet->components[nodetype]++;
1009 			}
1010 		}
1011 		spnet->nextnet = sim_spice_firstnet;
1012 		sim_spice_firstnet = spnet;
1013 	}
1014 
1015 	/* create Spice net information for all remaining nets in the cell */
1016 	for(net = np->firstnetwork; net != NONETWORK; net = net->nextnetwork)
1017 	{
1018 		if (net->temp1 != 0) continue;
1019 
1020 		/* add this net to the list */
1021 		spnet = sim_spice_allocspnet();
1022 		if (spnet == NOSPNET) return(-1);
1023 		net->temp1 = (INTBIG)spnet;
1024 		spnet->network = net;
1025 		spnet->netnumber = sim_spice_netindex++;
1026 		spnet->nextnet = sim_spice_firstnet;
1027 		sim_spice_firstnet = spnet;
1028 	}
1029 
1030 	/* make sure the ground net is number zero */
1031 	if (top && sim_spice_gnd != NONETWORK)
1032 		((SPNET *)sim_spice_gnd->temp1)->netnumber = 0;
1033 
1034 	posnet = negnet = subnet = NOSPNET;
1035 
1036 	/* second pass through the node list */
1037 	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1038 	{
1039 		state = nodefunction(ni);
1040 		switch (state)
1041 		{
1042 			case NPTRANPN:
1043 			case NPTRA4NPN:
1044 			case NPTRAPNP:
1045 			case NPTRA4PNP:
1046 			case NPTRANS:
1047 				nodetype = ISNONE;
1048 				bipolars++;
1049 				break;
1050 			case NPTRAEMES:
1051 			case NPTRA4EMES:
1052 			case NPTRADMES:
1053 			case NPTRA4DMES:
1054 			case NPTRADMOS:
1055 			case NPTRA4DMOS:
1056 			case NPTRANMOS:
1057 			case NPTRA4NMOS:
1058 				nodetype = ISNTYPE;
1059 				nmostrans++;
1060 				break;
1061 			case NPTRAPMOS:
1062 			case NPTRA4PMOS:
1063 				nodetype = ISPTYPE;
1064 				pmostrans++;
1065 				break;
1066 			case NPSUBSTRATE:
1067 				if (subnet == NOSPNET)
1068 					subnet = sim_spice_getnet(ni, ni->proto->firstportproto->network);
1069 				continue;
1070 			case NPTRANSREF:
1071 			case NPTRANJFET:
1072 			case NPTRA4NJFET:
1073 			case NPTRAPJFET:
1074 			case NPTRA4PJFET:
1075 			case NPRESIST:
1076 			case NPCAPAC:
1077 			case NPECAPAC:
1078 			case NPINDUCT:
1079 			case NPDIODE:
1080 			case NPDIODEZ:
1081 			case NPCONGROUND:
1082 			case NPCONPOWER:
1083 				nodetype = ISNONE;
1084 				break;
1085 			default:
1086 		        continue;
1087 		}
1088 
1089 		/*
1090 		 * find all wired ports on component and increment their count,
1091 		 * but only if they are a drain or source
1092 		 */
1093 		if (nodetype != ISNONE)
1094 		{
1095 			transistorports(ni, &gate, &gatedummy, &source, &drain);
1096 			for(spnet = sim_spice_firstnet; spnet != NOSPNET; spnet = spnet->nextnet)
1097 			{
1098 				for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
1099 				{
1100 					if (pi->proto != source) continue;
1101 					if (spnet->network == pi->conarcinst->network) break;
1102 				}
1103 				if (pi != NOPORTARCINST) spnet->components[nodetype]++;
1104 				for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
1105 				{
1106 					if (pi->proto != drain) continue;
1107 					if (spnet->network == pi->conarcinst->network) break;
1108 				}
1109 				if (pi != NOPORTARCINST) spnet->components[nodetype]++;
1110 			}
1111 		}
1112 	}
1113 
1114 	/* use ground net for substrate */
1115 	if (subnet == NOSPNET && sim_spice_gnd != NONETWORK)
1116 		subnet = (SPNET *)sim_spice_gnd->temp1;
1117 
1118 	if (pmostrans != 0 && posnet == NOSPNET)
1119 	{
1120 		if (sim_spice_vdd == NONETWORK)
1121 		{
1122 			infstr = initinfstr();
1123 			formatinfstr(infstr, _("WARNING: no power connection for P-transistor wells in cell %s"),
1124 				describenodeproto(np));
1125 			sim_spice_dumpstringerror(infstr, 0);
1126 		} else posnet = (SPNET *)sim_spice_vdd->temp1;
1127 	}
1128 	if (nmostrans != 0 && negnet == NOSPNET)
1129 	{
1130 		if (sim_spice_gnd == NONETWORK)
1131 		{
1132 			infstr = initinfstr();
1133 			formatinfstr(infstr, _("WARNING: no connection for N-transistor wells in cell %s"),
1134 				describenodeproto(np));
1135 			sim_spice_dumpstringerror(infstr, 0);
1136 		} else negnet = (SPNET *)sim_spice_gnd->temp1;
1137 	}
1138 
1139 	if (bipolars != 0 && subnet == NOSPNET)
1140 	{
1141 		infstr = initinfstr();
1142 		formatinfstr(infstr, _("WARNING: no explicit connection to the substrate in cell %s"),
1143 			describenodeproto(np));
1144 		sim_spice_dumpstringerror(infstr, 0);
1145 		if (sim_spice_gnd != NONETWORK)
1146 		{
1147 			ttyputmsg(_("     A connection to ground will be used if necessary."));
1148 			subnet = (SPNET *)sim_spice_gnd->temp1;
1149 		}
1150 	}
1151 
1152 	/* generate header for subckt or top-level cell */
1153 	if (top && !sim_spice_cdl)
1154 	{
1155 		sim_spice_xprintf(sim_spice_file, TRUE, x_("\n*** TOP LEVEL CELL: %s\n"), describenodeproto(np));
1156 		spCell = new SpiceCell();
1157 	} else
1158 	{
1159 		CHAR *cellName = (paramname != 0 ? paramname : sim_spice_cellname(np));
1160 		cellName = sim_spice_legalname( cellName );
1161 		sim_spice_xprintf(sim_spice_file, FALSE, x_("\n*** CELL: %s\n.SUBCKT %s"), describenodeproto(np), cellName );
1162 		spCell = new SpiceCell( cellName );
1163 		for(i=0; i<netcount; i++)
1164 		{
1165 			net = netlist[i];
1166 			if (net->globalnet >= 0) continue;
1167 			if (sim_spice_cdl)
1168 			{
1169 				if (i > 0 && netlist[i-1]->temp2 != net->temp2)
1170 					sim_spice_xprintf(sim_spice_file, FALSE, x_(" /"));
1171 			}
1172 			sim_spice_xprintf(sim_spice_file, FALSE, x_(" %s"), sim_spice_netname(net, 0, 0));
1173 		}
1174 		if ((sim_spice_state&SPICENODENAMES) == 0 || sim_spice_machine == SPICE3)
1175 		{
1176 			for (i = 0; i < np->globalnetcount; i++)
1177 			{
1178 				net = np->globalnetworks[i];
1179 				if (net == NONETWORK) continue;
1180 				sim_spice_xprintf(sim_spice_file, FALSE, x_(" %s"), sim_spice_netname(net, 0, 0));
1181 			}
1182 		}
1183 		if (!sim_spice_cdl && (sim_spice_state&SPICECELLPARAM) != 0)
1184 		{
1185 			/* add in parameters to this cell */
1186 			for(i=0; i<np->numvar; i++)
1187 			{
1188 				var = &np->firstvar[i];
1189 				if (TDGETISPARAM(var->textdescript) == 0) continue;
1190 				sim_spice_xprintf(sim_spice_file, FALSE, x_(" %s=%s"), truevariablename(var),
1191 					describesimplevariable(var));
1192 			}
1193 		}
1194 		sim_spice_xprintf(sim_spice_file, FALSE, x_("\n"));
1195 
1196 		/* generate pin descriptions for reference (when not using node names) */
1197 		for (i = 0; i < np->globalnetcount; i++)
1198 		{
1199 			net = np->globalnetworks[i];
1200 			if (net == NONETWORK) continue;
1201 			sim_spice_xprintf(sim_spice_file, TRUE, x_("** GLOBAL %s (network: %s)\n"),
1202 				sim_spice_netname(net, 0, 0),
1203 				sim_spice_describenetwork(net));
1204 		}
1205 
1206 		/* write exports to this cell */
1207 		for(i=0; i<netcount; i++)
1208 		{
1209 			net = netlist[i];
1210 			if (net == sim_spice_gnd || net == sim_spice_vdd) continue;
1211 			sim_spice_xprintf(sim_spice_file, TRUE, x_("** PORT %s"), sim_spice_netname(net, 0, 0));
1212 			if (net->namecount > 0)
1213 				sim_spice_xprintf(sim_spice_file, TRUE, x_(" (network: %s)"), sim_spice_describenetwork(net));
1214 			sim_spice_xprintf(sim_spice_file, TRUE, x_("\n"));
1215 		}
1216 	}
1217 
1218 	/* add nodes to SpiceCell */
1219 	for(spnet = sim_spice_firstnet; spnet != NOSPNET; spnet = spnet->nextnet)
1220 	{
1221 		spnet->spiceNet = new SpiceNet( spCell, spnet->netnumber, spnet->network );
1222 	}
1223 
1224 	/* now run through all components in the cell */
1225 	resistnum = capacnum = inductnum = diodenum = 1;
1226 
1227 	/* third pass through the node list, print it this time */
1228 	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1229 	{
1230 		/* handle sub-cell calls */
1231 		if (ni->proto->primindex == 0)
1232 		{
1233 			/* ignore recursive references (showing icon in contents) */
1234 			if (isiconof(ni->proto, np)) continue;
1235 
1236 			/* see if the node is arrayed */
1237 			nodewidth = ni->arraysize;
1238 			if (nodewidth < 1) nodewidth = 1;
1239 
1240 			/* look for a SPICE template on the prototype */
1241 			vartemplate = getvalkey((INTBIG)ni->proto, VNODEPROTO, VSTRING, sim_spice_preferedkey);
1242 			if (vartemplate == NOVARIABLE)
1243 				vartemplate = getvalkey((INTBIG)ni->proto, VNODEPROTO, VSTRING, sim_spice_template_key);
1244 
1245 			/* get the ports on this node (in proper order) */
1246 			cnp = contentsview(ni->proto);
1247 			if (cnp == NONODEPROTO) cnp = ni->proto; else
1248 			{
1249 				/* if there is a contents view, look for a SPICE template there, too */
1250 				if (vartemplate == NOVARIABLE)
1251 				{
1252 					vartemplate = getvalkey((INTBIG)cnp, VNODEPROTO, VSTRING, sim_spice_preferedkey);
1253 					if (vartemplate == NOVARIABLE)
1254 						vartemplate = getvalkey((INTBIG)cnp, VNODEPROTO, VSTRING, sim_spice_template_key);
1255 				}
1256 			}
1257 			netcount = sim_spice_getexportednetworks(cnp, &netlist, &subvdd, &subgnd, sim_spice_cdl);
1258 			err = 0;
1259 
1260 			/* if the node is arrayed, get the names of each instantiation */
1261 			if (nodewidth > 1)
1262 			{
1263 				var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name_key);
1264 				if (var == NOVARIABLE) nodewidth = 1; else
1265 				{
1266 					sigcount = net_evalbusname(APBUS, (CHAR *)var->addr, &nodenames,
1267 						NOARCINST, NONODEPROTO, 0);
1268 					if (sigcount != nodewidth) nodewidth = 1;
1269 				}
1270 			}
1271 
1272 			/* loop through each arrayed instantiation of this node */
1273 			for(nindex=0; nindex<nodewidth; nindex++)
1274 			{
1275 				for(net = cnp->firstnetwork; net != NONETWORK; net = net->nextnetwork)
1276 					net->temp1 = (INTBIG)NONETWORK;
1277 				for(pp = ni->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
1278 				{
1279 					cpp = equivalentport(ni->proto, pp, cnp);
1280 					if (cpp == NOPORTPROTO) continue;
1281 					net = NONETWORK;
1282 					for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
1283 						if (pi->proto == pp) break;
1284 					if (pi != NOPORTARCINST) net = pi->conarcinst->network; else
1285 					{
1286 						for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
1287 							if (pe->proto == pp) break;
1288 						if (pe != NOPORTEXPINST) net = pe->exportproto->network;
1289 					}
1290 					if (net == NONETWORK) continue;
1291 					if (net->buswidth > 1)
1292 					{
1293 						sigcount = net->buswidth;
1294 						if (nodewidth > 1 && cpp->network->buswidth * nodewidth == net->buswidth)
1295 						{
1296 							/* map wide bus to a particular instantiation of an arrayed node */
1297 							if (cpp->network->buswidth == 1)
1298 							{
1299 								cpp->network->temp1 = (INTBIG)net->networklist[nindex];
1300 							} else
1301 							{
1302 								for(i=0; i<cpp->network->buswidth; i++)
1303 									cpp->network->networklist[i]->temp1 =
1304 										(INTBIG)net->networklist[i + nindex*cpp->network->buswidth];
1305 							}
1306 						} else
1307 						{
1308 							if (cpp->network->buswidth != net->buswidth)
1309 							{
1310 								ttyputerr(_("***ERROR: port %s on node %s in cell %s is %d wide, but is connected/exported with width %d"),
1311 									pp->protoname, describenodeinst(ni), describenodeproto(np),
1312 										cpp->network->buswidth, net->buswidth);
1313 								sigcount = mini(sigcount, cpp->network->buswidth);
1314 								if (sigcount == 1) sigcount = 0;
1315 							}
1316 							cpp->network->temp1 = (INTBIG)net;
1317 							for(i=0; i<sigcount; i++)
1318 								cpp->network->networklist[i]->temp1 = (INTBIG)net->networklist[i];
1319 						}
1320 					} else
1321 					{
1322 						cpp->network->temp1 = (INTBIG)net;
1323 					}
1324 				}
1325 
1326 				/* handle self-defined models */
1327 				if (vartemplate != NOVARIABLE)
1328 				{
1329 					infstr = initinfstr();
1330 					for(pt = (CHAR *)vartemplate->addr; *pt != 0; pt++)
1331 					{
1332 						if (pt[0] != '$' || pt[1] != '(')
1333 						{
1334 							addtoinfstr(infstr, *pt);
1335 							continue;
1336 						}
1337 						start = pt + 2;
1338 						for(pt = start; *pt != 0; pt++)
1339 							if (*pt == ')') break;
1340 						save = *pt;
1341 						*pt = 0;
1342 						pp = getportproto(ni->proto, start);
1343 						if (pp != NOPORTPROTO)
1344 						{
1345 							/* port name found: use its spice node */
1346 							spnet = sim_spice_getnet(ni, pp->network);
1347 							if (spnet == NOSPNET)
1348 							{
1349 								if ((sim_spice_state&SPICENODENAMES) != 0)
1350 									formatinfstr(infstr, x_("UNNAMED%ld"), sim_spice_unnamednum++); else
1351 										formatinfstr(infstr, x_("%ld"), sim_spice_netindex++);
1352 								err++;
1353 							} else
1354 								addstringtoinfstr(infstr, sim_spice_netname(spnet->network, nodewidth, nindex));
1355 						} else
1356 						{
1357 							/* no port name found, look for variable name */
1358 							esnprintf(line, 100, x_("ATTR_%s"), start);
1359 							var = getval((INTBIG)ni, VNODEINST, -1, line);
1360 							if (var == NOVARIABLE)
1361 								var = getval((INTBIG)ni, VNODEINST, -1, start);
1362 							if (var == NOVARIABLE)
1363 							{
1364 								addstringtoinfstr(infstr, x_("??"));
1365 								err++;
1366 							} else
1367 							{
1368 								if (nodewidth > 1)
1369 								{
1370 									/* see if this name is arrayed, and pick a single entry from it if so */
1371 									count = net_evalbusname(APBUS, describesimplevariable(var),
1372 										&strings, NOARCINST, NONODEPROTO, 0);
1373 									if (count == nodewidth)
1374 										addstringtoinfstr(infstr, strings[nindex]); else
1375 											addstringtoinfstr(infstr, describesimplevariable(var));
1376 								} else
1377 								{
1378 									addstringtoinfstr(infstr, describesimplevariable(var));
1379 								}
1380 							}
1381 						}
1382 						*pt = save;
1383 						if (save == 0) break;
1384 					}
1385 					spInst = new SpiceInst( spCell, returninfstr(infstr) );
1386 					spInst->addParamM( ni );
1387 					continue;
1388 				}
1389 
1390 				if (nodewidth > 1)
1391 				{
1392 					pt = sim_spice_elementname(ni, 'X', &subcellindex, nodenames[nindex]);
1393 				} else
1394 				{
1395 					pt = sim_spice_elementname(ni, 'X', &subcellindex, 0);
1396 				}
1397 				spInst = new SpiceInst( spCell, pt );
1398 
1399 				for(i=0; i<netcount; i++)
1400 				{
1401 					net = netlist[i];
1402 					if (net->globalnet >= 0) continue;
1403 
1404 					rnet = (NETWORK *)net->temp1;
1405 					if (rnet == NONETWORK)
1406 					{
1407 						new SpiceBind( spInst );
1408 
1409 						/* do not report errors on power or ground net */
1410 						if (net == subvdd || net == subgnd) continue;
1411 						for(pp = cnp->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
1412 						{
1413 							if ((pp->userbits&STATEBITS) != OUTPORT &&
1414 								(pp->userbits&STATEBITS) != BIDIRPORT) continue;
1415 							if (pp->network == net) break;
1416 							if (pp->network->buswidth <= 1) continue;
1417 							for(j=0; j<pp->network->buswidth; j++)
1418 								if (pp->network->networklist[j] == net) break;
1419 							if (j < pp->network->buswidth) break;
1420 						}
1421 						if (pp != NOPORTPROTO) continue;
1422 						err++;
1423 						continue;
1424 					}
1425 					spnet = (SPNET *)rnet->temp1;
1426 					new SpiceBind( spInst, spnet->spiceNet );
1427 					/*if (spnet->netnumber == 0 && (sim_spice_state&SPICENODENAMES) == 0) continue;*/
1428 				}
1429 				if ((sim_spice_state&SPICENODENAMES) == 0 || sim_spice_machine == SPICE3)
1430 				{
1431 					for (i = 0; i < cnp->globalnetcount; i++)
1432 					{
1433 						net = cnp->globalnetworks[i];
1434 						if (net == NONETWORK) continue;
1435 						if (i >= 2)
1436 							i = net_findglobalnet(np, cnp->globalnetnames[i]);
1437 						rnet = np->globalnetworks[i];
1438 						spnet = (SPNET *)rnet->temp1;
1439 						new SpiceBind( spInst, spnet->spiceNet );
1440 					}
1441 				}
1442 				pname = 0;
1443 				if (sim_spice_cdl || (sim_spice_state&SPICECELLPARAM) == 0)
1444 				{
1445 					pname = parameterizedname(ni, sim_spice_cellname(ni->proto));
1446 
1447 					/* logical effort requires uniqueness for so marked insts, even if they are parameterized insts */
1448 					var = getvalkey((INTBIG)ni, VNODEINST, VINTEGER, sim_spice_nameuniqueid);
1449 					if (var != NOVARIABLE)
1450 					{
1451 						gettraversalpath(ni->parent, el_curwindowpart, &hier, &indexlist, &depth, 0);
1452 						infstr = initinfstr();
1453 						for(i=0; i<depth; i++)
1454 							addstringtoinfstr(infstr, describenodeinst(hier[i]));
1455 						addstringtoinfstr(infstr, describenodeinst(ni));
1456 						(void)allocstring(&pname, returninfstr(infstr), el_tempcluster);
1457 					}
1458 				}
1459 				CHAR *instCellName;
1460 				if (pname == 0)
1461 				{
1462 					instCellName = sim_spice_cellname(ni->proto);
1463 				} else
1464 				{
1465 					shortname = inparameterizedcells(ni->proto, pname);
1466 					instCellName = (shortname == 0 ? pname : shortname);
1467 				}
1468 				instCellName = sim_spice_legalname( instCellName );
1469 				SpiceCell *spinst = SpiceCell::findByName( instCellName );
1470 				if (spinst != 0) spInst->setInstCell( spinst ); else
1471 				{
1472 					ttyputerr(_("Warning: cannot determine name of instance %s in cell %s"),
1473 						instCellName, describenodeproto(np));
1474 				}
1475 				if (pname != 0) efree((CHAR *)pname);
1476 
1477 				if (!sim_spice_cdl && (sim_spice_state&SPICECELLPARAM) != 0)
1478 				{
1479 					/* add in parameters to this instance */
1480 					for(i=0; i<cnp->numvar; i++)
1481 					{
1482 						var = &cnp->firstvar[i];
1483 						if (TDGETISPARAM(var->textdescript) == 0) continue;
1484 						nivar = getvalkey((INTBIG)ni, VNODEINST, -1, var->key);
1485 						CHAR *paramStr = (nivar == NOVARIABLE ? (CHAR*)x_("??") : describesimplevariable(nivar));
1486 						spInst->addParam( new SpiceParam( paramStr ) );
1487 					}
1488 				}
1489 				spInst->addParamM( ni );
1490 			}
1491 			if (err != 0)
1492 			{
1493 				infstr = initinfstr();
1494 				formatinfstr(infstr, _("WARNING: subcell %s is not fully connected in cell %s"),
1495 					describenodeinst(ni), describenodeproto(np));
1496 				sim_spice_dumpstringerror(infstr, spInst);
1497 			}
1498 			continue;
1499 		}
1500 
1501 		/* get the type of this node */
1502 		state = nodefunction(ni);
1503 
1504 		/* handle resistors, inductors, capacitors, and diodes */
1505 		if (state == NPRESIST || state == NPCAPAC || state == NPECAPAC || state == NPINDUCT ||
1506 			state == NPDIODE || state == NPDIODEZ)
1507 		{
1508 			switch (state)
1509 			{
1510 				case NPRESIST:		/* resistor */
1511 					var = getvalkey((INTBIG)ni, VNODEINST, -1, sch_resistancekey);
1512 					if (var == NOVARIABLE) extra = x_(""); else
1513 					{
1514 						extra = describesimplevariable(var);
1515 						if (isanumber(extra))
1516 						{
1517 							purevalue = (float)eatof(extra);
1518 							extra = displayedunits(purevalue, VTUNITSRES, INTRESUNITOHM);
1519 						}
1520 					}
1521 					sim_spice_writetwoport(ni, state, extra, spCell, &resistnum, 1);
1522 					break;
1523 				case NPCAPAC:
1524 				case NPECAPAC:	/* capacitor */
1525 					var = getvalkey((INTBIG)ni, VNODEINST, -1, sch_capacitancekey);
1526 					if (var == NOVARIABLE) extra = x_(""); else
1527 					{
1528 						extra = describesimplevariable(var);
1529 						if (isanumber(extra))
1530 						{
1531 							purevalue = (float)eatof(extra);
1532 							extra = displayedunits(purevalue, VTUNITSCAP, INTCAPUNITFARAD);
1533 						}
1534 					}
1535 					sim_spice_writetwoport(ni, state, extra, spCell, &capacnum, 1);
1536 					break;
1537 				case NPINDUCT:		/* inductor */
1538 					var = getvalkey((INTBIG)ni, VNODEINST, -1, sch_inductancekey);
1539 					if (var == NOVARIABLE) extra = x_(""); else
1540 					{
1541 						extra = describesimplevariable(var);
1542 						if (isanumber(extra))
1543 						{
1544 							purevalue = (float)eatof(extra);
1545 							extra = displayedunits(purevalue, VTUNITSIND, INTINDUNITHENRY);
1546 						}
1547 					}
1548 					sim_spice_writetwoport(ni, state, extra, spCell, &inductnum, 1);
1549 					break;
1550 				case NPDIODE:		/* diode */
1551 				case NPDIODEZ:		/* Zener diode */
1552 					var = getvalkey((INTBIG)ni, VNODEINST, -1, sch_diodekey);
1553 					if (var == NOVARIABLE) extra = x_(""); else
1554 						extra = describesimplevariable(var);
1555 					sim_spice_writetwoport(ni, state, extra, spCell, &diodenum, 1);
1556 					break;
1557 			}
1558 			spInst->addParamM( ni );
1559 			continue;
1560 		}
1561 
1562 		/* the default is to handle everything else as a transistor */
1563 		if (state < NPTRANMOS || state > NPTRANS4)
1564 			continue;
1565 
1566 		/* get the source, gates, and drain for the transistor */
1567 		transistorports(ni, &gate, &gatedummy, &source, &drain);
1568 
1569 		/* determine the net numbers for these parts of the transistor */
1570 		gaten = sim_spice_getnet(ni, gate->network);     /* base */
1571 		sourcen = sim_spice_getnet(ni, source->network); /* emitter */
1572 		drainn = sim_spice_getnet(ni, drain->network);   /* collector */
1573 
1574 		/* make sure transistor is connected to nets */
1575 		if (sourcen == NOSPNET || gaten == NOSPNET || drainn == NOSPNET)
1576 		{
1577 			infstr = initinfstr();
1578 			formatinfstr(infstr, _("WARNING: %s not fully connected in cell %s"),
1579 				describenodeinst(ni), describenodeproto(np));
1580 			sim_spice_dumpstringerror(infstr, 0);
1581 		}
1582 
1583 		/* print source, gate, drain, and substrate */
1584 
1585 		/* get any special model information */
1586 		info = NULL;
1587 		var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, sch_spicemodelkey);
1588 		if (var != NOVARIABLE) info = (CHAR *)var->addr;
1589 
1590 		for(pp = ni->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
1591 			biasport = pp;
1592 		biasn = sim_spice_getnet(ni, biasport->network);
1593 		CHAR tranChar = 0;
1594 		switch (state)
1595 		{
1596 			case NPTRANSREF:			/* self-referential transistor */
1597 				tranChar = 'X';
1598 				biasn = negnet;
1599 				info = sim_spice_cellname(ni->proto);
1600 				break;
1601 			case NPTRANMOS:			/* NMOS (Enhancement) transistor */
1602 				tranChar = 'M';
1603 				biasn = negnet;
1604 				if (info == NULL) info = x_("N");
1605 				break;
1606 			case NPTRA4NMOS:		/* NMOS (Complementary) 4-port transistor */
1607 				tranChar = 'M';
1608 				if (info == NULL) info = x_("N");
1609 				break;
1610 			case NPTRADMOS:			/* DMOS (Depletion) transistor */
1611 				tranChar = 'M';
1612 				biasn = negnet;
1613 				if (info == NULL) info = x_("D");
1614 				break;
1615 			case NPTRA4DMOS:		/* DMOS (Depletion) 4-port transistor */
1616 				tranChar = 'M';
1617 				if (info == NULL) info = x_("D");
1618 				break;
1619 			case NPTRAPMOS:			/* PMOS (Complementary) transistor */
1620 				tranChar = 'M';
1621 				biasn = posnet;
1622 				if (info == NULL) info = x_("P");
1623 				break;
1624 			case NPTRA4PMOS:		/* PMOS (Complementary) 4-port transistor */
1625 				tranChar = 'M';
1626 				if (info == NULL) info = x_("P");
1627 				break;
1628 			case NPTRANPN:			/* NPN (Junction) transistor */
1629 				tranChar = 'Q';
1630 				biasn = subnet != NOSPNET ? subnet : 0;
1631 				if (info == NULL) info = x_("NBJT");
1632 				break;
1633 			case NPTRA4NPN:			/* NPN (Junction) 4-port transistor */
1634 				tranChar = 'Q';
1635 				if (info == NULL) info = x_("NBJT");
1636 				break;
1637 			case NPTRAPNP:			/* PNP (Junction) transistor */
1638 				tranChar = 'Q';
1639 				biasn = subnet != NOSPNET ? subnet : 0;
1640 				if (info == NULL) info = x_("PBJT");
1641 				break;
1642 			case NPTRA4PNP:			/* PNP (Junction) 4-port transistor */
1643 				tranChar = 'Q';
1644 				if (info == NULL) info = x_("PBJT");
1645 				break;
1646 			case NPTRANJFET:		/* NJFET (N Channel) transistor */
1647 				tranChar = 'J';
1648 				biasn = 0;
1649 				if (info == NULL) info = x_("NJFET");
1650 				break;
1651 			case NPTRA4NJFET:		/* NJFET (N Channel) 4-port transistor */
1652 				tranChar = 'J';
1653 				if (info == NULL) info = x_("NJFET");
1654 				break;
1655 			case NPTRAPJFET:		/* PJFET (P Channel) transistor */
1656 				tranChar = 'J';
1657 				biasn = 0;
1658 				if (info == NULL) info = x_("PJFET");
1659 				break;
1660 			case NPTRA4PJFET:		/* PJFET (P Channel) 4-port transistor */
1661 				tranChar = 'J';
1662 				if (info == NULL) info = x_("PJFET");
1663 				break;
1664 			case NPTRADMES:			/* DMES (Depletion) transistor */
1665 			case NPTRA4DMES:		/* DMES (Depletion) 4-port transistor */
1666 				tranChar = 'Z';
1667 				biasn = 0;
1668 				info = x_("DMES");
1669 				break;
1670 			case NPTRAEMES:			/* EMES (Enhancement) transistor */
1671 			case NPTRA4EMES:		/* EMES (Enhancement) 4-port transistor */
1672 				tranChar = 'Z';
1673 				biasn = 0;
1674 				info = x_("EMES");
1675 				break;
1676 			case NPTRANS:		/* special transistor */
1677 				tranChar = 'Q';
1678 				biasn = subnet != NOSPNET ? subnet : 0;
1679 				break;
1680 		}
1681 		CHAR *tranName = sim_spice_elementname(ni, tranChar, (tranChar == 'X' ? &subcellindex : &nodeindex), 0);
1682 		spInst = new SpiceInst( spCell, tranName );
1683 		new SpiceBind( spInst, (drainn != NOSPNET ? drainn->spiceNet : 0) );
1684 		new SpiceBind( spInst, (gaten != NOSPNET ? gaten->spiceNet : 0) );
1685 		new SpiceBind( spInst, (sourcen != NOSPNET ? sourcen->spiceNet : 0) );
1686 		if (biasn != 0)
1687 			new SpiceBind( spInst, (biasn != NOSPNET ? biasn->spiceNet : 0) );
1688 		if (info != NULL) spInst->addParam( new SpiceParam( info ) );
1689 
1690 		/* do not print area for self-referential transistors */
1691 		if (state == NPTRANSREF)
1692 		{
1693 			spInst->addParamM( ni );
1694 			continue;
1695 		}
1696 
1697 		/* compute length and width (or area for nonMOS transistors) */
1698 		nodelambda = lambdaofnode(ni);
1699 		reallambda = ni->parent->lib->lambda[sim_spice_tech->techindex];
1700 		transistorsize(ni, &lx, &ly);
1701 
1702 		if (lx >= 0 && ly >= 0)
1703 		{
1704 			if( (sim_spice_state&SPICEUSELAMBDAS) == 0)
1705 			{
1706 				/* write sizes in microns */
1707 				if (nodelambda != reallambda && nodelambda != 0)
1708 				{
1709 					lx = muldiv(lx, reallambda, nodelambda);
1710 					ly = muldiv(ly, reallambda, nodelambda);
1711 				}
1712 
1713 				a = sim_spice_mask_scale * lx;
1714 				b = sim_spice_mask_scale * ly;
1715 				if (state == NPTRANMOS  || state == NPTRADMOS  || state == NPTRAPMOS ||
1716 					state == NPTRA4NMOS || state == NPTRA4DMOS || state == NPTRA4PMOS ||
1717 					((state == NPTRANJFET || state == NPTRAPJFET || state == NPTRADMES ||
1718 					  state == NPTRAEMES) && (sim_spice_machine == SPICEHSPICE)))
1719 				{
1720 					esnprintf(line, 100, x_("L=%3.2fU"), scaletodispunit((INTBIG)a, DISPUNITMIC));
1721 					spInst->addParam( new SpiceParam( line ) );
1722 					esnprintf(line, 100, x_("W=%3.2fU"), scaletodispunit((INTBIG)b, DISPUNITMIC));
1723 					spInst->addParam( new SpiceParam( line ) );
1724 				}
1725 				if (state != NPTRANMOS && state != NPTRADMOS  && state != NPTRAPMOS &&
1726 					state != NPTRA4NMOS && state != NPTRA4DMOS && state != NPTRA4PMOS)
1727 				{
1728 					esnprintf(line, 100, x_("AREA=%3.2fP"), scaletodispunitsq((INTBIG)(a*b), DISPUNITMIC));
1729 					spInst->addParam( new SpiceParam( line ) );
1730 				}
1731 			} else
1732 				/* write sizes in lambda */
1733 			{
1734 				if (state == NPTRANMOS  || state == NPTRADMOS  || state == NPTRAPMOS ||
1735 					state == NPTRA4NMOS || state == NPTRA4DMOS || state == NPTRA4PMOS ||
1736 					((state == NPTRANJFET || state == NPTRAPJFET || state == NPTRADMES ||
1737 					  state == NPTRAEMES) && (sim_spice_machine == SPICEHSPICE)))
1738 				{
1739 					esnprintf(line, 100, x_("L=%4.2f"), scaletodispunit((INTBIG)lx, DISPUNITLAMBDA));
1740 					spInst->addParam( new SpiceParam( line ) );
1741 					esnprintf(line, 100, x_("W=%4.2f"), scaletodispunit((INTBIG)ly, DISPUNITLAMBDA));
1742 					spInst->addParam( new SpiceParam( line ) );
1743 				}
1744 				if (state != NPTRANMOS && state != NPTRADMOS  && state != NPTRAPMOS &&
1745 					state != NPTRA4NMOS && state != NPTRA4DMOS && state != NPTRA4PMOS)
1746 				{
1747 					esnprintf(line, 100, x_("AREA=%4.2f"), scaletodispunitsq((INTBIG)(lx*ly), DISPUNITLAMBDA));
1748 					spInst->addParam( new SpiceParam( line ) );
1749 				}
1750 			}
1751 		} else
1752 		{
1753 			/* if there is nonnumeric size on a schematic transistor, get it */
1754 			varl = getvalkey((INTBIG)ni, VNODEINST, -1, el_attrkey_length);
1755 			varw = getvalkey((INTBIG)ni, VNODEINST, -1, el_attrkey_width);
1756 			if (varl != NOVARIABLE && varw != NOVARIABLE)
1757 			{
1758 				if( (sim_spice_state&SPICEUSELAMBDAS) == 0)
1759 				{
1760 					/* write sizes in microns */
1761 					pt = describevariable(varl, -1, -1);
1762 					if (isanumber(pt))
1763 					{
1764 						lx = muldiv(atofr(pt), nodelambda, WHOLE);
1765 						if (nodelambda != reallambda && nodelambda != 0)
1766 							lx = muldiv(lx, reallambda, nodelambda);
1767 						a = sim_spice_mask_scale * lx;
1768 						esnprintf(line, 100, x_("L=%3.2fU"), scaletodispunit((INTBIG)a, DISPUNITMIC));
1769 					} else esnprintf(line, 100, x_("L=%s"), pt);
1770 					spInst->addParam( new SpiceParam( line ) );
1771 					pt = describevariable(varw, -1, -1);
1772 					if (isanumber(pt))
1773 					{
1774 						lx = muldiv(atofr(pt), nodelambda, WHOLE);
1775 						if (nodelambda != reallambda && nodelambda != 0)
1776 							lx = muldiv(lx, reallambda, nodelambda);
1777 						a = sim_spice_mask_scale * lx;
1778 						esnprintf(line, 100, x_("W=%3.2fU"), scaletodispunit((INTBIG)a, DISPUNITMIC));
1779 					} else esnprintf(line, 100, x_("W=%s"), pt);
1780 					spInst->addParam( new SpiceParam( line ) );
1781 				} else
1782 				{
1783 					/* write sizes in lambda */
1784 					pt = describevariable(varl, -1, -1);
1785 					if (isanumber(pt))
1786 					{
1787 						lx = atofr(pt);
1788 						esnprintf(line, 100, x_("L=%4.2f"), scaletodispunit((INTBIG)lx, DISPUNITLAMBDA));
1789 					} else esnprintf(line, 100, x_("L=%s"), pt);
1790 					spInst->addParam( new SpiceParam( line ) );
1791 					pt = describevariable(varw, -1, -1);
1792 					if (isanumber(pt))
1793 					{
1794 						lx = atofr(pt);
1795 						esnprintf(line, 100, x_("W=%4.2f"), scaletodispunit((INTBIG)lx, DISPUNITLAMBDA));
1796 					} else esnprintf(line, 100, x_("W=%s"), pt);
1797 					spInst->addParam( new SpiceParam( line ) );
1798 				}
1799 			}
1800 		}
1801 		spInst->addParamM( ni );
1802 
1803 		/* make sure transistor is connected to nets */
1804 		if (sourcen == NOSPNET || gaten == NOSPNET || drainn == NOSPNET) continue;
1805 
1806 		/* compute area of source and drain */
1807 		if (!sim_spice_cdl)
1808 		{
1809 			if (state == NPTRANMOS  || state == NPTRADMOS  || state == NPTRAPMOS ||
1810 				state == NPTRA4NMOS || state == NPTRA4DMOS || state == NPTRA4PMOS)
1811 			{
1812 				switch (state)
1813 				{
1814 					case NPTRADMOS:
1815 					case NPTRA4DMOS:
1816 					case NPTRANMOS:
1817 					case NPTRA4NMOS: i = ISNTYPE; break;
1818 					case NPTRAPMOS:
1819 					case NPTRA4PMOS: i = ISPTYPE; break;
1820 					default:         i = ISNONE;  break;
1821 				}
1822 
1823 				/* we should not look at the ISNONE entry of components[],
1824 				 * but the diffareas will be zero anyhow,
1825 				 */
1826 				if (sourcen->components[i] != 0)
1827 				{
1828 					a = scaletodispunitsq((INTBIG)(sourcen->diffarea[i] / sourcen->components[i]),
1829 						DISPUNITMIC);
1830 					if (a > 0.0)
1831 					{
1832 						esnprintf(line, 100, x_("AS=%5.2fP"), a);
1833 						spInst->addParam( new SpiceParam( line ) );
1834 					}
1835 				}
1836 				if (drainn->components[i] != 0)
1837 				{
1838 					b = scaletodispunitsq((INTBIG)(drainn->diffarea[i] / drainn->components[i]),
1839 						DISPUNITMIC);
1840 					if (b > 0.0)
1841 					{
1842 						esnprintf(line, 100, x_("AD=%5.2fP"), b);
1843 						spInst->addParam( new SpiceParam( line ) );
1844 					}
1845 				}
1846 
1847 				/* compute perimeters of source and drain */
1848 				if (sourcen->components[i] != 0)
1849 				{
1850 					a = scaletodispunit((INTBIG)(sourcen->diffperim[i] / sourcen->components[i]),
1851 						DISPUNITMIC);
1852 					if (a > 0.0)
1853 					{
1854 						esnprintf(line, 100, x_("PS=%5.2fU"), a);
1855 						spInst->addParam( new SpiceParam( line ) );
1856 					}
1857 				}
1858 				if (drainn->components[i] != 0)
1859 				{
1860 					b = scaletodispunit((INTBIG)(drainn->diffperim[i] / drainn->components[i]),
1861 						DISPUNITMIC);
1862 					if (b > 0.0)
1863 					{
1864 						esnprintf(line, 100, x_("PD=%5.2fU"), b);
1865 						spInst->addParam( new SpiceParam( line ) );
1866 					}
1867 				}
1868 			}
1869 		}
1870 	}
1871 	spCell->printSpice();
1872 
1873 	/* print resistances and capacitances */
1874 	if (!sim_spice_cdl)
1875 	{
1876 		if ((sim_spice_state&SPICERESISTANCE) != 0)
1877 		{
1878 			/* print parasitic capacitances */
1879 			first = 1;
1880 			for(spnet = sim_spice_firstnet; spnet != NOSPNET; spnet = spnet->nextnet)
1881 			{
1882 				spnet->resistance = scaletodispunitsq((INTBIG)spnet->resistance, DISPUNITMIC);
1883 				if (spnet->resistance > sim_spice_min_resist)
1884 				{
1885 					if (first != 0)
1886 					{
1887 						first = 0;
1888 						sim_spice_xprintf(sim_spice_file, TRUE, x_("** Extracted Parasitic Elements:\n"));
1889 					}
1890 					sim_spice_xprintf(sim_spice_file, FALSE, x_("R%ld ? ? %9.2f\n"), resistnum++, spnet->resistance);
1891 				}
1892 
1893 				if (spnet->network == sim_spice_gnd) continue;
1894 				if (spnet->capacitance > sim_spice_min_capac)
1895 				{
1896 					if (first != 0)
1897 					{
1898 						first = 0;
1899 						sim_spice_xprintf(sim_spice_file, TRUE, x_("** Extracted Parasitic Elements:\n"));
1900 					}
1901 					sim_spice_xprintf(sim_spice_file, FALSE, x_("C%ld%s 0 %9.2fF\n"), capacnum++,
1902 						sim_spice_nodename(spnet), spnet->capacitance);
1903 				}
1904 			}
1905 		}
1906 	}
1907 
1908 	/* write out any directly-typed SPICE cards */
1909 	if (sim_spice_card_key == 0)
1910 		sim_spice_card_key = makekey(x_("SIM_spice_card"));
1911 	spCell->setup();
1912 	if (top && !sim_spice_cdl && sim_spice_machine == SPICEGNUCAP)
1913 		SpiceCell::traverseAll();
1914 	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1915 	{
1916 		if (ni->proto != gen_invispinprim) continue;
1917 		var = getvalkey((INTBIG)ni, VNODEINST, -1, sim_spice_card_key);
1918 		if (var == NOVARIABLE) continue;
1919 		if ((var->type&VTYPE) != VSTRING) continue;
1920 		if ((var->type&VDISPLAY) == 0) continue;
1921 		if ((var->type&VISARRAY) == 0)
1922 		{
1923 			sim_spice_xprintf(sim_spice_file, FALSE, x_("%s\n"), (CHAR *)var->addr);
1924 		} else
1925 		{
1926 			len = getlength(var);
1927 			for(i=0; i<len; i++)
1928 				sim_spice_xprintf(sim_spice_file, FALSE, x_("%s\n"), ((CHAR **)var->addr)[i]);
1929 		}
1930 	}
1931 
1932 	/*
1933 	 * Now we're finished writing the subcircuit.
1934 	 * Only the top-level cell can contain meters and connections.
1935 	 */
1936 	if (top && !sim_spice_cdl)
1937 	{
1938 		/* if no voltmeters, print metered sources anyway */
1939 		if (*sim_spice_printcard != '\0')
1940 		{
1941 			sim_spice_puts((CHAR *)(sim_spice_state&SPICEPLOT ? x_(".PLOT") : x_(".PRINT")),
1942 				FALSE);
1943 
1944 			if (sim_spice_tran) sim_spice_puts(x_(" TRAN"), FALSE); else
1945 				if (sim_spice_dc) sim_spice_puts(x_(" DC"), FALSE); else
1946 					if (sim_spice_ac) sim_spice_puts(x_(" AC"), FALSE);
1947 
1948 			/* write what we have already (from metered sources) */
1949 			sim_spice_puts(sim_spice_printcard, FALSE);
1950 			sim_spice_xprintf(sim_spice_file, FALSE, x_("\n"));
1951 		}
1952 		efree(sim_spice_printcard);
1953 
1954 		/* miscellaneous checks */
1955 		for(spnet = sim_spice_firstnet; spnet != NOSPNET; spnet = spnet->nextnet)
1956 		{
1957 			for (i = 0; i < DIFFTYPES; i++)
1958 			{
1959 				if (spnet->diffarea[i] == 0.0 || spnet->components[i] != 0) continue;
1960 
1961 				/* do not issue errors for active area on supply rails (probably well contacts) */
1962 				if (spnet->network == sim_spice_vdd || spnet->network == sim_spice_gnd) continue;
1963 				switch (i)
1964 				{
1965 					case ISNTYPE:
1966 						uncon_diff_type = x_(" N-type");
1967 						break;
1968 					case ISPTYPE:
1969 						uncon_diff_type = x_(" P-type");
1970 						break;
1971 					case ISNONE:
1972 					default:
1973 						uncon_diff_type = x_("");
1974 						break;
1975 				}
1976 				infstr = initinfstr();
1977 				formatinfstr(infstr, _("WARNING: SPICE node%s has unconnected%s device diffusion in cell %s"),
1978 					sim_spice_nodename(spnet), uncon_diff_type, describenodeproto(np));
1979 				sim_spice_dumpstringerror(infstr, 0);
1980 			}
1981 		}
1982 	} else
1983 	{
1984 		if (paramname != 0)
1985 		{
1986 			/* parameterized subcircuit name */
1987 			sim_spice_xprintf(sim_spice_file, FALSE, x_(".ENDS %s\n"), sim_spice_legalname(paramname));
1988 		} else
1989 		{
1990 			sim_spice_xprintf(sim_spice_file, FALSE, x_(".ENDS %s\n"), sim_spice_legalname(sim_spice_cellname(np)));
1991 		}
1992 	}
1993 
1994 	/* free the net modules */
1995 	for(spnet = sim_spice_firstnet; spnet != NOSPNET; spnet = nspnet)
1996 	{
1997 		nspnet = spnet->nextnet;
1998 		sim_spice_freespnet(spnet);
1999 	}
2000 	if (backannotate) return(1);
2001 	return(0);
2002 }
2003 
2004 /******************** DECK GENERATION SUPPORT ********************/
2005 
2006 /*
2007  * write a header for "cell" to spice deck "sim_spice_file"
2008  * The model cards come from a file specified by tech:~.SIM_spice_model_file
2009  * or else tech:~.SIM_spice_header_level%ld
2010  * The spice model file can be located in el_libdir
2011  */
sim_spice_writeheader(NODEPROTO * cell)2012 void sim_spice_writeheader(NODEPROTO *cell)
2013 {
2014 	REGISTER INTBIG i, j, level, pathlen, liblen, lambda;
2015 	time_t cdate, rdate;
2016 	REGISTER VARIABLE *var;
2017 	CHAR hvar[80], *truefile;
2018 	REGISTER CHAR **name, *pt;
2019 	REGISTER void *infstr;
2020 	CHAR headerpath[500];
2021 	FILE *io;
2022 
2023 	/* Print the header line for SPICE  */
2024 	sim_spice_xprintf(sim_spice_file, TRUE, x_("*** SPICE deck for cell %s from library %s\n"),
2025 		describenodeproto(cell), cell->lib->libname);
2026 	us_emitcopyright(sim_spice_file, x_("*** "), x_(""));
2027 	if ((us_useroptions&NODATEORVERSION) == 0)
2028 	{
2029 		cdate = (time_t)cell->creationdate;
2030 		if (cdate != 0)
2031 			sim_spice_xprintf(sim_spice_file, TRUE, x_("*** Created on %s\n"), timetostring(cdate));
2032 		rdate = (time_t)cell->revisiondate;
2033 		if (rdate != 0)
2034 			sim_spice_xprintf(sim_spice_file, TRUE, x_("*** Last revised on %s\n"), timetostring(rdate));
2035 		(void)esnprintf(hvar, 80, x_("%s"), timetostring(getcurrenttime()));
2036 		sim_spice_xprintf(sim_spice_file, TRUE, x_("*** Written on %s by Electric VLSI Design System, %s\n"),
2037 			hvar, el_version);
2038 	} else
2039 	{
2040 		sim_spice_xprintf(sim_spice_file, TRUE, x_("*** Written by Electric VLSI Design System\n"));
2041 	}
2042 
2043 	sim_spice_xprintf(sim_spice_file, TRUE, x_("*** UC SPICE *** , MIN_RESIST %f, MIN_CAPAC %fFF\n"),
2044 		sim_spice_min_resist, sim_spice_min_capac);
2045 	sim_spice_puts(x_(".OPTIONS NOMOD NOPAGE\n"), FALSE);
2046 
2047 	/* if sizes to be written in lambda, tell spice conversion factor */
2048 	if( (sim_spice_state&SPICEUSELAMBDAS) != 0)
2049 	{
2050 		lambda = cell->lib->lambda[sim_spice_tech->techindex];
2051 		sim_spice_xprintf(sim_spice_file, FALSE, x_("*** Lambda Conversion ***\n"));
2052 		sim_spice_xprintf(sim_spice_file, FALSE, x_(".opt scale=%3.3fU\n\n"), scaletodispunit((INTBIG)lambda, DISPUNITMIC));
2053 	}
2054 
2055 	/* see if spice model/option cards from file if specified */
2056 	var = getval((INTBIG)sim_spice_tech, VTECHNOLOGY, VSTRING, x_("SIM_spice_model_file"));
2057 	if (var != NOVARIABLE)
2058 	{
2059 		pt = (CHAR *)var->addr;
2060 		if (estrncmp(pt, x_(":::::"), 5) == 0)
2061 		{
2062 			/* extension specified: look for a file with the cell name and that extension */
2063 			estrcpy(headerpath, truepath(cell->lib->libfile));
2064 			pathlen = estrlen(headerpath);
2065 			liblen = estrlen(skippath(headerpath));
2066 			if (liblen < pathlen) headerpath[pathlen-liblen-1] = 0; else
2067 				headerpath[0] = 0;
2068 			infstr = initinfstr();
2069 			formatinfstr(infstr, x_("%s%c%s.%s"), headerpath, DIRSEP, sim_spice_cellname(cell), &pt[5]);
2070 			pt = returninfstr(infstr);
2071 			if (fileexistence(pt) == 1)
2072 			{
2073 				xprintf(sim_spice_file, x_("* Model cards are described in this file:\n"));
2074 				sim_spice_addincludefile(pt);
2075 				return;
2076 			}
2077 		} else
2078 		{
2079 			/* normal header file specified */
2080 			xprintf(sim_spice_file, x_("* Model cards are described in this file:\n"));
2081 			io = xopen(pt, el_filetypetext, el_libdir, &truefile);
2082 			if (io == 0)
2083 			{
2084 				ttyputmsg(_("Warning: cannot find model file '%s'"), pt);
2085 			} else
2086 			{
2087 				pt = truefile;
2088 			}
2089 			sim_spice_addincludefile(pt);
2090 			return;
2091 		}
2092 	}
2093 
2094 	/* no header files: write predefined header for this level and technology */
2095 	var = getvalkey((INTBIG)sim_tool, VTOOL, VINTEGER, sim_spice_levelkey);
2096 	if (var == NOVARIABLE) level = 1; else
2097 		level = var->addr;
2098 	(void)esnprintf(hvar, 80, x_("SIM_spice_header_level%ld"), level);
2099 	var = getval((INTBIG)sim_spice_tech, VTECHNOLOGY, VSTRING|VISARRAY, hvar);
2100 	if (var != NOVARIABLE)
2101 	{
2102 		name = (CHAR **)var->addr;
2103 		j = getlength(var);
2104 		for(i=0; i<j; i++) sim_spice_xprintf(sim_spice_file, FALSE, x_("%s\n"), name[i]);
2105 	} else ttyputerr(_("WARNING: no model cards for SPICE level %ld in %s technology"),
2106 		level, sim_spice_tech->techname);
2107 }
2108 
2109 /*
2110  * Write a trailer from an external file, defined as a variable on
2111  * the current technology in this library: tech:~.SIM_spice_trailer_file
2112  * if it is available.
2113  */
sim_spice_writetrailer(NODEPROTO * cell)2114 void sim_spice_writetrailer(NODEPROTO *cell)
2115 {
2116 	REGISTER INTBIG pathlen, liblen;
2117 	VARIABLE *var;
2118 	REGISTER void *infstr;
2119 	REGISTER CHAR *pt;
2120 	CHAR trailerpath[500];
2121 
2122 	/* get spice trailer cards from file if specified */
2123 	var = getval((INTBIG)sim_spice_tech, VTECHNOLOGY, VSTRING, x_("SIM_spice_trailer_file"));
2124 	if (var != NOVARIABLE)
2125 	{
2126 		pt = (CHAR *)var->addr;
2127 		if (estrncmp(pt, x_(":::::"), 5) == 0)
2128 		{
2129 			/* extension specified: look for a file with the cell name and that extension */
2130 			estrcpy(trailerpath, truepath(cell->lib->libfile));
2131 			pathlen = estrlen(trailerpath);
2132 			liblen = estrlen(skippath(trailerpath));
2133 			if (liblen < pathlen) trailerpath[pathlen-liblen-1] = 0; else
2134 				trailerpath[0] = 0;
2135 			infstr = initinfstr();
2136 			formatinfstr(infstr, x_("%s%c%s.%s"), trailerpath, DIRSEP, sim_spice_cellname(cell), &pt[5]);
2137 			pt = returninfstr(infstr);
2138 			if (fileexistence(pt) == 1)
2139 			{
2140 				xprintf(sim_spice_file, x_("* Trailer cards are described in this file:\n"));
2141 				sim_spice_addincludefile(pt);
2142 			}
2143 		} else
2144 		{
2145 			/* normal trailer file specified */
2146 			xprintf(sim_spice_file, x_("* Trailer cards are described in this file:\n"));
2147 			sim_spice_addincludefile((CHAR *)var->addr);
2148 		}
2149 	}
2150 }
2151 
2152 /*
2153  * Function to write a two port device to the file. If the flag 'report'
2154  * is set, then complain about the missing connections.
2155  * Determine the port connections from the portprotos in the instance
2156  * prototype. Get the part number from the 'part' number value;
2157  * increment it. The type of device is declared in type; extra is the string
2158  * data acquired before calling here.
2159  * If the device is connected to the same net at both ends, do not
2160  * write it. Is this OK?
2161  */
sim_spice_writetwoport(NODEINST * ni,INTBIG type,CHAR * extra,SpiceCell * cell,INTBIG * part,INTBIG report)2162 void sim_spice_writetwoport(NODEINST *ni, INTBIG type, CHAR *extra,
2163 	SpiceCell *cell, INTBIG *part, INTBIG report)
2164 {
2165 	REGISTER PORTPROTO *pp1, *pp2;
2166 	REGISTER SPNET *end1, *end2;
2167 	REGISTER void *infstr;
2168 	CHAR partChar;
2169 
2170 	pp1 = ni->proto->firstportproto;
2171 	pp2 = pp1->nextportproto;
2172 	end1 = sim_spice_getnet(ni, pp1->network);
2173 	end2 = sim_spice_getnet(ni, pp2->network);
2174 
2175 	/* make sure the component is connected to nets */
2176 	if (end1 == NOSPNET || end2 == NOSPNET)
2177 	{
2178 		infstr = initinfstr();
2179 		formatinfstr(infstr, _("WARNING: %s component not fully connected in cell %s"),
2180 			describenodeinst(ni), describenodeproto(ni->parent));
2181 		sim_spice_dumpstringerror(infstr, 0);
2182 	}
2183 	if (end1 != NOSPNET && end2 != NOSPNET)
2184 		if (end1->netnumber == end2->netnumber)
2185 	{
2186 		if (report)
2187 		{
2188 			infstr = initinfstr();
2189 			formatinfstr(infstr, _("WARNING: %s component appears to be shorted on net %s in cell %s"),
2190 				describenodeinst(ni), sim_spice_nodename(end1), describenodeproto(ni->parent));
2191 			sim_spice_dumpstringerror(infstr, 0);
2192 		}
2193 		return;
2194 	}
2195 
2196 	/* next line is not really necessary any more */
2197 	switch (type)
2198 	{
2199 		case NPRESIST:		/* resistor */
2200 			partChar = 'R';
2201 			break;
2202 		case NPCAPAC:	/* capacitor */
2203 		case NPECAPAC:
2204 			partChar = 'C';
2205 			break;
2206 		case NPINDUCT:		/* inductor */
2207 			partChar = 'L';
2208 			break;
2209 		case NPDIODE:		/* diode */
2210 		case NPDIODEZ:		/* Zener diode */
2211 			partChar = 'D';
2212 			break;
2213 		default:
2214 			return;
2215 	}
2216 	SpiceInst *spInst = new SpiceInst( cell, sim_spice_elementname(ni, partChar, part, 0));
2217 
2218 	new SpiceBind( spInst, (end2 != NOSPNET ? end2->spiceNet : 0) );
2219 	new SpiceBind( spInst, (end1 != NOSPNET ? end1->spiceNet : 0) );  /* note order */
2220 	if ((type == NPDIODE || type == NPDIODEZ) && extra[0] == 0) extra = x_("DIODE");
2221 	spInst->addParam( new SpiceParam( extra ) );
2222 }
2223 
2224 /******************** PARASITIC CALCULATIONS ********************/
2225 
2226 /*
2227  * routine to recursively determine the area of diffusion and capacitance
2228  * associated with port "pp" of nodeinst "ni".  If the node is mult_layer, then
2229  * determine the dominant capacitance layer, and add its area; all other
2230  * layers will be added as well to the extra_area total.
2231  * Continue out of the ports on a complex cell
2232  */
sim_spice_nodearea(SPNET * spnet,NODEINST * ni,PORTPROTO * pp)2233 void sim_spice_nodearea(SPNET *spnet, NODEINST *ni, PORTPROTO *pp)
2234 {
2235 	REGISTER INTBIG tot, i, function;
2236 	REGISTER INTBIG fun;
2237 	BOOLEAN isabox;
2238 	XARRAY trans;
2239 	INTBIG lx, hx, ly, hy;
2240 	INTBIG dominant;
2241 	REGISTER POLYGON *poly, *lastpoly, *firstpoly;
2242 	REGISTER PORTARCINST *pi;
2243 	REGISTER TECHNOLOGY *tech;
2244 	float worst, cap;
2245 
2246 	/* make sure this node hasn't been completely examined */
2247 	if (ni->temp1 == 2) return;
2248 	if (ni->temp2 == (INTBIG)pp->network) return;	/* Is this recursive? */
2249 	ni->temp2 = (INTBIG)pp->network;
2250 
2251 	/* cells have no area or capacitance (for now) */
2252 	if (ni->proto->primindex != 0)  /* No area for complex nodes */
2253 	{
2254 		/* assign new state of this node */
2255 		function = nodefunction(ni);
2256 		if (function == NPCONGROUND || function == NPCONPOWER)
2257 			ni->temp1 = 2; else
2258 				ni->temp1 = 1;
2259 
2260 		/* initialize to examine the polygons on this node */
2261 		tech = ni->proto->tech;
2262 		makerot(ni, trans);
2263 
2264 		/*
2265 		 * NOW!  A fudge to make sure that well capacitors mask out the capacity
2266 		 * to substrate of their top plate polysilicon  or metal
2267 		 */
2268 		if (function == NPCAPAC || function == NPECAPAC) dominant = -1; else dominant = -2;
2269 		if (function == NPTRANMOS || function == NPTRA4NMOS ||
2270 			function == NPTRAPMOS || function == NPTRA4PMOS ||
2271 			function == NPTRADMOS || function == NPTRA4DMOS ||
2272 			function == NPTRAEMES || function == NPTRA4EMES ||
2273 			function == NPTRADMES || function == NPTRA4DMES)
2274 				function = NPTRANMOS;   /* One will do */
2275 
2276 		/* make linked list of polygons */
2277 		lastpoly = firstpoly = NOPOLYGON;
2278 		tot = nodeEpolys(ni, 0, NOWINDOWPART);
2279 		for(i=0; i<tot; i++)
2280 		{
2281 			poly = sim_spice_allocpolygon();
2282 			if (poly == NOPOLYGON) break;
2283 			shapeEnodepoly(ni, i, poly);
2284 
2285 			/* make sure this layer connects electrically to the desired port */
2286 			if (poly->portproto == NOPORTPROTO)
2287 			{
2288 				sim_spice_freepolygon(poly);   continue;
2289 			}
2290 			if (poly->portproto->network != pp->network)
2291 			{
2292 				sim_spice_freepolygon(poly);   continue;
2293 			}
2294 
2295 			/* don't bother with layers without capacity */
2296 			if ((sim_spice_layerisdiff(tech, poly->layer) == ISNONE) &&
2297 				(sim_spice_capacitance(tech, poly->layer) == 0.0))
2298 			{
2299 				sim_spice_freepolygon(poly);   continue;
2300 			}
2301 
2302 			/* leave out the gate capacitance of transistors */
2303 			if (function == NPTRANMOS)
2304 			{
2305 				fun = layerfunction(tech, poly->layer);
2306 				if ((fun & LFPSEUDO) == 0 && layerispoly(fun))
2307 				{
2308 					sim_spice_freepolygon(poly);   continue;
2309 				}
2310 			}
2311 			if (lastpoly != NOPOLYGON) lastpoly->nextpolygon = poly; else
2312 				firstpoly = poly;
2313 			lastpoly = poly;
2314 		}
2315 
2316 		/* do we need to test the layers? */
2317 		if (dominant != -1)
2318 		{
2319 			if (tot != 0 && firstpoly != NOPOLYGON) dominant = firstpoly->layer;
2320 
2321 			/* find the layer that will contribute the maximum capacitance */
2322 			if (tot > 1 && tech == el_curtech)
2323 			{
2324 				worst = 0.0;
2325 				for(poly = firstpoly; poly != NOPOLYGON; poly = poly->nextpolygon)
2326 				{
2327 					if (sim_spice_layerisdiff(tech, poly->layer) != ISNONE)
2328 					{
2329 						dominant = -1;      /* flag for diffusion on this port */
2330 						break;
2331 					} else
2332 					{
2333 						cap = (float)fabs(areapoly(poly));
2334 						if (cap * sim_spice_capacitance(tech, poly->layer) > worst)
2335 						{
2336 							worst = cap;
2337 							dominant = poly->layer;
2338 						}
2339 					}
2340 				}
2341 			}
2342 		}
2343 
2344 		for(poly = firstpoly; poly != NOPOLYGON; poly = poly->nextpolygon)
2345 		{
2346 			/* get the area of this polygon */
2347 			xformpoly(poly, trans);
2348 			isabox = isbox(poly, &lx, &hx, &ly, &hy);
2349 			if (tech != el_curtech || !isabox) continue;
2350 			sim_spice_storebox(tech, poly->layer, hx-lx, hy-ly, (lx+hx)/2, (ly+hy)/2);
2351 			if (sim_spice_layerisdiff(tech, poly->layer) == ISNONE &&
2352 				poly->layer != dominant)
2353 					sim_spice_extra_area[poly->layer] += (float)fabs(areapoly(poly));
2354 		}
2355 
2356 		/* free the polygons */
2357 		while (firstpoly != NOPOLYGON)
2358 		{
2359 			poly = firstpoly;
2360 			firstpoly = firstpoly->nextpolygon;
2361 			sim_spice_freepolygon(poly);
2362 		}
2363 	}
2364 	for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
2365 		if (pi->proto->network == pp->network)
2366 			sim_spice_arcarea(spnet, pi->conarcinst);
2367 	ni->temp2 = 0;      /* reset for next net pass */
2368 }
2369 
2370 /*
2371  * routine to recursively determine the area of diffusion, capacitance, (NOT
2372  * resistance) on arc "ai". If the arc contains active device diffusion, then
2373  * it will contribute to the area of sources and drains, and the other layers
2374  * will be ignored. This is not quite the same as the rule used for
2375  * contact (node) structures. Note: the earlier version of this
2376  * function assumed that diffusion arcs would always have zero capacitance
2377  * values for the other layers; this produces an error if any of these layers
2378  * have non-zero values assigned for other reasons. So we will check for the
2379  * function of the arc, and if it contains active device, we will ignore any
2380  * other layers
2381  */
sim_spice_arcarea(SPNET * spnet,ARCINST * ai)2382 void sim_spice_arcarea(SPNET *spnet, ARCINST *ai)
2383 {
2384 	REGISTER INTBIG i, tot;
2385 	REGISTER TECHNOLOGY *tech;
2386 	static POLYGON *poly = NOPOLYGON;
2387 	INTBIG lx, hx, ly, hy;
2388 	BOOLEAN j;
2389 	INTBIG isdiffarc;
2390 
2391 	if (ai->temp1 != 0) return;
2392 	ai->temp1++;
2393 
2394 	(void)needstaticpolygon(&poly, 4, sim_tool->cluster);
2395 
2396 	tot = arcpolys(ai, NOWINDOWPART);
2397 	tech = ai->proto->tech;
2398 	isdiffarc = sim_spice_arcisdiff(ai);    /* check arc function */
2399 	for(i=0; i<tot; i++)
2400 	{
2401 		shapearcpoly(ai, i, poly);
2402 		j = isbox(poly, &lx, &hx, &ly, &hy);
2403 		if (tech != el_curtech || !j) continue;
2404 		if ((layerfunction(tech, poly->layer)&LFPSEUDO) != 0) continue;
2405 		if (sim_spice_layerisdiff(tech, poly->layer) != ISNONE ||
2406 			(isdiffarc == 0 && sim_spice_capacitance(tech, poly->layer) > 0.0))
2407 				sim_spice_storebox(tech, poly->layer, hx-lx, hy-ly, (lx+hx)/2, (ly+hy)/2);
2408 	}
2409 
2410 	/* propagate to all of the nodes on this arc */
2411 	for(i=0; i<2; i++)
2412 		sim_spice_nodearea(spnet, ai->end[i].nodeinst, ai->end[i].portarcinst->proto);
2413 }
2414 
2415 /*
2416  * Routine to store a box on layer "layer" into the box merging system
2417  */
sim_spice_storebox(TECHNOLOGY * tech,INTBIG layer,INTBIG xs,INTBIG ys,INTBIG xc,INTBIG yc)2418 void sim_spice_storebox(TECHNOLOGY *tech, INTBIG layer, INTBIG xs, INTBIG ys, INTBIG xc, INTBIG yc)
2419 {
2420 	INTBIG type;
2421 
2422 	if ((type = sim_spice_layerisdiff(tech, layer)) != ISNONE)
2423 	{
2424 		if (sim_spice_diffusion_index[type] < 0)
2425 			sim_spice_diffusion_index[type] = layer; else
2426 				layer = sim_spice_diffusion_index[type];
2427 	}
2428 	mrgstorebox(layer, tech, xs, ys, xc, yc);
2429 }
2430 
2431 /*
2432  * routine to obtain a polygon from the box merging system
2433  */
sim_spice_evalpolygon(INTBIG layer,TECHNOLOGY * tech,INTBIG * xbuf,INTBIG * ybuf,INTBIG count)2434 void sim_spice_evalpolygon(INTBIG layer, TECHNOLOGY *tech, INTBIG *xbuf, INTBIG *ybuf, INTBIG count)
2435 {
2436 	REGISTER INTBIG perim;
2437 	float area;
2438 	REGISTER INTBIG i, j;
2439 
2440 	/* compute perimeter */
2441 	perim = 0;
2442 	for(i=0; i<count; i++)
2443 	{
2444 		if (i == 0) j = count-1; else j = i-1;
2445 		perim += computedistance(xbuf[j], ybuf[j], xbuf[i], ybuf[i]);
2446 	}
2447 
2448 	/* get area */
2449 	area = areapoints(count, xbuf, ybuf);
2450 	if (sim_spice_extra_area[layer] != 0.0)
2451 	{
2452 		area -= sim_spice_extra_area[layer];
2453 		sim_spice_extra_area[layer] = 0.0; /* but only once */
2454 	}
2455 
2456 	i = sim_spice_layerisdiff(tech, layer);
2457 	if (i != ISNONE)
2458 	{
2459 		sim_spice_cur_net->diffarea[i] += area * sim_spice_mask_scale * sim_spice_mask_scale;
2460 		sim_spice_cur_net->diffperim[i] += perim * sim_spice_mask_scale;
2461 	} else
2462 	{
2463 		sim_spice_cur_net->capacitance += scaletodispunitsq((INTBIG)(sim_spice_capacitance(
2464 			tech, layer) * area), DISPUNITMIC) *
2465 				sim_spice_mask_scale * sim_spice_mask_scale;
2466 		sim_spice_cur_net->capacitance += scaletodispunit((INTBIG)(sim_spice_edgecapacitance(
2467 			tech, layer) * perim), DISPUNITMIC) *
2468 				sim_spice_mask_scale;
2469 	}
2470 }
2471 
2472 /*
2473  * routine to find the capacitance for the arc on layer "layer"
2474  */
sim_spice_capacitance(TECHNOLOGY * tech,INTBIG layer)2475 float sim_spice_capacitance(TECHNOLOGY *tech, INTBIG layer)
2476 {
2477 	if (layer < 0 || tech->temp2 == 0) return(0.0);
2478 	return(castfloat(((INTBIG *)tech->temp2)[layer]));
2479 }
2480 
2481 /*
2482  * Routine to return the fringing capacitance of layer "layer" in tech "tech"
2483  */
sim_spice_edgecapacitance(TECHNOLOGY * tech,INTBIG layer)2484 float sim_spice_edgecapacitance(TECHNOLOGY *tech, INTBIG layer)
2485 {
2486 	REGISTER INTBIG addr;
2487 	REGISTER VARIABLE *var;
2488 	REGISTER TECHNOLOGY *t;
2489 
2490 	if (layer < 0) return(0.0);
2491 	if (sim_spice_capacvalue == 0)
2492 	{
2493 		sim_spice_capacvalue = emalloc(el_maxtech * SIZEOFINTBIG, sim_tool->cluster);
2494 		if (sim_spice_capacvalue == 0) return(0.0);
2495 		for(t = el_technologies; t != NOTECHNOLOGY; t = t->nexttechnology)
2496 		{
2497 			var = getval((INTBIG)t, VTECHNOLOGY, VFLOAT|VISARRAY, x_("sim_spice_edgecapacitance"));
2498 			sim_spice_capacvalue[t->techindex] = (var == NOVARIABLE ? 0 : var->addr);
2499 		}
2500 	}
2501 	addr = sim_spice_capacvalue[tech->techindex];
2502 	if (addr == 0) return(0.0);
2503 	return(castfloat(((INTBIG *)(addr))[layer]));
2504 }
2505 
2506 /******************** TEXT ROUTINES ********************/
2507 
2508 /*
2509  * Routine to insert an "include" of file "filename" into the stream "io".
2510  */
sim_spice_addincludefile(CHAR * filename)2511 void sim_spice_addincludefile(CHAR *filename)
2512 {
2513 	switch (sim_spice_machine)
2514 	{
2515 		case SPICE2:
2516 		case SPICE3:
2517 		case SPICEGNUCAP:
2518 		case SPICESMARTSPICE:
2519 			xprintf(sim_spice_file, x_(".include %s\n"), filename);
2520 			break;
2521 		case SPICEHSPICE:
2522 			xprintf(sim_spice_file, x_(".include '%s'\n"), filename);
2523 			break;
2524 		case SPICEPSPICE:
2525 			xprintf(sim_spice_file, x_(".INC %s\n"), filename);
2526 			break;
2527 	}
2528 }
2529 
2530 /*
2531  * Routine to convert "name" to a legal SPICE name (converting illegal characters
2532  * to "_") and return that name.
2533  */
sim_spice_legalname(CHAR * name)2534 CHAR *sim_spice_legalname(CHAR *name)
2535 {
2536 	REGISTER CHAR *pt;
2537 	REGISTER INTBIG i;
2538 	REGISTER void *infstr;
2539 
2540 	for(pt = name; *pt != 0; pt++)
2541 	{
2542 		if (isalnum(*pt) != 0) continue;
2543 		for(i=0; sim_spicelegalchars[i] != 0; i++)
2544 			if (*pt == sim_spicelegalchars[i]) break;
2545 		if (sim_spicelegalchars[i] == 0) break;
2546 	}
2547 	if (*pt == 0) return(name);
2548 
2549 	/* convert the name */
2550 	infstr = initinfstr();
2551 	for(pt = name; *pt != 0; pt++)
2552 	{
2553 		if (isalnum(*pt) != 0)
2554 		{
2555 			addtoinfstr(infstr, *pt);
2556 			continue;
2557 		}
2558 		for(i=0; sim_spicelegalchars[i] != 0; i++)
2559 			if (*pt == sim_spicelegalchars[i]) break;
2560 		if (sim_spicelegalchars[i] != 0)
2561 		{
2562 			addtoinfstr(infstr, *pt);
2563 			continue;
2564 		}
2565 		addtoinfstr(infstr, '_');
2566 	}
2567 	return(returninfstr(infstr));
2568 }
2569 
2570 /*
2571  * Function to return a spice "element" name.
2572  * The first character (eg. R,L,C,D,X,...) is specified by "first".
2573  * the rest of the name comes from the name on inst "ni".
2574  * If there is no node name, a unique number specified by "counter" is used
2575  * and counter is incremented.
2576  *
2577  * Warning: This routine is not re-entrant.  You must use the returned string
2578  * before calling the routine again.
2579  */
sim_spice_elementname(NODEINST * ni,CHAR first,INTBIG * counter,CHAR * overridename)2580 CHAR *sim_spice_elementname(NODEINST *ni, CHAR first, INTBIG *counter, CHAR *overridename)
2581 {
2582 	VARIABLE *varname;
2583 	static CHAR s[200];
2584 
2585 	if (overridename != 0)
2586 	{
2587 		(void)esnprintf(s, 200, x_("%c%s"), first, sim_spice_legalname(overridename));
2588 	} else
2589 	{
2590 		varname = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name_key);
2591 		if (varname == NOVARIABLE)
2592 		{
2593 			ttyputerr(_("WARNING: no name on node %s"), describenodeinst(ni));
2594 			(void)esnprintf(s, 200, x_("%c%ld"), first, (*counter)++);
2595 		} else
2596 		{
2597 			(void)esnprintf(s, 200, x_("%c%s"), first, sim_spice_legalname((CHAR *)varname->addr));
2598 		}
2599 	}
2600 
2601 	return(s);
2602 }
2603 
2604 /*
2605  * Routine to return the net name of SPICE net "spnet".
2606  * Unknown nets are assigned as node '*'.
2607  *
2608  * Warning: This routine is not re-entrant.  You must use the returned string
2609  * before calling the routine again.
2610  */
sim_spice_nodename(SPNET * spnet)2611 CHAR *sim_spice_nodename(SPNET *spnet)
2612 {
2613 	REGISTER NETWORK *net;
2614 	REGISTER void *infstr;
2615 
2616 	if (spnet == NOSPNET) net = NONETWORK; else
2617 		net = spnet->network;
2618 	infstr = initinfstr();
2619 	formatinfstr(infstr, x_(" %s"), sim_spice_netname(net, 0, 0));
2620 	return(returninfstr(infstr));
2621 }
2622 
2623 /*
2624  * Routine to return the net name of net "net".
2625  */
sim_spice_netname(NETWORK * net,INTBIG bussize,INTBIG busindex)2626 CHAR *sim_spice_netname(NETWORK *net, INTBIG bussize, INTBIG busindex)
2627 {
2628 	static CHAR s[80];
2629 	REGISTER SPNET *spnet;
2630 	REGISTER INTBIG count;
2631 	CHAR *pt, **strings;
2632 
2633 	if (net == NONETWORK)
2634 	{
2635 		if ((sim_spice_state&SPICENODENAMES) != 0)
2636 			esnprintf(s, 80, x_("UNNAMED%ld"), sim_spice_unnamednum++); else
2637 				esnprintf(s, 80, x_("%ld"), sim_spice_netindex++);
2638 		return(s);
2639 	}
2640 
2641 	if ((sim_spice_state&SPICENODENAMES) != 0)
2642 	{
2643 		/* SPICE3, HSPICE, or PSPICE only */
2644 		if (sim_spice_machine == SPICE3)
2645 		{
2646 			/* SPICE3 treats Ground as "0" in top-level cell */
2647 			if (net == sim_spice_gnd && net->parent == sim_simnt) return(x_("0"));
2648 		}
2649 		if (net->globalnet >= 0) return(sim_spice_describenetwork(net));
2650 		if (net->namecount > 0)
2651 		{
2652 			pt = networkname(net, 0);
2653 			if (isdigit(pt[0]) == 0)
2654 			{
2655 				if (bussize > 1)
2656 				{
2657 					/* see if this name is arrayed, and pick a single entry from it if so */
2658 					count = net_evalbusname(APBUS, networkname(net, 0), &strings, NOARCINST, NONODEPROTO, 0);
2659 					if (count == bussize)
2660 						return(strings[busindex]);
2661 				}
2662 				return(sim_spice_legalname(pt));
2663 			}
2664 		}
2665 	}
2666 	spnet = (SPNET *)net->temp1;
2667 	(void)esnprintf(s, 80, x_("%ld"), spnet->netnumber);
2668 	return(s);
2669 }
2670 
sim_spice_describenetwork(NETWORK * net)2671 CHAR *sim_spice_describenetwork(NETWORK *net)
2672 {
2673 	static CHAR gennetname[50];
2674 	REGISTER NODEPROTO *np;
2675 
2676 	/* write global net name if present (HSPICE and PSPICE only) */
2677 	if (net->globalnet >= 0)
2678 	{
2679 		if (net->globalnet == GLOBALNETGROUND) return(x_("gnd"));
2680 		if (net->globalnet == GLOBALNETPOWER) return(x_("vdd"));
2681 		np = net->parent;
2682 		if (net->globalnet >= np->globalnetcount)
2683 			return(_("UNKNOWN"));
2684 		return(np->globalnetnames[net->globalnet]);
2685 	}
2686 	if (net->namecount == 0)
2687 	{
2688 		esnprintf(gennetname, 50, _("UNNAMED%ld"), (INTBIG)net);
2689 		return(gennetname);
2690 	}
2691 	return(sim_spice_legalname(networkname(net, 0)));
2692 }
2693 
2694 /*
2695  * Routine to mark nodeinst temps if nodeinst->proto contains LEGATEs,
2696  * or if any nodeinsts within nodeinst->proto contain LEGATEs, and so forth,
2697  * to be uniquified when spice netlisting.  This is because LEGATEs use the
2698  * LE.getdrive() function to find their attribute values, and thus unique values
2699  * do not show up as parameters when the spice netlister decides if a cell needs
2700  * to be written again as a unique cell.
2701  * This function is bottom-up recursive because if a subcell must be uniquified
2702  * so must the current cell.
2703  * returns 1 if cell marked to uniquify.
2704  */
sim_spice_markuniquenodeinsts(NODEPROTO * np)2705 INTBIG sim_spice_markuniquenodeinsts(NODEPROTO *np)
2706 {
2707 	REGISTER NODEINST *ni;
2708 	REGISTER NODEPROTO *cnp;
2709 	REGISTER INTBIG marked;
2710 	static INTBIG attrLEGATEkey = 0;
2711 	static INTBIG attrLEKEEPERkey = 0;
2712 
2713 	if (attrLEGATEkey == 0) attrLEGATEkey = makekey(x_("ATTR_LEGATE"));
2714 	if (attrLEKEEPERkey == 0) attrLEKEEPERkey = makekey(x_("ATTR_LEKEEPER"));
2715 
2716 	/* temp1 == 1 means marked unique
2717 	   temp1 == 2 means checked, not marked unique
2718 	   temp1 == 0 means not check yet
2719 	   */
2720 	if (np->temp1 == 1) return(TRUE);
2721 	if (np->temp2 == 2) return(FALSE);
2722 	marked = 0;
2723 	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
2724 	{
2725 		/* ignore primitives and ignore self */
2726 		if (ni->proto->primindex != 0) continue;
2727 		if (isiconof(ni->proto, np)) continue;
2728 
2729 		/* if ni is LEGATE or LEKEEPER, mark current cell and no recurse into */
2730 		if (getvalkeynoeval((INTBIG)ni, VNODEINST, -1, attrLEGATEkey) != NOVARIABLE) { marked = 1; continue; }
2731 		if (getvalkeynoeval((INTBIG)ni, VNODEINST, -1, attrLEKEEPERkey) != NOVARIABLE) { marked = 1; continue; }
2732 
2733 		/* remove old var (marker) if present */
2734 		if (getvalkeynoeval((INTBIG)ni, VNODEINST, -1, sim_spice_nameuniqueid) != NOVARIABLE)
2735 			delvalkey((INTBIG)ni, VNODEINST, sim_spice_nameuniqueid);
2736 
2737 		/* recurse, bottom-up into ni */
2738 		cnp = contentsview(ni->proto);
2739 		if (cnp == NONODEPROTO) cnp = ni->proto;
2740 		if (sim_spice_markuniquenodeinsts(cnp) != 0)
2741 		{
2742 			/* mark nodeinst, mark current cell */
2743 			setvalkey((INTBIG)ni, VNODEINST, sim_spice_nameuniqueid, 1, VINTEGER|VDONTSAVE);
2744 			marked = 1;
2745 		}
2746 	}
2747 	if (marked) np->temp1 = 1; else np->temp1 = 2;
2748 
2749 	return(marked);
2750 }
2751 
2752 /******************** SUPPORT ********************/
2753 
2754 /*
2755  * routine to return nonzero if layer "layer" is on diffusion
2756  * Return the type of the diffusion
2757  */
sim_spice_layerisdiff(TECHNOLOGY * tech,INTBIG layer)2758 INTBIG sim_spice_layerisdiff(TECHNOLOGY *tech, INTBIG layer)
2759 {
2760 	REGISTER INTBIG i;
2761 
2762 	i = layerfunction(tech, layer);
2763 	if ((i&LFPSEUDO) != 0) return(ISNONE);
2764 	if ((i&LFTYPE) != LFDIFF) return(ISNONE);
2765 	if ((i&LFPTYPE) != 0) return(ISPTYPE);
2766 	if ((i&LFNTYPE) != 0) return(ISNTYPE);
2767 	return(ISNTYPE);		/* Default to N-type  */
2768 }
2769 
2770 /*
2771  * routine to return value if arc contains device active diffusion
2772  * Return the type of the diffusion, else ISNONE
2773  */
sim_spice_arcisdiff(ARCINST * ai)2774 INTBIG sim_spice_arcisdiff(ARCINST *ai)
2775 {
2776 	REGISTER INTBIG i;
2777 
2778 	i = (ai->proto->userbits&AFUNCTION)>>AFUNCTIONSH;
2779 	switch (i)
2780 	{
2781 		case APDIFFP:
2782 			return(ISPTYPE);
2783 		case APDIFFN:
2784 			return(ISNTYPE);
2785 		case APDIFF:
2786 			return(ISNTYPE);	/* Default device is n-type */
2787 		default:
2788 			return(ISNONE);	/* Default to Unknown  */
2789 	}
2790 }
2791 
2792 /*
2793  * routine to search the net list for this cell and return the net number
2794  * associated with nodeinst "ni", network "net"
2795  */
sim_spice_getnet(NODEINST * ni,NETWORK * net)2796 SPNET *sim_spice_getnet(NODEINST *ni, NETWORK *net)
2797 {
2798 	REGISTER SPNET *spnet;
2799 	REGISTER PORTARCINST *pi;
2800 	REGISTER PORTEXPINST *pe;
2801 
2802 	/* search for arcs electrically connected to this port */
2803 	for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
2804 		if (pi->proto->network == net)
2805 	{
2806 		for(spnet = sim_spice_firstnet; spnet != NOSPNET; spnet = spnet->nextnet)
2807 			if (pi->conarcinst->network == spnet->network) return(spnet);
2808 	}
2809 
2810 	/* search for exports on the node, connected to this port */
2811 	for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
2812 		if (pe->proto->network == net)
2813 	{
2814 		for(spnet = sim_spice_firstnet; spnet != NOSPNET; spnet = spnet->nextnet)
2815 			if (pe->exportproto->network == spnet->network) return(spnet);
2816 	}
2817 	return(NOSPNET);
2818 }
2819 
2820 /******************** LOW-LEVEL OUTPUT ROUTINES ********************/
2821 
2822 /*
2823  * Routine to report an error that is built in the infinite string.
2824  * The error is sent to the messages window and also to the SPICE deck "f".
2825  */
sim_spice_dumpstringerror(void * infstr,SpiceInst * inst)2826 void sim_spice_dumpstringerror(void *infstr, SpiceInst *inst)
2827 {
2828 	REGISTER CHAR *error;
2829 
2830 	error = returninfstr(infstr);
2831 	if (inst)
2832 		inst->addError( error );
2833 	else
2834 		sim_spice_xprintf(sim_spice_file, TRUE, x_("*** %s\n"), error);
2835 	ttyputmsg(x_("%s"), error);
2836 }
2837 
2838 /*
2839  * Formatted output to file "stream".  All spice output is in upper case.
2840  * The buffer can contain no more than 1024 chars including the newline
2841  * and null characters.
2842  * Doesn't return anything.
2843  */
sim_spice_xprintf(FILE * stream,BOOLEAN iscomment,CHAR * format,...)2844 void sim_spice_xprintf(FILE *stream, BOOLEAN iscomment, CHAR *format, ...)
2845 {
2846 	va_list ap;
2847 	CHAR s[1024];
2848 
2849 	var_start(ap, format);
2850 	evsnprintf(s, 1024, format, ap);
2851 	va_end(ap);
2852 	sim_spice_xputs(s, stream, iscomment);
2853 }
2854 
2855 /*
2856  * Routine to write string "s" onto stream "stream", breaking
2857  * it into lines of the proper width, and converting to upper case
2858  * if SPICE2.
2859  */
sim_spice_xputs(CHAR * s,FILE * stream,BOOLEAN iscomment)2860 void sim_spice_xputs(CHAR *s, FILE *stream, BOOLEAN iscomment)
2861 {
2862 	CHAR *pt, *lastspace, contchar;
2863 	BOOLEAN insidequotes = FALSE;
2864 	static INTBIG i=0;
2865 
2866 	/* put in line continuations, if over 78 chars long */
2867 	if (iscomment) contchar = '*'; else
2868 		contchar = '+';
2869 	lastspace = NULL;
2870 	for (pt = s; *pt; pt++)
2871 	{
2872 		if (sim_spice_machine == SPICE2)
2873 		{
2874 			if (islower(*pt)) *pt = toupper(*pt);
2875 		}
2876 		if (*pt == '\n')
2877 		{
2878 			i = 0;
2879 			lastspace = NULL;
2880 		} else
2881 		{
2882 			/* removed '/' from check here, not sure why it's there */
2883 			if (*pt == ' ' || *pt == '\'') lastspace = pt;
2884 			if (*pt == '\'') insidequotes = !insidequotes;
2885 			++i;
2886 			if (i >= 78 && !insidequotes)
2887 			{
2888 				if (lastspace != NULL)
2889 				{
2890 					if( *lastspace == '\'')
2891 					{
2892 						*lastspace = '\0';
2893 						xputs(s, stream);
2894 						xprintf(stream, x_("'\n%c  "), contchar);
2895 					} else
2896 					{
2897 						*lastspace = '\0';
2898 						xputs(s, stream);
2899 						xprintf(stream, x_("\n%c  "), contchar);
2900 					}
2901 					s = lastspace + 1;
2902 					i = 9 + pt-s+1;
2903 					lastspace = NULL;
2904 				} else
2905 				{
2906 					xprintf(stream, x_("\n%c  "), contchar);
2907 					i = 9 + 1;
2908 				}
2909 			}
2910 		}
2911 	}
2912 	xputs(s, stream);
2913 }
2914 
2915 /******************** MEMORY ALLOCATION ********************/
2916 
sim_spice_allocpolygon(void)2917 POLYGON *sim_spice_allocpolygon(void)
2918 {
2919 	REGISTER POLYGON *poly;
2920 
2921 	if (sim_polygonfree != NOPOLYGON)
2922 	{
2923 		poly = sim_polygonfree;
2924 		sim_polygonfree = poly->nextpolygon;
2925 	} else
2926 	{
2927 		poly = allocpolygon(4, sim_tool->cluster);
2928 		if (poly == NOPOLYGON) return(NOPOLYGON);
2929 	}
2930 	poly->nextpolygon = NOPOLYGON;
2931 	return(poly);
2932 }
2933 
sim_spice_freepolygon(POLYGON * poly)2934 void sim_spice_freepolygon(POLYGON *poly)
2935 {
2936 	poly->nextpolygon = sim_polygonfree;
2937 	sim_polygonfree = poly;
2938 }
2939 
2940 /*
2941  * routine to allocate and initialize a new net module from the pool
2942  * (if any) or memory
2943  */
sim_spice_allocspnet(void)2944 SPNET *sim_spice_allocspnet(void)
2945 {
2946 	REGISTER SPNET *spnet;
2947 	REGISTER INTBIG j;
2948 
2949 	if (sim_spice_netfree == NOSPNET)
2950 	{
2951 		spnet = (SPNET *)emalloc(sizeof (SPNET), sim_tool->cluster);
2952 		if (spnet == 0) return(NOSPNET);
2953 	} else
2954 	{
2955 		/* take module from free list */
2956 		spnet = sim_spice_netfree;
2957 		sim_spice_netfree = spnet->nextnet;
2958 	}
2959 
2960 	/* Initialize it to empty values */
2961 	spnet->resistance = 0.0;
2962 	spnet->capacitance = 0.0;
2963 	spnet->network = NONETWORK;
2964 	for (j = 0; j < DIFFTYPES; j++)
2965 	{
2966 		spnet->diffperim[j] = 0.0;
2967 		spnet->diffarea[j] = 0.0;
2968 		spnet->components[j] = 0;
2969 	}
2970 	return(spnet);
2971 }
2972 
2973 /*
2974  * routine to return net module "spnet" to the pool of free modules
2975  */
sim_spice_freespnet(SPNET * spnet)2976 void sim_spice_freespnet(SPNET *spnet)
2977 {
2978 	spnet->nextnet = sim_spice_netfree;
2979 	sim_spice_netfree = spnet;
2980 }
2981 
2982 /*
2983  * Routine to scan networks in cell "cell".  All networks are sorted by name.
2984  * The number of networks is returned and the list of them is put in "netlist".
2985  * Each network's "temp2" field is 2 for output, 1 for all other export types
2986  * (this is reversed for CDL).  Also, the power and ground networks are put into
2987  * "vdd" and "gnd".
2988  */
sim_spice_getexportednetworks(NODEPROTO * cell,NETWORK *** netlist,NETWORK ** vdd,NETWORK ** gnd,BOOLEAN cdl)2989 INTBIG sim_spice_getexportednetworks(NODEPROTO *cell, NETWORK ***netlist, NETWORK **vdd, NETWORK **gnd,
2990 	BOOLEAN cdl)
2991 {
2992 	REGISTER NETWORK *net;
2993 	REGISTER PORTPROTO *pp;
2994 	REGISTER INTBIG wirecount, i, termtype;
2995 	REGISTER UINTBIG characteristics;
2996 
2997 	/* initialize to describe all nets */
2998 	for(net = cell->firstnetwork; net != NONETWORK; net = net->nextnetwork)
2999 		net->temp2 = 0;
3000 
3001 	/* mark exported networks */
3002 	*vdd = *gnd = NONETWORK;
3003 	for(i = 0; i < cell->globalnetcount; i++)
3004 	{
3005 		net = cell->globalnetworks[i];
3006 		if(net == NONETWORK) continue;
3007 		characteristics = cell->globalnetchar[i];
3008 		if (i == 0 || (characteristics == GNDPORT && *gnd == NONETWORK))
3009 			*gnd = net;
3010 		if (i == 1 || (characteristics == PWRPORT && *vdd == NONETWORK))
3011 			*vdd = net;
3012 	}
3013 	for(pp = cell->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
3014 	{
3015 		net = pp->network;
3016 		if (portispower(pp) && *vdd == NONETWORK) *vdd = net;
3017 		if (portisground(pp) && *gnd == NONETWORK) *gnd = net;
3018 		if (cdl)
3019 		{
3020 			if ((pp->userbits&STATEBITS) == OUTPORT) termtype = 1; else
3021 				termtype = 2;
3022 		} else
3023 		{
3024 			if ((pp->userbits&STATEBITS) == OUTPORT) termtype = 2; else
3025 				termtype = 1;
3026 		}
3027 		if (net->buswidth > 1)
3028 		{
3029 			/* bus export: mark individual network entries */
3030 			for(i=0; i<net->buswidth; i++)
3031 				net->networklist[i]->temp2 = termtype;
3032 		} else
3033 		{
3034 			/* single wire export: mark the network */
3035 			net->temp2 = termtype;
3036 		}
3037 	}
3038 	if (*vdd != NONETWORK && *vdd == *gnd)
3039 		ttyputerr(_("*** WARNING: Power and Ground are shorted together in cell %s"),
3040 			describenodeproto(cell));
3041 
3042 	wirecount = 0;
3043 	for(net = cell->firstnetwork; net != NONETWORK; net = net->nextnetwork)
3044 	{
3045 		if (net->temp2 != 0) wirecount++;
3046 	}
3047 
3048 	if (wirecount > 0)
3049 	{
3050 		/* make sure there is room in the array of networks */
3051 		if (wirecount > sim_spicewirelisttotal)
3052 		{
3053 			if (sim_spicewirelisttotal > 0) efree((CHAR *)sim_spicewirelist);
3054 			sim_spicewirelisttotal = 0;
3055 			sim_spicewirelist = (NETWORK **)emalloc(wirecount * (sizeof (NETWORK *)),
3056 				sim_tool->cluster);
3057 			if (sim_spicewirelist == 0) return(0);
3058 			sim_spicewirelisttotal = wirecount;
3059 		}
3060 
3061 		/* load the array */
3062 		i = 0;
3063 		for(net = cell->firstnetwork; net != NONETWORK; net = net->nextnetwork)
3064 		{
3065 			if (net->temp2 != 0) sim_spicewirelist[i++] = net;
3066 		}
3067 
3068 		/* sort the networks by name */
3069 		esort(sim_spicewirelist, wirecount, sizeof (NETWORK *), sim_spice_sortnetworksbyname);
3070 		*netlist = sim_spicewirelist;
3071 	}
3072 
3073 	return(wirecount);
3074 }
3075 
3076 /*
3077  * Routine to recursively examine cells and gather global network names.
3078  */
sim_spice_gatherglobals(NODEPROTO * np)3079 void sim_spice_gatherglobals(NODEPROTO *np)
3080 {
3081 	for(INTBIG k = 0; k < np->globalnetcount; k++)
3082 	{
3083 		if (np->globalnetworks[k] == NONETWORK) continue;
3084 		CHAR *gname = sim_spice_describenetwork(np->globalnetworks[k]);
3085 		/* add the global net name */
3086 		if (sim_spiceglobalnetcount >= sim_spiceglobalnettotal)
3087 		{
3088 			INTBIG newtotal = sim_spiceglobalnettotal * 2;
3089 			INTBIG i;
3090 
3091 			if (sim_spiceglobalnetcount >= newtotal)
3092 				newtotal = sim_spiceglobalnetcount + 5;
3093 			CHAR **newlist = (CHAR **)emalloc(newtotal * (sizeof (CHAR *)), sim_tool->cluster);
3094 			if (newlist == 0) return;
3095 			for(i=0; i<sim_spiceglobalnettotal; i++)
3096 				newlist[i] = sim_spiceglobalnets[i];
3097 			for(i=sim_spiceglobalnettotal; i<newtotal; i++)
3098 				newlist[i] = 0;
3099 			if (sim_spiceglobalnettotal > 0)
3100 				efree((CHAR *)sim_spiceglobalnets);
3101 			sim_spiceglobalnets = newlist;
3102 			sim_spiceglobalnettotal = newtotal;
3103 		}
3104 		if (sim_spiceglobalnets[sim_spiceglobalnetcount] != 0)
3105 			efree((CHAR *)sim_spiceglobalnets[sim_spiceglobalnetcount]);
3106 		(void)allocstring(&sim_spiceglobalnets[sim_spiceglobalnetcount], gname,
3107 			sim_tool->cluster);
3108 		sim_spiceglobalnetcount++;
3109 	}
3110 }
3111 
3112 /*
3113  * Routine to determine the proper name to use for cell "np".
3114  */
sim_spice_cellname(NODEPROTO * np)3115 CHAR *sim_spice_cellname(NODEPROTO *np)
3116 {
3117 	REGISTER void *infstr;
3118 
3119 	if (np->temp2 == 0) return(np->protoname);
3120 	infstr = initinfstr();
3121 	formatinfstr(infstr, "%s-%s", np->lib->libname, np->protoname);
3122 	return(returninfstr(infstr));
3123 }
3124 
3125 /*
3126  * Helper routine for "esort" that makes networks with names go in ascending name order.
3127  */
sim_spice_sortnetworksbyname(const void * e1,const void * e2)3128 int sim_spice_sortnetworksbyname(const void *e1, const void *e2)
3129 {
3130 	REGISTER NETWORK *net1, *net2;
3131 	REGISTER CHAR *pt1, *pt2;
3132 	CHAR empty[1];
3133 
3134 	net1 = *((NETWORK **)e1);
3135 	net2 = *((NETWORK **)e2);
3136 	if (net1->temp2 != net2->temp2)
3137 		return(net1->temp2 - net2->temp2);
3138 	empty[0] = 0;
3139 	if (net1->namecount == 0) pt1 = empty; else pt1 = networkname(net1, 0);
3140 	if (net2->namecount == 0) pt2 = empty; else pt2 = networkname(net2, 0);
3141 	return(namesamenumeric(pt1, pt2));
3142 }
3143 
3144 /******************** Spice Classes ********************/
3145 
3146 SpiceCell *SpiceCell::_firstCell = 0;
3147 CHAR *SpiceCell::_path = 0;
3148 CHAR *SpiceCell::_pathend;
3149 CHAR *SpiceCell::_rpath = 0;
3150 CHAR *SpiceCell::_rpathbeg;
3151 CHAR **sim_spice_printlist = 0;
3152 INTBIG sim_spice_printlistlen = 0;
3153 
3154 
SpiceCell(CHAR * name)3155 SpiceCell::SpiceCell( CHAR *name )
3156 	: _firstInst( 0 ), _lastInst( 0 ), _firstNet( 0 )
3157 {
3158 	_name = new CHAR[estrlen(name) + 1];
3159 	estrcpy( _name, name );
3160 	_nextCell = _firstCell;
3161 	_firstCell = this;
3162 }
3163 
~SpiceCell()3164 SpiceCell::~SpiceCell()
3165 {
3166 	delete[] _name;
3167 	while (_firstInst)
3168 	{
3169 		SpiceInst *inst = _firstInst;
3170 		_firstInst = _firstInst->_nextInst;
3171 		delete inst;
3172 	}
3173 	while (_firstNet)
3174 	{
3175 		SpiceNet *net = _firstNet;
3176 		_firstNet = _firstNet->_nextNet;
3177 		delete net;
3178 	}
3179 }
3180 
findByName(CHAR * name)3181 SpiceCell *SpiceCell::findByName( CHAR *name )
3182 {
3183 	SpiceCell *cell;
3184 	for (cell = _firstCell; cell && estrcmp(name, cell->_name) != 0; cell = cell->_nextCell);
3185 	return cell;
3186 }
3187 
setup()3188 void SpiceCell::setup()
3189 {
3190 	_pathlen = 15;
3191 	_totalnets = 0;
3192 	for (SpiceInst *inst = _firstInst; inst; inst = inst->_nextInst)
3193 	{
3194 		if (!inst->_instCell) continue;
3195 		if ((INTBIG)estrlen(inst->_name) + inst->_instCell->_pathlen >= _pathlen)
3196 			_pathlen = estrlen(inst->_name) + 1 + inst->_instCell->_pathlen;
3197 		_totalnets += inst->_instCell->_totalnets;
3198 	}
3199 	for (SpiceNet *net = _firstNet; net; net = net->_nextNet)
3200 	{
3201 		if (net->_netw && net->_netw->namecount > 0)
3202 		{
3203 			INTBIG len = estrlen(networkname(net->_netw, 0));
3204 			if (len > _pathlen)
3205 				_pathlen = len;
3206 		}
3207 		_totalnets++;
3208 	}
3209 }
3210 
printSpice()3211 void SpiceCell::printSpice()
3212 {
3213 	for (SpiceInst *inst = _firstInst; inst; inst = inst->_nextInst)
3214 		inst->printSpice();
3215 }
3216 
traverse()3217 void SpiceCell::traverse()
3218 {
3219 	for (SpiceNet *net = _firstNet; net; net = net->_nextNet)
3220 	{
3221 		if (net->_netnumber == 0) continue;
3222 		xprintf(sim_spice_file, x_(" V(%ld%s)"), net->_netnumber, _rpathbeg);
3223 		if (net->_netw && net->_netw->namecount > 0)
3224 			estrcpy(_pathend, networkname(net->_netw, 0));
3225 		else
3226 			esnprintf(_pathend, 10, x_("%ld"), net->_netnumber);
3227 		sim_spice_printlist[sim_spice_printlistlen] = new CHAR[estrlen(_path) + 1];
3228 		estrcpy(sim_spice_printlist[sim_spice_printlistlen], _path);
3229 		sim_spice_printlistlen++;
3230 	}
3231 	for (SpiceInst *inst = _firstInst; inst; inst = inst->_nextInst)
3232 	{
3233 		if (!inst->_instCell) continue;
3234 		inst->traverse();
3235 	}
3236 }
3237 
traverseAll()3238 void SpiceCell::traverseAll()
3239 {
3240 	_pathend = _path = new CHAR[_firstCell->_pathlen + 1];
3241 	_rpath = new CHAR[_firstCell->_pathlen + 1];
3242 	_rpathbeg = _rpath + _firstCell->_pathlen;
3243 	_path[0] = _rpathbeg[0] = 0;
3244 
3245 	if (sim_spice_printlist)
3246 	{
3247 		for (int i = 0; i < sim_spice_printlistlen; i++) delete[] sim_spice_printlist[i];
3248 		delete[] sim_spice_printlist;
3249 	}
3250 	sim_spice_printlistlen = 0;
3251 	sim_spice_printlist = new (CHAR*[_firstCell->_totalnets]);
3252 	xprintf(sim_spice_file, x_(".print tran") );
3253 	_firstCell->traverse();
3254 	if (sim_spice_printlistlen > _firstCell->_totalnets)
3255 		ttyputmsg(x_("Spice cell: sim_spice_printlistlen=%ld totalnets=%ld"),
3256 			sim_spice_printlistlen, _firstCell->_totalnets);
3257 	xprintf(sim_spice_file, x_("\n"));
3258 	delete _path;
3259 	delete _rpath;
3260 }
3261 
clearAll()3262 void SpiceCell::clearAll()
3263 {
3264 	while (_firstCell)
3265 	{
3266 		SpiceCell *cell = _firstCell;
3267 		_firstCell = _firstCell->_nextCell;
3268 		delete cell;
3269 	}
3270 	if (sim_spice_printlist)
3271 	{
3272 		for (int i = 0; i < sim_spice_printlistlen; i++) delete[] sim_spice_printlist[i];
3273 		delete[] sim_spice_printlist;
3274 	}
3275 	sim_spice_printlist = 0;
3276 	sim_spice_printlistlen = 0;
3277 }
3278 
SpiceInst(SpiceCell * parentCell,CHAR * name)3279 SpiceInst::SpiceInst( SpiceCell *parentCell, CHAR *name )
3280 	: _parentCell( parentCell ), _instCell( 0 ), _firstBind( 0 ), _lastBind( 0 ), _firstParam( 0 ), _lastParam( 0 ),
3281 	  _error( 0 ), _nextInst( 0 )
3282 {
3283 	_name = new CHAR[estrlen(name) + 1];
3284 	estrcpy( _name, name );
3285 	if ( _parentCell->_firstInst )
3286 		_parentCell->_lastInst->_nextInst = this;
3287 	else
3288 		_parentCell->_firstInst = this;
3289 	_parentCell->_lastInst = this;
3290 }
3291 
~SpiceInst()3292 SpiceInst::~SpiceInst()
3293 {
3294 	while (_firstBind)
3295 	{
3296 		SpiceBind *bind = _firstBind;
3297 		_firstBind = bind->_nextBind;
3298 		delete bind;
3299 	}
3300 	while (_firstParam)
3301 	{
3302 		SpiceParam *param = _firstParam;
3303 		_firstParam = param->_nextParam;
3304 		delete param;
3305 	}
3306 	if (_error) delete[] _error;
3307 	delete[] _name;
3308 }
3309 
setInstCell(SpiceCell * instCell)3310 void SpiceInst::setInstCell( SpiceCell *instCell )
3311 {
3312 	_instCell = instCell;
3313 }
3314 
addBind(SpiceBind * bind)3315 void SpiceInst::addBind( SpiceBind *bind )
3316 {
3317 	if (_lastBind)
3318 		_lastBind->_nextBind = bind;
3319 	else
3320 		_firstBind = bind;
3321 	_lastBind = bind;
3322 	bind->_nextBind = 0;
3323 	bind->_inst = this;
3324 }
3325 
addParam(SpiceParam * param)3326 void SpiceInst::addParam( SpiceParam *param )
3327 {
3328 	if (_lastParam)
3329 		_lastParam->_nextParam = param;
3330 	else
3331 		_firstParam = param;
3332 	_lastParam = param;
3333 	param->_nextParam = 0;
3334 }
3335 
addParamM(NODEINST * ni)3336 void SpiceInst::addParamM( NODEINST *ni )
3337 {
3338 	CHAR line[100];
3339 
3340 	VARIABLE *varm = getvalkey((INTBIG)ni, VNODEINST, -1, el_attrkey_M);
3341 	if (varm == NOVARIABLE) return;
3342 	if (ni->proto->primindex == 0 && TDGETISPARAM(varm->textdescript) != 0) return;
3343 	CHAR *pt = describevariable(varm, -1, -1);
3344 	esnprintf(line, 100, x_("M=%s"), pt);
3345 	addParam( new SpiceParam( line ) );
3346 }
3347 
addError(CHAR * error)3348 void SpiceInst::addError( CHAR *error )
3349 {
3350 	void *infstr = initinfstr();
3351 	if (_error) addstringtoinfstr( infstr, _error );
3352 	formatinfstr(infstr, x_("*** %s\n"), error);
3353 	CHAR *newStr = returninfstr(infstr);
3354 	if (_error) delete[] _error;
3355 	_error = new CHAR[estrlen(newStr) + 1];
3356 	estrcpy( _error, newStr );
3357 }
3358 
printSpice()3359 void SpiceInst::printSpice()
3360 {
3361 	sim_spice_puts( _name, FALSE );
3362 	for (SpiceBind *bind = _firstBind; bind; bind = bind->_nextBind)
3363 		bind->printSpice();
3364 	if (sim_spice_cdl)
3365 		sim_spice_puts( x_(" /"), FALSE );
3366 	if (_instCell)
3367 		sim_spice_xprintf( sim_spice_file, FALSE, x_(" %s"), _instCell->_name );
3368 	for (SpiceParam *param = _firstParam; param; param = param->_nextParam)
3369 		sim_spice_xprintf(sim_spice_file, FALSE, x_(" %s"), param->str());
3370 	sim_spice_puts( x_("\n"), FALSE );
3371 	if (_error)
3372 		sim_spice_puts( _error, TRUE );
3373 }
3374 
traverse()3375 void SpiceInst::traverse()
3376 {
3377 	estrcpy(SpiceCell::_pathend, _name);
3378 	estrcat(SpiceCell::_pathend, x_("/"));
3379 	SpiceCell::_pathend += estrlen(_name) + 1;
3380 	SpiceCell::_rpathbeg -= estrlen(_name) + 1;
3381 	estrncpy(SpiceCell::_rpathbeg + 1, _name, estrlen(_name));
3382 	*SpiceCell::_rpathbeg = '.';
3383 	_instCell->traverse();
3384 	SpiceCell::_pathend -= estrlen(_name) + 1;
3385 	SpiceCell::_rpathbeg += estrlen(_name) + 1;
3386 }
3387 
SpiceBind(SpiceInst * inst,SpiceNet * net)3388 SpiceBind::SpiceBind( SpiceInst *inst, SpiceNet *net )
3389 {
3390 	if (!net)
3391 	{
3392 		INTBIG unnamed = ((sim_spice_state&SPICENODENAMES) != 0 ? sim_spice_unnamednum++ : sim_spice_netindex++);
3393 		net = new SpiceNet( inst->_parentCell, unnamed );
3394 	}
3395 	_net = net;
3396 	inst->addBind( this );
3397 }
3398 
~SpiceBind()3399 SpiceBind::~SpiceBind()
3400 {
3401 }
3402 
printSpice()3403 void SpiceBind::printSpice()
3404 {
3405 	_net->printSpice();
3406 }
3407 
SpiceParam(CHAR * str)3408 SpiceParam::SpiceParam( CHAR *str )
3409 	: _nextParam( 0 )
3410 {
3411 	_str = new CHAR[estrlen(str) + 1];
3412 	estrcpy( _str, str );
3413 }
3414 
~SpiceParam()3415 SpiceParam::~SpiceParam()
3416 {
3417 	delete[] _str;
3418 }
3419 
SpiceNet(SpiceCell * parentCell,INTBIG netnumber,NETWORK * netw)3420 SpiceNet::SpiceNet( SpiceCell *parentCell, INTBIG netnumber, NETWORK *netw )
3421 	: _netnumber( netnumber ), _netw( netw ), _parentCell( parentCell )
3422 {
3423 	_nextNet = _parentCell->_firstNet;
3424 	_parentCell->_firstNet = this;
3425 }
3426 
~SpiceNet()3427 SpiceNet::~SpiceNet()
3428 {
3429 }
3430 
printSpice()3431 void SpiceNet::printSpice()
3432 {
3433 	CHAR line[100];
3434 
3435 	if ((sim_spice_state&SPICENODENAMES) != 0)
3436 	{
3437 		if (_netw)
3438 		{
3439 			void *infstr = initinfstr();
3440 			formatinfstr(infstr, x_(" %s"), sim_spice_netname(_netw, 0, 0));
3441 			sim_spice_puts(returninfstr(infstr), FALSE);
3442 			return;
3443 		}
3444 		esnprintf(line, 100, x_(" UNNAMED%ld"), _netnumber);
3445 	} else
3446 	{
3447 		esnprintf(line, 100, x_(" %ld"), _netnumber);
3448 	}
3449 	sim_spice_puts(line, FALSE);
3450 }
3451 
3452 #endif  /* SIMTOOL - at top */
3453