/*
 * gaChannel.c -
 *
 * Channel management for the gate-array router.
 *
 *     ********************************************************************* 
 *     * 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[] = "$Header: /ufs/repository/magic/garouter/gaChannel.c,v 1.2 2001/01/12 22:12:39 jsolomon Exp $";
#endif  not lint

#include <stdio.h>
#include "misc/magic.h"
#include "utils/geometry.h"
#include "utils/hash.h"
#include "utils/doubleint.h"
#include "utils/heap.h"
#include "tiles/tile.h"
#include "database/database.h"
#include "windows/windows.h"
#include "main/main.h"
#include "dbwind/dbwind.h"
#include "signals/signals.h"
#include "netmenu/netmenu.h"
#include "gcr/gcr.h"
#include "router/router.h"
#include "grouter/grouter.h"
#include "garouter/garouter.h"
#include "netlist/netlist.h"
#include "textio/textio.h"
#include "misc/styles.h"
#include "debug/debug.h"

/* List of all active channels */
GCRChannel *gaChannelList = NULL;

/* Def used to hold channel plane */
CellDef *gaChannelDef = NULL;

/* Forward declarations */
int gaSplitTile();
int gaSetClient();

#define	CNULL	((ClientData) NULL)

int gaTotNormCross, gaTotRiverCross, gaClearNormCross, gaClearRiverCross;

/*
 * ----------------------------------------------------------------------------
 *
 * GAChannelInitOnce --
 *
 * Once-only channel initialization for the gate-array router.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Allocates the tile plane used to hold channel information,
 *	RtrChannelPlane.
 *
 * ----------------------------------------------------------------------------
 */

Void
GAChannelInitOnce()
{
    if (gaChannelDef == NULL)
	gaChannelDef = RtrFindChannelDef();
    RtrChannelPlane = gaChannelDef->cd_planes[PL_DRC_ERROR];
    GAClearChannels();
}

/*
 * ----------------------------------------------------------------------------
 *
 * GAClearChannels --
 *
 * Clear any pre-existing channels in preparation for definition of
 * new ones.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Paints TileType 1 (blocked) over the tile plane that records all
 *	available channel area, and frees all channel structures.
 *
 * ----------------------------------------------------------------------------
 */

Void
GAClearChannels()
{
    GCRChannel *ch;
    Rect r;

    r.r_xbot = TiPlaneRect.r_xbot / 2;
    r.r_ybot = TiPlaneRect.r_ybot / 2;
    r.r_xtop = TiPlaneRect.r_xtop / 2;
    r.r_ytop = TiPlaneRect.r_ytop / 2;
    SigDisableInterrupts();
    (void) DBPaintPlane(RtrChannelPlane, &r, DBStdWriteTbl(1),
		    (PaintUndoInfo *) NULL);
    for (ch = gaChannelList; ch; ch = ch->gcr_next)
	GCRFreeChannel(ch);
    gaChannelList = (GCRChannel *) NULL;
    SigEnableInterrupts();
}

/*
 * ----------------------------------------------------------------------------
 *
 * GADefineChannel --
 *
 * Add a new channel definition to the list of channels we know
 * about.  First ensure that the channel boundaries are correctly
 * aligned (round down if they aren't) and also that the area of
 * this channel has not already been assigned to another channel.
 *
 * The parameter chanType is one of 0 (for an ordinary channel),
 * CHAN_HRIVER (for a horizontal river-routing channel), or CHAN_VRIVER
 * (for a vertical river-routing channel).
 *
 * The rectangle 'r' gives the area of the channel.
 *
 * Results:
 *	TRUE on success, FALSE on failure.
 *
 * Side effects:
 *	Paints the area of this channel into RtrChannelPlane (as space)
 *	to mark that this area is used.
 *
 * ----------------------------------------------------------------------------
 */

bool
GADefineChannel(chanType, r)
    int chanType;
    Rect *r;
{
    int length, width, halfGrid = RtrGridSpacing / 2;
    GCRChannel *ch;
    Point origin;
    Rect r2;

    /*
     * Make the channel boundaries lie halfway between grid lines.
     * To ensure consistent results when RtrGridSpacing is odd,
     * we always subtract halfGrid from a grid line.
     */
    r2 = *r;
    r->r_xbot = RTR_GRIDUP(r->r_xbot, RtrOrigin.p_x) - halfGrid;
    r->r_ybot = RTR_GRIDUP(r->r_ybot, RtrOrigin.p_y) - halfGrid;
    r->r_xtop = RTR_GRIDDOWN(r->r_xtop, RtrOrigin.p_x) + RtrGridSpacing
		- halfGrid;
    r->r_ytop = RTR_GRIDDOWN(r->r_ytop, RtrOrigin.p_y) + RtrGridSpacing
		- halfGrid;

    if (r2.r_xbot != r->r_xbot || r2.r_ybot != r->r_ybot
	    || r2.r_xtop != r->r_xtop || r2.r_ytop != r->r_ytop)
    {
	TxPrintf("Rounding channel to center-grid alignment: ");
	TxPrintf("ll=(%d,%d) ur=(%d,%d)\n",
		r->r_xbot, r->r_ybot, r->r_xtop, r->r_ytop);
    }

    /* Ensure no overlap */
    if (DBSrPaintArea((Tile *) NULL, RtrChannelPlane, r, &DBSpaceBits,
	    gaAlwaysOne, (ClientData) NULL))
    {
	TxError("Channel ll=(%d,%d) ur=(%d,%d) overlaps existing channels\n",
		r->r_xbot, r->r_ybot, r->r_xtop, r->r_ytop);
	return (FALSE);
    }

    if (DebugIsSet(gaDebugID, gaDebShowChans))
	DBWFeedbackAdd(r, "Channel area", EditCellUse->cu_def,
		    1, STYLE_OUTLINEHIGHLIGHTS);

    /* Paint it into the channel plane */
    SigDisableInterrupts();
    (void) DBPaintPlane(RtrChannelPlane, r, DBStdWriteTbl(TT_SPACE),
		    (PaintUndoInfo *) NULL);

    /* Allocate the new channel */
    RtrChannelBounds(r, &length, &width, &origin);
    ch = GCRNewChannel(length, width);
    ch->gcr_area = *r;
    ch->gcr_origin = origin;
    ch->gcr_type = chanType;

    /* Link it in with the pre-existing list */
    ch->gcr_next = gaChannelList;
    gaChannelList = ch;
    SigEnableInterrupts();

    return (TRUE);
}

/*
 * ----------------------------------------------------------------------------
 *
 * gaChannelInit --
 *
 * Called immediately prior to routing to set up lots of useful
 * information in channels:
 *
 * Tile plane initialization:
 *	- Carve up the tile plane so each channel corresponds to a
 *	  single space tile, and unavailable areas correspond to
 *	  tiles of type 1.
 *	- Make the ti_client fields of tiles in the channel plane
 *	  point to their corresponding channels.
 *
 * Obstacle initialization:
 *	- Initialize the obstacle and hazard maps for each channel.
 *	  Identify pins too close to two-layer blockages and mark
 *	  them as already taken (by net GCR_BLOCKEDNETID).
 *	- For river-routing channels, ensure that pairs of pins
 *	  can be river-routed to each other; if not, mark both
 *	  as unavailable.
 *
 * Pin initialization (including stem assignment):
 *	- Fill in the global-routing information in the channel
 *	  structure.  Specifically, set gcr_ch, gcr_side, gcr_linked,
 *	  and gcr_point for each GCRPin.  Link available pins for
 *	  each side of the channel into a doubly-linked list so
 *	  they can be visited easily during global routing.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Fills in a lot of information in the channel structures.
 *
 * ----------------------------------------------------------------------------
 */

Void
gaChannelInit(list, routeUse, netList)
    GCRChannel *list;	/* List of channels to process */
    CellUse *routeUse;	/* Cell being routed (searched for obstacles) */
    NLNetList *netList;	/* Netlist being routed */
{
    register GCRChannel *ch;

    RtrMilestoneStart("Obstacle map initialization");
    for (ch = list; ch && !SigInterruptPending; ch = ch->gcr_next)
    {
	/* Ensure that no channel tiles cross channel boundaries */
	while (DBSrPaintArea((Tile *) NULL, RtrChannelPlane, &ch->gcr_area,
		&DBAllTypeBits, gaSplitTile, (ClientData) &ch->gcr_area))
	    /* Nothing */;

	/* Obstacle initialization */
	RtrMilestonePrint();
	RtrChannelObstacles(routeUse, ch);
	if (ch->gcr_type == CHAN_NORMAL)
	    RtrChannelDensity(ch);
	RtrChannelCleanObstacles(ch);
    }
    RtrMilestoneDone();

    /*
     * Set all ti_client fields in the channel plane to NULL, and then
     * for each channel set the ti_client fields of the tiles it overlaps
     * to point back to that channel.
     */
    (void) DBSrPaintArea((Tile *) NULL, RtrChannelPlane, &TiPlaneRect,
		    &DBAllTypeBits, gaSetClient, (ClientData) NULL);
    for (ch = list; ch && !SigInterruptPending; ch = ch->gcr_next)
	(void) DBSrPaintArea((Tile *) NULL, RtrChannelPlane, &ch->gcr_area,
		    &DBAllTypeBits, gaSetClient, (ClientData) ch);
    if (SigInterruptPending)
	return;

    /*
     * Pin initialization.
     * This fills in a bunch of information needed for global
     * routing in each pin along each channel boundary.
     */
    for (ch = list; ch && !SigInterruptPending; ch = ch->gcr_next)
	RtrPinsInit(ch);

    /*
     * Pick the actual pin locations for terminals.
     * This comes here because it needs the blockage and gcr_linked
     * information set above.  However, it must precede marking
     * pairs of river-routing crossings as unavailable, since
     * these pairs are only marked unavailable for routes across
     * the channel.  They may still be usable as stem tips, so
     * we can't block them until after stems have been assigned.
     */
    gaStemAssignAll(routeUse, netList);
    if (SigInterruptPending)
	return;

    /*
     * Now mark pairs of river-routing crossings as unavailable.
     * A crossing is unavailable if a straight-across from one side
     * of the channel to the other on a single layer is impossible.
     * Propagate these blockages..
     */
    for (ch = list; ch && !SigInterruptPending; ch = ch->gcr_next)
	if (ch->gcr_type != CHAN_NORMAL)
	    gaInitRiverBlockages(routeUse, ch);
    gaPropagateBlockages(list);
    if (SigInterruptPending)
	return;

    /*
     * Hazard initialization.
     * This has to happen after blockages have been propagated.
     */
    RtrMilestoneStart("Hazard initialization");
    for (ch = list; ch && !SigInterruptPending; ch = ch->gcr_next)
	if (ch->gcr_type == CHAN_NORMAL)
	{
	    RtrHazards(ch);
	    RtrMilestonePrint();
	}
    RtrMilestoneDone();

    /*
     * Link all the available pins along each side of each
     * channel into a linked list.  Only unblocked pins with
     * non-NULL gcr_linked pins are linked into this list.
     * Since pins taken as stem tips by gaAssignPins above
     * were marked, these lists don't include any pins that
     * are stem tips.
     */
    for (ch = list; ch && !SigInterruptPending; ch = ch->gcr_next)
	RtrPinsLink(ch);
    if (DebugIsSet(gaDebugID, gaDebChanStats))
	gaChannelStats(list);
}

gaChannelStats(list)
    GCRChannel *list;
{
    GCRChannel *ch;
    int *tot, *clear, numTot, numClear;
    double fNorm, fRiver, fTot;

    gaTotNormCross = 0;
    gaTotRiverCross = 0;
    gaClearNormCross = 0;
    gaClearRiverCross = 0;

    for (ch = list; ch; ch = ch->gcr_next)
    {
	switch (ch->gcr_type)
	{
	    case CHAN_NORMAL:
		tot = &gaTotNormCross;
		clear = &gaClearNormCross;
		break;
	    case CHAN_HRIVER:
	    case CHAN_VRIVER:
		tot = &gaTotRiverCross;
		clear = &gaClearRiverCross;
		break;
	}
	gaPinStats(ch->gcr_tPins, ch->gcr_length, tot, clear);
	gaPinStats(ch->gcr_bPins, ch->gcr_length, tot, clear);
	gaPinStats(ch->gcr_lPins, ch->gcr_width, tot, clear);
	gaPinStats(ch->gcr_rPins, ch->gcr_width, tot, clear);
    }

    numTot = gaTotRiverCross + gaTotNormCross;
    numClear = gaClearRiverCross + gaClearNormCross;
    fRiver = ((double) gaClearRiverCross / (double) gaTotRiverCross) * 100.0;
    fNorm = ((double) gaClearNormCross / (double) gaTotNormCross) * 100.0;
    fTot = ((double) numClear / (double) numTot) * 100.0;
    TxPrintf("Total pins: %d, clear: %d (%.1f%%)\n", numTot, numClear, fTot);
    TxPrintf("Norm chan pins: %d, clear: %d (%.1f%%)\n", gaTotNormCross,
		gaClearNormCross, fNorm);
    TxPrintf("River chan pins: %d, clear: %d (%.1f%%)\n", gaTotRiverCross,
		gaClearRiverCross, fRiver);
}

gaPinStats(pins, nPins, pTot, pClear)
    GCRPin *pins;
    int nPins;
    int *pTot, *pClear;
{
    register GCRPin *pin, *pend;

    pin = &pins[1];
    pend = &pins[nPins];
    for ( ; pin <= pend; pin++)
    {
	*pTot += 1;
	if (pin->gcr_linked && pin->gcr_pId == (GCRNet *) NULL
		&& pin->gcr_linked->gcr_pId == (GCRNet *) NULL)
	{
	    *pClear += 1;
	}
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * gaPropagateBlockages --
 *
 * If a pin is blocked on one side of a channel BOUNDARY,
 * it is blocked on the other side as well.  If a pin on
 * one side of a river-routing CHANNEL is blocked, the pin
 * on the other side gets blocked too.  Several iterations
 * may be necessary to propagate blockages across all
 * channel boundaries and river-routing channels.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	See above.
 *
 * ----------------------------------------------------------------------------
 */

Void
gaPropagateBlockages(list)
    GCRChannel *list;
{
    GCRChannel *ch;
    bool changed;

    do
    {
	changed = FALSE;
	for (ch = list; ch; ch = ch->gcr_next)
	    if (RtrPinsBlock(ch))
		changed = TRUE;
    } while (changed);
}

/*
 * ----------------------------------------------------------------------------
 *
 * gaSetClient --
 *
 * Called for each tile overlapping ch->gcr_area to set the
 * client pointer for that tile to 'cdata'.
 *
 * Results:
 *	Always returns 0.
 *
 * Side effects:
 *	See above.
 *
 * ----------------------------------------------------------------------------
 */

int
gaSetClient(tile, cdata)
    register Tile *tile;
    ClientData cdata;
{
    tile->ti_client = (ClientData) cdata;
    return (0);
}

/*
 * ----------------------------------------------------------------------------
 *
 * gaSplitTile --
 *
 * Called for each tile overlapping the area of a channel (the Rect 'r'),
 * we ensure that no tiles cross the channel boundary.  If one does, we
 * split it at the boundary and return 1.
 *
 * Results:
 *	Returns 1 if tile was split, or 0 if no split occurred.
 *	We return 1 to ensure that DBSrPaintArea doesn't get
 *	confused from our having modified the tile plane.
 *
 * Side effects:
 *	May split 'tile'; if it does, we return 1.
 *
 * ----------------------------------------------------------------------------
 */

int
gaSplitTile(tile, r)
    register Tile *tile;
    register Rect *r;
{
    register Tile *tp;
    ASSERT(TiGetType(tile) == TT_SPACE, "gaSplitTile");

    if (TOP(tile) > r->r_ytop)
    {
	tp = TiSplitY(tile, r->r_ytop);
	TiSetBody(tp, TT_SPACE);
	return (1);
    }
    if (BOTTOM(tile) < r->r_ybot)
    {
	tp = TiSplitY(tile, r->r_ybot);
	TiSetBody(tp, TT_SPACE);
	return (1);
    }
    if (LEFT(tile) < r->r_xbot)
    {
	tp = TiSplitX(tile, r->r_xbot);
	TiSetBody(tp, TT_SPACE);
	return (1);
    }
    if (RIGHT(tile) > r->r_xtop)
    {
	tp = TiSplitX(tile, r->r_xtop);
	TiSetBody(tp, TT_SPACE);
	return (1);
    }
    return (0);
}

/*
 * ----------------------------------------------------------------------------
 *
 * gaInitRiverBlockages --
 *
 * The channel 'ch' is a river-routing channel (ch->gcr_type is either
 * CHAN_HRIVER or CHAN_VRIVER).  These channels can only be routed
 * across in a single direction (horizontally for CHAN_HRIVER, or
 * vertically for CHAN_VRIVER).
 *
 * If pins have already been taken by terminals (as stem tips), we
 * don't mark them as blocked.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Marks pairs of pins on the channel boundary as blocked if
 *	a river-route across the channel is not possible along
 *	the track that runs between the pin pair.  Blocked pins
 *	have a special gcr_pId of GCR_BLOCKEDNETID to mark them as
 *	"in use".
 *
 * ----------------------------------------------------------------------------
 */

Void
gaInitRiverBlockages(routeUse, ch)
    CellUse *routeUse;
    GCRChannel *ch;
{
    register GCRPin *p1, *p2;
    int n, nPins, coord;
    SearchContext scx;

    switch (ch->gcr_type)
    {
	case CHAN_HRIVER:
	    p1 = &ch->gcr_lPins[1];
	    p2 = &ch->gcr_rPins[1];
	    nPins = ch->gcr_width;
	    scx.scx_area.r_xbot = ch->gcr_area.r_xbot;
	    scx.scx_area.r_xtop = ch->gcr_area.r_xtop;
	    coord = ch->gcr_origin.p_y + RtrGridSpacing;
	    break;
	case CHAN_VRIVER:
	    p1 = &ch->gcr_tPins[1];
	    p2 = &ch->gcr_bPins[1];
	    nPins = ch->gcr_length;
	    scx.scx_area.r_ybot = ch->gcr_area.r_ybot;
	    scx.scx_area.r_ytop = ch->gcr_area.r_ytop;
	    coord = ch->gcr_origin.p_x + RtrGridSpacing;
	    break;
    }

    scx.scx_use = routeUse;
    scx.scx_trans = GeoIdentityTransform;
    for (n = 1; n <= nPins; n++, p1++, p2++, coord += RtrGridSpacing)
    {
	switch (ch->gcr_type)
	{
	    case CHAN_HRIVER:
		scx.scx_area.r_ybot = coord - RtrSubcellSepUp;
		scx.scx_area.r_ytop = coord + RtrSubcellSepDown;
		break;
	    case CHAN_VRIVER:
		scx.scx_area.r_xbot = coord - RtrSubcellSepUp;
		scx.scx_area.r_xtop = coord + RtrSubcellSepDown;
		break;
	}

	/*
	 * If obstacles to both routing layers are found,
	 * then block this pair of crossing points if they
	 * aren't already taken.
	 */
	if (DBTreeSrTiles(&scx, &RtrMetalObstacles, 0, gaAlwaysOne, CNULL)
	    && DBTreeSrTiles(&scx, &RtrPolyObstacles, 0, gaAlwaysOne, CNULL))
	{
	    if (p1->gcr_pId == (GCRNet *) NULL) p1->gcr_pId = GCR_BLOCKEDNETID;
	    if (p2->gcr_pId == (GCRNet *) NULL) p2->gcr_pId = GCR_BLOCKEDNETID;
	}
    }

}

int
gaAlwaysOne()
{
    return (1);
}
