Lab 3: Audio Processing System #3
@@ -31,152 +31,123 @@ END led_level_controller;
|
|||||||
|
|
||||||
ARCHITECTURE Behavioral OF led_level_controller IS
|
ARCHITECTURE Behavioral OF led_level_controller IS
|
||||||
|
|
||||||
-- Calculate number of clock cycles for LED refresh timing
|
-- Calculate clock cycles needed for LED refresh timing
|
||||||
-- Example: 1ms refresh at 100MHz = (1*1,000,000)/10 = 100,000 cycles
|
-- Formula: (refresh_time_ms * 1_000_000 ns/ms) / clock_period_ns
|
||||||
CONSTANT REFRESH_CYCLES : NATURAL := (refresh_time_ms * 1_000_000) / 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;
|
||||||
|
|
||||||
-- Audio processing signals
|
-- LED refresh timing control signals
|
||||||
SIGNAL abs_audio_left : unsigned(CHANNEL_LENGHT - 2 DOWNTO 0) := (OTHERS => '0'); -- Absolute value of left channel
|
SIGNAL refresh_counter : INTEGER RANGE 0 TO REFRESH_CYCLES := 0; -- Counts clock cycles between LED updates
|
||||||
SIGNAL abs_audio_right : unsigned(CHANNEL_LENGHT - 2 DOWNTO 0) := (OTHERS => '0'); -- Absolute value of right channel
|
SIGNAL refresh_tick : STD_LOGIC := '0'; -- Pulse signal generated every refresh period
|
||||||
|
|
||||||
-- LED control signals
|
-- Audio amplitude storage for both stereo channels
|
||||||
SIGNAL leds_int : STD_LOGIC_VECTOR(NUM_LEDS - 1 DOWNTO 0) := (OTHERS => '0'); -- Internal LED state
|
-- Stores absolute values (magnitude) of left and right audio channels
|
||||||
SIGNAL led_update : STD_LOGIC := '0'; -- Trigger for LED refresh
|
SIGNAL abs_l, abs_r : UNSIGNED(CHANNEL_LENGHT - 1 DOWNTO 0) := (OTHERS => '0'); -- Absolute amplitude registers
|
||||||
|
|
||||||
-- Timing control
|
|
||||||
SIGNAL refresh_counter : NATURAL RANGE 0 TO REFRESH_CYCLES - 1 := 0; -- Counter for refresh timing
|
|
||||||
|
|
||||||
BEGIN
|
BEGIN
|
||||||
|
|
||||||
-- Connect internal signals to output ports
|
-- AXI4-Stream interface: Always ready to receive audio data
|
||||||
led <= leds_int; -- Drive external LEDs with internal state
|
-- This ensures continuous audio processing without backpressure
|
||||||
s_axis_tready <= '1'; -- Always ready to accept audio data (no backpressure)
|
s_axis_tready <= '1';
|
||||||
|
|
||||||
-- Audio sample processing and absolute value calculation
|
-- Audio sample acquisition process based on channel identification
|
||||||
-- Converts signed audio samples to unsigned absolute values for level detection
|
-- 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 sdata_signed : signed(CHANNEL_LENGHT - 1 DOWNTO 0); -- Temporary signed audio value
|
VARIABLE signed_sample : SIGNED(CHANNEL_LENGHT - 1 DOWNTO 0); -- Temporary variable for signed arithmetic
|
||||||
VARIABLE abs_value : unsigned(CHANNEL_LENGHT - 1 DOWNTO 0); -- Temporary absolute value
|
|
||||||
BEGIN
|
BEGIN
|
||||||
|
|
||||||
IF rising_edge(aclk) THEN
|
IF rising_edge(aclk) THEN
|
||||||
|
|
||||||
IF aresetn = '0' THEN
|
IF aresetn = '0' THEN
|
||||||
-- Reset: Clear all audio processing signals
|
-- Reset: Clear both channel amplitude registers
|
||||||
abs_audio_left <= (OTHERS => '0'); -- Clear left channel level
|
abs_l <= (OTHERS => '0');
|
||||||
abs_audio_right <= (OTHERS => '0'); -- Clear right channel level
|
abs_r <= (OTHERS => '0');
|
||||||
|
|
||||||
ELSIF s_axis_tvalid = '1' THEN
|
ELSIF s_axis_tvalid = '1' THEN
|
||||||
-- Process new audio sample when valid data is available
|
-- Valid audio data received: Process the sample
|
||||||
sdata_signed := signed(s_axis_tdata); -- Convert input to signed format
|
signed_sample := SIGNED(s_axis_tdata); -- Convert input to signed for ABS operation
|
||||||
|
|
||||||
-- Absolute value calculation for amplitude detection
|
-- Channel routing based on AXI4-Stream tlast signal
|
||||||
-- Handle two's complement signed numbers correctly
|
-- tlast = '1' indicates left channel, tlast = '0' indicates right channel
|
||||||
IF s_axis_tdata(s_axis_tdata'high) = '1' THEN
|
|
||||||
-- Negative number: Take two's complement to get absolute value
|
|
||||||
abs_value := unsigned(-sdata_signed);
|
|
||||||
ELSE
|
|
||||||
-- Positive number: Direct conversion to unsigned
|
|
||||||
abs_value := unsigned(sdata_signed);
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
-- Channel assignment based on tlast signal
|
|
||||||
-- Note: Channel assignment appears reversed from typical convention
|
|
||||||
IF s_axis_tlast = '1' THEN
|
IF s_axis_tlast = '1' THEN
|
||||||
-- tlast = '1': Assign to left channel
|
-- Left channel: Store absolute value of audio sample
|
||||||
abs_audio_left <= abs_value(CHANNEL_LENGHT - 2 DOWNTO 0);
|
abs_l <= UNSIGNED(ABS(signed_sample));
|
||||||
ELSE
|
ELSE
|
||||||
-- tlast = '0': Assign to right channel
|
-- Right channel: Store absolute value of audio sample
|
||||||
abs_audio_right <= abs_value(CHANNEL_LENGHT - 2 DOWNTO 0);
|
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 control
|
-- LED refresh timing generator process
|
||||||
-- Generates periodic update signals for smooth LED display updates
|
-- 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 timing control
|
-- Reset: Initialize counter and tick signal
|
||||||
refresh_counter <= 0; -- Clear refresh counter
|
refresh_counter <= 0;
|
||||||
led_update <= '0'; -- Clear update trigger
|
refresh_tick <= '0';
|
||||||
|
|
||||||
ELSIF refresh_counter = REFRESH_CYCLES - 1 THEN
|
|
||||||
-- End of refresh period: Trigger LED update
|
|
||||||
refresh_counter <= 0; -- Reset counter for next period
|
|
||||||
led_update <= '1'; -- Set update trigger
|
|
||||||
|
|
||||||
ELSE
|
ELSE
|
||||||
-- Continue counting refresh period
|
-- Normal operation: Count clock cycles and generate refresh tick
|
||||||
refresh_counter <= refresh_counter + 1; -- Increment counter
|
IF refresh_counter = REFRESH_CYCLES - 1 THEN
|
||||||
led_update <= '0'; -- Clear update trigger
|
-- End of refresh period: Reset counter and generate tick pulse
|
||||||
|
refresh_counter <= 0;
|
||||||
|
refresh_tick <= '1'; -- Single clock cycle pulse for LED update
|
||||||
|
ELSE
|
||||||
|
-- Continue counting: Increment counter, no tick
|
||||||
|
refresh_counter <= refresh_counter + 1;
|
||||||
|
refresh_tick <= '0';
|
||||||
|
END IF;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
END PROCESS;
|
END PROCESS;
|
||||||
|
|
||||||
-- LED level calculation and bar graph generation
|
-- LED level calculation and bar graph generation process
|
||||||
-- Combines left and right channel levels and maps to LED array
|
-- Combines left and right channel amplitudes and converts to LED display pattern
|
||||||
|
-- Updates only when refresh_tick is active to maintain stable visual display
|
||||||
PROCESS (aclk)
|
PROCESS (aclk)
|
||||||
VARIABLE leds_on : NATURAL RANGE 0 TO NUM_LEDS; -- Number of LEDs to illuminate
|
VARIABLE combined_amp : UNSIGNED(CHANNEL_LENGHT - 1 DOWNTO 0); -- Combined amplitude of both channels
|
||||||
VARIABLE temp_led_level : INTEGER RANGE 0 TO NUM_LEDS; -- Calculated LED level
|
VARIABLE led_level : INTEGER RANGE 0 TO NUM_LEDS := 0; -- Calculated LED level for bar graph display
|
||||||
VARIABLE abs_audio_sum : unsigned(CHANNEL_LENGHT - 1 DOWNTO 0); -- Combined channel amplitude
|
|
||||||
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
|
-- Reset: Turn off all LEDs and reset level counter
|
||||||
leds_int <= (OTHERS => '0');
|
led <= (OTHERS => '0');
|
||||||
|
|
||||||
ELSIF led_update = '1' THEN
|
ELSIF refresh_tick = '1' THEN
|
||||||
-- Update LED display when refresh trigger is active
|
-- 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
|
||||||
abs_audio_sum := abs_audio_left + abs_audio_right;
|
-- 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.
|
||||||
|
combined_amp := RESIZE(abs_l + abs_r, combined_amp'LENGTH);
|
||||||
|
|
||||||
-- Level calculation with linear scaling to get the best visual result
|
-- Normalize combined amplitude to LED scale (0 to NUM_LEDS)
|
||||||
IF abs_audio_sum = 0 THEN
|
-- The combined amplitude is mapped to the number of LEDs using a right shift.
|
||||||
-- Mute: No LEDs illuminated
|
-- For 24-bit audio, shifting by (CHANNEL_LENGHT - 4) reduces the range to approximately 4 bits (0-15).
|
||||||
temp_led_level := 0;
|
-- Adding 1 ensures that at least one LED lights up for any non-zero audio input.
|
||||||
|
-- Example: For 24-bit input, 1 + (combined_amp >> 20) gives a range from 1 to 16.
|
||||||
ELSE
|
led_level := 1 + to_integer(shift_right(combined_amp, CHANNEL_LENGHT - 4));
|
||||||
-- Right shift by (CHANNEL_LENGHT - 4) to take in account only the most significant bits
|
|
||||||
-- to scale the audio amplitude in a linear way to fit within the LED range
|
|
||||||
temp_led_level := 1 + to_integer(shift_right(abs_audio_sum, CHANNEL_LENGHT - 4));
|
|
||||||
|
|
||||||
|
-- Saturation protection: Limit LED level to maximum available LEDs
|
||||||
|
-- Prevents overflow and ensures the LED index stays within bounds.
|
||||||
|
IF led_level > NUM_LEDS THEN
|
||||||
|
led_level := NUM_LEDS;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
-- Limit LED level to available LEDs (prevent array overflow)
|
-- Generate bar graph LED pattern
|
||||||
IF temp_led_level > NUM_LEDS THEN
|
-- Implements a "thermometer" style display: all LEDs from 0 up to (led_level-1) are ON.
|
||||||
leds_on := NUM_LEDS; -- Cap at maximum LEDs
|
-- All higher LEDs remain OFF.
|
||||||
ELSE
|
-- The assignment first turns all LEDs OFF, then sets the lower 'led_level' LEDs ON.
|
||||||
leds_on := temp_led_level; -- Use calculated level
|
led <= (OTHERS => '0');
|
||||||
|
IF led_level > 0 THEN
|
||||||
|
led(led_level - 1 DOWNTO 0) <= (OTHERS => '1');
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
-- 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 IF;
|
||||||
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
END PROCESS;
|
END PROCESS;
|
||||||
|
|
||||||
END Behavioral;
|
END Behavioral;
|
||||||
Reference in New Issue
Block a user