LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;

-- Entity: volume_saturator
-- Purpose: Second stage of volume control pipeline - clips multiplication results to prevent overflow
--          Reduces bit width from extended multiplier output back to original audio format
--          Implements saturation (clipping) to prevent audio distortion from mathematical overflow
ENTITY volume_saturator IS
    GENERIC (
        TDATA_WIDTH : POSITIVE := 24;       -- Width of final audio data output (24-bit audio samples)
        VOLUME_WIDTH : POSITIVE := 10;      -- Width of volume control input (10-bit = 0-1023 range)
        VOLUME_STEP_2 : POSITIVE := 6;      -- Log2 of volume values per step (2^6 = 64 values per step)
        HIGHER_BOUND : INTEGER := 2 ** 15 - 1; -- Maximum positive value for saturation (inclusive)
        LOWER_BOUND : INTEGER := - 2 ** 15     -- Maximum negative value for saturation (inclusive)
    );
    PORT (
        -- Clock and reset signals
        aclk : IN STD_LOGIC;        -- Main clock input
        aresetn : IN STD_LOGIC;     -- Active-low asynchronous reset

        -- AXI4-Stream Slave Interface (Extended width input from multiplier)
        s_axis_tvalid : IN STD_LOGIC;                                                                           -- Input data valid signal
        s_axis_tdata : IN STD_LOGIC_VECTOR(TDATA_WIDTH - 1 + 2 ** (VOLUME_WIDTH - VOLUME_STEP_2 - 1) DOWNTO 0); -- Wide input data from multiplier
        s_axis_tlast : IN STD_LOGIC;                                                                            -- Channel indicator (0=left, 1=right)
        s_axis_tready : OUT STD_LOGIC;                                                                          -- Ready to accept input data

        -- AXI4-Stream Master Interface (Original width output for audio)
        m_axis_tvalid : OUT STD_LOGIC;                                     -- Output data valid signal
        m_axis_tdata : OUT STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0);     -- Saturated audio sample output
        m_axis_tlast : OUT STD_LOGIC;                                      -- Channel indicator passthrough
        m_axis_tready : IN STD_LOGIC                                       -- Downstream ready signal
    );
END volume_saturator;

ARCHITECTURE Behavioral OF volume_saturator IS

    -- Pre-calculated saturation bounds as STD_LOGIC_VECTORS for efficient comparison
    -- These constants define the valid range for audio samples after volume processing
    CONSTANT HIGHER_BOUND_VEC : STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0) := STD_LOGIC_VECTOR(to_signed(HIGHER_BOUND, TDATA_WIDTH));
    CONSTANT LOWER_BOUND_VEC : STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0) := STD_LOGIC_VECTOR(to_signed(LOWER_BOUND, TDATA_WIDTH));

    -- Internal AXI4-Stream control signal
    SIGNAL m_axis_tvalid_int : STD_LOGIC;

BEGIN

    -- Connect internal signals to output ports
    m_axis_tvalid <= m_axis_tvalid_int;
    
    -- Input ready logic: Ready when downstream is ready OR no valid data pending, AND not in reset
    -- This implements proper AXI4-Stream backpressure handling
    s_axis_tready <= (m_axis_tready OR NOT m_axis_tvalid_int) AND aresetn;

    -- Main saturation and data processing logic
    PROCESS (aclk)
    BEGIN

        IF rising_edge(aclk) THEN

            IF aresetn = '0' THEN
                -- Reset output interface
                m_axis_tvalid_int <= '0';  -- No valid output data during reset
                m_axis_tlast <= '0';       -- Clear channel indicator

            ELSE
                -- Normal operation

                -- Output handshake management:
                -- Clear valid flag when downstream accepts data
                IF m_axis_tready = '1' THEN
                    m_axis_tvalid_int <= '0';
                END IF;

                -- Data processing: Apply saturation when both input and output are ready
                IF s_axis_tvalid = '1' AND m_axis_tready = '1' THEN
                    
                    -- Saturation Logic:
                    -- Check if the wide input data exceeds the valid audio range
                    -- If so, clip (saturate) to the nearest valid value
                    -- This prevents overflow distortion while preserving audio quality
                    
                    IF signed(s_axis_tdata) > signed(HIGHER_BOUND_VEC) THEN
                        -- Positive overflow: Clip to maximum positive value
                        m_axis_tdata <= HIGHER_BOUND_VEC;

                    ELSIF signed(s_axis_tdata) < signed(LOWER_BOUND_VEC) THEN
                        -- Negative overflow: Clip to maximum negative value
                        m_axis_tdata <= LOWER_BOUND_VEC;

                    ELSE
                        -- Value within valid range: Resize to output width without clipping
                        -- This preserves the original audio quality when no overflow occurs
                        m_axis_tdata <= STD_LOGIC_VECTOR(resize(signed(s_axis_tdata), TDATA_WIDTH));
                        
                    END IF;

                    -- Set output control signals
                    m_axis_tvalid_int <= '1';        -- Mark output data as valid
                    m_axis_tlast <= s_axis_tlast;    -- Pass through channel indicator

                END IF;

            END IF;

        END IF;

    END PROCESS;

END Behavioral;