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