- LFO <-> LFO_1
- Effect Selector
- modify led_level_controller (1)
This commit is contained in:
2025-05-31 19:07:49 +02:00
parent 6ded9dc0a8
commit c66c218f65
4 changed files with 344 additions and 359 deletions

View File

@@ -2,16 +2,16 @@ LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.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 -- Purpose: Applies tremolo effect to audio by modulating amplitude with a triangular wave
-- Creates classic audio effects like vibrato, tremolo, and amplitude modulation -- This is a simplified, single-process implementation compared to the pipelined version
-- Implements a 3-stage pipeline for efficient real-time audio processing -- Provides real-time audio amplitude modulation for musical effects
ENTITY LFO IS ENTITY LFO IS
GENERIC ( GENERIC (
CHANNEL_LENGHT : INTEGER := 24; -- Bit width of audio samples (24-bit signed) 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) 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) 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 ( PORT (
-- Clock and Reset -- Clock and Reset
@@ -38,219 +38,188 @@ END ENTITY LFO;
ARCHITECTURE Behavioral OF LFO IS ARCHITECTURE Behavioral OF LFO IS
-- Constants for LFO timing configuration -- LFO timing configuration constants
CONSTANT BASE_PERIOD_MICROSECONDS : INTEGER := 1000; -- Base period: 1ms (1kHz base frequency) CONSTANT LFO_COUNTER_BASE_PERIOD_US : INTEGER := 1000; -- Base period: 1ms (1kHz base frequency)
CONSTANT FREQUENCY_ADJUSTMENT_FACTOR : INTEGER := 90; -- Frequency adjustment sensitivity (clock cycles per joystick unit) CONSTANT 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) 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 -- 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 -- Calculate frequency range limits based on joystick range
-- Minimum frequency (fastest LFO): occurs when joystick is at minimum position CONSTANT LFO_CLK_CYCLES_MIN : INTEGER := LFO_COUNTER_BASE_CLK_CYCLES - ADJUSTMENT_FACTOR * (2 ** (JOYSTICK_LENGHT - 1)); -- 53,920 clk cycles (faster)
CONSTANT MIN_CLOCK_CYCLES : INTEGER := BASE_CLOCK_CYCLES - FREQUENCY_ADJUSTMENT_FACTOR * (2 ** (JOYSTICK_LENGHT - 1)); CONSTANT LFO_CLK_CYCLES_MAX : INTEGER := LFO_COUNTER_BASE_CLK_CYCLES + ADJUSTMENT_FACTOR * (2 ** (JOYSTICK_LENGHT - 1) - 1); -- 145,990 clk cycles (slower)
-- 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);
-- Internal signals for LFO control -- LFO timing control signals
-- Period adjustment based on joystick input (positive = slower, negative = faster) SIGNAL step_clk_cycles_delta : INTEGER RANGE - 2 ** (JOYSTICK_LENGHT - 1) * ADJUSTMENT_FACTOR TO (2 ** (JOYSTICK_LENGHT - 1) - 1) * ADJUSTMENT_FACTOR := 0;
SIGNAL period_adjustment_delta : INTEGER RANGE - 2 ** (JOYSTICK_LENGHT - 1) * FREQUENCY_ADJUSTMENT_FACTOR SIGNAL step_clk_cycles : INTEGER RANGE LFO_CLK_CYCLES_MIN TO LFO_CLK_CYCLES_MAX := LFO_COUNTER_BASE_CLK_CYCLES;
TO (2 ** (JOYSTICK_LENGHT - 1) - 1) * FREQUENCY_ADJUSTMENT_FACTOR := 0; SIGNAL step_counter : NATURAL RANGE 0 TO LFO_CLK_CYCLES_MAX := 0;
SIGNAL current_period_cycles : INTEGER RANGE MIN_CLOCK_CYCLES TO MAX_CLOCK_CYCLES := BASE_CLOCK_CYCLES;
-- Pipeline stage 1 registers - Input processing and period calculation -- Triangular wave generation signals
SIGNAL audio_data_stage1 : STD_LOGIC_VECTOR(CHANNEL_LENGHT - 1 DOWNTO 0) := (OTHERS => '0'); -- Registered audio input -- Note: Using signed counter with extra bit to handle full range calculations
SIGNAL enable_flag_stage1 : STD_LOGIC := '0'; -- Registered LFO enable SIGNAL tri_counter : SIGNED(TRIANGULAR_COUNTER_LENGHT DOWNTO 0) := (OTHERS => '0'); -- Triangular wave amplitude
SIGNAL valid_flag_stage1 : STD_LOGIC := '0'; -- Valid data in stage 1 SIGNAL direction_up : STD_LOGIC := '1'; -- Wave direction: '1' = ascending, '0' = descending
SIGNAL last_flag_stage1 : STD_LOGIC := '0'; -- Registered channel indicator
-- Pipeline stage 2 registers - Triangular wave generation -- AXI4-Stream control signals
SIGNAL triangular_wave_value : unsigned(TRIANGULAR_COUNTER_LENGHT - 1 DOWNTO 0) := (OTHERS => '0'); -- Current triangular wave amplitude SIGNAL trigger : STD_LOGIC := '0'; -- Trigger to indicate new processed data is ready
SIGNAL wave_direction_up : STD_LOGIC := '1'; -- Triangle wave direction: '1' = ascending, '0' = descending SIGNAL s_axis_tlast_reg : STD_LOGIC := '0'; -- Registered version of tlast for output synchronization
SIGNAL timing_counter : NATURAL RANGE 0 TO MAX_CLOCK_CYCLES := 0; -- Clock cycle counter for LFO timing SIGNAL m_axis_tvalid_int : STD_LOGIC := '0'; -- Internal output valid signal
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
-- Pipeline stage 3 registers - Modulation and output -- Audio processing signal with extended width for multiplication
-- Extended width to accommodate multiplication result before scaling -- Width accommodates: audio sample + triangular counter to prevent overflow
SIGNAL multiplication_result : STD_LOGIC_VECTOR(CHANNEL_LENGHT + TRIANGULAR_COUNTER_LENGHT - 1 DOWNTO 0) := (OTHERS => '0'); SIGNAL m_axis_tdata_temp : SIGNED(CHANNEL_LENGHT + TRIANGULAR_COUNTER_LENGHT 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 BEGIN
-- Direct connection: tlast passes through unchanged (maintains channel timing) -- Output signal assignments with proper AXI4-Stream flow control
m_axis_tlast <= last_flag_stage1; 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 -- Optimized single process for LFO timing and triangular waveform generation
-- This stage captures input data and calculates the LFO period based on joystick position -- This process handles both the frequency control and wave shape generation
input_processing_stage : PROCESS (aclk) triangular_wave_lfo_generator : PROCESS (aclk)
BEGIN BEGIN
IF rising_edge(aclk) THEN IF rising_edge(aclk) THEN
IF aresetn = '0' THEN IF aresetn = '0' THEN
-- Reset all stage 1 registers to safe initial states -- Reset LFO generator to initial state
audio_data_stage1 <= (OTHERS => '0'); -- Clear audio data step_clk_cycles <= LFO_COUNTER_BASE_CLK_CYCLES; -- Set to base frequency
current_period_cycles <= BASE_CLOCK_CYCLES; -- Set to base frequency step_counter <= 0; -- Clear timing counter
enable_flag_stage1 <= '0'; -- Disable LFO tri_counter <= (OTHERS => '0'); -- Start triangular wave at zero
valid_flag_stage1 <= '0'; -- No valid data direction_up <= '1'; -- Start counting up
last_flag_stage1 <= '0'; -- Clear channel indicator
ELSE ELSE
-- Calculate LFO period based on joystick y-axis input -- Calculate LFO period based on joystick input
-- Joystick mapping: -- Joystick mapping:
-- 0-511: Faster than base frequency (shorter period) -- 0-511: Faster than base frequency (shorter period, higher frequency)
-- 512: Base frequency (1kHz) -- 512: Base frequency (1kHz)
-- 513-1023: Slower than base frequency (longer period) -- 513-1023: Slower than base frequency (longer period, lower frequency)
period_adjustment_delta <= (to_integer(unsigned(lfo_period)) - JOYSTICK_CENTER_VALUE) * FREQUENCY_ADJUSTMENT_FACTOR; step_clk_cycles_delta <= (to_integer(unsigned(lfo_period)) - JSTK_CENTER_VALUE) * ADJUSTMENT_FACTOR;
current_period_cycles <= BASE_CLOCK_CYCLES - period_adjustment_delta; step_clk_cycles <= LFO_COUNTER_BASE_CLK_CYCLES - step_clk_cycles_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
-- Generate triangular wave when LFO is enabled -- Generate triangular wave when LFO is enabled
IF enable_flag_stage1 = '1' THEN IF lfo_enable = '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
-- Update triangular wave: count up or down based on current direction -- 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 -- This creates the classic triangular waveform shape
IF wave_direction_up = '1' THEN IF direction_up = '1' THEN
-- Ascending phase: check if we reached maximum amplitude tri_counter <= tri_counter + 1; -- Ascending: increment
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 ELSE
triangular_wave_value <= triangular_wave_value + 1; -- Continue increasing tri_counter <= tri_counter - 1; -- Descending: decrement
END IF; 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 ELSE
-- This stage applies the LFO effect by multiplying audio samples with the triangular wave step_counter <= step_counter + 1; -- Continue counting towards next update
modulation_and_output : PROCESS (aclk)
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 BEGIN
IF rising_edge(aclk) THEN IF rising_edge(aclk) THEN
IF aresetn = '0' THEN IF aresetn = '0' THEN
-- Reset output stage to safe initial state -- Reset AXI4-Stream interface and audio processing
m_axis_tdata <= (OTHERS => '0'); -- Clear output data s_axis_tlast_reg <= '0'; -- Clear registered channel indicator
master_valid_internal <= '0'; -- No valid output m_axis_tdata_temp <= (OTHERS => '0'); -- Clear temporary audio data
slave_ready_internal <= '1'; -- Ready to accept input m_axis_tvalid_int <= '0'; -- No valid output data
m_axis_tlast <= '0'; -- Clear output channel indicator
ELSE ELSE
-- Output flow control: handle backpressure from downstream modules -- Output handshake management:
IF master_valid_internal = '1' AND m_axis_tready = '0' THEN -- Clear valid flag when downstream accepts data
-- Downstream not ready: maintain current output valid state IF m_axis_tready = '1' THEN
-- This implements proper AXI4-Stream backpressure handling m_axis_tvalid_int <= '0';
master_valid_internal <= '1'; END IF;
ELSIF valid_flag_stage2 = '1' THEN
-- New data available from stage 2: apply LFO effect or bypass -- Data output logic: Send processed audio when trigger is active and output is available
IF enable_flag_stage2 = '1' THEN IF trigger = '1' AND (m_axis_tvalid_int = '0' OR m_axis_tready = '1') THEN
-- Apply LFO tremolo effect: multiply audio sample by triangular wave -- Scale down the multiplication result to original audio bit width
-- This creates amplitude modulation (tremolo effect) -- Right shift by TRIANGULAR_COUNTER_LENGHT effectively divides by 2^TRIANGULAR_COUNTER_LENGHT
multiplication_result <= STD_LOGIC_VECTOR( -- This maintains proper audio amplitude after modulation
m_axis_tdata <= STD_LOGIC_VECTOR(
resize( resize(
signed(audio_data_stage2) * signed('0' & triangular_wave_value), shift_right(
multiplication_result'length m_axis_tdata_temp, -- Wide multiplication result
TRIANGULAR_COUNTER_LENGHT -- Scale factor
),
CHANNEL_LENGHT -- Final audio sample width
) )
); );
-- Scale down result by removing lower bits (equivalent to division by 2^TRIANGULAR_COUNTER_LENGHT) m_axis_tlast <= s_axis_tlast_reg; -- Output registered channel indicator
-- This maintains proper audio amplitude range after multiplication
m_axis_tdata <= multiplication_result(multiplication_result'high DOWNTO TRIANGULAR_COUNTER_LENGHT); 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)
m_axis_tdata_temp <= signed(s_axis_tdata) * tri_counter;
s_axis_tlast_reg <= s_axis_tlast; -- Register channel indicator
ELSE ELSE
-- LFO disabled: pass audio through unchanged (bypass mode) -- LFO disabled: pass audio through unchanged but maintain bit width
-- This allows seamless switching between effect and clean audio -- Left shift compensates for the right shift that occurs during output
m_axis_tdata <= audio_data_stage2; -- This ensures unity gain when LFO is bypassed
END IF; m_axis_tdata_temp <= shift_left(
master_valid_internal <= '1'; -- Mark output as valid resize(
ELSE signed(s_axis_tdata), -- Convert input to signed
-- No new data available: clear output valid flag m_axis_tdata_temp'length -- Extend to full processing width
master_valid_internal <= '0'; ),
TRIANGULAR_COUNTER_LENGHT -- Compensate for output scaling
);
s_axis_tlast_reg <= s_axis_tlast; -- Register channel indicator
END IF; END IF;
-- AXI4-Stream ready signal management for proper flow control trigger <= '1'; -- Set trigger to indicate new processed data is ready
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 END IF;
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 Effect Summary: END IF;
-- 1. Stage 1: Calculates LFO frequency based on joystick position
-- 2. Stage 2: Generates triangular wave at calculated frequency END IF;
-- 3. Stage 3: Multiplies audio samples by triangular wave (tremolo effect)
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 (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 Effect Characteristics:
-- - Tremolo: Periodic amplitude modulation creates "shaking" sound
-- - Frequency range: Approximately 0.1Hz to 10Hz (typical for audio LFO) -- - Frequency range: Approximately 0.1Hz to 10Hz (typical for audio LFO)
-- - Modulation depth: Controlled by TRIANGULAR_COUNTER_LENGHT generic -- - Modulation depth: Controlled by TRIANGULAR_COUNTER_LENGHT generic
-- - Waveform: Triangular (linear amplitude changes, smooth transitions)
-- - Bypass capability: Clean audio passthrough when disabled -- - 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; END ARCHITECTURE Behavioral;

View File

@@ -2,16 +2,16 @@ LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.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 -- 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 -- Creates classic audio effects like vibrato, tremolo, and amplitude modulation
-- Provides real-time audio amplitude modulation for musical effects -- Implements a 3-stage pipeline for efficient real-time audio processing
ENTITY LFO_1 IS ENTITY LFO_1 IS
GENERIC ( GENERIC (
CHANNEL_LENGHT : INTEGER := 24; -- Bit width of audio samples (24-bit signed) 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) 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) 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 ( PORT (
-- Clock and Reset -- Clock and Reset
@@ -38,188 +38,219 @@ END ENTITY LFO_1;
ARCHITECTURE Behavioral OF LFO_1 IS ARCHITECTURE Behavioral OF LFO_1 IS
-- LFO_1 timing configuration constants -- Constants for LFO_1 timing configuration
CONSTANT LFO_1_COUNTER_BASE_PERIOD_US : INTEGER := 1000; -- Base period: 1ms (1kHz base frequency) CONSTANT BASE_PERIOD_MICROSECONDS : INTEGER := 1000; -- Base period: 1ms (1kHz base frequency)
CONSTANT ADJUSTMENT_FACTOR : INTEGER := 90; -- Frequency adjustment sensitivity (clock cycles per joystick unit) CONSTANT FREQUENCY_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) 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 -- 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 -- 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) -- Minimum frequency (fastest LFO_1): occurs when joystick is at minimum position
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) 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 -- Internal signals for LFO_1 control
SIGNAL step_clk_cycles_delta : INTEGER RANGE - 2 ** (JOYSTICK_LENGHT - 1) * ADJUSTMENT_FACTOR TO (2 ** (JOYSTICK_LENGHT - 1) - 1) * ADJUSTMENT_FACTOR := 0; -- Period adjustment based on joystick input (positive = slower, negative = faster)
SIGNAL step_clk_cycles : INTEGER RANGE LFO_1_CLK_CYCLES_MIN TO LFO_1_CLK_CYCLES_MAX := LFO_1_COUNTER_BASE_CLK_CYCLES; SIGNAL period_adjustment_delta : INTEGER RANGE - 2 ** (JOYSTICK_LENGHT - 1) * FREQUENCY_ADJUSTMENT_FACTOR
SIGNAL step_counter : NATURAL RANGE 0 TO LFO_1_CLK_CYCLES_MAX := 0; 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 -- Pipeline stage 1 registers - Input processing and period calculation
-- Note: Using signed counter with extra bit to handle full range calculations SIGNAL audio_data_stage1 : STD_LOGIC_VECTOR(CHANNEL_LENGHT - 1 DOWNTO 0) := (OTHERS => '0'); -- Registered audio input
SIGNAL tri_counter : SIGNED(TRIANGULAR_COUNTER_LENGHT DOWNTO 0) := (OTHERS => '0'); -- Triangular wave amplitude SIGNAL enable_flag_stage1 : STD_LOGIC := '0'; -- Registered LFO_1 enable
SIGNAL direction_up : STD_LOGIC := '1'; -- Wave direction: '1' = ascending, '0' = descending 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 -- Pipeline stage 2 registers - Triangular wave generation
SIGNAL trigger : STD_LOGIC := '0'; -- Trigger to indicate new processed data is ready SIGNAL triangular_wave_value : unsigned(TRIANGULAR_COUNTER_LENGHT - 1 DOWNTO 0) := (OTHERS => '0'); -- Current triangular wave amplitude
SIGNAL s_axis_tlast_reg : STD_LOGIC := '0'; -- Registered version of tlast for output synchronization SIGNAL wave_direction_up : STD_LOGIC := '1'; -- Triangle wave direction: '1' = ascending, '0' = descending
SIGNAL m_axis_tvalid_int : STD_LOGIC := '0'; -- Internal output valid signal 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 -- Pipeline stage 3 registers - Modulation and output
-- Width accommodates: audio sample + triangular counter to prevent overflow -- Extended width to accommodate multiplication result before scaling
SIGNAL m_axis_tdata_temp : SIGNED(CHANNEL_LENGHT + TRIANGULAR_COUNTER_LENGHT DOWNTO 0) := (OTHERS => '0'); 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 BEGIN
-- Output signal assignments with proper AXI4-Stream flow control -- Direct connection: tlast passes through unchanged (maintains channel timing)
m_axis_tvalid <= m_axis_tvalid_int; m_axis_tlast <= last_flag_stage1;
-- 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_1 timing and triangular waveform generation -- Pipeline stage 1: Input registration and LFO_1 period calculation
-- This process handles both the frequency control and wave shape generation -- This stage captures input data and calculates the LFO_1 period based on joystick position
triangular_wave_lfo_generator : PROCESS (aclk) input_processing_stage : PROCESS (aclk)
BEGIN BEGIN
IF rising_edge(aclk) THEN IF rising_edge(aclk) THEN
IF aresetn = '0' THEN IF aresetn = '0' THEN
-- Reset LFO_1 generator to initial state -- Reset all stage 1 registers to safe initial states
step_clk_cycles <= LFO_1_COUNTER_BASE_CLK_CYCLES; -- Set to base frequency audio_data_stage1 <= (OTHERS => '0'); -- Clear audio data
step_counter <= 0; -- Clear timing counter current_period_cycles <= BASE_CLOCK_CYCLES; -- Set to base frequency
tri_counter <= (OTHERS => '0'); -- Start triangular wave at zero enable_flag_stage1 <= '0'; -- Disable LFO_1
direction_up <= '1'; -- Start counting up valid_flag_stage1 <= '0'; -- No valid data
last_flag_stage1 <= '0'; -- Clear channel indicator
ELSE ELSE
-- Calculate LFO_1 period based on joystick input -- Calculate LFO_1 period based on joystick y-axis input
-- Joystick mapping: -- Joystick mapping:
-- 0-511: Faster than base frequency (shorter period, higher frequency) -- 0-511: Faster than base frequency (shorter period)
-- 512: Base frequency (1kHz) -- 512: Base frequency (1kHz)
-- 513-1023: Slower than base frequency (longer period, lower frequency) -- 513-1023: Slower than base frequency (longer period)
step_clk_cycles_delta <= (to_integer(unsigned(lfo_period)) - JSTK_CENTER_VALUE) * ADJUSTMENT_FACTOR; period_adjustment_delta <= (to_integer(unsigned(lfo_period)) - JOYSTICK_CENTER_VALUE) * FREQUENCY_ADJUSTMENT_FACTOR;
step_clk_cycles <= LFO_1_COUNTER_BASE_CLK_CYCLES - step_clk_cycles_delta; 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 -- Generate triangular wave when LFO_1 is enabled
IF lfo_enable = '1' THEN 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
-- Clock divider: Update triangular wave at calculated rate -- Update triangular wave: count up or down based on current direction
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 -- This creates the classic triangular waveform shape
IF direction_up = '1' THEN IF wave_direction_up = '1' THEN
tri_counter <= tri_counter + 1; -- Ascending: increment -- 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 ELSE
tri_counter <= tri_counter - 1; -- Descending: decrement triangular_wave_value <= triangular_wave_value + 1; -- Continue increasing
END IF; END IF;
ELSE ELSE
step_counter <= step_counter + 1; -- Continue counting towards next update -- 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; 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 IF;
END PROCESS triangular_wave_generator;
END PROCESS triangular_wave_lfo_generator; -- Pipeline stage 3: Audio modulation and output control
-- This stage applies the LFO_1 effect by multiplying audio samples with the triangular wave
-- AXI4-Stream handshake logic and audio processing modulation_and_output : PROCESS (aclk)
-- This process handles input/output data flow and applies the LFO_1 modulation
AXIS : PROCESS (aclk)
BEGIN BEGIN
IF rising_edge(aclk) THEN IF rising_edge(aclk) THEN
IF aresetn = '0' THEN IF aresetn = '0' THEN
-- Reset AXI4-Stream interface and audio processing -- Reset output stage to safe initial state
s_axis_tlast_reg <= '0'; -- Clear registered channel indicator m_axis_tdata <= (OTHERS => '0'); -- Clear output data
m_axis_tdata_temp <= (OTHERS => '0'); -- Clear temporary audio data master_valid_internal <= '0'; -- No valid output
m_axis_tvalid_int <= '0'; -- No valid output data slave_ready_internal <= '1'; -- Ready to accept input
m_axis_tlast <= '0'; -- Clear output channel indicator
ELSE ELSE
-- Output handshake management: -- Output flow control: handle backpressure from downstream modules
-- Clear valid flag when downstream accepts data IF master_valid_internal = '1' AND m_axis_tready = '0' THEN
IF m_axis_tready = '1' THEN -- Downstream not ready: maintain current output valid state
m_axis_tvalid_int <= '0'; -- This implements proper AXI4-Stream backpressure handling
END IF; master_valid_internal <= '1';
ELSIF valid_flag_stage2 = '1' THEN
-- Data output logic: Send processed audio when trigger is active and output is available -- New data available from stage 2: apply LFO_1 effect or bypass
IF trigger = '1' AND (m_axis_tvalid_int = '0' OR m_axis_tready = '1') THEN IF enable_flag_stage2 = '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_1 tremolo effect: multiply audio sample by triangular wave -- Apply LFO_1 tremolo effect: multiply audio sample by triangular wave
-- This creates amplitude modulation (tremolo effect) -- This creates amplitude modulation (tremolo effect)
m_axis_tdata_temp <= signed(s_axis_tdata) * tri_counter; multiplication_result <= STD_LOGIC_VECTOR(
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(
resize( resize(
signed(s_axis_tdata), -- Convert input to signed signed(audio_data_stage2) * signed('0' & triangular_wave_value),
m_axis_tdata_temp'length -- Extend to full processing width multiplication_result'length
), )
TRIANGULAR_COUNTER_LENGHT -- Compensate for output scaling
); );
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;
master_valid_internal <= '1'; -- Mark output as valid
ELSE
-- No new data available: clear output valid flag
master_valid_internal <= '0';
END IF; END IF;
trigger <= '1'; -- Set trigger to indicate new processed data is ready -- 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 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: -- LFO_1 Effect Summary:
-- 1. Generates triangular wave at frequency controlled by joystick input -- 1. Stage 1: Calculates LFO_1 frequency based on joystick position
-- 2. When enabled: multiplies audio samples by triangular wave (tremolo effect) -- 2. Stage 2: Generates triangular wave at calculated frequency
-- 3. When disabled: passes audio through unchanged (bypass mode) -- 3. Stage 3: Multiplies audio samples by triangular wave (tremolo effect)
-- 4. Uses proper AXI4-Stream handshaking for real-time audio processing
-- --
-- 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) -- - Frequency range: Approximately 0.1Hz to 10Hz (typical for audio LFO_1)
-- - Modulation depth: Controlled by TRIANGULAR_COUNTER_LENGHT generic -- - Modulation depth: Controlled by TRIANGULAR_COUNTER_LENGHT generic
-- - Waveform: Triangular (linear amplitude changes, smooth transitions)
-- - Bypass capability: Clean audio passthrough when disabled -- - 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; END ARCHITECTURE Behavioral;

View File

@@ -1,5 +1,6 @@
LIBRARY IEEE; LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;
-- Entity: effect_selector -- Entity: effect_selector
-- Purpose: Routes joystick input to different audio control parameters based on effect mode -- 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 aresetn : IN STD_LOGIC; -- Active-low asynchronous reset
-- Control and input signals -- 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_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 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 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 BEGIN
-- Main control logic process -- Main control logic process
@@ -37,31 +40,33 @@ BEGIN
IF rising_edge(aclk) THEN IF rising_edge(aclk) THEN
IF aresetn = '0' THEN IF aresetn = '0' THEN
-- Reset all outputs to zero (minimum values) -- Reset all outputs to default/center values
volume <= (OTHERS => '0'); -- Minimum volume volume <= JOYSTICK_DEFAULT; -- Default volume (center position)
balance <= (OTHERS => '0'); -- Full left balance balance <= JOYSTICK_DEFAULT; -- Center balance position
lfo_period <= (OTHERS => '0'); -- Minimum LFO period lfo_period <= JOYSTICK_DEFAULT; -- Default LFO period (center position)
ELSE ELSE
-- Normal operation: Route joystick inputs based on effect mode -- Normal operation: Route joystick inputs based on effect mode
-- X-axis always controls balance regardless of mode -- Y-axis always controls different parameters based on effect mode
-- This provides consistent left/right audio balance control -- X-axis behavior differs between modes
balance <= jstck_x; -- Note: When switching between modes, some outputs are reset while others preserved
-- 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)
IF effect = '1' THEN IF effect = '1' THEN
-- LFO Mode: Y-axis controls Low Frequency Oscillator period -- LFO Mode: Y-axis controls Low Frequency Oscillator period
-- Used for tremolo, vibrato, or other modulation effects -- Used for tremolo, vibrato, or other modulation effects
lfo_period <= jstck_y; 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 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 -- Traditional audio mixer control mode
volume <= jstck_y; volume <= jstck_y;
balance <= jstck_x;
-- LFO period remains at last set value (preserved from previous LFO mode) -- LFO period remains at last set value (preserved from previous LFO mode)
END IF; END IF;

View File

@@ -36,7 +36,6 @@ ARCHITECTURE Behavioral OF led_level_controller IS
CONSTANT REFRESH_CYCLES : NATURAL := (refresh_time_ms * 1_000_000) / clock_period_ns; CONSTANT REFRESH_CYCLES : NATURAL := (refresh_time_ms * 1_000_000) / clock_period_ns;
-- Audio processing signals -- 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_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 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 IF aresetn = '0' THEN
-- Reset: Clear all audio processing signals -- Reset: Clear all audio processing signals
volume_value <= (OTHERS => '0'); -- Clear current sample
abs_audio_left <= (OTHERS => '0'); -- Clear left channel level abs_audio_left <= (OTHERS => '0'); -- Clear left channel level
abs_audio_right <= (OTHERS => '0'); -- Clear right channel level abs_audio_right <= (OTHERS => '0'); -- Clear right channel level
ELSIF s_axis_tvalid = '1' THEN ELSIF s_axis_tvalid = '1' THEN
-- Process new audio sample when valid data is available -- Process new audio sample when valid data is available
sdata_signed := signed(s_axis_tdata); -- Convert input to signed format sdata_signed := signed(s_axis_tdata); -- Convert input to signed format
volume_value <= sdata_signed; -- Store current sample
-- Absolute value calculation for amplitude detection -- Absolute value calculation for amplitude detection
-- Handle two's complement signed numbers correctly -- 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 -- Negative number: Take two's complement to get absolute value
abs_value := unsigned(-sdata_signed); abs_value := unsigned(-sdata_signed);
ELSE ELSE
@@ -145,19 +142,16 @@ BEGIN
-- Update LED display when refresh trigger is active -- Update LED display when refresh trigger is active
-- Combine left and right channel amplitudes -- Combine left and right channel amplitudes
-- Resize both channels to full width before addition to prevent overflow abs_audio_sum := abs_audio_left + abs_audio_right;
abs_audio_sum := resize(abs_audio_left, CHANNEL_LENGHT) + resize(abs_audio_right, CHANNEL_LENGHT);
-- Level calculation with automatic sensitivity scaling -- Level calculation with linear scaling to get the best visual result
IF (abs_audio_left = 0 AND abs_audio_right = 0) THEN IF abs_audio_sum = 0 THEN
-- Silence: No LEDs illuminated -- Mute: No LEDs illuminated
temp_led_level := 0; temp_led_level := 0;
ELSE ELSE
-- Audio present: Calculate LED level using logarithmic-like scaling -- Right shift by (CHANNEL_LENGHT - 4) to take in account only the most significant bits
-- Right shift by (CHANNEL_LENGHT - 4) provides automatic sensitivity adjustment -- to scale the audio amplitude in a linear way to fit within the LED range
-- The "1 +" ensures at least one LED is on when audio is present
-- Shift amount determines sensitivity: larger shift = less sensitive
temp_led_level := 1 + to_integer(shift_right(abs_audio_sum, CHANNEL_LENGHT - 4)); temp_led_level := 1 + to_integer(shift_right(abs_audio_sum, CHANNEL_LENGHT - 4));
END IF; END IF;
@@ -185,18 +179,4 @@ BEGIN
END PROCESS; 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; END Behavioral;