Block Preview

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

IN Input Variable bit BIT
Input data to write. Width: Bit Width (1-1024 bits).
Default: Must be connected
ADDR Input 16 bit BIT VECTOR
Address for read/write operations. Width: 16 bits. Valid range: 0 to Memory Size-1.
Default: Must be connected
WR Input 1 bit BIT
Write enable (1-bit). When 1, writes IN to ADDR. When 0, reads from ADDR to OUT.
Default: Must be connected
CLK Input 1 bit BIT
Clock signal for synchronous operation.
Default: Must be connected
RESET Input 1 bit BIT
Active-high reset (resets pointers, not RAM contents).
Default: Must be connected
OUT Output Variable bit BIT
Output data from read operations. Width: Bit Width. Latency: 1 clock cycle. On a cycle with WR=1 it shows the OLD content of ADDR (read-first); see “Read during write”.

Properties

Property window

Memory Size MemorySize

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

Bit Width BusWidth

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