/* * DRCmain.c -- * * This file provides global routines that are invoked at * command-level. They do things like give information about * errors and print statistics. * * ********************************************************************* * * 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/DRCmain.c,v 1.4 2010/06/24 12:37:16 tim Exp $"; #endif /* not lint */ #include #include #include #include "tcltk/tclmagic.h" #include "utils/magic.h" #include "utils/malloc.h" #include "textio/textio.h" #include "utils/geometry.h" #include "tiles/tile.h" #include "utils/hash.h" #include "database/database.h" #include "windows/windows.h" #include "dbwind/dbwind.h" #include "drc/drc.h" #include "cif/cif.h" #include "utils/undo.h" /* The global variables defined below are parameters between * the DRC error routines (drcPaintError and drcPrintError) * and the higher-level routines that start up DRC error checks. * It seemed simpler to do the communication this way rather * than creating a special new record that is passed down as * ClientData. Any routine invoking DRCBasicCheck with drcPaintError * or drcPrintError as action routine should fill in the relevant * variables. */ /* The following points to a list of all the DRC styles currently * understood: */ DRCKeep *DRCStyleList = NULL; DRCStyle *DRCCurStyle = NULL; /* Used by both routines: */ int DRCErrorCount; /* Incremented by each call to either routine. */ /* Used by drcPaintError: */ CellDef *DRCErrorDef; /* Place to paint error tiles. */ TileType DRCErrorType; /* Type of error tile to paint. */ /* Used by drcPrintError: */ int *DRCErrorList; /* List of DRC error type counts */ HashTable DRCErrorTable; /* Table of DRC errors and geometry */ /* Global variables used by all DRC modules to record statistics. * For each statistic we keep two values, the count since stats * were last printed (in DRCstatXXX), and the total count (in * drcTotalXXX). */ int DRCstatSquares = 0; /* Number of DRCStepSize-by-DRCStepSize * squares processed by continuous checker. */ int DRCstatTiles = 0; /* Number of tiles processed by basic * checker. */ int DRCstatEdges = 0; /* Number of "atomic" edges processed * by basic checker. */ int DRCstatRules = 0; /* Number of rules processed by basic checker * (rule = one constraint for one edge). */ int DRCstatSlow = 0; /* Number of places where constraint doesn't * all fall in a single tile. */ int DRCstatInteractions = 0; /* Number of times drcInt is called to check * an interaction area. */ int DRCstatIntTiles = 0; /* Number of tiles processed as part of * subcell interaction checks. */ int DRCstatCifTiles = 0; /* Number of tiles processed as part of * cif checks. */ int DRCstatArrayTiles = 0; /* Number of tiles processed as part of * array interaction checks. */ #ifdef DRCRULESHISTO int DRCstatVRulesHisto[DRC_MAXRULESHISTO]; int DRCstatHRulesHisto[DRC_MAXRULESHISTO]; #endif /* DRCRULESHISTO */ static int drcTotalSquares = 0; static int drcTotalTiles = 0; static int drcTotalEdges = 0; static int drcTotalRules = 0; static int drcTotalSlow = 0; static int drcTotalInteractions = 0; static int drcTotalIntTiles = 0; static int drcTotalArrayTiles = 0; #ifdef DRCRULESHISTO static int drcTotalVRulesHisto[DRC_MAXRULESHISTO]; static int drcTotalHRulesHisto[DRC_MAXRULESHISTO]; #endif /* DRCRULESHISTO */ LinkedIndex *DRCIgnoreRules = NULL; /* * ---------------------------------------------------------------------------- * drcPaintError -- * * Action function that paints error tiles for each violation found. * * Results: * None. * * Side effects: * A tile of type DRCErrorType is painted over the area of * the error, in the plane given by "plane". Also, DRCErrorCount * is incremented. * ---------------------------------------------------------------------------- */ void drcPaintError(celldef, rect, cptr, plane) CellDef * celldef; /* CellDef being checked */ Rect * rect; /* Area of error */ DRCCookie * cptr; /* Design rule violated -- not used */ Plane * plane; /* Where to paint error tiles. */ { PaintUndoInfo ui; ui.pu_def = celldef; ui.pu_pNum = PL_DRC_ERROR; DBPaintPlane(plane, rect, DBStdPaintTbl(DRCErrorType, PL_DRC_ERROR), &ui); DRCErrorCount += 1; } /* * ---------------------------------------------------------------------------- * drcSubstitute --- * * Check for substitution sequences in the DRC "why" string in the DRCCookie * record passed in cptr, and make the appropriate substitutions. Return * the modified string. * * Currently supported: * %d encodes the rule distance. Output is given in microns * %c encodes the rule corner distance. Output is given in microns. * %a encodes the rule distance as area. Output is given in microns squared. * * To do: Add flag bits to the drcc_flags field to indicate what type of * rule this is (easier than decoding it from context), and add a substitution * sequence "%s" to build the entire "why" string from the relevant rule data. * ---------------------------------------------------------------------------- */ char * drcSubstitute (cptr) DRCCookie * cptr; /* Design rule violated */ { static char *why_out = NULL; char *whyptr, *sptr, *wptr; int subscnt = 0, whylen; float oscale, value; extern float CIFGetOutputScale(); whyptr = DRCCurStyle->DRCWhyList[cptr->drcc_tag]; while ((sptr = strchr(whyptr, '%')) != NULL) { subscnt++; whyptr = sptr + 1; } if (subscnt == 0) return whyptr; /* No substitutions */ whyptr = DRCCurStyle->DRCWhyList[cptr->drcc_tag]; whylen = strlen(whyptr) + 20 * subscnt; if (why_out != NULL) freeMagic(why_out); why_out = (char *)mallocMagic(whylen * sizeof(char)); strcpy(why_out, whyptr); if (cptr->drcc_flags & DRC_CIFRULE) oscale = CIFGetScale(100); /* 100 = microns to centimicrons */ else oscale = CIFGetOutputScale(1000); /* 1000 for conversion to um */ wptr = why_out; while ((sptr = strchr(whyptr, '%')) != NULL) { /* copy up to sptr */ strncpy(wptr, whyptr, (int)(sptr - whyptr)); wptr += (sptr - whyptr); switch (*(sptr + 1)) { case 'd': /* Replace with "dist" value in microns */ value = (float)cptr->drcc_dist * oscale; snprintf(wptr, 20, "%01.3gum", value); wptr += strlen(wptr); break; case 'c': /* Replace with "cdist" value in microns */ value = (float)cptr->drcc_cdist * oscale; snprintf(wptr, 20, "%01.3gum", value); wptr += strlen(wptr); break; case 'a': /* Replace with "cdist" value in microns squared */ value = (float)cptr->drcc_cdist * oscale * oscale; snprintf(wptr, 20, "%01.4gum^2", value); wptr += strlen(wptr); break; default: /* Any other character after '%', treat as literal */ wptr += 2; break; } whyptr = sptr + 2; } /* copy remainder of string (including trailing null) */ strncpy(wptr, whyptr, strlen(whyptr) + 1); return why_out; } /* * ---------------------------------------------------------------------------- * drcPrintError -- * * Action function that prints the error message associated with each * violation found. * * Results: * None. * * Side effects: * DRCErrorCount is incremented. The text associated with * the error is entered into DRCErrorList, and, if this is * the first time that entry has been seen, then the error * text is printed. If the area parameter is non-NULL, then * only errors intersecting that area are considered. * ---------------------------------------------------------------------------- */ void drcPrintError (celldef, rect, cptr, scx) CellDef * celldef; /* CellDef being checked -- not used here */ Rect * rect; /* Area of error */ DRCCookie * cptr; /* Design rule violated */ SearchContext * scx; /* Only errors in scx->scx_area get reported. */ { HashEntry *h; int i; Rect *area; int drcsave = DRCErrorCount; /* Forward declaration */ void drcWhyFunc(SearchContext *scx, ClientData cdarg); ASSERT (cptr != (DRCCookie *) NULL, "drcPrintError"); area = &scx->scx_area; if ((area != NULL) && (!GEO_OVERLAP(area, rect))) return; if (cptr->drcc_tag == DRC_IN_SUBCELL_TAG) { SearchContext newscx; /* Recurse into subcells to find the error being flagged */ /* recursive call is drcWhyFunc, clientdata is FALSE */ newscx.scx_area = *rect; newscx.scx_use = scx->scx_use; newscx.scx_x = scx->scx_use->cu_xlo; newscx.scx_y = scx->scx_use->cu_ylo; newscx.scx_trans = scx->scx_trans; DBTreeSrCells(&newscx, 0, drcWhyFunc, (ClientData)FALSE); } /* Hack to avoid printing "no errors found" when recursing on * drcWhyFunc() above. In some cases like run-length rules, * changing the search area can make the error disappear. If * that happens, "See error definition in subcell" will be * printed. The underlying error needs to be fixed, but this * method provides the information the user needs to find the * error. */ if (drcsave == DRCErrorCount) { i = DRCErrorList[cptr->drcc_tag]; if (i == 0) TxPrintf("%s\n", drcSubstitute(cptr)); if (i >= 0) { DRCErrorCount += 1; DRCErrorList[cptr->drcc_tag] = i + 1; } } } /* Same routine as above, but output goes to a Tcl list and is appended */ /* to the interpreter result. */ #ifdef MAGIC_WRAPPER void drcListError (celldef, rect, cptr, scx) CellDef * celldef; /* CellDef being checked -- not used here */ Rect * rect; /* Area of error */ DRCCookie * cptr; /* Design rule violated */ SearchContext * scx; /* Only errors in scx->scx_area get reported */ { HashEntry *h; int i; Rect *area; int drcsave = DRCErrorCount; /* Forward declaration */ void drcWhyFunc(SearchContext *scx, ClientData cdarg); ASSERT (cptr != (DRCCookie *) NULL, "drcListError"); area = &scx->scx_area; if ((area != NULL) && (!GEO_OVERLAP(area, rect))) return; if (cptr->drcc_tag == DRC_IN_SUBCELL_TAG) { SearchContext newscx; /* Recurse into subcells to find the error being flagged */ /* recursive call is drcWhyFunc, clientdata is TRUE */ newscx.scx_area = *rect; newscx.scx_use = scx->scx_use; newscx.scx_x = scx->scx_use->cu_xlo; newscx.scx_y = scx->scx_use->cu_ylo; newscx.scx_trans = scx->scx_trans; DBTreeSrCells(&newscx, 0, drcWhyFunc, (ClientData)TRUE); } if (drcsave == DRCErrorCount) { i = DRCErrorList[cptr->drcc_tag]; if (i == 0) { Tcl_Obj *lobj; lobj = Tcl_GetObjResult(magicinterp); Tcl_ListObjAppendElement(magicinterp, lobj, Tcl_NewStringObj(drcSubstitute(cptr), -1)); Tcl_SetObjResult(magicinterp, lobj); } if (i >= 0) { DRCErrorCount += 1; DRCErrorList[cptr->drcc_tag] = i + 1; } } } /* Same routine as above, but output for every single error is recorded */ /* along with position information. */ void drcListallError (celldef, rect, cptr, scx) CellDef * celldef; /* CellDef being checked -- not used here */ Rect * rect; /* Area of error */ DRCCookie * cptr; /* Design rule violated */ SearchContext * scx; /* Only errors in scx->scx_area get reported. */ { Tcl_Obj *lobj, *pobj; HashEntry *h; Rect *area, r; int drcsave = DRCErrorCount; /* Forward declaration */ int drcWhyAllFunc(SearchContext *scx, ClientData cdarg); ASSERT (cptr != (DRCCookie *) NULL, "drcListallError"); // Report in top-level coordinates GeoTransRect(&scx->scx_trans, rect, &r); area = &scx->scx_area; if ((area != NULL) && (!GEO_OVERLAP(area, rect))) return; if (cptr->drcc_tag == DRC_IN_SUBCELL_TAG) { SearchContext newscx; /* Recurse into subcells to find the error being flagged */ /* recursive call is drcWhyAllFunc, clientdata is NULL */ newscx.scx_area = *rect; newscx.scx_use = scx->scx_use; newscx.scx_x = scx->scx_use->cu_xlo; newscx.scx_y = scx->scx_use->cu_ylo; newscx.scx_trans = scx->scx_trans; DBTreeSrCells(&newscx, 0, drcWhyAllFunc, (ClientData)NULL); } if (drcsave == DRCErrorCount) { DRCErrorCount += 1; h = HashFind(&DRCErrorTable, drcSubstitute(cptr)); lobj = (Tcl_Obj *) HashGetValue(h); if (lobj == NULL) lobj = Tcl_NewListObj(0, NULL); pobj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(magicinterp, pobj, Tcl_NewIntObj(r.r_xbot)); Tcl_ListObjAppendElement(magicinterp, pobj, Tcl_NewIntObj(r.r_ybot)); Tcl_ListObjAppendElement(magicinterp, pobj, Tcl_NewIntObj(r.r_xtop)); Tcl_ListObjAppendElement(magicinterp, pobj, Tcl_NewIntObj(r.r_ytop)); Tcl_ListObjAppendElement(magicinterp, lobj, pobj); HashSetValue(h, lobj); } } #else #define drcListError drcPrintError #endif /* * ---------------------------------------------------------------------------- * * DRCPrintStats -- * * Prints out statistics gathered by the DRC checking routines. * * Results: * None. * * Side effects: * Statistics are printed. Two values are printed for each * statistic: the number since statistics were last printed, * and the total to date. The own variables used to keep * track of statistics are updated. * * ---------------------------------------------------------------------------- */ void DRCPrintStats() { #ifdef DRCRULESHISTO int n; #endif /* DRCRULESHISTO */ TxPrintf("Design-rule checker statistics (recent/total):\n"); drcTotalSquares += DRCstatSquares; TxPrintf(" Squares processed: %d/%d\n", DRCstatSquares, drcTotalSquares); DRCstatSquares = 0; drcTotalTiles += DRCstatTiles; TxPrintf(" Tiles processed: %d/%d\n", DRCstatTiles, drcTotalTiles); DRCstatTiles = 0; drcTotalEdges += DRCstatEdges; TxPrintf(" Edges pieces processed: %d/%d\n", DRCstatEdges, drcTotalEdges); DRCstatEdges = 0; drcTotalRules += DRCstatRules; TxPrintf(" Constraint areas checked: %d/%d\n", DRCstatRules, drcTotalRules); DRCstatRules = 0; drcTotalSlow += DRCstatSlow; TxPrintf(" Multi-tile constraints: %d/%d\n", DRCstatSlow, drcTotalSlow); DRCstatSlow = 0; drcTotalInteractions += DRCstatInteractions; TxPrintf(" Interaction areas processed: %d/%d\n", DRCstatInteractions, drcTotalInteractions); DRCstatInteractions = 0; drcTotalIntTiles += DRCstatIntTiles; TxPrintf(" Tiles processed for interactions: %d/%d\n", DRCstatIntTiles, drcTotalIntTiles); DRCstatIntTiles = 0; drcTotalArrayTiles += DRCstatArrayTiles; TxPrintf(" Tiles processed for arrays: %d/%d\n", DRCstatArrayTiles, drcTotalArrayTiles); DRCstatArrayTiles = 0; #ifdef DRCRULESHISTO TxPrintf(" Number of rules applied per edge:\n"); TxPrintf(" # rules Horiz freq Vert freq\n"); for (n = 0; n < DRC_MAXRULESHISTO; n++) { drcTotalHRulesHisto[n] += DRCstatHRulesHisto[n]; drcTotalVRulesHisto[n] += DRCstatVRulesHisto[n]; if (drcTotalHRulesHisto[n] == 0 && drcTotalVRulesHisto[n] == 0) continue; TxPrintf(" %3d %10d/%10d %10d/%10d\n", n, DRCstatHRulesHisto[n], drcTotalHRulesHisto[n], DRCstatVRulesHisto[n], drcTotalVRulesHisto[n]); DRCstatHRulesHisto[n] = DRCstatVRulesHisto[n] = 0; } #endif /* DRCRULESHISTO */ } /* * ---------------------------------------------------------------------------- * * DRCWhy -- * * This procedure finds all errors within an area and prints messages * about each distinct kind of violation found. * * Results: * TRUE if errors were found, and FALSE if not. * * Side effects: * None, except that error messages are printed. The given * area is DRC'ed for both paint and subcell violations in every * cell of def's tree that it intersects. * * ---------------------------------------------------------------------------- */ bool DRCWhy(dolist, use, area, findonly) bool dolist; /* * Generate Tcl list for value */ CellUse *use; /* Use in whose definition to start * the hierarchical check. */ Rect *area; /* Area, in def's coordinates, that * is to be checked. */ bool findonly; /* If TRUE, contents of DRCIgnoreRules * are inverted; that is, flag only * the marked rules instead of ignoring * them. */ { SearchContext scx; Rect box; int i, nerrors; extern void drcWhyFunc(); /* Forward reference. */ LinkedIndex *li; /* Create a hash table to eliminate duplicate messages. */ DRCErrorList = (int *)mallocMagic((DRCCurStyle->DRCWhySize + 1) * sizeof(int)); if (!findonly) { for (i = 0; i <= DRCCurStyle->DRCWhySize; i++) DRCErrorList[i] = 0; /* Ignore rules as specified by setting the DRCErrorList entry to */ /* -1, indicating that the error type should be ignored. */ for (li = DRCIgnoreRules; li; li = li->li_next) DRCErrorList[li->li_index] = -1; } else { /* Inverted behavior: Only look at rules in the DRCIgnoreRules list */ for (i = 0; i <= DRCCurStyle->DRCWhySize; i++) DRCErrorList[i] = -1; for (li = DRCIgnoreRules; li; li = li->li_next) DRCErrorList[li->li_index] = 0; } DRCErrorCount = 0; box = DRCdef->cd_bbox; /* Undo will only slow things down in here, so turn it off. */ UndoDisable(); scx.scx_use = use; scx.scx_x = use->cu_xlo; scx.scx_y = use->cu_ylo; scx.scx_area = *area; scx.scx_trans = GeoIdentityTransform; drcWhyFunc(&scx, (pointertype)dolist); UndoEnable(); /* Delete the error list */ freeMagic(DRCErrorList); /* Redisplay the DRC yank definition in case anyone is looking * at it. */ DBReComputeBbox(DRCdef); (void) GeoInclude(&DRCdef->cd_bbox, &box); DBWAreaChanged(DRCdef, &box, DBW_ALLWINDOWS, &DBAllButSpaceBits); return (DRCErrorCount > 0) ? TRUE : FALSE; } #ifdef MAGIC_WRAPPER void DRCWhyAll(use, area, fout) CellUse *use; /* Use in whose definition to start * the hierarchical check. */ Rect *area; /* Area, in def's coordinates, that * is to be checked. */ FILE *fout; /* * Write formatted output to fout */ { SearchContext scx; Rect box; extern int drcWhyAllFunc(); /* Forward reference. */ HashSearch hs; HashEntry *he; Tcl_Obj *lobj, *robj; /* Create a hash table for storing all of the results */ HashInit(&DRCErrorTable, 16, HT_STRINGKEYS); DRCErrorCount = 0; box = DRCdef->cd_bbox; /* Undo will only slow things down in here, so turn it off. */ UndoDisable(); scx.scx_use = use; scx.scx_x = use->cu_xlo; scx.scx_y = use->cu_ylo; scx.scx_area = *area; scx.scx_trans = GeoIdentityTransform; drcWhyAllFunc(&scx, NULL); UndoEnable(); /* Generate results */ robj = Tcl_NewListObj(0, NULL); HashStartSearch(&hs); while ((he = HashNext(&DRCErrorTable, &hs)) != (HashEntry *)NULL) { lobj = (Tcl_Obj *)HashGetValue(he); if (lobj != NULL) { Tcl_ListObjAppendElement(magicinterp, robj, Tcl_NewStringObj((char *)he->h_key.h_name, -1)); Tcl_ListObjAppendElement(magicinterp, robj, lobj); } } Tcl_SetObjResult(magicinterp, robj); /* Delete the error table now that we're finished */ HashKill(&DRCErrorTable); /* Redisplay the DRC yank definition in case anyone is looking * at it. */ DBReComputeBbox(DRCdef); (void) GeoInclude(&DRCdef->cd_bbox, &box); DBWAreaChanged(DRCdef, &box, DBW_ALLWINDOWS, &DBAllButSpaceBits); if (DRCErrorCount == 0) TxPrintf("No errors found.\n"); } #endif /* MAGIC_WRAPPER */ /* * ---------------------------------------------------------------------------- * * drcWhyFunc -- * * This function is invoked underneath DrcWhy. It's called once * for each subcell instance of the current cell. If the subcell * is expanded, then it computes errors in that subcell and * searches the subcell recursively. * * Results: * Returns the number of errors found by DRCInteractionCheck(). * * Side effects: * None. * * ---------------------------------------------------------------------------- */ /* ARGSUSED */ void drcWhyFunc(scx, cdarg) SearchContext *scx; /* Describes current state of search. */ ClientData cdarg; /* Used to hold boolean value "dolist" */ { CellDef *def = scx->scx_use->cu_def; bool dolist = (bool)((pointertype)cdarg); /* Check paint and interactions in this subcell. */ DRCInteractionCheck(def, &scx->scx_area, &scx->scx_area, (dolist) ? drcListError : drcPrintError, (ClientData) scx); } #ifdef MAGIC_WRAPPER int drcWhyAllFunc(scx, cdarg) SearchContext *scx; /* Describes current state of search. */ ClientData cdarg; /* Unused */ { CellDef *def = scx->scx_use->cu_def; /* Check paint and interactions in this subcell. */ (void) DRCInteractionCheck(def, &scx->scx_area, &scx->scx_area, drcListallError, (ClientData)scx); return 0; } #endif /* MAGIC_WRAPPER */ /* * ---------------------------------------------------------------------------- * * DRCCheck -- * * Marks all areas underneath the cursor, forcing them to be * rechecked by the DRC. * * Results: * None. * * Side effects: * Check tiles are painted. * * ---------------------------------------------------------------------------- */ void DRCCheck(use, area) CellUse *use; /* Top-level use of hierarchy. */ Rect *area; /* This area is rechecked everywhere in the * hierarchy underneath use. */ { SearchContext scx; CellDef *err_def; extern int drcCheckFunc(); /* Forward reference. */ err_def = DBCellReadArea(use, area, TRUE); if (err_def != NULL) { TxError("Failure to read in entire subtree of cell.\n"); TxError("Failed on cell %s.\n", err_def->cd_name); return; } scx.scx_use = use; scx.scx_x = use->cu_xlo; scx.scx_y = use->cu_ylo; scx.scx_area = *area; scx.scx_trans = GeoIdentityTransform; (void) drcCheckFunc(&scx, (ClientData) NULL); } /* ARGSUSED */ int drcCheckFunc(scx, cdarg) SearchContext *scx; ClientData cdarg; /* Not used. */ { Rect cellArea; CellDef *def; /* Clip the area to the size of the cell, then recheck that area. * The recheck is handled by painting the check info directly * and then calling DRCCheckThis only to add the cell to the * list of those to be checked. This avoids the hierarchical * upwards search that would normally be made by DRCCheckThis, * but which is unwelcome (and slow) here. */ cellArea = scx->scx_area; def = scx->scx_use->cu_def; GeoClip(&cellArea, &def->cd_bbox); GEO_EXPAND(&cellArea, DRCTechHalo, &cellArea); DBPaintPlane(def->cd_planes[PL_DRC_CHECK], &cellArea, DBStdPaintTbl(TT_CHECKPAINT, PL_DRC_CHECK), (PaintUndoInfo *) NULL); /* Search children and apply recursively */ (void) DBCellSrArea(scx, drcCheckFunc, (ClientData) NULL); /* Then do self */ DRCCheckThis(def, TT_CHECKPAINT, (Rect *) NULL); /* As a special performance hack, if the complete cell area is * handled here, don't bother to look at any more array elements. */ if (GEO_SURROUND(&cellArea, &def->cd_bbox)) return 2; else return 0; } /* * ---------------------------------------------------------------------------- * * DRCCount -- * * Searches the entire hierarchy underneath the given area. * For each cell found, counts design-rule violations in * that cell and outputs the counts. * * Results: * Return linked list of cell definitions and their error counts. * * Side effects: * None. * * ---------------------------------------------------------------------------- */ DRCCountList * DRCCount(use, area, recurse) CellUse *use; /* Top-level use of hierarchy. */ Rect *area; /* Area in which violations are counted. */ bool recurse; /* If TRUE, count errors in all subcells */ { DRCCountList *dcl, *newdcl; HashTable dupTable; HashEntry *he; HashSearch hs; int count; SearchContext scx; CellDef *def; extern int drcCountFunc(); /* Shouldn't happen? */ if (!(use->cu_def->cd_flags & CDAVAILABLE)) return NULL; /* Use a hash table to make sure that we don't output information * for any cell more than once. */ HashInit(&dupTable, 16, HT_WORDKEYS); /* Clearing CDAVAILABLE from cd_flags keeps the count from recursing */ if (recurse == FALSE) use->cu_def->cd_flags &= ~CDAVAILABLE; scx.scx_use = use; scx.scx_x = use->cu_xlo; scx.scx_y = use->cu_ylo; scx.scx_area = *area; scx.scx_trans = GeoIdentityTransform; (void) drcCountFunc(&scx, &dupTable); /* Create the list from the hash table */ dcl = NULL; if (dupTable.ht_table != (HashEntry **) NULL) { HashStartSearch(&hs); while ((he = HashNext(&dupTable, &hs)) != (HashEntry *)NULL) { count = (spointertype)HashGetValue(he); if (count > 1) { newdcl = (DRCCountList *)mallocMagic(sizeof(DRCCountList)); newdcl->dcl_count = count - 1; newdcl->dcl_def = (CellDef *)he->h_key.h_ptr; newdcl->dcl_next = dcl; dcl = newdcl; } } } HashKill(&dupTable); /* Restore the CDAVAILABLE flag */ if (recurse == FALSE) use->cu_def->cd_flags |= CDAVAILABLE; return dcl; } int drcCountFunc(scx, dupTable) SearchContext *scx; HashTable *dupTable; /* Passed as client data, used to * avoid searching any cell twice. */ { int count; HashEntry *h; CellDef *def; extern int drcCountFunc2(); /* If we've already seen this cell definition before, then skip it * now. */ def = scx->scx_use->cu_def; h = HashFind(dupTable, (char *)def); if (HashGetValue(h) != 0) goto done; HashSetValue(h, 1); /* Count errors in this cell definition by scanning the error plane. */ count = 0; (void) DBSrPaintArea((Tile *) NULL, def->cd_planes[PL_DRC_ERROR], &def->cd_bbox, &DBAllButSpaceBits, drcCountFunc2, (ClientData)(&count)); HashSetValue(h, (spointertype)count + 1); /* Ignore children that have not been loaded---we will only report */ /* errors that can be seen. This avoids immediately loading and */ /* drc processing large layouts simply because we asked for an */ /* error count. When the cell is loaded, drc will be checked */ /* anyway, and the count can be updated in response to that check. */ if ((scx->scx_use->cu_def->cd_flags & CDAVAILABLE) == 0) return 0; /* Scan children recursively. */ DBCellSrArea(scx, drcCountFunc, (ClientData)dupTable); /* As a special performance hack, if the complete cell area is * handled here, don't bother to look at any more array elements. */ done: if (GEO_SURROUND(&scx->scx_area, &def->cd_bbox)) return 2; else return 0; } int drcCountFunc2(tile, countptr) Tile *tile; /* Tile found in error plane. */ int *countptr; /* Address of count word. */ { if (TiGetType(tile) != (TileType) TT_SPACE) (*countptr)++; return 0; } /* * ---------------------------------------------------------------------------- * * DRCCatchUp-- * * This procedure just runs the background checker, regardless * of whether it's enabled or not, and waits for it to complete. * * Results: * None. * * Side effects: * Error and check tiles get painted and erased by the checker. * * ---------------------------------------------------------------------------- */ void DRCCatchUp() { int background; background = DRCBackGround; DRCBackGround = DRC_SET_ON; #ifdef MAGIC_WRAPPER /* Always reset DRC status to "not running" before calling DRCContinous() * directly. */ DRCStatus = DRC_NOT_RUNNING; #endif DRCContinuous(); DRCBackGround = background; } /* * ---------------------------------------------------------------------------- * * DRCFind -- * * Locates the next violation tile in the cell pointed to by * "use" and its children. * Successive calls will located successive violations, in * circular order. * * Results: * If an error tile was found in def, returns the indx of * the error tile (> 0). Returns 0 if there were no error * tiles in def. Returns -1 if indx was out-of-range * (not that many errors in def). * * Side effects: * Rect is filled with the location of the tile, if one is found. * * ---------------------------------------------------------------------------- */ typedef struct { int current; /* count of current error */ int target; /* count of target error */ Rect *rect; /* Return rectangle */ Transform trans; /* Return transform */ HashTable *deft; /* Table of cell definitions to avoid duplicates */ } Sindx; int DRCFind(use, area, rect, indx) CellUse *use; /* Cell use to check. */ Rect *area; /* Area of search */ Rect *rect; /* Rectangle to fill in with tile location. */ int indx; /* Go to this error. */ { SearchContext scx; Sindx finddata; Rect trect; int result; int drcFindFunc(); HashTable defTable; scx.scx_use = use; scx.scx_x = use->cu_xlo; scx.scx_y = use->cu_ylo; scx.scx_area = *area; scx.scx_trans = GeoIdentityTransform; HashInit(&defTable, 16, HT_WORDKEYS); finddata.current = 0; finddata.target = indx; finddata.rect = &trect; finddata.trans = scx.scx_trans; finddata.deft = &defTable; result = drcFindFunc(&scx, &finddata); HashKill(&defTable); if (result == 0) { if (finddata.current == 0) return 0; else return -1; } /* Translate rectangle back into coordinate system of "use" */ GeoTransRect(&finddata.trans, &trect, rect); return indx; } int drcFindFunc(scx, finddata) SearchContext *scx; Sindx *finddata; { CellDef *def; HashEntry *h; int drcFindFunc2(); def = scx->scx_use->cu_def; h = HashFind(finddata->deft, (char *)def); if (HashGetValue(h) != 0) return 0; HashSetValue(h, 1); (void) DBCellRead(def, TRUE, TRUE, NULL); if (DBSrPaintArea((Tile *) NULL, def->cd_planes[PL_DRC_ERROR], &def->cd_bbox, &DBAllButSpaceBits, drcFindFunc2, (ClientData)finddata) != 0) { finddata->trans = scx->scx_trans; return 1; } /* New behavior: Don't search children, instead propagate errors up. */ /* return DBCellSrArea(scx, drcFindFunc, (ClientData)finddata); */ return 0; } int drcFindFunc2(tile, finddata) Tile *tile; /* Tile in error plane. */ Sindx *finddata; /* Information about error to find */ { if (TiGetType(tile) == (TileType) TT_SPACE) return 0; if (++finddata->current == finddata->target) { TiToRect(tile, finddata->rect); return 1; } return 0; }