diff --git a/himbaechel/uarch/xilinx/pack.cc b/himbaechel/uarch/xilinx/pack.cc index df802fd8..2b54bbcd 100644 --- a/himbaechel/uarch/xilinx/pack.cc +++ b/himbaechel/uarch/xilinx/pack.cc @@ -808,6 +808,42 @@ void XC7Packer::pack_bram() } } } + + // Cluster cascaded RAMB36 pairs: CASCADEOUT->CASCADEIN are dedicated + // wires that only reach the vertically adjacent RAMB36 site, so the + // pair must be placed together (cf. carry chains). BRAM tiles are 5 + // grid rows apart within a column. + int cascade_pairs = 0; + for (auto &cell : ctx->cells) { + CellInfo *lower = cell.second.get(); + if (lower->type != id_RAMB36E1_RAMB36E1) + continue; + for (auto pn : {ctx->id("CASCADEOUTA"), ctx->id("CASCADEOUTB")}) { + NetInfo *cn = lower->getPort(pn); + if (cn == nullptr) + continue; + for (auto &usr : cn->users) { + CellInfo *upper = usr.cell; + if (upper == lower || upper->type != id_RAMB36E1_RAMB36E1) + continue; + if (upper->cluster != ClusterId()) + continue; // already clustered (e.g. via the A cascade) + if (lower->cluster == ClusterId()) + lower->cluster = lower->name; + else if (lower->cluster != lower->name) + continue; // lower is a child of another cluster; unsupported + upper->cluster = lower->name; + lower->constr_children.push_back(upper); + upper->constr_x = 0; + upper->constr_y = 5; + upper->constr_z = 0; + upper->constr_abs_z = false; + ++cascade_pairs; + } + } + } + if (cascade_pairs > 0) + log_info(" Clustered %d cascaded BRAM pairs\n", cascade_pairs); } void XilinxPacker::pack_inverters() diff --git a/himbaechel/uarch/xilinx/xilinx.cc b/himbaechel/uarch/xilinx/xilinx.cc index 6e9fdb77..0f8f4772 100644 --- a/himbaechel/uarch/xilinx/xilinx.cc +++ b/himbaechel/uarch/xilinx/xilinx.cc @@ -101,6 +101,49 @@ void XilinxImpl::init(Context *ctx) auto extra_data = tile_extra_data(i); tile_status.at(i).site_variant.resize(extra_data->sites.ssize()); } + + // Build the next-RAMB36-up-the-column map for cascade placement + std::map> bram36_by_col; // x -> y -> bel + for (BelId bel : ctx->getBels()) { + if (ctx->getBelType(bel) != id_RAMB36E1_RAMB36E1) + continue; + Loc l = ctx->getBelLocation(bel); + bram36_by_col[l.x][l.y] = bel; + } + for (auto &col : bram36_by_col) { + BelId prev; + int prev_y = 0; + for (auto &entry : col.second) { + if (prev != BelId() && (entry.first - prev_y) <= 6) + next_bram36_up[entry.second] = prev; // grid Y increases downwards: 'up' = smaller y + prev = entry.second; + prev_y = entry.first; + } + } +} + +bool XilinxImpl::getClusterPlacement(ClusterId cluster, BelId root_bel, + std::vector> &placement) const +{ + CellInfo *root_cell = ctx->getClusterRootCell(cluster); + if (root_cell->type == id_RAMB36E1_RAMB36E1) { + if (ctx->getBelType(root_bel) != id_RAMB36E1_RAMB36E1) + return false; + placement.clear(); + placement.emplace_back(root_cell, root_bel); + BelId cursor = root_bel; + for (auto child : root_cell->constr_children) { + auto fnd = next_bram36_up.find(cursor); + if (fnd == next_bram36_up.end()) + return false; + cursor = fnd->second; + if (ctx->getBelType(cursor) != id_RAMB36E1_RAMB36E1) + return false; + placement.emplace_back(child, cursor); + } + return true; + } + return HimbaechelAPI::getClusterPlacement(cluster, root_bel, placement); } SiteIndex XilinxImpl::get_bel_site(BelId bel) const diff --git a/himbaechel/uarch/xilinx/xilinx.h b/himbaechel/uarch/xilinx/xilinx.h index 973ee221..140e2880 100644 --- a/himbaechel/uarch/xilinx/xilinx.h +++ b/himbaechel/uarch/xilinx/xilinx.h @@ -123,6 +123,14 @@ struct XilinxImpl : HimbaechelAPI bool isBelLocationValid(BelId bel, bool explain_invalid = false) const override; bool xc7_logic_tile_valid(IdString tileType, const LogicTileStatus <s) const; + // BRAM cascade support: CASCADEOUT->CASCADEIN only reaches the next + // RAMB36 site up the column; bel z indices are not uniform across BRAM + // tiles, so relative (x,y,z) constraints cannot express this. Precompute + // the next-RAMB36-in-column map and use it in getClusterPlacement. + dict next_bram36_up; + bool getClusterPlacement(ClusterId cluster, BelId root_bel, + std::vector> &placement) const override; + // Pips bool is_pip_unavail(PipId pip) const; bool checkPipAvail(PipId pip) const override { return !is_pip_unavail(pip); }