Files
magic/drc/DRCbasic.c
T
Tim Edwards 8e25303db4 Corrected what looks like a very long-standing error in the DRC
engine.  Rule checks of triggering rules are not subject to
clipping to the clip area.  However, they *must* be clipped to
the (larger) overall DRC check area, because no layout is valid
outside of that area.  This clipping was missed, allowing
triggering rules to trigger on areas outside the valid layout,
resulting in mysterious false-positive DRC errors.  This has
been fixed.
2023-08-27 14:21:02 -04:00

1322 lines
37 KiB
C

/*
* DRCbasic.c --
*
* This file provides routines that make perform basic design-rule
* checking: given an area of a cell definition, this file will
* find all of the rule violations and call a client procedure for
* each one.
*
* *********************************************************************
* * Copyright (C) 1985, 1990 Regents of the University of California. *
* * Permission to use, copy, modify, and distribute this *
* * software and its documentation for any purpose and without *
* * fee is hereby granted, provided that the above copyright *
* * notice appear in all copies. The University of California *
* * makes no representations about the suitability of this *
* * software for any purpose. It is provided "as is" without *
* * express or implied warranty. Export of this software outside *
* * of the United States of America may require an export license. *
* *********************************************************************
*/
#ifndef lint
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/drc/DRCbasic.c,v 1.7 2010/09/20 21:13:22 tim Exp $";
#endif /* not lint */
#include <sys/types.h>
#include <stdio.h>
#include <string.h> // for memcpy()
#include <math.h> // for sqrt() for diagonal check
#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/signals.h"
#include "utils/maxrect.h"
#include "utils/malloc.h"
int dbDRCDebug = 0;
/* The following DRC cookie is used when there are tiles of type
* TT_ERROR_S found during the basic DRC. These arise during
* hierarchical checking when there are illegal overlaps.
*/
static DRCCookie drcOverlapCookie = {
0, 0, 0, 0,
{ 0 }, { 0 },
0, 0, 0,
DRC_OVERLAP_TAG,
(DRCCookie *) NULL
};
/* Forward references: */
extern int areaCheck();
extern int drcTile();
extern MaxRectsData *drcCanonicalMaxwidth();
/*
*-----------------------------------------------------------------------
*
* point_to_segment
*
* Euclidean-distance point-to-segment distance (squared)
* calculation (borrowed from XCircuit)
*
* Results:
* Squared Euclidean distance of the closest approach of the
* line segment to the point (long result).
*
* Side Effects:
* None.
*
*-----------------------------------------------------------------------
*/
long
point_to_segment(px, py, s1x, s1y, s2x, s2y)
int px, py; /* The position of the point */
int s1x, s1y; /* One endpoint of the line segment */
int s2x, s2y; /* The other endpoint of the line segment */
{
long x, y;
long a, b, c, frac;
float protod;
x = (long)s2x - (long)s1x;
y = (long)s2y - (long)s1y;
c = (x * x + y * y);
x = (long)px - (long)s1x;
y = (long)py - (long)s1y;
a = (x * x + y * y);
x = (long)px - (long)s2x;
y = (long)py - (long)s2y;
b = (x * x + y * y);
frac = a - b;
if (frac >= c) return b;
else if (-frac >= c) return a;
else
{
protod = (float)(c + a - b);
return (a - (long)((protod * protod) / (float)(c << 2)));
}
}
/* Define Euclidean distance checks */
#define RADIAL_NW 0x1000
#define RADIAL_NE 0x8000
#define RADIAL_SW 0x2000
#define RADIAL_SE 0x4000
/*
* ----------------------------------------------------------------------------
*
* areaCheck --
*
* Call the function passed down from DRCBasicCheck() if the current tile
* violates the rule in the given DRCCookie. If the rule's connectivity
* flag is set, then make sure the violating material isn't connected
* to what's on the initial side of the edge before calling the client
* error function.
*
* This function is called from DBSrPaintArea().
*
* Results:
* Zero (so that the search will continue).
*
* Side effects:
* Applies the function passed as an argument.
*
* ----------------------------------------------------------------------------
*/
int
areaCheck(tile, arg)
Tile *tile;
struct drcClientData *arg;
{
Rect rect; /* Area where error is to be recorded. */
TiToRect(tile, &rect);
/* Only consider the portion of the suspicious tile that overlaps
* the clip area for errors, unless this is a trigger rule, in
* which case it should be restricted only to the full check area.
*/
if (!(arg->dCD_cptr->drcc_flags & DRC_TRIGGER))
GeoClip(&rect, arg->dCD_clip);
else
GeoClip(&rect, arg->dCD_rect);
GeoClip(&rect, arg->dCD_constraint);
if ((rect.r_xbot >= rect.r_xtop) || (rect.r_ybot >= rect.r_ytop))
return 0;
/*
* When Euclidean distance checks are enabled, check for error tiles
* outside of the perimeter of the circle in the corner extension area
* that extends "sdist" from the corner of the edge.
*
* Also check the relatively rare case where the tile is inside the
* circle perimeter, but only the corner of the triangle projects into
* the error check rectangle, and is outside of the circle.
*/
if (arg->dCD_radial != 0)
{
unsigned int i;
int sqx, sqy;
int sdist = arg->dCD_radial & 0xfff;
long sstest, ssdist = sdist * sdist;
if ((arg->dCD_radial & RADIAL_NW) != 0)
{
if (((sqx = arg->dCD_constraint->r_xbot + sdist
- rect.r_xtop) >= 0) && ((sqy = rect.r_ybot
- arg->dCD_constraint->r_ytop + sdist) >= 0)
&& ((sqx * sqx + sqy * sqy) >= ssdist))
return 0;
else if (IsSplit(tile) && !SplitDirection(tile) && !SplitSide(tile))
{
sstest = point_to_segment(arg->dCD_constraint->r_xbot + sdist,
arg->dCD_constraint->r_ytop - sdist,
LEFT(tile), BOTTOM(tile), RIGHT(tile), TOP(tile));
if (sstest > ssdist) return 0;
}
}
if ((arg->dCD_radial & RADIAL_NE) != 0)
{
if (((sqx = rect.r_xbot - arg->dCD_constraint->r_xtop
+ sdist) >= 0) && ((sqy = rect.r_ybot
- arg->dCD_constraint->r_ytop + sdist) >= 0)
&& ((sqx * sqx + sqy * sqy) >= ssdist))
return 0;
else if (IsSplit(tile) && SplitDirection(tile) && SplitSide(tile))
{
sstest = point_to_segment(arg->dCD_constraint->r_xtop - sdist,
arg->dCD_constraint->r_ytop - sdist,
LEFT(tile), TOP(tile), RIGHT(tile), BOTTOM(tile));
if (sstest > ssdist) return 0;
}
}
if ((arg->dCD_radial & RADIAL_SW) != 0)
{
if (((sqx = arg->dCD_constraint->r_xbot + sdist
- rect.r_xtop) >= 0) &&
((sqy = arg->dCD_constraint->r_ybot
+ sdist - rect.r_ytop) >= 0)
&& ((sqx * sqx + sqy * sqy) >= ssdist))
return 0;
else if (IsSplit(tile) && SplitDirection(tile) && !SplitSide(tile))
{
sstest = point_to_segment(arg->dCD_constraint->r_xbot + sdist,
arg->dCD_constraint->r_ybot + sdist,
LEFT(tile), TOP(tile), RIGHT(tile), BOTTOM(tile));
if (sstest > ssdist) return 0;
}
}
if ((arg->dCD_radial & RADIAL_SE) != 0)
{
if (((sqx = rect.r_xbot - arg->dCD_constraint->r_xtop
+ sdist) >= 0) &&
((sqy = arg->dCD_constraint->r_ybot
+ sdist - rect.r_ytop) >= 0)
&& ((sqx * sqx + sqy * sqy) >= ssdist))
return 0;
else if (IsSplit(tile) && !SplitDirection(tile) && SplitSide(tile))
{
sstest = point_to_segment(arg->dCD_constraint->r_xtop - sdist,
arg->dCD_constraint->r_ybot + sdist,
LEFT(tile), BOTTOM(tile), RIGHT(tile), TOP(tile));
if (sstest > ssdist) return 0;
}
}
}
if (arg->dCD_cptr->drcc_flags & DRC_TRIGGER)
{
Rect *newrlist;
int entries = arg->dCD_entries;
/* The following code allows the rect list to be expanded by */
/* multiples of 8, when necessary. */
arg->dCD_entries++;
if (arg->dCD_rlist == NULL)
arg->dCD_rlist = (Rect *)mallocMagic(8 * sizeof(Rect));
else if ((arg->dCD_entries & ~(entries | 7)) == arg->dCD_entries)
{
newrlist = (Rect *)mallocMagic((arg->dCD_entries << 1) * sizeof(Rect));
memcpy((void *)newrlist, (void *)arg->dCD_rlist, (size_t)entries *
sizeof(Rect));
freeMagic(arg->dCD_rlist);
arg->dCD_rlist = newrlist;
}
arg->dCD_rlist[arg->dCD_entries - 1] = rect;
}
/* "angles 45-only" needs to reject errors that are inside split tiles */
else if (!IsSplit(tile) ||
((arg->dCD_cptr->drcc_flags & (DRC_ANGLES_45 | DRC_ANGLES_90))
!= DRC_ANGLES_45))
{
(*(arg->dCD_function))(arg->dCD_celldef, &rect, arg->dCD_cptr,
arg->dCD_clientData);
(*(arg->dCD_errors))++;
}
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* areaNMCheck ---
*
* Check for errors in triangular area of a tile
*
* ----------------------------------------------------------------------------
*/
int
areaNMCheck(tile, arg)
Tile *tile;
struct drcClientData *arg;
{
Rect rect; /* Area where error is to be recorded. */
/* Ignore the tile that initiates the check, because the error area */
/* of a non-Manhattan check may fall inside of it. */
if (tile == arg->dCD_initial) return 0;
TiToRect(tile, &rect);
/* Only consider the portion of the suspicious tile that overlaps
* the clip area for errors, unless this is a trigger rule.
*/
if (!(arg->dCD_cptr->drcc_flags & DRC_TRIGGER))
GeoClip(&rect, arg->dCD_clip);
else
GeoClip(&rect, arg->dCD_rect);
GeoClip(&rect, arg->dCD_constraint);
if ((rect.r_xbot >= rect.r_xtop) || (rect.r_ybot >= rect.r_ytop))
return 0;
(*(arg->dCD_function))(arg->dCD_celldef, &rect, arg->dCD_cptr,
arg->dCD_clientData);
(*(arg->dCD_errors))++;
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* DRCBasicCheck --
*
* This is the top-level routine for basic design-rule checking.
*
* Results:
* Number of errors found.
*
* Side effects:
* Calls function for each design-rule violation in celldef
* that is triggered by an edge in rect and whose violation
* area falls within clipRect. This routine makes a flat check:
* it considers only information in the paint planes of celldef,
* and does not expand children. Function should have the form:
* void
* function(def, area, rule, cdarg)
* CellDef *def;
* Rect *area;
* DRCCookie *rule;
* ClientData cdarg;
* {
* }
*
* In the call to function, def is the definition containing the
* basic area being checked, area is the actual area where a
* rule is violated, rule is the rule being violated, and cdarg
* is the client data passed through all of our routines.
*
* Note:
* If an interrupt occurs (SigInterruptPending gets set), then
* the basic will be aborted immediately. This means the check
* may be incomplete.
*
* ----------------------------------------------------------------------------
*/
int
DRCBasicCheck (celldef, checkRect, clipRect, function, cdata)
CellDef *celldef; /* CellDef being checked */
Rect *checkRect; /* Check rules in this area -- usually two Haloes
* larger than the area where changes were made.
*/
Rect *clipRect; /* Clip error tiles against this area. */
void (*function)(); /* Function to apply for each error found. */
ClientData cdata; /* Passed to function as argument. */
{
struct drcClientData arg;
int errors;
int planeNum;
if (DRCCurStyle == NULL) return 0; /* No DRC, no errors */
/* Insist on top quality rectangles. */
if ((checkRect->r_xbot >= checkRect->r_xtop)
|| (checkRect->r_ybot >= checkRect->r_ytop))
return (0);
errors = 0;
arg.dCD_celldef = celldef;
arg.dCD_rect = checkRect;
arg.dCD_errors = &errors;
arg.dCD_function = function;
arg.dCD_clip = clipRect;
arg.dCD_clientData = cdata;
arg.dCD_rlist = NULL;
arg.dCD_entries = 0;
for (planeNum = PL_TECHDEPBASE; planeNum < DBNumPlanes; planeNum++)
{
arg.dCD_plane = planeNum;
DBResetTilePlane(celldef->cd_planes[planeNum], DRC_UNPROCESSED);
(void) DBSrPaintArea ((Tile *) NULL, celldef->cd_planes[planeNum],
checkRect, &DBAllTypeBits, drcTile, (ClientData) &arg);
}
drcCifCheck(&arg);
if (arg.dCD_rlist != NULL) freeMagic(arg.dCD_rlist);
return (errors);
}
/*
* ----------------------------------------------------------------------------
*
* drcTile --
*
* This is a search function invoked once for each tile in
* the area to be checked. It checks design rules along the left
* and bottom of the given tile. If the tile extends beyond the
* clipping rectangle in any direction, then the boundary on that
* side of the tile will be skipped.
*
* Results:
* Zero (so that the search will continue), unless an interrupt
* occurs, in which case 1 is returned to stop the check.
*
* Side effects:
* Calls the client's error function if errors are found.
*
* ----------------------------------------------------------------------------
*/
int
drcTile (tile, arg)
Tile *tile; /* Tile being examined */
struct drcClientData *arg;
{
DRCCookie *cptr; /* Current design rule on list */
Rect *rect = arg->dCD_rect; /* Area being checked */
Rect errRect; /* Area checked for an individual rule */
MaxRectsData *mrd; /* Used by widespacing rule */
TileTypeBitMask tmpMask, *rMask;
bool trigpending; /* Hack for widespacing rule */
bool firsttile;
int triggered;
int cdist, dist, ccdist, result;
arg->dCD_constraint = &errRect;
/*
* If we were interrupted, we want to
* abort the check as quickly as possible.
*/
if (SigInterruptPending) return 1;
DRCstatTiles++;
/* If this tile is an error tile, it arose because of an illegal
* overlap between things in adjacent cells. This means that
* there's an automatic violation over the area of the tile.
*/
if (TiGetType(tile) == TT_ERROR_S)
{
TiToRect(tile, &errRect);
GeoClip(&errRect, rect);
(*(arg->dCD_function)) (arg->dCD_celldef, &errRect,
&drcOverlapCookie, arg->dCD_clientData);
(*(arg->dCD_errors))++;
}
/* Diagonally split tiles are processed twice. Just like the */
/* DRC searches only one direction on regular tiles, the split */
/* tiles are only processed for one of the two cases. */
if (IsSplit(tile) && !SplitSide(tile))
{
int deltax, deltay;
TileType tt, to;
/* Check rules for DRC_ANGLES_90 rule and process */
tt = TiGetLeftType(tile);
if (tt != TT_SPACE)
{
for (cptr = DRCCurStyle->DRCRulesTbl[TT_SPACE][tt];
cptr != (DRCCookie *) NULL; cptr = cptr->drcc_next)
if (cptr->drcc_flags & DRC_ANGLES_90)
{
drcCheckAngles(tile, arg, cptr);
break;
}
}
tt = TiGetRightType(tile);
if (tt != TT_SPACE)
{
for (cptr = DRCCurStyle->DRCRulesTbl[TT_SPACE][tt];
cptr != (DRCCookie *) NULL; cptr = cptr->drcc_next)
if (cptr->drcc_flags & DRC_ANGLES_90)
{
drcCheckAngles(tile, arg, cptr);
break;
}
}
/* Full check of edge rules along the diagonal. */
tt = TiGetLeftType(tile); /* inside type */
to = TiGetRightType(tile); /* outside type */
for (cptr = DRCCurStyle->DRCRulesTbl[to][tt]; cptr != (DRCCookie *) NULL;
cptr = cptr->drcc_next)
{
int deltax, deltay, w, h;
double r;
TileType dinfo, dsplit;
/* Work to be done: Handle triggering rules for non-Manhattan */
/* edges; especially important for the wide-spacing rule. */
if (cptr->drcc_flags & DRC_TRIGGER)
{
cptr = cptr->drcc_next; // Skip both triggering and triggered rules
continue;
}
/* Note: Triggered wide-spacing rule will require handling */
/* the DRC_MAXWIDTH rule on non-Manhattan edges. */
if (cptr->drcc_flags & (DRC_ANGLES_90 | DRC_AREA | DRC_MAXWIDTH
| DRC_RECTSIZE | DRC_OFFGRID))
continue;
TiToRect(tile, &errRect);
/* Find the rule distances according to the scale factor */
dist = cptr->drcc_dist;
/* drcc_edgeplane is used to avoid checks on edges */
/* in more than one plane */
if (arg->dCD_plane != cptr->drcc_edgeplane) continue;
DRCstatRules++;
DRCstatSlow++;
arg->dCD_cptr = cptr;
arg->dCD_entries = 0;
TTMaskCom2(&tmpMask, &cptr->drcc_mask);
TTMaskClearType(&tmpMask, TT_ERROR_S);
arg->dCD_initial = tile;
/* Compute position that is the rule distance away from */
/* the tile's diagonal edge, by Euclidean measure */
/* (rounded down if fractional). Use the forward */
/* position, and negate if reversed. */
w = RIGHT(tile) - LEFT(tile);
h = TOP(tile) - BOTTOM(tile);
r = 1.0 / (1.0 + ((double)(w * w) / (double)(h * h)));
deltax = (int)((double)cptr->drcc_dist * sqrt(r));
deltay = (deltax * w) / h;
if (SplitDirection(tile) == 0) deltay = -deltay;
dinfo = TiGetTypeExact(tile) & (TT_DIAGONAL | TT_DIRECTION);
if (!(cptr->drcc_flags & DRC_REVERSE))
{
/* Forward case is behind the triangle */
deltax = -deltax;
deltay = -deltay;
/* Split side changes in the reverse case */
dinfo |= TT_SIDE;
}
/* errRect is the tile area offset by (deltax, deltay) */
errRect.r_xbot += deltax;
errRect.r_ybot += deltay;
errRect.r_xtop += deltax;
errRect.r_ytop += deltay;
DBSrPaintNMArea((Tile *) NULL,
arg->dCD_celldef->cd_planes[cptr->drcc_plane], dinfo,
&errRect, &tmpMask, areaNMCheck, (ClientData) arg);
}
DRCstatEdges++;
}
/*
* Check design rules along a vertical boundary between two tiles.
*
* 1 | 4
* T
* |
* tpleft | tile
* |
* B
* 2 | 3
*
* The labels "T" and "B" indicate pointT and pointB respectively.
*
* If a rule's direction is FORWARD, then check from left to right.
*
* * Check the top right corner if the 1x1 lambda square
* on the top left corner (1) of pointT matches the design
* rule's "corner" mask.
*
* * Check the bottom right corner if the rule says check
* BOTHCORNERS and the 1x1 lambda square on the bottom left
* corner (2) of pointB matches the design rule's "corner" mask.
*
* If a rule's direction is REVERSE, then check from right to left.
*
* * Check the bottom left corner if the 1x1 lambda square
* on the bottom right corner (3) of pointB matches the design
* rule's "corner" mask.
*
* * Check the top left corner if the rule says check BOTHCORNERS
* and the 1x1 lambda square on the top right corner (4) of
* pointT matches the design rule's "corner" mask.
*/
if (LEFT(tile) >= rect->r_xbot) /* check tile against rect */
{
Tile *tpleft, *tpl, *tpr;
TileType tt, to;
int edgeTop, edgeBot;
int top = MIN(TOP(tile), rect->r_ytop);
int bottom = MAX(BOTTOM(tile), rect->r_ybot);
int edgeX = LEFT(tile);
firsttile = TRUE;
mrd = NULL;
for (tpleft = BL(tile); BOTTOM(tpleft) < top; tpleft = RT(tpleft))
{
/* Get the tile types to the left and right of the edge */
tt = TiGetLeftType(tile);
to = TiGetRightType(tpleft);
/* Don't check synthetic edges, i.e. edges with same type on
* both sides. Such "edges" have no physical significance, and
* depend on internal-details of how paint is spit into tiles.
* Thus checking them just leads to confusion. (When edge rules
* involving such edges are encountered during technology read-in
* the user is warned that such edges are not checked).
*/
if (tt == to) continue;
/*
* Go through list of design rules triggered by the
* left-to-right edge.
*/
edgeTop = MIN(TOP (tpleft), top);
edgeBot = MAX(BOTTOM(tpleft), bottom);
if (edgeTop <= edgeBot)
continue;
triggered = 0;
for (cptr = DRCCurStyle->DRCRulesTbl[to][tt]; cptr != (DRCCookie *) NULL;
cptr = cptr->drcc_next)
{
/* DRC_ANGLES_90 and DRC_SPLITTILE rules are handled by */
/* the code above for non-Manhattan shapes and do not */
/* need to be processed again. */
if (cptr->drcc_flags & (DRC_ANGLES_90 | DRC_SPLITTILE))
continue;
/* Find the rule distances according to the scale factor */
dist = cptr->drcc_dist;
cdist = cptr->drcc_cdist;
trigpending = (cptr->drcc_flags & DRC_TRIGGER) ? TRUE : FALSE;
/* drcc_edgeplane is used to avoid checks on edges */
/* in more than one plane */
if (arg->dCD_plane != cptr->drcc_edgeplane)
{
if (trigpending) cptr = cptr->drcc_next;
continue;
}
DRCstatRules++;
if (cptr->drcc_flags & DRC_AREA)
{
if (firsttile)
drcCheckArea(tile, arg, cptr);
continue;
}
if ((cptr->drcc_flags & (DRC_MAXWIDTH | DRC_BENDS)) ==
(DRC_MAXWIDTH | DRC_BENDS))
{
/* New algorithm --- Tim 3/6/05 */
Rect *lr;
int i;
if (!trigpending || (DRCCurStyle->DRCFlags
& DRC_FLAGS_WIDEWIDTH_NONINCLUSIVE))
cptr->drcc_dist++;
if (cptr->drcc_flags & DRC_REVERSE)
mrd = drcCanonicalMaxwidth(tpleft, GEO_WEST, arg, cptr);
else if (firsttile)
mrd = drcCanonicalMaxwidth(tile, GEO_EAST, arg, cptr);
if (!trigpending || (DRCCurStyle->DRCFlags
& DRC_FLAGS_WIDEWIDTH_NONINCLUSIVE))
cptr->drcc_dist--;
if (trigpending)
{
if (mrd)
{
if (cptr->drcc_cdist <= cptr->drcc_dist)
triggered = mrd->entries;
else if ((edgeTop - edgeBot) >= cptr->drcc_cdist)
{
/* Run-length rule */
for (i = 0; i < mrd->entries; i++)
{
lr = &mrd->rlist[i];
GeoClip(lr, arg->dCD_clip);
if ((lr->r_ytop - lr->r_ybot) > cptr->drcc_cdist)
{
triggered = mrd->entries;
break;
}
}
if (i == mrd->entries)
cptr = cptr->drcc_next;
}
else
cptr = cptr->drcc_next;
}
else
cptr = cptr->drcc_next;
}
else if (mrd)
{
for (i = 0; i < mrd->entries; i++)
{
lr = &mrd->rlist[i];
GeoClip(lr, arg->dCD_clip);
if (!GEO_RECTNULL(lr))
{
(*(arg->dCD_function)) (arg->dCD_celldef,
lr, cptr, arg->dCD_clientData);
(*(arg->dCD_errors))++;
}
}
}
continue;
}
else if (cptr->drcc_flags & DRC_MAXWIDTH)
{
if (cptr->drcc_flags & DRC_MAXWIDTH_BOTH)
{
if (firsttile)
drcCheckMaxwidth(tile, arg, cptr, TRUE);
}
else
{
/* bends_illegal option only */
if (firsttile)
drcCheckMaxwidth(tile, arg, cptr, FALSE);
}
continue;
}
if (cptr->drcc_flags & DRC_RECTSIZE)
{
/* only checked for bottom-left tile in a rect area */
if (firsttile && !TTMaskHasType(&cptr->drcc_mask,
TiGetRightType(BL(tile))) &&
!TTMaskHasType(&cptr->drcc_mask,
TiGetTopType(LB(tile))))
drcCheckRectSize(tile, arg, cptr);
continue;
}
/* Off-grid checks apply only to edge */
if (cptr->drcc_flags & DRC_OFFGRID)
{
errRect.r_ytop = edgeTop;
errRect.r_ybot = edgeBot;
errRect.r_xtop = errRect.r_xbot = edgeX;
arg->dCD_cptr = cptr;
drcCheckOffGrid(&errRect, arg, cptr);
continue;
}
result = 0;
arg->dCD_radial = 0;
arg->dCD_entries = 0;
do {
if (triggered)
{
/* For triggered rules, we want to look at the */
/* clipped region found by the triggering rule */
if (mrd)
errRect = mrd->rlist[--triggered];
else
errRect = arg->dCD_rlist[--triggered];
errRect.r_ytop += cdist;
errRect.r_ybot -= cdist;
if (errRect.r_ytop > edgeTop) errRect.r_ytop = edgeTop;
if (errRect.r_ybot < edgeBot) errRect.r_ybot = edgeBot;
}
else
{
errRect.r_ytop = edgeTop;
errRect.r_ybot = edgeBot;
}
if (cptr->drcc_flags & DRC_REVERSE)
{
/*
* Determine corner extensions.
*/
/* Find the point (3) to the bottom right of pointB */
if (BOTTOM(tile) >= errRect.r_ybot) tpr = LB(tile);
else tpr = tile;
/* Also find point (2) to check for edge continuation */
if (BOTTOM(tpleft) >= errRect.r_ybot)
for (tpl = LB(tpleft); RIGHT(tpl) < edgeX; tpl = TR(tpl));
else tpl = tpleft;
/* Make sure the edge stops at edgeBot */
if ((TiGetRightType(tpl) != TiGetRightType(tpleft)) ||
(TiGetLeftType(tpr) != TiGetLeftType(tile)))
{
if (TTMaskHasType(&cptr->drcc_corner, TiGetTopType(tpr)))
{
errRect.r_ybot -= cdist;
if (DRCEuclidean)
arg->dCD_radial |= RADIAL_SW;
}
}
if (cptr->drcc_flags & DRC_BOTHCORNERS)
{
/*
* Check the other corner
*/
/* Find point (4) to the top right of pointT */
if (TOP(tile) <= errRect.r_ytop)
for (tpr = RT(tile); LEFT(tpr) > edgeX; tpr = BL(tpr));
else tpr = tile;
/* Also find point (1) to check for edge continuation */
if (TOP(tpleft) <= errRect.r_ytop) tpl = RT(tpleft);
else tpl = tpleft;
/* Make sure the edge stops at edgeTop */
if ((TiGetRightType(tpl) != TiGetRightType(tpleft)) ||
(TiGetLeftType(tpr) != TiGetLeftType(tile)))
{
if (TTMaskHasType(&cptr->drcc_corner,
TiGetBottomType(tpr)))
{
errRect.r_ytop += cdist;
if (DRCEuclidean)
arg->dCD_radial |= RADIAL_NW;
}
}
}
/*
* Just for grins, see if we could avoid a messy search
* by looking only at tpleft.
*/
errRect.r_xbot = edgeX - dist;
if (cptr->drcc_flags & DRC_OUTSIDE) errRect.r_xbot--;
if (LEFT(tpleft) <= errRect.r_xbot
&& BOTTOM(tpleft) <= errRect.r_ybot
&& TOP(tpleft) >= errRect.r_ytop
&& arg->dCD_plane == cptr->drcc_plane
&& TTMaskHasType(&cptr->drcc_mask,
TiGetRightType(tpleft)))
continue;
errRect.r_xtop = edgeX;
if (cptr->drcc_flags & DRC_OUTSIDE) errRect.r_xtop -= dist;
arg->dCD_initial = tile;
}
else /* FORWARD */
{
/*
* Determine corner extensions.
*/
/* Find the point (1) to the top left of pointT */
if (TOP(tpleft) <= errRect.r_ytop) tpl = RT(tpleft);
else tpl = tpleft;
/* Also find point (4) to check for edge continuation */
if (TOP(tile) <= errRect.r_ytop)
for (tpr = RT(tile); LEFT(tpr) > edgeX; tpr = BL(tpr));
else tpr = tile;
/* Make sure the edge stops at edgeTop */
if ((TiGetRightType(tpl) != TiGetRightType(tpleft)) ||
(TiGetLeftType(tpr) != TiGetLeftType(tile)))
{
if (TTMaskHasType(&cptr->drcc_corner, TiGetBottomType(tpl)))
{
errRect.r_ytop += cdist;
if (DRCEuclidean)
arg->dCD_radial |= RADIAL_NE;
}
}
if (cptr->drcc_flags & DRC_BOTHCORNERS)
{
/*
* Check the other corner
*/
/* Find point (2) to the bottom left of pointB. */
if (BOTTOM(tpleft) >= errRect.r_ybot)
for (tpl = LB(tpleft); RIGHT(tpl) < edgeX; tpl = TR(tpl));
else tpl = tpleft;
/* Also find point (3) to check for edge continuation */
if (BOTTOM(tile) >= errRect.r_ybot) tpr = LB(tile);
else tpr = tile;
/* Make sure the edge stops at edgeBot */
if ((TiGetRightType(tpl) != TiGetRightType(tpleft)) ||
(TiGetLeftType(tpr) != TiGetLeftType(tile)))
{
if (TTMaskHasType(&cptr->drcc_corner, TiGetTopType(tpl)))
{
errRect.r_ybot -= cdist;
if (DRCEuclidean)
arg->dCD_radial |= RADIAL_SE;
}
}
}
/*
* Just for grins, see if we could avoid a messy search
* by looking only at tile.
*/
errRect.r_xtop = edgeX + dist;
if (cptr->drcc_flags & DRC_OUTSIDE) errRect.r_xtop++;
if (RIGHT(tile) >= errRect.r_xtop
&& BOTTOM(tile) <= errRect.r_ybot
&& TOP(tile) >= errRect.r_ytop
&& arg->dCD_plane == cptr->drcc_plane
&& TTMaskHasType(&cptr->drcc_mask,
TiGetLeftType(tile)))
continue;
errRect.r_xbot = edgeX;
if (cptr->drcc_flags & DRC_OUTSIDE) errRect.r_xbot += dist;
arg->dCD_initial= tpleft;
}
if (arg->dCD_radial)
{
arg->dCD_radial &= 0xf000;
arg->dCD_radial |= (0xfff & cdist);
}
DRCstatSlow++;
arg->dCD_cptr = cptr;
arg->dCD_entries = 0;
TTMaskCom2(&tmpMask, &cptr->drcc_mask);
TTMaskClearType(&tmpMask, TT_ERROR_S);
DBSrPaintArea((Tile *) NULL,
arg->dCD_celldef->cd_planes[cptr->drcc_plane],
&errRect, &tmpMask, areaCheck, (ClientData) arg);
} while (triggered);
if (arg->dCD_entries == 0)
{
/* Trigger rule: If rule check found errors, */
/* do the next rule. Otherwise, skip it. */
if (trigpending)
cptr = cptr->drcc_next;
}
else
triggered = arg->dCD_entries;
}
DRCstatEdges++;
firsttile = FALSE;
}
}
/*
* Check design rules along a horizontal boundary between two tiles.
*
* 4 tile 3
* --L----------------R--
* 1 tpbot 2
*
* The labels "L" and "R" indicate pointL and pointR respectively.
* If a rule's direction is FORWARD, then check from bottom to top.
*
* * Check the top left corner if the 1x1 lambda square on the bottom
* left corner (1) of pointL matches the design rule's "corner" mask.
*
* * Check the top right corner if the rule says check BOTHCORNERS and
* the 1x1 lambda square on the bottom right (2) corner of pointR
* matches the design rule's "corner" mask.
*
* If a rule's direction is REVERSE, then check from top to bottom.
*
* * Check the bottom right corner if the 1x1 lambda square on the top
* right corner (3) of pointR matches the design rule's "corner"
* mask.
*
* * Check the bottom left corner if the rule says check BOTHCORNERS
* and the 1x1 lambda square on the top left corner (4) of pointL
* matches the design rule's "corner" mask.
*/
if (BOTTOM(tile) >= rect->r_ybot)
{
Tile *tpbot, *tpx;
TileType tt, to;
int edgeLeft, edgeRight;
int left = MAX(LEFT(tile), rect->r_xbot);
int right = MIN(RIGHT(tile), rect->r_xtop);
int edgeY = BOTTOM(tile);
/* Go right across bottom of tile */
firsttile = TRUE;
mrd = NULL;
for (tpbot = LB(tile); LEFT(tpbot) < right; tpbot = TR(tpbot))
{
/* Get the tile types to the top and bottom of the edge */
tt = TiGetBottomType(tile);
to = TiGetTopType(tpbot);
/* Don't check synthetic edges, i.e. edges with same type on
* both sides. Such "edges" have no physical significance, and
* depend on internal-details of how paint is spit into tiles.
* Thus checking them just leads to confusion. (When edge rules
* involving such edges are encountered during technology readin
* the user is warned that such edges are not checked).
*/
if (tt == to) continue;
/*
* Check to insure that we are inside the clip area.
* Go through list of design rules triggered by the
* bottom-to-top edge.
*/
edgeLeft = MAX(LEFT(tpbot), left);
edgeRight = MIN(RIGHT(tpbot), right);
if (edgeLeft >= edgeRight)
continue;
triggered = 0;
for (cptr = DRCCurStyle->DRCRulesTbl[to][tt]; cptr != (DRCCookie *) NULL;
cptr = cptr->drcc_next)
{
/* DRC_ANGLES_90 and DRC_SPLITTILE rules are handled by */
/* the code above for non-Manhattan shapes and do not */
/* need to be processed again. */
if (cptr->drcc_flags & (DRC_ANGLES_90 | DRC_SPLITTILE))
continue;
/* Find the rule distances according to the scale factor */
dist = cptr->drcc_dist;
cdist = cptr->drcc_cdist;
trigpending = (cptr->drcc_flags & DRC_TRIGGER) ? TRUE : FALSE;
/* drcc_edgeplane is used to avoid checks on edges */
/* in more than one plane */
if (arg->dCD_plane != cptr->drcc_edgeplane)
{
if (trigpending) cptr = cptr->drcc_next;
continue;
}
DRCstatRules++;
/* top to bottom */
if ((cptr->drcc_flags & (DRC_MAXWIDTH | DRC_BENDS)) ==
(DRC_MAXWIDTH | DRC_BENDS))
{
/* New algorithm --- Tim 3/6/05 */
Rect *lr;
int i;
if (!trigpending || (DRCCurStyle->DRCFlags
& DRC_FLAGS_WIDEWIDTH_NONINCLUSIVE))
cptr->drcc_dist++;
if (cptr->drcc_flags & DRC_REVERSE)
mrd = drcCanonicalMaxwidth(tpbot, GEO_SOUTH, arg, cptr);
else if (firsttile)
mrd = drcCanonicalMaxwidth(tile, GEO_NORTH, arg, cptr);
if (!trigpending || (DRCCurStyle->DRCFlags
& DRC_FLAGS_WIDEWIDTH_NONINCLUSIVE))
cptr->drcc_dist--;
if (trigpending)
{
if (mrd)
{
if (cptr->drcc_cdist <= cptr->drcc_dist)
triggered = mrd->entries;
else if ((edgeRight - edgeLeft) >= cptr->drcc_cdist)
{
/* Run-length rule */
for (i = 0; i < mrd->entries; i++)
{
lr = &mrd->rlist[i];
GeoClip(lr, arg->dCD_clip);
if ((lr->r_xtop - lr->r_xbot) > cptr->drcc_cdist)
{
triggered = mrd->entries;
break;
}
}
if (i == mrd->entries)
cptr = cptr->drcc_next;
}
else
cptr = cptr->drcc_next;
}
else
cptr = cptr->drcc_next;
}
else if (mrd)
{
for (i = 0; i < mrd->entries; i++)
{
lr = &mrd->rlist[i];
GeoClip(lr, arg->dCD_clip);
if (!GEO_RECTNULL(lr))
{
(*(arg->dCD_function)) (arg->dCD_celldef,
lr, cptr, arg->dCD_clientData);
(*(arg->dCD_errors))++;
}
}
}
continue;
}
else if (cptr->drcc_flags & (DRC_AREA | DRC_RECTSIZE | DRC_MAXWIDTH))
{
/* only have to do these checks in one direction */
if (trigpending) cptr = cptr->drcc_next;
continue;
}
/* Off-grid checks apply only to edge */
if (cptr->drcc_flags & DRC_OFFGRID)
{
errRect.r_xtop = edgeRight;
errRect.r_xbot = edgeLeft;
errRect.r_ytop = errRect.r_ybot = edgeY;
arg->dCD_cptr = cptr;
drcCheckOffGrid(&errRect, arg, cptr);
continue;
}
result = 0;
arg->dCD_radial = 0;
arg->dCD_entries = 0;
do {
if (triggered)
{
/* For triggered rules, we want to look at the */
/* clipped region found by the triggering rule */
if (mrd)
errRect = mrd->rlist[--triggered];
else
errRect = arg->dCD_rlist[--triggered];
errRect.r_xtop += cdist;
errRect.r_xbot -= cdist;
if (errRect.r_xtop > edgeRight) errRect.r_xtop = edgeRight;
if (errRect.r_xbot < edgeLeft) errRect.r_xbot = edgeLeft;
}
else
{
errRect.r_xbot = edgeLeft;
errRect.r_xtop = edgeRight;
}
if (cptr->drcc_flags & DRC_REVERSE)
{
/*
* Determine corner extensions.
* Find the point (3) to the top right of pointR
*/
if (RIGHT(tile) <= errRect.r_xtop)
for (tpx = TR(tile); BOTTOM(tpx) > edgeY; tpx = LB(tpx));
else tpx = tile;
if (TTMaskHasType(&cptr->drcc_corner, TiGetBottomType(tpx)))
{
errRect.r_xtop += cdist;
if (DRCEuclidean)
arg->dCD_radial |= RADIAL_SE;
}
if (cptr->drcc_flags & DRC_BOTHCORNERS)
{
/*
* Check the other corner by finding the
* point (4) to the top left of pointL.
*/
if (LEFT(tile) >= errRect.r_xbot) tpx = BL(tile);
else tpx = tile;
if (TTMaskHasType(&cptr->drcc_corner, TiGetBottomType(tpx)))
{
errRect.r_xbot -= cdist;
if (DRCEuclidean)
arg->dCD_radial |= RADIAL_SW;
}
}
/*
* Just for grins, see if we could avoid
* a messy search by looking only at tpbot.
*/
errRect.r_ybot = edgeY - dist;
if (cptr->drcc_flags & DRC_OUTSIDE) errRect.r_ybot--;
if (BOTTOM(tpbot) <= errRect.r_ybot
&& LEFT(tpbot) <= errRect.r_xbot
&& RIGHT(tpbot) >= errRect.r_xtop
&& arg->dCD_plane == cptr->drcc_plane
&& TTMaskHasType(&cptr->drcc_mask,
TiGetTopType(tpbot)))
continue;
errRect.r_ytop = edgeY;
if (cptr->drcc_flags & DRC_OUTSIDE) errRect.r_ytop -= dist;
arg->dCD_initial = tile;
}
else /* FORWARD */
{
/*
* Determine corner extensions.
* Find the point (1) to the bottom left of pointL
*/
if (LEFT(tpbot) >= errRect.r_xbot)
for (tpx = BL(tpbot); TOP(tpx) < edgeY; tpx = RT(tpx));
else tpx = tpbot;
if (TTMaskHasType(&cptr->drcc_corner, TiGetTopType(tpx)))
{
errRect.r_xbot -= cdist;
if (DRCEuclidean)
arg->dCD_radial |= RADIAL_NW;
}
if (cptr->drcc_flags & DRC_BOTHCORNERS)
{
/*
* Check the other corner by finding the
* point (2) to the bottom right of pointR.
*/
if (RIGHT(tpbot) <= errRect.r_xtop) tpx = TR(tpbot);
else tpx = tpbot;
if (TTMaskHasType(&cptr->drcc_corner, TiGetTopType(tpx)))
{
errRect.r_xtop += cdist;
if (DRCEuclidean)
arg->dCD_radial |= RADIAL_NE;
}
}
/*
* Just for grins, see if we could avoid
* a messy search by looking only at tile.
*/
errRect.r_ytop = edgeY + dist;
if (cptr->drcc_flags & DRC_OUTSIDE) errRect.r_ytop++;
if (TOP(tile) >= errRect.r_ytop
&& LEFT(tile) <= errRect.r_xbot
&& RIGHT(tile) >= errRect.r_xtop
&& arg->dCD_plane == cptr->drcc_plane
&& TTMaskHasType(&cptr->drcc_mask,
TiGetBottomType(tile)))
continue;
errRect.r_ybot = edgeY;
if (cptr->drcc_flags & DRC_OUTSIDE) errRect.r_ybot += dist;
arg->dCD_initial = tpbot;
}
if (arg->dCD_radial)
{
arg->dCD_radial &= 0xf000;
arg->dCD_radial |= (0xfff & cdist);
}
DRCstatSlow++;
arg->dCD_cptr = cptr;
arg->dCD_entries = 0;
TTMaskCom2(&tmpMask, &cptr->drcc_mask);
TTMaskClearType(&tmpMask, TT_ERROR_S);
DBSrPaintArea((Tile *) NULL,
arg->dCD_celldef->cd_planes[cptr->drcc_plane],
&errRect, &tmpMask, areaCheck, (ClientData) arg);
} while (triggered);
if (arg->dCD_entries == 0)
{
/* Trigger rule: If rule check found errors, */
/* do the next rule. Otherwise, skip it. */
if (trigpending)
cptr = cptr->drcc_next;
}
else
triggered = arg->dCD_entries;
}
DRCstatEdges++;
firsttile = FALSE;
}
}
return (0);
}