From d157eea7f3c397271a0b6d1e5a98b06101284a13 Mon Sep 17 00:00:00 2001 From: "R. Timothy Edwards" Date: Tue, 14 Apr 2026 17:37:10 -0400 Subject: [PATCH] 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. --- VERSION | 2 +- drc/DRCbasic.c | 58 ++++++++++++++++++++++++++++++++++++++++++++----- drc/DRCextend.c | 6 +++-- 3 files changed, 57 insertions(+), 9 deletions(-) diff --git a/VERSION b/VERSION index 24ae6434..f9f294c7 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.3.636 +8.3.637 diff --git a/drc/DRCbasic.c b/drc/DRCbasic.c index f548be34..d23dbe81 100644 --- a/drc/DRCbasic.c +++ b/drc/DRCbasic.c @@ -524,6 +524,13 @@ DRCBasicCheck (celldef, checkRect, clipRect, function, cdata) return (errors); } +/* 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 + /* * ---------------------------------------------------------------------------- * @@ -561,6 +568,19 @@ drcTile (tile, dinfo, arg) int triggered; int cdist, dist, ccdist, result; + /* 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; + arg->dCD_constraint = &errRect; /* @@ -702,6 +722,8 @@ drcTile (tile, dinfo, arg) DRCstatEdges++; } + cptrcache = NULL; + /* * Check design rules along a vertical boundary between two tiles. * @@ -857,12 +879,23 @@ drcTile (tile, dinfo, arg) if (cptr->drcc_flags & DRC_REVERSE) { - mrd = drcCanonicalMaxwidth(tpleft, GEO_WEST, arg, cptr); + mrd = drcCanonicalMaxwidth(tpleft, GEO_WEST, arg, cptr, + &mrdcache[0]); triggered = 0; } - else if (firsttile) + else { - mrd = drcCanonicalMaxwidth(tile, GEO_EAST, arg, cptr); + 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]; triggered = 0; } if (!trigpending || (DRCCurStyle->DRCFlags @@ -1153,6 +1186,8 @@ drcTile (tile, dinfo, arg) } } + cptrcache = NULL; + /* * Check design rules along a horizontal boundary between two tiles. * @@ -1299,12 +1334,23 @@ drcTile (tile, dinfo, arg) if (cptr->drcc_flags & DRC_REVERSE) { - mrd = drcCanonicalMaxwidth(tpbot, GEO_SOUTH, arg, cptr); + mrd = drcCanonicalMaxwidth(tpbot, GEO_SOUTH, arg, cptr, + &mrdcache[0]); triggered = 0; } - else if (firsttile) + else { - mrd = drcCanonicalMaxwidth(tile, GEO_NORTH, arg, cptr); + 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]; triggered = 0; } if (!trigpending || (DRCCurStyle->DRCFlags diff --git a/drc/DRCextend.c b/drc/DRCextend.c index 5855babb..6162070a 100644 --- a/drc/DRCextend.c +++ b/drc/DRCextend.c @@ -510,16 +510,17 @@ MaxRectsExclude( */ MaxRectsData * -drcCanonicalMaxwidth(starttile, dir, arg, cptr) +drcCanonicalMaxwidth(starttile, dir, arg, cptr, mrdptr) Tile *starttile; int dir; /* direction of rule */ struct drcClientData *arg; DRCCookie *cptr; + MaxRectsData **mrdptr; { int s, edgelimit; Tile *tile,*tp; TileTypeBitMask wrongtypes; - static MaxRectsData *mrd = (MaxRectsData *)NULL; + MaxRectsData *mrd = *mrdptr; Rect *boundrect, boundorig; /* Generate an initial array size of 8 for rlist and swap. */ @@ -529,6 +530,7 @@ drcCanonicalMaxwidth(starttile, dir, arg, cptr) mrd->rlist = (Rect *)mallocMagic(8 * sizeof(Rect)); mrd->swap = (Rect *)mallocMagic(8 * sizeof(Rect)); mrd->listdepth = 8; + *mrdptr = mrd; } if (starttile == NULL) return mrd;