A single-port RAM reads and writes through one address. When both happen in the same cycle, the output depends on the mode.
Mode | Output when you write address A in this cycle | Block RAM support |
|---|---|---|
Read-first | The old contents of A (read, then write) | Supported on most FPGA block RAMs; for ASIC SRAM macros check the datasheet (single-port macros often hold Q or write-through during a write) |
Write-first (transparent) | The new data being written | Supported on many FPGA block RAMs |
No-change | Unchanged: the previous output holds | Supported; lowest power on some devices |
Behaviour is part of the RAM's contract. If the RTL describes write-first and the target block does not provide it, the tool adds a bypass mux and comparator around the RAM (extra logic and delay) to emulate it.
If the RTL describes read-first and the block provides it natively, nothing extra is needed.
None: any mode returns the new data on a read issued after the write (step 4 in the table below). Only a read in the same cycle as the write needs write-first behaviour or a bypass.
All three modes plus a testbench that writes and reads the same word in each mode.
// Single-port RAM with the three classic read-during-write behaviours.
// READ_FIRST : dout gets the OLD word, then the write happens
// WRITE_FIRST : dout gets the NEW data (the write data passes straight through)
// NO_CHANGE : dout holds its previous value while writing
module ram_modes #(parameter int MODE = 0, parameter int AW = 4, parameter int DW = 8) (
input logic clk, we,
input logic [AW-1:0] addr,
input logic [DW-1:0] din,
output logic [DW-1:0] dout
);
localparam int READ_FIRST = 0, WRITE_FIRST = 1, NO_CHANGE = 2;
logic [DW-1:0] mem [2**AW];
always_ff @(posedge clk) begin
if (we) begin
mem[addr] <= din;
unique case (MODE)
READ_FIRST: dout <= mem[addr]; // old contents
WRITE_FIRST: dout <= din; // new contents
NO_CHANGE: ; // dout keeps its value
default: dout <= 'x;
endcase
end else begin
dout <= mem[addr];
end
end
endmodule
Each slot is one clock cycle. A request offered in one slot lands at the edge ending that slot, so its effect shows in the outputs of the next slot.
Slots 0 and 1 agree at 0x22 in every mode: the read, then the same-address write still in flight.
Slot 2 is the one slot where the modes differ. Read-first still shows the old word 0x22, write-first shows the new word 0x11, and no-change holds 0x22.
Slots 3 to 5 agree at 0x11 again: any mode returns the new data on a read issued after the write.
Step | Operation | Read-first output | Write-first output | No-change output |
|---|---|---|---|---|
1 | Write 0x22 to address 3 | – | – | – |
2 | Read address 3 | 0x22 | 0x22 | 0x22 |
3 | Write 0x11 to address 3 (same word) | 0x22 (old) | 0x11 (new) | 0x22 (kept) |
4 | Read address 3 again | 0x11 | 0x11 | 0x11 |
5 | Write 0x77 to address 5 | (old word of 5) | 0x77 | 0x11 (kept) |