A sync read registers the data: the address goes in one cycle and the data comes out the next. An async read is combinational: the data follows the address in the same cycle.
Most compiled SRAM macros offer sync read only. An async read usually forces a flop implementation, so the read style picks the technology.
// not-run: illustrative excerpt, not a complete testbench
// sync read: data appears one cycle after the address
always @(posedge clk) rdata <= mem[raddr];
// async read: data follows the address in the same cycle
always @(*) rdata = mem[raddr];The two listings above are illustrative excerpts, not runnable testbenches.
Style | Latency | Macro fit | Typical use |
|---|---|---|---|
Sync read | One cycle | Fits compiled macros | Tables, buffers, caches |
Async read | Same cycle | Usually flops only | Register files, bypass paths |
A write port takes an address, data and enable each cycle. One write port plus one read port is the common single-port shape; extra ports serve more accesses per cycle.
Each added port widens the bit cell and the access muxing. Port count therefore moves the flop-versus-macro boundary toward flops while the memory (depth times width) is small.
A read and a write to the same address in the same cycle is read-during-write. The read can return the old data, the new data, or an undefined value.
Which one appears varies by macro and mode. Portable RTL assumes none of them and handles the case explicitly.
Behavior | Meaning | Safe handling |
|---|---|---|
Read-old | Read returns the previous contents | Add bypass when the new data is needed |
Read-new | Read returns the data being written | Still add bypass for macros that differ |
Undefined | Read returns neither reliably | Avoid the collision or register around it |
The script below collects a 16-entry sync-read memory into one memory cell, then maps it to 20 flop cells plus the read muxes. The assertions pin each stage.
read_verilog <<EOV
module top (
input wire clk,
input wire we,
input wire [3:0] waddr,
input wire [3:0] raddr,
input wire [7:0] wdata,
output reg [7:0] rdata
);
reg [7:0] mem [0:15];
always @(posedge clk) begin
if (we)
mem[waddr] <= wdata;
rdata <= mem[raddr];
end
endmodule
EOV
hierarchy -check -top top
proc
memory_collect
select -assert-count 1 t:$mem*
memory_map
select -assert-none t:$mem*
select -assert-count 20 t:$dff
select -assert-count 146 t:$mux
log PASS: 16x8 sync-read memory collects to one memory cell and maps to a flop arrayAsync read. It costs macro fit: most compiled macros cannot do it, so the table becomes flops.
Old data, new data, or an undefined value, depending on the macro and mode. The design must not rely on any one of them.