/* DBconnect.c - * * This file contains routines that extract electrically connected * regions of a layout for Magic. There are two extractors, one * that operates only within the paint of a single cell (DBSrConnect), * and one that operates hierarchically, across cell boundaries * (DBTreeCopyConnect). * * ********************************************************************* * * 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/database/DBconnect.c,v 1.6 2010/09/15 18:15:40 tim Exp $"; #endif /* not lint */ #include #include // for memcpy() #include "utils/magic.h" #include "utils/geometry.h" #include "tiles/tile.h" #include "utils/hash.h" #include "utils/stack.h" #include "database/database.h" #include "database/databaseInt.h" #include "select/select.h" #include "utils/signals.h" #include "utils/malloc.h" /* C99 compat */ #include "textio/textio.h" /* Global variable */ Stack *dbConnectStack = (Stack *)NULL; /* General note for DBSrConnect: * * The connectivity extractor works in two passes, in order to avoid * circularities. During the first pass, each connected tile gets * marked, using the ti_client field. This marking is needed to * avoid infinite searches on circular structures. The second pass * is used to clear the markings again. */ /* *----------------------------------------------------------------- * DBTransformDiagonal -- * * Resolve geometric transformations on diagonally-split tiles * Assumes that we have already determined that this tile is * split. * * Results: * A tile type containing embedded diagonal and side information. * Note that this tile type does NOT contain any actual type * information. * * Side Effects: * None. *----------------------------------------------------------------- */ TileType DBTransformDiagonal(oldtype, trans) TileType oldtype; Transform *trans; { TileType dinfo; int o1, o2, o3, dir, side; o1 = ((trans->t_e > 0) || (trans->t_d > 0)) ? 1 : 0; o2 = ((trans->t_a > 0) || (trans->t_b > 0)) ? 1 : 0; o3 = (trans->t_a != 0) ? 1 : 0; dir = (oldtype & TT_DIRECTION) ? 1 : 0; side = ((oldtype & TT_SIDE) ? 1 : 0) ^ o2 ^ (dir | o3); dir ^= o1 ^ o2; dinfo = TT_DIAGONAL; if (side) dinfo |= TT_SIDE; if (dir) dinfo |= TT_DIRECTION; return dinfo; } /* *----------------------------------------------------------------- * DBInvTransformDiagonal -- * * This is equivalent to the routine above, but inverted, so * that the result is correct for the orientation of the * triangle in the coordinate system of the child cell, * rather than the parent cell (which comes down to merely * swapping transform positions b and d, since translation * isn't considered). *----------------------------------------------------------------- */ TileType DBInvTransformDiagonal(oldtype, trans) TileType oldtype; Transform *trans; { TileType dinfo; int o1, o2, o3; int dir, side; o1 = ((trans->t_e > 0) || (trans->t_b > 0)) ? 1 : 0; o2 = ((trans->t_a > 0) || (trans->t_d > 0)) ? 1 : 0; o3 = (trans->t_a != 0) ? 1 : 0; dir = (oldtype & TT_DIRECTION) ? 1 : 0; side = ((oldtype & TT_SIDE) ? 1 : 0) ^ o2 ^ (dir | o3); dir ^= o1 ^ o2; dinfo = TT_DIAGONAL; if (side) dinfo |= TT_SIDE; if (dir) dinfo |= TT_DIRECTION; return dinfo; } /* * ---------------------------------------------------------------------------- * * DBSrConnectOnePlane -- * * Search from a starting tile to find all paint that is electrically * connected to that tile in the same plane. * * Results: * 0 is returned if the search finished normally. 1 is returned * if the search was aborted. * * Side effects: * For every paint tile that is electrically connected to the initial * tile, func is called. Func should have the following form: * * int * func(tile, clientData) * Tile *tile; * ClientData clientData; * { * } * * The clientData passed to func is the same one that was passed * to us. Func returns 0 under normal conditions; if it returns * 1 then the search is aborted. * * *** WARNING *** * * Func should not modify any paint during the search, since this * will mess up pointers kept by these procedures and likely cause * a core-dump. * * ---------------------------------------------------------------------------- */ int DBSrConnectOnePlane(startTile, connect, func, clientData) Tile *startTile; /* Starting tile for search */ TileTypeBitMask *connect; /* Pointer to a table indicating what tile * types connect to what other tile types. * Each entry gives a mask of types that * connect to tiles of a given type. */ int (*func)(); /* Function to apply at each connected tile. */ ClientData clientData; /* Client data for above function. */ { struct conSrArg csa; int result; result = 0; csa.csa_def = (CellDef *)NULL; csa.csa_bounds = TiPlaneRect; /* Pass 1. During this pass the client function gets called. */ csa.csa_clientFunc = func; csa.csa_clientData = clientData; csa.csa_clientDefault = startTile->ti_client; csa.csa_clear = FALSE; csa.csa_connect = connect; csa.csa_pNum = -1; if (dbSrConnectFunc(startTile, PTR2CD(&csa)) != 0) result = 1; /* Pass 2. Don't call any client function, just clear the marks. * Don't allow any interruptions. */ SigDisableInterrupts(); csa.csa_clientFunc = NULL; csa.csa_clear = TRUE; (void) dbSrConnectFunc(startTile, PTR2CD(&csa)); SigEnableInterrupts(); return result; } /* * ---------------------------------------------------------------------------- * * DBSrConnect -- * * Search through a cell to find all paint that is electrically * connected to things in a given starting area. * * Results: * 0 is returned if the search finished normally. 1 is returned * if the search was aborted. * * Side effects: * The search starts from one (random) non-space tile in "startArea" * that matches the types in the mask parameter. For every paint * tile that is electrically connected to the initial tile and that * intersects the rectangle "bounds", func is called. Func should * have the following form: * * int * func(tile, clientData) * Tile *tile; * ClientData clientData; * { * } * * The clientData passed to func is the same one that was passed * to us. Func returns 0 under normal conditions; if it returns * 1 then the search is aborted. * * *** WARNING *** * * Func should not modify any paint during the search, since this * will mess up pointers kept by these procedures and likely cause * a core-dump. * * ---------------------------------------------------------------------------- */ int DBSrConnect(def, startArea, mask, connect, bounds, func, clientData) CellDef *def; /* Cell definition in which to carry out * the connectivity search. Only paint * in this definition is considered. */ Rect *startArea; /* Area to search for an initial tile. Only * tiles OVERLAPPING the area are considered. * This area should have positive x and y * dimensions. */ TileTypeBitMask *mask; /* Only tiles of one of these types are used * as initial tiles. */ TileTypeBitMask *connect; /* Pointer to a table indicating what tile * types connect to what other tile types. * Each entry gives a mask of types that * connect to tiles of a given type. */ Rect *bounds; /* Area, in coords of scx->scx_use->cu_def, * that limits the search: only tiles * overalapping this area will be returned. * Use TiPlaneRect to search everywhere. */ int (*func)(); /* Function to apply at each connected tile. */ ClientData clientData; /* Client data for above function. */ { struct conSrArg csa; int startPlane, result; Tile *startTile; /* Starting tile for search. */ result = 0; csa.csa_def = def; csa.csa_bounds = *bounds; /* Find a starting tile (if there are many tiles underneath the * starting area, pick any one). The search function just saves * the tile address and returns. */ startTile = NULL; for (startPlane = PL_TECHDEPBASE; startPlane < DBNumPlanes; startPlane++) { csa.csa_pNum = startPlane; if (DBSrPaintArea((Tile *) NULL, def->cd_planes[startPlane], startArea, mask, dbSrConnectStartFunc, PTR2CD(&startTile)) != 0) break; } if (startTile == NULL) return 0; /* The following lets us call DBSrConnect recursively */ else if (startTile->ti_client == (ClientData)1) return 0; /* Pass 1. During this pass the client function gets called. */ csa.csa_clientFunc = func; csa.csa_clientData = clientData; csa.csa_clientDefault = CLIENTDEFAULT; csa.csa_clear = FALSE; csa.csa_connect = connect; if (dbSrConnectFunc(startTile, PTR2CD(&csa)) != 0) result = 1; /* Pass 2. Don't call any client function, just clear the marks. * Don't allow any interruptions. */ SigDisableInterrupts(); csa.csa_clientFunc = NULL; csa.csa_clear = TRUE; (void) dbSrConnectFunc(startTile, PTR2CD(&csa)); SigEnableInterrupts(); return result; } /** @typedef cb_database_srpaintarea_t */ int dbSrConnectStartFunc( Tile *tile, /* This will be the starting tile. */ ClientData cdata) /* We store tile's address here. */ /* (Tile **pTile) */ { Tile **pTile = (Tile **)CD2PTR(cdata); *pTile = tile; return 1; } /* Function similar to DBSrConnect but which does the first pass only */ /* and leaves the marked tiles intact. Tiles must be cleared by the */ /* caller. */ int DBSrConnectOnePass(def, startArea, mask, connect, bounds, func, clientData) CellDef *def; /* Cell definition in which to carry out * the connectivity search. Only paint * in this definition is considered. */ Rect *startArea; /* Area to search for an initial tile. Only * tiles OVERLAPPING the area are considered. * This area should have positive x and y * dimensions. */ TileTypeBitMask *mask; /* Only tiles of one of these types are used * as initial tiles. */ TileTypeBitMask *connect; /* Pointer to a table indicating what tile * types connect to what other tile types. * Each entry gives a mask of types that * connect to tiles of a given type. */ Rect *bounds; /* Area, in coords of scx->scx_use->cu_def, * that limits the search: only tiles * overalapping this area will be returned. * Use TiPlaneRect to search everywhere. */ int (*func)(); /* Function to apply at each connected tile. */ ClientData clientData; /* Client data for above function. */ { struct conSrArg csa; int startPlane, result; Tile *startTile; /* Starting tile for search. */ result = 0; csa.csa_def = def; csa.csa_bounds = *bounds; /* Find a starting tile (if there are many tiles underneath the * starting area, pick any one). The search function just saves * the tile address and returns. */ startTile = NULL; for (startPlane = PL_TECHDEPBASE; startPlane < DBNumPlanes; startPlane++) { csa.csa_pNum = startPlane; if (DBSrPaintArea((Tile *) NULL, def->cd_planes[startPlane], startArea, mask, dbSrConnectStartFunc, PTR2CD(&startTile)) != 0) break; } if (startTile == NULL) return 0; /* The following lets us call DBSrConnect recursively */ else if (startTile->ti_client == (ClientData)1) return 0; /* Pass 1. During this pass the client function gets called. */ csa.csa_clientFunc = func; csa.csa_clientData = clientData; csa.csa_clientDefault = CLIENTDEFAULT; csa.csa_clear = FALSE; csa.csa_connect = connect; if (dbSrConnectFunc(startTile, PTR2CD(&csa)) != 0) result = 1; return result; } /* * ---------------------------------------------------------------------------- * * dbcFindTileFunc -- * * Simple callback used by dbSrConnectFunc to return any tile found * matching the search type mask. * * Results: * Always 1. * * Side effects: * Fill client data with a pointer to the tile found. * * ---------------------------------------------------------------------------- */ int dbcFindTileFunc(tile, arg) Tile *tile; ClientData arg; { Tile **tptr = (Tile **)arg; *tptr = tile; return 1; } /* * ---------------------------------------------------------------------------- * * dbSrConnectFunc -- * * This search function gets called by DBSrPaintArea as part * of DBSrConnect, and also recursively by itself. Each invocation * is made to process a single tile that is of interest. * * Results: * 0 is returned unless the client function returns a non-zero * value, in which case 1 is returned. * * Side effects: * If this tile has been seen before, then just return * immediately. If this tile hasn't been seen before, it is * marked and the client procedure is called. A NULL client * procedure is not called, of course. In addition, we scan * the tiles perimeter for any connected tiles, and call * ourselves recursively on them. * * Design note: * This one procedure is used during both the marking and clearing * passes, so "seen before" is a function both of the ti_client * field in the tile and the csa_clear value. * * 9/21/2022: Changed from being a recursive routine to using a * stack method, as large power/ground networks were causing stack * smashing. * * ---------------------------------------------------------------------------- */ int dbSrConnectFunc( Tile *tile, /* Tile that is connected. */ ClientData cdata) /* Contains information about the search. */ /* (struct conSrArg *csa) */ { struct conSrArg *csa = (struct conSrArg *)CD2PTR(cdata); Tile *t2; Rect tileArea; int i, pNum; int result = 0; bool callClient; const TileTypeBitMask *connectMask; TileType loctype, checktype; PlaneMask planes; if (dbConnectStack == (Stack *)NULL) dbConnectStack = StackNew(256); /* Drop the first entry on the stack */ pNum = csa->csa_pNum; STACKPUSH(INT2CD(tile), dbConnectStack); STACKPUSH(INT2CD(pNum), dbConnectStack); while (!StackEmpty(dbConnectStack)) { pNum = (int)CD2INT(STACKPOP(dbConnectStack)); tile = (Tile *)CD2INT(STACKPOP(dbConnectStack)); if (result == 1) continue; TiToRect(tile, &tileArea); /* Make sure this tile overlaps the area we're interested in. */ if (!GEO_OVERLAP(&tileArea, &csa->csa_bounds)) continue; /* See if we've already been here before, and mark the tile as already * visited. */ callClient = TRUE; if (csa->csa_clear) { if (tile->ti_client == csa->csa_clientDefault) continue; tile->ti_client = csa->csa_clientDefault; } else { if (tile->ti_client == (ClientData) 1) continue; /* Allow a process to mark tiles for skipping the client function */ if (tile->ti_client != csa->csa_clientDefault) callClient = FALSE; tile->ti_client = (ClientData) 1; } /* Call the client function, if there is one. */ if (callClient && (csa->csa_clientFunc != NULL)) { if ((*csa->csa_clientFunc)(tile, pNum, csa->csa_clientData) != 0) { result = 1; continue; } } /* Now search around each of the four sides of this tile for * connected tiles. For each one found, call ourselves * recursively. */ if (IsSplit(tile)) { if (SplitSide(tile)) loctype = SplitRightType(tile); else loctype = SplitLeftType(tile); } else loctype = TiGetTypeExact(tile); connectMask = &csa->csa_connect[loctype]; /* Left side: */ if (IsSplit(tile) && SplitSide(tile)) goto bottomside; for (t2 = BL(tile); BOTTOM(t2) < tileArea.r_ytop; t2 = RT(t2)) { if (IsSplit(t2)) { checktype = SplitRightType(t2); } else checktype = TiGetTypeExact(t2); if (TTMaskHasType(connectMask, checktype)) { if (csa->csa_clear) { if (t2->ti_client == csa->csa_clientDefault) continue; } else if (t2->ti_client == (ClientData) 1) continue; if (IsSplit(t2)) TiSetBody(t2, INT2CD(CD2INT(t2->ti_body) | TT_SIDE)); /* bit set */ STACKPUSH(INT2CD(t2), dbConnectStack); STACKPUSH(INT2CD(pNum), dbConnectStack); } } /* Bottom side: */ bottomside: if (IsSplit(tile) && (!(SplitSide(tile) ^ SplitDirection(tile)))) goto rightside; for (t2 = LB(tile); LEFT(t2) < tileArea.r_xtop; t2 = TR(t2)) { if (IsSplit(t2)) { checktype = SplitTopType(t2); } else checktype = TiGetTypeExact(t2); if (TTMaskHasType(connectMask, checktype)) { if (csa->csa_clear) { if (t2->ti_client == csa->csa_clientDefault) continue; } else if (t2->ti_client == (ClientData) 1) continue; if (IsSplit(t2)) { if (SplitDirection(t2)) /* bit set */ TiSetBody(t2, INT2CD(CD2INT(t2->ti_body) | TT_SIDE)); else /* bit clear */ TiSetBody(t2, INT2CD(CD2INT(t2->ti_body) & ~TT_SIDE)); } STACKPUSH(INT2CD(t2), dbConnectStack); STACKPUSH(INT2CD(pNum), dbConnectStack); } } /* Right side: */ rightside: if (IsSplit(tile) && !SplitSide(tile)) goto topside; for (t2 = TR(tile); ; t2 = LB(t2)) { if (IsSplit(t2)) { checktype = SplitLeftType(t2); } else checktype = TiGetTypeExact(t2); if (TTMaskHasType(connectMask, checktype)) { if (csa->csa_clear) { if (t2->ti_client == csa->csa_clientDefault) goto nextRight; } else if (t2->ti_client == (ClientData) 1) goto nextRight; if (IsSplit(t2)) TiSetBody(t2, INT2CD(CD2INT(t2->ti_body) & ~TT_SIDE)); /* bit clear */ STACKPUSH(INT2CD(t2), dbConnectStack); STACKPUSH(INT2CD(pNum), dbConnectStack); } nextRight: if (BOTTOM(t2) <= tileArea.r_ybot) break; } /* Top side: */ topside: if (IsSplit(tile) && (SplitSide(tile) ^ SplitDirection(tile))) goto donesides; for (t2 = RT(tile); ; t2 = BL(t2)) { if (IsSplit(t2)) { checktype = SplitBottomType(t2); } else checktype = TiGetTypeExact(t2); if (TTMaskHasType(connectMask, checktype)) { if (csa->csa_clear) { if (t2->ti_client == csa->csa_clientDefault) goto nextTop; } else if (t2->ti_client == (ClientData) 1) goto nextTop; if (IsSplit(t2)) { if (SplitDirection(t2)) /* bit clear */ TiSetBody(t2, INT2CD(CD2INT(t2->ti_body) & ~TT_SIDE)); else /* bit set */ TiSetBody(t2, INT2CD(CD2INT(t2->ti_body) | TT_SIDE)); } STACKPUSH(INT2CD(t2), dbConnectStack); STACKPUSH(INT2CD(pNum), dbConnectStack); } nextTop: if (LEFT(t2) <= tileArea.r_xbot) break; } donesides: if (pNum < 0) continue; /* Used for single-plane search */ /* Lastly, check to see if this tile connects to anything on * other planes. If so, search those planes. */ planes = DBConnPlanes[loctype]; planes &= ~(PlaneNumToMaskBit(pNum)); if (planes != 0) { Rect newArea; GEO_EXPAND(&tileArea, 1, &newArea); for (i = PL_TECHDEPBASE; i < DBNumPlanes; i++) { if (!PlaneMaskHasPlane(planes, i)) continue; if (IsSplit(tile)) { if (DBSrPaintNMArea((Tile *) NULL, csa->csa_def->cd_planes[i], TiGetTypeExact(tile), &newArea, connectMask, dbcFindTileFunc, (ClientData)&t2) != 0) { STACKPUSH(INT2CD(t2), dbConnectStack); STACKPUSH(INT2CD(i), dbConnectStack); } } else if (DBSrPaintArea((Tile *) NULL, csa->csa_def->cd_planes[i], &newArea, connectMask, dbcFindTileFunc, (ClientData)&t2) != 0) { STACKPUSH(INT2CD(t2), dbConnectStack); STACKPUSH(INT2CD(i), dbConnectStack); } } } } return result; } /* * ---------------------------------------------------------------------------- * * dbcUnconnectFunc -- * * This search function is invoked by DBSrPaintArea from * DBTreeCopyConnect, whenever a tile is found in the result * plane that is NOT connected to the current area. It * returns 1 so that DBTreeCopyConnect will know it has * to do a hierarchical check for the current area. * * Results: * If the current tile OVERLAPS the search area, 1 is * returned. Otherwise 0 is returned. * * Side effects: * None. * * ---------------------------------------------------------------------------- */ /** @typedef cb_database_srpaintnmarea_t */ /** @typedef cb_database_srpaintarea_t */ int dbcUnconnectFunc(tile, clientData) Tile *tile; /* Current tile */ ClientData clientData; /* Unused. */ { return 1; } /* * ---------------------------------------------------------------------------- * * dbcConnectLabelFunc -- * * This function is invoked by DBTreeSrTiles from DBTreeCopyConnect; * when a label is found which is connected to paint belonging to the * network, it adds it to the destination definition. * * In addition to simply adding a label to the destination, this * routine also checks port connections on abstract cells. Abstract * cells can have multiple ports of the same name with implied * connectivity between them. * * Results: * Return 0 normally, 1 if list size exceeds integer bounds. * * Side effects: * Adds a label to the destination definition "def". * * ---------------------------------------------------------------------------- */ int dbcConnectLabelFunc(scx, lab, tpath, csa2) SearchContext *scx; Label *lab; TerminalPath *tpath; struct conSrArg2 *csa2; { CellDef *def = csa2->csa2_use->cu_def; Rect r; Point offset; int pos, rotate; char newlabtext[FLATTERMSIZE]; char *newlabptr; int dbcConnectFunc(); /* Forward declaration */ GeoTransRect(&scx->scx_trans, &lab->lab_rect, &r); pos = GeoTransPos(&scx->scx_trans, lab->lab_just); GeoTransPointDelta(&scx->scx_trans, &lab->lab_offset, &offset); rotate = GeoTransAngle(&scx->scx_trans, lab->lab_rotate); /* Do not add any labels to the destination unless they are on */ /* the top level (Note: Could alter label to be placed with */ /* tpath). */ if (scx->scx_use != csa2->csa2_topscx->scx_use) { if (tpath) { int newllen = tpath->tp_next - tpath->tp_first; newlabtext[0] = '\0'; if (tpath->tp_first == NULL) newlabptr = lab->lab_text; else { if (newllen > 0) strncpy(newlabtext, tpath->tp_first, newllen); sprintf(newlabtext + newllen, "%s", lab->lab_text); newlabptr = newlabtext; } } else return 0; } else newlabptr = lab->lab_text; /* Do not repeat a label copy; check that the label doesn't */ /* already exist in the destination def first. */ if (DBCheckLabelsByContent(def, &r, lab->lab_type, lab->lab_text)) return 0; if (DBCheckLabelsByContent(def, &r, lab->lab_type, newlabptr)) return 0; DBEraseLabelsByContent(def, &r, -1, lab->lab_text); DBPutFontLabel(def, &r, lab->lab_font, lab->lab_size, rotate, &offset, pos, newlabptr, lab->lab_type, lab->lab_flags, lab->lab_port); if (lab->lab_flags & PORT_DIR_MASK) { CellDef *orig_def = scx->scx_use->cu_def; Label *slab; int lidx = lab->lab_port; const TileTypeBitMask *connectMask; /* Check for equivalent ports. For any found, call */ /* DBTreeSrTiles recursively on the type and area */ /* of the label. */ /* Don't recurse, just add area to the csa2_list. */ /* Only add the next label found to the list. If there */ /* are more equivalent ports, they will be found when */ /* processing this label's area. */ for (slab = orig_def->cd_labels; slab != NULL; slab = slab->lab_next) if ((slab->lab_flags & PORT_DIR_MASK) && (slab != lab)) if (slab->lab_port == lidx) { Rect newarea; int i, pNum; // Do NOT go searching on labels connected to space! if (slab->lab_type == TT_SPACE) continue; GeoTransRect(&scx->scx_trans, &slab->lab_rect, &newarea); // Avoid infinite looping. If material under the label // has already been added to the destination, then ignore. connectMask = &csa2->csa2_connect[slab->lab_type]; pNum = DBPlane(slab->lab_type); // Do *not* run this check on zero area labels if (!GEO_RECTNULL(&newarea)) if (DBSrPaintArea((Tile *) NULL, def->cd_planes[pNum], &newarea, connectMask, dbcUnconnectFunc, (ClientData) NULL) == 1) continue; newarea.r_xbot--; newarea.r_xtop++; newarea.r_ybot--; newarea.r_ytop++; /* Check if any of the last 5 entries has the same type and */ /* area. If so, don't duplicate the existing entry. */ for (i = csa2->csa2_lasttop; (i >= 0) && (i > csa2->csa2_lasttop - 5); i--) if (connectMask == csa2->csa2_list[i].connectMask) if (GEO_SURROUND(&csa2->csa2_list[i].area, &newarea)) return 0; /* Register the area and connection mask as needing to be processed */ if (++csa2->csa2_top == CSA2_LIST_SIZE) { /* Reached list size limit---need to push the list and */ /* start a new one. */ conSrArea *newlist; newlist = (conSrArea *)mallocMagic(CSA2_LIST_SIZE * sizeof(conSrArea)); StackPush((ClientData)csa2->csa2_list, csa2->csa2_stack); csa2->csa2_list = newlist; csa2->csa2_top = 0; } csa2->csa2_list[csa2->csa2_top].area = newarea; csa2->csa2_list[csa2->csa2_top].connectMask = connectMask; csa2->csa2_list[csa2->csa2_top].dinfo = 0; /* See above: Process only one equivalent port at a time */ break; } } return 0; } /* * ---------------------------------------------------------------------------- * * dbcConnectFunc -- * * This procedure is invoked by DBTreeSrTiles from DBTreeCopyConnect, * whenever a tile is found that is connected to the current area * being processed. If the tile overlaps the search area in a non- * trivial way (i.e. more than a 1x1 square of overlap at a corner), * then its area is checked against the equivalent destination area. * If the destination area contains unconnected portions, then the * area of the tile is painted into the destination, and an area 1 * unit larger than the tile is recursively checked for connecting * tiles. The "non-trivial" overlap check is needed to prevent * catecorner tiles from being considered as connected. * * Results: * Returns 0 normally to keep the search from aborting; returns 1 * if allocation of list failed due to exceeding integer bounds. * * Side effects: * Adds paint to the destination definition. * * ---------------------------------------------------------------------------- */ int dbcConnectFunc(tile, cx) Tile *tile; /* Tile found. */ TreeContext *cx; /* Describes context of search. The client * data is a pointer to a conSrArg2 record * containing various required information. */ { struct conSrArg2 *csa2; Rect tileArea, newarea; const TileTypeBitMask *connectMask; TileTypeBitMask notConnectMask; Rect *srArea; SearchContext *scx = cx->tc_scx; SearchContext scx2; TileType loctype = TiGetTypeExact(tile); TileType dinfo = 0; int retval, i, pNum = cx->tc_plane; CellDef *def; TiToRect(tile, &tileArea); srArea = &scx->scx_area; if (((tileArea.r_xbot >= srArea->r_xtop-1) || (tileArea.r_xtop <= srArea->r_xbot+1)) && ((tileArea.r_ybot >= srArea->r_ytop-1) || (tileArea.r_ytop <= srArea->r_ybot+1))) { /* If the search area is only one unit wide or tall, then it's * OK to have only a small overlap. This happens only when * looking for an initial search tile. */ if (((srArea->r_xtop-1) != srArea->r_xbot) && ((srArea->r_ytop-1) != srArea->r_ybot)) return 0; } GeoTransRect(&scx->scx_trans, &tileArea, &newarea); /* Clip the current area down to something that overlaps the * area of interest. */ csa2 = (struct conSrArg2 *)cx->tc_filter->tf_arg; GeoClip(&newarea, csa2->csa2_bounds); if (GEO_RECTNULL(&newarea)) return 0; if (IsSplit(tile)) { dinfo = DBTransformDiagonal(loctype, &scx->scx_trans); loctype = (SplitSide(tile)) ? SplitRightType(tile) : SplitLeftType(tile); } /* See if the destination cell contains stuff over the whole * current area (on its home plane) that is connected to it. * If so, then there's no need to process the current area, * since any processing that is needed was already done before. */ connectMask = &csa2->csa2_connect[loctype]; /* In the case of contact bits, the types underneath * must be constituents of the contact before we punt */ if (DBIsContact(loctype)) { /* Different contact types may share residues (6/18/04) */ /* Use TTMaskIntersect(), not TTMaskEqual()---types */ /* which otherwise stack may be in separate cells */ /* (12/1/05) */ /* The mask of contact types must include all stacked contacts */ TTMaskZero(¬ConnectMask); TTMaskSetMask(¬ConnectMask, &DBNotConnectTbl[loctype]); } else { TTMaskCom2(¬ConnectMask, connectMask); } /* Only check those tiles in the destination (select) */ /* which have not already been painted. */ def = csa2->csa2_use->cu_def; retval = 1; if (DBSrPaintNMArea((Tile *) NULL, def->cd_planes[pNum], dinfo, &newarea, ¬ConnectMask, dbcUnconnectFunc, (ClientData) NULL) == 0) retval = 0; /* Paint this tile into the destination cell. This * marks its area has having been processed. Then recycle * the storage for the current list element. */ DBNMPaintPlane(def->cd_planes[pNum], dinfo, &newarea, DBStdPaintTbl(loctype, pNum), (PaintUndoInfo *) NULL); if (retval == 0) return 0; /* Since the whole area of this tile hasn't been recorded, * we must process its area to find any other tiles that * connect to it. Add each of them to the list of things * to process. We have to expand the search area by 1 unit * on all sides because DBTreeSrTiles only returns things * that overlap the search area, and we want things that * even just touch. */ /* Only extend those sides bordering the diagonal tile */ if (dinfo & TT_DIAGONAL) { if (dinfo & TT_SIDE) /* right */ newarea.r_xtop += 1; else /* left */ newarea.r_xbot -= 1; if (((dinfo & TT_SIDE) >> 1) == (dinfo & TT_DIRECTION)) /* top */ newarea.r_ytop += 1; else /* bottom */ newarea.r_ybot -= 1; } else { newarea.r_ybot -= 1; newarea.r_ytop += 1; newarea.r_xbot -= 1; newarea.r_xtop += 1; } /* Check if any of the last 5 entries has the same type and */ /* area. If so, don't duplicate the existing entry. */ /* (NOTE: Connect masks are all from the same table, so */ /* they can be compared by address, no need for TTMaskEqual)*/ for (i = csa2->csa2_lasttop; (i >= 0) && (i > csa2->csa2_lasttop - 5); i--) if (connectMask == csa2->csa2_list[i].connectMask) if (GEO_SURROUND(&csa2->csa2_list[i].area, &newarea)) return 0; /* Register the area and connection mask as needing to be processed */ if (++csa2->csa2_top == CSA2_LIST_SIZE) { /* Reached list size limit---need to push the list and */ /* start a new one. */ conSrArea *newlist; newlist = (conSrArea *)mallocMagic(CSA2_LIST_SIZE * sizeof(conSrArea)); StackPush((ClientData)csa2->csa2_list, csa2->csa2_stack); csa2->csa2_list = newlist; csa2->csa2_top = 0; } csa2->csa2_list[csa2->csa2_top].area = newarea; csa2->csa2_list[csa2->csa2_top].connectMask = connectMask; csa2->csa2_list[csa2->csa2_top].dinfo = dinfo; return 0; } /* * ---------------------------------------------------------------------------- * * DBTreeCopyConnect -- * * This procedure copies connected information from a given cell * hierarchy to a given (flat) cell. Starting from the tile underneath * the given area, this procedure finds all paint in all cells * that is connected to that information. All such paint is * copied into the result cell. If there are several electrically * distinct nets underneath the given area, one of them is picked * at more-or-less random. * * Modified so the result cell is NOT first cleared of all paint. This * allows multiple calls, to highlight incomplete routing nets. * * Results: * None. * * Side effects: * The contents of the result cell are modified. * * ---------------------------------------------------------------------------- */ void DBTreeCopyConnect(scx, mask, xMask, connect, area, doLabels, destUse) SearchContext *scx; /* Describes starting area. The * scx_use field gives the root of * the hierarchy to search, and the * scx_area field gives the starting * area. An initial tile must overlap * this area. The transform is from * coords of scx_use to destUse. */ TileTypeBitMask *mask; /* Tile types to start from in area. */ int xMask; /* Information must be expanded in all * of the windows indicated by this * mask. Use 0 to consider all info * regardless of expansion. */ TileTypeBitMask *connect; /* Points to table that defines what * each tile type is considered to * connect to. Use DBConnectTbl as * a default. */ Rect *area; /* The resulting information is * clipped to this area. Pass * TiPlaneRect to get everything. */ unsigned char doLabels; /* If SEL_DO_LABELS, copy connected labels * and paint. If SEL_NO_LABELS, copy only * connected paint. If SEL_SIMPLE_LABELS, * copy only root of labels in subcircuits. */ CellUse *destUse; /* Result use in which to place * anything connected to material of * type mask in area of rootUse. */ { struct conSrArg2 csa2; const TileTypeBitMask *newmask; TileType newtype; unsigned char searchtype; csa2.csa2_use = destUse; csa2.csa2_bounds = area; csa2.csa2_connect = connect; csa2.csa2_topscx = scx; /* Instead of using a linked list, we keep down the number of */ /* malloc calls by maintaining a small list and expanding it only */ /* when necessary. */ csa2.csa2_list = (conSrArea *)mallocMagic(CSA2_LIST_SIZE * sizeof(conSrArea)); csa2.csa2_top = -1; csa2.csa2_lasttop = -1; csa2.csa2_stack = StackNew(100); DBTreeSrTiles(scx, mask, xMask, dbcConnectFunc, (ClientData) &csa2); while (csa2.csa2_top >= 0) { char pathstring[FLATTERMSIZE]; TerminalPath tpath; tpath.tp_first = tpath.tp_next = pathstring; tpath.tp_last = pathstring + FLATTERMSIZE; pathstring[0] = '\0'; newmask = csa2.csa2_list[csa2.csa2_top].connectMask; scx->scx_area = csa2.csa2_list[csa2.csa2_top].area; newtype = csa2.csa2_list[csa2.csa2_top].dinfo; if (csa2.csa2_top == 0) { if (StackLook(csa2.csa2_stack) != (ClientData)NULL) { freeMagic(csa2.csa2_list); csa2.csa2_list = (conSrArea *)StackPop(csa2.csa2_stack); csa2.csa2_top = CSA2_LIST_SIZE - 1; } else csa2.csa2_top--; } else csa2.csa2_top--; csa2.csa2_lasttop = csa2.csa2_top; if (newtype & TT_DIAGONAL) DBTreeSrNMTiles(scx, newtype, newmask, xMask, dbcConnectFunc, (ClientData) &csa2); else DBTreeSrTiles(scx, newmask, xMask, dbcConnectFunc, (ClientData) &csa2); /* Check the source def for any labels belonging to this */ /* tile area and plane, and add them to the destination. */ /* (This code previously in dbcConnectFunc, but moved to avoid */ /* running the cell search from the top within another cell */ /* search, which creates deep stacks and can trigger stack */ /* overflow.) */ searchtype = TF_LABEL_ATTACH; if (newtype & TT_DIAGONAL) { /* If the tile is split, then labels attached to the */ /* opposite point of the triangle are NOT connected. */ if (newtype & TT_SIDE) { if (newtype & TT_DIRECTION) searchtype |= TF_LABEL_ATTACH_NOT_SW; else searchtype |= TF_LABEL_ATTACH_NOT_NW; } else { if (newtype & TT_DIRECTION) searchtype |= TF_LABEL_ATTACH_NOT_NE; else searchtype |= TF_LABEL_ATTACH_NOT_SE; } } if (doLabels == SEL_SIMPLE_LABELS) tpath.tp_first = NULL; if (doLabels != SEL_NO_LABELS) if (DBTreeSrLabels(scx, newmask, xMask, &tpath, searchtype, dbcConnectLabelFunc, (ClientData) &csa2) != 0) { TxError("Connection search hit memory limit and stopped.\n"); break; } } freeMagic((char *)csa2.csa2_list); StackFree(csa2.csa2_stack); /* Recompute the bounding box of the destination and record its area * for redisplay. */ DBReComputeBbox(destUse->cu_def); }