archapi: Add new API for global constant routing

Signed-off-by: gatecat <gatecat@ds0.me>
This commit is contained in:
gatecat 2023-10-18 15:57:33 +01:00 committed by myrtle
parent e3c44dd20a
commit fe52840054
8 changed files with 424 additions and 79 deletions

View File

@ -83,6 +83,7 @@ template <typename R> struct ArchAPI : BaseCtx
virtual WireId getConflictingWireWire(WireId wire) const = 0; virtual WireId getConflictingWireWire(WireId wire) const = 0;
virtual NetInfo *getConflictingWireNet(WireId wire) const = 0; virtual NetInfo *getConflictingWireNet(WireId wire) const = 0;
virtual DelayQuad getWireDelay(WireId wire) const = 0; virtual DelayQuad getWireDelay(WireId wire) const = 0;
virtual IdString getWireConstantValue(WireId wire) const = 0;
// Pip methods // Pip methods
virtual typename R::AllPipsRangeT getPips() const = 0; virtual typename R::AllPipsRangeT getPips() const = 0;
virtual PipId getPipByName(IdStringList name) const = 0; virtual PipId getPipByName(IdStringList name) const = 0;

View File

@ -237,6 +237,7 @@ template <typename R> struct BaseArch : ArchAPI<R>
} }
virtual WireId getConflictingWireWire(WireId wire) const override { return wire; }; virtual WireId getConflictingWireWire(WireId wire) const override { return wire; };
virtual NetInfo *getConflictingWireNet(WireId wire) const override { return getBoundWireNet(wire); } virtual NetInfo *getConflictingWireNet(WireId wire) const override { return getBoundWireNet(wire); }
virtual IdString getWireConstantValue(WireId wire) const override { return {}; }
// Pip methods // Pip methods
virtual IdString getPipType(PipId pip) const override { return IdString(); } virtual IdString getPipType(PipId pip) const override { return IdString(); }

View File

@ -132,6 +132,10 @@ struct NetInfo : ArchNetInfo
indexed_store<PortRef> users; indexed_store<PortRef> users;
dict<IdString, Property> attrs; dict<IdString, Property> attrs;
// If this is set to a non-empty ID, then the driver is ignored and it will be routed from any wire with a matching
// getWireConstantValue
IdString constant_value;
// wire -> uphill_pip // wire -> uphill_pip
dict<WireId, PipMap> wires; dict<WireId, PipMap> wires;

View File

@ -133,8 +133,10 @@ struct Router1
if (queued_arcs.count(arc)) if (queued_arcs.count(arc))
return; return;
delay_t pri = ctx->estimateDelay(src_wire, dst_wire) * delay_t pri = (arc.net_info->constant_value == IdString())
(100 * tmg.get_criticality(CellPortKey(arc.net_info->users.at(arc.user_idx)))); ? (ctx->estimateDelay(src_wire, dst_wire) *
(100 * tmg.get_criticality(CellPortKey(arc.net_info->users.at(arc.user_idx)))))
: 0;
arc_entry entry; arc_entry entry;
entry.arc = arc; entry.arc = arc;
@ -293,7 +295,7 @@ struct Router1
if (net_info->is_global) if (net_info->is_global)
return true; return true;
#endif #endif
if (net_info->driver.cell == nullptr) if (net_info->driver.cell == nullptr && net_info->constant_value == IdString())
return true; return true;
return false; return false;
@ -380,7 +382,7 @@ struct Router1
auto src_wire = ctx->getNetinfoSourceWire(net_info); auto src_wire = ctx->getNetinfoSourceWire(net_info);
if (src_wire == WireId()) if (src_wire == WireId() && net_info->constant_value == IdString())
log_error("No wire found for port %s on source cell %s.\n", ctx->nameOf(net_info->driver.port), log_error("No wire found for port %s on source cell %s.\n", ctx->nameOf(net_info->driver.port),
ctx->nameOf(net_info->driver.cell)); ctx->nameOf(net_info->driver.cell));
@ -430,7 +432,8 @@ struct Router1
wire_to_arcs[cursor].insert(arc); wire_to_arcs[cursor].insert(arc);
arc_to_wires[arc].insert(cursor); arc_to_wires[arc].insert(cursor);
while (src_wire != cursor) { while (src_wire != cursor && (net_info->constant_value == IdString() ||
ctx->getWireConstantValue(cursor) != net_info->constant_value)) {
auto it = net_info->wires.find(cursor); auto it = net_info->wires.find(cursor);
if (it == net_info->wires.end()) { if (it == net_info->wires.end()) {
arc_queue_insert(arc, src_wire, dst_wire); arc_queue_insert(arc, src_wire, dst_wire);
@ -797,6 +800,298 @@ struct Router1
return true; return true;
} }
bool route_const_arc(const arc_key &arc, bool ripup)
{
NetInfo *net_info = arc.net_info;
auto user_idx = arc.user_idx;
auto dst_wire = ctx->getNetinfoSinkWire(net_info, net_info->users[user_idx], arc.phys_idx);
ripup_flag = false;
if (ctx->debug) {
log("Routing constant arc %d on net %s (%d arcs total):\n", user_idx.idx(), ctx->nameOf(net_info),
int(net_info->users.capacity()));
log(" value ... %s\n", ctx->nameOf(net_info->constant_value));
log(" sink ..... %s\n", ctx->nameOfWire(dst_wire));
}
// unbind wires that are currently used exclusively by this arc
pool<WireId> old_arc_wires;
old_arc_wires.swap(arc_to_wires[arc]);
for (WireId wire : old_arc_wires) {
auto &arc_wires = wire_to_arcs.at(wire);
NPNR_ASSERT(arc_wires.count(arc));
arc_wires.erase(arc);
if (arc_wires.empty()) {
if (ctx->debug)
log(" unbind %s\n", ctx->nameOfWire(wire));
ctx->unbindWire(wire);
}
}
// special case
if (ctx->getWireConstantValue(dst_wire) == net_info->constant_value) {
NetInfo *bound = ctx->getBoundWireNet(dst_wire);
if (bound != nullptr)
NPNR_ASSERT(bound == net_info);
else {
ctx->bindWire(dst_wire, net_info, STRENGTH_WEAK);
}
arc_to_wires[arc].insert(dst_wire);
wire_to_arcs[dst_wire].insert(arc);
return true;
}
// reset wire queue
if (!queue.empty()) {
std::priority_queue<QueuedWire, std::vector<QueuedWire>, QueuedWire::Greater> new_queue;
queue.swap(new_queue);
}
dict<WireId, QueuedWire> visited;
// A* main loop
int visitCnt = 0;
int maxVisitCnt = INT_MAX;
delay_t best_score = -1;
WireId best_src;
{
QueuedWire qw;
qw.wire = dst_wire;
qw.pip = PipId();
qw.delay = ctx->getWireDelay(qw.wire).maxDelay();
qw.penalty = 0;
qw.bonus = 0;
qw.randtag = ctx->rng();
queue.push(qw);
visited[qw.wire] = qw;
}
while (visitCnt++ < maxVisitCnt && !queue.empty()) {
QueuedWire qw = queue.top();
queue.pop();
for (auto pip : ctx->getPipsUphill(qw.wire)) {
delay_t next_delay = qw.delay + ctx->getPipDelay(pip).maxDelay();
delay_t next_penalty = qw.penalty;
delay_t next_bonus = qw.bonus;
delay_t penalty_delta = 0;
WireId next_wire = ctx->getPipSrcWire(pip);
next_delay += ctx->getWireDelay(next_wire).maxDelay();
WireId conflictWireWire = WireId(), conflictPipWire = WireId();
NetInfo *conflictWireNet = nullptr, *conflictPipNet = nullptr;
if (net_info->wires.count(qw.wire)) {
if (net_info->wires.at(qw.wire).pip != pip)
continue;
next_bonus += cfg.reuseBonus;
} else {
if (!ctx->checkWireAvail(next_wire)) {
if (!ripup)
continue;
conflictWireWire = ctx->getConflictingWireWire(next_wire);
if (conflictWireWire == WireId()) {
conflictWireNet = ctx->getConflictingWireNet(next_wire);
if (conflictWireNet == nullptr)
continue;
else {
if (conflictWireNet->wires.count(next_wire) &&
conflictWireNet->wires.at(next_wire).strength > STRENGTH_STRONG)
continue;
}
} else {
NetInfo *conflicting = ctx->getBoundWireNet(conflictWireWire);
if (conflicting != nullptr) {
if (conflicting->wires.count(conflictWireWire) &&
conflicting->wires.at(conflictWireWire).strength > STRENGTH_STRONG)
continue;
}
}
}
if (!ctx->checkPipAvail(pip)) {
if (!ripup)
continue;
conflictPipWire = ctx->getConflictingPipWire(pip);
if (conflictPipWire == WireId()) {
conflictPipNet = ctx->getConflictingPipNet(pip);
if (conflictPipNet == nullptr)
continue;
else {
if (conflictPipNet->wires.count(next_wire) &&
conflictPipNet->wires.at(next_wire).strength > STRENGTH_STRONG)
continue;
}
} else {
NetInfo *conflicting = ctx->getBoundWireNet(conflictPipWire);
if (conflicting != nullptr) {
if (conflicting->wires.count(conflictPipWire) &&
conflicting->wires.at(conflictPipWire).strength > STRENGTH_STRONG)
continue;
}
}
}
if (conflictWireNet != nullptr && conflictPipWire != WireId() &&
conflictWireNet->wires.count(conflictPipWire))
conflictPipWire = WireId();
if (conflictPipNet != nullptr && conflictWireWire != WireId() &&
conflictPipNet->wires.count(conflictWireWire))
conflictWireWire = WireId();
if (conflictWireWire == conflictPipWire)
conflictWireWire = WireId();
if (conflictWireNet == conflictPipNet)
conflictWireNet = nullptr;
if (conflictWireWire != WireId()) {
auto scores_it = wireScores.find(conflictWireWire);
if (scores_it != wireScores.end())
penalty_delta += scores_it->second * cfg.wireRipupPenalty;
penalty_delta += cfg.wireRipupPenalty;
}
if (conflictPipWire != WireId()) {
auto scores_it = wireScores.find(conflictPipWire);
if (scores_it != wireScores.end())
penalty_delta += scores_it->second * cfg.wireRipupPenalty;
penalty_delta += cfg.wireRipupPenalty;
}
if (conflictWireNet != nullptr) {
auto scores_it = netScores.find(conflictWireNet);
if (scores_it != netScores.end())
penalty_delta += scores_it->second * cfg.netRipupPenalty;
penalty_delta += cfg.netRipupPenalty;
penalty_delta += conflictWireNet->wires.size() * cfg.wireRipupPenalty;
}
if (conflictPipNet != nullptr) {
auto scores_it = netScores.find(conflictPipNet);
if (scores_it != netScores.end())
penalty_delta += scores_it->second * cfg.netRipupPenalty;
penalty_delta += cfg.netRipupPenalty;
penalty_delta += conflictPipNet->wires.size() * cfg.wireRipupPenalty;
}
}
next_penalty += penalty_delta;
delay_t next_score = next_delay + next_penalty;
NPNR_ASSERT(next_score >= 0);
if ((best_score >= 0) && (next_score - next_bonus - cfg.estimatePrecision > best_score))
continue;
auto old_visited_it = visited.find(next_wire);
if (old_visited_it != visited.end()) {
continue;
}
QueuedWire next_qw;
next_qw.wire = next_wire;
next_qw.pip = pip;
next_qw.delay = next_delay;
next_qw.penalty = next_penalty;
next_qw.bonus = next_bonus;
next_qw.randtag = ctx->rng();
visited[next_qw.wire] = next_qw;
queue.push(next_qw);
if (ctx->getWireConstantValue(next_wire) == net_info->constant_value) {
maxVisitCnt = std::min(maxVisitCnt, 2 * visitCnt + (next_qw.penalty > 0 ? 100 : 0));
if (best_src == WireId() || next_score < best_score) {
best_src = next_wire;
}
best_score = next_score - next_bonus;
}
}
}
if (ctx->debug)
log(" total number of visited nodes: %d\n", visitCnt);
if (best_src == WireId()) {
if (ctx->debug)
log(" no route found for this arc\n");
return false;
}
if (ctx->debug) {
log(" final route delay: %8.2f\n", ctx->getDelayNS(visited[dst_wire].delay));
log(" final route penalty: %8.2f\n", ctx->getDelayNS(visited[dst_wire].penalty));
log(" final route bonus: %8.2f\n", ctx->getDelayNS(visited[dst_wire].bonus));
}
// bind resulting route (and maybe unroute other nets)
pool<WireId> unassign_wires = arc_to_wires[arc];
WireId cursor = best_src;
if (!net_info->wires.count(cursor)) {
if (!ctx->checkWireAvail(cursor)) {
ripup_wire(cursor);
NPNR_ASSERT(ctx->checkWireAvail(cursor));
}
ctx->bindWire(cursor, net_info, STRENGTH_WEAK);
}
wire_to_arcs[cursor].insert(arc);
arc_to_wires[arc].insert(cursor);
while (1) {
auto pip = visited[cursor].pip;
if (pip == PipId()) {
NPNR_ASSERT(cursor == dst_wire);
break;
}
WireId next = ctx->getPipDstWire(pip);
if (!net_info->wires.count(next) || (net_info->wires.count(next) && net_info->wires.at(next).pip != pip)) {
if (!ctx->checkWireAvail(next)) {
ripup_wire(next);
NPNR_ASSERT(ctx->checkWireAvail(next));
}
if (!ctx->checkPipAvail(pip)) {
ripup_pip(pip);
NPNR_ASSERT(ctx->checkPipAvail(pip));
}
if (ctx->debug)
log(" bind pip %s\n", ctx->nameOfPip(pip));
ctx->bindPip(pip, net_info, STRENGTH_WEAK);
}
wire_to_arcs[next].insert(arc);
arc_to_wires[arc].insert(next);
cursor = next;
}
if (ripup_flag)
arcs_with_ripup++;
else
arcs_without_ripup++;
return true;
}
delay_t find_slack_thresh() delay_t find_slack_thresh()
{ {
// If more than 5% of arcs have negative slack; use the 5% threshold as a ripup criteria // If more than 5% of arcs have negative slack; use the 5% threshold as a ripup criteria
@ -911,15 +1206,26 @@ bool router1(Context *ctx, const Router1Cfg &cfg)
log("-- %d --\n", iter_cnt); log("-- %d --\n", iter_cnt);
arc_key arc = router.arc_queue_pop(); arc_key arc = router.arc_queue_pop();
if (arc.net_info->constant_value != IdString()) {
if (!router.route_arc(arc, true)) { if (!router.route_const_arc(arc, true)) {
log_warning("Failed to find a route for arc %d of net %s.\n", arc.user_idx.idx(), log_warning("Failed to find a route for arc %d of net %s.\n", arc.user_idx.idx(),
ctx->nameOf(arc.net_info)); ctx->nameOf(arc.net_info));
#ifndef NDEBUG #ifndef NDEBUG
router.check(); router.check();
ctx->check(); ctx->check();
#endif #endif
return false; return false;
}
} else {
if (!router.route_arc(arc, true)) {
log_warning("Failed to find a route for arc %d of net %s.\n", arc.user_idx.idx(),
ctx->nameOf(arc.net_info));
#ifndef NDEBUG
router.check();
ctx->check();
#endif
return false;
}
} }
// Timing driven ripup // Timing driven ripup
if (timing_ripup && router.arc_queue.empty() && timing_fail_count < 50) { if (timing_ripup && router.arc_queue.empty() && timing_fail_count < 50) {
@ -1039,17 +1345,19 @@ bool Context::checkRoutedDesign() const
} }
auto src_wire = ctx->getNetinfoSourceWire(net_info); auto src_wire = ctx->getNetinfoSourceWire(net_info);
if (src_wire == WireId()) { if (net_info->constant_value == IdString()) {
log_assert(net_info->driver.cell == nullptr); if (src_wire == WireId()) {
if (ctx->debug) log_assert(net_info->driver.cell == nullptr);
log(" undriven and unrouted\n"); if (ctx->debug)
continue; log(" undriven and unrouted\n");
} continue;
}
if (net_info->wires.count(src_wire) == 0) { if (net_info->wires.count(src_wire) == 0) {
if (ctx->debug) if (ctx->debug)
log(" source (%s) not bound to net\n", ctx->nameOfWire(src_wire)); log(" source (%s) not bound to net\n", ctx->nameOfWire(src_wire));
found_unrouted = true; found_unrouted = true;
}
} }
dict<WireId, store_index<PortRef>> dest_wires; dict<WireId, store_index<PortRef>> dest_wires;
@ -1100,7 +1408,14 @@ bool Context::checkRoutedDesign() const
log(" driver: %s\n", ctx->nameOfWire(src_wire)); log(" driver: %s\n", ctx->nameOfWire(src_wire));
logged_wires.insert(src_wire); logged_wires.insert(src_wire);
} }
setOrderNum(src_wire, 1); if (net_info->constant_value != IdString()) {
for (const auto &wire : net_info->wires) {
if (wire.second.pip == PipId() && ctx->getWireConstantValue(wire.first) == net_info->constant_value)
setOrderNum(wire.first, 1);
}
} else {
setOrderNum(src_wire, 1);
}
pool<WireId> dangling_wires; pool<WireId> dangling_wires;

View File

@ -346,7 +346,8 @@ struct Router2
return; return;
WireId src = nets.at(net->udata).src_wire; WireId src = nets.at(net->udata).src_wire;
WireId cursor = ad.sink_wire; WireId cursor = ad.sink_wire;
while (cursor != src) { while (cursor != src &&
(net->constant_value == IdString() || ctx->getWireConstantValue(cursor) == net->constant_value)) {
PipId pip = nd.wires.at(cursor).first; PipId pip = nd.wires.at(cursor).first;
unbind_pip_internal(nd, user, cursor); unbind_pip_internal(nd, user, cursor);
cursor = ctx->getPipSrcWire(pip); cursor = ctx->getPipSrcWire(pip);
@ -533,18 +534,11 @@ struct Router2
// These nets have very-high-fanout pips and special rules must be followed (only working backwards) to avoid // These nets have very-high-fanout pips and special rules must be followed (only working backwards) to avoid
// crippling perf // crippling perf
bool is_pseudo_const_net(const NetInfo *net) bool is_dedi_const_net(const NetInfo *net) { return net->constant_value != IdString(); }
{
#ifdef ARCH_NEXUS
if (net->driver.cell != nullptr && net->driver.cell->type == id_VCC_DRV)
return true;
#endif
return false;
}
void update_wire_by_loc(ThreadContext &t, NetInfo *net, store_index<PortRef> i, size_t phys_pin, bool is_mt) void update_wire_by_loc(ThreadContext &t, NetInfo *net, store_index<PortRef> i, size_t phys_pin, bool is_mt)
{ {
if (is_pseudo_const_net(net)) if (is_dedi_const_net(net))
return; return;
auto &nd = nets.at(net->udata); auto &nd = nets.at(net->udata);
auto &ad = nd.arcs.at(i.idx()).at(phys_pin); auto &ad = nd.arcs.at(i.idx()).at(phys_pin);
@ -613,16 +607,17 @@ struct Router2
auto &nd = nets[net->udata]; auto &nd = nets[net->udata];
auto &ad = nd.arcs.at(i.idx()).at(phys_pin); auto &ad = nd.arcs.at(i.idx()).at(phys_pin);
auto &usr = net->users.at(i); auto &usr = net->users.at(i);
bool const_mode = is_dedi_const_net(net);
ROUTE_LOG_DBG("Routing arc %d of net '%s' (%d, %d) -> (%d, %d)\n", i.idx(), ctx->nameOf(net), ad.bb.x0, ROUTE_LOG_DBG("Routing arc %d of net '%s' (%d, %d) -> (%d, %d)\n", i.idx(), ctx->nameOf(net), ad.bb.x0,
ad.bb.y0, ad.bb.x1, ad.bb.y1); ad.bb.y0, ad.bb.x1, ad.bb.y1);
WireId src_wire = ctx->getNetinfoSourceWire(net), dst_wire = ctx->getNetinfoSinkWire(net, usr, phys_pin); WireId src_wire = ctx->getNetinfoSourceWire(net), dst_wire = ctx->getNetinfoSinkWire(net, usr, phys_pin);
if (src_wire == WireId()) if (src_wire == WireId() && !const_mode)
ARC_LOG_ERR("No wire found for port %s on source cell %s.\n", ctx->nameOf(net->driver.port), ARC_LOG_ERR("No wire found for port %s on source cell %s.\n", ctx->nameOf(net->driver.port),
ctx->nameOf(net->driver.cell)); ctx->nameOf(net->driver.cell));
if (dst_wire == WireId()) if (dst_wire == WireId())
ARC_LOG_ERR("No wire found for port %s on destination cell %s.\n", ctx->nameOf(usr.port), ARC_LOG_ERR("No wire found for port %s on destination cell %s.\n", ctx->nameOf(usr.port),
ctx->nameOf(usr.cell)); ctx->nameOf(usr.cell));
int src_wire_idx = wire_to_idx.at(src_wire); int src_wire_idx = const_mode ? -1 : wire_to_idx.at(src_wire);
int dst_wire_idx = wire_to_idx.at(dst_wire); int dst_wire_idx = wire_to_idx.at(dst_wire);
// Calculate a timing weight based on criticality // Calculate a timing weight based on criticality
float crit = get_arc_crit(net, i); float crit = get_arc_crit(net, i);
@ -683,12 +678,13 @@ struct Router2
if (mode == 0 && t.fwd_queue.size() < 4) if (mode == 0 && t.fwd_queue.size() < 4)
continue; continue;
if (!const_mode) {
if (mode == 1 && !is_pseudo_const_net(net)) { if (mode == 1) {
// Seed forwards with the source wire, if less than 8 existing wires added // Seed forwards with the source wire, if less than 8 existing wires added
seed_queue_fwd(src_wire); seed_queue_fwd(src_wire);
} else { } else {
set_visited_fwd(t, src_wire_idx, PipId(), 0.0); set_visited_fwd(t, src_wire_idx, PipId(), 0.0);
}
} }
auto seed_queue_bwd = [&](WireId wire) { auto seed_queue_bwd = [&](WireId wire) {
WireScore base_score; WireScore base_score;
@ -711,7 +707,7 @@ struct Router2
: (!t.fwd_queue.empty() || !t.bwd_queue.empty())) && : (!t.fwd_queue.empty() || !t.bwd_queue.empty())) &&
(!is_bb || iter < toexplore)) { (!is_bb || iter < toexplore)) {
++iter; ++iter;
if (!t.fwd_queue.empty()) { if (!t.fwd_queue.empty() && !const_mode) {
// Explore forwards // Explore forwards
auto curr = t.fwd_queue.top(); auto curr = t.fwd_queue.top();
t.fwd_queue.pop(); t.fwd_queue.pop();
@ -760,12 +756,13 @@ struct Router2
auto curr = t.bwd_queue.top(); auto curr = t.bwd_queue.top();
t.bwd_queue.pop(); t.bwd_queue.pop();
++explored; ++explored;
if (was_visited_fwd(curr.wire, std::numeric_limits<float>::max())) { auto &curr_data = flat_wires.at(curr.wire);
if (was_visited_fwd(curr.wire, std::numeric_limits<float>::max()) ||
(const_mode && ctx->getWireConstantValue(curr_data.w) == net->constant_value)) {
// Meet in the middle; done // Meet in the middle; done
midpoint_wire = curr.wire; midpoint_wire = curr.wire;
break; break;
} }
auto &curr_data = flat_wires.at(curr.wire);
// Don't allow the same wire to be bound to the same net with a different driving pip // Don't allow the same wire to be bound to the same net with a different driving pip
PipId bound_pip; PipId bound_pip;
auto fnd_wire = nd.wires.find(curr_data.w); auto fnd_wire = nd.wires.find(curr_data.w);
@ -809,44 +806,48 @@ struct Router2
ArcRouteResult result = ARC_SUCCESS; ArcRouteResult result = ARC_SUCCESS;
if (midpoint_wire != -1) { if (midpoint_wire != -1) {
ROUTE_LOG_DBG(" Routed (explored %d wires): ", explored); ROUTE_LOG_DBG(" Routed (explored %d wires): ", explored);
int cursor_bwd = midpoint_wire; if (const_mode) {
while (was_visited_fwd(cursor_bwd, std::numeric_limits<float>::max())) { bind_pip_internal(nd, i, midpoint_wire, PipId());
PipId pip = flat_wires.at(cursor_bwd).pip_fwd; } else {
if (pip == PipId() && cursor_bwd != src_wire_idx) int cursor_bwd = midpoint_wire;
break; while (was_visited_fwd(cursor_bwd, std::numeric_limits<float>::max())) {
bind_pip_internal(nd, i, cursor_bwd, pip); PipId pip = flat_wires.at(cursor_bwd).pip_fwd;
if (ctx->debug && !is_mt) { if (pip == PipId() && cursor_bwd != src_wire_idx)
auto &wd = flat_wires.at(cursor_bwd); break;
ROUTE_LOG_DBG(" fwd wire: %s (curr %d hist %f share %d)\n", ctx->nameOfWire(wd.w), bind_pip_internal(nd, i, cursor_bwd, pip);
wd.curr_cong - 1, wd.hist_cong_cost, nd.wires.at(wd.w).second); 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);
}
if (pip == PipId()) {
break;
}
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));
} }
if (pip == PipId()) {
break;
}
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));
}
while (cursor_bwd != src_wire_idx) { while (cursor_bwd != src_wire_idx) {
// Tack onto existing routing // Tack onto existing routing
WireId bwd_w = flat_wires.at(cursor_bwd).w; WireId bwd_w = flat_wires.at(cursor_bwd).w;
if (!nd.wires.count(bwd_w)) if (!nd.wires.count(bwd_w))
break; break;
auto &bound = nd.wires.at(bwd_w); auto &bound = nd.wires.at(bwd_w);
PipId pip = bound.first; PipId pip = bound.first;
if (ctx->debug && !is_mt) { if (ctx->debug && !is_mt) {
auto &wd = flat_wires.at(cursor_bwd); auto &wd = flat_wires.at(cursor_bwd);
ROUTE_LOG_DBG(" ext wire: %s (curr %d hist %f share %d)\n", ctx->nameOfWire(wd.w), ROUTE_LOG_DBG(" ext wire: %s (curr %d hist %f share %d)\n", ctx->nameOfWire(wd.w),
wd.curr_cong - 1, wd.hist_cong_cost, bound.second); wd.curr_cong - 1, wd.hist_cong_cost, bound.second);
}
bind_pip_internal(nd, i, cursor_bwd, pip);
if (pip == PipId())
break;
cursor_bwd = wire_to_idx.at(ctx->getPipSrcWire(pip));
} }
bind_pip_internal(nd, i, cursor_bwd, pip);
if (pip == PipId())
break;
cursor_bwd = wire_to_idx.at(ctx->getPipSrcWire(pip));
}
NPNR_ASSERT(cursor_bwd == src_wire_idx); NPNR_ASSERT(cursor_bwd == src_wire_idx);
}
int cursor_fwd = midpoint_wire; int cursor_fwd = midpoint_wire;
while (was_visited_bwd(cursor_fwd, std::numeric_limits<float>::max())) { while (was_visited_bwd(cursor_fwd, std::numeric_limits<float>::max())) {
@ -1029,7 +1030,7 @@ struct Router2
auto &usr = net->users.at(usr_idx); auto &usr = net->users.at(usr_idx);
WireId src = ctx->getNetinfoSourceWire(net); WireId src = ctx->getNetinfoSourceWire(net);
// Skip routes with no source // Skip routes with no source
if (src == WireId()) if (src == WireId() && net->constant_value == IdString())
return true; return true;
WireId dst = ctx->getNetinfoSinkWire(net, usr, phys_pin); WireId dst = ctx->getNetinfoSinkWire(net, usr, phys_pin);
if (dst == WireId()) if (dst == WireId())
@ -1086,6 +1087,10 @@ struct Router2
break; break;
} }
cursor = ctx->getPipSrcWire(p); cursor = ctx->getPipSrcWire(p);
if (net->constant_value != IdString() && ctx->getWireConstantValue(cursor) == net->constant_value) {
src = cursor;
break;
}
} }
if (success) { if (success) {
@ -1343,6 +1348,8 @@ struct Router2
delay_t get_route_delay(int net, store_index<PortRef> usr_idx, int phys_idx) delay_t get_route_delay(int net, store_index<PortRef> usr_idx, int phys_idx)
{ {
auto &nd = nets.at(net); auto &nd = nets.at(net);
if (nets_by_udata.at(net)->constant_value != IdString())
return 0;
auto &ad = nd.arcs.at(usr_idx.idx()).at(phys_idx); auto &ad = nd.arcs.at(usr_idx.idx()).at(phys_idx);
WireId cursor = ad.sink_wire; WireId cursor = ad.sink_wire;
if (cursor == WireId() || nd.src_wire == WireId()) if (cursor == WireId() || nd.src_wire == WireId())

View File

@ -340,6 +340,14 @@ chip. There may be significant performance impacts if routing regularly
exceeds these bounds by more than a small margin; so an over-estimate exceeds these bounds by more than a small margin; so an over-estimate
of the bounds is almost always better than an under-estimate. of the bounds is almost always better than an under-estimate.
### IdString getWireConstantValue() const
If not an empty string, indicate this wire can be used to source nets
with their `constant_value` equal to its return value.
*BaseArch default: returns `IdString()`*
Pip Methods Pip Methods
----------- -----------

View File

@ -1170,6 +1170,13 @@ struct Arch : BaseArch<ArchRanges>
virtual bool checkWireAvail(WireId wire) const override { return getBoundWireNet(wire) == nullptr; } virtual bool checkWireAvail(WireId wire) const override { return getBoundWireNet(wire) == nullptr; }
NetInfo *getBoundWireNet(WireId wire) const override { return tileStatus.at(wire.tile).boundwires.at(wire.index); } NetInfo *getBoundWireNet(WireId wire) const override { return tileStatus.at(wire.tile).boundwires.at(wire.index); }
IdString getWireConstantValue(WireId wire) const override
{
if (chip_wire_data(db, chip_info, wire).name == ID_LOCAL_VCC)
return id_VCC_DRV;
else
return {};
}
// ------------------------------------------------- // -------------------------------------------------
PipId getPipByName(IdStringList name) const override; PipId getPipByName(IdStringList name) const override;

View File

@ -325,6 +325,8 @@ struct NexusPacker
CellInfo *new_cell = ctx->createCell(ctx->idf("$CONST_%s_DRV_", type.c_str(ctx)), type); CellInfo *new_cell = ctx->createCell(ctx->idf("$CONST_%s_DRV_", type.c_str(ctx)), type);
new_cell->addOutput(id_Z); new_cell->addOutput(id_Z);
new_cell->connectPort(id_Z, new_net); new_cell->connectPort(id_Z, new_net);
if (type == id_VCC_DRV)
new_net->constant_value = id_VCC_DRV;
return new_net; return new_net;
} }