himbaechel/xilinx: emit BRAM36 width-1 features for RAMB36E1

A RAMB36E1 port configured with width 1 (true 32K x 1 mode) needs the
RAMB36-level BRAM36_{READ,WRITE}_WIDTH_{A,B}_1 feature in addition to
the per-half RAMB18_Yx width bits, which were the only ones emitted.

In this mode the two 16K halves interleave even/odd bit addresses.
Without the RAMB36-level feature both halves respond at addr>>1:
every write hits the bit pair 2k/2k+1 and the address LSB is ignored,
so half the memory aliases the other half.

Verified against Vivado bitstreams for the same configuration and on
xc7a100t hardware (32K x 1 and cascaded 64K x 1 inferred memories).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Joris van Zwieten 2026-07-30 15:45:59 +02:00
parent 68c1acd80a
commit 75c8144d42
1 changed files with 12 additions and 0 deletions

View File

@ -1442,6 +1442,18 @@ 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");
pop();
}
pop();
}