gatemate: diagnose multiplier routing conflicts instead of asserting

find_and_bind_downhill_pip() bound multiplier halo pips unconditionally, so two
multiplier clusters placed close enough that their dedicated (locked) routing
overlaps would trip the opaque bindPip assertion (base_arch.h: w2n_entry == nullptr).

Make the primitive:
 - idempotent when the destination wire is already part of THIS net's route
   (a revisit of a shared start wire is a no-op, not an error);
 - fail with an actionable message naming the wire and both contending nets when
   a DIFFERENT net already holds the wire -- never silently drop the connection.

This does not yet prevent the collision (that needs a placement-spacing rule so
two multiplier clusters' halos cannot overlap); it converts an opaque abort into
a diagnosable error and removes a latent same-net re-bind assertion.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Max Aigner 2026-07-10 12:30:09 +02:00
parent 2b560ad0cc
commit ca97eb6ec2
1 changed files with 16 additions and 0 deletions

View File

@ -39,6 +39,22 @@ void find_and_bind_downhill_pip(Context *ctx, WireId from, WireId to, NetInfo *n
{
NPNR_ASSERT(from != WireId());
NPNR_ASSERT(to != WireId());
// Multiplier routes are hand-bound with STRENGTH_LOCKED. If this exact
// destination wire is already part of THIS net's route, re-binding it would
// trip the bindPip double-bind assertion (base_arch.h) -- it is just a
// revisit of a shared start wire, so treat it as a no-op.
if (ctx->getBoundWireNet(to) == net)
return;
// If the wire is held by a DIFFERENT net, two multiplier clusters were placed
// close enough that their dedicated (locked, hand-picked) routing overlaps.
// Fail with an actionable message instead of the opaque bindPip assertion, and
// never silently drop the connection (that would mis-route the product).
if (ctx->getBoundWireNet(to) != nullptr)
log_error("Multiplier routing conflict on wire %s: needed by net '%s' but already held by "
"net '%s' -- two multiplier clusters are placed too close.\n",
ctx->nameOfWire(to), net->name.c_str(ctx), ctx->getBoundWireNet(to)->name.c_str(ctx));
for (auto pip : ctx->getPipsDownhill(from)) {
if (ctx->getPipDstWire(pip) == to) {
if (ctx->debug)