#include "coloquinte/legalizer.hxx" #include "coloquinte/optimization_subproblems.hxx" #include #include #include #include namespace coloquinte{ namespace dp{ void get_result(netlist const & circuit, detailed_placement const & dpl, placement_t & gpl){ for(index_t c=0; c task() const{ return legalizable_task(width, x_pos, original_cell); } }; struct fixed_cell_interval{ int_t min_x, max_x; index_t cell_ind; bool operator<(fixed_cell_interval const o) const{ return min_x > o.min_x; } fixed_cell_interval(int_t mn, int_t mx, index_t ind) : min_x(mn), max_x(mx), cell_ind(ind){} }; struct cell_leg_properties{ int_t x_pos; index_t row_pos; index_t ind; cell_leg_properties(){} cell_leg_properties(int_t x, int_t r, index_t i) : x_pos(x), row_pos(r), ind(i){} }; std::vector simple_legalize( std::vector > obstacles, std::vector cells, std::vector > & rows, int_t x_min, int_t x_max, int_t y_orig, int_t row_height, index_t nbr_rows ){ std::vector first_available_position(nbr_rows, x_min); rows.resize(nbr_rows); // Sort the cells by x position std::sort(cells.begin(), cells.end()); std::vector ret; for(cell_to_leg C : cells){ // Dumb, quick and dirty best-fit legalization bool found_location = false; // Properties of the current best solution int_t best_x=0; int_t best_cost=0; index_t best_row=0; // Helper function auto check_row_cost = [&](index_t r, cell_to_leg const cell, int_t additional_cost){ // Find where to put the cell in these rows // Simple method: get a range where we can put the cell assert(r + cell.nbr_rows <= nbr_rows); assert(additional_cost >= 0); // First position where we can put it int_t cur_pos = *std::max_element(first_available_position.begin() + r, first_available_position.begin() + r + cell.nbr_rows); int_t max_lim = x_max - cell.width; int_t interval_lim; do{ interval_lim = max_lim; // For each row, test if obstacles prevent us from putting a cell here // Until we find a correct position or are beyond the maximum position for(index_t i = 0; imax_x <= cur_pos; ++it){ } if(it != obstacles[r+i].rend()){ // There is an obstacle on the right assert(it->min_x < it->max_x); int_t cur_lim = it->min_x - cell.width; // Where the obstacles contrains us interval_lim = std::min(cur_lim, interval_lim); // Constraint if(cur_lim < cur_pos){ // If this particular obstacle constrained us so that it is not possible to make it here, we increment the position cur_pos = std::max(it->max_x, cur_pos); } } } // Do it again until we find a solution // TODO: continue until we can't find a better solution (currently sticks before the first obstacle if there is enough whitespace) }while(interval_lim < cur_pos and interval_lim < max_lim and cur_pos < max_lim); // Not admissible and we encountered an obstacle and there is still hope if(interval_lim >= cur_pos){ // An admissible solution is found (and if cell.x_pos is between cur_pos and interval_lim it is optimal) int_t row_best_x = std::min(interval_lim, std::max(cur_pos, cell.x_pos)); int_t row_cost_x = std::abs((float)(row_best_x - cell.x_pos)); if(not found_location or row_cost_x + additional_cost < best_cost){ found_location = true; best_cost = row_cost_x + additional_cost; best_x = row_best_x; best_row = r; } } }; // The row where we would prefer the cell to go if(C.nbr_rows > nbr_rows) throw std::runtime_error("Impossible to legalize a cell spanning more rows than are available\n"); index_t central_row = std::min( (index_t) std::max( (C.y_pos - y_orig) / row_height, 0), nbr_rows-C.nbr_rows); // Try every possible row from the best one, until we can't improve the cost for(index_t row_dist = 0; (central_row + row_dist < nbr_rows or central_row >= row_dist) and (not found_location or (int_t) row_dist * row_height * C.width < (int_t) row_height + best_cost); ++row_dist ){ if(central_row + row_dist < nbr_rows - C.nbr_rows){ int_t add_cost = C.width * std::abs((float)static_cast(central_row + row_dist) * static_cast(row_height) + y_orig - C.y_pos); check_row_cost(central_row + row_dist, C, add_cost); } if(central_row >= row_dist){ int_t add_cost = C.width * std::abs((float)static_cast(central_row - row_dist) * static_cast(row_height) + y_orig - C.y_pos); check_row_cost(central_row - row_dist, C, add_cost); } } if(not found_location){ // We didn't find any whitespace to put the cell in throw std::runtime_error("Didn't manage to pack a cell due to dumb algorithm\n"); } else{ assert(best_x + C.width <= x_max and best_x >= x_min); // Update the occupied rows for(index_t r = best_row; r < best_row + C.nbr_rows; ++r){ // Include the obstacles while(not obstacles[r].empty() and obstacles[r].back().max_x <= best_x){ rows[r].push_back(obstacles[r].back().cell_ind); obstacles[r].pop_back(); } assert(obstacles[r].empty() or obstacles[r].back().min_x >= best_x + C.width); rows[r].push_back(C.original_cell); first_available_position[r] = best_x + C.width; } ret.push_back(cell_leg_properties(best_x, best_row, C.original_cell)); } } // Finally, push the remaining fixed cells for(index_t r=0; r good_legalize( std::vector > obstacles, std::vector cells, std::vector > & rows, int_t x_min, int_t x_max, int_t y_orig, int_t row_height, index_t nbr_rows ){ // Two possibilities: // * Single OSRP (group of movable cells) at the current end of the row of standard cells // * Multiple OSRPs, between each pair of obstacles // -> allows pushing cells past obstacles // -> tricky with multiple standard cell heights // Therefore I chose single OSRP, which gets cleared and pushed to the final state whenever // * we encounter a multiple-rows cell // * a new standard cell gets past an obstacle // The current group of standard cells on the right of the row std::vector > single_row_problems(nbr_rows); for(index_t r=0; r(x_min, obstacles[r].empty() ? x_max : obstacles[r].back().min_x); } rows.resize(nbr_rows); // Sort the cells by x position std::sort(cells.begin(), cells.end()); std::vector ret; for(cell_to_leg C : cells){ // Dumb, quick and dirty best-fit legalization bool found_location = false; // Properties of the current best solution int_t best_cost=0; index_t best_row=0; index_t obstacles_passed = 0; // Helper function auto check_row_cost = [&](index_t r, cell_to_leg const cell, int_t additional_cost){ // Find where to put the cell in these rows // Check if we can put it in the current ranges and at what cost; if not or if the optimal position is beyond an obstacle, try after this obstacle too assert(cell.nbr_rows > 0); assert(r + cell.nbr_rows <= nbr_rows); assert(additional_cost >= 0); // Where can we put a standard cell if we allow to move the cells? if(cell.nbr_rows == 1){ int_t cur_cost = 0; // Can we simply add it to the single row problem? bool found_here = single_row_problems[r].remaining_space() >= cell.width; int_t loc_obstacles_passed = 0; if(found_here){ // Check the cost of pushing it here with possible displacement cur_cost = single_row_problems[r].get_cost(cell.task()); // Don't update the row } // Other positions where we can put it, without moving other cells this time if(not found_here or cur_cost > 0){ index_t obstacles_to_throw = 0; auto it = obstacles[r].rbegin(); while(it != obstacles[r].rend()){ ++ obstacles_to_throw; auto prev_it = it++; int_t region_end = it != obstacles[r].rend() ? it->min_x : x_max; if(region_end >= prev_it->max_x + cell.width){ int_t loc_x = std::min(region_end - cell.width, std::max(prev_it->max_x, cell.x_pos)); int_t loc_cost = cell.width * std::abs((float)(cell.x_pos - loc_x)); if(not found_here or cur_cost > loc_cost){ found_here = true; cur_cost = loc_cost; loc_obstacles_passed = obstacles_to_throw; } } } } if(found_here and (not found_location or cur_cost + additional_cost < best_cost)){ found_location = true; //std::cout << "Found with displacement cost " << cur_cost << " and total cost " << cur_cost + additional_cost << std::endl; best_cost = cur_cost + additional_cost; best_row = r; obstacles_passed = loc_obstacles_passed; if(loc_obstacles_passed > 0) assert(not obstacles[r].empty()); } } else{ // If it is a fixed cell, we use fixed locations std::cerr << "cell.nbr_rows:" << cell.nbr_rows << std::endl; throw std::runtime_error("I don't handle fucking macros (good_legalize)\n"); } }; // The row where we would prefer the cell to go if(C.nbr_rows > nbr_rows) throw std::runtime_error("Impossible to legalize a cell spanning more rows than are available\n"); index_t central_row = std::min( (index_t) std::max( (C.y_pos - y_orig) / row_height, 0), nbr_rows-C.nbr_rows); // Try every possible row from the best one, until we can't improve the cost for(index_t row_dist = 0; (central_row + row_dist < nbr_rows or central_row >= row_dist) and (not found_location or (int_t) row_dist * row_height * C.width < (int_t) row_height + best_cost); ++row_dist ){ if(central_row + row_dist < nbr_rows - C.nbr_rows){ int_t add_cost = C.width * std::abs((float)static_cast(central_row + row_dist) * static_cast(row_height) + y_orig - C.y_pos); check_row_cost(central_row + row_dist, C, add_cost); } if(central_row >= row_dist){ int_t add_cost = C.width * std::abs((float)static_cast(central_row - row_dist) * static_cast(row_height) + y_orig - C.y_pos); check_row_cost(central_row - row_dist, C, add_cost); } } if(not found_location){ // We didn't find any whitespace to put the cell in throw std::runtime_error("Didn't manage to pack a cell: leave more whitespace and avoid macros near the right side\n"); } else{ //std::cout << "Cell " << C.original_cell << " of width " << C.width << " targetting row " << central_row << " and position " << C.x_pos << " put at row " << best_row << " with displacement " << best_cost / C.width << " with " << obstacles_passed << " obstacles passed" << std::endl; // If the cell spans multiple rows, it becomes fixed // In this case or if the cell goes after an obstacle, push everything before the cell to the fixed state if(C.nbr_rows == 1){ if(obstacles_passed == 0){ // Ok; just update the old single row problem single_row_problems[best_row].push(C.task()); // Push it to the row } else{ assert(obstacles_passed > 0); // Empty the single row problem for(auto p : single_row_problems[best_row].get_placement()){ rows[best_row].push_back(p.first); ret.push_back(cell_leg_properties(p.second, best_row, p.first)); } // Find where to put it int_t region_begin = x_min; for(index_t i=0; i(region_begin, region_end); assert(region_end - region_begin >= C.width); single_row_problems[best_row].push(C.task()); // Push this only cell to the single row problem } } else{ throw std::runtime_error("I don't handle fucking macros (here)\n"); } } } for(index_t r=0; r surface, int_t row_height){ if(row_height <= 0) throw std::runtime_error("The rows' height should be positive\n"); index_t nbr_rows = (surface.y_max - surface.y_min) / row_height; // The position of the ith row is surface.y_min + i * row_height std::vector > row_occupation(nbr_rows); std::vector cells; placement_t new_placement = pl; std::vector placement_rows(circuit.cell_cnt()); std::vector cell_heights(circuit.cell_cnt()); for(index_t i=0; i target_pos = pl.positions_[i]; index_t cur_cell_rows = (cur.size.y + row_height -1) / row_height; cells.push_back(cell_to_leg(target_pos.x, target_pos.y, i, cur.size.x, cur_cell_rows)); cell_heights[i] = cur_cell_rows; } else{ // In each row, we put the index of the fixed cell and the range that is already occupied int_t low_x_pos = pl.positions_[i].x, hgh_x_pos = pl.positions_[i].x + cur.size.x, low_y_pos = pl.positions_[i].y, hgh_y_pos = pl.positions_[i].y + cur.size.y; new_placement.positions_[i] = point(low_x_pos, low_y_pos); if(hgh_y_pos <= surface.y_min or low_y_pos >= surface.y_max or hgh_x_pos <= surface.x_min or low_x_pos >= surface.x_max){ placement_rows[i] = null_ind; cell_heights[i] = 0; } else{ assert(low_x_pos < hgh_x_pos and low_y_pos < hgh_y_pos); int_t rnd_hgh_x_pos = std::min(surface.x_max, hgh_x_pos); int_t rnd_hgh_y_pos = std::min(surface.y_max, hgh_y_pos); int_t rnd_low_x_pos = std::max(surface.x_min, low_x_pos); int_t rnd_low_y_pos = std::max(surface.y_min, low_y_pos); index_t first_row = (rnd_low_y_pos - surface.y_min) / row_height; index_t last_row = (index_t) (rnd_hgh_y_pos - surface.y_min + row_height - 1) / row_height; // Exclusive: if the cell spans the next row, i.e. pos % row_height >= 0, include it too assert(last_row <= nbr_rows); placement_rows[i] = first_row; cell_heights[i] = last_row - first_row; for(index_t r=first_row; r & L : row_occupation){ std::sort(L.begin(), L.end()); // Sorts from last to first, so that we may use pop_back() // Doesn't collapse them yet, which may make for bigger complexities for(index_t i=0; i+1 > cells_by_rows; auto final_cells = good_legalize(row_occupation, cells, cells_by_rows, surface.x_min, surface.x_max, surface.y_min, row_height, nbr_rows ); for(cell_leg_properties C : final_cells){ new_placement.positions_[C.ind] = point(C.x_pos, static_cast(C.row_pos) * row_height + surface.y_min); placement_rows[C.ind] = C.row_pos; } return detailed_placement( new_placement, placement_rows, cell_heights, cells_by_rows, surface.x_min, surface.x_max, surface.y_min, nbr_rows, row_height ); } } // namespace dp } // namespace coloquinte