himbaechel: Enable use of electrostatic placer (#1657)

Signed-off-by: gatecat <gatecat@ds0.me>
This commit is contained in:
myrtle 2026-03-03 12:19:41 +01:00 committed by GitHub
parent ab7aa9ffab
commit 575689b7e4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 112 additions and 5 deletions

View File

@ -279,13 +279,16 @@ class StaticPlacer
}
}
bool lookup_group(IdString type, int &group, StaticRect &rect)
bool lookup_group(const CellInfo *ci, int &group, StaticRect &rect)
{
for (size_t i = 0; i < cfg.cell_groups.size(); i++) {
const auto &g = cfg.cell_groups.at(i);
if (g.cell_area.count(type)) {
if (g.cell_area.count(ci->type)) {
group = i;
rect = g.cell_area.at(type);
rect = g.cell_area.at(ci->type);
if (auto rect_override = cfg.get_cell_area_override(ctx, ci)) {
rect = *rect_override;
}
return true;
}
}
@ -320,6 +323,8 @@ class StaticPlacer
for (int dx = 0; dx <= int(size.w); dx++) {
float h = (dy == int(size.h)) ? (size.h - int(size.h)) : 1;
float w = (dx == int(size.w)) ? (size.w - int(size.w)) : 1;
if ((loc.x + dx) >= width || (loc.y + dy) >= height)
continue;
group.loc_area.at(loc.x + dx, loc.y + dy) += w * h;
}
}
@ -412,7 +417,7 @@ class StaticPlacer
int cell_group;
StaticRect rect;
// Mismatched group case
if (!lookup_group(ci->type, cell_group, rect)) {
if (!lookup_group(ci, cell_group, rect)) {
if (ci->bel == BelId()) {
for (auto bel : ctx->getBels()) {
if (ctx->isValidBelForCellType(ci->type, bel) && ctx->checkBelAvail(bel)) {

View File

@ -23,6 +23,8 @@
#include "log.h"
#include "nextpnr.h"
#include <optional>
NEXTPNR_NAMESPACE_BEGIN
struct StaticRect
@ -64,6 +66,11 @@ struct PlacerStaticCfg
// groups < logic_groups are logic like LUTs and FFs, further groups for BRAM/DSP/misc
std::vector<StaticCellGroupCfg> cell_groups;
int logic_groups = 2;
// this is an optional callback to override the area of a cell e.g. based on configuration
std::function<std::optional<StaticRect>(Context *, const CellInfo *)> get_cell_area_override = [](Context *, const CellInfo *) {
return std::optional<StaticRect>{};
};
};
extern bool placer_static(Context *ctx, PlacerStaticCfg cfg);

View File

@ -26,6 +26,8 @@
#include "command.h"
#include "placer1.h"
#include "placer_heap.h"
#include "placer_static.h"
#include "router1.h"
#include "router2.h"
#include "util.h"
@ -271,6 +273,10 @@ bool Arch::place()
uarch->configurePlacerHeap(cfg);
cfg.ioBufTypes.insert(id("GENERIC_IOB"));
retVal = placer_heap(getCtx(), cfg);
} else if (placer == "static") {
PlacerStaticCfg cfg(getCtx());
uarch->configurePlacerStatic(cfg);
retVal = placer_static(getCtx(), cfg);
} else if (placer == "sa") {
retVal = placer1(getCtx(), Placer1Cfg(getCtx()));
} else {
@ -403,7 +409,7 @@ void IdString::initialize_arch(const BaseCtx *ctx) {}
const std::string Arch::defaultPlacer = "heap";
const std::vector<std::string> Arch::availablePlacers = {"sa", "heap"};
const std::vector<std::string> Arch::availablePlacers = {"sa", "heap", "static"};
const std::string Arch::defaultRouter = "default";
const std::vector<std::string> Arch::availableRouters = {"default", "router1", "router2"};

View File

@ -102,6 +102,11 @@ TimingClockingInfo HimbaechelAPI::getPortClockingInfo(const CellInfo *cell, IdSt
return ctx->get_port_clocking_info_default(cell, port, index);
}
void HimbaechelAPI::configurePlacerStatic(PlacerStaticCfg &)
{
log_error("This uarch does not support the 'static' placer.\n");
}
HimbaechelArch *HimbaechelArch::list_head;
HimbaechelArch::HimbaechelArch(const std::string &name) : name(name)
{

View File

@ -55,6 +55,7 @@ struct Arch;
struct Context;
struct PlacerHeapCfg;
struct PlacerStaticCfg;
namespace po = boost::program_options;
@ -146,6 +147,7 @@ struct HimbaechelAPI
// For custom placer configuration
virtual void configurePlacerHeap(PlacerHeapCfg &cfg) {};
virtual void configurePlacerStatic(PlacerStaticCfg &cfg);
virtual std::string getDefaultRouter() const { return "router1"; };

View File

@ -27,6 +27,8 @@
#include "util.h"
#include "placer_heap.h"
#include "placer_static.h"
#include "xilinx.h"
#include "himbaechel_helpers.h"
@ -295,6 +297,85 @@ void XilinxImpl::configurePlacerHeap(PlacerHeapCfg &cfg)
};
}
void XilinxImpl::configurePlacerStatic(PlacerStaticCfg &cfg)
{
cfg.hpwl_scale_x = 2;
cfg.hpwl_scale_y = 1;
{
cfg.cell_groups.emplace_back();
auto &comb = cfg.cell_groups.back();
comb.name = ctx->id("COMB");
comb.bel_area[id_SLICE_LUTX] = StaticRect(1.0f, 0.125f);
comb.bel_area[id_CARRY4] = StaticRect(0.0f, 0.0f);
comb.bel_area[id_SELMUX2_1] = StaticRect(0.0f, 0.0f);
comb.cell_area[id_SLICE_LUTX] = StaticRect(1.0f, 0.125f);
comb.cell_area[id_CARRY4] = StaticRect(1.0f, 0.5f);
comb.cell_area[id_SELMUX2_1] = StaticRect(1.0f, 0.125f);
comb.zero_area_cells.insert(id_CARRY4);
comb.zero_area_cells.insert(id_SELMUX2_1);
comb.spacer_rect = StaticRect(1.0f, 0.125f);
}
{
cfg.cell_groups.emplace_back();
auto &comb = cfg.cell_groups.back();
comb.name = ctx->id("FF");
comb.cell_area[id_SLICE_FFX] = StaticRect(1.0f, 0.125f);
comb.bel_area[id_SLICE_FFX] = StaticRect(1.0f, 0.125f);
comb.spacer_rect = StaticRect(1.0f, 0.125f);
}
{
cfg.cell_groups.emplace_back();
auto &comb = cfg.cell_groups.back();
comb.name = ctx->id("RAM");
comb.cell_area[id_RAMB18E1_RAMB18E1] = StaticRect(1.0f, 3.0f);
comb.bel_area[id_RAMB18E1_RAMB18E1] = StaticRect(1.0f, 3.0f);
comb.cell_area[id_RAMB36E1_RAMB36E1] = StaticRect(1.0f, 6.0f);
comb.bel_area[id_RAMB36E1_RAMB36E1] = StaticRect(0.0f, 0.0f);
comb.spacer_rect = StaticRect(1.0f, 3.0f);
}
{
cfg.cell_groups.emplace_back();
auto &comb = cfg.cell_groups.back();
comb.name = ctx->id("DSP");
comb.cell_area[id_DSP48E1_DSP48E1] = StaticRect(1.0f, 3.0f);
comb.bel_area[id_DSP48E1_DSP48E1] = StaticRect(1.0f, 3.0f);
comb.spacer_rect = StaticRect(1.0f, 3.0f);
}
cfg.get_cell_area_override = [this](Context *ctx, const CellInfo *ci) -> std::optional<StaticRect> {
if (ci->type != id_SLICE_LUTX)
return {};
auto tags = get_tags(ci);
if (tags->lut.is_memory ||
(ci->cluster != ClusterId() && ctx->getClusterRootCell(ci->cluster)->type == id_CARRY4)) {
// macro LUTs use either a half or whole LUT, always
return {(tags->lut.input_count == 6) ? StaticRect(1.0f, 0.125f) : StaticRect(1.0f, 0.0625f)};
} else {
switch (tags->lut.input_count) { // sliding scale, smaller LUTs pack better
case 6:
return {StaticRect(1.0f, 0.125f)};
case 5:
return {StaticRect(1.0f, 0.09f)};
case 4:
return {StaticRect(1.0f, 0.07f)};
case 3:
return {StaticRect(1.0f, 0.06f)};
case 2:
return {StaticRect(1.0f, 0.04f)};
case 1:
return {StaticRect(1.0f, 0.03f)};
default:
NPNR_ASSERT_FALSE("unhandled LUT input count");
}
}
};
}
void XilinxImpl::preRoute()
{
find_source_sink_locs();

View File

@ -137,6 +137,7 @@ struct XilinxImpl : HimbaechelAPI
void write_fasm(const std::string &filename);
void configurePlacerHeap(PlacerHeapCfg &cfg) override;
void configurePlacerStatic(PlacerStaticCfg &cfg) override;
void fixup_placement();
void fixup_routing();