magic/extflat/EFantenna.c

858 lines
22 KiB
C

/*
* EFantenna.c --
*
* Program to flatten hierarchical .ext files and then execute an
* antenna violation check for every MOSFET device in the flattened
* design.
*
* Flattens the tree rooted at file.ext, reading in additional .ext
* files as specified by "use" lines in file.ext.
*
*/
#include <stdio.h>
#include <stdlib.h> /* for atof() */
#include <string.h>
#include <ctype.h>
#include <math.h> /* for INFINITY */
#ifdef MAGIC_WRAPPER
#include "tcltk/tclmagic.h"
#endif
#include "utils/magic.h"
#include "utils/geometry.h"
#include "utils/hash.h"
#include "utils/utils.h"
#include "utils/styles.h"
#include "tiles/tile.h"
#include "database/database.h"
#include "windows/windows.h"
#include "textio/textio.h"
#include "dbwind/dbwind.h"
#include "textio/txcommands.h"
#include "extflat/extflat.h"
#include "extflat/EFint.h"
#include "extract/extract.h"
#include "extract/extractInt.h"
#include "select/select.h"
#include "utils/malloc.h"
/* Forward declarations */
int antennacheckArgs();
int antennacheckVisit();
typedef struct {
long visitMask:MAXDEVTYPES;
} nodeClient;
typedef struct {
HierName *lastPrefix;
long visitMask:MAXDEVTYPES;
} nodeClientHier;
#define NO_RESCLASS -1
#define markVisited(client, rclass) \
{ (client)->visitMask |= (1<<rclass); }
#define clearVisited(client) \
{ (client)->visitMask = (long)0; }
#define beenVisited(client, rclass) \
( (client)->visitMask & (1<<rclass))
#define initNodeClient(node) \
{ \
(node)->efnode_client = (ClientData) mallocMagic((unsigned) (sizeof(nodeClient))); \
(( nodeClient *)(node)->efnode_client)->visitMask = (long) 0; \
}
#define initNodeClientHier(node) \
{ \
(node)->efnode_client = (ClientData) mallocMagic((unsigned) (sizeof(nodeClientHier))); \
((nodeClientHier *) (node)->efnode_client)->visitMask = (long) 0; \
}
/* Diagnostic */
int efGates;
static int efAntennaDebug = FALSE;
/* The extract file is designed to be independent of the magic database, */
/* but that means that the device types do not match magic database types. */
/* A lookup table is needed to cross-reference the device types. */
TileType *EFDeviceTypes;
typedef struct _aas {
dlong *accum; /* Pointer to array of accumulated areas per type */
int pNum; /* Plane of check */
Rect r; /* Holds any one visited rectangle */
CellDef *def; /* CellDef for adding feedback */
} AntennaAccumStruct;
typedef struct _gdas {
dlong accum; /* Accumulated area of all gates/diff */
Rect r; /* Holds any one visited rectangle */
CellDef *def; /* CellDef for adding feedback */
} GateDiffAccumStruct;
typedef struct _ams {
int pNum; /* Plane of check */
CellDef *def; /* CellDef for adding feedback */
} AntennaMarkStruct;
/*
* ----------------------------------------------------------------------------
*
* Main Tcl callback for command "magic::antennacheck"
*
* ----------------------------------------------------------------------------
*/
#define ANTENNACHECK_RUN 0
#define ANTENNACHECK_DEBUG 1
#define ANTENNACHECK_HELP 2
void
CmdAntennaCheck(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
int i, flatFlags;
char *inName;
FILE *f;
TileType t;
int option = ANTENNACHECK_RUN;
int value;
int argc = cmd->tx_argc;
char **argv = cmd->tx_argv;
char **msg;
bool err_result;
short sd_rclass;
short sub_rclass;
char *devname;
int idx;
CellUse *editUse;
static char *cmdAntennaCheckOption[] = {
"[run] [options] run antennacheck on current cell\n"
" use \"run -help\" to get standard options",
"debug print detailed information about each error",
"help print help information",
NULL
};
if (cmd->tx_argc > 1)
{
option = Lookup(cmd->tx_argv[1], cmdAntennaCheckOption);
if (option < 0) option = ANTENNACHECK_RUN;
else argv++;
}
switch (option)
{
case ANTENNACHECK_RUN:
goto runantennacheck;
break;
case ANTENNACHECK_DEBUG:
efAntennaDebug = TRUE;
break;
case ANTENNACHECK_HELP:
usage:
for (msg = &(cmdAntennaCheckOption[0]); *msg != NULL; msg++)
{
TxPrintf(" %s\n", *msg);
}
break;
}
return;
runantennacheck:
if (ExtCurStyle->exts_planeOrderStatus == noPlaneOrder)
{
TxError("No planeorder specified for this process: "
"Cannot run antenna checks!\n");
return;
}
EFInit();
EFCapThreshold = INFINITY;
EFResistThreshold = INFINITY;
/* Process command line arguments */
inName = EFArgs(argc, argv, &err_result, antennacheckArgs, (ClientData) NULL);
if (err_result == TRUE)
{
EFDone();
return /* TCL_ERROR */;
}
if (inName == NULL)
{
/* Assume that we want to do exttospice on the currently loaded cell */
if (w == (MagWindow *) NULL)
windCheckOnlyWindow(&w, DBWclientID);
if (w == (MagWindow *) NULL)
{
TxError("Point to a window or specify a cell name.\n");
EFDone();
return /* TCL_ERROR */;
}
inName = ((CellUse *) w->w_surfaceID)->cu_def->cd_name;
}
editUse = (CellUse *)w->w_surfaceID;
/*
* Initializations specific to this program.
*/
/* Read the hierarchical description of the input circuit */
TxPrintf("Reading extract file.\n");
if (EFReadFile(inName, FALSE, FALSE, FALSE) == FALSE)
{
EFDone();
return /* TCL_ERROR */;
}
/* Convert the hierarchical description to a flat one */
flatFlags = EF_FLATNODES;
TxPrintf("Building flattened netlist.\n");
EFFlatBuild(inName, flatFlags);
/* Build device lookup table */
EFDeviceTypes = (TileType *)mallocMagic(MAXDEVTYPES * sizeof(TileType));
for (i = 0; i < MAXDEVTYPES; i++)
if (EFDevTypes[i])
EFDeviceTypes[i] = extGetDevType(EFDevTypes[i]);
efGates = 0;
TxPrintf("Running antenna checks.\n");
EFVisitDevs(antennacheckVisit, (ClientData)editUse);
EFFlatDone();
EFDone();
TxPrintf("antennacheck finished.\n");
freeMagic(EFDeviceTypes);
efAntennaDebug = FALSE;
}
/*
* ----------------------------------------------------------------------------
*
* antennacheckArgs --
*
* Process those arguments that are specific to antennacheck.
* Assumes that *pargv[0][0] is '-', indicating a flag
* argument.
*
* Results:
* None. TCL version returns False if an error is encountered
* while parsing arguments, True otherwise.
*
* Side effects:
* After processing an argument, updates *pargc and *pargv
* to point to after the argument.
*
* May initialize various global variables based on the
* arguments given to us.
*
* Exits in the event of an improper argument.
*
* ----------------------------------------------------------------------------
*/
int
antennacheckArgs(pargc, pargv)
int *pargc;
char ***pargv;
{
char **argv = *pargv, *cp;
int argc = *pargc;
switch (argv[0][1])
{
default:
TxError("Unrecognized flag: %s\n", argv[0]);
goto usage;
}
*pargv = argv;
*pargc = argc;
return 0;
usage:
TxError("Usage: antennacheck\n");
return 1;
}
/*
* ----------------------------------------------------------------------------
*
* AntennaGetNode --
*
* function to find a node given its hierarchical prefix and suffix
*
* Results:
* a pointer to the node struct or NULL
*
* ----------------------------------------------------------------------------
*/
EFNode *
AntennaGetNode(prefix, suffix)
HierName *prefix;
HierName *suffix;
{
HashEntry *he;
he = EFHNConcatLook(prefix, suffix, "output");
return(((EFNodeName *) HashGetValue(he))->efnn_node);
}
/*
* ----------------------------------------------------------------------------
*
* antennacheckVisit --
*
* Procedure to check for antenna violations from a single device.
* Called by EFVisitDevs().
*
* Results:
* Returns 0 always.
*
* Side effects:
* May tag other device records to avoid double-counting devices.
* Generates feedback entries if an antenna violation is found.
*
* ----------------------------------------------------------------------------
*/
int
antennacheckVisit(dev, hc, scale, trans, editUse)
Dev *dev; /* Device being output */
HierContext *hc; /* Hierarchical context down to this device */
float scale; /* Scale transform for output */
Transform *trans; /* Coordinate transform */
CellUse *editUse; /* ClientData is edit cell use */
{
DevTerm *gate;
TileType t, conType;
int pos, pNum, pNum2, pmax, p, i, j, total;
dlong gatearea, diffarea;
double anttotal;
float saveRatio, ratioTotal;
dlong *antennaarea;
Rect r, gaterect;
EFNode *gnode;
SearchContext scx;
TileTypeBitMask gatemask, saveConMask;
bool antennaError;
HierName *hierName = hc->hc_hierName;
extern CellDef *extPathDef; /* see extract/ExtLength.c */
extern CellUse *extPathUse; /* see extract/ExtLength.c */
extern int areaAccumFunc(), antennaAccumFunc(), areaMarkFunc();
antennaarea = (dlong *)mallocMagic(DBNumTypes * sizeof(dlong));
switch(dev->dev_class)
{
case DEV_FET:
case DEV_MOSFET:
case DEV_MSUBCKT:
case DEV_ASYMMETRIC:
/* Procedure:
*
* 1. If device gate node is marked visited, return.
* 2. Mark device gate node visited
* 3. For each plane from metal1 up (determined by planeorder):
* a. Run DBTreeCopyConnect()
* b. Accumulate gate area of connected devices
* c. Accumulate diffusion area of connected devices
* d. Accumulate metal area of connected devices
* e. Check against antenna ratio(s)
* f. Generate feedback if in violation of antenna rule
*
* NOTE: DBTreeCopyConnect() is used cumulatively, so that
* additional searching only needs to be done for the additional
* layer being searched.
*/
GeoTransRect(trans, &dev->dev_rect, &r);
gate = &dev->dev_terms[0];
gnode = AntennaGetNode(hierName, gate->dterm_node->efnode_name->efnn_hier);
if (gnode->efnode_client == (ClientData) NULL)
initNodeClient(gnode);
if (beenVisited((nodeClient *)gnode->efnode_client, 0))
return 0;
else
markVisited((nodeClient *)gnode->efnode_client, 0);
/* Diagnostic stuff */
efGates++;
if (efGates % 100 == 0) TxPrintf(" %d gates analyzed.\n", efGates);
/* Diagnostic for debugging */
/*
TxPrintf("Gate %d : (%d %d) to (%d %d) net %s\n", efGates, r.r_xbot,
r.r_ybot, r.r_xtop, r.r_ytop,
gnode->efnode_name->efnn_hier->hn_name);
*/
/* Find the plane of the gate type */
t = EFDeviceTypes[dev->dev_type];
pNum = DBPlane(t);
pos = ExtCurStyle->exts_planeOrder[pNum];
pmax = ++pos;
/* Find the highest plane in the technology */
for (p = PL_TECHDEPBASE; p < DBNumPlanes; p++)
if (ExtCurStyle->exts_planeOrder[p] > pmax)
pmax = ExtCurStyle->exts_planeOrder[p];
/* Create the yank cell if it doesn't already exist */
if (extPathDef == (CellDef *) NULL)
DBNewYank("__PATHYANK__", &extPathUse, &extPathDef);
/* Use the cellDef reserved for extraction */
/* DBCellClearDef(extPathDef); */ /* See below */
scx.scx_use = editUse;
scx.scx_trans = GeoIdentityTransform;
scx.scx_area = r;
/* gatemask is a mask of all gate types for MOSFET devices */
TTMaskZero(&gatemask);
for (i = 0; i < DBNumTypes; i++)
{
ExtDevice *ed;
char devclass;
if (ExtCurStyle->exts_device[i] != NULL)
{
for (ed = ExtCurStyle->exts_device[i]; ed; ed = ed->exts_next)
{
devclass = ed->exts_deviceClass;
switch (devclass)
{
case DEV_MOSFET:
case DEV_FET:
case DEV_ASYMMETRIC:
case DEV_MSUBCKT:
TTMaskSetType(&gatemask, i);
break;
}
}
}
}
for (; pos <= pmax; pos++)
{
GateDiffAccumStruct gdas;
AntennaAccumStruct aas;
AntennaMarkStruct ams;
/* Find the plane of pos */
for (p = 0; p < DBNumPlanes; p++)
if (ExtCurStyle->exts_planeOrder[p] == pos)
pNum2 = p;
/* Find the tiletype which is a contact and whose base is pNum2 */
/* (NOTE: Need to extend to all such contacts, as there may be */
/* more than one.) (Also should find these types up top, not */
/* within the loop.) */
/* Modify DBConnectTbl to limit connectivity to the plane */
/* of the antenna check and below */
conType = -1;
for (i = 0; i < DBNumTypes; i++)
if (DBIsContact(i) && DBPlane(i) == pNum2)
{
conType = i;
TTMaskZero(&saveConMask);
TTMaskSetMask(&saveConMask, &DBConnectTbl[i]);
TTMaskZero(&DBConnectTbl[i]);
for (j = 0; j < DBNumTypes; j++)
if (TTMaskHasType(&saveConMask, j) &&
(DBPlane(j) <= pNum2))
TTMaskSetType(&DBConnectTbl[i], j);
break;
}
for (i = 0; i < DBNumTypes; i++) antennaarea[i] = (dlong)0;
gatearea = 0;
diffarea = 0;
/* Note: Ideally, the addition of material in the next */
/* metal plane is additive. But that requires enumerating */
/* all the vias and using those as starting points for the */
/* next connectivity search, which needs to be coded. */
DBCellClearDef(extPathDef);
/* To do: Mark tiles so area count can be progressive */
DBTreeCopyConnect(&scx, &DBConnectTbl[t], 0,
DBConnectTbl, &TiPlaneRect, SEL_NO_LABELS, extPathUse);
/* Search planes of tie types and accumulate all tiedown areas */
gdas.accum = (dlong)0;
for (p = 0; p < DBNumPlanes; p++)
DBSrPaintArea((Tile *)NULL, extPathUse->cu_def->cd_planes[p],
&TiPlaneRect, &ExtCurStyle->exts_antennaTieTypes,
areaAccumFunc, (ClientData)&gdas);
diffarea = gdas.accum;
/* Search plane of gate type and accumulate all gate area */
gdas.accum = (dlong)0;
DBSrPaintArea((Tile *)NULL, extPathUse->cu_def->cd_planes[pNum],
&TiPlaneRect, &gatemask, areaAccumFunc, (ClientData)&gdas);
gatearea = gdas.accum;
/* Search metal planes and accumulate all antenna areas */
for (p = 0; p < DBNumPlanes; p++)
{
if (ExtCurStyle->exts_antennaModel & ANTENNAMODEL_PARTIAL)
if (p != pNum2) continue;
aas.pNum = p;
aas.accum = &antennaarea[0];
if (ExtCurStyle->exts_planeOrder[p] <= pos)
DBSrPaintArea((Tile *)NULL, extPathUse->cu_def->cd_planes[p],
&TiPlaneRect, &DBAllButSpaceAndDRCBits,
antennaAccumFunc, (ClientData)&aas);
}
antennaError = FALSE;
if (diffarea == 0)
{
anttotal = 0.0;
saveRatio = 0.0;
for (i = 0; i < DBNumTypes; i++)
{
if (ExtCurStyle->exts_antennaRatio[i].ratioGate > 0)
{
anttotal += (double)antennaarea[i] /
(double)ExtCurStyle->exts_antennaRatio[i].ratioGate;
}
if (ExtCurStyle->exts_antennaRatio[i].ratioGate > saveRatio)
saveRatio = ExtCurStyle->exts_antennaRatio[i].ratioGate;
}
if (anttotal > (double)gatearea)
{
antennaError = TRUE;
if (efAntennaDebug == TRUE)
{
TxError("Cell: %s\n", hc->hc_use->use_id);
TxError("Antenna violation detected at plane %s\n",
DBPlaneLongNameTbl[pNum2]);
TxError("Effective antenna ratio %g > limit %g\n",
saveRatio * (float)anttotal / (float)gatearea,
saveRatio);
TxError("Gate rect (%d %d) to (%d %d)\n",
gdas.r.r_xbot, gdas.r.r_ybot,
gdas.r.r_xtop, gdas.r.r_ytop);
TxError("Antenna rect (%d %d) to (%d %d)\n",
aas.r.r_xbot, aas.r.r_ybot,
aas.r.r_xtop, aas.r.r_ytop);
}
}
}
else
{
anttotal = 0.0;
saveRatio = 0.0;
for (i = 0; i < DBNumTypes; i++)
if (ExtCurStyle->exts_antennaRatio[i].ratioDiffB != INFINITY)
{
ratioTotal = ExtCurStyle->exts_antennaRatio[i].ratioDiffB +
diffarea * ExtCurStyle->exts_antennaRatio[i].ratioDiffA;
if (ratioTotal > 0)
anttotal += (double)antennaarea[i] / (double)ratioTotal;
if (ratioTotal > saveRatio)
saveRatio = ratioTotal;
}
if (anttotal > (double)gatearea)
{
antennaError = TRUE;
if (efAntennaDebug == TRUE)
{
TxError("Antenna violation detected at plane %s\n",
DBPlaneLongNameTbl[pNum2]);
TxError("Effective antenna ratio %g > limit %g\n",
saveRatio * (float)anttotal / (float)gatearea,
saveRatio);
TxError("Gate rect (%d %d) to (%d %d)\n",
gdas.r.r_xbot, gdas.r.r_ybot,
gdas.r.r_xtop, gdas.r.r_ytop);
TxError("Antenna rect (%d %d) to (%d %d)\n",
aas.r.r_xbot, aas.r.r_ybot,
aas.r.r_xtop, aas.r.r_ytop);
}
}
}
if (antennaError)
{
/* Search plane of gate type and mark all gate areas */
ams.def = editUse->cu_def;
ams.pNum = pNum2;
DBSrPaintArea((Tile *)NULL, extPathUse->cu_def->cd_planes[pNum],
&TiPlaneRect, &gatemask, areaMarkFunc, (ClientData)&ams);
/* Search metal planes and accumulate all antenna areas */
for (p = 0; p < DBNumPlanes; p++)
{
if (ExtCurStyle->exts_antennaModel & ANTENNAMODEL_PARTIAL)
if (p != pNum2) continue;
if (ExtCurStyle->exts_planeOrder[p] <= pos)
DBSrPaintArea((Tile *)NULL, extPathUse->cu_def->cd_planes[p],
&TiPlaneRect, &DBAllButSpaceAndDRCBits,
areaMarkFunc, (ClientData)&ams);
}
}
/* Put the connect table back the way it was */
if (conType >= 0)
TTMaskSetMask(&DBConnectTbl[conType], &saveConMask);
}
}
freeMagic(antennaarea);
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* areaMarkFunc --
*
* Mark the tile areas searched with feedback entries
*
* ----------------------------------------------------------------------------
*/
int
areaMarkFunc(tile, ams)
Tile *tile;
AntennaMarkStruct *ams;
{
Rect rect;
char msg[200];
TiToRect(tile, &rect);
sprintf(msg, "Antenna error at plane %s\n", DBPlaneLongNameTbl[ams->pNum]);
DBWFeedbackAdd(&rect, msg, ams->def, 1, STYLE_PALEHIGHLIGHTS);
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* areaAccumFunc --
*
* Accumulate the total tile area searched
*
* ----------------------------------------------------------------------------
*/
int
areaAccumFunc(tile, gdas)
Tile *tile;
GateDiffAccumStruct *gdas;
{
Rect *rect = &(gdas->r);
int type;
dlong area;
TiToRect(tile, rect);
area = (dlong)(rect->r_xtop - rect->r_xbot) * (dlong)(rect->r_ytop - rect->r_ybot);
gdas->accum += area;
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* antennaAccumFunc --
*
* Accumulate the total tile area searched, keeping an individual
* count for each tile type. If the antenna model is SIDEWALL, then
* calculate the area of the tile sidewall (tile perimeter * layer
* thickness), rather than the drawn tile area.
*
* ----------------------------------------------------------------------------
*/
int
antennaAccumFunc(tile, aaptr)
Tile *tile;
AntennaAccumStruct *aaptr;
{
Rect *rect = &(aaptr->r);
dlong area;
int type;
dlong *typeareas = aaptr->accum;
int plane = aaptr->pNum;
float thick;
type = TiGetType(tile);
TiToRect(tile, rect);
if (ExtCurStyle->exts_antennaRatio[type].areaType & ANTENNAMODEL_SIDEWALL)
{
/* Accumulate perimeter of tile where tile abuts space */
Tile *tp;
int perimeter = 0, pmax, pmin;
/* Top */
for (tp = RT(tile); RIGHT(tp) > LEFT(tile); tp = BL(tp))
{
if (TiGetBottomType(tp) == TT_SPACE)
{
pmin = MAX(LEFT(tile), LEFT(tp));
pmax = MIN(RIGHT(tile), RIGHT(tp));
perimeter += (pmax - pmin);
}
}
/* Bottom */
for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp = TR(tp))
{
if (TiGetTopType(tp) == TT_SPACE)
{
pmin = MAX(LEFT(tile), LEFT(tp));
pmax = MIN(RIGHT(tile), RIGHT(tp));
perimeter += (pmax - pmin);
}
}
/* Left */
for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp = RT(tp))
{
if (TiGetRightType(tp) == TT_SPACE)
{
pmin = MAX(BOTTOM(tile), BOTTOM(tp));
pmax = MIN(TOP(tile), TOP(tp));
perimeter += (pmax - pmin);
}
}
/* Right */
for (tp = TR(tile); TOP(tp) > BOTTOM(tile); tp = LB(tp))
{
if (TiGetLeftType(tp) == TT_SPACE)
{
pmin = MAX(BOTTOM(tile), BOTTOM(tp));
pmax = MIN(TOP(tile), TOP(tp));
perimeter += (pmax - pmin);
}
}
if (DBIsContact(type))
{
int cperim;
TileType ttype;
TileTypeBitMask sMask;
float thick;
cperim = ((rect->r_xtop - rect->r_xbot) + (rect->r_ytop - rect->r_ybot)) << 1;
/* For contacts, add the area of the perimeter to the */
/* residue (metal) type on the plane being searched. */
/* Then, if the plane is the same as the base type of */
/* the contact, add the entire perimeter area of the */
/* tile to the total for the contact type itself. */
DBFullResidueMask(type, &sMask);
for (ttype = TT_TECHDEPBASE; ttype < DBNumTypes; ttype++)
if (TTMaskHasType(&sMask, ttype))
if (DBTypeOnPlane(ttype, plane))
{
thick = ExtCurStyle->exts_thick[ttype];
typeareas[ttype] += (dlong)((float)perimeter * thick);
}
if (type >= DBNumUserLayers)
{
DBResidueMask(type, &sMask);
for (ttype = TT_TECHDEPBASE; ttype < DBNumTypes; ttype++)
if (TTMaskHasType(&sMask, ttype))
if (DBTypeOnPlane(ttype, plane))
{
thick = ExtCurStyle->exts_thick[ttype];
typeareas[ttype] += (dlong)((float)perimeter * thick);
break;
}
}
else
{
thick = ExtCurStyle->exts_thick[type];
typeareas[type] += (dlong)((float)perimeter * thick);
}
}
else
{
/* Area is perimeter times layer thickness */
thick = ExtCurStyle->exts_thick[type];
typeareas[type] += (dlong)((float)perimeter * thick);
}
}
else if (ExtCurStyle->exts_antennaRatio[type].areaType & ANTENNAMODEL_SURFACE)
{
/* Simple tile area calculation */
area = (dlong)(rect->r_xtop - rect->r_xbot)
* (dlong)(rect->r_ytop - rect->r_ybot);
/* If type is a contact, then add area to both residues as well */
/* as the contact type. */
/* NOTE: Restrict area counts per plane so areas of contacts */
/* are not double-counted. */
if (DBIsContact(type))
{
TileType ttype;
TileTypeBitMask sMask;
DBFullResidueMask(type, &sMask);
for (ttype = TT_TECHDEPBASE; ttype < DBNumTypes; ttype++)
if (TTMaskHasType(&sMask, ttype))
if (DBTypeOnPlane(ttype, plane))
typeareas[ttype] += area;
if (type >= DBNumUserLayers)
{
DBResidueMask(type, &sMask);
for (ttype = TT_TECHDEPBASE; ttype < DBNumTypes; ttype++)
if (TTMaskHasType(&sMask, ttype))
if (DBTypeOnPlane(ttype, plane))
{
typeareas[ttype] += area;
break;
}
}
else
typeareas[type] += area;
}
else
typeareas[type] += area;
}
return 0;
}