diff --git a/common/place/placer_static.cc b/common/place/placer_static.cc index 50f65022..91c81e59 100644 --- a/common/place/placer_static.cc +++ b/common/place/placer_static.cc @@ -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)) { diff --git a/common/place/placer_static.h b/common/place/placer_static.h index 1f7097de..dccbb4cc 100644 --- a/common/place/placer_static.h +++ b/common/place/placer_static.h @@ -23,6 +23,8 @@ #include "log.h" #include "nextpnr.h" +#include + 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 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(Context *, const CellInfo *)> get_cell_area_override = [](Context *, const CellInfo *) { + return std::optional{}; + }; }; extern bool placer_static(Context *ctx, PlacerStaticCfg cfg); diff --git a/himbaechel/arch.cc b/himbaechel/arch.cc index 664fffe0..02904951 100644 --- a/himbaechel/arch.cc +++ b/himbaechel/arch.cc @@ -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 Arch::availablePlacers = {"sa", "heap"}; +const std::vector Arch::availablePlacers = {"sa", "heap", "static"}; const std::string Arch::defaultRouter = "default"; const std::vector Arch::availableRouters = {"default", "router1", "router2"}; diff --git a/himbaechel/himbaechel_api.cc b/himbaechel/himbaechel_api.cc index 9fe47bd2..c4b5e7fa 100644 --- a/himbaechel/himbaechel_api.cc +++ b/himbaechel/himbaechel_api.cc @@ -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) { diff --git a/himbaechel/himbaechel_api.h b/himbaechel/himbaechel_api.h index 6aae2cae..57f934f1 100644 --- a/himbaechel/himbaechel_api.h +++ b/himbaechel/himbaechel_api.h @@ -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"; }; diff --git a/himbaechel/uarch/xilinx/xilinx.cc b/himbaechel/uarch/xilinx/xilinx.cc index f97ad447..ee07e48e 100644 --- a/himbaechel/uarch/xilinx/xilinx.cc +++ b/himbaechel/uarch/xilinx/xilinx.cc @@ -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 { + 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(); diff --git a/himbaechel/uarch/xilinx/xilinx.h b/himbaechel/uarch/xilinx/xilinx.h index 26b1c8c8..cffae181 100644 --- a/himbaechel/uarch/xilinx/xilinx.h +++ b/himbaechel/uarch/xilinx/xilinx.h @@ -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();