309 lines
9.9 KiB
C
309 lines
9.9 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);
|
||
}
|
||
}
|