/* * ExtTech.c -- * * Circuit extraction. * Code to read and process the sections of a technology file * that are specific to circuit extraction. * * ********************************************************************* * * 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 sccsid[] = "@(#)ExtTech.c 4.8 MAGIC (Berkeley) 10/26/85"; #endif /* not lint */ #include #include /* for strtod() */ #include #include #include #include /* for isspace() */ #include "tcltk/tclmagic.h" #include "utils/magic.h" #include "utils/utils.h" #include "utils/geometry.h" #include "tiles/tile.h" #include "utils/hash.h" #include "database/database.h" #include "database/databaseInt.h" #include "utils/malloc.h" #include "textio/textio.h" #include "utils/tech.h" #include "debug/debug.h" #include "extract/extract.h" #include "extract/extractInt.h" #include "cif/CIFint.h" #include "cif/cif.h" /* Whether we are converting units from microns to lambda */ bool doConvert; /* Current extraction style */ ExtStyle *ExtCurStyle = NULL; /* List of all styles */ ExtKeep *ExtAllStyles = NULL; /* Mask of all types found in the extract section */ TileTypeBitMask *allExtractTypes = NULL; /* Forward declarations */ void extTechFinalStyle(); void ExtLoadStyle(); void ExtTechScale(int, int); /* This is a placeholder value; it may be approximately * constant across processes, or it may need to be set * per process. */ #define FRINGE_MULT 0.02 /* * Table used for parsing the extract section of a .tech file * Each line in the extract section is of a type determined by * its first keyword. There is one entry in the following table * for each such keyword. */ typedef enum { AREAC, CONTACT, CSCALE, DEFAULTAREACAP, DEFAULTOVERLAP, DEFAULTPERIMETER, DEFAULTSIDEOVERLAP, DEFAULTSIDEWALL, DEVICE, DEVRESIST, FET, FETRESIST, FRINGESHIELDHALO, HEIGHT, ANTENNA, MODEL, TIEDOWN, LAMBDA, OVERC, PERIMC, PLANEORDER, NOPLANEORDER, RESIST, RSCALE, SIDEHALO, SIDEOVERLAP, SIDEWALL, STEP, STYLE, SUBSTRATE, UNITS, VARIANT } Key; typedef struct { const char *k_name; int k_key; int k_minargs; int k_maxargs; const char *k_usage; } keydesc; static const keydesc keyTable[] = { {"areacap", AREAC, 3, 3, "types capacitance"}, {"contact", CONTACT, 3, 6, "type resistance"}, {"cscale", CSCALE, 2, 2, "capacitance-scalefactor"}, {"defaultareacap", DEFAULTAREACAP, 4, 6, "types plane capacitance"}, {"defaultoverlap", DEFAULTOVERLAP, 6, 6, "types plane otertypes otherplane capacitance"}, {"defaultperimeter", DEFAULTPERIMETER, 4, 6, "types plane capacitance"}, {"defaultsideoverlap", DEFAULTSIDEOVERLAP, 6, 6, "types plane othertypes otherplane capacitance"}, {"defaultsidewall", DEFAULTSIDEWALL, 4, 5, "types plane capacitance [offset]"}, {"device", DEVICE, 4, 10, "dev-type dev-name types options..."}, {"devresist", DEVRESIST, 4, 4, "type region ohms-per-square"}, {"fet", FET, 8, 9, "types terminal-types min-#-terminals name [subs-types] subs-node gscap gate-chan-cap"}, {"fetresist", FETRESIST, 4, 4, "type region ohms-per-square"}, {"fringeshieldhalo", FRINGESHIELDHALO, 2, 2, "distance"}, {"height", HEIGHT, 4, 4, "type height-above-subtrate thickness"}, {"antenna", ANTENNA, 4, 6, "type [calc-type] [antenna-ratio-proportional] antenna-ratio-const"}, {"model", MODEL, 2, 3, "partial-cumulative [area-sidewall]"}, {"tiedown", TIEDOWN, 2, 2, "types"}, {"lambda", LAMBDA, 2, 2, "units-per-lambda"}, {"overlap", OVERC, 4, 5, "toptypes bottomtypes capacitance [shieldtypes]"}, {"perimc", PERIMC, 4, 4, "intypes outtypes capacitance"}, {"planeorder", PLANEORDER, 3, 3, "plane index"}, {"noplaneordering", NOPLANEORDER, 1, 1, "(no arguments needed)"}, {"resist", RESIST, 3, 4, "types resistance"}, {"rscale", RSCALE, 2, 2, "resistance-scalefactor"}, {"sidehalo", SIDEHALO, 2, 2, "distance"}, {"sideoverlap", SIDEOVERLAP, 5, 6, "intypes outtypes ovtypes capacitance [shieldtypes]"}, {"sidewall", SIDEWALL, 6, 7, "intypes outtypes neartypes fartypes capacitance [offset]"}, {"step", STEP, 2, 2, "size"}, {"style", STYLE, 2, 4, "stylename"}, {"substrate", SUBSTRATE, 3, 5, "types plane [subs-node]"}, {"units", UNITS, 2, 2, "lambda|microns"}, {"variants", VARIANT, 2, 2, "style,..."}, {0} }; /* * Table used for parsing the "device" keyword types * * (Note: "10" for max types in subcircuit is arbitrary---the parser * ignores max types for DEV_SUBCKT, DEV_MSUBCKT, and DEV_VERILOGA). */ /* types are enumerated in extract.h */ static const keydesc devTable[] = { {"mosfet", DEV_MOSFET, 5, 10, "name gate-types src-types [drn-types] sub-types|None sub-node [gscap gccap]"}, {"bjt", DEV_BJT, 5, 5, "name base-types emitter-types collector-types"}, {"capacitor", DEV_CAP, 4, 8, "name top-types bottom-types [sub-types|None sub-node] [[perimcap] areacap]"}, {"capreverse", DEV_CAPREV, 4, 8, "name bottom-types top-types [sub-types|None sub-node] [[perimcap] areacap]"}, {"resistor", DEV_RES, 4, 6, "name|None res-types terminal-types [sub-types|None sub-node]"}, {"diode", DEV_DIODE, 4, 6, "name pos-types neg-types [sub-types|None sub-node]"}, {"pdiode", DEV_PDIODE, 4, 6, "name pos-types neg-types [sub-types|None sub-node]"}, {"ndiode", DEV_NDIODE, 4, 6, "name neg-types pos-types [sub-types|None sub-node]"}, {"subcircuit", DEV_SUBCKT, 3, 11, "name dev-types [N] [term1-types ... termN-types [sub-types|None sub-node]] [options]"}, {"rsubcircuit", DEV_RSUBCKT, 4, 7, "name dev-types terminal-types [sub-types|None sub-node] [options]"}, {"msubcircuit", DEV_MSUBCKT, 3, 11, "name dev-types [N] [term1-types ... termN-types [sub-types|None sub-node]] [options]"}, {"csubcircuit", DEV_CSUBCKT, 4, 7, "name dev-types terminal-types [sub-types|None sub-node] [options]"}, {"veriloga", DEV_VERILOGA, 3, 11, "name dev-types [N] [term1-types ... termN-types [sub-types|None sub-node]] [options]"}, {0} }; #ifdef MAGIC_WRAPPER /* * ---------------------------------------------------------------------------- * * ExtCompareStyle -- * * This routine is designed to work with embedded exttosim and * exttospice. It determines whether the current extract style * matches the string (picked up from the .ext file). If so, it * returns TRUE. If not, it checks whether the style exists in * the list of known files for this technology. If so, it loads * the correct style and returns TRUE. If not, it returns FALSE. * * ---------------------------------------------------------------------------- */ bool ExtCompareStyle(stylename) char *stylename; { ExtKeep *style; if (!strcmp(stylename, ExtCurStyle->exts_name)) return TRUE; else { for (style = ExtAllStyles; style != NULL; style = style->exts_next) { if (!strcmp(stylename, style->exts_name)) { ExtLoadStyle(stylename); return TRUE; } } } return FALSE; } #endif /* MAGIC_WRAPPER */ /* * ---------------------------------------------------------------------------- * * ExtGetDevInfo -- * * This routine is designed to work with the embedded exttosim and * exttospice commands under the Tcl-based magic, such that all * device information needed by these commands can be picked up * from the current extraction style (as it should!). This * really should be set up when the extract file is read, which is * why the routine is here, although this is not a very efficient * way to do it (but it needs to be this way to keep backward * compatibility with the non-Tcl, standalone programs ext2sim and * ext2spice). * * Note that finding the device by index ("idx") is horribly * inefficient, but keeps the netlist generator separated from * the extractor. Some of this code is seriously schizophrenic, * and should not be investigated too closely. * * Results: * Return FALSE if no device corresponds to index "idx". TRUE * otherwise. * * Side Effects: * Fills values in the argument list. * * Notes: * The original sd_rclassptr has been expanded to s_rclassptr and * d_rclassptr to capture asymmetric devices, bipolars, etc. Note * that this is not a general-purpose method extending beyond two * (non-gate) terminals, and should be updated. * * ---------------------------------------------------------------------------- */ bool ExtGetDevInfo(idx, devnameptr, devtypeptr, s_rclassptr, d_rclassptr, sub_rclassptr, subnameptr) int idx; char **devnameptr; /* Name of extracted device model */ TileType *devtypeptr; /* Magic tile type of device */ short *s_rclassptr; /* Source (1st terminal) type only */ short *d_rclassptr; /* Drain (2nd terminal) type only */ short *sub_rclassptr; char **subnameptr; { TileType t; TileTypeBitMask *rmask, *tmask; int n, i = 0, j; bool repeat, found; ExtDevice *devptr; char *locdname; char **uniquenamelist = (char **)mallocMagic(DBNumTypes * sizeof(char *)); found = FALSE; for (t = TT_TECHDEPBASE; t < DBNumTypes; t++) { for (devptr = ExtCurStyle->exts_device[t]; devptr; devptr = devptr->exts_next) { locdname = devptr->exts_deviceName; if (locdname != NULL) { repeat = FALSE; for (j = 0; j < i; j++) if (!strcmp(uniquenamelist[j], locdname)) { repeat = TRUE; break; } if (repeat == FALSE) { if (i == idx) { found = TRUE; break; } uniquenamelist[i] = locdname; i++; } } } if (found == TRUE) break; } if (t == DBNumTypes) { freeMagic(uniquenamelist); return FALSE; } if (devptr == NULL) { freeMagic(uniquenamelist); return FALSE; } if (devnameptr) *devnameptr = locdname; if (subnameptr) *subnameptr = devptr->exts_deviceSubstrateName; if (devtypeptr) *devtypeptr = t; tmask = &devptr->exts_deviceSDTypes[0]; if (s_rclassptr) { *s_rclassptr = (short)(-1); /* NO_RESCLASS */ for (n = 0; n < ExtCurStyle->exts_numResistClasses; n++) { rmask = &ExtCurStyle->exts_typesByResistClass[n]; if (TTMaskIntersect(rmask, tmask)) { *s_rclassptr = (short)n; break; } } } if (d_rclassptr) { tmask = &devptr->exts_deviceSDTypes[1]; if (TTMaskIsZero(tmask)) { /* Set source and drain resistance classes to be the same */ *d_rclassptr = (short)n; } else { *d_rclassptr = (short)(-1); /* NO_RESCLASS */ for (n = 0; n < ExtCurStyle->exts_numResistClasses; n++) { rmask = &ExtCurStyle->exts_typesByResistClass[n]; if (TTMaskIntersect(rmask, tmask)) { *d_rclassptr = (short)n; break; } } } } if (sub_rclassptr) { tmask = &devptr->exts_deviceSubstrateTypes; *sub_rclassptr = (short)(-1); /* NO_RESCLASS */ for (n = 0; n < ExtCurStyle->exts_numResistClasses; n++) { rmask = &ExtCurStyle->exts_typesByResistClass[n]; if (TTMaskIntersect(rmask, tmask)) { *sub_rclassptr = (short)(n); break; } } } freeMagic(uniquenamelist); return TRUE; } /* * ---------------------------------------------------------------------------- * * extGetDevType -- * * Given an extraction model device name (devname), return the associated * magic tiletype for the device. * * Results: * Tile type that represents the device "devname" in the magic database. * * Notes: * It is possible for the extract section to define multiple tile types * that produce the same extracted device name, so the returned TileType * is not guaranteed to be unique to the device name. * * ---------------------------------------------------------------------------- */ TileType extGetDevType(devname) char *devname; { TileType t; ExtDevice *devptr; for (t = TT_TECHDEPBASE; t < DBNumTypes; t++) for (devptr = ExtCurStyle->exts_device[t]; devptr; devptr = devptr->exts_next) if (!strcmp(devptr->exts_deviceName, devname)) return t; return -1; } /* * ---------------------------------------------------------------------------- * * ExtGetGateTypesMask -- * * Put a mask of FET gate types in the mask pointer argument. * Return 0 on success, 1 on failure (no extraction style set) * * ---------------------------------------------------------------------------- */ int ExtGetGateTypesMask(mask) TileTypeBitMask *mask; { TileType ttype; if (ExtCurStyle == NULL) return 1; TTMaskZero(mask); for (ttype = TT_TECHDEPBASE; ttype < DBNumTypes; ttype++) { if (TTMaskHasType(&ExtCurStyle->exts_deviceMask, ttype)) { ExtDevice *devptr = ExtCurStyle->exts_device[ttype]; for (; devptr; devptr = devptr->exts_next) if ((devptr->exts_deviceClass == DEV_MOSFET) || (devptr->exts_deviceClass == DEV_FET) || (devptr->exts_deviceClass == DEV_ASYMMETRIC) || (devptr->exts_deviceClass == DEV_MSUBCKT)) TTMaskSetType(mask, ttype); } } return 0; } /* * ---------------------------------------------------------------------------- * * ExtGetDiffTypesMask -- * * Put a mask of diffusion types in the mask pointer argument. * Return 0 on success, 1 on failure (no extraction style set) * * ---------------------------------------------------------------------------- */ int ExtGetDiffTypesMask(mask) TileTypeBitMask *mask; { TileType ttype; if (ExtCurStyle == NULL) return 1; TTMaskZero(mask); TTMaskSetMask(mask, &ExtCurStyle->exts_antennaTieTypes); return 0; } #ifdef THREE_D /* * ---------------------------------------------------------------------------- * * ExtGetZAxis -- * * Get the height and thickness parameters for a layer (used by the * graphics module which does not have access to internal variables * in the extract section). * * Results: * None * * Side Effects: * Fills values "height" and "thick" in argument list. * * ---------------------------------------------------------------------------- */ void ExtGetZAxis(tile, height, thick) Tile *tile; float *height, *thick; { TileType ttype; if (ExtCurStyle == NULL) return; ttype = TiGetLeftType(tile); /* Ignore non-Manhattan for now */ /* Note that w_scale is multiplied by SUBPIXEL to get fixed-point accuracy. */ /* However, we downshift by only 9 (divide by 512) so that heights are */ /* exaggerated in the layout by a factor of 8 (height exaggeration is */ /* standard practice for VLSI cross-sections). */ *height = ExtCurStyle->exts_height[ttype]; *thick = ExtCurStyle->exts_thick[ttype]; } #endif /* THREE_D */ /* * ---------------------------------------------------------------------------- * * ExtPrintPath -- * * Print the path where extraction files will be written * * Results: * None. * * Side effects: * Output. * * ---------------------------------------------------------------------------- */ void ExtPrintPath(dolist) bool dolist; { if (ExtLocalPath == NULL) { if (dolist) { #ifdef MAGIC_WRAPPER Tcl_SetObjResult(magicinterp, Tcl_NewStringObj("(none)", -1)); #else TxPrintf("(none)\n"); #endif } else TxPrintf("(none)\n"); } else { if (dolist) { #ifdef MAGIC_WRAPPER Tcl_SetObjResult(magicinterp, Tcl_NewStringObj(ExtLocalPath, -1)); #else TxPrintf("%s\n", ExtLocalPath); #endif } else TxPrintf("The extraction path is: %s\n", ExtLocalPath); } } /* * ---------------------------------------------------------------------------- * * ExtSetPath -- * * Set the current extraction path to 'path', unless 'path' is "none" or * "(none)", in which case clear the path and set it to NULL. * * Results: * None. * * Side effects: * Just told you. * * ---------------------------------------------------------------------------- */ void ExtSetPath(path) char *path; { if (path != NULL) { if (!strcasecmp(path, "none") || !strcasecmp(path, "(none)") || !strcasecmp(path, "null")) { StrDup(&ExtLocalPath, NULL); return; } } StrDup(&ExtLocalPath, path); return; } /* * ---------------------------------------------------------------------------- * * ExtPrintStyle -- * * Print the available and/or current extraction styles. * * Results: * None. * * Side effects: * Output. * * ---------------------------------------------------------------------------- */ void ExtPrintStyle(dolist, doforall, docurrent) bool dolist; bool doforall; bool docurrent; { ExtKeep *style; if (docurrent) { if (ExtCurStyle == 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 *) ExtCurStyle->exts_name, NULL); else #endif TxPrintf("%s", ExtCurStyle->exts_name); if (!dolist) TxPrintf("\".\n"); } } if (doforall) { if (!dolist) TxPrintf("The extraction styles are: "); for (style = ExtAllStyles; style; style = style->exts_next) { if (dolist) { #ifdef MAGIC_WRAPPER Tcl_AppendElement(magicinterp, style->exts_name); #else if (style != ExtAllStyles) TxPrintf(" "); TxPrintf("%s", style->exts_name); #endif } else { if (style != ExtAllStyles) TxPrintf(", "); TxPrintf("%s", style->exts_name); } } if (!dolist) TxPrintf(".\n"); } } /* * ---------------------------------------------------------------------------- * * ExtSetStyle -- * * Set the current extraction style to 'name', or print * the available and current styles if 'name' is NULL. * * Results: * None. * * Side effects: * Just told you. * * ---------------------------------------------------------------------------- */ void ExtSetStyle(name) char *name; { ExtKeep *style, *match; int length; if (name == NULL) return; match = NULL; length = strlen(name); for (style = ExtAllStyles; style; style = style->exts_next) { if (strncmp(name, style->exts_name, length) == 0) { if (match != NULL) { TxError("Extraction style \"%s\" is ambiguous.\n", name); ExtPrintStyle(FALSE, TRUE, TRUE); return; } match = style; } } if (match != NULL) { ExtLoadStyle(match->exts_name); TxPrintf("Extraction style is now \"%s\"\n", name); return; } TxError("\"%s\" is not one of the extraction styles Magic knows.\n", name); ExtPrintStyle(FALSE, TRUE, TRUE); } /* * ---------------------------------------------------------------------------- * * extTechStyleAlloc -- * * Allocate memory for a new extract style structure. * * ---------------------------------------------------------------------------- */ ExtStyle * extTechStyleAlloc() { ExtStyle *style; TileType r; style = (ExtStyle *) mallocMagic(sizeof (ExtStyle)); return style; } /* * ---------------------------------------------------------------------------- * * extTechStyleInit -- * * Fill in the extract style structure with initial values. * * ---------------------------------------------------------------------------- */ void extTechStyleInit(style) ExtStyle *style; { TileType r, s; style->exts_name = NULL; style->exts_status = TECH_NOT_LOADED; style->exts_sidePlanes = style->exts_overlapPlanes = 0; TTMaskZero(&style->exts_deviceMask); style->exts_activeTypes = DBAllButSpaceAndDRCBits; for (r = 0; r < NP; r++) { TTMaskZero(&style->exts_sideTypes[r]); TTMaskZero(&style->exts_overlapTypes[r]); style->exts_planeOrder[r] = -1; } for (r = 0; r < NT; r++) { TTMaskZero(&style->exts_nodeConn[r]); TTMaskZero(&style->exts_resistConn[r]); TTMaskZero(&style->exts_deviceConn[r]); style->exts_allConn[r] = DBAllTypeBits; style->exts_sheetResist[r] = 0; style->exts_cornerChop[r] = 1.0; style->exts_viaResist[r] = (ResValue) 0; style->exts_height[r] = 0.0; style->exts_thick[r] = 0.0; style->exts_areaCap[r] = (CapValue) 0; style->exts_overlapOtherPlanes[r] = 0; TTMaskZero(&style->exts_overlapOtherTypes[r]); TTMaskZero(&style->exts_sideEdges[r]); for (s = 0; s < NT; s++) { TTMaskZero(&style->exts_sideCoupleOtherEdges[r][s]); TTMaskZero(&style->exts_sideOverlapOtherTypes[r][s]); style->exts_sideOverlapOtherPlanes[r][s] = 0; style->exts_sideCoupleCap[r][s] = (EdgeCap *) NULL; style->exts_sideOverlapCap[r][s] = (EdgeCap *) NULL; style->exts_perimCap[r][s] = (CapValue) 0; style->exts_overlapCap[r][s] = (CapValue) 0; style->exts_overlapMult[r][s] = (float) 0; TTMaskZero(&style->exts_overlapShieldTypes[r][s]); style->exts_overlapShieldPlanes[r][s] = 0; style->exts_sideOverlapShieldPlanes[r][s] = 0; } TTMaskZero(&style->exts_perimCapMask[r]); #ifdef ARIEL TTMaskZero(&style->exts_subsTransistorTypes[r]); #endif if (style->exts_device[r] != NULL) { ExtDevice *devptr; for (devptr = style->exts_device[r]; devptr; devptr = devptr->exts_next) { if (devptr->exts_deviceSDTypes != NULL) freeMagic(devptr->exts_deviceSDTypes); if (devptr->exts_deviceSubstrateName != (char *) NULL) freeMagic(devptr->exts_deviceSubstrateName); if (devptr->exts_deviceName != (char *) NULL) freeMagic(devptr->exts_deviceName); while (devptr->exts_deviceParams != (ParamList *) NULL) { /* Parameter lists are shared. Only free the last one! */ if (devptr->exts_deviceParams->pl_count > 1) { devptr->exts_deviceParams->pl_count--; devptr->exts_deviceParams = (ParamList *)NULL; } else { if (devptr->exts_deviceParams->pl_name != NULL) freeMagic(devptr->exts_deviceParams->pl_name); freeMagic(devptr->exts_deviceParams); devptr->exts_deviceParams = devptr->exts_deviceParams->pl_next; } } if (devptr->exts_deviceResist.ht_table != (HashEntry **) NULL) HashKill(&devptr->exts_deviceResist); freeMagic(devptr); } style->exts_device[r] = (ExtDevice *)NULL; } } style->exts_sideCoupleHalo = 0; style->exts_stepSize = 100; style->exts_unitsPerLambda = 100.0; style->exts_resistScale = 1000; style->exts_capScale = 1000; style->exts_numResistClasses = 0; style->exts_planeOrderStatus = needPlaneOrder ; TTMaskZero(&style->exts_antennaTieTypes); for (r = 0; r < DBNumTypes; r++) { style->exts_antennaRatio[r].areaType = (char)0; style->exts_antennaRatio[r].ratioGate = 0.0; style->exts_antennaRatio[r].ratioDiffA = 0.0; style->exts_antennaRatio[r].ratioDiffB = 0.0; style->exts_resistByResistClass[r] = (ResValue) 0; TTMaskZero(&style->exts_typesByResistClass[r]); style->exts_typesResistChanged[r] = DBAllButSpaceAndDRCBits; TTMaskSetType(&style->exts_typesResistChanged[r], TT_SPACE); style->exts_typeToResistClass[r] = -1; } doConvert = FALSE; // The exts_globSubstratePlane setting of -1 will be used to set a // backwards-compatibility mode matching previous behavior with // respect to the substrate when there is no "substrate" line in // the techfile. style->exts_globSubstratePlane = -1; style->exts_globSubstrateDefaultType = -1; TTMaskZero(&style->exts_globSubstrateTypes); TTMaskZero(&style->exts_globSubstrateShieldTypes); style->exts_globSubstrateName = (char *)NULL; } /* * ---------------------------------------------------------------------------- * * extTechStyleNew -- * * Allocate a new style with zeroed technology variables. * * Results: * Allocates a new ExtStyle, initializes it, and returns it. * * Side effects: * See above. * * ---------------------------------------------------------------------------- */ ExtStyle * extTechStyleNew() { ExtStyle *style; TileType r; style = extTechStyleAlloc(); /* This entry in ExtStyle is allocated and must be initialized */ for (r = 0; r < NT; r++) style->exts_device[r] = NULL; extTechStyleInit(style); return style; } /* * ---------------------------------------------------------------------------- * * aToCap -- * * Utility procedure for reading capacitance values. * * Returns: * A value of type CapValue. * * Side effects: * none. * ---------------------------------------------------------------------------- */ CapValue aToCap(str) char *str; { CapValue capVal; if (sscanf(str, "%lf", &capVal) != 1) { capVal = (CapValue) 0; TechError("Capacitance value %s must be a number\n", str); } return capVal; } /* * ---------------------------------------------------------------------------- * * aToRes -- * * Utility procedure for reading resistance values. * * Returns: * A value of type ResValue. * * Side effects: * none. * ---------------------------------------------------------------------------- */ ResValue aToRes(str) char *str; { ResValue resVal; if (sscanf(str, "%d", &resVal) != 1) { resVal = (ResValue) 0; TechError("Resistance value %s must be a number\n", str); } return resVal; } /* * ---------------------------------------------------------------------------- * * ExtLoadStyle -- * * Re-read the technology file to load the specified technology extraction * style into structure ExtCurStyle. This is much more memory-efficient than * keeping a separate (and huge!) ExtStyle structure for each extraction style. * 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 default, so * that re-reading the tech file is not necessary on startup unless the default * extraction style is changed by a call to "extract style". * * ---------------------------------------------------------------------------- */ void ExtLoadStyle(stylename) char *stylename; { SectionID invext; extTechStyleInit(ExtCurStyle); /* Reinitialize and mark as not */ ExtCurStyle->exts_name = stylename; /* loaded. */ /* Invalidate the extract section, and reload it. */ /* The second parameter to TechSectionGetMask is NULL because */ /* no other tech client sections depend on the extract section. */ invext = TechSectionGetMask("extract", NULL); /* If microns are used as units, then the TechLoad needs to convert */ /* units based on *unscaled* dimensions. Since it gets the scale */ /* factor from CIFGetOutputScale(), the CIF units need to be */ /* unscaled. This is cumbersome but ensures that the right units */ /* are obtained. */ CIFTechOutputScale(DBLambda[1], DBLambda[0]); TechLoad(NULL, invext); /* Put the CIF output scale units back to what they were */ CIFTechOutputScale(DBLambda[0], DBLambda[1]); /* extTechFinalStyle(ExtCurStyle); */ /* Taken care of by TechLoad() */ ExtTechScale(DBLambda[0], DBLambda[1]); } /* * ---------------------------------------------------------------------------- * * ExtTechInit -- * * Ensure that all memory allocated to the extract database has * been freed up. Called before loading a new technology. * * ---------------------------------------------------------------------------- */ void ExtTechInit() { ExtKeep *style; int r; /* Delete everything in ExtCurStyle */ if (ExtCurStyle != NULL) { extTechStyleInit(ExtCurStyle); ExtCurStyle = NULL; } /* Forget all the extract style names */ for (style = ExtAllStyles; style != NULL; style = style->exts_next) { freeMagic(style->exts_name); freeMagic(style); } ExtAllStyles = NULL; if (allExtractTypes == NULL) allExtractTypes = (TileTypeBitMask *)mallocMagic(sizeof(TileTypeBitMask)); TTMaskZero(allExtractTypes); } /* * ---------------------------------------------------------------------------- * * ExtTechSimpleAreaCap -- * * Parse the techfile line for the "defaultareacap" keyword. * This is equivalent to the "areacap" line but also applies * to "overlap" of types on the second plane (if specified) and * all planes below it, with appropriate intervening types. * * Usage: * defaultareacap types plane [[subtypes] subplane] value * * where "types" are the types for which to compute area cap, * "plane" is the plane of "types", "subplane" is a plane * containing wells or any types that have the same coupling * as the substrate. If absent, it is assumed that nothing * shields "types" from the subtrate. Additional optional * "subtypes" is a list of types in "subplane" that shield. * If absent, then all types in "subplane" are shields to the * substrate. All types specified in the "substrate" statement * as shielding substrate are automatically included. "value" * is the area capacitance in aF/um^2. * * Results: * None. * * Side Effects: * Adds information into the ExtCurStyle records. * * ---------------------------------------------------------------------------- */ void ExtTechSimpleAreaCap(argc, argv) int argc; char *argv[]; { TileType s, t; TileTypeBitMask types, subtypes, shields; CapValue capVal; float multVal; int plane1, plane2, plane3, pnum1, pnum2, pnum3; PlaneMask pshield; if (ExtCurStyle->exts_planeOrderStatus != seenPlaneOrder) { TechError("Cannot parse area cap line without plane ordering!\n"); return; } DBTechNoisyNameMask(argv[1], &types); TTMaskSetMask(allExtractTypes, &types); plane1 = DBTechNoisyNamePlane(argv[2]); TTMaskAndMask(&types, &DBPlaneTypes[plane1]); capVal = aToCap(argv[argc - 1]); if (argc == 4) plane2 = ExtCurStyle->exts_globSubstratePlane; else plane2 = DBTechNoisyNamePlane(argv[argc - 2]); if (argc > 5) { DBTechNoisyNameMask(argv[argc - 3], &subtypes); TTMaskSetMask(allExtractTypes, &subtypes); } else TTMaskZero(&subtypes); /* Part 1: Area cap */ for (t = TT_TECHDEPBASE; t < DBNumTypes; t++) if (TTMaskHasType(&types, t)) { ExtCurStyle->exts_areaCap[t] = capVal; ExtCurStyle->exts_overlapMult[t][0] = (float) capVal * FRINGE_MULT; ExtCurStyle->exts_overlapMult[0][t] = (float) capVal * FRINGE_MULT; } if ((plane2 == -1) && (ExtCurStyle->exts_globSubstratePlane == -1)) return; else if (plane1 == plane2) return; /* shouldn't happen */ pnum1 = ExtCurStyle->exts_planeOrder[plane1]; if (plane2 != -1) pnum2 = ExtCurStyle->exts_planeOrder[plane2]; /* Part 2: Overlap cap on types equivalent to substrate */ /* Find all types in or below plane2 (i.e., ~(space)/plane2) */ /* Shield types are everything in the planes between plane1 and plane2 */ TTMaskZero(&shields); pshield = 0; if (plane2 != -1) { for (plane3 = PL_TECHDEPBASE; plane3 < DBNumPlanes; plane3++) { pnum3 = ExtCurStyle->exts_planeOrder[plane3]; if (pnum3 > pnum2 && pnum3 < pnum1) { TTMaskSetMask(&shields, &DBPlaneTypes[plane3]); pshield |= PlaneNumToMaskBit(plane3); } else if (pnum3 <= pnum2) { TTMaskAndMask(&subtypes, &DBPlaneTypes[plane3]); TTMaskClearType(&subtypes, TT_SPACE); } TTMaskClearType(&shields, TT_SPACE); } } /* Defaults from the "substrate" line, if used, and if the arguments */ /* do not specify the plane and shielding types. */ if ((ExtCurStyle->exts_globSubstratePlane != -1) && (argc == 4)) { TTMaskSetMask(&subtypes, &ExtCurStyle->exts_globSubstrateTypes); TTMaskClearMask(&subtypes, &ExtCurStyle->exts_globSubstrateShieldTypes); TTMaskClearType(&subtypes, TT_SPACE); /* If the substrate type is used for isolated substrate regions, */ /* then the substrate plane is also a shielding plane, and the */ /* default substrate type is a shielding type. */ if (ExtCurStyle->exts_globSubstrateDefaultType != -1) { pshield |= PlaneNumToMaskBit(ExtCurStyle->exts_globSubstratePlane); /* The substrate default type now marks isolated regions */ /* and so is not itself a substrate type. */ TTMaskClearType(&subtypes, ExtCurStyle->exts_globSubstrateDefaultType); /* All types on the substrate plane that are not substrate */ /* are by definition shielding types. */ for (t = TT_TECHDEPBASE; t < DBNumTypes; t++) if (DBPlane(t) == ExtCurStyle->exts_globSubstratePlane) if (!TTMaskHasType(&ExtCurStyle->exts_globSubstrateTypes, t)) TTMaskSetType(&shields, t); } } /* Now record all of the overlap capacitances */ for (s = TT_TECHDEPBASE; s < DBNumTypes; s++) { if (TTMaskHasType(&types, s)) { /* Contact overlap caps are determined from residues */ if (DBIsContact(s)) continue; for (t = TT_TECHDEPBASE; t < DBNumTypes; t++) { if (!TTMaskHasType(&subtypes, t)) continue; if (s == t) continue; /* shouldn't happen */ if (ExtCurStyle->exts_overlapCap[s][t] > (CapValue) 0) continue; /* redundant overlap */ ExtCurStyle->exts_overlapCap[s][t] = capVal; ExtCurStyle->exts_overlapMult[s][t] = (float)capVal * FRINGE_MULT; ExtCurStyle->exts_overlapMult[t][s] = (float)capVal * FRINGE_MULT; ExtCurStyle->exts_overlapPlanes |= PlaneNumToMaskBit(plane1); if (plane2 != -1) ExtCurStyle->exts_overlapOtherPlanes[s] |= PlaneNumToMaskBit(plane2); TTMaskSetType(&ExtCurStyle->exts_overlapTypes[plane1], s); TTMaskSetType(&ExtCurStyle->exts_overlapOtherTypes[s], t); ExtCurStyle->exts_overlapShieldPlanes[s][t] = pshield; ExtCurStyle->exts_overlapShieldTypes[s][t] = shields; } } } } /* * ---------------------------------------------------------------------------- * * ExtTechSimplePerimCap -- * * Parse the techfile line for the "defaultperimeter" keyword. * This comprises both the "perimc" statement and the "sideoverlap" * statement for overlaps to types that are effectively substrate * (e.g., well, implant, marker layers, etc.) * * Usage: * defaultperimeter types plane [[subtypes] subplane] value * * where "types" are the types for which to compute fringing cap, * "plane" is the plane of the types, "subplane" is an optional * plane that shields "types" from substrate, and "value" is the * fringing cap in aF/micron. If "subplane" is omitted, then * nothing shields "types" from the substrate. Optional "subtypes" * lists the types in "subplane" that shield. Otherwise, it is * assumed that all types in "subplane" shield "types" from the * substrate. Additionally, all types declared to shield the * substrate are included. * * Results: * None. * * Side Effects: * Adds information into the ExtCurStyle records. * * ---------------------------------------------------------------------------- */ void ExtTechSimplePerimCap(argc, argv) int argc; char *argv[]; { TileType r, s, t; TileTypeBitMask types, nottypes, subtypes, shields; CapValue capVal; int plane1, plane2, plane3, pnum1, pnum2, pnum3; PlaneMask pshield; EdgeCap *cnew; if (ExtCurStyle->exts_planeOrderStatus != seenPlaneOrder) { TechError("Cannot parse area cap line without plane ordering!\n"); return; } DBTechNoisyNameMask(argv[1], &types); TTMaskSetMask(allExtractTypes, &types); plane1 = DBTechNoisyNamePlane(argv[2]); TTMaskCom2(¬types, &types); TTMaskAndMask(&types, &DBPlaneTypes[plane1]); TTMaskAndMask(¬types, &DBPlaneTypes[plane1]); capVal = aToCap(argv[argc - 1]); if (argc == 4) plane2 = ExtCurStyle->exts_globSubstratePlane; else plane2 = DBTechNoisyNamePlane(argv[argc - 2]); if (argc > 5) { DBTechNoisyNameMask(argv[argc - 3], &subtypes); TTMaskSetMask(allExtractTypes, &subtypes); } else if (ExtCurStyle->exts_globSubstratePlane != -1) { TTMaskZero(&subtypes); TTMaskSetMask(&subtypes, &ExtCurStyle->exts_globSubstrateTypes); } else TTMaskSetOnlyType(&subtypes, TT_SPACE); /* Part 1: Perimeter cap */ for (s = TT_TECHDEPBASE; s < DBNumTypes; s++) for (t = 0; t < DBNumTypes; t++) if (TTMaskHasType(&types, s) && TTMaskHasType(¬types, t)) { ExtCurStyle->exts_perimCap[s][t] = capVal; TTMaskSetType(&ExtCurStyle->exts_perimCapMask[s], t); } if ((plane2 == -1) && (ExtCurStyle->exts_globSubstratePlane == -1)) return; else if (plane1 == plane2) return; /* shouldn't happen */ pnum1 = ExtCurStyle->exts_planeOrder[plane1]; if (plane2 != -1) pnum2 = ExtCurStyle->exts_planeOrder[plane2]; /* Part 2: Sidewall overlap cap on types equivalent to substrate */ /* Find all types in or below plane2 (i.e., ~(space)/plane2) */ /* Shield types are everything in the planes between plane1 and plane2 */ TTMaskZero(&shields); pshield = 0; if (plane2 != -1) { for (plane3 = PL_TECHDEPBASE; plane3 < DBNumPlanes; plane3++) { pnum3 = ExtCurStyle->exts_planeOrder[plane3]; if (pnum3 > pnum2 && pnum3 < pnum1) { TTMaskSetMask(&shields, &DBPlaneTypes[plane3]); pshield |= PlaneNumToMaskBit(plane3); } else if (pnum3 <= pnum2) { TTMaskAndMask(&subtypes, &DBPlaneTypes[plane3]); } } TTMaskClearType(&shields, TT_SPACE); TTMaskClearType(&subtypes, TT_SPACE); } /* Defaults from the "substrate" line, if used, and if the arguments */ /* do not specify the plane and shielding types. */ if ((ExtCurStyle->exts_globSubstratePlane != -1) && (argc == 4)) { TTMaskSetMask(&subtypes, &ExtCurStyle->exts_globSubstrateTypes); TTMaskClearMask(&subtypes, &ExtCurStyle->exts_globSubstrateShieldTypes); TTMaskClearType(&subtypes, TT_SPACE); /* If the substrate type is used for isolated substrate regions, */ /* then the substrate plane is also a shielding plane, and the */ /* default substrate type is a shielding type. */ if (ExtCurStyle->exts_globSubstrateDefaultType != -1) { pshield |= PlaneNumToMaskBit(ExtCurStyle->exts_globSubstratePlane); /* The substrate default type now marks isolated regions */ /* and so is not itself a substrate type. */ TTMaskClearType(&subtypes, ExtCurStyle->exts_globSubstrateDefaultType); /* But space is */ TTMaskSetType(&subtypes, TT_SPACE); /* All types on the substrate plane that are not substrate */ /* are by definition shielding types. */ for (t = TT_TECHDEPBASE; t < DBNumTypes; t++) if (DBPlane(t) == ExtCurStyle->exts_globSubstratePlane) if (!TTMaskHasType(&ExtCurStyle->exts_globSubstrateTypes, t)) TTMaskSetType(&shields, t); } } /* Record all of the sideoverlap capacitances */ for (s = TT_TECHDEPBASE; s < DBNumTypes; s++) { /* Side overlap computed from residues */ if (DBIsContact(s)) continue; if (TTMaskHasType(&types, s)) // Corrected, 2/21/2017 { ExtCurStyle->exts_sidePlanes |= PlaneNumToMaskBit(plane1); TTMaskSetType(&ExtCurStyle->exts_sideTypes[plane1], s); TTMaskSetMask(&ExtCurStyle->exts_sideEdges[s], ¬types); for (t = 0; t < DBNumTypes; t++) { if (!TTMaskHasType(¬types, t)) continue; if (DBIsContact(t)) continue; TTMaskSetMask(&ExtCurStyle->exts_sideOverlapOtherTypes[s][t], &subtypes); if (plane2 != -1) ExtCurStyle->exts_sideOverlapOtherPlanes[s][t] |= PlaneNumToMaskBit(plane2); cnew = (EdgeCap *) mallocMagic((unsigned) (sizeof (EdgeCap))); cnew->ec_cap = capVal; cnew->ec_offset = 0; /* No offsets on perimeter caps */ cnew->ec_far = shields; /* Types that shield */ cnew->ec_near = subtypes; /* Types we create cap with */ if (plane2 != -1) cnew->ec_pmask = PlaneNumToMaskBit(plane2); else cnew->ec_pmask = 0; cnew->ec_next = ExtCurStyle->exts_sideOverlapCap[s][t]; ExtCurStyle->exts_sideOverlapCap[s][t] = cnew; for (r = TT_TECHDEPBASE; r < DBNumTypes; r++) if (TTMaskHasType(&subtypes, r)) ExtCurStyle->exts_sideOverlapShieldPlanes[s][r] |= pshield; } } } } /* * ---------------------------------------------------------------------------- * * ExtTechSimpleSidewallCap -- * * Parse the techfile line for the "defaultsidewall" keyword. * * Results: * None. * * Side Effects: * Adds information into the ExtCurStyle records. * * ---------------------------------------------------------------------------- */ void ExtTechSimpleSidewallCap(argc, argv) int argc; char *argv[]; { /* Like ExtTechLine, but with near = types2 and far = types1 */ TileType s, t; TileTypeBitMask types1, types2; CapValue capVal; EdgeCap *cnew; int offset; double doffset; int plane; DBTechNoisyNameMask(argv[1], &types1); TTMaskSetMask(allExtractTypes, &types1); plane = DBTechNoisyNamePlane(argv[2]); capVal = aToCap(argv[3]); if (argc == 5) { /* Save a value of 1000 * the offset, which will be converted * appropriately to magic units like exts->sideCoupleHalo. */ sscanf(argv[4], "%lg", &doffset); offset = (int)(0.5 + doffset * 1000.0); } else offset = 0; // Like perimeter cap, treat only space and space-like types // TTMaskCom2(&types2, &types1); TTMaskZero(&types2); TTMaskSetType(&types2, TT_SPACE); TTMaskAndMask(&types1, &DBPlaneTypes[plane]); TTMaskAndMask(&types2, &DBPlaneTypes[plane]); if (TTMaskHasType(&types1, TT_SPACE)) TechError("Can't have space on inside of edge [ignored]\n"); for (s = TT_TECHDEPBASE; s < DBNumTypes; s++) { if (TTMaskHasType(&types1, s)) { ExtCurStyle->exts_sidePlanes |= PlaneNumToMaskBit(DBPlane(s)); TTMaskSetType(&ExtCurStyle->exts_sideTypes[DBPlane(s)], s); TTMaskSetMask(&ExtCurStyle->exts_sideEdges[s], &types2); for (t = 0; t < DBNumTypes; t++) { if (!TTMaskHasType(&types2, t)) continue; TTMaskSetMask(&ExtCurStyle->exts_sideCoupleOtherEdges[s][t], &types1); cnew = (EdgeCap *) mallocMagic((unsigned) (sizeof (EdgeCap))); cnew->ec_cap = capVal; cnew->ec_offset = offset; cnew->ec_near = types2; cnew->ec_far = types1; cnew->ec_next = ExtCurStyle->exts_sideCoupleCap[s][t]; cnew->ec_pmask = 0; ExtCurStyle->exts_sideCoupleCap[s][t] = cnew; } } } } /* * ---------------------------------------------------------------------------- * * ExtTechSimpleOverlapCap -- * * Parse the techfile line for the "defaultoverlap" keyword. * This is the same as the "overlap" statement excet that shield * types are determined automatically from the planeorder. * * Results: * None. * * Side Effects: * Adds information into the ExtCurStyle records. * * ---------------------------------------------------------------------------- */ void ExtTechSimpleOverlapCap(argv) char *argv[]; { TileType s, t; TileTypeBitMask types1, types2, shields; CapValue capVal; int plane1, plane2, plane3, pnum1, pnum2, pnum3; PlaneMask pshield; if (ExtCurStyle->exts_planeOrderStatus != seenPlaneOrder) { TechError("Cannot parse area cap line without plane ordering!\n"); return; } DBTechNoisyNameMask(argv[1], &types1); TTMaskSetMask(allExtractTypes, &types1); plane1 = DBTechNoisyNamePlane(argv[2]); TTMaskAndMask(&types1, &DBPlaneTypes[plane1]); DBTechNoisyNameMask(argv[3], &types2); TTMaskSetMask(allExtractTypes, &types2); plane2 = DBTechNoisyNamePlane(argv[4]); TTMaskAndMask(&types2, &DBPlaneTypes[plane2]); capVal = aToCap(argv[5]); pnum1 = ExtCurStyle->exts_planeOrder[plane1]; pnum2 = ExtCurStyle->exts_planeOrder[plane2]; /* Find all types in or below plane2 (i.e., ~(space)/plane2) */ /* Shield types are everything in the planes between plane1 and plane2 */ TTMaskZero(&shields); pshield = 0; for (plane3 = PL_TECHDEPBASE; plane3 < DBNumPlanes; plane3++) { pnum3 = ExtCurStyle->exts_planeOrder[plane3]; if (pnum3 > pnum2 && pnum3 < pnum1) { TTMaskSetMask(&shields, &DBPlaneTypes[plane3]); pshield |= PlaneNumToMaskBit(plane3); } } TTMaskClearType(&shields, TT_SPACE); /* Now record all of the overlap capacitances */ for (s = TT_TECHDEPBASE; s < DBNumTypes; s++) { if (TTMaskHasType(&types1, s)) { /* Contact overlap caps are determined from residues */ if (DBIsContact(s)) continue; for (t = TT_TECHDEPBASE; t < DBNumTypes; t++) { if (!TTMaskHasType(&types2, t)) continue; if (DBIsContact(t)) continue; if (s == t) continue; /* shouldn't happen */ if (plane1 == plane2) continue; /* shouldn't happen */ if (ExtCurStyle->exts_overlapCap[s][t] > (CapValue) 0) continue; /* redundant overlap */ ExtCurStyle->exts_overlapCap[s][t] = capVal; ExtCurStyle->exts_overlapMult[s][t] = (float)capVal * FRINGE_MULT; ExtCurStyle->exts_overlapMult[t][s] = (float)capVal * FRINGE_MULT; ExtCurStyle->exts_overlapPlanes |= PlaneNumToMaskBit(plane1); ExtCurStyle->exts_overlapOtherPlanes[s] |= PlaneNumToMaskBit(plane2); TTMaskSetType(&ExtCurStyle->exts_overlapTypes[plane1], s); TTMaskSetType(&ExtCurStyle->exts_overlapOtherTypes[s], t); ExtCurStyle->exts_overlapShieldPlanes[s][t] = pshield; ExtCurStyle->exts_overlapShieldTypes[s][t] = shields; } } } } /* * ---------------------------------------------------------------------------- * * ExtTechSimpleSideOverlapCap -- * * Parse the techfile line for the "defaultsideoverlap" keyword. * * Results: * None. * * Side Effects: * Adds information into the ExtCurStyle records. * * ---------------------------------------------------------------------------- */ void ExtTechSimpleSideOverlapCap(argv) char *argv[]; { TileType r, s, t; TileTypeBitMask types, nottypes, ov, notov, shields; CapValue capVal; int plane1, plane2, plane3, pnum1, pnum2, pnum3; PlaneMask pshield; EdgeCap *cnew; bool forward; if (ExtCurStyle->exts_planeOrderStatus != seenPlaneOrder) { TechError("Cannot parse area cap line without plane ordering!\n"); return; } DBTechNoisyNameMask(argv[1], &types); TTMaskSetMask(allExtractTypes, &types); plane1 = DBTechNoisyNamePlane(argv[2]); TTMaskCom2(¬types, &types); TTMaskAndMask(&types, &DBPlaneTypes[plane1]); TTMaskAndMask(¬types, &DBPlaneTypes[plane1]); DBTechNoisyNameMask(argv[3], &ov); TTMaskSetMask(allExtractTypes, &ov); plane2 = DBTechNoisyNamePlane(argv[4]); // TTMaskCom2(¬ov, &ov); TTMaskZero(¬ov); TTMaskSetType(¬ov, TT_SPACE); TTMaskAndMask(&ov, &DBPlaneTypes[plane2]); TTMaskAndMask(¬ov, &DBPlaneTypes[plane2]); capVal = aToCap(argv[5]); pnum1 = ExtCurStyle->exts_planeOrder[plane1]; pnum2 = ExtCurStyle->exts_planeOrder[plane2]; if (pnum1 == pnum2) { TechError("Cannot have fringing capacitance between " "types on the same plane\n"); return; } /* Fringing cap works both directions. Consider the case plane1 < */ /* plane2 as the "forward" case, and plane1 > plane2 as the */ /* "reverse" case. */ forward = (plane1 < plane2) ? TRUE : FALSE; /* Find all types in or below plane2 (i.e., ~(space)/plane2) */ /* Shield planes are the ones between plane1 and plane2 */ TTMaskZero(&shields); pshield = 0; for (plane3 = PL_TECHDEPBASE; plane3 < DBNumPlanes; plane3++) { pnum3 = ExtCurStyle->exts_planeOrder[plane3]; if ((forward == FALSE) && (pnum3 > pnum2 && pnum3 < pnum1)) { TTMaskSetMask(&shields, &DBPlaneTypes[plane3]); pshield |= PlaneNumToMaskBit(plane3); } else if ((forward == TRUE) && (pnum3 < pnum2 && pnum3 > pnum1)) { TTMaskSetMask(&shields, &DBPlaneTypes[plane3]); pshield |= PlaneNumToMaskBit(plane3); } } TTMaskClearType(&shields, TT_SPACE); /* Now record all of the sideoverlap capacitances */ if (TTMaskHasType(&types, TT_SPACE) || TTMaskHasType(&ov, TT_SPACE)) { TechError("Overlap types can't contain space [ignored]\n"); return; } /* Record all of the sideoverlap capacitances */ for (s = TT_TECHDEPBASE; s < DBNumTypes; s++) { /* Side overlap computed from residues */ if (DBIsContact(s)) continue; if (TTMaskHasType(&types, s)) { ExtCurStyle->exts_sidePlanes |= PlaneNumToMaskBit(plane1); TTMaskSetType(&ExtCurStyle->exts_sideTypes[plane1], s); TTMaskSetMask(&ExtCurStyle->exts_sideEdges[s], ¬types); for (t = TT_SPACE; t < DBNumTypes; t++) { if (!TTMaskHasType(¬types, t)) continue; if (DBIsContact(t)) continue; TTMaskSetMask(&ExtCurStyle->exts_sideOverlapOtherTypes[s][t], &ov); ExtCurStyle->exts_sideOverlapOtherPlanes[s][t] |= PlaneNumToMaskBit(plane2); cnew = (EdgeCap *) mallocMagic((unsigned) (sizeof (EdgeCap))); cnew->ec_cap = capVal; cnew->ec_offset = 0; /* No offsets on overlap caps */ cnew->ec_far = shields; /* Types that shield */ cnew->ec_near = ov; /* Types we create cap with */ cnew->ec_pmask = PlaneNumToMaskBit(plane2); cnew->ec_next = ExtCurStyle->exts_sideOverlapCap[s][t]; ExtCurStyle->exts_sideOverlapCap[s][t] = cnew; for (r = TT_TECHDEPBASE; r < DBNumTypes; r++) if (TTMaskHasType(&ov, r)) ExtCurStyle->exts_sideOverlapShieldPlanes[s][r] |= pshield; } } /* There is no "reverse case". Overlap from A->B will be different */ /* than B->A because it depends on the thickness of A and B. For */ /* the reverse case, the defaultsideoverlap statement must be */ /* repeated with the correct capacitance. */ } } /* * ---------------------------------------------------------------------------- * * ExtTechLine -- * * Process a line from the "extract" section of a technology file. * * Each line in the extract section of a technology begins * with a keyword that identifies the format of the rest of * the line. * * The following three kinds of lines are used to define the resistance * and parasitic capacitance to substrate of each tile type: * * resist types resistance * areacap types capacitance * perimcap inside outside capacitance * * where 'types', 'inside', and 'outside' are comma-separated lists * of tile types, 'resistance' is an integer giving the resistance * per square in milli-ohms, and 'capacitance' is an integer giving * capacitance (per square lambda for areacap, or per lambda perimeter * for perimcap) in attofarads. * * The perimeter (sidewall) capacitance depends both on the types * inside and outside the perimeter. For a given 'perimcap' line, * any segment of perimeter with a type in 'inside' inside the * perimeter and a type in 'outside' ontside the perimeter will * have the indicated capacitance. * * Both area and perimeter capacitance computed from the information * above apply between a given node and the substrate beneath it, as * determined by extSubstrate[]. * * Contact resistances are specified by: * * contact type minsize resistance * * where type is the type of contact tile, minsize is chosen so that contacts * that are integer multiples of minsize get an additional contact cut for each * increment of minsize, and resistance is in milliohms. * * Overlap coupling capacitance is specified by: * * overlap toptypes bottomtypes capacitance [shieldtypes] * * where 'toptypes' and 'bottomtypes' are comma-separated lists of tile types, * and 'capacitance' is an integer giving capacitance in attofarads per * square lambda of overlap. The sets 'toptypes' and 'bottomtypes' should * be disjoint. Also, the union of the planes of 'toptypes' should be disjoint * from the union of the planes of 'bottomtypes'. If 'shieldtypes' are * present, they should also be a comma-separated list of types, on * planes disjoint from those of either 'toptypes' or 'bottomtypes'. * * Whenever a tile of a type in 'toptypes' overlaps one of a type in * 'bottomtypes', we deduct the capacitance to substrate of the 'toptypes' * tile for the area of the overlap, and create an overlap capacitance * between the two nodes based on 'capacitance'. When material in * 'shieldtypes' appears over any of this overlap area, however, we * only deduct the substrate capacitance; we don't create an overlap * capacitor. * * Sidewall coupling capacitance is specified by: * * sidewall intypes outtypes neartypes fartypes capacitance * * where 'intypes', 'outtypes', 'neartypes', and 'fartypes' are all comma- * separated lists of types, and 'capacitance' is an integer giving capacitance * in attofarads. All of the tiles in all four lists should be on the same * plane. * * Whenever an edge of the form i|j is seen, where 'i' is in intypes and * 'j' is in outtypes, we search on the 'j' side for a distance of * ExtCurStyle->exts_sideCoupleHalo for edges with 'neartypes' on the * close side and 'fartypes' on the far side. We create a capacitance * equal to the length of overlap, times capacitance, divided by the * separation between the edges (poor approximation, but better than * none). * * Sidewall overlap coupling capacitance is specified by: * * sideoverlap intypes outtypes ovtypes capacitance * * where 'intypes', 'outtypes', and 'ovtypes' are comma-separated lists * of types, and 'capacitance' is an integer giving capacitance in attofarads * per lambda. Both intypes and outtypes should be in the same plane, and * ovtypes should be in a different plane from intypes and outtypes. * * The next kind of line describes transistors: * * fet types terminals min-#terminals names substrate gscap gccap * * where 'types' and 'terminals' are comma-separated lists of tile types. * The meaning is that each type listed in 'types' is a transistor, whose * source and drain connect to any of the types listed in 'terminals'. * These transistors must have exactly min-#terminals terminals, in addition * to the gate (whose connectivity is specified in the system-wide connectivity * table in the "connect" section of the .tech file). Currently gscap and * gccap are unused, but refer to the gate-source (or gate-drain) capacitance * and the gate-channel capacitance in units of attofarads per lambda and * attofarads per square lambda respectively. * * The resistances of transistors is specified by: * * fetresist type region ohms * * where type is a type of tile that is a fet, region is a string ("linear" * is treated specially), and ohms is the resistance per square of the fet * type while operating in "region". The values of fets in the "linear" * region are stored in a separate table. * * Results: * Returns TRUE normally, or FALSE if the line from the * technology file is so malformed that Magic should abort. * Currently, we always return TRUE. * * Side effects: * Initializes the per-technology variables that appear at the * beginning of this file. * * ---------------------------------------------------------------------------- */ #define MAXSD 6 /*ARGSUSED*/ bool ExtTechLine(sectionName, argc, argv) char *sectionName; int argc; char *argv[]; { int n, l, i, j, size, val, p1, p2, p3, nterm, iterm, class; PlaneMask pshield, pov; CapValue capVal, gscap, gccap; ResValue resVal; TileTypeBitMask types1, types2, termtypes[MAXSD]; TileTypeBitMask near, far, ov, shield, subsTypes, idTypes; char *subsName, *transName, *cp, *endptr, *paramName; TileType s, t, r, o; const keydesc *kp, *dv; HashEntry *he; EdgeCap *cnew; ExtKeep *es, *newStyle; ParamList *subcktParams, *newParam; ExtDevice *devptr; int refcnt; int offset; double dhalo; double doffset; bool bad; if (argc < 1) { TechError("Each line must begin with a keyword\n"); return (TRUE); } n = LookupStruct(argv[0], (const LookupTable *) keyTable, sizeof keyTable[0]); if (n < 0) { TechError("Illegal keyword. Legal keywords are:\n\t"); for (n = 0; keyTable[n].k_name; n++) TxError(" %s", keyTable[n].k_name); TxError("\n"); return (TRUE); } kp = &keyTable[n]; if (argc < kp->k_minargs) goto usage; /* Handle maxargs for DEVICE type separately */ if ((argc > kp->k_maxargs) && (kp->k_key != DEVICE)) goto usage; else if (argc >= 2) l = strlen(argv[1]); /* If ExtCurStyle is NULL, this is a first pass, and we should */ /* immediately load this style as default. Otherwise, check if */ /* the style name is in the table of styles, and add it if it is */ /* not. */ if (kp->k_key == STYLE) { if (argc != 2) if ((argc != 4) || (strncmp(argv[2], "variant", 7))) goto usage; for (newStyle = ExtAllStyles; newStyle != NULL; newStyle = newStyle->exts_next) { if (!strncmp(newStyle->exts_name, argv[1], l)) break; } if (newStyle == NULL) { if (argc == 2) { newStyle = (ExtKeep *)mallocMagic(sizeof(ExtKeep)); newStyle->exts_next = NULL; newStyle->exts_name = StrDup((char **) NULL, argv[1]); if (ExtAllStyles == NULL) ExtAllStyles = newStyle; else { /* Append to end of style list */ for (es = ExtAllStyles; es->exts_next; es = es->exts_next); es->exts_next = newStyle; } } else /* Handle style variants */ { ExtKeep *saveStyle = NULL; char *tptr, *cptr; /* 4th argument is a comma-separated list of variants. */ /* In addition to the default name recorded above, */ /* record each of the variants. */ tptr = argv[3]; while (*tptr != '\0') { cptr = strchr(tptr, ','); if (cptr != NULL) *cptr = '\0'; newStyle = (ExtKeep *)mallocMagic(sizeof(ExtKeep)); newStyle->exts_next = NULL; newStyle->exts_name = (char *)mallocMagic(l + strlen(tptr) + 1); sprintf(newStyle->exts_name, "%s%s", argv[1], tptr); /* Remember the first variant as the default */ if (saveStyle == NULL) saveStyle= newStyle; /* Append to end of style list */ if (ExtAllStyles == NULL) ExtAllStyles = newStyle; else { for (es = ExtAllStyles; es->exts_next; es = es->exts_next); es->exts_next = newStyle; } if (cptr == NULL) break; else tptr = cptr + 1; } newStyle = saveStyle; } } /* Load style as default extraction style if this is the first */ /* style encountered. Otherwise, if we are changing styles, */ /* load this style only if the name matches that in ExtCurStyle.*/ if (ExtCurStyle == NULL) { ExtCurStyle = extTechStyleNew(); ExtCurStyle->exts_name = newStyle->exts_name; ExtCurStyle->exts_status = TECH_PENDING; } else if ((ExtCurStyle->exts_status == TECH_PENDING) || (ExtCurStyle->exts_status == TECH_SUSPENDED)) /* Finished loading; stop */ ExtCurStyle->exts_status = TECH_LOADED; else if (ExtCurStyle->exts_status == TECH_NOT_LOADED) { if (ExtCurStyle->exts_name == NULL) return (FALSE); /* Don't know what to load! */ else if (argc == 2) { if (!strcmp(argv[1], ExtCurStyle->exts_name)) ExtCurStyle->exts_status = TECH_PENDING; /* load pending */ } else if (argc == 4) { /* Verify that the style matches one variant */ char *tptr, *cptr; if (!strncmp(ExtCurStyle->exts_name, argv[1], l)) { tptr = argv[3]; while (*tptr != '\0') { cptr = strchr(tptr, ','); if (cptr != NULL) *cptr = '\0'; if (!strcmp(ExtCurStyle->exts_name + l, tptr)) { ExtCurStyle->exts_status = TECH_PENDING; return TRUE; } if (cptr == NULL) return TRUE; else tptr = cptr + 1; } } } } return (TRUE); } /* Only continue past this point if we are loading the extraction style */ if (ExtCurStyle == NULL) return FALSE; if ((ExtCurStyle->exts_status != TECH_PENDING) && (ExtCurStyle->exts_status != TECH_SUSPENDED)) return TRUE; /* Process "variant" lines next */ if (kp->k_key == VARIANT) { int l; char *cptr, *tptr; /* 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 usage; 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 == '*') /* Wildcard for "all variants" */ { ExtCurStyle->exts_status = TECH_PENDING; return TRUE; } else { l = strlen(ExtCurStyle->exts_name) - strlen(tptr); if (!strcmp(tptr, ExtCurStyle->exts_name + l)) { ExtCurStyle->exts_status = TECH_PENDING; return TRUE; } } if (cptr == NULL) break; else tptr = cptr + 1; } ExtCurStyle->exts_status = TECH_SUSPENDED; } /* Anything below this line is not parsed if we're in TECH_SUSPENDED mode */ if (ExtCurStyle->exts_status != TECH_PENDING) return TRUE; switch (kp->k_key) { case AREAC: case CONTACT: case FET: case FETRESIST: case DEVRESIST: case HEIGHT: case ANTENNA: case TIEDOWN: case OVERC: case PERIMC: case RESIST: case SIDEWALL: case SIDEOVERLAP: case SUBSTRATE: DBTechNoisyNameMask(argv[1], &types1); TTMaskSetMask(allExtractTypes, &types1); break; case DEVICE: DBTechNoisyNameMask(argv[3], &types1); TTMaskSetMask(allExtractTypes, &types1); break; case PLANEORDER: case NOPLANEORDER: default: break; } switch (kp->k_key) { case AREAC: capVal = aToCap(argv[2]); for (t = TT_TECHDEPBASE; t < DBNumTypes; t++) if (TTMaskHasType(&types1, t)) { ExtCurStyle->exts_areaCap[t] = capVal; ExtCurStyle->exts_overlapMult[t][0] = (float) capVal * FRINGE_MULT; ExtCurStyle->exts_overlapMult[0][t] = (float) capVal * FRINGE_MULT; } break; case CONTACT: /* Contact size, border, spacing deprecated (now taken from */ /* cifoutput "squares" generation parameters). */ if (argc != 3) { if (argc == 4) TxPrintf("Contact size value ignored " "(using GDS generation rules).\n"); else TxPrintf("Contact size, spacing, and border values ignored " "(using GDS generation rules).\n"); } if (!StrIsInt(argv[argc - 1])) { TechError("Contact resistivity %s must be an integer value " "(in milliohms/square).\n", argv[argc - 1]); break; } resVal = aToRes(argv[argc - 1]); for (t = TT_TECHDEPBASE; t < DBNumTypes; t++) if (TTMaskHasType(&types1, t)) ExtCurStyle->exts_viaResist[t] = resVal; break; case CSCALE: ExtCurStyle->exts_capScale = strtol(argv[1], &endptr, 10); if (endptr == argv[1]) { TechError("Cannot parse cap scale value \"%s\"\n", argv[1]); ExtCurStyle->exts_capScale = 1; } break; case FET: /* Original FET format, kept for backwards compatibility */ DBTechNoisyNameMask(argv[2], &termtypes[0]); TTMaskSetMask(allExtractTypes, &termtypes[0]); nterm = atoi(argv[3]); transName = argv[4]; subsName = argv[5]; // From magic version 8.1, subs name can be a nonfunctional // throwaway (e.g., "error"), so don't throw a warning. cp = strchr(subsName, '!'); if (cp == NULL || cp[1] != '\0') { if (strcasecmp(subsName, "error")) { TechError("Fet substrate node %s is not a global name\n", subsName); } } subsTypes = DBZeroTypeBits; if (sscanf(argv[6], "%lf", &capVal) != 1) { DBTechNoisyNameMask(argv[6], &subsTypes); TTMaskSetMask(allExtractTypes, &subsTypes); gscap = aToCap(argv[7]); gccap = (argc > 8) ? aToCap(argv[8]) : (CapValue) 0; } else { gscap = aToCap(argv[6]); gccap = (argc > 7) ? aToCap(argv[7]) : (CapValue) 0; } TTMaskSetMask(&ExtCurStyle->exts_deviceMask, &types1); for (t = TT_TECHDEPBASE; t < DBNumTypes; t++) if (TTMaskHasType(&types1, t)) { devptr = (ExtDevice *)mallocMagic(sizeof(ExtDevice)); devptr->exts_deviceSDTypes = (TileTypeBitMask *) mallocMagic(2 * sizeof(TileTypeBitMask)); devptr->exts_deviceSDTypes[0] = termtypes[0]; devptr->exts_deviceSDTypes[1] = DBZeroTypeBits; devptr->exts_deviceSDCount = nterm; devptr->exts_deviceSDCap = gscap; devptr->exts_deviceGateCap = gccap; devptr->exts_deviceClass = DEV_FET; devptr->exts_deviceName = StrDup((char **) NULL, transName); devptr->exts_deviceSubstrateName = StrDup((char **) NULL, subsName); devptr->exts_deviceSubstrateTypes = subsTypes; devptr->exts_deviceIdentifierTypes = DBZeroTypeBits; devptr->exts_deviceParams = (ParamList *) NULL; devptr->exts_deviceResist.ht_table = (HashEntry **) NULL; HashInit(&devptr->exts_deviceResist, 8, HT_STRINGKEYS); TTMaskSetMask(ExtCurStyle->exts_deviceConn + t, &types1); devptr->exts_next = ExtCurStyle->exts_device[t]; ExtCurStyle->exts_device[t] = devptr; #ifdef ARIEL { int z; for (z = TT_TECHDEPBASE; z < DBNumTypes; z++) { if (TTMaskHasType(&subsTypes, z)) TTMaskSetType(&ExtCurStyle->exts_subsTransistorTypes[z], t); } } #endif } break; case DEFAULTAREACAP: ExtTechSimpleAreaCap(argc, argv); break; case DEFAULTOVERLAP: ExtTechSimpleOverlapCap(argv); break; case DEFAULTPERIMETER: ExtTechSimplePerimCap(argc, argv); break; case DEFAULTSIDEOVERLAP: ExtTechSimpleSideOverlapCap(argv); break; case DEFAULTSIDEWALL: ExtTechSimpleSidewallCap(argc, argv); break; case DEVICE: /* Parse second argument for device type */ n = LookupStruct(argv[1], (const LookupTable *)devTable, sizeof devTable[0]); if (n < 0) { TechError("Illegal device. Legal devices are:\n\t"); for (n = 0; devTable[n].k_name; n++) TxError(" %s", devTable[n].k_name); TxError("\n"); return (TRUE); } dv = &devTable[n]; if ((argc - 1) < dv->k_minargs) goto usage; /* Parse parameters from the end of the argument list. */ /* Parameters may be provided for any device. */ /* Check final arguments for "x=y" statements showing what */ /* parameter names the device uses. */ subcktParams = NULL; while ((paramName = strchr(argv[argc - 1], '=')) != NULL) { char *mult, *offset; double dval; /* Ignore ">=" and "<=", which are handled below */ if (paramName > argv[argc - 1]) if ((*(paramName - 1) == '>') || (*(paramName - 1) == '<')) break; paramName++; newParam = (ParamList *)mallocMagic(sizeof(ParamList)); newParam->pl_count = 0; newParam->pl_param[0] = *argv[argc - 1]; newParam->pl_param[1] = '\0'; newParam->pl_maximum = -1; newParam->pl_minimum = 0; newParam->pl_offset = 0; if (paramName - argv[argc - 1] == 3) newParam->pl_param[1] = *(argv[argc - 1] + 1); else if (paramName - argv[argc - 1] > 3) TechError("Parameter name %s can be no more than" "two characters.\n", argv[argc - 1]); // Parameter syntax "=*" indicates // that the subcircuit has internal scaling, and the // extractor should multiply the parameter by this value // before passing it to the subcircuit. if ((mult = strchr(paramName, '*')) != NULL) { *mult = '\0'; mult++; newParam->pl_scale = atof(mult); } else { newParam->pl_scale = 1.0; /* NOTE: If allowing both scale and offset, be sure * to distinguish between +/- used for offsets and * +/- used as sign. */ if ((offset = strchr(paramName, '+')) != NULL) { *offset = '\0'; offset++; dval = atof(offset); newParam->pl_offset = (int)(0.5 + (dval * 1000)); } else if ((offset = strchr(paramName, '-')) != NULL) { *offset = '\0'; offset++; dval = -atof(offset); newParam->pl_offset = (int)(0.5 + (dval * 1000)); } else newParam->pl_offset = 0; } newParam->pl_name = StrDup((char **)NULL, paramName); newParam->pl_next = subcktParams; subcktParams = newParam; argc--; } /* Check for parameter range limits in one of these forms: */ /* x>y, x=y, x<=y. */ while (TRUE) { ParamList *chkParam; char *limitstr; char cond; bool equal = FALSE; double dval; int ival; limitstr = strchr(argv[argc - 1], '<'); if (limitstr == NULL) limitstr = strchr(argv[argc - 1], '>'); if (limitstr == NULL) break; cond = *limitstr; *limitstr = '\0'; /* If the parameter exists, then modify its min/max values. * If not, then create a parameter and fill in only the * min/max values. */ if (limitstr - argv[argc - 1] > 3) { TechError("Parameter name %s can be no more than" "two characters.\n", argv[argc - 1]); break; } for (chkParam = subcktParams; chkParam; chkParam = chkParam->pl_next) if ((chkParam->pl_param[0] == argv[argc - 1][0]) && (chkParam->pl_param[1] == argv[argc - 1][1])) break; /* If there is no defined parameter with the given name * to be output, then create the parameter for checking * limits only. */ if (chkParam == NULL) { newParam = (ParamList *)mallocMagic(sizeof(ParamList)); newParam->pl_count = 0; newParam->pl_param[0] = argv[argc - 1][0]; newParam->pl_param[1] = argv[argc - 1][1]; newParam->pl_maximum = -1; newParam->pl_minimum = 0; newParam->pl_name = NULL; newParam->pl_scale = 1.0; newParam->pl_offset = 0; newParam->pl_next = subcktParams; subcktParams = newParam; chkParam = newParam; } /* Change limit */ limitstr++; if (*limitstr == '=') { equal = TRUE; limitstr++; } if (sscanf(limitstr, "%lg", &dval) != 1) { TxError("Non-numeric limit \"%s\" for parameter \"%c%s\".\n", limitstr, cond, argv[argc - 1]); break; } /* Convert dval to internal (integer) units. Scale up by */ /* 1000 so the value can be converted if it's in microns */ /* without losing precision. */ ival = (int)(0.5 + (dval * 1000)); /* Make adjustment for greater than or less than */ if (cond == '>') { if (!equal) ival++; chkParam->pl_minimum = ival; } else { if (!equal) ival--; chkParam->pl_maximum = ival; } /* Move to next argument */ argc--; } /* If the last entry before any parameters starts with '+', */ /* then use it to set the identity marker. Otherwise, the */ /* identity marker is NULL. */ idTypes = DBZeroTypeBits; if (*argv[argc - 1] == '+') { if ((DBTechNameMask(argv[argc - 1] + 1, &idTypes)) == 0) idTypes = DBZeroTypeBits; argc--; } class = dv->k_key; /* Note: This check has been removed. Parameters for non- */ /* subcircuit devices are allowed for support of CDL */ /* netlists, which uses arbitrary subcircuit-like */ /* parameters combined with a SPICE-like device prefix. */ #if 0 /* Check the number of arguments after splitting out */ /* parameter entries. There is no limit on arguments in */ /* DEV_SUBCKT, DEV_MSUBCKT, and DEV_VERILOGA. */ switch (class) { case DEV_SUBCKT: case DEV_MSUBCKT: case DEV_VERILOGA: break; default: /* If parameters were saved but the */ /* argument list indicates a bad */ /* device entry, then free up the */ /* parameters. */ if ((argc - 1) > dv->k_maxargs) { while (subcktParams != NULL) { if (subcktParams->pl_name != NULL) freeMagic(subcktParams->pl_name); freeMagic(subcktParams); subcktParams = subcktParams->pl_next; } goto usage; } break; } #endif gscap = (CapValue) 0; gccap = (CapValue) 0; subsName = NULL; subsTypes = DBZeroTypeBits; transName = argv[2]; switch (dv->k_key) { case DEV_BJT: DBTechNoisyNameMask(argv[4], &termtypes[0]); /* emitter */ TTMaskSetMask(allExtractTypes, &termtypes[0]); termtypes[1] = DBZeroTypeBits; DBTechNoisyNameMask(argv[5], &subsTypes); /* collector */ TTMaskSetMask(allExtractTypes, &subsTypes); nterm = 1; /* emitter is the only "terminal type" expected */ break; case DEV_MOSFET: if ((argc > 7) && (!StrIsNumeric(argv[7]))) { /* Asymmetric device with different source and drain types */ DBTechNoisyNameMask(argv[4], &termtypes[0]); /* source */ TTMaskSetMask(allExtractTypes, &termtypes[0]); DBTechNoisyNameMask(argv[5], &termtypes[1]); /* drain */ TTMaskSetMask(allExtractTypes, &termtypes[1]); TTMaskAndMask3(&termtypes[2], &termtypes[0], &termtypes[1]); if (TTMaskEqual(&termtypes[0], &termtypes[1])) termtypes[1] = DBZeroTypeBits; /* Make it symmetric */ else if (!TTMaskIsZero(&termtypes[2])) { class = DEV_ASYMMETRIC; TechError("Device mosfet %s has overlapping drain" " and source types!\n", transName); /* Should this device be disabled? */ } else class = DEV_ASYMMETRIC; termtypes[2] = DBZeroTypeBits; if (strcmp(argv[6], "None")) DBTechNoisyNameMask(argv[6], &subsTypes); /* substrate */ TTMaskSetMask(allExtractTypes, &subsTypes); subsName = argv[7]; if (argc > 8) gscap = aToCap(argv[8]); if (argc > 9) gccap = aToCap(argv[9]); nterm = 2; } else { /* Normal symmetric device with swappable source/drain */ DBTechNoisyNameMask(argv[4], &termtypes[0]); /* source/drain */ TTMaskSetMask(allExtractTypes, &termtypes[0]); termtypes[1] = DBZeroTypeBits; if (strcmp(argv[5], "None")) { DBTechNoisyNameMask(argv[5], &subsTypes); /* substrate */ TTMaskSetMask(allExtractTypes, &subsTypes); } if (argc > 6) subsName = argv[6]; if (argc > 7) gscap = aToCap(argv[7]); if (argc > 8) gccap = aToCap(argv[8]); /* nterm = 1; */ /* Symmetric devices can be MOScaps */ nterm = 2; } break; case DEV_DIODE: case DEV_PDIODE: case DEV_NDIODE: DBTechNoisyNameMask(argv[4], &termtypes[0]); /* negative types */ TTMaskSetMask(allExtractTypes, &termtypes[0]); termtypes[1] = DBZeroTypeBits; nterm = 1; if ((argc > 4) && strcmp(argv[4], "None")) { DBTechNoisyNameMask(argv[4], &subsTypes); /* substrate */ TTMaskSetMask(allExtractTypes, &subsTypes); } else subsTypes = DBZeroTypeBits; if (argc > 5) subsName = argv[5]; break; case DEV_RES: DBTechNoisyNameMask(argv[4], &termtypes[0]); /* terminals */ TTMaskSetMask(allExtractTypes, &termtypes[0]); termtypes[1] = DBZeroTypeBits; nterm = 2; if ((argc > 5) && strcmp(argv[5], "None")) { DBTechNoisyNameMask(argv[5], &subsTypes); /* substrate */ TTMaskSetMask(allExtractTypes, &subsTypes); } else subsTypes = DBZeroTypeBits; if (argc > 6) subsName = argv[6]; break; case DEV_CAP: case DEV_CAPREV: DBTechNoisyNameMask(argv[4], &termtypes[0]); /* bottom */ TTMaskSetMask(allExtractTypes, &termtypes[0]); termtypes[1] = DBZeroTypeBits; if (argc > 5) gccap = aToCap(argv[argc - 1]); /* area cap */ if ((argc > 6) && StrIsNumeric(argv[argc - 2])) { gscap = aToCap(argv[argc - 2]); /* perimeter cap */ argc--; } nterm = 1; if ((argc > 6) && strcmp(argv[5], "None")) { DBTechNoisyNameMask(argv[5], &subsTypes); /* substrate */ TTMaskSetMask(allExtractTypes, &subsTypes); } else subsTypes = DBZeroTypeBits; if (argc > 7) subsName = argv[6]; break; case DEV_SUBCKT: case DEV_MSUBCKT: case DEV_VERILOGA: // Determine if [substrate, name] optional arguments // are present by checking if the last argument // parses as a layer list. if (DBTechNameMask(argv[argc - 1], &termtypes[0]) <= 0) { if (strcmp(argv[argc - 2], "None")) { DBTechNoisyNameMask(argv[argc - 2], &subsTypes); TTMaskSetMask(allExtractTypes, &subsTypes); } else subsTypes = DBZeroTypeBits; subsName = argv[argc - 1]; argc -= 2; } if (StrIsInt(argv[4])) { nterm = atoi(argv[4]); iterm = 5; if (nterm > argc - 5) { TechError("Not enough terminals for subcircuit, " "%d were required, %d found.\n", nterm, argc - 5); nterm = argc - 5; } } else { nterm = argc - 4; iterm = 4; } /* terminals */ for (i = iterm; i < iterm + nterm; i++) { DBTechNoisyNameMask(argv[i], &termtypes[i - iterm]); TTMaskSetMask(allExtractTypes, &termtypes[i - iterm]); } termtypes[nterm] = DBZeroTypeBits; if (nterm == 0) i++; // Type MSUBCKT: If source and drain are symmetric (both // have the same types), then they must both be declared, // but only one is used (same policy as "device mosfet"). if ((nterm == 2) && TTMaskEqual(&termtypes[nterm - 1], &termtypes[nterm - 2])) termtypes[nterm - 1] = DBZeroTypeBits; break; case DEV_RSUBCKT: case DEV_CSUBCKT: nterm = (dv->k_key == DEV_RSUBCKT) ? 2 : 1; DBTechNoisyNameMask(argv[4], &termtypes[0]); /* terminals */ TTMaskSetMask(allExtractTypes, &termtypes[0]); termtypes[1] = DBZeroTypeBits; if ((argc > 5) && strcmp(argv[5], "None") && (strchr(argv[5], '=') == NULL)) { DBTechNoisyNameMask(argv[5], &subsTypes); /* substrate */ TTMaskSetMask(allExtractTypes, &subsTypes); } else subsTypes = DBZeroTypeBits; if ((argc > 6) && (strchr(argv[6], '=') == NULL)) subsName = argv[6]; break; } TTMaskSetMask(&ExtCurStyle->exts_deviceMask, &types1); for (t = TT_TECHDEPBASE; t < DBNumTypes; t++) { if (TTMaskHasType(&types1, t)) { devptr = (ExtDevice *)mallocMagic(sizeof(ExtDevice)); for (i = 0; !TTMaskIsZero(&termtypes[i]); i++); devptr->exts_deviceSDTypes = (TileTypeBitMask *) mallocMagic((i + 1) * sizeof(TileTypeBitMask)); for (i = 0; !TTMaskIsZero(&termtypes[i]); i++) devptr->exts_deviceSDTypes[i] = termtypes[i]; devptr->exts_deviceSDTypes[i] = DBZeroTypeBits; devptr->exts_deviceSDCount = nterm; devptr->exts_deviceSDCap = gscap; devptr->exts_deviceGateCap = gccap; devptr->exts_deviceClass = class; devptr->exts_deviceName = StrDup((char **) NULL, transName); if (subsName != NULL) devptr->exts_deviceSubstrateName = StrDup((char **) NULL, subsName); else devptr->exts_deviceSubstrateName = (char *)NULL; devptr->exts_deviceSubstrateTypes = subsTypes; devptr->exts_deviceIdentifierTypes = idTypes; devptr->exts_deviceParams = (ParamList *) NULL; if (subcktParams != NULL) { devptr->exts_deviceParams = subcktParams; subcktParams->pl_count++; } devptr->exts_deviceResist.ht_table = (HashEntry **) NULL; HashInit(&devptr->exts_deviceResist, 8, HT_STRINGKEYS); devptr->exts_next = ExtCurStyle->exts_device[t]; ExtCurStyle->exts_device[t] = devptr; TTMaskSetMask(ExtCurStyle->exts_deviceConn + t, &types1); #ifdef ARIEL { int z; for (z = TT_TECHDEPBASE; z < DBNumTypes; z++) { if (TTMaskHasType(&subsTypes, z)) TTMaskSetType(&ExtCurStyle-> exts_subsTransistorTypes[z], t); } } #endif } } break; case DEVRESIST: case FETRESIST: if (!StrIsNumeric(argv[3])) { TechError("Device resistivity %s must be numeric\n", argv[3]); break; } resVal = aToRes(argv[3]); for (t = TT_TECHDEPBASE; t < DBNumTypes; t++) { ExtDevice *devptr; if (TTMaskHasType(&types1, t)) { for (devptr = ExtCurStyle->exts_device[t]; devptr; devptr = devptr->exts_next) { he = HashFind(&devptr->exts_deviceResist, argv[2]); HashSetValue(he, (spointertype)resVal); } } } break; case HEIGHT: { float height, thick; if (!StrIsNumeric(argv[2])) { TechError("Layer height %s must be numeric\n", argv[2]); break; } if (!StrIsNumeric(argv[3])) { TechError("Layer thickness %s must be numeric\n", argv[3]); break; } height = (float)strtod(argv[2], NULL); thick = (float)strtod(argv[3], NULL); for (t = TT_TECHDEPBASE; t < DBNumTypes; t++) if (TTMaskHasType(&types1, t)) { ExtCurStyle->exts_height[t] = height; ExtCurStyle->exts_thick[t] = thick; } } break; case ANTENNA: { float antennaratio; char areaType; bool hasModel = FALSE; int argidx = 2; if (!StrIsNumeric(argv[2])) { if (!strcmp(argv[2], "surface") || !strcmp(argv[2], "area")) { areaType = ANTENNAMODEL_SURFACE; hasModel = TRUE; } else if (!strcmp(argv[2], "sidewall") || !strcmp(argv[2], "perimeter")) { areaType = ANTENNAMODEL_SIDEWALL; hasModel = TRUE; } else { TechError("Error in layer antenna calculation type \"%s\"; " " must be \"surface\" or \"sidewall\"\n", argv[2]); break; } } if (hasModel == FALSE) { if (ExtCurStyle->exts_antennaModel & ANTENNAMODEL_SURFACE) areaType = ANTENNAMODEL_SURFACE; else if (ExtCurStyle->exts_antennaModel & ANTENNAMODEL_SIDEWALL) areaType = ANTENNAMODEL_SIDEWALL; else TechError("No antenna calculation type given for layer(s) %s " " and no default calculation type found.\n", argv[1]); } for (t = TT_TECHDEPBASE; t < DBNumTypes; t++) if (TTMaskHasType(&types1, t)) ExtCurStyle->exts_antennaRatio[t].areaType = areaType; if (hasModel == TRUE) argidx = 3; if (!StrIsNumeric(argv[argidx])) { TechError("Gate layer antenna ratio %s must be numeric\n", argv[argidx]); break; } antennaratio = (float)strtod(argv[argidx], NULL); for (t = TT_TECHDEPBASE; t < DBNumTypes; t++) if (TTMaskHasType(&types1, t)) ExtCurStyle->exts_antennaRatio[t].ratioGate = antennaratio; argidx++; if (!StrIsNumeric(argv[argidx])) { if (!strcasecmp(argv[argidx], "none")) antennaratio = INFINITY; else { TechError("Diff layer antenna ratio %s must be numeric\n", argv[argidx]); break; } } else antennaratio = (float)strtod(argv[argidx], NULL); for (t = TT_TECHDEPBASE; t < DBNumTypes; t++) if (TTMaskHasType(&types1, t)) ExtCurStyle->exts_antennaRatio[t].ratioDiffB = antennaratio; argidx++; if (argidx < argc) { if (!StrIsNumeric(argv[argidx])) { TechError("Diff layer antenna ratio %s must be numeric\n", argv[argidx]); break; } antennaratio = (float)strtod(argv[argidx], NULL); } else antennaratio = 0; /* NOTE: antennaratio is multiplied by diffusion area and so has * units of (1/area^2) and so it should be scaled with other * dimension-scaled units. */ for (t = TT_TECHDEPBASE; t < DBNumTypes; t++) if (TTMaskHasType(&types1, t)) ExtCurStyle->exts_antennaRatio[t].ratioDiffA = antennaratio; break; } case MODEL: if (!strcmp(argv[1], "partial")) ExtCurStyle->exts_antennaModel |= ANTENNAMODEL_PARTIAL; else if (!strcmp(argv[1], "cumulative")) ExtCurStyle->exts_antennaModel |= ANTENNAMODEL_CUMULATIVE; else TxError("Unknown antenna model \"%s\": Use \"partial\" or " "\"cumulative\"", argv[1]); if (argc > 2) { if (!strcmp(argv[2], "surface") || !strcmp(argv[2], "area")) ExtCurStyle->exts_antennaModel |= ANTENNAMODEL_SURFACE; else if (!strcmp(argv[2], "sidewall") || !strcmp(argv[2], "perimeter")) ExtCurStyle->exts_antennaModel |= ANTENNAMODEL_SIDEWALL; else TxError("Unknown antenna model \"%s\": Use \"surface\" or " "\"sidewall\"", argv[2]); } break; case TIEDOWN: TTMaskSetMask(&ExtCurStyle->exts_antennaTieTypes, &types1); break; case UNITS: if (!strcmp(argv[1], "microns")) doConvert = TRUE; else if (!strcmp(argv[1], "um")) doConvert = TRUE; else if (strcmp(argv[1], "lambda")) TechError("Units must be microns or lambda. Using the " "default value (lambda).\n"); break; case LAMBDA: ExtCurStyle->exts_unitsPerLambda = (float)atof(argv[1]); break; case OVERC: DBTechNoisyNameMask(argv[2], &types2); TTMaskSetMask(allExtractTypes, &types2); capVal = aToCap(argv[3]); bad = FALSE; shield = DBZeroTypeBits; if (argc > 4) { DBTechNoisyNameMask(argv[4], &shield); TTMaskSetMask(allExtractTypes, &shield); } for (s = TT_TECHDEPBASE; s < DBNumTypes; s++) { if (!TTMaskHasType(&types1, s)) continue; /* Contact overlap caps are determined from residues */ if (DBIsContact(s)) continue; for (t = TT_TECHDEPBASE; t < DBNumTypes; t++) { if (!TTMaskHasType(&types2, t)) continue; /* Contact overlap caps are determined from residues */ if (DBIsContact(t)) continue; if (s == t) { TechError("Can't have overlap capacitance between" " tiles of the same type (%s)\n", DBTypeLongName(s)); bad = TRUE; continue; } p1 = DBPlane(s), p2 = DBPlane(t); if (p1 == p2) { TechError("Can't have overlap capacitance between" " tiles on the same plane (%s, %s)\n", DBTypeLongName(s), DBTypeLongName(t)); bad = TRUE; continue; } if (ExtCurStyle->exts_overlapCap[s][t] > (CapValue) 0) { TechError("Only one of \"overlap %s %s\" or" " \"overlap %s %s\" allowed\n", DBTypeLongName(s), DBTypeLongName(t), DBTypeLongName(t), DBTypeLongName(s)); bad = TRUE; continue; } ExtCurStyle->exts_overlapCap[s][t] = capVal; ExtCurStyle->exts_overlapMult[s][t] = (float)capVal * FRINGE_MULT; ExtCurStyle->exts_overlapMult[t][s] = (float)capVal * FRINGE_MULT; ExtCurStyle->exts_overlapPlanes |= PlaneNumToMaskBit(p1); ExtCurStyle->exts_overlapOtherPlanes[s] |= PlaneNumToMaskBit(p2); TTMaskSetType(&ExtCurStyle->exts_overlapTypes[p1], s); TTMaskSetType(&ExtCurStyle->exts_overlapOtherTypes[s], t); if (argc == 4) continue; /* Shielding */ pshield = 0; for (r = TT_TECHDEPBASE; r < DBNumTypes; r++) { if (TTMaskHasType(&shield, r)) { /* Shielding types are determined from residues */ if (DBIsContact(r)) continue; p3 = DBPlane(r); if (p3 == p1 || p3 == p2) { TechError("Shielding type (%s) must be on a" " different plane from shielded types.\n", DBTypeLongName(r)); bad = TRUE; continue; } pshield |= PlaneNumToMaskBit(p3); } } ExtCurStyle->exts_overlapShieldPlanes[s][t] = pshield; ExtCurStyle->exts_overlapShieldTypes[s][t] = shield; } } if (bad) return (TRUE); break; case SIDEOVERLAP: bad = FALSE; DBTechNoisyNameMask(argv[2], &types2); TTMaskSetMask(allExtractTypes, &types2); pov = DBTechNoisyNameMask(argv[3], &ov); TTMaskSetMask(allExtractTypes, &ov); capVal = aToCap(argv[4]); shield = DBZeroTypeBits; if (argc == 6) { DBTechNoisyNameMask(argv[5], &shield); TTMaskSetMask(allExtractTypes, &shield); } if (TTMaskHasType(&types1, TT_SPACE)) TechError("Can't have space on inside of edge [ignored]\n"); /* It's ok to have the overlap be to space as long as a plane is */ /* specified. */ if (TTMaskHasType(&ov, TT_SPACE)) { if ((cp = strchr(argv[3],'/')) == NULL) { TechError("Must specify plane for sideoverlap to space\n"); } cp++; p3 = (spointertype) dbTechNameLookup(cp, &dbPlaneNameLists); if (p3 < 0) TechError("Unknown overlap plane %s\n",argv[3]); else pov = PlaneNumToMaskBit(p3); } for (s = TT_TECHDEPBASE; s < DBNumTypes; s++) { if (!TTMaskHasType(&types1, s)) continue; /* Side overlap computed from residues */ if (DBIsContact(s)) continue; p1 = DBPlane(s); if (PlaneMaskHasPlane(pov, p1)) goto diffplane; ExtCurStyle->exts_sidePlanes |= PlaneNumToMaskBit(p1); TTMaskSetType(&ExtCurStyle->exts_sideTypes[p1], s); TTMaskSetMask(&ExtCurStyle->exts_sideEdges[s], &types2); for (t = 0; t < DBNumTypes; t++) { if (!TTMaskHasType(&types2, t)) continue; /* Side overlap computed from residues */ if (DBIsContact(t)) continue; p2 = DBPlane(t); if (t != TT_SPACE && PlaneMaskHasPlane(pov, p2)) goto diffplane; TTMaskSetMask(&ExtCurStyle->exts_sideOverlapOtherTypes[s][t], &ov); ExtCurStyle->exts_sideOverlapOtherPlanes[s][t] |= pov; cnew = (EdgeCap *) mallocMagic((unsigned) (sizeof (EdgeCap))); cnew->ec_cap = capVal; cnew->ec_offset = 0; /* No offsets on overlap caps */ cnew->ec_far = shield; /* Really types that shield */ cnew->ec_near = ov; /* Really types we create cap with */ cnew->ec_pmask = pov; cnew->ec_next = ExtCurStyle->exts_sideOverlapCap[s][t]; ExtCurStyle->exts_sideOverlapCap[s][t] = cnew; /* Shielding */ pshield = 0; for (r = TT_TECHDEPBASE; r < DBNumTypes; r++) { if (TTMaskHasType(&shield, r)) { /* Side overlap shielding computed from residues */ if (DBIsContact(r)) continue; p3 = DBPlane(r); if (p3 == p1 || p3 == p2) { TechError("Shielding type (%s) must be on" " a different plane from shielded types.\n", DBTypeLongName(r)); bad = TRUE; continue; } pshield |= PlaneNumToMaskBit(p3); } } for (o = TT_TECHDEPBASE; o < DBNumTypes; o++) { if (TTMaskHasType(&ov, o)) { ExtCurStyle->exts_sideOverlapShieldPlanes[s][o] |= pshield; } } } } if (bad) return (TRUE); break; case SIDEWALL: DBTechNoisyNameMask(argv[2], &types2); TTMaskSetMask(allExtractTypes, &types2); DBTechNoisyNameMask(argv[3], &near); TTMaskSetMask(allExtractTypes, &near); DBTechNoisyNameMask(argv[4], &far); TTMaskSetMask(allExtractTypes, &far); if (TTMaskHasType(&types1, TT_SPACE)) TechError("Can't have space on inside of edge [ignored]\n"); capVal = aToCap(argv[5]); if (argc == 7) { sscanf(argv[6], "%lg", &doffset); offset = (int)(0.5 + doffset * 1000.0); } else offset = 0; for (s = TT_TECHDEPBASE; s < DBNumTypes; s++) { if (!TTMaskHasType(&types1, s)) continue; ExtCurStyle->exts_sidePlanes |= PlaneNumToMaskBit(DBPlane(s)); TTMaskSetType(&ExtCurStyle->exts_sideTypes[DBPlane(s)], s); TTMaskSetMask(&ExtCurStyle->exts_sideEdges[s], &types2); for (t = 0; t < DBNumTypes; t++) { if (!TTMaskHasType(&types2, t)) continue; TTMaskSetMask(&ExtCurStyle->exts_sideCoupleOtherEdges[s][t], &far); cnew = (EdgeCap *) mallocMagic((unsigned) (sizeof (EdgeCap))); cnew->ec_cap = capVal; cnew->ec_offset = offset; cnew->ec_near = near; cnew->ec_far = far; cnew->ec_next = ExtCurStyle->exts_sideCoupleCap[s][t]; cnew->ec_pmask = 0; ExtCurStyle->exts_sideCoupleCap[s][t] = cnew; } } break; case SIDEHALO: /* Allow floating-point and increase by factor of 1000 */ /* to accommodate "units microns". */ /* Warning: Due to some gcc bug with an i686 FPU, using a */ /* result from atof() with a static value like 1000 */ /* produces a NaN result! sscanf() seems to be safe. . . */ sscanf(argv[1], "%lg", &dhalo); dhalo *= (double)1000.0; ExtCurStyle->exts_sideCoupleHalo = (int)dhalo; break; case FRINGESHIELDHALO: /* This is deprecated. . . Ignore */ break; case PERIMC: DBTechNoisyNameMask(argv[2], &types2); TTMaskSetMask(allExtractTypes, &types2); capVal = aToCap(argv[3]); if (capVal == (CapValue) 0) break; for (s = TT_TECHDEPBASE; s < DBNumTypes; s++) for (t = 0; t < DBNumTypes; t++) if (TTMaskHasType(&types1, s) && TTMaskHasType(&types2, t)) { ExtCurStyle->exts_perimCap[s][t] = capVal; TTMaskSetType(&ExtCurStyle->exts_perimCapMask[s], t); } break; case RESIST: { float chop = 1.0; if (!StrIsInt(argv[2])) { if (!strcmp(argv[2], "None")) { for (t = TT_TECHDEPBASE; t < DBNumTypes; t++) if (TTMaskHasType(&types1, t)) TTMaskClearType(&ExtCurStyle->exts_activeTypes, t); break; } else { TxError("Resist argument must be integer or \"None\".\n"); break; } } else resVal = aToRes(argv[2]); if (argc == 4) chop = atof(argv[3]); class = ExtCurStyle->exts_numResistClasses++; for (t = TT_TECHDEPBASE; t < DBNumTypes; t++) if (TTMaskHasType(&types1, t)) { ExtCurStyle->exts_sheetResist[t] = resVal; ExtCurStyle->exts_cornerChop[t] = chop; ExtCurStyle->exts_typeToResistClass[t] = class; } ExtCurStyle->exts_resistByResistClass[class] = resVal; ExtCurStyle->exts_typesByResistClass[class] = types1; } break; case RSCALE: ExtCurStyle->exts_resistScale = atoi(argv[1]); break; case STEP: val = (int)atof(argv[1]); if (val <= 0) { TechError("Hierarchical interaction step size must be > 0\n"); return (FALSE); } ExtCurStyle->exts_stepSize = val; break; case SUBSTRATE: /* If the last entry starts with '-', then use it to set */ /* the shield types. Otherwise, the shield types mask is */ /* NULL. */ idTypes = DBZeroTypeBits; if (*argv[argc - 1] == '-') { if ((DBTechNameMask(argv[argc - 1] + 1, &idTypes)) == 0) idTypes = DBZeroTypeBits; else TTMaskSetMask(allExtractTypes, &idTypes); argc--; } TTMaskZero(&ExtCurStyle->exts_globSubstrateTypes); TTMaskZero(&ExtCurStyle->exts_globSubstrateShieldTypes); TTMaskSetMask(&ExtCurStyle->exts_globSubstrateTypes, &types1); ExtCurStyle->exts_globSubstrateShieldTypes = idTypes; ExtCurStyle->exts_globSubstratePlane = DBTechNoisyNamePlane(argv[2]); /* The "default" substrate type is a type that is in the */ /* list of substrate types and exists on the substrate */ /* plane, where space on the same plane is also declared to */ /* be the substrate type. */ if (ExtCurStyle->exts_globSubstratePlane != -1) { TileType subType; for (subType = TT_TECHDEPBASE; subType < DBNumUserLayers; subType++) if (TTMaskHasType(&ExtCurStyle->exts_globSubstrateTypes, subType)) if (DBPlane(subType) == ExtCurStyle->exts_globSubstratePlane) { ExtCurStyle->exts_globSubstrateDefaultType = subType; break; } } /* Handle optional substrate node name */ if (argc == 4) ExtCurStyle->exts_globSubstrateName = StrDup((char **)NULL, argv[3]); break; case NOPLANEORDER: { if ( ExtCurStyle->exts_planeOrderStatus == seenPlaneOrder ) TechError("\"noplaneordering\" specified after \"planeorder\".\n"); else ExtCurStyle->exts_planeOrderStatus = noPlaneOrder ; } break; case PLANEORDER: { int pnum = (spointertype) dbTechNameLookup(argv[1], &dbPlaneNameLists); int pos = atoi(argv[2]); if ( ExtCurStyle->exts_planeOrderStatus == noPlaneOrder ) { TechError("\"planeorder\" specified after \"noplaneordering\".\n"); } ExtCurStyle->exts_planeOrderStatus = seenPlaneOrder ; if (pnum < 0) TechError("Unknown planeorder plane %s\n", argv[1]); else if (pos < 0 || pos >= DBNumPlanes-PL_TECHDEPBASE) TechError("Planeorder index must be [0..%d]\n", DBNumPlanes-PL_TECHDEPBASE-1); else ExtCurStyle->exts_planeOrder[pnum] = pos; } break; } return (TRUE); usage: TechError("Malformed line for keyword %s. Correct usage:\n\t%s %s\n", kp->k_name, kp->k_name, kp->k_usage); return (TRUE); diffplane: TechError("Overlapped types in \"sideoverlap\" rule must be on a\n" "\tdifferent plane from intypes and outtypes.\n"); return (TRUE); } /* * ---------------------------------------------------------------------------- * * ExtTechFinal -- * * Postprocess the technology specific information for extraction. * Builds the connectivity tables exts_nodeConn[], exts_resistConn[], * and exts_deviceConn[]. * * Results: * None. * * Side effects: * Initializes the tables mentioned above. * Leaves ExtCurStyle pointing to the first style in the list * ExtAllStyles. * * ---------------------------------------------------------------------------- */ void ExtTechFinal() { ExtStyle *es; TileType s, t; /* Create a "default" style if there isn't one */ if (ExtAllStyles == NULL) { ExtAllStyles = (ExtKeep *)mallocMagic(sizeof(ExtKeep)); ExtAllStyles->exts_next = NULL; ExtAllStyles->exts_name = StrDup((char **) NULL, "default"); ExtCurStyle = extTechStyleNew(); ExtCurStyle->exts_name = ExtAllStyles->exts_name; ExtCurStyle->exts_status = TECH_LOADED; } extTechFinalStyle(ExtCurStyle); /* Any type in the connection tables that is connected to */ /* something other than itself is added to the list of */ /* extractable types. */ for (t = TT_TECHDEPBASE; t < DBNumUserLayers; t++) { TileTypeBitMask mask; TTMaskZero(&mask); TTMaskSetMask(&mask, &DBConnectTbl[t]); TTMaskClearType(&mask, t); if (!TTMaskIsZero(&mask)) TTMaskSetType(allExtractTypes, t); } /* Any type that wasn't found anywhere in the extract section */ /* is considered non-electrical. */ for (s = TT_TECHDEPBASE; s < DBNumUserLayers; s++) if (!TTMaskHasType(allExtractTypes, s)) { TxPrintf("The following types are not handled by extraction and will" " be treated as non-electrical types:\n"); TxPrintf(" "); for (t = TT_TECHDEPBASE; t < DBNumUserLayers; t++) if (!TTMaskHasType(allExtractTypes, t)) { TxPrintf("%s ", DBTypeLongNameTbl[t]); TTMaskClearType(&ExtCurStyle->exts_activeTypes, t); } TxPrintf("\n"); break; } } void extTechFinalStyle(style) ExtStyle *style; { TileTypeBitMask maskBits; TileType r, s, t; int p, p1, missing, conflict; int indicis[NP]; for (r = TT_TECHDEPBASE; r < DBNumTypes; r++) { maskBits = style->exts_nodeConn[r] = DBConnectTbl[r]; if (!TTMaskHasType(&style->exts_deviceMask, r)) { TTMaskZero(&style->exts_deviceConn[r]); } for (s = TT_TECHDEPBASE; s < DBNumTypes; s++) { if (TTMaskHasType(&maskBits, s)) if (style->exts_typeToResistClass[s] != style->exts_typeToResistClass[r]) TTMaskClearType(&maskBits, s); } style->exts_resistConn[r] = maskBits; } /* r ranges over types, s over resistance entries */ for (r = TT_TECHDEPBASE; r < DBNumTypes; r++) { s = style->exts_typeToResistClass[r]; if (s >= 0) TTMaskClearMask(&style->exts_typesResistChanged[r], &style->exts_typesByResistClass[s]); } /* * Residue check: * We have ignored all contact types when parsing parasitic * capacitances. Now we need to add them. For each contact * type, add the contact type to the types lists accordingly. * Note that we don't have to record any cap values, since the * extraction routine dissolves contacts into their residue * types when computing the parasitics. But, the type must be * in the type lists or contact tiles will be passed over during * searches. */ for (s = TT_TECHDEPBASE; s < DBNumTypes; s++) { TileTypeBitMask rmask; PlaneMask pMask; TileType q; if (!DBIsContact(s)) continue; pMask = DBTypePlaneMaskTbl[s]; for (p = 0; p < DBNumPlanes; p++) { if (PlaneMaskHasPlane(pMask, p)) { TTMaskSetType(&style->exts_overlapTypes[p], s); TTMaskSetType(&style->exts_sideTypes[p], s); } } DBFullResidueMask(s, &rmask); for (r = TT_TECHDEPBASE; r < DBNumUserLayers; r++) { if (!TTMaskHasType(&rmask, r)) continue; TTMaskSetMask(&style->exts_sideEdges[s], &style->exts_sideEdges[r]); for (q = TT_TECHDEPBASE; q < DBNumUserLayers; q++) { if (TTMaskHasType(&style->exts_overlapOtherTypes[q], r)) TTMaskSetType(&style->exts_overlapOtherTypes[q], s); for (t = TT_TECHDEPBASE; t < DBNumUserLayers; t++) if (TTMaskHasType(&style->exts_overlapShieldTypes[q][t], r) && !TTMaskHasType(&rmask, q) && !TTMaskHasType(&rmask, t)) TTMaskSetType(&style->exts_overlapShieldTypes[q][t], s); /* For sideOverlap, t is "outtypes" and includes space, so we */ /* must count from TT_SPACE, not TT_TECHDEPBASE. */ for (t = TT_SPACE; t < DBNumUserLayers; t++) if (TTMaskHasType(&style->exts_sideOverlapOtherTypes[q][t], r)) TTMaskSetType(&style->exts_sideOverlapOtherTypes[q][t], s); } } } /* * Consistency check: * If a type R shields S from T, make sure that R is listed as * being in the list of overlapped types for S, even if there * was no overlap capacitance explicitly specified for this * pair of types in an "overlap" line. This guarantees that * R will shield S from substrate even if there is no capacitance * associated with the overlap. */ for (s = TT_TECHDEPBASE; s < DBNumTypes; s++) for (t = TT_TECHDEPBASE; t < DBNumTypes; t++) { if (style->exts_overlapShieldPlanes[s][t] == 0) continue; for (r = TT_TECHDEPBASE; r < DBNumTypes; r++) { if (!TTMaskHasType(&style->exts_overlapShieldTypes[s][t], r)) continue; p1 = DBPlane(s); style->exts_overlapPlanes |= PlaneNumToMaskBit(p1); style->exts_overlapOtherPlanes[s] |= PlaneNumToMaskBit(DBPlane(r)); TTMaskSetType(&style->exts_overlapTypes[p1], s); TTMaskSetType(&style->exts_overlapOtherTypes[s], r); } } /* Finally, for all coupling type masks, remove those types */ /* that have been declared not to participate in extraction. */ for (s = TT_TECHDEPBASE; s < DBNumTypes; s++) { TTMaskAndMask(&style->exts_overlapOtherTypes[s], &style->exts_activeTypes); TTMaskAndMask(&style->exts_perimCapMask[s], &style->exts_activeTypes); for (t = TT_TECHDEPBASE; t < DBNumTypes; t++) { TTMaskAndMask(&style->exts_overlapShieldTypes[s][t], &style->exts_activeTypes); TTMaskAndMask(&style->exts_sideOverlapOtherTypes[s][t], &style->exts_activeTypes); TTMaskAndMask(&style->exts_sideCoupleOtherEdges[s][t], &style->exts_activeTypes); } } for (p = 0; p < DBNumPlanes; p++) { TTMaskAndMask(&style->exts_overlapTypes[p], &style->exts_activeTypes); TTMaskAndMask(&style->exts_sideTypes[p], &style->exts_activeTypes); } if ( style->exts_planeOrderStatus == noPlaneOrder ) return /* no need to check */ ; /* Else Check to make sure the plane order is a permutation of the numbers 0..DBNumPlanes-DBNumPlanes-1 */ for (p1 = PL_TECHDEPBASE; p1 < DBNumPlanes; p1++) { indicis[p1] = 0; } for (p1 = PL_TECHDEPBASE; p1 < DBNumPlanes; p1++) { int pn = style->exts_planeOrder[p1]+PL_TECHDEPBASE; if (pn >= PL_TECHDEPBASE && pn < DBNumPlanes) indicis[pn]++; } conflict = FALSE; missing = FALSE; for (p1 = PL_TECHDEPBASE; p1 < DBNumPlanes; p1++) { if (indicis[p1] > 1) conflict = TRUE ; if (indicis[p1] < 1) missing = TRUE ; } if (!conflict && !missing) /* Everything was ok */ goto zinit; TxError ("\nWarning: Extraction Style %s\n", style -> exts_name); if (conflict) { TxError (" Conflicting planeorder for plane(s):\n "); for (p1 = PL_TECHDEPBASE; p1 < DBNumPlanes; p1++) { if (indicis[p1] > 1) TxError (" %s,", DBPlaneLongNameTbl[p1]); } TxError("\n"); } if (missing) { TxError (" Missing planeorder for plane(s):\n "); for (p1 = PL_TECHDEPBASE; p1 < DBNumPlanes; p1++) { if (indicis[p1] < 1) TxError (" %s,", DBPlaneLongNameTbl[p1]); } TxError("\n"); } TxError(" Magic will use the default planeorder for this style:\n "); for (p1 = PL_TECHDEPBASE; p1 < DBNumPlanes; p1++) { style->exts_planeOrder[p1] = p1 - PL_TECHDEPBASE ; TxError(" %s=%d,",DBPlaneLongNameTbl[p1], style->exts_planeOrder[p1]); } TxError("\n"); /* Now that we have a plane ordering, we can apply default height */ /* and thickness values for those layers. */ zinit: for (r = TT_TECHDEPBASE; r < DBNumTypes; r++) { if (style->exts_thick[r] == 0) style->exts_thick[r] = 0.05; if (style->exts_height[r] == 0) style->exts_height[r] = 0.1 * style->exts_planeOrder[DBPlane(r)]; } /* If global variable "doConvert" is TRUE, then we convert from */ /* microns to lambda and microns^2 to lambda^2. */ if (doConvert) { /* Use current CIF output scale for determining the scale */ /* factor between micron units in the extract section and */ /* lambda units of the database (conversion from lambda to */ /* internal units is done separately). */ float dscale = CIFGetOutputScale(1000); float dsq = dscale * dscale; CapValue scalefac = (CapValue)dscale; CapValue sqfac = scalefac * scalefac; for (r = 0; r < DBNumTypes; r++) { ExtDevice *devptr; for (devptr = style->exts_device[r]; devptr; devptr = devptr->exts_next) { ParamList *chkParam; HashEntry *he; ResValue res; devptr->exts_deviceSDCap *= sqfac; devptr->exts_deviceGateCap *= sqfac; for (chkParam = devptr->exts_deviceParams; chkParam; chkParam = chkParam->pl_next) { if (chkParam->pl_offset != 0) { if (chkParam->pl_param[0] == 'a') chkParam->pl_offset /= dsq; else chkParam->pl_offset /= dscale; } if (chkParam->pl_minimum > chkParam->pl_maximum) continue; else if (chkParam->pl_param[0] == 'a') { chkParam->pl_maximum /= dsq; chkParam->pl_minimum /= dsq; } else { chkParam->pl_maximum /= dscale; chkParam->pl_minimum /= dscale; } } he = HashLookOnly(&devptr->exts_deviceResist, "area"); if (he != NULL) { res = (ResValue)(spointertype)(HashGetValue(he)); res /= dsq; HashSetValue(he, (spointertype)res); } he = HashLookOnly(&devptr->exts_deviceResist, "perimeter"); if (he != NULL) { res = (ResValue)(spointertype)(HashGetValue(he)); res /= dscale; HashSetValue(he, (spointertype)res); } he = HashLookOnly(&devptr->exts_deviceResist, "linear"); if (he != NULL) { res = (ResValue)(spointertype)(HashGetValue(he)); res /= dscale; HashSetValue(he, (spointertype)res); } } style->exts_areaCap[r] *= sqfac; for (s = 0; s < DBNumTypes; s++) { EdgeCap *ec; style->exts_perimCap[r][s] *= scalefac; style->exts_overlapCap[r][s] *= sqfac; style->exts_overlapMult[r][s] *= scalefac; for (ec = style->exts_sideOverlapCap[r][s]; ec != NULL; ec = ec->ec_next) ec->ec_cap *= scalefac; // Note that because sidewall caps are referred to // a specific distance, the value (run / separation) // is unscaled, so the capacitance does not get // modified by the scalefactor. However, the lambda // reference for sidewall cap is 2 lambda, so if // the reference is to be interpreted as 1 micron, // the value needs to be divided by 2 (the factor of // 2 is made up by the fact that the sidewall is // independently accumulated on each plate of the // capacitor). ALSO: ec_offset was multiplied up by // 1000 so that micron distances could be saved as // integer values, so that factor needs to be divided out. for (ec = style->exts_sideCoupleCap[r][s]; ec != NULL; ec = ec->ec_next) { ec->ec_cap *= 0.5; ec->ec_offset = (int)(((float)ec->ec_offset / dscale) + 0.5); ec->ec_offset /= 1000; } } /* Layer thickness and height are in microns, but are floating-point */ style->exts_thick[r] /= dscale; style->exts_height[r] /= dscale; /* Only this antenna coefficient has dimensioned units */ style->exts_antennaRatio[r].ratioDiffA *= dsq; } /* side halo, fringe shield halo, and step size are also in microns */ style->exts_sideCoupleHalo = (int)(((float)style->exts_sideCoupleHalo / dscale) + 0.5); style->exts_stepSize = (int)(((float)style->exts_stepSize / dscale) + 0.5); } /* Avoid setting stepSize to zero, or extraction will hang! */ if (style->exts_stepSize <= 0) { TxError("Warning: zero step size! Resetting to default.\n"); style->exts_stepSize = 100; /* Revert to default */ } /* We had multiplied sideCoupleHalo by 1000 to accommodate a */ /* floating-point value in microns, whether or not doConvert was */ /* needed, so normalize it back to lambda units. */ style->exts_sideCoupleHalo /= 1000; /* Ditto for the parameter maximum/minimum limits */ for (r = 0; r < DBNumTypes; r++) { ExtDevice *devptr; ParamList *chkParam; for (devptr = style->exts_device[r]; devptr; devptr = devptr->exts_next) { for (chkParam = devptr->exts_deviceParams; chkParam; chkParam = chkParam->pl_next) { if (chkParam->pl_offset != 0) chkParam->pl_offset /= 1000; if (chkParam->pl_minimum > chkParam->pl_maximum) continue; chkParam->pl_maximum /= 1000; chkParam->pl_minimum /= 1000; } } } } /* * ---------------------------------------------------------------------------- * ExtTechScale -- * * Scale all extraction values appropriately when rescaling the grid. * ---------------------------------------------------------------------------- */ void ExtTechScale(scalen, scaled) int scalen; /* Scale numerator */ int scaled; /* Scale denominator */ { ExtStyle *style = ExtCurStyle; EdgeCap *ec; int i, j; float sqn, sqd; if (style == NULL) return; sqn = (float)(scalen * scalen); sqd = (float)(scaled * scaled); style->exts_unitsPerLambda = style->exts_unitsPerLambda * (float)scalen / (float)scaled; DBScaleValue(&style->exts_sideCoupleHalo, scaled, scalen); DBScaleValue(&style->exts_stepSize, scaled, scalen); for (i = 0; i < DBNumTypes; i++) { ExtDevice *devptr; ParamList *chkParam; style->exts_areaCap[i] *= sqn; style->exts_areaCap[i] /= sqd; for (devptr = style->exts_device[i]; devptr; devptr = devptr->exts_next) { devptr->exts_deviceSDCap *= sqn; devptr->exts_deviceSDCap /= sqd; devptr->exts_deviceGateCap *= sqn; devptr->exts_deviceGateCap /= sqd; for (chkParam = devptr->exts_deviceParams; chkParam; chkParam = chkParam->pl_next) { if (chkParam->pl_offset != 0) { if (chkParam->pl_param[0] == 'a') { chkParam->pl_offset *= sqd; chkParam->pl_offset /= sqn; } else { chkParam->pl_offset *= scaled; chkParam->pl_offset /= scalen; } } if (chkParam->pl_minimum > chkParam->pl_maximum) continue; else if (chkParam->pl_param[0] == 'a') { chkParam->pl_maximum *= sqd; chkParam->pl_maximum /= sqn; chkParam->pl_minimum *= sqd; chkParam->pl_minimum /= sqn; } else { chkParam->pl_maximum *= scaled; chkParam->pl_maximum /= scalen; chkParam->pl_minimum *= scaled; chkParam->pl_minimum /= scalen; } } } style->exts_height[i] *= scaled; style->exts_height[i] /= scalen; style->exts_thick[i] *= scaled; style->exts_thick[i] /= scalen; style->exts_antennaRatio[i].ratioDiffA *= sqn; style->exts_antennaRatio[i].ratioDiffA /= sqd; for (j = 0; j < DBNumTypes; j++) { style->exts_perimCap[i][j] *= scalen; style->exts_perimCap[i][j] /= scaled; style->exts_overlapCap[i][j] *= sqn; style->exts_overlapCap[i][j] /= sqd; /* Typo fixed in 7.2.57 */ style->exts_overlapMult[i][j] *= scalen; style->exts_overlapMult[i][j] /= scaled; // Do not scale sidewall cap, for while the value is // per distance, the distance is referred to a separation // distance in the same units, so the cap never scales. for (ec = style->exts_sideCoupleCap[i][j]; ec != NULL; ec = ec->ec_next) { // ec->ec_cap *= scalen; // ec->ec_cap /= scaled; DBScaleValue(&(ec->ec_offset), scaled, scalen); } for (ec = style->exts_sideOverlapCap[i][j]; ec != NULL; ec = ec->ec_next) { ec->ec_cap *= scalen; ec->ec_cap /= scaled; } } } return; }