diff --git a/common/placer1.cc b/common/placer1.cc index efa8a674..5e9c0f6d 100644 --- a/common/placer1.cc +++ b/common/placer1.cc @@ -414,10 +414,6 @@ class SAPlacer } } } - for (auto &cell : ctx->cells) - if (get_constraints_distance(ctx, cell.second.get()) != 0) - log_error("constraint satisfaction check failed for cell '%s' at Bel '%s'\n", cell.first.c_str(ctx), - ctx->nameOfBel(cell.second->bel)); timing_analysis(ctx); return true; diff --git a/nexus/arch.cc b/nexus/arch.cc index f3430968..3f3d8c83 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -967,6 +967,48 @@ TimingPortClass Arch::lookup_port_type(int type_idx, IdString port, PortType dir return lookup_cell_delay(type_idx, clock, port, dly) ? TMG_REGISTER_OUTPUT : TMG_COMB_OUTPUT; } } +// ----------------------------------------------------------------------- + +bool Arch::getClusterPlacement(ClusterId cluster, BelId root_bel, + std::vector> &placement) const +{ + CellInfo *root_cell = cells.at(cluster).get(); + placement.clear(); + NPNR_ASSERT(root_bel != BelId()); + Loc root_loc = getBelLocation(root_bel); + + if (root_cell->constr_abs_z) { + // Coerce root to absolute z constraint + root_loc.z = root_cell->constr_z; + root_bel = getBelByLocation(root_loc); + if (root_bel == BelId() || !isValidBelForCellType(root_cell->type, root_bel)) + return false; + } + placement.emplace_back(root_cell, root_bel); + + for (auto child : root_cell->constr_children) { + Loc child_loc; + child_loc.x = root_loc.x + child->constr_x; + child_loc.y = root_loc.y + child->constr_y; + child_loc.z = child->constr_abs_z ? child->constr_z : (root_loc.z + child->constr_z); + BelId child_bel = getBelByLocation(child_loc); + if (child_bel == BelId() || !isValidBelForCellType(child->type, child_bel)) { + // Special case for DSPs where the delta is sometimes different + bool fixed = false; + if (child->type == id_REG18_CORE && root_cell->is_9x9_18x18) { + child_loc.x -= 1; + child_loc.z += 2; + child_bel = getBelByLocation(child_loc); + if (child_bel != BelId() && isValidBelForCellType(child->type, child_bel)) + fixed = true; + } + if (!fixed) + return false; + } + placement.emplace_back(child, child_bel); + } + return true; +} // ----------------------------------------------------------------------- diff --git a/nexus/arch.h b/nexus/arch.h index fcb864f6..973d97bf 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -1241,6 +1241,10 @@ struct Arch : BaseArch // ------------------------------------------------- // Arch-specific global routing void route_globals(); + // ------------------------------------------------- + // Override for DSP clusters + bool getClusterPlacement(ClusterId cluster, BelId root_bel, + std::vector> &placement) const override; // ------------------------------------------------- diff --git a/nexus/archdefs.h b/nexus/archdefs.h index ab1010d0..f12206f0 100644 --- a/nexus/archdefs.h +++ b/nexus/archdefs.h @@ -182,6 +182,8 @@ struct ArchCellInfo : BaseClusterInfo int tmg_index = -1; // Map from cell/bel ports to logical timing ports dict tmg_portmap; + // For DSP cluster override + bool is_9x9_18x18 = false; }; NEXTPNR_NAMESPACE_END diff --git a/nexus/pack.cc b/nexus/pack.cc index 6913ef00..281a7cf2 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -1200,7 +1200,8 @@ struct NexusPacker NetInfo *tout = get_net_or_empty(ci, id_TOUT); if (tout != nullptr && tout->users.size() == 1) iob = tout->users.at(0).cell; - if (iob == nullptr || (iob->type != id_SEIO18_CORE && iob->type != id_SEIO33_CORE && iob->type != id_DIFFIO18_CORE)) + if (iob == nullptr || + (iob->type != id_SEIO18_CORE && iob->type != id_SEIO33_CORE && iob->type != id_DIFFIO18_CORE)) log_error("Failed to find associated IOB for IOLOGIC %s\n", ctx->nameOf(ci)); io_to_iol[iob->name].push_back(ci); } @@ -1710,6 +1711,8 @@ struct NexusPacker } for (int i = 0; i < mt.N18x18; i++) mult18[i] = create_dsp_cell(ci->name, id_MULT18_CORE, preadd9[0], (i / 2) * 4 + i % 2, 4); + if (mt.N18x18 <= 2) + preadd9[0]->is_9x9_18x18 = true; for (int i = 0; i < mt.N18x36; i++) mult18x36[i] = create_dsp_cell(ci->name, id_MULT18X36_CORE, preadd9[0], (i * 4) + 2, 4); for (int i = 0; i < Nreg18; i++) { @@ -2078,24 +2081,25 @@ struct NexusPacker // Finds and returns a flip-flop that drives the given port of an IOB cell // If an associated IOLOGIC cell is provided then checks whether the // flip-flop matches its clock and reset. - CellInfo* get_ff_for_iob (CellInfo* iob, IdString port, CellInfo* iol) { + CellInfo *get_ff_for_iob(CellInfo *iob, IdString port, CellInfo *iol) + { // Get the net - NetInfo* net = get_net_or_empty(iob, port); + NetInfo *net = get_net_or_empty(iob, port); if (net == nullptr) { return nullptr; } // Get the flip-flop that drives it - CellInfo* ff = net->driver.cell; + CellInfo *ff = net->driver.cell; if (ff->type != id_OXIDE_FF) { return nullptr; } // Get clock nets of IOLOGIC and the flip-flop if (iol != nullptr) { - NetInfo* iol_c = get_net_or_empty(iol, id_SCLKOUT); - NetInfo* ff_c = get_net_or_empty(ff, id_CLK); + NetInfo *iol_c = get_net_or_empty(iol, id_SCLKOUT); + NetInfo *ff_c = get_net_or_empty(ff, id_CLK); // If one of them is floating or it is not the same net then abort if (iol_c == nullptr || ff_c == nullptr) { @@ -2108,8 +2112,8 @@ struct NexusPacker // Get reset nets of IOLOGIC and the flip-flop if (iol != nullptr) { - NetInfo* iol_r = get_net_or_empty(iol, id_LSROUT); - NetInfo* ff_r = get_net_or_empty(ff, id_LSR); + NetInfo *iol_r = get_net_or_empty(iol, id_LSROUT); + NetInfo *ff_r = get_net_or_empty(ff, id_LSR); // If one of them is floating or it is not the same net then abort. // But both can be floating. @@ -2133,14 +2137,15 @@ struct NexusPacker // IOLOGIC requires some special handling around itself and IOB. This // function does that. - void handle_iologic() { + void handle_iologic() + { log_info("Packing IOLOGIC...\n"); // Map of flip-flop cells that drive IOLOGIC+IOB pairs - dict>> tff_map; + dict>> tff_map; for (auto &cell : ctx->cells) { - CellInfo* iol = cell.second.get(); + CellInfo *iol = cell.second.get(); if (iol->type != id_SIOLOGIC && iol->type != id_IOLOGIC) { continue; } @@ -2199,25 +2204,21 @@ struct NexusPacker // same ned as SEIO33_CORE.I. // // - NetInfo* iob_t = get_net_or_empty(iob, id_T); + NetInfo *iob_t = get_net_or_empty(iob, id_T); if (iob_t != nullptr && isODDR) { - NetInfo* iol_t = get_net_or_empty(iol, id_TOUT); + NetInfo *iol_t = get_net_or_empty(iol, id_TOUT); // SIOLOGIC.TOUT is not driving SEIO33_CORE.T - if ((iol_t == nullptr) || - (iol_t != nullptr && iol_t->users.empty()) || + if ((iol_t == nullptr) || (iol_t != nullptr && iol_t->users.empty()) || (iol_t != nullptr && !iol_t->users.empty() && iol_t->name != iob_t->name)) { // In this case if SIOLOGIC.TSDATA0 is not connected // to the same net as SEIO33_CORE.T and is not // floating then that configuration is illegal. - NetInfo* iol_ti = get_net_or_empty(iol, id_TSDATA0); - if (iol_ti != nullptr && (iol_ti->name != iob_t->name) - && (iol_ti->name != gnd_net->name)) - { + NetInfo *iol_ti = get_net_or_empty(iol, id_TSDATA0); + if (iol_ti != nullptr && (iol_ti->name != iob_t->name) && (iol_ti->name != gnd_net->name)) { log_error("Cannot have %s.TSDATA0 and %s.T driven by different nets (%s vs. %s)\n", - ctx->nameOf(iol), ctx->nameOf(iob), - ctx->nameOf(iol_ti), ctx->nameOf(iob_t)); + ctx->nameOf(iol), ctx->nameOf(iob), ctx->nameOf(iol_ti), ctx->nameOf(iob_t)); } // Re-connect TSDATA (even if it has already been @@ -2241,10 +2242,9 @@ struct NexusPacker // Check if the T input is driven by a flip-flop. Store // in the map for later integration with IOLOGIC. - CellInfo* ff = get_ff_for_iob(iob, id_T, iol); + CellInfo *ff = get_ff_for_iob(iob, id_T, iol); if (ff != nullptr && syn_useioff) { - tff_map[ff->name].push_back(std::make_pair( - iol->name, iob->name)); + tff_map[ff->name].push_back(std::make_pair(iol->name, iob->name)); } } } @@ -2252,18 +2252,18 @@ struct NexusPacker } // Integrate flip-flops that drive T with IOLOGIC - for (auto& it : tff_map) { - CellInfo* ff = ctx->cells.at(it.first).get(); + for (auto &it : tff_map) { + CellInfo *ff = ctx->cells.at(it.first).get(); - NetInfo* ff_d = get_net_or_empty(ff, id_M); // FIXME: id_D or id_M ?! + NetInfo *ff_d = get_net_or_empty(ff, id_M); // FIXME: id_D or id_M ?! NPNR_ASSERT(ff_d != nullptr); - NetInfo* ff_q = get_net_or_empty(ff, id_Q); + NetInfo *ff_q = get_net_or_empty(ff, id_Q); NPNR_ASSERT(ff_q != nullptr); - for (auto& ios : it.second) { - CellInfo* iol = ctx->cells.at(ios.first).get(); - CellInfo* iob = ctx->cells.at(ios.second).get(); + for (auto &ios : it.second) { + CellInfo *iol = ctx->cells.at(ios.first).get(); + CellInfo *iob = ctx->cells.at(ios.second).get(); log_info(" Integrating %s into %s\n", ctx->nameOf(ff), ctx->nameOf(iol)); @@ -2286,7 +2286,7 @@ struct NexusPacker } // Disconnect the flip-flop - for (auto& port : ff->ports) { + for (auto &port : ff->ports) { disconnect_port(ctx, ff, port.first); }