/* * 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 #include /* for atof() */ #include #include #include /* 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" #include "cif/cif.h" /* For CIFGetContactSize() */ #include "cif/CIFint.h" /* For CIFCurStyle */ /* C99 compat */ #include "extract/extract.h" /* Forward declarations */ int antennacheckArgs(int *pargc, char ***pargv); int antennacheckVisit(Dev *dev, HierContext *hc, float scale, Transform *trans, ClientData cdata); /* @typedef cb_extflat_visitdevs_t (CellUse *editUse) */ typedef struct { TileTypeBitMask visitMask; } nodeClient; typedef struct { HierName *lastPrefix; TileTypeBitMask visitMask; } nodeClientHier; #define NO_RESCLASS -1 #define markVisited(client, rclass) \ { TTMaskSetType(&((client)->visitMask), rclass); } #define clearVisited(client) \ { TTMaskZero(&((client)->visitMask); } #define beenVisited(client, rclass) \ ( TTMaskHasType(&((client)->visitMask), rclass) ) #define initNodeClient(node) \ { \ (node)->efnode_client = (ClientData) mallocMagic((unsigned) (sizeof(nodeClient))); \ TTMaskZero(&(( nodeClient *)(node)->efnode_client)->visitMask); \ } #define initNodeClientHier(node) \ { \ (node)->efnode_client = (ClientData) mallocMagic((unsigned) (sizeof(nodeClientHier))); \ TTMaskZero(&(( nodeClientHier *)(node)->efnode_client)->visitMask); \ } /* 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 */ Rect via; /* Holds any one visitid via rectangle */ CellDef *def; /* CellDef for adding feedback */ } AntennaAccumStruct; typedef struct _gdas { dlong accum; /* Accumulated area of all gates/diff */ int pNum; /* Plane of check */ 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; const char * const *msg; bool err_result; short sd_rclass; short sub_rclass; char *devname; int idx; CellUse *editUse; static const char * const 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(NULL); 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(NULL); 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) == FALSE) { EFDone(NULL); return /* TCL_ERROR */; } /* Convert the hierarchical description to a flat one */ flatFlags = EF_FLATNODES | EF_WARNABSTRACT; TxPrintf("Building flattened netlist.\n"); EFFlatBuild(inName, flatFlags); /* Get device information from the current extraction style */ idx = 0; while (ExtGetDevInfo(idx++, &devname, NULL, NULL, NULL, NULL, NULL)) { if (idx == TT_MAXTYPES) { TxError("Error: Ran out of space for device types!\n"); break; } efBuildAddStr(EFDevTypes, &EFDevNumTypes, TT_MAXTYPES, devname); } /* Build device lookup table */ EFDeviceTypes = (TileType *)mallocMagic(EFDevNumTypes * sizeof(TileType)); for (i = 0; i < EFDevNumTypes; i++) if (EFDevTypes[i]) EFDeviceTypes[i] = extGetDevType(EFDevTypes[i]); efGates = 0; TxPrintf("Running antenna checks.\n"); EFVisitDevs(antennacheckVisit, PTR2CD(editUse)); EFFlatDone(NULL); EFDone(NULL); 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. * * ---------------------------------------------------------------------------- */ /* @typedef cb_extflat_visitdevs_t (CellUse *editUse) */ int antennacheckVisit( Dev *dev, /* Device being output */ HierContext *hc, /* Hierarchical context down to this device */ float scale, /* Scale transform for output */ Transform *trans, /* Coordinate transform */ ClientData cdata) /* ClientData is edit cell use */ { CellUse *editUse = (CellUse *) CD2PTR(cdata); DevTerm *gate; TileType t, conType; int pos, pNum, pNum2, pmax, p, i, j, total; dlong gatearea, diffarea; double anttotal, conttotal; 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)) { freeMagic(antennaarea); 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 tile types and accumulate all tiedown areas */ gdas.accum = (dlong)0; for (p = 0; p < DBNumPlanes; p++) { gdas.pNum = 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; gdas.pNum = pNum; 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; conttotal = 0.0; saveRatio = 0.0; for (i = 0; i < DBNumUserLayers; i++) { if (ExtCurStyle->exts_antennaRatio[i].ratioGate > 0) { /* Partial model computes vias separately */ if ((ExtCurStyle->exts_antennaModel & ANTENNAMODEL_PARTIAL) && !DBIsContact(i)) anttotal += (double)antennaarea[i] / (double)ExtCurStyle->exts_antennaRatio[i].ratioGate; else if ((ExtCurStyle->exts_antennaModel & ANTENNAMODEL_PARTIAL) && (DBPlane(i) == pNum2)) conttotal += (double)antennaarea[i] / (double)ExtCurStyle->exts_antennaRatio[i].ratioGate; } if (ExtCurStyle->exts_antennaRatio[i].ratioGate > saveRatio) saveRatio = ExtCurStyle->exts_antennaRatio[i].ratioGate; } /* gatearea == 0 indicates that something went wrong---No device * of type "t" was found connected to this net. This should be * reported as an error. Avoid generating an antenna error with * infinite area ratio. */ if ((gatearea > 0) && (anttotal > (double)gatearea)) { antennaError = TRUE; if (efAntennaDebug == TRUE) { if (hc->hc_use->use_id) TxError("Cell: %s\n", hc->hc_use->use_id); else TxError("Cell: %s\n", hc->hc_use->use_def->def_name); 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 ((gatearea > 0) && (conttotal > (double)gatearea)) { antennaError = TRUE; if (efAntennaDebug == TRUE) { if (hc->hc_use->use_id) TxError("Cell: %s\n", hc->hc_use->use_id); else TxError("Cell: %s\n", hc->hc_use->use_def->def_name); TxError("Antenna violation detected at plane %s contact\n", DBPlaneLongNameTbl[pNum2]); TxError("Effective antenna ratio %g > limit %g\n", saveRatio * (float)conttotal / (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; conttotal = 0.0; saveRatio = 0.0; for (i = 0; i < DBNumUserLayers; i++) if (ExtCurStyle->exts_antennaRatio[i].ratioDiffB != INFINITY) { /* Compute effective gate ratio increased by diffusion area */ ratioTotal = ExtCurStyle->exts_antennaRatio[i].ratioGate + ExtCurStyle->exts_antennaRatio[i].ratioDiffB + ExtCurStyle->exts_antennaRatio[i].ratioDiffA * (double)diffarea; if (ratioTotal > 0) { /* Partial model computes vias separately */ if ((ExtCurStyle->exts_antennaModel & ANTENNAMODEL_PARTIAL) && !DBIsContact(i)) anttotal += (double)antennaarea[i] / ratioTotal; else if ((ExtCurStyle->exts_antennaModel & ANTENNAMODEL_PARTIAL) && (DBPlane(i) == pNum2)) conttotal += (double)antennaarea[i] / ratioTotal; } if (ratioTotal > saveRatio) saveRatio = ratioTotal; } if (anttotal > (double)gatearea) { antennaError = TRUE; if (efAntennaDebug == TRUE) { if (hc->hc_use->use_id) TxError("Cell: %s\n", hc->hc_use->use_id); else TxError("Cell: %s\n", hc->hc_use->use_def->def_name); 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 (conttotal > (double)gatearea) { antennaError = TRUE; if (efAntennaDebug == TRUE) { if (hc->hc_use->use_id) TxError("Cell: %s\n", hc->hc_use->use_id); else TxError("Cell: %s\n", hc->hc_use->use_def->def_name); TxError("Antenna violation detected at plane %s contact\n", DBPlaneLongNameTbl[pNum2]); TxError("Effective antenna ratio %g > limit %g\n", saveRatio * (float)conttotal / (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; /* Avoid double-counting the area of contacts */ if (IsSplit(tile)) type = SplitSide(tile) ? SplitRightType(tile) : SplitLeftType(tile); else type = TiGetType(tile); if (DBIsContact(type)) if (DBPlane(type) != gdas->pNum) return 0; 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); Rect *cont = &(aaptr->via); dlong area; int type; dlong *typeareas = aaptr->accum; int plane = aaptr->pNum; float thick, fcutsize; int cutsize, cutsep, cutborder, nx, ny, w, h; type = TiGetType(tile); if (ExtCurStyle->exts_antennaRatio[type].areaType & ANTENNAMODEL_SIDEWALL) { if (DBIsContact(type)) { int cperim; TileType ttype; TileTypeBitMask sMask; float thick; TiToRect(tile, cont); /* Simple cut area calculation. Note that this is not the same as */ /* the GDS output routine for contact cuts, and can be wrong for */ /* partially overlapping contacts where the tiles get subdivided. */ CIFGetContactSize(type, &cutsize, &cutsep, &cutborder); w = cont->r_xtop - cont->r_xbot; h = cont->r_ytop - cont->r_ybot; nx = (w - 2 * cutborder) / (cutsize + cutsep); ny = (h - 2 * cutborder) / (cutsize + cutsep); if (nx == 0) nx = 1; if (ny == 0) ny = 1; cperim = nx * ny * (cutsize << 2) / CIFCurStyle->cs_scaleFactor; /* 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)cperim * thick); } /* NOTE: The "partial" model ignores the contribution of vias. */ if (!(ExtCurStyle->exts_antennaModel & ANTENNAMODEL_PARTIAL)) { if (type >= DBNumUserLayers) { for (ttype = TT_TECHDEPBASE; ttype < DBNumTypes; ttype++) if (TTMaskHasType(&sMask, ttype)) if (DBTypeOnPlane(ttype, plane)) { thick = ExtCurStyle->exts_thick[ttype]; typeareas[ttype] += (dlong)((float)cperim * thick); break; } } else { thick = ExtCurStyle->exts_thick[type]; typeareas[type] += (dlong)((float)cperim * thick); } } } else { Tile *tp; int perimeter = 0, pmax, pmin; TiToRect(tile, rect); /* Accumulate perimeter of tile where tile abuts space */ /* 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); } } /* 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) { /* 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; TiToRect(tile, cont); /* Simple cut area calculation. Note that this is not the same as */ /* the GDS output routine for contact cuts, and can be wrong for */ /* partially overlapping contacts where the tiles get subdivided. */ CIFGetContactSize(type, &cutsize, &cutsep, &cutborder); w = cont->r_xtop - cont->r_xbot; h = cont->r_ytop - cont->r_ybot; nx = (w - 2 * cutborder) / (cutsize + cutsep); ny = (h - 2 * cutborder) / (cutsize + cutsep); if (nx == 0) nx = 1; if (ny == 0) ny = 1; fcutsize = (float)cutsize / (float)CIFCurStyle->cs_scaleFactor; area = (dlong)(nx * ny) * (dlong)(fcutsize * fcutsize); DBFullResidueMask(type, &sMask); for (ttype = TT_TECHDEPBASE; ttype < DBNumTypes; ttype++) if (TTMaskHasType(&sMask, ttype)) if (DBTypeOnPlane(ttype, plane)) typeareas[ttype] += area; if (type >= DBNumUserLayers) { for (ttype = TT_TECHDEPBASE; ttype < DBNumTypes; ttype++) if (TTMaskHasType(&sMask, ttype)) if (DBTypeOnPlane(ttype, plane)) { typeareas[ttype] += area; break; } } else typeareas[type] += area; } else { TiToRect(tile, rect); area = (dlong)(rect->r_xtop - rect->r_xbot) * (dlong)(rect->r_ytop - rect->r_ybot); typeareas[type] += area; } } return 0; }