A dual-port RAM serves two sides at once. The two flavours differ in how many sides may write.
Type | Ports | Typical use |
|---|---|---|
Simple dual-port | One write port, one read port, each with its own clock | FIFOs (including the async FIFO), line buffers, producer/consumer buffers |
True dual-port | Two ports, each can read or write, each with its own clock | Shared buffers between two masters, frame stores |
Count the arrows on each side. In the simple dual-port RAM the left side can only write and the right side can only read. In the true dual-port RAM both sides have the full set: clock, write enable, address, write data and read data.
Simple dual-port: one writer, so two ports can never write the same word at once. The only collision is a read of the word being written.
True dual-port: two writers. Both can target the same word in the same cycle, which is the collision the next lesson handles.
When both sides need to read and write the same memory (for example two processors sharing a buffer, or a frame store updated by one master and read-modify-written by another). If one side only writes and the other only reads, a simple dual-port RAM is enough.
Both listings plus a testbench that writes and reads back the full array on unrelated clocks.
// Simple dual-port RAM: one write port (clock wclk), one read port (clock rclk).
module ram_sdp #(parameter int AW = 5, parameter int DW = 8) (
input logic wclk, we,
input logic [AW-1:0] waddr,
input logic [DW-1:0] wdata,
input logic rclk,
input logic [AW-1:0] raddr,
output logic [DW-1:0] rdata
);
logic [DW-1:0] mem [2**AW];
always_ff @(posedge wclk) if (we) mem[waddr] <= wdata;
always_ff @(posedge rclk) rdata <= mem[raddr]; // read-during-write to the same address: old or new, undefined across clocks
endmodule
// True dual-port RAM: two independent read/write ports.
module ram_tdp #(parameter int AW = 5, parameter int DW = 8) (
input logic clka, wea, input logic [AW-1:0] addra, input logic [DW-1:0] dina, output logic [DW-1:0] douta,
input logic clkb, web, input logic [AW-1:0] addrb, input logic [DW-1:0] dinb, output logic [DW-1:0] doutb
);
logic [DW-1:0] mem [2**AW];
always @(posedge clka) begin
if (wea) mem[addra] <= dina;
douta <= mem[addra];
end
always @(posedge clkb) begin
if (web) mem[addrb] <= dinb;
doutb <= mem[addrb];
end
endmodule
Simple dual-port: the testbench writes all 32 words on wclk (10 ns) and reads them back on rclk (7.4 ns).
True dual-port: the testbench initializes each port's half, overwrites random words there using separate modelS and modelT references, then compares both ports' full-array readback.
The testbench stays away from the same address at the same time on purpose. That case is the subject of the next lesson.
Simple dual-port: source checker, run with Icarus Verilog 13.0 (2026-10-16): prints PASS. It writes all 32 words on wclk (10 ns) and reads them back on rclk (7.4 ns). The clocks have different periods, but this finite simulation does not cover every physical phase.
True dual-port: a finite behavioural check using separate modelS and modelT references. It is not measured mapping or collision evidence.