The basic building block
Almost every on-chip buffer that needs to be written at runtime (as opposed to a ROM’s fixed content) starts from this shape: one address, one data-in port, a write-enable, and a registered data-out. It’s called “single-port” because there is exactly one address bus shared by both the read and the write — you cannot read one location and write a different one in the same cycle (that requires two ports; see the Dual-Port RAM problem in this category).
Interface
| Signal | Direction | Width | Description |
|---|---|---|---|
clk |
input | 1 | Clock. |
addr |
input | AW |
Shared address for both read and write. |
din |
input | DW |
Data to write when we is high. |
we |
input | 1 | Write-enable. |
dout |
output | DW |
Registered read data for addr, one cycle later. |
Read-during-write behavior
Because addr is shared, it’s possible to assert we and effectively “read” the same address in the same cycle. This design uses the standard read-old-data convention (sometimes called “no-change” or “read-before-write” behavior in vendor memory-compiler terminology): dout reflects whatever was in mem[addr] before this cycle’s write, and the new data only becomes visible on a subsequent read. This is the default behavior most FPGA block-RAM primitives synthesize to when read and write share a clock and address, and it’s a common source of confusion for anyone expecting write-forwarding — so it’s called out explicitly here and tested directly.
Cycle-by-cycle example
| Cycle | addr |
din |
we |
dout (this cycle, i.e. result of previous cycle’s access) |
|---|---|---|---|---|
| 0 | 10 | — | 0 | (whatever was previously registered) |
| 1 | 10 | 0xA5 |
1 | old mem[10] (write takes effect after this cycle) |
| 2 | 10 | — | 0 | 0xA5 (the write from cycle 1 is now visible) |
Correctness constraints
doutmust be a registered (one-cycle-delayed) view ofmem[addr], not combinational.- On a cycle where
weis asserted foraddr,doutmust reflect the pre-write (“old”) contents of that address, not the value being written this same cycle. - Once a write has completed, all subsequent reads (with
welow) of that address must return the newly written value, not the original one. - The memory must support the full address range implied by
AW(i.e.2^AWdistinct locations), each independently readable/writable.