Block RAM (BRAM) is dedicated, on-chip synchronous RAM embedded directly in FPGA fabric, implemented as hard memory macros rather than as logic cells. Exact naming, block sizes, and timing behavior vary by vendor and device family. It provides high-bandwidth, low-latency storage that would be prohibitively expensive to build from lookup tables (LUTs) and flip-flops alone.
In practice
BRAM is one of the primary fixed resources on an FPGA, alongside LUTs, DSP slices, and I/O blocks. Xilinx 7-series and UltraScale devices generally use 36 Kb dual-port BRAM tiles (also configurable as two independent 18 Kb blocks); Intel/Altera devices use embedded memory blocks whose size and naming vary by family (for example, M10K blocks in Cyclone V, M20K blocks in Arria 10, and other variants in other generations). The total available BRAM varies widely, from tens of kilobits on small parts like the Xilinx Spartan-6 XC6SLX4 (approximately 72 Kb) to tens of megabits on larger devices like the Xilinx Kintex-7 XC7K480T (approximately 34 Mb); these figures are device-specific and should always be verified against the relevant datasheet.
In practice, BRAMs are used for FIFOs, frame buffers, packet buffers, lookup tables, soft-processor instruction and data memory, and any structure where distributed RAM would consume too many LUTs. HDL synthesis tools will infer BRAM automatically when they recognize certain memory patterns (e.g., a registered read with a synchronous write in RTL), or you can instantiate vendor primitives directly (RAMB36E1, RAMB18E2 in Xilinx; altsyncram in Intel) for precise control. The "Using the Beaglebone PRU to achieve realtime at low cost" post illustrates a related concept: tightly coupled, deterministic on-chip memory is critical for real-time data paths, a role BRAM fills in FPGA designs.
A common pitfall is running out of BRAM before running out of LUTs, especially in designs with many small FIFOs or lookup tables. Synthesis tools sometimes fail to pack small memories efficiently, wasting entire 36 Kb blocks for a 64-word table. Checking the post-synthesis resource report early and considering memory packing (multiple logical memories sharing one BRAM) is important. Another pitfall is the read-first vs. write-first vs. no-change mode: the read behavior on a write collision differs between these modes and between vendors, and misunderstanding this can produce subtle data corruption bugs.
Frequently asked
Can BRAM be used as dual-port memory?
On most
FPGA families, yes. Many BRAM tiles support true dual-port or simple dual-port modes with two independent read/write ports that can operate at different clock frequencies and, on some devices, different data widths. Exact port capabilities and supported asymmetries vary by vendor and block type, so consult the device documentation for the specific modes available.
What is the difference between BRAM and distributed RAM?
Distributed
RAM is built from the
FPGA's LUT fabric (e.g., Xilinx SLICEM LUTs configured as 64x1 RAM). On many architectures it supports asynchronous reads by default, though the exact behavior depends on the family and inference style. It is lower
latency for very small memories and consumes routing resources proportional to memory size. BRAM is a hard macro, fully synchronous, more area-efficient for larger memories (typically above a few hundred bits), and has a fixed, predictable timing profile independent of placement.
Will HDL synthesis tools infer BRAM automatically?
Usually yes, if you write RTL in the standard template: a synchronous write, a registered (clocked) read, and no reset on the read data register. Xilinx Vivado and Intel Quartus both recognize these patterns and map them to BRAM. However, adding an asynchronous read or certain reset conditions can prevent inference and force the tool to use distributed
RAM instead. Check the synthesis log for 'RAM style' messages to confirm the intended mapping.
Can BRAM be initialized at power-on or bitstream load?
Yes.
FPGA BRAM content is part of the configuration bitstream. You can initialize it with a .mif (Intel) or a $readmemh/$readmemb call in RTL (Xilinx and Intel both support this). This is commonly used to pre-load firmware for a soft processor like MicroBlaze or
RISC-V into BRAM, eliminating the need to fetch code from external
flash at startup.
Is BRAM the same as the embedded SRAM found in MCUs like STM32 or nRF52?
No.
MCU on-chip
SRAM (e.g., the 256 KB SRAM in an STM32H7 or 256 KB in an nRF5340) is typically a compiled embedded SRAM macro and is always present on the die.
FPGA BRAM is also a hard macro, but it is a configurable resource (often supporting dual-port or other modes depending on the device) whose connectivity is defined by the FPGA configuration bitstream. The two serve similar roles (fast on-chip storage) but exist in fundamentally different device architectures.
Differentiators vs similar concepts
BRAM is often confused with distributed
RAM (LUT RAM) on FPGAs. Distributed RAM is built from configurable LUT fabric and typically supports asynchronous reads on many architectures, though the exact behavior varies by family and inference style; BRAM is a hard macro that is fully synchronous on both ports. BRAM is more bit-efficient for larger memories; distributed RAM has lower
latency and more flexibility for small memories. BRAM should also not be confused with general
MCU on-chip
SRAM, which is a fixed compiled memory macro in a microcontroller rather than a reconfigurable memory tile in programmable logic.