From c66c218f658e5492afbbc80f6765eaea454018de Mon Sep 17 00:00:00 2001 From: Davide Date: Sat, 31 May 2025 19:07:49 +0200 Subject: [PATCH] Update: - LFO <-> LFO_1 - Effect Selector - modify led_level_controller (1) --- LAB3/src/LFO.vhd | 323 ++++++++++++++---------------- LAB3/src/LFO_1.vhd | 313 ++++++++++++++++------------- LAB3/src/effect_selector.vhd | 33 +-- LAB3/src/led_level_controller.vhd | 34 +--- 4 files changed, 344 insertions(+), 359 deletions(-) diff --git a/LAB3/src/LFO.vhd b/LAB3/src/LFO.vhd index 011d3ce..5c2812b 100644 --- a/LAB3/src/LFO.vhd +++ b/LAB3/src/LFO.vhd @@ -2,16 +2,16 @@ LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.NUMERIC_STD.ALL; --- Entity: LFO (Low Frequency Oscillator) +-- Entity: LFO (Low Frequency Oscillator) - Alternative Implementation -- Purpose: Applies tremolo effect to audio by modulating amplitude with a triangular wave --- Creates classic audio effects like vibrato, tremolo, and amplitude modulation --- Implements a 3-stage pipeline for efficient real-time audio processing +-- 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 -- Bit width of triangular wave counter (affects modulation depth) + TRIANGULAR_COUNTER_LENGHT : INTEGER := 10 -- Triangular wave counter length (affects modulation depth) ); PORT ( -- Clock and Reset @@ -38,219 +38,188 @@ END ENTITY LFO; ARCHITECTURE Behavioral OF LFO IS - -- Constants for LFO timing configuration - CONSTANT BASE_PERIOD_MICROSECONDS : INTEGER := 1000; -- Base period: 1ms (1kHz base frequency) - CONSTANT FREQUENCY_ADJUSTMENT_FACTOR : INTEGER := 90; -- Frequency adjustment sensitivity (clock cycles per joystick unit) - CONSTANT JOYSTICK_CENTER_VALUE : INTEGER := 2 ** (JOYSTICK_LENGHT - 1); -- Joystick center position (512 for 10-bit) + -- 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 BASE_CLOCK_CYCLES : INTEGER := BASE_PERIOD_MICROSECONDS * 1000 / CLK_PERIOD_NS; + 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 - -- Minimum frequency (fastest LFO): occurs when joystick is at minimum position - CONSTANT MIN_CLOCK_CYCLES : INTEGER := BASE_CLOCK_CYCLES - FREQUENCY_ADJUSTMENT_FACTOR * (2 ** (JOYSTICK_LENGHT - 1)); - -- Maximum frequency (slowest LFO): occurs when joystick is at maximum position - CONSTANT MAX_CLOCK_CYCLES : INTEGER := BASE_CLOCK_CYCLES + FREQUENCY_ADJUSTMENT_FACTOR * (2 ** (JOYSTICK_LENGHT - 1) - 1); + 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) - -- Internal signals for LFO control - -- Period adjustment based on joystick input (positive = slower, negative = faster) - SIGNAL period_adjustment_delta : INTEGER RANGE - 2 ** (JOYSTICK_LENGHT - 1) * FREQUENCY_ADJUSTMENT_FACTOR - TO (2 ** (JOYSTICK_LENGHT - 1) - 1) * FREQUENCY_ADJUSTMENT_FACTOR := 0; - SIGNAL current_period_cycles : INTEGER RANGE MIN_CLOCK_CYCLES TO MAX_CLOCK_CYCLES := BASE_CLOCK_CYCLES; + -- 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; - -- Pipeline stage 1 registers - Input processing and period calculation - SIGNAL audio_data_stage1 : STD_LOGIC_VECTOR(CHANNEL_LENGHT - 1 DOWNTO 0) := (OTHERS => '0'); -- Registered audio input - SIGNAL enable_flag_stage1 : STD_LOGIC := '0'; -- Registered LFO enable - SIGNAL valid_flag_stage1 : STD_LOGIC := '0'; -- Valid data in stage 1 - SIGNAL last_flag_stage1 : STD_LOGIC := '0'; -- Registered channel indicator + -- 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 - -- Pipeline stage 2 registers - Triangular wave generation - SIGNAL triangular_wave_value : unsigned(TRIANGULAR_COUNTER_LENGHT - 1 DOWNTO 0) := (OTHERS => '0'); -- Current triangular wave amplitude - SIGNAL wave_direction_up : STD_LOGIC := '1'; -- Triangle wave direction: '1' = ascending, '0' = descending - SIGNAL timing_counter : NATURAL RANGE 0 TO MAX_CLOCK_CYCLES := 0; -- Clock cycle counter for LFO timing - SIGNAL enable_flag_stage2 : STD_LOGIC := '0'; -- LFO enable flag for stage 2 - SIGNAL valid_flag_stage2 : STD_LOGIC := '0'; -- Valid data in stage 2 - SIGNAL last_flag_stage2 : STD_LOGIC := '0'; -- Channel indicator for stage 2 - SIGNAL audio_data_stage2 : STD_LOGIC_VECTOR(CHANNEL_LENGHT - 1 DOWNTO 0) := (OTHERS => '0'); -- Audio data for stage 2 + -- 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 - -- Pipeline stage 3 registers - Modulation and output - -- Extended width to accommodate multiplication result before scaling - SIGNAL multiplication_result : STD_LOGIC_VECTOR(CHANNEL_LENGHT + TRIANGULAR_COUNTER_LENGHT - 1 DOWNTO 0) := (OTHERS => '0'); - - -- Internal AXI4-Stream control signals - SIGNAL master_valid_internal : STD_LOGIC := '0'; -- Internal output valid signal - SIGNAL slave_ready_internal : STD_LOGIC := '1'; -- Internal input ready 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 - -- Direct connection: tlast passes through unchanged (maintains channel timing) - m_axis_tlast <= last_flag_stage1; + -- 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; - -- Pipeline stage 1: Input registration and LFO period calculation - -- This stage captures input data and calculates the LFO period based on joystick position - input_processing_stage : PROCESS (aclk) + -- 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 all stage 1 registers to safe initial states - audio_data_stage1 <= (OTHERS => '0'); -- Clear audio data - current_period_cycles <= BASE_CLOCK_CYCLES; -- Set to base frequency - enable_flag_stage1 <= '0'; -- Disable LFO - valid_flag_stage1 <= '0'; -- No valid data - last_flag_stage1 <= '0'; -- Clear channel indicator + -- 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 y-axis input + -- Calculate LFO period based on joystick input -- Joystick mapping: - -- 0-511: Faster than base frequency (shorter period) + -- 0-511: Faster than base frequency (shorter period, higher frequency) -- 512: Base frequency (1kHz) - -- 513-1023: Slower than base frequency (longer period) - period_adjustment_delta <= (to_integer(unsigned(lfo_period)) - JOYSTICK_CENTER_VALUE) * FREQUENCY_ADJUSTMENT_FACTOR; - current_period_cycles <= BASE_CLOCK_CYCLES - period_adjustment_delta; - - -- AXI4-Stream handshake: accept new data when both valid and ready - IF s_axis_tvalid = '1' AND slave_ready_internal = '1' THEN - audio_data_stage1 <= s_axis_tdata; -- Register input audio sample - enable_flag_stage1 <= lfo_enable; -- Register enable control - valid_flag_stage1 <= '1'; -- Mark data as valid for next stage - last_flag_stage1 <= s_axis_tlast; -- Register channel boundary signal - ELSE - valid_flag_stage1 <= '0'; -- No valid data to pass to next stage - END IF; - END IF; - END IF; - END PROCESS input_processing_stage; - - -- Pipeline stage 2: Triangular wave generation - -- This stage generates the triangular wave that will modulate the audio amplitude - triangular_wave_generator : PROCESS (aclk) - BEGIN - IF rising_edge(aclk) THEN - IF aresetn = '0' THEN - -- Reset triangular wave generator to initial state - timing_counter <= 0; -- Clear timing counter - triangular_wave_value <= (OTHERS => '0'); -- Start at zero amplitude - wave_direction_up <= '1'; -- Start counting up - enable_flag_stage2 <= '0'; -- Disable LFO - valid_flag_stage2 <= '0'; -- No valid data - last_flag_stage2 <= '0'; -- Clear channel indicator - audio_data_stage2 <= (OTHERS => '0'); -- Clear audio data - ELSE - -- Pass through pipeline registers from stage 1 to stage 2 - enable_flag_stage2 <= enable_flag_stage1; -- Forward enable flag - valid_flag_stage2 <= valid_flag_stage1; -- Forward valid flag - last_flag_stage2 <= last_flag_stage1; -- Forward channel indicator - audio_data_stage2 <= audio_data_stage1; -- Forward audio data + -- 513-1023: Slower than base frequency (longer period, lower frequency) + step_clk_cycles_delta <= (to_integer(unsigned(lfo_period)) - JSTK_CENTER_VALUE) * ADJUSTMENT_FACTOR; + step_clk_cycles <= LFO_COUNTER_BASE_CLK_CYCLES - step_clk_cycles_delta; -- Generate triangular wave when LFO is enabled - IF enable_flag_stage1 = '1' THEN - -- Clock divider: update triangular counter based on calculated period - IF timing_counter < current_period_cycles THEN - timing_counter <= timing_counter + 1; -- Count towards period target - ELSE - timing_counter <= 0; -- Reset counter for next period + 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 to avoid edge cases + 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 - -- Update triangular wave: count up or down based on current direction - -- This creates the classic triangular waveform shape - IF wave_direction_up = '1' THEN - -- Ascending phase: check if we reached maximum amplitude - IF triangular_wave_value = (2 ** TRIANGULAR_COUNTER_LENGHT) - 1 THEN - wave_direction_up <= '0'; -- Switch to descending phase - triangular_wave_value <= triangular_wave_value - 1; -- Start decreasing - ELSE - triangular_wave_value <= triangular_wave_value + 1; -- Continue increasing - END IF; - ELSE - -- Descending phase: check if we reached minimum amplitude - IF triangular_wave_value = 0 THEN - wave_direction_up <= '1'; -- Switch to ascending phase - triangular_wave_value <= triangular_wave_value + 1; -- Start increasing - ELSE - triangular_wave_value <= triangular_wave_value - 1; -- Continue decreasing - END IF; END IF; - END IF; - ELSE - -- LFO disabled: reset triangular wave generator to idle state - timing_counter <= 0; -- Clear timing counter - triangular_wave_value <= (OTHERS => '0'); -- Reset to zero amplitude - wave_direction_up <= '1'; -- Reset to ascending direction - END IF; - END IF; - END IF; - END PROCESS triangular_wave_generator; - -- Pipeline stage 3: Audio modulation and output control - -- This stage applies the LFO effect by multiplying audio samples with the triangular wave - modulation_and_output : PROCESS (aclk) + -- 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 output stage to safe initial state - m_axis_tdata <= (OTHERS => '0'); -- Clear output data - master_valid_internal <= '0'; -- No valid output - slave_ready_internal <= '1'; -- Ready to accept input + -- 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 flow control: handle backpressure from downstream modules - IF master_valid_internal = '1' AND m_axis_tready = '0' THEN - -- Downstream not ready: maintain current output valid state - -- This implements proper AXI4-Stream backpressure handling - master_valid_internal <= '1'; - ELSIF valid_flag_stage2 = '1' THEN - -- New data available from stage 2: apply LFO effect or bypass - IF enable_flag_stage2 = '1' THEN + -- 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 tremolo effect: multiply audio sample by triangular wave -- This creates amplitude modulation (tremolo effect) - multiplication_result <= STD_LOGIC_VECTOR( - resize( - signed(audio_data_stage2) * signed('0' & triangular_wave_value), - multiplication_result'length - ) - ); - -- Scale down result by removing lower bits (equivalent to division by 2^TRIANGULAR_COUNTER_LENGHT) - -- This maintains proper audio amplitude range after multiplication - m_axis_tdata <= multiplication_result(multiplication_result'high DOWNTO TRIANGULAR_COUNTER_LENGHT); + 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 (bypass mode) - -- This allows seamless switching between effect and clean audio - m_axis_tdata <= audio_data_stage2; + -- 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; - master_valid_internal <= '1'; -- Mark output as valid - ELSE - -- No new data available: clear output valid flag - master_valid_internal <= '0'; + + trigger <= '1'; -- Set trigger to indicate new processed data is ready + END IF; - -- AXI4-Stream ready signal management for proper flow control - IF master_valid_internal = '1' AND m_axis_tready = '1' THEN - -- Successful output handshake: ready for new input data - slave_ready_internal <= '1'; - ELSIF s_axis_tvalid = '1' AND slave_ready_internal = '1' THEN - -- Accepted new input: not ready until current output is consumed - -- This prevents data loss in the pipeline - slave_ready_internal <= '0'; - END IF; END IF; + END IF; - END PROCESS modulation_and_output; - -- Output signal assignments - s_axis_tready <= slave_ready_internal; -- Connect internal ready to output port - m_axis_tvalid <= master_valid_internal; -- Connect internal valid to output port + END PROCESS AXIS; - -- LFO Effect Summary: - -- 1. Stage 1: Calculates LFO frequency based on joystick position - -- 2. Stage 2: Generates triangular wave at calculated frequency - -- 3. Stage 3: Multiplies audio samples by triangular wave (tremolo effect) + -- LFO Implementation Summary: + -- 1. Generates triangular wave at frequency controlled by joystick input + -- 2. When enabled: multiplies audio samples by triangular wave (tremolo effect) + -- 3. When disabled: passes audio through unchanged (bypass mode) + -- 4. Uses proper AXI4-Stream handshaking for real-time audio processing -- - -- Audio Effect Characteristics: - -- - Tremolo: Periodic amplitude modulation creates "shaking" sound + -- Tremolo 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 - -- - -- Pipeline Benefits: - -- - Maintains real-time audio processing with no dropouts - -- - Allows complex calculations without affecting audio timing - -- - Provides proper AXI4-Stream flow control and backpressure handling END ARCHITECTURE Behavioral; \ No newline at end of file diff --git a/LAB3/src/LFO_1.vhd b/LAB3/src/LFO_1.vhd index c7b6ec0..d04411f 100644 --- a/LAB3/src/LFO_1.vhd +++ b/LAB3/src/LFO_1.vhd @@ -2,16 +2,16 @@ LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.NUMERIC_STD.ALL; --- Entity: LFO_1 (Low Frequency Oscillator) - Alternative Implementation +-- Entity: LFO_1 (Low Frequency Oscillator) -- Purpose: Applies tremolo 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 +-- Creates classic audio effects like vibrato, tremolo, and amplitude modulation +-- Implements a 3-stage pipeline for efficient real-time audio processing ENTITY LFO_1 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) + TRIANGULAR_COUNTER_LENGHT : INTEGER := 10 -- Bit width of triangular wave counter (affects modulation depth) ); PORT ( -- Clock and Reset @@ -38,188 +38,219 @@ END ENTITY LFO_1; ARCHITECTURE Behavioral OF LFO_1 IS - -- LFO_1 timing configuration constants - CONSTANT LFO_1_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) + -- Constants for LFO_1 timing configuration + CONSTANT BASE_PERIOD_MICROSECONDS : INTEGER := 1000; -- Base period: 1ms (1kHz base frequency) + CONSTANT FREQUENCY_ADJUSTMENT_FACTOR : INTEGER := 90; -- Frequency adjustment sensitivity (clock cycles per joystick unit) + CONSTANT JOYSTICK_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_1_COUNTER_BASE_CLK_CYCLES : INTEGER := LFO_1_COUNTER_BASE_PERIOD_US * 1000 / CLK_PERIOD_NS; -- 1ms = 100,000 clk cycles + CONSTANT BASE_CLOCK_CYCLES : INTEGER := BASE_PERIOD_MICROSECONDS * 1000 / CLK_PERIOD_NS; -- Calculate frequency range limits based on joystick range - CONSTANT LFO_1_CLK_CYCLES_MIN : INTEGER := LFO_1_COUNTER_BASE_CLK_CYCLES - ADJUSTMENT_FACTOR * (2 ** (JOYSTICK_LENGHT - 1)); -- 53,920 clk cycles (faster) - CONSTANT LFO_1_CLK_CYCLES_MAX : INTEGER := LFO_1_COUNTER_BASE_CLK_CYCLES + ADJUSTMENT_FACTOR * (2 ** (JOYSTICK_LENGHT - 1) - 1); -- 145,990 clk cycles (slower) + -- Minimum frequency (fastest LFO_1): occurs when joystick is at minimum position + CONSTANT MIN_CLOCK_CYCLES : INTEGER := BASE_CLOCK_CYCLES - FREQUENCY_ADJUSTMENT_FACTOR * (2 ** (JOYSTICK_LENGHT - 1)); + -- Maximum frequency (slowest LFO_1): occurs when joystick is at maximum position + CONSTANT MAX_CLOCK_CYCLES : INTEGER := BASE_CLOCK_CYCLES + FREQUENCY_ADJUSTMENT_FACTOR * (2 ** (JOYSTICK_LENGHT - 1) - 1); - -- LFO_1 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_1_CLK_CYCLES_MIN TO LFO_1_CLK_CYCLES_MAX := LFO_1_COUNTER_BASE_CLK_CYCLES; - SIGNAL step_counter : NATURAL RANGE 0 TO LFO_1_CLK_CYCLES_MAX := 0; + -- Internal signals for LFO_1 control + -- Period adjustment based on joystick input (positive = slower, negative = faster) + SIGNAL period_adjustment_delta : INTEGER RANGE - 2 ** (JOYSTICK_LENGHT - 1) * FREQUENCY_ADJUSTMENT_FACTOR + TO (2 ** (JOYSTICK_LENGHT - 1) - 1) * FREQUENCY_ADJUSTMENT_FACTOR := 0; + SIGNAL current_period_cycles : INTEGER RANGE MIN_CLOCK_CYCLES TO MAX_CLOCK_CYCLES := BASE_CLOCK_CYCLES; - -- 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 + -- Pipeline stage 1 registers - Input processing and period calculation + SIGNAL audio_data_stage1 : STD_LOGIC_VECTOR(CHANNEL_LENGHT - 1 DOWNTO 0) := (OTHERS => '0'); -- Registered audio input + SIGNAL enable_flag_stage1 : STD_LOGIC := '0'; -- Registered LFO_1 enable + SIGNAL valid_flag_stage1 : STD_LOGIC := '0'; -- Valid data in stage 1 + SIGNAL last_flag_stage1 : STD_LOGIC := '0'; -- Registered channel indicator - -- 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 + -- Pipeline stage 2 registers - Triangular wave generation + SIGNAL triangular_wave_value : unsigned(TRIANGULAR_COUNTER_LENGHT - 1 DOWNTO 0) := (OTHERS => '0'); -- Current triangular wave amplitude + SIGNAL wave_direction_up : STD_LOGIC := '1'; -- Triangle wave direction: '1' = ascending, '0' = descending + SIGNAL timing_counter : NATURAL RANGE 0 TO MAX_CLOCK_CYCLES := 0; -- Clock cycle counter for LFO_1 timing + SIGNAL enable_flag_stage2 : STD_LOGIC := '0'; -- LFO_1 enable flag for stage 2 + SIGNAL valid_flag_stage2 : STD_LOGIC := '0'; -- Valid data in stage 2 + SIGNAL last_flag_stage2 : STD_LOGIC := '0'; -- Channel indicator for stage 2 + SIGNAL audio_data_stage2 : STD_LOGIC_VECTOR(CHANNEL_LENGHT - 1 DOWNTO 0) := (OTHERS => '0'); -- Audio data for stage 2 - -- 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'); + -- Pipeline stage 3 registers - Modulation and output + -- Extended width to accommodate multiplication result before scaling + SIGNAL multiplication_result : STD_LOGIC_VECTOR(CHANNEL_LENGHT + TRIANGULAR_COUNTER_LENGHT - 1 DOWNTO 0) := (OTHERS => '0'); + + -- Internal AXI4-Stream control signals + SIGNAL master_valid_internal : STD_LOGIC := '0'; -- Internal output valid signal + SIGNAL slave_ready_internal : STD_LOGIC := '1'; -- Internal input ready signal 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; + -- Direct connection: tlast passes through unchanged (maintains channel timing) + m_axis_tlast <= last_flag_stage1; - -- Optimized single process for LFO_1 timing and triangular waveform generation - -- This process handles both the frequency control and wave shape generation - triangular_wave_lfo_generator : PROCESS (aclk) + -- Pipeline stage 1: Input registration and LFO_1 period calculation + -- This stage captures input data and calculates the LFO_1 period based on joystick position + input_processing_stage : PROCESS (aclk) BEGIN IF rising_edge(aclk) THEN - IF aresetn = '0' THEN - -- Reset LFO_1 generator to initial state - step_clk_cycles <= LFO_1_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 - + -- Reset all stage 1 registers to safe initial states + audio_data_stage1 <= (OTHERS => '0'); -- Clear audio data + current_period_cycles <= BASE_CLOCK_CYCLES; -- Set to base frequency + enable_flag_stage1 <= '0'; -- Disable LFO_1 + valid_flag_stage1 <= '0'; -- No valid data + last_flag_stage1 <= '0'; -- Clear channel indicator ELSE - -- Calculate LFO_1 period based on joystick input + -- Calculate LFO_1 period based on joystick y-axis input -- Joystick mapping: - -- 0-511: Faster than base frequency (shorter period, higher frequency) + -- 0-511: Faster than base frequency (shorter period) -- 512: Base frequency (1kHz) - -- 513-1023: Slower than base frequency (longer period, lower frequency) - step_clk_cycles_delta <= (to_integer(unsigned(lfo_period)) - JSTK_CENTER_VALUE) * ADJUSTMENT_FACTOR; - step_clk_cycles <= LFO_1_COUNTER_BASE_CLK_CYCLES - step_clk_cycles_delta; + -- 513-1023: Slower than base frequency (longer period) + period_adjustment_delta <= (to_integer(unsigned(lfo_period)) - JOYSTICK_CENTER_VALUE) * FREQUENCY_ADJUSTMENT_FACTOR; + current_period_cycles <= BASE_CLOCK_CYCLES - period_adjustment_delta; + + -- AXI4-Stream handshake: accept new data when both valid and ready + IF s_axis_tvalid = '1' AND slave_ready_internal = '1' THEN + audio_data_stage1 <= s_axis_tdata; -- Register input audio sample + enable_flag_stage1 <= lfo_enable; -- Register enable control + valid_flag_stage1 <= '1'; -- Mark data as valid for next stage + last_flag_stage1 <= s_axis_tlast; -- Register channel boundary signal + ELSE + valid_flag_stage1 <= '0'; -- No valid data to pass to next stage + END IF; + END IF; + END IF; + END PROCESS input_processing_stage; + + -- Pipeline stage 2: Triangular wave generation + -- This stage generates the triangular wave that will modulate the audio amplitude + triangular_wave_generator : PROCESS (aclk) + BEGIN + IF rising_edge(aclk) THEN + IF aresetn = '0' THEN + -- Reset triangular wave generator to initial state + timing_counter <= 0; -- Clear timing counter + triangular_wave_value <= (OTHERS => '0'); -- Start at zero amplitude + wave_direction_up <= '1'; -- Start counting up + enable_flag_stage2 <= '0'; -- Disable LFO_1 + valid_flag_stage2 <= '0'; -- No valid data + last_flag_stage2 <= '0'; -- Clear channel indicator + audio_data_stage2 <= (OTHERS => '0'); -- Clear audio data + ELSE + -- Pass through pipeline registers from stage 1 to stage 2 + enable_flag_stage2 <= enable_flag_stage1; -- Forward enable flag + valid_flag_stage2 <= valid_flag_stage1; -- Forward valid flag + last_flag_stage2 <= last_flag_stage1; -- Forward channel indicator + audio_data_stage2 <= audio_data_stage1; -- Forward audio data -- Generate triangular wave when LFO_1 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 to avoid edge cases - 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; - + IF enable_flag_stage1 = '1' THEN + -- Clock divider: update triangular counter based on calculated period + IF timing_counter < current_period_cycles THEN + timing_counter <= timing_counter + 1; -- Count towards period target ELSE - step_counter <= step_counter + 1; -- Continue counting towards next update + timing_counter <= 0; -- Reset counter for next period + -- Update triangular wave: count up or down based on current direction + -- This creates the classic triangular waveform shape + IF wave_direction_up = '1' THEN + -- Ascending phase: check if we reached maximum amplitude + IF triangular_wave_value = (2 ** TRIANGULAR_COUNTER_LENGHT) - 1 THEN + wave_direction_up <= '0'; -- Switch to descending phase + triangular_wave_value <= triangular_wave_value - 1; -- Start decreasing + ELSE + triangular_wave_value <= triangular_wave_value + 1; -- Continue increasing + END IF; + ELSE + -- Descending phase: check if we reached minimum amplitude + IF triangular_wave_value = 0 THEN + wave_direction_up <= '1'; -- Switch to ascending phase + triangular_wave_value <= triangular_wave_value + 1; -- Start increasing + ELSE + triangular_wave_value <= triangular_wave_value - 1; -- Continue decreasing + END IF; + END IF; END IF; - + ELSE + -- LFO_1 disabled: reset triangular wave generator to idle state + timing_counter <= 0; -- Clear timing counter + triangular_wave_value <= (OTHERS => '0'); -- Reset to zero amplitude + wave_direction_up <= '1'; -- Reset to ascending direction END IF; - END IF; - END IF; + END PROCESS triangular_wave_generator; - END PROCESS triangular_wave_lfo_generator; - - -- AXI4-Stream handshake logic and audio processing - -- This process handles input/output data flow and applies the LFO_1 modulation - AXIS : PROCESS (aclk) + -- Pipeline stage 3: Audio modulation and output control + -- This stage applies the LFO_1 effect by multiplying audio samples with the triangular wave + modulation_and_output : 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 - + -- Reset output stage to safe initial state + m_axis_tdata <= (OTHERS => '0'); -- Clear output data + master_valid_internal <= '0'; -- No valid output + slave_ready_internal <= '1'; -- Ready to accept input 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 + -- Output flow control: handle backpressure from downstream modules + IF master_valid_internal = '1' AND m_axis_tready = '0' THEN + -- Downstream not ready: maintain current output valid state + -- This implements proper AXI4-Stream backpressure handling + master_valid_internal <= '1'; + ELSIF valid_flag_stage2 = '1' THEN + -- New data available from stage 2: apply LFO_1 effect or bypass + IF enable_flag_stage2 = '1' THEN -- Apply LFO_1 tremolo effect: multiply audio sample by triangular wave -- This creates amplitude modulation (tremolo effect) - m_axis_tdata_temp <= signed(s_axis_tdata) * tri_counter; - s_axis_tlast_reg <= s_axis_tlast; -- Register channel indicator - - ELSE - -- LFO_1 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_1 is bypassed - m_axis_tdata_temp <= shift_left( + multiplication_result <= STD_LOGIC_VECTOR( 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 + signed(audio_data_stage2) * signed('0' & triangular_wave_value), + multiplication_result'length + ) ); - s_axis_tlast_reg <= s_axis_tlast; -- Register channel indicator - + -- Scale down result by removing lower bits (equivalent to division by 2^TRIANGULAR_COUNTER_LENGHT) + -- This maintains proper audio amplitude range after multiplication + m_axis_tdata <= multiplication_result(multiplication_result'high DOWNTO TRIANGULAR_COUNTER_LENGHT); + ELSE + -- LFO_1 disabled: pass audio through unchanged (bypass mode) + -- This allows seamless switching between effect and clean audio + m_axis_tdata <= audio_data_stage2; END IF; - - trigger <= '1'; -- Set trigger to indicate new processed data is ready - + master_valid_internal <= '1'; -- Mark output as valid + ELSE + -- No new data available: clear output valid flag + master_valid_internal <= '0'; END IF; + -- AXI4-Stream ready signal management for proper flow control + IF master_valid_internal = '1' AND m_axis_tready = '1' THEN + -- Successful output handshake: ready for new input data + slave_ready_internal <= '1'; + ELSIF s_axis_tvalid = '1' AND slave_ready_internal = '1' THEN + -- Accepted new input: not ready until current output is consumed + -- This prevents data loss in the pipeline + slave_ready_internal <= '0'; + END IF; END IF; - END IF; + END PROCESS modulation_and_output; - END PROCESS AXIS; + -- Output signal assignments + s_axis_tready <= slave_ready_internal; -- Connect internal ready to output port + m_axis_tvalid <= master_valid_internal; -- Connect internal valid to output port - -- LFO_1 Implementation Summary: - -- 1. Generates triangular wave at frequency controlled by joystick input - -- 2. When enabled: multiplies audio samples by triangular wave (tremolo effect) - -- 3. When disabled: passes audio through unchanged (bypass mode) - -- 4. Uses proper AXI4-Stream handshaking for real-time audio processing + -- LFO_1 Effect Summary: + -- 1. Stage 1: Calculates LFO_1 frequency based on joystick position + -- 2. Stage 2: Generates triangular wave at calculated frequency + -- 3. Stage 3: Multiplies audio samples by triangular wave (tremolo effect) -- - -- Tremolo Effect Characteristics: + -- Audio Effect Characteristics: + -- - Tremolo: Periodic amplitude modulation creates "shaking" sound -- - Frequency range: Approximately 0.1Hz to 10Hz (typical for audio LFO_1) -- - Modulation depth: Controlled by TRIANGULAR_COUNTER_LENGHT generic - -- - Waveform: Triangular (linear amplitude changes, smooth transitions) -- - Bypass capability: Clean audio passthrough when disabled + -- + -- Pipeline Benefits: + -- - Maintains real-time audio processing with no dropouts + -- - Allows complex calculations without affecting audio timing + -- - Provides proper AXI4-Stream flow control and backpressure handling END ARCHITECTURE Behavioral; \ No newline at end of file diff --git a/LAB3/src/effect_selector.vhd b/LAB3/src/effect_selector.vhd index 2a853fe..cc9255b 100644 --- a/LAB3/src/effect_selector.vhd +++ b/LAB3/src/effect_selector.vhd @@ -1,5 +1,6 @@ LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; +USE IEEE.NUMERIC_STD.ALL; -- Entity: effect_selector -- Purpose: Routes joystick input to different audio control parameters based on effect mode @@ -14,7 +15,7 @@ ENTITY effect_selector IS aresetn : IN STD_LOGIC; -- Active-low asynchronous reset -- Control and input signals - effect : IN STD_LOGIC; -- Effect mode selector (0=volume/balance mode, 1=LFO/balance mode) + effect : IN STD_LOGIC; -- Effect mode selector (0=volume/balance mode, 1=LFO) jstck_x : IN STD_LOGIC_VECTOR(JOYSTICK_LENGHT - 1 DOWNTO 0); -- X-axis joystick position jstck_y : IN STD_LOGIC_VECTOR(JOYSTICK_LENGHT - 1 DOWNTO 0); -- Y-axis joystick position @@ -27,6 +28,8 @@ END effect_selector; ARCHITECTURE Behavioral OF effect_selector IS + constant JOYSTICK_DEFAULT : STD_LOGIC_VECTOR(JOYSTICK_LENGHT - 1 DOWNTO 0) := STD_LOGIC_VECTOR(to_unsigned(2 ** (JOYSTICK_LENGHT - 1), JOYSTICK_LENGHT)); -- Default joystick value + BEGIN -- Main control logic process @@ -37,31 +40,33 @@ BEGIN IF rising_edge(aclk) THEN IF aresetn = '0' THEN - -- Reset all outputs to zero (minimum values) - volume <= (OTHERS => '0'); -- Minimum volume - balance <= (OTHERS => '0'); -- Full left balance - lfo_period <= (OTHERS => '0'); -- Minimum LFO period + -- Reset all outputs to default/center values + volume <= JOYSTICK_DEFAULT; -- Default volume (center position) + balance <= JOYSTICK_DEFAULT; -- Center balance position + lfo_period <= JOYSTICK_DEFAULT; -- Default LFO period (center position) ELSE -- Normal operation: Route joystick inputs based on effect mode - -- X-axis always controls balance regardless of mode - -- This provides consistent left/right audio balance control - balance <= jstck_x; - - -- Y-axis control depends on selected effect mode - -- Note: When switching between modes, the previous joystick values - -- are preserved in the non-active outputs (volume/lfo_period) + -- Y-axis always controls different parameters based on effect mode + -- X-axis behavior differs between modes + -- Note: When switching between modes, some outputs are reset while others preserved IF effect = '1' THEN -- LFO Mode: Y-axis controls Low Frequency Oscillator period -- Used for tremolo, vibrato, or other modulation effects lfo_period <= jstck_y; - -- Volume remains at last set value (preserved from previous volume mode) + -- Volume remains at last set value (preserved from previous mode) + + -- Reset balance to center/default position when in LFO mode + balance <= JOYSTICK_DEFAULT; -- Reset balance to center position ELSE - -- Volume/Balance Mode: Y-axis controls overall volume level + -- Volume/Balance Mode: + -- Y-axis controls overall volume level + -- X-axis controls left/right audio balance -- Traditional audio mixer control mode volume <= jstck_y; + balance <= jstck_x; -- LFO period remains at last set value (preserved from previous LFO mode) END IF; diff --git a/LAB3/src/led_level_controller.vhd b/LAB3/src/led_level_controller.vhd index 004f664..6dd7803 100644 --- a/LAB3/src/led_level_controller.vhd +++ b/LAB3/src/led_level_controller.vhd @@ -36,7 +36,6 @@ ARCHITECTURE Behavioral OF led_level_controller IS CONSTANT REFRESH_CYCLES : NATURAL := (refresh_time_ms * 1_000_000) / clock_period_ns; -- Audio processing signals - SIGNAL volume_value : signed(CHANNEL_LENGHT - 1 DOWNTO 0) := (OTHERS => '0'); -- Current audio sample (signed) SIGNAL abs_audio_left : unsigned(CHANNEL_LENGHT - 2 DOWNTO 0) := (OTHERS => '0'); -- Absolute value of left channel SIGNAL abs_audio_right : unsigned(CHANNEL_LENGHT - 2 DOWNTO 0) := (OTHERS => '0'); -- Absolute value of right channel @@ -64,18 +63,16 @@ BEGIN IF aresetn = '0' THEN -- Reset: Clear all audio processing signals - volume_value <= (OTHERS => '0'); -- Clear current sample abs_audio_left <= (OTHERS => '0'); -- Clear left channel level abs_audio_right <= (OTHERS => '0'); -- Clear right channel level ELSIF s_axis_tvalid = '1' THEN -- Process new audio sample when valid data is available sdata_signed := signed(s_axis_tdata); -- Convert input to signed format - volume_value <= sdata_signed; -- Store current sample -- Absolute value calculation for amplitude detection -- Handle two's complement signed numbers correctly - IF sdata_signed(CHANNEL_LENGHT - 1) = '1' THEN + IF s_axis_tdata(s_axis_tdata'high) = '1' THEN -- Negative number: Take two's complement to get absolute value abs_value := unsigned(-sdata_signed); ELSE @@ -145,19 +142,16 @@ BEGIN -- Update LED display when refresh trigger is active -- Combine left and right channel amplitudes - -- Resize both channels to full width before addition to prevent overflow - abs_audio_sum := resize(abs_audio_left, CHANNEL_LENGHT) + resize(abs_audio_right, CHANNEL_LENGHT); + abs_audio_sum := abs_audio_left + abs_audio_right; - -- Level calculation with automatic sensitivity scaling - IF (abs_audio_left = 0 AND abs_audio_right = 0) THEN - -- Silence: No LEDs illuminated + -- Level calculation with linear scaling to get the best visual result + IF abs_audio_sum = 0 THEN + -- Mute: No LEDs illuminated temp_led_level := 0; ELSE - -- Audio present: Calculate LED level using logarithmic-like scaling - -- Right shift by (CHANNEL_LENGHT - 4) provides automatic sensitivity adjustment - -- The "1 +" ensures at least one LED is on when audio is present - -- Shift amount determines sensitivity: larger shift = less sensitive + -- Right shift by (CHANNEL_LENGHT - 4) to take in account only the most significant bits + -- to scale the audio amplitude in a linear way to fit within the LED range temp_led_level := 1 + to_integer(shift_right(abs_audio_sum, CHANNEL_LENGHT - 4)); END IF; @@ -185,18 +179,4 @@ BEGIN END PROCESS; - -- LED Level Meter Operation Summary: - -- 1. Continuously samples stereo audio data - -- 2. Calculates absolute value (amplitude) for each channel - -- 3. Combines left and right channels for total signal strength - -- 4. Updates LED display at regular intervals (refresh_time_ms) - -- 5. Maps audio amplitude to number of illuminated LEDs - -- 6. Creates bar graph visualization with automatic sensitivity scaling - -- - -- Leds Behavior: - -- - No audio: All LEDs off - -- - Low audio: Few LEDs illuminated (bottom of bar) - -- - High audio: Many LEDs illuminated (full bar) - -- - Overload: All LEDs illuminated (maximum indication) - END Behavioral; \ No newline at end of file