netgen/base/bottomup.c

688 lines
18 KiB
C

/* "NETGEN", a netlist-specification tool for VLSI
Copyright (C) 1989, 1990 Massimo A. Sivilotti
Author's address: mass@csvax.cs.caltech.edu;
Caltech 256-80, Pasadena CA 91125.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation (any version).
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file copying. If not, write to
the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/* bottomup.c -- Bottom-up graph partitioning algorithm for embedding
netlists on hierarchical prototyping chip.
*/
#include "config.h"
#include <stdio.h>
#include "hash.h"
#include "objlist.h"
#include "timing.h"
#include "embed.h"
#include "dbug.h"
#include "print.h"
/* count invokations of different test procedures */
int CountIndependent;
int CountFanoutOK;
int CountSwallowedElements;
int Independent(int E1, int E2)
/* return 1 if E1 and E2 share no leaves in common */
{
int leafelement;
CountIndependent++;
for (leafelement = 0; leafelement <= PackedLeaves; leafelement++)
if (MSTAR[E1][leafelement] & MSTAR[E2][leafelement]) return (0);
return(1);
}
int FanoutOK(int E1, int E2)
{
int approxfanout;
int node;
CountFanoutOK++;
/* trivial exclusion based on fanout upper bound */
/* remember, the current PROTOCHIP requires allocation of
an output even for swallowed nodes */
approxfanout = 0;
for (node = 1; node <= Nodes; node++)
if (C[E1][node] || C[E2][node]) approxfanout++;
if (approxfanout > TreeFanout[MAX(LEVEL(E1),LEVEL(E2)) + 1]) return(0);
return(1);
}
int Swallowed(int Parent, int Child)
/* returns 1 if Child's fanout is contained in Parent's */
{
int node;
for (node = 1; node <= Nodes; node++)
if (C[Child][node] && !(C[Parent][node])) return(0);
#ifdef PLACE_DEBUG
Printf("Element %d swallowed by %d\n",Child,Parent);
#endif
return(1);
}
#if 1
#define SmallEnough(a,b) 0
#else
int SmallEnough(int E1, int E2)
/* returns 1 if fanout(E1+E2) is less than MAX(fanout(E1),fanout(E2)) */
{
int node;
int fanout;
return(0); /* for now */
fanout = 0;
for (node = 1; node <= Nodes; node++)
if (C[E1][node] || C[E2][node])
if (CSTAR[E1][node] + CSTAR[E2][node] < CSTAR[0][node]) fanout++;
return (fanout <= MAX(PINS(E1), PINS(E2)));
#ifdef PLACE_DEBUG
Printf("Element %d,%d is SmallEnough\n",E1,E2);
#endif
return(1);
}
#endif
int SuccessfulEmbedding(int E)
/* returns 1 if element E is a successful embedding */
{
int testleaf;
#if 1
for (testleaf = 0; testleaf <= PackedLeaves; testleaf++)
if (MSTAR[E][testleaf] != MSTAR[0][testleaf]) return (0);
#else
for (testleaf = 1; testleaf <= Leaves; testleaf++)
if (!TestPackedArrayBit(MSTAR[E],testleaf)) return(0);
#endif
return(1);
}
void STARTPASS(FILE *f, int level1, int level2)
{
if (f == NULL) return;
Fprintf(f,"%2d: ",Pass);
if (Exhaustive)
Fprintf(f,"to level %d: (%d) ",level1,CountInLevel(level1,1));
else Fprintf(f,"(%d,%d) [%5d,%5d]",
level1,level2,CountInLevel(level1,0),CountInLevel(level2,0));
#ifdef PLACE_DEBUG
Fprintf(f,"\n");
#endif
Fflush(f);
}
void ENDPASS(FILE *f, int level1, int level2)
{
if (f == NULL) return;
Fprintf(f,"%5dA,%3dS,%5dT",
NewElements, NewSwallowed, Elements);
#if 1
if (NewElements) {
int level;
level = MAX(level1, level2) + 1;
Fprintf(f," (%4.1fP %4.1fC %4.1fL)(%2d %2d %2d)",
(float)SumPINS / NewElements, (float)SumCommonNodes / NewElements,
(float)SumUsedLeaves / NewElements,
TreeFanout[level], MinCommonNodes[level], MinUsedLeaves[level] );
}
#else
if (NewElements) Fprintf(f," (%4.1fP <= %2d, %4.1fC >= %2d, %4.1fL >= %2d)",
(float)SumPINS / (float)NewElements,
TreeFanout[MAX(level1,level2) + 1],
(float)SumCommonNodes / (float)NewElements,
MinCommonNodes[MAX(level1,level2) + 1],
(float)SumUsedLeaves / (float)NewElements,
MinUsedLeaves[MAX(level1,level2) + 1] );
#endif
Fprintf(f,"\n");
Fflush(f);
}
static float StartTime;
void EPILOG(FILE *f, int element)
{
if (f == NULL) return;
Fprintf(f,"Stats: Passes = %d, Elements = %d", Pass, Elements);
if (element) Fprintf(f, ", Level = %d",LEVEL(element));
Fprintf(f, ", Elapsed CPU time = %.2f s\n", ElapsedCPUTime(StartTime));
Fprintf(f,"Tests: Indep. = %d, Conn. = %d, Fanout = %d, Exists = %d\n",
CountIndependent, CountAnyCommonNodes, CountFanoutOK, CountExists);
PrintExistSetStats(f);
Fprintf(f,"Swallowed elements = %d", CountSwallowedElements);
if (Exhaustive) Fprintf(f,", EXHAUSTIVE");
Fprintf(f,"\n\n");
Fflush(f);
}
void SwallowSubTree(int E, int level)
{
if (E != 0 && !(SWALLOWED(E))) {
SWALLOWED(E) = level;
CountSwallowedElements++;
NewSwallowed++;
SwallowSubTree(L(E),level);
SwallowSubTree(R(E),level);
}
}
int Logging(int level1, int level2)
/* return 1 if we should log this level */
{
if (!logging) return(0);
if (!selectivelogging) return(1);
if (level2 == -1) return(level1 == LogLevel1);
if (LogLevel2 == -1) return(level1 == LogLevel1);
if (LogLevel1 == -1) return(level2 == LogLevel2);
return((level1 == LogLevel1) && (level2 == LogLevel2));
}
#if 0
int DoAPass(int Level1, int Level2)
/* tries to merge elements of level 'Level1' with elements of level 'Level2' */
/* returns element number if successful embedding found */
{
int E1, E2;
int PossiblyDone;
int MaxLevel, MinLevel, ThisLevel;
int MinDoneLevel, junk;
int found;
Pass++;
STARTPASS(stdout,Level1,Level2);
STARTPASS(outfile,Level1,Level2);
if (logging) STARTPASS(logfile,Level1,Level2);
/* determine the minimum embedding level */
MaxLevel = MAX(Level1, Level2);
MinLevel = MIN(Level1, Level2);
ThisLevel = MaxLevel + 1; /* level at which any new element will go */
junk = Leaves - 1;
for (MinDoneLevel = 0; junk; MinDoneLevel++) junk = junk >> 1;
PossiblyDone = (MaxLevel >= MinDoneLevel - 1);
NewElements = 0;
NewSwallowed = 0;
SumPINS = 0;
SumCommonNodes = 0;
SumUsedLeaves = 0;
found = 0;
/* make first pass to determine what we can swallow */
#if 1
for (E1 = 1; E1 <= Elements; E1++) {
#else
for (E1 = Elements; E1 > 0; E1--) {
#endif
if ((LEVEL(E1) != MaxLevel && LEVEL(E1) != MinLevel)) continue;
/* might exclude SWALLOWED elements as well */
if (SWALLOWED(E1) && SWALLOWED(E1) != Pass) continue;
for (E2 = E1 - 1; E2 > 0; E2--) {
if ((LEVEL(E2) != MaxLevel && LEVEL(E1) == MinLevel) ||
(LEVEL(E2) != MinLevel && LEVEL(E1) == MaxLevel)) continue;
/* might exclude SWALLOWED elements as well */
if (SWALLOWED(E2) && SWALLOWED(E2) != Pass) continue;
if (Independent(E1,E2)
#ifdef CHECK_MINUSEDLEAVES
&& UsedLeaves(E1,E2) >= MinUsedLeaves[MaxLevel + 1]
#endif
&& AnyCommonNodes(E1,E2)
&& FanoutOK(E1,E2)
&& !Exists(E1,E2)
&& (Swallowed(E1, E2) || Swallowed(E2, E1) || SmallEnough(E1,E2))) {
SwallowSubTree(E1, Pass);
SwallowSubTree(E2, Pass);
AddNewElement(E1,E2);
#ifdef PLACE_DEBUG
Printf("Swallowing (%d,%d) (%d common nodes) into element %d\n",
E1,E2,CommonNodes(E1,E2,1),NewN);
#endif
if (PossiblyDone && SuccessfulEmbedding(NewN)) {
found = NewN;
goto done;
}
if (NewN >= MAX_ELEMENTS) return(MAX_ELEMENTS);
if (FatalError) goto done;
/* break; do not consider E1 or E2 any further */
}
}
}
/* make second pass to consider merging everything else */
#if 1
for (E1 = 1; E1 <= Elements; E1++) {
#else
for (E1 = Elements; E1 > 0; E1--) {
#endif
if ((LEVEL(E1) != MinLevel && LEVEL(E1) != MaxLevel) ||
SWALLOWED(E1)) continue;
#ifdef CHECK_MINCOMMONNODES
if (PINS(E1) < MinCommonNodes[MaxLevel + 1]) continue;
#endif
for (E2 = E1 - 1; E2 > 0; E2--) {
if ((LEVEL(E2) != MaxLevel && LEVEL(E1) == MinLevel) ||
(LEVEL(E2) != MinLevel && LEVEL(E1) == MaxLevel) ||
SWALLOWED(E2)) continue;
#ifdef CHECK_MINCOMMONNODES
if (PINS(E2) < MinCommonNodes[MaxLevel + 1]) continue;
#endif
if (Independent(E1,E2)
#ifdef CHECK_MINUSEDLEAVES
&& UsedLeaves(E1,E2) >= MinUsedLeaves[MaxLevel + 1]
#endif
&& AnyCommonNodes(E1,E2)
&& FanoutOK(E1,E2)
&& !Exists(E1,E2)) {
#ifdef CHECK_MINCOMMONNODES
int commonnodes;
commonnodes = CommonNodes(E1, E2, 1);
if (commonnodes < MinCommonNodes[MaxLevel + 1])
continue;
Printf("Adding element (%d,%d) with %d common nodes\n",
E1,E2,commonnodes);
#endif
AddNewElement(E1,E2);
if (PossiblyDone && SuccessfulEmbedding(NewN)) {
found = NewN;
goto done;
}
if (NewN >= MAX_ELEMENTS) return(MAX_ELEMENTS);
if (FatalError) goto done;
}
}
}
done:
Elements = NewN;
ENDPASS(stdout, Level1, Level2);
#ifdef PLACE_DEBUG
EPILOG(stdout, found);
#endif
ENDPASS(outfile, Level1, Level2);
EPILOG(outfile, found);
if (logging) {
ENDPASS(logfile, Level1, Level2);
EPILOG(logfile, found);
if (NewElements && Logging(Level1,Level2)) {
PrintOwnership(logfile); PrintC(logfile);
PrintCSTAR(logfile); Fflush(logfile);
}
}
return(found);
}
#else
int DoAPass(int Level1, int Level2)
/* tries to merge elements of level 'Level1' with elements of level 'Level2' */
/* returns element number if successful embedding found */
{
int E1, E2;
int PossiblyDone;
int MaxLevel, MinLevel;
int MinDoneLevel, junk;
int found;
Pass++;
STARTPASS(stdout,Level1,Level2);
STARTPASS(outfile,Level1,Level2);
if (logging) STARTPASS(logfile,Level1,Level2);
/* determine the minimum embedding level */
MaxLevel = MAX(Level1, Level2);
MinLevel = MIN(Level1, Level2);
junk = Leaves - 1;
for (MinDoneLevel = 0; junk; MinDoneLevel++) junk = junk >> 1;
PossiblyDone = (MaxLevel >= MinDoneLevel - 1);
NewElements = 0;
NewSwallowed = 0;
SumPINS = 0;
SumCommonNodes = 0;
SumUsedLeaves = 0;
found = 0;
/* make single pass to consider merging and swallow */
#if 1
for (E1 = 1; E1 <= Elements; E1++) {
#else
for (E1 = Elements; E1 > 0; E1--) {
#endif
if ((LEVEL(E1) != MinLevel && LEVEL(E1) != MaxLevel) ||
/* (SWALLOWED(E1) && SWALLOWED(E1) != Pass)) */
SWALLOWED(E1))
continue;
#ifdef CHECK_MINCOMMONNODES
if (PINS(E1) < MinCommonNodes[MaxLevel + 1]) continue;
#endif
for (E2 = E1 - 1; E2 > 0; E2--) {
if ((LEVEL(E2) != MaxLevel && LEVEL(E1) == MinLevel) ||
(LEVEL(E2) != MinLevel && LEVEL(E1) == MaxLevel) ||
/* (SWALLOWED(E2) && SWALLOWED(E2) != Pass)) */
SWALLOWED(E2))
continue;
#ifdef CHECK_MINCOMMONNODES
if (PINS(E2) < MinCommonNodes[MaxLevel + 1]) continue;
#endif
if (Independent(E1,E2)
#ifdef CHECK_MINUSEDLEAVES
&& UsedLeaves(E1,E2) >= MinUsedLeaves[MaxLevel + 1]
#endif
&& AnyCommonNodes(E1,E2)
&& FanoutOK(E1,E2)
&& !Exists(E1,E2)) {
#ifdef CHECK_MINCOMMONNODES
int commonnodes;
commonnodes = CommonNodes(E1, E2, 1);
if (commonnodes < MinCommonNodes[MaxLevel + 1])
continue;
#ifdef PLACE_DEBUG
Printf("Adding element (%d,%d) with %d common nodes\n",
E1,E2,commonnodes);
#endif
#endif
AddNewElement(E1,E2);
if (Swallowed(E1, E2) || Swallowed(E2, E1) || SmallEnough(E1,E2)) {
SwallowSubTree(E1, Pass);
SwallowSubTree(E2, Pass);
#ifdef PLACE_DEBUG
Printf("Swallowing (%d,%d) (%d common nodes) into element %d\n",
E1,E2,CommonNodes(E1,E2,1),NewN);
#endif
}
if (PossiblyDone && SuccessfulEmbedding(NewN)) {
found = NewN;
goto done;
}
if (NewN >= MAX_ELEMENTS) return(MAX_ELEMENTS);
if (FatalError) goto done;
}
}
}
done:
Elements = NewN;
ENDPASS(stdout, Level1, Level2);
#ifdef PLACE_DEBUG
EPILOG(stdout, found);
#endif
ENDPASS(outfile, Level1, Level2);
EPILOG(outfile, found);
if (logging) {
ENDPASS(logfile, Level1, Level2);
EPILOG(logfile, found);
/* if (NewElements && Logging(Level1,Level2)) { */
if (Logging(Level1,Level2)) {
PrintOwnership(logfile); PrintC(logfile);
PrintCSTAR(logfile); Fflush(logfile);
}
}
return(found);
}
#endif
int ExhaustivePass(int Level)
/* tries to merge elements of level 'Level' and below */
/* returns element number if successful embedding found */
{
int E1, E2;
int found;
int PossiblyDone;
int MinDoneLevel, junk;
Pass++;
STARTPASS(stdout, Level, Level);
STARTPASS(outfile, Level, Level);
if (logging) STARTPASS(logfile, Level, Level);
/* determine the minimum embedding level */
junk = Leaves - 1;
for (MinDoneLevel = 0; junk; MinDoneLevel++) junk = junk >> 1;
PossiblyDone = (Level >= MinDoneLevel - 1);
NewElements = 0;
SumPINS = 0;
SumCommonNodes = 0;
SumUsedLeaves = 0;
found = 0;
for (E1 = 1; E1 <= Elements; E1++) {
if (LEVEL(E1) != Level) continue;
#if 1
for (E2 = E1 - 1; E2 > 0; E2--)
#else
for (E2 = 1; E2 < E1; E2++) /* statistically, slightly worse */
#endif
if (LEVEL(E2) <= LEVEL(E1)
&& Independent(E1,E2)
&& AnyCommonNodes(E1,E2)
&& FanoutOK(E1,E2)
&& !Exists(E1,E2)) {
AddNewElement(E1,E2);
if (PossiblyDone && SuccessfulEmbedding(NewN)) {
found = NewN;
goto done;
}
if (NewN >= MAX_ELEMENTS) return(MAX_ELEMENTS);
#if 0
if (Swallowed(E1,E2)) break; /* works OK, but makes csrlntk 5 deep*/
#endif
#if 0
if (Swallowed(E2,E1)) break; /* largely untested: csrlntk in 5 */
#endif
if (FatalError) goto done;
}
}
done:
Elements = NewN;
ENDPASS(stdout, Level, Level);
ENDPASS(outfile, Level, Level);
#ifdef PLACE_DEBUG
EPILOG(stdout, found);
EPILOG(outfile, found);
#endif
if (logging) {
ENDPASS(logfile, Level, Level);
EPILOG(logfile, found);
if (NewElements && Logging(Level,-1)) {
PrintOwnership(logfile); PrintC(logfile);
PrintCSTAR(logfile); Fflush(logfile);
}
}
return(found);
}
void PROLOG(FILE *f)
{
long totalsize;
int junk, MinDoneLevel;
/* determine the minimum embedding level */
junk = Leaves - 1;
for (MinDoneLevel = 0; junk; MinDoneLevel++) junk = junk >> 1;
Fprintf(f,"MAX_ELEMENTS = %d, ",MAX_ELEMENTS);
Fprintf(f,"MAX_LEAVES = %d, ",MAX_LEAVES);
Fprintf(f,"MAX_NODES = %d, ",MAX_NODES);
Fprintf(f,"MAX_TREE_DEPTH = %d\n",MAX_TREE_DEPTH);
Fprintf(f,"Matrix sizes: M = %ldK, MSTAR = %ldK, C = %ldK, CSTAR = %ldK\n",
(long)sizeof(M)/1024,(long)sizeof(MSTAR)/1024,
(long)sizeof(C)/1024, (long)sizeof(CSTAR)/1024);
totalsize = sizeof(M) + sizeof(MSTAR) + sizeof(C) + sizeof(CSTAR);
#ifdef EX_TREE_FOR_EXIST
totalsize += sizeof(ex_array);
Fprintf(f," ex_array = %ldK, total = %ldK\n",
(long)sizeof(ex_array)/1024, (long)totalsize/1024);
#else
Fprintf(f," total = %ldK\n", (long)totalsize/1024);
#endif
Fprintf(f,
" 0: %d elements, %d nodes, %d ports. Earliest embedding level = %d\n",
Elements, Nodes, PINS(0), MinDoneLevel);
Fflush(f);
}
void EmbedCell(char *cellname, char *filename)
{
int found;
int level1, level2;
int FillingLevel;
/* int SomeNewElements; */
if (!OpenEmbeddingFile(cellname, filename)) return;
StartTime = CPUTime();
if (!InitializeMatrices(cellname)) return;
if (!InitializeExistTest()) return;
FatalError = 0;
NewN = Elements;
Pass = 0;
CountIndependent = 0;
CountAnyCommonNodes = 0;
CountFanoutOK = 0;
CountExists = 0;
CountSwallowedElements = 0;
Fprintf(stdout,"Embedding cell: %s\n",cellname);
PROLOG(stdout);
Fprintf(outfile,"Embedding cell: %s\n",cellname);
PROLOG(outfile);
if (logging) {
Fprintf(logfile,"Embedding cell: %s\n",cellname);
PROLOG(logfile);
PrintOwnership(logfile);
PrintC(logfile);
PrintCSTAR(logfile);
Fflush(logfile);
}
if (Exhaustive) {
for (level1 = 0; level1 < MAX_TREE_DEPTH; level1++) {
found = ExhaustivePass(level1);
if (found || FatalError) goto done;
}
}
else {
#if 1
/* do not try to be clever about minimizing passes */
found = -1; /* fake-out to first call below */
for (FillingLevel = 0; FillingLevel < MAX_TREE_DEPTH; FillingLevel++) {
for (level1 = FillingLevel - 1; level1 >= 0 || found == -1; level1--){
if (found == -1) level1 = 0;
found = DoAPass(FillingLevel, level1);
if (found || FatalError) goto done;
/* now try to go up the ladder */
/* NewElements = 1; only do it if we added something in DoAPass */
for (level2 = FillingLevel + 1;
NewElements && level2 < MAX_TREE_DEPTH; level2++) {
found = DoAPass(level2, level2);
if (found || FatalError) goto done;
}
}
}
#else
/* try to minimize number of passes; only works for trees with
leaves at level 0 */
found = -1; /* fake-out to first call below */
SomeNewElements = 1;
for (FillingLevel = 0;
FillingLevel < MAX_TREE_DEPTH && SomeNewElements; FillingLevel++) {
SomeNewElements = 0;
for (level1 = FillingLevel - 1; level1 >= 0 || found == -1; level1--){
if (found == -1) level1 = 0;
found = DoAPass(FillingLevel, level1);
if (NewElements) SomeNewElements = 1;
if (found || FatalError) goto done;
/* now try to go up the ladder */
NewElements = 1;
for (level2 = FillingLevel + 1;
NewElements && level2 < MAX_TREE_DEPTH; level2++) {
found = DoAPass(level2, level2);
if (found || FatalError) goto done;
}
}
}
#endif
}
done:
if (FatalError) {
Fprintf(stdout,"Internal Fatal Error\n");
Fprintf(outfile,"Internal Fatal Error\n");
found = 0;
}
if (found >= MAX_ELEMENTS) found = 0;
if (found) {
struct nlist *tp;
tp = LookupCell(cellname);
FreeEmbeddingTree((struct embed *)(tp->embedding));
tp->embedding = EmbeddingTree(tp, found);
#if 0
PrintEmbedding(stdout,cellname,found);
PrintEmbedding(outfile,cellname,found);
if (logging) PrintEmbedding(logfile,cellname,found);
#endif
PrintEmbeddingTree(stdout,cellname,1);
PrintEmbeddingTree(outfile,cellname,1);
if (logging) PrintEmbeddingTree(logfile,cellname,1);
}
else {
Fprintf(stdout,"No embedding found. Sorry.\n");
Fprintf(outfile,"No embedding found. Sorry.\n");
if (logging) Fprintf(logfile,"No embedding found. Sorry.\n");
}
EPILOG(stdout, found);
EPILOG(outfile, found);
if (logging) EPILOG(logfile, found);
CloseEmbeddingFile();
}