router2: Make magic numbers configurable
Signed-off-by: David Shah <dave@ds0.me>
This commit is contained in:
parent
5e1aac67db
commit
ad1cc12df1
@ -26,6 +26,7 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "router2.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <boost/container/flat_map.hpp>
|
#include <boost/container/flat_map.hpp>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
@ -39,8 +40,6 @@
|
|||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
#define RUNTIME_PROFILE
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
struct Router2
|
struct Router2
|
||||||
{
|
{
|
||||||
@ -105,6 +104,9 @@ struct Router2
|
|||||||
}
|
}
|
||||||
|
|
||||||
Context *ctx;
|
Context *ctx;
|
||||||
|
Router2Cfg cfg;
|
||||||
|
|
||||||
|
Router2(Context *ctx, const Router2Cfg &cfg) : ctx(ctx), cfg(cfg) {}
|
||||||
|
|
||||||
// Use 'udata' for fast net lookups and indexing
|
// Use 'udata' for fast net lookups and indexing
|
||||||
std::vector<NetInfo *> nets_by_udata;
|
std::vector<NetInfo *> nets_by_udata;
|
||||||
@ -221,11 +223,10 @@ struct Router2
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
int bb_margin_x = 4, bb_margin_y = 4; // number of units outside the bounding box we may go
|
|
||||||
bool hit_test_pip(ArcBounds &bb, Loc l)
|
bool hit_test_pip(ArcBounds &bb, Loc l)
|
||||||
{
|
{
|
||||||
return l.x >= (bb.x0 - bb_margin_x) && l.x <= (bb.x1 + bb_margin_x) && l.y >= (bb.y0 - bb_margin_y) &&
|
return l.x >= (bb.x0 - cfg.bb_margin_x) && l.x <= (bb.x1 + cfg.bb_margin_x) &&
|
||||||
l.y <= (bb.y1 + bb_margin_y);
|
l.y >= (bb.y0 - cfg.bb_margin_y) && l.y <= (bb.y1 + cfg.bb_margin_y);
|
||||||
}
|
}
|
||||||
|
|
||||||
double curr_cong_weight, hist_cong_weight, estimate_weight;
|
double curr_cong_weight, hist_cong_weight, estimate_weight;
|
||||||
@ -320,7 +321,7 @@ struct Router2
|
|||||||
source_uses = wd.bound_nets.at(net->udata).first;
|
source_uses = wd.bound_nets.at(net->udata).first;
|
||||||
if (pip != PipId()) {
|
if (pip != PipId()) {
|
||||||
Loc pl = ctx->getPipLocation(pip);
|
Loc pl = ctx->getPipLocation(pip);
|
||||||
bias_cost = 0.25f * (base_cost / int(net->users.size())) *
|
bias_cost = cfg.bias_cost_factor * (base_cost / int(net->users.size())) *
|
||||||
((std::abs(pl.x - nd.cx) + std::abs(pl.y - nd.cy)) / float(nd.hpwl));
|
((std::abs(pl.x - nd.cx) + std::abs(pl.y - nd.cy)) / float(nd.hpwl));
|
||||||
}
|
}
|
||||||
return base_cost * hist_cost * present_cost / (1 + source_uses) + bias_cost;
|
return base_cost * hist_cost * present_cost / (1 + source_uses) + bias_cost;
|
||||||
@ -333,7 +334,7 @@ struct Router2
|
|||||||
if (wd.bound_nets.count(net->udata))
|
if (wd.bound_nets.count(net->udata))
|
||||||
source_uses = wd.bound_nets.at(net->udata).first;
|
source_uses = wd.bound_nets.at(net->udata).first;
|
||||||
// FIXME: timing/wirelength balance?
|
// FIXME: timing/wirelength balance?
|
||||||
return ctx->getDelayNS(ctx->estimateDelay(wd.w, sink)) / (1 + source_uses);
|
return (ctx->getDelayNS(ctx->estimateDelay(wd.w, sink)) / (1 + source_uses)) + cfg.ipin_cost_adder;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool check_arc_routing(NetInfo *net, size_t usr)
|
bool check_arc_routing(NetInfo *net, size_t usr)
|
||||||
@ -469,7 +470,8 @@ struct Router2
|
|||||||
// This could also be used to speed up forwards routing by a hybrid
|
// This could also be used to speed up forwards routing by a hybrid
|
||||||
// bidirectional approach
|
// bidirectional approach
|
||||||
int backwards_iter = 0;
|
int backwards_iter = 0;
|
||||||
int backwards_limit = ctx->getBelGlobalBuf(net->driver.cell->bel) ? 20000 : 15;
|
int backwards_limit =
|
||||||
|
ctx->getBelGlobalBuf(net->driver.cell->bel) ? cfg.global_backwards_max_iter : cfg.backwards_max_iter;
|
||||||
t.backwards_queue.push(wire_to_idx.at(dst_wire));
|
t.backwards_queue.push(wire_to_idx.at(dst_wire));
|
||||||
while (!t.backwards_queue.empty() && backwards_iter < backwards_limit) {
|
while (!t.backwards_queue.empty() && backwards_iter < backwards_limit) {
|
||||||
int cursor = t.backwards_queue.front();
|
int cursor = t.backwards_queue.front();
|
||||||
@ -611,7 +613,7 @@ struct Router2
|
|||||||
next_score.cost = curr.score.cost + score_wire_for_arc(net, i, next, dh);
|
next_score.cost = curr.score.cost + score_wire_for_arc(net, i, next, dh);
|
||||||
next_score.delay =
|
next_score.delay =
|
||||||
curr.score.delay + ctx->getPipDelay(dh).maxDelay() + ctx->getWireDelay(next).maxDelay();
|
curr.score.delay + ctx->getPipDelay(dh).maxDelay() + ctx->getWireDelay(next).maxDelay();
|
||||||
next_score.togo_cost = 1.75 * get_togo_cost(net, i, next_idx, dst_wire);
|
next_score.togo_cost = cfg.estimate_weight * get_togo_cost(net, i, next_idx, dst_wire);
|
||||||
const auto &v = nwd.visit;
|
const auto &v = nwd.visit;
|
||||||
if (!v.visited || (v.score.total() > next_score.total())) {
|
if (!v.visited || (v.score.total() > next_score.total())) {
|
||||||
++explored;
|
++explored;
|
||||||
@ -669,9 +671,7 @@ struct Router2
|
|||||||
|
|
||||||
ROUTE_LOG_DBG("Routing net '%s'...\n", ctx->nameOf(net));
|
ROUTE_LOG_DBG("Routing net '%s'...\n", ctx->nameOf(net));
|
||||||
|
|
||||||
#ifdef RUNTIME_PROFILE
|
|
||||||
auto rstart = std::chrono::high_resolution_clock::now();
|
auto rstart = std::chrono::high_resolution_clock::now();
|
||||||
#endif
|
|
||||||
|
|
||||||
// Nothing to do if net is undriven
|
// Nothing to do if net is undriven
|
||||||
if (net->driver.cell == nullptr)
|
if (net->driver.cell == nullptr)
|
||||||
@ -715,11 +715,11 @@ struct Router2
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifdef RUNTIME_PROFILE
|
if (cfg.perf_profile) {
|
||||||
auto rend = std::chrono::high_resolution_clock::now();
|
auto rend = std::chrono::high_resolution_clock::now();
|
||||||
nets.at(net->udata).total_route_us +=
|
nets.at(net->udata).total_route_us +=
|
||||||
(std::chrono::duration_cast<std::chrono::microseconds>(rend - rstart).count());
|
(std::chrono::duration_cast<std::chrono::microseconds>(rend - rstart).count());
|
||||||
#endif
|
}
|
||||||
return !have_failures;
|
return !have_failures;
|
||||||
}
|
}
|
||||||
#undef ROUTE_LOG_DBG
|
#undef ROUTE_LOG_DBG
|
||||||
@ -951,10 +951,10 @@ struct Router2
|
|||||||
auto &nd = nets.at(n);
|
auto &nd = nets.at(n);
|
||||||
auto ni = nets_by_udata.at(n);
|
auto ni = nets_by_udata.at(n);
|
||||||
int bin = N;
|
int bin = N;
|
||||||
int le_x = mid_x - bb_margin_x;
|
int le_x = mid_x - cfg.bb_margin_x;
|
||||||
int rs_x = mid_x + bb_margin_x;
|
int rs_x = mid_x + cfg.bb_margin_x;
|
||||||
int le_y = mid_y - bb_margin_y;
|
int le_y = mid_y - cfg.bb_margin_y;
|
||||||
int rs_y = mid_y + bb_margin_y;
|
int rs_y = mid_y + cfg.bb_margin_y;
|
||||||
// Quadrants
|
// Quadrants
|
||||||
if (nd.bb.x0 < le_x && nd.bb.x1 < le_x && nd.bb.y0 < le_y && nd.bb.y1 < le_y)
|
if (nd.bb.x0 < le_x && nd.bb.x1 < le_x && nd.bb.y0 < le_y && nd.bb.y1 < le_y)
|
||||||
bin = 0;
|
bin = 0;
|
||||||
@ -1010,14 +1010,14 @@ struct Router2
|
|||||||
route_net(tcs.at(N), fail, false);
|
route_net(tcs.at(N), fail, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void router_test()
|
void operator()()
|
||||||
{
|
{
|
||||||
setup_nets();
|
setup_nets();
|
||||||
setup_wires();
|
setup_wires();
|
||||||
find_all_reserved_wires();
|
find_all_reserved_wires();
|
||||||
partition_nets();
|
partition_nets();
|
||||||
curr_cong_weight = 0.5;
|
curr_cong_weight = cfg.init_curr_cong_weight;
|
||||||
hist_cong_weight = 1.0;
|
hist_cong_weight = cfg.hist_cong_weight;
|
||||||
ThreadContext st;
|
ThreadContext st;
|
||||||
int iter = 1;
|
int iter = 1;
|
||||||
|
|
||||||
@ -1051,32 +1051,48 @@ struct Router2
|
|||||||
log_info("iter=%d wires=%d overused=%d overuse=%d archfail=%s\n", iter, total_wire_use, overused_wires,
|
log_info("iter=%d wires=%d overused=%d overuse=%d archfail=%s\n", iter, total_wire_use, overused_wires,
|
||||||
total_overuse, overused_wires > 0 ? "NA" : std::to_string(arch_fail).c_str());
|
total_overuse, overused_wires > 0 ? "NA" : std::to_string(arch_fail).c_str());
|
||||||
++iter;
|
++iter;
|
||||||
curr_cong_weight *= 2;
|
curr_cong_weight *= cfg.curr_cong_mult;
|
||||||
} while (!failed_nets.empty());
|
} while (!failed_nets.empty());
|
||||||
#ifdef RUNTIME_PROFILE
|
if (cfg.perf_profile) {
|
||||||
std::vector<std::pair<int, IdString>> nets_by_runtime;
|
std::vector<std::pair<int, IdString>> nets_by_runtime;
|
||||||
for (auto &n : nets_by_udata) {
|
for (auto &n : nets_by_udata) {
|
||||||
nets_by_runtime.emplace_back(nets.at(n->udata).total_route_us, n->name);
|
nets_by_runtime.emplace_back(nets.at(n->udata).total_route_us, n->name);
|
||||||
|
}
|
||||||
|
std::sort(nets_by_runtime.begin(), nets_by_runtime.end(), std::greater<std::pair<int, IdString>>());
|
||||||
|
log_info("1000 slowest nets by runtime:\n");
|
||||||
|
for (int i = 0; i < std::min(int(nets_by_runtime.size()), 1000); i++) {
|
||||||
|
log(" %80s %6d %.1fms\n", nets_by_runtime.at(i).second.c_str(ctx),
|
||||||
|
int(ctx->nets.at(nets_by_runtime.at(i).second)->users.size()),
|
||||||
|
nets_by_runtime.at(i).first / 1000.0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
std::sort(nets_by_runtime.begin(), nets_by_runtime.end(), std::greater<std::pair<int, IdString>>());
|
|
||||||
log_info("1000 slowest nets by runtime:\n");
|
|
||||||
for (int i = 0; i < std::min(int(nets_by_runtime.size()), 1000); i++) {
|
|
||||||
log(" %80s %6d %.1fms\n", nets_by_runtime.at(i).second.c_str(ctx),
|
|
||||||
int(ctx->nets.at(nets_by_runtime.at(i).second)->users.size()), nets_by_runtime.at(i).first / 1000.0);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void router2(Context *ctx)
|
void router2(Context *ctx, const Router2Cfg &cfg)
|
||||||
{
|
{
|
||||||
Router2 rt;
|
Router2 rt(ctx, cfg);
|
||||||
rt.ctx = ctx;
|
rt.ctx = ctx;
|
||||||
auto rstart = std::chrono::high_resolution_clock::now();
|
auto rstart = std::chrono::high_resolution_clock::now();
|
||||||
rt.router_test();
|
rt();
|
||||||
auto rend = std::chrono::high_resolution_clock::now();
|
auto rend = std::chrono::high_resolution_clock::now();
|
||||||
log_info("Router2 time %.02fs\n", std::chrono::duration<float>(rend - rstart).count());
|
log_info("Router2 time %.02fs\n", std::chrono::duration<float>(rend - rstart).count());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Router2Cfg::Router2Cfg(Context *ctx)
|
||||||
|
{
|
||||||
|
backwards_max_iter = ctx->setting<int>("router2/bwdMaxIter", 20);
|
||||||
|
global_backwards_max_iter = ctx->setting<int>("router2/glbBwdMaxIter", 200);
|
||||||
|
bb_margin_x = ctx->setting<int>("router2/bbMargin/x", 3);
|
||||||
|
bb_margin_y = ctx->setting<int>("router2/bbMargin/y", 3);
|
||||||
|
ipin_cost_adder = ctx->setting<float>("router2/ipinCostAdder", 0.0f);
|
||||||
|
bias_cost_factor = ctx->setting<float>("router2/biasCostFactor", 0.25f);
|
||||||
|
init_curr_cong_weight = ctx->setting<float>("router2/initCurrCongWeight", 0.5f);
|
||||||
|
hist_cong_weight = ctx->setting<float>("router2/histCongWeight", 1.0f);
|
||||||
|
curr_cong_mult = ctx->setting<float>("router2/currCongWeightMult", 2.0f);
|
||||||
|
estimate_weight = ctx->setting<float>("router2/estimateWeight", 1.75f);
|
||||||
|
perf_profile = ctx->setting<float>("router2/perfProfile", false);
|
||||||
|
}
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
@ -21,6 +21,36 @@
|
|||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
void router2(Context *ctx);
|
struct Router2Cfg
|
||||||
|
{
|
||||||
|
Router2Cfg(Context *ctx);
|
||||||
|
|
||||||
|
// Maximum iterations for backwards routing attempt
|
||||||
|
int backwards_max_iter;
|
||||||
|
// Maximum iterations for backwards routing attempt for global nets
|
||||||
|
int global_backwards_max_iter;
|
||||||
|
// Padding added to bounding boxes to account for imperfect routing,
|
||||||
|
// congestion, etc
|
||||||
|
int bb_margin_x, bb_margin_y;
|
||||||
|
// Cost factor added to input pin wires; effectively reduces the
|
||||||
|
// benefit of sharing interconnect
|
||||||
|
float ipin_cost_adder;
|
||||||
|
// Cost factor for "bias" towards center location of net
|
||||||
|
float bias_cost_factor;
|
||||||
|
// Starting current and historical congestion cost factor
|
||||||
|
float init_curr_cong_weight, hist_cong_weight;
|
||||||
|
// Current congestion cost multiplier
|
||||||
|
float curr_cong_mult;
|
||||||
|
|
||||||
|
// Weight given to delay estimate in A*. Higher values
|
||||||
|
// mean faster and more directed routing, at the risk
|
||||||
|
// of choosing a less congestion/delay-optimal route
|
||||||
|
float estimate_weight;
|
||||||
|
|
||||||
|
// Print additional performance profiling information
|
||||||
|
bool perf_profile = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
void router2(Context *ctx, const Router2Cfg &cfg);
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
@ -625,7 +625,7 @@ bool Arch::route()
|
|||||||
route_ecp5_globals(getCtx());
|
route_ecp5_globals(getCtx());
|
||||||
assignArchInfo();
|
assignArchInfo();
|
||||||
assign_budget(getCtx(), true);
|
assign_budget(getCtx(), true);
|
||||||
router2(getCtx());
|
router2(getCtx(), Router2Cfg(getCtx()));
|
||||||
bool result = router1(getCtx(), Router1Cfg(getCtx()));
|
bool result = router1(getCtx(), Router1Cfg(getCtx()));
|
||||||
#if 0
|
#if 0
|
||||||
std::vector<std::pair<WireId, int>> fanout_vector;
|
std::vector<std::pair<WireId, int>> fanout_vector;
|
||||||
|
Loading…
Reference in New Issue
Block a user