2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* DRCtech.c --
|
|
|
|
|
*
|
|
|
|
|
* Technology initialization for the DRC module.
|
|
|
|
|
*
|
2020-05-23 23:13:14 +02:00
|
|
|
* *********************************************************************
|
|
|
|
|
* * 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. *
|
2017-04-25 14:41:48 +02:00
|
|
|
* *********************************************************************
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#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>
|
2022-01-07 20:56:43 +01:00
|
|
|
#include <ctype.h>
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
#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"
|
2022-02-23 23:16:42 +01:00
|
|
|
#include "drc/drc.h"
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-10-10 11:50:15 +02:00
|
|
|
/* C99 compat */
|
|
|
|
|
#include "utils/tech.h"
|
|
|
|
|
#include "plow/plow.h"
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
CIFStyle *drcCifStyle = NULL;
|
|
|
|
|
bool DRCForceReload = FALSE;
|
2020-03-06 19:46:40 +01:00
|
|
|
HashTable DRCWhyErrorTable; /* Table of DRC errors */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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;
|
|
|
|
|
|
2020-02-25 19:57:41 +01:00
|
|
|
/* Rules with unique names are tagged with a reference number */
|
|
|
|
|
/* for use in placing errors into the DRC error plane. */
|
|
|
|
|
|
|
|
|
|
static int DRCtag = 0;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* Forward declarations.
|
|
|
|
|
*/
|
|
|
|
|
int drcWidth(), drcSpacing(), drcEdge(), drcNoOverlap();
|
|
|
|
|
int drcExactOverlap(), drcExtend();
|
|
|
|
|
int drcSurround(), drcRectOnly(), drcOverhang();
|
2021-02-04 23:35:43 +01:00
|
|
|
int drcStepSize(), drcOption(), drcOffGrid();
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* 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, 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)
|
|
|
|
|
{
|
2020-02-25 19:57:41 +01:00
|
|
|
char *old = (char *)dp;
|
2017-04-25 14:41:48 +02:00
|
|
|
dp = dp->drcc_next;
|
|
|
|
|
freeMagic(old);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-25 19:57:41 +01:00
|
|
|
/* Clear the Why string list */
|
|
|
|
|
freeMagic(DRCCurStyle->DRCWhyList);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
2020-02-25 19:57:41 +01:00
|
|
|
* drcWhyCreate --
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
2020-02-25 19:57:41 +01:00
|
|
|
* Create a hash entry for the DRC "why" string, if it does not already
|
|
|
|
|
* exist.
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* Returns:
|
2020-02-25 19:57:41 +01:00
|
|
|
* A pointer to the drcWhy structure containing the string and tag.
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* Side effects:
|
2020-02-25 19:57:41 +01:00
|
|
|
* 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.
|
2020-03-06 19:46:40 +01:00
|
|
|
* Temporary hash table DRCWhyErrorTable is used to determine if a
|
2020-02-25 19:57:41 +01:00
|
|
|
* string entry is unique. It is cleared after the technology file
|
|
|
|
|
* has been processed.
|
2017-04-25 14:41:48 +02:00
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
2020-02-25 19:57:41 +01:00
|
|
|
int
|
|
|
|
|
drcWhyCreate(whystring)
|
|
|
|
|
char *whystring;
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2020-02-25 19:57:41 +01:00
|
|
|
HashEntry *he;
|
|
|
|
|
|
2020-03-06 19:46:40 +01:00
|
|
|
he = HashLookOnly(&DRCWhyErrorTable, whystring);
|
2020-02-25 19:57:41 +01:00
|
|
|
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 *));
|
2020-03-05 19:14:47 +01:00
|
|
|
newList[0] = (char *)NULL;
|
|
|
|
|
for (i = 1; i <= DRCCurStyle->DRCWhySize; i++)
|
2020-02-25 19:57:41 +01:00
|
|
|
newList[i] = DRCCurStyle->DRCWhyList[i];
|
|
|
|
|
if (DRCCurStyle->DRCWhySize > 0)
|
|
|
|
|
freeMagic((char *)DRCCurStyle->DRCWhyList);
|
|
|
|
|
DRCCurStyle->DRCWhyList = newList;
|
|
|
|
|
}
|
|
|
|
|
DRCCurStyle->DRCWhySize++;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2020-03-06 19:46:40 +01:00
|
|
|
he = HashFind(&DRCWhyErrorTable, whystring);
|
2020-02-25 19:57:41 +01:00
|
|
|
HashSetValue(he, (char *)((pointertype)DRCCurStyle->DRCWhySize));
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2020-02-25 19:57:41 +01:00
|
|
|
DRCCurStyle->DRCWhyList[DRCCurStyle->DRCWhySize] = StrDup((char **)NULL, whystring);
|
|
|
|
|
|
|
|
|
|
return DRCCurStyle->DRCWhySize;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* 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 --
|
2020-05-23 23:13:14 +02:00
|
|
|
*
|
2017-04-25 14:41:48 +02:00
|
|
|
* 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.
|
2020-05-23 23:13:14 +02:00
|
|
|
*
|
2017-04-25 14:41:48 +02:00
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
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;
|
2018-09-19 23:04:13 +02:00
|
|
|
DRCCurStyle->DRCFlags = (char)0;
|
2020-02-25 19:57:41 +01:00
|
|
|
DRCCurStyle->DRCWhySize = 0;
|
|
|
|
|
|
2020-03-06 19:46:40 +01:00
|
|
|
HashInit(&DRCWhyErrorTable, 16, HT_STRINGKEYS);
|
2020-02-25 19:57:41 +01:00
|
|
|
|
|
|
|
|
/* 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");
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2020-10-15 23:31:20 +02:00
|
|
|
/* 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");
|
|
|
|
|
|
2021-02-04 23:35:43 +01:00
|
|
|
/* 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");
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
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.
|
|
|
|
|
*/
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* 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 *locargv[2][10] = {"style", "default"};
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
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
|
2020-02-25 19:57:41 +01:00
|
|
|
drcCifAssign(cookie, dist, next, mask, corner, tag, cdist, flags, planeto, planefrom)
|
2017-04-25 14:41:48 +02:00
|
|
|
DRCCookie *cookie, *next;
|
|
|
|
|
int dist, cdist;
|
|
|
|
|
TileTypeBitMask *mask, *corner;
|
2020-02-25 19:57:41 +01:00
|
|
|
int tag;
|
2021-02-04 23:35:43 +01:00
|
|
|
unsigned short flags;
|
|
|
|
|
int planeto, planefrom;
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
(cookie)->drcc_dist = dist;
|
|
|
|
|
(cookie)->drcc_next = next;
|
|
|
|
|
(cookie)->drcc_mask = *mask;
|
|
|
|
|
(cookie)->drcc_corner = *corner;
|
2020-02-25 19:57:41 +01:00
|
|
|
(cookie)->drcc_tag = tag;
|
2017-04-25 14:41:48 +02:00
|
|
|
(cookie)->drcc_cdist = cdist;
|
|
|
|
|
(cookie)->drcc_flags = flags;
|
|
|
|
|
(cookie)->drcc_edgeplane = planefrom;
|
|
|
|
|
(cookie)->drcc_plane = planeto;
|
|
|
|
|
(cookie)->drcc_mod = 0;
|
|
|
|
|
(cookie)->drcc_cmod = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-19 20:33:30 +02:00
|
|
|
// 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;
|
2020-02-25 19:57:41 +01:00
|
|
|
int why;
|
2021-02-04 23:35:43 +01:00
|
|
|
unsigned short flags;
|
|
|
|
|
int planeto, planefrom;
|
2019-09-19 20:33:30 +02:00
|
|
|
{
|
|
|
|
|
/* 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);
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* 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;
|
|
|
|
|
char *fmt;
|
|
|
|
|
static struct
|
|
|
|
|
{
|
|
|
|
|
char *rk_keyword; /* Initial keyword */
|
|
|
|
|
int rk_minargs; /* Min # arguments */
|
|
|
|
|
int rk_maxargs; /* Max # arguments */
|
|
|
|
|
int (*rk_proc)(); /* Procedure implementing this keyword */
|
|
|
|
|
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,
|
2022-04-25 19:23:29 +02:00
|
|
|
"layers1 layers2 distance [option] why",
|
2017-04-25 14:41:48 +02:00
|
|
|
"no_overlap", 3, 3, drcNoOverlap,
|
|
|
|
|
"layers1 layers2",
|
2018-09-19 23:04:13 +02:00
|
|
|
"option", 2, 2, drcOption,
|
|
|
|
|
"option_name option_value",
|
2017-04-25 14:41:48 +02:00
|
|
|
"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",
|
2022-11-30 21:33:21 +01:00
|
|
|
"surround", 6, 7, drcSurround,
|
2017-04-25 14:41:48 +02:00
|
|
|
"layers1 layers2 distance presence why",
|
2022-04-21 03:29:32 +02:00
|
|
|
"width", 4, 5, drcWidth,
|
2017-04-25 14:41:48 +02:00
|
|
|
"layers width why",
|
2018-09-19 23:04:13 +02:00
|
|
|
"widespacing", 7, 8, drcSpacing,
|
2017-04-25 14:41:48 +02:00
|
|
|
"layers1 width layers2 separation adjacency why",
|
|
|
|
|
"area", 5, 5, drcArea,
|
|
|
|
|
"layers area horizon why",
|
2021-02-04 23:35:43 +01:00
|
|
|
"off_grid", 4, 4, drcOffGrid,
|
|
|
|
|
"layers pitch why",
|
2017-04-25 14:41:48 +02:00
|
|
|
"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], (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:
|
|
|
|
|
*
|
2022-04-21 03:29:32 +02:00
|
|
|
* extend layers1 layers2 distance [exact_width | exclusive] why
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* 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:
|
|
|
|
|
*
|
2022-04-21 03:29:32 +02:00
|
|
|
* extend nfet *poly 3 "n-transistor width must be at least 3"
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* "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.
|
|
|
|
|
*
|
2022-04-21 03:29:32 +02:00
|
|
|
* 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.
|
|
|
|
|
*
|
2017-04-25 14:41:48 +02:00
|
|
|
* 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]);
|
2020-02-25 19:57:41 +01:00
|
|
|
int why;
|
2017-04-25 14:41:48 +02:00
|
|
|
TileTypeBitMask set1, setC;
|
|
|
|
|
DRCCookie *dp, *dpnew, *dptrig;
|
|
|
|
|
TileType i, j;
|
|
|
|
|
int plane, plane2;
|
2017-08-02 04:14:42 +02:00
|
|
|
TileTypeBitMask set2, setZ, setN, setM;
|
2017-04-25 14:41:48 +02:00
|
|
|
PlaneMask pMask1, pMask2, pset, ptest;
|
|
|
|
|
bool exact = FALSE;
|
2022-04-21 03:29:32 +02:00
|
|
|
bool exclusive = FALSE;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if (!strncmp(argv[4], "exact_", 6))
|
|
|
|
|
{
|
|
|
|
|
exact = TRUE;
|
2020-02-25 19:57:41 +01:00
|
|
|
why = drcWhyCreate(argv[5]);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2022-04-21 03:29:32 +02:00
|
|
|
else if (!strncmp(argv[4], "exclu", 5))
|
|
|
|
|
{
|
|
|
|
|
exclusive = TRUE;
|
|
|
|
|
why = drcWhyCreate(argv[5]);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
else
|
2020-02-25 19:57:41 +01:00
|
|
|
why = drcWhyCreate(argv[4]);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
ptest = DBTechNoisyNameMask(layers1, &set1);
|
|
|
|
|
pMask1 = CoincidentPlanes(&set1, ptest);
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
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);
|
|
|
|
|
}
|
2017-08-02 04:14:42 +02:00
|
|
|
|
2022-04-21 03:29:32 +02:00
|
|
|
/* setM is the union of set1 and set2 unless exclusive == TRUE */
|
2017-08-02 04:14:42 +02:00
|
|
|
TTMaskZero(&setM);
|
2022-04-21 03:29:32 +02:00
|
|
|
if (exclusive)
|
|
|
|
|
TTMaskSetMask(&setM, &set1);
|
|
|
|
|
else
|
|
|
|
|
TTMaskSetMask3(&setM, &set1, &set2);
|
2017-08-02 04:14:42 +02:00
|
|
|
|
|
|
|
|
/* setN is the inverse of set1, and setC is the inverse of set2 */
|
|
|
|
|
TTMaskCom2(&setN, &set1);
|
2017-04-25 14:41:48 +02:00
|
|
|
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));
|
2017-08-02 04:14:42 +02:00
|
|
|
drcAssign(dpnew, distance, dp->drcc_next, &setM, &setZ, why,
|
2017-04-25 14:41:48 +02:00
|
|
|
0, DRC_FORWARD, plane, plane);
|
|
|
|
|
|
|
|
|
|
dp->drcc_next = dpnew;
|
|
|
|
|
|
|
|
|
|
dp = drcFindBucket(j, i, distance);
|
|
|
|
|
dpnew = (DRCCookie *)mallocMagic(sizeof(DRCCookie));
|
2017-08-02 04:14:42 +02:00
|
|
|
drcAssign(dpnew, distance, dp->drcc_next, &setM, &setZ, why,
|
2017-04-25 14:41:48 +02:00
|
|
|
0, DRC_REVERSE, plane, plane);
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
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);
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
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));
|
2017-08-02 04:14:42 +02:00
|
|
|
drcAssign(dpnew, distance, dp->drcc_next, &setM, &setZ, why,
|
2017-04-25 14:41:48 +02:00
|
|
|
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));
|
2017-08-02 04:14:42 +02:00
|
|
|
drcAssign(dpnew, distance, dp->drcc_next, &setM, &setZ, why,
|
2017-04-25 14:41:48 +02:00
|
|
|
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:
|
|
|
|
|
*
|
2022-04-21 03:29:32 +02:00
|
|
|
* width layers distance [angles] why
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* e.g,
|
|
|
|
|
*
|
|
|
|
|
* width poly,pmc 2 "poly width must be at least 2"
|
|
|
|
|
*
|
2022-04-21 03:29:32 +02:00
|
|
|
* 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.
|
|
|
|
|
*
|
2017-04-25 14:41:48 +02:00
|
|
|
* 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]);
|
2022-04-21 03:29:32 +02:00
|
|
|
int why;
|
2017-04-25 14:41:48 +02:00
|
|
|
TileTypeBitMask set, setC;
|
|
|
|
|
PlaneMask pmask, pset, ptest;
|
|
|
|
|
DRCCookie *dp, *dpnew;
|
|
|
|
|
TileType i, j;
|
2022-04-21 03:29:32 +02:00
|
|
|
int plane, flags = 0;
|
|
|
|
|
|
|
|
|
|
if (!strncmp(argv[3], "angle", 5))
|
|
|
|
|
{
|
|
|
|
|
flags = DRC_SPLITTILE;
|
|
|
|
|
why = drcWhyCreate(argv[4]);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
why = drcWhyCreate(argv[3]);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
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,
|
2022-04-21 03:29:32 +02:00
|
|
|
why, distance, DRC_FORWARD | flags, plane, plane);
|
2017-04-25 14:41:48 +02:00
|
|
|
dp->drcc_next = dpnew;
|
|
|
|
|
}
|
2022-04-20 23:12:58 +02:00
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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]);
|
2020-02-25 19:57:41 +01:00
|
|
|
int why = drcWhyCreate(argv[4]);
|
2017-04-25 14:41:48 +02:00
|
|
|
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);
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
dp->drcc_next = dpnew;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return (horizon);
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-04 23:35:43 +01:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* 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);
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* 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"
|
|
|
|
|
*
|
2020-05-23 23:13:14 +02:00
|
|
|
* bend_illegal - means that one_dimension must be distance for any
|
2017-04-25 14:41:48 +02:00
|
|
|
* 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:
|
2021-02-18 02:53:17 +01:00
|
|
|
* both - implies bend_illegal and both directions are checked
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* XXXXX XXXXXX
|
|
|
|
|
* X X XXXXXX
|
|
|
|
|
* X X X X
|
|
|
|
|
* XXXXX XXXXXX
|
2020-05-23 23:13:14 +02:00
|
|
|
*
|
|
|
|
|
* OK BAD
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* 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];
|
2020-02-25 19:57:41 +01:00
|
|
|
int why;
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
2020-02-25 19:57:41 +01:00
|
|
|
why = drcWhyCreate(argv[3]);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (strcmp(bends,"bend_illegal") == 0) bend = 0;
|
|
|
|
|
else if (strcmp(bends,"bend_ok") == 0) bend = DRC_BENDS;
|
2021-02-18 02:53:17 +01:00
|
|
|
else if (strcmp(bends,"both") == 0) bend = DRC_MAXWIDTH_BOTH;
|
2017-04-25 14:41:48 +02:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TechError("unknown bend option %s\n",bends);
|
|
|
|
|
return (0);
|
|
|
|
|
}
|
2020-02-25 19:57:41 +01:00
|
|
|
why = drcWhyCreate(argv[4]);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
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];
|
2022-04-20 22:16:20 +02:00
|
|
|
char *endptr;
|
|
|
|
|
long value;
|
|
|
|
|
int angles;
|
2020-02-25 19:57:41 +01:00
|
|
|
int why = drcWhyCreate(argv[3]);
|
2017-04-25 14:41:48 +02:00
|
|
|
TileTypeBitMask set;
|
|
|
|
|
DRCCookie *dp, *dpnew;
|
|
|
|
|
int plane;
|
2022-04-20 22:16:20 +02:00
|
|
|
PlaneMask ptest, pset, pmask;
|
2017-04-25 14:41:48 +02:00
|
|
|
TileType i, j;
|
|
|
|
|
|
2022-04-20 22:16:20 +02:00
|
|
|
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 */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-04-20 22:16:20 +02:00
|
|
|
ptest = DBTechNoisyNameMask(layers, &set);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-04-20 22:16:20 +02:00
|
|
|
/* 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)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2022-04-20 22:16:20 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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,
|
2022-04-20 22:16:20 +02:00
|
|
|
1, angles, plane, plane);
|
2017-04-25 14:41:48 +02:00
|
|
|
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
|
|
|
|
|
*
|
2018-12-28 16:29:18 +01:00
|
|
|
* The complementary case is redundant, so not checked.
|
|
|
|
|
*
|
2017-04-25 14:41:48 +02:00
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
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];
|
2020-02-25 19:57:41 +01:00
|
|
|
int why = drcWhyCreate(argv[6]);
|
2017-04-25 14:41:48 +02:00
|
|
|
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.
|
2020-05-23 23:13:14 +02:00
|
|
|
*
|
2017-04-25 14:41:48 +02:00
|
|
|
*-------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
drcMaskSpacing(set1, set2, pmask1, pmask2, wwidth, distance, adjacency,
|
2018-09-19 23:04:13 +02:00
|
|
|
why, widerule, runlength, multiplane)
|
2017-04-25 14:41:48 +02:00
|
|
|
TileTypeBitMask *set1, *set2;
|
|
|
|
|
PlaneMask pmask1, pmask2;
|
|
|
|
|
int wwidth, distance;
|
2023-01-16 01:02:58 +01:00
|
|
|
char *adjacency;
|
|
|
|
|
int why;
|
2018-09-19 23:04:13 +02:00
|
|
|
bool widerule;
|
|
|
|
|
int runlength;
|
|
|
|
|
bool multiplane;
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
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;
|
2021-06-04 18:26:11 +02:00
|
|
|
bool surroundok = FALSE;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
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;
|
2020-10-15 18:18:14 +02:00
|
|
|
needReverse = TRUE;
|
2021-06-04 18:26:11 +02:00
|
|
|
surroundok = TRUE;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
|
{
|
2020-05-23 23:13:14 +02:00
|
|
|
/* In "touching_ok rules, spacing to set2 is be checked in FORWARD
|
2017-04-25 14:41:48 +02:00
|
|
|
* direction at edges between set1 and (setR = ~set1 AND ~set2).
|
|
|
|
|
*
|
2020-05-23 23:13:14 +02:00
|
|
|
* In addition, spacing to set1 is checked in FORWARD direction
|
2017-04-25 14:41:48 +02:00
|
|
|
* 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;
|
2020-05-23 23:13:14 +02:00
|
|
|
tmp2 = *set2;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* 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
|
2020-05-23 23:13:14 +02:00
|
|
|
* in FORWARD direction at edges between set1 and (setR=~set1).
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* 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. */
|
|
|
|
|
|
|
|
|
|
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,
|
2018-09-19 23:04:13 +02:00
|
|
|
runlength, DRC_REVERSE | DRC_MAXWIDTH |
|
2017-04-25 14:41:48 +02:00
|
|
|
DRC_TRIGGER | DRC_BENDS, plane2, plane);
|
|
|
|
|
|
|
|
|
|
dp->drcc_next = dptrig;
|
|
|
|
|
}
|
|
|
|
|
else if (needtrigger)
|
|
|
|
|
{
|
|
|
|
|
DRCCookie *dptrig;
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* 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)
|
|
|
|
|
{
|
2020-05-23 23:13:14 +02:00
|
|
|
/* Add check in reverse direction,
|
2017-04-25 14:41:48 +02:00
|
|
|
* NOTE: am assuming single plane rule here (since reverse
|
2020-05-23 23:13:14 +02:00
|
|
|
* rules only used with touching_ok which must be
|
2017-04-25 14:41:48 +02:00
|
|
|
* single plane)
|
|
|
|
|
*/
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* 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) */
|
|
|
|
|
|
|
|
|
|
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,
|
2018-09-19 23:04:13 +02:00
|
|
|
runlength, DRC_FORWARD | DRC_MAXWIDTH |
|
2017-04-25 14:41:48 +02:00
|
|
|
DRC_TRIGGER | DRC_BENDS, plane2, plane);
|
|
|
|
|
dp->drcc_next = dptrig;
|
|
|
|
|
}
|
|
|
|
|
else if (needtrigger)
|
|
|
|
|
{
|
|
|
|
|
DRCCookie *dptrig;
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* 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,
|
2020-05-23 23:13:14 +02:00
|
|
|
&tmp1, &tmp2, why, wwidth,
|
2017-04-25 14:41:48 +02:00
|
|
|
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
|
2021-06-04 18:26:11 +02:00
|
|
|
* and RHS in set2. HOWEVER, "surround_ok" rules are asymmetrically
|
|
|
|
|
* triggered and cannot be reversed between set1 and set2.
|
2017-04-25 14:41:48 +02:00
|
|
|
*/
|
|
|
|
|
|
2021-06-04 18:26:11 +02:00
|
|
|
if ((!surroundok) && (pset = (DBTypesOnSamePlane(i, j) & pmask2)))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
|
2020-10-15 23:31:20 +02:00
|
|
|
/* 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. */
|
2017-04-25 14:41:48 +02:00
|
|
|
if (needtrigger)
|
|
|
|
|
{
|
|
|
|
|
DRCCookie *dptrig;
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* 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);
|
2020-05-23 23:13:14 +02:00
|
|
|
dp->drcc_next = dpnew;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (needReverse)
|
|
|
|
|
dpnew->drcc_flags |= DRC_BOTHCORNERS;
|
|
|
|
|
|
|
|
|
|
if (needReverse)
|
|
|
|
|
{
|
2020-05-23 23:13:14 +02:00
|
|
|
/* Add check in reverse direction,
|
2017-04-25 14:41:48 +02:00
|
|
|
* NOTE: am assuming single plane rule here (since reverse
|
2020-05-23 23:13:14 +02:00
|
|
|
* rules only used with touching_ok which must be
|
2017-04-25 14:41:48 +02:00
|
|
|
* single plane)
|
|
|
|
|
*/
|
|
|
|
|
/* find bucket preceding new one we wish to insert */
|
|
|
|
|
dp = drcFindBucket(j, i, distance);
|
|
|
|
|
dpnew = (DRCCookie *) mallocMagic(sizeof (DRCCookie));
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2020-10-15 23:31:20 +02:00
|
|
|
/* See above */
|
2017-04-25 14:41:48 +02:00
|
|
|
if (needtrigger)
|
|
|
|
|
{
|
|
|
|
|
DRCCookie *dptrig;
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* 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,
|
2020-05-23 23:13:14 +02:00
|
|
|
&tmp1, &tmp2, why, distance,
|
2017-04-25 14:41:48 +02:00
|
|
|
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
|
2020-05-23 23:13:14 +02:00
|
|
|
* set2 rectangle.
|
2017-04-25 14:41:48 +02:00
|
|
|
* (This check added by Michael Arnold on 4/10/86.)
|
|
|
|
|
*/
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
if (needtrigger) continue;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
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 */
|
2020-05-23 23:13:14 +02:00
|
|
|
if (i == TT_SPACE)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
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:
|
|
|
|
|
*
|
2018-09-19 23:04:13 +02:00
|
|
|
* widespacing layers1 width [runlength] layers2 distance adjacency why
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* 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).
|
|
|
|
|
*
|
2018-09-19 23:04:13 +02:00
|
|
|
* (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).
|
|
|
|
|
*
|
2017-04-25 14:41:48 +02:00
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
drcSpacing(argc, argv)
|
|
|
|
|
int argc;
|
|
|
|
|
char *argv[];
|
|
|
|
|
{
|
|
|
|
|
char *layers1 = argv[1], *layers2;
|
|
|
|
|
char *adjacency;
|
2020-02-25 19:57:41 +01:00
|
|
|
int why;
|
2017-04-25 14:41:48 +02:00
|
|
|
TileTypeBitMask set1, set2, tmp1, tmp2;
|
|
|
|
|
PlaneMask pmask1, pmask2, pmaskA, pmaskB, ptest;
|
2018-09-19 23:04:13 +02:00
|
|
|
int wwidth, distance, plane, plane2, runlength;
|
2017-04-25 14:41:48 +02:00
|
|
|
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]);
|
2018-09-19 23:04:13 +02:00
|
|
|
|
|
|
|
|
if (argc == 8)
|
|
|
|
|
{
|
|
|
|
|
runlength = atoi(argv[3]);
|
|
|
|
|
layers2 = argv[4];
|
|
|
|
|
distance = atoi(argv[5]);
|
|
|
|
|
adjacency = argv[6];
|
2020-02-25 19:57:41 +01:00
|
|
|
why = drcWhyCreate(argv[7]);
|
2018-09-19 23:04:13 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
layers2 = argv[3];
|
|
|
|
|
distance = atoi(argv[4]);
|
2019-06-07 20:13:50 +02:00
|
|
|
runlength = distance;
|
2018-09-19 23:04:13 +02:00
|
|
|
adjacency = argv[5];
|
2020-02-25 19:57:41 +01:00
|
|
|
why = drcWhyCreate(argv[6]);
|
2018-09-19 23:04:13 +02:00
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
/* 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;
|
2020-02-25 19:57:41 +01:00
|
|
|
why = drcWhyCreate(argv[5]);
|
2018-09-19 23:04:13 +02:00
|
|
|
runlength = distance;
|
|
|
|
|
if (argc >= 7)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
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,
|
2018-09-19 23:04:13 +02:00
|
|
|
widerule, runlength, multiplane);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return drcMaskSpacing(&set1, &set2, pmask1, pmask2, wwidth,
|
2018-09-19 23:04:13 +02:00
|
|
|
distance, adjacency, why, widerule, runlength, multiplane);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
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]);
|
2020-02-25 19:57:41 +01:00
|
|
|
int why = drcWhyCreate(argv[7]);
|
2017-04-25 14:41:48 +02:00
|
|
|
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);
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
if (pMask1 == 0)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
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);
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
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. */
|
|
|
|
|
|
2021-12-30 10:35:03 +01:00
|
|
|
if ((pset & pMaskM) != 0)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
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]);
|
2020-02-25 19:57:41 +01:00
|
|
|
int why = drcWhyCreate(argv[4]);
|
2017-04-25 14:41:48 +02:00
|
|
|
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];
|
2020-02-25 19:57:41 +01:00
|
|
|
int why = drcWhyCreate(argv[2]);
|
2017-04-25 14:41:48 +02:00
|
|
|
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:
|
|
|
|
|
*
|
2022-11-30 21:33:21 +01:00
|
|
|
* surround layers1 layers2 dist [dist2] presence why
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* 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.
|
|
|
|
|
*
|
2022-11-30 21:33:21 +01:00
|
|
|
* 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.
|
|
|
|
|
*
|
2017-04-25 14:41:48 +02:00
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
drcSurround(argc, argv)
|
|
|
|
|
int argc;
|
|
|
|
|
char *argv[];
|
|
|
|
|
{
|
2022-11-30 21:33:21 +01:00
|
|
|
char *layers1 = argv[1], *layers2 = argv[2], *endptr;
|
2017-04-25 14:41:48 +02:00
|
|
|
int distance = atoi(argv[3]);
|
2022-11-30 21:33:21 +01:00
|
|
|
int distance2;
|
2017-04-25 14:41:48 +02:00
|
|
|
char *presence = argv[4];
|
2020-02-25 19:57:41 +01:00
|
|
|
int why = drcWhyCreate(argv[5]);
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
|
|
|
|
|
2022-11-30 21:33:21 +01:00
|
|
|
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]);
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
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 */
|
2020-06-09 21:11:22 +02:00
|
|
|
|
|
|
|
|
if (isDirectional)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2020-06-09 21:11:22 +02:00
|
|
|
if (pset = (DBTypesOnSamePlane(i, j) & pmask))
|
|
|
|
|
{
|
2017-04-25 14:41:48 +02:00
|
|
|
/* Directional surround is done entirely differently */
|
|
|
|
|
|
|
|
|
|
if (TTMaskHasType(&setM, i) && TTMaskHasType(&invM, j))
|
|
|
|
|
{
|
2020-06-09 21:11:22 +02:00
|
|
|
plane1 = LowestMaskBit(pset);
|
|
|
|
|
plane2 = LowestMaskBit(pmask2);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* 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,
|
2020-06-09 21:11:22 +02:00
|
|
|
plane2, plane1);
|
2017-04-25 14:41:48 +02:00
|
|
|
dptrig = (DRCCookie *)mallocMagic(sizeof(DRCCookie));
|
2022-11-30 21:33:21 +01:00
|
|
|
drcAssign(dptrig, distance2, dpnew, &set2,
|
2017-04-25 14:41:48 +02:00
|
|
|
&DBZeroTypeBits, why, 0,
|
|
|
|
|
DRC_FORWARD | DRC_TRIGGER,
|
2020-06-09 21:11:22 +02:00
|
|
|
plane2, plane1);
|
2017-04-25 14:41:48 +02:00
|
|
|
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,
|
2020-06-09 21:11:22 +02:00
|
|
|
plane2, plane1);
|
2017-04-25 14:41:48 +02:00
|
|
|
dptrig = (DRCCookie *)mallocMagic(sizeof(DRCCookie));
|
2022-11-30 21:33:21 +01:00
|
|
|
drcAssign(dptrig, distance2, dpnew, &set2,
|
2017-04-25 14:41:48 +02:00
|
|
|
&DBZeroTypeBits, why, 0,
|
|
|
|
|
DRC_REVERSE | DRC_TRIGGER,
|
2020-06-09 21:11:22 +02:00
|
|
|
plane2, plane1);
|
2017-04-25 14:41:48 +02:00
|
|
|
dp->drcc_next = dptrig;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-06-09 21:11:22 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (pset = (DBTypesOnSamePlane(i, j) & pmask2))
|
|
|
|
|
{
|
2017-04-25 14:41:48 +02:00
|
|
|
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);
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* 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
|
2020-05-23 23:13:14 +02:00
|
|
|
* 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
|
2017-04-25 14:41:48 +02:00
|
|
|
* 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];
|
2020-02-25 19:57:41 +01:00
|
|
|
int why = drcWhyCreate(argv[4]);
|
2017-04-25 14:41:48 +02:00
|
|
|
TileTypeBitMask types, nottypes;
|
|
|
|
|
int maxwidth;
|
|
|
|
|
static char *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(¬types, &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(¬types, j))
|
|
|
|
|
{
|
|
|
|
|
DRCCookie *dp, *dpnew;
|
|
|
|
|
|
|
|
|
|
plane = LowestMaskBit(pset);
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
/*
|
|
|
|
|
* A rule that checks rectangle-ness.
|
2017-04-25 14:41:48 +02:00
|
|
|
* 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)));
|
2020-05-23 23:13:14 +02:00
|
|
|
drcAssign(dpnew, distance, dp->drcc_next,
|
|
|
|
|
¬types, &DBAllTypeBits, why, distance,
|
2017-04-25 14:41:48 +02:00
|
|
|
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)));
|
2020-05-23 23:13:14 +02:00
|
|
|
drcAssign(dpnew, distance, dp->drcc_next,
|
|
|
|
|
¬types, &DBAllTypeBits, why, distance,
|
2017-04-25 14:41:48 +02:00
|
|
|
DRC_REVERSE, plane, plane);
|
|
|
|
|
dp->drcc_next = dpnew;
|
|
|
|
|
|
|
|
|
|
if (maxwidth > 0) {
|
2020-05-23 23:13:14 +02:00
|
|
|
/*
|
2017-04-25 14:41:48 +02:00
|
|
|
* 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));
|
2020-05-23 23:13:14 +02:00
|
|
|
drcAssign(dpnew, distance, dp->drcc_next,
|
|
|
|
|
&types, &DBZeroTypeBits, why, even,
|
2017-04-25 14:41:48 +02:00
|
|
|
DRC_RECTSIZE, plane, plane);
|
|
|
|
|
dp->drcc_next = dpnew;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return maxwidth;
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-19 23:04:13 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* 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);
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* 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)
|
|
|
|
|
{
|
2024-01-14 21:19:39 +01:00
|
|
|
int cmod;
|
2017-04-25 14:41:48 +02:00
|
|
|
int locscale = scalefactor;
|
|
|
|
|
if (dp->drcc_flags & DRC_AREA)
|
|
|
|
|
locscale *= scalefactor;
|
|
|
|
|
|
|
|
|
|
dist = dp->drcc_cdist;
|
|
|
|
|
dp->drcc_cdist /= locscale;
|
2024-01-14 21:19:39 +01:00
|
|
|
|
|
|
|
|
/* 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)
|
|
|
|
|
{
|
2017-04-25 14:41:48 +02:00
|
|
|
dp->drcc_cdist++;
|
2024-01-14 21:19:39 +01:00
|
|
|
if (dp->drcc_flags & DRC_AREA)
|
|
|
|
|
dp->drcc_cmod = (unsigned char)(cmod / scalefactor);
|
|
|
|
|
else
|
|
|
|
|
dp->drcc_cmod = (unsigned char)cmod;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
* 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)
|
2024-01-14 21:19:39 +01:00
|
|
|
{
|
2017-04-25 14:41:48 +02:00
|
|
|
dp->drcc_cdist *= scalefactor;
|
2024-01-14 21:19:39 +01:00
|
|
|
/* 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;
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
|
|
|
|
|
2020-03-06 19:46:40 +01:00
|
|
|
/* Done with DRCWhyErrorTable */
|
|
|
|
|
HashKill(&DRCWhyErrorTable);
|
2020-02-25 19:57:41 +01:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* 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);
|
|
|
|
|
|
2018-03-15 15:51:07 +01:00
|
|
|
/* Scale DRCTechHalo to match */
|
|
|
|
|
DRCTechHalo *= style->DRCScaleFactorD;
|
|
|
|
|
DRCTechHalo /= style->DRCScaleFactorN;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* 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
|
2020-05-23 23:13:14 +02:00
|
|
|
* results in a halo overhead of about 27%. If there's no DRC
|
2017-04-25 14:41:48 +02:00
|
|
|
* 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;
|
2020-05-23 23:13:14 +02:00
|
|
|
freeMagic((char *) dummy);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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.
|
|
|
|
|
*/
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
for (i = 0; i < DBNumTypes; i++)
|
|
|
|
|
{
|
|
|
|
|
for (j = 0; j < DBNumTypes; j++)
|
|
|
|
|
{
|
|
|
|
|
DRCCookie *keep = NULL, *dptest, *dptemp, *dpnew;
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
for (dp = style->DRCRulesTbl[i][j]; dp != NULL; dp = dp->drcc_next)
|
|
|
|
|
{
|
2021-08-01 18:04:13 +02:00
|
|
|
if (dp->drcc_flags & (DRC_NONSTANDARD | DRC_OUTSIDE)) continue;
|
2017-04-25 14:41:48 +02:00
|
|
|
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.
|
|
|
|
|
*/
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* 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;
|
|
|
|
|
}
|
|
|
|
|
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),
|
2020-02-25 19:57:41 +01:00
|
|
|
DRCCurStyle->DRCWhyList[next->drcc_tag],
|
|
|
|
|
DRCCurStyle->DRCWhyList[dp->drcc_tag]);
|
2017-04-25 14:41:48 +02:00
|
|
|
*/
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* 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 == 1) return;
|
|
|
|
|
|
|
|
|
|
/* Revert DRC rules to original (unscaled) values */
|
|
|
|
|
drcScaleUp(DRCCurStyle, DRCCurStyle->DRCScaleFactorN);
|
|
|
|
|
drcScaleDown(DRCCurStyle, DRCCurStyle->DRCScaleFactorD);
|
|
|
|
|
|
2018-04-01 20:40:10 +02:00
|
|
|
/* Do the same for the plow rules */
|
|
|
|
|
DRCPlowScale(DRCCurStyle->DRCScaleFactorN, DRCCurStyle->DRCScaleFactorD, FALSE);
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
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);
|
|
|
|
|
|
2018-04-01 20:40:10 +02:00
|
|
|
/* Do the same for the plow rules */
|
|
|
|
|
DRCPlowScale(DRCCurStyle->DRCScaleFactorD, DRCCurStyle->DRCScaleFactorN, TRUE);
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
|
|
|
|
|
2022-08-27 02:59:50 +02:00
|
|
|
if (ttype < 0)
|
|
|
|
|
{
|
|
|
|
|
TxError("Error: Attempt to get default width of invalid layer!\n");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
for (cptr = DRCCurStyle->DRCRulesTbl[TT_SPACE][ttype]; cptr != (DRCCookie *) NULL;
|
|
|
|
|
cptr = cptr->drcc_next)
|
|
|
|
|
{
|
2022-11-20 21:15:04 +01:00
|
|
|
/* 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;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* FORWARD rules only, and no MAXWIDTH */
|
|
|
|
|
if ((cptr->drcc_flags & (DRC_REVERSE | DRC_MAXWIDTH)) == 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;
|
|
|
|
|
}
|
2022-11-20 21:15:04 +01:00
|
|
|
|
|
|
|
|
/* Skip area rule */
|
|
|
|
|
if (cptr->drcc_flags & DRC_AREA) continue;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
2022-03-29 22:52:01 +02:00
|
|
|
TileTypeBitMask *set, *cset;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
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]);
|
|
|
|
|
*/
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-03-29 22:52:01 +02:00
|
|
|
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)) &&
|
2024-10-04 17:08:45 +02:00
|
|
|
(cptr->drcc_flags & DRC_BOTHCORNERS) &&
|
2022-03-29 22:52:01 +02:00
|
|
|
(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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
return layerSurround;
|
|
|
|
|
}
|
2020-03-25 14:25:32 +01:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
|
* 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.
|
|
|
|
|
*
|
2020-03-25 16:23:16 +01:00
|
|
|
* If "twidth" is zero, then return the maximum spacing rule distance.
|
|
|
|
|
*
|
2020-03-25 14:25:32 +01:00
|
|
|
* 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;
|
2020-03-25 16:23:16 +01:00
|
|
|
if (twidth > 0 && cptr->drcc_dist > twidth) /* Check against rule width */
|
2020-03-25 14:25:32 +01:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|