/* * EFflat.c - * * Procedures to flatten the hierarchical description built * by efReadDef(). * * ********************************************************************* * * Copyright (C) 1985, 1990 Regents of the University of California. * * * Permission to use, copy, modify, and distribute this * * * software and its documentation for any purpose and without * * * fee is hereby granted, provided that the above copyright * * * notice appear in all copies. The University of California * * * makes no representations about the suitability of this * * * software for any purpose. It is provided "as is" without * * * express or implied warranty. Export of this software outside * * * of the United States of America may require an export license. * * ********************************************************************* */ #ifndef lint static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/extflat/EFflat.c,v 1.5 2010/12/16 18:59:03 tim Exp $"; #endif /* not lint */ #include #include #include #include "utils/magic.h" #include "utils/geometry.h" #include "utils/geofast.h" #include "utils/hash.h" #include "utils/malloc.h" #include "utils/utils.h" #include "extflat/extflat.h" #include "extflat/EFint.h" /* C99 compat */ #include "textio/textio.h" /* Initial size of the hash table of all flattened node names */ #define INITFLATSIZE 1024 /* Hash table containing all flattened capacitors */ HashTable efCapHashTable; /* Hash table containing all flattened distances */ HashTable efDistHashTable; /* Head of circular list of all flattened nodes */ EFNode efNodeList; /* Root of the tree being flattened */ Def *efFlatRootDef; Use efFlatRootUse; HierContext efFlatContext; /* Forward declarations */ int efFlatSingleCap(HierContext *hc, char *name1, char *name2, Connection *conn); void efFlatGlob(void); int efFlatGlobHash(HierName *); bool efFlatGlobCmp(HierName *, HierName *); char *efFlatGlobCopy(HierName *); void efFlatGlobError(EFNodeName *nameGlob, EFNodeName *nameFlat); int efAddNodes(HierContext *hc, bool stdcell); int efAddConns(HierContext *hc, bool doWarn); int efAddOneConn(HierContext *hc, char *name1, char *name2, Connection *conn, bool doWarn); /* Flags passed to efFlatNode() */ #define FLATNODE_STDCELL 0x01 #define FLATNODE_DOWARN 0x02 #define FLATNODE_NOABSTRACT 0x04 /* * ---------------------------------------------------------------------------- * * EFFlatBuild -- * * First pass of flattening a circuit. * Builds up the flattened tables of nodes, capacitors, etc, depending * on the bits contained in flags: EF_FLATNODES causes the node table * to be built, EF_FLATCAPS the internodal capacitor table (implies * EF_FLATNODES), and EF_FLATDISTS the distance table. * * Callers who want various pieces of information should call * the relevant EFVisit procedures (e.g., EFVisitDevs(), EFVisitCaps(), * EFVisitNodes(), etc). * * Results: * None. * * Side effects: * Allocates lots of memory. * Be certain to call EFFlatDone() when this memory is * no longer needed. * * ---------------------------------------------------------------------------- */ void EFFlatBuild(name, flags) char *name; /* Name of root def being flattened */ int flags; /* Say what to flatten; see above */ { efFlatRootDef = efDefLook(name); if (efHNStats) efHNPrintSizes("before building flattened table"); /* Keyed by a full HierName */ HashInitClient(&efNodeHashTable, INITFLATSIZE, HT_CLIENTKEYS, efHNCompare, (char *(*)()) NULL, efHNHash, (int (*)()) NULL); /* Keyed by a pair of HierNames */ HashInitClient(&efDistHashTable, INITFLATSIZE, HT_CLIENTKEYS, efHNDistCompare, efHNDistCopy, efHNDistHash, efHNDistKill); /* Keyed by pairs of EFNode pointers (i.e., EFCoupleKeys) */ HashInit(&efCapHashTable, INITFLATSIZE, HashSize(sizeof (EFCoupleKey))); /* Keyed by a string and a HierName */ HashInitClient(&efHNUseHashTable, INITFLATSIZE, HT_CLIENTKEYS, efHNUseCompare, (char *(*)()) NULL, efHNUseHash, (int (*)()) NULL); /* Circular list of all nodes contains no elements initially */ efNodeList.efnode_next = (EFNodeHdr *) &efNodeList; efNodeList.efnode_prev = (EFNodeHdr *) &efNodeList; efFlatContext.hc_hierName = (HierName *) NULL; efFlatContext.hc_use = &efFlatRootUse; efFlatContext.hc_trans = GeoIdentityTransform; efFlatContext.hc_x = efFlatContext.hc_y = 0; efFlatRootUse.use_def = efFlatRootDef; if (flags & EF_FLATNODES) { int flatnodeflags = 0; if (flags & EF_WARNABSTRACT) flatnodeflags = FLATNODE_NOABSTRACT; if (flags & EF_NOFLATSUBCKT) { /* The top cell must always have the DEF_SUBCIRCUIT flag cleared */ efFlatRootDef->def_flags &= ~DEF_SUBCIRCUIT; efFlatNodesStdCell(&efFlatContext); } else { flatnodeflags |= FLATNODE_DOWARN; /* No FLATNODE_STDCELL flag */ efFlatNodes(&efFlatContext, INT2CD(flatnodeflags)); } efFlatKills(&efFlatContext); if (!(flags & EF_NONAMEMERGE)) efFlatGlob(); } /* Must happen after kill processing */ if (flags & EF_FLATCAPS) efFlatCaps(&efFlatContext); /* Distances are independent of kill processing */ if (flags & EF_FLATDISTS) efFlatDists(&efFlatContext); if (efHNStats) efHNPrintSizes("after building flattened table"); return; } /*----------------------------------------------------------------------*/ /* EFFlatBuildOneLevel -- */ /* */ /* EFFlatBuild for a single hierarchical level. Note, however that */ /* where subcircuits have no extracted components, the hierarchy of */ /* the subcircuit will be traversed and the subcircuit merged into */ /* the root being flattened. */ /* */ /* This routine used for hierarchical extraction. */ /*----------------------------------------------------------------------*/ HierContext * EFFlatBuildOneLevel(def, flags) Def *def; /* root def being flattened */ int flags; { int usecount, savecount; Use *use; int efFlatNodesDeviceless(); /* Forward declaration */ int efFlatCapsDeviceless(); /* Forward declaration */ int flatnodeflags; efFlatRootDef = def; /* Keyed by a full HierName */ HashInitClient(&efNodeHashTable, INITFLATSIZE, HT_CLIENTKEYS, efHNCompare, (char *(*)()) NULL, efHNHash, (int (*)()) NULL); /* Keyed by a pair of HierNames */ HashInitClient(&efDistHashTable, INITFLATSIZE, HT_CLIENTKEYS, efHNDistCompare, efHNDistCopy, efHNDistHash, efHNDistKill); /* Keyed by pairs of EFNode pointers (i.e., EFCoupleKeys) */ HashInit(&efCapHashTable, INITFLATSIZE, HashSize(sizeof (EFCoupleKey))); /* Keyed by a string and a HierName */ HashInitClient(&efHNUseHashTable, INITFLATSIZE, HT_CLIENTKEYS, efHNUseCompare, (char *(*)()) NULL, efHNUseHash, (int (*)()) NULL); /* Circular list of all nodes contains no elements initially */ efNodeList.efnode_next = (EFNodeHdr *) &efNodeList; efNodeList.efnode_prev = (EFNodeHdr *) &efNodeList; efFlatContext.hc_hierName = (HierName *) NULL; efFlatContext.hc_use = &efFlatRootUse; efFlatContext.hc_trans = GeoIdentityTransform; efFlatContext.hc_x = efFlatContext.hc_y = 0; efFlatRootUse.use_def = efFlatRootDef; /* Record all nodes down the hierarchy from here */ flatnodeflags = 0; /* No FLATNODE_DOWARN */ efFlatNodes(&efFlatContext, INT2CD(flatnodeflags)); /* Expand all subcells that contain connectivity information but */ /* no active devices (including those in subcells). */ usecount = HashGetNumEntries(&efFlatRootUse.use_def->def_uses); /* Recursively flatten uses that have no active devices */ if (usecount > 0) efHierSrUses(&efFlatContext, efFlatNodesDeviceless, (ClientData)&usecount); if ((usecount == 0) && (HashGetNumEntries(&efFlatRootUse.use_def->def_devs) == 0)) efFlatRootUse.use_def->def_flags |= DEF_NODEVICES; efFlatKills(&efFlatContext); if (!(flags & EF_NONAMEMERGE)) efFlatGlob(); if (flags & EF_FLATCAPS) efFlatCapsDeviceless(&efFlatContext); if (flags & EF_FLATDISTS) efFlatDists(&efFlatContext); return &efFlatContext; } /* * ---------------------------------------------------------------------------- * * EFFlatDone -- * * Cleanup by removing all memory used by the flattened circuit * representation. For use of func(), see efFreeNodeList() in EFbuild.c. * * Results: * None. * * Side effects: * Frees lots of memory. * * ---------------------------------------------------------------------------- */ void EFFlatDone(func) int (*func)(); { #ifdef MALLOCTRACE /* Hash table statistics */ TxPrintf("\n\nStatistics for node hash table:\n"); HashStats(&efNodeHashTable); #endif /* MALLOCTRACE */ /* Free temporary storage */ efFreeNodeTable(&efNodeHashTable); efFreeNodeList(&efNodeList, func); HashFreeKill(&efCapHashTable); HashKill(&efNodeHashTable); HashKill(&efDistHashTable); HashKill(&efHNUseHashTable); return; } /* * ---------------------------------------------------------------------------- * * efFlatNodes -- * * Recursive procedure to flatten the nodes in hc->hc_use->use_def, * using a depth-first post-order traversal of the hierarchy. * * Algorithm: * We first recursivly call efFlatNodes for all of our children uses. * This adds their node names to the global node table. Next we add * our own nodes to the table. Some nodes will have to be merged * by connections made in this def, or at least will require adjustments * to their resistance or capacitance. We walk down the connection * list hc->hc_use->use_def->def_conns to do this merging. Whenever * two nodes merge, the EFNodeName list for the resulting node is * rearranged to begin with the highest precedence name from the lists * for the two nodes being combined. See efNodeMerge for a discussion * of precedence. * * Results: * Returns 0 to keep efHierSrUses going. * * Side effects: * Adds node names to the table of flattened node names efNodeHashTable. * May merge nodes from the list efNodeList as per the connection * list hc->hc_use->use_def->def_conns. * * Note: * stdcell = TRUE is only used when writing DEF files. * * ---------------------------------------------------------------------------- */ int efFlatNodes(hc, clientData) HierContext *hc; ClientData clientData; { int flags = (int)CD2INT(clientData); bool stdcell = (flags & FLATNODE_STDCELL) ? TRUE : FALSE; bool doWarn = (flags & FLATNODE_DOWARN) ? TRUE : FALSE; if (flags & FLATNODE_NOABSTRACT) { Def *def = hc->hc_use->use_def; if (def->def_flags & DEF_ABSTRACT) TxError("Error: Cell %s was extracted as an abstract view.\n", def->def_name); } (void) efHierSrUses(hc, efFlatNodes, clientData); /* Add all our own nodes to the table */ efAddNodes(hc, stdcell); /* Process our own connections and adjustments */ (void) efAddConns(hc, doWarn); return (0); } /* * ---------------------------------------------------------------------------- * * efFlatNodesStdCell -- * * Recursive procedure to flatten the nodes in hc->hc_use->use_def, * using a depth-first post-order traversal of the hierarchy. We stop * whenever we reach a subcircuit definition, only enumerating its ports. * * Algorithm: * We first recursivly call efFlatNodes for all of our children uses. * This adds their node names to the global node table. Next we add * our own nodes to the table. Some nodes will have to be merged * by connections made in this def, or at least will require adjustments * to their resistance or capacitance. We walk down the connection * list hc->hc_use->use_def->def_conns to do this merging. Whenever * two nodes merge, the EFNodeName list for the resulting node is * rearranged to begin with the highest precedence name from the lists * for the two nodes being combined. See efNodeMerge for a discussion * of precedence. * * Results: * Returns 0 to keep efHierSrUses going. * * Side effects: * Adds node names to the table of flattened node names efNodeHashTable. * May merge nodes from the list efNodeList as per the connection * list hc->hc_use->use_def->def_conns. * * ---------------------------------------------------------------------------- */ int efFlatNodesStdCell(hc) HierContext *hc; { if (!(hc->hc_use->use_def->def_flags & DEF_SUBCIRCUIT)) { /* Recursively flatten each use, except in defined subcircuits */ (void) efHierSrUses(hc, efFlatNodesStdCell, (ClientData) NULL); } /* Add all our own nodes to the table */ efAddNodes(hc, TRUE); /* Process our own connections and adjustments */ if (!(hc->hc_use->use_def->def_flags & DEF_SUBCIRCUIT)) (void) efAddConns(hc, TRUE); return (0); } int efFlatNodesDeviceless(hc, cdata) HierContext *hc; ClientData cdata; { int *usecount = (int *)cdata; int newcount; Use *use; newcount = HashGetNumEntries(&hc->hc_use->use_def->def_uses); /* Recursively flatten uses that have no active devices */ if (newcount > 0) efHierSrUses(hc, efFlatNodesDeviceless, (ClientData)&newcount); if ((HashGetNumEntries(&hc->hc_use->use_def->def_devs) == 0) && (newcount == 0)) { /* Add all our own nodes to the table */ efAddNodes(hc, TRUE); /* Process our own connections and adjustments */ efAddConns(hc, TRUE); /* Mark this definition as having no devices, so it will not be visited */ hc->hc_use->use_def->def_flags |= DEF_NODEVICES; /* If this definition has no devices but has ports, then it is treated */ /* as a black-box device, so don't decrement the use count of the */ /* parent. Alternately, devices flagged "abstract" or "primitive" are */ /* automatically treated as black-box devices. */ if (!(hc->hc_use->use_def->def_flags & DEF_SUBCIRCUIT)) if (!(hc->hc_use->use_def->def_flags & (DEF_ABSTRACT | DEF_PRIMITIVE))) (*usecount)--; } return (0); } /* * ---------------------------------------------------------------------------- * * efAddNodes -- * * Add all the nodes defined by the def 'hc->hc_use->use_def' to the * global symbol table. Each global name is prefixed by the hierarchical * name component hc->hc_hierName. If "stdcell" is TRUE, we ONLY add nodes * that are defined ports. Otherwise, we add all nodes. * * Results: * None. * * Side effects: * Adds node names to the table of flattened node names efNodeHashTable. * * ---------------------------------------------------------------------------- */ int efAddNodes( HierContext *hc, bool stdcell) { Def *def = hc->hc_use->use_def; EFNodeName *nn, *newname, *oldname; EFNode *node, *newnode; EFAttr *ap, *newap; LinkedRect *lr, *newlr; HierName *hierName; int size, asize; HashEntry *he; bool is_subcircuit = (def->def_flags & DEF_SUBCIRCUIT) ? TRUE : FALSE; size = sizeof (EFNode) + (efNumResistClasses-1) * sizeof (EFPerimArea); for (node = (EFNode *) def->def_firstn.efnode_next; node != &def->def_firstn; node = (EFNode *) node->efnode_next) { /* In subcircuits, only enumerate the ports */ if (stdcell && is_subcircuit && !(node->efnode_flags & EF_PORT)) continue; newnode = (EFNode *) mallocMagic((unsigned)(size)); newnode->efnode_attrs = (EFAttr *) NULL; for (ap = node->efnode_attrs; ap; ap = ap->efa_next) { asize = ATTRSIZE(strlen(ap->efa_text)); newap = (EFAttr *) mallocMagic((unsigned)(asize)); (void) strcpy(newap->efa_text, ap->efa_text); GeoTransRect(&hc->hc_trans, &ap->efa_loc, &newap->efa_loc); newap->efa_type = ap->efa_type; newap->efa_next = newnode->efnode_attrs; newnode->efnode_attrs = newap; } newnode->efnode_disjoint = (LinkedRect *)NULL; for (lr = node->efnode_disjoint; lr; lr = lr->r_next) { newlr = (LinkedRect *)mallocMagic(sizeof(LinkedRect)); newlr->r_r = lr->r_r; newlr->r_type = lr->r_type; newlr->r_next = newnode->efnode_disjoint; newnode->efnode_disjoint = newlr; } // If called with "hierarchy on", all local node caps and adjustments // have been output and should be ignored. newnode->efnode_cap = (!stdcell) ? node->efnode_cap : (EFCapValue)0.0; newnode->efnode_client = (ClientData) NULL; newnode->efnode_flags = node->efnode_flags; newnode->efnode_type = node->efnode_type; newnode->efnode_num = 1; if (!stdcell) bcopy((char *) node->efnode_pa, (char *) newnode->efnode_pa, efNumResistClasses * sizeof (EFPerimArea)); else bzero((char *) newnode->efnode_pa, efNumResistClasses * sizeof (EFPerimArea)); GeoTransRect(&hc->hc_trans, &node->efnode_loc, &newnode->efnode_loc); /* Add each name for this node to the hash table */ newnode->efnode_name = (EFNodeName *) NULL; /* Prepend to global node list */ newnode->efnode_next = efNodeList.efnode_next; newnode->efnode_prev = (EFNodeHdr *) &efNodeList; efNodeList.efnode_next->efnhdr_prev = (EFNodeHdr *) newnode; efNodeList.efnode_next = (EFNodeHdr *) newnode; for (nn = node->efnode_name; nn; nn = nn->efnn_next) { /* * Construct the full hierarchical name of this node. * The path down to this point is given by hc->hc_hierName, * to which nn->efnn_hier is "appended". Exception: nodes * marked with EF_DEVTERM (fet substrate nodes used before * declared, so intended to refer to default global names) * are added as global nodes. */ if (node->efnode_flags & EF_DEVTERM) hierName = nn->efnn_hier; else hierName = EFHNConcat(hc->hc_hierName, nn->efnn_hier); he = HashFind(&efNodeHashTable, (char *) hierName); /* * The name should only have been in the hash table already * if the node was marked with EF_DEVTERM as described above. */ if ((oldname = (EFNodeName *) HashGetValue(he))) { if (hierName != nn->efnn_hier) EFHNFree(hierName, hc->hc_hierName, HN_CONCAT); if (oldname->efnn_node != newnode) efNodeMerge(&oldname->efnn_node, &newnode); newnode = oldname->efnn_node; continue; } /* * We only guarantee that the first name for the node remains * first (since the first name is the "canonical" name for the * node). The order of the remaining names will be reversed. */ newname = (EFNodeName *) mallocMagic((unsigned)(sizeof (EFNodeName))); HashSetValue(he, (char *) newname); newname->efnn_node = newnode; newname->efnn_hier = hierName; newname->efnn_port = -1; newname->efnn_refc = 0; if (newnode->efnode_name) { newname->efnn_next = newnode->efnode_name->efnn_next; newnode->efnode_name->efnn_next = newname; } else { newname->efnn_next = (EFNodeName *) NULL; newnode->efnode_name = newname; } } } return 0; } /* * ---------------------------------------------------------------------------- * * efAddConns -- * * Make all the connections for a given def. This may cause previously * distinct nodes in the flat table to merge. * * Results: * Returns 0 * * Side effects: * May merge nodes from the list efNodeList as per the connection * list hc->hc_use->use_def->def_conns. * * ---------------------------------------------------------------------------- */ int efAddConns( HierContext *hc, bool doWarn) { Connection *conn; if (efWatchNodes) TxPrintf("Processing %s (%s)\n", EFHNToStr(hc->hc_hierName), hc->hc_use->use_def->def_name); for (conn = hc->hc_use->use_def->def_conns; conn; conn = conn->conn_next) { /* Special case for speed when no array info is present */ if (conn->conn_1.cn_nsubs == 0) efAddOneConn(hc, conn->conn_name1, conn->conn_name2, conn, doWarn); else efHierSrArray(hc, conn, efAddOneConn, INT2CD(doWarn)); } return (0); } /* * ---------------------------------------------------------------------------- * * efAddOneConn -- * * Do the work of adding a single connection. The names of the nodes * to be connected are 'name1' and 'name2' (note that these are regular * strings, not HierNames). The resistance of the merged node is to be * adjusted by 'deltaR' and its capacitance by 'deltaC'. If 'name2' is * NULL, we just adjust the R and C of the node 'name1'. * * Results: * Returns 0 * * Side effects: * May merge nodes from the list efNodeList. * * ---------------------------------------------------------------------------- */ int efAddOneConn( HierContext *hc, char *name1, /* These are strings, not HierNames */ char *name2, Connection *conn, bool doWarn) { HashEntry *he1, *he2; EFNode *node, *newnode; int n; he1 = EFHNLook(hc->hc_hierName, name1, (doWarn) ? "connect(1)" : NULL); if (he1 == NULL) return 0; /* Adjust the resistance and capacitance of its corresponding node */ node = ((EFNodeName *) HashGetValue(he1))->efnn_node; node->efnode_cap += conn->conn_cap; for (n = 0; n < efNumResistClasses; n++) { node->efnode_pa[n].pa_area += conn->conn_pa[n].pa_area; node->efnode_pa[n].pa_perim += conn->conn_pa[n].pa_perim; } /* Merge this node with conn_name2 if one was specified */ if (name2) { he2 = EFHNLook(hc->hc_hierName, name2, (doWarn) ? "connect(2)" : NULL); if (he2 == NULL) return 0; newnode = ((EFNodeName *) HashGetValue(he2))->efnn_node; if (node != newnode) efNodeMerge(&node, &newnode); } return 0; } /* * ---------------------------------------------------------------------------- * * efFlatGlob -- * * This procedure checks to ensure that all occurrences of the same global * name are connected. It also adds the reduced form of the global name * (i.e., the global name with no pathname prefix) to the hash table * efNodeHashTable, making this the preferred name of the global node. * * Algorithm: * Scan through the node table looking for globals. Add each * global to a global name table keyed by just the first component * of the HierName. The value of the entry in this table is set * initially to the EFNodeName for the first occurrence of the * global node. If another occurrence of the global name is * found whose EFNode differs from this one's, it's an error. * However, we still merge all the pieces of a global node * into a single one at the end. * * Results: * None. * * Side effects: * See above. * * ---------------------------------------------------------------------------- */ void efFlatGlob(void) { EFNodeName *nameFlat, *nameGlob; EFNode *nodeFlat, *nodeGlob; HashEntry *heFlat, *heGlob; HierName *hnFlat, *hnGlob; HashTable globalTable; HashSearch hs; HashInitClient(&globalTable, INITFLATSIZE, HT_CLIENTKEYS, efFlatGlobCmp, efFlatGlobCopy, efFlatGlobHash, (int (*)()) NULL); /* * The following loop examines each global name (the last component of * each flat HierName that ends in the global symbol '!'), using the * hash table globalTable to keep track of how many times each global * name has been seen. Each global name should be seen exactly once. * The only exceptions are fet substrate nodes (nodes marked with the * flag EF_DEVTERM), which automatically merge with other global nodes * with the same name, since they're only implicitly connected anyway. */ for (nodeFlat = (EFNode *) efNodeList.efnode_next; nodeFlat != &efNodeList; nodeFlat = (EFNode *) nodeFlat->efnode_next) { /* * Ignore nodes whose names aren't global. NOTE: we rely on * the fact that EFHNBest() prefers global names to all others, * so if the first name in a node's list isn't global, none of * the rest are either. */ nameFlat = nodeFlat->efnode_name; hnFlat = nameFlat->efnn_hier; if (!EFHNIsGlob(hnFlat)) continue; /* * Look for an entry corresponding to the global part of hnFlat * (only the leaf component) in the global name table. If one * isn't found, an entry gets created. */ heGlob = HashFind(&globalTable, (char *) hnFlat); nameGlob = (EFNodeName *) HashGetValue(heGlob); if (nameGlob == NULL) { /* * Create a new EFNodeName that points to nodeFlat, but * don't link it in to nodeFlat->efnode_name yet. */ nameGlob = (EFNodeName *) mallocMagic((unsigned)(sizeof (EFNodeName))); HashSetValue(heGlob, (ClientData) nameGlob); nameGlob->efnn_node = nodeFlat; nameGlob->efnn_hier = (HierName *) heGlob->h_key.h_ptr; nameGlob->efnn_port = -1; nameGlob->efnn_refc = 0; } else if (nameGlob->efnn_node != nodeFlat) { /* * If either node is a fet substrate node (marked with EF_DEVTERM) * it's OK to merge them; otherwise, it's an error, but we still * merge the nodes. When merging, we blow away nodeGlob and * absorb it into nodeFlat for simplicity in control of the main * loop. Note that since nameGlob isn't on the efnode_name list * for nodeGlob, we have to update its node backpointer explicitly. */ nodeGlob = nameGlob->efnn_node; if ((nodeGlob->efnode_flags & EF_DEVTERM) == 0 && (nodeFlat->efnode_flags & EF_DEVTERM) == 0) { efFlatGlobError(nameGlob, nameFlat); } efNodeMerge(&nodeFlat, &nodeGlob); nameGlob->efnn_node = nodeFlat; } } /* * Now make another pass through the global name table, * prepending the global name (the HierName consisting of * the trailing component only that was allocated when the * name was added to globalTable above) to its node, and * also adding it to the global hash table efNodeHashTable. */ HashStartSearch(&hs); while ((heGlob = HashNext(&globalTable, &hs))) { /* * Add the name to the flat node name hash table, and * prepend the EFNodeName to the node's list, but only * if the node didn't already exist in efNodeHashTable. * Otherwise, free nameGlob. */ nameGlob = (EFNodeName *) HashGetValue(heGlob); hnGlob = nameGlob->efnn_hier; heFlat = HashFind(&efNodeHashTable, (char *) hnGlob); if (HashGetValue(heFlat) == NULL) { nodeFlat = nameGlob->efnn_node; HashSetValue(heFlat, (ClientData) nameGlob); nameGlob->efnn_next = nodeFlat->efnode_name; nodeFlat->efnode_name = nameGlob; } else { freeMagic((char *) nameGlob); EFHNFree(hnGlob, (HierName *) NULL, HN_GLOBAL); } } HashKill(&globalTable); return; } void efFlatGlobError( EFNodeName *nameGlob, EFNodeName *nameFlat) { EFNode *nodeGlob = nameGlob->efnn_node, *nodeFlat = nameFlat->efnn_node; EFNodeName *nn; int count; TxPrintf("*** Global name %s not fully connected:\n", nameGlob->efnn_hier->hn_name); TxPrintf("One portion contains the names:\n"); for (count = 0, nn = nodeGlob->efnode_name; count < 10 && nn; count++, nn = nn->efnn_next) { TxPrintf(" %s\n", EFHNToStr(nn->efnn_hier)); } if (nn) TxPrintf(" .... (no more names will be printed)\n"); TxPrintf("The other portion contains the names:\n"); for (count = 0, nn = nodeFlat->efnode_name; count < 10 && nn; count++, nn = nn->efnn_next) { TxPrintf(" %s\n", EFHNToStr(nn->efnn_hier)); } if (nn) TxPrintf(" .... (no more names will be printed)\n"); TxPrintf("I'm merging the two pieces into a single node, but you\n"); TxPrintf("should be sure eventually to connect them in the layout.\n\n"); return; } bool efFlatGlobCmp(hierName1, hierName2) HierName *hierName1, *hierName2; { if (hierName1 == hierName2) return FALSE; return ((bool)(hierName1 == NULL || hierName2 == NULL || hierName1->hn_hash != hierName2->hn_hash || strcmp(hierName1->hn_name, hierName2->hn_name) != 0 )); } char * efFlatGlobCopy(hierName) HierName *hierName; { HierName *hNew; int size; size = HIERNAMESIZE(strlen(hierName->hn_name)); hNew = (HierName *) mallocMagic((unsigned)(size)); (void) strcpy(hNew->hn_name, hierName->hn_name); hNew->hn_parent = (HierName *) NULL; hNew->hn_hash = hierName->hn_hash; if (efHNStats) efHNRecord(size, HN_GLOBAL); return (char *) hNew; } int efFlatGlobHash(hierName) HierName *hierName; { return hierName->hn_hash; } /* * ---------------------------------------------------------------------------- * * efFlatKills -- * * Recursively mark all killed nodes, using a depth-first post-order * traversal of the hierarchy. The algorithm is the same as for * efFlatNodes above. * * Results: * Returns 0 to keep efHierSrUses going. * * Side effects: * May mark node entries in the global name table as killed * by setting EF_KILLED in the efnode_flags field. * * ---------------------------------------------------------------------------- */ int efFlatKills(hc) HierContext *hc; { Def *def = hc->hc_use->use_def; HashEntry *he; EFNodeName *nn; Kill *k; /* Recursively visit each use */ (void) efHierSrUses(hc, efFlatKills, (ClientData) NULL); /* Process all of our kill information */ for (k = def->def_kills; k; k = k->kill_next) { if ((he = EFHNConcatLook(hc->hc_hierName, k->kill_name, "kill"))) { nn = (EFNodeName *) HashGetValue(he); nn->efnn_node->efnode_flags |= EF_KILLED; } } return (0); } /* * ---------------------------------------------------------------------------- * * efFlatCapsDeviceless -- * * Recursive procedure to flatten all capacitors in the circuit. * Works like efFlatCaps() (see below), but ignores CellDefs that * have no devices. * * Results: * Returns 0 to keep efHierSrUses going. * * Side effects: * See description of efFlatCaps(). * * ---------------------------------------------------------------------------- */ int efFlatCapsDeviceless(hc) HierContext *hc; { Connection *conn; int newcount; Use *use; newcount = HashGetNumEntries(&hc->hc_use->use_def->def_uses); /* Recursively flatten uses that have no active devices */ if (newcount > 0) efHierSrUses(hc, efFlatCapsDeviceless, (ClientData)NULL); if (!(hc->hc_use->use_def->def_flags & DEF_NODEVICES)) if (hc->hc_use->use_def->def_flags & DEF_PROCESSED) return 0; /* Output our own capacitors */ for (conn = hc->hc_use->use_def->def_caps; conn; conn = conn->conn_next) { /* Special case for speed if no arraying info */ if (conn->conn_1.cn_nsubs == 0) efFlatSingleCap(hc, conn->conn_name1, conn->conn_name2, conn); else efHierSrArray(hc, conn, efFlatSingleCap, (ClientData) NULL); } return (0); } /* * ---------------------------------------------------------------------------- * * efFlatCaps -- * * Recursive procedure to flatten all capacitors in the circuit. * Produces a single, global hash table (efCapHashTable) indexed * by pairs of EFNode pointers, where the value of each entry is the * capacitance between the two nodes. * * Algorithm: * Before this procedure is called, efFlatNodes() should have been * called to create a global table of all node names. We do a recursive * traversal of the design rooted at 'hc->hc_use->use_def', and construct * full hierarchical names from the terminals of each capacitor * encountered. * * These full names are used to find via a lookup in efNodeHashTable the * canonical name of the node for which this full name is an alias. The * canonical name is output as the node to which this terminal connects. * * Capacitance where one of the nodes is substrate is treated specially; * instead of adding an entry to the global hash table, we update * the substrate capacitance of the other node appropriately. * * Results: * Returns 0 to keep efHierSrUses going. * * Side effects: * See above. * * ---------------------------------------------------------------------------- */ int efFlatCaps(hc) HierContext *hc; { Connection *conn; /* Recursively flatten capacitors */ (void) efHierSrUses(hc, efFlatCaps, (ClientData) 0); /* Output our own capacitors */ for (conn = hc->hc_use->use_def->def_caps; conn; conn = conn->conn_next) { /* Special case for speed if no arraying info */ if (conn->conn_1.cn_nsubs == 0) efFlatSingleCap(hc, conn->conn_name1, conn->conn_name2, conn); else efHierSrArray(hc, conn, efFlatSingleCap, (ClientData) NULL); } return (0); } /* * ---------------------------------------------------------------------------- * * efFlatSingleCap -- * * Add a capacitor with value 'conn->conn_cap' between the nodes * 'name1' and 'name2' (text names, not hierarchical names). Don't * add the capacitor if either terminal is a killed node. * * Results: * Returns 0 * * Side effects: * Adds an entry to efCapHashTable indexed by the nodes of 'name1' * and 'name2' respectively. If the two nodes are the same, though, * nothing happens. If either node is ground (GND!), the capacitance * is added to the substrate capacitance of the other node instead of * creating a hash table entry. * * ---------------------------------------------------------------------------- */ int efFlatSingleCap( HierContext *hc, /* Contains hierarchical pathname to cell */ char *name1, /* Names of nodes connecting to capacitor */ char *name2, Connection *conn) /* Contains capacitance to add */ { EFNode *n1, *n2; HashEntry *he; EFCoupleKey ck; static char msg0[] = "cap(1)"; static char msg1[] = "cap(2)"; char *msg; /* Connections that are below threshold (ext2spice hierarchy only) */ /* will be missing. Do not generate errors for these. */ msg = (fabs((double)conn->conn_cap / 1000) < EFCapThreshold) ? NULL : msg0; if ((he = EFHNLook(hc->hc_hierName, name1, msg)) == NULL) return 0; n1 = ((EFNodeName *) HashGetValue(he))->efnn_node; if (n1->efnode_flags & EF_KILLED) return 0; if (msg) msg = msg1; if ((he = EFHNLook(hc->hc_hierName, name2, msg)) == NULL) return 0; n2 = ((EFNodeName *) HashGetValue(he))->efnn_node; if (n2->efnode_flags & EF_KILLED) return 0; /* Do nothing if the nodes aren't different */ if (n1 == n2) return 0; if (n1->efnode_flags & EF_GLOB_SUBS_NODE) n2->efnode_cap += conn->conn_cap; /* node 2 to substrate */ else if (n2->efnode_flags & EF_GLOB_SUBS_NODE) n1->efnode_cap += conn->conn_cap; /* node 1 to substrate */ else { /* node1 to node2 */ if (n1 < n2) ck.ck_1 = n1, ck.ck_2 = n2; else ck.ck_1 = n2, ck.ck_2 = n1; he = HashFind(&efCapHashTable, (char *) &ck); CapHashSetValue(he, (double) (conn->conn_cap + CapHashGetValue(he))); } return 0; } /* * ---------------------------------------------------------------------------- * * efFlatDists -- * * Recursive procedure to flatten all distance information in the circuit. * Produces a single, global hash table (efDistHashTable) indexed * by Distance structures, where the value of each entry is the same * as the key and gives the min and maximum distances between the two * points. * * Results: * Returns 0 to keep efHierSrUses going. * * Side effects: * See above. * * ---------------------------------------------------------------------------- */ int efFlatDists(hc) HierContext *hc; { Distance *dist, *distFlat, distKey; HashEntry *he, *heFlat; HashSearch hs; /* Recursively flatten distances */ (void) efHierSrUses(hc, efFlatDists, (ClientData) 0); /* Process our own distances */ HashStartSearch(&hs); while ((he = HashNext(&hc->hc_use->use_def->def_dists, &hs))) { dist = (Distance *) HashGetValue(he); efHNBuildDistKey(hc->hc_hierName, dist, &distKey); heFlat = HashFind(&efDistHashTable, (char *) &distKey); if ((distFlat = (Distance *) HashGetValue(heFlat))) { /* * This code differs from that in efBuildDist(), in that * we replace the min/max information in distFlat from * that in dist, rather than computing a new min/max. * The reason is that the information in dist (in the * parent) is assumed to override that already computed * in the child. */ distFlat->dist_min = dist->dist_min; distFlat->dist_max = dist->dist_max; EFHNFree(distKey.dist_1, hc->hc_hierName, HN_CONCAT); EFHNFree(distKey.dist_2, hc->hc_hierName, HN_CONCAT); } else { /* * If there was no entry in the table already with this * key, make the HashEntry point to its key (which is * the newly malloc'd Distance structure). */ HashSetValue(heFlat, (ClientData) he->h_key.h_ptr); } } return 0; } /* * CapHashGetValue() * do a HashGetValue, and if the pointer is null, return (EFCapValue)0.0 */ EFCapValue CapHashGetValue(he) HashEntry *he; { EFCapValue *capp = (EFCapValue *)HashGetValue(he); if(capp == NULL) return (EFCapValue)0; else return *capp; } /* * CapHashSetValue() * if the pointer is null, allocate a EFCapValue and point to it. * Then copy in the new value. * * need to pass doubles regardless of what CapValue is because of * argument promotion in ANSI C * */ void CapHashSetValue(he, c) HashEntry *he; double c; { EFCapValue *capp = (EFCapValue *)HashGetValue(he); if(capp == NULL) { capp = (EFCapValue *) mallocMagic((unsigned)(sizeof(EFCapValue))); HashSetValue(he, capp); } *capp = (EFCapValue) c; return; }