1035 lines
29 KiB
C
1035 lines
29 KiB
C
/*
|
|
* ExtLength.c --
|
|
*
|
|
* Circuit extraction.
|
|
* Computation of the length of the shortest path from a driver
|
|
* to a receiver. This information is intended to be used in
|
|
* computing delays of signals propagating in a transmission
|
|
* line mode, where delay is proportional to the driver-to-receiver
|
|
* distance.
|
|
*
|
|
* *********************************************************************
|
|
* * 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. *
|
|
* *********************************************************************
|
|
* Lawrence Livermore National Laboratory
|
|
*/
|
|
|
|
#ifndef lint
|
|
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/extract/ExtLength.c,v 1.3 2009/09/10 20:32:52 tim Exp $";
|
|
#endif /* not lint */
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <math.h>
|
|
|
|
#include "utils/magic.h"
|
|
#include "utils/geometry.h"
|
|
#include "utils/geofast.h"
|
|
#include "tiles/tile.h"
|
|
#include "utils/hash.h"
|
|
#include "database/database.h"
|
|
#include "utils/malloc.h"
|
|
#include "textio/textio.h"
|
|
#include "debug/debug.h"
|
|
#include "extract/extract.h"
|
|
#include "extract/extractInt.h"
|
|
#include "utils/signals.h"
|
|
#include "windows/windows.h"
|
|
#include "dbwind/dbwind.h"
|
|
#include "select/select.h"
|
|
#include "utils/styles.h"
|
|
#include "utils/stack.h"
|
|
#include "utils/main.h"
|
|
#include "utils/utils.h"
|
|
|
|
/* Temporary cell for holding an entire flattened net */
|
|
CellDef *extPathDef = NULL;
|
|
CellUse *extPathUse = NULL;
|
|
|
|
/*
|
|
* Tables that hold information describing each driver and receiver
|
|
* in the circuit.
|
|
*
|
|
* Each entry in the driver table will be initially 0, and later
|
|
* will be made to point to a list of hierarchical labels (i.e.,
|
|
* specially constructed labels whose text field contains a full
|
|
* hierarchical pathname) where the driver label appears in the
|
|
* design.
|
|
*
|
|
* Each entry in the receiver table is initially 0, and is set to 1
|
|
* when that receiver is processed as being connected to some driver.
|
|
*/
|
|
HashTable extDriverHash;
|
|
HashTable extReceiverHash;
|
|
|
|
/* Initial size of the hash tables used in this file */
|
|
#define INITHASHSIZE 32
|
|
|
|
/* Max length of a hierarchical name */
|
|
#define MAXNAMESIZE 2048
|
|
|
|
/*
|
|
* List of labels being built up hierarchically by extLengthYank().
|
|
* This is used within this file to pass data down to filter
|
|
* procedures of search functions.
|
|
*/
|
|
static Label *extLengthLabelList;
|
|
|
|
/*
|
|
* Used to hold information while tracing out paths.
|
|
* Passed directly down to filter procedures of search functions.
|
|
*/
|
|
struct extPathArg
|
|
{
|
|
int epa_min, epa_max;
|
|
int epa_pNum;
|
|
Label *epa_lab1, *epa_lab2;
|
|
};
|
|
|
|
/*
|
|
* Additional information passed down to extPathFloodFunc()
|
|
*/
|
|
struct extPathFloodArg
|
|
{
|
|
int epfa_distance;
|
|
Point *epfa_srcPoint;
|
|
Tile *epfa_srcTile;
|
|
Rect epfa_srcArea;
|
|
struct extPathArg *epfa_epa;
|
|
};
|
|
|
|
/* Used to mark tiles during path tracing */
|
|
#define MARKED (1)
|
|
|
|
/* Forward declarations */
|
|
Label *extPathLabel();
|
|
Label *extLengthYank();
|
|
int extLengthLabels();
|
|
int extLengthLabelsFunc();
|
|
int extPathPairFunc();
|
|
int extPathResetClient();
|
|
int extPathFloodFunc();
|
|
|
|
void extLengthInit();
|
|
void extPathPairDistance();
|
|
void extPathFlood();
|
|
void extPathFloodTile();
|
|
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* ExtSetDriver --
|
|
* ExtSetReceiver --
|
|
*
|
|
* Add a terminal name to either the driver or the receiver table.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Adds an entry to the hash tables extDriverHash or extReceiverHash
|
|
* respectively. The initial value of the hash entry is 0.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
ExtSetDriver(name)
|
|
char *name;
|
|
{
|
|
HashEntry *he;
|
|
|
|
he = HashFind(&extDriverHash, name);
|
|
HashSetValue(he, 0);
|
|
}
|
|
|
|
void
|
|
ExtSetReceiver(name)
|
|
char *name;
|
|
{
|
|
HashEntry *he;
|
|
|
|
he = HashFind(&extReceiverHash, name);
|
|
HashSetValue(he, 0);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* ExtLengthClear --
|
|
*
|
|
* Kill extDriverHash and extReceiverHash, and re-initialize them.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* See above.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
ExtLengthClear()
|
|
{
|
|
HashKill(&extDriverHash);
|
|
HashKill(&extReceiverHash);
|
|
extLengthInit();
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* extLengthInit --
|
|
*
|
|
* Allocates and initializes the hash tables extDriverHash
|
|
* and extReceiverHash.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* See above.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
extLengthInit()
|
|
{
|
|
HashInit(&extDriverHash, INITHASHSIZE, 0);
|
|
HashInit(&extReceiverHash, INITHASHSIZE, 0);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* extLength --
|
|
*
|
|
* Using the driver and receiver tables, compute the distances from
|
|
* each driver to each receiver on its (flattened) net. Output to
|
|
* the file 'f' lines of the following format:
|
|
*
|
|
* distance drivername receivername min max
|
|
*
|
|
* e.g,
|
|
*
|
|
* distance a/b/cOUT d/e/fIN 1234 2345
|
|
*
|
|
* The units of distance are lambda.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Outputs to the FILE 'f'.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
extLength(rootUse, f)
|
|
CellUse *rootUse; /* The names stored in the driver and receiver tables
|
|
* should all be relative to this root cell. It is
|
|
* the responsibility of the caller to ensure this.
|
|
*/
|
|
FILE *f; /* Open output file */
|
|
{
|
|
Label *dList, *rList, *dLab, *rLab;
|
|
int min, max;
|
|
HashSearch hs;
|
|
HashEntry *he;
|
|
|
|
/* Create the yank cell if it doesn't already exist */
|
|
if (extPathDef == (CellDef *) NULL)
|
|
DBNewYank("__PATHYANK__", &extPathUse, &extPathDef);
|
|
|
|
/*
|
|
* Initialize the entries in the driver table to point to
|
|
* a list of hierarchical labels describing the locations
|
|
* where that driver appears. These labels should all
|
|
* be from a single cell.
|
|
*/
|
|
HashStartSearch(&hs);
|
|
while ((he = HashNext(&extDriverHash, &hs)))
|
|
{
|
|
dList = extPathLabel(rootUse, he->h_key.h_name);
|
|
HashSetValue(he, (ClientData) dList);
|
|
}
|
|
|
|
/*
|
|
* Main loop.
|
|
* For each driver, find all the receivers connected to it, and
|
|
* then compute and output the distance to each.
|
|
*/
|
|
HashStartSearch(&hs);
|
|
while ((he = HashNext(&extDriverHash, &hs)))
|
|
{
|
|
/* Ignore drivers whose labels couldn't be found */
|
|
dList = (Label *) HashGetValue(he);
|
|
if (dList == (Label *) NULL)
|
|
continue;
|
|
|
|
/*
|
|
* Flatten this net into extPathDef.
|
|
* Find all the labels that overlap material we yanked and
|
|
* whose names appear in the receiver table. Build a hierarchical
|
|
* list of these labels.
|
|
*/
|
|
rList = extLengthYank(rootUse, dList);
|
|
|
|
/*
|
|
* Now compute the distance from the driver label to
|
|
* each of the receivers. Free each driver label
|
|
* as it is processed.
|
|
*/
|
|
for (dLab = dList; dLab; dLab = dLab->lab_next)
|
|
{
|
|
for (rLab = rList; rLab; rLab = rLab->lab_next)
|
|
{
|
|
extPathPairDistance(dLab, rLab, &min, &max);
|
|
fprintf(f, "distance %s %s %d %d\n",
|
|
dLab->lab_text, rLab->lab_text, min, max);
|
|
}
|
|
|
|
/* Free the driver label */
|
|
freeMagic((char *) dLab);
|
|
}
|
|
|
|
/* Free all the receiver labels built up during this iteration */
|
|
for (rLab = rList; rLab; rLab = rLab->lab_next)
|
|
freeMagic((char *) rLab);
|
|
|
|
/* For sanity since we've freed the driver label list */
|
|
HashSetValue(he, (ClientData) NULL);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* extLengthYank --
|
|
*
|
|
* Trace out all material connected to each location on the label
|
|
* list 'labList', both in the root cell use->cu_def and hierarchically
|
|
* in all of its children. Flatten this material into the cell
|
|
* 'extPathDef'.
|
|
*
|
|
* Results:
|
|
* Returns a list of the hierarchical labels whose names appear
|
|
* in the receiver table (extReceiverHash) that are overlapped
|
|
* by material we yanked.
|
|
*
|
|
* Side effects:
|
|
* Adds material to extPathDef after erasing its previous contents.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
Label *
|
|
extLengthYank(use, labList)
|
|
CellUse *use; /* Cell whose material is to be traced */
|
|
Label *labList; /* List of labels whose attached net is to be traced */
|
|
{
|
|
SearchContext scx;
|
|
char mesg[512];
|
|
Label *lab;
|
|
int pNum;
|
|
|
|
/* Eliminate previous contents of yank cell */
|
|
if (DebugIsSet(extDebugID, extDebLength))
|
|
{
|
|
DBReComputeBbox(extPathDef);
|
|
DBWAreaChanged(extPathDef, &extPathDef->cd_bbox, DBW_ALLWINDOWS,
|
|
&DBAllButSpaceBits);
|
|
}
|
|
DBCellClearDef(extPathDef);
|
|
|
|
/*
|
|
* Search out all material connected to each label.
|
|
* Bloat the label's rectangle to consider even material
|
|
* that only touches the label.
|
|
*/
|
|
for (lab = labList; lab; lab = lab->lab_next)
|
|
{
|
|
if (lab->lab_type == TT_SPACE)
|
|
continue;
|
|
scx.scx_use = use;
|
|
scx.scx_trans = GeoIdentityTransform;
|
|
GEO_EXPAND(&lab->lab_rect, 1, &scx.scx_area);
|
|
DBTreeCopyConnect(&scx, &DBConnectTbl[lab->lab_type], 0,
|
|
DBConnectTbl, &TiPlaneRect, SEL_DO_LABELS, extPathUse);
|
|
}
|
|
|
|
if (DebugIsSet(extDebugID, extDebLength))
|
|
{
|
|
DBReComputeBbox(extPathDef);
|
|
DBWAreaChanged(extPathDef, &extPathDef->cd_bbox, DBW_ALLWINDOWS,
|
|
&DBAllButSpaceBits);
|
|
WindUpdate();
|
|
(void) sprintf(mesg, "Yanked %s",
|
|
labList ? labList->lab_text : "(NONE)");
|
|
TxMore(mesg);
|
|
}
|
|
|
|
/*
|
|
* Now find all the labels appearing in the receiver table that are
|
|
* overlapped by any of the material we just yanked. This may not
|
|
* be the most efficient way to do things: we're searching the label
|
|
* list of at least the root cell every time we process a tile in
|
|
* the yanked net. The hope is that this is still fast enough.
|
|
* Possibly a better way would be to identify the CELLS that are
|
|
* overlapped by tiles in the net, and then to process each label
|
|
* list just once.
|
|
*/
|
|
extLengthLabelList = (Label *) NULL;
|
|
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
|
|
{
|
|
(void) DBSrPaintArea((Tile *) NULL, extPathDef->cd_planes[pNum],
|
|
&TiPlaneRect, &DBAllButSpaceBits, extLengthLabels,
|
|
(ClientData) use);
|
|
}
|
|
|
|
return (extLengthLabelList);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* extLengthLabels --
|
|
*
|
|
* Called for each paint tile in extPathDef to find all the labels
|
|
* in the original search tree that overlap the tile. We bloat each
|
|
* tile to the top and right by one unit to be certain to catch
|
|
* labels appearing on these edges.
|
|
*
|
|
* Results:
|
|
* Always returns 0.
|
|
*
|
|
* Side effects:
|
|
* May cons hierarchical labels (newly created labels whose text
|
|
* is the full hierarchical path of a label in a subcell) to the
|
|
* list extLengthLabelList.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
extLengthLabels(tile, rootUse)
|
|
Tile *tile; /* Some tile in extPathDef */
|
|
CellUse *rootUse; /* The original root cell */
|
|
{
|
|
char name[MAXNAMESIZE];
|
|
TileTypeBitMask mask;
|
|
TerminalPath tpath;
|
|
SearchContext scx;
|
|
|
|
/* Grow the search area to include labels on the top and right */
|
|
TITORECT(tile, &scx.scx_area);
|
|
scx.scx_area.r_xtop++;
|
|
scx.scx_area.r_ytop++;
|
|
scx.scx_use = rootUse;
|
|
scx.scx_trans = GeoIdentityTransform;
|
|
tpath.tp_first = tpath.tp_next = name;
|
|
tpath.tp_last = &name[sizeof name - 2];
|
|
|
|
TTMaskSetOnlyType(&mask, TiGetType(tile));
|
|
(void) DBTreeSrLabels(&scx, &mask, 0, &tpath, TF_LABEL_ATTACH,
|
|
extLengthLabelsFunc, (ClientData) NULL);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* extLengthLabelsFunc --
|
|
*
|
|
* Called for each label found while searching hierarchically the area
|
|
* beneath one of the tiles in extPathDef. If the hierarchical label
|
|
* name matches a name appearing in the receiver table (extReceiverHash)
|
|
* we cons a newly created hierarchical label onto the front of
|
|
* extLengthLabelList.
|
|
*
|
|
* Results:
|
|
* Always returns 0.
|
|
*
|
|
* Side effects:
|
|
* May cons hierarchical labels (newly created labels whose text
|
|
* is the full hierarchical path of a label in a subcell) to the
|
|
* list extLengthLabelList. Also, for each receiver label we
|
|
* did find, leaves a value of 1 (via HashSetValue()) in the
|
|
* receiver hash table, so we can know at the end which receiver
|
|
* labels weren't driven by any driver.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
extLengthLabelsFunc(scx, label, tpath)
|
|
SearchContext *scx; /* Where in the search tree we are */
|
|
Label *label; /* The label itself */
|
|
TerminalPath *tpath; /* Identifies hierarchical prefix for label.
|
|
* The full hierarchical pathname will be the
|
|
* concatenation of the string tpath->tp_first
|
|
* and the string label->lab_text.
|
|
*/
|
|
{
|
|
Label *newLab;
|
|
HashEntry *he;
|
|
int len;
|
|
|
|
/* Concatenate the prefix and label to get the full hierarchical name */
|
|
(void) strcpy(tpath->tp_next, label->lab_text);
|
|
|
|
/* Only bother with labels in the receiver table */
|
|
he = HashLookOnly(&extReceiverHash, tpath->tp_first);
|
|
if (he == NULL)
|
|
return (0);
|
|
|
|
/* Mark this receiver as being seen */
|
|
HashSetValue(he, (ClientData) 1);
|
|
|
|
/* Allocate and fill in a new hierarchical label */
|
|
len = strlen(tpath->tp_first) + sizeof (Label)
|
|
- sizeof newLab->lab_text + 1;
|
|
newLab = (Label *) mallocMagic((unsigned) len);
|
|
newLab->lab_type = label->lab_type;
|
|
newLab->lab_just = GeoTransPos(&scx->scx_trans, label->lab_just);
|
|
GeoTransRect(&scx->scx_trans, &label->lab_rect, &newLab->lab_rect);
|
|
newLab->lab_next = extLengthLabelList;
|
|
extLengthLabelList = newLab;
|
|
(void) strcpy(newLab->lab_text, tpath->tp_first);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* extPathLabel --
|
|
*
|
|
* Find all the locations of labels matching the hierarchical
|
|
* name 'text' and return a linked list of newly allocated
|
|
* labels with the full hierarchical name.
|
|
*
|
|
* Results:
|
|
* Returns a pointer to the newly allocated Label list
|
|
*
|
|
* Side effects:
|
|
* Allocates memory.
|
|
* Complains if the label couldn't be found.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
Label *
|
|
extPathLabel(use, text)
|
|
CellUse *use;
|
|
char *text;
|
|
{
|
|
int extPathLabelFunc();
|
|
Label *lab;
|
|
|
|
lab = (Label *) NULL;
|
|
(void) DBSrLabelLoc(use, text, extPathLabelFunc, (ClientData) &lab);
|
|
if (lab == NULL)
|
|
TxError("Can't find terminal \"%s\"\n", text);
|
|
return (lab);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* extPathLabelFunc --
|
|
*
|
|
* Called via DBSrLabelLoc() on behalf of extPathLabel() above.
|
|
* Creates a Label whose text is the string pointed to by text,
|
|
* whose lab_rect is *rect, and whose type is childLab->lab_type.
|
|
* Cons it onto the front of the list *pLabList.
|
|
*
|
|
* Results:
|
|
* Always returns 0.
|
|
*
|
|
* Side effects:
|
|
* Allocates memory.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
extPathLabelFunc(rect, text, childLab, pLabList)
|
|
Rect *rect; /* Transformed location of the label */
|
|
char *text; /* Full hierarchical name of the label */
|
|
Label *childLab; /* The label itself */
|
|
Label **pLabList; /* Cons the newly allocated label onto the front of
|
|
* this list.
|
|
*/
|
|
{
|
|
Label *lab;
|
|
int len;
|
|
|
|
len = strlen(text) + sizeof (Label) - sizeof lab->lab_text + 1;
|
|
lab = (Label *) mallocMagic((unsigned) len);
|
|
lab->lab_type = childLab->lab_type;
|
|
lab->lab_rect = *rect;
|
|
lab->lab_just = GEO_CENTER; /* Irrelevant */
|
|
lab->lab_next = *pLabList;
|
|
|
|
/* Cons to front of list */
|
|
*pLabList = lab;
|
|
(void) strcpy(lab->lab_text, text);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* extPathPairDistance --
|
|
*
|
|
* Compute the actual delay between two locations 'lab1' and 'lab2'.
|
|
* The delay is computed using optimistic and pessimistic assumptions
|
|
* to get a min and max delay respectively.
|
|
*
|
|
* Algorithm:
|
|
* The algorithm we use here is simplistic, assuming that all
|
|
* wires are of essentially uniform width. We use a tile-based
|
|
* depth-first flooding algorithm that computes a delay on the
|
|
* forward pass based on the wire's length.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Stores the min and max delay in *pMin and *pMax respectively.
|
|
* Uses the ti_client fields of the tiles in extPathDef, since
|
|
* these are used while tracing out paths.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
extPathPairDistance(lab1, lab2, pMin, pMax)
|
|
Label *lab1, *lab2;
|
|
int *pMin, *pMax;
|
|
{
|
|
struct extPathArg epa;
|
|
TileTypeBitMask mask;
|
|
PlaneMask pMask;
|
|
int pNum;
|
|
Rect r;
|
|
|
|
/* Skip if either type is space (sanity check) */
|
|
if (lab1->lab_type == TT_SPACE || lab2->lab_type == TT_SPACE)
|
|
return;
|
|
|
|
/* Include all tiles touching lab1 that are connected to it */
|
|
GEO_EXPAND(&lab1->lab_rect, 1, &r);
|
|
mask = DBConnectTbl[lab1->lab_type];
|
|
|
|
/*
|
|
* Find min and max delays, considering each plane that
|
|
* lab1 is connected to. Don't reset the ti_client fields
|
|
* until after we've found all paths.
|
|
*/
|
|
epa.epa_min = INFINITY;
|
|
epa.epa_max = MINFINITY;
|
|
epa.epa_lab1 = lab1;
|
|
epa.epa_lab2 = lab2;
|
|
pMask = DBTechTypesToPlanes(&mask);
|
|
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
|
|
if (PlaneMaskHasPlane(pMask, pNum))
|
|
{
|
|
epa.epa_pNum = pNum;
|
|
(void) DBSrPaintClient((Tile *) NULL, extPathDef->cd_planes[pNum],
|
|
&r, &mask, (ClientData) CLIENTDEFAULT,
|
|
extPathPairFunc, (ClientData) &epa);
|
|
}
|
|
|
|
/* Pass the min and max delay back to our caller */
|
|
*pMin = epa.epa_min;
|
|
*pMax = epa.epa_max;
|
|
|
|
/* Reset ti_client fields in tiles */
|
|
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
|
|
(void) DBSrPaintClient((Tile *) NULL, extPathDef->cd_planes[pNum],
|
|
&TiPlaneRect, &DBAllButSpaceBits, (ClientData) MARKED,
|
|
extPathResetClient, (ClientData) NULL);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
* extPathResetClient --
|
|
*
|
|
* Called by DBSrPaintClient() on behalf of extPathPairDistance()
|
|
* above to reset each tile's ti_client field to CLIENTDEFAULT.
|
|
*
|
|
* Results:
|
|
* Always returns 0.
|
|
*
|
|
* Side effects:
|
|
* Sets tile->ti_client to CLIENTDEFAULT.
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
extPathResetClient(tile)
|
|
Tile *tile;
|
|
{
|
|
TiSetClient(tile, CLIENTDEFAULT);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* extPathPairFunc --
|
|
*
|
|
* Called by DBSrPaintClient() on behalf of extPathPairDistance()
|
|
* above for each unprocessed tile (ti_client == CLIENTDEFAULT)
|
|
* overlapped by epa->epa_lab1 that is connected to it.
|
|
* Floods outward in depth-first search toward the destination
|
|
* epa->epa_lab2. Remembers the min and max delay to the
|
|
* destination in epa->epa_min and epa->epa_max.
|
|
*
|
|
* We use depth-first search instead of breadth-first because
|
|
* it's easier, we need to consider both the longest and shortest
|
|
* path, and we expect there to be only a few paths.
|
|
*
|
|
* Results:
|
|
* Returns 0 always.
|
|
*
|
|
* Side effects:
|
|
* See above.
|
|
* Marks the ti_client fields of the tiles we visit as MARKED.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
extPathPairFunc(tile, epa)
|
|
Tile *tile;
|
|
struct extPathArg *epa;
|
|
{
|
|
Point startPoint;
|
|
Rect r;
|
|
|
|
/*
|
|
* Visit all this tile's neighbors.
|
|
* Our initial delay is zero, and our initial starting point
|
|
* is in the center of the overlap of epa->epa_lab1 and tile.
|
|
*/
|
|
TITORECT(tile, &r);
|
|
GEOCLIP(&r, &epa->epa_lab1->lab_rect);
|
|
startPoint.p_x = (r.r_xtop + r.r_xbot) / 2;
|
|
startPoint.p_y = (r.r_ytop + r.r_ybot) / 2;
|
|
extPathFlood(tile, &startPoint, 0, epa);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* extPathFlood --
|
|
*
|
|
* Flood from a tile to all its connected and unprocessed neighbors.
|
|
* As we flood to each neighbor, we estimate a delay for the increment
|
|
* from the point 'p' (usually on the center of 'tile', and at distance
|
|
* 'distance' from the starting point) to the central point of the new
|
|
* tile. Contacts are processed in a similar way, except the point 'p'
|
|
* doesn't change.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* See above.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
extPathFlood(tile, p, distance, epa)
|
|
Tile *tile; /* Tile whose neighbors we are to visit */
|
|
Point *p; /* Usually at center of 'tile' */
|
|
int distance; /* Distance to 'p' */
|
|
struct extPathArg *epa; /* Update epa_min and epa_max when we reach
|
|
* the destination epa_lab2.
|
|
*/
|
|
{
|
|
TileType type = TiGetType(tile);
|
|
Label *lab2 = epa->epa_lab2;
|
|
int pNum, newdistance;
|
|
PlaneMask pMask;
|
|
Tile *tp;
|
|
char mesg[512];
|
|
Point p2;
|
|
Rect r;
|
|
|
|
/* Mark the tile as being visited */
|
|
TiSetClientINT(tile, MARKED);
|
|
|
|
/*
|
|
* Are we at the destination yet?
|
|
* If so, compute final delay and just return.
|
|
* Update the min and max delay if necessary.
|
|
* Don't propagate to neighboring tiles since doing so
|
|
* can only lengthen the path.
|
|
*/
|
|
TITORECT(tile, &r);
|
|
if (DebugIsSet(extDebugID, extDebLength))
|
|
{
|
|
ShowRect(extPathDef, &r, STYLE_SOLIDHIGHLIGHTS);
|
|
TxMore("Visit tile");
|
|
ShowRect(extPathDef, &r, STYLE_ERASEHIGHLIGHTS);
|
|
}
|
|
|
|
if (GEO_TOUCH(&r, &lab2->lab_rect) && DBConnectsTo(type, lab2->lab_type))
|
|
{
|
|
/* Find distance to closest point in 'lab2->lab_rect' to 'p' */
|
|
p2 = *p;
|
|
GeoClipPoint(&p2, &lab2->lab_rect);
|
|
newdistance = extPathTileDist(p, &p2, tile, distance);
|
|
|
|
if (DebugIsSet(extDebugID, extDebLength))
|
|
{
|
|
(void) sprintf(mesg, "Reached destination, dist = %d", newdistance);
|
|
TxMore(mesg);
|
|
}
|
|
|
|
/* Update min and max distance */
|
|
if (newdistance < epa->epa_min) epa->epa_min = newdistance;
|
|
if (newdistance > epa->epa_max) epa->epa_max = newdistance;
|
|
return;
|
|
}
|
|
|
|
/* Walk around the perimeter to connected tiles */
|
|
|
|
/* TOP */
|
|
for (tp = RT(tile); RIGHT(tp) > LEFT(tile); tp = BL(tp))
|
|
if (TiGetClientINT(tp) != MARKED && DBConnectsTo(TiGetType(tp), type))
|
|
extPathFloodTile(tile, p, distance, tp, epa);
|
|
|
|
/* RIGHT */
|
|
for (tp = TR(tile); TOP(tp) > BOTTOM(tile); tp = LB(tp))
|
|
if (TiGetClientINT(tp) != MARKED && DBConnectsTo(TiGetType(tp), type))
|
|
extPathFloodTile(tile, p, distance, tp, epa);
|
|
|
|
/* BOTTOM */
|
|
for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp = TR(tp))
|
|
if (TiGetClientINT(tp) != MARKED && DBConnectsTo(TiGetType(tp), type))
|
|
extPathFloodTile(tile, p, distance, tp, epa);
|
|
|
|
/* LEFT */
|
|
for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp = RT(tp))
|
|
if (TiGetClientINT(tp) != MARKED && DBConnectsTo(TiGetType(tp), type))
|
|
extPathFloodTile(tile, p, distance, tp, epa);
|
|
|
|
/* Try connections to other planes */
|
|
if (DBIsContact(type))
|
|
{
|
|
int saveplane = epa->epa_pNum;
|
|
PlaneMask pMask = DBConnPlanes[type];
|
|
pMask &= ~(PlaneNumToMaskBit(epa->epa_pNum));
|
|
|
|
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
|
|
if (PlaneMaskHasPlane(pMask, pNum))
|
|
{
|
|
Plane *plane = extPathDef->cd_planes[pNum];
|
|
|
|
/* Find the point on the new plane */
|
|
tp = PlaneGetHint(plane);
|
|
GOTOPOINT(tp, &tile->ti_ll);
|
|
PlaneSetHint(plane, tp);
|
|
|
|
/* If not yet visited, process tp */
|
|
if (TiGetClient(tp) == CLIENTDEFAULT
|
|
&& DBConnectsTo(type, TiGetType(tp)))
|
|
{
|
|
epa->epa_pNum = pNum;
|
|
extPathFlood(tp, p, distance, epa);
|
|
}
|
|
}
|
|
epa->epa_pNum = saveplane;
|
|
}
|
|
|
|
/*
|
|
* The hairiest case is when this type connects to stuff on
|
|
* other planes, but isn't itself connected as a contact.
|
|
* For example, a CMOS pwell connects to diffusion of the
|
|
* same doping (p substrate diff). In a case like this,
|
|
* we need to search the entire AREA of the tile plus a
|
|
* 1-lambda halo to find everything it overlaps or touches
|
|
* on the other plane.
|
|
*/
|
|
if ((pMask = DBAllConnPlanes[type]))
|
|
{
|
|
int saveplane = epa->epa_pNum;
|
|
struct extPathFloodArg epfa;
|
|
Rect biggerArea;
|
|
|
|
TITORECT(tile, &epfa.epfa_srcArea);
|
|
GEO_EXPAND(&epfa.epfa_srcArea, 1, &biggerArea);
|
|
epfa.epfa_distance = distance;
|
|
epfa.epfa_epa = epa;
|
|
epfa.epfa_srcPoint = p;
|
|
epfa.epfa_srcTile = tile;
|
|
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
|
|
if (pNum != epa->epa_pNum && PlaneMaskHasPlane(pMask, pNum))
|
|
{
|
|
epa->epa_pNum = pNum;
|
|
(void) DBSrPaintClient((Tile *) NULL,
|
|
extPathDef->cd_planes[pNum], &biggerArea,
|
|
&DBConnectTbl[type], (ClientData) CLIENTDEFAULT,
|
|
extPathFloodFunc, (ClientData) &epfa);
|
|
}
|
|
epa->epa_pNum = saveplane;
|
|
}
|
|
}
|
|
|
|
int
|
|
extPathFloodFunc(dstTile, epfa)
|
|
Tile *dstTile;
|
|
struct extPathFloodArg *epfa;
|
|
{
|
|
Rect srcRect, dstRect;
|
|
Point dstPoint, *p;
|
|
int dstDist;
|
|
|
|
/*
|
|
* If dstTile overlaps epfa->epfa_srcArea, use epfa->epfa_srcPoint;
|
|
* otherwise, pick a point along the boundary of epfa->epfa_srcArea
|
|
* that's in common with dstTile.
|
|
*/
|
|
dstDist = epfa->epfa_distance;
|
|
srcRect = epfa->epfa_srcArea;
|
|
TITORECT(dstTile, &dstRect);
|
|
if (GEO_OVERLAP(&srcRect, &dstRect))
|
|
p = epfa->epfa_srcPoint;
|
|
else
|
|
{
|
|
/* Pick a point along the boundary */
|
|
GEOCLIP(&srcRect, &dstRect);
|
|
dstPoint.p_x = (srcRect.r_xbot + srcRect.r_xtop) / 2;
|
|
dstPoint.p_y = (srcRect.r_ybot + srcRect.r_ytop) / 2;
|
|
|
|
/* Compute the incremental delay */
|
|
dstDist = extPathTileDist(epfa->epfa_srcPoint, &dstPoint,
|
|
epfa->epfa_srcTile, dstDist);
|
|
p = &dstPoint;
|
|
}
|
|
|
|
/* Recurse */
|
|
extPathFlood(dstTile, p, dstDist, epfa->epfa_epa);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* extPathFloodTile --
|
|
*
|
|
* Propagate from a tile 'srcTile' to one of its neighbors 'dstTile'.
|
|
* The delay 'srcDelay' has been computed to 'srcPoint' (which is
|
|
* contained within 'srcTile' or is on its border). We pick the
|
|
* midpoint of the overlap between 'srcTile' and 'dstTile' as the
|
|
* point to which the next cost is computed, and then recursively
|
|
* call extPathFlood() with dstTile and the new point.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* See above.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
extPathFloodTile(srcTile, srcPoint, srcDist, dstTile, epa)
|
|
Tile *srcTile; /* Tile through which we're propagating */
|
|
Point *srcPoint; /* Point inside or on srcTile */
|
|
int srcDist; /* Distance to srcPoint so far */
|
|
Tile *dstTile; /* Tile on border of srcTile */
|
|
struct extPathArg *epa;
|
|
{
|
|
Rect srcRect, dstRect;
|
|
Point dstPoint;
|
|
int dstDist;
|
|
|
|
/*
|
|
* Pick the central point along the boundary of srcTile and dstTile
|
|
* for purposes of computing costs.
|
|
*/
|
|
TITORECT(srcTile, &srcRect);
|
|
TITORECT(dstTile, &dstRect);
|
|
GEOCLIP(&srcRect, &dstRect);
|
|
dstPoint.p_x = (srcRect.r_xbot + srcRect.r_xtop) / 2;
|
|
dstPoint.p_y = (srcRect.r_ybot + srcRect.r_ytop) / 2;
|
|
|
|
/* Compute the incremental delay */
|
|
dstDist = extPathTileDist(srcPoint, &dstPoint, srcTile, srcDist);
|
|
|
|
/* Recurse */
|
|
extPathFlood(dstTile, &dstPoint, dstDist, epa);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* extPathTileDist --
|
|
*
|
|
* Update delay information to include the costs of passing through
|
|
* 'tile' from p1 to p2. The old distance is 'oldDist'. We account
|
|
* for the distance from p1 to p2 through the tile 'tile'.
|
|
*
|
|
* Results:
|
|
* Returns the distance described above.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
extPathTileDist(p1, p2, tile, oldDist)
|
|
Point *p1, *p2;
|
|
Tile *tile;
|
|
int oldDist;
|
|
{
|
|
int newDist;
|
|
|
|
newDist = oldDist + ABSDIFF(p1->p_x, p2->p_x) + ABSDIFF(p1->p_y, p2->p_y);
|
|
|
|
/*
|
|
* If both points were on the same side, include a little extra
|
|
* for passing through the middle of the tile (which wasn't counted).
|
|
*/
|
|
if (p1->p_x == p2->p_x)
|
|
{
|
|
if (p1->p_x == LEFT(tile) || p1->p_x == RIGHT(tile))
|
|
newDist += RIGHT(tile) - LEFT(tile);
|
|
}
|
|
if (p1->p_y == p2->p_y)
|
|
{
|
|
if (p1->p_y == BOTTOM(tile) || p1->p_y == TOP(tile))
|
|
newDist += TOP(tile) - BOTTOM(tile);
|
|
}
|
|
|
|
return (newDist);
|
|
}
|