Saving work so I can rebase on changes made to the magic extract

code which are relevant and need to be included.  Current state
is that hierarchical extresist "basically works" but entry and
exit points through the hierarchy are not being examined, so
results are currently based on port positions and not actual
connections.  Also, proper distribution of coupling caps has not
yet been worked on.

Rebased, fixing merge conflict.
This commit is contained in:
R. Timothy Edwards 2026-01-30 16:55:08 -05:00
parent 4dde62b206
commit 727649b308
8 changed files with 264 additions and 87 deletions

103
README
View File

@ -49,5 +49,104 @@ because it will have to be run independently in any routine that calls
"ExtCell". This looks like two places, discounting "ExtTech" which
shouldn't need it: ExtractOneCell() and extExtractStack().
Start by running tests of how the existing code works given a hierarchical
layout.
Done. Now it should be possible to read the .ext file of every parent
cell of a cell def to find all of the potential points of entry.
Dealing with those points of entry will be the hard part.
First, re-run the existing test (non-hierarchical) to make sure that
moving the point where extResisForDef() is called didn't mess up
anything. Looks good.
Next, run a test of how the existing code works given a hierarchical
layout. Example in ~/devel/magic/extresist_new/hierarchical/. Copying
layout from chipalooza_projects_2, top level cell sky130_am_ip__ldo_01v8.
First problem: Not finding the .ext files when reading for extresist.
But this literally *just* worked fine for the non-hierarchical cell.
What gives? --- "EFSearchPath" is NULL, this is not what's changed
by "extract path"? The value is in global variable ExtLocalPath.
Oh. . . So it *writes* the .res.ext file to the right place but it
doesn't *read* the .res.ext file from the right place. This also
means that the non-hierarchical test pulled the wrong .ext file and
should be done over, although eyeballing it, it looks okay.
There is a command "extFileOpen" that should be used.
Fixed, try again.
This more or less worked.
Needs to print out the name of the cell being processed.
Once cell, looks like the level shifter, had "missing terminal
connection" errors.
Might be source-drain connected? No, these are perfectly normal
transistors. . . Why this cell specifically? Happens even if
only the cell itself is being extracted (no hierarchy).
Error issued at ResRex.c:1512. layoutDev->rd_terminals has all
null contents.
Note that "rd_fet_gate/source/drain/subs" is equivalent to
"rd_terminals[0/1/2/3]" and either form might be used. The
rd_terminals[*] form seems to be used only for initialization.
Looks like ResNewSDDevice() was never run.
Would have come from walking the device on all four sides;
ResNewSDDevice would have been called from ResEachTile.
Break on ResEachTile() for the gate tile in question, which would
be at 1406, 563. Apparently that never happens?
5th call to ResProcessTiles().
Note that "Adding <node name>" only appears sometimes. When it
is omitted, resisdata->rg_bigdevres is zero. Example, VPWR?
rg_bigdevres copied from "minRes" in ResProcessNode().
"Find largest SD device connected to node" seems non-controversial
and should produce a result for node VPWR.
Break on ResProcessNode until node->name is "VPWR". Problem happens
immediately.
Ah---Caused by ptr->thisDev->resistance = 0.
This is "linear resistance" from FET line. In .sim files, this is
found in the devptr record and is relevant.
Added code to get the value of device width from the .ext file and
determine the linear resistance according to the tech file values.
(Except that the open PDKs don't have these values. . . something
to deal with later.) (Since this seems important, made it default
to 10k instead of 0.)
Oddly, these changes made exactly zero difference to the output.
"minRes" is being set to zero, perhaps for no good reason.
Now things are worse: "Adding" only shows up once, for VPB,
and now there's a new error about "VPWR" being an "orphaned node"
and being connected (arbitrarily) to "VPWR.t1". . .
Went back to setting minRes to zero for FORCE or DRIVELOC.
Anyway, this seems to have nothing to do with the missing terminal
connection and has just been a distraction.
Example where ResNewSDDevice() is being called correctly:
tile = pdiff at 686, 439
tile has clientdata with resDev = L=30, W=168 @656, 439.
Tile came from a contact point record, cp->cp_tile[0].
This does not say a whole lot about why a terminal connection
wasn't found.
Staring at it for a while, I get it now: These devices are on nets that
are split into two unconnected parts but labeled the same. So the
connectivity is tracing from one of the labels, and the other gets
unprocessed. Probably gets solved by using "extract do unique"?
Yes, it does!
Found an error in other code: "extract do unique" turned a font label
into a regular label, so it got left that way. No, this is something
else and is in the original cell. Actually, this is a strange error.
"select area labels" even when applied to the entire cell, does not
find (or does not display) VGND, LVPWR, VPWR, or VPB font labels.
All non-font labels and font labels VNB, A, and X are displayed.
This behavior is not new and I will punt on it. Have no idea what's
going on, as the graphics routines appear to be working and displaying
the font label as expected.
Anyway, with "extract do unique" and "extract do resistance", I can get
a full R-C extraction netlist from the LDO layout. That leaves a lot
to be analyzed. . .
Will need a simpler hierarchical layout example.

View File

@ -648,8 +648,21 @@ efBuildEquiv(def, nodeName1, nodeName2, resist, isspice)
return;
}
else if (!resist)
TxError("Warning: Ports \"%s\" and \"%s\" are electrically "
{
char *uptr1, *uptr2;
/* Do not generate an error message if one or both node names
* is made by "extract unique".
*/
if ((uptr1 = strstr(nodeName1, "_uq")) != 0) *uptr1 = '\0';
if ((uptr2 = strstr(nodeName2, "_uq")) != 0) *uptr2 = '\0';
if ((uptr1 == NULL && uptr2 == NULL) ||
strcmp(nodeName1, nodeName2))
TxError("Warning: Ports \"%s\" and \"%s\" are electrically "
"shorted.\n", nodeName1, nodeName2);
if (uptr1) *uptr1 = '_';
if (uptr2) *uptr2 = '_';
}
else
/* Do not merge the nodes when folding in extresist parasitics */
return;

View File

@ -802,14 +802,34 @@ extOutputNodes(nodeList, outFile)
lastname = ll->ll_label->lab_text;
}
/* Don't print a warning unless both labels are
* really ports.
* really ports. Also, don't print a warning for
* names generated by "extract unique" vs. real
* pin names or another unique name---"extract
* unique" does not observe where nets pass through
* subcircuits, so it tends to over-generated
* unique names, which "ext2spice" will filter out.
* For a net to be shorted to itself is not an error.
* NOTE: Potentially the unique name could be removed
* here and save ext2spice the trouble.
*/
if ((portname != NULL) &&
(ll->ll_attr == LL_PORTATTR) &&
(strcmp(ll->ll_label->lab_text, portname)))
TxError("Warning: Ports \"%s\" and \"%s\" are"
" electrically shorted.\n",
text, ll->ll_label->lab_text);
{
char *uptr1, *uptr2;
uptr1 = strstr(text, "_uq");
uptr2 = strstr(ll->ll_label->lab_text, "_uq");
if (uptr1) *uptr1 = '\0';
if (uptr2) *uptr2 = '\0';
if (strcmp(text, ll->ll_label->lab_text))
{
TxError("Warning: Ports \"%s\" and \"%s\" are"
" electrically shorted.\n",
text, ll->ll_label->lab_text);
}
if (uptr1) *uptr1 = '_';
if (uptr2) *uptr2 = '_';
}
if (!isPort && (ll->ll_attr == LL_PORTATTR))
portname = ll->ll_label->lab_text;
}

View File

@ -39,7 +39,6 @@ static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/
#include "debug/debug.h"
#include "extract/extract.h"
#include "extract/extractInt.h"
#include "resis/resis.h"
#include "utils/signals.h"
#include "utils/stack.h"
#include "utils/utils.h"
@ -51,7 +50,6 @@ static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/
/* Forward declarations */
int extOutputUsesFunc();
FILE *extFileOpen();
Plane* extCellFile();
void extHeader();
@ -97,7 +95,7 @@ ExtCell(def, outName, doLength)
if (def->cd_flags & CDNOEXTRACT)
return extPrepSubstrate(def);
f = extFileOpen(def, outName, "w", &filename);
f = ExtFileOpen(def, outName, "w", &filename);
TxPrintf("Extracting %s into %s:\n", def->cd_name, filename);
@ -116,25 +114,6 @@ ExtCell(def, outName, doLength)
savePlane = extCellFile(def, f, doLength);
if (f != NULL) fclose(f);
/* Integrated extresist --- Run "extresist" on the cell def just
* extracted and produce an annotation file "<file>.res.ext".
*/
if (ExtOptions & EXT_DOEXTRESIST)
{
ResisData *resisdata = ResInit();
UndoDisable();
ResOptionsFlags |= ResOpt_Signal;
resisdata->mainDef = def;
resisdata->savePlanes = (struct saveList *)NULL; /* unused */
ExtResisForDef(def, resisdata);
UndoEnable();
}
if (extNumErrors > 0 || extNumWarnings > 0)
{
TxPrintf("%s:", def->cd_name);
@ -152,7 +131,7 @@ ExtCell(def, outName, doLength)
/*
* ----------------------------------------------------------------------------
*
* extFileOpen --
* ExtFileOpen --
*
* Open the .ext file corresponding to a .mag file.
* If def->cd_file is non-NULL, the .ext file is just def->cd_file with
@ -170,7 +149,7 @@ ExtCell(def, outName, doLength)
*/
FILE *
extFileOpen(def, file, mode, prealfile)
ExtFileOpen(def, file, mode, prealfile)
CellDef *def; /* Cell whose .ext file is to be written */
char *file; /* If non-NULL, open 'name'.ext; otherwise,
* derive filename from 'def' as described

View File

@ -35,6 +35,7 @@ static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/
#include "debug/debug.h"
#include "extract/extract.h"
#include "extract/extractInt.h"
#include "resis/resis.h"
#include "utils/signals.h"
#include "utils/stack.h"
#include "utils/utils.h"
@ -43,9 +44,6 @@ static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/
#include "utils/main.h"
#include "utils/undo.h"
/* Imports from elsewhere in this module */
extern FILE *extFileOpen();
/* ------------------------ Exported variables ------------------------ */
/*
@ -83,16 +81,6 @@ typedef struct _linkedDef {
struct _linkedDef *ld_next;
} LinkedDef;
/* Linked list structure to use to store the substrate plane from each */
/* extracted CellDef so that they can be returned to the original after */
/* extraction. */
struct saveList {
Plane *sl_plane;
CellDef *sl_def;
struct saveList *sl_next;
};
/* Stack of defs pending extraction */
Stack *extDefStack;
@ -606,7 +594,7 @@ extParents(use, doExtract)
extDefParentFunc(use->cu_def);
/* Now extract all the cells we just found */
extExtractStack(extDefStack, doExtract, (CellDef *) NULL);
extExtractStack(extDefStack, doExtract, (CellDef *)NULL);
StackFree(extDefStack);
/* Replace any modified substrate planes in use->cu_def's children */
@ -688,7 +676,7 @@ ExtParentArea(use, changedArea, doExtract)
extDefParentAreaFunc(use->cu_def, use->cu_def, (CellUse *) NULL, &area);
/* Now extract all the cells we just found */
extExtractStack(extDefStack, doExtract, (CellDef *) NULL);
extExtractStack(extDefStack, doExtract, (CellDef *)NULL);
StackFree(extDefStack);
}
@ -806,6 +794,23 @@ ExtractOneCell(def, outName, doLength)
savePlane = ExtCell(def, outName, doLength);
/* Run full R-C extraction if specified in options */
if (ExtOptions & EXT_DOEXTRESIST)
{
ResisData *resisdata = ResInit();
UndoDisable();
ResOptionsFlags |= ResOpt_Signal;
resisdata->mainDef = def;
resisdata->savePlanes = (struct saveList *)NULL; /* unused */
ExtResisForDef(def, resisdata);
UndoEnable();
}
/* Restore all modified substrate planes and modified labels */
if (savePlane != NULL) ExtRevertSubstrate(def, savePlane);
@ -945,7 +950,7 @@ extTimestampMisMatch(def)
doLocal = (ExtLocalPath == NULL) ? FALSE : TRUE;
extFile = extFileOpen(def, (char *) NULL, "r", (char **) NULL);
extFile = ExtFileOpen(def, (char *) NULL, "r", (char **) NULL);
if (extFile == NULL)
return (TRUE);
@ -991,10 +996,18 @@ extExtractStack(stack, doExtract, rootDef)
bool first = TRUE;
Plane *savePlane;
CellDef *def;
LinkedDef *savelist = NULL, *revlist = NULL, *newld;
struct saveList *newsl, *sl = (struct saveList *)NULL;
while ((def = (CellDef *) StackPop(stack)))
{
if (ExtOptions & EXT_DOEXTRESIST)
{
newld = (LinkedDef *)mallocMagic(sizeof(LinkedDef));
newld->ld_def = def;
newld->ld_next = savelist;
savelist = newld;
}
def->cd_client = (ClientData) 0;
if (!SigInterruptPending)
{
@ -1027,6 +1040,44 @@ extExtractStack(stack, doExtract, rootDef)
}
}
/* Now that all cells have been processed, run full R-C extraction */
if (ExtOptions & EXT_DOEXTRESIST)
{
ResisData *resisdata = ResInit();
LinkedDef *srchld, *nextld;
UndoDisable();
/* Reverse the linked list from top-down to bottom-up */
srchld = savelist;
while (srchld != NULL)
{
nextld = srchld->ld_next;
srchld->ld_next = revlist;
revlist = srchld;
srchld = nextld;
}
/* Reprocess the list and call "extresist" for each cell def */
srchld = revlist;
while (srchld != NULL)
{
nextld = srchld->ld_next;
def = srchld->ld_def;
ResOptionsFlags |= ResOpt_Signal;
resisdata->mainDef = def;
resisdata->savePlanes = (struct saveList *)NULL; /* unused */
TxPrintf("Processing cell %s for resistance extraction.\n", def->cd_name);
ExtResisForDef(def, resisdata);
freeMagic(srchld);
srchld = nextld;
}
UndoEnable();
}
/* Replace any modified substrate planes and modified labels */
free_magic1_t mm1 = freeMagic1_init();
for (; sl; sl = sl->sl_next)

View File

@ -113,6 +113,8 @@ extern void ExtDumpCaps();
extern int extEnumTilePerim(Tile *tpIn, TileType dinfo, const TileTypeBitMask *maskp, int pNum, int (*func)(), ClientData cdata);
extern Plane *extPrepSubstrate();
extern FILE *ExtFileOpen(CellDef *def, char *file, char *mode, char **prealfile);
/* C99 compat */
extern void ExtAll();

View File

@ -58,6 +58,7 @@ static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/
#define DEV_NAME 2
#define DEV_X 3
#define DEV_Y 4
#define DEV_PARAM_START 7
#define NODES_NODENAME 1
#define NODES_NODEX 4
@ -102,7 +103,7 @@ ResFixPoint *ResFixList;
*/
int
ResReadExt(char *extfile)
ResReadExt(CellDef *def)
{
char *line = NULL, *argv[128];
int result, locresult;
@ -111,31 +112,12 @@ ResReadExt(char *extfile)
CellDef *dbdef;
ResExtNode *curnode;
/* Search for the .ext fie in the same way that efReadDef() does. */
fp = PaOpen(extfile, "r", ".ext", EFSearchPath, EFLibPath, (char **)NULL);
if ((fp == NULL) && ((dbdef = DBCellLookDef(extfile)) != NULL)
&& (dbdef->cd_file != NULL))
{
char *filepath, *sptr;
filepath = StrDup((char **)NULL, dbdef->cd_file);
sptr = strrchr(filepath, '/');
if (sptr)
{
*sptr = '\0';
fp = PaOpen(extfile, "r", ".ext", filepath, EFLibPath, (char **)NULL);
}
freeMagic(filepath);
}
/* Try with the standard search path */
if ((fp == NULL) && (EFSearchPath == NULL))
fp = PaOpen(extfile, "r", ".ext", Path, EFLibPath, (char **)NULL);
/* Search for the .ext file in the same way that efReadDef() does. */
fp = ExtFileOpen(def, (char *)NULL, "r", (char **)NULL);
if (fp == NULL)
{
TxError("Cannot open file %s%s\n", extfile, ".ext");
TxError("Cannot open file %s%s\n", def->cd_name, ".ext");
return 1;
}
@ -335,11 +317,11 @@ ResReadDevice(int argc,
TileType ttype;
HashEntry *entry;
ResExtNode *node;
ResValue rpersquare;
float wval;
device = (RDev *)mallocMagic((unsigned)(sizeof(RDev)));
device->resistance = 0; /* Linear resistance from FET line, unused */
device->status = FALSE;
device->nextDev = ResRDevList;
@ -365,10 +347,25 @@ ResReadDevice(int argc,
device->drain = (ResExtNode *)NULL;
device->subs = (ResExtNode *)NULL;
/* Pass over parameters and find the next argument */
entry = HashLookOnly(&devptr->exts_deviceResist, "linear");
if (entry != NULL)
rpersquare = (ResValue)(spointertype)HashGetValue(entry);
else
rpersquare = (ResValue)10000.0; /* Default to a sane value */
/* For devices, the device width is in the parameter list */
wval = 0.0;
for (i = DEV_Y; i < argc; i++)
if (!StrIsInt(argv[i]) && !(strchr(argv[i], '=')))
{
char *eptr;
if ((eptr = strchr(argv[i], '=')) != NULL)
{
if (*argv[i] == 'w')
sscanf(eptr + 1, "%f", &wval);
}
else if (!StrIsInt(argv[i]))
break;
}
if (i == argc)
{
@ -376,6 +373,8 @@ ResReadDevice(int argc,
argv[DEV_NAME]);
return 1;
}
else
device->resistance = wval * rpersquare; /* Channel resistance */
/* Find and record the device terminal nodes */
/* Note that this only records up to two terminals matching FET
@ -448,11 +447,11 @@ ResReadFET(int argc,
TileType ttype;
HashEntry *entry;
ResExtNode *node;
ResValue rpersquare;
float area, perim, wval, lval;
device = (RDev *)mallocMagic((unsigned)(sizeof(RDev)));
device->resistance = 0; /* Linear resistance from FET line, unused */
device->status = FALSE;
device->nextDev = ResRDevList;
@ -469,11 +468,25 @@ ResReadFET(int argc,
device->location.p_x = atoi(argv[FET_X]);
device->location.p_y = atoi(argv[FET_Y]);
device->rs_gattr=RDEV_NOATTR;
device->rs_sattr=RDEV_NOATTR;
device->rs_dattr=RDEV_NOATTR;
device->rs_gattr = RDEV_NOATTR;
device->rs_sattr = RDEV_NOATTR;
device->rs_dattr = RDEV_NOATTR;
device->rs_devptr = devptr;
entry = HashLookOnly(&devptr->exts_deviceResist, "linear");
if (entry != NULL)
rpersquare = (ResValue)(spointertype)HashGetValue(entry);
else
rpersquare = (ResValue)10000.0; /* Default to a sane value */
/* For old-style FETs, the width is determined from area and perimeter */
area = MagAtof(argv[FET_AREA]);
perim = MagAtof(argv[FET_PERIM]);
lval = 0.5 * (perim + sqrt(perim * perim - 4 * area));
wval = area / lval;
device->resistance = wval * rpersquare; /* Channel resistance */
/* Find and record the FET terminal nodes */
entry = HashFind(&ResNodeTable, argv[FET_GATE]);

View File

@ -114,7 +114,7 @@ ExtResisForDef(celldef, resisdata)
HashInit(&ResNodeTable, INITFLATSIZE, HT_STRINGKEYS);
/* Read in the .ext file */
result = (ResReadExt(celldef->cd_name) == 0);
result = (ResReadExt(celldef) == 0);
/* Clean up the EFDevTypes table */
for (idx = 0; idx < EFDevNumTypes; idx++) freeMagic(EFDevTypes[idx]);
@ -1043,20 +1043,17 @@ ResProcessNode(
}
}
/* special handling for FORCE and DRIVELOC labels: */
/* set minRes = node->minsizeres if it exists, 0 otherwise */
/* Special handling for FORCE and DRIVELOC labels: */
/* Set minRes = node->minsizeres if it exists, 0 otherwise. */
if (node->status & (FORCE|DRIVELOC))
{
if (node->status & MINSIZE)
{
minRes = node->minsizeres;
}
else
{
minRes = 0;
}
if (node->status & DRIVELOC)
if (node->status & DRIVELOC)
{
resisdata->rg_devloc = &node->drivepoint;
resisdata->rg_status |= DRIVEONLY;
@ -1825,6 +1822,9 @@ ResWriteExtFile(celldef, node, resisdata, nidx, eidx)
node->name, resisdata->rg_Tdi / Z_TO_P, RCdev / Z_TO_P);
}
}
else
TxPrintf("Adding %s\n", node->name);
for (ptr = node->firstDev; ptr != NULL; ptr=ptr->nextDev)
{
if ((layoutDev = ResGetDevice(&ptr->thisDev->location, ptr->thisDev->rs_ttype)))