magic/grouter/grouteMult.c

309 lines
9.8 KiB
C

/*
* grouteMulti.c
*
* Route a multi-terminal net.
* Currently includes just a single algorithm, for routing a net
* where arbitrary branching is allowed. This algorithm produces
* something like a steiner-tree, with intermediate points introduced
* at channel boundaries.
*
* *********************************************************************
* * 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/grouter/grouteMult.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $";
#endif /* lint */
#include <stdio.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 "router/router.h"
#include "gcr/gcr.h"
#include "grouter/grouter.h"
#include "utils/netlist.h"
#include "utils/signals.h"
#include "textio/textio.h"
#include "utils/malloc.h"
#include "utils/styles.h"
#include "windows/windows.h"
#include "dbwind/dbwind.h"
/* Forward declarations */
void glMultiAddStart();
/*
* ----------------------------------------------------------------------------
*
* glMultiSteiner --
*
* Perform global routing for all segments of a net.
* The caller supplies two procedures: one to produce a list of
* GlPoints from any of a set of possible starting points to the
* single destination, and the other to accept a list of GlPoints
* and remember it permanently, e.g, in the form of crossing assignments.
*
* The two procedures are of the following form:
*
* GlPoint *
* (*routeProc)(startList, loc, bestCost, cdRoute)
* GlPoint *startList; --- list of GlPoints that are possible
* --- starting points for the route; the
* --- points in the list are linked via gl_path
* --- fields as though they were a path.
* NLTermLoc *loc; --- loc->nloc_pin is the destination
* int bestCost; --- return NULL if we can't beat this cost
* ClientData cdRoute; --- same as cdRoute passed to us
* {
* }
*
* int
* (*markProc)(rootUse, path, pNetId, cdMark)
* CellUse *rootUse; --- leave feedback here if necessary and if
* --- rootUse is non-NULL
* GlPoint *path; --- path to be marked
* NetId *pNetId; --- netid_net is the argument 'net' to
* --- glMultiSteiner; netid_seg will be
* --- incremented for each new segment id
* --- assigned.
* ClientData cdMark; --- same as 'cdMark' passed to us
* {
* }
*
*
* Assumptions:
* The net has at least two terminals, each of which has at least
* one valid location.
*
* Algorithm:
* Multiterminal nets are routed using an algorithm that finds
* pseudo-Steiner tree routes. The idea is to process the
* terminals of the net in the order they appear in the netlist.
* Processing consists of finding the shortest path from the
* terminal being considered to all of the terminals processed
* before it, or to any of the crossing points used by the routes
* used to connect these previously-processed terminals.
*
* Results:
* Returns the number of terminals processed.
*
* Side effects:
* Whatever (*routeProc)() and (*markProc)() do.
*
* ----------------------------------------------------------------------------
*/
int
glMultiSteiner(rootUse, net, routeProc, markProc, cdRoute, cdMark)
CellUse *rootUse; /* If non-NULL, feedback for errors left here */
NLNet *net; /* Net to process */
GlPoint *(*routeProc)(); /* Procedure to route a segment */
int (*markProc)(); /* Procedure to remember the route */
ClientData cdRoute; /* Passed to (*routeProc)() */
ClientData cdMark; /* Passed to (*markProc)() */
{
GlPoint *startList, *bestDest, *dest;
char mesg[128], *lastTermName;
int bestCost, nterms;
NLTermLoc *loc;
NLTerm *term;
Rect errorArea;
NetId netid;
/* Skip to the first term that has a location */
ASSERT(net != (NLNet *) NULL, "glMultiSteiner");
for (term = net->nnet_terms; term; term = term->nterm_next)
if (term->nterm_locs)
break;
ASSERT(term != (NLTerm *) NULL, "glMultiSteiner");
/*
* For the first terminal in the net, mark the point where the terminal
* enters its adjacent channel. If there are several electrically
* equivalent terminals, then mark them all.
*/
nterms = 0;
startList = (GlPoint *) NULL;
lastTermName = term->nterm_name;
for (loc = term->nterm_locs; loc; loc = loc->nloc_next)
glListAdd(&startList, loc->nloc_pin, glMultiStemCost(loc));
/* Process all other terminals in net */
netid.netid_net = net;
netid.netid_seg = 1;
for (term = term->nterm_next; term; term = term->nterm_next)
{
/*
* Skip if no valid locations exist for this terminal;
* the error has already been reported (either in the
* stem generator or the netlist reader).
*/
if (term->nterm_locs == (NLTermLoc *) NULL)
continue;
/*
* Consider routing to each of the possible locations for
* this terminal, and use the best path. (The comparison of
* route cost includes the final channel in each path).
* After each call to rgRoutePath, 'dest' will be a GlPoint
* for one of the zero-cost points to 'loc' (or NULL if no
* path could be found).
*/
bestCost = INFINITY;
bestDest = (GlPoint *) NULL;
for (loc = term->nterm_locs; loc; loc = loc->nloc_next)
{
nterms++;
/* Try to find a path from a zero-cost point to loc */
dest = (*routeProc)(startList, loc, bestCost, cdRoute);
/* Remember it if it was better than the previous best */
if (dest && dest->gl_cost < bestCost)
{
if (bestDest) glPathFreePerm(bestDest);
bestDest = glPathCopyPerm(dest);
bestCost = dest->gl_cost;
}
/* Free all temporary storage used for GlPoints */
glPathFreeTemp();
}
/*
* If we were successful in finding a path, add the crossing points
* to the zero-cost list, mark all the crossings it used as allocated,
* and update the segment-id.
*/
if (bestDest)
{
glMultiAddStart(bestDest, &startList);
(*markProc)(rootUse, bestDest, &netid, cdMark);
glPathFreePerm(bestDest);
/*
* Finally, move all of the locations for the terminal just
* processed to the zero-cost list, since any of them can
* be used as a new starting point.
*/
for (loc = term->nterm_locs; loc; loc = loc->nloc_next)
glListAdd(&startList, loc->nloc_pin, glMultiStemCost(loc));
lastTermName = term->nterm_name;
}
else
{
GEO_EXPAND(&term->nterm_locs->nloc_rect, 1, &errorArea);
sprintf(mesg, "Can't find a path from \"%s\" to \"%s\"",
term->nterm_name, lastTermName);
if (rootUse)
DBWFeedbackAdd(&errorArea, mesg, rootUse->cu_def,
1, STYLE_PALEHIGHLIGHTS);
else TxError("%s\n", mesg);
}
}
/* Free the list of starting points */
glPathFreePerm(startList);
return nterms;
}
/*
* ----------------------------------------------------------------------------
*
* glMultiStemCost --
*
* Compute the initial cost of a terminal. This is the cost from the
* terminal loc->nloc_rect to its initial crossing point loc->nloc_stem.
*
* Results:
* Returns the Manhattan distance just described.
*
* Side effects:
* None.
*
* ----------------------------------------------------------------------------
*/
int
glMultiStemCost(loc)
NLTermLoc *loc;
{
int n1, n2, cost;
n1 = ABSDIFF(loc->nloc_stem.p_x, loc->nloc_rect.r_xbot);
n2 = ABSDIFF(loc->nloc_stem.p_x, loc->nloc_rect.r_xtop);
cost = MIN(n1, n2);
n1 = ABSDIFF(loc->nloc_stem.p_y, loc->nloc_rect.r_ybot);
n2 = ABSDIFF(loc->nloc_stem.p_y, loc->nloc_rect.r_ytop);
cost += MIN(n1, n2);
return cost;
}
/*
* ----------------------------------------------------------------------------
*
* glMultiAddStart --
*
* Add all the pins along the GlPoint 'path' to the list of
* starting points '*pStartList'. For each crossing we add
* up to two pins: one on each side of the crossing.
* If a pin has already been marked as belonging to
* a net, we don't add it, since it was already added
* in an earlier iteration.
*
* Results:
* None.
*
* Side effects:
* Prepends the GlPoints on the list 'path' to the list of
* starting points *pStart.
*
* ----------------------------------------------------------------------------
*/
void
glMultiAddStart(path, pStartList)
GlPoint *path; /* Path linked via gl_path pointers */
GlPoint **pStartList; /* List of starting points */
{
GlPoint *srcEntry, *dstEntry;
GCRPin *srcPin, *dstPin;
/*
* Walk from path back along gl_path pointers down the list.
* At each step, process the segment between srcEntry and
* dstEntry in the channel srcEntry->gl_pin->gcr_ch.
*/
for (srcEntry = path->gl_path, dstEntry = path;
srcEntry;
dstEntry = srcEntry, srcEntry = srcEntry->gl_path)
{
/* Use srcPin's channel for both srcPin and dstPin */
srcPin = srcEntry->gl_pin;
dstPin = dstEntry->gl_pin;
if (dstPin->gcr_ch != srcPin->gcr_ch) dstPin = dstPin->gcr_linked;
ASSERT(dstPin && dstPin->gcr_ch == srcPin->gcr_ch, "glMultiAddStart");
/* Add to list of starting points */
if (srcPin->gcr_pId == NULL || srcPin->gcr_pSeg == GCR_STEMSEGID)
glListAdd(pStartList, srcPin, 0);
glListAdd(pStartList, dstPin, 0);
}
}