magic/extract/ExtNghbors.c

632 lines
18 KiB
C

/*
* ExtNeighbors.c --
*
* Circuit extraction.
* This file contains the primitive function ExtFindNeighbors()
* for visiting all neighbors of a tile that connect to it, and
* applying a filter function at each tile.
*
* *********************************************************************
* * 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/extract/ExtNghbors.c,v 1.3 2010/09/12 20:32:33 tim Exp $";
#endif /* not lint */
#include <stdio.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 "debug/debug.h"
#include "extract/extract.h"
#include "extract/extractInt.h"
#include "utils/signals.h"
#include "utils/stack.h"
/*
* The algorithm used by ExtFindNeighbors is non-recursive.
* It uses a stack (extNodeStack) to hold a list of tiles yet to
* be processed. To mark a tile as being on the stack, we store
* the value VISITPENDING in its ti_client field.
*/
/*
* ----------------------------------------------------------------------------
*
* ExtFindNeighbors --
*
* For each tile adjacent to 'tile' that connects to it (according to
* arg->fra_connectsTo), and (if it is a contact) for tiles on other
* planes that connect to it, we recursively visit the tile, call the
* client's filter procedure (*arg->fra_each)(), if it is non-NULL.
* The tile is marked as being visited by setting it's ti_client field
* to arg->fra_region.
*
* Results:
* Returns the number of tiles that are found to be connected.
* This is used to find, for example, transistor gates made of
* only one tile, for which a simple calculation suffices for
* computing length and width. If an error occurred, returns
* the value -1.
*
* Side effects:
* See comments above.
*
* ----------------------------------------------------------------------------
*/
int
ExtFindNeighbors(tile, dinfo, tilePlaneNum, arg)
Tile *tile;
TileType dinfo;
int tilePlaneNum;
FindRegion *arg;
{
TileTypeBitMask *connTo = arg->fra_connectsTo;
Tile *tp;
TileType type, t, tpdinfo;
TileTypeBitMask *mask;
Rect biggerArea;
int pNum, tilesfound;
PlaneMask pMask;
PlaneAndArea pla;
ClientData extNbrUn = arg->fra_uninit;
tilesfound = 0;
if (extNodeStack == (Stack *) NULL)
extNodeStack = StackNew(64);
/* Mark this tile as pending and push it */
PUSHTILE(tile, dinfo, tilePlaneNum);
while (!StackEmpty(extNodeStack))
{
POPTILE(tile, dinfo, tilePlaneNum);
if (IsSplit(tile))
{
type = (dinfo & TT_SIDE) ? SplitRightType(tile):
SplitLeftType(tile);
}
else
type = TiGetTypeExact(tile);
ASSERT(type != TT_SPACE, "ExtFindNeighbors");
mask = &connTo[type];
/*
* Since tile was pushed on the stack, we know that it
* belongs to this region. Check to see that it hasn't
* been visited in the meantime. If it's still unvisited,
* visit it and process its neighbors.
*/
if (ExtGetRegion(tile, dinfo) == arg->fra_region)
continue;
ExtSetRegion(tile, dinfo, arg->fra_region);
tilesfound++;
if (DebugIsSet(extDebugID, extDebNeighbor))
extShowTile(tile, "neighbor", 1);
/* Top */
topside:
if (IsSplit(tile) && ((dinfo & TT_SIDE) ? 1 : 0) ^ SplitDirection(tile))
goto leftside;
for (tp = RT(tile); RIGHT(tp) > LEFT(tile); tp = BL(tp))
{
if (IsSplit(tp))
{
t = SplitBottomType(tp);
tpdinfo = SplitDirection(tp) ? (TileType)0 : (TileType)TT_SIDE;
if (ExtGetRegion(tp, tpdinfo) == CD2PTR(extNbrUn) && TTMaskHasType(mask, t))
{
PUSHTILEBOTTOM(tp, tilePlaneNum);
}
}
else
{
t = TiGetTypeExact(tp);
if (TiGetClient(tp) == extNbrUn && TTMaskHasType(mask, t))
{
PUSHTILELEFT(tp, tilePlaneNum);
}
}
}
/* Left */
leftside:
if (IsSplit(tile) && (dinfo & TT_SIDE)) goto bottomside;
for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp = RT(tp))
{
if (IsSplit(tp))
{
t = SplitRightType(tp);
if (ExtGetRegion(tp, (TileType)TT_SIDE) == CD2PTR(extNbrUn)
&& TTMaskHasType(mask, t))
{
PUSHTILERIGHT(tp, tilePlaneNum);
}
}
else
{
t = TiGetTypeExact(tp);
if (TiGetClient(tp) == extNbrUn && TTMaskHasType(mask, t))
{
PUSHTILELEFT(tp, tilePlaneNum);
}
}
}
/* Bottom */
bottomside:
if (IsSplit(tile) && (!(((dinfo & TT_SIDE) ? 1 : 0) ^ SplitDirection(tile))))
goto rightside;
for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp = TR(tp))
{
if (IsSplit(tp))
{
t = SplitTopType(tp);
tpdinfo = SplitDirection(tp) ? (TileType)TT_SIDE : (TileType)0;
if (ExtGetRegion(tp, tpdinfo) == CD2PTR(extNbrUn) && TTMaskHasType(mask, t))
{
PUSHTILETOP(tp, tilePlaneNum);
}
}
else
{
t = TiGetTypeExact(tp);
if (TiGetClient(tp) == extNbrUn && TTMaskHasType(mask, t))
{
PUSHTILELEFT(tp, tilePlaneNum);
}
}
}
/* Right */
rightside:
if (IsSplit(tile) && !(dinfo & TT_SIDE)) goto donesides;
for (tp = TR(tile); TOP(tp) > BOTTOM(tile); tp = LB(tp))
{
if (IsSplit(tp))
{
t = SplitLeftType(tp);
if (ExtGetRegion(tp, (TileType)0) == CD2PTR(extNbrUn)
&& TTMaskHasType(mask, t))
{
PUSHTILELEFT(tp, tilePlaneNum);
}
}
else
{
t = TiGetTypeExact(tp);
if (TiGetClient(tp) == extNbrUn && TTMaskHasType(mask, t))
{
PUSHTILELEFT(tp, tilePlaneNum);
}
}
}
donesides:
/* Apply the client's filter procedure if one exists */
if (arg->fra_each)
if ((*arg->fra_each)(tile, dinfo, tilePlaneNum, arg))
goto fail;
/* Use tilePlaneNum value -1 to force ExtFindNeighbors to stay
* on a single plane.
*/
if (tilePlaneNum < 0) continue;
/* If this is a contact, visit all the other planes */
if (DBIsContact(type))
{
pMask = DBConnPlanes[type];
pMask &= ~(PlaneNumToMaskBit(tilePlaneNum));
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
if (PlaneMaskHasPlane(pMask, pNum))
{
ExtRegion *tpreg;
Plane *plane = arg->fra_def->cd_planes[pNum];
/* Find the point on the new plane */
tp = PlaneGetHint(plane);
GOTOPOINT(tp, &tile->ti_ll);
PlaneSetHint(plane, tp);
/* tp and tile should have the same geometry for a contact */
if (IsSplit(tile) && IsSplit(tp))
{
if (dinfo & TT_SIDE)
{
/* Only process tp if not yet visited */
tpreg = ExtGetRegion(tp, (TileType)TT_SIDE);
if (tpreg != CD2PTR(extNbrUn)) continue;
t = SplitRightType(tp);
if (TTMaskHasType(mask, t))
{
PUSHTILERIGHT(tp, pNum);
}
}
else
{
/* Only process tp if not yet visited */
tpreg = ExtGetRegion(tp, (TileType)0);
if (tpreg != CD2PTR(extNbrUn)) continue;
t = SplitLeftType(tp);
if (TTMaskHasType(mask, t))
{
PUSHTILELEFT(tp, pNum);
}
}
}
else if (IsSplit(tp))
{
/* Only process tp if not yet visited */
tpreg = ExtGetRegion(tp, (TileType)TT_SIDE);
if (tpreg == CD2PTR(extNbrUn))
{
t = SplitRightType(tp);
if (TTMaskHasType(mask, t))
{
PUSHTILERIGHT(tp, pNum);
}
}
/* Try both sides */
tpreg = ExtGetRegion(tp, (TileType)0);
if (tpreg == CD2PTR(extNbrUn))
{
t = SplitLeftType(tp);
if (TTMaskHasType(mask, t))
{
PUSHTILELEFT(tp, pNum);
}
}
}
else
{
/* Only process tp if not yet visited */
tpreg = ExtGetRegion(tp, (TileType)0);
if (tpreg != CD2PTR(extNbrUn)) continue;
t = TiGetTypeExact(tp);
if (TTMaskHasType(mask, t))
{
PUSHTILELEFT(tp, pNum);
}
}
}
}
/*
* 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]))
{
pla.uninit = extNbrUn;
TITORECT(tile, &pla.area);
GEO_EXPAND(&pla.area, 1, &biggerArea);
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
if ((pNum != tilePlaneNum) && PlaneMaskHasPlane(pMask, pNum))
{
pla.plane = pNum;
(void) DBSrPaintNMArea((Tile *) NULL,
arg->fra_def->cd_planes[pNum],
TiGetTypeExact(tile) | dinfo, &biggerArea,
mask, extNbrPushFunc, (ClientData) &pla);
}
}
}
return tilesfound;
fail:
/* Flush the stack */
while (!StackEmpty(extNodeStack))
{
POPTILE(tile, dinfo, tilePlaneNum);
ExtSetRegion(tile, dinfo, arg->fra_region);
}
return -1;
}
/*
* ----------------------------------------------------------------------------
*
* extNbrPushFunc --
*
* Called for each tile overlapped by a 1-unit wide halo around the area
* tileArea. If the tile overlaps or shares a non-null segment of border
* with tileArea, and it hasn't already been visited, push it on the stack
* extNodeStack.
*
* Uses the value pla->uninit to determine whether or not a tile has been
* visited; if the tile's client field is equal to pla->uninit, then this
* is the first time the tile has been seen.
*
* Results:
* Always returns 0.
*
* Side effects:
* None.
*
* ----------------------------------------------------------------------------
*/
int
extNbrPushFunc(tile, dinfo, pla)
Tile *tile;
TileType dinfo;
PlaneAndArea *pla;
{
Rect *tileArea;
Rect r;
tileArea = &pla->area;
/* Ignore tile if it's already been visited */
if (ExtGetRegion(tile, dinfo) != CD2PTR(pla->uninit))
return 0;
/* Only consider tile if it overlaps tileArea or shares part of a side */
TITORECT(tile, &r);
if (!GEO_OVERLAP(&r, tileArea))
{
GEOCLIP(&r, tileArea);
if (r.r_xbot >= r.r_xtop && r.r_ybot >= r.r_ytop)
return 0;
}
/* Push tile on the stack and mark as being visited */
PUSHTILE(tile, dinfo, pla->plane);
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* extEnumTerminal ---
*
* Search out an area belonging to a device terminal starting with a given
* tile, and running a callback function for each tile found. Note that
* this routine is called from inside extEnumTilePerim and so is already
* inside an extFindNeighbors() search function. The function must be
* careful not to modify regions, as the outer search function depends on
* them. Because a device terminal should be a compact area, it is okay
* to create a linked list of tiles and use the linked list to reset the
* regions at the end, rather than depending on the state of any tile's
* ClientData record.
*
* Results:
* None.
*
* Side effects:
* Whatever the callback function does. Specifically, changing tile
* ClientData records is *not* supposed to be a side effect of this
* function, and all ClientData modifications must be put back exactly
* as they were found.
*
* NOTE: This routine should be called only once for each device terminal.
* Once the terminal area and perimeter have been measured, it will not be
* called again.
*
* ----------------------------------------------------------------------------
*/
void
extEnumTerminal(Tile *tile, /* Starting tile for search */
TileType dinfo, /* Split tile information */
TileTypeBitMask *connect, /* Pointer to connection table */
void (*func)(), /* Callback function */
ClientData clientData) /* Client data for callback function */
{
ExtRegion *termreg;
TileAndDinfo *pendlist = NULL, *resetlist = NULL;
TileAndDinfo *curtad;
const TileTypeBitMask *connectMask;
Tile *tp, *t2;
TileType tpdi, t2di;
TileType loctype, checktype;
Rect tileArea;
/* The region attached to the first terminal tile will be the
* "uninitialized" value to check.
*/
termreg = ExtGetRegion(tile, dinfo);
/* Set the ClientData to VISITPENDING */
ExtSetRegion(tile, dinfo, (ExtRegion *)VISITPENDING);
/* Start the linked list with this file */
curtad = (TileAndDinfo *)mallocMagic(sizeof(TileAndDinfo));
curtad->tad_tile = tile;
curtad->tad_dinfo = dinfo;
curtad->tad_next = NULL;
pendlist = curtad;
/* Yet another boundary search routine. Just done with a linked list
* and not a stack because it's expected to search only a handful of
* tiles.
*/
while (pendlist != NULL)
{
tp = pendlist->tad_tile;
tpdi = pendlist->tad_dinfo;
TiToRect(tp, &tileArea);
/* Call the client function. The function has no return value. */
(*func)(tp, tpdi, clientData);
/* Move this tile entry to reset list */
curtad = pendlist;
pendlist = pendlist->tad_next;
curtad->tad_next = resetlist;
resetlist = curtad;
/* Search all sides of the tile for other tiles having the same
* terminal node (same region in the ClientData record),
* and add them to the linked list. This code is largely copied
* from dbSrConnectFunc(). Note that the connect table is used
* because the device's gate node may have the same region but
* is not part of the terminal.
*/
if (IsSplit(tp))
{
if (tpdi & TT_SIDE)
loctype = SplitRightType(tp);
else
loctype = SplitLeftType(tp);
}
else
loctype = TiGetTypeExact(tp);
connectMask = &connect[loctype];
/* Left side */
if (IsSplit(tp) && (tpdi & TT_SIDE)) goto termbottom;
for (t2 = BL(tp); BOTTOM(t2) < tileArea.r_ytop; t2 = RT(t2))
{
if (IsSplit(t2))
checktype = SplitRightType(t2);
else
checktype = TiGetTypeExact(t2);
if (TTMaskHasType(connectMask, checktype))
{
t2di = (TileType)TT_SIDE;
/* Tile must belong to the terminal node and not been visited */
if (ExtGetRegion(t2, t2di) != termreg) continue;
/* Add t2 to the linked list */
curtad = (TileAndDinfo *)mallocMagic(sizeof(TileAndDinfo));
curtad->tad_tile = t2;
curtad->tad_dinfo = t2di;
curtad->tad_next = pendlist;
pendlist = curtad;
/* Set the ClientData to VISITPENDING */
ExtSetRegion(t2, t2di, (ExtRegion *)VISITPENDING);
}
}
/* Bottom side */
termbottom:
if (IsSplit(tp) && ((!((tpdi & TT_SIDE) ? 1 : 0)) ^ SplitDirection(tp)))
goto termright;
for (t2 = LB(tp); LEFT(t2) < tileArea.r_xtop; t2 = TR(t2))
{
if (IsSplit(t2))
checktype = SplitTopType(t2);
else
checktype = TiGetTypeExact(t2);
if (TTMaskHasType(connectMask, checktype))
{
t2di = SplitDirection(t2) ? (TileType)TT_SIDE : (TileType)0;
/* Tile must belong to the terminal node and not been visited */
if (ExtGetRegion(t2, t2di) != termreg) continue;
/* Add t2 to the linked list */
curtad = (TileAndDinfo *)mallocMagic(sizeof(TileAndDinfo));
curtad->tad_tile = t2;
curtad->tad_dinfo = t2di;
curtad->tad_next = pendlist;
pendlist = curtad;
/* Set the ClientData to VISITPENDING */
ExtSetRegion(t2, t2di, (ExtRegion *)VISITPENDING);
}
}
/* Right side: */
termright:
if (IsSplit(tp) && !(tpdi & TT_SIDE)) goto termtop;
for (t2 = TR(tp); TOP(t2) > tileArea.r_ybot; t2 = LB(t2))
{
if (IsSplit(t2))
checktype = SplitLeftType(t2);
else
checktype = TiGetTypeExact(t2);
if (TTMaskHasType(connectMask, checktype))
{
t2di = (TileType)0;
/* Tile must belong to the terminal node and not been visited */
if (ExtGetRegion(t2, t2di) != termreg) continue;
/* Add t2 to the linked list */
curtad = (TileAndDinfo *)mallocMagic(sizeof(TileAndDinfo));
curtad->tad_tile = t2;
curtad->tad_dinfo = t2di;
curtad->tad_next = pendlist;
pendlist = curtad;
/* Set the ClientData to VISITPENDING */
ExtSetRegion(t2, t2di, (ExtRegion *)VISITPENDING);
}
}
/* Top side */
termtop:
if (IsSplit(tp) && (((tpdi & TT_SIDE) ? 1 : 0) ^ SplitDirection(tp)))
goto termdone;
for (t2 = RT(tp); RIGHT(t2) > tileArea.r_xbot; t2 = BL(t2))
{
if (IsSplit(t2))
checktype = SplitBottomType(t2);
else
checktype = TiGetTypeExact(t2);
if (TTMaskHasType(connectMask, checktype))
{
t2di = SplitDirection(t2) ? (TileType)0 : (TileType)TT_SIDE;
/* Tile must belong to the terminal node and not been visited */
if (ExtGetRegion(t2, t2di) != termreg) continue;
/* Add t2 to the linked list */
curtad = (TileAndDinfo *)mallocMagic(sizeof(TileAndDinfo));
curtad->tad_tile = t2;
curtad->tad_dinfo = t2di;
curtad->tad_next = pendlist;
pendlist = curtad;
/* Set the ClientData to VISITPENDING */
ExtSetRegion(t2, t2di, (ExtRegion *)VISITPENDING);
}
}
termdone:
/* (continue) */
}
/* Clean up---Put the ClientData entries in the tiles back to
* term reg and free up the linked list memory.
*/
while (resetlist != NULL)
{
curtad = resetlist->tad_next;
ExtSetRegion(resetlist->tad_tile, resetlist->tad_dinfo, termreg);
freeMagic(resetlist);
resetlist = curtad;
}
}