magic/extract/ExtTech.c

3962 lines
111 KiB
C

/*
* 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 <stdio.h>
#include <stdlib.h> /* for strtod() */
#include <string.h>
#include <strings.h>
#include <math.h>
#include <ctype.h> /* 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(&nottypes, &types);
TTMaskAndMask(&types, &DBPlaneTypes[plane1]);
TTMaskAndMask(&nottypes, &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(&nottypes, 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], &nottypes);
for (t = 0; t < DBNumTypes; t++)
{
if (!TTMaskHasType(&nottypes, 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(&nottypes, &types);
TTMaskAndMask(&types, &DBPlaneTypes[plane1]);
TTMaskAndMask(&nottypes, &DBPlaneTypes[plane1]);
DBTechNoisyNameMask(argv[3], &ov);
TTMaskSetMask(allExtractTypes, &ov);
plane2 = DBTechNoisyNamePlane(argv[4]);
// TTMaskCom2(&notov, &ov);
TTMaskZero(&notov);
TTMaskSetType(&notov, TT_SPACE);
TTMaskAndMask(&ov, &DBPlaneTypes[plane2]);
TTMaskAndMask(&notov, &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], &nottypes);
for (t = TT_SPACE; t < DBNumTypes; t++)
{
if (!TTMaskHasType(&nottypes, 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 "<type>=<name>*<scale>" 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, 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;
}