This commit is contained in:
0xa000 2026-08-04 18:52:01 +09:30 committed by GitHub
commit 240a75da6e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 118 additions and 4 deletions

View File

@ -1442,6 +1442,28 @@ struct FasmBackend
write_bit("CASCOUT_ARD_ACTIVE", !used_rdaddrcasc.empty());
write_bit("CASCOUT_BWR_ACTIVE", !used_wraddrcasc.empty());
}
// A RAMB36E1 with a width-1 port needs the RAMB36-level BRAM36_*_WIDTH_*_1
// feature in addition to the per-half RAMB18_Yx width bits. Without it the
// two 16K halves (which interleave even/odd bit addresses in true 32K x 1
// mode) both respond at addr>>1: every write hits bit pairs 2k/2k+1 and
// the address LSB is ignored.
if (half == 0 && ci != nullptr && ci->type == id_RAMB36E1_RAMB36E1) {
push("RAMB36");
for (const char *port : {"READ_WIDTH_A", "READ_WIDTH_B", "WRITE_WIDTH_A", "WRITE_WIDTH_B"})
if (int_or_default(ci->params, ctx->id(port), 0) == 1)
write_bit(std::string("BRAM36_") + port + "_1");
// Depth-cascaded pairs (64K x 1): the LOWER cell needs its
// RAM_EXTENSION bit set; UPPER shares the NONE encoding (bit
// clear), so only LOWER is written. Was never emitted, which
// breaks true width-1 cascaded memories.
for (const char *port : {"A", "B"}) {
std::string ext =
str_or_default(ci->params, ctx->id(std::string("RAM_EXTENSION_") + port), "NONE");
if (ext == "LOWER")
write_bit(std::string("RAM_EXTENSION_") + port + "_LOWER");
}
pop();
}
pop();
}

View File

@ -754,14 +754,19 @@ void XC7Packer::pack_bram()
ci->connectPort(p, ctx->nets[ctx->id("$PACKER_VCC_NET")].get());
}
} else if (ci->type == id_RAMB36E1_RAMB36E1) {
// ADDRARDADDRL15/ADDRBWRADDRL15 must be tied high in non-cascaded
// modes, but in a depth-cascaded pair (RAM_EXTENSION_A/B = LOWER/
// UPPER) they carry the address MSB that selects the half; the
// multixform has already wired ADDRARDADDR[15] onto them, and
// overriding that with VCC pins both reads and writes to the upper
// 32K half. Only tie when not driven.
for (auto p : {id_ADDRARDADDRL15, id_ADDRBWRADDRL15}) {
if (!ci->ports.count(p)) {
ci->ports[p].name = p;
ci->ports[p].type = PORT_IN;
} else {
ci->disconnectPort(p);
}
ci->connectPort(p, ctx->nets[ctx->id("$PACKER_VCC_NET")].get());
if (ci->getPort(p) == nullptr)
ci->connectPort(p, ctx->nets[ctx->id("$PACKER_VCC_NET")].get());
}
if (int_or_default(ci->params, id_WRITE_WIDTH_A, 0) == 1) {
ci->disconnectPort(id_DIADI1);
@ -803,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()

View File

@ -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
@ -397,7 +440,7 @@ void XilinxImpl::configurePlacerStatic(PlacerStaticCfg &cfg)
comb.cell_area[id_RAMB18E1_RAMB18E1] = StaticRect(1.0f, 3.0f);
comb.bel_area[id_RAMB18E1_RAMB18E1] = StaticRect(1.0f, 3.0f);
comb.cell_area[id_RAMB36E1_RAMB36E1] = StaticRect(1.0f, 6.0f);
comb.bel_area[id_RAMB36E1_RAMB36E1] = StaticRect(0.0f, 0.0f);
comb.bel_area[id_RAMB36E1_RAMB36E1] = StaticRect(1.0f, 6.0f);
comb.spacer_rect = StaticRect(1.0f, 3.0f);
}
{

View File

@ -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 &lts) 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); }