1101 lines
29 KiB
C
1101 lines
29 KiB
C
/*
|
|
* grouteChan.c --
|
|
*
|
|
* Code to maintain a map of usable channel areas, using a
|
|
* tile plane. One tile is used for each "unobstructed"
|
|
* channel area, namely one that contains no embedded regions
|
|
* where the channel's density has been exceeded.
|
|
*
|
|
* *********************************************************************
|
|
* * 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. *
|
|
* *********************************************************************
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "utils/magic.h"
|
|
#include "utils/geometry.h"
|
|
#include "utils/styles.h"
|
|
#include "utils/hash.h"
|
|
#include "utils/heap.h"
|
|
#include "utils/utils.h"
|
|
#include "tiles/tile.h"
|
|
#include "database/database.h"
|
|
#include "debug/debug.h"
|
|
#include "gcr/gcr.h"
|
|
#include "windows/windows.h"
|
|
#include "utils/main.h"
|
|
#include "dbwind/dbwind.h"
|
|
#include "utils/signals.h"
|
|
#include "router/router.h"
|
|
#include "grouter/grouter.h"
|
|
#include "utils/netlist.h"
|
|
#include "utils/styles.h"
|
|
#include "textio/textio.h"
|
|
#include "utils/malloc.h"
|
|
|
|
/* Plane to hold channel information */
|
|
Plane *glChanPlane;
|
|
|
|
/* Dummy celldef whose PL_DRC_CHECK plane is identical to glChanPlane above */
|
|
CellDef *glChanDef;
|
|
CellUse *glChanUse;
|
|
|
|
TileTypeBitMask glMaskRiver; /* Mask of river channel types */
|
|
TileTypeBitMask glMaskNormal; /* Mask of normal channel type */
|
|
TileTypeBitMask glMaskChannel; /* Mask of all channel types */
|
|
|
|
int glChanSplitRiver();
|
|
int glChanRiverBlock();
|
|
int glChanFloodVFunc(), glChanFloodHFunc();
|
|
bool glChanClip();
|
|
int glChanClipFunc();
|
|
int glChanMergeFunc();
|
|
int glChanFeedFunc();
|
|
int glChanPaintFunc();
|
|
int glChanSetClient();
|
|
void glChanFreeMap();
|
|
void glChanCheckCover();
|
|
void glChanBlockDens();
|
|
void glChanFlood();
|
|
void glChanShowTiles();
|
|
int glChanShowFunc();
|
|
|
|
typedef struct pa
|
|
{
|
|
Rect pa_area;
|
|
TileType pa_type;
|
|
struct pa *pa_next;
|
|
} PaintArea;
|
|
|
|
PaintArea *glChanPaintList;
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* glChanBuildMap --
|
|
*
|
|
* Build a tile plane that represents all the channels in the routing
|
|
* problem. This tile plane will contain tiles of four types: CT_BLOCKED,
|
|
* CT_NORMAL, CT_HRIVER, and CT_VRIVER, corresponding to unusable routing
|
|
* area and the three types of channels respectively.
|
|
*
|
|
* Each channel tile's client field points back to the channel it covers.
|
|
* A single channel can be covered by several tiles, however. Initially,
|
|
* each normal channel is covered by a single tile, but each river tile
|
|
* is covered by several tiles. For example, the tiles covering a HRIVER
|
|
* channel are chosen such that there is never a vertical tile boundary
|
|
* on either their RHS or LHS except at the top or bottom of the CT_HRIVER
|
|
* tile. Situations such as the following:
|
|
*
|
|
* +-------+
|
|
* | |
|
|
* | | normal
|
|
* | |
|
|
* |HRIVER +------
|
|
* | |
|
|
* | | normal
|
|
* | |
|
|
* +-------+
|
|
*
|
|
* aren't allowed to exist; the HRIVER tile gets split:
|
|
*
|
|
* +-------+
|
|
* | |
|
|
* |HRIVER | normal
|
|
* | |
|
|
* +-------+------
|
|
* | |
|
|
* |HRIVER | normal
|
|
* | |
|
|
* +-------+
|
|
*
|
|
* The reason for splitting CT_HRIVER tiles in this way (and CT_VRIVER tiles
|
|
* vertically using similar criteria) is to ensure that there is a different
|
|
* river tile for each channel on its opposite side. This fact is crucial
|
|
* to the working of the inner loop of the global router; see the code in
|
|
* groutePair.c for details of the algorithm used.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Builds the tile plane glChanPlane as described above.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
glChanBuildMap(chanList)
|
|
GCRChannel *chanList; /* List of all channels in routing problem */
|
|
{
|
|
GCRChannel *ch;
|
|
bool workDone;
|
|
|
|
if (glChanPlane == NULL)
|
|
{
|
|
DBNewYank("__CHANMAP__", &glChanUse, &glChanDef);
|
|
glChanPlane = glChanDef->cd_planes[PL_DRC_CHECK];
|
|
glChanFreeMap();
|
|
TTMaskSetOnlyType(&glMaskRiver, CHAN_HRIVER);
|
|
TTMaskSetType(&glMaskRiver, CHAN_VRIVER);
|
|
TTMaskSetOnlyType(&glMaskNormal, CHAN_NORMAL);
|
|
TTMaskSetMask3(&glMaskChannel, &glMaskNormal, &glMaskRiver);
|
|
}
|
|
|
|
/*
|
|
* First pass: use painting to cover the areas of all channels
|
|
* with the appropriate type of tile.
|
|
*/
|
|
for (ch = chanList; ch; ch = ch->gcr_next)
|
|
DBPaintPlane(glChanPlane, &ch->gcr_area, DBStdWriteTbl(ch->gcr_type),
|
|
(PaintUndoInfo *) NULL);
|
|
|
|
if (DebugIsSet(glDebugID, glDebTiles))
|
|
glChanShowTiles("After painting all channels");
|
|
|
|
/*
|
|
* Second pass: use splits and merges to ensure that each channel
|
|
* covers exactly one tile. Leave this tile's ti_client field
|
|
* pointing to 'ch'.
|
|
*/
|
|
do
|
|
{
|
|
workDone = FALSE;
|
|
for (ch = chanList; ch; ch = ch->gcr_next)
|
|
if (glChanClip(ch))
|
|
workDone = TRUE;
|
|
}
|
|
while (workDone);
|
|
if (DebugIsSet(glDebugID, glDebTiles))
|
|
glChanShowTiles("After splits and merges");
|
|
if (DebugIsSet(glDebugID, glDebChan))
|
|
glChanCheckCover(chanList, &glMaskChannel);
|
|
|
|
/*
|
|
* Third pass: find regions of maximum density and "paint" them into
|
|
* the tile map. Regions where the density is exceeded in only one
|
|
* of the two possible directions through a normal channel result in
|
|
* painting a CHAN_HRIVER or CHAN_VRIVER tile as appropriate, while
|
|
* regions that are totally blocked result in CHAN_BLOCKED tiles.
|
|
* The client fields of the remaining nonblocked tiles are left
|
|
* pointing to the channels those tiles overlap.
|
|
*/
|
|
for (ch = chanList; ch; ch = ch->gcr_next)
|
|
glChanBlockDens(ch);
|
|
if (DebugIsSet(glDebugID, glDebTiles))
|
|
glChanShowTiles("After density blockages");
|
|
|
|
/*
|
|
* Fourth pass: split river tiles as necessary to ensure
|
|
* that no river tile has a perpendicular tile boundary
|
|
* on either of the sides from which signals can enter.
|
|
*/
|
|
while (DBSrPaintArea((Tile *) NULL, glChanPlane, &TiPlaneRect, &glMaskRiver,
|
|
glChanSplitRiver, (ClientData) NULL))
|
|
/* Nothing */;
|
|
if (DebugIsSet(glDebugID, glDebTiles))
|
|
glChanShowTiles("After splitting river tiles");
|
|
|
|
/*
|
|
* Final pass: turn any river tiles whose pins are completely
|
|
* blocked on one side into blocked tiles. This just involves
|
|
* changing the type of the tile.
|
|
*/
|
|
(void) DBSrPaintArea((Tile *) NULL, glChanPlane, &TiPlaneRect,
|
|
&glMaskRiver, glChanRiverBlock, (ClientData) NULL);
|
|
if (DebugIsSet(glDebugID, glDebTiles))
|
|
glChanShowTiles("After blocking river tiles");
|
|
if (DebugIsSet(glDebugID, glDebChan))
|
|
{
|
|
glChanCheckCover(chanList, &glMaskNormal);
|
|
(void) DBSrPaintArea((Tile *) NULL, glChanPlane, &TiPlaneRect,
|
|
&glMaskChannel, glChanFeedFunc, (ClientData) NULL);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* glChanFeedFunc --
|
|
*
|
|
* Used for leaving feedback for each channel tile above.
|
|
*
|
|
* Results:
|
|
* Always returns 0.
|
|
*
|
|
* Side effects:
|
|
* Leaves feedback for each tile found.
|
|
*/
|
|
|
|
int
|
|
glChanFeedFunc(tile)
|
|
Tile *tile;
|
|
{
|
|
char *mesg;
|
|
Rect r;
|
|
|
|
switch (TiGetType(tile))
|
|
{
|
|
case CHAN_NORMAL: mesg = "normal channel"; break;
|
|
case CHAN_HRIVER: mesg = "horizontal river channel"; break;
|
|
case CHAN_VRIVER: mesg = "vertical river channel"; break;
|
|
}
|
|
TITORECT(tile, &r);
|
|
DBWFeedbackAdd(&r, mesg, EditCellUse->cu_def, 1, STYLE_OUTLINEHIGHLIGHTS);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* glChanFreeMap --
|
|
*
|
|
* Free the tile plane built by glChanBuildMap().
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Frees memory.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
glChanFreeMap()
|
|
{
|
|
Tile *newCenterTile;
|
|
|
|
/* Eliminate all the tiles from this plane */
|
|
DBFreePaintPlane(glChanPlane);
|
|
|
|
/* Allocate a new central space tile */
|
|
newCenterTile = TiAlloc();
|
|
PlaneSetHint(glChanPlane, newCenterTile);
|
|
TiSetBody(newCenterTile, CHAN_BLOCKED);
|
|
dbSetPlaneTile(glChanPlane, newCenterTile);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* glChanCheckCover --
|
|
*
|
|
* Debugging procedure used to check that each channel whose type is
|
|
* contained in 'mask' is covered by exactly one tile. Also check
|
|
* all channels to make sure that the tiles they cover are the same
|
|
* as the channel type (except for blockages).
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Leaves feedback where errors occur.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int glChanCheckCount;
|
|
|
|
void
|
|
glChanCheckCover(chanList, mask)
|
|
GCRChannel *chanList;
|
|
TileTypeBitMask *mask;
|
|
{
|
|
int glChanCheckFunc();
|
|
GCRChannel *ch;
|
|
char mesg[1024];
|
|
|
|
for (ch = chanList; ch; ch = ch->gcr_next)
|
|
{
|
|
glChanCheckCount = 0;
|
|
(void) DBSrPaintArea((Tile *) NULL, glChanPlane, &ch->gcr_area,
|
|
&DBAllTypeBits, glChanCheckFunc,
|
|
(ClientData) ch);
|
|
if (TTMaskHasType(mask, ch->gcr_type) && glChanCheckCount != 1)
|
|
{
|
|
(void) sprintf(mesg, "%d tiles over channel", glChanCheckCount);
|
|
DBWFeedbackAdd(&ch->gcr_area, mesg, EditCellUse->cu_def, 1,
|
|
STYLE_PALEHIGHLIGHTS);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* glChanCheckFunc --
|
|
*
|
|
* Called by above for each tile overlapping a channel.
|
|
*
|
|
* Results:
|
|
* Always returns 0.
|
|
*
|
|
* Side effects:
|
|
* May leave feedback if a channel is overlapped by the
|
|
* wrong type of tile.
|
|
*/
|
|
|
|
int
|
|
glChanCheckFunc(tile, ch)
|
|
Tile *tile;
|
|
GCRChannel *ch;
|
|
{
|
|
char mesg[1024];
|
|
Rect r;
|
|
|
|
glChanCheckCount++;
|
|
if (NOTBLOCKED(tile))
|
|
{
|
|
if (TiGetType(tile) != ch->gcr_type)
|
|
{
|
|
TITORECT(tile, &r);
|
|
(void) sprintf(mesg, "Different tile type %d for chan %d",
|
|
TiGetType(tile), ch->gcr_type);
|
|
DBWFeedbackAdd(&r, mesg, EditCellUse->cu_def,
|
|
1, STYLE_MEDIUMHIGHLIGHTS);
|
|
}
|
|
if (tile->ti_client != (ClientData) ch)
|
|
{
|
|
TITORECT(tile, &r);
|
|
(void) sprintf(mesg, "Tile client 0x%"DLONG_PREFIX"x doesn't match chan %p",
|
|
(dlong) tile->ti_client, (void *) ch);
|
|
DBWFeedbackAdd(&r, mesg, EditCellUse->cu_def,
|
|
1, STYLE_MEDIUMHIGHLIGHTS);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* glChanClip --
|
|
*
|
|
* Leave a single tile overlapping the area of channel 'ch'.
|
|
* Leave this tile pointing to 'ch'.
|
|
*
|
|
* Results:
|
|
* Returns TRUE if we had to do any splits or merges,
|
|
* FALSE if everything was as it should be.
|
|
*
|
|
* Side effects:
|
|
* May split or merge tiles.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
bool
|
|
glChanClip(ch)
|
|
GCRChannel *ch;
|
|
{
|
|
bool ret;
|
|
|
|
ret = FALSE;
|
|
|
|
/* First part: clip every tile overlapping this channel to its boundary */
|
|
while (DBSrPaintArea((Tile *) NULL, glChanPlane, &ch->gcr_area,
|
|
&DBAllTypeBits, glChanClipFunc, (ClientData) &ch->gcr_area))
|
|
ret = TRUE;
|
|
|
|
/* Second part: set the client field of every tile to 'ch' */
|
|
(void) DBSrPaintArea((Tile *) NULL, glChanPlane, &ch->gcr_area,
|
|
&DBAllTypeBits, glChanSetClient, (ClientData) ch);
|
|
|
|
/*
|
|
* Third part: merge every tile overlapping this channel into a
|
|
* single one. The procedure called for each tile extracts the
|
|
* channel boundaries from the client field of the tile, and
|
|
* then generates merges within the channel boundary.
|
|
*/
|
|
while (DBSrPaintArea((Tile *) NULL, glChanPlane, &ch->gcr_area,
|
|
&DBAllTypeBits, glChanMergeFunc, (ClientData) NULL))
|
|
ret = TRUE;
|
|
|
|
if (DebugIsSet(glDebugID, glDebTiles))
|
|
{
|
|
char mesg[256];
|
|
|
|
(void) sprintf(mesg, "After clipping chan %p", (void *) ch);
|
|
glChanShowTiles(mesg);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
glChanSetClient(tile, cdata)
|
|
Tile *tile;
|
|
ClientData cdata;
|
|
{
|
|
tile->ti_client = cdata;
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
glChanShowTiles(mesg)
|
|
char *mesg;
|
|
{
|
|
char answer[100], m[1024];
|
|
|
|
DBWAreaChanged(glChanDef, &TiPlaneRect, DBW_ALLWINDOWS, 0);
|
|
WindUpdate();
|
|
(void) sprintf(m, "%s: --more-- (t for tiles): ", mesg);
|
|
if (TxGetLinePrompt(answer, sizeof answer, m) == NULL || answer[0] != 't')
|
|
return;
|
|
|
|
(void) DBSrPaintArea((Tile *) NULL, glChanPlane, &TiPlaneRect,
|
|
&DBAllTypeBits, glChanShowFunc, (ClientData) NULL);
|
|
}
|
|
|
|
int
|
|
glChanShowFunc(tile)
|
|
Tile *tile;
|
|
{
|
|
GCRChannel *ch;
|
|
char mesg[1024];
|
|
Rect r;
|
|
|
|
TITORECT(tile, &r);
|
|
ShowRect(EditCellUse->cu_def, &r, STYLE_PALEHIGHLIGHTS);
|
|
(void) sprintf(mesg, "tile ch=%"DLONG_PREFIX"x type=%d",
|
|
(dlong) tile->ti_client, TiGetType(tile));
|
|
TxMore(mesg);
|
|
ShowRect(EditCellUse->cu_def, &r, STYLE_ERASEHIGHLIGHTS);
|
|
if (tile->ti_client == (ClientData) CLIENTDEFAULT)
|
|
return 0;
|
|
ch = (GCRChannel *) tile->ti_client;
|
|
ShowRect(EditCellUse->cu_def, &ch->gcr_area, STYLE_MEDIUMHIGHLIGHTS);
|
|
(void) sprintf(mesg, "chan %p type=%d", (void *) ch, ch->gcr_type);
|
|
TxMore(mesg);
|
|
ShowRect(EditCellUse->cu_def, &ch->gcr_area, STYLE_ERASEHIGHLIGHTS);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* glChanClipFunc --
|
|
*
|
|
* Called by DBSrPaintArea() on behalf of glChanClip() above for each
|
|
* tile overlapping 'area': splits 'tile' as necessary to ensure that
|
|
* it is completely contained within area.
|
|
*
|
|
* Results:
|
|
* Returns 1 if we did any splitting, 0 otherwise.
|
|
*
|
|
* Side effects:
|
|
* May split tiles.
|
|
* When a tile is split, the new tile's body and ti_client
|
|
* field are set to copies of the original tile's.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
glChanClipFunc(tile, area)
|
|
Tile *tile;
|
|
Rect *area;
|
|
{
|
|
ClientData tileClient = tile->ti_client;
|
|
TileType type = TiGetType(tile);
|
|
Tile *newTile;
|
|
int ret;
|
|
|
|
ret = 0;
|
|
if (LEFT(tile) < area->r_xbot)
|
|
{
|
|
tile = TiSplitX(tile, area->r_xbot);
|
|
TiSetBody(tile, type);
|
|
tile->ti_client = tileClient;
|
|
ret = 1;
|
|
}
|
|
if (BOTTOM(tile) < area->r_ybot)
|
|
{
|
|
tile = TiSplitY(tile, area->r_ybot);
|
|
TiSetBody(tile, type);
|
|
tile->ti_client = tileClient;
|
|
ret = 1;
|
|
}
|
|
if (RIGHT(tile) > area->r_xtop)
|
|
{
|
|
newTile = TiSplitX(tile, area->r_xtop);
|
|
TiSetBody(newTile, type);
|
|
newTile->ti_client = tileClient;
|
|
ret = 1;
|
|
}
|
|
if (TOP(tile) > area->r_ytop)
|
|
{
|
|
newTile = TiSplitY(tile, area->r_ytop);
|
|
TiSetBody(newTile, type);
|
|
newTile->ti_client = tileClient;
|
|
ret = 1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* glChanMergeFunc --
|
|
*
|
|
* Called by DBSrPaintArea() on behalf of glChanClip() above for each
|
|
* tile contained within some area. Merges this tile with any of
|
|
* its neighbors within its channel's area, where the channel is
|
|
* pointed to by the ti_client field. We leave the resulting tile's
|
|
* ti_client field pointing to this channel ('ch').
|
|
*
|
|
* IMPORTANT ASSUMPTION: no tile actually overlaps the boundary of
|
|
* ch->gcr_area. This is ensured by the caller's having called
|
|
* glChanClipFunc() enough times to have split all the tiles
|
|
* that originally might have crossed the channel's boundary.
|
|
*
|
|
* Results:
|
|
* Returns 1 if we did any merging, 0 otherwise.
|
|
*
|
|
* Side effects:
|
|
* May merge tiles.
|
|
* Leaves tile->ti_client set to 'ch'.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
glChanMergeFunc(tile)
|
|
Tile *tile;
|
|
{
|
|
GCRChannel *ch = (GCRChannel *) tile->ti_client;
|
|
Tile *delayed = NULL; /* delayed free to extend lifetime */
|
|
Tile *tp;
|
|
int ret;
|
|
|
|
ret = 0;
|
|
if (TOP(tile) < ch->gcr_area.r_ytop)
|
|
{
|
|
tp = RT(tile);
|
|
if (TiGetType(tp) == TiGetType(tile)
|
|
&& LEFT(tp) == LEFT(tile)
|
|
&& RIGHT(tp) == RIGHT(tile))
|
|
{
|
|
TiJoinY1(&delayed, tile, tp, glChanPlane);
|
|
ret = 1;
|
|
}
|
|
}
|
|
if (LEFT(tile) > ch->gcr_area.r_xbot)
|
|
{
|
|
tp = BL(tile);
|
|
if (TiGetType(tp) == TiGetType(tile)
|
|
&& TOP(tp) == TOP(tile)
|
|
&& BOTTOM(tp) == BOTTOM(tile))
|
|
{
|
|
TiJoinX1(&delayed, tile, tp, glChanPlane);
|
|
ret = 1;
|
|
}
|
|
}
|
|
if (BOTTOM(tile) > ch->gcr_area.r_ybot)
|
|
{
|
|
tp = LB(tile);
|
|
if (TiGetType(tp) == TiGetType(tile)
|
|
&& LEFT(tp) == LEFT(tile)
|
|
&& RIGHT(tp) == RIGHT(tile))
|
|
{
|
|
TiJoinY1(&delayed, tile, tp, glChanPlane);
|
|
ret = 1;
|
|
}
|
|
}
|
|
if (RIGHT(tile) < ch->gcr_area.r_xtop)
|
|
{
|
|
tp = TR(tile);
|
|
if (TiGetType(tp) == TiGetType(tile)
|
|
&& TOP(tp) == TOP(tile)
|
|
&& BOTTOM(tp) == BOTTOM(tile))
|
|
{
|
|
TiJoinX1(&delayed, tile, tp, glChanPlane);
|
|
ret = 1;
|
|
}
|
|
}
|
|
|
|
TiFreeIf(delayed);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* glChanBlockDens --
|
|
*
|
|
* Find regions of maximum density in 'ch' (if 'ch' is a normal channel) and
|
|
* "paint" them into the tile map. Blockages of a single direction result
|
|
* in CHAN_HRIVER or CHAN_VRIVER tiles being painted as appropriate; if
|
|
* blockages exist in both directions, then we produce a CHAN_BLOCKED tile.
|
|
*
|
|
* We rely on the fact that regions of maximum density extend completely
|
|
* across a channel, and hence completely across any tile (since tiles are
|
|
* never bigger than channels). The client fields of the any tiles in the
|
|
* area of the channel that aren't totally blocked are left pointing to
|
|
* the overlapping channel.
|
|
*
|
|
* If columns i through j inclusive have maximum density, then the blockage
|
|
* we paint extends from midway between columns i-1 and i to midway between
|
|
* column j and j+1, guaranteeing that tile boundaries will always fall
|
|
* on half-grid lines.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* See above.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
glChanBlockDens(ch)
|
|
GCRChannel *ch;
|
|
{
|
|
GlobChan *gc = (GlobChan *) ch->gcr_client;
|
|
int halfGrid, shiftedOrigin;
|
|
DensMap *dRow, *dCol;
|
|
PaintArea *pa;
|
|
short *dens;
|
|
int lo, hi;
|
|
Rect area;
|
|
|
|
if (ch->gcr_type != CHAN_NORMAL)
|
|
return;
|
|
|
|
dCol = &gc->gc_postDens[CZ_COL];
|
|
dRow = &gc->gc_postDens[CZ_ROW];
|
|
halfGrid = RtrGridSpacing / 2;
|
|
glChanPaintList = (PaintArea *) NULL;
|
|
if (dCol->dm_max >= dCol->dm_cap)
|
|
{
|
|
dens = dCol->dm_value;
|
|
area.r_ybot = ch->gcr_area.r_ybot;
|
|
area.r_ytop = ch->gcr_area.r_ytop;
|
|
shiftedOrigin = ch->gcr_origin.p_x - halfGrid;
|
|
for (lo = 1; lo < dCol->dm_size; lo++)
|
|
{
|
|
if (dens[lo] >= dCol->dm_cap)
|
|
{
|
|
hi = lo+1;
|
|
while (dens[hi] >= dCol->dm_cap && hi < dCol->dm_size)
|
|
hi++;
|
|
|
|
/* Remember area of blocked tile to paint */
|
|
area.r_xbot = shiftedOrigin + lo * RtrGridSpacing;
|
|
area.r_xtop = shiftedOrigin + hi * RtrGridSpacing;
|
|
pa = (PaintArea *) mallocMagic((unsigned)(sizeof (PaintArea)));
|
|
pa->pa_area = area;
|
|
pa->pa_next = glChanPaintList;
|
|
pa->pa_type = CHAN_VRIVER;
|
|
glChanPaintList = pa;
|
|
|
|
/* Will get incremented in loop header */
|
|
lo = hi-1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dRow->dm_max >= dRow->dm_cap)
|
|
{
|
|
dens = dRow->dm_value;
|
|
area.r_xbot = ch->gcr_area.r_xbot;
|
|
area.r_xtop = ch->gcr_area.r_xtop;
|
|
shiftedOrigin = ch->gcr_origin.p_y - halfGrid;
|
|
for (lo = 1; lo < dRow->dm_size; lo++)
|
|
{
|
|
if (dens[lo] >= dRow->dm_cap)
|
|
{
|
|
hi = lo+1;
|
|
while (dens[hi] >= dRow->dm_cap && hi < dRow->dm_size)
|
|
hi++;
|
|
|
|
/* Remember area of blocked tile to paint */
|
|
area.r_ybot = shiftedOrigin + lo * RtrGridSpacing;
|
|
area.r_ytop = shiftedOrigin + hi * RtrGridSpacing;
|
|
pa = (PaintArea *) mallocMagic((unsigned)(sizeof (PaintArea)));
|
|
pa->pa_area = area;
|
|
pa->pa_next = glChanPaintList;
|
|
pa->pa_type = CHAN_HRIVER;
|
|
glChanPaintList = pa;
|
|
|
|
/* Will get incremented in loop header */
|
|
lo = hi-1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Now do all the work.
|
|
* First paint all the blocked areas on glChanPaintList.
|
|
* Then make a second pass through this list, searching the
|
|
* area around each blocked area tile we painted for river
|
|
* tiles of the appropriate type, and propagating this blockage
|
|
* to those river tiles as well. Do this until the wavefront
|
|
* stops, which will happen as soon as we reach a normal channel.
|
|
*/
|
|
do
|
|
{
|
|
for (pa = glChanPaintList; pa; pa = pa->pa_next)
|
|
{
|
|
/* Clip tiles overlapped by pa->pa_area */
|
|
while (DBSrPaintArea((Tile *) NULL, glChanPlane, &pa->pa_area,
|
|
&DBAllTypeBits, glChanClipFunc, PTR2CD(&pa->pa_area)))
|
|
/* Nothing */;
|
|
|
|
/* Change the type of all tiles within the area */
|
|
(void) DBSrPaintArea((Tile *) NULL, glChanPlane, &pa->pa_area,
|
|
&DBAllTypeBits, glChanPaintFunc, INT2CD(pa->pa_type));
|
|
|
|
/*
|
|
* Allow merging, as long as no tiles get merged across
|
|
* channel boundaries.
|
|
*/
|
|
while (DBSrPaintArea((Tile *) NULL, glChanPlane, &pa->pa_area,
|
|
&DBAllTypeBits, glChanMergeFunc, (ClientData) NULL));
|
|
}
|
|
|
|
/* Second pass to propagate blockages to nearby areas */
|
|
for (pa = glChanPaintList, glChanPaintList = NULL; pa; pa = pa->pa_next)
|
|
{
|
|
glChanFlood(&pa->pa_area, pa->pa_type);
|
|
freeMagic((char *) pa);
|
|
}
|
|
} while (glChanPaintList != NULL);
|
|
}
|
|
|
|
int
|
|
glChanPaintFunc(tile, type)
|
|
Tile *tile;
|
|
TileType type;
|
|
{
|
|
static TileType changeTable[4][4] = {
|
|
/* Paint atop CHAN_NORMAL */
|
|
{ CHAN_NORMAL, CHAN_HRIVER, CHAN_VRIVER, CHAN_BLOCKED },
|
|
|
|
/* Paint atop CHAN_HRIVER */
|
|
{ CHAN_HRIVER, CHAN_HRIVER, CHAN_BLOCKED, CHAN_BLOCKED },
|
|
|
|
/* Paint atop CHAN_VRIVER */
|
|
{ CHAN_VRIVER, CHAN_BLOCKED, CHAN_VRIVER, CHAN_BLOCKED },
|
|
|
|
/* Paint atop CHAN_BLOCKED */
|
|
{ CHAN_BLOCKED, CHAN_BLOCKED, CHAN_BLOCKED, CHAN_BLOCKED },
|
|
};
|
|
|
|
TiSetBody(tile, changeTable[TiGetType(tile)][type]);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* glChanFlood --
|
|
*
|
|
* Search all four sides of 'area' (an area that has just been painted
|
|
* as type) for river tiles of the appropriate type (where appropriate
|
|
* means tiles that are completely blocked by a tile of type type to
|
|
* the side to which it lies).
|
|
*
|
|
* Each time an appropriate river tiles is found, record a CHAN_BLOCKED
|
|
* rectangle on the list glChanPaintList that extends from 'area' through to
|
|
* the other side of the river channel.
|
|
*
|
|
* The purpose of this procedure is so that the presence of the blockage
|
|
* on one side of a river tile will be visible from its other side.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* May prepend stuff to glChanPaintList.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
glChanFlood(area, type)
|
|
Rect *area;
|
|
TileType type;
|
|
{
|
|
TileTypeBitMask hMask, vMask;
|
|
Rect outside;
|
|
|
|
TTMaskSetOnlyType(&hMask, CHAN_HRIVER);
|
|
TTMaskSetOnlyType(&vMask, CHAN_VRIVER);
|
|
|
|
if (type != CHAN_VRIVER)
|
|
{
|
|
/* TOP */
|
|
outside = *area;
|
|
outside.r_ybot = area->r_ytop;
|
|
outside.r_ytop = area->r_ytop + 1;
|
|
(void) DBSrPaintArea((Tile *) NULL, glChanPlane, &outside, &vMask,
|
|
glChanFloodVFunc, (ClientData) area);
|
|
|
|
/* BOTTOM */
|
|
outside = *area;
|
|
outside.r_ybot = area->r_ybot - 1;
|
|
outside.r_ytop = area->r_ybot;
|
|
(void) DBSrPaintArea((Tile *) NULL, glChanPlane, &outside, &vMask,
|
|
glChanFloodVFunc, (ClientData) area);
|
|
}
|
|
|
|
if (type != CHAN_HRIVER)
|
|
{
|
|
/* LEFT */
|
|
outside = *area;
|
|
outside.r_xbot = area->r_xbot - 1;
|
|
outside.r_xtop = area->r_xbot;
|
|
(void) DBSrPaintArea((Tile *) NULL, glChanPlane, &outside, &hMask,
|
|
glChanFloodHFunc, (ClientData) area);
|
|
|
|
/* RIGHT */
|
|
outside = *area;
|
|
outside.r_xbot = area->r_xtop;
|
|
outside.r_xtop = area->r_xtop + 1;
|
|
(void) DBSrPaintArea((Tile *) NULL, glChanPlane, &outside, &hMask,
|
|
glChanFloodHFunc, (ClientData) area);
|
|
}
|
|
}
|
|
|
|
int
|
|
glChanFloodVFunc(tile, area)
|
|
Tile *tile;
|
|
Rect *area;
|
|
{
|
|
PaintArea *pa;
|
|
|
|
pa = (PaintArea *) mallocMagic((unsigned) (sizeof (PaintArea)));
|
|
pa->pa_area.r_xbot = MAX(area->r_xbot, LEFT(tile));
|
|
pa->pa_area.r_xtop = MIN(area->r_xtop, RIGHT(tile));
|
|
pa->pa_area.r_ybot = BOTTOM(tile);
|
|
pa->pa_area.r_ytop = TOP(tile);
|
|
pa->pa_next = glChanPaintList;
|
|
pa->pa_type = CHAN_BLOCKED;
|
|
glChanPaintList = pa;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
glChanFloodHFunc(tile, area)
|
|
Tile *tile;
|
|
Rect *area;
|
|
{
|
|
PaintArea *pa;
|
|
|
|
pa = (PaintArea *) mallocMagic((unsigned)(sizeof (PaintArea)));
|
|
pa->pa_area.r_ybot = MAX(area->r_ybot, BOTTOM(tile));
|
|
pa->pa_area.r_ytop = MIN(area->r_ytop, TOP(tile));
|
|
pa->pa_area.r_xbot = LEFT(tile);
|
|
pa->pa_area.r_xtop = RIGHT(tile);
|
|
pa->pa_next = glChanPaintList;
|
|
pa->pa_type = CHAN_BLOCKED;
|
|
glChanPaintList = pa;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* glChanSplitRiver --
|
|
*
|
|
* Called for each river tile (CHAN_HRIVER or CHAN_VRIVER) in glChanPlane.
|
|
* Search along the two sides of this tile from which signals can enter
|
|
* (the LHS and RHS for CHAN_HRIVER, or the top and bottom for CHAN_VRIVER),
|
|
* and make sure that there are no tile boundaries involving some type of
|
|
* tile other than CHAN_BLOCKED along that side. If there is a boundary,
|
|
* split tile at the boundary.
|
|
*
|
|
* Results:
|
|
* Returns 1 if we did any splitting, 0 if not.
|
|
*
|
|
* Side effects:
|
|
* See above.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
glChanSplitRiver(tile)
|
|
Tile *tile;
|
|
{
|
|
ClientData tileClient = tile->ti_client;
|
|
Tile *tp, *newTile;
|
|
int ret;
|
|
|
|
ret = 0;
|
|
if (TiGetType(tile) == CHAN_HRIVER)
|
|
{
|
|
for (tp = BL(tile); TOP(tp) < TOP(tile); tp = RT(tp))
|
|
{
|
|
if (NOTBLOCKED(tp) || NOTBLOCKED(RT(tp)))
|
|
{
|
|
tile = TiSplitY(tile, TOP(tp));
|
|
TiSetBody(tile, CHAN_HRIVER);
|
|
tile->ti_client = tileClient;
|
|
ret = 1;
|
|
}
|
|
}
|
|
for (tp = TR(tile); BOTTOM(tp) > BOTTOM(tile); tp = LB(tp))
|
|
{
|
|
if (NOTBLOCKED(tp) || NOTBLOCKED(LB(tp)))
|
|
{
|
|
newTile = TiSplitY(tile, BOTTOM(tp));
|
|
TiSetBody(newTile, CHAN_HRIVER);
|
|
newTile->ti_client = tileClient;
|
|
ret = 1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (tp = RT(tile); LEFT(tp) > LEFT(tile); tp = BL(tp))
|
|
{
|
|
if (NOTBLOCKED(tp) || NOTBLOCKED(BL(tp)))
|
|
{
|
|
newTile = TiSplitX(tile, LEFT(tp));
|
|
TiSetBody(newTile, CHAN_VRIVER);
|
|
newTile->ti_client = tileClient;
|
|
ret = 1;
|
|
}
|
|
}
|
|
for (tp = LB(tile); RIGHT(tp) < RIGHT(tile); tp = TR(tp))
|
|
{
|
|
if (NOTBLOCKED(tp) || NOTBLOCKED(TR(tp)))
|
|
{
|
|
tile = TiSplitX(tile, RIGHT(tp));
|
|
TiSetBody(tile, CHAN_VRIVER);
|
|
tile->ti_client = tileClient;
|
|
ret = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* glChanRiverBlock --
|
|
*
|
|
* Called for each river routing tile. Checks to make sure that at least
|
|
* some of the pins along the usable sides of this channel are still free;
|
|
* if they're not, we change this tile's type to CHAN_BLOCKED.
|
|
*
|
|
* Note: river routing tiles can overlay portions of a normal channel
|
|
* in which the density of the channel equals its capacity in either
|
|
* the horizontal or vertical direction.
|
|
*
|
|
* Results:
|
|
* Always returns 0.
|
|
*
|
|
* Side effects:
|
|
* See above.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
glChanRiverBlock(tile)
|
|
Tile *tile;
|
|
{
|
|
GCRPin *pin, *pinLast;
|
|
GCRChannel *ch;
|
|
int lo, hi;
|
|
|
|
ch = (GCRChannel *) tile->ti_client;
|
|
if (TiGetType(tile) == CHAN_HRIVER)
|
|
{
|
|
lo = (BOTTOM(tile) - ch->gcr_origin.p_y) / RtrGridSpacing;
|
|
hi = (TOP(tile) - ch->gcr_origin.p_y) / RtrGridSpacing;
|
|
if (lo < 1) lo = 1;
|
|
if (hi > ch->gcr_width) hi = ch->gcr_width;
|
|
pinLast = &ch->gcr_lPins[hi];
|
|
for (pin = &ch->gcr_lPins[lo]; pin <= pinLast; pin++)
|
|
if (pin->gcr_pId == NULL && pin->gcr_linked)
|
|
return 0;
|
|
pinLast = &ch->gcr_rPins[hi];
|
|
for (pin = &ch->gcr_rPins[lo]; pin <= pinLast; pin++)
|
|
if (pin->gcr_pId == NULL && pin->gcr_linked)
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
/* CHAN_VRIVER */
|
|
lo = (LEFT(tile) - ch->gcr_origin.p_x) / RtrGridSpacing;
|
|
hi = (RIGHT(tile) - ch->gcr_origin.p_x) / RtrGridSpacing;
|
|
if (lo < 1) lo = 1;
|
|
if (hi > ch->gcr_length) hi = ch->gcr_length;
|
|
pinLast = &ch->gcr_bPins[hi];
|
|
for (pin = &ch->gcr_bPins[lo]; pin <= pinLast; pin++)
|
|
if (pin->gcr_pId == NULL && pin->gcr_linked)
|
|
return 0;
|
|
pinLast = &ch->gcr_tPins[hi];
|
|
for (pin = &ch->gcr_tPins[lo]; pin <= pinLast; pin++)
|
|
if (pin->gcr_pId == NULL && pin->gcr_linked)
|
|
return 0;
|
|
}
|
|
|
|
TiSetBody(tile, CHAN_BLOCKED);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* glChanPinToTile --
|
|
*
|
|
* Find the tile corresponding to the pin 'pin'.
|
|
* The pin mustn't be in one of the corners of a channel.
|
|
*
|
|
* Results:
|
|
* Returns a pointer to the tile found.
|
|
* Returns NULL if the tile found was of type CHAN_BLOCKED.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
Tile *
|
|
glChanPinToTile(hintTile, pin)
|
|
Tile *hintTile; /* Hint for starting the tile search */
|
|
GCRPin *pin; /* Find tile containing this pin */
|
|
{
|
|
GCRChannel *ch;
|
|
Tile *tp;
|
|
Point p;
|
|
Rect r;
|
|
|
|
/* Figure out which channel tile the output point lies in */
|
|
p = pin->gcr_point;
|
|
switch (pin->gcr_side)
|
|
{
|
|
case GEO_NORTH: p.p_y--; break;
|
|
case GEO_EAST: p.p_x--; break;
|
|
}
|
|
|
|
tp = TiSrPoint(hintTile, glChanPlane, &p);
|
|
if (TiGetType(tp) == CHAN_BLOCKED)
|
|
return (Tile *) NULL;
|
|
|
|
ASSERT(tp->ti_client != (ClientData) CLIENTDEFAULT, "glChanPinToTile");
|
|
ch = (GCRChannel *) tp->ti_client;
|
|
TITORECT(tp, &r);
|
|
ASSERT(GEO_SURROUND(&ch->gcr_area, &r), "glChanPinToTile");
|
|
|
|
return tp;
|
|
}
|