#ifndef lint static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/resis/ResMain.c,v 1.4 2010/06/24 12:37:56 tim Exp $"; #endif /* not lint */ #include #include #include #include #include "utils/magic.h" #include "utils/geometry.h" #include "utils/geofast.h" #include "tiles/tile.h" #include "utils/hash.h" #include "database/database.h" #include "utils/malloc.h" #include "textio/textio.h" #include "extract/extract.h" #include "extract/extractInt.h" #include "windows/windows.h" #include "dbwind/dbwind.h" #include "utils/tech.h" #include "select/select.h" #include "textio/txcommands.h" #include "resis/resis.h" CellUse *ResUse = NULL; /* Our use and def */ CellDef *ResDef = NULL; TileTypeBitMask ResConnectWithSD[NT]; /* A mask that goes from */ /* SD's to devices. */ TileTypeBitMask ResCopyMask[NT]; /* Indicates which tiles */ /* are to be copied. */ resResistor *ResResList = NULL; /* Resistor list */ resNode *ResNodeList = NULL; /* Processed Nodes */ resDevice *ResDevList = NULL; /* Devices */ ResContactPoint *ResContactList = NULL; /* Contacts */ resNode *ResNodeQueue = NULL; /* Pending nodes */ resNode *ResOriginNode = NULL; /* node where R=0 */ resNode *resCurrentNode; int ResTileCount = 0; /* Number of tiles rn_status */ extern ExtRegion *ResFirst(); extern Tile *FindStartTile(); extern int ResEachTile(); extern int ResLaplaceTile(); extern ResSimNode *ResInitializeNode(); TileTypeBitMask ResSDTypesBitMask; TileTypeBitMask ResSubTypesBitMask; extern HashTable ResNodeTable; /* *-------------------------------------------------------------------------- * * ResInitializeConn-- * * Sets up mask by Source/Drain type of devices. This is * exts_deviceSDtypes turned inside out. * * Results: none * * Side Effects: Sets up ResConnectWithSD. * *------------------------------------------------------------------------- */ void ResInitializeConn() { TileType dev, ttype; char *dev_name; int i; ExtDevice *devptr; for (dev = TT_TECHDEPBASE; dev < TT_MAXTYPES; dev++) { for (devptr = ExtCurStyle->exts_device[dev]; devptr; devptr = devptr->exts_next) { if ((devptr != NULL) && ((dev_name = devptr->exts_deviceName) != NULL) && (strcmp(dev_name, "None"))) { for (ttype = TT_TECHDEPBASE; ttype < TT_MAXTYPES; ttype++) { for (i = 0; ; i++) { if (TTMaskIsZero(&(devptr->exts_deviceSDTypes[i]))) break; if TTMaskHasType(&(devptr->exts_deviceSDTypes[i]), ttype) TTMaskSetType(&ResConnectWithSD[ttype], dev); } if TTMaskHasType(&(devptr->exts_deviceSubstrateTypes), ttype) TTMaskSetType(&ResConnectWithSD[ttype], dev); } } } TTMaskSetMask(&ResConnectWithSD[dev], &DBConnectTbl[dev]); } } /* * ---------------------------------------------------------------------------- * * ResGetReCell -- * * This procedure makes sure that ResUse,ResDef * have been properly initialized to refer to a cell definition * named "__RESIS__". * * Results: * None. * * Side effects: * A new cell use and/or def are created if necessary. * * -------------------------------------------------------------------------- */ void ResGetReCell() { if (ResUse != NULL) return; ResDef = DBCellLookDef("__RESIS__"); if (ResDef == NULL) { ResDef = DBCellNewDef("__RESIS__"); ASSERT (ResDef != (CellDef *) NULL, "ResGetReCell"); DBCellSetAvail(ResDef); ResDef->cd_flags |= CDINTERNAL; } ResUse = DBCellNewUse(ResDef, (char *) NULL); DBSetTrans(ResUse, &GeoIdentityTransform); ResUse->cu_expandMask = CU_DESCEND_SPECIAL; } /* *-------------------------------------------------------------------------- * * ResDissolveContacts-- * * results: none * * Side Effects: All contacts in the design are broken into their * constituent layers. There should be no contacts in ResDef after * this procedure runs. * * *------------------------------------------------------------------------ */ void ResDissolveContacts(contacts) ResContactPoint *contacts; { TileType t, oldtype; Tile *tp; TileTypeBitMask residues; for (; contacts != (ResContactPoint *)NULL; contacts = contacts->cp_nextcontact) { oldtype=contacts->cp_type; #ifdef PARANOID if (oldtype == TT_SPACE) TxError("Error in Contact Dissolving for %s \n",ResCurrentNode); #endif DBFullResidueMask(oldtype, &residues); DBErase(ResUse->cu_def, &(contacts->cp_rect), oldtype); for (t = TT_TECHDEPBASE; t < DBNumTypes; t++) if (TTMaskHasType(&residues, t)) DBPaint(ResUse->cu_def, &(contacts->cp_rect), t); tp = PlaneGetHint(ResDef->cd_planes[DBPlane(contacts->cp_type)]); GOTOPOINT(tp, &(contacts->cp_rect.r_ll)); #ifdef PARANOID if (TiGetTypeExact(tp) == contacts->cp_type) TxError("Error in Contact Preprocess Routines\n"); #endif } } /* *--------------------------------------------------------------------------- * * ResMakePortBreakpoints -- * * Search for nodes which are ports, and force them to be breakpoints * in the "tileJunk" field of their respective tiles in ResUse. This * ensures that connected nodes that stretch between two ports will * not be assumed to be "hanging" nodes. * * Do the same thing for labels. * *---------------------------------------------------------------------------- */ void ResMakePortBreakpoints(def) CellDef *def; { Plane *plane; Rect *rect; TileTypeBitMask mask; HashSearch hs; HashEntry *entry; ResSimNode *node; int ResAddBreakpointFunc(); /* Forward Declaration */ HashStartSearch(&hs); while((entry = HashNext(&ResNodeTable,&hs)) != NULL) { node = (ResSimNode *)HashGetValue(entry); if (node->status & PORTNODE) { if (node->rs_ttype <= 0) { TxError("Warning: Label \"%s\" is unconnected.\n", node->name); continue; } rect = &(node->rs_bbox); /* Beware of zero-area ports */ if (rect->r_xbot == rect->r_xtop) { rect->r_xbot--; rect->r_xtop++; } if (rect->r_ybot == rect->r_ytop) { rect->r_ybot--; rect->r_ytop++; } /* If label is on a contact, the contact has been dissolved. */ /* Assume that the uppermost residue is the port. This may */ /* not necessarily be the case. Could do a boundary scan on */ /* each residue plane to see which side of the contact is */ /* the internal connection in the def. . . */ if (DBIsContact(node->rs_ttype)) { TileType type; DBFullResidueMask(node->rs_ttype, &mask); for (type = DBNumUserLayers - 1; type >= TT_TECHDEPBASE; type--) if (TTMaskHasType(&mask, type)) { plane = def->cd_planes[DBPlane(type)]; break; } } else { TTMaskSetOnlyType(&mask, node->rs_ttype); plane = def->cd_planes[DBPlane(node->rs_ttype)]; } (void) DBSrPaintArea((Tile *) NULL, plane, rect, &mask, ResAddBreakpointFunc, (ClientData)node); } } } /* *--------------------------------------------------------------------------- * * ResMakeLabelBreakpoints -- * * Search for labels that are part of a node, and force them to be * breakpoints in the "tileJunk" field of their respective tiles in * ResUse. This ensures (among other things) that pins of a top level * cell will be retained and become the endpoint of a net. * *---------------------------------------------------------------------------- */ void ResMakeLabelBreakpoints(def, goodies) CellDef *def; ResGlobalParams *goodies; { Plane *plane; Rect *rect; TileTypeBitMask mask; HashEntry *entry; ResSimNode *node; Label *slab; int ResAddBreakpointFunc(); /* Forward Declaration */ for (slab = def->cd_labels; slab != NULL; slab = slab->lab_next) { /* Avoid any empty-string labels, or it will end up as */ /* missing terminal on a device. */ if (*(slab->lab_text) == '\0') continue; entry = HashFind(&ResNodeTable, slab->lab_text); node = ResInitializeNode(entry); /* If the drivepoint position changes and the drivepoint is */ /* in the "goodies" record, then make sure the tile type in */ /* "goodies" gets changed to match. */ if (goodies->rg_devloc == &node->drivepoint) goodies->rg_ttype = slab->lab_type; node->drivepoint = slab->lab_rect.r_ll; node->rs_bbox = slab->lab_rect; node->location = slab->lab_rect.r_ll; node->rs_ttype = slab->lab_type; node->type = slab->lab_type; rect = &(node->rs_bbox); /* If label is on a contact, the contact has been dissolved. */ /* Assume that the uppermost residue is the port. This may */ /* not necessarily be the case. Could do a boundary scan on */ /* each residue plane to see which side of the contact is */ /* the internal connection in the def. . . */ if (DBIsContact(slab->lab_type)) { TileType type; DBFullResidueMask(slab->lab_type, &mask); for (type = DBNumUserLayers - 1; type >= TT_TECHDEPBASE; type--) if (TTMaskHasType(&mask, type)) { plane = def->cd_planes[DBPlane(type)]; break; } } else { TTMaskSetOnlyType(&mask, slab->lab_type); plane = def->cd_planes[DBPlane(slab->lab_type)]; } (void) DBSrPaintArea((Tile *) NULL, plane, rect, &mask, ResAddBreakpointFunc, (ClientData)node); } } /* *---------------------------------------------------------------------------- * * ResAddBreakpointFunc -- * * Add a breakpoint to the "tileJunk" structure of the tile * *---------------------------------------------------------------------------- */ int ResAddBreakpointFunc(tile, node) Tile *tile; ResSimNode *node; { tileJunk *junk; if (TiGetClient(tile) == CLIENTDEFAULT) return 0; NEWPORT(node, tile); return 0; } /* *--------------------------------------------------------------------------- * * ResFindNewContactTiles -- * * * Results: none * * Side Effects: dissolving contacts eliminated the tiles that * contacts->nextcontact pointed to. This procedure finds the tile now under * center and sets that tile's ti_client field to point to the contact. The * old value of clientdata is set to nextTilecontact. * *---------------------------------------------------------------------------- */ void ResFindNewContactTiles(contacts) ResContactPoint *contacts; { int pNum; Tile *tile; TileTypeBitMask mask; for (; contacts != (ResContactPoint *) NULL; contacts = contacts->cp_nextcontact) { DBFullResidueMask(contacts->cp_type, &mask); /* Watch for types that connect to the substrate plane or well; */ /* e.g., psubstratepdiff connects to nwell but not through a */ /* contact. */ if (ExtCurStyle->exts_globSubstratePlane != -1) { TileTypeBitMask cMask; TTMaskAndMask3(&cMask, &DBConnectTbl[contacts->cp_type], &DBPlaneTypes[ExtCurStyle->exts_globSubstratePlane]); if (!TTMaskIsZero(&cMask)) TTMaskSetMask(&mask, &cMask); } for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++) { tile = PlaneGetHint(ResDef->cd_planes[pNum]); GOTOPOINT(tile, &(contacts->cp_center)); #ifdef PARANOID if (tile == (Tile *) NULL) { TxError("Error: setting contact tile to null\n"); } #endif if ((IsSplit(tile) && TTMaskHasType(&mask, TiGetRightType(tile))) || TTMaskHasType(&mask, TiGetType(tile))) { tileJunk *j = (tileJunk *)TiGetClientPTR(tile); cElement *ce; ce = (cElement *) mallocMagic((unsigned) (sizeof(cElement))); contacts->cp_tile[contacts->cp_currentcontact] = tile; ce->ce_thisc = contacts; ce->ce_nextc = j->contactList; (contacts->cp_currentcontact) += 1; j->contactList = ce; } else if (!IsSplit(tile)) { TileType ttype = TiGetTypeExact(tile); if (DBIsContact(ttype)) { /* Handle the exceptional case in which a contact * type is its own residue. This can be used for * devices whose terminals are always a contact * and for which a non-contact type cannot be drawn. */ if (TTMaskIntersect(DBResidueMask(ttype), &mask)) { tileJunk *j = (tileJunk *)TiGetClientPTR(tile); cElement *ce; ce = (cElement *) mallocMagic((unsigned) (sizeof(cElement))); contacts->cp_tile[contacts->cp_currentcontact] = tile; ce->ce_thisc = contacts; ce->ce_nextc = j->contactList; (contacts->cp_currentcontact) += 1; j->contactList = ce; } } } } #ifdef PARANOID if (contacts->cp_currentcontact >= LAYERS_PER_CONTACT) { TxError("Error: Not enough space allocated for contact nodes\n"); } #endif } } /* *-------------------------------------------------------------------------- * * ResProcessTiles--Calls ResEachTile with processed tiles belonging to * nodes in ResNodeQueue. When all the tiles corresponding * to a node have been processed, the node is moved to * ResNodeList. * * Results: Return 1 if any error occurred, 0 otherwise. * * Side Effects: Cleans extraneous linked lists from nodes. * *-------------------------------------------------------------------------- */ int ResProcessTiles(goodies, origin) Point *origin; ResGlobalParams *goodies; { Tile *startTile; int tilenum, merged; resNode *resptr2; jElement *workingj; cElement *workingc; ResFixPoint *fix; resNode *resptr; int (*tilefunc)(); #ifdef LAPLACE tilefunc = (ResOptionsFlags & ResOpt_DoLaplace) ? ResLaplaceTile : ResEachTile; #else tilefunc = ResEachTile; #endif if (ResOptionsFlags & ResOpt_Signal) { startTile = FindStartTile(goodies, origin); if (startTile == NULL) return(1); resCurrentNode = NULL; (void) (*tilefunc)(startTile, origin); } #ifdef ARIEL else if (ResOptionsFlags & ResOpt_Power) { for (fix = ResFixList; fix != NULL; fix = fix->fp_next) { Tile *tile = fix->fp_tile; if (tile == NULL) { tile = PlaneGetHint(ResDef->cd_planes[DBPlane(fix->fp_ttype)]); GOTOPOINT(tile, &(fix->fp_loc)); if (TiGetTypeExact(tile) != TT_SPACE) { fix->fp_tile = tile; } else { tile = NULL; } } if (tile != NULL) { int x = fix->fp_loc.p_x; int y = fix->fp_loc.p_y; resptr = (resNode *) mallocMagic((unsigned)(sizeof(resNode))); InitializeNode(resptr, x, y, RES_NODE_ORIGIN); resptr->rn_status = TRUE; resptr->rn_noderes = 0; ResAddToQueue(resptr, &ResNodeQueue); fix->fp_node = resptr; NEWBREAK(resptr, tile, x, y, NULL); } } for (fix = ResFixList; fix != NULL; fix = fix->fp_next) { Tile *tile = fix->fp_tile; if (tile != NULL && (((tileJunk *)TiGetClientPTR(tile)->tj_status & RES_TILE_DONE) == 0) { resCurrentNode = fix->fp_node; (void) (*tilefunc)(tile, (Point *)NULL); } } } #endif #ifdef PARANOID else { TxError("Unknown analysis type in ResProcessTiles\n"); } #endif /* Process Everything else */ while (ResNodeQueue != NULL) { /* * merged keeps track of whether another node gets merged into * the current one. If it does, then the node must be processed * because additional junctions or contacts were added */ resptr2 = ResNodeQueue; merged = FALSE; /* Process all junctions associated with node */ for (workingj = resptr2->rn_je; workingj != NULL; workingj = workingj->je_nextj) { ResJunction *rj = workingj->je_thisj; if (rj->rj_status == FALSE) { for (tilenum = 0; tilenum < TILES_PER_JUNCTION; tilenum++) { Tile *tile = rj->rj_Tile[tilenum]; tileJunk *j = (tileJunk *)TiGetClientPTR(tile); if ((j->tj_status & RES_TILE_DONE) == 0) { resCurrentNode = resptr2; merged |= (*tilefunc)(tile, (Point *)NULL); } if (merged & ORIGIN) break; } if (merged & ORIGIN) break; rj->rj_status = TRUE; } } /* Next, Process all contacts. */ for (workingc = resptr2->rn_ce; workingc != NULL; workingc = workingc->ce_nextc) { ResContactPoint *cp = workingc->ce_thisc; if (merged & ORIGIN) break; if (cp->cp_status == FALSE) { int newstatus = TRUE; for (tilenum = 0; tilenum < cp->cp_currentcontact; tilenum++) { Tile *tile = cp->cp_tile[tilenum]; tileJunk *j = (tileJunk *) TiGetClientPTR(tile); if ((j->tj_status & RES_TILE_DONE) == 0) { if (cp->cp_cnode[tilenum] == resptr2) { resCurrentNode = resptr2; merged |= (*tilefunc)(tile,(Point *)NULL); } else { newstatus = FALSE; } } if (merged & ORIGIN) break; } if (merged & ORIGIN) break; cp->cp_status = newstatus; } } /* * If nothing new has been added via a merge, then the node is * finished. It is removed from the pending queue, added to the * done list, cleaned up, and passed to ResDoneWithNode */ if (merged == FALSE) { ResRemoveFromQueue(resptr2, &ResNodeQueue); resptr2->rn_more = ResNodeList; resptr2->rn_less = NULL; resptr2->rn_status &= ~PENDING; resptr2->rn_status |= FINISHED | MARKED; if (ResNodeList != NULL) { ResNodeList->rn_less = resptr2; } if (resptr2->rn_noderes == 0) { ResOriginNode=resptr2; } ResNodeList = resptr2; ResCleanNode(resptr2, FALSE, &ResNodeList, &ResNodeQueue); ResDoneWithNode(resptr2); } } return(0); } /* *------------------------------------------------------------------------- * * ResCalcPerimOverlap --- * * Given a device tile, compute simple perimeter and overlap of the device * by the net under consideration. * * Results: * None. * * Side Effects: * The ResDevTile structure is updated with the overlap and perimeter * values. * *------------------------------------------------------------------------- */ void ResCalcPerimOverlap(tile, dev) Tile *tile; ResDevTile *dev; { Tile *tp; int t1; int overlap; TileTypeBitMask *omask; dev->perim = (TOP(tile) - BOTTOM(tile) - LEFT(tile) + RIGHT(tile)) << 1; overlap = 0; t1 = TiGetType(tile); omask = &(ExtCurStyle->exts_nodeConn[t1]); /* left */ for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp = RT(tp)) { if TTMaskHasType(omask, TiGetType(tp)) overlap += MIN(TOP(tile), TOP(tp)) - MAX(BOTTOM(tile), BOTTOM(tp)); } /* right */ for (tp = TR(tile); TOP(tp) > BOTTOM(tile); tp=LB(tp)) { if TTMaskHasType(omask, TiGetType(tp)) overlap += MIN(TOP(tile), TOP(tp)) - MAX(BOTTOM(tile), BOTTOM(tp)); } /* top */ for (tp = RT(tile); RIGHT(tp) > LEFT(tile); tp = BL(tp)) { if TTMaskHasType(omask, TiGetType(tp)) overlap += MIN(RIGHT(tile), RIGHT(tp)) - MAX(LEFT(tile), LEFT(tp)); } /* bottom */ for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp=TR(tp)) { if TTMaskHasType(omask, TiGetType(tp)) overlap += MIN(RIGHT(tile), RIGHT(tp)) - MAX(LEFT(tile), LEFT(tp)); } dev->overlap += overlap; } /* *------------------------------------------------------------------------- * * resMakeDevFunc -- * * Callback function from ResExtractNet. For each device in a node's * device list pulled from the .sim file, find the tile(s) corresponding * to the device in the source tree, and fill out the complete device * record (namely the full device area). * * Result: * Return 1 to stop the search because the device has been found. * *------------------------------------------------------------------------- */ int resMakeDevFunc(tile, cx) Tile *tile; TreeContext *cx; { ResDevTile *thisDev = (ResDevTile *)cx->tc_filter->tf_arg; Rect devArea; TileType ttype; TiToRect(tile, &devArea); GeoTransRect(&cx->tc_scx->scx_trans, &devArea, &thisDev->area); if (IsSplit(tile)) ttype = (SplitSide(tile)) ? SplitRightType(tile) : SplitLeftType(tile); else ttype = TiGetType(tile); /* If more than one tile type extracts to the same device, then */ /* the device type may be different from what was recorded when */ /* the sim file was read. Restricted to the plane of the */ /* original type to avoid conflict with completely different */ /* devices (like transistors vs. MiM caps). */ if (ttype != thisDev->type) { if (DBPlane(ttype) != DBPlane(thisDev->type)) return 0; /* Completely different device? */ thisDev->type = ttype; } return 1; } /* *------------------------------------------------------------------------- * * resExpandDevFunc -- * * Do a boundary search on the first tile found in the search context * path belonging to a device, including all tiles that belong to the * device. For each compatible tile found, paint the device tile * type into ResUse and calculate the overlap. * * Returns: * 1 to stop the search (only the first tile of a device needs to be * found). * * Side effects: * Paints into ResUse and recalculates values of thisDev. *------------------------------------------------------------------------- */ #define DEV_PROCESSED 1 int resExpandDevFunc(tile, cx) Tile *tile; TreeContext *cx; { ResDevTile *thisDev = (ResDevTile *)cx->tc_filter->tf_arg; static Stack *devExtentsStack = NULL; static Stack *devResetStack = NULL; TileTypeBitMask *rMask; Tile *tp, *tp2; TileType ttype; int pNum; Rect area; pNum = DBPlane(thisDev->type); if (devExtentsStack == NULL) devExtentsStack = StackNew(8); if (devResetStack == NULL) devResetStack = StackNew(8); TiSetClientINT(tile, DEV_PROCESSED); STACKPUSH((ClientData)tile, devExtentsStack); while (!StackEmpty(devExtentsStack)) { tp = (Tile *) STACKPOP(devExtentsStack); STACKPUSH((ClientData)tp, devResetStack); TiToRect(tp, &area); /* Paint type thisDev->type into ResUse over area of tile "tp" */ DBNMPaintPlane(ResUse->cu_def->cd_planes[pNum], TiGetTypeExact(tp), &area, DBStdPaintTbl(thisDev->type, pNum), (PaintUndoInfo *)NULL); /* Add source/drain perimeter overlap to the device for this tile */ ResCalcPerimOverlap(tp, thisDev); /* Search boundary of the device tile for more tiles belonging */ /* to the device. If contacts are found, replace them with the */ /* device type. */ /* top */ for (tp2 = RT(tp); RIGHT(tp2) > LEFT(tp); tp2 = BL(tp2)) { if (TiGetClientINT(tp2) == DEV_PROCESSED) continue; ttype = TiGetBottomType(tp2); if ((ttype == thisDev->type) || (DBIsContact(ttype) && TTMaskHasType(DBResidueMask(ttype), thisDev->type))) { TiSetClientINT(tp2, DEV_PROCESSED); STACKPUSH((ClientData)tp2, devExtentsStack); } } /* bottom */ for (tp2 = LB(tp); LEFT(tp2) < RIGHT(tp); tp2 = TR(tp2)) { if (TiGetClientINT(tp2) == DEV_PROCESSED) continue; ttype = TiGetTopType(tp2); if ((ttype == thisDev->type) || (DBIsContact(ttype) && TTMaskHasType(DBResidueMask(ttype), thisDev->type))) { TiSetClientINT(tp2, DEV_PROCESSED); STACKPUSH((ClientData)tp2, devExtentsStack); } } /* right */ for (tp2 = TR(tp); TOP(tp2) > BOTTOM(tp); tp2 = LB(tp2)) { if (TiGetClientINT(tp2) == DEV_PROCESSED) continue; ttype = TiGetLeftType(tp2); if ((ttype == thisDev->type) || (DBIsContact(ttype) && TTMaskHasType(DBResidueMask(ttype), thisDev->type))) { TiSetClientINT(tp2, DEV_PROCESSED); STACKPUSH((ClientData)tp2, devExtentsStack); } } /* left */ for (tp2 = BL(tp); BOTTOM(tp2) < TOP(tp); tp2 = RT(tp2)) { if (TiGetClientINT(tp2) == DEV_PROCESSED) continue; ttype = TiGetRightType(tp2); if ((ttype == thisDev->type) || (DBIsContact(ttype) && TTMaskHasType(DBResidueMask(ttype), thisDev->type))) { TiSetClientINT(tp2, DEV_PROCESSED); STACKPUSH((ClientData)tp2, devExtentsStack); } } } /* Reset the device tile client records */ while (!StackEmpty(devResetStack)) { tp = (Tile *) STACKPOP(devResetStack); TiSetClient(tp, CLIENTDEFAULT); } /* Return 1 to stop the search; we only need to run this from */ /* the first device tile. */ return 1; } /* *------------------------------------------------------------------------- * * ResShaveContacts --- * * Remove the top layer off of every contact in the design, leaving * only the bottom layer. This also resolves issues with stacked * contacts by leaving clean contact areas where stacked types * overlap. Contacts are removed from the plane above the search * plane, so the removal does not corrupt the current plane search. * * Results: * Return 0 to keep the search going. * *------------------------------------------------------------------------- */ int ResShaveContacts(tile, def) Tile *tile; CellDef *def; { TileType ttype; TileTypeBitMask *rmask; Rect area; Plane *plane; int pNum; int pMask; /* To do: Handle split tiles, although this is unlikely for * contact types. */ ttype = TiGetType(tile); if (DBIsContact(ttype)) { /* Remove the contact type from the plane above */ TiToRect(tile, &area); pMask = DBTypePlaneMaskTbl[ttype]; for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++) if (PlaneMaskHasPlane(pMask, pNum)) break; for (++pNum; pNum < DBNumPlanes; pNum++) if (PlaneMaskHasPlane(pMask, pNum)) { plane = def->cd_planes[pNum]; DBPaintPlane(plane, &area, DBStdEraseTbl(ttype, pNum), (PaintUndoInfo *)NULL); } } return 0; } /* *------------------------------------------------------------------------- * * ResExtractNet-- extracts the resistance net at the specified * rn_loc. If the resulting net is greater than the tolerance, * simplify and return the resulting network. * * Results: 0 iff it worked. * * Side effects: Produces a resistance network for the node. * *------------------------------------------------------------------------- */ bool ResExtractNet(node, goodies, cellname) ResSimNode *node; ResGlobalParams *goodies; char *cellname; { SearchContext scx; TileTypeBitMask FirstTileMask; TileTypeBitMask tMask; Point startpoint; static int first = 1; ResDevTile *DevTiles, *thisDev; ResFixPoint *fix; devPtr *tptr; int pNum; int resMakeDevFunc(); int resExpandDevFunc(); /* Make sure all global network variables are reset */ ResResList = NULL; ResNodeList = NULL; ResDevList = NULL; ResNodeQueue = NULL; ResContactList = NULL; ResOriginNode = NULL; /* Pass back network pointers */ goodies->rg_maxres = 0; goodies->rg_tilecount = 0; /* Set up internal stuff if this is the first time through */ if (first) { ResInitializeConn(); first = 0; ResGetReCell(); } /* Initialize Cell */ if (cellname) { CellDef *def = DBCellLookDef(cellname); if (def == (CellDef *)NULL) { TxError("Error: No such cell \"%s\"\n", cellname); return TRUE; } scx.scx_use = DBCellNewUse(def, (char *)NULL); DBSetTrans (scx.scx_use, &GeoIdentityTransform); scx.scx_trans = GeoIdentityTransform; } else { MagWindow *w = ToolGetBoxWindow(&scx.scx_area, (int *)NULL); if (w == (MagWindow *)NULL) { TxError("Sorry, the box must appear in one of the windows.\n"); return TRUE; } scx.scx_use = (CellUse *) w->w_surfaceID; scx.scx_trans = GeoIdentityTransform; } DBCellClearDef(ResUse->cu_def); #ifdef ARIEL if ((ResOptionsFlags & ResOpt_Power) && strcmp(node->name, goodies->rg_name) != 0) continue; #endif /* Copy Paint */ scx.scx_area.r_ll.p_x = node->location.p_x - 2; scx.scx_area.r_ll.p_y = node->location.p_y - 2; scx.scx_area.r_ur.p_x = node->location.p_x + 2; scx.scx_area.r_ur.p_y = node->location.p_y + 2; startpoint = node->location; /* Because node->type might come from a label with a sticky type * that does not correspond exactly to the layer underneath, include * all connecting types. */ if (node->type != TT_SPACE) { TTMaskZero(&FirstTileMask); TTMaskSetMask(&FirstTileMask, &DBConnectTbl[node->type]); DBTreeCopyConnect(&scx, &FirstTileMask, 0, ResCopyMask, &TiPlaneRect, SEL_DO_LABELS, ResUse); } TTMaskZero(&ResSDTypesBitMask); TTMaskZero(&ResSubTypesBitMask); /* Add devices to ResUse from list in node */ DevTiles = NULL; for (tptr = node->firstDev; tptr; tptr = tptr->nextDev) { int result; int i; ExtDevice *devptr; thisDev = (ResDevTile *)mallocMagic(sizeof(ResDevTile)); thisDev->devptr = tptr->thisDev->rs_devptr; thisDev->type = tptr->thisDev->rs_ttype; thisDev->overlap = 0; scx.scx_area.r_ll.p_x = tptr->thisDev->location.p_x; scx.scx_area.r_ll.p_y = tptr->thisDev->location.p_y; scx.scx_area.r_xtop = scx.scx_area.r_xbot + 1; scx.scx_area.r_ytop = scx.scx_area.r_ybot + 1; result = DBTreeSrTiles(&scx, &DBAllButSpaceAndDRCBits, 0, resMakeDevFunc, (ClientData)thisDev); if (result == 0) { TxError("No device of type %s found at location %d,%d\n", DBTypeLongNameTbl[thisDev->type], tptr->thisDev->location.p_x, tptr->thisDev->location.p_y); freeMagic(thisDev); continue; } thisDev->nextDev = DevTiles; DevTiles = thisDev; /* Paint the entire device into ResUse */ TTMaskSetOnlyType(&tMask, thisDev->type); DBTreeSrTiles(&scx, &tMask, 0, resExpandDevFunc, (ClientData)thisDev); /* If the device has source/drain types in a different plane than */ /* the device identifier type, then add the source/drain types to */ /* the mask ResSDTypesBitMask. */ devptr = tptr->thisDev->rs_devptr; for (i = 0; !TTMaskIsZero(&devptr->exts_deviceSDTypes[i]); i++) TTMaskSetMask(&ResSDTypesBitMask, &devptr->exts_deviceSDTypes[i]); /* Add the substrate types to the mask ResSubTypesBitMask */ TTMaskSetMask(&ResSubTypesBitMask, &devptr->exts_deviceSubstrateTypes); /* TT_SPACE should be removed from ResSubTypesBitMask */ TTMaskClearType(&ResSubTypesBitMask, TT_SPACE); } DBReComputeBbox(ResUse->cu_def); ExtResetTiles(scx.scx_use->cu_def, extUnInit); /* To avoid issues with overlapping stacked contact types and */ /* double-counting contacts on multiple planes, erase the top */ /* contact layers of all contacts. ExtFindRegions() will still */ /* find the connectivity above but will only process one tile per */ /* contact. This temporarily creates an improper database, but */ /* the contacts are all immediately erased by ResDissolveContacts().*/ for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++) { Plane *plane = ResUse->cu_def->cd_planes[pNum]; DBSrPaintArea(PlaneGetHint(plane), plane, &(ResUse->cu_def->cd_bbox), &DBAllButSpaceAndDRCBits, ResShaveContacts, (ClientData)ResUse->cu_def); } /* Find all contacts in design and note their position */ /* NOTE: ExtFindRegions() will call ResFirst or ResEach for BOTH */ /* planes of a contact. Rather than attempting to limit the */ /* search, ResDoContacts() will just double the resistance per via */ /* so that the final value is correct. */ ResContactList = (ResContactPoint *)ExtFindRegions(ResUse->cu_def, &(ResUse->cu_def->cd_bbox), &DBAllButSpaceAndDRCBits, ResConnectWithSD, extUnInit, ResFirst, ResEach); ExtResetTiles(ResUse->cu_def, extUnInit); /* * dissolve the contacts and find which tiles now cover the point * where the tile used to be. */ ResDissolveContacts(ResContactList); /* Add "junk" fields to tiles */ for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++) { Plane *plane = ResUse->cu_def->cd_planes[pNum]; Rect *rect = &ResUse->cu_def->cd_bbox; ResFracture(plane, rect); (void) DBSrPaintClient((Tile *) NULL, plane, rect, &DBAllButSpaceAndDRCBits, (ClientData) CLIENTDEFAULT, ResAddPlumbing, (ClientData) &ResDevList); } /* Finish preprocessing. */ ResMakePortBreakpoints(ResUse->cu_def); ResMakeLabelBreakpoints(ResUse->cu_def, goodies); ResFindNewContactTiles(ResContactList); ResPreProcessDevices(DevTiles, ResDevList, ResUse->cu_def); #ifdef LAPLACE if (ResOptionsFlags & ResOpt_DoLaplace) { for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++) { Plane *plane = ResUse->cu_def->cd_planes[pNum]; Rect *rect = &ResUse->cu_def->cd_bbox; Res1d(plane, rect); } } #endif #ifdef ARIEL if (ResOptionsFlags & ResOpt_Power) { for (fix = startlist; fix != NULL; fix = fix->fp_next) { fix->fp_tile = PlaneGetHint(ResUse->cu_def->cd_planes[DBPlane(fix->fp_ttype)]); GOTOPOINT(fix->fp_tile, &fix->fp_loc); if (TiGetTypeExact(fix->fp_tile) == TT_SPACE) fix->fp_tile = NULL; } } #endif /* do extraction */ if (ResProcessTiles(goodies, &startpoint) != 0) return TRUE; return FALSE; } /* *------------------------------------------------------------------------- * * ResCleanUpEverything--After each net is extracted by ResExtractNet, * the resulting memory must be freed up, and varius trash swept under * the carpet in preparation for the next extraction. * * Results: none * * Side Effects: Frees up memory formerly occupied by network elements. * *------------------------------------------------------------------------- */ void ResCleanUpEverything() { int pNum; resResistor *oldRes; resDevice *oldDev; ResContactPoint *oldCon; /* Check integrity of internal database. Free up lists. */ for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++) { (void) DBSrPaintClient((Tile *)NULL, ResUse->cu_def->cd_planes[pNum], &(ResUse->cu_def->cd_bbox), &DBAllButSpaceAndDRCBits, (ClientData)CLIENTDEFAULT, ResRemovePlumbing, (ClientData)NULL); } while (ResNodeList != NULL) { ResCleanNode(ResNodeList, TRUE, &ResNodeList, &ResNodeQueue); } while (ResContactList != NULL) { oldCon = ResContactList; ResContactList = oldCon->cp_nextcontact; freeMagic((char *)oldCon); } while (ResResList != NULL) { oldRes = ResResList; ResResList = ResResList->rr_nextResistor; freeMagic((char *)oldRes); } while (ResDevList != NULL) { oldDev = ResDevList; ResDevList = ResDevList->rd_nextDev; if ((oldDev->rd_status & RES_DEV_SAVE) == 0) { freeMagic((char *)oldDev->rd_terminals); freeMagic((char *)oldDev); } } DBCellClearDef(ResUse->cu_def); } /* *------------------------------------------------------------------------- * * ResGetTileFunc -- * * Callback function used by FindStartTile() when searching for * terminal connections of a device that may be on planes other * than the plane of the device identifier type. Ignore space * tiles. Otherwise, for any tile found, record the tile in * the client data record and return 1 to stop the search. * * Results: * Return 0 if tile is a space tile, to keep the search going. * Return 1 otherwise to stop the search immediately because * a valid start tile has been found. * *------------------------------------------------------------------------- */ int ResGetTileFunc(tile, tpptr) Tile *tile, **tpptr; { if (TiGetType(tile) != TT_SPACE) { *tpptr = tile; return 1; } return 0; } /* *------------------------------------------------------------------------- * * FindStartTile-- To start the extraction, we need to find the first driver. * The sim file gives us the location of a point in or near (within 1 * unit) of the device. FindStartTile looks for the device, then * for adjoining diffusion. The diffusion tile is returned. * * Results: returns source diffusion tile, if it exists. Otherwise, return * NULL. * * Side Effects: none * *------------------------------------------------------------------------- */ Tile * FindStartTile(goodies, SourcePoint) Point *SourcePoint; ResGlobalParams *goodies; { Point workingPoint; Tile *tile, *tp; int pnum, t1, t2, i; ExtDevice *devptr; Rect r; bool complex; static Stack *devStack = NULL; /* If the drive point is on a contact, check for the contact residues */ /* first, then the contact type itself. */ if (DBIsContact(goodies->rg_ttype)) { TileTypeBitMask *rmask = DBResidueMask(goodies->rg_ttype); TileType savtype = goodies->rg_ttype; TileType rtype; for (rtype = TT_TECHDEPBASE; rtype < DBNumUserLayers; rtype++) if (TTMaskHasType(rmask, rtype)) { goodies->rg_ttype = rtype; if ((tile = FindStartTile(goodies, SourcePoint)) != NULL) { goodies->rg_ttype = savtype; return tile; } } goodies->rg_ttype = savtype; } workingPoint.p_x = goodies->rg_devloc->p_x; workingPoint.p_y = goodies->rg_devloc->p_y; pnum = DBPlane(goodies->rg_ttype); /* for drivepoints, we don't have to find a device */ if (goodies->rg_status & DRIVEONLY) { tile = PlaneGetHint(ResUse->cu_def->cd_planes[pnum]); GOTOPOINT(tile, &workingPoint); SourcePoint->p_x = workingPoint.p_x; SourcePoint->p_y = workingPoint.p_y; if (TiGetTypeExact(tile) == goodies->rg_ttype) return tile; else { /* On the other hand, drivepoints derived from subcircuit */ /* boundaries lie on tile boundaries, and GOTOPOINT() will */ /* pick the tile on the wrong side for TOP and RIGHT */ /* segment coincidences. */ if (workingPoint.p_x == LEFT(tile)) { for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp=RT(tp)) if (TiGetRightType(tp) == goodies->rg_ttype) return(tp); } else if (workingPoint.p_y == BOTTOM(tile)) { for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp=TR(tp)) if (TiGetTopType(tp) == goodies->rg_ttype) return(tp); } } TxError("Couldn't find wire at %d %d\n", goodies->rg_devloc->p_x, goodies->rg_devloc->p_y); return NULL; } tile = PlaneGetHint(ResUse->cu_def->cd_planes[pnum]); GOTOPOINT(tile, &workingPoint); if (IsSplit(tile)) { if (TTMaskHasType(&ExtCurStyle->exts_deviceMask, TiGetLeftType(tile)) != 0) { t1 = TiGetLeftType(tile); TiSetBody(tile, t1 & ~TT_SIDE); } else if (TTMaskHasType(&ExtCurStyle->exts_deviceMask, TiGetRightType(tile)) != 0) { t1 = TiGetRightType(tile); TiSetBody(tile, t1 & TT_SIDE); } else { TxError("Couldn't find device at %d %d\n", goodies->rg_devloc->p_x, goodies->rg_devloc->p_y); return(NULL); } } else if (TTMaskHasType(&ExtCurStyle->exts_deviceMask, TiGetType(tile)) == 0) { TxError("Couldn't find device at %d %d\n", goodies->rg_devloc->p_x, goodies->rg_devloc->p_y); return(NULL); } else t1 = TiGetType(tile); /* NOTE: There must be a way to pass the device type from a device * record's rs_devptr instead of groping around for it. */ for (devptr = ExtCurStyle->exts_device[t1]; devptr; devptr = devptr->exts_next) { for (i = 0; i < devptr->exts_deviceSDCount; i++) { complex = FALSE; /* Assume device is a single tile */ /* left */ for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp = RT(tp)) { t2 = TiGetRightType(tp); if ((t2 != TT_SPACE) && TTMaskHasType(&(devptr->exts_deviceSDTypes[i]), t2)) { SourcePoint->p_x = LEFT(tile); SourcePoint->p_y = (MIN(TOP(tile),TOP(tp)) + MAX(BOTTOM(tile), BOTTOM(tp))) >> 1; return(tp); } else { const ClientData ticlient = TiGetClient(tp); const tileJunk *tj = (tileJunk *)CD2PTR(ticlient); if (ticlient != CLIENTDEFAULT && tj->tj_status & RES_TILE_DEV) complex = TRUE; } } /* right */ for (tp = TR(tile); TOP(tp) > BOTTOM(tile); tp = LB(tp)) { t2 = TiGetLeftType(tp); if ((t2 != TT_SPACE) && TTMaskHasType(&(devptr->exts_deviceSDTypes[i]), t2)) { SourcePoint->p_x = RIGHT(tile); SourcePoint->p_y = (MIN(TOP(tile), TOP(tp))+ MAX(BOTTOM(tile), BOTTOM(tp))) >> 1; return(tp); } else { const ClientData ticlient = TiGetClient(tp); const tileJunk *tj = (tileJunk *)CD2PTR(ticlient); if (ticlient != CLIENTDEFAULT && tj->tj_status & RES_TILE_DEV) complex = TRUE; } } /* top */ for (tp = RT(tile); RIGHT(tp) > LEFT(tile); tp = BL(tp)) { t2 = TiGetBottomType(tp); if ((t2 != TT_SPACE) && TTMaskHasType(&(devptr->exts_deviceSDTypes[i]), t2)) { SourcePoint->p_y = TOP(tile); SourcePoint->p_x = (MIN(RIGHT(tile),RIGHT(tp)) + MAX(LEFT(tile), LEFT(tp))) >> 1; return(tp); } else { const ClientData ticlient = TiGetClient(tp); const tileJunk *tj = (tileJunk *)CD2PTR(ticlient); if (ticlient != CLIENTDEFAULT && tj->tj_status & RES_TILE_DEV) complex = TRUE; } } /* bottom */ for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp = TR(tp)) { t2 = TiGetTopType(tp); if ((t2 != TT_SPACE) && TTMaskHasType(&(devptr->exts_deviceSDTypes[i]), t2)) { SourcePoint->p_y = BOTTOM(tile); SourcePoint->p_x = (MIN(RIGHT(tile), RIGHT(tp)) + MAX(LEFT(tile), LEFT(tp))) >> 1; return(tp); } else { const ClientData ticlient = TiGetClient(tp); const tileJunk *tj = (tileJunk *)CD2PTR(ticlient); if (ticlient != CLIENTDEFAULT && tj->tj_status & RES_TILE_DEV) complex = TRUE; } } if (complex == TRUE) { /* Didn't find a terminal but device has multiple */ /* tiles, so make a secondary search looking at all */ /* tiles of the device. */ if (devStack == NULL) devStack = StackNew(8); ((tileJunk *)TiGetClientPTR(tile))->tj_status |= RES_TILE_PUSHED; STACKPUSH((ClientData)tile, devStack); while (!StackEmpty(devStack)) { tile = (Tile *)STACKPOP(devStack); /* left */ for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp = RT(tp)) { t2 = TiGetRightType(tp); if ((t2 != TT_SPACE) && TTMaskHasType(&(devptr->exts_deviceSDTypes[i]), t2)) { SourcePoint->p_x = LEFT(tile); SourcePoint->p_y = (MIN(TOP(tile),TOP(tp)) + MAX(BOTTOM(tile), BOTTOM(tp))) >> 1; while (!StackEmpty(devStack)) { STACKPOP(devStack); } return(tp); } else { const ClientData ticlient = TiGetClient(tp); if (ticlient != CLIENTDEFAULT) { tileJunk *tj = (tileJunk *)CD2PTR(ticlient); if (tj->tj_status & RES_TILE_DEV) { if (!(tj->tj_status & RES_TILE_PUSHED)) { tj->tj_status |= RES_TILE_PUSHED; STACKPUSH((ClientData)tp, devStack); } } } } } /* right */ for (tp = TR(tile); TOP(tp) > BOTTOM(tile); tp = LB(tp)) { t2 = TiGetLeftType(tp); if ((t2 != TT_SPACE) && TTMaskHasType(&(devptr->exts_deviceSDTypes[i]), t2)) { SourcePoint->p_x = RIGHT(tile); SourcePoint->p_y = (MIN(TOP(tile), TOP(tp))+ MAX(BOTTOM(tile), BOTTOM(tp))) >> 1; while (!StackEmpty(devStack)) { STACKPOP(devStack); } return(tp); } else { const ClientData ticlient = TiGetClient(tp); if (ticlient != CLIENTDEFAULT) { tileJunk *tj = (tileJunk *)CD2PTR(ticlient); if (tj->tj_status & RES_TILE_DEV) { if (!(tj->tj_status & RES_TILE_PUSHED)) { tj->tj_status |= RES_TILE_PUSHED; STACKPUSH((ClientData)tp, devStack); } } } } } /* top */ for (tp = RT(tile); RIGHT(tp) > LEFT(tile); tp = BL(tp)) { t2 = TiGetBottomType(tp); if ((t2 != TT_SPACE) && TTMaskHasType(&(devptr->exts_deviceSDTypes[i]), t2)) { SourcePoint->p_y = TOP(tile); SourcePoint->p_x = (MIN(RIGHT(tile),RIGHT(tp)) + MAX(LEFT(tile), LEFT(tp))) >> 1; while (!StackEmpty(devStack)) { STACKPOP(devStack); } return(tp); } else { const ClientData ticlient = TiGetClient(tp); if (ticlient != CLIENTDEFAULT) { tileJunk *tj = (tileJunk *)CD2PTR(ticlient); if (tj->tj_status & RES_TILE_DEV) { if (!(tj->tj_status & RES_TILE_PUSHED)) { tj->tj_status |= RES_TILE_PUSHED; STACKPUSH((ClientData)tp, devStack); } } } } } /* bottom */ for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp = TR(tp)) { t2 = TiGetTopType(tp); if ((t2 != TT_SPACE) && TTMaskHasType(&(devptr->exts_deviceSDTypes[i]), t2)) { SourcePoint->p_y = BOTTOM(tile); SourcePoint->p_x = (MIN(RIGHT(tile), RIGHT(tp)) + MAX(LEFT(tile), LEFT(tp))) >> 1; while (!StackEmpty(devStack)) { STACKPOP(devStack); } return(tp); } else { const ClientData ticlient = TiGetClient(tp); if (ticlient != CLIENTDEFAULT) { tileJunk *tj = (tileJunk *)CD2PTR(ticlient); if (tj->tj_status & RES_TILE_DEV) { if (!(tj->tj_status & RES_TILE_PUSHED)) { tj->tj_status |= RES_TILE_PUSHED; STACKPUSH((ClientData)tp, devStack); } } } } } } } } /* Didn't find a terminal (S/D) type tile in the perimeter search. */ /* Check if S/D types are in a different plane from the identifier. */ TiToRect(tile, &r); tp = NULL; for (i = 0; i < devptr->exts_deviceSDCount; i++) { for (pnum = 0; pnum < DBNumPlanes; pnum++) { DBSrPaintArea((Tile *)NULL, ResUse->cu_def->cd_planes[pnum], &r, &(devptr->exts_deviceSDTypes[i]), ResGetTileFunc, &tp); if (tp != NULL) return tp; } } /* Is it possible that the net is the substrate under the device? */ for (pnum = 0; pnum < DBNumPlanes; pnum++) { DBSrPaintArea((Tile *)NULL, ResUse->cu_def->cd_planes[pnum], &r, &(devptr->exts_deviceSubstrateTypes), ResGetTileFunc, &tp); if (tp != NULL) return tp; } } /* If any device type has TT_SPACE as a substrate type, then don't * issue an error; however, not handling the substrate as a * resistive network is not a good idea and "extresist" needs to use * the method employed by "extract" of drawing a substrate type out * to the boundary of each subcell. */ for (devptr = ExtCurStyle->exts_device[t1]; devptr; devptr = devptr->exts_next) if (TTMaskHasType(&(devptr->exts_deviceSubstrateTypes), TT_SPACE)) break; /* Didn't find a terminal (S/D or substrate) type tile anywhere. Flag an error. */ if (devptr == NULL) TxError("Couldn't find a terminal of the device at %d %d\n", goodies->rg_devloc->p_x, goodies->rg_devloc->p_y); return((Tile *) NULL); } /* *------------------------------------------------------------------------- * * ResGetDevice -- Once the net is extracted, we still have to equate * the sim file devices with the layout devices. ResGetDevice * looks for a device at the given location. "type" is also * specified to that the right plane will be searched. * * Results: returns device structure at location DevicePoint, if it * exists. * * Side Effects: none * *------------------------------------------------------------------------- */ resDevice * ResGetDevice(pt, type) Point *pt; TileType type; { Point workingPoint; Tile *tile; int pnum; workingPoint.p_x = (*pt).p_x; workingPoint.p_y = (*pt).p_y; pnum = DBPlane(type); /* Start at hint tile for device plane */ tile = PlaneGetHint(ResUse->cu_def->cd_planes[pnum]); GOTOPOINT(tile, &workingPoint); const ClientData ticlient = TiGetClient(tile); if (IsSplit(tile)) { if (TTMaskHasType(&ExtCurStyle->exts_deviceMask, TiGetLeftType(tile)) || TTMaskHasType(&ExtCurStyle->exts_deviceMask, TiGetRightType(tile))) return (((tileJunk *)CD2PTR(ticlient))->deviceList); } else if (TTMaskHasType(&ExtCurStyle->exts_deviceMask, TiGetType(tile))) { /* Failure to have a valid client data will result in a "Bad Device" * error and indicates a problem that needs debugging. */ if (ticlient != CLIENTDEFAULT) return (((tileJunk *)CD2PTR(ticlient))->deviceList); } return NULL; }