From 005bffab480fc91882d7426a677d761e2818504c Mon Sep 17 00:00:00 2001 From: Lofty Date: Tue, 7 Jan 2025 09:37:41 +0000 Subject: [PATCH] Constrain routes to have correct inversion state --- common/kernel/base_arch.h | 3 + common/route/router2.cc | 119 +++++++++++++++------------ himbaechel/arch.h | 3 + himbaechel/himbaechel_api.h | 3 + himbaechel/uarch/gatemate/gatemate.h | 5 ++ tests | 2 +- 6 files changed, 83 insertions(+), 52 deletions(-) diff --git a/common/kernel/base_arch.h b/common/kernel/base_arch.h index 96f1d804..f3d6e2c4 100644 --- a/common/kernel/base_arch.h +++ b/common/kernel/base_arch.h @@ -454,6 +454,9 @@ template struct BaseArch : ArchAPI // Flow methods virtual void assignArchInfo() override {}; + // HACK + virtual bool isPipInverting(PipId pip) const { return false; } + // -------------------------------------------------------------- // These structures are used to provide default implementations of bel/wire/pip binding. Arches might want to // replace them with their own, for example to use faster access structures than dict. Arches might also diff --git a/common/route/router2.cc b/common/route/router2.cc index 148c4051..a00c74fd 100644 --- a/common/route/router2.cc +++ b/common/route/router2.cc @@ -94,9 +94,9 @@ struct Router2 // The notional location of the wire, to guarantee thread safety int16_t x = 0, y = 0; // Visit data - PipId pip_fwd, pip_bwd; - bool visited_fwd = false, visited_bwd = false; - float cost_fwd = 0.0, cost_bwd = 0.0; + PipId pip_fwd[2], pip_bwd[2]; + bool visited_fwd[2] = {false, false}, visited_bwd[2] = {false, false}; + float cost_fwd[2] = {0.0, 0.0}, cost_bwd[2] = {0.0, 0.0}; }; Context *ctx; @@ -240,11 +240,12 @@ struct Router2 struct QueuedWire { - explicit QueuedWire(int wire = -1, WireScore score = WireScore{}, int randtag = 0) - : wire(wire), score(score), randtag(randtag) {}; + explicit QueuedWire(int wire = -1, WireScore score = WireScore{}, bool inverted = false, int randtag = 0) + : wire(wire), score(score), inverted(inverted), randtag(randtag) {}; int wire; WireScore score; + bool inverted = false; int randtag = 0; struct Greater @@ -527,12 +528,18 @@ struct Router2 void reset_wires(ThreadContext &t) { for (auto w : t.dirty_wires) { - flat_wires[w].pip_fwd = PipId(); - flat_wires[w].pip_bwd = PipId(); - flat_wires[w].visited_fwd = false; - flat_wires[w].visited_bwd = false; - flat_wires[w].cost_fwd = 0.0; - flat_wires[w].cost_bwd = 0.0; + flat_wires[w].pip_fwd[0] = PipId(); + flat_wires[w].pip_fwd[1] = PipId(); + flat_wires[w].pip_bwd[0] = PipId(); + flat_wires[w].pip_bwd[1] = PipId(); + flat_wires[w].visited_fwd[0] = false; + flat_wires[w].visited_fwd[1] = false; + flat_wires[w].visited_bwd[0] = false; + flat_wires[w].visited_bwd[1] = false; + flat_wires[w].cost_fwd[0] = 0.0; + flat_wires[w].cost_fwd[1] = 0.0; + flat_wires[w].cost_bwd[0] = 0.0; + flat_wires[w].cost_bwd[1] = 0.0; } t.dirty_wires.clear(); } @@ -563,32 +570,32 @@ struct Router2 } // Functions for marking wires as visited, and checking if they have already been visited - void set_visited_fwd(ThreadContext &t, int wire, PipId pip, float cost) + void set_visited_fwd(ThreadContext &t, int wire, PipId pip, float cost, bool inverted) { auto &wd = flat_wires.at(wire); - if (!wd.visited_fwd && !wd.visited_bwd) + if (!wd.visited_fwd[0] && !wd.visited_fwd[1] && !wd.visited_bwd[0] && !wd.visited_bwd[1]) t.dirty_wires.push_back(wire); - wd.pip_fwd = pip; - wd.visited_fwd = true; - wd.cost_fwd = cost; + wd.pip_fwd[inverted] = pip; + wd.visited_fwd[inverted] = true; + wd.cost_fwd[inverted] = cost; } - void set_visited_bwd(ThreadContext &t, int wire, PipId pip, float cost) + void set_visited_bwd(ThreadContext &t, int wire, PipId pip, float cost, bool inverted) { auto &wd = flat_wires.at(wire); - if (!wd.visited_fwd && !wd.visited_bwd) + if (!wd.visited_fwd[0] && !wd.visited_fwd[1] && !wd.visited_bwd[0] && !wd.visited_bwd[1]) t.dirty_wires.push_back(wire); - wd.pip_bwd = pip; - wd.visited_bwd = true; - wd.cost_bwd = cost; + wd.pip_bwd[inverted] = pip; + wd.visited_bwd[inverted] = true; + wd.cost_bwd[inverted] = cost; } - bool was_visited_fwd(int wire, float cost) + bool was_visited_fwd(int wire, float cost, bool inverted) { - return flat_wires.at(wire).visited_fwd && flat_wires.at(wire).cost_fwd <= cost; + return flat_wires.at(wire).visited_fwd[inverted] && flat_wires.at(wire).cost_fwd[inverted] <= cost; } - bool was_visited_bwd(int wire, float cost) + bool was_visited_bwd(int wire, float cost, bool inverted) { - return flat_wires.at(wire).visited_bwd && flat_wires.at(wire).cost_bwd <= cost; + return flat_wires.at(wire).visited_bwd[inverted] && flat_wires.at(wire).cost_bwd[inverted] <= cost; } float get_arc_crit(NetInfo *net, store_index i) @@ -635,12 +642,13 @@ struct Router2 // We have two modes: // 0. starting within a small range of existing routing // 1. expanding from all routing - int mode = 0; + int mode = 1; if (net->users.entries() < 4 || nd.wires.empty() || (crit > 0.95)) mode = 1; // This records the point where forwards and backwards routing met int midpoint_wire = -1; + bool midpoint_inversion = false; int explored = 1; for (; mode < 2; mode++) { @@ -666,9 +674,11 @@ struct Router2 int wire_idx = wire_to_idx.at(wire); base_score.togo_cost = get_togo_cost(net, i, wire_idx, dst_wire, false, crit_weight); t.fwd_queue.push(QueuedWire(wire_idx, base_score)); - set_visited_fwd(t, wire_idx, PipId(), 0.0); + set_visited_fwd(t, wire_idx, PipId(), 0.0, false); }; +#ifndef ARCH_HIMBAECHEL auto &dst_data = flat_wires.at(dst_wire_idx); + // TODO: does this break gatemate because of not knowing inversion state? (yes) // Look for nearby existing routing for (int dy = -cfg.bb_margin_y; dy <= cfg.bb_margin_y; dy++) for (int dx = -cfg.bb_margin_x; dx <= cfg.bb_margin_x; dx++) { @@ -680,6 +690,7 @@ struct Router2 seed_queue_fwd(wire); } } +#endif if (mode == 0 && t.fwd_queue.size() < 4) continue; @@ -688,7 +699,7 @@ struct Router2 // Seed forwards with the source wire, if less than 8 existing wires added seed_queue_fwd(src_wire); } else { - set_visited_fwd(t, src_wire_idx, PipId(), 0.0); + set_visited_fwd(t, src_wire_idx, PipId(), 0.0, false); } } auto seed_queue_bwd = [&](WireId wire) { @@ -698,7 +709,7 @@ struct Router2 int wire_idx = wire_to_idx.at(wire); base_score.togo_cost = get_togo_cost(net, i, wire_idx, src_wire, true, crit_weight); t.bwd_queue.push(QueuedWire(wire_idx, base_score)); - set_visited_bwd(t, wire_idx, PipId(), 0.0); + set_visited_bwd(t, wire_idx, PipId(), 0.0, false); }; // Seed backwards with the dest wire @@ -717,9 +728,10 @@ struct Router2 auto curr = t.fwd_queue.top(); t.fwd_queue.pop(); ++explored; - if (was_visited_bwd(curr.wire, std::numeric_limits::max())) { + if (was_visited_bwd(curr.wire, std::numeric_limits::max(), curr.inverted)) { // Meet in the middle; done midpoint_wire = curr.wire; + midpoint_inversion = curr.inverted; break; } auto &curr_data = flat_wires.at(curr.wire); @@ -736,7 +748,7 @@ struct Router2 next_score.cost = curr.score.cost + score_wire_for_arc(net, i, phys_pin, next, dh, crit_weight); next_score.togo_cost = cfg.estimate_weight * get_togo_cost(net, i, next_idx, dst_wire, false, crit_weight); - if (was_visited_fwd(next_idx, next_score.delay)) { + if (was_visited_fwd(next_idx, next_score.delay, /*inverted=*/curr.inverted ^ ctx->isPipInverting(dh))) { // Don't expand the same node twice. continue; } @@ -752,8 +764,8 @@ struct Router2 continue; if (!thread_test_wire(t, nwd)) continue; // thread safety issue - set_visited_fwd(t, next_idx, dh, next_score.delay); - t.fwd_queue.push(QueuedWire(next_idx, next_score, t.rng.rng())); + set_visited_fwd(t, next_idx, dh, next_score.delay, curr.inverted ^ ctx->isPipInverting(dh)); + t.fwd_queue.push(QueuedWire(next_idx, next_score, curr.inverted ^ ctx->isPipInverting(dh), t.rng.rng())); } } if (!t.bwd_queue.empty()) { @@ -762,10 +774,11 @@ struct Router2 t.bwd_queue.pop(); ++explored; auto &curr_data = flat_wires.at(curr.wire); - if (was_visited_fwd(curr.wire, std::numeric_limits::max()) || - (const_mode && ctx->getWireConstantValue(curr_data.w) == net->constant_value)) { + if (was_visited_fwd(curr.wire, std::numeric_limits::max(), curr.inverted) || + (const_mode && ctx->getWireConstantValue(curr_data.w) == net->constant_value && !curr.inverted)) { // Meet in the middle; done midpoint_wire = curr.wire; + midpoint_inversion = curr.inverted; break; } // Don't allow the same wire to be bound to the same net with a different driving pip @@ -790,7 +803,7 @@ struct Router2 ? 0 : cfg.estimate_weight * get_togo_cost(net, i, next_idx, src_wire, true, crit_weight); - if (was_visited_bwd(next_idx, next_score.delay)) { + if (was_visited_bwd(next_idx, next_score.delay, /*inverted=*/curr.inverted ^ ctx->isPipInverting(uh))) { // Don't expand the same node twice. continue; } @@ -802,8 +815,8 @@ struct Router2 continue; if (!thread_test_wire(t, nwd)) continue; // thread safety issue - set_visited_bwd(t, next_idx, uh, next_score.delay); - t.bwd_queue.push(QueuedWire(next_idx, next_score, t.rng.rng())); + set_visited_bwd(t, next_idx, uh, next_score.delay, curr.inverted ^ ctx->isPipInverting(uh)); + t.bwd_queue.push(QueuedWire(next_idx, next_score, curr.inverted ^ ctx->isPipInverting(uh), t.rng.rng())); } } } @@ -812,27 +825,29 @@ struct Router2 } ArcRouteResult result = ARC_SUCCESS; if (midpoint_wire != -1) { - ROUTE_LOG_DBG(" Routed (explored %d wires): ", explored); + ROUTE_LOG_DBG(" Routed (explored %d wires):\n", explored); if (const_mode) { bind_pip_internal(nd, i, midpoint_wire, PipId()); } else { int cursor_bwd = midpoint_wire; - while (was_visited_fwd(cursor_bwd, std::numeric_limits::max())) { - PipId pip = flat_wires.at(cursor_bwd).pip_fwd; + int inversion_bwd = midpoint_inversion; + while (was_visited_fwd(cursor_bwd, std::numeric_limits::max(), inversion_bwd)) { + PipId pip = flat_wires.at(cursor_bwd).pip_fwd[inversion_bwd]; if (pip == PipId() && cursor_bwd != src_wire_idx) break; bind_pip_internal(nd, i, cursor_bwd, pip); if (ctx->debug && !is_mt) { auto &wd = flat_wires.at(cursor_bwd); - ROUTE_LOG_DBG(" fwd wire: %s (curr %d hist %f share %d)\n", ctx->nameOfWire(wd.w), - wd.curr_cong - 1, wd.hist_cong_cost, nd.wires.at(wd.w).second); + ROUTE_LOG_DBG(" fwd wire: %s (curr %d hist %f share %d inverted %d)\n", ctx->nameOfWire(wd.w), + wd.curr_cong - 1, wd.hist_cong_cost, nd.wires.at(wd.w).second, inversion_bwd); } if (pip == PipId()) { break; } - ROUTE_LOG_DBG(" fwd pip: %s (%d, %d)\n", ctx->nameOfPip(pip), ctx->getPipLocation(pip).x, - ctx->getPipLocation(pip).y); + ROUTE_LOG_DBG(" fwd pip: %s (%d, %d) (inverting %d)\n", ctx->nameOfPip(pip), ctx->getPipLocation(pip).x, + ctx->getPipLocation(pip).y, ctx->isPipInverting(pip)); cursor_bwd = wire_to_idx.at(ctx->getPipSrcWire(pip)); + inversion_bwd ^= ctx->isPipInverting(pip); } while (cursor_bwd != src_wire_idx) { @@ -857,19 +872,21 @@ struct Router2 } int cursor_fwd = midpoint_wire; - while (was_visited_bwd(cursor_fwd, std::numeric_limits::max())) { - PipId pip = flat_wires.at(cursor_fwd).pip_bwd; + int inversion_fwd = midpoint_inversion; + while (was_visited_bwd(cursor_fwd, std::numeric_limits::max(), inversion_fwd)) { + PipId pip = flat_wires.at(cursor_fwd).pip_bwd[inversion_fwd]; if (pip == PipId()) { break; } - ROUTE_LOG_DBG(" bwd pip: %s (%d, %d)\n", ctx->nameOfPip(pip), ctx->getPipLocation(pip).x, - ctx->getPipLocation(pip).y); + ROUTE_LOG_DBG(" bwd pip: %s (%d, %d) (inverting %d)\n", ctx->nameOfPip(pip), ctx->getPipLocation(pip).x, + ctx->getPipLocation(pip).y, ctx->isPipInverting(pip)); cursor_fwd = wire_to_idx.at(ctx->getPipDstWire(pip)); + inversion_fwd ^= ctx->isPipInverting(pip); bind_pip_internal(nd, i, cursor_fwd, pip); if (ctx->debug && !is_mt) { auto &wd = flat_wires.at(cursor_fwd); - ROUTE_LOG_DBG(" bwd wire: %s (curr %d hist %f share %d)\n", ctx->nameOfWire(wd.w), - wd.curr_cong - 1, wd.hist_cong_cost, nd.wires.at(wd.w).second); + ROUTE_LOG_DBG(" bwd wire: %s (curr %d hist %f share %d inverted %d)\n", ctx->nameOfWire(wd.w), + wd.curr_cong - 1, wd.hist_cong_cost, nd.wires.at(wd.w).second, inversion_fwd); } } NPNR_ASSERT(cursor_fwd == dst_wire_idx); diff --git a/himbaechel/arch.h b/himbaechel/arch.h index 29f5cb8d..d4a1f60c 100644 --- a/himbaechel/arch.h +++ b/himbaechel/arch.h @@ -662,6 +662,9 @@ struct Arch : BaseArch uarch->notifyPipChange(pip, nullptr); BaseArch::unbindPip(pip); } + bool isPipInverting(PipId pip) const override { + return uarch->isPipInverting(pip); + } // ------------------------------------------------- diff --git a/himbaechel/himbaechel_api.h b/himbaechel/himbaechel_api.h index 4f6f2101..ff87283b 100644 --- a/himbaechel/himbaechel_api.h +++ b/himbaechel/himbaechel_api.h @@ -130,6 +130,9 @@ struct HimbaechelAPI // For custom placer configuration virtual void configurePlacerHeap(PlacerHeapCfg &cfg) {}; + // HACK + virtual bool isPipInverting(PipId pip) const { return false; } + virtual ~HimbaechelAPI() {}; }; diff --git a/himbaechel/uarch/gatemate/gatemate.h b/himbaechel/uarch/gatemate/gatemate.h index 3895536e..ed4a8c4f 100644 --- a/himbaechel/uarch/gatemate/gatemate.h +++ b/himbaechel/uarch/gatemate/gatemate.h @@ -54,6 +54,11 @@ struct GateMateImpl : HimbaechelAPI IdString getBelBucketForCellType(IdString cell_type) const; bool isValidBelForCellType(IdString cell_type, BelId bel) const; + + bool isPipInverting(PipId pip) const override { + const auto &extra_data = *reinterpret_cast(chip_pip_info(ctx->chip_info, pip).extra_data.get()); + return extra_data.type == PipExtra::PIP_EXTRA_MUX && (extra_data.flags & MUX_INVERT); + } }; NEXTPNR_NAMESPACE_END diff --git a/tests b/tests index d2d5b69d..00c55a9e 160000 --- a/tests +++ b/tests @@ -1 +1 @@ -Subproject commit d2d5b69dd34b1a9a1e4e9466599ed18e9254fee9 +Subproject commit 00c55a9eb9ea2e062b51fe0d64741412b185d95d