/*
 * 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
 * RLW920721: Changed error and warning messages to use the following
 *     descriptor convention (all mutli-line messages have the extra lines
 *     indented):
 *                      Warning: - general warning message
 *                      WARNING: - special warning message (i.e. not fully
 *                                  connected transistor)
 *                      ERROR:   - all error messages
 *            Changed some of the messages to use "ttyputerr()" instead of
 *              "ttyputmsg()"
 *
 * SRP921116: Changed warning messages in spice file to comments so that
 *      simulator can test file
 *
 * 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 exported 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 exported 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 exported 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:
 *        tellaid simulation spice format [2 | 3 | hspice]
 *
 * 7) Run spice with the command:
 *        onaid 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:
 *        tellaid simulation not execute
 *    which will cause deck generation only.  Then run SPICE externally
 *    and convert the output listing to a plot with:
 *        tellaid 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
 *    "SCHEM_transistor_type" 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.)
 *    If you create the transistor manually, then the variable
 *    "SCHEM_transistor_type" will be of the form:  tttNNNNNNN
 *    where: ttt is one of "npn", "pnp", "pjfet", or "njfet"
 *           NNNNNNNN is your model name, ex: LARGE_NPN
 *    Note: you can also define model names for the types "dmes", "emes",
 *    "nmos" "pmos" and  "dmos", which are helpful in schematic designs.
 *
 * (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. This can be done by
 *    using the "variables" menu of the GUI, or by the 'var set ~.proto'
 *    keyboard command. 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 SIMAID

#include "global.h"
#include "sim.h"
#include "efunction.h"
#include "tecschem.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 SPICEDMOS       2	/* DMOD transistor */
#define SPICEPMOS       3	/* PMOS transistor */
#define SPICENPN        4	/* NPN transistor */
#define SPICEPNP        5	/* PNP transistor */
#define SPICENJFET      6	/* N-JFET transistor */
#define SPICEPJFET      7	/* P-JFET transistor */
#define SPICEDMES       8	/* Depletion MESFET transistor */
#define SPICEEMES       9	/* Enhancement MESFET transistor */
#define SPICEREF        10	/* Self-referential transistor */
#define SPICEMETER      11	/* meter */
#define SPICESOURCEV    12	/* voltage source */
#define SPICESOURCEC    13	/* current source */
#define SPICESOURCECM   14	/* current meter */
#define SPICESOURCET    15	/* transient analysis specification */
#define SPICESOURCEDC   16	/* DC analysis specification */
#define SPICESOURCEAC   17	/* AC analysis specification */
#define SPICESOURCEN    18	/* NodeSet source */
#define SPICESOURCEX    19	/* Extension source */
#define SPICESOURCEB    20	/* Bulk source */
#define SPICESOURCES    21	/* Special source */
#define SPICERESISTOR   22	/* resistor */
#define SPICECAPACITOR  23	/* capacitor */
#define SPICEINDUCTOR   24	/* inductor */
#define SPICEDIODE      25	/* diode */
#define SPICEDIODEZ     26	/* Zener diode */
#define SPICEARRAY      27	/* Multiport/function device */
#define SPICESUBSTRATE  28	/* connection to substrate */
#define SPICETRANS      29	/* generic (special) transistor */
#define SPICEGROUND     30	/* A node connected to ground */
#define SPICEPOWER      31	/* A node connected to power */

/* Types of spice sources */
#define SPICENORMAL     0	/* normal source */
#define SPICEAC	        1	/* .AC card */
#define SPICEDC	        2	/* .DC card */
#define SPICETRAN       3	/* .TRAN card */
#define SPICENODESET    4	/* .NODESET card */
#define SPICEBULK       5	/* bulk potential */
#define SPICEEXTENSION  6	/* extension CS901002 */

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

float       sim_spice_min_resist;					/* spice minimum resistance */
float       sim_spice_min_capac;					/* spice minimum capacitance */
char       *sim_spice_ac;							/* AC analysis message */
char       *sim_spice_dc;							/* DC analysis message */
char       *sim_spice_tran;							/* Transient analysis message */
POLYGON    *sim_polygonfree = NOPOLYGON;			/* list of free simulation polygons */
float       sim_spice_mask_scale = 1.0;				/* Mask shrink factor (default =1) */
float      *sim_spice_extra_area = 0;				/* Duplicated area on each layer */
INTSML      sim_spice_diffusion_index[DIFFTYPES];	/* diffusion layers indices */
INTSML      sim_spice_layer_count;
INTBIG      sim_spice_level;						/* key for "SIM_spice_level" */
INTBIG      sim_spice_state;						/* key for "SIM_spice_state" */
INTSML      spice_state;							/* value of "SIM_spice_state" */
INTSML      sim_spice_debug = 0;					/* SPICE debugging */
INTBIG      sim_listingfile;						/* key for "SIM_listingfile" */
char	    sim_deck_facetname[100] = "";			/* Name extracted from .SPO file */
VIEW       *spice_top_view;							/* view of top-level facet */
char       *sim_spice_printcard;					/* the .PRINT or .PLOT card */

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

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

#define NONET   ((NET *)-1)

typedef struct Inet
{
	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 */
	INTSML       components[DIFFTYPES];	/* number of components connected to net */
	struct Inet *nextnet;				/* next in linked list */
} NET;

NET *sim_spice_firstnet;				/* first in list of nets in this facet */
NET *sim_spice_netfree = NONET;			/* list of free nets */
NET *sim_spice_power;					/* net of power */
NET *sim_spice_ground;					/* net of ground */
NET *sim_spice_cur_net;					/* for polygon merging */

/* prototypes for local routines */
POLYGON *sim_allocpolygon(void);
void sim_freepolygon(POLYGON*);
NET *sim_allocnet(void);
void sim_freenet(NET*);
void sim_spice_writeheader(CELL*, FILE*, TECHNOLOGY*);
INTSML sim_spicewritefacet(NODEPROTO*, FILE*, INTSML);
INTSML sim_spice_nodetype(NODEINST*, char**);
NET *sim_spice_getnet(NODEINST*, PORTPROTO*);
void sim_spice_nodearea(NET*, NODEINST*, PORTPROTO*);
void sim_spice_arcarea(NET*, ARCINST*);
void sim_spice_writeports(FILE*, NODEPROTO*);
char *sim_spice_element_name(NODEINST*, char, INTSML*);
char *sim_spice_node_name(NET*);
char *sim_spice_pp_name(PORTPROTO*);
void sim_spice_xputs(char*, FILE*);
char *sim_spice_describesource(NODEINST*);
void sim_spice_store_box(TECHNOLOGY*, INTSML, INTBIG, INTBIG, INTBIG, INTBIG);
void sim_spice_evalpolygon(INTSML, TECHNOLOGY*, INTBIG*, INTBIG*, INTSML);
INTSML sim_spice_layerisdiff(TECHNOLOGY*, INTSML);
INTSML sim_spice_arcisdiff(ARCINST*);
float sim_spice_resistance(TECHNOLOGY*, INTSML);
float sim_spice_capacitance(TECHNOLOGY*, INTSML);
float sim_spice_edge_capacitance(TECHNOLOGY*, INTSML);
NODEPROTO *sim_spice_topview(NODEPROTO*);
void sim_spice_write_twoport(FILE*, NODEINST*, INTSML, char*, INTSML*, INTSML);
void sim_spice_writetrailer(CELL*, FILE*, TECHNOLOGY*);

/*
 * Routine to free all memory associated with this module.
 */
void sim_freespicememory(void)
{
	REGISTER POLYGON *poly;
	REGISTER NET *n;

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

	while (sim_spice_netfree != NONET)
	{
		n = sim_spice_netfree;
		sim_spice_netfree = sim_spice_netfree->nextnet;
		efree((char *)n);
	}
	if (sim_spice_capacvalue != 0) efree((char *)sim_spice_capacvalue);
}

/******************** SPICE POLYGON QUEUE ********************/

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

	if (sim_polygonfree != NOPOLYGON)
	{
		poly = sim_polygonfree;
		sim_polygonfree = poly->nextpolygon;
	} else
	{
		poly = allocpolygon(4, sim_aid->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
 */
NET *sim_allocnet(void)
{
	REGISTER NET *n;
	REGISTER INTBIG j;

	if (sim_spice_netfree == NONET)
	{
		n = (NET *)emalloc(sizeof (NET), sim_aid->cluster);
		if (n == 0) return(NONET);
	} else
	{
		/* take module from free list */
		n = sim_spice_netfree;
		sim_spice_netfree = n->nextnet;
	}

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

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

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

/*
 * procedure to write a spice deck to describe facet "np"
 */
void sim_writespice(NODEPROTO *np)
{
	FILE *spfile;
	REGISTER INTSML retval;
	char deckfile[256], *pt;
	REGISTER VARIABLE *var;
	REGISTER LIBRARY *lib;
	REGISTER NODEPROTO *onp;
	REGISTER TECHNOLOGY *tech, *t;
	extern AIDENTRY *net_aid;

	/* make sure network tool is on */
	if ((net_aid->aidstate&AIDON) == 0)
	{
		ttyputerr("Network tool must be running...turning it on");
		aidturnon(net_aid, 0);
		ttyputerr("...now reissue the simulation command");
		return;
	}

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

	/* record the view of this facet - try to use this view throughout */
	spice_top_view = np->cellview;

	/* use the current technology */
	tech = el_curtech;
	sim_simnt = np;
	sim_spice_layer_count = tech->layercount;

	/* get the SPICE state */
	var = getvalkey((INTBIG)sim_aid, VAID, VINTEGER, sim_spice_state);
	if (var != NOVARIABLE) spice_state = (INTSML)var->addr; else spice_state = 0;

	/* get the overall minimum resistance and capacitance */
	var = getval((INTBIG)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)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)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_aid->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);
	}

	(void)strcpy(deckfile, np->cell->cellname);
	(void)strcat(deckfile, ".spi");
	pt = deckfile;
	spfile = xcreate(deckfile, FILETYPESPICE, "SPICE File", &pt);
	if (pt != 0) (void)strcpy(deckfile, pt);
	if (spfile == NULL)
	{
		ttyputerr("Cannot create SPICE file: %s", deckfile);
		return;
	}

	/* write header to spice deck */
	(void)sim_spice_writeheader(np->cell, spfile, tech);

	/* 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;
	}

	/*
	 * 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_aid->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, 1);
	if (retval < 0) ttyputerr("No memory");
	if (retval > 0)
		ttyputmsg("Back-annotation information has been added (library must be saved)");

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

	sim_spice_writetrailer(np->cell, spfile, tech);
	sim_spice_xprintf(spfile, ".END\n");
	ttyputmsg("%s written", deckfile);
	xclose(spfile);
	efree((char *)sim_spice_extra_area);

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

	/* run spice (if requested) */
	var = getvalkey((INTBIG)sim_aid, VAID, VINTEGER, sim_dontrun);
	if (var != NOVARIABLE && var->addr != SIMRUNNO)
	{
		ttyputmsg("Running SPICE...");
		var = getvalkey((INTBIG)sim_aid, VAID, VSTRING, sim_listingfile);
		if (var == NOVARIABLE) sim_spice_execute(deckfile, ""); else
			sim_spice_execute(deckfile, (char *)var->addr);
	}
}

/*
 * write a header for "cell" 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%d
 * The spice model file can be located in el_libdir
 */
void sim_spice_writeheader(CELL *cell, FILE *spfile, TECHNOLOGY *tech)
{
	REGISTER INTSML i, j, level;
	UINTBIG cdate, rdate;
	INTBIG rnum;
	REGISTER VARIABLE *var;
	char c, hvar[80], *filename;
	REGISTER char **name;
	FILE *modelfile;

	/* Print the header line for SPICE  */
	sim_spice_xprintf(spfile, "*** FACET %s FROM LIBRARY %s ***\n", cell->cellname,
		cell->lib->libname);
	cdate = sim_simnt->creationdate;
	if (cdate != 0) sim_spice_xprintf(spfile, "*** FACET CREATED %s\n", timetostring(cdate));
	rnum = sim_simnt->version;
	if (rnum != 0) sim_spice_xprintf(spfile, "*** VERSION %ld", rnum); else
		sim_spice_xputs("***", spfile);
	rdate = sim_simnt->revisiondate;
	if (rdate != 0) sim_spice_xprintf(spfile, " LAST REVISED %s\n", timetostring(rdate));
	sim_spice_xprintf(spfile, "*** EXTRACTED BY ELECTRIC DESIGN SYSTEM, VERSION %s\n",
		el_version);
	sim_spice_xprintf(spfile,"*** 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);

	/* get spice model/option cards from file if specified */
	var = getval((INTBIG)tech, VTECHNOLOGY, VSTRING, "SIM_spice_model_file");
	if (var != NOVARIABLE)
	{
		if ((modelfile = xopen((char *)var->addr, 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_aid, VAID, VINTEGER, sim_spice_level);
		if (var == NOVARIABLE) level = 1; else
			level = (INTSML)var->addr;
		(void)sprintf(hvar, "SIM_spice_header_level%d", level);
		var = getval((INTBIG)tech, VTECHNOLOGY, VSTRING|VISARRAY, hvar);
		if (var != NOVARIABLE)
		{
			name = (char **)var->addr;
			j = (INTSML)getlength(var);
			for(i=0; i<j; i++) sim_spice_xprintf(spfile, "%s\n", name[i]);
		} else ttyputerr("Warning: no model cards for SPICE level %d in %s technology",
			level, tech->techname);
	}

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

/*
 * routine to write facet "np" and its all referenced subnodeprotos in SPICE
 * format to file "f".  If "top" is nonzero, this is the top facet.  The
 * spice level is "aid: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.
 */
INTSML sim_spicewritefacet(NODEPROTO *np, FILE *f, INTSML top)
{
	REGISTER NODEINST *ni;
	REGISTER NODEPROTO *subnt, *tnp, *cnp;
	REGISTER PORTPROTO *pp, *posport, *negport, *tpp, *otpp;
	PORTPROTO *gate, *source, *drain, *gatedummy;
	REGISTER PORTARCINST *pi;
	REGISTER ARCINST *ai;
	REGISTER NET *net, *gaten, *sourcen, *drainn, *nnet, *posnet, *negnet, *subnet, *tempnet;
	float a, b;
	REGISTER INTSML netindex, state, j, first, found, meterit, i, nodetype, backannotate, retval;
	INTSML subfacetindex, nodeindex, resistnum, capacnum, inductnum, diodenum;
	REGISTER VARIABLE *var;
	char *extra, *info;
	REGISTER char sourcetype;
	INTBIG lx, ly;
	INTSML sim_spice_sourceindex, bipolars, nmostrans, pmostrans;
	char *dummy, c, *filename;
	FILE *modelfile;
	char *uncon_diff_type;
	extern AIDENTRY *net_aid;

	/* stop if requested */
	if (stopping("Deck generation") != 0) return(-1);

	/* Look for a model file on the current facet (view) */
	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);
	}

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

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

		/* make sure the subfacet hasn't already been written */
		if (subnt->temp1 != 0) continue;
		if (subnt->cell->temp1 != 0) continue;
		retval = sim_spicewritefacet(subnt, f, 0);
		if (retval < 0) return(-1);
		if (retval > 0) backannotate = 1;
	}

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

	/* 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, FILETYPESPICE,  el_libdir, &filename);
		if (modelfile != NULL)
		{
			/* copy file */
			c = (char)xgetc(modelfile);
			while (!xeof(modelfile))
			{
				(void)xputc(c, f);
				c = (char)xgetc(modelfile);
			}
			return(backannotate);
		} else ttyputerr("Warning: cannot open model file: %s", (char *)var->addr);
	}

	/* zero the temp1 for arcs and nodes to compute area */
	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst) ai->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 = NONET;
	sim_spice_power = sim_spice_ground = NONET;

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

	/* look at all arcs in the facet */
	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
	{
		/* don't count non-electrical arcs */
		if (((ai->proto->userbits&AFUNCTION)>>AFUNCTIONSH) == APNONELEC) continue;

		/* see if this arc has the same net as another arc */
		for(net = sim_spice_firstnet; net != NONET; net = net->nextnet)
			if (net->network == ai->network) break;
		if (net != NONET) continue;

		/* add this net to the list */
		net = sim_allocnet();
		if (net == NONET) return(-1);
		net->network = ai->network;
		net->netnumber = 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;
		net->nextnet = sim_spice_firstnet;
		sim_spice_firstnet = net;

		if (sim_spice_debug != 0) ttyputmsg("Starting net %ld",net->netnumber);
		sim_spice_arcarea(net, ai); /* builds areas, etc */

		/* get merged polygons so far */
		sim_spice_cur_net = net;
		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 arc already mentioned */
		for(net = sim_spice_firstnet; net != NONET; net = net->nextnet)
			if (net->network == pp->network) break;
		if (net != NONET)
		{
			if (portispower(pp) != 0) sim_spice_power = net;
			if (portisground(pp) != 0)
			{
				sim_spice_ground = net;
				net->netnumber = 0;
			}
			pp->temp1 = net->netnumber;
			continue;
		}

		/* add this net to the list */
		net = sim_allocnet();
		if (net == NONET) return(-1);
		net->network = pp->network;
		if (portisground(pp) != 0)
		{
			net->netnumber = 0;
			sim_spice_ground = net;
		} else net->netnumber = netindex++;
		if (portispower(pp) != 0) sim_spice_power = net;
		pp->temp1 = net->netnumber;
		ni = pp->subnodeinst;

		/* 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;
		sim_spice_nodearea(net, ni, pp->subportproto);

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

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

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

	posnet = negnet = subnet = NONET;

	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		state = sim_spice_nodetype(ni, &extra);
		if (state == SPICESOURCEB)
		{
			posport = ni->proto->firstportproto;
			negport = posport->nextportproto;
			posnet = sim_spice_getnet(ni, posport);
			negnet = sim_spice_getnet(ni, negport);
			if (posnet == NONET || negnet == NONET)
				ttyputerr("Warning: %s component not properly connected",
					describenodeinst(ni));
			continue;
		}
		if (state == SPICEUNKNOWN || state == SPICEMETER || state == SPICESOURCEV ||
			state == SPICESOURCEC || state == SPICESOURCECM || state == SPICESOURCET ||
			state == SPICESOURCEDC || state == SPICESOURCEAC || state == SPICESOURCEN ||
			state == SPICESOURCEX || state == SPICESOURCES) continue;
		if (state == SPICESUBSTRATE)
		{
			if (subnet == NONET)
				subnet = sim_spice_getnet(ni, ni->proto->firstportproto);
			continue;
		}
		if (state == SPICEPOWER)
		{
			tempnet = sim_spice_getnet(ni, ni->proto->firstportproto);
			if (tempnet == NONET) continue;
			if (sim_spice_power == NONET) sim_spice_power = tempnet; else
			{
				if (tempnet->network != sim_spice_power->network)
					ttyputerr("Warning: there may be inconsistent power networks in %s",
						describenodeproto(np));
			}
		}
		if (state == SPICEGROUND)
		{
			tempnet = sim_spice_getnet(ni, ni->proto->firstportproto);
			if (tempnet == NONET) continue;
			tempnet->netnumber = 0;
			if (sim_spice_ground == NONET) sim_spice_ground = tempnet; else
			{
				if (tempnet->network != sim_spice_ground->network)
					ttyputerr("Warning: there may be inconsistent ground networks in %s",
						describenodeproto(np));
			}
		}
		switch (state)
		{
			case SPICENPN:
			case SPICEPNP:
			case SPICETRANS:
				nodetype = ISNONE;
				bipolars++;
				break;
			case SPICEEMES:
			case SPICEDMES:
			case SPICEDMOS:
			case SPICENMOS:
				nodetype = ISNTYPE;
				nmostrans++;
				break;
			case SPICEPMOS:
				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(net = sim_spice_firstnet; net != NONET; net = net->nextnet)
			{
				if (net->network == ai->network)
				{
					if (nodetype == ISNONE) net->components[nodetype]++; else
					{
						transistorports(ni, &gate, &gatedummy, &source, &drain);
						if (pi->proto == source || pi->proto == drain)
							net->components[nodetype]++;
					}
				}
			}
		}
	}

	/* use ground net for substrate */
	if (subnet == NONET) subnet = sim_spice_ground;

	if (sim_spice_power == NONET && top)
	{
		ttyputerr("Warning: cannot find power in the circuit");
		ttyputmsg("     Must export power port or use a 'DC' source node");
	}
	if (sim_spice_ground == NONET && top)
	{
		ttyputerr("Warning: cannot find ground in the circuit");
		ttyputmsg("     Must export ground port or use a 'DC' source node");
	}

	if (pmostrans != 0 && posnet == NONET)
	{
		if (sim_spice_power == NONET)
		{
			ttyputerr("Warning: no power connection for P-transistor wells in %s",
				describenodeproto(np));
		} else posnet = sim_spice_power;
	}
	if (nmostrans != 0 && negnet == NONET)
	{
		if (sim_spice_ground == NONET)
		{
			ttyputerr("Warning: no connection for N-transistor wells in %s",
				describenodeproto(np));
		} else negnet = sim_spice_ground;
	}

	if (bipolars != 0 && subnet == NONET)
	{
		ttyputerr("Warning: no explicit connection to the substrate in cell %s",
			describenodeproto(np));
		ttyputmsg("     A connection to ground will be assumed if necessary.");
		subnet = sim_spice_ground;
	}

	/* generate header for subckt or top-level facet */
	if (!top)
	{
		sim_spice_xprintf(f, "\n.SUBCKT %s", np->cell->cellname);
		sim_spice_writeports(f, np);
		sim_spice_xprintf(f, "\n");
	} else
		sim_spice_xprintf(f, "\n*** TOP LEVEL FACET: %s\n", describenodeproto(np));

	/* generate pin descriptions for reference (when not using node names) */
	/* Report on the ground network */
	if (sim_spice_power != NONET && sim_spice_power->network != NONETWORK)
		sim_spice_xprintf(f, "** POWER NET: %s\n",
			describenetwork(sim_spice_power->network));
	if (sim_spice_ground != NONET && sim_spice_ground->network != NONETWORK)
		sim_spice_xprintf(f, "** GROUND NET: 0 (%s)\n",
			describenetwork(sim_spice_ground->network));
	if ((spice_state&SPICENODENAMES) == 0)
	{
		for (net = sim_spice_firstnet; net != NONET; net = net->nextnet)
		{
			if (net->netnumber == 0) continue;
			for (pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
				if (pp->network == net->network) break;
			if (pp != NOPORTPROTO && net != NONET)
			{
				sim_spice_xprintf(f, "** PIN %ld: %s", net->netnumber, pp->protoname);
				if (net->network->namecount > 0)
					sim_spice_xprintf(f, " (network: %s)", describenetwork(net->network));
				sim_spice_xprintf(f, "\n");
			}
		}
	} else
	{
		/* Do something useful for named nodes */
		for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
		{
			/* do not write the ground net */
			if (pp->temp1 == 0) continue;
			for (tpp = np->firstportproto; tpp!=pp; tpp=tpp->nextportproto)
				if (pp->temp1 == tpp->temp1) break;	/* Already done */
			if (tpp != pp) continue;
			sim_spice_xprintf(f, "** PORT %s", sim_spice_pp_name(pp));
			if (pp->network->namecount > 0)
				sim_spice_xprintf(f, " (network: %s)", describenetwork(pp->network));
			sim_spice_xprintf(f, "\n");
		}
	}

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

	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		/* get the type of this node */
		state = sim_spice_nodetype(ni, &extra);

		/* handle sub-facet calls */
		if (state == SPICEFACET)
		{
			sim_spice_xputs(sim_spice_element_name(ni, 'X', &subfacetindex), f);

			/*
			 * We must be consistent in the ordering of ports.  However,
			 * subfacets may be another view (eg. an icon).  Therefore, we
			 * always try to use the same view - in this case, the view
			 * of the top-level facet.
			 */
			tnp = sim_spice_topview(ni->proto);
			i = 0;
			for(tpp = tnp->firstportproto; tpp != NOPORTPROTO; tpp = tpp->nextportproto)
			{
				/* ensure that common net numbers get written only once */
				for(otpp = tnp->firstportproto; otpp != tpp; otpp = otpp->nextportproto)
					if (otpp->temp1 == tpp->temp1) break; /* already done */
				if (otpp != tpp) continue;

				pp = equivalentport(tnp, tpp, ni->proto);
				if (pp == NOPORTPROTO) continue;
				net = sim_spice_getnet(ni, pp);
				if (net == NONET)
				{
					/* we have a problem here */
					i++;
				}

				/*
				 * write the ground net.
				 * It's valid to pass ground as a parameter to a
				 * sub-circuit, and the sub-circuit header has the
				 * matching parameter, as long as the net was not
				 * specified as Ground in the sub-circuit.
				 *
				 * if (net->netnumber != 0)
				 * WE WILL CHECK THE NET NUMBER IN THE PROTOTYPE
				 */
				if (tpp->temp1 != 0)
					sim_spice_xputs(sim_spice_node_name(net), f);
			}

			sim_spice_xprintf(f, " %s\n", ni->proto->cell->cellname);
			if (i != 0)
			{
				sim_spice_xprintf(f, "**ERROR: subfacet %s is not fully connected\n",
					describenodeinst(ni));
				ttyputmsg("ERROR: subfacet %s is not fully connected in facet %s",
					describenodeinst(ni), describenodeproto(np));
			}
			continue;
		}

		/* handle resistors, inductors, capacitors, and diodes */
		if (state == SPICERESISTOR || state == SPICECAPACITOR || state == SPICEINDUCTOR ||
			state == SPICEDIODE || state == SPICEDIODEZ)
		{
			switch (state)
			{
				case SPICERESISTOR:		/* resistor */
					sim_spice_write_twoport(f, ni, state, extra, &resistnum, 1);
					break;
				case SPICECAPACITOR:	/* capacitor */
					sim_spice_write_twoport(f, ni, state, extra, &capacnum, 1);
					break;
				case SPICEINDUCTOR:		/* inductor */
					sim_spice_write_twoport(f, ni, state, extra, &inductnum, 1);
					break;
				case SPICEDIODE:		/* diode */
				case SPICEDIODEZ:		/* Zener diode */
					sim_spice_write_twoport(f, ni, state, extra, &diodenum, 1);
					break;
			}
			continue;
		}
		if (state == SPICEARRAY)
		{
			sim_spice_xprintf(f, "*** ARRAY %s encountered\n", describenodeinst(ni));
			continue;
		}

		/* the default is to handle everything else as a transistor : */
		if (state == SPICEUNKNOWN || state == SPICEMETER || state == SPICESOURCEV ||
			state == SPICESOURCEC || state == SPICESOURCECM || state == SPICESOURCET ||
			state == SPICESOURCEDC || state == SPICESOURCEAC || state == SPICESOURCEN ||
			state == SPICESOURCEX || state == SPICESOURCEB || state == SPICESOURCES ||
			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);     /* base */
		sourcen = sim_spice_getnet(ni, source); /* emitter */
		drainn = sim_spice_getnet(ni, drain);   /* collector */

		/* make sure transistor is connected to nets */
		if (sourcen == NONET || gaten == NONET || drainn == NONET)
		{
			sim_spice_xprintf(f, "*** WARNING: transistor %s not fully connected:",
				describenodeinst(ni));
			sim_spice_xprintf(f, "\n*** Output line would be: ");
			sim_spice_xputs(sim_spice_element_name(ni, 'Q', &nodeindex), f);
			sim_spice_xputs(sim_spice_node_name(drainn), f);
			sim_spice_xputs(sim_spice_node_name(gaten), f);
			sim_spice_xputs(sim_spice_node_name(sourcen), f);
			sim_spice_xputs("\n", f);
			continue;
		}

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

		/* get any special model information */
		info = NULL;
		if (extra != 0)
		{
			if ((info = strchr(extra, '/')) != NULL)
			{
				info++;	 /* get past size information */
				if ((info = strchr(info, '/')) != NULL) /* more follows */
					info++;
			} else info = extra;
		}

		switch (state)
		{
			case SPICEREF:	    /* self-referential transistor */
				sim_spice_xputs(sim_spice_element_name(ni, 'X', &subfacetindex), f);
				sim_spice_xputs(sim_spice_node_name(drainn), f);
				sim_spice_xputs(sim_spice_node_name(gaten), f);
				sim_spice_xputs(sim_spice_node_name(sourcen), f);
				sim_spice_xputs(sim_spice_node_name(negnet), f);
				sim_spice_xprintf(f, " %s", ni->proto->primname);
				break;
			case SPICENMOS:	  /* NMOS (Enhancement) transistor */
				sim_spice_xputs(sim_spice_element_name(ni, 'M', &nodeindex), f);
				sim_spice_xputs(sim_spice_node_name(drainn), f);
				sim_spice_xputs(sim_spice_node_name(gaten), f);
				sim_spice_xputs(sim_spice_node_name(sourcen), f);
				sim_spice_xputs(sim_spice_node_name(negnet), f);
				if (info != NULL) sim_spice_xprintf(f, " %s",info); else
					sim_spice_xprintf(f, " N");
				break;
			case SPICEDMOS:	  /* DMOS (Depletion) transistor */
				sim_spice_xputs(sim_spice_element_name(ni, 'M', &nodeindex), f);
				sim_spice_xputs(sim_spice_node_name(drainn), f);
				sim_spice_xputs(sim_spice_node_name(gaten), f);
				sim_spice_xputs(sim_spice_node_name(sourcen), f);
				sim_spice_xputs(sim_spice_node_name(negnet), f);
				if (info != NULL) sim_spice_xprintf(f, " %s", info); else
					sim_spice_xprintf(f, " D");
				break;
			case SPICEPMOS:	  /* PMOS (Complementary) transistor */
				sim_spice_xputs(sim_spice_element_name(ni, 'M', &nodeindex), f);
				sim_spice_xputs(sim_spice_node_name(drainn), f);
				sim_spice_xputs(sim_spice_node_name(gaten), f);
				sim_spice_xputs(sim_spice_node_name(sourcen), f);
				sim_spice_xputs(sim_spice_node_name(posnet), f);
				if (info != NULL) sim_spice_xprintf(f, " %s", info); else
					sim_spice_xprintf(f, " P");
				break;
			case SPICENPN:	    /* NPN (Junction) transistor */
				sim_spice_xputs(sim_spice_element_name(ni, 'Q', &nodeindex), f);
				sim_spice_xputs(sim_spice_node_name(drainn), f);
				sim_spice_xputs(sim_spice_node_name(gaten), f);
				sim_spice_xputs(sim_spice_node_name(sourcen), f);
				if (subnet != NONET) sim_spice_xputs(sim_spice_node_name(subnet), f);
				if (info != NULL) sim_spice_xprintf(f, " %s", info); else
					sim_spice_xprintf(f, " NBJT");
				break;
			case SPICEPNP:	    /* PNP (Junction) transistor */
				sim_spice_xputs(sim_spice_element_name(ni, 'Q', &nodeindex), f);
				sim_spice_xputs(sim_spice_node_name(drainn), f);
				sim_spice_xputs(sim_spice_node_name(gaten), f);
				sim_spice_xputs(sim_spice_node_name(sourcen), f);
				if (subnet != NONET) sim_spice_xputs(sim_spice_node_name(subnet), f);
				if (info != NULL) sim_spice_xprintf(f, " %s", info); else
					sim_spice_xprintf(f, " PBJT");
				break;
			case SPICENJFET:	/* NJFET (N Channel) transistor */
				sim_spice_xputs(sim_spice_element_name(ni, 'J', &nodeindex), f);
				sim_spice_xputs(sim_spice_node_name(drainn), f);
				sim_spice_xputs(sim_spice_node_name(gaten), f);
				sim_spice_xputs(sim_spice_node_name(sourcen), f);
				if (info != NULL) sim_spice_xprintf(f,  " %s", info); else
					sim_spice_xprintf(f, " NJFET");
				break;
			case SPICEPJFET:	/* PJFET (P Channel) transistor */
				sim_spice_xputs(sim_spice_element_name(ni, 'J', &nodeindex), f);
				sim_spice_xputs(sim_spice_node_name(drainn), f);
				sim_spice_xputs(sim_spice_node_name(gaten), f);
				sim_spice_xputs(sim_spice_node_name(sourcen), f);
				if (info != NULL) sim_spice_xprintf(f, " %s", info); else
					sim_spice_xprintf(f, " PJFET");
				break;
			case SPICEDMES:	  /* DMES (Depletion) transistor */
				sim_spice_xputs(sim_spice_element_name(ni, 'Z', &nodeindex), f);
				sim_spice_xputs(sim_spice_node_name(drainn), f);
				sim_spice_xputs(sim_spice_node_name(gaten), f);
				sim_spice_xputs(sim_spice_node_name(sourcen), f);
				sim_spice_xprintf(f, " DMES");
				break;
			case SPICEEMES:	  /* EMES (Enhancement) transistor */
				sim_spice_xputs(sim_spice_element_name(ni, 'Z', &nodeindex), f);
				sim_spice_xputs(sim_spice_node_name(drainn), f);
				sim_spice_xputs(sim_spice_node_name(gaten), f);
				sim_spice_xputs(sim_spice_node_name(sourcen), f);
				sim_spice_xprintf(f, " EMES");
				break;
			case SPICETRANS:	 /* special transistor */
				sim_spice_xputs(sim_spice_element_name(ni, 'Q', &nodeindex), f);
				sim_spice_xputs(sim_spice_node_name(drainn), f);
				sim_spice_xputs(sim_spice_node_name(gaten), f);
				sim_spice_xputs(sim_spice_node_name(sourcen), f);
				/* sim_spice_xputs(sim_spice_node_name(subnet), f); */
				if (subnet != NONET) sim_spice_xputs(sim_spice_node_name(subnet), f);
				if (info != NULL) sim_spice_xprintf(f, " %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)
		{
			a = sim_spice_mask_scale*lx;
			b = sim_spice_mask_scale*ly;
			if (state == SPICENMOS || state == SPICEDMOS || state == SPICEPMOS)
			{
				sim_spice_xprintf(f, " L=%3.2fU W=%3.2fU", scaletodispunit((INTBIG)a, DISPUNITMIC),
					scaletodispunit((INTBIG)b, DISPUNITMIC));
			} else if ((state == SPICENJFET || state == SPICEPJFET || state == SPICEDMES ||
				state == SPICEEMES) && ((spice_state & SPICETYPE) == SPICEHSPICE))
			{
				sim_spice_xprintf(f, " 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, " AREA=%3.2f", scaletodispunitsq((INTBIG)(a*b), DISPUNITMIC));
		}

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

		/* compute area of source and drain */
		if (state == SPICENMOS || state == SPICEDMOS || state == SPICEPMOS)
		{
			switch (state)
			{
				case SPICEDMOS:
				case SPICENMOS: i = ISNTYPE; break;
				case SPICEPMOS: 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, " 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, " 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, " 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, " PD=%5.2fU", b);
			}
		}
		sim_spice_xprintf(f, "\n");
	}

	/* print resistances and capacitances */
	if ((spice_state&SPICERESISTANCE) != 0)
	{
		/* Print parasitic capacitances */
		first = 1;
		for(net = sim_spice_firstnet; net != NONET; net = net->nextnet)
		{
			net->resistance = scaletodispunitsq((INTBIG)net->resistance, DISPUNITMIC);
			if (net->resistance > sim_spice_min_resist)
			{
				if (first != 0)
				{
					first = 0;
					sim_spice_xprintf(f, "** Extracted Parasitic Elements:\n");
				}
				sim_spice_xprintf(f, "R%d ? ? %9.2f\n", resistnum++, net->resistance);
			}

			if (net->netnumber == 0) continue;
			if (net->capacitance > sim_spice_min_capac)
			{
				if (first != 0)
				{
					first = 0;
					sim_spice_xprintf(f, "** Extracted Parasitic Elements:\n");
				}
				sim_spice_xprintf(f, "C%d%s 0 %9.2fF\n", capacnum++,
					sim_spice_node_name(net), net->capacitance);
			}
		}
	}

	/* Sources */
	sim_spice_sourceindex = 1;
	first = 1;
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		/* only want primitive source nodes */
		if (ni->proto->primindex == 0) continue;
		state = sim_spice_nodetype(ni, &extra);
		if (state != SPICESOURCEV && state != SPICESOURCEC && state != SPICESOURCECM &&
			state != SPICESOURCET && state != SPICESOURCEDC && state != SPICESOURCEAC &&
			state != SPICESOURCEN && state != SPICESOURCEX && state != SPICESOURCEB && 
			state != SPICESOURCES) continue;
		if (first != 0)
		{
			first = 0;
			sim_spice_xprintf(f, "** Sources and special nodes:\n");
		}

		/* handle the source */
		source = ni->proto->firstportproto;
		sourcen = sim_spice_getnet(ni, source);
		if (source->nextportproto == NOPORTPROTO) drainn = NONET; else
			drainn = sim_spice_getnet(ni, source->nextportproto);

		/* determine the type of source node */
		found = SPICENORMAL;
		meterit = 0;
		switch (state)
		{
			case SPICESOURCEV:
				sourcetype = 'V';
				break;
			case SPICESOURCEC:
				sourcetype = 'I';
				break;
			case SPICESOURCECM:
				meterit = 1;
				break;
			case SPICESOURCET:
				found = SPICETRAN;
				break;
			case SPICESOURCEDC:
				found = SPICEDC;
				break;
			case SPICESOURCEAC:
				found = SPICEAC;
				break;
			case SPICESOURCEN:
				found = SPICENODESET;
				break;
			case SPICESOURCEX:
				found = SPICEEXTENSION;
				break;
			case SPICESOURCEB:
				found = SPICEBULK;
				break;
			case SPICESOURCES:
				sourcetype = extra[1];
				extra++;
				break;
		}

		/* make sure both connections are exported */
		if (found != SPICEEXTENSION)
		{
			if (sourcen != NONET)
			{
				for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
					if (pp->temp1 == sourcen->netnumber) break;
				if (pp == NOPORTPROTO)
					ttyputerr("Warning: top of %s in facet %s not connected to exported port",
						sim_spice_describesource(ni), describenodeproto(np));
			}
			if (drainn != NONET)
			{
				for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
					if (pp->temp1 == drainn->netnumber) break;
				if (pp == NOPORTPROTO)
					ttyputerr("Warning: bottom of %s in facet %s not connected to exported port",
						sim_spice_describesource(ni), describenodeproto(np));
			}
		}

		switch (found)
		{
			case SPICENORMAL:      /* handle normal source specification */
				if (sourcen == NONET || drainn == NONET)
				{
					ttyputerr("ERROR: %s in facet %s is not fully connected",
						sim_spice_describesource(ni), describenodeproto(np));
					/* break; */
				}

				/* write the card */
				dummy = sim_spice_element_name(ni, sourcetype, &sim_spice_sourceindex);
				sim_spice_xputs(dummy, f);
				sim_spice_xputs(sim_spice_node_name(sourcen), f);
				sim_spice_xputs(sim_spice_node_name(drainn), f);
				sim_spice_xprintf(f, " %s\n", extra);

				if (meterit != 0)
				{
					if (!top)
						ttyputerr("Warning: cannot meter source in subfacet: %s",
							describenodeproto(ni->proto));
					else
					{
						/* add this source to the spice .PRINT card */
						(void)initinfstr();
						(void)addstringtoinfstr(sim_spice_printcard);
						(void)addstringtoinfstr(" I(");
						(void)addstringtoinfstr(dummy);
						(void)addtoinfstr(')');
						if (reallocstring(&sim_spice_printcard, returninfstr(), sim_aid->cluster))
							return(-1);
					}
				}
				break;

			case SPICETRAN:     /* handle transient analysis specification */
				if (sim_spice_tran != 0)
				{
					ttyputerr("Warning: should be only one Transient source");
					efree(sim_spice_tran);
				}
				if (top == 0)
					ttyputerr("Warning: Transient source node found in subfacet %s",
						describenodeproto(np));

				/* create the .TRAN card */
				(void)initinfstr();
				(void)addstringtoinfstr(".TRAN ");
				(void)addstringtoinfstr(extra);
				if (allocstring(&sim_spice_tran, returninfstr(), sim_aid->cluster))
					return(-1);
				break;

			case SPICEDC:       /* handle DC analysis specification */
				if (sourcen == NONET || drainn == NONET)
				{
					ttyputerr("ERROR: DC %s in facet %s is not fully connected",
						sim_spice_describesource(ni), describenodeproto(np));
					/* break; */
				}
				if (sim_spice_dc != 0)
				{
					ttyputerr("Warning: should be only one DC source");
					efree(sim_spice_dc);
				}
				if (top == 0)
					ttyputerr("Warning: DC source node found in subfacet %s", describenodeproto(np));

				/* write the source to the deck */
				dummy = sim_spice_element_name(ni, sourcetype, &sim_spice_sourceindex);
				sim_spice_xputs(dummy, f);
				sim_spice_xputs(sim_spice_node_name(sourcen), f);
				sim_spice_xputs(sim_spice_node_name(drainn), f);
				sim_spice_xputs("\n", f);

				if (meterit != 0)
				{
					if (!top)
						ttyputerr("Warning: cannot meter source in subfacet: %s",
							describenodeproto(ni->proto));
					else
					{
						/* add this source to the spice .PRINT card */
						(void)initinfstr();
						(void)addstringtoinfstr(sim_spice_printcard);
						(void)addstringtoinfstr(" I(");
						(void)addstringtoinfstr(dummy);
						(void)addtoinfstr(')');
						if (reallocstring(&sim_spice_printcard, returninfstr(), sim_aid->cluster))
							return(-1);
					}
				}

				/* create the .DC card */
				(void)initinfstr();
				(void)addstringtoinfstr(".DC ");
				(void)addstringtoinfstr(dummy);
				(void)addtoinfstr(' ');
				(void)addstringtoinfstr(extra);
				if (allocstring(&sim_spice_dc, returninfstr(), sim_aid->cluster))
					return(-1);
				break;

			case SPICEAC:			/* handle ac analysis specification */
				if (sim_spice_ac != 0)
				{
					ttyputerr("Warning: should be only one AC source");
					efree(sim_spice_ac);
				}
				if (top == 0) ttyputerr("Warning: Transient source node found in subfacet %s",
					describenodeproto(np));

				/* create the .AC card */
				(void)initinfstr();
				(void)addstringtoinfstr(".AC ");
				(void)addstringtoinfstr(extra);
				if (allocstring(&sim_spice_ac, returninfstr(), sim_aid->cluster)) return(-1);
				break;

			case SPICENODESET:		/* handle NODESET specification */
				if (sourcen == NONET)
				{
					ttyputerr("ERROR: %s in facet %s has unconnected positive terminal",
						sim_spice_describesource(ni), describenodeproto(np));
					break;
				}

				/* no space in V(NName), because HSPICE can't handle line break after ")" */
				sim_spice_xprintf(f, ".NODESET V(%s)=%s\n", sim_spice_node_name(sourcen)+1, extra);
				break;

			case SPICEEXTENSION:	/* handle SPICEEXTENSION */
				sim_spice_xprintf(f, "X%s", extra);
				sim_spice_xputs(sim_spice_node_name(sourcen), f);
				sim_spice_xputs(sim_spice_node_name(drainn), f);
				sim_spice_xputs("\n", f);
				break;

			case SPICEBULK:			/* source to set substrate potential */
				/* We don't really have to do anything... but make a comment */
				/* print on a separate line, otherwise too long */
				sim_spice_xprintf(f, "*** P-well tied to%s\n", sim_spice_node_name(negnet));
				sim_spice_xprintf(f, "*** N-well tied to%s\n", sim_spice_node_name(posnet));
				break;
		}
	}

	/*
	 * Now we're finished writing the subcircuit.
	 * Only the top-level facet can contain meters.
	 */
	if (!top)
	{
		sim_spice_xprintf(f, ".ENDS %s\n", np->cell->cellname);

		/* free the net modules */
		for(net = sim_spice_firstnet; net != NONET; net = nnet)
		{
			nnet = net->nextnet;
			sim_freenet(net);
		}
		return(backannotate);
	}

	/* 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, &extra);
		if (state != SPICEMETER) continue;

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

		/* make sure meter is connected to exported ports */
		for(pp=np->firstportproto; pp!=NOPORTPROTO; pp=pp->nextportproto)
			if (pp->temp1 == sourcen->netnumber) break;
		if (pp == NOPORTPROTO)
			ttyputerr("Warning: top of %s component in facet %s not connected to exported port",
				describenodeinst(ni), describenodeproto(np));
		if (drainn != NONET)
		{
			for(pp=np->firstportproto; pp!=NOPORTPROTO; pp=pp->nextportproto)
				if (pp->temp1 == sourcen->netnumber) break;
			if (pp == NOPORTPROTO)
				ttyputerr("Warning: bottom of %s component in facet %s not connected to exported port",
					describenodeinst(ni), describenodeproto(np));
		}
		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((spice_state&SPICEPLOT ? ".PLOT" : ".PRINT"), f);

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

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

		/* see what kind of meter it is */
		var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, sch_meterkey);
		if (var != NOVARIABLE) extra = (char *)var->addr; else extra = "";
		if (drainn == NONET || drainn->netnumber == 0)
		{
			/* no space in V(NName), because HSPICE can't handle line break after ")" */
			sim_spice_xprintf(f, " V(%s)", sim_spice_node_name(sourcen)+1);
			if (*extra != 0) sim_spice_xprintf(f, " %s", extra);
			if (sim_spice_ac)
			{
				/* no space in V(NName), because HSPICE can't handle line break after ")" */
				sim_spice_xprintf(f, " VP(%s)", sim_spice_node_name(sourcen)+1);
				if (*extra != 0) sim_spice_xprintf(f, " %s", extra);
			}
		} else
		{
			/* no space in V(NName), because HSPICE can't handle line break after ")" */
			sim_spice_xprintf(f, " 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, "%s)", sim_spice_node_name(drainn)+1);
			if (*extra != 0) sim_spice_xprintf(f, " %s", extra);
			if (sim_spice_ac)
			{
				/* no space in V(NName), because HSPICE can't handle line break after ")" */
				sim_spice_xprintf(f, " VP(%s,", sim_spice_node_name(sourcen)+1);
				sim_spice_xprintf(f, "%s)", sim_spice_node_name(drainn)+1);
				if (*extra != 0) sim_spice_xprintf(f, " %s", extra);
			}
		}
	}
	if (first != 0) sim_spice_xprintf(f, "\n"); else
	{
		/* If no voltmeters, print metered sources anyway */
		if (*sim_spice_printcard != '\0')
		{
			sim_spice_xputs((spice_state&SPICEPLOT ? ".PLOT" : ".PRINT"), f);

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

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

	/* Miscellaneous checks */
	for(net = sim_spice_firstnet; net != NONET; net = net->nextnet)
	{
		for (i = 0; i < DIFFTYPES; i++)
			if (net->diffarea[i] != 0.0 && net->components[i] == 0)
		{
			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;
			}
			ttyputerr("Warning: SPICE node%s has unconnected%s device diffusion",
				sim_spice_node_name(net), uncon_diff_type);
		}
	}

	/* free the net modules */
	for(net = sim_spice_firstnet; net != NONET; net = nnet)
	{
		nnet = net->nextnet;
		sim_freenet(net);
	}
	return(backannotate);
}

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

	if (ni->proto->primindex == 0) return(SPICEFACET);
	nodetype = nodefunction(ni, extra);
	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 NPMETER:       return(SPICEMETER);
		case NPSOURCEV:     return(SPICESOURCEV);
		case NPSOURCEC:     return(SPICESOURCEC);
		case NPSOURCECM:    return(SPICESOURCECM);
		case NPSOURCET:     return(SPICESOURCET);
		case NPSOURCEDC:    return(SPICESOURCEDC);
		case NPSOURCEAC:    return(SPICESOURCEAC);
		case NPSOURCEN:     return(SPICESOURCEN);
		case NPSOURCEX:     return(SPICESOURCEX);
		case NPSOURCEB:     return(SPICESOURCEB);
		case NPSOURCES:     return(SPICESOURCES);
		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 NPARRAY:       return(SPICEARRAY);
		case NPCONGROUND:   return(SPICEGROUND);
		case NPCONPOWER:    return(SPICEPOWER);
	}
	return(SPICEUNKNOWN);
}

/*
 * routine to search the net list for this facet and return the net number
 * associated with nodeinst "ni", port "pp"
 */
NET *sim_spice_getnet(NODEINST *ni, PORTPROTO *pp)
{
	REGISTER NET *net;
	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 == pp->network)
	{
		for(net = sim_spice_firstnet; net != NONET; net = net->nextnet)
			if (pi->conarcinst->network == net->network) return(net);
	}

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

	return(NONET);
}

/*
 * 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(NET *net, NODEINST *ni, PORTPROTO *pp)
{
	REGISTER INTSML tot, i, function;
	REGISTER INTBIG fun;
	XARRAY trans;
	INTBIG lx, hx, ly, hy;
	INTSML j, dominant;
	REGISTER POLYGON *poly, *lastpoly, *firstpoly;
	REGISTER PORTARCINST *pi;
	REGISTER TECHNOLOGY *tech;
	float worst, cap;
	char *extra;

	/* 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, &extra);
		if (function == SPICEFACET || function == SPICEMETER || function == SPICESOURCEV ||
			function == SPICESOURCEC || function == SPICESOURCECM || function == SPICESOURCET ||
			function == SPICESOURCEDC || function == SPICESOURCEAC || function == SPICESOURCEN ||
			function == SPICESOURCEX || function == SPICESOURCEB || function == SPICESOURCES ||
			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 == SPICEPMOS || function == SPICEDMOS ||
			function == SPICEEMES || function == SPICEDMES) function = SPICENMOS;   /* One will do */

		/* make linked list of polygons */
		lastpoly = firstpoly = NOPOLYGON;
		tot = nodeEpolys(ni);
		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);
			j = isbox(poly, &lx, &hx, &ly, &hy);
			if (tech != el_curtech || j == 0)
			{
				if (sim_spice_debug != 0)
					ttyputmsg("%s POLYGON ON %s NODE IGNORED IN %s TECHNOLOGY",
						layername(tech, poly->layer), ni->proto->cell->cellname, tech->techname);
				continue;
			}
			sim_spice_store_box(tech, poly->layer, hx-lx, hy-ly, (lx+hx)/2, (ly+hy)/2);
			if (sim_spice_debug != 0)
				ttyputmsg("BOX: %ld to %ld; %ld to %ld; layer %s",lx,hx,ly,hy,
					layername(tech,poly->layer));
			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(net, 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(NET *net, ARCINST *ai)
{
	REGISTER INTSML i, tot;
	REGISTER TECHNOLOGY *tech;
	static POLYGON *poly = NOPOLYGON;
	INTBIG lx, hx, ly, hy;
	INTSML j, isdiffarc;

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

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

	tot = arcpolys(ai);
	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 == 0)
		{
			if (sim_spice_debug != 0)
				ttyputmsg("%s POLYGON ON %s ARC IGNORED IN %s TECHNOLOGY",
					layername(tech, poly->layer), describearcinst(ai), tech->techname);
			continue;
		}
		if ((layerfunction(tech, poly->layer)&LFPSEUDO) != 0) continue;
		if (sim_spice_layerisdiff(tech, poly->layer) != ISNONE ||
			(!isdiffarc && 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);
		if (sim_spice_debug != 0)
			ttyputmsg("BOX: %ld to %ld; %ld to %ld; layer %s",lx,hx,ly,hy, layername(tech,poly->layer));
	}

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

/*
 * routine to write the port net numbers of facet "np" to file "f".  The
 * numbers must be unique and must not include net zero (ground)
 */
void sim_spice_writeports(FILE *f, NODEPROTO *np)
{
	REGISTER PORTPROTO *pp, *opp;

	for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
	{
		/* ensure that this net number gets written only once */
		for(opp = np->firstportproto; opp != pp; opp = opp->nextportproto)
			if (opp->temp1 == pp->temp1) break;
		if (opp != pp) continue;

		/* do not write the ground net */
		if (pp->temp1 == 0) continue;

		/* write the net number */
		sim_spice_xprintf(f, " %s", sim_spice_pp_name(pp));
	}
}

/********************* Routines to build spice cards **********************/

/*
 * Warning:
 *      These routines are not re-entrant.  You must use the returned string
 *      before calling the routine again.
 */

/*
 * 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.
 */
char *sim_spice_element_name(NODEINST *ni, char first, INTSML *counter)
{
	VARIABLE *varname;
	static char s[80];  /* this is overkill - spice only recognizes 8 chars */

	varname = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name);
	if (varname == NOVARIABLE)
	{
		ttyputerr("SPICE generation warning: no name on node %s", describenodeinst(ni));
		(void)sprintf(s, "%c%d", first, (*counter)++);
	} else
	{
		(void)sprintf(s, "%c%s", first, (char *)varname->addr);
	}

	return(s);
}

/*
 * The ground node is always written as node 0 (name is never used)
 * in SPICE2.  Unknown nets are assigned as node '*'.
 */
char *sim_spice_node_name(NET *net)
{
	static char s[80];
	REGISTER INTSML namegroundzero;

	if (net == NONET) return(" *");

	/* decide whether or not to name the ground net "0" */
	namegroundzero = 1;
	/* if ((spice_state & SPICETYPE) != SPICE2) namegroundzero = 0; */

	if ((spice_state&SPICENODENAMES) != 0 &&
		(net->netnumber != 0 || namegroundzero == 0))
	{
		if ((net->network->namecount > 0))
		{
			(void)sprintf(s, " %s", net->network->netname);
			if (isdigit(s[1])) (void)sprintf(s, " %ld", net->netnumber);
		} else
			(void)sprintf(s, " %ld", net->netnumber);
	} else
		(void)sprintf(s, " %ld", net->netnumber);

	return(s);
}

/*
 * This is almost identical to sim_spice_node_name, but takes a portproto
 * instead.
 */
char *sim_spice_pp_name(PORTPROTO *pp)
{
	static char s[80];
	REGISTER INTSML namegroundzero;

	if (pp == NOPORTPROTO) return("0");

	/* decide whether or not to name the ground net "0" */
	namegroundzero = 1;
	/* if ((spice_state & SPICETYPE) != SPICE2) namegroundzero = 0; */

	if ((spice_state&SPICENODENAMES) != 0)
	{
		if (pp->network->namecount > 0 &&
			(pp->temp1 != 0 || namegroundzero == 0))
		{
			(void)strcpy(s, pp->network->netname);
			if (isdigit(s[0])) (void)sprintf(s, "%ld", pp->temp1);
		} else
			(void)sprintf(s, "%ld", pp->temp1);
	} else
		(void)sprintf(s, "%ld", pp->temp1);

	return(s);
}


/********************* Spice deck output routines ***************************/
/*
 * 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, char *format, ...)
{
	va_list ap;
	char s[256];

	var_start(ap, format);
	(void)vsprintf(s, format, ap);
	va_end(ap);
	sim_spice_xputs(s, stream);
}

/*
 * Routine to write string "s" onto stream "stream" in all upper case.
 */
void sim_spice_xputs(char *s, FILE *stream)
{
	char *pt;
	static INTSML i=0;
	char *lastspace;

	/* put in line continuations, if over 78 chars long */
	lastspace = NULL;
	for (pt = s; *pt; pt++)
	{
		if ((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);
					xputs("\n+  ", stream);
					s = lastspace + 1;
					i = 9 + pt-s+1;
					lastspace = NULL;
				} else
				{
					xputs("\n+  ", stream);
					i = 9 + 1;
				}
			}
		}
	}
	xputs(s, stream);
}

/****************************************************************************/

char *sim_spice_describesource(NODEINST *ni)
{
	REGISTER VARIABLE *var;
	REGISTER char *str;

	var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, sch_sourcekey);
	if (var == NOVARIABLE) return(describenodeinst(ni));
	str = (char *)var->addr;
	while (*str != 0 && *str != '/') str++;
	if (*str == 0) return(describenodeinst(ni));
	(void)initinfstr();
	(void)addstringtoinfstr(describenodeinst(ni));
	(void)addstringtoinfstr(str);
	return(returninfstr());
}

/*************************** BOX MERGING ROUTINES ***************************/

/*
 * Routine to store a box on layer "layer" into the box merging system
 */
void sim_spice_store_box(TECHNOLOGY *tech, INTSML layer, INTBIG xs, INTBIG ys, INTBIG xc, INTBIG yc)
{
	INTSML 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(INTSML layer, TECHNOLOGY *tech, INTBIG *xbuf, INTBIG *ybuf, INTSML count)
{
	REGISTER INTBIG perim;
	float area;
	REGISTER INTSML i, j;

	if (sim_spice_debug != 0)
	{
		ttyputmsg("** Evaluating polygon on Layer: %s; Net: %ld",
			layername(tech, layer), sim_spice_cur_net->netnumber);
	}

	/* 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;
	}
	if (sim_spice_debug != 0)
		ttyputmsg("Area: %f , Perim: %ld Cap: %f", area, perim, sim_spice_cur_net->capacitance);
}

/******************** ROUTINES TO ACCESS VARIABLES ********************/

/*
 * routine to return nonzero if layer "layer" is on diffusion
 * Return the type of the diffusion
 */
INTSML sim_spice_layerisdiff(TECHNOLOGY *tech, INTSML 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
 */
INTSML 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 find the capacitance for the arc on layer "layer"
 */
float sim_spice_capacitance(TECHNOLOGY *tech, INTSML 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, INTSML 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_aid->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]));
}

/*
 * Function to return the same view as top-level facet.
 * "spice_top_view" 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 == spice_top_view) return(rnp);

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

/*
* 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, INTSML type, char *extra,
	INTSML *part, INTSML report)
{
	REGISTER PORTPROTO *pp1, *pp2;
	REGISTER NET *end1, *end2;

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

	/* make sure the component is connected to nets */
	if (end1 == NONET || end2 == NONET)
	{
		sim_spice_xprintf(f, "*** WARNING: %s component not fully connected\n*** ",
			describenodeinst(ni));
	}
	if (end1 != NONET && end2 != NONET)
		if (end1->netnumber == end2->netnumber)
	{
		if (report)
			sim_spice_xprintf(f, "*** WARNING: %s component appears to be shorted on net %s\n",
				describenodeinst(ni), sim_spice_node_name(end1));
		return;
	}
	if (extra == 0) extra = "";

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

/* 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(CELL *cell, FILE *spfile, TECHNOLOGY *tech)
{
	VARIABLE *var;
	FILE    *trailerfile;
	char    c, *truename;

	/* get spice trailer cards from file if specified */
	var = getval((INTBIG)tech, VTECHNOLOGY, VSTRING, "SIM_spice_trailer_file");
	if (var != NOVARIABLE)
	{
		trailerfile = xopen((char *)var->addr, 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);
	}
}

#endif  /* SIMAID - at top */
