Merge pull request #357 from YosysHQ/heap-fixes
HeAP: Support for region constraints, better error handling, default for all arches
This commit is contained in:
commit
2f7c7ccf80
@ -308,6 +308,14 @@ class HeAPPlacer
|
|||||||
std::vector<std::vector<int>> nearest_row_with_bel;
|
std::vector<std::vector<int>> nearest_row_with_bel;
|
||||||
std::vector<std::vector<int>> nearest_col_with_bel;
|
std::vector<std::vector<int>> nearest_col_with_bel;
|
||||||
|
|
||||||
|
struct BoundingBox
|
||||||
|
{
|
||||||
|
// Actual bounding box
|
||||||
|
int x0 = 0, x1 = 0, y0 = 0, y1 = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unordered_map<IdString, BoundingBox> constraint_region_bounds;
|
||||||
|
|
||||||
// In some cases, we can't use bindBel because we allow overlap in the earlier stages. So we use this custom
|
// In some cases, we can't use bindBel because we allow overlap in the earlier stages. So we use this custom
|
||||||
// structure instead
|
// structure instead
|
||||||
struct CellLocation
|
struct CellLocation
|
||||||
@ -443,6 +451,31 @@ class HeAPPlacer
|
|||||||
nr.at(y) = loc.y;
|
nr.at(y) = loc.y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Determine bounding boxes of region constraints
|
||||||
|
for (auto ®ion : sorted(ctx->region)) {
|
||||||
|
Region *r = region.second;
|
||||||
|
BoundingBox bb;
|
||||||
|
if (r->constr_bels) {
|
||||||
|
bb.x0 = std::numeric_limits<int>::max();
|
||||||
|
bb.x1 = std::numeric_limits<int>::min();
|
||||||
|
bb.y0 = std::numeric_limits<int>::max();
|
||||||
|
bb.y1 = std::numeric_limits<int>::min();
|
||||||
|
for (auto bel : r->bels) {
|
||||||
|
Loc loc = ctx->getBelLocation(bel);
|
||||||
|
bb.x0 = std::min(bb.x0, loc.x);
|
||||||
|
bb.x1 = std::max(bb.x1, loc.x);
|
||||||
|
bb.y0 = std::min(bb.y0, loc.y);
|
||||||
|
bb.y1 = std::max(bb.y1, loc.y);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bb.x0 = 0;
|
||||||
|
bb.y0 = 0;
|
||||||
|
bb.x1 = max_x;
|
||||||
|
bb.y1 = max_y;
|
||||||
|
}
|
||||||
|
constraint_region_bounds[r->name] = bb;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build and solve in one direction
|
// Build and solve in one direction
|
||||||
@ -684,9 +717,15 @@ class HeAPPlacer
|
|||||||
if (yaxis) {
|
if (yaxis) {
|
||||||
cell_locs.at(solve_cells.at(i)->name).rawy = vals.at(i);
|
cell_locs.at(solve_cells.at(i)->name).rawy = vals.at(i);
|
||||||
cell_locs.at(solve_cells.at(i)->name).y = std::min(max_y, std::max(0, int(vals.at(i))));
|
cell_locs.at(solve_cells.at(i)->name).y = std::min(max_y, std::max(0, int(vals.at(i))));
|
||||||
|
if (solve_cells.at(i)->region != nullptr)
|
||||||
|
cell_locs.at(solve_cells.at(i)->name).y =
|
||||||
|
limit_to_reg(solve_cells.at(i)->region, cell_locs.at(solve_cells.at(i)->name).y, true);
|
||||||
} else {
|
} else {
|
||||||
cell_locs.at(solve_cells.at(i)->name).rawx = vals.at(i);
|
cell_locs.at(solve_cells.at(i)->name).rawx = vals.at(i);
|
||||||
cell_locs.at(solve_cells.at(i)->name).x = std::min(max_x, std::max(0, int(vals.at(i))));
|
cell_locs.at(solve_cells.at(i)->name).x = std::min(max_x, std::max(0, int(vals.at(i))));
|
||||||
|
if (solve_cells.at(i)->region != nullptr)
|
||||||
|
cell_locs.at(solve_cells.at(i)->name).x =
|
||||||
|
limit_to_reg(solve_cells.at(i)->region, cell_locs.at(solve_cells.at(i)->name).x, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -735,6 +774,7 @@ class HeAPPlacer
|
|||||||
}
|
}
|
||||||
int ripup_radius = 2;
|
int ripup_radius = 2;
|
||||||
int total_iters = 0;
|
int total_iters = 0;
|
||||||
|
int total_iters_noreset = 0;
|
||||||
while (!remaining.empty()) {
|
while (!remaining.empty()) {
|
||||||
auto top = remaining.top();
|
auto top = remaining.top();
|
||||||
remaining.pop();
|
remaining.pop();
|
||||||
@ -754,15 +794,38 @@ class HeAPPlacer
|
|||||||
int best_inp_len = std::numeric_limits<int>::max();
|
int best_inp_len = std::numeric_limits<int>::max();
|
||||||
|
|
||||||
total_iters++;
|
total_iters++;
|
||||||
|
total_iters_noreset++;
|
||||||
if (total_iters > int(solve_cells.size())) {
|
if (total_iters > int(solve_cells.size())) {
|
||||||
total_iters = 0;
|
total_iters = 0;
|
||||||
ripup_radius = std::max(std::max(max_x, max_y), ripup_radius * 2);
|
ripup_radius = std::max(std::max(max_x, max_y), ripup_radius * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (total_iters_noreset > std::max(5000, 8 * int(ctx->cells.size()))) {
|
||||||
|
log_error("Unable to find legal placement for all cells, design is probably at utilisation limit.\n");
|
||||||
|
}
|
||||||
|
|
||||||
while (!placed) {
|
while (!placed) {
|
||||||
|
|
||||||
int nx = ctx->rng(2 * radius + 1) + std::max(cell_locs.at(ci->name).x - radius, 0);
|
// Set a conservative timeout
|
||||||
int ny = ctx->rng(2 * radius + 1) + std::max(cell_locs.at(ci->name).y - radius, 0);
|
if (iter > std::max(1000, 3 * int(ctx->cells.size())))
|
||||||
|
log_error("Unable to find legal placement for cell '%s', check constraints and utilisation.\n",
|
||||||
|
ctx->nameOf(ci));
|
||||||
|
|
||||||
|
int rx = radius, ry = radius;
|
||||||
|
|
||||||
|
if (ci->region != nullptr) {
|
||||||
|
rx = std::min(radius, (constraint_region_bounds[ci->region->name].x1 -
|
||||||
|
constraint_region_bounds[ci->region->name].x0) /
|
||||||
|
2 +
|
||||||
|
1);
|
||||||
|
ry = std::min(radius, (constraint_region_bounds[ci->region->name].y1 -
|
||||||
|
constraint_region_bounds[ci->region->name].y0) /
|
||||||
|
2 +
|
||||||
|
1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int nx = ctx->rng(2 * rx + 1) + std::max(cell_locs.at(ci->name).x - rx, 0);
|
||||||
|
int ny = ctx->rng(2 * ry + 1) + std::max(cell_locs.at(ci->name).y - ry, 0);
|
||||||
|
|
||||||
iter++;
|
iter++;
|
||||||
iter_at_radius++;
|
iter_at_radius++;
|
||||||
@ -820,6 +883,8 @@ class HeAPPlacer
|
|||||||
|
|
||||||
if (ci->constr_children.empty() && !ci->constr_abs_z) {
|
if (ci->constr_children.empty() && !ci->constr_abs_z) {
|
||||||
for (auto sz : fb.at(nx).at(ny)) {
|
for (auto sz : fb.at(nx).at(ny)) {
|
||||||
|
if (ci->region != nullptr && ci->region->constr_bels && !ci->region->bels.count(sz))
|
||||||
|
continue;
|
||||||
if (ctx->checkBelAvail(sz) || (radius > ripup_radius || ctx->rng(20000) < 10)) {
|
if (ctx->checkBelAvail(sz) || (radius > ripup_radius || ctx->rng(20000) < 10)) {
|
||||||
CellInfo *bound = ctx->getBoundBelCell(sz);
|
CellInfo *bound = ctx->getBoundBelCell(sz);
|
||||||
if (bound != nullptr) {
|
if (bound != nullptr) {
|
||||||
@ -881,6 +946,8 @@ class HeAPPlacer
|
|||||||
Loc ploc = visit.front().second;
|
Loc ploc = visit.front().second;
|
||||||
visit.pop();
|
visit.pop();
|
||||||
BelId target = ctx->getBelByLocation(ploc);
|
BelId target = ctx->getBelByLocation(ploc);
|
||||||
|
if (vc->region != nullptr && vc->region->constr_bels && !vc->region->bels.count(target))
|
||||||
|
continue;
|
||||||
CellInfo *bound;
|
CellInfo *bound;
|
||||||
if (target == BelId() || ctx->getBelType(target) != vc->type)
|
if (target == BelId() || ctx->getBelType(target) != vc->type)
|
||||||
goto fail;
|
goto fail;
|
||||||
@ -948,6 +1015,15 @@ class HeAPPlacer
|
|||||||
// Implementation of the cut-based spreading as described in the HeAP/SimPL papers
|
// Implementation of the cut-based spreading as described in the HeAP/SimPL papers
|
||||||
static constexpr float beta = 0.9;
|
static constexpr float beta = 0.9;
|
||||||
|
|
||||||
|
template <typename T> T limit_to_reg(Region *reg, T val, bool dir)
|
||||||
|
{
|
||||||
|
if (reg == nullptr)
|
||||||
|
return val;
|
||||||
|
int limit_low = dir ? constraint_region_bounds[reg->name].y0 : constraint_region_bounds[reg->name].x0;
|
||||||
|
int limit_high = dir ? constraint_region_bounds[reg->name].y1 : constraint_region_bounds[reg->name].x1;
|
||||||
|
return std::max<T>(std::min<T>(val, limit_high), limit_low);
|
||||||
|
}
|
||||||
|
|
||||||
struct ChainExtent
|
struct ChainExtent
|
||||||
{
|
{
|
||||||
int x0, y0, x1, y1;
|
int x0, y0, x1, y1;
|
||||||
@ -1460,10 +1536,22 @@ class HeAPPlacer
|
|||||||
: p->cell_locs.at(cut_cells.at(br.first - 1)->name).rawx;
|
: p->cell_locs.at(cut_cells.at(br.first - 1)->name).rawx;
|
||||||
double m = (br.second - bl.second) / std::max(0.00001, orig_right - orig_left);
|
double m = (br.second - bl.second) / std::max(0.00001, orig_right - orig_left);
|
||||||
for (int j = bl.first; j < br.first; j++) {
|
for (int j = bl.first; j < br.first; j++) {
|
||||||
auto &pos = dir ? p->cell_locs.at(cut_cells.at(j)->name).rawy
|
Region *cr = cut_cells.at(j)->region;
|
||||||
: p->cell_locs.at(cut_cells.at(j)->name).rawx;
|
if (cr != nullptr) {
|
||||||
NPNR_ASSERT(pos >= orig_left && pos <= orig_right);
|
// Limit spreading bounds to constraint region; if applicable
|
||||||
pos = bl.second + m * (pos - orig_left);
|
double brsc = p->limit_to_reg(cr, br.second, dir);
|
||||||
|
double blsc = p->limit_to_reg(cr, bl.second, dir);
|
||||||
|
double mr = (brsc - blsc) / std::max(0.00001, orig_right - orig_left);
|
||||||
|
auto &pos = dir ? p->cell_locs.at(cut_cells.at(j)->name).rawy
|
||||||
|
: p->cell_locs.at(cut_cells.at(j)->name).rawx;
|
||||||
|
NPNR_ASSERT(pos >= orig_left && pos <= orig_right);
|
||||||
|
pos = blsc + mr * (pos - orig_left);
|
||||||
|
} else {
|
||||||
|
auto &pos = dir ? p->cell_locs.at(cut_cells.at(j)->name).rawy
|
||||||
|
: p->cell_locs.at(cut_cells.at(j)->name).rawx;
|
||||||
|
NPNR_ASSERT(pos >= orig_left && pos <= orig_right);
|
||||||
|
pos = bl.second + m * (pos - orig_left);
|
||||||
|
}
|
||||||
// log("[%f, %f] -> [%f, %f]: %f -> %f\n", orig_left, orig_right, bl.second, br.second,
|
// log("[%f, %f] -> [%f, %f]: %f -> %f\n", orig_left, orig_right, bl.second, br.second,
|
||||||
// orig_pos, pos);
|
// orig_pos, pos);
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include "nextpnr.h"
|
#include "nextpnr.h"
|
||||||
#include "placer1.h"
|
#include "placer1.h"
|
||||||
|
#include "placer_heap.h"
|
||||||
#include "router1.h"
|
#include "router1.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
@ -494,8 +495,29 @@ bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay
|
|||||||
bool Arch::place()
|
bool Arch::place()
|
||||||
{
|
{
|
||||||
std::string placer = str_or_default(settings, id("placer"), defaultPlacer);
|
std::string placer = str_or_default(settings, id("placer"), defaultPlacer);
|
||||||
// FIXME: No HeAP because it needs a list of IO buffers
|
if (placer == "heap") {
|
||||||
if (placer == "sa") {
|
bool have_iobuf_or_constr = false;
|
||||||
|
for (auto cell : sorted(cells)) {
|
||||||
|
CellInfo *ci = cell.second;
|
||||||
|
if (ci->type == id("GENERIC_IOB") || ci->bel != BelId() || ci->attrs.count(id("BEL"))) {
|
||||||
|
have_iobuf_or_constr = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool retVal;
|
||||||
|
if (!have_iobuf_or_constr) {
|
||||||
|
log_warning("Unable to use HeAP due to a lack of IO buffers or constrained cells as anchors; reverting to "
|
||||||
|
"SA.\n");
|
||||||
|
retVal = placer1(getCtx(), Placer1Cfg(getCtx()));
|
||||||
|
} else {
|
||||||
|
PlacerHeapCfg cfg(getCtx());
|
||||||
|
cfg.ioBufTypes.insert(id("GENERIC_IOB"));
|
||||||
|
retVal = placer_heap(getCtx(), cfg);
|
||||||
|
}
|
||||||
|
getCtx()->settings[getCtx()->id("place")] = 1;
|
||||||
|
archInfoToAttributes();
|
||||||
|
return retVal;
|
||||||
|
} else if (placer == "sa") {
|
||||||
bool retVal = placer1(getCtx(), Placer1Cfg(getCtx()));
|
bool retVal = placer1(getCtx(), Placer1Cfg(getCtx()));
|
||||||
getCtx()->settings[getCtx()->id("place")] = 1;
|
getCtx()->settings[getCtx()->id("place")] = 1;
|
||||||
archInfoToAttributes();
|
archInfoToAttributes();
|
||||||
@ -596,9 +618,17 @@ bool Arch::isBelLocationValid(BelId bel) const
|
|||||||
return cellsCompatible(cells.data(), int(cells.size()));
|
return cellsCompatible(cells.data(), int(cells.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef WITH_HEAP
|
||||||
|
const std::string Arch::defaultPlacer = "heap";
|
||||||
|
#else
|
||||||
const std::string Arch::defaultPlacer = "sa";
|
const std::string Arch::defaultPlacer = "sa";
|
||||||
const std::vector<std::string> Arch::availablePlacers = {"sa"};
|
#endif
|
||||||
|
|
||||||
|
const std::vector<std::string> Arch::availablePlacers = {"sa",
|
||||||
|
#ifdef WITH_HEAP
|
||||||
|
"heap"
|
||||||
|
#endif
|
||||||
|
};
|
||||||
void Arch::assignArchInfo()
|
void Arch::assignArchInfo()
|
||||||
{
|
{
|
||||||
for (auto &cell : getCtx()->cells) {
|
for (auto &cell : getCtx()->cells) {
|
||||||
|
@ -1245,7 +1245,11 @@ void Arch::assignCellInfo(CellInfo *cell)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef WITH_HEAP
|
||||||
|
const std::string Arch::defaultPlacer = "heap";
|
||||||
|
#else
|
||||||
const std::string Arch::defaultPlacer = "sa";
|
const std::string Arch::defaultPlacer = "sa";
|
||||||
|
#endif
|
||||||
|
|
||||||
const std::vector<std::string> Arch::availablePlacers = {"sa",
|
const std::vector<std::string> Arch::availablePlacers = {"sa",
|
||||||
#ifdef WITH_HEAP
|
#ifdef WITH_HEAP
|
||||||
|
Loading…
Reference in New Issue
Block a user