Circular Buffer Using SRAM

Author: Greg Nakatsui
Group: SysFX

In the code provided, a circular memory buffer was created using a 256Kbit SRAM chip organized as 32K 8-bit words.  The code could easily be adapted to accommodate a different chip size or configuration by altering the delay length, or memdata width.

The code works by proceeding through a set of steps to perform a read-write routine.  The steps are included in order to constrain the updating of the signals to a series of clock cycles.  Because the data and address pins are common for reads and writes, if the signals were changed concurrently the data would not be valid at the correct time.  As long as the setup times for the addressing and data are less than the clock period then the data will be valid.

Because we were using a 16-bit audio signal as data, each read and write consists of a set of two reads or writes, the first being the most significant byte and the second being the least significant byte.  The address is incremented between succesive reads and writes to successfully achieve this.

The data is written, starting on the first clock pulse, to the beginning of the memory.  The address is then incremented by the specified delay length and the data is read.  When the address gets to the end of the memory it is reset.  The result is that the read address follows the write address by a set amount and therefore the output is a delayed version of the input.  The input and its delayed version can then be mixed in the delay unit to create a delay, reverb or echo effect.

The final  two steps can be altered to account for varying clock frequencies.  The second last step keeps the process in an empty loop for a given number of clock cycles.  By keeping the process from reading and writing, the data can be prevented from updating until a new sample is available.  In this way, because the memory is faster than the input sampling, repeated data is not stored, only new sampled data is stored.

 

 

-------------------------------------------------
-- memory.vhd
--
-- Revised 2001/03/14
--

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;

package mem_pkg is

component memory is
      GENERIC (delay_ln : integer := 16384);                       -- length of delay
    PORT (
      memdata : buffer std_logic_vector(7 downto 0);       -- data from RAM
      clock : IN std_logic;
      reset : IN std_logic;
      Adatain : IN std_logic_vector(15 downto 0);        -- audio data from delay unit

      wrn : OUT std_logic;                                            -- write enable to RAM
      Adataout : OUT std_logic_vector(15 downto 0);        -- audio data to delay unit
      address : OUT std_logic_vector(14 downto 0)         -- address to RAM
      );
end component memory;

end package mem_pkg;


library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;



entity memory is
      GENERIC (delay_ln : integer := 16384);                       -- length of delay
    PORT (
      memdata : buffer std_logic_vector(7 downto 0);       -- data from RAM
      clock : IN std_logic;
      reset : IN std_logic;
      Adatain : IN std_logic_vector(15 downto 0);        -- audio data from delay unit

      wrn : OUT std_logic;                                            -- write enable to RAM
      Adataout : OUT std_logic_vector(15 downto 0);        -- audio data to delay unit
      address : OUT std_logic_vector(14 downto 0)         -- address to RAM
      );
end entity memory;



architecture memory of memory is

signal address1, address2, address1inc, address2inc : std_logic_vector(14 downto 0);
signal data_temp : std_logic_vector(15 downto 0);
signal step : std_logic_vector(3 downto 0);

begin

-------------------------------------------------------------------------------------
memloop: process is

begin
     
      wait until rising_edge(clock);
      if ( reset = '1' ) then                                    -- initialize memory addresses
            address1 <= B"000000000000000";
            address2 <= address1 + delay_ln;
            address1inc <= address1 + 1;
            address2inc <= address1 + delay_ln + 1;

            wrn <= '1';
            step <= B"0000";

-- check if either address pointer is at the end of memory
 
      elsif address1 = B"100000000000000" then
            address1 <= B"000000000000000";
           
--    elsif address2 = B"100000000000000" then
--          address2 <= B"000000000000000";


-- write incoming data to memory
     
      elsif step = B"0000" then                              -- initialize memory addresses
            address <= address1;
            address1inc <= address1 + 1;
            address2 <= address1 + delay_ln;
            address2inc <= address1 + delay_ln + 1;
           
            wrn <= '0';                                            -- enable write
            step <= B"0001";                               -- increment to step 1
           
      elsif step = B"0001" then
            memdata <= Adatain(15 downto 8);        -- write to MSB of data to RAM
           
            step <= B"0010";                               -- increment to step 2

      elsif step = B"0010" then
            wrn <= '1';                                            -- disable write
           
            step <= B"0011";                               -- increment to step 3
     
      elsif step = B"0011" then
            address <= address1inc;                              -- increment address to LSB
            wrn <= '0';                                            -- enable write
           
            step <= B"0100";                               -- increment to step 4

      elsif step = B"0100" then
            memdata <= Adatain(7 downto 0);              -- write LSB of data to RAM
           
            step <= B"0101";                               -- increment to step 5

      elsif step = B"0101" then
            wrn <= '1';                                            -- disable write

            step <= B"0110";                               -- increment to step 6


-- read data from memory for output

      elsif step = B"0110" then
            address <= address2;                           -- set address to delayed value

            step <= B"0111";                               -- increment to step 7                             

      elsif step = B"0111" then
            data_temp(15 downto 8) <= memdata;          -- read MSB from RAM
     
            step <= B"1000";                               -- increment to step 8
           
      elsif step = B"1000" then
            address <= address2inc;                              -- increment addres to LSB
           
            step <= B"1001";                               -- increment to step 9
           
      elsif step = B"1001" then
            data_temp(7 downto 0) <= memdata;          -- read LSB from RAM
           
            Adataout <= data_temp;                          -- send audio data out
           
            step <= B"1010";                               -- increment to step 10
           
      elsif ((step > B"1001") AND (step < B"1111")) then
            step <= step + 1;                                    -- loop for clock cycles
     
      elsif step = B"1111" then
            address1 <= address1inc + 1;              -- increment addresses
--          address2 <= address2inc + 1;

            step <= B"0000";                               -- reset steps
           
      end if;
     
end process memloop;


end memory;