/*
 * gcrChannel.c -
 *
 * Channel manipulation: allocation, freeing, and transforming
 * (e.g, flipping them left-to-right or rotating).  Transforming
 * is done so that the channel can be routed in the easiest
 * direction.
 *
 *     *********************************************************************
 *     * 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/gcr/gcrChannel.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 "gcr/gcr.h"
#include "utils/malloc.h"


/*
 * ----------------------------------------------------------------------------
 *
 * GCRNewChannel --
 *
 * This procedure allocates storage for a new channel structure,
 * and initializes most of the structure.
 *
 * Results:
 *	The return value is a pointer to the new channel.
 *
 * Side effects:
 *	Storage is allocated.  The initial state of the channel is
 *	completely empty, although all pin and result arrays are
 *	allocated.  This procedure does not initialize the fields
 *	gcr_area, gcr_origin, or gcr_lCol, although it allocates
 *	storage for gcr_lCol.  Also, it doesn't initialize the
 *	value of gcr_point for each pin.
 *
 * ----------------------------------------------------------------------------
 */

GCRChannel *
GCRNewChannel(length, width)
    int length;		/* Length of the channel (# of columns) */
    int width;		/* Width of the channel (# rows) */
{
    unsigned lenWds, widWds, nBytes;
    GCRChannel *ch;
    int i;

    lenWds = length + 2;
    widWds = width + 2;

    ch = (GCRChannel *) mallocMagic((unsigned) (sizeof (GCRChannel)));
    ch->gcr_type = CHAN_NORMAL;
    ch->gcr_length = length;
    ch->gcr_width = width;
    ch->gcr_transform = GeoIdentityTransform;
    ch->gcr_nets  = (GCRNet *) NULL;

    /* Malloc storage for pin arrays and zero each */
    nBytes = lenWds * sizeof (GCRPin);
    ch->gcr_tPins = (GCRPin *) mallocMagic((unsigned) nBytes);
    ch->gcr_bPins = (GCRPin *) mallocMagic((unsigned) nBytes);
    bzero((char *) ch->gcr_tPins, (int) nBytes);
    bzero((char *) ch->gcr_bPins, (int) nBytes);

    nBytes = widWds * sizeof (GCRPin);
    ch->gcr_lPins = (GCRPin *) mallocMagic((unsigned) nBytes);
    ch->gcr_rPins = (GCRPin *) mallocMagic((unsigned) nBytes);
    bzero((char *) ch->gcr_lPins, (int) nBytes);
    bzero((char *) ch->gcr_rPins, (int) nBytes);

    ch->gcr_lCol = (GCRColEl *) mallocMagic((unsigned) (widWds * sizeof (GCRColEl)));
    ch->gcr_density = (int *) mallocMagic((unsigned) (lenWds * sizeof (int)));

    /* Global router-specific initialization */
    ch->gcr_dRowsByCol = (short *) mallocMagic((unsigned) (lenWds * sizeof (short)));
    bzero((char *) ch->gcr_dRowsByCol, (int) lenWds * sizeof (short));
    ch->gcr_dColsByRow = (short *) mallocMagic ((unsigned) (widWds * sizeof (short)));
    bzero((char *) ch->gcr_dColsByRow, (int) widWds * sizeof (short));
    ch->gcr_dMaxByRow = ch->gcr_dMaxByCol = 0;

#ifdef	IDENSITY
	/* For debugging */
    ch->gcr_iRowsByCol = (short *) mallocMagic((unsigned) (lenWds * sizeof (short)));
    bzero((char *) ch->gcr_iRowsByCol, (int) lenWds * sizeof (short));
    ch->gcr_iColsByRow = (short *) mallocMagic((unsigned) (widWds * sizeof (short)));
    bzero((char *) ch->gcr_iColsByRow, (int) widWds * sizeof (short));
#endif	/* IDENSITY */

    ch->gcr_client = (ClientData) NULL;

    /* Allocate the result array */
    ch->gcr_result = (short **) mallocMagic ((unsigned) (lenWds * sizeof (short *)));

    /*
     * Fill in fields of pins that aren't zero; also allocate
     * and clear each row of the result array.
     */
    nBytes = widWds * sizeof (short);
    for (i = 0; i < lenWds; i++)
    {
	ch->gcr_result[i] = (short *) mallocMagic((unsigned) nBytes);
	bzero((char *) ch->gcr_result[i], (int) nBytes);

	/* BOTTOM */
	ch->gcr_bPins[i].gcr_pDist = -1;
	ch->gcr_bPins[i].gcr_x = i;
	ch->gcr_bPins[i].gcr_y = 0;

	/* TOP */
	ch->gcr_tPins[i].gcr_pDist = -1;
	ch->gcr_tPins[i].gcr_x = i;
	ch->gcr_tPins[i].gcr_y = widWds - 1;
    }

    for (i = 0; i < widWds; i++)
    {
	/* LEFT */
	ch->gcr_lPins[i].gcr_pDist = -1;
	ch->gcr_lPins[i].gcr_x = 0;
	ch->gcr_lPins[i].gcr_y = i;

	/* RIGHT */
	ch->gcr_rPins[i].gcr_pDist = -1;
	ch->gcr_rPins[i].gcr_x = lenWds - 1;
	ch->gcr_rPins[i].gcr_y = i;
    }

    return (ch);
}

/*
 * ----------------------------------------------------------------------------
 *
 * GCRFreeChannel --
 *
 * 	This procedure frees up all the storage associated with
 *	a channel
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The storage of ch is completely freed.  The caller should
 *	refrain from any use of the pointer after this procedure
 *	returns.
 *
 * ----------------------------------------------------------------------------
 */

void
GCRFreeChannel(ch)
    GCRChannel *ch;		/* Pointer to channel structure to be freed. */
{
    GCRNet *net;
    int i;

    freeMagic((char *) ch->gcr_tPins);
    freeMagic((char *) ch->gcr_bPins);
    freeMagic((char *) ch->gcr_lPins);
    freeMagic((char *) ch->gcr_rPins);
    free_magic1_t mm1 = freeMagic1_init();
    for (net = ch->gcr_nets; net; net = net->gcr_next)
	freeMagic1(&mm1, (char *) net);
    freeMagic1_end(&mm1);
    freeMagic((char *) ch->gcr_lCol);

    freeMagic((char *) ch->gcr_dRowsByCol);
    freeMagic((char *) ch->gcr_dColsByRow);

#ifdef	IDENSITY
	/* For debugging */
    freeMagic((char *) ch->gcr_iRowsByCol);
    freeMagic((char *) ch->gcr_iColsByRow);
#endif	/* IDENSITY */

    freeMagic((char *) ch->gcr_density);
    for (i = 0; i <= ch->gcr_length + 1; i++)
	freeMagic((char *) ch->gcr_result[i]);
    freeMagic((char *) ch->gcr_result);
    freeMagic((char *) ch);
}

/*
 * ----------------------------------------------------------------------------
 *
 * GCRFlipLeftRight --
 *
 * 	This procedure will flip the contents of a channel left-to-right.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The contents of channel dst are modified so that they are
 *	a left-to-right flip of src.  Only the four pin arrays, the
 *	transform, the origin, the area, and the result array are
 *	modified. The other fields of dst are untouched.  This means,
 *	in particular, that this procedure should be called BEFORE
 *	the linked lists for nets get set up, or else AFTER all the
 *	channel routing has been done and the nets have been freed up.
 *	Also, this procedure does not transform the flag bits GCRTE or
 *	GCRCE, so this procedure shouldn't be called when those flags
 *	are significant.
 *
 * ----------------------------------------------------------------------------
 */

void
GCRFlipLeftRight(src, dst)
    GCRChannel *src;	/* Original channel. */
    GCRChannel *dst;	/* Channel to be modified to contain
				 * transformed info from src.  The two
				 * channels must have the same dimensions,
				 * but must not be the same channel.
				 */
{
    int y, lenWds, widWds;
    short old, new;
    int i, j;
    Transform t;

    ASSERT(src->gcr_length == dst->gcr_length, "GCRFlipLeftRight: mismatch");
    ASSERT(src->gcr_width == dst->gcr_width, "GCRFlipLeftRight: mismatch");

    lenWds = src->gcr_length + 1;
    widWds = src->gcr_width + 1;

    for (i = 0; i <= lenWds; i++)
    {
	j = lenWds - i;

	    /* Exchange pairs of pins in the top and bottom arrays */
	dst->gcr_tPins[j] = src->gcr_tPins[i];
	dst->gcr_tPins[j].gcr_x = j;
	dst->gcr_bPins[j] = src->gcr_bPins[i];
	dst->gcr_bPins[j].gcr_x = j;

	    /*
	     * Go through the result array, exchanging flag values.  Also,
	     * be careful to switch the left-right hazard bits now that the
	     * sense of the channel is reversed.  Also switch the GCRR bit
	     * in case there's been routing done.
	     */
	for (y = 0; y <= widWds; y++)
	{
	    old = src->gcr_result[i][y];
	    new = old & ~(GCRVR|GCRVL|GCRR);
	    if (old & GCRVR) new |= GCRVL;
	    if (old & GCRVL) new |= GCRVR;
	    if (i != 0 && (src->gcr_result[i-1][y] & GCRR))
		new |= GCRR;
	    dst->gcr_result[j][y] = new;
	}
    }

	/* Switch the left and right end pins */
    for (i = 0; i <= widWds; i++)
    {
	dst->gcr_lPins[i] = src->gcr_rPins[i];
	dst->gcr_lPins[i].gcr_x = 0;
	dst->gcr_rPins[i] = src->gcr_lPins[i];
	dst->gcr_rPins[i].gcr_x = widWds;
    }

	/* Copy the horizontal and vertical density information */
    dst->gcr_dMaxByCol = src->gcr_dMaxByCol;
    dst->gcr_dMaxByRow = src->gcr_dMaxByRow;
    bcopy((char *) src->gcr_dColsByRow, (char *) dst->gcr_dColsByRow,
		sizeof (short) * widWds);
#ifdef	IDENSITY
    bcopy((char *) src->gcr_iColsByRow, (char *) dst->gcr_iColsByRow,
		sizeof (short) * widWds);
#endif	/* IDENSITY */
    for (i = 0; i <= lenWds; i++)
    {
	/* Flip left-to-right */
	j = lenWds - i;
	dst->gcr_dRowsByCol[j] = src->gcr_dRowsByCol[i];
#ifdef	IDENSITY
	dst->gcr_iRowsByCol[j] = src->gcr_iRowsByCol[i];
#endif	/* IDENSITY */
    }

	/* Now fix up the transform of the new channel */
    GeoTranslateTrans(&GeoSidewaysTransform, src->gcr_length+1, 0, &t);
    GeoTransTrans(&t, &src->gcr_transform, &dst->gcr_transform);
    dst->gcr_origin = src->gcr_origin;
    dst->gcr_area = src->gcr_area;
    dst->gcr_type = src->gcr_type;
}

/*
 * ----------------------------------------------------------------------------
 *
 * GCRFlipXY --
 *
 * 	This procedure rotates and flips a channel to interchange
 *	x and y coordinates.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The contents of channel dst are modified so that they are
 *	a x-y flip of src.  See comments for GCRFlipLeftRight for
 *	warnings about when it is and isn't safe to call this
 *	procedure.
 *
 * ----------------------------------------------------------------------------
 */

void
GCRFlipXY(src, dst)
    GCRChannel *src;	/* Original channel. */
    GCRChannel *dst;	/* Channel to be modified to contain
				 * transformed info from src.
				 */
{
    static Transform flipxy = {0, 1, 0, 1, 0, 0};
    int tmp, lenWds, widWds;
    short old, new;
    int i, j;

    ASSERT(src->gcr_width == dst->gcr_length, "gcrFlipXY: channel mismatch");
    ASSERT(src->gcr_length == dst->gcr_width, "gcrFlipXY: channel mismatch");

    lenWds = src->gcr_length + 1;
    widWds = src->gcr_width + 1;

	/* First, flip the side pins to top and bottom */
    for (i = 0; i <= widWds; i++)
    {
	dst->gcr_tPins[i] = src->gcr_rPins[i];
	tmp = dst->gcr_tPins[i].gcr_x;
	dst->gcr_tPins[i].gcr_x = dst->gcr_tPins[i].gcr_y;
	dst->gcr_tPins[i].gcr_y = tmp;
	dst->gcr_bPins[i] = src->gcr_lPins[i];
	tmp = dst->gcr_bPins[i].gcr_x;
	dst->gcr_bPins[i].gcr_x = dst->gcr_bPins[i].gcr_y;
	dst->gcr_bPins[i].gcr_y = tmp;
    }

	/* Same thing except flip top and bottom pins to sides */
    for (i = 0; i <= lenWds; i++)
    {
	dst->gcr_rPins[i] = src->gcr_tPins[i];
	tmp = dst->gcr_rPins[i].gcr_x;
	dst->gcr_rPins[i].gcr_x = dst->gcr_rPins[i].gcr_y;
	dst->gcr_rPins[i].gcr_y = tmp;
	dst->gcr_lPins[i] = src->gcr_bPins[i];
	tmp = dst->gcr_lPins[i].gcr_x;
	dst->gcr_lPins[i].gcr_x = dst->gcr_lPins[i].gcr_y;
	dst->gcr_lPins[i].gcr_y = tmp;
    }

    /*
     * Now flip the result array.  EXTRA SPECIAL TRICKINESS: the
     * GCRBLKM and GCRBLKP flags must get switched, because what
     * blocked a column in the old channel blocks a row in the
     * new one.
     */
    for (i = 0; i <= lenWds; i++)
	for (j = 0; j <= widWds; j++)
	{
	    old = src->gcr_result[i][j];
	    new = old & ~(GCRVR|GCRVL|GCRVU|GCRVD|GCRR|GCRU|GCRBLKM|GCRBLKP);
	    if (old & GCRVR) new |= GCRVU;
	    if (old & GCRVU) new |= GCRVR;
	    if (old & GCRVL) new |= GCRVD;
	    if (old & GCRVD) new |= GCRVL;
	    if (old & GCRR)  new |= GCRU;
	    if (old & GCRU)  new |= GCRR;
	    if (old & GCRBLKM) new |= GCRBLKP;
	    if (old & GCRBLKP) new |= GCRBLKM;
	    dst->gcr_result[j][i] = new;
	}

	/* Copy the horizontal and vertical density information */
    dst->gcr_dMaxByRow = src->gcr_dMaxByCol;
    dst->gcr_dMaxByCol = src->gcr_dMaxByRow;
    bcopy((char *) src->gcr_dRowsByCol, (char *) dst->gcr_dColsByRow,
		sizeof (short) * lenWds);
    bcopy((char *) src->gcr_dColsByRow, (char *) dst->gcr_dRowsByCol,
		sizeof (short) * widWds);
#ifdef	IDENSITY
    bcopy((char *) src->gcr_iRowsByCol, (char *) dst->gcr_iColsByRow,
		sizeof (short) * lenWds);
    bcopy((char *) src->gcr_iColsByRow, (char *) dst->gcr_iRowsByCol,
		sizeof (short) * widWds);
#endif	/* IDENSITY */

	/* Lastly, make a new transform */
    GeoTransTrans(&flipxy, &src->gcr_transform, &dst->gcr_transform);
    dst->gcr_origin = src->gcr_origin;
    dst->gcr_area = src->gcr_area;
    switch (src->gcr_type)
    {
	case CHAN_HRIVER:	dst->gcr_type = CHAN_VRIVER; break;
	case CHAN_VRIVER:	dst->gcr_type = CHAN_HRIVER; break;
	default:		dst->gcr_type = CHAN_NORMAL; break;
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * GCRNoFlip --
 *
 * 	This procedure performs the identity transform.  It makes a copy.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The contents of channel dst are modified so that they are
 *	a copy of src.  Only the four pin arrays, the transform, the
 *	the area, and the result array are modified. The other fields
 *	of dst are untouched.  This means,
 *	in particular, that this procedure should be called BEFORE
 *	the linked lists for nets get set up, or else AFTER all the
 *	channel routing has been done and the nets have been freed up.
 *
 * ----------------------------------------------------------------------------
 */

void
GCRNoFlip(src, dst)
    GCRChannel *src;	/* Original channel. */
    GCRChannel *dst;	/* Channel to be modified to contain
				 * transformed info from src.  The two
				 * channels must have the same dimensions,
				 * but must not be the same channel.
				 */
{
    int lenWds, widWds, pinBytes, resBytes;
    int i;

    ASSERT(src->gcr_length == dst->gcr_length, "GCRFlipLeftRight: mismatch");
    ASSERT(src->gcr_width == dst->gcr_width, "GCRFlipLeftRight: mismatch");

    lenWds = src->gcr_length + 1;
    widWds = src->gcr_width + 1;

	/* Copy pairs of pins in the top and bottom arrays */
    pinBytes = lenWds * sizeof (GCRPin);
    bcopy((char *) src->gcr_tPins, (char *) dst->gcr_tPins, pinBytes);
    bcopy((char *) src->gcr_bPins, (char *) dst->gcr_bPins, pinBytes);

	/* Copy flag values from the result array */
    resBytes = widWds * sizeof (short);
    for (i = 0; i <= lenWds; i++)
	bcopy((char *)src->gcr_result[i], (char *)dst->gcr_result[i], resBytes);

	/* Copy the left and right end pins */
    pinBytes = widWds * sizeof (GCRPin);
    bcopy((char *) src->gcr_lPins, (char *) dst->gcr_lPins, pinBytes);
    bcopy((char *) src->gcr_rPins, (char *) dst->gcr_rPins, pinBytes);

	/* Copy the horizontal and vertical density information */
    dst->gcr_dMaxByCol = src->gcr_dMaxByCol;
    dst->gcr_dMaxByRow = src->gcr_dMaxByRow;
    bcopy((char *) src->gcr_dRowsByCol, (char *) dst->gcr_dRowsByCol,
		sizeof (short) * lenWds);
    bcopy((char *) src->gcr_dColsByRow, (char *) dst->gcr_dColsByRow,
		sizeof (short) * widWds);
#ifdef	IDENSITY
    bcopy((char *) src->gcr_iRowsByCol, (char *) dst->gcr_iRowsByCol,
		sizeof (short) * lenWds);
    bcopy((char *) src->gcr_iColsByRow, (char *) dst->gcr_iColsByRow,
		sizeof (short) * widWds);
#endif	/* IDENSITY */

	/* Now fix up the transform of the new channel */
    dst->gcr_origin = src->gcr_origin;
    dst->gcr_transform = src->gcr_transform;
    dst->gcr_area = src->gcr_area;
    dst->gcr_type = src->gcr_type;
}
