Implement partitioning in placer_heap.

Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com>
This commit is contained in:
Keith Rothman 2021-01-28 18:32:42 -08:00
parent b4160c228e
commit 878fcdd22d
4 changed files with 247 additions and 134 deletions

View File

@ -28,8 +28,8 @@ NEXTPNR_NAMESPACE_BEGIN
// FastBels is a lookup class that provides a fast lookup for finding BELs // FastBels is a lookup class that provides a fast lookup for finding BELs
// that support a given cell type. // that support a given cell type.
struct FastBels { struct FastBels {
struct CellTypeData { struct TypeData {
size_t cell_type_index; size_t type_index;
size_t number_of_possible_bels; size_t number_of_possible_bels;
}; };
@ -44,10 +44,10 @@ struct FastBels {
size_t type_idx = cell_types.size(); size_t type_idx = cell_types.size();
auto &cell_type_data = cell_types[cell_type]; auto &cell_type_data = cell_types[cell_type];
cell_type_data.cell_type_index = type_idx; cell_type_data.type_index = type_idx;
fast_bels.resize(type_idx + 1); fast_bels_by_cell_type.resize(type_idx + 1);
auto &bel_data = fast_bels.at(type_idx); auto &bel_data = fast_bels_by_cell_type.at(type_idx);
for (auto bel : ctx->getBels()) { for (auto bel : ctx->getBels()) {
if(!ctx->isValidBelForCellType(cell_type, bel)) { if(!ctx->isValidBelForCellType(cell_type, bel)) {
@ -62,6 +62,10 @@ struct FastBels {
continue; continue;
} }
if(!ctx->isValidBelForCellType(cell_type, bel)) {
continue;
}
Loc loc = ctx->getBelLocation(bel); Loc loc = ctx->getBelLocation(bel);
if (minBelsForGridPick >= 0 && cell_type_data.number_of_possible_bels < minBelsForGridPick) { if (minBelsForGridPick >= 0 && cell_type_data.number_of_possible_bels < minBelsForGridPick) {
loc.x = loc.y = 0; loc.x = loc.y = 0;
@ -79,6 +83,54 @@ struct FastBels {
} }
} }
void addPartition(PartitionId partition) {
auto iter = partition_types.find(partition);
if(iter != partition_types.end()) {
// This partition has already been added to the fast BEL lookup.
return;
}
size_t type_idx = partition_types.size();
auto &type_data = partition_types[partition];
type_data.type_index = type_idx;
fast_bels_by_partition_type.resize(type_idx + 1);
auto &bel_data = fast_bels_by_partition_type.at(type_idx);
for (auto bel : ctx->getBels()) {
if(ctx->getPartitionForBel(bel) != partition) {
continue;
}
type_data.number_of_possible_bels += 1;
}
for (auto bel : ctx->getBels()) {
if(!ctx->checkBelAvail(bel)) {
continue;
}
if(ctx->getPartitionForBel(bel) != partition) {
continue;
}
Loc loc = ctx->getBelLocation(bel);
if (minBelsForGridPick >= 0 && type_data.number_of_possible_bels < minBelsForGridPick) {
loc.x = loc.y = 0;
}
if (int(bel_data.size()) < (loc.x + 1)) {
bel_data.resize(loc.x + 1);
}
if (int(bel_data.at(loc.x).size()) < (loc.y + 1)) {
bel_data.at(loc.x).resize(loc.y + 1);
}
bel_data.at(loc.x).at(loc.y).push_back(bel);
}
}
typedef std::vector<std::vector<std::vector<BelId>>> FastBelsData; typedef std::vector<std::vector<std::vector<BelId>>> FastBelsData;
size_t getBelsForCellType(IdString cell_type, FastBelsData **data) { size_t getBelsForCellType(IdString cell_type, FastBelsData **data) {
@ -91,15 +143,32 @@ struct FastBels {
auto cell_type_data = iter->second; auto cell_type_data = iter->second;
*data = &fast_bels.at(cell_type_data.cell_type_index); *data = &fast_bels_by_cell_type.at(cell_type_data.type_index);
return cell_type_data.number_of_possible_bels; return cell_type_data.number_of_possible_bels;
} }
size_t getBelsForPartition(PartitionId partition, FastBelsData **data) {
auto iter = partition_types.find(partition);
if(iter == partition_types.end()) {
addPartition(partition);
iter = partition_types.find(partition);
NPNR_ASSERT(iter != partition_types.end());
}
auto type_data = iter->second;
*data = &fast_bels_by_partition_type.at(type_data.type_index);
return type_data.number_of_possible_bels;
}
Context *ctx; Context *ctx;
int minBelsForGridPick; int minBelsForGridPick;
std::unordered_map<IdString, CellTypeData> cell_types; std::unordered_map<IdString, TypeData> cell_types;
std::vector<FastBelsData> fast_bels; std::vector<FastBelsData> fast_bels_by_cell_type;
std::unordered_map<PartitionId, TypeData> partition_types;
std::vector<FastBelsData> fast_bels_by_partition_type;
}; };
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END

View File

@ -50,6 +50,8 @@
#include "placer1.h" #include "placer1.h"
#include "timing.h" #include "timing.h"
#include "util.h" #include "util.h"
#include "fast_bels.h"
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
namespace { namespace {
@ -136,7 +138,7 @@ template <typename T> struct EquationSystem
class HeAPPlacer class HeAPPlacer
{ {
public: public:
HeAPPlacer(Context *ctx, PlacerHeapCfg cfg) : ctx(ctx), cfg(cfg) { Eigen::initParallel(); } HeAPPlacer(Context *ctx, PlacerHeapCfg cfg) : ctx(ctx), cfg(cfg), fast_bels(ctx, -1) { Eigen::initParallel(); }
bool place() bool place()
{ {
@ -144,7 +146,7 @@ class HeAPPlacer
ctx->lock(); ctx->lock();
place_constraints(); place_constraints();
build_fast_bels(); setup_grid();
seed_placement(); seed_placement();
update_all_chains(); update_all_chains();
wirelen_t hpwl = total_hpwl(); wirelen_t hpwl = total_hpwl();
@ -175,24 +177,26 @@ class HeAPPlacer
std::vector<std::tuple<CellInfo *, BelId, PlaceStrength>> solution; std::vector<std::tuple<CellInfo *, BelId, PlaceStrength>> solution;
std::vector<std::unordered_set<IdString>> heap_runs; std::vector<std::unordered_set<PartitionId>> heap_runs;
std::unordered_set<IdString> all_celltypes; std::unordered_set<PartitionId> all_partitions;
std::unordered_map<IdString, int> ct_count; std::unordered_map<PartitionId, int> partition_count;
for (auto cell : place_cells) { for (auto cell : place_cells) {
if (!all_celltypes.count(cell->type)) { PartitionId partition = ctx->getPartitionForCellType(cell->type);
heap_runs.push_back(std::unordered_set<IdString>{cell->type}); if (!all_partitions.count(partition)) {
all_celltypes.insert(cell->type); heap_runs.push_back(std::unordered_set<PartitionId>{partition});
all_partitions.insert(partition);
} }
ct_count[cell->type]++; partition_count[cell->type]++;
} }
// If more than 98% of cells are one cell type, always solve all at once // If more than 98% of cells are one cell type, always solve all at once
// Otherwise, follow full HeAP strategy of rotate&all // Otherwise, follow full HeAP strategy of rotate&all
for (auto &c : ct_count) for (auto &c : partition_count) {
if (c.second >= 0.98 * int(place_cells.size())) { if (c.second >= 0.98 * int(place_cells.size())) {
heap_runs.clear(); heap_runs.clear();
break; break;
} }
}
if (cfg.placeAllAtOnce) { if (cfg.placeAllAtOnce) {
// Never want to deal with LUTs, FFs, MUXFxs separately, // Never want to deal with LUTs, FFs, MUXFxs separately,
@ -201,7 +205,7 @@ class HeAPPlacer
heap_runs.clear(); heap_runs.clear();
} }
heap_runs.push_back(all_celltypes); heap_runs.push_back(all_partitions);
// The main HeAP placer loop // The main HeAP placer loop
log_info("Running main analytical placer.\n"); log_info("Running main analytical placer.\n");
while (stalled < 5 && (solved_hpwl <= legal_hpwl * 0.8)) { while (stalled < 5 && (solved_hpwl <= legal_hpwl * 0.8)) {
@ -238,7 +242,7 @@ class HeAPPlacer
for (auto type : sorted(run)) for (auto type : sorted(run))
if (std::all_of(cfg.cellGroups.begin(), cfg.cellGroups.end(), if (std::all_of(cfg.cellGroups.begin(), cfg.cellGroups.end(),
[type](const std::unordered_set<IdString> &grp) { return !grp.count(type); })) [type](const std::unordered_set<PartitionId> &grp) { return !grp.count(type); }))
CutSpreader(this, {type}).run(); CutSpreader(this, {type}).run();
update_all_chains(); update_all_chains();
@ -321,13 +325,6 @@ class HeAPPlacer
FastBels fast_bels; FastBels fast_bels;
std::unordered_map<IdString, std::tuple<int, int>> bel_types; std::unordered_map<IdString, std::tuple<int, int>> bel_types;
// For fast handling of heterogeneity during initial placement without full legalisation,
// for each Bel type this goes from x or y to the nearest x or y where a Bel of a given type exists
// This is particularly important for the iCE40 architecture, where multipliers and BRAM only exist at the
// edges and corners respectively
std::vector<std::vector<int>> nearest_row_with_bel;
std::vector<std::vector<int>> nearest_col_with_bel;
struct BoundingBox struct BoundingBox
{ {
// Actual bounding box // Actual bounding box
@ -411,8 +408,7 @@ class HeAPPlacer
ctx->yield(); ctx->yield();
} }
// Construct the fast_bels, nearest_row_with_bel and nearest_col_with_bel void setup_grid()
void build_fast_bels()
{ {
for (auto bel : ctx->getBels()) { for (auto bel : ctx->getBels()) {
if (!ctx->checkBelAvail(bel)) if (!ctx->checkBelAvail(bel))
@ -422,38 +418,6 @@ class HeAPPlacer
max_y = std::max(max_y, loc.y); max_y = std::max(max_y, loc.y);
} }
nearest_row_with_bel.resize(num_bel_types, std::vector<int>(max_y + 1, -1));
nearest_col_with_bel.resize(num_bel_types, std::vector<int>(max_x + 1, -1));
for (auto bel : ctx->getBels()) {
if (!ctx->checkBelAvail(bel))
continue;
Loc loc = ctx->getBelLocation(bel);
int type_idx = std::get<0>(bel_types.at(ctx->getBelType(bel)));
auto &nr = nearest_row_with_bel.at(type_idx), &nc = nearest_col_with_bel.at(type_idx);
// Traverse outwards through nearest_row_with_bel and nearest_col_with_bel, stopping once
// another row/col is already recorded as being nearer
for (int x = loc.x; x <= max_x; x++) {
if (nc.at(x) != -1 && std::abs(loc.x - nc.at(x)) <= (x - loc.x))
break;
nc.at(x) = loc.x;
}
for (int x = loc.x - 1; x >= 0; x--) {
if (nc.at(x) != -1 && std::abs(loc.x - nc.at(x)) <= (loc.x - x))
break;
nc.at(x) = loc.x;
}
for (int y = loc.y; y <= max_y; y++) {
if (nr.at(y) != -1 && std::abs(loc.y - nr.at(y)) <= (y - loc.y))
break;
nr.at(y) = loc.y;
}
for (int y = loc.y - 1; y >= 0; y--) {
if (nr.at(y) != -1 && std::abs(loc.y - nr.at(y)) <= (loc.y - y))
break;
nr.at(y) = loc.y;
}
}
// Determine bounding boxes of region constraints // Determine bounding boxes of region constraints
for (auto &region : sorted(ctx->region)) { for (auto &region : sorted(ctx->region)) {
Region *r = region.second; Region *r = region.second;
@ -505,15 +469,30 @@ class HeAPPlacer
// FIXME: Are there better approaches to the initial placement (e.g. greedy?) // FIXME: Are there better approaches to the initial placement (e.g. greedy?)
void seed_placement() void seed_placement()
{ {
std::unordered_map<IdString, std::deque<BelId>> available_bels; std::unordered_set<IdString> cell_types;
for (auto bel : ctx->getBels()) { for (const auto &cell : ctx->cells) {
if (!ctx->checkBelAvail(bel)) cell_types.insert(cell.second->type);
continue;
available_bels[ctx->getBelType(bel)].push_back(bel);
} }
std::unordered_set<BelId> bels_used;
std::unordered_map<IdString, std::deque<BelId>> available_bels;
for (auto bel : ctx->getBels()) {
if (!ctx->checkBelAvail(bel)) {
continue;
}
for(auto cell_type : cell_types) {
if(ctx->isValidBelForCellType(cell_type, bel)) {
available_bels[cell_type].push_back(bel);
}
}
}
for (auto &t : available_bels) { for (auto &t : available_bels) {
ctx->shuffle(t.second.begin(), t.second.end()); ctx->shuffle(t.second.begin(), t.second.end());
} }
for (auto cell : sorted(ctx->cells)) { for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second; CellInfo *ci = cell.second;
if (ci->bel != BelId()) { if (ci->bel != BelId()) {
@ -526,21 +505,41 @@ class HeAPPlacer
bool placed = false; bool placed = false;
int attempt_count = 0; int attempt_count = 0;
while (!placed) { while (!placed) {
if (!available_bels.count(ci->type) || available_bels.at(ci->type).empty()) if (!available_bels.count(ci->type) || available_bels.at(ci->type).empty()) {
log_error("Unable to place cell '%s', no Bels remaining of type '%s'\n", ci->name.c_str(ctx), log_error("Unable to place cell '%s', no BELs remaining to implement cell type '%s'\n",
ci->name.c_str(ctx),
ci->type.c_str(ctx)); ci->type.c_str(ctx));
}
++attempt_count; ++attempt_count;
if (attempt_count > 25000) if (attempt_count > 25000) {
log_error("Unable to find a placement location for cell '%s'\n", ci->name.c_str(ctx)); log_error("Unable to find a placement location for cell '%s'\n", ci->name.c_str(ctx));
BelId bel = available_bels.at(ci->type).back(); }
available_bels.at(ci->type).pop_back();
// Find an unused BEL from bels_for_cell_type.
auto &bels_for_cell_type = available_bels.at(ci->type);
BelId bel;
while(true) {
BelId candidate_bel = bels_for_cell_type.back();
bels_for_cell_type.pop_back();
if(bels_used.count(candidate_bel)) {
// candidate_bel has already been used by another
// cell type, skip it.
continue;
}
bel = candidate_bel;
break;
}
Loc loc = ctx->getBelLocation(bel); Loc loc = ctx->getBelLocation(bel);
cell_locs[cell.first].x = loc.x; cell_locs[cell.first].x = loc.x;
cell_locs[cell.first].y = loc.y; cell_locs[cell.first].y = loc.y;
cell_locs[cell.first].locked = false; cell_locs[cell.first].locked = false;
cell_locs[cell.first].global = ctx->getBelGlobalBuf(bel); cell_locs[cell.first].global = ctx->getBelGlobalBuf(bel);
// FIXME // FIXME
if (has_connectivity(cell.second) && !cfg.ioBufTypes.count(ci->type)) { if (has_connectivity(cell.second) && !cfg.ioBufTypes.count(ci->type)) {
bels_used.insert(bel);
place_cells.push_back(ci); place_cells.push_back(ci);
placed = true; placed = true;
} else { } else {
@ -548,6 +547,7 @@ class HeAPPlacer
ctx->bindBel(bel, ci, STRENGTH_STRONG); ctx->bindBel(bel, ci, STRENGTH_STRONG);
cell_locs[cell.first].locked = true; cell_locs[cell.first].locked = true;
placed = true; placed = true;
bels_used.insert(bel);
} else { } else {
available_bels.at(ci->type).push_front(bel); available_bels.at(ci->type).push_front(bel);
} }
@ -867,9 +867,6 @@ class HeAPPlacer
if (ny < 0 || ny > max_y) if (ny < 0 || ny > max_y)
continue; continue;
// ny = nearest_row_with_bel.at(bt).at(ny);
// nx = nearest_col_with_bel.at(bt).at(nx);
if (nx >= int(fb->size())) if (nx >= int(fb->size()))
continue; continue;
if (ny >= int(fb->at(nx).size())) if (ny >= int(fb->at(nx).size()))
@ -961,7 +958,7 @@ class HeAPPlacer
if (vc->region != nullptr && vc->region->constr_bels && !vc->region->bels.count(target)) if (vc->region != nullptr && vc->region->constr_bels && !vc->region->bels.count(target))
goto fail; goto fail;
CellInfo *bound; CellInfo *bound;
if (target == BelId() || ctx->getBelType(target) != vc->type) if (target == BelId() || ctx->isValidBelForCellType(vc->type, target))
goto fail; goto fail;
bound = ctx->getBoundBelCell(target); bound = ctx->getBoundBelCell(target);
// Chains cannot overlap // Chains cannot overlap
@ -1063,13 +1060,17 @@ class HeAPPlacer
class CutSpreader class CutSpreader
{ {
public: public:
CutSpreader(HeAPPlacer *p, const std::unordered_set<IdString> &beltype) : p(p), ctx(p->ctx), beltype(beltype) CutSpreader(HeAPPlacer *p, const std::unordered_set<PartitionId> &partitions) : p(p), ctx(p->ctx), partitions(partitions)
{ {
// Get fast BELs data for all partitions being Cut/Spread.
int idx = 0; int idx = 0;
for (IdString type : sorted(beltype)) { for (PartitionId partition : sorted(partitions)) {
type_index[type] = idx; type_index[partition] = idx;
fb.emplace_back(&(p->fast_bels.at(std::get<0>(p->bel_types.at(type))))); FastBels::FastBelsData *fast_bels;
p->fast_bels.getBelsForPartition(partition, &fast_bels);
fb.push_back(fast_bels);
++idx; ++idx;
NPNR_ASSERT(fb.size() == idx);
} }
} }
static int seq; static int seq;
@ -1151,8 +1152,8 @@ class HeAPPlacer
private: private:
HeAPPlacer *p; HeAPPlacer *p;
Context *ctx; Context *ctx;
std::unordered_set<IdString> beltype; std::unordered_set<PartitionId> partitions;
std::unordered_map<IdString, int> type_index; std::unordered_map<PartitionId, size_t> type_index;
std::vector<std::vector<std::vector<int>>> occupancy; std::vector<std::vector<std::vector<int>>> occupancy;
std::vector<std::vector<int>> groups; std::vector<std::vector<int>> groups;
std::vector<std::vector<ChainExtent>> chaines; std::vector<std::vector<ChainExtent>> chaines;
@ -1174,16 +1175,24 @@ class HeAPPlacer
return int(fb.at(type)->at(x).at(y).size()); return int(fb.at(type)->at(x).at(y).size());
} }
bool is_cell_fixed(const CellInfo & cell) const {
return partitions.count(ctx->getPartitionForCellType(cell.type)) == 0;
}
size_t cell_index(const CellInfo & cell) const {
return type_index.at(ctx->getPartitionForCellType(cell.type));
}
void init() void init()
{ {
occupancy.resize(p->max_x + 1, occupancy.resize(p->max_x + 1,
std::vector<std::vector<int>>(p->max_y + 1, std::vector<int>(beltype.size(), 0))); std::vector<std::vector<int>>(p->max_y + 1, std::vector<int>(partitions.size(), 0)));
groups.resize(p->max_x + 1, std::vector<int>(p->max_y + 1, -1)); groups.resize(p->max_x + 1, std::vector<int>(p->max_y + 1, -1));
chaines.resize(p->max_x + 1, std::vector<ChainExtent>(p->max_y + 1)); chaines.resize(p->max_x + 1, std::vector<ChainExtent>(p->max_y + 1));
cells_at_location.resize(p->max_x + 1, std::vector<std::vector<CellInfo *>>(p->max_y + 1)); cells_at_location.resize(p->max_x + 1, std::vector<std::vector<CellInfo *>>(p->max_y + 1));
for (int x = 0; x <= p->max_x; x++) for (int x = 0; x <= p->max_x; x++)
for (int y = 0; y <= p->max_y; y++) { for (int y = 0; y <= p->max_y; y++) {
for (int t = 0; t < int(beltype.size()); t++) { for (int t = 0; t < int(partitions.size()); t++) {
occupancy.at(x).at(y).at(t) = 0; occupancy.at(x).at(y).at(t) = 0;
} }
groups.at(x).at(y) = -1; groups.at(x).at(y) = -1;
@ -1201,55 +1210,77 @@ class HeAPPlacer
} }
}; };
for (auto &cell : p->cell_locs) { for (auto &cell_loc : p->cell_locs) {
if (!beltype.count(ctx->cells.at(cell.first)->type)) IdString cell_name = cell_loc.first;
const CellInfo & cell = *ctx->cells.at(cell_name);
const CellLocation & loc = cell_loc.second;
if(is_cell_fixed(cell)) {
continue; continue;
if (ctx->cells.at(cell.first)->belStrength > STRENGTH_STRONG) }
if (cell.belStrength > STRENGTH_STRONG) {
continue; continue;
occupancy.at(cell.second.x).at(cell.second.y).at(type_index.at(ctx->cells.at(cell.first)->type))++; }
occupancy.at(cell_loc.second.x).at(cell_loc.second.y).at(cell_index(cell))++;
// Compute ultimate extent of each chain root // Compute ultimate extent of each chain root
if (p->chain_root.count(cell.first)) { if (p->chain_root.count(cell_name)) {
set_chain_ext(p->chain_root.at(cell.first)->name, cell.second.x, cell.second.y); set_chain_ext(p->chain_root.at(cell_name)->name, loc.x, loc.y);
} else if (!ctx->cells.at(cell.first)->constr_children.empty()) { } else if (!ctx->cells.at(cell_name)->constr_children.empty()) {
set_chain_ext(cell.first, cell.second.x, cell.second.y); set_chain_ext(cell_name, loc.x, loc.y);
} }
} }
for (auto &cell : p->cell_locs) {
if (!beltype.count(ctx->cells.at(cell.first)->type)) for (auto &cell_loc : p->cell_locs) {
IdString cell_name = cell_loc.first;
const CellInfo & cell = *ctx->cells.at(cell_name);
const CellLocation & loc = cell_loc.second;
if(is_cell_fixed(cell)) {
continue; continue;
// Transfer chain extents to the actual chaines structure }
// Transfer chain extents to the actual chains structure
ChainExtent *ce = nullptr; ChainExtent *ce = nullptr;
if (p->chain_root.count(cell.first)) if (p->chain_root.count(cell_name)) {
ce = &(cell_extents.at(p->chain_root.at(cell.first)->name)); ce = &(cell_extents.at(p->chain_root.at(cell_name)->name));
else if (!ctx->cells.at(cell.first)->constr_children.empty()) } else if (!ctx->cells.at(cell_name)->constr_children.empty()) {
ce = &(cell_extents.at(cell.first)); ce = &(cell_extents.at(cell_name));
}
if (ce) { if (ce) {
auto &lce = chaines.at(cell.second.x).at(cell.second.y); auto &lce = chaines.at(loc.x).at(loc.y);
lce.x0 = std::min(lce.x0, ce->x0); lce.x0 = std::min(lce.x0, ce->x0);
lce.y0 = std::min(lce.y0, ce->y0); lce.y0 = std::min(lce.y0, ce->y0);
lce.x1 = std::max(lce.x1, ce->x1); lce.x1 = std::max(lce.x1, ce->x1);
lce.y1 = std::max(lce.y1, ce->y1); lce.y1 = std::max(lce.y1, ce->y1);
} }
} }
for (auto cell : p->solve_cells) { for (auto cell : p->solve_cells) {
if (!beltype.count(cell->type)) if(is_cell_fixed(*cell)) {
continue; continue;
}
cells_at_location.at(p->cell_locs.at(cell->name).x).at(p->cell_locs.at(cell->name).y).push_back(cell); cells_at_location.at(p->cell_locs.at(cell->name).x).at(p->cell_locs.at(cell->name).y).push_back(cell);
} }
} }
void merge_regions(SpreaderRegion &merged, SpreaderRegion &mergee) void merge_regions(SpreaderRegion &merged, SpreaderRegion &mergee)
{ {
// Prevent grow_region from recursing while doing this // Prevent grow_region from recursing while doing this
for (int x = mergee.x0; x <= mergee.x1; x++) for (int x = mergee.x0; x <= mergee.x1; x++) {
for (int y = mergee.y0; y <= mergee.y1; y++) { for (int y = mergee.y0; y <= mergee.y1; y++) {
// log_info("%d %d\n", groups.at(x).at(y), mergee.id); // log_info("%d %d\n", groups.at(x).at(y), mergee.id);
NPNR_ASSERT(groups.at(x).at(y) == mergee.id); NPNR_ASSERT(groups.at(x).at(y) == mergee.id);
groups.at(x).at(y) = merged.id; groups.at(x).at(y) = merged.id;
for (size_t t = 0; t < beltype.size(); t++) { for (size_t t = 0; t < partitions.size(); t++) {
merged.cells.at(t) += occ_at(x, y, t); merged.cells.at(t) += occ_at(x, y, t);
merged.bels.at(t) += bels_at(x, y, t); merged.bels.at(t) += bels_at(x, y, t);
} }
} }
}
merged_regions.insert(mergee.id); merged_regions.insert(mergee.id);
grow_region(merged, mergee.x0, mergee.y0, mergee.x1, mergee.y1); grow_region(merged, mergee.x0, mergee.y0, mergee.x1, mergee.y1);
} }
@ -1268,11 +1299,12 @@ class HeAPPlacer
auto process_location = [&](int x, int y) { auto process_location = [&](int x, int y) {
// Merge with any overlapping regions // Merge with any overlapping regions
if (groups.at(x).at(y) == -1) { if (groups.at(x).at(y) == -1) {
for (int t = 0; t < int(beltype.size()); t++) { for (size_t t = 0; t < partitions.size(); t++) {
r.bels.at(t) += bels_at(x, y, t); r.bels.at(t) += bels_at(x, y, t);
r.cells.at(t) += occ_at(x, y, t); r.cells.at(t) += occ_at(x, y, t);
} }
} }
if (groups.at(x).at(y) != -1 && groups.at(x).at(y) != r.id) if (groups.at(x).at(y) != -1 && groups.at(x).at(y) != r.id)
merge_regions(r, regions.at(groups.at(x).at(y))); merge_regions(r, regions.at(groups.at(x).at(y)));
groups.at(x).at(y) = r.id; groups.at(x).at(y) = r.id;
@ -1302,12 +1334,13 @@ class HeAPPlacer
if (groups.at(x).at(y) != -1) if (groups.at(x).at(y) != -1)
continue; continue;
bool overutilised = false; bool overutilised = false;
for (size_t t = 0; t < beltype.size(); t++) { for (size_t t = 0; t < partitions.size(); t++) {
if (occ_at(x, y, t) > bels_at(x, y, t)) { if (occ_at(x, y, t) > bels_at(x, y, t)) {
overutilised = true; overutilised = true;
break; break;
} }
} }
if (!overutilised) if (!overutilised)
continue; continue;
// log_info("%d %d %d\n", x, y, occ_at(x, y)); // log_info("%d %d %d\n", x, y, occ_at(x, y));
@ -1317,7 +1350,7 @@ class HeAPPlacer
reg.id = id; reg.id = id;
reg.x0 = reg.x1 = x; reg.x0 = reg.x1 = x;
reg.y0 = reg.y1 = y; reg.y0 = reg.y1 = y;
for (size_t t = 0; t < beltype.size(); t++) { for (size_t t = 0; t < partitions.size(); t++) {
reg.bels.push_back(bels_at(x, y, t)); reg.bels.push_back(bels_at(x, y, t));
reg.cells.push_back(occ_at(x, y, t)); reg.cells.push_back(occ_at(x, y, t));
} }
@ -1334,7 +1367,7 @@ class HeAPPlacer
if (reg.x1 < p->max_x) { if (reg.x1 < p->max_x) {
bool over_occ_x = false; bool over_occ_x = false;
for (int y1 = reg.y0; y1 <= reg.y1; y1++) { for (int y1 = reg.y0; y1 <= reg.y1; y1++) {
for (size_t t = 0; t < beltype.size(); t++) { for (size_t t = 0; t < partitions.size(); t++) {
if (occ_at(reg.x1 + 1, y1, t) > bels_at(reg.x1 + 1, y1, t)) { if (occ_at(reg.x1 + 1, y1, t) > bels_at(reg.x1 + 1, y1, t)) {
over_occ_x = true; over_occ_x = true;
break; break;
@ -1350,7 +1383,7 @@ class HeAPPlacer
if (reg.y1 < p->max_y) { if (reg.y1 < p->max_y) {
bool over_occ_y = false; bool over_occ_y = false;
for (int x1 = reg.x0; x1 <= reg.x1; x1++) { for (int x1 = reg.x0; x1 <= reg.x1; x1++) {
for (size_t t = 0; t < beltype.size(); t++) { for (size_t t = 0; t < partitions.size(); t++) {
if (occ_at(x1, reg.y1 + 1, t) > bels_at(x1, reg.y1 + 1, t)) { if (occ_at(x1, reg.y1 + 1, t) > bels_at(x1, reg.y1 + 1, t)) {
over_occ_y = true; over_occ_y = true;
break; break;
@ -1412,10 +1445,13 @@ class HeAPPlacer
} }
} }
if (!changed) { if (!changed) {
for (auto bt : sorted(beltype)) { for (auto partition : sorted(partitions)) {
if (reg.cells > reg.bels) if (reg.cells > reg.bels) {
IdString partition_name = ctx->getPartitionName(partition);
log_error("Failed to expand region (%d, %d) |_> (%d, %d) of %d %ss\n", reg.x0, reg.y0, log_error("Failed to expand region (%d, %d) |_> (%d, %d) of %d %ss\n", reg.x0, reg.y0,
reg.x1, reg.y1, reg.cells.at(type_index.at(bt)), bt.c_str(ctx)); reg.x1, reg.y1, reg.cells.at(type_index.at(partition)),
partition_name.c_str(ctx));
}
} }
break; break;
} }
@ -1436,7 +1472,7 @@ class HeAPPlacer
for (int x = r.x0; x <= r.x1; x++) { for (int x = r.x0; x <= r.x1; x++) {
for (int y = r.y0; y <= r.y1; y++) { for (int y = r.y0; y <= r.y1; y++) {
std::copy(cal.at(x).at(y).begin(), cal.at(x).at(y).end(), std::back_inserter(cut_cells)); std::copy(cal.at(x).at(y).begin(), cal.at(x).at(y).end(), std::back_inserter(cut_cells));
for (size_t t = 0; t < beltype.size(); t++) for (size_t t = 0; t < partitions.size(); t++)
total_bels += bels_at(x, y, t); total_bels += bels_at(x, y, t);
} }
} }
@ -1488,26 +1524,34 @@ class HeAPPlacer
int trimmed_l = dir ? r.y0 : r.x0, trimmed_r = dir ? r.y1 : r.x1; int trimmed_l = dir ? r.y0 : r.x0, trimmed_r = dir ? r.y1 : r.x1;
while (trimmed_l < (dir ? r.y1 : r.x1)) { while (trimmed_l < (dir ? r.y1 : r.x1)) {
bool have_bels = false; bool have_bels = false;
for (int i = dir ? r.x0 : r.y0; i <= (dir ? r.x1 : r.y1); i++) for (int i = dir ? r.x0 : r.y0; i <= (dir ? r.x1 : r.y1); i++) {
for (size_t t = 0; t < beltype.size(); t++) for (size_t t = 0; t < partitions.size(); t++) {
if (bels_at(dir ? i : trimmed_l, dir ? trimmed_l : i, t) > 0) { if (bels_at(dir ? i : trimmed_l, dir ? trimmed_l : i, t) > 0) {
have_bels = true; have_bels = true;
break; break;
} }
}
}
if (have_bels) if (have_bels)
break; break;
trimmed_l++; trimmed_l++;
} }
while (trimmed_r > (dir ? r.y0 : r.x0)) { while (trimmed_r > (dir ? r.y0 : r.x0)) {
bool have_bels = false; bool have_bels = false;
for (int i = dir ? r.x0 : r.y0; i <= (dir ? r.x1 : r.y1); i++) for (int i = dir ? r.x0 : r.y0; i <= (dir ? r.x1 : r.y1); i++) {
for (size_t t = 0; t < beltype.size(); t++) for (size_t t = 0; t < partitions.size(); t++) {
if (bels_at(dir ? i : trimmed_r, dir ? trimmed_r : i, t) > 0) { if (bels_at(dir ? i : trimmed_r, dir ? trimmed_r : i, t) > 0) {
have_bels = true; have_bels = true;
break; break;
} }
}
}
if (have_bels) if (have_bels)
break; break;
trimmed_r--; trimmed_r--;
} }
// log_info("tl %d tr %d cl %d cr %d\n", trimmed_l, trimmed_r, clearance_l, clearance_r); // log_info("tl %d tr %d cl %d cr %d\n", trimmed_l, trimmed_r, clearance_l, clearance_r);
@ -1515,27 +1559,27 @@ class HeAPPlacer
return {}; return {};
// Now find the initial target cut that minimises utilisation imbalance, whilst // Now find the initial target cut that minimises utilisation imbalance, whilst
// meeting the clearance requirements for any large macros // meeting the clearance requirements for any large macros
std::vector<int> left_cells_v(beltype.size(), 0), right_cells_v(beltype.size(), 0); std::vector<int> left_cells_v(partitions.size(), 0), right_cells_v(partitions.size(), 0);
std::vector<int> left_bels_v(beltype.size(), 0), right_bels_v(r.bels); std::vector<int> left_bels_v(partitions.size(), 0), right_bels_v(r.bels);
for (int i = 0; i <= pivot; i++) for (int i = 0; i <= pivot; i++)
left_cells_v.at(type_index.at(cut_cells.at(i)->type)) += left_cells_v.at(cell_index(*cut_cells.at(i))) +=
p->chain_size.count(cut_cells.at(i)->name) ? p->chain_size.at(cut_cells.at(i)->name) : 1; p->chain_size.count(cut_cells.at(i)->name) ? p->chain_size.at(cut_cells.at(i)->name) : 1;
for (int i = pivot + 1; i < int(cut_cells.size()); i++) for (int i = pivot + 1; i < int(cut_cells.size()); i++)
right_cells_v.at(type_index.at(cut_cells.at(i)->type)) += right_cells_v.at(cell_index(*cut_cells.at(i))) +=
p->chain_size.count(cut_cells.at(i)->name) ? p->chain_size.at(cut_cells.at(i)->name) : 1; p->chain_size.count(cut_cells.at(i)->name) ? p->chain_size.at(cut_cells.at(i)->name) : 1;
int best_tgt_cut = -1; int best_tgt_cut = -1;
double best_deltaU = std::numeric_limits<double>::max(); double best_deltaU = std::numeric_limits<double>::max();
// std::pair<int, int> target_cut_bels; // std::pair<int, int> target_cut_bels;
std::vector<int> slither_bels(beltype.size(), 0); std::vector<int> slither_bels(partitions.size(), 0);
for (int i = trimmed_l; i <= trimmed_r; i++) { for (int i = trimmed_l; i <= trimmed_r; i++) {
for (size_t t = 0; t < beltype.size(); t++) for (size_t t = 0; t < partitions.size(); t++)
slither_bels.at(t) = 0; slither_bels.at(t) = 0;
for (int j = dir ? r.x0 : r.y0; j <= (dir ? r.x1 : r.y1); j++) { for (int j = dir ? r.x0 : r.y0; j <= (dir ? r.x1 : r.y1); j++) {
for (size_t t = 0; t < beltype.size(); t++) for (size_t t = 0; t < partitions.size(); t++)
slither_bels.at(t) += dir ? bels_at(j, i, t) : bels_at(i, j, t); slither_bels.at(t) += dir ? bels_at(j, i, t) : bels_at(i, j, t);
} }
for (size_t t = 0; t < beltype.size(); t++) { for (size_t t = 0; t < partitions.size(); t++) {
left_bels_v.at(t) += slither_bels.at(t); left_bels_v.at(t) += slither_bels.at(t);
right_bels_v.at(t) -= slither_bels.at(t); right_bels_v.at(t) -= slither_bels.at(t);
} }
@ -1543,7 +1587,7 @@ class HeAPPlacer
if (((i - trimmed_l) + 1) >= clearance_l && ((trimmed_r - i) + 1) >= clearance_r) { if (((i - trimmed_l) + 1) >= clearance_l && ((trimmed_r - i) + 1) >= clearance_r) {
// Solution is potentially valid // Solution is potentially valid
double aU = 0; double aU = 0;
for (size_t t = 0; t < beltype.size(); t++) for (size_t t = 0; t < partitions.size(); t++)
aU += (left_cells_v.at(t) + right_cells_v.at(t)) * aU += (left_cells_v.at(t) + right_cells_v.at(t)) *
std::abs(double(left_cells_v.at(t)) / double(std::max(left_bels_v.at(t), 1)) - std::abs(double(left_cells_v.at(t)) / double(std::max(left_bels_v.at(t), 1)) -
double(right_cells_v.at(t)) / double(std::max(right_bels_v.at(t), 1))); double(right_cells_v.at(t)) / double(std::max(right_bels_v.at(t), 1)));
@ -1557,19 +1601,19 @@ class HeAPPlacer
return {}; return {};
// left_bels = target_cut_bels.first; // left_bels = target_cut_bels.first;
// right_bels = target_cut_bels.second; // right_bels = target_cut_bels.second;
for (size_t t = 0; t < beltype.size(); t++) { for (size_t t = 0; t < partitions.size(); t++) {
left_bels_v.at(t) = 0; left_bels_v.at(t) = 0;
right_bels_v.at(t) = 0; right_bels_v.at(t) = 0;
} }
for (int x = r.x0; x <= (dir ? r.x1 : best_tgt_cut); x++) for (int x = r.x0; x <= (dir ? r.x1 : best_tgt_cut); x++)
for (int y = r.y0; y <= (dir ? best_tgt_cut : r.y1); y++) { for (int y = r.y0; y <= (dir ? best_tgt_cut : r.y1); y++) {
for (size_t t = 0; t < beltype.size(); t++) { for (size_t t = 0; t < partitions.size(); t++) {
left_bels_v.at(t) += bels_at(x, y, t); left_bels_v.at(t) += bels_at(x, y, t);
} }
} }
for (int x = dir ? r.x0 : (best_tgt_cut + 1); x <= r.x1; x++) for (int x = dir ? r.x0 : (best_tgt_cut + 1); x <= r.x1; x++)
for (int y = dir ? (best_tgt_cut + 1) : r.y0; y <= r.y1; y++) { for (int y = dir ? (best_tgt_cut + 1) : r.y0; y <= r.y1; y++) {
for (size_t t = 0; t < beltype.size(); t++) { for (size_t t = 0; t < partitions.size(); t++) {
right_bels_v.at(t) += bels_at(x, y, t); right_bels_v.at(t) += bels_at(x, y, t);
} }
} }
@ -1589,15 +1633,15 @@ class HeAPPlacer
while (pivot > 0 && is_part_overutil(false)) { while (pivot > 0 && is_part_overutil(false)) {
auto &move_cell = cut_cells.at(pivot); auto &move_cell = cut_cells.at(pivot);
int size = p->chain_size.count(move_cell->name) ? p->chain_size.at(move_cell->name) : 1; int size = p->chain_size.count(move_cell->name) ? p->chain_size.at(move_cell->name) : 1;
left_cells_v.at(type_index.at(cut_cells.at(pivot)->type)) -= size; left_cells_v.at(cell_index(*cut_cells.at(pivot))) -= size;
right_cells_v.at(type_index.at(cut_cells.at(pivot)->type)) += size; right_cells_v.at(cell_index(*cut_cells.at(pivot))) += size;
pivot--; pivot--;
} }
while (pivot < int(cut_cells.size()) - 1 && is_part_overutil(true)) { while (pivot < int(cut_cells.size()) - 1 && is_part_overutil(true)) {
auto &move_cell = cut_cells.at(pivot + 1); auto &move_cell = cut_cells.at(pivot + 1);
int size = p->chain_size.count(move_cell->name) ? p->chain_size.at(move_cell->name) : 1; int size = p->chain_size.count(move_cell->name) ? p->chain_size.at(move_cell->name) : 1;
left_cells_v.at(type_index.at(cut_cells.at(pivot)->type)) += size; left_cells_v.at(cell_index(*cut_cells.at(pivot))) += size;
right_cells_v.at(type_index.at(cut_cells.at(pivot)->type)) -= size; right_cells_v.at(cell_index(*cut_cells.at(pivot))) -= size;
pivot++; pivot++;
} }

View File

@ -49,7 +49,7 @@ struct PlacerHeapCfg
std::unordered_set<IdString> ioBufTypes; std::unordered_set<IdString> ioBufTypes;
// These cell types are part of the same unit (e.g. slices split into // These cell types are part of the same unit (e.g. slices split into
// components) so will always be spread together // components) so will always be spread together
std::vector<std::unordered_set<IdString>> cellGroups; std::vector<std::unordered_set<PartitionId>> cellGroups;
}; };
extern bool placer_heap(Context *ctx, PlacerHeapCfg cfg); extern bool placer_heap(Context *ctx, PlacerHeapCfg cfg);

View File

@ -486,7 +486,7 @@ information for all edges. `index` must be in [0, clockInfoCount), behaviour is
Partition Methods Partition Methods
----------------- -----------------
Partitions are subsets of BelIds and cell types used by analytic placement to Partitions are subsets of BelIds and cell types used by analytic placer to
seperate types of BELs during placement. Typical partitions are: seperate types of BELs during placement. Typical partitions are:
- All LUT BELs - All LUT BELs
- All FF BELs - All FF BELs