Synchronous Memories
Modeling RAM/ROM with clock.
Memory modeling in Verilog is essential for designing processors, cache controllers, FIFOs, and embedded systems. Synchronous memories use a clock to register read and write operations, unlike asynchronous memories where outputs respond immediately to address changes. Understanding how to correctly model synchronous RAM and ROM in Verilog ensures that synthesis tools map the design onto on-chip memory blocks efficiently.
Core Concept: Clocked Memory Access
In a synchronous memory, all read and write operations are triggered by the rising (or falling) edge of a clock signal. This is in contrast to asynchronous memory where the output changes immediately when the address changes. In synchronous design, the write enable (we) signal is sampled on the clock edge. If we is high, the data on data_in is written to the location specified by addr. If we is low, the memory retains its contents.
For a read operation, there are two common behaviors depending on the design: read-first (output shows old data at the addressed location before the write happens on the same cycle) and write-first (output shows the new data being written). These are also called read-before-write and write-before-read modes. FPGA block RAMs support both modes and the Verilog coding style determines which is inferred.
For synchronous read, the data output register is updated on the clock edge: always @(posedge clk) data_out <= mem[addr];. This adds one clock cycle of read latency, meaning data is available one cycle after the address is presented. This is standard for FPGA BRAM inference and is essential for meeting timing at high clock frequencies.
Modeling Synchronous RAM in Verilog
A synchronous RAM is declared using a two-dimensional register array: reg [W-1:0] mem [0:DEPTH-1]; where W is the data width in bits and DEPTH is the number of addressable locations. The write port is inside a clocked always block, conditioned on we. The read port can be synchronous (clocked output register) or asynchronous (combinational assignment).
For synthesis tools to correctly infer a block RAM (BRAM) on an FPGA, both the read and write ports should be synchronous. If the read port is asynchronous, many tools will infer distributed RAM (using lookup tables) instead of block RAM, which is far less area-efficient for large memories. This is a critical difference that affects both area and timing in FPGA implementation.
Modeling Synchronous ROM in Verilog
A ROM has no write port. It is initialized at synthesis time using an initial block or a $readmemh / $readmemb system task. The initial block assigns constant values to all memory locations. Since ROM content never changes at runtime, synthesis tools recognize the read-only pattern and map it to appropriate on-chip resources. A typical synchronous ROM output uses a clocked always block: always @(posedge clk) data_out <= mem[addr];.
The $readmemh system task reads hexadecimal values from a file and initializes the memory array. This is useful for loading lookup tables, coefficient memories, or microcode ROMs. The corresponding task for binary is $readmemb. These tasks are supported in both simulation and synthesis on most FPGA tools, making them the preferred initialization method for large ROMs.
Mathematical Expression
For a memory with address width A bits and data width W bits: the total number of addressable locations is DEPTH = 2^A, and the total memory capacity in bits is: Capacity = 2^A x W bits. For example, a memory with 10-bit address and 8-bit data has 1024 locations and a capacity of 8192 bits (1 KB). The read latency in synchronous mode is exactly 1 clock cycle when a registered output stage is used.
Practical Understanding
FPGA block RAMs are dual-port synchronous memories with configurable read/write mode. Properly coded Verilog RAM inference allows the synthesis tool to use these hardware primitives directly, avoiding the overhead of implementing memory with thousands of flip-flops. ROMs are commonly used for sine lookup tables in DDS synthesizers, CRC tables, character font bitmaps, and instruction ROMs in small processors.
Given:
Synchronous single-port RAM: 8-bit data width, 6-bit address (64 locations)
Operation: Write 0xAB to address 10, then read address 10 one cycle later
Why this formula applies:
Synchronous write: on posedge clk, if we=1, mem[addr] <= data_in
Synchronous read: on posedge clk, data_out <= mem[addr]
Read latency = 1 clock cycle
Formula:
Capacity = 2^A x W = 2^6 x 8 = 512 bits = 64 bytes
Read latency = 1 cycle
Substitution:
Cycle 1: addr=10, we=1, data_in=0xAB -> mem[10] written = 0xAB
Cycle 2: addr=10, we=0 -> data_out registered = mem[10] = 0xAB
Calculation:
Cycle 1 posedge: write executes, mem[10]=0xAB
Cycle 2 posedge: read executes, data_out=0xAB (available after cycle 2 clock edge)
Final Answer:
data_out = 0xAB, available 1 clock cycle after read address presentedExam Tip: In synchronous RAM, the read latency is 1 clock cycle when a registered output is used. GATE and VLSI questions often ask to distinguish read-first vs write-first mode — the key is whether data_out reflects old or new data when a simultaneous read-write to the same address occurs.
Read/Write Timing Mechanism
- Synchronous RAM write: on posedge clk, if we=1, mem[addr] <= data_in.
- Synchronous read: data_out <= mem[addr] on posedge clk; 1 cycle read latency.
- ROM: read-only, initialized via initial block or $readmemh; no write port.
- Capacity = 2^A x W bits for address width A and data width W.
- Registered output is necessary for FPGA BRAM inference; asynchronous read infers distributed RAM.
Quick Revision
- Memory declaration: reg [W-1:0] mem [0:DEPTH-1] where DEPTH = 2^A.
- Synchronous write: always @(posedge clk) if(we) mem[addr] <= data_in.
- Synchronous read: always @(posedge clk) data_out <= mem[addr]; — 1 cycle latency.
- ROM: initial block assigns constants; $readmemh loads from hex file.
- Capacity = 2^A x W bits.
- Read-first vs write-first: determines data_out value on simultaneous read-write to same address.
- Exam trap: asynchronous read (combinational) does NOT infer BRAM on FPGA — always use registered read for block RAM.
Synchronous Memory Quiz
Test understanding of synchronous RAM and ROM modeling.
Q1.What syntax correctly declares a memory array of 256 bytes in Verilog?
Related Articles
Modeling Latches
Level sensitive behavior, inferring latches accident.
6 min read
Synchronous Counter
Common clock, simultaneous switching, faster operation.
8 min read
Modeling Flip-Flops
D-FF, T-FF, JK-FF with async/sync resets.
7 min read
Modeling Counters
Up/down counter, mod-N counter.
12 min read
ROM/PROM Design
Nor-based and Nand-based ROMs.
9 min read