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

-- Variable Size Delay Line
-- This entity is based on a single port RAM from which every clock cycle the old contents of an address 
-- are read out and updated with new contents. By incrementing the addresses in a pre-defined loop we 
-- have effectively a ring buffer that we can vary in size (up to the maximum capacity of the memory).
-- NOTE: Changing the 'delay' value may lead to glitches during the first "delay period", due to the
-- way the address generation is made. More specifically, these glitches happen when the 'delay' is 
-- increased (which is unavoidable), and when it is decreased while the momentarily counter value is 
-- the same as the new delay value [single cycle glitch].

entity delay_line is
        generic (
            DATA_WIDTH  : integer := 12;
            MAX_DELAY   : integer := 200;
            DELAY_WIDTH : integer := 8
        );
        port (
            clk         : in std_logic;
            reset       : in std_logic;
            delay       : in std_logic_vector(DELAY_WIDTH-1 downto 0);
            data_in     : in std_logic_vector(DATA_WIDTH-1 downto 0);
            data_out    : out std_logic_vector(DATA_WIDTH-1 downto 0)
        );
end entity;

architecture arch of delay_line is
    
    --*****COMPONENT DECLARATION*****
    component single_port_ram is
        generic (
            ADDR_WIDTH      : integer := 8;
            DATA_WIDTH      : integer := 12
        );
        port (
            clk         : in std_logic;
            addr        : in std_logic_vector(ADDR_WIDTH-1 downto 0);
            wen         : in std_logic;
            ren         : in std_logic;
            wr_data     : in std_logic_vector(DATA_WIDTH-1 downto 0);
            rd_data     : out std_logic_vector(DATA_WIDTH-1 downto 0)
        );
    end component;
    
    --*****SIGNAl DECLARATION*****
    signal cnt, cnt_next, cnt_max, cnt_max_next : integer range 0 to MAX_DELAY := 0;
    signal memory_out : std_logic_vector(DATA_WIDTH-1 downto 0) := (others => '0');
    
begin    
    
    --*****COMPONENT INSTANTIATION*****
    ram_inst : single_port_ram
        generic map (
            ADDR_WIDTH      => DELAY_WIDTH,
            DATA_WIDTH      => DATA_WIDTH
        )
        port map(
            clk         => clk,
            addr        => std_logic_vector(to_unsigned(cnt,DELAY_WIDTH)),
            wen         => '1',
            ren         => '1',
            wr_data     => data_in,
            rd_data     => memory_out
        );
    
    ctrl : process(all)
    begin
        -- DEFAULT VALUES
        cnt_next        <= cnt;
        cnt_max_next    <= cnt_max;
        
        if(to_integer(unsigned(delay)) = 0) then
            data_out <= data_in;
        else
            data_out <= memory_out;
            -- COUNT GENERATION
            cnt_max_next <= to_integer(unsigned(delay)) - 1;
            if (cnt >= cnt_max) then
                cnt_next    <= 0;
            else
                cnt_next    <= cnt + 1;
            end if;
        end if;
    end process;
    
    sync : process(clk)
    begin
        if rising_edge(clk) then
            if (reset = '1') then
                cnt     <= 0;
                cnt_max <= 0;
            else
                cnt     <= cnt_next;
                cnt_max <= cnt_max_next;
            end if;
        end if;
    end process;
    
end architecture;