From 2aa4b6c4ea4918be0cd674746a141334a0bc8b26 Mon Sep 17 00:00:00 2001 From: JayR-360 <149827065+JayR-360@users.noreply.github.com> Date: Sat, 21 Feb 2026 21:11:37 -0500 Subject: [PATCH] audio files that sythesize fifo is needed for audio dm module --- rtl/audio/audio_dma.sv | 89 +++++++++++++++++++++ rtl/audio/fifo.sv | 81 +++++++++++++++++++ rtl/audio/i2s_tx.sv | 176 +++++++++++++++++++++++++++++++++++++++++ rtl/audio/pwm_audio.sv | 122 ++++++++++++++++++++++++++++ 4 files changed, 468 insertions(+) create mode 100644 rtl/audio/fifo.sv diff --git a/rtl/audio/audio_dma.sv b/rtl/audio/audio_dma.sv index e69de29..505995c 100644 --- a/rtl/audio/audio_dma.sv +++ b/rtl/audio/audio_dma.sv @@ -0,0 +1,89 @@ +module audio_dma #( + parameter int unsigned ADDR_W = 32, + parameter int unsigned DATA_W = 32, //assumming 32 bit axi for right now + parameter int unsigned SIZE_W = 16, + parameter int unsigned SAMPLE_W = 16, + parameter int unsigned BURST_LEN = 8, + parameter int unsigned FIFO_DEPTH = 64, +)( + input logic clk_i, //system clock + input logic rst_ni, //system reset + input logic enable_i, //enable input to begin operation + input logic [ADDR_W-1:0] buf_base_i, //base address of audio ring buffer in system memory + input logic [SIZE_W-1:0] buf_size_i, //total size of ring buffer + input logic [ADDR_W-1:0] rd_offset_i, //byte offset from buf_bas_i oringally named rd_ptr_i + + + //AXI4 AR channel master to fabric + //when we are taking in data? + output logic [1:0] arburst_o, //what type of burst we are sending; i beleive there are 3 burst types + output logic [ADDR_W-1:0] araddr_o, //the starting address of burst + output logic [7:0] arlen_o, //beats - 1 + output logic [2:0] arsize_o, //how many bytes are in each transfer + input logic arready_i, //this is gonna be asserted when the memory is ready to receive a new burst + output logic arvalid_o, //this is gonna be asserted when the user (this module?) is ready to issue a new burst + /*When both arready and arvalid are high (reading a value of one) + a new burst is starting, and the memory is gonna start serving this burst */ + + //AXI R channel fabric to master stuff + //aka when we are streaming out chunks of data + input logic [DATA_W-1:0] rdata_i, //contains the actually data + input logic [1:0] rresp_i, //tells use whether the burst was succesfully or not + input logic rlast_i, //tells us the last piece of data + output logic rready_o, //when the reciever is ready to receive data, this value gets set to high + input logic rvalid_i, //when a chunk of data is ready to be sent out from the memory this value we be set to high + /*when both rready and rvalid high the chunk of data has been acknowledge and it will service the next chunk of data or finish*/ + + output logic [SAMPLE_W-1:0] sample_o, + output logic sample_valid_o, + input logic sample_ready_i, + + output logic underrun_o, + output logic irq_o +); + +localparam int unsigned BYTES_PER_BEAT = DATA_W/8; //4 +localparam int unsigned ARSIZE_VAL = $clog2(BYTES_PER_BEAT); //2 +localparam int unsigned ARLEN_VAL = BURST_LEN - 1; +localparam int unsigned SAMPLES_PER_BEAT = DATA_W / SAMPLE_W; // = 2 + + +always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + + + end +end + + +always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + + + + + + end +end +always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + + + + + + end +end +always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + + + + + + end +end + + + +endmodule \ No newline at end of file diff --git a/rtl/audio/fifo.sv b/rtl/audio/fifo.sv new file mode 100644 index 0000000..bdbfdc3 --- /dev/null +++ b/rtl/audio/fifo.sv @@ -0,0 +1,81 @@ +module fifo #( + //Parameters + parameter WIDTH = 32, + parameter DEPTH = 16 +)( + //Ports + input logic clk_i, + input logic rst_ni, + + //Write + input logic [WIDTH-1:0] wdata_i, + input logic wr_en_i, + output logic full_o, + + //Read + output logic [WIDTH-1:0] rdata_o, + input logic rd_en_i, + output logic empty_o +); + + timeunit 1ns; timeprecision 100ps; + + //local parameters + localparam ADDR_W = $clog2(DEPTH); + + //local signals + logic [ADDR_W-1:0] rptr, wptr; + logic full, empty; + logic last_was_read; + + //Register Array + logic [WIDTH-1:0] mem [0:DEPTH-1]; + + //Write operation + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + wptr <= 0; + end else begin + if (wr_en_i && !full) begin + mem[wptr] <= wdata_i; + wptr <= wptr + 1'b1; + end + end + end + + //Read operation + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + rptr <= 0; + end else begin + if (rd_en_i && !empty) begin + rptr <= rptr + 1'b1; + rdata_o <= mem[rptr]; + end + end + end + //Last operation tracker + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + last_was_read <= 1; + end else begin + if (rd_en_i && !empty) begin + last_was_read <= 1; + end else if (wr_en_i && !full) begin + last_was_read <= 0; + end else begin + last_was_read <= last_was_read; + end + end + end + + //Full and empty flags + + assign full = (wptr == rptr) && !last_was_read; + assign empty = (wptr == rptr) && last_was_read; + + assign full_o = full; + assign empty_o = empty; + + +endmodule \ No newline at end of file diff --git a/rtl/audio/i2s_tx.sv b/rtl/audio/i2s_tx.sv index e69de29..d84ee6b 100644 --- a/rtl/audio/i2s_tx.sv +++ b/rtl/audio/i2s_tx.sv @@ -0,0 +1,176 @@ +module i2s_tx #( + parameter int unsigned CLK_HZ = 100_000_000, + parameter int unsigned SAMPLE_HZ = 48_000, + parameter int unsigned SAMPLE_W = 16, + parameter int unsigned CHNL_CNT = 2 +)( + input logic clk_i, + input logic rst_ni, + input logic enable_i, + + //Sample steam input (from audio_dma) + input logic [SAMPLE_W-1:0] sample_i, + input logic sample_valid_i, + output logic sample_ready_o, + + //I2S output + output logic i2s_sdata_o, + output logic i2s_bclk_o, + output logic i2s_lrclk_o, + + + //Status / interrupts + output logic underrun_o, + output logic irq_o +); + +localparam int unsigned BCLK_HZ = SAMPLE_HZ * CHNL_CNT * SAMPLE_W; +localparam int unsigned LRCLK_HZ = SAMPLE_HZ; +localparam int unsigned BCLK_TOGGLE = 2 * BCLK_HZ; +localparam int unsigned DIV = CLK_HZ / (2 * BCLK_HZ); + +//clock generation and timing +logic bclk_q; +logic lrclk_q; +logic [8:0] div_cnt_q; +logic bclk_rise; +logic bclk_fall; +logic bclk_fall_q; +logic [3:0] bit_idx_q; +logic channel_q; + +//sample bufferinf (valid/ready) +logic [SAMPLE_W-1:0] samp_buf_q; +logic have_buf_q; + +//shift engine +logic [SAMPLE_W-1:0] shreg_q; +logic sdata_q; + +//staus / interrupts +logic underrun_q; +logic irq_q; + +//state machine logic +typedef enum logic [2:0] { + IDLE, + LOAD, + SHIFT, + ERROR +} state_e; + +state_e state_q, state_d; +//state variable reset and initalization +always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + state_q <= IDLE; + end else begin + state_q <= state_d; + end +end + +always_comb begin + state_d = state_q; + case (state_q) + IDLE: begin + if (enable_i) begin + + state_d = LOAD; + end + end + LOAD: begin + if (bclk_fall) begin + state_d = SHIFT; + end + end + SHIFT: begin + if (bclk_fall && (bit_idx_q == SAMPLE_W - 1)) begin + state_d = LOAD; + end + end + ERROR: begin + state_d = IDLE; + end + default: state_d = IDLE; + endcase +end + +assign sample_ready_o = enable_i && !have_buf_q; + +always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + channel_q <= 0; + have_buf_q <= 0; + samp_buf_q <= 0; + bit_idx_q <= 0; + underrun_q <= 0; + irq_q <= 0; + lrclk_q <= 0; + shreg_q <= 0; + sdata_q <= 0; + end else begin + + if (sample_valid_i && sample_ready_o) begin + samp_buf_q <= sample_i; + have_buf_q <= 1'b1; + end + + if (bclk_fall) begin + case (state_q) + LOAD: begin + bit_idx_q <= 0; + if (have_buf_q) begin + shreg_q <= samp_buf_q; + have_buf_q <= 0; + end else begin + shreg_q <= 0; + underrun_q <= 1; + irq_q <= 1; + end + lrclk_q <= channel_q; + sdata_q <= 0; + + end + SHIFT: begin + // if (bclk_fall) begin + if (bit_idx_q == SAMPLE_W-1) begin + bit_idx_q <= 0; //reset to zero when the max value it should hold is reach ... + channel_q <= ~channel_q; //switch channels when the word is complete + end else begin + bit_idx_q <= bit_idx_q + 1; //... keep climbing till it reaches max value + end + //end + sdata_q <= shreg_q[SAMPLE_W-1]; + shreg_q <= {shreg_q[SAMPLE_W-2:0], 1'b0}; + end + endcase + end + end +end + +//clock stuff +always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + bclk_q <= 0; + bclk_fall_q <= 0; + div_cnt_q <= 0; + end else begin + if (div_cnt_q == DIV-1) begin + bclk_fall_q <= bclk_q; + bclk_q <= ~bclk_q; + div_cnt_q <= 0; + end else begin + div_cnt_q <= div_cnt_q + 1; + bclk_fall_q <= 0; + end + end +end + +assign bclk_fall = bclk_fall_q; +assign i2s_lrclk_o = lrclk_q; +assign i2s_bclk_o = bclk_q; +assign i2s_sdata_o = sdata_q; +assign underrun_o = underrun_q; +assign irq_o = irq_q; + +endmodule \ No newline at end of file diff --git a/rtl/audio/pwm_audio.sv b/rtl/audio/pwm_audio.sv index e69de29..6013a4c 100644 --- a/rtl/audio/pwm_audio.sv +++ b/rtl/audio/pwm_audio.sv @@ -0,0 +1,122 @@ +module pwm_audio #( + parameter int unsigned SAMPLE_W = 16, + parameter int unsigned PWM_CNT_W = 3, + parameter int unsigned CLK_HZ = 100_000_000, + parameter int unsigned PWM_CARRIER_HZ = 25_600, + parameter int unsigned SAMPLE_HZ = 11_025 +) ( + input logic clk_i, + input logic rst_ni, + + input logic enable_i, + + // Sample stream in (valid/ready) + input logic [SAMPLE_W-1:0] sample_i, + input logic sample_valid_i, + output logic sample_ready_o, + + // Output + status + output logic pwm_o, + output logic underrun_o, + output logic irq_o +); + + localparam int unsigned PWM_STEPS = 1 << PWM_CNT_W; // 2^PWM_CNT_W + localparam int unsigned PWM_STEP_HZ = PWM_CARRIER_HZ * PWM_STEPS; // step clock + localparam int unsigned PWM_DIV = CLK_HZ / PWM_STEP_HZ; // cycles per step + localparam int unsigned SAMPLE_DIV = CLK_HZ / SAMPLE_HZ; // cycles per sample + + logic [$clog2(PWM_DIV)-1:0] pwm_div_cnt_q; + logic [PWM_CNT_W-1:0] pwm_step_q; // 0..PWM_STEPS-1 + + logic [$clog2(SAMPLE_DIV)-1:0] sample_div_cnt_q; + logic sample_tick; // 1-cycle strobe + + // Sample buffer / duty + logic [PWM_CNT_W-1:0] duty_q; // duty in PWM steps + + // Status + logic underrun_q; // sticky + logic irq_q; // pulse or sticky (define) + + // Divider tick generation + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + sample_div_cnt_q <= 0; + sample_tick <= 0; + end + else if (enable_i) begin + if (sample_div_cnt_q == SAMPLE_DIV-1) begin + sample_div_cnt_q <= 0; + sample_tick <= 1; + end else begin + sample_div_cnt_q <= sample_div_cnt_q + 1; + sample_tick <= 0; + end + end else begin + sample_div_cnt_q <= 0; + sample_tick <= 0; + end + end + + // PWM step counter + output compare + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + pwm_step_q <= 0; + pwm_div_cnt_q <= 0; + pwm_o <= 0; + end else if (!enable_i) begin + pwm_div_cnt_q <= 0; + pwm_step_q <= 0; + pwm_o <= 0; + end else begin + if (pwm_div_cnt_q == PWM_DIV - 1) begin + pwm_div_cnt_q <= 0; + if (pwm_step_q == PWM_STEPS-1) begin + pwm_step_q <= '0; + end else begin + pwm_step_q <= pwm_step_q + 1'b1; + end + end else begin + pwm_div_cnt_q <= pwm_div_cnt_q + 1'b1; + end + pwm_o <= (pwm_step_q < duty_q); + end + end + + +// Sample stream handshake + buffering +assign sample_ready_o = enable_i && sample_tick; + +always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + underrun_q <= 1'b0; + irq_q <= 1'b0; + end else if (!enable_i) begin + // clear-on-disable + underrun_q <= 1'b0; + irq_q <= 1'b0; + end else begin + irq_q <= 1'b0; + if (sample_tick && !sample_valid_i) begin + underrun_q <= 1'b1; // sticky flag + irq_q <= 1'b1; // pulse this cycle + end + end +end + + // Sample -> duty mapping (unsigned) +always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + duty_q <= 0; + end else if (!enable_i) begin + duty_q <= 0; + end else if (sample_valid_i && sample_ready_o) begin + duty_q <= sample_i[SAMPLE_W-1 -:PWM_CNT_W]; + end +end + +assign underrun_o = underrun_q; +assign irq_o = irq_q; + +endmodule \ No newline at end of file