(1)support ispd18/19 (2)fix bugs (3)improve runtime

This commit is contained in:
liulixinkerry 2023-12-27 23:02:20 +08:00
parent eaad4838f9
commit ba01cd3652
40 changed files with 1171 additions and 227 deletions

View File

@ -78,10 +78,14 @@ python main.py --dataset ispd2015_fix --design_name mgc_fft_1 --load_from_raw Tr
python main.py --dataset ispd2015_fix --run_all True --load_from_raw True --detail_placement True
```
- To run Routability GP + DP flow for ISPD2015 dataset:
- To run Routability GP + DP flow for ISPD2015/2018/2019 dataset:
```bash
# run all the designs in ispd2015 with routability optimization
# run all the designs with routability optimization
python main.py --dataset ispd2015_fix --run_all True --load_from_raw True --detail_placement True --use_cell_inflate True
python main.py --dataset ispd2018 --run_all True --load_from_raw True --detail_placement True --use_cell_inflate True
python main.py --dataset ispd2019_no_fence --run_all True --load_from_raw True --detail_placement True --use_cell_inflate True
```
**NOTE**: We default enable the deterministic mode. If you don't need determinism and want to run placement in an extremely fast mode, please try to set `--deterministic False` in the Python arguments.

View File

@ -37,19 +37,8 @@ void Cell::ctype(CellType* t) {
int Cell::lx() const { return _lx; }
int Cell::ly() const { return _ly; }
bool Cell::flipX() const { return _flipX; }
bool Cell::flipY() const { return _flipY; }
int Cell::orient() const {
if (!flipX() && !flipY()) {
return 0; // N
} else if (flipX() && flipY()) {
return 2; // S
} else if (flipX() && !flipY()) {
return 4; // FN
} else if (!flipX() && flipY()) {
return 6; // FS
}
return 0;
return _orient;
}
bool Cell::placed() const { return (lx() != INT_MIN) && (ly() != INT_MIN); }
@ -70,38 +59,7 @@ void Cell::place(int x, int y, int orient) {
}
_lx = x;
_ly = y;
switch (orient) {
case 0:
_flipX = false;
_flipY = false;
break;
case 2:
_flipX = true;
_flipY = true;
break;
case 4:
_flipX = true;
_flipY = false;
break;
case 6:
_flipX = false;
_flipY = true;
break;
default:
_flipX = false;
_flipY = false;
break;
}
}
void Cell::place(int x, int y, bool flipX, bool flipY) {
if (_fixed) {
logger.warning("moving fixed cell %s to (%d,%d)", _name.c_str(), x, y);
}
_lx = x;
_ly = y;
_flipX = flipX;
_flipY = flipY;
_orient = orient;
}
void Cell::unplace() {
@ -109,7 +67,7 @@ void Cell::unplace() {
logger.warning("unplace fixed cell %s", _name.c_str());
}
_lx = _ly = INT_MIN;
_flipX = _flipY = false;
_orient = -1;
}
/***** Cell Type *****/

View File

@ -14,19 +14,19 @@ private:
char _topPower = 'x';
vector<Geometry> _obs;
// _nonRegularRects.size() > 0 implies that this cell is a fixed cell and
// its shape is a polygon. Each rectangle is also appended into
// _nonRegularRects.size() > 0 implies that this cell is a fixed cell and
// its shape is a polygon. Each rectangle is also appended into
// Databased.placeBlockages during parsing the polygon-shape cell.
// NOTE: We only support this feature for ICCAD/DAC 2012 benchmarks.
// When processing GPDatabase, we will set this kind of cells' width and
// When processing GPDatabase, we will set this kind of cells' width and
// height as 0 and use placeement blockages to represent their shapes.
vector<Rectangle> _nonRegularRects;
vector<Rectangle> _nonRegularRects;
int _libcell = -1;
public:
std::string name = "";
char cls = 'x';
std::string cls = "x";
bool stdcell = false;
int width = 0;
int height = 0;
@ -84,8 +84,7 @@ private:
int _lx = INT_MIN;
int _ly = INT_MIN;
bool _flipX = false;
bool _flipY = false;
int _orient = -1; // 0:N, 1:W, 2:S, 3:E, 4:FN, 5:FW, 6:FS, 7:FE, -1:NONE
public:
bool highlighted = false;
@ -107,8 +106,6 @@ public:
int hy() const { return ly() + height(); }
int cx() const { return lx() + width() / 2; }
int cy() const { return ly() + height() / 2; }
bool flipX() const;
bool flipY() const;
int orient() const;
int width() const { return _type->width + _spaceL + _spaceR; }
int height() const { return _type->height + _spaceB + _spaceT; }
@ -119,7 +116,6 @@ public:
bool placed() const;
void place(int x, int y);
void place(int x, int y, int orient);
void place(int x, int y, bool flipX, bool flipY);
void unplace();
unsigned numPins() const { return _pins.size(); }

View File

@ -490,9 +490,11 @@ void Database::SetupRows() {
// verify row flipping conflict
bool flipCheckPass = true;
std::vector<char> flip(nSitesY, 0);
std::vector<int> orients(nSitesY, -1);
for (Row* row : rows) {
char isFlip = (row->flip() ? 1 : 2);
int y = (row->y() - coreLY) / siteH;
orients[y] = row->orient(); // TODO: how to handle multiple ROW definitions on same y?
if (flip[y] == 0) {
flip[y] = isFlip;
} else if (flip[y] != isFlip) {
@ -525,6 +527,7 @@ void Database::SetupRows() {
rows[y]->xNum(nSitesX);
rows[y]->yNum(1);
rows[y]->flip(flip[y] == 1);
rows[y]->orient(orients[y]);
}
// set row power-rail
@ -722,10 +725,11 @@ Row* Database::addRow(const string& name,
const int y,
const unsigned xNum,
const unsigned yNum,
const int orient,
const bool flip,
const unsigned xStep,
const unsigned yStep) {
Row* newrow = new Row(name, macro, x, y, xNum, yNum, flip, xStep, yStep);
Row* newrow = new Row(name, macro, x, y, xNum, yNum, orient, flip, xStep, yStep);
rows.push_back(newrow);
return newrow;
}

View File

@ -154,6 +154,7 @@ public:
const int y,
const unsigned xNum = 0,
const unsigned yNum = 0,
const int orient = 0,
const bool flip = false,
const unsigned xStep = 0,
const unsigned yStep = 0);

View File

@ -50,7 +50,7 @@ public:
string name = "";
int x = INT_MIN;
int y = INT_MIN;
int _orient = 0; // 0:N, 1:W, 2:S, 3:E, 4:FN, 5:FW, 6:FS, 7:FE
int _orient = -1; // 0:N, 1:W, 2:S, 3:E, 4:FN, 5:FW, 6:FS, 7:FE, -1:NONE
PinType* type;
Pin* pin;
int gpdb_id = -1;

View File

@ -11,6 +11,7 @@ private:
int _y = 0;
unsigned _xNum = 0;
unsigned _yNum = 0;
int _orient = 0; // 0:N, 1:W, 2:S, 3:E, 4:FN, 5:FW, 6:FS, 7:FE, -1:NONE
bool _flip = false;
unsigned _xStep = 0;
unsigned _yStep = 0;
@ -20,13 +21,14 @@ private:
public:
std::vector<RowSegment> segments;
Row(const string& name, const string& macro, const int x, const int y, const unsigned xNum = 0, const unsigned yNum = 0, const bool flip = false, const unsigned xStep = 0, const unsigned yStep = 0)
Row(const string& name, const string& macro, const int x, const int y, const unsigned xNum = 0, const unsigned yNum = 0, const int orient = 0, const bool flip = false, const unsigned xStep = 0, const unsigned yStep = 0)
: _name(name)
, _macro(macro)
, _x(x)
, _y(y)
, _xNum(xNum)
, _yNum(yNum)
, _orient(orient)
, _flip(flip)
, _xStep(xStep)
, _yStep(yStep) {}
@ -46,6 +48,7 @@ public:
int y() const { return _y; }
unsigned xNum() const { return _xNum; }
unsigned yNum() const { return _yNum; }
int orient() const { return _orient; }
bool flip() const { return _flip; }
unsigned xStep() const { return _xStep; }
unsigned yStep() const { return _yStep; }
@ -56,6 +59,7 @@ public:
void y(const int value) { _y = value; }
void xNum(const unsigned value) { _xNum = value; }
void yNum(const unsigned value) { _yNum = value; }
void orient(const int value) { _orient = value; }
void flip(const bool value) { _flip = value; }
void xStep(const unsigned value) { _xStep = value; }
void yStep(const unsigned value) { _yStep = value; }

View File

@ -417,6 +417,7 @@ bool Database::readBSAux(const std::string& auxFile, const std::string& plFile)
row->xNum(bsData.rowSites[i]);
row->yNum(1);
row->flip((i % 2) == 1);
row->orient((i % 2) * 6); // 0:N or 6:FS
this->dieLX = std::min(this->dieLX, row->x());
this->dieLY = std::min(this->dieLY, row->y());
this->dieHX = std::max(this->dieHX, row->x() + (int)row->width());
@ -575,7 +576,8 @@ bool Database::readBSAux(const std::string& auxFile, const std::string& plFile)
} else {
string celltypename(bsData.typeName[typeID]);
Cell* cell = this->addCell(bsData.cellName[i], this->getCellType(celltypename));
cell->place(bsData.cellX[i], bsData.cellY[i], false, false);
// In Bookshelf, we don't need to consider the cell orient, set it as -1
cell->place(bsData.cellX[i], bsData.cellY[i], -1);
// cout<<cell->x<<","<<cell->y<<endl;
cell->fixed((bsData.cellFixed[i] == (char)1));
}

View File

@ -22,6 +22,7 @@ bool isFlipX(int orient) {
}
return false;
}
bool isFlipY(int orient) {
switch (orient) {
case 0:
@ -36,7 +37,7 @@ bool isFlipY(int orient) {
return false;
}
string getOrient(bool flipX, bool flipY) {
string getRowOrient(bool flipX, bool flipY) {
if (flipX) {
if (flipY) {
return "S";
@ -52,6 +53,32 @@ string getOrient(bool flipX, bool flipY) {
}
}
string getOrient(int orient) {
// 0:N, 1:W, 2:S, 3:E, 4:FN, 5:FW, 6:FS, 7:FE, -1:NONE
switch (orient) {
case 0:
return "N";
case 1:
return "W";
case 2:
return "S";
case 3:
return "E";
case 4:
return "FN";
case 5:
return "FW";
case 6:
return "FS";
case 7:
return "FE";
case -1:
return "NONE";
default:
return "N";
}
}
#define DIR_UP 1
#define DIR_DOWN 2
#define DIR_LEFT 4
@ -360,16 +387,16 @@ bool Database::writeComponents(ofstream& ofs) {
oss << " - " << cell->name() << " " << cell->ctype()->name << endl;
// ofs << " - " << cell->name() << " " << cell->ctype()->name << endl;
if (cell->fixed()) {
oss << " + FIXED ( " << cell->lx() << " " << cell->ly() << " ) "
<< getOrient(cell->flipX(), cell->flipY()) << " ;" << endl;
oss << " + FIXED ( " << cell->lx() << " " << cell->ly() << " ) " << getOrient(cell->orient()) << " ;"
<< endl;
// ofs << " + FIXED ( " << cell->lx() << " " << cell->ly() << " ) "
// << getOrient(cell->flipX(), cell->flipY())
// << getOrient(cell->orient())
// << " ;" << endl;
} else if (cell->placed()) {
oss << " + PLACED ( " << cell->lx() << " " << cell->ly() << " ) "
<< getOrient(cell->flipX(), cell->flipY()) << " ;" << endl;
oss << " + PLACED ( " << cell->lx() << " " << cell->ly() << " ) " << getOrient(cell->orient()) << " ;"
<< endl;
// ofs << " + PLACED ( " << cell->lx() << " " << cell->ly() << " ) "
// << getOrient(cell->flipX(), cell->flipY())
// << getOrient(cell->orient())
// << " ;" << endl;
} else {
oss << " + UNPLACED ;" << endl;
@ -461,7 +488,7 @@ bool Database::writeDEF(const string& file) {
for (Row* row : rows) {
ofs << "ROW " << row->name() << ' ' << row->macro() << ' ' << row->x() << ' ' << row->y() << ' ';
ofs << getOrient(false, row->flip());
ofs << getRowOrient(false, row->flip());
ofs << " DO " << row->xNum() << " BY " << row->yNum() << " STEP " << row->xStep() << ' ' << row->yStep()
<< " ;\n";
}
@ -1158,19 +1185,18 @@ int readLefMacro(lefrCallbackType_e c, lefiMacro* macro, lefiUserData ud) {
celltype->height = round(macro->sizeY() * convertFactor);
if (macro->lefiMacro::hasClass()) {
char clsname[64] = {0};
strcpy(clsname, macro->macroClass());
if (!strcmp(clsname, "CORE")) {
celltype->cls = 'c';
std::string clsname(macro->macroClass());
if (clsname == "CORE") {
celltype->cls = clsname;
celltype->stdcell = true;
} else if (!strcmp(clsname, "BLOCK")) {
celltype->cls = 'b';
} else if (clsname == "BLOCK") {
celltype->cls = clsname;
} else {
celltype->cls = clsname[0];
logger.warning("Class type is not defined: %s", clsname);
celltype->cls = clsname;
logger.warning("Class type is not defined: %s", celltype->cls.c_str());
}
} else {
celltype->cls = 'c';
celltype->cls = "CORE"; // default value
}
if (macro->lefiMacro::hasOrigin()) {
@ -1273,6 +1299,7 @@ int readDefRow(defrCallbackType_e c, defiRow* drow, defiUserData ud) {
drow->y(),
drow->xNum(),
drow->yNum(),
drow->orient(),
isFlipY(drow->orient()),
drow->xStep(),
drow->yStep());
@ -1442,27 +1469,36 @@ int readDefComponentStart(defrCallbackType_e c, int num, defiUserData ud) {
int readDefComponent(defrCallbackType_e c, defiComponent* co, defiUserData ud) {
Database* db = (Database*)ud;
Cell* cell = db->addCell(co->id(), db->getCellType(co->name()));
CellType* celltype = db->getCellType(co->name());
Cell* cell = db->addCell(co->id(), celltype);
if (co->isUnplaced()) {
cell->fixed(false);
cell->unplace();
} else if (co->isPlaced()) {
cell->place(co->placementX(), co->placementY(), isFlipX(co->placementOrient()), isFlipY(co->placementOrient()));
cell->fixed(false);
cell->place(co->placementX(), co->placementY(), co->placementOrient());
if (celltype->cls == "CORE") {
cell->fixed(false);
} else {
// Set all non-CORE cells as fixed cells
cell->fixed(true);
}
if (co->placementOrient() % 2 == 1) {
// 0:N, 1:W, 2:S, 3:E, 4:FN, 5:FW, 6:FS, 7:FE
logger.warning(
"Cell [%s]'s placementOrient [%d] is not supported.", cell->name().c_str(), co->placementOrient());
// 0:N, 1:W, 2:S, 3:E, 4:FN, 5:FW, 6:FS, 7:FE, -1:NONE
logger.warning("Cell [%s]'s placementOrient [%s] is not supported, CLASS: %s.",
cell->name().c_str(),
getOrient(co->placementOrient()).c_str(),
celltype->cls.c_str());
}
} else if (co->isFixed()) {
cell->place(co->placementX(), co->placementY(), isFlipX(co->placementOrient()), isFlipY(co->placementOrient()));
cell->place(co->placementX(), co->placementY(), co->placementOrient());
cell->fixed(true);
if (co->placementOrient() % 2 == 1) {
// 0:N, 1:W, 2:S, 3:E, 4:FN, 5:FW, 6:FS, 7:FE
logger.warning(
"Cell [%s]'s placementOrient [%d] is not supported.", cell->name().c_str(), co->placementOrient());
// 0:N, 1:W, 2:S, 3:E, 4:FN, 5:FW, 6:FS, 7:FE, -1:NONE
logger.warning("Fixed Cell [%s]'s placementOrient [%s] is not supported, CLASS: %s.",
cell->name().c_str(),
getOrient(co->placementOrient()).c_str(),
celltype->cls.c_str());
}
}
return 0;

View File

@ -63,6 +63,7 @@ class PrintfLogger {
static constexpr bool write_log = false;
FILE* f;
bool tmp_verbose_parser_log = false;
int _global_log_level = LOG_INFO;
public:
// void setup_logger(argparse::ArgumentParser parser);
@ -84,12 +85,16 @@ public:
verbose_parser_log = tmp_verbose_parser_log;
}
void set_global_log_level(int value) {
_global_log_level = value;
}
template <typename... Args>
void log(int log_level, const char* format, Args&&... args) {
if (!verbose_parser_log) {
return;
}
if (log_level >= GLOBAL_LOG_LEVEL) {
if (log_level >= _global_log_level) {
std::string curr_log = tstamp.get_time_stamp();
if (log_level > LOG_INFO) {
curr_log += log_level_ANSI_color(log_level);
@ -111,7 +116,7 @@ public:
if (!verbose_parser_log) {
return;
}
if (log_level >= GLOBAL_LOG_LEVEL) {
if (log_level >= _global_log_level) {
std::string curr_log = tstamp.get_time_stamp();
if (log_level > LOG_INFO) {
curr_log += log_level_ANSI_color(log_level);

View File

@ -14,7 +14,6 @@ inline constexpr int LOG_WARN = 4; // 4
inline constexpr int LOG_ERROR = 5; // 5
inline constexpr int LOG_FATAL = 6; // 6
inline constexpr int LOG_OK = 7; // 7
inline int GLOBAL_LOG_LEVEL = LOG_INFO; // change verbose level in Setting.h
} // namespace utils::log_level

View File

@ -26,6 +26,7 @@ PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
float,
int,
int,
int,
float,
float>())
.def("check", &dp::DPTorchRawDB::check)
@ -57,6 +58,7 @@ PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
float xh_,
float yl_,
float yh_,
int num_conn_movable_nodes_,
int num_movable_nodes_,
int num_nodes_,
float site_width_,
@ -79,6 +81,7 @@ PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
xh_,
yl_,
yh_,
num_conn_movable_nodes_,
num_movable_nodes_,
num_nodes_,
site_width_,
@ -90,9 +93,10 @@ PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
m.def("abacusLegalization", [](std::shared_ptr<dp::DPTorchRawDB> at_db_ptr, int num_bins_x, int num_bins_y) {
return dp::abacusLegalization(*at_db_ptr, num_bins_x, num_bins_y);
});
m.def("greedyLegalization", [](std::shared_ptr<dp::DPTorchRawDB> at_db_ptr, int num_bins_x, int num_bins_y) {
return dp::greedyLegalization(*at_db_ptr, num_bins_x, num_bins_y);
});
m.def("greedyLegalization",
[](std::shared_ptr<dp::DPTorchRawDB> at_db_ptr, int num_bins_x, int num_bins_y, bool legalize_filler) {
return dp::greedyLegalization(*at_db_ptr, num_bins_x, num_bins_y, legalize_filler);
});
m.def("kReorder",
[](std::shared_ptr<dp::DPTorchRawDB> at_db_ptr, int num_bins_x, int num_bins_y, int K, int max_iters) {
return dp::kReorder(*at_db_ptr, num_bins_x, num_bins_y, K, max_iters);
@ -114,6 +118,8 @@ PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
m.def("legalityCheck", [](std::shared_ptr<dp::DPTorchRawDB> at_db_ptr, float scale_factor) {
return dp::legalityCheck(*at_db_ptr, scale_factor);
});
m.def("fillerLegalization",
[](std::shared_ptr<dp::DPTorchRawDB> at_db_ptr) { return dp::fillerLegalization(*at_db_ptr); });
}
} // namespace Xplace

View File

@ -4,6 +4,8 @@
namespace dp {
float floatRound(float a, float prec) { return std::round(a / prec) * prec; }
bool boundaryCheck(const float* x,
const float* y,
const float* node_size_x,
@ -94,14 +96,15 @@ bool fenceRegionCheck(const float* x,
const int* flat_region_boxes_start,
const int* node2fence_region_map,
int num_movable_nodes,
int num_regions) {
int num_regions,
float scale_factor) {
bool legal_flag = true;
// check fence regions
for (int i = 0; i < num_movable_nodes; ++i) {
float node_xl = x[i];
float node_yl = y[i];
float node_yl = floatRound(y[i], scale_factor);
float node_xh = node_xl + node_size_x[i];
float node_yh = node_yl + node_size_y[i];
float node_yh = floatRound(node_yl + node_size_y[i], scale_factor);
int region_id = node2fence_region_map[i];
if (region_id < num_regions) {
@ -112,10 +115,12 @@ bool fenceRegionCheck(const float* x,
// otherwise, preprocessing is required
for (int box_id = box_bgn; box_id < box_end; ++box_id) {
int box_offset = box_id * 4;
float box_xl = flat_region_boxes[box_offset];
float box_xh = flat_region_boxes[box_offset + 1];
float box_yl = flat_region_boxes[box_offset + 2];
float box_yh = flat_region_boxes[box_offset + 3];
float box_yl = floatRound(flat_region_boxes[box_offset + 2], scale_factor);
float box_yh = floatRound(flat_region_boxes[box_offset + 3], scale_factor);
float dx = std::max(std::min(node_xh, box_xh) - std::max(node_xl, box_xl), (float)0);
float dy = std::max(std::min(node_yh, box_yh) - std::max(node_yl, box_yl), (float)0);
@ -333,8 +338,19 @@ bool legalityCheckKernelCPU(const float* x,
std::cerr << "site alignment check error!" << std::endl;
}
if (!overlapCheck(
x, y, node_size_x, node_size_y, site_width, row_height, scale_factor, xl, yl, xh, yh, num_nodes, num_movable_nodes)) {
if (!overlapCheck(x,
y,
node_size_x,
node_size_y,
site_width,
row_height,
scale_factor,
xl,
yl,
xh,
yh,
num_nodes,
num_movable_nodes)) {
legal_flag = false;
std::cerr << "overlap check error!" << std::endl;
}
@ -348,7 +364,8 @@ bool legalityCheckKernelCPU(const float* x,
flat_region_boxes_start,
node2fence_region_map,
num_movable_nodes,
num_regions)) {
num_regions,
scale_factor)) {
legal_flag = false;
std::cerr << "fence region check error!" << std::endl;
}

View File

@ -23,6 +23,7 @@ DPTorchRawDB::DPTorchRawDB(torch::Tensor node_lpos_init_,
float xh_,
float yl_,
float yh_,
int num_conn_movable_nodes_,
int num_movable_nodes_,
int num_nodes_,
float site_width_,
@ -45,6 +46,7 @@ DPTorchRawDB::DPTorchRawDB(torch::Tensor node_lpos_init_,
num_nets = hyperedge_list_end_.size(0);
num_regions = region_boxes_end_.size(0);
num_movable_nodes = num_movable_nodes_;
num_conn_movable_nodes = num_conn_movable_nodes_;
flat_node2pin_start_map =
torch::cat({torch::zeros({1}, torch::dtype(torch::kInt32).device(torch::Device(node_size.device()))),
@ -92,12 +94,20 @@ bool DPTorchRawDB::check(float scale_factor) {
}
void DPTorchRawDB::scale(float scale_factor, bool use_round) {
pin_rel_lpos.mul_(scale_factor);
if (use_round) {
pin_rel_lpos.mul_(scale_factor);
node_size.mul_(scale_factor).round_();
node_lpos_init.mul_(scale_factor).round_();
node_size_x.mul_(scale_factor).round_();
node_size_y.mul_(scale_factor).round_();
init_x.mul_(scale_factor).round_();
init_y.mul_(scale_factor).round_();
pin_offset_x.mul_(scale_factor).round_();
pin_offset_y.mul_(scale_factor).round_();
x.mul_(scale_factor).round_();
y.mul_(scale_factor).round_();
flat_region_boxes.mul_(scale_factor).round_();
site_width = round(site_width * scale_factor);
row_height = round(row_height * scale_factor);
@ -106,17 +116,27 @@ void DPTorchRawDB::scale(float scale_factor, bool use_round) {
yl = round(yl * scale_factor);
yh = round(yh * scale_factor);
} else {
node_size.mul_(scale_factor);
node_lpos_init.mul_(scale_factor);
x.mul_(scale_factor);
y.mul_(scale_factor);
flat_region_boxes.mul_(scale_factor);
site_width = site_width * scale_factor;
row_height = row_height * scale_factor;
xl = xl * scale_factor;
xh = xh * scale_factor;
yl = yl * scale_factor;
yh = yh * scale_factor;
float inv_scale_factor = std::round(1.0 / scale_factor);
pin_rel_lpos.div_(inv_scale_factor);
node_size.div_(inv_scale_factor);
node_lpos_init.div_(inv_scale_factor);
node_size_x.div_(inv_scale_factor);
node_size_y.div_(inv_scale_factor);
init_x.div_(inv_scale_factor);
init_y.div_(inv_scale_factor);
pin_offset_x.div_(inv_scale_factor);
pin_offset_y.div_(inv_scale_factor);
x.div_(inv_scale_factor);
y.div_(inv_scale_factor);
flat_region_boxes.div_(inv_scale_factor);
site_width = site_width / inv_scale_factor;
row_height = row_height / inv_scale_factor;
xl = xl / inv_scale_factor;
xh = xh / inv_scale_factor;
yl = yl / inv_scale_factor;
yh = yh / inv_scale_factor;
}
}

View File

@ -25,6 +25,7 @@ public:
float xh_,
float yl_,
float yh_,
int num_conn_movable_nodes_,
int num_movable_nodes_,
int num_nodes_,
float site_width_,
@ -88,6 +89,7 @@ public:
int num_nets;
int num_nodes;
int num_movable_nodes;
int num_conn_movable_nodes;
int num_regions;
float site_width;
@ -100,7 +102,8 @@ public:
// Legalization
bool macroLegalization(DPTorchRawDB& at_db, int num_bins_x, int num_bins_y);
void abacusLegalization(DPTorchRawDB& at_db, int num_bins_x, int num_bins_y);
void greedyLegalization(DPTorchRawDB& at_db, int num_bins_x, int num_bins_y);
void greedyLegalization(DPTorchRawDB& at_db, int num_bins_x, int num_bins_y, bool legalize_filler);
void fillerLegalization(DPTorchRawDB& at_db);
// Detailed Placement
void kReorder(DPTorchRawDB& at_db, int num_bins_x, int num_bins_y, int K, int max_iters);

View File

@ -162,6 +162,10 @@ void construct_spaces(DetailedPlaceData& db,
int right_node_id = row2nodes[j + 1];
right_bound = min(right_bound, host_x[right_node_id]);
}
// space.xh = right_bound;
// NOTE: some designs' fixed nodes are not placed on site (e.g. mgc_edit_dist_a),
// fix these cases by aligning them to site
// FIXME: need regression
space.xh = std::floor(right_bound);
space.xh = floorDiv(space.xh - db.xl, db.site_width) * db.site_width + db.xl;
}

View File

@ -178,8 +178,30 @@ __global__ void move_nodes_kernel(DetailedPlaceDBType db, IndependentSetMatching
if (j != sol_k) {
atomicAdd(state.device_num_moved, 1);
auto const& orig_space = orig_spaces[sol_k];
// printf(
// "apply cost matrix %d, j %d, node_id %d, sol_k %d, pos_id %d, space (%g, %g), "
// "orig_space (%g, %g) xy (%g, %g) orig_xy (%g, %g)\n",
// i,
// j,
// node_id,
// sol_k,
// independent_set[sol_k],
// space.xl,
// space.xh,
// orig_space.xl,
// orig_space.xh,
// x,
// y,
// orig_x[sol_k],
// orig_y[sol_k]);
x = orig_x[sol_k];
bool ret = adjust_pos(x, node_width, orig_space);
if (!ret) {
printf("ERROR: ism adjust_pos, node_width: %g, orig_space(%g, %g)\n",
node_width,
orig_space.xl,
orig_space.xh);
}
assert(ret);
y = orig_y[sol_k];
space = orig_space;

View File

@ -0,0 +1,264 @@
#include "gpudp/lg/legalization_db.h"
namespace dp {
template <typename T>
struct FillerBlank {
T xl;
T yl;
T xh;
T yh;
int bucket_list_level;
FillerBlank() {}
FillerBlank(T xl_, T yl_, T xh_, T yh_) : xl(xl_), yl(yl_), xh(xh_), yh(yh_) {}
void intersect(const FillerBlank& rhs) {
xl = std::max(xl, rhs.xl);
xh = std::min(xh, rhs.xh);
yl = std::max(yl, rhs.yl);
yh = std::min(yh, rhs.yh);
}
};
void fixCells2Bins(const LegalizationData& db,
const float* x,
const float* y,
const float* node_size_x,
const float* node_size_y,
float bin_size_x,
float bin_size_y,
float xl,
float yl,
float xh,
float yh,
int num_bins_x,
int num_bins_y,
int num_nodes,
int num_movable_nodes,
int num_conn_movable_nodes,
std::vector<std::vector<int>>& bin_cells) {
// do not handle large macros
// one cell cannot be distributed to one bin
for (int i = 0; i < num_nodes; i += 1) {
if (i < num_conn_movable_nodes || i >= num_movable_nodes) {
int bin_id_x = (x[i] + node_size_x[i] / 2 - xl) / bin_size_x;
int bin_id_y = (y[i] + node_size_y[i] / 2 - yl) / bin_size_y;
bin_id_x = std::min(std::max(bin_id_x, 0), num_bins_x - 1);
bin_id_y = std::min(std::max(bin_id_y, 0), num_bins_y - 1);
int bin_id = bin_id_x * num_bins_y + bin_id_y;
bin_cells[bin_id].push_back(i);
}
}
// sort bin cells
for (int i = 0; i < num_bins_x * num_bins_y; i += 1) {
std::vector<int>& cells = bin_cells.at(i);
std::sort(cells.begin(), cells.end(), [&](int node_id1, int node_id2) {
float x1 = x[node_id1];
float x2 = x[node_id2];
return x1 < x2 || (x1 == x2 && node_id1 < node_id2);
});
}
}
void reduceBlanks(const float* x,
const float* y,
const float* node_size_x,
const float* node_size_y,
const std::vector<std::vector<int>>& bin_cells,
float bin_size_x,
float bin_size_y,
float xl,
float yl,
float xh,
float yh,
float site_width,
float row_height,
int num_bins_x,
int num_bins_y,
std::vector<std::vector<FillerBlank<float>>>& bin_blanks) {
for (int i = 0; i < num_bins_x * num_bins_y; i += 1) {
int bin_id_x = i / num_bins_y;
int bin_id_y = i - bin_id_x * num_bins_y;
int bin_id = bin_id_x * num_bins_y + bin_id_y;
float bin_xl = xl + bin_id_x * bin_size_x;
float bin_xh = std::min(bin_xl + bin_size_x, xh);
float bin_yl = yl + bin_id_y * bin_size_y;
float bin_yh = std::min(bin_yl + bin_size_y, yh);
FillerBlank<float> blank;
blank.xl = floorDiv((bin_xl - xl), site_width) * site_width + xl; // align blanks to sites
blank.xh = floorDiv((bin_xh - xl), site_width) * site_width + xl; // align blanks to sites
blank.yl = bin_yl;
blank.yh = bin_yl + row_height;
bin_blanks.at(bin_id).push_back(blank);
const std::vector<int>& cells = bin_cells.at(i);
std::vector<FillerBlank<float>>& blanks = bin_blanks.at(bin_id);
for (unsigned int ci = 0; ci < cells.size(); ++ci) {
int node_id = cells.at(ci);
float node_xl = x[node_id];
float node_yl = y[node_id];
float node_xh = node_xl + node_size_x[node_id];
float node_yh = node_yl + node_size_y[node_id];
if (blanks.empty()) {
break;
}
FillerBlank<float>& blank = blanks.back();
if (node_xh > blank.xl && node_xl < blank.xh) // overlap
{
if (node_xl == blank.xl && node_xh == blank.xh) // remove
{
blanks.pop_back();
}
if (node_xl == blank.xl && node_xh < blank.xh) // reduce
{
blank.xl = node_xh;
}
if (node_xl > blank.xl && node_xh == blank.xh) // reduce
{
blank.xh = node_xl;
}
if (node_xl > blank.xl && node_xh < blank.xh) // split
{
FillerBlank<float> new_blank(node_xh, blank.yl, blank.xh, blank.yh);
blank.xh = node_xl;
blanks.push_back(new_blank);
}
}
}
}
// // print blanks
// for (int i = 0; i < num_bins_x * num_bins_y; i += 1) {
// const std::vector<FillerBlank<float>>& blanks = bin_blanks.at(i);
// for (unsigned int j = 0; j < blanks.size(); ++j) {
// const FillerBlank<float>& blank = blanks.at(j);
// logger.info(
// "bin %d: blank %d: xl = %g, xh = %g, yl = %g, yh = %g", i, j, blank.xl, blank.xh, blank.yl, blank.yh);
// }
// }
}
void fillerLegalization(DPTorchRawDB& at_db) {
LegalizationData db(at_db);
int num_blanks_x = 1;
int num_bins_x = 1;
// bin dimension in y direction for blanks is different from that for cells
int num_blanks_y = floorDiv((db.yh - db.yl), db.row_height);
int num_bins_y = num_blanks_y;
logger.info("%s num_blanks_y = %d", "Standard cell legalization", num_blanks_y);
// adjust bin sizes
float bin_size_x = (db.xh - db.xl) / static_cast<float>(num_bins_x);
float bin_size_y = db.row_height;
// allocate bin blanks
std::vector<std::vector<FillerBlank<float>>> bin_blanks(num_blanks_x * num_blanks_y);
std::vector<std::vector<FillerBlank<float>>> bin_blanks_copy(num_blanks_x * num_blanks_y);
std::vector<std::vector<int>> bin_cells(num_bins_x * num_bins_y);
// distribute cells to bins
fixCells2Bins(db,
db.x,
db.y,
db.node_size_x,
db.node_size_y,
bin_size_x,
db.row_height,
db.xl,
db.yl,
db.xh,
db.yh,
num_bins_x,
num_bins_y,
db.num_nodes,
db.num_movable_nodes,
db.num_conn_movable_nodes,
bin_cells);
// distribute blanks to bins
reduceBlanks(db.x,
db.y,
db.node_size_x,
db.node_size_y,
bin_cells,
bin_size_x,
db.row_height,
db.xl,
db.yl,
db.xh,
db.yh,
db.site_width,
db.row_height,
num_bins_x,
num_bins_y,
bin_blanks);
// sort all blanks and create blank bucket list
int maxDegree = 0;
robin_hood::unordered_map<int, vector<FillerBlank<float>>> blank_bucket_list;
for (int i = 0; i < num_blanks_x * num_blanks_y; i += 1) {
std::vector<FillerBlank<float>>& blanks = bin_blanks.at(i);
for (unsigned int j = 0; j < blanks.size(); ++j) {
FillerBlank<float>& blank = blanks.at(j);
blank.bucket_list_level = roundDiv(blank.xh - blank.xl, db.site_width);
maxDegree = std::max(maxDegree, blank.bucket_list_level);
blank_bucket_list[blank.bucket_list_level].push_back(blank);
}
}
logger.info("%s maxDegree = %d", "Blanks", maxDegree);
// sorted filler cell id
vector<int> fillers_to_blank(db.num_movable_nodes - db.num_conn_movable_nodes);
for (int i = 0; i < db.num_movable_nodes - db.num_conn_movable_nodes; i += 1) {
fillers_to_blank[i] = db.num_conn_movable_nodes + i;
}
std::sort(fillers_to_blank.begin(), fillers_to_blank.end(), [&](int node_id1, int node_id2) {
float size_x1 = db.node_size_x[node_id1];
float size_x2 = db.node_size_x[node_id2];
return size_x1 < size_x2 || (size_x1 == size_x2 && node_id1 < node_id2);
});
// for (int i = 0; i < db.num_movable_nodes - db.num_conn_movable_nodes; i += 1) {
// logger.info("filler %d: size_x = %g", filler_id[i], db.node_size_x[filler_id[i]]);
// }
// put filler cells into blanks
while (!fillers_to_blank.empty()) {
int filler_id = fillers_to_blank.back();
int filler_size_int = roundDiv(db.node_size_x[filler_id], db.site_width);
FillerBlank<float>& max_blank = blank_bucket_list[maxDegree].back();
assert(max_blank.bucket_list_level >= filler_size_int);
// update filler position
db.x[filler_id] = max_blank.xl;
db.y[filler_id] = max_blank.yl;
max_blank.xl += db.node_size_x[filler_id];
max_blank.bucket_list_level -= filler_size_int;
blank_bucket_list[max_blank.bucket_list_level].push_back(max_blank);
blank_bucket_list[maxDegree].pop_back();
while (blank_bucket_list[maxDegree].empty()) {
maxDegree -= 1;
}
fillers_to_blank.pop_back();
// logger.info("put filler %d into blank %d: blank_xl = %g, blank_xh = %g",
// filler_id,
// max_blank.bucket_list_level,
// max_blank.xl,
// max_blank.xh);
// logger.info("num_remaining_fillers = %d, max degree = %d", fillers_to_blank.size(), maxDegree);
}
}
} // namespace dp

View File

@ -45,10 +45,16 @@ void distributeCells2Bins(const LegalizationData& db,
int num_bins_y,
int num_nodes,
int num_movable_nodes,
std::vector<std::vector<int>>& bin_cells) {
int num_conn_movable_nodes,
std::vector<std::vector<int>>& bin_cells,
bool legalize_filler) {
// do not handle large macros
// one cell cannot be distributed to one bin
for (int i = 0; i < num_movable_nodes; i += 1) {
int num_legalized_nodes = num_conn_movable_nodes;
if (legalize_filler) {
num_legalized_nodes = num_movable_nodes;
}
for (int i = 0; i < num_legalized_nodes; i += 1) {
if (!db.is_dummy_fixed(i)) {
int bin_id_x = (x[i] + node_size_x[i] / 2 - xl) / bin_size_x;
int bin_id_y = (y[i] + node_size_y[i] / 2 - yl) / bin_size_y;
@ -527,7 +533,7 @@ void minNodeSize(const std::vector<std::vector<int>>& bin_cells,
}
}
void greedyLegalization(DPTorchRawDB& at_db, int num_bins_x, int num_bins_y) {
void greedyLegalization(DPTorchRawDB& at_db, int num_bins_x, int num_bins_y, bool legalize_filler) {
LegalizationData db(at_db);
db.set_num_bins(num_bins_x, num_bins_y);
// first from right to left
@ -566,7 +572,9 @@ void greedyLegalization(DPTorchRawDB& at_db, int num_bins_x, int num_bins_y) {
num_bins_y,
db.num_nodes,
db.num_movable_nodes,
bin_cells);
db.num_conn_movable_nodes,
bin_cells,
legalize_filler);
// allocate bin fixed cells
std::vector<std::vector<int>> bin_fixed_cells(num_bins_x * num_bins_y);

View File

@ -82,6 +82,7 @@ public:
num_threads(at_db.num_threads),
num_nodes(at_db.num_nodes),
num_movable_nodes(at_db.num_movable_nodes),
num_conn_movable_nodes(at_db.num_conn_movable_nodes),
num_nets(at_db.num_nets),
num_pins(at_db.num_pins),
num_regions(at_db.num_regions) {}
@ -125,6 +126,7 @@ public:
float site_width;
int num_nets;
int num_conn_movable_nodes;
int num_movable_nodes;
int num_nodes;
int num_pins;

View File

@ -254,6 +254,7 @@ void GRDatabase::updateUsageLength() {
}
void GRDatabase::addFixObs() {
logger.info("Marking fixed cell obs...");
fixObs.clear();
// 1) add IOPins
for (auto iopin : rawdb.iopins) {
@ -309,6 +310,7 @@ void GRDatabase::addFixObs() {
}
void GRDatabase::addMovObs() {
logger.info("Marking movable cell obs...");
movObs.clear();
for (auto cell : rawdb.cells) {
if (!cell->fixed()) {
@ -320,16 +322,7 @@ void GRDatabase::addMovObs() {
void GRDatabase::addCellObs(std::vector<RectOnLayer>& allObs, db::Cell* cell) {
db::CellType* ctype = cell->ctype();
int cellOrient = 0;
if (!cell->flipX() && !cell->flipY()) {
cellOrient = 0; // N
} else if (cell->flipX() && cell->flipY()) {
cellOrient = 2; // S
} else if (cell->flipX() && !cell->flipY()) {
cellOrient = 4; // FN
} else if (!cell->flipX() && cell->flipY()) {
cellOrient = 6; // FS
}
int cellOrient = cell->orient();
int dx = ctype->originX() + cell->lx();
int dy = ctype->originY() + cell->ly();
// Macro Obs
@ -386,7 +379,7 @@ void GRDatabase::addCellObs(std::vector<RectOnLayer>& allObs, db::Cell* cell) {
tuple<int, int, int, int> GRDatabase::getOrientOffset(int orient, int lx, int ly, int hx, int hy) {
tuple<int, int, int, int> offset; // lx, ly, hx, hy
// 0:N, 1:W, 2:S, 3:E, 4:FN, 5:FW, 6:FS, 7:FE
// 0:N, 1:W, 2:S, 3:E, 4:FN, 5:FW, 6:FS, 7:FE, -1:NONE
switch (orient) {
case 0: // N
offset = {lx, ly, hx, hy};
@ -507,6 +500,19 @@ void GRDatabase::markObs(std::vector<RectOnLayer>& allObs,
utils::BoxT<int> obsBox(
curObs.lx - margin.x, curObs.ly - margin.y, curObs.hx + margin.x, curObs.hy + margin.y);
if (obsBox.IsValid()) {
if (obsBox.hx() <= gridlines[0][0] || obsBox.hy() <= gridlines[1][0] ||
obsBox.lx() >= gridlines[0][gridlines[0].size() - 1] ||
obsBox.ly() >= gridlines[1][gridlines[1].size() - 1]) {
logger.verbose("ignore obs that is outside gridgraph, obsBox: %d %d %d %d",
obsBox.lx(),
obsBox.hx(),
obsBox.ly(),
obsBox.hy());
continue;
}
}
int xmin =
std::upper_bound(gridlines[0].begin(), gridlines[0].end(), obsBox.lx()) - gridlines[0].begin() - 1;
int xmax =
@ -520,7 +526,15 @@ void GRDatabase::markObs(std::vector<RectOnLayer>& allObs,
xmax = std::min(xmax, xSize - 1);
ymax = std::min(ymax, ySize - 1);
if (xmin > xmax || ymin > ymax) {
logger.error("continue obs %d %d %d %d", xmin, xmax, ymin, ymax);
logger.error("continue, obs: %d %d %d %d, obsBox: %d %d %d %d",
xmin,
xmax,
ymin,
ymax,
obsBox.lx(),
obsBox.hx(),
obsBox.ly(),
obsBox.hy());
continue;
}
utils::BoxT<int> grBox(xmin, ymin, xmax, ymax);
@ -745,7 +759,8 @@ void GRDatabase::setupGrNets() {
continue;
}
std::set<std::tuple<int, int, int>> vis;
for (auto& e : pin_shapes) {
for (int shapeIdx = 0; shapeIdx < pin_shapes.size(); shapeIdx++) {
auto& e = pin_shapes[shapeIdx];
int xmin =
std::upper_bound(gridlines[0].begin(), gridlines[0].end(), e.lx) - gridlines[0].begin() - 1;
int xmax =
@ -761,7 +776,37 @@ void GRDatabase::setupGrNets() {
xmax = std::min(std::max(xmax, 0), xSize - 1);
ymax = std::min(std::max(ymax, 0), ySize - 1);
if (xmin > xmax || ymin > ymax) {
logger.error("continue pin %d %d %d %d", xmin, xmax, ymin, ymax);
std::string instName = "";
std::string instType = "";
if (net_pin->iopin != nullptr) {
db::IOPin* iopin = net_pin->iopin;
instName = iopin->name;
instType = iopin->type->name();
} else if (net_pin->cell != nullptr) {
db::Cell* cell = net_pin->cell;
instName = cell->name();
instType = cell->ctype()->name;
}
// NOTE: some benchmarks have strange definition of pin shapes (lx ly hx hy).
// For example, in ispd18_test9, one of ADDFHX2 CI shapes is "RECT 2.59 0.40 3.67 0.36".
logger.error(
"continue netId: %d netName: %s net_pinId: %d | instName: %s instType: %s pinName: %s "
"pinShapeId: %d | grid: %d %d %d %d | coord: %d %d %d %d",
netId,
rawdbNet->name.c_str(),
pinIdx,
instName.c_str(),
instType.c_str(),
net_pin->type->name().c_str(),
shapeIdx,
xmin,
xmax,
ymin,
ymax,
e.lx,
e.hx,
e.ly,
e.hy);
continue;
}
for (int x = xmin; x <= xmax; x++) {

View File

@ -659,6 +659,9 @@ void GPURouter::route(vector<GrNet> &nets, int iter) {
printf("%d\n", test.check_all(13, 128, 0));
exit(0);*/
// const int LEN = 10;
if (std::max(X, Y) >= 2000) {
logger.warning("Extremely large gridgraph may cause error during batch generation.");
}
if (vis.size() == 0) {
vis.resize(2000, std::vector<short>(2000, 0));
visLL.resize(2000, std::vector<short>(2000, 0));

View File

@ -66,6 +66,41 @@ bool loadParams(const py::dict& kwargs) {
utils::verbose_parser_log = false;
}
// global logging level, default is LOG_INFO
// need to enable "verbose_parser_log"
if (kwargs.contains("global_log_level")) {
int log_level = kwargs["global_log_level"].cast<int>();
switch (log_level) {
case 0:
logger.set_global_log_level(utils::log_level::LOG_DEBUG);
break;
case 1:
logger.set_global_log_level(utils::log_level::LOG_VERBOSE);
break;
case 2:
logger.set_global_log_level(utils::log_level::LOG_INFO);
break;
case 3:
logger.set_global_log_level(utils::log_level::LOG_NOTICE);
break;
case 4:
logger.set_global_log_level(utils::log_level::LOG_WARN);
break;
case 5:
logger.set_global_log_level(utils::log_level::LOG_ERROR);
break;
case 6:
logger.set_global_log_level(utils::log_level::LOG_FATAL);
break;
case 7:
logger.set_global_log_level(utils::log_level::LOG_OK);
break;
default:
logger.set_global_log_level(utils::log_level::LOG_INFO);
break;
}
}
if (kwargs.contains("num_threads")) {
db::setting.numThreads = kwargs["num_threads"].cast<int>();
}

View File

@ -305,6 +305,209 @@ void GPDatabase::setupCheckVar() {
// }
}
std::string orient2name(orient_type orient) {
switch (orient) {
case 0:
return "N";
case 1:
return "W";
case 2:
return "S";
case 3:
return "E";
case 4:
return "FN";
case 5:
return "FW";
case 6:
return "FS";
case 7:
return "FE";
case -1:
return "NONE";
default:
return "N";
}
}
orient_type hflip_orient(orient_type orient) {
// N => FN; W => FW; S => FS; E => FE
// FN => N; FW => W; FS => S; FE => E
if (orient == -1) {
return orient;
} else if (orient < 4) {
return orient + 4;
} else {
return orient - 4;
}
}
orient_type rotate_180_orient(orient_type orient) {
// N => S; W => E; S => N; E => W
// FN => FS; FW => FE; FS => FN; FE => FW
if (orient == -1) {
return orient;
} else if (orient == 0 || orient == 1 || orient == 4 || orient == 5) {
return orient + 2;
} else {
return orient - 2;
}
}
orient_type vflip_orient(orient_type orient) { return hflip_orient(rotate_180_orient(orient)); }
void GPDatabase::transferOrient() {
// Transfer all gpdb nodes to "N" direction for global placement
// NOTE: we don't change rawdb
logger.info("Updating gpdb node orient...");
// 0:N, 1:W, 2:S, 3:E, 4:FN, 5:FW, 6:FS, 7:FE, -1:NONE
auto getOrientDegreeFlip = [](orient_type orient) -> std::pair<int, int> {
// return value: {degree, flip}
if (orient == 0) { // N
return {0, 0};
} else if (orient == 1) { // W
return {90, 0};
} else if (orient == 2) { // S
return {180, 0};
} else if (orient == 3) { // E
return {270, 0};
} else if (orient == 4) { // FN
return {0, 1};
} else if (orient == 5) { // FW
return {90, 1};
} else if (orient == 6) { // FS
return {180, 1};
} else if (orient == 7) { // FE
return {270, 1};
} else { // assume NONE is N
return {0, 0};
}
};
auto getRotatedSizes = [](int rotDegree, coord_type width, coord_type height) -> std::pair<coord_type, coord_type> {
if (rotDegree == 0 || rotDegree == 180) {
return {width, height};
} else if (rotDegree == 90 || rotDegree == 270) {
return {height, width};
} else {
logger.warning("Unknown rotation degree %d, regarded as 0", rotDegree);
return {width, height};
}
};
auto getRotatedPinInfo = [](int rotDegree,
coord_type srcNodeWidth,
coord_type srcNodeHeight,
coord_type srcPinWidth,
coord_type srcPinHeight,
coord_type srcPinRelLx,
coord_type srcPinRelLy) {
coord_type dstPinWidth = srcPinWidth;
coord_type dstPinHeight = srcPinHeight;
coord_type dstPinRelLx = std::numeric_limits<coord_type>::max();
coord_type dstPinRelLy = std::numeric_limits<coord_type>::max();
switch (rotDegree) {
default:
logger.warning("Unknown rotation degree %d, regarded as 0", rotDegree);
case 0:
dstPinRelLx = srcPinRelLx;
dstPinRelLy = srcPinRelLy;
break;
case 180:
dstPinRelLx = srcNodeWidth - srcPinRelLx - srcPinWidth;
dstPinRelLy = srcNodeHeight - srcPinRelLy - srcPinHeight;
break;
case 90:
dstPinRelLx = srcNodeHeight - srcPinRelLy - srcPinHeight;
dstPinRelLy = srcPinRelLx;
std::swap(dstPinWidth, dstPinHeight);
break;
case 270:
dstPinRelLx = srcPinRelLy;
dstPinRelLy = srcNodeWidth - srcPinRelLx - srcPinWidth;
std::swap(dstPinWidth, dstPinHeight);
break;
}
return std::make_tuple(dstPinWidth, dstPinHeight, dstPinRelLx, dstPinRelLy);
};
auto getFlipYPinRelPos =
[](coord_type srcNodeWidth, coord_type srcNodeHeight, coord_type srcPinRelLx, coord_type srcPinRelLy) {
// assume the src here are after rotation
coord_type dstPinRelLx = srcNodeWidth - srcPinRelLx;
coord_type dstPinRelLy = srcPinRelLy;
return std::make_pair(dstPinRelLx, dstPinRelLy);
};
// create a vector to restore statistics
int numOrientTypes = 9; // 0:N, 1:W, 2:S, 3:E, 4:FN, 5:FW, 6:FS, 7:FE, -1:NONE
std::vector<int> orient2cnt(numOrientTypes, 0);
for (auto& node : nodes) {
orient_type srcOrient = node.getOrient();
if (srcOrient != 0 && srcOrient != -1) {
// srcOrient is not "N" or "NONE"
orient2cnt[srcOrient]++;
auto [srcDegree, srcFlip] = getOrientDegreeFlip(srcOrient);
auto [dstDegree, dstFlip] = getOrientDegreeFlip(0); // dst: N
// compute rotation degree and flipping Y
int rotDegree = (dstDegree - srcDegree + 360) % 360;
bool flipY = (dstFlip != srcFlip);
auto [dstNodeWidth, dstNodeHeight] = getRotatedSizes(rotDegree, node.getWidth(), node.getHeight());
for (auto& pin_id : node.pins()) {
auto& pin = pins[pin_id];
auto [dstPinWidth, dstPinHeight, dstPinRelLx, dstPinRelLy] = getRotatedPinInfo(rotDegree,
node.getWidth(),
node.getHeight(),
pin.getWidth(),
pin.getHeight(),
pin.getRelLx(),
pin.getRelLy());
pin.setWidth(dstPinWidth);
pin.setHeight(dstPinHeight);
pin.setRelLx(dstPinRelLx);
pin.setRelLy(dstPinRelLy);
}
node.setWidth(dstNodeWidth);
node.setHeight(dstNodeHeight);
if (flipY) {
for (auto& pin_id : node.pins()) {
auto& pin = pins[pin_id];
auto [dstPinRelLx, dstPinRelLy] =
getFlipYPinRelPos(node.getWidth(), node.getHeight(), pin.getRelLx(), pin.getRelLy());
pin.setRelLx(dstPinRelLx);
pin.setRelLy(dstPinRelLy);
}
}
}
}
logger.info("=== Transfer Node Orient Statistics ===");
for (int i = 0; i < orient2cnt.size(); i++) {
int count = orient2cnt[i];
orient_type orient;
if (i == 8) {
orient = -1; // NONE
} else {
orient = i;
}
std::string orientName = orient2name(orient);
if (orient != 0 && orient != -1) {
logger.info(" %s => N: %d nodes in gpdb", orientName.c_str(), count);
}
}
logger.info("=======================================");
}
bool GPDatabase::setup() {
if (db::setting.random_place) {
setup_random_place();
@ -316,6 +519,7 @@ bool GPDatabase::setup() {
setupNets();
setupIndexMap();
setupCheckVar();
transferOrient();
logger.info("Finish initializing global placement database");
return true;
}
@ -600,6 +804,7 @@ std::vector<torch::Tensor> GPDatabase::getSnetInfoTensor() {
void GPDatabase::applyOneNodeOrient(int node_id) {
auto& node = nodes[node_id];
int rowId;
int numRows = database.rows.size();
if (node.getLy() <= database.coreLY) {
@ -611,10 +816,14 @@ void GPDatabase::applyOneNodeOrient(int node_id) {
rowId = std::max(std::min(rowId, numRows - 1), 0);
}
auto row = database.rows[rowId];
if (row->flip()) {
node.setOrient(6); // FS
if (node.getOrient() == -1) {
node.setOrient(row->orient());
} else {
node.setOrient(0); // N
if (row->orient() == vflip_orient(node.getOrient())) {
node.setOrient(row->orient());
} else if (row->orient() == hflip_orient(vflip_orient(node.getOrient()))) {
node.setOrient(vflip_orient(node.getOrient()));
}
}
}

View File

@ -186,6 +186,7 @@ public:
void setupNets();
void setupIndexMap();
void setupCheckVar();
void transferOrient();
bool setup();
bool reset();

View File

@ -1,4 +1,10 @@
The following script will automatically download `ispd2005`, `ispd2015`, and `iccad2019` benchmarks in `./data/raw`. It also preprocesses `ispd2015` benchmark to fix some errors when routing them by Innovus®.
```bash
./download_data.sh
```
```
# Note of Fixing ISPD 2015
We provide a python scirpt `fix_ispd2015_route.py` to fix some errors in `ispd2015` benchmark. Thus, Innovus now can detailedly routed them.
## Limitations
**removeDefSNetVias**: Due to numerous DRVs caused by SNet Vias (spacing) after nanoroute routing, we have enabled `removeDefSNetVias` to remove these vias and address the above issue temporarily. It is likely that these vias are oversized, directly violating the spacing rule. While this adjustment has no significant impact on placement, it does result in open SNets. We sincerely encourage and appreciate contributions towards resolving this issue. Your contribution is highly valued and appreciated.

View File

@ -21,7 +21,29 @@ tar xvzf iccad2019.tar.gz
rm -rf iccad2019.tar.gz
mv iccad2019/ raw/
echo "=== Downloading ispd2018 ==="
mkdir raw/ispd2018
for i in {1..10}
do
wget --no-check-certificate https://www.ispd.cc/contests/18/ispd18_test$i.tgz
tar xvzf ispd18_test$i.tgz
rm -rf ispd18_test$i.tgz
mv ispd18_test$i/ raw/ispd2018/
done
echo "=== Downloading ispd2019 ==="
mkdir raw/ispd2019
for i in {1..10}
do
wget --no-check-certificate https://www.ispd.cc/contests/19/benchmarks/ispd19_test$i.tgz
tar xvzf ispd19_test$i.tgz
rm -rf ispd19_test$i.tgz
mv ispd19_test$i/ raw/ispd2019/
done
python remove_fence_in_ispd19_test5.py
# echo "=== (Optional) Converting raw design to torch data ==="
# python convert_design_to_torch_data.py --dataset ispd2005
# python convert_design_to_torch_data.py --dataset ispd2015_fix
# python convert_design_to_torch_data.py --dataset iccad2019
# python convert_design_to_torch_data.py --dataset iccad2019
# python convert_design_to_torch_data.py --dataset ispd2019

View File

@ -9,8 +9,12 @@ sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
dataset_root = "./raw"
# remove fence region related information, including GROUP, REGION
removeDefFence = True
# Since there are so many DRCs caused by SNet Vias (spacing) when routing by nanoroute,
# we remove these SNet Vias to avoid them. Note that such modification won't affect placement.
# Due to numerous DRVs caused by SNet Vias (spacing) after nanoroute routing, we have
# removed these vias to address the issue. It is likely that these vias are oversized,
# directly violating the spacing rule. While this adjustment has no significant impact
# on placement, it does result in an open SNet. We sincerely encourage and appreciate
# contributions towards resolving this issue. Your contribution is highly valued and
# appreciated.
removeDefSNetVias = True
# Some fixed cell is not placed on manufacture grid, we move them to the nearest grid
fixDefPlaceOnManGrid = True # only for mgc_fft_b, mgc_matrix_mult_b
@ -27,8 +31,9 @@ moveNDRToLef = True
# Add default to fix nanoroute via error.
# FIXME: I dont' know which vias in superblue should be set with default...
addDefaultForLefVia = True # only for mgc_superblue_*
# PG Nets will cause a lot of violations with macro OBS, use EXCEPTPGNET to avoid that
# PG Nets will cause a lot of violations with macro OBS/Route Blkg, use EXCEPTPGNET to avoid that
eceptPGNetsForObs = True
eceptPGNetsForBlkg = True
def generate_one_raw_design(input_root, output_root, design_name):
@ -207,6 +212,7 @@ def generateDefContent(defLines, design_name):
isSNet, isVia, isNdr = False, False, False
isRegion, isGroup = False, False
isBlockages = False
jumpToEndComponents = False
for lid, line in enumerate(defLines):
if fixDefTracksLayers:
@ -266,6 +272,15 @@ def generateDefContent(defLines, design_name):
if isGroup or isRegion:
continue
if eceptPGNetsForBlkg:
if "BLOCKAGES" in line and "END" not in line:
isBlockages = True
if "END BLOCKAGES" in line:
isBlockages = False
if isBlockages:
if "LAYER" in line:
line = line.replace("\n", " + EXCEPTPGNET\n")
if fixDefPlaceOnManGrid and "fft_b" in design_name:
if "+ PLACED ( 661359 799490 ) N" in line:
line = line.replace("661359", "661360")

View File

@ -0,0 +1,75 @@
# This script removes the fence region in ispd2019_test5
import os
import sys
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
# set dataset root
dataset_root = "./raw"
# remove fence region related information, including GROUP, REGION
removeDefFence = True
def generate_one_raw_design(input_root, output_root, design_name):
print("==============")
design_root = os.path.join(input_root, design_name)
#################### lef ####################
defFile = os.path.join(design_root, "%s.input.def" % design_name)
lefFile = os.path.join(design_root, "%s.input.lef" % design_name)
outputLef = "%s/%s/%s.input.lef" % (output_root, design_name, design_name)
if not os.path.exists(os.path.dirname(outputLef)):
os.makedirs(os.path.dirname(outputLef))
if design_name == "ispd19_test5":
with open(defFile) as t:
defLines = t.readlines()
#################### def ####################
defoutputFile = "%s/%s/%s.input.def" % (output_root, design_name, design_name)
defcontent = generateDefContent(defLines, design_name)
with open(defoutputFile, 'w') as f:
f.write(defcontent)
print(defoutputFile)
else:
os.system("cp %s %s/%s/%s.input.def" % (defFile, output_root, design_name, design_name))
os.system("cp %s %s/%s/%s.input.lef" % (lefFile, output_root, design_name, design_name))
def generateDefContent(defLines, design_name):
defcontent = ""
isRegion, isGroup = False, False
for lid, line in enumerate(defLines):
if removeDefFence:
if "REGIONS" in line and "END" not in line:
isRegion = True
continue
if "END REGIONS" in line:
isRegion = False
continue
if "GROUPS" in line and "END" not in line:
isGroup = True
continue
if "END GROUPS" in line:
isGroup = False
continue
if isGroup or isRegion:
continue
defcontent += line
return defcontent
if __name__ == "__main__":
raw_root = os.path.join(dataset_root, "ispd2019")
all_designs = os.listdir(raw_root)
sorted(all_designs)
# Raw design clean
print("-------------------------------------------------")
output_root = os.path.join(dataset_root, "ispd2019_no_fence")
for design_name in all_designs:
generate_one_raw_design(raw_root, output_root, design_name)

11
main.py
View File

@ -39,6 +39,7 @@ def get_option():
# global routing params
parser.add_argument('--use_cell_inflate', type=str2bool, default=False, help='use cell inflation')
parser.add_argument('--min_area_inc', type=float, default=0.01, help='threshold of terminating inflation')
parser.add_argument('--use_route_force', type=str2bool, default=False, help='use routing force')
parser.add_argument('--route_freq', type=int, default=1000, help='routing freq')
parser.add_argument('--num_route_iter', type=int, default=400, help='number of routing iters')
@ -54,7 +55,9 @@ def get_option():
parser.add_argument('--final_route_eval', type=str2bool, default=False, help='eval placement solution by GR')
# logging and saver
parser.add_argument('--log_freq', type=int, default=100)
parser.add_argument('--log_freq', type=int, default=100)
parser.add_argument('--verbose_cpp_log', type=str2bool, default=False, help='verbose cpp log for debugging')
parser.add_argument('--cpp_log_level', type=int, default=2, help='0: DEBUG, 1: VERBOSE, 2:INFO')
parser.add_argument('--result_dir', type=str, default='result', help='log/model root directory')
parser.add_argument('--exp_id', type=str, default='', help='experiment id')
parser.add_argument('--log_dir', type=str, default='log', help='log directory')
@ -76,6 +79,10 @@ def get_option():
print("We haven't yet support fence region in ispd2015, use ispd2015_fix instead")
args.dataset = "ispd2015_fix"
if args.dataset == "ispd2019":
print("We haven't yet support fence region in ispd2019, use ispd2019_no_fence instead")
args.dataset = "ispd2019_no_fence"
if args.custom_path != "":
get_custom_design_params(args)
@ -92,4 +99,4 @@ def main():
if __name__ == "__main__":
main()
main()

View File

@ -58,7 +58,7 @@ def calc_obj_and_grad(
data.hyperedge_list, data.hyperedge_list_end, data.net_mask,
data.hpwl_scale, ps.wa_coeff, args.deterministic
)
mov_node_pos.grad[mov_lhs:mov_rhs] = conn_node_grad_by_wl[mov_lhs:mov_rhs]
mov_node_pos.grad[mov_lhs:mov_rhs] += conn_node_grad_by_wl[mov_lhs:mov_rhs]
if ps.enable_sample_force:
if ps.iter > 3 and ps.iter % 20 == 0:
# ps.iter > 3 for warmup

View File

@ -26,9 +26,11 @@ class RouteCache:
self.original_filler_area_total = None
self.original_pin_rel_cpos: torch.Tensor = None
self.original_target_density = None
self.original_total_mov_area_without_filler = None
self.original_num_fillers = None
self.original_mov_node_size: torch.Tensor = None
self.original_mov_node_size_real: torch.Tensor = None
self.original_init_density_map: torch.Tensor = None
def reset(self):
self.first_run = True
@ -44,9 +46,11 @@ class RouteCache:
self.original_filler_area_total = None
self.original_pin_rel_cpos = None
self.original_target_density = None
self.original_total_mov_area_without_filler = None
self.original_num_fillers = None
self.original_mov_node_size = None
self.original_mov_node_size_real = None
self.original_init_density_map = None
route_cache = RouteCache()
@ -471,13 +475,21 @@ def filler_pseudo_wire_force(data, ps, mov_node_pos, mov_node_size, routeforce,
def route_inflation(
args, logger, data, rawdb, gpdb, ps, mov_node_pos, mov_node_size, expand_ratio,
constraint_fn=None, skip_m1_route=True, use_weighted_inflation=True, hv_same_ratio=True,
min_area_inc=0.01, decrease_target_density=False, **kwargs
dynamic_target_density=True, **kwargs
):
mov_lhs, mov_rhs = data.movable_index
fix_lhs, fix_rhs = data.fixed_connected_index
_, filler_lhs = data.movable_connected_index
filler_rhs = mov_node_pos.shape[0]
num_fillers = filler_rhs - filler_lhs
decrease_target_density = False
# FIXME: How can we dynamically set args.min_area_inc according to the congestion map?
# I believe there should be elegant ways to do that like computing local statistics...
if args.design_name == "ispd18_test10":
# This design is extremely congested in its center.
# Temporarily hard coded. This value may not be the best...
args.min_area_inc = 0.001
if route_cache.first_run:
# TODO: check args.target_density should be changed or not?
@ -500,6 +512,8 @@ def route_inflation(
route_cache.original_filler_area_total = torch.sum(torch.prod(filler_size, 1)).item()
route_cache.original_pin_rel_cpos = data.pin_rel_cpos.clone()
route_cache.original_target_density = copy.deepcopy(args.target_density)
route_cache.original_total_mov_area_without_filler = copy.deepcopy(data.__total_mov_area_without_filler__)
route_cache.original_init_density_map = data.init_density_map.clone()
ori_mov_node_size = route_cache.original_mov_node_size
ori_mov_node_size_real = route_cache.original_mov_node_size_real
@ -508,6 +522,7 @@ def route_inflation(
# 1) check remain space
last_mov_area = torch.prod(mov_node_size_real[mov_lhs:filler_lhs], 1)
last_mov_area_total = last_mov_area.sum().item()
last_filler_area_total = torch.sum(torch.prod(mov_node_size_real[filler_lhs:filler_rhs], 1)).item()
max_inc_area_total = min(0.1 * route_cache.whitespace_area, route_cache.placeable_area - last_mov_area_total) # TODO: tune
if max_inc_area_total <= 0:
logger.warning("No space to inflate. Terminate inflation.")
@ -521,6 +536,7 @@ def route_inflation(
**kwargs
)
grdb, routeforce, input_mat, cg_mapHV, _, _, route_gradmat, gr_metrics = output
numOvflNets, gr_wirelength, gr_numVias, gr_numShorts, rc_hor_mean, rc_ver_mean = gr_metrics
num_bin_x, num_bin_y = input_mat.shape[0], input_mat.shape[1]
unit_len_x, unit_len_y = routeforce.gcell_steps()
@ -534,6 +550,13 @@ def route_inflation(
inflate_mat: torch.Tensor = torch.stack((input_mat + 1, input_mat + 1)).contiguous().pow_(2)
else:
inflate_mat: torch.Tensor = (cg_mapHV + 1).permute(0, 2, 1).contiguous()
if dynamic_target_density and last_filler_area_total / last_mov_area_total > 1.1 and numOvflNets / data.num_nets > 0.04:
# filler area too large => low utilization, can adjust size more aggressively to reduce GR overflow
max_inc_area_total = route_cache.placeable_area - last_mov_area_total
logger.info("Low utilization detect, globally inflate...")
global_ratio = (rc_hor_mean + rc_ver_mean) / 2
inflate_mat *= global_ratio
decrease_target_density = True
inflate_mat.clamp_(min=1.0, max=2.0)
# NOTE: 1) If use_weighted_inflation == False, use max congestion as inflation ratio.
@ -556,6 +579,10 @@ def route_inflation(
inc_mov_area = expect_new_mov_area - last_mov_area
inc_mov_area_total = inc_mov_area.sum().item()
inc_area_scale = max_inc_area_total / inc_mov_area_total
if inc_mov_area_total <= 0:
logger.warning("Negative area increment %.4f. Early terminate cell inflation." % (inc_mov_area_total))
ps.use_cell_inflate = False # not inflation anymore
return gr_metrics, None, None
if inc_area_scale < 1:
logger.warning("Not enough space to inflate. Scale down.")
inc_mov_area *= inc_area_scale
@ -569,10 +596,10 @@ def route_inflation(
new_mov_node_size_real: torch.Tensor = mov_node_size_real.clone()
new_mov_node_size_real[mov_lhs:filler_lhs].mul_(this_mov_conn_inflate_ratio)
if inc_mov_area_total / last_mov_area_total < min_area_inc:
if inc_mov_area_total / last_mov_area_total < args.min_area_inc:
logger.warning(
"Too small relative area increment (%.4f < %.4f). Early terminate cell inflation." % (
inc_mov_area_total / last_mov_area_total, min_area_inc
inc_mov_area_total / last_mov_area_total, args.min_area_inc
))
ps.use_cell_inflate = False # not inflation anymore
return gr_metrics, None, None
@ -580,13 +607,13 @@ def route_inflation(
# 5) update total filler inflation ratio
new_mov_area_total = last_mov_area_total + inc_mov_area_total
new_filler_area_total = 0.0
last_filler_area_total = torch.sum(torch.prod(mov_node_size_real[filler_lhs:filler_rhs], 1)).item()
filler_scale = 0.0
if new_mov_area_total + last_filler_area_total > route_cache.target_area:
new_filler_area_total = max(route_cache.target_area - new_mov_area_total, 0)
if decrease_target_density and new_filler_area_total / route_cache.placeable_area > 0.2:
# remove some pre-inserted fillers / FloatMov nodes to decrease the target density
new_target_density = max(0.8, 0.85 * 1.0)
if decrease_target_density:
logger.info("Removing some pre-inserted fillers / FloatMov nodes to decrease the target density...")
# standard cell density: 1 / (1 + 1.1) = 0.4762
new_target_density = max(0.4762, 0.85 * args.target_density)
new_target_area = new_target_density * route_cache.placeable_area
new_filler_area_total = max(new_target_area - new_mov_area_total, 0)
filler_scale = new_filler_area_total / route_cache.original_filler_area_total
@ -594,11 +621,9 @@ def route_inflation(
num_remain_cells = filler_lhs + math.ceil(filler_scale * original_num_fillers)
route_cache.target_area = new_target_area
new_mov_node_size_real = new_mov_node_size_real[:num_remain_cells]
mov_node_size_real = mov_node_size_real[:num_remain_cells]
logger.warning("Remove nodes to reduce target density from %.4f to %.4f. \
This step may remove some FloatMov. #Nodes from %d to %d" %
(old_target_density, args.target_density, filler_rhs, num_remain_cells))
# set filler size as 0 to remove
new_mov_node_size_real[num_remain_cells:] = 0
mov_node_size_real[num_remain_cells:] = 0
elif route_cache.original_filler_area_total > 0:
filler_scale = math.sqrt(new_filler_area_total / route_cache.original_filler_area_total)
new_mov_node_size_real[filler_lhs:filler_rhs] = filler_scale * ori_mov_node_size_real[filler_lhs:filler_rhs]
@ -620,7 +645,19 @@ def route_inflation(
args.target_density = (new_mov_area_total + new_filler_area_total) / route_cache.placeable_area
logger.info("Update target density from %.4f to %.4f" % (old_target_density, args.target_density))
logger.info("Relative area | increment: %.4f, mov: %.4f, filler: %.4f, all_cells: %.4f" % (
if decrease_target_density:
# since we decrease target density, update the init density map correspondingly
logger.warning("Remove nodes to reduce target density from %.4f to %.4f. This step may remove some FloatMov. #Nodes from %d to %d" %
(old_target_density, args.target_density, filler_rhs, num_remain_cells))
data.init_density_map.clamp_(min=0.0, max=1.0).div_(old_target_density).mul_(args.target_density)
logger.info("Absolute area (last) | mov: %.4E, filler: %.4E, all_cells: %.4E" % (
last_mov_area_total, last_filler_area_total, last_mov_area_total + last_filler_area_total
))
logger.info("Absolute area (this) | mov: %.4E, filler: %.4E, all_cells: %.4E" % (
new_mov_area_total, new_filler_area_total, new_mov_area_total + new_filler_area_total
))
logger.info("Relative area change | increment: %.4f, mov: %.4f, filler: %.4f, all_cells: %.4f" % (
inc_mov_area_total / last_mov_area_total,
new_mov_area_total / last_mov_area_total,
new_filler_area_total / last_filler_area_total if last_filler_area_total > 1e-5 else 0,
@ -656,6 +693,11 @@ def route_inflation(
expand_ratio = mov_node_area / clamp_mov_node_area
mov_node_size = clamp_mov_node_size
if decrease_target_density:
# FIXME: should we update total_mov_area_without_filler when decrease_target_density == False?
mov_cell_area = torch.prod(new_mov_node_size_real[mov_lhs:mov_rhs, ...], 1)
data.__total_mov_area_without_filler__ = torch.sum(mov_cell_area).item()
ps.use_cell_inflate = True
return gr_metrics, mov_node_size, expand_ratio
@ -665,5 +707,8 @@ def route_inflation_roll_back(args, logger, data, mov_node_size):
mov_lhs, mov_rhs = data.movable_index
mov_node_size[mov_lhs:mov_rhs].copy_(route_cache.original_mov_node_size[mov_lhs:mov_rhs])
data.pin_rel_cpos.copy_(route_cache.original_pin_rel_cpos)
args.target_density = route_cache.original_target_density
data.__total_mov_area_without_filler__ = route_cache.original_total_mov_area_without_filler
if args.target_density != route_cache.original_target_density:
args.target_density = route_cache.original_target_density
data.init_density_map.copy_(route_cache.original_init_density_map)
route_cache.reset()

View File

@ -17,14 +17,16 @@ def load_dataset(args, logger, placement=None):
if args.load_from_raw:
logger.info("loading from original benchmark...")
rawdb, gpdb = parser.read(
params, verbose_log=False, lite_mode=True, random_place=False, num_threads=args.num_threads
params, verbose_log=args.verbose_cpp_log, log_level=args.cpp_log_level,
lite_mode=True, random_place=False, num_threads=args.num_threads
)
design_info = parser.preprocess_design_info(gpdb)
else:
logger.info("loading from pt benchmark...")
design_pt_path = "./data/cad/%s/%s.pt" % (args.dataset, args.design_name)
parser.load_params(
params, verbose_log=False, lite_mode=True, random_place=False, num_threads=args.num_threads
params, verbose_log=args.verbose_cpp_log, log_level=args.cpp_log_level,
lite_mode=True, random_place=False, num_threads=args.num_threads
)
design_info = torch.load(design_pt_path)
gpdb = None
@ -140,10 +142,11 @@ class PlaceData(object):
self.__site_height__ = site_info[1]
self.__row_height__ = site_info[1] # the same as site height
self.__ori_die_lx__ = die_info[0].item()
self.__ori_die_hx__ = die_info[1].item()
self.__ori_die_ly__ = die_info[2].item()
self.__ori_die_hy__ = die_info[3].item()
lx, hx, ly, hy = die_info.cpu().numpy()
self.__ori_die_lx__ = lx
self.__ori_die_hx__ = hx
self.__ori_die_ly__ = ly
self.__ori_die_hy__ = hy
self.__num_nodes__ = node_pos.shape[0]
self.__num_pins__ = pin_id2node_id.shape[0]
@ -489,7 +492,7 @@ class PlaceData(object):
def preshift(self):
# shift die info to (0.0, hx, 0.0, hy)
die_lx, _, die_ly, _ = self.die_info.tolist()
die_lx, _, die_ly, _ = self.die_info.cpu().numpy()
die_shift = torch.tensor(
[die_lx, die_ly], dtype=self.die_info.dtype, device=self.die_info.device,
)
@ -519,7 +522,7 @@ class PlaceData(object):
def prescale(self):
# scale die info to (0.0, 1.0, 0.0, 1.0)
die_lx, die_hx, die_ly, die_hy = self.die_info.tolist()
die_lx, die_hx, die_ly, die_hy = self.die_info.cpu().numpy()
die_scale = torch.tensor(
[die_hx - die_lx, die_hy - die_ly],
dtype=self.die_info.dtype,
@ -543,10 +546,11 @@ class PlaceData(object):
def pre_compute_var(self):
args = self.__args__
device = self.node_size.get_device()
dtype = self.node_size.dtype
# die related
lx, hx, ly, hy = self.die_info.tolist()
lx, hx, ly, hy = self.die_info.cpu().numpy()
self.unit_len = torch.tensor(
[(hx - lx) / self.num_bin_x, (hy - ly) / self.num_bin_y], device=device
[(hx - lx) / self.num_bin_x, (hy - ly) / self.num_bin_y], device=device, dtype=dtype
)
self.die_ur = self.die_info.reshape(2, 2).t()[1].clone()
self.die_ll = self.die_info.reshape(2, 2).t()[0].clone()
@ -742,7 +746,7 @@ class PlaceData(object):
num_fltiopin,
)
)
content += "Core Info " + str(self.die_info.tolist()) + "\n"
content += "Core Info " + str([i for i in self.die_info.cpu().numpy()]) + "\n"
content += "Site Width = %d, Row Height = %d\n" % (
self.site_width,
self.site_height,

View File

@ -145,14 +145,21 @@ def rearrange_dpdb_node_info(node_pos: torch.Tensor, data: PlaceData):
def setup_detailed_rawdb(
node_pos: torch.Tensor, use_cpu_db_: bool, data: PlaceData, args, logger
node_pos: torch.Tensor, use_cpu_db_: bool, data: PlaceData, args, logger, after_lg=True
):
curr_site_width = 1.0 # prescale_by_site_width
node_lpos, node_size, node_weight, pin_id2node_id, node2pin_list, node2pin_list_end = rearrange_dpdb_node_info(
node_pos, data
)
if after_lg:
# NOTE: we assume all legalized cells are on integer system
# this step can avoid some potential floating-point precision errors
_, floatmov_rhs, _ = data.node_type_indices[1]
inv_scalar = round(1.0 / get_ori_scale_factor(data))
node_lpos[:floatmov_rhs].mul_(inv_scalar).round_().div_(inv_scalar)
mov_lhs, mov_rhs = data.movable_index
conn_mov_lhs, conn_mov_rhs = data.movable_connected_index
if args.scale_design:
# scale back
die_scale = data.die_scale / data.site_width # assume site width == 1 in dp
@ -179,12 +186,9 @@ def setup_detailed_rawdb(
num_iopin = iopin_rhs - fix_rhs
num_floatiopin = floatiopin_rhs - blkg_rhs
die_info = die_info.cpu()
xl = die_info[0].item()
xh = die_info[1].item()
yl = die_info[2].item()
yh = die_info[3].item()
xl, xh, yl, yh = die_info.cpu().numpy()
num_movable_nodes = mov_rhs - mov_lhs
num_conn_movable_nodes = conn_mov_rhs - conn_mov_lhs
num_nodes = node_lpos.shape[0] - num_iopin - num_floatiopin
site_width = curr_site_width
row_height = data.row_height / data.site_width
@ -213,6 +217,7 @@ def setup_detailed_rawdb(
xh,
yl,
yh,
num_conn_movable_nodes,
num_movable_nodes,
num_nodes,
site_width,
@ -238,6 +243,7 @@ def setup_detailed_rawdb(
xh,
yl,
yh,
num_conn_movable_nodes,
num_movable_nodes,
num_nodes,
site_width,
@ -296,7 +302,7 @@ def commit_to_node_pos(node_pos: torch.Tensor, data:PlaceData, dp_rawdb):
def run_lg(node_pos: torch.Tensor, data: PlaceData, args, logger):
# CPU legalization
lg_rawdb = setup_detailed_rawdb(node_pos, True, data, args, logger)
lg_rawdb = setup_detailed_rawdb(node_pos, True, data, args, logger, after_lg=False)
# run LG
logger.info("Start running Macro Legalization...")
@ -306,13 +312,38 @@ def run_lg(node_pos: torch.Tensor, data: PlaceData, args, logger):
lg_rawdb.commit()
logger.info("Finish Macro Legalization. Time: %.4f" % (time.time() - ml_time))
total_cell_area = torch.sum(torch.prod(data.node_size, 1)).item()
die_area = torch.prod(data.die_ur - data.die_ll).item()
is_high_util = (total_cell_area / die_area) > 0.999
logger.info("Utilization: %.2f" % (total_cell_area / die_area))
logger.info("Start running Greedy Legalization...")
gl_time = time.time()
num_bins_x, num_bins_y = 1, 64
gpudp.greedyLegalization(lg_rawdb, num_bins_x, num_bins_y)
if not lg_rawdb.check(get_ori_scale_factor(data)):
logger.error("Check failed in Greedy Legalization")
logger.info("Finish Greedy Legalization. Time: %.4f" % (time.time() - gl_time))
if not is_high_util:
gpudp.greedyLegalization(lg_rawdb, num_bins_x, num_bins_y, True)
if is_high_util or not lg_rawdb.check(get_ori_scale_factor(data)):
logger.warning("Check failed in Greedy Legalization. Re-try by Greedy + Filler Legalization.")
# NOTE: this greedy legalization only legalizes movable connected cells
logger.info("Start running Greedy Legalization...")
gpudp.greedyLegalization(lg_rawdb, num_bins_x, num_bins_y, False)
# NOTE: filler legalization only legalizes movable unconnected cells (fillers)
logger.info("Start Filler Legalization...")
gpudp.fillerLegalization(lg_rawdb)
if not lg_rawdb.check(get_ori_scale_factor(data)):
logger.error("Check failed in Greedy + Filler Legalization.")
logger.info("Finish Greedy + Filler Legalization. Time: %.4f" % (time.time() - gl_time))
else:
logger.info("Finish Greedy Legalization. Time: %.4f" % (time.time() - gl_time))
# # Commit result
# commit_to_node_pos(node_pos, data, lg_rawdb)
# torch.cuda.synchronize(node_pos.device)
# if args.scale_design:
# node_pos /= data.die_scale
# info = (-1, 0, data.design_name)
# draw_fig_with_cairo_cpp(node_pos, data.node_size, data, info, args, base_size=4096)
logger.info("Start running Abacus Legalization...")
al_time = time.time()
@ -380,14 +411,16 @@ def run_dp(node_pos: torch.Tensor, data: PlaceData, args, logger):
ism_iter = 50
# use integer coordinate systems in DP for better quality
scalar = compute_scalar(get_ori_scale_factor(data))
# scalar = compute_scalar(get_ori_scale_factor(data))
scalar = 1.0
def dp_handler(dp_func, func_name, *func_args):
logger.info("Start running %s..." % func_name)
start_time = time.time()
if scalar != 1.0:
logger.info("scale dp_rawdb by %g" % (1.0 / scalar))
dp_rawdb.scale(1.0 / scalar, True)
# NOTE: we assume site_width is integer, so 1 / scalar should be an integer
logger.info("scale dp_rawdb by %g" % round(1.0 / scalar))
dp_rawdb.scale(round(1.0 / scalar), True)
dp_func(dp_rawdb, *func_args)
if scalar != 1.0:
logger.info("scale dp_rawdb back by %g" % scalar)
@ -431,6 +464,11 @@ def run_dp_route_opt(node_pos: torch.Tensor, gpdb, rawdb, ps, data: PlaceData, a
node_pos[:mov_rhs].detach() - data.node_size[:mov_rhs] / 2,
data.node_lpos[mov_rhs:],
), dim=0).cpu()
# this step can avoid some potential floating-point precision errors
_, floatmov_rhs, _ = data.node_type_indices[1]
inv_scalar = round(1.0 / get_ori_scale_factor(data))
node_lpos[:floatmov_rhs].mul_(inv_scalar).round_().div_(inv_scalar)
node_size = data.node_size.cpu()
if args.scale_design:
# scale back
@ -441,10 +479,7 @@ def run_dp_route_opt(node_pos: torch.Tensor, gpdb, rawdb, ps, data: PlaceData, a
site_width = 1.0
row_height = data.row_height / data.site_width
die_info = data.die_info.cpu()
dieLX = die_info[0].item()
dieHX = die_info[1].item()
dieLY = die_info[2].item()
dieHY = die_info[3].item()
dieLX, dieHX, dieLY, dieHY = die_info.numpy()
K = 5
new_node_lpos = routedp.dp_route_opt(
node_lpos, node_size, dieLX, dieHX, dieLY, dieHY,

View File

@ -54,6 +54,24 @@ def get_init_density_map(rawdb, gpdb, data: PlaceData, args, logger):
args.deterministic
)
init_density_map += snet_density_map.contiguous()
# compute utilization to determine whether there are sufficient space to enlarge snet density
mov_lhs, mov_rhs = data.movable_index
mov_node_size = data.node_size[mov_lhs:mov_rhs, ...]
total_mov_cell_area = torch.sum(torch.prod(mov_node_size, 1)).item()
die_area = torch.prod(data.die_ur - data.die_ll)
fixed_node_area = init_density_map.clamp_(min=0.0, max=1.0).sum() * data.bin_area
placeable_area = die_area - fixed_node_area
total_filler_area = max(args.target_density * placeable_area - total_mov_cell_area, 0.0)
if total_filler_area / total_mov_cell_area > 2:
# low utilization, can enlarge snet density to further enhance routability
tmp_map = init_density_map + (snet_density_map.contiguous() > 0.1).float()
diff_area = (tmp_map.clamp_(min=0.0, max=1.0).sum() * data.bin_area - fixed_node_area).item()
if diff_area < total_filler_area * 0.5:
logger.info("Low utilization detect, enlarge snet density.")
init_density_map += (snet_density_map.contiguous() > 0.1).float()
init_density_map.clamp_(min=0.0, max=1.0)
init_density_map.clamp_(min=0.0, max=1.0).mul_(args.target_density)
if args.use_route_force or args.use_cell_inflate:
# inflate connected IOPins

View File

@ -114,6 +114,8 @@ class ParamScheduler:
self.stop_overflow = args.stop_overflow
self.skip_update = False if args.enable_skip_update else None
self.enable_fence = data.enable_fence
self.min_enlarge_density_interval = 1000
self.last_enlarge_density_iter = -self.min_enlarge_density_interval
# skip density force
self.enable_sample_force = True
self.force_ratio = 0.0
@ -202,7 +204,7 @@ class ParamScheduler:
# self.skip_update = np.random.random() > (np.random.randn() * 0.08 + 0.4)
# if self.density_weight > 0.1 or self.recorder.overflow[-1] < 0.2: # 2021 11 13 best
# self.skip_update = np.random.random() > 0.4
if self.weighted_weight > 0.5 and self.weighted_weight < 0.99:
if self.weighted_weight > 0.5 and self.weighted_weight < 0.95:
self.skip_update = ((self.iter - self.init_iter) % 3 != 0)
elif self.iter - self.init_iter < 50:
# slow down the param update of early stage
@ -230,6 +232,19 @@ class ParamScheduler:
self.mu = 1.05 * np.clip(np.power(1.05, -delta_hpwl / 350000), 0.95, 1.05)
self.density_weight *= self.mu
if (
not self.enable_fence and
self.iter > 15 and
self.iter - self.last_enlarge_density_iter > self.min_enlarge_density_interval and
self.check_plateau(self.recorder.overflow, window=25, threshold=0.001)
):
if self.recorder.overflow[-1] > 0.9:
self.last_enlarge_density_iter = self.iter
self.density_weight *= 2
self.__logger__.warning(
"Detect plateau at early stage, enlarge density_weight. Iter: %d" %
self.iter)
def param_smooth_func(self, input, r=0.2, half_iter=30, end_iter=400):
logistic = lambda x,k,x_0: 1 / (1 + math.exp(-k * (x - x_0)))
lhs = 1 - logistic(input, r, end_iter - half_iter)

View File

@ -137,6 +137,7 @@ def run_placement_main_nesterov(args, logger):
# p.step()
# exit(0)
terminate_signal = False
route_early_terminate_signal = False
for iteration in range(args.inner_iter):
# optimizer.zero_grad() # zero grad inside obj_and_grad_fn
obj = optimizer.step(obj_and_grad_fn)
@ -189,7 +190,9 @@ def run_placement_main_nesterov(args, logger):
constraint_fn=trunc_node_pos_fn,
) # ps.use_cell_inflate is updated in route_inflation
if not ps.use_cell_inflate:
logger.info("Stop cell inflation...")
route_early_terminate_signal = True
terminate_signal = True
logger.info("Early stop cell inflation...")
if output is not None:
gr_metrics, new_mov_node_size, new_expand_ratio = output
ps.push_gr_sol(gr_metrics, hpwl, overflow, mov_node_pos)
@ -240,7 +243,7 @@ def run_placement_main_nesterov(args, logger):
)
ps.reset_best_sol()
if iteration % args.log_freq == 0 or iteration == args.inner_iter - 1 or ps.rerun_route:
if iteration % args.log_freq == 0 or iteration == args.inner_iter - 1 or ps.rerun_route or terminate_signal:
log_str = (
"iter: %d | masked_hpwl: %.2E overflow: %.4f obj: %.4E "
"density_weight: %.4E wa_coeff: %.4E"
@ -287,13 +290,14 @@ def run_placement_main_nesterov(args, logger):
mov_node_pos[mov_lhs:mov_rhs].data.copy_(best_sol[mov_lhs:mov_rhs])
if ps.enable_route:
route_inflation_roll_back(args, logger, data, mov_node_size)
ps.rerun_route = True
gr_metrics = run_gr_and_fft_main(
args, logger, data, rawdb, gpdb, ps, mov_node_pos, constraint_fn=trunc_node_pos_fn,
skip_m1_route=True, report_gr_metrics_only=True
)
ps.rerun_route = False
ps.push_gr_sol(gr_metrics, hpwl, overflow, mov_node_pos)
if not route_early_terminate_signal:
ps.rerun_route = True
gr_metrics = run_gr_and_fft_main(
args, logger, data, rawdb, gpdb, ps, mov_node_pos, constraint_fn=trunc_node_pos_fn,
skip_m1_route=True, report_gr_metrics_only=True
)
ps.rerun_route = False
ps.push_gr_sol(gr_metrics, hpwl, overflow, mov_node_pos)
best_sol_gr = ps.get_best_gr_sol()
mov_node_pos[mov_lhs:mov_rhs].data.copy_(best_sol_gr[mov_lhs:mov_rhs])

View File

@ -41,8 +41,8 @@ for idx, (lhs, rhs) in enumerate(design_lines):
if "End route_design" in line:
DrCpuTimeStr = line.split("total cpu=")[-1].split(", ")[0]
DrTotalTimeStr = line.split("real=")[-1].split(", ")[0]
DrCpuTime = sum(x * int(t) for x, t in zip([3600, 60, 1], DrCpuTimeStr.split(":"))) # seconds
DrTotalTime = sum(x * int(t) for x, t in zip([3600, 60, 1], DrTotalTimeStr.split(":"))) # seconds
DrCpuTime = sum(x * int(float(t)) for x, t in zip([3600, 60, 1], DrCpuTimeStr.split(":"))) # seconds
DrTotalTime = sum(x * int(float(t)) for x, t in zip([3600, 60, 1], DrTotalTimeStr.split(":"))) # seconds
if line.startswith("Wire Length Statistics :"):
findTotalWL = True

View File

@ -6,9 +6,11 @@ def find_benchmark(dataset_root, benchmark):
"ispd2005": os.path.join(dataset_root, "ispd2005"),
"dac2012": os.path.join(dataset_root, "iccad2012dac2012"),
"ispd2015": os.path.join(dataset_root, "ispd2015"),
"ispd2015_without_fence": os.path.join(dataset_root, "ispd2015_without_fence"),
"ispd2015_fix": os.path.join(dataset_root, "ispd2015_fix"),
"ispd2018": os.path.join(dataset_root, "ispd2018"),
"ispd2019_no_fence": os.path.join(dataset_root, "ispd2019_no_fence"),
"iccad2019": os.path.join(dataset_root, "iccad2019"),
"ispd2018": os.path.join(dataset_root, "ispd2018"),
}
root = bm_to_root[benchmark]
all_designs = [i for i in os.listdir(root) if os.path.isdir(os.path.join(root, i))]
@ -22,12 +24,16 @@ def get_single_design_params(dataset_root, benchmark, design_name, placement=Non
return single_dac2012(dataset_root, design_name, placement)
elif benchmark == "ispd2015":
return single_ispd2015(dataset_root, design_name, placement)
elif benchmark == "ispd2015_without_fence":
return single_ispd2015_without_fence(dataset_root, design_name, placement)
elif benchmark == "ispd2015_fix":
return single_ispd2015_fix(dataset_root, design_name, placement)
elif benchmark == "ispd2018":
return single_ispd2018(dataset_root, design_name, placement)
elif benchmark == "ispd2019_no_fence":
return single_ispd2019_no_fence(dataset_root, design_name, placement)
elif benchmark == "iccad2019":
return single_iccad2019(dataset_root, design_name, placement)
elif benchmark == "ispd2018":
return single_ispd2018(dataset_root, design_name, placement)
else:
raise NotImplementedError("benchmark %s is not found" % benchmark)
@ -87,22 +93,6 @@ def single_ispd2015(dataset_root, design_name, placement=None):
return params
def single_ispd2015_without_fence(dataset_root, design_name, placement=None):
# configuration
benchmark = "ispd2015_without_fence"
root, all_designs = find_benchmark(dataset_root, benchmark)
if design_name not in all_designs:
raise ValueError("Design Name %s should in %s" % (design_name, all_designs))
params = {
"benchmark": benchmark,
"tech_lef": "%s/%s/tech.lef" % (root, design_name),
"cell_lef": "%s/%s/cells.lef" % (root, design_name),
"def": "%s/%s/floorplan.def" % (root, design_name) if placement is None else placement,
"design_name": design_name,
}
return params
def single_ispd2015_fix(dataset_root, design_name, placement=None):
# configuration
benchmark = "ispd2015_fix"
@ -118,6 +108,36 @@ def single_ispd2015_fix(dataset_root, design_name, placement=None):
return params
def single_ispd2018(dataset_root, design_name, placement=None):
# configuration
benchmark = "ispd2018"
root, all_designs = find_benchmark(dataset_root, benchmark)
if design_name not in all_designs:
raise ValueError("Design Name %s should in %s" % (design_name, all_designs))
params = {
"benchmark": benchmark,
"lef": "%s/%s/%s.input.lef" % (root, design_name, design_name),
"def": "%s/%s/%s.input.def" % (root, design_name, design_name) if placement is None else placement,
"design_name": design_name,
}
return params
def single_ispd2019_no_fence(dataset_root, design_name, placement=None):
# configuration
benchmark = "ispd2019_no_fence"
root, all_designs = find_benchmark(dataset_root, benchmark)
if design_name not in all_designs:
raise ValueError("Design Name %s should in %s" % (design_name, all_designs))
params = {
"benchmark": benchmark,
"lef": "%s/%s/%s.input.lef" % (root, design_name, design_name),
"def": "%s/%s/%s.input.def" % (root, design_name, design_name) if placement is None else placement,
"design_name": design_name,
}
return params
def single_iccad2019(dataset_root, design_name, placement=None):
# configuration
benchmark = "iccad2019"
@ -133,15 +153,35 @@ def single_iccad2019(dataset_root, design_name, placement=None):
return params
def single_ispd2018(dataset_root, design_name, placement=None):
# configuration
benchmark = "ispd2018"
root, all_designs = find_benchmark(dataset_root, benchmark)
if design_name not in all_designs:
raise ValueError("Design Name %s should in %s" % (design_name, all_designs))
params = {
"benchmark": benchmark,
"lef": "%s/%s/%s.input.lef" % (root, design_name, design_name),
"def": "%s/%s/%s.input.def" % (root, design_name, design_name)
if placement is None
else placement,
"design_name": design_name,
}
return params
def get_custom_design_params(args):
params = dict([
[item.strip() for item in token.strip().split(":")]
for token in args.custom_path.split(",") if len(token) > 0
])
params = dict(
[
[item.strip() for item in token.strip().split(":")]
for token in args.custom_path.split(",")
if len(token) > 0
]
)
if "benchmark" not in params.keys():
raise ValueError("Cannot find 'benchmark' in args.custom_path")
if "design_name" not in params.keys():
raise ValueError("Cannot find 'design_name' in args.custom_path")
args.dataset = params["benchmark"]
args.design_name = params["design_name"]
return params
return params

View File

@ -14,11 +14,12 @@ class IOParser(object):
self,
params: dict,
verbose_log: bool = False,
log_level: int = 2,
lite_mode: bool = False,
random_place: bool = True,
num_threads: int = 8,
):
check_status = self.check_params(params, verbose_log, lite_mode, random_place, num_threads)
check_status = self.check_params(params, verbose_log, log_level, lite_mode, random_place, num_threads)
if not check_status:
raise ValueError(
"Checking failure. Please check the validity of params: %s" % params
@ -31,7 +32,7 @@ class IOParser(object):
return load_status
def check_params(
self, params: dict, verbose_log: bool, lite_mode: bool, random_place: bool, num_threads: int = 8
self, params: dict, verbose_log: bool, log_level: int, lite_mode: bool, random_place: bool, num_threads: int = 8
) -> bool:
if "def" not in params.keys() and "aux" not in params.keys():
print("def or aux is not found!")
@ -82,6 +83,14 @@ class IOParser(object):
else:
self.params["verbose_parser_log"] = False
# === LOG_LEVEL ===
# 0: DEBUG, 1: VERBOSE, 2: INFO, 3: NOTICE, 4: WARN, 5: ERROR, 6: FATAL, 7: OK
if log_level < 0:
log_level = 0
elif log_level > 7:
log_level = 2
self.params["global_log_level"] = log_level
if lite_mode:
self.params["lite_mode"] = True
else:
@ -100,12 +109,13 @@ class IOParser(object):
self,
params: dict,
verbose_log: bool = False,
log_level: int = 2,
lite_mode: bool = False,
random_place: bool = True,
num_threads: int = 8,
debug: bool = False,
):
check_status = self.check_params(params, verbose_log, lite_mode, random_place, num_threads)
check_status = self.check_params(params, verbose_log, log_level, lite_mode, random_place, num_threads)
if not check_status:
raise ValueError(
"Checking failure. Please check the validity of params: %s" % params