RAM Single Port
Single-port Random Access Memory with configurable depth and width. Provides read and write operations through a shared address port. Data is written when WR=1 and read otherwise. Features 1 clock cycle read latency. Uses FPGA block RAM for efficient storage.
Introduction
This block implements a single-port RAM where read and write operations share the same address port. Write operations occur when WR=1; read operations occur when WR=0.
Read latency: 1 clock cycle (registered output).
Read during write is READ-FIRST: on a cycle with WR=1, OUT carries the OLD content of the addressed cell. Read the “Read during write” section before building any read-modify-write loop on this block.
Pin Description
Properties
Set the amount of memory in number of words
Number of words in RAM. Allowed: 64-65536. Determines address range.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. Total RAM = MemorySize × BusWidth bits.Default: 1
Range: 1 – 1024
Functional description
Single-port RAM uses one address port for both read and write:
- When WR=1: Writes IN data to address ADDR
- When WR=0: Reads data from address ADDR to OUT (1 cycle later)
Cannot read and write simultaneously to different addresses.
Timing
Write: Data written on rising edge when WR=1. Read: Data available 1 cycle after ADDR provided with WR=0.
Read during write: the memory is READ-FIRST
When WR=1, OUT shows the OLD content of ADDR, not the word being written. The write itself is correct: from the next clock on, ADDR reads the new word. This is a property of the Xilinx macro this block is built on and it cannot be configured away - a simple dual-port block RAM has no path from the write data to the read port, so “write-first” simply does not exist for this topology (it is rejected by the macro’s own design rule checks, in both clocking modes).
What this breaks
Any read-modify-write pipeline that reads and writes the SAME address:
- accumulate-into-memory (
mem[a] = mem[a] + x) - histogram / bin counters
- moving average or running sum kept in RAM
- a delay line with feedback
If such a design reads and writes one address in the same clock, the value it reads is the value from before its own previous write, so every update computes on stale data. This is systematic, not occasional: it happens on every colliding cycle, which in a back-to-back accumulator is every cycle. It is exactly the class of design that wants write-first, which is why the behaviour has to be stated explicitly.
What to do instead
- Forward the write data around the memory. Keep the previous address and the previous input word in two registers, compare the current ADDR with the stored one, and multiplex the stored word onto your datapath instead of OUT when they match. This is the standard write-first bypass and it costs one comparator and one multiplexer.
- Or keep the two accesses apart: insert one idle clock between the write and the read of the same address, or organise the design so that the address being written is never the address being read in the same clock (ping-pong buffers, read pointer trailing the write pointer).
Typical use cases
- Simple data storage
- Lookup tables
- Small buffers