RAM Dual Port
Simple dual-port RAM with independent read and write ports. Allows simultaneous read and write operations to different addresses. Write operations use WRITE_ADDR and CLK_WR; read operations use READ_ADDR and CLK_RD. Features 1 clock cycle read latency.
Introduction
This block implements a simple dual-port RAM with separate read and write ports, allowing simultaneous access to different addresses. Read and write can operate on independent clock domains.
Read latency: 1 clock cycle.
Same-address collision is READ-FIRST: if READ_ADDR equals WRITE_ADDR on a cycle with WR=1, OUT carries the OLD content of that cell. Read the “Same-address collision” section before building any read-modify-write loop.
Pin Description
Properties
Set the amount of memory in number of words
Number of words. Allowed: 64-65536.Default: 1024
Options: 64 128 256 512 1024 2048 4096 8192 16384 32756 65536
Set the size in bits of the data
Bit width per word. Range: 1-1024.Default: 1
Range: 1 – 1024
Functional description
Dual-port RAM has independent ports:
- Write port: WRITE_ADDR, IN, WR, CLK_WR
- Read port: READ_ADDR, OUT, CLK_RD
Can read and write simultaneously to different addresses, even with different clocks.
Timing
Write: Data written to WRITE_ADDR on CLK_WR rising edge when WR=1. Read: Data from READ_ADDR available 1 cycle after CLK_RD rising edge.
Same-address collision: the memory is READ-FIRST
Writing and reading different addresses in the same clock costs nothing - that is the point of this block. When READ_ADDR equals WRITE_ADDR on a cycle with WR=1, however, OUT carries the OLD content of that cell, not the word being written. The write itself is correct: from the next clock on, that address reads the new word.
This is a property of the Xilinx simple dual-port macro and it cannot be configured away: there is no path from the write data of one port to the read port of the other, so “write-first” does not exist for this topology (the macro’s own design rule checks reject it, in both clocking modes). With independent read and write clocks the notion of “the same cycle” is not even well defined, which is the deeper reason.
What this breaks
Any read-modify-write pipeline in which the read address can catch up with
the write address: accumulate-into-memory (mem[a] = mem[a] + x), histogram or
bin counters, a running sum or moving average kept in RAM, a delay line with
feedback. On every colliding cycle the value read is the one from before the
previous write, so the update computes on stale data - systematically, not
occasionally, and in exactly the designs that would want write-first.
What to do instead
- Forward the write data around the memory: register WRITE_ADDR and IN, compare them with READ_ADDR, and multiplex the registered word onto your datapath instead of OUT when they match. One comparator and one multiplexer.
- Or make the collision impossible: keep the read pointer trailing the write pointer by at least one word, or use a ping-pong scheme in which the half being written is never the half being read. This is the natural shape of the producer-consumer and cross-clock uses below, and those are unaffected.
Typical use cases
- Ping-pong buffers
- Cross-clock-domain data storage
- Producer-consumer queues