A number of changes:

1) Corrected spurious error messages about cells already existing
   in GDS when using "flatten" or "flatglob".
2) Fixed handling of resistance as a subcircuit parameter
3) Added area and perimeter resistance for a device;  this is done
   through the "devresist" statement in the tech file, which is an
   extension of the original "fetresist" statement.  Where "fetresist"
   only supported type "linear", "devresist" supports types "area"
   and "perimeter".
4) Support for CDL syntax, including generating subcircuit-like
   parameters for components starting with SPICE-standard prefixes
   like M, R, C, etc., adding "/" between pins and subcircuit name,
   and saving the file as ".cdl" instead of ".spice".
5) Estimated L and W for devices whose geometry is complex and do not
   reduce to a simple rectangle.  L and W are estimated as the square
   root of the area.
6) Changed the method of extracting L and W for diodes to use the same
   method as capacitors.  Note that diodes are not usually specified
   by L and W, but if they are, this will produce the right result.
7) Corrected the reported filename and line number when printing error
   messages related to errors inside a technology file, when the
   technology file uses "include" to combine multiple files.
This commit is contained in:
R. Timothy Edwards
2025-10-01 15:17:49 -04:00
parent b1c7b52ed2
commit 78f7d22796
11 changed files with 321 additions and 45 deletions
+114 -6
View File
@@ -1152,7 +1152,7 @@ ExtSortTerminals(tran, ll)
* one terminal.
*
* Results:
* None
* TRUE if L and W were computed, FALSE if not.
*
* Side Effects:
* Puts effective length and width into the pointers
@@ -1160,7 +1160,7 @@ ExtSortTerminals(tran, ll)
*----------------------------------------------------------------------
*/
void
bool
extComputeCapLW(rlengthptr, rwidthptr)
int *rlengthptr, *rwidthptr;
{
@@ -1174,7 +1174,7 @@ extComputeCapLW(rlengthptr, rwidthptr)
if (lb == NULL)
{
TxError("extract: Can't get capacitor L and W\n");
return; /* error condition */
return FALSE; /* error condition */
}
bbox = lb->r;
for (lb = extSpecialBounds[0]; lb != NULL; lb = lb->b_next)
@@ -1182,6 +1182,7 @@ extComputeCapLW(rlengthptr, rwidthptr)
*rwidthptr = bbox.r_xtop - bbox.r_xbot;
*rlengthptr = bbox.r_ytop - bbox.r_ybot;
return TRUE;
}
/*
@@ -1789,6 +1790,8 @@ extOutputDevParams(reg, devptr, outFile, length, width, areavec, perimvec)
int *perimvec;
{
ParamList *chkParam;
HashEntry *he;
ResValue resvalue;
for (chkParam = devptr->exts_deviceParams; chkParam
!= NULL; chkParam = chkParam->pl_next)
@@ -1863,6 +1866,39 @@ extOutputDevParams(reg, devptr, outFile, length, width, areavec, perimvec)
(extTransRec.tr_devrec->exts_deviceSDCap
* extTransRec.tr_perim));
break;
case 'r':
/* If the device has an "area" resistance specified
* by "devresist" in the tech file, use that. If
* it has a "perimeter" resistance specified, use
* that as well. If neither, then find the sheet
* resistance of the device identifier layer and
* use that.
*/
resvalue = (ResValue)0.0;
he = HashLookOnly(&extTransRec.tr_devrec->exts_deviceResist, "area");
if (he != NULL)
{
resvalue = (ResValue)(pointertype)HashGetValue(he);
resvalue /= (ResValue)reg->treg_area;
he = HashLookOnly(&extTransRec.tr_devrec->exts_deviceResist,
"perimeter");
if (he != NULL)
{
ResValue perimr;
perimr = (ResValue)(pointertype)HashGetValue(he);
perimr /= (ResValue)extTransRec.tr_perim;
resvalue += perimr;
}
}
else
{
resvalue = ExtCurStyle->exts_sheetResist[reg->treg_type]
* (double)length / (double)width;
}
fprintf(outFile, " %c=%g", chkParam->pl_param[0], (float)resvalue);
break;
case 's':
case 'x':
case 'y':
@@ -2143,10 +2179,11 @@ extOutputDevices(def, transList, outFile)
NodeRegion *node, *subsNode;
TransRegion *reg;
ExtDevice *devptr, *deventry;
char *subsName;
ParamList *chkParam;
FindRegion arg;
LabelList *ll;
TileType t;
char *subsName;
int nsd, length, width, n, i, ntiles, corners, tn, rc, termcount;
double dres, dcap;
char mesg[256];
@@ -2668,7 +2705,70 @@ extOutputDevices(def, transList, outFile)
case DEV_DIODE: /* Only handle the optional substrate node */
case DEV_NDIODE:
case DEV_PDIODE:
/* Diode length and width are computed like capacitor */
/* length and width. This operation is expensive, so */
/* do this ONLY if length and width are specified as */
/* parameters. */
devptr = extDevFindParamMatch(devptr, length, width);
for (chkParam = devptr->exts_deviceParams; chkParam != NULL;
chkParam = chkParam->pl_next)
{
char param0;
if (chkParam->pl_name == NULL) continue;
param0 = tolower(chkParam->pl_param[0]);
if (param0 == 'l' || param0 == 'w') break;
}
if (chkParam != NULL)
{
for (n = 0; n < extTransRec.tr_nterm &&
extTransRec.tr_termnode[n] != NULL; n++);
if (n > 0)
{
LinkedBoundary *lb;
extSpecialBounds = (LinkedBoundary **)mallocMagic(n *
sizeof(LinkedBoundary *));
for (i = 0; i < n; i++) extSpecialBounds[i] = NULL;
/* Mark with reg and process each perimeter segment */
arg.fra_uninit = (ClientData) extTransRec.tr_gatenode;
arg.fra_region = (ExtRegion *) reg;
arg.fra_each = extAnnularTileFunc;
(void) ExtFindNeighbors(reg->treg_tile, arg.fra_pNum, &arg);
if (extComputeCapLW(&length, &width) == FALSE)
{
/* May be a complex area; fall back on
* computing an equivalent square area.
*/
length = (int)(sqrt((double)extTransRec.tr_termarea[0])
+ 0.5);
width = length;
}
/* Free the lists */
for (i = 0; i < n; i++)
for (lb = extSpecialBounds[i]; lb != NULL; lb = lb->b_next)
freeMagic((char *)lb);
freeMagic((char *)extSpecialBounds);
/* Put the region list back the way we found it: */
/* Re-mark with extTransRec.tr_gatenode */
arg.fra_uninit = (ClientData) reg;
arg.fra_region = (ExtRegion *) extTransRec.tr_gatenode;
arg.fra_each = (int (*)()) NULL;
(void) ExtFindNeighbors(reg->treg_tile, arg.fra_pNum, &arg);
}
}
fprintf(outFile, "%s %s",
extDevTable[(unsigned char)devptr->exts_deviceClass],
devptr->exts_deviceName);
@@ -2885,7 +2985,15 @@ extOutputDevices(def, transList, outFile)
arg.fra_each = extAnnularTileFunc;
(void) ExtFindNeighbors(reg->treg_tile, arg.fra_pNum, &arg);
extComputeCapLW(&length, &width);
if (extComputeCapLW(&length, &width) == FALSE)
{
/* May be a complex area; fall back on
* computing an equivalent square area.
*/
length = (int)(sqrt((double)extTransRec.tr_termarea[0])
+ 0.5);
width = length;
}
/* Free the lists */
@@ -2907,7 +3015,7 @@ extOutputDevices(def, transList, outFile)
{
/* (Nothing) */
}
else /* SPICE semiconductor resistor */
else /* SPICE semiconductor capacitor */
{
if ((length * width) > reg->treg_area)
{
+45 -12
View File
@@ -81,7 +81,7 @@ typedef enum
AREAC, CONTACT, CSCALE,
DEFAULTAREACAP, DEFAULTOVERLAP, DEFAULTPERIMETER, DEFAULTSIDEOVERLAP,
DEFAULTSIDEWALL,
DEVICE, FET, FETRESIST, FRINGESHIELDHALO,
DEVICE, DEVRESIST, FET, FETRESIST, FRINGESHIELDHALO,
HEIGHT, ANTENNA, MODEL, TIEDOWN, LAMBDA, OVERC,
PERIMC, PLANEORDER, NOPLANEORDER, RESIST, RSCALE, SIDEHALO, SIDEOVERLAP,
SIDEWALL, STEP, STYLE, SUBSTRATE, UNITS, VARIANT
@@ -122,7 +122,10 @@ static const keydesc keyTable[] = {
"types plane capacitance [offset]"},
{"device", DEVICE, 4, 10,
"device dev-type types options..."},
"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"},
@@ -1958,7 +1961,6 @@ ExtTechLine(sectionName, argc, argv)
char *subsName, *transName, *cp, *endptr, *paramName;
TileType s, t, r, o;
const keydesc *kp, *dv;
bool isLinear;
HashEntry *he;
EdgeCap *cnew;
ExtKeep *es, *newStyle;
@@ -2183,6 +2185,7 @@ ExtTechLine(sectionName, argc, argv)
case CONTACT:
case FET:
case FETRESIST:
case DEVRESIST:
case HEIGHT:
case ANTENNA:
case TIEDOWN:
@@ -2545,11 +2548,17 @@ ExtTechLine(sectionName, argc, argv)
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. */
class = dv->k_key;
switch (class)
{
case DEV_SUBCKT:
@@ -2575,6 +2584,7 @@ ExtTechLine(sectionName, argc, argv)
}
break;
}
#endif
gscap = (CapValue) 0;
gccap = (CapValue) 0;
@@ -2765,14 +2775,16 @@ ExtTechLine(sectionName, argc, argv)
TTMaskSetMask(allExtractTypes, &termtypes[0]);
termtypes[1] = DBZeroTypeBits;
if ((argc > 5) && strcmp(argv[5], "None"))
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) subsName = argv[6];
if ((argc > 6) && (strchr(argv[6], '=') == NULL))
subsName = argv[6];
break;
}
@@ -2812,7 +2824,6 @@ ExtTechLine(sectionName, argc, argv)
}
devptr->exts_deviceResist.ht_table = (HashEntry **) NULL;
HashInit(&devptr->exts_deviceResist, 8, HT_STRINGKEYS);
devptr->exts_linearResist = 0;
devptr->exts_next = ExtCurStyle->exts_device[t];
ExtCurStyle->exts_device[t] = devptr;
@@ -2834,14 +2845,14 @@ ExtTechLine(sectionName, argc, argv)
}
break;
case DEVRESIST:
case FETRESIST:
if (!StrIsInt(argv[3]))
if (!StrIsNumeric(argv[3]))
{
TechError("Fet resistivity %s must be numeric\n", argv[3]);
TechError("Device resistivity %s must be numeric\n", argv[3]);
break;
}
resVal = aToRes(argv[3]);
isLinear = (strcmp(argv[2], "linear") == 0);
for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
{
ExtDevice *devptr;
@@ -2851,8 +2862,6 @@ ExtTechLine(sectionName, argc, argv)
{
he = HashFind(&devptr->exts_deviceResist, argv[2]);
HashSetValue(he, (spointertype)resVal);
if (isLinear)
devptr->exts_linearResist = resVal;
}
}
}
@@ -3696,6 +3705,8 @@ zinit:
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;
@@ -3723,6 +3734,28 @@ zinit:
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;
-1
View File
@@ -506,7 +506,6 @@ typedef struct extDevice
*/
HashTable exts_deviceResist;
ResValue exts_linearResist;
/*
* Mask of the types of tiles that connect to the channel terminals