Lab 3: Audio Processing System #3

Merged
PickleRick merged 43 commits from LAB3 into main 2025-06-07 22:18:48 +02:00
Showing only changes of commit c4a48db729 - Show all commits

View File

@@ -6,39 +6,36 @@ USE IEEE.MATH_REAL.ALL;
-- Entity: led_level_controller -- Entity: led_level_controller
-- Purpose: Audio level meter using LEDs to display real-time audio amplitude -- Purpose: Audio level meter using LEDs to display real-time audio amplitude
-- Processes stereo audio samples and drives a bar graph LED display -- 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 ENTITY led_level_controller IS
GENERIC ( GENERIC (
NUM_LEDS : POSITIVE := 16; -- Number of LEDs in the level meter display NUM_LEDS : POSITIVE := 16; -- Number of LEDs in the level meter display
CHANNEL_LENGHT : POSITIVE := 24; -- Width of audio data (24-bit audio samples) 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) 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) clock_period_ns : POSITIVE := 10 -- System clock period in nanoseconds (10ns = 100MHz)
); );
PORT ( PORT (
-- Clock and reset signals -- Clock and reset signals
aclk : IN STD_LOGIC; -- Main clock input aclk : IN STD_LOGIC; -- Main clock input
aresetn : IN STD_LOGIC; -- Active-low asynchronous reset aresetn : IN STD_LOGIC; -- Active-low asynchronous reset
-- LED output array (bar graph display) -- LED output array (bar graph display)
led : OUT STD_LOGIC_VECTOR(NUM_LEDS - 1 DOWNTO 0); -- LED control signals (1=on, 0=off) led : OUT STD_LOGIC_VECTOR(NUM_LEDS - 1 DOWNTO 0); -- LED control signals (1=on, 0=off)
-- AXI4-Stream Slave Interface (Audio Input) -- AXI4-Stream Slave Interface (Audio Input)
s_axis_tvalid : IN STD_LOGIC; -- Input data valid signal 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_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_tlast : IN STD_LOGIC; -- Channel indicator (0=right, 1=left)
s_axis_tready : OUT STD_LOGIC -- Always ready to accept data s_axis_tready : OUT STD_LOGIC -- Always ready to accept data
); );
END led_level_controller; END led_level_controller;
ARCHITECTURE Behavioral OF led_level_controller IS ARCHITECTURE Behavioral OF led_level_controller IS
-- Calculate clock cycles needed for LED refresh timing -- Calculate clock cycles needed for LED refresh timing
-- Formula: (refresh_time_ms * 1_000_000 ns/ms) / clock_period_ns
-- Example: (1ms * 1,000,000) / 10ns = 100,000 cycles for 1ms refresh at 100MHz
CONSTANT REFRESH_CYCLES : INTEGER := (refresh_time_ms * 1_000_000) / clock_period_ns - 1; CONSTANT REFRESH_CYCLES : INTEGER := (refresh_time_ms * 1_000_000) / clock_period_ns - 1;
-- Calculate the number of bits needed to represent the number of LEDs -- Calculate the number of bits needed to represent the number of LEDs
CONSTANT NUMLEDS_BITS : INTEGER := INTEGER(ceil(log2(real(NUM_LEDS)))); CONSTANT NUMLEDS_BITS : INTEGER := INTEGER(ceil(log2(real(NUM_LEDS))));
-- LED refresh timing control signals -- LED refresh timing control signals
SIGNAL refresh_counter : INTEGER RANGE 0 TO REFRESH_CYCLES := 0; -- Counts clock cycles between LED updates SIGNAL refresh_counter : INTEGER RANGE 0 TO REFRESH_CYCLES := 0; -- Counts clock cycles between LED updates
@@ -50,17 +47,18 @@ ARCHITECTURE Behavioral OF led_level_controller IS
BEGIN BEGIN
-- AXI4-Stream interface: Always ready to receive audio data -- Always ready for AXI4-Stream input
-- This ensures continuous audio processing without backpressure
s_axis_tready <= '1'; s_axis_tready <= '1';
-- Audio sample acquisition process based on channel identification -- Capture absolute value of input sample for left/right channel
-- Processes incoming stereo audio samples and converts to absolute amplitude values
-- Uses s_axis_tlast to distinguish between left (1) and right (0) channels
PROCESS (aclk) PROCESS (aclk)
VARIABLE signed_sample : SIGNED(CHANNEL_LENGHT - 1 DOWNTO 0); -- Temporary variable for signed arithmetic
VARIABLE signed_sample : SIGNED(CHANNEL_LENGHT - 1 DOWNTO 0);
BEGIN BEGIN
IF rising_edge(aclk) THEN IF rising_edge(aclk) THEN
IF aresetn = '0' THEN IF aresetn = '0' THEN
-- Reset: Clear both channel amplitude registers -- Reset: Clear both channel amplitude registers
abs_l <= (OTHERS => '0'); abs_l <= (OTHERS => '0');
@@ -79,64 +77,72 @@ BEGIN
-- Right channel: Store absolute value of audio sample -- Right channel: Store absolute value of audio sample
abs_r <= UNSIGNED(ABS(signed_sample)); abs_r <= UNSIGNED(ABS(signed_sample));
END IF; END IF;
END IF; END IF;
END IF; END IF;
END PROCESS; END PROCESS;
-- LED refresh timing generator process -- LED refresh tick generator
-- Creates a periodic tick signal to control LED update rate
-- Prevents LED flickering by limiting update frequency to human-visible rates
PROCESS (aclk) PROCESS (aclk)
BEGIN BEGIN
IF rising_edge(aclk) THEN IF rising_edge(aclk) THEN
IF aresetn = '0' THEN IF aresetn = '0' THEN
-- Reset: Initialize counter and tick signal -- Reset: Initialize counter and tick signal
refresh_counter <= 0; refresh_counter <= 0;
refresh_tick <= '0'; refresh_tick <= '0';
ELSE ELSE
-- Normal operation: Count clock cycles and generate refresh tick
IF refresh_counter = REFRESH_CYCLES THEN IF refresh_counter = REFRESH_CYCLES THEN
-- End of refresh period: Reset counter and generate tick pulse
refresh_counter <= 0; refresh_counter <= 0;
refresh_tick <= '1'; -- Single clock cycle pulse for LED update refresh_tick <= '1';
ELSE ELSE
-- Continue counting: Increment counter, no tick
refresh_counter <= refresh_counter + 1; refresh_counter <= refresh_counter + 1;
refresh_tick <= '0'; refresh_tick <= '0';
END IF; END IF;
END IF; END IF;
END IF; END IF;
END PROCESS; END PROCESS;
-- LED level calculation and bar graph generation process -- LED level calculation and bar graph generation process
-- Combines left and right channel amplitudes and converts to LED display pattern -- Combines left and right channel amplitudes and converts to LED display pattern
-- Updates only when refresh_tick is active to maintain stable visual display -- Updates only when refresh_tick is active to maintain stable visual display
PROCESS (aclk) PROCESS (aclk)
VARIABLE combined_amp : UNSIGNED(CHANNEL_LENGHT - 1 DOWNTO 0); -- Combined amplitude of both channels
VARIABLE led_level : INTEGER RANGE 0 TO 2**NUMLEDS_BITS := 0; -- Calculated LED level for bar graph display VARIABLE combined_amp : UNSIGNED(CHANNEL_LENGHT - 1 DOWNTO 0);
VARIABLE led_level : INTEGER RANGE 0 TO 2 ** NUMLEDS_BITS := 0;
BEGIN BEGIN
IF rising_edge(aclk) THEN IF rising_edge(aclk) THEN
IF aresetn = '0' THEN IF aresetn = '0' THEN
-- Reset: Turn off all LEDs and reset level counter -- Reset: Turn off all LEDs and reset level counter
led <= (OTHERS => '0'); led <= (OTHERS => '0');
ELSIF refresh_tick = '1' THEN ELSIF refresh_tick = '1' THEN
-- LED update cycle: Calculate new LED pattern based on audio amplitude -- LED update cycle: Calculate new LED pattern based on audio amplitude
-- This section is executed once per refresh_tick to avoid flicker and ensure a stable display.
-- Combine left and right channel amplitudes -- Combine left and right channel amplitudes
-- The sum of the absolute values of both channels gives a measure of total audio energy.
-- RESIZE ensures the sum fits within the variable's bit width. -- RESIZE ensures the sum fits within the variable's bit width.
-- There isn't data loss here since both abs_l and abs_r are one bit shorter than combined_amp, -- There isn't data loss since both abs_l and abs_r are one bit shorter than combined_amp,
-- due to the absolute value operation. -- due to the absolute value operation.
combined_amp := RESIZE(abs_l + abs_r, combined_amp'LENGTH); combined_amp := RESIZE(abs_l + abs_r, combined_amp'LENGTH);
-- Normalize combined amplitude to LED scale (0 to NUM_LEDS) -- Linear scale to LED level conversion to get the best visual effect
-- The combined amplitude is mapped to the number of LEDs using a right shift. IF combined_amp = 0 THEN
-- For 24-bit audio, shifting by (CHANNEL_LENGHT - 4) reduces the range to approximately 4 bits (0-15). led_level := 0; -- No audio signal, turn off all LEDs
-- Adding 1 ensures that at least one LED lights up for any non-zero audio input. ELSE
-- Example: For 24-bit input, 1 + (combined_amp >> 20) gives a range from 1 to 16. led_level := 1 + to_integer(shift_right(combined_amp, CHANNEL_LENGHT - NUMLEDS_BITS));
led_level := 1 + to_integer(shift_right(combined_amp, CHANNEL_LENGHT - NUMLEDS_BITS)); END IF;
-- Saturation protection: Limit LED level to maximum available LEDs -- Saturation protection: Limit LED level to maximum available LEDs
-- Prevents overflow and ensures the LED index stays within bounds. -- Prevents overflow and ensures the LED index stays within bounds.
@@ -144,16 +150,16 @@ BEGIN
led_level := NUM_LEDS; led_level := NUM_LEDS;
END IF; END IF;
-- Generate bar graph LED pattern -- Update LED output based on calculated level
-- Implements a "thermometer" style display: all LEDs from 0 up to (led_level-1) are ON.
-- All higher LEDs remain OFF.
-- The assignment first turns all LEDs OFF, then sets the lower 'led_level' LEDs ON.
led <= (OTHERS => '0'); led <= (OTHERS => '0');
IF led_level > 0 THEN IF led_level > 0 THEN
led(led_level - 1 DOWNTO 0) <= (OTHERS => '1'); led(led_level - 1 DOWNTO 0) <= (OTHERS => '1');
END IF; END IF;
END IF; END IF;
END IF; END IF;
END PROCESS; END PROCESS;
END Behavioral; END Behavioral;