magic/drc/DRCbasic.c

1334 lines
38 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();
/*
*-----------------------------------------------------------------------
*
* drcCifPointToSegment
*
* 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
drcCifPointToSegment(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 = (long) 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 = drcCifPointToSegment(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 = drcCifPointToSegment(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 = drcCifPointToSegment(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 = drcCifPointToSegment(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);
triggered = 0;
}
else if (firsttile)
{
mrd = drcCanonicalMaxwidth(tile, GEO_EAST, arg, cptr);
triggered = 0;
}
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);
triggered = 0;
}
else if (firsttile)
{
mrd = drcCanonicalMaxwidth(tile, GEO_NORTH, arg, cptr);
triggered = 0;
}
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);
}