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