magic/drc/DRCtech.c

4532 lines
120 KiB
C

/*
* DRCtech.c --
*
* Technology initialization for the DRC module.
*
* *********************************************************************
* * 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/drc/DRCtech.c,v 1.12 2010/10/20 12:04:12 tim Exp $";
#endif /* not lint */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#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 "drc/drc.h"
#include "utils/tech.h"
#include "textio/textio.h"
#include "utils/malloc.h"
#include "cif/cif.h"
#include "cif/CIFint.h"
#include "drc/drc.h"
/* C99 compat */
#include "utils/tech.h"
#include "plow/plow.h"
CIFStyle *drcCifStyle = NULL;
bool DRCForceReload = FALSE;
HashTable DRCWhyErrorTable; /* Table of DRC errors */
/*
* DRC interaction radius being used (not necessarily the same as
* what's defined in the current DRC style).
*/
global int DRCTechHalo;
global int DRCStepSize;
/* The following variable can be set to zero to turn off
* any optimizations of design rule lists.
*/
global int DRCRuleOptimization = TRUE;
/* The following variables count how many rules were specified by
* the technology file and how many edge rules were optimized away.
*/
static int drcRulesSpecified = 0;
static int drcRulesOptimized = 0;
/* Rules with unique names are tagged with a reference number */
/* for use in placing errors into the DRC error plane. */
static int DRCtag = 0;
/*
* Forward declarations.
*/
int drcWidth(), drcSpacing(), drcEdge(), drcNoOverlap();
int drcExactOverlap(), drcExtend();
int drcSurround(), drcRectOnly(), drcOverhang();
int drcStepSize(), drcOption(), drcOffGrid();
int drcMaxwidth(), drcArea(), drcRectangle(), drcAngles();
int drcCifSetStyle(), drcCifWidth(), drcCifSpacing();
int drcCifMaxwidth(), drcCifArea();
void DRCTechStyleInit();
void drcLoadStyle();
void DRCTechFinal();
void drcTechFinalStyle();
/*
* ----------------------------------------------------------------------------
*
* Given a TileType bit mask, return the plane mask of planes that are
* coincident with all types. This is roughly equivalent to the deprecated
* DBTechMinSetPlanes(), but does not attempt to produce a reduced set of
* tile types. Since the collective mask of all possible planes is usually
* found by a call to DBTechNoisyNameMask, we don't attempt to OR all the
* plane masks together, but assume this collective mask is available and
* passed as an argument.
*
* ----------------------------------------------------------------------------
*/
PlaneMask
CoincidentPlanes(typeMask, pmask)
TileTypeBitMask *typeMask; /* Mask of types to check coincidence */
PlaneMask pmask; /* Mask of all possible planes of types */
{
PlaneMask planes = pmask;
TileType i;
/* AND each plane against the collective mask */
for (i = TT_SELECTBASE; i < DBNumTypes; i++)
if (TTMaskHasType(typeMask, i))
planes &= DBTypePlaneMaskTbl[i];
return planes;
}
/*
* ----------------------------------------------------------------------------
*
* Given a plane mask, return the plane number of the lowest plane in the mask.
*
* ----------------------------------------------------------------------------
*/
int
LowestMaskBit(PlaneMask pmask)
{
PlaneMask pset = pmask;
int plane = 0;
if (pmask == 0) return DBNumPlanes;
while ((pset & 0x1) == 0)
{
plane++;
pset >>= 1;
}
return plane;
}
/*
* ----------------------------------------------------------------------------
*
* DRCPrintStyle --
*
* Print the available and/or current extraction styles.
*
* Results:
* None.
*
* Side effects:
* Output.
*
* ----------------------------------------------------------------------------
*/
void
DRCPrintStyle(dolist, doforall, docurrent)
bool dolist, doforall, docurrent;
{
DRCKeep *style;
if (docurrent)
{
if (DRCCurStyle == NULL)
TxError("Error: No style is set\n");
else
{
if (!dolist) TxPrintf("The current style is \"");
#ifdef MAGIC_WRAPPER
if (dolist)
Tcl_SetResult(magicinterp, (char *)DRCCurStyle->ds_name, NULL);
else
#endif
TxPrintf("%s", DRCCurStyle->ds_name);
if (!dolist) TxPrintf("\".\n");
}
}
if (doforall)
{
if (!dolist) TxPrintf("The DRC styles are: ");
for (style = DRCStyleList; style; style = style->ds_next)
{
if (dolist)
{
#ifdef MAGIC_WRAPPER
Tcl_AppendElement(magicinterp, style->ds_name);
#else
if (style != DRCStyleList) TxPrintf(" ");
TxPrintf("%s", style->ds_name);
#endif
}
else
{
if (style != DRCStyleList) TxPrintf(", ");
TxPrintf("%s", style->ds_name);
}
}
if (!dolist) TxPrintf(".\n");
}
}
/*
* ----------------------------------------------------------------------------
*
* DRCSetStyle --
*
* Set the current DRC style to 'name'.
*
* Results:
* None.
*
* Side effects:
* Output.
*
* ----------------------------------------------------------------------------
*/
void
DRCSetStyle(name)
char *name;
{
DRCKeep *style, *match;
int length;
if (name == NULL) return;
match = NULL;
length = strlen(name);
for (style = DRCStyleList; style; style = style->ds_next)
{
if (strncmp(name, style->ds_name, length) == 0)
{
if (match != NULL)
{
TxError("DRC style \"%s\" is ambiguous.\n", name);
DRCPrintStyle(FALSE, TRUE, TRUE);
return;
}
match = style;
}
}
if (match != NULL)
{
drcLoadStyle(match->ds_name);
TxPrintf("DRC style is now \"%s\"\n", name);
return;
}
TxError("\"%s\" is not one of the DRC styles Magic knows.\n", name);
DRCPrintStyle(FALSE, TRUE, TRUE);
}
/*
* ----------------------------------------------------------------------------
*
* drcTechFreeStyle --
*
* This procedure frees all memory associated with a DRC style.
*
* Results:
* None.
*
* Side effects:
* Memory free'd.
*
* ----------------------------------------------------------------------------
*/
void
drcTechFreeStyle()
{
int i, j;
char *old;
DRCCookie *dp;
if (DRCCurStyle != NULL)
{
/* Remove all old rules from the DRC rules table */
for (i = 0; i < TT_MAXTYPES; i++)
for (j = 0; j < TT_MAXTYPES; j++)
{
dp = DRCCurStyle->DRCRulesTbl[i][j];
while (dp != NULL)
{
char *old = (char *)dp;
dp = dp->drcc_next;
freeMagic(old);
}
}
/* Clear the Why string list */
freeMagic(DRCCurStyle->DRCWhyList);
freeMagic(DRCCurStyle);
DRCCurStyle = NULL;
}
}
/*
* ----------------------------------------------------------------------------
*
* drcTechNewStyle --
*
* This procedure creates a new DRC style at the end of the list
* of styles and initializes it to completely null.
*
* Results:
* None.
*
* Side effects:
* A new element is added to the end of DRCStyleList, and DRCCurStyle
* is set to point to it.
*
* ----------------------------------------------------------------------------
*/
void
drcTechNewStyle()
{
drcTechFreeStyle();
DRCTechStyleInit();
}
/*
* ----------------------------------------------------------------------------
* drcWhyCreate --
*
* Create a hash entry for the DRC "why" string, if it does not already
* exist.
*
* Returns:
* A pointer to the drcWhy structure containing the string and tag.
*
* Side effects:
* Adds to the DRCWhyList if whystring has not been used before.
* Calls StrDup() and increments DRCWhySize. DRCWhyList is allocated
* in blocks of 50 at a time and only expands when filled.
* Temporary hash table DRCWhyErrorTable is used to determine if a
* string entry is unique. It is cleared after the technology file
* has been processed.
* ----------------------------------------------------------------------------
*/
int
drcWhyCreate(whystring)
char *whystring;
{
HashEntry *he;
he = HashLookOnly(&DRCWhyErrorTable, whystring);
if (he != NULL)
return (int)((pointertype)HashGetValue(he));
/* Grow the list in increments of 50 */
if ((DRCCurStyle->DRCWhySize % 50) == 0)
{
int i;
char **newList;
newList = (char **)mallocMagic((DRCCurStyle->DRCWhySize + 51) * sizeof(char *));
newList[0] = (char *)NULL;
for (i = 1; i <= DRCCurStyle->DRCWhySize; i++)
newList[i] = DRCCurStyle->DRCWhyList[i];
if (DRCCurStyle->DRCWhySize > 0)
freeMagic((char *)DRCCurStyle->DRCWhyList);
DRCCurStyle->DRCWhyList = newList;
}
DRCCurStyle->DRCWhySize++;
he = HashFind(&DRCWhyErrorTable, whystring);
HashSetValue(he, (char *)((pointertype)DRCCurStyle->DRCWhySize));
DRCCurStyle->DRCWhyList[DRCCurStyle->DRCWhySize] = StrDup((char **)NULL, whystring);
return DRCCurStyle->DRCWhySize;
}
/*
* ----------------------------------------------------------------------------
*
* drcFindBucket --
*
* Find the bucket preceding the point where we with to insert a new DRC
* cookie. Don't insert a cookie in the middle of a pair of coupled
* (trigger + check) rules.
*
* Results:
* Returns a pointer to the location where we want to insert a rule
*
* Side effects:
* None.
*
* ----------------------------------------------------------------------------
*/
DRCCookie *
drcFindBucket(i, j, distance)
int i, j, distance;
{
DRCCookie *dp;
if (DRCCurStyle == NULL) return NULL;
/* find bucket preceding the new one we wish to insert */
for (dp = DRCCurStyle->DRCRulesTbl[i][j];
dp->drcc_next != (DRCCookie *) NULL;
dp = dp->drcc_next)
{
if (dp->drcc_next->drcc_flags & DRC_TRIGGER)
{
if (dp->drcc_next->drcc_next->drcc_dist >= distance)
break;
else
dp = dp->drcc_next;
}
else if (dp->drcc_next->drcc_dist >= distance) break;
}
return dp;
}
/*
* ----------------------------------------------------------------------------
*
* drcLoadStyle --
*
* Re-read the technology file to load the specified technology DRC style
* into structure DRCCurStyle. It incurs a complete reading of the tech
* file on startup and every time the extraction style is changed, but we
* can assume that this does not happen often. The first style in the
* technology file is assumed to be the default, so that re-reading the
* tech file is not necessary on startup unless the default DRC style is
* changed by a call do "drc style".
*
* ----------------------------------------------------------------------------
*/
void
drcLoadStyle(stylename)
char *stylename;
{
SectionID invdrc;
if (DRCCurStyle->ds_name == stylename) return;
drcTechNewStyle(); /* Reinitialize and mark as not loaded */
DRCCurStyle->ds_name = stylename;
invdrc = TechSectionGetMask("drc", NULL);
TechLoad(NULL, invdrc);
DRCTechScale(DBLambda[0], DBLambda[1]);
}
/*
* ----------------------------------------------------------------------------
* DRCReloadCurStyle ---
*
* This routine is used by CIFLoadStyle whenever the DRC section makes
* reference to CIF layers.
*
* Results:
* None.
*
* Side Effects:
* DRC rule database is deleted and regenerated.
*
* ----------------------------------------------------------------------------
*/
void
DRCReloadCurStyle()
{
char *stylename;
DRCKeep * style;
if (DRCCurStyle == NULL) return;
for (style = DRCStyleList; style != NULL; style = style->ds_next)
{
if (!strcmp(style->ds_name, DRCCurStyle->ds_name))
{
DRCCurStyle->ds_name = NULL;
drcLoadStyle(style->ds_name);
break;
}
}
}
/*
* ----------------------------------------------------------------------------
* DRCTechInit --
*
* Free and re-initialize the technology-specific variables for the DRC module.
* This routine is *only* called upon a "tech load" command, when all existing
* DRC data must be cleaned up and free'd.
*
* Results:
* None.
*
* Side effects:
* Clears out all the DRC tables.
* ----------------------------------------------------------------------------
*/
void
DRCTechInit()
{
DRCKeep *style;
/* Clean up any old info */
drcTechFreeStyle();
for (style = DRCStyleList; style != NULL; style = style->ds_next)
{
freeMagic(style->ds_name);
freeMagic(style);
}
DRCStyleList = NULL;
}
/*
* ----------------------------------------------------------------------------
* DRCTechStyleInit --
*
* Initialize the technology-specific variables for the DRC module.
*
* Results:
* None.
*
* Side effects:
* Clears out all the DRC tables.
* ----------------------------------------------------------------------------
*/
void
DRCTechStyleInit()
{
int i, j, plane;
DRCCookie *dp;
PaintResultType result;
drcRulesOptimized = 0;
drcRulesSpecified = 0;
if (DRCCurStyle == NULL)
{
DRCCurStyle = (DRCStyle *) mallocMagic(sizeof(DRCStyle));
DRCCurStyle->ds_name = NULL;
}
DRCCurStyle->ds_status = TECH_NOT_LOADED;
TTMaskZero(&DRCCurStyle->DRCExactOverlapTypes);
DRCCurStyle->DRCTechHalo = 0;
DRCCurStyle->DRCScaleFactorN = 1;
DRCCurStyle->DRCScaleFactorD = 1;
DRCCurStyle->DRCStepSize = 0;
DRCCurStyle->DRCFlags = (char)0;
DRCCurStyle->DRCWhySize = 0;
HashInit(&DRCWhyErrorTable, 16, HT_STRINGKEYS);
/* First DRC entry is associated with the statically-allocated */
/* drcArrayCookie and has a tag of DRC_ARRAY_OVERLAP_TAG = 1 */
/* (see DRCarray.c). */
drcWhyCreate("This layer can't abut or partially overlap between array elements");
/* Second DRC entry is associated with the statically-allocated */
/* drcOverlapCookie and has a tag of DRC_OVERLAP_TAG = 2 */
/* (see DRCbasic.c). */
drcWhyCreate("Can't overlap those layers");
/* Third DRC entry is associated with the statically-allocated */
/* drcSubcellCookie and has a tag of DRC_SUBCELL_OVERLAP_TAG = 3 */
/* (see DRCsubcell.c). */
drcWhyCreate("This layer can't abut or partially overlap between subcells");
/* Fourth DRC entry is associated with the statically-allocated */
/* drcSubcellCookie and has a tag of DRC_IN_SUBCELL_TAG = 4 */
/* (see DRCsubcell.c). */
drcWhyCreate("See error definition in the subcell");
/* Fifth DRC entry is associated with the statically-allocated */
/* drcOffGridCookie and has a tag of DRC_OFFGRID_TAG = 5 */
/* (see DRCsubcell.c). */
drcWhyCreate("This position does not align with the manufacturing grid");
DRCTechHalo = 0;
/* Put a dummy rule at the beginning of the rules table for each entry */
for (i = 0; i < TT_MAXTYPES; i++)
{
for (j = 0; j < TT_MAXTYPES; j++)
{
dp = DRCCurStyle->DRCRulesTbl[i][j];
dp = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie)));
dp->drcc_dist = -1;
dp->drcc_cdist = -1;
dp->drcc_next = (DRCCookie *) NULL;
TTMaskZero(&dp->drcc_mask);
DRCCurStyle->DRCRulesTbl[i][j] = dp;
}
}
/* Copy the default paint table into the DRC paint table. The DRC
* paint table will be modified as we read the drc section. Also
* make sure that the error layer is super-persistent (once it
* appears, it can't be gotten rid of by painting). Also, make
* some crossings automatically illegal: two layers can't cross
* unless the result of painting one on top of the other is to
* get one of the layers, and it doesn't matter which is painted
* on top of which.
*/
for (plane = 0; plane < DBNumPlanes; plane++)
for (i = 0; i < DBNumTypes; i++)
for (j = 0; j < DBNumTypes; j++)
{
result = DBPaintResultTbl[plane][i][j];
if ((i == TT_ERROR_S) || (j == TT_ERROR_S))
DRCCurStyle->DRCPaintTable[plane][i][j] = TT_ERROR_S;
else if ((i == TT_SPACE) || (j == TT_SPACE)
|| !DBTypeOnPlane(j, plane)
|| !DBPaintOnTypePlanes(i, j))
DRCCurStyle->DRCPaintTable[plane][i][j] = result;
/* Modified for stackable types (Tim, 10/3/03) */
else if ((i >= DBNumUserLayers) ||
((result >= DBNumUserLayers)
&& (DBTechFindStacking(i, j) == result)))
{
DRCCurStyle->DRCPaintTable[plane][i][j] = result;
}
else if ((!TTMaskHasType(&DBLayerTypeMaskTbl[i], result)
&& !TTMaskHasType(&DBLayerTypeMaskTbl[j], result))
|| ((result != DBPaintResultTbl[plane][j][i])
&& DBTypeOnPlane(i, plane)
&& DBPaintOnTypePlanes(j, i)))
{
DRCCurStyle->DRCPaintTable[plane][i][j] = TT_ERROR_S;
/* TxError("Error: %s on %s, was %s\n",
DBTypeLongNameTbl[i], DBTypeLongNameTbl[j],
DBTypeLongNameTbl[result]); */
}
else
DRCCurStyle->DRCPaintTable[plane][i][j] = result;
}
drcCifInit();
}
/*
* ----------------------------------------------------------------------------
*
* DRCTechLine --
*
* Parse a line in the DRC section from the technology file. Handle DRC
* styles. The rules themselves are handled by DRCTechAddRule.
*
* Results:
* TRUE if line parsed correctly; FALSE if fatal error condition
* encountered.
*
* Side effects:
* Appends information to the DRC tables.
*
* ----------------------------------------------------------------------------
*/
bool
DRCTechLine(sectionName, argc, argv)
char *sectionName; /* The name of this section */
int argc; /* Number of fields on the line */
char *argv[]; /* Values of the fields */
{
int j, l;
DRCKeep *newStyle, *p;
char *tptr, *cptr;
if (argc <= 0) return TRUE;
else if (argc >= 2) l = strlen(argv[1]);
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]);
return TRUE;
}
}
for (newStyle = DRCStyleList; newStyle != NULL;
newStyle = newStyle->ds_next)
{
/* Here we're only establishing existence; break on
* the first variant found.
*/
if (!strncmp(newStyle->ds_name, argv[1], l))
break;
}
if (newStyle == NULL)
{
if (argc == 2)
{
newStyle = (DRCKeep *)mallocMagic(sizeof(DRCKeep));
newStyle->ds_next = NULL;
newStyle->ds_name = StrDup((char **) NULL, argv[1]);
/* Append to end of style list */
if (DRCStyleList == NULL)
DRCStyleList = newStyle;
else
{
for (p = DRCStyleList; p->ds_next; p = p->ds_next);
p->ds_next = newStyle;
}
}
else /* Handle style variants */
{
DRCKeep *saveStyle = NULL;
/* 4th argument is a comma-separated list of variants */
tptr = argv[3];
while (*tptr != '\0')
{
cptr = strchr(tptr, ',');
if (cptr != NULL) *cptr = '\0';
newStyle = (DRCKeep *)mallocMagic(sizeof(DRCKeep));
newStyle->ds_next = NULL;
newStyle->ds_name = (char *)mallocMagic(l
+ strlen(tptr) + 1);
sprintf(newStyle->ds_name, "%s%s", argv[1], tptr);
/* Remember the 1st variant as the default */
if (saveStyle == NULL) saveStyle = newStyle;
/* Append to end of style list */
if (DRCStyleList == NULL)
DRCStyleList = newStyle;
else
{
for (p = DRCStyleList; p->ds_next; p = p->ds_next);
p->ds_next = newStyle;
}
if (cptr == NULL)
break;
else
tptr = cptr + 1;
}
newStyle = saveStyle;
}
}
if (DRCCurStyle == NULL) /* Shouldn't happen, but be safe. . .*/
{
drcTechNewStyle();
DRCCurStyle->ds_name = newStyle->ds_name;
DRCCurStyle->ds_status = TECH_PENDING;
}
else if ((DRCCurStyle->ds_status == TECH_PENDING) ||
(DRCCurStyle->ds_status == TECH_SUSPENDED))
DRCCurStyle->ds_status = TECH_LOADED;
else if (DRCCurStyle->ds_status == TECH_NOT_LOADED)
{
if (DRCCurStyle->ds_name == NULL) {
DRCCurStyle->ds_name = newStyle->ds_name;
DRCCurStyle->ds_status = TECH_PENDING;
}
else if (argc == 2)
{
if (!strcmp(argv[1], DRCCurStyle->ds_name))
DRCCurStyle->ds_status = TECH_PENDING;
}
else if (argc == 4)
{
/* Verify that the style matches one variant */
if (!strncmp(DRCCurStyle->ds_name, argv[1], l))
{
tptr = argv[3];
while (*tptr != '\0')
{
cptr = strchr(tptr, ',');
if (cptr != NULL) *cptr = '\0';
if (!strcmp(DRCCurStyle->ds_name + l, tptr))
{
DRCCurStyle->ds_status = TECH_PENDING;
return TRUE;
}
if (cptr == NULL)
return TRUE;
else
tptr = cptr + 1;
}
}
}
}
return (TRUE);
}
if (DRCCurStyle == NULL)
return FALSE;
/* For backwards compatibility, if we have encountered a line that */
/* is not "style" prior to setting a style, then we create a style */
/* called "default". */
if (DRCStyleList == NULL)
{
char bufargv[2][10];
char *locargv[2];
strcpy(bufargv[0], "style");
strcpy(bufargv[1], "default");
locargv[0] = bufargv[0];
locargv[1] = bufargv[1];
if (DRCTechLine(sectionName, 2, locargv) == FALSE)
return FALSE;
}
else if (DRCStyleList->ds_next == NULL)
{
/* On reload, if there is only one style, use it. This is */
/* necessary for the DRC-CIF extensions, since even though */
/* the one-and-only DRC section may have been loaded, the DRC- */
/* CIF parts of it become enabled or disabled depending on what */
/* CIFCurStyle is active. */
DRCCurStyle->ds_status = TECH_PENDING;
}
/* Only continue past this point if we are loading the DRC style */
if ((DRCCurStyle->ds_status != TECH_PENDING) &&
(DRCCurStyle->ds_status != TECH_SUSPENDED))
return TRUE;
/* Process "scalefactor" line next (if any) */
if (strcmp(argv[0], "scalefactor") == 0)
{
int scaleN, scaleD;
if (argc != 2 && argc != 3) goto wrongNumArgs;
scaleN = atof(argv[1]);
if (argc == 3)
scaleD = atof(argv[2]);
else
scaleD = 1;
if (scaleN <= 0 || scaleD <= 0)
{
TechError("Scale factor must be greater than 0.\n");
TechError("Setting scale factor to default value 1.\n");
DRCCurStyle->DRCScaleFactorN = 1;
DRCCurStyle->DRCScaleFactorD = 1;
return TRUE;
}
DRCCurStyle->DRCScaleFactorN = scaleN;
DRCCurStyle->DRCScaleFactorD = scaleD;
return TRUE;
}
/* Process "variant" lines next. */
if (strncmp(argv[0], "variant", 7) == 0)
{
/* 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 == '*')
{
DRCCurStyle->ds_status = TECH_PENDING;
return TRUE;
}
else
{
l = strlen(DRCCurStyle->ds_name) - strlen(tptr);
if (!strcmp(tptr, DRCCurStyle->ds_name + l))
{
DRCCurStyle->ds_status = TECH_PENDING;
return TRUE;
}
}
if (cptr == NULL)
break;
else
tptr = cptr + 1;
}
DRCCurStyle->ds_status = TECH_SUSPENDED;
}
/* Anything below this line is not parsed if we're in TECH_SUSPENDED mode */
if (DRCCurStyle->ds_status != TECH_PENDING) return TRUE;
return DRCTechAddRule(sectionName, argc, argv);
}
void
drcCifAssign(cookie, dist, next, mask, corner, tag, cdist, flags, planeto, planefrom)
DRCCookie *cookie, *next;
int dist, cdist;
TileTypeBitMask *mask, *corner;
int tag;
unsigned short flags;
int planeto, planefrom;
{
(cookie)->drcc_dist = dist;
(cookie)->drcc_next = next;
(cookie)->drcc_mask = *mask;
(cookie)->drcc_corner = *corner;
(cookie)->drcc_tag = tag;
(cookie)->drcc_cdist = cdist;
(cookie)->drcc_flags = flags;
(cookie)->drcc_edgeplane = planefrom;
(cookie)->drcc_plane = planeto;
(cookie)->drcc_mod = 0;
(cookie)->drcc_cmod = 0;
}
// This is like drcCifAssign, but checks for bad plane numbers in planeto and
// planefrom
void
drcAssign(cookie, dist, next, mask, corner, why, cdist, flags, planeto, planefrom)
DRCCookie *cookie, *next;
int dist, cdist;
TileTypeBitMask *mask, *corner;
int why;
unsigned short flags;
int planeto, planefrom;
{
/* Diagnostic */
if (planeto >= DBNumPlanes)
TechError("Bad plane in DRC assignment.\n");
if (planefrom >= DBNumPlanes)
TechError("Bad edge plane in DRC assignment.\n");
drcCifAssign(cookie, dist, next, mask, corner, why, cdist, flags, planeto,
planefrom);
}
/*
* ----------------------------------------------------------------------------
*
* DRCTechAddRule --
*
* Add a new entry to the DRC table.
*
* Results:
* Always returns TRUE so that tech file read-in doesn't abort.
*
* Side effects:
* Updates the DRC technology variables.
*
* Organization:
* We select a procedure based on the first keyword (argv[0])
* and call it to do the work of implementing the rule. Each
* such procedure is of the following form:
*
* int
* proc(argc, argv)
* int argc;
* char *argv[];
* {
* }
*
* It returns the distance associated with the design rule,
* or -1 in the event of a fatal error that should cause
* DRCTechAddRule() to return FALSE (currently, none of them
* do, so we always return TRUE). If there is no distance
* associated with the design rule, 0 is returned.
*
* ----------------------------------------------------------------------------
*/
/* ARGSUSED */
bool
DRCTechAddRule(sectionName, argc, argv)
char *sectionName; /* Unused */
int argc;
char *argv[];
{
int which, distance, mdist;
const char *fmt;
static const struct
{
const char *rk_keyword; /* Initial keyword */
int rk_minargs; /* Min # arguments */
int rk_maxargs; /* Max # arguments */
int (*rk_proc)(); /* Procedure implementing this keyword */
const char *rk_err; /* Error message */
} ruleKeys[] = {
{"angles", 4, 4, drcAngles,
"layers 45|90 why"},
{"edge", 8, 9, drcEdge,
"layers1 layers2 distance okTypes cornerTypes cornerDistance why [plane]"},
{"edge4way", 8, 9, drcEdge,
"layers1 layers2 distance okTypes cornerTypes cornerDistance why [plane]"},
{"exact_overlap", 2, 2, drcExactOverlap,
"layers"},
{"extend", 5, 6, drcExtend,
"layers1 layers2 distance [option] why"},
{"no_overlap", 3, 3, drcNoOverlap,
"layers1 layers2"},
{"option", 2, 2, drcOption,
"option_name option_value"},
{"overhang", 5, 5, drcOverhang,
"layers1 layers2 distance why"},
{"rect_only", 3, 3, drcRectOnly,
"layers why"},
{"spacing", 6, 7, drcSpacing,
"layers1 layers2 separation [layers3] adjacency why"},
{"stepsize", 2, 2, drcStepSize,
"step_size"},
{"surround", 6, 7, drcSurround,
"layers1 layers2 distance presence why"},
{"width", 4, 5, drcWidth,
"layers width why"},
{"widespacing", 7, 8, drcSpacing,
"layers1 width layers2 separation adjacency why"},
{"area", 5, 5, drcArea,
"layers area horizon why"},
{"off_grid", 4, 4, drcOffGrid,
"layers pitch why"},
{"maxwidth", 4, 5, drcMaxwidth,
"layers maxwidth bends why"},
{"cifstyle", 2, 2, drcCifSetStyle,
"cif_style"},
{"cifwidth", 4, 4, drcCifWidth,
"layers width why"},
{"cifspacing", 6, 6, drcCifSpacing,
"layers1 layers2 separation adjacency why"},
{"cifarea", 5, 5, drcCifArea,
"layers area horizon why"},
{"cifmaxwidth", 5, 5, drcCifMaxwidth,
"layers maxwidth bends why"},
{"rectangle", 5, 5, drcRectangle,
"layers maxwidth [even|odd|any] why"},
{0}
}, *rp;
drcRulesSpecified += 1;
which = LookupStruct(argv[0], (const LookupTable *) ruleKeys, sizeof ruleKeys[0]);
if (which < 0)
{
TechError("Bad DRC rule type \"%s\"\n", argv[0]);
TxError("Valid rule types are:\n");
for (fmt = "%s", rp = ruleKeys; rp->rk_keyword; rp++, fmt = ", %s")
TxError(fmt, rp->rk_keyword);
TxError(".\n");
return (TRUE);
}
rp = &ruleKeys[which];
if (argc < rp->rk_minargs || argc > rp->rk_maxargs)
{
TechError("Rule type \"%s\" usage: %s %s\n",
rp->rk_keyword, rp->rk_keyword, rp->rk_err);
return (TRUE);
}
distance = (*rp->rk_proc)(argc, argv);
if (distance < 0)
return (FALSE);
/* Update the halo to be the maximum distance (in magic units) of */
/* any design rule */
mdist = distance;
if (mdist > DRCTechHalo)
DRCTechHalo = mdist;
return (TRUE);
}
/*
* ----------------------------------------------------------------------------
*
* drcExtend --
*
* Process an extension rule.
* This is of the form:
*
* extend layers1 layers2 distance [exact_width | exclusive] why
*
* indicating that if "layers1" extends from "layers2", then it must
* have a width of at least "distance". This is very much like the
* "overhang" rule, except that the extension is optional. Example:
*
* extend nfet *poly 3 "n-transistor width must be at least 3"
*
* "extend" implements the following general-purpose edge rule:
*
* edge4way layers2 layers1 distance layers1 0 0 why
*
* Option "exact_width" implements an additional rule that checks for
* maximum extension at distance. This is intended for use with, for
* example, a fixed gate length for a specific type of device.
*
* Option "exclusive" is for use for, e.g., transistor length when the
* length rule is larger than the minimum width rule. However, without
* the option, "extend" sets OKtypes to the union of layers1 and layers2;
* that covers, e.g., extension of diffusion from a gate where an
* additional gate within the distance does not violate the rule. With
* "exclusive", layers2 may not appear within "distance" of the edge.
*
* Results:
* Returns distance.
*
* Side effects:
* Updates the DRC technology variables.
*
* ----------------------------------------------------------------------------
*/
int
drcExtend(argc, argv)
int argc;
char *argv[];
{
char *layers1 = argv[1];
char *layers2 = argv[2];
int distance = atoi(argv[3]);
int why;
TileTypeBitMask set1, setC;
DRCCookie *dp, *dpnew, *dptrig;
TileType i, j;
int plane, plane2;
TileTypeBitMask set2, setZ, setN, setM;
PlaneMask pMask1, pMask2, pset, ptest;
bool exact = FALSE;
bool exclusive = FALSE;
if (!strncmp(argv[4], "exact_", 6))
{
exact = TRUE;
why = drcWhyCreate(argv[5]);
}
else if (!strncmp(argv[4], "exclu", 5))
{
exclusive = TRUE;
why = drcWhyCreate(argv[5]);
}
else
why = drcWhyCreate(argv[4]);
ptest = DBTechNoisyNameMask(layers1, &set1);
pMask1 = CoincidentPlanes(&set1, ptest);
if (pMask1 == 0)
{
TechError("All layers in first set for \"extend\" must be on "
"the same plane\n");
return (0);
}
ptest = DBTechNoisyNameMask(layers2, &set2);
pMask2 = CoincidentPlanes(&set2, ptest);
if (pMask2 == 0)
{
TechError("All layers in second set for \"extend\" must be on "
"the same plane\n");
return (0);
}
/* setM is the union of set1 and set2 unless exclusive == TRUE */
TTMaskZero(&setM);
if (exclusive)
TTMaskSetMask(&setM, &set1);
else
TTMaskSetMask3(&setM, &set1, &set2);
/* setN is the inverse of set1, and setC is the inverse of set2 */
TTMaskCom2(&setN, &set1);
TTMaskCom2(&setC, &set2);
/* Zero mask */
TTMaskZero(&setZ);
for (i = 0; i < DBNumTypes; i++)
{
for (j = 0; j < DBNumTypes; j++)
{
if (i == j) continue;
if ((pset = (DBTypesOnSamePlane(i, j) & pMask2)))
{
/* Edge depends on whether or not the extension is */
/* on the same plane as the layer from which it is */
/* measured. */
if ((pset & pMask1) != 0)
{
if (TTMaskHasType(&set2, i) && TTMaskHasType(&set1, j))
{
plane = LowestMaskBit(pset & pMask1);
/* find bucket preceding the new one we wish to insert */
dp = drcFindBucket(i, j, distance);
dpnew = (DRCCookie *)mallocMagic(sizeof(DRCCookie));
drcAssign(dpnew, distance, dp->drcc_next, &setM, &setZ, why,
0, DRC_FORWARD, plane, plane);
dp->drcc_next = dpnew;
dp = drcFindBucket(j, i, distance);
dpnew = (DRCCookie *)mallocMagic(sizeof(DRCCookie));
drcAssign(dpnew, distance, dp->drcc_next, &setM, &setZ, why,
0, DRC_REVERSE, plane, plane);
dp->drcc_next = dpnew;
if (exact)
{
/* find bucket preceding the new one we wish to insert */
dp = drcFindBucket(i, j, distance);
dpnew = (DRCCookie *)mallocMagic(sizeof(DRCCookie));
drcAssign(dpnew, distance, dp->drcc_next, &setN, &setZ, why,
0, DRC_FORWARD | DRC_OUTSIDE, plane, plane);
dp->drcc_next = dpnew;
dp = drcFindBucket(j, i, distance);
dpnew = (DRCCookie *)mallocMagic(sizeof(DRCCookie));
drcAssign(dpnew, distance, dp->drcc_next, &setN, &setZ, why,
0, DRC_REVERSE | DRC_OUTSIDE, plane, plane);
dp->drcc_next = dpnew;
}
}
}
else /* Multi-plane extend rule */
{
if (TTMaskHasType(&set2, i) && TTMaskHasType(&setC, j))
{
plane = LowestMaskBit(pset);
plane2 = LowestMaskBit(pMask1);
/* find bucket preceding the new one we wish to insert */
dp = drcFindBucket(i, j, distance);
dpnew = (DRCCookie *)mallocMagic(sizeof(DRCCookie));
drcAssign(dpnew, distance, dp->drcc_next, &setM, &setZ, why,
0, DRC_FORWARD, plane2, plane);
dptrig = (DRCCookie *)mallocMagic(sizeof(DRCCookie));
drcAssign(dptrig, 1, dpnew, &setN, &setZ, why,
0, DRC_FORWARD | DRC_TRIGGER, plane2, plane);
dp->drcc_next = dptrig;
dp = drcFindBucket(j, i, distance);
dpnew = (DRCCookie *)mallocMagic(sizeof(DRCCookie));
drcAssign(dpnew, distance, dp->drcc_next, &setM, &setZ, why,
0, DRC_REVERSE, plane2, plane);
dptrig = (DRCCookie *)mallocMagic(sizeof(DRCCookie));
drcAssign(dptrig, 1, dpnew, &setN, &setZ, why,
0, DRC_REVERSE | DRC_TRIGGER, plane2, plane);
dp->drcc_next = dptrig;
}
}
}
}
}
return (distance);
}
/*
* ----------------------------------------------------------------------------
*
* drcWidth --
*
* Process a width rule.
* This is of the form:
*
* width layers distance [angles] why
*
* e.g,
*
* width poly,pmc 2 "poly width must be at least 2"
*
* The option "angles" is computed only on non-manhattan edges and is used
* in cases were, for example, an angled transistor has a minimum width
* larger than that of a straight gate.
*
* Results:
* Returns distance.
*
* Side effects:
* Updates the DRC technology variables.
*
* ----------------------------------------------------------------------------
*/
int
drcWidth(argc, argv)
int argc;
char *argv[];
{
char *layers = argv[1];
int distance = atoi(argv[2]);
int why;
TileTypeBitMask set, setC;
PlaneMask pmask, pset, ptest;
DRCCookie *dp, *dpnew;
TileType i, j;
int plane, flags = 0;
if (!strncmp(argv[3], "angle", 5))
{
flags = DRC_SPLITTILE;
why = drcWhyCreate(argv[4]);
}
else
why = drcWhyCreate(argv[3]);
ptest = DBTechNoisyNameMask(layers, &set);
pmask = CoincidentPlanes(&set, ptest);
TTMaskCom2(&setC, &set);
if (pmask == 0)
{
TechError("All layers for \"width\" must be on same plane\n");
return (0);
}
for (i = 0; i < DBNumTypes; i++)
{
for (j = 0; j < DBNumTypes; j++)
{
if (i == j) continue;
/*
* Must have types in 'set' for at least 'distance'
* to the right of any edge between a type in '~set'
* and a type in 'set'.
*/
if ((pset = (DBTypesOnSamePlane(i, j) & pmask)))
{
if (TTMaskHasType(&setC, i) && TTMaskHasType(&set, j))
{
plane = LowestMaskBit(pset);
/* Find bucket preceding the new one we wish to insert */
dp = drcFindBucket(i, j, distance);
dpnew = (DRCCookie *)mallocMagic(sizeof (DRCCookie));
drcAssign(dpnew, distance, dp->drcc_next, &set, &set,
why, distance, DRC_FORWARD | flags, plane, plane);
dp->drcc_next = dpnew;
}
/* Width checks are symmetric and so need to be checked */
/* in only one direction. However, split tiles are */
/* not symmetric, so apply the reverse case with a flag */
/* that will allow the rule to be applied only in the */
/* case of a split tile. */
if (TTMaskHasType(&set, i) && TTMaskHasType(&setC, j))
{
plane = LowestMaskBit(pset);
/* Find bucket preceding the new one we wish to insert */
dp = drcFindBucket(i, j, distance);
dpnew = (DRCCookie *)mallocMagic(sizeof (DRCCookie));
drcAssign(dpnew, distance, dp->drcc_next, &set, &set,
why, distance, DRC_REVERSE | DRC_SPLITTILE,
plane, plane);
dp->drcc_next = dpnew;
}
}
}
}
return (distance);
}
/*
* ----------------------------------------------------------------------------
*
* drcArea --
*
* Process an area rule.
* This is of the form:
*
* area layers area horizon why
*
* e.g,
*
* area pmc 4 2 "poly contact area must be at least 4"
*
* "area" is the total area in lambda^2.
*
* "horizon" is the halo distance for the check. Normally, this would
* be the area (in lambda^2) divided by the minimum width rule (in
* lambda). Anything larger would not be a violation as long as the
* minimum width rule is satisfied.
*
* Results:
* Returns distance.
*
* Side effects:
* Updates the DRC technology variables.
*
* ----------------------------------------------------------------------------
*/
int
drcArea(argc, argv)
int argc;
char *argv[];
{
char *layers = argv[1];
int distance = atoi(argv[2]);
int horizon = atoi(argv[3]);
int why = drcWhyCreate(argv[4]);
TileTypeBitMask set, setC;
DRCCookie *dp, *dpnew;
TileType i, j;
PlaneMask pmask, ptest, pset;
int plane;
ptest = DBTechNoisyNameMask(layers, &set);
pmask = CoincidentPlanes(&set, ptest);
TTMaskCom2(&setC, &set);
if (pmask == 0)
{
TechError("All layers for \"area\" must be on same plane\n");
return (0);
}
for (i = 0; i < DBNumTypes; i++)
{
for (j = 0; j < DBNumTypes; j++)
{
if (i == j) continue;
/*
* Must have types in 'set' for at least 'distance'
* to the right of any edge between a type in '~set'
* and a type in 'set'.
*/
if ((pset = (DBTypesOnSamePlane(i, j) & pmask)))
{
if (TTMaskHasType(&setC, i) && TTMaskHasType(&set, j))
{
plane = LowestMaskBit(pset);
/* find bucket preceding the new one we wish to insert */
dp = drcFindBucket(i, j, horizon);
dpnew = (DRCCookie *) mallocMagic(sizeof (DRCCookie));
drcAssign(dpnew, horizon, dp->drcc_next, &set, &set, why,
distance, DRC_AREA|DRC_FORWARD, plane, plane);
dp->drcc_next = dpnew;
}
}
}
}
return (horizon);
}
/*
* ----------------------------------------------------------------------------
*
* drcOffGrid --
*
* Process an off-grid rule.
* This is of the form:
*
* off_grid layers pitch why
*
* e.g,
*
* off_grid m1 5 "metal shapes must be on %d grid"
*
* "pitch" is the grid pitch that shapes must be aligned to.
*
* Results:
* Returns pitch (the halo for detecting off-grid errors).
*
* Side effects:
* Updates the DRC technology variables.
*
* ----------------------------------------------------------------------------
*/
int
drcOffGrid(argc, argv)
int argc;
char *argv[];
{
char *layers = argv[1];
int pitch = atoi(argv[2]);
int why = drcWhyCreate(argv[3]);
TileTypeBitMask set, setC;
DRCCookie *dp, *dpnew;
TileType i, j;
PlaneMask pset;
int plane;
DBTechNoisyNameMask(layers, &set);
TTMaskCom2(&setC, &set);
for (i = 0; i < DBNumTypes; i++)
{
for (j = 0; j < DBNumTypes; j++)
{
if (i == j) continue;
/*
* Must have types in 'set' for at least 'distance'
* to the right of any edge between a type in '~set'
* and a type in 'set'.
*/
if ((pset = (DBTypesOnSamePlane(i, j))))
{
if (TTMaskHasType(&setC, i) && TTMaskHasType(&set, j))
{
plane = LowestMaskBit(pset);
/* find bucket preceding the new one we wish to insert */
dp = drcFindBucket(i, j, pitch);
dpnew = (DRCCookie *) mallocMagic(sizeof (DRCCookie));
drcAssign(dpnew, pitch, dp->drcc_next, &set, &set, why,
0, DRC_OFFGRID|DRC_FORWARD, plane, plane);
dp->drcc_next = dpnew;
/* opposite edge also needs to be checked */
dp = drcFindBucket(j, i, pitch);
dpnew = (DRCCookie *) mallocMagic(sizeof (DRCCookie));
drcAssign(dpnew, pitch, dp->drcc_next, &set, &set, why,
0, DRC_OFFGRID|DRC_REVERSE, plane, plane);
dp->drcc_next = dpnew;
}
}
}
}
return (pitch);
}
/*
* ----------------------------------------------------------------------------
*
* drcMaxwidth --
*
* Process a maxwidth rule.
* This is of the form:
*
* maxwidth layers distance [bends] why
*
* This routine was updated 3/6/05 to match the "canonical" definition of
* a maxwidth region, which is any rectangle containing <layers> that is
* at least <distance> in both width and height. If the keyword
* "bend_illegal" is present, then the definition reverts to the original
* (see below) for backwards-compatibility. Otherwise ("bend_ok" or
* nothing), the new routine is used.
*
* maxwidth metal1 389 "metal1 width > 35um must be slotted"
* maxwidth pmc 4 bend_illegal "poly contact area must be no wider than 4"
* maxwidth trench 4 bend_ok "trench width must be exactly 4"
*
* bend_illegal - means that one_dimension must be distance for any
* point in the region. This is used for emitters and contacts
* that are rectangular (so we can't generate them with the
* squares command) and some exact width in one direction.
* bend_ok - Used mainly for wide metal rules where metal greater than
* some given width must be slotted. Also, used for things
* like trench, where the width is some fixed value:
* both - implies bend_illegal and both directions are checked
*
* XXXXX XXXXXX
* X X XXXXXX
* X X X X
* XXXXX XXXXXX
*
* OK BAD
*
* Results:
* Returns distance.
*
* Side effects:
* Updates the DRC technology variables.
*
* ----------------------------------------------------------------------------
*/
int
drcMaxwidth(argc, argv)
int argc;
char *argv[];
{
char *layers = argv[1];
int distance = atoi(argv[2]);
char *bends = argv[3];
int why;
TileTypeBitMask set, setC;
DRCCookie *dp, *dpnew;
TileType i, j;
PlaneMask pmask, ptest, pset;
int plane;
int bend;
ptest = DBTechNoisyNameMask(layers, &set);
pmask = CoincidentPlanes(&set, ptest);
TTMaskCom2(&setC, &set);
if (pmask == 0)
{
TechError("All layers for \"maxwidth\" must be on same plane\n");
return (0);
}
if (argc == 4)
{
/* "bends" is irrelevent if distance is zero, so choose the */
/* faster algorithm to process */
if (distance == 0)
bend = 0;
else
bend = DRC_BENDS;
why = drcWhyCreate(argv[3]);
}
else
{
if (strcmp(bends,"bend_illegal") == 0) bend = 0;
else if (strcmp(bends,"bend_ok") == 0) bend = DRC_BENDS;
else if (strcmp(bends,"both") == 0) bend = DRC_MAXWIDTH_BOTH;
else
{
TechError("unknown bend option %s\n",bends);
return (0);
}
why = drcWhyCreate(argv[4]);
}
for (i = 0; i < DBNumTypes; i++)
{
for (j = 0; j < DBNumTypes; j++)
{
if (i == j) continue;
/*
* Must have types in 'set' for at least 'distance'
* to the right of any edge between a type in '~set'
* and a type in 'set'.
*/
if ((pset = (DBTypesOnSamePlane(i, j) & pmask)))
{
if (TTMaskHasType(&setC, i) && TTMaskHasType(&set, j))
{
plane = LowestMaskBit(pset);
/* find bucket preceding the new one we wish to insert */
dp = drcFindBucket(i, j, distance);
dpnew = (DRCCookie *) mallocMagic(sizeof (DRCCookie));
drcAssign(dpnew, distance, dp->drcc_next, &set, &set, why,
distance, DRC_MAXWIDTH | bend, plane, plane);
dp->drcc_next = dpnew;
}
}
}
}
return (distance);
}
/*
* ----------------------------------------------------------------------------
* drcAngles --
*
* Process an "angles" rule specifying that geometry for a certain layer
* must be limited to 90 degrees or 45 degrees. If not specified, any
* angle is allowed, although width rules will flag errors on acute angles.
*
* ----------------------------------------------------------------------------
*/
int
drcAngles(argc, argv)
int argc;
char *argv[];
{
char *layers = argv[1];
char *endptr;
long value;
int angles;
int why = drcWhyCreate(argv[3]);
TileTypeBitMask set;
DRCCookie *dp, *dpnew;
int plane;
PlaneMask ptest, pset, pmask;
TileType i, j;
value = strtol(argv[2], &endptr, 10);
switch (value)
{
case 45:
angles = DRC_ANGLES_45 | DRC_ANGLES_90;
break;
case 90:
angles = DRC_ANGLES_90;
break;
default:
TechError("angles must be 45 or 90\n");
return 0;
}
if (endptr != NULL)
if (!strcmp(endptr + 1, "only"))
if (angles & DRC_ANGLES_45)
angles = DRC_ANGLES_45; /* 45 degrees only */
ptest = DBTechNoisyNameMask(layers, &set);
/* The 45 degrees only case is implemented like drcRectOnly, except */
/* that drcRectOnly checks only for inside corners, while this */
/* also checks for outside corners. Where inside and outside */
/* corners have been beveled, the error is discarded while */
/* processing. */
if (angles == DRC_ANGLES_45)
{
TileTypeBitMask set2, setC, setM;
pmask = CoincidentPlanes(&set, ptest);
if (pmask == 0)
{
TechError("All types for \"rect_only\" must be on the same plane.\n");
return 0;
}
/* set2 is the inverse of set */
TTMaskCom2(&set2, &set);
for (i = 0; i < DBNumTypes; i++)
{
for (j = 0; j < DBNumTypes; j++)
{
if (i == j) continue;
if ((pset = (DBTypesOnSamePlane(i, j) & pmask)))
{
/* Inside corners */
if (TTMaskHasType(&set, i) && TTMaskHasType(&set2, j))
{
plane = LowestMaskBit(pset);
/* setC = all types in plane */
TTMaskZero(&setC);
TTMaskSetMask(&setC, &DBPlaneTypes[plane]);
/* Find bucket preceding the new one we wish to insert */
dp = drcFindBucket(i, j, 1);
dpnew = (DRCCookie *)mallocMagic(sizeof (DRCCookie));
drcAssign(dpnew, 1, dp->drcc_next, &set2, &setC, why, 1,
angles | DRC_FORWARD | DRC_BOTHCORNERS,
plane, plane);
dp->drcc_next = dpnew;
/* find bucket preceding new one we wish to insert */
dp = drcFindBucket(j, i, 1);
dpnew = (DRCCookie *)mallocMagic(sizeof (DRCCookie));
drcAssign(dpnew, 1, dp->drcc_next, &set2, &setC, why, 1,
angles | DRC_REVERSE | DRC_BOTHCORNERS,
plane, plane);
dp->drcc_next = dpnew;
}
/* Outside corners */
if (TTMaskHasType(&set2, i) && TTMaskHasType(&set, j))
{
plane = LowestMaskBit(pset);
/* setC = all types in plane */
TTMaskZero(&setC);
TTMaskSetMask(&setC, &DBPlaneTypes[plane]);
/* setM = not(i) */
TTMaskSetOnlyType(&setM, i);
TTMaskCom(&setM);
/* Find bucket preceding the new one we wish to insert */
dp = drcFindBucket(i, j, 1);
dpnew = (DRCCookie *)mallocMagic(sizeof (DRCCookie));
drcAssign(dpnew, 1, dp->drcc_next, &setM, &setC, why, 1,
angles | DRC_FORWARD | DRC_BOTHCORNERS,
plane, plane);
dp->drcc_next = dpnew;
/* find bucket preceding new one we wish to insert */
dp = drcFindBucket(j, i, 1);
dpnew = (DRCCookie *)mallocMagic(sizeof (DRCCookie));
drcAssign(dpnew, 1, dp->drcc_next, &setM, &setC, why, 1,
angles | DRC_REVERSE | DRC_BOTHCORNERS,
plane, plane);
dp->drcc_next = dpnew;
}
}
}
}
}
for (i = 0; i < DBNumTypes; i++)
{
if (TTMaskHasType(&set, i))
{
plane = DBPlane(i);
/* Insert rule at boundary of tile and TT_SPACE. This is */
/* processed for each tile, separately from other rules, so */
/* we don't really care what the edge is; TT_SPACE is */
/* chosen as an arbitrary place to hold the rule. */
dp = drcFindBucket(TT_SPACE, i, 1);
dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie)));
drcAssign(dpnew, 1, dp->drcc_next, &set, &set, why,
1, angles, plane, plane);
dp->drcc_next = dpnew;
}
}
return 1;
}
/*
* ----------------------------------------------------------------------------
*
* drcSpacing3 --
*
* Process a special spacing rule of the form:
*
* spacing layers1 layers2 distance corner_ok layers3 why
*
* This spacing rule is not checked when a type from "layers3" fills the
* corner between "layers1" and "layers2". This is used, e.g., for
* diffusion-to-poly spacing, where diffusion and poly may touch at the
* corner of a FET type. It is equivalent to:
*
* edge4way layers1 ~(layers3,layers1) distance ~(layers2) 0 0 why
*
* The complementary case is redundant, so not checked.
*
* ----------------------------------------------------------------------------
*/
int
drcSpacing3(argc, argv)
int argc;
char *argv[];
{
char *layers1 = argv[1], *layers2 = argv[2];
char *layers3 = argv[5];
int distance = atoi(argv[3]);
char *adjacency = argv[4];
int why = drcWhyCreate(argv[6]);
TileTypeBitMask set1, set2, set3;
int plane;
DRCCookie *dp, *dpnew;
TileType i, j;
PlaneMask pmask, pset, ptest;
ptest = DBTechNoisyNameMask(layers1, &set1);
pmask = CoincidentPlanes(&set1, ptest);
ptest = DBTechNoisyNameMask(layers2, &set2);
pmask &= CoincidentPlanes(&set2, ptest);
ptest = DBTechNoisyNameMask(layers3, &set3);
pmask &= CoincidentPlanes(&set3, ptest);
if (pmask == 0)
{
TechError("Spacing check with \"corner_ok\" must have"
" all types in one plane.\n");
return (0);
}
/* In this usage everything must fall in the same plane. */
/* We need masks for (~types2)/plane and for (~(types1,types3))/plane */
TTMaskCom(&set2);
TTMaskSetMask(&set3, &set1);
TTMaskCom(&set3);
for (i = 0; i < DBNumTypes; i++)
{
for (j = 0; j < DBNumTypes; j++)
{
if (i == j) continue;
if ((pset = (DBTypesOnSamePlane(i, j) & pmask)))
{
if (TTMaskHasType(&set1, i) && TTMaskHasType(&set3, j))
{
plane = LowestMaskBit(pset);
/* Find bucket preceding the new one we wish to insert */
dp = drcFindBucket(i, j, distance);
dpnew = (DRCCookie *)mallocMagic(sizeof (DRCCookie));
drcAssign(dpnew, distance, dp->drcc_next, &set2, &set3,
why, distance, DRC_FORWARD | DRC_BOTHCORNERS,
plane, plane);
dp->drcc_next = dpnew;
/* find bucket preceding new one we wish to insert */
dp = drcFindBucket(j, i, distance);
dpnew = (DRCCookie *)mallocMagic(sizeof (DRCCookie));
drcAssign(dpnew, distance, dp->drcc_next, &set2, &set3,
why, distance, DRC_REVERSE | DRC_BOTHCORNERS,
plane, plane);
dp->drcc_next = dpnew;
}
}
}
}
return distance;
}
/*
*-------------------------------------------------------------------
*
* drcMaskSpacing ---
*
* This is the core of the drcSpacing routine. When the spacing
* rule layers independently cover more than one plane, this routine
* is invoked for each independent plane.
*
* Results:
* Returns the rule's maximum distance
*
* Side effects:
* Adds rules to the DRC rule table.
*
*-------------------------------------------------------------------
*/
int
drcMaskSpacing(set1, set2, pmask1, pmask2, wwidth, distance, adjacency,
why, widerule, runlength, multiplane)
TileTypeBitMask *set1, *set2;
PlaneMask pmask1, pmask2;
int wwidth, distance;
char *adjacency;
int why;
bool widerule;
int runlength;
bool multiplane;
{
TileTypeBitMask tmp1, tmp2, setR, setRreverse;
int plane, plane2;
PlaneMask pset, ptest;
DRCCookie *dp, *dpnew;
int needReverse = (widerule) ? TRUE : FALSE;
TileType i, j, pref;
bool needtrigger = FALSE;
bool touchingok = TRUE;
bool cornerok = FALSE;
bool surroundok = FALSE;
if (!strcmp(adjacency, "surround_ok"))
{
if (multiplane)
{
TechError("\"surround_ok\" requires surrounding types to "
"be in the same plane.\n");
return (0);
}
else if ((pmask1 & pmask2) == 0)
{
/* New rule implementation (7/8/06): When types */
/* are on different planes and "surround_ok" is */
/* declared, implement as a triggered rule (but not */
/* for widespacing, which already uses the */
/* triggering rule mechanism). */
if (!widerule)
{
needtrigger = TRUE;
touchingok = FALSE;
needReverse = TRUE;
surroundok = TRUE;
}
else
{
TechError("Widespacing checks cannot use \"surround_ok\".\n");
return (0);
}
}
else
{
TechError("\"surround_ok\" used when spacing rule types are in "
"the same plane. Did you mean \"touching_ok\"?\n");
touchingok = TRUE; /* Treat like "touching_ok" */
}
}
else if (!strcmp(adjacency, "touching_ok"))
{
/* If touching is OK, everything must fall in the same plane. */
if (multiplane || ((pmask1 & pmask2) == 0))
{
TechError("Spacing check with \"touching_ok\" must have"
" all types in one plane. Possibly you want"
" \"surround_ok\"?\n");
return (0);
}
else
touchingok = TRUE;
}
else if (!strcmp(adjacency, "touching_illegal"))
{
touchingok = FALSE;
needtrigger = FALSE;
}
else
{
TechError("Badly formed drc spacing line: need \"touching_ok\", "
"\"touching_illegal\", or \"surround_ok\".\n");
return (0);
}
if (touchingok)
{
/* In "touching_ok rules, spacing to set2 is be checked in FORWARD
* direction at edges between set1 and (setR = ~set1 AND ~set2).
*
* In addition, spacing to set1 is checked in FORWARD direction
* at edges between set2 and (setRreverse = ~set1 AND ~set2).
*
* If set1 and set2 are different, above are checked in REVERSE as
* well as forward direction. This is important since touching
* material frequently masks violations in one direction.
*
* setR and setRreverse are set appropriately below.
*/
tmp1 = *set1;
tmp2 = *set2;
/* Restrict planes to those that are coincident */
pmask1 &= pmask2;
pmask2 = pmask1;
TTMaskCom(&tmp1);
TTMaskCom(&tmp2);
TTMaskAndMask(&tmp1, &tmp2);
setR = tmp1;
setRreverse = tmp1;
/* If set1 != set2, set flag to check rules in both directions */
if (!TTMaskEqual(set1, set2))
needReverse = TRUE;
}
else
{
/* In "touching_illegal" rules, spacing to set2 will be checked
* in FORWARD direction at edges between set1 and (setR=~set1).
*
* In addition, spacing to set1 will be checked in FORWARD direction
* at edges between set2 and (setRreverse= ~set2).
*
* setR and setRreverse are set appropriately below.
*/
TTMaskCom2(&setR, set1);
TTMaskCom2(&setRreverse, set2);
}
for (i = 0; i < DBNumTypes; i++)
{
for (j = 0; j < DBNumTypes; j++)
{
if (i == j) continue;
if ((pset = (DBTypesOnSamePlane(i, j) & pmask1)))
{
plane = LowestMaskBit(pset);
/* LHS is an element of set1, RHS is an element of setR */
if (TTMaskHasType(set1, i) && TTMaskHasType(&setR, j))
{
plane2 = LowestMaskBit(pmask2);
/*
* Must not have 'set2' for 'distance' to the right of
* an edge between 'set1' and the types not in 'set1'
* (touching_illegal case) or in neither
* 'set1' nor 'set2' (touching_ok case).
*/
/* Find bucket preceding the new one we wish to insert */
dp = drcFindBucket(i, j, distance);
dpnew = (DRCCookie *)mallocMagic(sizeof(DRCCookie));
TTMaskClearMask3(&tmp1, &DBPlaneTypes[plane2], set2);
TTMaskAndMask3(&tmp2, &DBPlaneTypes[plane], &setR);
if (widerule)
{
DRCCookie *dptrig;
/* Create two contiguous rules, one for spacing */
/* and one for width. These are created in */
/* reverse order due to the stack property of */
/* the linked list. */
if (runlength > 0)
{
drcAssign(dpnew, distance, dp->drcc_next, &tmp1, &tmp2,
why, runlength, DRC_RUNLENGTH |
DRC_FORWARD, plane2, plane);
dptrig = (DRCCookie *) mallocMagic((unsigned)
(sizeof (DRCCookie)));
drcAssign(dptrig, wwidth, dpnew, set1, set1, why,
runlength, DRC_REVERSE | DRC_MAXWIDTH |
DRC_TRIGGER | DRC_BENDS, plane2, plane);
}
else
{
drcAssign(dpnew, distance, dp->drcc_next, &tmp1, &tmp2,
why, distance, DRC_FORWARD, plane2, plane);
dptrig = (DRCCookie *) mallocMagic((unsigned)
(sizeof (DRCCookie)));
drcAssign(dptrig, wwidth, dpnew, set1, set1, why,
distance, DRC_REVERSE | DRC_MAXWIDTH |
DRC_TRIGGER | DRC_BENDS, plane2, plane);
}
dp->drcc_next = dptrig;
}
else if (needtrigger)
{
DRCCookie *dptrig;
/* Create two contiguous spacing rules */
drcAssign(dpnew, distance, dp->drcc_next, &tmp1, &tmp2,
why, wwidth, DRC_FORWARD | DRC_BOTHCORNERS,
plane2, plane);
dptrig = (DRCCookie *) mallocMagic((unsigned)
(sizeof (DRCCookie)));
drcAssign(dptrig, 1, dpnew, set2, &tmp2, why, 1,
DRC_FORWARD | DRC_TRIGGER,
plane2, plane);
dp->drcc_next = dptrig;
}
else
{
drcAssign(dpnew, distance, dp->drcc_next, &tmp1,
&tmp2, why, wwidth, DRC_FORWARD, plane2, plane);
dp->drcc_next = dpnew;
}
if (needReverse)
dpnew->drcc_flags |= DRC_BOTHCORNERS;
if (needReverse)
{
/* Add check in reverse direction,
* NOTE: am assuming single plane rule here (since reverse
* rules only used with touching_ok which must be
* single plane)
*/
/* find bucket preceding new one we wish to insert */
dp = drcFindBucket(j, i, distance);
dpnew = (DRCCookie *)mallocMagic(sizeof (DRCCookie));
if (widerule)
{
DRCCookie *dptrig;
/* Assign two coupled rules (see above) */
if (runlength > 0)
{
drcAssign(dpnew, distance, dp->drcc_next, &tmp1,
&tmp2, why, runlength, DRC_RUNLENGTH |
DRC_REVERSE | DRC_BOTHCORNERS, plane2, plane);
dptrig = (DRCCookie *) mallocMagic((unsigned)
(sizeof (DRCCookie)));
drcAssign(dptrig, wwidth, dpnew, set1, set1, why,
runlength, DRC_FORWARD | DRC_MAXWIDTH |
DRC_TRIGGER | DRC_BENDS, plane2, plane);
}
else
{
drcAssign(dpnew, distance, dp->drcc_next, &tmp1,
&tmp2, why, distance,
DRC_REVERSE | DRC_BOTHCORNERS, plane2, plane);
dptrig = (DRCCookie *) mallocMagic((unsigned)
(sizeof (DRCCookie)));
drcAssign(dptrig, wwidth, dpnew, set1, set1, why,
distance, DRC_FORWARD | DRC_MAXWIDTH |
DRC_TRIGGER | DRC_BENDS, plane2, plane);
}
dp->drcc_next = dptrig;
}
else if (needtrigger)
{
DRCCookie *dptrig;
/* Create two contiguous spacing rules */
drcAssign(dpnew, distance, dp->drcc_next, &tmp1, &tmp2,
why, wwidth, DRC_REVERSE | DRC_BOTHCORNERS,
plane2, plane);
dptrig = (DRCCookie *) mallocMagic((unsigned)
(sizeof (DRCCookie)));
drcAssign(dptrig, 1, dpnew, set2, &tmp2, why, 1,
DRC_REVERSE | DRC_TRIGGER,
plane2, plane);
dp->drcc_next = dptrig;
}
else
{
drcAssign(dpnew,distance,dp->drcc_next,
&tmp1, &tmp2, why, wwidth,
DRC_REVERSE | DRC_BOTHCORNERS, plane2, plane);
dp->drcc_next = dpnew;
}
}
}
}
if (TTMaskEqual(set1, set2)) continue;
if (widerule) continue; /* Can't determine width of set1 */
/* when looking at the edge of set2 */
/*
* Now, if set1 and set2 are distinct apply the rule for LHS in set1
* and RHS in set2. HOWEVER, "surround_ok" rules are asymmetrically
* triggered and cannot be reversed between set1 and set2.
*/
if ((!surroundok) && (pset = (DBTypesOnSamePlane(i, j) & pmask2)))
{
plane = LowestMaskBit(pset);
/* LHS is an element of set2, RHS is an element of setRreverse */
if (TTMaskHasType(set2, i) && TTMaskHasType(&setRreverse, j))
{
plane2 = LowestMaskBit(pmask1);
/* Find bucket preceding the new one we wish to insert */
dp = drcFindBucket(i, j, distance);
dpnew = (DRCCookie *)mallocMagic(sizeof (DRCCookie));
TTMaskClearMask3(&tmp1, &DBPlaneTypes[plane2], set1);
TTMaskAndMask3(&tmp2, &DBPlaneTypes[plane], &setRreverse);
/* NOTE: This is needed for some situation, but I */
/* do not recall the exact nature of it. In other */
/* cases only the simple rule check is needed. */
if (needtrigger)
{
DRCCookie *dptrig;
/* Create two contiguous spacing rules */
drcAssign(dpnew, distance, dp->drcc_next, &tmp1, &tmp2,
why, distance, DRC_FORWARD | DRC_BOTHCORNERS,
plane2, plane);
dptrig = (DRCCookie *)mallocMagic(sizeof (DRCCookie));
drcAssign(dptrig, 1, dpnew, set1, &tmp2, why, 1,
DRC_FORWARD | DRC_TRIGGER,
plane2, plane);
dp->drcc_next = dptrig;
}
else
{
drcAssign(dpnew, distance, dp->drcc_next, &tmp1, &tmp2,
why, distance, DRC_FORWARD, plane2, plane);
dp->drcc_next = dpnew;
}
if (needReverse)
dpnew->drcc_flags |= DRC_BOTHCORNERS;
if (needReverse)
{
/* Add check in reverse direction,
* NOTE: am assuming single plane rule here (since reverse
* rules only used with touching_ok which must be
* single plane)
*/
/* find bucket preceding new one we wish to insert */
dp = drcFindBucket(j, i, distance);
dpnew = (DRCCookie *) mallocMagic(sizeof (DRCCookie));
/* See above */
if (needtrigger)
{
DRCCookie *dptrig;
/* Create two contiguous spacing rules */
drcAssign(dpnew, distance, dp->drcc_next, &tmp1, &tmp2,
why, distance, DRC_REVERSE | DRC_BOTHCORNERS,
plane2, plane);
dptrig = (DRCCookie *)mallocMagic(sizeof (DRCCookie));
drcAssign(dptrig, 1, dpnew, set1, &tmp2, why, 1,
DRC_REVERSE | DRC_TRIGGER,
plane2, plane);
dp->drcc_next = dptrig;
}
else
{
drcAssign(dpnew, distance, dp->drcc_next,
&tmp1, &tmp2, why, distance,
DRC_REVERSE | DRC_BOTHCORNERS, plane2, plane);
dp->drcc_next = dpnew;
}
}
}
}
/* Finally, if multiplane rule then check that set2 types
* are not present just to right of edges with setR on LHS
* and set1 on RHS. This check is necessary to make sure
* that a set1 rectangle doesn't coincide exactly with a
* set2 rectangle.
* (This check added by Michael Arnold on 4/10/86.)
*/
if (needtrigger) continue;
if ((pset = (DBTypesOnSamePlane(i, j) & pmask1)))
{
plane = LowestMaskBit(pset);
/* LHS is an element of setR, RHS is an element of set1 */
if (TTMaskHasType(&setR, i) && TTMaskHasType(set1, j))
{
/*
* Must not have 'set2' for 'distance' to the right of
* an edge between the types not in set1 and set1.
* (is only checked for cross plane rules - these are
* all of type touching_illegal)
*/
/* Walk list to last check. New checks ("cookies") go
* at end of list since we are checking for distance of
* 1 and the list is sorted in order of decreasing distance.
*/
for (dp = DRCCurStyle->DRCRulesTbl [i][j];
dp->drcc_next != (DRCCookie *) NULL;
dp = dp->drcc_next); /* null body */
/* Insert one check for each plane involved in set2 */
plane2 = LowestMaskBit(pmask2);
/* filter out checks that are not cross plane */
if (i == TT_SPACE)
{
if (DBTypeOnPlane(j, plane2))
continue;
}
else
{
if (DBTypeOnPlane(i, plane2))
continue;
}
/* create new check and add it to list */
dpnew = (DRCCookie *) mallocMagic(sizeof (DRCCookie));
TTMaskClearMask3(&tmp1, &DBPlaneTypes[plane2], set2);
TTMaskZero(&tmp2);
drcAssign(dpnew, 1, dp->drcc_next, &tmp1, &tmp2, why,
distance, DRC_FORWARD, plane2, plane);
dp->drcc_next = dpnew;
}
}
}
}
return (MAX(wwidth, distance));
}
/*
* ----------------------------------------------------------------------------
*
* drcSpacing --
*
* Process a spacing rule.
* This is of the form:
*
* spacing layers1 layers2 distance adjacency why
*
* e.g,
*
* spacing metal,pmc/m,dmc/m metal,pmc/m,dmc/m 4 touching_ok \
* "metal spacing must be at least 4"
*
* Adjacency may be either "touching_ok" or "touching_illegal"
* In the first case, no violation occurs when types in layers1 are
* immediately adjacent to types in layers2. In the second case,
* such adjacency causes a violation.
*
* Results:
* Returns distance.
*
* Side effects:
* Updates the DRC technology variables.
*
* Notes:
* Extended to include the rule syntax:
*
* widespacing layers1 width [runlength] layers2 distance adjacency why
*
* This extension covers rules such as "If m1 width > 10um, then spacing to
* unconnected m1 must be at least 0.6um". This assumes that the instantiated
* edge4way rule is a standard "spacing" rule in which "dist" and "cdist"
* (corner extension) distances are always the same, so we can use the "cdist"
* record to encode the width of "layers1" which triggers the rule. We re-use
* the CheckMaxwidth() code, but with the following differences: 1) it does not
* trigger any error painting functions, 2) it returns a value stating whether
* the maxwidth rule applies or not, 3) it uses the DBLayerTypeMaskTbl[] for
* the tile type in question, not "oktypes", for its search, and 4) it uses
* the max width in the "cdist" record, not the "dist" record, for the max
* width rule. The "dist" record is copied to "cdist" before actually applying
* the spacing rule, so that the rule acts like a proper spacing rule.
*
* Added adjacency rule "surround_ok" to check spacing to a type that
* may also abut or surround the first layer---the portion of the layer
* that is abutting or surrounding is not checked. This allows checks
* of, e.g., diffusion in well to a separate well edge, or distance from
* a poly bottom plate to unconnected poly.
*
* (Added 11/6/06) Adjacency may be "corner_ok", in which case it calls
* routing drcSpacing3() (see above).
*
* (Added 9/19/18) Additional run-length distance can be used to limit the
* maxwidth rule to geometry exceeding the given run-length (often used with
* 65nm and smaller feature size processes).
*
* ----------------------------------------------------------------------------
*/
int
drcSpacing(argc, argv)
int argc;
char *argv[];
{
char *layers1 = argv[1], *layers2;
char *adjacency;
int why;
TileTypeBitMask set1, set2, tmp1, tmp2;
PlaneMask pmask1, pmask2, pmaskA, pmaskB, ptest;
int wwidth, distance, plane, plane2, runlength;
bool widerule, multiplane = FALSE;
if ((argc == 7) && (!strcmp(argv[4], "corner_ok")))
return drcSpacing3(argc, argv);
widerule = (strncmp(argv[0], "wide", 4) == 0);
if (widerule)
{
wwidth = atoi(argv[2]);
if (argc == 8)
{
runlength = atoi(argv[3]);
layers2 = argv[4];
distance = atoi(argv[5]);
adjacency = argv[6];
why = drcWhyCreate(argv[7]);
}
else
{
layers2 = argv[3];
distance = atoi(argv[4]);
runlength = -1;
adjacency = argv[5];
why = drcWhyCreate(argv[6]);
}
/* TxPrintf("Info: DRCtech: widespacing rule for %s width %d:"
" spacing must be %d\n", layers1, wwidth, distance); */
}
else
{
layers2 = argv[2];
distance = atoi(argv[3]);
adjacency = argv[4];
wwidth = distance;
why = drcWhyCreate(argv[5]);
runlength = -1;
if (argc >= 7)
{
TechError("Unknown argument in spacing line.\n");
return(0);
}
}
/* Either list of types may contain independent types on different */
/* planes. However, if so, then we may not use touching_ok, and */
/* there are other restrictions. */
ptest = DBTechNoisyNameMask(layers1, &set1);
pmask1 = CoincidentPlanes(&set1, ptest);
if ((pmask1 == 0) && (ptest != 0))
{
pmask1 = ptest;
multiplane = TRUE;
for (plane = 0; plane < DBNumPlanes; plane++)
for (plane2 = 0; plane2 < DBNumPlanes; plane2++)
{
if (plane == plane2) continue;
if (PlaneMaskHasPlane(pmask1, plane) &&
PlaneMaskHasPlane(pmask1, plane2))
{
TTMaskAndMask3(&tmp1, &DBPlaneTypes[plane],
&DBPlaneTypes[plane2]);
TTMaskAndMask(&tmp1, &set1);
if (!TTMaskIsZero(&tmp1))
{
TechError("Types in first list must either be in"
" one plane or else types must not share"
" planes.\n");
return (0);
}
}
}
}
ptest = DBTechNoisyNameMask(layers2, &set2);
pmask2 = CoincidentPlanes(&set2, ptest);
if ((pmask2 == 0) && (ptest != 0))
{
pmask2 = ptest;
multiplane = TRUE;
for (plane = 0; plane < DBNumPlanes; plane++)
for (plane2 = 0; plane2 < DBNumPlanes; plane2++)
{
if (plane == plane2) continue;
if (PlaneMaskHasPlane(pmask2, plane) &&
PlaneMaskHasPlane(pmask2, plane2))
{
TTMaskAndMask3(&tmp1, &DBPlaneTypes[plane],
&DBPlaneTypes[plane2]);
TTMaskAndMask(&tmp1, &set2);
if (!TTMaskIsZero(&tmp1))
{
TechError("Types in second list must either be in"
" one plane or else types must not share"
" planes.\n");
return (0);
}
}
}
}
if (multiplane)
{
/* Loop over independent plane/layer combinations */
for (plane = 0; plane < DBNumPlanes; plane++)
for (plane2 = 0; plane2 < DBNumPlanes; plane2++)
{
if (PlaneMaskHasPlane(pmask1, plane) &&
PlaneMaskHasPlane(pmask2, plane2))
{
pmaskA = PlaneNumToMaskBit(plane);
pmaskB = PlaneNumToMaskBit(plane2);
TTMaskAndMask3(&tmp1, &set1, &DBPlaneTypes[plane]);
TTMaskAndMask3(&tmp2, &set2, &DBPlaneTypes[plane2]);
return drcMaskSpacing(&tmp1, &tmp2, pmaskA, pmaskB,
wwidth, distance, adjacency, why,
widerule, runlength, multiplane);
}
}
}
else
return drcMaskSpacing(&set1, &set2, pmask1, pmask2, wwidth,
distance, adjacency, why, widerule, runlength, multiplane);
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* drcEdge --
*
* Process a primitive edge rule.
* This is of the form:
*
* edge layers1 layers2 dist OKtypes cornerTypes cornerDist why [plane]
* or edge4way layers1 layers2 dist OKtypes cornerTypes cornerDist why [plane]
*
* e.g,
*
* edge poly,pmc s 1 diff poly,pmc "poly-diff separation must be 2"
*
* An "edge" rule is applied only down and to the left.
* An "edge4way" rule is applied in all four directions.
*
* Results:
* Returns greater of dist and cdist.
*
* Side effects:
* Updates the DRC technology variables.
*
* ----------------------------------------------------------------------------
*/
int
drcEdge(argc, argv)
int argc;
char *argv[];
{
char *layers1 = argv[1], *layers2 = argv[2];
int distance = atoi(argv[3]);
char *okTypes = argv[4], *cornerTypes = argv[5];
int cdist = atoi(argv[6]);
int why = drcWhyCreate(argv[7]);
bool fourway = (strcmp(argv[0], "edge4way") == 0);
TileTypeBitMask set1, set2, setC, setM;
DRCCookie *dp, *dpnew;
int plane, checkPlane, tmpPlane;
PlaneMask pMask1, pMaskM, pMaskC, pset, ptest;
TileType i, j;
/*
* Edge4way rules produce [j][i] entries as well as [i][j]
* ones, and check both corners rather than just one corner.
*/
ptest = DBTechNoisyNameMask(layers1, &set1);
pMask1 = CoincidentPlanes(&set1, ptest);
ptest = DBTechNoisyNameMask(layers2, &set2);
pMask1 &= CoincidentPlanes(&set2, ptest);
if (pMask1 == 0)
{
TechError("All edges in edge rule must lie in shared planes.\n");
return (0);
}
/* Give warning if types1 and types2 intersect */
if (TTMaskIntersect(&set1, &set2))
TechError("Warning: types1 and types2 have nonempty intersection. "
"DRC does not check edges with the same type on both "
"sides.\n");
ptest = DBTechNoisyNameMask(cornerTypes, &setC);
pMaskC = CoincidentPlanes(&setC, ptest);
if ((pMaskC & pMask1) == 0)
{
TechError("Corner types aren't in same plane as edges.\n");
return (0);
}
if (argc == 9)
tmpPlane = DBTechNoisyNamePlane(argv[8]);
/*
* OKtypes determine the checkPlane. If checkPlane exists, it should
* only be used to check against the plane of OKtypes.
*/
ptest = DBTechNoisyNameMask(okTypes, &setM);
pMaskM = CoincidentPlanes(&setM, ptest);
if (pMaskM == 0 || pMaskM == DBTypePlaneMaskTbl[TT_SPACE])
{
/* Technically it should be illegal to specify simply "space"
* in the types list for a DRC rule, as it is ambiguous.
* However, we will assume that the plane of the edge is
* intended. The "plane" argument may be used to do the
* qualification (for backwards compatibility); any other use
* gets a warning.
*/
if (TTMaskEqual(&DBSpaceBits, &setM))
{
if (argc == 9)
pMaskM = PlaneNumToMaskBit(tmpPlane);
else
{
TechError("OK types \"%s\" in more than one plane.\n"
" Assuming same plane (%s) as edge.\n",
okTypes, DBPlaneLongNameTbl[LowestMaskBit(pMask1)]);
pMaskM = pMask1;
}
}
/* The case okTypes="0" is explicitly allowed according */
/* to the manual, so we parse it accordingly. */
else if (!strcmp(okTypes, "0"))
pMaskM = pMask1;
else
{
TechError("All OK types must lie in one plane.\n");
return (0);
}
}
/* "plane" argument deprecated; kept for backward compatibility only */
if ((argc == 9) && (PlaneNumToMaskBit(tmpPlane) != pMaskM))
TechError("Ignoring bad plane argument.\n");
for (i = 0; i < DBNumTypes; i++)
{
for (j = 0; j < DBNumTypes; j++)
{
if (i == j) continue;
if ((pset = (DBTypesOnSamePlane(i, j) & pMask1)))
{
if (TTMaskHasType(&set1, i) && TTMaskHasType(&set2, j))
{
/* For fastest DRC, checkPlane and plane should be */
/* the same, if possible. */
if ((pset & pMaskM) != 0)
{
plane = LowestMaskBit(pset & pMaskM);
checkPlane = plane;
}
else
{
plane = LowestMaskBit(pset);
checkPlane = LowestMaskBit(pMaskM);
}
/* Find bucket preceding the new one we wish to insert */
dp = drcFindBucket(i, j, distance);
dpnew = (DRCCookie *)mallocMagic(sizeof (DRCCookie));
drcAssign(dpnew, distance, dp->drcc_next, &setM, &setC,
why, cdist, DRC_FORWARD, checkPlane, plane);
if (fourway) dpnew->drcc_flags |= DRC_BOTHCORNERS;
dp->drcc_next = dpnew;
if (!fourway) continue;
/* find bucket preceding new one we wish to insert */
dp = drcFindBucket(j, i, distance);
dpnew = (DRCCookie *)mallocMagic(sizeof (DRCCookie));
drcAssign(dpnew,distance,dp->drcc_next, &setM, &setC,
why, cdist, DRC_REVERSE, checkPlane, plane);
dpnew->drcc_flags |= DRC_BOTHCORNERS;
dp->drcc_next = dpnew;
}
}
}
}
return (MAX(distance, cdist));
}
/*
* ----------------------------------------------------------------------------
*
* drcOverhang --
*
* Process an overhang rule.
* This is of the form:
*
* overhang layers2 layers1 dist why
*
* indicating that layers2 must overhang layers1 by a distance of at least
* dist.
*
* This rule is equivalent to:
*
* edge4way layers1 space/p2|layers2 dist layers1|layers2 \
* space/p2|layers2 dist why
*
* ----------------------------------------------------------------------------
*/
int
drcOverhang(argc, argv)
int argc;
char *argv[];
{
char *layers2 = argv[1], *layers1 = argv[2];
int distance = atoi(argv[3]);
int why = drcWhyCreate(argv[4]);
TileTypeBitMask set1, set2, setM, setC, setN, set2inv;
DRCCookie *dp, *dpnew, *dptrig;
int plane, plane2;
TileType i, j;
PlaneMask pMask1, pMask2, pset, ptest;
ptest = DBTechNoisyNameMask(layers1, &set1);
pMask1 = CoincidentPlanes(&set1, ptest);
if (pMask1 == 0)
{
TechError("All layers in first set for \"overhang\" must be on "
"the same plane\n");
return (0);
}
TTMaskCom2(&setN, &set1);
ptest = DBTechNoisyNameMask(layers2, &set2);
pMask2 = CoincidentPlanes(&set2, ptest);
if (pMask2 == 0)
{
TechError("All layers in second set for \"overhang\" must be on "
"the same plane\n");
return (0);
}
TTMaskCom2(&set2inv, &set2);
/* Warn if types1 and types2 intersect */
if (TTMaskIntersect(&set1, &set2))
TechError("Warning: inside and outside types have nonempty intersection. "
"DRC does not check edges with the same type on both sides.\n");
/* SetM is the union of set1 and set2 */
TTMaskZero(&setM);
TTMaskSetMask3(&setM, &set1, &set2);
/* Add space to set2 */
TTMaskSetType(&set2, TT_SPACE);
/* SetC is the empty set */
TTMaskZero(&setC);
for (i = 0; i < DBNumTypes; i++)
{
for (j = 0; j < DBNumTypes; j++)
{
if (i == j) continue;
if ((pset = (DBTypesOnSamePlane(i, j) & pMask2)))
{
if ((pset & pMask1) != 0)
{
if (TTMaskHasType(&set1, i) && TTMaskHasType(&set2, j))
{
plane = LowestMaskBit(pset);
/* Find bucket preceding the new one we wish to insert */
dp = drcFindBucket(i, j, distance);
dpnew = (DRCCookie *)mallocMagic (sizeof (DRCCookie));
drcAssign(dpnew, distance, dp->drcc_next, &setM,
&setM, why, distance,
DRC_FORWARD | DRC_BOTHCORNERS,
plane, plane);
dp->drcc_next = dpnew;
/* find bucket preceding new one we wish to insert */
dp = drcFindBucket(j, i, distance);
dpnew = (DRCCookie *)mallocMagic(sizeof (DRCCookie));
drcAssign(dpnew, distance, dp->drcc_next,
&setM, &setM, why, distance,
DRC_REVERSE | DRC_BOTHCORNERS,
plane, plane);
dp->drcc_next = dpnew;
}
}
else /* Multi-plane overhang rule */
{
if (TTMaskHasType(&set2, i) && TTMaskHasType(&set2inv, j))
{
plane = LowestMaskBit(pset);
plane2 = LowestMaskBit(pMask1);
/* find bucket preceding the new one we wish to insert */
dp = drcFindBucket(i, j, distance);
dpnew = (DRCCookie *)mallocMagic(sizeof(DRCCookie));
drcAssign(dpnew, distance, dp->drcc_next, &set1, &set1, why,
distance, DRC_FORWARD, plane2, plane);
dptrig = (DRCCookie *)mallocMagic(sizeof(DRCCookie));
drcAssign(dptrig, 1, dpnew, &setN, &setC, why,
0, DRC_FORWARD | DRC_TRIGGER, plane2, plane);
dp->drcc_next = dptrig;
dp = drcFindBucket(j, i, distance);
dpnew = (DRCCookie *)mallocMagic(sizeof(DRCCookie));
drcAssign(dpnew, distance, dp->drcc_next, &set1, &set1, why,
distance, DRC_REVERSE, plane2, plane);
dptrig = (DRCCookie *)mallocMagic(sizeof(DRCCookie));
drcAssign(dptrig, 1, dpnew, &setN, &setC, why,
0, DRC_REVERSE | DRC_TRIGGER, plane2, plane);
dp->drcc_next = dptrig;
}
}
}
}
}
return distance;
}
/*
* ----------------------------------------------------------------------------
*
* drcRectOnly --
*
* Process a rectangle-only rule. This rule prohibits non-rectangular
* geometry, and is used especially for contacts, as the "squares" operator
* in the CIF/GDS output generator can't handle non-rectangular areas.
* The rule is of the form:
*
* rect_only layers why
*
* and is equivalent to:
*
* edge4way layers ~(layers)/plane 1 ~(layers)/plane (all_layers)/plane 1
*
* The rect_only rule avoids the above contrived construction, especially the
* requirement of specifying "all_layers" as something like (~(x),x)/p, a sure-
* fire obfuscation.
*
* ----------------------------------------------------------------------------
*/
int
drcRectOnly(argc, argv)
int argc;
char *argv[];
{
char *layers = argv[1];
int why = drcWhyCreate(argv[2]);
TileTypeBitMask set1, set2, setC;
PlaneMask pmask, pset, ptest;
DRCCookie *dp, *dpnew;
int plane;
TileType i, j;
ptest = DBTechNoisyNameMask(layers, &set1);
pmask = CoincidentPlanes(&set1, ptest);
if (pmask == 0)
{
TechError("All types for \"rect_only\" must be on the same plane.\n");
return (0);
}
/* set2 is the inverse of set1 */
TTMaskCom2(&set2, &set1);
for (i = 0; i < DBNumTypes; i++)
{
for (j = 0; j < DBNumTypes; j++)
{
if (i == j) continue;
if ((pset = (DBTypesOnSamePlane(i, j) & pmask)))
{
if (TTMaskHasType(&set1, i) && TTMaskHasType(&set2, j))
{
plane = LowestMaskBit(pset);
/* setC = all types in plane */
TTMaskZero(&setC);
TTMaskSetMask(&setC, &DBPlaneTypes[plane]);
/* Find bucket preceding the new one we wish to insert */
dp = drcFindBucket(i, j, 1);
dpnew = (DRCCookie *)mallocMagic(sizeof (DRCCookie));
drcAssign(dpnew, 1, dp->drcc_next, &set2, &setC, why, 1,
DRC_FORWARD | DRC_BOTHCORNERS, plane, plane);
dp->drcc_next = dpnew;
/* find bucket preceding new one we wish to insert */
dp = drcFindBucket(j, i, 1);
dpnew = (DRCCookie *)mallocMagic(sizeof (DRCCookie));
drcAssign(dpnew,1,dp->drcc_next, &set2, &setC, why, 1,
DRC_REVERSE | DRC_BOTHCORNERS, plane, plane);
dp->drcc_next = dpnew;
}
}
}
}
return 1;
}
/*
* ----------------------------------------------------------------------------
*
* drcSurround --
*
* Process a surround rule.
* This is of the form:
*
* surround layers1 layers2 dist [dist2] presence why
*
* indicating that layers2 must surround layers1 by at least distance
* dist in all directions.
*
* This rule is equivalent to:
*
* edge4way ~(layers2)/plane2 layers2 dist ~(layers1)/plane1 \
* layers2 dist why
*
* When presence=absence_illegal, the following additional rule is needed:
*
* edge4way layers1 ~(layers2)/plane1 dist NULL ~(layers2)/plane1 \
* dist why
*
* Extension added July 12, 2014: For via rules where an asymmetric
* surround is allowed, with a smaller surround allowed on two sides if
* the remaining two sides have a larger surround. This can be implemented
* with a trigger rule, and is specified by the syntax above with "presence"
* being "directional". Note that the rule expresses that the overhang rule
* requires the presence of the material on one side of a corner. If the
* other side has a non-zero minimum surround requirement, then it should
* be implemented with an additional (absence_illegal) surround rule.
* Otherwise, any width of material less than "dist" on one side of a
* corner will trigger the rule requiring at least "dist" width of the same
* material on the other side of the corner.
*
* Extension added November 30, 2022: Similar to the asymmetric via
* extension rule above are rules of the type "extension on one side
* must be >= dist if the extension on the adjacent side is < dist2",
* for which the asymmetric rule above is the limiting case dist = dist2.
* dist2 is optional and is taken to be equal to dist if not present,
* which is the original form of the "directional" rule.
*
* ----------------------------------------------------------------------------
*/
int
drcSurround(argc, argv)
int argc;
char *argv[];
{
char *layers1 = argv[1], *layers2 = argv[2], *endptr;
int distance = atoi(argv[3]);
int distance2;
char *presence = argv[4];
int why = drcWhyCreate(argv[5]);
TileTypeBitMask set1, set2, setM, invM, setR;
DRCCookie *dp, *dpnew, *dptrig;
int plane1, plane2;
PlaneMask pmask, pmask2, pset, ptest;
TileType i, j;
bool isExact = FALSE;
bool isDirectional = FALSE;
if (argc == 7)
{
distance2 = strtol(argv[4], &endptr, 10);
if ((endptr != NULL) && (*endptr != '\0'))
{
TechError("Incorrect arguments in directional \"surround\" rule\n");
return (0);
}
presence = argv[5];
why = drcWhyCreate(argv[6]);
}
else
{
distance2 = distance;
presence = argv[4];
why = drcWhyCreate(argv[5]);
}
ptest = DBTechNoisyNameMask(layers1, &setM);
pmask = CoincidentPlanes(&setM, ptest);
if (pmask == 0)
{
TechError("Inside types in \"surround\" must be on the same plane\n");
return (0);
}
ptest = DBTechNoisyNameMask(layers2, &set2);
pmask2 = CoincidentPlanes(&set2, ptest);
if (pmask2 == 0)
{
TechError("Outside types in \"surround\" must be on the same plane\n");
return (0);
}
/* "exact_width" rule implemented 9/16/10. This enforces an exact */
/* surround distance. "absence_illegal" is implied. */
if (!strncmp(presence, "exact_", 6)) isExact = TRUE;
else if (!strncmp(presence, "directional", 11))
{
isDirectional = TRUE;
/* Combined mask */
TTMaskZero(&setR);
TTMaskSetMask(&setR, &setM);
TTMaskSetMask(&setR, &set2);
}
/* invert setM */
TTMaskCom2(&invM, &setM);
/* set1 is the inverse of set2 */
TTMaskCom2(&set1, &set2);
for (i = 0; i < DBNumTypes; i++)
{
for (j = 0; j < DBNumTypes; j++)
{
if (i == j) continue; /* Ignore false edges */
if (isDirectional)
{
if ((pset = (DBTypesOnSamePlane(i, j) & pmask)))
{
/* Directional surround is done entirely differently */
if (TTMaskHasType(&setM, i) && TTMaskHasType(&invM, j))
{
plane1 = LowestMaskBit(pset);
plane2 = LowestMaskBit(pmask2);
/* Find bucket preceding the new one we wish to insert */
dp = drcFindBucket(i, j, distance);
dpnew = (DRCCookie *)mallocMagic(sizeof(DRCCookie));
/* Insert triggered rule */
drcAssign(dpnew, distance, dp->drcc_next, &setR,
&DBAllTypeBits,
why, distance,
DRC_REVERSE | DRC_BOTHCORNERS,
plane2, plane1);
dptrig = (DRCCookie *)mallocMagic(sizeof(DRCCookie));
drcAssign(dptrig, distance2, dpnew, &set2,
&DBZeroTypeBits, why, 0,
DRC_FORWARD | DRC_TRIGGER,
plane2, plane1);
dp->drcc_next = dptrig;
/* And the other direction. . . */
dp = drcFindBucket(j, i, distance);
dpnew = (DRCCookie *)mallocMagic(sizeof(DRCCookie));
/* Insert triggered rule */
drcAssign(dpnew, distance, dp->drcc_next, &setR,
&DBAllTypeBits,
why, distance,
DRC_FORWARD | DRC_BOTHCORNERS,
plane2, plane1);
dptrig = (DRCCookie *)mallocMagic(sizeof(DRCCookie));
drcAssign(dptrig, distance2, dpnew, &set2,
&DBZeroTypeBits, why, 0,
DRC_REVERSE | DRC_TRIGGER,
plane2, plane1);
dp->drcc_next = dptrig;
}
}
}
else
{
if ((pset = (DBTypesOnSamePlane(i, j) & pmask2)))
{
if (TTMaskHasType(&set1, i) && TTMaskHasType(&set2, j))
{
plane1 = LowestMaskBit(pmask);
plane2 = LowestMaskBit(pset);
/* Find bucket preceding the new one we wish to insert */
dp = drcFindBucket(i, j, distance);
dpnew = (DRCCookie *)mallocMagic(sizeof(DRCCookie));
drcAssign(dpnew, distance, dp->drcc_next, &invM, &set2,
why, distance,
DRC_FORWARD | DRC_BOTHCORNERS,
plane1, plane2);
dp->drcc_next = dpnew;
/* find bucket preceding new one we wish to insert */
dp = drcFindBucket(j, i, distance);
dpnew = (DRCCookie *)mallocMagic(sizeof(DRCCookie));
drcAssign(dpnew, distance, dp->drcc_next, &invM, &set2,
why, distance,
DRC_REVERSE | DRC_BOTHCORNERS,
plane1, plane2);
dp->drcc_next = dpnew;
}
}
}
}
}
if (isExact)
{
for (i = 0; i < DBNumTypes; i++)
{
for (j = 0; j < DBNumTypes; j++)
{
if (i == j) continue; /* Ignore false edges */
if ((pset = (DBTypesOnSamePlane(i, j) & pmask)))
{
if (TTMaskHasType(&setM, i) && TTMaskHasType(&set2, j))
{
plane1 = LowestMaskBit(pset);
/* Find bucket preceding the new one we wish to insert */
dp = drcFindBucket(i, j, distance);
dpnew = (DRCCookie *)mallocMagic(sizeof(DRCCookie));
drcAssign(dpnew, distance, dp->drcc_next, &set1, &set2,
why, distance,
DRC_FORWARD | DRC_BOTHCORNERS | DRC_OUTSIDE,
plane1, plane1);
dp->drcc_next = dpnew;
/* find bucket preceding new one we wish to insert */
dp = drcFindBucket(j, i, distance);
dpnew = (DRCCookie *)mallocMagic(sizeof(DRCCookie));
drcAssign(dpnew, distance, dp->drcc_next, &set1, &set2,
why, distance,
DRC_REVERSE | DRC_BOTHCORNERS | DRC_OUTSIDE,
plane1, plane1);
dp->drcc_next = dpnew;
}
}
}
}
}
if ((!isExact) && strcmp(presence, "absence_illegal")) return distance;
/* Add an extra rule when presence of the surrounding */
/* layer is required. Rule is different if planes match. */
if ((pset = pmask & pmask2))
{
TTMaskZero(&invM);
TTMaskSetMask(&invM, &setM);
TTMaskSetMask(&invM, &set2);
TTMaskCom(&invM);
TTMaskZero(&set1);
for (i = 0; i < DBNumTypes; i++)
for (j = 0; j < DBNumTypes; j++)
{
if (i == j) continue;
if ((pset = (DBTypesOnSamePlane(i, j) & pmask & pmask2)))
{
plane1 = LowestMaskBit(pset);
if (TTMaskHasType(&setM, i) && TTMaskHasType(&invM, j))
{
/* Find bucket preceding the new one we wish to insert */
dp = drcFindBucket(i, j, distance);
dpnew = (DRCCookie *) mallocMagic((unsigned)
(sizeof (DRCCookie)));
drcAssign(dpnew, distance, dp->drcc_next,
&set1, &invM, why, distance,
DRC_FORWARD | DRC_BOTHCORNERS,
plane1, plane1);
dp->drcc_next = dpnew;
/* find bucket preceding new one we wish to insert */
dp = drcFindBucket(j, i, distance);
dpnew = (DRCCookie *) mallocMagic((unsigned)
(sizeof (DRCCookie)));
drcAssign(dpnew,distance,dp->drcc_next,
&set1, &invM, why, distance,
DRC_REVERSE | DRC_BOTHCORNERS,
plane1, plane1);
dp->drcc_next = dpnew;
}
}
}
}
else
{
for (i = 0; i < DBNumTypes; i++)
for (j = 0; j < DBNumTypes; j++)
{
if (i == j) continue;
if ((pset = (DBTypesOnSamePlane(i, j) & pmask)))
{
if (TTMaskHasType(&setM, i) && TTMaskHasType(&invM, j))
{
plane1 = LowestMaskBit(pset);
plane2 = LowestMaskBit(pmask2);
/* Find bucket preceding the new one we wish to insert */
dp = drcFindBucket(i, j, distance);
dpnew = (DRCCookie *) mallocMagic((unsigned)
(sizeof (DRCCookie)));
drcAssign(dpnew, distance, dp->drcc_next,
&set2, &invM, why, distance,
DRC_FORWARD | DRC_BOTHCORNERS,
plane2, plane1);
dp->drcc_next = dpnew;
/* find bucket preceding new one we wish to insert */
dp = drcFindBucket(j, i, distance);
dpnew = (DRCCookie *) mallocMagic((unsigned)
(sizeof (DRCCookie)));
drcAssign(dpnew,distance,dp->drcc_next,
&set2, &invM, why, distance,
DRC_REVERSE | DRC_BOTHCORNERS,
plane2, plane1);
dp->drcc_next = dpnew;
}
}
}
}
return distance;
}
/*
* ----------------------------------------------------------------------------
*
* drcNoOverlap --
*
* Process a no-overlap rule.
* This is of the form:
*
* no_overlap layers1 layers2
*
* e.g,
*
* no_overlap poly m2contact
*
* Results:
* Returns 0.
*
* Side effects:
* Updates the DRC technology variables.
*
* ----------------------------------------------------------------------------
*/
int
drcNoOverlap(argc, argv)
int argc;
char *argv[];
{
char *layers1 = argv[1], *layers2 = argv[2];
TileTypeBitMask set1, set2;
TileType i, j;
int plane;
/*
* Grab up two sets of tile types, and make sure that if
* any type from one set is painted over any type from the
* other, then an error results.
*/
DBTechNoisyNameMask(layers1, &set1);
DBTechNoisyNameMask(layers2, &set2);
for (i = 0; i < DBNumTypes; i++)
for (j = 0; j < DBNumTypes; j++)
if (TTMaskHasType(&set1, i) && TTMaskHasType(&set2, j))
for (plane = 0; plane < DBNumPlanes; plane++)
{
DRCCurStyle->DRCPaintTable[plane][j][i] = TT_ERROR_S;
DRCCurStyle->DRCPaintTable[plane][i][j] = TT_ERROR_S;
}
return (0);
}
/*
* ----------------------------------------------------------------------------
*
* drcExactOverlap --
*
* Process an exact overlap
* This is of the form:
*
* exact_overlap layers
*
* e.g,
*
* exact_overlap pmc,dmc
*
* Results:
* Returns 0.
*
* Side effects:
* Updates DRCExactOverlapTypes.
*
* ----------------------------------------------------------------------------
*/
int
drcExactOverlap(argc, argv)
int argc;
char *argv[];
{
char *layers = argv[1];
TileTypeBitMask set;
/*
* Grab up a bunch of tile types, and remember these: tiles
* of these types cannot overlap themselves in different cells
* unless they overlap exactly.
*/
DBTechNoisyNameMask(layers, &set);
TTMaskSetMask(&DRCCurStyle->DRCExactOverlapTypes, &set);
return (0);
}
/*
* ----------------------------------------------------------------------------
*
* drcRectangle --
*
* Process a rectangle rule. This is of the form:
*
* rectangle layers maxwidth [even|odd|any] why
*
* The rule checks to make sure that the region is rectangular and that the
* width and length are even or odd, as specified. These two criteria ensure
* that the squares rule of the cifout section can properly produce via
* holes without misaligning them between cells and without putting the via
* holes off grid. The maxwidth is required to make the extent of this rule
* a finite size, so that we can set the DRChalo to something finite.
*
* Results:
* maxwidth
*
* Side effects:
* Updates the DRC technology variables.
*
* ----------------------------------------------------------------------------
*/
int
drcRectangle(argc, argv)
int argc;
char *argv[];
{
char *layers = argv[1];
int why = drcWhyCreate(argv[4]);
TileTypeBitMask types, nottypes;
int maxwidth;
static const char * const drcRectOpt[4] = {"any", "even", "odd", 0};
int i, j, even, plane;
PlaneMask pMask, pset, ptest;
/* parse arguments */
ptest = DBTechNoisyNameMask(layers, &types);
pMask = CoincidentPlanes(&types, ptest);
if (pMask == 0) {
TechError("Layers in rectangle rule must lie in a single plane.");
return 0;
}
TTMaskCom2(&nottypes, &types);
if (sscanf(argv[2], "%d", &maxwidth) != 1) {
TechError("bad maxwidth in rectangle rule");
return 0;
}
even = Lookup(argv[3], drcRectOpt);
if (even < 0) {
TechError("bad [even|odd|any] selection in rectangle rule");
return 0;
}
even--; /* -1: any, 0: even, 1: odd */
/* Install 2 edge rules: one that checks rectangle-ness, and one that
* checks size
*/
for (i = 0; i < DBNumTypes; i++)
{
for (j = 0; j < DBNumTypes; j++)
{
if (i == j) continue;
if ((pset = (DBTypesOnSamePlane(i, j) & pMask)))
{
if (TTMaskHasType(&types, i) && TTMaskHasType(&nottypes, j))
{
DRCCookie *dp, *dpnew;
plane = LowestMaskBit(pset);
/*
* A rule that checks rectangle-ness.
* left: oktypes, right: other types
* This rule needs to be checked in all 4 directions
*/
int distance = 1;
/* Find bucket preceding the new one we wish to insert */
dp = drcFindBucket(i, j, distance);
dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie)));
drcAssign(dpnew, distance, dp->drcc_next,
&nottypes, &DBAllTypeBits, why, distance,
DRC_FORWARD, plane, plane);
dp->drcc_next = dpnew;
/* Find bucket preceding the new one we wish to insert */
dp = drcFindBucket(j, i, distance); /* note: j, i not i, j */
dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie)));
drcAssign(dpnew, distance, dp->drcc_next,
&nottypes, &DBAllTypeBits, why, distance,
DRC_REVERSE, plane, plane);
dp->drcc_next = dpnew;
if (maxwidth > 0) {
/*
* A rule that checks size.
* left: other types, right: oktypes
*/
distance = maxwidth;
/* note: j, i not i, j */
for (dp = DRCCurStyle->DRCRulesTbl[j][i];
dp->drcc_next != (DRCCookie *) NULL &&
dp->drcc_next->drcc_dist < distance;
dp = dp->drcc_next); /* null body */
dpnew = (DRCCookie *)mallocMagic(sizeof (DRCCookie));
drcAssign(dpnew, distance, dp->drcc_next,
&types, &DBZeroTypeBits, why, even,
DRC_RECTSIZE, plane, plane);
dp->drcc_next = dpnew;
}
}
}
}
}
return maxwidth;
}
/*
* ----------------------------------------------------------------------------
*
* drcOption --
*
* Process an option declaration
* This is of the form:
*
* option option_name [...]
*
* e.g,
*
* option wide-width-inclusive
*
* Results:
* Returns 0.
*
* Side effects:
* Updates DRCFlags.
*
* ----------------------------------------------------------------------------
*/
int
drcOption(argc, argv)
int argc;
char *argv[];
{
int i;
if (DRCCurStyle == NULL) return 0;
/* Assume space-separated list of options. Flag an error on any */
/* option name not recognized. */
for (i = 1; i < argc; i++)
{
if (!strcmp(argv[i], "wide-width-noninclusive"))
DRCCurStyle->DRCFlags |= DRC_FLAGS_WIDEWIDTH_NONINCLUSIVE;
else
{
TechError("Unrecognized DRC option \"%s\" (ignored).\n", argv[i]);
}
}
return (0);
}
/*
* ----------------------------------------------------------------------------
*
* drcStepSize --
*
* Process a declaration of the step size.
* This is of the form:
*
* stepsize step_size
*
* e.g,
*
* stepsize 1000
*
* Results:
* Returns 0.
*
* Side effects:
* Updates DRCStepSize.
*
* ----------------------------------------------------------------------------
*/
int
drcStepSize(argc, argv)
int argc;
char *argv[];
{
if (DRCCurStyle == NULL) return 0;
DRCCurStyle->DRCStepSize = atoi(argv[1]);
if (DRCCurStyle->DRCStepSize <= 0)
{
TechError("Step size must be a positive integer.\n");
DRCCurStyle->DRCStepSize = 0;
}
else if (DRCCurStyle->DRCStepSize < 16)
{
TechError("Warning: abnormally small DRC step size (%d)\n",
DRCCurStyle->DRCStepSize);
}
return (0);
}
/*
* ----------------------------------------------------------------------------
*
* DRCTechFinal --
*
* Called after all lines of the drc section in the technology file have been
* read. Ensures that a valid style is in effect, and then calls
* drcTechFinalStyle().
*
* Results:
* None.
*
* Side effects:
* See drcTechFinalStyle();
*
* ----------------------------------------------------------------------------
*/
void
DRCTechFinal()
{
DRCStyle *ds;
/* Create a "default" style if there isn't one */
if (DRCStyleList == NULL)
{
DRCStyleList = (DRCKeep *)mallocMagic(sizeof(DRCKeep));
DRCStyleList->ds_next = NULL;
DRCStyleList->ds_name = StrDup((char **)NULL, "default");
drcTechNewStyle();
DRCCurStyle->ds_name = DRCStyleList->ds_name;
DRCCurStyle->ds_status = TECH_LOADED;
}
drcTechFinalStyle(DRCCurStyle);
}
/*
* ----------------------------------------------------------------------------
* drcScaleDown ---
*
* DRC distances may be specified with a scale factor so that physically
* based rules can be recorded, but the rules used will be rounded (up)
* to the nearest lambda. The fractional part of the true distance in
* lambda is saved, so that the original value can be recovered when
* the magic grid is rescaled.
*
* Results:
* None.
*
* Side Effects:
* Scales all the DRC distances by dividing by the DRC scale factor.
* ----------------------------------------------------------------------------
*/
void
drcScaleDown(style, scalefactor)
DRCStyle *style;
int scalefactor;
{
TileType i, j;
DRCCookie *dp;
int dist;
if (scalefactor > 1)
{
for (i = 0; i < TT_MAXTYPES; i++)
for (j = 0; j < TT_MAXTYPES; j++)
for (dp = style->DRCRulesTbl[i][j]; dp != NULL; dp = dp->drcc_next)
{
if (dp->drcc_dist > 0)
{
dist = dp->drcc_dist;
dp->drcc_dist /= scalefactor;
if ((dp->drcc_mod = (unsigned char)(dist % scalefactor)) != 0)
if (!(dp->drcc_flags & DRC_MAXWIDTH))
dp->drcc_dist++;
}
if (dp->drcc_cdist > 0)
{
int cmod;
int locscale = scalefactor;
if (dp->drcc_flags & DRC_AREA)
locscale *= scalefactor;
dist = dp->drcc_cdist;
dp->drcc_cdist /= locscale;
/* Save the amount by which the distance needs to be
* corrected when multiplied back to the original
* scale. For area, the modulus will always be
* a multiple of the scalefactor, so it can be
* divided by the scalefactor so it still fits in an
* unsigned char.
*/
if ((cmod = (dist % locscale)) != 0)
{
dp->drcc_cdist++;
if (dp->drcc_flags & DRC_AREA)
dp->drcc_cmod = (unsigned char)(cmod / scalefactor);
else
dp->drcc_cmod = (unsigned char)cmod;
}
}
}
}
}
/*
* ----------------------------------------------------------------------------
* drcScaleUp ---
*
* Recovers the original (pre-scaled) values for drcc_dist and
* drcc_cdist in the DRC cookies.
*
* Results:
* None.
*
* Side Effects:
* Scales all the DRC distances by multiplying by the DRC scale factor.
* ----------------------------------------------------------------------------
*/
void
drcScaleUp(style, scalefactor)
DRCStyle *style;
int scalefactor;
{
TileType i, j;
DRCCookie *dp;
int dist;
if (style == NULL) return;
if (scalefactor > 1)
{
for (i = 0; i < TT_MAXTYPES; i++)
for (j = 0; j < TT_MAXTYPES; j++)
for (dp = style->DRCRulesTbl[i][j]; dp != NULL; dp = dp->drcc_next)
{
if (dp->drcc_dist > 0)
{
dist = dp->drcc_dist;
if (dp->drcc_mod != 0)
if (!(dp->drcc_flags & DRC_MAXWIDTH))
dp->drcc_dist--;
dp->drcc_dist *= scalefactor;
dp->drcc_dist += (short)dp->drcc_mod;
dp->drcc_mod = 0;
}
if (dp->drcc_cdist > 0)
{
dist = dp->drcc_cdist;
if (dp->drcc_cmod != 0)
dp->drcc_cdist--;
dp->drcc_cdist *= scalefactor;
if (dp->drcc_flags & DRC_AREA)
{
dp->drcc_cdist *= scalefactor;
/* See note above on how cmod is divided by the
* scalefactor for area values.
*/
dp->drcc_cdist += (short)(dp->drcc_cmod * scalefactor);
}
else
dp->drcc_cdist += (short)dp->drcc_cmod;
dp->drcc_cmod = 0;
}
}
}
}
/*
* ----------------------------------------------------------------------------
*
* drcTechFinalStyle --
*
* Called after all lines of the drc section in the technology file have been
* read. The preliminary DRC Rules Table is pruned by removing rules covered
* by other (longer distance) rules, and by removing the dummy rule at the
* front of each list. Where edges are completely illegal, the rule list is
* pruned to a single rule.
*
* Results:
* None.
*
* Side effects:
* May remove DRCCookies from the linked lists of the DRCRulesTbl.
*
* ----------------------------------------------------------------------------
*/
void
drcTechFinalStyle(style)
DRCStyle *style;
{
TileTypeBitMask tmpMask, nextMask;
DRCCookie *dummy, *dp, *next, *dptrig;
DRCCookie **dpp, **dp2back;
TileType i, j;
/* Done with DRCWhyErrorTable */
HashKill(&DRCWhyErrorTable);
/* If the scale factor is not 1, then divide all distances by */
/* the scale factor, take the ceiling, and save the (negative) */
/* remainder. */
drcScaleUp(style, style->DRCScaleFactorD);
drcScaleDown(style, style->DRCScaleFactorN);
/* Scale DRCTechHalo to match */
DRCTechHalo *= style->DRCScaleFactorD;
DRCTechHalo /= style->DRCScaleFactorN;
/* Set maximum halo */
style->DRCTechHalo = DRCTechHalo;
/* A reasonable chunk size for design-rule checking is about
* 16 times the maximum design-rule interaction distance. This
* results in a halo overhead of about 27%. If there's no DRC
* information at all (TechHalo is zero), just pick any size.
* (Update 1/13/09: "any size" needs a bit of modification,
* because 64 will be way too small for a layout with a small
* scalefactor. Assuming that the CIF output style is valid,
* use its scalefactor to adjust the step size).
*/
if (style->DRCStepSize == 0)
{
if (style->DRCTechHalo == 0)
{
if (CIFCurStyle != NULL)
style->DRCStepSize = 6400 / CIFCurStyle->cs_scaleFactor;
else
style->DRCStepSize = 64;
}
else
style->DRCStepSize = 16 * style->DRCTechHalo;
}
DRCStepSize = style->DRCStepSize;
/* Remove dummy buckets */
for (i = 0; i < TT_MAXTYPES; i++)
{
for (j = 0; j < TT_MAXTYPES; j++)
{
dpp = &(style->DRCRulesTbl [i][j]);
dummy = *dpp;
*dpp = dummy->drcc_next;
freeMagic((char *) dummy);
}
}
drcCifFinal();
if (!DRCRuleOptimization) return;
/* Check for edges that are completely illegal. Where this is the
* case, eliminate all of the edge's rules except one.
*/
for (i = 0; i < DBNumTypes; i++)
{
for (j = 0; j < DBNumTypes; j++)
{
DRCCookie *keep = NULL, *dptest, *dptemp, *dpnew;
for (dp = style->DRCRulesTbl[i][j]; dp != NULL; dp = dp->drcc_next)
{
if (dp->drcc_flags & (DRC_NONSTANDARD | DRC_OUTSIDE)) continue;
if (dp->drcc_flags & DRC_REVERSE)
{
if ((i == TT_SPACE) || TTMaskHasType(&dp->drcc_mask, i)) continue;
}
else
{
if ((j == TT_SPACE) || TTMaskHasType(&dp->drcc_mask, j)) continue;
}
/* Rules where okTypes are in a different plane don't count, */
/* unless i or j also appear in the checked plane. */
if (dp->drcc_plane != dp->drcc_edgeplane)
{
if (dp->drcc_flags & DRC_REVERSE)
{
if ((i == TT_SPACE) || !DBTypeOnPlane(i, dp->drcc_plane))
continue;
}
else
{
if ((j == TT_SPACE) || !DBTypeOnPlane(j, dp->drcc_plane))
continue;
}
}
// if (DBIsContact(i) || DBIsContact(j) || i == TT_SPACE ||
// j == TT_SPACE) continue;
dpnew = NULL;
keep = dp;
/* This edge is illegal. Throw away all rules except the one
* needed that is always violated.
*/
dptest = style->DRCRulesTbl[i][j];
while (dptest != NULL)
{
dptemp = dptest->drcc_next;
if ((dptest == keep) || (dptest->drcc_edgeplane !=
keep->drcc_edgeplane))
{
dptest->drcc_next = NULL;
if (dpnew == NULL)
style->DRCRulesTbl[i][j] = dptest;
else
dpnew->drcc_next = dptest;
dpnew = dptest;
/* "keep" can't be a trigger rule! */
if (dptest == keep)
keep->drcc_flags &= ~DRC_TRIGGER;
}
else
{
freeMagic((char *)dptest);
drcRulesOptimized++;
}
dptest = dptemp;
}
}
/* TxPrintf("Edge %s-%s is illegal.\n", DBTypeShortName(i),
DBTypeShortName(j));
*/
}
}
/*
* Remove any rule A "covered" by another rule B, i.e.,
* B's distance >= A's distance,
* B's corner distance >= A's corner distance,
* B's RHS type mask is a subset of A's RHS type mask, and
* B's corner mask == A's corner mask
* B's check plane == A's check plane
* either both A and B or neither is a REVERSE direction rule
* if A is BOTHCORNERS then B must be, too
*/
for (i = 0; i < DBNumTypes; i++)
{
for (j = 0; j < DBNumTypes; j++)
{
for (dp = style->DRCRulesTbl[i][j]; dp != NULL; dp = dp->drcc_next)
{
/* Don't optimize on trigger rules; optimize on the */
/* rule that gets triggered. */
if (dp->drcc_flags & DRC_TRIGGER)
{
dptrig = dp;
dp = dp->drcc_next;
}
else
dptrig = NULL;
/*
* Check following buckets to see if any is a superset.
*/
if (dp->drcc_flags & DRC_NONSTANDARD) continue;
for (next = dp->drcc_next; next != NULL;
next = next->drcc_next)
{
if (next->drcc_flags & DRC_TRIGGER)
{
/* A triggered rule cannot be considered */
/* a superset of a non-triggered rule or */
/* a rule with a different trigger, so */
/* we skip all triggered rules and their */
/* triggering rule. */
next = next->drcc_next;
continue;
}
tmpMask = nextMask = next->drcc_mask;
TTMaskAndMask(&tmpMask, &dp->drcc_mask);
if (!TTMaskEqual(&tmpMask, &nextMask)) continue;
if (!TTMaskEqual(&dp->drcc_corner, &next->drcc_corner))
continue;
if (dp->drcc_dist > next->drcc_dist) continue;
if (dp->drcc_cdist > next->drcc_cdist) continue;
if (dp->drcc_plane != next->drcc_plane) continue;
if (dp->drcc_flags & DRC_REVERSE)
{
if (!(next->drcc_flags & DRC_REVERSE)) continue;
}
else if (next->drcc_flags & DRC_REVERSE) continue;
if ((next->drcc_flags & DRC_BOTHCORNERS)
&& (dp->drcc_flags & DRC_BOTHCORNERS) == 0)
continue;
if (next->drcc_flags & DRC_NONSTANDARD) continue;
if (dp->drcc_dist == next->drcc_dist)
{
if ((next->drcc_flags & DRC_OUTSIDE) &&
!(dp->drcc_flags & DRC_OUTSIDE)) continue;
if (!(next->drcc_flags & DRC_OUTSIDE) &&
(dp->drcc_flags & DRC_OUTSIDE)) continue;
}
if (dp->drcc_flags & DRC_SPLITTILE)
{
if (!(next->drcc_flags & DRC_SPLITTILE)) continue;
}
else if (next->drcc_flags & DRC_SPLITTILE) continue;
break;
}
if (next == NULL) continue;
/* "dp" is a subset of "next". Eliminate it. */
/* For triggered rules, eliminate both the rule */
/* and the trigger. */
if (dptrig != NULL) dp = dptrig;
/* TxPrintf("For edge %s-%s, \"%s\" covers \"%s\"\n",
DBTypeShortName(i), DBTypeShortName(j),
DRCCurStyle->DRCWhyList[next->drcc_tag],
DRCCurStyle->DRCWhyList[dp->drcc_tag]);
*/
dp2back = &(style->DRCRulesTbl[i][j]);
while (*dp2back != dp)
dp2back = &(*dp2back)->drcc_next;
/* Trigger rules */
if (dptrig != NULL)
{
dptrig = dp->drcc_next;
freeMagic((char *)dp->drcc_next);
*dp2back = dp->drcc_next->drcc_next;
/* Replace this entry so on the next cycle */
/* dp will be the next rule. This works */
/* even though dp is free'd (below), due to */
/* the one-delayed free mechanism. */
dp->drcc_next = *dp2back;
}
else
*dp2back = dp->drcc_next;
freeMagic((char *) dp);
drcRulesOptimized += 1;
}
}
}
}
/*
* ----------------------------------------------------------------------------
*
* DRCTechRuleStats --
*
* Print out some statistics about the design rule database.
*
* Results:
* None.
*
* Side effects:
* A bunch of stuff gets printed on the terminal.
*
* ----------------------------------------------------------------------------
*/
#define MAXBIN 10
void
DRCTechRuleStats()
{
int counts[MAXBIN+1];
int edgeRules, overflow;
int i, j;
DRCCookie *dp;
/* Count up the total number of edge rules, and histogram them
* by the number of rules per edge.
*/
edgeRules = 0;
overflow = 0;
for (i=0; i<=MAXBIN; i++) counts[i] = 0;
for (i=0; i<DBNumTypes; i++)
for (j=0; j<DBNumTypes; j++)
{
int thisCount = 0;
for (dp = DRCCurStyle->DRCRulesTbl[i][j]; dp != NULL; dp = dp->drcc_next)
thisCount++;
edgeRules += thisCount;
if (!DBTypesOnSamePlane(i, j)) continue;
if (thisCount <= MAXBIN) counts[thisCount] += 1;
else overflow += 1;
}
/* Print out the results. */
TxPrintf("Total number of rules specifed in tech file: %d\n",
drcRulesSpecified);
TxPrintf("Edge rules optimized away: %d\n", drcRulesOptimized);
TxPrintf("Edge rules left in database: %d\n", edgeRules);
TxPrintf("Histogram of # edges vs. rules per edge:\n");
for (i=0; i<=MAXBIN; i++)
{
TxPrintf(" %2d rules/edge: %d.\n", i, counts[i]);
}
TxPrintf(" >%2d rules/edge: %d.\n", MAXBIN, overflow);
}
/*
* ----------------------------------------------------------------------------
*
* DRCTechScale --
*
* Multiply all DRC rule widths and spacings by a factor of scaled/scalen.
* (Don't need to use DBScaleValue() because all values must be positive
* and cannot be (M)INFINITY.)
*
* ----------------------------------------------------------------------------
*/
void
DRCTechScale(scalen, scaled)
int scalen, scaled;
{
DRCCookie *dp;
TileType i, j;
int scalegcf;
if (DRCCurStyle == NULL) return;
else if (scalen == scaled) return;
/* Revert DRC rules to original (unscaled) values */
drcScaleUp(DRCCurStyle, DRCCurStyle->DRCScaleFactorN);
drcScaleDown(DRCCurStyle, DRCCurStyle->DRCScaleFactorD);
/* Do the same for the plow rules */
DRCPlowScale(DRCCurStyle->DRCScaleFactorN, DRCCurStyle->DRCScaleFactorD, FALSE);
DRCCurStyle->DRCScaleFactorD *= scaled;
DRCCurStyle->DRCScaleFactorN *= scalen;
/* Reduce scalefactor ratio by greatest common factor */
scalegcf = FindGCF(DRCCurStyle->DRCScaleFactorD, DRCCurStyle->DRCScaleFactorN);
DRCCurStyle->DRCScaleFactorD /= scalegcf;
DRCCurStyle->DRCScaleFactorN /= scalegcf;
/* Rescale all rules to the new scalefactor */
drcScaleUp(DRCCurStyle, DRCCurStyle->DRCScaleFactorD);
drcScaleDown(DRCCurStyle, DRCCurStyle->DRCScaleFactorN);
/* Do the same for the plow rules */
DRCPlowScale(DRCCurStyle->DRCScaleFactorD, DRCCurStyle->DRCScaleFactorN, TRUE);
DRCTechHalo *= scaled;
DRCTechHalo /= scalen;
DRCStepSize *= scaled;
DRCStepSize /= scalen;
DRCCurStyle->DRCTechHalo *= scaled;
DRCCurStyle->DRCTechHalo /= scalen;
DRCCurStyle->DRCStepSize *= scaled;
DRCCurStyle->DRCStepSize /= scalen;
}
/* The following routines are used by the "tech" command (and in other places,
* such as the LEF file reader) to query the DRC database.
*/
/*
*-----------------------------------------------------------------------------
* DRCGetDefaultLayerWidth ---
*
* Determine a default layer width from the DRC width rules
* of a layer. Continue processing until we have processed all
* rules, since rules are ordered from shortest to longest distance,
* and the maximum distance rule will mask any rules with a shorter
* distance.
*
* Results:
* The minimum width of the magic layer, in magic internal units
*
* Side effects:
* None.
*
*-----------------------------------------------------------------------------
*/
int
DRCGetDefaultLayerWidth(ttype)
TileType ttype;
{
int routeWidth = 0;
DRCCookie *cptr;
TileTypeBitMask *set;
if (ttype < 0)
{
TxError("Error: Attempt to get default width of invalid layer!\n");
return 0;
}
for (cptr = DRCCurStyle->DRCRulesTbl[TT_SPACE][ttype]; cptr != (DRCCookie *) NULL;
cptr = cptr->drcc_next)
{
/* Skip triggered and triggering rules */
if (cptr->drcc_flags & DRC_TRIGGER)
{
cptr = cptr->drcc_next;
continue;
}
/* Skip area rule */
if (cptr->drcc_flags & DRC_AREA) continue;
/* FORWARD rules only, and no MAXWIDTH or SPLITTILE */
if ((cptr->drcc_flags & (DRC_REVERSE | DRC_MAXWIDTH | DRC_SPLITTILE)) == 0)
{
set = &cptr->drcc_mask;
if (TTMaskHasType(set, ttype) && TTMaskEqual(set, &cptr->drcc_corner))
if ((cptr->drcc_plane == DBPlane(ttype)) &&
(cptr->drcc_dist == cptr->drcc_cdist))
{
routeWidth = cptr->drcc_dist;
/* Diagnostic */
/*
TxPrintf("DRC: Layer %s has default width %d\n",
DBTypeLongNameTbl[ttype], routeWidth);
*/
}
}
}
return routeWidth;
}
/*
*-----------------------------------------------------------------------------
* DRCGetDefaultLayerSpacing ---
*
* Determine a default layer-to-layer spacing from the DRC width
* rules of a layer. Continue processing all rules, since rules
* are ordered from shortest to longest distance, and the largest
* distance matching the criteria sets the rule.
*
* Results:
* The minimum spacing between the specified magic layer types,
* in magic internal units
*
* Side effects:
* None.
*
*-----------------------------------------------------------------------------
*/
int
DRCGetDefaultLayerSpacing(ttype1, ttype2)
TileType ttype1, ttype2;
{
int routeSpacing = 0;
DRCCookie *cptr;
TileTypeBitMask *set;
for (cptr = DRCCurStyle->DRCRulesTbl[ttype1][TT_SPACE]; cptr != (DRCCookie *) NULL;
cptr = cptr->drcc_next)
{
if (cptr->drcc_flags & DRC_TRIGGER) { /* Skip widespacing rules */
cptr = cptr->drcc_next;
continue;
}
/* Skip area rule */
if (cptr->drcc_flags & DRC_AREA) continue;
if ((cptr->drcc_flags & DRC_REVERSE) == 0) /* FORWARD only */
{
set = &cptr->drcc_mask;
if (!TTMaskHasType(set, ttype2))
if (PlaneMaskHasPlane(DBTypePlaneMaskTbl[ttype2], cptr->drcc_plane) &&
(cptr->drcc_dist == cptr->drcc_cdist))
{
routeSpacing = cptr->drcc_dist;
/* Diagnostic */
/*
TxPrintf("DRC: Layer %s has default spacing %d to layer %s\n",
DBTypeLongNameTbl[ttype1], routeSpacing,
DBTypeLongNameTbl[ttype2]);
*/
}
}
}
return routeSpacing;
}
/*
*-----------------------------------------------------------------------------
* DRCGetDefaultLayerSurround ---
*
* Determine the default minimum required surround amount
* of layer type 2 around layer type 1.
* Continue processing all rules, since rules are ordered from
* shortest to longest distance, and the largest value of the
* surround material sets the minimum required width.
*
* Results:
* The minimum spacing between the specified magic layer types,
* in magic internal units
*
* Side effects:
* None.
*
*-----------------------------------------------------------------------------
*/
int
DRCGetDefaultLayerSurround(ttype1, ttype2)
TileType ttype1, ttype2;
{
int layerSurround = 0;
DRCCookie *cptr;
TileTypeBitMask *set, *cset;
for (cptr = DRCCurStyle->DRCRulesTbl[ttype1][TT_SPACE]; cptr != (DRCCookie *) NULL;
cptr = cptr->drcc_next)
{
if ((cptr->drcc_flags & DRC_REVERSE) == 0) /* FORWARD only */
{
set = &cptr->drcc_mask;
if (!TTMaskHasType(set, TT_SPACE))
if (PlaneMaskHasPlane(DBTypePlaneMaskTbl[ttype2], cptr->drcc_plane) &&
(cptr->drcc_dist == cptr->drcc_cdist))
{
layerSurround = cptr->drcc_dist;
/* Diagnostic */
/*
TxPrintf("DRC: Layer %s has default surround %d over layer %s\n",
DBTypeLongNameTbl[ttype2], layerSurround,
DBTypeLongNameTbl[ttype1]);
*/
}
}
}
if (layerSurround > 0) return layerSurround;
for (cptr = DRCCurStyle->DRCRulesTbl[TT_SPACE][ttype1]; cptr != (DRCCookie *) NULL;
cptr = cptr->drcc_next)
{
if ((cptr->drcc_flags & DRC_REVERSE) == 0) /* FORWARD only */
{
set = &cptr->drcc_mask;
cset = &cptr->drcc_corner;
if (TTMaskHasType(set, TT_SPACE) && !TTMaskHasType(set, ttype1))
if ((TTMaskHasType(cset, ttype2)) &&
(cptr->drcc_flags & DRC_BOTHCORNERS) &&
(cptr->drcc_edgeplane == cptr->drcc_plane) &&
(cptr->drcc_dist == cptr->drcc_cdist))
{
layerSurround = cptr->drcc_dist;
/* Diagnostic */
/*
TxPrintf("DRC: Layer %s has default surround %d over layer %s\n",
DBTypeLongNameTbl[ttype2], layerSurround,
DBTypeLongNameTbl[ttype1]);
*/
}
}
}
return layerSurround;
}
/*
*-----------------------------------------------------------------------------
* DRCGetDirectionalLayerSurround ---
*
* Determine the minimum required surround amount of layer type 2
* around layer type 1 for a directional surround rule (rule
* applies for two opposing sides of layer type 1).
*
* Results:
* The minimum spacing between the specified magic layer types,
* in magic internal units
*
* Side effects:
* None.
*
*-----------------------------------------------------------------------------
*/
int
DRCGetDirectionalLayerSurround(ttype1, ttype2)
TileType ttype1, ttype2;
{
int layerSurround = 0;
DRCCookie *cptr, *cnext;
TileTypeBitMask *set, *cset;
for (cptr = DRCCurStyle->DRCRulesTbl[ttype1][TT_SPACE]; cptr != (DRCCookie *) NULL;
cptr = cptr->drcc_next)
{
if (cptr->drcc_flags & DRC_TRIGGER)
{
set = &cptr->drcc_mask;
if (!TTMaskHasType(set, TT_SPACE) && TTMaskHasType(set, ttype2))
if ((cptr->drcc_plane == cptr->drcc_edgeplane) &&
(cptr->drcc_cdist == 0))
{
layerSurround = cptr->drcc_dist;
/* Diagnostic */
/*
TxPrintf("DRC: Layer %s has default surround %d over layer %s\n",
DBTypeLongNameTbl[ttype2], layerSurround,
DBTypeLongNameTbl[ttype1]);
*/
}
cnext = cptr->drcc_next;
}
}
return layerSurround;
}
/*
*-----------------------------------------------------------------------------
* DRCGetDefaultLayerWideSpacing ---
*
* Determine a default layer-to-self wide-layer spacing rule from
* the DRC width rules of a layer.
*
* Results:
* The minimum spacing between the specified magic layer type and
* itself, where one of the shapes has width greater than "twidth".
* The result value is in magic internal units.
*
* If "twidth" is zero, then return the maximum spacing rule distance.
*
* Side effects:
* None.
*
*-----------------------------------------------------------------------------
*/
int
DRCGetDefaultWideLayerSpacing(ttype, twidth)
TileType ttype;
int twidth;
{
int routeSpacing = 0;
DRCCookie *cptr;
TileTypeBitMask *set;
bool widerule = FALSE;
for (cptr = DRCCurStyle->DRCRulesTbl[ttype][TT_SPACE]; cptr != (DRCCookie *) NULL;
cptr = cptr->drcc_next)
{
if (cptr->drcc_flags & DRC_TRIGGER) /* Widespacing rule */
{
widerule = TRUE;
if (twidth > 0 && cptr->drcc_dist > twidth) /* Check against rule width */
return routeSpacing;
}
if (widerule && ((cptr->drcc_flags & DRC_REVERSE) == 0)) /* FORWARD only */
{
set = &cptr->drcc_mask;
if (!TTMaskHasType(set, ttype))
if (PlaneMaskHasPlane(DBTypePlaneMaskTbl[ttype], cptr->drcc_plane) &&
(cptr->drcc_dist == cptr->drcc_cdist))
{
routeSpacing = cptr->drcc_dist;
/* Diagnostic */
/*
TxPrintf("DRC: Layer %s has wide spacing %d to layer %s width %d\n",
DBTypeLongNameTbl[ttype1], routeSpacing,
DBTypeLongNameTbl[ttype2], twidth);
*/
}
}
if (!(cptr->drcc_flags & DRC_TRIGGER)) widerule = FALSE;
}
return routeSpacing;
}