mirror of
https://github.com/YosysHQ/abc.git
synced 2026-09-08 11:53:16 +02:00
Version abc70930
This commit is contained in:
@@ -1,30 +0,0 @@
|
||||
TARGETS = place_test BookshelfView.class
|
||||
|
||||
CFLAGS = -g -pedantic -Wall
|
||||
|
||||
STATIC_LIBS = libhmetis.a
|
||||
DYNAMIC_LIBS = -lm
|
||||
|
||||
OBJECTS = place_test.o place_qpsolver.o place_base.o place_pads.o place_genqp.o place_gordian.o \
|
||||
place_partition.o place_legalize.o place_bin.o
|
||||
|
||||
|
||||
# For hMetis free code, uncomment the following lines
|
||||
#
|
||||
# CFLAGS = -g -pedantic -Wall -DNO_HMETIS
|
||||
# STATIC_LIBS =
|
||||
|
||||
|
||||
all: $(TARGETS)
|
||||
|
||||
%.o: %.c *.h
|
||||
gcc $(CFLAGS) -c -o $@ $<
|
||||
|
||||
place_test: $(OBJECTS)
|
||||
gcc *.o $(STATIC_LIBS) $(DYNAMIC_LIBS) -o place_test
|
||||
|
||||
BookshelfView.class: BookshelfView.java
|
||||
javac BookshelfView.java
|
||||
|
||||
clean:
|
||||
rm -rf *.o place_test *.class *~
|
||||
@@ -1,50 +0,0 @@
|
||||
/*===================================================================*/
|
||||
//
|
||||
// GORDIAN-like placement package
|
||||
//
|
||||
// Aaron P. Hurst ([email protected])
|
||||
// Addl code from Philip Chong ([email protected])
|
||||
// hMetis partitioner (www.cs.umn.edu/~metis)
|
||||
//
|
||||
/*===================================================================*/
|
||||
|
||||
1. Requirements
|
||||
|
||||
An i386 Linux system (though others will certainly work with some tweaks).
|
||||
A standard ANSI C development platform.
|
||||
|
||||
The following are optional, but useful:
|
||||
|
||||
- hMetis partitioner. This can be obtained from (www.cs.umn.edu/~metis)
|
||||
Place (links to) the files "libhmetis.a" and "libhtmetis.h" in this directory.
|
||||
Otherwise, #define NO_HMETIS in the file "place_gordian.h"
|
||||
- Java SDK, if compiling BookshelfView is desired.
|
||||
- Perl, if additional script utilities are desired.
|
||||
|
||||
2. Descriptions of contents:
|
||||
|
||||
place_base.h contains the basic data structures and "external" API.
|
||||
place_gordian.h contains the "internal" API and configuration options.
|
||||
|
||||
There are also several utilities:
|
||||
|
||||
i) place_test
|
||||
|
||||
Reads a netlist description in GSRC Bookshelf format, performs global placement,
|
||||
and rewrites the placement file. An example usage:
|
||||
|
||||
./place_test ac97_emap.nodes ac97_emap.nets ac97_emap.pl
|
||||
|
||||
ii) BookshelfView
|
||||
|
||||
A simple Java GUI to view the resulting placements. It has been tested with
|
||||
Java 5 and 6. Usage:
|
||||
|
||||
java BookshelfView ac97_emap.nodes ac97_emap.pl
|
||||
|
||||
iii) hpwl
|
||||
|
||||
A perl script to print the half-perimeter wirelength of a placement. Usage:
|
||||
|
||||
./hpwl ac97_emap.nets ac97_emal.pl
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
#! /usr/bin/perl
|
||||
|
||||
$netsfile = shift;
|
||||
$plfile = shift;
|
||||
|
||||
# ------------------------------ read placement
|
||||
|
||||
open FILE, $plfile;
|
||||
while (<FILE>) {
|
||||
chop;
|
||||
if (/(\w+)\s+([\-\d\.]+)\s+([\-\d\.]+)\s+\:/) {
|
||||
$loc{$1} = "$2 $3";
|
||||
}
|
||||
}
|
||||
close FILE;
|
||||
|
||||
open FILE, $netsfile;
|
||||
while (<FILE>) {
|
||||
chop;
|
||||
$net = $2 if /NetDegree\s+\:\s+(\d+)\s+(\w+)/;
|
||||
if (/(\w+)\s+(\w+)\s+\:/) {
|
||||
$netconn{$net} .= "$1 ";
|
||||
$cellconn{$1} .= "$net ";
|
||||
}
|
||||
}
|
||||
close FILE;
|
||||
|
||||
# ----------------------------- compute HPWL
|
||||
|
||||
$hpwl = 0;
|
||||
foreach $net (keys %netconn) {
|
||||
@conns = split ' ',$netconn{$net};
|
||||
$min_x = $min_y = 1e12;
|
||||
$max_x = $max_y = -1e12;
|
||||
foreach $cell (@conns) {
|
||||
if (!exists $loc{$cell}) {
|
||||
print "WARNING: Unknown cell location: $cell\n";
|
||||
} else {
|
||||
($x, $y) = split ' ',$loc{$cell};
|
||||
$min_x = $x if $x < $min_x;
|
||||
$min_y = $y if $y < $min_y;
|
||||
$max_x = $x if $x > $max_x;
|
||||
$max_y = $y if $y > $max_y;
|
||||
}
|
||||
}
|
||||
|
||||
if ($min_x eq 1e12 or $min_y eq 1e12 or
|
||||
$max_x eq -1e12 or $max_y eq -1e12) {
|
||||
print "WARNING: Unbounded box\n";
|
||||
} else {
|
||||
$hpwl = $hpwl + $max_x - $min_x + $max_y - $min_y;
|
||||
}
|
||||
}
|
||||
|
||||
print "HPWL = ";
|
||||
printf "%e",$hpwl;
|
||||
print "\n";
|
||||
@@ -1,31 +0,0 @@
|
||||
// A. Hurst [email protected]
|
||||
|
||||
#ifndef LIBHMETIS_H_
|
||||
#define LIBHMETIS_H_
|
||||
|
||||
static void HMETIS_PartRecursive(int nvtxs,
|
||||
int nhedges,
|
||||
int *vwgts,
|
||||
int *eptr,
|
||||
int *eind,
|
||||
int *hewgts,
|
||||
int nparts,
|
||||
int nbfactor,
|
||||
int *options,
|
||||
int *part,
|
||||
int *edgecnt ) {} //;
|
||||
|
||||
|
||||
static void HMETIS_PartKway(int nvtxs,
|
||||
int nhedges,
|
||||
int *vwgts,
|
||||
int *eptr,
|
||||
int *eind,
|
||||
int *hewgts,
|
||||
int nparts,
|
||||
int nbfactor,
|
||||
int *options,
|
||||
int *part,
|
||||
int *edgecnt ) {} //;
|
||||
|
||||
#endif
|
||||
@@ -1,10 +0,0 @@
|
||||
SRC += src/phys/place/place_base.c \
|
||||
src/phys/place/place_bin.c \
|
||||
src/phys/place/place_genqp.c \
|
||||
src/phys/place/place_gordian.c \
|
||||
src/phys/place/place_legalize.c \
|
||||
src/phys/place/place_pads.c \
|
||||
src/phys/place/place_partition.c \
|
||||
src/phys/place/place_qpsolver.c \
|
||||
src/phys/place/place_io.c \
|
||||
src/phys/place/place_inc.c
|
||||
@@ -1,345 +0,0 @@
|
||||
/*===================================================================*/
|
||||
//
|
||||
// place_base.c
|
||||
//
|
||||
// Aaron P. Hurst, 2003-2007
|
||||
// [email protected]
|
||||
//
|
||||
/*===================================================================*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "place_base.h"
|
||||
#include "place_gordian.h"
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Global variables
|
||||
//
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
int g_place_numCells = 0;
|
||||
int g_place_numNets = 0;
|
||||
float g_place_rowHeight = 1.0;
|
||||
|
||||
Rect g_place_coreBounds;
|
||||
Rect g_place_padBounds;
|
||||
|
||||
ConcreteCell **g_place_concreteCells = NULL;
|
||||
int g_place_concreteCellsSize = 0;
|
||||
ConcreteNet **g_place_concreteNets = NULL;
|
||||
int g_place_concreteNetsSize = 0;
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// getNetBBox()
|
||||
//
|
||||
/// \brief Returns the bounding box of a net.
|
||||
//
|
||||
// --------------------------------------------------------------------
|
||||
Rect getNetBBox(const ConcreteNet *net) {
|
||||
int t;
|
||||
Rect r;
|
||||
|
||||
assert(net);
|
||||
|
||||
r.x = r.y = INT_MAX;
|
||||
r.w = r.h = -INT_MAX;
|
||||
for(t=0; t<net->m_numTerms; t++) {
|
||||
r.x = net->m_terms[t]->m_x < r.x ? net->m_terms[t]->m_x : r.x;
|
||||
r.y = net->m_terms[t]->m_y < r.y ? net->m_terms[t]->m_y : r.y;
|
||||
r.w = net->m_terms[t]->m_x > r.w ? net->m_terms[t]->m_x : r.w;
|
||||
r.h = net->m_terms[t]->m_y > r.h ? net->m_terms[t]->m_y : r.h;
|
||||
}
|
||||
r.w -= r.x; r.h -= r.y;
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// getNetWirelength()
|
||||
//
|
||||
/// \brief Returns the half-perimeter wirelength of a net.
|
||||
//
|
||||
// --------------------------------------------------------------------
|
||||
float getNetWirelength(const ConcreteNet *net) {
|
||||
Rect r;
|
||||
|
||||
assert(net);
|
||||
|
||||
r = getNetBBox(net);
|
||||
return r.w+r.h;
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// getTotalWirelength()
|
||||
//
|
||||
/// \brief Returns the total HPWL of all nets.
|
||||
//
|
||||
// --------------------------------------------------------------------
|
||||
float getTotalWirelength() {
|
||||
float r = 0;
|
||||
int n;
|
||||
for(n=0; n<g_place_numNets; n++) if (g_place_concreteNets[n])
|
||||
r += getNetWirelength(g_place_concreteNets[n]);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// getCellArea()
|
||||
//
|
||||
// --------------------------------------------------------------------
|
||||
float getCellArea(const ConcreteCell *cell) {
|
||||
assert(cell);
|
||||
return cell->m_parent->m_width*cell->m_parent->m_height;
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// addConcreteNet()
|
||||
//
|
||||
/// \brief Adds a net to the placement database.
|
||||
///
|
||||
/// The net object must already be allocated and the ID must be set
|
||||
/// appropriately.
|
||||
//
|
||||
// --------------------------------------------------------------------
|
||||
void addConcreteNet(ConcreteNet *net) {
|
||||
assert(net);
|
||||
assert(net->m_id >= 0);
|
||||
if (net->m_id >= g_place_concreteNetsSize) {
|
||||
g_place_concreteNetsSize = (net->m_id > g_place_concreteNetsSize ?
|
||||
net->m_id : g_place_concreteNetsSize);
|
||||
g_place_concreteNetsSize *= 1.5;
|
||||
g_place_concreteNetsSize += 20;
|
||||
g_place_concreteNets = (ConcreteNet**)realloc(g_place_concreteNets,
|
||||
sizeof(ConcreteNet*)*g_place_concreteNetsSize);
|
||||
assert(g_place_concreteNets);
|
||||
}
|
||||
if (net->m_id >= g_place_numNets) {
|
||||
memset(&(g_place_concreteNets[g_place_numNets]), 0,
|
||||
sizeof(ConcreteNet*)*(net->m_id+1-g_place_numNets));
|
||||
g_place_numNets = net->m_id+1;
|
||||
assert(g_place_numNets <= g_place_concreteNetsSize);
|
||||
}
|
||||
g_place_concreteNets[net->m_id] = net;
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// delConcreteNet()
|
||||
//
|
||||
/// Does not deallocate memory.
|
||||
// --------------------------------------------------------------------
|
||||
void delConcreteNet(ConcreteNet *net) {
|
||||
assert(net);
|
||||
g_place_concreteNets[net->m_id] = 0;
|
||||
while(!g_place_concreteNets[g_place_numNets-1]) g_place_numNets--;
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// addConcreteCell()
|
||||
//
|
||||
/// The cell object must already be allocated and the ID must be set
|
||||
/// appropriately.
|
||||
//
|
||||
// --------------------------------------------------------------------
|
||||
void addConcreteCell(ConcreteCell *cell) {
|
||||
assert(cell);
|
||||
assert(cell->m_id >= 0);
|
||||
if (cell->m_id >= g_place_concreteCellsSize) {
|
||||
g_place_concreteCellsSize = (cell->m_id > g_place_concreteCellsSize ?
|
||||
cell->m_id : g_place_concreteCellsSize);
|
||||
g_place_concreteCellsSize *= 1.5;
|
||||
g_place_concreteCellsSize += 20;
|
||||
g_place_concreteCells = (ConcreteCell**)realloc(g_place_concreteCells,
|
||||
sizeof(ConcreteCell*)*g_place_concreteCellsSize);
|
||||
assert(g_place_concreteCells);
|
||||
}
|
||||
if (cell->m_id >= g_place_numCells) {
|
||||
memset(&(g_place_concreteCells[g_place_numCells]), 0,
|
||||
sizeof(ConcreteCell*)*(cell->m_id+1-g_place_numCells));
|
||||
g_place_numCells = cell->m_id+1;
|
||||
}
|
||||
g_place_concreteCells[cell->m_id] = cell;
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// delCellFromPartition()
|
||||
//
|
||||
// --------------------------------------------------------------------
|
||||
void delCellFromPartition(ConcreteCell *cell, Partition *p) {
|
||||
int c;
|
||||
bool found = false;
|
||||
|
||||
assert(cell);
|
||||
assert(p);
|
||||
|
||||
for(c=0; c<p->m_numMembers; c++)
|
||||
if (p->m_members[c] == cell) {
|
||||
p->m_members[c] = 0;
|
||||
p->m_area -= getCellArea(cell);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found) return;
|
||||
|
||||
if (!p->m_leaf) {
|
||||
delCellFromPartition(cell, p->m_sub1);
|
||||
delCellFromPartition(cell, p->m_sub2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// delConcreteCell()
|
||||
//
|
||||
/// \brief Removes a cell from the placement database.
|
||||
///
|
||||
/// Does not deallocate memory.
|
||||
///
|
||||
/// Important: does not modify nets that may point to this
|
||||
/// cell. If these are connections are not removed, segmentation faults
|
||||
/// and other nasty errors will occur.
|
||||
//
|
||||
// --------------------------------------------------------------------
|
||||
void delConcreteCell(ConcreteCell *cell) {
|
||||
assert(cell);
|
||||
g_place_concreteCells[cell->m_id] = 0;
|
||||
while(!g_place_concreteCells[g_place_numCells-1]) g_place_numCells--;
|
||||
|
||||
if (g_place_rootPartition) delCellFromPartition(cell, g_place_rootPartition);
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// netSortByX...
|
||||
//
|
||||
/// \brief Sorts nets by position of one of its corners.
|
||||
//
|
||||
/// These are for use with qsort().
|
||||
///
|
||||
/// Can tolerate pointers to NULL objects.
|
||||
///
|
||||
// --------------------------------------------------------------------
|
||||
int netSortByL(const void *a, const void *b) {
|
||||
const ConcreteNet *pa = *(const ConcreteNet **)a;
|
||||
const ConcreteNet *pb = *(const ConcreteNet **)b;
|
||||
Rect ba, bb;
|
||||
|
||||
if (!pa && !pb) return 0;
|
||||
else if (!pa) return -1;
|
||||
else if (!pb) return 1;
|
||||
ba = getNetBBox(pa), bb = getNetBBox(pb);
|
||||
if (ba.x < bb.x) return -1;
|
||||
if (ba.x > bb.x) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int netSortByR(const void *a, const void *b) {
|
||||
const ConcreteNet *pa = *(const ConcreteNet **)a;
|
||||
const ConcreteNet *pb = *(const ConcreteNet **)b;
|
||||
Rect ba, bb;
|
||||
|
||||
if (!pa && !pb) return 0;
|
||||
else if (!pa) return -1;
|
||||
else if (!pb) return 1;
|
||||
ba = getNetBBox(pa), bb = getNetBBox(pb);
|
||||
if (ba.x + ba.w < bb.x + bb.w) return -1;
|
||||
if (ba.x + ba.w > bb.x + bb.w) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int netSortByB(const void *a, const void *b) {
|
||||
const ConcreteNet *pa = *(const ConcreteNet **)a;
|
||||
const ConcreteNet *pb = *(const ConcreteNet **)b;
|
||||
Rect ba, bb;
|
||||
|
||||
if (!pa && !pb) return 0;
|
||||
else if (!pa) return -1;
|
||||
else if (!pb) return 1;
|
||||
ba = getNetBBox(pa), bb = getNetBBox(pb);
|
||||
if (ba.y + ba.h < bb.y + bb.h) return -1;
|
||||
if (ba.y + ba.h > bb.y + bb.h) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int netSortByT(const void *a, const void *b) {
|
||||
const ConcreteNet *pa = *(const ConcreteNet **)a;
|
||||
const ConcreteNet *pb = *(const ConcreteNet **)b;
|
||||
Rect ba, bb;
|
||||
|
||||
if (!pa && !pb) return 0;
|
||||
else if (!pa) return -1;
|
||||
else if (!pb) return 1;
|
||||
ba = getNetBBox(pa), bb = getNetBBox(pb);
|
||||
if (ba.y < bb.y) return -1;
|
||||
if (ba.y > bb.y) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int netSortByID(const void *a, const void *b) {
|
||||
const ConcreteNet *pa = *(const ConcreteNet **)a;
|
||||
const ConcreteNet *pb = *(const ConcreteNet **)b;
|
||||
|
||||
if (!pa && !pb) return 0;
|
||||
else if (!pa) return -1;
|
||||
else if (!pb) return 1;
|
||||
if (pa->m_id < pb->m_id) return -1;
|
||||
if (pa->m_id > pb->m_id) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// cellSortByX...
|
||||
//
|
||||
/// \brief Sorts cells by either position coordinate.
|
||||
//
|
||||
/// These are for use with qsort().
|
||||
///
|
||||
/// Can tolerate pointers to NULL objects.
|
||||
//
|
||||
// --------------------------------------------------------------------
|
||||
int cellSortByX(const void *a, const void *b) {
|
||||
const ConcreteCell *pa = *(const ConcreteCell **)a;
|
||||
const ConcreteCell *pb = *(const ConcreteCell **)b;
|
||||
|
||||
if (!pa && !pb) return 0;
|
||||
else if (!pa) return -1;
|
||||
else if (!pb) return 1;
|
||||
if (pa->m_x < pb->m_x) return -1;
|
||||
if (pa->m_x > pb->m_x) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cellSortByY(const void *a, const void *b) {
|
||||
const ConcreteCell *pa = *(const ConcreteCell **)a;
|
||||
const ConcreteCell *pb = *(const ConcreteCell **)b;
|
||||
|
||||
if (!pa && !pb) return 0;
|
||||
else if (!pa) return -1;
|
||||
else if (!pb) return 1;
|
||||
if (pa->m_y < pb->m_y) return -1;
|
||||
if (pa->m_y > pb->m_y) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cellSortByID(const void *a, const void *b) {
|
||||
const ConcreteCell *pa = *(const ConcreteCell **)a;
|
||||
const ConcreteCell *pb = *(const ConcreteCell **)b;
|
||||
|
||||
if (!pa && !pb) return 0;
|
||||
else if (!pa) return -1;
|
||||
else if (!pb) return 1;
|
||||
if (pa->m_id < pb->m_id) return -1;
|
||||
if (pa->m_id > pb->m_id) return 1;
|
||||
return 0;
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
/*===================================================================*/
|
||||
//
|
||||
// place_base.h
|
||||
//
|
||||
// Aaron P. Hurst, 2003-2007
|
||||
// [email protected]
|
||||
//
|
||||
/*===================================================================*/
|
||||
|
||||
#if !defined(PLACE_BASE_H_)
|
||||
#define PLACE_BASE_H_
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Data structures
|
||||
//
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
// --- a C++ bool-like type
|
||||
//typedef char bool;
|
||||
#ifndef bool
|
||||
#define bool int
|
||||
#endif
|
||||
|
||||
#define true 1
|
||||
#define false 0
|
||||
|
||||
|
||||
// --- Rect - rectangle
|
||||
|
||||
typedef struct Rect {
|
||||
float x, y;
|
||||
float w, h;
|
||||
} Rect;
|
||||
|
||||
|
||||
// --- AbstractCell - a definition of a cell type
|
||||
|
||||
typedef struct AbstractCell {
|
||||
char *m_label; // string description
|
||||
|
||||
float m_width, m_height; // dimensions
|
||||
|
||||
bool m_pad; // a pad (external I/O) cell?
|
||||
} AbstractCell;
|
||||
|
||||
|
||||
// --- ConcreteCell - a design object
|
||||
|
||||
typedef struct ConcreteCell {
|
||||
int m_id; // a unique ID (see below)
|
||||
char *m_label; // string description
|
||||
|
||||
AbstractCell *m_parent; // cell type
|
||||
|
||||
bool m_fixed; // position is fixed?
|
||||
float m_x, m_y; // center of cell
|
||||
|
||||
int m_data;
|
||||
} ConcreteCell;
|
||||
|
||||
|
||||
// --- ConcreteNet - a design net
|
||||
|
||||
typedef struct ConcreteNet {
|
||||
int m_id; // a unique ID (see below)
|
||||
|
||||
int m_numTerms; // num. of connected cells
|
||||
ConcreteCell **m_terms; // connected cells
|
||||
|
||||
float m_weight; // relative weight
|
||||
|
||||
int m_data;
|
||||
} ConcreteNet;
|
||||
|
||||
|
||||
// A note about IDs - the IDs are non-nonegative integers. They need not
|
||||
// be contiguous, but this is certainly a good idea, as they are stored
|
||||
// in a non-sparse array.
|
||||
// Cells and nets have separate ID spaces.
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Global variable prototypes
|
||||
//
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
// NOTE: None of these need to be managed externally.
|
||||
|
||||
extern int g_place_numCells; // number of cells
|
||||
extern int g_place_numNets; // number of nets
|
||||
extern float g_place_rowHeight; // height of placement row
|
||||
extern Rect g_place_coreBounds; // border of placeable area
|
||||
// (x,y) = corner
|
||||
extern Rect g_place_padBounds; // border of total die area
|
||||
// (x,y) = corner
|
||||
|
||||
extern ConcreteCell **g_place_concreteCells; // all concrete cells
|
||||
extern ConcreteNet **g_place_concreteNets; // all concrete nets
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Function prototypes
|
||||
//
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
void addConcreteNet(ConcreteNet *net);
|
||||
void addConcreteCell(ConcreteCell *cell);
|
||||
void delConcreteNet(ConcreteNet *net);
|
||||
void delConcreteCell(ConcreteCell *cell);
|
||||
|
||||
void globalPreplace(float utilization);
|
||||
void globalPlace();
|
||||
void globalIncremental();
|
||||
void globalFixDensity(int numBins, float maxMovement);
|
||||
|
||||
float fastEstimate(ConcreteCell *cell,
|
||||
int numNets, ConcreteNet *nets[]);
|
||||
float fastTopoPlace(int numCells, ConcreteCell *cells[],
|
||||
int numNets, ConcreteNet *nets[]);
|
||||
|
||||
Rect getNetBBox(const ConcreteNet *net);
|
||||
float getNetWirelength(const ConcreteNet *net);
|
||||
float getTotalWirelength();
|
||||
float getCellArea(const ConcreteCell *cell);
|
||||
|
||||
void writeBookshelf(const char *filename);
|
||||
|
||||
// comparative qsort-style functions
|
||||
int netSortByL(const void *a, const void *b);
|
||||
int netSortByR(const void *a, const void *b);
|
||||
int netSortByB(const void *a, const void *b);
|
||||
int netSortByT(const void *a, const void *b);
|
||||
int netSortByID(const void *a, const void *b);
|
||||
int cellSortByX(const void *a, const void *b);
|
||||
int cellSortByY(const void *a, const void *b);
|
||||
int cellSortByID(const void *a, const void *b);
|
||||
|
||||
#endif
|
||||
@@ -1,277 +0,0 @@
|
||||
/*===================================================================*/
|
||||
//
|
||||
// place_bin.c
|
||||
//
|
||||
// Aaron P. Hurst, 2007
|
||||
// [email protected]
|
||||
//
|
||||
/*===================================================================*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <assert.h>
|
||||
|
||||
//#define DEBUG
|
||||
|
||||
#include "place_base.h"
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Global variables
|
||||
//
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Function prototypes and local data structures
|
||||
//
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
void spreadDensityX(int numBins, float maxMovement);
|
||||
void spreadDensityY(int numBins, float maxMovement);
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// globalFixDensity()
|
||||
//
|
||||
/// Doesn't deal well with fixed cells in the core area.
|
||||
// --------------------------------------------------------------------
|
||||
void globalFixDensity(int numBins, float maxMovement) {
|
||||
|
||||
printf("QCLN-10 : \tbin-based density correction\n");
|
||||
|
||||
spreadDensityX(numBins, maxMovement);
|
||||
// spreadDensityY(numBins, maxMovement);
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// spreadDensityX()
|
||||
//
|
||||
// --------------------------------------------------------------------
|
||||
void spreadDensityX(int numBins, float maxMovement) {
|
||||
|
||||
int c, c2, c3, x, y;
|
||||
float totalArea = 0;
|
||||
int moveableCells = 0;
|
||||
float yBinArea = 0, yCumArea = 0;
|
||||
int yBinStart = 0, yBinCount = 0;
|
||||
int xBinCount, xBinStart;
|
||||
float xBinArea, xCumArea;
|
||||
float lastOldEdge;
|
||||
float lastNewEdge;
|
||||
float curOldEdge, curNewEdge;
|
||||
float stretch, w;
|
||||
ConcreteCell *xCell, *yCell;
|
||||
ConcreteCell **binCells;
|
||||
ConcreteCell **allCells;
|
||||
|
||||
binCells = (ConcreteCell **)malloc(sizeof(ConcreteCell*)*g_place_numCells);
|
||||
allCells = (ConcreteCell **)malloc(sizeof(ConcreteCell*)*g_place_numCells);
|
||||
|
||||
for(c=0; c<g_place_numCells; c++) if (g_place_concreteCells[c]) {
|
||||
ConcreteCell *cell = g_place_concreteCells[c];
|
||||
if (!cell->m_fixed && !cell->m_parent->m_pad) {
|
||||
allCells[moveableCells++] = cell;
|
||||
totalArea += getCellArea(cell);
|
||||
}
|
||||
}
|
||||
|
||||
// spread X
|
||||
qsort(allCells, moveableCells, sizeof(ConcreteCell*), cellSortByY);
|
||||
|
||||
y = 0;
|
||||
|
||||
// for each y-bin...
|
||||
for(c=0; c<moveableCells; c++) {
|
||||
yCell = allCells[c];
|
||||
yBinArea += getCellArea(yCell);
|
||||
yCumArea += getCellArea(yCell);
|
||||
yBinCount++;
|
||||
|
||||
// have we filled up a y-bin?
|
||||
if (yCumArea >= totalArea*(y+1)/numBins && yBinArea > 0) {
|
||||
memcpy(binCells, &(allCells[yBinStart]), sizeof(ConcreteCell*)*yBinCount);
|
||||
qsort(binCells, yBinCount, sizeof(ConcreteCell*), cellSortByX);
|
||||
|
||||
#if defined(DEBUG)
|
||||
printf("y-bin %d count=%d area=%f\n",y,yBinCount, yBinArea);
|
||||
#endif
|
||||
|
||||
x = 0;
|
||||
xBinCount = 0, xBinStart = 0;
|
||||
xBinArea = 0, xCumArea = 0;
|
||||
lastOldEdge = g_place_coreBounds.x;
|
||||
lastNewEdge = g_place_coreBounds.x;
|
||||
|
||||
// for each x-bin...
|
||||
for(c2=0; c2<yBinCount; c2++) {
|
||||
xCell = binCells[c2];
|
||||
xBinArea += getCellArea(xCell);
|
||||
xCumArea += getCellArea(xCell);
|
||||
xBinCount++;
|
||||
curOldEdge = xCell->m_x;
|
||||
|
||||
printf("%.3f ", xCell->m_x);
|
||||
|
||||
// have we filled up an x-bin?
|
||||
if (xCumArea >= yBinArea*(x+1)/numBins && xBinArea > 0) {
|
||||
curNewEdge = lastNewEdge + g_place_coreBounds.w*xBinArea/yBinArea;
|
||||
|
||||
if (curNewEdge > g_place_coreBounds.x+g_place_coreBounds.w)
|
||||
curNewEdge = g_place_coreBounds.x+g_place_coreBounds.w;
|
||||
if ((curNewEdge-curOldEdge)>maxMovement) curNewEdge = curOldEdge + maxMovement;
|
||||
if ((curOldEdge-curNewEdge)>maxMovement) curNewEdge = curOldEdge - maxMovement;
|
||||
|
||||
#if defined(DEBUG)
|
||||
printf("->\tx-bin %d count=%d area=%f (%f,%f)->(%f,%f)\n",x, xBinCount, xBinArea,
|
||||
curOldEdge, lastOldEdge, curNewEdge, lastNewEdge);
|
||||
#endif
|
||||
|
||||
stretch = (curNewEdge-lastNewEdge)/(curOldEdge-lastOldEdge);
|
||||
|
||||
// stretch!
|
||||
for(c3=xBinStart; c3<xBinStart+xBinCount; c3++) {
|
||||
if (curOldEdge == lastOldEdge)
|
||||
binCells[c3]->m_x = lastNewEdge+(c3-xBinStart)*(curNewEdge-lastNewEdge);
|
||||
else
|
||||
binCells[c3]->m_x = lastNewEdge+(binCells[c3]->m_x-lastOldEdge)*stretch;
|
||||
|
||||
// force within core
|
||||
w = binCells[c3]->m_parent->m_width*0.5;
|
||||
if (binCells[c3]->m_x-w < g_place_coreBounds.x)
|
||||
binCells[c3]->m_x = g_place_coreBounds.x+w;
|
||||
if (binCells[c3]->m_x+w > g_place_coreBounds.x+g_place_coreBounds.w)
|
||||
binCells[c3]->m_x = g_place_coreBounds.x+g_place_coreBounds.w-w;
|
||||
}
|
||||
|
||||
lastOldEdge = curOldEdge;
|
||||
lastNewEdge = curNewEdge;
|
||||
x++;
|
||||
xBinCount = 0;
|
||||
xBinArea = 0;
|
||||
xBinStart = c2+1;
|
||||
}
|
||||
}
|
||||
|
||||
y++;
|
||||
yBinCount = 0;
|
||||
yBinArea = 0;
|
||||
yBinStart = c+1;
|
||||
}
|
||||
}
|
||||
|
||||
free(binCells);
|
||||
free(allCells);
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// spreadDensityY()
|
||||
//
|
||||
// --------------------------------------------------------------------
|
||||
void spreadDensityY(int numBins, float maxMovement) {
|
||||
|
||||
int c, c2, c3, x, y;
|
||||
float totalArea = 0;
|
||||
int moveableCells = 0;
|
||||
float xBinArea = 0, xCumArea = 0;
|
||||
int xBinStart = 0, xBinCount = 0;
|
||||
int yBinCount, yBinStart;
|
||||
float yBinArea, yCumArea;
|
||||
float lastOldEdge;
|
||||
float lastNewEdge;
|
||||
float curOldEdge, curNewEdge;
|
||||
float stretch, h;
|
||||
ConcreteCell *xCell, *yCell;
|
||||
ConcreteCell **binCells;
|
||||
ConcreteCell **allCells;
|
||||
|
||||
binCells = (ConcreteCell **)malloc(sizeof(ConcreteCell*)*g_place_numCells);
|
||||
allCells = (ConcreteCell **)malloc(sizeof(ConcreteCell*)*g_place_numCells);
|
||||
|
||||
for(c=0; c<g_place_numCells; c++) if (g_place_concreteCells[c]) {
|
||||
ConcreteCell *cell = g_place_concreteCells[c];
|
||||
if (!cell->m_fixed && !cell->m_parent->m_pad) {
|
||||
allCells[moveableCells++] = cell;
|
||||
totalArea += getCellArea(cell);
|
||||
}
|
||||
}
|
||||
|
||||
// spread Y
|
||||
qsort(allCells, moveableCells, sizeof(ConcreteCell*), cellSortByX);
|
||||
|
||||
x = 0;
|
||||
|
||||
// for each x-bin...
|
||||
for(c=0; c<moveableCells; c++) {
|
||||
xCell = allCells[c];
|
||||
xBinArea += getCellArea(xCell);
|
||||
xCumArea += getCellArea(xCell);
|
||||
xBinCount++;
|
||||
|
||||
// have we filled up an x-bin?
|
||||
if (xCumArea >= totalArea*(x+1)/numBins && xBinArea > 0) {
|
||||
memcpy(binCells, &(allCells[xBinStart]), sizeof(ConcreteCell*)*xBinCount);
|
||||
qsort(binCells, xBinCount, sizeof(ConcreteCell*), cellSortByY);
|
||||
|
||||
// printf("x-bin %d count=%d area=%f\n",y,yBinCount, yBinArea);
|
||||
|
||||
y = 0;
|
||||
yBinCount = 0, yBinStart = 0;
|
||||
yBinArea = 0, yCumArea = 0;
|
||||
lastOldEdge = g_place_coreBounds.y;
|
||||
lastNewEdge = g_place_coreBounds.y;
|
||||
|
||||
// for each y-bin...
|
||||
for(c2=0; c2<xBinCount; c2++) {
|
||||
yCell = binCells[c2];
|
||||
yBinArea += getCellArea(yCell);
|
||||
yCumArea += getCellArea(yCell);
|
||||
yBinCount++;
|
||||
curOldEdge = yCell->m_y;
|
||||
|
||||
// have we filled up an x-bin?
|
||||
if (yCumArea >= xBinArea*(y+1)/numBins && yBinArea > 0) {
|
||||
curNewEdge = lastNewEdge + g_place_coreBounds.h*yBinArea/xBinArea;
|
||||
|
||||
if (curNewEdge > g_place_coreBounds.y+g_place_coreBounds.h)
|
||||
curNewEdge = g_place_coreBounds.y+g_place_coreBounds.h;
|
||||
if ((curNewEdge-curOldEdge)>maxMovement) curNewEdge = curOldEdge + maxMovement;
|
||||
if ((curOldEdge-curNewEdge)>maxMovement) curNewEdge = curOldEdge - maxMovement;
|
||||
|
||||
if (curOldEdge == lastOldEdge) continue; // hmmm
|
||||
stretch = (curNewEdge-lastNewEdge)/(curOldEdge-lastOldEdge);
|
||||
|
||||
// stretch!
|
||||
for(c3=yBinStart; c3<yBinStart+yBinCount; c3++) {
|
||||
binCells[c3]->m_y = lastNewEdge+(binCells[c3]->m_y-lastOldEdge)*stretch;
|
||||
|
||||
// force within core
|
||||
h = binCells[c3]->m_parent->m_height;
|
||||
if (binCells[c3]->m_y-h < g_place_coreBounds.y)
|
||||
binCells[c3]->m_y = g_place_coreBounds.y+h;
|
||||
if (binCells[c3]->m_y+h > g_place_coreBounds.y+g_place_coreBounds.h)
|
||||
binCells[c3]->m_y = g_place_coreBounds.y+g_place_coreBounds.h-h;
|
||||
}
|
||||
|
||||
lastOldEdge = curOldEdge;
|
||||
lastNewEdge = curNewEdge;
|
||||
y++;
|
||||
yBinCount = 0;
|
||||
yBinArea = 0;
|
||||
yBinStart = c2+1;
|
||||
}
|
||||
}
|
||||
|
||||
x++;
|
||||
xBinCount = 0;
|
||||
xBinArea = 0;
|
||||
xBinStart = c+1;
|
||||
}
|
||||
}
|
||||
|
||||
free(binCells);
|
||||
free(allCells);
|
||||
}
|
||||
@@ -1,309 +0,0 @@
|
||||
/*===================================================================*/
|
||||
//
|
||||
// place_genqp.c
|
||||
//
|
||||
// Aaron P. Hurst, 2003-2007
|
||||
// [email protected]
|
||||
//
|
||||
/*===================================================================*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "place_base.h"
|
||||
#include "place_qpsolver.h"
|
||||
#include "place_gordian.h"
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Global variables
|
||||
//
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
qps_problem_t *g_place_qpProb = NULL;
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// splitPenalty()
|
||||
//
|
||||
/// \brief Returns a weight for all of the edges in the clique for a multipin net.
|
||||
//
|
||||
// --------------------------------------------------------------------
|
||||
float splitPenalty(int pins) {
|
||||
|
||||
if (pins > 1) {
|
||||
return 1.0 + CLIQUE_PENALTY/(pins - 1);
|
||||
// return pow(pins - 1, CLIQUE_PENALTY);
|
||||
}
|
||||
return 1.0 + CLIQUE_PENALTY;
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// constructQuadraticProblem()
|
||||
//
|
||||
/// \brief Constructs the matrices necessary to do analytical placement.
|
||||
//
|
||||
// --------------------------------------------------------------------
|
||||
void constructQuadraticProblem() {
|
||||
int maxConnections = 1;
|
||||
int ignoreNum = 0;
|
||||
int n,t,c,c2,p;
|
||||
ConcreteCell *cell;
|
||||
ConcreteNet *net;
|
||||
int *cell_numTerms = calloc(g_place_numCells, sizeof(int));
|
||||
ConcreteNet ***cell_terms = calloc(g_place_numCells, sizeof(ConcreteNet**));
|
||||
bool incremental = false;
|
||||
int nextIndex = 1;
|
||||
int *seen = calloc(g_place_numCells, sizeof(int));
|
||||
float weight;
|
||||
int last_index;
|
||||
|
||||
// create problem object
|
||||
if (!g_place_qpProb) {
|
||||
g_place_qpProb = malloc(sizeof(qps_problem_t));
|
||||
g_place_qpProb->area = NULL;
|
||||
g_place_qpProb->x = NULL;
|
||||
g_place_qpProb->y = NULL;
|
||||
g_place_qpProb->fixed = NULL;
|
||||
g_place_qpProb->connect = NULL;
|
||||
g_place_qpProb->edge_weight = NULL;
|
||||
}
|
||||
|
||||
// count the maximum possible number of non-sparse entries
|
||||
for(n=0; n<g_place_numNets; n++) if (g_place_concreteNets[n]) {
|
||||
ConcreteNet *net = g_place_concreteNets[n];
|
||||
if (net->m_numTerms > IGNORE_NETSIZE) {
|
||||
ignoreNum++;
|
||||
}
|
||||
else {
|
||||
maxConnections += net->m_numTerms*(net->m_numTerms-1);
|
||||
for(t=0; t<net->m_numTerms; t++) {
|
||||
c = net->m_terms[t]->m_id;
|
||||
p = ++cell_numTerms[c];
|
||||
cell_terms[c] = (ConcreteNet**)realloc(cell_terms[c], p*sizeof(ConcreteNet*));
|
||||
cell_terms[c][p-1] = net;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(ignoreNum) {
|
||||
printf("QMAN-10 : \t\t%d large nets ignored\n", ignoreNum);
|
||||
}
|
||||
|
||||
// initialize the data structures
|
||||
g_place_qpProb->num_cells = g_place_numCells;
|
||||
maxConnections += g_place_numCells + 1;
|
||||
|
||||
g_place_qpProb->area = realloc(g_place_qpProb->area,
|
||||
sizeof(float)*g_place_numCells);// "area" matrix
|
||||
g_place_qpProb->edge_weight = realloc(g_place_qpProb->edge_weight,
|
||||
sizeof(float)*maxConnections); // "weight" matrix
|
||||
g_place_qpProb->connect = realloc(g_place_qpProb->connect,
|
||||
sizeof(int)*maxConnections); // "connectivity" matrix
|
||||
g_place_qpProb->fixed = realloc(g_place_qpProb->fixed,
|
||||
sizeof(int)*g_place_numCells); // "fixed" matrix
|
||||
|
||||
// initialize or keep preexisting locations
|
||||
if (g_place_qpProb->x != NULL && g_place_qpProb->y != NULL) {
|
||||
printf("QMAN-10 :\tperforming incremental placement\n");
|
||||
incremental = true;
|
||||
}
|
||||
g_place_qpProb->x = (float*)realloc(g_place_qpProb->x, sizeof(float)*g_place_numCells);
|
||||
g_place_qpProb->y = (float*)realloc(g_place_qpProb->y, sizeof(float)*g_place_numCells);
|
||||
|
||||
// form a row for each cell
|
||||
// build data
|
||||
for(c = 0; c < g_place_numCells; c++) if (g_place_concreteCells[c]) {
|
||||
cell = g_place_concreteCells[c];
|
||||
|
||||
// fill in the characteristics for this cell
|
||||
g_place_qpProb->area[c] = getCellArea(cell);
|
||||
if (cell->m_fixed || cell->m_parent->m_pad) {
|
||||
g_place_qpProb->x[c] = cell->m_x;
|
||||
g_place_qpProb->y[c] = cell->m_y;
|
||||
g_place_qpProb->fixed[c] = 1;
|
||||
} else {
|
||||
if (!incremental) {
|
||||
g_place_qpProb->x[c] = g_place_coreBounds.x+g_place_coreBounds.w*0.5;
|
||||
g_place_qpProb->y[c] = g_place_coreBounds.y+g_place_coreBounds.h*0.5;
|
||||
}
|
||||
g_place_qpProb->fixed[c] = 0;
|
||||
}
|
||||
|
||||
// update connectivity matrices
|
||||
last_index = nextIndex;
|
||||
for(n=0; n<cell_numTerms[c]; n++) {
|
||||
net = cell_terms[c][n];
|
||||
weight = net->m_weight / splitPenalty(net->m_numTerms);
|
||||
for(t=0; t<net->m_numTerms; t++) {
|
||||
c2 = net->m_terms[t]->m_id;
|
||||
if (c2 == c) continue;
|
||||
if (seen[c2] < last_index) {
|
||||
// not seen
|
||||
g_place_qpProb->connect[nextIndex-1] = c2;
|
||||
g_place_qpProb->edge_weight[nextIndex-1] = weight;
|
||||
seen[c2] = nextIndex;
|
||||
nextIndex++;
|
||||
} else {
|
||||
// seen
|
||||
g_place_qpProb->edge_weight[seen[c2]-1] += weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
g_place_qpProb->connect[nextIndex-1] = -1;
|
||||
g_place_qpProb->edge_weight[nextIndex-1] = -1.0;
|
||||
nextIndex++;
|
||||
} else {
|
||||
// fill in dummy values for connectivity matrices
|
||||
g_place_qpProb->connect[nextIndex-1] = -1;
|
||||
g_place_qpProb->edge_weight[nextIndex-1] = -1.0;
|
||||
nextIndex++;
|
||||
}
|
||||
|
||||
free(cell_numTerms);
|
||||
free(cell_terms);
|
||||
free(seen);
|
||||
}
|
||||
|
||||
typedef struct reverseCOG {
|
||||
float x,y;
|
||||
Partition *part;
|
||||
float delta;
|
||||
} reverseCOG;
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// generateCoGConstraints()
|
||||
//
|
||||
/// \brief Generates center of gravity constraints.
|
||||
//
|
||||
// --------------------------------------------------------------------
|
||||
int generateCoGConstraints(reverseCOG COG_rev[]) {
|
||||
int numConstraints = 0; // actual num contraints
|
||||
int cogRevNum = 0;
|
||||
Partition **stack = malloc(sizeof(Partition*)*g_place_numPartitions*2);
|
||||
int stackPtr = 0;
|
||||
Partition *p;
|
||||
float cgx, cgy;
|
||||
int next_index = 0, last_constraint = 0;
|
||||
bool isTrueConstraint = false;
|
||||
int i, m;
|
||||
float totarea;
|
||||
ConcreteCell *cell;
|
||||
|
||||
// each partition may give rise to a center-of-gravity constraint
|
||||
stack[stackPtr] = g_place_rootPartition;
|
||||
while(stackPtr >= 0) {
|
||||
p = stack[stackPtr--];
|
||||
assert(p);
|
||||
|
||||
// traverse down the partition tree to leaf nodes-only
|
||||
if (!p->m_leaf) {
|
||||
stack[++stackPtr] = p->m_sub1;
|
||||
stack[++stackPtr] = p->m_sub2;
|
||||
} else {
|
||||
/*
|
||||
cout << "adding a COG constraint for box "
|
||||
<< p->bounds.x << ","
|
||||
<< p->bounds.y << " of size"
|
||||
<< p->bounds.w << "x"
|
||||
<< p->bounds.h
|
||||
<< endl;
|
||||
*/
|
||||
cgx = p->m_bounds.x + p->m_bounds.w*0.5;
|
||||
cgy = p->m_bounds.y + p->m_bounds.h*0.5;
|
||||
COG_rev[cogRevNum].x = cgx;
|
||||
COG_rev[cogRevNum].y = cgy;
|
||||
COG_rev[cogRevNum].part = p;
|
||||
COG_rev[cogRevNum].delta = 0;
|
||||
|
||||
cogRevNum++;
|
||||
}
|
||||
}
|
||||
|
||||
assert(cogRevNum == g_place_numPartitions);
|
||||
|
||||
for (i = 0; i < g_place_numPartitions; i++) {
|
||||
p = COG_rev[i].part;
|
||||
assert(p);
|
||||
g_place_qpProb->cog_x[numConstraints] = COG_rev[i].x;
|
||||
g_place_qpProb->cog_y[numConstraints] = COG_rev[i].y;
|
||||
totarea = 0.0;
|
||||
for(m=0; m<p->m_numMembers; m++) if (p->m_members[m]) {
|
||||
cell = p->m_members[m];
|
||||
assert(cell);
|
||||
|
||||
if (!cell->m_fixed && !cell->m_parent->m_pad) {
|
||||
isTrueConstraint = true;
|
||||
}
|
||||
else {
|
||||
continue;
|
||||
}
|
||||
g_place_qpProb->cog_list[next_index++] = cell->m_id;
|
||||
totarea += getCellArea(cell);
|
||||
}
|
||||
if (totarea == 0.0) {
|
||||
isTrueConstraint = false;
|
||||
}
|
||||
if (isTrueConstraint) {
|
||||
numConstraints++;
|
||||
g_place_qpProb->cog_list[next_index++] = -1;
|
||||
last_constraint = next_index;
|
||||
}
|
||||
else {
|
||||
next_index = last_constraint;
|
||||
}
|
||||
}
|
||||
|
||||
free(stack);
|
||||
|
||||
return --numConstraints;
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// solveQuadraticProblem()
|
||||
//
|
||||
/// \brief Calls quadratic solver.
|
||||
//
|
||||
// --------------------------------------------------------------------
|
||||
void solveQuadraticProblem(bool useCOG) {
|
||||
int c;
|
||||
|
||||
reverseCOG *COG_rev = malloc(sizeof(reverseCOG)*g_place_numPartitions);
|
||||
|
||||
g_place_qpProb->cog_list = malloc(sizeof(int)*(g_place_numPartitions+g_place_numCells));
|
||||
g_place_qpProb->cog_x = malloc(sizeof(float)*g_place_numPartitions);
|
||||
g_place_qpProb->cog_y = malloc(sizeof(float)*g_place_numPartitions);
|
||||
|
||||
// memset(g_place_qpProb->x, 0, sizeof(float)*g_place_numCells);
|
||||
// memset(g_place_qpProb->y, 0, sizeof(float)*g_place_numCells);
|
||||
|
||||
qps_init(g_place_qpProb);
|
||||
|
||||
if (useCOG)
|
||||
g_place_qpProb->cog_num = generateCoGConstraints(COG_rev);
|
||||
else
|
||||
g_place_qpProb->cog_num = 0;
|
||||
|
||||
g_place_qpProb->loop_num = 0;
|
||||
|
||||
qps_solve(g_place_qpProb);
|
||||
|
||||
qps_clean(g_place_qpProb);
|
||||
|
||||
// set the positions
|
||||
for(c = 0; c < g_place_numCells; c++) if (g_place_concreteCells[c]) {
|
||||
g_place_concreteCells[c]->m_x = g_place_qpProb->x[c];
|
||||
g_place_concreteCells[c]->m_y = g_place_qpProb->y[c];
|
||||
}
|
||||
|
||||
// clean up
|
||||
free(g_place_qpProb->cog_list);
|
||||
free(g_place_qpProb->cog_x);
|
||||
free(g_place_qpProb->cog_y);
|
||||
|
||||
free(COG_rev);
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
/*===================================================================*/
|
||||
//
|
||||
// place_gordian.c
|
||||
//
|
||||
// Aaron P. Hurst, 2003-2007
|
||||
// [email protected]
|
||||
//
|
||||
/*===================================================================*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "place_gordian.h"
|
||||
#include "place_base.h"
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Global variables
|
||||
//
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
int g_place_numPartitions;
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// globalPlace()
|
||||
//
|
||||
/// \brief Performs analytic placement using a GORDIAN-like algorithm.
|
||||
//
|
||||
/// Updates the positions of all non-fixed non-pad cells.
|
||||
///
|
||||
// --------------------------------------------------------------------
|
||||
void globalPlace() {
|
||||
bool completionFlag = false;
|
||||
int iteration = 0;
|
||||
|
||||
printf("PLAC-10 : Global placement (wirelength-driven Gordian)\n");
|
||||
|
||||
initPartitioning();
|
||||
|
||||
// build matrices representing interconnections
|
||||
printf("QMAN-00 : \tconstructing initial quadratic problem...\n");
|
||||
constructQuadraticProblem();
|
||||
|
||||
// iterate placement until termination condition is met
|
||||
while(!completionFlag) {
|
||||
printf("QMAN-01 : \titeration %d numPartitions = %d\n",iteration,g_place_numPartitions);
|
||||
|
||||
// do the global optimization in each direction
|
||||
printf("QMAN-01 : \t\tglobal optimization\n");
|
||||
solveQuadraticProblem(!IGNORE_COG);
|
||||
|
||||
// -------- PARTITIONING BASED CELL SPREADING ------
|
||||
|
||||
// bisection
|
||||
printf("QMAN-01 : \t\tpartition refinement\n");
|
||||
if (REALLOCATE_PARTITIONS) reallocPartitions();
|
||||
completionFlag |= refinePartitions();
|
||||
|
||||
printf("QMAN-01 : \t\twirelength = %e\n", getTotalWirelength());
|
||||
|
||||
iteration++;
|
||||
}
|
||||
|
||||
// final global optimization
|
||||
printf("QMAN-02 : \t\tfinal pass\n");
|
||||
if (FINAL_REALLOCATE_PARTITIONS) reallocPartitions();
|
||||
solveQuadraticProblem(!IGNORE_COG);
|
||||
printf("QMAN-01 : \t\twirelength = %e\n", getTotalWirelength());
|
||||
|
||||
// clean up
|
||||
sanitizePlacement();
|
||||
printf("QMAN-01 : \t\twirelength = %e\n", getTotalWirelength());
|
||||
globalFixDensity(25, g_place_rowHeight*5);
|
||||
printf("QMAN-01 : \t\twirelength = %e\n", getTotalWirelength());
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// globalIncremental()
|
||||
//
|
||||
/// \brief Performs analytic placement using a GORDIAN-like algorithm.
|
||||
//
|
||||
/// Requires a valid set of partitions.
|
||||
///
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
void globalIncremental() {
|
||||
if (!g_place_rootPartition) {
|
||||
printf("WARNING: Can not perform incremental placement\n");
|
||||
globalPlace();
|
||||
return;
|
||||
}
|
||||
|
||||
printf("PLAC-10 : Incremental global placement\n");
|
||||
|
||||
incrementalPartition();
|
||||
|
||||
printf("QMAN-00 : \tconstructing initial quadratic problem...\n");
|
||||
constructQuadraticProblem();
|
||||
|
||||
solveQuadraticProblem(!IGNORE_COG);
|
||||
printf("QMAN-01 : \t\twirelength = %e\n", getTotalWirelength());
|
||||
|
||||
// clean up
|
||||
sanitizePlacement();
|
||||
printf("QMAN-01 : \t\twirelength = %e\n", getTotalWirelength());
|
||||
globalFixDensity(25, g_place_rowHeight*5);
|
||||
printf("QMAN-01 : \t\twirelength = %e\n", getTotalWirelength());
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// sanitizePlacement()
|
||||
//
|
||||
/// \brief Moves any cells that are outside of the core bounds to the nearest location within.
|
||||
//
|
||||
// --------------------------------------------------------------------
|
||||
void sanitizePlacement() {
|
||||
int c;
|
||||
float order_width = g_place_rowHeight;
|
||||
float x, y, edge, w, h;
|
||||
|
||||
printf("QCLN-10 : \tsanitizing placement\n");
|
||||
|
||||
for(c=0; c<g_place_numCells; c++) if (g_place_concreteCells[c]) {
|
||||
ConcreteCell *cell = g_place_concreteCells[c];
|
||||
if (cell->m_fixed || cell->m_parent->m_pad) {
|
||||
continue;
|
||||
}
|
||||
// the new locations of the cells will be distributed within
|
||||
// a small margin inside the border so that ordering is preserved
|
||||
order_width = g_place_rowHeight;
|
||||
|
||||
x = cell->m_x, y = cell->m_y,
|
||||
w = cell->m_parent->m_width, h = cell->m_parent->m_height;
|
||||
|
||||
if ((edge=x-w*0.5) < g_place_coreBounds.x) {
|
||||
x = g_place_coreBounds.x+w*0.5 +
|
||||
order_width/(1.0+g_place_coreBounds.x-edge);
|
||||
}
|
||||
else if ((edge=x+w*0.5) > g_place_coreBounds.x+g_place_coreBounds.w) {
|
||||
x = g_place_coreBounds.x+g_place_coreBounds.w-w*0.5 -
|
||||
order_width/(1.0+edge-g_place_coreBounds.x-g_place_coreBounds.w);
|
||||
}
|
||||
if ((edge=y-h*0.5) < g_place_coreBounds.y) {
|
||||
y = g_place_coreBounds.y+h*0.5 +
|
||||
order_width/(1.0+g_place_coreBounds.y-edge);
|
||||
}
|
||||
else if ((edge=y+h*0.5) > g_place_coreBounds.y+g_place_coreBounds.h) {
|
||||
y = g_place_coreBounds.y+g_place_coreBounds.h-h*0.5 -
|
||||
order_width/(1.0+edge-g_place_coreBounds.x-g_place_coreBounds.w);
|
||||
}
|
||||
cell->m_x = x;
|
||||
cell->m_y = y;
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
/*===================================================================*/
|
||||
//
|
||||
// place_gordian.h
|
||||
//
|
||||
// Aaron P. Hurst, 2003-2007
|
||||
// [email protected]
|
||||
//
|
||||
/*===================================================================*/
|
||||
|
||||
#if !defined(PLACE_GORDIAN_H_)
|
||||
#define PLACE_GORDIAN_H_
|
||||
|
||||
#include "place_base.h"
|
||||
#include "place_qpsolver.h"
|
||||
|
||||
// Parameters for analytic placement
|
||||
#define CLIQUE_PENALTY 1.0
|
||||
#define IGNORE_NETSIZE 20
|
||||
|
||||
// Parameters for partitioning
|
||||
#define LARGEST_FINAL_SIZE 20
|
||||
#define PARTITION_AREA_ONLY true
|
||||
#define REALLOCATE_PARTITIONS false
|
||||
#define FINAL_REALLOCATE_PARTITIONS false
|
||||
#define IGNORE_COG false
|
||||
#define MAX_PARTITION_NONSYMMETRY 0.30
|
||||
|
||||
// Parameters for re-partitioning
|
||||
#define REPARTITION_LEVEL_DEPTH 4
|
||||
#define REPARTITION_TARGET_FRACTION 0.15
|
||||
#define REPARTITION_FM false
|
||||
#define REPARTITION_HMETIS true
|
||||
|
||||
// Parameters for F-M re-partitioning
|
||||
#define FM_MAX_BIN 10
|
||||
#define FM_MAX_PASSES 10
|
||||
|
||||
extern int g_place_numPartitions;
|
||||
|
||||
extern qps_problem_t *g_place_qpProb;
|
||||
|
||||
typedef struct Partition {
|
||||
|
||||
int m_numMembers;
|
||||
ConcreteCell **m_members;
|
||||
Rect m_bounds;
|
||||
bool m_done,
|
||||
m_leaf,
|
||||
m_vertical;
|
||||
float m_area;
|
||||
int m_level;
|
||||
struct Partition *m_sub1, *m_sub2;
|
||||
} Partition;
|
||||
|
||||
extern Partition *g_place_rootPartition;
|
||||
|
||||
void initPartitioning();
|
||||
|
||||
void incrementalPartition();
|
||||
|
||||
bool refinePartitions();
|
||||
void reallocPartitions();
|
||||
bool refinePartition(Partition *p);
|
||||
void resizePartition(Partition *p);
|
||||
void reallocPartition(Partition *p);
|
||||
|
||||
void repartitionHMetis(Partition *parent);
|
||||
void repartitionFM(Partition *parent);
|
||||
|
||||
void partitionScanlineMincut(Partition *parent);
|
||||
void partitionEqualArea(Partition *parent);
|
||||
|
||||
void sanitizePlacement();
|
||||
|
||||
void constructQuadraticProblem();
|
||||
void solveQuadraticProblem(bool useCOG);
|
||||
|
||||
#endif
|
||||
@@ -1,106 +0,0 @@
|
||||
/*===================================================================*/
|
||||
//
|
||||
// place_inc.c
|
||||
//
|
||||
// Aaron P. Hurst, 2003-2007
|
||||
// [email protected]
|
||||
//
|
||||
/*===================================================================*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "place_base.h"
|
||||
#include "place_gordian.h"
|
||||
|
||||
inline int sqHashId(int id, int max) {
|
||||
return ((id * (id+17)) % max);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// --------------------------------------------------------------------
|
||||
// fastPlace()
|
||||
//
|
||||
/// The first cell is assumed to be the "output".
|
||||
// --------------------------------------------------------------------
|
||||
float fastPlace(int numCells, ConcreteCell *cells[],
|
||||
int numNets, ConcreteNet *nets[]) {
|
||||
|
||||
int n, t, c, i, local_id = 0, pass;
|
||||
const int NUM_PASSES = 4;
|
||||
int *cell_numTerms = calloc(numCells, sizeof(int));
|
||||
ConcreteNet **cell_terms;
|
||||
ConcreteNet *net;
|
||||
Rect outputBox;
|
||||
|
||||
outputBox = getNetBBox(nets[0]);
|
||||
|
||||
// assign local ids
|
||||
// put cells in reasonable initial location
|
||||
for(n=0; n<numNets; n++)
|
||||
for(t=0; nets[n]->m_numTerms; t++)
|
||||
nets[n]->m_terms[t]->m_data = -1;
|
||||
|
||||
for(c=0; c<numCells; c++) {
|
||||
cells[c]->m_data = local_id;
|
||||
cells[c]->m_x = outputBox.x + 0.5*outputBox.w;
|
||||
cells[c]->m_y = outputBox.y + 0.5*outputBox.h;
|
||||
}
|
||||
|
||||
// build reverse map of cells to nets
|
||||
for(n=0; n<numNets; n++)
|
||||
for(t=0; nets[n]->m_numTerms; t++) {
|
||||
local_id = nets[n]->m_terms[t]->m_data;
|
||||
if (local_id >= 0)
|
||||
cell_numTerms[local_id]++;
|
||||
}
|
||||
|
||||
for(c=0; c<numCells; c++) {
|
||||
cell_terms[c] = malloc(sizeof(ConcreteNet*)*cell_numTerms[c]);
|
||||
cell_numTerms[c] = 0;
|
||||
}
|
||||
|
||||
for(n=0; n<numNets; n++)
|
||||
for(t=0; nets[n]->m_numTerms; t++) {
|
||||
local_id = nets[n]->m_terms[t]->m_data;
|
||||
if (local_id >= 0)
|
||||
cell_terms[cell_numTerms[local_id]++] = nets[n];
|
||||
}
|
||||
|
||||
// topological order?
|
||||
|
||||
// iterative linear
|
||||
for(pass=0; pass<NUM_PASSES; pass++)
|
||||
for(c=0; c<numCells; c++) {
|
||||
for(n=0; n<cell_numTerms[c]; n++) {
|
||||
net = cell_terms[c];
|
||||
for(t=0; t<net->m_numTerms; t++);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// fastEstimate()
|
||||
//
|
||||
// --------------------------------------------------------------------
|
||||
float fastEstimate(ConcreteCell *cell,
|
||||
int numNets, ConcreteNet *nets[]) {
|
||||
float len = 0;
|
||||
int n;
|
||||
Rect box;
|
||||
|
||||
assert(cell);
|
||||
|
||||
for(n=0; n<numNets; n++) {
|
||||
box = getNetBBox(nets[n]);
|
||||
if (cell->m_x < box.x) len += (box.x - cell->m_x);
|
||||
if (cell->m_x > box.x+box.w) len += (cell->m_x-box.x-box.w);
|
||||
if (cell->m_y < box.y) len += (box.x - cell->m_y);
|
||||
if (cell->m_y > box.y+box.h) len += (cell->m_y-box.y-box.h);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
/*===================================================================*/
|
||||
//
|
||||
// place_io.c
|
||||
//
|
||||
// Aaron P. Hurst, 2003-2007
|
||||
// [email protected]
|
||||
//
|
||||
/*===================================================================*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "place_base.h"
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// writeBookshelfNodes()
|
||||
//
|
||||
// --------------------------------------------------------------------
|
||||
void writeBookshelfNodes(const char *filename) {
|
||||
|
||||
int c = 0;
|
||||
int numNodes, numTerms;
|
||||
|
||||
FILE *nodesFile = fopen(filename, "w");
|
||||
if (!nodesFile) {
|
||||
printf("ERROR: Could not open .nodes file\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
numNodes = numTerms = 0;
|
||||
for(c=0; c<g_place_numCells; c++) if (g_place_concreteCells[c]) {
|
||||
numNodes++;
|
||||
if (g_place_concreteCells[c]->m_parent->m_pad)
|
||||
numTerms++;
|
||||
}
|
||||
|
||||
|
||||
|
||||
fprintf(nodesFile, "UCLA nodes 1.0\n");
|
||||
fprintf(nodesFile, "NumNodes : %d\n", numNodes);
|
||||
fprintf(nodesFile, "NumTerminals : %d\n", numTerms);
|
||||
|
||||
for(c=0; c<g_place_numCells; c++) if (g_place_concreteCells[c]) {
|
||||
fprintf(nodesFile, "CELL%d %f %f %s\n",
|
||||
g_place_concreteCells[c]->m_id,
|
||||
g_place_concreteCells[c]->m_parent->m_width,
|
||||
g_place_concreteCells[c]->m_parent->m_height,
|
||||
(g_place_concreteCells[c]->m_parent->m_pad ? " terminal" : ""));
|
||||
}
|
||||
|
||||
fclose(nodesFile);
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// writeBookshelfPl()
|
||||
//
|
||||
// --------------------------------------------------------------------
|
||||
void writeBookshelfPl(const char *filename) {
|
||||
|
||||
int c = 0;
|
||||
|
||||
FILE *plFile = fopen(filename, "w");
|
||||
if (!plFile) {
|
||||
printf("ERROR: Could not open .pl file\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fprintf(plFile, "UCLA pl 1.0\n");
|
||||
for(c=0; c<g_place_numCells; c++) if (g_place_concreteCells[c]) {
|
||||
fprintf(plFile, "CELL%d %f %f : N %s\n",
|
||||
g_place_concreteCells[c]->m_id,
|
||||
g_place_concreteCells[c]->m_x,
|
||||
g_place_concreteCells[c]->m_y,
|
||||
(g_place_concreteCells[c]->m_fixed ? "\\FIXED" : ""));
|
||||
}
|
||||
|
||||
fclose(plFile);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// writeBookshelf()
|
||||
//
|
||||
// --------------------------------------------------------------------
|
||||
void writeBookshelf(const char *filename) {
|
||||
writeBookshelfNodes("out.nodes");
|
||||
writeBookshelfPl("out.pl");
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
/*===================================================================*/
|
||||
//
|
||||
// place_legalize.c
|
||||
//
|
||||
// Aaron P. Hurst, 2007
|
||||
// [email protected]
|
||||
//
|
||||
/*===================================================================*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "place_base.h"
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// legalize()
|
||||
//
|
||||
// --------------------------------------------------------------------
|
||||
void legalize() {
|
||||
// UNIMPLEMENTED
|
||||
}
|
||||
|
||||
@@ -1,141 +0,0 @@
|
||||
/*===================================================================*/
|
||||
//
|
||||
// place_pads.c
|
||||
//
|
||||
// Aaron P. Hurst, 2003-2007
|
||||
// [email protected]
|
||||
//
|
||||
/*===================================================================*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "place_base.h"
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// globalPreplace()
|
||||
//
|
||||
/// \brief Place pad ring, leaving a core area to meet a desired utilization.
|
||||
//
|
||||
/// Sets the position of pads that aren't already fixed.
|
||||
///
|
||||
/// Computes g_place_coreBounds and g_place_padBounds. Determines
|
||||
/// g_place_rowHeight.
|
||||
//
|
||||
// --------------------------------------------------------------------
|
||||
void globalPreplace(float utilization) {
|
||||
int i, c, h, numRows;
|
||||
float coreArea = 0, totalArea = 0;
|
||||
int padCount = 0;
|
||||
float area;
|
||||
ConcreteCell **padCells = NULL;
|
||||
AbstractCell *padType = NULL;
|
||||
ConcreteCell *cell;
|
||||
float nextPos;
|
||||
int remainingPads, northPads, southPads, eastPads, westPads;
|
||||
|
||||
printf("PLAC-00 : Placing IO pads\n");;
|
||||
|
||||
// identify the pads and compute the total core area
|
||||
g_place_coreBounds.x = g_place_coreBounds.y = 0;
|
||||
g_place_coreBounds.w = g_place_coreBounds.h = -INT_MAX;
|
||||
|
||||
for(c=0; c<g_place_numCells; c++) if (g_place_concreteCells[c]) {
|
||||
cell = g_place_concreteCells[c];
|
||||
area = getCellArea(cell);
|
||||
if (cell->m_parent->m_pad) {
|
||||
padType = cell->m_parent;
|
||||
} else {
|
||||
coreArea += area;
|
||||
g_place_rowHeight = cell->m_parent->m_height;
|
||||
}
|
||||
|
||||
if (cell->m_fixed) {
|
||||
g_place_coreBounds.x = g_place_coreBounds.x < cell->m_x ? g_place_coreBounds.x : cell->m_x;
|
||||
g_place_coreBounds.y = g_place_coreBounds.y < cell->m_y ? g_place_coreBounds.y : cell->m_y;
|
||||
g_place_coreBounds.w = g_place_coreBounds.w > cell->m_x ? g_place_coreBounds.w : cell->m_x;
|
||||
g_place_coreBounds.h = g_place_coreBounds.h > cell->m_y ? g_place_coreBounds.h : cell->m_y;
|
||||
} else if (cell->m_parent->m_pad) {
|
||||
padCells = realloc(padCells, sizeof(ConcreteCell **)*(padCount+1));
|
||||
padCells[padCount++] = cell;
|
||||
}
|
||||
totalArea += area;
|
||||
}
|
||||
if (!padType) {
|
||||
printf("ERROR: No pad cells\n");
|
||||
exit(1);
|
||||
}
|
||||
g_place_padBounds.w -= g_place_padBounds.x;
|
||||
g_place_padBounds.h -= g_place_padBounds.y;
|
||||
|
||||
coreArea /= utilization;
|
||||
|
||||
// create the design boundaries
|
||||
numRows = sqrt(coreArea)/g_place_rowHeight+1;
|
||||
h = numRows * g_place_rowHeight;
|
||||
g_place_coreBounds.h = g_place_coreBounds.h > h ? g_place_coreBounds.h : h;
|
||||
g_place_coreBounds.w = g_place_coreBounds.w > coreArea/g_place_coreBounds.h ?
|
||||
g_place_coreBounds.w : coreArea/g_place_coreBounds.h;
|
||||
// increase the dimensions by the width of the padring
|
||||
g_place_padBounds = g_place_coreBounds;
|
||||
if (padCount) {
|
||||
printf("PLAC-05 : \tpreplacing %d pad cells\n", padCount);
|
||||
g_place_padBounds.x -= padType->m_width;
|
||||
g_place_padBounds.y -= padType->m_height;
|
||||
g_place_padBounds.w = g_place_coreBounds.w+2*padType->m_width;
|
||||
g_place_padBounds.h = g_place_coreBounds.h+2*padType->m_height;
|
||||
}
|
||||
|
||||
printf("PLAC-05 : \tplaceable rows : %d\n", numRows);
|
||||
printf("PLAC-05 : \tcore dimensions : %.0fx%.0f\n",
|
||||
g_place_coreBounds.w, g_place_coreBounds.h);
|
||||
printf("PLAC-05 : \tchip dimensions : %.0fx%.0f\n",
|
||||
g_place_padBounds.w, g_place_padBounds.h);
|
||||
|
||||
remainingPads = padCount;
|
||||
c = 0;
|
||||
|
||||
// north pads
|
||||
northPads = remainingPads/4; remainingPads -= northPads;
|
||||
nextPos = 0;
|
||||
for(i=0; i<northPads; i++) {
|
||||
cell = padCells[c++];
|
||||
cell->m_x = g_place_padBounds.x+cell->m_parent->m_width*0.5 + nextPos;
|
||||
cell->m_y = g_place_padBounds.y+cell->m_parent->m_height*0.5;
|
||||
nextPos += (g_place_padBounds.w-padType->m_width) / northPads;
|
||||
}
|
||||
|
||||
// south pads
|
||||
southPads = remainingPads/3; remainingPads -= southPads;
|
||||
nextPos = 0;
|
||||
for(i=0; i<southPads; i++) {
|
||||
cell = padCells[c++];
|
||||
cell->m_x = g_place_padBounds.w+g_place_padBounds.x-cell->m_parent->m_width*0.5 - nextPos;
|
||||
cell->m_y = g_place_padBounds.h+g_place_padBounds.y-cell->m_parent->m_height*0.5;
|
||||
nextPos += (g_place_padBounds.w-2*padType->m_width) / southPads;
|
||||
}
|
||||
|
||||
// east pads
|
||||
eastPads = remainingPads/2; remainingPads -= eastPads;
|
||||
nextPos = 0;
|
||||
for(i=0; i<eastPads; i++) {
|
||||
cell = padCells[c++];
|
||||
cell->m_x = g_place_padBounds.w+g_place_padBounds.x-cell->m_parent->m_width*0.5;
|
||||
cell->m_y = g_place_padBounds.y+cell->m_parent->m_height*0.5 + nextPos;
|
||||
nextPos += (g_place_padBounds.h-padType->m_height) / eastPads;
|
||||
}
|
||||
|
||||
// west pads
|
||||
westPads = remainingPads;
|
||||
nextPos = 0;
|
||||
for(i=0; i<westPads; i++) {
|
||||
cell = padCells[c++];
|
||||
cell->m_x = g_place_padBounds.x+cell->m_parent->m_width*0.5;
|
||||
cell->m_y = g_place_padBounds.h+g_place_padBounds.y-cell->m_parent->m_height*0.5 - nextPos;
|
||||
nextPos += (g_place_padBounds.h-padType->m_height) / westPads;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,140 +0,0 @@
|
||||
/*===================================================================*/
|
||||
//
|
||||
// place_qpsolver.h
|
||||
//
|
||||
// Philip Chong
|
||||
// [email protected]
|
||||
//
|
||||
/*===================================================================*/
|
||||
|
||||
#if !defined(_QPS_H)
|
||||
#define _QPS_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
typedef float qps_float_t;
|
||||
|
||||
typedef struct qps_problem {
|
||||
|
||||
/* Basic stuff */
|
||||
int num_cells; /* Total number of cells (both fixed and
|
||||
floating) to be placed. */
|
||||
int *connect; /* Connectivity array. Must contain at least
|
||||
num_cells elements with value -1. The
|
||||
entries which precede the first element
|
||||
with value -1 are the indices of the cells
|
||||
which connect to cell 0; the entries
|
||||
which lie between the first and second
|
||||
elements with value -1 are the indices of
|
||||
the cells which connect to cell 1; etc.
|
||||
Example: cells 0 and 1 are connected
|
||||
together, and 1 and 2 are connected as
|
||||
well. *connect = { 1, -1, 0, 2, -1, 1, -1
|
||||
}. */
|
||||
qps_float_t *edge_weight; /* Same structure as connectivity array, but
|
||||
giving the weights assigned to each edge
|
||||
instead. */
|
||||
qps_float_t *x; /* num_cells element array which contains the
|
||||
x-coordinates of the cells. This is used
|
||||
for the initial values in the iterative
|
||||
solution of floating cells, and for the
|
||||
fixed location of fixed cells. */
|
||||
qps_float_t *y; /* num_cells element array of
|
||||
y-coordinates. */
|
||||
int *fixed; /* num_cells element array with value 1 if
|
||||
corresponding cell is fixed, 0 if
|
||||
floating. */
|
||||
qps_float_t f; /* return value for sum-of-square
|
||||
wirelengths. */
|
||||
|
||||
/* COG stuff */
|
||||
int cog_num; /* Number of COG constraints. */
|
||||
int *cog_list; /* Array indicating for each COG constraint
|
||||
which cells belong to that constraint.
|
||||
Format is similar to c array: there must
|
||||
be at least cog_num elements with value
|
||||
-1. The entries of cog_list preceding the
|
||||
first -1 element are the indices of the
|
||||
cells which belong to the first COG
|
||||
constraint; etc. Example: cells 0 and 1
|
||||
belong to one COG constraint, cells 4 and
|
||||
5 belong to another. *cog_list= { 0, 1,
|
||||
-1, 4, 5, -1 }. */
|
||||
qps_float_t *cog_x; /* cog_num element array whose values are the
|
||||
x-coordinates for the corresponding COG
|
||||
constraints. */
|
||||
qps_float_t *cog_y; /* cog_num element array whose values are the
|
||||
y-coordinates for the corresponding COG
|
||||
constraints. */
|
||||
qps_float_t *area; /* num_cells element array whose values are
|
||||
the areas for the corresponding cells;
|
||||
only useful with COG constraints. */
|
||||
|
||||
/* Loop constraint stuff */
|
||||
int loop_num; /* Number of loop constraints. */
|
||||
int *loop_list; /* Array with list of cells for each loop
|
||||
constraint. Format is similar to cog_list.
|
||||
*/
|
||||
qps_float_t *loop_max; /* loop_num element array indicating maximum
|
||||
distance for each loop. */
|
||||
qps_float_t *loop_penalty; /* loop_num element array indicating penalty
|
||||
for each loop. */
|
||||
int loop_k; /* Current iteration for loop optimization. */
|
||||
int loop_done; /* Done flag for loop optimization. */
|
||||
int loop_fail;
|
||||
|
||||
/* max_x/max_y stuff */
|
||||
qps_float_t max_x; /* max x location; only used in
|
||||
constrained optimization. */
|
||||
qps_float_t max_y; /* max y location; only used in
|
||||
constrained optimization. */
|
||||
int max_enable; /* Set to 1 after qps_init() to enable
|
||||
max_x/max_y. */
|
||||
int max_done; /* Done flag for max optimization. */
|
||||
|
||||
/* Private stuff */
|
||||
int *priv_ii;
|
||||
int *priv_cc, *priv_cr;
|
||||
qps_float_t *priv_cw, *priv_ct;
|
||||
int priv_cm;
|
||||
int *priv_gt;
|
||||
int *priv_la;
|
||||
int priv_lm;
|
||||
qps_float_t *priv_gm, *priv_gw;
|
||||
qps_float_t *priv_g, *priv_h, *priv_xi;
|
||||
qps_float_t *priv_tp, *priv_tp2;
|
||||
int priv_n;
|
||||
qps_float_t *priv_cp;
|
||||
qps_float_t priv_f;
|
||||
qps_float_t *priv_lt;
|
||||
qps_float_t *priv_pcg, *priv_pcgt;
|
||||
qps_float_t priv_fmax;
|
||||
qps_float_t priv_fprev;
|
||||
qps_float_t priv_fopt;
|
||||
qps_float_t priv_eps;
|
||||
int priv_pn;
|
||||
qps_float_t *priv_mxl, *priv_mxh, *priv_myl, *priv_myh;
|
||||
int priv_ik;
|
||||
FILE *priv_fp;
|
||||
|
||||
} qps_problem_t;
|
||||
|
||||
/* call qps_init() as soon as the qps_problem_t has been set up */
|
||||
/* this initializes some private data structures */
|
||||
extern void qps_init(qps_problem_t *);
|
||||
|
||||
/* call qps_solve() to solve the given qp problem */
|
||||
extern void qps_solve(qps_problem_t *);
|
||||
|
||||
/* call qps_clean() when finished with the qps_problem_t */
|
||||
/* this discards the private data structures assigned by qps_init() */
|
||||
extern void qps_clean(qps_problem_t *);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* _QPS_H */
|
||||
@@ -1,360 +0,0 @@
|
||||
/*===================================================================*/
|
||||
//
|
||||
// place_test.c
|
||||
//
|
||||
// Aaron P. Hurst, 2003-2007
|
||||
// [email protected]
|
||||
//
|
||||
/*===================================================================*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "place_base.h"
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Hash type/functions
|
||||
//
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
struct hash_element {
|
||||
ConcreteCell *obj;
|
||||
struct hash_element *next;
|
||||
} hash_element;
|
||||
|
||||
int hash_string(int hash_max, const char *str) {
|
||||
unsigned int hash = 0;
|
||||
int p;
|
||||
for(p = 0; p<strlen(str); p++)
|
||||
hash += str[p]*p;
|
||||
return hash % hash_max;
|
||||
}
|
||||
|
||||
void hash_add(struct hash_element **hash, int hash_max,
|
||||
ConcreteCell *cell) {
|
||||
int key = hash_string(hash_max, cell->m_label);
|
||||
// printf("adding %s key = %d\n", cell->m_label, key);
|
||||
struct hash_element *element = malloc(sizeof(struct hash_element));
|
||||
assert(element);
|
||||
element->obj = cell;
|
||||
element->next = hash[key];
|
||||
hash[key] = element;
|
||||
}
|
||||
|
||||
ConcreteCell *hash_find(struct hash_element **hash, int hash_max, const char *str) {
|
||||
int key = hash_string(hash_max, str);
|
||||
// printf("looking for %s key = %d\n", str, key);
|
||||
struct hash_element *next = hash[key];
|
||||
while(next) {
|
||||
if (!strcmp(str, next->obj->m_label))
|
||||
return next->obj;
|
||||
next = next->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Global variables
|
||||
//
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
struct hash_element **hash_cellname;
|
||||
|
||||
int numCells = 0, numNets = 0;
|
||||
|
||||
AbstractCell *abstractCells;
|
||||
ConcreteCell *concreteCells;
|
||||
ConcreteNet *concreteNets;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Function implementations
|
||||
//
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
void readBookshelfNets(char *filename) {
|
||||
char *tok;
|
||||
char buf[1024];
|
||||
const char *DELIMITERS = " \n\t:";
|
||||
int id = 0;
|
||||
int t;
|
||||
ConcreteCell *cell;
|
||||
|
||||
FILE *netsFile = fopen(filename, "r");
|
||||
if (!netsFile) {
|
||||
printf("ERROR: Could not open .nets file\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// line 1 : version
|
||||
while (fgets(buf, 1024, netsFile) && (buf[0] == '\n' || buf[0] == '#'));
|
||||
|
||||
// line 2 : number of nets
|
||||
while (fgets(buf, 1024, netsFile) && (buf[0] == '\n' || buf[0] == '#'));
|
||||
tok = strtok(buf, DELIMITERS);
|
||||
tok = strtok(NULL, DELIMITERS);
|
||||
numNets = atoi(tok);
|
||||
printf("READ-20 : number of nets = %d\n", numNets);
|
||||
concreteNets = malloc(sizeof(ConcreteNet)*numNets);
|
||||
|
||||
// line 3 : number of pins
|
||||
while (fgets(buf, 1024, netsFile) && (buf[0] == '\n' || buf[0] == '#'));
|
||||
|
||||
// line XXX : net definitions
|
||||
while(fgets(buf, 1024, netsFile)) {
|
||||
if (buf[0] == '\n' || buf[0] == '#') continue;
|
||||
|
||||
concreteNets[id].m_id = id;
|
||||
concreteNets[id].m_weight = 1.0;
|
||||
|
||||
tok = strtok(buf, DELIMITERS);
|
||||
if (!!strcmp(tok, "NetDegree")) {
|
||||
printf("%s\n",buf);
|
||||
printf("ERROR: Incorrect format in .nets file\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
tok = strtok(NULL, DELIMITERS);
|
||||
concreteNets[id].m_numTerms = atoi(tok);
|
||||
if (concreteNets[id].m_numTerms < 0 ||
|
||||
concreteNets[id].m_numTerms > 100000) {
|
||||
printf("ERROR: Bad net degree\n");
|
||||
exit(1);
|
||||
}
|
||||
concreteNets[id].m_terms = malloc(sizeof(ConcreteCell*)*
|
||||
concreteNets[id].m_numTerms);
|
||||
|
||||
// read terms
|
||||
t = 0;
|
||||
while(t < concreteNets[id].m_numTerms &&
|
||||
fgets(buf, 1024, netsFile)) {
|
||||
if (buf[0] == '\n' || buf[0] == '#') continue;
|
||||
|
||||
// cell name
|
||||
tok = strtok(buf, DELIMITERS);
|
||||
cell = hash_find(hash_cellname, numCells, tok);
|
||||
if (!cell) {
|
||||
printf("ERROR: Could not find cell %s in .nodes file\n", tok);
|
||||
exit(1);
|
||||
}
|
||||
concreteNets[id].m_terms[t] = cell;
|
||||
t++;
|
||||
}
|
||||
|
||||
// add!
|
||||
addConcreteNet(&(concreteNets[id]));
|
||||
|
||||
id++;
|
||||
}
|
||||
|
||||
fclose(netsFile);
|
||||
}
|
||||
|
||||
void readBookshelfNodes(char *filename) {
|
||||
char *tok;
|
||||
char buf[1024];
|
||||
const char *DELIMITERS = " \n\t:";
|
||||
int id = 0;
|
||||
|
||||
FILE *nodesFile = fopen(filename, "r");
|
||||
if (!nodesFile) {
|
||||
printf("ERROR: Could not open .nodes file\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// line 1 : version
|
||||
while (fgets(buf, 1024, nodesFile) && (buf[0] == '\n' || buf[0] == '#'));
|
||||
|
||||
// line 2 : num nodes
|
||||
while (fgets(buf, 1024, nodesFile) && (buf[0] == '\n' || buf[0] == '#'));
|
||||
tok = strtok(buf, DELIMITERS);
|
||||
tok = strtok(NULL, DELIMITERS);
|
||||
numCells = atoi(tok);
|
||||
printf("READ-10 : number of cells = %d\n", numCells);
|
||||
concreteCells = malloc(sizeof(ConcreteCell)*numCells);
|
||||
abstractCells = malloc(sizeof(AbstractCell)*numCells);
|
||||
hash_cellname = calloc(numCells, sizeof(struct hash_element*));
|
||||
|
||||
// line 3 : num terminals
|
||||
while (fgets(buf, 1024, nodesFile) && (buf[0] == '\n' || buf[0] == '#'));
|
||||
|
||||
// line XXX : cell definitions
|
||||
while(fgets(buf, 1024, nodesFile)) {
|
||||
if (buf[0] == '\n' || buf[0] == '#') continue;
|
||||
|
||||
tok = strtok(buf, DELIMITERS);
|
||||
concreteCells[id].m_id = id;;
|
||||
|
||||
// label
|
||||
concreteCells[id].m_parent = &(abstractCells[id]);
|
||||
concreteCells[id].m_label = malloc(sizeof(char)*strlen(tok)+1);
|
||||
strcpy(concreteCells[id].m_label, tok);
|
||||
abstractCells[id].m_label = concreteCells[id].m_label;
|
||||
hash_add(hash_cellname, numCells,
|
||||
&(concreteCells[id]));
|
||||
|
||||
// dimensions
|
||||
tok = strtok(NULL, DELIMITERS);
|
||||
abstractCells[id].m_width = atof(tok);
|
||||
tok = strtok(NULL, DELIMITERS);
|
||||
abstractCells[id].m_height = atof(tok);
|
||||
tok = strtok(NULL, DELIMITERS);
|
||||
// terminal
|
||||
abstractCells[id].m_pad = tok && !strcmp(tok, "terminal");
|
||||
|
||||
// add!
|
||||
addConcreteCell(&(concreteCells[id]));
|
||||
|
||||
// DEBUG
|
||||
/*
|
||||
printf("\"%s\" : %f x %f\n", concreteCells[id].m_label,
|
||||
abstractCells[id].m_width,
|
||||
abstractCells[id].m_height);
|
||||
*/
|
||||
id++;
|
||||
}
|
||||
|
||||
fclose(nodesFile);
|
||||
}
|
||||
|
||||
void readBookshelfPlacement(char *filename) {
|
||||
char *tok;
|
||||
char buf[1024];
|
||||
const char *DELIMITERS = " \n\t:";
|
||||
ConcreteCell *cell;
|
||||
|
||||
FILE *plFile = fopen(filename, "r");
|
||||
FILE *netsFile = fopen(filename, "r");
|
||||
if (!plFile) {
|
||||
printf("ERROR: Could not open .pl file\n");
|
||||
exit(1);
|
||||
}
|
||||
if (!netsFile) {
|
||||
printf("ERROR: Could not open .nets file\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// line 1 : version
|
||||
while (fgets(buf, 1024, plFile) && (buf[0] == '\n' || buf[0] == '#'));
|
||||
|
||||
// line XXX : placement definitions
|
||||
while(fgets(buf, 1024, plFile)) {
|
||||
if (buf[0] == '\n' || buf[0] == '#') continue;
|
||||
|
||||
tok = strtok(buf, DELIMITERS);
|
||||
|
||||
// cell name
|
||||
cell = hash_find(hash_cellname, numCells, tok);
|
||||
if (!cell) {
|
||||
printf("ERROR: Could not find cell %s in .nodes file\n",tok);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// position
|
||||
tok = strtok(NULL, DELIMITERS);
|
||||
cell->m_x = atof(tok);
|
||||
tok = strtok(NULL, DELIMITERS);
|
||||
cell->m_y = atof(tok);
|
||||
|
||||
// hfixed
|
||||
cell->m_fixed = strtok(NULL, DELIMITERS) &&
|
||||
(tok = strtok(NULL, DELIMITERS)) &&
|
||||
!strcmp(tok, "\\FIXED");
|
||||
}
|
||||
|
||||
fclose(plFile);
|
||||
}
|
||||
|
||||
void writeBookshelfPlacement(char *filename) {
|
||||
int c = 0;
|
||||
|
||||
FILE *plFile = fopen(filename, "w");
|
||||
if (!plFile) {
|
||||
printf("ERROR: Could not open .pl file\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fprintf(plFile, "UCLA pl 1.0\n");
|
||||
for(c=0; c<numCells; c++) {
|
||||
fprintf(plFile, "%s %f %f : N %s\n",
|
||||
concreteCells[c].m_label,
|
||||
concreteCells[c].m_x,
|
||||
concreteCells[c].m_y,
|
||||
(concreteCells[c].m_fixed ? "\\FIXED" : ""));
|
||||
}
|
||||
|
||||
fclose(plFile);
|
||||
}
|
||||
|
||||
// deletes all connections to a cell
|
||||
void delNetConnections(ConcreteCell *cell) {
|
||||
int n, t, t2, count = 0;
|
||||
ConcreteCell **old = malloc(sizeof(ConcreteCell*)*g_place_numCells);
|
||||
|
||||
for(n=0; n<g_place_numNets; n++) if (g_place_concreteNets[n]) {
|
||||
ConcreteNet *net = g_place_concreteNets[n];
|
||||
count = 0;
|
||||
for(t=0; t<net->m_numTerms; t++)
|
||||
if (net->m_terms[t] == cell) count++;
|
||||
if (count) {
|
||||
memcpy(old, net->m_terms, sizeof(ConcreteCell*)*net->m_numTerms);
|
||||
net->m_terms = realloc(net->m_terms,
|
||||
sizeof(ConcreteCell*)*(net->m_numTerms-count));
|
||||
t2 = 0;
|
||||
for(t=0; t<net->m_numTerms; t++)
|
||||
if (old[t] != cell) net->m_terms[t2++] = old[t];
|
||||
net->m_numTerms -= count;
|
||||
}
|
||||
}
|
||||
free(old);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
if (argc != 4) {
|
||||
printf("Usage: %s [nodes] [nets] [pl]\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
readBookshelfNodes(argv[1]);
|
||||
readBookshelfNets(argv[2]);
|
||||
readBookshelfPlacement(argv[3]);
|
||||
|
||||
globalPreplace(0.8);
|
||||
globalPlace();
|
||||
|
||||
// DEBUG net/cell removal/addition
|
||||
/*
|
||||
int i;
|
||||
for(i=1000; i<2000; i++) {
|
||||
delConcreteNet(g_place_concreteNets[i]);
|
||||
delNetConnections(g_place_concreteCells[i]);
|
||||
delConcreteCell(g_place_concreteCells[i]);
|
||||
}
|
||||
|
||||
ConcreteCell newCell[2];
|
||||
newCell[0].m_id = g_place_numCells+1;
|
||||
newCell[0].m_x = 1000;
|
||||
newCell[0].m_y = 1000;
|
||||
newCell[0].m_fixed = false;
|
||||
newCell[0].m_parent = &(abstractCells[1000]);
|
||||
newCell[0].m_label = " ";
|
||||
addConcreteCell(&newCell[0]);
|
||||
newCell[1].m_id = g_place_numCells+3;
|
||||
newCell[1].m_x = 1000;
|
||||
newCell[1].m_y = 1000;
|
||||
newCell[1].m_fixed = false;
|
||||
newCell[1].m_parent = &(abstractCells[1000]);
|
||||
newCell[1].m_label = " ";
|
||||
addConcreteCell(&newCell[1]);
|
||||
*/
|
||||
|
||||
globalIncremental();
|
||||
|
||||
writeBookshelfPlacement(argv[3]);
|
||||
|
||||
free(hash_cellname);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user