magic/drc/DRCmain.c

1122 lines
31 KiB
C

/*
* 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 <sys/types.h>
#include <stdio.h>
#include <string.h>
#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;
}