/*
 * Electric(tm) VLSI Design System
 *
 * File: simsim.c
 * Simulation aid: ESIM, RSIM, RNL, and COSMOS controller
 * Written by: Steven M. Rubin, Static Free Software
 *
 * 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
 */

#include "config.h"
#if SIMAID

#include "global.h"
#include "sim.h"
#include "efunction.h"
#include "usr.h"
#include "tecschem.h"
#include <setjmp.h>

static INTBIG sim_cosmos_attribute = 0;
jmp_buf sim_userdone;		/* for when simulator command is done */
INTBIG sim_index, sim_vdd, sim_gnd, sim_namethresh,
	  sim_phi1h, sim_phi1l, sim_phi2h, sim_phi2l, count_unconn;
char *sim_name, *sim_prompt, *sim_inputfile = 0;

/* prototypes for local routines */
INTSML sim_simsearch(INTSML, NODEPROTO*, INTSML);
void sim_simtell(void);
void sim_simprint(NODEPROTO*, FILE*, INTSML);
void sim_simprintcosmos(NODEPROTO*, FILE*, INTSML);
char *sim_makenodename(INTBIG, INTSML);
void sim_prop(ARCINST*, INTBIG, INTBIG*, INTBIG*, INTBIG*, INTBIG*, INTBIG*, INTBIG*);

void sim_writesim(NODEPROTO *simnt, INTSML format)
{
	REGISTER INTBIG save0, save1, save2;
	REGISTER INTSML i;
	REGISTER VARIABLE *var;
	REGISTER FILE *f;
	REGISTER PORTPROTO *pp, *spt, *shpt;
	char *truename, *presimloc, *execargs[5];
	REGISTER char *simprog, *presimtmp, *presimprog, *netfile;

	/* 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 (format == COSMOS)
	{
		/* get variable names (only once) */
		if (sim_cosmos_attribute == 0) sim_cosmos_attribute = makekey("SIM_cosmos_attribute");
	}

	count_unconn = 0;
	sim_simnt = simnt;

	/* initialize for the particular simulator */
	var = getvalkey((INTBIG)sim_aid, VAID, VINTEGER, sim_format);
	if (var == NOVARIABLE) format = -1; else format = (INTSML)var->addr;

	/* construct the simulation description file name from the facet */
	(void)initinfstr();
	(void)addstringtoinfstr(simnt->cell->cellname);
	(void)addstringtoinfstr(".sim");
	var = setvalkey((INTBIG)sim_aid, VAID, sim_netfile, (INTBIG)returninfstr(), VSTRING|VDONTSAVE);
	if (var != NOVARIABLE) netfile = (char *)var->addr; else netfile = "";

	/* also record the actual input file to the simulator */
	if (sim_inputfile != 0) efree(sim_inputfile);
	(void)allocstring(&sim_inputfile, netfile, sim_aid->cluster);

	/* create the simulation file */
	f = xcreate(netfile, sim_filetypeesim, _("Netlist File"), &truename);
	if (f == NULL)
	{
		if (truename != 0) ttyputerr(_("Cannot write %s"), truename);
		return;
	}
	xprintf(f,"| %s\n", netfile);
	if (simnt->creationdate != 0)
		xprintf(f, "| Facet created %s\n", timetostring(simnt->creationdate));
	xprintf(f, "| Version %d", simnt->version);
	if (simnt->revisiondate != 0)
		xprintf(f, " last revised %s\n", timetostring(simnt->revisiondate));
	if (format == COSMOS)
	{
		xprintf(f,"| [e | d | p | n] gate source drain length width xpos ypos {[gsd]=attrs}\n");
		xprintf(f,"| N node D-area D-perim P-area P-perim M-area M-perim\n");
		xprintf(f,"| A node attrs\n");
		xprintf(f,"|  attrs = [Sim:[In | Out | 1 | 2 | 3 | Z | U]]\n");
	} else
	{
		xprintf(f,"| [epd] gate source drain length width r xpos ypos area\n");
		xprintf(f,"| N node xpos ypos M-area P-area D-area D-perim\n");
	}

	/* assign net-list values to each port of the top facet */
	sim_index = 1;
	sim_vdd = sim_gnd = 0;
	sim_phi1h = sim_phi1l = sim_phi2h = sim_phi2l = 0;
	for(pp = simnt->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
	{
		/* see if this port is on the same net number as some other port */
		for(spt=simnt->firstportproto; spt!=pp; spt=spt->nextportproto)
			if (spt->network == pp->network) break;

		/* this port is the same as another one, use same net */
		if (spt != pp)
		{
			pp->temp1 = spt->temp1;
			continue;
		}

		/* see if the port is special */
		if (portispower(pp) != 0)
		{
			if (sim_vdd == 0) sim_vdd = sim_index++;
			pp->temp1 = sim_vdd;   continue;
		}
		if (portisground(pp) != 0)
		{
			if (sim_gnd == 0) sim_gnd = sim_index++;
			pp->temp1 = sim_gnd;   continue;
		}
		if ((pp->userbits&STATEBITS) == C1PORT ||
			namesamen(pp->protoname, "clk1", 4) == 0 || namesamen(pp->protoname, "phi1h", 5) == 0)
		{
			if (sim_phi1h == 0) sim_phi1h = sim_index++;
			pp->temp1 = sim_phi1h;   continue;
		}
		if ((pp->userbits&STATEBITS) == C2PORT || namesamen(pp->protoname, "phi1l", 5) == 0)
		{
			if (sim_phi1l == 0) sim_phi1l = sim_index++;
			pp->temp1 = sim_phi1l;   continue;
		}
		if ((pp->userbits&STATEBITS) == C3PORT ||
			namesamen(pp->protoname, "clk2", 4) == 0 || namesamen(pp->protoname, "phi2h", 5) == 0)
		{
			if (sim_phi2h == 0) sim_phi2h = sim_index++;
			pp->temp1 = sim_phi2h;   continue;
		}
		if ((pp->userbits&STATEBITS) == C4PORT || namesamen(pp->protoname, "phi2l", 5) == 0)
		{
			if (sim_phi2l == 0) sim_phi2l = sim_index++;
			pp->temp1 = sim_phi2l;   continue;
		}

		/* assign a new nodeinst number to this portinst */
		pp->temp1 = sim_index++;
	}
	sim_namethresh = sim_index;

	if (sim_vdd == 0) ttyputmsg(_("Warning: no power export in this facet"));
	if (sim_gnd == 0) ttyputmsg(_("Warning: no ground export in this facet"));

	/* select the shortest name for electrically equivalent ports */
	for(pp = simnt->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
		pp->temp2 = 0;
	for(pp = simnt->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
	{
		if (pp->temp2 != 0) continue;
		for(spt = simnt->firstportproto; spt != NOPORTPROTO; spt = spt->nextportproto)
			if (spt->temp1 == pp->temp1) break;
		if (spt == NOPORTPROTO)
		{
			pp->temp2 = 1;
			continue;
		}

		/* multiple electrically equivalent ports: find shortest of all */
		i = strlen(pp->protoname);
		shpt = pp;
		for(spt = simnt->firstportproto; spt != NOPORTPROTO; spt = spt->nextportproto)
		{
			if (spt->temp1 != pp->temp1) continue;
			spt->temp2 = -1;
			if ((INTBIG)strlen(spt->protoname) >= i) continue;
			shpt = spt;
			i = strlen(spt->protoname);
		}
		shpt->temp2 = 1;
	}

	if (format == COSMOS) sim_simprintcosmos(simnt, f, format);
		else sim_simprint(simnt, f, format);

	/* describe the nonspecial ports */
	i = 0;
	for(pp = simnt->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
	{
		if (pp->temp1 == sim_vdd || pp->temp1 == sim_gnd || pp->temp1 == sim_phi1h ||
			pp->temp1 == sim_phi1l || pp->temp1 == sim_phi2h || pp->temp1 == sim_phi2l) continue;

		/* see if this port name is nonunique and unwanted */
		if (pp->temp2 < 0) continue;

		/* just a normal node: write its characteristics */
		if (i++ == 0) ttyputmsg(_("These export names are available:"));
		ttyputmsg("   %s", pp->protoname);
		if (format == COSMOS)
		{
			if ((pp->userbits & STATEBITS) == INPORT)
				xprintf(f, "A %s Sim:In\n", pp->protoname);
					else if ((pp->userbits & STATEBITS) == OUTPORT ||
						(pp->userbits & STATEBITS) == BIDIRPORT)
							xprintf(f, "A %s Sim:Out\n", pp->protoname);
		}
	}

	/* clean up */
	xclose(f);

	/* if execution is not desired, quit now */
	var = getvalkey((INTBIG)sim_aid, VAID, VINTEGER, sim_dontrun);
	if (format == COSMOS || (var != NOVARIABLE && var->addr == SIMRUNNO))
	{
		if (count_unconn != 0)
			ttyputmsg(_("Warning: %ld unconnected nodes were found"), count_unconn);
		ttyputmsg(_("%s written"), truename);
		return;
	}

	/* with RNL, there is a pre-processing step */
	if (format == RNL || format == RSIM)
	{
		/* start up the presimulator */
		if (format == RSIM)
		{
			presimtmp = RSIMIN;
			presimprog = RSIMPRENAME;
		} else
		{
			presimtmp = RNLIN;
			presimprog = RNLPRENAME;
		}

		/* try to fork */
		sim_process = efork();

		/* if forking is not available, just show files and quit */
		if (sim_process == 1)
		{
			if (count_unconn != 0)
				ttyputmsg(_("Warning: %ld unconnected nodes were found"), count_unconn);
			ttyputverbose(_("Created simulation file %s"), netfile);
			return;
		}

		/* fork the process */
		if (sim_process == 0)
		{
			save1 = channelreplacewithfile(1, "/dev/null");
			save2 = channelreplacewithfile(2, "/dev/null");
			presimloc = egetenv("ELECTRIC_PRESIMLOC");
			if (presimloc == NULL) presimloc = PRESIMLOC;
			execargs[0] = presimprog;
			execargs[1] = netfile;
			execargs[2] = presimtmp;
			execargs[3] = 0;
			eexec(presimloc, execargs);
			channelrestore(1, save1);
			channelrestore(2, save2);
			ttyputerr(_("Cannot run the presimulator"));
			exit(1);
		}
		ewait(sim_process);
		ttyputmsg(_("Pre-simulator finished"));
		(void)reallocstring(&sim_inputfile, presimtmp, sim_aid->cluster);
	}

	/* for communication with simulator, read fromsim[0], write tosim[1] */
	(void)epipe(sim_tosim);
	(void)epipe(sim_fromsim);

	/* start up the simulation process */
	switch (format)
	{
		case ESIM:
			sim_name = ESIMNAME;
			simprog = egetenv("ELECTRIC_ESIMLOC");
			if (simprog == NULL) simprog = ESIMLOC;
			sim_prompt = "sim> ";
			break;
		case RSIM:
			sim_name = RSIMNAME;
			simprog = egetenv("ELECTRIC_RSIMLOC");
			if (simprog == NULL) simprog = RSIMLOC;
			sim_prompt = "rsim> ";
			break;
		case RNL:
			sim_name = RNLNAME;
			simprog = egetenv("ELECTRIC_RNLLOC");
			if (simprog == NULL) simprog = RNLLOC;
			sim_prompt = "";
			break;
	}

	/* try to fork */
	sim_process = efork();

	/* if forking is not available, just show files and quit */
	if (sim_process == 1)
	{
		if (count_unconn != 0)
			ttyputmsg(_("Warning: %ld unconnected nodes were found"), count_unconn);
		ttyputverbose(_("Created simulation file %s"), netfile);
		return;
	}

	/* fork the process */
	if (sim_process == 0)
	{
		save0 = channelreplacewithchannel(0, sim_tosim[0]);
		save1 = channelreplacewithchannel(1, sim_fromsim[1]);
		save2 = channelreplacewithchannel(2, sim_fromsim[1]);

		execargs[0] = sim_name;
		if (format == RNL)
		{
			execargs[1] = 0;
		} else
		{
			execargs[1] = sim_inputfile;
			execargs[2] = 0;
		}
		eexec(simprog, execargs);
		channelrestore(0, save0);
		channelrestore(1, save1);
		channelrestore(2, save2);
		ttyputerr(_("Cannot run simulator %s"), simprog);
		exit(1);
	}

	/* close the file descriptor so the pipe will terminate when done */
	eclose(sim_fromsim[1]);

	/* setup the line back from the simulator */
	setinteractivemode(sim_fromsim[0]);

	/* now communicate with the simulator */
	sim_resumesim(1);
}

INTSML sim_simpointout(char *nonum, INTSML format)
{
	REGISTER INTSML want;
	REGISTER PORTPROTO *pp, *spt;

	if (sim_simnt == NONODEPROTO)
	{
		ttyputerr(_("No simulation done"));
		return(1);
	}

	/* make sure user is still in the same facet */
	if (sim_simnt != getcurfacet())
	{
		ttyputerr(_("Simulation was done on facet %s; please edit that facet"),
			describenodeproto(sim_simnt));
		return(1);
	}

	/* figure out the desired nodeinst number */
	if (*nonum >= '0' && *nonum <= '9') want = atoi(nonum); else
	{
		for(pp = sim_simnt->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
			if (namesame(nonum, pp->protoname) == 0) break;
		if (pp == NOPORTPROTO)
		{
			ttyputerr(_("Cannot find a port called '%s'"), nonum);
			return(1);
		}
		want = (INTSML)pp->temp1;
	}

	/* re-assign net-list values, starting at each portinst of the top facet */
	sim_index = 1;
	sim_vdd = sim_gnd = 0;
	sim_phi1h = sim_phi1l = sim_phi2h = sim_phi2l = 0;
	for(pp=sim_simnt->firstportproto; pp!=NOPORTPROTO; pp=pp->nextportproto)
	{
		for(spt=sim_simnt->firstportproto; spt!=pp; spt=spt->nextportproto)
		{
			if (spt->network != pp->network) continue;

			/* this port is the same as another one, use same net */
			pp->temp1 = spt->temp1;
			break;
		}
		if (spt != pp) continue;
		if (format == COSMOS)
		{
			/* see if the port is special */
			if (portispower(pp) != 0)
			{
				if (sim_vdd == 0) sim_vdd = sim_index++;
				pp->temp1 = sim_vdd;   continue;
			}
			if (portisground(pp) != 0)
			{
				if (sim_gnd == 0) sim_gnd = sim_index++;
				pp->temp1 = sim_gnd;   continue;
			}
			if ((pp->userbits&STATEBITS) == C1PORT ||
				namesamen(pp->protoname, "clk1", 4) == 0 || namesamen(pp->protoname, "phi1h", 5) == 0)
			{
				if (sim_phi1h == 0) sim_phi1h = sim_index++;
				pp->temp1 = sim_phi1h;   continue;
			}
			if ((pp->userbits&STATEBITS) == C2PORT || namesamen(pp->protoname, "phi1l", 5) == 0)
			{
				if (sim_phi1l == 0) sim_phi1l = sim_index++;
				pp->temp1 = sim_phi1l;   continue;
			}
			if ((pp->userbits&STATEBITS) == C3PORT ||
				namesamen(pp->protoname, "clk2", 4) == 0 || namesamen(pp->protoname, "phi2h", 5) == 0)
			{
				if (sim_phi2h == 0) sim_phi2h = sim_index++;
				pp->temp1 = sim_phi2h;   continue;
			}
			if ((pp->userbits&STATEBITS) == C4PORT || namesamen(pp->protoname, "phi2l", 5) == 0)
			{
				if (sim_phi2l == 0) sim_phi2l = sim_index++;
				pp->temp1 = sim_phi2l;   continue;
			}
			/* assign a new nodeinst number to this portinst */
			pp->temp1 = sim_index++;
		} else pp->temp1 = sim_index++;
	}

	/* look for node number */
	if (sim_simsearch(want, sim_simnt, format) != 0)
	{
		ttyputerr(_("Cannot find node '%s'"), nonum);
		return(1);
	}
	return(0);
}

INTSML sim_simsearch(INTSML want, NODEPROTO *facet, INTSML format)
{
	char *newpar[2];
	INTBIG darea, dperim, parea, pperim, marea, mperim;
	REGISTER INTSML i;
	REGISTER ARCINST *ai;
	REGISTER PORTPROTO *pp, *spt;
	REGISTER PORTARCINST *pi;
	REGISTER PORTEXPINST *pe;
	REGISTER NODEINST *ni;
	REGISTER NODEPROTO *np, *snp;
	REGISTER INTBIG type;

	/* reset the arcinst node values */
	for(ai = facet->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
		ai->temp1 = 0;

	/* set every arcinst to a global node number (from inside or outside) */
	for(ai = facet->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
	{
		if (ai->temp1 != 0) continue;

		/* see if this arcinst is connected to a port */
		for(pp=facet->firstportproto; pp!=NOPORTPROTO; pp=pp->nextportproto)
		{
			if (pp->network != ai->network) continue;
			if (pp->temp1 == want)
			{
				(void)askaid(us_aid, "clear");
				(void)askaid(us_aid, "show-object", (INTBIG)ai->geom);
				newpar[0] = "highlight";
				(void)tellaid(net_aid, 1, newpar);
				return(0);
			}
			darea = dperim = parea = pperim = marea = mperim = 0;
			sim_prop(ai, (INTBIG)pp->temp1, &darea, &dperim, &parea, &pperim, &marea, &mperim);
			break;
		}

		/* if not on a port, this is an internal node */
		if (pp == NOPORTPROTO)
		{
			if (sim_index == want)
			{
				(void)askaid(us_aid, "clear");
				(void)askaid(us_aid, "show-object", (INTBIG)ai->geom);
				newpar[0] = "highlight";
				(void)tellaid(net_aid, 1, newpar);
				return(0);
			}
			darea = dperim = parea = pperim = marea = mperim = 0;
			sim_prop(ai, sim_index, &darea, &dperim, &parea, &pperim, &marea, &mperim);
			sim_index++;
		}
	}

	/* see if the port has the right node but is unconnected */
	for(pp = facet->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
		if (pp->temp1 == want)
	{
		(void)askaid(us_aid, "clear");
		(void)askaid(us_aid, "show-port", (INTBIG)pp->subnodeinst->geom, (INTBIG)pp->subportproto);
		return(0);
	}

	/* look at every nodeinst in the facet */
	for(ni = facet->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		np = ni->proto;
		if (format == COSMOS)
		{
			type = (np->userbits&NFUNCTION) >> NFUNCTIONSH;
			snp = np;
			if (np->primindex == 0)
			{
				type = NPUNKNOWN;
				np = contentsview(snp);
				if (np == NONODEPROTO) np = snp;
			}

			/* ignore artwork */
			if (type == NPART) continue;
		}

		/* initialize network numbers on every portinst on this nodeinst */
		for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto) pp->temp1 = 0;

		/* set network numbers from arcs and exports */
		for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
		{
			pp = pi->proto;
			if (format == COSMOS && snp != np)
			{
				pp = equivalentport(snp, pp, np);
				if (pp == NOPORTPROTO) continue;
			}
			if (pp->temp1 != 0) continue;
			pp->temp1 = pi->conarcinst->temp1;
			if (pp->temp1 == 0) continue;
			for(spt = np->firstportproto; spt != NOPORTPROTO; spt = spt->nextportproto)
				if (spt->network == pp->network) spt->temp1 = pp->temp1;
		}
		for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
		{
			pp = pe->proto;
			if (format == COSMOS && snp != np)
			{
				pp = equivalentport(snp, pp, np);
				if (pp == NOPORTPROTO) continue;
			}
			if (pp->temp1 != 0) continue;
			pp->temp1 = pe->exportproto->temp1;
			if (pp->temp1 == 0) continue;
			for(spt = np->firstportproto; spt != NOPORTPROTO; spt = spt->nextportproto)
				if (spt->network == pp->network) spt->temp1 = pp->temp1;
		}

		/* look for unconnected ports and assign new network numbers */
		for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
		{
			if (pp->temp1 != 0) continue;

			/* Hack!!! ignore unconnected well and select ports, flag the others */
			if (format == COSMOS)
			{
				if (namesame(pp->protoname, "well") != 0 && namesame(pp->protoname, "select") != 0)
					pp->temp1 = sim_index++;
			} else pp->temp1 = sim_index++;
		}

		/* if it is a facet, recurse */
		if (np->primindex == 0)
		{
			i = sim_simsearch(want, ni->proto, format);
			if (i == 0) return(0);
		}
	}
	return(1);
}

void sim_resumesim(INTSML firsttime)
{
	REGISTER INTBIG i, format;
	char line[200], *ptr;
	REGISTER VARIABLE *var;

	if (sim_process == 1)
	{
		ttyputerr(_("Cannot communicate with the simulator"));
		return;
	}
	ttyputmsg(_("YOU ARE TALKING TO THE SIMULATOR '%s'"), sim_name);

	if (firsttime)
	{
		ttyputmsg(_("Type 'help' if you need it"));
		var = getvalkey((INTBIG)sim_aid, VAID, VINTEGER, sim_format);
		if (var == NOVARIABLE) format = -1; else format = var->addr;
		if (format == RNL)
		{
			/* get the simulator to load the default commands */
			(void)sprintf(line, "(load \"%s\") (read-network \"%s%s\")", el_libdir, RNLCOMM,
				sim_inputfile);
			ttyputmsg(line);
			(void)ewrite(sim_tosim[1], line, strlen(line));
			(void)ewrite(sim_tosim[1], "\n", 1);
		}
	}
	ptr = line;
	for(;;)
	{
		i = setjmp(sim_userdone);
		switch (i)
		{
			case 0: break;		/* first return from "setjmp" */
			case 1: continue;		/* normal return from sim_simtell */
			case 2:			/* stop the simulation */
				eclose(sim_fromsim[0]);
				eclose(sim_tosim[1]);
				ekill(sim_process);
				ewait(sim_process);
				sim_process = -1;
				var = getvalkey((INTBIG)sim_aid, VAID, VSTRING, sim_netfile);
				if (var != NOVARIABLE)
					ttyputmsg(_("Net list saved in '%s'"), (char *)var->addr);
				return;
			case 3:			/* pause the simulation */
				if (firsttime)
					ttyputmsg(_("Type '-tellaid simulation resume' to return"));
				return;
		}

		/* read from the simulator and list on the status terminal */
		for(;;)
		{
			i = eread(sim_fromsim[0], ptr, 1);
			if (i <= 0) break;

			if (*ptr == '\n')
			{
				*ptr = 0;
				ttyputmsg("%s", line);
				ptr = line;
				continue;
			}
			*++ptr = 0;
			if (*sim_prompt != 0 && strcmp(line, sim_prompt) == 0)
			{
				ptr = line;
				sim_simtell();
			}
		}
		if (ttydataready() != 0) sim_simtell();

		ttyputmsg(_("Abnormal simulator termination"));
		longjmp(sim_userdone, 2);
	}
}

void sim_simtell(void)
{
	char *newpar[2];
	REGISTER char *pp;
	REGISTER INTSML len;
	REGISTER VARIABLE *var;

	for(;;)
	{
		pp = ttygetline(sim_prompt);
		if (pp == 0) break;

		/* handle nonsimulator commands */
		if (*pp == 'p')
		{
			(void)ewrite(sim_tosim[1], "\n", 1);
			longjmp(sim_userdone, 3);
		}
		len = strlen(pp);
		if (len > 1 && namesamen(pp, "help", len) == 0)
		{
			newpar[0] = "help";   newpar[1] = sim_name;
			(void)tellaid(us_aid, 2, newpar);
		} else break;
	}

	/* send user command to simulator */
	if (*pp == 'q')
	{
		var = getvalkey((INTBIG)sim_aid, VAID, VINTEGER, sim_format);
		if (var != NOVARIABLE && var->addr == RNL) *pp = CTRLDKEY;
	}
	len = strlen(pp);
	if (len > 0) (void)ewrite(sim_tosim[1], pp, len);
	(void)ewrite(sim_tosim[1], "\n", 1);
	if (*pp == 'q' || *pp == CTRLDKEY) longjmp(sim_userdone, 2);
	longjmp(sim_userdone, 1);
}

void sim_simprint(NODEPROTO *facet, FILE *f, INTSML format)
{
	REGISTER INTBIG gate, source, drain, type;
	INTBIG darea, dperim, parea, pperim, marea, mperim, length, width;
	REGISTER ARCINST *ai;
	REGISTER PORTPROTO *p, *pp, *spt;
	REGISTER PORTARCINST *pi;
	REGISTER PORTEXPINST *pe;
	REGISTER NODEINST *ni;
	REGISTER NODEPROTO *np, *snp;
	REGISTER VARIABLE *var;
	REGISTER char *str;

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

	/* reset the arcinst node values */
	for(ai = facet->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
		ai->temp1 = 0;

	/* set every arcinst to a global node number (from inside or outside) */
	for(ai = facet->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
	{
		if (ai->temp1 != 0) continue;

		/* see if this arcinst is connected to a port */
		for(pp=facet->firstportproto; pp!=NOPORTPROTO; pp=pp->nextportproto)
		{
			if (pp->network != ai->network) continue;
			darea = dperim = parea = pperim = marea = mperim = 0;
			sim_prop(ai, (INTBIG)pp->temp1, &darea, &dperim, &parea, &pperim, &marea, &mperim);
			if (pp->temp1 != sim_vdd && pp->temp1 != sim_gnd &&
				(marea != 0 || parea != 0 || darea != 0))
					xprintf(f, "N %s 0 0 %g %g %g %g\n", sim_makenodename(pp->temp1, format),
						scaletodispunitsq(marea, DISPUNITMIC), scaletodispunitsq(parea, DISPUNITMIC),
							scaletodispunitsq(darea, DISPUNITMIC), scaletodispunit(dperim, DISPUNITMIC));
			break;
		}

		/* if not on a portinst, this is an internal node */
		if (pp == NOPORTPROTO)
		{
			darea = dperim = parea = pperim = marea = mperim = 0;
			sim_prop(ai, sim_index, &darea, &dperim, &parea, &pperim, &marea, &mperim);
			if (marea != 0 || parea != 0 || darea != 0)
				xprintf(f, "N %s 0 0 %g %g %g %g\n", sim_makenodename(sim_index, format),
					scaletodispunitsq(marea, DISPUNITMIC), scaletodispunitsq(parea, DISPUNITMIC),
						scaletodispunitsq(darea, DISPUNITMIC), scaletodispunit(dperim, DISPUNITMIC));
			sim_index++;
		}
	}

	/* look at every nodeinst in the facet */
	for(ni = facet->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		/* initialize network numbers on every portinst on this nodeinst */
		np = ni->proto;
		type = (np->userbits&NFUNCTION) >> NFUNCTIONSH;
		snp = np;
		if (np->primindex == 0)
		{
			type = NPUNKNOWN;
			np = contentsview(snp);
			if (np == NONODEPROTO) np = snp;
		}

		/* ignore artwork */
		if (type == NPART) continue;

		for(pp=np->firstportproto; pp!=NOPORTPROTO; pp=pp->nextportproto)
			pp->temp1 = 0;

		/* set network numbers from arcs and exports */
		for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
		{
			pp = pi->proto;
			if (snp != np)
			{
				pp = equivalentport(snp, pp, np);
				if (pp == NOPORTPROTO) continue;
			}
			if (pp->temp1 != 0) continue;
			pp->temp1 = pi->conarcinst->temp1;
			if (pp->temp1 == 0) continue;
			for(spt = np->firstportproto; spt != NOPORTPROTO; spt = spt->nextportproto)
				if (spt->network == pp->network) spt->temp1 = pp->temp1;
		}
		for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
		{
			pp = pe->proto;
			if (snp != np)
			{
				pp = equivalentport(snp, pp, np);
				if (pp == NOPORTPROTO) continue;
			}
			if (pp->temp1 != 0) continue;
			pp->temp1 = pe->exportproto->temp1;
			if (pp->temp1 == 0) continue;
			for(spt = np->firstportproto; spt != NOPORTPROTO; spt = spt->nextportproto)
				if (spt->network == pp->network) spt->temp1 = pp->temp1;
		}

		/* look for unconnected ports and assign new network numbers */
		for(pp=np->firstportproto; pp!=NOPORTPROTO; pp=pp->nextportproto)
		{
			if (pp->temp1 != 0) continue;

			/* Hack!!! ignore unconnected p-well ports, flag the others */
			if (namesame(pp->protoname, "P-well") != 0 &&
				namesame(pp->protoname, "Ntrans-P-well") != 0 &&
				namesame(pp->protoname, "P-welln") != 0)
			{
				count_unconn++;
				xprintf(f, "| Warning: no connection to node %s port %s at (%ld,%ld)\n",
					describenodeinst(ni), pp->protoname, ni->lowx, ni->lowy);
			}
			marea = parea = darea = dperim = 0;
			xprintf(f, "N %s 0 0 %g %g %g %g\n", sim_makenodename(sim_index, format),
				scaletodispunitsq(marea, DISPUNITMIC), scaletodispunitsq(parea, DISPUNITMIC),
					scaletodispunitsq(darea, DISPUNITMIC), scaletodispunit(dperim, DISPUNITMIC));
			pp->temp1 = sim_index++;
		}

		/* some static checking while we are at it */
		if ((type == NPSUBSTRATE && np->firstportproto->temp1 != sim_vdd) ||
			(type == NPWELL && np->firstportproto->temp1 != sim_gnd))
		{
			ttyputmsg(_("WARNING: node %s in facet %s not connected to proper supply"),
				describenodeinst(ni), describenodeproto(facet));
		}

		/* if it is a transistor, write the information */
		if (type == NPTRANMOS || type == NPTRADMOS || type == NPTRAPMOS || type == NPTRANS)
		{
			if (type == NPTRANS)
			{
				/* undefined transistor: look at its transistor type */
				var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, sch_transistortypekey);
				if (var != NOVARIABLE)
				{
					str = (char *)var->addr;
					if (namesamen(str, "nmos", 4) == 0) type=NPTRANMOS; else
						if (namesamen(str, "dmos", 4) == 0) type=NPTRADMOS; else
							if (namesamen(str, "pmos", 4) == 0) type=NPTRAPMOS;
				}

				/* gate is port 0, source is port 1, drain is port 2 */
				p = np->firstportproto;    gate = p->temp1;
				p = p->nextportproto;      source = p->temp1;
				p = p->nextportproto;      drain = p->temp1;
			} else
			{
				/* gate is port 0 or 2, source is port 1, drain is port 3 */
				p = np->firstportproto;                gate = p->temp1;
				p = p->nextportproto;                  source = p->temp1;
				p = p->nextportproto->nextportproto;   drain = p->temp1;
			}

			if (type == NPTRANMOS) xprintf(f, "e"); else
				if (type == NPTRADMOS) xprintf(f, "d"); else
					if (type == NPTRAPMOS) xprintf(f, "p"); else
						xprintf(f, "U");
			xprintf(f, " %s", sim_makenodename(gate, format));
			xprintf(f, " %s", sim_makenodename(source, format));
			xprintf(f, " %s", sim_makenodename(drain, format));

			/* determine size of transistor */
			transistorsize(ni, &length, &width);
			if (length < 0) length = 0;
			if (width < 0) width = 0;
			xprintf(f, " %g %g r 0 0 %g\n", scaletodispunit(abs(length), DISPUNITMIC),
				scaletodispunit(abs(width), DISPUNITMIC),
					scaletodispunitsq(abs(length*width), DISPUNITMIC));

			/* approximate source and drain diffusion capacitances */
			xprintf(f, "N %s", sim_makenodename(source, format));
			xprintf(f, " 0 0 0 0 %g %g\n", scaletodispunitsq(abs(length*width), DISPUNITMIC),
				scaletodispunit(abs(width), DISPUNITMIC));
			xprintf(f, "N %s", sim_makenodename(drain, format));
			xprintf(f, " 0 0 0 0 %g %g\n",scaletodispunitsq(abs(length*width), DISPUNITMIC),
				scaletodispunit(abs(width), DISPUNITMIC));
			continue;
		}

		/* recurse on nonprimitive nodeinst */
		if (np->primindex == 0)
		{
			sim_simprint(np, f, format);
		}
	}
}

void sim_simprintcosmos(NODEPROTO *facet, FILE *f, INTSML format)
{
	REGISTER PORTPROTO *gate, *source, *drain;
	REGISTER INTBIG type;
	INTBIG darea, dperim, parea, pperim, marea, mperim, length, width;
	REGISTER INTBIG lambda, lambda_2;
	REGISTER ARCINST *ai;
	REGISTER PORTPROTO *pp, *spt;
	REGISTER PORTARCINST *pi;
	REGISTER PORTEXPINST *pe;
	REGISTER NODEINST *ni;
	REGISTER NODEPROTO *np, *snp;
	REGISTER VARIABLE *var;
	REGISTER char *str;

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

	xprintf(f, "| cell %s\n", facet->cell->cellname);

	lambda = lambdaoffacet(facet); /* setup lambda for scaling */
	lambda_2 = lambda*lambda;

	/* reset the arcinst node values */
	for(ai = facet->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
		ai->temp1 = 0;

	/* set every arcinst to a global node number (from inside or outside) */
	for(ai = facet->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
	{
		if (ai->temp1 != 0) continue;

		/* see if this arcinst is connected to a port */
		for(pp=facet->firstportproto; pp!=NOPORTPROTO; pp=pp->nextportproto)
		{
			if (pp->network != ai->network) continue;
			darea = dperim = parea = pperim = marea = mperim = 0;
			sim_prop(ai, (INTBIG)pp->temp1, &darea, &dperim, &parea, &pperim, &marea, &mperim);
			if (pp->temp1 != sim_vdd && pp->temp1 != sim_gnd &&
				(mperim != 0 || pperim != 0 || dperim != 0))
					xprintf(f, "N %s %ld %ld %ld %ld %ld %ld\n", sim_makenodename(pp->temp1, format),
						darea/lambda_2, dperim/lambda, parea/lambda_2, pperim/lambda,
							marea/lambda_2, mperim/lambda);
			break;
		}

		/* if not on a portinst, this is an internal node */
		if (pp == NOPORTPROTO)
		{
			darea = dperim = parea = pperim = marea = mperim = 0;
			sim_prop(ai, sim_index, &darea, &dperim, &parea, &pperim, &marea, &mperim);
			if (mperim != 0 || pperim != 0 || dperim != 0)
				xprintf(f, "N %s %ld %ld %ld %ld %ld %ld\n", sim_makenodename(sim_index, format),
					darea/lambda_2, dperim/lambda, parea/lambda_2, pperim/lambda, marea/lambda_2,
						mperim/lambda);
			sim_index++;
		}
	}

	/* Test each arc for attributes */
	for(ai = facet->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
	{
		var = getvalkey((INTBIG)ai, VARCINST, -1, sim_cosmos_attribute);
		if (var != NOVARIABLE)
		{
			if (var->type == VINTEGER)
				xprintf(f, "A %s Sim:%ld\n", sim_makenodename(ai->temp1, format), var->addr); else
					xprintf(f, "A %s Sim:%s\n", sim_makenodename(ai->temp1, format), (char *)var->addr);
		}
	}

	/* look at every nodeinst in the facet */
	for(ni = facet->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		/* initialize network numbers on every portinst on this nodeinst */
		np = ni->proto;
		type = (np->userbits&NFUNCTION) >> NFUNCTIONSH;
		snp = np;
		if (np->primindex == 0)
		{
			type = NPUNKNOWN;
			np = contentsview(snp);
			if (np == NONODEPROTO) np = snp;
		}

		/* ignore artwork */
		if (type == NPART) continue;

		for(pp=np->firstportproto; pp!=NOPORTPROTO; pp=pp->nextportproto)
			pp->temp1 = 0;

		/* set network numbers from arcs and exports */
		for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
		{
			pp = pi->proto;
			if (snp != np)
			{
				pp = equivalentport(snp, pp, np);
				if (pp == NOPORTPROTO) continue;
			}
			if (pp->temp1 != 0) continue;
			pp->temp1 = pi->conarcinst->temp1;
			if (pp->temp1 == 0) continue;
			for(spt = np->firstportproto; spt != NOPORTPROTO; spt = spt->nextportproto)
				if (spt->network == pp->network) spt->temp1 = pp->temp1;
		}
		for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
		{
			pp = pe->proto;
			if (snp != np)
			{
				pp = equivalentport(snp, pp, np);
				if (pp == NOPORTPROTO) continue;
			}
			if (pp->temp1 != 0) continue;
			pp->temp1 = pe->exportproto->temp1;
			if (pp->temp1 == 0) continue;
			for(spt = np->firstportproto; spt != NOPORTPROTO; spt = spt->nextportproto)
				if (spt->network == pp->network) spt->temp1 = pp->temp1;
		}

		/* look for unconnected ports and assign new network numbers */
		for(pp=np->firstportproto; pp!=NOPORTPROTO; pp=pp->nextportproto)
		{
			if (pp->temp1 != 0) continue;

			/* Hack!!! ignore unconnected well and select ports, flag the others */
			if (namesame(pp->protoname, "well") != 0 &&
				namesame(pp->protoname, "select") != 0)
			{
				count_unconn++;
				xprintf(f, "| Warning: no connection to node %s port %s at (%ld,%ld)\n",
					describenodeinst(ni), pp->protoname, ni->lowx, ni->lowy);

				/* Calculate area and perimiter of nodes! */
				darea = dperim = parea = pperim = marea = mperim = 0;
				xprintf(f, "N %s %ld %ld %ld %ld %ld %ld\n",
					sim_makenodename(sim_index, format), darea/lambda_2, dperim/lambda,
						parea/lambda_2, pperim/lambda, marea/lambda_2, mperim/lambda);
				pp->temp1 = sim_index++;
			}
		}

		/* some static checking while we are at it */
		if ((type == NPSUBSTRATE && np->firstportproto->temp1 != sim_vdd) ||
			(type == NPWELL && np->firstportproto->temp1 != sim_gnd))
		{
			ttyputmsg(_("WARNING: node %s in facet %s not connected to proper supply"),
				describenodeinst(ni), describenodeproto(facet));
		}

		/* if it is a transistor, write the information */
		if (type == NPTRANMOS || type == NPTRADMOS || type == NPTRAPMOS || type == NPTRANS)
		{
			if (type == NPTRANS)
			{
				/* undefined transistor: look at its transistor type */
				var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, sch_transistortypekey);
				if (var != NOVARIABLE)
				{
					str = (char *)var->addr;
					if (namesamen(str, "nmos", 4) == 0) type=NPTRANMOS; else
						if (namesamen(str, "dmos", 4) == 0) type=NPTRADMOS; else
							if (namesamen(str, "pmos", 4) == 0) type=NPTRAPMOS;
				}

				/* gate is port 0, source is port 1, drain is port 2 */
				gate = np->firstportproto;
				source = gate->nextportproto;
				drain = source->nextportproto;
			} else
			{
				/* gate is port 0 or 2, source is port 1, drain is port 3 */
				gate = np->firstportproto;
				source = gate->nextportproto;
				drain = source->nextportproto->nextportproto;
			}

			if (type == NPTRANMOS)
			{			/* nmos processes don't have a 'c' */
				if (strchr(el_curtech->techname, 'c') != 0) xprintf(f, "n");
				else xprintf(f, "e");
			} else
				if (type == NPTRADMOS) xprintf(f, "d"); else
					if (type == NPTRAPMOS) xprintf(f, "p"); else
						xprintf(f, "U");
			xprintf(f, " %s", sim_makenodename(gate->temp1, format));
			xprintf(f, " %s", sim_makenodename(source->temp1, format));
			xprintf(f, " %s", sim_makenodename(drain->temp1, format));

			/* determine size of transistor */
			transistorsize(ni, &length, &width);
			if (length < 0) length = 0;
			if (width < 0) width = 0;
			xprintf(f, " %ld %ld %ld %ld", abs(length)/lambda, abs(width)/lambda, ni->lowx, ni->lowy);

			/* see if there is a strength mentioned on this node */
			for (pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
			{
				var = getvalkey((INTBIG)pi, VPORTARCINST, -1, sim_cosmos_attribute);
				if (var != NOVARIABLE)
				{
					if (pi->proto->network == source->network) xprintf(f, " s="); else
						if (pi->proto->network == drain->network) xprintf(f, " d="); else
							if (pi->proto->network == gate->network) xprintf(f, " g="); else
								ttyputerr(_("Bad cosmos gate/source/drain transistor variable %s"),
									(char *)var->addr);
					if (var->type == VINTEGER) xprintf(f, "Sim:%ld", var->addr); else
						if (var->type == VSTRING) xprintf(f, "Sim:%s", (char *)var->addr); else
							ttyputerr(_("Bad cosmos attribute type"));
				}
			}
			var = getvalkey((INTBIG)ni, VNODEINST, -1, sim_cosmos_attribute);
			if (var != NOVARIABLE)
			{
				if (var->type == VINTEGER) xprintf(f, " g=Sim:%ld", var->addr);
					else xprintf(f, " g=Sim:%s", (char *)var->addr);
			}
			xprintf(f, "\n");

			/* approximate source and drain diffusion capacitances */
			xprintf(f, "N %s", sim_makenodename(source->temp1, format));
			xprintf(f, " %ld %ld 0 0 0 0\n", abs(length*width)/lambda_2, abs(width)/lambda);
			xprintf(f, "N %s", sim_makenodename(drain->temp1, format));
			xprintf(f, " %ld %ld 0 0 0 0\n", abs(length*width)/lambda_2, abs(width)/lambda);
			continue;
		}

		/* recurse on nonprimitive nodeinst */
		if (np->primindex == 0)
		{
			sim_simprintcosmos(np, f, format);
		}
	}
}

/*
 * routine to generate the name of simulation node "node" and return the
 * string name (either a numeric node number if internal, an export
 * name if at the top level, or "power", "ground", etc. if special).  The
 * "format" is the particular simuator being used
 */
char *sim_makenodename(INTBIG node, INTSML format)
{
	static char line[50];
	REGISTER PORTPROTO *pp;

	/* values above a threshold are internal and printed as numbers */
	if (node >= sim_namethresh)
	{
		(void)sprintf(line, "%ld", node);
		return(line);
	}

	/* test for special names */
	if (node == sim_vdd) return("vdd");
	if (node == sim_gnd) return("gnd");
	if (node == sim_phi1h)
	{
		if (format == RSIM) return("phi1h"); else return("clk1");
	}
	if (node == sim_phi1l) return("phi1l");
	if (node == sim_phi2h)
	{
		if (format == RSIM) return("phi2h"); else return("clk2");
	}
	if (node == sim_phi2l) return("phi2l");

	/* see if it is an export name */
	for(pp = sim_simnt->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
		if (pp->temp1 == node && pp->temp2 >= 0) break;
	if (pp != NOPORTPROTO) return(pp->protoname);

	/* this is an error, but can be handled anyway */
	ttyputmsg(_("simsim: should have real name for node %ld"), node);
	(void)sprintf(line, "%ld", node);
	return(line);
}

void sim_prop(ARCINST *ai, INTBIG indx, INTBIG *darea, INTBIG *dperim, INTBIG *parea,
	INTBIG *pperim, INTBIG *marea, INTBIG *mperim)
{
	REGISTER ARCINST *oar;
	REGISTER NODEINST *ni;
	REGISTER PORTARCINST *pi;
	REGISTER PORTPROTO *pp;
	REGISTER INTSML i;
	INTBIG length, width;

	/* see if this arcinst is already on a path */
	if (ai->temp1 != 0) return;
	ai->temp1 = indx;
	if (((ai->proto->userbits&AFUNCTION) >> AFUNCTIONSH) == APNONELEC)
		return;

	/* calculate true length and width of arc */
	width = ai->width - arcwidthoffset(ai);
	length = ai->length;
	if ((ai->userbits&NOEXTEND) != 0)
	{
		if ((ai->userbits&NOTEND0) == 0) length += width/2;
		if ((ai->userbits&NOTEND1) == 0) length += width/2;
	} else length += width;

	/* sum up area and perimeter to total */
	switch ((ai->proto->userbits&AFUNCTION) >> AFUNCTIONSH)
	{
		case APMETAL1:   case APMETAL2:   case APMETAL3:
		case APMETAL4:   case APMETAL5:   case APMETAL6:
			*marea += length*width;
			*mperim += 2 * (length + width);
			break;
		case APPOLY1:    case APPOLY2:    case APPOLY3:
			*parea += length*width;
			*pperim += 2 * (length + width);
			break;
		case APDIFF:     case APDIFFP:    case APDIFFN:
		case APDIFFS:    case APDIFFW:
			*darea += length*width;
			*dperim += 2 * (length + width);
			break;
	}

	/* recursively set all arcs and nodes touching this */
	for(i=0; i<2; i++)
	{
		if ((ai->end[i].portarcinst->proto->userbits&PORTISOLATED) != 0) continue;
		ni = ai->end[i].nodeinst;
		for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
		{
			/* select an arcinst that has not been examined */
			pp = pi->proto;
			oar = pi->conarcinst;

			/* see if the two ports connect electrically */
			if (ai->end[i].portarcinst->proto->network != pp->network) continue;

			/* recurse on the nodes of this arcinst */
			sim_prop(oar, indx, darea, dperim, parea, pperim, marea, mperim);
		}
	}
}

#endif  /* SIMAID - at top */
