/*
 * Electric(tm) VLSI Design System
 *
 * File: planprog1.c
 * Programmable Logic Array Generator, part I
 * Written by: Sundaravarathan R. Iyengar, Schlumberger Palo Alto Research
 *
 * 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
 */

/*
 * Module to generate PLA layouts.  Input in a truth table format
 * is read from a file.  The output is in the form of facet prototypes
 * defined in the current library.
 */

#include "config.h"
#if PLAAID

#include "global.h"
#include "planmos.h"

/* prototypes for local routines */
PROGTRANS *pla_FindPColTrans(INTSML, PROGTRANS*);
FACETIST *pla_PlaceInputs(INTBIG, INTBIG, INTBIG, NODEPROTO*);
FACETIST *pla_PlaceOutputs(INTBIG, INTBIG, INTBIG, NODEPROTO*);
FACETIST *pla_PlacePullupsonAND(INTBIG, INTBIG, INTBIG, NODEPROTO*);
FACETIST *pla_PlacePullupsonOR(INTBIG, INTBIG, INTBIG, INTBIG, INTBIG, NODEPROTO*);
PROGTRANS *pla_Program(INTBIG, INTBIG, INTBIG, NODEPROTO*);
PROGTRANS *pla_ProgramAND(INTBIG, INTBIG, INTBIG, INTBIG, INTBIG, INTBIG, INTBIG);
PROGTRANS *pla_ProgramOR(INTBIG, INTBIG, INTBIG, INTBIG, INTBIG);
INTSML pla_ProgramPLA(INTBIG, INTBIG, INTBIG, INTBIG, INTBIG, INTBIG, INTBIG, INTBIG, NODEPROTO*);

/*
 * Make the PLA.  First define all the primitives and then
 * call pla_ProgramPLA to program the planes.
 */
INTSML pla_Make(void)
{
	INTBIG inX, inY,               /* Dimensions of Input facet */
		  puX, puY,               /* Dimensions of Double Pullup facet */
		  outX, outY,             /* Dimensions of Output facet */
		  progX, progY,           /* Dimensions of Programming Transistor */
		  connectX, connectY;     /* Dimensions of Connect facet */
	INTSML pos;
	char keyword[32], *filename;
	TECHNOLOGY *tech;

	if (pla_fin != NULL) xclose(pla_fin);
	pla_fin = xopen(pla_infile, FILETYPEPLATAB, "", &filename);
	if (pla_fin == NULL)
	{
		ttyputerr("File '%s' not found, use 'tellaid pla nmos file FILE'", pla_infile);
		return(ERRORRET);
	}
	if (pla_verbose == ON)
		ttyputmsg("File '%s' has been opened for reading", pla_infile);

	/* Set the Technology to the Default Technology (nmos) */
	tech = gettechnology(PLADEFTECH);
	if (tech == NOTECHNOLOGY)
	{
		ttyputerr("'%s' is an undefined technology", PLADEFTECH);
		return(ERRORRET);
	}
	if (el_curtech != tech)
		ttyputmsg("Warning: current technology should be %s", tech->techname);

	pla_lam = tech->deflambda;

	/* Compute the widths of Vdd and Gnd lines */
	if (pla_VddWidth < 4*pla_lam) pla_VddWidth = 4 * pla_lam;
	if (pla_GndWidth < 4*pla_lam) pla_GndWidth = 4 * pla_lam;
	pla_halfVdd = pla_VddWidth / 2;
	pla_halfGnd = pla_GndWidth / 2;

	/* Search for the prototypes for PLA generation */
	pla_md_proto = getnodeproto("nmos:metal-diffusion-con");
	pla_mp_proto = getnodeproto("nmos:metal-polysilicon-con");
	pla_dp_proto = getnodeproto("nmos:diffusion-pin");
	pla_bp_proto = getnodeproto("nmos:metal-pin");
	pla_pp_proto = getnodeproto("nmos:polysilicon-pin");
	pla_etran_proto = getnodeproto("nmos:transistor");
	pla_dtran_proto = getnodeproto("nmos:implant-transistor");
	if (pla_buttingcontact) pla_bc_proto = getnodeproto("nmos:butting-con");
		else pla_bc_proto = getnodeproto("nmos:buried-con-cross-t");
	pla_ma_proto = getarcproto("nmos:metal");
	pla_da_proto = getarcproto("nmos:diffusion");
	pla_pa_proto = getarcproto("nmos:polysilicon");
	if (pla_md_proto    == NONODEPROTO || pla_dp_proto  == NONODEPROTO ||
		pla_mp_proto    == NONODEPROTO || pla_pp_proto  == NONODEPROTO ||
		pla_da_proto    == NOARCPROTO  || pla_pa_proto  == NOARCPROTO  ||
		pla_dtran_proto == NONODEPROTO || pla_ma_proto  == NOARCPROTO  ||
		pla_etran_proto == NONODEPROTO || pla_bc_proto  == NONODEPROTO ||
		pla_bp_proto    == NONODEPROTO)
	{
		ttyputerr("Cannot find primitive prototypes of nmos technology");
		return(ERRORRET);
	}

	/* Create all the facet prototypes.  Facets are defined in planfacets.c */
	pla_pu_proto = pla_nmos_Pullup();
	if (pla_pu_proto  == NONODEPROTO)
	{
		ttyputerr("Cannot create pullup facet");
		return(ERRORRET);
	}
	puX = pla_pu_proto->highx - pla_pu_proto->lowx;
	puY = pla_pu_proto->highy - pla_pu_proto->lowy;

	pla_in_proto = pla_nmos_Input();
	if (pla_in_proto == NONODEPROTO)
	{
		ttyputerr("Cannot create input facet");
		return(ERRORRET);
	}
	inX = pla_in_proto->highx - pla_in_proto->lowx;
	inY = pla_in_proto->highy - pla_in_proto->lowy;

	pla_connect_proto = pla_nmos_Connect();
	if (pla_connect_proto == NONODEPROTO)
	{
		ttyputerr("Cannot create connect facet");
		return(ERRORRET);
	}
	connectX = pla_connect_proto->highx - pla_connect_proto->lowx;
	connectY = pla_connect_proto->highy - pla_connect_proto->lowy;

	pla_prog_proto = pla_nmos_Program();
	if (pla_prog_proto== NONODEPROTO)
	{
		ttyputerr("Cannot create program facet");
		return(ERRORRET);
	}
	progX = pla_prog_proto->highx - pla_prog_proto->lowx;
	progY = pla_prog_proto->highy - pla_prog_proto->lowy;

	pla_out_proto = pla_nmos_Output();
	if (pla_out_proto == NONODEPROTO)
	{
		ttyputerr("Cannot create output facet");
		return(ERRORRET);
	}
	outX = pla_out_proto->highx - pla_out_proto->lowx;
	outY = pla_out_proto->highy - pla_out_proto->lowy;

	if (pla_verbose == ON)
	{
		ttyputmsg("Facets have been defined with sizes given below:");
		ttyputmsg("Pullup  = %sx%s", latoa(puX), latoa(puY));
		ttyputmsg("Input   = %sx%s", latoa(inX), latoa(inY));
		ttyputmsg("Output  = %sx%s", latoa(outX), latoa(outY));
		ttyputmsg("Program = %sx%s", latoa(progX), latoa(progY));
		ttyputmsg("Connect = %sx%s", latoa(connectX), latoa(connectY));
		ttyputmsg("VDD Width = %s, Gnd Width = %s", latoa(pla_VddWidth), latoa(pla_GndWidth));
	}

	/* Create the pla prototype with these dimensions.
	 * First find a name for the PLA.
	 */
	while(1)
	{
		if (pla_ProcessFlags() == ERRORRET) return(OK);

		pla_facet = NONODEPROTO;
		pla_alv = pla_atg = pla_arg = pla_otv = NOVDDGND;
		pla_org = pla_icg = pla_icv = pla_ocg = pla_ocv = NOVDDGND;

		if (pla_inputs == 0)
		{
			ttyputerr("Use 'tellaid pla nmos inputs VALUE' to set inputs");
			return(ERRORRET);
		}
		if (pla_outputs == 0)
		{
			ttyputerr("Use 'tellaid pla nmos outputs VALUE' to set outputs");
			return(ERRORRET);
		}
		if (pla_pterms == 0)
		{
			ttyputerr("Use 'tellaid pla nmos pterms VALUE' to set prod. terms");
			return(ERRORRET);
		}
		if (namesame(pla_name, DEFNAME) == 0)
			(void)sprintf(pla_name, "PLA-%d-%d-%d", pla_inputs, pla_pterms, pla_outputs);

		pla_facet = newnodeproto(pla_name, el_curlib);
		if (pla_facet == NONODEPROTO)
		{
			ttyputerr("Cannot create '%s' prototype", pla_name);
			return(ERRORRET);
		}

		ttyputmsg("Defining '%s'", pla_name);
		if (pla_verbose == ON)
			ttyputmsg("Beginning to program the AND and OR planes.");
		if (pla_ProgramPLA(inX, inY, puX, puY, outX, outY, connectX,
			connectY, pla_facet) == ERRORRET)
				ttyputerr("PLA '%s' is incompletely defined", pla_name);

		while((pos = pla_GetKeyWord(keyword)) != ERRORRET)
			if (pos == END) break;
	}
}

/*
 * pla_FindPColTrans: Given a pterm, and the address of an input facet,
 * it tries to find a point below the row indicated by the pterm,
 * which needs to be connected to the programming transistor to be
 * placed on the pterm row.
 */
PROGTRANS *pla_FindPColTrans(INTSML pterm, PROGTRANS *trans)
{
	INTSML column;

	column = trans->column;

	while (trans->uprogtrans != NOPROGTRANS)
	{
		if (trans->column != column)
			ttyputerr("FindPColTrans: Column mismatch: wanted %d, got %d/%d",
				column, trans->column, trans->row);
		trans = trans->uprogtrans;
	}

	if (trans->row >= pterm)
	{
		ttyputerr("FindPColTrans: Error.  Gone past row %d in column %d", pterm, trans->column);
		ttyputerr("   Last progtrans in this column is at row=%d", trans->row);
		return(NOPROGTRANS);
	}
	return(trans);
}

/*
 * Place the Input facets one for each input.  The left most
 * facet is placed at X = X length of pullup facets and
 * Y = 0.  Also we have to remember the addresses of the facets
 * for later processing.
 */
FACETIST *pla_PlaceInputs(INTBIG puX, INTBIG inX, INTBIG inY, NODEPROTO *pla)
{
	FACETIST *ptr, *prevptr, *firstptr;
	VDDGND *cicv, *picv, *cicg, *picg;
	INTSML i;
	INTBIG llXpos, llYpos, VddYpos, GndYpos;
	char portname[16];

	llXpos = puX;
	llYpos = 0 - (pla_VddWidth - 4*pla_lam);
	VddYpos = llYpos + 17*pla_lam + pla_halfVdd;
	GndYpos = llYpos + inY - pla_halfGnd;

	prevptr = NOFACETIST;
	firstptr= NOFACETIST;
	cicv = picv = cicg = picg = NOVDDGND;

	if (pla_verbose == ON) ttyputmsg("Placing the input facets");

	for(i = 1; i <= pla_inputs; i++)
	{
		if (pla_verbose == ON) ttyputmsg("Placing input facet %d", i);
		ptr = (FACETIST *)emalloc(sizeof(FACETIST), pla_aid->cluster);
		if (ptr == NOFACETIST)
		{
			ttyputerr("PlaceInputs: ran out of storage space. i=%d", i);
			break;
		} else if (ptr == NOFACETIST) break;
		ptr->findex = i;    /* remember which input this facet corresponds to */
		ptr->facetinst = newnodeinst(pla_in_proto, llXpos, llXpos+inX,
			llYpos, llYpos+inY, NOTRANS, NOROT, pla);
		if (ptr->facetinst == NONODEINST)
			ttyputerr("PlaceInputs: cannot instance input# %d", i);

		/* Record the Gnd and Vdd points for later connection */
		cicg = pla_MkVddGnd(INFACETGND, &picg);
		cicg->inst = ptr->facetinst;
		cicg->port = pla_AssignPort(ptr->facetinst, llXpos+6*pla_lam, GndYpos);
		if (cicg->port->port == NOPORTPROTO)
			ttyputerr("PlaceInputs: can't access Gnd contact");

		cicv = pla_MkVddGnd(INFACETVDD, &picv);
		cicv->inst = ptr->facetinst;
		cicv->port = pla_AssignPort(ptr->facetinst, llXpos+5*pla_lam, VddYpos);
		if (cicv->port->port == NOPORTPROTO)
			ttyputerr("PlaceInputs: can't access Vdd contact");

		/* Find the ports that connect into the PLA */
		ptr->lport = pla_AssignPort(ptr->facetinst, llXpos+2*pla_lam, llYpos+inY-pla_lam);
		ptr->rport = pla_AssignPort(ptr->facetinst, llXpos+10*pla_lam, llYpos+inY-pla_lam);
		ptr->gport = pla_AssignPort(ptr->facetinst, llXpos+6*pla_lam, GndYpos);

		if (ptr->lport->port == NOPORTPROTO ||
			ptr->rport->port == NOPORTPROTO || ptr->gport->port == NOPORTPROTO)
				ttyputerr("PlaceInputs: cannot access ports into PLA");

		/* Initialize the progtrans ends */
		ptr->rcoltrans = ptr->lcoltrans = NOPROGTRANS;

		/* Export the input point giving it a name */
		(void)sprintf(portname, "Input%d", i);
		if (newportproto(pla, ptr->facetinst, pla_FindPort(ptr->facetinst,llXpos+10*pla_lam,
			llYpos+pla_lam), portname) == NOPORTPROTO)
				ttyputerr("PlaceInputs: cannot export port Input%d", i);

		if (prevptr != NOFACETIST) prevptr->nextfacet = ptr;
		prevptr = ptr;

		if (firstptr == NOFACETIST) firstptr = ptr;

		llXpos = llXpos + inX;
	}
	if (ptr != NOFACETIST) ptr->nextfacet = NOFACETIST;

	return(firstptr);
}

FACETIST *pla_PlaceOutputs(INTBIG orLeft, INTBIG outX, INTBIG outY, NODEPROTO *pla)
{
	FACETIST *ptr, *prevptr, *firstptr;
	VDDGND *cocv, *pocv, *cocg, *pocg;
	INTSML o;
	INTBIG llXpos, llYpos, VddYpos, GndYpos;
	char portname[16];

	llXpos = orLeft - pla_lam;
	llYpos = -(pla_VddWidth + 4 * pla_lam);
	VddYpos = llYpos + 7 * pla_lam + pla_halfVdd;
	GndYpos = llYpos + outY - (8 * pla_lam + pla_halfGnd);

	prevptr = firstptr = NOFACETIST;
	cocv = pocv = cocg = pocg = NOVDDGND;

	if (pla_verbose == ON) ttyputmsg("Placing the output facets");

	for(o = 1; o <= (pla_outputs+pla_outputs%2)/2; o++)
	{
		if (pla_verbose == ON) ttyputmsg("Placing output facet %d", o);

		ptr = (FACETIST *)emalloc(sizeof(FACETIST), pla_aid->cluster);
		if (ptr == NOFACETIST)
		{
			ttyputerr("PlaceOutputs: ran out of storage space. o=%d", o);
			break;
		} else if (ptr == NOFACETIST) break;
		ptr->findex = o;
		ptr->facetinst = newnodeinst(pla_out_proto, llXpos, llXpos+outX,
			llYpos, llYpos+outY, NOTRANS, NOROT, pla);
		if (ptr->facetinst == NONODEINST)
			ttyputerr("PlaceOutputs: cannot instance output# %d", o);

		/*
		 * Connect the facets together in metal on the Gnd line if more than
		 * one facet is placed
		 */
		/* Remember the Gnd and Vdd points for later connection */
		cocg = pla_MkVddGnd(OUTFACETGND, &pocg);
		cocg->inst = ptr->facetinst;
		cocg->port = pla_AssignPort(ptr->facetinst, llXpos+outX-5*pla_lam, GndYpos);
		if (cocg->port->port == NOPORTPROTO)
			ttyputerr("PlaceOutputs: can't access Gnd contact");

		cocv = pla_MkVddGnd(OUTFACETVDD, &pocv);
		cocv->inst = ptr->facetinst;
		cocv->port = pla_AssignPort(ptr->facetinst, llXpos+5*pla_lam, VddYpos);
		if (cocv->port->port == NOPORTPROTO)
			ttyputerr("PlaceOutputs: can't access Vdd contact");

		/* Find the ports that connect into the PLA */
		ptr->lport = pla_AssignPort(ptr->facetinst, llXpos+5*pla_lam, llYpos+outY-2*pla_lam);
		ptr->rport = pla_AssignPort(ptr->facetinst, llXpos+outX-2*pla_lam, llYpos+outY-2*pla_lam);
		ptr->gport = NOPORT;

		if (ptr->lport->port == NOPORTPROTO || ptr->rport->port == NOPORTPROTO)
			ttyputerr("PlaceOutputs: cant access ports to connect into PLA");

		/* Initialize the progtrans ends */
		ptr->rcoltrans = ptr->lcoltrans = NOPROGTRANS;

		/* Export the output ports */
		(void)sprintf(portname, "Output%d", 2*o-1);
		if (newportproto(pla, ptr->facetinst,
			pla_FindPort(ptr->facetinst,llXpos+pla_lam,llYpos+pla_lam), portname) == NOPORTPROTO)
				ttyputerr("PlaceOutputs: cant export port Output%d", 2*o-1);
		(void)sprintf(portname, "Output%d", 2*o);
		if (newportproto(pla, ptr->facetinst,
			pla_FindPort(ptr->facetinst,llXpos+11*pla_lam, llYpos+pla_lam), portname) == NOPORTPROTO)
				ttyputerr("PlaceOutputs: cannot export port Output%d", 2*o);

		if (prevptr != NOFACETIST) prevptr->nextfacet = ptr;
		prevptr = ptr;

		if (firstptr == NOFACETIST) firstptr = ptr;

		llXpos = llXpos + outX + pla_lam;
	}
	if (ptr != NOFACETIST) ptr->nextfacet = NOFACETIST;

	return(firstptr);
}

FACETIST *pla_PlacePullupsonAND(INTBIG puX, INTBIG puY, INTBIG inY, NODEPROTO *pla)
{
	FACETIST *ptr, *prevptr, *firstptr;
	VDDGND *calv, *palv;
	INTBIG llXpos, llYpos;
	INTSML p;

	llXpos = 0;
	llYpos = inY + 2*pla_lam - (pla_VddWidth - 4*pla_lam);

	prevptr = NOFACETIST;
	firstptr= NOFACETIST;
	calv = palv = NOVDDGND;

	if (pla_verbose == ON)
		ttyputmsg("Placing the pullup facets on the left edge of the AND-plane");

	for(p = 1; p <= pla_pterms; p++)
	{
		if (pla_verbose == ON)
			ttyputmsg("Placing pullup facet %d in AND-plane", p);

		ptr = (FACETIST *)emalloc(sizeof(FACETIST), pla_aid->cluster);
		if (ptr == NOFACETIST)
		{
			ttyputerr("PlacePullupsonAND: ran out of storage space, p=%d", p);
			break;
		} else if (ptr == NOFACETIST) break;
		ptr->findex = p;
		ptr->facetinst = newnodeinst(pla_pu_proto, llXpos, llXpos+puX,
			llYpos, llYpos+puY, NOTRANS, NOROT, pla);
		if (ptr->facetinst == NONODEINST)
			ttyputerr("PlacePullupsonAND: cannot instance pu# %d", p);

		/* Remember the Vdd points for later connection */
		calv = pla_MkVddGnd(ANDLEFTVDD, &palv);
		calv->inst = ptr->facetinst;
		calv->port = pla_AssignPort(ptr->facetinst, pla_halfVdd, llYpos+4*pla_lam);
		if (calv->port->port == NOPORTPROTO)
			ttyputerr("PlacePullupsonAND: can't access Vdd contact");

		/* Mark the diffusion end of the pu for connections across the plane */
		ptr->lport = pla_AssignPort(ptr->facetinst, llXpos+puX-2*pla_lam,llYpos+3*pla_lam);
		if (ptr->lport->port == NOPORTPROTO)
			ttyputerr("PlacePullupsonAND: cannot access bc port of pullup");

		ptr->rport = NOPORT;
		ptr->rcoltrans = ptr->lcoltrans = NOPROGTRANS;

		if (prevptr != NOFACETIST) prevptr->nextfacet = ptr;
		prevptr = ptr;

		if (firstptr == NOFACETIST) firstptr = ptr;

		llYpos = llYpos + puY;
	}
	if (ptr != NOFACETIST) ptr->nextfacet = NOFACETIST;

	return(firstptr);
}

FACETIST *pla_PlacePullupsonOR(INTBIG andY, INTBIG orLeft, INTBIG inY, INTBIG puX,
	INTBIG puY, NODEPROTO *pla)
{
	FACETIST *ptr, *prevptr, *firstptr;
	VDDGND *cotv, *potv;
	INTBIG llXpos, llYpos, VddYpos, cX, cY, bc_portY;
	INTSML i;

	inY = inY - (pla_VddWidth - 4*pla_lam); /* Compensate for Extra Vdd Width */
	cX = orLeft + 5*pla_lam;	      /* Center coordinates */
	cY = inY + andY + puX/2 + (pla_GndWidth-4*pla_lam);

	llXpos = cX - puX/2;
	llYpos = cY - puY/2;
	VddYpos = cY + puX/2 - pla_halfVdd;
	bc_portY = cY - puX/2 + 2*pla_lam;

	prevptr = firstptr = NOFACETIST;
	cotv = potv = NOVDDGND;

	if (pla_verbose == ON)
		ttyputmsg("Placing the pullups on the %s edge of the OR-plane",
			(pla_samesideoutput == ON) ? "top" : "bottom");

	for(i = 1; i <= pla_outputs; i++)
	{
		if (pla_verbose == ON)
			ttyputmsg("Placing pullup facet %d in OR-plane", i);

		ptr = (FACETIST *)emalloc(sizeof(FACETIST), pla_aid->cluster);
		if (ptr == NOFACETIST)
		{
			ttyputerr("PlacePullupsonOR: ran out of storage space, i=%d", i);
			break;
		} else if (ptr == NOFACETIST) break;
		ptr->findex = i;
		ptr->facetinst = newnodeinst(pla_pu_proto, llXpos, llXpos+puX,
			llYpos, llYpos+puY, NOTRANS, 2700, pla);
		if (ptr->facetinst == NONODEINST)
			ttyputerr("PlacePullupsonOR: cannot instance pu# %d", i);

		/* Remember the Vdd points for later connection */
		cotv = pla_MkVddGnd(ORTOPVDD, &potv);
		cotv->inst = ptr->facetinst;
		cotv->port = pla_AssignPort(ptr->facetinst, llXpos+puX/2, VddYpos);
		if (cotv->port->port == NOPORTPROTO)
			ttyputerr("PlacePullupsonOR: can't access Vdd contact");

		/* Mark the diffusion end of the pu for connections across the plane */
		ptr->lport = pla_AssignPort(ptr->facetinst, llXpos+puX/2-pla_lam, bc_portY);
		if (ptr->lport->port == NOPORTPROTO)
			ttyputerr("PlacePullupsonOR: cannot access bc port of pullup");

		ptr->rport = NOPORT;
		ptr->rcoltrans = ptr->lcoltrans = NOPROGTRANS;

		if (prevptr != NOFACETIST) prevptr->nextfacet = ptr;
		prevptr = ptr;

		if (firstptr == NOFACETIST) firstptr = ptr;

		llXpos = llXpos + puY;
	}
	if (ptr != NOFACETIST) ptr->nextfacet = NOFACETIST;

	return(firstptr);
}

/*
 * Place a programming transistor at the given position and orientation.
 * Also record its ports for later use.
 */
PROGTRANS *pla_Program(INTBIG lowx, INTBIG lowy, INTBIG orientation, NODEPROTO *pla)
{
	INTBIG highx, highy, Ylen, Xlen, centerx, centery;
	INTSML rot;
	NODEINST *inst;
	PROGTRANS *trans;

	Ylen = pla_prog_proto->highy - pla_prog_proto->lowy;
	Xlen = pla_prog_proto->highx - pla_prog_proto->lowx;

	centerx = lowx + Xlen/2;
	centery = lowy + Ylen/2;

	/* Find the location of the programming transistor */
	highx = lowx + Xlen;
	highy = lowy + Ylen;

	switch(orientation)
	{
		case LEFT:  rot = 1800;     break;
		case RIGHT: rot = NOROT;    break;
		case UP:    rot = 900;      break;
		case DOWN:  rot = 2700;     break;
	}

	/*
	 * Now instance the pla_prog_proto at the coordinates computed above
	 * in the calculated orientation
	 */
	trans = (PROGTRANS *)emalloc(sizeof(PROGTRANS), pla_aid->cluster);
	if (trans == NOPROGTRANS)
	{
		ttyputerr("Program: ran out of storage for PROGTRANS");
		return(NOPROGTRANS);
	} else if (trans == NOPROGTRANS) return(NOPROGTRANS);

	inst = newnodeinst(pla_prog_proto, lowx, highx, lowy, highy, NOTRANS, rot, pla);
	if (inst == NONODEINST)
		ttyputerr("Program: cannot instance programming trans");

	trans->trans = inst;

	/*
	 * Find the four ports of the programming transistor.
	 * ports 1 is the diff arc port.  ports 3 and 4 are poly arc
	 * ports. port2 is the metal arc port.
	 */
	switch(orientation)
	{
		case LEFT:
			trans->metalport = pla_AssignPort(inst, lowx+2*pla_lam, lowy+Ylen/2);
			trans->diffport = pla_AssignPort(inst, highx-2*pla_lam, lowy+Ylen/2);
			trans->polyblport = pla_AssignPort(inst, lowx+Xlen/2, lowy+pla_lam);
			trans->polytrport = pla_AssignPort(inst, lowx+Xlen/2, highy-pla_lam);
			break;
		case RIGHT:
			trans->diffport = pla_AssignPort(inst, lowx+2*pla_lam, lowy+Ylen/2);
			trans->metalport = pla_AssignPort(inst,highx-2*pla_lam, lowy+Ylen/2);
			trans->polyblport = pla_AssignPort(inst, lowx+Xlen/2, lowy+pla_lam);
			trans->polytrport = pla_AssignPort(inst, lowx+Xlen/2, highy-pla_lam);
			break;
		case UP:
			trans->diffport = pla_AssignPort(inst, centerx, centery-Xlen/2+2*pla_lam);
			trans->metalport = pla_AssignPort(inst, centerx, centery+Xlen/2-2*pla_lam);
			trans->polyblport = pla_AssignPort(inst, centerx-Ylen/2+pla_lam, centery);
			trans->polytrport = pla_AssignPort(inst, centerx+Ylen/2-pla_lam, centery);
			break;
		case DOWN:
			trans->metalport = pla_AssignPort(inst,centerx, centery-Xlen/2+2*pla_lam);
			trans->diffport = pla_AssignPort(inst,centerx, centery+Xlen/2-2*pla_lam);
			trans->polyblport = pla_AssignPort(inst,centerx-Ylen/2+pla_lam, centery);
			trans->polytrport = pla_AssignPort(inst,centerx+Ylen/2-pla_lam, centery);
			break;
	}
	if ((trans->diffport->port   == NOPORTPROTO ||
		trans->metalport->port  == NOPORTPROTO ||
		trans->polyblport->port == NOPORTPROTO ||
		trans->polytrport->port == NOPORTPROTO))
			ttyputerr("Program: cannot find transistor ports");

	trans->uprogtrans = trans->rprogtrans = NOPROGTRANS;

	return(trans);
}

PROGTRANS *pla_ProgramAND(INTBIG in, INTBIG arm, INTBIG pterm, INTBIG puX, INTBIG puY,
	INTBIG inX, INTBIG inY)
{
	INTBIG llXpos, llYpos;  /* Lower Left X,Y position of the point
						   where an instance of the programming
						   facet is to be placed */

	/*
	 * NOTE:  We are not taking care of situations where if there are
	 * a large number of inputs, pterms and outputs, extra
	 * Vdd and Gnd lines have to run within the PLA.
	 */
	inY = inY - (pla_VddWidth - 4*pla_lam); /* Compensate for Extra Vdd Width */
	llYpos = inY + (pterm-1) * puY + pla_lam; /* 1 pla_lam from top edge of
											   Input facet */
	if (arm == TRUE)
	{
		llXpos = puX+(in-1)*inX+4*pla_lam;
		return(pla_Program(llXpos, llYpos, RIGHT, pla_facet));
	}

	/* FALSE: */
	llXpos = puX+(in-1)*inX-4*pla_lam;
	return(pla_Program(llXpos, llYpos, LEFT, pla_facet));
}

PROGTRANS *pla_ProgramOR(INTBIG out, INTBIG row, INTBIG orLeft, INTBIG puY, INTBIG inY)
{
	INTBIG llXpos, llYpos;

	/*
	 * NOTE:  We are not taking care of situations where if there are
	 * a large number of inputs, pterms and outputs, extra
	 * Vdd and Gnd lines have to run within the PLA.
	 */
	inY = inY - (pla_VddWidth - 4*pla_lam); /* Compensate for Extra Vdd Width */
	llXpos = orLeft - 2*pla_lam + (out-1)*8*pla_lam;
	llYpos = (row-1)*puY + inY + 2*pla_lam;

	/* The programming transistor next to the pullup must always be oriented UP*/
	if (pla_pterms % 2)		/* Odd number of pterms */
	{
		if (row % 2)		/*  Odd pterm */
			return(pla_Program(llXpos, llYpos, UP, pla_facet));
		else				/*  Even pterm */
			return(pla_Program(llXpos, llYpos, DOWN, pla_facet));
	} else
	{
		if (row % 2)		/*  Odd pterm */
			return(pla_Program(llXpos, llYpos, DOWN, pla_facet));
		else				/* Even pterm */
			return(pla_Program(llXpos, llYpos, UP, pla_facet));
	}
}

/*
 * Now it is time to program the PLA by instancing the various facets
 * we have created.  The origin of the structure is set to the Lower
 * Left (LL) corner in line with the bottom of the Input facets and the
 * Pullup facets on the AND plane.  This means that the LL corner of
 * Output facets will be on the negative  side of Y axis.
 */
INTSML pla_ProgramPLA(INTBIG inX, INTBIG inY, INTBIG puX, INTBIG puY, INTBIG outX, INTBIG outY,
	INTBIG connectX, INTBIG connectY, NODEPROTO *pla)
{
	INTBIG i, o, p, out,		/* Loop control variables */
		val,				/* Temporary */
		andX, andY, orX,	/* Dimensions of the planes */
		orLeft,				/* Left edge of OR plane */
		orRight,			/* Right edge of OR plane */
		CurRow, CurCol;		/* Current Row and Column positions */
	char keyword[16];		/* Input holder */
	NODEINST *connect;		/* Metal-Poly Contact */
	FACETIST *PullupAND,	/* List of Pullups on AND plane */
		*PullupOR,			/* List of Pullups on OR plane */
		*Inputs,			/* List of Input facets */
		*Outputs,			/* List of Output facets */
		*inptr,				/* To keep track of current column in AND */
		*ptermptr,			/* To keep track of current row */
		*outptr;			/* To keep track of current column in OR */
	PROGTRANS *CurTrans,	/* Current transistor being placed */
		*PRowTrans,			/* Trans on the same row, previous column */
		*PColTrans;			/* Trans on the same column, bottom row*/

	/*
	 * Let us first figure out the dimensions of the planes.
	 * The Input facets can be placed side by side without any gaps
	 * between them.  The required gap has been wired in to the facet itself.
	 * Hence, andX = inX * number of inputs.  Similarly,
	 * andY = orY = puY * number of pterms, orX = outX * number of outputs.
	 */
	andX = inX  * pla_inputs;
	andY = puY  * pla_pterms;
	orX  = outX * (pla_outputs+pla_outputs%2)/2;
	orLeft = puX + andX + connectX;
	orRight = orLeft + orX + 3*pla_lam;  /* 3 is the metal-metal separation */

	if (pla_verbose == ON)
	{
		ttyputmsg("Dimensions of the planes:");
		ttyputmsg("  andX = %s,    andY = %s", latoa(andX), latoa(andY));
		ttyputmsg("   orX = %s,     orY = %s", latoa(orX), latoa(andY));
		ttyputmsg("orLeft = %s, orRight = %s", latoa(orLeft), latoa(orRight));
	}

	/* Place all the input, output and pullup facets */
	PullupAND = pla_PlacePullupsonAND(puX, puY, inY, pla);
	PullupOR  = pla_PlacePullupsonOR(andY, orLeft, inY, puX, puY, pla);
	Inputs    = pla_PlaceInputs(puX, inX, inY, pla);
	Outputs   = pla_PlaceOutputs(orLeft, outX, outY, pla);

	/* Initialize all the pointers */
	ptermptr = PullupAND;
	inptr    = Inputs;
	outptr   = Outputs;
	CurTrans = PRowTrans = NOPROGTRANS;

	/* Place the programming transistors */
	for(p = 1; p <= pla_pterms; p++)
	{
		if (ptermptr == NOFACETIST)
		{
			ttyputerr("ProgramPLA: insufficient number of pullups on AND plane");
			return(ERRORRET);
		}

		if (pla_verbose == ON) ttyputmsg("Programming row %ld.", p);

		CurRow   = ptermptr->findex;
		if (CurRow != p)
			ttyputerr("ProgramPLA: Row Mismatch. CurRow=%ld, p=%ld", CurRow, p);

		/* Program the AND plane */
		for(i = 1; i <= pla_inputs; i++)
		{
			if (inptr == NOFACETIST)
			{
				ttyputerr(" ProgramPLA: insufficient number of input facets");
				return(ERRORRET);
			}

			CurCol = inptr->findex;
			if (CurCol != i)
				ttyputerr("ProgramPLA: Column Mismatch. CurCol=%ld, input=%ld", CurCol,i);

			PColTrans = NOPROGTRANS;

			/*
			 * Read a character from the input file.  Depending on its
			 * value (0 or 1 or X or x or -) place a enhancement mode
			 * transistor at the appropriate place in the AND plane.
			 */
			if ((val = pla_GetKeyWord(keyword)) == ERRORRET)
			{
				ttyputerr("End-of-File while programming AND plane");
				return(ERRORRET);
			}
			switch(val)
			{
				case ONE:
					CurTrans = pla_ProgramAND(i, TRUE,  p, puX,puY, inX,inY);
					break;
				case ZERO:
					CurTrans = pla_ProgramAND(i, FALSE, p, puX,puY, inX,inY);
					break;
				case DONTCAREX:  /* Do nothing for don't cares */
				case DONTCAREM: CurTrans = NOPROGTRANS;
					break;
				default:
					ttyputerr("Undefined characters '%s' in programming section", keyword);
					break;
			}

		   /*
			* This point is not being programmed.
			* It could also mean errror. However, that would have been
			* pointed out by pla_ProgramAND.
			*/
			if (CurTrans == NOPROGTRANS)
			{
				if (pla_verbose == ON)
					ttyputmsg("row=%ld, ANDcolumn=%ld not programmed.", CurRow, CurCol);
				inptr = inptr->nextfacet;
				continue;
			}

			if (pla_verbose == ON) ttyputmsg("row=%ld, ANDcolumn=%ld programmed.", CurRow, CurCol);

			CurTrans->row = (INTSML)CurRow;
			CurTrans->column = (INTSML)CurCol;

		   /*
			* This is the first transistor on this row.
			* Connect it to the Pullup.
			*/
			if (PRowTrans == NOPROGTRANS) ptermptr->lcoltrans = CurTrans;
				else PRowTrans->rprogtrans = CurTrans;

			PRowTrans = CurTrans;

			switch(val)
			{
				case ONE:
					if (inptr->rcoltrans == NOPROGTRANS)
						inptr->rcoltrans = CurTrans; else
							PColTrans = pla_FindPColTrans((INTSML)p, inptr->rcoltrans);
					break;
				case ZERO:
					if (inptr->lcoltrans == NOPROGTRANS)
						inptr->lcoltrans = CurTrans; else
							PColTrans = pla_FindPColTrans((INTSML)p, inptr->lcoltrans);
					break;
			}
			if (PColTrans != NOPROGTRANS) PColTrans->uprogtrans = CurTrans;
			inptr = inptr->nextfacet;
		}

		/*
		 * Before we program the OR plane, it is necessary to place the
		 * facets connecting it to the AND plane and the Ground line.
		 *
		 *  PRowTrans points to the last transistor placed on this row.
		 */
		/* compensate for extra Vdd Width */
		inY = inY - (pla_VddWidth-4*pla_lam);
		connect = newnodeinst(pla_connect_proto, orLeft-connectX, orLeft,
			inY+(p-1)*puY, inY+(p-1)*puY+connectY, NOTRANS, NOROT, pla);

		if (connect == NONODEINST)
			ttyputerr("ProgramPLA: cannot instance connect facet. p=%ld", p);

		/*
		 * Instance a progtrans structure and place the connect facet in it
		 * so that the connections can be made more uniformly
		 */
		CurTrans = (PROGTRANS *)emalloc(sizeof(PROGTRANS), pla_aid->cluster);
		if (CurTrans == NOPROGTRANS)
		{
			ttyputerr("ProgramPLA: out of space while instancing connect. p=%ld", p);
			return(ERRORRET);
		} else if (CurTrans == NOPROGTRANS) return(ERRORRET);

		/* Fill in the details for this connect facet */
		CurTrans->row = (INTSML)CurRow;
		CurTrans->column = -1;	/* Undefined */
		CurTrans->trans = connect;
		CurTrans->diffport = pla_AssignPort(connect, orLeft-pla_lam-pla_halfGnd,
			inY+(p-1)*puY+2*pla_lam);
		CurTrans->metalport = pla_AssignPort(connect, orLeft-connectX+2*pla_lam,
			inY+(p-1)*puY+5*pla_lam);
		CurTrans->polytrport = pla_AssignPort(connect, orLeft-pla_lam,
			inY+(p-1)*puY+6*pla_lam);
		CurTrans->polyblport = NOPORT;

		inY = inY + (pla_VddWidth - 4*pla_lam); /* Uncompensate */

		if ((CurTrans->metalport->port == NOPORTPROTO ||
			CurTrans->diffport->port == NOPORTPROTO || CurTrans->polytrport->port == NOPORTPROTO))
				ttyputerr("ProgramPLA: cannot find connect facet ports. p=%ld",p);

		CurTrans->uprogtrans = CurTrans->rprogtrans = NOPROGTRANS;

		if (PRowTrans == NOPROGTRANS)
		{
			/*
			 * Error.  This means that no programming transistors were
			 * placed on this row in the AND plane.
			 */
			ttyputerr(" *** Warning: Nothing on AND plane on row %ld", p);
			ptermptr->lcoltrans = CurTrans;
		} else PRowTrans->rprogtrans = CurTrans;

		/* Now connect these two together on metal */
		if (newarcinst(pla_ma_proto, 3*pla_lam, pla_userbits, PRowTrans->trans,
			PRowTrans->metalport->port, PRowTrans->metalport->x, PRowTrans->metalport->y,
				CurTrans->trans, CurTrans->metalport->port, CurTrans->metalport->x,
					CurTrans->metalport->y, pla) == NOARCINST)
						ttyputerr("Cannot connect to connect facet. row=%ld", CurRow);

		PRowTrans = CurTrans;

		for(o = 1; o <= (pla_outputs+pla_outputs%2)/2; o++)
		{
			if (outptr == NOFACETIST)
			{
				ttyputerr(" ProgramPLA: insufficient number of output facets");
				return(ERRORRET);
			}

			/*
			 * Each output facet corresponds to two vertical columns of output.
			 *  So run in a loop twice.
			 */
			if (o != outptr->findex)
				ttyputerr("ProgramPLA: Column Mismatch. outfacet#=%d, o=%d", outptr->findex, o);

			for(out = 1; out >= 0; out--)
			{
				CurCol = 2 * o - out;
				if (CurCol <= pla_outputs)
				{
					PColTrans = NOPROGTRANS;

					if ((val = pla_GetKeyWord(keyword)) == ERRORRET)
					{
						ttyputerr("End-of-File while programming OR plane");
						return(ERRORRET);
					}
					switch(val)
					{
						case ONE:
							CurTrans = pla_ProgramOR(CurCol, p, orLeft, puY,inY);
							break;
						case ZERO:	/* 0 is undefined here */
						case DONTCAREX:	/* Do nothing for don't cares */
						case DONTCAREM:
							CurTrans = NOPROGTRANS;
							break;
						default:
							ttyputerr("Bad characters in programming section");
							break;
					}
					if (CurTrans == NOPROGTRANS)
					{
						/* This point is not being programmed */
						if (pla_verbose == ON)
							ttyputmsg("row=%ld, ORcolumn=%ld not programmed.", CurRow, CurCol);
						continue;
					}

					if (pla_verbose == ON)
						ttyputmsg("row=%ld, ORcolumn=%ld programmed.", CurRow, CurCol);

					CurTrans->row = (INTSML)CurRow;
					CurTrans->column = (INTSML)CurCol;

					PRowTrans->rprogtrans = CurTrans;
					PRowTrans = CurTrans;

					switch(out)
					{
						case 1:
							if (outptr->lcoltrans == NOPROGTRANS)
								outptr->lcoltrans = CurTrans; else
									PColTrans = pla_FindPColTrans((INTSML)p, outptr->lcoltrans);
							break;
						case 0:
							if (outptr->rcoltrans == NOPROGTRANS)
								outptr->rcoltrans = CurTrans; else
									PColTrans = pla_FindPColTrans((INTSML)p, outptr->rcoltrans);
							break;
					}
					if (PColTrans != NOPROGTRANS)
						PColTrans->uprogtrans = CurTrans;
				}
			}
			outptr = outptr->nextfacet;
		}

		/* Before we switch to a new row, let us reassign the pointers */
		inptr = Inputs;
		outptr = Outputs;
		ptermptr = ptermptr->nextfacet;
		PRowTrans = NOPROGTRANS;
	}

	/*
	 * Let us now draw poly, diff and metal arcs to interconnect the
	 * programming trasistors, pullup, input and output facets.
	 */
	pla_ConnectPlanes(inY, andY, orRight, Inputs, Outputs, PullupAND, PullupOR, pla);

	pla_RunVddGnd(puX, andX, pla);
	return(OK);
}

#endif  /* PLAAID - at top */
