/*
 * lefTech.c --      
 *
 * This module incorporates the LEF/DEF format for standard-cell place and
 * route.  Defines technology file layer mapping between LEF and magic layer
 * types.
 *
 * Version 0.1 (December 5, 2003):  LEF section of the technology file.
 * Lets the technology file state how layers declared in the LEF and DEF
 * files should correspond to magic layout units.  If these are not
 * declared, magic will attempt to relate layer names in the LEF file to
 * magic layer names and their aliases.  The LEF section allows the declaration
 * of obstruction layer names, which may be the same as actual layer names in
 * the LEF or DEF file, but which should declare a type that takes up space
 * on the routing plane but does not connect to any other material.  Otherwise,
 * some LEF/DEF files will end up merging ports together with the obstruction
 * layer.
 *
 * Layer name case sensitivity should depend on the
 * NAMESCASESENSITIVE [ON|OFF] LEF instruction!
 */

#ifndef lint
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic/lef/lefTech.c,v 1.2 2005/04/02 23:58:24 tim Exp $";
#endif  /* not lint */

#include <stdio.h>
#include <stdlib.h>
#ifdef SYSV
#include <string.h>
#endif

#include "tcltk/tclmagic.h"
#include "utils/magic.h"
#include "utils/geometry.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "utils/malloc.h"
#include "lef/lefInt.h"
#include "drc/drc.h"

/* ---------------------------------------------------------------------*/

/* Layer and Via routing information table.  */

HashTable LefInfo;

/*
 *-----------------------------------------------------------------------------
 *  LefInit --
 *
 *	Things that need to be initialized on startup, before the technology
 *	file is read in.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Hash Table initialization.
 *
 *-----------------------------------------------------------------------------
 */

void
LefInit()
{
    /* Ensure that the table has a null entry so we don't run HashKill	*/
    /* on it when we to the tech initialization.			*/

    LefInfo.ht_table = (HashEntry **) NULL;
}

/*
 *-----------------------------------------------------------------------------
 *	LefTechInit --
 *
 *	Called once at beginning of technology file read-in to initialize
 *	data structures.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Clears out the LEF layer info table.  Because multiple LEF names
 *	can point to the same record, this is handled by reference count.
 * ----------------------------------------------------------------------------
 */

void
LefTechInit()
{
    HashSearch hs;
    HashEntry *he;
    lefLayer *lefl;

    if (LefInfo.ht_table != (HashEntry **) NULL)
    {
	HashStartSearch(&hs);
	while (he = HashNext(&LefInfo, &hs))
	{
	    lefl = (lefLayer *)HashGetValue(he);
	    lefl->refCnt--;
	    if (lefl->refCnt <= 0)
	    {
		/* Via detailed information, if it exists,	*/
		/* needs to have its allocated memory free'd.	*/

		if (lefl->is_via)
		    if (lefl->info.via.lr != NULL)
			freeMagic(lefl->info.via.lr);

		freeMagic(lefl);
	    }
	}
	HashKill(&LefInfo);
    }
    HashInit(&LefInfo, 32, HT_STRINGKEYS);
}

/*
 *-----------------------------------------------------------------------------
 *  LefTechLine --
 *
 *	This procedure is invoked by the technology module once for
 *	each line in the "lef" section of the technology file.
 *
 * Results:
 *	Always returns TRUE (otherwise the technology module would
 *	abort Magic with a fatal error).
 *
 * Side effects:
 *	Builds up the LEF layer table, prints error messages if necessary.
 * ----------------------------------------------------------------------------
 */

bool
LefTechLine(sectionName, argc, argv)
    char *sectionName;          /* Name of this section (unused). */
    int argc;                   /* Number of arguments on line. */
    char *argv[];               /* Pointers to fields of line. */
{
    bool isObstruction, isContact;
    HashEntry *he;
    TileType mtype;
    lefLayer *lefl, *newlefl;
    int i;

    if (!strncmp(argv[0], "obs", 3))
	isObstruction = TRUE;
    else if (!strncmp(argv[0], "layer", 5))
	isObstruction = FALSE;
    else
    {
        TechError("Unknown LEF section keyword: %s.  Line ignored.\n", argv[0]);
        return TRUE;
    }

    if (argc < 3)
    {
        TechError("No LEF layer names present!\n");
	return TRUE;
    }

    mtype = DBTechNoisyNameType(argv[1]);
    if (mtype < 0) return TRUE;
    isContact = DBIsContact(mtype);

    /* All other aliases are stuffed in the hash table but point to the	*/
    /* same record as the first.  If any name is repeated, then report	*/
    /* an error condition.						*/

    newlefl = NULL;
    for (i = 2; i < argc; i++)
    {
	he = HashFind(&LefInfo, argv[i]);
	lefl = (lefLayer *)HashGetValue(he);
	if (lefl == NULL)
	{
	    /* Create an entry in the hash table for this layer or obstruction */

	    if (newlefl == NULL)
	    {
		newlefl = (lefLayer *)mallocMagic(sizeof(lefLayer));
		newlefl->refCnt = 0;
		newlefl->type = -1;
		newlefl->obsType = -1;
		if (isObstruction)
		   newlefl->obsType = mtype;
		else
		   newlefl->type = mtype;

		newlefl->is_via = isContact;

		/* Fill in default values for type */
		if (isContact)
		{
		    newlefl->info.via.area = GeoNullRect;
		    newlefl->info.via.cell = (CellDef *)NULL;
		    newlefl->info.via.lr = (linkedRect *)NULL;
		}
		else
		{
		    float oscale = CIFGetOutputScale(1000);
		    newlefl->info.route.width = DRCGetDefaultLayerWidth(mtype);
		    if (newlefl->info.route.width == 0)
			newlefl->info.route.width = DEFAULT_WIDTH;
		    newlefl->info.route.spacing = DRCGetDefaultLayerSpacing(mtype,
				mtype);
		    if (newlefl->info.route.spacing == 0)
			newlefl->info.route.spacing = DEFAULT_SPACING;
		    newlefl->info.route.pitch = 0;
		    newlefl->info.route.hdirection = TRUE;
		}
	    }
	    HashSetValue(he, newlefl);
	    newlefl->refCnt++;
	}
	else
	{
	    if ((lefl->obsType == -1) && isObstruction)
	    {
//		if (DBIsContact(lefl->type) != isContact)
//		    TechError("Error: Cannot mix layer and via types\n");
//		else
		    lefl->obsType = mtype;
	    }
	    else if ((lefl->type == -1) && !isObstruction)
	    {
//		if (DBIsContact(lefl->obsType) != isContact)
//		    TechError("Error: Cannot mix layer and via types\n");
//		else
		    lefl->type = mtype;
	    }
	    else
		TechError("LEF name %s already used for magic type %s\n",
				argv[i], DBTypeLongNameTbl[lefl->type]);
	}
    }
    return TRUE;
}

/*
 *-----------------------------------------------------------------------------
 *  LefTechScale --
 *
 *	Change parameters of the LEF section as required when
 *	redefining magic's internal grid relative to the technology lambda. 
 *
 * ----------------------------------------------------------------------------
 */

void
LefTechScale(scalen, scaled)
    int scalen, scaled;
{
    HashSearch hs;
    HashEntry *he;
    lefLayer *lefl;

    if (LefInfo.ht_table != (HashEntry **) NULL)
    {
	HashStartSearch(&hs);
	while (he = HashNext(&LefInfo, &hs))
	{
	    lefl = (lefLayer *)HashGetValue(he);
	    if (lefl->is_via)
	    {
		DBScalePoint(&lefl->info.via.area.r_ll, scalen, scaled);
		DBScalePoint(&lefl->info.via.area.r_ur, scalen, scaled);
	    }
	    else
	    {
		lefl->info.route.width *= scalen;
		lefl->info.route.width /= scaled;
		lefl->info.route.spacing *= scalen;
		lefl->info.route.spacing /= scaled;
		lefl->info.route.pitch *= scalen;
		lefl->info.route.pitch /= scaled;
	    }
	}
    }
}
