/* rtrChannel.c -
 *
 *	Code to handle channels and the obstacles within them.
 *
 *     *********************************************************************
 *     * 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/router/rtrChannel.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $";
#endif  /* not lint */

#include <stdio.h>
#include <string.h>

#include "utils/magic.h"
#include "utils/geometry.h"
#include "utils/geofast.h"
#include "utils/hash.h"
#include "utils/heap.h"
#include "tiles/tile.h"
#include "database/database.h"
#include "gcr/gcr.h"
#include "windows/windows.h"
#include "dbwind/dbwind.h"
#include "utils/signals.h"
#include "utils/main.h"
#include "router/router.h"
#include "router/rtrDcmpose.h"
#include "grouter/grouter.h"
#include "textio/textio.h"
#include "utils/styles.h"

/* C99 compat */
#include "router/routerInt.h"

/*
 * Maps a tile pointer to a channel structure.
 * We use this rather than the client fields of tiles because
 * they are already used to hold flags during channel decomposition.
 */
HashTable RtrTileToChannel;

/* Plane in __CHANNEL__ def holding channel tiles */
Plane *RtrChannelPlane;

/* List of channels created from decomposed tile plane */
GCRChannel *RtrChannelList = NULL;

/* Multiplier for when to make end connections */
#ifndef	lint
float RtrEndConst = 1.0;
#else
float RtrEndConst;	/* Sun lint brain death */
#endif	/* lint */

/* Forward declarations */
extern int rtrChannelObstacleMark();
extern void rtrChannelObstaclePins();


/*
 * ----------------------------------------------------------------------------
 *
 * RtrChannelRoute --
 *
 * This procedure invokes the channel router for the channel 'ch'.
 * If the channel is taller than it is wide, swap in x and y before
 * routing.  If the channel has more pins on the left edge than the
 * right, flip left to right before routing.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The parameter *pCount is incremented by the number of errors that
 *	occurred while routing this channel.
 *
 * ----------------------------------------------------------------------------
 */

void
RtrChannelRoute(ch, pCount)
    GCRChannel *ch;
    int *pCount;
{
    GCRChannel *flipped, *flipped_again, *copy;
    int errs1, errs2;

    /*
     * Mark unused stem-tip crossing points as unused once again.
     * (They had been marked as blocked during global routing so
     * the global router wouldn't try to use them to route over
     * cell tops, thereby making it impossible to generate a stem).
     */
    RtrPinsFixStems(ch);

    /*
     * If the channel is taller than it is long, flip it before
     * routing, in order to give the router a better chance of success.
     */
    if (ch->gcr_width < ch->gcr_length)
    {
	flipped = GCRNewChannel(ch->gcr_length, ch->gcr_width);
	GCRNoFlip(ch, flipped);
	errs1 = GCRroute(flipped);
	if (errs1 == 0)
	{
	    /* Save the routing results back in ch.  Clean up and return */
	    GCRNoFlip(flipped, ch);
	    RtrFBPaint(0);
	    goto bottom;
	}

	/* Try again with a left-right flip */
	RtrFBSwitch();
	flipped_again = GCRNewChannel(ch->gcr_length, ch->gcr_width);
	GCRFlipLeftRight(ch, flipped_again);
	errs2 = GCRroute(flipped_again);
	if (GcrDebug)
	    TxError("   Rerouting a channel with %d errors...", errs1);
	if (errs2 < errs1)
	{
	    errs1 = errs2;
	    GCRFlipLeftRight(flipped_again, ch);
	    if (GcrDebug)
		TxError(" to get %d errors\n", errs1);
	    RtrFBPaint(1);
	}
	else
	{
	    GCRNoFlip(flipped, ch);
	    if(GcrDebug)
		TxError(" unsuccessfully.\n");
	    RtrFBPaint(0);
	}
	GCRFreeChannel(flipped_again);
	goto bottom;
    }
    else
    {
	flipped = GCRNewChannel(ch->gcr_width, ch->gcr_length);
	GCRFlipXY(ch, flipped);
	errs1 = GCRroute(flipped);
	if(errs1 == 0)
	{
	    GCRFlipXY(flipped, ch);
	    RtrFBPaint(0);
	    goto bottom;
	}

	RtrFBSwitch();
	flipped_again = GCRNewChannel(flipped->gcr_length, flipped->gcr_width);
	GCRFlipXY(ch, flipped_again);
	copy = GCRNewChannel(flipped->gcr_length, flipped->gcr_width);
	GCRFlipLeftRight(flipped_again, copy);
	if(GcrDebug)
	    TxError("   Rerouting a channel with %d errors ...", errs1);
	errs2 = GCRroute(copy);
	if(errs2 < errs1)
	{
	    errs1 = errs2;
	    GCRFlipLeftRight(copy, flipped);
	    if(GcrDebug)
		TxError(" successfully, with %d errors\n", errs1);
	    RtrFBPaint(1);
	}
	else
	{
	    RtrFBPaint(0);
	    if(GcrDebug)
		TxError(" unsuccessfully\n");
	}

	GCRFlipXY(flipped, ch);
	GCRFreeChannel(flipped_again);
	// GCRFreeChannel(copy); // FIXME evaluate if this should be uncommented
    }

bottom:
    GCRFreeChannel(flipped);
    if (errs1 > 0)
	gcrSaveChannel(ch);
    *pCount += errs1;
    RtrMilestonePrint();
}

/*
 * ----------------------------------------------------------------------------
 *
 * RtrChannelBounds --
 *
 * Figure out the dimensions of the given channel.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The parameters plength and pwidth are filled in with the number
 *	of usable columns and rows in channel.  The Point pointed to
 *	by 'origin' is filled in with x and y coords to go in
 *	ch->origin.
 *
 * ----------------------------------------------------------------------------
 */

void
RtrChannelBounds(loc, pLength, pWidth, origin)
    Rect *loc;		/* Area the channel is to occupy */
    int *pLength;	/* Filled in with # columns in channel */
    int *pWidth;	/* Filled in with # rows in channel */
    Point *origin;	/* Filled in with coords of (0,0) grid point
			 * (one grid line below and to left of first
			 * usable grid point)
			 */
{
    char mesg[256];
    int hi, lo;

    /* Find rightmost and leftmost grid contained within channel area */
    hi = RTR_GRIDDOWN(loc->r_xtop, RtrOrigin.p_x);
    lo = RTR_GRIDUP(loc->r_xbot, RtrOrigin.p_x);
    origin->p_x = lo - RtrGridSpacing;
    if (hi < lo)
    {
	(void) sprintf(mesg, "Degenerate channel at (%d, %d) (%d, %d)",
		loc->r_xbot, loc->r_ybot, loc->r_xtop, loc->r_ytop);
	DBWFeedbackAdd(loc, mesg, EditCellUse->cu_def, 1, STYLE_PALEHIGHLIGHTS);
	TxError("%s\n", mesg);
    }
    *pLength = (hi - lo) / RtrGridSpacing + 1;

    /* Find topmost and bottommost grid contained within channel area */
    hi = RTR_GRIDDOWN(loc->r_ytop, RtrOrigin.p_y);
    lo = RTR_GRIDUP(loc->r_ybot, RtrOrigin.p_y);
    origin->p_y = lo - RtrGridSpacing;
    if (hi < lo)
    {
	(void) sprintf(mesg, "Degenerate channel at (%d, %d) (%d, %d)",
		loc->r_xbot, loc->r_ybot, loc->r_xtop, loc->r_ytop);
	DBWFeedbackAdd(loc, mesg, EditCellUse->cu_def, 1, STYLE_PALEHIGHLIGHTS);
	TxError("%s\n", mesg);
    }
    *pWidth = (hi - lo) / RtrGridSpacing + 1;
}

/*
 * ----------------------------------------------------------------------------
 *
 * RtrChannelObstacles --
 *
 * Searches a channel area for obstacles.  Sets the channel obstacle map
 * to indicate which grid locations have obstacles.  Uses DBTreeSrTiles()
 * to search for obstacles, rather than DBSrPaintArea, since channels can
 * lie over existing cells.
 *
 * Mark obstacles to channel crossings.  Reserve blocked pins for net
 * GCR_BLOCKEDNETID.  Flag obstructed pins to allow the size of their
 * accompanying obstacle to be set later (by RtrHazards()).
 *
 * Only obstacles in this channel are considered; GLBlockPins() must be
 * called later to propagate the obstacle information from the border of
 * this channel to adjacent channels.
 *
 * NOTE: RtrChannelObstacles() and those procedures it calls are heavily
 * tuned for speed, since DBTreeSrTiles can cause flat searching of
 * large portions of a design hierarchy if invoked over the tops of cells.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Sets the channel obstacle map.
 *
 * ----------------------------------------------------------------------------
 */

void
RtrChannelObstacles(use, ch)
    CellUse *use;
    GCRChannel * ch;
{
    int l, w, up = RtrSubcellSepUp, down = RtrSubcellSepDown;
    TileTypeBitMask allObs;
    SearchContext scx;

    /*
     * Set the obstacle maps in the metal and poly planes.
     * Ensure that the combination of this channel and its
     * neighbors are sufficient to cover all obstacles in
     * the area between the outermost tracks/columns of this
     * channel and those of its neighbors.
     */
    if (RtrSubcellSepUp + RtrSubcellSepDown < RtrGridSpacing)
    {
	/* Shouldn't happen; this is just insurance */
	up = RtrGridSpacing - RtrSubcellSepDown;
    }
    l = ch->gcr_length + 1;
    w = ch->gcr_width + 1;
    scx.scx_area.r_ll = scx.scx_area.r_ur = ch->gcr_origin;
    scx.scx_area.r_xbot -= up;
    scx.scx_area.r_ybot -= up;
    scx.scx_area.r_xtop += l * RtrGridSpacing + down;
    scx.scx_area.r_ytop += w * RtrGridSpacing + down;
    scx.scx_use = use;
    scx.scx_trans = GeoIdentityTransform;
    TTMaskZero(&allObs);
    TTMaskSetMask3(&allObs, &RtrMetalObstacles, &RtrPolyObstacles);
    (void) DBTreeSrTiles(&scx, &allObs, 0, rtrChannelObstacleMark, (ClientData) ch);

    rtrChannelObstaclePins(ch);
}

/*
 * ----------------------------------------------------------------------------
 *
 * rtrChannelObstaclePins --
 *
 * Mark obstacles to channel crossings.  An obstacle affects a channel
 * crossing if it lies within the area from the first track of this
 * channel to the first track of the next channel.  Reserve blocked pins
 * for net GCR_BLOCKEDNETID (ILLEGAL).  Flag obstructed pins to allow the
 * size of their accompanying obstacle to be set later.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Sets the channel obstacle map.
 *
 * ----------------------------------------------------------------------------
 */

void
rtrChannelObstaclePins(ch)
    GCRChannel *ch;
{
    short **res;
    int row, col, end;

    res = ch->gcr_result;
    end = ch->gcr_length + 1;
    for (row = 1; row <= ch->gcr_width; row++)
    {
	if (BLOCK(res[0][row]))
	{
	    ch->gcr_lPins[row].gcr_pId = GCR_BLOCKEDNETID;
	    ch->gcr_lPins[row].gcr_pFlags = GCRBLK;
	}
	else if (!CLEAR(res[0][row])) ch->gcr_lPins[row].gcr_pFlags = GCROBST;

	if (BLOCK(res[end][row]))
	{
	    ch->gcr_rPins[row].gcr_pId = GCR_BLOCKEDNETID;
	    ch->gcr_rPins[row].gcr_pFlags = GCRBLK;
	}
	else if (!CLEAR(res[end][row])) ch->gcr_rPins[row].gcr_pFlags = GCROBST;
    }

    end = ch->gcr_width+1;
    for (col = 1; col <= ch->gcr_length; col++)
    {
	if (BLOCK(res[col][0]))
	{
	    ch->gcr_bPins[col].gcr_pId = GCR_BLOCKEDNETID;
	    ch->gcr_bPins[col].gcr_pFlags = GCRBLK;
	}
	else if (!CLEAR(res[col][0])) ch->gcr_bPins[col].gcr_pFlags = GCROBST;

	if (BLOCK(res[col][end]))
	{
	    ch->gcr_tPins[col].gcr_pId = GCR_BLOCKEDNETID;
	    ch->gcr_tPins[col].gcr_pFlags = GCRBLK;
	}
	else if (!CLEAR(res[col][end])) ch->gcr_tPins[col].gcr_pFlags = GCROBST;
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * rtrChannelObstacleMark --
 *
 * Search routine called by DBTreeSrTiles() from RtrChannelObstacles
 * for each tile found in the given area.  If the tile is an obstacle,
 * mark locations in the channel flag map to indicate their presence.
 *
 * In addition to setting the GCRBLKM/GCRBLKP flags, we set the bits
 * GCRBLKT/GCRBLKC to indicate whether the obstacle blocks a track or
 * a column.  These latter bits are used only for initializing the
 * density information for global routing, and should be reset to
 * zero prior to calling the channel router (since they conflict
 * with the bits used to show routing).
 *
 * Results:
 *	Always returns 0.
 *
 * Side effects:
 *	Sets the obstacle map for obstacles in a given channel.
 *
 * ----------------------------------------------------------------------------
 */

int
rtrChannelObstacleMark(tile, dinfo, cxp)
    Tile *tile;
    TileType dinfo;	/* (unused) */
    TreeContext *cxp;
{
    short **mcol, *mrow, *mrowend, mask;
    GCRChannel *ch = (GCRChannel *) cxp->tc_filter->tf_arg;
    TileType type = TiGetType(tile);
    int loX, numX, loY;
    short **mcolend;
    int n;
    Rect r, r2;

    mask = 0;
    if (TTMaskHasType(&RtrMetalObstacles, type)) mask |= GCRBLKM;
    if (TTMaskHasType(&RtrPolyObstacles, type)) mask |= GCRBLKP;
    if (mask == 0)
	return (0);

    TITORECT(tile, &r);
    GEOTRANSRECT(&cxp->tc_scx->scx_trans, &r, &r2);

    /*
     * Determine the range of columns in this channel that are blocked
     * by the obstacle: find the grid coordinates enclosing the tile.
     * Clip to a grid 1 larger than the size of the channel.
     */
    n = r2.r_xbot - RtrPaintSepsDown[type] + 1;
    n = (RTR_GRIDUP(n, RtrOrigin.p_x) - ch->gcr_origin.p_x) / RtrGridSpacing;
    loX = MAX(n, 0);
    n = r2.r_xtop + RtrPaintSepsUp[type] - 1;
    n = (RTR_GRIDUP(n, RtrOrigin.p_x) - ch->gcr_origin.p_x) / RtrGridSpacing;
    numX = MIN(n, ch->gcr_length + 1) - loX;

    /* Do the same thing for the rows (n will be the number of rows) */
    n = r2.r_ybot - RtrPaintSepsDown[type] + 1;
    n = (RTR_GRIDUP(n, RtrOrigin.p_y) - ch->gcr_origin.p_y) / RtrGridSpacing;
    loY = MAX(n, 0);
    n = r2.r_ytop + RtrPaintSepsUp[type] - 1;
    n = (RTR_GRIDDOWN(n, RtrOrigin.p_y) - ch->gcr_origin.p_y) / RtrGridSpacing;
    n = MIN(n, ch->gcr_width + 1) - loY;

    /*
     * Figure out whether tracks or columns are being blocked,
     * for purposes of the density initialization.
     */
    if ((mask & (GCRBLKM|GCRBLKP)) == (GCRBLKM|GCRBLKP))
    {
	/* 2-layer obstacles block both tracks and columns */
	mask |= GCRBLKT|GCRBLKC;
    }
    else if (numX < n)
    {
	/* Tall and narrow obstacles block columns */
	mask |= GCRBLKC;
    }
    else
    {
	/* Short and wide obstacles block tracks */
	mask |= GCRBLKT;
    }

    /*
     * Now set the flags in the channel.
     * The following loop is tuned for speed.
     */
    mcol = &ch->gcr_result[loX];
    mcolend = &mcol[numX];
    while (mcol <= mcolend)
    {
	mrow = &(*mcol++)[loY];
	mrowend = &mrow[n];
	while (mrow <= mrowend)
	    *mrow++ |= mask;
    }

    return (0);
}

/*
 * ----------------------------------------------------------------------------
 *
 * RtrChannelDensity --
 *
 * Adjusts the density variables gcr_dRowsByCol and gcr_dColsByRow
 * to reflect the commitment of tracks or columns to existing wiring;
 * for each column or track blocked by material, the density for
 * that track is incremented.  Also updates gcr_dMaxByCol and
 * gcr_dMaxByRow, the maximum column and track densities.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Initializes ch->gcr_dRowsByCol, ch->gcr_dColsByRow arrays
 *	and the corresponding maximum values ch->gcr_dMaxByCol
 *	and ch->gcr_dMaxByRow.
 *
 * ----------------------------------------------------------------------------
 */

void
RtrChannelDensity(ch)
    GCRChannel *ch;
{
    short *hdens, *vdens, *rptr;
    int col, density;
    short *hdend, *vdend;

    /*
     * Adjust the density information for global routing.
     * Whether a column or track is blocked is indicated by the
     * presence of the GCRBLKC/GCRBLKT flags in the result array.
     * If both bits are set, then both a column and a track are
     * blocked.
     */
    hdens = &ch->gcr_dRowsByCol[1];
    hdend = &ch->gcr_dRowsByCol[ch->gcr_length];
    for (col = 1; hdens <= hdend; hdens++, col++)
    {
	vdens = &ch->gcr_dColsByRow[1];
	vdend = &ch->gcr_dColsByRow[ch->gcr_width];
	rptr = &ch->gcr_result[col][1];
	for ( ; vdens <= vdend; vdens++, rptr++)
	{
	    if (*rptr & GCRBLKT) *hdens += 1;
	    if (*rptr & GCRBLKC) *vdens += 1;
	}
    }

#ifdef	IDENSITY
    bcopy((char *) ch->gcr_dColsByRow, (char *) ch->gcr_iColsByRow,
		(ch->gcr_width + 2) * sizeof (short));
    bcopy((char *) ch->gcr_dRowsByCol, (char *) ch->gcr_iRowsByCol,
		(ch->gcr_length + 2) * sizeof (short));
#endif	/* IDENSITY */

    /* Compute the maximum row and column density */

	/* Column density */
    density = 0;
    hdens = &ch->gcr_dRowsByCol[1];
    hdend = &ch->gcr_dRowsByCol[ch->gcr_length];
    for ( ; hdens <= hdend; hdens++)
	if (*hdens > density) density = *hdens;
    ch->gcr_dMaxByCol = density;

	/* Row density */
    density = 0;
    vdens = &ch->gcr_dColsByRow[1];
    vdend = &ch->gcr_dColsByRow[ch->gcr_width];
    for ( ; vdens <= vdend; vdens++)
	if (*vdens > density) density = *vdens;
    ch->gcr_dMaxByRow = density;
}

/*
 * ----------------------------------------------------------------------------
 *
 * RtrChannelCleanObstacles --
 *
 * Clears the GCRBLKC/GCRBLKT bits in the result array of 'ch'.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Resets the GCRBLKC/GCRBLKT flags in the result array,
 *	since these bits are also used to mean something else
 *	by the channel router.
 *
 * ----------------------------------------------------------------------------
 */

void
RtrChannelCleanObstacles(ch)
    GCRChannel *ch;
{
    short *rptr;
    int row, rtop;
    int col, ctop;

    ctop = ch->gcr_length + 1;
    rtop = ch->gcr_width + 1;
    for (col = 0; col <= ctop; col++)
    {
	rptr = &ch->gcr_result[col][0];
	for (row = 0; row <= rtop; row++)
	    *rptr++ &= ~(GCRBLKT|GCRBLKC);
    }
}
