#include #include "common/common.h" #include "common/db/Database.h" #include "flute.h" #include "io_parser/gp/GPDatabase.h" namespace routedp { inline int floorDiv(float a, float b, float rtol = 1e-4) { return std::floor((a + rtol * b) / b); } inline int ceilDiv(float a, float b, float rtol = 1e-4) { return std::ceil((a - rtol * b) / b); } inline int roundDiv(float a, float b) { return std::round(a / b); } torch::Tensor dp_route_opt(torch::Tensor node_lpos_init_, torch::Tensor node_size_, float dieLX, float dieHX, float dieLY, float dieHY, float site_width, float row_height, std::shared_ptr rawdb_, std::shared_ptr gpdb_, int K) { // We found that placing cells under M2 SNet will easily cause DRVs // this function will shift cells outside the SNet within an acceptable range db::Database& rawdb = *rawdb_; gp::GPDatabase& gpdb = *gpdb_; torch::Tensor at_node_size_x = node_size_.index({"...", 0}).clone().contiguous(); torch::Tensor at_node_size_y = node_size_.index({"...", 1}).clone().contiguous(); torch::Tensor at_init_x = node_lpos_init_.index({"...", 0}).clone().contiguous(); torch::Tensor at_init_y = node_lpos_init_.index({"...", 1}).clone().contiguous(); torch::Tensor at_x = node_lpos_init_.index({"...", 0}).clone().contiguous(); torch::Tensor at_y = node_lpos_init_.index({"...", 1}).clone().contiguous(); float* node_size_x = at_node_size_x.data_ptr(); float* node_size_y = at_node_size_y.data_ptr(); float* init_x = at_init_x.data_ptr(); float* init_y = at_init_y.data_ptr(); float* x = at_x.data_ptr(); float* y = at_y.data_ptr(); int num_bin_x = 1; // row-based int nLayers = rawdb.getNumRLayers(); float bin_size_x = (dieHX - dieLX) / num_bin_x; float bin_size_y = row_height; // row height int num_bin_y = ceilDiv(dieHY - dieLY, bin_size_y); std::vector>> bin2snets(nLayers * num_bin_x * num_bin_y); for (int snetId = 0; snetId < rawdb.snets.size(); snetId++) { db::SNet* snet = rawdb.snets[snetId]; for (size_t shapeIdx = 0; shapeIdx < snet->shapes.size(); shapeIdx++) { auto& shape = snet->shapes[shapeIdx]; int currLayer = shape.layer.rIndex; int bin_id_lx = std::max(((float)shape.lx / rawdb.siteW - dieLX) / bin_size_x, (float)0); int bin_id_hx = std::min((int)ceil(((float)shape.hx / rawdb.siteW - dieLX) / bin_size_x), num_bin_x); int bin_id_ly = std::max(((float)shape.ly / rawdb.siteW - dieLY) / bin_size_y, (float)0); int bin_id_hy = std::min((int)ceil(((float)shape.hy / rawdb.siteW - dieLY) / bin_size_y), num_bin_y); for (int bin_id_x = bin_id_lx; bin_id_x < bin_id_hx; bin_id_x++) { for (int bin_id_y = bin_id_ly; bin_id_y < bin_id_hy; bin_id_y++) { int bin_id = currLayer * num_bin_x * num_bin_y + bin_id_x * num_bin_y + bin_id_y; bin2snets[bin_id].emplace_back(snetId, shapeIdx); } } } } for (int bin_id = 0; bin_id < bin2snets.size(); bin_id++) { if (bin2snets[bin_id].size() == 0) continue; std::stable_sort( bin2snets[bin_id].begin(), bin2snets[bin_id].end(), [&](std::pair p1, std::pair p2) { int id1 = p1.first; int id2 = p2.first; db::SNet* snet1 = rawdb.snets[id1]; db::SNet* snet2 = rawdb.snets[id2]; int shapeIdx1 = p1.second; int shapeIdx2 = p2.second; db::Geometry& shape1 = snet1->shapes[shapeIdx1]; db::Geometry& shape2 = snet2->shapes[shapeIdx2]; float x1 = shape1.lx; float x2 = shape2.lx; float xx1 = shape1.hx; float xx2 = shape2.hx; return x1 < x2 || (x1 == x2 && (xx1 > xx2 || (xx1 == xx2 && id1 < id2))); }); } std::vector> bin2cells(num_bin_x * num_bin_y); // add nodes for (int i = 0; i < gpdb.getNodes().size(); i++) { if (gpdb.getNodes()[i].getNodeType() == "IOPin" || gpdb.getNodes()[i].getNodeType() == "FloatIOPin") { continue; } if (std::round(node_size_y[i] / bin_size_y) <= 1) { int bin_id_x = (x[i] + node_size_x[i] / 2 - dieLX) / bin_size_x; int bin_id_y = (y[i] + node_size_y[i] / 2 - dieLY) / bin_size_y; bin_id_x = std::min(std::max(bin_id_x, 0), num_bin_x - 1); bin_id_y = std::min(std::max(bin_id_y, 0), num_bin_y - 1); int bin_id = bin_id_x * num_bin_y + bin_id_y; bin2cells[bin_id].emplace_back(i); } else { int bin_id_lx = std::max((x[i] - dieLX) / bin_size_x, (float)0); int bin_id_hx = std::min((int)ceil((x[i] + node_size_x[i] - dieLX) / bin_size_x), num_bin_x); int bin_id_ly = std::max((y[i] - dieLY) / bin_size_y, (float)0); int bin_id_hy = std::min((int)ceil((y[i] + node_size_y[i] - dieLY) / bin_size_y), num_bin_y); for (int bin_id_x = bin_id_lx; bin_id_x < bin_id_hx; bin_id_x++) { for (int bin_id_y = bin_id_ly; bin_id_y < bin_id_hy; bin_id_y++) { int bin_id = bin_id_x * num_bin_y + bin_id_y; bin2cells[bin_id].emplace_back(i); } } } } std::vector cellIsMove(gpdb.getNodes().size(), false); for (int i = 0; i < gpdb.getNodes().size(); i++) { auto node_type = gpdb.getNodes()[i].getNodeType(); if (node_type == "Mov" || node_type == "FloatMov") { cellIsMove[i] = true; } } float totalDisplace = 0.0; int totalNumMoves = 0; for (int bin_id = 0; bin_id < bin2cells.size(); bin_id++) { auto& currBin2cellsTmp = bin2cells[bin_id]; if (currBin2cellsTmp.size() == 0) { continue; } float rowDisplace = 0.0; int rowNumMoves = 0; int bin_id_x = bin_id / num_bin_y; int bin_id_y = bin_id % num_bin_y; int m2BinId = 1 * num_bin_x * num_bin_y + bin_id_x * num_bin_y + bin_id_y; auto& currM2Snets = bin2snets[m2BinId]; if (currM2Snets.size() == 0) { continue; } // sort all nodes by row std::sort(currBin2cellsTmp.begin(), currBin2cellsTmp.end(), [&](int i, int j) { float x1 = x[i]; float x2 = x[j]; float w1 = node_size_x[i]; float w2 = node_size_x[j]; return x1 < x2 || (x1 == x2 && (w1 > w2 || (w1 == w2 && i < j))); }); // remove fixed cell overlap in row bool errorFlag = false; std::vector> currBin2cells; // nodeId, lx, hx for (int i = 0; i < currBin2cellsTmp.size(); i++) { int this_id = currBin2cellsTmp[i]; float this_lx = x[this_id]; float this_hx = x[this_id] + node_size_x[this_id]; if (currBin2cells.size() == 0) { currBin2cells.emplace_back(this_id, this_lx, this_hx); continue; } else { auto [last_id, last_lx, last_hx] = currBin2cells.back(); if (std::max(this_lx, last_lx) < std::min(this_hx, last_hx)) { if (cellIsMove[this_id] || cellIsMove[last_id]) { // one of movable cells overlap currBin2cells.emplace_back(this_id, this_lx, this_hx); logger.error("Node %d (%.1f, %.1f) overlap with Node %d (%.1f, %.1f)", this_id, this_lx, this_hx, last_id, last_lx, last_hx); errorFlag = true; } else { // two fixed cells overlap, merge them and mark it as true float new_lx = std::min(this_lx, last_lx); float new_hx = std::max(this_hx, last_hx); currBin2cells[currBin2cells.size() - 1] = {last_id, new_lx, new_hx}; } } else { currBin2cells.emplace_back(this_id, this_lx, this_hx); } } } if (errorFlag) continue; float binLx = std::max(bin_id_x * bin_size_x, (float)0); float binHx = std::min((bin_id_x + 1) * bin_size_x, (float)dieHX); float binLy = std::max(bin_id_y * bin_size_y, (float)0); float binHy = std::min((bin_id_y + 1) * bin_size_y, (float)dieHY); int cellPtr = 0; int snetPtr = 0; while (cellPtr != currBin2cells.size() && snetPtr != currM2Snets.size()) { if (!cellIsMove[std::get<0>(currBin2cells[cellPtr])]) { cellPtr++; continue; } db::SNet* snet = rawdb.snets[currM2Snets[snetPtr].first]; int shapeIdx = currM2Snets[snetPtr].second; db::Geometry& shape = snet->shapes[shapeIdx]; auto [node_id, node_lx, node_hx] = currBin2cells[cellPtr]; float snetLx = (float)shape.lx / rawdb.siteW; float snetHx = (float)shape.hx / rawdb.siteW; if (std::max(node_lx, snetLx) < std::min(node_hx, snetHx)) { // overlap with snet std::vector> movesL; // cellId, x, currBin2cellsId std::vector> movesR; // cellId, x, currBin2cellsId // when more than one cells are located in the same SNet stripe, two cases: // (1) move the first cell to left // (2) move the all cells to right // Move left: int cellPtrL = cellPtr; float src_width_l = ceilDiv(node_hx - node_lx, site_width) * site_width; // record the position of cellPtrL + ptrOffsetL + 1 float lhsX = std::max(floorDiv(snetLx - dieLX, site_width) * site_width + dieLX, dieLX); float blank_width_l = 0; int ptrOffsetL = 0; bool doMoveL = true; float displaceL = dieHX; doMoveL = doMoveL && (snetLx > dieLX); if (doMoveL && blank_width_l < src_width_l) { for (ptrOffsetL = -1; ptrOffsetL >= -K; ptrOffsetL--) { int targetPtr = cellPtrL + ptrOffsetL; if (targetPtr >= 0) { auto [node_id1, node_lx1, node_hx1] = currBin2cells[targetPtr]; float blank = std::max(lhsX - (ceilDiv(node_hx1 - dieLX, site_width) * site_width + dieLX), (float)0); blank_width_l += blank; if (blank_width_l >= src_width_l) { // src_width_l, blank_width_l, blank are both integer multiple of site_width lhsX = lhsX - (src_width_l - blank_width_l + blank); break; } lhsX = std::max(floorDiv(node_lx1 - dieLX, site_width) * site_width + dieLX, dieLX); if (!cellIsMove[node_id1]) { break; } } else { float blank = std::max(floorDiv(lhsX - dieLX, site_width) * site_width, (float)0); blank_width_l += blank; if (blank_width_l >= src_width_l) { lhsX = lhsX - (src_width_l - blank_width_l + blank); break; } lhsX = dieLX; break; } } } doMoveL = doMoveL && (blank_width_l >= src_width_l); if (doMoveL) { // compute cell movement and displacement // shift cell to the lhs of snet polygon float lhsX_copy = lhsX; displaceL = 0.0; for (int targetPtr = cellPtrL + ptrOffsetL + 1; targetPtr <= cellPtrL; targetPtr++) { auto [node_id1, node_lx1, node_hx1] = currBin2cells[targetPtr]; movesL.emplace_back(node_id1, lhsX_copy, targetPtr); displaceL += std::abs(init_x[node_id1] - lhsX_copy); lhsX_copy = ceilDiv(node_hx1 - dieLX, site_width) * site_width + dieLX; } } // Move right: // find all cells overlap with current SNet int cellPtrR = cellPtr; int node_id1; float node_lx1, node_hx1; std::tie(node_id1, node_lx1, node_hx1) = currBin2cells[cellPtrR]; bool doMoveR = true; float displaceR = dieHX; while (std::max(node_lx1, snetLx) < std::min(node_hx1, snetHx)) { cellPtrR++; if (cellPtrR == currBin2cells.size()) break; std::tie(node_id1, node_lx1, node_hx1) = currBin2cells[cellPtrR]; if (!cellIsMove[node_id1]) { doMoveR = false; break; } } cellPtrR--; float src_width_r = 0; for (int i = cellPtrL; i <= cellPtrR; i++) { auto [node_id1, node_lx1, node_hx1] = currBin2cells[i]; src_width_r += ceilDiv(node_hx1 - node_lx1, site_width) * site_width; } float rhsX = std::min(ceilDiv(snetHx - dieLX, site_width) * site_width + dieLX, dieHX); float blank_width_r = 0; int ptrOffsetR = 0; doMoveR = doMoveR && (snetHx < dieHX); if (doMoveR && blank_width_r < src_width_r) { for (ptrOffsetR = 1; ptrOffsetR <= K; ptrOffsetR++) { int targetPtr = cellPtrR + ptrOffsetR; if (targetPtr < currBin2cells.size()) { auto [node_id1, node_lx1, node_hx1] = currBin2cells[targetPtr]; float blank = std::max((floorDiv(node_lx1 - dieLX, site_width) * site_width + dieLX) - rhsX, (float)0); blank_width_r += blank; if (blank_width_r >= src_width_r) { // src_width_r, blank_width_r, blank are both integer multiple of site_width rhsX = rhsX + (src_width_r - blank_width_r + blank); break; } rhsX = std::min(ceilDiv(node_hx1 - dieLX, site_width) * site_width + dieLX, dieHX); if (!cellIsMove[node_id1]) { break; } } else { float blank = std::max(floorDiv(dieHX - rhsX, site_width) * site_width, (float)0); blank_width_r += blank; if (blank_width_r >= src_width_r) { rhsX = rhsX + (src_width_r - blank_width_r + blank); break; } rhsX = dieHX; break; } } } doMoveR = doMoveR && (blank_width_r >= src_width_r); if (doMoveR) { // compute cell movement and displacement // shift cells to the rhs of snet polygon float rhsX_copy = rhsX; displaceR = 0.0; for (int targetPtr = cellPtrR + ptrOffsetR - 1; targetPtr >= cellPtrL; targetPtr--) { auto [node_id1, node_lx1, node_hx1] = currBin2cells[targetPtr]; float thisLhs = rhsX_copy - ceilDiv(node_hx1 - node_lx1, site_width) * site_width; movesR.emplace_back(node_id1, thisLhs, targetPtr); displaceR += std::abs(init_x[node_id1] - thisLhs); rhsX_copy = thisLhs; } } // do move and update currBin2cells by the new lx and new hx float maxDis = site_width * 50 + 1e-4; // max displacement if ((doMoveL || doMoveR) && (std::round(displaceL) < maxDis || std::round(displaceR) < maxDis)) { if (std::round(displaceL) <= std::round(displaceR)) { for (auto [node_id1, targetX, targetPtr] : movesL) { currBin2cells[targetPtr] = {node_id1, targetX, targetX + node_size_x[node_id1]}; x[node_id1] = targetX; } } else { for (auto [node_id1, targetX, targetPtr] : movesR) { currBin2cells[targetPtr] = {node_id1, targetX, targetX + node_size_x[node_id1]}; x[node_id1] = targetX; } } logger.debug("SNetMove Node: %d PtrOffsetL: %d DisplaceL %.1f PtrOffsetR: %d DisplaceR %.1f", node_id, ptrOffsetL, displaceL, ptrOffsetR, displaceR); } cellPtr++; } else if (node_hx <= snetLx) { cellPtr++; } else if (node_lx >= snetHx) { snetPtr++; } else { cellPtr++; } } for (auto node_id : currBin2cellsTmp) { float dis = std::abs(x[node_id] - init_x[node_id]); if (std::lround(dis) > 0) { rowNumMoves++; rowDisplace += dis; } } totalDisplace += rowDisplace; totalNumMoves += rowNumMoves; } torch::Tensor new_node_lpos = torch::stack({at_x, at_y}, 1); logger.info("Route Move #Moves: %d Displacement: %.1f", totalNumMoves, totalDisplace); return new_node_lpos; } } // namespace routedp namespace Xplace { PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { m.def("dp_route_opt", &routedp::dp_route_opt, "dp_route_opt"); } } // namespace Xplace