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)))