ROM, RAM, and FIFO
Describe memories that tools can recognize and handle FIFO boundaries correctly.
A ROM can be described with a case statement
A small constant table is easy to describe with a combinational block. Every address must produce a value.
module opcode_rom (
input wire [2:0] i_address,
output reg [7:0] o_data
);
always @* begin
case (i_address)
3'd0: o_data = 8'h13;
3'd1: o_data = 8'h27;
3'd2: o_data = 8'h42;
3'd3: o_data = 8'h81;
3'd4: o_data = 8'hA5;
default: o_data = 8'h00;
endcase
end
endmoduleDepending on its size and the target FPGA, the tool may implement this table with distributed logic or internal memory.
Describing synchronous RAM
A memory is an array of words. The process style often determines which resource can be inferred.
module synchronous_ram #(
parameter WIDTH = 8,
parameter ADDRESS_WIDTH = 6
) (
input wire i_clk,
input wire i_write_enable,
input wire [ADDRESS_WIDTH-1:0] i_address,
input wire [WIDTH-1:0] i_write_data,
output reg [WIDTH-1:0] o_read_data
);
localparam DEPTH = 1 << ADDRESS_WIDTH;
reg [WIDTH-1:0] memory [0:DEPTH-1];
always @(
The read is registered. An address presented before an edge produces its data after that edge. The result of reading and writing the same address on one edge depends on the coding style, tool, and target memory mode. Specify that case and verify it after synthesis.
Resetting every word with a loop can prevent block RAM inference on some FPGAs. If stored data does not need a reset value, reset only the control registers.
What a FIFO stores
A synchronous FIFO contains:
- a memory;
- a write pointer;
- a read pointer;
- a counter or another method that distinguishes empty from full.
Status flags must reflect transfers that were actually accepted, not only requests that were presented.
module synchronous_fifo #(
parameter WIDTH = 8,
parameter ADDRESS_WIDTH = 3
) (
input wire i_clk,
input wire i_reset,
input wire i_write,
input wire [WIDTH-1:0] i_write_data,
input wire i_read,
output reg [WIDTH-1:0] o_read_data,
output reg o_read_valid,
output wire o_empty,
output wire o_full,
output wire [ADDRESS_WIDTH:0] o_level
);
localparam DEPTH
This version uses a power-of-two depth, so the pointers wrap naturally. It rejects a write when already full and a read when empty. An accepted read and write in the same cycle leave count unchanged.
Interface choices must be stated
Several behaviors are valid: first-word fall-through, registered output after a request, accepting a write while reading from a full FIFO, or passing a new item directly through an empty FIFO.
None is universal. The producer, consumer, and testbench must use the same rule.
An asynchronous FIFO is a different circuit
Adding a second clock to this FIFO is not enough. An asynchronous FIFO needs pointers designed for clock-domain crossing, often Gray-coded, synchronizers, and careful full and empty logic. Treat it as a separate architecture.
Key points
- RTL style affects ROM and RAM inference.
- A synchronous read adds clock latency.
- Read-during-write behavior must be known.
- A FIFO advances only on accepted transfers.
- An asynchronous FIFO is not a synchronous FIFO with one more clock port.
📝 Test your knowledge - Chapter quiz