/* * ext2spice.c -- * * Program to flatten hierarchical .ext files and produce * a .spice file, suitable for use as input to spiceulators * such as spice and hspice. * * 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.spice, unless '-o esSpiceFile' is specified, in which case the * output is left in 'esSpiceFile'. * */ #ifndef lint static char rcsid[] = "$Header: /ufs/repository/magic/ext2spice/ext2spice.c,v 1.4 2001/12/04 21:44:14 tim Exp $"; #endif #include #include #include #include "misc/magic.h" #include "utils/malloc.h" #include "utils/geometry.h" #include "utils/hash.h" #include "utils/dqueue.h" #include "utils/utils.h" #include "extflat/extflat.h" #include "utils/runstats.h" /* this is temporary - should move efNumResistClasses to extflat.h */ #include "../extflat/EFint.h" /* Forward declarations */ int mainArgs(); int capVisit(), fetVisit(), nodeVisit(), resistVisit(), fetMergeVisit(), fetDistJunctVisit(); EFNode *fetSubstrate(); char *nodeSpiceName(); /* Options specific to ext2spice */ bool esFetNodesOnly = FALSE; bool esNoAttrs = FALSE; bool esHierAP = FALSE; char esDefaultOut[FNSIZE]; int esCapAccuracy = 1; char esCapFormat[FNSIZE]; char *esOutName = esDefaultOut; FILE *esSpiceF = NULL; float esScale = -1.0 ; /* negative if hspice the EFScale/100 otherwise */ #define SPICE2 0 #define SPICE3 1 #define HSPICE 2 static unsigned short esFormat = HSPICE ; int esCapNum = 0, esFetNum = 1000, esResNum = 0; int esNodeNum = 10; /* just in case we' re extracting spice2 */ int esSbckNum = 0; /* used in hspice node name shortening */ /* * The following hash table and associated functions are used only if * the format is hspice to keep the translation between the hierarchical * prefix of a node and the xnumthat we use to output valid hspice * which also are meaningfull. */ HashTable subcktNameTable ; /* the hash table itself */ DQueue subcktNameQueue ; /* q used to print it sorted at the end*/ #define MAX_STR_SIZE (1<<11) /* 2K should be enough for keeping temp names even of the most complicated design */ 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]; /* Node clients for figuring out areas and perimeters of sources and drains */ typedef struct { long _duml:MAXFETTYPES; } _dum; /* if you get an error here you should change the data structures and visitMask */ typedef union { long visitMask; /* mask for normal visits */ float *widths; /* width used for distributing area perim */ } maskOrWidth ; typedef struct { char *spiceNodeName; maskOrWidth m_w; } nodeClient; typedef struct { HierName *lastPrefix; maskOrWidth m_w; } nodeClientHier; #define NO_RESCLASS -1 #define markVisited(client, rclass) \ { (client)->m_w.visitMask |= (1<m_w.visitMask = (long)0; } #define beenVisited(client, rclass) \ ( (client)->m_w.visitMask & (1<efnode_client,sizeof(nodeClient)); \ (( nodeClient *)(node)->efnode_client)->spiceNodeName = NULL; \ (( nodeClient *)(node)->efnode_client)->m_w.visitMask = (unsigned long)initMask; \ } #define initNodeClientHier(node) \ { \ MALLOC(ClientData,(node)->efnode_client,sizeof(nodeClientHier)); \ ((nodeClientHier *) (node)->efnode_client)->m_w.visitMask = (unsigned 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]*" bool esMergeFetsA = FALSE; /* aggresive merging of fets L1=L2 merge them */ bool esMergeFetsC = FALSE; /* conservative merging of fets L1=L2 and W1=W2 */ /* used with the hspice multiplier */ bool esDistrJunct = FALSE; /* *--------------------------------------------------------- * Variables & macros used for merging parallel fets * The merging of fets is based on the fact that fetVisit * visits the fets in the same order all the time so the * value of esFMult[i] keeps the multiplier for the ith fet *--------------------------------------------------------- */ #define FET_KILLED ((float) -1.0) #define FMULT_SIZE (1<<10) static float *esFMult = NULL; /* the array itself */ static int esFMIndex = 0; /* current index to it */ static int esFMSize = FMULT_SIZE ; /* its current size (growable) */ static int esFetsMerged = 0; #define fetIsKilled(n) ( esFMult[(n)] <=(float)0.0 ) #define FET_KILLED ((float) -1.0) int nodeVisitDebug(); /* macro to add a fet's multiplier to the table and grow it if necessary */ #define addFetMult(f) \ { \ if ( esFMult == NULL ) { \ MALLOC(float *, esFMult, esFMSize*sizeof(float)); \ } else if ( esFMIndex >= esFMSize ) { \ int i; \ float *op = esFMult ; \ MALLOC(float *, esFMult, (esFMSize = esFMSize*2)*sizeof(float)); \ for ( i = 0 ; i < esFMSize/2 ; i++ ) esFMult[i] = op[i]; \ if (op) FREE(op); \ } \ esFMult[esFMIndex++] = (float)(f); \ } #define setFetMult(i,f) { esFMult[(i)] = (float)(f); } #define getCurFetMult() ((esFMult) ? esFMult[esFMIndex-1] : (float)1.0) /* cache list used to find parallel fets */ typedef struct _fetMerge { int l, w; EFNode *g, *s, *d, *b; Fet * fet; int esFMIndex; HierName *hierName; struct _fetMerge *next; } fetMerge; fetMerge *fetMergeList = NULL ; /* * ---------------------------------------------------------------------------- * * main -- * * Top level of ext2spice. * * ---------------------------------------------------------------------------- */ main(argc, argv) char *argv[]; { int i,flatFlags; char *inName; FILE *f; EFInit(); EFResistThreshold = INFINITE_THRESHOLD ; /* 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 name inName.spice if they weren't * explicitly specified */ if (esOutName == esDefaultOut) sprintf(esDefaultOut, "%s.spice", inName); if ((esSpiceF = fopen(esOutName, "w")) == NULL) { perror(esOutName); exit (1); } /* Read the hierarchical description of the input circuit */ EFReadFile(inName); fprintf(esSpiceF, "* %s file created from %s.ext - technology: %s\n\n", (esFormat == SPICE2) ? "SPICE2" : ( (esFormat == SPICE3) ? "SPICE3" : "HSPICE" ), inName, EFTech); if ( esFormat == HSPICE ) fprintf(esSpiceF,".option scale=%gu\n\n", ((float) EFScale)/100.0); else esScale = EFScale/100.0; /* Convert the hierarchical description to a flat one */ flatFlags = EF_FLATNODES; EFTrimFlags |= EF_TRIMGLOB ; if (EFCapThreshold < INFINITE_THRESHOLD) flatFlags |= EF_FLATCAPS; if (esFormat == HSPICE ) { EFTrimFlags |= EF_TRIMLOCAL ; HashInit(&subcktNameTable, 32, HT_STRINGKEYS); #ifndef UNSORTED_SUBCKT DQInit(&subcktNameQueue, 64); #endif } EFFlatBuild(inName, flatFlags); initMask = ( esDistrJunct ) ? (unsigned long)0 : FET_CONNECT_MASK ; if ( esMergeFetsA || esMergeFetsC ) { EFVisitFets(fetMergeVisit, (ClientData) NULL); printf("Fets merged: %d\n", esFetsMerged); esFMIndex = 0 ; #ifdef free_all_mem { register fetMerge *p; for ( p = fetMergeList ; p != NULL ; p=p->next ) FREE(p); } #endif } else if ( esDistrJunct ) EFVisitFets(fetDistJunctVisit, (ClientData) NULL); EFVisitFets(fetVisit, (ClientData) NULL); initMask = (unsigned long) 0; if (flatFlags & EF_FLATCAPS) { (void) sprintf( esCapFormat, "C%%d %%s %%s %%.%dlffF\n",esCapAccuracy); EFVisitCaps(capVisit, (ClientData) NULL); } EFVisitResists(resistVisit, (ClientData) NULL); (void) sprintf( esCapFormat, "C%%d %%s GND %%.%dlffF%%s", esCapAccuracy); EFVisitNodes(nodeVisit, (ClientData) NULL); if ( esFormat == HSPICE ) printSubcktDict(); #ifdef free_all_mem EFFlatDone(); EFDone(); #endif printf("Memory used: %s\n", RunStats(RS_MEM, NULL, NULL)); exit (0); } /* * ---------------------------------------------------------------------------- * * mainArgs -- * * Process those arguments that are specific to ext2spice. * 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 'd': esDistrJunct = TRUE; break; case 'M': esMergeFetsA = TRUE; break; case 'm': esMergeFetsC = TRUE; break; case 'B': esNoAttrs = TRUE; break; case 'F': esFetNodesOnly = TRUE; 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,"SPICE2") == 0 ) esFormat = SPICE2 ; else if ( strcasecmp(ftmp,"SPICE3") == 0 ) esFormat = SPICE3 ; else if ( strcasecmp(ftmp,"HSPICE") == 0 ) esFormat = HSPICE ; else goto usage; 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 'y': { char *t; if (( t = ArgStr(&argc, &argv, "cap-accuracy") ) == NULL) goto usage; esCapAccuracy = atoi(t); break; } case 'j': { char *rp, subsNode[80] ; int ndx, rClass, rClassSub = -1; 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 = -1 ; if ( sscanf(rp, "%d/%s", &rClass, subsNode) != 2 ) goto usage; } ndx = efBuildAddStr(EFFetTypes, &EFFetNumTypes, MAXFETTYPES, cp); fetInfo[ndx].resClassSD = rClass; fetInfo[ndx].resClassSub = rClassSub; if ( ((1<efnn_node); } /* * ---------------------------------------------------------------------------- * * extHierSDAttr -- * Check if the attribute of the argument fet_terminal or the global * settings are such that we want a hierarchical extraction of its S/D * * * Results: * TRUE or FALSE * * Side effects: * None. * * ---------------------------------------------------------------------------- */ bool extHierSDAttr(term) FetTerm *term; { bool r = esHierAP; if (term->fterm_attrs) { if ( Match(ATTR_HIERAP, term->fterm_attrs ) != FALSE ) r = TRUE ; else if ( Match(ATTR_FLATAP, term->fterm_attrs ) != FALSE ) r = FALSE ; } return r; } /* * ---------------------------------------------------------------------------- * * fetVisit -- * * Procedure to output a single fet to the .spice file. * Called by EFVisitFets(). * * Results: * Returns 0 always. * * Side effects: * Writes to the file esSpiceF. * * Format of a .spice fet line: * * M%d drain gate source substrate type w=w l=l * x y * + ad= pd= as= ps= * asub= psub= * **fetattr g= s= d= * * where * type is a name identifying this type of transistor * other types of transistors are extracted with * an M card but it should be easy to turn them to whatever * you want. * 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, *subnodeFlat; int scale; Rect r; bool subAP= FALSE, hierS, hierD, extHierSDAttr() ; float sdM ; char name[12], *dotptr, devchar; sprintf(name, "output"); /* If no terminals, can't do much of anything */ if (fet->fet_nterm < 2 ) return 0; if ( (esMergeFetsA || esMergeFetsC) && fetIsKilled(esFMIndex++) ) 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; /* Output drain, gate, source, and substrate node names and fet type */ /* Method for generating devices at subcircuit entries */ if (((dotptr = strrchr(EFFetTypes[fet->fet_type], '.')) != NULL) && !strcmp(dotptr, ".subckt")) { *dotptr = '\0'; devchar = 'x'; /* use a subcircuit */ } else devchar = 'm'; /* Hack for BiCMOS, Tim 10/4/97 */ if (!strcmp(EFFetTypes[fet->fet_type], "npn")) { if (devchar == 'm') devchar = 'q'; /* default device name */ sprintf(name, "fet"); if (gate->fterm_attrs) (void) fprintf(esSpiceF, "%c%s", devchar, gate->fterm_attrs); else (void) fprintf(esSpiceF, "%c%d", devchar, esFetNum++); fetOutNode(hierName, subnode->efnode_name->efnn_hier, name, esSpiceF); } else { if (gate->fterm_attrs) (void) fprintf(esSpiceF, "%c%s", devchar, gate->fterm_attrs); else (void) fprintf(esSpiceF, "%c%d", devchar, esFetNum++); fetOutNode(hierName, drain->fterm_node->efnode_name->efnn_hier, name, esSpiceF); } fetOutNode(hierName, gate->fterm_node->efnode_name->efnn_hier, name, esSpiceF); /* fix mixed up drain/source for bjts hace 2/2/99 */ if (!strcmp(EFFetTypes[fet->fet_type], "npn") && gate->fterm_node->efnode_name->efnn_hier == source->fterm_node->efnode_name->efnn_hier) fetOutNode(hierName, drain->fterm_node->efnode_name->efnn_hier, name, esSpiceF); else fetOutNode(hierName, source->fterm_node->efnode_name->efnn_hier, name, esSpiceF); putc(' ', esSpiceF); /* * 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 .spice file. */ GeoTransRect(trans, &fet->fet_rect, &r); scale = GeoScale(trans); /* hace don't print bjt substrate nodes */ if (strcmp(EFFetTypes[fet->fet_type], "npn")) subnodeFlat = fetSubstrate(hierName, subnode->efnode_name->efnn_hier, fet->fet_type, esSpiceF); fprintf(esSpiceF, " %s", EFFetTypes[fet->fet_type]); sdM = getCurFetMult(); /* don't output width, length for npn bipolars -- Tim 10/9/97 */ if (strcmp(EFFetTypes[fet->fet_type], "npn")) { if ( esFormat == HSPICE ) { fprintf(esSpiceF, " w=%d l=%d", w*scale, l*scale); if ( sdM != 1.0 ) fprintf(esSpiceF, " M=%g", sdM); } else { fprintf(esSpiceF, " w=%gu l=%gu", (float)w*scale*esScale*sdM, (float)l*scale*esScale); sdM = 1.0; } /* * Check controling attributes and output area and perimeter. */ hierS = extHierSDAttr(source); hierD = extHierSDAttr(drain); if ( gate->fterm_attrs ) subAP = Match(ATTR_SUBSAP, gate->fterm_attrs ) ; fprintf(esSpiceF, "\n+ "); if (hierD) nAPHier(drain, hierName, fetInfo[fet->fet_type].resClassSD, scale, "d", sdM, esSpiceF); else { dnode = GetNode(hierName, drain->fterm_node->efnode_name->efnn_hier); nAP(dnode, fetInfo[fet->fet_type].resClassSD, scale,"d",sdM,esSpiceF,w); } if (hierS) nAPHier(source, hierName, fetInfo[fet->fet_type].resClassSD, scale, "s", sdM, esSpiceF); else { snode= GetNode(hierName, source->fterm_node->efnode_name->efnn_hier); nAP(snode, fetInfo[fet->fet_type].resClassSD, scale,"s",sdM, esSpiceF,w); } if ( subAP ) { fprintf(esSpiceF, " * "); if ( fetInfo[fet->fet_type].resClassSub < 0 ) { fprintf(stderr, "error: subap for fettype %d unspecified\n", fet->fet_type); fprintf(esSpiceF,"asub=0 psub=0"); } else if ( subnodeFlat ) nAP(subnodeFlat, fetInfo[fet->fet_type].resClassSub, scale, "sub", sdM, esSpiceF, -1); else fprintf(esSpiceF,"asub=0 psub=0"); } /* Now output attributes, if present */ if (!esNoAttrs) { if ( gate->fterm_attrs || source->fterm_attrs || drain->fterm_attrs) fprintf(esSpiceF,"\n**fetattr"); if (gate->fterm_attrs) (void) fprintf(esSpiceF, " g=%s", gate->fterm_attrs); if (source->fterm_attrs) (void) fprintf(esSpiceF, " s=%s", source->fterm_attrs); if (drain->fterm_attrs) (void) fprintf(esSpiceF, " d=%s", drain->fterm_attrs); } } fprintf(esSpiceF, "\n"); if (dotptr != NULL) *dotptr = '.'; return 0; } /* * ---------------------------------------------------------------------------- * * fetSubstrate - * * Output the node name of the substrate of a fet. If the suffix is the * same as the default dont go looking for it just output the default * (trimmed appropriately). Otherwise look it up .. * * Results: * NULL if not found or the default substrate or the node pointer * otherwise (might be reused to output area and perimeter of * the substrate). * * Side effects: * Might allocate the nodeClient for the node through nodeSpiceName. * * ---------------------------------------------------------------------------- */ EFNode *fetSubstrate( prefix, suffix, type, outf) HierName *prefix, *suffix; int type; FILE *outf; { HashEntry *he; EFNodeName *nn; char *suf ; int l ; suf = EFHNToStr(suffix); if (fetInfo[type].defSubs && strcasecmp(suf,fetInfo[type].defSubs) == 0) { if ( outf ) { l = strlen(suf) - 1; if ( ( EFTrimFlags & EF_TRIMGLOB ) && suf[l] =='!' || ( EFTrimFlags & EF_TRIMLOCAL ) && suf[l] == '#' ) suf[l] = '\0' ; fprintf(outf, "%s", suf); } return NULL; } else { he = EFHNConcatLook(prefix, suffix, "substrate"); if (he == NULL) { if (outf) fprintf(outf, "errGnd!"); return NULL; } /* Canonical name */ nn = (EFNodeName *) HashGetValue(he); if (outf) fprintf(outf, nodeSpiceName(nn->efnn_node->efnode_name->efnn_hier)); return nn->efnn_node; } } /* * ---------------------------------------------------------------------------- * * 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 * * ---------------------------------------------------------------------------- */ Void nAP(node, resClass, scale, sterm, m, outf, w) EFNode *node; int resClass, scale; char *sterm; float m; FILE *outf; int w; { char fmt[30]; float dsc ; if ( node->efnode_client == NULL ) { fprintf(stderr,"nAP: major internal inconsistency\n"); } if ( esFormat == HSPICE ) sprintf(fmt,"a%s=%%g p%s=%%g ", sterm, sterm); else sprintf(fmt,"a%s=%%gp p%s=%%gu ", sterm, sterm); if ( !esDistrJunct || w == -1 ) goto oldFmt; dsc = w/((nodeClient*)node->efnode_client)->m_w.widths[resClass]; if ( esFormat == HSPICE ) fprintf(outf,fmt, node->efnode_pa[resClass].pa_area*scale*scale*dsc, node->efnode_pa[resClass].pa_perim*scale*dsc); else fprintf(outf,fmt, ((float)node->efnode_pa[resClass].pa_area*scale*scale) *esScale*esScale*dsc, ((float)node->efnode_pa[resClass].pa_perim*scale)*esScale*dsc); return; oldFmt: if ( resClass == NO_RESCLASS || beenVisited((nodeClient *)node->efnode_client, resClass) ) scale = 0 ; else markVisited((nodeClient *)node->efnode_client, resClass); if ( esFormat == HSPICE ) fprintf(outf,fmt, node->efnode_pa[resClass].pa_area*scale*scale/m, node->efnode_pa[resClass].pa_perim*scale/m); else fprintf(outf,fmt, ((float)node->efnode_pa[resClass].pa_area*scale*scale)*esScale*esScale, ((float)node->efnode_pa[resClass].pa_perim*scale)*esScale); } Void nAPHier(fterm, hierName, resClass, scale, sterm, m, outf) FetTerm *fterm; HierName *hierName; int resClass, scale; float m; char *sterm; FILE *outf; { EFNode *node = fterm->fterm_node; nodeClientHier *nc ; char fmt[30]; if ( esFormat == HSPICE ) sprintf(fmt,"a%s=%%g p%s=%%g ", sterm, sterm); else sprintf(fmt,"a%s=%%gp p%s=%%gu ", sterm, sterm); if ( node->efnode_client == (ClientData) NULL ) if ( node->efnode_client == (ClientData) NULL ) initNodeClientHier(node); nc = (nodeClientHier *)node->efnode_client; if ( nc->lastPrefix != hierName ) { clearVisited(nc); nc->lastPrefix = hierName; } if (resClass == NO_RESCLASS || beenVisited((nodeClientHier *)node->efnode_client, resClass) ) scale = 0; else markVisited((nodeClientHier *)node->efnode_client, resClass); if ( esFormat == HSPICE ) fprintf(outf,fmt, node->efnode_pa[resClass].pa_area*scale*scale/m, node->efnode_pa[resClass].pa_perim*scale/m); else fprintf(outf,fmt, ((float)node->efnode_pa[resClass].pa_area*scale)*esScale*esScale, ((float)node->efnode_pa[resClass].pa_perim*scale)*esScale); } /* * ---------------------------------------------------------------------------- * * 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, " errGnd!"); return; } nn = (EFNodeName *) HashGetValue(he); fprintf(outf, " %s", nodeSpiceName(nn->efnn_node->efnode_name->efnn_hier)); } /* * ---------------------------------------------------------------------------- * * capVisit -- * * Procedure to output a single capacitor to the .spice file. * Called by EFVisitCaps(). * * Results: * Returns 0 always. * * Side effects: * Writes to the file esSpiceF. Increments esCapNum. * * Format of a .spice cap line: * * C%d 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; fprintf(esSpiceF, esCapFormat ,esCapNum++,nodeSpiceName(hierName1), nodeSpiceName(hierName2), cap); return 0; } /* * ---------------------------------------------------------------------------- * * resistVisit -- * * Procedure to output a single resistor to the .spice file. * Called by EFVisitResists(). * * Results: * Returns 0 always. * * Side effects: * Writes to the file esSpiceF. Increments esResNum. * * Format of a .spice resistor line: * * R%d 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; fprintf(esSpiceF, "R%d %s %s %d\n", esResNum++, nodeSpiceName(hierName1), nodeSpiceName(hierName2), res); return 0; } /* * ---------------------------------------------------------------------------- * * nodeVisit -- * * Procedure to output a single node to the .spice file along with its * attributes and its dictionary (if present). Called by EFVisitNodes(). * * Results: * Returns 0 always. * * Side effects: * Writes to the files esSpiceF * * ---------------------------------------------------------------------------- */ int nodeVisit(node, res, cap) register EFNode *node; int res; double cap; { register EFNodeName *nn; HierName *hierName; bool isConnected = FALSE; char *fmt, *nsn; EFAttr *ap; if ( node->efnode_client ) isConnected = ( esDistrJunct ) ? (((nodeClient *)node->efnode_client)->m_w.widths != NULL) : (((nodeClient *)node->efnode_client)->m_w.visitMask&FET_CONNECT_MASK); if ( !isConnected && esFetNodesOnly) return 0; hierName = (HierName *) node->efnode_name->efnn_hier; nsn = nodeSpiceName(hierName); if (esFormat == SPICE2 || esFormat == HSPICE && strncmp(nsn, "z@", 2)==0 ) { static char ntmp[MAX_STR_SIZE]; EFHNSprintf(ntmp, hierName); fprintf(esSpiceF, "** %s == %s\n", ntmp, nsn); } cap = cap / 1000; if (cap > EFCapThreshold) { fprintf(esSpiceF, esCapFormat, esCapNum++, nsn, cap, (isConnected) ? "\n" : " **FLOATING\n"); } if (node->efnode_attrs && !esNoAttrs) { (void) fprintf(esSpiceF, "**nodeattr %s :",nsn ); for (fmt = " %s", ap = node->efnode_attrs; ap; ap = ap->efa_next) { (void) fprintf(esSpiceF, fmt, ap->efa_text); fmt = ",%s"; } putc('\n', esSpiceF); } return 0; } /* a debugging procedure */ int nodeVisitDebug(node, res, cap) register EFNode *node; int res; double cap; { register EFNodeName *nn; HierName *hierName; bool isConnected; char *fmt, *nsn; EFAttr *ap; hierName = (HierName *) node->efnode_name->efnn_hier; nsn = nodeSpiceName(hierName); fprintf(stderr, "** %s (%x)\n", nsn, node); printf("\t client.name=%s, client.m_w=%x\n", ((nodeClient *)node->efnode_client)->spiceNodeName, ((nodeClient *)node->efnode_client)->m_w.widths); return 0; } /* * ---------------------------------------------------------------------------- * * nodeSpiceName -- * Find the real spice name for the node with hierarchical name hname. * SPICE2 ==> numeric * SPICE3 ==> full magic path * HSPICE ==> less than 15 characters long * * Results: * Returns the spice node name. * * Side effects: * Allocates nodeClients for the node. * * ---------------------------------------------------------------------------- */ static char esTempName[MAX_STR_SIZE]; char *nodeSpiceName(hname) HierName *hname; { EFNodeName *nn; HashEntry *he; EFNode *node; he = EFHNLook(hname, (char *) NULL, "nodeName"); if ( he == NULL ) return "errGnd!"; nn = (EFNodeName *) HashGetValue(he); node = nn->efnn_node; if ( (nodeClient *) (node->efnode_client) == NULL ) { initNodeClient(node); goto makeName; } else if ( ((nodeClient *) (node->efnode_client))->spiceNodeName == NULL) goto makeName; else goto retName; makeName: if ( esFormat == SPICE2 ) sprintf(esTempName, "%d", esNodeNum++); else { EFHNSprintf(esTempName, node->efnode_name->efnn_hier); if ( esFormat == HSPICE ) /* more processing */ nodeHspiceName(esTempName); } ((nodeClient *) (node->efnode_client))->spiceNodeName = StrDup(NULL, esTempName); retName: return ((nodeClient *) (node->efnode_client))->spiceNodeName; } /* * ---------------------------------------------------------------------------- * * EFHNSprintf -- * * Create a hierarchical node name. * The flags in EFTrimFlags control whether global (!) or local (#) * suffixes are to be trimmed. Also substitutes \. with \@ if the * format is hspice. * * Results: * None. * * Side effects: * Changes the area pointed to by str * * ---------------------------------------------------------------------------- */ Void EFHNSprintf(str, hierName) register char *str; HierName *hierName; { bool trimGlob, trimLocal; register char *s, *cp, c; char *efHNSprintfPrefix(); s = str; if (hierName->hn_parent) str = efHNSprintfPrefix(hierName->hn_parent, str); if (EFTrimFlags) { cp = hierName->hn_name; trimGlob = (EFTrimFlags & EF_TRIMGLOB); trimLocal = (EFTrimFlags & EF_TRIMLOCAL); while (c = *cp++) { switch (c) { case '!': if (!trimGlob) *str++ = c; break; case '.': *str++ = (esFormat == HSPICE)?'@':'.'; break; case '#': if (trimLocal) break; default: *str++ = c; break; } } *str++ = '\0'; } else strcpy(str, hierName->hn_name); } char *efHNSprintfPrefix(hierName, str) register HierName *hierName; register char *str; { register char *cp, c; if (hierName->hn_parent) str = efHNSprintfPrefix(hierName->hn_parent, str); cp = hierName->hn_name; while (*str++ = *cp++) ; *(--str) = '/'; return ++str; } /* * ---------------------------------------------------------------------------- * * nodeHspiceName -- * * Convert the hierarchical node name used in Berkeley spice * to a name understodd by hspice and hopefully by the user. * * Results: * A somewhat meaningfull node name * * Side effects: * Mucks with the hash table above. * * ---------------------------------------------------------------------------- */ Void nodeHspiceName(s) char *s; { register char *p, *sf; int l, snum=-1 ; HashEntry *he; static char map[MAX_STR_SIZE]; /* * find the suffix */ l = strlen(s); for ( p = s+l ; (p > s) && *p != '/' ; p-- ) ; if ( p == s ) { sprintf(map, s); goto topLevel; } /* * break it into prefix '/0' suffix */ if ( *p == '/' ) *p = 0; sf = p+1; /* * look up prefix in the hash table ad create it if doesnt exist */ if ( (he = HashLookOnly(&subcktNameTable, s)) == NULL ) { snum = esSbckNum++; he = HashFind(&subcktNameTable, s); HashSetValue(he, (ClientData) snum); #ifndef UNSORTED_SUBCKT DQPushRear(&subcktNameQueue, he); #endif } else snum = (int) HashGetValue(he); sprintf(map, "x%d/%s", snum, sf); topLevel: strcpy(s, map); if ( strlen(s) > 15 ) { /* still hspice does not get it */ sprintf(s, "z@%d", esNodeNum++); if ( strlen(s) > 15 ) { /* screw it: hspice will not work */ fprintf(stderr,"error: too many nodes in this circuit to be output as names\n"); fprintf(stderr," use spice2 format or call and complain to Meta software about their stupid parser\n"); exit(1); } } } /* * ---------------------------------------------------------------------------- * * printSubcktDict -- * * Print out the hspice subcircuit dictionary. Ideally this should go to a * pa0 file but uncfortunately hspice crashes if the node names contain * dots so we just append it at the end of the spice file * * Results: * None. * * Side effects: * Writes to the output file * * ---------------------------------------------------------------------------- */ Void printSubcktDict() { HashSearch hs; HashEntry *he; fprintf(esSpiceF,"\n** hspice subcircuit dictionary\n"); #ifndef UNSORTED_SUBCKT while ( (he = ( HashEntry *) DQPopFront(&subcktNameQueue) ) != NULL ) #else HashStartSearch(&hs); while ( (he = HashNext(&subcktNameTable, &hs)) != NULL ) #endif fprintf(esSpiceF,"* x%d\t%s\n", HashGetValue(he), he->h_key.h_name); } /* * ---------------------------------------------------------------------------- * * mkFetMerge -- * Create a new fetMerge structure. * * Results: * Obvious * * Side effects: * Allocates memory and sets the fields of the structure. * * ---------------------------------------------------------------------------- */ fetMerge *mkFetMerge(l, w, g, s, d, b, hn, fet) int l, w; EFNode *g, *s, *d, *b; HierName *hn; Fet *fet; { register fetMerge *fp; MALLOC (fetMerge *, fp, sizeof(fetMerge)); fp->l = l; fp->w = w; fp->g = g; fp->s = s; fp->d = d; fp->b = b; fp->fet = fet; fp->esFMIndex = esFMIndex; fp->hierName = hn; fp->next = NULL; addFetMult(1.0); return fp; } /* * ---------------------------------------------------------------------------- * * parallelFets -- * Macro to look if two fets are in parallel * * Results: * NOT_PARALLEL if not in parallel * PARALLEL if s==s d==d and g==g and bulk=bulk * FLIP_PARALLEL if s==d d==s --->>---------------- * * Side effects: * None. * * ---------------------------------------------------------------------------- */ #define NOT_PARALLEL 0 #define PARALLEL 1 #define PARALLEL_R 2 #define parallelFets(f1, f2) \ ( \ ( (f1)->g == (f2)->g && (f1)->b == (f2)->b && (f1)->l == (f2)->l && \ ( esMergeFetsA || (f1)->w == (f2)->w ) ) ? \ ( ((f1)->d == (f2)->d && (f1)->s == (f2)->s ) ? \ PARALLEL : \ (((f1)->s == (f2)->d && (f1)->d == (f2)->s ) ? PARALLEL_R : NOT_PARALLEL) )\ : NOT_PARALLEL \ ) /* * ---------------------------------------------------------------------------- * * mergeAttr -- * Macro to merge two attribute strings * * Results: * The merged strings * * Side effects: * Might allocate and free memory. * * ---------------------------------------------------------------------------- */ #define mergeAttr(a1, a2) { \ if ( a1 == NULL ) a1 = a2 ; \ else { \ char *t; \ int l1 = strlen(a1); \ int l2 = strlen(a2); \ MALLOC(char *, t, (l1+l2)+1); \ t = (char *) strcat(a1,a2); \ FREE(a1); \ a1 = t ; \ } \ } /* * ---------------------------------------------------------------------------- * * fetMergeVisit -- * Visits each fet throu EFVisitFets and finds if it is in parallel with * any previously visited fet. * * Results: * 0 always to keep the caller going. * * Side effects: * Numerous. * * ---------------------------------------------------------------------------- */ int fetMergeVisit(fet, hierName, trans, l, w) Fet *fet; /* Fet to examine */ HierName *hierName; /* Hierarchical path down to this fet */ Transform *trans; /* Coordinate transform not used */ int l, w; { FetTerm *gate, *source, *drain; Fet *cf; FetTerm *cg, *cs, *cd; EFNode *subnode, *snode, *dnode, *gnode; int scale, pmode; bool hS, hD, chS, chD; Rect r; register fetMerge *fp, *cfp; float m; if (esDistrJunct) fetDistJunctVisit(fet, hierName, trans, l, w); if (fet->fet_nterm < 2) { fprintf(stderr, "outPremature\n"); return 0; } gate = &fet->fet_terms[0]; source = drain = &fet->fet_terms[1]; if (fet->fet_nterm >= 3) drain = &fet->fet_terms[2]; gnode = GetNode (hierName, gate->fterm_node->efnode_name->efnn_hier); snode = GetNode (hierName, source->fterm_node->efnode_name->efnn_hier); dnode = GetNode (hierName, drain->fterm_node->efnode_name->efnn_hier); subnode = fetSubstrate(hierName, fet->fet_subsnode->efnode_name->efnn_hier, fet->fet_type, NULL); GeoTransRect(trans, &fet->fet_rect, &r); scale = GeoScale(trans); fp = mkFetMerge(l*scale, w*scale, gnode, snode, dnode, subnode, hierName, fet); hS = extHierSDAttr(source); hD = extHierSDAttr(drain); /* * run the list of fets. compare the current one with * each one in the list. if they fullfill the matching requirements * merge them only if: * 1) they have both apf S, D attributes * or * 2) one of them has aph S, D attributes and they have the same * hierarchical prefix * If one of them has apf and the other aph print a warning. */ for ( cfp = fetMergeList ; cfp != NULL ; cfp = cfp->next ) { if ((pmode = parallelFets(fp, cfp)) != NOT_PARALLEL) { cf = cfp->fet; cg = &cfp->fet->fet_terms[0]; cs = cd = &cfp->fet->fet_terms[1]; if (cfp->fet->fet_nterm >= 3) { if ( pmode == PARALLEL ) cd = &cfp->fet->fet_terms[2]; else if ( pmode == PARALLEL_R ) cs = &cfp->fet->fet_terms[2]; } chS = extHierSDAttr(cs); chD = extHierSDAttr(cd); if ( ! (chS||chD||hS||hD) ) /* all flat S, D */ goto mergeThem; if ( cfp->hierName != hierName && ( (chS && !hS) || ( chD && !hD ) || (!chS && hS) || ( !chD && hD ) ) ) { efHNSprintfPrefix((cfp->hierName)?cfp->hierName:hierName, esTempName); fprintf(stderr, "Warning: conflicting SD attributes of parallel fets in cell: %s\n", esTempName); break; } else if ( cfp->hierName == hierName ) { if ( hS && !chS ) { mergeAttr(cs->fterm_attrs, source->fterm_attrs); } if ( hD && !chD ) { mergeAttr(cd->fterm_attrs, drain->fterm_attrs); } } else /* cfp->hierName != hierName */ break; mergeThem: m = esFMult[cfp->esFMIndex] + ((float)fp->w/(float)cfp->w); setFetMult(fp->esFMIndex, FET_KILLED); setFetMult(cfp->esFMIndex, m); esFetsMerged++; /* Need to do attribute stuff here */ FREE(fp); return 0; } } /* No parallel fets to it yet */ fp->next = fetMergeList; fetMergeList = fp; return 0; } /* * ---------------------------------------------------------------------------- * * update_w -- * Updates the width client of node n with the current fet width * * Results: * N/A * * Side effects: * might allocate node client and widths * * ---------------------------------------------------------------------------- */ Void update_w(resClass, w, n) short resClass; int w; EFNode *n; { nodeClient *nc; int i; if ( n->efnode_client == NULL ) initNodeClient(n); nc = (nodeClient *) n->efnode_client; if ( nc->m_w.widths == NULL ) { MALLOC(float *, (nc->m_w.widths), sizeof(float)*efNumResistClasses); for ( i=0; im_w.widths[i] = 0.0; } nc->m_w.widths[resClass] += (float) w; } /* * ---------------------------------------------------------------------------- * * fetDistJunctVisit -- * Called for every fet and updates the nodeclients of its terminals * * Results: * 0 to keep the calling procedure going * * Side effects: * calls update_w which might allocate stuff * * ---------------------------------------------------------------------------- */ int fetDistJunctVisit(fet, hierName, trans, l, w) Fet *fet; /* Fet to examine */ HierName *hierName; /* Hierarchical path down to this fet */ Transform *trans; /* Coordinate transform not used */ int l, w; { EFNode *n; int scale, i; Rect r; if (fet->fet_nterm < 2) { fprintf(stderr, "outPremature\n"); return 0; } GeoTransRect(trans, &fet->fet_rect, &r); scale = GeoScale(trans); w = w*scale; for ( i = 1; ifet_nterm; i++ ) { n = GetNode(hierName, fet->fet_terms[i].fterm_node->efnode_name->efnn_hier); update_w(fetInfo[fet->fet_type].resClassSD, w, n); } return 0; } /* debugging stuff */ #include #define DBG 1 DBPRINT( char *fmt, ... ) { va_list args; FILE *fp; char buff[ 300 ]; if ( DBG ) { va_start( args, fmt ); fp = stderr ; (void) vsprintf( buff, fmt, args ); va_end( args ); (void) fputs( buff, fp ); } }