Constrain routes to have correct inversion state
This commit is contained in:
parent
1fc8809e18
commit
005bffab48
@ -454,6 +454,9 @@ template <typename R> struct BaseArch : ArchAPI<R>
|
|||||||
// Flow methods
|
// Flow methods
|
||||||
virtual void assignArchInfo() override {};
|
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
|
// 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
|
// replace them with their own, for example to use faster access structures than dict. Arches might also
|
||||||
|
@ -94,9 +94,9 @@ struct Router2
|
|||||||
// The notional location of the wire, to guarantee thread safety
|
// The notional location of the wire, to guarantee thread safety
|
||||||
int16_t x = 0, y = 0;
|
int16_t x = 0, y = 0;
|
||||||
// Visit data
|
// Visit data
|
||||||
PipId pip_fwd, pip_bwd;
|
PipId pip_fwd[2], pip_bwd[2];
|
||||||
bool visited_fwd = false, visited_bwd = false;
|
bool visited_fwd[2] = {false, false}, visited_bwd[2] = {false, false};
|
||||||
float cost_fwd = 0.0, cost_bwd = 0.0;
|
float cost_fwd[2] = {0.0, 0.0}, cost_bwd[2] = {0.0, 0.0};
|
||||||
};
|
};
|
||||||
|
|
||||||
Context *ctx;
|
Context *ctx;
|
||||||
@ -240,11 +240,12 @@ struct Router2
|
|||||||
struct QueuedWire
|
struct QueuedWire
|
||||||
{
|
{
|
||||||
|
|
||||||
explicit QueuedWire(int wire = -1, WireScore score = WireScore{}, int randtag = 0)
|
explicit QueuedWire(int wire = -1, WireScore score = WireScore{}, bool inverted = false, int randtag = 0)
|
||||||
: wire(wire), score(score), randtag(randtag) {};
|
: wire(wire), score(score), inverted(inverted), randtag(randtag) {};
|
||||||
|
|
||||||
int wire;
|
int wire;
|
||||||
WireScore score;
|
WireScore score;
|
||||||
|
bool inverted = false;
|
||||||
int randtag = 0;
|
int randtag = 0;
|
||||||
|
|
||||||
struct Greater
|
struct Greater
|
||||||
@ -527,12 +528,18 @@ struct Router2
|
|||||||
void reset_wires(ThreadContext &t)
|
void reset_wires(ThreadContext &t)
|
||||||
{
|
{
|
||||||
for (auto w : t.dirty_wires) {
|
for (auto w : t.dirty_wires) {
|
||||||
flat_wires[w].pip_fwd = PipId();
|
flat_wires[w].pip_fwd[0] = PipId();
|
||||||
flat_wires[w].pip_bwd = PipId();
|
flat_wires[w].pip_fwd[1] = PipId();
|
||||||
flat_wires[w].visited_fwd = false;
|
flat_wires[w].pip_bwd[0] = PipId();
|
||||||
flat_wires[w].visited_bwd = false;
|
flat_wires[w].pip_bwd[1] = PipId();
|
||||||
flat_wires[w].cost_fwd = 0.0;
|
flat_wires[w].visited_fwd[0] = false;
|
||||||
flat_wires[w].cost_bwd = 0.0;
|
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();
|
t.dirty_wires.clear();
|
||||||
}
|
}
|
||||||
@ -563,32 +570,32 @@ struct Router2
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Functions for marking wires as visited, and checking if they have already been visited
|
// 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);
|
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);
|
t.dirty_wires.push_back(wire);
|
||||||
wd.pip_fwd = pip;
|
wd.pip_fwd[inverted] = pip;
|
||||||
wd.visited_fwd = true;
|
wd.visited_fwd[inverted] = true;
|
||||||
wd.cost_fwd = cost;
|
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);
|
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);
|
t.dirty_wires.push_back(wire);
|
||||||
wd.pip_bwd = pip;
|
wd.pip_bwd[inverted] = pip;
|
||||||
wd.visited_bwd = true;
|
wd.visited_bwd[inverted] = true;
|
||||||
wd.cost_bwd = cost;
|
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<PortRef> i)
|
float get_arc_crit(NetInfo *net, store_index<PortRef> i)
|
||||||
@ -635,12 +642,13 @@ struct Router2
|
|||||||
// We have two modes:
|
// We have two modes:
|
||||||
// 0. starting within a small range of existing routing
|
// 0. starting within a small range of existing routing
|
||||||
// 1. expanding from all routing
|
// 1. expanding from all routing
|
||||||
int mode = 0;
|
int mode = 1;
|
||||||
if (net->users.entries() < 4 || nd.wires.empty() || (crit > 0.95))
|
if (net->users.entries() < 4 || nd.wires.empty() || (crit > 0.95))
|
||||||
mode = 1;
|
mode = 1;
|
||||||
|
|
||||||
// This records the point where forwards and backwards routing met
|
// This records the point where forwards and backwards routing met
|
||||||
int midpoint_wire = -1;
|
int midpoint_wire = -1;
|
||||||
|
bool midpoint_inversion = false;
|
||||||
int explored = 1;
|
int explored = 1;
|
||||||
|
|
||||||
for (; mode < 2; mode++) {
|
for (; mode < 2; mode++) {
|
||||||
@ -666,9 +674,11 @@ struct Router2
|
|||||||
int wire_idx = wire_to_idx.at(wire);
|
int wire_idx = wire_to_idx.at(wire);
|
||||||
base_score.togo_cost = get_togo_cost(net, i, wire_idx, dst_wire, false, crit_weight);
|
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));
|
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);
|
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
|
// Look for nearby existing routing
|
||||||
for (int dy = -cfg.bb_margin_y; dy <= cfg.bb_margin_y; dy++)
|
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++) {
|
for (int dx = -cfg.bb_margin_x; dx <= cfg.bb_margin_x; dx++) {
|
||||||
@ -680,6 +690,7 @@ struct Router2
|
|||||||
seed_queue_fwd(wire);
|
seed_queue_fwd(wire);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (mode == 0 && t.fwd_queue.size() < 4)
|
if (mode == 0 && t.fwd_queue.size() < 4)
|
||||||
continue;
|
continue;
|
||||||
@ -688,7 +699,7 @@ struct Router2
|
|||||||
// 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, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto seed_queue_bwd = [&](WireId wire) {
|
auto seed_queue_bwd = [&](WireId wire) {
|
||||||
@ -698,7 +709,7 @@ struct Router2
|
|||||||
int wire_idx = wire_to_idx.at(wire);
|
int wire_idx = wire_to_idx.at(wire);
|
||||||
base_score.togo_cost = get_togo_cost(net, i, wire_idx, src_wire, true, crit_weight);
|
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));
|
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
|
// Seed backwards with the dest wire
|
||||||
@ -717,9 +728,10 @@ struct Router2
|
|||||||
auto curr = t.fwd_queue.top();
|
auto curr = t.fwd_queue.top();
|
||||||
t.fwd_queue.pop();
|
t.fwd_queue.pop();
|
||||||
++explored;
|
++explored;
|
||||||
if (was_visited_bwd(curr.wire, std::numeric_limits<float>::max())) {
|
if (was_visited_bwd(curr.wire, std::numeric_limits<float>::max(), curr.inverted)) {
|
||||||
// Meet in the middle; done
|
// Meet in the middle; done
|
||||||
midpoint_wire = curr.wire;
|
midpoint_wire = curr.wire;
|
||||||
|
midpoint_inversion = curr.inverted;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
auto &curr_data = flat_wires.at(curr.wire);
|
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.cost = curr.score.cost + score_wire_for_arc(net, i, phys_pin, next, dh, crit_weight);
|
||||||
next_score.togo_cost =
|
next_score.togo_cost =
|
||||||
cfg.estimate_weight * get_togo_cost(net, i, next_idx, dst_wire, false, crit_weight);
|
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.
|
// Don't expand the same node twice.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -752,8 +764,8 @@ struct Router2
|
|||||||
continue;
|
continue;
|
||||||
if (!thread_test_wire(t, nwd))
|
if (!thread_test_wire(t, nwd))
|
||||||
continue; // thread safety issue
|
continue; // thread safety issue
|
||||||
set_visited_fwd(t, next_idx, dh, next_score.delay);
|
set_visited_fwd(t, next_idx, dh, next_score.delay, curr.inverted ^ ctx->isPipInverting(dh));
|
||||||
t.fwd_queue.push(QueuedWire(next_idx, next_score, t.rng.rng()));
|
t.fwd_queue.push(QueuedWire(next_idx, next_score, curr.inverted ^ ctx->isPipInverting(dh), t.rng.rng()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!t.bwd_queue.empty()) {
|
if (!t.bwd_queue.empty()) {
|
||||||
@ -762,10 +774,11 @@ struct Router2
|
|||||||
t.bwd_queue.pop();
|
t.bwd_queue.pop();
|
||||||
++explored;
|
++explored;
|
||||||
auto &curr_data = flat_wires.at(curr.wire);
|
auto &curr_data = flat_wires.at(curr.wire);
|
||||||
if (was_visited_fwd(curr.wire, std::numeric_limits<float>::max()) ||
|
if (was_visited_fwd(curr.wire, std::numeric_limits<float>::max(), curr.inverted) ||
|
||||||
(const_mode && ctx->getWireConstantValue(curr_data.w) == net->constant_value)) {
|
(const_mode && ctx->getWireConstantValue(curr_data.w) == net->constant_value && !curr.inverted)) {
|
||||||
// Meet in the middle; done
|
// Meet in the middle; done
|
||||||
midpoint_wire = curr.wire;
|
midpoint_wire = curr.wire;
|
||||||
|
midpoint_inversion = curr.inverted;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// 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
|
||||||
@ -790,7 +803,7 @@ struct Router2
|
|||||||
? 0
|
? 0
|
||||||
: cfg.estimate_weight * get_togo_cost(net, i, next_idx, src_wire,
|
: cfg.estimate_weight * get_togo_cost(net, i, next_idx, src_wire,
|
||||||
true, crit_weight);
|
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.
|
// Don't expand the same node twice.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -802,8 +815,8 @@ struct Router2
|
|||||||
continue;
|
continue;
|
||||||
if (!thread_test_wire(t, nwd))
|
if (!thread_test_wire(t, nwd))
|
||||||
continue; // thread safety issue
|
continue; // thread safety issue
|
||||||
set_visited_bwd(t, next_idx, uh, next_score.delay);
|
set_visited_bwd(t, next_idx, uh, next_score.delay, curr.inverted ^ ctx->isPipInverting(uh));
|
||||||
t.bwd_queue.push(QueuedWire(next_idx, next_score, t.rng.rng()));
|
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;
|
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):\n", explored);
|
||||||
if (const_mode) {
|
if (const_mode) {
|
||||||
bind_pip_internal(nd, i, midpoint_wire, PipId());
|
bind_pip_internal(nd, i, midpoint_wire, PipId());
|
||||||
} else {
|
} else {
|
||||||
int cursor_bwd = midpoint_wire;
|
int cursor_bwd = midpoint_wire;
|
||||||
while (was_visited_fwd(cursor_bwd, std::numeric_limits<float>::max())) {
|
int inversion_bwd = midpoint_inversion;
|
||||||
PipId pip = flat_wires.at(cursor_bwd).pip_fwd;
|
while (was_visited_fwd(cursor_bwd, std::numeric_limits<float>::max(), inversion_bwd)) {
|
||||||
|
PipId pip = flat_wires.at(cursor_bwd).pip_fwd[inversion_bwd];
|
||||||
if (pip == PipId() && cursor_bwd != src_wire_idx)
|
if (pip == PipId() && cursor_bwd != src_wire_idx)
|
||||||
break;
|
break;
|
||||||
bind_pip_internal(nd, i, cursor_bwd, pip);
|
bind_pip_internal(nd, i, cursor_bwd, pip);
|
||||||
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(" fwd wire: %s (curr %d hist %f share %d)\n", ctx->nameOfWire(wd.w),
|
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);
|
wd.curr_cong - 1, wd.hist_cong_cost, nd.wires.at(wd.w).second, inversion_bwd);
|
||||||
}
|
}
|
||||||
if (pip == PipId()) {
|
if (pip == PipId()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ROUTE_LOG_DBG(" fwd pip: %s (%d, %d)\n", ctx->nameOfPip(pip), ctx->getPipLocation(pip).x,
|
ROUTE_LOG_DBG(" fwd pip: %s (%d, %d) (inverting %d)\n", ctx->nameOfPip(pip), ctx->getPipLocation(pip).x,
|
||||||
ctx->getPipLocation(pip).y);
|
ctx->getPipLocation(pip).y, ctx->isPipInverting(pip));
|
||||||
cursor_bwd = wire_to_idx.at(ctx->getPipSrcWire(pip));
|
cursor_bwd = wire_to_idx.at(ctx->getPipSrcWire(pip));
|
||||||
|
inversion_bwd ^= ctx->isPipInverting(pip);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (cursor_bwd != src_wire_idx) {
|
while (cursor_bwd != src_wire_idx) {
|
||||||
@ -857,19 +872,21 @@ struct Router2
|
|||||||
}
|
}
|
||||||
|
|
||||||
int cursor_fwd = midpoint_wire;
|
int cursor_fwd = midpoint_wire;
|
||||||
while (was_visited_bwd(cursor_fwd, std::numeric_limits<float>::max())) {
|
int inversion_fwd = midpoint_inversion;
|
||||||
PipId pip = flat_wires.at(cursor_fwd).pip_bwd;
|
while (was_visited_bwd(cursor_fwd, std::numeric_limits<float>::max(), inversion_fwd)) {
|
||||||
|
PipId pip = flat_wires.at(cursor_fwd).pip_bwd[inversion_fwd];
|
||||||
if (pip == PipId()) {
|
if (pip == PipId()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ROUTE_LOG_DBG(" bwd pip: %s (%d, %d)\n", ctx->nameOfPip(pip), ctx->getPipLocation(pip).x,
|
ROUTE_LOG_DBG(" bwd pip: %s (%d, %d) (inverting %d)\n", ctx->nameOfPip(pip), ctx->getPipLocation(pip).x,
|
||||||
ctx->getPipLocation(pip).y);
|
ctx->getPipLocation(pip).y, ctx->isPipInverting(pip));
|
||||||
cursor_fwd = wire_to_idx.at(ctx->getPipDstWire(pip));
|
cursor_fwd = wire_to_idx.at(ctx->getPipDstWire(pip));
|
||||||
|
inversion_fwd ^= ctx->isPipInverting(pip);
|
||||||
bind_pip_internal(nd, i, cursor_fwd, pip);
|
bind_pip_internal(nd, i, cursor_fwd, pip);
|
||||||
if (ctx->debug && !is_mt) {
|
if (ctx->debug && !is_mt) {
|
||||||
auto &wd = flat_wires.at(cursor_fwd);
|
auto &wd = flat_wires.at(cursor_fwd);
|
||||||
ROUTE_LOG_DBG(" bwd wire: %s (curr %d hist %f share %d)\n", ctx->nameOfWire(wd.w),
|
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);
|
wd.curr_cong - 1, wd.hist_cong_cost, nd.wires.at(wd.w).second, inversion_fwd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NPNR_ASSERT(cursor_fwd == dst_wire_idx);
|
NPNR_ASSERT(cursor_fwd == dst_wire_idx);
|
||||||
|
@ -662,6 +662,9 @@ struct Arch : BaseArch<ArchRanges>
|
|||||||
uarch->notifyPipChange(pip, nullptr);
|
uarch->notifyPipChange(pip, nullptr);
|
||||||
BaseArch::unbindPip(pip);
|
BaseArch::unbindPip(pip);
|
||||||
}
|
}
|
||||||
|
bool isPipInverting(PipId pip) const override {
|
||||||
|
return uarch->isPipInverting(pip);
|
||||||
|
}
|
||||||
|
|
||||||
// -------------------------------------------------
|
// -------------------------------------------------
|
||||||
|
|
||||||
|
@ -130,6 +130,9 @@ struct HimbaechelAPI
|
|||||||
// For custom placer configuration
|
// For custom placer configuration
|
||||||
virtual void configurePlacerHeap(PlacerHeapCfg &cfg) {};
|
virtual void configurePlacerHeap(PlacerHeapCfg &cfg) {};
|
||||||
|
|
||||||
|
// HACK
|
||||||
|
virtual bool isPipInverting(PipId pip) const { return false; }
|
||||||
|
|
||||||
virtual ~HimbaechelAPI() {};
|
virtual ~HimbaechelAPI() {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -54,6 +54,11 @@ struct GateMateImpl : HimbaechelAPI
|
|||||||
|
|
||||||
IdString getBelBucketForCellType(IdString cell_type) const;
|
IdString getBelBucketForCellType(IdString cell_type) const;
|
||||||
bool isValidBelForCellType(IdString cell_type, BelId bel) const;
|
bool isValidBelForCellType(IdString cell_type, BelId bel) const;
|
||||||
|
|
||||||
|
bool isPipInverting(PipId pip) const override {
|
||||||
|
const auto &extra_data = *reinterpret_cast<const GateMatePipExtraDataPOD*>(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
|
NEXTPNR_NAMESPACE_END
|
||||||
|
2
tests
2
tests
@ -1 +1 @@
|
|||||||
Subproject commit d2d5b69dd34b1a9a1e4e9466599ed18e9254fee9
|
Subproject commit 00c55a9eb9ea2e062b51fe0d64741412b185d95d
|
Loading…
Reference in New Issue
Block a user