From c7f11d2169f6af8751ae22b5d70250b331e1a667 Mon Sep 17 00:00:00 2001 From: Tim Edwards Date: Thu, 16 Feb 2023 11:59:13 -0500 Subject: [PATCH] Important update: Reworked the extraction method to properly isolate the terminal areas of a device (e.g., source and drain) and calculate their area and perimeter individually for the device (in addition to the traditional method of calculating area and perimeter of each resistance class for the entire node). Also: Reworked the SPICE syntax output to generate SI values in the range 1-1000 with the appropriate suffix (e.g., "20u") instead of defaulting to "u" for lengths and "p" for areas. This prevents it from producing weird units like "150000u" when a process definition already includes a scalefactor. Reworked the "extresist" code to use the device terminal area and perimeter. This fixes an error in which "extresist" would lose these values and "ext2spice" with option "extresist on" would generate a new netlist output with zero terminal areas and perimeters. --- VERSION | 2 +- database/DBconnect.c | 96 ++++++++++- database/database.h.in | 2 + ext2sim/ext2sim.c | 6 + ext2spice/ext2hier.c | 83 ++++++---- ext2spice/ext2spice.c | 362 +++++++++++++++++++++++++++++------------ ext2spice/ext2spice.h | 1 + extflat/EFbuild.c | 2 - extflat/extflat.h | 2 - extract/ExtBasic.c | 288 +++++++++++++++++++++++++++----- resis/ResBasic.c | 44 ----- resis/ResReadSim.c | 54 ++++-- 12 files changed, 698 insertions(+), 244 deletions(-) diff --git a/VERSION b/VERSION index 8e5ca847..21d6308a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.3.366 +8.3.367 diff --git a/database/DBconnect.c b/database/DBconnect.c index c57ab7b7..aa085d53 100644 --- a/database/DBconnect.c +++ b/database/DBconnect.c @@ -129,6 +129,85 @@ DBInvTransformDiagonal(oldtype, trans) } +/* + * ---------------------------------------------------------------------------- + * + * DBSrConnectOnePlane -- + * + * Search from a starting tile to find all paint that is electrically + * connected to that tile in the same plane. + * + * Results: + * 0 is returned if the search finished normally. 1 is returned + * if the search was aborted. + * + * Side effects: + * For every paint tile that is electrically connected to the initial + * tile, func is called. Func should have the following form: + * + * int + * func(tile, clientData) + * Tile *tile; + * ClientData clientData; + * { + * } + * + * The clientData passed to func is the same one that was passed + * to us. Func returns 0 under normal conditions; if it returns + * 1 then the search is aborted. + * + * *** WARNING *** + * + * Func should not modify any paint during the search, since this + * will mess up pointers kept by these procedures and likely cause + * a core-dump. + * + * ---------------------------------------------------------------------------- + */ + +int +DBSrConnectOnePlane(startTile, connect, func, clientData) + Tile *startTile; /* Starting tile for search */ + TileTypeBitMask *connect; /* Pointer to a table indicating what tile + * types connect to what other tile types. + * Each entry gives a mask of types that + * connect to tiles of a given type. + */ + int (*func)(); /* Function to apply at each connected tile. */ + ClientData clientData; /* Client data for above function. */ + +{ + struct conSrArg csa; + int result; + extern int dbSrConnectFunc(); /* Forward declaration. */ + + result = 0; + csa.csa_def = (CellDef *)NULL; + csa.csa_bounds = TiPlaneRect; + + /* Pass 1. During this pass the client function gets called. */ + + csa.csa_clientFunc = func; + csa.csa_clientData = clientData; + csa.csa_clientDefault = startTile->ti_client; + csa.csa_clear = FALSE; + csa.csa_connect = connect; + csa.csa_pNum = -1; + if (dbSrConnectFunc(startTile, &csa) != 0) result = 1; + + /* Pass 2. Don't call any client function, just clear the marks. + * Don't allow any interruptions. + */ + + SigDisableInterrupts(); + csa.csa_clientFunc = NULL; + csa.csa_clear = TRUE; + (void) dbSrConnectFunc(startTile, &csa); + SigEnableInterrupts(); + + return result; +} + /* * ---------------------------------------------------------------------------- * @@ -227,6 +306,7 @@ DBSrConnect(def, startArea, mask, connect, bounds, func, clientData) csa.csa_clientFunc = func; csa.csa_clientData = clientData; + csa.csa_clientDefault = CLIENTDEFAULT; csa.csa_clear = FALSE; csa.csa_connect = connect; if (dbSrConnectFunc(startTile, &csa) != 0) result = 1; @@ -316,6 +396,7 @@ DBSrConnectOnePass(def, startArea, mask, connect, bounds, func, clientData) csa.csa_clientFunc = func; csa.csa_clientData = clientData; + csa.csa_clientDefault = CLIENTDEFAULT; csa.csa_clear = FALSE; csa.csa_connect = connect; if (dbSrConnectFunc(startTile, &csa) != 0) result = 1; @@ -424,15 +505,15 @@ dbSrConnectFunc(tile, csa) callClient = TRUE; if (csa->csa_clear) { - if (tile->ti_client == (ClientData) CLIENTDEFAULT) continue; - tile->ti_client = (ClientData) CLIENTDEFAULT; + if (tile->ti_client == csa->csa_clientDefault) continue; + tile->ti_client = csa->csa_clientDefault; } else { if (tile->ti_client == (ClientData) 1) continue; /* Allow a process to mark tiles for skipping the client function */ - if (tile->ti_client != (ClientData) CLIENTDEFAULT) + if (tile->ti_client != csa->csa_clientDefault) callClient = FALSE; tile->ti_client = (ClientData) 1; } @@ -480,7 +561,7 @@ dbSrConnectFunc(tile, csa) { if (csa->csa_clear) { - if (t2->ti_client == (ClientData) CLIENTDEFAULT) continue; + if (t2->ti_client == csa->csa_clientDefault) continue; } else if (t2->ti_client == (ClientData) 1) continue; if (IsSplit(t2)) @@ -508,7 +589,7 @@ bottomside: { if (csa->csa_clear) { - if (t2->ti_client == (ClientData) CLIENTDEFAULT) continue; + if (t2->ti_client == csa->csa_clientDefault) continue; } else if (t2->ti_client == (ClientData) 1) continue; if (IsSplit(t2)) @@ -542,7 +623,7 @@ rightside: { if (csa->csa_clear) { - if (t2->ti_client == (ClientData) CLIENTDEFAULT) goto nextRight; + if (t2->ti_client == csa->csa_clientDefault) goto nextRight; } else if (t2->ti_client == (ClientData) 1) goto nextRight; if (IsSplit(t2)) @@ -570,7 +651,7 @@ topside: { if (csa->csa_clear) { - if (t2->ti_client == (ClientData) CLIENTDEFAULT) goto nextTop; + if (t2->ti_client == csa->csa_clientDefault) goto nextTop; } else if (t2->ti_client == (ClientData) 1) goto nextTop; if (IsSplit(t2)) @@ -589,6 +670,7 @@ topside: } donesides: + if (pNum < 0) continue; /* Used for single-plane search */ /* Lastly, check to see if this tile connects to anything on * other planes. If so, search those planes. diff --git a/database/database.h.in b/database/database.h.in index ef8ff802..808ea983 100644 --- a/database/database.h.in +++ b/database/database.h.in @@ -691,6 +691,7 @@ struct conSrArg */ int (*csa_clientFunc)(); /* Client function to call. */ ClientData csa_clientData; /* Argument for clientFunc. */ + ClientData csa_clientDefault; /* Value to reset tiles' ClientData to. */ bool csa_clear; /* FALSE means pass 1, TRUE * means pass 2. */ @@ -1001,6 +1002,7 @@ extern void DBResetTilePlane(); extern void DBNewYank(); extern int DBSrPaintClient(); extern int DBSrConnect(); +extern int DBSrConnectOnePlane(); extern char *dbFgets(); extern void DBAdjustLabelsNew(); extern bool DBScaleValue(); diff --git a/ext2sim/ext2sim.c b/ext2sim/ext2sim.c index 8f00ea58..40c5ad35 100644 --- a/ext2sim/ext2sim.c +++ b/ext2sim/ext2sim.c @@ -1225,6 +1225,12 @@ simdevVisit(dev, hc, scale, trans) } else if (is_subckt) { + /* Output source and drain attributes */ + if (source->dterm_attrs) + fprintf(esSimF, " s=%s", source->dterm_attrs); + if (drain->dterm_attrs) + fprintf(esSimF, " d=%s", drain->dterm_attrs); + /* Output length, width, and position as attributes */ fprintf(esSimF, " l=%g w=%g x=%g y=%g", l * scale, w * scale, r.r_xbot * scale, r.r_ybot * scale); diff --git a/ext2spice/ext2hier.c b/ext2spice/ext2hier.c index e52a68a5..8a47fbb7 100644 --- a/ext2spice/ext2hier.c +++ b/ext2spice/ext2hier.c @@ -173,7 +173,7 @@ spcHierWriteParams(hc, dev, scale, l, w, sdM) * esScale * esScale * plist->parm_scale * 1E-12); else - fprintf(esSpiceF, "%gp", parmval * scale * scale + esSIvalue(esSpiceF, 1.0E-12 * parmval * scale * scale * esScale * esScale); } else @@ -194,15 +194,15 @@ spcHierWriteParams(hc, dev, scale, l, w, sdM) 'p' && plist->parm_next->parm_type[1] == plist->parm_type[1]) { - spcnAP(dnode, resclass, scale, plist->parm_name, - plist->parm_next->parm_name, sdM, - esSpiceF, w); + spcnAP(&dev->dev_terms[pn], dnode, resclass, scale, + plist->parm_name, plist->parm_next->parm_name, + sdM, esSpiceF, w); plist = plist->parm_next; } else { - spcnAP(dnode, resclass, scale, plist->parm_name, NULL, sdM, - esSpiceF, w); + spcnAP(&dev->dev_terms[pn], dnode, resclass, scale, + plist->parm_name, NULL, sdM, esSpiceF, w); } } @@ -220,7 +220,7 @@ spcHierWriteParams(hc, dev, scale, l, w, sdM) fprintf(esSpiceF, "%g", parmval * scale * esScale * plist->parm_scale * 1E-6); else - fprintf(esSpiceF, "%gu", parmval * scale * esScale); + esSIvalue(esSpiceF, 1.0E-6 * parmval * scale * esScale); } else { @@ -240,14 +240,15 @@ spcHierWriteParams(hc, dev, scale, l, w, sdM) 'a' && plist->parm_next->parm_type[1] == plist->parm_type[1]) { - spcnAP(dnode, resclass, scale, plist->parm_next->parm_name, + spcnAP(&dev->dev_terms[pn], dnode, resclass, scale, + plist->parm_next->parm_name, plist->parm_name, sdM, esSpiceF, w); plist = plist->parm_next; } else { - spcnAP(dnode, resclass, scale, NULL, plist->parm_name, sdM, - esSpiceF, w); + spcnAP(&dev->dev_terms[pn], dnode, resclass, scale, NULL, + plist->parm_name, sdM, esSpiceF, w); } } @@ -263,7 +264,7 @@ spcHierWriteParams(hc, dev, scale, l, w, sdM) fprintf(esSpiceF, "%g", l * scale * esScale * plist->parm_scale * 1E-6); else - fprintf(esSpiceF, "%gu", l * scale * esScale); + esSIvalue(esSpiceF, 1.0E-6 * l * scale * esScale); } else { @@ -286,7 +287,7 @@ spcHierWriteParams(hc, dev, scale, l, w, sdM) fprintf(esSpiceF, "%g", dval * scale * esScale * plist->parm_scale * 1E-6); else - fprintf(esSpiceF, "%gu", dval * scale * esScale); + esSIvalue(esSpiceF, 1.0E-6 * dval * scale * esScale); dparam->parm_name[0] = '\0'; break; } @@ -303,7 +304,7 @@ spcHierWriteParams(hc, dev, scale, l, w, sdM) fprintf(esSpiceF, "%g", w * scale * esScale * plist->parm_scale * 1E-6); else - fprintf(esSpiceF, "%gu", w * scale * esScale); + esSIvalue(esSpiceF, 1.0E-6 * w * scale * esScale); break; case 's': fprintf(esSpiceF, " %s=", plist->parm_name); @@ -319,8 +320,7 @@ spcHierWriteParams(hc, dev, scale, l, w, sdM) fprintf(esSpiceF, "%g", dev->dev_rect.r_xbot * scale * esScale * plist->parm_scale * 1E-6); else - fprintf(esSpiceF, "%gu", dev->dev_rect.r_xbot * scale - * esScale); + esSIvalue(esSpiceF, 1.0E-6 * dev->dev_rect.r_xbot * scale * esScale); break; case 'y': fprintf(esSpiceF, " %s=", plist->parm_name); @@ -330,8 +330,7 @@ spcHierWriteParams(hc, dev, scale, l, w, sdM) fprintf(esSpiceF, "%g", dev->dev_rect.r_ybot * scale * esScale * plist->parm_scale * 1E-6); else - fprintf(esSpiceF, "%gu", dev->dev_rect.r_ybot * scale - * esScale); + esSIvalue(esSpiceF, 1.0E-6 * dev->dev_rect.r_ybot * scale * esScale); break; case 'r': fprintf(esSpiceF, " %s=", plist->parm_name); @@ -426,9 +425,10 @@ esOutputHierResistor(hc, dev, scale, term1, term2, has_model, l, w, dscale) } else { - fprintf(esSpiceF, " w=%gu l=%gu", - (float)w * scale * esScale, - (float)((l * scale * esScale) / dscale)); + fprintf(esSpiceF, " w="); + esSIvalue(esSpiceF, 1.0E-6 * (float)w * scale * esScale); + fprintf(esSpiceF, " w="); + esSIvalue(esSpiceF, 1.0E-6 * (float)((l * scale * esScale) / dscale)); } spcHierWriteParams(hc, dev, scale, l, w, sdM); if (sdM != 1.0) @@ -934,9 +934,10 @@ spcdevHierVisit(hc, dev, scale) } else { - fprintf(esSpiceF, " w=%gu l=%gu", - w * scale * esScale, - l * scale * esScale); + fprintf(esSpiceF, " w="); + esSIvalue(esSpiceF, 1.0E-6 * w * scale * esScale); + fprintf(esSpiceF, " l="); + esSIvalue(esSpiceF, 1.0E-6 * l * scale * esScale); } spcHierWriteParams(hc, dev, scale, l, w, sdM); if (sdM != 1.0) @@ -981,9 +982,10 @@ spcdevHierVisit(hc, dev, scale) } else { - fprintf(esSpiceF, " w=%gu l=%gu", - w * scale * esScale, - l * scale * esScale); + fprintf(esSpiceF, " w="); + esSIvalue(esSpiceF, 1.0E-6 * w * scale * esScale); + fprintf(esSpiceF, " l="); + esSIvalue(esSpiceF, 1.0E-6 * l * scale * esScale); } spcHierWriteParams(hc, dev, scale, l, w, sdM); if (sdM != 1.0) @@ -1024,9 +1026,10 @@ spcdevHierVisit(hc, dev, scale) } else { - fprintf(esSpiceF, " w=%gu l=%gu", - w * scale * esScale, - l * scale * esScale); + fprintf(esSpiceF, " w="); + esSIvalue(esSpiceF, 1.0E-6 * w * scale * esScale); + fprintf(esSpiceF, " l="); + esSIvalue(esSpiceF, 1.0E-6 * l * scale * esScale); } spcHierWriteParams(hc, dev, scale, l, w, sdM); if (sdM != 1.0) @@ -1042,10 +1045,10 @@ spcdevHierVisit(hc, dev, scale) fprintf(esSpiceF, "\n+ "); dnode = GetHierNode(hc, drain->dterm_node->efnode_name->efnn_hier); - spcnAP(dnode, esFetInfo[dev->dev_type].resClassDrain, scale, + spcnAP(drain, dnode, esFetInfo[dev->dev_type].resClassDrain, scale, "ad", "pd", sdM, esSpiceF, w); snode= GetHierNode(hc, source->dterm_node->efnode_name->efnn_hier); - spcnAP(snode, esFetInfo[dev->dev_type].resClassSource, scale, + spcnAP(source, snode, esFetInfo[dev->dev_type].resClassSource, scale, "as", "ps", sdM, esSpiceF, w); if (subAP) { @@ -1057,8 +1060,8 @@ spcdevHierVisit(hc, dev, scale) fprintf(esSpiceF, "asub=0 psub=0"); } else if (subnodeFlat) - spcnAP(subnodeFlat, esFetInfo[dev->dev_type].resClassSub, scale, - "asub", "psub", sdM, esSpiceF, -1); + spcnAP(NULL, subnodeFlat, esFetInfo[dev->dev_type].resClassSub, + scale, "asub", "psub", sdM, esSpiceF, -1); else fprintf(esSpiceF, "asub=0 psub=0"); } @@ -1073,13 +1076,21 @@ spcdevHierVisit(hc, dev, scale) case DEV_MSUBCKT: if (!esNoAttrs) { - if (gate->dterm_attrs || source->dterm_attrs || drain->dterm_attrs) + bool haveSattr = FALSE; + bool haveDattr = FALSE; + + if (source->dterm_attrs && (*source->dterm_attrs)) + haveSattr = TRUE; + if (drain->dterm_attrs && (*drain->dterm_attrs)) + haveDattr = TRUE; + + if (gate->dterm_attrs || haveSattr || haveDattr) fprintf(esSpiceF,"\n**devattr"); if (gate->dterm_attrs) fprintf(esSpiceF, " g=%s", gate->dterm_attrs); - if (source->dterm_attrs) + if (haveSattr) fprintf(esSpiceF, " s=%s", source->dterm_attrs); - if (drain->dterm_attrs) + if (haveDattr) fprintf(esSpiceF, " d=%s", drain->dterm_attrs); } break; diff --git a/ext2spice/ext2spice.c b/ext2spice/ext2spice.c index 6d2e6033..735ffde4 100644 --- a/ext2spice/ext2spice.c +++ b/ext2spice/ext2spice.c @@ -20,6 +20,7 @@ static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/ #include /* for atof() */ #include #include +#include /* for fabs() */ #include "tcltk/tclmagic.h" #include "utils/magic.h" @@ -1002,7 +1003,11 @@ runexttospice: fprintf(esSpiceF, "* %s file created from %s.ext - technology: %s\n\n", spiceFormats[esFormat], inName, EFTech); if (esScale < 0) - fprintf(esSpiceF, ".option scale=%gu\n\n", EFScale / 100.0); + { + fprintf(esSpiceF, ".option scale="); + esSIvalue(esSpiceF, 1.0E-6 * EFScale / 100.0); + fprintf(esSpiceF, "\n\n"); + } else esScale = EFScale / 100.0; @@ -1237,7 +1242,11 @@ main(argc, argv) fprintf(esSpiceF, "* %s file created from %s.ext - technology: %s\n\n", spiceFormats[esFormat], inName, EFTech); if (esScale < 0) - fprintf(esSpiceF,".option scale=%gu\n\n", EFScale / 100.0); + { + fprintf(esSpiceF,".option scale="); + esSIvalue(esSpiceF, 1.0E-6 * EFScale / 100.0); + fprintf(esSpiceF, "\n\n"); + } else esScale = EFScale / 100.0; @@ -2005,7 +2014,7 @@ spcWriteParams(dev, hierName, scale, l, w, sdM) * esScale * esScale * plist->parm_scale * 1E-12); else - fprintf(esSpiceF, "%gp", parmval * scale * scale + esSIvalue(esSpiceF, 1.0E-12 * parmval * scale * scale * esScale * esScale); } else @@ -2036,8 +2045,8 @@ spcWriteParams(dev, hierName, scale, l, w, sdM) { dnode = SpiceGetNode(hierName, dev->dev_terms[pn].dterm_node->efnode_name->efnn_hier); - spcnAP(dnode, resclass, scale, plist->parm_name, - plist->parm_next->parm_name, + spcnAP(&dev->dev_terms[pn], dnode, resclass, scale, + plist->parm_name, plist->parm_next->parm_name, sdM, esSpiceF, w); } plist = plist->parm_next; @@ -2052,8 +2061,8 @@ spcWriteParams(dev, hierName, scale, l, w, sdM) { dnode = SpiceGetNode(hierName, dev->dev_terms[pn].dterm_node->efnode_name->efnn_hier); - spcnAP(dnode, resclass, scale, plist->parm_name, NULL, - sdM, esSpiceF, w); + spcnAP(&dev->dev_terms[pn], dnode, resclass, scale, + plist->parm_name, NULL, sdM, esSpiceF, w); } } } @@ -2071,7 +2080,7 @@ spcWriteParams(dev, hierName, scale, l, w, sdM) fprintf(esSpiceF, "%g", parmval * scale * esScale * plist->parm_scale * 1E-6); else - fprintf(esSpiceF, "%gu", parmval * scale * esScale); + esSIvalue(esSpiceF, 1.0E-12 * parmval * scale * esScale); } else { @@ -2100,7 +2109,8 @@ spcWriteParams(dev, hierName, scale, l, w, sdM) { dnode = SpiceGetNode(hierName, dev->dev_terms[pn].dterm_node->efnode_name->efnn_hier); - spcnAP(dnode, resclass, scale, plist->parm_next->parm_name, + spcnAP(&dev->dev_terms[pn], dnode, resclass, scale, + plist->parm_next->parm_name, plist->parm_name, sdM, esSpiceF, w); } plist = plist->parm_next; @@ -2115,8 +2125,8 @@ spcWriteParams(dev, hierName, scale, l, w, sdM) { dnode = SpiceGetNode(hierName, dev->dev_terms[pn].dterm_node->efnode_name->efnn_hier); - spcnAP(dnode, resclass, scale, NULL, plist->parm_name, - sdM, esSpiceF, w); + spcnAP(&dev->dev_terms[pn], dnode, resclass, scale, + NULL, plist->parm_name, sdM, esSpiceF, w); } } } @@ -2133,7 +2143,7 @@ spcWriteParams(dev, hierName, scale, l, w, sdM) fprintf(esSpiceF, "%g", l * scale * esScale * plist->parm_scale * 1E-6); else - fprintf(esSpiceF, "%gu", l * scale * esScale); + esSIvalue(esSpiceF, 1.0E-6 * l * scale * esScale); } else { @@ -2156,7 +2166,7 @@ spcWriteParams(dev, hierName, scale, l, w, sdM) fprintf(esSpiceF, "%g", dval * scale * esScale * plist->parm_scale * 1E-6); else - fprintf(esSpiceF, "%gu", dval * scale * esScale); + esSIvalue(esSpiceF, 1.0E-6 * dval * scale * esScale); dparam->parm_name[0] = '\0'; break; } @@ -2173,7 +2183,7 @@ spcWriteParams(dev, hierName, scale, l, w, sdM) fprintf(esSpiceF, "%g", w * scale * esScale * plist->parm_scale * 1E-6); else - fprintf(esSpiceF, "%gu", w * scale * esScale); + esSIvalue(esSpiceF, 1.0E-6 * w * scale * esScale); break; case 's': fprintf(esSpiceF, " %s=", plist->parm_name); @@ -2189,8 +2199,7 @@ spcWriteParams(dev, hierName, scale, l, w, sdM) fprintf(esSpiceF, "%g", dev->dev_rect.r_xbot * scale * esScale * plist->parm_scale * 1E-6); else - fprintf(esSpiceF, "%gu", dev->dev_rect.r_xbot * scale - * esScale); + esSIvalue(esSpiceF, 1.0E-6 * dev->dev_rect.r_xbot * scale * esScale); break; case 'y': fprintf(esSpiceF, " %s=", plist->parm_name); @@ -2200,8 +2209,7 @@ spcWriteParams(dev, hierName, scale, l, w, sdM) fprintf(esSpiceF, "%g", dev->dev_rect.r_ybot * scale * esScale * plist->parm_scale * 1E-6); else - fprintf(esSpiceF, "%gu", dev->dev_rect.r_ybot * scale - * esScale); + esSIvalue(esSpiceF, 1.0E-6 * dev->dev_rect.r_ybot * scale * esScale); break; case 'r': fprintf(esSpiceF, " %s=", plist->parm_name); @@ -2283,9 +2291,12 @@ esOutputResistor(dev, hierName, scale, term1, term2, has_model, l, w, dscale) if (esScale < 0) fprintf(esSpiceF, " w=%g l=%g", w * scale, (l * scale) / dscale); else - fprintf(esSpiceF, " w=%gu l=%gu", - w * scale * esScale, - ((l * scale * esScale) / dscale)); + { + fprintf(esSpiceF, " w="); + esSIvalue(esSpiceF, 1.0E-6 * w * scale * esScale); + fprintf(esSpiceF, " l="); + esSIvalue(esSpiceF, 1.0E-6 * (l * scale * esScale) / dscale); + } spcWriteParams(dev, hierName, scale, l, w, sdM); if (sdM != 1.0) @@ -2838,9 +2849,12 @@ spcdevVisit(dev, hc, scale, trans) if (esScale < 0) fprintf(esSpiceF, " w=%g l=%g", w*scale, l*scale); else - fprintf(esSpiceF, " w=%gu l=%gu", - w * scale * esScale, - l * scale * esScale); + { + fprintf(esSpiceF, " w="); + esSIvalue(esSpiceF, 1.0E-6 * w * scale * esScale); + fprintf(esSpiceF, " l="); + esSIvalue(esSpiceF, 1.0E-6 * l * scale * esScale); + } spcWriteParams(dev, hierName, scale, l, w, sdM); if (sdM != 1.0) @@ -2879,9 +2893,12 @@ spcdevVisit(dev, hc, scale, trans) if (esScale < 0) fprintf(esSpiceF, " w=%g l=%g", w*scale, l*scale); else - fprintf(esSpiceF, " w=%gu l=%gu", - w * scale * esScale, - l * scale * esScale); + { + fprintf(esSpiceF, " w="); + esSIvalue(esSpiceF, 1.0E-6 * w * scale * esScale); + fprintf(esSpiceF, " l="); + esSIvalue(esSpiceF, 1.0E-6 * l * scale * esScale); + } spcWriteParams(dev, hierName, scale, l, w, sdM); if (sdM != 1.0) @@ -2924,9 +2941,12 @@ spcdevVisit(dev, hc, scale, trans) if (esScale < 0) fprintf(esSpiceF, " w=%g l=%g", w*scale, l*scale); else - fprintf(esSpiceF, " w=%gu l=%gu", - w * scale * esScale, - l * scale * esScale); + { + fprintf(esSpiceF, " w="); + esSIvalue(esSpiceF, 1.0E-6 * w * scale * esScale); + fprintf(esSpiceF, " l="); + esSIvalue(esSpiceF, 1.0E-6 * l * scale * esScale); + } spcWriteParams(dev, hierName, scale, l, w, sdM); if (sdM != 1.0) @@ -2947,7 +2967,7 @@ spcdevVisit(dev, hc, scale, trans) else { dnode = SpiceGetNode(hierName, drain->dterm_node->efnode_name->efnn_hier); - spcnAP(dnode, esFetInfo[dev->dev_type].resClassDrain, scale, + spcnAP(drain, dnode, esFetInfo[dev->dev_type].resClassDrain, scale, "ad", "pd", sdM, esSpiceF, w); } if (hierS) @@ -2955,7 +2975,7 @@ spcdevVisit(dev, hc, scale, trans) scale, "as", "ps", sdM, esSpiceF); else { snode= SpiceGetNode(hierName, source->dterm_node->efnode_name->efnn_hier); - spcnAP(snode, esFetInfo[dev->dev_type].resClassSource, scale, + spcnAP(source, snode, esFetInfo[dev->dev_type].resClassSource, scale, "as", "ps", sdM, esSpiceF, w); } if (subAP) @@ -2968,8 +2988,8 @@ spcdevVisit(dev, hc, scale, trans) fprintf(esSpiceF, "asub=0 psub=0"); } else if (subnodeFlat) - spcnAP(subnodeFlat, esFetInfo[dev->dev_type].resClassSub, scale, - "asub", "psub", sdM, esSpiceF, -1); + spcnAP(NULL, subnodeFlat, esFetInfo[dev->dev_type].resClassSub, + scale, "asub", "psub", sdM, esSpiceF, -1); else fprintf(esSpiceF, "asub=0 psub=0"); } @@ -2977,13 +2997,21 @@ spcdevVisit(dev, hc, scale, trans) /* Now output attributes, if present */ if (!esNoAttrs) { - if (gate->dterm_attrs || source->dterm_attrs || drain->dterm_attrs) + bool haveSattr = FALSE; + bool haveDattr = FALSE; + + if (source->dterm_attrs && (*source->dterm_attrs)) + haveSattr = TRUE; + if (drain->dterm_attrs && (*drain->dterm_attrs)) + haveDattr = TRUE; + + if (gate->dterm_attrs || haveSattr || haveDattr) fprintf(esSpiceF,"\n**devattr"); if (gate->dterm_attrs) fprintf(esSpiceF, " g=%s", gate->dterm_attrs); - if (source->dterm_attrs) + if (haveSattr) fprintf(esSpiceF, " s=%s", source->dterm_attrs); - if (drain->dterm_attrs) + if (haveDattr) fprintf(esSpiceF, " d=%s", drain->dterm_attrs); } break; @@ -3054,6 +3082,83 @@ FILE *outf; } } +/* + * ---------------------------------------------------------------------------- + * + * esSIvalue -- + * + * Print an output in appropriate SI units used by SPICE. e.g., 1.0e-6 + * will be printed as "1.0u"; 1.0e-12 will be printed as "1.0p", etc. + * + * Return value: + * None. + * + * Side effects: + * Generates output to stream "file". + * + * ---------------------------------------------------------------------------- + */ + +void +esSIvalue(file, value) + FILE *file; + float value; +{ + char suffix = '\0'; + float avalue; + + avalue = fabsf(value); + + if (avalue < 1.0E-18) + { + /* Do nothing---value is probably zero */ + } + else if (avalue < 1.0E-15) + { + suffix = 'a'; + value *= 1.0E18; + } + else if (avalue < 1.0E-12) + { + suffix = 'f'; + value *= 1.0E15; + } + else if (avalue < 1.0E-9) + { + suffix = 'p'; + value *= 1.0E12; + } + else if (avalue < 1.0E-6) + { + suffix = 'n'; + value *= 1.0E9; + } + else if (avalue < 1.0E-3) + { + suffix = 'u'; + value *= 1.0E6; + } + else if (avalue <= 1.0E-3) + { + suffix = 'm'; + value *= 1.0E3; + } + else if (avalue >= 1.0E9) + { + suffix = 'G'; + value /= 1.0E9; + } + else if (avalue >= 1.0E3) + { + suffix = 'k'; + value /= 1.0E3; + } + + if (suffix == '\0') + fprintf(file, "%g", value); + else + fprintf(file, "%g%c", value, suffix); +} /* * ---------------------------------------------------------------------------- @@ -3074,7 +3179,8 @@ FILE *outf; * * ---------------------------------------------------------------------------- */ -int spcnAP(node, resClass, scale, asterm, psterm, m, outf, w) +int spcnAP(dterm, node, resClass, scale, asterm, psterm, m, outf, w) + DevTerm *dterm; EFNode *node; int resClass; float scale, m; @@ -3084,6 +3190,9 @@ int spcnAP(node, resClass, scale, asterm, psterm, m, outf, w) { char afmt[15], pfmt[15]; float dsc; + int area, perim; + char *cptr; + bool haveAttrs = FALSE; if ((node == NULL) || (node->efnode_client == (ClientData)NULL)) { @@ -3091,18 +3200,10 @@ int spcnAP(node, resClass, scale, asterm, psterm, m, outf, w) return 1; } - if (esScale < 0) - { - if (asterm) sprintf(afmt, " %s=%%g", asterm); - if (psterm) sprintf(pfmt, " %s=%%g", psterm); - } - else - { - if (asterm) sprintf(afmt, " %s=%%gp", asterm); - if (psterm) sprintf(pfmt, " %s=%%gu", psterm); - } + if (asterm) sprintf(afmt, " %s=", asterm); + if (psterm) sprintf(pfmt, " %s=", psterm); - if (!esDistrJunct || w == -1) goto oldFmt; + if (!esDistrJunct || w == -1) goto newFmt; if (((nodeClient*)node->efnode_client)->m_w.widths != NULL) dsc = w / ((nodeClient*)node->efnode_client)->m_w.widths[resClass]; @@ -3115,52 +3216,96 @@ int spcnAP(node, resClass, scale, asterm, psterm, m, outf, w) if (esScale < 0) { if (asterm) - fprintf(outf, afmt, - node->efnode_pa[resClass].pa_area * scale * scale * dsc); + { + fprintf(outf, afmt); + esSIvalue(outf, 1.0E-12 * node->efnode_pa[resClass].pa_area + * scale * scale * dsc); + } if (psterm) - fprintf(outf, pfmt, - node->efnode_pa[resClass].pa_perim * scale * dsc); + { + fprintf(outf, pfmt); + esSIvalue(outf, 1.0E-6 * node->efnode_pa[resClass].pa_perim * scale * dsc); + } } else { if (asterm) - fprintf(outf, afmt, - ((float)node->efnode_pa[resClass].pa_area * scale * scale) - * esScale * esScale * dsc); + { + fprintf(outf, afmt); + esSIvalue(outf, 1.0E-12 * ((float)node->efnode_pa[resClass].pa_area + * scale * scale) * esScale * esScale * dsc); + } if (psterm) - fprintf(outf, pfmt, - ((float)node->efnode_pa[resClass].pa_perim * scale) - * esScale * dsc); + { + fprintf(outf, pfmt); + esSIvalue(outf, 1.0E-6 * ((float)node->efnode_pa[resClass].pa_perim + * scale) * esScale * dsc); + } } return 0; -oldFmt: - if (resClass == NO_RESCLASS || - beenVisited((nodeClient *)node->efnode_client, resClass)) - scale = 0; - else - markVisited((nodeClient *)node->efnode_client, resClass); +newFmt: + /* New format introduced 2/15/2023: Area and perimeter of each terminal + * are maintained in the terminal attributes for "fet" or "device mosfet" + * type devices (otherwise, for subcircuit device types, this routine is + * not used and the same values are found in the device parameters). + * + * Values are the last two values in a comma-separated list in + * dterm_attrs. If not found, then default to the + */ + + cptr = (dterm) ? dterm->dterm_attrs : NULL; + while (cptr) + { + if (*cptr == ',') cptr++; + if (sscanf(cptr, "%d,%d", &area, &perim) != 2) + cptr = strchr(cptr, ','); + else + { + haveAttrs = TRUE; + *cptr = '\0'; + break; + } + } + + if (!haveAttrs) + { + area = node->efnode_pa[resClass].pa_area; + perim = node->efnode_pa[resClass].pa_perim; + + if (resClass == NO_RESCLASS || + beenVisited((nodeClient *)node->efnode_client, resClass)) + scale = 0; + else + markVisited((nodeClient *)node->efnode_client, resClass); + } if (esScale < 0) { if (asterm) - fprintf(outf, afmt, - node->efnode_pa[resClass].pa_area * scale * scale / m); + { + fprintf(outf, afmt); + esSIvalue(outf, 1.0E-12 * area * scale * scale / m); + } if (psterm) - fprintf(outf, pfmt, - node->efnode_pa[resClass].pa_perim * scale / m); + { + fprintf(outf, pfmt); + esSIvalue(outf, 1.0E-6 * perim * scale / m); + } } else { if (asterm) - fprintf(outf, afmt, - ((float)node->efnode_pa[resClass].pa_area * scale * scale) - * esScale * esScale); + { + fprintf(outf, afmt); + esSIvalue(outf, 1.0E-12 * ((float)area * scale * scale) * esScale * esScale); + } if (psterm) - fprintf(outf, pfmt, - ((float)node->efnode_pa[resClass].pa_perim * scale) - * esScale); + { + fprintf(outf, pfmt); + esSIvalue(outf, 1.0E-6 * ((float)perim * scale) * esScale); + } } return 0; } @@ -3176,17 +3321,13 @@ int spcnAPHier(dterm, hierName, resClass, scale, asterm, psterm, m, outf) EFNode *node = dterm->dterm_node; nodeClientHier *nc; char afmt[15], pfmt[15]; + int area, perim; + char *cptr; + bool haveAttrs = FALSE; + + sprintf(afmt," %s=", asterm); + sprintf(pfmt," %s=", psterm); - if (esScale < 0) - { - sprintf(afmt," %s=%%g", asterm); - sprintf(pfmt," %s=%%g", psterm); - } - else - { - sprintf(afmt," %s=%%gp", asterm); - sprintf(pfmt," %s=%%gu", psterm); - } if (node->efnode_client == (ClientData) NULL) initNodeClientHier(node); @@ -3196,27 +3337,48 @@ int spcnAPHier(dterm, hierName, resClass, scale, asterm, psterm, m, outf) clearVisited(nc); nc->lastPrefix = hierName; } - if (resClass == NO_RESCLASS || - beenVisited((nodeClientHier *)node->efnode_client, resClass) ) - scale = 0.0; - else - markVisited((nodeClientHier *)node->efnode_client, resClass); + + /* Check for area and perim values in dterm_attrs */ + + cptr = dterm->dterm_attrs; + while (cptr) + { + if (*cptr == ',') cptr++; + if (sscanf(cptr, "%d,%d", &area, &perim) != 2) + cptr = strchr(cptr, ','); + else + { + haveAttrs = TRUE; + *cptr = '\0'; + break; + } + } + + if (!haveAttrs) + { + area = node->efnode_pa[resClass].pa_area; + perim = node->efnode_pa[resClass].pa_perim; + + if (resClass == NO_RESCLASS || + beenVisited((nodeClient *)node->efnode_client, resClass)) + scale = 0; + else + markVisited((nodeClient *)node->efnode_client, resClass); + } if (esScale < 0) { - fprintf(outf, afmt, - node->efnode_pa[resClass].pa_area * scale * scale / m); - fprintf(outf, pfmt, - node->efnode_pa[resClass].pa_perim * scale / m); + fprintf(outf, afmt); + esSIvalue(outf, 1.0E-12 * area * scale * scale / m); + fprintf(outf, pfmt); + esSIvalue(outf, 1.0E-6 * perim * scale / m); } else { - fprintf(outf, afmt, - ((float)node->efnode_pa[resClass].pa_area * scale) - * esScale * esScale); - fprintf(outf, pfmt, - ((float)node->efnode_pa[resClass].pa_perim * scale) - * esScale); + fprintf(outf, afmt); + esSIvalue(outf, 1.0E-12 * ((float)area * scale) * esScale * esScale); + fprintf(outf, pfmt); + esSIvalue(outf, 1.0E-6 * ((float)perim * scale) * esScale); } return 0; } diff --git a/ext2spice/ext2spice.h b/ext2spice/ext2spice.h index 8d7dde38..4ba9da2c 100644 --- a/ext2spice/ext2spice.h +++ b/ext2spice/ext2spice.h @@ -52,6 +52,7 @@ extern int devDistJunctHierVisit(); extern int spcnAPHier(); extern void mergeAttr(); extern int update_w(); +extern void esSIvalue(); /* Options specific to ext2spice */ extern bool esDoExtResis; diff --git a/extflat/EFbuild.c b/extflat/EFbuild.c index a167878d..e1822a88 100644 --- a/extflat/EFbuild.c +++ b/extflat/EFbuild.c @@ -1083,8 +1083,6 @@ efBuildDevice(def, class, type, r, argc, argv) term = &newdev->dev_terms[n]; term->dterm_node = efBuildDevNode(def, av[TERM_NAME], FALSE); term->dterm_length = atoi(av[TERM_PERIM]); - term->dterm_area = 0; - term->dterm_perim = 0; /* If the attr list is '0', this signifies no attributes */ if (av[TERM_ATTRS][0] == '0' && av[TERM_ATTRS][1] == '\0') diff --git a/extflat/extflat.h b/extflat/extflat.h index ef475d3e..2275313b 100644 --- a/extflat/extflat.h +++ b/extflat/extflat.h @@ -255,8 +255,6 @@ typedef struct devterm EFNode *dterm_node; /* Node to which we're connected */ char *dterm_attrs; /* Attribute list */ int dterm_length; /* Length of terminal connection to gate */ - int dterm_perim; /* Terminal perimeter if passed as a param */ - int dterm_area; /* Terminal area if passed as a param */ } DevTerm; /* diff --git a/extract/ExtBasic.c b/extract/ExtBasic.c index 62244ad9..87e1656d 100644 --- a/extract/ExtBasic.c +++ b/extract/ExtBasic.c @@ -105,9 +105,9 @@ struct transRec int tr_termlen[MAXSD]; /* Length of each diff terminal edge, * used for computing L/W for the fet. */ - int tr_termdepth[MAXSD]; /* Length of the terminal perpendicular - * to the device edge. - */ + int tr_termarea[MAXSD]; /* Total area of the terminal */ + int tr_termperim[MAXSD]; /* Total perimeter of the terminal */ + int tr_termshared[MAXSD]; /* Number of devices sharing this terminal */ Point tr_termvector[MAXSD]; /* Perimeter traversal vector, used to * find and calculate correct parameters * for annular (ring) devices and other @@ -1061,7 +1061,7 @@ ExtSortTerminals(tran, ll) TermTilePos *p1, *p2; NodeRegion *tmp_node; TermTilePos tmp_pos; - int tmp_len, tmp_depth; + int tmp_len, tmp_area, tmp_perim, tmp_shared; LabelList *lp; do @@ -1089,21 +1089,29 @@ ExtSortTerminals(tran, ll) tmp_node = tran->tr_termnode[nsd]; tmp_pos = tran->tr_termpos[nsd]; tmp_len = tran->tr_termlen[nsd]; - tmp_depth = tran->tr_termdepth[nsd]; + tmp_area = tran->tr_termarea[nsd]; + tmp_perim = tran->tr_termperim[nsd]; + tmp_shared = tran->tr_termshared[nsd]; tran->tr_termnode[nsd] = tran->tr_termnode[nsd+1]; tran->tr_termpos[nsd] = tran->tr_termpos[nsd+1]; tran->tr_termlen[nsd] = tran->tr_termlen[nsd+1]; - tran->tr_termdepth[nsd] = tran->tr_termdepth[nsd+1]; + tran->tr_termperim[nsd] = tran->tr_termperim[nsd+1]; + tran->tr_termarea[nsd] = tran->tr_termarea[nsd+1]; + tran->tr_termshared[nsd] = tran->tr_termshared[nsd+1]; tran->tr_termnode[nsd+1] = tmp_node; tran->tr_termpos[nsd+1] = tmp_pos; tran->tr_termlen[nsd+1] = tmp_len; - tran->tr_termdepth[nsd+1] = tmp_depth; + tran->tr_termarea[nsd+1] = tmp_area; + tran->tr_termperim[nsd+1] = tmp_perim; + tran->tr_termshared[nsd+1] = tmp_shared; + /* Need to SWAP the indices in the labRegion too. * These for loops within the bubblesort in here are kinda slow * but S,D attributes are not that common so it should not matter * that much -- Stefanos 5/96 */ + for ( lp = ll ; lp ; lp = lp->ll_next ) if ( lp->ll_attr == nsd ) lp->ll_attr = LL_SORTATTR ; else if ( lp->ll_attr == nsd+1 ) lp->ll_attr = nsd ; @@ -1690,13 +1698,13 @@ extOutputParameters(def, transList, outFile) */ void -extOutputDevParams(reg, devptr, outFile, length, width, depthvec) +extOutputDevParams(reg, devptr, outFile, length, width, areavec) TransRegion *reg; ExtDevice *devptr; FILE *outFile; int length; int width; - int *depthvec; + int *areavec; { ParamList *chkParam; @@ -1710,12 +1718,14 @@ extOutputDevParams(reg, devptr, outFile, length, width, depthvec) chkParam->pl_param[1] == '0') fprintf(outFile, " %c=%d", chkParam->pl_param[0], reg->treg_area); + /* Note: a1, a2, etc., are standard output */ break; case 'p': if (chkParam->pl_param[1] == '\0' || chkParam->pl_param[1] == '0') fprintf(outFile, " %c=%d", chkParam->pl_param[0], extTransRec.tr_perim); + /* Note: p1, p2, etc., are standard output */ break; case 'l': if (chkParam->pl_param[1] == '\0' || @@ -1725,10 +1735,12 @@ extOutputDevParams(reg, devptr, outFile, length, width, depthvec) else if (chkParam->pl_param[1] > '0' && chkParam->pl_param[1] <= '9') { int tidx = chkParam->pl_param[1] - '1'; - /* output depth of terminal */ + /* output length of terminal, assuming a rectangular + * shape, as simplified terminal area / width + */ fprintf(outFile, " %c%c=%d", chkParam->pl_param[0], chkParam->pl_param[1], - depthvec[tidx]); + areavec[tidx] / width); } break; case 'w': @@ -1953,7 +1965,9 @@ extOutputDevices(def, transList, outFile) while (extTransRec.tr_nterm < nsd) { extTransRec.tr_termlen[extTransRec.tr_nterm] = 0; - extTransRec.tr_termdepth[extTransRec.tr_nterm] = 0; + extTransRec.tr_termarea[extTransRec.tr_nterm] = 0; + extTransRec.tr_termperim[extTransRec.tr_nterm] = 0; + extTransRec.tr_termshared[extTransRec.tr_nterm] = 0; extTransRec.tr_termnode[extTransRec.tr_nterm++] = node; } } @@ -2177,7 +2191,7 @@ extOutputDevices(def, transList, outFile) } extOutputDevParams(reg, devptr, outFile, length, width, - extTransRec.tr_termdepth); + extTransRec.tr_termarea); fprintf(outFile, " \"%s\"", (subsName == NULL) ? "None" : subsName); @@ -2187,7 +2201,7 @@ extOutputDevices(def, transList, outFile) case DEV_NDIODE: case DEV_PDIODE: extOutputDevParams(reg, devptr, outFile, length, width, - extTransRec.tr_termdepth); + extTransRec.tr_termarea); if (subsName != NULL) fprintf(outFile, " \"%s\"", subsName); break; @@ -2315,7 +2329,7 @@ extOutputDevices(def, transList, outFile) fprintf(outFile, " %g", dres / 1000.0); /* mOhms -> Ohms */ extOutputDevParams(reg, devptr, outFile, length, width, - extTransRec.tr_termdepth); + extTransRec.tr_termarea); if (devptr->exts_deviceClass == DEV_RSUBCKT) { @@ -2406,7 +2420,7 @@ extOutputDevices(def, transList, outFile) } extOutputDevParams(reg, devptr, outFile, length, width, - extTransRec.tr_termdepth); + extTransRec.tr_termarea); if (devptr->exts_deviceClass == DEV_CSUBCKT) { @@ -2428,7 +2442,7 @@ extOutputDevices(def, transList, outFile) node = (NodeRegion *) extGetRegion(reg->treg_tile); ll = node->nreg_labels; extTransOutTerminal((LabRegion *) node, ll, LL_GATEATTR, - extTransRec.tr_gatelen, outFile); + extTransRec.tr_gatelen, 0, 0, 0, outFile); /* Sort source and drain terminals by position, unless the */ /* device is asymmetric, in which case source and drain do not */ @@ -2440,7 +2454,10 @@ extOutputDevices(def, transList, outFile) /* each non-gate terminal */ for (nsd = 0; nsd < extTransRec.tr_nterm; nsd++) extTransOutTerminal((LabRegion *) extTransRec.tr_termnode[nsd], ll, - nsd, extTransRec.tr_termlen[nsd], outFile); + nsd, extTransRec.tr_termlen[nsd], + extTransRec.tr_termarea[nsd], + extTransRec.tr_termperim[nsd], + extTransRec.tr_termshared[nsd], outFile); (void) fputs("\n", outFile); } @@ -2867,6 +2884,171 @@ extTransTileFunc(tile, pNum, arg) return 0; } +/* Structures used by extTermAPFunc() for storing area and perimeter data */ + +typedef struct _nodelist { + struct _nodelist *nl_next; + NodeRegion *nl_node; +} ExtNodeList; + +typedef struct _extareaperimdata { + int eapd_area; + int eapd_perim; + TileTypeBitMask eapd_mask; + TileTypeBitMask *eapd_gatemask; + NodeRegion *eapd_gatenode; + ExtNodeList *eapd_shared; +} ExtAreaPerimData; + +/* + * ---------------------------------------------------------------------------- + * + * extAddSharedDevice -- + * + * Add a node region representing a device to the list of nodes + * kept in the structure passed to extTermAPFunc(), to keep track + * of how many devices share the same terminal area. + * + * ---------------------------------------------------------------------------- + */ + +void +extAddSharedDevice(eapd, node) + ExtAreaPerimData *eapd; + NodeRegion *node; +{ + ExtNodeList *nl, *newnl; + + for (nl = eapd->eapd_shared; nl; nl = nl->nl_next) + if (nl->nl_node == node) break; + + if (nl == NULL) + { + newnl = (ExtNodeList *)mallocMagic(sizeof(ExtNodeList)); + newnl->nl_node = node; + newnl->nl_next = eapd->eapd_shared; + eapd->eapd_shared = newnl; + } +} + +/* + * ---------------------------------------------------------------------------- + * + * extTermAPFunc -- + * + * Callback function used by extTransPerimFunc() to find the largest + * area encompassing a device terminal. This is the bounding box of + * the area containing terminal types connected to a device tile. + * + * This routine is redundant with the area and perimeter calculations + * in extFindNodes(), but that routine traverses an entire net. This + * routine finds the area and perimeter belonging to material on a + * single plane extending from a device (e.g., diffusion and contacts + * on a FET source or drain). + * + * Note that this definition is not necessarily accurate for defining + * terminal area and perimeter, as the area of terminal types may not + * be rectangular, making an approximation using length and width + * inappropriate. + * + * ---------------------------------------------------------------------------- + */ + +int +extTermAPFunc(tile, pNum, eapd) + Tile *tile; /* Tile extending a device terminal */ + int pNum; /* Plane of tile (unused, set to -1) */ + ExtAreaPerimData *eapd; /* Area and perimeter totals for terminal */ +{ + TileType type; + Tile *tp; + Rect r; + + TiToRect(tile, &r); + eapd->eapd_area += (r.r_xtop - r.r_xbot) * (r.r_ytop - r.r_ybot); + + /* Diagonal */ + if (IsSplit(tile)) + { + int w, h, l; + type = (SplitSide(tile)) ? SplitLeftType(tile): SplitRightType(tile); + w = RIGHT(tile) - LEFT(tile); + h = TOP(tile) - BOTTOM(tile); + l = w * w + h * h; + eapd->eapd_perim += (int)sqrt((double)l); + } + + /* Top */ + for (tp = RT(tile); RIGHT(tp) > LEFT(tile); tp = BL(tp)) + { + type = TiGetBottomType(tp); + if (TTMaskHasType(&eapd->eapd_mask, type)) + { + eapd->eapd_perim += MIN(RIGHT(tile), RIGHT(tp)) - + MAX(LEFT(tile), LEFT(tp)); + if (TTMaskHasType(eapd->eapd_gatemask, type)) + if (tp->ti_client != (ClientData)eapd->eapd_gatenode) + extAddSharedDevice(eapd, (NodeRegion *)tp->ti_client); + } + } + + /* Bottom */ + for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp = TR(tp)) + { + type = TiGetTopType(tp); + if (TTMaskHasType(&eapd->eapd_mask, type)) + { + eapd->eapd_perim += MIN(RIGHT(tile), RIGHT(tp)) - + MAX(LEFT(tile), LEFT(tp)); + if (TTMaskHasType(eapd->eapd_gatemask, type)) + if (tp->ti_client != (ClientData)eapd->eapd_gatenode) + extAddSharedDevice(eapd, (NodeRegion *)tp->ti_client); + } + } + + /* Left */ + for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp = RT(tp)) + { + type = TiGetRightType(tp); + if (TTMaskHasType(&eapd->eapd_mask, type)) + { + eapd->eapd_perim += MIN(TOP(tile), TOP(tp)) - + MAX(BOTTOM(tile), BOTTOM(tp)); + if (TTMaskHasType(eapd->eapd_gatemask, type)) + if (tp->ti_client != (ClientData)eapd->eapd_gatenode) + extAddSharedDevice(eapd, (NodeRegion *)tp->ti_client); + } + } + + /* Right */ + for (tp = TR(tile); TOP(tp) > BOTTOM(tile); tp = LB(tp)) + { + type = TiGetLeftType(tp); + if (TTMaskHasType(&eapd->eapd_mask, type)) + { + eapd->eapd_perim += MIN(TOP(tile), TOP(tp)) - + MAX(BOTTOM(tile), BOTTOM(tp)); + if (TTMaskHasType(eapd->eapd_gatemask, type)) + if (tp->ti_client != (ClientData)eapd->eapd_gatenode) + extAddSharedDevice(eapd, (NodeRegion *)tp->ti_client); + } + } + + return 0; +} + +/* + * ---------------------------------------------------------------------------- + * + * extTransPerimFunc -- + * + * Callback function for exploring the perimeter of a device to find + * areas connected to the device (e.g., gate) and areas adjacent but + * not connected (e.g., source and drain). + * + * ---------------------------------------------------------------------------- + */ + int extTransPerimFunc(bp) Boundary *bp; @@ -2875,11 +3057,10 @@ extTransPerimFunc(bp) Tile *tile; NodeRegion *diffNode = (NodeRegion *) extGetRegion(bp->b_outside); ExtDevice *devptr, *deventry; - int i, depth, len = BoundaryLength(bp); + int i, area, perim, len = BoundaryLength(bp); int thisterm; LabelList *ll; Label *lab; - Rect r; bool SDterm = FALSE; tile = bp->b_inside; @@ -2893,17 +3074,6 @@ extTransPerimFunc(bp) else toutside = TiGetTypeExact(bp->b_outside); - /* Experimental---find the depth of the area outside the boundary. - * This can be used in limited circumstances to extract the terminal - * length (here, called tr_depth). - */ - if (toutside == TT_SPACE) - depth = 0; - else if (bp->b_segment.r_xtop == bp->b_segment.r_xbot) - depth = RIGHT(bp->b_outside) - LEFT(bp->b_outside); - else - depth = TOP(bp->b_outside) - BOTTOM(bp->b_outside); - if (extTransRec.tr_devrec != NULL) devptr = extTransRec.tr_devrec; else @@ -2943,7 +3113,9 @@ extTransPerimFunc(bp) extTransRec.tr_nterm++; extTransRec.tr_termnode[thisterm] = diffNode; extTransRec.tr_termlen[thisterm] = 0; - extTransRec.tr_termdepth[thisterm] = 0; + extTransRec.tr_termarea[thisterm] = 0; + extTransRec.tr_termperim[thisterm] = 0; + extTransRec.tr_termshared[thisterm] = 0; extTransRec.tr_termvector[thisterm].p_x = 0; extTransRec.tr_termvector[thisterm].p_y = 0; extTransRec.tr_termpos[thisterm].pnum = DBPlane(toutside); @@ -2986,9 +3158,39 @@ extTransPerimFunc(bp) /* Add the length to this terminal's perimeter */ extTransRec.tr_termlen[thisterm] += len; - /* Update the terminal depth */ - if (depth > extTransRec.tr_termdepth[thisterm]) - extTransRec.tr_termdepth[thisterm] = depth; + if (extTransRec.tr_termarea[thisterm] == 0) + { + /* Find the area and perimeter of the terminal area (connected + * area outside the boundary on a single plane). Note that + * this does not consider terminal area outside of the cell + * or how area or perimeter may be shared or overlap between + * devices. + */ + + ExtAreaPerimData eapd; + int shared; + + eapd.eapd_area = eapd.eapd_perim = 0; + TTMaskCom2(&eapd.eapd_mask, &DBConnectTbl[toutside]); + eapd.eapd_gatemask = &ExtCurStyle->exts_deviceMask; + eapd.eapd_gatenode = (NodeRegion *)extGetRegion(bp->b_inside); + eapd.eapd_shared = NULL; + + DBSrConnectOnePlane(bp->b_outside, DBConnectTbl, + extTermAPFunc, (ClientData)&eapd); + + shared = 1; + while (eapd.eapd_shared) + { + shared++; + freeMagic(eapd.eapd_shared); + eapd.eapd_shared = eapd.eapd_shared->nl_next; + } + + extTransRec.tr_termarea[thisterm] = eapd.eapd_area; + extTransRec.tr_termperim[thisterm] = eapd.eapd_perim; + extTransRec.tr_termshared[thisterm] = shared; + } /* Update the boundary traversal vector */ switch(bp->b_direction) { @@ -3435,13 +3637,16 @@ extSpecialPerimFunc(bp, sense) */ void -extTransOutTerminal(lreg, ll, whichTerm, len, outFile) +extTransOutTerminal(lreg, ll, whichTerm, len, area, perim, shared, outFile) LabRegion *lreg; /* Node connected to terminal */ LabelList *ll; /* Gate's label list */ int whichTerm; /* Which terminal we are processing. The gate * is indicated by LL_GATEATTR. */ int len; /* Length of perimeter along terminal */ + int area; /* Total area of terminal */ + int perim; /* Total perimeter of terminal (includes len) */ + int shared; /* Number of devices sharing the terminal */ FILE *outFile; /* Output file */ { char *cp; @@ -3463,7 +3668,16 @@ extTransOutTerminal(lreg, ll, whichTerm, len, outFile) fmt = ','; } - if (fmt == ' ') + /* NOTE: The area and perimeter of a terminal are divided equally + * among devices that share the same terminal area. This may not + * necessarily be the best way to handle shared terminals; in + * particular, it is preferable to detect and output fingered + * devices separately. + */ + + if ((whichTerm != LL_GATEATTR) && (area != 0) && (perim != 0)) + fprintf(outFile, "%c%d,%d", fmt, (area / shared), (perim / shared)); + else if (fmt == ' ') fprintf(outFile, " 0"); } diff --git a/resis/ResBasic.c b/resis/ResBasic.c index b471954b..cd40077e 100644 --- a/resis/ResBasic.c +++ b/resis/ResBasic.c @@ -24,8 +24,6 @@ static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/ #include "textio/txcommands.h" #include "resis/resis.h" -int resSubDevFunc(); - /* *-------------------------------------------------------------------------- * @@ -440,45 +438,3 @@ ResEachTile(tile, startpoint) return(merged); } - -/* - *------------------------------------------------------------------------- - * - * resSubDevFunc -- called when DBSrPaintArea finds a device within - * a substrate area. - * - * Results: always returns 0 to keep search going. - * - * Side Effects: allocates substrate node. - * - *------------------------------------------------------------------------- - */ - -int -resSubDevFunc(tile, tp) - Tile *tile, *tp; -{ - tileJunk *junk = (tileJunk *)(tile->ti_client); - resNode *resptr; - tElement *tcell; - int x, y; - - if (junk->deviceList->rd_fet_subs == NULL) - { - resptr = (resNode *) mallocMagic((unsigned)(sizeof(resNode))); - junk->deviceList->rd_fet_subs = resptr; - junk->tj_status |= RES_TILE_DEV; - tcell = (tElement *) mallocMagic((unsigned)(sizeof(tElement))); - tcell->te_thist = junk->deviceList; - tcell->te_nextt = NULL; - x = (LEFT(tile) + RIGHT(tile)) >> 1; - y = (TOP(tile) + BOTTOM(tile)) >> 1; - - InitializeNode(resptr, x, y, RES_NODE_JUNCTION); - resptr->rn_te = tcell; - ResAddToQueue(resptr, &ResNodeQueue); - - NEWBREAK(resptr, tp, x, y, NULL); - } - return 0; -} diff --git a/resis/ResReadSim.c b/resis/ResReadSim.c index 61007f2a..85f35f6d 100644 --- a/resis/ResReadSim.c +++ b/resis/ResReadSim.c @@ -382,8 +382,9 @@ ResSimSubckt(line) if (!strcmp(EFDevTypes[j], line[i])) break; - /* Read attributes, especially to pick up values for L, W, X, and Y, - * that are critical for use by extresist. + /* Read attributes, especially to pick up values for L, W, X, and Y; + * and source and drain area and perimeter, that are critical for use + * by extresist. */ for (k = 1; line[k][0] != '\0'; k++) { @@ -406,6 +407,12 @@ ResSimSubckt(line) case 'y': device->location.p_y = (int)((float)atof(eqptr) / lambda); break; + case 's': + device->rs_sattr = StrDup((char **)NULL, eqptr); + break; + case 'd': + device->rs_dattr = StrDup((char **)NULL, eqptr); + break; } } } @@ -531,30 +538,47 @@ ResSimDevice(line, rpersquare, devptr) /* sim attributes look like g=a1,a2 */ /* ext attributes are "a1","a2" */ - /* do conversion from one to the other here */ + /* Do conversion from one to the other here */ + /* NOTE: As of version 8.3.366, .ext attributes will end in two */ + /* integer values, not quoted, for device area and perimeter. Do */ + /* not quote them. */ for (i = RDEV_ATTR; i < RDEV_ATTR + RDEV_NUM_ATTR; i++) { + char *cptr, *sptr; + int d1, d2; + if (line[i][0] == '\0') break; - k = 0; - tmpattr[k++] = '"'; - for (j = 2; line[i][j] != '\0'; j++) + + sptr = &line[i][2]; /* Start after "s=" or "d=" */ + tmpattr[0] = '\0'; + while ((cptr = strchr(sptr, ',')) != NULL) { - if (line[i][j] == ',') + if (sscanf(sptr, "%d,%d", &d1, &d2) == 2) { - tmpattr[k++] = '"'; - tmpattr[k++] = ','; - tmpattr[k++] = '"'; + strcat(tmpattr, sptr); + sptr = NULL; + break; } else { - tmpattr[k++] = line[i][j]; + *cptr = '\0'; + strcat(tmpattr, "\""); + strcat(tmpattr, sptr); + strcat(tmpattr, "\","); + sptr = cptr + 1; + *cptr = ','; } } - tmpattr[k++] = '"'; - tmpattr[k++] = '\0'; - newattr = (char *)mallocMagic((unsigned)k); - strncpy(newattr, tmpattr, k); + if (sptr && (strlen(sptr) != 0)) + { + strcat(tmpattr, "\""); + strcat(tmpattr, sptr); + strcat(tmpattr, "\""); + } + + newattr = (char *)mallocMagic(strlen(tmpattr) + 1); + strcpy(newattr, tmpattr); switch (line[i][0]) { case 'g':