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

-- Entity: LFO (Low Frequency Oscillator) - Alternative Implementation
-- Purpose: Applies effect to audio by modulating amplitude with a triangular wave
--          This is a simplified, single-process implementation compared to the pipelined version
--          Provides real-time audio amplitude modulation for musical effects
ENTITY LFO IS
    GENERIC (
        CHANNEL_LENGHT : INTEGER := 24;              -- Bit width of audio samples (24-bit signed)
        JOYSTICK_LENGHT : INTEGER := 10;             -- Bit width of joystick input (10-bit = 0-1023 range)
        CLK_PERIOD_NS : INTEGER := 10;               -- Clock period in nanoseconds (10ns = 100MHz)
        TRIANGULAR_COUNTER_LENGHT : INTEGER := 10    -- Triangular wave counter length (affects modulation depth)
    );
    PORT (
        -- Clock and Reset
        aclk : IN STD_LOGIC;        -- Main system clock
        aresetn : IN STD_LOGIC;     -- Active-low asynchronous reset

        -- LFO Control inputs
        lfo_period : IN STD_LOGIC_VECTOR(JOYSTICK_LENGHT - 1 DOWNTO 0);  -- Controls LFO frequency (joystick Y-axis)
        lfo_enable : IN STD_LOGIC;                                       -- Enable/bypass LFO effect

        -- Slave AXI Stream interface (audio input)
        s_axis_tvalid : IN STD_LOGIC;                                      -- Input data valid signal
        s_axis_tdata : IN STD_LOGIC_VECTOR(CHANNEL_LENGHT - 1 DOWNTO 0);   -- Audio sample input
        s_axis_tlast : IN STD_LOGIC;                                       -- Channel indicator (0=left, 1=right)
        s_axis_tready : OUT STD_LOGIC;                                     -- Ready to accept input data

        -- Master AXI Stream interface (audio output)
        m_axis_tvalid : OUT STD_LOGIC;                                     -- Output data valid signal
        m_axis_tdata : OUT STD_LOGIC_VECTOR(CHANNEL_LENGHT - 1 DOWNTO 0);  -- Modulated audio sample output
        m_axis_tlast : OUT STD_LOGIC;                                      -- Channel indicator passthrough
        m_axis_tready : IN STD_LOGIC                                       -- Downstream ready signal
    );
END ENTITY LFO;

ARCHITECTURE Behavioral OF LFO IS

    -- LFO timing configuration constants
    CONSTANT LFO_COUNTER_BASE_PERIOD_US : INTEGER := 1000; -- Base period: 1ms (1kHz base frequency)
    CONSTANT ADJUSTMENT_FACTOR : INTEGER := 90;             -- Frequency adjustment sensitivity (clock cycles per joystick unit)
    CONSTANT JSTK_CENTER_VALUE : INTEGER := 2 ** (JOYSTICK_LENGHT - 1); -- Joystick center position (512 for 10-bit)

    -- Calculate base clock cycles for 1ms period at current clock frequency
    CONSTANT LFO_COUNTER_BASE_CLK_CYCLES : INTEGER := LFO_COUNTER_BASE_PERIOD_US * 1000 / CLK_PERIOD_NS; -- 1ms = 100,000 clk cycles

    -- Calculate frequency range limits based on joystick range
    CONSTANT LFO_CLK_CYCLES_MIN : INTEGER := LFO_COUNTER_BASE_CLK_CYCLES - ADJUSTMENT_FACTOR * (2 ** (JOYSTICK_LENGHT - 1)); -- 53,920 clk cycles (faster)
    CONSTANT LFO_CLK_CYCLES_MAX : INTEGER := LFO_COUNTER_BASE_CLK_CYCLES + ADJUSTMENT_FACTOR * (2 ** (JOYSTICK_LENGHT - 1) - 1); -- 145,990 clk cycles (slower)

    -- LFO timing control signals
    SIGNAL step_clk_cycles_delta : INTEGER RANGE - 2 ** (JOYSTICK_LENGHT - 1) * ADJUSTMENT_FACTOR TO (2 ** (JOYSTICK_LENGHT - 1) - 1) * ADJUSTMENT_FACTOR := 0;
    SIGNAL step_clk_cycles : INTEGER RANGE LFO_CLK_CYCLES_MIN TO LFO_CLK_CYCLES_MAX := LFO_COUNTER_BASE_CLK_CYCLES;
    SIGNAL step_counter : NATURAL RANGE 0 TO LFO_CLK_CYCLES_MAX := 0;

    -- Triangular wave generation signals
    -- Note: Using signed counter with extra bit to handle full range calculations
    SIGNAL tri_counter : SIGNED(TRIANGULAR_COUNTER_LENGHT DOWNTO 0) := (OTHERS => '0');   -- Triangular wave amplitude
    SIGNAL direction_up : STD_LOGIC := '1';                                               -- Wave direction: '1' = ascending, '0' = descending

    -- AXI4-Stream control signals
    SIGNAL trigger : STD_LOGIC := '0';                     -- Trigger to indicate new processed data is ready
    SIGNAL s_axis_tlast_reg : STD_LOGIC := '0';            -- Registered version of tlast for output synchronization
    SIGNAL m_axis_tvalid_int : STD_LOGIC := '0';           -- Internal output valid signal

    -- Audio processing signal with extended width for multiplication
    -- Width accommodates: audio sample + triangular counter to prevent overflow
    SIGNAL m_axis_tdata_temp : SIGNED(CHANNEL_LENGHT + TRIANGULAR_COUNTER_LENGHT DOWNTO 0) := (OTHERS => '0');

BEGIN

    -- Output signal assignments with proper AXI4-Stream flow control
    m_axis_tvalid <= m_axis_tvalid_int;
    -- Input ready logic: Ready when downstream is ready OR no valid data pending, AND not in reset
    s_axis_tready <= (m_axis_tready OR NOT m_axis_tvalid_int) AND aresetn;

    -- Optimized single process for LFO timing and triangular waveform generation
    -- This process handles both the frequency control and wave shape generation
    triangular_wave_lfo_generator : PROCESS (aclk)
    BEGIN
        IF rising_edge(aclk) THEN

            IF aresetn = '0' THEN
                -- Reset LFO generator to initial state
                step_clk_cycles <= LFO_COUNTER_BASE_CLK_CYCLES;  -- Set to base frequency
                step_counter <= 0;                               -- Clear timing counter
                tri_counter <= (OTHERS => '0');                 -- Start triangular wave at zero
                direction_up <= '1';                            -- Start counting up

            ELSE
                -- Calculate LFO period based on joystick input
                -- Joystick mapping:
                -- 0-511: Slower than base frequency (longer period, lower frequency)
                -- 512: Base frequency (1kHz)
                -- 513-1023: Faster than base frequency (shorter period, higher frequency)
                step_clk_cycles_delta <= (to_integer(unsigned(lfo_period)) - JSTK_CENTER_VALUE);
                step_clk_cycles <= LFO_COUNTER_BASE_CLK_CYCLES - step_clk_cycles_delta * ADJUSTMENT_FACTOR;

                -- Generate triangular wave when LFO is enabled
                IF lfo_enable = '1' THEN

                    -- Clock divider: Update triangular wave at calculated rate
                    IF step_counter >= step_clk_cycles THEN
                        step_counter <= 0;  -- Reset counter for next period

                        -- Check for triangular wave direction changes at extremes
                        -- Note: Using (2^n - 2) and 1 instead of (2^n - 1) and 0 due to process signal assignment
                        IF tri_counter = (2 ** TRIANGULAR_COUNTER_LENGHT) - 2 THEN
                            direction_up <= '0';  -- Switch to descending at near-maximum

                        ELSIF tri_counter = 1 THEN
                            direction_up <= '1';  -- Switch to ascending at near-minimum

                        END IF;

                        -- Update triangular wave value based on current direction
                        -- This creates the classic triangular waveform shape
                        IF direction_up = '1' THEN
                            tri_counter <= tri_counter + 1;  -- Ascending: increment
                        ELSE
                            tri_counter <= tri_counter - 1;  -- Descending: decrement
                        END IF;

                    ELSE
                        step_counter <= step_counter + 1;  -- Continue counting towards next update

                    END IF;

                END IF;

            END IF;

        END IF;

    END PROCESS triangular_wave_lfo_generator;

    -- AXI4-Stream handshake logic and audio processing
    -- This process handles input/output data flow and applies the LFO modulation
    AXIS : PROCESS (aclk)
    BEGIN
        IF rising_edge(aclk) THEN

            IF aresetn = '0' THEN
                -- Reset AXI4-Stream interface and audio processing
                s_axis_tlast_reg <= '0';                  -- Clear registered channel indicator
                m_axis_tdata_temp <= (OTHERS => '0');     -- Clear temporary audio data
                m_axis_tvalid_int <= '0';                 -- No valid output data
                m_axis_tlast <= '0';                      -- Clear output channel indicator

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

                -- Data output logic: Send processed audio when trigger is active and output is available
                IF trigger = '1' AND (m_axis_tvalid_int = '0' OR m_axis_tready = '1') THEN
                    -- Scale down the multiplication result to original audio bit width
                    -- Right shift by TRIANGULAR_COUNTER_LENGHT effectively divides by 2^TRIANGULAR_COUNTER_LENGHT
                    -- This maintains proper audio amplitude after modulation
                    m_axis_tdata <= STD_LOGIC_VECTOR(
                        resize(
                            shift_right(
                                m_axis_tdata_temp,                -- Wide multiplication result
                                TRIANGULAR_COUNTER_LENGHT         -- Scale factor
                            ),
                            CHANNEL_LENGHT                        -- Final audio sample width
                        )
                    );
                    m_axis_tlast <= s_axis_tlast_reg;             -- Output registered channel indicator

                    m_axis_tvalid_int <= '1';                     -- Mark output as valid
                    trigger <= '0';                               -- Clear trigger - data has been output
                    
                END IF;

                -- Data input logic: Process new audio samples when available and output is ready
                IF s_axis_tvalid = '1' AND (m_axis_tready = '1' OR m_axis_tvalid_int = '0') THEN	
                    IF lfo_enable = '1' THEN
                        -- Apply LFO effect: multiply audio sample by triangular wave
                        -- This creates amplitude modulation (effect)
                        m_axis_tdata_temp <= signed(s_axis_tdata) * tri_counter;
                        s_axis_tlast_reg <= s_axis_tlast;         -- Register channel indicator

                    ELSE
                        -- LFO disabled: pass audio through unchanged but maintain bit width
                        -- Left shift compensates for the right shift that occurs during output
                        -- This ensures unity gain when LFO is bypassed
                        m_axis_tdata_temp <= shift_left(
                            resize(
                                signed(s_axis_tdata),             -- Convert input to signed
                                m_axis_tdata_temp'length          -- Extend to full processing width
                            ),
                            TRIANGULAR_COUNTER_LENGHT             -- Compensate for output scaling
                        );
                        s_axis_tlast_reg <= s_axis_tlast;         -- Register channel indicator

                    END IF;

                    trigger <= '1';  -- Set trigger to indicate new processed data is ready

                END IF;

            END IF;

        END IF;

    END PROCESS AXIS;

    -- LFO Implementation Summary:
    -- 1. Generates triangular wave at frequency controlled by joystick input
    -- 2. When enabled: multiplies audio samples by triangular wave (multiplier value range from 0 to 1)
    -- 3. When disabled: passes audio through unchanged (bypass mode)
    -- 4. Uses proper AXI4-Stream handshaking for real-time audio processing
    --
    -- Effect Characteristics:
    -- - Frequency range: Approximately 0.1Hz to 10Hz (typical for audio LFO)
    -- - Modulation depth: Controlled by TRIANGULAR_COUNTER_LENGHT generic
    -- - Waveform: Triangular (linear amplitude changes, smooth transitions)
    -- - Bypass capability: Clean audio passthrough when disabled

END ARCHITECTURE Behavioral;