1766 lines
47 KiB
C
1766 lines
47 KiB
C
#ifndef lint
|
|
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/resis/ResMain.c,v 1.4 2010/06/24 12:37:56 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 "extract/extract.h"
|
|
#include "extract/extractInt.h"
|
|
#include "windows/windows.h"
|
|
#include "dbwind/dbwind.h"
|
|
#include "utils/tech.h"
|
|
#include "select/select.h"
|
|
#include "textio/txcommands.h"
|
|
#include "resis/resis.h"
|
|
|
|
CellUse *ResUse = NULL; /* Our use and def */
|
|
CellDef *ResDef = NULL;
|
|
TileTypeBitMask ResConnectWithSD[NT]; /* A mask that goes from */
|
|
/* SD's to devices. */
|
|
TileTypeBitMask ResCopyMask[NT]; /* Indicates which tiles */
|
|
/* are to be copied. */
|
|
resResistor *ResResList = NULL; /* Resistor list */
|
|
resNode *ResNodeList = NULL; /* Processed Nodes */
|
|
resDevice *ResDevList = NULL; /* Devices */
|
|
ResContactPoint *ResContactList = NULL; /* Contacts */
|
|
resNode *ResNodeQueue = NULL; /* Pending nodes */
|
|
resNode *ResOriginNode = NULL; /* node where R=0 */
|
|
resNode *resCurrentNode;
|
|
int ResTileCount = 0; /* Number of tiles rn_status */
|
|
extern ExtRegion *ResFirst();
|
|
extern Tile *FindStartTile();
|
|
extern int ResEachTile();
|
|
extern int ResLaplaceTile();
|
|
extern ResSimNode *ResInitializeNode();
|
|
TileTypeBitMask ResSDTypesBitMask;
|
|
TileTypeBitMask ResSubTypesBitMask;
|
|
|
|
extern HashTable ResNodeTable;
|
|
|
|
/*
|
|
*--------------------------------------------------------------------------
|
|
*
|
|
* ResInitializeConn--
|
|
*
|
|
* Sets up mask by Source/Drain type of devices. This is
|
|
* exts_deviceSDtypes turned inside out.
|
|
*
|
|
* Results: none
|
|
*
|
|
* Side Effects: Sets up ResConnectWithSD.
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
ResInitializeConn()
|
|
{
|
|
TileType dev, ttype;
|
|
char *dev_name;
|
|
int i;
|
|
ExtDevice *devptr;
|
|
|
|
for (dev = TT_TECHDEPBASE; dev < TT_MAXTYPES; dev++)
|
|
{
|
|
for (devptr = ExtCurStyle->exts_device[dev]; devptr; devptr = devptr->exts_next)
|
|
{
|
|
if ((devptr != NULL) && ((dev_name = devptr->exts_deviceName) != NULL)
|
|
&& (strcmp(dev_name, "None")))
|
|
{
|
|
for (ttype = TT_TECHDEPBASE; ttype < TT_MAXTYPES; ttype++)
|
|
{
|
|
for (i = 0; ; i++)
|
|
{
|
|
if (TTMaskIsZero(&(devptr->exts_deviceSDTypes[i])))
|
|
break;
|
|
if TTMaskHasType(&(devptr->exts_deviceSDTypes[i]), ttype)
|
|
TTMaskSetType(&ResConnectWithSD[ttype], dev);
|
|
}
|
|
|
|
if TTMaskHasType(&(devptr->exts_deviceSubstrateTypes), ttype)
|
|
TTMaskSetType(&ResConnectWithSD[ttype], dev);
|
|
}
|
|
}
|
|
}
|
|
TTMaskSetMask(&ResConnectWithSD[dev], &DBConnectTbl[dev]);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* ResGetReCell --
|
|
*
|
|
* This procedure makes sure that ResUse,ResDef
|
|
* have been properly initialized to refer to a cell definition
|
|
* named "__RESIS__".
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* A new cell use and/or def are created if necessary.
|
|
*
|
|
* --------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
ResGetReCell()
|
|
{
|
|
if (ResUse != NULL) return;
|
|
ResDef = DBCellLookDef("__RESIS__");
|
|
if (ResDef == NULL)
|
|
{
|
|
ResDef = DBCellNewDef("__RESIS__");
|
|
ASSERT (ResDef != (CellDef *) NULL, "ResGetReCell");
|
|
DBCellSetAvail(ResDef);
|
|
ResDef->cd_flags |= CDINTERNAL;
|
|
}
|
|
ResUse = DBCellNewUse(ResDef, (char *) NULL);
|
|
DBSetTrans(ResUse, &GeoIdentityTransform);
|
|
ResUse->cu_expandMask = CU_DESCEND_SPECIAL;
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------------
|
|
*
|
|
* ResDissolveContacts--
|
|
*
|
|
* results: none
|
|
*
|
|
* Side Effects: All contacts in the design are broken into their
|
|
* constituent layers. There should be no contacts in ResDef after
|
|
* this procedure runs.
|
|
*
|
|
*
|
|
*------------------------------------------------------------------------
|
|
*/
|
|
void
|
|
ResDissolveContacts(contacts)
|
|
ResContactPoint *contacts;
|
|
{
|
|
TileType t, oldtype;
|
|
Tile *tp;
|
|
TileTypeBitMask residues;
|
|
|
|
for (; contacts != (ResContactPoint *)NULL; contacts = contacts->cp_nextcontact)
|
|
{
|
|
oldtype=contacts->cp_type;
|
|
|
|
#ifdef PARANOID
|
|
if (oldtype == TT_SPACE)
|
|
TxError("Error in Contact Dissolving for %s \n",ResCurrentNode);
|
|
#endif
|
|
DBFullResidueMask(oldtype, &residues);
|
|
|
|
DBErase(ResUse->cu_def, &(contacts->cp_rect), oldtype);
|
|
for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
|
|
if (TTMaskHasType(&residues, t))
|
|
DBPaint(ResUse->cu_def, &(contacts->cp_rect), t);
|
|
|
|
tp = PlaneGetHint(ResDef->cd_planes[DBPlane(contacts->cp_type)]);
|
|
GOTOPOINT(tp, &(contacts->cp_rect.r_ll));
|
|
|
|
#ifdef PARANOID
|
|
if (TiGetTypeExact(tp) == contacts->cp_type)
|
|
TxError("Error in Contact Preprocess Routines\n");
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/*
|
|
*---------------------------------------------------------------------------
|
|
*
|
|
* ResMakePortBreakpoints --
|
|
*
|
|
* Search for nodes which are ports, and force them to be breakpoints
|
|
* in the "tileJunk" field of their respective tiles in ResUse. This
|
|
* ensures that connected nodes that stretch between two ports will
|
|
* not be assumed to be "hanging" nodes.
|
|
*
|
|
* Do the same thing for labels.
|
|
*
|
|
*----------------------------------------------------------------------------
|
|
*/
|
|
void
|
|
ResMakePortBreakpoints(def)
|
|
CellDef *def;
|
|
{
|
|
Plane *plane;
|
|
Rect *rect;
|
|
TileTypeBitMask mask;
|
|
HashSearch hs;
|
|
HashEntry *entry;
|
|
ResSimNode *node;
|
|
int ResAddBreakpointFunc(); /* Forward Declaration */
|
|
|
|
HashStartSearch(&hs);
|
|
while((entry = HashNext(&ResNodeTable,&hs)) != NULL)
|
|
{
|
|
node = (ResSimNode *)HashGetValue(entry);
|
|
if (node->status & PORTNODE)
|
|
{
|
|
if (node->rs_ttype <= 0)
|
|
{
|
|
TxError("Warning: Label \"%s\" is unconnected.\n", node->name);
|
|
continue;
|
|
}
|
|
|
|
rect = &(node->rs_bbox);
|
|
|
|
/* Beware of zero-area ports */
|
|
if (rect->r_xbot == rect->r_xtop)
|
|
{
|
|
rect->r_xbot--;
|
|
rect->r_xtop++;
|
|
}
|
|
if (rect->r_ybot == rect->r_ytop)
|
|
{
|
|
rect->r_ybot--;
|
|
rect->r_ytop++;
|
|
}
|
|
|
|
/* If label is on a contact, the contact has been dissolved. */
|
|
/* Assume that the uppermost residue is the port. This may */
|
|
/* not necessarily be the case. Could do a boundary scan on */
|
|
/* each residue plane to see which side of the contact is */
|
|
/* the internal connection in the def. . . */
|
|
|
|
if (DBIsContact(node->rs_ttype))
|
|
{
|
|
TileType type;
|
|
|
|
DBFullResidueMask(node->rs_ttype, &mask);
|
|
for (type = DBNumUserLayers - 1; type >= TT_TECHDEPBASE; type--)
|
|
if (TTMaskHasType(&mask, type))
|
|
{
|
|
plane = def->cd_planes[DBPlane(type)];
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TTMaskSetOnlyType(&mask, node->rs_ttype);
|
|
plane = def->cd_planes[DBPlane(node->rs_ttype)];
|
|
}
|
|
|
|
(void) DBSrPaintArea((Tile *) NULL, plane, rect, &mask,
|
|
ResAddBreakpointFunc, (ClientData)node);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
*---------------------------------------------------------------------------
|
|
*
|
|
* ResMakeLabelBreakpoints --
|
|
*
|
|
* Search for labels that are part of a node, and force them to be
|
|
* breakpoints in the "tileJunk" field of their respective tiles in
|
|
* ResUse. This ensures (among other things) that pins of a top level
|
|
* cell will be retained and become the endpoint of a net.
|
|
*
|
|
*----------------------------------------------------------------------------
|
|
*/
|
|
void
|
|
ResMakeLabelBreakpoints(def, goodies)
|
|
CellDef *def;
|
|
ResGlobalParams *goodies;
|
|
{
|
|
Plane *plane;
|
|
Rect *rect;
|
|
TileTypeBitMask mask;
|
|
HashEntry *entry;
|
|
ResSimNode *node;
|
|
Label *slab;
|
|
int ResAddBreakpointFunc(); /* Forward Declaration */
|
|
|
|
for (slab = def->cd_labels; slab != NULL; slab = slab->lab_next)
|
|
{
|
|
/* Avoid any empty-string labels, or it will end up as */
|
|
/* missing terminal on a device. */
|
|
if (*(slab->lab_text) == '\0') continue;
|
|
|
|
entry = HashFind(&ResNodeTable, slab->lab_text);
|
|
node = ResInitializeNode(entry);
|
|
|
|
/* If the drivepoint position changes and the drivepoint is */
|
|
/* in the "goodies" record, then make sure the tile type in */
|
|
/* "goodies" gets changed to match. */
|
|
|
|
if (goodies->rg_devloc == &node->drivepoint)
|
|
goodies->rg_ttype = slab->lab_type;
|
|
|
|
node->drivepoint = slab->lab_rect.r_ll;
|
|
node->rs_bbox = slab->lab_rect;
|
|
node->location = slab->lab_rect.r_ll;
|
|
node->rs_ttype = slab->lab_type;
|
|
node->type = slab->lab_type;
|
|
|
|
rect = &(node->rs_bbox);
|
|
|
|
/* If label is on a contact, the contact has been dissolved. */
|
|
/* Assume that the uppermost residue is the port. This may */
|
|
/* not necessarily be the case. Could do a boundary scan on */
|
|
/* each residue plane to see which side of the contact is */
|
|
/* the internal connection in the def. . . */
|
|
|
|
if (DBIsContact(slab->lab_type))
|
|
{
|
|
TileType type;
|
|
|
|
DBFullResidueMask(slab->lab_type, &mask);
|
|
for (type = DBNumUserLayers - 1; type >= TT_TECHDEPBASE; type--)
|
|
if (TTMaskHasType(&mask, type))
|
|
{
|
|
plane = def->cd_planes[DBPlane(type)];
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TTMaskSetOnlyType(&mask, slab->lab_type);
|
|
plane = def->cd_planes[DBPlane(slab->lab_type)];
|
|
}
|
|
|
|
(void) DBSrPaintArea((Tile *) NULL, plane, rect, &mask,
|
|
ResAddBreakpointFunc, (ClientData)node);
|
|
|
|
}
|
|
}
|
|
|
|
/*
|
|
*----------------------------------------------------------------------------
|
|
*
|
|
* ResAddBreakpointFunc --
|
|
*
|
|
* Add a breakpoint to the "tileJunk" structure of the tile
|
|
*
|
|
*----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
ResAddBreakpointFunc(tile, node)
|
|
Tile *tile;
|
|
ResSimNode *node;
|
|
{
|
|
tileJunk *junk;
|
|
|
|
if (TiGetClient(tile) == CLIENTDEFAULT)
|
|
return 0;
|
|
|
|
NEWPORT(node, tile);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*---------------------------------------------------------------------------
|
|
*
|
|
* ResFindNewContactTiles --
|
|
*
|
|
*
|
|
* Results: none
|
|
*
|
|
* Side Effects: dissolving contacts eliminated the tiles that
|
|
* contacts->nextcontact pointed to. This procedure finds the tile now under
|
|
* center and sets that tile's ti_client field to point to the contact. The
|
|
* old value of clientdata is set to nextTilecontact.
|
|
*
|
|
*----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
ResFindNewContactTiles(contacts)
|
|
ResContactPoint *contacts;
|
|
{
|
|
int pNum;
|
|
Tile *tile;
|
|
TileTypeBitMask mask;
|
|
|
|
for (; contacts != (ResContactPoint *) NULL; contacts = contacts->cp_nextcontact)
|
|
{
|
|
DBFullResidueMask(contacts->cp_type, &mask);
|
|
|
|
/* Watch for types that connect to the substrate plane or well; */
|
|
/* e.g., psubstratepdiff connects to nwell but not through a */
|
|
/* contact. */
|
|
|
|
if (ExtCurStyle->exts_globSubstratePlane != -1)
|
|
{
|
|
TileTypeBitMask cMask;
|
|
TTMaskAndMask3(&cMask, &DBConnectTbl[contacts->cp_type],
|
|
&DBPlaneTypes[ExtCurStyle->exts_globSubstratePlane]);
|
|
|
|
if (!TTMaskIsZero(&cMask))
|
|
TTMaskSetMask(&mask, &cMask);
|
|
}
|
|
|
|
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
|
|
{
|
|
tile = PlaneGetHint(ResDef->cd_planes[pNum]);
|
|
GOTOPOINT(tile, &(contacts->cp_center));
|
|
#ifdef PARANOID
|
|
if (tile == (Tile *) NULL)
|
|
{
|
|
TxError("Error: setting contact tile to null\n");
|
|
}
|
|
#endif
|
|
|
|
if ((IsSplit(tile) && TTMaskHasType(&mask, TiGetRightType(tile)))
|
|
|| TTMaskHasType(&mask, TiGetType(tile)))
|
|
{
|
|
tileJunk *j = (tileJunk *)TiGetClientPTR(tile);
|
|
cElement *ce;
|
|
|
|
ce = (cElement *) mallocMagic((unsigned) (sizeof(cElement)));
|
|
contacts->cp_tile[contacts->cp_currentcontact] = tile;
|
|
ce->ce_thisc = contacts;
|
|
ce->ce_nextc = j->contactList;
|
|
(contacts->cp_currentcontact) += 1;
|
|
j->contactList = ce;
|
|
}
|
|
else if (!IsSplit(tile))
|
|
{
|
|
TileType ttype = TiGetTypeExact(tile);
|
|
if (DBIsContact(ttype))
|
|
{
|
|
/* Handle the exceptional case in which a contact
|
|
* type is its own residue. This can be used for
|
|
* devices whose terminals are always a contact
|
|
* and for which a non-contact type cannot be drawn.
|
|
*/
|
|
if (TTMaskIntersect(DBResidueMask(ttype), &mask))
|
|
{
|
|
tileJunk *j = (tileJunk *)TiGetClientPTR(tile);
|
|
cElement *ce;
|
|
|
|
ce = (cElement *) mallocMagic((unsigned) (sizeof(cElement)));
|
|
contacts->cp_tile[contacts->cp_currentcontact] = tile;
|
|
ce->ce_thisc = contacts;
|
|
ce->ce_nextc = j->contactList;
|
|
(contacts->cp_currentcontact) += 1;
|
|
j->contactList = ce;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#ifdef PARANOID
|
|
if (contacts->cp_currentcontact >= LAYERS_PER_CONTACT)
|
|
{
|
|
TxError("Error: Not enough space allocated for contact nodes\n");
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------------
|
|
*
|
|
* ResProcessTiles--Calls ResEachTile with processed tiles belonging to
|
|
* nodes in ResNodeQueue. When all the tiles corresponding
|
|
* to a node have been processed, the node is moved to
|
|
* ResNodeList.
|
|
*
|
|
* Results: Return 1 if any error occurred, 0 otherwise.
|
|
*
|
|
* Side Effects: Cleans extraneous linked lists from nodes.
|
|
*
|
|
*--------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
ResProcessTiles(goodies, origin)
|
|
Point *origin;
|
|
ResGlobalParams *goodies;
|
|
|
|
{
|
|
Tile *startTile;
|
|
int tilenum, merged;
|
|
resNode *resptr2;
|
|
jElement *workingj;
|
|
cElement *workingc;
|
|
ResFixPoint *fix;
|
|
resNode *resptr;
|
|
int (*tilefunc)();
|
|
|
|
#ifdef LAPLACE
|
|
tilefunc = (ResOptionsFlags & ResOpt_DoLaplace) ? ResLaplaceTile : ResEachTile;
|
|
#else
|
|
tilefunc = ResEachTile;
|
|
#endif
|
|
|
|
if (ResOptionsFlags & ResOpt_Signal)
|
|
{
|
|
startTile = FindStartTile(goodies, origin);
|
|
if (startTile == NULL) return(1);
|
|
resCurrentNode = NULL;
|
|
(void) (*tilefunc)(startTile, origin);
|
|
}
|
|
#ifdef ARIEL
|
|
else if (ResOptionsFlags & ResOpt_Power)
|
|
{
|
|
for (fix = ResFixList; fix != NULL; fix = fix->fp_next)
|
|
{
|
|
Tile *tile = fix->fp_tile;
|
|
if (tile == NULL)
|
|
{
|
|
tile = PlaneGetHint(ResDef->cd_planes[DBPlane(fix->fp_ttype)]);
|
|
GOTOPOINT(tile, &(fix->fp_loc));
|
|
if (TiGetTypeExact(tile) != TT_SPACE)
|
|
{
|
|
fix->fp_tile = tile;
|
|
}
|
|
else
|
|
{
|
|
tile = NULL;
|
|
}
|
|
}
|
|
if (tile != NULL)
|
|
{
|
|
int x = fix->fp_loc.p_x;
|
|
int y = fix->fp_loc.p_y;
|
|
resptr = (resNode *) mallocMagic((unsigned)(sizeof(resNode)));
|
|
InitializeNode(resptr, x, y, RES_NODE_ORIGIN);
|
|
resptr->rn_status = TRUE;
|
|
resptr->rn_noderes = 0;
|
|
ResAddToQueue(resptr, &ResNodeQueue);
|
|
fix->fp_node = resptr;
|
|
NEWBREAK(resptr, tile, x, y, NULL);
|
|
}
|
|
}
|
|
for (fix = ResFixList; fix != NULL; fix = fix->fp_next)
|
|
{
|
|
Tile *tile = fix->fp_tile;
|
|
|
|
if (tile != NULL && (((tileJunk *)TiGetClientPTR(tile)->tj_status &
|
|
RES_TILE_DONE) == 0)
|
|
{
|
|
resCurrentNode = fix->fp_node;
|
|
(void) (*tilefunc)(tile, (Point *)NULL);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef PARANOID
|
|
else
|
|
{
|
|
TxError("Unknown analysis type in ResProcessTiles\n");
|
|
}
|
|
#endif
|
|
|
|
/* Process Everything else */
|
|
|
|
while (ResNodeQueue != NULL)
|
|
{
|
|
/*
|
|
* merged keeps track of whether another node gets merged into
|
|
* the current one. If it does, then the node must be processed
|
|
* because additional junctions or contacts were added
|
|
*/
|
|
|
|
resptr2 = ResNodeQueue;
|
|
merged = FALSE;
|
|
|
|
/* Process all junctions associated with node */
|
|
|
|
for (workingj = resptr2->rn_je; workingj != NULL; workingj = workingj->je_nextj)
|
|
{
|
|
ResJunction *rj = workingj->je_thisj;
|
|
if (rj->rj_status == FALSE)
|
|
{
|
|
for (tilenum = 0; tilenum < TILES_PER_JUNCTION; tilenum++)
|
|
{
|
|
Tile *tile = rj->rj_Tile[tilenum];
|
|
tileJunk *j = (tileJunk *)TiGetClientPTR(tile);
|
|
|
|
if ((j->tj_status & RES_TILE_DONE) == 0)
|
|
{
|
|
resCurrentNode = resptr2;
|
|
merged |= (*tilefunc)(tile, (Point *)NULL);
|
|
}
|
|
if (merged & ORIGIN) break;
|
|
}
|
|
if (merged & ORIGIN) break;
|
|
rj->rj_status = TRUE;
|
|
}
|
|
}
|
|
|
|
/* Next, Process all contacts. */
|
|
|
|
for (workingc = resptr2->rn_ce; workingc != NULL; workingc = workingc->ce_nextc)
|
|
{
|
|
ResContactPoint *cp = workingc->ce_thisc;
|
|
|
|
if (merged & ORIGIN) break;
|
|
if (cp->cp_status == FALSE)
|
|
{
|
|
int newstatus = TRUE;
|
|
for (tilenum = 0; tilenum < cp->cp_currentcontact; tilenum++)
|
|
{
|
|
Tile *tile = cp->cp_tile[tilenum];
|
|
tileJunk *j = (tileJunk *) TiGetClientPTR(tile);
|
|
|
|
if ((j->tj_status & RES_TILE_DONE) == 0)
|
|
{
|
|
if (cp->cp_cnode[tilenum] == resptr2)
|
|
{
|
|
resCurrentNode = resptr2;
|
|
merged |= (*tilefunc)(tile,(Point *)NULL);
|
|
}
|
|
else
|
|
{
|
|
newstatus = FALSE;
|
|
}
|
|
}
|
|
if (merged & ORIGIN) break;
|
|
}
|
|
if (merged & ORIGIN) break;
|
|
cp->cp_status = newstatus;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If nothing new has been added via a merge, then the node is
|
|
* finished. It is removed from the pending queue, added to the
|
|
* done list, cleaned up, and passed to ResDoneWithNode
|
|
*/
|
|
|
|
if (merged == FALSE)
|
|
{
|
|
ResRemoveFromQueue(resptr2, &ResNodeQueue);
|
|
resptr2->rn_more = ResNodeList;
|
|
resptr2->rn_less = NULL;
|
|
resptr2->rn_status &= ~PENDING;
|
|
resptr2->rn_status |= FINISHED | MARKED;
|
|
if (ResNodeList != NULL)
|
|
{
|
|
ResNodeList->rn_less = resptr2;
|
|
}
|
|
if (resptr2->rn_noderes == 0)
|
|
{
|
|
ResOriginNode=resptr2;
|
|
}
|
|
ResNodeList = resptr2;
|
|
ResCleanNode(resptr2, FALSE, &ResNodeList, &ResNodeQueue);
|
|
ResDoneWithNode(resptr2);
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
*-------------------------------------------------------------------------
|
|
*
|
|
* ResCalcPerimOverlap ---
|
|
*
|
|
* Given a device tile, compute simple perimeter and overlap of the device
|
|
* by the net under consideration.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side Effects:
|
|
* The ResDevTile structure is updated with the overlap and perimeter
|
|
* values.
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
ResCalcPerimOverlap(tile, dev)
|
|
Tile *tile;
|
|
ResDevTile *dev;
|
|
{
|
|
Tile *tp;
|
|
int t1;
|
|
int overlap;
|
|
TileTypeBitMask *omask;
|
|
|
|
dev->perim = (TOP(tile) - BOTTOM(tile) - LEFT(tile) + RIGHT(tile)) << 1;
|
|
overlap = 0;
|
|
|
|
t1 = TiGetType(tile);
|
|
omask = &(ExtCurStyle->exts_nodeConn[t1]);
|
|
|
|
/* left */
|
|
for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp = RT(tp))
|
|
{
|
|
if TTMaskHasType(omask, TiGetType(tp))
|
|
overlap += MIN(TOP(tile), TOP(tp)) - MAX(BOTTOM(tile), BOTTOM(tp));
|
|
}
|
|
|
|
/* right */
|
|
for (tp = TR(tile); TOP(tp) > BOTTOM(tile); tp=LB(tp))
|
|
{
|
|
if TTMaskHasType(omask, TiGetType(tp))
|
|
overlap += MIN(TOP(tile), TOP(tp)) - MAX(BOTTOM(tile), BOTTOM(tp));
|
|
}
|
|
|
|
/* top */
|
|
for (tp = RT(tile); RIGHT(tp) > LEFT(tile); tp = BL(tp))
|
|
{
|
|
if TTMaskHasType(omask, TiGetType(tp))
|
|
overlap += MIN(RIGHT(tile), RIGHT(tp)) - MAX(LEFT(tile), LEFT(tp));
|
|
}
|
|
|
|
/* bottom */
|
|
for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp=TR(tp))
|
|
{
|
|
if TTMaskHasType(omask, TiGetType(tp))
|
|
overlap += MIN(RIGHT(tile), RIGHT(tp)) - MAX(LEFT(tile), LEFT(tp));
|
|
}
|
|
dev->overlap += overlap;
|
|
}
|
|
/*
|
|
*-------------------------------------------------------------------------
|
|
*
|
|
* resMakeDevFunc --
|
|
*
|
|
* Callback function from ResExtractNet. For each device in a node's
|
|
* device list pulled from the .sim file, find the tile(s) corresponding
|
|
* to the device in the source tree, and fill out the complete device
|
|
* record (namely the full device area).
|
|
*
|
|
* Result:
|
|
* Return 1 to stop the search because the device has been found.
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
resMakeDevFunc(tile, cx)
|
|
Tile *tile;
|
|
TreeContext *cx;
|
|
{
|
|
ResDevTile *thisDev = (ResDevTile *)cx->tc_filter->tf_arg;
|
|
Rect devArea;
|
|
TileType ttype;
|
|
|
|
TiToRect(tile, &devArea);
|
|
GeoTransRect(&cx->tc_scx->scx_trans, &devArea, &thisDev->area);
|
|
|
|
if (IsSplit(tile))
|
|
ttype = (SplitSide(tile)) ? SplitRightType(tile) : SplitLeftType(tile);
|
|
else
|
|
ttype = TiGetType(tile);
|
|
|
|
/* If more than one tile type extracts to the same device, then */
|
|
/* the device type may be different from what was recorded when */
|
|
/* the sim file was read. Restricted to the plane of the */
|
|
/* original type to avoid conflict with completely different */
|
|
/* devices (like transistors vs. MiM caps). */
|
|
|
|
if (ttype != thisDev->type)
|
|
{
|
|
if (DBPlane(ttype) != DBPlane(thisDev->type))
|
|
return 0; /* Completely different device? */
|
|
thisDev->type = ttype;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
*-------------------------------------------------------------------------
|
|
*
|
|
* resExpandDevFunc --
|
|
*
|
|
* Do a boundary search on the first tile found in the search context
|
|
* path belonging to a device, including all tiles that belong to the
|
|
* device. For each compatible tile found, paint the device tile
|
|
* type into ResUse and calculate the overlap.
|
|
*
|
|
* Returns:
|
|
* 1 to stop the search (only the first tile of a device needs to be
|
|
* found).
|
|
*
|
|
* Side effects:
|
|
* Paints into ResUse and recalculates values of thisDev.
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
#define DEV_PROCESSED 1
|
|
|
|
int
|
|
resExpandDevFunc(tile, cx)
|
|
Tile *tile;
|
|
TreeContext *cx;
|
|
{
|
|
ResDevTile *thisDev = (ResDevTile *)cx->tc_filter->tf_arg;
|
|
static Stack *devExtentsStack = NULL;
|
|
static Stack *devResetStack = NULL;
|
|
TileTypeBitMask *rMask;
|
|
Tile *tp, *tp2;
|
|
TileType ttype;
|
|
int pNum;
|
|
Rect area;
|
|
|
|
pNum = DBPlane(thisDev->type);
|
|
if (devExtentsStack == NULL)
|
|
devExtentsStack = StackNew(8);
|
|
if (devResetStack == NULL)
|
|
devResetStack = StackNew(8);
|
|
|
|
TiSetClientINT(tile, DEV_PROCESSED);
|
|
STACKPUSH((ClientData)tile, devExtentsStack);
|
|
|
|
while (!StackEmpty(devExtentsStack))
|
|
{
|
|
tp = (Tile *) STACKPOP(devExtentsStack);
|
|
STACKPUSH((ClientData)tp, devResetStack);
|
|
TiToRect(tp, &area);
|
|
|
|
/* Paint type thisDev->type into ResUse over area of tile "tp" */
|
|
DBNMPaintPlane(ResUse->cu_def->cd_planes[pNum], TiGetTypeExact(tp),
|
|
&area, DBStdPaintTbl(thisDev->type, pNum), (PaintUndoInfo *)NULL);
|
|
|
|
/* Add source/drain perimeter overlap to the device for this tile */
|
|
ResCalcPerimOverlap(tp, thisDev);
|
|
|
|
/* Search boundary of the device tile for more tiles belonging */
|
|
/* to the device. If contacts are found, replace them with the */
|
|
/* device type. */
|
|
|
|
/* top */
|
|
for (tp2 = RT(tp); RIGHT(tp2) > LEFT(tp); tp2 = BL(tp2))
|
|
{
|
|
if (TiGetClientINT(tp2) == DEV_PROCESSED) continue;
|
|
ttype = TiGetBottomType(tp2);
|
|
if ((ttype == thisDev->type) || (DBIsContact(ttype)
|
|
&& TTMaskHasType(DBResidueMask(ttype), thisDev->type)))
|
|
{
|
|
TiSetClientINT(tp2, DEV_PROCESSED);
|
|
STACKPUSH((ClientData)tp2, devExtentsStack);
|
|
}
|
|
}
|
|
|
|
/* bottom */
|
|
for (tp2 = LB(tp); LEFT(tp2) < RIGHT(tp); tp2 = TR(tp2))
|
|
{
|
|
if (TiGetClientINT(tp2) == DEV_PROCESSED) continue;
|
|
ttype = TiGetTopType(tp2);
|
|
if ((ttype == thisDev->type) || (DBIsContact(ttype)
|
|
&& TTMaskHasType(DBResidueMask(ttype), thisDev->type)))
|
|
{
|
|
TiSetClientINT(tp2, DEV_PROCESSED);
|
|
STACKPUSH((ClientData)tp2, devExtentsStack);
|
|
}
|
|
}
|
|
|
|
/* right */
|
|
for (tp2 = TR(tp); TOP(tp2) > BOTTOM(tp); tp2 = LB(tp2))
|
|
{
|
|
if (TiGetClientINT(tp2) == DEV_PROCESSED) continue;
|
|
ttype = TiGetLeftType(tp2);
|
|
if ((ttype == thisDev->type) || (DBIsContact(ttype)
|
|
&& TTMaskHasType(DBResidueMask(ttype), thisDev->type)))
|
|
{
|
|
TiSetClientINT(tp2, DEV_PROCESSED);
|
|
STACKPUSH((ClientData)tp2, devExtentsStack);
|
|
}
|
|
}
|
|
|
|
/* left */
|
|
for (tp2 = BL(tp); BOTTOM(tp2) < TOP(tp); tp2 = RT(tp2))
|
|
{
|
|
if (TiGetClientINT(tp2) == DEV_PROCESSED) continue;
|
|
ttype = TiGetRightType(tp2);
|
|
if ((ttype == thisDev->type) || (DBIsContact(ttype)
|
|
&& TTMaskHasType(DBResidueMask(ttype), thisDev->type)))
|
|
{
|
|
TiSetClientINT(tp2, DEV_PROCESSED);
|
|
STACKPUSH((ClientData)tp2, devExtentsStack);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Reset the device tile client records */
|
|
while (!StackEmpty(devResetStack))
|
|
{
|
|
tp = (Tile *) STACKPOP(devResetStack);
|
|
TiSetClient(tp, CLIENTDEFAULT);
|
|
}
|
|
|
|
/* Return 1 to stop the search; we only need to run this from */
|
|
/* the first device tile. */
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
*-------------------------------------------------------------------------
|
|
*
|
|
* ResShaveContacts ---
|
|
*
|
|
* Remove the top layer off of every contact in the design, leaving
|
|
* only the bottom layer. This also resolves issues with stacked
|
|
* contacts by leaving clean contact areas where stacked types
|
|
* overlap. Contacts are removed from the plane above the search
|
|
* plane, so the removal does not corrupt the current plane search.
|
|
*
|
|
* Results:
|
|
* Return 0 to keep the search going.
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
ResShaveContacts(tile, def)
|
|
Tile *tile;
|
|
CellDef *def;
|
|
{
|
|
TileType ttype;
|
|
TileTypeBitMask *rmask;
|
|
Rect area;
|
|
Plane *plane;
|
|
int pNum;
|
|
int pMask;
|
|
|
|
/* To do: Handle split tiles, although this is unlikely for
|
|
* contact types.
|
|
*/
|
|
ttype = TiGetType(tile);
|
|
|
|
if (DBIsContact(ttype))
|
|
{
|
|
/* Remove the contact type from the plane above */
|
|
TiToRect(tile, &area);
|
|
pMask = DBTypePlaneMaskTbl[ttype];
|
|
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
|
|
if (PlaneMaskHasPlane(pMask, pNum))
|
|
break;
|
|
|
|
for (++pNum; pNum < DBNumPlanes; pNum++)
|
|
if (PlaneMaskHasPlane(pMask, pNum))
|
|
{
|
|
plane = def->cd_planes[pNum];
|
|
DBPaintPlane(plane, &area, DBStdEraseTbl(ttype, pNum),
|
|
(PaintUndoInfo *)NULL);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*-------------------------------------------------------------------------
|
|
*
|
|
* ResExtractNet-- extracts the resistance net at the specified
|
|
* rn_loc. If the resulting net is greater than the tolerance,
|
|
* simplify and return the resulting network.
|
|
*
|
|
* Results: 0 iff it worked.
|
|
*
|
|
* Side effects: Produces a resistance network for the node.
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
bool
|
|
ResExtractNet(node, goodies, cellname)
|
|
ResSimNode *node;
|
|
ResGlobalParams *goodies;
|
|
char *cellname;
|
|
{
|
|
SearchContext scx;
|
|
TileTypeBitMask FirstTileMask;
|
|
TileTypeBitMask tMask;
|
|
Point startpoint;
|
|
static int first = 1;
|
|
ResDevTile *DevTiles, *thisDev;
|
|
ResFixPoint *fix;
|
|
devPtr *tptr;
|
|
int pNum;
|
|
int resMakeDevFunc();
|
|
int resExpandDevFunc();
|
|
|
|
/* Make sure all global network variables are reset */
|
|
|
|
ResResList = NULL;
|
|
ResNodeList = NULL;
|
|
ResDevList = NULL;
|
|
ResNodeQueue = NULL;
|
|
ResContactList = NULL;
|
|
ResOriginNode = NULL;
|
|
|
|
/* Pass back network pointers */
|
|
|
|
goodies->rg_maxres = 0;
|
|
goodies->rg_tilecount = 0;
|
|
|
|
/* Set up internal stuff if this is the first time through */
|
|
|
|
if (first)
|
|
{
|
|
ResInitializeConn();
|
|
first = 0;
|
|
ResGetReCell();
|
|
}
|
|
|
|
/* Initialize Cell */
|
|
|
|
if (cellname)
|
|
{
|
|
CellDef *def = DBCellLookDef(cellname);
|
|
if (def == (CellDef *)NULL)
|
|
{
|
|
TxError("Error: No such cell \"%s\"\n", cellname);
|
|
return TRUE;
|
|
}
|
|
scx.scx_use = DBCellNewUse(def, (char *)NULL);
|
|
DBSetTrans (scx.scx_use, &GeoIdentityTransform);
|
|
scx.scx_trans = GeoIdentityTransform;
|
|
}
|
|
else
|
|
{
|
|
MagWindow *w = ToolGetBoxWindow(&scx.scx_area, (int *)NULL);
|
|
if (w == (MagWindow *)NULL)
|
|
{
|
|
TxError("Sorry, the box must appear in one of the windows.\n");
|
|
return TRUE;
|
|
}
|
|
scx.scx_use = (CellUse *) w->w_surfaceID;
|
|
scx.scx_trans = GeoIdentityTransform;
|
|
}
|
|
|
|
DBCellClearDef(ResUse->cu_def);
|
|
|
|
#ifdef ARIEL
|
|
if ((ResOptionsFlags & ResOpt_Power) &&
|
|
strcmp(node->name, goodies->rg_name) != 0) continue;
|
|
#endif
|
|
|
|
/* Copy Paint */
|
|
|
|
scx.scx_area.r_ll.p_x = node->location.p_x - 2;
|
|
scx.scx_area.r_ll.p_y = node->location.p_y - 2;
|
|
scx.scx_area.r_ur.p_x = node->location.p_x + 2;
|
|
scx.scx_area.r_ur.p_y = node->location.p_y + 2;
|
|
startpoint = node->location;
|
|
|
|
/* Because node->type might come from a label with a sticky type
|
|
* that does not correspond exactly to the layer underneath, include
|
|
* all connecting types.
|
|
*/
|
|
if (node->type != TT_SPACE)
|
|
{
|
|
TTMaskZero(&FirstTileMask);
|
|
TTMaskSetMask(&FirstTileMask, &DBConnectTbl[node->type]);
|
|
|
|
DBTreeCopyConnect(&scx, &FirstTileMask, 0, ResCopyMask, &TiPlaneRect,
|
|
SEL_DO_LABELS, ResUse);
|
|
}
|
|
|
|
TTMaskZero(&ResSDTypesBitMask);
|
|
TTMaskZero(&ResSubTypesBitMask);
|
|
|
|
/* Add devices to ResUse from list in node */
|
|
DevTiles = NULL;
|
|
for (tptr = node->firstDev; tptr; tptr = tptr->nextDev)
|
|
{
|
|
int result;
|
|
int i;
|
|
ExtDevice *devptr;
|
|
|
|
thisDev = (ResDevTile *)mallocMagic(sizeof(ResDevTile));
|
|
thisDev->devptr = tptr->thisDev->rs_devptr;
|
|
thisDev->type = tptr->thisDev->rs_ttype;
|
|
thisDev->overlap = 0;
|
|
scx.scx_area.r_ll.p_x = tptr->thisDev->location.p_x;
|
|
scx.scx_area.r_ll.p_y = tptr->thisDev->location.p_y;
|
|
scx.scx_area.r_xtop = scx.scx_area.r_xbot + 1;
|
|
scx.scx_area.r_ytop = scx.scx_area.r_ybot + 1;
|
|
result = DBTreeSrTiles(&scx, &DBAllButSpaceAndDRCBits, 0,
|
|
resMakeDevFunc, (ClientData)thisDev);
|
|
if (result == 0)
|
|
{
|
|
TxError("No device of type %s found at location %d,%d\n",
|
|
DBTypeLongNameTbl[thisDev->type],
|
|
tptr->thisDev->location.p_x,
|
|
tptr->thisDev->location.p_y);
|
|
freeMagic(thisDev);
|
|
continue;
|
|
}
|
|
thisDev->nextDev = DevTiles;
|
|
DevTiles = thisDev;
|
|
|
|
/* Paint the entire device into ResUse */
|
|
TTMaskSetOnlyType(&tMask, thisDev->type);
|
|
DBTreeSrTiles(&scx, &tMask, 0, resExpandDevFunc, (ClientData)thisDev);
|
|
|
|
/* If the device has source/drain types in a different plane than */
|
|
/* the device identifier type, then add the source/drain types to */
|
|
/* the mask ResSDTypesBitMask. */
|
|
|
|
devptr = tptr->thisDev->rs_devptr;
|
|
for (i = 0; !TTMaskIsZero(&devptr->exts_deviceSDTypes[i]); i++)
|
|
TTMaskSetMask(&ResSDTypesBitMask, &devptr->exts_deviceSDTypes[i]);
|
|
|
|
/* Add the substrate types to the mask ResSubTypesBitMask */
|
|
TTMaskSetMask(&ResSubTypesBitMask, &devptr->exts_deviceSubstrateTypes);
|
|
|
|
/* TT_SPACE should be removed from ResSubTypesBitMask */
|
|
TTMaskClearType(&ResSubTypesBitMask, TT_SPACE);
|
|
}
|
|
DBReComputeBbox(ResUse->cu_def);
|
|
|
|
ExtResetTiles(scx.scx_use->cu_def, extUnInit);
|
|
|
|
/* To avoid issues with overlapping stacked contact types and */
|
|
/* double-counting contacts on multiple planes, erase the top */
|
|
/* contact layers of all contacts. ExtFindRegions() will still */
|
|
/* find the connectivity above but will only process one tile per */
|
|
/* contact. This temporarily creates an improper database, but */
|
|
/* the contacts are all immediately erased by ResDissolveContacts().*/
|
|
|
|
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
|
|
{
|
|
Plane *plane = ResUse->cu_def->cd_planes[pNum];
|
|
DBSrPaintArea(PlaneGetHint(plane), plane, &(ResUse->cu_def->cd_bbox),
|
|
&DBAllButSpaceAndDRCBits, ResShaveContacts,
|
|
(ClientData)ResUse->cu_def);
|
|
}
|
|
|
|
/* Find all contacts in design and note their position */
|
|
|
|
/* NOTE: ExtFindRegions() will call ResFirst or ResEach for BOTH */
|
|
/* planes of a contact. Rather than attempting to limit the */
|
|
/* search, ResDoContacts() will just double the resistance per via */
|
|
/* so that the final value is correct. */
|
|
|
|
ResContactList = (ResContactPoint *)ExtFindRegions(ResUse->cu_def,
|
|
&(ResUse->cu_def->cd_bbox),
|
|
&DBAllButSpaceAndDRCBits,
|
|
ResConnectWithSD, extUnInit, ResFirst,
|
|
ResEach);
|
|
ExtResetTiles(ResUse->cu_def, extUnInit);
|
|
|
|
/*
|
|
* dissolve the contacts and find which tiles now cover the point
|
|
* where the tile used to be.
|
|
*/
|
|
|
|
ResDissolveContacts(ResContactList);
|
|
|
|
/* Add "junk" fields to tiles */
|
|
|
|
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
|
|
{
|
|
Plane *plane = ResUse->cu_def->cd_planes[pNum];
|
|
Rect *rect = &ResUse->cu_def->cd_bbox;
|
|
ResFracture(plane, rect);
|
|
(void) DBSrPaintClient((Tile *) NULL, plane, rect,
|
|
&DBAllButSpaceAndDRCBits,
|
|
(ClientData) CLIENTDEFAULT, ResAddPlumbing,
|
|
(ClientData) &ResDevList);
|
|
}
|
|
|
|
/* Finish preprocessing. */
|
|
|
|
ResMakePortBreakpoints(ResUse->cu_def);
|
|
ResMakeLabelBreakpoints(ResUse->cu_def, goodies);
|
|
ResFindNewContactTiles(ResContactList);
|
|
ResPreProcessDevices(DevTiles, ResDevList, ResUse->cu_def);
|
|
|
|
#ifdef LAPLACE
|
|
if (ResOptionsFlags & ResOpt_DoLaplace)
|
|
{
|
|
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
|
|
{
|
|
Plane *plane = ResUse->cu_def->cd_planes[pNum];
|
|
Rect *rect = &ResUse->cu_def->cd_bbox;
|
|
Res1d(plane, rect);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef ARIEL
|
|
if (ResOptionsFlags & ResOpt_Power)
|
|
{
|
|
for (fix = startlist; fix != NULL; fix = fix->fp_next)
|
|
{
|
|
fix->fp_tile = PlaneGetHint(ResUse->cu_def->cd_planes[DBPlane(fix->fp_ttype)]);
|
|
GOTOPOINT(fix->fp_tile, &fix->fp_loc);
|
|
if (TiGetTypeExact(fix->fp_tile) == TT_SPACE) fix->fp_tile = NULL;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* do extraction */
|
|
if (ResProcessTiles(goodies, &startpoint) != 0) return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
*-------------------------------------------------------------------------
|
|
*
|
|
* ResCleanUpEverything--After each net is extracted by ResExtractNet,
|
|
* the resulting memory must be freed up, and varius trash swept under
|
|
* the carpet in preparation for the next extraction.
|
|
*
|
|
* Results: none
|
|
*
|
|
* Side Effects: Frees up memory formerly occupied by network elements.
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
ResCleanUpEverything()
|
|
{
|
|
|
|
int pNum;
|
|
resResistor *oldRes;
|
|
resDevice *oldDev;
|
|
ResContactPoint *oldCon;
|
|
|
|
/* Check integrity of internal database. Free up lists. */
|
|
|
|
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
|
|
{
|
|
(void) DBSrPaintClient((Tile *)NULL, ResUse->cu_def->cd_planes[pNum],
|
|
&(ResUse->cu_def->cd_bbox), &DBAllButSpaceAndDRCBits,
|
|
(ClientData)CLIENTDEFAULT, ResRemovePlumbing,
|
|
(ClientData)NULL);
|
|
}
|
|
|
|
while (ResNodeList != NULL)
|
|
{
|
|
ResCleanNode(ResNodeList, TRUE, &ResNodeList, &ResNodeQueue);
|
|
}
|
|
while (ResContactList != NULL)
|
|
{
|
|
oldCon = ResContactList;
|
|
ResContactList = oldCon->cp_nextcontact;
|
|
freeMagic((char *)oldCon);
|
|
}
|
|
while (ResResList != NULL)
|
|
{
|
|
oldRes = ResResList;
|
|
ResResList = ResResList->rr_nextResistor;
|
|
freeMagic((char *)oldRes);
|
|
}
|
|
while (ResDevList != NULL)
|
|
{
|
|
oldDev = ResDevList;
|
|
ResDevList = ResDevList->rd_nextDev;
|
|
if ((oldDev->rd_status & RES_DEV_SAVE) == 0)
|
|
{
|
|
freeMagic((char *)oldDev->rd_terminals);
|
|
freeMagic((char *)oldDev);
|
|
}
|
|
}
|
|
DBCellClearDef(ResUse->cu_def);
|
|
}
|
|
|
|
/*
|
|
*-------------------------------------------------------------------------
|
|
*
|
|
* ResGetTileFunc --
|
|
*
|
|
* Callback function used by FindStartTile() when searching for
|
|
* terminal connections of a device that may be on planes other
|
|
* than the plane of the device identifier type. Ignore space
|
|
* tiles. Otherwise, for any tile found, record the tile in
|
|
* the client data record and return 1 to stop the search.
|
|
*
|
|
* Results:
|
|
* Return 0 if tile is a space tile, to keep the search going.
|
|
* Return 1 otherwise to stop the search immediately because
|
|
* a valid start tile has been found.
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
ResGetTileFunc(tile, tpptr)
|
|
Tile *tile, **tpptr;
|
|
{
|
|
if (TiGetType(tile) != TT_SPACE)
|
|
{
|
|
*tpptr = tile;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*-------------------------------------------------------------------------
|
|
*
|
|
* FindStartTile-- To start the extraction, we need to find the first driver.
|
|
* The sim file gives us the location of a point in or near (within 1
|
|
* unit) of the device. FindStartTile looks for the device, then
|
|
* for adjoining diffusion. The diffusion tile is returned.
|
|
*
|
|
* Results: returns source diffusion tile, if it exists. Otherwise, return
|
|
* NULL.
|
|
*
|
|
* Side Effects: none
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
Tile *
|
|
FindStartTile(goodies, SourcePoint)
|
|
Point *SourcePoint;
|
|
ResGlobalParams *goodies;
|
|
|
|
{
|
|
Point workingPoint;
|
|
Tile *tile, *tp;
|
|
int pnum, t1, t2, i;
|
|
ExtDevice *devptr;
|
|
Rect r;
|
|
bool complex;
|
|
static Stack *devStack = NULL;
|
|
|
|
/* If the drive point is on a contact, check for the contact residues */
|
|
/* first, then the contact type itself. */
|
|
|
|
if (DBIsContact(goodies->rg_ttype))
|
|
{
|
|
TileTypeBitMask *rmask = DBResidueMask(goodies->rg_ttype);
|
|
TileType savtype = goodies->rg_ttype;
|
|
TileType rtype;
|
|
|
|
for (rtype = TT_TECHDEPBASE; rtype < DBNumUserLayers; rtype++)
|
|
if (TTMaskHasType(rmask, rtype))
|
|
{
|
|
goodies->rg_ttype = rtype;
|
|
if ((tile = FindStartTile(goodies, SourcePoint)) != NULL)
|
|
{
|
|
goodies->rg_ttype = savtype;
|
|
return tile;
|
|
}
|
|
}
|
|
goodies->rg_ttype = savtype;
|
|
}
|
|
|
|
workingPoint.p_x = goodies->rg_devloc->p_x;
|
|
workingPoint.p_y = goodies->rg_devloc->p_y;
|
|
|
|
pnum = DBPlane(goodies->rg_ttype);
|
|
|
|
/* for drivepoints, we don't have to find a device */
|
|
if (goodies->rg_status & DRIVEONLY)
|
|
{
|
|
tile = PlaneGetHint(ResUse->cu_def->cd_planes[pnum]);
|
|
GOTOPOINT(tile, &workingPoint);
|
|
SourcePoint->p_x = workingPoint.p_x;
|
|
SourcePoint->p_y = workingPoint.p_y;
|
|
|
|
if (TiGetTypeExact(tile) == goodies->rg_ttype)
|
|
return tile;
|
|
else
|
|
{
|
|
/* On the other hand, drivepoints derived from subcircuit */
|
|
/* boundaries lie on tile boundaries, and GOTOPOINT() will */
|
|
/* pick the tile on the wrong side for TOP and RIGHT */
|
|
/* segment coincidences. */
|
|
|
|
if (workingPoint.p_x == LEFT(tile))
|
|
{
|
|
for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp=RT(tp))
|
|
if (TiGetRightType(tp) == goodies->rg_ttype)
|
|
return(tp);
|
|
}
|
|
else if (workingPoint.p_y == BOTTOM(tile))
|
|
{
|
|
for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp=TR(tp))
|
|
if (TiGetTopType(tp) == goodies->rg_ttype)
|
|
return(tp);
|
|
}
|
|
}
|
|
TxError("Couldn't find wire at %d %d\n",
|
|
goodies->rg_devloc->p_x, goodies->rg_devloc->p_y);
|
|
return NULL;
|
|
}
|
|
|
|
tile = PlaneGetHint(ResUse->cu_def->cd_planes[pnum]);
|
|
GOTOPOINT(tile, &workingPoint);
|
|
|
|
if (IsSplit(tile))
|
|
{
|
|
if (TTMaskHasType(&ExtCurStyle->exts_deviceMask, TiGetLeftType(tile)) != 0)
|
|
{
|
|
t1 = TiGetLeftType(tile);
|
|
TiSetBody(tile, t1 & ~TT_SIDE);
|
|
}
|
|
else if (TTMaskHasType(&ExtCurStyle->exts_deviceMask, TiGetRightType(tile)) != 0)
|
|
{
|
|
t1 = TiGetRightType(tile);
|
|
TiSetBody(tile, t1 & TT_SIDE);
|
|
}
|
|
else
|
|
{
|
|
TxError("Couldn't find device at %d %d\n",
|
|
goodies->rg_devloc->p_x, goodies->rg_devloc->p_y);
|
|
return(NULL);
|
|
}
|
|
}
|
|
else if (TTMaskHasType(&ExtCurStyle->exts_deviceMask, TiGetType(tile)) == 0)
|
|
{
|
|
TxError("Couldn't find device at %d %d\n",
|
|
goodies->rg_devloc->p_x, goodies->rg_devloc->p_y);
|
|
return(NULL);
|
|
}
|
|
else
|
|
t1 = TiGetType(tile);
|
|
|
|
/* NOTE: There must be a way to pass the device type from a device
|
|
* record's rs_devptr instead of groping around for it.
|
|
*/
|
|
|
|
for (devptr = ExtCurStyle->exts_device[t1]; devptr; devptr = devptr->exts_next)
|
|
{
|
|
for (i = 0; i < devptr->exts_deviceSDCount; i++)
|
|
{
|
|
complex = FALSE; /* Assume device is a single tile */
|
|
|
|
/* left */
|
|
for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp = RT(tp))
|
|
{
|
|
t2 = TiGetRightType(tp);
|
|
if ((t2 != TT_SPACE) &&
|
|
TTMaskHasType(&(devptr->exts_deviceSDTypes[i]), t2))
|
|
{
|
|
SourcePoint->p_x = LEFT(tile);
|
|
SourcePoint->p_y = (MIN(TOP(tile),TOP(tp)) +
|
|
MAX(BOTTOM(tile), BOTTOM(tp))) >> 1;
|
|
return(tp);
|
|
}
|
|
else
|
|
{
|
|
const ClientData ticlient = TiGetClient(tp);
|
|
const tileJunk *tj = (tileJunk *)CD2PTR(ticlient);
|
|
if (ticlient != CLIENTDEFAULT && tj->tj_status & RES_TILE_DEV)
|
|
complex = TRUE;
|
|
}
|
|
}
|
|
|
|
/* right */
|
|
for (tp = TR(tile); TOP(tp) > BOTTOM(tile); tp = LB(tp))
|
|
{
|
|
t2 = TiGetLeftType(tp);
|
|
if ((t2 != TT_SPACE) &&
|
|
TTMaskHasType(&(devptr->exts_deviceSDTypes[i]), t2))
|
|
{
|
|
SourcePoint->p_x = RIGHT(tile);
|
|
SourcePoint->p_y = (MIN(TOP(tile), TOP(tp))+
|
|
MAX(BOTTOM(tile), BOTTOM(tp))) >> 1;
|
|
return(tp);
|
|
}
|
|
else
|
|
{
|
|
const ClientData ticlient = TiGetClient(tp);
|
|
const tileJunk *tj = (tileJunk *)CD2PTR(ticlient);
|
|
if (ticlient != CLIENTDEFAULT && tj->tj_status & RES_TILE_DEV)
|
|
complex = TRUE;
|
|
}
|
|
}
|
|
|
|
/* top */
|
|
for (tp = RT(tile); RIGHT(tp) > LEFT(tile); tp = BL(tp))
|
|
{
|
|
t2 = TiGetBottomType(tp);
|
|
if ((t2 != TT_SPACE) &&
|
|
TTMaskHasType(&(devptr->exts_deviceSDTypes[i]), t2))
|
|
{
|
|
SourcePoint->p_y = TOP(tile);
|
|
SourcePoint->p_x = (MIN(RIGHT(tile),RIGHT(tp)) +
|
|
MAX(LEFT(tile), LEFT(tp))) >> 1;
|
|
return(tp);
|
|
}
|
|
else
|
|
{
|
|
const ClientData ticlient = TiGetClient(tp);
|
|
const tileJunk *tj = (tileJunk *)CD2PTR(ticlient);
|
|
if (ticlient != CLIENTDEFAULT && tj->tj_status & RES_TILE_DEV)
|
|
complex = TRUE;
|
|
}
|
|
}
|
|
|
|
/* bottom */
|
|
for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp = TR(tp))
|
|
{
|
|
t2 = TiGetTopType(tp);
|
|
if ((t2 != TT_SPACE) &&
|
|
TTMaskHasType(&(devptr->exts_deviceSDTypes[i]), t2))
|
|
{
|
|
SourcePoint->p_y = BOTTOM(tile);
|
|
SourcePoint->p_x = (MIN(RIGHT(tile), RIGHT(tp)) +
|
|
MAX(LEFT(tile), LEFT(tp))) >> 1;
|
|
return(tp);
|
|
}
|
|
else
|
|
{
|
|
const ClientData ticlient = TiGetClient(tp);
|
|
const tileJunk *tj = (tileJunk *)CD2PTR(ticlient);
|
|
if (ticlient != CLIENTDEFAULT && tj->tj_status & RES_TILE_DEV)
|
|
complex = TRUE;
|
|
}
|
|
}
|
|
|
|
if (complex == TRUE)
|
|
{
|
|
/* Didn't find a terminal but device has multiple */
|
|
/* tiles, so make a secondary search looking at all */
|
|
/* tiles of the device. */
|
|
|
|
if (devStack == NULL) devStack = StackNew(8);
|
|
|
|
((tileJunk *)TiGetClientPTR(tile))->tj_status |= RES_TILE_PUSHED;
|
|
STACKPUSH((ClientData)tile, devStack);
|
|
while (!StackEmpty(devStack))
|
|
{
|
|
tile = (Tile *)STACKPOP(devStack);
|
|
|
|
/* left */
|
|
for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp = RT(tp))
|
|
{
|
|
t2 = TiGetRightType(tp);
|
|
if ((t2 != TT_SPACE) &&
|
|
TTMaskHasType(&(devptr->exts_deviceSDTypes[i]), t2))
|
|
{
|
|
SourcePoint->p_x = LEFT(tile);
|
|
SourcePoint->p_y = (MIN(TOP(tile),TOP(tp)) +
|
|
MAX(BOTTOM(tile), BOTTOM(tp))) >> 1;
|
|
while (!StackEmpty(devStack))
|
|
{
|
|
STACKPOP(devStack);
|
|
}
|
|
return(tp);
|
|
}
|
|
else
|
|
{
|
|
const ClientData ticlient = TiGetClient(tp);
|
|
if (ticlient != CLIENTDEFAULT)
|
|
{
|
|
tileJunk *tj = (tileJunk *)CD2PTR(ticlient);
|
|
if (tj->tj_status & RES_TILE_DEV)
|
|
{
|
|
if (!(tj->tj_status & RES_TILE_PUSHED))
|
|
{
|
|
tj->tj_status |= RES_TILE_PUSHED;
|
|
STACKPUSH((ClientData)tp, devStack);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* right */
|
|
for (tp = TR(tile); TOP(tp) > BOTTOM(tile); tp = LB(tp))
|
|
{
|
|
t2 = TiGetLeftType(tp);
|
|
if ((t2 != TT_SPACE) &&
|
|
TTMaskHasType(&(devptr->exts_deviceSDTypes[i]), t2))
|
|
{
|
|
SourcePoint->p_x = RIGHT(tile);
|
|
SourcePoint->p_y = (MIN(TOP(tile), TOP(tp))+
|
|
MAX(BOTTOM(tile), BOTTOM(tp))) >> 1;
|
|
while (!StackEmpty(devStack))
|
|
{
|
|
STACKPOP(devStack);
|
|
}
|
|
return(tp);
|
|
}
|
|
else
|
|
{
|
|
const ClientData ticlient = TiGetClient(tp);
|
|
if (ticlient != CLIENTDEFAULT)
|
|
{
|
|
tileJunk *tj = (tileJunk *)CD2PTR(ticlient);
|
|
if (tj->tj_status & RES_TILE_DEV)
|
|
{
|
|
if (!(tj->tj_status & RES_TILE_PUSHED))
|
|
{
|
|
tj->tj_status |= RES_TILE_PUSHED;
|
|
STACKPUSH((ClientData)tp, devStack);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* top */
|
|
for (tp = RT(tile); RIGHT(tp) > LEFT(tile); tp = BL(tp))
|
|
{
|
|
t2 = TiGetBottomType(tp);
|
|
if ((t2 != TT_SPACE) &&
|
|
TTMaskHasType(&(devptr->exts_deviceSDTypes[i]), t2))
|
|
{
|
|
SourcePoint->p_y = TOP(tile);
|
|
SourcePoint->p_x = (MIN(RIGHT(tile),RIGHT(tp)) +
|
|
MAX(LEFT(tile), LEFT(tp))) >> 1;
|
|
while (!StackEmpty(devStack))
|
|
{
|
|
STACKPOP(devStack);
|
|
}
|
|
return(tp);
|
|
}
|
|
else
|
|
{
|
|
const ClientData ticlient = TiGetClient(tp);
|
|
if (ticlient != CLIENTDEFAULT)
|
|
{
|
|
tileJunk *tj = (tileJunk *)CD2PTR(ticlient);
|
|
if (tj->tj_status & RES_TILE_DEV)
|
|
{
|
|
if (!(tj->tj_status & RES_TILE_PUSHED))
|
|
{
|
|
tj->tj_status |= RES_TILE_PUSHED;
|
|
STACKPUSH((ClientData)tp, devStack);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* bottom */
|
|
for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp = TR(tp))
|
|
{
|
|
t2 = TiGetTopType(tp);
|
|
if ((t2 != TT_SPACE) &&
|
|
TTMaskHasType(&(devptr->exts_deviceSDTypes[i]), t2))
|
|
{
|
|
SourcePoint->p_y = BOTTOM(tile);
|
|
SourcePoint->p_x = (MIN(RIGHT(tile), RIGHT(tp)) +
|
|
MAX(LEFT(tile), LEFT(tp))) >> 1;
|
|
while (!StackEmpty(devStack))
|
|
{
|
|
STACKPOP(devStack);
|
|
}
|
|
return(tp);
|
|
}
|
|
else
|
|
{
|
|
const ClientData ticlient = TiGetClient(tp);
|
|
if (ticlient != CLIENTDEFAULT)
|
|
{
|
|
tileJunk *tj = (tileJunk *)CD2PTR(ticlient);
|
|
if (tj->tj_status & RES_TILE_DEV)
|
|
{
|
|
if (!(tj->tj_status & RES_TILE_PUSHED))
|
|
{
|
|
tj->tj_status |= RES_TILE_PUSHED;
|
|
STACKPUSH((ClientData)tp, devStack);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Didn't find a terminal (S/D) type tile in the perimeter search. */
|
|
/* Check if S/D types are in a different plane from the identifier. */
|
|
|
|
TiToRect(tile, &r);
|
|
tp = NULL;
|
|
for (i = 0; i < devptr->exts_deviceSDCount; i++)
|
|
{
|
|
for (pnum = 0; pnum < DBNumPlanes; pnum++)
|
|
{
|
|
DBSrPaintArea((Tile *)NULL, ResUse->cu_def->cd_planes[pnum],
|
|
&r, &(devptr->exts_deviceSDTypes[i]), ResGetTileFunc, &tp);
|
|
if (tp != NULL) return tp;
|
|
}
|
|
}
|
|
|
|
/* Is it possible that the net is the substrate under the device? */
|
|
for (pnum = 0; pnum < DBNumPlanes; pnum++)
|
|
{
|
|
DBSrPaintArea((Tile *)NULL, ResUse->cu_def->cd_planes[pnum],
|
|
&r, &(devptr->exts_deviceSubstrateTypes), ResGetTileFunc, &tp);
|
|
if (tp != NULL) return tp;
|
|
}
|
|
}
|
|
|
|
/* If any device type has TT_SPACE as a substrate type, then don't
|
|
* issue an error; however, not handling the substrate as a
|
|
* resistive network is not a good idea and "extresist" needs to use
|
|
* the method employed by "extract" of drawing a substrate type out
|
|
* to the boundary of each subcell.
|
|
*/
|
|
|
|
for (devptr = ExtCurStyle->exts_device[t1]; devptr; devptr = devptr->exts_next)
|
|
if (TTMaskHasType(&(devptr->exts_deviceSubstrateTypes), TT_SPACE))
|
|
break;
|
|
|
|
/* Didn't find a terminal (S/D or substrate) type tile anywhere. Flag an error. */
|
|
|
|
if (devptr == NULL)
|
|
TxError("Couldn't find a terminal of the device at %d %d\n",
|
|
goodies->rg_devloc->p_x, goodies->rg_devloc->p_y);
|
|
return((Tile *) NULL);
|
|
}
|
|
|
|
/*
|
|
*-------------------------------------------------------------------------
|
|
*
|
|
* ResGetDevice -- Once the net is extracted, we still have to equate
|
|
* the sim file devices with the layout devices. ResGetDevice
|
|
* looks for a device at the given location. "type" is also
|
|
* specified to that the right plane will be searched.
|
|
*
|
|
* Results: returns device structure at location DevicePoint, if it
|
|
* exists.
|
|
*
|
|
* Side Effects: none
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
resDevice *
|
|
ResGetDevice(pt, type)
|
|
Point *pt;
|
|
TileType type;
|
|
{
|
|
Point workingPoint;
|
|
Tile *tile;
|
|
int pnum;
|
|
|
|
workingPoint.p_x = (*pt).p_x;
|
|
workingPoint.p_y = (*pt).p_y;
|
|
|
|
pnum = DBPlane(type);
|
|
|
|
/* Start at hint tile for device plane */
|
|
|
|
tile = PlaneGetHint(ResUse->cu_def->cd_planes[pnum]);
|
|
GOTOPOINT(tile, &workingPoint);
|
|
|
|
const ClientData ticlient = TiGetClient(tile);
|
|
if (IsSplit(tile))
|
|
{
|
|
if (TTMaskHasType(&ExtCurStyle->exts_deviceMask, TiGetLeftType(tile))
|
|
|| TTMaskHasType(&ExtCurStyle->exts_deviceMask, TiGetRightType(tile)))
|
|
return (((tileJunk *)CD2PTR(ticlient))->deviceList);
|
|
}
|
|
else if (TTMaskHasType(&ExtCurStyle->exts_deviceMask, TiGetType(tile)))
|
|
{
|
|
/* Failure to have a valid client data will result in a "Bad Device"
|
|
* error and indicates a problem that needs debugging.
|
|
*/
|
|
if (ticlient != CLIENTDEFAULT)
|
|
return (((tileJunk *)CD2PTR(ticlient))->deviceList);
|
|
}
|
|
return NULL;
|
|
}
|