From 2472b0d38e19ba3a934ee9296c8c195d5fbb5cd1 Mon Sep 17 00:00:00 2001 From: Lofty Date: Mon, 26 Jan 2026 14:01:12 +0000 Subject: [PATCH] current progress --- common/route/router2.cc | 214 +++++++++++++++++++++++--- himbaechel/uarch/gatemate/gatemate.cc | 36 ++++- himbaechel/uarch/gatemate/gatemate.h | 4 + 3 files changed, 222 insertions(+), 32 deletions(-) diff --git a/common/route/router2.cc b/common/route/router2.cc index df1a7876..674c5ed0 100644 --- a/common/route/router2.cc +++ b/common/route/router2.cc @@ -37,8 +37,10 @@ #include #include +#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 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> wires; std::vector> arcs; + dict 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 value_count; + float hist_cong_cost = 1.0; + }; + Context *ctx; Router2Cfg cfg; @@ -237,6 +254,25 @@ struct Router2 } } + dict resource_to_idx; + std::vector 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 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 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 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 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 route_queue; std::set 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 already_updated; + pool already_updated_wires; + pool 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()); diff --git a/himbaechel/uarch/gatemate/gatemate.cc b/himbaechel/uarch/gatemate/gatemate.cc index 4d1a9c1a..87ee84cf 100644 --- a/himbaechel/uarch/gatemate/gatemate.cc +++ b/himbaechel/uarch/gatemate/gatemate.cc @@ -20,6 +20,7 @@ #include #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 &net ctx->bindBel(bel, cell, PlaceStrength::STRENGTH_FIXED); } - auto resource_map = dict{ - {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{ @@ -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{ + {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; diff --git a/himbaechel/uarch/gatemate/gatemate.h b/himbaechel/uarch/gatemate/gatemate.h index d775166d..67b531e8 100644 --- a/himbaechel/uarch/gatemate/gatemate.h +++ b/himbaechel/uarch/gatemate/gatemate.h @@ -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;