/*
 * Electric(tm) VLSI Design System
 *
 * File: vhdl.c
 * This is the main file for the VHDL front-end compiler for generating
 * input files for the ALS Design System and QUISC (Silicon Compiler)
 * Written by: Andrew R. Kostiuk, Queen's University
 * Modified 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 VHDLAID

#include "global.h"
#include "efunction.h"
#include "vhdl.h"
#include "tecschem.h"
#include "edialogs.h"

#define	MAXINPUTS	30		/* maximum inputs to logic gate */

/* special codes during VHDL generation */
#define BLOCKNORMAL   0		/* ordinary block */
#define BLOCKMOSTRAN  1		/* a MOS transistor */
#define BLOCKBUFFER   2		/* a buffer */
#define BLOCKPOSLOGIC 3		/* an and, or, xor */
#define BLOCKINVERTER 4		/* an inverter */
#define BLOCKNAND     5		/* a nand */
#define BLOCKNOR      6		/* a nor */
#define BLOCKXNOR     7		/* an xnor */
#define BLOCKFLOPDS   8		/* a settable D flip-flop */
#define BLOCKFLOPDR   9		/* a resettable D flip-flop */
#define BLOCKFLOPTS  10		/* a settable T flip-flop */
#define BLOCKFLOPTR  11		/* a resettable T flip-flop */
#define BLOCKFLOP    12		/* a general flip-flop */

/* the VHDL compiler aid table */
static KEYWORD vhdloutputopt[] =
{
	{"netlisp",     0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"als",         0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"quisc",       0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"silos",       0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"rsim",        0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
static COMCOMP vhdloutputp = {vhdloutputopt, NOTOPLIST, NONEXTLIST, NOPARAMS,
	NOBACKUP, INPUTOPT, " \t", "VHDL compiler output format", 0};
static COMCOMP vhdllibraryp = {NOKEYWORD, topoflibs, nextlibs, NOPARAMS,
	NOBACKUP, INPUTOPT, " \t", "Behavioral library to use in VHDL compilation", 0};
static KEYWORD vhdlnopt[] =
{
	{"vhdl-on-disk",       0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"netlist-on-disk",    0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"external",           0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"warn",               0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"generate-equations", 0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
static COMCOMP vhdlnp = {vhdlnopt, NOTOPLIST, NONEXTLIST, NOPARAMS,
	NOBACKUP, INPUTOPT, " \t", "Negating action", 0};
static KEYWORD vhdlsetopt[] =
{
	{"output-format",      1,{&vhdloutputp,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"library",            1,{&vhdllibraryp,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"external",           0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"warn",               0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"vhdl-on-disk",       0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"netlist-on-disk",    0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"not",                1,{&vhdlnp,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"generate-equations", 0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"make-vhdl",          0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"compile-now",        0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
COMCOMP vhdl_comp = {vhdlsetopt, NOTOPLIST, NONEXTLIST, NOPARAMS, NOBACKUP,
	0, " \t", "VHDL compiler action", 0};

char	vhdl_delimiterstr[] = "&'()*+,-./:;<=>|";
char	vhdl_doubledelimiterstr[] = "=>..**:=/=>=<=<>";

static VKEYWORD vhdl_keywords[] =
{
	{"abs",				KEY_ABS},
	{"after",			KEY_AFTER},
	{"alias",			KEY_ALIAS},
#ifndef VHDL50
	{"all",				KEY_ALL},
#endif
	{"and",				KEY_AND},
#ifdef VHDL50
	{"architectural",	KEY_ARCHITECTURAL},
#else
	{"architecture",	KEY_ARCHITECTURE},
#endif
	{"array",			KEY_ARRAY},
	{"assertion",		KEY_ASSERTION},
	{"attribute",		KEY_ATTRIBUTE},
	{"begin",			KEY_BEGIN},
	{"behavioral",		KEY_BEHAVIORAL},
	{"body",			KEY_BODY},
	{"case",			KEY_CASE},
	{"component",		KEY_COMPONENT},
	{"connect",			KEY_CONNECT},
	{"constant",		KEY_CONSTANT},
	{"convert",			KEY_CONVERT},
	{"dot",				KEY_DOT},
	{"downto",			KEY_DOWNTO},
	{"else",			KEY_ELSE},
	{"elsif",			KEY_ELSIF},
	{"end",				KEY_END},
	{"entity",			KEY_ENTITY},
	{"exit",			KEY_EXIT},
	{"for",				KEY_FOR},
	{"function",		KEY_FUNCTION},
	{"generate",		KEY_GENERATE},
	{"generic",			KEY_GENERIC},
	{"if",				KEY_IF},
	{"in",				KEY_IN},
	{"inout",			KEY_INOUT},
	{"is",				KEY_IS},
	{"linkage",			KEY_LINKAGE},
	{"loop",			KEY_LOOP},
#ifndef VHDL50
	{"map",				KEY_MAP},
#endif
	{"mod",				KEY_MOD},
	{"nand",			KEY_NAND},
	{"next",			KEY_NEXT},
	{"nor",				KEY_NOR},
	{"not",				KEY_NOT},
	{"null",			KEY_NULL},
	{"of",				KEY_OF},
#ifndef VHDL50
	{"open",			KEY_OPEN},
#endif
	{"or",				KEY_OR},
	{"others",			KEY_OTHERS},
	{"out",				KEY_OUT},
	{"package",			KEY_PACKAGE},
	{"port",			KEY_PORT},
	{"range",			KEY_RANGE},
	{"record",			KEY_RECORD},
	{"rem",				KEY_REM},
	{"report",			KEY_REPORT},
	{"resolve",			KEY_RESOLVE},
	{"return",			KEY_RETURN},
	{"severity",		KEY_SEVERITY},
	{"signal",			KEY_SIGNAL},
	{"standard",		KEY_STANDARD},
	{"static",			KEY_STATIC},
	{"subtype",			KEY_SUBTYPE},
	{"then",			KEY_THEN},
	{"to",				KEY_TO},
	{"type",			KEY_TYPE},
	{"units",			KEY_UNITS},
	{"use",				KEY_USE},
	{"variable",		KEY_VARIABLE},
	{"when",			KEY_WHEN},
	{"while",			KEY_WHILE},
	{"with",			KEY_WITH},
	{"xor",				KEY_XOR}
};

IDENTTABLE        *vhdl_identtable = 0;
TOKENLIST         *vhdl_tliststart, *vhdl_tlistend;
INTBIG             vhdl_externentities;		/* enternal entities flag */
INTBIG             vhdl_warnflag;			/* warning flag, TRUE warn */
INTSML             vhdl_genequations;		/* nonzero to generate VHDL equations */
INTBIG             vhdl_vhdlondisk;			/* cached key for "VHDL_vhdl_on_disk" */
INTBIG             vhdl_netlistondisk;		/* cached key for "VHDL_netlist_on_disk" */
INTBIG             vhdl_target;				/* Current simulator */
LIBRARY           *vhdl_lib;				/* behavioral library */
FILE              *vhdl_fp;					/* current file */
VARIABLE          *vhdl_var;				/* current variable */
INTBIG             vhdl_linecount;			/* current line number */
NODEPROTO         *vhdl_facet;				/* current output facet */
extern INTBIG      vhdl_errorcount;
extern SYMBOLLIST *vhdl_gsymbols;
AIDENTRY          *vhdl_aid;
void              *vhdl_componentarray = 0;
void              *vhdl_stringarray;
UNRESLIST         *vhdl_unresolved_list = 0;

/* VHDL Options */
DIALOGITEM vhdl_optionsdialogitems[] =
{
 /*  1 */ {0, {336,228,360,292}, BUTTON, "OK"},
 /*  2 */ {0, {300,228,324,292}, BUTTON, "Cancel"},
 /*  3 */ {0, {308,4,324,166}, CHECK, "VHDL stored in facet"},
 /*  4 */ {0, {332,4,348,179}, CHECK, "Netlist stored in facet"},
 /*  5 */ {0, {28,8,224,295}, SCROLL, ""},
 /*  6 */ {0, {236,4,252,179}, MESSAGE, "VHDL for primitive:"},
 /*  7 */ {0, {260,4,276,179}, MESSAGE, "VHDL for negated primitive:"},
 /*  8 */ {0, {236,184,252,295}, EDITTEXT, ""},
 /*  9 */ {0, {260,184,276,295}, EDITTEXT, ""},
 /* 10 */ {0, {8,8,24,183}, MESSAGE, "Schematics primitives:"},
 /* 11 */ {0, {292,8,293,295}, DIVIDELINE, ""}
};
DIALOG vhdl_optionsdialog = {{50,75,419,379}, "VHDL Options", 11, vhdl_optionsdialogitems};

/* prototypes for local routines */
void      vhdl_addportlist(NODEINST*, NODEPROTO*, INTSML);
void      vhdl_addrealports(NODEINST *ni, INTSML special);
void      vhdl_addstring(char*);
void      vhdl_addtheseports(NODEINST*, NODEPROTO*, INTBIG, char*);
void      vhdl_compile(NODEPROTO*);
void      vhdl_convertfacet(NODEPROTO*, INTSML);
INTBIG    vhdl_endinput(void);
INTBIG    vhdl_endoutput(void);
void      vhdl_freescannermemory(void);
INTSML    vhdl_generatevhdl(NODEPROTO *np);
UINTBIG   vhdl_getfacetdate(NODEPROTO *np);
void      vhdl_getnetlistfactors(char**, char**);
INTBIG    vhdl_getnextline(char*);
INTBIG    vhdl_identfirsthash(char*);
INTBIG    vhdl_identsecondhash(char*, INTBIG);
VKEYWORD *vhdl_iskeyword(char*, INTBIG);
void      vhdl_makechartoken(char, INTBIG, INTBIG);
void      vhdl_makedecimaltoken(char*, INTBIG, INTBIG, INTBIG);
void      vhdl_makeidenttoken(char*, INTBIG, INTBIG, INTBIG);
void      vhdl_makekeytoken(VKEYWORD*, INTBIG, INTBIG);
INTSML    vhdl_morerecentcontents(NODEPROTO*, UINTBIG);
void      vhdl_makestrtoken(char*, INTBIG, INTBIG, INTBIG);
void      vhdl_maketoken(INTBIG, INTBIG, INTBIG);
void      vhdl_optionsdlog(void);
char     *vhdl_primname(NODEINST*, INTSML*);
INTSML    vhdl_propernetlist(NODEPROTO*, char*);
void      vhdl_scanner(void);
INTBIG    vhdl_startinput(NODEPROTO*, INTSML, INTSML, char**, UINTBIG*);
INTSML    vhdl_startoutput(NODEPROTO*, INTSML, INTSML, char**);
char      vhdl_toupper(char);

void vhdl_init(INTBIG *argc, char *argv[], AIDENTRY *thisaid)
{
	/* only initialize during pass 1 */
	if (thisaid == NOAID || thisaid == 0) return;

	vhdl_aid = thisaid;
	vhdl_target = TARGET_QUISC;
	vhdl_externentities = TRUE;
	vhdl_warnflag = FALSE;
	vhdl_genequations = FALSE;
	vhdl_lib = NOLIBRARY;
	vhdl_vhdlondisk = makekey("VHDL_vhdl_on_disk");
	vhdl_netlistondisk = makekey("VHDL_netlist_on_disk");
	DiaDeclareHook("vhdlopt", &vhdl_comp, vhdl_optionsdlog);
}

void vhdl_done(void)
{
	if (vhdl_componentarray != 0) killstringarray(vhdl_componentarray);
	vhdl_freescannermemory();
	if (vhdl_identtable != 0) efree((char *)vhdl_identtable);
	vhdl_freeparsermemory();
	vhdl_freesemantic();
	vhdl_freeunresolvedlist(&vhdl_unresolved_list);
}

INTSML vhdl_set(INTSML count, char *par[])
{
	INTBIG l;
	char *pp;
	REGISTER NODEPROTO *np;

	if (count == 0) return(0);
	l = strlen(pp = par[0]);

	if (namesamen(pp, "make-vhdl", l) == 0)
	{
		if (count >= 2)
		{
			np = getnodeproto(par[1]);
			if (np == NONODEPROTO)
			{
				ttyputerr("No facet named %s", par[1]);
				return(1);
			}
			if (np->primindex != 0)
			{
				ttyputerr("Can only convert facets to VHDL, not primitives");
				return(1);
			}
		} else
		{
			np = getcurfacet();
			if (np == NONODEPROTO)
			{
				ttyputerr("No current facet");
				return(1);
			}
		}
		begintraversehierarchy();
		vhdl_convertfacet(np, 1);

		return(0);
	}

	if (namesamen(pp, "compile-now", l) == 0)
	{
		np = getcurfacet();
		if (np == NONODEPROTO)
		{
			ttyputerr("Must be editing a cell");
			return(1);
		}

		vhdl_compile(np);
		return(0);
	}

	if (namesamen(pp, "library", l) == 0)
	{
		if (count <= 1)
		{
			ttyputerr("Usage: tellaid VHDL-compiler library LIBRARY");
			return(0);
		}
		vhdl_lib = getlibrary(par[1]);
		if (vhdl_lib == NOLIBRARY)
		{
			ttyputerr("Cannot find library %s", par[1]);
			return(0);
		}
		ttyputverbose("Library %s will be used for behavioral descriptions", par[1]);
		return(0);
	}

	if (namesamen(pp, "vhdl-on-disk", l) == 0)
	{
		setvalkey((INTBIG)vhdl_aid, VAID, vhdl_vhdlondisk, 1, VINTEGER);
		ttyputverbose("VHDL will be kept in separate disk files");
		return(0);
	}

	if (namesamen(pp, "netlist-on-disk", l) == 0)
	{
		setvalkey((INTBIG)vhdl_aid, VAID, vhdl_netlistondisk, 1, VINTEGER);
		ttyputverbose("Netlists will be kept in separate disk files");
		return(0);
	}

	if (namesamen(pp, "warn", l) == 0)
	{
		vhdl_warnflag = TRUE;
		ttyputverbose("VHDL compiler will display warnings");
		return(0);
	}

	if (namesamen(pp, "external", l) == 0)
	{
		vhdl_externentities = TRUE;
		ttyputverbose("VHDL compiler will allow external references");
		return(0);
	}

	if (namesamen(pp, "generate-equations", l) == 0)
	{
		vhdl_genequations = TRUE;
		ttyputverbose("VHDL generation will use equations instead of logic block calls");
		return(0);
	}

	if (namesamen(pp, "not", l) == 0)
	{
		if (count <= 1)
		{
			ttyputerr("Usage: tellaid VHDL-compiler not OPTION");
			return(0);
		}
		l = strlen(pp = par[1]);
		if (namesamen(pp, "warn", l) == 0)
		{
			vhdl_warnflag = FALSE;
			ttyputverbose("VHDL compiler will not display warnings");
			return(0);
		}
		if (namesamen(pp, "external", l) == 0)
		{
			vhdl_externentities = FALSE;
			ttyputverbose("VHDL compiler will not allow external references");
			return(0);
		}
		if (namesamen(pp, "vhdl-on-disk", l) == 0)
		{
			setvalkey((INTBIG)vhdl_aid, VAID, vhdl_vhdlondisk, 0, VINTEGER);
			ttyputverbose("VHDL will be kept in facets");
			return(0);
		}
		if (namesamen(pp, "netlist-on-disk", l) == 0)
		{
			setvalkey((INTBIG)vhdl_aid, VAID, vhdl_netlistondisk, 0, VINTEGER);
			ttyputverbose("Netlists will be kept in facets");
			return(0);
		}
		if (namesamen(pp, "generate-equations", l) == 0)
		{
			vhdl_genequations = FALSE;
			ttyputverbose("VHDL generation will use logic block calls instead of equations");
			return(0);
		}
		ttyputerr("Invalid VHDL compiler NOT option: %s", pp);
		return(0);
	}

	if (namesamen(pp, "output-format", l) == 0)
	{
		if (count <= 1)
		{
			ttyputerr("Usage: tellaid VHDL-compiler output FORMAT");
			return(0);
		}
		l = strlen(pp = par[1]);
		if (namesamen(pp, "netlisp", l) == 0)
		{
			vhdl_target = TARGET_NETLISP;
			ttyputverbose("VHDL compiles to net lisp");
			return(0);
		}
		if (namesamen(pp, "als", l) == 0 && l >= 1)
		{
			vhdl_target = TARGET_ALS;
			ttyputverbose("VHDL compiles to ALS simulation");
			return(0);
		}
		if (namesamen(pp, "quisc", l) == 0 && l >= 3)
		{
			vhdl_target = TARGET_QUISC;
			ttyputverbose("VHDL compiles to QUISC place-and-route");
			return(0);
		}
		if (namesamen(pp, "silos", l) == 0)
		{
			vhdl_target = TARGET_SILOS;
			ttyputverbose("VHDL compiles to SILOS simulation");
			return(0);
		}
		if (namesamen(pp, "rsim", l) == 0)
		{
			vhdl_target = TARGET_RSIM;
			ttyputverbose("VHDL compiles to RSIM simulation");
			return(0);
		}
		ttyputerr("Invalid VHDL compiler output: %s", pp);
		return(0);
	}

	ttyputerr("Unknown VHDL compiler option: %s", pp);
	return(0);
}

void vhdl_slice(void)
{
	REGISTER NODEPROTO *np;

	np = getcurfacet();
	if (np == NONODEPROTO)
	{
		ttyputerr("Must be editing a cell");
		aidturnoff(vhdl_aid, 0);
		return;
	}

	vhdl_compile(np);

	aidturnoff(vhdl_aid, 0);
	ttyputmsg("VHDL compiler turned off");
}

/*
 * make requests of the VHDL aid:
 *  "begin-vhdl-input" TAKES: facet, string for source  RETURNS: nonzero on error
 *  "begin-netlist-input" TAKES: facet, filetype, string for source
 *                        RETURNS: nonzero on error
 *  "want-netlist-input" TAKES: facet, filetype
 *                       RETURNS: nonzero on error
 *  "get-line" TAKES: buffer address  RETURNS: line number, zero on EOF
 *  "end-input"
 *
 *  "begin-vhdl-output" TAKES: facet, string for source  RETURNS: output name (zero on error)
 *  "begin-netlist-output" TAKES: facet  RETURNS: output name (zero on error)
 *  "put-line" TAKES: buffer address
 *  "end-output"
 */
INTBIG vhdl_request(char *command, va_list ap)
{
	INTBIG i;
	INTSML vhdlondisk, netlistondisk;
	UINTBIG netlistdate, vhdldate;
	char *intended;
	REGISTER VARIABLE *var;
	REGISTER INTBIG arg1, arg2, arg3;
	REGISTER NODEPROTO *np, *orignp;
	REGISTER VIEW *view;

	var = getvalkey((INTBIG)vhdl_aid, VAID, VINTEGER, vhdl_vhdlondisk);
	if (var == NOVARIABLE) vhdlondisk = 0; else
		vhdlondisk = (INTSML)var->addr;
	var = getvalkey((INTBIG)vhdl_aid, VAID, VINTEGER, vhdl_netlistondisk);
	if (var == NOVARIABLE) netlistondisk = 0; else
		netlistondisk = (INTSML)var->addr;

	if (namesame(command, "begin-vhdl-input") == 0)
	{
		/* get the arguments (1=facet, 2=source) */
		arg1 = va_arg(ap, INTBIG);
		arg2 = va_arg(ap, INTBIG);

		return(vhdl_startinput((NODEPROTO *)arg1, FILETYPEVHDL, vhdlondisk,
			(char **)arg2, &vhdldate));
	}

	if (namesame(command, "begin-netlist-input") == 0)
	{
		/* get the arguments (1=facet, 2=filetype, 3=source) */
		arg1 = va_arg(ap, INTBIG);
		arg2 = va_arg(ap, INTBIG);
		arg3 = va_arg(ap, INTBIG);

		return(vhdl_startinput((NODEPROTO *)arg1, (INTSML)arg2, netlistondisk,
			(char **)arg3, &netlistdate));
	}

	if (namesame(command, "get-line") == 0)
	{
		/* get the arguments (1=buffer) */
		arg1 = va_arg(ap, INTBIG);

		return(vhdl_getnextline((char *)arg1));
	}

	if (namesame(command, "end-input") == 0)
	{
		return(vhdl_endinput());
	}

	if (namesame(command, "want-netlist-input") == 0)
	{
		/* get the arguments (1=facet, 2=filetype) */
		arg1 = va_arg(ap, INTBIG);
		arg2 = va_arg(ap, INTBIG);

		switch (arg2)
		{
			case FILETYPEQUISC:   view = el_netlistquiscview;   vhdl_target = TARGET_QUISC;   break;
			case FILETYPEALS:     view = el_netlistalsview;     vhdl_target = TARGET_ALS;     break;
			case FILETYPENETLISP: view = el_netlistnetlispview; vhdl_target = TARGET_NETLISP; break;
			case FILETYPESILOS:   view = el_netlistsilosview;   vhdl_target = TARGET_SILOS;   break;
			case FILETYPERSIM:    view = el_netlistrsimview;    vhdl_target = TARGET_RSIM;    break;
		}

		/* must have a valid netlist */
		orignp = (NODEPROTO *)arg1;
		i = vhdl_startinput(orignp, (INTSML)arg2, netlistondisk, &intended, &netlistdate);
		vhdl_endinput();
		if (i == 0)
		{
			/* got netlist: make sure it is more recent than any VHDL or layout */
			if (orignp->cellview != view)
			{
				/* determine date of associated VHDL */
				if (vhdl_startinput(orignp, FILETYPEVHDL, vhdlondisk, &intended,
					&vhdldate) == 0)
				{
					if (netlistdate < vhdldate) i = 1;
				}
				vhdl_endinput();

				/* if original is not VHDL, check all facets for recency */
				if (orignp->cellview != el_vhdlview && i == 0)
				{
					/* check all in cell for recency */
					for(np = orignp->cell->firstincell; np != NONODEPROTO; np = np->nextincell)
						if (netlistdate < np->revisiondate)
					{
						i = 1;
						break;
					}
					if (i == 0) i = vhdl_morerecentcontents(orignp, netlistdate);
				}
			}
		}

		if (i != 0)
		{
			/* no valid netlist: look for VHDL */
			i = vhdl_startinput(orignp, FILETYPEVHDL, vhdlondisk, &intended, &vhdldate);
			vhdl_endinput();
			if (i == 0)
			{
				/* got VHDL: make sure it is more recent than any layout */
				if (orignp->cellview != el_vhdlview)
				{
					/* check all in cell for recency */
					for(np = orignp->cell->firstincell; np != NONODEPROTO; np = np->nextincell)
						if (vhdldate < np->revisiondate)
					{
						i = 1;
						break;
					}
					if (i == 0) i = vhdl_morerecentcontents(orignp, vhdldate);
				}
			}
			if (i != 0)
			{
				/* no valid VHDL: convert facet */
				np = (NODEPROTO *)arg1;
				begintraversehierarchy();
				vhdl_convertfacet(np, 0);
			}

			/* compile VHDL to netlist */
			vhdl_compile(getcurfacet());
		}
		return(0);
	}

	if (namesame(command, "begin-vhdl-output") == 0)
	{
		/* get the arguments (1=facet, 2=source) */
		arg1 = va_arg(ap, INTBIG);
		arg2 = va_arg(ap, INTBIG);

		return((INTBIG)vhdl_startoutput((NODEPROTO *)arg1, FILETYPEVHDL, vhdlondisk,
			(char **)arg2));
	}

	if (namesame(command, "put-line") == 0)
	{
		/* get the arguments */
		arg1 = va_arg(ap, INTBIG);
		arg2 = va_arg(ap, INTBIG);
		arg3 = va_arg(ap, INTBIG);

		vhdl_print((char *)arg1, (char *)arg2, (char *)arg3);
		return(0);
	}

	if (namesame(command, "end-output") == 0)
	{
		return(vhdl_endoutput());
	}
	return(1);
}

/*
 * routine returns 0 if all facets in the hierarchy below facet "np" are less recent
 * than "dateoffile".  Returns 1 if any more recent facet is found.
 */
INTSML vhdl_morerecentcontents(NODEPROTO *np, UINTBIG dateoffile)
{
	REGISTER NODEINST *ni;
	REGISTER NODEPROTO *subnp;

	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		subnp = ni->proto;
		if (subnp->primindex != 0) continue;
		if (subnp->cellview == el_iconview)
		{
			subnp = contentsview(subnp);
			if (subnp == NONODEPROTO) continue;
		}
		if (dateoffile < subnp->revisiondate) return(1);
		if (vhdl_morerecentcontents(subnp, dateoffile) != 0) return(1);
	}
	return(0);
}

/****************************** GENERALIZED VHDL/NET I/O ******************************/

/*
 * Routine to begin input of a file or facet of type "filetype".  If "diskflag" is nonzero,
 * use a disk file whose name is the cell name of facet "np".  If "diskflag" is zero, use
 * the facet with the appropriate view of "np".  Returns the name of the input source in
 * "intended" and its last modification date in "revisiondate".  Returns nonzero on error.
 */
INTBIG vhdl_startinput(NODEPROTO *np, INTSML filetype, INTSML diskflag, char **intended,
	UINTBIG *revisiondate)
{
	REGISTER NODEPROTO *onp;
	char *filename;
	REGISTER VIEW *view;

	if (np == NONODEPROTO || np->primindex != 0)
	{
		*intended = "NO CURRENT FACET";
		return(1);
	}

	switch (filetype)
	{
		case FILETYPEVHDL:    view = el_vhdlview;            break;
		case FILETYPEQUISC:   view = el_netlistquiscview;    break;
		case FILETYPEALS:     view = el_netlistalsview;      break;
		case FILETYPENETLISP: view = el_netlistnetlispview;  break;
		case FILETYPESILOS:   view = el_netlistsilosview;    break;
		case FILETYPERSIM:    view = el_netlistrsimview;     break;
	}

	vhdl_fp = 0;
	vhdl_var = NOVARIABLE;
	if (diskflag != 0)
	{
		/* attempt to open file */
		(void)initinfstr();
		(void)addstringtoinfstr("file ");
		(void)addstringtoinfstr(np->cell->cellname);
		*intended = returninfstr();
		if ((vhdl_fp = xopen(np->cell->cellname, filetype, "", &filename)) == 0) return(1);
		*revisiondate = filedate(filename);
	} else
	{
		/* find proper view of this cell */
		if (np->cellview == view) onp = np; else
			for(onp = np->cell->firstincell; onp != NONODEPROTO; onp = onp->nextincell)
				if (onp->cellview == view) break;
		if (onp == NONODEPROTO)
		{
			*intended = "CANNOT FIND FACET VIEW";
			return(1);
		}

		(void)initinfstr();
		(void)addstringtoinfstr("facet ");
		(void)addstringtoinfstr(describenodeproto(onp));
		*intended = returninfstr();
		vhdl_var = getvalkey((INTBIG)onp, VNODEPROTO, VSTRING|VISARRAY, el_facet_message);
		if (vhdl_var == NOVARIABLE) return(1);
		*revisiondate = onp->revisiondate;
	}
	vhdl_linecount = 0;
	return(0);
}

INTBIG vhdl_endinput(void)
{
	if (vhdl_fp != 0)
	{
		xclose(vhdl_fp);
		vhdl_fp = 0;
	}
	if (vhdl_var != NOVARIABLE) vhdl_var = NOVARIABLE;
	return(0);
}

/*
 * Routine to get the next line from the current input file/facet into the buffer
 * at "addr".  Returns the line number (1-based).  Returns zero on EOF.
 */
INTBIG vhdl_getnextline(char *addr)
{
	INTSML ret;

	if (vhdl_fp != 0)
	{
		ret = xfgets(addr, MAXVHDLLINE-1, vhdl_fp);
		if (ret != 0) return(0);
	} else if (vhdl_var != NOVARIABLE)
	{
		if (vhdl_linecount >= getlength(vhdl_var)) return(0);
		(void)strcpy(addr, ((char **)vhdl_var->addr)[vhdl_linecount]);
	} else return(0);
	vhdl_linecount++;
	return(vhdl_linecount);
}

/*
 * Routine to begin output of a file of type "filetype" (either VHDL or Netlist) that is either
 * to disk ("diskflag" nonzero) or to a facet ("diskflag" zero).  If it is to disk,
 * the file name is the cell name of facet "np".  If it is to a facet, that facet is the
 * appropriate view of "np".  Returns the intended destination in "indended".
 * Returns 1 on error, -1 if aborted.
 */
INTSML vhdl_startoutput(NODEPROTO *np, INTSML filetype, INTSML diskflag, char **intended)
{
	REGISTER char *prompt;
	char *truefile, *ext;
	REGISTER VIEW *view;

	switch (filetype)
	{
		case FILETYPEVHDL:    view = el_vhdlview;            ext = "vhdl";  break;
		case FILETYPEQUISC:   view = el_netlistquiscview;    ext = "sci";   break;
		case FILETYPEALS:     view = el_netlistalsview;      ext = "net";   break;
		case FILETYPENETLISP: view = el_netlistnetlispview;  ext = "net";   break;
		case FILETYPESILOS:   view = el_netlistsilosview;    ext = "sil";   break;
		case FILETYPERSIM:    view = el_netlistrsimview;     ext = "net";   break;
	}

	vhdl_fp = 0;
	vhdl_facet = NONODEPROTO;
	if (diskflag != 0)
	{
		/* attempt to open output file */
		(void)initinfstr();
		(void)addstringtoinfstr("file ");
		(void)addstringtoinfstr(np->cell->cellname);
		(void)addstringtoinfstr(".");
		(void)addstringtoinfstr(ext);
		*intended = returninfstr();
		if (namesame(ext, "vhdl") == 0) prompt = "VHDL File"; else
			prompt = "Netlist File";
		vhdl_fp = xcreate(&(*intended)[5], filetype, prompt, &truefile);
		if (vhdl_fp == 0)
		{
			if (truefile == 0) return(-1);
			return(1);
		}
	} else
	{
		/* make proper view of this cell */
		(void)initinfstr();
		(void)addstringtoinfstr("facet ");
		(void)addstringtoinfstr(np->cell->cellname);
		(void)addstringtoinfstr("{");
		(void)addstringtoinfstr(view->sviewname);
		(void)addstringtoinfstr("}");
		*intended = returninfstr();
		vhdl_facet = newnodeproto(&(*intended)[6], el_curlib);
		if (vhdl_facet == NONODEPROTO) return(1);
		vhdl_stringarray = newstringarray(vhdl_aid->cluster);
	}
	return(0);
}

INTBIG vhdl_endoutput(void)
{
	if (vhdl_fp != 0)
	{
		xclose(vhdl_fp);
		vhdl_fp = 0;
	}
	if (vhdl_facet != NONODEPROTO)
	{
		stringarraytotextfacet(vhdl_stringarray, vhdl_facet, 0);
		killstringarray(vhdl_stringarray);
		vhdl_facet = NONODEPROTO;
	}
	return(0);
}

/*
 * Routine to write a formatted string to the current output file.
 */
void vhdl_print(char *fstring, ...)
{
	INTSML i;
	char buff[4000], *sptr, save;
	va_list ap;

	var_start(ap, fstring);
	(void)vsprintf(buff, fstring, ap);
	va_end(ap);
	sptr = buff;
	while (strlen(sptr) > 70)
	{
		for(i=70; i>0; i--) if (isspace(sptr[i])) break;
		if (i <= 0) break;
		save = sptr[i];   sptr[i] = 0;
		if (vhdl_fp == 0) addtostringarray(vhdl_stringarray, sptr); else
			xprintf(vhdl_fp, "%s\n", sptr);
		sptr[i] = save;
		if (vhdl_target == TARGET_SILOS)
		{
			i--;
			sptr[i] = '+';
		}
		sptr = &sptr[i];
	}
	if (vhdl_fp == 0) addtostringarray(vhdl_stringarray, sptr); else
		xprintf(vhdl_fp, "%s\n", sptr);
}

/*
 * Routine to determine the revision date of facet "np", including any subfacets.
 */
UINTBIG vhdl_getfacetdate(NODEPROTO *np)
{
	REGISTER UINTBIG mostrecent, subfacetdate;
	REGISTER NODEINST *ni;

	mostrecent = np->revisiondate;
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		if (ni->proto->primindex != 0) continue;
		subfacetdate = vhdl_getfacetdate(ni->proto);
		if (subfacetdate > mostrecent) mostrecent = subfacetdate;
	}
	return(mostrecent);
}

/****************************** GENERATION ******************************/

/*
 * Routine to ensure that facet "np" and all subfacets have VHDL on them.
 * If "force" is nonzero, do conversion even if VHDL facet is newer.  Otherwise
 * convert only if necessary.
 */
void vhdl_convertfacet(NODEPROTO *np, INTSML force)
{
	REGISTER NODEPROTO *npvhdl, *onp;
	REGISTER LIBRARY *lib;
	REGISTER INTSML i, backannotate, vhdlondisk, len;
	REGISTER UINTBIG dateoflayout;
	REGISTER VARIABLE *var;
	FILE *f;
	char *intended, *filename, *desired, *firstline;

	/* cannot make VHDL for facet with no ports */
	if (np->firstportproto == NOPORTPROTO)
	{
		ttyputerr("Cannot convert facet %s to VHDL: it has no ports", describenodeproto(np));
		return;
	}

	var = getvalkey((INTBIG)vhdl_aid, VAID, VINTEGER, vhdl_vhdlondisk);
	if (var == NOVARIABLE) vhdlondisk = 0; else
		vhdlondisk = (INTSML)var->addr;

	if (force == 0)
	{
		/* determine most recent change to this or any subfacet */
		dateoflayout = vhdl_getfacetdate(np);

		/* if there is already VHDL that is newer, stop now */
		if (vhdlondisk != 0)
		{
			/* look for the disk file */
			f = xopen(np->cell->cellname, FILETYPEVHDL, "", &filename);
			if (f != NULL)
			{
				xclose(f);
				if (filedate(filename) >= dateoflayout) return;
			}
		} else
		{
			/* look for the facet */
			npvhdl = vhdlview(np);
			if (npvhdl != NONODEPROTO)
			{
				/* examine the VHDL to see if it really came from this facet */
				var = getvalkey((INTBIG)npvhdl, VNODEPROTO, VSTRING|VISARRAY, el_facet_message);
				if (var != NOVARIABLE)
				{
					firstline = ((char **)var->addr)[0];
					desired = "-- VHDL automatically generated from facet ";
					len = strlen(desired);
					if (strncmp(firstline, desired, len) == 0)
					{
						if (strcmp(&desired[len], describenodeproto(np)) != 0)
							dateoflayout = npvhdl->revisiondate + 1;
					}
				}

				if (npvhdl->revisiondate >= dateoflayout) return;
			}
		}
	}

	/* begin output of VHDL */
	i = vhdl_startoutput(np, FILETYPEVHDL, vhdlondisk, &intended);
	if (i != 0)
	{
		if (i > 0) ttyputerr("Cannot write %s", intended);
		return;
	}
	ttyputmsg("Converting layout in facet %s, writing VHDL to %s", describenodeproto(np),
		intended);

	/* recursively generate the VHDL */
	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
		for(onp = lib->firstnodeproto; onp != NONODEPROTO; onp = onp->nextnodeproto)
			onp->temp1 = 0;
	backannotate = vhdl_generatevhdl(np);
	if (backannotate != 0)
		ttyputmsg("Back-annotation information has been added (library must be saved)");

	/* that's it */
	vhdl_endoutput();
}

INTSML vhdl_generatevhdl(NODEPROTO *np)
{
	REGISTER NODEINST *ni;
	REGISTER NODEPROTO *onp, *cnp;
	REGISTER INTSML first, i, j, instnum, linelen, thisend, gotinverters, backannotate;
	INTBIG componentcount;
	INTSML special;
	REGISTER NETWORK *net;
	REGISTER ARCINST *ai;
	REGISTER VARIABLE *var;
	char line[30], *pt, gotnand[MAXINPUTS+1], gotxnor[MAXINPUTS+1],
		gotnor[MAXINPUTS+1], **componentlist;
	extern AIDENTRY *net_aid;

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

	/* indicate the source of this VHDL */
	(void)initinfstr();
	(void)addstringtoinfstr("-- VHDL automatically generated from facet ");
	(void)addstringtoinfstr(describenodeproto(np));
	vhdl_print(returninfstr());

	/* build the "entity" line */
	(void)initinfstr();
	(void)addstringtoinfstr("entity ");
	vhdl_addstring(np->cell->cellname);
	(void)addstringtoinfstr(" is port(");
	vhdl_addportlist(NONODEINST, np, 0);
	(void)addstringtoinfstr(");");
	vhdl_print(returninfstr());

	/* add the "end" line */
	(void)initinfstr();
	(void)addstringtoinfstr("  end ");
	vhdl_addstring(np->cell->cellname);
	(void)addstringtoinfstr(";");
	vhdl_print(returninfstr());

	/* now write the "architecture" line */
	(void)initinfstr();
	(void)addstringtoinfstr("architecture ");
	vhdl_addstring(np->cell->cellname);
	(void)addstringtoinfstr("_BODY of ");
	vhdl_addstring(np->cell->cellname);
	(void)addstringtoinfstr(" is");
	vhdl_print(returninfstr());

	/* enumerate negated arcs */
	instnum = 1;
	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
	{
		if ((ai->userbits&ISNEGATED) == 0) continue;
		ai->temp1 = instnum;
		instnum++;
	}

	/* write prototypes for each node */
	for(i=0; i<=MAXINPUTS; i++) gotnand[i] = gotnor[i] = gotxnor[i] = 0;
	if (vhdl_componentarray == 0)
	{
		vhdl_componentarray = newstringarray(vhdl_aid->cluster);
		if (vhdl_componentarray == 0) return(backannotate);
	}
	clearstrings(vhdl_componentarray);
	componentlist = getstringarray(vhdl_componentarray, &componentcount);
	instnum = 1;
	gotinverters = 0;
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		pt = vhdl_primname(ni, &special);
		if (pt[0] == 0) continue;

		/* see if the node has a name, number it if not */
		var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name);
		if (var == NOVARIABLE) ni->temp1 = instnum++;

		/* write only once per prototype */
		if (special == BLOCKINVERTER)
		{
			gotinverters = 1;
			continue;
		}
		if (special == BLOCKNAND)
		{
			i = atoi(&pt[4]);
			if (i <= MAXINPUTS) gotnand[i]++; else
				ttyputerr("Cannot handle %d-input NAND, limit is %d", i, MAXINPUTS);
			continue;
		}
		if (special == BLOCKNOR)
		{
			i = atoi(&pt[3]);
			if (i <= MAXINPUTS) gotnor[i]++; else
				ttyputerr("Cannot handle %d-input NOR, limit is %d", i, MAXINPUTS);
			continue;
		}
		if (special == BLOCKXNOR)
		{
			i = atoi(&pt[3]);
			if (i <= MAXINPUTS) gotxnor[i]++; else
				ttyputerr("Cannot handle %d-input XNOR, limit is %d", i, MAXINPUTS);
			continue;
		}

		/* ignore component with no ports */
		if (ni->proto->firstportproto == NOPORTPROTO) continue;

		/* see if this component is already written to the header */
		for(i=0; i<componentcount; i++)
			if (namesame(pt, componentlist[i]) == 0) break;
		if (i < componentcount) continue;

		/* new component: add to the list */
		addtostringarray(vhdl_componentarray, pt);
		componentlist = getstringarray(vhdl_componentarray, &componentcount);

		(void)initinfstr();
		(void)addstringtoinfstr("  component ");
		vhdl_addstring(pt);
		(void)addstringtoinfstr(" port(");
		vhdl_addportlist(ni, ni->proto, special);
		(void)addstringtoinfstr(");");
		vhdl_print(returninfstr());
		vhdl_print("    end component;");
	}

	/* write pseudo-prototype if there are any negated arcs */
	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
		if ((ai->userbits&ISNEGATED) != 0 && ai->temp1 != 0)
	{
		gotinverters = 1;
		break;
	}
	if (gotinverters != 0)
	{
		vhdl_print("  component inverter port(a: in BIT; y: out BIT);");
		vhdl_print("    end component;");
	}
	for(i=0; i<MAXINPUTS; i++)
	{
		if (gotnand[i] != 0)
		{
			(void)initinfstr();
			(void)addstringtoinfstr("  component nand");
			(void)sprintf(line, "%d", i);
			(void)addstringtoinfstr(line);
			(void)addstringtoinfstr(" port(");
			for(j=1; j<=i; j++)
			{
				if (j > 1) (void)addstringtoinfstr(", ");
				(void)sprintf(line, "a%d", j);
				(void)addstringtoinfstr(line);
			}
			(void)addstringtoinfstr(": in BIT; y: out BIT);");
			vhdl_print(returninfstr());
			vhdl_print("    end component;");
		}
		if (gotnor[i] != 0)
		{
			(void)initinfstr();
			(void)addstringtoinfstr("  component nor");
			(void)sprintf(line, "%d", i);
			(void)addstringtoinfstr(line);
			(void)addstringtoinfstr(" port(");
			for(j=1; j<=i; j++)
			{
				if (j > 1) (void)addstringtoinfstr(", ");
				(void)sprintf(line, "a%d", j);
				(void)addstringtoinfstr(line);
			}
			(void)addstringtoinfstr(": in BIT; y: out BIT);");
			vhdl_print(returninfstr());
			vhdl_print("    end component;");
		}
		if (gotxnor[i] != 0)
		{
			(void)initinfstr();
			(void)addstringtoinfstr("  component xnor");
			(void)sprintf(line, "%d", i);
			(void)addstringtoinfstr(line);
			(void)addstringtoinfstr(" port(");
			for(j=1; j<=i; j++)
			{
				if (j > 1) (void)addstringtoinfstr(", ");
				(void)sprintf(line, "a%d", j);
				(void)addstringtoinfstr(line);
			}
			(void)addstringtoinfstr(": in BIT; y: out BIT);");
			vhdl_print(returninfstr());
			vhdl_print("    end component;");
		}
	}

	/* write internal nodes */
	first = 0;
	for(net = np->firstnetwork; net != NONETWORK; net = net->nextnetwork)
	{
		if (net->portcount != 0) continue;
		if (first == 0)
		{
			(void)initinfstr();
			(void)addstringtoinfstr("  signal ");
			linelen = 9;
		} else
		{
			(void)addstringtoinfstr(", ");
			linelen += 2;
		}
		first = 1;
		if (net->namecount > 0) pt = net->netname; else
		{
			(void)sprintf(line, "NET%ld", (INTBIG)net);
			pt = line;
		}
		if (linelen + strlen(pt) > 80)
		{
			vhdl_print(returninfstr());
			(void)initinfstr();
			(void)addstringtoinfstr("    ");
			linelen = 4;
		}
		vhdl_addstring(pt);
		linelen += strlen(pt);
	}
	if (first != 0)
	{
		(void)addstringtoinfstr(": BIT;");
		vhdl_print(returninfstr());
	}

	/* write pseudo-internal nodes for all negated arcs */
	first = 0;
	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
	{
		if ((ai->userbits&ISNEGATED) == 0 || ai->temp1 == 0) continue;
		if (first == 0)
		{
			(void)initinfstr();
			(void)addstringtoinfstr("  signal ");
		} else (void)addstringtoinfstr(", ");
		(void)sprintf(line, "PINV%ld", ai->temp1);
		(void)addstringtoinfstr(line);
		first++;
	}
	if (first != 0)
	{
		(void)addstringtoinfstr(": BIT;");
		vhdl_print(returninfstr());
	}

	/* write the instances */
	vhdl_print("begin");
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		/* ignore component with no ports */
		if (ni->proto->firstportproto == NOPORTPROTO) continue;

		pt = vhdl_primname(ni, &special);
		if (pt[0] == 0) continue;

		(void)initinfstr();
		(void)addstringtoinfstr("  ");
		var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name);
		if (var != NOVARIABLE) vhdl_addstring((char *)var->addr); else
		{
			ttyputerr("VHDL conversion warning: no name on node %s", describenodeinst(ni));
			(void)sprintf(line, "NODE%ld", (INTBIG)ni);
			(void)addstringtoinfstr(line);
		}
		(void)addstringtoinfstr(": ");
		vhdl_addstring(pt);

		(void)addstringtoinfstr(" port map(");
		vhdl_addrealports(ni, special);
		(void)addstringtoinfstr(");");
		vhdl_print(returninfstr());
	}

	/* write pseudo-nodes for all negated arcs */
	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
	{
		if ((ai->userbits&ISNEGATED) == 0 || ai->temp1 == 0) continue;
		(void)initinfstr();
		(void)addstringtoinfstr("  PSEUDO_INVERT");
		(void)sprintf(line, "%ld", ai->temp1);
		(void)addstringtoinfstr(line);
		(void)addstringtoinfstr(": inverter port map(");
		if ((ai->userbits&REVERSEEND) == 0) thisend = 0; else thisend = 1;
		if ((ai->end[thisend].portarcinst->proto->userbits&STATEBITS) == OUTPORT)
		{
			(void)sprintf(line, "PINV%ld", ai->temp1);
			(void)addstringtoinfstr(line);
			(void)addstringtoinfstr(", ");
			net = ai->network;
			if (net->namecount > 0) vhdl_addstring(net->netname); else
			{
				(void)addstringtoinfstr("NET");
				(void)sprintf(line, "%ld", (INTBIG)net);
				(void)addstringtoinfstr(line);
			}
		} else
		{
			net = ai->network;
			if (net->namecount > 0) vhdl_addstring(net->netname); else
			{
				(void)addstringtoinfstr("NET");
				(void)sprintf(line, "%ld", (INTBIG)net);
				(void)addstringtoinfstr(line);
			}
			(void)addstringtoinfstr(", ");
			(void)sprintf(line, "PINV%ld", ai->temp1);
			(void)addstringtoinfstr(line);
		}
		(void)addstringtoinfstr(");");
		vhdl_print(returninfstr());
	}

	/* write the end of the body */
	(void)initinfstr();
	(void)addstringtoinfstr("end ");
	vhdl_addstring(np->cell->cellname);
	(void)addstringtoinfstr("_BODY;");
	vhdl_print(returninfstr());
	vhdl_print("");

	/* finally, generate VHDL for all subfacets */
	np->temp1 = 1;
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		if (ni->proto->primindex != 0) continue;
		if (ni->proto->temp1 != 0) continue;
		cnp = contentsview(ni->proto);
		if (cnp == NONODEPROTO) cnp = ni->proto;
		if (cnp->temp1 != 0) continue;

		/* ignore component with no ports */
		if (cnp->firstportproto == NOPORTPROTO) continue;

		/* if ALS, see if this facet is in the reference library */
		if (vhdl_lib != NOLIBRARY && vhdl_target == TARGET_ALS)
		{
			for(onp = vhdl_lib->firstnodeproto; onp != NONODEPROTO; onp = onp->nextnodeproto)
				if (onp->cellview == el_netlistalsview &&
					namesame(onp->cell->cellname, cnp->cell->cellname) == 0) break;
			if (onp != NONODEPROTO) continue;
		}

		downhierarchy(ni);
		if (vhdl_generatevhdl(cnp) != 0) backannotate = 1;
		uphierarchy();
	}
	return(backannotate);
}

void vhdl_addrealports(NODEINST *ni, INTSML special)
{
	REGISTER INTSML first, pass;
	REGISTER PORTPROTO *pp, *opp;
	REGISTER ARCINST *ai;
	REGISTER PORTARCINST *pi;
	REGISTER PORTEXPINST *pe;
	REGISTER NETWORK *net;
	char line[50];

	first = 0;
	for(pass = 0; pass < 5; pass++)
	{
		for(pp = ni->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
		{
			if (pass == 0)
			{
				/* must be an input port */
				if ((pp->userbits&STATEBITS) != INPORT) continue;
			}
			if (pass == 1)
			{
				/* must be an output port */
				if ((pp->userbits&STATEBITS) != OUTPORT) continue;
			}
			if (pass == 2)
			{
				/* must be an output port */
				if ((pp->userbits&STATEBITS) != PWRPORT) continue;
			}
			if (pass == 3)
			{
				/* must be an output port */
				if ((pp->userbits&STATEBITS) != GNDPORT) continue;
			}
			if (pass == 4)
			{
				/* any other port type */
				if ((pp->userbits&STATEBITS) == INPORT || (pp->userbits&STATEBITS) == OUTPORT ||
					(pp->userbits&STATEBITS) == PWRPORT || (pp->userbits&STATEBITS) == GNDPORT)
						continue;
			}

			if (special == BLOCKMOSTRAN)
			{
				/* ignore electrically connected ports */
				for(opp = ni->proto->firstportproto; opp != pp; opp = opp->nextportproto)
					if (opp->network == pp->network) break;
				if (opp != pp) continue;
			}
			if (special == BLOCKPOSLOGIC || special == BLOCKBUFFER || special == BLOCKINVERTER ||
				special == BLOCKNAND || special == BLOCKNOR || special == BLOCKXNOR)
			{
				/* ignore ports not named "a" or "y" */
				if (strcmp(pp->protoname, "a") != 0 && strcmp(pp->protoname, "y") != 0)
				{
					pp->temp1 = 1;
					continue;
				}
			}
			if (special == BLOCKFLOPTS || special == BLOCKFLOPDS)
			{
				/* ignore ports not named "i1", "ck", "preset", or "q" */
				if (strcmp(pp->protoname, "i1") != 0 && strcmp(pp->protoname, "ck") != 0 &&
					strcmp(pp->protoname, "preset") != 0 && strcmp(pp->protoname, "q") != 0)
				{
					pp->temp1 = 1;
					continue;
				}
			}
			if (special == BLOCKFLOPTR || special == BLOCKFLOPDR)
			{
				/* ignore ports not named "i1", "ck", "clear", or "q" */
				if (strcmp(pp->protoname, "i1") != 0 && strcmp(pp->protoname, "ck") != 0 &&
					strcmp(pp->protoname, "clear") != 0 && strcmp(pp->protoname, "q") != 0)
				{
					pp->temp1 = 1;
					continue;
				}
			}

			/* if multiple connections, get them all */
			if ((pp->userbits&PORTISOLATED) != 0)
			{
				for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
				{
					if (pi->proto != pp) continue;
					ai = pi->conarcinst;
					if (((ai->proto->userbits&AFUNCTION)>>AFUNCTIONSH) == APNONELEC) continue;
					if (first != 0) (void)addstringtoinfstr(", ");   first = 1;
					if ((ai->userbits&ISNEGATED) != 0)
					{
						if ((ai->end[0].portarcinst == pi && (ai->userbits&REVERSEEND) == 0) ||
							(ai->end[1].portarcinst == pi && (ai->userbits&REVERSEEND) != 0))
						{
							(void)sprintf(line, "PINV%ld", ai->temp1);
							(void)addstringtoinfstr(line);
							continue;
						}
					}
					net = ai->network;
					if (net->namecount > 0) vhdl_addstring(net->netname); else
					{
						(void)addstringtoinfstr("NET");
						(void)sprintf(line, "%ld", (INTBIG)net);
						(void)addstringtoinfstr(line);
					}
				}
				continue;
			}

			/* get connection */
			for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
				if (pi->proto->network == pp->network) break;
			if (pi != NOPORTARCINST)
			{
				ai = pi->conarcinst;
				if (((ai->proto->userbits&AFUNCTION)>>AFUNCTIONSH) != APNONELEC)
				{
					if (first != 0) (void)addstringtoinfstr(", ");   first = 1;
					if ((ai->userbits&ISNEGATED) != 0 && ai->temp1 != 0)
					{
						if ((ai->end[0].portarcinst == pi && (ai->userbits&REVERSEEND) == 0) ||
							(ai->end[1].portarcinst == pi && (ai->userbits&REVERSEEND) != 0))
						{
							(void)sprintf(line, "PINV%ld", ai->temp1);
							(void)addstringtoinfstr(line);
							continue;
						}
					}

					net = ai->network;
					if (net->namecount > 0) vhdl_addstring(net->netname); else
					{
						(void)addstringtoinfstr("NET");
						(void)sprintf(line, "%ld", (INTBIG)net);
						(void)addstringtoinfstr(line);
					}
					continue;
				}
			}

			/* see if this is exported */
			for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
				if (pe->proto->network == pp->network) break;
			if (pe != NOPORTEXPINST)
			{
				if (first != 0) (void)addstringtoinfstr(", ");   first = 1;
				vhdl_addstring(pe->exportproto->protoname);
				continue;
			}

			/* port is not connected or exported */
			if (first != 0) (void)addstringtoinfstr(", ");   first = 1;
			(void)addstringtoinfstr("open");
		}
	}
}

/*
 * routine to return the VHDL name to use for node "ni".  Returns a "special" value
 * that indicates the nature of the node.
 *  BLOCKNORMAL: no special port arrangements necessary
 *  BLOCKMOSTRAN: only output ports that are not electrically connected
 *  BLOCKBUFFER: only include input port "a" and output port "y"
 *  BLOCKPOSLOGIC: only include input port "a" and output port "y"
 *  BLOCKINVERTER: only include input port "a" and output port "y"
 *  BLOCKNAND: only include input port "a" and output port "y"
 *  BLOCKNOR: only include input port "a" and output port "y"
 *  BLOCKXNOR: only include input port "a" and output port "y"
 *  BLOCKFLOPTS: only include input ports "i1", "ck", "preset" and output port "q"
 *  BLOCKFLOPTR: only include input ports "i1", "ck", "clear" and output port "q"
 *  BLOCKFLOPDS: only include input ports "i1", "ck", "preset" and output port "q"
 *  BLOCKFLOPDR: only include input ports "i1", "ck", "clear" and output port "q"
 *  BLOCKFLOP: include input ports "i1", "i2", "ck", "preset", "clear", and output ports "q" and "qb"
 */
char *vhdl_primname(NODEINST *ni, INTSML *special)
{
	REGISTER INTSML k, inport, isneg;
	static INTBIG tech_vhdl_names = 0;
	char *dummy;
	REGISTER char *str, *ptr;
	REGISTER VARIABLE *var;
	REGISTER PORTARCINST *pi;
	REGISTER PORTEXPINST *pe;
	REGISTER PORTPROTO *pp;
	REGISTER ARCINST *ai;
	static char pt[30];

	/* initialize */
	if (tech_vhdl_names == 0) tech_vhdl_names = makekey("TECH_vhdl_names");

	/* facet instances are easy */
	*special = BLOCKNORMAL;
	if (ni->proto->primindex == 0) return(ni->proto->cell->cellname);

	/* get the primitive function */
	k = nodefunction(ni, &dummy);
	pt[0] = 0;
	switch (k)
	{
		case NPTRANMOS:   (void)strcpy(pt, "nMOStran");   *special = BLOCKMOSTRAN;   break;
		case NPTRADMOS:   (void)strcpy(pt, "DMOStran");   *special = BLOCKMOSTRAN;   break;
		case NPTRAPMOS:   (void)strcpy(pt, "PMOStran");   *special = BLOCKMOSTRAN;   break;
		case NPTRANPN:    (void)strcpy(pt, "NPNtran");    break;
		case NPTRAPNP:    (void)strcpy(pt, "PNPtran");    break;
		case NPTRANJFET:  (void)strcpy(pt, "NJFET");      break;
		case NPTRAPJFET:  (void)strcpy(pt, "PJFET");      break;
		case NPTRADMES:   (void)strcpy(pt, "DMEStran");   break;
		case NPTRAEMES:   (void)strcpy(pt, "EMEStran");   break;
		case NPTRANE2L:   (void)strcpy(pt, "E2Ltran");    break;

		case NPFLIPFLOP:
			var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, sch_flipfloptypekey);
			if (var != NOVARIABLE)
			{
				if (namesamen((char *)var->addr, "rs", 2) == 0)
				{
					(void)strcpy(pt, "rsff");
					*special = BLOCKFLOP;
					break;
				}
				if (namesamen((char *)var->addr, "jk", 2) == 0)
				{
					(void)strcpy(pt, "jkff");
					*special = BLOCKFLOP;
					break;
				}
				if (namesamen((char *)var->addr, "d", 1) == 0)
				{
					(void)strcpy(pt, "dsff");
					*special = BLOCKFLOPDS;
					for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
					{
						if (namesame(pi->proto->protoname, "clear") == 0)
						{
							(void)strcpy(pt, "drff");
							*special = BLOCKFLOPDR;
							break;
						}
					}
					break;
				}
				if (namesamen((char *)var->addr, "t", 1) == 0)
				{
					(void)strcpy(pt, "tsff");
					*special = BLOCKFLOPTS;
					for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
					{
						if (namesame(pi->proto->protoname, "clear") == 0)
						{
							(void)strcpy(pt, "trff");
							*special = BLOCKFLOPTR;
							break;
						}
					}
					break;
				}
			}
			(void)strcpy(pt, "fflop");
			*special = BLOCKFLOP;
			break;

		case NPBUFFER:
			var = getvalkey((INTBIG)el_curtech, VTECHNOLOGY, VSTRING|VISARRAY, tech_vhdl_names);
			if (var == NOVARIABLE) str = "buffer/inverter"; else
				str = ((char **)var->addr)[ni->proto->primindex-1];
			*special = BLOCKBUFFER;
			for(pi=ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
			{
				if (strcmp(pi->proto->protoname, "y") != 0) continue;
				ai = pi->conarcinst;
				if ((ai->userbits&ISNEGATED) == 0) continue;
				if (ai->end[0].portarcinst == pi && (ai->userbits&REVERSEEND) == 0) break;
				if (ai->end[1].portarcinst == pi && (ai->userbits&REVERSEEND) != 0) break;
			}
			if (pi != NOPORTARCINST)
			{
				for(ptr = str; *ptr != 0; ptr++) if (*ptr == '/') break;
				if (*ptr == '/') strcpy(pt, &ptr[1]); else
					strcpy(pt, str);
				*special = BLOCKINVERTER;
				ai->temp1 = 0;
			} else
			{
				strcpy(pt, str);
				for(ptr = pt; *ptr != 0; ptr++) if (*ptr == '/') break;
				if (*ptr == '/') *ptr = 0;					
			}
			break;
		case NPGATEAND:
			var = getvalkey((INTBIG)el_curtech, VTECHNOLOGY, VSTRING|VISARRAY, tech_vhdl_names);
			if (var == NOVARIABLE) str = "and%d/nand%d"; else
				str = ((char **)var->addr)[ni->proto->primindex-1];
			inport = isneg = 0;
			for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
			{
				if (strcmp(pi->proto->protoname, "a") == 0) inport++;
				if (strcmp(pi->proto->protoname, "y") != 0) continue;
				ai = pi->conarcinst;
				if ((ai->userbits&ISNEGATED) == 0) continue;
				if (ai->end[0].portarcinst == pi && (ai->userbits&REVERSEEND) == 0) isneg++;
				if (ai->end[1].portarcinst == pi && (ai->userbits&REVERSEEND) != 0) isneg++;
			}
			if (isneg != 0)
			{
				for(ptr = str; *ptr != 0; ptr++) if (*ptr == '/') break;
				if (*ptr == '/') (void)sprintf(pt, &ptr[1], inport); else
					(void)sprintf(pt, str, inport);
				*special = BLOCKNAND;
				ai->temp1 = 0;
			} else
			{
				(void)sprintf(pt, str, inport);
				for(ptr = pt; *ptr != 0; ptr++) if (*ptr == '/') break;
				if (*ptr == '/') *ptr = 0;					
				*special = BLOCKPOSLOGIC;
			}
			break;
		case NPGATEOR:
			var = getvalkey((INTBIG)el_curtech, VTECHNOLOGY, VSTRING|VISARRAY, tech_vhdl_names);
			if (var == NOVARIABLE) str = "or%d/nor%d"; else
				str = ((char **)var->addr)[ni->proto->primindex-1];
			inport = isneg = 0;
			for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
			{
				if (strcmp(pi->proto->protoname, "a") == 0) inport++;
				if (strcmp(pi->proto->protoname, "y") != 0) continue;
				ai = pi->conarcinst;
				if ((ai->userbits&ISNEGATED) == 0) continue;
				if (ai->end[0].portarcinst == pi && (ai->userbits&REVERSEEND) == 0) isneg++;
				if (ai->end[1].portarcinst == pi && (ai->userbits&REVERSEEND) != 0) isneg++;
			}
			if (isneg != 0)
			{
				for(ptr = str; *ptr != 0; ptr++) if (*ptr == '/') break;
				if (*ptr == '/') (void)sprintf(pt, &ptr[1], inport); else
					(void)sprintf(pt, str, inport);
				*special = BLOCKNOR;
				ai->temp1 = 0;
			} else
			{
				(void)sprintf(pt, str, inport);
				for(ptr = pt; *ptr != 0; ptr++) if (*ptr == '/') break;
				if (*ptr == '/') *ptr = 0;					
				*special = BLOCKPOSLOGIC;
			}
			break;
		case NPGATEXOR:
			var = getvalkey((INTBIG)el_curtech, VTECHNOLOGY, VSTRING|VISARRAY, tech_vhdl_names);
			if (var == NOVARIABLE) str = "xor%d/xnor%d"; else
				str = ((char **)var->addr)[ni->proto->primindex-1];
			inport = isneg = 0;
			for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
			{
				if (strcmp(pi->proto->protoname, "a") == 0) inport++;
				if (strcmp(pi->proto->protoname, "y") != 0) continue;
				ai = pi->conarcinst;
				if ((ai->userbits&ISNEGATED) == 0) continue;
				if (ai->end[0].portarcinst == pi && (ai->userbits&REVERSEEND) == 0) isneg++;
				if (ai->end[1].portarcinst == pi && (ai->userbits&REVERSEEND) != 0) isneg++;
			}
			if (isneg != 0)
			{
				for(ptr = str; *ptr != 0; ptr++) if (*ptr == '/') break;
				if (*ptr == '/') (void)sprintf(pt, &ptr[1], inport); else
					(void)sprintf(pt, str, inport);
				*special = BLOCKNOR;
				ai->temp1 = 0;
			} else
			{
				(void)sprintf(pt, str, inport);
				for(ptr = pt; *ptr != 0; ptr++) if (*ptr == '/') break;
				if (*ptr == '/') *ptr = 0;					
				*special = BLOCKPOSLOGIC;
			}
			break;
		case NPMUX:
			var = getvalkey((INTBIG)el_curtech, VTECHNOLOGY, VSTRING|VISARRAY, tech_vhdl_names);
			if (var == NOVARIABLE) str = "mux%d"; else
				str = ((char **)var->addr)[ni->proto->primindex-1];
			inport = isneg = 0;
			inport = 0;
			for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
				if (strcmp(pi->proto->protoname, "a") == 0) inport++;
			(void)sprintf(pt, str, inport);
			break;
		case NPCONPOWER:
			strcpy(pt, "power");
			break;
		case NPCONGROUND:
			strcpy(pt, "ground");
			break;
	}
	if (*pt == 0)
	{
		/* if the node has an exported port with power/ground, make it that */
		for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
		{
			pp = pe->exportproto;
			if (portispower(pp) != 0)
			{
				strcpy(pt, "power");
				break;
			}
			if (portisground(pp) != 0)
			{
				strcpy(pt, "ground");
				break;
			}
		}
	}
	return(pt);
}

void vhdl_addstring(char *orig)
{
	char *pt;
	INTSML len, nonalnum;

	/* remove all nonVHDL characters while adding to current string */
	nonalnum = 0;
	for(pt = orig; *pt != 0; pt++)
		if (isalnum(*pt)) (void)addtoinfstr(*pt); else
	{
		(void)addtoinfstr('_');
		nonalnum++;
	}

	/* if there were nonalphanumeric characters, this cannot be a VHDL keyword */
	if (nonalnum != 0) return;

	/* check for VHDL keyword clashes */
	len = strlen(orig);
	if (vhdl_iskeyword(orig, len) != NOVKEYWORD) (void)addstringtoinfstr("NV");
}

/*
 * routine to add, to the infinite string, VHDL for the ports on instance "ni"
 * (which is of prototype "np").  If "ni" is NONODEINST, use only prototype information,
 * otherwise, treat each connection on an isolated port as a separate port.
 * If "special" is BLOCKMOSTRAN, only list ports that are not electrically connected.
 * If "special" is BLOCKPOSLOGIC, BLOCKBUFFER or BLOCKINVERTER, only include input
 *    port "a" and output port "y".
 * If "special" is BLOCKFLOPTS or BLOCKFLOPDS, only include input ports "i1", "ck", "preset"
 *    and output port "q".
 * If "special" is BLOCKFLOPTR or BLOCKFLOPDR, only include input ports "i1", "ck", "clear"
 *    and output port "q".
 */
void vhdl_addportlist(NODEINST *ni, NODEPROTO *np, INTSML special)
{
	REGISTER PORTPROTO *pp, *opp;
	char before[10];

	if (special == BLOCKFLOPTS || special == BLOCKFLOPDS)
	{
		(void)addstringtoinfstr("i1, ck, preset: in BIT; q: out BIT");
		return;
	}
	if (special == BLOCKFLOPTR || special == BLOCKFLOPDR)
	{
		(void)addstringtoinfstr("i1, ck, clear: in BIT; q: out BIT");
		return;
	}

	/* flag important ports */
	for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
	{
		pp->temp1 = 0;
		if (special == BLOCKMOSTRAN)
		{
			/* ignore ports that are electrically connected to previous ones */
			for(opp = np->firstportproto; opp != pp; opp = opp->nextportproto)
				if (opp->network == pp->network) break;
			if (opp != pp) { pp->temp1 = 1;   continue; }
		}
		if (special == BLOCKPOSLOGIC || special == BLOCKBUFFER || special == BLOCKINVERTER)
		{
			/* ignore ports not named "a" or "y" */
			if (strcmp(pp->protoname, "a") != 0 && strcmp(pp->protoname, "y") != 0)
			{
				pp->temp1 = 1;
				continue;
			}
		}
	}

	(void)strcpy(before, "");
	vhdl_addtheseports(ni, np, INPORT, before);
	vhdl_addtheseports(ni, np, OUTPORT, before);
	vhdl_addtheseports(ni, np, PWRPORT, before);
	vhdl_addtheseports(ni, np, GNDPORT, before);
	vhdl_addtheseports(ni, np, 0, before);
}

void vhdl_addtheseports(NODEINST *ni, NODEPROTO *np, INTBIG bits, char *before)
{
	REGISTER PORTPROTO *pp;
	REGISTER PORTARCINST *pi;
	REGISTER INTSML didsome, inst;
	char instname[10];

	didsome = 0;
	for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
	{
		if (pp->temp1 != 0) continue;
		if (bits == 0)
		{
			if ((pp->userbits&STATEBITS) == INPORT || (pp->userbits&STATEBITS) == OUTPORT ||
				(pp->userbits&STATEBITS) == PWRPORT || (pp->userbits&STATEBITS) == GNDPORT)
					continue;
		} else
		{
			if ((INTBIG)((pp->userbits&STATEBITS)) != bits) continue;
		}
		if (ni != NONODEINST && (pp->userbits&PORTISOLATED) != 0)
		{
			inst = 1;
			for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
			{
				if (pi->proto != pp) continue;
				(void)addstringtoinfstr(before);
				(void)strcpy(before, ", ");
				vhdl_addstring(pp->protoname);
				(void)sprintf(instname, "%d", inst++);
				(void)addstringtoinfstr(instname);
				didsome++;
			}
		} else
		{
			(void)addstringtoinfstr(before);
			(void)strcpy(before, ", ");
			vhdl_addstring(pp->protoname);
			didsome++;
		}
	}
	if (didsome != 0)
	{
		if (bits == INPORT)
		{
			(void)addstringtoinfstr(": in BIT");
		} else if (bits == OUTPORT || bits == PWRPORT || bits == GNDPORT)
		{
			(void)addstringtoinfstr(": out BIT");
		} else
		{
			(void)addstringtoinfstr(": inout BIT");
		}
		(void)strcpy(before, "; ");
	}
}

/****************************** COMPILATION ******************************/

/*
 * routine to compile the VHDL in "basefacet" (or wherever the related VHDL
 * view is).  If "toplevel" is nonzero, this network is the final output, so
 * it MUST be recompiled if any subfacets have changed.
 */
void vhdl_compile(NODEPROTO *basefacet)
{
	char *intended, *fromintended;
	INTBIG	i, err;
	REGISTER VARIABLE *var;
	UINTBIG	netlistdate, vhdldate;
	INTSML retval, filetype, vhdlondisk, netlistondisk;

	/* determine netlist type */
	switch (vhdl_target)
	{
		case TARGET_ALS:     filetype = FILETYPEALS;      break;
		case TARGET_NETLISP: filetype = FILETYPENETLISP;  break;
		case TARGET_RSIM:    filetype = FILETYPERSIM;     break;
		case TARGET_SILOS:   filetype = FILETYPESILOS;    break;
		case TARGET_QUISC:   filetype = FILETYPEQUISC;    break;
	}

	var = getvalkey((INTBIG)vhdl_aid, VAID, VINTEGER, vhdl_vhdlondisk);
	if (var == NOVARIABLE) vhdlondisk = 0; else
		vhdlondisk = (INTSML)var->addr;
	var = getvalkey((INTBIG)vhdl_aid, VAID, VINTEGER, vhdl_netlistondisk);
	if (var == NOVARIABLE) netlistondisk = 0; else
		netlistondisk = (INTSML)var->addr;

	/* see if there is netlist associated with this facet */
	err = vhdl_startinput(basefacet, filetype, netlistondisk, &intended, &netlistdate);
	if (err == 0)
	{
		/* got netlist, don't need to read it, just wanted the date and existence */
		vhdl_endinput();
	}

	/* get VHDL */
	if (vhdl_startinput(basefacet, FILETYPEVHDL, vhdlondisk, &intended, &vhdldate) != 0)
	{
		/* issue error if this isn't an icon */
		if (basefacet->cellview != el_iconview)
			ttyputerr("Cannot find VHDL for %s", basefacet->cell->cellname);
		return;
	}

	/* if there is a newer netlist, stop now */
	if (err == 0 && netlistdate >= vhdldate)
	{
		vhdl_endinput();
		return;
	}

	/* build and clear vhdl_identtable */
	if (vhdl_identtable == 0)
	{
		vhdl_identtable = (IDENTTABLE *)emalloc((INTBIG)IDENT_TABLE_SIZE *
			(INTBIG)(sizeof (IDENTTABLE)), vhdl_aid->cluster);
		if (vhdl_identtable == 0) return;
		for (i = 0; i < IDENT_TABLE_SIZE; i++)
			vhdl_identtable[i].string = (char *)0;
	}

	if (allocstring(&fromintended, intended, el_tempcluster) != 0) return;
	vhdl_errorcount = 0;
	vhdl_scanner();
	(void)vhdl_endinput();
	err = vhdl_parser(vhdl_tliststart);
	if (!err) err = vhdl_semantic();
	if (err)
	{
		ttyputmsg("ERRORS during compilation, no output produced");
		efree(fromintended);
		return;
	}

	/* prepare to create netlist */
	retval = vhdl_startoutput(basefacet, filetype, netlistondisk, &intended);
	if (retval != 0)
	{
		if (retval > 0) ttyputerr("Cannot write %s", intended);
		return;
	}
	ttyputmsg("Compiling VHDL in %s, writing netlist to %s", fromintended, intended);
	efree(fromintended);

	/* write output */
	switch (vhdl_target)
	{
		case TARGET_ALS:     vhdl_genals(vhdl_lib, basefacet);      break;
		case TARGET_NETLISP: vhdl_gennet((INTSML)vhdl_target);   	break;
		case TARGET_RSIM:    vhdl_gennet((INTSML)vhdl_target);   	break;
		case TARGET_SILOS:   vhdl_gensilos();            			break;
		case TARGET_QUISC:   vhdl_genquisc();            			break;
	}

	/* finish up */
	vhdl_endoutput();
}

/*
Module:  vhdl_scanner
------------------------------------------------------------------------
Description:
	Lexical scanner of input file creating token list.
------------------------------------------------------------------------
Calling Sequence:  vhdl_scanner();
------------------------------------------------------------------------
*/
void vhdl_scanner(void)
{
	char *c, buffer[MAXVHDLLINE], *end, tchar;
	INTBIG line_num, space;
	VKEYWORD *key;

	/* start by clearing previous scanner memory */
	vhdl_freescannermemory();

	c = "";
	while (TRUE)
	{
		if (*c == 0)
		{
			line_num = vhdl_getnextline(buffer);
			if (line_num == 0) return;
			space = TRUE;
			c = buffer;
		} else if (isspace(*c)) space = TRUE; else
			space = FALSE;
		while (isspace(*c)) c++;
		if (*c == 0) continue;
		if (isalpha(*c))
		{
			/* could be identifier (keyword) or bit string literal */
			if (*(c + 1) == '"')
			{
				if ((tchar = vhdl_toupper(*c)) == 'B')
				{
				} else if (tchar == '0')
				{
				} else if (tchar == 'X')
				{
				}
			}
			end = c + 1;
			while (isalnum(*end) || *end == '_') end++;

			/* got alphanumeric from c to end - 1 */
			if ((key = vhdl_iskeyword(c, (INTBIG)(end - c))) != NOVKEYWORD)
			{
				vhdl_makekeytoken(key, line_num, (INTBIG)space);
			} else
			{
				vhdl_makeidenttoken(c, (INTBIG)(end - c), line_num, (INTBIG)space);
			}
			c = end;
		} else if (isdigit(*c))
		{
			/* could be decimal or based literal */
			end = c + 1;
			while (isdigit(*end) || *end == '_') end++;

			/* got numeric from c to end - 1 */
			vhdl_makedecimaltoken(c, (INTBIG)(end - c), line_num, (INTBIG)space);
			c = end;
		} else
		{
			switch (*c)
			{
				case '"':
					/* got a start of a string */
					end = c + 1;
					while (*end != '\n')
					{
						if (*end == '"')
						{
							if (*(end + 1) == '"') end++; else
								break;
						}
						end++;
					}
					/* string from c + 1 to end - 1 */
					vhdl_makestrtoken(c + 1, (INTBIG)(end - c - 1), line_num, (INTBIG)space);
					if (*end == '"') end++;
					c = end;
					break;
				case '&':
					vhdl_maketoken((INTBIG)TOKEN_AMPERSAND, line_num, (INTBIG)space);
					c++;
					break;
				case '\'':
					/* character literal */
					if (isgraph(*(c + 1)) && *(c + 2) == '\'')
					{
						vhdl_makechartoken(*(c + 1), line_num, (INTBIG)space);
						c += 3;
					} else c++;
					break;
				case '(':
					vhdl_maketoken((INTBIG)TOKEN_LEFTBRACKET, line_num, (INTBIG)space);
					c++;
					break;
				case ')':
					vhdl_maketoken((INTBIG)TOKEN_RIGHTBRACKET, line_num, (INTBIG)space);
					c++;
					break;
				case '*':
					/* could be STAR or DOUBLESTAR */
					if (*(c + 1) == '*')
					{
						vhdl_maketoken((INTBIG)TOKEN_DOUBLESTAR, line_num, (INTBIG)space);
						c += 2;
					} else
					{
						vhdl_maketoken((INTBIG)TOKEN_STAR, line_num, (INTBIG)space);
						c++;
					}
					break;
				case '+':
					vhdl_maketoken((INTBIG)TOKEN_PLUS, line_num, (INTBIG)space);
					c++;
					break;
				case ',':
					vhdl_maketoken((INTBIG)TOKEN_COMMA, line_num, (INTBIG)space);
					c++;
					break;
				case '-':
					if (*(c + 1) == '-')
					{
						/* got a comment, throw away rest of line */
						c = buffer + strlen(buffer);
					} else
					{
						/* got a minus sign */
						vhdl_maketoken((INTBIG)TOKEN_MINUS, line_num, (INTBIG)space);
						c++;
					}
					break;
				case '.':
					/* could be PERIOD or DOUBLEDOT */
					if (*(c + 1) == '.')
					{
						vhdl_maketoken((INTBIG)TOKEN_DOUBLEDOT, line_num, (INTBIG)space);
						c += 2;
					} else
					{
						vhdl_maketoken((INTBIG)TOKEN_PERIOD, line_num, (INTBIG)space);
						c++;
					}
					break;
				case '/':
					/* could be SLASH or NE */
					if (*(c + 1) == '=')
					{
						vhdl_maketoken((INTBIG)TOKEN_NE, line_num, (INTBIG)space);
						c += 2;
					} else
					{
						vhdl_maketoken((INTBIG)TOKEN_SLASH, line_num, (INTBIG)space);
						c++;
					}
					break;
				case ':':
					/* could be COLON or VARASSIGN */
					if (*(c + 1) == '=')
					{
						vhdl_maketoken((INTBIG)TOKEN_VARASSIGN, line_num, (INTBIG)space);
						c += 2;
					} else
					{
						vhdl_maketoken((INTBIG)TOKEN_COLON, line_num, (INTBIG)space);
						c++;
					}
					break;
				case ';':
					vhdl_maketoken((INTBIG)TOKEN_SEMICOLON, line_num, (INTBIG)space);
					c++;
					break;
				case '<':
					/* could be LT or LE or BOX */
					switch (*(c + 1))
					{
						case '=':
							vhdl_maketoken((INTBIG)TOKEN_LE, line_num, (INTBIG)space);
							c += 2;
							break;
						case '>':
							vhdl_maketoken((INTBIG)TOKEN_BOX, line_num, (INTBIG)space);
							c += 2;
							break;
						default:
							vhdl_maketoken((INTBIG)TOKEN_LT, line_num, (INTBIG)space);
							c++;
							break;
					}
					break;
				case '=':
					/* could be EQUAL or double delimiter ARROW */
					if (*(c + 1) == '>')
					{
						vhdl_maketoken((INTBIG)TOKEN_ARROW, line_num, (INTBIG)space);
						c += 2;
					} else
					{
						vhdl_maketoken((INTBIG)TOKEN_EQ, line_num, (INTBIG)space);
						c++;
					}
					break;
				case '>':
					/* could be GT or GE */
					if (*(c + 1) == '=')
					{
						vhdl_maketoken((INTBIG)TOKEN_GE, line_num, (INTBIG)space);
						c += 2;
					} else
					{
						vhdl_maketoken((INTBIG)TOKEN_GT, line_num, (INTBIG)space);
						c++;
					}
					break;
				case '|':
					vhdl_maketoken((INTBIG)TOKEN_VERTICALBAR, line_num, (INTBIG)space);
					c++;
					break;
				default:
	/*	AJ	vhdl_makestrtoken(TOKEN_UNKNOWN, c, 1, line_num, space); */
					vhdl_maketoken((INTBIG)TOKEN_UNKNOWN, line_num, (INTBIG)space);
					c++;
					break;
			}
		}
	}
}

/*
Module:  vhdl_makekeytoken
------------------------------------------------------------------------
Description:
	Add a token to the token list which has a key reference.
------------------------------------------------------------------------
Calling Sequence:  vhdl_makekeytoken(key, line_num, space);

Name		Type		Description
----		----		-----------
key			*VKEYWORD	Pointer to keyword in table.
line_num	INTBIG		Line number of occurence.
space		INTBIG		Previous space flag.
------------------------------------------------------------------------
*/
void vhdl_makekeytoken(VKEYWORD *key, INTBIG line_num, INTBIG space)
{
	TOKENLIST *newtoken;

	newtoken = (TOKENLIST *)emalloc((INTBIG)sizeof(TOKENLIST), vhdl_aid->cluster);
	newtoken->token = TOKEN_KEYWORD;
	newtoken->pointer = (char *)key;
	newtoken->line_num = line_num;
	newtoken->space = TRUE;
	newtoken->next = NOTOKENLIST;
	newtoken->last = vhdl_tlistend;
	if (vhdl_tlistend == NOTOKENLIST)
	{
		vhdl_tliststart = vhdl_tlistend = newtoken;
	} else
	{
		vhdl_tlistend->space = space;
		vhdl_tlistend->next = newtoken;
		vhdl_tlistend = newtoken;
	}
}

/*
Module:  vhdl_makeidenttoken
------------------------------------------------------------------------
Description:
	Add a identity token to the token list which has a string reference.
------------------------------------------------------------------------
Calling Sequence:  vhdl_makeidenttoken(str, length, line_num, space);

Name		Type		Description
----		----		-----------
str			*char		Pointer to start of string (not 0 term).
length		INTBIG		Length of string.
line_num	INTBIG		Line number of occurence.
space		INTBIG		Previous space flag.
------------------------------------------------------------------------
*/
void vhdl_makeidenttoken(char *str, INTBIG length, INTBIG line_num, INTBIG space)
{
	TOKENLIST *newtoken;
	char *newstring;
	IDENTTABLE *ikey;

	newtoken = (TOKENLIST *)emalloc((INTBIG)sizeof(TOKENLIST), vhdl_aid->cluster);
	newtoken->token = TOKEN_IDENTIFIER;
	newstring = (char *)emalloc((INTBIG)length + 1, vhdl_aid->cluster);
	strncpy(newstring, str, length);
	newstring[length] = 0;

	/* check if ident exits in the global name space */
	if ((ikey = vhdl_findidentkey(newstring)) == (IDENTTABLE *)0)
	{
		ikey = vhdl_makeidentkey(newstring);
		if (ikey == 0) return;
	} else efree(newstring);
	newtoken->pointer = (char *)ikey;
	newtoken->line_num = line_num;
	newtoken->space = TRUE;
	newtoken->next = NOTOKENLIST;
	newtoken->last = vhdl_tlistend;
	if (vhdl_tlistend == NOTOKENLIST)
	{
		vhdl_tliststart = vhdl_tlistend = newtoken;
	} else
	{
		vhdl_tlistend->space = space;
		vhdl_tlistend->next = newtoken;
		vhdl_tlistend = newtoken;
	}
}

/*
Module:  vhdl_makestrtoken
------------------------------------------------------------------------
Description:
	Add a string token to the token list.  Note that two adjacent double
	quotes should be mergered into one.
------------------------------------------------------------------------
Calling Sequence:  vhdl_makestrtoken(string, length, line_num, space);

Name		Type		Description
----		----		-----------
string		*char		Pointer to start of string (not 0 term).
length		INTBIG		Length of string.
line_num	INTBIG		Line number of occurence.
space		INTBIG		Previous space flag.
------------------------------------------------------------------------
*/
void vhdl_makestrtoken(char *string, INTBIG length, INTBIG line_num, INTBIG space)
{
	TOKENLIST *newtoken;
	char *newstring, *str, *str2;

	newtoken = (TOKENLIST *)emalloc((INTBIG)sizeof(TOKENLIST), vhdl_aid->cluster);
	newtoken->token = TOKEN_STRING;
	newstring = (char *)emalloc((INTBIG)length + 1, vhdl_aid->cluster);
	strncpy(newstring, string, length);
	newstring[length] = 0;

	/* merge two adjacent double quotes */
	str = newstring;
	while (*str)
	{
		if (*str == '"')
		{
			if (*(str + 1) == '"')
			{
				str2 = str + 1;
				do
				{
					*str2 = *(str2 + 1);
					str2++;
				} while (*str2 != 0);
			}
		}
		str++;
	}
	newtoken->pointer = newstring;
	newtoken->line_num = line_num;
	newtoken->space = TRUE;
	newtoken->next = NOTOKENLIST;
	newtoken->last = vhdl_tlistend;
	if (vhdl_tlistend == NOTOKENLIST)
	{
		vhdl_tliststart = vhdl_tlistend = newtoken;
	} else
	{
		vhdl_tlistend->space = space;
		vhdl_tlistend->next = newtoken;
		vhdl_tlistend = newtoken;
	}
}

/*
Module:  vhdl_makedecimaltoken
------------------------------------------------------------------------
Description:
	Add a numeric token to the token list which has a string reference.
------------------------------------------------------------------------
Calling Sequence:  vhdl_makedecimaltoken(string, length, line_num, space);

Name		Type		Description
----		----		-----------
string		*char		Pointer to start of string (not 0 term).
length		INTBIG		Length of string.
line_num	INTBIG		Line number of occurence.
space		INTBIG		Previous space flag.
------------------------------------------------------------------------
*/
void vhdl_makedecimaltoken(char *string, INTBIG length, INTBIG line_num,
	INTBIG space)
{
	TOKENLIST *newtoken;
	char *newstring;

	newtoken = (TOKENLIST *)emalloc((INTBIG)sizeof(TOKENLIST), vhdl_aid->cluster);
	newtoken->token = TOKEN_DECIMAL;
	newstring = (char *)emalloc((INTBIG)length + 1, vhdl_aid->cluster);
	strncpy(newstring, string, length);
	newstring[length] = 0;
	newtoken->pointer = newstring;
	newtoken->line_num = line_num;
	newtoken->space = TRUE;
	newtoken->next = NOTOKENLIST;
	newtoken->last = vhdl_tlistend;
	if (vhdl_tlistend == NOTOKENLIST)
	{
		vhdl_tliststart = vhdl_tlistend = newtoken;
	} else
	{
		vhdl_tlistend->space = space;
		vhdl_tlistend->next = newtoken;
		vhdl_tlistend = newtoken;
	}
}

/*
Module:  vhdl_maketoken
------------------------------------------------------------------------
Description:
	Add a token to the token list which has no string reference.
------------------------------------------------------------------------
Calling Sequence:  vhdl_maketoken(token, line_num, space);

Name		Type		Description
----		----		-----------
token		INTBIG		Token number.
line_num	INTBIG		Line number of occurence.
space		INTBIG		Previous space flag.
------------------------------------------------------------------------
*/
void vhdl_maketoken(INTBIG token, INTBIG line_num, INTBIG space)
{
	TOKENLIST *newtoken;

	newtoken = (TOKENLIST *)emalloc((INTBIG)sizeof(TOKENLIST), vhdl_aid->cluster);
	newtoken->token = token;
	newtoken->pointer = (char *)0;
	newtoken->line_num = line_num;
	newtoken->space = TRUE;
	newtoken->next = NOTOKENLIST;
	newtoken->last = vhdl_tlistend;
	if (vhdl_tlistend == NOTOKENLIST)
	{
		vhdl_tliststart = vhdl_tlistend = newtoken;
	} else
	{
		vhdl_tlistend->space = space;
		vhdl_tlistend->next = newtoken;
		vhdl_tlistend = newtoken;
	}
}

/*
Module:  vhdl_makechartoken
------------------------------------------------------------------------
Description:
	Make a character literal token.
------------------------------------------------------------------------
Calling Sequence:  vhdl_makechartoken(c, line_num, space);

Name		Type		Description
----		----		-----------
c			char		Character literal.
line_num	INTBIG		Number of source line.
space		INTBIG		Previous space flag.
------------------------------------------------------------------------
*/
void vhdl_makechartoken(char c, INTBIG line_num, INTBIG space)
{
	TOKENLIST *newtoken;

	newtoken = (TOKENLIST *)emalloc((INTBIG)sizeof(TOKENLIST), vhdl_aid->cluster);
	newtoken->token = TOKEN_CHAR;
	newtoken->pointer = (char *)((INTBIG)c);
	newtoken->line_num = line_num;
	newtoken->space = TRUE;
	newtoken->next = NOTOKENLIST;
	newtoken->last = vhdl_tlistend;
	if (vhdl_tlistend == NOTOKENLIST)
	{
		vhdl_tliststart = vhdl_tlistend = newtoken;
	} else
	{
		vhdl_tlistend->space = space;
		vhdl_tlistend->next = newtoken;
		vhdl_tlistend = newtoken;
	}
}

void vhdl_freescannermemory(void)
{
	TOKENLIST *tok, *nexttok;
	REGISTER INTSML i;

	/* free parsed tokens */
	for(tok = vhdl_tliststart; tok != NOTOKENLIST; tok = nexttok)
	{
		nexttok = tok->next;
		if (tok->token == TOKEN_STRING || tok->token == TOKEN_DECIMAL)
			efree(tok->pointer);
		efree((char *)tok);
	}
	vhdl_tliststart = vhdl_tlistend = NOTOKENLIST;

	/* clear identifier table */
	if (vhdl_identtable != 0)
	{
		for (i = 0; i < IDENT_TABLE_SIZE; i++)
			if (vhdl_identtable[i].string != 0)
		{
			efree(vhdl_identtable[i].string);
			vhdl_identtable[i].string = 0;
		}
	}
}

/*
Module:  vhdl_iskeyword
------------------------------------------------------------------------
Description:
	If the passed string is a keyword, return its address in the
	keyword table, else return NOVKEYWORD.
------------------------------------------------------------------------
Calling Sequence:  key = vhdl_iskeyword(string, length);

Name		Type		Description
----		----		-----------
string		*char		Pointer to start of string (not 0 term).
length		INTBIG		Length of string.
key			*VKEYWORD	Pointer to entry in keywords table if
								keyword, else NOVKEYWORD.
------------------------------------------------------------------------
*/
VKEYWORD *vhdl_iskeyword(char *string, INTBIG length)
{
	INTBIG aindex, num, check, base;
	char tstring[MAXVHDLLINE];

	if (length >= MAXVHDLLINE) return(NOVKEYWORD);
	for (aindex = 0; aindex < length; aindex++)
		tstring[aindex] = string[aindex];
	tstring[length] = 0;
	base = 0;
	num = sizeof(vhdl_keywords) / sizeof(VKEYWORD);
	aindex = num >> 1;
	while (num)
	{
		check = namesame(tstring, vhdl_keywords[base + aindex].name);
		if (check == 0) return(&vhdl_keywords[base + aindex]);
		if (check < 0)
		{
			num = aindex;
			aindex = num >> 1;
		} else
		{
			base += aindex + 1;
			num -= aindex + 1;
			aindex = num >> 1;
		}
	}
	return(NOVKEYWORD);
}

/*
Module:  vhdl_findidentkey
------------------------------------------------------------------------
Description:
	Search for the passed string in the global name space.  Return
	a pointer to the ident table entry if found or 0 if not
	found.
------------------------------------------------------------------------
Calling Sequence:  ikey = vhdl_findidentkey(string);

Name		Type		Description
----		----		-----------
string		*char		Pointer to string of name.
ikey		*IDENTTABLE	Pointer to entry in table if found,
								or 0 if not found.
------------------------------------------------------------------------
*/
IDENTTABLE *vhdl_findidentkey(char *string)
{
	INTBIG attempt;
	char *ident;

	attempt = vhdl_identfirsthash(string);
	while ((ident = vhdl_identtable[attempt].string))
	{
		if (namesame(string, ident) == 0) return(&(vhdl_identtable[attempt]));
		attempt = vhdl_identsecondhash(string, attempt);
	}
	return((IDENTTABLE *)0);
}

/*
Module:  vhdl_makeidentkey
------------------------------------------------------------------------
Description:
	Make an entry for the passed string in the global name space.
	Return a pointer to the ident table entry created.
	Returns zero on error.
------------------------------------------------------------------------
Calling Sequence:  ikey = vhdl_makeidentkey(string);

Name		Type		Description
----		----		-----------
string		*char		Pointer to string of name.
ikey		*IDENTTABLE	Pointer to entry in table created.
------------------------------------------------------------------------
*/
IDENTTABLE *vhdl_makeidentkey(char *string)
{
	INTBIG attempt, count;

	count = 0;
	attempt = vhdl_identfirsthash(string);
	while (vhdl_identtable[attempt].string)
	{
		if (++count >= MAX_HASH_TRYS)
		{
			ttyputmsg("ERROR hashing identifier - tried %ld times", count);
			return(0);
		}
		attempt = vhdl_identsecondhash(string, attempt);
	}
	vhdl_identtable[attempt].string = string;
	return(&vhdl_identtable[attempt]);
}

/*
Module:  vhdl_identfirsthash
------------------------------------------------------------------------
Description:
	Return the hash value for the passed string.  The first hash
	function is:

		value = ( (firstchar << 7) + (lastchar << 4) + length ) mod
				IDENT_TABLE_SIZE;

	Note:  lowercase letters are converted to uppercase.
------------------------------------------------------------------------
Calling Sequence:  value = vhdl_identfirsthash(string);

Name		Type		Description
----		----		-----------
string		*char		Pointer to string to hash.
value		INTBIG		Returned hash value.
------------------------------------------------------------------------
*/
INTBIG vhdl_identfirsthash(char *string)
{
	INTBIG length, value;

	length = (INTBIG)strlen(string);
	value = ((vhdl_toupper(*string) << 7) + (vhdl_toupper(string[length - 1]) << 4) + length) %
		IDENT_TABLE_SIZE;
	return(value);
}

/*
Module:  vhdl_identsecondhash
------------------------------------------------------------------------
Description:
	Return the hash value for the passed string.  The second hash
	function is:

		value = (previous_hash +  ( (summation of characters) << 4) +
				(length of string)) mod IDENT_TABLE_SIZE;
------------------------------------------------------------------------
Calling Sequence:  value = vhdl_identsecondhash(string, first_hash);

Name			Type		Description
----			----		-----------
string			*char		Pointer to string to hash.
previous_hash	INTBIG		Value of previous hash.
value			INTBIG		Returned hash value.
------------------------------------------------------------------------
*/
INTBIG vhdl_identsecondhash(char *string, INTBIG previous_hash)
{
	INTBIG length, value;
	char *sptr;

	value = 0;
	for (sptr = string; *sptr; sptr++)
		value += vhdl_toupper(*sptr);
	value <<= 4;
	length = (INTBIG)strlen(string);
	value = (previous_hash + value + length) % IDENT_TABLE_SIZE;
	return(value);
}

/*
Module:  vhdl_toupper
------------------------------------------------------------------------
Description:
	Convert a character to uppercase if it is a lowercase letter only.
------------------------------------------------------------------------
Calling Sequence:  c = vhdl_toupper(c);

Name		Type		Description
----		----		-----------
c			char		Character to be converted.
------------------------------------------------------------------------
*/
char vhdl_toupper(char c)
{
	if (islower(c))
		c = toupper(c);
	return(c);
}

/*
Module:  vhdl_findtopinterface
------------------------------------------------------------------------
Description:
	Find the top interface in the database.  The top interface is defined
	as the interface is called by no other architectural bodies.
------------------------------------------------------------------------
Calling Sequence:  top_interface = vhdl_findtopinterface(units);

Name			Type		Description
----			----		-----------
units			*DBUNITS	Pointer to database design units.
top_interface	*DBINTERFACE	Pointer to top interface.
------------------------------------------------------------------------
*/
DBINTERFACE *vhdl_findtopinterface(DBUNITS *units)
{
	DBINTERFACE *interfacef;
	DBBODY *body;
	DBCOMPONENTS *compo;
	SYMBOLTREE *symbol;

	/* clear flags of all interfaces in database */
	for (interfacef = units->interfaces; interfacef != NULL; interfacef = interfacef->next)
		interfacef->flags &= ~TOP_ENTITY_FLAG;

	/* go through the list of bodies and flag any interfaces */
	for (body = units->bodies; body != NULL; body = body->next)
	{
		/* go through component list */
		if (body->declare == 0) continue;
		for (compo = body->declare->components; compo != NULL; compo = compo->next)
		{
			symbol = vhdl_searchsymbol(compo->name, vhdl_gsymbols);
			if (symbol != NULL && symbol->pointer != NULL)
			{
				((DBINTERFACE *)(symbol->pointer))->flags |= TOP_ENTITY_FLAG;
			}
		}
	}

	/* find interface with the flag bit not set */
	for (interfacef = units->interfaces; interfacef != NULL; interfacef = interfacef->next)
	{
		if ( !(interfacef->flags & TOP_ENTITY_FLAG) ) break;
	}
	return(interfacef);
}

void vhdl_freeunresolvedlist(UNRESLIST **lstart)
{
	UNRESLIST *unres;

	while (*lstart != 0)
	{
		unres = *lstart;
		*lstart = (*lstart)->next;
		efree((char *)unres);
	}
}

/*
Module:  vhdl_unresolved
------------------------------------------------------------------------
Description:
	Maintain a list of unresolved interfaces for later reporting.
------------------------------------------------------------------------
Calling Sequence:  vhdl_unresolved(iname, lstart);

Name		Type		Description
----		----		-----------
iname		*IDENTTABLE	Pointer to global name space.
lstart		**UNRESLIST	Address of start of list pointer;
------------------------------------------------------------------------
*/
void vhdl_unresolved(IDENTTABLE *iname, UNRESLIST **lstart)
{
	UNRESLIST *ulist;

	for (ulist = *lstart; ulist != NULL; ulist = ulist->next)
	{
		if (ulist->interfacef == iname) break;
	}
	if (ulist) ulist->numref++; else
	{
		ulist = (UNRESLIST *)emalloc((INTBIG)sizeof(UNRESLIST), vhdl_aid->cluster);
		ulist->interfacef = iname;
		ulist->numref = 1;
		ulist->next = *lstart;
		*lstart = ulist;
	}
}

/****************************** VHDL OPTIONS DIALOG ******************************/

/*
 * special case for the "VHDL Options" dialog
 * VHDL stored in facet     = 3 (check)
 * Netlist stored in facet  = 4 (check)
 * List of schematics prims = 5 (scroll)
 * Unnegated name           = 8 (edit text)
 * Negated name             = 9 (edit text)
 */
void vhdl_optionsdlog(void)
{
	INTBIG itemHit;
	REGISTER char **varnames, *pt, *opt;
	REGISTER VARIABLE *var;
	REGISTER TECHNOLOGY *tech;
	REGISTER NODEPROTO *np, **nplist;
	REGISTER INTBIG i, len, vhdlondisk, netlistondisk;
	extern AIDENTRY *vhdl_aid;

	if (DiaInitDialog(&vhdl_optionsdialog) != 0) return;
	var = getval((INTBIG)vhdl_aid, VAID, VINTEGER, "VHDL_vhdl_on_disk");
	if (var == NOVARIABLE) vhdlondisk = 0; else
		vhdlondisk = var->addr;
	var = getval((INTBIG)vhdl_aid, VAID, VINTEGER, "VHDL_netlist_on_disk");
	if (var == NOVARIABLE) netlistondisk = 0; else
		netlistondisk = var->addr;

	/* setup for generated names in the technology */
	tech = sch_tech;
	DiaInitTextDialog(5, DiaNullDlogList, DiaNullDlogItem, DiaNullDlogDone, 0,
		SCSELMOUSE|SCREPORT);
	var = getval((INTBIG)tech, VTECHNOLOGY, VSTRING|VISARRAY, "TECH_vhdl_names");
	if (var == NOVARIABLE)
	{
		for(len=0, np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto, len++) ;
		varnames = (char **)emalloc(len * (sizeof (char *)), el_tempcluster);
		for(i=0, np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto, i++)
		{
			varnames[i] = (char *)emalloc(1, el_tempcluster);
			varnames[i][0] = 0;
		}
	} else
	{
		len = getlength(var);
		varnames = (char **)emalloc(len * (sizeof (char *)), el_tempcluster);
		for(i=0; i<len; i++)
		{
			pt = ((char **)var->addr)[i];
			varnames[i] = (char *)emalloc(strlen(pt)+1, el_tempcluster);
			strcpy(varnames[i], pt);
		}
	}
	nplist = (NODEPROTO **)emalloc(i * (sizeof (NODEPROTO *)), el_tempcluster);
	for(i=0, np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto, i++)
		nplist[i] = np;
	for(i=0; i<len; i++)
	{
		(void)initinfstr();
		(void)addstringtoinfstr(nplist[i]->primname);
		if (*varnames[i] != 0)
		{
			(void)addstringtoinfstr(" (");
			(void)addstringtoinfstr(varnames[i]);
			(void)addstringtoinfstr(")");
		}
		DiaStuffLine(5, returninfstr());
	}
	DiaSelectLine(5, 0);

	if (vhdlondisk == 0) DiaSetControl(3, 1);
	if (netlistondisk == 0) DiaSetControl(4, 1);

	/* loop until done */
	for(;;)
	{
		itemHit = DiaNextHit();
		if (itemHit == OK || itemHit == CANCEL) break;
		if (itemHit >= 3 && itemHit <= 4)
		{
			DiaSetControl(itemHit, 1 - DiaGetControl(itemHit));
			continue;
		}
		if (itemHit == 5)
		{
			DiaSetText(8, "");
			DiaSetText(9, "");
			i = DiaGetCurLine(5);
			pt = varnames[i];
			for(opt = pt; *opt != 0; opt++) if (*opt == '/') break;
			if (*opt == '/')
			{
				*opt = 0;
				DiaSetText(8, pt);
				DiaSetText(9, &opt[1]);
				*opt = '/';
			} else
			{
				DiaSetText(8, pt);
				DiaSetText(9, pt);
			}
			continue;
		}
		if (itemHit == 8 || itemHit == 9)
		{
			i = DiaGetCurLine(5);
			(void)initinfstr();
			(void)addstringtoinfstr(DiaGetText(8));
			(void)addtoinfstr('/');
			(void)addstringtoinfstr(DiaGetText(9));
			efree(varnames[i]);
			pt = returninfstr();
			varnames[i] = (char *)emalloc(strlen(pt)+1, el_tempcluster);
			strcpy(varnames[i], pt);

			(void)initinfstr();
			(void)addstringtoinfstr(nplist[i]->primname);
			if (*varnames[i] != 0)
			{
				(void)addstringtoinfstr(" (");
				(void)addstringtoinfstr(varnames[i]);
				(void)addstringtoinfstr(")");
			}
			DiaSetScrollLine(5, i, returninfstr());
			continue;
		}
	}

	if (itemHit != CANCEL)
	{
		if (DiaGetControl(3) != 0) vhdlondisk = 0; else
			vhdlondisk = 1;
		setval((INTBIG)vhdl_aid, VAID, "VHDL_vhdl_on_disk", vhdlondisk, VINTEGER);
		if (DiaGetControl(4) != 0) netlistondisk = 0; else
			netlistondisk = 1;
		setval((INTBIG)vhdl_aid, VAID, "VHDL_netlist_on_disk", netlistondisk, VINTEGER);
		setval((INTBIG)tech, VTECHNOLOGY, "TECH_vhdl_names", (INTBIG)varnames,
			VSTRING|VISARRAY|(len<<VLENGTHSH));
	}
	DiaDoneDialog();
	for(i=0; i<len; i++) efree(varnames[i]);
	efree((char *)varnames);
	efree((char *)nplist);
}

#endif  /* VHDLAID - at top */
