/* * DRCtech.c -- * * Technology initialization for the DRC module. * * ********************************************************************* * * Copyright (C) 1985, 1990 Regents of the University of California. * * * Permission to use, copy, modify, and distribute this * * * software and its documentation for any purpose and without * * * fee is hereby granted, provided that the above copyright * * * notice appear in all copies. The University of California * * * makes no representations about the suitability of this * * * software for any purpose. It is provided "as is" without * * * express or implied warranty. Export of this software outside * * * of the United States of America may require an export license. * * ********************************************************************* */ #ifndef lint static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/drc/DRCtech.c,v 1.12 2010/10/20 12:04:12 tim Exp $"; #endif /* not lint */ #include #include #include #include #include "tcltk/tclmagic.h" #include "utils/magic.h" #include "utils/geometry.h" #include "tiles/tile.h" #include "utils/hash.h" #include "database/database.h" #include "drc/drc.h" #include "utils/tech.h" #include "textio/textio.h" #include "utils/malloc.h" #include "cif/cif.h" #include "cif/CIFint.h" #include "drc/drc.h" /* C99 compat */ #include "utils/tech.h" #include "plow/plow.h" CIFStyle *drcCifStyle = NULL; bool DRCForceReload = FALSE; HashTable DRCWhyErrorTable; /* Table of DRC errors */ /* * DRC interaction radius being used (not necessarily the same as * what's defined in the current DRC style). */ global int DRCTechHalo; global int DRCStepSize; /* The following variable can be set to zero to turn off * any optimizations of design rule lists. */ global int DRCRuleOptimization = TRUE; /* The following variables count how many rules were specified by * the technology file and how many edge rules were optimized away. */ static int drcRulesSpecified = 0; static int drcRulesOptimized = 0; /* Rules with unique names are tagged with a reference number */ /* for use in placing errors into the DRC error plane. */ static int DRCtag = 0; /* * Forward declarations. */ int drcWidth(), drcSpacing(), drcEdge(), drcNoOverlap(); int drcExactOverlap(), drcExtend(); int drcSurround(), drcRectOnly(), drcOverhang(); int drcStepSize(), drcOption(), drcOffGrid(); int drcMaxwidth(), drcArea(), drcRectangle(), drcAngles(); int drcCifSetStyle(), drcCifWidth(), drcCifSpacing(); int drcCifMaxwidth(), drcCifArea(); void DRCTechStyleInit(); void drcLoadStyle(); void DRCTechFinal(); void drcTechFinalStyle(); /* * ---------------------------------------------------------------------------- * * Given a TileType bit mask, return the plane mask of planes that are * coincident with all types. This is roughly equivalent to the deprecated * DBTechMinSetPlanes(), but does not attempt to produce a reduced set of * tile types. Since the collective mask of all possible planes is usually * found by a call to DBTechNoisyNameMask, we don't attempt to OR all the * plane masks together, but assume this collective mask is available and * passed as an argument. * * ---------------------------------------------------------------------------- */ PlaneMask CoincidentPlanes(typeMask, pmask) TileTypeBitMask *typeMask; /* Mask of types to check coincidence */ PlaneMask pmask; /* Mask of all possible planes of types */ { PlaneMask planes = pmask; TileType i; /* AND each plane against the collective mask */ for (i = TT_SELECTBASE; i < DBNumTypes; i++) if (TTMaskHasType(typeMask, i)) planes &= DBTypePlaneMaskTbl[i]; return planes; } /* * ---------------------------------------------------------------------------- * * Given a plane mask, return the plane number of the lowest plane in the mask. * * ---------------------------------------------------------------------------- */ int LowestMaskBit(PlaneMask pmask) { PlaneMask pset = pmask; int plane = 0; if (pmask == 0) return DBNumPlanes; while ((pset & 0x1) == 0) { plane++; pset >>= 1; } return plane; } /* * ---------------------------------------------------------------------------- * * DRCPrintStyle -- * * Print the available and/or current extraction styles. * * Results: * None. * * Side effects: * Output. * * ---------------------------------------------------------------------------- */ void DRCPrintStyle(dolist, doforall, docurrent) bool dolist, doforall, docurrent; { DRCKeep *style; if (docurrent) { if (DRCCurStyle == NULL) TxError("Error: No style is set\n"); else { if (!dolist) TxPrintf("The current style is \""); #ifdef MAGIC_WRAPPER if (dolist) Tcl_SetResult(magicinterp, (char *)DRCCurStyle->ds_name, NULL); else #endif TxPrintf("%s", DRCCurStyle->ds_name); if (!dolist) TxPrintf("\".\n"); } } if (doforall) { if (!dolist) TxPrintf("The DRC styles are: "); for (style = DRCStyleList; style; style = style->ds_next) { if (dolist) { #ifdef MAGIC_WRAPPER Tcl_AppendElement(magicinterp, style->ds_name); #else if (style != DRCStyleList) TxPrintf(" "); TxPrintf("%s", style->ds_name); #endif } else { if (style != DRCStyleList) TxPrintf(", "); TxPrintf("%s", style->ds_name); } } if (!dolist) TxPrintf(".\n"); } } /* * ---------------------------------------------------------------------------- * * DRCSetStyle -- * * Set the current DRC style to 'name'. * * Results: * None. * * Side effects: * Output. * * ---------------------------------------------------------------------------- */ void DRCSetStyle(name) char *name; { DRCKeep *style, *match; int length; if (name == NULL) return; match = NULL; length = strlen(name); for (style = DRCStyleList; style; style = style->ds_next) { if (strncmp(name, style->ds_name, length) == 0) { if (match != NULL) { TxError("DRC style \"%s\" is ambiguous.\n", name); DRCPrintStyle(FALSE, TRUE, TRUE); return; } match = style; } } if (match != NULL) { drcLoadStyle(match->ds_name); TxPrintf("DRC style is now \"%s\"\n", name); return; } TxError("\"%s\" is not one of the DRC styles Magic knows.\n", name); DRCPrintStyle(FALSE, TRUE, TRUE); } /* * ---------------------------------------------------------------------------- * * drcTechFreeStyle -- * * This procedure frees all memory associated with a DRC style. * * Results: * None. * * Side effects: * Memory free'd. * * ---------------------------------------------------------------------------- */ void drcTechFreeStyle() { int i, j; char *old; DRCCookie *dp; if (DRCCurStyle != NULL) { /* Remove all old rules from the DRC rules table */ for (i = 0; i < TT_MAXTYPES; i++) for (j = 0; j < TT_MAXTYPES; j++) { dp = DRCCurStyle->DRCRulesTbl[i][j]; while (dp != NULL) { char *old = (char *)dp; dp = dp->drcc_next; freeMagic(old); } } /* Clear the Why string list */ freeMagic(DRCCurStyle->DRCWhyList); freeMagic(DRCCurStyle); DRCCurStyle = NULL; } } /* * ---------------------------------------------------------------------------- * * drcTechNewStyle -- * * This procedure creates a new DRC style at the end of the list * of styles and initializes it to completely null. * * Results: * None. * * Side effects: * A new element is added to the end of DRCStyleList, and DRCCurStyle * is set to point to it. * * ---------------------------------------------------------------------------- */ void drcTechNewStyle() { drcTechFreeStyle(); DRCTechStyleInit(); } /* * ---------------------------------------------------------------------------- * drcWhyCreate -- * * Create a hash entry for the DRC "why" string, if it does not already * exist. * * Returns: * A pointer to the drcWhy structure containing the string and tag. * * Side effects: * Adds to the DRCWhyList if whystring has not been used before. * Calls StrDup() and increments DRCWhySize. DRCWhyList is allocated * in blocks of 50 at a time and only expands when filled. * Temporary hash table DRCWhyErrorTable is used to determine if a * string entry is unique. It is cleared after the technology file * has been processed. * ---------------------------------------------------------------------------- */ int drcWhyCreate(whystring) char *whystring; { HashEntry *he; he = HashLookOnly(&DRCWhyErrorTable, whystring); if (he != NULL) return (int)((pointertype)HashGetValue(he)); /* Grow the list in increments of 50 */ if ((DRCCurStyle->DRCWhySize % 50) == 0) { int i; char **newList; newList = (char **)mallocMagic((DRCCurStyle->DRCWhySize + 51) * sizeof(char *)); newList[0] = (char *)NULL; for (i = 1; i <= DRCCurStyle->DRCWhySize; i++) newList[i] = DRCCurStyle->DRCWhyList[i]; if (DRCCurStyle->DRCWhySize > 0) freeMagic((char *)DRCCurStyle->DRCWhyList); DRCCurStyle->DRCWhyList = newList; } DRCCurStyle->DRCWhySize++; he = HashFind(&DRCWhyErrorTable, whystring); HashSetValue(he, (char *)((pointertype)DRCCurStyle->DRCWhySize)); DRCCurStyle->DRCWhyList[DRCCurStyle->DRCWhySize] = StrDup((char **)NULL, whystring); return DRCCurStyle->DRCWhySize; } /* * ---------------------------------------------------------------------------- * * drcFindBucket -- * * Find the bucket preceding the point where we with to insert a new DRC * cookie. Don't insert a cookie in the middle of a pair of coupled * (trigger + check) rules. * * Results: * Returns a pointer to the location where we want to insert a rule * * Side effects: * None. * * ---------------------------------------------------------------------------- */ DRCCookie * drcFindBucket(i, j, distance) int i, j, distance; { DRCCookie *dp; if (DRCCurStyle == NULL) return NULL; /* find bucket preceding the new one we wish to insert */ for (dp = DRCCurStyle->DRCRulesTbl[i][j]; dp->drcc_next != (DRCCookie *) NULL; dp = dp->drcc_next) { if (dp->drcc_next->drcc_flags & DRC_TRIGGER) { if (dp->drcc_next->drcc_next->drcc_dist >= distance) break; else dp = dp->drcc_next; } else if (dp->drcc_next->drcc_dist >= distance) break; } return dp; } /* * ---------------------------------------------------------------------------- * * drcLoadStyle -- * * Re-read the technology file to load the specified technology DRC style * into structure DRCCurStyle. It incurs a complete reading of the tech * file on startup and every time the extraction style is changed, but we * can assume that this does not happen often. The first style in the * technology file is assumed to be the default, so that re-reading the * tech file is not necessary on startup unless the default DRC style is * changed by a call do "drc style". * * ---------------------------------------------------------------------------- */ void drcLoadStyle(stylename) char *stylename; { SectionID invdrc; if (DRCCurStyle->ds_name == stylename) return; drcTechNewStyle(); /* Reinitialize and mark as not loaded */ DRCCurStyle->ds_name = stylename; invdrc = TechSectionGetMask("drc", NULL); TechLoad(NULL, invdrc); DRCTechScale(DBLambda[0], DBLambda[1]); } /* * ---------------------------------------------------------------------------- * DRCReloadCurStyle --- * * This routine is used by CIFLoadStyle whenever the DRC section makes * reference to CIF layers. * * Results: * None. * * Side Effects: * DRC rule database is deleted and regenerated. * * ---------------------------------------------------------------------------- */ void DRCReloadCurStyle() { char *stylename; DRCKeep * style; if (DRCCurStyle == NULL) return; for (style = DRCStyleList; style != NULL; style = style->ds_next) { if (!strcmp(style->ds_name, DRCCurStyle->ds_name)) { DRCCurStyle->ds_name = NULL; drcLoadStyle(style->ds_name); break; } } } /* * ---------------------------------------------------------------------------- * DRCTechInit -- * * Free and re-initialize the technology-specific variables for the DRC module. * This routine is *only* called upon a "tech load" command, when all existing * DRC data must be cleaned up and free'd. * * Results: * None. * * Side effects: * Clears out all the DRC tables. * ---------------------------------------------------------------------------- */ void DRCTechInit() { DRCKeep *style; /* Clean up any old info */ drcTechFreeStyle(); for (style = DRCStyleList; style != NULL; style = style->ds_next) { freeMagic(style->ds_name); freeMagic(style); } DRCStyleList = NULL; } /* * ---------------------------------------------------------------------------- * DRCTechStyleInit -- * * Initialize the technology-specific variables for the DRC module. * * Results: * None. * * Side effects: * Clears out all the DRC tables. * ---------------------------------------------------------------------------- */ void DRCTechStyleInit() { int i, j, plane; DRCCookie *dp; PaintResultType result; drcRulesOptimized = 0; drcRulesSpecified = 0; if (DRCCurStyle == NULL) { DRCCurStyle = (DRCStyle *) mallocMagic(sizeof(DRCStyle)); DRCCurStyle->ds_name = NULL; } DRCCurStyle->ds_status = TECH_NOT_LOADED; TTMaskZero(&DRCCurStyle->DRCExactOverlapTypes); DRCCurStyle->DRCTechHalo = 0; DRCCurStyle->DRCScaleFactorN = 1; DRCCurStyle->DRCScaleFactorD = 1; DRCCurStyle->DRCStepSize = 0; DRCCurStyle->DRCFlags = (char)0; DRCCurStyle->DRCWhySize = 0; HashInit(&DRCWhyErrorTable, 16, HT_STRINGKEYS); /* First DRC entry is associated with the statically-allocated */ /* drcArrayCookie and has a tag of DRC_ARRAY_OVERLAP_TAG = 1 */ /* (see DRCarray.c). */ drcWhyCreate("This layer can't abut or partially overlap between array elements"); /* Second DRC entry is associated with the statically-allocated */ /* drcOverlapCookie and has a tag of DRC_OVERLAP_TAG = 2 */ /* (see DRCbasic.c). */ drcWhyCreate("Can't overlap those layers"); /* Third DRC entry is associated with the statically-allocated */ /* drcSubcellCookie and has a tag of DRC_SUBCELL_OVERLAP_TAG = 3 */ /* (see DRCsubcell.c). */ drcWhyCreate("This layer can't abut or partially overlap between subcells"); /* Fourth DRC entry is associated with the statically-allocated */ /* drcSubcellCookie and has a tag of DRC_IN_SUBCELL_TAG = 4 */ /* (see DRCsubcell.c). */ drcWhyCreate("See error definition in the subcell"); /* Fifth DRC entry is associated with the statically-allocated */ /* drcOffGridCookie and has a tag of DRC_OFFGRID_TAG = 5 */ /* (see DRCsubcell.c). */ drcWhyCreate("This position does not align with the manufacturing grid"); DRCTechHalo = 0; /* Put a dummy rule at the beginning of the rules table for each entry */ for (i = 0; i < TT_MAXTYPES; i++) { for (j = 0; j < TT_MAXTYPES; j++) { dp = DRCCurStyle->DRCRulesTbl[i][j]; dp = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie))); dp->drcc_dist = -1; dp->drcc_cdist = -1; dp->drcc_next = (DRCCookie *) NULL; TTMaskZero(&dp->drcc_mask); DRCCurStyle->DRCRulesTbl[i][j] = dp; } } /* Copy the default paint table into the DRC paint table. The DRC * paint table will be modified as we read the drc section. Also * make sure that the error layer is super-persistent (once it * appears, it can't be gotten rid of by painting). Also, make * some crossings automatically illegal: two layers can't cross * unless the result of painting one on top of the other is to * get one of the layers, and it doesn't matter which is painted * on top of which. */ for (plane = 0; plane < DBNumPlanes; plane++) for (i = 0; i < DBNumTypes; i++) for (j = 0; j < DBNumTypes; j++) { result = DBPaintResultTbl[plane][i][j]; if ((i == TT_ERROR_S) || (j == TT_ERROR_S)) DRCCurStyle->DRCPaintTable[plane][i][j] = TT_ERROR_S; else if ((i == TT_SPACE) || (j == TT_SPACE) || !DBTypeOnPlane(j, plane) || !DBPaintOnTypePlanes(i, j)) DRCCurStyle->DRCPaintTable[plane][i][j] = result; /* Modified for stackable types (Tim, 10/3/03) */ else if ((i >= DBNumUserLayers) || ((result >= DBNumUserLayers) && (DBTechFindStacking(i, j) == result))) { DRCCurStyle->DRCPaintTable[plane][i][j] = result; } else if ((!TTMaskHasType(&DBLayerTypeMaskTbl[i], result) && !TTMaskHasType(&DBLayerTypeMaskTbl[j], result)) || ((result != DBPaintResultTbl[plane][j][i]) && DBTypeOnPlane(i, plane) && DBPaintOnTypePlanes(j, i))) { DRCCurStyle->DRCPaintTable[plane][i][j] = TT_ERROR_S; /* TxError("Error: %s on %s, was %s\n", DBTypeLongNameTbl[i], DBTypeLongNameTbl[j], DBTypeLongNameTbl[result]); */ } else DRCCurStyle->DRCPaintTable[plane][i][j] = result; } drcCifInit(); } /* * ---------------------------------------------------------------------------- * * DRCTechLine -- * * Parse a line in the DRC section from the technology file. Handle DRC * styles. The rules themselves are handled by DRCTechAddRule. * * Results: * TRUE if line parsed correctly; FALSE if fatal error condition * encountered. * * Side effects: * Appends information to the DRC tables. * * ---------------------------------------------------------------------------- */ bool DRCTechLine(sectionName, argc, argv) char *sectionName; /* The name of this section */ int argc; /* Number of fields on the line */ char *argv[]; /* Values of the fields */ { int j, l; DRCKeep *newStyle, *p; char *tptr, *cptr; if (argc <= 0) return TRUE; else if (argc >= 2) l = strlen(argv[1]); if (strcmp(argv[0], "style") == 0) { if (argc != 2) { if ((argc != 4) || (strncmp(argv[2], "variant", 7))) { wrongNumArgs: TechError("Wrong number of arguments in %s statement.\n", argv[0]); return TRUE; } } for (newStyle = DRCStyleList; newStyle != NULL; newStyle = newStyle->ds_next) { /* Here we're only establishing existence; break on * the first variant found. */ if (!strncmp(newStyle->ds_name, argv[1], l)) break; } if (newStyle == NULL) { if (argc == 2) { newStyle = (DRCKeep *)mallocMagic(sizeof(DRCKeep)); newStyle->ds_next = NULL; newStyle->ds_name = StrDup((char **) NULL, argv[1]); /* Append to end of style list */ if (DRCStyleList == NULL) DRCStyleList = newStyle; else { for (p = DRCStyleList; p->ds_next; p = p->ds_next); p->ds_next = newStyle; } } else /* Handle style variants */ { DRCKeep *saveStyle = NULL; /* 4th argument is a comma-separated list of variants */ tptr = argv[3]; while (*tptr != '\0') { cptr = strchr(tptr, ','); if (cptr != NULL) *cptr = '\0'; newStyle = (DRCKeep *)mallocMagic(sizeof(DRCKeep)); newStyle->ds_next = NULL; newStyle->ds_name = (char *)mallocMagic(l + strlen(tptr) + 1); sprintf(newStyle->ds_name, "%s%s", argv[1], tptr); /* Remember the 1st variant as the default */ if (saveStyle == NULL) saveStyle = newStyle; /* Append to end of style list */ if (DRCStyleList == NULL) DRCStyleList = newStyle; else { for (p = DRCStyleList; p->ds_next; p = p->ds_next); p->ds_next = newStyle; } if (cptr == NULL) break; else tptr = cptr + 1; } newStyle = saveStyle; } } if (DRCCurStyle == NULL) /* Shouldn't happen, but be safe. . .*/ { drcTechNewStyle(); DRCCurStyle->ds_name = newStyle->ds_name; DRCCurStyle->ds_status = TECH_PENDING; } else if ((DRCCurStyle->ds_status == TECH_PENDING) || (DRCCurStyle->ds_status == TECH_SUSPENDED)) DRCCurStyle->ds_status = TECH_LOADED; else if (DRCCurStyle->ds_status == TECH_NOT_LOADED) { if (DRCCurStyle->ds_name == NULL) { DRCCurStyle->ds_name = newStyle->ds_name; DRCCurStyle->ds_status = TECH_PENDING; } else if (argc == 2) { if (!strcmp(argv[1], DRCCurStyle->ds_name)) DRCCurStyle->ds_status = TECH_PENDING; } else if (argc == 4) { /* Verify that the style matches one variant */ if (!strncmp(DRCCurStyle->ds_name, argv[1], l)) { tptr = argv[3]; while (*tptr != '\0') { cptr = strchr(tptr, ','); if (cptr != NULL) *cptr = '\0'; if (!strcmp(DRCCurStyle->ds_name + l, tptr)) { DRCCurStyle->ds_status = TECH_PENDING; return TRUE; } if (cptr == NULL) return TRUE; else tptr = cptr + 1; } } } } return (TRUE); } if (DRCCurStyle == NULL) return FALSE; /* For backwards compatibility, if we have encountered a line that */ /* is not "style" prior to setting a style, then we create a style */ /* called "default". */ if (DRCStyleList == NULL) { char bufargv[2][10]; char *locargv[2]; strcpy(bufargv[0], "style"); strcpy(bufargv[1], "default"); locargv[0] = bufargv[0]; locargv[1] = bufargv[1]; if (DRCTechLine(sectionName, 2, locargv) == FALSE) return FALSE; } else if (DRCStyleList->ds_next == NULL) { /* On reload, if there is only one style, use it. This is */ /* necessary for the DRC-CIF extensions, since even though */ /* the one-and-only DRC section may have been loaded, the DRC- */ /* CIF parts of it become enabled or disabled depending on what */ /* CIFCurStyle is active. */ DRCCurStyle->ds_status = TECH_PENDING; } /* Only continue past this point if we are loading the DRC style */ if ((DRCCurStyle->ds_status != TECH_PENDING) && (DRCCurStyle->ds_status != TECH_SUSPENDED)) return TRUE; /* Process "scalefactor" line next (if any) */ if (strcmp(argv[0], "scalefactor") == 0) { int scaleN, scaleD; if (argc != 2 && argc != 3) goto wrongNumArgs; scaleN = atof(argv[1]); if (argc == 3) scaleD = atof(argv[2]); else scaleD = 1; if (scaleN <= 0 || scaleD <= 0) { TechError("Scale factor must be greater than 0.\n"); TechError("Setting scale factor to default value 1.\n"); DRCCurStyle->DRCScaleFactorN = 1; DRCCurStyle->DRCScaleFactorD = 1; return TRUE; } DRCCurStyle->DRCScaleFactorN = scaleN; DRCCurStyle->DRCScaleFactorD = scaleD; return TRUE; } /* Process "variant" lines next. */ if (strncmp(argv[0], "variant", 7) == 0) { /* If our style variant is not one of the ones declared */ /* on the line, then we ignore all input until we */ /* either reach the end of the style, the end of the */ /* section, or another "variant" line. */ if (argc != 2) goto wrongNumArgs; tptr = argv[1]; while (*tptr != '\0') { cptr = strchr(tptr, ','); if (cptr != NULL) { *cptr = '\0'; for (j = 1; isspace(*(cptr - j)); j++) *(cptr - j) = '\0'; } if (*tptr == '*') { DRCCurStyle->ds_status = TECH_PENDING; return TRUE; } else { l = strlen(DRCCurStyle->ds_name) - strlen(tptr); if (!strcmp(tptr, DRCCurStyle->ds_name + l)) { DRCCurStyle->ds_status = TECH_PENDING; return TRUE; } } if (cptr == NULL) break; else tptr = cptr + 1; } DRCCurStyle->ds_status = TECH_SUSPENDED; } /* Anything below this line is not parsed if we're in TECH_SUSPENDED mode */ if (DRCCurStyle->ds_status != TECH_PENDING) return TRUE; return DRCTechAddRule(sectionName, argc, argv); } void drcCifAssign(cookie, dist, next, mask, corner, tag, cdist, flags, planeto, planefrom) DRCCookie *cookie, *next; int dist, cdist; TileTypeBitMask *mask, *corner; int tag; unsigned short flags; int planeto, planefrom; { (cookie)->drcc_dist = dist; (cookie)->drcc_next = next; (cookie)->drcc_mask = *mask; (cookie)->drcc_corner = *corner; (cookie)->drcc_tag = tag; (cookie)->drcc_cdist = cdist; (cookie)->drcc_flags = flags; (cookie)->drcc_edgeplane = planefrom; (cookie)->drcc_plane = planeto; (cookie)->drcc_mod = 0; (cookie)->drcc_cmod = 0; } // This is like drcCifAssign, but checks for bad plane numbers in planeto and // planefrom void drcAssign(cookie, dist, next, mask, corner, why, cdist, flags, planeto, planefrom) DRCCookie *cookie, *next; int dist, cdist; TileTypeBitMask *mask, *corner; int why; unsigned short flags; int planeto, planefrom; { /* Diagnostic */ if (planeto >= DBNumPlanes) TechError("Bad plane in DRC assignment.\n"); if (planefrom >= DBNumPlanes) TechError("Bad edge plane in DRC assignment.\n"); drcCifAssign(cookie, dist, next, mask, corner, why, cdist, flags, planeto, planefrom); } /* * ---------------------------------------------------------------------------- * * DRCTechAddRule -- * * Add a new entry to the DRC table. * * Results: * Always returns TRUE so that tech file read-in doesn't abort. * * Side effects: * Updates the DRC technology variables. * * Organization: * We select a procedure based on the first keyword (argv[0]) * and call it to do the work of implementing the rule. Each * such procedure is of the following form: * * int * proc(argc, argv) * int argc; * char *argv[]; * { * } * * It returns the distance associated with the design rule, * or -1 in the event of a fatal error that should cause * DRCTechAddRule() to return FALSE (currently, none of them * do, so we always return TRUE). If there is no distance * associated with the design rule, 0 is returned. * * ---------------------------------------------------------------------------- */ /* ARGSUSED */ bool DRCTechAddRule(sectionName, argc, argv) char *sectionName; /* Unused */ int argc; char *argv[]; { int which, distance, mdist; const char *fmt; static const struct { const char *rk_keyword; /* Initial keyword */ int rk_minargs; /* Min # arguments */ int rk_maxargs; /* Max # arguments */ int (*rk_proc)(); /* Procedure implementing this keyword */ const char *rk_err; /* Error message */ } ruleKeys[] = { {"angles", 4, 4, drcAngles, "layers 45|90 why"}, {"edge", 8, 10, drcEdge, "layers1 layers2 distance okTypes cornerTypes cornerDistance [option] why [plane]"}, {"edge4way", 8, 10, drcEdge, "layers1 layers2 distance okTypes cornerTypes cornerDistance [option] why [plane]"}, {"exact_overlap", 2, 2, drcExactOverlap, "layers"}, {"extend", 5, 6, drcExtend, "layers1 layers2 distance [option] why"}, {"no_overlap", 3, 3, drcNoOverlap, "layers1 layers2"}, {"option", 2, 2, drcOption, "option_name option_value"}, {"overhang", 5, 5, drcOverhang, "layers1 layers2 distance why"}, {"rect_only", 3, 3, drcRectOnly, "layers why"}, {"spacing", 6, 7, drcSpacing, "layers1 layers2 separation [layers3] adjacency why"}, {"stepsize", 2, 2, drcStepSize, "step_size"}, {"surround", 6, 7, drcSurround, "layers1 layers2 distance presence why"}, {"width", 4, 5, drcWidth, "layers width why"}, {"widespacing", 7, 8, drcSpacing, "layers1 width layers2 separation adjacency why"}, {"area", 5, 5, drcArea, "layers area horizon why"}, {"off_grid", 4, 4, drcOffGrid, "layers pitch why"}, {"maxwidth", 4, 5, drcMaxwidth, "layers maxwidth bends why"}, {"cifstyle", 2, 2, drcCifSetStyle, "cif_style"}, {"cifwidth", 4, 4, drcCifWidth, "layers width why"}, {"cifspacing", 6, 6, drcCifSpacing, "layers1 layers2 separation adjacency why"}, {"cifarea", 5, 5, drcCifArea, "layers area horizon why"}, {"cifmaxwidth", 5, 5, drcCifMaxwidth, "layers maxwidth bends why"}, {"rectangle", 5, 5, drcRectangle, "layers maxwidth [even|odd|any] why"}, {0} }, *rp; drcRulesSpecified += 1; which = LookupStruct(argv[0], (const LookupTable *) ruleKeys, sizeof ruleKeys[0]); if (which < 0) { TechError("Bad DRC rule type \"%s\"\n", argv[0]); TxError("Valid rule types are:\n"); for (fmt = "%s", rp = ruleKeys; rp->rk_keyword; rp++, fmt = ", %s") TxError(fmt, rp->rk_keyword); TxError(".\n"); return (TRUE); } rp = &ruleKeys[which]; if (argc < rp->rk_minargs || argc > rp->rk_maxargs) { TechError("Rule type \"%s\" usage: %s %s\n", rp->rk_keyword, rp->rk_keyword, rp->rk_err); return (TRUE); } distance = (*rp->rk_proc)(argc, argv); if (distance < 0) return (FALSE); /* Update the halo to be the maximum distance (in magic units) of */ /* any design rule */ mdist = distance; if (mdist > DRCTechHalo) DRCTechHalo = mdist; return (TRUE); } /* * ---------------------------------------------------------------------------- * * drcExtend -- * * Process an extension rule. * This is of the form: * * extend layers1 layers2 distance [exact_width | exclusive] why * * indicating that if "layers1" extends from "layers2", then it must * have a width of at least "distance". This is very much like the * "overhang" rule, except that the extension is optional. Example: * * extend nfet *poly 3 "n-transistor width must be at least 3" * * "extend" implements the following general-purpose edge rule: * * edge4way layers2 layers1 distance layers1 0 0 why * * Option "exact_width" implements an additional rule that checks for * maximum extension at distance. This is intended for use with, for * example, a fixed gate length for a specific type of device. * * Option "exclusive" is for use for, e.g., transistor length when the * length rule is larger than the minimum width rule. However, without * the option, "extend" sets OKtypes to the union of layers1 and layers2; * that covers, e.g., extension of diffusion from a gate where an * additional gate within the distance does not violate the rule. With * "exclusive", layers2 may not appear within "distance" of the edge. * * Results: * Returns distance. * * Side effects: * Updates the DRC technology variables. * * ---------------------------------------------------------------------------- */ int drcExtend(argc, argv) int argc; char *argv[]; { char *layers1 = argv[1]; char *layers2 = argv[2]; int distance = atoi(argv[3]); int why; TileTypeBitMask set1, setC; DRCCookie *dp, *dpnew, *dptrig; TileType i, j; int plane, plane2; TileTypeBitMask set2, setZ, setN, setM; PlaneMask pMask1, pMask2, pset, ptest; bool exact = FALSE; bool exclusive = FALSE; if (!strncmp(argv[4], "exact_", 6)) { exact = TRUE; why = drcWhyCreate(argv[5]); } else if (!strncmp(argv[4], "exclu", 5)) { exclusive = TRUE; why = drcWhyCreate(argv[5]); } else why = drcWhyCreate(argv[4]); ptest = DBTechNoisyNameMask(layers1, &set1); pMask1 = CoincidentPlanes(&set1, ptest); if (pMask1 == 0) { TechError("All layers in first set for \"extend\" must be on " "the same plane\n"); return (0); } ptest = DBTechNoisyNameMask(layers2, &set2); pMask2 = CoincidentPlanes(&set2, ptest); if (pMask2 == 0) { TechError("All layers in second set for \"extend\" must be on " "the same plane\n"); return (0); } /* setM is the union of set1 and set2 unless exclusive == TRUE */ TTMaskZero(&setM); if (exclusive) TTMaskSetMask(&setM, &set1); else TTMaskSetMask3(&setM, &set1, &set2); /* setN is the inverse of set1, and setC is the inverse of set2 */ TTMaskCom2(&setN, &set1); TTMaskCom2(&setC, &set2); /* Zero mask */ TTMaskZero(&setZ); for (i = 0; i < DBNumTypes; i++) { for (j = 0; j < DBNumTypes; j++) { if (i == j) continue; if ((pset = (DBTypesOnSamePlane(i, j) & pMask2))) { /* Edge depends on whether or not the extension is */ /* on the same plane as the layer from which it is */ /* measured. */ if ((pset & pMask1) != 0) { if (TTMaskHasType(&set2, i) && TTMaskHasType(&set1, j)) { plane = LowestMaskBit(pset & pMask1); /* find bucket preceding the new one we wish to insert */ dp = drcFindBucket(i, j, distance); dpnew = (DRCCookie *)mallocMagic(sizeof(DRCCookie)); drcAssign(dpnew, distance, dp->drcc_next, &setM, &setZ, why, 0, DRC_FORWARD, plane, plane); dp->drcc_next = dpnew; dp = drcFindBucket(j, i, distance); dpnew = (DRCCookie *)mallocMagic(sizeof(DRCCookie)); drcAssign(dpnew, distance, dp->drcc_next, &setM, &setZ, why, 0, DRC_REVERSE, plane, plane); dp->drcc_next = dpnew; if (exact) { /* find bucket preceding the new one we wish to insert */ dp = drcFindBucket(i, j, distance); dpnew = (DRCCookie *)mallocMagic(sizeof(DRCCookie)); drcAssign(dpnew, distance, dp->drcc_next, &setN, &setZ, why, 0, DRC_FORWARD | DRC_OUTSIDE, plane, plane); dp->drcc_next = dpnew; dp = drcFindBucket(j, i, distance); dpnew = (DRCCookie *)mallocMagic(sizeof(DRCCookie)); drcAssign(dpnew, distance, dp->drcc_next, &setN, &setZ, why, 0, DRC_REVERSE | DRC_OUTSIDE, plane, plane); dp->drcc_next = dpnew; } } } else /* Multi-plane extend rule */ { if (TTMaskHasType(&set2, i) && TTMaskHasType(&setC, j)) { plane = LowestMaskBit(pset); plane2 = LowestMaskBit(pMask1); /* find bucket preceding the new one we wish to insert */ dp = drcFindBucket(i, j, distance); dpnew = (DRCCookie *)mallocMagic(sizeof(DRCCookie)); drcAssign(dpnew, distance, dp->drcc_next, &setM, &setZ, why, 0, DRC_FORWARD, plane2, plane); dptrig = (DRCCookie *)mallocMagic(sizeof(DRCCookie)); drcAssign(dptrig, 1, dpnew, &setN, &setZ, why, 0, DRC_FORWARD | DRC_TRIGGER, plane2, plane); dp->drcc_next = dptrig; dp = drcFindBucket(j, i, distance); dpnew = (DRCCookie *)mallocMagic(sizeof(DRCCookie)); drcAssign(dpnew, distance, dp->drcc_next, &setM, &setZ, why, 0, DRC_REVERSE, plane2, plane); dptrig = (DRCCookie *)mallocMagic(sizeof(DRCCookie)); drcAssign(dptrig, 1, dpnew, &setN, &setZ, why, 0, DRC_REVERSE | DRC_TRIGGER, plane2, plane); dp->drcc_next = dptrig; } } } } } return (distance); } /* * ---------------------------------------------------------------------------- * * drcWidth -- * * Process a width rule. * This is of the form: * * width layers distance [angles] why * * e.g, * * width poly,pmc 2 "poly width must be at least 2" * * The option "angles" is computed only on non-manhattan edges and is used * in cases were, for example, an angled transistor has a minimum width * larger than that of a straight gate. * * Results: * Returns distance. * * Side effects: * Updates the DRC technology variables. * * ---------------------------------------------------------------------------- */ int drcWidth(argc, argv) int argc; char *argv[]; { char *layers = argv[1]; int distance = atoi(argv[2]); int why; TileTypeBitMask set, setC; PlaneMask pmask, pset, ptest; DRCCookie *dp, *dpnew; TileType i, j; int plane, flags = 0; if (!strncmp(argv[3], "angle", 5)) { flags = DRC_SPLITTILE; why = drcWhyCreate(argv[4]); } else why = drcWhyCreate(argv[3]); ptest = DBTechNoisyNameMask(layers, &set); pmask = CoincidentPlanes(&set, ptest); TTMaskCom2(&setC, &set); if (pmask == 0) { TechError("All layers for \"width\" must be on same plane\n"); return (0); } for (i = 0; i < DBNumTypes; i++) { for (j = 0; j < DBNumTypes; j++) { if (i == j) continue; /* * Must have types in 'set' for at least 'distance' * to the right of any edge between a type in '~set' * and a type in 'set'. */ if ((pset = (DBTypesOnSamePlane(i, j) & pmask))) { if (TTMaskHasType(&setC, i) && TTMaskHasType(&set, j)) { plane = LowestMaskBit(pset); /* Find bucket preceding the new one we wish to insert */ dp = drcFindBucket(i, j, distance); dpnew = (DRCCookie *)mallocMagic(sizeof (DRCCookie)); drcAssign(dpnew, distance, dp->drcc_next, &set, &set, why, distance, DRC_FORWARD | flags, plane, plane); dp->drcc_next = dpnew; } /* Width checks are symmetric and so need to be checked */ /* in only one direction. However, split tiles are */ /* not symmetric, so apply the reverse case with a flag */ /* that will allow the rule to be applied only in the */ /* case of a split tile. */ if (TTMaskHasType(&set, i) && TTMaskHasType(&setC, j)) { plane = LowestMaskBit(pset); /* Find bucket preceding the new one we wish to insert */ dp = drcFindBucket(i, j, distance); dpnew = (DRCCookie *)mallocMagic(sizeof (DRCCookie)); drcAssign(dpnew, distance, dp->drcc_next, &set, &set, why, distance, DRC_REVERSE | DRC_SPLITTILE, plane, plane); dp->drcc_next = dpnew; } } } } return (distance); } /* * ---------------------------------------------------------------------------- * * drcArea -- * * Process an area rule. * This is of the form: * * area layers area horizon why * * e.g, * * area pmc 4 2 "poly contact area must be at least 4" * * "area" is the total area in lambda^2. * * "horizon" is the halo distance for the check. Normally, this would * be the area (in lambda^2) divided by the minimum width rule (in * lambda). Anything larger would not be a violation as long as the * minimum width rule is satisfied. * * Results: * Returns distance. * * Side effects: * Updates the DRC technology variables. * * ---------------------------------------------------------------------------- */ int drcArea(argc, argv) int argc; char *argv[]; { char *layers = argv[1]; int distance = atoi(argv[2]); int horizon = atoi(argv[3]); int why = drcWhyCreate(argv[4]); TileTypeBitMask set, setC; DRCCookie *dp, *dpnew; TileType i, j; PlaneMask pmask, ptest, pset; int plane; ptest = DBTechNoisyNameMask(layers, &set); pmask = CoincidentPlanes(&set, ptest); TTMaskCom2(&setC, &set); if (pmask == 0) { TechError("All layers for \"area\" must be on same plane\n"); return (0); } for (i = 0; i < DBNumTypes; i++) { for (j = 0; j < DBNumTypes; j++) { if (i == j) continue; /* * Must have types in 'set' for at least 'distance' * to the right of any edge between a type in '~set' * and a type in 'set'. */ if ((pset = (DBTypesOnSamePlane(i, j) & pmask))) { if (TTMaskHasType(&setC, i) && TTMaskHasType(&set, j)) { plane = LowestMaskBit(pset); /* find bucket preceding the new one we wish to insert */ dp = drcFindBucket(i, j, horizon); dpnew = (DRCCookie *) mallocMagic(sizeof (DRCCookie)); drcAssign(dpnew, horizon, dp->drcc_next, &set, &set, why, distance, DRC_AREA|DRC_FORWARD, plane, plane); dp->drcc_next = dpnew; } } } } return (horizon); } /* * ---------------------------------------------------------------------------- * * drcOffGrid -- * * Process an off-grid rule. * This is of the form: * * off_grid layers pitch why * * e.g, * * off_grid m1 5 "metal shapes must be on %d grid" * * "pitch" is the grid pitch that shapes must be aligned to. * * Results: * Returns pitch (the halo for detecting off-grid errors). * * Side effects: * Updates the DRC technology variables. * * ---------------------------------------------------------------------------- */ int drcOffGrid(argc, argv) int argc; char *argv[]; { char *layers = argv[1]; int pitch = atoi(argv[2]); int why = drcWhyCreate(argv[3]); TileTypeBitMask set, setC; DRCCookie *dp, *dpnew; TileType i, j; PlaneMask pset; int plane; DBTechNoisyNameMask(layers, &set); TTMaskCom2(&setC, &set); for (i = 0; i < DBNumTypes; i++) { for (j = 0; j < DBNumTypes; j++) { if (i == j) continue; /* * Must have types in 'set' for at least 'distance' * to the right of any edge between a type in '~set' * and a type in 'set'. */ if ((pset = (DBTypesOnSamePlane(i, j)))) { if (TTMaskHasType(&setC, i) && TTMaskHasType(&set, j)) { plane = LowestMaskBit(pset); /* find bucket preceding the new one we wish to insert */ dp = drcFindBucket(i, j, pitch); dpnew = (DRCCookie *) mallocMagic(sizeof (DRCCookie)); drcAssign(dpnew, pitch, dp->drcc_next, &set, &set, why, 0, DRC_OFFGRID|DRC_FORWARD, plane, plane); dp->drcc_next = dpnew; /* opposite edge also needs to be checked */ dp = drcFindBucket(j, i, pitch); dpnew = (DRCCookie *) mallocMagic(sizeof (DRCCookie)); drcAssign(dpnew, pitch, dp->drcc_next, &set, &set, why, 0, DRC_OFFGRID|DRC_REVERSE, plane, plane); dp->drcc_next = dpnew; } } } } return (pitch); } /* * ---------------------------------------------------------------------------- * * drcMaxwidth -- * * Process a maxwidth rule. * This is of the form: * * maxwidth layers distance [bends] why * * This routine was updated 3/6/05 to match the "canonical" definition of * a maxwidth region, which is any rectangle containing that is * at least in both width and height. If the keyword * "bend_illegal" is present, then the definition reverts to the original * (see below) for backwards-compatibility. Otherwise ("bend_ok" or * nothing), the new routine is used. * * maxwidth metal1 389 "metal1 width > 35um must be slotted" * maxwidth pmc 4 bend_illegal "poly contact area must be no wider than 4" * maxwidth trench 4 bend_ok "trench width must be exactly 4" * * bend_illegal - means that one_dimension must be distance for any * point in the region. This is used for emitters and contacts * that are rectangular (so we can't generate them with the * squares command) and some exact width in one direction. * bend_ok - Used mainly for wide metal rules where metal greater than * some given width must be slotted. Also, used for things * like trench, where the width is some fixed value: * both - implies bend_illegal and both directions are checked * * XXXXX XXXXXX * X X XXXXXX * X X X X * XXXXX XXXXXX * * OK BAD * * Results: * Returns distance. * * Side effects: * Updates the DRC technology variables. * * ---------------------------------------------------------------------------- */ int drcMaxwidth(argc, argv) int argc; char *argv[]; { char *layers = argv[1]; int distance = atoi(argv[2]); char *bends = argv[3]; int why; TileTypeBitMask set, setC; DRCCookie *dp, *dpnew; TileType i, j; PlaneMask pmask, ptest, pset; int plane; int bend; ptest = DBTechNoisyNameMask(layers, &set); pmask = CoincidentPlanes(&set, ptest); TTMaskCom2(&setC, &set); if (pmask == 0) { TechError("All layers for \"maxwidth\" must be on same plane\n"); return (0); } if (argc == 4) { /* "bends" is irrelevent if distance is zero, so choose the */ /* faster algorithm to process */ if (distance == 0) bend = 0; else bend = DRC_BENDS; why = drcWhyCreate(argv[3]); } else { if (strcmp(bends,"bend_illegal") == 0) bend = 0; else if (strcmp(bends,"bend_ok") == 0) bend = DRC_BENDS; else if (strcmp(bends,"both") == 0) bend = DRC_MAXWIDTH_BOTH; else { TechError("unknown bend option %s\n",bends); return (0); } why = drcWhyCreate(argv[4]); } for (i = 0; i < DBNumTypes; i++) { for (j = 0; j < DBNumTypes; j++) { if (i == j) continue; /* * Must have types in 'set' for at least 'distance' * to the right of any edge between a type in '~set' * and a type in 'set'. */ if ((pset = (DBTypesOnSamePlane(i, j) & pmask))) { if (TTMaskHasType(&setC, i) && TTMaskHasType(&set, j)) { plane = LowestMaskBit(pset); /* find bucket preceding the new one we wish to insert */ dp = drcFindBucket(i, j, distance); dpnew = (DRCCookie *) mallocMagic(sizeof (DRCCookie)); drcAssign(dpnew, distance, dp->drcc_next, &set, &set, why, distance, DRC_MAXWIDTH | bend, plane, plane); dp->drcc_next = dpnew; } } } } return (distance); } /* * ---------------------------------------------------------------------------- * drcAngles -- * * Process an "angles" rule specifying that geometry for a certain layer * must be limited to 90 degrees or 45 degrees. If not specified, any * angle is allowed, although width rules will flag errors on acute angles. * * For all types in "layers" (argv[1]), edges between types on the same * plane are considered false edges (e.g., if poly is 90-degrees only, * then a transistor-to-poly edge is not considered because it represents * an angled edge on diffusion, not poly, assuming that both poly and * transistor types appear in the layers list). * * ---------------------------------------------------------------------------- */ int drcAngles(argc, argv) int argc; char *argv[]; { char *layers = argv[1]; char *endptr; long value; int angles; int why = drcWhyCreate(argv[3]); TileTypeBitMask set; DRCCookie *dp, *dpnew; int plane; PlaneMask ptest, pset, pmask; TileType i, j; value = strtol(argv[2], &endptr, 10); switch (value) { case 45: angles = DRC_ANGLES_45 | DRC_ANGLES_90; break; case 90: angles = DRC_ANGLES_90; break; default: TechError("angles must be 45 or 90\n"); return 0; } if (endptr != NULL) if (!strcmp(endptr + 1, "only")) if (angles & DRC_ANGLES_45) angles = DRC_ANGLES_45; /* 45 degrees only */ ptest = DBTechNoisyNameMask(layers, &set); /* The 45 degrees only case is implemented like drcRectOnly, except */ /* that drcRectOnly checks only for inside corners, while this */ /* also checks for outside corners. Where inside and outside */ /* corners have been beveled, the error is discarded while */ /* processing. */ if (angles == DRC_ANGLES_45) { TileTypeBitMask set2, setC, setM; pmask = CoincidentPlanes(&set, ptest); if (pmask == 0) { TechError("All types for \"angles\" must be on the same plane.\n"); return 0; } /* set2 is the inverse of set */ TTMaskCom2(&set2, &set); for (i = 0; i < DBNumTypes; i++) { for (j = 0; j < DBNumTypes; j++) { if (i == j) continue; if ((pset = (DBTypesOnSamePlane(i, j) & pmask))) { /* Inside corners */ if (TTMaskHasType(&set, i) && TTMaskHasType(&set2, j)) { plane = LowestMaskBit(pset); /* setC = all types in plane */ TTMaskZero(&setC); TTMaskSetMask(&setC, &DBPlaneTypes[plane]); /* Find bucket preceding the new one we wish to insert */ dp = drcFindBucket(i, j, 1); dpnew = (DRCCookie *)mallocMagic(sizeof (DRCCookie)); drcAssign(dpnew, 1, dp->drcc_next, &set2, &setC, why, 1, angles | DRC_FORWARD | DRC_BOTHCORNERS, plane, plane); dp->drcc_next = dpnew; /* find bucket preceding new one we wish to insert */ dp = drcFindBucket(j, i, 1); dpnew = (DRCCookie *)mallocMagic(sizeof (DRCCookie)); drcAssign(dpnew, 1, dp->drcc_next, &set2, &setC, why, 1, angles | DRC_REVERSE | DRC_BOTHCORNERS, plane, plane); dp->drcc_next = dpnew; } /* Outside corners */ if (TTMaskHasType(&set2, i) && TTMaskHasType(&set, j)) { plane = LowestMaskBit(pset); /* setC = all types in plane */ TTMaskZero(&setC); TTMaskSetMask(&setC, &DBPlaneTypes[plane]); /* setM = not(i) */ TTMaskSetOnlyType(&setM, i); TTMaskCom(&setM); /* Find bucket preceding the new one we wish to insert */ dp = drcFindBucket(i, j, 1); dpnew = (DRCCookie *)mallocMagic(sizeof (DRCCookie)); drcAssign(dpnew, 1, dp->drcc_next, &setM, &setC, why, 1, angles | DRC_FORWARD | DRC_BOTHCORNERS, plane, plane); dp->drcc_next = dpnew; /* find bucket preceding new one we wish to insert */ dp = drcFindBucket(j, i, 1); dpnew = (DRCCookie *)mallocMagic(sizeof (DRCCookie)); drcAssign(dpnew, 1, dp->drcc_next, &setM, &setC, why, 1, angles | DRC_REVERSE | DRC_BOTHCORNERS, plane, plane); dp->drcc_next = dpnew; } } } } } for (i = 0; i < DBNumTypes; i++) { if (TTMaskHasType(&set, i)) { plane = DBPlane(i); /* Insert rule at boundary of type and all other types */ /* in the same plane that do not belong to the layer list. */ for (j = 0; j < DBNumTypes; j++) { if (i == j) continue; if ((j != TT_SPACE) && (DBPlane(j) != plane)) continue; if (!TTMaskHasType(&set, j)) { dp = drcFindBucket(j, i, 1); dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie))); drcAssign(dpnew, 1, dp->drcc_next, &set, &set, why, 1, angles, plane, plane); dp->drcc_next = dpnew; dp = drcFindBucket(i, j, 1); dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie))); drcAssign(dpnew, 1, dp->drcc_next, &set, &set, why, 1, angles | DRC_REVERSE, plane, plane); dp->drcc_next = dpnew; } } } } return 1; } /* * ---------------------------------------------------------------------------- * * drcSpacing3 -- * * Process a special spacing rule of the form: * * spacing layers1 layers2 distance corner_ok layers3 why * * This spacing rule is not checked when a type from "layers3" fills the * corner between "layers1" and "layers2". This is used, e.g., for * diffusion-to-poly spacing, where diffusion and poly may touch at the * corner of a FET type. It is equivalent to: * * edge4way layers1 ~(layers3,layers1) distance ~(layers2) 0 0 why * * The complementary case is redundant, so not checked. * * ---------------------------------------------------------------------------- */ int drcSpacing3(argc, argv) int argc; char *argv[]; { char *layers1 = argv[1], *layers2 = argv[2]; char *layers3 = argv[5]; int distance = atoi(argv[3]); char *adjacency = argv[4]; int why = drcWhyCreate(argv[6]); TileTypeBitMask set1, set2, set3; int plane; DRCCookie *dp, *dpnew; TileType i, j; PlaneMask pmask, pset, ptest; ptest = DBTechNoisyNameMask(layers1, &set1); pmask = CoincidentPlanes(&set1, ptest); ptest = DBTechNoisyNameMask(layers2, &set2); pmask &= CoincidentPlanes(&set2, ptest); ptest = DBTechNoisyNameMask(layers3, &set3); pmask &= CoincidentPlanes(&set3, ptest); if (pmask == 0) { TechError("Spacing check with \"corner_ok\" must have" " all types in one plane.\n"); return (0); } /* In this usage everything must fall in the same plane. */ /* We need masks for (~types2)/plane and for (~(types1,types3))/plane */ TTMaskCom(&set2); TTMaskSetMask(&set3, &set1); TTMaskCom(&set3); for (i = 0; i < DBNumTypes; i++) { for (j = 0; j < DBNumTypes; j++) { if (i == j) continue; if ((pset = (DBTypesOnSamePlane(i, j) & pmask))) { if (TTMaskHasType(&set1, i) && TTMaskHasType(&set3, j)) { plane = LowestMaskBit(pset); /* Find bucket preceding the new one we wish to insert */ dp = drcFindBucket(i, j, distance); dpnew = (DRCCookie *)mallocMagic(sizeof (DRCCookie)); drcAssign(dpnew, distance, dp->drcc_next, &set2, &set3, why, distance, DRC_FORWARD | DRC_BOTHCORNERS, plane, plane); dp->drcc_next = dpnew; /* find bucket preceding new one we wish to insert */ dp = drcFindBucket(j, i, distance); dpnew = (DRCCookie *)mallocMagic(sizeof (DRCCookie)); drcAssign(dpnew, distance, dp->drcc_next, &set2, &set3, why, distance, DRC_REVERSE | DRC_BOTHCORNERS, plane, plane); dp->drcc_next = dpnew; } } } } return distance; } /* *------------------------------------------------------------------- * * drcMaskSpacing --- * * This is the core of the drcSpacing routine. When the spacing * rule layers independently cover more than one plane, this routine * is invoked for each independent plane. * * Results: * Returns the rule's maximum distance * * Side effects: * Adds rules to the DRC rule table. * * Notes: * 8/13/2025: Added "manhattan_dist" as an option indicating * that corner checks should assume manhattan distance. Otherwise * the rule type is the same as "touching_illegal". *------------------------------------------------------------------- */ int drcMaskSpacing(set1, set2, pmask1, pmask2, wwidth, distance, adjacency, why, widerule, runlength, multiplane) TileTypeBitMask *set1, *set2; PlaneMask pmask1, pmask2; int wwidth, distance; char *adjacency; int why; bool widerule; int runlength; bool multiplane; { TileTypeBitMask tmp1, tmp2, setR, setRreverse; int plane, plane2; PlaneMask pset, ptest; DRCCookie *dp, *dpnew; int needReverse = (widerule) ? TRUE : FALSE; TileType i, j, pref; bool needtrigger = FALSE; bool touchingok = TRUE; bool cornerok = FALSE; bool surroundok = FALSE; unsigned short flags = 0; if (!strcmp(adjacency, "surround_ok")) { if (multiplane) { TechError("\"surround_ok\" requires surrounding types to " "be in the same plane.\n"); return (0); } else if ((pmask1 & pmask2) == 0) { /* New rule implementation (7/8/06): When types */ /* are on different planes and "surround_ok" is */ /* declared, implement as a triggered rule (but not */ /* for widespacing, which already uses the */ /* triggering rule mechanism). */ if (!widerule) { needtrigger = TRUE; touchingok = FALSE; needReverse = TRUE; surroundok = TRUE; } else { TechError("Widespacing checks cannot use \"surround_ok\".\n"); return (0); } } else { TechError("\"surround_ok\" used when spacing rule types are in " "the same plane. Did you mean \"touching_ok\"?\n"); touchingok = TRUE; /* Treat like "touching_ok" */ } } else if (!strcmp(adjacency, "touching_ok")) { /* If touching is OK, everything must fall in the same plane. */ if (multiplane || ((pmask1 & pmask2) == 0)) { TechError("Spacing check with \"touching_ok\" must have" " all types in one plane. Possibly you want" " \"surround_ok\"?\n"); return (0); } else touchingok = TRUE; } else if (!strcmp(adjacency, "touching_illegal")) { touchingok = FALSE; needtrigger = FALSE; } else if (!strcmp(adjacency, "manhattan_dist")) { touchingok = FALSE; needtrigger = FALSE; flags = DRC_MANHATTAN; } else { TechError("Badly formed drc spacing line: need \"touching_ok\", " "\"touching_illegal\", or \"surround_ok\".\n"); return (0); } if (touchingok) { /* In "touching_ok rules, spacing to set2 is be checked in FORWARD * direction at edges between set1 and (setR = ~set1 AND ~set2). * * In addition, spacing to set1 is checked in FORWARD direction * at edges between set2 and (setRreverse = ~set1 AND ~set2). * * If set1 and set2 are different, above are checked in REVERSE as * well as forward direction. This is important since touching * material frequently masks violations in one direction. * * setR and setRreverse are set appropriately below. */ tmp1 = *set1; tmp2 = *set2; /* Restrict planes to those that are coincident */ pmask1 &= pmask2; pmask2 = pmask1; TTMaskCom(&tmp1); TTMaskCom(&tmp2); TTMaskAndMask(&tmp1, &tmp2); setR = tmp1; setRreverse = tmp1; /* If set1 != set2, set flag to check rules in both directions */ if (!TTMaskEqual(set1, set2)) needReverse = TRUE; } else { /* In "touching_illegal" rules, spacing to set2 will be checked * in FORWARD direction at edges between set1 and (setR=~set1). * * In addition, spacing to set1 will be checked in FORWARD direction * at edges between set2 and (setRreverse= ~set2). * * setR and setRreverse are set appropriately below. */ TTMaskCom2(&setR, set1); TTMaskCom2(&setRreverse, set2); } for (i = 0; i < DBNumTypes; i++) { for (j = 0; j < DBNumTypes; j++) { if (i == j) continue; if ((pset = (DBTypesOnSamePlane(i, j) & pmask1))) { plane = LowestMaskBit(pset); /* LHS is an element of set1, RHS is an element of setR */ if (TTMaskHasType(set1, i) && TTMaskHasType(&setR, j)) { plane2 = LowestMaskBit(pmask2); /* * Must not have 'set2' for 'distance' to the right of * an edge between 'set1' and the types not in 'set1' * (touching_illegal case) or in neither * 'set1' nor 'set2' (touching_ok case). */ /* Find bucket preceding the new one we wish to insert */ dp = drcFindBucket(i, j, distance); dpnew = (DRCCookie *)mallocMagic(sizeof(DRCCookie)); TTMaskClearMask3(&tmp1, &DBPlaneTypes[plane2], set2); TTMaskAndMask3(&tmp2, &DBPlaneTypes[plane], &setR); if (widerule) { DRCCookie *dptrig; /* Create two contiguous rules, one for spacing */ /* and one for width. These are created in */ /* reverse order due to the stack property of */ /* the linked list. */ if (runlength > 0) { drcAssign(dpnew, distance, dp->drcc_next, &tmp1, &tmp2, why, runlength, DRC_RUNLENGTH | DRC_FORWARD, plane2, plane); dptrig = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie))); drcAssign(dptrig, wwidth, dpnew, set1, set1, why, runlength, DRC_REVERSE | DRC_MAXWIDTH | DRC_TRIGGER | DRC_BENDS, plane2, plane); } else { drcAssign(dpnew, distance, dp->drcc_next, &tmp1, &tmp2, why, distance, DRC_FORWARD, plane2, plane); dptrig = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie))); drcAssign(dptrig, wwidth, dpnew, set1, set1, why, distance, DRC_REVERSE | DRC_MAXWIDTH | DRC_TRIGGER | DRC_BENDS, plane2, plane); } dp->drcc_next = dptrig; } else if (needtrigger) { DRCCookie *dptrig; /* Create two contiguous spacing rules */ drcAssign(dpnew, distance, dp->drcc_next, &tmp1, &tmp2, why, wwidth, DRC_FORWARD | DRC_BOTHCORNERS, plane2, plane); dptrig = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie))); drcAssign(dptrig, 1, dpnew, set2, &tmp2, why, 1, DRC_FORWARD | DRC_TRIGGER, plane2, plane); dp->drcc_next = dptrig; } else { drcAssign(dpnew, distance, dp->drcc_next, &tmp1, &tmp2, why, wwidth, DRC_FORWARD | flags, plane2, plane); dp->drcc_next = dpnew; } if (needReverse) dpnew->drcc_flags |= DRC_BOTHCORNERS; if (needReverse) { /* Add check in reverse direction, * NOTE: am assuming single plane rule here (since reverse * rules only used with touching_ok which must be * single plane) */ /* find bucket preceding new one we wish to insert */ dp = drcFindBucket(j, i, distance); dpnew = (DRCCookie *)mallocMagic(sizeof (DRCCookie)); if (widerule) { DRCCookie *dptrig; /* Assign two coupled rules (see above) */ if (runlength > 0) { drcAssign(dpnew, distance, dp->drcc_next, &tmp1, &tmp2, why, runlength, DRC_RUNLENGTH | DRC_REVERSE | DRC_BOTHCORNERS, plane2, plane); dptrig = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie))); drcAssign(dptrig, wwidth, dpnew, set1, set1, why, runlength, DRC_FORWARD | DRC_MAXWIDTH | DRC_TRIGGER | DRC_BENDS, plane2, plane); } else { drcAssign(dpnew, distance, dp->drcc_next, &tmp1, &tmp2, why, distance, DRC_REVERSE | DRC_BOTHCORNERS, plane2, plane); dptrig = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie))); drcAssign(dptrig, wwidth, dpnew, set1, set1, why, distance, DRC_FORWARD | DRC_MAXWIDTH | DRC_TRIGGER | DRC_BENDS, plane2, plane); } dp->drcc_next = dptrig; } else if (needtrigger) { DRCCookie *dptrig; /* Create two contiguous spacing rules */ drcAssign(dpnew, distance, dp->drcc_next, &tmp1, &tmp2, why, wwidth, DRC_REVERSE | DRC_BOTHCORNERS, plane2, plane); dptrig = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie))); drcAssign(dptrig, 1, dpnew, set2, &tmp2, why, 1, DRC_REVERSE | DRC_TRIGGER, plane2, plane); dp->drcc_next = dptrig; } else { drcAssign(dpnew,distance,dp->drcc_next, &tmp1, &tmp2, why, wwidth, DRC_REVERSE | DRC_BOTHCORNERS | flags, plane2, plane); dp->drcc_next = dpnew; } } } } if (TTMaskEqual(set1, set2)) continue; if (widerule) continue; /* Can't determine width of set1 */ /* when looking at the edge of set2 */ /* * Now, if set1 and set2 are distinct apply the rule for LHS in set1 * and RHS in set2. HOWEVER, "surround_ok" rules are asymmetrically * triggered and cannot be reversed between set1 and set2. */ if ((!surroundok) && (pset = (DBTypesOnSamePlane(i, j) & pmask2))) { plane = LowestMaskBit(pset); /* LHS is an element of set2, RHS is an element of setRreverse */ if (TTMaskHasType(set2, i) && TTMaskHasType(&setRreverse, j)) { plane2 = LowestMaskBit(pmask1); /* Find bucket preceding the new one we wish to insert */ dp = drcFindBucket(i, j, distance); dpnew = (DRCCookie *)mallocMagic(sizeof (DRCCookie)); TTMaskClearMask3(&tmp1, &DBPlaneTypes[plane2], set1); TTMaskAndMask3(&tmp2, &DBPlaneTypes[plane], &setRreverse); /* NOTE: This is needed for some situation, but I */ /* do not recall the exact nature of it. In other */ /* cases only the simple rule check is needed. */ if (needtrigger) { DRCCookie *dptrig; /* Create two contiguous spacing rules */ drcAssign(dpnew, distance, dp->drcc_next, &tmp1, &tmp2, why, distance, DRC_FORWARD | DRC_BOTHCORNERS, plane2, plane); dptrig = (DRCCookie *)mallocMagic(sizeof (DRCCookie)); drcAssign(dptrig, 1, dpnew, set1, &tmp2, why, 1, DRC_FORWARD | DRC_TRIGGER, plane2, plane); dp->drcc_next = dptrig; } else { drcAssign(dpnew, distance, dp->drcc_next, &tmp1, &tmp2, why, distance, DRC_FORWARD | flags, plane2, plane); dp->drcc_next = dpnew; } if (needReverse) dpnew->drcc_flags |= DRC_BOTHCORNERS; if (needReverse) { /* Add check in reverse direction, * NOTE: am assuming single plane rule here (since reverse * rules only used with touching_ok which must be * single plane) */ /* find bucket preceding new one we wish to insert */ dp = drcFindBucket(j, i, distance); dpnew = (DRCCookie *) mallocMagic(sizeof (DRCCookie)); /* See above */ if (needtrigger) { DRCCookie *dptrig; /* Create two contiguous spacing rules */ drcAssign(dpnew, distance, dp->drcc_next, &tmp1, &tmp2, why, distance, DRC_REVERSE | DRC_BOTHCORNERS, plane2, plane); dptrig = (DRCCookie *)mallocMagic(sizeof (DRCCookie)); drcAssign(dptrig, 1, dpnew, set1, &tmp2, why, 1, DRC_REVERSE | DRC_TRIGGER, plane2, plane); dp->drcc_next = dptrig; } else { drcAssign(dpnew, distance, dp->drcc_next, &tmp1, &tmp2, why, distance, DRC_REVERSE | DRC_BOTHCORNERS | flags, plane2, plane); dp->drcc_next = dpnew; } } } } /* Finally, if multiplane rule then check that set2 types * are not present just to right of edges with setR on LHS * and set1 on RHS. This check is necessary to make sure * that a set1 rectangle doesn't coincide exactly with a * set2 rectangle. * (This check added by Michael Arnold on 4/10/86.) */ if (needtrigger) continue; if ((pset = (DBTypesOnSamePlane(i, j) & pmask1))) { plane = LowestMaskBit(pset); /* LHS is an element of setR, RHS is an element of set1 */ if (TTMaskHasType(&setR, i) && TTMaskHasType(set1, j)) { /* * Must not have 'set2' for 'distance' to the right of * an edge between the types not in set1 and set1. * (is only checked for cross plane rules - these are * all of type touching_illegal) */ /* Walk list to last check. New checks ("cookies") go * at end of list since we are checking for distance of * 1 and the list is sorted in order of decreasing distance. */ for (dp = DRCCurStyle->DRCRulesTbl [i][j]; dp->drcc_next != (DRCCookie *) NULL; dp = dp->drcc_next); /* null body */ /* Insert one check for each plane involved in set2 */ plane2 = LowestMaskBit(pmask2); /* filter out checks that are not cross plane */ if (i == TT_SPACE) { if (DBTypeOnPlane(j, plane2)) continue; } else { if (DBTypeOnPlane(i, plane2)) continue; } /* create new check and add it to list */ dpnew = (DRCCookie *) mallocMagic(sizeof (DRCCookie)); TTMaskClearMask3(&tmp1, &DBPlaneTypes[plane2], set2); TTMaskZero(&tmp2); drcAssign(dpnew, 1, dp->drcc_next, &tmp1, &tmp2, why, distance, DRC_FORWARD | flags, plane2, plane); dp->drcc_next = dpnew; } } } } return (MAX(wwidth, distance)); } /* * ---------------------------------------------------------------------------- * * drcSpacing -- * * Process a spacing rule. * This is of the form: * * spacing layers1 layers2 distance adjacency why * * e.g, * * spacing metal,pmc/m,dmc/m metal,pmc/m,dmc/m 4 touching_ok \ * "metal spacing must be at least 4" * * Adjacency may be either "touching_ok" or "touching_illegal" * In the first case, no violation occurs when types in layers1 are * immediately adjacent to types in layers2. In the second case, * such adjacency causes a violation. * * Results: * Returns distance. * * Side effects: * Updates the DRC technology variables. * * Notes: * Extended to include the rule syntax: * * widespacing layers1 width [runlength] layers2 distance adjacency why * * This extension covers rules such as "If m1 width > 10um, then spacing to * unconnected m1 must be at least 0.6um". This assumes that the instantiated * edge4way rule is a standard "spacing" rule in which "dist" and "cdist" * (corner extension) distances are always the same, so we can use the "cdist" * record to encode the width of "layers1" which triggers the rule. We re-use * the CheckMaxwidth() code, but with the following differences: 1) it does not * trigger any error painting functions, 2) it returns a value stating whether * the maxwidth rule applies or not, 3) it uses the DBLayerTypeMaskTbl[] for * the tile type in question, not "oktypes", for its search, and 4) it uses * the max width in the "cdist" record, not the "dist" record, for the max * width rule. The "dist" record is copied to "cdist" before actually applying * the spacing rule, so that the rule acts like a proper spacing rule. * * Added adjacency rule "surround_ok" to check spacing to a type that * may also abut or surround the first layer---the portion of the layer * that is abutting or surrounding is not checked. This allows checks * of, e.g., diffusion in well to a separate well edge, or distance from * a poly bottom plate to unconnected poly. * * (Added 11/6/06) Adjacency may be "corner_ok", in which case it calls * routing drcSpacing3() (see above). * * (Added 9/19/18) Additional run-length distance can be used to limit the * maxwidth rule to geometry exceeding the given run-length (often used with * 65nm and smaller feature size processes). * * ---------------------------------------------------------------------------- */ int drcSpacing(argc, argv) int argc; char *argv[]; { char *layers1 = argv[1], *layers2; char *adjacency; int why; TileTypeBitMask set1, set2, tmp1, tmp2; PlaneMask pmask1, pmask2, pmaskA, pmaskB, ptest; int wwidth, distance, plane, plane2, runlength; bool widerule, multiplane = FALSE; if ((argc == 7) && (!strcmp(argv[4], "corner_ok"))) return drcSpacing3(argc, argv); widerule = (strncmp(argv[0], "wide", 4) == 0); if (widerule) { wwidth = atoi(argv[2]); if (argc == 8) { runlength = atoi(argv[3]); layers2 = argv[4]; distance = atoi(argv[5]); adjacency = argv[6]; why = drcWhyCreate(argv[7]); } else { layers2 = argv[3]; distance = atoi(argv[4]); runlength = -1; adjacency = argv[5]; why = drcWhyCreate(argv[6]); } /* TxPrintf("Info: DRCtech: widespacing rule for %s width %d:" " spacing must be %d\n", layers1, wwidth, distance); */ } else { layers2 = argv[2]; distance = atoi(argv[3]); adjacency = argv[4]; wwidth = distance; why = drcWhyCreate(argv[5]); runlength = -1; if (argc >= 7) { TechError("Unknown argument in spacing line.\n"); return(0); } } /* Either list of types may contain independent types on different */ /* planes. However, if so, then we may not use touching_ok, and */ /* there are other restrictions. */ ptest = DBTechNoisyNameMask(layers1, &set1); pmask1 = CoincidentPlanes(&set1, ptest); if ((pmask1 == 0) && (ptest != 0)) { pmask1 = ptest; multiplane = TRUE; for (plane = 0; plane < DBNumPlanes; plane++) for (plane2 = 0; plane2 < DBNumPlanes; plane2++) { if (plane == plane2) continue; if (PlaneMaskHasPlane(pmask1, plane) && PlaneMaskHasPlane(pmask1, plane2)) { TTMaskAndMask3(&tmp1, &DBPlaneTypes[plane], &DBPlaneTypes[plane2]); TTMaskAndMask(&tmp1, &set1); if (!TTMaskIsZero(&tmp1)) { TechError("Types in first list must either be in" " one plane or else types must not share" " planes.\n"); return (0); } } } } ptest = DBTechNoisyNameMask(layers2, &set2); pmask2 = CoincidentPlanes(&set2, ptest); if ((pmask2 == 0) && (ptest != 0)) { pmask2 = ptest; multiplane = TRUE; for (plane = 0; plane < DBNumPlanes; plane++) for (plane2 = 0; plane2 < DBNumPlanes; plane2++) { if (plane == plane2) continue; if (PlaneMaskHasPlane(pmask2, plane) && PlaneMaskHasPlane(pmask2, plane2)) { TTMaskAndMask3(&tmp1, &DBPlaneTypes[plane], &DBPlaneTypes[plane2]); TTMaskAndMask(&tmp1, &set2); if (!TTMaskIsZero(&tmp1)) { TechError("Types in second list must either be in" " one plane or else types must not share" " planes.\n"); return (0); } } } } if (multiplane) { /* Loop over independent plane/layer combinations */ for (plane = 0; plane < DBNumPlanes; plane++) for (plane2 = 0; plane2 < DBNumPlanes; plane2++) { if (PlaneMaskHasPlane(pmask1, plane) && PlaneMaskHasPlane(pmask2, plane2)) { pmaskA = PlaneNumToMaskBit(plane); pmaskB = PlaneNumToMaskBit(plane2); TTMaskAndMask3(&tmp1, &set1, &DBPlaneTypes[plane]); TTMaskAndMask3(&tmp2, &set2, &DBPlaneTypes[plane2]); return drcMaskSpacing(&tmp1, &tmp2, pmaskA, pmaskB, wwidth, distance, adjacency, why, widerule, runlength, multiplane); } } } else return drcMaskSpacing(&set1, &set2, pmask1, pmask2, wwidth, distance, adjacency, why, widerule, runlength, multiplane); return 0; } /* * ---------------------------------------------------------------------------- * * drcEdge -- * * Process a primitive edge rule. * This is of the form: * * edge layers1 layers2 dist OKtypes cornerTypes cornerDist [option] why [plane] * or edge4way layers1 layers2 dist OKtypes cornerTypes cornerDist [option] why [plane] * * e.g, * * edge poly,pmc s 1 diff poly,pmc "poly-diff separation must be 2" * * An "edge" rule is applied only down and to the left. * An "edge4way" rule is applied in all four directions. * * "option" can be "manhattan_dist", which forces the corner areas to be * checked per manhattan distance rules, not euclidean distance rules. * * Results: * Returns greater of dist and cdist. * * Side effects: * Updates the DRC technology variables. * * ---------------------------------------------------------------------------- */ int drcEdge(argc, argv) int argc; char *argv[]; { char *layers1 = argv[1], *layers2 = argv[2]; int distance = atoi(argv[3]); char *okTypes = argv[4], *cornerTypes = argv[5]; int cdist = atoi(argv[6]); unsigned short flags = 0; int why; bool fourway = (strcmp(argv[0], "edge4way") == 0); TileTypeBitMask set1, set2, setC, setM; DRCCookie *dp, *dpnew; int plane, checkPlane, tmpPlane = 0; PlaneMask pMask1, pMaskM, pMaskC, pset, ptest; TileType i, j; if ((argc > 7) && (!strcmp(argv[7], "manhattan_dist"))) { flags = DRC_MANHATTAN; why = drcWhyCreate(argv[8]); if (argc == 10) tmpPlane = DBTechNoisyNamePlane(argv[9]); argc--; } else { why = drcWhyCreate(argv[7]); if (argc == 9) tmpPlane = DBTechNoisyNamePlane(argv[8]); } /* * Edge4way rules produce [j][i] entries as well as [i][j] * ones, and check both corners rather than just one corner. */ ptest = DBTechNoisyNameMask(layers1, &set1); pMask1 = CoincidentPlanes(&set1, ptest); ptest = DBTechNoisyNameMask(layers2, &set2); pMask1 &= CoincidentPlanes(&set2, ptest); if (pMask1 == 0) { TechError("All edges in edge rule must lie in shared planes.\n"); return (0); } /* Give warning if types1 and types2 intersect */ if (TTMaskIntersect(&set1, &set2)) TechError("Warning: types1 and types2 have nonempty intersection. " "DRC does not check edges with the same type on both " "sides.\n"); ptest = DBTechNoisyNameMask(cornerTypes, &setC); pMaskC = CoincidentPlanes(&setC, ptest); if ((pMaskC & pMask1) == 0) { TechError("Corner types aren't in same plane as edges.\n"); return (0); } /* * OKtypes determine the checkPlane. If checkPlane exists, it should * only be used to check against the plane of OKtypes. */ ptest = DBTechNoisyNameMask(okTypes, &setM); pMaskM = CoincidentPlanes(&setM, ptest); if (pMaskM == 0 || pMaskM == DBTypePlaneMaskTbl[TT_SPACE]) { /* Technically it should be illegal to specify simply "space" * in the types list for a DRC rule, as it is ambiguous. * However, we will assume that the plane of the edge is * intended. The "plane" argument may be used to do the * qualification (for backwards compatibility); any other use * gets a warning. */ if (TTMaskEqual(&DBSpaceBits, &setM)) { if (argc == 9) pMaskM = PlaneNumToMaskBit(tmpPlane); else { TechError("OK types \"%s\" in more than one plane.\n" " Assuming same plane (%s) as edge.\n", okTypes, DBPlaneLongNameTbl[LowestMaskBit(pMask1)]); pMaskM = pMask1; } } /* The case okTypes="0" is explicitly allowed according */ /* to the manual, so we parse it accordingly. */ else if (!strcmp(okTypes, "0")) pMaskM = pMask1; else { TechError("All OK types must lie in one plane.\n"); return (0); } } /* "plane" argument deprecated; kept for backward compatibility only */ if ((argc == 9) && (PlaneNumToMaskBit(tmpPlane) != pMaskM)) TechError("Ignoring bad plane argument.\n"); for (i = 0; i < DBNumTypes; i++) { for (j = 0; j < DBNumTypes; j++) { if (i == j) continue; if ((pset = (DBTypesOnSamePlane(i, j) & pMask1))) { if (TTMaskHasType(&set1, i) && TTMaskHasType(&set2, j)) { /* For fastest DRC, checkPlane and plane should be */ /* the same, if possible. */ if ((pset & pMaskM) != 0) { plane = LowestMaskBit(pset & pMaskM); checkPlane = plane; } else { plane = LowestMaskBit(pset); checkPlane = LowestMaskBit(pMaskM); } /* Find bucket preceding the new one we wish to insert */ dp = drcFindBucket(i, j, distance); dpnew = (DRCCookie *)mallocMagic(sizeof (DRCCookie)); drcAssign(dpnew, distance, dp->drcc_next, &setM, &setC, why, cdist, DRC_FORWARD | flags, checkPlane, plane); if (fourway) dpnew->drcc_flags |= DRC_BOTHCORNERS; dp->drcc_next = dpnew; if (!fourway) continue; /* find bucket preceding new one we wish to insert */ dp = drcFindBucket(j, i, distance); dpnew = (DRCCookie *)mallocMagic(sizeof (DRCCookie)); drcAssign(dpnew,distance,dp->drcc_next, &setM, &setC, why, cdist, DRC_REVERSE | flags, checkPlane, plane); dpnew->drcc_flags |= DRC_BOTHCORNERS; dp->drcc_next = dpnew; } } } } return (MAX(distance, cdist)); } /* * ---------------------------------------------------------------------------- * * drcOverhang -- * * Process an overhang rule. * This is of the form: * * overhang layers2 layers1 dist why * * indicating that layers2 must overhang layers1 by a distance of at least * dist. * * This rule is equivalent to: * * edge4way layers1 space/p2|layers2 dist layers1|layers2 \ * space/p2|layers2 dist why * * ---------------------------------------------------------------------------- */ int drcOverhang(argc, argv) int argc; char *argv[]; { char *layers2 = argv[1], *layers1 = argv[2]; int distance = atoi(argv[3]); int why = drcWhyCreate(argv[4]); TileTypeBitMask set1, set2, setM, setC, setN, set2inv; DRCCookie *dp, *dpnew, *dptrig; int plane, plane2; TileType i, j; PlaneMask pMask1, pMask2, pset, ptest; ptest = DBTechNoisyNameMask(layers1, &set1); pMask1 = CoincidentPlanes(&set1, ptest); if (pMask1 == 0) { TechError("All layers in first set for \"overhang\" must be on " "the same plane\n"); return (0); } TTMaskCom2(&setN, &set1); ptest = DBTechNoisyNameMask(layers2, &set2); pMask2 = CoincidentPlanes(&set2, ptest); if (pMask2 == 0) { TechError("All layers in second set for \"overhang\" must be on " "the same plane\n"); return (0); } TTMaskCom2(&set2inv, &set2); /* Warn if types1 and types2 intersect */ if (TTMaskIntersect(&set1, &set2)) TechError("Warning: inside and outside types have nonempty intersection. " "DRC does not check edges with the same type on both sides.\n"); /* SetM is the union of set1 and set2 */ TTMaskZero(&setM); TTMaskSetMask3(&setM, &set1, &set2); /* Add space to set2 */ TTMaskSetType(&set2, TT_SPACE); /* SetC is the empty set */ TTMaskZero(&setC); for (i = 0; i < DBNumTypes; i++) { for (j = 0; j < DBNumTypes; j++) { if (i == j) continue; if ((pset = (DBTypesOnSamePlane(i, j) & pMask2))) { if ((pset & pMask1) != 0) { if (TTMaskHasType(&set1, i) && TTMaskHasType(&set2, j)) { plane = LowestMaskBit(pset); /* Find bucket preceding the new one we wish to insert */ dp = drcFindBucket(i, j, distance); dpnew = (DRCCookie *)mallocMagic (sizeof (DRCCookie)); drcAssign(dpnew, distance, dp->drcc_next, &setM, &setM, why, distance, DRC_FORWARD | DRC_BOTHCORNERS, plane, plane); dp->drcc_next = dpnew; /* find bucket preceding new one we wish to insert */ dp = drcFindBucket(j, i, distance); dpnew = (DRCCookie *)mallocMagic(sizeof (DRCCookie)); drcAssign(dpnew, distance, dp->drcc_next, &setM, &setM, why, distance, DRC_REVERSE | DRC_BOTHCORNERS, plane, plane); dp->drcc_next = dpnew; } } else /* Multi-plane overhang rule */ { if (TTMaskHasType(&set2, i) && TTMaskHasType(&set2inv, j)) { plane = LowestMaskBit(pset); plane2 = LowestMaskBit(pMask1); /* find bucket preceding the new one we wish to insert */ dp = drcFindBucket(i, j, distance); dpnew = (DRCCookie *)mallocMagic(sizeof(DRCCookie)); drcAssign(dpnew, distance, dp->drcc_next, &set1, &set1, why, distance, DRC_FORWARD, plane2, plane); dptrig = (DRCCookie *)mallocMagic(sizeof(DRCCookie)); drcAssign(dptrig, 1, dpnew, &setN, &setC, why, 0, DRC_FORWARD | DRC_TRIGGER, plane2, plane); dp->drcc_next = dptrig; dp = drcFindBucket(j, i, distance); dpnew = (DRCCookie *)mallocMagic(sizeof(DRCCookie)); drcAssign(dpnew, distance, dp->drcc_next, &set1, &set1, why, distance, DRC_REVERSE, plane2, plane); dptrig = (DRCCookie *)mallocMagic(sizeof(DRCCookie)); drcAssign(dptrig, 1, dpnew, &setN, &setC, why, 0, DRC_REVERSE | DRC_TRIGGER, plane2, plane); dp->drcc_next = dptrig; } } } } } return distance; } /* * ---------------------------------------------------------------------------- * * drcRectOnly -- * * Process a rectangle-only rule. This rule prohibits non-rectangular * geometry, and is used especially for contacts, as the "squares" operator * in the CIF/GDS output generator can't handle non-rectangular areas. * The rule is of the form: * * rect_only layers why * * and is equivalent to: * * edge4way layers ~(layers)/plane 1 ~(layers)/plane (all_layers)/plane 1 * * The rect_only rule avoids the above contrived construction, especially the * requirement of specifying "all_layers" as something like (~(x),x)/p, a sure- * fire obfuscation. * * ---------------------------------------------------------------------------- */ int drcRectOnly(argc, argv) int argc; char *argv[]; { char *layers = argv[1]; int why = drcWhyCreate(argv[2]); TileTypeBitMask set1, set2, setC; PlaneMask pmask, pset, ptest; DRCCookie *dp, *dpnew; int plane; TileType i, j; ptest = DBTechNoisyNameMask(layers, &set1); pmask = CoincidentPlanes(&set1, ptest); if (pmask == 0) { TechError("All types for \"rect_only\" must be on the same plane.\n"); return (0); } /* set2 is the inverse of set1 */ TTMaskCom2(&set2, &set1); for (i = 0; i < DBNumTypes; i++) { for (j = 0; j < DBNumTypes; j++) { if (i == j) continue; if ((pset = (DBTypesOnSamePlane(i, j) & pmask))) { if (TTMaskHasType(&set1, i) && TTMaskHasType(&set2, j)) { plane = LowestMaskBit(pset); /* setC = all types in plane */ TTMaskZero(&setC); TTMaskSetMask(&setC, &DBPlaneTypes[plane]); /* Find bucket preceding the new one we wish to insert */ dp = drcFindBucket(i, j, 1); dpnew = (DRCCookie *)mallocMagic(sizeof (DRCCookie)); drcAssign(dpnew, 1, dp->drcc_next, &set2, &setC, why, 1, DRC_FORWARD | DRC_BOTHCORNERS, plane, plane); dp->drcc_next = dpnew; /* find bucket preceding new one we wish to insert */ dp = drcFindBucket(j, i, 1); dpnew = (DRCCookie *)mallocMagic(sizeof (DRCCookie)); drcAssign(dpnew,1,dp->drcc_next, &set2, &setC, why, 1, DRC_REVERSE | DRC_BOTHCORNERS, plane, plane); dp->drcc_next = dpnew; } } } } return 1; } /* * ---------------------------------------------------------------------------- * * drcSurround -- * * Process a surround rule. * This is of the form: * * surround layers1 layers2 dist [dist2] presence why * * indicating that layers2 must surround layers1 by at least distance * dist in all directions. * * This rule is equivalent to: * * edge4way ~(layers2)/plane2 layers2 dist ~(layers1)/plane1 \ * layers2 dist why * * When presence=absence_illegal, the following additional rule is needed: * * edge4way layers1 ~(layers2)/plane1 dist NULL ~(layers2)/plane1 \ * dist why * * Extension added July 12, 2014: For via rules where an asymmetric * surround is allowed, with a smaller surround allowed on two sides if * the remaining two sides have a larger surround. This can be implemented * with a trigger rule, and is specified by the syntax above with "presence" * being "directional". Note that the rule expresses that the overhang rule * requires the presence of the material on one side of a corner. If the * other side has a non-zero minimum surround requirement, then it should * be implemented with an additional (absence_illegal) surround rule. * Otherwise, any width of material less than "dist" on one side of a * corner will trigger the rule requiring at least "dist" width of the same * material on the other side of the corner. * * Extension added November 30, 2022: Similar to the asymmetric via * extension rule above are rules of the type "extension on one side * must be >= dist if the extension on the adjacent side is < dist2", * for which the asymmetric rule above is the limiting case dist = dist2. * dist2 is optional and is taken to be equal to dist if not present, * which is the original form of the "directional" rule. * * ---------------------------------------------------------------------------- */ int drcSurround(argc, argv) int argc; char *argv[]; { char *layers1 = argv[1], *layers2 = argv[2], *endptr; int distance = atoi(argv[3]); int distance2; char *presence = argv[4]; int why = drcWhyCreate(argv[5]); TileTypeBitMask set1, set2, setM, invM, setR; DRCCookie *dp, *dpnew, *dptrig; int plane1, plane2; PlaneMask pmask, pmask2, pset, ptest; TileType i, j; bool isExact = FALSE; bool isDirectional = FALSE; if (argc == 7) { distance2 = strtol(argv[4], &endptr, 10); if ((endptr != NULL) && (*endptr != '\0')) { TechError("Incorrect arguments in directional \"surround\" rule\n"); return (0); } presence = argv[5]; why = drcWhyCreate(argv[6]); } else { distance2 = distance; presence = argv[4]; why = drcWhyCreate(argv[5]); } ptest = DBTechNoisyNameMask(layers1, &setM); pmask = CoincidentPlanes(&setM, ptest); if (pmask == 0) { TechError("Inside types in \"surround\" must be on the same plane\n"); return (0); } ptest = DBTechNoisyNameMask(layers2, &set2); pmask2 = CoincidentPlanes(&set2, ptest); if (pmask2 == 0) { TechError("Outside types in \"surround\" must be on the same plane\n"); return (0); } /* "exact_width" rule implemented 9/16/10. This enforces an exact */ /* surround distance. "absence_illegal" is implied. */ if (!strncmp(presence, "exact_", 6)) isExact = TRUE; else if (!strncmp(presence, "directional", 11)) { isDirectional = TRUE; /* Combined mask */ TTMaskZero(&setR); TTMaskSetMask(&setR, &setM); TTMaskSetMask(&setR, &set2); } /* invert setM */ TTMaskCom2(&invM, &setM); /* set1 is the inverse of set2 */ TTMaskCom2(&set1, &set2); for (i = 0; i < DBNumTypes; i++) { for (j = 0; j < DBNumTypes; j++) { if (i == j) continue; /* Ignore false edges */ if (isDirectional) { if ((pset = (DBTypesOnSamePlane(i, j) & pmask))) { /* Directional surround is done entirely differently */ if (TTMaskHasType(&setM, i) && TTMaskHasType(&invM, j)) { plane1 = LowestMaskBit(pset); plane2 = LowestMaskBit(pmask2); /* Find bucket preceding the new one we wish to insert */ dp = drcFindBucket(i, j, distance); dpnew = (DRCCookie *)mallocMagic(sizeof(DRCCookie)); /* Insert triggered rule */ drcAssign(dpnew, distance, dp->drcc_next, &setR, &DBAllTypeBits, why, distance, DRC_REVERSE | DRC_BOTHCORNERS, plane2, plane1); dptrig = (DRCCookie *)mallocMagic(sizeof(DRCCookie)); drcAssign(dptrig, distance2, dpnew, &set2, &DBZeroTypeBits, why, 0, DRC_FORWARD | DRC_TRIGGER, plane2, plane1); dp->drcc_next = dptrig; /* And the other direction. . . */ dp = drcFindBucket(j, i, distance); dpnew = (DRCCookie *)mallocMagic(sizeof(DRCCookie)); /* Insert triggered rule */ drcAssign(dpnew, distance, dp->drcc_next, &setR, &DBAllTypeBits, why, distance, DRC_FORWARD | DRC_BOTHCORNERS, plane2, plane1); dptrig = (DRCCookie *)mallocMagic(sizeof(DRCCookie)); drcAssign(dptrig, distance2, dpnew, &set2, &DBZeroTypeBits, why, 0, DRC_REVERSE | DRC_TRIGGER, plane2, plane1); dp->drcc_next = dptrig; } } } else { if ((pset = (DBTypesOnSamePlane(i, j) & pmask2))) { if (TTMaskHasType(&set1, i) && TTMaskHasType(&set2, j)) { plane1 = LowestMaskBit(pmask); plane2 = LowestMaskBit(pset); /* Find bucket preceding the new one we wish to insert */ dp = drcFindBucket(i, j, distance); dpnew = (DRCCookie *)mallocMagic(sizeof(DRCCookie)); drcAssign(dpnew, distance, dp->drcc_next, &invM, &set2, why, distance, DRC_FORWARD | DRC_BOTHCORNERS, plane1, plane2); dp->drcc_next = dpnew; /* find bucket preceding new one we wish to insert */ dp = drcFindBucket(j, i, distance); dpnew = (DRCCookie *)mallocMagic(sizeof(DRCCookie)); drcAssign(dpnew, distance, dp->drcc_next, &invM, &set2, why, distance, DRC_REVERSE | DRC_BOTHCORNERS, plane1, plane2); dp->drcc_next = dpnew; } } } } } if (isExact) { for (i = 0; i < DBNumTypes; i++) { for (j = 0; j < DBNumTypes; j++) { if (i == j) continue; /* Ignore false edges */ if ((pset = (DBTypesOnSamePlane(i, j) & pmask))) { if (TTMaskHasType(&setM, i) && TTMaskHasType(&set2, j)) { plane1 = LowestMaskBit(pset); /* Find bucket preceding the new one we wish to insert */ dp = drcFindBucket(i, j, distance); dpnew = (DRCCookie *)mallocMagic(sizeof(DRCCookie)); drcAssign(dpnew, distance, dp->drcc_next, &set1, &set2, why, distance, DRC_FORWARD | DRC_BOTHCORNERS | DRC_OUTSIDE, plane1, plane1); dp->drcc_next = dpnew; /* find bucket preceding new one we wish to insert */ dp = drcFindBucket(j, i, distance); dpnew = (DRCCookie *)mallocMagic(sizeof(DRCCookie)); drcAssign(dpnew, distance, dp->drcc_next, &set1, &set2, why, distance, DRC_REVERSE | DRC_BOTHCORNERS | DRC_OUTSIDE, plane1, plane1); dp->drcc_next = dpnew; } } } } } if ((!isExact) && strcmp(presence, "absence_illegal")) return distance; /* Add an extra rule when presence of the surrounding */ /* layer is required. Rule is different if planes match. */ if ((pset = pmask & pmask2)) { TTMaskZero(&invM); TTMaskSetMask(&invM, &setM); TTMaskSetMask(&invM, &set2); TTMaskCom(&invM); TTMaskZero(&set1); for (i = 0; i < DBNumTypes; i++) for (j = 0; j < DBNumTypes; j++) { if (i == j) continue; if ((pset = (DBTypesOnSamePlane(i, j) & pmask & pmask2))) { plane1 = LowestMaskBit(pset); if (TTMaskHasType(&setM, i) && TTMaskHasType(&invM, j)) { /* Find bucket preceding the new one we wish to insert */ dp = drcFindBucket(i, j, distance); dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie))); drcAssign(dpnew, distance, dp->drcc_next, &set1, &invM, why, distance, DRC_FORWARD | DRC_BOTHCORNERS, plane1, plane1); dp->drcc_next = dpnew; /* find bucket preceding new one we wish to insert */ dp = drcFindBucket(j, i, distance); dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie))); drcAssign(dpnew,distance,dp->drcc_next, &set1, &invM, why, distance, DRC_REVERSE | DRC_BOTHCORNERS, plane1, plane1); dp->drcc_next = dpnew; } } } } else { for (i = 0; i < DBNumTypes; i++) for (j = 0; j < DBNumTypes; j++) { if (i == j) continue; if ((pset = (DBTypesOnSamePlane(i, j) & pmask))) { if (TTMaskHasType(&setM, i) && TTMaskHasType(&invM, j)) { plane1 = LowestMaskBit(pset); plane2 = LowestMaskBit(pmask2); /* Find bucket preceding the new one we wish to insert */ dp = drcFindBucket(i, j, distance); dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie))); drcAssign(dpnew, distance, dp->drcc_next, &set2, &invM, why, distance, DRC_FORWARD | DRC_BOTHCORNERS, plane2, plane1); dp->drcc_next = dpnew; /* find bucket preceding new one we wish to insert */ dp = drcFindBucket(j, i, distance); dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie))); drcAssign(dpnew,distance,dp->drcc_next, &set2, &invM, why, distance, DRC_REVERSE | DRC_BOTHCORNERS, plane2, plane1); dp->drcc_next = dpnew; } } } } return distance; } /* * ---------------------------------------------------------------------------- * * drcNoOverlap -- * * Process a no-overlap rule. * This is of the form: * * no_overlap layers1 layers2 * * e.g, * * no_overlap poly m2contact * * Results: * Returns 0. * * Side effects: * Updates the DRC technology variables. * * ---------------------------------------------------------------------------- */ int drcNoOverlap(argc, argv) int argc; char *argv[]; { char *layers1 = argv[1], *layers2 = argv[2]; TileTypeBitMask set1, set2; TileType i, j; int plane; /* * Grab up two sets of tile types, and make sure that if * any type from one set is painted over any type from the * other, then an error results. */ DBTechNoisyNameMask(layers1, &set1); DBTechNoisyNameMask(layers2, &set2); for (i = 0; i < DBNumTypes; i++) for (j = 0; j < DBNumTypes; j++) if (TTMaskHasType(&set1, i) && TTMaskHasType(&set2, j)) for (plane = 0; plane < DBNumPlanes; plane++) { DRCCurStyle->DRCPaintTable[plane][j][i] = TT_ERROR_S; DRCCurStyle->DRCPaintTable[plane][i][j] = TT_ERROR_S; } return (0); } /* * ---------------------------------------------------------------------------- * * drcExactOverlap -- * * Process an exact overlap * This is of the form: * * exact_overlap layers * * e.g, * * exact_overlap pmc,dmc * * Results: * Returns 0. * * Side effects: * Updates DRCExactOverlapTypes. * * ---------------------------------------------------------------------------- */ int drcExactOverlap(argc, argv) int argc; char *argv[]; { char *layers = argv[1]; TileTypeBitMask set; /* * Grab up a bunch of tile types, and remember these: tiles * of these types cannot overlap themselves in different cells * unless they overlap exactly. */ DBTechNoisyNameMask(layers, &set); TTMaskSetMask(&DRCCurStyle->DRCExactOverlapTypes, &set); return (0); } /* * ---------------------------------------------------------------------------- * * drcRectangle -- * * Process a rectangle rule. This is of the form: * * rectangle layers maxwidth [even|odd|any] why * * The rule checks to make sure that the region is rectangular and that the * width and length are even or odd, as specified. These two criteria ensure * that the squares rule of the cifout section can properly produce via * holes without misaligning them between cells and without putting the via * holes off grid. The maxwidth is required to make the extent of this rule * a finite size, so that we can set the DRChalo to something finite. * * Results: * maxwidth * * Side effects: * Updates the DRC technology variables. * * ---------------------------------------------------------------------------- */ int drcRectangle(argc, argv) int argc; char *argv[]; { char *layers = argv[1]; int why = drcWhyCreate(argv[4]); TileTypeBitMask types, nottypes; int maxwidth; static const char * const drcRectOpt[4] = {"any", "even", "odd", 0}; int i, j, even, plane; PlaneMask pMask, pset, ptest; /* parse arguments */ ptest = DBTechNoisyNameMask(layers, &types); pMask = CoincidentPlanes(&types, ptest); if (pMask == 0) { TechError("Layers in rectangle rule must lie in a single plane."); return 0; } TTMaskCom2(¬types, &types); if (sscanf(argv[2], "%d", &maxwidth) != 1) { TechError("bad maxwidth in rectangle rule"); return 0; } even = Lookup(argv[3], drcRectOpt); if (even < 0) { TechError("bad [even|odd|any] selection in rectangle rule"); return 0; } even--; /* -1: any, 0: even, 1: odd */ /* Install 2 edge rules: one that checks rectangle-ness, and one that * checks size */ for (i = 0; i < DBNumTypes; i++) { for (j = 0; j < DBNumTypes; j++) { if (i == j) continue; if ((pset = (DBTypesOnSamePlane(i, j) & pMask))) { if (TTMaskHasType(&types, i) && TTMaskHasType(¬types, j)) { DRCCookie *dp, *dpnew; plane = LowestMaskBit(pset); /* * A rule that checks rectangle-ness. * left: oktypes, right: other types * This rule needs to be checked in all 4 directions */ int distance = 1; /* Find bucket preceding the new one we wish to insert */ dp = drcFindBucket(i, j, distance); dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie))); drcAssign(dpnew, distance, dp->drcc_next, ¬types, &DBAllTypeBits, why, distance, DRC_FORWARD, plane, plane); dp->drcc_next = dpnew; /* Find bucket preceding the new one we wish to insert */ dp = drcFindBucket(j, i, distance); /* note: j, i not i, j */ dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie))); drcAssign(dpnew, distance, dp->drcc_next, ¬types, &DBAllTypeBits, why, distance, DRC_REVERSE, plane, plane); dp->drcc_next = dpnew; if (maxwidth > 0) { /* * A rule that checks size. * left: other types, right: oktypes */ distance = maxwidth; /* note: j, i not i, j */ for (dp = DRCCurStyle->DRCRulesTbl[j][i]; dp->drcc_next != (DRCCookie *) NULL && dp->drcc_next->drcc_dist < distance; dp = dp->drcc_next); /* null body */ dpnew = (DRCCookie *)mallocMagic(sizeof (DRCCookie)); drcAssign(dpnew, distance, dp->drcc_next, &types, &DBZeroTypeBits, why, even, DRC_RECTSIZE, plane, plane); dp->drcc_next = dpnew; } } } } } return maxwidth; } /* * ---------------------------------------------------------------------------- * * drcOption -- * * Process an option declaration * This is of the form: * * option option_name [...] * * e.g, * * option wide-width-inclusive * * Results: * Returns 0. * * Side effects: * Updates DRCFlags. * * ---------------------------------------------------------------------------- */ int drcOption(argc, argv) int argc; char *argv[]; { int i; if (DRCCurStyle == NULL) return 0; /* Assume space-separated list of options. Flag an error on any */ /* option name not recognized. */ for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "wide-width-noninclusive")) DRCCurStyle->DRCFlags |= DRC_FLAGS_WIDEWIDTH_NONINCLUSIVE; else { TechError("Unrecognized DRC option \"%s\" (ignored).\n", argv[i]); } } return (0); } /* * ---------------------------------------------------------------------------- * * drcStepSize -- * * Process a declaration of the step size. * This is of the form: * * stepsize step_size * * e.g, * * stepsize 1000 * * Results: * Returns 0. * * Side effects: * Updates DRCStepSize. * * ---------------------------------------------------------------------------- */ int drcStepSize(argc, argv) int argc; char *argv[]; { if (DRCCurStyle == NULL) return 0; DRCCurStyle->DRCStepSize = atoi(argv[1]); if (DRCCurStyle->DRCStepSize <= 0) { TechError("Step size must be a positive integer.\n"); DRCCurStyle->DRCStepSize = 0; } else if (DRCCurStyle->DRCStepSize < 16) { TechError("Warning: abnormally small DRC step size (%d)\n", DRCCurStyle->DRCStepSize); } return (0); } /* * ---------------------------------------------------------------------------- * * DRCTechFinal -- * * Called after all lines of the drc section in the technology file have been * read. Ensures that a valid style is in effect, and then calls * drcTechFinalStyle(). * * Results: * None. * * Side effects: * See drcTechFinalStyle(); * * ---------------------------------------------------------------------------- */ void DRCTechFinal() { DRCStyle *ds; /* Create a "default" style if there isn't one */ if (DRCStyleList == NULL) { DRCStyleList = (DRCKeep *)mallocMagic(sizeof(DRCKeep)); DRCStyleList->ds_next = NULL; DRCStyleList->ds_name = StrDup((char **)NULL, "default"); drcTechNewStyle(); DRCCurStyle->ds_name = DRCStyleList->ds_name; DRCCurStyle->ds_status = TECH_LOADED; } drcTechFinalStyle(DRCCurStyle); } /* * ---------------------------------------------------------------------------- * drcScaleDown --- * * DRC distances may be specified with a scale factor so that physically * based rules can be recorded, but the rules used will be rounded (up) * to the nearest lambda. The fractional part of the true distance in * lambda is saved, so that the original value can be recovered when * the magic grid is rescaled. * * Results: * None. * * Side Effects: * Scales all the DRC distances by dividing by the DRC scale factor. * ---------------------------------------------------------------------------- */ void drcScaleDown(style, scalefactor) DRCStyle *style; int scalefactor; { TileType i, j; DRCCookie *dp; int dist; if (scalefactor > 1) { for (i = 0; i < TT_MAXTYPES; i++) for (j = 0; j < TT_MAXTYPES; j++) for (dp = style->DRCRulesTbl[i][j]; dp != NULL; dp = dp->drcc_next) { if (dp->drcc_dist > 0) { dist = dp->drcc_dist; dp->drcc_dist /= scalefactor; if ((dp->drcc_mod = (unsigned char)(dist % scalefactor)) != 0) if (!(dp->drcc_flags & DRC_MAXWIDTH)) dp->drcc_dist++; } if (dp->drcc_cdist > 0) { int cmod; int locscale = scalefactor; if (dp->drcc_flags & DRC_AREA) locscale *= scalefactor; dist = dp->drcc_cdist; dp->drcc_cdist /= locscale; /* Save the amount by which the distance needs to be * corrected when multiplied back to the original * scale. For area, the modulus will always be * a multiple of the scalefactor, so it can be * divided by the scalefactor so it still fits in an * unsigned char. */ if ((cmod = (dist % locscale)) != 0) { dp->drcc_cdist++; if (dp->drcc_flags & DRC_AREA) dp->drcc_cmod = (unsigned char)(cmod / scalefactor); else dp->drcc_cmod = (unsigned char)cmod; } } } } } /* * ---------------------------------------------------------------------------- * drcScaleUp --- * * Recovers the original (pre-scaled) values for drcc_dist and * drcc_cdist in the DRC cookies. * * Results: * None. * * Side Effects: * Scales all the DRC distances by multiplying by the DRC scale factor. * ---------------------------------------------------------------------------- */ void drcScaleUp(style, scalefactor) DRCStyle *style; int scalefactor; { TileType i, j; DRCCookie *dp; int dist; if (style == NULL) return; if (scalefactor > 1) { for (i = 0; i < TT_MAXTYPES; i++) for (j = 0; j < TT_MAXTYPES; j++) for (dp = style->DRCRulesTbl[i][j]; dp != NULL; dp = dp->drcc_next) { if (dp->drcc_dist > 0) { dist = dp->drcc_dist; if (dp->drcc_mod != 0) if (!(dp->drcc_flags & DRC_MAXWIDTH)) dp->drcc_dist--; dp->drcc_dist *= scalefactor; dp->drcc_dist += (short)dp->drcc_mod; dp->drcc_mod = 0; } if (dp->drcc_cdist > 0) { dist = dp->drcc_cdist; if (dp->drcc_cmod != 0) dp->drcc_cdist--; dp->drcc_cdist *= scalefactor; if (dp->drcc_flags & DRC_AREA) { dp->drcc_cdist *= scalefactor; /* See note above on how cmod is divided by the * scalefactor for area values. */ dp->drcc_cdist += (short)(dp->drcc_cmod * scalefactor); } else dp->drcc_cdist += (short)dp->drcc_cmod; dp->drcc_cmod = 0; } } } } /* * ---------------------------------------------------------------------------- * * drcTechFinalStyle -- * * Called after all lines of the drc section in the technology file have been * read. The preliminary DRC Rules Table is pruned by removing rules covered * by other (longer distance) rules, and by removing the dummy rule at the * front of each list. Where edges are completely illegal, the rule list is * pruned to a single rule. * * Results: * None. * * Side effects: * May remove DRCCookies from the linked lists of the DRCRulesTbl. * * ---------------------------------------------------------------------------- */ void drcTechFinalStyle(style) DRCStyle *style; { TileTypeBitMask tmpMask, nextMask; DRCCookie *dummy, *dp, *next, *dptrig; DRCCookie **dpp, **dp2back; TileType i, j; /* Done with DRCWhyErrorTable */ HashKill(&DRCWhyErrorTable); /* If the scale factor is not 1, then divide all distances by */ /* the scale factor, take the ceiling, and save the (negative) */ /* remainder. */ drcScaleUp(style, style->DRCScaleFactorD); drcScaleDown(style, style->DRCScaleFactorN); /* Scale DRCTechHalo to match */ DRCTechHalo *= style->DRCScaleFactorD; DRCTechHalo /= style->DRCScaleFactorN; /* Set maximum halo */ style->DRCTechHalo = DRCTechHalo; /* A reasonable chunk size for design-rule checking is about * 16 times the maximum design-rule interaction distance. This * results in a halo overhead of about 27%. If there's no DRC * information at all (TechHalo is zero), just pick any size. * (Update 1/13/09: "any size" needs a bit of modification, * because 64 will be way too small for a layout with a small * scalefactor. Assuming that the CIF output style is valid, * use its scalefactor to adjust the step size). */ if (style->DRCStepSize == 0) { if (style->DRCTechHalo == 0) { if (CIFCurStyle != NULL) style->DRCStepSize = 6400 / CIFCurStyle->cs_scaleFactor; else style->DRCStepSize = 64; } else style->DRCStepSize = 16 * style->DRCTechHalo; } DRCStepSize = style->DRCStepSize; /* Remove dummy buckets */ for (i = 0; i < TT_MAXTYPES; i++) { for (j = 0; j < TT_MAXTYPES; j++) { dpp = &(style->DRCRulesTbl [i][j]); dummy = *dpp; *dpp = dummy->drcc_next; freeMagic((char *) dummy); } } drcCifFinal(); if (!DRCRuleOptimization) return; /* Check for edges that are completely illegal. Where this is the * case, eliminate all of the edge's rules except one. */ for (i = 0; i < DBNumTypes; i++) { for (j = 0; j < DBNumTypes; j++) { DRCCookie *keep = NULL, *dptest, *dptemp, *dpnew; for (dp = style->DRCRulesTbl[i][j]; dp != NULL; dp = dp->drcc_next) { if (dp->drcc_flags & (DRC_NONSTANDARD | DRC_OUTSIDE)) continue; if (dp->drcc_flags & DRC_REVERSE) { if ((i == TT_SPACE) || TTMaskHasType(&dp->drcc_mask, i)) continue; } else { if ((j == TT_SPACE) || TTMaskHasType(&dp->drcc_mask, j)) continue; } /* Rules where okTypes are in a different plane don't count, */ /* unless i or j also appear in the checked plane. */ if (dp->drcc_plane != dp->drcc_edgeplane) { if (dp->drcc_flags & DRC_REVERSE) { if ((i == TT_SPACE) || !DBTypeOnPlane(i, dp->drcc_plane)) continue; } else { if ((j == TT_SPACE) || !DBTypeOnPlane(j, dp->drcc_plane)) continue; } } // if (DBIsContact(i) || DBIsContact(j) || i == TT_SPACE || // j == TT_SPACE) continue; dpnew = NULL; keep = dp; /* This edge is illegal. Throw away all rules except the one * needed that is always violated. */ dptest = style->DRCRulesTbl[i][j]; while (dptest != NULL) { dptemp = dptest->drcc_next; if ((dptest == keep) || (dptest->drcc_edgeplane != keep->drcc_edgeplane)) { dptest->drcc_next = NULL; if (dpnew == NULL) style->DRCRulesTbl[i][j] = dptest; else dpnew->drcc_next = dptest; dpnew = dptest; /* "keep" can't be a trigger rule! */ if (dptest == keep) keep->drcc_flags &= ~DRC_TRIGGER; } else { freeMagic((char *)dptest); drcRulesOptimized++; } dptest = dptemp; } } /* TxPrintf("Edge %s-%s is illegal.\n", DBTypeShortName(i), DBTypeShortName(j)); */ } } /* * Remove any rule A "covered" by another rule B, i.e., * B's distance >= A's distance, * B's corner distance >= A's corner distance, * B's RHS type mask is a subset of A's RHS type mask, and * B's corner mask == A's corner mask * B's check plane == A's check plane * either both A and B or neither is a REVERSE direction rule * if A is BOTHCORNERS then B must be, too */ for (i = 0; i < DBNumTypes; i++) { for (j = 0; j < DBNumTypes; j++) { for (dp = style->DRCRulesTbl[i][j]; dp != NULL; dp = dp->drcc_next) { /* Don't optimize on trigger rules; optimize on the */ /* rule that gets triggered. */ if (dp->drcc_flags & DRC_TRIGGER) { dptrig = dp; dp = dp->drcc_next; } else dptrig = NULL; /* * Check following buckets to see if any is a superset. */ if (dp->drcc_flags & DRC_NONSTANDARD) continue; for (next = dp->drcc_next; next != NULL; next = next->drcc_next) { if (next->drcc_flags & DRC_TRIGGER) { /* A triggered rule cannot be considered */ /* a superset of a non-triggered rule or */ /* a rule with a different trigger, so */ /* we skip all triggered rules and their */ /* triggering rule. */ next = next->drcc_next; continue; } tmpMask = nextMask = next->drcc_mask; TTMaskAndMask(&tmpMask, &dp->drcc_mask); if (!TTMaskEqual(&tmpMask, &nextMask)) continue; if (!TTMaskEqual(&dp->drcc_corner, &next->drcc_corner)) continue; if (dp->drcc_dist > next->drcc_dist) continue; if (dp->drcc_cdist > next->drcc_cdist) continue; if (dp->drcc_plane != next->drcc_plane) continue; if (dp->drcc_flags & DRC_REVERSE) { if (!(next->drcc_flags & DRC_REVERSE)) continue; } else if (next->drcc_flags & DRC_REVERSE) continue; if ((next->drcc_flags & DRC_BOTHCORNERS) && (dp->drcc_flags & DRC_BOTHCORNERS) == 0) continue; if (next->drcc_flags & DRC_NONSTANDARD) continue; if (dp->drcc_dist == next->drcc_dist) { if ((next->drcc_flags & DRC_OUTSIDE) && !(dp->drcc_flags & DRC_OUTSIDE)) continue; if (!(next->drcc_flags & DRC_OUTSIDE) && (dp->drcc_flags & DRC_OUTSIDE)) continue; } if (dp->drcc_flags & DRC_SPLITTILE) { if (!(next->drcc_flags & DRC_SPLITTILE)) continue; } else if (next->drcc_flags & DRC_SPLITTILE) continue; break; } if (next == NULL) continue; /* "dp" is a subset of "next". Eliminate it. */ /* For triggered rules, eliminate both the rule */ /* and the trigger. */ if (dptrig != NULL) dp = dptrig; /* TxPrintf("For edge %s-%s, \"%s\" covers \"%s\"\n", DBTypeShortName(i), DBTypeShortName(j), DRCCurStyle->DRCWhyList[next->drcc_tag], DRCCurStyle->DRCWhyList[dp->drcc_tag]); */ dp2back = &(style->DRCRulesTbl[i][j]); while (*dp2back != dp) dp2back = &(*dp2back)->drcc_next; /* Trigger rules */ if (dptrig != NULL) { dptrig = dp->drcc_next; freeMagic((char *)dp->drcc_next); *dp2back = dp->drcc_next->drcc_next; /* Replace this entry so on the next cycle */ /* dp will be the next rule. This works */ /* even though dp is free'd (below), due to */ /* the one-delayed free mechanism. */ dp->drcc_next = *dp2back; } else *dp2back = dp->drcc_next; freeMagic((char *) dp); drcRulesOptimized += 1; } } } } /* * ---------------------------------------------------------------------------- * * DRCTechRuleStats -- * * Print out some statistics about the design rule database. * * Results: * None. * * Side effects: * A bunch of stuff gets printed on the terminal. * * ---------------------------------------------------------------------------- */ #define MAXBIN 10 void DRCTechRuleStats() { int counts[MAXBIN+1]; int edgeRules, overflow; int i, j; DRCCookie *dp; /* Count up the total number of edge rules, and histogram them * by the number of rules per edge. */ edgeRules = 0; overflow = 0; for (i=0; i<=MAXBIN; i++) counts[i] = 0; for (i=0; iDRCRulesTbl[i][j]; dp != NULL; dp = dp->drcc_next) thisCount++; edgeRules += thisCount; if (!DBTypesOnSamePlane(i, j)) continue; if (thisCount <= MAXBIN) counts[thisCount] += 1; else overflow += 1; } /* Print out the results. */ TxPrintf("Total number of rules specifed in tech file: %d\n", drcRulesSpecified); TxPrintf("Edge rules optimized away: %d\n", drcRulesOptimized); TxPrintf("Edge rules left in database: %d\n", edgeRules); TxPrintf("Histogram of # edges vs. rules per edge:\n"); for (i=0; i<=MAXBIN; i++) { TxPrintf(" %2d rules/edge: %d.\n", i, counts[i]); } TxPrintf(" >%2d rules/edge: %d.\n", MAXBIN, overflow); } /* * ---------------------------------------------------------------------------- * * DRCTechScale -- * * Multiply all DRC rule widths and spacings by a factor of scaled/scalen. * (Don't need to use DBScaleValue() because all values must be positive * and cannot be (M)INFINITY.) * * ---------------------------------------------------------------------------- */ void DRCTechScale(scalen, scaled) int scalen, scaled; { DRCCookie *dp; TileType i, j; int scalegcf; if (DRCCurStyle == NULL) return; else if (scalen == scaled) return; /* Revert DRC rules to original (unscaled) values */ drcScaleUp(DRCCurStyle, DRCCurStyle->DRCScaleFactorN); drcScaleDown(DRCCurStyle, DRCCurStyle->DRCScaleFactorD); /* Do the same for the plow rules */ DRCPlowScale(DRCCurStyle->DRCScaleFactorN, DRCCurStyle->DRCScaleFactorD, FALSE); DRCCurStyle->DRCScaleFactorD *= scaled; DRCCurStyle->DRCScaleFactorN *= scalen; /* Reduce scalefactor ratio by greatest common factor */ scalegcf = FindGCF(DRCCurStyle->DRCScaleFactorD, DRCCurStyle->DRCScaleFactorN); DRCCurStyle->DRCScaleFactorD /= scalegcf; DRCCurStyle->DRCScaleFactorN /= scalegcf; /* Rescale all rules to the new scalefactor */ drcScaleUp(DRCCurStyle, DRCCurStyle->DRCScaleFactorD); drcScaleDown(DRCCurStyle, DRCCurStyle->DRCScaleFactorN); /* Do the same for the plow rules */ DRCPlowScale(DRCCurStyle->DRCScaleFactorD, DRCCurStyle->DRCScaleFactorN, TRUE); DRCTechHalo *= scaled; DRCTechHalo /= scalen; DRCStepSize *= scaled; DRCStepSize /= scalen; DRCCurStyle->DRCTechHalo *= scaled; DRCCurStyle->DRCTechHalo /= scalen; DRCCurStyle->DRCStepSize *= scaled; DRCCurStyle->DRCStepSize /= scalen; } /* The following routines are used by the "tech" command (and in other places, * such as the LEF file reader) to query the DRC database. */ /* *----------------------------------------------------------------------------- * DRCGetDefaultLayerWidth --- * * Determine a default layer width from the DRC width rules * of a layer. Continue processing until we have processed all * rules, since rules are ordered from shortest to longest distance, * and the maximum distance rule will mask any rules with a shorter * distance. * * Results: * The minimum width of the magic layer, in magic internal units * * Side effects: * None. * *----------------------------------------------------------------------------- */ int DRCGetDefaultLayerWidth(ttype) TileType ttype; { int routeWidth = 0; DRCCookie *cptr; TileTypeBitMask *set; if (ttype < 0) { TxError("Error: Attempt to get default width of invalid layer!\n"); return 0; } for (cptr = DRCCurStyle->DRCRulesTbl[TT_SPACE][ttype]; cptr != (DRCCookie *) NULL; cptr = cptr->drcc_next) { /* Skip triggered and triggering rules */ if (cptr->drcc_flags & DRC_TRIGGER) { cptr = cptr->drcc_next; continue; } /* Skip area rule */ if (cptr->drcc_flags & DRC_AREA) continue; /* FORWARD rules only, and no MAXWIDTH or SPLITTILE */ if ((cptr->drcc_flags & (DRC_REVERSE | DRC_MAXWIDTH | DRC_SPLITTILE)) == 0) { set = &cptr->drcc_mask; if (TTMaskHasType(set, ttype) && TTMaskEqual(set, &cptr->drcc_corner)) if ((cptr->drcc_plane == DBPlane(ttype)) && (cptr->drcc_dist == cptr->drcc_cdist)) { routeWidth = cptr->drcc_dist; /* Diagnostic */ /* TxPrintf("DRC: Layer %s has default width %d\n", DBTypeLongNameTbl[ttype], routeWidth); */ } } } return routeWidth; } /* *----------------------------------------------------------------------------- * DRCGetDefaultLayerSpacing --- * * Determine a default layer-to-layer spacing from the DRC width * rules of a layer. Continue processing all rules, since rules * are ordered from shortest to longest distance, and the largest * distance matching the criteria sets the rule. * * Results: * The minimum spacing between the specified magic layer types, * in magic internal units * * Side effects: * None. * *----------------------------------------------------------------------------- */ int DRCGetDefaultLayerSpacing(ttype1, ttype2) TileType ttype1, ttype2; { int routeSpacing = 0; DRCCookie *cptr; TileTypeBitMask *set; for (cptr = DRCCurStyle->DRCRulesTbl[ttype1][TT_SPACE]; cptr != (DRCCookie *) NULL; cptr = cptr->drcc_next) { if (cptr->drcc_flags & DRC_TRIGGER) { /* Skip widespacing rules */ cptr = cptr->drcc_next; continue; } /* Skip area rule */ if (cptr->drcc_flags & DRC_AREA) continue; if ((cptr->drcc_flags & DRC_REVERSE) == 0) /* FORWARD only */ { set = &cptr->drcc_mask; if (!TTMaskHasType(set, ttype2)) if (PlaneMaskHasPlane(DBTypePlaneMaskTbl[ttype2], cptr->drcc_plane) && (cptr->drcc_dist == cptr->drcc_cdist)) { routeSpacing = cptr->drcc_dist; /* Diagnostic */ /* TxPrintf("DRC: Layer %s has default spacing %d to layer %s\n", DBTypeLongNameTbl[ttype1], routeSpacing, DBTypeLongNameTbl[ttype2]); */ } } } return routeSpacing; } /* *----------------------------------------------------------------------------- * DRCGetDefaultLayerSurround --- * * Determine the default minimum required surround amount * of layer type 2 around layer type 1. * Continue processing all rules, since rules are ordered from * shortest to longest distance, and the largest value of the * surround material sets the minimum required width. * * Results: * The minimum spacing between the specified magic layer types, * in magic internal units * * Side effects: * None. * *----------------------------------------------------------------------------- */ int DRCGetDefaultLayerSurround(ttype1, ttype2) TileType ttype1, ttype2; { int layerSurround = 0; DRCCookie *cptr; TileTypeBitMask *set, *cset; for (cptr = DRCCurStyle->DRCRulesTbl[ttype1][TT_SPACE]; cptr != (DRCCookie *) NULL; cptr = cptr->drcc_next) { if ((cptr->drcc_flags & DRC_REVERSE) == 0) /* FORWARD only */ { set = &cptr->drcc_mask; if (!TTMaskHasType(set, TT_SPACE)) if (PlaneMaskHasPlane(DBTypePlaneMaskTbl[ttype2], cptr->drcc_plane) && (cptr->drcc_dist == cptr->drcc_cdist)) { layerSurround = cptr->drcc_dist; /* Diagnostic */ /* TxPrintf("DRC: Layer %s has default surround %d over layer %s\n", DBTypeLongNameTbl[ttype2], layerSurround, DBTypeLongNameTbl[ttype1]); */ } } } if (layerSurround > 0) return layerSurround; for (cptr = DRCCurStyle->DRCRulesTbl[TT_SPACE][ttype1]; cptr != (DRCCookie *) NULL; cptr = cptr->drcc_next) { if ((cptr->drcc_flags & DRC_REVERSE) == 0) /* FORWARD only */ { set = &cptr->drcc_mask; cset = &cptr->drcc_corner; if (TTMaskHasType(set, TT_SPACE) && !TTMaskHasType(set, ttype1)) if ((TTMaskHasType(cset, ttype2)) && (cptr->drcc_flags & DRC_BOTHCORNERS) && (cptr->drcc_edgeplane == cptr->drcc_plane) && (cptr->drcc_dist == cptr->drcc_cdist)) { layerSurround = cptr->drcc_dist; /* Diagnostic */ /* TxPrintf("DRC: Layer %s has default surround %d over layer %s\n", DBTypeLongNameTbl[ttype2], layerSurround, DBTypeLongNameTbl[ttype1]); */ } } } return layerSurround; } /* *----------------------------------------------------------------------------- * DRCGetDirectionalLayerSurround --- * * Determine the minimum required surround amount of layer type 2 * around layer type 1 for a directional surround rule (rule * applies for two opposing sides of layer type 1). * * Results: * The minimum spacing between the specified magic layer types, * in magic internal units * * Side effects: * None. * *----------------------------------------------------------------------------- */ int DRCGetDirectionalLayerSurround(ttype1, ttype2) TileType ttype1, ttype2; { int layerSurround = 0; DRCCookie *cptr, *cnext; TileTypeBitMask *set, *cset; for (cptr = DRCCurStyle->DRCRulesTbl[ttype1][TT_SPACE]; cptr != (DRCCookie *) NULL; cptr = cptr->drcc_next) { if (cptr->drcc_flags & DRC_TRIGGER) { set = &cptr->drcc_mask; if (!TTMaskHasType(set, TT_SPACE) && TTMaskHasType(set, ttype2)) if ((cptr->drcc_plane == cptr->drcc_edgeplane) && (cptr->drcc_cdist == 0)) { layerSurround = cptr->drcc_dist; /* Diagnostic */ /* TxPrintf("DRC: Layer %s has default surround %d over layer %s\n", DBTypeLongNameTbl[ttype2], layerSurround, DBTypeLongNameTbl[ttype1]); */ } cnext = cptr->drcc_next; } } return layerSurround; } /* *----------------------------------------------------------------------------- * DRCGetDefaultLayerWideSpacing --- * * Determine a default layer-to-self wide-layer spacing rule from * the DRC width rules of a layer. * * Results: * The minimum spacing between the specified magic layer type and * itself, where one of the shapes has width greater than "twidth". * The result value is in magic internal units. * * If "twidth" is zero, then return the maximum spacing rule distance. * * Side effects: * None. * *----------------------------------------------------------------------------- */ int DRCGetDefaultWideLayerSpacing(ttype, twidth) TileType ttype; int twidth; { int routeSpacing = 0; DRCCookie *cptr; TileTypeBitMask *set; bool widerule = FALSE; for (cptr = DRCCurStyle->DRCRulesTbl[ttype][TT_SPACE]; cptr != (DRCCookie *) NULL; cptr = cptr->drcc_next) { if (cptr->drcc_flags & DRC_TRIGGER) /* Widespacing rule */ { widerule = TRUE; if (twidth > 0 && cptr->drcc_dist > twidth) /* Check against rule width */ return routeSpacing; } if (widerule && ((cptr->drcc_flags & DRC_REVERSE) == 0)) /* FORWARD only */ { set = &cptr->drcc_mask; if (!TTMaskHasType(set, ttype)) if (PlaneMaskHasPlane(DBTypePlaneMaskTbl[ttype], cptr->drcc_plane) && (cptr->drcc_dist == cptr->drcc_cdist)) { routeSpacing = cptr->drcc_dist; /* Diagnostic */ /* TxPrintf("DRC: Layer %s has wide spacing %d to layer %s width %d\n", DBTypeLongNameTbl[ttype1], routeSpacing, DBTypeLongNameTbl[ttype2], twidth); */ } } if (!(cptr->drcc_flags & DRC_TRIGGER)) widerule = FALSE; } return routeSpacing; }