/*
 * netlist.c
 *
 * This code is responsible for building up the internal representation
 * of a netlist, finding all locations for each terminal, and sorting
 * the nets of a netlist into order of increasing area on a Heap.
 *
 *     *********************************************************************
 *     * 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/utils/netlist.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $";
#endif	/* lint */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "utils/magic.h"
#include "utils/geometry.h"
#include "utils/hash.h"
#include "utils/heap.h"
#include "tiles/tile.h"
#include "database/database.h"
#include "utils/netlist.h"
#include "utils/signals.h"
#include "textio/textio.h"
#include "utils/malloc.h"
#include "utils/styles.h"

/* C99 compat */
#include "netmenu/netmenu.h"
#include "dbwind/dbwind.h"

/* Compute the "cost" of a net from the bounding rect for all its terminals */
#define	NETSIZE(r)  ((int)((r)->r_xtop - (r)->r_xbot + (r)->r_ytop - (r)->r_ybot))

/* Forward declarations */
int nlTermFunc(), nlLabelFunc();

/*
 * ----------------------------------------------------------------------------
 *
 * NLBuild --
 *
 * Build an internal NLNetList structure from the information
 * in the current netlist.  We call the netmenu module to enumerate
 * the names of all the terminals in each net, and then search
 * the layout rooted at 'rootUse' for all occurrences of each
 * terminal label.
 *
 * Results:
 *	Returns the number of nets in the netlist.
 *
 * Side effects:
 *	Fills in the NLNetList pointed to by netList.
 *	The HashTable netList->nnl_names is initialized;
 *	it is indexed by the name of a terminal and each entry
 *	so found points to the NLTerm struct with that name.
 *
 * ----------------------------------------------------------------------------
 */

int
NLBuild(rootUse, netList)
    CellUse *rootUse;	/* Cell searched for terminals */
    NLNetList *netList;	/* Netlist to build */
{
    NLTerm *term;
    NLNet *net;

    netList->nnl_nets = (NLNet *) NULL;
    HashInit(&netList->nnl_names, 128, 0);

    /* Build list of all nets and terminals, but don't assign locations */
    (void) NMEnumNets(nlTermFunc, (ClientData) netList);

    /* Count the number of nets */
    netList->nnl_numNets = 0;
    for (net = netList->nnl_nets; net; net = net->nnet_next)
	netList->nnl_numNets++;
    if (SigInterruptPending) goto done;

    /* Now find all the occurrences of each terminal */
    for (net = netList->nnl_nets; net; net = net->nnet_next)
	for (term = net->nnet_terms; term; term = term->nterm_next)
	    (void) DBSrLabelLoc(rootUse, term->nterm_name, nlLabelFunc,
		    (ClientData) term);

    /*
     * Sanity checking and error reporting.
     * Complain if no occurrences were found for a terminal, or
     * if a net had only a single terminal in it.
     */
    for (net = netList->nnl_nets; net; net = net->nnet_next)
    {
	char mesg[256];
	int nterms = 0;
	Rect r;

	for (term = net->nnet_terms; term; term = term->nterm_next)
	{
	    /* Complain if terminal couldn't be found */
	    if (term->nterm_locs == NULL)
		TxError("Terminal %s couldn't be found\n", term->nterm_name);
	    nterms++;
	}

	/* Complain if only one terminal in net */
	if (nterms == 1)
	{
	    (void) sprintf(mesg, "Net %s has only one terminal",
			net->nnet_terms->nterm_name);
	    if ( net->nnet_terms->nterm_locs )
	    {
		GEO_EXPAND(&net->nnet_terms->nterm_locs->nloc_rect, 1, &r);
		DBWFeedbackAdd(&r, mesg, rootUse->cu_def, 1,
			STYLE_PALEHIGHLIGHTS);
	    }
	}
    }

done:
    return (netList->nnl_numNets);
}

/*
 * ----------------------------------------------------------------------------
 *
 * nlTermFunc --
 *
 * Called for each terminal in each net.  If the first terminal
 * in the net, allocate a new NLNet and prepend to netList->nnl_nets.
 * In any case, allocate a new NLTerm and prepend to the net on the
 * head of netList->nnl_nets (the new net allocated above if the
 * first terminal in the net).
 *
 * Results:
 *	Always returns 0.
 *
 * Side effects:
 *	See above.
 *
 * ----------------------------------------------------------------------------
 */

int
nlTermFunc(name, firstInNet, netList)
    char *name;
    bool firstInNet;
    NLNetList *netList;
{
    NLNet *net;
    NLTerm *term;
    HashEntry *he;

    if (firstInNet)
    {
	net = (NLNet *) mallocMagic((unsigned) (sizeof (NLNet)));
	bzero( (char *) net, sizeof(*net));
	net->nnet_terms = (NLTerm *) NULL;
	net->nnet_next = netList->nnl_nets;
	net->nnet_area = GeoNullRect;
	net->nnet_cdata = (ClientData) NULL;
	netList->nnl_nets = net;
    }
    else net = netList->nnl_nets;

    /* Find hash entry for this terminal */
    he = HashFind(&netList->nnl_names, name);
    if (HashGetValue(he))
	TxError("Warning: terminal %s appears in more than one net\n", name);

    term = (NLTerm *) mallocMagic((unsigned) (sizeof (NLTerm)));
    term->nterm_locs = (NLTermLoc *) NULL;
    term->nterm_net = net;
    term->nterm_name = he->h_key.h_name;
    term->nterm_next = net->nnet_terms;
    term->nterm_flags = 0;
    net->nnet_terms = term;

    HashSetValue(he, (ClientData) term);

    return (0);
}

/*
 * ----------------------------------------------------------------------------
 *
 * nlLabelFunc --
 *
 * Called for each occurrence of each label named in a netlist.
 * Allocates a new NLTermLoc for this label and prepends it to
 * the list for 'term'.
 *
 * Results:
 *	Always returns 0.
 *
 * Side effects:
 *	See above.
 *
 * ----------------------------------------------------------------------------
 */

int
nlLabelFunc(area, name, label, term)
    Rect *area;		/* Root coords of label */
    char *name;		/* Same as term->nterm_name (UNUSED) */
    Label *label;	/* Label within scx->scx_use->cu_def */
    NLTerm *term;	/* Prepend new NLTermLoc to this terminal */
{
    NLTermLoc *loc;

    loc = (NLTermLoc *) mallocMagic((unsigned) (sizeof (NLTermLoc)));
    loc->nloc_term = term;
    loc->nloc_rect = *area;
    loc->nloc_label = label;

	/* Uninitialized */
    loc->nloc_dir = -1;
    loc->nloc_pin = (struct pin *) NULL;
    loc->nloc_chan = (struct chan *) NULL;
    loc->nloc_stem = TiPlaneRect.r_ll;

    loc->nloc_region = (struct region *) NULL;
    loc->nloc_czone  = (struct czone *) NULL;
    loc->nloc_stemcost = 0;

    /* Link into term's list */
    loc->nloc_next = term->nterm_locs;
    term->nterm_locs = loc;

    (void) GeoInclude(&loc->nloc_rect, &term->nterm_net->nnet_area);
    return (0);
}

/*
 * ----------------------------------------------------------------------------
 *
 * NLFree --
 *
 * Free the storage in the NLNetList that was allocated
 * by glBuildNetList() above.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Frees memory.
 *
 * ----------------------------------------------------------------------------
 */

void
NLFree(netList)
    NLNetList *netList;
{
    NLTermLoc *loc;
    NLTerm *term;
    NLNet *net;

    free_magic1_t mm1 = freeMagic1_init();
    for (net = netList->nnl_nets; net; net = net->nnet_next)
    {
	for (term = net->nnet_terms; term; term = term->nterm_next)
	{
	    for (loc = term->nterm_locs; loc; loc = loc->nloc_next)
		freeMagic1(&mm1, (char *) loc);
	    freeMagic1(&mm1, (char *) term);
	}
	freeMagic1(&mm1, (char *) net);
    }
    freeMagic1_end(&mm1);

    HashKill(&netList->nnl_names);
}

/*
 * ----------------------------------------------------------------------------
 *
 * NLSort --
 *
 * Build a heap of nets, ordered with smallest area first.
 * Nets with only one terminal are not added to the heap.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Initializes and builds the heap netHeap, which is
 *	sorted in order of increasing area.
 *
 * ----------------------------------------------------------------------------
 */

void
NLSort(netList, netHeap)
    NLNetList *netList;
    Heap *netHeap;
{
    NLTermLoc *loc;
    NLTerm *term;
    NLNet *net;
    int nterms;
    Rect r;

    HeapInit(netHeap, 128, FALSE, FALSE);
    for (net = netList->nnl_nets; net; net = net->nnet_next)
    {
	/* Skip nets with only one terminal */
	if (net->nnet_terms == NULL || net->nnet_terms->nterm_next == NULL)
	    continue;

	/* Find bounding box around all terminals in this net */
	nterms = 0;
	for (term = net->nnet_terms; term; term = term->nterm_next)
	    if ((loc = term->nterm_locs))
	    {
		for ( ; loc; loc = loc->nloc_next)
		{
		    if (nterms++ == 0) r = loc->nloc_rect;
		    else (void) GeoInclude(&loc->nloc_rect, &r);
		}
	    }

	/* Add it to the heap if non-degenerate */
	if (nterms >= 2)
	{
	    HeapAddInt(netHeap, NETSIZE(&r), (char *)net);
	}
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * NLNetName --
 *
 * Given a pointer to a net, return the name of some terminal associated
 * with the net.
 *
 * Kludge: it's possible, when debugging the channel router, that the
 * nets it passes aren't pointers to real NLNets at all, but simply
 * small integers.  We're clever enough to try to recognize this by
 * comparing net with _etext; if it's earlier, we assume it's a small
 * integer and not a NLNet pointer.
 *
 * Results:
 *	Pointer to the name of the net.
 *
 * Side effects:
 *	If the net appears to have no terminal names, return a
 *	pointer to a statically allocated string that contains
 *	the hex address of the net structure.
 *
 * ----------------------------------------------------------------------------
 */

char *
NLNetName(net)
    NLNet *net;
{
    static char tempId[100];
#if defined(EMSCRIPTEN)
    int etext;
#elif defined(linux) && defined(__GLIBC__) && defined(__GLIBC_MINOR__) && (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 29)
    extern char etext;
#elif defined(linux) && defined(__clang__)
    extern char etext;
#elif defined(linux) || defined(CYGWIN)
    extern int etext asm("etext");
#elif defined(__APPLE__)
    int etext;
#else
    extern int etext;
#endif
    NLTerm *term;

    if (net == (NLNet *) NULL)
	return "(NULL)";

    /* Handle case of small integers, for debugging the channel router */
    if (net <= (NLNet *)(&etext))
    {
	(void) sprintf(tempId, "#%"DLONG_PREFIX"d", (dlong) net);
	return tempId;
    }

    term = net->nnet_terms;
    if (term == NULL || term->nterm_name == NULL)
    {
	(void) sprintf(tempId, "[%p]", (void *) net);
	return tempId;
    }

    return term->nterm_name;
}

