Merge pull request #557 from litghost/refactor_placer_arch_api

RFC: Initial refactoring of placer API.
This commit is contained in:
David Shah 2021-02-02 16:55:13 +00:00 committed by GitHub
commit d1ef3ae1ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 978 additions and 207 deletions

View File

@ -110,3 +110,19 @@ fn_wrapper_0a<Context, decltype(&Context::archId), &Context::archId, conv_to_str
fn_wrapper_2a_v<Context, decltype(&Context::writeSVG), &Context::writeSVG, pass_through<std::string>,
pass_through<std::string>>::def_wrap(ctx_cls, "writeSVG");
// const\_range\<BelBucketId\> getBelBuckets() const
fn_wrapper_0a<Context, decltype(&Context::getBelBuckets), &Context::getBelBuckets,
wrap_context<BelBucketRange>>::def_wrap(ctx_cls, "getBelBuckets");
// BelBucketId getBelBucketForBel(BelId bel) const
fn_wrapper_1a<Context, decltype(&Context::getBelBucketForBel), &Context::getBelBucketForBel, conv_to_str<BelBucketId>,
conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelBucketForBel");
// BelBucketId getBelBucketForCellType(IdString cell\_type) const
fn_wrapper_1a<Context, decltype(&Context::getBelBucketForCellType), &Context::getBelBucketForCellType,
conv_to_str<BelBucketId>, conv_from_str<IdString>>::def_wrap(ctx_cls, "getBelBucketForCellType");
// const\_range\<BelId\> getBelsInBucket(BelBucketId bucket) const
fn_wrapper_1a<Context, decltype(&Context::getBelsInBucket), &Context::getBelsInBucket,
wrap_context<BelRangeForBelBucket>, conv_from_str<BelBucketId>>::def_wrap(ctx_cls, "getBelsInBucket");
// bool isValidBelForCellType(IdString cell\_type, BelId bel) const
fn_wrapper_2a<Context, decltype(&Context::isValidBelForCellType), &Context::isValidBelForCellType, pass_through<bool>,
conv_from_str<IdString>, conv_from_str<BelId>>::def_wrap(ctx_cls, "isValidBelForCellType");

View File

@ -51,6 +51,16 @@ void archcheck_names(const Context *ctx)
log_error("wire != wire2, name = %s\n", name.c_str(ctx));
}
}
log_info("Checking bucket names..\n");
for (BelBucketId bucket : ctx->getBelBuckets()) {
IdString name = ctx->getBelBucketName(bucket);
BelBucketId bucket2 = ctx->getBelBucketByName(name);
if (bucket != bucket2) {
log_error("bucket != bucket2, name = %s\n", name.c_str(ctx));
}
}
#ifndef ARCH_ECP5
log_info("Checking pip names..\n");
for (PipId pip : ctx->getPips()) {
@ -187,6 +197,59 @@ void archcheck_conn(const Context *ctx)
}
}
void archcheck_buckets(const Context *ctx)
{
log_info("Checking bucket data.\n");
// BEL buckets should be subsets of BELs that form an exact cover.
// In particular that means cell types in a bucket should only be
// placable in that bucket.
for (BelBucketId bucket : ctx->getBelBuckets()) {
// Find out which cell types are in this bucket.
std::unordered_set<IdString> cell_types_in_bucket;
for (IdString cell_type : ctx->getCellTypes()) {
if (ctx->getBelBucketForCellType(cell_type) == bucket) {
cell_types_in_bucket.insert(cell_type);
}
}
// Make sure that all cell types in this bucket have at least one
// BelId they can be placed at.
std::unordered_set<IdString> cell_types_unused;
std::unordered_set<BelId> bels_in_bucket;
for (BelId bel : ctx->getBelsInBucket(bucket)) {
BelBucketId bucket2 = ctx->getBelBucketForBel(bel);
log_assert(bucket == bucket2);
bels_in_bucket.insert(bel);
// Check to see if a cell type not in this bucket can be
// placed at a BEL in this bucket.
for (IdString cell_type : ctx->getCellTypes()) {
if (ctx->getBelBucketForCellType(cell_type) == bucket) {
if (ctx->isValidBelForCellType(cell_type, bel)) {
cell_types_unused.erase(cell_type);
}
} else {
log_assert(!ctx->isValidBelForCellType(cell_type, bel));
}
}
}
// Verify that any BEL not in this bucket reports a different
// bucket.
for (BelId bel : ctx->getBels()) {
if (ctx->getBelBucketForBel(bel) != bucket) {
log_assert(bels_in_bucket.count(bel) == 0);
}
}
log_assert(cell_types_unused.empty());
}
}
} // namespace
NEXTPNR_NAMESPACE_BEGIN
@ -199,6 +262,7 @@ void Context::archcheck() const
archcheck_names(this);
archcheck_locs(this);
archcheck_conn(this);
archcheck_buckets(this);
}
NEXTPNR_NAMESPACE_END

184
common/fast_bels.h Normal file
View File

@ -0,0 +1,184 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#pragma once
#include <cstddef>
#include "nextpnr.h"
NEXTPNR_NAMESPACE_BEGIN
// FastBels is a lookup class that provides a fast lookup for finding BELs
// that support a given cell type.
struct FastBels
{
struct TypeData
{
size_t type_index;
int number_of_possible_bels;
};
FastBels(Context *ctx, bool check_bel_available, int minBelsForGridPick)
: ctx(ctx), check_bel_available(check_bel_available), minBelsForGridPick(minBelsForGridPick)
{
}
void addCellType(IdString cell_type)
{
auto iter = cell_types.find(cell_type);
if (iter != cell_types.end()) {
// This cell type has already been added to the fast BEL lookup.
return;
}
size_t type_idx = cell_types.size();
auto &cell_type_data = cell_types[cell_type];
cell_type_data.type_index = type_idx;
fast_bels_by_cell_type.resize(type_idx + 1);
auto &bel_data = fast_bels_by_cell_type.at(type_idx);
for (auto bel : ctx->getBels()) {
if (!ctx->isValidBelForCellType(cell_type, bel)) {
continue;
}
cell_type_data.number_of_possible_bels += 1;
}
for (auto bel : ctx->getBels()) {
if (check_bel_available && !ctx->checkBelAvail(bel)) {
continue;
}
if (!ctx->isValidBelForCellType(cell_type, bel)) {
continue;
}
Loc loc = ctx->getBelLocation(bel);
if (minBelsForGridPick >= 0 && cell_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);
}
}
void addBelBucket(BelBucketId 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->getBelBucketForBel(bel) != partition) {
continue;
}
type_data.number_of_possible_bels += 1;
}
for (auto bel : ctx->getBels()) {
if (check_bel_available && !ctx->checkBelAvail(bel)) {
continue;
}
if (ctx->getBelBucketForBel(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;
int getBelsForCellType(IdString cell_type, FastBelsData **data)
{
auto iter = cell_types.find(cell_type);
if (iter == cell_types.end()) {
addCellType(cell_type);
iter = cell_types.find(cell_type);
NPNR_ASSERT(iter != cell_types.end());
}
auto cell_type_data = iter->second;
*data = &fast_bels_by_cell_type.at(cell_type_data.type_index);
return cell_type_data.number_of_possible_bels;
}
size_t getBelsForBelBucket(BelBucketId partition, FastBelsData **data)
{
auto iter = partition_types.find(partition);
if (iter == partition_types.end()) {
addBelBucket(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;
const bool check_bel_available;
const int minBelsForGridPick;
std::unordered_map<IdString, TypeData> cell_types;
std::vector<FastBelsData> fast_bels_by_cell_type;
std::unordered_map<BelBucketId, TypeData> partition_types;
std::vector<FastBelsData> fast_bels_by_partition_type;
};
NEXTPNR_NAMESPACE_END

View File

@ -118,7 +118,8 @@ bool place_single_cell(Context *ctx, CellInfo *cell, bool require_legality)
}
IdString targetType = cell->type;
for (auto bel : ctx->getBels()) {
if (ctx->getBelType(bel) == targetType && (!require_legality || ctx->isValidBelForCell(cell, bel))) {
if (ctx->isValidBelForCellType(targetType, bel) &&
(!require_legality || ctx->isValidBelForCell(cell, bel))) {
if (ctx->checkBelAvail(bel)) {
wirelen_t wirelen = get_cell_metric_at_bel(ctx, cell, bel, MetricType::COST);
if (iters >= 4)
@ -229,7 +230,7 @@ class ConstraintLegaliseWorker
if (locBel == BelId()) {
return false;
}
if (ctx->getBelType(locBel) != cell->type) {
if (!ctx->isValidBelForCellType(cell->type, locBel)) {
return false;
}
if (!ctx->checkBelAvail(locBel)) {

View File

@ -39,6 +39,7 @@
#include <stdlib.h>
#include <string.h>
#include <vector>
#include "fast_bels.h"
#include "log.h"
#include "place_common.h"
#include "timing.h"
@ -75,36 +76,26 @@ class SAPlacer
};
public:
SAPlacer(Context *ctx, Placer1Cfg cfg) : ctx(ctx), cfg(cfg)
SAPlacer(Context *ctx, Placer1Cfg cfg)
: ctx(ctx), fast_bels(ctx, /*check_bel_available=*/false, cfg.minBelsForGridPick), cfg(cfg)
{
int num_bel_types = 0;
for (auto bel : ctx->getBels()) {
IdString type = ctx->getBelType(bel);
if (bel_types.find(type) == bel_types.end()) {
bel_types[type] = std::tuple<int, int>(num_bel_types++, 1);
} else {
std::get<1>(bel_types.at(type))++;
}
}
for (auto bel : ctx->getBels()) {
Loc loc = ctx->getBelLocation(bel);
IdString type = ctx->getBelType(bel);
int type_idx = std::get<0>(bel_types.at(type));
int type_cnt = std::get<1>(bel_types.at(type));
if (type_cnt < cfg.minBelsForGridPick)
loc.x = loc.y = 0;
if (int(fast_bels.size()) < type_idx + 1)
fast_bels.resize(type_idx + 1);
if (int(fast_bels.at(type_idx).size()) < (loc.x + 1))
fast_bels.at(type_idx).resize(loc.x + 1);
if (int(fast_bels.at(type_idx).at(loc.x).size()) < (loc.y + 1))
fast_bels.at(type_idx).at(loc.x).resize(loc.y + 1);
max_x = std::max(max_x, loc.x);
max_y = std::max(max_y, loc.y);
fast_bels.at(type_idx).at(loc.x).at(loc.y).push_back(bel);
}
diameter = std::max(max_x, max_y) + 1;
std::unordered_set<IdString> cell_types_in_use;
for (auto cell : sorted(ctx->cells)) {
IdString cell_type = cell.second->type;
cell_types_in_use.insert(cell_type);
}
for (auto cell_type : cell_types_in_use) {
fast_bels.addCellType(cell_type);
}
net_bounds.resize(ctx->nets.size());
net_arc_tcost.resize(ctx->nets.size());
old_udata.reserve(ctx->nets.size());
@ -170,13 +161,14 @@ class SAPlacer
loc_name.c_str(), cell->name.c_str(ctx));
}
IdString bel_type = ctx->getBelType(bel);
if (bel_type != cell->type) {
if (!ctx->isValidBelForCellType(cell->type, bel)) {
IdString bel_type = ctx->getBelType(bel);
log_error("Bel \'%s\' of type \'%s\' does not match cell "
"\'%s\' of type \'%s\'\n",
loc_name.c_str(), bel_type.c_str(ctx), cell->name.c_str(ctx), cell->type.c_str(ctx));
}
if (!ctx->isValidBelForCell(cell, bel)) {
IdString bel_type = ctx->getBelType(bel);
log_error("Bel \'%s\' of type \'%s\' is not valid for cell "
"\'%s\' of type \'%s\'\n",
loc_name.c_str(), bel_type.c_str(ctx), cell->name.c_str(ctx), cell->type.c_str(ctx));
@ -452,7 +444,7 @@ class SAPlacer
IdString targetType = cell->type;
auto proc_bel = [&](BelId bel) {
if (ctx->getBelType(bel) == targetType && ctx->isValidBelForCell(cell, bel)) {
if (ctx->isValidBelForCellType(targetType, bel) && ctx->isValidBelForCell(cell, bel)) {
if (ctx->checkBelAvail(bel)) {
uint64_t score = ctx->rng64();
if (score <= best_score) {
@ -651,7 +643,7 @@ class SAPlacer
BelId targetBel = ctx->getBelByLocation(targetLoc);
if (targetBel == BelId())
return false;
if (ctx->getBelType(targetBel) != cell->type)
if (!ctx->isValidBelForCellType(cell->type, targetBel))
return false;
CellInfo *bound = ctx->getBoundBelCell(targetBel);
// We don't consider swapping chains with other chains, at least for the time being - unless it is
@ -730,18 +722,19 @@ class SAPlacer
curr_loc.y = std::min(region_bounds[cell->region->name].y1, curr_loc.y);
}
FastBels::FastBelsData *bel_data;
auto type_cnt = fast_bels.getBelsForCellType(targetType, &bel_data);
while (true) {
int nx = ctx->rng(2 * dx + 1) + std::max(curr_loc.x - dx, 0);
int ny = ctx->rng(2 * dy + 1) + std::max(curr_loc.y - dy, 0);
int beltype_idx, beltype_cnt;
std::tie(beltype_idx, beltype_cnt) = bel_types.at(targetType);
if (beltype_cnt < cfg.minBelsForGridPick)
if (cfg.minBelsForGridPick >= 0 && type_cnt < cfg.minBelsForGridPick)
nx = ny = 0;
if (nx >= int(fast_bels.at(beltype_idx).size()))
if (nx >= int(bel_data->size()))
continue;
if (ny >= int(fast_bels.at(beltype_idx).at(nx).size()))
if (ny >= int(bel_data->at(nx).size()))
continue;
const auto &fb = fast_bels.at(beltype_idx).at(nx).at(ny);
const auto &fb = bel_data->at(nx).at(ny);
if (fb.size() == 0)
continue;
BelId bel = fb.at(ctx->rng(int(fb.size())));
@ -1217,7 +1210,7 @@ class SAPlacer
int diameter = 35, max_x = 1, max_y = 1;
std::unordered_map<IdString, std::tuple<int, int>> bel_types;
std::unordered_map<IdString, BoundingBox> region_bounds;
std::vector<std::vector<std::vector<std::vector<BelId>>>> fast_bels;
FastBels fast_bels;
std::unordered_set<BelId> locked_bels;
std::vector<NetInfo *> net_by_udata;
std::vector<decltype(NetInfo::udata)> old_udata;

View File

@ -44,12 +44,14 @@
#include <queue>
#include <tuple>
#include <unordered_map>
#include "fast_bels.h"
#include "log.h"
#include "nextpnr.h"
#include "place_common.h"
#include "placer1.h"
#include "timing.h"
#include "util.h"
NEXTPNR_NAMESPACE_BEGIN
namespace {
@ -136,7 +138,10 @@ template <typename T> struct EquationSystem
class HeAPPlacer
{
public:
HeAPPlacer(Context *ctx, PlacerHeapCfg cfg) : ctx(ctx), cfg(cfg) { Eigen::initParallel(); }
HeAPPlacer(Context *ctx, PlacerHeapCfg cfg) : ctx(ctx), cfg(cfg), fast_bels(ctx, /*check_bel_available=*/true, -1)
{
Eigen::initParallel();
}
bool place()
{
@ -175,24 +180,26 @@ class HeAPPlacer
std::vector<std::tuple<CellInfo *, BelId, PlaceStrength>> solution;
std::vector<std::unordered_set<IdString>> heap_runs;
std::unordered_set<IdString> all_celltypes;
std::unordered_map<IdString, int> ct_count;
std::vector<std::unordered_set<BelBucketId>> heap_runs;
std::unordered_set<BelBucketId> all_buckets;
std::unordered_map<BelBucketId, int> bucket_count;
for (auto cell : place_cells) {
if (!all_celltypes.count(cell->type)) {
heap_runs.push_back(std::unordered_set<IdString>{cell->type});
all_celltypes.insert(cell->type);
BelBucketId bucket = ctx->getBelBucketForCellType(cell->type);
if (!all_buckets.count(bucket)) {
heap_runs.push_back(std::unordered_set<BelBucketId>{bucket});
all_buckets.insert(bucket);
}
ct_count[cell->type]++;
bucket_count[bucket]++;
}
// If more than 98% of cells are one cell type, always solve all at once
// Otherwise, follow full HeAP strategy of rotate&all
for (auto &c : ct_count)
for (auto &c : bucket_count) {
if (c.second >= 0.98 * int(place_cells.size())) {
heap_runs.clear();
break;
}
}
if (cfg.placeAllAtOnce) {
// Never want to deal with LUTs, FFs, MUXFxs separately,
@ -201,7 +208,7 @@ class HeAPPlacer
heap_runs.clear();
}
heap_runs.push_back(all_celltypes);
heap_runs.push_back(all_buckets);
// The main HeAP placer loop
log_info("Running main analytical placer.\n");
while (stalled < 5 && (solved_hpwl <= legal_hpwl * 0.8)) {
@ -238,7 +245,7 @@ class HeAPPlacer
for (auto type : sorted(run))
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<BelBucketId> &grp) { return !grp.count(type); }))
CutSpreader(this, {type}).run();
update_all_chains();
@ -248,8 +255,10 @@ class HeAPPlacer
legal_hpwl = total_hpwl();
auto run_stopt = std::chrono::high_resolution_clock::now();
IdString bucket_name = ctx->getBelBucketName(*run.begin());
log_info(" at iteration #%d, type %s: wirelen solved = %d, spread = %d, legal = %d; time = %.02fs\n",
iter + 1, (run.size() > 1 ? "ALL" : run.begin()->c_str(ctx)), int(solved_hpwl),
iter + 1, (run.size() > 1 ? "ALL" : bucket_name.c_str(ctx)), int(solved_hpwl),
int(spread_hpwl), int(legal_hpwl),
std::chrono::duration<double>(run_stopt - run_startt).count());
}
@ -318,16 +327,9 @@ class HeAPPlacer
PlacerHeapCfg cfg;
int max_x = 0, max_y = 0;
std::vector<std::vector<std::vector<std::vector<BelId>>>> fast_bels;
FastBels fast_bels;
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
{
// Actual bounding box
@ -384,13 +386,14 @@ class HeAPPlacer
loc_name.c_str(), cell->name.c_str(ctx));
}
IdString bel_type = ctx->getBelType(bel);
if (bel_type != cell->type) {
if (!ctx->isValidBelForCellType(cell->type, bel)) {
IdString bel_type = ctx->getBelType(bel);
log_error("Bel \'%s\' of type \'%s\' does not match cell "
"\'%s\' of type \'%s\'\n",
loc_name.c_str(), bel_type.c_str(ctx), cell->name.c_str(ctx), cell->type.c_str(ctx));
}
if (!ctx->isValidBelForCell(cell, bel)) {
IdString bel_type = ctx->getBelType(bel);
log_error("Bel \'%s\' of type \'%s\' is not valid for cell "
"\'%s\' of type \'%s\'\n",
loc_name.c_str(), bel_type.c_str(ctx), cell->name.c_str(ctx), cell->type.c_str(ctx));
@ -410,66 +413,30 @@ class HeAPPlacer
ctx->yield();
}
// Construct the fast_bels, nearest_row_with_bel and nearest_col_with_bel
void build_fast_bels()
{
int num_bel_types = 0;
for (auto bel : ctx->getBels()) {
IdString type = ctx->getBelType(bel);
if (bel_types.find(type) == bel_types.end()) {
bel_types[type] = std::tuple<int, int>(num_bel_types++, 1);
} else {
std::get<1>(bel_types.at(type))++;
}
}
for (auto bel : ctx->getBels()) {
if (!ctx->checkBelAvail(bel))
continue;
Loc loc = ctx->getBelLocation(bel);
IdString type = ctx->getBelType(bel);
int type_idx = std::get<0>(bel_types.at(type));
if (int(fast_bels.size()) < type_idx + 1)
fast_bels.resize(type_idx + 1);
if (int(fast_bels.at(type_idx).size()) < (loc.x + 1))
fast_bels.at(type_idx).resize(loc.x + 1);
if (int(fast_bels.at(type_idx).at(loc.x).size()) < (loc.y + 1))
fast_bels.at(type_idx).at(loc.x).resize(loc.y + 1);
max_x = std::max(max_x, loc.x);
max_y = std::max(max_y, loc.y);
fast_bels.at(type_idx).at(loc.x).at(loc.y).push_back(bel);
}
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;
}
std::unordered_set<IdString> cell_types_in_use;
std::unordered_set<BelBucketId> buckets_in_use;
for (auto cell : sorted(ctx->cells)) {
IdString cell_type = cell.second->type;
cell_types_in_use.insert(cell_type);
BelBucketId bucket = ctx->getBelBucketForCellType(cell_type);
buckets_in_use.insert(bucket);
}
for (auto cell_type : cell_types_in_use) {
fast_bels.addCellType(cell_type);
}
for (auto bucket : buckets_in_use) {
fast_bels.addBelBucket(bucket);
}
// Determine bounding boxes of region constraints
@ -523,15 +490,30 @@ class HeAPPlacer
// FIXME: Are there better approaches to the initial placement (e.g. greedy?)
void seed_placement()
{
std::unordered_map<IdString, std::deque<BelId>> available_bels;
for (auto bel : ctx->getBels()) {
if (!ctx->checkBelAvail(bel))
continue;
available_bels[ctx->getBelType(bel)].push_back(bel);
std::unordered_set<IdString> cell_types;
for (const auto &cell : ctx->cells) {
cell_types.insert(cell.second->type);
}
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) {
ctx->shuffle(t.second.begin(), t.second.end());
}
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (ci->bel != BelId()) {
@ -544,21 +526,48 @@ class HeAPPlacer
bool placed = false;
int attempt_count = 0;
while (!placed) {
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),
ci->type.c_str(ctx));
++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));
BelId bel = available_bels.at(ci->type).back();
available_bels.at(ci->type).pop_back();
}
// Make sure this cell type is in the available BEL map at
// all.
if (!available_bels.count(ci->type)) {
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));
}
// Find an unused BEL from bels_for_cell_type.
auto &bels_for_cell_type = available_bels.at(ci->type);
BelId bel;
while (true) {
if (bels_for_cell_type.empty()) {
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));
}
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);
cell_locs[cell.first].x = loc.x;
cell_locs[cell.first].y = loc.y;
cell_locs[cell.first].locked = false;
cell_locs[cell.first].global = ctx->getBelGlobalBuf(bel);
// FIXME
if (has_connectivity(cell.second) && !cfg.ioBufTypes.count(ci->type)) {
bels_used.insert(bel);
place_cells.push_back(ci);
placed = true;
} else {
@ -566,6 +575,7 @@ class HeAPPlacer
ctx->bindBel(bel, ci, STRENGTH_STRONG);
cell_locs[cell.first].locked = true;
placed = true;
bels_used.insert(bel);
} else {
available_bels.at(ci->type).push_front(bel);
}
@ -576,7 +586,7 @@ class HeAPPlacer
}
// Setup the cells to be solved, returns the number of rows
int setup_solve_cells(std::unordered_set<IdString> *celltypes = nullptr)
int setup_solve_cells(std::unordered_set<BelBucketId> *buckets = nullptr)
{
int row = 0;
solve_cells.clear();
@ -585,7 +595,7 @@ class HeAPPlacer
cell.second->udata = dont_solve;
// Then update cells to be placed, which excludes cell children
for (auto cell : place_cells) {
if (celltypes && !celltypes->count(cell->type))
if (buckets && !buckets->count(ctx->getBelBucketForCellType(cell->type)))
continue;
cell->udata = row++;
solve_cells.push_back(cell);
@ -814,8 +824,8 @@ class HeAPPlacer
if (ci->bel != BelId())
continue;
// log_info(" Legalising %s (%s)\n", top.second.c_str(ctx), ci->type.c_str(ctx));
int bt = std::get<0>(bel_types.at(ci->type));
auto &fb = fast_bels.at(bt);
FastBels::FastBelsData *fb;
fast_bels.getBelsForCellType(ci->type, &fb);
int radius = 0;
int iter = 0;
int iter_at_radius = 0;
@ -864,13 +874,13 @@ class HeAPPlacer
while (radius < std::max(max_x, max_y)) {
for (int x = std::max(0, cell_locs.at(ci->name).x - radius);
x <= std::min(max_x, cell_locs.at(ci->name).x + radius); x++) {
if (x >= int(fb.size()))
if (x >= int(fb->size()))
break;
for (int y = std::max(0, cell_locs.at(ci->name).y - radius);
y <= std::min(max_y, cell_locs.at(ci->name).y + radius); y++) {
if (y >= int(fb.at(x).size()))
if (y >= int(fb->at(x).size()))
break;
if (fb.at(x).at(y).size() > 0)
if (fb->at(x).at(y).size() > 0)
goto notempty;
}
}
@ -885,14 +895,11 @@ class HeAPPlacer
if (ny < 0 || ny > max_y)
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;
if (ny >= int(fb.at(nx).size()))
if (ny >= int(fb->at(nx).size()))
continue;
if (fb.at(nx).at(ny).empty())
if (fb->at(nx).at(ny).empty())
continue;
int need_to_explore = 2 * radius;
@ -912,7 +919,7 @@ class HeAPPlacer
}
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)) {
@ -962,7 +969,7 @@ class HeAPPlacer
}
}
} else {
for (auto sz : fb.at(nx).at(ny)) {
for (auto sz : fb->at(nx).at(ny)) {
Loc loc = ctx->getBelLocation(sz);
if (ci->constr_abs_z && loc.z != ci->constr_z)
continue;
@ -979,7 +986,7 @@ class HeAPPlacer
if (vc->region != nullptr && vc->region->constr_bels && !vc->region->bels.count(target))
goto fail;
CellInfo *bound;
if (target == BelId() || ctx->getBelType(target) != vc->type)
if (target == BelId() || !ctx->isValidBelForCellType(vc->type, target))
goto fail;
bound = ctx->getBoundBelCell(target);
// Chains cannot overlap
@ -1081,13 +1088,17 @@ class HeAPPlacer
class CutSpreader
{
public:
CutSpreader(HeAPPlacer *p, const std::unordered_set<IdString> &beltype) : p(p), ctx(p->ctx), beltype(beltype)
CutSpreader(HeAPPlacer *p, const std::unordered_set<BelBucketId> &buckets) : p(p), ctx(p->ctx), buckets(buckets)
{
int idx = 0;
for (IdString type : sorted(beltype)) {
type_index[type] = idx;
fb.emplace_back(&(p->fast_bels.at(std::get<0>(p->bel_types.at(type)))));
// Get fast BELs data for all buckets being Cut/Spread.
size_t idx = 0;
for (BelBucketId bucket : sorted(buckets)) {
type_index[bucket] = idx;
FastBels::FastBelsData *fast_bels;
p->fast_bels.getBelsForBelBucket(bucket, &fast_bels);
fb.push_back(fast_bels);
++idx;
NPNR_ASSERT(fb.size() == idx);
}
}
static int seq;
@ -1169,8 +1180,8 @@ class HeAPPlacer
private:
HeAPPlacer *p;
Context *ctx;
std::unordered_set<IdString> beltype;
std::unordered_map<IdString, int> type_index;
std::unordered_set<BelBucketId> buckets;
std::unordered_map<BelBucketId, size_t> type_index;
std::vector<std::vector<std::vector<int>>> occupancy;
std::vector<std::vector<int>> groups;
std::vector<std::vector<ChainExtent>> chaines;
@ -1192,16 +1203,23 @@ class HeAPPlacer
return int(fb.at(type)->at(x).at(y).size());
}
bool is_cell_fixed(const CellInfo &cell) const
{
return buckets.count(ctx->getBelBucketForCellType(cell.type)) == 0;
}
size_t cell_index(const CellInfo &cell) const { return type_index.at(ctx->getBelBucketForCellType(cell.type)); }
void init()
{
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>(buckets.size(), 0)));
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));
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 y = 0; y <= p->max_y; y++) {
for (int t = 0; t < int(beltype.size()); t++) {
for (int t = 0; t < int(buckets.size()); t++) {
occupancy.at(x).at(y).at(t) = 0;
}
groups.at(x).at(y) = -1;
@ -1219,55 +1237,77 @@ class HeAPPlacer
}
};
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;
if (ctx->cells.at(cell.first)->belStrength > STRENGTH_STRONG)
}
if (cell.belStrength > STRENGTH_STRONG) {
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
if (p->chain_root.count(cell.first)) {
set_chain_ext(p->chain_root.at(cell.first)->name, cell.second.x, cell.second.y);
} else if (!ctx->cells.at(cell.first)->constr_children.empty()) {
set_chain_ext(cell.first, cell.second.x, cell.second.y);
if (p->chain_root.count(cell_name)) {
set_chain_ext(p->chain_root.at(cell_name)->name, loc.x, loc.y);
} else if (!ctx->cells.at(cell_name)->constr_children.empty()) {
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;
// Transfer chain extents to the actual chaines structure
}
// Transfer chain extents to the actual chains structure
ChainExtent *ce = nullptr;
if (p->chain_root.count(cell.first))
ce = &(cell_extents.at(p->chain_root.at(cell.first)->name));
else if (!ctx->cells.at(cell.first)->constr_children.empty())
ce = &(cell_extents.at(cell.first));
if (p->chain_root.count(cell_name)) {
ce = &(cell_extents.at(p->chain_root.at(cell_name)->name));
} else if (!ctx->cells.at(cell_name)->constr_children.empty()) {
ce = &(cell_extents.at(cell_name));
}
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.y0 = std::min(lce.y0, ce->y0);
lce.x1 = std::max(lce.x1, ce->x1);
lce.y1 = std::max(lce.y1, ce->y1);
}
}
for (auto cell : p->solve_cells) {
if (!beltype.count(cell->type))
if (is_cell_fixed(*cell)) {
continue;
}
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)
{
// 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++) {
// log_info("%d %d\n", groups.at(x).at(y), mergee.id);
NPNR_ASSERT(groups.at(x).at(y) == mergee.id);
groups.at(x).at(y) = merged.id;
for (size_t t = 0; t < beltype.size(); t++) {
for (size_t t = 0; t < buckets.size(); t++) {
merged.cells.at(t) += occ_at(x, y, t);
merged.bels.at(t) += bels_at(x, y, t);
}
}
}
merged_regions.insert(mergee.id);
grow_region(merged, mergee.x0, mergee.y0, mergee.x1, mergee.y1);
}
@ -1286,11 +1326,12 @@ class HeAPPlacer
auto process_location = [&](int x, int y) {
// Merge with any overlapping regions
if (groups.at(x).at(y) == -1) {
for (int t = 0; t < int(beltype.size()); t++) {
for (size_t t = 0; t < buckets.size(); t++) {
r.bels.at(t) += bels_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)
merge_regions(r, regions.at(groups.at(x).at(y)));
groups.at(x).at(y) = r.id;
@ -1320,12 +1361,13 @@ class HeAPPlacer
if (groups.at(x).at(y) != -1)
continue;
bool overutilised = false;
for (size_t t = 0; t < beltype.size(); t++) {
for (size_t t = 0; t < buckets.size(); t++) {
if (occ_at(x, y, t) > bels_at(x, y, t)) {
overutilised = true;
break;
}
}
if (!overutilised)
continue;
// log_info("%d %d %d\n", x, y, occ_at(x, y));
@ -1335,7 +1377,7 @@ class HeAPPlacer
reg.id = id;
reg.x0 = reg.x1 = x;
reg.y0 = reg.y1 = y;
for (size_t t = 0; t < beltype.size(); t++) {
for (size_t t = 0; t < buckets.size(); t++) {
reg.bels.push_back(bels_at(x, y, t));
reg.cells.push_back(occ_at(x, y, t));
}
@ -1352,7 +1394,7 @@ class HeAPPlacer
if (reg.x1 < p->max_x) {
bool over_occ_x = false;
for (int y1 = reg.y0; y1 <= reg.y1; y1++) {
for (size_t t = 0; t < beltype.size(); t++) {
for (size_t t = 0; t < buckets.size(); t++) {
if (occ_at(reg.x1 + 1, y1, t) > bels_at(reg.x1 + 1, y1, t)) {
over_occ_x = true;
break;
@ -1368,7 +1410,7 @@ class HeAPPlacer
if (reg.y1 < p->max_y) {
bool over_occ_y = false;
for (int x1 = reg.x0; x1 <= reg.x1; x1++) {
for (size_t t = 0; t < beltype.size(); t++) {
for (size_t t = 0; t < buckets.size(); t++) {
if (occ_at(x1, reg.y1 + 1, t) > bels_at(x1, reg.y1 + 1, t)) {
over_occ_y = true;
break;
@ -1430,10 +1472,12 @@ class HeAPPlacer
}
}
if (!changed) {
for (auto bt : sorted(beltype)) {
if (reg.cells > reg.bels)
for (auto bucket : sorted(buckets)) {
if (reg.cells > reg.bels) {
IdString bucket_name = ctx->getBelBucketName(bucket);
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(bucket)), bucket_name.c_str(ctx));
}
}
break;
}
@ -1454,7 +1498,7 @@ class HeAPPlacer
for (int x = r.x0; x <= r.x1; x++) {
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));
for (size_t t = 0; t < beltype.size(); t++)
for (size_t t = 0; t < buckets.size(); t++)
total_bels += bels_at(x, y, t);
}
}
@ -1506,26 +1550,34 @@ class HeAPPlacer
int trimmed_l = dir ? r.y0 : r.x0, trimmed_r = dir ? r.y1 : r.x1;
while (trimmed_l < (dir ? r.y1 : r.x1)) {
bool have_bels = false;
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 (int i = dir ? r.x0 : r.y0; i <= (dir ? r.x1 : r.y1); i++) {
for (size_t t = 0; t < buckets.size(); t++) {
if (bels_at(dir ? i : trimmed_l, dir ? trimmed_l : i, t) > 0) {
have_bels = true;
break;
}
}
}
if (have_bels)
break;
trimmed_l++;
}
while (trimmed_r > (dir ? r.y0 : r.x0)) {
bool have_bels = false;
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 (int i = dir ? r.x0 : r.y0; i <= (dir ? r.x1 : r.y1); i++) {
for (size_t t = 0; t < buckets.size(); t++) {
if (bels_at(dir ? i : trimmed_r, dir ? trimmed_r : i, t) > 0) {
have_bels = true;
break;
}
}
}
if (have_bels)
break;
trimmed_r--;
}
// log_info("tl %d tr %d cl %d cr %d\n", trimmed_l, trimmed_r, clearance_l, clearance_r);
@ -1533,27 +1585,27 @@ class HeAPPlacer
return {};
// Now find the initial target cut that minimises utilisation imbalance, whilst
// 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_bels_v(beltype.size(), 0), right_bels_v(r.bels);
std::vector<int> left_cells_v(buckets.size(), 0), right_cells_v(buckets.size(), 0);
std::vector<int> left_bels_v(buckets.size(), 0), right_bels_v(r.bels);
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;
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;
int best_tgt_cut = -1;
double best_deltaU = std::numeric_limits<double>::max();
// std::pair<int, int> target_cut_bels;
std::vector<int> slither_bels(beltype.size(), 0);
std::vector<int> slither_bels(buckets.size(), 0);
for (int i = trimmed_l; i <= trimmed_r; i++) {
for (size_t t = 0; t < beltype.size(); t++)
for (size_t t = 0; t < buckets.size(); t++)
slither_bels.at(t) = 0;
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 < buckets.size(); 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 < buckets.size(); t++) {
left_bels_v.at(t) += slither_bels.at(t);
right_bels_v.at(t) -= slither_bels.at(t);
}
@ -1561,7 +1613,7 @@ class HeAPPlacer
if (((i - trimmed_l) + 1) >= clearance_l && ((trimmed_r - i) + 1) >= clearance_r) {
// Solution is potentially valid
double aU = 0;
for (size_t t = 0; t < beltype.size(); t++)
for (size_t t = 0; t < buckets.size(); 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)) -
double(right_cells_v.at(t)) / double(std::max(right_bels_v.at(t), 1)));
@ -1575,19 +1627,19 @@ class HeAPPlacer
return {};
// left_bels = target_cut_bels.first;
// right_bels = target_cut_bels.second;
for (size_t t = 0; t < beltype.size(); t++) {
for (size_t t = 0; t < buckets.size(); t++) {
left_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 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 < buckets.size(); 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 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 < buckets.size(); t++) {
right_bels_v.at(t) += bels_at(x, y, t);
}
}
@ -1607,15 +1659,15 @@ class HeAPPlacer
while (pivot > 0 && is_part_overutil(false)) {
auto &move_cell = cut_cells.at(pivot);
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;
right_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(cell_index(*cut_cells.at(pivot))) += size;
pivot--;
}
while (pivot < int(cut_cells.size()) - 1 && is_part_overutil(true)) {
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;
left_cells_v.at(type_index.at(cut_cells.at(pivot)->type)) += size;
right_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(cell_index(*cut_cells.at(pivot))) -= size;
pivot++;
}

View File

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

View File

@ -215,7 +215,7 @@ class TimingOptimiser
std::vector<BelId> free_bels_at_loc;
std::vector<BelId> bound_bels_at_loc;
for (auto bel : ctx->getBelsByTile(curr_loc.x + dx, curr_loc.y + dy)) {
if (ctx->getBelType(bel) != cell->type)
if (!ctx->isValidBelForCellType(cell->type, bel))
continue;
CellInfo *bound = ctx->getBoundBelCell(bel);
if (bound == nullptr) {

View File

@ -40,6 +40,10 @@ A type representing a wire name. `WireId()` must construct a unique null-value.
A type representing a pip name. `PipId()` must construct a unique null-value. Must provide `==`, `!=`, and `<` operators and a specialization for `std::hash<PipId>`.
### BelBucketId
A type representing a bel bucket. `BelBucketId()` must construct a unique null-value. Must provide `==`, `!=`, and `<` operators and a specialization for `std::hash<BelBucketId>`.
### GroupId
A type representing a group name. `GroupId()` must construct a unique null-value. Must provide `==` and `!=` operators and a specialization for `std::hash<GroupId>`.
@ -88,6 +92,14 @@ Get Z dimension for the specified tile for bels. All bels with at specified X an
Get Z dimension for the specified tile for pips. All pips with at specified X and Y coordinates must have a Z coordinate in the range `0 .. getTileDimZ(X,Y)-1` (inclusive).
Cell Methods
-----------
### const\_range\<IdString\> getCellTypes() const
Get list of cell types that this architecture accepts.
Bel Methods
-----------
@ -377,7 +389,7 @@ the given dst wire.
This should return a low upper bound for the fastest route from `src` to `dst`.
Or in other words it should assume an otherwise unused chip (thus "fastest route").
But it only produces an estimate for that fastest route, not an exact
But it only produces an estimate for that fastest route, not an exact
result, and for that estimate it is considered more acceptable to return a
slightly too high result and it is considered less acceptable to return a
too low result (thus "low upper bound").
@ -463,21 +475,74 @@ Cell Delay Methods
Returns the delay for the specified path through a cell in the `&delay` argument. The method returns
false if there is no timing relationship from `fromPort` to `toPort`.
### TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const
### TimingPortClass getPortTimingClass(const CellInfo \*cell, IdString port, int &clockInfoCount) const
Return the _timing port class_ of a port. This can be a register or combinational input or output; clock input or
output; general startpoint or endpoint; or a port ignored for timing purposes. For register ports, clockInfoCount is set
to the number of associated _clock edges_ that can be queried by getPortClockingInfo.
### TimingClockingInfo getPortClockingInfo(const CellInfo *cell, IdString port, int index) const
### TimingClockingInfo getPortClockingInfo(const CellInfo \*cell, IdString port, int index) const
Return the _clocking info_ (including port name of clock, clock polarity and setup/hold/clock-to-out times) of a
port. Where ports have more than one clock edge associated with them (such as DDR outputs), `index` can be used to obtain
information for all edges. `index` must be in [0, clockInfoCount), behaviour is undefined otherwise.
Bel Buckets Methods
-------------------
Bel buckets are subsets of BelIds and cell types used by analytic placer to
seperate types of bels during placement. The buckets should form an exact
cover over all BelIds and cell types.
Each bel bucket should be BelIds and cell types that are roughly
interchangable during placement. Typical buckets are:
- All LUT bels
- All FF bels
- All multipliers bels
- All block RAM bels
- etc.
The bel buckets will be used during analytic placement for spreading prior to
strict legality enforcement. It is not required that all bels within a bucket
are strictly equivelant.
Strict legality step will enforce those differences, along with additional
local constraints. `isValidBelForCell`, `isValidBelForCellType`, and
`isBelLocationValid` are used to enforce strict legality checks.
### const\_range\<BelBucketId\> getBelBuckets() const
Return a list of all bel buckets on the device.
### IdString getBelBucketName(BelBucketId bucket) const
Return the name of this bel bucket.
### BelBucketId getBelBucketByName(IdString bucket\_name) const
Return the BelBucketId for the specified bucket name.
### BelBucketId getBelBucketForBel(BelId bel) const
Returns the bucket for a particular bel.
### BelBucketId getBelBucketForCell(IdString cell\_type) const
Returns the bel bucket for a particular cell type.
### const\_range\<BelId\> getBelsInBucket(BelBucketId bucket) const
Return the list of bels within a bucket.
Placer Methods
--------------
### bool isValidBelForCellType(IdString cell\_type, BelId bel) const
Returns true if the given cell can be bound to the given bel. This check
should be fast, compared with isValidBelForCell. This check should always
return the same value regardless if other cells are placed within the fabric.
### bool isValidBelForCell(CellInfo \*cell, BelId bel) const
Returns true if the given cell can be bound to the given bel, considering
@ -489,7 +554,6 @@ a certain number of different clock signals allowed for a group of bels.
Returns true if a bell in the current configuration is valid, i.e. if
`isValidBelForCell()` would return true for the current mapping.
### static const std::string defaultPlacer
Name of the default placement algorithm for the architecture, if

View File

@ -14,9 +14,9 @@ This document aims to provide an overview into the philosophy behind nextpnr's c
An architecture in nextpnr is described first and foremost as code. The exact details are given in the [Arch API reference](archapi.md); this aims to explain the core concept.
By choosing this approach; this gives architectures significant flexibility to use more advanced database representations than a simple flat database - for example deduplicated approaches that store similar tiles only once.
By choosing this approach; this gives architectures significant flexibility to use more advanced database representations than a simple flat database - for example deduplicated approaches that store similar tiles only once.
Architectures can also implement custom algorithms for packing (or other initial netlist transformations) and specialized parts of placement and routing such as global clock networks. This is because architectures provide the `pack()`, `place()` and `route()` functions, although the latter two will normally use generic algorithms (such as HeAP and router1) to do most of the work.
Architectures can also implement custom algorithms for packing (or other initial netlist transformations) and specialized parts of placement and routing such as global clock networks. This is because architectures provide the `pack()`, `place()` and `route()` functions, although the latter two will normally use generic algorithms (such as HeAP and router1) to do most of the work.
Another important function provided by architectures is placement validity checking. This allows the placer to check whether or not a given cell placement is valid. An example of this is for iCE40, where 8 logic cells in a tile share one clock signal - this is checked here.
@ -24,13 +24,13 @@ This function allows architectures in nextpnr to do significantly less packing t
Additionally to this; architectures provide functions for checking the availability and conflicts between resources (e.g. `checkBelAvail`, `checkPipAvail`, etc). This enables arbitrary constraints between resource availability to be defined, for example:
- where a group of Pips share bitstream bits, only one can be used at a time
- where a group of pips share bitstream bits, only one can be used at a time
- Pips that represent LUT permutation are not available when the LUT is in memory mode
- only a certain total number of Pips in a switchbox can be used at once due to power supply limitations
- only a certain total number of pips in a switchbox can be used at once due to power supply limitations
## `IdString`s
To avoid the high cost of using strings as identifiers directly; almost all "string" identifiers in nextpnr (such as cell names and types) use an indexed string pool type named `IdString`. Unlike Yosys, which has a global garbage collected pool, nextpnr has a per-Context pool without any garbage collection.
To avoid the high cost of using strings as identifiers directly; almost all "string" identifiers in nextpnr (such as cell names and types) use an indexed string pool type named `IdString`. Unlike Yosys, which has a global garbage collected pool, nextpnr has a per-Context pool without any garbage collection.
`IdString`s can be created in two ways. Architectures can add `IdString`s with constant indices - allowing `IdString` constants to be provided too - using `initialize_add` at startup. See how `constids.inc` is used in iCE40 for an example of this. The main way to create `IdString`s, however, is at runtime using the `id` member function of `BaseCtx` given the string to create from (if an `IdString` of that string already exists, the existing `IdString` will be returned).
@ -42,15 +42,36 @@ Packing in nextpnr could be done in two ways (if significant packing is done at
- replacing multiple cells with a single larger cell that corresponds to a bel
- combining smaller cells using relative placement constraints
In flows with minimal packing the main task of the packer is to transform cells into common types that correspond to a bel (e.g. convert multiple flipflop primitives to a single common type with some extra parameters). The packer will also have to add relative constraints for fixed structures such as carry chains or LUT-MUX cascades.
The packer will also have to add relative constraints for fixed structures such as carry chains or LUT-MUX cascades.
There are several helper functions that are useful for developing packers and other passes that perform significant netlist modifications in `util.h`. It is often preferable to use these compared to direct modification of the netlist structures due to the "double linking" nextpnr does - e.g. when connecting a port you must update both the `net` field of the ports and the `driver`/`users` of the net.
### Cell to bel mapping
There is an Arch API choice when it comes to representing the relationship
between cell types and bel types. One option is to transform cells into
common types that correspond to a bel (e.g. convert multiple flipflop
primitives to a single common type with some extra parameters). In Arch APIs
designed like this, packer transformations are required to convert input cell
types into nextpnr specific cell types that have a 1 to 1 relationship with
bel types.
For Arch APIs of this type, the method `isValidBelForCellType` reduces to:
```
bool isValidBelForCellType(IdString cell_type, BelId bel) const {
return cell_type == getBelType(bel);
}
```
The alternative is to implement a fast `isValidBelForCellType` method that
determines if this cell type can be bound to this bel.
## Developing CAD algorithms - placement
The job of the placer in nextpnr is to find a suitable bel for each cell in the design; while respecting legality and relative constraints.
Placers might want to create their own indices of bels (for example, bels by type and location) to speed up the search.
Placers might want to create their own indices of bels (for example, bels by type and location) to speed up the search.
As nextpnr allows arbitrary constraints on bels for more advanced packer-free flows and complex real-world architectures; placements must be checked for legality using `isValidBelForCell` (before placement) or `isBelLocationValid` (after placement) and the placement rejected if invalid. For analytical placement algorithms; after creating a spread-out AP solution the legality of placing each cell needs to be checked. In practice, the cost of this is fairly low as the architecture should ensure these functions are as fast as possible.
@ -59,6 +80,17 @@ There are several routes for timing information in the placer:
- sink ports can have a criticality (value between 0 and 1 where 1 is the critical path) associated with them by using `get_criticalities` and a `NetCriticalityMap`
- `predictDelay` returns an estimated delay for a sink port based on placement information
### Bel Buckets
The bel Bucket Arch APIs can be used by an analytical placer (AP) for getting
groups of bels and cell types placed together. This grouping is important for
algorithms like HeAP which typically want to do operate on subsets of the
design for some portions of the placement.
The HeAP implementation allows for multiple bel buckets to be placed on
together, see the "cellGroups" field.
## Routing
The job of the router is to ensure that the `wires` map for each net contains a complete routing tree; populated using the Arch functions to bind wires and pips. The ripup invariants in the [FAQ](faq.md) are important to bear in mind; as there may be complex constraints on the usage of wires and pips in some architectures.

View File

@ -23,6 +23,7 @@ For nextpnr we are using the following terminology.
- **Wire**: a fixed physical connection inside the FPGA between Pips and/or Bel pins.
- **Alias**: a special automatic-on Pip to represent a permanent connection between two wires
- **Group**: a collection of bels, pips, wires, and/or other groups
- **BelBucket**: a collection of bels and cell types. All of the bel buckets form a set cover of bels and cell types.
### Flow Terminology

View File

@ -115,6 +115,19 @@ Arch::Arch(ArchArgs args) : args(args)
log_error("Unsupported package '%s' for '%s'.\n", args.package.c_str(), getChipName().c_str());
bel_to_cell.resize(chip_info->height * chip_info->width * max_loc_bels, nullptr);
std::unordered_set<IdString> bel_types;
for (BelId bel : getBels()) {
bel_types.insert(getBelType(bel));
}
for (IdString bel_type : bel_types) {
cell_types.push_back(bel_type);
BelBucketId bucket;
bucket.name = bel_type;
buckets.push_back(bucket);
}
}
// -----------------------------------------------------------------------

View File

@ -950,6 +950,46 @@ struct Arch : BaseCtx
// -------------------------------------------------
// Placement validity checks
bool isValidBelForCellType(IdString cell_type, BelId bel) const { return cell_type == getBelType(bel); }
const std::vector<IdString> &getCellTypes() const { return cell_types; }
std::vector<BelBucketId> getBelBuckets() const { return buckets; }
IdString getBelBucketName(BelBucketId bucket) const { return bucket.name; }
BelBucketId getBelBucketByName(IdString name) const
{
BelBucketId bucket;
bucket.name = name;
return bucket;
}
BelBucketId getBelBucketForBel(BelId bel) const
{
BelBucketId bucket;
bucket.name = getBelType(bel);
return bucket;
}
BelBucketId getBelBucketForCellType(IdString cell_type) const
{
BelBucketId bucket;
bucket.name = cell_type;
return bucket;
}
std::vector<BelId> getBelsInBucket(BelBucketId bucket) const
{
std::vector<BelId> bels;
for (BelId bel : getBels()) {
if (getBelType(bel) == bucket.name) {
bels.push_back(bel);
}
}
return bels;
}
bool isValidBelForCell(CellInfo *cell, BelId bel) const;
bool isBelLocationValid(BelId bel) const;
@ -1022,6 +1062,9 @@ struct Arch : BaseCtx
static const std::vector<std::string> availablePlacers;
static const std::string defaultRouter;
static const std::vector<std::string> availableRouters;
std::vector<IdString> cell_types;
std::vector<BelBucketId> buckets;
};
NEXTPNR_NAMESPACE_END

View File

@ -59,6 +59,8 @@ void arch_wrap_python(py::module &m)
typedef const PipRange UphillPipRange;
typedef const PipRange DownhillPipRange;
typedef const std::vector<BelBucketId> &BelBucketRange;
typedef const std::vector<BelId> &BelRangeForBelBucket;
#include "arch_pybindings_shared.h"
WRAP_RANGE(m, Bel, conv_to_str<BelId>);

View File

@ -76,6 +76,18 @@ template <> struct string_converter<PipId>
}
};
template <> struct string_converter<BelBucketId>
{
BelBucketId from_str(Context *ctx, std::string name) { return ctx->getBelBucketByName(ctx->id(name)); }
std::string to_str(Context *ctx, BelBucketId id)
{
if (id == BelBucketId())
throw bad_wrap();
return ctx->getBelBucketName(id).str(ctx);
}
};
template <> struct string_converter<BelPin>
{
BelPin from_str(Context *ctx, std::string name)

View File

@ -126,6 +126,15 @@ struct PipId
}
};
struct BelBucketId
{
IdString name;
bool operator==(const BelBucketId &other) const { return (name == other.name); }
bool operator!=(const BelBucketId &other) const { return (name != other.name); }
bool operator<(const BelBucketId &other) const { return name < other.name; }
};
struct GroupId
{
enum : int8_t
@ -262,4 +271,14 @@ template <> struct hash<NEXTPNR_NAMESPACE_PREFIX DecalId>
}
};
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX BelBucketId>
{
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX BelBucketId &partition) const noexcept
{
std::size_t seed = 0;
boost::hash_combine(seed, hash<NEXTPNR_NAMESPACE_PREFIX IdString>()(partition.name));
return seed;
}
};
} // namespace std

View File

@ -271,6 +271,38 @@ struct Arch : BaseCtx
bool place();
bool route();
std::vector<IdString> getCellTypes() const
{
std::vector<IdString> cell_types;
cell_types.reserve(bels.size());
for (auto bel : bels) {
cell_types.push_back(bel.first);
}
return cell_types;
}
std::vector<BelBucketId> getBelBuckets() const { return getCellTypes(); }
IdString getBelBucketName(BelBucketId bucket) const { return bucket; }
BelBucketId getBelBucketByName(IdString bucket) const { return bucket; }
BelBucketId getBelBucketForBel(BelId bel) const { return getBelType(bel); }
BelBucketId getBelBucketForCellType(IdString cell_type) const { return cell_type; }
std::vector<BelId> getBelsInBucket(BelBucketId bucket) const
{
std::vector<BelId> bels;
for (BelId bel : getBels()) {
if (bucket == getBelBucketForBel(bel)) {
bels.push_back(bel);
}
}
return bels;
}
const std::vector<GraphicElement> &getDecalGraphics(DecalId decal) const;
DecalXY getBelDecal(BelId bel) const;
DecalXY getWireDecal(WireId wire) const;
@ -283,6 +315,7 @@ struct Arch : BaseCtx
// Get the TimingClockingInfo of a port
TimingClockingInfo getPortClockingInfo(const CellInfo *cell, IdString port, int index) const;
bool isValidBelForCellType(IdString cell_type, BelId bel) const { return cell_type == getBelType(bel); }
bool isValidBelForCell(CellInfo *cell, BelId bel) const;
bool isBelLocationValid(BelId bel) const;

View File

@ -226,6 +226,28 @@ void arch_wrap_python(py::module &m)
pass_through<DelayInfo>>::def_wrap(ctx_cls, "addCellTimingClockToOut", "cell"_a, "port"_a,
"clock"_a, "clktoq"_a);
// const\_range\<BelBucketId\> getBelBuckets() const
fn_wrapper_0a<Context, decltype(&Context::getBelBuckets), &Context::getBelBuckets,
wrap_context<const std::vector<BelBucketId> &>>::def_wrap(ctx_cls, "getBelBuckets");
// BelBucketId getBelBucketForBel(BelId bel) const
fn_wrapper_1a<Context, decltype(&Context::getBelBucketForBel), &Context::getBelBucketForBel,
conv_to_str<BelBucketId>, conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelBucketForBel");
// BelBucketId getBelBucketForCellType(IdString cell\_type) const
fn_wrapper_1a<Context, decltype(&Context::getBelBucketForCellType), &Context::getBelBucketForCellType,
conv_to_str<BelBucketId>, conv_from_str<IdString>>::def_wrap(ctx_cls, "getBelBucketForCellType");
// const\_range\<BelId\> getBelsInBucket(BelBucketId bucket) const
fn_wrapper_1a<Context, decltype(&Context::getBelsInBucket), &Context::getBelsInBucket,
wrap_context<const std::vector<BelId> &>, conv_from_str<BelBucketId>>::def_wrap(ctx_cls,
"getBelsInBucket");
// bool isValidBelForCellType(IdString cell\_type, BelId bel) const
fn_wrapper_2a<Context, decltype(&Context::isValidBelForCellType), &Context::isValidBelForCellType,
pass_through<bool>, conv_from_str<IdString>, conv_from_str<BelId>>::def_wrap(ctx_cls,
"isValidBelForCellType");
WRAP_MAP_UPTR(m, CellMap, "IdCellMap");
WRAP_MAP_UPTR(m, NetMap, "IdNetMap");
WRAP_MAP(m, HierarchyMap, wrap_context<HierarchicalCell &>, "HierarchyMap");

View File

@ -51,6 +51,7 @@ typedef IdString WireId;
typedef IdString PipId;
typedef IdString GroupId;
typedef IdString DecalId;
typedef IdString BelBucketId;
struct ArchNetInfo
{

View File

@ -739,6 +739,15 @@ Arch::Arch(ArchArgs args) : args(args)
}
// Dummy for empty decals
decal_graphics[IdString()];
std::unordered_set<IdString> bel_types;
for (BelId bel : getBels()) {
bel_types.insert(getBelType(bel));
}
for (IdString bel_type : bel_types) {
cell_types.push_back(bel_type);
}
}
void IdString::initialize_arch(const BaseCtx *ctx)

View File

@ -422,6 +422,31 @@ struct Arch : BaseCtx
// Get the TimingClockingInfo of a port
TimingClockingInfo getPortClockingInfo(const CellInfo *cell, IdString port, int index) const;
bool isValidBelForCellType(IdString cell_type, BelId bel) const { return cell_type == getBelType(bel); }
const std::vector<IdString> &getCellTypes() const { return cell_types; }
std::vector<BelBucketId> getBelBuckets() const { return cell_types; }
IdString getBelBucketName(BelBucketId bucket) const { return bucket; }
BelBucketId getBelBucketByName(IdString name) const { return name; }
BelBucketId getBelBucketForBel(BelId bel) const { return getBelType(bel); }
BelBucketId getBelBucketForCellType(IdString cell_type) const { return cell_type; }
std::vector<BelId> getBelsInBucket(BelBucketId bucket) const
{
std::vector<BelId> bels;
for (BelId bel : getBels()) {
if (getBelType(bel) == bucket) {
bels.push_back(bel);
}
}
return bels;
}
bool isValidBelForCell(CellInfo *cell, BelId bel) const;
bool isBelLocationValid(BelId bel) const;
@ -434,6 +459,8 @@ struct Arch : BaseCtx
// Internal usage
void assignArchInfo();
bool cellsCompatible(const CellInfo **cells, int count) const;
std::vector<IdString> cell_types;
};
NEXTPNR_NAMESPACE_END

View File

@ -72,6 +72,7 @@ typedef IdString WireId;
typedef IdString PipId;
typedef IdString GroupId;
typedef IdString DecalId;
typedef IdString BelBucketId;
struct ArchNetInfo
{

View File

@ -115,6 +115,19 @@ Arch::Arch(ArchArgs args) : args(args)
wire_to_net.resize(chip_info->wire_data.size());
pip_to_net.resize(chip_info->pip_data.size());
switches_locked.resize(chip_info->num_switches);
std::unordered_set<IdString> bel_types;
for (BelId bel : getBels()) {
bel_types.insert(getBelType(bel));
}
for (IdString bel_type : bel_types) {
cell_types.push_back(bel_type);
BelBucketId bucket;
bucket.name = bel_type;
buckets.push_back(bucket);
}
}
// -----------------------------------------------------------------------

View File

@ -821,6 +821,47 @@ struct Arch : BaseCtx
// Perform placement validity checks, returning false on failure (all
// implemented in arch_place.cc)
// Whether this cell type can be placed at this BEL.
bool isValidBelForCellType(IdString cell_type, BelId bel) const { return cell_type == getBelType(bel); }
const std::vector<IdString> &getCellTypes() const { return cell_types; }
std::vector<BelBucketId> getBelBuckets() const { return buckets; }
IdString getBelBucketName(BelBucketId bucket) const { return bucket.name; }
BelBucketId getBelBucketByName(IdString name) const
{
BelBucketId bucket;
bucket.name = name;
return bucket;
}
BelBucketId getBelBucketForBel(BelId bel) const
{
BelBucketId bucket;
bucket.name = getBelType(bel);
return bucket;
}
BelBucketId getBelBucketForCellType(IdString cell_type) const
{
BelBucketId bucket;
bucket.name = cell_type;
return bucket;
}
std::vector<BelId> getBelsInBucket(BelBucketId bucket) const
{
std::vector<BelId> bels;
for (BelId bel : getBels()) {
if (getBelType(bel) == bucket.name) {
bels.push_back(bel);
}
}
return bels;
}
// Whether or not a given cell can be placed at a given Bel
// This is not intended for Bel type checks, but finer-grained constraints
// such as conflicting set/reset signals, etc
@ -862,6 +903,9 @@ struct Arch : BaseCtx
static const std::vector<std::string> availablePlacers;
static const std::string defaultRouter;
static const std::vector<std::string> availableRouters;
std::vector<IdString> cell_types;
std::vector<BelBucketId> buckets;
};
void ice40DelayFuzzerMain(Context *ctx);

View File

@ -75,6 +75,8 @@ void arch_wrap_python(py::module &m)
typedef const PipRange UphillPipRange;
typedef const PipRange DownhillPipRange;
typedef const std::vector<BelBucketId> &BelBucketRange;
typedef const std::vector<BelId> &BelRangeForBelBucket;
#include "arch_pybindings_shared.h"
WRAP_RANGE(m, Bel, conv_to_str<BelId>);
@ -83,7 +85,6 @@ void arch_wrap_python(py::module &m)
WRAP_RANGE(m, Pip, conv_to_str<PipId>);
WRAP_RANGE(m, BelPin, wrap_context<BelPin>);
WRAP_MAP_UPTR(m, CellMap, "IdCellMap");
WRAP_MAP_UPTR(m, NetMap, "IdNetMap");
WRAP_MAP(m, HierarchyMap, wrap_context<HierarchicalCell &>, "HierarchyMap");

View File

@ -76,6 +76,18 @@ template <> struct string_converter<PipId>
}
};
template <> struct string_converter<BelBucketId>
{
BelBucketId from_str(Context *ctx, std::string name) { return ctx->getBelBucketByName(ctx->id(name)); }
std::string to_str(Context *ctx, BelBucketId id)
{
if (id == BelBucketId())
throw bad_wrap();
return ctx->getBelBucketName(id).str(ctx);
}
};
template <> struct string_converter<BelPin>
{
BelPin from_str(Context *ctx, std::string name)

View File

@ -170,6 +170,15 @@ struct ArchCellInfo
};
};
struct BelBucketId
{
IdString name;
bool operator==(const BelBucketId &other) const { return (name == other.name); }
bool operator!=(const BelBucketId &other) const { return (name != other.name); }
bool operator<(const BelBucketId &other) const { return name < other.name; }
};
NEXTPNR_NAMESPACE_END
namespace std {
@ -213,4 +222,15 @@ template <> struct hash<NEXTPNR_NAMESPACE_PREFIX DecalId>
return seed;
}
};
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX BelBucketId>
{
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX BelBucketId &bucket) const noexcept
{
std::size_t seed = 0;
boost::hash_combine(seed, hash<NEXTPNR_NAMESPACE_PREFIX IdString>()(bucket.name));
return seed;
}
};
} // namespace std

View File

@ -171,6 +171,19 @@ Arch::Arch(ArchArgs args) : args(args)
}
if (!speed_grade)
log_error("Unknown speed grade '%s'.\n", speed.c_str());
std::unordered_set<IdString> bel_types;
for (BelId bel : getBels()) {
bel_types.insert(getBelType(bel));
}
for (IdString bel_type : bel_types) {
cell_types.push_back(bel_type);
BelBucketId bucket;
bucket.name = bel_type;
buckets.push_back(bucket);
}
}
// -----------------------------------------------------------------------
@ -635,8 +648,8 @@ bool Arch::place()
cfg.ioBufTypes.insert(id_SEIO18_CORE);
cfg.ioBufTypes.insert(id_OSC_CORE);
cfg.cellGroups.emplace_back();
cfg.cellGroups.back().insert(id_OXIDE_COMB);
cfg.cellGroups.back().insert(id_OXIDE_FF);
cfg.cellGroups.back().insert({id_OXIDE_COMB});
cfg.cellGroups.back().insert({id_OXIDE_FF});
cfg.beta = 0.5;
cfg.criticalityExponent = 7;

View File

@ -1335,6 +1335,47 @@ struct Arch : BaseCtx
// Perform placement validity checks, returning false on failure (all
// implemented in arch_place.cc)
// Whether this cell type can be placed at this BEL.
bool isValidBelForCellType(IdString cell_type, BelId bel) const { return cell_type == getBelType(bel); }
const std::vector<IdString> &getCellTypes() const { return cell_types; }
std::vector<BelBucketId> getBelBuckets() const { return buckets; }
IdString getBelBucketName(BelBucketId bucket) const { return bucket.name; }
BelBucketId getBelBucketByName(IdString name) const
{
BelBucketId bucket;
bucket.name = name;
return bucket;
}
BelBucketId getBelBucketForBel(BelId bel) const
{
BelBucketId bucket;
bucket.name = getBelType(bel);
return bucket;
}
BelBucketId getBelBucketForCellType(IdString cell_type) const
{
BelBucketId bucket;
bucket.name = cell_type;
return bucket;
}
std::vector<BelId> getBelsInBucket(BelBucketId bucket) const
{
std::vector<BelId> bels;
for (BelId bel : getBels()) {
if (getBelType(bel) == bucket.name) {
bels.push_back(bel);
}
}
return bels;
}
// Whether or not a given cell can be placed at a given Bel
// This is not intended for Bel type checks, but finer-grained constraints
// such as conflicting set/reset signals, etc
@ -1536,6 +1577,9 @@ struct Arch : BaseCtx
// -------------------------------------------------
void write_fasm(std::ostream &out) const;
std::vector<IdString> cell_types;
std::vector<BelBucketId> buckets;
};
NEXTPNR_NAMESPACE_END

View File

@ -55,6 +55,9 @@ void arch_wrap_python(py::module &m)
typedef UpDownhillPipRange DownhillPipRange;
typedef WireBelPinRange BelPinRange;
typedef const std::vector<BelBucketId> &BelBucketRange;
typedef const std::vector<BelId> &BelRangeForBelBucket;
#include "arch_pybindings_shared.h"
WRAP_RANGE(m, Bel, conv_to_str<BelId>);

View File

@ -76,6 +76,18 @@ template <> struct string_converter<PipId>
}
};
template <> struct string_converter<BelBucketId>
{
BelBucketId from_str(Context *ctx, std::string name) { return ctx->getBelBucketByName(ctx->id(name)); }
std::string to_str(Context *ctx, BelBucketId id)
{
if (id == BelBucketId())
throw bad_wrap();
return ctx->getBelBucketName(id).str(ctx);
}
};
template <> struct string_converter<BelPin>
{
BelPin from_str(Context *ctx, std::string name)

View File

@ -114,6 +114,15 @@ struct PipId
}
};
struct BelBucketId
{
IdString name;
bool operator==(const BelBucketId &other) const { return (name == other.name); }
bool operator!=(const BelBucketId &other) const { return (name != other.name); }
bool operator<(const BelBucketId &other) const { return name < other.name; }
};
struct GroupId
{
enum : int8_t
@ -250,4 +259,15 @@ template <> struct hash<NEXTPNR_NAMESPACE_PREFIX DecalId>
return seed;
}
};
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX BelBucketId>
{
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX BelBucketId &bucket) const noexcept
{
std::size_t seed = 0;
boost::hash_combine(seed, hash<NEXTPNR_NAMESPACE_PREFIX IdString>()(bucket.name));
return seed;
}
};
} // namespace std