Two flavors of “dual-port”
“Dual-port RAM” covers two genuinely different designs, and it matters which one a spec is asking for:
- True dual-port (what this problem implements): both ports can independently read or write, at independent addresses, in the same cycle. This is the more general — and more expensive in silicon — variant, used when two independent agents (e.g. two CPU cores, or a CPU and a DMA engine) each need full read/write access to a shared memory.
- Simple dual-port (one write-only port + one read-only port): cheaper and very common when the access pattern is naturally asymmetric — e.g. one producer writing a buffer while one consumer reads it (this is exactly what a synchronous FIFO’s underlying memory needs). It looks like this:
// Simple dual-port RAM: one write-only port, one read-only port.
module simple_dpram #(parameter AW = 6, parameter DW = 8) (
input wire clk,
input wire [AW-1:0] waddr,
input wire [DW-1:0] wdata,
input wire we,
input wire [AW-1:0] raddr,
output reg [DW-1:0] rdata
);
reg [DW-1:0] mem [0:(1<<AW)-1];
always @(posedge clk) if (we) mem[waddr] <= wdata;
always @(posedge clk) rdata <= mem[raddr];
endmodule
The graded solution for this problem is the true dual-port variant, since it’s the more general case and subsumes the simple one (you can always leave one port’s we tied low to get simple dual-port behavior out of a true dual-port implementation).
Interface
| Signal | Description |
|---|---|
clk |
Single shared clock for both ports. |
addr_a/din_a/we_a/dout_a |
Port A: independent address, write data, write-enable, registered read data. |
addr_b/din_b/we_b/dout_b |
Port B: independent address, write data, write-enable, registered read data. |
Correctness constraints
- Each port must be able to read or write any address, completely independently of what the other port is doing that cycle, as long as the two ports don’t target the same address in a conflicting way (see below).
- A write on one port must become visible to a read from the other port on a subsequent cycle (cross-port visibility) — this is the entire point of sharing one memory array between two ports.
- Same-address collision hazard: if both ports write to the same address in the same cycle with different data, or one port writes while the other simultaneously reads that same address, the result is genuinely implementation-defined (real dual-port memory macros differ on how they resolve this). The design here does not attempt to define a specific resolution for that hazard — the surrounding system must guarantee it never issues a same-address write/write or write/read collision if a specific outcome is required. The provided testbench deliberately avoids exercising same-address collisions and only checks independent-address concurrent access, which is a well-defined and safe usage pattern.
- Reads must be registered (one cycle of latency), matching the Single-Port RAM problem’s convention.