2642 lines
70 KiB
C
2642 lines
70 KiB
C
/* CIFtech.c -
|
|
*
|
|
* This module processes the portions of technology
|
|
* files pertaining to CIF, and builds the tables
|
|
* used by the CIF generator.
|
|
*
|
|
* *********************************************************************
|
|
* * 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 const char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/cif/CIFtech.c,v 1.7 2010/10/20 20:34:19 tim Exp $";
|
|
#endif /* not lint */
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h> /* for atof() */
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <math.h> /* for pow() */
|
|
|
|
#include "utils/magic.h"
|
|
#include "utils/geometry.h"
|
|
#include "tiles/tile.h"
|
|
#include "utils/hash.h"
|
|
#include "database/database.h"
|
|
#include "utils/tech.h"
|
|
#include "utils/utils.h"
|
|
#include "utils/styles.h"
|
|
#include "cif/CIFint.h"
|
|
#include "calma/calmaInt.h"
|
|
#include "textio/textio.h"
|
|
#include "utils/malloc.h"
|
|
#include "cif/cif.h"
|
|
#include "drc/drc.h" /* For WRL's DRC-CIF extensions */
|
|
|
|
/* C99 compat */
|
|
#include "calma/calma.h"
|
|
#include "dbwind/dbwind.h"
|
|
#include "drc/drc.h"
|
|
|
|
/* The following statics are used to keep track of things between
|
|
* calls to CIFTechLine.
|
|
*/
|
|
|
|
static CIFLayer *cifCurLayer; /* Current layer whose spec. is being read. */
|
|
static CIFOp *cifCurOp; /* Last geometric operation read in. */
|
|
static bool cifGotLabels; /* TRUE means some labels have been assigned
|
|
* to the current layer.
|
|
*/
|
|
|
|
/* The following is a TileTypeBitMask array with only the CIF_SOLIDTYPE
|
|
* bit set in it.
|
|
*/
|
|
TileTypeBitMask CIFSolidBits;
|
|
|
|
/* Forward Declarations */
|
|
|
|
void cifTechStyleInit(void);
|
|
bool cifCheckCalmaNum(char *str);
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* cifTechFreeStyle --
|
|
*
|
|
* This procedure frees memory for the current CIF style, and
|
|
* sets the current style to NULL.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Memory is free'd.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
cifTechFreeStyle(void)
|
|
{
|
|
int i;
|
|
CIFOp *op;
|
|
CIFLayer *layer;
|
|
|
|
if (CIFCurStyle != NULL)
|
|
{
|
|
/* Destroy old style structure and free memory allocated to it */
|
|
|
|
for (i = 0; i < MAXCIFLAYERS; i++)
|
|
{
|
|
layer = CIFCurStyle->cs_layers[i];
|
|
if (layer != NULL)
|
|
{
|
|
for (op = layer->cl_ops; op != NULL; op = op->co_next)
|
|
{
|
|
if (op->co_client != (ClientData)NULL)
|
|
{
|
|
switch (op->co_opcode)
|
|
{
|
|
case CIFOP_OR:
|
|
case CIFOP_BBOX:
|
|
case CIFOP_MAXRECT:
|
|
case CIFOP_BOUNDARY:
|
|
case CIFOP_INTERACT:
|
|
case CIFOP_MANHATTAN:
|
|
/* These options use co_client to hold a single */
|
|
/* integer value, so it is not allocated. */
|
|
break;
|
|
default:
|
|
freeMagic((char *)op->co_client);
|
|
break;
|
|
}
|
|
}
|
|
freeMagic((char *)op);
|
|
}
|
|
freeMagic((char *)layer);
|
|
}
|
|
}
|
|
freeMagic(CIFCurStyle);
|
|
CIFCurStyle = NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* cifTechNewStyle --
|
|
*
|
|
* This procedure creates a new CIF style at the end of
|
|
* the list of style and initializes it to completely
|
|
* null.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* A new element is added to the end of CIFStyleList, and CIFCurStyle
|
|
* is set to point to it.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
cifTechNewStyle(void)
|
|
{
|
|
cifTechFreeStyle();
|
|
cifTechStyleInit();
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* cifTechStyleInit --
|
|
*
|
|
* Fill in the current cif input style structure with initial values
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
cifTechStyleInit(void)
|
|
{
|
|
int i;
|
|
|
|
if (CIFCurStyle == NULL)
|
|
CIFCurStyle = (CIFStyle *) mallocMagic(sizeof(CIFStyle));
|
|
|
|
CIFCurStyle->cs_name = NULL;
|
|
CIFCurStyle->cs_status = TECH_NOT_LOADED;
|
|
|
|
CIFCurStyle->cs_nLayers = 0;
|
|
CIFCurStyle->cs_scaleFactor = 0;
|
|
CIFCurStyle->cs_stepSize = 0;
|
|
CIFCurStyle->cs_gridLimit = 0;
|
|
CIFCurStyle->cs_reducer = 0;
|
|
CIFCurStyle->cs_expander = 1;
|
|
CIFCurStyle->cs_yankLayers = DBZeroTypeBits;
|
|
CIFCurStyle->cs_hierLayers = DBZeroTypeBits;
|
|
CIFCurStyle->cs_flags = 0;
|
|
for (i=0; i<TT_MAXTYPES; i+=1)
|
|
{
|
|
CIFCurStyle->cs_labelLayer[i] = -1;
|
|
CIFCurStyle->cs_portLayer[i] = -1;
|
|
CIFCurStyle->cs_portText[i] = -1;
|
|
}
|
|
for (i = 0; i < MAXCIFLAYERS; i++)
|
|
CIFCurStyle->cs_layers[i] = NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* cifParseLayers --
|
|
*
|
|
* Takes a comma-separated list of layers and turns it into two
|
|
* masks, one of paint layers and one of previously-defined CIF
|
|
* layers.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* The masks pointed to by the paintMask and cifMask parameters
|
|
* are modified. If some of the layers are unknown, then an error
|
|
* message is printed.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
cifParseLayers(
|
|
char *string, /* List of layers. */
|
|
CIFStyle *style, /* Gives CIF style for parsing string.*/
|
|
TileTypeBitMask *paintMask, /* Place to store mask of paint layers. If
|
|
* NULL, then only CIF layer names are
|
|
* considered.
|
|
*/
|
|
TileTypeBitMask *cifMask, /* Place to store mask of CIF layers. If
|
|
* NULL, then only paint layer names are
|
|
* considered.
|
|
*/
|
|
int spaceOK) /* are space layers permissible in this cif
|
|
layer?
|
|
*/
|
|
{
|
|
TileTypeBitMask curCifMask, curPaintMask;
|
|
char curLayer[40], *p, *cp;
|
|
TileType paintType;
|
|
int i;
|
|
bool allResidues;
|
|
|
|
if (paintMask != NULL) TTMaskZero(paintMask);
|
|
if (cifMask != NULL) TTMaskZero(cifMask);
|
|
|
|
while (*string != 0)
|
|
{
|
|
p = curLayer;
|
|
|
|
if (*string == '*')
|
|
{
|
|
allResidues = TRUE;
|
|
string++;
|
|
}
|
|
else
|
|
allResidues = FALSE;
|
|
|
|
while ((*string != ',') && (*string != 0))
|
|
*p++ = *string++;
|
|
*p = 0;
|
|
while (*string == ',') string += 1;
|
|
|
|
/* See if this is a paint type. */
|
|
|
|
if (paintMask != NULL)
|
|
{
|
|
paintType = DBTechNameTypes(curLayer, &curPaintMask);
|
|
if (paintType >= 0) goto okpaint;
|
|
}
|
|
else paintType = -2;
|
|
|
|
okpaint:
|
|
/* See if this is the name of another CIF layer. Be
|
|
* careful not to let the current layer be used in
|
|
* generating itself. Exact match is requred on CIF
|
|
* layer names, but the same name can appear multiple
|
|
* times in different styles.
|
|
*/
|
|
|
|
TTMaskZero(&curCifMask);
|
|
if (cifMask != NULL)
|
|
{
|
|
for (i = 0; i < style->cs_nLayers; i++)
|
|
{
|
|
if (style->cs_layers[i] == cifCurLayer) continue;
|
|
if (strcmp(curLayer, style->cs_layers[i]->cl_name) == 0)
|
|
{
|
|
TTMaskSetType(&curCifMask, i);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Make sure that there's exactly one match among cif and
|
|
* paint layers together.
|
|
*/
|
|
|
|
if ((paintType == -1)
|
|
|| ((paintType >= 0) && !TTMaskEqual(&curCifMask, &DBZeroTypeBits)))
|
|
{
|
|
TechError("Ambiguous layer (type) \"%s\".\n", curLayer);
|
|
continue;
|
|
}
|
|
if (paintType >= 0)
|
|
{
|
|
if (paintType == TT_SPACE && spaceOK ==0)
|
|
TechError("\"Space\" layer not permitted in CIF rules.\n");
|
|
else
|
|
{
|
|
TileType rtype;
|
|
TileTypeBitMask *rMask;
|
|
|
|
TTMaskSetMask(paintMask, &curPaintMask);
|
|
|
|
/* Add residues from '*' notation */
|
|
if (allResidues)
|
|
for (rtype = TT_TECHDEPBASE; rtype < DBNumUserLayers; rtype++)
|
|
{
|
|
rMask = DBResidueMask(rtype);
|
|
if (TTMaskHasType(rMask, paintType))
|
|
TTMaskSetType(paintMask, rtype);
|
|
}
|
|
}
|
|
}
|
|
else if (!TTMaskEqual(&curCifMask, &DBZeroTypeBits))
|
|
{
|
|
TTMaskSetMask(cifMask, &curCifMask);
|
|
}
|
|
else
|
|
{
|
|
HashEntry *he;
|
|
TileTypeBitMask *amask;
|
|
|
|
he = HashLookOnly(&DBTypeAliasTable, curLayer);
|
|
if (he != NULL)
|
|
{
|
|
amask = (TileTypeBitMask *)HashGetValue(he);
|
|
TTMaskSetMask(paintMask, amask);
|
|
}
|
|
else
|
|
TechError("Unrecognized layer (type) \"%s\".\n", curLayer);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* CIFTechInit --
|
|
*
|
|
* Called before loading a new technology.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Clears out list of styles and resets the current style.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
CIFTechInit(void)
|
|
{
|
|
CIFKeep *style;
|
|
|
|
/* Cleanup any old info. */
|
|
|
|
cifTechFreeStyle();
|
|
|
|
/* forget the list of styles */
|
|
|
|
for (style = CIFStyleList; style != NULL; style = style->cs_next)
|
|
{
|
|
freeMagic(style->cs_name);
|
|
freeMagic(style);
|
|
}
|
|
CIFStyleList = NULL;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* CIFTechStyleInit --
|
|
*
|
|
* Called once at beginning of technology file read-in to
|
|
* initialize data structures.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Just clears out the layer data structures.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
CIFTechStyleInit(void)
|
|
{
|
|
CalmaTechInit();
|
|
|
|
/* Create the TileTypeBitMask array with only the CIF_SOLIDTYPE bit set */
|
|
TTMaskZero(&CIFSolidBits);
|
|
TTMaskSetType(&CIFSolidBits, CIF_SOLIDTYPE);
|
|
|
|
cifCurOp = NULL;
|
|
cifCurLayer = NULL;
|
|
cifGotLabels = FALSE;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* CIFTechLimitScale --
|
|
*
|
|
* Determine if the scalefactor (ns / ds), applied to the current
|
|
* grid scaling, would result in a grid finer than the minimum
|
|
* resolution allowed by the process, as set by the "gridlimit"
|
|
* statement in the "cifoutput" section (note that the scaling
|
|
* depends on the output style chosen, and can be subverted by
|
|
* scaling while a fine-grid output style is active, then switching
|
|
* to a coarse-grid output style).
|
|
*
|
|
* Note that even if the scalefactor is larger than the minimum
|
|
* grid, it must be a MULTIPLE of the minimum grid, or else geometry
|
|
* can be generated off-grid.
|
|
*
|
|
* Results:
|
|
* TRUE if scaling by (ns / ds) would violate minimum grid resolution,
|
|
* FALSE if not.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
bool
|
|
CIFTechLimitScale(
|
|
int ns,
|
|
int ds)
|
|
{
|
|
int gridup, scaledown;
|
|
int scale, limit, expand;
|
|
|
|
if (CIFCurStyle == NULL) return FALSE;
|
|
|
|
scale = CIFCurStyle->cs_scaleFactor;
|
|
limit = CIFCurStyle->cs_gridLimit;
|
|
expand = CIFCurStyle->cs_expander;
|
|
|
|
if (limit == 0) limit = 1;
|
|
|
|
gridup = limit * expand * ds;
|
|
scaledown = scale * ns * 10;
|
|
|
|
if ((scaledown / gridup) == 0) return TRUE;
|
|
if ((scaledown % gridup) != 0) return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* CIFParseScale --
|
|
*
|
|
* Read a scale value and determine scaleFactor and expander values
|
|
*
|
|
* Results:
|
|
* Returns the value for cs_scaleFactor
|
|
*
|
|
* Side effects:
|
|
* Alters the value of expander (pointer to cs_expander)
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
CIFParseScale(
|
|
char *true_scale,
|
|
int *expander)
|
|
{
|
|
char *decimal;
|
|
short places;
|
|
int n, d;
|
|
|
|
decimal = strchr(true_scale, '.');
|
|
|
|
if (decimal == NULL) /* true_scale is integer */
|
|
{
|
|
*expander = 1;
|
|
return atoi(true_scale);
|
|
}
|
|
else
|
|
{
|
|
*decimal = '\0';
|
|
places = strlen(decimal + 1);
|
|
d = pow(10,places);
|
|
n = atoi(true_scale);
|
|
*decimal = '.';
|
|
n *= d;
|
|
n += atoi(decimal + 1);
|
|
ReduceFraction(&n, &d);
|
|
*expander = d;
|
|
return n;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* CIFTechLine --
|
|
*
|
|
* This procedure is called once for each line in the "cif"
|
|
* section of the technology file.
|
|
*
|
|
* Results:
|
|
* TRUE if line parsed correctly; FALSE if fatal error condition
|
|
* encountered.
|
|
*
|
|
* Side effects:
|
|
* Sets up information in the tables of CIF layers, and
|
|
* prints error messages where there are problems.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
bool
|
|
CIFTechLine(
|
|
char *sectionName, /* The name of this section. */
|
|
int argc, /* Number of fields on line. */
|
|
char *argv[]) /* Values of fields. */
|
|
{
|
|
TileTypeBitMask mask, tempMask, cifMask, bloatLayers;
|
|
int i, j, l, distance;
|
|
CIFLayer *newLayer;
|
|
CIFOp *newOp = NULL;
|
|
CIFKeep *newStyle, *p;
|
|
char **bloatArg;
|
|
BloatData *bloats;
|
|
BridgeData *bridge;
|
|
SquaresData *squares;
|
|
SlotsData *slots;
|
|
|
|
if (argc <= 0) return TRUE;
|
|
else if (argc >= 2) l = strlen(argv[1]);
|
|
|
|
/* See if we're starting a new CIF style. If not, make
|
|
* sure that the current (maybe default?) CIF style has
|
|
* a name.
|
|
*/
|
|
|
|
if (strcmp(argv[0], "style") == 0)
|
|
{
|
|
if (argc != 2)
|
|
{
|
|
if ((argc != 4) || (strncmp(argv[2], "variant", 7)))
|
|
{
|
|
wrongNumArgs:
|
|
TechError("Wrong number of arguments in %s statement.\n",
|
|
argv[0]);
|
|
errorReturn:
|
|
if (newOp != NULL)
|
|
freeMagic((char *) newOp);
|
|
return TRUE;
|
|
}
|
|
}
|
|
for (newStyle = CIFStyleList; newStyle != NULL;
|
|
newStyle = newStyle->cs_next)
|
|
{
|
|
/* Here we're only establishing existence; */
|
|
/* break on the first variant found. */
|
|
|
|
if (!strncmp(newStyle->cs_name, argv[1], l))
|
|
break;
|
|
}
|
|
if (newStyle == NULL)
|
|
{
|
|
if (argc == 2)
|
|
{
|
|
newStyle = (CIFKeep *)mallocMagic(sizeof(CIFKeep));
|
|
newStyle->cs_next = NULL;
|
|
newStyle->cs_name = StrDup((char **) NULL, argv[1]);
|
|
|
|
/* Append to end of style list */
|
|
if (CIFStyleList == NULL)
|
|
CIFStyleList = newStyle;
|
|
else
|
|
{
|
|
for (p = CIFStyleList; p->cs_next; p = p->cs_next);
|
|
p->cs_next = newStyle;
|
|
}
|
|
}
|
|
else /* Handle style variants */
|
|
{
|
|
CIFKeep *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 = (CIFKeep *)mallocMagic(sizeof(CIFKeep));
|
|
newStyle->cs_next = NULL;
|
|
newStyle->cs_name = (char *)mallocMagic(l
|
|
+ strlen(tptr) + 1);
|
|
sprintf(newStyle->cs_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 (CIFStyleList == NULL)
|
|
CIFStyleList = newStyle;
|
|
else
|
|
{
|
|
for (p = CIFStyleList; p->cs_next; p = p->cs_next);
|
|
p->cs_next = newStyle;
|
|
}
|
|
|
|
if (cptr == NULL)
|
|
break;
|
|
else
|
|
tptr = cptr + 1;
|
|
}
|
|
newStyle = saveStyle;
|
|
}
|
|
}
|
|
|
|
if (CIFCurStyle == NULL)
|
|
{
|
|
cifTechNewStyle();
|
|
CIFCurStyle->cs_name = newStyle->cs_name;
|
|
CIFCurStyle->cs_status = TECH_PENDING;
|
|
}
|
|
else if ((CIFCurStyle->cs_status == TECH_PENDING) ||
|
|
(CIFCurStyle->cs_status == TECH_SUSPENDED))
|
|
CIFCurStyle->cs_status = TECH_LOADED;
|
|
else if (CIFCurStyle->cs_status == TECH_NOT_LOADED)
|
|
{
|
|
if (CIFCurStyle->cs_name == NULL)
|
|
return (FALSE);
|
|
else if (argc == 2)
|
|
{
|
|
if (!strcmp(argv[1], CIFCurStyle->cs_name))
|
|
CIFCurStyle->cs_status = TECH_PENDING;
|
|
}
|
|
else if (argc == 4)
|
|
{
|
|
/* Verify that the style matches one variant */
|
|
|
|
char *tptr, *cptr;
|
|
|
|
if (!strncmp(CIFCurStyle->cs_name, argv[1], l))
|
|
{
|
|
tptr = argv[3];
|
|
while (*tptr != '\0')
|
|
{
|
|
cptr = strchr(tptr, ',');
|
|
if (cptr != NULL) *cptr = '\0';
|
|
if (!strcmp(CIFCurStyle->cs_name + l, tptr))
|
|
{
|
|
CIFCurStyle->cs_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 cif output style */
|
|
if (CIFCurStyle == NULL) return FALSE;
|
|
if ((CIFCurStyle->cs_status != TECH_PENDING) &&
|
|
(CIFCurStyle->cs_status != TECH_SUSPENDED))
|
|
return TRUE;
|
|
|
|
/* Process scalefactor lines next. */
|
|
|
|
if (strcmp(argv[0], "scalefactor") == 0)
|
|
{
|
|
if ((argc < 2) || (argc > 4)) goto wrongNumArgs;
|
|
CIFCurStyle->cs_scaleFactor = CIFParseScale(argv[1],
|
|
&CIFCurStyle->cs_expander);
|
|
|
|
/*
|
|
* The "nanometers" keyword multiplies the expander by 10.
|
|
* Any reducer value and keyword "calmaonly" are now both ignored.
|
|
*/
|
|
|
|
if (argc >= 3)
|
|
{
|
|
if (strncmp(argv[argc - 1], "nanom", 5) == 0)
|
|
CIFCurStyle->cs_expander *= 10;
|
|
else if (strncmp(argv[argc - 1], "angstr", 6) == 0)
|
|
CIFCurStyle->cs_expander *= 100;
|
|
}
|
|
|
|
CIFCurStyle->cs_reducer = 1; /* initial value only */
|
|
|
|
if (CIFCurStyle->cs_scaleFactor <= 0)
|
|
{
|
|
CIFCurStyle->cs_scaleFactor = 0;
|
|
TechError("Scalefactor must be a strictly positive value.\n");
|
|
goto errorReturn;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/* New for magic-7.3.100---allow GDS database units to be other */
|
|
/* than nanometers. Really, there is only one option here, which */
|
|
/* is "units angstroms". This option does not affect CIF output, */
|
|
/* whose units are fixed at centimicrons. */
|
|
|
|
if (strcmp(argv[0], "units") == 0)
|
|
{
|
|
if (argc != 2) goto wrongNumArgs;
|
|
if (!strncmp(argv[1], "angstr", 6))
|
|
CIFCurStyle->cs_flags |= CWF_ANGSTROMS;
|
|
return TRUE;
|
|
}
|
|
|
|
if (strcmp(argv[0], "stepsize") == 0)
|
|
{
|
|
if (argc != 2) goto wrongNumArgs;
|
|
CIFCurStyle->cs_stepSize = atoi(argv[1]);
|
|
if (CIFCurStyle->cs_stepSize <= 0)
|
|
{
|
|
TechError("Step size must be positive integer.\n");
|
|
CIFCurStyle->cs_stepSize = 0;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/* Process "gridlimit" line next. */
|
|
if (strncmp(argv[0], "grid", 4) == 0)
|
|
{
|
|
if (StrIsInt(argv[1]))
|
|
{
|
|
CIFCurStyle->cs_gridLimit = atoi(argv[1]);
|
|
if (CIFCurStyle->cs_gridLimit < 0)
|
|
{
|
|
TechError("Grid limit must be a positive integer.\n");
|
|
CIFCurStyle->cs_gridLimit = 0;
|
|
}
|
|
}
|
|
else
|
|
TechError("Unable to parse grid limit value.\n");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* Process "variant" lines next. */
|
|
|
|
if (strncmp(argv[0], "variant", 7) == 0)
|
|
{
|
|
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 wrongNumArgs;
|
|
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 == '*')
|
|
{
|
|
CIFCurStyle->cs_status = TECH_PENDING;
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
l = strlen(CIFCurStyle->cs_name) - strlen(tptr);
|
|
if (!strcmp(tptr, CIFCurStyle->cs_name + l))
|
|
{
|
|
CIFCurStyle->cs_status = TECH_PENDING;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (cptr == NULL)
|
|
break;
|
|
else
|
|
tptr = cptr + 1;
|
|
}
|
|
CIFCurStyle->cs_status = TECH_SUSPENDED;
|
|
}
|
|
|
|
/* Anything below this line is not parsed if we're in TECH_SUSPENDED mode */
|
|
if (CIFCurStyle->cs_status != TECH_PENDING) return TRUE;
|
|
|
|
newLayer = NULL;
|
|
if ((strcmp(argv[0], "templayer") == 0) || (strcmp(argv[0], "layer") == 0) ||
|
|
(strcmp(argv[0], "labellayer") == 0))
|
|
{
|
|
if (CIFCurStyle->cs_nLayers == MAXCIFLAYERS)
|
|
{
|
|
cifCurLayer = NULL;
|
|
TechError("Can't handle more than %d CIF layers.\n", MAXCIFLAYERS);
|
|
TechError("Your local Magic wizard can fix this.\n");
|
|
goto errorReturn;
|
|
}
|
|
if (argc != 2 && argc != 3)
|
|
{
|
|
cifCurLayer = NULL;
|
|
goto wrongNumArgs;
|
|
}
|
|
newLayer = CIFCurStyle->cs_layers[CIFCurStyle->cs_nLayers]
|
|
= (CIFLayer *) mallocMagic(sizeof(CIFLayer));
|
|
CIFCurStyle->cs_nLayers += 1;
|
|
if ((cifCurOp == NULL) && (cifCurLayer != NULL) && !cifGotLabels)
|
|
{
|
|
TechError("Layer \"%s\" contains no material.\n",
|
|
cifCurLayer->cl_name);
|
|
}
|
|
newLayer->cl_name = NULL;
|
|
(void) StrDup(&newLayer->cl_name, argv[1]);
|
|
newLayer->cl_ops = NULL;
|
|
newLayer->cl_flags = 0;
|
|
newLayer->cl_calmanum = newLayer->cl_calmatype = -1;
|
|
newLayer->min_width = 0; /* for growSlivers */
|
|
#ifdef THREE_D
|
|
newLayer->cl_height = 0.0;
|
|
newLayer->cl_thick = 0.0;
|
|
newLayer->cl_renderStyle = STYLE_PALEHIGHLIGHTS - TECHBEGINSTYLES;
|
|
#endif
|
|
if (strcmp(argv[0], "templayer") == 0)
|
|
newLayer->cl_flags |= CIF_TEMP;
|
|
else if (strcmp(argv[0], "labellayer") == 0)
|
|
newLayer->cl_flags |= CIF_LABEL;
|
|
cifCurLayer = newLayer;
|
|
cifCurOp = NULL;
|
|
cifGotLabels = FALSE;
|
|
|
|
/* Handle a special case of a list of layer names on the layer
|
|
* line. Turn them into an OR operation.
|
|
*/
|
|
|
|
if (argc == 3)
|
|
{
|
|
cifCurOp = (CIFOp *) mallocMagic(sizeof(CIFOp));
|
|
cifCurOp->co_opcode = CIFOP_OR;
|
|
cifParseLayers(argv[2], CIFCurStyle, &cifCurOp->co_paintMask,
|
|
&cifCurOp->co_cifMask, FALSE);
|
|
cifCurOp->co_distance = 0;
|
|
cifCurOp->co_next = NULL;
|
|
cifCurOp->co_client = (ClientData)NULL;
|
|
cifCurLayer->cl_ops = cifCurOp;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
if (strcmp(argv[0], "labels") == 0)
|
|
{
|
|
bool portOnly = FALSE, noPort = FALSE, textOnly = FALSE;
|
|
|
|
if (cifCurLayer == NULL)
|
|
{
|
|
TechError("Must define layer before giving labels it holds.\n");
|
|
goto errorReturn;
|
|
}
|
|
if (cifCurLayer->cl_flags & CIF_TEMP)
|
|
TechError("Why are you attaching labels to a temporary layer?\n");
|
|
if (argc == 3)
|
|
{
|
|
if (!strncmp(argv[2], "port", 4))
|
|
portOnly = TRUE;
|
|
else if (!strncmp(argv[2], "noport", 6))
|
|
noPort = TRUE;
|
|
else if (!strncmp(argv[2], "text", 6))
|
|
textOnly = TRUE;
|
|
else
|
|
{
|
|
TechError("Unknown option %s for labels statement.\n", argv[2]);
|
|
goto wrongNumArgs;
|
|
}
|
|
}
|
|
else if (argc != 2) goto wrongNumArgs;
|
|
DBTechNoisyNameMask(argv[1], &mask);
|
|
for (i=0; i<TT_MAXTYPES; i+=1)
|
|
{
|
|
if (TTMaskHasType(&mask, i))
|
|
{
|
|
if (portOnly == TRUE)
|
|
{
|
|
/* With "port", use layer for port geometry.
|
|
* If the port text type has not been set, set it to
|
|
* this layer.
|
|
*/
|
|
CIFCurStyle->cs_portLayer[i] = CIFCurStyle->cs_nLayers-1;
|
|
if (CIFCurStyle->cs_portText[i] == -1)
|
|
CIFCurStyle->cs_portText[i] = CIFCurStyle->cs_nLayers-1;
|
|
}
|
|
else
|
|
{
|
|
/* For "noport" or no argument, the label text and data
|
|
* are set to this layer. If no argument, then set the
|
|
* port text type to this type. If a later "port" statement
|
|
* applies to the same layer, then the data type will be
|
|
* separate from the text type.
|
|
*/
|
|
CIFCurStyle->cs_labelLayer[i] = CIFCurStyle->cs_nLayers-1;
|
|
if (noPort == FALSE)
|
|
CIFCurStyle->cs_portText[i] = CIFCurStyle->cs_nLayers-1;
|
|
}
|
|
}
|
|
}
|
|
cifGotLabels = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
if ((strcmp(argv[0], "calma") == 0) || (strncmp(argv[0], "gds", 3) == 0))
|
|
{
|
|
if (cifCurLayer == NULL)
|
|
{
|
|
TechError("Must define layers before giving their Calma types.\n");
|
|
goto errorReturn;
|
|
}
|
|
if (cifCurLayer->cl_flags & CIF_TEMP)
|
|
TechError("Why assign a Calma number to a temporary layer?\n");
|
|
if (argc != 3) goto wrongNumArgs;
|
|
if (!cifCheckCalmaNum(argv[1]) || !cifCheckCalmaNum(argv[2]))
|
|
TechError("Calma layer and type numbers must be 0 to %d.\n",
|
|
CALMA_LAYER_MAX);
|
|
cifCurLayer->cl_calmanum = atoi(argv[1]);
|
|
cifCurLayer->cl_calmatype = atoi(argv[2]);
|
|
return TRUE;
|
|
}
|
|
if (strcmp(argv[0], "min-width") == 0) /* used in growSliver */
|
|
{
|
|
if (cifCurLayer == NULL)
|
|
{
|
|
TechError("Must define layers before assigning a minimum width.\n");
|
|
goto errorReturn;
|
|
}
|
|
if (argc != 2) goto wrongNumArgs;
|
|
cifCurLayer->min_width = atoi(argv[1]);
|
|
CIFCurStyle->cs_flags |= CWF_GROW_SLIVERS;
|
|
return TRUE;
|
|
}
|
|
|
|
if (strcmp(argv[0], "render") == 0) /* used by specialopen wind3d client */
|
|
{
|
|
#ifdef THREE_D
|
|
float height, thick;
|
|
int i, style, lcnt;
|
|
CIFLayer *layer;
|
|
|
|
if (argc != 5) goto wrongNumArgs;
|
|
|
|
cifCurLayer = NULL; /* This is not in a layer definition */
|
|
|
|
style = DBWTechParseStyle(argv[2]);
|
|
if (style < 0)
|
|
{
|
|
TechError("Error: Bad render style for CIF layer.\n");
|
|
goto errorReturn;
|
|
}
|
|
|
|
if (!StrIsNumeric(argv[3]) || !StrIsNumeric(argv[4]))
|
|
{
|
|
TechError("Syntax: render <layer> <style> <height> <thick>\n");
|
|
goto errorReturn;
|
|
}
|
|
height = (float)atof(argv[3]);
|
|
thick = (float)atof(argv[4]);
|
|
|
|
lcnt = 0;
|
|
for (i = 0; i < CIFCurStyle->cs_nLayers; i++)
|
|
{
|
|
layer = CIFCurStyle->cs_layers[i];
|
|
if (!strcmp(argv[1], layer->cl_name))
|
|
{
|
|
layer->cl_height = height;
|
|
layer->cl_thick = thick;
|
|
layer->cl_renderStyle = style;
|
|
lcnt++;
|
|
}
|
|
}
|
|
if (lcnt == 0)
|
|
{
|
|
TechError("Unknown layer name.\n");
|
|
goto errorReturn;
|
|
}
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* miscellaneous cif/calma-writing boolean options
|
|
*/
|
|
|
|
if (strcmp(argv[0], "options") == 0)
|
|
{
|
|
int i;
|
|
if (argc < 2) goto wrongNumArgs;
|
|
for (i = 1; i < argc; i++)
|
|
{
|
|
if (strcmp(argv[i], "calma-permissive-labels") == 0)
|
|
CIFCurStyle->cs_flags |= CWF_PERMISSIVE_LABELS;
|
|
else if (strcmp(argv[i], "set-minimum-grid") == 0)
|
|
CIFCurStyle->cs_flags |= CWF_MINIMUM_GRID;
|
|
else if (strcmp(argv[i], "grow-euclidean") == 0)
|
|
CIFCurStyle->cs_flags |= CWF_GROW_EUCLIDEAN;
|
|
else if (strcmp(argv[i], "see-no-vendor") == 0)
|
|
CIFCurStyle->cs_flags |= CWF_SEE_NO_VENDOR;
|
|
else if (strcmp(argv[i], "no-errors") == 0)
|
|
CIFCurStyle->cs_flags |= CWF_NO_ERRORS;
|
|
else if (strcmp(argv[i], "string-limit") == 0)
|
|
CIFCurStyle->cs_flags |= CWF_STRING_LIMIT;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/* Anything below here is a geometric operation, so we can
|
|
* do some set-up that is common to all the operations.
|
|
*/
|
|
|
|
if (cifCurLayer == NULL)
|
|
{
|
|
TechError("Must define layer before specifying operation.\n");
|
|
goto errorReturn;
|
|
}
|
|
newOp = (CIFOp *) mallocMagic(sizeof(CIFOp));
|
|
TTMaskZero(&newOp->co_paintMask);
|
|
TTMaskZero(&newOp->co_cifMask);
|
|
newOp->co_opcode = 0;
|
|
newOp->co_distance = 0;
|
|
newOp->co_next = NULL;
|
|
newOp->co_client = (ClientData)NULL;
|
|
|
|
if (strcmp(argv[0], "and") == 0)
|
|
newOp->co_opcode = CIFOP_AND;
|
|
else if (strcmp(argv[0], "and-not") == 0)
|
|
newOp->co_opcode = CIFOP_ANDNOT;
|
|
else if (strcmp(argv[0], "or") == 0)
|
|
newOp->co_opcode = CIFOP_OR;
|
|
else if (strcmp(argv[0], "grow") == 0)
|
|
newOp->co_opcode = CIFOP_GROW;
|
|
else if (strcmp(argv[0], "grow-min") == 0)
|
|
newOp->co_opcode = CIFOP_GROWMIN;
|
|
else if (strcmp(argv[0], "grow-grid") == 0)
|
|
newOp->co_opcode = CIFOP_GROW_G;
|
|
else if (strcmp(argv[0], "shrink") == 0)
|
|
newOp->co_opcode = CIFOP_SHRINK;
|
|
else if (strcmp(argv[0], "bloat-or") == 0)
|
|
newOp->co_opcode = CIFOP_BLOAT;
|
|
else if (strcmp(argv[0], "bloat-max") == 0)
|
|
newOp->co_opcode = CIFOP_BLOATMAX;
|
|
else if (strcmp(argv[0], "bloat-min") == 0)
|
|
newOp->co_opcode = CIFOP_BLOATMIN;
|
|
else if (strcmp(argv[0], "bloat-all") == 0)
|
|
newOp->co_opcode = CIFOP_BLOATALL;
|
|
else if (strcmp(argv[0], "squares") == 0)
|
|
newOp->co_opcode = CIFOP_SQUARES;
|
|
else if (strcmp(argv[0], "squares-grid") == 0)
|
|
newOp->co_opcode = CIFOP_SQUARES_G;
|
|
else if (strcmp(argv[0], "slots") == 0)
|
|
newOp->co_opcode = CIFOP_SLOTS;
|
|
else if (strcmp(argv[0], "bbox") == 0)
|
|
newOp->co_opcode = CIFOP_BBOX;
|
|
else if (strcmp(argv[0], "net") == 0)
|
|
newOp->co_opcode = CIFOP_NET;
|
|
else if (strcmp(argv[0], "maxrect") == 0)
|
|
newOp->co_opcode = CIFOP_MAXRECT;
|
|
else if (strcmp(argv[0], "boundary") == 0)
|
|
newOp->co_opcode = CIFOP_BOUNDARY;
|
|
else if (strcmp(argv[0], "mask-hints") == 0)
|
|
newOp->co_opcode = CIFOP_MASKHINTS;
|
|
else if (strcmp(argv[0], "close") == 0)
|
|
newOp->co_opcode = CIFOP_CLOSE;
|
|
else if (strcmp(argv[0], "orthogonal") == 0)
|
|
newOp->co_opcode = CIFOP_MANHATTAN;
|
|
else if (strcmp(argv[0], "bridge") == 0)
|
|
newOp->co_opcode = CIFOP_BRIDGE;
|
|
else if (strcmp(argv[0], "bridge-lim") == 0)
|
|
newOp->co_opcode = CIFOP_BRIDGELIM;
|
|
else if (strcmp(argv[0], "overlapping") == 0)
|
|
newOp->co_opcode = CIFOP_INTERACT;
|
|
else if (strcmp(argv[0], "nonoverlapping") == 0)
|
|
{
|
|
newOp->co_opcode = CIFOP_INTERACT;
|
|
newOp->co_client = (ClientData)CIFOP_INT_NOT;
|
|
}
|
|
else if (strcmp(argv[0], "interacting") == 0)
|
|
{
|
|
newOp->co_opcode = CIFOP_INTERACT;
|
|
newOp->co_client = (ClientData)CIFOP_INT_TOUCHING;
|
|
}
|
|
else if (strcmp(argv[0], "noninteracting") == 0)
|
|
{
|
|
newOp->co_opcode = CIFOP_INTERACT;
|
|
newOp->co_client = (ClientData)(CIFOP_INT_TOUCHING | CIFOP_INT_NOT);
|
|
}
|
|
else
|
|
{
|
|
TechError("Unknown statement \"%s\".\n", argv[0]);
|
|
goto errorReturn;
|
|
}
|
|
|
|
switch (newOp->co_opcode)
|
|
{
|
|
case CIFOP_AND:
|
|
case CIFOP_ANDNOT:
|
|
case CIFOP_OR:
|
|
case CIFOP_INTERACT:
|
|
if (argc != 2) goto wrongNumArgs;
|
|
cifParseLayers(argv[1], CIFCurStyle, &newOp->co_paintMask,
|
|
&newOp->co_cifMask,FALSE);
|
|
break;
|
|
|
|
case CIFOP_GROW:
|
|
case CIFOP_GROWMIN:
|
|
case CIFOP_GROW_G:
|
|
case CIFOP_SHRINK:
|
|
if (argc != 2) goto wrongNumArgs;
|
|
newOp->co_distance = atoi(argv[1]);
|
|
if (newOp->co_distance <= 0)
|
|
{
|
|
TechError("Grow/shrink distance must be greater than zero.\n");
|
|
goto errorReturn;
|
|
}
|
|
break;
|
|
|
|
case CIFOP_CLOSE:
|
|
/* "close" is like "grow" and "shrink" except that it can have
|
|
* no argument, in which case any closed shape of any size
|
|
* will be closed (useful for finding enclosed areas).
|
|
*/
|
|
if (argc == 1)
|
|
newOp->co_distance = 0;
|
|
else if (argc != 2)
|
|
goto wrongNumArgs;
|
|
else
|
|
{
|
|
newOp->co_distance = atoi(argv[1]);
|
|
if (newOp->co_distance <= 0)
|
|
{
|
|
TechError("Grow/shrink distance must be greater than zero.\n");
|
|
goto errorReturn;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CIFOP_BRIDGE:
|
|
if (argc != 3) goto wrongNumArgs;
|
|
newOp->co_distance = atoi(argv[1]);
|
|
if (newOp->co_distance <= 0)
|
|
{
|
|
TechError("Bridge distance must be greater than zero.\n");
|
|
goto errorReturn;
|
|
}
|
|
bridge = (BridgeData *)mallocMagic(sizeof(BridgeData));
|
|
bridge->br_width = atoi(argv[2]);
|
|
if (bridge->br_width <= 0)
|
|
{
|
|
TechError("Bridge width must be greater than zero.\n");
|
|
freeMagic(bridge);
|
|
goto errorReturn;
|
|
}
|
|
newOp->co_client = (ClientData)bridge;
|
|
break;
|
|
|
|
case CIFOP_BRIDGELIM:
|
|
if (argc != 4) goto wrongNumArgs;
|
|
newOp->co_distance = atoi(argv[1]);
|
|
if (newOp->co_distance <= 0)
|
|
{
|
|
TechError("Bridge distance must be greater than zero.\n");
|
|
goto errorReturn;
|
|
}
|
|
bridge = (BridgeData *)mallocMagic(sizeof(BridgeData));
|
|
bridge->br_width = atoi(argv[2]);
|
|
if (bridge->br_width <= 0)
|
|
{
|
|
TechError("Bridge width must be greater than zero.\n");
|
|
freeMagic(bridge);
|
|
goto errorReturn;
|
|
}
|
|
cifParseLayers(argv[3], CIFCurStyle, &newOp->co_paintMask, &newOp->co_cifMask,FALSE);
|
|
newOp->co_client = (ClientData)bridge;
|
|
break;
|
|
|
|
case CIFOP_BLOATALL:
|
|
if (argc != 3 && argc != 4) goto wrongNumArgs;
|
|
cifParseLayers(argv[1], CIFCurStyle, &newOp->co_paintMask,
|
|
&newOp->co_cifMask, FALSE);
|
|
bloats = (BloatData *)mallocMagic(sizeof(BloatData));
|
|
for (i = 0; i < TT_MAXTYPES; i++)
|
|
bloats->bl_distance[i] = 0;
|
|
newOp->co_client = (ClientData)bloats;
|
|
cifParseLayers(argv[2], CIFCurStyle, &mask, &cifMask, TRUE);
|
|
|
|
/* 5/25/2020: Lifting restriction that bloatLayers types */
|
|
/* cannot be CIF or temp layers. However, CIF/temp layers */
|
|
/* and magic database layers may not be mixed. */
|
|
|
|
if (!TTMaskIsZero(&mask) && !TTMaskIsZero(&cifMask))
|
|
TechError("Can't mix CIF and magic layers in bloat statement.\n");
|
|
|
|
if (argc == 4)
|
|
{
|
|
/* 12/23/2024: Allow an additional argument, which is a
|
|
* maximum halo distance to bloat (i.e., clip mask)
|
|
*/
|
|
newOp->co_distance = atoi(argv[3]);
|
|
if (newOp->co_distance <= 0)
|
|
{
|
|
TechError("Bloat distance must be greater than zero.\n");
|
|
goto errorReturn;
|
|
}
|
|
}
|
|
else
|
|
newOp->co_distance = 0;
|
|
|
|
/* 10/15/2019: Lifting restriction that the types that */
|
|
/* trigger the bloating must be in the same plane as the */
|
|
/* types that are bloated into. */
|
|
|
|
TTMaskZero(&bloatLayers);
|
|
if (!TTMaskIsZero(&mask))
|
|
{
|
|
TTMaskSetMask(&bloatLayers, &mask);
|
|
for (i = 0; i < TT_MAXTYPES; i++)
|
|
if (TTMaskHasType(&mask, i))
|
|
bloats->bl_distance[i] = 1;
|
|
|
|
goto bloatCheck;
|
|
}
|
|
else
|
|
{
|
|
TTMaskSetMask(&bloatLayers, &cifMask);
|
|
for (i = 0; i < TT_MAXTYPES; i++)
|
|
if (TTMaskHasType(&cifMask, i))
|
|
bloats->bl_distance[i] = 1;
|
|
|
|
bloats->bl_plane = -1; /* Indicates CIF types */
|
|
}
|
|
break;
|
|
|
|
case CIFOP_BLOAT:
|
|
case CIFOP_BLOATMIN:
|
|
case CIFOP_BLOATMAX:
|
|
if (argc < 4) goto wrongNumArgs;
|
|
cifParseLayers(argv[1], CIFCurStyle, &newOp->co_paintMask,
|
|
(TileTypeBitMask *)NULL, FALSE);
|
|
argc -= 2;
|
|
bloatArg = argv + 2;
|
|
bloatLayers = newOp->co_paintMask;
|
|
bloats = (BloatData *)mallocMagic(sizeof(BloatData));
|
|
for (i = 0; i < TT_MAXTYPES; i++)
|
|
bloats->bl_distance[i] = 0;
|
|
newOp->co_client = (ClientData)bloats;
|
|
|
|
while (argc > 0)
|
|
{
|
|
if (argc == 1) goto wrongNumArgs;
|
|
if (strcmp(*bloatArg, "*") == 0)
|
|
{
|
|
mask = DBAllTypeBits;
|
|
tempMask = DBZeroTypeBits;
|
|
}
|
|
else
|
|
{
|
|
cifParseLayers(*bloatArg, CIFCurStyle, &mask, &tempMask, TRUE);
|
|
TTMaskSetMask(&bloatLayers, &mask);
|
|
}
|
|
if (!TTMaskEqual(&tempMask, &DBZeroTypeBits))
|
|
TechError("Can't use templayers in bloat statement.\n");
|
|
|
|
distance = atoi(bloatArg[1]);
|
|
if ((distance < 0) && (newOp->co_opcode == CIFOP_BLOAT))
|
|
{
|
|
TechError("Bloat-or distances must not be negative.\n");
|
|
distance = 0;
|
|
}
|
|
for (i = 0; i < TT_MAXTYPES; i++)
|
|
if (TTMaskHasType(&mask, i))
|
|
bloats->bl_distance[i] = distance;
|
|
|
|
argc -= 2;
|
|
bloatArg += 2;
|
|
}
|
|
|
|
bloatCheck:
|
|
/* Don't do any bloating at boundaries between tiles of the
|
|
* types being bloated. Otherwise a bloat could pass right
|
|
* through a skinny tile and out the other side.
|
|
*/
|
|
for (i = 0; i < TT_MAXTYPES; i++)
|
|
if (TTMaskHasType(&newOp->co_paintMask, i))
|
|
bloats->bl_distance[i] = 0;
|
|
|
|
/* Make sure that all the layers specified in the statement
|
|
* fall in a single plane.
|
|
*/
|
|
|
|
for (i = 0; i < PL_MAXTYPES; i++)
|
|
{
|
|
tempMask = bloatLayers;
|
|
TTMaskAndMask(&tempMask, &DBPlaneTypes[i]);
|
|
if (TTMaskEqual(&tempMask, &bloatLayers)) {
|
|
bloats->bl_plane = i;
|
|
goto bloatDone;
|
|
}
|
|
}
|
|
TechError("Not all bloat layers fall in the same plane.\n");
|
|
bloats->bl_plane = 0; /* Prevents magic from segfaulting */
|
|
bloatDone: break;
|
|
|
|
case CIFOP_NET:
|
|
if (argc != 3) goto wrongNumArgs;
|
|
newOp->co_client = (ClientData)StrDup((char **)NULL, argv[1]);
|
|
cifParseLayers(argv[2], CIFCurStyle, &newOp->co_paintMask,
|
|
&newOp->co_cifMask, FALSE);
|
|
break;
|
|
|
|
case CIFOP_MASKHINTS:
|
|
if (argc != 2) goto wrongNumArgs;
|
|
newOp->co_client = (ClientData)StrDup((char **)NULL, argv[1]);
|
|
break;
|
|
|
|
case CIFOP_MAXRECT:
|
|
if (argc == 2)
|
|
{
|
|
if (!strncmp(argv[1], "ext", 3))
|
|
newOp->co_client = (ClientData)1;
|
|
else if (strncmp(argv[1], "int", 3))
|
|
TechError("Maxrect takes only one optional argument "
|
|
"\"external\" or \"internal\" (default).\n");
|
|
}
|
|
else if (argc != 1)
|
|
goto wrongNumArgs;
|
|
break;
|
|
|
|
case CIFOP_MANHATTAN:
|
|
if (argc == 2)
|
|
{
|
|
if (!strcmp(argv[1], "fill"))
|
|
newOp->co_client = (ClientData)1;
|
|
else if (strcmp(argv[1], "remove"))
|
|
TechError("Orthogonal takes only one optional argument "
|
|
"\"fill\" or \"remove\" (default).\n");
|
|
}
|
|
else if (argc != 1)
|
|
goto wrongNumArgs;
|
|
break;
|
|
|
|
case CIFOP_BBOX:
|
|
if (argc == 2)
|
|
{
|
|
if (!strcmp(argv[1], "top"))
|
|
newOp->co_client = (ClientData)1;
|
|
else
|
|
TechError("BBox takes only one optional argument \"top\".\n");
|
|
}
|
|
else if (argc != 1)
|
|
goto wrongNumArgs;
|
|
break;
|
|
|
|
case CIFOP_BOUNDARY:
|
|
/* CIFOP_BOUNDARY has no arguments */
|
|
if (argc != 1)
|
|
goto wrongNumArgs;
|
|
break;
|
|
|
|
case CIFOP_SQUARES_G:
|
|
case CIFOP_SQUARES:
|
|
|
|
squares = (SquaresData *)mallocMagic(sizeof(SquaresData));
|
|
newOp->co_client = (ClientData)squares;
|
|
|
|
if (argc == 2)
|
|
{
|
|
i = atoi(argv[1]);
|
|
squares->sq_border = atoi(argv[1]);
|
|
if ((i <= 0) || (i & 1))
|
|
{
|
|
TechError("Squares must have positive even sizes.\n");
|
|
goto errorReturn;
|
|
}
|
|
squares->sq_border = i/2;
|
|
squares->sq_size = i;
|
|
squares->sq_sep = i;
|
|
squares->sq_gridx = 1; /* set default grid */
|
|
squares->sq_gridy = 1; /* set default grid */
|
|
}
|
|
else if (argc == 4 || ((argc == 5 || argc == 6)
|
|
&& newOp->co_opcode==CIFOP_SQUARES_G))
|
|
{
|
|
squares->sq_border = atoi(argv[1]);
|
|
if (squares->sq_border < 0)
|
|
{
|
|
TechError("Square border must not be negative.\n");
|
|
goto errorReturn;
|
|
}
|
|
squares->sq_size = atoi(argv[2]);
|
|
if (squares->sq_size <= 0)
|
|
{
|
|
TechError("Squares must have positive sizes.\n");
|
|
goto errorReturn;
|
|
}
|
|
squares->sq_sep = atoi(argv[3]);
|
|
if (squares->sq_sep <= 0)
|
|
{
|
|
TechError("Square separation must be positive.\n");
|
|
goto errorReturn;
|
|
}
|
|
if (argc >= 5)
|
|
{
|
|
squares->sq_gridx = squares->sq_gridy = atoi(argv[4]);
|
|
if (squares->sq_gridx <= 0)
|
|
{
|
|
TechError("Square grid must be strictly positive.\n");
|
|
squares->sq_gridx = squares->sq_gridy = 1;
|
|
goto errorReturn;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
squares->sq_gridx = 1; /* set default grid */
|
|
squares->sq_gridy = 1; /* set default grid */
|
|
}
|
|
if (argc == 6)
|
|
{
|
|
squares->sq_gridy = atoi(argv[5]);
|
|
if (squares->sq_gridy <= 0)
|
|
{
|
|
TechError("Square y-grid must be strictly positive.\n");
|
|
squares->sq_gridy = 1;
|
|
goto errorReturn;
|
|
}
|
|
}
|
|
}
|
|
else goto wrongNumArgs;
|
|
|
|
/* Ensure that squares are never placed at less than the */
|
|
/* minimum allowed mask resolution. This may require that */
|
|
/* operation "squares" be changed to "squares-grid". */
|
|
|
|
if (squares->sq_gridx < CIFCurStyle->cs_gridLimit)
|
|
{
|
|
squares->sq_gridx = CIFCurStyle->cs_gridLimit;
|
|
newOp->co_opcode = CIFOP_SQUARES_G;
|
|
}
|
|
if (squares->sq_gridy < CIFCurStyle->cs_gridLimit)
|
|
{
|
|
squares->sq_gridy = CIFCurStyle->cs_gridLimit;
|
|
newOp->co_opcode = CIFOP_SQUARES_G;
|
|
}
|
|
break;
|
|
|
|
case CIFOP_SLOTS:
|
|
|
|
slots = (SlotsData *)mallocMagic(sizeof(SlotsData));
|
|
newOp->co_client = (ClientData)slots;
|
|
|
|
if (argc >= 4)
|
|
{
|
|
i = atoi(argv[1]);
|
|
slots->sl_sborder = i;
|
|
if (i < 0)
|
|
{
|
|
TechError("Slot border must be non-negative.\n");
|
|
goto errorReturn;
|
|
}
|
|
i = atoi(argv[2]);
|
|
slots->sl_ssize = i;
|
|
if (i <= 0)
|
|
{
|
|
TechError("Slot short-side size must be strictly positive.\n");
|
|
goto errorReturn;
|
|
}
|
|
i = atoi(argv[3]);
|
|
slots->sl_ssep = i;
|
|
if (i <= 0)
|
|
{
|
|
TechError("Slot separation must be strictly positive.\n");
|
|
goto errorReturn;
|
|
}
|
|
/* Initialize other values, in case they are not specified */
|
|
slots->sl_lborder = 0;
|
|
slots->sl_lsize = 0;
|
|
slots->sl_lsep = 0;
|
|
slots->sl_offset = 0;
|
|
slots->sl_start = 0;
|
|
}
|
|
if (argc >= 5)
|
|
{
|
|
i = atoi(argv[4]);
|
|
slots->sl_lborder = i;
|
|
if (i < 0)
|
|
{
|
|
TechError("Slot border must be non-negative.\n");
|
|
goto errorReturn;
|
|
}
|
|
}
|
|
if (argc >= 6)
|
|
{
|
|
i = atoi(argv[5]);
|
|
slots->sl_lsize = i;
|
|
if (i < 0)
|
|
{
|
|
TechError("Slot long-side size must be positive or zero.\n");
|
|
goto errorReturn;
|
|
}
|
|
i = atoi(argv[6]);
|
|
slots->sl_lsep = i;
|
|
if (i < 0)
|
|
{
|
|
TechError("Slot long-side separation must be non-negative.\n");
|
|
goto errorReturn;
|
|
}
|
|
else if (i == 0 && (slots->sl_lsize > 0))
|
|
{
|
|
TechError("Slot long-side separation must be strictly positive"
|
|
" when long-side size is nonzero\n");
|
|
goto errorReturn;
|
|
}
|
|
}
|
|
if (argc >= 8)
|
|
{
|
|
i = atoi(argv[7]);
|
|
slots->sl_offset = i;
|
|
if (i < 0)
|
|
{
|
|
TechError("Slot offset must be non-negative.\n");
|
|
goto errorReturn;
|
|
}
|
|
}
|
|
if (argc == 9)
|
|
{
|
|
i = atoi(argv[8]);
|
|
slots->sl_start = i;
|
|
if (i < 0)
|
|
{
|
|
TechError("Slot start must be non-negative.\n");
|
|
goto errorReturn;
|
|
}
|
|
}
|
|
if ((argc < 4) || (argc == 6) || (argc > 9))
|
|
goto wrongNumArgs;
|
|
break;
|
|
}
|
|
|
|
/* Link the new CIFOp into the list. */
|
|
|
|
if (cifCurOp == NULL)
|
|
cifCurLayer->cl_ops = newOp;
|
|
else
|
|
cifCurOp->co_next = newOp;
|
|
cifCurOp = newOp;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* cifCheckCalmaNum --
|
|
*
|
|
* This local procedure checks whether its argument is the ASCII
|
|
* representation of a positive integer between 0 and CALMA_LAYER_MAX.
|
|
*
|
|
* Results:
|
|
* TRUE if the argument string is valid as described above, FALSE if not.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
bool
|
|
cifCheckCalmaNum(
|
|
char *str)
|
|
{
|
|
int n = atoi(str);
|
|
|
|
if (n < 0 || n > CALMA_LAYER_MAX)
|
|
return (FALSE);
|
|
|
|
while (*str) {
|
|
char ch = *str++;
|
|
if (ch < '0' || ch > '9')
|
|
return (FALSE);
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* cifComputeRadii --
|
|
*
|
|
* This local procedure computes and fills in the grow and
|
|
* shrink distances for a layer. Before calling this procedure,
|
|
* the distances must have been computed for all temporary
|
|
* layers used by this layer.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Modifies cl_growDist and cl_shrinkDist in layer.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
cifComputeRadii(
|
|
CIFLayer *layer, /* Layer for which to compute distances. */
|
|
CIFStyle *des) /* CIF style (used to find temp layer
|
|
* distances.
|
|
*/
|
|
{
|
|
int i, grow, shrink, curGrow, curShrink;
|
|
CIFOp *op;
|
|
BloatData *bloats;
|
|
|
|
grow = shrink = 0;
|
|
|
|
for (op = layer->cl_ops; op != NULL; op = op->co_next)
|
|
{
|
|
/* BBOX, NET, and MASKHINTS operators should never be used */
|
|
/* hierarchically so ignore any grow/shrink operators that */
|
|
/* come after them. */
|
|
|
|
if (op->co_opcode == CIFOP_BBOX || op->co_opcode == CIFOP_NET ||
|
|
op->co_opcode == CIFOP_MASKHINTS)
|
|
break;
|
|
|
|
/* If CIF layers are used, switch to the max of current
|
|
* distances and those of the layers used.
|
|
*/
|
|
|
|
if (!TTMaskEqual(&op->co_cifMask, &DBZeroTypeBits))
|
|
{
|
|
for (i=0; i < des->cs_nLayers; i++)
|
|
{
|
|
if (TTMaskHasType(&op->co_cifMask, i))
|
|
{
|
|
if (des->cs_layers[i]->cl_growDist > grow)
|
|
grow = des->cs_layers[i]->cl_growDist;
|
|
if (des->cs_layers[i]->cl_shrinkDist > shrink)
|
|
shrink = des->cs_layers[i]->cl_shrinkDist;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Add in grows and shrinks at this step. */
|
|
|
|
switch (op->co_opcode)
|
|
{
|
|
case CIFOP_AND:
|
|
case CIFOP_ANDNOT:
|
|
case CIFOP_OR:
|
|
case CIFOP_INTERACT:
|
|
case CIFOP_MASKHINTS:
|
|
break;
|
|
|
|
case CIFOP_GROW:
|
|
case CIFOP_GROWMIN:
|
|
case CIFOP_GROW_G:
|
|
grow += op->co_distance;
|
|
break;
|
|
|
|
case CIFOP_SHRINK:
|
|
shrink += op->co_distance;
|
|
break;
|
|
|
|
/* For bloats use the largest distances (negative values are
|
|
* for shrinks).
|
|
*/
|
|
|
|
case CIFOP_BLOAT:
|
|
curGrow = curShrink = 0;
|
|
bloats = (BloatData *)op->co_client;
|
|
for (i = 0; i < TT_MAXTYPES; i++)
|
|
{
|
|
if (bloats->bl_distance[i] > curGrow)
|
|
curGrow = bloats->bl_distance[i];
|
|
else if ((-bloats->bl_distance[i]) > curShrink)
|
|
curShrink = -bloats->bl_distance[i];
|
|
}
|
|
grow += curGrow;
|
|
shrink += curShrink;
|
|
break;
|
|
|
|
case CIFOP_BRIDGE: break;
|
|
case CIFOP_BRIDGELIM: break;
|
|
case CIFOP_SQUARES: break;
|
|
case CIFOP_SQUARES_G: break;
|
|
|
|
}
|
|
}
|
|
|
|
layer->cl_growDist = grow;
|
|
layer->cl_shrinkDist = shrink;
|
|
|
|
/* TxPrintf("Radii for %s: grow %d, shrink %d.\n", layer->cl_name,
|
|
grow, shrink);
|
|
*/
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* cifComputeHalo --
|
|
*
|
|
* Compute grow and shrink distances for each layer, and remember the
|
|
* largest. Convert from CIF/GDS to Magic internal dimensions.
|
|
*
|
|
* Results: None.
|
|
* Side effects: Sets cs_radius value in the current cifoutput style.
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
cifComputeHalo(
|
|
CIFStyle *style)
|
|
{
|
|
int maxGrow, maxShrink, i;
|
|
|
|
maxGrow = maxShrink = 0;
|
|
for (i = 0; i < style->cs_nLayers; i++)
|
|
{
|
|
cifComputeRadii(style->cs_layers[i], style);
|
|
if (style->cs_layers[i]->cl_growDist > maxGrow)
|
|
maxGrow = style->cs_layers[i]->cl_growDist;
|
|
if (style->cs_layers[i]->cl_shrinkDist > maxShrink)
|
|
maxShrink = style->cs_layers[i]->cl_shrinkDist;
|
|
}
|
|
if (maxGrow > maxShrink)
|
|
style->cs_radius = 2*maxGrow;
|
|
else style->cs_radius = 2*maxShrink;
|
|
style->cs_radius /= style->cs_scaleFactor;
|
|
style->cs_radius++;
|
|
|
|
/* TxPrintf("Radius for %s CIF is %d.\n",
|
|
* style->cs_name, style->cs_radius);
|
|
*/
|
|
}
|
|
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* CIFTechFinal --
|
|
*
|
|
* This procedure is invoked after all the lines of a technology
|
|
* file have been read. It checks to make sure that the
|
|
* section ended at a consistent point, and computes the interaction
|
|
* distances for hierarchical CIF processing.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Error messages are output if there's incomplete stuff left.
|
|
* Interaction distances get computed for each CIF style
|
|
* in two steps. First, for each layer the total grow and
|
|
* shrink distances are computed. These are the maximum distances
|
|
* that edges may move because of grows and shrinks in creating
|
|
* the layer. Second, the radius for the style is computed.
|
|
* The radius is used in two ways: first to determine how far
|
|
* apart two subcells may be and still interact during CIF
|
|
* generation; and second, to see how much material to yank in
|
|
* order to find all additional CIF resulting from interactions.
|
|
* Right now, a conservative approach is used: use the greater
|
|
* of twice the largest grow distance or twice the largest shrink
|
|
* distance for both. Twice the grow distance must be considered
|
|
* because two pieces of material may each grow towards the other
|
|
* and interact in the middle. Twice the largest shrink distance
|
|
* is needed because subcells considered individually may each
|
|
* shrink away from a boundary where they touch; the parent must
|
|
* fill in the gap. To do this, it must include 2S additional
|
|
* material: S is the size of the gap that must be filled, but
|
|
* its outside edge will shrink in by S, so we must start with
|
|
* 2S material to have S left after the shrink. Finally, one extra
|
|
* unit gets added because two pieces of material one radius apart
|
|
* can interact: to find all this material we must look one unit
|
|
* farther out for anything overlapping (the search routines only
|
|
* look for overlapping material and ignore abutting material).
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
CIFTechFinal(void)
|
|
{
|
|
CIFStyle *style = CIFCurStyle;
|
|
CIFOp *op;
|
|
int i, minReduce;
|
|
|
|
/* Allow the case where there's no CIF at all. This is indicated
|
|
* by a NULL CIFCurStyle.
|
|
*/
|
|
|
|
if (!style) return;
|
|
|
|
if ((cifCurLayer != NULL) && (cifCurOp == NULL) && !cifGotLabels)
|
|
{
|
|
TechError("Layer \"%s\" contains no material.\n",
|
|
cifCurLayer->cl_name);
|
|
}
|
|
cifCurLayer = NULL;
|
|
|
|
/*
|
|
* If cs_expander > 1, then all CIF op values must be scaled accordingly.
|
|
* This routine loops through all CIF output styles.
|
|
*/
|
|
|
|
CIFTechOutputScale(1, 1);
|
|
|
|
if (style->cs_scaleFactor <= 0)
|
|
{
|
|
TechError("No valid scale factor was given for %s CIF.\n",
|
|
style->cs_name);
|
|
style->cs_scaleFactor = 1;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Make sure that all contact layers include stacked contact types
|
|
*/
|
|
for (i = 0; i < style->cs_nLayers; i++)
|
|
for (op = style->cs_layers[i]->cl_ops; op != NULL; op = op->co_next)
|
|
{
|
|
TileType d, s;
|
|
TileTypeBitMask *rMask;
|
|
|
|
for (d = TT_TECHDEPBASE; d < DBNumUserLayers; d++)
|
|
if (TTMaskHasType(&op->co_paintMask, d) && DBIsContact(d))
|
|
for (s = DBNumUserLayers; s < DBNumTypes; s++)
|
|
{
|
|
rMask = DBResidueMask(s);
|
|
if (TTMaskHasType(rMask, d))
|
|
TTMaskSetType(&op->co_paintMask, s);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Find the largest reducer value which divides into all of the CIF values.
|
|
*/
|
|
|
|
minReduce = style->cs_scaleFactor;
|
|
for (i = 0; i < style->cs_nLayers; i++)
|
|
{
|
|
for (op = style->cs_layers[i]->cl_ops; op != NULL; op = op->co_next)
|
|
{
|
|
int j, c, bvalue;
|
|
if (op->co_distance > 0)
|
|
{
|
|
c = FindGCF(style->cs_scaleFactor, op->co_distance);
|
|
if (c < minReduce) minReduce = c;
|
|
}
|
|
if (op->co_client)
|
|
{
|
|
BloatData *bloats;
|
|
BridgeData *bridge;
|
|
SquaresData *squares;
|
|
SlotsData *slots;
|
|
if (op->co_opcode == CIFOP_SLOTS)
|
|
{
|
|
slots = (SlotsData *)op->co_client;
|
|
|
|
for (j = 0; j < 8; j++)
|
|
{
|
|
switch (j) {
|
|
case 0: bvalue = slots->sl_sborder; break;
|
|
case 1: bvalue = slots->sl_ssize; break;
|
|
case 2: bvalue = slots->sl_ssep; break;
|
|
case 3: bvalue = slots->sl_lborder; break;
|
|
case 4: bvalue = slots->sl_lsize; break;
|
|
case 5: bvalue = slots->sl_lsep; break;
|
|
case 6: bvalue = slots->sl_offset; break;
|
|
case 7: bvalue = slots->sl_start; break;
|
|
}
|
|
if (bvalue != 0)
|
|
{
|
|
if ((j == 1) || (j == 2) || (j == 4) || (j == 5))
|
|
{
|
|
if (bvalue & 0x1)
|
|
TxError("Internal error: slot size/sep %d"
|
|
" cannot be halved.\n", bvalue);
|
|
bvalue >>= 1;
|
|
}
|
|
c = FindGCF(style->cs_scaleFactor, bvalue);
|
|
if (c < minReduce) minReduce = c;
|
|
}
|
|
}
|
|
}
|
|
if (op->co_opcode == CIFOP_SQUARES)
|
|
{
|
|
squares = (SquaresData *)op->co_client;
|
|
|
|
for (j = 0; j < 3; j++)
|
|
{
|
|
switch (j) {
|
|
case 0: bvalue = squares->sq_border; break;
|
|
case 1: bvalue = squares->sq_size; break;
|
|
case 2: bvalue = squares->sq_sep; break;
|
|
}
|
|
if (bvalue != 0)
|
|
{
|
|
if ((j == 1) || (j == 2))
|
|
{
|
|
if (bvalue & 0x1)
|
|
TxError("Internal error: contact size/sep %d"
|
|
" cannot be halved.\n", bvalue);
|
|
bvalue >>= 1;
|
|
}
|
|
c = FindGCF(style->cs_scaleFactor, bvalue);
|
|
if (c < minReduce) minReduce = c;
|
|
}
|
|
}
|
|
}
|
|
else if (op->co_opcode == CIFOP_SQUARES_G)
|
|
{
|
|
squares = (SquaresData *)op->co_client;
|
|
for (j = 0; j < TT_MAXTYPES; j++)
|
|
{
|
|
switch (j) {
|
|
case 0: bvalue = squares->sq_border; break;
|
|
case 1: bvalue = squares->sq_size; break;
|
|
case 2: bvalue = squares->sq_sep; break;
|
|
case 3: bvalue = squares->sq_gridx; break;
|
|
case 4: bvalue = squares->sq_gridy; break;
|
|
}
|
|
if (bvalue != 0)
|
|
{
|
|
c = FindGCF(style->cs_scaleFactor, bvalue);
|
|
if (c < minReduce) minReduce = c;
|
|
}
|
|
}
|
|
}
|
|
/* Presence of op->co_opcode in CIFOP_OR indicates a copy */
|
|
/* of the SquaresData pointer from a following operator. */
|
|
/* CIFOP_BBOX and CIFOP_MAXRECT uses the co_client field */
|
|
/* as a flag field, while CIFOP_NET and CIFOP_MASKHINTS */
|
|
/* uses it for a string. */
|
|
else
|
|
{
|
|
switch (op->co_opcode)
|
|
{
|
|
case CIFOP_OR:
|
|
case CIFOP_BBOX:
|
|
case CIFOP_MASKHINTS:
|
|
case CIFOP_BOUNDARY:
|
|
case CIFOP_MAXRECT:
|
|
case CIFOP_MANHATTAN:
|
|
case CIFOP_NET:
|
|
break;
|
|
case CIFOP_BRIDGELIM:
|
|
case CIFOP_BRIDGE:
|
|
bridge = (BridgeData *)op->co_client;
|
|
c = FindGCF(style->cs_scaleFactor,
|
|
bridge->br_width);
|
|
if (c < minReduce) minReduce = c;
|
|
break;
|
|
default:
|
|
bloats = (BloatData *)op->co_client;
|
|
for (j = 0; j < TT_MAXTYPES; j++)
|
|
{
|
|
if (bloats->bl_distance[j] != 0)
|
|
{
|
|
c = FindGCF(style->cs_scaleFactor,
|
|
bloats->bl_distance[j]);
|
|
if (c < minReduce) minReduce = c;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (minReduce == 1) break;
|
|
}
|
|
}
|
|
style->cs_reducer = minReduce;
|
|
|
|
/* Debug info --- Tim, 1/3/02 */
|
|
/* TxPrintf("Output style %s: scaleFactor=%d, reducer=%d, expander=%d.\n",
|
|
style->cs_name, style->cs_scaleFactor,
|
|
style->cs_reducer, style->cs_expander); */
|
|
|
|
/* Compute grow and shrink distances for each layer,
|
|
* and remember the largest.
|
|
*/
|
|
cifComputeHalo(style);
|
|
|
|
/* Go through the layers to see which ones depend on which
|
|
* other ones. The purpose of this is so that we don't
|
|
* have to yank unnecessary layers in processing subcell
|
|
* interactions. Also find out which layers involve only
|
|
* a single OR operation, and remember the others specially
|
|
* (they'll require fancy CIF geometry processing).
|
|
*/
|
|
|
|
for (i = style->cs_nLayers-1; i >= 0; i -= 1)
|
|
{
|
|
TileTypeBitMask ourDepend, ourYank;
|
|
bool needThisLayer;
|
|
int j;
|
|
|
|
ourDepend = DBZeroTypeBits;
|
|
ourYank = DBZeroTypeBits;
|
|
|
|
/* This layer must be computed hierarchically if it is needed
|
|
* by some other layer that is computed hierarchically, or if
|
|
* it includes operations that require hierarchical processing.
|
|
*/
|
|
|
|
needThisLayer = TTMaskHasType(&style->cs_hierLayers, i);
|
|
|
|
for (op = style->cs_layers[i]->cl_ops; op != NULL;
|
|
op = op->co_next)
|
|
{
|
|
BloatData *bloats;
|
|
|
|
TTMaskSetMask(&ourDepend, &op->co_cifMask);
|
|
TTMaskSetMask(&ourYank, &op->co_paintMask);
|
|
switch (op->co_opcode)
|
|
{
|
|
case CIFOP_BLOAT:
|
|
case CIFOP_BLOATMAX:
|
|
case CIFOP_BLOATMIN:
|
|
bloats = (BloatData *)op->co_client;
|
|
for (j = 0; j < TT_MAXTYPES; j++)
|
|
if (bloats->bl_distance[j] != bloats->bl_distance[TT_SPACE])
|
|
TTMaskSetType(&ourYank, j);
|
|
needThisLayer = TRUE;
|
|
break;
|
|
|
|
case CIFOP_BLOATALL:
|
|
bloats = (BloatData *)op->co_client;
|
|
for (j = 0; j < TT_MAXTYPES; j++)
|
|
{
|
|
if (bloats->bl_distance[j] != 0)
|
|
{
|
|
if (bloats->bl_plane < 0)
|
|
TTMaskSetType(&ourDepend, j);
|
|
else
|
|
TTMaskSetType(&ourYank, j);
|
|
}
|
|
}
|
|
needThisLayer = TRUE;
|
|
break;
|
|
|
|
case CIFOP_AND:
|
|
case CIFOP_ANDNOT:
|
|
case CIFOP_INTERACT:
|
|
case CIFOP_SHRINK:
|
|
case CIFOP_CLOSE:
|
|
case CIFOP_BRIDGELIM:
|
|
case CIFOP_BRIDGE:
|
|
needThisLayer = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (needThisLayer)
|
|
{
|
|
TTMaskSetMask(&style->cs_yankLayers, &ourYank);
|
|
TTMaskSetType(&style->cs_hierLayers, i);
|
|
TTMaskSetMask(&style->cs_hierLayers, &ourDepend);
|
|
}
|
|
}
|
|
|
|
/* Added by Tim, 5/16/19 */
|
|
/* Layers that depend on hierarchically generated layers */
|
|
/* (i.e., templayers) must themselves be hierarchically */
|
|
/* processed. */
|
|
|
|
for (i = 0; i < style->cs_nLayers; i++)
|
|
{
|
|
TileTypeBitMask ourDepend, mmask;
|
|
|
|
ourDepend = DBZeroTypeBits;
|
|
for (op = style->cs_layers[i]->cl_ops; op != NULL; op = op->co_next)
|
|
TTMaskSetMask(&ourDepend, &op->co_cifMask);
|
|
|
|
TTMaskAndMask3(&mmask, &ourDepend, &style->cs_hierLayers);
|
|
if (!TTMaskIsZero(&mmask))
|
|
TTMaskSetType(&style->cs_hierLayers, i);
|
|
}
|
|
|
|
/* Added by Tim, 6/17/20: Do not set dependencies of BOUNDARY */
|
|
/* or BBOX because these are unable to be represented in a */
|
|
/* hierarchy without producing conflicting parent/child mask */
|
|
/* geometry. Generally, BOUNDARY and BBOX operations are */
|
|
/* intended to be restricted to the child cell and should not */
|
|
/* interact hierarchically. Possibly it would be useful to */
|
|
/* provide some method to preserve the bounding box information */
|
|
/* when flattening? */
|
|
|
|
for (i = 0; i < style->cs_nLayers; i++)
|
|
{
|
|
for (op = style->cs_layers[i]->cl_ops; op != NULL; op = op->co_next)
|
|
{
|
|
if (op->co_opcode == CIFOP_BOUNDARY || op->co_opcode == CIFOP_BBOX)
|
|
{
|
|
TTMaskClearType(&style->cs_hierLayers, i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Added by Tim, 10/18/04 */
|
|
|
|
/* Go through the layer operators looking for those that */
|
|
/* contain only OR operators followed by a single SQUARES */
|
|
/* operator. If found, set the clientdata record of the OR */
|
|
/* operator to be a copy of that for the SQUARES operator. */
|
|
/* This is used by the GDS generator when the "gds contact */
|
|
/* on" option is enabled (CalmaContactArrays is TRUE) to */
|
|
/* translate contact areas into contact subcell arrays. */
|
|
|
|
for (i = 0; i < style->cs_nLayers; i++)
|
|
{
|
|
ClientData clientdata;
|
|
for (op = style->cs_layers[i]->cl_ops; (op != NULL) &&
|
|
(op->co_opcode == CIFOP_OR) &&
|
|
(TTMaskIsZero(&op->co_cifMask)); op = op->co_next);
|
|
if (op && ((op->co_opcode == CIFOP_SQUARES) ||
|
|
(op->co_opcode == CIFOP_SQUARES_G)) && (op->co_next == NULL))
|
|
{
|
|
clientdata = op->co_client;
|
|
for (op = style->cs_layers[i]->cl_ops; op->co_opcode == CIFOP_OR;
|
|
op = op->co_next)
|
|
{
|
|
/* Copy the client record from the CIFOP_SQUARES operator
|
|
* into the CIFOP_OR operator.
|
|
*/
|
|
op->co_client = clientdata;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Uncomment this code to print out information about which
|
|
* layers have to be processed hierarchically.
|
|
|
|
for (i = 0; i < DBNumUserLayers; i++)
|
|
{
|
|
if (TTMaskHasType(&style->cs_yankLayers, i))
|
|
TxPrintf("Will have to yank %s in style %s.\n",
|
|
DBTypeLongName(i), style->cs_name);
|
|
}
|
|
for (i = 0; i < CIFCurStyle->cs_nLayers; i++)
|
|
{
|
|
if (TTMaskHasType(&style->cs_hierLayers, i))
|
|
TxPrintf("Layer %s must be processed hierarchically.\n",
|
|
style->cs_layers[i]->cl_name);
|
|
}
|
|
|
|
*/
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* CIFLoadStyle --
|
|
*
|
|
* Re-read the technology file to load the specified technology cif output
|
|
* style into structure CIFCurStyle. This is much more memory-efficient than
|
|
* keeping a separate structure for each cif output style. It incurs a complete
|
|
* reading of the tech file on startup and every time the cif output 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 cif output
|
|
* style is changed by a call to "cif ostyle".
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
CIFLoadStyle(
|
|
char *stylename)
|
|
{
|
|
SectionID invcif;
|
|
|
|
if (CIFCurStyle && (CIFCurStyle->cs_name == stylename)) return;
|
|
|
|
cifTechNewStyle();
|
|
CIFCurStyle->cs_name = stylename;
|
|
|
|
invcif = TechSectionGetMask("cifoutput", NULL);
|
|
TechLoad(NULL, invcif);
|
|
|
|
/* CIFTechFinal(); */ /* handled by TechLoad() */
|
|
CIFTechOutputScale(DBLambda[0], DBLambda[1]);
|
|
|
|
/* If the DRC section makes reference to CIF layers, then */
|
|
/* we need to re-read the DRC section as well. */
|
|
|
|
if ((DRCForceReload == TRUE) && (DRCCurStyle != NULL))
|
|
DRCReloadCurStyle();
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* CIFGetContactSize --
|
|
*
|
|
* Return the smallest allowable contact size in lambda units corresponding
|
|
* to the "squares" function operating on the indicated type. This value
|
|
* is computed as the (cut size) + 2 * (cut border).
|
|
*
|
|
* 9/12/2013: Added "squares-grid" and "slots" to the functions understood
|
|
* by the routine. "squares-grid" behaves the same as "squares". "slots"
|
|
* can be used for contact cuts with differing metal overlap on different
|
|
* sides. Normally this would define a square slot; this routine finds
|
|
* the minimum cut size for the slot.
|
|
*
|
|
* Results:
|
|
* Contact minimum dimension, in CIF/GDS units
|
|
*
|
|
* Side effects:
|
|
* If any of edge, border, or spacing is non-NULL, the appropriate
|
|
* cut dimension is filled in.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
CIFGetContactSize(
|
|
TileType type,
|
|
int *edge,
|
|
int *spacing,
|
|
int *border)
|
|
{
|
|
CIFStyle *style = CIFCurStyle;
|
|
CIFOp *op, *sop;
|
|
int i;
|
|
SquaresData *squares;
|
|
SlotsData *slots;
|
|
|
|
if (style == NULL)
|
|
{
|
|
edge = spacing = border = 0;
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i < style->cs_nLayers; i++)
|
|
{
|
|
for (op = style->cs_layers[i]->cl_ops; (op != NULL) &&
|
|
(op->co_opcode == CIFOP_OR) &&
|
|
(TTMaskIsZero(&op->co_cifMask)); op = op->co_next)
|
|
if (TTMaskHasType(&op->co_paintMask, type))
|
|
for (sop = op->co_next; sop != NULL; sop = sop->co_next)
|
|
{
|
|
if (sop->co_opcode == CIFOP_SQUARES ||
|
|
sop->co_opcode == CIFOP_SQUARES_G)
|
|
{
|
|
squares = (SquaresData *)sop->co_client;
|
|
if (edge != NULL) *edge = squares->sq_size;
|
|
if (border != NULL) *border = squares->sq_border;
|
|
if (spacing != NULL) *spacing = squares->sq_sep;
|
|
return (squares->sq_size + (squares->sq_border << 1));
|
|
}
|
|
else if (sop->co_opcode == CIFOP_SLOTS)
|
|
{
|
|
slots = (SlotsData *)sop->co_client;
|
|
if (edge != NULL) *edge = slots->sl_ssize;
|
|
if (border != NULL) *border = slots->sl_sborder;
|
|
if (spacing != NULL) *spacing = slots->sl_ssep;
|
|
return (slots->sl_ssize + (slots->sl_sborder << 1));
|
|
}
|
|
|
|
/* Anything other than an OR function will break */
|
|
/* the relationship between magic layers and cuts. */
|
|
/* NOTE: Making an exception for AND_NOT, which is */
|
|
/* used to distinguish between small and large via */
|
|
/* areas. */
|
|
else if ((sop->co_opcode != CIFOP_OR) &&
|
|
(sop->co_opcode != CIFOP_ANDNOT))
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* CIFTechOutputScale(n, d) --
|
|
*
|
|
* Scale all CIF output scale factors to make them equivalent
|
|
* to reducing the magic internal unit spacing by a factor of n/d.
|
|
* It is important not to reduce the scaleFactor to zero! So, we
|
|
* multiply scaleFactor by n and the expander by d, then reduce
|
|
* the fraction using the FindGCF routine (utils/fraction.c).
|
|
*
|
|
* Because all CIF operation distances (grow, shrink, bloat, squares)
|
|
* are in units of centimicrons multiplied by the expander (e.g.,
|
|
* expander = 10 means units are in nanometers), we need to rescale them
|
|
* by the expander. To optimize CIF output, the fraction scale/expander
|
|
* is reduced to minimize expander; that is, to make the numbers in the
|
|
* CIF output as small as possible while making sure all output can be
|
|
* represented by integers.
|
|
*
|
|
* Note that because contacts may be placed at 1/2 grid spacing to
|
|
* center them, the size and spacing of contacts as given in the
|
|
* "squares" function must *always* be an even number. To ensure this,
|
|
* we check for odd numbers in these positions. If there are any, and
|
|
* "d" is also an odd number, then we multiply both "n" and "d" by 2.
|
|
*
|
|
* (Added 2/23/05)---The above is not sufficient! If the contact size
|
|
* is an odd number, then centering may not be possible even if all
|
|
* the contact parameters are even numbers. e.g., for size=22 in a 0.18
|
|
* process (scalefactor=9), a 5x5 lambda contact is 45x45 centimicrons.
|
|
* 45 - 22 = 23, so border is 11.5 centimicrons. Therefore, when
|
|
* the scalefactor is even, the contact size must be even, but when the
|
|
* scalefactor is odd, then we have to multiply both "n" and "d" by 2
|
|
* regardless, since contact areas may be either odd or even (compare
|
|
* the above example to one where the contact is drawn 6x6 lambda).
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
CIFTechOutputScale(
|
|
int n,
|
|
int d)
|
|
{
|
|
int i, j, lgcf, lexpand;
|
|
CIFStyle *ostyle = CIFCurStyle;
|
|
CIFLayer *cl;
|
|
CIFOp *op;
|
|
SquaresData *squares;
|
|
SlotsData *slots;
|
|
BloatData *bloats;
|
|
BridgeData *bridge;
|
|
bool has_odd_space = FALSE;
|
|
|
|
if (ostyle == NULL) return;
|
|
|
|
/* For contact half-grid centering, check for odd numbers. . . */
|
|
|
|
if (ostyle->cs_scaleFactor & 0x1)
|
|
{
|
|
n *= 2;
|
|
d *= 2;
|
|
has_odd_space = TRUE;
|
|
}
|
|
else if (d & 0x1)
|
|
{
|
|
has_odd_space = FALSE;
|
|
for (i = 0; i < ostyle->cs_nLayers; i++)
|
|
{
|
|
cl = ostyle->cs_layers[i];
|
|
for (op = cl->cl_ops; op != NULL; op = op->co_next)
|
|
{
|
|
if (op->co_opcode == CIFOP_SQUARES)
|
|
{
|
|
squares = (SquaresData *)op->co_client;
|
|
if ((squares->sq_size & 0x01) || (squares->sq_sep & 0x01))
|
|
{
|
|
has_odd_space = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
else if (op->co_opcode == CIFOP_SLOTS)
|
|
{
|
|
slots = (SlotsData *)op->co_client;
|
|
if ((slots->sl_lsize & 0x01) || (slots->sl_lsep & 0x01)
|
|
|| (slots->sl_ssize & 0x01) || (slots->sl_ssep & 0x01))
|
|
{
|
|
has_odd_space = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (has_odd_space)
|
|
{
|
|
n *= 2;
|
|
d *= 2;
|
|
has_odd_space = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* fprintf(stderr, "CIFTechOutputScale(%d, %d)\n", n, d); */
|
|
|
|
ostyle->cs_scaleFactor *= n;
|
|
ostyle->cs_expander *= d;
|
|
|
|
/* fprintf(stderr, "CIFStyle %s:\n", ostyle->cs_name); */
|
|
|
|
lexpand = ostyle->cs_expander;
|
|
for (i = 0; i < ostyle->cs_nLayers; i++)
|
|
{
|
|
cl = ostyle->cs_layers[i];
|
|
for (op = cl->cl_ops; op != NULL; op = op->co_next)
|
|
{
|
|
if (op->co_distance)
|
|
{
|
|
op->co_distance *= d;
|
|
lgcf = FindGCF(abs(op->co_distance), ostyle->cs_expander);
|
|
lexpand = FindGCF(lexpand, lgcf);
|
|
}
|
|
if (op->co_client)
|
|
{
|
|
int bvalue, *bptr;
|
|
if (op->co_opcode == CIFOP_SQUARES)
|
|
{
|
|
squares = (SquaresData *)op->co_client;
|
|
for (j = 0; j < 3; j++)
|
|
{
|
|
switch (j) {
|
|
case 0: bptr = &squares->sq_border; break;
|
|
case 1: bptr = &squares->sq_size; break;
|
|
case 2: bptr = &squares->sq_sep; break;
|
|
}
|
|
if (*bptr != 0)
|
|
{
|
|
(*bptr) *= d;
|
|
bvalue = abs(*bptr);
|
|
if ((j == 1) || (j == 2)) bvalue >>= 1; /* half-grid */
|
|
if (has_odd_space) bvalue >>= 1; /* force half-grid */
|
|
lgcf = FindGCF(bvalue, ostyle->cs_expander);
|
|
lexpand = FindGCF(lexpand, lgcf);
|
|
}
|
|
}
|
|
}
|
|
else if (op->co_opcode == CIFOP_SLOTS)
|
|
{
|
|
slots = (SlotsData *)op->co_client;
|
|
for (j = 0; j < 8; j++)
|
|
{
|
|
switch (j) {
|
|
case 0: bptr = &slots->sl_sborder; break;
|
|
case 1: bptr = &slots->sl_ssize; break;
|
|
case 2: bptr = &slots->sl_ssep; break;
|
|
case 3: bptr = &slots->sl_lborder; break;
|
|
case 4: bptr = &slots->sl_lsize; break;
|
|
case 5: bptr = &slots->sl_lsep; break;
|
|
case 6: bptr = &slots->sl_offset; break;
|
|
case 7: bptr = &slots->sl_start; break;
|
|
}
|
|
if (*bptr != 0)
|
|
{
|
|
(*bptr) *= d;
|
|
bvalue = abs(*bptr);
|
|
if ((j == 1) || (j == 2) || (j == 4) || (j == 5))
|
|
bvalue >>= 1; /* half-grid */
|
|
if (has_odd_space) bvalue >>= 1; /* force half-grid */
|
|
lgcf = FindGCF(bvalue, ostyle->cs_expander);
|
|
lexpand = FindGCF(lexpand, lgcf);
|
|
}
|
|
}
|
|
}
|
|
else if (op->co_opcode == CIFOP_SQUARES_G)
|
|
{
|
|
squares = (SquaresData *)op->co_client;
|
|
for (j = 0; j < 5; j++)
|
|
{
|
|
switch (j) {
|
|
case 0: bptr = &squares->sq_border; break;
|
|
case 1: bptr = &squares->sq_size; break;
|
|
case 2: bptr = &squares->sq_sep; break;
|
|
case 3: bptr = &squares->sq_gridx; break;
|
|
case 4: bptr = &squares->sq_gridy; break;
|
|
}
|
|
if (*bptr != 0)
|
|
{
|
|
(*bptr) *= d;
|
|
lgcf = FindGCF(abs(*bptr), ostyle->cs_expander);
|
|
lexpand = FindGCF(lexpand, lgcf);
|
|
}
|
|
}
|
|
}
|
|
/* Presence of op->co_opcode in CIFOP_OR indicates a copy */
|
|
/* of the SquaresData pointer from a following operator */
|
|
/* CIFOP_BBOX uses the co_client field as a flag field. */
|
|
else
|
|
{
|
|
switch (op->co_opcode)
|
|
{
|
|
case CIFOP_OR:
|
|
case CIFOP_BBOX:
|
|
case CIFOP_BOUNDARY:
|
|
case CIFOP_MASKHINTS:
|
|
case CIFOP_MAXRECT:
|
|
case CIFOP_MANHATTAN:
|
|
case CIFOP_NET:
|
|
case CIFOP_INTERACT:
|
|
break;
|
|
case CIFOP_BRIDGELIM:
|
|
case CIFOP_BRIDGE:
|
|
bridge = (BridgeData *)op->co_client;
|
|
bridge->br_width *= d;
|
|
lgcf = FindGCF(abs(bridge->br_width),
|
|
ostyle->cs_expander);
|
|
lexpand = FindGCF(lexpand, lgcf);
|
|
break;
|
|
default:
|
|
bloats = (BloatData *)op->co_client;
|
|
for (j = 0; j < TT_MAXTYPES; j++)
|
|
{
|
|
if (bloats->bl_distance[j] != 0)
|
|
{
|
|
bloats->bl_distance[j] *= d;
|
|
lgcf = FindGCF(abs(bloats->bl_distance[j]),
|
|
ostyle->cs_expander);
|
|
lexpand = FindGCF(lexpand, lgcf);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* time-saving quick check */
|
|
if ((lexpand == 1) && (d == 1)) break;
|
|
}
|
|
}
|
|
|
|
/* Recompute drc-cif rule distances */
|
|
drcCifScale(d, 1);
|
|
|
|
/* Reduce the scale and all distances by the greatest common */
|
|
/* factor of everything. */
|
|
|
|
/* fprintf(stderr, "All CIF units divisible by %d\n", lexpand); */
|
|
/* fflush(stderr); */
|
|
|
|
lgcf = FindGCF(ostyle->cs_scaleFactor, ostyle->cs_expander);
|
|
if (lgcf < lexpand) lexpand = lgcf;
|
|
if (lexpand <= 1) return;
|
|
|
|
/* fprintf(stderr, "Expander goes from %d to %d\n", ostyle->cs_expander,
|
|
ostyle->cs_expander / lexpand); */
|
|
/* fprintf(stderr, "All CIF op distances are divided by %d\n", lexpand); */
|
|
/* fflush(stderr); */
|
|
|
|
ostyle->cs_scaleFactor /= lexpand;
|
|
ostyle->cs_expander /= lexpand;
|
|
|
|
for (i = 0; i < ostyle->cs_nLayers; i++)
|
|
{
|
|
cl = ostyle->cs_layers[i];
|
|
for (op = cl->cl_ops; op != NULL; op = op->co_next)
|
|
{
|
|
if (op->co_distance)
|
|
op->co_distance /= lexpand;
|
|
|
|
if (op->co_client)
|
|
{
|
|
int nlayers;
|
|
switch (op->co_opcode)
|
|
{
|
|
case CIFOP_SLOTS:
|
|
slots = (SlotsData *)op->co_client;
|
|
if (slots->sl_sborder != 0)
|
|
slots->sl_sborder /= lexpand;
|
|
if (slots->sl_ssize != 0)
|
|
slots->sl_ssize /= lexpand;
|
|
if (slots->sl_ssep != 0)
|
|
slots->sl_ssep /= lexpand;
|
|
if (slots->sl_lborder != 0)
|
|
slots->sl_lborder /= lexpand;
|
|
if (slots->sl_lsize != 0)
|
|
slots->sl_lsize /= lexpand;
|
|
if (slots->sl_lsep != 0)
|
|
slots->sl_lsep /= lexpand;
|
|
if (slots->sl_offset != 0)
|
|
slots->sl_offset /= lexpand;
|
|
if (slots->sl_start != 0)
|
|
slots->sl_start /= lexpand;
|
|
break;
|
|
case CIFOP_SQUARES_G:
|
|
squares = (SquaresData *)op->co_client;
|
|
if (squares->sq_gridx != 0)
|
|
squares->sq_gridx /= lexpand;
|
|
if (squares->sq_gridy != 0)
|
|
squares->sq_gridy /= lexpand;
|
|
/* (drop through) */
|
|
case CIFOP_SQUARES:
|
|
squares = (SquaresData *)op->co_client;
|
|
if (squares->sq_border != 0)
|
|
squares->sq_border /= lexpand;
|
|
if (squares->sq_size != 0)
|
|
squares->sq_size /= lexpand;
|
|
if (squares->sq_sep != 0)
|
|
squares->sq_sep /= lexpand;
|
|
break;
|
|
case CIFOP_BLOAT:
|
|
case CIFOP_BLOATMIN:
|
|
case CIFOP_BLOATMAX:
|
|
bloats = (BloatData *)op->co_client;
|
|
for (j = 0; j < TT_MAXTYPES; j++)
|
|
if (bloats->bl_distance[j] != 0)
|
|
bloats->bl_distance[j] /= lexpand;
|
|
break;
|
|
case CIFOP_BRIDGELIM:
|
|
case CIFOP_BRIDGE:
|
|
bridge = (BridgeData *)op->co_client;
|
|
bridge->br_width /= lexpand;
|
|
break;
|
|
default:
|
|
/* op->co_opcode in CIFOP_OR is a pointer copy, */
|
|
/* in CIFOP_BBOX and CIFOP_MAXRECT is a flag, */
|
|
/* and in CIFOP_NET and CIFOP_MASKHINTS is a */
|
|
/* string. */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Recompute drc-cif rule distances */
|
|
drcCifScale(1, lexpand);
|
|
|
|
/* Recompute value of cs_radius */
|
|
cifComputeHalo(ostyle);
|
|
}
|