/*
 * ExtTech.c --
 *
 * Circuit extraction.
 * Code to read and process the sections of a technology file
 * that are specific to circuit extraction.
 *
 *     ********************************************************************* 
 *     * Copyright (C) 1985, 1990 Regents of the University of California. * 
 *     * Permission to use, copy, modify, and distribute this              * 
 *     * software and its documentation for any purpose and without        * 
 *     * fee is hereby granted, provided that the above copyright          * 
 *     * notice appear in all copies.  The University of California        * 
 *     * makes no representations about the suitability of this            * 
 *     * software for any purpose.  It is provided "as is" without         * 
 *     * express or implied warranty.  Export of this software outside     * 
 *     * of the United States of America may require an export license.    * 
 *     *********************************************************************
 */

#ifndef lint
static char sccsid[] = "@(#)ExtTech.c	4.8 MAGIC (Berkeley) 10/26/85";
#endif  /* not lint */

#include <stdio.h>
#include <stdlib.h>		/* for strtod() */
#include <string.h>
#include <math.h>
#include <ctype.h>		/* for isspace() */

#include "tcltk/tclmagic.h"
#include "utils/magic.h"
#include "utils/utils.h"
#include "utils/geometry.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "database/databaseInt.h"
#include "utils/malloc.h"
#include "textio/textio.h"
#include "utils/tech.h"
#include "debug/debug.h"
#include "extract/extract.h"
#include "extract/extractInt.h"
#include "cif/CIFint.h"

/* Current extraction style */
ExtStyle *ExtCurStyle = NULL;

/* List of all styles */
ExtKeep *ExtAllStyles = NULL;

/* Forward declarations */
void extTechFinalStyle();
void ExtLoadStyle();
void ExtTechScale(int, int);

/*
 * Table used for parsing the extract section of a .tech file
 * Each line in the extract section is of a type determined by
 * its first keyword.  There is one entry in the following table
 * for each such keyword.
 */

typedef enum
{
    AREAC, CONTACT, CSCALE, DEVICE, FET, FETRESIST, HEIGHT, LAMBDA, OVERC,
    PERIMC, PLANEORDER, NOPLANEORDER, RESIST, RSCALE, SIDEHALO, SIDEOVERLAP,
    SIDEWALL, STEP, STYLE, VARIANT
} Key;

typedef struct
{
    char	*k_name;
    int		 k_key;
    int		 k_minargs;
    int		 k_maxargs;
    char	*k_usage;
} keydesc;

static keydesc keyTable[] = {
    "areacap",		AREAC,		3,	3,
"types capacitance",

    "contact",		CONTACT,	4,	6,
"type size [border] [sep] resistance",

    "cscale",		CSCALE,		2,	2,
"capacitance-scalefactor",

    "device",		DEVICE,		4,	9,
"device dev-type types options...",

    "fet",		FET,		8,	9,
"types terminal-types min-#-terminals name [subs-types] subs-node gscap gate-chan-cap",

    "fetresist",	FETRESIST,	4,	4,
"type region ohms-per-square",

    "height",		HEIGHT,		4,	4,
"type height-above-subtrate thickness",

    "lambda",		LAMBDA,		2,	2,
"units-per-lambda",

    "overlap",		OVERC,		4,	5,
"toptypes bottomtypes capacitance [shieldtypes]",

    "perimc",		PERIMC,		4,	4,
"intypes outtypes capacitance",

    "planeorder",	PLANEORDER,	3,	3,
"plane index",
    "noplaneordering",	NOPLANEORDER,	1,	1,
"(no arguments needed)",

    "resist",		RESIST,		3,	3,
"types resistance",

    "rscale",		RSCALE,		2,	2,
"resistance-scalefactor",

    "sidehalo",		SIDEHALO,	2,	2,
"halo(lambda)",

    "sideoverlap",	SIDEOVERLAP,	5,	6,
"intypes outtypes ovtypes capacitance [shieldtypes]",

    "sidewall",		SIDEWALL,	6,	6,
"intypes outtypes neartypes fartypes capacitance",

    "step",		STEP,		2,	2,
"size(lambda)",

    "style",		STYLE,		2,	4,
"stylename",

    "variants",		VARIANT,	2,	2,
"style,...",

    0
};


/*
 * Table used for parsing the "device" keyword types
 */

/* types are enumerated in extract.h */

static keydesc devTable[] = {
    "mosfet",		DEV_MOSFET,		5,	9,
"name gate-types src-drn-types sub-types|None sub-node [gscap gccap]",

    "bjt",		DEV_BJT,		5,	5,
"name base-types emitter-types collector-types",

    "capacitor",	DEV_CAP,		5,	6,
"name top-types bottom-types [perimcap] areacap",

    "resistor",		DEV_RES,		4,	4,
"name|None res-types terminal-types",

    "diode",		DEV_DIODE,		4,	4,
"name pos-types neg-types",

    "subcircuit",	DEV_SUBCKT,		3,	5,
"name dev-types [term-types [sub-types]]",

    "rsubcircuit",	DEV_RSUBCKT,		4,	4,
"name dev-types terminal-types",

    0
};

#ifdef MAGIC_WRAPPER

/*
 * ----------------------------------------------------------------------------
 *
 * ExtCompareStyle --
 *
 *	This routine is designed to work with embedded exttosim and
 *	exttospice.  It determines whether the current extract style
 *	matches the string (picked up from the .ext file).  If so, it
 *	returns TRUE.  If not, it checks whether the style exists in
 *	the list of known files for this technology.  If so, it loads
 *	the correct style and returns TRUE.  If not, it returns FALSE.
 *
 * ----------------------------------------------------------------------------
 */

bool
ExtCompareStyle(stylename)
    char *stylename;
{
    ExtKeep *style;

    if (!strcmp(stylename, ExtCurStyle->exts_name))
	return TRUE;
    else
    {
	for (style = ExtAllStyles; style != NULL; style = style->exts_next)
	{
	    if (!strcmp(stylename, style->exts_name))
	    {
		ExtLoadStyle(stylename);
		return TRUE;
	    }
	}
    }
    return FALSE;
}

/*
 * ----------------------------------------------------------------------------
 *
 * ExtGetDevInfo --
 *
 *	This routine is designed to work with the embedded exttosim and
 *	exttospice commands under the Tcl-based magic, such that all
 *	device information needed by these commands can be picked up
 *	from the current extraction style (as it should!).  This
 *	really should be set up when the extract file is read, which is
 *	why the routine is here, although this is not a very efficient
 *	way to do it (but it needs to be this way to keep backward
 *	compatibility with the non-Tcl, standalone programs ext2sim and
 *	ext2spice).
 *
 *	Note that finding the device by index ("idx") is horribly
 *	inefficient, but keeps the netlist generator separated from
 *	the extractor.  Some of this code is seriously schizophrenic,
 *	and should not be investigated too closely.
 * 
 * Results:
 *	Return FALSE if no device corresponds to index "idx".  TRUE
 *	otherwise.
 *
 * Side Effects:
 *	Fills values in the argument list.
 * ----------------------------------------------------------------------------
 */

bool
ExtGetDevInfo(idx, devnameptr, sd_rclassptr, sub_rclassptr, subnameptr)
    int idx;
    char **devnameptr;
    short *sd_rclassptr;
    short *sub_rclassptr;
    char **subnameptr;
{
    TileType t;
    TileTypeBitMask rmask, tmask;
    int n, i = 0, j;
    bool repeat;
    char *locdname;
    char **uniquenamelist = (char **)mallocMagic(DBNumTypes * sizeof(char *));


    for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
    {
	locdname = ExtCurStyle->exts_transName[t];
	if (locdname != NULL)
	{
	    repeat = FALSE;
	    for (j = 0; j < i; j++)
		if (!strcmp(uniquenamelist[j], locdname))
		{
		    repeat = TRUE;
		    break;
		}
	    if (repeat == FALSE)
	    {
		if (i == idx) break;
		uniquenamelist[i] = locdname;
		i++;
	    }
	}
    }
    if (t == DBNumTypes) return FALSE;

    *devnameptr = locdname;
    *subnameptr = ExtCurStyle->exts_transSubstrateName[t];

    tmask = ExtCurStyle->exts_transSDTypes[t];
    *sd_rclassptr = (short)(-1);	/* NO_RESCLASS */

    for (n = 0; n < ExtCurStyle->exts_numResistClasses; n++)
    {
        rmask = ExtCurStyle->exts_typesByResistClass[n];
	if (TTMaskAndMask(&rmask, &tmask))
	{
	    *sd_rclassptr = (short)n;
	    break;
	}
    }

    tmask = ExtCurStyle->exts_transSubstrateTypes[t];
    *sub_rclassptr = (short)(-1);	/* NO_RESCLASS */

    for (n = 0; n < ExtCurStyle->exts_numResistClasses; n++)
    {
        rmask = ExtCurStyle->exts_typesByResistClass[n];
	if (TTMaskAndMask(&rmask, &tmask))
	{
	    *sub_rclassptr = (short)(n);
	    break;
	}
    }

    freeMagic(uniquenamelist);
    return TRUE;
}

#endif /* MAGIC_WRAPPER */


#ifdef THREE_D
/*
 * ----------------------------------------------------------------------------
 *
 * ExtGetZAxis --
 * 
 *	Get the height and thickness parameters for a layer (used by the
 *	graphics module which does not have access to internal variables
 *	in the extract section).
 *
 * Results:
 *	None
 *
 * Side Effects:
 *	Fills values "height" and "thick" in argument list.
 *
 * ----------------------------------------------------------------------------
 */

void
ExtGetZAxis(tile, height, thick)
    Tile *tile;
    float *height, *thick;
{
    TileType ttype;

    if (ExtCurStyle == NULL) return;

    ttype = TiGetLeftType(tile);        /* Ignore non-Manhattan for now */

    /* Note that w_scale is multiplied by SUBPIXEL to get fixed-point accuracy. */
    /* However, we downshift by only 9 (divide by 512) so that heights are      */
    /* exaggerated in the layout by a factor of 8 (height exaggeration is       */
    /* standard practice for VLSI cross-sections).                              */

    *height = ExtCurStyle->exts_height[ttype];
    *thick = ExtCurStyle->exts_thick[ttype];
}
#endif  /* THREE_D */


/*
 * ----------------------------------------------------------------------------
 *
 * ExtPrintStyle --
 *
 *	Print the available and/or current extraction styles.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Output.
 *
 * ----------------------------------------------------------------------------
 */

void
ExtPrintStyle(dolist, doforall, docurrent)
    bool dolist;
    bool doforall;
    bool docurrent;
{
    ExtKeep *style;

    if (docurrent)
    {
	if (ExtCurStyle == NULL)
	    TxError("Error: No style is set\n");
	else
	{
	    if (!dolist) TxPrintf("The current style is \"");
#ifdef MAGIC_WRAPPER
	    if (dolist)
		Tcl_SetResult(magicinterp, ExtCurStyle->exts_name, NULL);
	    else
#endif
	    TxPrintf("%s", ExtCurStyle->exts_name);
	    if (!dolist) TxPrintf("\".\n");
	}
    }

    if (doforall)
    {
	if (!dolist) TxPrintf("The extraction styles are: ");

	for (style = ExtAllStyles; style; style = style->exts_next)
	{
	    if (dolist)
	    {
#ifdef MAGIC_WRAPPER
		Tcl_AppendElement(magicinterp, style->exts_name);
#else
		if (style != ExtAllStyles) TxPrintf(" ");
		TxPrintf("%s", style->exts_name);
#endif
	    }
	    else
	    {
		if (style != ExtAllStyles) TxPrintf(", ");
		TxPrintf("%s", style->exts_name);
	    }
	}
	if (!dolist) TxPrintf(".\n");
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * ExtSetStyle --
 *
 * Set the current extraction style to 'name', or print
 * the available and current styles if 'name' is NULL.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Just told you.
 *
 * ----------------------------------------------------------------------------
 */

void
ExtSetStyle(name)
    char *name;
{
    ExtKeep *style, *match;
    int length;

    if (name == NULL) return;

    match = NULL;
    length = strlen(name);
    for (style = ExtAllStyles; style; style = style->exts_next)
    {
	if (strncmp(name, style->exts_name, length) == 0)
	{
	    if (match != NULL)
	    {
		TxError("Extraction style \"%s\" is ambiguous.\n", name);
		ExtPrintStyle(FALSE, TRUE, TRUE);
		return;
	    }
	    match = style;
	}
    }

    if (match != NULL)
    {
	ExtLoadStyle(match->exts_name);
	TxPrintf("Extraction style is now \"%s\"\n", name);
	return;
    }

    TxError("\"%s\" is not one of the extraction styles Magic knows.\n", name);
    ExtPrintStyle(FALSE, TRUE, TRUE);
}

/*
 * ----------------------------------------------------------------------------
 *
 * extTechStyleAlloc --
 *
 * Allocate memory for a new extract style structure.
 *
 * ----------------------------------------------------------------------------
 */

ExtStyle *
extTechStyleAlloc()
{
    ExtStyle *style;
    TileType r;

    style = (ExtStyle *) mallocMagic(sizeof (ExtStyle));

    /* Make sure that the memory for character strings is NULL, */
    /* because we want the Init section to free memory if has	*/
    /* been previously allocated.				*/

    for (r = 0; r < NT; r++)
    {
	style->exts_transSubstrateName[r] = (char *) NULL;
	style->exts_transName[r] = (char *) NULL;
	style->exts_deviceClass[r] = (char) 0;
	style->exts_transResist[r].ht_table = (HashEntry **) NULL;
    }

    return style;
}

/*
 * ----------------------------------------------------------------------------
 *
 * extTechStyleInit --
 *
 * Fill in the extract style structure with initial values.
 *
 * ----------------------------------------------------------------------------
 */

void
extTechStyleInit(style)
    ExtStyle *style;
{
    TileType r, s;

    style->exts_name = NULL;
    style->exts_status = TECH_NOT_LOADED;

    style->exts_sidePlanes = style->exts_overlapPlanes = 0;
    TTMaskZero(&style->exts_transMask);

    for (r = 0; r < NP; r++)
    {
	TTMaskZero(&style->exts_sideTypes[r]);
	TTMaskZero(&style->exts_overlapTypes[r]);
	style->exts_planeOrder[r] = -1;
    }

    for (r = 0; r < NT; r++)
    {
	TTMaskZero(&style->exts_nodeConn[r]);
	TTMaskZero(&style->exts_resistConn[r]);
	TTMaskZero(&style->exts_transConn[r]);
	style->exts_allConn[r] = DBAllTypeBits;

	style->exts_sheetResist[r] = 0;
	style->exts_viaSize[r] = 0;
	style->exts_viaBorder[r] = 0;
	style->exts_viaSpacing[r] = 0;
	style->exts_viaResist[r] = 0;
	style->exts_height[r] = 0.0;
	style->exts_thick[r] = 0.0;
	style->exts_areaCap[r] = (CapValue) 0;
	style->exts_overlapOtherPlanes[r] = 0;
	TTMaskZero(&style->exts_overlapOtherTypes[r]);
	TTMaskZero(&style->exts_sideEdges[r]);
	for (s = 0; s < NT; s++)
	{
	    TTMaskZero(&style->exts_sideCoupleOtherEdges[r][s]);
	    TTMaskZero(&style->exts_sideOverlapOtherTypes[r][s]);
	    style->exts_sideOverlapOtherPlanes[r][s] = 0;
	    style->exts_sideCoupleCap[r][s] = (EdgeCap *) NULL;
	    style->exts_sideOverlapCap[r][s] = (EdgeCap *) NULL;
	    style->exts_perimCap[r][s] = (CapValue) 0;
	    style->exts_overlapCap[r][s] = (CapValue) 0;

	    TTMaskZero(&style->exts_overlapShieldTypes[r][s]);
	    style->exts_overlapShieldPlanes[r][s] = 0;
	    style->exts_sideOverlapShieldPlanes[r][s] = 0;
	}

	TTMaskZero(&style->exts_perimCapMask[r]);
	TTMaskZero(&style->exts_transSDTypes[r]);
	TTMaskZero(&style->exts_subsTransistorTypes[r]);
	style->exts_transSDCount[r] = 0;
	style->exts_transGateCap[r] = (CapValue) 0;
	style->exts_transSDCap[r] = (CapValue) 0;
	if (style->exts_transSubstrateName[r] != (char *) NULL)
	{
	    freeMagic(style->exts_transSubstrateName[r]);
	    style->exts_transSubstrateName[r] = (char *) NULL;
	}
	if (style->exts_transName[r] != (char *) NULL)
	{
	    freeMagic(style->exts_transName[r]);
	    style->exts_transName[r] = (char *) NULL;
	}
	style->exts_deviceClass[r] = (char)0;
	if (style->exts_transResist[r].ht_table != (HashEntry **) NULL)
	    HashKill(&style->exts_transResist[r]);
	HashInit(&style->exts_transResist[r], 8, HT_STRINGKEYS);
	style->exts_linearResist[r] = 0;
    }

    style->exts_sideCoupleHalo = 0;

    style->exts_stepSize = 100;
    style->exts_unitsPerLambda = 100;
    style->exts_resistScale = 1000;
    style->exts_capScale = 1000.0;
    style->exts_numResistClasses = 0;

    style->exts_planeOrderStatus = needPlaneOrder ;

    for (r = 0; r < DBNumTypes; r++)
    {
	style->exts_resistByResistClass[r] = 0;
	TTMaskZero(&style->exts_typesByResistClass[r]);
	style->exts_typesResistChanged[r] = DBAllButSpaceAndDRCBits;
	TTMaskSetType(&style->exts_typesResistChanged[r], TT_SPACE);
	style->exts_typeToResistClass[r] = -1;
    }
}


/*
 * ----------------------------------------------------------------------------
 *
 * extTechStyleNew --
 *
 * Allocate a new style with zeroed technology variables.
 *
 * Results:
 *	Allocates a new ExtStyle, initializes it, and returns it.
 *
 * Side effects:
 *	See above.
 *
 * ----------------------------------------------------------------------------
 */

ExtStyle *
extTechStyleNew()
{
    ExtStyle *style;

    style = extTechStyleAlloc();
    extTechStyleInit(style);
    return style;
}

/*
 * ----------------------------------------------------------------------------
 *
 * aToCap --
 *
 *    Utility procedure for reading capacitance values.
 *
 * Returns:
 *    A value of type CapValue.
 *
 * Side effects:
 *    none.
 * ----------------------------------------------------------------------------
 */

CapValue
aToCap(str)
    char *str;
{
    CapValue capVal;
    if (sscanf(str, "%lf", &capVal) != 1) {
	capVal = (CapValue) 0;
	TechError("Capacitance value %s must be a number\n", str);
    }
    return capVal;
}

/*
 * ----------------------------------------------------------------------------
 *
 * ExtLoadStyle --
 *
 * Re-read the technology file to load the specified technology extraction
 * style into structure ExtCurStyle.  This is much more memory-efficient than
 * keeping a separate (and huge!) ExtStyle structure for each extraction style.
 * It incurs a complete reading of the tech file on startup and every time the 
 * extraction style is changed, but we can assume that this does not happen
 * often.  The first style in the technology file is assumed to be default, so
 * that re-reading the tech file is not necessary on startup unless the default
 * extraction style is changed by a call to "extract style".
 *
 * ----------------------------------------------------------------------------
 */
void
ExtLoadStyle(stylename)
   char *stylename;
{
    SectionID invext;

    extTechStyleInit(ExtCurStyle);	/* Reinitialize and mark as not	*/
    ExtCurStyle->exts_name = stylename; /* loaded.			*/

    /* Invalidate the extract section, and reload it. 			*/
    /* The second parameter to TechSectionGetMask is NULL because	*/
    /* no other tech client sections depend on the extract section.	*/

    invext = TechSectionGetMask("extract", NULL);
    TechLoad(NULL, invext);

    ExtTechScale(DBLambda[0], DBLambda[1]);
}

/*
 * ----------------------------------------------------------------------------
 *
 * ExtTechInit --
 *
 *	Ensure that all memory allocated to the extract database has
 *	been freed up.  Called before loading a new technology.
 *
 * ----------------------------------------------------------------------------
 */
void
ExtTechInit()
{
    ExtKeep *style;
    int r;

    /* Delete everything in ExtCurStyle */

    if (ExtCurStyle != NULL)
    {
	extTechStyleInit(ExtCurStyle);

	/* Everything has been freed except the hash tables, which */
	/* were just reinitialized by extTechStyleInit().	   */
        for (r = 0; r < NT; r++)
	{
	    if (ExtCurStyle->exts_transResist[r].ht_table != (HashEntry **) NULL)
		HashKill(&ExtCurStyle->exts_transResist[r]);
	}
	ExtCurStyle = NULL;
    }

    /* Forget all the extract style names */

    for (style = ExtAllStyles; style != NULL; style = style->exts_next)
    {
	freeMagic(style->exts_name);
	freeMagic(style);
    }
    ExtAllStyles = NULL;
}


/*
 * ----------------------------------------------------------------------------
 *
 * ExtTechLine --
 *
 * Process a line from the "extract" section of a technology file.
 *
 * Each line in the extract section of a technology begins
 * with a keyword that identifies the format of the rest of
 * the line.
 *
 * The following three kinds of lines are used to define the resistance
 * and parasitic capacitance to substrate of each tile type:
 *
 *	resist	 types resistance
 *	areacap	 types capacitance
 *	perimcap inside outside capacitance
 *
 * where 'types', 'inside', and 'outside' are comma-separated lists
 * of tile types, 'resistance' is an integer giving the resistance
 * per square in milli-ohms, and 'capacitance' is an integer giving
 * capacitance (per square lambda for areacap, or per lambda perimeter
 * for perimcap) in attofarads.
 *
 * The perimeter (sidewall) capacitance depends both on the types
 * inside and outside the perimeter.  For a given 'perimcap' line,
 * any segment of perimeter with a type in 'inside' inside the
 * perimeter and a type in 'outside' ontside the perimeter will
 * have the indicated capacitance.
 *
 * Both area and perimeter capacitance computed from the information
 * above apply between a given node and the substrate beneath it, as
 * determined by extSubstrate[].
 *
 * Contact resistances are specified by:
 *
 *	contact	type	minsize	resistance
 *
 * where type is the type of contact tile, minsize is chosen so that contacts
 * that are integer multiples of minsize get an additional contact cut for each
 * increment of minsize, and resistance is in milliohms.
 *
 * +++ FOR NOW, CONSIDER ALL SUBSTRATE TO BE AT GROUND +++
 *
 * Overlap coupling capacitance is specified by:
 *
 *	overlap	 toptypes bottomtypes capacitance [shieldtypes]
 *
 * where 'toptypes' and 'bottomtypes' are comma-separated lists of tile types,
 * and 'capacitance' is an integer giving capacitance in attofarads per
 * square lambda of overlap.  The sets 'toptypes' and 'bottomtypes' should
 * be disjoint.  Also, the union of the planes of 'toptypes' should be disjoint
 * from the union of the planes of 'bottomtypes'.  If 'shieldtypes' are
 * present, they should also be a comma-separated list of types, on
 * planes disjoint from those of either 'toptypes' or 'bottomtypes'.
 *
 * Whenever a tile of a type in 'toptypes' overlaps one of a type in
 * 'bottomtypes', we deduct the capacitance to substrate of the 'toptypes'
 * tile for the area of the overlap, and create an overlap capacitance
 * between the two nodes based on 'capacitance'.  When material in
 * 'shieldtypes' appears over any of this overlap area, however, we
 * only deduct the substrate capacitance; we don't create an overlap
 * capacitor.
 *
 * Sidewall coupling capacitance is specified by:
 *
 *	sidewall  intypes outtypes neartypes fartypes capacitance
 *
 * where 'intypes', 'outtypes', 'neartypes', and 'fartypes' are all comma-
 * separated lists of types, and 'capacitance' is an integer giving capacitance
 * in attofarads.  All of the tiles in all four lists should be on the same
 * plane.
 *
 * Whenever an edge of the form i|j is seen, where 'i' is in intypes and
 * 'j' is in outtypes, we search on the 'j' side for a distance of
 * ExtCurStyle->exts_sideCoupleHalo for edges with 'neartypes' on the
 * close side and 'fartypes' on the far side.  We create a capacitance
 * equal to the length of overlap, times capacitance, divided by the
 * separation between the edges (poor approximation, but better than
 * none).
 *
 * Sidewall overlap coupling capacitance is specified by:
 *
 *	sideoverlap  intypes outtypes ovtypes capacitance
 *
 * where 'intypes', 'outtypes', and 'ovtypes' are comma-separated lists
 * of types, and 'capacitance' is an integer giving capacitance in attofarads
 * per lambda.  Both intypes and outtypes should be in the same plane, and
 * ovtypes should be in a different plane from intypes and outtypes.
 *
 * The next kind of line describes transistors:
 *
 *	fet	 types terminals min-#terminals names substrate gscap gccap
 *
 * where 'types' and 'terminals' are comma-separated lists of tile types.
 * The meaning is that each type listed in 'types' is a transistor, whose
 * source and drain connect to any of the types listed in 'terminals'.
 * These transistors must have exactly min-#terminals terminals, in addition
 * to the gate (whose connectivity is specified in the system-wide connectivity
 * table in the "connect" section of the .tech file).  Currently gscap and
 * gccap are unused, but refer to the gate-source (or gate-drain) capacitance
 * and the gate-channel capacitance in units of attofarads per lambda and
 * attofarads per square lambda respectively.
 *
 * The resistances of transistors is specified by:
 *
 *	fetresist type region ohms
 *
 * where type is a type of tile that is a fet, region is a string ("linear"
 * is treated specially), and ohms is the resistance per square of the fet
 * type while operating in "region".  The values of fets in the "linear"
 * region are stored in a separate table.
 *
 * Results:
 *	Returns TRUE normally, or FALSE if the line from the
 *	technology file is so malformed that Magic should abort.
 *	Currently, we always return TRUE.
 *
 * Side effects:
 *	Initializes the per-technology variables that appear at the
 *	beginning of this file.
 *
 * ----------------------------------------------------------------------------
 */

    /*ARGSUSED*/
bool
ExtTechLine(sectionName, argc, argv)
    char *sectionName;
    int argc;
    char *argv[];
{
    int n, l, j, size, val, p1, p2, p3, nterm, class;
    PlaneMask pshield, pov;
    CapValue capVal, gscap, gccap;
    TileTypeBitMask types1, types2, near, far, ov, shield, subsTypes;
    char *subsName, *transName, *cp, *endptr;
    TileType s, t, r, o;
    keydesc *kp, *dv;
    bool isLinear;
    HashEntry *he;
    EdgeCap *cnew;
    ExtKeep *es, *newStyle;
    bool bad;

    if (argc < 1)
    {
	TechError("Each line must begin with a keyword\n");
	return (TRUE);
    }

    n = LookupStruct(argv[0], (LookupTable *) keyTable, sizeof keyTable[0]);
    if (n < 0)
    {
	TechError("Illegal keyword.  Legal keywords are:\n\t");
	for (n = 0; keyTable[n].k_name; n++)
	    TxError(" %s", keyTable[n].k_name);
	TxError("\n");
	return (TRUE);
    }

    kp = &keyTable[n];
    if (argc < kp->k_minargs || argc > kp->k_maxargs)
	goto usage;
    else if (argc >= 2) l = strlen(argv[1]);

    /* If ExtCurStyle is NULL, this is a first pass, and we should	*/
    /* immediately load this style as default.  Otherwise, check if	*/
    /* the style name is in the table of styles, and add it if it is	*/
    /* not.								*/

    if (kp->k_key == STYLE)
    {
	if (argc != 2)
	    if ((argc != 4) || (strncmp(argv[2], "variant", 7)))
		goto usage;

	for (newStyle = ExtAllStyles; newStyle != NULL;
		newStyle = newStyle->exts_next)
	{
	    if (!strncmp(newStyle->exts_name, argv[1], l))
		break;
	}
	if (newStyle == NULL)
	{
	    if (argc == 2)
	    {
		newStyle = (ExtKeep *)mallocMagic(sizeof(ExtKeep));
		newStyle->exts_next = NULL;
		newStyle->exts_name = StrDup((char **) NULL, argv[1]);

		if (ExtAllStyles == NULL)
		    ExtAllStyles = newStyle;
		else
		{
	            /* Append to end of style list */
	            for (es = ExtAllStyles; es->exts_next; es = es->exts_next);
	            es->exts_next = newStyle;
		}
	    }
	    else	/* Handle style variants */
	    {
		ExtKeep *saveStyle = NULL;
		char *tptr, *cptr;

		/* 4th argument is a comma-separated list of variants.	*/
		/* In addition to the default name recorded above,	*/
		/* record each of the variants.				*/

		tptr = argv[3];
		while (*tptr != '\0')
		{
		    cptr = strchr(tptr, ',');
		    if (cptr != NULL) *cptr = '\0';
		    newStyle = (ExtKeep *)mallocMagic(sizeof(ExtKeep));
		    newStyle->exts_next = NULL;
		    newStyle->exts_name = (char *)mallocMagic(l
				+ strlen(tptr) + 1);
		    sprintf(newStyle->exts_name, "%s%s", argv[1], tptr);

		    /* Remember the first variant as the default */
		    if (saveStyle == NULL) saveStyle= newStyle;

		    /* Append to end of style list */
		    if (ExtAllStyles == NULL)
			ExtAllStyles = newStyle;
		    else
		    {
			for (es = ExtAllStyles; es->exts_next; es = es->exts_next);
			es->exts_next = newStyle;
		    }
		    
		    if (cptr == NULL)
			break;
		    else
			tptr = cptr + 1;
		}
		newStyle = saveStyle;
	    }
	}

	/* Load style as default extraction style if this is the first	*/
	/* style encountered.  Otherwise, if we are changing styles, 	*/
	/* load this style only if the name matches that in ExtCurStyle.*/

	if (ExtCurStyle == NULL) 
	{
	    ExtCurStyle = extTechStyleNew();
	    ExtCurStyle->exts_name = newStyle->exts_name;
	    ExtCurStyle->exts_status = TECH_PENDING;
	}
	else if ((ExtCurStyle->exts_status == TECH_PENDING) ||
		(ExtCurStyle->exts_status == TECH_SUSPENDED))
	    /* Finished loading; stop */
	    ExtCurStyle->exts_status = TECH_LOADED;
	else if (ExtCurStyle->exts_status == TECH_NOT_LOADED)
	{
	    if (ExtCurStyle->exts_name == NULL)
	        return (FALSE);		/* Don't know what to load! */
	    else if (argc == 2)
	    {
		if (!strcmp(argv[1], ExtCurStyle->exts_name))
		     ExtCurStyle->exts_status = TECH_PENDING; 	/* load pending */
	    }
	    else if (argc == 4)
	    {
		/* Verify that the style matches one variant */
		char *tptr, *cptr;
		if (!strncmp(ExtCurStyle->exts_name, argv[1], l))
		{
		    tptr = argv[3];
		    while (*tptr != '\0')
		    {
			cptr = strchr(tptr, ',');
			if (cptr != NULL) *cptr = '\0';
			if (!strcmp(ExtCurStyle->exts_name + l, tptr))
			{
			    ExtCurStyle->exts_status = TECH_PENDING;
			    return TRUE;
			}
			if (cptr == NULL)
			    return TRUE;
			else
			    tptr = cptr + 1;
		    }
		}
	    }
	}
	return (TRUE);
    }

    /* Only continue past this point if we are loading the extraction style */
    if (ExtCurStyle == NULL) return FALSE;
    if ((ExtCurStyle->exts_status != TECH_PENDING) &&
		(ExtCurStyle->exts_status != TECH_SUSPENDED))
	return TRUE;

    /* Process "variant" lines next */

    if (kp->k_key == VARIANT)
    {
	int l;
	char *cptr, *tptr;

	/* If our style variant is not one of the ones declared */
	/* on the line, then we ignore all input until we 	*/
	/* either reach the end of the style, the end of the	*/
	/* section, or another "variant" line.			*/

	if (argc != 2) goto usage;
	tptr = argv[1];
	while (*tptr != '\0')
	{
	    cptr = strchr(tptr, ',');
	    if (cptr != NULL) 
	    {
		*cptr = '\0';
		for (j = 1; isspace(*(cptr - j)); j++)
		    *(cptr - j) = '\0';
	    }

	    if (*tptr == '*')	/* Wildcard for "all variants" */
	    {
		ExtCurStyle->exts_status = TECH_PENDING;
		return TRUE;
	    }
	    else
	    {
		l = strlen(ExtCurStyle->exts_name) - strlen(tptr);
		if (!strcmp(tptr, ExtCurStyle->exts_name + l))
		{
		    ExtCurStyle->exts_status = TECH_PENDING;
		    return TRUE;
		}
	    }
	
	    if (cptr == NULL)
		break;
	    else
		tptr = cptr + 1;
	}
	ExtCurStyle->exts_status = TECH_SUSPENDED;
    }

    /* Anything below this line is not parsed if we're in TECH_SUSPENDED mode */
    if (ExtCurStyle->exts_status != TECH_PENDING) return TRUE;

    switch (kp->k_key)
    {
	case AREAC:
	case CONTACT:
	case FET:
	case FETRESIST:
	case HEIGHT:
	case OVERC:
	case PERIMC:
	case RESIST:
	case SIDEWALL:
	case SIDEOVERLAP:
	    DBTechNoisyNameMask(argv[1], &types1);
	    break;
	case DEVICE:
	    DBTechNoisyNameMask(argv[3], &types1);
	    break;
	case PLANEORDER:
	case NOPLANEORDER:
	default:
	    break;
    }

    switch (kp->k_key)
    {
	case AREAC:
	    capVal = aToCap(argv[2]);
	    for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
		if (TTMaskHasType(&types1, t))
		    ExtCurStyle->exts_areaCap[t] = capVal;
	    break;
	case CONTACT: {
	    int sidx = 2, vidx = 3, border, spacing;

	    if (argc == 6)
	    {
		vidx = 5;
		sidx = 3;
		if (!StrIsInt(argv[2]))
		{
		    TechError("Contact border %s must be numeric\n", argv[2]);
		    break;
		}
		border = atoi(argv[2]);
		if (!StrIsInt(argv[4]))
		{
		    TechError("Contact spacing %s must be numeric\n", argv[4]);
		    break;
		}
		spacing = atoi(argv[4]);
	    }
	    if (!StrIsInt(argv[sidx]))
	    {
		TechError("Contact minimum size %s must be numeric\n", argv[sidx]);
		break;
	    }
	    size = atoi(argv[sidx]);
	    if (argc != 6)
	    {
		size = (size + 1) >> 1;
		border = (size + 1) >> 1;
		spacing = size;
	    }
	    if (!StrIsInt(argv[vidx]))
	    {
		TechError("Contact resistivity %s must be numeric\n", argv[vidx]);
		break;
	    }
	    val = atoi(argv[vidx]);
	    for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
		if (TTMaskHasType(&types1, t))
		{
		    ExtCurStyle->exts_viaSize[t] = size;
		    ExtCurStyle->exts_viaBorder[t] = border;
		    ExtCurStyle->exts_viaSpacing[t] = spacing;
		    ExtCurStyle->exts_viaResist[t] = val;
		}
	    }
	    break;
	case CSCALE:
	    ExtCurStyle->exts_capScale = strtod(argv[1], &endptr);
	    if (endptr == argv[1])
	    {
		TechError("Cannot parse cscale value \"%s\"\n", argv[1]);
		ExtCurStyle->exts_capScale = 1.0;
	    }
	    break;
	case FET:

	    /* Original FET format, kept for backwards compatibility */

	    DBTechNoisyNameMask(argv[2], &types2);
	    nterm = atoi(argv[3]);
	    transName = argv[4];
	    subsName = argv[5];
	    cp = index(subsName, '!');
	    if (cp == NULL || cp[1] != '\0')
	    {
		TechError("Fet substrate node %s is not a global name\n",
		    subsName);
	    }
	    subsTypes = DBZeroTypeBits;
	    if (sscanf(argv[6], "%lf", &capVal) != 1)
	    {
		DBTechNoisyNameMask(argv[6], &subsTypes);
		gscap = aToCap(argv[7]);
		gccap = (argc > 8) ? aToCap(argv[8]) : (CapValue) 0;
	    }
	    else
	    {
		gscap = aToCap(argv[6]);
		gccap = (argc > 7) ? aToCap(argv[7]) : (CapValue) 0;
	    }
	    TTMaskSetMask(&ExtCurStyle->exts_transMask, &types1);
	    for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
		if (TTMaskHasType(&types1, t))
		{
		    TTMaskSetMask(ExtCurStyle->exts_transConn+t,&types1);
		    ExtCurStyle->exts_transSDTypes[t] = types2;
		    ExtCurStyle->exts_transSDCount[t] = nterm;
		    ExtCurStyle->exts_transSDCap[t] = gscap;
		    ExtCurStyle->exts_transGateCap[t] = gccap;
		    ExtCurStyle->exts_deviceClass[t] = DEV_FET;
		    ExtCurStyle->exts_transName[t] =
			    StrDup((char **) NULL, transName);
		    ExtCurStyle->exts_transSubstrateName[t] =
			    StrDup((char **) NULL, subsName);
		    ExtCurStyle->exts_transSubstrateTypes[t] = subsTypes;
		    {
			int z;

			for (z = TT_TECHDEPBASE; z < DBNumTypes; z++)
			{
			    if (TTMaskHasType(&subsTypes, z))
				TTMaskSetType(&ExtCurStyle->exts_subsTransistorTypes[z],
					t);
			}
		    }
		}
	    break;

	case DEVICE:

	    /* Parse second argument for device type */

	    n = LookupStruct(argv[1], (LookupTable *) devTable, sizeof devTable[0]);
	    if (n < 0)
	    {
		TechError("Illegal device.  Legal devices are:\n\t");
		for (n = 0; devTable[n].k_name; n++)
		    TxError(" %s", devTable[n].k_name);
		TxError("\n");
		return (TRUE);
	    }

	    dv = &devTable[n];
	    if ((argc - 1) < dv->k_minargs || (argc - 1) > dv->k_maxargs)
		goto usage;

	    gscap = (CapValue) 0;
	    gccap = (CapValue) 0;
	    subsName = NULL;
	    subsTypes = DBZeroTypeBits;
	    transName = argv[2];

	    switch (dv->k_key)
	    {
		case DEV_BJT:
		    DBTechNoisyNameMask(argv[4], &types2);	/* emitter */
		    DBTechNoisyNameMask(argv[5], &subsTypes);	/* collector */
		    nterm = 1;	/* emitter is the only "terminal type" expected */
		    break;
		case DEV_MOSFET:
		    DBTechNoisyNameMask(argv[4], &types2);	/* source/drain */
		    if (strcmp(argv[5], "None"))
			DBTechNoisyNameMask(argv[5], &subsTypes);   /* substrate */
		    subsName = argv[6];
		    if (argc > 7) gscap = aToCap(argv[7]);
		    if (argc > 8) gccap = aToCap(argv[8]);
		    nterm = 2;
		    break;
		case DEV_DIODE:
		    DBTechNoisyNameMask(argv[4], &types2);	/* negative types */
		    subsTypes = DBZeroTypeBits;
		    nterm = 1;
		    break;
		case DEV_RES:
		    DBTechNoisyNameMask(argv[4], &types2);	/* terminals */
		    subsTypes = DBZeroTypeBits;
		    nterm = 2;
		    break;
		case DEV_CAP:
		    DBTechNoisyNameMask(argv[4], &types2);	/* bottom */
		    subsTypes = DBZeroTypeBits;
		    if (argc > 5) gscap = aToCap(argv[5]);	/* perimeter cap */
		    gccap = aToCap(argv[argc - 1]);		/* area cap */
		    nterm = 1;
		    break;
		case DEV_SUBCKT:
		    nterm = 1;
		    if (argc > 4)
			DBTechNoisyNameMask(argv[4], &types2);	  /* terminals */
		    if (argc > 5)
			DBTechNoisyNameMask(argv[5], &subsTypes); /* substrate */
		    break;
		case DEV_RSUBCKT:
		    nterm = 2;
		    DBTechNoisyNameMask(argv[4], &types2);	  /* terminals */
		    subsTypes = DBZeroTypeBits;
		    break;
	    }

	    TTMaskSetMask(&ExtCurStyle->exts_transMask, &types1);
	    for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
	    {
		if (TTMaskHasType(&types1, t))
		{
		    TTMaskSetMask(ExtCurStyle->exts_transConn + t, &types1);
		    ExtCurStyle->exts_transSDTypes[t] = types2;
		    ExtCurStyle->exts_transSDCount[t] = nterm;
		    ExtCurStyle->exts_transSDCap[t] = gscap;
		    ExtCurStyle->exts_transGateCap[t] = gccap;
		    ExtCurStyle->exts_deviceClass[t] = dv->k_key;
		    ExtCurStyle->exts_transName[t] =
			    StrDup((char **) NULL, transName);
		    ExtCurStyle->exts_transSubstrateName[t] =
			    StrDup((char **) NULL, subsName);
		    ExtCurStyle->exts_transSubstrateTypes[t] = subsTypes;
		    {
			int z;

			for (z = TT_TECHDEPBASE; z < DBNumTypes; z++)
			{
			    if (TTMaskHasType(&subsTypes, z))
				TTMaskSetType(&ExtCurStyle->
					exts_subsTransistorTypes[z], t);
			}
		    }
		}
	    }
	    break;

	case FETRESIST:
	    if (!StrIsInt(argv[3]))
	    {
		TechError("Fet resistivity %s must be numeric\n", argv[3]);
		break;
	    }
	    val = atoi(argv[3]);
	    isLinear = (strcmp(argv[2], "linear") == 0);
	    for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
		if (TTMaskHasType(&types1, t))
		{
		    he = HashFind(&ExtCurStyle->exts_transResist[t], argv[2]);
		    HashSetValue(he, (spointertype)val);
		    if (isLinear)
			ExtCurStyle->exts_linearResist[t] = val;
		}
	    break;
	case HEIGHT: {
	    float height, thick;

	    if (!StrIsNumeric(argv[2]))
	    {
		TechError("Layer height %s must be numeric\n", argv[2]);
		break;
	    }
	    if (!StrIsNumeric(argv[3]))
	    {
		TechError("Layer thickness %s must be numeric\n", argv[3]);
		break;
	    }
	    height = (float)strtod(argv[2], NULL);
	    thick = (float)strtod(argv[3], NULL);
	    for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
		if (TTMaskHasType(&types1, t))
		{
		    ExtCurStyle->exts_height[t] = height;
		    ExtCurStyle->exts_thick[t] = thick;
		}
	    }
	    break;
	case LAMBDA:
	    ExtCurStyle->exts_unitsPerLambda = atoi(argv[1]);
	    break;
	case OVERC:
	    DBTechNoisyNameMask(argv[2], &types2);
	    capVal = aToCap(argv[3]);
	    bad = FALSE;
	    shield = DBZeroTypeBits;
	    if (argc > 4)
		DBTechNoisyNameMask(argv[4], &shield);
	    for (s = TT_TECHDEPBASE; s < DBNumTypes; s++)
	    {
		if (!TTMaskHasType(&types1, s)) continue;

		/* Contact overlap caps are determined from residues */
		if (DBIsContact(s)) continue;

		for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
		{
		    if (!TTMaskHasType(&types2, t)) continue;

		    /* Contact overlap caps are determined from residues */
		    if (DBIsContact(t)) continue;

		    if (s == t)
		    {
			TechError("Can't have overlap capacitance between"
				" tiles of the same type (%s)\n",
				DBTypeLongName(s));
			bad = TRUE;
			continue;
		    }
		    p1 = DBPlane(s), p2 = DBPlane(t);
		    if (p1 == p2)
		    {
			TechError("Can't have overlap capacitance between"
				" tiles on the same plane (%s, %s)\n",
				DBTypeLongName(s), DBTypeLongName(t));
			bad = TRUE;
			continue;
		    }
		    if (ExtCurStyle->exts_overlapCap[s][t] > (CapValue) 0)
		    {
			TechError("Only one of \"overlap %s %s\" or"
				" \"overlap %s %s\" allowed\n",
				DBTypeLongName(s), DBTypeLongName(t),
				DBTypeLongName(t), DBTypeLongName(s));
			bad = TRUE;
			continue;
		    }
		    ExtCurStyle->exts_overlapCap[s][t] = capVal;
		    ExtCurStyle->exts_overlapPlanes |= PlaneNumToMaskBit(p1);
		    ExtCurStyle->exts_overlapOtherPlanes[s]
			    |= PlaneNumToMaskBit(p2);
		    TTMaskSetType(&ExtCurStyle->exts_overlapTypes[p1], s);
		    TTMaskSetType(&ExtCurStyle->exts_overlapOtherTypes[s], t);
		    if (argc == 4) continue;

		    /* Shielding */
		    pshield = 0;
		    for (r = TT_TECHDEPBASE; r < DBNumTypes; r++)
		    {
			if (TTMaskHasType(&shield, r))
			{
			    /* Shielding types are determined from residues */
			    if (DBIsContact(r)) continue;

			    p3 = DBPlane(r);
			    if (p3 == p1 || p3 == p2)
			    {
				TechError("Shielding type (%s) must be on a"
					" different plane from shielded types.\n",
					DBTypeLongName(r));
				bad = TRUE;
				continue;
			    }
			    pshield |= PlaneNumToMaskBit(p3);
			}
		    }
		    ExtCurStyle->exts_overlapShieldPlanes[s][t] = pshield;
		    ExtCurStyle->exts_overlapShieldTypes[s][t] = shield;
		}
	    }
	    if (bad)
		return (TRUE);
	    break;
	case SIDEOVERLAP:
	    bad = FALSE;
	    DBTechNoisyNameMask(argv[2], &types2);
	    pov = DBTechNoisyNameMask(argv[3], &ov);
	    capVal = aToCap(argv[4]);
	    shield = DBZeroTypeBits;
	    if (argc == 6) DBTechNoisyNameMask(argv[5], &shield);
	    if (TTMaskHasType(&types1, TT_SPACE))
		TechError("Can't have space on inside of edge [ignored]\n");
	    /* It's ok to have the overlap be to space as long as a plane is */
	    /* specified.						     */
	    if (TTMaskHasType(&ov, TT_SPACE))
	    {
	    	if ((cp = index(argv[3],'/')) == NULL)
		{
		    TechError("Must specify plane for sideoverlap to space\n");
		}
		cp++;
		p3 = (spointertype) dbTechNameLookup(cp, &dbPlaneNameLists);
		if (p3 < 0)
		    TechError("Unknown overlap plane %s\n",argv[3]);
		else
		    pov = PlaneNumToMaskBit(p3);
	
	    }
	    for (s = TT_TECHDEPBASE; s < DBNumTypes; s++)
	    {
		if (!TTMaskHasType(&types1, s))
		    continue;

		/* Side overlap computed from residues */
		if (DBIsContact(s)) continue;

		p1 = DBPlane(s);
		if (PlaneMaskHasPlane(pov, p1))
		    goto diffplane;
		ExtCurStyle->exts_sidePlanes |= PlaneNumToMaskBit(p1);
		TTMaskSetType(&ExtCurStyle->exts_sideTypes[p1], s);
		TTMaskSetMask(&ExtCurStyle->exts_sideEdges[s], &types2);
		for (t = 0; t < DBNumTypes; t++)
		{
		    if (!TTMaskHasType(&types2, t))
			continue;

		    /* Side overlap computed from residues */
		    if (DBIsContact(t)) continue;

		    p2 = DBPlane(t);
		    if (t != TT_SPACE && PlaneMaskHasPlane(pov, p2))
			goto diffplane;
		    TTMaskSetMask(&ExtCurStyle->exts_sideOverlapOtherTypes[s][t], &ov);
		    ExtCurStyle->exts_sideOverlapOtherPlanes[s][t] |= pov;
		    cnew = (EdgeCap *) mallocMagic((unsigned) (sizeof (EdgeCap)));
		    cnew->ec_cap = capVal;
		    cnew->ec_far = shield; /* Really types that shield */
		    cnew->ec_near = ov;  /* Really types we create cap with */
		    cnew->ec_pmask = pov;
		    cnew->ec_next = ExtCurStyle->exts_sideOverlapCap[s][t];
		    ExtCurStyle->exts_sideOverlapCap[s][t] = cnew;

		    /* Shielding */
		    pshield = 0;
		    for (r = TT_TECHDEPBASE; r < DBNumTypes; r++)
		    {
			if (TTMaskHasType(&shield, r))
			{
			    /* Side overlap shielding computed from residues */
			    if (DBIsContact(r)) continue;

			    p3 = DBPlane(r);
			    if (p3 == p1 || p3 == p2)
			    {
				TechError("Shielding type (%s) must be on"
					" a different plane from shielded types.\n",
					DBTypeLongName(r));
				bad = TRUE;
				continue;
			    }
			    pshield |= PlaneNumToMaskBit(p3);
			}
		    }
		    for (o = TT_TECHDEPBASE; o < DBNumTypes; o++)
		    {
			if (TTMaskHasType(&ov, o))
			{
			    ExtCurStyle->exts_sideOverlapShieldPlanes[s][o] |= pshield;
			}
		    }
		}
	    }
	    if (bad)
		return (TRUE);
	    break;
	case SIDEWALL:
	    DBTechNoisyNameMask(argv[2], &types2);
	    DBTechNoisyNameMask(argv[3], &near);
	    DBTechNoisyNameMask(argv[4], &far);
	    if (TTMaskHasType(&types1, TT_SPACE))
		TechError("Can't have space on inside of edge [ignored]\n");
	    capVal = aToCap(argv[5]);
	    for (s = TT_TECHDEPBASE; s < DBNumTypes; s++)
	    {
		if (!TTMaskHasType(&types1, s))
		    continue;
		ExtCurStyle->exts_sidePlanes |= PlaneNumToMaskBit(DBPlane(s));
		TTMaskSetType(&ExtCurStyle->exts_sideTypes[DBPlane(s)], s);
		TTMaskSetMask(&ExtCurStyle->exts_sideEdges[s], &types2);
		for (t = 0; t < DBNumTypes; t++)
		{
		    if (!TTMaskHasType(&types2, t))
			continue;
		    TTMaskSetMask(&ExtCurStyle->exts_sideCoupleOtherEdges[s][t], &far);
		    cnew = (EdgeCap *) mallocMagic((unsigned) (sizeof (EdgeCap)));
		    cnew->ec_cap = capVal;
		    cnew->ec_near = near;
		    cnew->ec_far = far;
		    cnew->ec_next = ExtCurStyle->exts_sideCoupleCap[s][t];
		    cnew->ec_pmask = 0;
		    ExtCurStyle->exts_sideCoupleCap[s][t] = cnew;
		}
	    }
	    break;
	case SIDEHALO:
	    ExtCurStyle->exts_sideCoupleHalo = atoi(argv[1]);
	    break;
	case PERIMC:
	    DBTechNoisyNameMask(argv[2], &types2);
	    capVal = aToCap(argv[3]);
	    if (capVal == (CapValue) 0)
		break;
	    for (s = TT_TECHDEPBASE; s < DBNumTypes; s++)
		for (t = 0; t < DBNumTypes; t++)
		    if (TTMaskHasType(&types1, s) && TTMaskHasType(&types2, t))
		    {
			ExtCurStyle->exts_perimCap[s][t] = capVal;
			TTMaskSetType(&ExtCurStyle->exts_perimCapMask[s], t);
		    }
	    break;
	case RESIST:
	    val = atoi(argv[2]);
	    class = ExtCurStyle->exts_numResistClasses++;
	    for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
		if (TTMaskHasType(&types1, t))
		{
		    ExtCurStyle->exts_sheetResist[t] = val;
		    ExtCurStyle->exts_typeToResistClass[t] = class;
		}
	    ExtCurStyle->exts_resistByResistClass[class] = val;
	    ExtCurStyle->exts_typesByResistClass[class] = types1;
	    break;
	case RSCALE:
	    ExtCurStyle->exts_resistScale = atoi(argv[1]);
	    break;
	case STEP:
	    val = atoi(argv[1]);
	    if (val <= 0)
	    {
		TechError("Hierarchical interaction step size must be > 0\n");
		return (FALSE);
	    }
	    ExtCurStyle->exts_stepSize = val;
	    break;
	case NOPLANEORDER: {
	     if ( ExtCurStyle->exts_planeOrderStatus == seenPlaneOrder ) 
		TechError("\"noplaneordering\" specified after \"planeorder\".\n");
	     else 
		ExtCurStyle->exts_planeOrderStatus = noPlaneOrder ;
	    }
	    break ;
	case PLANEORDER: {
	    int pnum = (spointertype) dbTechNameLookup(argv[1], &dbPlaneNameLists);
	    int pos = atoi(argv[2]);

	    if ( ExtCurStyle->exts_planeOrderStatus == noPlaneOrder ) {
		TechError("\"planeorder\" specified after \"noplaneordering\".\n");
	    }
	    ExtCurStyle->exts_planeOrderStatus = seenPlaneOrder ;
	    if (pnum < 0) 
		TechError("Unknown planeorder plane %s\n", argv[1]);
	    else if (pos < 0 || pos >= DBNumPlanes-PL_TECHDEPBASE)
		TechError("Planeorder index must be [0..%d]\n", 
		    DBNumPlanes-PL_TECHDEPBASE-1);
	    else
		ExtCurStyle->exts_planeOrder[pnum] = pos;
	    }
	    break;
    }
    return (TRUE);

usage:
    TechError("Malformed line for keyword %s.  Correct usage:\n\t%s %s\n",
		    kp->k_name, kp->k_name, kp->k_usage);
    return (TRUE);

diffplane:
    TechError("Overlapped types in \"sideoverlap\" rule must be on a\n"
		"\tdifferent plane from intypes and outtypes.\n");
    return (TRUE);
}


/*
 * ----------------------------------------------------------------------------
 *
 * ExtTechFinal --
 *
 * Postprocess the technology specific information for extraction.
 * Builds the connectivity tables exts_nodeConn[], exts_resistConn[],
 * and exts_transConn[].
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Initializes the tables mentioned above.
 *	Leaves ExtCurStyle pointing to the first style in the list
 *	ExtAllStyles.
 *
 * ----------------------------------------------------------------------------
 */

void
ExtTechFinal()
{
    ExtStyle *es;

    /* Create a "default" style if there isn't one */
    if (ExtAllStyles == NULL)
    {
	ExtAllStyles = (ExtKeep *)mallocMagic(sizeof(ExtKeep));
	ExtAllStyles->exts_next = NULL;
	ExtAllStyles->exts_name = StrDup((char **) NULL, "default");

	ExtCurStyle = extTechStyleNew();
	ExtCurStyle->exts_name = ExtAllStyles->exts_name;
	ExtCurStyle->exts_status = TECH_LOADED;
    }
    extTechFinalStyle(ExtCurStyle);
}


void
extTechFinalStyle(style)
    ExtStyle *style;
{
    TileTypeBitMask maskBits;
    TileType r, s, t;
    int p1, missing, conflict;
    int indicis[NP];

    for (r = TT_TECHDEPBASE; r < DBNumTypes; r++)
    {
	maskBits = style->exts_nodeConn[r] = DBConnectTbl[r];
	if (!TTMaskHasType(&style->exts_transMask, r))
	{
	     TTMaskZero(&style->exts_transConn[r]);
	}
	for (s = TT_TECHDEPBASE; s < DBNumTypes; s++)
	{
	    if (TTMaskHasType(&maskBits, s))
		if (style->exts_typeToResistClass[s]
			!= style->exts_typeToResistClass[r])
		    TTMaskClearType(&maskBits, s);
	}
	style->exts_resistConn[r] = maskBits;
    }

    /* r ranges over types, s over resistance entries */
    for (r = TT_TECHDEPBASE; r < DBNumTypes; r++)
    {
	s = style->exts_typeToResistClass[r];
	if (s >= 0)
	    TTMaskClearMask(&style->exts_typesResistChanged[r],
			&style->exts_typesByResistClass[s]);
    }

    /*
     * Residue check:
     * We have ignored all contact types when parsing parasitic
     * capacitances.  Now we need to add them.  For each contact
     * type, add the contact type to the types lists accordingly.
     * Note that we don't have to record any cap values, since the
     * extraction routine dissolves contacts into their residue
     * types when computing the parasitics.  But, the type must be
     * in the type lists or contact tiles will be passed over during
     * searches.
     */

    for (s = TT_TECHDEPBASE; s < DBNumTypes; s++)
    {
	TileTypeBitMask rmask;
	PlaneMask pMask;
	int p;
	TileType q;

	if (!DBIsContact(s)) continue;

	pMask = DBTypePlaneMaskTbl[s];
	for (p = 0; p < DBNumPlanes; p++)
	{
	    if (PlaneMaskHasPlane(pMask, p))
	    {
		TTMaskSetType(&style->exts_overlapTypes[p], s);
		TTMaskSetType(&style->exts_sideTypes[p], s);
	    }
	}
	DBFullResidueMask(s, &rmask);
	for (r = TT_TECHDEPBASE; r < DBNumUserLayers; r++)
	{
	    if (!TTMaskHasType(&rmask, r)) continue;

	    TTMaskSetMask(&style->exts_sideEdges[s], &style->exts_sideEdges[r]);

	    for (q = TT_TECHDEPBASE; q < DBNumUserLayers; q++)
	    {
		if (TTMaskHasType(&style->exts_overlapOtherTypes[q], r)) 
		    TTMaskSetType(&style->exts_overlapOtherTypes[q], s);

	        for (t = TT_TECHDEPBASE; t < DBNumUserLayers; t++)
		    if (TTMaskHasType(&style->exts_overlapShieldTypes[q][t], r)
				&& !TTMaskHasType(&rmask, q)
				&& !TTMaskHasType(&rmask, t))
			TTMaskSetType(&style->exts_overlapShieldTypes[q][t], s);

		/* For sideOverlap, t is "outtypes" and includes space, so we	*/
		/* must count from TT_SPACE, not TT_TECHDEPBASE.		*/

	        for (t = TT_SPACE; t < DBNumUserLayers; t++)
		    if (TTMaskHasType(&style->exts_sideOverlapOtherTypes[q][t], r)) 
			TTMaskSetType(&style->exts_sideOverlapOtherTypes[q][t], s);
	    }
	}
    }

    /*
     * Consistency check:
     * If a type R shields S from T, make sure that R is listed as
     * being in the list of overlapped types for S, even if there
     * was no overlap capacitance explicitly specified for this
     * pair of types in an "overlap" line.  This guarantees that
     * R will shield S from substrate even if there is no capacitance
     * associated with the overlap.
     */
    for (s = TT_TECHDEPBASE; s < DBNumTypes; s++)
	for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
	{
	    if (style->exts_overlapShieldPlanes[s][t] == 0)
		continue;
	    for (r = TT_TECHDEPBASE; r < DBNumTypes; r++)
	    {
		if (!TTMaskHasType(&style->exts_overlapShieldTypes[s][t], r))
		    continue;
		p1 = DBPlane(s);
		style->exts_overlapPlanes |= PlaneNumToMaskBit(p1);
		style->exts_overlapOtherPlanes[s]
			|= PlaneNumToMaskBit(DBPlane(r));
		TTMaskSetType(&style->exts_overlapTypes[p1], s);
		TTMaskSetType(&style->exts_overlapOtherTypes[s], r);
	    }
	}

    if ( style->exts_planeOrderStatus == noPlaneOrder ) 
    	return /* no need to check */ ;
    /* Else Check to make sure the plane order is a permutation of the
       numbers 0..DBNumPlanes-DBNumPlanes-1 */
    for (p1 = PL_TECHDEPBASE; p1 < DBNumPlanes; p1++) {
	indicis[p1] = 0;
    }
    for (p1 = PL_TECHDEPBASE; p1 < DBNumPlanes; p1++) {
	int pn = style->exts_planeOrder[p1]+PL_TECHDEPBASE;
	if (pn >= PL_TECHDEPBASE && pn < DBNumPlanes)
	    indicis[pn]++;
    }
    conflict = FALSE;
    missing = FALSE;
    for (p1 = PL_TECHDEPBASE; p1 < DBNumPlanes; p1++) {
	if (indicis[p1] > 1) conflict = TRUE ;
	if (indicis[p1] < 1) missing = TRUE ;
    }
    if (!conflict && !missing)		/* Everything was ok */
	goto zinit; 	

    TxError ("\nWarning: Extraction Style %s\n", style -> exts_name);
    if (conflict) {
	TxError ("  Conflicting planeorder for plane(s):\n   ");
	for (p1 = PL_TECHDEPBASE; p1 < DBNumPlanes; p1++) {
	    if (indicis[p1] > 1)
		TxError (" %s,", DBPlaneLongNameTbl[p1]);
	}
    	TxError("\n");
    }
    if (missing) {
	TxError ("  Missing planeorder for plane(s):\n   ");
	for (p1 = PL_TECHDEPBASE; p1 < DBNumPlanes; p1++) {
	    if (indicis[p1] < 1)
		TxError (" %s,", DBPlaneLongNameTbl[p1]);
	}
    	TxError("\n");
    }
    TxError("  Magic will use the default planeorder for this style:\n   ");
    for (p1 = PL_TECHDEPBASE; p1 < DBNumPlanes; p1++) {
    	style->exts_planeOrder[p1] = p1 - PL_TECHDEPBASE ;
	TxError(" %s=%d,",DBPlaneLongNameTbl[p1], style->exts_planeOrder[p1]);
    }
    TxError("\n");

    /* Now that we have a plane ordering, we can apply default height	*/
    /* and thickness values for those layers.				*/

zinit:
    for (r = TT_TECHDEPBASE; r < DBNumTypes; r++)
    {
	if (style->exts_thick[r] == 0)
	    style->exts_thick[r] = 0.05;
	if (style->exts_height[r] == 0)
	    style->exts_height[r] = 0.1 * style->exts_planeOrder[DBPlane(r)];
    }
}

/*
 * ----------------------------------------------------------------------------
 * ExtTechScale --
 *
 *	Scale all extraction values appropriately when rescaling the grid.
 * ----------------------------------------------------------------------------
 */

void
ExtTechScale(scalen, scaled)
    int scalen;			/* Scale numerator */
    int scaled;			/* Scale denominator */
{
    ExtStyle *style = ExtCurStyle;
    EdgeCap *ec;
    int i, j;
    float sqn, sqd;

    sqn = (float)(scalen * scalen);
    sqd = (float)(scaled * scaled);

    DBScaleValue(&style->exts_unitsPerLambda, scalen, scaled);
    DBScaleValue(&style->exts_sideCoupleHalo, scaled, scalen);
    DBScaleValue(&style->exts_stepSize, scaled, scalen);

    for (i = 0; i < DBNumTypes; i++)
    {
	style->exts_areaCap[i] *= sqn;
	style->exts_areaCap[i] /= sqd;

	style->exts_transSDCap[i] *= sqn;
	style->exts_transSDCap[i] /= sqd;
	style->exts_transGateCap[i] *= sqn;
	style->exts_transGateCap[i] /= sqd;

	DBScaleValue(&style->exts_viaSize[i], scaled, scalen);
	DBScaleValue(&style->exts_viaSpacing[i], scaled, scalen);
	DBScaleValue(&style->exts_viaBorder[i], scaled, scalen);
	style->exts_height[i] *= scaled;
	style->exts_height[i] /= scalen;
	style->exts_thick[i] *= scaled;
	style->exts_thick[i] /= scalen;

	for (j = 0; j < DBNumTypes; j++)
	{
	    style->exts_perimCap[i][j] *= scalen;
	    style->exts_perimCap[i][j] /= scaled;
	    style->exts_overlapCap[i][j] *= sqn;
	    style->exts_overlapCap[i][j] /= sqd;    /* Typo fixed in 7.2.57 */
	    for (ec = style->exts_sideCoupleCap[i][j]; ec != NULL;
				ec = ec->ec_next)
	    {
		ec->ec_cap *= scalen;
		ec->ec_cap /= scaled;
	    }
	}
    }

    return;
}
