/* * ext2sim.c -- * * Program to flatten hierarchical .ext files and produce * a .sim file, suitable for use as input to simulators * such as esim and crystal. * * Flattens the tree rooted at file.ext, reading in additional .ext * files as specified by "use" lines in file.ext. The output is left * in file.sim, unless '-o esSimFile' is specified, in which case the * output is left in 'esSimFile'. * */ #ifndef lint static char rcsid[] = "$Header: /ufs/repository/magic/ext2sim/ext2sim.c,v 1.5 2001/07/06 17:58:30 rajit Exp $"; #endif not lint #include #include #include #include #include "misc/magic.h" #include "utils/geometry.h" #include "utils/hash.h" #include "utils/utils.h" #include "extflat/extflat.h" #include "utils/runstats.h" #include "utils/malloc.h" #include "math.h" /* for sqrt() in bipolar L,W calculation */ /* Forward declarations */ int mainArgs(); int fetVisit(), resistVisit(), capVisit(), nodeVisit(); /* Options specific to ext2sim */ bool esFetNodesOnly = FALSE; bool esNoAlias = FALSE; bool esNoAttrs = FALSE; bool esNoLabel = FALSE; bool esHierAP = FALSE; char esDefaultOut[FNSIZE], esDefaultAlias[FNSIZE], esDefaultLabel[FNSIZE]; char *esOutName = esDefaultOut, *esAliasName = esDefaultAlias; char *esLabelName = esDefaultLabel; int esCapAccuracy = 1; char esCapFormat[FNSIZE]; FILE *esSimF = NULL; FILE *esAliasF = NULL; FILE *esLabF = NULL; #define MIT 0 #define LBL 1 #define SU 2 static unsigned short esFormat = SU ; struct { short resClassSD ; /* the resistance class of the src/drn of the fet */ short resClassSub ; /* the resistance class of the substrate of the fet */ char *defSubs ; /* the default substrate node */ } fetInfo[MAXFETTYPES]; typedef struct { long visitMask:MAXFETTYPES; } nodeClient; typedef struct { HierName *lastPrefix; long visitMask:MAXFETTYPES; } nodeClientHier; #define NO_RESCLASS -1 #define markVisited(client, rclass) \ { (client)->visitMask |= (1<visitMask = (long)0; } #define beenVisited(client, rclass) \ ( (client)->visitMask & (1<efnode_client,sizeof(nodeClient)); \ (( nodeClient *)(node)->efnode_client)->visitMask = (long) 0; \ } #define initNodeClientHier(node) \ { \ MALLOC(ClientData,(node)->efnode_client,sizeof(nodeClientHier)); \ ((nodeClientHier *) (node)->efnode_client)->visitMask = (long) 0; \ } /* attributes controlling the Area/Perimeter extraction of fet terminals */ #define ATTR_FLATAP "*[Ee][Xx][Tt]:[Aa][Pp][Ff]*" #define ATTR_HIERAP "*[Ee][Xx][Tt]:[Aa][Pp][Hh]*" #define ATTR_SUBSAP "*[Ee][Xx][Tt]:[Aa][Pp][Ss]*" /* * ---------------------------------------------------------------------------- * * main -- * * Top level of ext2sim. * * ---------------------------------------------------------------------------- */ main(argc, argv) char *argv[]; { int i,flatFlags; char *inName; FILE *f; EFInit(); /* create default fetinfo entries (MOSIS) which can be overriden by the command line arguments */ for ( i = 0 ; i < MAXFETTYPES ; i++ ) { fetInfo[i].resClassSD = NO_RESCLASS; fetInfo[i].resClassSub = NO_RESCLASS; fetInfo[i].defSubs = NULL; } i = efBuildAddStr(EFFetTypes, &EFFetNumTypes, MAXFETTYPES, "nfet"); fetInfo[i].resClassSD = 0 ; fetInfo[i].resClassSub = NO_RESCLASS ; fetInfo[i].defSubs = "Gnd!"; i = efBuildAddStr(EFFetTypes, &EFFetNumTypes, MAXFETTYPES, "pfet"); fetInfo[i].resClassSD = 1 ; fetInfo[i].resClassSub = 6 ; fetInfo[i].defSubs = "Vdd!"; i = efBuildAddStr(EFFetTypes, &EFFetNumTypes, MAXFETTYPES, "nmos"); fetInfo[i].resClassSD = 0 ; fetInfo[i].resClassSub = NO_RESCLASS ; fetInfo[i].defSubs = "Gnd!"; i = efBuildAddStr(EFFetTypes, &EFFetNumTypes, MAXFETTYPES, "pmos"); fetInfo[i].resClassSD = 1 ; fetInfo[i].resClassSub = 6 ; fetInfo[i].defSubs = "Vdd!"; /* Process command line arguments */ inName = EFArgs(argc, argv, mainArgs, (ClientData) NULL); if (inName == NULL) exit (1); /* * Initializations specific to this program. * Make output, alias, and label names be of the form * inName.suffix if they weren't explicitly specified, * where suffix is .sim, .al, or .nodes. */ if (esOutName == esDefaultOut) (void) sprintf(esDefaultOut, "%s.sim", inName); if (esAliasName == esDefaultAlias) (void) sprintf(esDefaultAlias, "%s.al", inName); if (esLabelName == esDefaultLabel) (void) sprintf(esDefaultLabel, "%s.nodes", inName); if ((esSimF = fopen(esOutName, "w")) == NULL) { perror(esOutName); exit (1); } if (!esNoAlias && (esAliasF = fopen(esAliasName, "w")) == NULL) { perror(esAliasName); exit (1); } if (!esNoLabel && (esLabF = fopen(esLabelName, "w")) == NULL) { perror(esLabelName); exit (1); } /* Read the hierarchical description of the input circuit */ EFReadFile(inName); fprintf(esSimF, "| units: %d tech: %s format: %s\n", EFScale, EFTech, (esFormat == MIT) ? "MIT" : ( (esFormat == LBL) ? "LBL" : "SU" ) ); /* Convert the hierarchical description to a flat one */ flatFlags = EF_FLATNODES; if (EFCapThreshold < INFINITE_THRESHOLD) flatFlags |= EF_FLATCAPS; if (EFResistThreshold < INFINITE_THRESHOLD) flatFlags |= EF_FLATRESISTS; EFFlatBuild(inName, flatFlags); EFVisitFets(fetVisit, (ClientData) NULL); if (flatFlags & EF_FLATCAPS) { (void) sprintf( esCapFormat, " %%.%dlf\n", esCapAccuracy); EFVisitCaps(capVisit, (ClientData) NULL); } EFVisitResists(resistVisit, (ClientData) NULL); (void) sprintf( esCapFormat, " GND %%.%dlf\n", esCapAccuracy); EFVisitNodes(nodeVisit, (ClientData) NULL); #ifdef free_all_mem EFFlatDone(); EFDone(); #endif free_all_mem printf("Memory used: %s\n", RunStats(RS_MEM, NULL, NULL)); exit (0); } /* * ---------------------------------------------------------------------------- * * mainArgs -- * * Process those arguments that are specific to ext2sim. * Assumes that *pargv[0][0] is '-', indicating a flag * argument. * * Results: * None. * * Side effects: * After processing an argument, updates *pargc and *pargv * to point to after the argument. * * May initialize various global variables based on the * arguments given to us. * * Exits in the event of an improper argument. * * ---------------------------------------------------------------------------- */ Void mainArgs(pargc, pargv) int *pargc; char ***pargv; { char **argv = *pargv, *cp; int argc = *pargc; switch (argv[0][1]) { case 'A': esNoAlias = TRUE; break; case 'B': esNoAttrs = TRUE; break; case 'F': esFetNodesOnly = TRUE; break; case 'L': esNoLabel = TRUE; break; case 'a': if ((esAliasName = ArgStr(&argc, &argv, "filename")) == NULL) goto usage; break; case 'l': if ((esLabelName = ArgStr(&argc, &argv, "filename")) == NULL) goto usage; break; case 'o': if ((esOutName = ArgStr(&argc, &argv, "filename")) == NULL) goto usage; break; case 'f': { char *ftmp ; if ((ftmp = ArgStr(&argc, &argv, "format")) == NULL) goto usage; if ( strcasecmp(ftmp,"MIT") == 0 ) esFormat = MIT ; else if ( strcasecmp(ftmp,"LBL") == 0 ) esFormat = LBL ; else if ( strcasecmp(ftmp,"SU") == 0 ) esFormat = SU ; else goto usage; break; } case 'y': { char *t; if (( t = ArgStr(&argc, &argv, "cap-accuracy") ) == NULL) goto usage; esCapAccuracy = atoi(t); break; } case 'J': { char *ftmp ; if ((ftmp = ArgStr(&argc, &argv, "hierAP_SD")) == NULL) goto usage; if ( strcasecmp(ftmp, "HIER") == 0 ) esHierAP = TRUE ; else if ( strcasecmp(ftmp, "FLAT") == 0 ) esHierAP = FALSE ; else goto usage; break; } case 'j': { char *rp, subsNode[80] ; int ndx, rClass, rClassSub = NO_RESCLASS; if ((cp = ArgStr(&argc,&argv,"resistance class")) == NULL) goto usage; if ( (rp = (char *)index(cp,':')) == NULL ) goto usage; else *rp++ = '\0'; if ( sscanf(rp, "%d/%d/%s", &rClass, &rClassSub, subsNode) != 3 ) { rClassSub = NO_RESCLASS ; if ( sscanf(rp, "%d/%s", &rClass, subsNode) != 2 ) goto usage; } ndx = efBuildAddStr(EFFetTypes, &EFFetNumTypes, MAXFETTYPES, cp); fetInfo[ndx].resClassSD = rClass; fetInfo[ndx].resClassSub = rClassSub; MALLOC(char *, fetInfo[ndx].defSubs, (strlen(subsNode)+1)); strcpy(fetInfo[ndx].defSubs,subsNode); fprintf(stderr, "Info: fet %s(%d) sdRclass=%d subRclass=%d dSub=%s\n", cp, ndx, fetInfo[ndx].resClassSD, fetInfo[ndx].resClassSub, fetInfo[ndx].defSubs); break; } default: fprintf(stderr, "Unrecognized flag: %s\n", argv[0]); goto usage; } *pargv = argv; *pargc = argc; return; usage: fprintf(stderr, "\ Usage: ext2sim [-a aliasfile] [-A] [-B] [-l labelfile] [-L]\n\ [-o simfile] [-J flat|hier] [-y cap_digits]\n\ [-j device:sdRclass[/subRclass]/defaultSubstrate]\n\ [-f mit|lbl|su] file\n\ or else see options to extcheck(1)\n"); exit (1); } /* * ---------------------------------------------------------------------------- * * GetNode -- * * function to find a node given its hierarchical prefix and suffix * * Results: * a pointer to the node struct or NULL * * ---------------------------------------------------------------------------- */ EFNode * GetNode(prefix, suffix) HierName *prefix; HierName *suffix; { HashEntry *he; he = EFHNConcatLook(prefix, suffix, "output"); return(((EFNodeName *) HashGetValue(he))->efnn_node); } /* * ---------------------------------------------------------------------------- * * fetVisit -- * * Procedure to output a single fet to the .sim file. * Called by EFVisitFets(). * * Results: * Returns 0 always. * * Side effects: * Writes to the file esSimF. * * Format of a .sim fet line: * * type gate source drain l w x y g= s= d= * * where * type is a name identifying this type of transistor * gate, source, and drain are the nodes to which these three * terminals connect * l, w are the length and width of the channel * x, y are the x, y coordinates of a point within the channel. * g=, s=, d= are the (optional) attributes; if present, each * is followed by a comma-separated list of attributes. * * ---------------------------------------------------------------------------- */ int fetVisit(fet, hierName, trans, l, w) Fet *fet; /* Fet being output */ HierName *hierName; /* Hierarchical path down to this fet */ Transform *trans; /* Coordinate transform for output */ int l, w; { FetTerm *gate, *source, *drain; EFNode *subnode, *snode, *dnode; int scale; Rect r; char name[12]; sprintf(name, "output"); /* If no terminals, can't do much of anything */ if (fet->fet_nterm < 2) return 0; /* If only two terminals, connect the source to the drain */ gate = &fet->fet_terms[0]; source = drain = &fet->fet_terms[1]; if (fet->fet_nterm >= 3) drain = &fet->fet_terms[2]; subnode = fet->fet_subsnode; /* Kludge for .sim: transistor types can only be one character */ fprintf(esSimF, "%c", EFFetTypes[fet->fet_type][0]); /* Output gate, source, and drain node names */ fetOutNode(hierName, gate->fterm_node->efnode_name->efnn_hier, name, esSimF); fetOutNode(hierName, source->fterm_node->efnode_name->efnn_hier, name, esSimF); /* Hack for BiCMOS---see scmos.tech27 hack: Tim, 7/16/96 */ if (EFFetTypes[fet->fet_type][0] == 'b') { sprintf(name, "fet"); fetOutNode(hierName, subnode->efnode_name->efnn_hier, name, esSimF); } else fetOutNode(hierName, drain->fterm_node->efnode_name->efnn_hier, name, esSimF); if ( esFormat == LBL ) { /* support gemini's substrate comparison */ putc(' ', esSimF); fetSubstrate(hierName, subnode->efnode_name->efnn_hier, fet->fet_type, 0, FALSE, esSimF); } GeoTransRect(trans, &fet->fet_rect, &r); scale = GeoScale(trans); if (EFFetTypes[fet->fet_type][0] == 'b') { /* Bipolar sim format: We don't have the length and width * of the collector well, but we can get it from the area * and perimeter measurements; hopefully any strict netlist * comparator will deal with any problem arising from * swapping L and W. */ int n; double cl, cw; double chp = 0.0; double ca = 0.0; for (n = 0; n < efNumResistClasses; n++) { ca += (double)(subnode->efnode_pa[n].pa_area); chp += 0.5 * (double)(subnode->efnode_pa[n].pa_perim); } cl = 0.5 * (chp + sqrt(chp * chp - 4 * ca)); cw = ca / cl; fprintf(esSimF, " %d %d %d %d", (int)cl, (int)cw, r.r_xbot, r.r_ybot); } else { /* * Scale L and W appropriately by the same amount as distance * values in the transform. The transform will have a scale * different from 1 only in the case when the scale factors of * some of the .ext files differed, making it necessary to scale * all dimensions explicitly instead of having a single scale * factor at the beginning of the .sim file. */ fprintf(esSimF, " %d %d %d %d", l*scale, w*scale, r.r_xbot, r.r_ybot); /* Attributes, if present */ if (!esNoAttrs) { bool subAP= FALSE, hierS = esHierAP, hierD = esHierAP; if (gate->fterm_attrs) fprintf(esSimF, " g=%s", gate->fterm_attrs); if ( esFormat == SU ) { if ( gate->fterm_attrs ) { subAP = Match(ATTR_SUBSAP, gate->fterm_attrs ) ; fprintf(esSimF, ","); } else fprintf(esSimF, " g="); fetSubstrate(hierName, subnode->efnode_name->efnn_hier, fet->fet_type, scale, subAP, esSimF); } if (source->fterm_attrs) { fprintf(esSimF, " s=%s", source->fterm_attrs); if ( Match(ATTR_HIERAP, source->fterm_attrs ) != FALSE ) hierS = TRUE ; else if ( Match(ATTR_FLATAP, source->fterm_attrs ) != FALSE ) hierS = FALSE ; } if ( esFormat == SU ) { fprintf(esSimF, "%s", (source->fterm_attrs) ? "," : " s=" ); if (hierS) nAPHier(source, hierName, fetInfo[fet->fet_type].resClassSD, scale, esSimF); else { snode= GetNode(hierName, source->fterm_node->efnode_name->efnn_hier); nAP(snode, fetInfo[fet->fet_type].resClassSD, scale, esSimF); } } if (drain->fterm_attrs) { fprintf(esSimF, " d=%s", drain->fterm_attrs); if ( Match(ATTR_HIERAP, drain->fterm_attrs ) != FALSE ) hierD = TRUE ; else if ( Match(ATTR_FLATAP, drain->fterm_attrs ) != FALSE ) hierD = FALSE ; } if ( esFormat == SU ) { fprintf(esSimF, "%s", (drain->fterm_attrs) ? "," : " d=" ); if (hierD) nAPHier(drain, hierName, fetInfo[fet->fet_type].resClassSD, scale, esSimF); else { dnode = GetNode(hierName, drain->fterm_node->efnode_name->efnn_hier); nAP(dnode, fetInfo[fet->fet_type].resClassSD, scale, esSimF); } } } } fprintf(esSimF, "\n"); return 0; } Void fetSubstrate( prefix, suffix, type, scale, doAP, outf) HierName *prefix; HierName *suffix; int type, scale; bool doAP; FILE *outf; { HashEntry *he; EFNodeName *nn; char *suf ; int l ; EFNode *subnode; suf = EFHNToStr(suffix); if (fetInfo[type].defSubs && strcasecmp(suf,fetInfo[type].defSubs) == 0) { l = strlen(suf) - 1; if ( ( EFTrimFlags & EF_TRIMGLOB ) && suf[l] =='!' || ( EFTrimFlags & EF_TRIMLOCAL ) && suf[l] == '#' ) suf[l] = '\0' ; if ( esFormat == SU ) fprintf(outf, "S_"); fprintf(outf, "%s", suf); } else { he = EFHNConcatLook(prefix, suffix, "substrate"); if (he == NULL) { (void) fprintf(outf, "errGnd!"); return; } /* Canonical name */ nn = (EFNodeName *) HashGetValue(he); subnode = nn->efnn_node; if ( esFormat == SU ) { if ( doAP ) { if ( fetInfo[type].resClassSub < 0 ) { fprintf(stderr, "error: subap for fettype %d required but not specified on command line\n", type); fprintf(outf,"A_0,P_0,"); } else { nAP(subnode, fetInfo[type].resClassSub, scale, outf); putc(',', outf); } } fprintf(outf, "S_"); } EFHNOut(nn->efnn_node->efnode_name->efnn_hier, outf); } } /* * ---------------------------------------------------------------------------- * * nAP, nAPHier -- * * Output the area perimeter of the node with type type if it has not * been visited. * The nAPHier version outputs the area and perimeter only within the * local subcell with hierarchical name hierName. * * Side effects: * Set the visited flags so that the node A/P will not be output multiple * times * * ---------------------------------------------------------------------------- */ int nAP(node, resClass, scale, outf) EFNode *node; int resClass, scale; FILE *outf; { int a, p; if ( node->efnode_client == (ClientData) NULL ) initNodeClient(node); if ( resClass == NO_RESCLASS || beenVisited((nodeClient *)node->efnode_client, resClass) ) { fprintf(outf,"A_0,P_0"); return FALSE; } markVisited((nodeClient *)node->efnode_client, resClass); a = node->efnode_pa[resClass].pa_area*scale*scale; p = node->efnode_pa[resClass].pa_perim*scale; if ( a < 0 ) a = 0; if ( p < 0 ) p = 0; fprintf(outf,"A_%d,P_%d", a, p); return TRUE; } int nAPHier(fterm, hierName, resClass, scale, outf) FetTerm *fterm; HierName *hierName; int resClass, scale; FILE *outf; { EFNode *node = fterm->fterm_node; nodeClientHier *nc ; int a, p; if ( node->efnode_client == (ClientData) NULL ) initNodeClientHier(node); nc = (nodeClientHier *)node->efnode_client; if ( nc->lastPrefix != hierName ) { nc->visitMask = 0; nc->lastPrefix = hierName; } if ( resClass == NO_RESCLASS || beenVisited((nodeClientHier *)node->efnode_client, resClass) ) { fprintf(outf,"A_0,P_0"); return FALSE; } markVisited((nodeClientHier *)node->efnode_client, resClass); a = node->efnode_pa[resClass].pa_area*scale*scale; p = node->efnode_pa[resClass].pa_perim*scale; if ( a < 0 ) a = 0; if ( p < 0 ) p = 0; fprintf(outf,"A_%d,P_%d", a, p); return TRUE; } /* * ---------------------------------------------------------------------------- * * fetOutNode -- * * Output the name of the node whose hierarchical prefix down to this * point is 'prefix' and whose name from the end of suffix down to the * leaves is 'suffix', just as in the arguments to EFHNConcat(). * * * Results: * None. * * Side effects: * Writes to the file 'outf'. * Sets the efnode_client field as described above. * * ---------------------------------------------------------------------------- */ Void fetOutNode(prefix, suffix, name, outf) HierName *prefix; HierName *suffix; char *name; FILE *outf; { HashEntry *he; EFNodeName *nn; he = EFHNConcatLook(prefix, suffix, name); if (he == NULL) { (void) fprintf(outf, " GND"); return; } /* Canonical name */ nn = (EFNodeName *) HashGetValue(he); (void) putc(' ', outf); EFHNOut(nn->efnn_node->efnode_name->efnn_hier, outf); if ( nn->efnn_node->efnode_client == (ClientData) NULL ) initNodeClient(nn->efnn_node); } /* * ---------------------------------------------------------------------------- * * capVisit -- * * Procedure to output a single capacitor to the .sim file. * Called by EFVisitCaps(). * * Results: * Returns 0 always. * * Side effects: * Writes to the file esSimF. * * Format of a .sim cap line: * * C node1 node2 cap * * where * node1, node2 are the terminals of the capacitor * cap is the capacitance in femtofarads (NOT attofarads). * * ---------------------------------------------------------------------------- */ int capVisit(hierName1, hierName2, cap) HierName *hierName1; HierName *hierName2; double cap; { cap = cap / 1000; if (cap <= EFCapThreshold) return 0; (void) fprintf(esSimF, "C "); EFHNOut(hierName1, esSimF); (void) fprintf(esSimF, " "); EFHNOut(hierName2, esSimF); (void) fprintf(esSimF, esCapFormat, cap); return 0; } /* * ---------------------------------------------------------------------------- * * resistVisit -- * * Procedure to output a single resistor to the .sim file. * Called by EFVisitResists(). * * Results: * Returns 0 always. * * Side effects: * Writes to the file esSimF. * * Format of a .sim resistor line: * * r node1 node2 res * * where * node1, node2 are the terminals of the resistor * res is the resistance in ohms (NOT milliohms) * * * ---------------------------------------------------------------------------- */ int resistVisit(hierName1, hierName2, res) HierName *hierName1; HierName *hierName2; int res; { res = (res+ 500)/1000; (void) fprintf(esSimF, "r "); EFHNOut(hierName1, esSimF); (void) fprintf(esSimF, " "); EFHNOut(hierName2, esSimF); (void) fprintf(esSimF, " %d\n", res); return 0; } /* * ---------------------------------------------------------------------------- * * nodeVisit -- * * Procedure to output a single node to the .sim file, along with * its aliases to the .al file and its location to the .nodes file. * Called by EFVisitNodes(). * * Results: * Returns 0 always. * * Side effects: * Writes to the files esSimF, esAliasF, and esLabF. * * ---------------------------------------------------------------------------- */ int nodeVisit(node, res, cap) register EFNode *node; int res; double cap; { register EFNodeName *nn; HierName *hierName; bool isGlob; char *fmt; EFAttr *ap; if (esFetNodesOnly && node->efnode_client == (ClientData) NULL) return 0; hierName = (HierName *) node->efnode_name->efnn_hier; cap = cap / 1000; res = (res + 500) / 1000; if (cap > EFCapThreshold) { (void) fprintf(esSimF, "C "); EFHNOut(hierName, esSimF); (void) fprintf(esSimF, esCapFormat, cap); } if (res > EFResistThreshold) { (void) fprintf(esSimF, "R "); EFHNOut(hierName, esSimF); (void) fprintf(esSimF, " %d\n", res); } if (node->efnode_attrs && !esNoAttrs) { (void) fprintf(esSimF, "A "); EFHNOut(hierName, esSimF); for (fmt = " %s", ap = node->efnode_attrs; ap; ap = ap->efa_next) { (void) fprintf(esSimF, fmt, ap->efa_text); fmt = ",%s"; } putc('\n', esSimF); } if (esAliasF) { isGlob = EFHNIsGlob(hierName); for (nn = node->efnode_name->efnn_next; nn; nn = nn->efnn_next) { if (isGlob && EFHNIsGlob(nn->efnn_hier)) continue; (void) fprintf(esAliasF, "= "); EFHNOut(hierName, esAliasF); (void) fprintf(esAliasF, " "); EFHNOut(nn->efnn_hier, esAliasF); (void) fprintf(esAliasF, "\n"); } } if (esLabF) { (void) fprintf(esLabF, "94 "); EFHNOut(hierName, esLabF); (void) fprintf(esLabF, " %d %d %s;\n", node->efnode_loc.r_xbot, node->efnode_loc.r_ybot, EFLayerNames[node->efnode_type]); } return 0; }