2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* 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.
|
|
|
|
|
*
|
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/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()
|
2021-05-15 00:02:34 +02:00
|
|
|
#include <math.h> // for sqrt() for diagonal check
|
2026-03-10 19:25:02 +01:00
|
|
|
|
|
|
|
|
#include "tcltk/tclmagic.h"
|
2017-04-25 14:41:48 +02:00
|
|
|
#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"
|
2026-03-10 19:25:02 +01:00
|
|
|
#include "utils/undo.h"
|
2025-10-30 17:07:29 +01:00
|
|
|
#include "textio/textio.h"
|
2026-03-10 19:25:02 +01:00
|
|
|
#include "cif/CIFint.h"
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
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,
|
2024-10-04 18:02:09 +02:00
|
|
|
{ {0} }, { {0} },
|
2026-03-09 01:12:58 +01:00
|
|
|
0, DRC_EXCEPTION_NONE, 0, 0,
|
2020-02-25 19:57:41 +01:00
|
|
|
DRC_OVERLAP_TAG,
|
2017-04-25 14:41:48 +02:00
|
|
|
(DRCCookie *) NULL
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Forward references: */
|
|
|
|
|
|
|
|
|
|
extern int areaCheck();
|
|
|
|
|
extern int drcTile();
|
|
|
|
|
extern MaxRectsData *drcCanonicalMaxwidth();
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*-----------------------------------------------------------------------
|
|
|
|
|
*
|
2026-03-10 19:25:02 +01:00
|
|
|
* drcFoundOneFunc --
|
|
|
|
|
*
|
|
|
|
|
* Simple callback for a plane search on a mask-hint plane inside
|
|
|
|
|
* a DRC check area.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Return 1 always, indicating that a tile has been found in the
|
|
|
|
|
* DRC search area, and the search can end.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
*-----------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
drcFoundOneFunc(Tile *tile,
|
|
|
|
|
TileType dinfo,
|
|
|
|
|
ClientData cdata)
|
|
|
|
|
{
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*-----------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* drcCifPointToSegment --
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* 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
|
2024-04-06 22:36:31 +02:00
|
|
|
drcCifPointToSegment(px, py, s1x, s1y, s2x, s2y)
|
2017-04-25 14:41:48 +02:00
|
|
|
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
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
2020-05-23 23:13:14 +02:00
|
|
|
* areaCheck --
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* 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
|
2026-01-03 02:12:37 +01:00
|
|
|
areaCheck(tile, dinfo, arg)
|
2017-04-25 14:41:48 +02:00
|
|
|
Tile *tile;
|
2026-01-03 02:12:37 +01:00
|
|
|
TileType dinfo;
|
2017-04-25 14:41:48 +02:00
|
|
|
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
|
2023-08-27 20:21:02 +02:00
|
|
|
* the clip area for errors, unless this is a trigger rule, in
|
|
|
|
|
* which case it should be restricted only to the full check area.
|
2017-04-25 14:41:48 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (!(arg->dCD_cptr->drcc_flags & DRC_TRIGGER))
|
|
|
|
|
GeoClip(&rect, arg->dCD_clip);
|
2023-08-27 20:21:02 +02:00
|
|
|
else
|
|
|
|
|
GeoClip(&rect, arg->dCD_rect);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
GeoClip(&rect, arg->dCD_constraint);
|
|
|
|
|
if ((rect.r_xbot >= rect.r_xtop) || (rect.r_ybot >= rect.r_ytop))
|
|
|
|
|
return 0;
|
|
|
|
|
|
2024-12-12 20:10:03 +01:00
|
|
|
/* Run-length rules are ignored unless the width of the error
|
|
|
|
|
* exceeds the run-length distance (which is held in the
|
|
|
|
|
* drcc_cdist entry for the rule).
|
|
|
|
|
*/
|
|
|
|
|
if (arg->dCD_cptr->drcc_flags & DRC_RUNLENGTH)
|
|
|
|
|
if (((rect.r_xtop - rect.r_xbot) < arg->dCD_cptr->drcc_cdist) &&
|
|
|
|
|
((rect.r_ytop - rect.r_ybot) < arg->dCD_cptr->drcc_cdist))
|
|
|
|
|
return 0;
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
/*
|
2017-04-25 14:41:48 +02:00
|
|
|
* 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;
|
2024-09-30 08:12:08 +02:00
|
|
|
long sstest, ssdist = (long) sdist * sdist;
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
2026-01-03 02:12:37 +01:00
|
|
|
else if (IsSplit(tile) && !SplitDirection(tile) && !(dinfo & TT_SIDE))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2024-04-06 22:36:31 +02:00
|
|
|
sstest = drcCifPointToSegment(arg->dCD_constraint->r_xbot + sdist,
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
2026-01-03 02:12:37 +01:00
|
|
|
else if (IsSplit(tile) && SplitDirection(tile) && (dinfo & TT_SIDE))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2024-04-06 22:36:31 +02:00
|
|
|
sstest = drcCifPointToSegment(arg->dCD_constraint->r_xtop - sdist,
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
2026-01-03 02:12:37 +01:00
|
|
|
else if (IsSplit(tile) && SplitDirection(tile) && !(dinfo & TT_SIDE))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2024-04-06 22:36:31 +02:00
|
|
|
sstest = drcCifPointToSegment(arg->dCD_constraint->r_xbot + sdist,
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
2026-01-03 02:12:37 +01:00
|
|
|
else if (IsSplit(tile) && !SplitDirection(tile) && (dinfo & TT_SIDE))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2024-04-06 22:36:31 +02:00
|
|
|
sstest = drcCifPointToSegment(arg->dCD_constraint->r_xtop - sdist,
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
|
|
|
|
}
|
2022-04-20 22:16:20 +02:00
|
|
|
/* "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))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
(*(arg->dCD_function))(arg->dCD_celldef, &rect, arg->dCD_cptr,
|
|
|
|
|
arg->dCD_clientData);
|
|
|
|
|
(*(arg->dCD_errors))++;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2025-10-30 17:07:29 +01:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* areaNMReject ---
|
|
|
|
|
*
|
|
|
|
|
* Trivial callback function used by areaNMCheck to see if a tile
|
|
|
|
|
* found in the error area of a reverse non-manhattan check exists
|
|
|
|
|
* only on the other side of the original check boundary. If it
|
|
|
|
|
* is found in this search, return 1 to immediately stop the search.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Returns 1 if the tile indicated in the ClientData argument was
|
|
|
|
|
* found in the check area, otherwise return 0 to keep looking.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
2026-01-03 02:12:37 +01:00
|
|
|
areaNMReject(tile, dinfo, arg)
|
2025-10-30 17:07:29 +01:00
|
|
|
Tile *tile;
|
2026-01-03 02:12:37 +01:00
|
|
|
TileType dinfo;
|
2025-10-30 17:07:29 +01:00
|
|
|
ClientData *arg;
|
|
|
|
|
{
|
|
|
|
|
Tile *checktile = (Tile *)arg;
|
|
|
|
|
|
|
|
|
|
if (tile == checktile)
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-15 00:02:34 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* areaNMCheck ---
|
|
|
|
|
*
|
2025-10-30 17:07:29 +01:00
|
|
|
* Check for errors in triangular area of a tile.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Return 0 always to keep the search going.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* If the tile is not rejected due to being outside of the various
|
|
|
|
|
* clip areas, then call the function specified in the drcClientData
|
|
|
|
|
* argument.
|
2021-05-15 00:02:34 +02:00
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
2026-01-03 02:12:37 +01:00
|
|
|
areaNMCheck(tile, dinfo, arg)
|
2021-05-15 00:02:34 +02:00
|
|
|
Tile *tile;
|
2026-01-03 02:12:37 +01:00
|
|
|
TileType dinfo;
|
2021-05-15 00:02:34 +02:00
|
|
|
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);
|
2023-08-27 20:21:02 +02:00
|
|
|
else
|
|
|
|
|
GeoClip(&rect, arg->dCD_rect);
|
2021-05-15 00:02:34 +02:00
|
|
|
|
|
|
|
|
GeoClip(&rect, arg->dCD_constraint);
|
|
|
|
|
if ((rect.r_xbot >= rect.r_xtop) || (rect.r_ybot >= rect.r_ytop))
|
|
|
|
|
return 0;
|
|
|
|
|
|
2025-10-30 17:07:29 +01:00
|
|
|
if (arg->dCD_entries & TT_DIAGONAL)
|
|
|
|
|
{
|
|
|
|
|
TileTypeBitMask mask;
|
|
|
|
|
int dinfo = arg->dCD_entries;
|
|
|
|
|
|
|
|
|
|
/* In the DRC_REVERSE case, the area being searched extends
|
|
|
|
|
* behind the edge that triggered the DRC check, but any
|
|
|
|
|
* tile that is outside that edge should be ignored. This
|
|
|
|
|
* requires a separate check.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
TTMaskSetOnlyType(&mask, TiGetLeftType(tile));
|
|
|
|
|
TTMaskSetType(&mask, TiGetRightType(tile));
|
2026-01-18 02:49:52 +01:00
|
|
|
if (DBSrPaintNMArea((Tile *)tile, (Plane *)NULL,
|
|
|
|
|
TiGetTypeExact(tile) | dinfo, arg->dCD_rlist,
|
2025-10-30 17:07:29 +01:00
|
|
|
&mask, areaNMReject, (ClientData)tile) == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-15 00:02:34 +02:00
|
|
|
(*(arg->dCD_function))(arg->dCD_celldef, &rect, arg->dCD_cptr,
|
|
|
|
|
arg->dCD_clientData);
|
|
|
|
|
(*(arg->dCD_errors))++;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* 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
|
2018-03-15 15:51:07 +01:00
|
|
|
* area falls within clipRect. This routine makes a flat check:
|
2017-04-25 14:41:48 +02:00
|
|
|
* 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);
|
2026-03-10 19:25:02 +01:00
|
|
|
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
/* Execute pending Tcl events, so the DRC process doesn't block. */
|
2026-03-27 00:44:00 +01:00
|
|
|
|
|
|
|
|
/* WARNING: This code cannot be enabled until some method is
|
|
|
|
|
* worked out to determine if any event resulted in a change
|
|
|
|
|
* to the DRC check plane which would invalidate the current
|
|
|
|
|
* search. If so, the search must end immediately and the
|
|
|
|
|
* area being checked must be reinstated. The code was added
|
|
|
|
|
* to see how it speeds up the response time of magic when
|
|
|
|
|
* some of the DRC rules are compute-intensive. It speeds up
|
|
|
|
|
* performance enough that it is worthwhile to implement the
|
|
|
|
|
* method just mentioned.
|
|
|
|
|
*/
|
|
|
|
|
#if 0
|
2026-03-10 19:25:02 +01:00
|
|
|
UndoEnable();
|
|
|
|
|
while (Tcl_DoOneEvent(TCL_DONT_WAIT));
|
|
|
|
|
UndoDisable();
|
2026-03-27 00:44:00 +01:00
|
|
|
#endif
|
2026-03-10 19:25:02 +01:00
|
|
|
#endif
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
drcCifCheck(&arg);
|
|
|
|
|
if (arg.dCD_rlist != NULL) freeMagic(arg.dCD_rlist);
|
|
|
|
|
return (errors);
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
Fixed an error that was discovered with the drcCanonicalMaxwidth()
routine, which in turn affects various DRC rules like maxrect,
widespacing, and runlength spacing. drcTile() was computing
the drcCanonicalMaxwidth() result for the tile and reusing it,
failing to account for the fact that within the loop over
DRCCookies, other rules might require calling drcCanonicalMaxwidth()
on a neighboring tile, or on the same tile with a different width
requirement. Implemented a cached version, in which three results
are kept: One for neighboring tiles (which can never be reused on
the same edge), one for the first required use of the routine for
the tile, and one for any other use required for the tile. If
there are one or two such rules for an edge, then the routine will
work at maximum efficiency. If there are three rules, then one
will always be a cache hit and reduce the total amount of
computation, although it will still be doing a massive amount of
redundant computation. If this seems to be something that
occurs regularly, then it can be revisited. The existing
implementation works fine for all the open PDKs. Some more
advanced PDKs with a number of staged wide-spacing rules could
have issues.
2026-04-14 23:37:10 +02:00
|
|
|
/* Expect that keeping around 3 MaxRectsData records should be sufficient
|
|
|
|
|
* to avoid recomputing drcCanonicalMaxwidth() multiple times. Note that
|
|
|
|
|
* if a PDK sets up multiple rules on an edge which all require running
|
|
|
|
|
* drcCanonicalMaxwidth(), then this cache size may need to be revisited.
|
|
|
|
|
*/
|
|
|
|
|
#define MAXRECTSCACHE 3
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* 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
|
2026-01-03 02:12:37 +01:00
|
|
|
drcTile (tile, dinfo, arg)
|
|
|
|
|
Tile *tile; /* Tile being examined */
|
|
|
|
|
TileType dinfo; /* Split tile information */
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
|
|
|
|
|
Fixed an error that was discovered with the drcCanonicalMaxwidth()
routine, which in turn affects various DRC rules like maxrect,
widespacing, and runlength spacing. drcTile() was computing
the drcCanonicalMaxwidth() result for the tile and reusing it,
failing to account for the fact that within the loop over
DRCCookies, other rules might require calling drcCanonicalMaxwidth()
on a neighboring tile, or on the same tile with a different width
requirement. Implemented a cached version, in which three results
are kept: One for neighboring tiles (which can never be reused on
the same edge), one for the first required use of the routine for
the tile, and one for any other use required for the tile. If
there are one or two such rules for an edge, then the routine will
work at maximum efficiency. If there are three rules, then one
will always be a cache hit and reduce the total amount of
computation, although it will still be doing a massive amount of
redundant computation. If this seems to be something that
occurs regularly, then it can be revisited. The existing
implementation works fine for all the open PDKs. Some more
advanced PDKs with a number of staged wide-spacing rules could
have issues.
2026-04-14 23:37:10 +02:00
|
|
|
/* Keep up to three MaxRectsData records to avoid doing the same
|
|
|
|
|
* expensive computation more than once.
|
|
|
|
|
*
|
|
|
|
|
* mrdcache[0] will be used for the tpleft tile, since it will never
|
|
|
|
|
* be reused. mrdcache[1] and mrdcache[2] will be used for the tile
|
|
|
|
|
* itself. Note that if more than 2 DRCCookie entries for the same
|
|
|
|
|
* edge require drcCanonicalMaxwidth(), then mrdcache[2] will be
|
|
|
|
|
* re-used so that at least mrdcache[1] is always a cache hit.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static MaxRectsData *mrdcache[MAXRECTSCACHE] = {NULL, NULL, NULL};
|
|
|
|
|
DRCCookie *cptrcache;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
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.
|
|
|
|
|
*/
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
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))++;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-17 16:55:50 +01:00
|
|
|
/* 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. */
|
|
|
|
|
|
2026-01-03 02:12:37 +01:00
|
|
|
if (IsSplit(tile) && !(dinfo & TT_SIDE))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2021-05-15 00:02:34 +02:00
|
|
|
int deltax, deltay;
|
|
|
|
|
TileType tt, to;
|
|
|
|
|
|
2021-11-17 16:55:50 +01:00
|
|
|
tt = TiGetLeftType(tile); /* inside type */
|
|
|
|
|
to = TiGetRightType(tile); /* outside type */
|
2021-05-15 00:02:34 +02:00
|
|
|
|
2025-08-06 15:22:12 +02:00
|
|
|
/* Check rules for DRC_ANGLES_90 rule and process */
|
|
|
|
|
for (cptr = DRCCurStyle->DRCRulesTbl[to][tt];
|
|
|
|
|
cptr != (DRCCookie *) NULL; cptr = cptr->drcc_next)
|
|
|
|
|
if (cptr->drcc_flags & DRC_ANGLES_90)
|
|
|
|
|
{
|
|
|
|
|
drcCheckAngles(tile, arg, cptr);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-15 00:02:34 +02:00
|
|
|
for (cptr = DRCCurStyle->DRCRulesTbl[to][tt]; cptr != (DRCCookie *) NULL;
|
|
|
|
|
cptr = cptr->drcc_next)
|
|
|
|
|
{
|
|
|
|
|
int deltax, deltay, w, h;
|
|
|
|
|
double r;
|
2026-01-03 02:12:37 +01:00
|
|
|
TileType newdinfo, dsplit;
|
2021-05-15 00:02:34 +02:00
|
|
|
|
|
|
|
|
/* 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. */
|
|
|
|
|
|
2022-04-20 22:16:20 +02:00
|
|
|
if (cptr->drcc_flags & (DRC_ANGLES_90 | DRC_AREA | DRC_MAXWIDTH
|
2021-05-15 00:02:34 +02:00
|
|
|
| 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;
|
|
|
|
|
|
2021-11-17 16:55:50 +01:00
|
|
|
if (SplitDirection(tile) == 0) deltay = -deltay;
|
2021-05-15 00:02:34 +02:00
|
|
|
|
2026-01-03 02:12:37 +01:00
|
|
|
newdinfo = TiGetTypeExact(tile) & (TT_DIAGONAL | TT_DIRECTION);
|
2021-05-15 00:02:34 +02:00
|
|
|
if (!(cptr->drcc_flags & DRC_REVERSE))
|
|
|
|
|
{
|
|
|
|
|
/* Forward case is behind the triangle */
|
|
|
|
|
deltax = -deltax;
|
|
|
|
|
deltay = -deltay;
|
2021-11-17 16:55:50 +01:00
|
|
|
|
2021-05-15 00:02:34 +02:00
|
|
|
/* Split side changes in the reverse case */
|
2026-01-03 02:12:37 +01:00
|
|
|
newdinfo |= TT_SIDE;
|
2021-05-15 00:02:34 +02:00
|
|
|
}
|
2025-10-30 17:37:00 +01:00
|
|
|
|
|
|
|
|
/* The area to check is bounded between the diagonals of
|
|
|
|
|
* tile and errRect (which is the tile area, offset).
|
2026-01-03 02:12:37 +01:00
|
|
|
* Pass errRect and newdinfo to areaNMCheck using the
|
2025-10-30 17:37:00 +01:00
|
|
|
* ClientData structure arg->dCD_rlist and arg->dCD_entries,
|
|
|
|
|
* which are not used by areaNMCheck.
|
|
|
|
|
*/
|
|
|
|
|
arg->dCD_rlist = (Rect *)mallocMagic(sizeof(Rect));
|
|
|
|
|
*(arg->dCD_rlist) = errRect;
|
2026-01-03 02:12:37 +01:00
|
|
|
arg->dCD_entries = newdinfo;
|
|
|
|
|
if (newdinfo & TT_SIDE)
|
2025-10-30 17:37:00 +01:00
|
|
|
arg->dCD_entries &= ~TT_SIDE;
|
2025-10-30 17:07:29 +01:00
|
|
|
else
|
2025-10-30 17:37:00 +01:00
|
|
|
arg->dCD_entries |= TT_SIDE;
|
2021-05-15 00:02:34 +02:00
|
|
|
|
|
|
|
|
/* 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,
|
2026-01-03 02:12:37 +01:00
|
|
|
arg->dCD_celldef->cd_planes[cptr->drcc_plane], newdinfo,
|
2021-05-15 00:02:34 +02:00
|
|
|
&errRect, &tmpMask, areaNMCheck, (ClientData) arg);
|
2025-10-30 17:07:29 +01:00
|
|
|
|
2025-10-30 17:37:00 +01:00
|
|
|
arg->dCD_entries = 0;
|
|
|
|
|
freeMagic(arg->dCD_rlist);
|
|
|
|
|
arg->dCD_rlist = (Rect *)NULL;
|
2021-05-15 00:02:34 +02:00
|
|
|
}
|
|
|
|
|
DRCstatEdges++;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
Fixed an error that was discovered with the drcCanonicalMaxwidth()
routine, which in turn affects various DRC rules like maxrect,
widespacing, and runlength spacing. drcTile() was computing
the drcCanonicalMaxwidth() result for the tile and reusing it,
failing to account for the fact that within the loop over
DRCCookies, other rules might require calling drcCanonicalMaxwidth()
on a neighboring tile, or on the same tile with a different width
requirement. Implemented a cached version, in which three results
are kept: One for neighboring tiles (which can never be reused on
the same edge), one for the first required use of the routine for
the tile, and one for any other use required for the tile. If
there are one or two such rules for an edge, then the routine will
work at maximum efficiency. If there are three rules, then one
will always be a cache hit and reduce the total amount of
computation, although it will still be doing a massive amount of
redundant computation. If this seems to be something that
occurs regularly, then it can be revisited. The existing
implementation works fine for all the open PDKs. Some more
advanced PDKs with a number of staged wide-spacing rules could
have issues.
2026-04-14 23:37:10 +02:00
|
|
|
cptrcache = NULL;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* 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;
|
2019-11-01 17:01:07 +01:00
|
|
|
mrd = NULL;
|
2017-04-25 14:41:48 +02:00
|
|
|
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)
|
|
|
|
|
{
|
2026-03-09 01:12:58 +01:00
|
|
|
/* Handle rule exceptions and exemptions */
|
2026-03-24 14:37:50 +01:00
|
|
|
if (cptr->drcc_exception != DRC_EXCEPTION_NONE)
|
2026-03-09 01:12:58 +01:00
|
|
|
{
|
|
|
|
|
PropertyRecord *proprec;
|
2026-03-09 19:40:53 +01:00
|
|
|
bool propfound, isinside = FALSE;
|
2026-03-09 01:12:58 +01:00
|
|
|
char *name;
|
2026-03-24 14:37:50 +01:00
|
|
|
int idx = cptr->drcc_exception & ~DRC_EXCEPTION_MASK;
|
2026-03-09 01:12:58 +01:00
|
|
|
name = DRCCurStyle->DRCExceptionList[idx];
|
|
|
|
|
|
|
|
|
|
/* Is there any exception area defined? */
|
|
|
|
|
proprec = DBPropGet(arg->dCD_celldef, name, &propfound);
|
|
|
|
|
|
|
|
|
|
/* If an exception area exists, is the error edge inside? */
|
|
|
|
|
if (propfound)
|
|
|
|
|
{
|
2026-03-10 19:25:02 +01:00
|
|
|
Rect redge;
|
2026-03-09 01:12:58 +01:00
|
|
|
|
|
|
|
|
redge.r_xbot = redge.r_xtop = edgeX;
|
|
|
|
|
redge.r_ybot = edgeBot;
|
|
|
|
|
redge.r_ytop = edgeTop;
|
|
|
|
|
|
2026-03-10 19:25:02 +01:00
|
|
|
if (DBSrPaintArea(PlaneGetHint(proprec->prop_value.prop_plane),
|
|
|
|
|
proprec->prop_value.prop_plane,
|
|
|
|
|
&redge, &CIFSolidBits, drcFoundOneFunc,
|
|
|
|
|
(ClientData)NULL) == 1)
|
|
|
|
|
isinside = TRUE;
|
2026-03-09 01:12:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Exemption rules are ignored if the edge is inside
|
|
|
|
|
* an exception area. Exception rules are ignored if
|
|
|
|
|
* the edge is outside an exception area.
|
|
|
|
|
*/
|
2026-03-25 19:07:54 +01:00
|
|
|
if (!isinside && (!(cptr->drcc_exception & DRC_EXCEPTION_MASK) == 0))
|
2026-03-24 14:37:50 +01:00
|
|
|
continue;
|
2026-03-25 19:07:54 +01:00
|
|
|
if (isinside && ((cptr->drcc_exception & DRC_EXCEPTION_MASK) != 0))
|
2026-03-24 14:37:50 +01:00
|
|
|
continue;
|
2026-03-09 01:12:58 +01:00
|
|
|
}
|
|
|
|
|
|
2022-04-20 23:12:58 +02:00
|
|
|
/* 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;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
2018-09-19 23:04:13 +02:00
|
|
|
if (!trigpending || (DRCCurStyle->DRCFlags
|
|
|
|
|
& DRC_FLAGS_WIDEWIDTH_NONINCLUSIVE))
|
|
|
|
|
cptr->drcc_dist++;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if (cptr->drcc_flags & DRC_REVERSE)
|
2024-10-02 03:14:12 +02:00
|
|
|
{
|
Fixed an error that was discovered with the drcCanonicalMaxwidth()
routine, which in turn affects various DRC rules like maxrect,
widespacing, and runlength spacing. drcTile() was computing
the drcCanonicalMaxwidth() result for the tile and reusing it,
failing to account for the fact that within the loop over
DRCCookies, other rules might require calling drcCanonicalMaxwidth()
on a neighboring tile, or on the same tile with a different width
requirement. Implemented a cached version, in which three results
are kept: One for neighboring tiles (which can never be reused on
the same edge), one for the first required use of the routine for
the tile, and one for any other use required for the tile. If
there are one or two such rules for an edge, then the routine will
work at maximum efficiency. If there are three rules, then one
will always be a cache hit and reduce the total amount of
computation, although it will still be doing a massive amount of
redundant computation. If this seems to be something that
occurs regularly, then it can be revisited. The existing
implementation works fine for all the open PDKs. Some more
advanced PDKs with a number of staged wide-spacing rules could
have issues.
2026-04-14 23:37:10 +02:00
|
|
|
mrd = drcCanonicalMaxwidth(tpleft, GEO_WEST, arg, cptr,
|
|
|
|
|
&mrdcache[0]);
|
2024-10-02 03:14:12 +02:00
|
|
|
triggered = 0;
|
|
|
|
|
}
|
Fixed an error that was discovered with the drcCanonicalMaxwidth()
routine, which in turn affects various DRC rules like maxrect,
widespacing, and runlength spacing. drcTile() was computing
the drcCanonicalMaxwidth() result for the tile and reusing it,
failing to account for the fact that within the loop over
DRCCookies, other rules might require calling drcCanonicalMaxwidth()
on a neighboring tile, or on the same tile with a different width
requirement. Implemented a cached version, in which three results
are kept: One for neighboring tiles (which can never be reused on
the same edge), one for the first required use of the routine for
the tile, and one for any other use required for the tile. If
there are one or two such rules for an edge, then the routine will
work at maximum efficiency. If there are three rules, then one
will always be a cache hit and reduce the total amount of
computation, although it will still be doing a massive amount of
redundant computation. If this seems to be something that
occurs regularly, then it can be revisited. The existing
implementation works fine for all the open PDKs. Some more
advanced PDKs with a number of staged wide-spacing rules could
have issues.
2026-04-14 23:37:10 +02:00
|
|
|
else
|
2024-10-02 03:14:12 +02:00
|
|
|
{
|
Fixed an error that was discovered with the drcCanonicalMaxwidth()
routine, which in turn affects various DRC rules like maxrect,
widespacing, and runlength spacing. drcTile() was computing
the drcCanonicalMaxwidth() result for the tile and reusing it,
failing to account for the fact that within the loop over
DRCCookies, other rules might require calling drcCanonicalMaxwidth()
on a neighboring tile, or on the same tile with a different width
requirement. Implemented a cached version, in which three results
are kept: One for neighboring tiles (which can never be reused on
the same edge), one for the first required use of the routine for
the tile, and one for any other use required for the tile. If
there are one or two such rules for an edge, then the routine will
work at maximum efficiency. If there are three rules, then one
will always be a cache hit and reduce the total amount of
computation, although it will still be doing a massive amount of
redundant computation. If this seems to be something that
occurs regularly, then it can be revisited. The existing
implementation works fine for all the open PDKs. Some more
advanced PDKs with a number of staged wide-spacing rules could
have issues.
2026-04-14 23:37:10 +02:00
|
|
|
if (cptrcache == NULL)
|
|
|
|
|
{
|
|
|
|
|
mrd = drcCanonicalMaxwidth(tile, GEO_EAST, arg, cptr,
|
|
|
|
|
&mrdcache[1]);
|
|
|
|
|
cptrcache = cptr;
|
|
|
|
|
}
|
|
|
|
|
else if (cptrcache != cptr)
|
|
|
|
|
mrd = drcCanonicalMaxwidth(tile, GEO_EAST, arg, cptr,
|
|
|
|
|
&mrdcache[2]);
|
|
|
|
|
else
|
|
|
|
|
mrd = mrdcache[1];
|
2024-10-02 03:14:12 +02:00
|
|
|
triggered = 0;
|
|
|
|
|
}
|
2018-09-19 23:04:13 +02:00
|
|
|
if (!trigpending || (DRCCurStyle->DRCFlags
|
|
|
|
|
& DRC_FLAGS_WIDEWIDTH_NONINCLUSIVE))
|
|
|
|
|
cptr->drcc_dist--;
|
2017-04-25 14:41:48 +02:00
|
|
|
if (trigpending)
|
|
|
|
|
{
|
|
|
|
|
if (mrd)
|
2018-09-19 23:04:13 +02:00
|
|
|
{
|
2018-09-21 20:42:59 +02:00
|
|
|
if (cptr->drcc_cdist <= cptr->drcc_dist)
|
2018-09-19 23:04:13 +02:00
|
|
|
triggered = mrd->entries;
|
2018-09-21 21:18:33 +02:00
|
|
|
else if ((edgeTop - edgeBot) >= cptr->drcc_cdist)
|
2018-09-21 20:42:59 +02:00
|
|
|
{
|
|
|
|
|
/* Run-length rule */
|
|
|
|
|
for (i = 0; i < mrd->entries; i++)
|
|
|
|
|
{
|
|
|
|
|
lr = &mrd->rlist[i];
|
|
|
|
|
if ((lr->r_ytop - lr->r_ybot) > cptr->drcc_cdist)
|
|
|
|
|
{
|
|
|
|
|
triggered = mrd->entries;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (i == mrd->entries)
|
|
|
|
|
cptr = cptr->drcc_next;
|
|
|
|
|
}
|
2018-09-21 21:18:33 +02:00
|
|
|
else
|
|
|
|
|
cptr = cptr->drcc_next;
|
2018-09-19 23:04:13 +02:00
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
else
|
|
|
|
|
cptr = cptr->drcc_next;
|
|
|
|
|
}
|
|
|
|
|
else if (mrd)
|
|
|
|
|
{
|
|
|
|
|
for (i = 0; i < mrd->entries; i++)
|
|
|
|
|
{
|
|
|
|
|
lr = &mrd->rlist[i];
|
|
|
|
|
GeoClip(lr, arg->dCD_clip);
|
2020-05-23 23:13:14 +02:00
|
|
|
if (!GEO_RECTNULL(lr))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
(*(arg->dCD_function)) (arg->dCD_celldef,
|
|
|
|
|
lr, cptr, arg->dCD_clientData);
|
|
|
|
|
(*(arg->dCD_errors))++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
else if (cptr->drcc_flags & DRC_MAXWIDTH)
|
|
|
|
|
{
|
2021-02-18 02:53:17 +01:00
|
|
|
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);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
|
|
|
|
}
|
2021-02-04 23:35:43 +01:00
|
|
|
/* 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;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
result = 0;
|
2020-05-23 23:13:14 +02:00
|
|
|
arg->dCD_radial = 0;
|
2019-11-11 21:56:17 +01:00
|
|
|
arg->dCD_entries = 0;
|
2017-04-25 14:41:48 +02:00
|
|
|
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 */
|
2021-05-15 00:02:34 +02:00
|
|
|
if ((TiGetRightType(tpl) != TiGetRightType(tpleft)) ||
|
|
|
|
|
(TiGetLeftType(tpr) != TiGetLeftType(tile)))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
if (TTMaskHasType(&cptr->drcc_corner, TiGetTopType(tpr)))
|
|
|
|
|
{
|
|
|
|
|
errRect.r_ybot -= cdist;
|
2025-08-13 17:34:29 +02:00
|
|
|
if (DRCEuclidean && !(cptr->drcc_flags & DRC_MANHATTAN))
|
2017-04-25 14:41:48 +02:00
|
|
|
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 */
|
2021-05-15 00:02:34 +02:00
|
|
|
if ((TiGetRightType(tpl) != TiGetRightType(tpleft)) ||
|
|
|
|
|
(TiGetLeftType(tpr) != TiGetLeftType(tile)))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
if (TTMaskHasType(&cptr->drcc_corner,
|
|
|
|
|
TiGetBottomType(tpr)))
|
|
|
|
|
{
|
|
|
|
|
errRect.r_ytop += cdist;
|
2025-08-13 17:34:29 +02:00
|
|
|
if (DRCEuclidean &&
|
|
|
|
|
!(cptr->drcc_flags & DRC_MANHATTAN))
|
2017-04-25 14:41:48 +02:00
|
|
|
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 */
|
2021-05-15 00:02:34 +02:00
|
|
|
if ((TiGetRightType(tpl) != TiGetRightType(tpleft)) ||
|
|
|
|
|
(TiGetLeftType(tpr) != TiGetLeftType(tile)))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
if (TTMaskHasType(&cptr->drcc_corner, TiGetBottomType(tpl)))
|
|
|
|
|
{
|
|
|
|
|
errRect.r_ytop += cdist;
|
2025-08-13 17:34:29 +02:00
|
|
|
if (DRCEuclidean && !(cptr->drcc_flags & DRC_MANHATTAN))
|
2017-04-25 14:41:48 +02:00
|
|
|
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 */
|
2021-05-15 00:02:34 +02:00
|
|
|
if ((TiGetRightType(tpl) != TiGetRightType(tpleft)) ||
|
|
|
|
|
(TiGetLeftType(tpr) != TiGetLeftType(tile)))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
if (TTMaskHasType(&cptr->drcc_corner, TiGetTopType(tpl)))
|
|
|
|
|
{
|
|
|
|
|
errRect.r_ybot -= cdist;
|
2025-08-13 17:34:29 +02:00
|
|
|
if (DRCEuclidean &&
|
|
|
|
|
!(cptr->drcc_flags & DRC_MANHATTAN))
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Fixed an error that was discovered with the drcCanonicalMaxwidth()
routine, which in turn affects various DRC rules like maxrect,
widespacing, and runlength spacing. drcTile() was computing
the drcCanonicalMaxwidth() result for the tile and reusing it,
failing to account for the fact that within the loop over
DRCCookies, other rules might require calling drcCanonicalMaxwidth()
on a neighboring tile, or on the same tile with a different width
requirement. Implemented a cached version, in which three results
are kept: One for neighboring tiles (which can never be reused on
the same edge), one for the first required use of the routine for
the tile, and one for any other use required for the tile. If
there are one or two such rules for an edge, then the routine will
work at maximum efficiency. If there are three rules, then one
will always be a cache hit and reduce the total amount of
computation, although it will still be doing a massive amount of
redundant computation. If this seems to be something that
occurs regularly, then it can be revisited. The existing
implementation works fine for all the open PDKs. Some more
advanced PDKs with a number of staged wide-spacing rules could
have issues.
2026-04-14 23:37:10 +02:00
|
|
|
cptrcache = NULL;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* 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;
|
2019-11-01 17:01:07 +01:00
|
|
|
mrd = NULL;
|
2017-04-25 14:41:48 +02:00
|
|
|
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)
|
|
|
|
|
{
|
2026-03-09 01:12:58 +01:00
|
|
|
/* Handle rule exceptions and exemptions */
|
2026-03-24 14:37:50 +01:00
|
|
|
if (cptr->drcc_exception != DRC_EXCEPTION_NONE)
|
2026-03-09 01:12:58 +01:00
|
|
|
{
|
|
|
|
|
PropertyRecord *proprec;
|
2026-03-09 19:40:53 +01:00
|
|
|
bool propfound, isinside = FALSE;
|
2026-03-09 01:12:58 +01:00
|
|
|
char *name;
|
2026-03-24 14:37:50 +01:00
|
|
|
int idx = cptr->drcc_exception & ~DRC_EXCEPTION_MASK;
|
2026-03-09 01:12:58 +01:00
|
|
|
name = DRCCurStyle->DRCExceptionList[idx];
|
|
|
|
|
|
|
|
|
|
/* Is there any exception area defined? */
|
|
|
|
|
proprec = DBPropGet(arg->dCD_celldef, name, &propfound);
|
|
|
|
|
|
|
|
|
|
/* If an exception area exists, is the error edge inside? */
|
|
|
|
|
if (propfound)
|
|
|
|
|
{
|
2026-03-10 19:25:02 +01:00
|
|
|
Rect redge;
|
2026-03-09 01:12:58 +01:00
|
|
|
|
|
|
|
|
redge.r_ybot = redge.r_ytop = edgeY;
|
|
|
|
|
redge.r_xbot = edgeLeft;
|
|
|
|
|
redge.r_xtop = edgeRight;
|
|
|
|
|
|
2026-03-10 19:25:02 +01:00
|
|
|
if (DBSrPaintArea(PlaneGetHint(proprec->prop_value.prop_plane),
|
|
|
|
|
proprec->prop_value.prop_plane,
|
|
|
|
|
&redge, &CIFSolidBits, drcFoundOneFunc,
|
|
|
|
|
(ClientData)NULL) == 1)
|
|
|
|
|
isinside = TRUE;
|
2026-03-09 01:12:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Exemption rules are ignored if the edge is inside
|
|
|
|
|
* an exception area. Exception rules are ignored if
|
|
|
|
|
* the edge is outside an exception area.
|
|
|
|
|
*/
|
2026-03-25 19:07:54 +01:00
|
|
|
if (!isinside && ((cptr->drcc_exception & DRC_EXCEPTION_MASK) == 0))
|
2026-03-24 14:37:50 +01:00
|
|
|
continue;
|
2026-03-25 19:07:54 +01:00
|
|
|
if (isinside && ((cptr->drcc_exception & DRC_EXCEPTION_MASK) != 0))
|
2026-03-24 14:37:50 +01:00
|
|
|
continue;
|
2026-03-09 01:12:58 +01:00
|
|
|
}
|
|
|
|
|
|
2022-04-20 23:12:58 +02:00
|
|
|
/* 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;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* Find the rule distances according to the scale factor */
|
|
|
|
|
dist = cptr->drcc_dist;
|
2024-12-12 20:10:03 +01:00
|
|
|
cdist = (cptr->drcc_flags & DRC_RUNLENGTH) ? 0 : cptr->drcc_cdist;
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
|
|
|
|
|
2018-09-19 23:04:13 +02:00
|
|
|
if (!trigpending || (DRCCurStyle->DRCFlags
|
|
|
|
|
& DRC_FLAGS_WIDEWIDTH_NONINCLUSIVE))
|
|
|
|
|
cptr->drcc_dist++;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if (cptr->drcc_flags & DRC_REVERSE)
|
2024-10-02 03:14:12 +02:00
|
|
|
{
|
Fixed an error that was discovered with the drcCanonicalMaxwidth()
routine, which in turn affects various DRC rules like maxrect,
widespacing, and runlength spacing. drcTile() was computing
the drcCanonicalMaxwidth() result for the tile and reusing it,
failing to account for the fact that within the loop over
DRCCookies, other rules might require calling drcCanonicalMaxwidth()
on a neighboring tile, or on the same tile with a different width
requirement. Implemented a cached version, in which three results
are kept: One for neighboring tiles (which can never be reused on
the same edge), one for the first required use of the routine for
the tile, and one for any other use required for the tile. If
there are one or two such rules for an edge, then the routine will
work at maximum efficiency. If there are three rules, then one
will always be a cache hit and reduce the total amount of
computation, although it will still be doing a massive amount of
redundant computation. If this seems to be something that
occurs regularly, then it can be revisited. The existing
implementation works fine for all the open PDKs. Some more
advanced PDKs with a number of staged wide-spacing rules could
have issues.
2026-04-14 23:37:10 +02:00
|
|
|
mrd = drcCanonicalMaxwidth(tpbot, GEO_SOUTH, arg, cptr,
|
|
|
|
|
&mrdcache[0]);
|
2024-10-02 03:14:12 +02:00
|
|
|
triggered = 0;
|
|
|
|
|
}
|
Fixed an error that was discovered with the drcCanonicalMaxwidth()
routine, which in turn affects various DRC rules like maxrect,
widespacing, and runlength spacing. drcTile() was computing
the drcCanonicalMaxwidth() result for the tile and reusing it,
failing to account for the fact that within the loop over
DRCCookies, other rules might require calling drcCanonicalMaxwidth()
on a neighboring tile, or on the same tile with a different width
requirement. Implemented a cached version, in which three results
are kept: One for neighboring tiles (which can never be reused on
the same edge), one for the first required use of the routine for
the tile, and one for any other use required for the tile. If
there are one or two such rules for an edge, then the routine will
work at maximum efficiency. If there are three rules, then one
will always be a cache hit and reduce the total amount of
computation, although it will still be doing a massive amount of
redundant computation. If this seems to be something that
occurs regularly, then it can be revisited. The existing
implementation works fine for all the open PDKs. Some more
advanced PDKs with a number of staged wide-spacing rules could
have issues.
2026-04-14 23:37:10 +02:00
|
|
|
else
|
2024-10-02 03:14:12 +02:00
|
|
|
{
|
Fixed an error that was discovered with the drcCanonicalMaxwidth()
routine, which in turn affects various DRC rules like maxrect,
widespacing, and runlength spacing. drcTile() was computing
the drcCanonicalMaxwidth() result for the tile and reusing it,
failing to account for the fact that within the loop over
DRCCookies, other rules might require calling drcCanonicalMaxwidth()
on a neighboring tile, or on the same tile with a different width
requirement. Implemented a cached version, in which three results
are kept: One for neighboring tiles (which can never be reused on
the same edge), one for the first required use of the routine for
the tile, and one for any other use required for the tile. If
there are one or two such rules for an edge, then the routine will
work at maximum efficiency. If there are three rules, then one
will always be a cache hit and reduce the total amount of
computation, although it will still be doing a massive amount of
redundant computation. If this seems to be something that
occurs regularly, then it can be revisited. The existing
implementation works fine for all the open PDKs. Some more
advanced PDKs with a number of staged wide-spacing rules could
have issues.
2026-04-14 23:37:10 +02:00
|
|
|
if (cptrcache == NULL)
|
|
|
|
|
{
|
|
|
|
|
mrd = drcCanonicalMaxwidth(tile, GEO_NORTH, arg, cptr,
|
|
|
|
|
&mrdcache[1]);
|
|
|
|
|
cptrcache = cptr;
|
|
|
|
|
}
|
|
|
|
|
else if (cptrcache != cptr)
|
|
|
|
|
mrd = drcCanonicalMaxwidth(tile, GEO_NORTH, arg, cptr,
|
|
|
|
|
&mrdcache[2]);
|
|
|
|
|
else
|
|
|
|
|
mrd = mrdcache[1];
|
2024-10-02 03:14:12 +02:00
|
|
|
triggered = 0;
|
|
|
|
|
}
|
2018-09-19 23:04:13 +02:00
|
|
|
if (!trigpending || (DRCCurStyle->DRCFlags
|
|
|
|
|
& DRC_FLAGS_WIDEWIDTH_NONINCLUSIVE))
|
|
|
|
|
cptr->drcc_dist--;
|
2017-04-25 14:41:48 +02:00
|
|
|
if (trigpending)
|
|
|
|
|
{
|
|
|
|
|
if (mrd)
|
2018-09-21 21:18:33 +02:00
|
|
|
{
|
2018-09-21 20:42:59 +02:00
|
|
|
if (cptr->drcc_cdist <= cptr->drcc_dist)
|
2018-09-19 23:04:13 +02:00
|
|
|
triggered = mrd->entries;
|
2018-09-21 21:18:33 +02:00
|
|
|
else if ((edgeRight - edgeLeft) >= cptr->drcc_cdist)
|
2018-09-21 20:42:59 +02:00
|
|
|
{
|
|
|
|
|
/* Run-length rule */
|
|
|
|
|
for (i = 0; i < mrd->entries; i++)
|
|
|
|
|
{
|
|
|
|
|
lr = &mrd->rlist[i];
|
|
|
|
|
if ((lr->r_xtop - lr->r_xbot) > cptr->drcc_cdist)
|
|
|
|
|
{
|
|
|
|
|
triggered = mrd->entries;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (i == mrd->entries)
|
|
|
|
|
cptr = cptr->drcc_next;
|
|
|
|
|
}
|
2018-09-21 21:18:33 +02:00
|
|
|
else
|
|
|
|
|
cptr = cptr->drcc_next;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
else
|
|
|
|
|
cptr = cptr->drcc_next;
|
|
|
|
|
}
|
|
|
|
|
else if (mrd)
|
|
|
|
|
{
|
|
|
|
|
for (i = 0; i < mrd->entries; i++)
|
|
|
|
|
{
|
|
|
|
|
lr = &mrd->rlist[i];
|
|
|
|
|
GeoClip(lr, arg->dCD_clip);
|
2020-05-23 23:13:14 +02:00
|
|
|
if (!GEO_RECTNULL(lr))
|
2025-10-23 23:11:44 +02:00
|
|
|
{
|
2017-04-25 14:41:48 +02:00
|
|
|
(*(arg->dCD_function)) (arg->dCD_celldef,
|
|
|
|
|
lr, cptr, arg->dCD_clientData);
|
|
|
|
|
(*(arg->dCD_errors))++;
|
2020-05-23 23:13:14 +02:00
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2021-02-04 23:35:43 +01:00
|
|
|
else if (cptr->drcc_flags & (DRC_AREA | DRC_RECTSIZE | DRC_MAXWIDTH))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
/* only have to do these checks in one direction */
|
|
|
|
|
if (trigpending) cptr = cptr->drcc_next;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2021-02-04 23:35:43 +01:00
|
|
|
/* 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;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
result = 0;
|
2020-05-23 23:13:14 +02:00
|
|
|
arg->dCD_radial = 0;
|
2019-11-11 21:56:17 +01:00
|
|
|
arg->dCD_entries = 0;
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
|
|
|
|
|
2021-05-15 00:02:34 +02:00
|
|
|
if (TTMaskHasType(&cptr->drcc_corner, TiGetBottomType(tpx)))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
errRect.r_xtop += cdist;
|
2025-08-13 17:34:29 +02:00
|
|
|
if (DRCEuclidean && !(cptr->drcc_flags & DRC_MANHATTAN))
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
|
|
|
|
|
2021-05-15 00:02:34 +02:00
|
|
|
if (TTMaskHasType(&cptr->drcc_corner, TiGetBottomType(tpx)))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
errRect.r_xbot -= cdist;
|
2025-08-13 17:34:29 +02:00
|
|
|
if (DRCEuclidean && !(cptr->drcc_flags & DRC_MANHATTAN))
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
|
|
|
|
|
2021-05-15 00:02:34 +02:00
|
|
|
if (TTMaskHasType(&cptr->drcc_corner, TiGetTopType(tpx)))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
errRect.r_xbot -= cdist;
|
2025-08-13 17:34:29 +02:00
|
|
|
if (DRCEuclidean && !(cptr->drcc_flags & DRC_MANHATTAN))
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
|
|
|
|
|
2021-05-15 00:02:34 +02:00
|
|
|
if (TTMaskHasType(&cptr->drcc_corner, TiGetTopType(tpx)))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
errRect.r_xtop += cdist;
|
2025-08-13 17:34:29 +02:00
|
|
|
if (DRCEuclidean && !(cptr->drcc_flags & DRC_MANHATTAN))
|
2017-04-25 14:41:48 +02:00
|
|
|
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);
|
|
|
|
|
}
|