Files
magic/extflat/EFvisit.c
T
R. Timothy Edwards d3a0228958 Added a new device type "dsubcircuit" which basically behaves like
"csubcircuit" but swaps the first two pins (with the device
identifier layer becoming the 2nd pin and the other terminal the
first), which is needed for n-type diodes modeled as subcircuits
where the subcircuit pin order matches the order of pins for a
SPICE n-type diode component.  Previously "msubcircuit" was used
for this purpose, but will calculate the wrong L and W.  While
use of L and W for diodes is rare, this device type also works for
reversed capacitors (where the bottom or non-identifying layer
terminal is in the first pin position of the subcircuit).
2025-10-07 16:45:22 -04:00

940 lines
22 KiB
C

/*
* 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 "database/database.h" /* before EFint.h for ArrayInfo */
#include "extflat/EFint.h" /* also has ArrayInfo */
#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_VERILOGA:
case DEV_MSUBCKT:
case DEV_RSUBCKT:
case DEV_DSUBCKT:
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
* see also typedef cb_extflat_visitdevs_t:
*
* int (*devProc)(
* Dev *dev,
* HierContext *hc,
* 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(
const cb_extflat_visitdevs_t devProc,
ClientData cdata)
{
CallArg ca;
ca.ca_proc = (int (*)()) 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;
const cb_extflat_visitdevs_t ca_visitdevs_proc = (cb_extflat_visitdevs_t) ca->ca_proc; /* FIXME temporary */
if ((*ca_visitdevs_proc)(dev, hc, scale, &t, ca->ca_cdata)) /* @invoke cb_extflat_visitdevs_t */
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
* see also typedef cb_extflat_visitresists_t:
*
* int (*resProc)(
* const HierName *hn1,
* const HierName *hn2,
* float 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(
const cb_extflat_visitresists_t resProc,
ClientData cdata)
{
CallArg ca;
ca.ca_proc = (int (*)()) resProc;
ca.ca_cdata = cdata;
return efVisitResists(&efFlatContext, &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(HierContext *hc, const char *name1, const char *name2,
Connection *res, ClientData cdata); /* @typedef cb_extflat_hiersrarray_t (CallArg*) */
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.
*
* ----------------------------------------------------------------------------
*/
/* @typedef cb_extflat_hiersrarray_t (CallArg*) */
int
efVisitSingleResist(
HierContext *hc, /* Contains hierarchical pathname to cell */
const char *name1,
const char *name2, /* Names of nodes connecting to resistor */
Connection *res, /* Contains resistance to add */
ClientData cdata)
{
CallArg *ca = (CallArg *)CD2PTR(cdata);
const EFNode *n1, *n2;
HashEntry *he;
if ((he = EFHNLook(hc->hc_hierName, name1, "resist(1)")) == NULL)
return 0;
n1 = ((const 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 = ((const 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;
const cb_extflat_visitresists_t ca_visitresists_proc = (cb_extflat_visitresists_t) ca->ca_proc; /* FIXME temporary */
return (*ca_visitresists_proc)(n1->efnode_name->efnn_hier,
n2->efnode_name->efnn_hier,
res->conn_res, ca->ca_cdata); /* @invoke cb_extflat_visitresists_t */
}
/*
* ----------------------------------------------------------------------------
*
* 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);
}