/*
 * Electric(tm) VLSI Design System
 *
 * File: simspice.c
 * SPICE list generator: write a SPICE format file for the current facet
 * Written by: Steven M. Rubin, Static Free Software
 * Improved by: Sid Penstone, Queen's University
 *
 * Copyright (c) 2000 Static Free Software.
 *
 * Electric(tm) is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Electric(tm) is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Electric(tm); see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, Mass 02111-1307, USA.
 *
 * Static Free Software
 * 4119 Alpine Road
 * Portola Valley, California 94028
 * info@staticfreesoft.com
 */

/*
 * Modified to get areas correctly by Oct.6/87 S.R.Penstone, Queen's U.
 * Pools diffusion areas (see notes below)
 * Revision Nov.12-- fixed error that applied mask scale factor twice
 * Revised Dec.2/87 SRP - separate out the diffusion and transistor types
 * Revision Dec.30/87, to leave out the poly gate on a transistor
 * Revision Mar.29/89 to declare atof() as double (SRP)
 * Revised June 6/89 to ask for cell name during parse-output operation (QU)
 * Revised June 6/89 to no longer look for X in 1st column of spice output (QU)
 * Revised Aug 31/89 to verify cell name during parse-output operation (QU)
 * Revised Aug 31/89 to look for layout view (QU)
 * Revised Nov 27/89 merged with version in Electric 4.04 (QU)
 * Revised Nov 28/89 merged with version from Chris Schneider at U. of Manitoba (QU)
 * Revised Mar 89 to support the use of node names, external model files,
 *    remote execution (UNIX), .NODESET and special sources, meters (fixed
 *    bug), varargs (UNIX), interactive use with spice2 spice3 or hspice
 *    simulators... by L. Swab (QU).  Some code by C. Schneider.
 * Revised June 7/90 to check for prototype ground net when exporting
 * subcircuit ports; if it is ground, do not export it. (SRP) This will
 * be looked at again.
 * Revised Dec.7/90 to start handling ARRAY types
 * MAJOR CHANGES Dec/90 SRP:
 * prepare to split source files into two files: simspice.c and simspicerun.c to
 * separate execution from writing
 * Create separate function to write 2 port types, so it can be called
 * Do not write  two terminal elements that are shorted out 91-1-30 SRP
 * Mar. 1991:  Include a substrate connection for bipolar transistors; use the
 * subnet defined by a substrate connection or default to ground.
 * Nov.18/91 Test the temp bits in the cell in case we
 * are doing an extraction from icons...
 * Nov. 29/91: Added option to write a trailer file, tech:~.SIM_spice_trailer_file
 * modified comment header to describe capabilities
 * Removed bug in writing substrate of bipolar transistors
 * 920113: Created default ground net in facets; output only the first
 * name of multiply-named networks; output a reference list of named arcs
 * Do not complain about posnet in subcircuits
 * SRP920603: Added option to include behavioral file for a facet, or a cell.
 * The variable "SPICE_behave_file" can be attached to the facet (in case it is
 * an icon, or to any facet in the cell on the nodeproto.
 * Changed the name of the behavior file variable to "sim_spice_behave_file"
 * SRP920604
 * We should only count diffusion connections to the network, to calculate
 * the drain and source areas correctly!
 * Removed trapdoor in sim_spice_arcarea() for well layers SRP920618:
 * Added function to look for diffusion arc function
 * Added check for isolated diffusion that will not be modelled correctly
 * Caught multiple polygons on different arc in sim_spice_evalpolygon()
 * Changed call to mrgdonefacet() and evalpolygon() to use (float) area
 * Changed conditions for error messages about wells and substrates (SRP)
 * Identify ground node even if the port is not flagged SRP920623
 * SRP920712: Changed storage in nets for diffarea[] to float as well
 *     "    Changed to write facet pins in numerical order
 * Fixed bug in sim_spice_nodearea() that didi not traverse the arcs of
 * a complex facet
 * Wrote temporary local version of decsribenetwork() to get around bug
 * in infinitstr() code
 * RLW920716: modified sim_spicewritefacet() to indicate the type of
 *     unconnected diffusion on a node
 *
 * SRP921116: Changed warning messages in spice file to comments so that
 *      simulator can test file
 *
 * TADR20000805 Added dependent sources CCVS CCCS VCVS VCCS
 *
 * To properly simulate a facet, it must have the following (many of these
 * commands are found in the "spice.mac" command file):
 *
 * 1) Power and ground must be exports and explicitly connected to sources.
 *    You can do this with a power source (the primitive node prototype
 *    "schematic:source") which must be connected to power at the top and to
 *    ground at the bottom.  The source should then be parameterized to
 *    indicate the amount and whether it is voltage or current.  For example,
 *    to make a 5 volt supply, create a source node and use:
 *        setsource v "DC 5"
 *
 * 2) All input ports must be exports and connected to the positive side
 *    of sources (again, the "schematic:source" node).  Time values may be
 *    placed on the source nodes.  For example, if the source is to produce
 *    values that are 0 volts from 0 to 10NS and then 5 volts, use:
 *        setsource v "PWL(0NS 0 10NS 0 11NS 5)"
 *    constant input values can use the same form as for power and ground.
 *
 *    A .NODESET source can be created by connecting the positive end of the
 *    source to the appropriate node, and typing:
 *        setsource n VALUE
 *
 *    Special sources such as VCVS's can be created with the "special" source.
 *    The letter following the "s" in the parameterization will appear as
 *    the first letter in the deck.  For example, to create a VCVS, create
 *    a source and connect it to the approprite nodes.  Then parameterize it
 *    with
 *        setsource se "NC+ NC-"
 *    Where NC+ and NC- are the names of ports which specify the control
 *    voltages.  This would produce a spice card of the form:
 *        EX N+ N- NC+ NC-
 *    Where X is a unique name or number, and N+ and N- are the nodes which
 *    are connected to the source.
 *
 * 3) All values that are being watched must be exports and have meters
 *    placed on them.  The primitive nodeproto "schematic:meter" can be placed
 *    anywhere and it should have the top and bottom ports connected
 *    appropriately.  For the meter to watch voltage from 0 to 5 volts, use:
 *        setmeter "(0 5)"
 *    To watch the current through any voltage source, parameterize it in the
 *    usual way, but add the "m" option, eg.
 *        setsource vm ...
 *        setsource vdm ...
 *
 * 4) Determine the level of simulation by saying:
 *        setlevel 2
 *    This will extract the appropriate header from the technology.
 *    Alternately, you can specify a file to read the header info from
 *    with the command:
 *        variable set tech:~.SIM_spice_model_file FILENAME
 *
 * 5) Determine the type of analysis with another source node.  For transient
 *    analysis that displays half NS intervals, runs for 20NS, starts at time
 *    0, and uses internal steps of 0.1NS, create an unconnected source node
 *    and issue this statement:
 *        setsource vt ".5NS 20NS 0NS .1NS"
 *    For DC analysis you must connect the power side of the source node to
 *    the DC point and then use:
 *        setsource vd "0V 5V .1V"
 *    For AC analysis, create a source node and parameterize it with (eg.)
 *        setsource va "DEC 10 1 10K"
 *    There must be exactly one of these source nodes.
 *
 * 6) Define the spice format to use when producing the deck and parsing
 *    the output:
 *        telltool simulation spice format [2 | 3 | hspice]
 *
 * 7) Run spice with the command:
 *        ontool simulation
 *    This generates a deck and runs the simulator.  The results from .PRINT
 *    commands will be converted to a facet in the current library that
 *    contains a plot of the data.
 *
 * 8) You can also run SPICE on another machine (or otherwise outside of
 *    Electric).  To do this, supress SPICE execution with:
 *        telltool simulation not execute
 *    which will cause deck generation only.  Then run SPICE externally
 *    and convert the output listing to a plot with:
 *        telltool simulation spice parse-output FILE
 *
 * 9) You can replace the internal spice header file that is part of the
 *        technology by defining the variable "SIM_spice_model_file" on the
 *        technology, which is the name of a file. You can add a trailer to the
 *    spice deck by attaching a variable "SIM_spice_trailer_file" to the
 *    technology, that is the name of a file. These variables are most
 *    easily created in the Info.variables window, by clicking on
 *    "Current Technology", then "New Attribute", defining "SIM_spice_model_file"
 *    with the name of the file. Remember to click on "Set Attribute".
 *    Include a full path if the file will not be in the current directory at
 *    run time.
 *
 * 10) You can call up special bipolar and jfet models that are defined in
 *    your header file by including an appropriate string in the variable
 *    "SIM_spice_model" that is attached to transistors when they
 *    are created. When the 'Transistor size' window appears, enter the
 *    model name, ex: LARGE_NPN
 *        The extractor will use the string in the call in the
 *    spice file, ex:   Q1 3 4 0 0 LARGE_NPN
 *    (in fact, any string that does not include the character '/' will
 *    be used to describe the transistor in the spice file; you can
 *    use this to define the attributes of individual transistors in your
 *    circuit. The character '/' is reserved as a separator for length
 *    and width values, and if present, causes the default type to be
 *    invoked.)
 *
 * 11) You can use the contents of a file to replace the extracted description
 *    of any facet, by attaching the name of the file as a variable
 *    called "SIM_spice_behave_file" to the prototype.  The extractor will always
 *    use the file, if found, instead of extracting the facet network, but it will
 *    still extract any subfacets in the facet. These in turn could also be described by
 *    behavior files. If an icon or different view is used, it can have a
 *    different behavior file than the other views.
 *
 */

/*
 * Extraction notes: Layers on arcs and nodes that overlap are
 * merged into polygons, and the area and perimeter of the resulting polygon
 * is computed. Overlapping areas are thus eliminated. The resultinmg areas
 * are used to calculate the capacitance on the net, with the following special
 * treatment: If the node or arc has multiple layers, the layer that gives the
 * largest capacitance is left as the only active capacitance, and the other
 * layers have an their area equal to their area on this port of the node
 * removed.
 * BUT, if the node or arc has a diffusion layer, that layer is always assumed
 * dominant, and the area of the nondominant layers are subtracted from
 * their accumulated area. This is not quite correct, when the diffusion area
 * only partly covers the other areas.
 * The NPCAPAC nodes (series capacitors) are defined to have a dominant
 * diffusion layer, so that their nondiffusion layers are cancelled out. In
 * order to cancel out the perimeter capacity of a top-plate layer, there
 * should be an identical-sized layer with a nonzero area capacitance value,
 * and a negative edge capacitance value equal to that of the layer to be
 * cancelled out.
 * The diffusion areas are gathered up according to whether they are declared
 * as n-type, or p-type, or undefined. DMOS are assumed n-type. The number
 * of n-transistors and p-transistors on the net are counted up, and the
 * corresponding diffusion shared equally.
 * It is assumed that the technology file has correctly used the bits that
 * define layer functions.
 * MOS Transistors must have a correct labelling of the source and drain, or
 * there may be problems in the simulations if the wrong end is connected to
 * the substrate. In this extractor, the order of extraction will be gate,
 * source, gate, drain, based on the order of the ports in the technology file.
 * This will be correct for PMOS with the Vdd at the top. The extracted values
 * source and drain areas will correspond to this order.
 * pMOS-transistors as taken from the technology file prototype will have their
 * source at the top (more positive end), and nMOS-transistors taken in the
 * prototype position will have to be rotated to have their drain at the top.
 * Other device types will output collector, base, emitter, corresponding to
 * extraction of the first three ports from the prototype in the tech file.
 * Otherwise manual editing of the SPICE file is required.
 */

#include "config.h"
#if SIMTOOL

#include "global.h"
#include "sim.h"
#include "eio.h"
#include "usr.h"
#include "network.h"
#include "efunction.h"
#include "tecschem.h"
#include "tecgen.h"
#include <math.h>

/* the spice node types returned by "sim_spice_nodetype" */
#define SPICEFACET     -1	/* sub-facet */
#define SPICEUNKNOWN    0	/* unimportant node (all connected) */
#define SPICENMOS       1	/* nMOS transistor */
#define SPICENMOS4      2	/* nMOS 4-port transistor */
#define SPICEDMOS       3	/* DMOS transistor */
#define SPICEDMOS4      4	/* DMOS 4-port transistor */
#define SPICEPMOS       5	/* PMOS transistor */
#define SPICEPMOS4      6	/* PMOS 4-port transistor */
#define SPICENPN        7	/* NPN transistor */
#define SPICENPN4       8	/* NPN 4-port transistor */
#define SPICEPNP        9	/* PNP transistor */
#define SPICEPNP4       10	/* PNP 4-port transistor */
#define SPICENJFET      11	/* N-JFET transistor */
#define SPICENJFET4     12	/* N-JFET 4-port transistor */
#define SPICEPJFET      13	/* P-JFET transistor */
#define SPICEPJFET4     14	/* P-JFET 4-port transistor */
#define SPICEDMES       15	/* Depletion MESFET transistor */
#define SPICEDMES4      16	/* Depletion MESFET 4-port transistor */
#define SPICEEMES       17	/* Enhancement MESFET transistor */
#define SPICEEMES4      18	/* Enhancement MESFET 4-port transistor */
#define SPICEREF        19	/* Self-referential transistor */
#define SPICERESISTOR   20	/* resistor */
#define SPICECAPACITOR  21	/* capacitor */
#define SPICEINDUCTOR   22	/* inductor */
#define SPICEDIODE      23	/* diode */
#define SPICEDIODEZ     24	/* Zener diode */
#define SPICESUBSTRATE  25	/* connection to substrate */
#define SPICETRANS      26	/* generic (special) transistor */
#define SPICEGROUND     27	/* A node connected to ground */
#define SPICEPOWER      28	/* A node connected to power */

#define DIFFTYPES       3	/* Types of diffusions & transistors plus 1 */
#define ISNONE	        0
#define ISNTYPE	        1
#define ISPTYPE	        2

#define SPICELEGALCHARS        "!#$%*+-/<>[]_"
#define CDLNOBRACKETLEGALCHARS "!#$%*+-/<>_"

static char       *sim_spicelegalchars;					/* legal characters */
static float       sim_spice_min_resist;				/* spice minimum resistance */
static float       sim_spice_min_capac;					/* spice minimum capacitance */
static char       *sim_spice_ac;						/* AC analysis message */
static char       *sim_spice_dc;						/* DC analysis message */
static char       *sim_spice_tran;						/* Transient analysis message */
static POLYGON    *sim_polygonfree = NOPOLYGON;			/* list of free simulation polygons */
static float       sim_spice_mask_scale = 1.0;			/* Mask shrink factor (default =1) */
static float      *sim_spice_extra_area = 0;			/* Duplicated area on each layer */
static INTBIG      sim_spice_diffusion_index[DIFFTYPES];/* diffusion layers indices */
static INTBIG      sim_spice_layer_count;
static INTBIG      sim_spice_unnamednum;
static INTBIG      sim_spice_netindex;
       INTBIG      sim_spice_levelkey;					/* key for "SIM_spice_level" */
       INTBIG      sim_spice_statekey;					/* key for "SIM_spice_state" */
       INTBIG      sim_spice_state;						/* value of "SIM_spice_state" */
       INTBIG      sim_spice_listingfilekey;			/* key for "SIM_listingfile" */
static char       *sim_spice_printcard;					/* the .PRINT or .PLOT card */
static TECHNOLOGY *sim_spice_tech;						/* default technology to use */
static INTBIG      sim_spicewirelisttotal = 0;
static NETWORK   **sim_spicewirelist;
static INTBIG      sim_spice_card_key = 0;				/* key for "SPICE_card" */
static INTBIG      sim_spice_template_key = 0;			/* key for "ATTR_SPICE_template" */
static INTBIG      sim_spiceglobalnetcount;				/* number of global nets */
static INTBIG      sim_spiceglobalnettotal = 0;			/* size of global net array */
static char      **sim_spiceglobalnets;					/* global net names */

/* working memory for "sim_spice_edge_capacitance()" */
static INTBIG *sim_spice_capacvalue = 0;

/******************** SPICE NET QUEUE ********************/

#define NOSPNET   ((SPNET *)-1)

typedef struct Ispnet
{
	NETWORK       *network;					/* network object associated with this */
	INTBIG         netnumber;				/* internal unique net number */
	float          diffarea[DIFFTYPES];		/* area of diffusion */
	float          diffperim[DIFFTYPES];	/* perimeter of diffusion */
	float          resistance;				/* amount of resistance */
	float          capacitance;				/* amount of capacitance */
	INTBIG         components[DIFFTYPES];	/* number of components connected to net */
	struct Ispnet *nextnet;					/* next in linked list */
} SPNET;

static SPNET     *sim_spice_firstnet;				/* first in list of nets in this facet */
static SPNET     *sim_spice_netfree = NOSPNET;		/* list of free nets */
static SPNET     *sim_spice_cur_net;				/* for polygon merging */

static NETWORK   *sim_spice_gnd;					/* net of ground */
static NETWORK   *sim_spice_vdd;					/* net of power */

/* prototypes for local routines */
static POLYGON   *sim_allocpolygon(void);
static void       sim_freepolygon(POLYGON*);
static SPNET     *sim_allocspnet(void);
static void       sim_freespnet(SPNET*);
static void       sim_spice_writeheader(NODEPROTO*, FILE*);
static INTBIG     sim_spicewritefacet(NODEPROTO*, FILE*, BOOLEAN, BOOLEAN, char*);
static INTBIG     sim_spice_nodetype(NODEINST*);
static SPNET     *sim_spice_getnet(NODEINST*, NETWORK*);
static void       sim_spice_nodearea(SPNET*, NODEINST*, PORTPROTO*);
static void       sim_spice_arcarea(SPNET*, ARCINST*);
static char      *sim_spice_element_name(NODEINST*, char, INTBIG*, char*);
static char      *sim_spice_node_name(SPNET*);
static void       sim_spice_xputs(char*, FILE*, BOOLEAN);
static void       sim_spice_store_box(TECHNOLOGY*, INTBIG, INTBIG, INTBIG, INTBIG, INTBIG);
static void       sim_spice_evalpolygon(INTBIG, TECHNOLOGY*, INTBIG*, INTBIG*, INTBIG);
static INTBIG     sim_spice_layerisdiff(TECHNOLOGY*, INTBIG);
static INTBIG     sim_spice_arcisdiff(ARCINST*);
static float      sim_spice_capacitance(TECHNOLOGY*, INTBIG);
static float      sim_spice_edge_capacitance(TECHNOLOGY*, INTBIG);
static NODEPROTO *sim_spice_topview(NODEPROTO*);
static void       sim_spice_write_twoport(FILE*, NODEINST*, INTBIG, char*, INTBIG*, INTBIG);
static void       sim_spice_writetrailer(FILE*);
static char      *sim_spice_net_name(NETWORK *net);
static INTBIG     sim_spiceisexported(SPNET *spnet, NODEPROTO *np);
static INTBIG     sim_spicegetexportednetworks(NODEPROTO *facet, NETWORK ***netlist,
					NETWORK **vdd, NETWORK **gnd, BOOLEAN cdl);
static int        sim_spicesortnetworksbyname(const void *e1, const void *e2);
static void       sim_spicedumpstringerror(FILE *f);
static void       sim_spicegatherglobals(NODEPROTO *np);
static char      *sim_spicedescribenetwork(NETWORK *net);
static char      *sim_spice_legal_name(char *name);

/*
 * Routine to free all memory associated with this module.
 */
void sim_freespicememory(void)
{
	REGISTER POLYGON *poly;
	REGISTER SPNET *spnet;
	REGISTER INTBIG i;

	while (sim_polygonfree != NOPOLYGON)
	{
		poly = sim_polygonfree;
		sim_polygonfree = sim_polygonfree->nextpolygon;
		freepolygon(poly);
	}

	while (sim_spice_netfree != NOSPNET)
	{
		spnet = sim_spice_netfree;
		sim_spice_netfree = sim_spice_netfree->nextnet;
		efree((char *)spnet);
	}
	if (sim_spice_capacvalue != 0) efree((char *)sim_spice_capacvalue);
	if (sim_spicewirelisttotal > 0) efree((char *)sim_spicewirelist);
	for(i=0; i<sim_spiceglobalnettotal; i++)
		if (sim_spiceglobalnets[i] != 0)
			efree((char *)sim_spiceglobalnets[i]);
	if (sim_spiceglobalnettotal > 0)
		efree((char *)sim_spiceglobalnets);
}

/******************** SPICE DECK GENERATION ********************/

/*
 * Procedure to write a spice deck to describe facet "np".
 * If "cdl" is true, put handle CDL format (no parameters, no top-level).
 */
void sim_writespice(NODEPROTO *np, BOOLEAN cdl)
{
	FILE *spfile;
	REGISTER INTBIG retval, *curstate;
	REGISTER INTBIG analysiscards, i;
	char deckfile[256], *pt;
	REGISTER VARIABLE *var;
	REGISTER LIBRARY *lib;
	REGISTER NODEPROTO *onp;
	REGISTER TECHNOLOGY *t;
	char templatefile[256], *libname, *libpath;
	extern TOOL *io_tool;

	/* make sure network tool is on */
	if ((net_tool->toolstate&TOOLON) == 0)
	{
		ttyputerr(_("Network tool must be running...turning it on"));
		toolturnon(net_tool, FALSE);
		ttyputerr(_("...now reissue the simulation command"));
		return;
	}

	if (np == NONODEPROTO)
	{
		ttyputerr(_("Must have a facet to edit"));
		return;
	}
	sim_simnt = np;

	/* determine technology to use */
	sim_spice_tech = sim_defschematictechnology(np->tech);
	sim_spice_layer_count = sim_spice_tech->layercount;

	/* get the SPICE state */
	var = getvalkey((INTBIG)sim_tool, VTOOL, VINTEGER, sim_spice_statekey);
	if (var != NOVARIABLE) sim_spice_state = var->addr; else sim_spice_state = 0;

	/* get the overall minimum resistance and capacitance */
	var = getval((INTBIG)sim_spice_tech, VTECHNOLOGY, VFLOAT, "SIM_spice_min_resistance");
	if (var != NOVARIABLE) sim_spice_min_resist = castfloat(var->addr); else
		sim_spice_min_resist = 0.0;
	var = getval((INTBIG)sim_spice_tech, VTECHNOLOGY, VFLOAT, "SIM_spice_min_capacitance");
	if (var != NOVARIABLE) sim_spice_min_capac = castfloat(var->addr); else
		sim_spice_min_capac = 0.0;
	var = getval((INTBIG)sim_spice_tech, VTECHNOLOGY, VFLOAT, "SIM_spice_mask_scale");
	if (var != NOVARIABLE) sim_spice_mask_scale = castfloat(var->addr); else
		sim_spice_mask_scale = 1.0;
	sim_spice_extra_area = (float *)emalloc(sizeof(float) * sim_spice_layer_count,
		sim_tool->cluster);

	/* get the layer resistance and capacitance arrays for each technology */
	for(t = el_technologies; t != NOTECHNOLOGY; t = t->nexttechnology)
	{
		var = getval((INTBIG)t, VTECHNOLOGY, VFLOAT|VISARRAY, "SIM_spice_resistance");
		t->temp1 = (var == NOVARIABLE ? 0 : var->addr);
		var = getval((INTBIG)t, VTECHNOLOGY, VFLOAT|VISARRAY, "SIM_spice_capacitance");
		t->temp2 = (var == NOVARIABLE ? 0 : var->addr);
	}

	/* setup the legal characters */
	sim_spicelegalchars = SPICELEGALCHARS;

	/* start writing the spice deck */
	if (cdl)
	{
		/* setup bracket conversion for CDL */
		curstate = io_getstatebits();
		if ((curstate[1]&CDLNOBRACKETS) != 0)
			sim_spicelegalchars = CDLNOBRACKETLEGALCHARS;

		(void)strcpy(deckfile, np->cell->cellname);
		(void)strcat(deckfile, ".cdl");
		pt = deckfile;
		spfile = xcreate(deckfile, sim_filetypecdl, _("CDL File"), &pt);
		if (pt != 0) (void)strcpy(deckfile, pt);
		if (spfile == NULL)
		{
			ttyputerr(_("Cannot create CDL file: %s"), deckfile);
			return;
		}
		sim_spice_xprintf(spfile, TRUE, "* First line is ignored\n");
	} else
	{
		(void)strcpy(deckfile, np->cell->cellname);
		(void)strcat(deckfile, ".spi");
		pt = deckfile;
		spfile = xcreate(deckfile, sim_filetypespice, _("SPICE File"), &pt);
		if (pt != 0) (void)strcpy(deckfile, pt);
		if (spfile == NULL)
		{
			ttyputerr(_("Cannot create SPICE file: %s"), deckfile);
			return;
		}
		(void)sim_spice_writeheader(np, spfile);
	}

	/* gather all global signal names */
	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
		for(onp = lib->firstnodeproto; onp != NONODEPROTO; onp = onp->nextnodeproto)
			onp->temp1 = 0;
	sim_spiceglobalnetcount = 0;
	sim_spicegatherglobals(np);
	if (sim_spiceglobalnetcount > 0)
	{
		sim_spice_xprintf(spfile, FALSE, "\n");
		(void)initinfstr();
		(void)addstringtoinfstr(".global");
		for(i=0; i<sim_spiceglobalnetcount; i++)
			(void)formatinfstr(" %s", sim_spiceglobalnets[i]);
		sim_spice_xprintf(spfile, FALSE, "%s\n", returninfstr());
	}

	/* reset bits on all facets */
	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
		for(onp = lib->firstnodeproto; onp != NONODEPROTO; onp = onp->nextnodeproto)
	{
		onp->temp1 = 0;
		onp->cell->temp1 = 0;
	}
	sim_spice_unnamednum = 1;

	/* initialize for parameterized cells */
	initparameterizedcells();
	begintraversehierarchy();

	/*
	 * initialize the .PRINT or .PLOT card.
	 * As we find sources/meters, we will tack things onto this string
	 * and 'reallocstring' it.
	 * It is printed and then freed in sim_spice_writefacet.
	 */
	if (allocstring(&sim_spice_printcard, "", sim_tool->cluster)) return;

	/* we don't know the type of analysis yet... */
	sim_spice_ac = sim_spice_dc = sim_spice_tran = NULL;

	/* initialize the polygon merging system */
	mrginit();

	/* recursively write all facets below this one */
	retval = sim_spicewritefacet(np, spfile, TRUE, cdl, 0);
	if (retval < 0) ttyputnomemory();
	if (retval > 0)
		ttyputmsg(_("Back-annotation information has been added (library must be saved)"));

	/* handle AC, DC, and TRAN analysis cards */
	analysiscards = 0;
	if (sim_spice_dc != NULL) analysiscards++;
	if (sim_spice_tran != NULL) analysiscards++;
	if (sim_spice_ac != NULL) analysiscards++;
	if (analysiscards > 1)
		ttyputerr(_("WARNING: can only have one DC, Transient or AC source node"));
	if (sim_spice_tran != NULL)
	{
		sim_spice_xprintf(spfile, FALSE, "%s\n", sim_spice_tran);
		efree(sim_spice_tran);
	} else if (sim_spice_ac != NULL)
	{
		sim_spice_xprintf(spfile, FALSE, "%s\n", sim_spice_ac);
		efree(sim_spice_ac);
	} else if (sim_spice_dc != NULL)
	{
		sim_spice_xprintf(spfile, FALSE, "%s\n", sim_spice_dc);
		efree(sim_spice_dc);
	}

	if (!cdl)
	{
		sim_spice_writetrailer(spfile);
		sim_spice_xprintf(spfile, FALSE, ".END\n");
	}
	xclose(spfile);
	ttyputmsg(_("%s written"), deckfile);
	efree((char *)sim_spice_extra_area);

	/* finish polygon merging subsystem */
	mrgterm();

	if (cdl)
	{
		/* write the control files */
		(void)strcpy(templatefile, np->cell->cellname);
		(void)strcat(templatefile, ".cdltemplate");
		spfile = xcreate(templatefile, sim_filetypectemp, _("CDL Template File"), &pt);
		if (pt != 0) (void)strcpy(templatefile, pt);
		if (spfile == NULL)
		{
			ttyputerr(_("Cannot create CDL template file: %s"), templatefile);
			return;
		}
		for(i=strlen(deckfile)-1; i>0; i--) if (deckfile[i] == DIRSEP) break;
		if (deckfile[i] == DIRSEP) deckfile[i++] = 0;
		var = getval((INTBIG)io_tool, VTOOL, VSTRING, "IO_cdl_library_name");
		if (var == NOVARIABLE) libname = ""; else
			libname = (char *)var->addr;
		var = getval((INTBIG)io_tool, VTOOL, VSTRING, "IO_cdl_library_path");
		if (var == NOVARIABLE) libpath = ""; else
			libpath = (char *)var->addr;
		xprintf(spfile, "cdlInKeys = list(nil\n");
		xprintf(spfile, "    'searchPath             \"%s", deckfile);
		if (libpath[0] != 0)
			xprintf(spfile, "\n                             %s", libpath);
		xprintf(spfile, "\"\n");
		xprintf(spfile, "    'cdlFile                \"%s\"\n", &deckfile[i]);
		xprintf(spfile, "    'userSkillFile          \"\"\n");
		xprintf(spfile, "    'opusLib                \"%s\"\n", libname);
		xprintf(spfile, "    'primaryCell            \"%s\"\n", np->cell->cellname);
		xprintf(spfile, "    'caseSensitivity        \"preserve\"\n");
		xprintf(spfile, "    'hierarchy              \"flatten\"\n");
		xprintf(spfile, "    'cellTable              \"\"\n");
		xprintf(spfile, "    'viewName               \"netlist\"\n");
		xprintf(spfile, "    'viewType               \"\"\n");
		xprintf(spfile, "    'pr                     nil\n");
		xprintf(spfile, "    'skipDevice             nil\n");
		xprintf(spfile, "    'schemaLib              \"sample\"\n");
		xprintf(spfile, "    'refLib                 \"\"\n");
		xprintf(spfile, "    'globalNodeExpand       \"full\"\n");
		xprintf(spfile, ")\n");
		xclose(spfile);
		ttyputmsg(_("%s written"), templatefile);
		ttyputmsg("Now type: exec nino CDLIN %s &", templatefile);
	}

	/* run spice (if requested) */
	var = getvalkey((INTBIG)sim_tool, VTOOL, VINTEGER, sim_dontrunkey);
	if (var != NOVARIABLE && var->addr != SIMRUNNO)
	{
		ttyputmsg(_("Running SPICE..."));
		var = getvalkey((INTBIG)sim_tool, VTOOL, VSTRING, sim_spice_listingfilekey);
		if (var == NOVARIABLE) sim_spice_execute(deckfile, ""); else
			sim_spice_execute(deckfile, (char *)var->addr);
	}
}

/*
 * routine to write facet "np" and its all referenced subnodeprotos in SPICE
 * format to file "f".  If "top" is true, this is the top facet.  The
 * spice level is "tool:sim.SIM_spice_level" and "sim_spice_min_resist" and
 * "sim_spice_min_capac" are the minimum required resistance and capacitance.
 * Returns negative on error, positive if back annotation was added.
 */
INTBIG sim_spicewritefacet(NODEPROTO *np, FILE *f, BOOLEAN top, BOOLEAN cdl, char *paramname)
{
	REGISTER NODEINST *ni;
	REGISTER NODEPROTO *subnt, *cnp;
	REGISTER PORTPROTO *pp, *biasport, *cpp;
	PORTPROTO *gate, *source, *drain, *gatedummy;
	NETWORK **netlist, *subvdd, *subgnd;
	REGISTER PORTARCINST *pi;
	REGISTER PORTEXPINST *pe;
	REGISTER ARCINST *ai;
	REGISTER SPNET *spnet, *nspnet, *gaten, *sourcen, *drainn, *posnet, *negnet,
		*subnet, *biasn;
	float a, b;
	REGISTER BOOLEAN backannotate;
	REGISTER INTBIG state, j, first, i, nodetype, retval, len,
		reallambda, nodelambda, netcount, err, sigcount, dumpfacet, nodewidth, nindex;
	INTBIG subfacetindex, nodeindex, resistnum, capacnum, inductnum, diodenum;
	REGISTER VARIABLE *var, *vartemplate;
	REGISTER NETWORK *net, *rnet;
	char *extra, *info, line[100];
	REGISTER char *pname, *pt, *start, save;
	INTBIG lx, ly;
	INTBIG bipolars, nmostrans, pmostrans;
	char c, *filename;
	FILE *modelfile;
	char *uncon_diff_type, **nodenames;

	/* stop if requested */
	if (stopping(STOPREASONDECK)) return(-1);

	/* make sure key is cached */
	if (sim_spice_template_key == 0)
		sim_spice_template_key = makekey("ATTR_SPICE_template");

	/* look for a model file on the current facet */
	var = getval((INTBIG)np, VNODEPROTO, VSTRING, "SIM_spice_behave_file");

	/* if this facet's view has a behavioral model attached, then use the facet */
	if (var == NOVARIABLE)
	{
		/* if not, try to use view in same view as top-level facet */
		np = sim_spice_topview(np);
	}

	/* first pass through the node list */
	/* if there are any sub-facets that have not been written, write them */
	backannotate = FALSE;
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		subnt = ni->proto;
		if (subnt->primindex != 0) continue;

		/* ignore recursive references (showing icon in contents) */
		if (subnt->cell == np->cell) continue;

		/* see if this facet has a template */
		var = getvalkey((INTBIG)subnt, VNODEPROTO, VSTRING, sim_spice_template_key);
		if (var != NOVARIABLE) continue;

		/* use the contents view if this is an icon */
		if (subnt->cellview == el_iconview)
		{
			cnp = contentsview(subnt);
			if (cnp != NONODEPROTO)
			{
				subnt = cnp;

				/* see if this contents facet has a template */
				var = getvalkey((INTBIG)subnt, VNODEPROTO, VSTRING, sim_spice_template_key);
				if (var != NOVARIABLE) continue;
			}
		}

		/* make sure the subfacet hasn't already been written */
		dumpfacet = 1;
		if (subnt->temp1 != 0) dumpfacet = 0;
		if (subnt->cell->temp1 != 0) dumpfacet = 0;

		/* see if this is a new parameterized cell combination */
		pname = parameterizedname(ni);
		if (pname != 0)
		{
			if (inparameterizedcells(subnt->cell, pname) == 0)
			{
				dumpfacet = 1;
				addtoparameterizedcells(subnt->cell, pname);
			}
		}

		if (dumpfacet != 0)
		{
			downhierarchy(ni, 0);
			retval = sim_spicewritefacet(subnt, f, FALSE, cdl, pname);
			uphierarchy();
			if (retval < 0) return(-1);
			if (retval > 0) backannotate = TRUE;
		}
		if (pname != 0) efree((char *)pname);
	}

	/* make sure that all nodes have names on them */
	if (asktool(net_tool, "name-nodes", (INTBIG)np) != 0) backannotate = TRUE;
	if (asktool(net_tool, "name-nets", (INTBIG)np) != 0) backannotate = TRUE;

	/* mark this facet as written */
	np->temp1 = 1;
	np->cell->temp1 = 1;

	/* look on the current view of the cell for models */
	var = getval((INTBIG)np, VNODEPROTO, VSTRING, "SIM_spice_behave_file");
	if (var != NOVARIABLE)
	{
		modelfile = xopen((char *)var->addr, sim_filetypespice,  el_libdir, &filename);
		if (modelfile != NULL)
		{
			/* copy file */
			c = (char)xgetc(modelfile);
			while (!xeof(modelfile))
			{
				(void)xputc(c, f);
				c = (char)xgetc(modelfile);
			}
			if (backannotate) return(1);
			return(0);
		} else ttyputerr(_("WARNING: cannot open model file: %s"), (char *)var->addr);
	}

	/* find all networks in this facet, and find power and ground */
	netcount = sim_spicegetexportednetworks(np, &netlist, &sim_spice_vdd, &sim_spice_gnd, cdl);

	/* make sure power and ground appear at the top level */
	if (top && (sim_spice_state&SPICEGLOBALPG) == 0)
	{
		if (sim_spice_vdd == NONETWORK)
			ttyputerr(_("WARNING: cannot find power at top level of circuit"));
		if (sim_spice_gnd == NONETWORK)
			ttyputerr(_("WARNING: cannot find ground at top level of circuit"));
	}

	/* zero the temp1 for arcs and nodes to compute area */
	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst) ai->temp1 = 0;
	for(net = np->firstnetwork; net != NONETWORK; net = net->nextnetwork) net->temp1 = 0;
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
		ni->temp1 = ni->temp2 = 0;
	bipolars = nmostrans = pmostrans = 0;

	/* create linked list of electrical nets in this facet */
	sim_spice_firstnet = NOSPNET;

	/* must have a default node 0 in subfacets */
	nodeindex = subfacetindex = 1;
	sim_spice_netindex = 2;	   /* save 1 for the substrate */

	/* look at all arcs in the facet */
	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
	{
		if (ai->temp1 != 0) continue;

		/* don't count non-electrical arcs */
		if (((ai->proto->userbits&AFUNCTION)>>AFUNCTIONSH) == APNONELEC) continue;

		/* ignore busses */
		if (ai->network->signals > 1) continue;

		/* add this net to the list */
		spnet = sim_allocspnet();
		if (spnet == NOSPNET) return(-1);
		spnet->network = ai->network;
		ai->network->temp1 = (INTBIG)spnet;
		spnet->netnumber = sim_spice_netindex++;

		/* reset */
		for (j = 0; j < sim_spice_layer_count; j++) sim_spice_extra_area[j] = 0.0;
		for (j = 0; j < DIFFTYPES; j++) sim_spice_diffusion_index[j] = -1;
		spnet->nextnet = sim_spice_firstnet;
		sim_spice_firstnet = spnet;

		sim_spice_arcarea(spnet, ai); /* builds areas, etc */

		/* get merged polygons so far */
		sim_spice_cur_net = spnet;
		mrgdonefacet(sim_spice_evalpolygon);
	}

	/* now add any unwired but exported ports to the list of nets */
	for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
	{
		/* determine the net number of the export */
		if (pp->network->temp1 != 0) continue;

		/* add this net to the list */
		spnet = sim_allocspnet();
		if (spnet == NOSPNET) return(-1);
		spnet->network = pp->network;
		pp->network->temp1 = (INTBIG)spnet;
		spnet->netnumber = sim_spice_netindex++;

		/* reset */
		ni = pp->subnodeinst;
		for (j = 0; j < sim_spice_layer_count; j++) sim_spice_extra_area[j] = 0.0;
		for (j = 0; j < DIFFTYPES; j++) sim_spice_diffusion_index[j] = -1;
		sim_spice_nodearea(spnet, ni, pp->subportproto);

		/* get merged polygons so far */
		sim_spice_cur_net = spnet;
		mrgdonefacet(sim_spice_evalpolygon);

		/* count the number of components on the net */
		state = sim_spice_nodetype(ni);
		switch (state)
		{
			case SPICEEMES:
			case SPICEEMES4:
			case SPICEDMES:
			case SPICEDMES4:
			case SPICEDMOS:
			case SPICEDMOS4:
			case SPICENMOS:
			case SPICENMOS4: nodetype = ISNTYPE; break;
			case SPICEPMOS:
			case SPICEPMOS4: nodetype = ISPTYPE; break;
			default:         nodetype = ISNONE;  break;
		}

		/* only count drains and source connections in counting transistors */
		if (nodetype == ISNONE)
			spnet->components[nodetype]++; /* We don't use this, anyhow */
		else
		{
			transistorports(ni, &gate, &gatedummy, &source, &drain);
			if (pp->subportproto == source || pp->subportproto == drain)
			{
				spnet->components[nodetype]++;
			}
		}
		spnet->nextnet = sim_spice_firstnet;
		sim_spice_firstnet = spnet;
	}

	/* create Spice net information for all remaining nets in the facet */
	for(net = np->firstnetwork; net != NONETWORK; net = net->nextnetwork)
	{
		if (net->temp1 != 0) continue;

		/* add this net to the list */
		spnet = sim_allocspnet();
		if (spnet == NOSPNET) return(-1);
		net->temp1 = (INTBIG)spnet;
		spnet->network = net;
		spnet->netnumber = sim_spice_netindex++;
		spnet->nextnet = sim_spice_firstnet;
		sim_spice_firstnet = spnet;
	}

	/* make sure the ground net is number zero */
	if (sim_spice_gnd != NONETWORK)
		((SPNET *)sim_spice_gnd->temp1)->netnumber = 0;

	posnet = negnet = subnet = NOSPNET;

	/* second pass through the node list */ 
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		state = sim_spice_nodetype(ni);
		if (state == SPICEUNKNOWN) continue;
		if (state == SPICESUBSTRATE)
		{
			if (subnet == NOSPNET)
				subnet = sim_spice_getnet(ni, ni->proto->firstportproto->network);
			continue;
		}
		switch (state)
		{
			case SPICENPN:
			case SPICENPN4:
			case SPICEPNP:
			case SPICEPNP4:
			case SPICETRANS:
				nodetype = ISNONE;
				bipolars++;
				break;
			case SPICEEMES:
			case SPICEEMES4:
			case SPICEDMES:
			case SPICEDMES4:
			case SPICEDMOS:
			case SPICEDMOS4:
			case SPICENMOS:
			case SPICENMOS4:
				nodetype = ISNTYPE;
				nmostrans++;
				break;
			case SPICEPMOS:
			case SPICEPMOS4:
				nodetype = ISPTYPE;
				pmostrans++;
				break;
			default:
				nodetype = ISNONE;
				break;
		}

		/*
		 * find all wired ports on component and increment their count,
		 * but only if they are a drain or source
		 */
		for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
		{
			ai = pi->conarcinst;
			for(spnet = sim_spice_firstnet; spnet != NOSPNET; spnet = spnet->nextnet)
			{
				if (spnet->network == ai->network)
				{
					if (nodetype == ISNONE) spnet->components[nodetype]++; else
					{
						transistorports(ni, &gate, &gatedummy, &source, &drain);
						if (pi->proto == source || pi->proto == drain)
						{
							spnet->components[nodetype]++;
						}
					}
				}
			}
		}
	}

	/* use ground net for substrate */
	if (subnet == NOSPNET && sim_spice_gnd != NONETWORK)
		subnet = (SPNET *)sim_spice_gnd->temp1;

	if (pmostrans != 0 && posnet == NOSPNET)
	{
		if (sim_spice_vdd == NONETWORK)
		{
			(void)initinfstr();
			(void)formatinfstr(_("WARNING: no power connection for P-transistor wells in facet %s"),
				describenodeproto(np));
			sim_spicedumpstringerror(f);
		} else posnet = (SPNET *)sim_spice_vdd->temp1;
	}
	if (nmostrans != 0 && negnet == NOSPNET)
	{
		if (sim_spice_gnd == NONETWORK)
		{
			(void)initinfstr();
			(void)formatinfstr(_("WARNING: no connection for N-transistor wells in facet %s"),
				describenodeproto(np));
			sim_spicedumpstringerror(f);
		} else negnet = (SPNET *)sim_spice_gnd->temp1;
	}

	if (bipolars != 0 && subnet == NOSPNET)
	{
		(void)initinfstr();
		(void)formatinfstr(_("WARNING: no explicit connection to the substrate in facet %s"),
			describenodeproto(np));
		sim_spicedumpstringerror(f);
		if (sim_spice_gnd != NONETWORK)
		{
			ttyputmsg(_("     A connection to ground will be used if necessary."));
			subnet = (SPNET *)sim_spice_gnd->temp1;
		}
	}

	/* generate header for subckt or top-level facet */
	if (top && !cdl)
	{
		sim_spice_xprintf(f, TRUE, "\n*** TOP LEVEL FACET: %s\n", describenodeproto(np));
	} else
	{
		if (paramname != 0)
		{
			/* parameterized subcircuit name */
			sim_spice_xprintf(f, FALSE, "\n.SUBCKT %s", sim_spice_legal_name(paramname));
		} else
		{
			sim_spice_xprintf(f, FALSE, "\n.SUBCKT %s", sim_spice_legal_name(np->cell->cellname));
		}
		for(i=0; i<netcount; i++)
		{
			net = netlist[i];
			if (net == sim_spice_gnd)
			{
				if ((sim_spice_state&SPICEGLOBALPG) != 0 ||
					(sim_spice_state&SPICENODENAMES) == 0) continue;
			}
			if (net == sim_spice_vdd)
			{
				if ((sim_spice_state&SPICEGLOBALPG) != 0) continue;
			}
			if (cdl)
			{
				if (i > 0 && netlist[i-1]->temp2 != net->temp2)
					sim_spice_xprintf(f, FALSE, " /");
			}
			sim_spice_xprintf(f, FALSE, " %s", sim_spice_net_name(net));
		}
		sim_spice_xprintf(f, FALSE, "\n");

		/* generate pin descriptions for reference (when not using node names) */
		if ((sim_spice_state&SPICEGLOBALPG) == 0)
		{
			if (sim_spice_vdd != NONETWORK)
				sim_spice_xprintf(f, TRUE, "** POWER NET: %s\n",
					sim_spicedescribenetwork(sim_spice_vdd));
			if (sim_spice_gnd != NONETWORK)
			{
				if ((sim_spice_state&SPICENODENAMES) == 0)
				{
					sim_spice_xprintf(f, TRUE, "** GROUND NET: 0 (%s)\n",
						sim_spicedescribenetwork(sim_spice_gnd));
				} else
				{
					sim_spice_xprintf(f, TRUE, "** GROUND NET: %s\n",
						sim_spicedescribenetwork(sim_spice_gnd));
				}
			}
		}

		/* write exports to this facet */
		for(i=0; i<netcount; i++)
		{
			net = netlist[i];
			if (net == sim_spice_gnd || net == sim_spice_vdd) continue;
			sim_spice_xprintf(f, TRUE, "** PORT %s", sim_spice_net_name(net));
			if (net->namecount > 0)
				sim_spice_xprintf(f, TRUE, " (network: %s)", sim_spicedescribenetwork(net));
			sim_spice_xprintf(f, TRUE, "\n");
		}
	}

	/* now run through all components in the facet */
	resistnum = capacnum = inductnum = diodenum = 1;

	/* third pass through the node list, print it this time */
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		/* get the type of this node */
		state = sim_spice_nodetype(ni);

		/* handle sub-facet calls */
		if (state == SPICEFACET)
		{
			/* ignore recursive references (showing icon in contents) */
			if (ni->proto->cell == np->cell) continue;

			/* see if the node is arrayed */
			nodewidth = ni->arraysize;
			if (nodewidth < 1) nodewidth = 1;

			/* look for a SPICE template on the prototype */
			vartemplate = getvalkey((INTBIG)ni->proto, VNODEPROTO, VSTRING, sim_spice_template_key);

			/* get the ports on this node (in proper order) */
			cnp = contentsview(ni->proto);
			if (cnp == NONODEPROTO) cnp = ni->proto; else
			{
				/* if there is a contents view, look for a SPICE template there, too */
				if (vartemplate == NOVARIABLE)
					vartemplate = getvalkey((INTBIG)cnp, VNODEPROTO, VSTRING, sim_spice_template_key);
			}
			netcount = sim_spicegetexportednetworks(cnp, &netlist, &subvdd, &subgnd, cdl);
			err = 0;

			/* if the node is arrayed, get the names of each instantiation */
			if (nodewidth > 1)
			{
				var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name_key);
				if (var == NOVARIABLE) nodewidth = 1; else
				{
					sigcount = net_evalbusname(APBUS, (char *)var->addr, &nodenames,
						NOARCINST, NONODEPROTO, 0);
					if (sigcount != nodewidth) nodewidth = 1;
				}
			}

			/* loop through each arrayed instantiation of this node */
			for(nindex=0; nindex<nodewidth; nindex++)
			{
				for(net = cnp->firstnetwork; net != NONETWORK; net = net->nextnetwork)
					net->temp1 = (INTBIG)NONETWORK;
				for(pp = ni->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
				{
					cpp = equivalentport(ni->proto, pp, cnp);
					net = NONETWORK;
					for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
						if (pi->proto == pp) break;
					if (pi != NOPORTARCINST) net = pi->conarcinst->network; else
					{
						for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
							if (pe->proto == pp) break;
						if (pe != NOPORTEXPINST) net = pe->exportproto->network;
					}
					if (net == NONETWORK) continue;
					if (net->signals > 1)
					{
						sigcount = net->signals;
						if (nodewidth > 1 && cpp->network->signals * nodewidth == net->signals)
						{
							/* map wide bus to a particular instantiation of an arrayed node */
							if (cpp->network->signals == 1)
							{
								cpp->network->temp1 = (INTBIG)net->networklist[nindex];
							} else
							{
								for(i=0; i<cpp->network->signals; i++)
									cpp->network->networklist[i]->temp1 =
										(INTBIG)net->networklist[i + nindex*cpp->network->signals];
							}
						} else
						{
							if (cpp->network->signals != net->signals)
							{
								ttyputerr(_("***ERROR: port %s on node %s in facet %s is %d wide, but is connected/exported with width %d"),
									pp->protoname, describenodeinst(ni), describenodeproto(np),
										cpp->network->signals, net->signals);
								sigcount = mini(sigcount, cpp->network->signals);
								if (sigcount == 1) sigcount = 0;
							}
							cpp->network->temp1 = (INTBIG)net;
							for(i=0; i<sigcount; i++)
								cpp->network->networklist[i]->temp1 = (INTBIG)net->networklist[i];
						}
					} else
					{
						cpp->network->temp1 = (INTBIG)net;
					}
				}

				/* handle self-defined models */
				if (vartemplate != NOVARIABLE)
				{
					(void)initinfstr();
					for(pt = (char *)vartemplate->addr; *pt != 0; pt++)
					{
						if (pt[0] != '$' || pt[1] != '(')
						{
							(void)addtoinfstr(*pt);
							continue;
						}
						start = pt + 2;
						for(pt = start; *pt != 0; pt++)
							if (*pt == ')') break;
						save = *pt;
						*pt = 0;
						for(pp = ni->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
							if (namesame(start, pp->protoname) == 0) break;
						if (pp != NOPORTPROTO)
						{
							/* port name found: use its spice node */
							spnet = sim_spice_getnet(ni, pp->network);
							if (spnet == NOSPNET)
							{
								if ((sim_spice_state&SPICENODENAMES) != 0)
									(void)formatinfstr("UNNAMED%ld", sim_spice_unnamednum++); else
										(void)formatinfstr("%ld", sim_spice_netindex++);
								err++;
							} else
								(void)addstringtoinfstr(sim_spice_net_name(spnet->network));
						} else
						{
							/* no port name found, look for variable name */
							sprintf(line, "ATTR_%s", start);
							var = getval((INTBIG)ni, VNODEINST, -1, line);
							if (var == NOVARIABLE)
								var = getval((INTBIG)ni, VNODEINST, -1, start);
							if (var == NOVARIABLE)
							{
								(void)addstringtoinfstr("??");
								err++;
							} else
								(void)addstringtoinfstr(describesimplevariable(var));
						}
						*pt = save;
						if (save == 0) break;
					}
					sim_spice_xprintf(f, FALSE, "%s\n", returninfstr());
					continue;
				}

				if (nodewidth > 1)
				{
					pt = sim_spice_element_name(ni, 'X', &subfacetindex, nodenames[nindex]);
				} else
				{
					pt = sim_spice_element_name(ni, 'X', &subfacetindex, 0);
				}
				sim_spice_xputs(pt, f, FALSE);

				for(i=0; i<netcount; i++)
				{
					net = netlist[i];
					if (net == subgnd)
					{
						if ((sim_spice_state&SPICEGLOBALPG) != 0 ||
							(sim_spice_state&SPICENODENAMES) == 0) continue;
					} else if (net == subvdd)
					{
						if ((sim_spice_state&SPICEGLOBALPG) != 0) continue;
					}

					rnet = (NETWORK *)net->temp1;
					if (rnet == NONETWORK)
					{
						if ((sim_spice_state&SPICENODENAMES) != 0)
							sprintf(line, " UNNAMED%ld", sim_spice_unnamednum++); else
								sprintf(line, " %ld", sim_spice_netindex++);
						sim_spice_xputs(line, f, FALSE);

						/* do not report errors on power or ground net */
						if (net == subvdd || net == subgnd) continue;
						for(pp = cnp->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
						{
							if ((pp->userbits&STATEBITS) != OUTPORT &&
								(pp->userbits&STATEBITS) != BIDIRPORT) continue;
							if (pp->network == net) break;
							if (pp->network->signals <= 1) continue;
							for(j=0; j<pp->network->signals; j++)
								if (pp->network->networklist[j] == net) break;
							if (j < pp->network->signals) break;
						}
						if (pp != NOPORTPROTO) continue;
						err++;
						continue;
					}
					spnet = (SPNET *)rnet->temp1;
					if (spnet->netnumber == 0 && (sim_spice_state&SPICENODENAMES) == 0) continue;
					sim_spice_xputs(sim_spice_node_name(spnet), f, FALSE);
				}
				if (cdl) sim_spice_xprintf(f, FALSE, " /");
				pname = parameterizedname(ni);
				if (pname == 0)
				{
					sim_spice_xprintf(f, FALSE, " %s\n", sim_spice_legal_name(ni->proto->cell->cellname));
				} else
				{
					sim_spice_xprintf(f, FALSE, " %s\n", sim_spice_legal_name(pname));
					efree((char *)pname);
				}
			}
			if (err != 0)
			{
				(void)initinfstr();
				(void)formatinfstr(_("WARNING: subfacet %s is not fully connected in facet %s"),
					describenodeinst(ni), describenodeproto(np));
				sim_spicedumpstringerror(f);
			}
			continue;
		}

		/* handle resistors, inductors, capacitors, and diodes */
		if (state == SPICERESISTOR || state == SPICECAPACITOR || state == SPICEINDUCTOR ||
			state == SPICEDIODE || state == SPICEDIODEZ)
		{
			switch (state)
			{
				case SPICERESISTOR:		/* resistor */
					var = getvalkey((INTBIG)ni, VNODEINST, -1, sch_resistancekey);
					if (var == NOVARIABLE) extra = ""; else
						extra = describesimplevariable(var);
					sim_spice_write_twoport(f, ni, state, extra, &resistnum, 1);
					break;
				case SPICECAPACITOR:	/* capacitor */
					var = getvalkey((INTBIG)ni, VNODEINST, -1, sch_capacitancekey);
					if (var == NOVARIABLE) extra = ""; else
						extra = describesimplevariable(var);
					sim_spice_write_twoport(f, ni, state, extra, &capacnum, 1);
					break;
				case SPICEINDUCTOR:		/* inductor */
					var = getvalkey((INTBIG)ni, VNODEINST, -1, sch_inductancekey);
					if (var == NOVARIABLE) extra = ""; else
						extra = describesimplevariable(var);
					sim_spice_write_twoport(f, ni, state, extra, &inductnum, 1);
					break;
				case SPICEDIODE:		/* diode */
				case SPICEDIODEZ:		/* Zener diode */
					var = getvalkey((INTBIG)ni, VNODEINST, -1, sch_diodekey);
					if (var == NOVARIABLE) extra = ""; else
						extra = describesimplevariable(var);
					sim_spice_write_twoport(f, ni, state, extra, &diodenum, 1);
					break;
			}
			continue;
		}

		/* the default is to handle everything else as a transistor */
		if (state == SPICEUNKNOWN || state == SPICESUBSTRATE ||
			state == SPICEGROUND || state == SPICEPOWER)
				continue;

		/* get the source, gates, and drain for the transistor */
		transistorports(ni, &gate, &gatedummy, &source, &drain);

		/* determine the net numbers for these parts of the transistor */
		gaten = sim_spice_getnet(ni, gate->network);     /* base */
		sourcen = sim_spice_getnet(ni, source->network); /* emitter */
		drainn = sim_spice_getnet(ni, drain->network);   /* collector */

		/* make sure transistor is connected to nets */
		if (sourcen == NOSPNET || gaten == NOSPNET || drainn == NOSPNET)
		{
			(void)initinfstr();
			(void)formatinfstr(_("WARNING: %s not fully connected in facet %s"),
				describenodeinst(ni), describenodeproto(np));
			sim_spicedumpstringerror(f);
		}

		/* print source, gate, drain, and substrate */

		/* get any special model information */
		info = NULL;
		var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, sch_spicemodelkey);
		if (var != NOVARIABLE) info = (char *)var->addr;

		switch (state)
		{
			case SPICEREF:			/* self-referential transistor */
				sim_spice_xputs(sim_spice_element_name(ni, 'X', &subfacetindex, 0), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(drainn), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(gaten), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(sourcen), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(negnet), f, FALSE);
				sim_spice_xprintf(f, FALSE, " %s", ni->proto->primname);
				break;
			case SPICENMOS:			/* NMOS (Enhancement) transistor */
				sim_spice_xputs(sim_spice_element_name(ni, 'M', &nodeindex, 0), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(drainn), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(gaten), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(sourcen), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(negnet), f, FALSE);
				if (info != NULL) sim_spice_xprintf(f, FALSE, " %s", info); else
					sim_spice_xprintf(f, FALSE, " N");
				break;
			case SPICENMOS4:		/* NMOS (Complementary) 4-port transistor */
				sim_spice_xputs(sim_spice_element_name(ni, 'M', &nodeindex, 0), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(drainn), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(gaten), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(sourcen), f, FALSE);
				for(pp = ni->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
					biasport = pp;
				biasn = sim_spice_getnet(ni, biasport->network);
				sim_spice_xputs(sim_spice_node_name(biasn), f, FALSE);
				if (info != NULL) sim_spice_xprintf(f, FALSE, " %s", info); else
					sim_spice_xprintf(f, FALSE, " N");
				break;
			case SPICEDMOS:			/* DMOS (Depletion) transistor */
				sim_spice_xputs(sim_spice_element_name(ni, 'M', &nodeindex, 0), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(drainn), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(gaten), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(sourcen), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(negnet), f, FALSE);
				if (info != NULL) sim_spice_xprintf(f, FALSE, " %s", info); else
					sim_spice_xprintf(f, FALSE, " D");
				break;
			case SPICEDMOS4:		/* DMOS (Depletion) 4-port transistor */
				sim_spice_xputs(sim_spice_element_name(ni, 'M', &nodeindex, 0), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(drainn), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(gaten), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(sourcen), f, FALSE);
				for(pp = ni->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
					biasport = pp;
				biasn = sim_spice_getnet(ni, biasport->network);
				sim_spice_xputs(sim_spice_node_name(biasn), f, FALSE);
				if (info != NULL) sim_spice_xprintf(f, FALSE, " %s", info); else
					sim_spice_xprintf(f, FALSE, " D");
				break;
			case SPICEPMOS:			/* PMOS (Complementary) transistor */
				sim_spice_xputs(sim_spice_element_name(ni, 'M', &nodeindex, 0), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(drainn), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(gaten), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(sourcen), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(posnet), f, FALSE);
				if (info != NULL) sim_spice_xprintf(f, FALSE, " %s", info); else
					sim_spice_xprintf(f, FALSE, " P");
				break;
			case SPICEPMOS4:		/* PMOS (Complementary) 4-port transistor */
				sim_spice_xputs(sim_spice_element_name(ni, 'M', &nodeindex, 0), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(drainn), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(gaten), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(sourcen), f, FALSE);
				for(pp = ni->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
					biasport = pp;
				biasn = sim_spice_getnet(ni, biasport->network);
				sim_spice_xputs(sim_spice_node_name(biasn), f, FALSE);
				if (info != NULL) sim_spice_xprintf(f, FALSE, " %s", info); else
					sim_spice_xprintf(f, FALSE, " P");
				break;
			case SPICENPN:			/* NPN (Junction) transistor */
				sim_spice_xputs(sim_spice_element_name(ni, 'Q', &nodeindex, 0), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(drainn), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(gaten), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(sourcen), f, FALSE);
				if (subnet != NOSPNET) sim_spice_xputs(sim_spice_node_name(subnet), f, FALSE);
				if (info != NULL) sim_spice_xprintf(f, FALSE, " %s", info); else
					sim_spice_xprintf(f, FALSE, " NBJT");
				break;
			case SPICENPN4:			/* NPN (Junction) 4-port transistor */
				sim_spice_xputs(sim_spice_element_name(ni, 'Q', &nodeindex, 0), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(drainn), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(gaten), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(sourcen), f, FALSE);
				for(pp = ni->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
					biasport = pp;
				biasn = sim_spice_getnet(ni, biasport->network);
				sim_spice_xputs(sim_spice_node_name(biasn), f, FALSE);
				if (info != NULL) sim_spice_xprintf(f, FALSE, " %s", info); else
					sim_spice_xprintf(f, FALSE, " NBJT");
				break;
			case SPICEPNP:			/* PNP (Junction) transistor */
				sim_spice_xputs(sim_spice_element_name(ni, 'Q', &nodeindex, 0), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(drainn), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(gaten), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(sourcen), f, FALSE);
				if (subnet != NOSPNET) sim_spice_xputs(sim_spice_node_name(subnet), f, FALSE);
				if (info != NULL) sim_spice_xprintf(f, FALSE, " %s", info); else
					sim_spice_xprintf(f, FALSE, " PBJT");
				break;
			case SPICEPNP4:			/* PNP (Junction) 4-port transistor */
				sim_spice_xputs(sim_spice_element_name(ni, 'Q', &nodeindex, 0), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(drainn), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(gaten), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(sourcen), f, FALSE);
				for(pp = ni->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
					biasport = pp;
				biasn = sim_spice_getnet(ni, biasport->network);
				sim_spice_xputs(sim_spice_node_name(biasn), f, FALSE);
				if (info != NULL) sim_spice_xprintf(f, FALSE, " %s", info); else
					sim_spice_xprintf(f, FALSE, " PBJT");
				break;
			case SPICENJFET:		/* NJFET (N Channel) transistor */
				sim_spice_xputs(sim_spice_element_name(ni, 'J', &nodeindex, 0), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(drainn), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(gaten), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(sourcen), f, FALSE);
				if (info != NULL) sim_spice_xprintf(f, FALSE,  " %s", info); else
					sim_spice_xprintf(f, FALSE, " NJFET");
				break;
			case SPICENJFET4:		/* NJFET (N Channel) 4-port transistor */
				sim_spice_xputs(sim_spice_element_name(ni, 'J', &nodeindex, 0), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(drainn), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(gaten), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(sourcen), f, FALSE);
				for(pp = ni->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
					biasport = pp;
				biasn = sim_spice_getnet(ni, biasport->network);
				sim_spice_xputs(sim_spice_node_name(biasn), f, FALSE);
				if (info != NULL) sim_spice_xprintf(f, FALSE, " %s", info); else
					sim_spice_xprintf(f, FALSE, " NJFET");
				break;
			case SPICEPJFET:		/* PJFET (P Channel) transistor */
				sim_spice_xputs(sim_spice_element_name(ni, 'J', &nodeindex, 0), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(drainn), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(gaten), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(sourcen), f, FALSE);
				if (info != NULL) sim_spice_xprintf(f, FALSE, " %s", info); else
					sim_spice_xprintf(f, FALSE, " PJFET");
				break;
			case SPICEPJFET4:		/* PJFET (P Channel) 4-port transistor */
				sim_spice_xputs(sim_spice_element_name(ni, 'J', &nodeindex, 0), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(drainn), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(gaten), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(sourcen), f, FALSE);
				for(pp = ni->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
					biasport = pp;
				biasn = sim_spice_getnet(ni, biasport->network);
				sim_spice_xputs(sim_spice_node_name(biasn), f, FALSE);
				if (info != NULL) sim_spice_xprintf(f, FALSE, " %s", info); else
					sim_spice_xprintf(f, FALSE, " PJFET");
				break;
			case SPICEDMES:			/* DMES (Depletion) transistor */
			case SPICEDMES4:		/* DMES (Depletion) 4-port transistor */
				sim_spice_xputs(sim_spice_element_name(ni, 'Z', &nodeindex, 0), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(drainn), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(gaten), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(sourcen), f, FALSE);
				sim_spice_xprintf(f, FALSE, " DMES");
				break;
			case SPICEEMES:			/* EMES (Enhancement) transistor */
			case SPICEEMES4:		/* EMES (Enhancement) 4-port transistor */
				sim_spice_xputs(sim_spice_element_name(ni, 'Z', &nodeindex, 0), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(drainn), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(gaten), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(sourcen), f, FALSE);
				sim_spice_xprintf(f, FALSE, " EMES");
				break;
			case SPICETRANS:		/* special transistor */
				sim_spice_xputs(sim_spice_element_name(ni, 'Q', &nodeindex, 0), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(drainn), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(gaten), f, FALSE);
				sim_spice_xputs(sim_spice_node_name(sourcen), f, FALSE);
				/* sim_spice_xputs(sim_spice_node_name(subnet), f, FALSE); */
				if (subnet != NOSPNET) sim_spice_xputs(sim_spice_node_name(subnet), f, FALSE);
				if (info != NULL) sim_spice_xprintf(f, FALSE, " %s", info);
				break;
		}

		/* do not print area for self-referential transistors */
		if (state == SPICEREF) continue;

		/* compute length and width (or area for nonMOS transistors) */
		transistorsize(ni, &lx, &ly);
		if (lx >= 0 && ly >= 0)
		{
			nodelambda = lambdaofnode(ni);
			reallambda = ni->parent->cell->lib->lambda[sim_spice_tech->techindex];
			if (nodelambda != reallambda && nodelambda != 0)
			{
				lx = muldiv(lx, reallambda, nodelambda);
				ly = muldiv(ly, reallambda, nodelambda);
			}

			a = sim_spice_mask_scale * lx;
			b = sim_spice_mask_scale * ly;
			if (state == SPICENMOS  || state == SPICEDMOS  || state == SPICEPMOS ||
				state == SPICENMOS4 || state == SPICEDMOS4 || state == SPICEPMOS4)
			{
				sim_spice_xprintf(f, FALSE, " L=%3.2fU W=%3.2fU", scaletodispunit((INTBIG)a, DISPUNITMIC),
					scaletodispunit((INTBIG)b, DISPUNITMIC));
			} else if ((state == SPICENJFET || state == SPICEPJFET || state == SPICEDMES ||
				state == SPICEEMES) && ((sim_spice_state & SPICETYPE) == SPICEHSPICE))
			{
				sim_spice_xprintf(f, FALSE, " AREA=%3.2f W=%3.2fU L=%3.2fU",
					scaletodispunitsq((INTBIG)(a*b), DISPUNITMIC), scaletodispunit((INTBIG)b, DISPUNITMIC),
						scaletodispunit((INTBIG)a, DISPUNITMIC));
			} else
				sim_spice_xprintf(f, FALSE, " AREA=%3.2f", scaletodispunitsq((INTBIG)(a*b), DISPUNITMIC));
		}

		/* make sure transistor is connected to nets */
		if (sourcen == NOSPNET || gaten == NOSPNET || drainn == NOSPNET)
		{
			sim_spice_xprintf(f, FALSE, "\n");
			continue;
		}

		/* compute area of source and drain */
		if (!cdl)
		{
			if (state == SPICENMOS  || state == SPICEDMOS  || state == SPICEPMOS ||
				state == SPICENMOS4 || state == SPICEDMOS4 || state == SPICEPMOS4)
			{
				switch (state)
				{
					case SPICEDMOS:
					case SPICEDMOS4:
					case SPICENMOS:
					case SPICENMOS4: i = ISNTYPE; break;
					case SPICEPMOS:
					case SPICEPMOS4: i = ISPTYPE; break;
					default:         i = ISNONE;  break;
				}

				/* we should not look at the ISNONE entry of components[],
				 * but the diffareas will be zero anyhow,
				 */
				if (sourcen->components[i] != 0)
				{
					a = scaletodispunitsq((INTBIG)(sourcen->diffarea[i] / sourcen->components[i]),
						DISPUNITMIC);
					if (a > 0.0) sim_spice_xprintf(f, FALSE, " AS=%5.2fP", a);
				}
				if (drainn->components[i] != 0)
				{
					b = scaletodispunitsq((INTBIG)(drainn->diffarea[i] / drainn->components[i]),
						DISPUNITMIC);
					if (b > 0.0) sim_spice_xprintf(f, FALSE, " AD=%5.2fP", b);
				}

				/* compute perimeters of source and drain */
				if (sourcen->components[i] != 0)
				{
					a = scaletodispunit((INTBIG)(sourcen->diffperim[i] / sourcen->components[i]),
						DISPUNITMIC);
					if (a > 0.0) sim_spice_xprintf(f, FALSE, " PS=%5.2fU", a);
				}
				if (drainn->components[i] != 0)
				{
					b = scaletodispunit((INTBIG)(drainn->diffperim[i] / drainn->components[i]),
						DISPUNITMIC);
					if (b > 0.0) sim_spice_xprintf(f, FALSE, " PD=%5.2fU", b);
				}
			}
			sim_spice_xprintf(f, FALSE, "\n");
		}
	}

	/* print resistances and capacitances */
	if (!cdl)
	{
		if ((sim_spice_state&SPICERESISTANCE) != 0)
		{
			/* print parasitic capacitances */
			first = 1;
			for(spnet = sim_spice_firstnet; spnet != NOSPNET; spnet = spnet->nextnet)
			{
				spnet->resistance = scaletodispunitsq((INTBIG)spnet->resistance, DISPUNITMIC);
				if (spnet->resistance > sim_spice_min_resist)
				{
					if (first != 0)
					{
						first = 0;
						sim_spice_xprintf(f, TRUE, "** Extracted Parasitic Elements:\n");
					}
					sim_spice_xprintf(f, FALSE, "R%ld ? ? %9.2f\n", resistnum++, spnet->resistance);
				}

				if (spnet->network == sim_spice_gnd) continue;
				if (spnet->capacitance > sim_spice_min_capac)
				{
					if (first != 0)
					{
						first = 0;
						sim_spice_xprintf(f, TRUE, "** Extracted Parasitic Elements:\n");
					}
					sim_spice_xprintf(f, FALSE, "C%ld%s 0 %9.2fF\n", capacnum++,
						sim_spice_node_name(spnet), spnet->capacitance);
				}
			}
		}
	}

	/* write out any directly-typed SPICE cards */
	if (sim_spice_card_key == 0)
		sim_spice_card_key = makekey("SIM_spice_card");
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		if (ni->proto != gen_invispinprim) continue;
		var = getvalkey((INTBIG)ni, VNODEINST, -1, sim_spice_card_key);
		if (var == NOVARIABLE) continue;
		if ((var->type&VTYPE) != VSTRING) continue;
		if ((var->type&VDISPLAY) == 0) continue;
		if ((var->type&VISARRAY) == 0)
		{
			sim_spice_xprintf(f, FALSE, "%s\n", (char *)var->addr);
		} else
		{
			len = getlength(var);
			for(i=0; i<len; i++)
				sim_spice_xprintf(f, FALSE, "%s\n", ((char **)var->addr)[i]);
		}
	}

	/*
	 * Now we're finished writing the subcircuit.
	 * Only the top-level facet can contain meters and connections.
	 */
	if (top && !cdl)
	{
#if 0
		/* now look for meters in the facet */
		first = 0;
		for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
		{
			/* only want primitive meter nodes */
			if (ni->proto->primindex == 0) continue;
			state = sim_spice_nodetype(ni);
			if (state != SPICEMETER) continue;

			/* get the connections on the meter */
			source = ni->proto->firstportproto;
			sourcen = sim_spice_getnet(ni, source->network);
			drain = source->nextportproto;
			drainn = sim_spice_getnet(ni, drain->network);
			if (sourcen == NOSPNET)
			{
				(void)initinfstr();
				(void)formatinfstr(_("ERROR: Top of %s component in facet %s is not connected"),
					describenodeinst(ni), describenodeproto(np));
				sim_spicedumpstringerror(f);
				continue;
			}

			/* make sure meter is connected to exports */
			if (sim_spiceisexported(sourcen, np) == 0)
			{
				(void)initinfstr();
				(void)formatinfstr(_("WARNING: top of %s component in facet %s not connected to an export"),
					describenodeinst(ni), describenodeproto(np));
				sim_spicedumpstringerror(f);
			}
			if (drainn != NOSPNET)
			{
				if (sim_spiceisexported(drainn, np) == 0)
				{
					(void)initinfstr();
					(void)formatinfstr(_("WARNING: bottom of %s component in facet %s not connected to an export"),
						describenodeinst(ni), describenodeproto(np));
					sim_spicedumpstringerror(f);
				}
			}
			if (top == 0) ttyputerr(_("WARNING: Meter node found in subfacet %s"), describenodeproto(np));

			/* begin printing the meter card */
			if (first == 0)
			{
				first++;
				sim_spice_xputs((sim_spice_state&SPICEPLOT ? ".PLOT" : ".PRINT"), f, FALSE);

				if (sim_spice_tran) sim_spice_xputs(" TRAN", f, FALSE); else
					if (sim_spice_dc) sim_spice_xputs(" DC", f, FALSE); else
						if (sim_spice_ac) sim_spice_xputs(" AC", f, FALSE);

				/* write what we have already (from metered sources) */
				sim_spice_xputs(sim_spice_printcard, f, FALSE);
				efree(sim_spice_printcard);
			}

			/* see what kind of meter it is */
			var = getvalkey((INTBIG)ni, VNODEINST, -1, sch_meterkey);
			if (var != NOVARIABLE) extra = describesimplevariable(var); else extra = "";
			if (drainn == NOSPNET || drainn->network == sim_spice_gnd)
			{
				/* no space in V(NName), because HSPICE can't handle line break after ")" */
				sim_spice_xprintf(f, FALSE, " V(%s)", sim_spice_node_name(sourcen)+1);
				if (*extra != 0) sim_spice_xprintf(f, FALSE, " %s", extra);
				if (sim_spice_ac)
				{
					/* no space in V(NName), because HSPICE can't handle line break after ")" */
					sim_spice_xprintf(f, FALSE, " VP(%s)", sim_spice_node_name(sourcen)+1);
					if (*extra != 0) sim_spice_xprintf(f, FALSE, " %s", extra);
				}
			} else
			{
				/* no space in V(NName), because HSPICE can't handle line break after ")" */
				sim_spice_xprintf(f, FALSE, " V(%s,",  sim_spice_node_name(sourcen)+1);

				/* can't use sim_spice_node_name() twice in call, because static storage is used */
				sim_spice_xprintf(f, FALSE, "%s)", sim_spice_node_name(drainn)+1);
				if (*extra != 0) sim_spice_xprintf(f, FALSE, " %s", extra);
				if (sim_spice_ac)
				{
					/* no space in V(NName), because HSPICE can't handle line break after ")" */
					sim_spice_xprintf(f, FALSE, " VP(%s,", sim_spice_node_name(sourcen)+1);
					sim_spice_xprintf(f, FALSE, "%s)", sim_spice_node_name(drainn)+1);
					if (*extra != 0) sim_spice_xprintf(f, FALSE, " %s", extra);
				}
			}
		}
		if (first != 0) sim_spice_xprintf(f, FALSE, "\n"); else
#endif
		{
			/* if no voltmeters, print metered sources anyway */
			if (*sim_spice_printcard != '\0')
			{
				sim_spice_xputs((char *)(sim_spice_state&SPICEPLOT ? ".PLOT" : ".PRINT"),
					f, FALSE);

				if (sim_spice_tran) sim_spice_xputs(" TRAN", f, FALSE); else
					if (sim_spice_dc) sim_spice_xputs(" DC", f, FALSE); else
						if (sim_spice_ac) sim_spice_xputs(" AC", f, FALSE);

				/* write what we have already (from metered sources) */
				sim_spice_xputs(sim_spice_printcard, f, FALSE);
				sim_spice_xprintf(f, FALSE, "\n");
			}
			efree(sim_spice_printcard);
		}

		/* miscellaneous checks */
		for(spnet = sim_spice_firstnet; spnet != NOSPNET; spnet = spnet->nextnet)
		{
			for (i = 0; i < DIFFTYPES; i++)
			{
				if (spnet->diffarea[i] == 0.0 || spnet->components[i] != 0) continue;

				/* do not issue errors for active area on supply rails (probably well contacts) */
				if (spnet->network == sim_spice_vdd || spnet->network == sim_spice_gnd) continue;
				switch (i)
				{
					case ISNTYPE:
						uncon_diff_type = " N-type";
						break;
					case ISPTYPE:
						uncon_diff_type = " P-type";
						break;
					case ISNONE:
					default:
						uncon_diff_type = "";
						break;
				}
				(void)initinfstr();
				(void)formatinfstr(_("WARNING: SPICE node%s has unconnected%s device diffusion in facet %s"),
					sim_spice_node_name(spnet), uncon_diff_type, describenodeproto(np));
				sim_spicedumpstringerror(f);
			}
		}
	} else
	{
		if (paramname != 0)
		{
			/* parameterized subcircuit name */
			sim_spice_xprintf(f, FALSE, ".ENDS %s\n", sim_spice_legal_name(paramname));
		} else
		{
			sim_spice_xprintf(f, FALSE, ".ENDS %s\n", sim_spice_legal_name(np->cell->cellname));
		}
	}

	/* free the net modules */
	for(spnet = sim_spice_firstnet; spnet != NOSPNET; spnet = nspnet)
	{
		nspnet = spnet->nextnet;
		sim_freespnet(spnet);
	}
	if (backannotate) return(1);
	return(0);
}

/******************** DECK GENERATION SUPPORT ********************/

/*
 * write a header for "acet" to spice deck "spfile"
 * The model cards come from a file specified by tech:~.SIM_spice_model_file
 * or else tech:~.SIM_spice_header_level%ld
 * The spice model file can be located in el_libdir
 */
void sim_spice_writeheader(NODEPROTO *facet, FILE *spfile)
{
	REGISTER INTBIG i, j, level;
	UINTBIG cdate, rdate;
	REGISTER VARIABLE *var;
	char c, hvar[80], *filename;
	REGISTER char **name;
	FILE *modelfile;

	/* Print the header line for SPICE  */
	sim_spice_xprintf(spfile, TRUE, "*** SPICE deck for facet %s from library %s\n",
		describenodeproto(facet), facet->cell->lib->libname);
	if ((us_useroptions&NODATEORVERSION) == 0)
	{
		cdate = facet->creationdate;
		if (cdate != 0)
			sim_spice_xprintf(spfile, TRUE, "*** Created on %s\n", timetostring(cdate));
		rdate = facet->revisiondate;
		if (rdate != 0)
			sim_spice_xprintf(spfile, TRUE, "*** Last revised on %s\n", timetostring(rdate));
		(void)sprintf(hvar, "%s", timetostring(getcurrenttime()));
		sim_spice_xprintf(spfile, TRUE, "*** Written on %s by Electric VLSI Design System, %s\n",
			hvar, el_version);
	} else
	{
		sim_spice_xprintf(spfile, TRUE, "*** Written by Electric VLSI Design System\n");
	}

	sim_spice_xprintf(spfile, TRUE, "*** UC SPICE *** , MIN_RESIST %f, MIN_CAPAC %fFF\n",
		sim_spice_min_resist, sim_spice_min_capac);
	sim_spice_xputs(".OPTIONS NOMOD NOPAGE\n", spfile, FALSE);

	/* get spice model/option cards from file if specified */
	var = getval((INTBIG)sim_spice_tech, VTECHNOLOGY, VSTRING, "SIM_spice_model_file");
	if (var != NOVARIABLE)
	{
		if ((modelfile = xopen((char *)var->addr, sim_filetypespice, el_libdir, &filename)) != NULL)
		{
			/* copy file */
			c = (char)xgetc(modelfile);
			while (!xeof(modelfile))
			{
				(void)xputc(c, spfile);
				c = (char)xgetc(modelfile);
			}
			xclose(modelfile);
		} else ttyputerr(_("WARNING: cannot open model file: %s"), (char **)var->addr);
	} else
	{
		/* get the spice level and header from technology */
		var = getvalkey((INTBIG)sim_tool, VTOOL, VINTEGER, sim_spice_levelkey);
		if (var == NOVARIABLE) level = 1; else
			level = var->addr;
		(void)sprintf(hvar, "SIM_spice_header_level%ld", level);
		var = getval((INTBIG)sim_spice_tech, VTECHNOLOGY, VSTRING|VISARRAY, hvar);
		if (var != NOVARIABLE)
		{
			name = (char **)var->addr;
			j = getlength(var);
			for(i=0; i<j; i++) sim_spice_xprintf(spfile, FALSE, "%s\n", name[i]);
		} else ttyputerr(_("WARNING: no model cards for SPICE level %ld in %s technology"),
			level, sim_spice_tech->techname);
	}

	/* write some extra stuff for HSPICE */
	if ((sim_spice_state & SPICETYPE) == SPICEHSPICE)
		sim_spice_xputs(".OPTIONS INGOLD=2 POST=ASCII TNOM=27 PLIM\n", spfile, FALSE);
}

/*
 * Write a trailer from an external file, defined as a variable on
 * the current technology in this library: tech:~.SIM_spice_trailer_file
 * if it is available.
 */
void sim_spice_writetrailer(FILE *spfile)
{
	VARIABLE *var;
	FILE    *trailerfile;
	char    c, *truename;

	/* get spice trailer cards from file if specified */
	var = getval((INTBIG)sim_spice_tech, VTECHNOLOGY, VSTRING, "SIM_spice_trailer_file");
	if (var != NOVARIABLE)
	{
		trailerfile = xopen((char *)var->addr, sim_filetypespice, el_libdir, &truename);
		if (trailerfile != NULL)
		{
			/* copy file */
			c = (char)xgetc(trailerfile);
			while (!xeof(trailerfile))
			{
				(void)xputc(c, spfile);
				c = (char)xgetc(trailerfile);
			}
			xclose(trailerfile);
		} else
			ttyputerr(_("WARNING: cannot open trailer file: %s"), (char **)var->addr);
	}
}

/*
 * Function to write a two port device to the file. If the flag 'report'
 * is set, then complain about the missing connections.
 * Determine the port connections from the portprotos in the instance
 * prototype. Get the part number from the 'part' number value;
 * increment it. The type of device is declared in type; extra is the string
 * data acquired before calling here.
 * If the device is connected to the same net at both ends, do not
 * write it. Is this OK?
 */
void sim_spice_write_twoport(FILE *f, NODEINST *ni, INTBIG type, char *extra,
	INTBIG *part, INTBIG report)
{
	REGISTER PORTPROTO *pp1, *pp2;
	REGISTER SPNET *end1, *end2;

	pp1 = ni->proto->firstportproto;
	pp2 = pp1->nextportproto;
	end1 = sim_spice_getnet(ni, pp1->network);
	end2 = sim_spice_getnet(ni, pp2->network);

	/* make sure the component is connected to nets */
	if (end1 == NOSPNET || end2 == NOSPNET)
	{
		(void)initinfstr();
		(void)formatinfstr(_("WARNING: %s component not fully connected in facet %s"),
			describenodeinst(ni), describenodeproto(ni->parent));
		sim_spicedumpstringerror(f);
	}
	if (end1 != NOSPNET && end2 != NOSPNET)
		if (end1->netnumber == end2->netnumber)
	{
		if (report)
		{
			(void)initinfstr();
			(void)formatinfstr(_("WARNING: %s component appears to be shorted on net %s in facet %s"),
				describenodeinst(ni), sim_spice_node_name(end1), describenodeproto(ni->parent));
			sim_spicedumpstringerror(f);
		}
		return;
	}

	/* next line is not really necessary any more */
	switch (type)
	{
		case SPICERESISTOR:		/* resistor */
			sim_spice_xputs(sim_spice_element_name(ni, 'R', part, 0), f, FALSE);
			break;
		case SPICECAPACITOR:	/* capacitor */
			sim_spice_xputs(sim_spice_element_name(ni, 'C', part, 0), f, FALSE);
			break;
		case SPICEINDUCTOR:		/* inductor */
			sim_spice_xputs(sim_spice_element_name(ni, 'L', part, 0), f, FALSE);
			break;
		case SPICEDIODE:		/* diode */
		case SPICEDIODEZ:		/* Zener diode */
			sim_spice_xputs(sim_spice_element_name(ni, 'D', part, 0), f, FALSE);
			break;
	}
	sim_spice_xputs(sim_spice_node_name(end2), f, FALSE);
	sim_spice_xputs(sim_spice_node_name(end1), f, FALSE);  /* note order */
	if (type == SPICEDIODE || type == SPICEDIODEZ)
	{
		if (extra[0] != 0) sim_spice_xprintf(f, FALSE, " %s\n", extra); else
			sim_spice_xprintf(f, FALSE, " DIODE\n");
	} else
		sim_spice_xprintf(f, FALSE, " %s\n", extra);
}

/******************** PARASITIC CALCULATIONS ********************/

/*
 * routine to recursively determine the area of diffusion and capacitance
 * associated with port "pp" of nodeinst "ni".  If the node is mult_layer, then
 * determine the dominant capacitance layer, and add its area; all other
 * layers will be added as well to the extra_area total.
 * Continue out of the ports on a complex facet
 */
void sim_spice_nodearea(SPNET *spnet, NODEINST *ni, PORTPROTO *pp)
{
	REGISTER INTBIG tot, i, function;
	REGISTER INTBIG fun;
	BOOLEAN isabox;
	XARRAY trans;
	INTBIG lx, hx, ly, hy;
	INTBIG dominant;
	REGISTER POLYGON *poly, *lastpoly, *firstpoly;
	REGISTER PORTARCINST *pi;
	REGISTER TECHNOLOGY *tech;
	float worst, cap;

	/* make sure this node hasn't been completely examined */
	if (ni->temp1 == 2) return;
	if (ni->temp2 == (INTBIG)pp->network) return;	/* Is this recursive? */
	ni->temp2 = (INTBIG)pp->network;

	/* facets have no area or capacitance (for now) */
	if (ni->proto->primindex != 0)  /* No area for complex nodes */
	{
		/* assign new state of this node */
		function = sim_spice_nodetype(ni);
		if (function == SPICEFACET || function == SPICEGROUND || function == SPICEPOWER)
			ni->temp1 = 2; else
				ni->temp1 = 1;

		/* initialize to examine the polygons on this node */
		tech = ni->proto->tech;
		makerot(ni, trans);

		/*
		 * NOW!  A fudge to make sure that well capacitors mask out the capacity
		 * to substrate of their top plate polysilicon  or metal
		 */
		if (function == SPICECAPACITOR) dominant = -1; else dominant = -2;
		if (function == SPICENMOS || function == SPICENMOS4 ||
			function == SPICEPMOS || function == SPICEPMOS4 ||
			function == SPICEDMOS || function == SPICEDMOS4 ||
			function == SPICEEMES || function == SPICEEMES4 ||
			function == SPICEDMES || function == SPICEDMES4)
				function = SPICENMOS;   /* One will do */

		/* make linked list of polygons */
		lastpoly = firstpoly = NOPOLYGON;
		tot = nodeEpolys(ni, 0, NOWINDOWPART);
		for(i=0; i<tot; i++)
		{
			poly = sim_allocpolygon();
			if (poly == NOPOLYGON) break;
			shapeEnodepoly(ni, i, poly);

			/* make sure this layer connects electrically to the desired port */
			if (poly->portproto == NOPORTPROTO)
			{
				sim_freepolygon(poly);   continue;
			}
			if (poly->portproto->network != pp->network)
			{
				sim_freepolygon(poly);   continue;
			}

			/* don't bother with layers without capacity */
			if ((sim_spice_layerisdiff(tech, poly->layer) == ISNONE) &&
				(sim_spice_capacitance(tech, poly->layer) == 0.0))
			{
				sim_freepolygon(poly);   continue;
			}

			/* leave out the gate capacitance of transistors */
			if (function == SPICENMOS)
			{
				fun = layerfunction(tech, poly->layer);
				if ((fun & LFPSEUDO) == 0 && layerispoly(fun))
				{
					sim_freepolygon(poly);   continue;
				}
			}
			if (lastpoly != NOPOLYGON) lastpoly->nextpolygon = poly; else
				firstpoly = poly;
			lastpoly = poly;
		}

		/* do we need to test the layers? */
		if (dominant != -1)
		{
			if (tot != 0 && firstpoly != NOPOLYGON) dominant = firstpoly->layer;

			/* find the layer that will contribute the maximum capacitance */
			if (tot > 1 && tech == el_curtech)
			{
				worst = 0.0;
				for(poly = firstpoly; poly != NOPOLYGON; poly = poly->nextpolygon)
				{
					if (sim_spice_layerisdiff(tech, poly->layer) != ISNONE)
					{
						dominant = -1;      /* flag for diffusion on this port */
						break;
					} else
					{
						cap = (float)fabs(areapoly(poly));
						if (cap * sim_spice_capacitance(tech, poly->layer) > worst)
						{
							worst = cap;
							dominant = poly->layer;
						}
					}
				}
			}
		}

		for(poly = firstpoly; poly != NOPOLYGON; poly = poly->nextpolygon)
		{
			/* get the area of this polygon */
			xformpoly(poly, trans);
			isabox = isbox(poly, &lx, &hx, &ly, &hy);
			if (tech != el_curtech || !isabox) continue;
			sim_spice_store_box(tech, poly->layer, hx-lx, hy-ly, (lx+hx)/2, (ly+hy)/2);
			if (sim_spice_layerisdiff(tech, poly->layer) == ISNONE &&
				poly->layer != dominant)
					sim_spice_extra_area[poly->layer] += (float)fabs(areapoly(poly));
		}

		/* free the polygons */
		while (firstpoly != NOPOLYGON)
		{
			poly = firstpoly;
			firstpoly = firstpoly->nextpolygon;
			sim_freepolygon(poly);
		}
	}
	for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
		if (pi->proto->network == pp->network)
			sim_spice_arcarea(spnet, pi->conarcinst);
	ni->temp2 = 0;      /* reset for next net pass */
}

/*
 * routine to recursively determine the area of diffusion, capacitance, (NOT
 * resistance) on arc "ai". If the arc contains active device diffusion, then
 * it will contribute to the area of sources and drains, and the other layers
 * will be ignored. This is not quite the same as the rule used for
 * contact (node) structures. Note: the earlier version of this
 * function assumed that diffusion arcs would always have zero capacitance
 * values for the other layers; this produces an error if any of these layers
 * have non-zero values assigned for other reasons. So we will check for the
 * function of the arc, and if it contains active device, we will ignore any
 * other layers
 */
void sim_spice_arcarea(SPNET *spnet, ARCINST *ai)
{
	REGISTER INTBIG i, tot;
	REGISTER TECHNOLOGY *tech;
	static POLYGON *poly = NOPOLYGON;
	INTBIG lx, hx, ly, hy;
	BOOLEAN j;
	INTBIG isdiffarc;

	if (ai->temp1 != 0) return;
	ai->temp1++;

	if (poly == NOPOLYGON) poly = allocstaticpolygon(4, sim_tool->cluster);

	tot = arcpolys(ai, NOWINDOWPART);
	tech = ai->proto->tech;
	isdiffarc = sim_spice_arcisdiff(ai);    /* check arc function */
	for(i=0; i<tot; i++)
	{
		shapearcpoly(ai, i, poly);
		j = isbox(poly, &lx, &hx, &ly, &hy);
		if (tech != el_curtech || !j) continue;
		if ((layerfunction(tech, poly->layer)&LFPSEUDO) != 0) continue;
		if (sim_spice_layerisdiff(tech, poly->layer) != ISNONE ||
			(isdiffarc == 0 && sim_spice_capacitance(tech, poly->layer) > 0.0))
				sim_spice_store_box(tech, poly->layer, hx-lx, hy-ly, (lx+hx)/2, (ly+hy)/2);
	}

	/* propagate to all of the nodes on this arc */
	for(i=0; i<2; i++)
		sim_spice_nodearea(spnet, ai->end[i].nodeinst, ai->end[i].portarcinst->proto);
}

/*
 * Routine to store a box on layer "layer" into the box merging system
 */
void sim_spice_store_box(TECHNOLOGY *tech, INTBIG layer, INTBIG xs, INTBIG ys, INTBIG xc, INTBIG yc)
{
	INTBIG type;

	if ((type = sim_spice_layerisdiff(tech, layer)) != ISNONE)
	{
		if (sim_spice_diffusion_index[type] < 0)
			sim_spice_diffusion_index[type] = layer; else
				layer = sim_spice_diffusion_index[type];
	}
	mrgstorebox(layer, tech, xs, ys, xc, yc);
}

/*
 * routine to obtain a polygon from the box merging system
 */
void sim_spice_evalpolygon(INTBIG layer, TECHNOLOGY *tech, INTBIG *xbuf, INTBIG *ybuf, INTBIG count)
{
	REGISTER INTBIG perim;
	float area;
	REGISTER INTBIG i, j;

	/* compute perimeter */
	perim = 0;
	for(i=0; i<count; i++)
	{
		if (i == 0) j = count-1; else j = i-1;
		perim += computedistance(xbuf[j], ybuf[j], xbuf[i], ybuf[i]);
	}

	/* get area */
	area = areapoints(count, xbuf, ybuf);
	if (sim_spice_extra_area[layer] != 0.0)
	{
		area -= sim_spice_extra_area[layer];
		sim_spice_extra_area[layer] = 0.0; /* but only once */
	}

	i = sim_spice_layerisdiff(tech, layer);
	if (i != ISNONE)
	{
		sim_spice_cur_net->diffarea[i] += area * sim_spice_mask_scale * sim_spice_mask_scale;
		sim_spice_cur_net->diffperim[i] += perim * sim_spice_mask_scale;
	} else
	{
		sim_spice_cur_net->capacitance += scaletodispunitsq((INTBIG)(sim_spice_capacitance(
			tech, layer) * area), DISPUNITMIC) *
				sim_spice_mask_scale * sim_spice_mask_scale;
		sim_spice_cur_net->capacitance += scaletodispunit((INTBIG)(sim_spice_edge_capacitance(
			tech, layer) * perim), DISPUNITMIC) *
				sim_spice_mask_scale;
	}
}

/*
 * routine to find the capacitance for the arc on layer "layer"
 */
float sim_spice_capacitance(TECHNOLOGY *tech, INTBIG layer)
{
	if (layer < 0 || tech->temp2 == 0) return(0.0);
	return(castfloat(((INTBIG *)tech->temp2)[layer]));
}

/*
 * Routine to return the fringing capacitance of layer "layer" in tech "tech"
 */
float sim_spice_edge_capacitance(TECHNOLOGY *tech, INTBIG layer)
{
	REGISTER INTBIG addr;
	REGISTER VARIABLE *var;
	REGISTER TECHNOLOGY *t;

	if (layer < 0) return(0.0);
	if (sim_spice_capacvalue == 0)
	{
		sim_spice_capacvalue = emalloc(el_maxtech * SIZEOFINTBIG, sim_tool->cluster);
		if (sim_spice_capacvalue == 0) return(0.0);
		for(t = el_technologies; t != NOTECHNOLOGY; t = t->nexttechnology)
		{
			var = getval((INTBIG)t, VTECHNOLOGY, VFLOAT|VISARRAY, "SIM_spice_edge_capacitance");
			sim_spice_capacvalue[t->techindex] = (var == NOVARIABLE ? 0 : var->addr);
		}
	}
	addr = sim_spice_capacvalue[tech->techindex];
	if (addr == 0) return(0.0);
	return(castfloat(((INTBIG *)(addr))[layer]));
}

/******************** TEXT ROUTINES ********************/

/*
 * Routine to convert "name" to a legal SPICE name (converting illegal characters
 * to "_") and return that name.
 */
char *sim_spice_legal_name(char *name)
{
	REGISTER char *pt;
	REGISTER INTBIG i;

	for(pt = name; *pt != 0; pt++)
	{
		if (isalnum(*pt) != 0) continue;
		for(i=0; sim_spicelegalchars[i] != 0; i++)
			if (*pt == sim_spicelegalchars[i]) break;
		if (sim_spicelegalchars[i] == 0) break;
	}
	if (*pt == 0) return(name);

	/* convert the name */
	(void)initinfstr();
	for(pt = name; *pt != 0; pt++)
	{
		if (isalnum(*pt) != 0)
		{
			(void)addtoinfstr(*pt);
			continue;
		}
		for(i=0; sim_spicelegalchars[i] != 0; i++)
			if (*pt == sim_spicelegalchars[i]) break;
		if (sim_spicelegalchars[i] != 0)
		{
			(void)addtoinfstr(*pt);
			continue;
		}
		(void)addtoinfstr('_');
	}
	return(returninfstr());
}

/*
 * Function to return a spice "element" name.
 * The first character (eg. R,L,C,D,X,...) is specified by "first".
 * the rest of the name comes from the name on inst "ni".
 * If there is no node name, a unique number specified by "counter" is used
 * and counter is incremented.
 *
 * Warning: This routine is not re-entrant.  You must use the returned string
 * before calling the routine again.
 */
char *sim_spice_element_name(NODEINST *ni, char first, INTBIG *counter, char *overridename)
{
	VARIABLE *varname;
	static char s[200];

	if (overridename != 0)
	{
		(void)sprintf(s, "%c%s", first, sim_spice_legal_name(overridename));
	} else
	{
		varname = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name_key);
		if (varname == NOVARIABLE)
		{
			ttyputerr(_("WARNING: no name on node %s"), describenodeinst(ni));
			(void)sprintf(s, "%c%ld", first, (*counter)++);
		} else
		{
			(void)sprintf(s, "%c%s", first, sim_spice_legal_name((char *)varname->addr));
		}
	}

	return(s);
}

/*
 * Routine to return the net name of SPICE net "spnet".
 * Unknown nets are assigned as node '*'.
 *
 * Warning: This routine is not re-entrant.  You must use the returned string
 * before calling the routine again.
 */
char *sim_spice_node_name(SPNET *spnet)
{
	REGISTER NETWORK *net;

	if (spnet == NOSPNET) net = NONETWORK; else
		net = spnet->network;
	(void)initinfstr();
	(void)formatinfstr(" %s", sim_spice_net_name(net));
	return(returninfstr());
}

/*
 * Routine to return the net name of net "net".
 *
 * Warning: This routine is not re-entrant.  You must use the returned string
 * before calling the routine again.
 */
char *sim_spice_net_name(NETWORK *net)
{
	static char s[80];
	REGISTER SPNET *spnet;

	if (net == NONETWORK)
	{
		if ((sim_spice_state&SPICENODENAMES) != 0)
			sprintf(s, "UNNAMED%ld", sim_spice_unnamednum++); else
				sprintf(s, "%ld", sim_spice_netindex++);
		return(s);
	}

	if ((sim_spice_state&SPICEGLOBALPG) != 0)
	{
		if (net == sim_spice_vdd) return("vdd");
		if (net == sim_spice_gnd) return("gnd");
	}
	if ((sim_spice_state&SPICENODENAMES) != 0)
	{
		if (net->globalnet >= 0) return(sim_spicedescribenetwork(net));
		if (net->namecount > 0 && isdigit(net->netname[0]) == 0)
			return(sim_spice_legal_name(net->netname));
	}
	spnet = (SPNET *)net->temp1;
	(void)sprintf(s, "%ld", spnet->netnumber);
	return(s);
}

char *sim_spicedescribenetwork(NETWORK *net)
{
	static char gennetname[50];
	REGISTER NODEPROTO *np;

	if (net->globalnet >= 0)
	{
		if (net->globalnet == GLOBALNETGROUND) return("gnd");
		if (net->globalnet == GLOBALNETPOWER) return("vdd");
		np = net->parent;
		if (net->globalnet >= np->globalnetcount)
			return(_("UNKNOWN"));
		return(np->globalnetnames[net->globalnet]);
	}
	if (net->namecount == 0)
	{
		sprintf(gennetname, _("UNNAMED%ld"), (INTBIG)net);
		return(gennetname);
	}
	return(sim_spice_legal_name(net->netname));
}

/******************** SUPPORT ********************/

/*
 * Routine to return nonzero if the network associated with spice net "spnet" in
 * facet "np" is exported.
 */
INTBIG sim_spiceisexported(SPNET *spnet, NODEPROTO *np)
{
	REGISTER PORTPROTO *pp;
	REGISTER INTBIG i;

	for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
	{
		if (pp->network == spnet->network) return(1);
		if (pp->network->signals > 1)
		{
			for(i=0; i<pp->network->signals; i++)
				if (pp->network->networklist[i] == spnet->network) return(1);
		}
	}
	return(0);
}

/*
 * routine to return the type of node in "ni", according to the defines at
 * the top of this module
 */
INTBIG sim_spice_nodetype(NODEINST *ni)
{
	REGISTER INTBIG nodetype;

	if (ni->proto->primindex == 0) return(SPICEFACET);
	nodetype = nodefunction(ni);
	switch (nodetype)
	{
		case NPTRANMOS:     return(SPICENMOS);
		case NPTRADMOS:     return(SPICEDMOS);
		case NPTRAPMOS:     return(SPICEPMOS);
		case NPTRANPN:      return(SPICENPN);
		case NPTRAPNP:      return(SPICEPNP);
		case NPTRANJFET:    return(SPICENJFET);
		case NPTRAPJFET:    return(SPICEPJFET);
		case NPTRADMES:     return(SPICEDMES);
		case NPTRAEMES:     return(SPICEEMES);
		case NPTRANSREF:    return(SPICEREF);
		case NPTRANS:       return(SPICETRANS);
		case NPTRA4NMOS:    return(SPICENMOS4);
		case NPTRA4DMOS:    return(SPICEDMOS4);
		case NPTRA4PMOS:    return(SPICEPMOS4);
		case NPTRA4NPN:     return(SPICENPN4);
		case NPTRA4PNP:     return(SPICEPNP4);
		case NPTRA4NJFET:   return(SPICENJFET4);
		case NPTRA4PJFET:   return(SPICEPJFET4);
		case NPTRA4DMES:    return(SPICEDMES4);
		case NPTRA4EMES:    return(SPICEEMES4);
		case NPRESIST:      return(SPICERESISTOR);
		case NPCAPAC:
		case NPECAPAC:      return(SPICECAPACITOR);
		case NPINDUCT:      return(SPICEINDUCTOR);
		case NPDIODE:       return(SPICEDIODE);
		case NPDIODEZ:      return(SPICEDIODEZ);
		case NPSUBSTRATE:   return(SPICESUBSTRATE);
		case NPCONGROUND:   return(SPICEGROUND);
		case NPCONPOWER:    return(SPICEPOWER);
	}
	return(SPICEUNKNOWN);
}

/*
 * routine to return nonzero if layer "layer" is on diffusion
 * Return the type of the diffusion
 */
INTBIG sim_spice_layerisdiff(TECHNOLOGY *tech, INTBIG layer)
{
	REGISTER INTBIG i;

	i = layerfunction(tech, layer);
	if ((i&LFPSEUDO) != 0) return(ISNONE);
	if ((i&LFTYPE) != LFDIFF) return(ISNONE);
	if ((i&LFPTYPE) != 0) return(ISPTYPE);
	if ((i&LFNTYPE) != 0) return(ISNTYPE);
	return(ISNTYPE);		/* Default to N-type  */
}

/*
 * routine to return value if arc contains device active diffusion
 * Return the type of the diffusion, else ISNONE
 */
INTBIG sim_spice_arcisdiff(ARCINST *ai)
{
	REGISTER INTBIG i;

	i = (ai->proto->userbits&AFUNCTION)>>AFUNCTIONSH;
	switch (i)
	{
		case APDIFFP:
			return(ISPTYPE);
		case APDIFFN:
			return(ISNTYPE);
		case APDIFF:
			return(ISNTYPE);	/* Default device is n-type */
		default:
			return(ISNONE);	/* Default to Unknown  */
	}
}

/*
 * routine to search the net list for this facet and return the net number
 * associated with nodeinst "ni", network "net"
 */
SPNET *sim_spice_getnet(NODEINST *ni, NETWORK *net)
{
	REGISTER SPNET *spnet;
	REGISTER PORTARCINST *pi;
	REGISTER PORTEXPINST *pe;

	/* search for arcs electrically connected to this port */
	for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
		if (pi->proto->network == net)
	{
		for(spnet = sim_spice_firstnet; spnet != NOSPNET; spnet = spnet->nextnet)
			if (pi->conarcinst->network == spnet->network) return(spnet);
	}

	/* search for exports on the node, connected to this port */
	for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
		if (pe->proto->network == net)
	{
		for(spnet = sim_spice_firstnet; spnet != NOSPNET; spnet = spnet->nextnet)
			if (pe->exportproto->network == spnet->network) return(spnet);
	}
	return(NOSPNET);
}

/*
 * Function to return the same view as top-level facet.
 * "sim_simnt" must be set before calling this function.
 * Returns the current view if there is no top-level view.
 */
NODEPROTO *sim_spice_topview(NODEPROTO *np)
{
	REGISTER NODEPROTO *rnp;

	if (np == NONODEPROTO) return(NONODEPROTO);

	if (np->primindex != 0) return(NONODEPROTO);    /* must be complex */

	/* Now look for views */
	for(rnp = np->cell->firstincell; rnp != NONODEPROTO; rnp = rnp->nextincell)
		if (rnp->cellview == sim_simnt->cellview) return(rnp);

	/* keep what we have */
	return(np);
}

/******************** LOW-LEVEL OUTPUT ROUTINES ********************/

/*
 * Routine to report an error that is built in the infinite string.
 * The error is sent to the messages window and also to the SPICE deck "f".
 */
void sim_spicedumpstringerror(FILE *f)
{
	REGISTER char *error;

	error = returninfstr();
	sim_spice_xprintf(f, TRUE, "*** %s\n", error);
	ttyputmsg("%s", error);
}

/*
 * Formatted output to file "stream".  All spice output is in upper case.
 * The buffer can contain no more than 256 chars including the newline
 * and null characters.
 * Doesn't return anything.
 */
void sim_spice_xprintf(FILE *stream, BOOLEAN iscomment, char *format, ...)
{
	va_list ap;
	char s[256];

	var_start(ap, format);
	evsnprintf(s, 256, format, ap);
	va_end(ap);
	sim_spice_xputs(s, stream, iscomment);
}

/*
 * Routine to write string "s" onto stream "stream", breaking
 * it into lines of the proper width, and converting to upper case
 * if SPICE2.
 */
void sim_spice_xputs(char *s, FILE *stream, BOOLEAN iscomment)
{
	char *pt, *lastspace, contchar;
	static INTBIG i=0;

	/* put in line continuations, if over 78 chars long */
	if (iscomment) contchar = '*'; else
		contchar = '+';
	lastspace = NULL;
	for (pt = s; *pt; pt++)
	{
		if ((sim_spice_state & SPICETYPE) == SPICE2)
		{
			if (islower(*pt)) *pt = toupper(*pt);
		}
		if (*pt == '\n')
		{
			i = 0;
			lastspace = NULL;
		} else
		{
			if (*pt == ' ' || *pt == '/') lastspace = pt;
			++i;
			if (i >= 78)
			{
				if (lastspace != NULL)
				{
					*lastspace = '\0';
					xputs(s, stream);
					xprintf(stream, "\n%c  ", contchar);
					s = lastspace + 1;
					i = 9 + pt-s+1;
					lastspace = NULL;
				} else
				{
					xprintf(stream, "\n%c  ", contchar);
					i = 9 + 1;
				}
			}
		}
	}
	xputs(s, stream);
}

/******************** MEMORY ALLOCATION ********************/

POLYGON *sim_allocpolygon(void)
{
	REGISTER POLYGON *poly;

	if (sim_polygonfree != NOPOLYGON)
	{
		poly = sim_polygonfree;
		sim_polygonfree = poly->nextpolygon;
	} else
	{
		poly = allocpolygon(4, sim_tool->cluster);
		if (poly == NOPOLYGON) return(NOPOLYGON);
	}
	poly->nextpolygon = NOPOLYGON;
	return(poly);
}

void sim_freepolygon(POLYGON *poly)
{
	poly->nextpolygon = sim_polygonfree;
	sim_polygonfree = poly;
}

/*
 * routine to allocate and initialize a new net module from the pool
 * (if any) or memory
 */
SPNET *sim_allocspnet(void)
{
	REGISTER SPNET *spnet;
	REGISTER INTBIG j;

	if (sim_spice_netfree == NOSPNET)
	{
		spnet = (SPNET *)emalloc(sizeof (SPNET), sim_tool->cluster);
		if (spnet == 0) return(NOSPNET);
	} else
	{
		/* take module from free list */
		spnet = sim_spice_netfree;
		sim_spice_netfree = spnet->nextnet;
	}

	/* Initialize it to empty values */
	spnet->resistance = 0.0;
	spnet->capacitance = 0.0;
	spnet->network = NONETWORK;
	for (j = 0; j < DIFFTYPES; j++)
	{
		spnet->diffperim[j] = 0.0;
		spnet->diffarea[j] = 0.0;
		spnet->components[j] = 0;
	}
	return(spnet);
}

/*
 * routine to return net module "spnet" to the pool of free modules
 */
void sim_freespnet(SPNET *spnet)
{
	spnet->nextnet = sim_spice_netfree;
	sim_spice_netfree = spnet;
}

/*
 * Routine to scan networks in facet "facet".  All networks are sorted by name.
 * The number of networks is returned and the list of them is put in "netlist".
 * Each network's "temp2" field is 2 for output, 1 for all other export types
 * (this is reversed for CDL).  Also, the power and ground networks are put into
 * "vdd" and "gnd".
 */
INTBIG sim_spicegetexportednetworks(NODEPROTO *facet, NETWORK ***netlist, NETWORK **vdd, NETWORK **gnd,
	BOOLEAN cdl)
{
	REGISTER NETWORK *net;
	REGISTER NODEINST *ni;
	REGISTER PORTPROTO *pp;
	REGISTER PORTARCINST *pi;
	REGISTER INTBIG wirecount, i, fun, termtype, characteristics;

	/* initialize to describe all nets */
	for(net = facet->firstnetwork; net != NONETWORK; net = net->nextnetwork)
		net->temp2 = 0;

	/* mark exported networks */
	*vdd = *gnd = NONETWORK;
	for(pp = facet->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
	{
		net = pp->network;
		if (portispower(pp)) *vdd = net;
		if (portisground(pp)) *gnd = net;
		if (cdl)
		{
			if ((pp->userbits&STATEBITS) == OUTPORT) termtype = 1; else
				termtype = 2;
		} else
		{
			if ((pp->userbits&STATEBITS) == OUTPORT) termtype = 2; else
				termtype = 1;
		}
		if (net->signals > 1)
		{
			/* bus export: mark individual network entries */
			for(i=0; i<net->signals; i++)
				net->networklist[i]->temp2 = termtype;
		} else
		{
			/* single wire export: mark the network */
			net->temp2 = termtype;
		}
	}
	for(ni = facet->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		fun = nodefunction(ni);
		if (fun != NPCONPOWER && fun != NPCONGROUND) continue;
		for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
		{
			net = pi->conarcinst->network;
			if (fun == NPCONPOWER) *vdd = net; else
				*gnd = net;
		}
	}
	if (*vdd == NONETWORK || *gnd == NONETWORK)
	{
		/* search for globals with power/ground */
		for(ni = facet->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
		{
			if (ni->proto != sch_globalprim) continue;
			for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
			{
				net = pi->conarcinst->network;
				characteristics = ((ni->userbits&NTECHBITS) >> NTECHBITSSH) << STATEBITSSH;
				if (characteristics == PWRPORT && *vdd == NONETWORK)
					*vdd = net;
				if (characteristics == GNDPORT && *gnd == NONETWORK)
					*gnd = net;
			}
		}
	}
	if (*vdd != NONETWORK && *vdd == *gnd)
		ttyputerr(_("*** WARNING: Power and Ground are shorted together in facet %s"),
			describenodeproto(facet));

	wirecount = 0;
	for(net = facet->firstnetwork; net != NONETWORK; net = net->nextnetwork)
	{
		if (net->temp2 != 0) wirecount++;
	}

	if (wirecount > 0)
	{
		/* make sure there is room in the array of networks */
		if (wirecount > sim_spicewirelisttotal)
		{
			if (sim_spicewirelisttotal > 0) efree((char *)sim_spicewirelist);
			sim_spicewirelisttotal = 0;
			sim_spicewirelist = (NETWORK **)emalloc(wirecount * (sizeof (NETWORK *)),
				sim_tool->cluster);
			if (sim_spicewirelist == 0) return(0);
			sim_spicewirelisttotal = wirecount;
		}

		/* load the array */
		i = 0;
		for(net = facet->firstnetwork; net != NONETWORK; net = net->nextnetwork)
		{
			if (net->temp2 != 0) sim_spicewirelist[i++] = net;
		}

		/* sort the networks by name */
		esort(sim_spicewirelist, wirecount, sizeof (NETWORK *), sim_spicesortnetworksbyname);
		*netlist = sim_spicewirelist;
	}

	return(wirecount);
}

/*
 * Routine to recursively examine facets and gather global network names.
 */
void sim_spicegatherglobals(NODEPROTO *np)
{
	REGISTER INTBIG netcount, i, newtotal, globalnet;
	NETWORK **netlist, *net;
	REGISTER char *gname, **newlist;
	REGISTER NODEPROTO *onp, *cnp;
	REGISTER NODEINST *ni;
	REGISTER VARIABLE *var;

	if (np->temp1 != 0) return;
	np->temp1 = 1;
	netcount = sim_spicegetexportednetworks(np, &netlist, &sim_spice_vdd, &sim_spice_gnd, FALSE);
	for(net = np->firstnetwork; net != NONETWORK; net = net->nextnetwork)
		net->temp1 = 0;
	for(i=0; i<netcount; i++)
	{
		net = netlist[i];
		net->temp1 = 1;
	}
	if ((sim_spice_state&SPICEGLOBALPG) != 0)
	{
		if (sim_spice_vdd != NONETWORK) sim_spice_vdd->temp1 = 0;
		if (sim_spice_gnd != NONETWORK) sim_spice_gnd->temp1 = 0;
	}
	for(net = np->firstnetwork; net != NONETWORK; net = net->nextnetwork)
	{
		if (net->temp1 != 0) continue;
		globalnet = net->globalnet;
		if (globalnet < 0)
		{
			if ((sim_spice_state&SPICEGLOBALPG) != 0)
			{
				if (net == sim_spice_vdd) globalnet = GLOBALNETPOWER;
				if (net == sim_spice_vdd) globalnet = GLOBALNETGROUND;
			}
			if (globalnet < 0) continue;
		}

		/* global net found: see if it is already in the list */
		gname = sim_spicedescribenetwork(net);
		for(i=0; i<sim_spiceglobalnetcount; i++)
			if (namesame(gname, sim_spiceglobalnets[i]) == 0) break;
		if (i < sim_spiceglobalnetcount) continue;

		/* add the global net name */
		if (sim_spiceglobalnetcount >= sim_spiceglobalnettotal)
		{
			newtotal = sim_spiceglobalnettotal * 2;
			if (sim_spiceglobalnetcount >= newtotal)
				newtotal = sim_spiceglobalnetcount + 5;
			newlist = (char **)emalloc(newtotal * (sizeof (char *)), sim_tool->cluster);
			if (newlist == 0) return;
			for(i=0; i<sim_spiceglobalnettotal; i++)
				newlist[i] = sim_spiceglobalnets[i];
			for(i=sim_spiceglobalnettotal; i<newtotal; i++)
				newlist[i] = 0;
			if (sim_spiceglobalnettotal > 0)
				efree((char *)sim_spiceglobalnets);
			sim_spiceglobalnets = newlist;
			sim_spiceglobalnettotal = newtotal;
		}
		if (sim_spiceglobalnets[sim_spiceglobalnetcount] != 0)
			efree((char *)sim_spiceglobalnets[sim_spiceglobalnetcount]);
		(void)allocstring(&sim_spiceglobalnets[sim_spiceglobalnetcount], gname,
			sim_tool->cluster);
		sim_spiceglobalnetcount++;
	}

	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		onp = ni->proto;
		if (onp->primindex != 0) continue;

		/* ignore recursive references (showing icon in contents) */
		if (onp->cell == np->cell) continue;

		/* see if this facet has a template */
		var = getvalkey((INTBIG)onp, VNODEPROTO, VSTRING, sim_spice_template_key);
		if (var != NOVARIABLE) continue;

		if (onp->cellview == el_iconview)
		{
			cnp = contentsview(onp);
			if (cnp != NONODEPROTO)
			{
				onp = cnp;

				/* see if this facet has a template */
				var = getvalkey((INTBIG)onp, VNODEPROTO, VSTRING, sim_spice_template_key);
				if (var != NOVARIABLE) continue;
			}
		}

		sim_spicegatherglobals(onp);
	}
}

/*
 * Helper routine for "esort" that makes networks with names go in ascending name order.
 */
int sim_spicesortnetworksbyname(const void *e1, const void *e2)
{
	REGISTER NETWORK *net1, *net2;
	REGISTER char *pt1, *pt2;
	char empty[1];

	net1 = *((NETWORK **)e1);
	net2 = *((NETWORK **)e2);
	if (net1->temp2 != net2->temp2)
		return(net1->temp2 - net2->temp2);
	empty[0] = 0;
	if (net1->namecount == 0) pt1 = empty; else pt1 = net1->netname;
	if (net2->namecount == 0) pt2 = empty; else pt2 = net2->netname;
	return(namesamenumeric(pt1, pt2));
}

#endif  /* SIMTOOL - at top */
