current progress

This commit is contained in:
Lofty 2026-01-26 14:01:12 +00:00
parent 86d0858308
commit 2472b0d38e
3 changed files with 222 additions and 32 deletions

View File

@ -37,8 +37,10 @@
#include <queue>
#include <set>
#include "idstringlist.h"
#include "log.h"
#include "nextpnr.h"
#include "nextpnr_assertions.h"
#include "router1.h"
#include "scope_lock.h"
#include "timing.h"
@ -49,6 +51,12 @@ NEXTPNR_NAMESPACE_BEGIN
namespace {
struct Router2
{
// std::pair<int, int> is a bit too confusing, so:
struct NetResourceData
{
int value = 0;
int count = 0;
};
struct PerArcData
{
@ -64,6 +72,7 @@ struct Router2
WireId src_wire;
dict<WireId, std::pair<PipId, int>> wires;
std::vector<std::vector<PerArcData>> arcs;
dict<IdStringList, NetResourceData> resources;
BoundingBox bb;
// Coordinates of the center of the net, used for the weight-to-average
int cx, cy, hpwl;
@ -99,6 +108,14 @@ struct Router2
float cost_fwd = 0.0, cost_bwd = 0.0;
};
struct PerResourceData
{
IdStringList key;
// Historical congestion cost
dict<int, int> value_count;
float hist_cong_cost = 1.0;
};
Context *ctx;
Router2Cfg cfg;
@ -237,6 +254,25 @@ struct Router2
}
}
dict<IdStringList, int> resource_to_idx;
std::vector<PerResourceData> flat_resources;
PerResourceData &resource_data(IdStringList r) { return flat_resources[resource_to_idx.at(r)]; }
void setup_resources()
{
// should we have a getResources()???
for (auto pip : ctx->getPips()) {
auto resource_key = ctx->getResourceKeyForPip(pip);
if (resource_key == IdStringList() || resource_to_idx.count(resource_key) != 0)
continue;
auto data = PerResourceData{};
data.key = resource_key;
resource_to_idx.insert({resource_key, flat_resources.size()});
flat_resources.push_back(data);
}
}
struct QueuedWire
{
@ -316,8 +352,8 @@ struct Router2
void bind_pip_internal(PerNetData &net, store_index<PortRef> user, int wire, PipId pip)
{
auto &wd = flat_wires.at(wire);
auto found = net.wires.find(wd.w);
if (found == net.wires.end()) {
auto wire_found = net.wires.find(wd.w);
if (wire_found == net.wires.end()) {
// Not yet used for any arcs of this net, add to list
net.wires.emplace(wd.w, std::make_pair(pip, 1));
// Increase bound count of wire by 1
@ -325,22 +361,72 @@ struct Router2
} else {
// Already used for at least one other arc of this net
// Don't allow two uphill PIPs for the same net and wire
NPNR_ASSERT(found->second.first == pip);
NPNR_ASSERT(wire_found->second.first == pip);
// Increase the count of bound arcs
++found->second.second;
++wire_found->second.second;
}
if (pip == PipId())
return;
auto resource_key = ctx->getResourceKeyForPip(pip);
if (resource_key == IdStringList())
return;
auto &rd = resource_data(resource_key);
auto resource_value = ctx->getResourceValueForPip(pip);
auto resource_found = net.resources.find(resource_key);
if (resource_found == net.resources.end()) {
net.resources.emplace(resource_key, NetResourceData{resource_value, 1});
} else {
// net resource value must agree with arc resource value
NPNR_ASSERT(resource_found->second.value == resource_value);
++resource_found->second.count;
}
auto resource_value_count = rd.value_count.find(resource_value);
if (resource_value_count == rd.value_count.end()) {
rd.value_count.insert({resource_value, 1});
} else {
++resource_value_count->second;
}
}
void unbind_pip_internal(PerNetData &net, store_index<PortRef> user, WireId wire)
{
auto &wd = wire_data(wire);
auto &b = net.wires.at(wd.w);
--b.second;
if (b.second == 0) {
auto &wire_found = net.wires.at(wd.w);
auto pip = wire_found.first;
--wire_found.second;
if (wire_found.second == 0) {
// No remaining arcs of this net bound to this wire
--wd.curr_cong;
net.wires.erase(wd.w);
}
if (pip == PipId())
return;
auto resource_key = ctx->getResourceKeyForPip(pip);
if (resource_key == IdStringList())
return;
auto &rd = resource_data(resource_key);
auto resource_value = ctx->getResourceValueForPip(pip);
auto resource_found = net.resources.at(resource_key);
--resource_found.count;
--rd.value_count.at(resource_value);
if (resource_found.count == 0) {
net.resources.erase(resource_key);
}
if (rd.value_count.at(resource_value) == 0) {
rd.value_count.erase(resource_value);
}
}
void ripup_arc(NetInfo *net, store_index<PortRef> user, size_t phys_pin)
@ -369,6 +455,8 @@ struct Router2
int overuse = wd.curr_cong;
float hist_cost = 1.0f + crit_weight * (wd.hist_cong_cost - 1.0f);
float bias_cost = 0;
float resource_hist_cost = 1.0f;
float resource_present_cost = 1.0f;
int source_uses = 0;
if (nd.wires.count(wire)) {
overuse -= 1;
@ -379,8 +467,15 @@ struct Router2
Loc pl = ctx->getPipLocation(pip);
bias_cost = cfg.bias_cost_factor * (base_cost / int(net->users.entries())) *
((std::abs(pl.x - nd.cx) + std::abs(pl.y - nd.cy)) / float(nd.hpwl));
auto resource_key = ctx->getResourceKeyForPip(pip);
if (resource_key != IdStringList()) {
auto &rd = resource_data(resource_key);
resource_hist_cost = 1.0f + crit_weight * (rd.hist_cong_cost - 1.0f);
resource_present_cost = 1.0f + rd.value_count.size() * curr_cong_weight * crit_weight;
}
}
return base_cost * hist_cost * present_cost / (1 + (source_uses * crit_weight)) + bias_cost;
return base_cost * hist_cost * present_cost / (1 + (source_uses * crit_weight)) + bias_cost +
resource_hist_cost * resource_present_cost;
}
float get_togo_cost(NetInfo *net, store_index<PortRef> user, int wire, WireId src_sink, bool bwd, float crit_weight)
@ -409,6 +504,12 @@ struct Router2
auto &uh = nd.wires.at(cursor).first;
if (uh == PipId())
break;
auto resource_key = ctx->getResourceKeyForPip(uh);
if (resource_key != IdStringList()) {
auto &rd = resource_data(resource_key);
if (rd.value_count.size() > 1)
break;
}
cursor = ctx->getPipSrcWire(uh);
}
return (cursor == src_wire);
@ -812,6 +913,14 @@ struct Router2
auto fnd_wire = nd.wires.find(next);
if (fnd_wire != nd.wires.end() && fnd_wire->second.first != dh)
continue;
// Don't allow the same resource to be bound to the same net with a different value
auto resource_key = ctx->getResourceKeyForPip(dh);
if (resource_key != IdStringList()) {
auto fnd_resource = nd.resources.find(resource_key);
if (fnd_resource != nd.resources.end() &&
fnd_resource->second.value != ctx->getResourceValueForPip(dh))
continue;
}
if (!thread_test_wire(t, nwd))
continue; // thread safety issue
set_visited_fwd(t, next_idx, dh, next_score.delay);
@ -835,7 +944,6 @@ struct Router2
auto fnd_wire = nd.wires.find(curr_data.w);
if (fnd_wire != nd.wires.end())
bound_pip = fnd_wire->second.first;
for (PipId uh : ctx->getPipsUphill(curr_data.w)) {
if (bound_pip != PipId() && bound_pip != uh)
continue;
@ -862,6 +970,14 @@ struct Router2
// Reserved for another net
if (nwd.reserved_net != -1 && nwd.reserved_net != net->udata)
continue;
// Don't allow the same resource to be bound to the same net with a different value
auto resource_key = ctx->getResourceKeyForPip(uh);
if (resource_key != IdStringList()) {
auto fnd_resource = nd.resources.find(resource_key);
if (fnd_resource != nd.resources.end() &&
fnd_resource->second.value != ctx->getResourceValueForPip(uh))
continue;
}
if (!thread_test_wire(t, nwd))
continue; // thread safety issue
set_visited_bwd(t, next_idx, uh, next_score.delay);
@ -892,8 +1008,15 @@ struct Router2
if (pip == PipId()) {
break;
}
ROUTE_LOG_DBG(" fwd pip: %s (%d, %d)\n", ctx->nameOfPip(pip), ctx->getPipLocation(pip).x,
ctx->getPipLocation(pip).y);
auto resource_key = ctx->getResourceKeyForPip(pip);
if (resource_key != IdStringList()) {
ROUTE_LOG_DBG(" fwd pip: %s (%d, %d) %s = %d\n", ctx->nameOfPip(pip),
ctx->getPipLocation(pip).x, ctx->getPipLocation(pip).y,
resource_key.str(ctx).c_str(), ctx->getResourceValueForPip(pip));
} else {
ROUTE_LOG_DBG(" fwd pip: %s (%d, %d)\n", ctx->nameOfPip(pip),
ctx->getPipLocation(pip).x, ctx->getPipLocation(pip).y);
}
cursor_bwd = wire_to_idx.at(ctx->getPipSrcWire(pip));
}
@ -924,8 +1047,16 @@ struct Router2
if (pip == PipId()) {
break;
}
ROUTE_LOG_DBG(" bwd pip: %s (%d, %d)\n", ctx->nameOfPip(pip), ctx->getPipLocation(pip).x,
ctx->getPipLocation(pip).y);
auto resource_key = ctx->getResourceKeyForPip(pip);
if (resource_key != IdStringList()) {
ROUTE_LOG_DBG(" bwd pip: %s (%d, %d) %s = %d\n", ctx->nameOfPip(pip),
ctx->getPipLocation(pip).x, ctx->getPipLocation(pip).y, resource_key.str(ctx).c_str(),
ctx->getResourceValueForPip(pip));
} else {
ROUTE_LOG_DBG(" bwd pip: %s (%d, %d)\n", ctx->nameOfPip(pip), ctx->getPipLocation(pip).x,
ctx->getPipLocation(pip).y);
}
cursor_fwd = wire_to_idx.at(ctx->getPipDstWire(pip));
bind_pip_internal(nd, i, cursor_fwd, pip);
if (ctx->debug && !is_mt) {
@ -1040,35 +1171,65 @@ struct Router2
int total_wire_use = 0;
int overused_wires = 0;
int total_overuse = 0;
int total_wire_overuse = 0;
int total_resource_use = 0;
int overused_resources = 0;
int total_resource_overuse = 0;
std::vector<int> route_queue;
std::set<int> failed_nets;
void update_congestion()
{
total_overuse = 0;
total_wire_overuse = 0;
overused_wires = 0;
total_wire_use = 0;
total_resource_overuse = 0;
overused_resources = 0;
total_resource_use = 0;
failed_nets.clear();
pool<WireId> already_updated;
pool<WireId> already_updated_wires;
pool<IdStringList> already_updated_resources;
for (size_t i = 0; i < nets.size(); i++) {
auto &nd = nets.at(i);
for (const auto &w : nd.wires) {
++total_wire_use;
auto &wd = wire_data(w.first);
if (wd.curr_cong > 1) {
if (already_updated.count(w.first)) {
++total_overuse;
if (already_updated_wires.count(w.first)) {
++total_wire_overuse;
} else {
if (curr_cong_weight > 0)
wd.hist_cong_cost =
std::min(1e9, wd.hist_cong_cost + (wd.curr_cong - 1) * hist_cong_weight);
already_updated.insert(w.first);
already_updated_wires.insert(w.first);
++overused_wires;
}
failed_nets.insert(i);
}
}
for (const auto &r : nd.resources) {
++total_resource_use;
auto &rd = resource_data(r.first);
if (rd.value_count.size() > 1) {
printf("overused resource %s by %s\n", r.first.str(ctx).c_str(),
nets_by_udata.at(i)->name.c_str(ctx));
if (already_updated_resources.count(r.first)) {
++total_resource_overuse;
} else {
if (curr_cong_weight > 0)
rd.hist_cong_cost =
std::min(1e9, rd.hist_cong_cost + (rd.value_count.size() - 1) * hist_cong_weight);
already_updated_resources.insert(r.first);
++overused_resources;
printf("overused resource %s in {", r.first.str(ctx).c_str());
for (const auto &pair : rd.value_count) {
printf("%d, ", pair.first);
}
printf("}\n");
}
failed_nets.insert(i);
}
}
}
for (int n : failed_nets) {
auto &net_data = nets.at(n);
@ -1482,6 +1643,7 @@ struct Router2
log_info("Running router2...\n");
log_info("Setting up routing resources...\n");
auto rstart = std::chrono::high_resolution_clock::now();
setup_resources();
setup_nets();
setup_wires();
find_all_reserved_wires();
@ -1567,7 +1729,7 @@ struct Router2
}
}
}
if (overused_wires == 0 && tmgfail == 0) {
if (overused_wires == 0 && overused_resources == 0 && tmgfail == 0) {
// Try and actually bind nextpnr Arch API wires
bind_and_check_all();
}
@ -1575,13 +1737,17 @@ struct Router2
route_queue.push_back(cn);
if (timing_driven_ripup)
log_info(" iter=%d wires=%d overused=%d overuse=%d tmgfail=%d archfail=%s\n", iter, total_wire_use,
overused_wires, total_overuse, tmgfail,
overused_wires, total_wire_overuse, tmgfail,
(overused_wires > 0 || tmgfail > 0) ? "NA" : std::to_string(arch_fail).c_str());
else
log_info(" iter=%d wires=%d overused=%d overuse=%d archfail=%s\n", iter, total_wire_use,
overused_wires, total_overuse,
(overused_wires > 0 || tmgfail > 0) ? "NA" : std::to_string(arch_fail).c_str());
log_info(
" iter=%d wires=%d overused=%d overuse=%d resources=%d overused=%d overuse=%d archfail=%s\n",
iter, total_wire_use, overused_wires, total_wire_overuse, total_resource_use,
overused_resources, total_resource_overuse,
(overused_wires > 0 || tmgfail > 0) ? "NA" : std::to_string(arch_fail).c_str());
++iter;
if (iter >= 1000)
log_error("giving up\n");
if (curr_cong_weight < 1e9)
curr_cong_weight += cfg.curr_cong_mult;
} while (!failed_nets.empty());

View File

@ -20,6 +20,7 @@
#include <utility>
#include "gatemate.h"
#include "idstringlist.h"
#include "log.h"
#include "nextpnr_assertions.h"
#include "placer_heap.h"
@ -516,15 +517,8 @@ void GateMateImpl::reassign_cplines(NetInfo *ni, const dict<WireId, PipMap> &net
ctx->bindBel(bel, cell, PlaceStrength::STRENGTH_FIXED);
}
auto resource_map = dict<uint32_t, IdString>{
{PipMask::C_SELX, id_C_SELX}, {PipMask::C_SELY1, id_C_SELY1}, {PipMask::C_SELY2, id_C_SELY2},
{PipMask::C_SEL_C, id_C_SEL_C}, {PipMask::C_SEL_P, id_C_SEL_P}, {PipMask::C_Y12, id_C_Y12},
{PipMask::C_CX_I, id_C_CX_I}, {PipMask::C_CY1_I, id_C_CY1_I}, {PipMask::C_CY2_I, id_C_CY2_I},
{PipMask::C_PX_I, id_C_PX_I}, {PipMask::C_PY1_I, id_C_PY1_I}, {PipMask::C_PY2_I, id_C_PY2_I},
};
NPNR_ASSERT(resource_map.count(extra_data.resource));
cell->setParam(resource_map.at(extra_data.resource), Property(extra_data.value, extra_data.bits));
cell->setParam(ctx->getResourceKeyForPip(pip)[1], Property(extra_data.value, extra_data.bits));
// We have to discover the ports needed by this config.
auto input_port_map = dict<IdString, IdString>{
@ -935,6 +929,32 @@ void GateMateImpl::expandBoundingBox(BoundingBox &bb) const
bb.y1 = std::min((bb.y1 & 0xfffe) + 5, ctx->getGridDimY());
}
IdStringList GateMateImpl::getResourceKeyForPip(PipId pip) const
{
const auto &extra_data = *pip_extra_data(pip);
if (extra_data.type != PipExtra::PIP_EXTRA_MUX || extra_data.resource == 0)
return IdStringList();
auto resource_map = dict<uint32_t, IdString>{
{PipMask::C_SELX, id_C_SELX}, {PipMask::C_SELY1, id_C_SELY1}, {PipMask::C_SELY2, id_C_SELY2},
{PipMask::C_SEL_C, id_C_SEL_C}, {PipMask::C_SEL_P, id_C_SEL_P}, {PipMask::C_Y12, id_C_Y12},
{PipMask::C_CX_I, id_C_CX_I}, {PipMask::C_CY1_I, id_C_CY1_I}, {PipMask::C_CY2_I, id_C_CY2_I},
{PipMask::C_PX_I, id_C_PX_I}, {PipMask::C_PY1_I, id_C_PY1_I}, {PipMask::C_PY2_I, id_C_PY2_I},
};
NPNR_ASSERT(resource_map.count(extra_data.resource));
return IdStringList::concat(ctx->getPipName(pip)[0], resource_map.at(extra_data.resource));
}
int GateMateImpl::getResourceValueForPip(PipId pip) const
{
const auto &extra_data = *pip_extra_data(pip);
if (extra_data.type != PipExtra::PIP_EXTRA_MUX || extra_data.resource == 0)
return 0;
return extra_data.value;
}
void GateMateImpl::configurePlacerHeap(PlacerHeapCfg &cfg)
{
cfg.chainRipup = true;

View File

@ -22,6 +22,7 @@
#include "extra_data.h"
#include "himbaechel_api.h"
#include "idstringlist.h"
#include "log.h"
#include "nextpnr.h"
#include "util.h"
@ -79,6 +80,9 @@ struct GateMateImpl : HimbaechelAPI
bool isPipInverting(PipId pip) const override;
IdStringList getResourceKeyForPip(PipId pip) const override;
int getResourceValueForPip(PipId pip) const override;
const GateMateTileExtraDataPOD *tile_extra_data(int tile) const;
const GateMateBelExtraDataPOD *bel_extra_data(BelId bel) const;
const GateMatePipExtraDataPOD *pip_extra_data(PipId pip) const;