magic/extflat/EFvisit.c

921 lines
21 KiB
C
Raw Normal View History

/*
* EFvisit.c -
*
* Procedures to traverse and output flattened nodes, capacitors,
* transistors, resistors, and Distances.
*
* *********************************************************************
* * 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/EFvisit.c,v 1.5 2010/08/10 00:18:45 tim Exp $";
#endif /* not lint */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#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"
#include "tiles/tile.h"
#include "extract/extract.h"
/* C99 compat */
#include "textio/textio.h"
/* Root of the tree being flattened */
extern Def *efFlatRootDef;
extern Use efFlatRootUse;
extern HierContext efFlatContext;
extern void efDevFixLW();
extern void efHNOutPrefix();
bool efDevKilled();
/*
* ----------------------------------------------------------------------------
*
* EFVisitSubcircuits --
*
* Visit all of the "defined" subcircuits in the circuit.
* This is meant to provide a generic functionality similar to
* the transistor/resistor/capacitor extraction. It assumes that the
* end-user has an existing description of the extracted subcircuit,
* such as a characterized standard cell, and that magic is not to
* attempt an extraction itself, but only to call the predefined
* subcircuit, matching nodes to the subcircuit's port list.
*
* For each def encountered which has the DEF_SUBCIRCUIT flag set,
* call the user-supplied procedure (*subProc)(), which should be of
* the following form:
*
* (*subProc)(use, hierName, is_top)
* Use *use;
* HierName *hierName;
* bool is_top;
* {
* }
*
* is_top will be TRUE for the top-level cell, and FALSE for all
* other cells. The procedure should return 0 normally, or 1 to abort
* the search.
*
* Results:
* Returns 0 if terminated normally, or 1 if the search
* was aborted.
*
* Side effects:
* Whatever (*subProc)() does.
*
* ----------------------------------------------------------------------------
*/
int
EFVisitSubcircuits(subProc, cdata)
int (*subProc)();
ClientData cdata;
{
CallArg ca;
HierContext *hc;
int efVisitSubcircuits(); /* Forward declaration */
/* If the top-level def is defined as a subcircuit, call topProc */
hc = &efFlatContext;
if (hc->hc_use->use_def->def_flags & DEF_SUBCIRCUIT)
if ((*subProc)(hc->hc_use, hc->hc_hierName, TRUE))
return 1;
/* For each subcell of the top-level def that is defined as */
/* a subcircuit, call subProc. */
ca.ca_proc = subProc;
ca.ca_cdata = cdata;
if (efHierSrUses(hc, efVisitSubcircuits, (ClientData) &ca))
return 1;
return 0;
}
/*
* Procedure to visit recursively all subcircuits in the design.
* Does all the work of EFVisitSubcircuits() above.
*
* Results:
* Returns 0 to keep efHierSrUses going.
*
* Side effects:
* Calls the client procedure (*ca->ca_proc)().
*/
int
efVisitSubcircuits(hc, ca)
HierContext *hc;
CallArg *ca;
{
/* Look for children of this def which are defined */
/* as subcircuits via the DEF_SUBCIRCUIT flag. */
if (hc->hc_use->use_def->def_flags & DEF_SUBCIRCUIT)
{
if ((*ca->ca_proc)(hc->hc_use, hc->hc_hierName, NULL))
return 1;
else
return 0;
}
/* Recursively visit subcircuits in our children last. */
if (efHierSrUses(hc, efVisitSubcircuits, (ClientData) ca))
return 1;
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* EFGetLengthAndWidth --
*
* Estimate length and width for a device from area and perimeter values.
* Mostly this routine is meant to handle the older "fet" record.
* Newer "device" types should have length and width properly determined
* already, and we just return those values from the device structure.
*
* Results:
* None
*
* Side Effects:
* Values substituted for length and width.
*
* ----------------------------------------------------------------------------
*/
void
EFGetLengthAndWidth(dev, lptr, wptr)
Dev *dev;
int *lptr;
int *wptr;
{
DevTerm *gate, *source, *drain;
int area, perim, l, w;
switch (dev->dev_class)
{
case DEV_MOSFET:
case DEV_ASYMMETRIC:
case DEV_BJT:
case DEV_SUBCKT:
case DEV_MSUBCKT:
case DEV_RSUBCKT:
case DEV_CSUBCKT:
case DEV_DIODE:
case DEV_PDIODE:
case DEV_NDIODE:
case DEV_CAP:
case DEV_CAPREV:
case DEV_RES:
l = dev->dev_length;
w = dev->dev_width;
break;
case DEV_FET:
area = dev->dev_area;
perim = dev->dev_perim;
gate = &dev->dev_terms[0];
/*
* L, W, and flat coordinates of a point inside the channel.
* Handle FETs with two terminals (capacitors) separately.
*/
if (dev->dev_nterm == 2)
{
/* Convert area to type double to avoid overflow in */
/* extreme cases. */
l = perim - (int)sqrt((double)(perim * perim) - 16 * (double)area);
l >>= 2;
w = area / l;
}
else
{
source = drain = &dev->dev_terms[1];
if (dev->dev_nterm >= 3)
drain = &dev->dev_terms[2];
l = gate->dterm_length / 2;
w = (source->dterm_length + drain->dterm_length) / 2;
}
if (gate->dterm_attrs) efDevFixLW(gate->dterm_attrs, &l, &w);
break;
default:
l = w = 0;
break;
}
*lptr = l;
*wptr = w;
}
/*
* ----------------------------------------------------------------------------
*
* EFVisitDevs --
*
* Visit all the devs in the circuit.
* Must be called after EFFlatBuild().
* For each dev in the circuit, call the user-supplied procedure
* (*devProc)(), which should be of the following form:
*
* (*devProc)(dev, hierName, scale, cdata)
* Dev *dev;
* HierName *hierName;
* float scale;
* Transform *trans;
* ClientData cdata;
* {
* }
*
* The procedure should return 0 normally, or 1 to abort the
* search.
*
* We ensure that no devs connected to killed nodes are passed
* to this procedure.
*
* Results:
* Returns 0 if terminated normally, or 1 if the search
* was aborted.
*
* Side effects:
* Whatever (*devProc)() does.
*
* ----------------------------------------------------------------------------
*/
int
EFVisitDevs(devProc, cdata)
int (*devProc)();
ClientData cdata;
{
CallArg ca;
ca.ca_proc = devProc;
ca.ca_cdata = cdata;
return efVisitDevs(&efFlatContext, (ClientData) &ca);
}
/*
* Procedure to visit recursively all devs in the design.
* Does all the work of EFVisitDevs() above.
*
* Results:
* Returns 0 to keep efHierSrUses going.
*
* Side effects:
* Calls the client procedure (*ca->ca_proc)().
*/
int
efVisitDevs(hc, ca)
HierContext *hc;
CallArg *ca;
{
Def *def = hc->hc_use->use_def;
Dev *dev;
float scale;
Transform t;
HashSearch hs;
HashEntry *he;
if (def->def_flags & DEF_SUBCIRCUIT) return 0;
/* Recursively visit devs in our children first */
if (efHierSrUses(hc, efVisitDevs, (ClientData) ca))
return 1;
scale = (efScaleChanged && def->def_scale != 1.0) ? def->def_scale : 1.0;
t = hc->hc_trans;
/* Visit our own devices */
HashStartSearch(&hs);
while (he = HashNext(&def->def_devs, &hs))
{
dev = (Dev *)HashGetValue(he);
if (efDevKilled(dev, hc->hc_hierName))
continue;
if ((*ca->ca_proc)(dev, hc, scale, &t, ca->ca_cdata))
return 1;
}
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* efDevKilled --
*
* Check all of the nodes to which the dev 'dev' is connected (its
* hierarchical prefix is hc->hc_hierName). If any of these nodes
* have been killed, then the dev is also killed.
*
* Results:
* TRUE if the dev is connected to a killed node, FALSE if it's ok.
*
* Side effects:
* None.
*
* ----------------------------------------------------------------------------
*/
bool
efDevKilled(dev, prefix)
Dev *dev;
HierName *prefix;
{
HierName *suffix;
HashEntry *he;
EFNodeName *nn;
int n;
for (n = 0; n < dev->dev_nterm; n++)
{
suffix = dev->dev_terms[n].dterm_node->efnode_name->efnn_hier;
he = EFHNConcatLook(prefix, suffix, "kill");
if (he && (nn = (EFNodeName *) HashGetValue(he))
&& (nn->efnn_node->efnode_flags & EF_KILLED))
return TRUE;
}
return FALSE;
}
/*
* ----------------------------------------------------------------------------
*
* efDevFixLW --
*
* Called for any devs that have gate attributes; these attributes may
* specify the L and W of the dev explicitly. The attributes will be
* of the form ext:l=value or ext:w=value, where value is either numerical
* or symbolic; if symbolic the symbol must have been defined via efSymAdd().
* If the value is symbolic but wasn't defined by efSymAdd(), it's ignored.
* The variables *pL and *pW are changed to reflect the new L and W as
* appropriate.
*
* Results:
* None.
*
* Side effects:
* See above.
*
* ----------------------------------------------------------------------------
*/
void
efDevFixLW(attrs, pL, pW)
char *attrs;
int *pL, *pW;
{
char *cp, *ep;
char attrName, savec;
int value;
cp = attrs;
while (cp && *cp)
{
if (*cp != 'e' || strncmp(cp, "ext:", 4) != 0)
goto skip;
cp += 4;
if (*cp && cp[1] == '=')
{
switch (*cp)
{
case 'w': case 'W':
attrName = 'w';
goto both;
case 'l': case 'L':
attrName = 'l';
both:
cp += 2;
for (ep = cp; *ep && *ep != ','; ep++)
/* Nothing */;
savec = *ep;
*ep = '\0';
if (StrIsInt(cp)) value = atoi(cp);
else if (!efSymLook(cp, &value)) goto done;
if (attrName == 'w')
*pW = value;
else if (attrName == 'l')
*pL = value;
done:
*ep = savec;
}
}
skip:
/* Skip to next attribute */
while (*cp && *cp++ != ',')
/* Nothing */;
}
}
/*
* ----------------------------------------------------------------------------
*
* EFVisitResists --
*
* Visit all the resistors in the circuit.
* Must be called after EFFlatBuild().
* For each resistor in the circuit, call the user-supplied procedure
* (*resProc)(), which should be of the following form, where hn1 and
* hn2 are the HierNames of the two nodes connected by the resistor.
*
* (*resProc)(hn1, hn2, resistance, cdata)
* HierName *hn1, *hn2;
* int resistance;
* ClientData cdata;
* {
* }
*
* The procedure should return 0 normally, or 1 to abort the
* search.
*
* We ensure that no resistors connected to killed nodes are passed
* to this procedure.
*
* Results:
* Returns 0 if terminated normally, or 1 if the search
* was aborted.
*
* Side effects:
* Whatever (*resProc)() does.
*
* ----------------------------------------------------------------------------
*/
int
EFVisitResists(resProc, cdata)
int (*resProc)();
ClientData cdata;
{
CallArg ca;
ca.ca_proc = resProc;
ca.ca_cdata = cdata;
return efVisitResists(&efFlatContext, (ClientData) &ca);
}
/*
* Procedure to visit recursively all resistors in the design.
* Does all the work of EFVisitResists() above.
*
* Results:
* Returns 0 to keep efHierSrUses going.
*
* Side effects:
* Calls the client procedure (*ca->ca_proc)().
*/
extern int efVisitSingleResist();
int
efVisitResists(hc, ca)
HierContext *hc;
CallArg *ca;
{
Def *def = hc->hc_use->use_def;
Connection *res;
/* Ignore subcircuits */
if (def->def_flags & DEF_SUBCIRCUIT) return 0;
/* Recursively visit resistors in our children first */
if (efHierSrUses(hc, efVisitResists, (ClientData) ca))
return 1;
/* Visit our own resistors */
for (res = def->def_resistors; res; res = res->conn_next)
{
/* Special case for speed if no arraying info */
if (res->conn_1.cn_nsubs == 0)
{
if (efVisitSingleResist(hc, res->conn_name1, res->conn_name2,
res, ca))
return 1;
}
else if (efHierSrArray(hc, res, efVisitSingleResist, (ClientData) ca))
return 1;
}
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* efVisitSingleResist --
*
* Visit a resistor of res->conn_res milliohms between the nodes
* 'name1' and 'name2' (text names, not hierarchical names). Don't
* process the resistor if either terminal is a killed node.
*
* Results:
* Whatever the user-supplied procedure (*ca->ca_proc)() returns
* (type int).
*
* Side effects:
* Calls the user-supplied procedure.
*
* ----------------------------------------------------------------------------
*/
int
efVisitSingleResist(hc, name1, name2, res, ca)
HierContext *hc; /* Contains hierarchical pathname to cell */
char *name1, *name2; /* Names of nodes connecting to resistor */
Connection *res; /* Contains resistance to add */
CallArg *ca;
{
EFNode *n1, *n2;
HashEntry *he;
if ((he = EFHNLook(hc->hc_hierName, name1, "resist(1)")) == NULL)
return 0;
n1 = ((EFNodeName *) HashGetValue(he))->efnn_node;
if (n1->efnode_flags & EF_KILLED)
return 0;
if ((he = EFHNLook(hc->hc_hierName, name2, "resist(2)")) == 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;
return (*ca->ca_proc)(n1->efnode_name->efnn_hier,
n2->efnode_name->efnn_hier,
res->conn_res, ca->ca_cdata);
}
/*
* ----------------------------------------------------------------------------
*
* EFVisitCaps --
*
* Visit all the capacitors built up by efFlatCaps.
* Calls the user-provided procedure (*capProc)()
* which should be of the following format:
*
* (*capProc)(hierName1, hierName2, cap, cdata)
* HierName *hierName1, *hierName2;
* EFCapValue cap;
* ClientData cdata;
* {
* }
*
* Here cap is the capacitance in attofarads.
*
* Results:
* Returns 1 if the client procedure returned 1;
* otherwise returns 0.
*
* Side effects:
* Calls the user-provided procedure (*capProc)().
*
* ----------------------------------------------------------------------------
*/
int
EFVisitCaps(capProc, cdata)
int (*capProc)();
ClientData cdata;
{
HashSearch hs;
HashEntry *he;
EFCoupleKey *ck;
EFCapValue cap;
HashStartSearch(&hs);
while (he = HashNext(&efCapHashTable, &hs))
{
cap = CapHashGetValue(he);
ck = (EFCoupleKey *) he->h_key.h_words;
if ((*capProc)(ck->ck_1->efnode_name->efnn_hier,
ck->ck_2->efnode_name->efnn_hier,
(double) cap, cdata))
return 1;
}
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* EFVisitNodes --
*
* Procedure to visit all flat nodes in the circuit.
* For each node, calls the procedure (*nodeProc)(),
* which should be of the following form:
*
* (*nodeProc)(node, r, c, cdata)
* EFNode *node;
* int r;
* EFCapValue c;
* ClientData cdata;
* {
* }
*
* Where 'r' and 'c' are the lumped resistance estimate
* and capacitance to ground, in milliohms and attofarads
* respectively. When either falls below the threshold
* for output, they are passed as 0.
*
* Results:
* Returns 1 if (*nodeProc)() returned 1 to abort the
* search; otherwise, returns 0.
*
* Side effects:
* Calls (*nodeProc)().
*
* ----------------------------------------------------------------------------
*/
int
EFVisitNodes(nodeProc, cdata)
int (*nodeProc)();
ClientData cdata;
{
EFNode *node;
EFNodeName *nn;
HierName *hierName;
EFCapValue cap;
int res;
for (node = (EFNode *) efNodeList.efnode_next;
node && (node != &efNodeList);
node = (EFNode *) node->efnode_next)
{
res = EFNodeResist(node);
cap = node->efnode_cap;
hierName = (HierName *) node->efnode_name->efnn_hier;
if (EFCompat)
{
if (EFHNIsGND(hierName))
cap = 0;
}
else
{
if (node->efnode_flags & EF_GLOB_SUBS_NODE)
cap = 0;
}
if (efWatchNodes)
{
for (nn = node->efnode_name; nn; nn = nn->efnn_next)
if (HashLookOnly(&efWatchTable, (char *) nn->efnn_hier))
{
TxPrintf("Equivalent nodes:\n");
for (nn = node->efnode_name; nn; nn = nn->efnn_next)
TxPrintf("\t%s\n", EFHNToStr(nn->efnn_hier));
break;
}
}
if (node->efnode_flags & EF_KILLED)
continue;
if ((*nodeProc)(node, res, (double) cap, cdata))
return 1;
}
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* EFNodeResist --
*
* The input to this procedure is a pointer to a EFNode.
* Its resistance is computed from the area and perimeter stored
* in the array efnode_pa.
*
* We approximate the resistive region as a collection of rectangles
* of width W and length L, one for each set of layers having a different
* sheet resistivity. We do so by noting that for a rectangle,
*
* Area = L * W
* Perimeter = 2 * (L + W)
*
* Solving the two simultaneous equations for L yields the following
* quadratic:
*
* 2 * (L**2) - Perimeter * L + 2 * Area = 0
*
* Solving this quadratic for L, the longer dimension, we get
*
* L = (Perimeter + S) / 4
*
* where
*
* S = sqrt( (Perimeter**2) - 16 * Area )
*
* The smaller dimension is W, ie,
*
* W = (Perimeter - S) / 4
*
* The resistance is L / W squares:
*
* Perimeter + S
* R = -------------
* Perimeter - S
*
* Results:
* Returns the resistance.
*
* Side effects:
* None.
*
* ----------------------------------------------------------------------------
*/
int
EFNodeResist(node)
EFNode *node;
{
int n, perim, area;
float s, fperim;
double v, dresist;
int resist;
resist = 0;
for (n = 0; n < efNumResistClasses; n++)
{
area = node->efnode_pa[n].pa_area;
perim = node->efnode_pa[n].pa_perim;
if (area > 0 && perim > 0)
{
v = (double) perim * (double) perim - 16.0 * area;
/* Approximate by one square if v < 0; shouldn't happen! */
if (v < 0.0) s = 0.0; else s = sqrt(v);
fperim = (float) perim;
dresist = ((double)fperim + (double)s)/((double)fperim - (double)s) * efResists[n];
if (dresist + (double) resist > (double) INT_MAX)
resist = INT_MAX;
else
resist += dresist;
}
}
return (resist);
}
/*
* ----------------------------------------------------------------------------
*
* EFLookDist --
*
* Look for the Distance between two points given by their HierNames.
*
* Results:
* TRUE if a distance was found, FALSE if not.
*
* Side effects:
* Sets *pMinDist and *pMaxDist to the min and max distances
* if found.
*
* ----------------------------------------------------------------------------
*/
bool
EFLookDist(hn1, hn2, pMinDist, pMaxDist)
HierName *hn1, *hn2;
int *pMinDist, *pMaxDist;
{
Distance distKey, *dist;
HashEntry *he;
if (EFHNBest(hn1, hn2))
{
distKey.dist_1 = hn1;
distKey.dist_2 = hn2;
}
else
{
distKey.dist_1 = hn2;
distKey.dist_2 = hn1;
}
he = HashLookOnly(&efDistHashTable, (char *) &distKey);
if (he == NULL)
return FALSE;
dist = (Distance *) HashGetValue(he);
*pMinDist = dist->dist_min;
*pMaxDist = dist->dist_max;
return TRUE;
}
/*
* ----------------------------------------------------------------------------
*
* EFHNOut --
*
* Output a hierarchical node name.
* The flags in EFOutputFlags control whether global (!) or local (#)
* suffixes are to be trimmed.
*
* Results:
* None.
*
* Side effects:
* Writes to the files 'outf'.
*
* ----------------------------------------------------------------------------
*/
void
EFHNOut(hierName, outf)
HierName *hierName;
FILE *outf;
{
bool trimGlob, trimLocal, convComma, convBrackets;
char *cp, c;
if (hierName->hn_parent) efHNOutPrefix(hierName->hn_parent, outf);
if (EFOutputFlags)
{
cp = hierName->hn_name;
trimGlob = (EFOutputFlags & EF_TRIMGLOB);
trimLocal = (EFOutputFlags & EF_TRIMLOCAL);
convComma = (EFOutputFlags & EF_CONVERTCOMMA);
convBrackets = (EFOutputFlags & EF_CONVERTBRACKETS);
while (c = *cp++)
{
if (*cp)
{
if (c == ',')
{
if (convComma)
putc('|', outf);
}
else if (convBrackets && ((c == '[') || (c == ']')))
putc('_', outf);
else
putc(c, outf);
}
else switch (c)
{
case '!': if (!trimGlob) (void) putc(c, outf); break;
case '#': if (trimLocal) break;
default: (void) putc(c, outf); break;
}
}
}
else (void) fputs(hierName->hn_name, outf);
}
void
efHNOutPrefix(hierName, outf)
HierName *hierName;
FILE *outf;
{
char *cp, c;
if (hierName->hn_parent)
efHNOutPrefix(hierName->hn_parent, outf);
cp = hierName->hn_name;
while (c = *cp++)
putc(c, outf);
putc('/', outf);
}