mirror of https://github.com/YosysHQ/nextpnr.git
control set awareness in the HeAP legaliser (#1678)
* xilinx: Index control sets Signed-off-by: gatecat <gatecat@ds0.me> * heap: data structure for control sets Signed-off-by: gatecat <gatecat@ds0.me> * heap: fail faster on control set mismatch Signed-off-by: gatecat <gatecat@ds0.me> * xilinx: Reduce control set search radius Signed-off-by: gatecat <gatecat@ds0.me> * Fix compiler warning Signed-off-by: gatecat <gatecat@ds0.me> * heap: Allow disabling control set awareness for comparison/debug Signed-off-by: gatecat <gatecat@ds0.me> * heap: Add some notes about control sets Signed-off-by: gatecat <gatecat@ds0.me> * heap: Fix typo and regression Signed-off-by: gatecat <gatecat@ds0.me> * heap: Add a schedule for ctrlset search radius Signed-off-by: gatecat <gatecat@ds0.me> * heap: Tidy up Signed-off-by: gatecat <gatecat@ds0.me> --------- Signed-off-by: gatecat <gatecat@ds0.me>
This commit is contained in:
parent
12bb6df237
commit
c6b876fc85
|
|
@ -62,6 +62,7 @@ template <typename T> class array2d
|
||||||
if ((new_width * new_height) > m_size) {
|
if ((new_width * new_height) > m_size) {
|
||||||
delete[] data;
|
delete[] data;
|
||||||
m_size = new_width * new_height;
|
m_size = new_width * new_height;
|
||||||
|
NPNR_ASSERT(m_size >= 0);
|
||||||
data = new T[m_size];
|
data = new T[m_size];
|
||||||
}
|
}
|
||||||
m_width = new_width;
|
m_width = new_width;
|
||||||
|
|
|
||||||
|
|
@ -391,6 +391,8 @@ po::options_description CommandHandler::getGeneralOptions()
|
||||||
"allow placer to attempt up to max(10000, total cells^2 / N) iterations to place a cell (int "
|
"allow placer to attempt up to max(10000, total cells^2 / N) iterations to place a cell (int "
|
||||||
"N, default: 8, 0 for no timeout)");
|
"N, default: 8, 0 for no timeout)");
|
||||||
|
|
||||||
|
general.add_options()("placer-heap-no-ctrl-set", "disable control set awareness in placer heap");
|
||||||
|
|
||||||
general.add_options()("static-dump-density", "write density csv files during placer-static flow");
|
general.add_options()("static-dump-density", "write density csv files during placer-static flow");
|
||||||
|
|
||||||
#if !defined(NPNR_DISABLE_THREADS)
|
#if !defined(NPNR_DISABLE_THREADS)
|
||||||
|
|
@ -536,6 +538,9 @@ void CommandHandler::setupContext(Context *ctx)
|
||||||
ctx->settings[ctx->id("placerHeap/cellPlacementTimeout")] =
|
ctx->settings[ctx->id("placerHeap/cellPlacementTimeout")] =
|
||||||
std::to_string(std::max(0, vm["placer-heap-cell-placement-timeout"].as<int>()));
|
std::to_string(std::max(0, vm["placer-heap-cell-placement-timeout"].as<int>()));
|
||||||
|
|
||||||
|
if (vm.count("placer-heap-no-ctrl-set"))
|
||||||
|
ctx->settings[ctx->id("placerHeap/noCtrlSet")] = true;
|
||||||
|
|
||||||
if (vm.count("parallel-refine"))
|
if (vm.count("parallel-refine"))
|
||||||
ctx->settings[ctx->id("placerHeap/parallelRefine")] = true;
|
ctx->settings[ctx->id("placerHeap/parallelRefine")] = true;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
#include "array2d.h"
|
||||||
#include "fast_bels.h"
|
#include "fast_bels.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "nextpnr.h"
|
#include "nextpnr.h"
|
||||||
|
|
@ -132,6 +133,29 @@ template <typename T> struct EquationSystem
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ControlSetState
|
||||||
|
{
|
||||||
|
int32_t ctrl_set = -1;
|
||||||
|
int32_t count = 0;
|
||||||
|
void bind(int32_t ctrl_set)
|
||||||
|
{
|
||||||
|
if (count == 0) {
|
||||||
|
this->ctrl_set = ctrl_set;
|
||||||
|
} else {
|
||||||
|
NPNR_ASSERT(this->ctrl_set == ctrl_set);
|
||||||
|
}
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
void unbind()
|
||||||
|
{
|
||||||
|
--count;
|
||||||
|
NPNR_ASSERT(count >= 0);
|
||||||
|
if (count == 0)
|
||||||
|
this->ctrl_set = -1;
|
||||||
|
}
|
||||||
|
bool check(int32_t ctrl_set) { return count == 0 || ctrl_set == this->ctrl_set; }
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
class HeAPPlacer
|
class HeAPPlacer
|
||||||
|
|
@ -156,6 +180,7 @@ class HeAPPlacer
|
||||||
ScopeLock<Context> lock(ctx);
|
ScopeLock<Context> lock(ctx);
|
||||||
place_constraints();
|
place_constraints();
|
||||||
build_fast_bels();
|
build_fast_bels();
|
||||||
|
alloc_control_sets();
|
||||||
seed_placement();
|
seed_placement();
|
||||||
update_all_chains();
|
update_all_chains();
|
||||||
wirelen_t hpwl = total_hpwl();
|
wirelen_t hpwl = total_hpwl();
|
||||||
|
|
@ -182,7 +207,8 @@ class HeAPPlacer
|
||||||
}
|
}
|
||||||
|
|
||||||
wirelen_t solved_hpwl = 0, spread_hpwl = 0, legal_hpwl = 0, best_hpwl = std::numeric_limits<wirelen_t>::max();
|
wirelen_t solved_hpwl = 0, spread_hpwl = 0, legal_hpwl = 0, best_hpwl = std::numeric_limits<wirelen_t>::max();
|
||||||
int iter = 0, stalled = 0;
|
iter = 0;
|
||||||
|
int stalled = 0;
|
||||||
|
|
||||||
std::vector<std::tuple<CellInfo *, BelId, PlaceStrength>> solution;
|
std::vector<std::tuple<CellInfo *, BelId, PlaceStrength>> solution;
|
||||||
|
|
||||||
|
|
@ -425,9 +451,14 @@ class HeAPPlacer
|
||||||
std::vector<CellInfo *> solve_cells;
|
std::vector<CellInfo *> solve_cells;
|
||||||
|
|
||||||
dict<ClusterId, std::vector<CellInfo *>> cluster2cells;
|
dict<ClusterId, std::vector<CellInfo *>> cluster2cells;
|
||||||
|
dict<IdString, int> cell_ctrl_set;
|
||||||
dict<ClusterId, int> chain_size;
|
dict<ClusterId, int> chain_size;
|
||||||
|
// Tracking control sets
|
||||||
|
array2d<std::vector<ControlSetState>> control_sets;
|
||||||
|
dict<int, int> z_to_ctrl_set;
|
||||||
// Performance counting
|
// Performance counting
|
||||||
double solve_time = 0, cl_time = 0, sl_time = 0;
|
double solve_time = 0, cl_time = 0, sl_time = 0;
|
||||||
|
int iter = 0;
|
||||||
|
|
||||||
// Place cells with the BEL attribute set to constrain them
|
// Place cells with the BEL attribute set to constrain them
|
||||||
void place_constraints()
|
void place_constraints()
|
||||||
|
|
@ -528,6 +559,144 @@ class HeAPPlacer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void alloc_control_sets()
|
||||||
|
{
|
||||||
|
if (cfg.ff_bel_bucket == BelBucketId() || cfg.disableCtrlSet)
|
||||||
|
return;
|
||||||
|
FastBels::FastBelsData *ff_bels;
|
||||||
|
fast_bels.getBelsForBelBucket(cfg.ff_bel_bucket, &ff_bels);
|
||||||
|
control_sets.reset(max_x + 1, max_y + 1);
|
||||||
|
for (int x = 0; x <= max_x; x++) {
|
||||||
|
if (x >= int(ff_bels->size()))
|
||||||
|
continue;
|
||||||
|
auto &col = ff_bels->at(x);
|
||||||
|
for (int y = 0; y <= max_y; y++) {
|
||||||
|
if (y < int(col.size()) && !col.at(y).empty())
|
||||||
|
control_sets.at(x, y).resize(cfg.ff_control_set_groups.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int g = 0; g < int(cfg.ff_control_set_groups.size()); g++) {
|
||||||
|
for (int z : cfg.ff_control_set_groups.at(g)) {
|
||||||
|
z_to_ctrl_set[z] = g;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// determine cell control sets
|
||||||
|
for (const auto &cell : ctx->cells) {
|
||||||
|
const CellInfo *ci = cell.second.get();
|
||||||
|
if (ctx->getBelBucketForCellType(ci->type) != cfg.ff_bel_bucket)
|
||||||
|
continue;
|
||||||
|
auto ctrl_set = cfg.get_cell_control_set(ctx, ci);
|
||||||
|
if (ctrl_set != -1) {
|
||||||
|
cell_ctrl_set[ci->name] = ctrl_set;
|
||||||
|
if (ci->bel != BelId())
|
||||||
|
bind_ctrl_set(ci->bel, ci->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool test_ctrl_set(BelId bel, IdString cell)
|
||||||
|
{
|
||||||
|
if (cfg.ff_bel_bucket == BelBucketId() || cfg.disableCtrlSet)
|
||||||
|
return true;
|
||||||
|
if (ctx->getBelBucketForBel(bel) != cfg.ff_bel_bucket)
|
||||||
|
return true;
|
||||||
|
auto loc = ctx->getBelLocation(bel);
|
||||||
|
return control_sets.at(loc.x, loc.y).at(z_to_ctrl_set.at(loc.z)).check(cell_ctrl_set.at(cell));
|
||||||
|
}
|
||||||
|
|
||||||
|
void bind_ctrl_set(BelId bel, IdString cell)
|
||||||
|
{
|
||||||
|
if (cfg.ff_bel_bucket == BelBucketId() || cfg.disableCtrlSet)
|
||||||
|
return;
|
||||||
|
if (ctx->getBelBucketForBel(bel) != cfg.ff_bel_bucket)
|
||||||
|
return;
|
||||||
|
auto loc = ctx->getBelLocation(bel);
|
||||||
|
control_sets.at(loc.x, loc.y).at(z_to_ctrl_set.at(loc.z)).bind(cell_ctrl_set.at(cell));
|
||||||
|
}
|
||||||
|
|
||||||
|
void unbind_ctrl_set(BelId bel)
|
||||||
|
{
|
||||||
|
if (cfg.ff_bel_bucket == BelBucketId() || cfg.disableCtrlSet)
|
||||||
|
return;
|
||||||
|
if (ctx->getBelBucketForBel(bel) != cfg.ff_bel_bucket)
|
||||||
|
return;
|
||||||
|
auto loc = ctx->getBelLocation(bel);
|
||||||
|
auto &tile = control_sets.at(loc.x, loc.y);
|
||||||
|
if (tile.empty())
|
||||||
|
return;
|
||||||
|
auto fnd = z_to_ctrl_set.find(loc.z);
|
||||||
|
if (fnd == z_to_ctrl_set.end())
|
||||||
|
return;
|
||||||
|
tile.at(fnd->second).unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t get_cluster_control_set(ClusterId cluster)
|
||||||
|
{
|
||||||
|
int32_t ctrl_set = -1;
|
||||||
|
if (cfg.ff_bel_bucket == BelBucketId() || cfg.disableCtrlSet)
|
||||||
|
return -1;
|
||||||
|
for (auto cell : cluster2cells.at(cluster)) {
|
||||||
|
auto ofs = ctx->getClusterOffset(cell);
|
||||||
|
if (ofs.x != 0 || ofs.y != 0)
|
||||||
|
return -1; // big cluster
|
||||||
|
if (ctx->getBelBucketForCellType(cell->type) != cfg.ff_bel_bucket)
|
||||||
|
continue;
|
||||||
|
auto cell_ctrl_set = cfg.get_cell_control_set(ctx, cell);
|
||||||
|
if (cell_ctrl_set == -1)
|
||||||
|
continue;
|
||||||
|
if (ctrl_set == -1 || ctrl_set == cell_ctrl_set) {
|
||||||
|
ctrl_set = cell_ctrl_set;
|
||||||
|
} else {
|
||||||
|
// mismatch, complex cluster
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ctrl_set;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Loc> find_control_set_candidates(int cx, int cy, int32_t ctrl_set, int max_radius, int &nonempty)
|
||||||
|
{
|
||||||
|
std::vector<Loc> result;
|
||||||
|
|
||||||
|
int radius = 1;
|
||||||
|
nonempty = 0;
|
||||||
|
auto process_location = [&](int x, int y) {
|
||||||
|
if (y < 0 || y > max_y)
|
||||||
|
return;
|
||||||
|
if (x < 0 || x > max_x)
|
||||||
|
return;
|
||||||
|
const auto &tile = control_sets.at(x, y);
|
||||||
|
if (tile.empty())
|
||||||
|
return;
|
||||||
|
++nonempty;
|
||||||
|
for (int g = 0; g < int(tile.size()); g++) {
|
||||||
|
if (tile.at(g).count > 0 && tile.at(g).ctrl_set == ctrl_set) {
|
||||||
|
result.emplace_back(x, y, g);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
process_location(cx, cy);
|
||||||
|
while (radius < max_radius && int(result.size()) < 10) {
|
||||||
|
for (int y = cy - radius; y <= cy + radius; y++) {
|
||||||
|
process_location(cx - radius, y);
|
||||||
|
process_location(cx + radius, y);
|
||||||
|
}
|
||||||
|
for (int x = cx - (radius - 1); x <= cx + (radius - 1); x++) {
|
||||||
|
process_location(x, cy - radius);
|
||||||
|
process_location(x, cy + radius);
|
||||||
|
}
|
||||||
|
++radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::stable_sort(result.begin(), result.end(), [&](Loc a, Loc b) {
|
||||||
|
int d0 = std::abs(a.x - cx) + std::abs(a.y - cy);
|
||||||
|
int d1 = std::abs(b.y - cx) + std::abs(b.y - cy);
|
||||||
|
return d0 < d1;
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// Build and solve in one direction
|
// Build and solve in one direction
|
||||||
void build_solve_direction(bool yaxis, int iter)
|
void build_solve_direction(bool yaxis, int iter)
|
||||||
{
|
{
|
||||||
|
|
@ -645,12 +814,14 @@ class HeAPPlacer
|
||||||
placed = true;
|
placed = true;
|
||||||
} else {
|
} else {
|
||||||
ctx->bindBel(bel, ci, STRENGTH_STRONG);
|
ctx->bindBel(bel, ci, STRENGTH_STRONG);
|
||||||
|
bind_ctrl_set(bel, ci->name);
|
||||||
if (ctx->isBelLocationValid(bel)) {
|
if (ctx->isBelLocationValid(bel)) {
|
||||||
cell_locs[cell.first].locked = true;
|
cell_locs[cell.first].locked = true;
|
||||||
placed = true;
|
placed = true;
|
||||||
bels_used.insert(bel);
|
bels_used.insert(bel);
|
||||||
} else {
|
} else {
|
||||||
ctx->unbindBel(bel);
|
ctx->unbindBel(bel);
|
||||||
|
unbind_ctrl_set(bel);
|
||||||
available_bels.at(ci->type).push_front(bel);
|
available_bels.at(ci->type).push_front(bel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -908,8 +1079,10 @@ class HeAPPlacer
|
||||||
CellInfo *ci = cell.second.get();
|
CellInfo *ci = cell.second.get();
|
||||||
if (ci->bel != BelId() &&
|
if (ci->bel != BelId() &&
|
||||||
(ci->udata != dont_solve ||
|
(ci->udata != dont_solve ||
|
||||||
(ci->cluster != ClusterId() && ctx->getClusterRootCell(ci->cluster)->udata != dont_solve)))
|
(ci->cluster != ClusterId() && ctx->getClusterRootCell(ci->cluster)->udata != dont_solve))) {
|
||||||
|
p->unbind_ctrl_set(ci->bel);
|
||||||
ctx->unbindBel(ci->bel);
|
ctx->unbindBel(ci->bel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// At the moment we don't follow the full HeAP algorithm using cuts for legalisation, instead using
|
// At the moment we don't follow the full HeAP algorithm using cuts for legalisation, instead using
|
||||||
|
|
@ -970,6 +1143,39 @@ class HeAPPlacer
|
||||||
log_error("Unable to find legal placement for all cells, design is probably at utilisation limit.\n");
|
log_error("Unable to find legal placement for all cells, design is probably at utilisation limit.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (p->cfg.ff_bel_bucket != BelBucketId() && !p->cfg.disableCtrlSet) {
|
||||||
|
// Try placing based on same control set in window first
|
||||||
|
int32_t ctrl_set = -1;
|
||||||
|
if (ci->cluster != ClusterId()) {
|
||||||
|
ctrl_set = p->get_cluster_control_set(ci->cluster);
|
||||||
|
} else if (ctx->getBelBucketForCellType(ci->type) == p->cfg.ff_bel_bucket) {
|
||||||
|
ctrl_set = p->cfg.get_cell_control_set(ctx, ci);
|
||||||
|
}
|
||||||
|
if (ctrl_set != -1) {
|
||||||
|
int nonempty = 0;
|
||||||
|
int ctrl_set_radius = p->cfg.ctrl_set_max_radius.at(
|
||||||
|
std::min(p->iter, int(p->cfg.ctrl_set_max_radius.size()) - 1));
|
||||||
|
auto candidates =
|
||||||
|
p->find_control_set_candidates(p->cell_locs.at(ci->name).x, p->cell_locs.at(ci->name).y,
|
||||||
|
ctrl_set, ctrl_set_radius, nonempty);
|
||||||
|
// log_info("%s %d/%d %d (%d, %d)\n", ci->name.c_str(ctx), int(candidates.size()), nonempty,
|
||||||
|
// ctrl_set,
|
||||||
|
// int(p->cell_locs.at(ci->name).x), int(p->cell_locs.at(ci->name).y));
|
||||||
|
for (auto loc : candidates) {
|
||||||
|
|
||||||
|
if (ci->cluster == ClusterId()) {
|
||||||
|
try_place_cell(ci, loc.x, loc.y, loc.z);
|
||||||
|
} else {
|
||||||
|
try_place_cluster(ci, loc.x, loc.y, loc.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (placed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
while (!placed) {
|
while (!placed) {
|
||||||
if (p->cfg.cell_placement_timeout > 0 && total_iters_for_cell > p->cfg.cell_placement_timeout)
|
if (p->cfg.cell_placement_timeout > 0 && total_iters_for_cell > p->cfg.cell_placement_timeout)
|
||||||
log_error("Unable to find legal placement for cell '%s' of type '%s' after %d attempts, check "
|
log_error("Unable to find legal placement for cell '%s' of type '%s' after %d attempts, check "
|
||||||
|
|
@ -1028,10 +1234,12 @@ class HeAPPlacer
|
||||||
if (iter_at_radius >= need_to_explore && bestBel != BelId()) {
|
if (iter_at_radius >= need_to_explore && bestBel != BelId()) {
|
||||||
CellInfo *bound = ctx->getBoundBelCell(bestBel);
|
CellInfo *bound = ctx->getBoundBelCell(bestBel);
|
||||||
if (bound != nullptr) {
|
if (bound != nullptr) {
|
||||||
|
p->unbind_ctrl_set(bound->bel);
|
||||||
ctx->unbindBel(bound->bel);
|
ctx->unbindBel(bound->bel);
|
||||||
remaining.emplace(p->chain_size[bound->name] * p->cfg.get_cell_legalisation_weight(ctx, bound),
|
remaining.emplace(p->chain_size[bound->name] * p->cfg.get_cell_legalisation_weight(ctx, bound),
|
||||||
bound->name);
|
bound->name);
|
||||||
}
|
}
|
||||||
|
p->bind_ctrl_set(bestBel, ci->name);
|
||||||
ctx->bindBel(bestBel, ci, STRENGTH_WEAK);
|
ctx->bindBel(bestBel, ci, STRENGTH_WEAK);
|
||||||
placed = true;
|
placed = true;
|
||||||
Loc loc = ctx->getBelLocation(bestBel);
|
Loc loc = ctx->getBelLocation(bestBel);
|
||||||
|
|
@ -1095,15 +1303,20 @@ class HeAPPlacer
|
||||||
return std::make_pair(nx, ny);
|
return std::make_pair(nx, ny);
|
||||||
}
|
}
|
||||||
|
|
||||||
void try_place_cell(CellInfo *ci, int nx, int ny)
|
void try_place_cell(CellInfo *ci, int nx, int ny, int ctrl_set_group = -1)
|
||||||
{
|
{
|
||||||
for (auto sz : fb->at(nx).at(ny)) {
|
for (auto sz : fb->at(nx).at(ny)) {
|
||||||
// Look through all bels in this tile; checking region constraint if applicable
|
// Look through all bels in this tile; checking region constraint if applicable
|
||||||
if (!ci->testRegion(sz))
|
if (!ci->testRegion(sz))
|
||||||
continue;
|
continue;
|
||||||
|
if (ctrl_set_group != -1 && p->z_to_ctrl_set.at(ctx->getBelLocation(sz).z) != ctrl_set_group)
|
||||||
|
continue;
|
||||||
|
if (ctrl_set_group == -1 && !p->test_ctrl_set(sz, ci->name))
|
||||||
|
continue;
|
||||||
// Prefer available bels; unless we are dealing with a wide radius (e.g. difficult control sets)
|
// Prefer available bels; unless we are dealing with a wide radius (e.g. difficult control sets)
|
||||||
// or occasionally trigger a tiebreaker
|
// or occasionally trigger a tiebreaker
|
||||||
if (ctx->checkBelAvail(sz) || (radius > ripup_radius || ctx->rng(20000) < 10)) {
|
if (ctx->checkBelAvail(sz) ||
|
||||||
|
(ctrl_set_group == -1 && (radius > ripup_radius || ctx->rng(20000) < 10))) {
|
||||||
CellInfo *bound = ctx->getBoundBelCell(sz);
|
CellInfo *bound = ctx->getBoundBelCell(sz);
|
||||||
if (bound != nullptr) {
|
if (bound != nullptr) {
|
||||||
// Only rip up cells without constraints
|
// Only rip up cells without constraints
|
||||||
|
|
@ -1117,13 +1330,15 @@ class HeAPPlacer
|
||||||
// New location is not legal; unbind the cell (and rebind the cell we ripped up if
|
// New location is not legal; unbind the cell (and rebind the cell we ripped up if
|
||||||
// applicable)
|
// applicable)
|
||||||
ctx->unbindBel(sz);
|
ctx->unbindBel(sz);
|
||||||
if (bound != nullptr)
|
if (bound != nullptr) {
|
||||||
ctx->bindBel(sz, bound, STRENGTH_WEAK);
|
ctx->bindBel(sz, bound, STRENGTH_WEAK);
|
||||||
} else if (iter_at_radius < need_to_explore) {
|
}
|
||||||
|
} else if (ctrl_set_group == -1 && iter_at_radius < need_to_explore) {
|
||||||
// It's legal, but we haven't tried enough locations yet
|
// It's legal, but we haven't tried enough locations yet
|
||||||
ctx->unbindBel(sz);
|
ctx->unbindBel(sz);
|
||||||
if (bound != nullptr)
|
if (bound != nullptr) {
|
||||||
ctx->bindBel(sz, bound, STRENGTH_WEAK);
|
ctx->bindBel(sz, bound, STRENGTH_WEAK);
|
||||||
|
}
|
||||||
int input_len = 0;
|
int input_len = 0;
|
||||||
// Compute a fast input wirelength metric at this bel; and save if better than our last
|
// Compute a fast input wirelength metric at this bel; and save if better than our last
|
||||||
// try
|
// try
|
||||||
|
|
@ -1146,11 +1361,14 @@ class HeAPPlacer
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
// It's legal, and we've tried enough. Finish.
|
// It's legal, and we've tried enough. Finish.
|
||||||
if (bound != nullptr)
|
if (bound != nullptr) {
|
||||||
|
p->unbind_ctrl_set(sz);
|
||||||
remaining.emplace(p->chain_size[bound->name] *
|
remaining.emplace(p->chain_size[bound->name] *
|
||||||
p->cfg.get_cell_legalisation_weight(ctx, bound),
|
p->cfg.get_cell_legalisation_weight(ctx, bound),
|
||||||
bound->name);
|
bound->name);
|
||||||
|
}
|
||||||
Loc loc = ctx->getBelLocation(sz);
|
Loc loc = ctx->getBelLocation(sz);
|
||||||
|
p->bind_ctrl_set(sz, ci->name);
|
||||||
p->cell_locs[ci->name].x = loc.x;
|
p->cell_locs[ci->name].x = loc.x;
|
||||||
p->cell_locs[ci->name].y = loc.y;
|
p->cell_locs[ci->name].y = loc.y;
|
||||||
placed = true;
|
placed = true;
|
||||||
|
|
@ -1160,7 +1378,7 @@ class HeAPPlacer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void try_place_cluster(CellInfo *ci, int nx, int ny)
|
void try_place_cluster(CellInfo *ci, int nx, int ny, int ctrl_set_group = -1)
|
||||||
{
|
{
|
||||||
// We do have relative constraints
|
// We do have relative constraints
|
||||||
for (auto sz : fb->at(nx).at(ny)) {
|
for (auto sz : fb->at(nx).at(ny)) {
|
||||||
|
|
@ -1172,19 +1390,30 @@ class HeAPPlacer
|
||||||
if (!ctx->getClusterPlacement(ci->cluster, sz, targets))
|
if (!ctx->getClusterPlacement(ci->cluster, sz, targets))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
bool ctrl_set_match = false;
|
||||||
|
|
||||||
for (auto &target : targets) {
|
for (auto &target : targets) {
|
||||||
// Check it satisfies the region constraint if applicable
|
// Check it satisfies the region constraint if applicable
|
||||||
if (!target.first->testRegion(target.second))
|
if (!target.first->testRegion(target.second))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
if (ctrl_set_group != -1 && ctx->getBelBucketForBel(target.second) == p->cfg.ff_bel_bucket &&
|
||||||
|
p->z_to_ctrl_set.at(ctx->getBelLocation(target.second).z) == ctrl_set_group)
|
||||||
|
ctrl_set_match = true;
|
||||||
CellInfo *bound = ctx->getBoundBelCell(target.second);
|
CellInfo *bound = ctx->getBoundBelCell(target.second);
|
||||||
// Chains cannot overlap; so if we have to ripup a cell make sure it isn't part of a chain
|
// Chains cannot overlap; so if we have to ripup a cell make sure it isn't part of a chain
|
||||||
if (bound != nullptr) {
|
if (bound != nullptr) {
|
||||||
|
if (ctrl_set_group != -1)
|
||||||
|
goto fail;
|
||||||
if (bound->belStrength > (p->cfg.chainRipup ? STRENGTH_STRONG : STRENGTH_WEAK))
|
if (bound->belStrength > (p->cfg.chainRipup ? STRENGTH_STRONG : STRENGTH_WEAK))
|
||||||
goto fail;
|
goto fail;
|
||||||
if (bound->cluster != ClusterId() && (!p->cfg.chainRipup || radius < chain_ripup_radius))
|
if (bound->cluster != ClusterId() && (!p->cfg.chainRipup || radius < chain_ripup_radius))
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ctrl_set_group != -1 && !ctrl_set_match)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
// Actually perform the move; keeping track of the moves we make so we can revert them if needed
|
// Actually perform the move; keeping track of the moves we make so we can revert them if needed
|
||||||
for (auto &target : targets) {
|
for (auto &target : targets) {
|
||||||
CellInfo *bound = ctx->getBoundBelCell(target.second);
|
CellInfo *bound = ctx->getBoundBelCell(target.second);
|
||||||
|
|
@ -1213,17 +1442,24 @@ class HeAPPlacer
|
||||||
fail:
|
fail:
|
||||||
// If the move turned out to be illegal; revert all the moves we made
|
// If the move turned out to be illegal; revert all the moves we made
|
||||||
for (auto &move : moves_made) {
|
for (auto &move : moves_made) {
|
||||||
if (ctx->getBoundBelCell(move.first))
|
if (ctx->getBoundBelCell(move.first)) {
|
||||||
ctx->unbindBel(move.first);
|
ctx->unbindBel(move.first);
|
||||||
if (move.second != nullptr)
|
}
|
||||||
|
if (move.second != nullptr) {
|
||||||
ctx->bindBel(move.first, move.second, STRENGTH_WEAK);
|
ctx->bindBel(move.first, move.second, STRENGTH_WEAK);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
for (auto &move : moves_made) {
|
||||||
|
if (move.second)
|
||||||
|
p->unbind_ctrl_set(move.first);
|
||||||
|
}
|
||||||
for (auto &target : targets) {
|
for (auto &target : targets) {
|
||||||
Loc loc = ctx->getBelLocation(target.second);
|
Loc loc = ctx->getBelLocation(target.second);
|
||||||
p->cell_locs[target.first->name].x = loc.x;
|
p->cell_locs[target.first->name].x = loc.x;
|
||||||
p->cell_locs[target.first->name].y = loc.y;
|
p->cell_locs[target.first->name].y = loc.y;
|
||||||
|
p->bind_ctrl_set(target.second, target.first->name);
|
||||||
// log_info("%s %d %d %d\n", target.first->name.c_str(ctx), loc.x, loc.y, loc.z);
|
// log_info("%s %d %d %d\n", target.first->name.c_str(ctx), loc.x, loc.y, loc.z);
|
||||||
}
|
}
|
||||||
for (auto &move : moves_made) {
|
for (auto &move : moves_made) {
|
||||||
|
|
@ -1931,6 +2167,7 @@ PlacerHeapCfg::PlacerHeapCfg(Context *ctx)
|
||||||
timingWeight = ctx->setting<int>("placerHeap/timingWeight");
|
timingWeight = ctx->setting<int>("placerHeap/timingWeight");
|
||||||
parallelRefine = ctx->setting<bool>("placerHeap/parallelRefine", false);
|
parallelRefine = ctx->setting<bool>("placerHeap/parallelRefine", false);
|
||||||
netShareWeight = ctx->setting<float>("placerHeap/netShareWeight", 0);
|
netShareWeight = ctx->setting<float>("placerHeap/netShareWeight", 0);
|
||||||
|
disableCtrlSet = ctx->setting<bool>("placerHeap/noCtrlSet", false);
|
||||||
|
|
||||||
timing_driven = ctx->setting<bool>("timing_driven");
|
timing_driven = ctx->setting<bool>("timing_driven");
|
||||||
solverTolerance = 1e-5;
|
solverTolerance = 1e-5;
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,35 @@ struct PlacerHeapCfg
|
||||||
|
|
||||||
// this is an optional callback to prioritise certain cells/clusters for legalisation
|
// this is an optional callback to prioritise certain cells/clusters for legalisation
|
||||||
std::function<float(Context *, CellInfo *)> get_cell_legalisation_weight = [](Context *, CellInfo *) { return 1; };
|
std::function<float(Context *, CellInfo *)> get_cell_legalisation_weight = [](Context *, CellInfo *) { return 1; };
|
||||||
|
|
||||||
|
bool disableCtrlSet;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Control set API
|
||||||
|
HeAP legalisation can be sped up by directly searching for nearby tiles to place an FF with a compatible control
|
||||||
|
set. Only one shared control set is currently supported, however, as a full validity check is always performed too,
|
||||||
|
this doesn't need to encompass every possible incompatibility (this is only for performance/QoR not correctness)
|
||||||
|
|
||||||
|
ff_bel_bucket is the bel bucket ID for the flipflop (or logic cell if combined with LUT) bel type
|
||||||
|
|
||||||
|
ff_control_set_groups contains the Z-location of flipflops in a control set group.
|
||||||
|
Each entry in this represents a SLICE, i.e. the set of flipflops that share the control set. In XC7 this would be
|
||||||
|
the two SLICEs in a tile.
|
||||||
|
|
||||||
|
get_cell_control_set should return a unique index for every control set possibility. i.e. if this function returns
|
||||||
|
the same value the flipflops could be placed in the same group.
|
||||||
|
*/
|
||||||
|
|
||||||
|
BelBucketId ff_bel_bucket = BelBucketId();
|
||||||
|
std::vector<std::vector<int>> ff_control_set_groups;
|
||||||
|
|
||||||
|
// ctrl_set_max_radius is specified as a schedule per iteration, in general this should decrease over time
|
||||||
|
std::vector<int> ctrl_set_max_radius;
|
||||||
|
|
||||||
|
// TODO: control sets might have a hierarchy, like ultrascale+ CE vs CLK/SR
|
||||||
|
std::function<int32_t(Context *, const CellInfo *)> get_cell_control_set = [](Context *, const CellInfo *) {
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
extern bool placer_heap(Context *ctx, PlacerHeapCfg cfg);
|
extern bool placer_heap(Context *ctx, PlacerHeapCfg cfg);
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,30 @@
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
struct FFControlSet
|
||||||
|
{
|
||||||
|
unsigned flags = 0;
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
IS_LATCH = 1,
|
||||||
|
IS_CLKINV = 2,
|
||||||
|
IS_SRINV = 4,
|
||||||
|
FFSYNC = 8,
|
||||||
|
};
|
||||||
|
IdString clk, sr, ce;
|
||||||
|
bool operator==(const FFControlSet &other) const
|
||||||
|
{
|
||||||
|
return flags == other.flags && clk == other.clk && ce == other.ce && sr == other.sr;
|
||||||
|
};
|
||||||
|
unsigned hash() const
|
||||||
|
{
|
||||||
|
unsigned hash = mkhash(clk.hash(), sr.hash());
|
||||||
|
hash = mkhash(hash, ce.hash());
|
||||||
|
hash = mkhash(hash, flags);
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
XilinxImpl::~XilinxImpl() {};
|
XilinxImpl::~XilinxImpl() {};
|
||||||
|
|
||||||
po::options_description XilinxImpl::getUArchOptions()
|
po::options_description XilinxImpl::getUArchOptions()
|
||||||
|
|
@ -274,7 +298,11 @@ bool XilinxImpl::is_pip_unavail(PipId pip) const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void XilinxImpl::prePlace() { assign_cell_tags(); }
|
void XilinxImpl::prePlace()
|
||||||
|
{
|
||||||
|
assign_cell_tags();
|
||||||
|
index_control_sets();
|
||||||
|
}
|
||||||
|
|
||||||
void XilinxImpl::postPlace()
|
void XilinxImpl::postPlace()
|
||||||
{
|
{
|
||||||
|
|
@ -295,6 +323,21 @@ void XilinxImpl::configurePlacerHeap(PlacerHeapCfg &cfg)
|
||||||
// Place memory first, because they require entire SLICEMs
|
// Place memory first, because they require entire SLICEMs
|
||||||
return tags->lut.is_memory ? 100 : 1;
|
return tags->lut.is_memory ? 100 : 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
cfg.ff_bel_bucket = id_SLICE_FFX;
|
||||||
|
cfg.ff_control_set_groups.resize(2);
|
||||||
|
for (int z = 0; z < 8; z++) {
|
||||||
|
cfg.ff_control_set_groups.at(z / 4).push_back((z << 4) | BEL_FF);
|
||||||
|
cfg.ff_control_set_groups.at(z / 4).push_back((z << 4) | BEL_FF2);
|
||||||
|
}
|
||||||
|
cfg.ctrl_set_max_radius = std::vector<int>{18, 15, 12, 9, 6, 3};
|
||||||
|
|
||||||
|
cfg.get_cell_control_set = [this](Context *, const CellInfo *ci) {
|
||||||
|
if (ci->type != id_SLICE_FFX)
|
||||||
|
return -1;
|
||||||
|
auto tags = get_tags(ci);
|
||||||
|
return tags->ff.control_set;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void XilinxImpl::configurePlacerStatic(PlacerStaticCfg &cfg)
|
void XilinxImpl::configurePlacerStatic(PlacerStaticCfg &cfg)
|
||||||
|
|
@ -542,6 +585,26 @@ void XilinxImpl::assign_cell_tags()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void XilinxImpl::index_control_sets()
|
||||||
|
{
|
||||||
|
idict<FFControlSet> control_sets;
|
||||||
|
for (auto &cell : ctx->cells) {
|
||||||
|
CellInfo *ci = cell.second.get();
|
||||||
|
if (ci->type == id_SLICE_FFX) {
|
||||||
|
auto &ct = cell_tags.at(ci->flat_index);
|
||||||
|
FFControlSet ctrl_set;
|
||||||
|
ctrl_set.clk = ct.ff.clk ? ct.ff.clk->name : IdString();
|
||||||
|
ctrl_set.ce = ct.ff.ce ? ct.ff.ce->name : IdString();
|
||||||
|
ctrl_set.sr = ct.ff.sr ? ct.ff.sr->name : IdString();
|
||||||
|
ctrl_set.flags = (ct.ff.is_clkinv ? FFControlSet::IS_CLKINV : 0) |
|
||||||
|
(ct.ff.is_srinv ? FFControlSet::IS_SRINV : 0) |
|
||||||
|
(ct.ff.is_latch ? FFControlSet::IS_LATCH : 0) | (ct.ff.ffsync ? FFControlSet::FFSYNC : 0);
|
||||||
|
ct.ff.control_set = control_sets(ctrl_set);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log_info("Indexed %d control sets.\n", int(control_sets.size()));
|
||||||
|
}
|
||||||
|
|
||||||
bool XilinxImpl::is_general_routing(WireId wire) const
|
bool XilinxImpl::is_general_routing(WireId wire) const
|
||||||
{
|
{
|
||||||
IdString intent = ctx->getWireType(wire);
|
IdString intent = ctx->getWireType(wire);
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ struct XilinxCellTags
|
||||||
bool is_latch, is_clkinv, is_srinv, ffsync;
|
bool is_latch, is_clkinv, is_srinv, ffsync;
|
||||||
bool is_paired;
|
bool is_paired;
|
||||||
NetInfo *clk, *sr, *ce, *d;
|
NetInfo *clk, *sr, *ce, *d;
|
||||||
|
int32_t control_set;
|
||||||
} ff;
|
} ff;
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
|
|
@ -183,6 +184,7 @@ struct XilinxImpl : HimbaechelAPI
|
||||||
private:
|
private:
|
||||||
HimbaechelHelpers h;
|
HimbaechelHelpers h;
|
||||||
void assign_cell_tags();
|
void assign_cell_tags();
|
||||||
|
void index_control_sets();
|
||||||
};
|
};
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue