Added a new method to the extraction which allows parameter limits

to be declared for each device model, so that different models can
be specified for different parameter ranges.  For example, this
will simplify the definition of the high-sheet-rho poly resistors
and the bipolar transistors in sky130 by removing the need for ID
marker layers;  it also allows the correct device model to be
extracted when reading data from GDS that does not contain the
extra (and not foundry-approved) ID markers.
This commit is contained in:
Tim Edwards 2023-07-06 12:35:47 -04:00
parent cc4eef4eb1
commit b899a500d5
4 changed files with 398 additions and 26 deletions

View File

@ -1 +1 @@
8.3.410
8.3.411

View File

@ -89,8 +89,9 @@ typedef struct /* Position of each terminal (below) tile position */
/* Field definitions for tr_devmatch */
#define MATCH_ID 0x01 /* Device matches identifier in devrec */
#define MATCH_SUB 0x02 /* Device matches substrate type in devrec */
#define MATCH_TERM 0x04 /* Device matches terminal in devrec */
#define MATCH_PARAM 0x02 /* Device is compatible with parameter range */
#define MATCH_SUB 0x04 /* Device matches substrate type in devrec */
#define MATCH_TERM 0x08 /* Device matches terminal in devrec */
/* (additional fields: bit shifts up by 1 for each defined device terminal) */
struct transRec
@ -1647,16 +1648,30 @@ extOutputParameters(def, transList, outFile)
for (devptr = ExtCurStyle->exts_device[t]; devptr; devptr = devptr->exts_next)
{
bool has_output = FALSE;
/* Do not output parameters for ignored devices */
if (!strcmp(devptr->exts_deviceName, "Ignore")) continue;
/* Do a quick first pass to determine if there is anything
* to output (only entries with non-NULL pl_name get output).
*/
plist = devptr->exts_deviceParams;
if (plist != (ParamList *)NULL)
for (; plist != NULL; plist = plist->pl_next)
if (plist->pl_name != NULL)
{
has_output = TRUE;
break;
}
if (has_output)
{
fprintf(outFile, "parameters %s", devptr->exts_deviceName);
plist = devptr->exts_deviceParams;
for (; plist != NULL; plist = plist->pl_next)
{
if (plist->pl_param[1] != '\0')
if (plist->pl_name == NULL) continue;
else if (plist->pl_param[1] != '\0')
{
if (plist->pl_scale != 1.0)
fprintf(outFile, " %c%c=%s*%g",
@ -1725,6 +1740,7 @@ extOutputDevParams(reg, devptr, outFile, length, width, areavec)
for (chkParam = devptr->exts_deviceParams; chkParam
!= NULL; chkParam = chkParam->pl_next)
{
if (chkParam->pl_name == NULL) continue;
switch(tolower(chkParam->pl_param[0]))
{
case 'a':
@ -1796,6 +1812,161 @@ typedef struct _extareaperimdata {
ExtNodeList *eapd_shared;
} ExtAreaPerimData;
/*
* ----------------------------------------------------------------------------
*
* extDevFindParamMatch --
*
* Routine which checks parameter values of a device against parameter
* ranges specified for the device model. If the parameters of the
* current device do not match the ranges, then another device record
* with matching parameters will be sought, and returned if found.
* If no device with matching parameters is found, then the original
* device record is returned, and a warning about parameter mismatch
* is printed.
*
* When looking for alternate parameter ranges, all other parameter
* record values must be the same as the current one. A record with
* no range (min > max) always matches.
*
* Return value:
* Pointer to a device record.
*
* ----------------------------------------------------------------------------
*/
ExtDevice *
extDevFindParamMatch(devptr, length, width)
ExtDevice *devptr;
int length; /* Computed effective length of device */
int width; /* Computed effective width of device */
{
ExtDevice *newdevptr, *nextdev;
int i;
while (TRUE)
{
ParamList *chkParam;
bool out_of_bounds = FALSE;
newdevptr = devptr;
nextdev = devptr->exts_next;
for (chkParam = devptr->exts_deviceParams; chkParam != NULL;
chkParam = chkParam->pl_next)
{
if (chkParam->pl_minimum > chkParam->pl_maximum) continue;
switch (tolower(chkParam->pl_param[0]))
{
case 'a':
if (chkParam->pl_param[1] == '\0' ||
chkParam->pl_param[1] == '0')
{
int area = length * width;
if (area < chkParam->pl_minimum) out_of_bounds = TRUE;
if (area > chkParam->pl_maximum) out_of_bounds = TRUE;
}
else
{
int tidx = chkParam->pl_param[1] - '1';
int area = extTransRec.tr_termarea[tidx];
if (area < chkParam->pl_minimum) out_of_bounds = TRUE;
if (area > chkParam->pl_maximum) out_of_bounds = TRUE;
}
break;
case 'p':
if (chkParam->pl_param[1] == '\0' ||
chkParam->pl_param[1] == '0')
{
int perim = 2 * (length + width);
if (perim < chkParam->pl_minimum) out_of_bounds = TRUE;
if (perim > chkParam->pl_maximum) out_of_bounds = TRUE;
}
else
{
int tidx = chkParam->pl_param[1] - '1';
int perim = extTransRec.tr_termperim[tidx];
if (perim < chkParam->pl_minimum) out_of_bounds = TRUE;
if (perim > chkParam->pl_maximum) out_of_bounds = TRUE;
}
break;
case 'l':
if (chkParam->pl_param[1] == '\0' ||
chkParam->pl_param[1] == '0')
{
if (length < chkParam->pl_minimum) out_of_bounds = TRUE;
if (length > chkParam->pl_maximum) out_of_bounds = TRUE;
}
else if (chkParam->pl_param[1] > '0' && chkParam->pl_param[1] <= '9')
{
int tidx = chkParam->pl_param[1] - '1';
int len = extTransRec.tr_termlen[tidx];
if (len < chkParam->pl_minimum) out_of_bounds = TRUE;
if (len > chkParam->pl_maximum) out_of_bounds = TRUE;
}
break;
case 'w':
if (width < chkParam->pl_minimum) out_of_bounds = TRUE;
if (width > chkParam->pl_maximum) out_of_bounds = TRUE;
break;
default:
/* Do nothing; these parameters cannot be used for
* differentiating device models.
*/
break;
}
if (out_of_bounds) break;
}
if (chkParam == NULL) break;
/* Check that the next device record is compatible in all values
* except for parameters and name.
*/
if (nextdev != NULL)
{
if (nextdev->exts_deviceClass != devptr->exts_deviceClass)
nextdev = NULL;
else if (nextdev->exts_deviceSDCount != devptr->exts_deviceSDCount)
nextdev = NULL;
}
if (nextdev != NULL)
{
for (i = 0; i < nextdev->exts_deviceSDCount; i++)
{
if (!TTMaskEqual(&nextdev->exts_deviceSDTypes[i],
&devptr->exts_deviceSDTypes[i]))
{
nextdev = NULL;
break;
}
}
}
if (nextdev != NULL)
if (!TTMaskEqual(&nextdev->exts_deviceSubstrateTypes,
&devptr->exts_deviceSubstrateTypes))
nextdev = NULL;
if (nextdev != NULL)
if (!TTMaskEqual(&nextdev->exts_deviceIdentifierTypes,
&devptr->exts_deviceIdentifierTypes))
nextdev = NULL;
if (nextdev == NULL)
{
newdevptr = devptr; /* Return to original entry */
TxError("Device parameters do not match any extraction model.\n");
break;
}
else
devptr = nextdev;
}
return newdevptr;
}
/*
* ----------------------------------------------------------------------------
*
@ -2032,7 +2203,7 @@ extOutputDevices(def, transList, outFile)
else
{
DBSrPaintArea((Tile *)NULL, def->cd_planes[DBPlane(tt)],
&r, &tmask, extTransFindTermArea, (ClientData)&eapd);
&r, tmask, extTransFindTermArea, (ClientData)&eapd);
extTransRec.tr_termarea[termcount] = eapd.eapd_area;
extTransRec.tr_termperim[termcount] = eapd.eapd_perim;
}
@ -2173,27 +2344,23 @@ extOutputDevices(def, transList, outFile)
if (devptr->exts_deviceClass != DEV_FET)
fprintf(outFile, "device ");
fprintf(outFile, "%s %s",
extDevTable[devptr->exts_deviceClass],
devptr->exts_deviceName);
fprintf(outFile, " %d %d %d %d",
reg->treg_ll.p_x, reg->treg_ll.p_y,
reg->treg_ll.p_x + 1, reg->treg_ll.p_y + 1);
/* NOTE: The following code makes unreasonable simplifying */
/* assumptions about how to calculate device length and width. */
/* However, it is the same as was always used by ext2sim and */
/* ext2spice. By putting it here, where all the tile */
/* information exists, it is at least theoretically possible to */
/* write better routines that can deal with bends in resistors */
/* and transistors, annular devices, multiple-drain devices, */
/* etc., etc. */
/* Tim, 2/20/03 */
/* NOTE: The code for the old FET device makes unreasonable */
/* simplifying assumptions about how to calculate device length */
/* and width. The newer MOSFET and MSUBCKT and other devices */
/* compute proper length and width, including but not limited */
/* to dealing with bends and annular shapes. */
switch (devptr->exts_deviceClass)
{
case DEV_FET: /* old style, perimeter & area */
fprintf(outFile, "%s %s",
extDevTable[devptr->exts_deviceClass],
devptr->exts_deviceName);
fprintf(outFile, " %d %d %d %d",
reg->treg_ll.p_x, reg->treg_ll.p_y,
reg->treg_ll.p_x + 1, reg->treg_ll.p_y + 1);
fprintf(outFile, " %d %d \"%s\"",
reg->treg_area, extTransRec.tr_perim,
(subsName == NULL) ? "None" : subsName);
@ -2320,6 +2487,15 @@ extOutputDevices(def, transList, outFile)
}
devptr = extDevFindParamMatch(devptr, length, width);
fprintf(outFile, "%s %s",
extDevTable[devptr->exts_deviceClass],
devptr->exts_deviceName);
fprintf(outFile, " %d %d %d %d",
reg->treg_ll.p_x, reg->treg_ll.p_y,
reg->treg_ll.p_x + 1, reg->treg_ll.p_y + 1);
if (devptr->exts_deviceClass == DEV_MOSFET ||
devptr->exts_deviceClass == DEV_ASYMMETRIC ||
devptr->exts_deviceClass == DEV_BJT)
@ -2337,6 +2513,15 @@ extOutputDevices(def, transList, outFile)
case DEV_DIODE: /* Only handle the optional substrate node */
case DEV_NDIODE:
case DEV_PDIODE:
devptr = extDevFindParamMatch(devptr, length, width);
fprintf(outFile, "%s %s",
extDevTable[devptr->exts_deviceClass],
devptr->exts_deviceName);
fprintf(outFile, " %d %d %d %d",
reg->treg_ll.p_x, reg->treg_ll.p_y,
reg->treg_ll.p_x + 1, reg->treg_ll.p_y + 1);
extOutputDevParams(reg, devptr, outFile, length, width,
extTransRec.tr_termarea);
if (subsName != NULL)
@ -2452,6 +2637,16 @@ extOutputDevices(def, transList, outFile)
"Resistor has zero width");
}
devptr = extDevFindParamMatch(devptr, length, width);
fprintf(outFile, "%s %s",
extDevTable[devptr->exts_deviceClass],
devptr->exts_deviceName);
fprintf(outFile, " %d %d %d %d",
reg->treg_ll.p_x, reg->treg_ll.p_y,
reg->treg_ll.p_x + 1, reg->treg_ll.p_y + 1);
if (devptr->exts_deviceClass == DEV_RSUBCKT)
{
/* (Nothing) */
@ -2478,6 +2673,14 @@ extOutputDevices(def, transList, outFile)
case DEV_CAP:
case DEV_CAPREV:
case DEV_CSUBCKT:
fprintf(outFile, "%s %s",
extDevTable[devptr->exts_deviceClass],
devptr->exts_deviceName);
fprintf(outFile, " %d %d %d %d",
reg->treg_ll.p_x, reg->treg_ll.p_y,
reg->treg_ll.p_x + 1, reg->treg_ll.p_y + 1);
hasModel = strcmp(devptr->exts_deviceName, "None");
if (hasModel)
{
@ -2781,6 +2984,10 @@ extDevFindMatch(deventry, t)
if (!TTMaskEqual(&devptr->exts_deviceSubstrateTypes,
&deventry->exts_deviceSubstrateTypes)) continue;
if (matchflags & MATCH_PARAM) /* Must have compatible parameter range */
/* To be completed */
;
j = MATCH_TERM;
i = 0;
match = TRUE;

View File

@ -888,7 +888,8 @@ extTechStyleInit(style)
}
else
{
freeMagic(devptr->exts_deviceParams->pl_name);
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;
}
@ -2366,11 +2367,19 @@ ExtTechLine(sectionName, argc, argv)
{
char *mult, *offset;
/* 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.0;
if (paramName - argv[argc - 1] == 3)
newParam->pl_param[1] = *(argv[argc - 1] + 1);
@ -2421,6 +2430,102 @@ ExtTechLine(sectionName, argc, argv)
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.0;
newParam->pl_next = subcktParams;
subcktParams = newParam;
chkParam = newParam;
}
/* Change limit */
limitstr++;
if (*limitstr == '=')
{
equal = TRUE;
limitstr++;
}
if (sscanf(limitstr, "%lg", &dval) == 0)
{
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. */
@ -2453,7 +2558,8 @@ ExtTechLine(sectionName, argc, argv)
{
while (subcktParams != NULL)
{
freeMagic(subcktParams->pl_name);
if (subcktParams->pl_name != NULL)
freeMagic(subcktParams->pl_name);
freeMagic(subcktParams);
subcktParams = subcktParams->pl_next;
}
@ -3578,9 +3684,27 @@ zinit:
ExtDevice *devptr;
for (devptr = style->exts_device[r]; devptr; devptr = devptr->exts_next)
{
{
ParamList *chkParam;
devptr->exts_deviceSDCap *= sqfac;
devptr->exts_deviceGateCap *= sqfac;
for (chkParam = devptr->exts_deviceParams; chkParam;
chkParam = chkParam->pl_next)
{
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;
}
}
}
style->exts_areaCap[r] *= sqfac;
@ -3645,6 +3769,24 @@ zinit:
/* 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_minimum > chkParam->pl_maximum) continue;
chkParam->pl_maximum /= 1000;
chkParam->pl_minimum /= 1000;
}
}
}
}
/*
@ -3678,6 +3820,7 @@ ExtTechScale(scalen, scaled)
for (i = 0; i < DBNumTypes; i++)
{
ExtDevice *devptr;
ParamList *chkParam;
style->exts_areaCap[i] *= sqn;
style->exts_areaCap[i] /= sqd;
@ -3688,6 +3831,26 @@ ExtTechScale(scalen, scaled)
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_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;

View File

@ -79,6 +79,8 @@ typedef struct pl
char *pl_name; /* Full name for parameter */
double pl_scale; /* Scaling of parameter, if specified */
double pl_offset; /* Offset of parameter, if specified */
int pl_maximum; /* Maximum value for this device model */
int pl_minimum; /* Minimum value for this device model */
struct pl *pl_next; /* Next parameter in list */
} ParamList;