----------------------------------------------------------------------------------
-- Testbench for volume_multiplier
----------------------------------------------------------------------------------
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;

ENTITY tb_volume_multiplier IS
END tb_volume_multiplier;

ARCHITECTURE Behavioral OF tb_volume_multiplier IS

    CONSTANT TDATA_WIDTH   : POSITIVE := 24;
    CONSTANT VOLUME_WIDTH  : POSITIVE := 10;
    CONSTANT VOLUME_STEP_2 : POSITIVE := 6;
    CONSTANT N_SAMPLES     : INTEGER  := 8;
    CONSTANT N_VOLUMES    : INTEGER  := 10;

    -- Output width calculation (as in DUT)
    CONSTANT TDATA_OUT_WIDTH : INTEGER := TDATA_WIDTH - 1 + 2 ** (VOLUME_WIDTH - VOLUME_STEP_2 - 1) + 1;

    COMPONENT volume_multiplier IS
        GENERIC (
            TDATA_WIDTH   : POSITIVE := 24;
            VOLUME_WIDTH  : POSITIVE := 10;
            VOLUME_STEP_2 : POSITIVE := 6
        );
        PORT (
            aclk           : IN  STD_LOGIC;
            aresetn        : IN  STD_LOGIC;
            s_axis_tvalid  : IN  STD_LOGIC;
            s_axis_tdata   : IN  STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0);
            s_axis_tlast   : IN  STD_LOGIC;
            s_axis_tready  : OUT STD_LOGIC;
            m_axis_tvalid  : OUT STD_LOGIC;
            m_axis_tdata   : OUT STD_LOGIC_VECTOR(TDATA_OUT_WIDTH - 1 DOWNTO 0);
            m_axis_tlast   : OUT STD_LOGIC;
            m_axis_tready  : IN  STD_LOGIC;
            volume         : IN  STD_LOGIC_VECTOR(VOLUME_WIDTH - 1 DOWNTO 0)
        );
    END COMPONENT;

    SIGNAL aclk          : STD_LOGIC := '0';
    SIGNAL aresetn       : STD_LOGIC := '0';
    SIGNAL s_axis_tvalid : STD_LOGIC := '0';
    SIGNAL s_axis_tdata  : STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0) := (OTHERS => '0');
    SIGNAL s_axis_tlast  : STD_LOGIC := '0';
    SIGNAL s_axis_tready : STD_LOGIC;
    SIGNAL m_axis_tvalid : STD_LOGIC;
    SIGNAL m_axis_tdata  : STD_LOGIC_VECTOR(TDATA_OUT_WIDTH - 1 DOWNTO 0);
    SIGNAL m_axis_tlast  : STD_LOGIC;
    SIGNAL m_axis_tready : STD_LOGIC := '1';
    SIGNAL volume        : STD_LOGIC_VECTOR(VOLUME_WIDTH - 1 DOWNTO 0) := (OTHERS => '0');

    -- Test input samples
    TYPE sample_mem_type IS ARRAY(0 TO N_SAMPLES-1) OF STD_LOGIC_VECTOR(TDATA_WIDTH-1 DOWNTO 0);
    SIGNAL sample_mem : sample_mem_type := (
        x"000100", -- +256
        x"FFFE00", -- -512
        x"000001", -- +1
        x"FFFFFF", -- -1 (2's comp)
        x"7FFFFF", -- max positive
        x"800000", -- max negative
        x"000A00", -- +2560
        x"FFF600"  -- -2560
    );
    
    -- Vettore di memoria per i valori di volume
    TYPE volume_mem_type IS ARRAY(0 TO N_VOLUMES-1) OF STD_LOGIC_VECTOR(VOLUME_WIDTH-1 DOWNTO 0);
    SIGNAL volume_mem : volume_mem_type := (
        std_logic_vector(to_unsigned(0, VOLUME_WIDTH)),   -- 0.25x (forte attenuazione)
        std_logic_vector(to_unsigned(64, VOLUME_WIDTH)),   -- 0.375x (attenuazione media)
        std_logic_vector(to_unsigned(479, VOLUME_WIDTH)),   -- 0.4375x (leggera attenuazione)
        std_logic_vector(to_unsigned(480, VOLUME_WIDTH)),   -- 0.5x (volume neutro)
        std_logic_vector(to_unsigned(513, VOLUME_WIDTH)),   -- Circa 0.5x (volume neutro)
        std_logic_vector(to_unsigned(576, VOLUME_WIDTH)),   -- 0.5625x (leggero aumento)
        std_logic_vector(to_unsigned(640, VOLUME_WIDTH)),   -- 0.625x (aumento medio)
        std_logic_vector(to_unsigned(768, VOLUME_WIDTH)),   -- 0.75x (aumento forte)
        std_logic_vector(to_unsigned(896, VOLUME_WIDTH)),   -- 0.875x (aumento molto forte)
        std_logic_vector(to_unsigned(1023, VOLUME_WIDTH))   -- 1x (massimo volume)
    );

BEGIN

    -- Clock generation
    aclk <= NOT aclk AFTER 5 ns;

    -- DUT instantiation
    uut: volume_multiplier
    GENERIC MAP (
        TDATA_WIDTH   => TDATA_WIDTH,
        VOLUME_WIDTH  => VOLUME_WIDTH,
        VOLUME_STEP_2 => VOLUME_STEP_2
    )
    PORT MAP (
        aclk           => aclk,
        aresetn        => aresetn,
        s_axis_tvalid  => s_axis_tvalid,
        s_axis_tdata   => s_axis_tdata,
        s_axis_tlast   => s_axis_tlast,
        s_axis_tready  => s_axis_tready,
        m_axis_tvalid  => m_axis_tvalid,
        m_axis_tdata   => m_axis_tdata,
        m_axis_tlast   => m_axis_tlast,
        m_axis_tready  => m_axis_tready,
        volume         => volume
    );

    -- Stimulus process
    stimulus : PROCESS
    BEGIN
        -- Reset
        WAIT FOR 10 ns;
        aresetn <= '1';
        WAIT UNTIL rising_edge(aclk);

        -- Set volume to mid (no gain/loss)
        volume <= volume_mem(7);
        WAIT UNTIL rising_edge(aclk);

        -- Send all samples
        FOR i IN 0 TO N_SAMPLES-1 LOOP
            s_axis_tdata  <= sample_mem(i);
            s_axis_tvalid <= '1';
            IF i = N_SAMPLES-1 THEN
                s_axis_tlast <= '1';
            ELSE
                s_axis_tlast <= '0';
            END IF;
            -- Wait for handshake
            WAIT UNTIL rising_edge(aclk);
            WHILE s_axis_tready = '0' LOOP
                WAIT UNTIL rising_edge(aclk);
            END LOOP;
        END LOOP;
        s_axis_tvalid <= '0';
        s_axis_tlast  <= '0';

        -- Change volume (attenuate)
        WAIT FOR 20 ns;
        volume <= volume_mem(1);

        -- Send one more sample
        WAIT UNTIL rising_edge(aclk);
        s_axis_tdata  <= x"000100";
        s_axis_tvalid <= '1';
        s_axis_tlast  <= '1';
        WAIT UNTIL rising_edge(aclk);
        WHILE s_axis_tready = '0' LOOP
            WAIT UNTIL rising_edge(aclk);
        END LOOP;
        s_axis_tvalid <= '0';
        s_axis_tlast  <= '0';

        FOR i IN 2 TO N_VOLUMES-1 LOOP
            WAIT FOR 20 ns;
            volume <= volume_mem(i);
            WAIT UNTIL rising_edge(aclk);
            END LOOP;

        -- Wait and finish
        WAIT FOR 100 ns;
        WAIT;
    END PROCESS;

    -- Optionally, block m_axis_tready to test backpressure
    PROCESS
    BEGIN
        WAIT FOR 60 ns;
        WAIT UNTIL rising_edge(aclk);
        m_axis_tready <= '0';
        WAIT FOR 20 ns;
        WAIT UNTIL rising_edge(aclk);
        m_axis_tready <= '1';
        WAIT;
    END PROCESS;

END Behavioral;