mirror of https://github.com/YosysHQ/nextpnr.git
himbaechel/xilinx: place cascaded RAMB36 pairs as clusters
CASCADEOUTA/B -> CASCADEINA/B are dedicated wires that only reach the vertically adjacent RAMB36 site in the same column; the router has no general path for them. Without a placement constraint the two cells of a depth-cascaded pair land on arbitrary sites and routing fails. Cluster each cascade pair in pack_bram (analogous to carry chains). Plain relative (x,y,z) constraints cannot express "next RAMB36 up the column": bel z indices are not uniform across BRAM tiles. Instead, precompute a next-RAMB36-in-column map at init and resolve child bels through a getClusterPlacement override. Hardware-verified on xc7a100t: cascaded 64K x 1 memories place, route and function (128K ROM content fingerprint passes). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
a652a752de
commit
1c85dee263
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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<int, std::map<int, BelId>> 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<std::pair<CellInfo *, BelId>> &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
|
||||
|
|
|
|||
|
|
@ -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<BelId, BelId> next_bram36_up;
|
||||
bool getClusterPlacement(ClusterId cluster, BelId root_bel,
|
||||
std::vector<std::pair<CellInfo *, BelId>> &placement) const override;
|
||||
|
||||
// Pips
|
||||
bool is_pip_unavail(PipId pip) const;
|
||||
bool checkPipAvail(PipId pip) const override { return !is_pip_unavail(pip); }
|
||||
|
|
|
|||
Loading…
Reference in New Issue