From d99d84c5337268107ee120c94fb1d2b55d42e0c4 Mon Sep 17 00:00:00 2001 From: Tim Edwards Date: Fri, 16 Oct 2020 09:37:24 -0400 Subject: [PATCH 01/19] Removed the scalefactor from the DRC-CIF rule parsing, so that rules are interpreted in the dimensions used in the cifoutput section rather than always in centimicrons (otherwise, rules at, say, 5nm cannot be represented in the DRC section). --- VERSION | 2 +- drc/DRCcif.c | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/VERSION b/VERSION index f3d26a30..721cea68 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.3.67 +8.3.68 diff --git a/drc/DRCcif.c b/drc/DRCcif.c index 942501e1..c280c4bd 100644 --- a/drc/DRCcif.c +++ b/drc/DRCcif.c @@ -157,6 +157,13 @@ drcCifWarning() * Side effects: * Updates the DRC technology variables. * + * Notes: "centidistance" is by default in centimicrons, but it is really in + * whatever units are declared in the cifoutput section; so if cifoutput + * declares nanometers, then units are in nanometers. This may be in + * different units from the non-CIF part of the DRC section. The escape + * sequence substitution in the "Why" text will make sure that the correct + * physical dimensions are presented to the end user. + * * ---------------------------------------------------------------------------- */ @@ -194,7 +201,7 @@ drcCifWidth(argc, argv) } scalefactor = drcCifStyle->cs_scaleFactor; - centidistance *= drcCifStyle->cs_expander; // BSI + // centidistance *= drcCifStyle->cs_expander; // BSI dpnext = drcCifRules[thislayer][DRC_CIF_SPACE]; dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie))); @@ -287,7 +294,7 @@ drcCifSpacing(argc, argv) } scalefactor = drcCifStyle->cs_scaleFactor; - centidistance *= drcCifStyle->cs_expander; // BSI + // centidistance *= drcCifStyle->cs_expander; // BSI dpnext = drcCifRules[layer[0]][DRC_CIF_SOLID]; dpnew = (DRCCookie *) mallocMagic((unsigned) sizeof (DRCCookie)); drcCifAssign(dpnew, centidistance, dpnext, &DBSpaceBits, @@ -1167,7 +1174,7 @@ drcCifMaxwidth(argc, argv) } scalefactor = drcCifStyle->cs_scaleFactor; - centidistance *= drcCifStyle->cs_expander; // BSI + // centidistance *= drcCifStyle->cs_expander; // BSI dpnext = drcCifRules[thislayer][DRC_CIF_SPACE]; dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie))); drcCifAssign(dpnew, centidistance, dpnext, &CIFSolidBits, &CIFSolidBits, From 65b7ca8dac2bca6d84281e7612e29c7385f16abe Mon Sep 17 00:00:00 2001 From: Tim Edwards Date: Fri, 16 Oct 2020 21:14:10 -0400 Subject: [PATCH 02/19] Corrected a few wayward commas in the "lef" command that prevented correct operation of "lef write". --- lef/lefCmd.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lef/lefCmd.c b/lef/lefCmd.c index e8984361..32b9e881 100644 --- a/lef/lefCmd.c +++ b/lef/lefCmd.c @@ -112,15 +112,15 @@ CmdLef(w, cmd) static char *cmdLefOption[] = { "read [filename] read a LEF file filename[.lef]\n" - " read [filename] -import read a LEF file; import cells from .mag files\n", - " read [filename] -annotate read a LEF file for cell annotation only.", + " read [filename] -import read a LEF file; import cells from .mag files\n" + " read [filename] -annotate read a LEF file for cell annotation only." "write [filename] [-tech] write LEF for current cell\n" - " write [filename] -hide hide all details other than ports\n", + " write [filename] -hide hide all details other than ports\n" " write [filename] -hide hide details in area set back distance ", "writeall write all cells including the top-level cell\n" " writeall -notop write all children of the top-level cell\n" - " writeall -all recurse on all subcells of the top-level cell\n", - " writeall -hide hide all details other than ports\n", + " writeall -all recurse on all subcells of the top-level cell\n" + " writeall -hide hide all details other than ports\n" " writeall -hide [dist] hide details in area set back distance dist", "help print this help information", NULL From 6b633be797d5c17ddbec78de8adeb3422286cefe Mon Sep 17 00:00:00 2001 From: Tim Edwards Date: Sat, 17 Oct 2020 11:26:01 -0400 Subject: [PATCH 03/19] Corrected the algorithm created yesterday for copying up DRC errors from child cells, which was incorrectly descending all the way down into the hierarchy; not only can this produce the incorrect result but it also wastes time searching cells that don't need to be searched. --- VERSION | 2 +- drc/DRCsubcell.c | 72 ++++++++++++++++++++++++++++++++++++------------ 2 files changed, 56 insertions(+), 18 deletions(-) diff --git a/VERSION b/VERSION index 721cea68..e57c01d5 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.3.68 +8.3.69 diff --git a/drc/DRCsubcell.c b/drc/DRCsubcell.c index 6fb049a9..0a2bd477 100644 --- a/drc/DRCsubcell.c +++ b/drc/DRCsubcell.c @@ -107,10 +107,22 @@ drcFindOtherCells(use, area) return 0; } -/* For each tile found in drcCopyErrorsFunc(), translate the */ -/* tile position into the coordinate system of the parent cell */ -/* (represented by the drcTemp plane in ClientData) and */ -/* copy (paint) into it. */ +/* + * ---------------------------------------------------------------------------- + * + * drcSubCopyErrors --- + * + * For each tile found in drcCopyErrorsFunc(), translate the tile position + * into the coordinate system of the parent cell (represented by the drcTemp + * plane in ClientData) and apply the function passed in the filter, which is + * whatever function handles DRC errors inside an error tile (which is + * different for "drc why" commands than for "drc check". + * + * Returns: + * 0 to keep the search going. + * + * ---------------------------------------------------------------------------- + */ int drcSubCopyErrors(tile, cxp) @@ -121,9 +133,6 @@ drcSubCopyErrors(tile, cxp) Rect destArea; struct drcClientData *arg = (struct drcClientData *)cxp->tc_filter->tf_arg; - // DBTreeSrTiles() checks its own tiles, which we want to ignore. - if (arg->dCD_celldef == cxp->tc_scx->scx_use->cu_def) return 0; - TiToRect(tile, &area); GeoClip(&area, &cxp->tc_scx->scx_area); GeoTransRect(&cxp->tc_scx->scx_trans, &area, &destArea); @@ -135,6 +144,40 @@ drcSubCopyErrors(tile, cxp) return 0; } +/* + * ---------------------------------------------------------------------------- + * + * drcSubCopyFunc --- + * + * This routine is applied for each subcell of a parent def. It calls + * DBNoTreeSrTiles() with the above routine drcSubCopyErrors(), which + * copies all TT_ERROR_P tiles from the child cell up into the parent. + * This is used only within areas found to be non-interacting with the + * parent, such that any error found in the child cell is guaranteed to + * be a real error, and not one that might be resolved by additional + * material found in the parent or a sibling cell. + * + * Returns: + * Whatever DBNoTreeSrTiles() returns. + * + * ---------------------------------------------------------------------------- + */ + +int +drcSubCopyFunc(scx, fp) + SearchContext *scx; + TreeFilter *fp; +{ + TileTypeBitMask drcMask; + + /* Create a mask with only TT_ERROR_P in it */ + TTMaskZero(&drcMask); + TTMaskSetType(&drcMask, TT_ERROR_P); + + /* Use DBNoTreeSrTiles() because we want to search only one level down */ + return DBNoTreeSrTiles(scx, &drcMask, 0, drcSubCopyErrors, fp->tf_arg); +} + /* * ---------------------------------------------------------------------------- * @@ -607,17 +650,12 @@ DRCInteractionCheck(def, area, erasebox, func, cdarg) void (*savedPaintPlane)(); struct drcClientData arg; SearchContext scx; - TileTypeBitMask drcMask; drcSubFunc = func; drcSubClientData = cdarg; oldTiles = DRCstatTiles; count = 0; - /* Create a mask with only TT_ERROR_P in it */ - TTMaskZero(&drcMask); - TTMaskSetType(&drcMask, TT_ERROR_P); - /* Divide the area to be checked up into squares. Process each * square separately. */ @@ -670,7 +708,7 @@ DRCInteractionCheck(def, area, erasebox, func, cdarg) /* Copy errors up from all non-interacting children */ scx.scx_area = subArea; - DBTreeSrTiles(&scx, &drcMask, 0, drcSubCopyErrors, &arg); + DBCellSrArea(&scx, drcSubCopyFunc, &arg); DRCErrorType = errorSaveType; continue; @@ -706,7 +744,7 @@ DRCInteractionCheck(def, area, erasebox, func, cdarg) DRCBasicCheck(def, &eraseHalo, &subArea, func, cdarg); /* Copy errors up from all non-interacting children */ scx.scx_area = subArea; - DBTreeSrTiles(&scx, &drcMask, 0, drcSubCopyErrors, &arg); + DBCellSrArea(&scx, drcSubCopyFunc, &arg); } /* check below */ if (intArea.r_ybot > eraseClip.r_ybot) @@ -717,7 +755,7 @@ DRCInteractionCheck(def, area, erasebox, func, cdarg) DRCBasicCheck(def, &eraseHalo, &subArea, func, cdarg); /* Copy errors up from all non-interacting children */ scx.scx_area = subArea; - DBTreeSrTiles(&scx, &drcMask, 0, drcSubCopyErrors, &arg); + DBCellSrArea(&scx, drcSubCopyFunc, &arg); } subArea.r_ytop = intArea.r_ytop; subArea.r_ybot = intArea.r_ybot; @@ -730,7 +768,7 @@ DRCInteractionCheck(def, area, erasebox, func, cdarg) DRCBasicCheck(def, &eraseHalo, &subArea, func, cdarg); /* Copy errors up from all non-interacting children */ scx.scx_area = subArea; - DBTreeSrTiles(&scx, &drcMask, 0, drcSubCopyErrors, &arg); + DBCellSrArea(&scx, drcSubCopyFunc, &arg); } /* check left */ if (intArea.r_xbot > eraseClip.r_xbot) @@ -741,7 +779,7 @@ DRCInteractionCheck(def, area, erasebox, func, cdarg) DRCBasicCheck(def, &eraseHalo, &subArea, func, cdarg); /* Copy errors up from all non-interacting children */ scx.scx_area = subArea; - DBTreeSrTiles(&scx, &drcMask, 0, drcSubCopyErrors, &arg); + DBCellSrArea(&scx, drcSubCopyFunc, &arg); } DRCErrorType = errorSaveType; } From c872db78b362e8fa0eeb037f8966f0296d261e52 Mon Sep 17 00:00:00 2001 From: fabian-l-c <66440964+fabian-l-c@users.noreply.github.com> Date: Mon, 27 Jul 2020 17:40:31 -0300 Subject: [PATCH 04/19] Added new cif operator bridge-lim The new CIF operator BRIDGE-LIM is similar to the BRIDGE operator with the difference that the material created to meet the minimum spacing/width rules do not overlap the limiting layers. Syntax of the new operator is: bridge-lim spacing width layers where "layers" are the limiting layers. --- cif/CIFgen.c | 852 ++++++++++++++++++++++++++++++++++++++++++++++++++ cif/CIFint.h | 3 + cif/CIFtech.c | 27 ++ 3 files changed, 882 insertions(+) diff --git a/cif/CIFgen.c b/cif/CIFgen.c index 1095024a..618598b2 100644 --- a/cif/CIFgen.c +++ b/cif/CIFgen.c @@ -3775,6 +3775,831 @@ cifSrTiles(cifOp, area, cellDef, temps, func, cdArg) &CIFSolidBits, func, (ClientData) cdArg); } +/* Data structure to pass plane and minimum width to the callback function */ +typedef struct _bridgeLimStruct { + Plane *plane; + BridgeData *bridge; + CellDef *def; + Plane **temps; + TileTypeBitMask co_paintMask;/* Zero or more paint layers to consider. */ + TileTypeBitMask co_cifMask; /* Zero or more other CIF layers. */ +} BridgeLimStruct; + +static int xOverlap, yOverlap; + +/* Bridge-lim Check data structure */ +typedef struct _bridgeLimCheckStruct { + Tile *tile; /* Tile that triggered search (ignore this tile) */ + int direction; /* What outside corner to look for */ + Tile *violator; /* Return the violator tile in this space */ + TileType checktype; /* Type to check for, either TT_SPACE or CIF_SOLIDTYPE */ + long sqdistance; /* Square of the minimum distance */ +} BridgeLimCheckStruct; +/* + *----------------------------------------------------------------------- + * Callback function for bridgeLimSrTiles used when a limiting layer tile + * was found in the specified area. If calcOverlap is TRUE then the xOverlap + * and yOverlap are updated and return 0 to continue searching. Otherwise + * return 1 to stop the search. + *----------------------------------------------------------------------- + */ +int +bridgeLimFound(tile, calcOverlap) + Tile *tile; + bool calcOverlap; +{ + if (calcOverlap) + { + if (RIGHT(tile) > xOverlap) + xOverlap = RIGHT(tile); + if (TOP(tile) > yOverlap) + yOverlap = TOP(tile); + return 0; // continue searching + } else + return 1; // tile found, stop the search +} + +/* + *------------------------------------------------------------------------ + * Callback function used in bridge-lim operations to find if the specified + * area contains tiles of the limiting layers. + *------------------------------------------------------------------------ + */ +int +bridgeLimSrTiles(brlims, area, calcOverlap) + BridgeLimStruct *brlims; /* Bridge-Lim structure. */ + Rect *area; /* Area of Magic paint to consider. */ + bool calcOverlap; /* TRUE to calculate the overlap of the limiting tiles in the specified area. */ +{ + TileTypeBitMask maskBits; + TileType t; + int i; + Plane **temps = brlims->temps; + xOverlap = area->r_xbot; + yOverlap = area->r_ybot; + + for (i = PL_DRC_CHECK; i < DBNumPlanes; i++) + { + maskBits = DBPlaneTypes[i]; + TTMaskAndMask(&maskBits, &brlims->co_paintMask); + if (!TTMaskEqual(&maskBits, &DBZeroTypeBits)) + if (DBSrPaintArea((Tile *) NULL, brlims->def->cd_planes[i], area, &brlims->co_paintMask, bridgeLimFound, calcOverlap)) + return 0; + } + + for (t = 0; t < TT_MAXTYPES; t++, temps++) + if (TTMaskHasType(&brlims->co_cifMask, t)) + if (DBSrPaintArea((Tile *) NULL, *temps, area, &CIFSolidBits, bridgeLimFound, calcOverlap)) + return 0; + + if (( xOverlap != area->r_xbot) || (yOverlap != area->r_ybot)) + return 0; //Constraint tile found + else + return 1; //Nothing found +} + +/* + * ---------------------------------------------------------------------------- + * + * cifBridgeLimFunc0 -- + * + * Called for each relevant tile during bridge-lim operations. The + * bridge-lim operation is similar to the bridge operation with the + * difference that the material created to meet the minimum spacing/width + * rules do not overlap the limiting layers. + * + * Results: + * Always returns 0 to keep the search alive. + * + * Side effects: + * May paint into cifNewPlane + * + * Algorithm (based on maximum horizontal stripes rule): + * This function grows dimentions of tiles to meet a minimimum width rule (only + * applies to vertical or horizontal directions). This is done in two steps: + * 1. Adjust tile width to accomplish the minimimum width rule, limited by + * the constraint layers. + * 2. Search top and bottom tiles of same type, for each segment verify if + * height meets the minimimum value, if not, adjust the segment height + * but do not paint over limiting layer tiles. + * ---------------------------------------------------------------------------- + */ +int +cifBridgeLimFunc0(tile, brlims) + Tile *tile; + BridgeLimStruct *brlims; +{ + Plane *plane = brlims->plane; + Rect area, parea; + int minDistance = brlims->bridge->br_width; + int width, height, ybot0, tp2lim; + Tile *tp, *tp2; + + TiToRect(tile, &area); + + /* Check whole tile for minimum width */ + width = area.r_xtop - area.r_xbot; + if (width < minDistance) + { + area.r_xbot = area.r_xtop - minDistance; + if (!bridgeLimSrTiles(brlims, &area, TRUE)) + { + area.r_xbot = MIN(LEFT(tile), xOverlap); + area.r_xtop = area.r_xbot + minDistance; + } + } + + /* If there is another tile on top or bottom, and the height is */ + /* less than minimum, then extend height in the direction of */ + /* the bordering tile. Otherwise, if the height is less than */ + /* minimum, then grow considering the limiting layers. */ + + height = area.r_ytop - area.r_ybot; + if (height < minDistance) + { + for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp = TR(tp)) + { + tp2lim = MAX(LEFT(tp), area.r_xbot); + for (tp2 = RT(tile); RIGHT(tp2) > tp2lim; tp2 = BL(tp2)) + if (LEFT(tp2) < RIGHT(tp)) + { + parea.r_xbot = MAX(LEFT(tp2), tp2lim); + parea.r_xtop = MIN(MIN(RIGHT(tp2), RIGHT(tp)), area.r_xtop); + if (TiGetBottomType(tp2) == TiGetTopType(tile)) + parea.r_ytop = TOP(tp2); + else + parea.r_ytop = area.r_ytop; + if (TiGetTopType(tp) == TiGetBottomType(tile)) + ybot0 = BOTTOM(tp); + else + ybot0 = area.r_ybot; + height = parea.r_ytop - ybot0; + if (height < minDistance) + { + parea.r_ybot = parea.r_ytop - minDistance; + if (!bridgeLimSrTiles(brlims, &parea, TRUE)) + { + parea.r_ybot = MIN(ybot0 , yOverlap); + parea.r_ytop = parea.r_ybot + minDistance; + } + DBPaintPlane(cifPlane, &parea, CIFPaintTable, (PaintUndoInfo *) NULL); + } + } + } + } + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + return 0; +} + +/* + *----------------------------------------------------------------------- + * Callback function for cifBridgeLimFunc1 and cifBridgeLimFunc2 to find if + * there are violator cells in the search area. If a violator cell is + * found, then put the tile pointer in the BridgeCheckStruct and return + * value 1 to stop the search. Otherwise return 0 to keep going. + *----------------------------------------------------------------------- + */ +int +bridgeLimCheckFunc(tile, brlimcs) + Tile *tile; + BridgeLimCheckStruct *brlimcs; +{ + int dir = brlimcs->direction; + Tile *self = brlimcs->tile; + Tile *tp1, *tp2; + TileType checktype = brlimcs->checktype; + long sqdistance = brlimcs->sqdistance; + int dx, dy; + long sqcheck; + + if (self == tile) return 0; /* Ignore the triggering tile */ + + switch (dir) { + case BRIDGE_NW: + /* Ignore tile if split, and SE corner is clipped */ + if (TiGetRightType(tile) == checktype || TiGetBottomType(tile) == checktype) + break; + for (tp1 = RT(tile); LEFT(tp1) > LEFT(tile); tp1 = BL(tp1)); + for (tp2 = BL(tile); TOP(tp2) < TOP(tile); tp2 = RT(tp2)); + if ((TiGetBottomType(tp1) == checktype) && + (TiGetRightType(tp2) == checktype)) + { + dx = LEFT(tile) - RIGHT(self); + dy = BOTTOM(self) - TOP(tile); + if ((dx > 0) && (dy > 0)) + { + sqcheck = (long)dx*dx + (long)dy*dy; + if (sqcheck >= sqdistance) + return 0; + } + brlimcs->violator = tile; + return 1; /* Violator found */ + } + break; + case BRIDGE_NE: + /* Ignore tile if split, and SW corner is clipped */ + if (TiGetLeftType(tile) == checktype || TiGetBottomType(tile) == checktype) + break; + tp1 = RT(tile); + tp2 = TR(tile); + if ((TiGetBottomType(tp1) == checktype) && + (TiGetLeftType(tp2) == checktype)) + { + dx = LEFT(self) - RIGHT(tile); + dy = BOTTOM(self) - TOP(tile); + if ((dx > 0) && (dy > 0)) + { + sqcheck = (long)dx*dx + (long)dy*dy; + if (sqcheck >= sqdistance) + return 0; + } + brlimcs->violator = tile; + return 1; /* Violator found */ + } + break; + case BRIDGE_SW: + /* Ignore tile if split, and NE corner is clipped */ + if (TiGetRightType(tile) == checktype || TiGetTopType(tile) == checktype) + break; + tp1 = LB(tile); + tp2 = BL(tile); + if ((TiGetTopType(tp1) == checktype) && + (TiGetRightType(tp2) == checktype)) + { + dx = LEFT(tile) - RIGHT(self); + dy = BOTTOM(tile) - TOP(self); + if ((dx > 0) && (dy > 0)) + { + sqcheck = (long)dx*dx + (long)dy*dy; + if (sqcheck >= sqdistance) + return 0; + } + brlimcs->violator = tile; + return 1; /* Violator found */ + } + break; + case BRIDGE_SE: + /* Ignore tile if split, and NW corner is clipped */ + if (TiGetLeftType(tile) == checktype || TiGetTopType(tile) == checktype) + break; + for (tp1 = LB(tile); RIGHT(tp1) < RIGHT(tile); tp1 = TR(tp1)); + for (tp2 = TR(tile); BOTTOM(tp2) > BOTTOM(tile); tp2 = LB(tp2)); + if ((TiGetTopType(tp1) == checktype) && + (TiGetLeftType(tp2) == checktype)) + { + dx = LEFT(self) - RIGHT(tile); + dy = BOTTOM(tile) - TOP(self); + if ((dx > 0) && (dy > 0)) + { + sqcheck = (long)dx*dx + (long)dy*dy; + if (sqcheck >= sqdistance) + return 0; + } + brlimcs->violator = tile; + return 1; /* Violator found */ + } + break; + } + return 0; /* Nothing found here, so keep going */ +} + +/* + *----------------------------------------------------------------------- + * Callback function used in bridge-lim operations to do not paint areas + * where new bridge overlaps the limiting layer tiles. + *----------------------------------------------------------------------- + */ +int +bridgeErase(brlims, area) + BridgeLimStruct *brlims; /* Bridge-lim structure. */ + Rect *area; /* Area of Magic paint to consider. */ +{ + TileTypeBitMask maskBits; + TileType t; + int i; + Plane **temps = brlims->temps; + + for (i = PL_DRC_CHECK; i < DBNumPlanes; i++) + { + maskBits = DBPlaneTypes[i]; + TTMaskAndMask(&maskBits, &brlims->co_paintMask); + if (!TTMaskEqual(&maskBits, &DBZeroTypeBits)) + if (DBSrPaintArea((Tile *) NULL, brlims->def->cd_planes[i], area, &brlims->co_paintMask, cifPaintFunc, CIFEraseTable)) + return 0; + } + + for (t = 0; t < TT_MAXTYPES; t++, temps++) + if (TTMaskHasType(&brlims->co_cifMask, t)) + if (DBSrPaintArea((Tile *) NULL, *temps, area, &CIFSolidBits, cifPaintFunc, CIFEraseTable)) + return 0; + + return 1; //Nothing found +} + +/* + * ---------------------------------------------------------------------------- + * + * cifBridgeLimFunc1 -- + * + * Called for each relevant tile during bridge-lim operations. The + * bridge-lim operation is similar to the bridge operation with the + * difference that the material created to meet the minimum spacing/width + * rules do not overlap the limiting layers. + * + * Results: + * Always returns 0 to keep the search alive. + * + * Side effects: + * May paint into cifNewPlane + * ---------------------------------------------------------------------------- + */ +int +cifBridgeLimFunc1(tile, brlims) + Tile *tile; + BridgeLimStruct *brlims; +{ + Plane *plane = brlims->plane; + Rect area; + Tile *tp1, *tp2, *tpx; + int width = brlims->bridge->br_width; + int spacing = growDistance; + BridgeLimCheckStruct brlimcs; + brlimcs.sqdistance = (long) spacing * spacing; + + /* If tile is marked, then it has been handled, so ignore it */ + if (tile->ti_client != (ClientData)CIF_UNPROCESSED) return 0; + + /* Find each tile outside corner (up to four) */ + + /* Check for NE outside corner */ + tp1 = TR(tile); /* Tile on right side at the top of this tile */ + tp2 = RT(tile); /* Tile on top side at the right of this tile */ + if ((TiGetLeftType(tp1) == TT_SPACE) && + (TiGetBottomType(tp2) == TT_SPACE)) + { + /* Set search box */ + area.r_xbot = RIGHT(tile); + area.r_xtop = RIGHT(tile) + spacing; + area.r_ybot = TOP(tile); + area.r_ytop = TOP(tile) + spacing; + + /* Find violator tiles */ + brlimcs.tile = tile; + brlimcs.direction = BRIDGE_SW; + brlimcs.checktype = TT_SPACE; + if (DBSrPaintArea((Tile *) NULL, plane, &area, + &CIFSolidBits, bridgeLimCheckFunc, (ClientData)&brlimcs) == 1) + { + tpx = brlimcs.violator; + area.r_xtop = MAX(RIGHT(tile), LEFT(tpx) + width); + area.r_ytop = MAX(TOP(tile), BOTTOM(tpx)); + area.r_xbot = MIN(LEFT(tpx), RIGHT(tile)); + area.r_ybot = MIN(BOTTOM(tpx), TOP(tile) - width); + if (bridgeLimSrTiles(brlims, &area, FALSE)) + { + /* First option of bridge can be implemented (there are not limiting tiles in the area)*/ + area.r_ytop = TOP(tile); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + area.r_xbot = LEFT(tpx); + area.r_ytop = MAX(TOP(tile), BOTTOM(tpx)); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + } + else + { + area.r_xtop = MAX(RIGHT(tile), LEFT(tpx)); + area.r_ytop = MAX(TOP(tile), BOTTOM(tpx) + width); + area.r_xbot = MIN(LEFT(tpx), RIGHT(tile) - width); + area.r_ybot = MIN(BOTTOM(tpx), TOP(tile)); + if (bridgeLimSrTiles(brlims, &area, FALSE)) + { + /* Second option of bridge can be implemented (there are not limiting tiles in the area)*/ + area.r_ybot = BOTTOM(tpx); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + area.r_xtop = RIGHT(tile); + area.r_ybot = MIN(BOTTOM(tpx), TOP(tile)); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + } + else + { + /* Last option (Limiting tiles are present on both sides, those areas must be excluded from the bridge)*/ + area.r_xtop = MAX(RIGHT(tile), LEFT(tpx) + width); + area.r_ytop = MAX(TOP(tile), BOTTOM(tpx) + width); + area.r_xbot = MIN(LEFT(tpx), RIGHT(tile) - width); + area.r_ybot = MIN(BOTTOM(tpx), TOP(tile) - width); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + bridgeErase(brlims, &area); + } + } + } + } + + /* Check for SW outside corner */ + tp1 = BL(tile); /* Tile on left side at the bottom of this tile */ + tp2 = LB(tile); /* Tile on bottom side at the left of this tile */ + if ((TiGetRightType(tp1) == TT_SPACE) && + (TiGetTopType(tp2) == TT_SPACE)) + { + /* Set search box */ + area.r_xbot = LEFT(tile) - spacing; + area.r_xtop = LEFT(tile); + area.r_ybot = BOTTOM(tile) - spacing; + area.r_ytop = BOTTOM(tile); + + /* Find violator tiles */ + brlimcs.tile = tile; + brlimcs.direction = BRIDGE_NE; + brlimcs.checktype = TT_SPACE; + if (DBSrPaintArea((Tile *) NULL, plane, &area, + &CIFSolidBits, bridgeLimCheckFunc, (ClientData)&brlimcs) == 1) + { + tpx = brlimcs.violator; + area.r_xtop = MAX(RIGHT(tpx), LEFT(tile) + width); + area.r_ytop = MAX(TOP(tpx), BOTTOM(tile)); + area.r_xbot = MIN(LEFT(tile), RIGHT(tpx)); + area.r_ybot = MIN(BOTTOM(tile), TOP(tpx) - width); + if (bridgeLimSrTiles(brlims, &area, FALSE)) + { + /* First option of bridge can be implemented (there are not limiting tiles in the area)*/ + area.r_ytop = TOP(tpx); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + area.r_xbot = LEFT(tile); + area.r_ytop = MAX(TOP(tpx), BOTTOM(tile)); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + } + else + { + area.r_xtop = MAX(RIGHT(tpx), LEFT(tile)); + area.r_ytop = MAX(TOP(tpx), BOTTOM(tile) + width); + area.r_xbot = MIN(LEFT(tile), RIGHT(tpx) - width); + area.r_ybot = MIN(BOTTOM(tile), TOP(tpx)); + if (bridgeLimSrTiles(brlims, &area, FALSE)) + { + /* Second option of bridge can be implemented (there are not limiting tiles in the area)*/ + area.r_ybot = BOTTOM(tile); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + area.r_xtop = RIGHT(tpx); + area.r_ybot = MIN(BOTTOM(tile), TOP(tpx)); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + } + else + { + /* Last option (Limiting tiles are present on both sides, those areas must be excluded from the bridge)*/ + area.r_xtop = MAX(RIGHT(tpx), LEFT(tile) + width); + area.r_ytop = MAX(TOP(tpx), BOTTOM(tile) + width); + area.r_xbot = MIN(LEFT(tile), RIGHT(tpx) - width); + area.r_ybot = MIN(BOTTOM(tile), TOP(tpx) - width); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + bridgeErase(brlims, &area); + } + } + } + } + + /* Check for SE outside corner */ + for (tp1 = TR(tile); BOTTOM(tp1) > BOTTOM(tile); tp1 = LB(tp1)); + for (tp2 = LB(tile); RIGHT(tp1) < RIGHT(tile); tp2 = TR(tp2)); + if ((TiGetLeftType(tp1) == TT_SPACE) && + (TiGetTopType(tp2) == TT_SPACE)) + { + /* Set search box */ + area.r_xbot = RIGHT(tile); + area.r_xtop = RIGHT(tile) + spacing; + area.r_ybot = BOTTOM(tile) - spacing; + area.r_ytop = BOTTOM(tile); + + /* Find violator tiles */ + brlimcs.tile = tile; + brlimcs.direction = BRIDGE_NW; + brlimcs.checktype = TT_SPACE; + if (DBSrPaintArea((Tile *) NULL, plane, &area, + &CIFSolidBits, bridgeLimCheckFunc, (ClientData)&brlimcs) == 1) + { + tpx = brlimcs.violator; + area.r_xtop = MAX(RIGHT(tile), LEFT(tpx)); + area.r_ybot = MIN(BOTTOM(tile), TOP(tpx) - width); + area.r_xbot = MIN(LEFT(tpx), RIGHT(tile) - width); + area.r_ytop = MAX(TOP(tpx), BOTTOM(tile)); + if (bridgeLimSrTiles(brlims, &area, FALSE)) + { + /* First option of bridge can be implemented (there are not limiting tiles in the area)*/ + area.r_xtop = RIGHT(tile); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + area.r_xtop = MAX(RIGHT(tile), LEFT(tpx)); + area.r_ytop = TOP(tpx); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + } + else + { + area.r_xtop = MAX(RIGHT(tile), LEFT(tpx) + width); + area.r_ybot = MIN(BOTTOM(tile), TOP(tpx)); + area.r_xbot = MIN(LEFT(tpx), RIGHT(tile)); + area.r_ytop = MAX(TOP(tpx), BOTTOM(tile) + width); + if (bridgeLimSrTiles(brlims, &area, FALSE)) + { + /* Second option of bridge can be implemented (there are not limiting tiles in the area)*/ + area.r_xbot = LEFT(tpx); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + area.r_xbot = MIN(LEFT(tpx), RIGHT(tile)); + area.r_ybot = BOTTOM(tile); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + } + else + { + /* Last option (Limiting tiles are present on both sides, those areas must be excluded from the bridge)*/ + area.r_xtop = MAX(RIGHT(tile), LEFT(tpx) + width); + area.r_ybot = MIN(BOTTOM(tile), TOP(tpx) - width); + area.r_xbot = MIN(LEFT(tpx), RIGHT(tile) - width); + area.r_ytop = MAX(TOP(tpx), BOTTOM(tile) + width); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + bridgeErase(brlims, &area); + } + } + } + } + + /* Check for NW outside corner */ + for (tp1 = BL(tile); TOP(tp1) < TOP(tile); tp1 = RT(tp1)); + for (tp2 = RT(tile); LEFT(tp1) > LEFT(tile); tp2 = BL(tp1)); + if ((TiGetRightType(tp1) == TT_SPACE) && + (TiGetBottomType(tp2) == TT_SPACE)) + { + /* Set search box */ + area.r_xbot = LEFT(tile) - spacing; + area.r_xtop = LEFT(tile); + area.r_ybot = TOP(tile); + area.r_ytop = TOP(tile) + spacing; + + /* Find violator tiles */ + brlimcs.tile = tile; + brlimcs.direction = BRIDGE_SE; + brlimcs.checktype = TT_SPACE; + if (DBSrPaintArea((Tile *) NULL, plane, &area, + &CIFSolidBits, bridgeLimCheckFunc, (ClientData)&brlimcs) == 1) + { + tpx = brlimcs.violator; + area.r_xtop = MAX(RIGHT(tpx), LEFT(tile)); + area.r_ybot = MIN(BOTTOM(tpx), TOP(tile) - width); + area.r_xbot = MIN(LEFT(tile), RIGHT(tpx) - width); + area.r_ytop = MAX(TOP(tile), BOTTOM(tpx)); + if (bridgeLimSrTiles(brlims, &area, FALSE)) + { + /* First option of bridge can be implemented (there are not limiting tiles in the area)*/ + area.r_xtop = RIGHT(tpx); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + area.r_xtop = MAX(RIGHT(tpx), LEFT(tile)); + area.r_ytop = TOP(tile); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + } + else + { + area.r_xtop = MAX(RIGHT(tpx), LEFT(tile) + width); + area.r_ybot = MIN(BOTTOM(tpx), TOP(tile)); + area.r_xbot = MIN(LEFT(tile), RIGHT(tpx)); + area.r_ytop = MAX(TOP(tile), BOTTOM(tpx) + width); + if (bridgeLimSrTiles(brlims, &area, FALSE)) + { + /* Second option of bridge can be implemented (there are not limiting tiles in the area)*/ + area.r_xbot = LEFT(tile); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + area.r_xbot = MIN(LEFT(tile), RIGHT(tpx)); + area.r_ybot = BOTTOM(tpx); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + } + else + { + /* Last option (Limiting tiles are present on both sides, those areas must be excluded from the bridge)*/ + area.r_xtop = MAX(RIGHT(tpx), LEFT(tile) + width); + area.r_ybot = MIN(BOTTOM(tpx), TOP(tile) - width); + area.r_xbot = MIN(LEFT(tile), RIGHT(tpx) - width); + area.r_ytop = MAX(TOP(tile), BOTTOM(tpx) + width); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + bridgeErase(brlims, &area); + } + } + } + } + + return 0; +} + +/* + * ---------------------------------------------------------------------------- + * cifBridgeLimFunc2 -- + * + * Called for each relevant tile during bridge-lim operations. The + * bridge-lim operation is similar to the bridge operation with the + * difference that the material created to meet the minimum spacing/width + * rules do not overlap the limiting layers. + * + * Results: + * Always returns 0 to keep the search alive. + * + * Side effects: + * May paint into cifNewPlane + * ---------------------------------------------------------------------------- + */ +int +cifBridgeLimFunc2(tile, brlims) + Tile *tile; + BridgeLimStruct *brlims; +{ + Plane *plane = brlims->plane; + Rect area; + Tile *tp1, *tp2, *tpx; + int width = brlims->bridge->br_width; + int thirdOption; + int spacing = growDistance; + BridgeLimCheckStruct brlimcs; + brlimcs.sqdistance = (long) width * width; + + /* If tile is marked, then it has been handled, so ignore it */ + if (tile->ti_client != (ClientData)CIF_UNPROCESSED) return 0; + + /* Find each tile outside corner (up to four) */ + + /* Check for NE outside corner */ + tp1 = TR(tile); /* Tile on right side at the top of this tile */ + tp2 = RT(tile); /* Tile on top side at the right of this tile */ + if ((TiGetLeftType(tp1) == CIF_SOLIDTYPE) && + (TiGetBottomType(tp2) == CIF_SOLIDTYPE)) + { + /* Set search box */ + area.r_xbot = RIGHT(tile); + area.r_xtop = RIGHT(tile) + width; + area.r_ybot = TOP(tile); + area.r_ytop = TOP(tile) + width; + + /* Find violator tiles */ + brlimcs.tile = tile; + brlimcs.direction = BRIDGE_SW; + brlimcs.checktype = CIF_SOLIDTYPE; + if (DBSrPaintArea((Tile *) NULL, plane, &area, + &DBSpaceBits, bridgeLimCheckFunc, (ClientData)&brlimcs) == 1) + { + tpx = brlimcs.violator; + area.r_xtop = RIGHT(tile); + area.r_ytop = TOP(tile); + area.r_xbot = LEFT(tpx) - width; + area.r_ybot = BOTTOM(tpx) - width; + + thirdOption = 0; + if (!bridgeLimSrTiles(brlims, &area, FALSE)) + { + area.r_xtop = RIGHT(tile) + width; + area.r_ytop = TOP(tile) + width; + area.r_xbot = LEFT(tpx); + area.r_ybot = BOTTOM(tpx); + if (!bridgeLimSrTiles(brlims, &area, FALSE)) + { + area.r_xbot = LEFT(tpx) - width; + area.r_ybot = BOTTOM(tpx) - width; + thirdOption = 1; + } + } + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + if (thirdOption) + bridgeErase(brlims, &area); + } + } + + /* Check for SW outside corner */ + tp1 = BL(tile); /* Tile on left side at the bottom of this tile */ + tp2 = LB(tile); /* Tile on bottom side at the left of this tile */ + if ((TiGetRightType(tp1) == CIF_SOLIDTYPE) && + (TiGetTopType(tp2) == CIF_SOLIDTYPE)) + { + /* Set search box */ + area.r_xbot = LEFT(tile) - width; + area.r_xtop = LEFT(tile); + area.r_ybot = BOTTOM(tile) - width; + area.r_ytop = BOTTOM(tile); + + /* Find violator tiles */ + brlimcs.tile = tile; + brlimcs.direction = BRIDGE_NE; + brlimcs.checktype = CIF_SOLIDTYPE; + if (DBSrPaintArea((Tile *) NULL, plane, &area, + &DBSpaceBits, bridgeLimCheckFunc, (ClientData)&brlimcs) == 1) + { + tpx = brlimcs.violator; + area.r_xtop = RIGHT(tpx); + area.r_ytop = TOP(tpx); + area.r_xbot = LEFT(tile) - width; + area.r_ybot = BOTTOM(tile) - width; + + thirdOption = 0; + if (!bridgeLimSrTiles(brlims, &area, FALSE)) + { + area.r_xtop = RIGHT(tpx) + width; + area.r_ytop = TOP(tpx) + width; + area.r_xbot = LEFT(tile); + area.r_ybot = BOTTOM(tile); + if (!bridgeLimSrTiles(brlims, &area, FALSE)) + { + area.r_xbot = LEFT(tile) - width; + area.r_ybot = BOTTOM(tile) - width; + thirdOption = 1; + } + } + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + if (thirdOption) + bridgeErase(brlims, &area); + } + } + + /* Check for SE outside corner */ + for (tp1 = TR(tile); BOTTOM(tp1) > BOTTOM(tile); tp1 = LB(tp1)); + for (tp2 = LB(tile); RIGHT(tp2) < RIGHT(tile); tp2 = TR(tp2)); + if ((TiGetLeftType(tp1) == CIF_SOLIDTYPE) && + (TiGetTopType(tp2) == CIF_SOLIDTYPE)) + { + /* Set search box */ + area.r_xbot = RIGHT(tile); + area.r_xtop = RIGHT(tile) + width; + area.r_ybot = BOTTOM(tile) - width; + area.r_ytop = BOTTOM(tile); + + /* Find violator tiles */ + brlimcs.tile = tile; + brlimcs.direction = BRIDGE_NW; + brlimcs.checktype = CIF_SOLIDTYPE; + if (DBSrPaintArea((Tile *) NULL, plane, &area, + &DBSpaceBits, bridgeLimCheckFunc, (ClientData)&brlimcs) == 1) + { + tpx = brlimcs.violator; + area.r_xtop = RIGHT(tile) + width; + area.r_ytop = TOP(tpx); + area.r_xbot = LEFT(tpx); + area.r_ybot = BOTTOM(tile) - width; + + thirdOption = 0; + if (!bridgeLimSrTiles(brlims, &area, FALSE)) + { + area.r_xtop = RIGHT(tile); + area.r_ytop = TOP(tpx) + width; + area.r_xbot = LEFT(tpx) - width; + area.r_ybot = BOTTOM(tile); + if (!bridgeLimSrTiles(brlims, &area, FALSE)) + { + area.r_xtop = RIGHT(tile) + width; + area.r_ybot = BOTTOM(tile) - width; + thirdOption = 1; + } + } + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + if (thirdOption) + bridgeErase(brlims, &area); + } + } + + /* Check for NW outside corner */ + for (tp1 = BL(tile); TOP(tp1) < TOP(tile); tp1 = RT(tp1)); + for (tp2 = RT(tile); LEFT(tp2) > LEFT(tile); tp2 = BL(tp2)); + if ((TiGetRightType(tp1) == CIF_SOLIDTYPE) && + (TiGetBottomType(tp2) == CIF_SOLIDTYPE)) + { + /* Set search box */ + area.r_xbot = LEFT(tile) - width; + area.r_xtop = LEFT(tile); + area.r_ybot = TOP(tile); + area.r_ytop = TOP(tile) + width; + + /* Find violator tiles */ + brlimcs.tile = tile; + brlimcs.direction = BRIDGE_SE; + brlimcs.checktype = CIF_SOLIDTYPE; + if (DBSrPaintArea((Tile *) NULL, plane, &area, + &DBSpaceBits, bridgeLimCheckFunc, (ClientData)&brlimcs) == 1) + { + tpx = brlimcs.violator; + area.r_xtop = RIGHT(tpx) + width; + area.r_ytop = TOP(tile); + area.r_xbot = LEFT(tile); + area.r_ybot = BOTTOM(tpx) - width; + + thirdOption = 0; + if (!bridgeLimSrTiles(brlims, &area, FALSE)) + { + area.r_xtop = RIGHT(tpx); + area.r_ytop = TOP(tile) + width; + area.r_xbot = LEFT(tile) - width; + area.r_ybot = BOTTOM(tpx); + if (!bridgeLimSrTiles(brlims, &area, FALSE)) + { + area.r_xtop = RIGHT(tpx) + width; + area.r_ybot = BOTTOM(tpx) - width; + thirdOption = 1; + } + } + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + if (thirdOption) + bridgeErase(brlims, &area); + } + } + + return 0; +} + /* * ---------------------------------------------------------------------------- * @@ -3828,6 +4653,7 @@ CIFGenLayer(op, area, cellDef, origDef, temps, clientdata) char *netname; BloatStruct bls; BridgeStruct brs; + BridgeLimStruct brlims; BridgeData *bridge; int (*cifGrowFuncPtr)() = (CIFCurStyle->cs_flags & CWF_GROW_EUCLIDEAN) ? cifGrowEuclideanFunc : cifGrowFunc; @@ -4052,6 +4878,32 @@ CIFGenLayer(op, area, cellDef, origDef, temps, clientdata) nextPlane = temp; break; + case CIFOP_BRIDGELIM: + growDistance = op->co_distance; + DBClearPaintPlane(nextPlane); + cifPlane = nextPlane; + cifScale = 1; + + brlims.bridge = (BridgeData *)op->co_client; + brlims.def = cellDef; + brlims.temps = temps; + brlims.co_paintMask = op->co_paintMask; + brlims.co_cifMask = op->co_cifMask; + + (void) DBSrPaintArea((Tile *) NULL, curPlane, &TiPlaneRect, + &CIFSolidBits, cifBridgeLimFunc0, (ClientData)&brlims); + temp = curPlane; + curPlane = nextPlane; + brlims.plane = curPlane; + + (void) DBSrPaintArea((Tile *) NULL, curPlane, &TiPlaneRect, + &CIFSolidBits, cifBridgeLimFunc1, (ClientData)&brlims); + (void) DBSrPaintArea((Tile *) NULL, curPlane, &TiPlaneRect, + &DBSpaceBits, cifBridgeLimFunc2, (ClientData)&brlims); + curPlane = nextPlane; + nextPlane = temp; + break; + case CIFOP_BLOAT: cifPlane = curPlane; cifSrTiles(op, area, cellDef, temps, diff --git a/cif/CIFint.h b/cif/CIFint.h index a95cc0f3..fcde35e3 100644 --- a/cif/CIFint.h +++ b/cif/CIFint.h @@ -141,6 +141,7 @@ typedef struct cifop * current cell. This replaces the "fault" method. * CIFOP_CLOSE - Added 11/25/19---close up areas smaller than indicated * CIFOP_BRIDGE - Added 6/11/20---Bridge across catecorner gaps + * CIFOP_BRIDGELIM - Added 27/07/20---Bridge across catecorner gaps, but with limiting layers */ #define CIFOP_AND 1 @@ -164,6 +165,8 @@ typedef struct cifop #define CIFOP_COPYUP 19 #define CIFOP_CLOSE 20 #define CIFOP_BRIDGE 21 +#define CIFOP_BRIDGELIM 22 + /* Added by Tim 10/21/2004 */ /* The following structure is used to pass information on how to draw diff --git a/cif/CIFtech.c b/cif/CIFtech.c index 0c905592..07343f05 100644 --- a/cif/CIFtech.c +++ b/cif/CIFtech.c @@ -1058,6 +1058,8 @@ CIFTechLine(sectionName, argc, argv) newOp->co_opcode = CIFOP_CLOSE; else if (strcmp(argv[0], "bridge") == 0) newOp->co_opcode = CIFOP_BRIDGE; + else if (strcmp(argv[0], "bridge-lim") == 0) + newOp->co_opcode = CIFOP_BRIDGELIM; else { TechError("Unknown statement \"%s\".\n", argv[0]); @@ -1107,6 +1109,26 @@ CIFTechLine(sectionName, argc, argv) newOp->co_client = (ClientData)bridge; break; + case CIFOP_BRIDGELIM: + if (argc != 4) goto wrongNumArgs; + newOp->co_distance = atoi(argv[1]); + if (newOp->co_distance <= 0) + { + TechError("Bridge distance must be greater than zero.\n"); + goto errorReturn; + } + bridge = (BridgeData *)mallocMagic(sizeof(BridgeData)); + bridge->br_width = atoi(argv[2]); + if (bridge->br_width <= 0) + { + TechError("Bridge width must be greater than zero.\n"); + freeMagic(bridge); + goto errorReturn; + } + cifParseLayers(argv[3], CIFCurStyle, &newOp->co_paintMask, &newOp->co_cifMask,FALSE); + newOp->co_client = (ClientData)bridge; + break; + case CIFOP_BLOATALL: if (argc != 3) goto wrongNumArgs; cifParseLayers(argv[1], CIFCurStyle, &newOp->co_paintMask, @@ -1572,6 +1594,7 @@ cifComputeRadii(layer, des) break; case CIFOP_BRIDGE: break; + case CIFOP_BRIDGELIM: break; case CIFOP_SQUARES: break; case CIFOP_SQUARES_G: break; } @@ -1831,6 +1854,7 @@ CIFTechFinal() case CIFOP_MAXRECT: case CIFOP_NET: break; + case CIFOP_BRIDGELIM: case CIFOP_BRIDGE: bridge = (BridgeData *)op->co_client; c = FindGCF(style->cs_scaleFactor, @@ -1928,6 +1952,7 @@ CIFTechFinal() case CIFOP_ANDNOT: case CIFOP_SHRINK: case CIFOP_CLOSE: + case CIFOP_BRIDGELIM: case CIFOP_BRIDGE: needThisLayer = TRUE; break; @@ -2359,6 +2384,7 @@ CIFTechOutputScale(n, d) case CIFOP_MAXRECT: case CIFOP_NET: break; + case CIFOP_BRIDGELIM: case CIFOP_BRIDGE: bridge = (BridgeData *)op->co_client; bridge->br_width *= d; @@ -2463,6 +2489,7 @@ CIFTechOutputScale(n, d) if (bloats->bl_distance[j] != 0) bloats->bl_distance[j] /= lexpand; break; + case CIFOP_BRIDGELIM: case CIFOP_BRIDGE: bridge = (BridgeData *)op->co_client; bridge->br_width /= lexpand; From a015b7f8ebe38b671d47440e8b2371aa2e8d4cd9 Mon Sep 17 00:00:00 2001 From: Tim Edwards Date: Sat, 17 Oct 2020 12:43:22 -0400 Subject: [PATCH 05/19] Corrected a problem in which extraction of MOS caps (or any device with two or more terminals other than the device identifier type tied together) would fail if there were not a device record specifically matching a one-S/D-terminal device. This is inconsistent with past behavior, and so has been fixed. --- extract/ExtBasic.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/extract/ExtBasic.c b/extract/ExtBasic.c index 17b41ae0..b273c5ab 100644 --- a/extract/ExtBasic.c +++ b/extract/ExtBasic.c @@ -1726,9 +1726,15 @@ extOutputDevices(def, transList, outFile) tmask = &devptr->exts_deviceSDTypes[termcount]; if (TTMaskIsZero(tmask)) { if (termcount < nsd) { + ExtDevice *devcheck; /* See if there is another matching device record with */ /* a different number of terminals, and try again. */ - devptr = extDevFindMatch(devptr, t); + devcheck = extDevFindMatch(devptr, t); + if (devcheck != NULL) devptr = devcheck; + + /* Not finding another device record just means that */ + /* terminals are tied together on the same net, such as */ + /* with a MOS cap. Accept this fact and move on. */ } break; /* End of SD terminals */ } From a67b8cc3956a0a2782827532948ae90ad2ac1aa4 Mon Sep 17 00:00:00 2001 From: Tim Edwards Date: Sun, 18 Oct 2020 14:03:15 -0400 Subject: [PATCH 06/19] Corrected an error in the "bloat-all" operator of the CIF generation, which was failing to clear tiles in the layout of the "processed" state, leading to unpredicatable results if the same layout layer is used in a subsequent CIF operation. --- VERSION | 2 +- cif/CIFgen.c | 74 +++++++++++++++++++++++++++++----------------------- 2 files changed, 43 insertions(+), 33 deletions(-) diff --git a/VERSION b/VERSION index e57c01d5..6aac87ef 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.3.69 +8.3.70 diff --git a/cif/CIFgen.c b/cif/CIFgen.c index 618598b2..cde2142d 100644 --- a/cif/CIFgen.c +++ b/cif/CIFgen.c @@ -1024,6 +1024,27 @@ endbloat: STACKPUSH((ClientData) (tp), stack); \ } +/* + *------------------------------------------------------- + * + * cifProcessResetFunc -- + * + * Unmark tiles + * + * Results: Return 0 to keep the search going. + * + *------------------------------------------------------- + */ + +int +cifProcessResetFunc(tile, clientData) + Tile *tile; + ClientData clientData; /* unused */ +{ + tile->ti_client = (ClientData) CIF_UNPROCESSED; + return 0; +} + /* *------------------------------------------------------- * @@ -1260,6 +1281,21 @@ cifBloatAllFunc(tile, bls) STACKPUSH(tp, BloatStack); } } + + /* Reset marked tiles */ + + if (bloats->bl_plane < 0) /* Bloat types are CIF types */ + { + temps = bls->temps; + for (ttype = 0; ttype < TT_MAXTYPES; ttype++, temps++) + if (bloats->bl_distance[ttype] > 0) + (void) DBSrPaintArea((Tile *)NULL, *temps, &TiPlaneRect, + &CIFSolidBits, cifProcessResetFunc, (ClientData)NULL); + } + else + DBSrPaintArea((Tile *)NULL, def->cd_planes[bloats->bl_plane], &TiPlaneRect, + &connect, cifProcessResetFunc, (ClientData)NULL); + return 0; /* Keep the search alive. . . */ } @@ -2188,28 +2224,6 @@ cifSquaresStripFunc(tile, stripsData) return 0; } - -/* - *------------------------------------------------------- - * - * cifSquaresResetFunc -- - * - * Unmark tiles - * - * Results: Return 0 to keep the search going. - * - *------------------------------------------------------- - */ - -int -cifSquaresResetFunc(tile, clientData) - Tile *tile; - ClientData clientData; /* unused */ -{ - tile->ti_client = (ClientData) CIF_UNPROCESSED; - return 0; -} - /* *------------------------------------------------------- * @@ -2387,10 +2401,6 @@ cifRectBoundingBox(op, cellDef, plane) } } } - - /* Clear all the tiles that were processed */ - DBSrPaintArea((Tile *)NULL, plane, &TiPlaneRect, &CIFSolidBits, - cifSquaresResetFunc, (ClientData)NULL); } /* @@ -2725,7 +2735,7 @@ cifSquaresFillArea(op, cellDef, plane) /* Clear all the tiles that were processed */ DBSrPaintArea((Tile *)NULL, plane, &TiPlaneRect, &CIFSolidBits, - cifSquaresResetFunc, (ClientData)NULL); + cifProcessResetFunc, (ClientData)NULL); } /* @@ -3088,7 +3098,7 @@ cifSlotsFillArea(op, cellDef, plane) /* Clear all the tiles that were processed */ DBSrPaintArea((Tile *)NULL, plane, &TiPlaneRect, &CIFSolidBits, - cifSquaresResetFunc, (ClientData)NULL); + cifProcessResetFunc, (ClientData)NULL); } /* @@ -4848,11 +4858,11 @@ CIFGenLayer(op, area, cellDef, origDef, temps, clientdata) cifPlane = nextPlane; cifScale = 1; /* First copy the existing paint into the target plane */ - (void) DBSrPaintArea((Tile *) NULL, curPlane, &TiPlaneRect, - &CIFSolidBits, cifPaintFunc, (ClientData)CIFPaintTable); + DBSrPaintArea((Tile *) NULL, curPlane, &TiPlaneRect, + &CIFSolidBits, cifPaintFunc, (ClientData)CIFPaintTable); + DBSrPaintArea((Tile *) NULL, curPlane, &TiPlaneRect, + &DBSpaceBits, cifCloseFunc, (ClientData)&curPlane); - (void) DBSrPaintArea((Tile *) NULL, curPlane, &TiPlaneRect, - &DBSpaceBits, cifCloseFunc, (ClientData)&curPlane); temp = curPlane; curPlane = nextPlane; nextPlane = temp; From 503a6bc1907f54e251acdc0594984e0d9bb9a727 Mon Sep 17 00:00:00 2001 From: Tim Edwards Date: Sun, 18 Oct 2020 14:28:23 -0400 Subject: [PATCH 07/19] Corrected the (minor) problem caused by the slightly different handling of an alias that defines only one type (which is treated like it would be if it were declared on the same line as the original type declaration), such that the alias name does not become the default name for the layer (which is the one that ends up showing in the title bar when hovering over the layer toolbar icon). Mainly the problem showed up as "nwell" for the sky130A process appearing in the toolbar as "allwellplane". --- database/DBtechname.c | 3 +++ database/DBtechtype.c | 6 ++++-- database/databaseInt.h | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/database/DBtechname.c b/database/DBtechname.c index df052581..5cf97b1b 100644 --- a/database/DBtechname.c +++ b/database/DBtechname.c @@ -451,6 +451,9 @@ DBTechPrintTypes(mask, dolist) for (p = dbTypeNameLists.sn_next; p != &dbTypeNameLists; p = p->sn_next) { + /* Ignore aliases */ + if (p->sn_alias) continue; + if (((TileType)(spointertype) p->sn_value) == i) { if (dolist) diff --git a/database/DBtechtype.c b/database/DBtechtype.c index 5eeb9d1f..e7fe51ed 100644 --- a/database/DBtechtype.c +++ b/database/DBtechtype.c @@ -760,7 +760,7 @@ dbTechNameAdd(name, cdata, ptable, alias) *cp = '\0'; if (*(cp = onename)) { - if ((current = dbTechNameAddOne(cp, cdata, FALSE, ptable)) == NULL) + if ((current = dbTechNameAddOne(cp, cdata, FALSE, alias, ptable)) == NULL) return (NULL); if (first == NULL) first = current->sn_name; @@ -793,10 +793,11 @@ dbTechNameAdd(name, cdata, ptable, alias) */ NameList * -dbTechNameAddOne(name, cdata, isPrimary, ptable) +dbTechNameAddOne(name, cdata, isPrimary, isAlias, ptable) char *name; /* Name to be added */ ClientData cdata; /* Client value associated with this name */ bool isPrimary; /* TRUE if this is the primary abbreviation */ + bool isAlias; /* TRUE if this name is an alias */ NameList *ptable; /* Table of names to which we're adding this */ { int cmp; @@ -817,6 +818,7 @@ dbTechNameAddOne(name, cdata, isPrimary, ptable) new->sn_name = StrDup((char **) NULL, name); new->sn_value = cdata; new->sn_primary = isPrimary; + new->sn_alias = isAlias; /* Link this entry in to the list before 'tbl' */ new->sn_next = tbl; diff --git a/database/databaseInt.h b/database/databaseInt.h index 5f631cfb..eda67bed 100644 --- a/database/databaseInt.h +++ b/database/databaseInt.h @@ -81,6 +81,7 @@ typedef struct namelist char *sn_name; /* Text of name */ ClientData sn_value; /* Value (TileType or plane number) */ bool sn_primary; /* If TRUE, this is the primary name */ + bool sn_alias; /* If TRUE, this is an alias name */ } NameList; typedef struct From 8da47b6e8494ed1b0d4495cdc2034c67f5e80583 Mon Sep 17 00:00:00 2001 From: Tim Edwards Date: Tue, 20 Oct 2020 12:22:02 -0400 Subject: [PATCH 08/19] Made a change to the way ext2sim determines if a FET type is an n or p device; this was previously dependent only on the first character of the extracted device model name. Since the tech file has control over what the device layer names are but not the extracted model names, the device layer type name is used as a backup way to determine if the type is n or p, if that cannot be determined from the extracted model name. --- ext2sim/ext2sim.c | 52 ++++++++++++++++++++++++++++++++++++++----- ext2spice/ext2spice.c | 4 +++- extract/ExtTech.c | 7 ++++-- 3 files changed, 55 insertions(+), 8 deletions(-) diff --git a/ext2sim/ext2sim.c b/ext2sim/ext2sim.c index 7034cc71..35b28c05 100644 --- a/ext2sim/ext2sim.c +++ b/ext2sim/ext2sim.c @@ -92,10 +92,11 @@ FILE *esLabF = NULL; static unsigned short esFormat = MIT ; struct { - short resClassSource ; /* the resistance class of the source of the dev */ - short resClassDrain ; /* the resistance class of the drain of the dev */ - short resClassSub ; /* the resistance class of the substrate of the dev */ - char *defSubs ; /* the default substrate node */ + short resClassSource ; /* The resistance class of the source of the dev */ + short resClassDrain ; /* The resistance class of the drain of the dev */ + short resClassSub ; /* The resistance class of the substrate of the dev */ + TileType devType ; /* Magic tile type of the device */ + char *defSubs ; /* The default substrate node */ } fetInfo[MAXDEVTYPES]; typedef struct { @@ -266,6 +267,7 @@ CmdExtToSim(w, cmd) short s_rclass, d_rclass, sub_rclass; char *devname; char *subname; + TileType devtype; int idx; static EFCapValue LocCapThreshold = 2; @@ -576,6 +578,7 @@ runexttosim: fetInfo[i].resClassDrain = NO_RESCLASS; fetInfo[i].resClassSub = NO_RESCLASS; fetInfo[i].defSubs = NULL; + fetInfo[i].devType = TT_SPACE; } /* Get fetInfo information from the current extraction style */ @@ -583,7 +586,8 @@ runexttosim: /* command) */ idx = 0; - while (ExtGetDevInfo(idx++, &devname, &s_rclass, &d_rclass, &sub_rclass, &subname)) + while (ExtGetDevInfo(idx++, &devname, &devtype, &s_rclass, &d_rclass, + &sub_rclass, &subname)) { if (idx == MAXDEVTYPES) { @@ -598,6 +602,7 @@ runexttosim: fetInfo[i].resClassDrain = d_rclass; fetInfo[i].resClassSub = sub_rclass; fetInfo[i].defSubs = subname; + fetInfo[i].devType = devtype; } } @@ -676,6 +681,7 @@ main(argc, argv) fetInfo[i].resClassDrain = NO_RESCLASS; fetInfo[i].resClassSub = NO_RESCLASS; fetInfo[i].defSubs = NULL; + fetInfo[i].devType = TT_SPACE; } i = efBuildAddStr(EFDevTypes, &EFDevNumTypes, MAXDEVTYPES, "nfet"); fetInfo[i].resClassSource = fetInfo[i].resClassDrain = 0 ; @@ -895,6 +901,7 @@ simmainArgs(pargc, pargv) fetInfo[ndx].resClassDrain = rClass; fetInfo[ndx].resClassSub = rClassSub; fetInfo[ndx].defSubs = (char *) mallocMagic((unsigned) (strlen(subsNode)+1)); + fetInfo[ndx].devType = TT_SPACE; strcpy(fetInfo[ndx].defSubs,subsNode); TxError("Info: fet %s(%d) sdRclass=%d subRclass=%d dSub=%s\n", cp, ndx, fetInfo[ndx].resClassSD, fetInfo[ndx].resClassSub, @@ -1047,6 +1054,41 @@ simdevVisit(dev, hc, scale, trans) case DEV_CAPREV: fprintf(esSimF, "c"); /* sim format extension */ break; + case DEV_FET: + case DEV_MOSFET: + case DEV_ASYMMETRIC: + case DEV_MSUBCKT: + /* The sim file format only understands "n" and "p" for FETs. */ + /* The extraction method says nothing about which is which. */ + /* The EFDevTypes[] should ideally start with "n" or "p". If */ + /* it doesn't, then dev->dev_type should. If neither does, */ + /* then use EFDevTypes[] but flag an error. */ + + if (EFDevTypes[dev->dev_type][0] == 'n' || + EFDevTypes[dev->dev_type][0] == 'p') + { + fprintf(esSimF, "%c", EFDevTypes[dev->dev_type][0]); + } + else + { + TileType ttype = fetInfo[dev->dev_type].devType; + + if (DBTypeLongNameTbl[ttype][0] == 'n' || + DBTypeLongNameTbl[ttype][0] == 'p') + { + fprintf(esSimF, "%c", DBTypeLongNameTbl[ttype][0]); + } + else + { + TxError("Error: MOSFET device type \"%s\" does not start with " + "\"n\" or \"p\" as required for the .sim format\n", + EFDevTypes[dev->dev_type]); + + /* Default to "n" */ + fprintf(esSimF, "n"); + } + } + break; default: fprintf(esSimF, "%c", EFDevTypes[dev->dev_type][0]); break; diff --git a/ext2spice/ext2spice.c b/ext2spice/ext2spice.c index 0a402d7b..5abbfe43 100644 --- a/ext2spice/ext2spice.c +++ b/ext2spice/ext2spice.c @@ -242,6 +242,7 @@ CmdExtToSpice(w, cmd) short s_rclass, d_rclass, sub_rclass; char *devname; char *subname; + TileType devtype; int idx, idx2; globalList *glist = NULL; @@ -739,7 +740,8 @@ runexttospice: /* command) */ idx = 0; - while (ExtGetDevInfo(idx++, &devname, &s_rclass, &d_rclass, &sub_rclass, &subname)) + while (ExtGetDevInfo(idx++, &devname, &devtype, &s_rclass, &d_rclass, + &sub_rclass, &subname)) { if (idx == MAXDEVTYPES) { diff --git a/extract/ExtTech.c b/extract/ExtTech.c index c0b2da44..4f6bdb59 100644 --- a/extract/ExtTech.c +++ b/extract/ExtTech.c @@ -304,9 +304,11 @@ ExtCompareStyle(stylename) */ bool -ExtGetDevInfo(idx, devnameptr, s_rclassptr, d_rclassptr, sub_rclassptr, subnameptr) +ExtGetDevInfo(idx, devnameptr, devtypeptr, s_rclassptr, d_rclassptr, + sub_rclassptr, subnameptr) int idx; - char **devnameptr; + char **devnameptr; /* Name of extracted device model */ + TileType *devtypeptr; /* Magic tile type of device */ short *s_rclassptr; /* Source (1st terminal) type only */ short *d_rclassptr; /* Drain (2nd terminal) type only */ short *sub_rclassptr; @@ -354,6 +356,7 @@ ExtGetDevInfo(idx, devnameptr, s_rclassptr, d_rclassptr, sub_rclassptr, subnamep *devnameptr = locdname; *subnameptr = devptr->exts_deviceSubstrateName; + *devtypeptr = t; tmask = &devptr->exts_deviceSDTypes[0]; *s_rclassptr = (short)(-1); /* NO_RESCLASS */ From d1793ce19f80d42c6466cc63dc81cd0053440e13 Mon Sep 17 00:00:00 2001 From: Tim Edwards Date: Tue, 20 Oct 2020 12:25:10 -0400 Subject: [PATCH 09/19] Updated version to go along with the last commit. --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 6aac87ef..2e03c5a9 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.3.70 +8.3.71 From be17067e1338d67aed361bf7a451b6cf145fa202 Mon Sep 17 00:00:00 2001 From: Tim Edwards Date: Thu, 22 Oct 2020 10:44:59 -0400 Subject: [PATCH 10/19] Corrected a problem with argument passing in the DRC subcell copying up of DRC errors into the parent cell. --- VERSION | 2 +- drc/DRCsubcell.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/VERSION b/VERSION index 2e03c5a9..fbf1f18d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.3.71 +8.3.72 diff --git a/drc/DRCsubcell.c b/drc/DRCsubcell.c index 0a2bd477..9ecdb353 100644 --- a/drc/DRCsubcell.c +++ b/drc/DRCsubcell.c @@ -164,9 +164,9 @@ drcSubCopyErrors(tile, cxp) */ int -drcSubCopyFunc(scx, fp) +drcSubCopyFunc(scx, cdarg) SearchContext *scx; - TreeFilter *fp; + ClientData cdarg; { TileTypeBitMask drcMask; @@ -175,7 +175,7 @@ drcSubCopyFunc(scx, fp) TTMaskSetType(&drcMask, TT_ERROR_P); /* Use DBNoTreeSrTiles() because we want to search only one level down */ - return DBNoTreeSrTiles(scx, &drcMask, 0, drcSubCopyErrors, fp->tf_arg); + return DBNoTreeSrTiles(scx, &drcMask, 0, drcSubCopyErrors, cdarg); } /* From 0a1cb9ca99cc42c41a62d70cc842fbdc4966862f Mon Sep 17 00:00:00 2001 From: Tim Edwards Date: Mon, 26 Oct 2020 11:41:36 -0400 Subject: [PATCH 11/19] Added a method to ignore a specific device extraction combination by setting the model name to "Ignore" in the tech file. --- VERSION | 2 +- extract/ExtBasic.c | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/VERSION b/VERSION index fbf1f18d..ff64910e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.3.72 +8.3.73 diff --git a/extract/ExtBasic.c b/extract/ExtBasic.c index b273c5ab..7d9aa667 100644 --- a/extract/ExtBasic.c +++ b/extract/ExtBasic.c @@ -1857,6 +1857,14 @@ extOutputDevices(def, transList, outFile) #endif extTransRec.tr_devrec = devptr; + /* Model type "Ignore" in the techfile indicates a device */ + /* to be ignored (i.e., a specific combination of layers */ + /* does not form an extractable device, or overlaps another */ + /* device type that should take precedence). */ + + if (!strcmp(devptr->exts_deviceName, "Ignore")) + continue; + /* Original-style FET record backward compatibility */ if (devptr->exts_deviceClass != DEV_FET) fprintf(outFile, "device "); From 3755661196cb42f55d263a329ff79730c37ea423 Mon Sep 17 00:00:00 2001 From: Tim Edwards Date: Mon, 26 Oct 2020 13:01:04 -0400 Subject: [PATCH 12/19] Changed the behavior of searches for labels in the basic extraction such that it looks for material connecting to the label at the center point of the label and not the lower left corner. This keeps the behavior of looking for tiles on the corners of a degenerate label line or point between layers, but avoids problems with sticky labels that are not quite aligned with the rectangle (due to certain commercial EDA tools that have a sloppier notion of labeling). --- extract/ExtRegion.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/extract/ExtRegion.c b/extract/ExtRegion.c index 0843b7a0..658d272c 100644 --- a/extract/ExtRegion.c +++ b/extract/ExtRegion.c @@ -218,13 +218,14 @@ ExtLabelRegions(def, connTo, nodeList, clipArea) for (quad = 0; quad < 4; quad++) { /* - * Visit each of the four quadrants surrounding - * the lower-left corner of the label, searching - * for a tile whose type matches that of the label - * or connects to it. + * Visit each of the four quadrants surrounding the center + * point of the label, searching for a tile whose type matches + * that of the label or connects to it. */ - p.p_x = lab->lab_rect.r_xbot + offsets[quad].p_x; - p.p_y = lab->lab_rect.r_ybot + offsets[quad].p_y; + p.p_x = ((lab->lab_rect.r_xbot + lab->lab_rect.r_xtop) >> 1) + + offsets[quad].p_x; + p.p_y = ((lab->lab_rect.r_ybot + lab->lab_rect.r_ytop) >> 1) + + offsets[quad].p_y; tp = def->cd_planes[pNum]->pl_hint; GOTOPOINT(tp, &p); def->cd_planes[pNum]->pl_hint = tp; From 1891abe3481b6d9f62c55d3366376ae5c707feb6 Mon Sep 17 00:00:00 2001 From: Tim Edwards Date: Mon, 26 Oct 2020 13:12:18 -0400 Subject: [PATCH 13/19] Also added code to not put parameters for a device marked "Ignore" into the .ext file, in case such devices were assigned parameters. --- extract/ExtBasic.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/extract/ExtBasic.c b/extract/ExtBasic.c index 7d9aa667..d8175087 100644 --- a/extract/ExtBasic.c +++ b/extract/ExtBasic.c @@ -1484,6 +1484,10 @@ extOutputParameters(def, transList, outFile) for (devptr = ExtCurStyle->exts_device[t]; devptr; devptr = devptr->exts_next) { + /* Do not output parameters for ignored devices */ + if (!strcmp(devptr->exts_deviceName, "Ignore")) continue; + + fprintf(outFile, "parameters %s", devptr->exts_deviceName); plist = devptr->exts_deviceParams; if (plist != (ParamList *)NULL) { From d50fd1c42c87caabc3f2418a42c7e9a267f82267 Mon Sep 17 00:00:00 2001 From: Tim Edwards Date: Mon, 26 Oct 2020 13:23:52 -0400 Subject: [PATCH 14/19] Corrected a wayward cut-and-paste error from the last commit. --- extract/ExtBasic.c | 1 - 1 file changed, 1 deletion(-) diff --git a/extract/ExtBasic.c b/extract/ExtBasic.c index d8175087..0dd5cde2 100644 --- a/extract/ExtBasic.c +++ b/extract/ExtBasic.c @@ -1487,7 +1487,6 @@ extOutputParameters(def, transList, outFile) /* Do not output parameters for ignored devices */ if (!strcmp(devptr->exts_deviceName, "Ignore")) continue; - fprintf(outFile, "parameters %s", devptr->exts_deviceName); plist = devptr->exts_deviceParams; if (plist != (ParamList *)NULL) { From 1fe032a79ba401887377cc1cdf8c1377aea20903 Mon Sep 17 00:00:00 2001 From: Tim Edwards Date: Tue, 27 Oct 2020 15:39:12 -0400 Subject: [PATCH 15/19] Changed the readspice script to avoid changing pin direction from a CDL file unless the existing pin direction is set to "default". This is consistent with the way the LEF annotation works, as well. --- VERSION | 2 +- tcltk/readspice.tcl | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/VERSION b/VERSION index ff64910e..dc0d58f7 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.3.73 +8.3.74 diff --git a/tcltk/readspice.tcl b/tcltk/readspice.tcl index dda21d63..333534f4 100644 --- a/tcltk/readspice.tcl +++ b/tcltk/readspice.tcl @@ -220,10 +220,14 @@ proc readspice {netfile} { set pinname [lindex $infopair 0] set pindir [lindex $infopair 1] if {![catch {set pin [dict get $pindict $pinname]}]} { - case $pindir { - B {port $pin class inout} - I {port $pin class input} - O {port $pin class output} + # Only set pin class if the pin class is currently default + set pinclass [port $pin class] + if {$pinclass == "default"} { + case $pindir { + B {port $pin class inout} + I {port $pin class input} + O {port $pin class output} + } } } elseif {$pinname != ""} { puts stderr ".PININFO error: Pin $pinname not found." From 06ab6b3a8aae0918987b8d90a0b900a5cd7c1087 Mon Sep 17 00:00:00 2001 From: Tim Edwards Date: Fri, 30 Oct 2020 11:20:19 -0400 Subject: [PATCH 16/19] Made a change to the way that the parameterized cells are handled. Instead of a 6-character suffix generated randomly, the 6-character suffix is generated by a hash algorithm from the device parameters. If the cell parameters are changed, then the cell itself changes. If the instance name was default (derived from the cell name) then the instance name changes accordingly. The result is that there cannot be two (auto-)generated cells with the same parameters but with different cell names. --- VERSION | 2 +- commands/CmdCD.c | 62 ++++++++---- commands/CmdLQ.c | 155 +++++++++++++++++++++++++++++ database/DBcellname.c | 159 +++++++++++++++++++++++++++-- database/database.h.in | 1 + dbwind/DBWcommands.c | 7 +- tcltk/cellmgr.tcl | 2 +- tcltk/toolkit.tcl | 220 ++++++++++++++++++++++++++++++++++++++++- 8 files changed, 571 insertions(+), 37 deletions(-) diff --git a/VERSION b/VERSION index dc0d58f7..c56821cd 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.3.74 +8.3.76 diff --git a/commands/CmdCD.c b/commands/CmdCD.c index f235f693..fda146a9 100644 --- a/commands/CmdCD.c +++ b/commands/CmdCD.c @@ -624,7 +624,8 @@ outputCalma: * or * cellname property [name] [property_key [property_value]] * or - * instance orientation [name] [-def] + * instance orientation [name] [-def] [orient] + * instance abutment [name] * * Results: * None. @@ -648,7 +649,7 @@ CmdCellname(w, cmd) bool dodef = FALSE; int option; int locargc = cmd->tx_argc; - char *cellname = NULL; + char *cellname = NULL, *orient = NULL; void (*func)(); CellDef *newDef, *cellDef; @@ -671,6 +672,7 @@ CmdCellname(w, cmd) "lock lock the named cell (prevent changes to cell use)", "unlock unlock the named cell (allow changes to cell use)", "property list or set cell definition properties", + "abutment list instance abutment coordinates", "orientation list or set instance orientation", "rename rename the indicated cell", "writeable make the cell definition read-only or read-write", @@ -681,7 +683,7 @@ CmdCellname(w, cmd) IDX_INSTANCE, IDX_CHILDINST, IDX_CELLDEF, IDX_ALLCELLS, IDX_TOPCELLS, IDX_IN_WINDOW, IDX_CREATE, IDX_DELETE, IDX_FILEPATH, IDX_FLAGS, IDX_LOCK, IDX_UNLOCK, - IDX_PROPERTY, IDX_ORIENTATION, IDX_RENAME, + IDX_PROPERTY, IDX_ABUTMENT, IDX_ORIENTATION, IDX_RENAME, IDX_READWRITE, IDX_MODIFIED } optionType; if (strstr(cmd->tx_argv[0], "in")) @@ -720,7 +722,7 @@ CmdCellname(w, cmd) if ((locargc > 3) && (option != IDX_RENAME) && (option != IDX_DELETE) && (option != IDX_READWRITE) && (option != IDX_PROPERTY) && - (option != IDX_FILEPATH)) + (option != IDX_FILEPATH) && (option != IDX_ORIENTATION)) goto badusage; if ((locargc > 4) && (option != IDX_PROPERTY)) @@ -751,6 +753,10 @@ CmdCellname(w, cmd) TxError("Cell definitions do not have orientations." " Use \"instance\"?\n"); return; + case IDX_ABUTMENT: + TxError("Use \"property get FIXED_BBOX\" to get the cell " + "abutment box.\n"); + return; } } else @@ -1029,7 +1035,16 @@ CmdCellname(w, cmd) } break; case IDX_ORIENTATION: - DBOrientUse(cellname, dodef); + orient = (locargc == 4) ? cmd->tx_argv[3 + ((dolist) ? 1 : 0)] : NULL; + if ((cellname != NULL) && (orient != NULL)) + { + TxError("Cannot set orientation by name. Use selection.\n"); + break; + } + DBOrientUse(cellname, dodef, orient); + break; + case IDX_ABUTMENT: + DBAbutmentUse(cellname, dolist); break; case IDX_LOCK: DBLockUse(cellname, TRUE); @@ -1647,7 +1662,7 @@ CmdCif(w, cmd) * the interpreter to uniquely identify it. * * The "-origin" option has been added to allow rotating stuff - * relative to the origin, insteadd of the lower-left corner. + * relative to the origin, instead of the lower-left corner. * * ---------------------------------------------------------------------------- */ @@ -3897,9 +3912,9 @@ cmdDumpParseArgs(cmdName, w, cmd, dummy, scx) hasChild = hasRoot = hasTrans = FALSE; while (ac > 0) { - static char *kwdNames[] = { "child", "parent", "90", "180", "270", - "v", "90v", "180v", "270v", - "h", "90h", "180h", "270h", 0 }; + static char *kwdNames[] = { "child", "parent", "0", "90", "180", "270", + "v", "0v", "90v", "180v", "270v", + "h", "0h", "90h", "180h", "270h", 0 }; static char *refPointNames[] = { "ll", "lr", "ul", "ur", 0 }; Label *lab; int n,p; @@ -4043,8 +4058,8 @@ cmdDumpParseArgs(cmdName, w, cmd, dummy, scx) } hasRoot = TRUE; break; - case 2: /* 90 */ - tx_cell = &Geo90Transform; + case 2: /* 0 */ + tx_cell = &GeoIdentityTransform; transform_cell: if (ac < 2 ) { @@ -4127,34 +4142,39 @@ default_action: } hasTrans = TRUE; break; - case 3: /* 180 */ + case 3: /* 90 */ + tx_cell = &Geo90Transform; + goto transform_cell; + case 4: /* 180 */ tx_cell = &Geo180Transform; goto transform_cell; - case 4: /* 270 */ + case 5: /* 270 */ tx_cell = &Geo270Transform; goto transform_cell; - case 5: /* v */ + case 6: /* v */ + case 7: /* 0v */ tx_cell = &GeoUpsideDownTransform; goto transform_cell; - case 6: /* 90v */ + case 8: /* 90v */ tx_cell = &GeoRef45Transform; goto transform_cell; - case 7: /* 180v */ + case 9: /* 180v */ tx_cell = &GeoSidewaysTransform; goto transform_cell; - case 8: /* 270v */ + case 10: /* 270v */ tx_cell = &GeoRef135Transform; goto transform_cell; - case 9: /* h */ + case 11: /* h */ + case 12: /* 0h */ tx_cell = &GeoSidewaysTransform; goto transform_cell; - case 10: /* 90h */ + case 13: /* 90h */ tx_cell = &GeoRef135Transform; goto transform_cell; - case 11: /* 180h */ + case 14: /* 180h */ tx_cell = &GeoUpsideDownTransform; goto transform_cell; - case 12: /* 270h */ + case 15: /* 270h */ tx_cell = &GeoRef45Transform; goto transform_cell; } diff --git a/commands/CmdLQ.c b/commands/CmdLQ.c index c0bf6ce9..1c68635b 100644 --- a/commands/CmdLQ.c +++ b/commands/CmdLQ.c @@ -2276,3 +2276,158 @@ CmdNetlist(w, cmd) break; } } + +/* + * ---------------------------------------------------------------------------- + * + * CmdOrient -- + * + * Implement the "orient" command. Set the orientation of the selection and + * according to the orientation string, which can be in Magic's format + * (using number of degrees, and "h" for horizontal flip or "v" for vertical + * flip) or DEF format ("N", "S", "FN", etc.). The cursor box is rotated to + * match. + * + * Usage: + * orient [orientation] [-origin] + * + * Results: + * None. + * + * Side effects: + * Modifies the edit cell. + * + * Notes: + * The "-origin" option sets the orientation relative to the origin, + * instead of the lower-left corner. + * + * ---------------------------------------------------------------------------- + */ + +void +CmdOrient(w, cmd) + MagWindow *w; + TxCommand *cmd; +{ + Transform trans, t2; + int orientidx, locargc; + char *orientstr; + Rect rootBox, bbox; + CellDef *rootDef; + bool noAdjust = FALSE; + + static char *orientNames[] = { "0", "90", "180", "270", + "v", "0v", "90v", "180v", "270v", + "h", "0h", "90h", "180h", "270h", + "N", "E", "S", "W", + "FN", "FE", "FS", "FW", + 0 }; + + typedef enum { IDX_ORIENT_0, IDX_ORIENT_90, IDX_ORIENT_180, IDX_ORIENT_270, + IDX_ORIENT_V, IDX_ORIENT_0V, IDX_ORIENT_90V, IDX_ORIENT_180V, + IDX_ORIENT_270V, IDX_ORIENT_H, IDX_ORIENT_0H, IDX_ORIENT_90H, + IDX_ORIENT_180H, IDX_ORIENT_270H, IDX_ORIENT_N, IDX_ORIENT_E, + IDX_ORIENT_S, IDX_ORIENT_W, IDX_ORIENT_FN, IDX_ORIENT_FE, + IDX_ORIENT_FS, IDX_ORIENT_FW } orientType; + + locargc = cmd->tx_argc; + if (!strncmp(cmd->tx_argv[locargc - 1], "-orig", 5)) + { + noAdjust = TRUE; + locargc--; + } + + if (!ToolGetEditBox((Rect *)NULL)) return; + + if (locargc == 2) + orientstr = cmd->tx_argv[1]; + else + goto badusage; + + orientidx = Lookup(orientstr, orientNames); + + switch (orientidx) + { + case IDX_ORIENT_0: + case IDX_ORIENT_N: + /* ORIENT_NORTH */ + t2 = GeoIdentityTransform; + break; + case IDX_ORIENT_S: + case IDX_ORIENT_180: + /* ORIENT_SOUTH */ + t2 = Geo180Transform; + break; + case IDX_ORIENT_E: + case IDX_ORIENT_90: + /* ORIENT_EAST */ + t2 = Geo90Transform; + break; + case IDX_ORIENT_W: + case IDX_ORIENT_270: + /* ORIENT_WEST */ + t2 = Geo270Transform; + break; + case IDX_ORIENT_FN: + case IDX_ORIENT_H: + case IDX_ORIENT_0H: + /* ORIENT_FLIPPED_NORTH */ + t2 = GeoSidewaysTransform; + break; + case IDX_ORIENT_FS: + case IDX_ORIENT_180H: + case IDX_ORIENT_V: + case IDX_ORIENT_0V: + /* ORIENT_FLIPPED_SOUTH */ + t2 = GeoUpsideDownTransform; + break; + case IDX_ORIENT_FE: + case IDX_ORIENT_90H: + case IDX_ORIENT_270V: + /* ORIENT_FLIPPED_EAST */ + t2 = GeoRef135Transform; + break; + case IDX_ORIENT_FW: + case IDX_ORIENT_270H: + case IDX_ORIENT_90V: + /* ORIENT_FLIPPED_WEST */ + t2 = GeoRef45Transform; + break; + default: + goto badusage; + } + + /* To orient the selection, first orient it relative to the origin + * then move it so its lower-left corner is at the same place + * that it used to be. If the "-origin" option was selected, then + * only orient relative to the origin. + */ + + GeoTransRect(&t2, &SelectDef->cd_bbox, &bbox); + if (noAdjust) + trans = t2; + else + { + GeoTranslateTrans(&t2, SelectDef->cd_bbox.r_xbot - bbox.r_xbot, + SelectDef->cd_bbox.r_ybot - bbox.r_ybot, &trans); + } + + SelectTransform(&trans); + + /* Rotate the box, if it exists and is in the same window as the + * selection. + */ + + if (ToolGetBox(&rootDef, &rootBox) && (rootDef == SelectRootDef)) + { + Rect newBox; + + GeoTransRect(&trans, &rootBox, &newBox); + DBWSetBox(rootDef, &newBox); + } + + return; + + badusage: + TxError("Usage: %s [orientation]\n", cmd->tx_argv[0]); +} diff --git a/database/DBcellname.c b/database/DBcellname.c index ec10d1ee..dbfc32f0 100644 --- a/database/DBcellname.c +++ b/database/DBcellname.c @@ -1204,19 +1204,20 @@ DBLockUse(UseName, bval) * * DBOrientUse -- * - * This routine sets or reports a cell instance's orientation + * This routine reports a cell instance's orientation. * UseName is the name of a specific CellUse. If NULL, then the - * operation applies to all selected cell uses. "orient" is a - * string in the form used by "getcell" (e.g., "180", "270v", - * etc.), unless "dodef" is true, in which case the output is - * given in the form used by DEF ("N", "FN", etc.). - * reported. + * operation applies to all selected cell uses. * * Results: * None. * * Side effects: - * cu_transform changed for indicated cell use. + * In the Tcl/Tk implementation, the result is set in the interpreter. + * + * Notes: + * This routine only reports orientation. Setting orientation must + * be done through the selection interface (i.e., commands "sideways", + * "upsidedown", "clockwise" ("rotate"), or "orient" (added 10/30/2020)). * * ---------------------------------------------------------------------------- */ @@ -1294,6 +1295,7 @@ dbOrientUseFunc(selUse, use, transform, data) ClientData data; { bool *dodef = (bool *)data; + int orient; if (EditCellUse && !DBIsChild(use, EditCellUse)) { @@ -1302,9 +1304,16 @@ dbOrientUseFunc(selUse, use, transform, data) return 0; } + orient = -1; + if (selUse != NULL) + orient = GeoTransOrient(&selUse->cu_transform); + else if (use != NULL) + orient = GeoTransOrient(&use->cu_transform); + + if (orient != -1) { - switch (GeoTransOrient(&selUse->cu_transform)) { + switch (orient) { #ifdef MAGIC_WRAPPER case ORIENT_NORTH: Tcl_AppendElement(magicinterp, (*dodef) ? "N" : "0"); @@ -1362,6 +1371,140 @@ dbOrientUseFunc(selUse, use, transform, data) return 0; } +/* + * ---------------------------------------------------------------------------- + * + * DBAbutmentUse -- + * + * This routine reports the cell instance's abutment box in the + * coordinate system of the parent (edit) cell. + * + * Results: + * None. + * + * Side effects: + * None. + * + * ---------------------------------------------------------------------------- + */ + +void +DBAbutmentUse(UseName, dolist) + char *UseName; + bool dolist; +{ + int found; + HashSearch hs; + HashEntry *entry; + CellDef *celldef; + CellUse *celluse; + + int dbAbutmentUseFunc(); + + /* + * + * Check to see if a cell name was specified. If not, then search + * for selected cells. + * + */ + + if (UseName == NULL) + { + SelEnumCells(TRUE, (int *)NULL, (SearchContext *)NULL, + dbAbutmentUseFunc, (ClientData)&dolist); + } + else + { + SearchContext scx; + + bzero(&scx, sizeof(SearchContext)); + found = 0; + + HashStartSearch(&hs); + while( (entry = HashNext(&dbCellDefTable, &hs)) != NULL) + { + celldef = (CellDef *) HashGetValue(entry); + if (celldef != (CellDef *) NULL) + { + celluse = celldef->cd_parents; /* only need one */ + if (celluse != (CellUse *)NULL) { + DBTreeFindUse(UseName, celluse, &scx); + if (scx.scx_use != NULL) break; + } + } + } + + if (scx.scx_use == NULL) + TxError("Cell %s is not currently loaded.\n", UseName); + else + dbAbutmentUseFunc(NULL, scx.scx_use, NULL, (ClientData)&dolist); + } +} + +/* + * dbAbutmentUseFunc() + */ + +int +dbAbutmentUseFunc(selUse, use, transform, data) + CellUse *selUse; /* Use from selection cell */ + CellUse *use; /* Use from layout corresponding to selection */ + Transform *transform; + ClientData data; +{ + Rect bbox, refbox; + Transform *trans; + char *propvalue; + bool found; + bool *dolist = (bool *)data; + +#ifdef MAGIC_WRAPPER + Tcl_Obj *pobj; +#endif + + if (EditCellUse && !DBIsChild(use, EditCellUse)) + { + TxError("Cell %s (%s) isn't a child of the edit cell.\n", + use->cu_id, use->cu_def->cd_name); + return 0; + } + + if (use == NULL) + { + TxError("No instance in selection!\n"); + return 0; + } + + trans = &use->cu_transform; + propvalue = DBPropGet(use->cu_def, "FIXED_BBOX", &found); + if (!found) + bbox = use->cu_def->cd_bbox; + else + { + if (sscanf(propvalue, "%d %d %d %d", &bbox.r_xbot, &bbox.r_ybot, + &bbox.r_xtop, &bbox.r_ytop) != 4) + bbox = use->cu_def->cd_bbox; + } + GeoTransRect(trans, &bbox, &refbox); + +#ifdef MAGIC_WRAPPER + if (*dolist) + { + pobj = Tcl_NewListObj(0, NULL); + Tcl_ListObjAppendElement(magicinterp, pobj, Tcl_NewIntObj(refbox.r_xbot)); + Tcl_ListObjAppendElement(magicinterp, pobj, Tcl_NewIntObj(refbox.r_ybot)); + Tcl_ListObjAppendElement(magicinterp, pobj, Tcl_NewIntObj(refbox.r_xtop)); + Tcl_ListObjAppendElement(magicinterp, pobj, Tcl_NewIntObj(refbox.r_ytop)); + Tcl_SetObjResult(magicinterp, pobj); + } + else +#endif + TxPrintf("Abutment box: %d %d %d %d\n", refbox.r_xbot, refbox.r_ybot, + refbox.r_xtop, refbox.r_ytop); + + return 0; +} + /* * ---------------------------------------------------------------------------- * diff --git a/database/database.h.in b/database/database.h.in index af818186..c17d8d8c 100644 --- a/database/database.h.in +++ b/database/database.h.in @@ -778,6 +778,7 @@ extern CellUse *DBCellFindDup(); extern void DBLockUse(); extern void DBUnlockUse(); extern void DBOrientUse(); +extern void DBAbutmentUse(); /* Cell selection */ extern CellUse *DBSelectCell(); diff --git a/dbwind/DBWcommands.c b/dbwind/DBWcommands.c index 6a5150e4..9b1d423d 100644 --- a/dbwind/DBWcommands.c +++ b/dbwind/DBWcommands.c @@ -47,8 +47,8 @@ extern void CmdEdit(), CmdElement(), CmdErase(), CmdExpand(), CmdExtract(); extern void CmdFeedback(), CmdFill(), CmdFindBox(), CmdFindLabel(), CmdFlush(); extern void CmdGetcell(), CmdGrid(), CmdIdentify(); extern void CmdLabel(), CmdLoad(); -extern void CmdMove(), CmdNetlist(), CmdPaint(), CmdPath(), CmdPlow(); -extern void CmdPolygon(), CmdPort(), CmdProperty(); +extern void CmdMove(), CmdNetlist(), CmdOrient(), CmdPaint(), CmdPath(); +extern void CmdPlow(), CmdPolygon(), CmdPort(), CmdProperty(); extern void CmdSave(), CmdScaleGrid(), CmdSee(); extern void CmdSelect(), CmdSetLabel(), CmdSideways(); extern void CmdShell(), CmdSnap(); @@ -378,6 +378,9 @@ DBWInitCommands() for information on options", CmdNetlist, FALSE); + WindAddCommand(DBWclientID, + "orient [orientation] orient selection and box", + CmdOrient, FALSE); WindAddCommand(DBWclientID, "paint layers|cursor paint mask information", CmdPaint, FALSE); diff --git a/tcltk/cellmgr.tcl b/tcltk/cellmgr.tcl index 1ea0b0db..5f715857 100644 --- a/tcltk/cellmgr.tcl +++ b/tcltk/cellmgr.tcl @@ -10,7 +10,7 @@ if {$::tk_version >= 8.5} { set Opts(cellmgr) 0 -magic::tag select "magic::mgrselect %R" +magic::tag select "magic::mgrselect %r" magic::tag load "catch {magic::clearstack}; magic::cellmanager" magic::tag getcell "magic::cellmanager" diff --git a/tcltk/toolkit.tcl b/tcltk/toolkit.tcl index 75fa12cd..b3d43019 100644 --- a/tcltk/toolkit.tcl +++ b/tcltk/toolkit.tcl @@ -6,6 +6,8 @@ # Revision 0 # December 15, 2016 # Revision 1 +# October 29, 2020 +# Revision 2 (names are hashed from properties) #-------------------------------------------------------------- # Sets up the environment for a toolkit. The toolkit must # supply a namespace that is the "library name". For each @@ -237,6 +239,36 @@ proc magic::gencell {gencell_name {instname {}} args} { } } +#------------------------------------------------------------- +# gencell_makecell +# +# This is a variation of magic::gencell and is used to generate +# a cell and return the cell name without creating or placing +# an instance. +#------------------------------------------------------------- + +proc magic::gencell_makecell {gencell_fullname args} { + + set argpar [dict create {*}$args] + set gencell_basename [namespace tail $gencell_fullname] + set library [namespace qualifiers $gencell_fullname] + set parameters [magic::gencell_defaults $gencell_basename $library $argpar] + set gsuffix [magic::get_gencell_hash ${parameters}] + set gname ${gencell_basename}_${gsuffix} + suspendall + cellname create $gname + pushstack $gname + if {[catch {${library}::${gencell_basename}_draw $parameters} drawerr]} { + puts stderr $drawerr + } + property library $library + property gencell $gencell_basename + property parameters $parameters + popstack + resumeall + return $gname +} + #------------------------------------------------------------- # gencell_getparams # @@ -271,6 +303,9 @@ proc magic::gencell_setparams {parameters} { if {[catch {set state [wm state .params]}]} {return} set slist [grid slaves .params.edits] foreach s $slist { + # ignore .params.edits.gencell_sel, as that does not exist in the + # parameters dictionary + if {$s == ".params.edits.gencell_sel"} {continue} if {[regexp {^.params.edits.(.*)_ent$} $s valid pname] != 0} { set value [dict get $parameters $pname] set magic::${pname}_val $value @@ -293,7 +328,16 @@ proc magic::gencell_setparams {parameters} { #------------------------------------------------------------- # gencell_change # -# Redraw a gencell with new parameters. +# Recreate a gencell with new parameters. Note that because +# each cellname is uniquely identified by the (hashed) set +# of parameters, changing parameters effectively means +# creating a new cell. If the original cell has parents +# other than the parent of the instance being changed, then +# it is retained; otherwise, it is deleted. The instance +# being edited gets replaced by an instance of the new cell. +# If the instance name was the cellname + suffix, then the +# instance name is regenerated. Otherwise, the instance +# name is retained. #------------------------------------------------------------- proc magic::gencell_change {instname gencell_type library parameters} { @@ -324,6 +368,128 @@ proc magic::gencell_change {instname gencell_type library parameters} { set parameters [dict remove $parameters gencell] } + set old_gname [instance list celldef $instname] + set gsuffix [magic::get_gencell_hash ${parameters}] + set gname ${gencell_type}_${gsuffix} + + # Guard against instance having been deleted. Also, if parameters have not + # changed as evidenced by the cell suffix not changing, then nothing further + # needs to be done. + if {$gname == "" || $gname == $old_gname} { + resumeall + return + } + + set snaptype [snap list] + snap internal + set savebox [box values] + + catch {setpoint 0 0 $Opts(focus)} + if [dict exists $parameters nocell] { + select cell $instname + set abox [instance list abutment] + delete + if {$abox != ""} {box values {*}$abox} + if {[catch {set newinst [${library}::${gencell_type}_draw $parameters]} \ + drawerr]} { + puts stderr $drawerr + } + select cell $newinst + } elseif {[cellname list exists $gname] != 0} { + # If there is already a cell of this type then it is only required to + # remove the instance and replace it with an instance of the cell + select cell $instname + # check rotate/flip before replacing and replace with same + set orient [instance list orientation] + set abox [instance list abutment] + delete + + if {$abox != ""} {box values {*}$abox} + set newinstname [getcell $gname $orient] + select cell $newinstname + expand + + # If the old instance name was not formed from the old cell name, + # then keep the old instance name. + if {[string first $old_gname $instname] != 0} { + set newinstname $instname + } + + if {[cellname list parents $old_gname] == []} { + # If the original cell has no intances left, delete it. It can + # be regenerated if and when necessary. + cellname delete $old_gname + } + + } else { + select cell $instname + set orient [instance list orientation] + set abox [instance list abutment] + delete + + # There is no cell of this name, so generate one and instantiate it. + if {$abox != ""} {box values {*}$abox} + set newinstname [magic::gencell_create $gencell_type $library $parameters $orient] + select cell $newinstname + + # If the old instance name was not formed from the old cell name, + # then keep the old instance name. + if {[string first $old_gname $instname] != 0} { + set newinstname $instname + } + } + identify $newinstname + eval "box values $savebox" + snap $snaptype + + # Update window + if {$gname != $old_gname} { + catch {.params.title.glab configure -text "$gname"} + } + if {$instname != $newinstname} { + catch {.params.title.ient delete 0 end} + catch {.params.title.ient insert 0 "$newinstname"} + } + + resumeall + redraw +} + +#------------------------------------------------------------- +# gencell_change_orig +# +# Original version: Redraw a gencell with new parameters, +# without changing the cell itself. +#------------------------------------------------------------- + +proc magic::gencell_change_orig {instname gencell_type library parameters} { + global Opts + suspendall + + set newinstname $instname + if {$parameters == {}} { + # Get device defaults + set pdefaults [${library}::${gencell_type}_defaults] + # Pull user-entered values from dialog + set parameters [dict merge $pdefaults [magic::gencell_getparams]] + set newinstname [.params.title.ient get] + if {$newinstname == "(default)"} {set newinstname $instname} + if {$newinstname == $instname} {set newinstname $instname} + if {[instance list exists $newinstname] != ""} {set newinstname $instname} + } + if {[dict exists $parameters gencell]} { + # Setting special parameter "gencell" forces the gencell to change type + set gencell_type [dict get $parameters gencell] + } + if {[catch {set parameters [${library}::${gencell_type}_check $parameters]} \ + checkerr]} { + puts stderr $checkerr + } + magic::gencell_setparams $parameters + if {[dict exists $parameters gencell]} { + set parameters [dict remove $parameters gencell] + } + set gname [instance list celldef $instname] # Guard against instance having been deleted @@ -391,6 +557,51 @@ proc magic::get_gencell_name {gencell_type} { return ${gencell_type}_$postfix } +#---------------------------------------------------------------- +# get_gencell_hash +# +# A better approach to the above. Take the parameter +# dictionary, and run all the values through a hash function +# to generate a 30-bit value, then convert to base32. This +# gives a result that is repeatable for the same set of +# parameter values with a very low probability of a collision. +# +# The hash function is similar to elfhash but reduced from 32 +# to 30 bits so that the result can form a 6-character value +# in base32 with all characters being valid for a SPICE subcell +# name (e.g., alphanumeric only and case-insensitive). +#---------------------------------------------------------------- + +proc magic::get_gencell_hash {parameters} { + set hash 0 + # Apply hash + dict for {key value} $parameters { + foreach s [split $value {}] { + set hash [expr {($hash << 4) + [scan $s %c]}] + set high [expr {$hash & 0x03c0000000}] + set hash [expr {$hash ^ ($high >> 30)}] + set hash [expr {$hash & (~$high)}] + } + } + # Divide hash up into 5 bit values and convert to base32 + # using letters A-Z less I and O, and digits 2-9. + set cvals "" + for {set i 0} {$i < 6} {incr i} { + set oval [expr {($hash >> ($i * 5)) & 0x1f}] + if {$oval < 8} { + set bval [expr {$oval + 50}] + } elseif {$oval < 16} { + set bval [expr {$oval + 57}] + } elseif {$oval < 21} { + set bval [expr {$oval + 58}] + } else { + set bval [expr {$oval + 59}] + } + append cvals [binary format c* $bval] + } + return $cvals +} + #------------------------------------------------------------- # gencell_create # @@ -402,7 +613,7 @@ proc magic::get_gencell_name {gencell_type} { # the drawing routine is going to do to the stack! #------------------------------------------------------------- -proc magic::gencell_create {gencell_type library parameters} { +proc magic::gencell_create {gencell_type library parameters {orient 0}} { global Opts suspendall @@ -451,7 +662,8 @@ proc magic::gencell_create {gencell_type library parameters} { set gname [instance list celldef $instname] eval "box values $savebox" } else { - set gname [magic::get_gencell_name ${gencell_type}] + set gsuffix [magic::get_gencell_hash ${parameters}] + set gname ${gencell_type}_${gsuffix} cellname create $gname pushstack $gname if {[catch {${library}::${gencell_type}_draw $parameters} drawerr]} { @@ -462,7 +674,7 @@ proc magic::gencell_create {gencell_type library parameters} { property parameters $parameters popstack eval "box values $savebox" - set instname [getcell $gname] + set instname [getcell $gname $orient] expand } if {$newinstname != ""} { From f7eb54fac78deef463017179b132b6c2efdecddd Mon Sep 17 00:00:00 2001 From: Tim Edwards Date: Fri, 30 Oct 2020 20:19:29 -0400 Subject: [PATCH 17/19] Corrected the paint table from "compose", which was improperly handling compose lines that override contact behavior. --- VERSION | 2 +- database/DBtpaint.c | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index c56821cd..e7e69613 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.3.76 +8.3.75 diff --git a/database/DBtpaint.c b/database/DBtpaint.c index 26c8d884..10090b9a 100644 --- a/database/DBtpaint.c +++ b/database/DBtpaint.c @@ -639,7 +639,14 @@ dbTechAddPaintErase(type, sectionName, argc, argv) pMask &= ~rMask; - for (tres = 0; tres < DBNumTypes; tres++) + /* 10/30/2020: Changed from DBNumTypes to DBNumUserLayers, */ + /* because DBTechNoisyNameMask() was modified to add stacking */ + /* contact types, and it is not correct for tMask to have more */ + /* than one type in the mask that share the same plane. */ + /* NOTE: Paint rules for stacked contacts probably have to be */ + /* handled too, but in a separate way. */ + + for (tres = 0; tres < DBNumUserLayers; tres++) { if (TTMaskHasType(&tMask, tres)) { From 9931244e1e86f6f30bb6c141b08c159c479de4e1 Mon Sep 17 00:00:00 2001 From: Tim Edwards Date: Tue, 3 Nov 2020 12:17:16 -0500 Subject: [PATCH 18/19] Corrected what appears to be a long-standing error in the "extract unique" code. It was using DBEraseLabelsByContent() which would erase all matching labels, and could potentially erase labels that were still remaining on the list being processed, causing a segfault. Also corrected minor errors identified by valgrind during debugging the above-referenced problem. --- VERSION | 2 +- commands/CmdSubrs.c | 5 +++-- database/DBio.c | 2 +- database/DBlabel.c | 20 +++++--------------- database/database.h.in | 2 +- extract/ExtHard.c | 2 +- extract/ExtUnique.c | 2 +- 7 files changed, 13 insertions(+), 22 deletions(-) diff --git a/VERSION b/VERSION index e7e69613..c56821cd 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.3.75 +8.3.76 diff --git a/commands/CmdSubrs.c b/commands/CmdSubrs.c index b2f2b954..0f3b4ec1 100644 --- a/commands/CmdSubrs.c +++ b/commands/CmdSubrs.c @@ -734,8 +734,9 @@ again: } /* Remove any ".mag" file extension from the name */ - if (!strcmp(returnname + strlen(returnname) - 4, ".mag")) - *(returnname + strlen(returnname) - 4) = '\0'; + if (strlen(returnname) > 4) + if (!strcmp(returnname + strlen(returnname) - 4, ".mag")) + *(returnname + strlen(returnname) - 4) = '\0'; if (strcmp(returnname, def->cd_name) != 0) { diff --git a/database/DBio.c b/database/DBio.c index 76ab5a30..bac43ab1 100644 --- a/database/DBio.c +++ b/database/DBio.c @@ -2774,7 +2774,7 @@ DBCellWrite(cellDef, fileName) /* The cd_file should not have the .mag suffix, but make sure */ /* it doesn't before adding one. */ - if (strcmp(fileName + strlen(fileName) - 4, DBSuffix)) + if ((strlen(fileName) < 4) || (strcmp(fileName + strlen(fileName) - 4, DBSuffix))) { realname = (char *) mallocMagic(strlen(fileName) + strlen(DBSuffix) + 1); (void) sprintf(realname, "%s%s", fileName, DBSuffix); diff --git a/database/DBlabel.c b/database/DBlabel.c index 24225f7d..7786a3fa 100644 --- a/database/DBlabel.c +++ b/database/DBlabel.c @@ -426,9 +426,9 @@ DBEraseLabelsByContent(def, rect, type, text) /* * ---------------------------------------------------------------------------- * - * DBEraseLabelsByFunction -- + * DBRemoveLabel -- * - * Erase any labels found on the label list for which the function returns + * Erase a labels by reference. * TRUE. * * Results: @@ -443,19 +443,9 @@ DBEraseLabelsByContent(def, rect, type, text) */ void -DBEraseLabelsByFunction(def, func) +DBRemoveLabel(def, refLab) CellDef *def; /* Where to look for label to delete. */ - bool (*func)(); /* Function to call for each label. If it - * returns TRUE, we delete the label. - * - * Function should be of the form: - * - * bool func(lab) - * Label *lab; - * { - * return XXX; - * } - */ + Label *refLab; { Label *lab, *labPrev; @@ -464,7 +454,7 @@ DBEraseLabelsByFunction(def, func) labPrev = lab, lab = lab->lab_next) { nextCheck: - if (!(*func)(lab)) continue; + if (lab != refLab) continue; DBUndoEraseLabel(def, lab); DBWLabelChanged(def, lab, DBW_ALLWINDOWS); if (labPrev == NULL) diff --git a/database/database.h.in b/database/database.h.in index c17d8d8c..9069cc8d 100644 --- a/database/database.h.in +++ b/database/database.h.in @@ -729,7 +729,7 @@ extern void DBFontLabelSetBBox(); extern bool DBEraseLabel(); extern void DBEraseLabelAll(); extern void DBEraseLabelsByContent(); -extern void DBEraseLabelsByFunction(); +extern void DBRemoveLabel(); extern void DBReOrientLabel(); extern void DBAdjustLabels(); diff --git a/extract/ExtHard.c b/extract/ExtHard.c index a884a87e..7d733caa 100644 --- a/extract/ExtHard.c +++ b/extract/ExtHard.c @@ -427,9 +427,9 @@ extHardGenerateLabel(scx, reg, arg) len = strlen(gen) + prefixlen; newlab = (Label *) mallocMagic((unsigned) (sizeof (Label) + len - 3)); r.r_ll = reg->treg_tile->ti_ll; - GEOCLIP(&r,&scx->scx_area); r.r_ur.p_x = r.r_ll.p_x+1; r.r_ur.p_y = r.r_ll.p_y+1; + GEOCLIP(&r,&scx->scx_area); GeoTransRect(&scx->scx_trans, &r, &newlab->lab_rect); newlab->lab_type = TiGetType(reg->treg_tile); diff --git a/extract/ExtUnique.c b/extract/ExtUnique.c index 01947754..accb53fa 100644 --- a/extract/ExtUnique.c +++ b/extract/ExtUnique.c @@ -260,7 +260,7 @@ makeUnique: lab = ll2->ll_label; saveLab = *lab; - DBEraseLabelsByContent(def, &lab->lab_rect, lab->lab_type, lab->lab_text); + DBRemoveLabel(def, lab); (void) DBPutFontLabel(def, &saveLab.lab_rect, saveLab.lab_font, saveLab.lab_size, saveLab.lab_rotate, &saveLab.lab_offset, saveLab.lab_just, name2, From 6d463a20fedfd6a11fba067f8e90009bd53bebeb Mon Sep 17 00:00:00 2001 From: Tim Edwards Date: Fri, 6 Nov 2020 14:39:10 -0500 Subject: [PATCH 19/19] Corrected the "suspendall" and "resumeall" Tcl scripts so that they do not fail when using "magicexec -dnull" (because there are no layout windows, there is nothing to suspend and resume). --- VERSION | 2 +- tcltk/tools.tcl | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/VERSION b/VERSION index c56821cd..4fe55683 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.3.76 +8.3.77 diff --git a/tcltk/tools.tcl b/tcltk/tools.tcl index 89cd8401..6addd626 100644 --- a/tcltk/tools.tcl +++ b/tcltk/tools.tcl @@ -15,6 +15,7 @@ proc magic::suspendall {} { global Winopts if {[info commands winfo] != ""} { foreach window [magic::windownames layout] { + if {$window == 0} {continue} set framename [winfo toplevel $window] if {$framename == "."} { set framename $window @@ -30,6 +31,7 @@ proc magic::resumeall {} { global Winopts if {[info commands winfo] != ""} { foreach window [magic::windownames layout] { + if {$window == 0} {continue} set framename [winfo toplevel $window] if {$framename == "."} { set framename $window