From 727649b308794c92e0aa3f8d4f3f8a1f5e8fcbe6 Mon Sep 17 00:00:00 2001 From: "R. Timothy Edwards" Date: Fri, 30 Jan 2026 16:55:08 -0500 Subject: [PATCH] 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. --- README | 103 ++++++++++++++++++++++++++++++++++++++++++++- extflat/EFbuild.c | 15 ++++++- extract/ExtBasic.c | 28 ++++++++++-- extract/ExtCell.c | 27 ++---------- extract/ExtMain.c | 83 +++++++++++++++++++++++++++++------- extract/extract.h | 2 + resis/ResReadExt.c | 77 +++++++++++++++++++-------------- resis/ResRex.c | 16 +++---- 8 files changed, 264 insertions(+), 87 deletions(-) diff --git a/README b/README index d1c5f8d1..8e1048f5 100644 --- a/README +++ b/README @@ -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 " 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. + diff --git a/extflat/EFbuild.c b/extflat/EFbuild.c index fed2f975..acf51539 100644 --- a/extflat/EFbuild.c +++ b/extflat/EFbuild.c @@ -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; diff --git a/extract/ExtBasic.c b/extract/ExtBasic.c index 3f4a8ce0..0fbc7ab2 100644 --- a/extract/ExtBasic.c +++ b/extract/ExtBasic.c @@ -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; } diff --git a/extract/ExtCell.c b/extract/ExtCell.c index 0cdc396c..d8221253 100644 --- a/extract/ExtCell.c +++ b/extract/ExtCell.c @@ -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 ".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 diff --git a/extract/ExtMain.c b/extract/ExtMain.c index c502d998..47e649f9 100644 --- a/extract/ExtMain.c +++ b/extract/ExtMain.c @@ -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) diff --git a/extract/extract.h b/extract/extract.h index 7bd25850..c38bcb79 100644 --- a/extract/extract.h +++ b/extract/extract.h @@ -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(); diff --git a/resis/ResReadExt.c b/resis/ResReadExt.c index 40b16cb1..01f4d502 100644 --- a/resis/ResReadExt.c +++ b/resis/ResReadExt.c @@ -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]); diff --git a/resis/ResRex.c b/resis/ResRex.c index befbb73b..91b7284c 100644 --- a/resis/ResRex.c +++ b/resis/ResRex.c @@ -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)))