Compare commits
13 Commits
d65bb35afe
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| dc5ba6c745 | |||
| 2da1ff0ea7 | |||
| 902163424b | |||
| 1109f8b130 | |||
| 2968db7e3f | |||
| c4a48db729 | |||
| dff2eb439d | |||
| 79373768fa | |||
| 9828eed333 | |||
| a52023733d | |||
| c66c218f65 | |||
| 6ded9dc0a8 | |||
| 2448aecddf |
39
LAB2/Readme.md
Normal file
39
LAB2/Readme.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# LAB2 Project Presentation
|
||||||
|
|
||||||
|
## Project Description
|
||||||
|
|
||||||
|
The LAB2 project consists of the design and implementation of a digital system for image processing and data communication. The system is composed of several functional blocks, each responsible for a specific task in the processing pipeline. The main objectives are:
|
||||||
|
|
||||||
|
- Receive and process image data
|
||||||
|
- Perform color space conversion (e.g., RGB to grayscale)
|
||||||
|
- Apply convolution filters to the image
|
||||||
|
- Packetize the processed data for transmission
|
||||||
|
- Support loopback and test modes for verification
|
||||||
|
|
||||||
|
## Block Diagram
|
||||||
|
|
||||||
|
Below is a conceptual block diagram of the LAB2 system:
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
IN[PC] -.-> UART[UART]
|
||||||
|
UART -.-> IN
|
||||||
|
UART --> DEPACK[Depack]
|
||||||
|
DEPACK --> C2G[Color to Grayscale Converter]
|
||||||
|
C2G --> BRAM[BRAM Writer]
|
||||||
|
BRAM --> Underflow
|
||||||
|
BRAM -->Overflow
|
||||||
|
BRAM -->Ok
|
||||||
|
BRAM <-.-> CONV[Convolution Filter]
|
||||||
|
BRAM <-.-> CONV
|
||||||
|
CONV --> PACK[Packetizer]
|
||||||
|
PACK --> UART
|
||||||
|
```
|
||||||
|
|
||||||
|
## Areas for Improvement
|
||||||
|
|
||||||
|
- In loopback mode, when sending an empty packet (FFF1), the module temporarily stores H and F and prepends them to the header of the next packet. There is an error in the pack/depack logic.
|
||||||
|
- The implementation of the depacketizer is complex, and the precise error has not been identified.
|
||||||
|
- The color conversion (C2G) uses a divider by 3, but the approximation method is unclear (it sums half of the power-of-two factor used in the divider—why?). -> Just add comments explainig how it works
|
||||||
|
- Convolution is performed with various unconstrained integers.
|
||||||
|
- In general, the VHDL code is somewhat complex, although generally correct. Aim to simplify and make the code more readable.
|
||||||
16
LAB3/Readme.md
Normal file
16
LAB3/Readme.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# LAB3 Project Presentation
|
||||||
|
|
||||||
|
## Project Description
|
||||||
|
|
||||||
|
*To be completed: Add a description of the LAB3 project here.*
|
||||||
|
|
||||||
|
## Block Diagram
|
||||||
|
|
||||||
|
*To be completed: Add a block diagram of the LAB3 system here (e.g., using Mermaid or an image).*
|
||||||
|
|
||||||
|
## Areas for Improvement
|
||||||
|
|
||||||
|
- JSTK: Non-atomicity in writing to the RGB LED; the rest is well done and clear.
|
||||||
|
- Mute: (IF s_axis_tvalid = '1' AND m_axis_tready = '1' THEN) AXIS error; s_axis_tready should be checked. The original would be correct if s_axis_tready was directly connected to m_axis_tready. As written, it waits for ready to assert valid.
|
||||||
|
- Volume controller: Waits for ready to assert valid (IF s_axis_tvalid = '1' AND m_axis_tready = '1' THEN).
|
||||||
|
- Balance: (IF s_axis_tvalid = '1' AND m_axis_tready = '1' THEN) AXI condition is incorrect; the rest is correct.
|
||||||
333
LAB3/src/LFO.vhd
333
LAB3/src/LFO.vhd
@@ -2,16 +2,16 @@ LIBRARY IEEE;
|
|||||||
USE IEEE.STD_LOGIC_1164.ALL;
|
USE IEEE.STD_LOGIC_1164.ALL;
|
||||||
USE IEEE.NUMERIC_STD.ALL;
|
USE IEEE.NUMERIC_STD.ALL;
|
||||||
|
|
||||||
-- Entity: LFO (Low Frequency Oscillator)
|
-- Entity: LFO (Low Frequency Oscillator) - Alternative Implementation
|
||||||
-- Purpose: Applies tremolo effect to audio by modulating amplitude with a triangular wave
|
-- Purpose: Applies effect to audio by modulating amplitude with a triangular wave
|
||||||
-- Creates classic audio effects like vibrato, tremolo, and amplitude modulation
|
-- This is a simplified, single-process implementation compared to the pipelined version
|
||||||
-- Implements a 3-stage pipeline for efficient real-time audio processing
|
-- Provides real-time audio amplitude modulation for musical effects
|
||||||
ENTITY LFO IS
|
ENTITY LFO IS
|
||||||
GENERIC (
|
GENERIC (
|
||||||
CHANNEL_LENGHT : INTEGER := 24; -- Bit width of audio samples (24-bit signed)
|
CHANNEL_LENGHT : INTEGER := 24; -- Bit width of audio samples (24-bit signed)
|
||||||
JOYSTICK_LENGHT : INTEGER := 10; -- Bit width of joystick input (10-bit = 0-1023 range)
|
JOYSTICK_LENGHT : INTEGER := 10; -- Bit width of joystick input (10-bit = 0-1023 range)
|
||||||
CLK_PERIOD_NS : INTEGER := 10; -- Clock period in nanoseconds (10ns = 100MHz)
|
CLK_PERIOD_NS : INTEGER := 10; -- Clock period in nanoseconds (10ns = 100MHz)
|
||||||
TRIANGULAR_COUNTER_LENGHT : INTEGER := 10 -- Bit width of triangular wave counter (affects modulation depth)
|
TRIANGULAR_COUNTER_LENGHT : INTEGER := 10 -- Triangular wave counter length (affects modulation depth)
|
||||||
);
|
);
|
||||||
PORT (
|
PORT (
|
||||||
-- Clock and Reset
|
-- Clock and Reset
|
||||||
@@ -24,13 +24,13 @@ ENTITY LFO IS
|
|||||||
|
|
||||||
-- Slave AXI Stream interface (audio input)
|
-- Slave AXI Stream 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=left, 1=right)
|
s_axis_tlast : IN STD_LOGIC; -- Channel indicator (0=left, 1=right)
|
||||||
s_axis_tready : OUT STD_LOGIC; -- Ready to accept input data
|
s_axis_tready : OUT STD_LOGIC; -- Ready to accept input data
|
||||||
|
|
||||||
-- Master AXI Stream interface (audio output)
|
-- Master AXI Stream interface (audio output)
|
||||||
m_axis_tvalid : OUT STD_LOGIC; -- Output data valid signal
|
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_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_tlast : OUT STD_LOGIC; -- Channel indicator passthrough
|
||||||
m_axis_tready : IN STD_LOGIC -- Downstream ready signal
|
m_axis_tready : IN STD_LOGIC -- Downstream ready signal
|
||||||
);
|
);
|
||||||
@@ -38,219 +38,188 @@ END ENTITY LFO;
|
|||||||
|
|
||||||
ARCHITECTURE Behavioral OF LFO IS
|
ARCHITECTURE Behavioral OF LFO IS
|
||||||
|
|
||||||
-- Constants for LFO timing configuration
|
-- LFO timing configuration constants
|
||||||
CONSTANT BASE_PERIOD_MICROSECONDS : INTEGER := 1000; -- Base period: 1ms (1kHz base frequency)
|
CONSTANT LFO_COUNTER_BASE_PERIOD_US : INTEGER := 1000; -- Base period: 1ms (1kHz base frequency)
|
||||||
CONSTANT FREQUENCY_ADJUSTMENT_FACTOR : INTEGER := 90; -- Frequency adjustment sensitivity (clock cycles per joystick unit)
|
CONSTANT ADJUSTMENT_FACTOR : INTEGER := 90; -- Frequency adjustment sensitivity (clock cycles per joystick unit)
|
||||||
CONSTANT JOYSTICK_CENTER_VALUE : INTEGER := 2 ** (JOYSTICK_LENGHT - 1); -- Joystick center position (512 for 10-bit)
|
CONSTANT JSTK_CENTER_VALUE : INTEGER := 2 ** (JOYSTICK_LENGHT - 1); -- Joystick center position (512 for 10-bit)
|
||||||
|
|
||||||
-- Calculate base clock cycles for 1ms period at current clock frequency
|
-- Calculate base clock cycles for 1ms period at current clock frequency
|
||||||
CONSTANT BASE_CLOCK_CYCLES : INTEGER := BASE_PERIOD_MICROSECONDS * 1000 / CLK_PERIOD_NS;
|
CONSTANT LFO_COUNTER_BASE_CLK_CYCLES : INTEGER := LFO_COUNTER_BASE_PERIOD_US * 1000 / CLK_PERIOD_NS; -- 1ms = 100,000 clk cycles
|
||||||
|
|
||||||
-- Calculate frequency range limits based on joystick range
|
-- Calculate frequency range limits based on joystick range
|
||||||
-- Minimum frequency (fastest LFO): occurs when joystick is at minimum position
|
CONSTANT LFO_CLK_CYCLES_MIN : INTEGER := LFO_COUNTER_BASE_CLK_CYCLES - ADJUSTMENT_FACTOR * (2 ** (JOYSTICK_LENGHT - 1)); -- 53,920 clk cycles (faster)
|
||||||
CONSTANT MIN_CLOCK_CYCLES : INTEGER := BASE_CLOCK_CYCLES - FREQUENCY_ADJUSTMENT_FACTOR * (2 ** (JOYSTICK_LENGHT - 1));
|
CONSTANT LFO_CLK_CYCLES_MAX : INTEGER := LFO_COUNTER_BASE_CLK_CYCLES + ADJUSTMENT_FACTOR * (2 ** (JOYSTICK_LENGHT - 1) - 1); -- 145,990 clk cycles (slower)
|
||||||
-- Maximum frequency (slowest LFO): occurs when joystick is at maximum position
|
|
||||||
CONSTANT MAX_CLOCK_CYCLES : INTEGER := BASE_CLOCK_CYCLES + FREQUENCY_ADJUSTMENT_FACTOR * (2 ** (JOYSTICK_LENGHT - 1) - 1);
|
|
||||||
|
|
||||||
-- Internal signals for LFO control
|
-- LFO timing control signals
|
||||||
-- Period adjustment based on joystick input (positive = slower, negative = faster)
|
SIGNAL step_clk_cycles_delta : INTEGER RANGE - 2 ** (JOYSTICK_LENGHT - 1) * ADJUSTMENT_FACTOR TO (2 ** (JOYSTICK_LENGHT - 1) - 1) * ADJUSTMENT_FACTOR := 0;
|
||||||
SIGNAL period_adjustment_delta : INTEGER RANGE - 2 ** (JOYSTICK_LENGHT - 1) * FREQUENCY_ADJUSTMENT_FACTOR
|
SIGNAL step_clk_cycles : INTEGER RANGE LFO_CLK_CYCLES_MIN TO LFO_CLK_CYCLES_MAX := LFO_COUNTER_BASE_CLK_CYCLES;
|
||||||
TO (2 ** (JOYSTICK_LENGHT - 1) - 1) * FREQUENCY_ADJUSTMENT_FACTOR := 0;
|
SIGNAL step_counter : NATURAL RANGE 0 TO LFO_CLK_CYCLES_MAX := 0;
|
||||||
SIGNAL current_period_cycles : INTEGER RANGE MIN_CLOCK_CYCLES TO MAX_CLOCK_CYCLES := BASE_CLOCK_CYCLES;
|
|
||||||
|
|
||||||
-- Pipeline stage 1 registers - Input processing and period calculation
|
-- Triangular wave generation signals
|
||||||
SIGNAL audio_data_stage1 : STD_LOGIC_VECTOR(CHANNEL_LENGHT - 1 DOWNTO 0) := (OTHERS => '0'); -- Registered audio input
|
-- Note: Using signed counter with extra bit to handle full range calculations
|
||||||
SIGNAL enable_flag_stage1 : STD_LOGIC := '0'; -- Registered LFO enable
|
SIGNAL tri_counter : SIGNED(TRIANGULAR_COUNTER_LENGHT DOWNTO 0) := (OTHERS => '0'); -- Triangular wave amplitude
|
||||||
SIGNAL valid_flag_stage1 : STD_LOGIC := '0'; -- Valid data in stage 1
|
SIGNAL direction_up : STD_LOGIC := '1'; -- Wave direction: '1' = ascending, '0' = descending
|
||||||
SIGNAL last_flag_stage1 : STD_LOGIC := '0'; -- Registered channel indicator
|
|
||||||
|
|
||||||
-- Pipeline stage 2 registers - Triangular wave generation
|
-- AXI4-Stream control signals
|
||||||
SIGNAL triangular_wave_value : unsigned(TRIANGULAR_COUNTER_LENGHT - 1 DOWNTO 0) := (OTHERS => '0'); -- Current triangular wave amplitude
|
SIGNAL trigger : STD_LOGIC := '0'; -- Trigger to indicate new processed data is ready
|
||||||
SIGNAL wave_direction_up : STD_LOGIC := '1'; -- Triangle wave direction: '1' = ascending, '0' = descending
|
SIGNAL s_axis_tlast_reg : STD_LOGIC := '0'; -- Registered version of tlast for output synchronization
|
||||||
SIGNAL timing_counter : NATURAL RANGE 0 TO MAX_CLOCK_CYCLES := 0; -- Clock cycle counter for LFO timing
|
SIGNAL m_axis_tvalid_int : STD_LOGIC := '0'; -- Internal output valid signal
|
||||||
SIGNAL enable_flag_stage2 : STD_LOGIC := '0'; -- LFO enable flag for stage 2
|
|
||||||
SIGNAL valid_flag_stage2 : STD_LOGIC := '0'; -- Valid data in stage 2
|
|
||||||
SIGNAL last_flag_stage2 : STD_LOGIC := '0'; -- Channel indicator for stage 2
|
|
||||||
SIGNAL audio_data_stage2 : STD_LOGIC_VECTOR(CHANNEL_LENGHT - 1 DOWNTO 0) := (OTHERS => '0'); -- Audio data for stage 2
|
|
||||||
|
|
||||||
-- Pipeline stage 3 registers - Modulation and output
|
-- Audio processing signal with extended width for multiplication
|
||||||
-- Extended width to accommodate multiplication result before scaling
|
-- Width accommodates: audio sample + triangular counter to prevent overflow
|
||||||
SIGNAL multiplication_result : STD_LOGIC_VECTOR(CHANNEL_LENGHT + TRIANGULAR_COUNTER_LENGHT - 1 DOWNTO 0) := (OTHERS => '0');
|
SIGNAL m_axis_tdata_temp : SIGNED(CHANNEL_LENGHT + TRIANGULAR_COUNTER_LENGHT DOWNTO 0) := (OTHERS => '0');
|
||||||
|
|
||||||
-- Internal AXI4-Stream control signals
|
|
||||||
SIGNAL master_valid_internal : STD_LOGIC := '0'; -- Internal output valid signal
|
|
||||||
SIGNAL slave_ready_internal : STD_LOGIC := '1'; -- Internal input ready signal
|
|
||||||
|
|
||||||
BEGIN
|
BEGIN
|
||||||
|
|
||||||
-- Direct connection: tlast passes through unchanged (maintains channel timing)
|
-- Output signal assignments with proper AXI4-Stream flow control
|
||||||
m_axis_tlast <= last_flag_stage1;
|
m_axis_tvalid <= m_axis_tvalid_int;
|
||||||
|
-- Input ready logic: Ready when downstream is ready OR no valid data pending, AND not in reset
|
||||||
|
s_axis_tready <= (m_axis_tready OR NOT m_axis_tvalid_int) AND aresetn;
|
||||||
|
|
||||||
-- Pipeline stage 1: Input registration and LFO period calculation
|
-- Optimized single process for LFO timing and triangular waveform generation
|
||||||
-- This stage captures input data and calculates the LFO period based on joystick position
|
-- This process handles both the frequency control and wave shape generation
|
||||||
input_processing_stage : PROCESS (aclk)
|
triangular_wave_lfo_generator : PROCESS (aclk)
|
||||||
BEGIN
|
BEGIN
|
||||||
IF rising_edge(aclk) THEN
|
IF rising_edge(aclk) THEN
|
||||||
|
|
||||||
IF aresetn = '0' THEN
|
IF aresetn = '0' THEN
|
||||||
-- Reset all stage 1 registers to safe initial states
|
-- Reset LFO generator to initial state
|
||||||
audio_data_stage1 <= (OTHERS => '0'); -- Clear audio data
|
step_clk_cycles <= LFO_COUNTER_BASE_CLK_CYCLES; -- Set to base frequency
|
||||||
current_period_cycles <= BASE_CLOCK_CYCLES; -- Set to base frequency
|
step_counter <= 0; -- Clear timing counter
|
||||||
enable_flag_stage1 <= '0'; -- Disable LFO
|
tri_counter <= (OTHERS => '0'); -- Start triangular wave at zero
|
||||||
valid_flag_stage1 <= '0'; -- No valid data
|
direction_up <= '1'; -- Start counting up
|
||||||
last_flag_stage1 <= '0'; -- Clear channel indicator
|
|
||||||
ELSE
|
ELSE
|
||||||
-- Calculate LFO period based on joystick y-axis input
|
-- Calculate LFO period based on joystick input
|
||||||
-- Joystick mapping:
|
-- Joystick mapping:
|
||||||
-- 0-511: Faster than base frequency (shorter period)
|
-- 0-511: Slower than base frequency (longer period, lower frequency)
|
||||||
-- 512: Base frequency (1kHz)
|
-- 512: Base frequency (1kHz)
|
||||||
-- 513-1023: Slower than base frequency (longer period)
|
-- 513-1023: Faster than base frequency (shorter period, higher frequency)
|
||||||
period_adjustment_delta <= (to_integer(unsigned(lfo_period)) - JOYSTICK_CENTER_VALUE) * FREQUENCY_ADJUSTMENT_FACTOR;
|
step_clk_cycles_delta <= (to_integer(unsigned(lfo_period)) - JSTK_CENTER_VALUE);
|
||||||
current_period_cycles <= BASE_CLOCK_CYCLES - period_adjustment_delta;
|
step_clk_cycles <= LFO_COUNTER_BASE_CLK_CYCLES - step_clk_cycles_delta * ADJUSTMENT_FACTOR;
|
||||||
|
|
||||||
-- AXI4-Stream handshake: accept new data when both valid and ready
|
|
||||||
IF s_axis_tvalid = '1' AND slave_ready_internal = '1' THEN
|
|
||||||
audio_data_stage1 <= s_axis_tdata; -- Register input audio sample
|
|
||||||
enable_flag_stage1 <= lfo_enable; -- Register enable control
|
|
||||||
valid_flag_stage1 <= '1'; -- Mark data as valid for next stage
|
|
||||||
last_flag_stage1 <= s_axis_tlast; -- Register channel boundary signal
|
|
||||||
ELSE
|
|
||||||
valid_flag_stage1 <= '0'; -- No valid data to pass to next stage
|
|
||||||
END IF;
|
|
||||||
END IF;
|
|
||||||
END IF;
|
|
||||||
END PROCESS input_processing_stage;
|
|
||||||
|
|
||||||
-- Pipeline stage 2: Triangular wave generation
|
|
||||||
-- This stage generates the triangular wave that will modulate the audio amplitude
|
|
||||||
triangular_wave_generator : PROCESS (aclk)
|
|
||||||
BEGIN
|
|
||||||
IF rising_edge(aclk) THEN
|
|
||||||
IF aresetn = '0' THEN
|
|
||||||
-- Reset triangular wave generator to initial state
|
|
||||||
timing_counter <= 0; -- Clear timing counter
|
|
||||||
triangular_wave_value <= (OTHERS => '0'); -- Start at zero amplitude
|
|
||||||
wave_direction_up <= '1'; -- Start counting up
|
|
||||||
enable_flag_stage2 <= '0'; -- Disable LFO
|
|
||||||
valid_flag_stage2 <= '0'; -- No valid data
|
|
||||||
last_flag_stage2 <= '0'; -- Clear channel indicator
|
|
||||||
audio_data_stage2 <= (OTHERS => '0'); -- Clear audio data
|
|
||||||
ELSE
|
|
||||||
-- Pass through pipeline registers from stage 1 to stage 2
|
|
||||||
enable_flag_stage2 <= enable_flag_stage1; -- Forward enable flag
|
|
||||||
valid_flag_stage2 <= valid_flag_stage1; -- Forward valid flag
|
|
||||||
last_flag_stage2 <= last_flag_stage1; -- Forward channel indicator
|
|
||||||
audio_data_stage2 <= audio_data_stage1; -- Forward audio data
|
|
||||||
|
|
||||||
-- Generate triangular wave when LFO is enabled
|
-- Generate triangular wave when LFO is enabled
|
||||||
IF enable_flag_stage1 = '1' THEN
|
IF lfo_enable = '1' THEN
|
||||||
-- Clock divider: update triangular counter based on calculated period
|
|
||||||
IF timing_counter < current_period_cycles THEN
|
-- Clock divider: Update triangular wave at calculated rate
|
||||||
timing_counter <= timing_counter + 1; -- Count towards period target
|
IF step_counter >= step_clk_cycles THEN
|
||||||
ELSE
|
step_counter <= 0; -- Reset counter for next period
|
||||||
timing_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 due to process signal assignment
|
||||||
|
IF tri_counter = (2 ** TRIANGULAR_COUNTER_LENGHT) - 2 THEN
|
||||||
|
direction_up <= '0'; -- Switch to descending at near-maximum
|
||||||
|
|
||||||
|
ELSIF tri_counter = 1 THEN
|
||||||
|
direction_up <= '1'; -- Switch to ascending at near-minimum
|
||||||
|
|
||||||
-- Update triangular wave: count up or down based on current direction
|
|
||||||
-- This creates the classic triangular waveform shape
|
|
||||||
IF wave_direction_up = '1' THEN
|
|
||||||
-- Ascending phase: check if we reached maximum amplitude
|
|
||||||
IF triangular_wave_value = (2 ** TRIANGULAR_COUNTER_LENGHT) - 1 THEN
|
|
||||||
wave_direction_up <= '0'; -- Switch to descending phase
|
|
||||||
triangular_wave_value <= triangular_wave_value - 1; -- Start decreasing
|
|
||||||
ELSE
|
|
||||||
triangular_wave_value <= triangular_wave_value + 1; -- Continue increasing
|
|
||||||
END IF;
|
|
||||||
ELSE
|
|
||||||
-- Descending phase: check if we reached minimum amplitude
|
|
||||||
IF triangular_wave_value = 0 THEN
|
|
||||||
wave_direction_up <= '1'; -- Switch to ascending phase
|
|
||||||
triangular_wave_value <= triangular_wave_value + 1; -- Start increasing
|
|
||||||
ELSE
|
|
||||||
triangular_wave_value <= triangular_wave_value - 1; -- Continue decreasing
|
|
||||||
END IF;
|
|
||||||
END IF;
|
END IF;
|
||||||
END IF;
|
|
||||||
ELSE
|
|
||||||
-- LFO disabled: reset triangular wave generator to idle state
|
|
||||||
timing_counter <= 0; -- Clear timing counter
|
|
||||||
triangular_wave_value <= (OTHERS => '0'); -- Reset to zero amplitude
|
|
||||||
wave_direction_up <= '1'; -- Reset to ascending direction
|
|
||||||
END IF;
|
|
||||||
END IF;
|
|
||||||
END IF;
|
|
||||||
END PROCESS triangular_wave_generator;
|
|
||||||
|
|
||||||
-- Pipeline stage 3: Audio modulation and output control
|
-- Update triangular wave value based on current direction
|
||||||
-- This stage applies the LFO effect by multiplying audio samples with the triangular wave
|
-- This creates the classic triangular waveform shape
|
||||||
modulation_and_output : PROCESS (aclk)
|
IF direction_up = '1' THEN
|
||||||
|
tri_counter <= tri_counter + 1; -- Ascending: increment
|
||||||
|
ELSE
|
||||||
|
tri_counter <= tri_counter - 1; -- Descending: decrement
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
ELSE
|
||||||
|
step_counter <= step_counter + 1; -- Continue counting towards next update
|
||||||
|
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
END PROCESS triangular_wave_lfo_generator;
|
||||||
|
|
||||||
|
-- AXI4-Stream handshake logic and audio processing
|
||||||
|
-- This process handles input/output data flow and applies the LFO modulation
|
||||||
|
AXIS : PROCESS (aclk)
|
||||||
BEGIN
|
BEGIN
|
||||||
IF rising_edge(aclk) THEN
|
IF rising_edge(aclk) THEN
|
||||||
|
|
||||||
IF aresetn = '0' THEN
|
IF aresetn = '0' THEN
|
||||||
-- Reset output stage to safe initial state
|
-- Reset AXI4-Stream interface and audio processing
|
||||||
m_axis_tdata <= (OTHERS => '0'); -- Clear output data
|
s_axis_tlast_reg <= '0'; -- Clear registered channel indicator
|
||||||
master_valid_internal <= '0'; -- No valid output
|
m_axis_tdata_temp <= (OTHERS => '0'); -- Clear temporary audio data
|
||||||
slave_ready_internal <= '1'; -- Ready to accept input
|
m_axis_tvalid_int <= '0'; -- No valid output data
|
||||||
|
m_axis_tlast <= '0'; -- Clear output channel indicator
|
||||||
|
|
||||||
ELSE
|
ELSE
|
||||||
-- Output flow control: handle backpressure from downstream modules
|
-- Output handshake management:
|
||||||
IF master_valid_internal = '1' AND m_axis_tready = '0' THEN
|
-- Clear valid flag when downstream accepts data
|
||||||
-- Downstream not ready: maintain current output valid state
|
IF m_axis_tready = '1' THEN
|
||||||
-- This implements proper AXI4-Stream backpressure handling
|
m_axis_tvalid_int <= '0';
|
||||||
master_valid_internal <= '1';
|
END IF;
|
||||||
ELSIF valid_flag_stage2 = '1' THEN
|
|
||||||
-- New data available from stage 2: apply LFO effect or bypass
|
-- Data output logic: Send processed audio when trigger is active and output is available
|
||||||
IF enable_flag_stage2 = '1' THEN
|
IF trigger = '1' AND (m_axis_tvalid_int = '0' OR m_axis_tready = '1') THEN
|
||||||
-- Apply LFO tremolo effect: multiply audio sample by triangular wave
|
-- Scale down the multiplication result to original audio bit width
|
||||||
-- This creates amplitude modulation (tremolo effect)
|
-- Right shift by TRIANGULAR_COUNTER_LENGHT effectively divides by 2^TRIANGULAR_COUNTER_LENGHT
|
||||||
multiplication_result <= STD_LOGIC_VECTOR(
|
-- This maintains proper audio amplitude after modulation
|
||||||
resize(
|
m_axis_tdata <= STD_LOGIC_VECTOR(
|
||||||
signed(audio_data_stage2) * signed('0' & triangular_wave_value),
|
resize(
|
||||||
multiplication_result'length
|
shift_right(
|
||||||
)
|
m_axis_tdata_temp, -- Wide multiplication result
|
||||||
);
|
TRIANGULAR_COUNTER_LENGHT -- Scale factor
|
||||||
-- Scale down result by removing lower bits (equivalent to division by 2^TRIANGULAR_COUNTER_LENGHT)
|
),
|
||||||
-- This maintains proper audio amplitude range after multiplication
|
CHANNEL_LENGHT -- Final audio sample width
|
||||||
m_axis_tdata <= multiplication_result(multiplication_result'high DOWNTO TRIANGULAR_COUNTER_LENGHT);
|
)
|
||||||
|
);
|
||||||
|
m_axis_tlast <= s_axis_tlast_reg; -- Output registered channel indicator
|
||||||
|
|
||||||
|
m_axis_tvalid_int <= '1'; -- Mark output as valid
|
||||||
|
trigger <= '0'; -- Clear trigger - data has been output
|
||||||
|
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- Data input logic: Process new audio samples when available and output is ready
|
||||||
|
IF s_axis_tvalid = '1' AND (m_axis_tready = '1' OR m_axis_tvalid_int = '0') THEN
|
||||||
|
IF lfo_enable = '1' THEN
|
||||||
|
-- Apply LFO effect: multiply audio sample by triangular wave
|
||||||
|
-- This creates amplitude modulation (effect)
|
||||||
|
m_axis_tdata_temp <= signed(s_axis_tdata) * tri_counter;
|
||||||
|
s_axis_tlast_reg <= s_axis_tlast; -- Register channel indicator
|
||||||
|
|
||||||
ELSE
|
ELSE
|
||||||
-- LFO disabled: pass audio through unchanged (bypass mode)
|
-- LFO disabled: pass audio through unchanged but maintain bit width
|
||||||
-- This allows seamless switching between effect and clean audio
|
-- Left shift compensates for the right shift that occurs during output
|
||||||
m_axis_tdata <= audio_data_stage2;
|
-- This ensures unity gain when LFO is bypassed
|
||||||
|
m_axis_tdata_temp <= shift_left(
|
||||||
|
resize(
|
||||||
|
signed(s_axis_tdata), -- Convert input to signed
|
||||||
|
m_axis_tdata_temp'length -- Extend to full processing width
|
||||||
|
),
|
||||||
|
TRIANGULAR_COUNTER_LENGHT -- Compensate for output scaling
|
||||||
|
);
|
||||||
|
s_axis_tlast_reg <= s_axis_tlast; -- Register channel indicator
|
||||||
|
|
||||||
END IF;
|
END IF;
|
||||||
master_valid_internal <= '1'; -- Mark output as valid
|
|
||||||
ELSE
|
trigger <= '1'; -- Set trigger to indicate new processed data is ready
|
||||||
-- No new data available: clear output valid flag
|
|
||||||
master_valid_internal <= '0';
|
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
-- AXI4-Stream ready signal management for proper flow control
|
|
||||||
IF master_valid_internal = '1' AND m_axis_tready = '1' THEN
|
|
||||||
-- Successful output handshake: ready for new input data
|
|
||||||
slave_ready_internal <= '1';
|
|
||||||
ELSIF s_axis_tvalid = '1' AND slave_ready_internal = '1' THEN
|
|
||||||
-- Accepted new input: not ready until current output is consumed
|
|
||||||
-- This prevents data loss in the pipeline
|
|
||||||
slave_ready_internal <= '0';
|
|
||||||
END IF;
|
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
END IF;
|
END IF;
|
||||||
END PROCESS modulation_and_output;
|
|
||||||
|
|
||||||
-- Output signal assignments
|
END PROCESS AXIS;
|
||||||
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:
|
-- LFO Implementation Summary:
|
||||||
-- 1. Stage 1: Calculates LFO frequency based on joystick position
|
-- 1. Generates triangular wave at frequency controlled by joystick input
|
||||||
-- 2. Stage 2: Generates triangular wave at calculated frequency
|
-- 2. When enabled: multiplies audio samples by triangular wave (multiplier value range from 0 to 1)
|
||||||
-- 3. Stage 3: Multiplies audio samples by triangular wave (tremolo effect)
|
-- 3. When disabled: passes audio through unchanged (bypass mode)
|
||||||
|
-- 4. Uses proper AXI4-Stream handshaking for real-time audio processing
|
||||||
--
|
--
|
||||||
-- Audio Effect Characteristics:
|
-- Effect Characteristics:
|
||||||
-- - Tremolo: Periodic amplitude modulation creates "shaking" sound
|
|
||||||
-- - Frequency range: Approximately 0.1Hz to 10Hz (typical for audio LFO)
|
-- - Frequency range: Approximately 0.1Hz to 10Hz (typical for audio LFO)
|
||||||
-- - Modulation depth: Controlled by TRIANGULAR_COUNTER_LENGHT generic
|
-- - Modulation depth: Controlled by TRIANGULAR_COUNTER_LENGHT generic
|
||||||
|
-- - Waveform: Triangular (linear amplitude changes, smooth transitions)
|
||||||
-- - Bypass capability: Clean audio passthrough when disabled
|
-- - Bypass capability: Clean audio passthrough when disabled
|
||||||
--
|
|
||||||
-- Pipeline Benefits:
|
|
||||||
-- - Maintains real-time audio processing with no dropouts
|
|
||||||
-- - Allows complex calculations without affecting audio timing
|
|
||||||
-- - Provides proper AXI4-Stream flow control and backpressure handling
|
|
||||||
|
|
||||||
END ARCHITECTURE Behavioral;
|
END ARCHITECTURE Behavioral;
|
||||||
@@ -2,16 +2,16 @@ LIBRARY IEEE;
|
|||||||
USE IEEE.STD_LOGIC_1164.ALL;
|
USE IEEE.STD_LOGIC_1164.ALL;
|
||||||
USE IEEE.NUMERIC_STD.ALL;
|
USE IEEE.NUMERIC_STD.ALL;
|
||||||
|
|
||||||
-- Entity: LFO_1 (Low Frequency Oscillator) - Alternative Implementation
|
-- Entity: LFO_1 (Low Frequency Oscillator)
|
||||||
-- Purpose: Applies tremolo effect to audio by modulating amplitude with a triangular wave
|
-- Purpose: Applies tremolo effect to audio by modulating amplitude with a triangular wave
|
||||||
-- This is a simplified, single-process implementation compared to the pipelined version
|
-- Creates classic audio effects like vibrato, tremolo, and amplitude modulation
|
||||||
-- Provides real-time audio amplitude modulation for musical effects
|
-- Implements a 3-stage pipeline for efficient real-time audio processing
|
||||||
ENTITY LFO_1 IS
|
ENTITY LFO_1 IS
|
||||||
GENERIC (
|
GENERIC (
|
||||||
CHANNEL_LENGHT : INTEGER := 24; -- Bit width of audio samples (24-bit signed)
|
CHANNEL_LENGHT : INTEGER := 24; -- Bit width of audio samples (24-bit signed)
|
||||||
JOYSTICK_LENGHT : INTEGER := 10; -- Bit width of joystick input (10-bit = 0-1023 range)
|
JOYSTICK_LENGHT : INTEGER := 10; -- Bit width of joystick input (10-bit = 0-1023 range)
|
||||||
CLK_PERIOD_NS : INTEGER := 10; -- Clock period in nanoseconds (10ns = 100MHz)
|
CLK_PERIOD_NS : INTEGER := 10; -- Clock period in nanoseconds (10ns = 100MHz)
|
||||||
TRIANGULAR_COUNTER_LENGHT : INTEGER := 10 -- Triangular wave counter length (affects modulation depth)
|
TRIANGULAR_COUNTER_LENGHT : INTEGER := 10 -- Bit width of triangular wave counter (affects modulation depth)
|
||||||
);
|
);
|
||||||
PORT (
|
PORT (
|
||||||
-- Clock and Reset
|
-- Clock and Reset
|
||||||
@@ -38,188 +38,219 @@ END ENTITY LFO_1;
|
|||||||
|
|
||||||
ARCHITECTURE Behavioral OF LFO_1 IS
|
ARCHITECTURE Behavioral OF LFO_1 IS
|
||||||
|
|
||||||
-- LFO_1 timing configuration constants
|
-- Constants for LFO_1 timing configuration
|
||||||
CONSTANT LFO_1_COUNTER_BASE_PERIOD_US : INTEGER := 1000; -- Base period: 1ms (1kHz base frequency)
|
CONSTANT BASE_PERIOD_MICROSECONDS : INTEGER := 1000; -- Base period: 1ms (1kHz base frequency)
|
||||||
CONSTANT ADJUSTMENT_FACTOR : INTEGER := 90; -- Frequency adjustment sensitivity (clock cycles per joystick unit)
|
CONSTANT FREQUENCY_ADJUSTMENT_FACTOR : INTEGER := 90; -- Frequency adjustment sensitivity (clock cycles per joystick unit)
|
||||||
CONSTANT JSTK_CENTER_VALUE : INTEGER := 2 ** (JOYSTICK_LENGHT - 1); -- Joystick center position (512 for 10-bit)
|
CONSTANT JOYSTICK_CENTER_VALUE : INTEGER := 2 ** (JOYSTICK_LENGHT - 1); -- Joystick center position (512 for 10-bit)
|
||||||
|
|
||||||
-- Calculate base clock cycles for 1ms period at current clock frequency
|
-- Calculate base clock cycles for 1ms period at current clock frequency
|
||||||
CONSTANT LFO_1_COUNTER_BASE_CLK_CYCLES : INTEGER := LFO_1_COUNTER_BASE_PERIOD_US * 1000 / CLK_PERIOD_NS; -- 1ms = 100,000 clk cycles
|
CONSTANT BASE_CLOCK_CYCLES : INTEGER := BASE_PERIOD_MICROSECONDS * 1000 / CLK_PERIOD_NS;
|
||||||
|
|
||||||
-- Calculate frequency range limits based on joystick range
|
-- Calculate frequency range limits based on joystick range
|
||||||
CONSTANT LFO_1_CLK_CYCLES_MIN : INTEGER := LFO_1_COUNTER_BASE_CLK_CYCLES - ADJUSTMENT_FACTOR * (2 ** (JOYSTICK_LENGHT - 1)); -- 53,920 clk cycles (faster)
|
-- Minimum frequency (fastest LFO_1): occurs when joystick is at minimum position
|
||||||
CONSTANT LFO_1_CLK_CYCLES_MAX : INTEGER := LFO_1_COUNTER_BASE_CLK_CYCLES + ADJUSTMENT_FACTOR * (2 ** (JOYSTICK_LENGHT - 1) - 1); -- 145,990 clk cycles (slower)
|
CONSTANT MIN_CLOCK_CYCLES : INTEGER := BASE_CLOCK_CYCLES - FREQUENCY_ADJUSTMENT_FACTOR * (2 ** (JOYSTICK_LENGHT - 1));
|
||||||
|
-- Maximum frequency (slowest LFO_1): occurs when joystick is at maximum position
|
||||||
|
CONSTANT MAX_CLOCK_CYCLES : INTEGER := BASE_CLOCK_CYCLES + FREQUENCY_ADJUSTMENT_FACTOR * (2 ** (JOYSTICK_LENGHT - 1) - 1);
|
||||||
|
|
||||||
-- LFO_1 timing control signals
|
-- Internal signals for LFO_1 control
|
||||||
SIGNAL step_clk_cycles_delta : INTEGER RANGE - 2 ** (JOYSTICK_LENGHT - 1) * ADJUSTMENT_FACTOR TO (2 ** (JOYSTICK_LENGHT - 1) - 1) * ADJUSTMENT_FACTOR := 0;
|
-- Period adjustment based on joystick input (positive = slower, negative = faster)
|
||||||
SIGNAL step_clk_cycles : INTEGER RANGE LFO_1_CLK_CYCLES_MIN TO LFO_1_CLK_CYCLES_MAX := LFO_1_COUNTER_BASE_CLK_CYCLES;
|
SIGNAL period_adjustment_delta : INTEGER RANGE - 2 ** (JOYSTICK_LENGHT - 1) * FREQUENCY_ADJUSTMENT_FACTOR
|
||||||
SIGNAL step_counter : NATURAL RANGE 0 TO LFO_1_CLK_CYCLES_MAX := 0;
|
TO (2 ** (JOYSTICK_LENGHT - 1) - 1) * FREQUENCY_ADJUSTMENT_FACTOR := 0;
|
||||||
|
SIGNAL current_period_cycles : INTEGER RANGE MIN_CLOCK_CYCLES TO MAX_CLOCK_CYCLES := BASE_CLOCK_CYCLES;
|
||||||
|
|
||||||
-- Triangular wave generation signals
|
-- Pipeline stage 1 registers - Input processing and period calculation
|
||||||
-- Note: Using signed counter with extra bit to handle full range calculations
|
SIGNAL audio_data_stage1 : STD_LOGIC_VECTOR(CHANNEL_LENGHT - 1 DOWNTO 0) := (OTHERS => '0'); -- Registered audio input
|
||||||
SIGNAL tri_counter : SIGNED(TRIANGULAR_COUNTER_LENGHT DOWNTO 0) := (OTHERS => '0'); -- Triangular wave amplitude
|
SIGNAL enable_flag_stage1 : STD_LOGIC := '0'; -- Registered LFO_1 enable
|
||||||
SIGNAL direction_up : STD_LOGIC := '1'; -- Wave direction: '1' = ascending, '0' = descending
|
SIGNAL valid_flag_stage1 : STD_LOGIC := '0'; -- Valid data in stage 1
|
||||||
|
SIGNAL last_flag_stage1 : STD_LOGIC := '0'; -- Registered channel indicator
|
||||||
|
|
||||||
-- AXI4-Stream control signals
|
-- Pipeline stage 2 registers - Triangular wave generation
|
||||||
SIGNAL trigger : STD_LOGIC := '0'; -- Trigger to indicate new processed data is ready
|
SIGNAL triangular_wave_value : unsigned(TRIANGULAR_COUNTER_LENGHT - 1 DOWNTO 0) := (OTHERS => '0'); -- Current triangular wave amplitude
|
||||||
SIGNAL s_axis_tlast_reg : STD_LOGIC := '0'; -- Registered version of tlast for output synchronization
|
SIGNAL wave_direction_up : STD_LOGIC := '1'; -- Triangle wave direction: '1' = ascending, '0' = descending
|
||||||
SIGNAL m_axis_tvalid_int : STD_LOGIC := '0'; -- Internal output valid signal
|
SIGNAL timing_counter : NATURAL RANGE 0 TO MAX_CLOCK_CYCLES := 0; -- Clock cycle counter for LFO_1 timing
|
||||||
|
SIGNAL enable_flag_stage2 : STD_LOGIC := '0'; -- LFO_1 enable flag for stage 2
|
||||||
|
SIGNAL valid_flag_stage2 : STD_LOGIC := '0'; -- Valid data in stage 2
|
||||||
|
SIGNAL last_flag_stage2 : STD_LOGIC := '0'; -- Channel indicator for stage 2
|
||||||
|
SIGNAL audio_data_stage2 : STD_LOGIC_VECTOR(CHANNEL_LENGHT - 1 DOWNTO 0) := (OTHERS => '0'); -- Audio data for stage 2
|
||||||
|
|
||||||
-- Audio processing signal with extended width for multiplication
|
-- Pipeline stage 3 registers - Modulation and output
|
||||||
-- Width accommodates: audio sample + triangular counter to prevent overflow
|
-- Extended width to accommodate multiplication result before scaling
|
||||||
SIGNAL m_axis_tdata_temp : SIGNED(CHANNEL_LENGHT + TRIANGULAR_COUNTER_LENGHT DOWNTO 0) := (OTHERS => '0');
|
SIGNAL multiplication_result : STD_LOGIC_VECTOR(CHANNEL_LENGHT + TRIANGULAR_COUNTER_LENGHT - 1 DOWNTO 0) := (OTHERS => '0');
|
||||||
|
|
||||||
|
-- Internal AXI4-Stream control signals
|
||||||
|
SIGNAL master_valid_internal : STD_LOGIC := '0'; -- Internal output valid signal
|
||||||
|
SIGNAL slave_ready_internal : STD_LOGIC := '1'; -- Internal input ready signal
|
||||||
|
|
||||||
BEGIN
|
BEGIN
|
||||||
|
|
||||||
-- Output signal assignments with proper AXI4-Stream flow control
|
-- Direct connection: tlast passes through unchanged (maintains channel timing)
|
||||||
m_axis_tvalid <= m_axis_tvalid_int;
|
m_axis_tlast <= last_flag_stage1;
|
||||||
-- Input ready logic: Ready when downstream is ready OR no valid data pending, AND not in reset
|
|
||||||
s_axis_tready <= (m_axis_tready OR NOT m_axis_tvalid_int) AND aresetn;
|
|
||||||
|
|
||||||
-- Optimized single process for LFO_1 timing and triangular waveform generation
|
-- Pipeline stage 1: Input registration and LFO_1 period calculation
|
||||||
-- This process handles both the frequency control and wave shape generation
|
-- This stage captures input data and calculates the LFO_1 period based on joystick position
|
||||||
triangular_wave_lfo_generator : PROCESS (aclk)
|
input_processing_stage : PROCESS (aclk)
|
||||||
BEGIN
|
BEGIN
|
||||||
IF rising_edge(aclk) THEN
|
IF rising_edge(aclk) THEN
|
||||||
|
|
||||||
IF aresetn = '0' THEN
|
IF aresetn = '0' THEN
|
||||||
-- Reset LFO_1 generator to initial state
|
-- Reset all stage 1 registers to safe initial states
|
||||||
step_clk_cycles <= LFO_1_COUNTER_BASE_CLK_CYCLES; -- Set to base frequency
|
audio_data_stage1 <= (OTHERS => '0'); -- Clear audio data
|
||||||
step_counter <= 0; -- Clear timing counter
|
current_period_cycles <= BASE_CLOCK_CYCLES; -- Set to base frequency
|
||||||
tri_counter <= (OTHERS => '0'); -- Start triangular wave at zero
|
enable_flag_stage1 <= '0'; -- Disable LFO_1
|
||||||
direction_up <= '1'; -- Start counting up
|
valid_flag_stage1 <= '0'; -- No valid data
|
||||||
|
last_flag_stage1 <= '0'; -- Clear channel indicator
|
||||||
ELSE
|
ELSE
|
||||||
-- Calculate LFO_1 period based on joystick input
|
-- Calculate LFO_1 period based on joystick y-axis input
|
||||||
-- Joystick mapping:
|
-- Joystick mapping:
|
||||||
-- 0-511: Faster than base frequency (shorter period, higher frequency)
|
-- 0-511: Faster than base frequency (shorter period)
|
||||||
-- 512: Base frequency (1kHz)
|
-- 512: Base frequency (1kHz)
|
||||||
-- 513-1023: Slower than base frequency (longer period, lower frequency)
|
-- 513-1023: Slower than base frequency (longer period)
|
||||||
step_clk_cycles_delta <= (to_integer(unsigned(lfo_period)) - JSTK_CENTER_VALUE) * ADJUSTMENT_FACTOR;
|
period_adjustment_delta <= (to_integer(unsigned(lfo_period)) - JOYSTICK_CENTER_VALUE) * FREQUENCY_ADJUSTMENT_FACTOR;
|
||||||
step_clk_cycles <= LFO_1_COUNTER_BASE_CLK_CYCLES - step_clk_cycles_delta;
|
current_period_cycles <= BASE_CLOCK_CYCLES - period_adjustment_delta;
|
||||||
|
|
||||||
|
-- AXI4-Stream handshake: accept new data when both valid and ready
|
||||||
|
IF s_axis_tvalid = '1' AND slave_ready_internal = '1' THEN
|
||||||
|
audio_data_stage1 <= s_axis_tdata; -- Register input audio sample
|
||||||
|
enable_flag_stage1 <= lfo_enable; -- Register enable control
|
||||||
|
valid_flag_stage1 <= '1'; -- Mark data as valid for next stage
|
||||||
|
last_flag_stage1 <= s_axis_tlast; -- Register channel boundary signal
|
||||||
|
ELSE
|
||||||
|
valid_flag_stage1 <= '0'; -- No valid data to pass to next stage
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
END PROCESS input_processing_stage;
|
||||||
|
|
||||||
|
-- Pipeline stage 2: Triangular wave generation
|
||||||
|
-- This stage generates the triangular wave that will modulate the audio amplitude
|
||||||
|
triangular_wave_generator : PROCESS (aclk)
|
||||||
|
BEGIN
|
||||||
|
IF rising_edge(aclk) THEN
|
||||||
|
IF aresetn = '0' THEN
|
||||||
|
-- Reset triangular wave generator to initial state
|
||||||
|
timing_counter <= 0; -- Clear timing counter
|
||||||
|
triangular_wave_value <= (OTHERS => '0'); -- Start at zero amplitude
|
||||||
|
wave_direction_up <= '1'; -- Start counting up
|
||||||
|
enable_flag_stage2 <= '0'; -- Disable LFO_1
|
||||||
|
valid_flag_stage2 <= '0'; -- No valid data
|
||||||
|
last_flag_stage2 <= '0'; -- Clear channel indicator
|
||||||
|
audio_data_stage2 <= (OTHERS => '0'); -- Clear audio data
|
||||||
|
ELSE
|
||||||
|
-- Pass through pipeline registers from stage 1 to stage 2
|
||||||
|
enable_flag_stage2 <= enable_flag_stage1; -- Forward enable flag
|
||||||
|
valid_flag_stage2 <= valid_flag_stage1; -- Forward valid flag
|
||||||
|
last_flag_stage2 <= last_flag_stage1; -- Forward channel indicator
|
||||||
|
audio_data_stage2 <= audio_data_stage1; -- Forward audio data
|
||||||
|
|
||||||
-- Generate triangular wave when LFO_1 is enabled
|
-- Generate triangular wave when LFO_1 is enabled
|
||||||
IF lfo_enable = '1' THEN
|
IF enable_flag_stage1 = '1' THEN
|
||||||
|
-- Clock divider: update triangular counter based on calculated period
|
||||||
-- Clock divider: Update triangular wave at calculated rate
|
IF timing_counter < current_period_cycles THEN
|
||||||
IF step_counter >= step_clk_cycles THEN
|
timing_counter <= timing_counter + 1; -- Count towards period target
|
||||||
step_counter <= 0; -- Reset counter for next period
|
|
||||||
|
|
||||||
-- Check for triangular wave direction changes at extremes
|
|
||||||
-- Note: Using (2^n - 2) and 1 instead of (2^n - 1) and 0 to avoid edge cases
|
|
||||||
IF tri_counter = (2 ** TRIANGULAR_COUNTER_LENGHT) - 2 THEN
|
|
||||||
direction_up <= '0'; -- Switch to descending at near-maximum
|
|
||||||
|
|
||||||
ELSIF tri_counter = 1 THEN
|
|
||||||
direction_up <= '1'; -- Switch to ascending at near-minimum
|
|
||||||
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
-- Update triangular wave value based on current direction
|
|
||||||
-- This creates the classic triangular waveform shape
|
|
||||||
IF direction_up = '1' THEN
|
|
||||||
tri_counter <= tri_counter + 1; -- Ascending: increment
|
|
||||||
ELSE
|
|
||||||
tri_counter <= tri_counter - 1; -- Descending: decrement
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
ELSE
|
ELSE
|
||||||
step_counter <= step_counter + 1; -- Continue counting towards next update
|
timing_counter <= 0; -- Reset counter for next period
|
||||||
|
|
||||||
|
-- Update triangular wave: count up or down based on current direction
|
||||||
|
-- This creates the classic triangular waveform shape
|
||||||
|
IF wave_direction_up = '1' THEN
|
||||||
|
-- Ascending phase: check if we reached maximum amplitude
|
||||||
|
IF triangular_wave_value = (2 ** TRIANGULAR_COUNTER_LENGHT) - 1 THEN
|
||||||
|
wave_direction_up <= '0'; -- Switch to descending phase
|
||||||
|
triangular_wave_value <= triangular_wave_value - 1; -- Start decreasing
|
||||||
|
ELSE
|
||||||
|
triangular_wave_value <= triangular_wave_value + 1; -- Continue increasing
|
||||||
|
END IF;
|
||||||
|
ELSE
|
||||||
|
-- Descending phase: check if we reached minimum amplitude
|
||||||
|
IF triangular_wave_value = 0 THEN
|
||||||
|
wave_direction_up <= '1'; -- Switch to ascending phase
|
||||||
|
triangular_wave_value <= triangular_wave_value + 1; -- Start increasing
|
||||||
|
ELSE
|
||||||
|
triangular_wave_value <= triangular_wave_value - 1; -- Continue decreasing
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
END IF;
|
END IF;
|
||||||
|
ELSE
|
||||||
|
-- LFO_1 disabled: reset triangular wave generator to idle state
|
||||||
|
timing_counter <= 0; -- Clear timing counter
|
||||||
|
triangular_wave_value <= (OTHERS => '0'); -- Reset to zero amplitude
|
||||||
|
wave_direction_up <= '1'; -- Reset to ascending direction
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
END IF;
|
END IF;
|
||||||
|
END PROCESS triangular_wave_generator;
|
||||||
|
|
||||||
END PROCESS triangular_wave_lfo_generator;
|
-- Pipeline stage 3: Audio modulation and output control
|
||||||
|
-- This stage applies the LFO_1 effect by multiplying audio samples with the triangular wave
|
||||||
-- AXI4-Stream handshake logic and audio processing
|
modulation_and_output : PROCESS (aclk)
|
||||||
-- This process handles input/output data flow and applies the LFO_1 modulation
|
|
||||||
AXIS : PROCESS (aclk)
|
|
||||||
BEGIN
|
BEGIN
|
||||||
IF rising_edge(aclk) THEN
|
IF rising_edge(aclk) THEN
|
||||||
|
|
||||||
IF aresetn = '0' THEN
|
IF aresetn = '0' THEN
|
||||||
-- Reset AXI4-Stream interface and audio processing
|
-- Reset output stage to safe initial state
|
||||||
s_axis_tlast_reg <= '0'; -- Clear registered channel indicator
|
m_axis_tdata <= (OTHERS => '0'); -- Clear output data
|
||||||
m_axis_tdata_temp <= (OTHERS => '0'); -- Clear temporary audio data
|
master_valid_internal <= '0'; -- No valid output
|
||||||
m_axis_tvalid_int <= '0'; -- No valid output data
|
slave_ready_internal <= '1'; -- Ready to accept input
|
||||||
m_axis_tlast <= '0'; -- Clear output channel indicator
|
|
||||||
|
|
||||||
ELSE
|
ELSE
|
||||||
-- Output handshake management:
|
-- Output flow control: handle backpressure from downstream modules
|
||||||
-- Clear valid flag when downstream accepts data
|
IF master_valid_internal = '1' AND m_axis_tready = '0' THEN
|
||||||
IF m_axis_tready = '1' THEN
|
-- Downstream not ready: maintain current output valid state
|
||||||
m_axis_tvalid_int <= '0';
|
-- This implements proper AXI4-Stream backpressure handling
|
||||||
END IF;
|
master_valid_internal <= '1';
|
||||||
|
ELSIF valid_flag_stage2 = '1' THEN
|
||||||
-- Data output logic: Send processed audio when trigger is active and output is available
|
-- New data available from stage 2: apply LFO_1 effect or bypass
|
||||||
IF trigger = '1' AND (m_axis_tvalid_int = '0' OR m_axis_tready = '1') THEN
|
IF enable_flag_stage2 = '1' THEN
|
||||||
-- Scale down the multiplication result to original audio bit width
|
|
||||||
-- Right shift by TRIANGULAR_COUNTER_LENGHT effectively divides by 2^TRIANGULAR_COUNTER_LENGHT
|
|
||||||
-- This maintains proper audio amplitude after modulation
|
|
||||||
m_axis_tdata <= STD_LOGIC_VECTOR(
|
|
||||||
resize(
|
|
||||||
shift_right(
|
|
||||||
m_axis_tdata_temp, -- Wide multiplication result
|
|
||||||
TRIANGULAR_COUNTER_LENGHT -- Scale factor
|
|
||||||
),
|
|
||||||
CHANNEL_LENGHT -- Final audio sample width
|
|
||||||
)
|
|
||||||
);
|
|
||||||
m_axis_tlast <= s_axis_tlast_reg; -- Output registered channel indicator
|
|
||||||
|
|
||||||
m_axis_tvalid_int <= '1'; -- Mark output as valid
|
|
||||||
trigger <= '0'; -- Clear trigger - data has been output
|
|
||||||
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
-- Data input logic: Process new audio samples when available and output is ready
|
|
||||||
IF s_axis_tvalid = '1' AND (m_axis_tready = '1' OR m_axis_tvalid_int = '0') THEN
|
|
||||||
IF lfo_enable = '1' THEN
|
|
||||||
-- Apply LFO_1 tremolo effect: multiply audio sample by triangular wave
|
-- Apply LFO_1 tremolo effect: multiply audio sample by triangular wave
|
||||||
-- This creates amplitude modulation (tremolo effect)
|
-- This creates amplitude modulation (tremolo effect)
|
||||||
m_axis_tdata_temp <= signed(s_axis_tdata) * tri_counter;
|
multiplication_result <= STD_LOGIC_VECTOR(
|
||||||
s_axis_tlast_reg <= s_axis_tlast; -- Register channel indicator
|
|
||||||
|
|
||||||
ELSE
|
|
||||||
-- LFO_1 disabled: pass audio through unchanged but maintain bit width
|
|
||||||
-- Left shift compensates for the right shift that occurs during output
|
|
||||||
-- This ensures unity gain when LFO_1 is bypassed
|
|
||||||
m_axis_tdata_temp <= shift_left(
|
|
||||||
resize(
|
resize(
|
||||||
signed(s_axis_tdata), -- Convert input to signed
|
signed(audio_data_stage2) * signed('0' & triangular_wave_value),
|
||||||
m_axis_tdata_temp'length -- Extend to full processing width
|
multiplication_result'length
|
||||||
),
|
)
|
||||||
TRIANGULAR_COUNTER_LENGHT -- Compensate for output scaling
|
|
||||||
);
|
);
|
||||||
s_axis_tlast_reg <= s_axis_tlast; -- Register channel indicator
|
-- Scale down result by removing lower bits (equivalent to division by 2^TRIANGULAR_COUNTER_LENGHT)
|
||||||
|
-- This maintains proper audio amplitude range after multiplication
|
||||||
|
m_axis_tdata <= multiplication_result(multiplication_result'high DOWNTO TRIANGULAR_COUNTER_LENGHT);
|
||||||
|
ELSE
|
||||||
|
-- LFO_1 disabled: pass audio through unchanged (bypass mode)
|
||||||
|
-- This allows seamless switching between effect and clean audio
|
||||||
|
m_axis_tdata <= audio_data_stage2;
|
||||||
END IF;
|
END IF;
|
||||||
|
master_valid_internal <= '1'; -- Mark output as valid
|
||||||
trigger <= '1'; -- Set trigger to indicate new processed data is ready
|
ELSE
|
||||||
|
-- No new data available: clear output valid flag
|
||||||
|
master_valid_internal <= '0';
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
|
-- AXI4-Stream ready signal management for proper flow control
|
||||||
|
IF master_valid_internal = '1' AND m_axis_tready = '1' THEN
|
||||||
|
-- Successful output handshake: ready for new input data
|
||||||
|
slave_ready_internal <= '1';
|
||||||
|
ELSIF s_axis_tvalid = '1' AND slave_ready_internal = '1' THEN
|
||||||
|
-- Accepted new input: not ready until current output is consumed
|
||||||
|
-- This prevents data loss in the pipeline
|
||||||
|
slave_ready_internal <= '0';
|
||||||
|
END IF;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
END IF;
|
END IF;
|
||||||
|
END PROCESS modulation_and_output;
|
||||||
|
|
||||||
END PROCESS AXIS;
|
-- Output signal assignments
|
||||||
|
s_axis_tready <= slave_ready_internal; -- Connect internal ready to output port
|
||||||
|
m_axis_tvalid <= master_valid_internal; -- Connect internal valid to output port
|
||||||
|
|
||||||
-- LFO_1 Implementation Summary:
|
-- LFO_1 Effect Summary:
|
||||||
-- 1. Generates triangular wave at frequency controlled by joystick input
|
-- 1. Stage 1: Calculates LFO_1 frequency based on joystick position
|
||||||
-- 2. When enabled: multiplies audio samples by triangular wave (tremolo effect)
|
-- 2. Stage 2: Generates triangular wave at calculated frequency
|
||||||
-- 3. When disabled: passes audio through unchanged (bypass mode)
|
-- 3. Stage 3: Multiplies audio samples by triangular wave (tremolo effect)
|
||||||
-- 4. Uses proper AXI4-Stream handshaking for real-time audio processing
|
|
||||||
--
|
--
|
||||||
-- Tremolo Effect Characteristics:
|
-- Audio Effect Characteristics:
|
||||||
|
-- - Tremolo: Periodic amplitude modulation creates "shaking" sound
|
||||||
-- - Frequency range: Approximately 0.1Hz to 10Hz (typical for audio LFO_1)
|
-- - Frequency range: Approximately 0.1Hz to 10Hz (typical for audio LFO_1)
|
||||||
-- - Modulation depth: Controlled by TRIANGULAR_COUNTER_LENGHT generic
|
-- - Modulation depth: Controlled by TRIANGULAR_COUNTER_LENGHT generic
|
||||||
-- - Waveform: Triangular (linear amplitude changes, smooth transitions)
|
|
||||||
-- - Bypass capability: Clean audio passthrough when disabled
|
-- - Bypass capability: Clean audio passthrough when disabled
|
||||||
|
--
|
||||||
|
-- Pipeline Benefits:
|
||||||
|
-- - Maintains real-time audio processing with no dropouts
|
||||||
|
-- - Allows complex calculations without affecting audio timing
|
||||||
|
-- - Provides proper AXI4-Stream flow control and backpressure handling
|
||||||
|
|
||||||
END ARCHITECTURE Behavioral;
|
END ARCHITECTURE Behavioral;
|
||||||
@@ -4,7 +4,7 @@ USE ieee.numeric_std.ALL;
|
|||||||
|
|
||||||
-- Entity: all_pass_filter
|
-- Entity: all_pass_filter
|
||||||
-- Purpose: A pass-through filter that maintains the same interface and timing
|
-- Purpose: A pass-through filter that maintains the same interface and timing
|
||||||
-- characteristics as a moving average filter but passes data unchanged.
|
-- characteristics as the moving average filter but passes data unchanged.
|
||||||
ENTITY all_pass_filter IS
|
ENTITY all_pass_filter IS
|
||||||
GENERIC (
|
GENERIC (
|
||||||
TDATA_WIDTH : POSITIVE := 24 -- Width of the data bus in bits
|
TDATA_WIDTH : POSITIVE := 24 -- Width of the data bus in bits
|
||||||
@@ -16,13 +16,13 @@ ENTITY all_pass_filter IS
|
|||||||
|
|
||||||
-- AXI4-Stream Slave Interface (Input)
|
-- AXI4-Stream Slave Interface (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(TDATA_WIDTH - 1 DOWNTO 0); -- Input data
|
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_tlast : IN STD_LOGIC; -- Input end-of-packet signal
|
||||||
s_axis_tready : OUT STD_LOGIC; -- Ready to accept input data
|
s_axis_tready : OUT STD_LOGIC; -- Ready to accept input data
|
||||||
|
|
||||||
-- AXI4-Stream Master Interface (Output)
|
-- AXI4-Stream Master Interface (Output)
|
||||||
m_axis_tvalid : OUT STD_LOGIC; -- Output data valid signal
|
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_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_tlast : OUT STD_LOGIC; -- Output end-of-packet signal
|
||||||
m_axis_tready : IN STD_LOGIC -- Downstream ready signal
|
m_axis_tready : IN STD_LOGIC -- Downstream ready signal
|
||||||
);
|
);
|
||||||
@@ -36,9 +36,9 @@ ARCHITECTURE Behavioral OF all_pass_filter IS
|
|||||||
-- Internal slave interface signals
|
-- Internal slave interface signals
|
||||||
SIGNAL s_axis_tready_int : STD_LOGIC := '0'; -- Internal ready signal for input
|
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
|
SIGNAL s_axis_tlast_reg : STD_LOGIC := '0'; -- Registered version of input tlast
|
||||||
|
SIGNAL s_axis_tdata_reg : STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0) := (OTHERS => '0'); -- Temporary storage for input data
|
||||||
|
|
||||||
-- Internal data storage and master interface signals
|
-- 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
|
SIGNAL m_axis_tvalid_int : STD_LOGIC := '0'; -- Internal valid signal for output
|
||||||
|
|
||||||
BEGIN
|
BEGIN
|
||||||
@@ -66,10 +66,10 @@ BEGIN
|
|||||||
-- Asynchronous reset logic (active low)
|
-- Asynchronous reset logic (active low)
|
||||||
IF aresetn = '0' THEN
|
IF aresetn = '0' THEN
|
||||||
-- Reset all internal signals to known states
|
-- Reset all internal signals to known states
|
||||||
trigger <= '0'; -- Clear data processing trigger
|
trigger <= '0'; -- Clear data processing trigger
|
||||||
s_axis_tlast_reg <= '0'; -- Clear registered tlast
|
s_axis_tlast_reg <= '0'; -- Clear registered tlast
|
||||||
s_axis_tready_int <= '0'; -- Not ready to accept data during reset
|
s_axis_tready_int <= '0'; -- Not ready to accept data during reset
|
||||||
m_axis_tdata_temp <= (OTHERS => '0'); -- Clear temporary data storage
|
s_axis_tdata_reg <= (OTHERS => '0'); -- Clear temporary data storage
|
||||||
m_axis_tvalid_int <= '0'; -- No valid data on output during reset
|
m_axis_tvalid_int <= '0'; -- No valid data on output during reset
|
||||||
m_axis_tlast <= '0'; -- Clear output tlast
|
m_axis_tlast <= '0'; -- Clear output tlast
|
||||||
|
|
||||||
@@ -88,7 +88,7 @@ BEGIN
|
|||||||
-- (either no valid data pending OR downstream is ready)
|
-- (either no valid data pending OR downstream is ready)
|
||||||
IF trigger = '1' AND (m_axis_tvalid_int = '0' OR m_axis_tready = '1') THEN
|
IF trigger = '1' AND (m_axis_tvalid_int = '0' OR m_axis_tready = '1') THEN
|
||||||
-- Transfer stored data to output
|
-- Transfer stored data to output
|
||||||
m_axis_tdata <= m_axis_tdata_temp; -- Output the stored input data unchanged
|
m_axis_tdata <= s_axis_tdata_reg; -- Output the stored input data unchanged
|
||||||
m_axis_tlast <= s_axis_tlast_reg; -- Output the registered tlast signal
|
m_axis_tlast <= s_axis_tlast_reg; -- Output the registered tlast signal
|
||||||
|
|
||||||
-- Set output control signals
|
-- Set output control signals
|
||||||
@@ -101,7 +101,7 @@ BEGIN
|
|||||||
IF s_axis_tvalid = '1' AND s_axis_tready_int = '1' THEN
|
IF s_axis_tvalid = '1' AND s_axis_tready_int = '1' THEN
|
||||||
-- Store input signals for later output
|
-- Store input signals for later output
|
||||||
s_axis_tlast_reg <= s_axis_tlast; -- Register the tlast signal
|
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)
|
s_axis_tdata_reg <= s_axis_tdata; -- Store input data (pass-through, no processing)
|
||||||
|
|
||||||
-- Set trigger to indicate data is ready for output
|
-- Set trigger to indicate data is ready for output
|
||||||
trigger <= '1';
|
trigger <= '1';
|
||||||
|
|||||||
@@ -19,25 +19,25 @@ ENTITY balance_controller IS
|
|||||||
|
|
||||||
-- AXI4-Stream Slave Interface (Audio Input)
|
-- AXI4-Stream Slave Interface (Audio Input)
|
||||||
s_axis_tvalid : IN STD_LOGIC; -- Input data valid
|
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_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_tready : OUT STD_LOGIC; -- Ready to accept input
|
||||||
s_axis_tlast : IN STD_LOGIC; -- Channel indicator (0=left, 1=right)
|
s_axis_tlast : IN STD_LOGIC; -- Channel indicator (0=left, 1=right)
|
||||||
|
|
||||||
-- AXI4-Stream Master Interface (Audio Output)
|
-- AXI4-Stream Master Interface (Audio Output)
|
||||||
m_axis_tvalid : OUT STD_LOGIC; -- Output data valid
|
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_tdata : OUT STD_LOGIC_VECTOR(TDATA_WIDTH - 1 DOWNTO 0); -- Balanced audio sample
|
||||||
m_axis_tready : IN STD_LOGIC; -- Downstream ready
|
m_axis_tready : IN STD_LOGIC; -- Downstream ready
|
||||||
m_axis_tlast : OUT STD_LOGIC; -- Channel indicator passthrough
|
m_axis_tlast : OUT STD_LOGIC; -- Channel indicator passthrough
|
||||||
|
|
||||||
-- Balance Control Input
|
-- Balance Control Input
|
||||||
balance : IN STD_LOGIC_VECTOR(BALANCE_WIDTH - 1 DOWNTO 0) -- Balance position (0=full left, 1023=full right, 512=center)
|
balance : IN STD_LOGIC_VECTOR(BALANCE_WIDTH - 1 DOWNTO 0) -- Balance position (0=full left, 1023=full right, 512=center)
|
||||||
);
|
);
|
||||||
END balance_controller;
|
END balance_controller;
|
||||||
|
|
||||||
ARCHITECTURE Behavioral OF balance_controller IS
|
ARCHITECTURE Behavioral OF balance_controller IS
|
||||||
|
|
||||||
-- Balance calculation constants
|
-- 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 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 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)
|
CONSTANT DEAD_ZONE : INTEGER := (2 ** BALANCE_STEP_2) / 2; -- Dead zone around center (32 for step size 64)
|
||||||
|
|
||||||
|
|||||||
@@ -16,17 +16,17 @@ ENTITY digilent_jstk2 IS
|
|||||||
|
|
||||||
-- AXI4-Stream Master Interface: Data going TO the SPI IP-Core (and so, to the JSTK2 module)
|
-- 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_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_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
|
m_axis_tready : IN STD_LOGIC; -- SPI IP-Core ready to accept data
|
||||||
|
|
||||||
-- AXI4-Stream Slave Interface: Data coming FROM the SPI IP-Core (and so, from the JSTK2 module)
|
-- 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!
|
-- 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_tvalid : IN STD_LOGIC; -- Input data valid signal
|
||||||
s_axis_tdata : IN STD_LOGIC_VECTOR(7 DOWNTO 0); -- 8-bit data received via SPI
|
s_axis_tdata : IN STD_LOGIC_VECTOR(7 DOWNTO 0); -- 8-bit data received via SPI
|
||||||
|
|
||||||
-- Joystick and button values read from the JSTK2 module
|
-- 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_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)
|
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_jstk : OUT STD_LOGIC; -- Joystick button state (1=pressed)
|
||||||
btn_trigger : OUT STD_LOGIC; -- Trigger button state (1=pressed)
|
btn_trigger : OUT STD_LOGIC; -- Trigger button state (1=pressed)
|
||||||
|
|
||||||
@@ -43,10 +43,8 @@ ARCHITECTURE Behavioral OF digilent_jstk2 IS
|
|||||||
CONSTANT CMDSETLEDRGB : STD_LOGIC_VECTOR(7 DOWNTO 0) := x"84";
|
CONSTANT CMDSETLEDRGB : STD_LOGIC_VECTOR(7 DOWNTO 0) := x"84";
|
||||||
|
|
||||||
-- Calculate delay in clock cycles: (delay_period + 1_SPI_clock_period) * clock_frequency
|
-- Calculate delay in clock cycles: (delay_period + 1_SPI_clock_period) * clock_frequency
|
||||||
-- Uses integer arithmetic optimized to avoid truncation by performing multiplications before divisions
|
|
||||||
-- Formula: ((DELAY_US * SPI_SCLKFREQ + 1_000_000) * CLKFREQ) / (SPI_SCLKFREQ * 1_000_000)
|
|
||||||
-- This ensures proper timing between SPI packets as required by JSTK2 datasheet
|
-- This ensures proper timing between SPI packets as required by JSTK2 datasheet
|
||||||
CONSTANT DELAY_CLK_CYCLES : INTEGER := ((DELAY_US * SPI_SCLKFREQ + 1_000_000) * CLKFREQ) / (SPI_SCLKFREQ * 1_000_000) + 1;
|
CONSTANT DELAY_CLK_CYCLES : INTEGER := (DELAY_US + 1_000_000 / SPI_SCLKFREQ) * (CLKFREQ / 1_000_000) - 1;
|
||||||
|
|
||||||
-- State machine type definitions
|
-- State machine type definitions
|
||||||
TYPE tx_state_type IS (DELAY, SEND_CMD, SEND_RED, SEND_GREEN, SEND_BLUE, SEND_DUMMY);
|
TYPE tx_state_type IS (DELAY, SEND_CMD, SEND_RED, SEND_GREEN, SEND_BLUE, SEND_DUMMY);
|
||||||
@@ -58,7 +56,7 @@ ARCHITECTURE Behavioral OF digilent_jstk2 IS
|
|||||||
|
|
||||||
-- Timing and data storage signals
|
-- Timing and data storage signals
|
||||||
SIGNAL tx_delay_counter : INTEGER RANGE 0 TO DELAY_CLK_CYCLES := 0; -- Counter for inter-packet delay timing
|
SIGNAL tx_delay_counter : INTEGER RANGE 0 TO DELAY_CLK_CYCLES := 0; -- Counter for inter-packet delay timing
|
||||||
SIGNAL rx_cache : STD_LOGIC_VECTOR(7 DOWNTO 0); -- Temporary storage for multi-byte data reception
|
SIGNAL rx_cache : STD_LOGIC_VECTOR(s_axis_tdata'range); -- Temporary storage for multi-byte data reception
|
||||||
|
|
||||||
BEGIN
|
BEGIN
|
||||||
|
|
||||||
@@ -82,7 +80,8 @@ BEGIN
|
|||||||
"01101001" WHEN SEND_DUMMY; -- Dummy byte to complete 5-byte transaction
|
"01101001" WHEN SEND_DUMMY; -- Dummy byte to complete 5-byte transaction
|
||||||
|
|
||||||
-- TX State Machine: Sends LED color commands to JSTK2 module
|
-- TX State Machine: Sends LED color commands to JSTK2 module
|
||||||
-- Protocol: Command(1) + Red(1) + Green(1) + Blue(1) + Dummy(1) = 5 bytes total
|
-- Protocol: Command(1) + Red(1) + Green(1) + Blue(1) + Dummy(1) = 5 bytes total > Delay before next command
|
||||||
|
-- The delay is required by the JSTK datasheet to ensure proper timing between SPI transactions
|
||||||
TX : PROCESS (aclk)
|
TX : PROCESS (aclk)
|
||||||
BEGIN
|
BEGIN
|
||||||
IF rising_edge(aclk) THEN
|
IF rising_edge(aclk) THEN
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
LIBRARY IEEE;
|
LIBRARY IEEE;
|
||||||
USE IEEE.STD_LOGIC_1164.ALL;
|
USE IEEE.STD_LOGIC_1164.ALL;
|
||||||
|
USE IEEE.NUMERIC_STD.ALL;
|
||||||
|
|
||||||
-- Entity: effect_selector
|
-- Entity: effect_selector
|
||||||
-- Purpose: Routes joystick input to different audio control parameters based on effect mode
|
-- Purpose: Routes joystick input to different audio control parameters based on effect mode
|
||||||
@@ -14,7 +15,7 @@ ENTITY effect_selector IS
|
|||||||
aresetn : IN STD_LOGIC; -- Active-low asynchronous reset
|
aresetn : IN STD_LOGIC; -- Active-low asynchronous reset
|
||||||
|
|
||||||
-- Control and input signals
|
-- Control and input signals
|
||||||
effect : IN STD_LOGIC; -- Effect mode selector (0=volume/balance mode, 1=LFO/balance mode)
|
effect : IN STD_LOGIC; -- Effect mode selector (0=volume/balance mode, 1=LFO)
|
||||||
jstck_x : IN STD_LOGIC_VECTOR(JOYSTICK_LENGHT - 1 DOWNTO 0); -- X-axis joystick position
|
jstck_x : IN STD_LOGIC_VECTOR(JOYSTICK_LENGHT - 1 DOWNTO 0); -- X-axis joystick position
|
||||||
jstck_y : IN STD_LOGIC_VECTOR(JOYSTICK_LENGHT - 1 DOWNTO 0); -- Y-axis joystick position
|
jstck_y : IN STD_LOGIC_VECTOR(JOYSTICK_LENGHT - 1 DOWNTO 0); -- Y-axis joystick position
|
||||||
|
|
||||||
@@ -27,6 +28,8 @@ END effect_selector;
|
|||||||
|
|
||||||
ARCHITECTURE Behavioral OF effect_selector IS
|
ARCHITECTURE Behavioral OF effect_selector IS
|
||||||
|
|
||||||
|
constant JOYSTICK_DEFAULT : STD_LOGIC_VECTOR(JOYSTICK_LENGHT - 1 DOWNTO 0) := STD_LOGIC_VECTOR(to_unsigned(2 ** (JOYSTICK_LENGHT - 1), JOYSTICK_LENGHT)); -- Default joystick value (center position for 10-bit joystick is 512)
|
||||||
|
|
||||||
BEGIN
|
BEGIN
|
||||||
|
|
||||||
-- Main control logic process
|
-- Main control logic process
|
||||||
@@ -37,31 +40,35 @@ BEGIN
|
|||||||
IF rising_edge(aclk) THEN
|
IF rising_edge(aclk) THEN
|
||||||
|
|
||||||
IF aresetn = '0' THEN
|
IF aresetn = '0' THEN
|
||||||
-- Reset all outputs to zero (minimum values)
|
-- Reset all outputs to default/center values
|
||||||
volume <= (OTHERS => '0'); -- Minimum volume
|
volume <= JOYSTICK_DEFAULT; -- Default volume (center position)
|
||||||
balance <= (OTHERS => '0'); -- Full left balance
|
balance <= JOYSTICK_DEFAULT; -- Center balance position
|
||||||
lfo_period <= (OTHERS => '0'); -- Minimum LFO period
|
lfo_period <= JOYSTICK_DEFAULT; -- Default LFO period (center position)
|
||||||
|
|
||||||
ELSE
|
ELSE
|
||||||
-- Normal operation: Route joystick inputs based on effect mode
|
-- Normal operation: Route joystick inputs based on effect mode
|
||||||
|
|
||||||
-- X-axis always controls balance regardless of mode
|
-- Y-axis always controls different parameters based on effect mode
|
||||||
-- This provides consistent left/right audio balance control
|
-- X-axis behavior differs between modes
|
||||||
balance <= jstck_x;
|
-- Note: When switching between modes, some outputs are reset while others preserved
|
||||||
|
|
||||||
-- Y-axis control depends on selected effect mode
|
|
||||||
-- Note: When switching between modes, the previous joystick values
|
|
||||||
-- are preserved in the non-active outputs (volume/lfo_period)
|
|
||||||
IF effect = '1' THEN
|
IF effect = '1' THEN
|
||||||
-- LFO Mode: Y-axis controls Low Frequency Oscillator period
|
-- LFO Mode:
|
||||||
-- Used for tremolo, vibrato, or other modulation effects
|
-- Y-axis controls Low Frequency Oscillator period
|
||||||
|
-- X-axis is ignored in this mode
|
||||||
lfo_period <= jstck_y;
|
lfo_period <= jstck_y;
|
||||||
-- Volume remains at last set value (preserved from previous volume mode)
|
|
||||||
|
-- Volume remains at last set value (preserved from previous mode)
|
||||||
|
|
||||||
|
-- Reset balance to center/default position when in LFO mode
|
||||||
|
balance <= JOYSTICK_DEFAULT; -- Reset balance to center position
|
||||||
|
|
||||||
ELSE
|
ELSE
|
||||||
-- Volume/Balance Mode: Y-axis controls overall volume level
|
-- Volume/Balance Mode:
|
||||||
-- Traditional audio mixer control mode
|
-- Y-axis controls overall volume level
|
||||||
|
-- X-axis controls left/right audio balance
|
||||||
volume <= jstck_y;
|
volume <= jstck_y;
|
||||||
|
balance <= jstck_x;
|
||||||
|
|
||||||
-- LFO period remains at last set value (preserved from previous LFO mode)
|
-- LFO period remains at last set value (preserved from previous LFO mode)
|
||||||
|
|
||||||
END IF;
|
END IF;
|
||||||
|
|||||||
@@ -1,96 +1,83 @@
|
|||||||
LIBRARY IEEE;
|
LIBRARY IEEE;
|
||||||
USE IEEE.STD_LOGIC_1164.ALL;
|
USE IEEE.STD_LOGIC_1164.ALL;
|
||||||
USE IEEE.NUMERIC_STD.ALL;
|
USE IEEE.NUMERIC_STD.ALL;
|
||||||
|
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 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
|
CONSTANT REFRESH_CYCLES : INTEGER := (refresh_time_ms * 1_000_000) / clock_period_ns - 1;
|
||||||
CONSTANT REFRESH_CYCLES : NATURAL := (refresh_time_ms * 1_000_000) / clock_period_ns;
|
|
||||||
|
|
||||||
-- Audio processing signals
|
-- Calculate the number of bits needed to represent the number of LEDs
|
||||||
SIGNAL volume_value : signed(CHANNEL_LENGHT - 1 DOWNTO 0) := (OTHERS => '0'); -- Current audio sample (signed)
|
CONSTANT NUMLEDS_BITS : INTEGER := INTEGER(ceil(log2(real(NUM_LEDS))));
|
||||||
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
|
-- LED refresh timing control signals
|
||||||
SIGNAL leds_int : STD_LOGIC_VECTOR(NUM_LEDS - 1 DOWNTO 0) := (OTHERS => '0'); -- Internal LED state
|
SIGNAL refresh_counter : INTEGER RANGE 0 TO REFRESH_CYCLES := 0; -- Counts clock cycles between LED updates
|
||||||
SIGNAL led_update : STD_LOGIC := '0'; -- Trigger for LED refresh
|
SIGNAL refresh_tick : STD_LOGIC := '0'; -- Pulse signal generated every refresh period
|
||||||
|
|
||||||
-- Timing control
|
-- Audio amplitude storage for both stereo channels
|
||||||
SIGNAL refresh_counter : NATURAL RANGE 0 TO REFRESH_CYCLES - 1 := 0; -- Counter for refresh timing
|
-- Stores absolute values (magnitude) of left and right audio channels
|
||||||
|
SIGNAL abs_l : UNSIGNED(CHANNEL_LENGHT - 1 DOWNTO 0) := (OTHERS => '0'); -- Absolute left channel amplitude registers
|
||||||
|
SIGNAL combined_amp : UNSIGNED(CHANNEL_LENGHT - 1 DOWNTO 0) := (OTHERS => '0'); -- Combined amplitude for LED level calculation
|
||||||
|
|
||||||
BEGIN
|
BEGIN
|
||||||
|
|
||||||
-- Connect internal signals to output ports
|
-- Always ready for AXI4-Stream input
|
||||||
led <= leds_int; -- Drive external LEDs with internal state
|
s_axis_tready <= '1';
|
||||||
s_axis_tready <= '1'; -- Always ready to accept audio data (no backpressure)
|
|
||||||
|
|
||||||
-- Audio sample processing and absolute value calculation
|
-- Capture absolute value of input sample for left/right channel
|
||||||
-- Converts signed audio samples to unsigned absolute values for level detection
|
|
||||||
PROCESS (aclk)
|
PROCESS (aclk)
|
||||||
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
|
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
|
||||||
volume_value <= (OTHERS => '0'); -- Clear current sample
|
abs_l <= (OTHERS => '0');
|
||||||
abs_audio_left <= (OTHERS => '0'); -- Clear left channel level
|
combined_amp <= (OTHERS => '0');
|
||||||
abs_audio_right <= (OTHERS => '0'); -- Clear right channel level
|
|
||||||
|
|
||||||
ELSIF s_axis_tvalid = '1' THEN
|
ELSIF s_axis_tvalid = '1' THEN
|
||||||
-- Process new audio sample when valid data is available
|
-- Valid audio data received: Process the sample
|
||||||
sdata_signed := signed(s_axis_tdata); -- Convert input to signed format
|
|
||||||
volume_value <= sdata_signed; -- Store current sample
|
|
||||||
|
|
||||||
-- Absolute value calculation for amplitude detection
|
-- Channel routing based on AXI4-Stream tlast signal
|
||||||
-- Handle two's complement signed numbers correctly
|
-- tlast = '1' indicates right channel, tlast = '0' indicates left channel
|
||||||
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;
|
|
||||||
|
|
||||||
-- 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
|
-- Right channel: Combine left and right channel amplitudes
|
||||||
abs_audio_left <= abs_value(CHANNEL_LENGHT - 2 DOWNTO 0);
|
-- RESIZE ensures the sum fits within the variable's bit width.
|
||||||
|
-- There isn't data loss since both abs_l and abs(s_axis_tdata) are one bit shorter than combined_amp,
|
||||||
|
-- due to the absolute value operation.
|
||||||
|
combined_amp <= RESIZE(abs_l + UNSIGNED(ABS(SIGNED(s_axis_tdata))), combined_amp'LENGTH);
|
||||||
|
|
||||||
ELSE
|
ELSE
|
||||||
-- tlast = '0': Assign to right channel
|
-- Left channel: Store absolute value of audio sample
|
||||||
abs_audio_right <= abs_value(CHANNEL_LENGHT - 2 DOWNTO 0);
|
abs_l <= UNSIGNED(ABS(SIGNED(s_axis_tdata)));
|
||||||
|
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
END IF;
|
END IF;
|
||||||
@@ -99,27 +86,27 @@ BEGIN
|
|||||||
|
|
||||||
END PROCESS;
|
END PROCESS;
|
||||||
|
|
||||||
-- LED refresh timing control
|
-- LED refresh tick generator
|
||||||
-- Generates periodic update signals for smooth LED display updates
|
|
||||||
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
|
IF refresh_counter = REFRESH_CYCLES THEN
|
||||||
refresh_counter <= refresh_counter + 1; -- Increment counter
|
refresh_counter <= 0;
|
||||||
led_update <= '0'; -- Clear update trigger
|
refresh_tick <= '1';
|
||||||
|
|
||||||
|
ELSE
|
||||||
|
refresh_counter <= refresh_counter + 1;
|
||||||
|
refresh_tick <= '0';
|
||||||
|
|
||||||
|
END IF;
|
||||||
|
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
@@ -127,56 +114,46 @@ BEGIN
|
|||||||
|
|
||||||
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 temp_led_level : INTEGER RANGE 0 TO NUM_LEDS; -- Calculated LED level
|
VARIABLE led_level : INTEGER RANGE 0 TO 2 ** NUMLEDS_BITS := 0;
|
||||||
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
|
||||||
|
|
||||||
-- Combine left and right channel amplitudes
|
-- Linear scale to LED level conversion to get the best visual effect
|
||||||
-- Resize both channels to full width before addition to prevent overflow
|
IF combined_amp = 0 THEN
|
||||||
abs_audio_sum := resize(abs_audio_left, CHANNEL_LENGHT) + resize(abs_audio_right, CHANNEL_LENGHT);
|
led_level := 0; -- No audio signal, turn off all LEDs
|
||||||
|
|
||||||
-- 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
|
ELSE
|
||||||
-- Audio present: Calculate LED level using logarithmic-like scaling
|
led_level := 1 + to_integer(shift_right(combined_amp, CHANNEL_LENGHT - NUMLEDS_BITS));
|
||||||
-- 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;
|
END IF;
|
||||||
|
|
||||||
-- Limit LED level to available LEDs (prevent array overflow)
|
-- Saturation protection: Limit LED level to maximum available LEDs
|
||||||
IF temp_led_level > NUM_LEDS THEN
|
-- Prevents overflow and ensures the LED index stays within bounds.
|
||||||
leds_on := NUM_LEDS; -- Cap at maximum LEDs
|
IF led_level > NUM_LEDS THEN
|
||||||
ELSE
|
led_level := NUM_LEDS;
|
||||||
leds_on := temp_led_level; -- Use calculated level
|
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
-- Generate bar graph pattern: illuminate LEDs from 0 to (leds_on-1)
|
-- Update LED output based on calculated level
|
||||||
-- This creates a classic audio level meter appearance
|
led <= (OTHERS => '0');
|
||||||
leds_int <= (OTHERS => '0'); -- Start with all LEDs off
|
|
||||||
|
IF led_level > 0 THEN
|
||||||
|
led(led_level - 1 DOWNTO 0) <= (OTHERS => '1');
|
||||||
|
|
||||||
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;
|
||||||
@@ -185,18 +162,4 @@ BEGIN
|
|||||||
|
|
||||||
END PROCESS;
|
END PROCESS;
|
||||||
|
|
||||||
-- LED Level Meter Operation Summary:
|
|
||||||
-- 1. Continuously samples stereo audio data
|
|
||||||
-- 2. Calculates absolute value (amplitude) for each channel
|
|
||||||
-- 3. Combines left and right channels for total signal strength
|
|
||||||
-- 4. Updates LED display at regular intervals (refresh_time_ms)
|
|
||||||
-- 5. Maps audio amplitude to number of illuminated LEDs
|
|
||||||
-- 6. Creates bar graph visualization with automatic sensitivity scaling
|
|
||||||
--
|
|
||||||
-- Leds Behavior:
|
|
||||||
-- - No audio: All LEDs off
|
|
||||||
-- - Low audio: Few LEDs illuminated (bottom of bar)
|
|
||||||
-- - High audio: Many LEDs illuminated (full bar)
|
|
||||||
-- - Overload: All LEDs illuminated (maximum indication)
|
|
||||||
|
|
||||||
END Behavioral;
|
END Behavioral;
|
||||||
@@ -3,7 +3,7 @@ USE IEEE.STD_LOGIC_1164.ALL;
|
|||||||
USE ieee.numeric_std.ALL;
|
USE ieee.numeric_std.ALL;
|
||||||
|
|
||||||
-- Entity: moving_average_filter
|
-- Entity: moving_average_filter
|
||||||
-- Purpose: Implements a finite impulse response (FIR) low-pass filter using moving average
|
-- Purpose: Implements a moving average filter for audio data
|
||||||
-- Maintains separate circular buffers for left and right audio channels
|
-- Maintains separate circular buffers for left and right audio channels
|
||||||
-- Filter order is configurable as 2^(FILTER_ORDER_POWER) samples
|
-- Filter order is configurable as 2^(FILTER_ORDER_POWER) samples
|
||||||
ENTITY moving_average_filter IS
|
ENTITY moving_average_filter IS
|
||||||
@@ -21,13 +21,13 @@ ENTITY moving_average_filter IS
|
|||||||
|
|
||||||
-- 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(TDATA_WIDTH - 1 DOWNTO 0); -- Audio sample input
|
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_tlast : IN STD_LOGIC; -- Channel indicator (0=left, 1=right)
|
||||||
s_axis_tready : OUT STD_LOGIC; -- Ready to accept input data
|
s_axis_tready : OUT STD_LOGIC; -- Ready to accept input data
|
||||||
|
|
||||||
-- AXI4-Stream Master Interface (Audio Output)
|
-- AXI4-Stream Master Interface (Audio Output)
|
||||||
m_axis_tvalid : OUT STD_LOGIC; -- Output data valid signal
|
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_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_tlast : OUT STD_LOGIC; -- Channel indicator passthrough
|
||||||
m_axis_tready : IN STD_LOGIC -- Downstream ready signal
|
m_axis_tready : IN STD_LOGIC -- Downstream ready signal
|
||||||
);
|
);
|
||||||
@@ -42,14 +42,14 @@ ARCHITECTURE Behavioral OF moving_average_filter IS
|
|||||||
TYPE sample_array IS ARRAY (0 TO FILTER_ORDER - 1) OF signed(TDATA_WIDTH - 1 DOWNTO 0);
|
TYPE sample_array IS ARRAY (0 TO FILTER_ORDER - 1) OF signed(TDATA_WIDTH - 1 DOWNTO 0);
|
||||||
|
|
||||||
-- Right channel (RX) processing signals
|
-- Right channel (RX) processing signals
|
||||||
SIGNAL samples_rx : sample_array := (OTHERS => (OTHERS => '0')); -- Circular buffer for right channel samples
|
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 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
|
SIGNAL wr_ptr_rx : INTEGER RANGE 0 TO FILTER_ORDER - 1 := 0; -- Write pointer for right channel buffer
|
||||||
|
|
||||||
-- Left channel (LX) processing signals
|
-- Left channel (LX) processing signals
|
||||||
SIGNAL samples_lx : sample_array := (OTHERS => (OTHERS => '0')); -- Circular buffer for left channel samples
|
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 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
|
SIGNAL wr_ptr_lx : INTEGER RANGE 0 TO FILTER_ORDER - 1 := 0; -- Write pointer for left channel buffer
|
||||||
|
|
||||||
-- Control and interface signals
|
-- Control and interface signals
|
||||||
SIGNAL trigger : STD_LOGIC := '0'; -- Trigger signal to indicate when to output filtered data
|
SIGNAL trigger : STD_LOGIC := '0'; -- Trigger signal to indicate when to output filtered data
|
||||||
|
|||||||
@@ -22,13 +22,13 @@ ENTITY moving_average_filter_en IS
|
|||||||
|
|
||||||
-- 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(TDATA_WIDTH - 1 DOWNTO 0); -- Audio sample input
|
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_tlast : IN STD_LOGIC; -- Channel indicator (0=left, 1=right)
|
||||||
s_axis_tready : OUT STD_LOGIC; -- Ready to accept input data
|
s_axis_tready : OUT STD_LOGIC; -- Ready to accept input data
|
||||||
|
|
||||||
-- AXI4-Stream Master Interface (Audio Output)
|
-- AXI4-Stream Master Interface (Audio Output)
|
||||||
m_axis_tvalid : OUT STD_LOGIC; -- Output data valid signal
|
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_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_tlast : OUT STD_LOGIC; -- Channel indicator passthrough
|
||||||
m_axis_tready : IN STD_LOGIC; -- Downstream ready signal
|
m_axis_tready : IN STD_LOGIC; -- Downstream ready signal
|
||||||
|
|
||||||
|
|||||||
@@ -21,13 +21,13 @@ ENTITY volume_controller IS
|
|||||||
|
|
||||||
-- 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(TDATA_WIDTH - 1 DOWNTO 0); -- Audio sample input
|
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_tlast : IN STD_LOGIC; -- Channel indicator (0=left, 1=right)
|
||||||
s_axis_tready : OUT STD_LOGIC; -- Ready to accept input data
|
s_axis_tready : OUT STD_LOGIC; -- Ready to accept input data
|
||||||
|
|
||||||
-- AXI4-Stream Master Interface (Audio Output)
|
-- AXI4-Stream Master Interface (Audio Output)
|
||||||
m_axis_tvalid : OUT STD_LOGIC; -- Output data valid signal
|
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_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_tlast : OUT STD_LOGIC; -- Channel indicator passthrough
|
||||||
m_axis_tready : IN STD_LOGIC; -- Downstream ready signal
|
m_axis_tready : IN STD_LOGIC; -- Downstream ready signal
|
||||||
|
|
||||||
@@ -98,10 +98,10 @@ ARCHITECTURE Behavioral OF volume_controller IS
|
|||||||
|
|
||||||
-- Internal AXI4-Stream signals between multiplier and saturator
|
-- Internal AXI4-Stream signals between multiplier and saturator
|
||||||
-- These signals carry the wide multiplication results before saturation
|
-- These signals carry the wide multiplication results before saturation
|
||||||
SIGNAL int_axis_tvalid : STD_LOGIC; -- Valid signal between stages
|
SIGNAL int_axis_tvalid : STD_LOGIC; -- Valid signal between stages
|
||||||
SIGNAL int_axis_tready : STD_LOGIC; -- Ready 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_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
|
SIGNAL int_axis_tlast : STD_LOGIC; -- Channel indicator between stages
|
||||||
|
|
||||||
BEGIN
|
BEGIN
|
||||||
|
|
||||||
|
|||||||
@@ -19,15 +19,15 @@ ENTITY volume_multiplier IS
|
|||||||
|
|
||||||
-- 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(TDATA_WIDTH - 1 DOWNTO 0); -- Audio sample input
|
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_tlast : IN STD_LOGIC; -- Channel indicator (0=left, 1=right)
|
||||||
s_axis_tready : OUT STD_LOGIC; -- Ready to accept input data
|
s_axis_tready : OUT STD_LOGIC; -- Ready to accept input data
|
||||||
|
|
||||||
-- AXI4-Stream Master Interface (Audio Output with extended width)
|
-- AXI4-Stream Master Interface (Audio Output with extended width)
|
||||||
m_axis_tvalid : OUT STD_LOGIC; -- Output data valid signal
|
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_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_tlast : OUT STD_LOGIC; -- Channel indicator passthrough
|
||||||
m_axis_tready : IN STD_LOGIC; -- Downstream ready signal
|
m_axis_tready : IN STD_LOGIC; -- Downstream ready signal
|
||||||
|
|
||||||
-- Volume control input
|
-- Volume control input
|
||||||
volume : IN STD_LOGIC_VECTOR(VOLUME_WIDTH - 1 DOWNTO 0) -- Volume level (0=minimum, 1023=maximum)
|
volume : IN STD_LOGIC_VECTOR(VOLUME_WIDTH - 1 DOWNTO 0) -- Volume level (0=minimum, 1023=maximum)
|
||||||
@@ -37,7 +37,7 @@ END volume_multiplier;
|
|||||||
ARCHITECTURE Behavioral OF volume_multiplier IS
|
ARCHITECTURE Behavioral OF volume_multiplier IS
|
||||||
|
|
||||||
-- Calculate volume control parameters based on generics
|
-- 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 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 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)
|
CONSTANT DEAD_ZONE : INTEGER := (2 ** VOLUME_STEP_2) / 2; -- Dead zone around center (32 for step size 64)
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ BEGIN
|
|||||||
|
|
||||||
-- Volume to exponent conversion process
|
-- Volume to exponent conversion process
|
||||||
-- Converts joystick y-axis position to bit-shift amount for exponential volume scaling
|
-- Converts joystick y-axis position to bit-shift amount for exponential volume scaling
|
||||||
PROCESS (aclk)
|
VOLUME_CALC : PROCESS (aclk)
|
||||||
BEGIN
|
BEGIN
|
||||||
|
|
||||||
IF rising_edge(aclk) THEN
|
IF rising_edge(aclk) THEN
|
||||||
@@ -78,6 +78,7 @@ BEGIN
|
|||||||
-- volume = 0-479: Negative exponent (attenuation, right shift)
|
-- volume = 0-479: Negative exponent (attenuation, right shift)
|
||||||
-- volume = 480-543: Zero exponent (unity gain, dead zone)
|
-- volume = 480-543: Zero exponent (unity gain, dead zone)
|
||||||
-- volume = 544-1023: Positive exponent (amplification, left shift)
|
-- volume = 544-1023: Positive exponent (amplification, left shift)
|
||||||
|
|
||||||
volume_exp_mult <= to_integer(
|
volume_exp_mult <= to_integer(
|
||||||
shift_right(
|
shift_right(
|
||||||
signed('0' & volume) - to_signed(VOL_MID - DEAD_ZONE, volume'length + 1),
|
signed('0' & volume) - to_signed(VOL_MID - DEAD_ZONE, volume'length + 1),
|
||||||
@@ -89,11 +90,11 @@ BEGIN
|
|||||||
|
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
END PROCESS;
|
END PROCESS VOLUME_CALC;
|
||||||
|
|
||||||
-- AXI4-Stream data processing
|
-- AXI4-Stream data processing
|
||||||
-- Applies exponential volume scaling using bit-shifting multiplication
|
-- Applies exponential volume scaling using bit-shifting multiplication
|
||||||
PROCESS (aclk)
|
AXIS : PROCESS (aclk)
|
||||||
BEGIN
|
BEGIN
|
||||||
|
|
||||||
IF rising_edge(aclk) THEN
|
IF rising_edge(aclk) THEN
|
||||||
@@ -155,7 +156,7 @@ BEGIN
|
|||||||
|
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
END PROCESS;
|
END PROCESS AXIS;
|
||||||
|
|
||||||
-- Example scaling factors (VOLUME_STEP_2 = 6, 64 values per step):
|
-- Example scaling factors (VOLUME_STEP_2 = 6, 64 values per step):
|
||||||
-- volume_exp_mult = -3: Divide by 8 (>>3)
|
-- volume_exp_mult = -3: Divide by 8 (>>3)
|
||||||
|
|||||||
@@ -20,14 +20,14 @@ ENTITY volume_saturator IS
|
|||||||
aresetn : IN STD_LOGIC; -- Active-low asynchronous reset
|
aresetn : IN STD_LOGIC; -- Active-low asynchronous reset
|
||||||
|
|
||||||
-- AXI4-Stream Slave Interface (Extended width input from multiplier)
|
-- AXI4-Stream Slave Interface (Extended width input from multiplier)
|
||||||
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(TDATA_WIDTH - 1 + 2 ** (VOLUME_WIDTH - VOLUME_STEP_2 - 1) DOWNTO 0); -- Wide input data from multiplier
|
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_tlast : IN STD_LOGIC; -- Channel indicator (0=left, 1=right)
|
||||||
s_axis_tready : OUT STD_LOGIC; -- Ready to accept input data
|
s_axis_tready : OUT STD_LOGIC; -- Ready to accept input data
|
||||||
|
|
||||||
-- AXI4-Stream Master Interface (Original width output for audio)
|
-- AXI4-Stream Master Interface (Original width output for audio)
|
||||||
m_axis_tvalid : OUT STD_LOGIC; -- Output data valid signal
|
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_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_tlast : OUT STD_LOGIC; -- Channel indicator passthrough
|
||||||
m_axis_tready : IN STD_LOGIC -- Downstream ready signal
|
m_axis_tready : IN STD_LOGIC -- Downstream ready signal
|
||||||
);
|
);
|
||||||
@@ -82,12 +82,10 @@ BEGIN
|
|||||||
|
|
||||||
IF signed(s_axis_tdata) > signed(HIGHER_BOUND_VEC) THEN
|
IF signed(s_axis_tdata) > signed(HIGHER_BOUND_VEC) THEN
|
||||||
-- Positive overflow: Clip to maximum positive value
|
-- Positive overflow: Clip to maximum positive value
|
||||||
-- This prevents "wraparound" distortion from mathematical overflow
|
|
||||||
m_axis_tdata <= HIGHER_BOUND_VEC;
|
m_axis_tdata <= HIGHER_BOUND_VEC;
|
||||||
|
|
||||||
ELSIF signed(s_axis_tdata) < signed(LOWER_BOUND_VEC) THEN
|
ELSIF signed(s_axis_tdata) < signed(LOWER_BOUND_VEC) THEN
|
||||||
-- Negative overflow: Clip to maximum negative value
|
-- Negative overflow: Clip to maximum negative value
|
||||||
-- This prevents "wraparound" distortion from mathematical underflow
|
|
||||||
m_axis_tdata <= LOWER_BOUND_VEC;
|
m_axis_tdata <= LOWER_BOUND_VEC;
|
||||||
|
|
||||||
ELSE
|
ELSE
|
||||||
@@ -109,11 +107,4 @@ BEGIN
|
|||||||
|
|
||||||
END PROCESS;
|
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
|
|
||||||
|
|
||||||
END Behavioral;
|
END Behavioral;
|
||||||
@@ -137,12 +137,6 @@
|
|||||||
<Attr Name="UsedIn" Val="simulation"/>
|
<Attr Name="UsedIn" Val="simulation"/>
|
||||||
</FileInfo>
|
</FileInfo>
|
||||||
</File>
|
</File>
|
||||||
<File Path="$PPRDIR/../../src/digilent_jstk2.vhd">
|
|
||||||
<FileInfo>
|
|
||||||
<Attr Name="UsedIn" Val="synthesis"/>
|
|
||||||
<Attr Name="UsedIn" Val="simulation"/>
|
|
||||||
</FileInfo>
|
|
||||||
</File>
|
|
||||||
<File Path="$PPRDIR/../../src/all_pass_filter.vhd">
|
<File Path="$PPRDIR/../../src/all_pass_filter.vhd">
|
||||||
<FileInfo>
|
<FileInfo>
|
||||||
<Attr Name="UsedIn" Val="synthesis"/>
|
<Attr Name="UsedIn" Val="synthesis"/>
|
||||||
@@ -167,6 +161,12 @@
|
|||||||
<Attr Name="UsedIn" Val="simulation"/>
|
<Attr Name="UsedIn" Val="simulation"/>
|
||||||
</FileInfo>
|
</FileInfo>
|
||||||
</File>
|
</File>
|
||||||
|
<File Path="$PPRDIR/../../src/digilent_jstk2.vhd">
|
||||||
|
<FileInfo>
|
||||||
|
<Attr Name="UsedIn" Val="synthesis"/>
|
||||||
|
<Attr Name="UsedIn" Val="simulation"/>
|
||||||
|
</FileInfo>
|
||||||
|
</File>
|
||||||
<File Path="$PPRDIR/../../design/lab_3/lab_3.bd">
|
<File Path="$PPRDIR/../../design/lab_3/lab_3.bd">
|
||||||
<FileInfo>
|
<FileInfo>
|
||||||
<Attr Name="UsedIn" Val="synthesis"/>
|
<Attr Name="UsedIn" Val="synthesis"/>
|
||||||
@@ -180,13 +180,6 @@
|
|||||||
<Attr Name="UsedIn" Val="simulation"/>
|
<Attr Name="UsedIn" Val="simulation"/>
|
||||||
</FileInfo>
|
</FileInfo>
|
||||||
</File>
|
</File>
|
||||||
<File Path="$PPRDIR/../../src/jstk_uart_bridge.vhd">
|
|
||||||
<FileInfo>
|
|
||||||
<Attr Name="AutoDisabled" Val="1"/>
|
|
||||||
<Attr Name="UsedIn" Val="synthesis"/>
|
|
||||||
<Attr Name="UsedIn" Val="simulation"/>
|
|
||||||
</FileInfo>
|
|
||||||
</File>
|
|
||||||
<Config>
|
<Config>
|
||||||
<Option Name="DesignMode" Val="RTL"/>
|
<Option Name="DesignMode" Val="RTL"/>
|
||||||
<Option Name="TopModule" Val="lab_3_wrapper"/>
|
<Option Name="TopModule" Val="lab_3_wrapper"/>
|
||||||
|
|||||||
Reference in New Issue
Block a user