/*
 * ExtCell.c --
 *
 * Circuit extraction.
 * Extract a single cell.
 *
 *     *********************************************************************
 *     * 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 rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/extract/ExtCell.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $";
#endif  /* not lint */

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <math.h>
#include <string.h>

#include "utils/magic.h"
#include "utils/geometry.h"
#include "utils/styles.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "utils/malloc.h"
#include "textio/textio.h"
#include "debug/debug.h"
#include "extract/extract.h"
#include "extract/extractInt.h"
#include "utils/signals.h"
#include "utils/stack.h"
#include "utils/utils.h"
#include "windows/windows.h"
#include "utils/main.h"
#include "utils/undo.h"

/* ------------------------ Data local to this file ------------------- */

/* Forward declarations */
int extOutputUsesFunc();

Plane* extCellFile(CellDef *def, FILE *f, bool isTop);
void extHeader();

/*
 * ----------------------------------------------------------------------------
 *
 * ExtCell --
 *
 * Extract the cell 'def', plus all its interactions with its subcells.
 * Place the result in the file named 'outName'.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Creates the file 'outName'.ext and writes to it.
 *	May leave feedback information where errors were encountered.
 *	Upon return, extNumErrors contains the number of (likely serious)
 *	errors encountered while extracting 'def', and extNumWarnings
 *	contains the number of warnings.
 *
 * ----------------------------------------------------------------------------
 */

Plane *
ExtCell(
    CellDef *def,	/* Cell being extracted */
    char *outName,	/* Name of output file; if NULL, derive from def name */
    bool isTop)		/* If TRUE, cell is the top level cell */
{
    char *filename;
    FILE *f = NULL;
    Plane *savePlane;
    bool noextract;

    /* If marked abstract, then don't extract the cell */
    DBPropGet(def, "noextract", &noextract);
    if (noextract) return extPrepSubstrate(def);

    /* Incremental extraction:  If the cell is marked for no extraction,
     * then just prepare the substrate plane and return it to the caller.
     */
    if (def->cd_flags & CDNOEXTRACT)
	return extPrepSubstrate(def);

    f = ExtFileOpen(def, outName, "w", &filename);

    TxPrintf("Extracting %s into %s:\n", def->cd_name, filename);

    if (f == NULL)
    {
#ifdef MAGIC_WRAPPER
	TxError("Cannot open output file.\n");
#else
	TxError("Cannot open output file: ");
	perror(filename);
#endif
	return NULL;
    }

    extNumErrors = extNumWarnings = 0;
    savePlane = extCellFile(def, f, isTop);
    if (f != NULL) fclose(f);

    if (extNumErrors > 0 || extNumWarnings > 0)
    {
	TxPrintf("%s:", def->cd_name);
	if (extNumErrors > 0)
	    TxPrintf(" %d error%s",
		extNumErrors, extNumErrors != 1 ? "s" : "");
	if (extNumWarnings > 0)
	    TxPrintf(" %d warning%s",
		extNumWarnings, extNumWarnings != 1 ? "s" : "");
	TxPrintf("\n");
    }
    return savePlane;
}

/*
 * ----------------------------------------------------------------------------
 *
 * ExtFileOpen --
 *
 * Open the .ext file corresponding to a .mag file.
 * If def->cd_file is non-NULL, the .ext file is just def->cd_file with
 * the trailing .mag replaced by .ext.  Otherwise, the .ext file is just
 * def->cd_name followed by .ext.
 *
 * Results:
 *	Return a pointer to an open FILE, or NULL if the .ext
 *	file could not be opened in the specified mode.
 *
 * Side effects:
 *	Opens a file.
 *
 * ----------------------------------------------------------------------------
 */

FILE *
ExtFileOpen(
    CellDef *def,	/* Cell whose .ext file is to be written */
    char *file,		/* If non-NULL, open 'name'.ext; otherwise,
			 * derive filename from 'def' as described
			 * above.
			 */
    char *mode,		/* Either "r" or "w", the mode in which the .ext
			 * file is to be opened.
			 */
    char **prealfile)	/* If this is non-NULL, it gets set to point to
			 * a string holding the name of the .ext file.
			 */
{
    char namebuf[512], *name, *endp, *ends;
    int len;
    FILE *rfile, *testf;

    if (file) name = file;
    else if (ExtLocalPath != NULL)
    {
	if (!strcmp(ExtLocalPath, "."))
	    name = def->cd_name;	/* Save locally */
	else
	{
	    struct stat st = {0};

	    /* Test presence of the directory */
	    if (stat(ExtLocalPath, &st) == -1) {
		TxError("Path \"%s\" does not exist;  attempting to create it.\n",
			ExtLocalPath);
		mkdir(ExtLocalPath, 0755);
		if (stat(ExtLocalPath, &st) == -1) {
		    TxError("Path \"%s\" does not exist;  saving locally.\n",
				ExtLocalPath);
		    name = def->cd_name;	/* Save locally */
		}
		else
		{
		    /* Save locally in specified path, which was just created */
		    name = namebuf;
		    sprintf(namebuf, "%s/%s", ExtLocalPath, def->cd_name);
		}
	    }
	    else
	    {
		/* Save locally in specified path, which is assumed to exist */
		name = namebuf;
		sprintf(namebuf, "%s/%s", ExtLocalPath, def->cd_name);
	    }
	}
    }
    else if (def->cd_file)
    {
	name = def->cd_file;
	ends = strrchr(def->cd_file, '/');
	if (ends == NULL) ends = def->cd_file;
	if ((endp = strrchr(ends + 1, '.')))
	{
	    name = namebuf;
	    len = endp - def->cd_file;
	    if (len > sizeof namebuf - 1) len = sizeof namebuf - 1;
	    (void) strncpy(namebuf, def->cd_file, len);
	    namebuf[len] = '\0';
	}
    }
    else name = def->cd_name;

    /* Try once as-is, and if this fails, try stripping any leading	*/
    /* path information in case cell is in a read-only directory.	*/

    if ((rfile = PaOpen(name, mode, ".ext", Path, CellLibPath, prealfile)) != NULL)
	return rfile;

    if (!strcmp(mode, "r")) return NULL;	/* Not even readable */

    /* Try writing to the cwd IF there is no .mag file by the	*/
    /* same name in the cwd that would conflict.		*/

    name = strrchr(def->cd_name, '/');
    if (name != NULL)
	name++;
    else
	name = def->cd_name;

    if (def->cd_file)
    {
	ends = strrchr(def->cd_file, '/');
	if (ends != NULL)
	{
	    testf = PaOpen(ends + 1, "r", ".mag", ".", ".", NULL);
	    if (testf)
	    {
		fclose(testf);
		return NULL;
	    }
	}
    }
    return (PaOpen(name, mode, ".ext", ".", ".", prealfile));
}

/*
 * ----------------------------------------------------------------------------
 *
 * extPrepSubstrate ---
 *
 * Prepare a replacement plane for the plane representing the substrate, as
 * defined in ExtCurStyle->exts_globSubstratePlane.  The target CellDef is
 * searched for types that shield (i.e., isolate) a section of the layout
 * from the global substrate.  The tile type that represents the substrate
 * is painted into the isolated regions.
 *
 * The purpose of this method is to deal with the common methodology in
 * which the substrate is not represented by any tile type, because no mask
 * is defined for the substrate.  Typically, an entire cell such as a digital
 * standard cell may be placed on the default substrate or in a deep nwell
 * region.  It is therefore necessary to be able to detect what is underneath
 * a cell on the plane representing the substrate to determine if the area is
 * the default substrate or an isolated region.  If an isolated region, it
 * must be painted with a tile type so that the extraction code can tag the
 * tiles with an ExtRegion and assign it a node.  This code creates the substrate
 * paint in the isolated regions for the duration of the extration, then
 * reverts back to the original plane afterward.
 *
 * Results:
 *	Returns a Plane structure that is the original substrate plane from
 *	CellDef "def", with isolated substrate regions filled with the
 *	substrate tile type.  If there are no isolated substrate regions,
 *	or if a substrate plane or substrate type is not defined by the
 *	technology, then the routine returns NULL.
 *
 * Side effects:
 *	All modifications are limited to the returned plane structure.
 *
 * ----------------------------------------------------------------------------
 */

Plane *
extPrepSubstrate(
    CellDef *def)
{
    SearchContext scx;
    CellUse dummy;
    TileType subType;
    TileTypeBitMask subMask, notSubMask;
    Plane *subPlane, *savePlane;
    int pNum;

    /* Determine if substrate copying is required. */

    if (ExtCurStyle->exts_globSubstratePlane == -1) return NULL;

    /* Find a type to use for the substrate, and the mask of all types      */
    /* in the same plane as the substrate that are not connected to the     */
    /* substrate.  If there is not a simple type representing the substrate */
    /* then do not attempt to resolve substrate regions.                    */

    if ((subType = ExtCurStyle->exts_globSubstrateDefaultType) == -1) return NULL;

    TTMaskZero(&subMask);
    TTMaskSetMask(&subMask, &ExtCurStyle->exts_globSubstrateTypes);
    TTMaskCom2(&notSubMask, &subMask);
    TTMaskAndMask(&notSubMask, &DBPlaneTypes[ExtCurStyle->exts_globSubstratePlane]);

    /* Generate the full flattened substrate into a new plane structure	*/
    /* (called subPlane).  This adds layer geometry for the substrate	*/
    /* in the typical case where the substrate may be space (implicitly	*/
    /* defined substrate).                                  		*/

    scx.scx_trans = GeoIdentityTransform;
    scx.scx_area = def->cd_bbox;
    scx.scx_use = &dummy;
    dummy.cu_def = def;
    dummy.cu_id = NULL;

    subPlane = DBCellGenerateSubstrate(&scx, subType, &notSubMask,
		&ExtCurStyle->exts_globSubstrateShieldTypes, def);
    if (subPlane != NULL)
    {
	pNum = ExtCurStyle->exts_globSubstratePlane;
	savePlane = def->cd_planes[pNum];
	def->cd_planes[pNum] = subPlane;
	return savePlane;
    }
    else
        return NULL;
}

/*
 * ----------------------------------------------------------------------------
 *
 * extResPrepSubstrate ---
 *
 * This works similarly to extPrepSubstrate above, but is used for the
 * "extresist" command, where the method is to make sure that the whole
 * substrate area of the cell has non-space tiles, so that these can be
 * used to estimate the resistance of the substrate from point to point,
 * and to eliminate isolated substrate regions, since those represent an
 * idealized cutoff of resistance that "extresist" is supposed to be
 * replacing with an accurate resitance network.
 *
 * Results:
 *	Returns a Plane structure that is the original substrate plane from
 *	CellDef "def", with the entire substrate region filled with the
 *	substrate tile type.  If a substrate plane or substrate type is not
 *	defined by the technology, then the routine returns NULL.
 *
 * Side effects:
 *	All modifications are limited to the returned plane structure.
 *
 * ----------------------------------------------------------------------------
 */

Plane *
extResPrepSubstrate(
    CellDef *def)
{
    SearchContext scx;
    CellUse dummy;
    TileType subType;
    TileTypeBitMask subMask, notSubMask;
    Plane *subPlane, *savePlane;
    int pNum;

    /* Determine if substrate copying is required. */

    if (ExtCurStyle->exts_globSubstratePlane == -1) return NULL;

    /* Find a type to use for the substrate, and the mask of all types      */
    /* in the same plane as the substrate that are not connected to the     */
    /* substrate.  If there is not a simple type representing the substrate */
    /* then do not attempt to resolve substrate regions.                    */

    if ((subType = ExtCurStyle->exts_globSubstrateDefaultType) == -1) return NULL;

    TTMaskZero(&subMask);
    TTMaskSetMask(&subMask, &ExtCurStyle->exts_globSubstrateTypes);
    TTMaskCom2(&notSubMask, &subMask);
    TTMaskAndMask(&notSubMask, &DBPlaneTypes[ExtCurStyle->exts_globSubstratePlane]);

    /* Generate the full flattened substrate into a new plane structure	*/
    /* (called subPlane).  This adds layer geometry for the substrate	*/
    /* in the typical case where the substrate may be space (implicitly	*/
    /* defined substrate).                                  		*/

    scx.scx_trans = GeoIdentityTransform;
    scx.scx_area = def->cd_bbox;
    scx.scx_use = &dummy;
    dummy.cu_def = def;
    dummy.cu_id = NULL;

    subPlane = DBCellGenerateSimpleSubstrate(&scx, subType, &notSubMask, def);
    if (subPlane != NULL)
    {
	pNum = ExtCurStyle->exts_globSubstratePlane;
	savePlane = def->cd_planes[pNum];
	def->cd_planes[pNum] = subPlane;
	return savePlane;
    }
    else
        return NULL;
}

/*
 * ----------------------------------------------------------------------------
 *
 * ExtRevertSubstrate ---
 *
 * This routine swaps the substrate plane of CellDef "def" with the plane
 * structure provided in the argument "savePlane".  It should be called at
 * the end of extraction.  "savePlane" should be the pointer to the substrate
 * plane of "def" before it was swapped out for the modified plane created by
 * the routine "extPrepSubstrate", above.  The calling routine is responsible
 * for knowing if extPrepSubstrate returned NULL in which case there is
 * nothing to revert.
 *
 * Returns:
 *	Nothing.
 *
 * Side effects:
 *	The CellDef "def" has its substrate plane swapped out for "savePlane",
 *	and the original substrate plane and its contents are freed.
 * ----------------------------------------------------------------------------
 */


void
ExtRevertSubstrate(
    CellDef *def,
    Plane *savePlane)
{
    int pNum;
    Plane *subPlane;

    pNum = ExtCurStyle->exts_globSubstratePlane;
    subPlane = def->cd_planes[pNum];
    def->cd_planes[pNum] = savePlane;
    DBFreePaintPlane(subPlane);
    TiFreePlane(subPlane);
}

/*
 * ----------------------------------------------------------------------------
 *
 * extCellFile --
 *
 * Internal interface for extracting a single cell.
 * Extracts it to the open FILE 'f'.  Doesn't print
 * any messages.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	May leave feedback information where errors were encountered.
 *	Upon return, extNumErrors has been incremented by the number of
 *	fatal errors encountered while extracting 'def', and extNumWarnings
 *	by the number of warnings.
 *
 * ----------------------------------------------------------------------------
 */

Plane *
extCellFile(
    CellDef *def,	/* Def to be extracted */
    FILE *f,		/* Output to this file */
    bool isTop)		/* TRUE if the cell is the top level cell */
{
    NodeRegion *reg;
    Plane *saveSub;
    Label *lab;

    UndoDisable();

    /* If "extract do unique" was specified, then make labels in the
     * cell unique.
     */

    if (ExtOptions & EXT_DOUNIQUE)
    {
	if (ExtOptions & EXT_DOUNIQNOTOPPORTS)
	{
	    if (isTop)
		extUniqueCell(def, EXT_UNIQ_TEMP_NOPORTS);
	    else
		extUniqueCell(def, EXT_UNIQ_TEMP);
	}
	else
	    extUniqueCell(def, EXT_UNIQ_TEMP);
    }

    /* Prep any isolated substrate areas */
    if (ExtOptions & EXT_DOEXTRESIST)
	saveSub = extResPrepSubstrate(def);
    else
	saveSub = extPrepSubstrate(def);

    /* Remove any label markers that were made by a previous extraction */
    for (lab = def->cd_labels; lab; lab = lab->lab_next)
	if (lab->lab_port == INFINITY)
	    lab->lab_port = 0;

    /* Output the header: timestamp, technology, calls on cell uses */
    if (!SigInterruptPending) extHeader(def, f);

    /* Extract the mask information in this cell */
    reg = (NodeRegion *) NULL;
    if (!SigInterruptPending) reg = extBasic(def, f);

    /* Do hierarchical extraction */
    extParentUse->cu_def = def;
    if (!SigInterruptPending) extSubtree(extParentUse, reg, f);
    if (!SigInterruptPending) extArray(extParentUse, f);

    /* Clean up from basic extraction */
    if (reg) ExtFreeLabRegions((LabRegion *) reg);
    ExtResetTiles(def, CLIENTDEFAULT);

    /* Final pass: extract length information if desired */
    if (!SigInterruptPending && isTop && (ExtOptions & EXT_DOLENGTH))
	extLength(extParentUse, f);

    UndoEnable();
    return saveSub;
}

/*
 * ----------------------------------------------------------------------------
 *
 * extHeader --
 *
 * Output header information to the .ext file for a cell.
 * This information consists of:
 *
 *	timestamp
 *	extractor version number
 *	technology
 *	scale factors for resistance, capacitance, and lambda
 *	calls on all subcells used by this cell (see extOutputUsesFunc)
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Writes to (FILE *) 'f'.
 *
 * ----------------------------------------------------------------------------
 */

void
extHeader(
    CellDef *def,	/* Cell being extracted */
    FILE *f)		/* Write to this file */
{
    int n;
    bool propfound;
    char *propvalue;

    ASSERT(DBTechName != NULL, "extHeader");

    /* Output a timestamp (should be first) */
    fprintf(f, "timestamp %d\n", def->cd_timestamp);

    /* Output our version number */
    fprintf(f, "version %s\n", MagicVersion);

    /* Output the technology */
    fprintf(f, "tech %s\n", DBTechName);

    /* Output the extract style name */
    fprintf(f, "style %s\n", ExtCurStyle->exts_name);

    /*
     * Output scaling factors: R C D
     *		R = amount to multiply all resistances in the file by
     *		C = amount to multiply all capacitances by
     *		D = amount to multiply all linear distances by (areas
     *		    should be multiplied by D**2).
     */
    fprintf(f, "scale %d %d %g\n",
		ExtCurStyle->exts_resistScale,
		ExtCurStyle->exts_capScale,
		ExtCurStyle->exts_unitsPerLambda);

    /* Output the sheet resistivity classes */
    fprintf(f, "resistclasses");
    for (n = 0; n < ExtCurStyle->exts_numResistClasses; n++)
	fprintf(f, " %d", ExtCurStyle->exts_resistByResistClass[n]);
    fprintf(f, "\n");

    /* Output any parameters defined for this cell that	*/
    /* are to be passed to instances of the cell	*/
    /* (created by defining property "parameter")	*/

    propvalue = DBPropGetString(def, "parameter", &propfound);
    if (propfound)
    {
	// Use device parameter table to store the cell def parameters,
	// but preface name with ":" to avoid any conflict with device
	// names.
	fprintf(f, "parameters :%s %s\n", def->cd_name, propvalue);
    }

    /* Output all calls on subcells */
    (void) DBCellEnum(def, extOutputUsesFunc, (ClientData) f);
}

/*
 * ----------------------------------------------------------------------------
 *
 * extOutputUsesFunc --
 *
 * Filter function, called via DBCellEnum, that outputs all the
 * cell uses contained in the parent's cell tile planes.
 *
 * Results:
 *	Always returns 0, for DBCellEnum to keep going.
 *
 * Side effects:
 *	Writes a line for each use encountered to 'outf'.
 *	The line is of the following form:
 *
 *		use defname useid Ta ... Tf
 *
 *	where 'defname' is the name of the cell def referenced (cd_name),
 *	'useid' is its use identifier (cu_id), and Ta ... Tf are the six
 *	components of the transform from coordinates of this use up to
 *	its parent.  If the cell is an array, the use id may be followed by:
 *
 *		[xlo,xhi,xsep][ylo,yhi,ysep]
 *
 *	The indices are xlo through xhi inclusive, or ylo through yhi
 *	inclusive.  The separation between adjacent elements is xsep
 *	or ysep; this is used in computing the transform for a particular
 *	array element.  If arraying is not present in a given direction,
 *	the low and high indices are equal and the separation is ignored.
 *
 * ----------------------------------------------------------------------------
 */

int
extOutputUsesFunc(
    CellUse *cu,
    FILE *outf)
{
    Transform *t = &cu->cu_transform;

    if (cu->cu_def->cd_flags & CDDONTUSE) return 0;

    fprintf(outf, "use %s %s", cu->cu_def->cd_name, cu->cu_id);
    if (cu->cu_xlo != cu->cu_xhi || cu->cu_ylo != cu->cu_yhi)
    {
	fprintf(outf, "[%d:%d:%d]",
			cu->cu_xlo, cu->cu_xhi, cu->cu_xsep);
	fprintf(outf, "[%d:%d:%d]",
			cu->cu_ylo, cu->cu_yhi, cu->cu_ysep);
    }

    /* Output transform to parent */
    fprintf(outf, " %d %d %d %d %d %d\n",
			t->t_a, t->t_b, t->t_c, t->t_d, t->t_e, t->t_f);

    return (0);
}
