Lesson 16/2564%
MODULE 16 OF 2515 MINFPGA FUNDAMENTALS

Block RAM (BRAM)

Master the RAMB36E1 primitive — dual-port modes, width configurations, ECC error correction, FIFO mode, and cascade techniques for on-chip memory in Xilinx 7-series FPGAs.

What is Block RAM?

Block RAM (BRAM) is a dedicated on-chip SRAM resource embedded in the FPGA fabric. Unlike LUT-based memory, BRAM is a fixed silicon block optimized purely for data storage. In 7-series Xilinx devices, the core primitive is RAMB36E1 — a 36 Kb dual-port synchronous SRAM that can also be split into two independent 18 Kb blocks (RAMB18E1).

BRAMs are arranged in columns throughout the FPGA fabric, interleaved between CLB columns. They are the go-to solution for FIFOs, packet buffers, coefficient tables, and frame memories — anything that needs dense, fast on-chip storage.

RAMB36E1 — Block Diagram

graph TD subgraph BRAM["RAMB36E1 — 36Kb Block RAM"] PORTA["Port A\nADDRA[15:0]\nDINA[31:0]\nDOUTA[31:0]\nWEA[3:0] · CLKA · ENA"] CORE["36Kb SRAM Core\n+ 4Kb Parity Bits\n= 40Kb total capacity"] PORTB["Port B\nADDRB[15:0]\nDINB[31:0]\nDOUTB[31:0]\nWEB[3:0] · CLKB · ENB"] ECC["ECC Logic\nSECDED 72-bit mode\nEN_ECC_READ / EN_ECC_WRITE"] PORTA <--> CORE PORTB <--> CORE CORE --> ECC end

Width Configurations

Each RAMB36E1 port can be independently configured for different aspect ratios. The total bit capacity stays constant at 36Kb (plus 4Kb parity):

Width Mode Depth Data Bits Address Bits Parity Bits Notes
×1 32K 1 15 0 Deepest configuration
×2 16K 2 14 0
×4 8K 4 13 0
×9 4K 8 12 1 Parity bit for ECC
×18 2K 16 11 2 Common FIFO width
×36 1K 32 10 4 Widest per-port config
×72 (SDP) 512 64 9 8 SDP mode only — 72-bit ECC

Dual-Port Modes

Mode Port A Port B Max Width Best Use Case
Simple Dual-Port (SDP) Write only Read only 72-bit (combined) FIFOs, streaming buffers
True Dual-Port (TDP) Read + Write Read + Write 36-bit each port Shared memory, CDC buffers

In SDP mode, ports A and B are combined: Port A becomes the full-width write port and Port B becomes the full-width read port. This allows a maximum width of 72 bits (64 data + 8 parity) — ideal for FIFOs. In TDP mode, both ports operate independently, each up to 36 bits wide, and can use different clocks. This enables true shared memory between two clock domains.

Read/Write Collision Modes

When both ports access the same address simultaneously, the behavior is determined by the READ_WIDTH_A/B and write mode settings:

Mode Simultaneous Read+Write Behavior Best For
READ_FIRST Old data is read before new data is written. Output shows previous value. Collision-safe register files
WRITE_FIRST New written data is immediately forwarded to the read output (transparent). Look-ahead FIFOs
NO_CHANGE Read output does not change during a write. Previous output is held. Power saving; most FIFOs
Warning — Read Latency BRAM output registers add 1 clock cycle of read latency. Standard BRAM read latency is 2 cycles (1 cycle address registered + 1 cycle output registered). If you bypass output registers for 1-cycle latency, the read path may not meet timing at high clock speeds above 200–250MHz.

ECC — Error Correction Code

RAMB36E1 implements SECDED (Single-Error Correct, Double-Error Detect) ECC in 72-bit wide mode. The 8 parity bits in the ×9 width configuration store syndrome bits for error detection and correction. Enable via EN_ECC_READ and EN_ECC_WRITE generic parameters. ECC status pins SBITERR and DBITERR signal single-bit and double-bit errors.

Initialization

BRAMs can be pre-loaded with data at FPGA configuration time using INIT_00 through INIT_7F parameters. Each parameter is a 256-bit hex string. This makes BRAMs ideal for ROM coefficient tables, boot code storage, and lookup tables:

RAMB36E1 #( .INIT_00(256'hDEADBEEF_CAFEBABE_...), // First 8 locations .INIT_01(256'h...), // Next 8 locations // ... up to INIT_7F .INITP_00(256'h...) // Parity initialization ) bram_inst (...);

FIFO Mode

RAMB36E1 can operate as a built-in FIFO with dedicated read/write pointers and flags: EMPTY, FULL, ALMOST_EMPTY, ALMOST_FULL, RDCOUNT, WRCOUNT. Both synchronous (single-clock) and asynchronous (dual-clock) FIFO modes are supported. Xilinx FIFO Generator IP and the xpm_fifo_sync/xpm_fifo_async macros use this mode internally.

Engineering Tip — Use XPM FIFOs For FIFOs, ALWAYS use Xilinx FIFO Generator IP (or xpm_fifo_sync/xpm_fifo_async) instead of manual BRAM instantiation. The IP handles write-to-read synchronization, ECC, ALMOST_FULL programmable thresholds, and timing corner cases that are extremely easy to miss in manual implementations.

BRAM Capacity — Across Device Families

Cascade

Multiple BRAMs can be cascaded to build deeper or wider memories. RAMB36E1 provides CASCADEOUTA/CASCADEOUTB and CASCADEINA/CASCADEINB pins for address extension, allowing two 36Kb BRAMs to form a 72Kb×1 deep memory without routing overhead.

UltraScale+ — UltraRAM (URAM) In UltraScale+, BRAMs remain 36Kb (same as 7-series) but Xilinx added a new tier: UltraRAM (URAM) blocks — each 288Kb (8× denser than BRAM). Virtex UltraScale+ VU13P has 960 URAMs = 276 Mb of ultra-dense on-chip storage, plus 2160 BRAMs. URAMs have 2-cycle read latency and 72-bit data width.
Interview Question Q: What is the difference between SDP and TDP BRAM mode?

A: SDP (Simple Dual-Port) combines both RAMB36E1 ports to create one full-width write port and one full-width read port, enabling up to 72-bit wide access — the most common configuration for FIFOs. TDP (True Dual-Port) gives two fully independent ports, each capable of read and write with separate clocks, enabling shared memory between two independent clock domains at up to 36-bit width each.
Best Practice — BRAM vs Distributed RAM Use BRAM for: FIFOs, packet buffers, coefficient lookup tables, frame buffers, any memory larger than 1Kb. Use distributed RAM (LUT-based) only for very small memories (under 1Kb) that require asynchronous (0-cycle) read access. Mixing both intelligently maximizes resource utilization.

Knowledge Check

1. What is the data capacity of one RAMB36E1 primitive (excluding parity bits)?

  • A 18 Kb
  • B 36 Kb
  • C 40 Kb
  • D 72 Kb
Correct! RAMB36E1 is 36Kb of data SRAM plus an additional 4Kb of parity bits, totaling 40Kb of physical capacity. However, the usable data capacity is 36Kb.

2. Which BRAM mode allows both ports to independently read AND write, making it suitable for shared memory between two clock domains?

  • A Simple Dual-Port (SDP)
  • B True Dual-Port (TDP)
  • C Single-Port (SP)
  • D FIFO mode
Correct! True Dual-Port (TDP) mode gives both Port A and Port B full read-and-write capability, each with an independent clock. This is the ideal configuration for ping-pong buffers and clock-domain-crossing memories.

3. What ECC scheme does RAMB36E1 implement in 72-bit mode?

  • A CRC-32
  • B Hamming(7,4)
  • C SECDED (Single-Error Correct, Double-Error Detect)
  • D Reed-Solomon
Correct! RAMB36E1 uses SECDED — Single-Error Correct, Double-Error Detect — across a 72-bit data word (64 data + 8 syndrome bits). Single-bit errors are automatically corrected; double-bit errors are flagged via the DBITERR pin.

4. Which generic parameter set is used to pre-initialize BRAM contents at configuration time?

  • A ROM_CONTENT_00 through ROM_CONTENT_FF
  • B INIT_00 through INIT_7F
  • C PRELOAD_00 through PRELOAD_FF
  • D MEM_INIT_0 through MEM_INIT_7
Correct! INIT_00 through INIT_7F are 128 parameters, each a 256-bit hex string, used to initialize the BRAM contents during FPGA bitstream generation. This allows BRAMs to function as pre-loaded ROMs immediately after power-up.

5. What is the standard read latency of a RAMB36E1 with output registers enabled?

  • A 0 cycles (combinational)
  • B 1 cycle
  • C 2 cycles
  • D 4 cycles
Correct! Standard BRAM read latency with output registers is 2 cycles: 1 cycle for the address to be registered into the BRAM, and 1 cycle for the output register to capture the data. Bypassing the output register reduces this to 1 cycle but limits maximum clock frequency.

Practical Exercise

BRAM Design Challenge

  1. Design a 1024×32-bit synchronous FIFO using BRAM in Vivado. Use xpm_fifo_sync with FIFO_DEPTH=1024 and DATA_WIDTH=32.
  2. Verify the FIFO can run at 250MHz by checking the Vivado timing report after implementation. What is the worst negative slack?
  3. Check the Vivado utilization report: how many RAMB36E1 (or RAMB18E1) primitives does the FIFO consume?
  4. Enable ECC (set ECC_MODE="en_ecc"). What happens to the data width? How does BRAM count change? What extra output signals become available?