/*
 * DBtech.c --
 *
 * Technology initialization for the database module.
 * This file handles overall initialization, construction
 * of the general-purpose exported TileTypeBitMasks, and
 * the "connect" section.
 *
 *     *********************************************************************
 *     * 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/database/DBtech.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $";
#endif  /* not lint */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "utils/magic.h"
#include "utils/geometry.h"
#include "utils/utils.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "database/databaseInt.h"
#include "utils/tech.h"
#include "textio/textio.h"
#include "utils/malloc.h"

/* Name of this technology */
char *DBTechName = 0;
char *DBTechVersion = 0;
char *DBTechDescription = 0;

/* Connectivity */
TileTypeBitMask	 DBConnectTbl[NT];
TileTypeBitMask	 DBNotConnectTbl[NT];
PlaneMask	 DBConnPlanes[NT];
PlaneMask	 DBAllConnPlanes[NT];

/*
 * ----------------------------------------------------------------------------
 *
 * DBTechInit --
 *
 * Clear technology description information for database module.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

void
DBTechInit()
{
    /* StrDup() takes care of reallocating DBTechName */

    /* TECH_FORMAT_VERSION is defined in utils/tech.h,	*/
    /* and should be 27, the last version to be		*/
    /* included as part of the tech filename suffix.	*/

    TechFormatVersion = TECH_FORMAT_VERSION;

    /* Initialization of bezier coefficients for font vectors */
    DBFontInitCurves();
}


/*
 * ----------------------------------------------------------------------------
 *
 * DBTechSetTech --
 *
 * Set the name for the technology.
 *
 * Results:
 *	Returns FALSE if there were an improper number of
 *	tokens on the line.
 *
 * Side effects:
 *	Sets DBTechName to the name of the technology.
 *
 * ----------------------------------------------------------------------------
 */

    /*ARGSUSED*/
bool
DBTechSetTech(
    char *sectionName,
    int argc,
    char *argv[])
{
    if (argc != 1)
    {
	if (argc == 2)
	{
	    if (strncmp(argv[0], "format", 6) == 0 ||
			strncmp(argv[0], "version", 7) == 0)
	    {
		if (StrIsInt(argv[1]))
		{
		    TechFormatVersion = atoi(argv[1]);
		    return TRUE;
		}
		else
		{
		    TechError("Bad format version number. . . assuming %d\n",
				TECH_FORMAT_VERSION);
		    return TRUE;
		}
	    }
	}
	TechError("Badly formed technology name\n");
	return FALSE;
    }
    (void) StrDup(&DBTechName, argv[0]);
    return TRUE;
}

/*
 * ----------------------------------------------------------------------------
 *
 * DBTechInitVersion --
 *
 *	Clean up memory allocated by the "version" section
 *
 * ----------------------------------------------------------------------------
 */

void
DBTechInitVersion()
{
    /* StrDup() takes care of reallocating DBTechVersion and	*/
    /* DBTechDescription.					*/
}

/*
 * ----------------------------------------------------------------------------
 *
 * DBTechSetVersion --
 *
 * Set the version number & description for the technology.
 *
 * Results:
 *	Returns FALSE if there were an improper number of
 *	tokens on the line.
 *
 * Side effects:
 *	Sets DBTechVersion and DBTechDescription.
 *
 * ----------------------------------------------------------------------------
 */

    /*ARGSUSED*/
bool
DBTechSetVersion(
    char *sectionName,
    int argc,
    char *argv[])
{
    char *contline;
    int n, slen;

    if (argc < 2) goto usage;
    if (strcmp(argv[0], "version") == 0)
    {
	(void) StrDup(&DBTechVersion, argv[1]);
	for (n = 2; n < argc; n++)
	{
	    slen = strlen(DBTechVersion);
	    contline = mallocMagic(strlen(argv[n]) + slen + 1);
	    sprintf(contline, "%s\n%s", DBTechVersion, argv[n]);
	    freeMagic(DBTechVersion);
	    DBTechVersion = contline;
	}
	return TRUE;
    }
    if (strcmp(argv[0], "description") == 0)
    {
	(void) StrDup(&DBTechDescription, argv[1]);
	for (n = 2; n < argc; n++)
	{
	    slen = strlen(DBTechDescription);
	    contline = mallocMagic(strlen(argv[n]) + slen + 1);
	    sprintf(contline, "%s\n%s", DBTechDescription, argv[n]);
	    freeMagic(DBTechDescription);
	    DBTechDescription = contline;
	}
	return TRUE;
    }
    if (strcmp(argv[0], "requires") == 0)
    {
	/* Version requirement check.  If the techfile has "requires" followed
	 * by a magic version number in the form [magic-]<major>.<minor>.<revision>,
	 * then the version of magic is checked against this, and the tech
	 * loading will fail if there is a version incompatibility.
	 */

	int major, minor, rev;
	int rmajor, rminor, rrev;
	bool goodversion = FALSE;
	char *vstring;

	vstring = argv[1];
	while ((*vstring != '\0') && !isdigit(*vstring)) vstring++;

	major = minor = rev = 0;
	rmajor = rminor = rrev = 0;
	
	if (sscanf(vstring, "%d.%d.%d", &rmajor, &rminor, &rrev) != 3)
	{
	    TechError("Badly formed magic version string, should be major.minor.rev\n");
	    return FALSE;
	}
	sscanf(MagicVersion, "%d.%d", &major, &minor);
	sscanf(MagicRevision, "%d", &rev);
	if (major > rmajor)
	    goodversion = TRUE;
	else if (major == rmajor)
	{
	    if (minor > rminor)
		goodversion = TRUE;
	    else if (minor == rminor)
	    {
		if (rev >= rrev)
		    goodversion = TRUE;
	    }
	}
	if (goodversion == FALSE)
	{
	    TechError("Error:  Magic version %d.%d.%d is required by this "
		    "techfile, but this version of magic is %d.%d.%d.\n",
		    rmajor, rminor, rrev, major, minor, rev);
	    return FALSE;
	}
	return TRUE;
    }

usage:
    TechError("Badly formed version line\n"
	    "Usage: {version text}|{description text}|{requires text}\n");
    return FALSE;
}

/*
 * ----------------------------------------------------------------------------
 *
 * DBTechInitConnect --
 *
 * Initialize the connectivity tables.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Initializes DBConnectTbl[], DBConnPlanes[], and DBAllConnPlanes[].
 *
 * ----------------------------------------------------------------------------
 */

void
DBTechInitConnect()
{
    int i;

    for (i = 0; i < TT_MAXTYPES; i++)
    {
	TTMaskSetOnlyType(&DBConnectTbl[i], i);
	DBConnPlanes[i] = 0;
	DBAllConnPlanes[i] = 0;
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * DBTechAddConnect --
 *
 * Add connectivity information.
 * Record the fact that material of the types in the comma-separated
 * list types1 connects to material of the types in the list types2.
 *
 * Results:
 *	TRUE if successful, FALSE on error
 *
 * Side effects:
 *	Updates DBConnectTbl[].
 *
 * ----------------------------------------------------------------------------
 */

    /*ARGSUSED*/
bool
DBTechAddConnect(
    char *sectionName,
    int argc,
    char *argv[])
{
    TileTypeBitMask types1, types2;
    TileType t1, t2;

    if (argc != 2)
    {
	TechError("Line must contain exactly 2 lists of types\n");
	return FALSE;
    }

    DBTechNoisyNameMask(argv[0], &types1);
    DBTechNoisyNameMask(argv[1], &types2);
    for (t1 = 0; t1 < DBNumTypes; t1++)
	if (TTMaskHasType(&types1, t1))
	    for (t2 = 0; t2 < DBNumTypes; t2++)
		if (TTMaskHasType(&types2, t2))
		{
		    TTMaskSetType(&DBConnectTbl[t1], t2);
		    TTMaskSetType(&DBConnectTbl[t2], t1);
		}


    return TRUE;
}

/*
 * ----------------------------------------------------------------------------
 *
 * DBTechFinalConnect --
 *
 * Postprocessing for the connectivity information.
 * Modify DBConnectTbl[] so that:
 *
 *	(1) Any type connecting to one of the images of a contact
 *	    connects to all images of the contact.
 *	(2) Each image of a contact connects to the union of what
 *	    all the images connect to.
 *
 * Modify DBConnPlanes[] so that only types belonging to a contact
 * appear to connect to any plane other than their own.
 *
 * Constructs DBAllConnPlanes, which will be non-zero for those planes
 * to which each type connects, exclusive of that type's home plane and
 * those planes to which it connects as a contact.
 *
 * Create DBNotConnectTbl[], the complement of DBConnectTbl[].
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Modifies DBConnPlanes[], DBAllConnPlanes[], and DBConnectTbl[]
 *	as above.
 *
 * ----------------------------------------------------------------------------
 */

void
DBTechFinalConnect()
{
    /* TileTypeBitMask saveConnect[TT_MAXTYPES]; */
    TileTypeBitMask *cMask, *rMask;
    TileType base, s;
    LayerInfo *lp, *ls;
    int n;

    for (s = 0; s < DBNumTypes; s++)
	DBConnPlanes[s] = 0;

    /*
     * Each stacked contact type must necessarily connect to its
     * residual contact types, and the connecting types of those
     * contacts.  Each stacked contact type also connects to
     * other contact types that share (first) residues.
     */
    for (base = DBNumUserLayers; base < DBNumTypes; base++)
    {
	TileTypeBitMask *smask, *rmask = DBResidueMask(base), cmask;
	TTMaskSetMask(&DBConnectTbl[base], rmask);
	for (s = TT_TECHDEPBASE; s < DBNumUserLayers; s++)
	    if (TTMaskHasType(rmask, s))
		TTMaskSetMask(&DBConnectTbl[base], &DBConnectTbl[s]);

	/* Only need to start above "base";  the mirror-image	*/
	/* connection is ensured by the code below.		*/

	for (s = base + 1; s < DBNumTypes; s++)
	{
	    smask = DBResidueMask(s);
	    TTMaskAndMask3(&cmask, smask, rmask);
	    if (!TTMaskIsZero(&cmask))
		TTMaskSetType(&DBConnectTbl[base], s);
	}
    }

    /* Make the connectivity matrix symmetric */
    for (base = TT_TECHDEPBASE; base < DBNumTypes; base++)
	for (s = TT_TECHDEPBASE; s < DBNumTypes; s++)
	    if (TTMaskHasType(&DBConnectTbl[base], s))
		TTMaskSetType(&DBConnectTbl[s], base);

    /* Construct DBNotConnectTbl[] */
    /* For purposes of connectivity searching, this is not exactly the	*/
    /* complement of DBConnectTbl[].  For non-contact layers, it is.	*/
    /* For contact images, we compute differently to account for	*/
    /* contacts being stacked by having different parts in different	*/
    /* cells, which requires a different mask.				*/

    for (base = 0; base < TT_MAXTYPES; base++)
	TTMaskCom2(&DBNotConnectTbl[base], &DBConnectTbl[base]);

    for (n = 0; n < dbNumContacts; n++)
    {
	lp = dbContactInfo[n];
	TTMaskZero(&DBNotConnectTbl[lp->l_type]);
	TTMaskSetMask(&DBNotConnectTbl[lp->l_type], &DBConnectTbl[lp->l_type]);
	rMask = DBResidueMask(lp->l_type);

	/* Different contact types may share residues.		*/
	/* Use TTMaskIntersect(), not TTMaskEqual()---types	*/
	/* which otherwise stack may be in separate cells.	*/

	for (s = 0; s < dbNumContacts; s++)
	{
	    ls = dbContactInfo[s];
	    cMask = DBResidueMask(ls->l_type);
	    if (TTMaskIntersect(rMask, cMask))
		TTMaskSetType(&DBNotConnectTbl[lp->l_type], ls->l_type);
	}

	/* The mask of contact types must include all stacked contacts */

	for (base = DBNumUserLayers; base < DBNumTypes; base++)
	{
	    cMask = DBResidueMask(base);
	    if (TTMaskHasType(cMask, lp->l_type))
		TTMaskSetType(&DBNotConnectTbl[lp->l_type], base);
	}

	/* Note that everything above counted for all the	*/
	/* connecting types.  Invert this to get non-connecting	*/
	/* types.						*/

	TTMaskCom(&DBNotConnectTbl[lp->l_type]);
    }

    /*
     * DBConnPlanes[] is nonzero only for contact images, for which
     * it shows the planes connected to an image.  It is the
     * responsibility of the routine examining this value to exclude
     * the plane being searched, if necessary.
     */
    for (n = 0; n < dbNumContacts; n++)
    {
	lp = dbContactInfo[n];
	DBConnPlanes[lp->l_type] = lp->l_pmask;
    }

    /*
     * Now finally construct DBAllConnPlanes, which will be non-zero
     * for those planes to which each type 'base' connects, exclusive
     * of its home plane and those planes to which it connects as a
     * contact.
     *
     * Note that DBAllConnPlanes() is used to check for connectivity
     * (e.g., during extraction) where connections are made where no
     * contact exists.  This is VERY compute-intensive, so adding
     * planes unnecessarily must be avoided at all costs.  Consider
     * that a better alternative might be to designate layers that
     * connect to other planes as contacts themselves (such as
     * nsd, which connects to nwell) and restrict the "connect" section
     * to types in a plane (such as poly and transistor).
     */

    for (base = TT_TECHDEPBASE; base < DBNumTypes; base++)
    {
	TileTypeBitMask baseConnList;

	/* For one, remove all contact types from the list of	*/
	/* connecting types.  Otherwise, simple connect		*/
	/* expressions like "*m1 *m1" have the unintended	*/
	/* consequence of connecting the metal2 plane (because	*/
	/* via contains m1) to the active plane (because active	*/
	/* contacts also contain m1).				*/

	baseConnList = DBConnectTbl[base];
	for (s = 0; s < dbNumContacts; s++)
	{
	    ls = dbContactInfo[s];
	    TTMaskClearType(&baseConnList, ls->l_type);
	}

	DBAllConnPlanes[base] = DBTechTypesToPlanes(&baseConnList);
	DBAllConnPlanes[base] &= ~(PlaneNumToMaskBit(DBPlane(base)));
	DBAllConnPlanes[base] &= ~DBConnPlanes[base];
    }
}
