diff --git a/LAB3/src/LFO.vhd b/LAB3/src/LFO.vhd
index 2f51ce8..ee49199 100644
--- a/LAB3/src/LFO.vhd
+++ b/LAB3/src/LFO.vhd
@@ -2,180 +2,255 @@ LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;
+-- Entity: LFO (Low Frequency Oscillator)
+-- 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
ENTITY LFO IS
GENERIC (
- CHANNEL_LENGHT : INTEGER := 24;
- JOYSTICK_LENGHT : INTEGER := 10;
- CLK_PERIOD_NS : INTEGER := 10;
- TRIANGULAR_COUNTER_LENGHT : INTEGER := 10
+ 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)
);
PORT (
- aclk : IN STD_LOGIC;
- aresetn : IN STD_LOGIC;
- lfo_period : IN STD_LOGIC_VECTOR(JOYSTICK_LENGHT - 1 DOWNTO 0);
- lfo_enable : IN STD_LOGIC;
- -- Slave AXI Stream interface
- s_axis_tvalid : IN STD_LOGIC;
- s_axis_tdata : IN STD_LOGIC_VECTOR(CHANNEL_LENGHT - 1 DOWNTO 0);
- s_axis_tlast : IN STD_LOGIC;
- s_axis_tready : OUT STD_LOGIC;
- -- Master AXI Stream interface
- m_axis_tvalid : OUT STD_LOGIC;
- m_axis_tdata : OUT STD_LOGIC_VECTOR(CHANNEL_LENGHT - 1 DOWNTO 0);
- m_axis_tlast : OUT STD_LOGIC;
- m_axis_tready : IN STD_LOGIC
+ -- 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
- CONSTANT LFO_COUNTER_BASE_PERIOD_US : INTEGER := 1000; -- 1ms
- CONSTANT ADJUSTMENT_FACTOR : INTEGER := 90;
- CONSTANT JSTK_CENTER_VALUE : INTEGER := 2 ** (JOYSTICK_LENGHT - 1); -- 512 for 10 bits
- CONSTANT LFO_COUNTER_BASE_CLK_CYCLES : INTEGER := LFO_COUNTER_BASE_PERIOD_US * 1000 / CLK_PERIOD_NS; -- 1ms = 100_000 clk cycles
- CONSTANT LFO_CLK_CYCLES_MIN : INTEGER := LFO_COUNTER_BASE_CLK_CYCLES - ADJUSTMENT_FACTOR * (2 ** (JOYSTICK_LENGHT - 1)); -- 53_920 clk cycles
- CONSTANT LFO_CLK_CYCLES_MAX : INTEGER := LFO_COUNTER_BASE_CLK_CYCLES + ADJUSTMENT_FACTOR * (2 ** (JOYSTICK_LENGHT - 1) - 1); -- 145_990 clk cycles
+ -- 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)
- -- Signals for LFO operation
- 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;
+ -- Calculate base clock cycles for 1ms period at current clock frequency
+ CONSTANT BASE_CLOCK_CYCLES : INTEGER := BASE_PERIOD_MICROSECONDS * 1000 / CLK_PERIOD_NS;
- -- Pipeline stage registers
- SIGNAL s_axis_tdata_r1 : STD_LOGIC_VECTOR(CHANNEL_LENGHT - 1 DOWNTO 0) := (OTHERS => '0');
- SIGNAL lfo_enable_r1 : STD_LOGIC := '0';
- SIGNAL s_axis_tvalid_r1 : STD_LOGIC := '0';
- SIGNAL s_axis_tlast_r1 : STD_LOGIC := '0';
+ -- 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);
- SIGNAL tri_counter_r2 : unsigned(TRIANGULAR_COUNTER_LENGHT - 1 DOWNTO 0) := (OTHERS => '0');
- SIGNAL direction_up_r2 : STD_LOGIC := '1';
- SIGNAL step_counter_r2 : NATURAL RANGE 0 TO LFO_CLK_CYCLES_MAX := 0;
- SIGNAL lfo_enable_r2 : STD_LOGIC := '0';
- SIGNAL s_axis_tvalid_r2 : STD_LOGIC := '0';
- SIGNAL s_axis_tlast_r2 : STD_LOGIC := '0';
- SIGNAL s_axis_tdata_r2 : STD_LOGIC_VECTOR(CHANNEL_LENGHT - 1 DOWNTO 0) := (OTHERS => '0');
+ -- 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;
- SIGNAL temp_r3 : STD_LOGIC_VECTOR(CHANNEL_LENGHT + TRIANGULAR_COUNTER_LENGHT - 1 DOWNTO 0) := (OTHERS => '0');
- SIGNAL m_axis_tvalid_i : STD_LOGIC := '0';
- SIGNAL s_axis_tready_i : STD_LOGIC := '1';
+ -- 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
+
+ -- 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
+
+ -- 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
- m_axis_tlast <= s_axis_tlast_r1;
+ -- Direct connection: tlast passes through unchanged (maintains channel timing)
+ m_axis_tlast <= last_flag_stage1;
- -- Stage 1: Input registration and LFO period calculation
- PROCESS (aclk)
+ -- 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)
BEGIN
IF rising_edge(aclk) THEN
IF aresetn = '0' THEN
- s_axis_tdata_r1 <= (OTHERS => '0');
- step_clk_cycles <= LFO_COUNTER_BASE_CLK_CYCLES;
- lfo_enable_r1 <= '0';
- s_axis_tvalid_r1 <= '0';
- s_axis_tlast_r1 <= '0';
+ -- 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
ELSE
- -- Set the step_clk_cycles based on the joystick input
- 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;
+ -- Calculate LFO period based on joystick input
+ -- Joystick mapping:
+ -- 0-511: Faster than base frequency (shorter period)
+ -- 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;
- IF s_axis_tvalid = '1' AND s_axis_tready_i = '1' THEN
- s_axis_tdata_r1 <= s_axis_tdata;
- lfo_enable_r1 <= lfo_enable;
- s_axis_tvalid_r1 <= '1';
- s_axis_tlast_r1 <= s_axis_tlast;
+ -- 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
- s_axis_tvalid_r1 <= '0';
+ valid_flag_stage1 <= '0'; -- No valid data to pass to next stage
END IF;
END IF;
END IF;
- END PROCESS;
+ END PROCESS input_processing_stage;
- -- Stage 2: Triangular counter control
- PROCESS (aclk)
+ -- 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
- step_counter_r2 <= 0;
- tri_counter_r2 <= (OTHERS => '0');
- direction_up_r2 <= '1';
- lfo_enable_r2 <= '0';
- s_axis_tvalid_r2 <= '0';
- s_axis_tlast_r2 <= '0';
- s_axis_tdata_r2 <= (OTHERS => '0');
+ -- 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
- lfo_enable_r2 <= lfo_enable_r1;
- s_axis_tvalid_r2 <= s_axis_tvalid_r1;
- s_axis_tlast_r2 <= s_axis_tlast_r1;
- s_axis_tdata_r2 <= s_axis_tdata_r1;
+ -- 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
- IF lfo_enable_r1 = '1' THEN
- IF step_counter_r2 < step_clk_cycles THEN
- step_counter_r2 <= step_counter_r2 + 1;
+ -- 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
- step_counter_r2 <= 0;
- IF direction_up_r2 = '1' THEN
- IF tri_counter_r2 = (2 ** TRIANGULAR_COUNTER_LENGHT) - 1 THEN
- direction_up_r2 <= '0';
- tri_counter_r2 <= tri_counter_r2 - 1;
+ 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
- tri_counter_r2 <= tri_counter_r2 + 1;
+ triangular_wave_value <= triangular_wave_value + 1; -- Continue increasing
END IF;
ELSE
- IF tri_counter_r2 = 0 THEN
- direction_up_r2 <= '1';
- tri_counter_r2 <= tri_counter_r2 + 1;
+ -- 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
- tri_counter_r2 <= tri_counter_r2 - 1;
+ triangular_wave_value <= triangular_wave_value - 1; -- Continue decreasing
END IF;
END IF;
END IF;
ELSE
- step_counter_r2 <= 0;
- tri_counter_r2 <= (OTHERS => '0');
- direction_up_r2 <= '1';
+ -- 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;
+ END PROCESS triangular_wave_generator;
- -- Stage 3: Optimized modulation and output handling
- PROCESS (aclk)
+ -- 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)
BEGIN
IF rising_edge(aclk) THEN
IF aresetn = '0' THEN
- m_axis_tdata <= (OTHERS => '0');
- m_axis_tvalid_i <= '0';
- s_axis_tready_i <= '1';
+ -- 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
- IF m_axis_tvalid_i = '1' AND m_axis_tready = '0' THEN
- m_axis_tvalid_i <= '1';
- ELSIF s_axis_tvalid_r2 = '1' THEN
- IF lfo_enable_r2 = '1' THEN
- temp_r3 <= STD_LOGIC_VECTOR(
+ -- 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
+ -- Apply LFO tremolo effect: multiply audio sample by triangular wave
+ -- This creates amplitude modulation (tremolo effect)
+ multiplication_result <= STD_LOGIC_VECTOR(
resize(
- signed(s_axis_tdata_r2) * signed('0' & tri_counter_r2),
- temp_r3'length
+ signed(audio_data_stage2) * signed('0' & triangular_wave_value),
+ multiplication_result'length
)
- );
- m_axis_tdata <= temp_r3(temp_r3'high DOWNTO TRIANGULAR_COUNTER_LENGHT);
+ );
+ -- 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
- m_axis_tdata <= s_axis_tdata_r2;
+ -- LFO disabled: pass audio through unchanged (bypass mode)
+ -- This allows seamless switching between effect and clean audio
+ m_axis_tdata <= audio_data_stage2;
END IF;
- m_axis_tvalid_i <= '1';
+ master_valid_internal <= '1'; -- Mark output as valid
ELSE
- m_axis_tvalid_i <= '0';
+ -- No new data available: clear output valid flag
+ master_valid_internal <= '0';
END IF;
- -- Ready signal management
- IF m_axis_tvalid_i = '1' AND m_axis_tready = '1' THEN
- s_axis_tready_i <= '1';
- ELSIF s_axis_tvalid = '1' AND s_axis_tready_i = '1' THEN
- s_axis_tready_i <= '0';
+ -- 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;
+ END PROCESS modulation_and_output;
- s_axis_tready <= s_axis_tready_i;
- m_axis_tvalid <= m_axis_tvalid_i;
+ -- 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 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)
+ --
+ -- Audio Effect Characteristics:
+ -- - Tremolo: Periodic amplitude modulation creates "shaking" sound
+ -- - Frequency range: Approximately 0.1Hz to 10Hz (typical for audio LFO)
+ -- - Modulation depth: Controlled by TRIANGULAR_COUNTER_LENGHT generic
+ -- - 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 d432a6e..9fad294 100644
--- a/LAB3/src/LFO_1.vhd
+++ b/LAB3/src/LFO_1.vhd
@@ -5,100 +5,129 @@ USE IEEE.STD_LOGIC_1164.ALL;
-- arithmetic functions with Signed or Unsigned values
USE IEEE.NUMERIC_STD.ALL;
+-- Entity: LFO (Low Frequency Oscillator) - Alternative Implementation
+-- 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
ENTITY LFO IS
GENERIC (
- CHANNEL_LENGHT : INTEGER := 24;
- JOYSTICK_LENGHT : INTEGER := 10;
- CLK_PERIOD_NS : INTEGER := 10;
- TRIANGULAR_COUNTER_LENGHT : INTEGER := 10 -- Triangular wave period length
+ 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
- aclk : IN STD_LOGIC;
- aresetn : IN STD_LOGIC;
+ -- 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
- lfo_period : IN STD_LOGIC_VECTOR(JOYSTICK_LENGHT - 1 DOWNTO 0);
+ -- 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
- lfo_enable : IN STD_LOGIC;
-
- s_axis_tvalid : IN STD_LOGIC;
- s_axis_tdata : IN STD_LOGIC_VECTOR(CHANNEL_LENGHT - 1 DOWNTO 0);
- s_axis_tlast : IN STD_LOGIC;
- s_axis_tready : OUT STD_LOGIC;
-
- m_axis_tvalid : OUT STD_LOGIC;
- m_axis_tdata : OUT STD_LOGIC_VECTOR(CHANNEL_LENGHT - 1 DOWNTO 0);
- m_axis_tlast : OUT STD_LOGIC;
- m_axis_tready : IN STD_LOGIC
+ -- 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
- CONSTANT LFO_COUNTER_BASE_PERIOD_US : INTEGER := 1000; -- 1ms
- CONSTANT ADJUSTMENT_FACTOR : INTEGER := 90;
- CONSTANT JSTK_CENTER_VALUE : INTEGER := 2 ** (JOYSTICK_LENGHT - 1); -- 512 for 10 bits
- CONSTANT LFO_COUNTER_BASE_CLK_CYCLES : INTEGER := LFO_COUNTER_BASE_PERIOD_US * 1000 / CLK_PERIOD_NS; -- 1ms = 100_000 clk cycles
- CONSTANT LFO_CLK_CYCLES_MIN : INTEGER := LFO_COUNTER_BASE_CLK_CYCLES - ADJUSTMENT_FACTOR * (2 ** (JOYSTICK_LENGHT - 1)); -- 53_920 clk cycles
- CONSTANT LFO_CLK_CYCLES_MAX : INTEGER := LFO_COUNTER_BASE_CLK_CYCLES + ADJUSTMENT_FACTOR * (2 ** (JOYSTICK_LENGHT - 1) - 1); -- 145_990 clk cycles
+ -- 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;
- SIGNAL tri_counter : SIGNED(TRIANGULAR_COUNTER_LENGHT DOWNTO 0) := (OTHERS => '0');
- SIGNAL direction_up : STD_LOGIC := '1';
- SIGNAL trigger : STD_LOGIC := '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
- SIGNAL s_axis_tlast_reg : STD_LOGIC := '0';
+ -- 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');
- SIGNAL m_axis_tvalid_int : STD_LOGIC := '0';
BEGIN
- -- Assigning the output signals
- m_axis_tvalid <= m_axis_tvalid_int;
- s_axis_tready <= (m_axis_tready OR NOT m_axis_tvalid_int) AND aresetn;
+ -- 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 step and triangular waveform generation
- 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
- step_clk_cycles <= LFO_COUNTER_BASE_CLK_CYCLES;
- step_counter <= 0;
- tri_counter <= (OTHERS => '0');
- direction_up <= '1';
+ -- 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
- -- Set the step_clk_cycles based on the joystick input
+ -- Calculate LFO period based on joystick input
+ -- Joystick mapping:
+ -- 0-511: Faster than base frequency (shorter period, higher frequency)
+ -- 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_COUNTER_BASE_CLK_CYCLES - step_clk_cycles_delta;
+ -- 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;
+ 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';
+ direction_up <= '0'; -- Switch to descending at near-maximum
ELSIF tri_counter = 1 THEN
- direction_up <= '1';
+ 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;
-
+ tri_counter <= tri_counter + 1; -- Ascending: increment
ELSE
- tri_counter <= tri_counter - 1;
-
+ tri_counter <= tri_counter - 1; -- Descending: decrement
END IF;
ELSE
- step_counter <= step_counter + 1;
+ step_counter <= step_counter + 1; -- Continue counting towards next update
END IF;
@@ -108,62 +137,73 @@ BEGIN
END IF;
- END PROCESS;
+ END PROCESS triangular_wave_lfo_generator;
- -- Handshake logic for the AXIS interface
- AXIS: PROCESS (aclk)
+ -- 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
- s_axis_tlast_reg <= '0';
- m_axis_tdata_temp <= (OTHERS => '0');
- m_axis_tvalid_int <= '0';
- m_axis_tlast <= '0';
+ -- 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
- -- Clear valid flag when master interface is ready
+ -- 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
- IF trigger = '1' AND (m_axis_tvalid_int = '0' OR m_axis_tready = '1') THEN
- m_axis_tdata <= STD_LOGIC_VECTOR(
+ -- 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,
- TRIANGULAR_COUNTER_LENGHT
+ m_axis_tdata_temp, -- Wide multiplication result
+ TRIANGULAR_COUNTER_LENGHT -- Scale factor
),
- CHANNEL_LENGHT
+ CHANNEL_LENGHT -- Final audio sample width
)
);
- m_axis_tlast <= s_axis_tlast_reg;
+ m_axis_tlast <= s_axis_tlast_reg; -- Output registered channel indicator
- m_axis_tvalid_int <= '1';
- trigger <= '0';
-
- END IF;
+ m_axis_tvalid_int <= '1'; -- Mark output as valid
+ trigger <= '0'; -- Clear trigger - data has been output
+
+ END IF;
- -- Data input logic
- IF s_axis_tvalid = '1' AND (m_axis_tready = '1' OR m_axis_tvalid_int = '0') THEN
+ -- 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;
+ 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),
- m_axis_tdata_temp'length
+ signed(s_axis_tdata), -- Convert input to signed
+ m_axis_tdata_temp'length -- Extend to full processing width
),
- TRIANGULAR_COUNTER_LENGHT
+ TRIANGULAR_COUNTER_LENGHT -- Compensate for output scaling
);
- s_axis_tlast_reg <= s_axis_tlast;
+ s_axis_tlast_reg <= s_axis_tlast; -- Register channel indicator
END IF;
- trigger <= '1';
+ trigger <= '1'; -- Set trigger to indicate new processed data is ready
END IF;
@@ -173,4 +213,16 @@ BEGIN
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
+ --
+ -- 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
+
END ARCHITECTURE Behavioral;
\ No newline at end of file
diff --git a/LAB3/src/all_pass_filter.vhd b/LAB3/src/all_pass_filter.vhd
index 37c06c5..364ec50 100644
--- a/LAB3/src/all_pass_filter.vhd
+++ b/LAB3/src/all_pass_filter.vhd
@@ -2,90 +2,123 @@ LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE ieee.numeric_std.ALL;
+-- Entity: all_pass_filter
+-- Purpose: A pass-through filter that maintains the same interface and timing
+-- characteristics as a moving average filter but passes data unchanged.
+-- This is useful for testing or as a baseline comparison.
ENTITY all_pass_filter IS
- GENERIC (
- TDATA_WIDTH : POSITIVE := 24
- );
- PORT (
- aclk : IN STD_LOGIC;
- aresetn : IN STD_LOGIC;
+ GENERIC (
+ TDATA_WIDTH : POSITIVE := 24 -- Width of the data bus in bits
+ );
+ PORT (
+ -- Clock and reset signals
+ aclk : IN STD_LOGIC; -- Main clock input
+ aresetn : IN STD_LOGIC; -- Active-low asynchronous reset
- s_axis_tvalid : IN STD_LOGIC;
- s_axis_tdata : IN STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0);
- s_axis_tlast : IN STD_LOGIC;
- s_axis_tready : OUT STD_LOGIC;
+ -- AXI4-Stream Slave Interface (Input)
+ s_axis_tvalid : IN STD_LOGIC; -- Input data valid signal
+ s_axis_tdata : IN STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0); -- Input data
+ s_axis_tlast : IN STD_LOGIC; -- Input end-of-packet signal
+ s_axis_tready : OUT STD_LOGIC; -- Ready to accept input data
- m_axis_tvalid : OUT STD_LOGIC;
- m_axis_tdata : OUT STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0);
- m_axis_tlast : OUT STD_LOGIC;
- m_axis_tready : IN STD_LOGIC
- );
+ -- AXI4-Stream Master Interface (Output)
+ m_axis_tvalid : OUT STD_LOGIC; -- Output data valid signal
+ m_axis_tdata : OUT STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0); -- Output data
+ m_axis_tlast : OUT STD_LOGIC; -- Output end-of-packet signal
+ m_axis_tready : IN STD_LOGIC -- Downstream ready signal
+ );
END all_pass_filter;
ARCHITECTURE Behavioral OF all_pass_filter IS
- SIGNAL trigger : STD_LOGIC := '0';
+ -- Internal control signal to trigger data output after input processing
+ SIGNAL trigger : STD_LOGIC := '0';
- SIGNAL s_axis_tready_int : STD_LOGIC := '0';
- SIGNAL s_axis_tlast_reg : STD_LOGIC := '0';
- SIGNAL m_axis_tdata_temp : STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0) := (OTHERS => '0');
- SIGNAL m_axis_tvalid_int : STD_LOGIC := '0';
+ -- Internal slave interface signals
+ SIGNAL s_axis_tready_int : STD_LOGIC := '0'; -- Internal ready signal for input
+ SIGNAL s_axis_tlast_reg : STD_LOGIC := '0'; -- Registered version of input tlast
+
+ -- Internal data storage and master interface signals
+ SIGNAL m_axis_tdata_temp : STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0) := (OTHERS => '0'); -- Temporary storage for input data
+ SIGNAL m_axis_tvalid_int : STD_LOGIC := '0'; -- Internal valid signal for output
BEGIN
- -- This architecture mimics the structure, handshake logic, and timing (clock cycles spent)
+ -- Architecture Overview:
+ -- This design mimics the structure, handshake logic, and timing (clock cycles spent)
-- of the moving_average_filter, but does not process or modify the samples.
-- It simply passes input data to the output unchanged, ensuring the same latency and interface behavior.
- -- Assigning the output signals
- m_axis_tvalid <= m_axis_tvalid_int;
- s_axis_tready <= s_axis_tready_int;
+ -- Data Flow:
+ -- 1. Input data is captured when s_axis_tvalid and s_axis_tready are both high
+ -- 2. Data is stored in temporary registers and a trigger is set
+ -- 3. On the next clock cycle, if output interface is ready, data is output
+ -- 4. This creates a 1-clock cycle latency matching the moving average filter
- PROCESS (aclk)
- BEGIN
- IF rising_edge(aclk) THEN
+ -- Connect internal signals to output ports
+ m_axis_tvalid <= m_axis_tvalid_int; -- Drive output valid with internal signal
+ s_axis_tready <= s_axis_tready_int; -- Drive input ready with internal signal
- IF aresetn = '0' THEN
- -- Reset all internal signals
- trigger <= '0';
+ -- Main processing logic - synchronous to clock
+ PROCESS (aclk)
+ BEGIN
+ IF rising_edge(aclk) THEN
- s_axis_tlast_reg <= '0';
- s_axis_tready_int <= '0';
- m_axis_tdata_temp <= (OTHERS => '0');
- m_axis_tvalid_int <= '0';
- m_axis_tlast <= '0';
+ -- Asynchronous reset logic (active low)
+ IF aresetn = '0' THEN
+ -- Reset all internal signals to known states
+ trigger <= '0'; -- Clear data processing trigger
+ s_axis_tlast_reg <= '0'; -- Clear registered tlast
+ s_axis_tready_int <= '0'; -- Not ready to accept data during reset
+ m_axis_tdata_temp <= (OTHERS => '0'); -- Clear temporary data storage
+ m_axis_tvalid_int <= '0'; -- No valid data on output during reset
+ m_axis_tlast <= '0'; -- Clear output tlast
- ELSE
- -- Clear valid flag when master interface is ready
- IF m_axis_tready = '1' THEN
- m_axis_tvalid_int <= '0';
- END IF;
+ ELSE
+ -- Normal operation logic
- -- Data output logic
- IF trigger = '1' AND (m_axis_tvalid_int = '0' OR m_axis_tready = '1') THEN
- m_axis_tdata <= m_axis_tdata_temp;
- m_axis_tlast <= s_axis_tlast_reg;
+ -- Output handshake management:
+ -- Clear the output valid flag when downstream is ready to accept data
+ -- This allows new data to be output on the next cycle
+ IF m_axis_tready = '1' THEN
+ m_axis_tvalid_int <= '0';
+ END IF;
- m_axis_tvalid_int <= '1';
- trigger <= '0';
-
- END IF;
+ -- Data output logic:
+ -- Output data when trigger is set AND output interface is available
+ -- (either no valid data pending OR downstream is ready)
+ IF trigger = '1' AND (m_axis_tvalid_int = '0' OR m_axis_tready = '1') THEN
+ -- Transfer stored data to output
+ m_axis_tdata <= m_axis_tdata_temp; -- Output the stored input data unchanged
+ m_axis_tlast <= s_axis_tlast_reg; -- Output the registered tlast signal
+
+ -- Set output control signals
+ m_axis_tvalid_int <= '1'; -- Mark output data as valid
+ trigger <= '0'; -- Clear trigger - data has been output
+ END IF;
- -- Data input logic
- IF s_axis_tvalid = '1' AND s_axis_tready_int = '1' THEN
- s_axis_tlast_reg <= s_axis_tlast;
- m_axis_tdata_temp <= s_axis_tdata;
-
- trigger <= '1';
+ -- Data input logic:
+ -- Capture input data when both valid and ready are asserted (AXI handshake)
+ IF s_axis_tvalid = '1' AND s_axis_tready_int = '1' THEN
+ -- Store input signals for later output
+ s_axis_tlast_reg <= s_axis_tlast; -- Register the tlast signal
+ m_axis_tdata_temp <= s_axis_tdata; -- Store input data (pass-through, no processing)
+
+ -- Set trigger to indicate data is ready for output
+ trigger <= '1';
+ END IF;
- END IF;
+ -- Input ready logic:
+ -- Ready to accept new input when:
+ -- - Downstream is ready (can immediately pass data through), OR
+ -- - No valid data is pending on output (have buffer space)
+ -- This implements backpressure - if output is blocked, input will be blocked
+ s_axis_tready_int <= m_axis_tready OR NOT m_axis_tvalid_int;
- s_axis_tready_int <= m_axis_tready OR NOT m_axis_tvalid_int;
+ END IF;
- END IF;
+ END IF;
- END IF;
-
- END PROCESS;
+ END PROCESS;
END Behavioral;
\ No newline at end of file
diff --git a/LAB3/src/balance_controller.vhd b/LAB3/src/balance_controller.vhd
index 9941019..4586d60 100644
--- a/LAB3/src/balance_controller.vhd
+++ b/LAB3/src/balance_controller.vhd
@@ -2,112 +2,146 @@ LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE ieee.numeric_std.ALL;
+-- Entity: balance_controller
+-- Purpose: Controls the stereo balance (left/right channel volume) of audio data
+-- based on a balance control input. Implements variable attenuation
+-- using bit shifting for efficient hardware implementation.
ENTITY balance_controller IS
- GENERIC (
- TDATA_WIDTH : POSITIVE := 24;
- BALANCE_WIDTH : POSITIVE := 10;
- BALANCE_STEP_2 : POSITIVE := 6 -- i.e., balance_values_per_step = 2**BALANCE_STEP_2
- );
- PORT (
- aclk : IN STD_LOGIC;
- aresetn : IN STD_LOGIC;
+ GENERIC (
+ TDATA_WIDTH : POSITIVE := 24; -- Width of audio data bus (24-bit audio samples)
+ BALANCE_WIDTH : POSITIVE := 10; -- Width of balance control input (10-bit = 0-1023 range)
+ BALANCE_STEP_2 : POSITIVE := 6 -- Log2 of balance values per step (2^6 = 64 values per step)
+ );
+ PORT (
+ -- Clock and reset
+ aclk : IN STD_LOGIC; -- Main clock
+ aresetn : IN STD_LOGIC; -- Active-low asynchronous reset
- s_axis_tvalid : IN STD_LOGIC;
- s_axis_tdata : IN STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0);
- s_axis_tready : OUT STD_LOGIC;
- s_axis_tlast : IN STD_LOGIC;
+ -- AXI4-Stream Slave Interface (Audio Input)
+ s_axis_tvalid : IN STD_LOGIC; -- Input data valid
+ s_axis_tdata : IN STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0); -- Audio sample data
+ s_axis_tready : OUT STD_LOGIC; -- Ready to accept input
+ s_axis_tlast : IN STD_LOGIC; -- Channel indicator (0=left, 1=right)
- m_axis_tvalid : OUT STD_LOGIC;
- m_axis_tdata : OUT STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0);
- m_axis_tready : IN STD_LOGIC;
- m_axis_tlast : OUT STD_LOGIC;
+ -- AXI4-Stream Master Interface (Audio Output)
+ m_axis_tvalid : OUT STD_LOGIC; -- Output data valid
+ m_axis_tdata : OUT STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0); -- Balanced audio sample
+ m_axis_tready : IN STD_LOGIC; -- Downstream ready
+ m_axis_tlast : OUT STD_LOGIC; -- Channel indicator passthrough
- balance : IN STD_LOGIC_VECTOR(BALANCE_WIDTH - 1 DOWNTO 0)
- );
+ -- Balance Control Input
+ balance : IN STD_LOGIC_VECTOR(BALANCE_WIDTH - 1 DOWNTO 0) -- Balance position (0=full left, 1023=full right, 512=center)
+ );
END balance_controller;
ARCHITECTURE Behavioral OF balance_controller IS
- CONSTANT BALANCE_STEPS : INTEGER := (2 ** (BALANCE_WIDTH - 1)) / (2 ** BALANCE_STEP_2) + 1;
- CONSTANT BAL_MID : INTEGER := 2 ** (BALANCE_WIDTH - 1); -- 512 for 10 bit
- CONSTANT DEAD_ZONE : INTEGER := (2 ** BALANCE_STEP_2) / 2;
+ -- Balance calculation constants
+ CONSTANT BALANCE_STEPS : INTEGER := (2 ** (BALANCE_WIDTH - 1)) / (2 ** BALANCE_STEP_2) + 1; -- Number of attenuation steps (9 steps for 10-bit balance)
+ CONSTANT BAL_MID : INTEGER := 2 ** (BALANCE_WIDTH - 1); -- Center balance position (512 for 10-bit)
+ CONSTANT DEAD_ZONE : INTEGER := (2 ** BALANCE_STEP_2) / 2; -- Dead zone around center (32 for step size 64)
- SIGNAL left_channel : INTEGER RANGE 0 TO BALANCE_STEPS := 0;
- SIGNAL right_channel : INTEGER RANGE 0 TO BALANCE_STEPS := 0;
+ -- Channel attenuation levels (number of bit shifts for volume reduction)
+ SIGNAL left_channel : INTEGER RANGE 0 TO BALANCE_STEPS := 0; -- Left channel attenuation (0 = no attenuation, higher = more attenuation)
+ SIGNAL right_channel : INTEGER RANGE 0 TO BALANCE_STEPS := 0; -- Right channel attenuation (0 = no attenuation, higher = more attenuation)
- SIGNAL m_axis_tvalid_int : STD_LOGIC;
+ -- Internal AXI signals
+ SIGNAL m_axis_tvalid_int : STD_LOGIC; -- Internal valid signal for output
BEGIN
- -- Assigning the output signals
- m_axis_tvalid <= m_axis_tvalid_int;
- s_axis_tready <= (m_axis_tready OR NOT m_axis_tvalid_int) AND aresetn;
+ -- Connect internal signals to output ports
+ m_axis_tvalid <= m_axis_tvalid_int;
+ -- Input ready logic: Ready when downstream is ready OR no valid data pending, AND not in reset
+ s_axis_tready <= (m_axis_tready OR NOT m_axis_tvalid_int) AND aresetn;
- -- Balance to exp process to avoid changing the balance value when multiplying it for the sample data
- BALANCE_CALC : PROCESS (aclk)
- BEGIN
+ -- Balance calculation process
+ -- Converts balance knob position to attenuation levels for each channel
+ BALANCE_CALC : PROCESS (aclk)
+ BEGIN
- IF rising_edge(aclk) THEN
+ IF rising_edge(aclk) THEN
- IF aresetn = '0' THEN
- left_channel <= 0;
- right_channel <= 0;
+ IF aresetn = '0' THEN
+ -- Reset: No attenuation on either channel
+ left_channel <= 0;
+ right_channel <= 0;
- ELSE
- -- Balance left and right channels
- IF to_integer(unsigned(balance)) > (BAL_MID + DEAD_ZONE) THEN
- left_channel <= to_integer((unsigned(balance) - to_unsigned(BAL_MID + DEAD_ZONE, balance'length)) SRL BALANCE_STEP_2) + 1; -- +1 due to shift approximation defect
- ELSE
- left_channel <= 0;
- END IF;
+ ELSE
+ -- Balance Mapping:
+ -- balance = 0-479: Left (right channel attenuated)
+ -- balance = 480-543: Center with dead zone (no attenuation)
+ -- balance = 544-1023: Right (left channel attenuated)
- IF to_integer(unsigned(balance)) < (BAL_MID - DEAD_ZONE) THEN
- right_channel <= to_integer((to_unsigned(BAL_MID - DEAD_ZONE, balance'length) - unsigned(balance)) SRL BALANCE_STEP_2) + 1;
- ELSE
- right_channel <= 0;
- END IF;
+ -- Right-leaning balance: Attenuate left channel
+ IF to_integer(unsigned(balance)) > (BAL_MID + DEAD_ZONE) THEN
+ -- Calculate left channel attenuation based on how far right of center
+ -- Subtract center+deadzone, divide by step size, add 1 for rounding
+ left_channel <= to_integer((unsigned(balance) - to_unsigned(BAL_MID + DEAD_ZONE, balance'length)) SRL BALANCE_STEP_2) + 1;
+ ELSE
+ -- Balance not significantly right of center: no left attenuation
+ left_channel <= 0;
+ END IF;
- END IF;
+ -- Left-leaning balance: Attenuate right channel
+ IF to_integer(unsigned(balance)) < (BAL_MID - DEAD_ZONE) THEN
+ -- Calculate right channel attenuation based on how far left of center
+ -- Subtract balance from center-deadzone, divide by step size, add 1 for rounding
+ right_channel <= to_integer((to_unsigned(BAL_MID - DEAD_ZONE, balance'length) - unsigned(balance)) SRL BALANCE_STEP_2) + 1;
+ ELSE
+ -- Balance not significantly left of center: no right attenuation
+ right_channel <= 0;
+ END IF;
- END IF;
+ END IF;
- END PROCESS BALANCE_CALC;
+ END IF;
- -- Handle AXIS stream
- AXIS : PROCESS (aclk)
- BEGIN
+ END PROCESS BALANCE_CALC;
- IF rising_edge(aclk) THEN
+ -- AXI4-Stream data processing
+ -- Applies calculated attenuation to audio samples based on channel
+ AXIS : PROCESS (aclk)
+ BEGIN
- IF aresetn = '0' THEN
- m_axis_tvalid_int <= '0';
- m_axis_tlast <= '0';
+ IF rising_edge(aclk) THEN
- ELSE
- -- Clear valid flag when master interface is ready
- IF m_axis_tready = '1' THEN
- m_axis_tvalid_int <= '0';
- END IF;
+ IF aresetn = '0' THEN
+ -- Reset output interface
+ m_axis_tvalid_int <= '0'; -- No valid output data
+ m_axis_tlast <= '0'; -- Clear channel indicator
- -- Handle the data flow
- IF s_axis_tvalid = '1' AND m_axis_tready = '1' THEN
- -- Joystick datasheet: (x-axis) a 0 value corresponds to the axis
- -- being tilted fully to the left and a value of 1023
- -- corresponds fully to the right
- IF s_axis_tlast = '0' THEN -- left
- m_axis_tdata <= STD_LOGIC_VECTOR(shift_right(signed(s_axis_tdata), left_channel));
- ELSE -- right
- m_axis_tdata <= STD_LOGIC_VECTOR(shift_right(signed(s_axis_tdata), right_channel));
- END IF;
+ ELSE
+ -- Output handshake: Clear valid flag when downstream accepts data
+ IF m_axis_tready = '1' THEN
+ m_axis_tvalid_int <= '0';
+ END IF;
- m_axis_tvalid_int <= '1';
- m_axis_tlast <= s_axis_tlast;
+ -- Data processing: Apply balance when input and output are ready
+ IF s_axis_tvalid = '1' AND m_axis_tready = '1' THEN
+ -- Channel identification based on tlast signal:
+ -- tlast = '0': Left channel sample
+ -- tlast = '1': Right channel sample
+
+ IF s_axis_tlast = '0' THEN
+ -- Left channel: Apply left channel attenuation
+ -- Arithmetic right shift preserves sign for signed audio data
+ m_axis_tdata <= STD_LOGIC_VECTOR(shift_right(signed(s_axis_tdata), left_channel));
+ ELSE
+ -- Right channel: Apply right channel attenuation
+ -- Arithmetic right shift preserves sign for signed audio data
+ m_axis_tdata <= STD_LOGIC_VECTOR(shift_right(signed(s_axis_tdata), right_channel));
+ END IF;
- END IF;
+ -- Set output control signals
+ m_axis_tvalid_int <= '1'; -- Mark output as valid
+ m_axis_tlast <= s_axis_tlast; -- Pass through channel indicator
- END IF;
+ END IF;
- END IF;
+ END IF;
- END PROCESS AXIS;
+ END IF;
+
+ END PROCESS AXIS;
END Behavioral;
\ No newline at end of file
diff --git a/LAB3/src/digilent_jstk2.vhd b/LAB3/src/digilent_jstk2.vhd
index 4fa024b..6c9599f 100644
--- a/LAB3/src/digilent_jstk2.vhd
+++ b/LAB3/src/digilent_jstk2.vhd
@@ -1,180 +1,200 @@
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
+-- Entity: digilent_jstk2
+-- Purpose: Interface controller for the Digilent JSTK2 joystick module via SPI
+-- Sends LED color commands and receives joystick/button data
ENTITY digilent_jstk2 IS
- GENERIC (
- DELAY_US : INTEGER := 225; -- Delay (in us) between two packets
- -- 25us Required by the SPI IP-Core doesn't work,
- -- it requires another SPI clock cycle
- CLKFREQ : INTEGER := 100_000_000; -- Frequency of the aclk signal (in Hz)
- SPI_SCLKFREQ : INTEGER := 5_000 -- Frequency of the SPI SCLK clock signal (in Hz)
- );
- PORT (
- aclk : IN STD_LOGIC;
- aresetn : IN STD_LOGIC;
+ GENERIC (
+ DELAY_US : INTEGER := 225; -- Delay (in microseconds) between two SPI packets
+ -- 25us required by SPI IP-Core doesn't work,
+ -- it requires another SPI clock cycle
+ CLKFREQ : INTEGER := 100_000_000; -- Frequency of the aclk signal (in Hz)
+ SPI_SCLKFREQ : INTEGER := 5_000 -- Frequency of the SPI SCLK clock signal (in Hz)
+ );
+ PORT (
+ aclk : IN STD_LOGIC; -- Main clock input
+ aresetn : IN STD_LOGIC; -- Active-low asynchronous reset
- -- Data going TO the SPI IP-Core (and so, to the JSTK2 module)
- m_axis_tvalid : OUT STD_LOGIC;
- m_axis_tdata : OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
- m_axis_tready : IN STD_LOGIC;
+ -- AXI4-Stream Master Interface: Data going TO the SPI IP-Core (and so, to the JSTK2 module)
+ m_axis_tvalid : OUT STD_LOGIC; -- Output data valid signal
+ m_axis_tdata : OUT STD_LOGIC_VECTOR(7 DOWNTO 0); -- 8-bit data to send via SPI
+ m_axis_tready : IN STD_LOGIC; -- SPI IP-Core ready to accept data
- -- Data coming FROM the SPI IP-Core (and so, from the JSTK2 module)
- -- There is no tready signal, so you must be always ready to accept and use the incoming data, or it will be lost!
- s_axis_tvalid : IN STD_LOGIC;
- s_axis_tdata : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
+ -- AXI4-Stream Slave Interface: Data coming FROM the SPI IP-Core (and so, from the JSTK2 module)
+ -- Note: There is no tready signal, so you must be always ready to accept incoming data, or it will be lost!
+ s_axis_tvalid : IN STD_LOGIC; -- Input data valid signal
+ s_axis_tdata : IN STD_LOGIC_VECTOR(7 DOWNTO 0); -- 8-bit data received via SPI
- -- Joystick and button values read from the module
- jstk_x : OUT STD_LOGIC_VECTOR(9 DOWNTO 0);
- jstk_y : OUT STD_LOGIC_VECTOR(9 DOWNTO 0);
- btn_jstk : OUT STD_LOGIC;
- btn_trigger : OUT STD_LOGIC;
+ -- Joystick and button values read from the JSTK2 module
+ jstk_x : OUT STD_LOGIC_VECTOR(9 DOWNTO 0); -- X-axis joystick position (10-bit, 0-1023)
+ jstk_y : OUT STD_LOGIC_VECTOR(9 DOWNTO 0); -- Y-axis joystick position (10-bit, 0-1023)
+ btn_jstk : OUT STD_LOGIC; -- Joystick button state (1=pressed)
+ btn_trigger : OUT STD_LOGIC; -- Trigger button state (1=pressed)
- -- LED color to send to the module
- led_r : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
- led_g : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
- led_b : IN STD_LOGIC_VECTOR(7 DOWNTO 0)
- );
+ -- LED color values to send to the JSTK2 module
+ led_r : IN STD_LOGIC_VECTOR(7 DOWNTO 0); -- Red LED intensity (0-255)
+ led_g : IN STD_LOGIC_VECTOR(7 DOWNTO 0); -- Green LED intensity (0-255)
+ led_b : IN STD_LOGIC_VECTOR(7 DOWNTO 0) -- Blue LED intensity (0-255)
+ );
END digilent_jstk2;
ARCHITECTURE Behavioral OF digilent_jstk2 IS
- -- Code for the SetLEDRGB command, see the JSTK2 datasheet.
- CONSTANT CMDSETLEDRGB : STD_LOGIC_VECTOR(7 DOWNTO 0) := x"84";
+ -- Command code for the SetLEDRGB command, see the JSTK2 datasheet
+ CONSTANT CMDSETLEDRGB : STD_LOGIC_VECTOR(7 DOWNTO 0) := x"84";
- -- Do not forget that you MUST wait a bit between two packets. See the JSTK2 datasheet (and the SPI IP-Core README).
- ------------------------------------------------------------
+ -- Calculate delay in clock cycles based on microsecond delay and clock frequency
+ -- Important: You MUST wait between two SPI packets (see JSTK2 datasheet and SPI IP-Core README)
+ CONSTANT DELAY_CLK_CYCLES : INTEGER := DELAY_US * (CLKFREQ / 1_000_000);
- CONSTANT DELAY_CLK_CYCLES : INTEGER := DELAY_US * (CLKFREQ / 1_000_000);
+ -- State machine type definitions
+ TYPE tx_state_type IS (DELAY, SEND_CMD, SEND_RED, SEND_GREEN, SEND_BLUE, SEND_DUMMY);
+ TYPE rx_state_type IS (JSTK_X_LOW, JSTK_X_HIGH, JSTK_Y_LOW, JSTK_Y_HIGH, BUTTONS);
- -- State machine states
- TYPE tx_state_type IS (DELAY, SEND_CMD, SEND_RED, SEND_GREEN, SEND_BLUE, SEND_DUMMY);
- TYPE rx_state_type IS (JSTK_X_LOW, JSTK_X_HIGH, JSTK_Y_LOW, JSTK_Y_HIGH, BUTTONS);
+ -- State machine signals
+ SIGNAL tx_state : tx_state_type := DELAY; -- Transmit state machine current state
+ SIGNAL rx_state : rx_state_type := JSTK_X_LOW; -- Receive state machine current state
- SIGNAL tx_state : tx_state_type := DELAY;
- SIGNAL rx_state : rx_state_type := JSTK_X_LOW;
-
- SIGNAL tx_delay_counter : INTEGER := 0;
-
- SIGNAL rx_cache : STD_LOGIC_VECTOR(7 DOWNTO 0);
+ -- Timing and data storage signals
+ SIGNAL tx_delay_counter : INTEGER := 0; -- Counter for inter-packet delay timing
+ SIGNAL rx_cache : STD_LOGIC_VECTOR(7 DOWNTO 0); -- Temporary storage for multi-byte data reception
BEGIN
- -- The SPI IP-Core is a slave, so we must set the m_axis_tvalid signal to '1' when we want to send data to it.
- WITH tx_state SELECT m_axis_tvalid <=
- '0' WHEN DELAY,
- '1' WHEN SEND_CMD,
- '1' WHEN SEND_RED,
- '1' WHEN SEND_GREEN,
- '1' WHEN SEND_BLUE,
- '1' WHEN SEND_DUMMY;
- -- Send the data to the SPI IP-Core based on the current state of the TX FSM
- WITH tx_state SELECT m_axis_tdata <=
- (OTHERS => '0') WHEN DELAY,
- CMDSETLEDRGB WHEN SEND_CMD,
- led_r WHEN SEND_RED,
- led_g WHEN SEND_GREEN,
- led_b WHEN SEND_BLUE,
- "01101000" WHEN SEND_DUMMY; -- Dummy byte
+ -- Output valid signal control: Set to '1' when we want to send data to SPI IP-Core
+ -- Only inactive during DELAY state when waiting between packets
+ WITH tx_state SELECT m_axis_tvalid <=
+ '0' WHEN DELAY, -- No transmission during delay
+ '1' WHEN SEND_CMD, -- Send command byte
+ '1' WHEN SEND_RED, -- Send red LED value
+ '1' WHEN SEND_GREEN, -- Send green LED value
+ '1' WHEN SEND_BLUE, -- Send blue LED value
+ '1' WHEN SEND_DUMMY; -- Send dummy byte to complete transaction
- -- TX FSM: invia un nuovo comando solo dopo che la risposta precedente è stata ricevuta (rx_done = '1')
- TX : PROCESS (aclk)
- BEGIN
- IF rising_edge(aclk) THEN
- IF aresetn = '0' THEN
+ -- Output data multiplexer: Select what data to send based on current TX state
+ WITH tx_state SELECT m_axis_tdata <=
+ (OTHERS => '0') WHEN DELAY, -- No data during delay
+ CMDSETLEDRGB WHEN SEND_CMD, -- SetLEDRGB command (0x84)
+ led_r WHEN SEND_RED, -- Red LED intensity value
+ led_g WHEN SEND_GREEN, -- Green LED intensity value
+ led_b WHEN SEND_BLUE, -- Blue LED intensity value
+ "01101001" WHEN SEND_DUMMY; -- Dummy byte to complete 5-byte transaction
- tx_state <= DELAY;
- tx_delay_counter <= 0;
+ -- TX State Machine: Sends LED color commands to JSTK2 module
+ -- Protocol: Command(1) + Red(1) + Green(1) + Blue(1) + Dummy(1) = 5 bytes total
+ TX : PROCESS (aclk)
+ BEGIN
+ IF rising_edge(aclk) THEN
+ IF aresetn = '0' THEN
+ -- Reset: Start in delay state with counter cleared
+ tx_state <= DELAY;
+ tx_delay_counter <= 0;
- ELSE
+ ELSE
- CASE tx_state IS
+ CASE tx_state IS
- WHEN DELAY =>
- IF tx_delay_counter >= DELAY_CLK_CYCLES THEN
- tx_delay_counter <= 0;
- tx_state <= SEND_CMD;
- ELSE
- tx_delay_counter <= tx_delay_counter + 1;
- END IF;
+ WHEN DELAY =>
+ -- Wait for required delay period between SPI transactions
+ IF tx_delay_counter >= DELAY_CLK_CYCLES THEN
+ tx_delay_counter <= 0; -- Reset counter
+ tx_state <= SEND_CMD; -- Start new transmission
+ ELSE
+ tx_delay_counter <= tx_delay_counter + 1; -- Continue counting
+ END IF;
- WHEN SEND_CMD =>
- IF m_axis_tready = '1' THEN
- tx_state <= SEND_RED;
- END IF;
+ WHEN SEND_CMD =>
+ -- Send SetLEDRGB command byte
+ IF m_axis_tready = '1' THEN
+ tx_state <= SEND_RED; -- Move to red LED transmission
+ END IF;
- WHEN SEND_RED =>
- IF m_axis_tready = '1' THEN
- tx_state <= SEND_GREEN;
- END IF;
+ WHEN SEND_RED =>
+ -- Send red LED intensity value
+ IF m_axis_tready = '1' THEN
+ tx_state <= SEND_GREEN; -- Move to green LED transmission
+ END IF;
- WHEN SEND_GREEN =>
- IF m_axis_tready = '1' THEN
- tx_state <= SEND_BLUE;
- END IF;
+ WHEN SEND_GREEN =>
+ -- Send green LED intensity value
+ IF m_axis_tready = '1' THEN
+ tx_state <= SEND_BLUE; -- Move to blue LED transmission
+ END IF;
- WHEN SEND_BLUE =>
- IF m_axis_tready = '1' THEN
- tx_state <= SEND_DUMMY;
- END IF;
+ WHEN SEND_BLUE =>
+ -- Send blue LED intensity value
+ IF m_axis_tready = '1' THEN
+ tx_state <= SEND_DUMMY; -- Move to dummy byte transmission
+ END IF;
- WHEN SEND_DUMMY =>
- IF m_axis_tready = '1' THEN
- tx_state <= DELAY;
- END IF;
+ WHEN SEND_DUMMY =>
+ -- Send dummy byte to complete 5-byte transaction
+ IF m_axis_tready = '1' THEN
+ tx_state <= DELAY; -- Return to delay state
+ END IF;
- END CASE;
- END IF;
- END IF;
- END PROCESS TX;
+ END CASE;
+ END IF;
+ END IF;
+ END PROCESS TX;
- -- RX FSM: riceve 5 byte, aggiorna le uscite e segnala a TX FSM quando la risposta è completa
- RX : PROCESS (aclk)
- BEGIN
- IF rising_edge(aclk) THEN
+ -- RX State Machine: Receives 5 bytes of response data and updates outputs
+ -- Protocol: X_low(1) + X_high(1) + Y_low(1) + Y_high(1) + Buttons(1) = 5 bytes total
+ RX : PROCESS (aclk)
+ BEGIN
+ IF rising_edge(aclk) THEN
- IF aresetn = '0' THEN
+ IF aresetn = '0' THEN
+ -- Reset: Start waiting for X-axis low byte
+ rx_state <= JSTK_X_LOW;
+ rx_cache <= (OTHERS => '0');
- rx_state <= JSTK_X_LOW;
- rx_cache <= (OTHERS => '0');
+ ELSE
- ELSE
+ CASE rx_state IS
- CASE rx_state IS
+ WHEN JSTK_X_LOW =>
+ -- Receive X-axis low byte (bits 7:0)
+ IF s_axis_tvalid = '1' THEN
+ rx_cache <= s_axis_tdata; -- Store low byte temporarily
+ rx_state <= JSTK_X_HIGH; -- Wait for high byte
+ END IF;
- WHEN JSTK_X_LOW =>
- IF s_axis_tvalid = '1' THEN
- rx_cache <= s_axis_tdata;
- rx_state <= JSTK_X_HIGH;
- END IF;
+ WHEN JSTK_X_HIGH =>
+ -- Receive X-axis high byte (bits 9:8) and assemble complete X value
+ IF s_axis_tvalid = '1' THEN
+ -- Combine: high_byte(1:0) & low_byte(7:0) = 10-bit X position
+ jstk_x <= s_axis_tdata(1 DOWNTO 0) & rx_cache;
+ rx_state <= JSTK_Y_LOW; -- Move to Y-axis reception
+ END IF;
- WHEN JSTK_X_HIGH =>
- IF s_axis_tvalid = '1' THEN
- jstk_x <= s_axis_tdata(1 DOWNTO 0) & rx_cache;
- rx_state <= JSTK_Y_LOW;
- END IF;
+ WHEN JSTK_Y_LOW =>
+ -- Receive Y-axis low byte (bits 7:0)
+ IF s_axis_tvalid = '1' THEN
+ rx_cache <= s_axis_tdata; -- Store low byte temporarily
+ rx_state <= JSTK_Y_HIGH; -- Wait for high byte
+ END IF;
- WHEN JSTK_Y_LOW =>
- IF s_axis_tvalid = '1' THEN
- rx_cache <= s_axis_tdata;
- rx_state <= JSTK_Y_HIGH;
- END IF;
+ WHEN JSTK_Y_HIGH =>
+ -- Receive Y-axis high byte (bits 9:8) and assemble complete Y value
+ IF s_axis_tvalid = '1' THEN
+ -- Combine: high_byte(1:0) & low_byte(7:0) = 10-bit Y position
+ jstk_y <= s_axis_tdata(1 DOWNTO 0) & rx_cache;
+ rx_state <= BUTTONS; -- Move to button reception
+ END IF;
- WHEN JSTK_Y_HIGH =>
- IF s_axis_tvalid = '1' THEN
- jstk_y <= s_axis_tdata(1 DOWNTO 0) & rx_cache;
- rx_state <= BUTTONS;
- END IF;
+ WHEN BUTTONS =>
+ -- Receive button states byte
+ IF s_axis_tvalid = '1' THEN
+ btn_jstk <= s_axis_tdata(0); -- Joystick button (bit 0)
+ btn_trigger <= s_axis_tdata(1); -- Trigger button (bit 1)
+ rx_state <= JSTK_X_LOW; -- Return to start for next packet
+ END IF;
- WHEN BUTTONS =>
- IF s_axis_tvalid = '1' THEN
- btn_jstk <= s_axis_tdata(0);
- btn_trigger <= s_axis_tdata(1);
- rx_state <= JSTK_X_LOW;
- END IF;
-
- END CASE;
- END IF;
- END IF;
- END PROCESS RX;
+ END CASE;
+ END IF;
+ END IF;
+ END PROCESS RX;
END ARCHITECTURE;
\ No newline at end of file
diff --git a/LAB3/src/effect_selector.vhd b/LAB3/src/effect_selector.vhd
index 3936ea5..21eb378 100644
--- a/LAB3/src/effect_selector.vhd
+++ b/LAB3/src/effect_selector.vhd
@@ -1,20 +1,27 @@
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
+-- Entity: effect_selector
+-- Purpose: Routes joystick input to different audio control parameters based on effect mode
+-- Acts as a multiplexer/router for joystick control signals
ENTITY effect_selector IS
GENERIC (
- JOYSTICK_LENGHT : INTEGER := 10
+ JOYSTICK_LENGHT : INTEGER := 10 -- Width of joystick position data (10-bit = 0-1023 range)
);
PORT (
- aclk : IN STD_LOGIC;
- aresetn : IN STD_LOGIC;
+ -- Clock and reset signals
+ aclk : IN STD_LOGIC; -- Main clock input
+ aresetn : IN STD_LOGIC; -- Active-low asynchronous reset
- effect : IN STD_LOGIC;
- jstck_x : IN STD_LOGIC_VECTOR(JOYSTICK_LENGHT - 1 DOWNTO 0);
- jstck_y : IN STD_LOGIC_VECTOR(JOYSTICK_LENGHT - 1 DOWNTO 0);
- volume : OUT STD_LOGIC_VECTOR(JOYSTICK_LENGHT - 1 DOWNTO 0);
- balance : OUT STD_LOGIC_VECTOR(JOYSTICK_LENGHT - 1 DOWNTO 0);
- lfo_period : OUT STD_LOGIC_VECTOR(JOYSTICK_LENGHT - 1 DOWNTO 0)
+ -- Control and input signals
+ effect : IN STD_LOGIC; -- Effect mode selector (0=volume/balance mode, 1=LFO/balance mode)
+ 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
+
+ -- Output control parameters
+ volume : OUT STD_LOGIC_VECTOR(JOYSTICK_LENGHT - 1 DOWNTO 0); -- Volume control output
+ balance : OUT STD_LOGIC_VECTOR(JOYSTICK_LENGHT - 1 DOWNTO 0); -- Balance control output
+ lfo_period : OUT STD_LOGIC_VECTOR(JOYSTICK_LENGHT - 1 DOWNTO 0) -- LFO period control output
);
END effect_selector;
@@ -22,27 +29,39 @@ ARCHITECTURE Behavioral OF effect_selector IS
BEGIN
+ -- Main control logic process
+ -- Routes joystick axes to appropriate audio control parameters based on selected mode
PROCESS (aclk)
BEGIN
IF rising_edge(aclk) THEN
IF aresetn = '0' THEN
- volume <= (OTHERS => '0');
- balance <= (OTHERS => '0');
- lfo_period <= (OTHERS => '0');
+ -- 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
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
IF effect = '1' THEN
- -- LFO control
+ -- 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 (not updated in LFO mode)
ELSE
- -- volume/balance control
+ -- Volume/Balance Mode: Y-axis controls overall volume level
+ -- Traditional audio mixer control mode
volume <= jstck_y;
-
+ -- LFO period remains at last set value (not updated in volume mode)
+
END IF;
END IF;
diff --git a/LAB3/src/led_controller.vhd b/LAB3/src/led_controller.vhd
index ebeae91..8c7e198 100644
--- a/LAB3/src/led_controller.vhd
+++ b/LAB3/src/led_controller.vhd
@@ -1,37 +1,64 @@
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
+-- Entity: led_controller
+-- Purpose: Controls RGB LED indicators based on audio system status
+-- Provides visual feedback for mute and filter enable states
ENTITY led_controller IS
- GENERIC (
- LED_WIDTH : POSITIVE := 8
- );
- PORT (
- mute_enable : IN STD_LOGIC;
- filter_enable : IN STD_LOGIC;
+ GENERIC (
+ LED_WIDTH : POSITIVE := 8 -- Width of LED intensity control (8-bit = 0-255 intensity levels)
+ );
+ PORT (
+ -- Control input signals
+ mute_enable : IN STD_LOGIC; -- Mute status (1=audio muted, 0=audio active)
+ filter_enable : IN STD_LOGIC; -- Filter status (1=filter active, 0=filter bypassed)
- led_r : OUT STD_LOGIC_VECTOR(LED_WIDTH - 1 DOWNTO 0);
- led_g : OUT STD_LOGIC_VECTOR(LED_WIDTH - 1 DOWNTO 0);
- led_b : OUT STD_LOGIC_VECTOR(LED_WIDTH - 1 DOWNTO 0)
- );
+ -- RGB LED output signals (intensity control)
+ led_r : OUT STD_LOGIC_VECTOR(LED_WIDTH - 1 DOWNTO 0); -- Red LED intensity (0-255)
+ led_g : OUT STD_LOGIC_VECTOR(LED_WIDTH - 1 DOWNTO 0); -- Green LED intensity (0-255)
+ led_b : OUT STD_LOGIC_VECTOR(LED_WIDTH - 1 DOWNTO 0) -- Blue LED intensity (0-255)
+ );
END led_controller;
ARCHITECTURE Behavioral OF led_controller IS
- -- constant for "ON" and "OFF"
- CONSTANT ALL_ON : STD_LOGIC_VECTOR(LED_WIDTH - 1 DOWNTO 0) := (OTHERS => '1');
- CONSTANT ALL_OFF : STD_LOGIC_VECTOR(LED_WIDTH - 1 DOWNTO 0) := (OTHERS => '0');
+ -- Constants for LED intensity levels
+ CONSTANT ALL_ON : STD_LOGIC_VECTOR(LED_WIDTH - 1 DOWNTO 0) := (OTHERS => '1'); -- Maximum brightness (255)
+ CONSTANT ALL_OFF : STD_LOGIC_VECTOR(LED_WIDTH - 1 DOWNTO 0) := (OTHERS => '0'); -- LED off (0)
BEGIN
- -- If mute_enable = '1' Red LEDs on, others off
- -- Else if filter_enable='1' Blue LEDs on, others off
- -- Else Green LEDs on, others off
+ -- LED Status Indication Logic:
+ -- Priority-based color coding for system status
+ --
+ -- Color Scheme:
+ -- RED = Mute active (highest priority - audio completely off)
+ -- BLUE = Filter active (medium priority - audio processing enabled)
+ -- GREEN = Normal operation (lowest priority - audio pass-through)
- led_r <= ALL_ON WHEN mute_enable = '1' ELSE
- ALL_OFF;
- led_b <= ALL_ON WHEN (mute_enable = '0' AND filter_enable = '1') ELSE
- ALL_OFF;
- led_g <= ALL_ON WHEN (mute_enable = '0' AND filter_enable = '0') ELSE
- ALL_OFF;
+ -- Red LED Control: Indicates mute status
+ -- Turn on red LED when audio is muted, regardless of filter state
+ led_r <= ALL_ON WHEN mute_enable = '1' ELSE
+ ALL_OFF;
+
+ -- Blue LED Control: Indicates filter activation
+ -- Turn on blue LED when filter is active AND audio is not muted
+ -- Mute has higher priority than filter indication
+ led_b <= ALL_ON WHEN (mute_enable = '0' AND filter_enable = '1') ELSE
+ ALL_OFF;
+
+ -- Green LED Control: Indicates normal operation
+ -- Turn on green LED when audio is active (not muted) AND filter is disabled
+ -- This represents the default "audio pass-through" state
+ led_g <= ALL_ON WHEN (mute_enable = '0' AND filter_enable = '0') ELSE
+ ALL_OFF;
+
+ -- Truth Table for LED States:
+ -- mute_enable | filter_enable | RED | GREEN | BLUE | Status
+ -- ------------|---------------|-----|-------|------|------------------
+ -- 0 | 0 | 0 | 1 | 0 | Normal (Green)
+ -- 0 | 1 | 0 | 0 | 1 | Filter On (Blue)
+ -- 1 | 0 | 1 | 0 | 0 | Muted (Red)
+ -- 1 | 1 | 1 | 0 | 0 | Muted (Red)
END Behavioral;
\ No newline at end of file
diff --git a/LAB3/src/led_level_controller.vhd b/LAB3/src/led_level_controller.vhd
index 8558470..004f664 100644
--- a/LAB3/src/led_level_controller.vhd
+++ b/LAB3/src/led_level_controller.vhd
@@ -2,121 +2,201 @@ LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;
+-- Entity: led_level_controller
+-- Purpose: Audio level meter using LEDs to display real-time audio amplitude
+-- Processes stereo audio samples and drives a bar graph LED display
+-- Provides visual feedback of audio signal strength for both channels combined
ENTITY led_level_controller IS
GENERIC (
- NUM_LEDS : POSITIVE := 16;
- CHANNEL_LENGHT : POSITIVE := 24;
- refresh_time_ms : POSITIVE := 1;
- clock_period_ns : POSITIVE := 10
+ NUM_LEDS : POSITIVE := 16; -- Number of LEDs in the level meter display
+ CHANNEL_LENGHT : POSITIVE := 24; -- Width of audio data (24-bit audio samples)
+ refresh_time_ms : POSITIVE := 1; -- LED refresh rate in milliseconds (1ms = 1kHz update rate)
+ clock_period_ns : POSITIVE := 10 -- System clock period in nanoseconds (10ns = 100MHz)
);
PORT (
+ -- Clock and reset signals
+ aclk : IN STD_LOGIC; -- Main clock input
+ aresetn : IN STD_LOGIC; -- Active-low asynchronous reset
- aclk : IN STD_LOGIC;
- aresetn : IN STD_LOGIC;
-
- led : OUT STD_LOGIC_VECTOR(NUM_LEDS - 1 DOWNTO 0);
-
- s_axis_tvalid : IN STD_LOGIC;
- s_axis_tdata : IN STD_LOGIC_VECTOR(CHANNEL_LENGHT - 1 DOWNTO 0);
- s_axis_tlast : IN STD_LOGIC;
- s_axis_tready : OUT STD_LOGIC
+ -- LED output array (bar graph display)
+ led : OUT STD_LOGIC_VECTOR(NUM_LEDS - 1 DOWNTO 0); -- LED control signals (1=on, 0=off)
+ -- AXI4-Stream Slave 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=right, 1=left)
+ s_axis_tready : OUT STD_LOGIC -- Always ready to accept data
);
END led_level_controller;
ARCHITECTURE Behavioral OF led_level_controller IS
+
+ -- Calculate number of clock cycles for LED refresh timing
+ -- Example: 1ms refresh at 100MHz = (1*1,000,000)/10 = 100,000 cycles
CONSTANT REFRESH_CYCLES : NATURAL := (refresh_time_ms * 1_000_000) / clock_period_ns;
- SIGNAL volume_value : signed(CHANNEL_LENGHT - 1 DOWNTO 0) := (OTHERS => '0');
- SIGNAL abs_audio_left : unsigned(CHANNEL_LENGHT - 2 DOWNTO 0) := (OTHERS => '0');
- SIGNAL abs_audio_right : unsigned(CHANNEL_LENGHT - 2 DOWNTO 0) := (OTHERS => '0');
- SIGNAL leds_int : STD_LOGIC_VECTOR(NUM_LEDS - 1 DOWNTO 0) := (OTHERS => '0');
- SIGNAL led_update : STD_LOGIC := '0';
- SIGNAL refresh_counter : NATURAL RANGE 0 TO REFRESH_CYCLES - 1 := 0;
+ -- 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
+
+ -- LED control signals
+ SIGNAL leds_int : STD_LOGIC_VECTOR(NUM_LEDS - 1 DOWNTO 0) := (OTHERS => '0'); -- Internal LED state
+ SIGNAL led_update : STD_LOGIC := '0'; -- Trigger for LED refresh
+
+ -- Timing control
+ SIGNAL refresh_counter : NATURAL RANGE 0 TO REFRESH_CYCLES - 1 := 0; -- Counter for refresh timing
BEGIN
- led <= leds_int;
- s_axis_tready <= '1';
- -- Registering the absolute audio value
+ -- Connect internal signals to output ports
+ led <= leds_int; -- Drive external LEDs with internal state
+ s_axis_tready <= '1'; -- Always ready to accept audio data (no backpressure)
+
+ -- Audio sample processing and absolute value calculation
+ -- Converts signed audio samples to unsigned absolute values for level detection
PROCESS (aclk)
- VARIABLE sdata_signed : signed(CHANNEL_LENGHT - 1 DOWNTO 0);
- VARIABLE abs_value : unsigned(CHANNEL_LENGHT - 1 DOWNTO 0);
+ VARIABLE sdata_signed : signed(CHANNEL_LENGHT - 1 DOWNTO 0); -- Temporary signed audio value
+ VARIABLE abs_value : unsigned(CHANNEL_LENGHT - 1 DOWNTO 0); -- Temporary absolute value
BEGIN
+
IF rising_edge(aclk) THEN
+
IF aresetn = '0' THEN
- volume_value <= (OTHERS => '0');
- abs_audio_left <= (OTHERS => '0');
- abs_audio_right <= (OTHERS => '0');
+ -- 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
- sdata_signed := signed(s_axis_tdata);
- volume_value <= sdata_signed;
- -- Absolute value calculation
+ -- 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
+ -- Negative number: Take two's complement to get absolute value
abs_value := unsigned(-sdata_signed);
ELSE
+ -- Positive number: Direct conversion to unsigned
abs_value := unsigned(sdata_signed);
END IF;
- -- Assign to the correct channel
- IF s_axis_tlast = '1' THEN -- Left channel
+
+ -- Channel assignment based on tlast signal
+ -- Note: Channel assignment appears reversed from typical convention
+ IF s_axis_tlast = '1' THEN
+ -- tlast = '1': Assign to left channel
abs_audio_left <= abs_value(CHANNEL_LENGHT - 2 DOWNTO 0);
- ELSE -- Right channel
+ ELSE
+ -- tlast = '0': Assign to right channel
abs_audio_right <= abs_value(CHANNEL_LENGHT - 2 DOWNTO 0);
END IF;
+
END IF;
+
END IF;
+
END PROCESS;
- -- Refresh counter
+ -- LED refresh timing control
+ -- Generates periodic update signals for smooth LED display updates
PROCESS (aclk)
BEGIN
+
IF rising_edge(aclk) THEN
+
IF aresetn = '0' THEN
- refresh_counter <= 0;
- led_update <= '0';
+ -- Reset timing control
+ refresh_counter <= 0; -- Clear refresh counter
+ led_update <= '0'; -- Clear update trigger
+
ELSIF refresh_counter = REFRESH_CYCLES - 1 THEN
- refresh_counter <= 0;
- led_update <= '1';
+ -- End of refresh period: Trigger LED update
+ refresh_counter <= 0; -- Reset counter for next period
+ led_update <= '1'; -- Set update trigger
+
ELSE
- refresh_counter <= refresh_counter + 1;
- led_update <= '0';
+ -- Continue counting refresh period
+ refresh_counter <= refresh_counter + 1; -- Increment counter
+ led_update <= '0'; -- Clear update trigger
+
END IF;
+
END IF;
+
END PROCESS;
- -- Linear scaling and LED update
+ -- LED level calculation and bar graph generation
+ -- Combines left and right channel levels and maps to LED array
PROCESS (aclk)
- VARIABLE leds_on : NATURAL RANGE 0 TO NUM_LEDS;
- VARIABLE temp_led_level : INTEGER RANGE 0 TO NUM_LEDS;
- VARIABLE abs_audio_sum : unsigned(CHANNEL_LENGHT - 1 DOWNTO 0);
+ VARIABLE leds_on : NATURAL RANGE 0 TO NUM_LEDS; -- Number of LEDs to illuminate
+ VARIABLE temp_led_level : INTEGER RANGE 0 TO NUM_LEDS; -- Calculated LED level
+ VARIABLE abs_audio_sum : unsigned(CHANNEL_LENGHT - 1 DOWNTO 0); -- Combined channel amplitude
BEGIN
+
IF rising_edge(aclk) THEN
+
IF aresetn = '0' THEN
+ -- Reset: Turn off all LEDs
leds_int <= (OTHERS => '0');
+
ELSIF led_update = '1' THEN
+ -- 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);
+ -- Level calculation with automatic sensitivity scaling
IF (abs_audio_left = 0 AND abs_audio_right = 0) THEN
+ -- Silence: No LEDs illuminated
temp_led_level := 0;
+
ELSE
- -- Automatic scaling
- -- Sensitivity can be adjusted by changing the shift constant
+ -- 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
temp_led_level := 1 + to_integer(shift_right(abs_audio_sum, CHANNEL_LENGHT - 4));
+
END IF;
- -- Limit to the maximum number of LEDs
+ -- Limit LED level to available LEDs (prevent array overflow)
IF temp_led_level > NUM_LEDS THEN
- leds_on := NUM_LEDS;
+ leds_on := NUM_LEDS; -- Cap at maximum LEDs
ELSE
- leds_on := temp_led_level;
+ leds_on := temp_led_level; -- Use calculated level
END IF;
- -- Update the LEDs
- leds_int <= (OTHERS => '0');
+ -- Generate bar graph pattern: illuminate LEDs from 0 to (leds_on-1)
+ -- This creates a classic audio level meter appearance
+ leds_int <= (OTHERS => '0'); -- Start with all LEDs off
+
IF leds_on > 0 THEN
+ -- Turn on LEDs from index 0 up to (leds_on-1)
+ -- Creates solid bar from bottom to current level
leds_int(leds_on - 1 DOWNTO 0) <= (OTHERS => '1');
END IF;
+
END IF;
+
END IF;
+
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
diff --git a/LAB3/src/moving_average_filter.vhd b/LAB3/src/moving_average_filter.vhd
index 9daa59a..881fa57 100644
--- a/LAB3/src/moving_average_filter.vhd
+++ b/LAB3/src/moving_average_filter.vhd
@@ -2,153 +2,199 @@ LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE ieee.numeric_std.ALL;
+-- Entity: moving_average_filter
+-- Purpose: Implements a finite impulse response (FIR) low-pass filter using moving average
+-- Maintains separate circular buffers for left and right audio channels
+-- Filter order is configurable as 2^(FILTER_ORDER_POWER) samples
ENTITY moving_average_filter IS
- GENERIC (
- -- Filter order expressed as 2^(FILTER_ORDER_POWER)
- FILTER_ORDER_POWER : INTEGER := 5;
+ GENERIC (
+ -- Filter order expressed as 2^(FILTER_ORDER_POWER)
+ -- Example: FILTER_ORDER_POWER = 5 means 32-sample moving average
+ FILTER_ORDER_POWER : INTEGER := 5;
- TDATA_WIDTH : POSITIVE := 24
- );
- PORT (
- aclk : IN STD_LOGIC;
- aresetn : IN STD_LOGIC;
+ TDATA_WIDTH : POSITIVE := 24 -- Width of audio data bus (24-bit audio samples)
+ );
+ PORT (
+ -- Clock and reset signals
+ aclk : IN STD_LOGIC; -- Main clock input
+ aresetn : IN STD_LOGIC; -- Active-low asynchronous reset
- s_axis_tvalid : IN STD_LOGIC;
- s_axis_tdata : IN STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0);
- s_axis_tlast : IN STD_LOGIC;
- s_axis_tready : OUT STD_LOGIC;
+ -- AXI4-Stream Slave Interface (Audio Input)
+ s_axis_tvalid : IN STD_LOGIC; -- Input data valid signal
+ s_axis_tdata : IN STD_LOGIC_VECTOR(TDATA_WIDTH - 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
- m_axis_tvalid : OUT STD_LOGIC;
- m_axis_tdata : OUT STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0);
- m_axis_tlast : OUT STD_LOGIC;
- m_axis_tready : IN STD_LOGIC
- );
+ -- AXI4-Stream Master Interface (Audio Output)
+ m_axis_tvalid : OUT STD_LOGIC; -- Output data valid signal
+ m_axis_tdata : OUT STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0); -- Filtered audio sample output
+ m_axis_tlast : OUT STD_LOGIC; -- Channel indicator passthrough
+ m_axis_tready : IN STD_LOGIC -- Downstream ready signal
+ );
END moving_average_filter;
ARCHITECTURE Behavioral OF moving_average_filter IS
- CONSTANT FILTER_ORDER : INTEGER := 2 ** FILTER_ORDER_POWER;
+ -- Calculate actual filter order from power-of-2 representation
+ CONSTANT FILTER_ORDER : INTEGER := 2 ** FILTER_ORDER_POWER;
- TYPE sample_array IS ARRAY (0 TO FILTER_ORDER - 1) OF signed(TDATA_WIDTH - 1 DOWNTO 0);
+ -- Array type for storing audio samples in circular buffers
+ TYPE sample_array IS ARRAY (0 TO FILTER_ORDER - 1) OF signed(TDATA_WIDTH - 1 DOWNTO 0);
- -- RX
- SIGNAL samples_rx : sample_array := (OTHERS => (OTHERS => '0'));
- SIGNAL sum_rx : signed(TDATA_WIDTH + FILTER_ORDER_POWER - 1 DOWNTO 0) := (OTHERS => '0');
- SIGNAL wr_ptr_rx : INTEGER RANGE 0 TO FILTER_ORDER - 1 := 0;
+ -- Right channel (RX) processing signals
+ SIGNAL samples_rx : sample_array := (OTHERS => (OTHERS => '0')); -- Circular buffer for right channel samples
+ SIGNAL sum_rx : signed(TDATA_WIDTH + FILTER_ORDER_POWER - 1 DOWNTO 0) := (OTHERS => '0'); -- Running sum of right channel samples
+ SIGNAL wr_ptr_rx : INTEGER RANGE 0 TO FILTER_ORDER - 1 := 0; -- Write pointer for right channel buffer
- -- LX
- SIGNAL samples_lx : sample_array := (OTHERS => (OTHERS => '0'));
- SIGNAL sum_lx : signed(TDATA_WIDTH + FILTER_ORDER_POWER - 1 DOWNTO 0) := (OTHERS => '0');
- SIGNAL wr_ptr_lx : INTEGER RANGE 0 TO FILTER_ORDER - 1 := 0;
+ -- Left channel (LX) processing signals
+ SIGNAL samples_lx : sample_array := (OTHERS => (OTHERS => '0')); -- Circular buffer for left channel samples
+ SIGNAL sum_lx : signed(TDATA_WIDTH + FILTER_ORDER_POWER - 1 DOWNTO 0) := (OTHERS => '0'); -- Running sum of left channel samples
+ SIGNAL wr_ptr_lx : INTEGER RANGE 0 TO FILTER_ORDER - 1 := 0; -- Write pointer for left channel buffer
- -- Trigger signal to indicate when to output data
- SIGNAL trigger : STD_LOGIC := '0';
-
- -- Output signals
- SIGNAL s_axis_tready_int : STD_LOGIC := '0';
- SIGNAL s_axis_tlast_reg : STD_LOGIC := '0';
- SIGNAL m_axis_tvalid_int : STD_LOGIC := '0';
+ -- Control and interface signals
+ SIGNAL trigger : STD_LOGIC := '0'; -- Trigger signal to indicate when to output filtered data
+ SIGNAL s_axis_tready_int : STD_LOGIC := '0'; -- Internal ready signal for input interface
+ SIGNAL s_axis_tlast_reg : STD_LOGIC := '0'; -- Registered version of tlast for output synchronization
+ SIGNAL m_axis_tvalid_int : STD_LOGIC := '0'; -- Internal valid signal for output interface
BEGIN
- -- Assigning the output signals
- m_axis_tvalid <= m_axis_tvalid_int;
- s_axis_tready <= s_axis_tready_int;
+ -- Connect internal signals to output ports
+ m_axis_tvalid <= m_axis_tvalid_int;
+ s_axis_tready <= s_axis_tready_int;
- PROCESS (aclk)
- BEGIN
+ -- Main processing logic - synchronous to clock
+ PROCESS (aclk)
+ BEGIN
- IF rising_edge(aclk) THEN
+ IF rising_edge(aclk) THEN
- IF aresetn = '0' THEN
- samples_rx <= (OTHERS => (OTHERS => '0'));
- samples_lx <= (OTHERS => (OTHERS => '0'));
- sum_rx <= (OTHERS => '0');
- sum_lx <= (OTHERS => '0');
- wr_ptr_rx <= 0;
- wr_ptr_lx <= 0;
+ IF aresetn = '0' THEN
+ -- Reset all filter state and control signals
+ samples_rx <= (OTHERS => (OTHERS => '0')); -- Clear right channel sample buffer
+ samples_lx <= (OTHERS => (OTHERS => '0')); -- Clear left channel sample buffer
+ sum_rx <= (OTHERS => '0'); -- Clear right channel running sum
+ sum_lx <= (OTHERS => '0'); -- Clear left channel running sum
+ wr_ptr_rx <= 0; -- Reset right channel write pointer
+ wr_ptr_lx <= 0; -- Reset left channel write pointer
- s_axis_tlast_reg <= '0';
- s_axis_tready_int <= '0';
- m_axis_tvalid_int <= '0';
- m_axis_tlast <= '0';
+ -- Reset AXI4-Stream interface signals
+ s_axis_tlast_reg <= '0'; -- Clear registered tlast
+ s_axis_tready_int <= '0'; -- Not ready during reset
+ m_axis_tvalid_int <= '0'; -- No valid output during reset
+ m_axis_tlast <= '0'; -- Clear output tlast
- ELSE
- -- Clear valid flag when master interface is ready
- IF m_axis_tready = '1' THEN
- m_axis_tvalid_int <= '0';
- END IF;
+ ELSE
+ -- Normal operation
- -- Data output logic
- IF trigger = '1' AND (m_axis_tvalid_int = '0' OR m_axis_tready = '1') THEN
- IF s_axis_tlast_reg = '1' THEN
- m_axis_tdata <= STD_LOGIC_VECTOR(
- resize(
- shift_right(
- sum_rx,
- FILTER_ORDER_POWER
- ),
- m_axis_tdata'length
- )
- );
+ -- Output handshake management:
+ -- Clear valid flag when downstream accepts data
+ IF m_axis_tready = '1' THEN
+ m_axis_tvalid_int <= '0';
+ END IF;
- ELSE
- m_axis_tdata <= STD_LOGIC_VECTOR(
- resize(
- shift_right(
- sum_lx,
- FILTER_ORDER_POWER
- ),
- m_axis_tdata'length
- )
- );
+ -- Data output logic:
+ -- Output filtered data when trigger is set AND output interface is available
+ IF trigger = '1' AND (m_axis_tvalid_int = '0' OR m_axis_tready = '1') THEN
+
+ -- Select which channel's filtered data to output based on registered tlast
+ IF s_axis_tlast_reg = '1' THEN
+ -- Right channel: Output averaged right channel data
+ -- Divide sum by filter order using right shift (efficient division by power of 2)
+ m_axis_tdata <= STD_LOGIC_VECTOR(
+ resize(
+ shift_right(
+ sum_rx, -- Right channel running sum
+ FILTER_ORDER_POWER -- Divide by 2^FILTER_ORDER_POWER
+ ),
+ m_axis_tdata'length -- Resize to output width
+ )
+ );
- END IF;
-
- m_axis_tlast <= s_axis_tlast_reg;
+ ELSE
+ -- Left channel: Output averaged left channel data
+ -- Divide sum by filter order using right shift (efficient division by power of 2)
+ m_axis_tdata <= STD_LOGIC_VECTOR(
+ resize(
+ shift_right(
+ sum_lx, -- Left channel running sum
+ FILTER_ORDER_POWER -- Divide by 2^FILTER_ORDER_POWER
+ ),
+ m_axis_tdata'length -- Resize to output width
+ )
+ );
- m_axis_tvalid_int <= '1';
- trigger <= '0';
- END IF;
+ END IF;
+
+ -- Set output control signals
+ m_axis_tlast <= s_axis_tlast_reg; -- Pass through the registered channel indicator
+ m_axis_tvalid_int <= '1'; -- Mark output as valid
+ trigger <= '0'; -- Clear trigger - data has been output
- -- Data input logic
- IF s_axis_tvalid = '1' AND s_axis_tready_int = '1' THEN
- IF s_axis_tlast = '1' THEN
- -- Right channel
- -- Circular buffer overwrite oldest saple with the new one from next clk cycle
- samples_rx(wr_ptr_rx) <= signed(s_axis_tdata);
+ END IF;
- -- Update the write pointer
- wr_ptr_rx <= (wr_ptr_rx + 1) MOD FILTER_ORDER;
+ -- Data input logic:
+ -- Process new input samples when both valid and ready are asserted
+ IF s_axis_tvalid = '1' AND s_axis_tready_int = '1' THEN
+
+ -- Channel identification and processing based on tlast signal
+ IF s_axis_tlast = '1' THEN
+ -- Right channel processing (tlast = '1')
+
+ -- Store new sample in circular buffer, overwriting oldest sample
+ samples_rx(wr_ptr_rx) <= signed(s_axis_tdata);
- -- Update the sum_rx removing the oldest sample and adding the new one
- sum_rx <= sum_rx - samples_rx(wr_ptr_rx) + signed(s_axis_tdata);
+ -- Update write pointer with wraparound (circular buffer behavior)
+ wr_ptr_rx <= (wr_ptr_rx + 1) MOD FILTER_ORDER;
- s_axis_tlast_reg <= s_axis_tlast;
- ELSE
- -- Left channel
- -- Circular buffer overwrite oldest saple with the new one from next clk cycle
- samples_lx(wr_ptr_lx) <= signed(s_axis_tdata);
+ -- Update running sum using sliding window technique:
+ -- Remove the oldest sample (about to be overwritten) and add the new sample
+ -- This maintains the sum of the most recent FILTER_ORDER samples
+ sum_rx <= sum_rx - samples_rx(wr_ptr_rx) + signed(s_axis_tdata);
- -- Update the write pointer
- wr_ptr_lx <= (wr_ptr_lx + 1) MOD FILTER_ORDER;
+ -- Register tlast for output synchronization
+ s_axis_tlast_reg <= s_axis_tlast;
- -- Update the sum_rx removing the oldest sample and adding the new one
- sum_lx <= sum_lx - samples_lx(wr_ptr_lx) + signed(s_axis_tdata);
+ ELSE
+ -- Left channel processing (tlast = '0')
+
+ -- Store new sample in circular buffer, overwriting oldest sample
+ samples_lx(wr_ptr_lx) <= signed(s_axis_tdata);
- s_axis_tlast_reg <= s_axis_tlast;
- END IF;
+ -- Update write pointer with wraparound (circular buffer behavior)
+ wr_ptr_lx <= (wr_ptr_lx + 1) MOD FILTER_ORDER;
- trigger <= '1';
+ -- Update running sum using sliding window technique:
+ -- Remove the oldest sample (about to be overwritten) and add the new sample
+ -- This maintains the sum of the most recent FILTER_ORDER samples
+ sum_lx <= sum_lx - samples_lx(wr_ptr_lx) + signed(s_axis_tdata);
- END IF;
+ -- Register tlast for output synchronization
+ s_axis_tlast_reg <= s_axis_tlast;
- s_axis_tready_int <= m_axis_tready OR NOT m_axis_tvalid_int;
+ END IF;
- END IF;
+ -- Set trigger to indicate that new filtered data is ready for output
+ trigger <= '1';
- END IF;
+ END IF;
- END PROCESS;
+ -- Input ready logic:
+ -- Ready to accept new input when downstream is ready OR no valid output pending
+ -- This implements proper AXI4-Stream backpressure handling
+ s_axis_tready_int <= m_axis_tready OR NOT m_axis_tvalid_int;
+
+ END IF;
+
+ END IF;
+
+ END PROCESS;
+
+ -- Filter Operation Summary:
+ -- 1. Maintains separate circular buffers for left and right audio channels
+ -- 2. Uses running sum technique for efficient moving average calculation
+ -- 3. Each new sample: removes oldest from sum, adds newest to sum
+ -- 4. Output = sum / FILTER_ORDER (using efficient bit shift division)
+ -- 5. Provides low-pass filtering with configurable filter order
END Behavioral;
\ No newline at end of file
diff --git a/LAB3/src/moving_average_filter_en.vhd b/LAB3/src/moving_average_filter_en.vhd
index 0d3f037..3386a56 100644
--- a/LAB3/src/moving_average_filter_en.vhd
+++ b/LAB3/src/moving_average_filter_en.vhd
@@ -2,135 +2,167 @@ LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE ieee.numeric_std.ALL;
+-- Entity: moving_average_filter_en
+-- Purpose: Switchable audio filter that can be enabled or disabled at runtime
+-- When enabled: applies moving average low-pass filtering
+-- When disabled: passes audio through unchanged (all-pass filter)
+-- This allows real-time comparison between filtered and unfiltered audio
ENTITY moving_average_filter_en IS
- GENERIC (
- -- Filter order expressed as 2^(FILTER_ORDER_POWER)
- FILTER_ORDER_POWER : INTEGER := 5;
+ GENERIC (
+ -- Filter order expressed as 2^(FILTER_ORDER_POWER)
+ -- Example: FILTER_ORDER_POWER = 5 means filter order = 32 samples
+ FILTER_ORDER_POWER : INTEGER := 5;
- TDATA_WIDTH : POSITIVE := 24
- );
- PORT (
- aclk : IN STD_LOGIC;
- aresetn : IN STD_LOGIC;
+ TDATA_WIDTH : POSITIVE := 24 -- Width of audio data bus (24-bit audio samples)
+ );
+ PORT (
+ -- Clock and reset signals
+ aclk : IN STD_LOGIC; -- Main clock input
+ aresetn : IN STD_LOGIC; -- Active-low asynchronous reset
- s_axis_tvalid : IN STD_LOGIC;
- s_axis_tdata : IN STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0);
- s_axis_tlast : IN STD_LOGIC;
- s_axis_tready : OUT STD_LOGIC;
+ -- AXI4-Stream Slave Interface (Audio Input)
+ s_axis_tvalid : IN STD_LOGIC; -- Input data valid signal
+ s_axis_tdata : IN STD_LOGIC_VECTOR(TDATA_WIDTH - 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
- m_axis_tvalid : OUT STD_LOGIC;
- m_axis_tdata : OUT STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0);
- m_axis_tlast : OUT STD_LOGIC;
- m_axis_tready : IN STD_LOGIC;
+ -- AXI4-Stream Master Interface (Audio Output)
+ m_axis_tvalid : OUT STD_LOGIC; -- Output data valid signal
+ m_axis_tdata : OUT STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0); -- Audio sample output (filtered or unfiltered)
+ m_axis_tlast : OUT STD_LOGIC; -- Channel indicator passthrough
+ m_axis_tready : IN STD_LOGIC; -- Downstream ready signal
- enable_filter : IN STD_LOGIC
- );
+ -- Filter control input
+ enable_filter : IN STD_LOGIC -- Filter enable control (1=moving average filter, 0=all-pass filter)
+ );
END moving_average_filter_en;
ARCHITECTURE Behavioral OF moving_average_filter_en IS
- -- Component declarations
- COMPONENT all_pass_filter IS
- GENERIC (
- TDATA_WIDTH : POSITIVE := 24
- );
- PORT (
- aclk : IN STD_LOGIC;
- aresetn : IN STD_LOGIC;
+ -- Component declaration for all-pass filter
+ -- This filter passes audio unchanged but maintains the same latency as the moving average filter
+ COMPONENT all_pass_filter IS
+ GENERIC (
+ TDATA_WIDTH : POSITIVE := 24
+ );
+ PORT (
+ aclk : IN STD_LOGIC;
+ aresetn : IN STD_LOGIC;
- s_axis_tvalid : IN STD_LOGIC;
- s_axis_tdata : IN STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0);
- s_axis_tlast : IN STD_LOGIC;
- s_axis_tready : OUT STD_LOGIC;
+ s_axis_tvalid : IN STD_LOGIC;
+ s_axis_tdata : IN STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0);
+ s_axis_tlast : IN STD_LOGIC;
+ s_axis_tready : OUT STD_LOGIC;
- m_axis_tvalid : OUT STD_LOGIC;
- m_axis_tdata : OUT STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0);
- m_axis_tlast : OUT STD_LOGIC;
- m_axis_tready : IN STD_LOGIC
- );
- END COMPONENT;
+ m_axis_tvalid : OUT STD_LOGIC;
+ m_axis_tdata : OUT STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0);
+ m_axis_tlast : OUT STD_LOGIC;
+ m_axis_tready : IN STD_LOGIC
+ );
+ END COMPONENT;
- COMPONENT moving_average_filter IS
- GENERIC (
- FILTER_ORDER_POWER : INTEGER := 5;
- TDATA_WIDTH : POSITIVE := 24
- );
- PORT (
- aclk : IN STD_LOGIC;
- aresetn : IN STD_LOGIC;
+ -- Component declaration for moving average filter
+ -- This filter applies low-pass filtering by averaging multiple audio samples
+ COMPONENT moving_average_filter IS
+ GENERIC (
+ FILTER_ORDER_POWER : INTEGER := 5;
+ TDATA_WIDTH : POSITIVE := 24
+ );
+ PORT (
+ aclk : IN STD_LOGIC;
+ aresetn : IN STD_LOGIC;
- s_axis_tvalid : IN STD_LOGIC;
- s_axis_tdata : IN STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0);
- s_axis_tlast : IN STD_LOGIC;
- s_axis_tready : OUT STD_LOGIC;
-
- m_axis_tvalid : OUT STD_LOGIC;
- m_axis_tdata : OUT STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0);
- m_axis_tlast : OUT STD_LOGIC;
- m_axis_tready : IN STD_LOGIC
- );
- END COMPONENT;
+ s_axis_tvalid : IN STD_LOGIC;
+ s_axis_tdata : IN STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0);
+ s_axis_tlast : IN STD_LOGIC;
+ s_axis_tready : OUT STD_LOGIC;
+
+ m_axis_tvalid : OUT STD_LOGIC;
+ m_axis_tdata : OUT STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0);
+ m_axis_tlast : OUT STD_LOGIC;
+ m_axis_tready : IN STD_LOGIC
+ );
+ END COMPONENT;
- -- Internal signals for the all-pass filter
- SIGNAL all_pass_m_tvalid : STD_LOGIC;
- SIGNAL all_pass_m_tdata : STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0);
- SIGNAL all_pass_m_tlast : STD_LOGIC;
- SIGNAL all_pass_s_tready : STD_LOGIC;
+ -- Internal signals for the all-pass filter path
+ -- These signals carry data when filtering is disabled
+ SIGNAL all_pass_m_tvalid : STD_LOGIC; -- All-pass filter output valid
+ SIGNAL all_pass_m_tdata : STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0); -- All-pass filter output data
+ SIGNAL all_pass_m_tlast : STD_LOGIC; -- All-pass filter output tlast
+ SIGNAL all_pass_s_tready : STD_LOGIC; -- All-pass filter input ready
- -- Internal signals for the moving average filter
- SIGNAL moving_avg_m_tvalid : STD_LOGIC;
- SIGNAL moving_avg_m_tdata : STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0);
- SIGNAL moving_avg_m_tlast : STD_LOGIC;
- SIGNAL moving_avg_s_tready : STD_LOGIC;
+ -- Internal signals for the moving average filter path
+ -- These signals carry data when filtering is enabled
+ SIGNAL moving_avg_m_tvalid : STD_LOGIC; -- Moving average filter output valid
+ SIGNAL moving_avg_m_tdata : STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0); -- Moving average filter output data
+ SIGNAL moving_avg_m_tlast : STD_LOGIC; -- Moving average filter output tlast
+ SIGNAL moving_avg_s_tready : STD_LOGIC; -- Moving average filter input ready
BEGIN
- -- Instantiate the all-pass filter
- all_pass_inst : all_pass_filter
- GENERIC MAP(
- TDATA_WIDTH => TDATA_WIDTH
- )
- PORT MAP(
- aclk => aclk,
- aresetn => aresetn,
+ -- Instantiate the all-pass filter (bypass path)
+ -- This provides unfiltered audio with matched latency for seamless switching
+ all_pass_inst : all_pass_filter
+ GENERIC MAP(
+ TDATA_WIDTH => TDATA_WIDTH
+ )
+ PORT MAP(
+ aclk => aclk,
+ aresetn => aresetn,
- s_axis_tvalid => s_axis_tvalid,
- s_axis_tdata => s_axis_tdata,
- s_axis_tlast => s_axis_tlast,
- s_axis_tready => all_pass_s_tready,
+ -- Connect input directly to all-pass filter
+ s_axis_tvalid => s_axis_tvalid,
+ s_axis_tdata => s_axis_tdata,
+ s_axis_tlast => s_axis_tlast,
+ s_axis_tready => all_pass_s_tready,
- m_axis_tvalid => all_pass_m_tvalid,
- m_axis_tdata => all_pass_m_tdata,
- m_axis_tlast => all_pass_m_tlast,
- m_axis_tready => m_axis_tready
- );
+ -- All-pass filter outputs (used when enable_filter = '0')
+ m_axis_tvalid => all_pass_m_tvalid,
+ m_axis_tdata => all_pass_m_tdata,
+ m_axis_tlast => all_pass_m_tlast,
+ m_axis_tready => m_axis_tready
+ );
- -- Instantiate the moving average filter
- moving_avg_inst : moving_average_filter
- GENERIC MAP(
- FILTER_ORDER_POWER => FILTER_ORDER_POWER,
- TDATA_WIDTH => TDATA_WIDTH
- )
- PORT MAP(
- aclk => aclk,
- aresetn => aresetn,
+ -- Instantiate the moving average filter (filtering path)
+ -- This provides low-pass filtered audio for noise reduction
+ moving_avg_inst : moving_average_filter
+ GENERIC MAP(
+ FILTER_ORDER_POWER => FILTER_ORDER_POWER,
+ TDATA_WIDTH => TDATA_WIDTH
+ )
+ PORT MAP(
+ aclk => aclk,
+ aresetn => aresetn,
- s_axis_tvalid => s_axis_tvalid,
- s_axis_tdata => s_axis_tdata,
- s_axis_tlast => s_axis_tlast,
- s_axis_tready => moving_avg_s_tready,
+ -- Connect input directly to moving average filter
+ s_axis_tvalid => s_axis_tvalid,
+ s_axis_tdata => s_axis_tdata,
+ s_axis_tlast => s_axis_tlast,
+ s_axis_tready => moving_avg_s_tready,
- m_axis_tvalid => moving_avg_m_tvalid,
- m_axis_tdata => moving_avg_m_tdata,
- m_axis_tlast => moving_avg_m_tlast,
- m_axis_tready => m_axis_tready
- );
+ -- Moving average filter outputs (used when enable_filter = '1')
+ m_axis_tvalid => moving_avg_m_tvalid,
+ m_axis_tdata => moving_avg_m_tdata,
+ m_axis_tlast => moving_avg_m_tlast,
+ m_axis_tready => m_axis_tready
+ );
- -- Main AXIS assignments based on enable_filter
- s_axis_tready <= all_pass_s_tready WHEN enable_filter = '0' ELSE moving_avg_s_tready;
-
- m_axis_tvalid <= all_pass_m_tvalid WHEN enable_filter = '0' ELSE moving_avg_m_tvalid;
- m_axis_tdata <= all_pass_m_tdata WHEN enable_filter = '0' ELSE moving_avg_m_tdata;
- m_axis_tlast <= all_pass_m_tlast WHEN enable_filter = '0' ELSE moving_avg_m_tlast;
+ -- Output multiplexer: Select between filtered and unfiltered audio paths
+ -- This switching is controlled by the enable_filter signal
+
+ -- Input ready selection: Route backpressure to the active filter
+ s_axis_tready <= all_pass_s_tready WHEN enable_filter = '0' ELSE moving_avg_s_tready;
+
+ -- Output data selection: Route output from the active filter
+ m_axis_tvalid <= all_pass_m_tvalid WHEN enable_filter = '0' ELSE moving_avg_m_tvalid;
+ m_axis_tdata <= all_pass_m_tdata WHEN enable_filter = '0' ELSE moving_avg_m_tdata;
+ m_axis_tlast <= all_pass_m_tlast WHEN enable_filter = '0' ELSE moving_avg_m_tlast;
+
+ -- Filter Path Selection Logic:
+ -- enable_filter = '0': All-pass path (unfiltered audio with matched latency)
+ -- enable_filter = '1': Moving average path (low-pass filtered audio)
+ --
+ -- Both filters run continuously but only the selected path is routed to output
+ -- This allows for glitch-free switching between filtered and unfiltered audio
END Behavioral;
\ No newline at end of file
diff --git a/LAB3/src/mute_controller.vhd b/LAB3/src/mute_controller.vhd
index 217c66a..32e5fc8 100644
--- a/LAB3/src/mute_controller.vhd
+++ b/LAB3/src/mute_controller.vhd
@@ -2,70 +2,95 @@ LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;
+-- Entity: mute_controller
+-- Purpose: Controls audio muting by replacing audio samples with zeros when mute is active
+-- Implements AXI4-Stream interface for seamless integration in audio processing chain
ENTITY mute_controller IS
- GENERIC (
- TDATA_WIDTH : POSITIVE := 24
- );
- PORT (
- aclk : IN STD_LOGIC;
- aresetn : IN STD_LOGIC;
+ GENERIC (
+ TDATA_WIDTH : POSITIVE := 24 -- Width of audio data bus (24-bit audio samples)
+ );
+ PORT (
+ -- Clock and reset signals
+ aclk : IN STD_LOGIC; -- Main clock input
+ aresetn : IN STD_LOGIC; -- Active-low asynchronous reset
- s_axis_tvalid : IN STD_LOGIC;
- s_axis_tdata : IN STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0);
- s_axis_tlast : IN STD_LOGIC;
- s_axis_tready : OUT STD_LOGIC;
+ -- AXI4-Stream Slave Interface (Audio Input)
+ s_axis_tvalid : IN STD_LOGIC; -- Input data valid signal
+ s_axis_tdata : IN STD_LOGIC_VECTOR(TDATA_WIDTH - 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
- m_axis_tvalid : OUT STD_LOGIC;
- m_axis_tdata : OUT STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0);
- m_axis_tlast : OUT STD_LOGIC;
- m_axis_tready : IN STD_LOGIC;
+ -- AXI4-Stream Master Interface (Audio Output)
+ m_axis_tvalid : OUT STD_LOGIC; -- Output data valid signal
+ m_axis_tdata : OUT STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0); -- Audio sample output (muted or passed through)
+ m_axis_tlast : OUT STD_LOGIC; -- Channel indicator passthrough
+ m_axis_tready : IN STD_LOGIC; -- Downstream ready signal
- mute : IN STD_LOGIC
- );
+ -- Control input
+ mute : IN STD_LOGIC -- Mute control (1=mute audio, 0=pass audio through)
+ );
END mute_controller;
ARCHITECTURE Behavioral OF mute_controller IS
- SIGNAL m_axis_tvalid_int : STD_LOGIC;
+ -- Internal signal for output valid control
+ SIGNAL m_axis_tvalid_int : STD_LOGIC;
BEGIN
- -- Assigning the output signals
- m_axis_tvalid <= m_axis_tvalid_int;
- s_axis_tready <= (m_axis_tready OR NOT m_axis_tvalid_int) AND aresetn;
+ -- Connect internal signals to output ports
+ m_axis_tvalid <= m_axis_tvalid_int;
+
+ -- Input ready logic: Ready when downstream is ready OR no valid data pending, AND not in reset
+ -- This implements proper AXI4-Stream backpressure handling
+ s_axis_tready <= (m_axis_tready OR NOT m_axis_tvalid_int) AND aresetn;
- PROCESS (aclk)
- BEGIN
+ -- Main mute control process
+ -- Handles AXI4-Stream protocol and mute functionality
+ PROCESS (aclk)
+ BEGIN
- IF rising_edge(aclk) THEN
+ IF rising_edge(aclk) THEN
- IF aresetn = '0' THEN
- m_axis_tvalid_int <= '0';
- m_axis_tlast <= '0';
+ IF aresetn = '0' THEN
+ -- Reset state: Clear all output control signals
+ m_axis_tvalid_int <= '0'; -- No valid output data during reset
+ m_axis_tlast <= '0'; -- Clear channel indicator
- ELSE
- -- Clear valid flag when master interface is ready
- IF m_axis_tready = '1' THEN
- m_axis_tvalid_int <= '0';
- END IF;
+ ELSE
+ -- Normal operation
- -- Handle the data flow
- IF s_axis_tvalid = '1' AND m_axis_tready = '1' THEN
- IF mute = '1' THEN
- m_axis_tdata <= (OTHERS => '0');
- ELSE
- m_axis_tdata <= s_axis_tdata;
- END IF;
+ -- Output handshake management:
+ -- Clear valid flag when downstream accepts data
+ -- This allows new data to be output on the next cycle
+ IF m_axis_tready = '1' THEN
+ m_axis_tvalid_int <= '0';
+ END IF;
- m_axis_tvalid_int <= '1';
- m_axis_tlast <= s_axis_tlast;
+ -- Data processing: Handle mute control when both input and output are ready
+ -- This ensures proper AXI4-Stream handshaking
+ IF s_axis_tvalid = '1' AND m_axis_tready = '1' THEN
+ -- Mute control logic:
+ IF mute = '1' THEN
+ -- Mute active: Replace audio data with silence (all zeros)
+ -- This effectively removes all audio content while maintaining data flow
+ m_axis_tdata <= (OTHERS => '0');
+ ELSE
+ -- Mute inactive: Pass audio data through unchanged
+ -- Normal audio processing mode
+ m_axis_tdata <= s_axis_tdata;
+ END IF;
- END IF;
+ -- Set output control signals
+ m_axis_tvalid_int <= '1'; -- Mark output data as valid
+ m_axis_tlast <= s_axis_tlast; -- Pass through channel indicator unchanged
- END IF;
+ END IF;
- END IF;
-
- END PROCESS;
+ END IF;
+
+ END IF;
+
+ END PROCESS;
END Behavioral;
\ No newline at end of file
diff --git a/LAB3/src/volume_controller.vhd b/LAB3/src/volume_controller.vhd
index f983367..b251243 100644
--- a/LAB3/src/volume_controller.vhd
+++ b/LAB3/src/volume_controller.vhd
@@ -2,137 +2,169 @@ LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;
+-- Entity: volume_controller
+-- Purpose: Controls audio volume by scaling audio samples according to volume control input
+-- Implements a two-stage processing pipeline: multiplication followed by saturation
+-- This approach prevents overflow and distortion in the audio signal
ENTITY volume_controller IS
- GENERIC (
- TDATA_WIDTH : POSITIVE := 24;
- VOLUME_WIDTH : POSITIVE := 10;
- VOLUME_STEP_2 : POSITIVE := 6; -- i.e., volume_values_per_step = 2**VOLUME_STEP_2
- HIGHER_BOUND : INTEGER := 2 ** 23 - 1; -- Inclusive
- LOWER_BOUND : INTEGER := - 2 ** 23 -- Inclusive
- );
- PORT (
- aclk : IN STD_LOGIC;
- aresetn : IN STD_LOGIC;
+ GENERIC (
+ TDATA_WIDTH : POSITIVE := 24; -- Width of audio data bus (24-bit audio samples)
+ VOLUME_WIDTH : POSITIVE := 10; -- Width of volume control input (10-bit = 0-1023 range)
+ VOLUME_STEP_2 : POSITIVE := 6; -- Log2 of volume values per step (2^6 = 64 values per step)
+ HIGHER_BOUND : INTEGER := 2 ** 23 - 1; -- Maximum positive value for saturation (inclusive)
+ LOWER_BOUND : INTEGER := - 2 ** 23 -- Maximum negative value for saturation (inclusive)
+ );
+ PORT (
+ -- Clock and reset signals
+ aclk : IN STD_LOGIC; -- Main clock input
+ aresetn : IN STD_LOGIC; -- Active-low asynchronous reset
- s_axis_tvalid : IN STD_LOGIC;
- s_axis_tdata : IN STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0);
- s_axis_tlast : IN STD_LOGIC;
- s_axis_tready : OUT STD_LOGIC;
+ -- AXI4-Stream Slave Interface (Audio Input)
+ s_axis_tvalid : IN STD_LOGIC; -- Input data valid signal
+ s_axis_tdata : IN STD_LOGIC_VECTOR(TDATA_WIDTH - 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
- m_axis_tvalid : OUT STD_LOGIC;
- m_axis_tdata : OUT STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0);
- m_axis_tlast : OUT STD_LOGIC;
- m_axis_tready : IN STD_LOGIC;
+ -- AXI4-Stream Master Interface (Audio Output)
+ m_axis_tvalid : OUT STD_LOGIC; -- Output data valid signal
+ m_axis_tdata : OUT STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0); -- Audio sample output (volume adjusted)
+ m_axis_tlast : OUT STD_LOGIC; -- Channel indicator passthrough
+ m_axis_tready : IN STD_LOGIC; -- Downstream ready signal
- volume : IN STD_LOGIC_VECTOR(VOLUME_WIDTH - 1 DOWNTO 0)
- );
+ -- Volume control input
+ volume : IN STD_LOGIC_VECTOR(VOLUME_WIDTH - 1 DOWNTO 0) -- Volume level (0=minimum, 1023=maximum)
+ );
END volume_controller;
ARCHITECTURE Behavioral OF volume_controller IS
- -- Component declarations
- COMPONENT volume_multiplier IS
- GENERIC (
- TDATA_WIDTH : POSITIVE := 24;
- VOLUME_WIDTH : POSITIVE := 10;
- VOLUME_STEP_2 : POSITIVE := 6 -- i.e., volume_values_per_step = 2**VOLUME_STEP_2
- );
- PORT (
- aclk : IN STD_LOGIC;
- aresetn : IN STD_LOGIC;
+ -- Component declaration for volume multiplier
+ -- First stage: multiplies audio samples by volume scaling factor
+ -- Output has wider bit width to accommodate multiplication results
+ COMPONENT volume_multiplier IS
+ GENERIC (
+ TDATA_WIDTH : POSITIVE := 24; -- Input audio data width
+ VOLUME_WIDTH : POSITIVE := 10; -- Volume control width
+ VOLUME_STEP_2 : POSITIVE := 6 -- Step size for volume control
+ );
+ PORT (
+ aclk : IN STD_LOGIC;
+ aresetn : IN STD_LOGIC;
- s_axis_tvalid : IN STD_LOGIC;
- s_axis_tdata : IN STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0);
- s_axis_tlast : IN STD_LOGIC;
- s_axis_tready : OUT STD_LOGIC;
+ -- Input AXI4-Stream interface
+ s_axis_tvalid : IN STD_LOGIC;
+ s_axis_tdata : IN STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0);
+ s_axis_tlast : IN STD_LOGIC;
+ s_axis_tready : OUT STD_LOGIC;
- m_axis_tvalid : OUT STD_LOGIC;
- m_axis_tdata : OUT STD_LOGIC_VECTOR(TDATA_WIDTH - 1 + 2 ** (VOLUME_WIDTH - VOLUME_STEP_2 - 1) DOWNTO 0);
- m_axis_tlast : OUT STD_LOGIC;
- m_axis_tready : IN STD_LOGIC;
+ -- Output AXI4-Stream interface (wider data width due to multiplication)
+ m_axis_tvalid : OUT STD_LOGIC;
+ m_axis_tdata : OUT STD_LOGIC_VECTOR(TDATA_WIDTH - 1 + 2 ** (VOLUME_WIDTH - VOLUME_STEP_2 - 1) DOWNTO 0);
+ m_axis_tlast : OUT STD_LOGIC;
+ m_axis_tready : IN STD_LOGIC;
- volume : IN STD_LOGIC_VECTOR(VOLUME_WIDTH - 1 DOWNTO 0)
- );
- END COMPONENT;
+ volume : IN STD_LOGIC_VECTOR(VOLUME_WIDTH - 1 DOWNTO 0)
+ );
+ END COMPONENT;
- COMPONENT volume_saturator IS
- GENERIC (
- TDATA_WIDTH : POSITIVE := 24;
- VOLUME_WIDTH : POSITIVE := 10;
- VOLUME_STEP_2 : POSITIVE := 6; -- i.e., number_of_steps = 2**VOLUME_STEP_2
- HIGHER_BOUND : INTEGER := 2 ** 15 - 1; -- Inclusive
- LOWER_BOUND : INTEGER := - 2 ** 15 -- Inclusive
- );
- PORT (
- aclk : IN STD_LOGIC;
- aresetn : IN STD_LOGIC;
+ -- Component declaration for volume saturator
+ -- Second stage: clips multiplication results to prevent overflow and distortion
+ -- Reduces bit width back to original audio format
+ COMPONENT volume_saturator IS
+ GENERIC (
+ TDATA_WIDTH : POSITIVE := 24; -- Final audio data width
+ VOLUME_WIDTH : POSITIVE := 10; -- Volume control width
+ VOLUME_STEP_2 : POSITIVE := 6; -- Step size for volume control
+ HIGHER_BOUND : INTEGER := 2 ** 15 - 1; -- Upper saturation limit (inclusive)
+ LOWER_BOUND : INTEGER := - 2 ** 15 -- Lower saturation limit (inclusive)
+ );
+ PORT (
+ aclk : IN STD_LOGIC;
+ aresetn : IN STD_LOGIC;
- s_axis_tvalid : IN STD_LOGIC;
- s_axis_tdata : IN STD_LOGIC_VECTOR(TDATA_WIDTH - 1 + 2 ** (VOLUME_WIDTH - VOLUME_STEP_2 - 1) DOWNTO 0);
- s_axis_tlast : IN STD_LOGIC;
- s_axis_tready : OUT STD_LOGIC;
+ -- Input AXI4-Stream interface (wide data from multiplier)
+ s_axis_tvalid : IN STD_LOGIC;
+ s_axis_tdata : IN STD_LOGIC_VECTOR(TDATA_WIDTH - 1 + 2 ** (VOLUME_WIDTH - VOLUME_STEP_2 - 1) DOWNTO 0);
+ s_axis_tlast : IN STD_LOGIC;
+ s_axis_tready : OUT STD_LOGIC;
- m_axis_tvalid : OUT STD_LOGIC;
- m_axis_tdata : OUT STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0);
- m_axis_tlast : OUT STD_LOGIC;
- m_axis_tready : IN STD_LOGIC
- );
- END COMPONENT;
+ -- Output AXI4-Stream interface (original audio data width)
+ m_axis_tvalid : OUT STD_LOGIC;
+ m_axis_tdata : OUT STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0);
+ m_axis_tlast : OUT STD_LOGIC;
+ m_axis_tready : IN STD_LOGIC
+ );
+ END COMPONENT;
- -- Internal AXIS signals
- SIGNAL int_axis_tvalid : STD_LOGIC;
- SIGNAL int_axis_tready : STD_LOGIC;
- SIGNAL int_axis_tdata : STD_LOGIC_VECTOR(TDATA_WIDTH - 1 + 2 ** (VOLUME_WIDTH - VOLUME_STEP_2 - 1) DOWNTO 0);
- SIGNAL int_axis_tlast : STD_LOGIC;
+ -- Internal AXI4-Stream signals between multiplier and saturator
+ -- These signals carry the wide multiplication results before saturation
+ SIGNAL int_axis_tvalid : STD_LOGIC; -- Valid signal between stages
+ SIGNAL int_axis_tready : STD_LOGIC; -- Ready signal between stages
+ SIGNAL int_axis_tdata : STD_LOGIC_VECTOR(TDATA_WIDTH - 1 + 2 ** (VOLUME_WIDTH - VOLUME_STEP_2 - 1) DOWNTO 0); -- Wide data between stages
+ SIGNAL int_axis_tlast : STD_LOGIC; -- Channel indicator between stages
BEGIN
- -- Instantiate volume_multiplier
- volume_multiplier_inst : volume_multiplier
- GENERIC MAP(
- TDATA_WIDTH => TDATA_WIDTH,
- VOLUME_WIDTH => VOLUME_WIDTH,
- VOLUME_STEP_2 => VOLUME_STEP_2
- )
- PORT MAP(
- aclk => aclk,
- aresetn => aresetn,
+ -- Instantiate volume_multiplier (First Stage)
+ -- Multiplies incoming audio samples by volume scaling factor
+ -- Output has extended bit width to prevent loss of precision
+ volume_multiplier_inst : volume_multiplier
+ GENERIC MAP(
+ TDATA_WIDTH => TDATA_WIDTH, -- Input audio sample width
+ VOLUME_WIDTH => VOLUME_WIDTH, -- Volume control resolution
+ VOLUME_STEP_2 => VOLUME_STEP_2 -- Volume step size
+ )
+ PORT MAP(
+ aclk => aclk,
+ aresetn => aresetn,
- s_axis_tvalid => s_axis_tvalid,
- s_axis_tdata => s_axis_tdata,
- s_axis_tlast => s_axis_tlast,
- s_axis_tready => s_axis_tready,
+ -- Connect to external input interface
+ s_axis_tvalid => s_axis_tvalid,
+ s_axis_tdata => s_axis_tdata,
+ s_axis_tlast => s_axis_tlast,
+ s_axis_tready => s_axis_tready,
- m_axis_tvalid => int_axis_tvalid,
- m_axis_tdata => int_axis_tdata,
- m_axis_tlast => int_axis_tlast,
- m_axis_tready => int_axis_tready,
+ -- Connect to internal interface (wide data)
+ m_axis_tvalid => int_axis_tvalid,
+ m_axis_tdata => int_axis_tdata,
+ m_axis_tlast => int_axis_tlast,
+ m_axis_tready => int_axis_tready,
- volume => volume
- );
+ volume => volume
+ );
- -- Instantiate volume_saturator
- volume_saturator_inst : volume_saturator
- GENERIC MAP(
- TDATA_WIDTH => TDATA_WIDTH,
- VOLUME_WIDTH => VOLUME_WIDTH,
- VOLUME_STEP_2 => VOLUME_STEP_2,
- HIGHER_BOUND => HIGHER_BOUND,
- LOWER_BOUND => LOWER_BOUND
- )
- PORT MAP(
- aclk => aclk,
- aresetn => aresetn,
+ -- Instantiate volume_saturator (Second Stage)
+ -- Clips multiplication results to prevent overflow and distortion
+ -- Reduces bit width back to original audio format for output
+ volume_saturator_inst : volume_saturator
+ GENERIC MAP(
+ TDATA_WIDTH => TDATA_WIDTH, -- Final audio sample width
+ VOLUME_WIDTH => VOLUME_WIDTH, -- Volume control resolution
+ VOLUME_STEP_2 => VOLUME_STEP_2, -- Volume step size
+ HIGHER_BOUND => HIGHER_BOUND, -- Upper saturation limit
+ LOWER_BOUND => LOWER_BOUND -- Lower saturation limit
+ )
+ PORT MAP(
+ aclk => aclk,
+ aresetn => aresetn,
- s_axis_tvalid => int_axis_tvalid,
- s_axis_tdata => int_axis_tdata,
- s_axis_tlast => int_axis_tlast,
- s_axis_tready => int_axis_tready,
+ -- Connect to internal interface (wide data from multiplier)
+ s_axis_tvalid => int_axis_tvalid,
+ s_axis_tdata => int_axis_tdata,
+ s_axis_tlast => int_axis_tlast,
+ s_axis_tready => int_axis_tready,
- m_axis_tvalid => m_axis_tvalid,
- m_axis_tdata => m_axis_tdata,
- m_axis_tlast => m_axis_tlast,
- m_axis_tready => m_axis_tready
- );
+ -- Connect to external output interface
+ m_axis_tvalid => m_axis_tvalid,
+ m_axis_tdata => m_axis_tdata,
+ m_axis_tlast => m_axis_tlast,
+ m_axis_tready => m_axis_tready
+ );
+
+ -- Pipeline Operation:
+ -- 1. Audio samples enter volume_multiplier with original bit width
+ -- 2. Multiplier scales samples by volume factor, output has extended bit width
+ -- 3. Saturator clips results to prevent overflow, reduces to original bit width
+ -- 4. Final audio samples has adjusted volume and bit width, ready for downstream processing
END Behavioral;
\ No newline at end of file
diff --git a/LAB3/src/volume_multiplier.vhd b/LAB3/src/volume_multiplier.vhd
index dd4f041..19106fb 100644
--- a/LAB3/src/volume_multiplier.vhd
+++ b/LAB3/src/volume_multiplier.vhd
@@ -2,107 +2,168 @@ LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;
+-- Entity: volume_multiplier
+-- Purpose: First stage of volume control pipeline - multiplies audio samples by volume scaling factor
+-- Uses bit-shifting multiplication for efficient hardware implementation
+-- Implements exponential volume scaling for natural-feeling volume control
ENTITY volume_multiplier IS
- GENERIC (
- TDATA_WIDTH : POSITIVE := 24;
- VOLUME_WIDTH : POSITIVE := 10;
- VOLUME_STEP_2 : POSITIVE := 6 -- i.e., volume_values_per_step = 2**VOLUME_STEP_2
- );
- PORT (
- aclk : IN STD_LOGIC;
- aresetn : IN STD_LOGIC;
+ GENERIC (
+ TDATA_WIDTH : POSITIVE := 24; -- Width of input audio data (24-bit audio samples)
+ VOLUME_WIDTH : POSITIVE := 10; -- Width of volume control input (10-bit = 0-1023 range)
+ VOLUME_STEP_2 : POSITIVE := 6 -- Log2 of volume values per step (2^6 = 64 values per step)
+ );
+ PORT (
+ -- Clock and reset signals
+ aclk : IN STD_LOGIC; -- Main clock input
+ aresetn : IN STD_LOGIC; -- Active-low asynchronous reset
- s_axis_tvalid : IN STD_LOGIC;
- s_axis_tdata : IN STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0);
- s_axis_tlast : IN STD_LOGIC;
- s_axis_tready : OUT STD_LOGIC;
+ -- AXI4-Stream Slave Interface (Audio Input)
+ s_axis_tvalid : IN STD_LOGIC; -- Input data valid signal
+ s_axis_tdata : IN STD_LOGIC_VECTOR(TDATA_WIDTH - 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
- m_axis_tvalid : OUT STD_LOGIC;
- m_axis_tdata : OUT STD_LOGIC_VECTOR(TDATA_WIDTH - 1 + 2 ** (VOLUME_WIDTH - VOLUME_STEP_2 - 1) DOWNTO 0);
- m_axis_tlast : OUT STD_LOGIC;
- m_axis_tready : IN STD_LOGIC;
+ -- AXI4-Stream Master Interface (Audio Output with extended width)
+ m_axis_tvalid : OUT STD_LOGIC; -- Output data valid signal
+ m_axis_tdata : OUT STD_LOGIC_VECTOR(TDATA_WIDTH - 1 + 2 ** (VOLUME_WIDTH - VOLUME_STEP_2 - 1) DOWNTO 0); -- Extended width output data
+ m_axis_tlast : OUT STD_LOGIC; -- Channel indicator passthrough
+ m_axis_tready : IN STD_LOGIC; -- Downstream ready signal
- volume : IN STD_LOGIC_VECTOR(VOLUME_WIDTH - 1 DOWNTO 0)
- );
+ -- Volume control input
+ volume : IN STD_LOGIC_VECTOR(VOLUME_WIDTH - 1 DOWNTO 0) -- Volume level (0=minimum, 1023=maximum)
+ );
END volume_multiplier;
ARCHITECTURE Behavioral OF volume_multiplier IS
- CONSTANT VOLUME_STEPS : INTEGER := (2 ** (VOLUME_WIDTH - 1)) / (2 ** VOLUME_STEP_2) + 1;
- CONSTANT VOL_MID : INTEGER := 2 ** (VOLUME_WIDTH - 1); -- 512 for 10 bit
- CONSTANT DEAD_ZONE : INTEGER := (2 ** VOLUME_STEP_2) / 2;
+ -- Calculate volume control parameters based on generics
+ CONSTANT VOLUME_STEPS : INTEGER := (2 ** (VOLUME_WIDTH - 1)) / (2 ** VOLUME_STEP_2) + 1; -- Number of volume steps (9 steps for 10-bit)
+ CONSTANT VOL_MID : INTEGER := 2 ** (VOLUME_WIDTH - 1); -- Center volume position (512 for 10-bit)
+ CONSTANT DEAD_ZONE : INTEGER := (2 ** VOLUME_STEP_2) / 2; -- Dead zone around center (32 for step size 64)
- SIGNAL volume_exp_mult : INTEGER RANGE -VOLUME_STEPS TO VOLUME_STEPS := 0;
+ -- Volume scaling factor as exponential multiplier
+ -- Positive values = amplification (left shift), negative values = attenuation (right shift)
+ SIGNAL volume_exp_mult : INTEGER RANGE -VOLUME_STEPS TO VOLUME_STEPS := 0;
- SIGNAL m_axis_tvalid_int : STD_LOGIC;
+ -- Internal AXI4-Stream signals
+ SIGNAL m_axis_tvalid_int : STD_LOGIC; -- Internal valid signal for output
BEGIN
- -- Assigning the output signals
- m_axis_tvalid <= m_axis_tvalid_int;
- s_axis_tready <= (m_axis_tready OR NOT m_axis_tvalid_int) AND aresetn;
+ -- Connect internal signals to output ports
+ m_axis_tvalid <= m_axis_tvalid_int;
+
+ -- Input ready logic: Ready when downstream is ready OR no valid data pending, AND not in reset
+ -- This implements proper AXI4-Stream backpressure handling
+ s_axis_tready <= (m_axis_tready OR NOT m_axis_tvalid_int) AND aresetn;
- -- Volume to exp process to avoid changing the volume value when multiplying it for the sample data
- PROCESS (aclk)
- BEGIN
+ -- Volume to exponent conversion process
+ -- Converts joystick position to bit-shift amount for exponential volume scaling
+ PROCESS (aclk)
+ BEGIN
- IF rising_edge(aclk) THEN
+ IF rising_edge(aclk) THEN
- IF aresetn = '0' THEN
- volume_exp_mult <= 0;
+ IF aresetn = '0' THEN
+ -- Reset: Set to unity gain (no scaling)
+ volume_exp_mult <= 0;
- ELSE
- -- Volume to signed and centered and convert to power of 2 exponent
- volume_exp_mult <= to_integer(
- shift_right(
- signed('0' & volume) - to_signed(VOL_MID - DEAD_ZONE, volume'length + 1),
- VOLUME_STEP_2
- )
- );
+ ELSE
+ -- Volume mapping and conversion to exponential scaling factor:
+ -- 1. Convert volume to signed value
+ -- 2. Center around middle position (VOL_MID)
+ -- 3. Apply dead zone offset for smooth center operation
+ -- 4. Divide by step size to get exponential scaling factor
+ --
+ -- Volume Range Mapping:
+ -- volume = 0-479: Negative exponent (attenuation, right shift)
+ -- volume = 480-543: Zero exponent (unity gain, dead zone)
+ -- volume = 544-1023: Positive exponent (amplification, left shift)
+ volume_exp_mult <= to_integer(
+ shift_right(
+ signed('0' & volume) - to_signed(VOL_MID - DEAD_ZONE, volume'length + 1),
+ VOLUME_STEP_2
+ )
+ );
- END IF;
+ END IF;
- END IF;
+ END IF;
- END PROCESS;
+ END PROCESS;
- -- Handle AXIS stream
- PROCESS (aclk)
- BEGIN
+ -- AXI4-Stream data processing
+ -- Applies exponential volume scaling using bit-shifting multiplication
+ PROCESS (aclk)
+ BEGIN
- IF rising_edge(aclk) THEN
+ IF rising_edge(aclk) THEN
- IF aresetn = '0' THEN
- m_axis_tvalid_int <= '0';
- m_axis_tlast <= '0';
+ IF aresetn = '0' THEN
+ -- Reset output interface
+ m_axis_tvalid_int <= '0'; -- No valid output data
+ m_axis_tlast <= '0'; -- Clear channel indicator
- ELSE
- -- Clear valid flag when master interface is ready
- IF m_axis_tready = '1' THEN
- m_axis_tvalid_int <= '0';
- END IF;
+ ELSE
+ -- Output handshake management:
+ -- Clear valid flag when downstream accepts data
+ IF m_axis_tready = '1' THEN
+ m_axis_tvalid_int <= '0';
+ END IF;
- -- Handle the data flow
- IF s_axis_tvalid = '1' AND m_axis_tready = '1' THEN
- -- Multiply the input data with the volume and assign to output
- -- Joystick datasheet: (y-axis) a value of 0 when it is tilted all the way down
- -- and a value of 1023 when it is tilted all the way up
- IF volume_exp_mult >= 0 THEN
- m_axis_tdata <= STD_LOGIC_VECTOR(shift_left(resize(signed(s_axis_tdata), m_axis_tdata'LENGTH), volume_exp_mult));
+ -- Data processing: Apply volume scaling when both input and output are ready
+ IF s_axis_tvalid = '1' AND m_axis_tready = '1' THEN
+ -- Volume scaling using bit-shifting for exponential response:
+ --
+ -- Joystick mapping (from datasheet):
+ -- Y-axis: 0 = tilted all the way down (minimum volume)
+ -- 1023 = tilted all the way up (maximum volume)
+ --
+ -- Scaling method:
+ -- Positive exponent: Left shift = amplification (multiply by 2^n)
+ -- Negative exponent: Right shift = attenuation (divide by 2^n)
+ -- Zero exponent: No shift = unity gain (pass through)
+
+ IF volume_exp_mult >= 0 THEN
+ -- Amplification: Left shift for volume boost
+ -- Resize to extended width first to prevent overflow
+ m_axis_tdata <= STD_LOGIC_VECTOR(
+ shift_left(
+ resize(signed(s_axis_tdata), m_axis_tdata'LENGTH),
+ volume_exp_mult
+ )
+ );
- ELSE
- m_axis_tdata <= STD_LOGIC_VECTOR(shift_right(resize(signed(s_axis_tdata), m_axis_tdata'LENGTH), - volume_exp_mult));
+ ELSE
+ -- Attenuation: Right shift for volume reduction
+ -- Arithmetic right shift preserves sign for signed audio data
+ m_axis_tdata <= STD_LOGIC_VECTOR(
+ shift_right(
+ resize(signed(s_axis_tdata), m_axis_tdata'LENGTH),
+ - volume_exp_mult -- Convert negative to positive shift amount
+ )
+ );
- END IF;
+ END IF;
- m_axis_tvalid_int <= '1';
- m_axis_tlast <= s_axis_tlast;
+ -- Set output control signals
+ m_axis_tvalid_int <= '1'; -- Mark output data as valid
+ m_axis_tlast <= s_axis_tlast; -- Pass through channel indicator
- END IF;
+ END IF;
- END IF;
+ END IF;
- END IF;
+ END IF;
- END PROCESS;
+ END PROCESS;
+
+ -- Example scaling factors (VOLUME_STEP_2 = 6, 64 values per step):
+ -- volume_exp_mult = -3: Divide by 8 (>>3)
+ -- volume_exp_mult = -2: Divide by 4 (>>2)
+ -- volume_exp_mult = -1: Divide by 2 (>>1)
+ -- volume_exp_mult = 0: No change (unity)
+ -- volume_exp_mult = +1: Multiply by 2 (<<1)
+ -- volume_exp_mult = +2: Multiply by 4 (<<2)
+ -- volume_exp_mult = +3: Multiply by 8 (<<3)
END Behavioral;
\ No newline at end of file
diff --git a/LAB3/src/volume_saturator.vhd b/LAB3/src/volume_saturator.vhd
index 10549d4..f1c063c 100644
--- a/LAB3/src/volume_saturator.vhd
+++ b/LAB3/src/volume_saturator.vhd
@@ -2,80 +2,123 @@ LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;
+-- Entity: volume_saturator
+-- Purpose: Second stage of volume control pipeline - clips multiplication results to prevent overflow
+-- Reduces bit width from extended multiplier output back to original audio format
+-- Implements saturation (clipping) to prevent audio distortion from mathematical overflow
ENTITY volume_saturator IS
- GENERIC (
- TDATA_WIDTH : POSITIVE := 24;
- VOLUME_WIDTH : POSITIVE := 10;
- VOLUME_STEP_2 : POSITIVE := 6; -- i.e., number_of_steps = 2**(VOLUME_STEP_2)
- HIGHER_BOUND : INTEGER := 2 ** 15 - 1; -- Inclusive
- LOWER_BOUND : INTEGER := - 2 ** 15 -- Inclusive
- );
- PORT (
- aclk : IN STD_LOGIC;
- aresetn : IN STD_LOGIC;
+ GENERIC (
+ TDATA_WIDTH : POSITIVE := 24; -- Width of final audio data output (24-bit audio samples)
+ VOLUME_WIDTH : POSITIVE := 10; -- Width of volume control input (10-bit = 0-1023 range)
+ VOLUME_STEP_2 : POSITIVE := 6; -- Log2 of volume values per step (2^6 = 64 values per step)
+ HIGHER_BOUND : INTEGER := 2 ** 15 - 1; -- Maximum positive value for saturation (inclusive)
+ LOWER_BOUND : INTEGER := - 2 ** 15 -- Maximum negative value for saturation (inclusive)
+ );
+ PORT (
+ -- Clock and reset signals
+ aclk : IN STD_LOGIC; -- Main clock input
+ aresetn : IN STD_LOGIC; -- Active-low asynchronous reset
- s_axis_tvalid : IN STD_LOGIC;
- s_axis_tdata : IN STD_LOGIC_VECTOR(TDATA_WIDTH - 1 + 2 ** (VOLUME_WIDTH - VOLUME_STEP_2 - 1) DOWNTO 0);
- s_axis_tlast : IN STD_LOGIC;
- s_axis_tready : OUT STD_LOGIC;
+ -- AXI4-Stream Slave Interface (Extended width input from multiplier)
+ s_axis_tvalid : IN STD_LOGIC; -- Input data valid signal
+ s_axis_tdata : IN STD_LOGIC_VECTOR(TDATA_WIDTH - 1 + 2 ** (VOLUME_WIDTH - VOLUME_STEP_2 - 1) DOWNTO 0); -- Wide input data from multiplier
+ s_axis_tlast : IN STD_LOGIC; -- Channel indicator (0=left, 1=right)
+ s_axis_tready : OUT STD_LOGIC; -- Ready to accept input data
- m_axis_tvalid : OUT STD_LOGIC;
- m_axis_tdata : OUT STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0);
- m_axis_tlast : OUT STD_LOGIC;
- m_axis_tready : IN STD_LOGIC
- );
+ -- AXI4-Stream Master Interface (Original width output for audio)
+ m_axis_tvalid : OUT STD_LOGIC; -- Output data valid signal
+ m_axis_tdata : OUT STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0); -- Saturated audio sample output
+ m_axis_tlast : OUT STD_LOGIC; -- Channel indicator passthrough
+ m_axis_tready : IN STD_LOGIC -- Downstream ready signal
+ );
END volume_saturator;
ARCHITECTURE Behavioral OF volume_saturator IS
- CONSTANT HIGHER_BOUND_VEC : STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0) := STD_LOGIC_VECTOR(to_signed(HIGHER_BOUND, TDATA_WIDTH));
- CONSTANT LOWER_BOUND_VEC : STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0) := STD_LOGIC_VECTOR(to_signed(LOWER_BOUND, TDATA_WIDTH));
+ -- Pre-calculated saturation bounds as STD_LOGIC_VECTORS for efficient comparison
+ -- These constants define the valid range for audio samples after volume processing
+ CONSTANT HIGHER_BOUND_VEC : STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0) := STD_LOGIC_VECTOR(to_signed(HIGHER_BOUND, TDATA_WIDTH));
+ CONSTANT LOWER_BOUND_VEC : STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0) := STD_LOGIC_VECTOR(to_signed(LOWER_BOUND, TDATA_WIDTH));
- SIGNAL m_axis_tvalid_int : STD_LOGIC;
+ -- Internal AXI4-Stream control signal
+ SIGNAL m_axis_tvalid_int : STD_LOGIC;
BEGIN
- -- Output assignments
- m_axis_tvalid <= m_axis_tvalid_int;
- s_axis_tready <= (m_axis_tready OR NOT m_axis_tvalid_int) AND aresetn;
- PROCESS (aclk)
- BEGIN
+ -- Connect internal signals to output ports
+ m_axis_tvalid <= m_axis_tvalid_int;
+
+ -- Input ready logic: Ready when downstream is ready OR no valid data pending, AND not in reset
+ -- This implements proper AXI4-Stream backpressure handling
+ s_axis_tready <= (m_axis_tready OR NOT m_axis_tvalid_int) AND aresetn;
- IF rising_edge(aclk) THEN
+ -- Main saturation and data processing logic
+ PROCESS (aclk)
+ BEGIN
- IF aresetn = '0' THEN
- m_axis_tvalid_int <= '0';
- m_axis_tlast <= '0';
+ IF rising_edge(aclk) THEN
- ELSE
- -- Clear valid flag when master interface is ready
- IF m_axis_tready = '1' THEN
- m_axis_tvalid_int <= '0';
- END IF;
+ IF aresetn = '0' THEN
+ -- Reset output interface
+ m_axis_tvalid_int <= '0'; -- No valid output data during reset
+ m_axis_tlast <= '0'; -- Clear channel indicator
- -- Handle the data flow
- IF s_axis_tvalid = '1' AND m_axis_tready = '1' THEN
- -- Check if the input data is within the bounds else saturate
- IF signed(s_axis_tdata) > signed(HIGHER_BOUND_VEC) THEN
- m_axis_tdata <= HIGHER_BOUND_VEC;
+ ELSE
+ -- Normal operation
- ELSIF signed(s_axis_tdata) < signed(LOWER_BOUND_VEC) THEN
- m_axis_tdata <= LOWER_BOUND_VEC;
+ -- Output handshake management:
+ -- Clear valid flag when downstream accepts data
+ IF m_axis_tready = '1' THEN
+ m_axis_tvalid_int <= '0';
+ END IF;
- ELSE
- m_axis_tdata <= STD_LOGIC_VECTOR(resize(signed(s_axis_tdata), TDATA_WIDTH));
-
- END IF;
+ -- Data processing: Apply saturation when both input and output are ready
+ IF s_axis_tvalid = '1' AND m_axis_tready = '1' THEN
+
+ -- Saturation Logic:
+ -- Check if the wide input data exceeds the valid audio range
+ -- If so, clip (saturate) to the nearest valid value
+ -- This prevents overflow distortion while preserving audio quality
+
+ IF signed(s_axis_tdata) > signed(HIGHER_BOUND_VEC) THEN
+ -- Positive overflow: Clip to maximum positive value
+ -- This prevents "wraparound" distortion from mathematical overflow
+ m_axis_tdata <= HIGHER_BOUND_VEC;
- m_axis_tvalid_int <= '1';
- m_axis_tlast <= s_axis_tlast;
+ ELSIF signed(s_axis_tdata) < signed(LOWER_BOUND_VEC) THEN
+ -- Negative overflow: Clip to maximum negative value
+ -- This prevents "wraparound" distortion from mathematical underflow
+ m_axis_tdata <= LOWER_BOUND_VEC;
- END IF;
+ ELSE
+ -- Value within valid range: Resize to output width without clipping
+ -- This preserves the original audio quality when no overflow occurs
+ m_axis_tdata <= STD_LOGIC_VECTOR(resize(signed(s_axis_tdata), TDATA_WIDTH));
+
+ END IF;
- END IF;
+ -- Set output control signals
+ m_axis_tvalid_int <= '1'; -- Mark output data as valid
+ m_axis_tlast <= s_axis_tlast; -- Pass through channel indicator
- END IF;
+ END IF;
- END PROCESS;
+ END IF;
+
+ END IF;
+
+ END PROCESS;
+
+ -- Saturation Purpose and Benefits:
+ -- 1. Prevents audio distortion from mathematical overflow
+ -- 2. Maintains audio dynamic range within valid bit representation
+ -- 3. Reduces wide multiplication results back to standard audio format
+ -- 4. Provides "soft limiting" behavior for volume control
+ -- 5. Ensures compatibility with downstream audio processing blocks
+ --
+ -- Without saturation, volume amplification could cause:
+ -- - Mathematical overflow leading to sign bit flips
+ -- - Severe audio distortion (crackling, popping sounds)
+ -- - Invalid audio sample values outside the representable range
END Behavioral;
\ No newline at end of file
diff --git a/LAB3/vivado/LFO/LFO.xpr b/LAB3/vivado/LFO/LFO.xpr
index 6467833..7206ae2 100644
--- a/LAB3/vivado/LFO/LFO.xpr
+++ b/LAB3/vivado/LFO/LFO.xpr
@@ -47,7 +47,7 @@
-
+