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;