Memories: RAM, ROM, FIFOs

Synthesis tools infer block RAM / distributed RAM when you follow standard templates.

Synthesis tools infer block RAM / distributed RAM when you follow standard templates.

22.1 Single-port RAM (synchronous read → block RAM)

Read-during-write modes: read-first, write-first, no-change. Asynchronous reads (dout <= ram(addr) outside the clocked process) infer distributed/LUT RAM.

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity spram is
  generic (ADDR_W : positive := 10; DATA_W : positive := 8);
  port (
    clk  : in  std_logic;
    we   : in  std_logic;
    addr : in  std_logic_vector(ADDR_W-1 downto 0);
    din  : in  std_logic_vector(DATA_W-1 downto 0);
    dout : out std_logic_vector(DATA_W-1 downto 0));
end entity;

architecture rtl of spram is
  type ram_t is array (0 to 2**ADDR_W - 1) of std_logic_vector(DATA_W-1 downto 0);
  signal ram : ram_t := (others => (others => '0'));
begin
  process (clk)
  begin
    if rising_edge(clk) then
      if we = '1' then
        ram(to_integer(unsigned(addr))) <= din;
      end if;
      dout <= ram(to_integer(unsigned(addr)));      -- read-first behavior
    end if;
  end process;
end architecture;

22.2 Simple dual-port & true dual-port RAM

For true dual-port RAM with two clocks and two writers, use a shared variable of the RAM type (tool templates) or vendor IP (XPM macros: xpm_memory_tdpram).

process (clk)                         -- write port A
begin
  if rising_edge(clk) then
    if we_a = '1' then ram(to_integer(unsigned(addr_a))) <= din_a; end if;
  end if;
end process;

process (clk_b)                       -- read port B (may be a different clock)
begin
  if rising_edge(clk_b) then
    dout_b <= ram(to_integer(unsigned(addr_b)));
  end if;
end process;

22.3 ROM

Generate tables with functions (math_real.sin in a constant function) or load from files (§19).

type rom_t is array (0 to 15) of std_logic_vector(7 downto 0);
constant SINE_ROM : rom_t := (x"80", x"B0", x"DA", x"F5", x"FF", x"F5", x"DA", x"B0",
                              x"80", x"4F", x"25", x"0A", x"00", x"0A", x"25", x"4F");
process (clk)
begin
  if rising_edge(clk) then
    data <= SINE_ROM(to_integer(unsigned(addr)));
  end if;
end process;

22.4 Synchronous FIFO

Extensions: almost-full/almost-empty flags, first-word-fall-through (FWFT), data count, asynchronous (dual-clock) FIFOs with Gray-coded pointers (§27).

entity sync_fifo is
  generic (DATA_W : positive := 8; DEPTH_LOG2 : positive := 4);
  port (
    clk, rst    : in  std_logic;
    wr_en       : in  std_logic;
    din         : in  std_logic_vector(DATA_W-1 downto 0);
    full        : out std_logic;
    rd_en       : in  std_logic;
    dout        : out std_logic_vector(DATA_W-1 downto 0);
    empty       : out std_logic);
end entity;

architecture rtl of sync_fifo is
  constant DEPTH : positive := 2**DEPTH_LOG2;
  type mem_t is array (0 to DEPTH-1) of std_logic_vector(DATA_W-1 downto 0);
  signal mem : mem_t;
  signal wr_ptr, rd_ptr : unsigned(DEPTH_LOG2 downto 0) := (others => '0');   -- extra MSB for full/empty
begin
  full  <= '1' when (wr_ptr(DEPTH_LOG2) /= rd_ptr(DEPTH_LOG2)) and
                    (wr_ptr(DEPTH_LOG2-1 downto 0) = rd_ptr(DEPTH_LOG2-1 downto 0)) else '0';
  empty <= '1' when wr_ptr = rd_ptr else '0';

  process (clk)
  begin
    if rising_edge(clk) then
      if rst = '1' then
        wr_ptr <= (others => '0'); rd_ptr <= (others => '0');
      else
        if wr_en = '1' and full = '0' then
          mem(to_integer(wr_ptr(DEPTH_LOG2-1 downto 0))) <= din;
          wr_ptr <= wr_ptr + 1;
        end if;
      
…