I2S从机接收Verilog模块

Jun. 17, 2023

分享一个I2S从机接收的Verilog模块,用在了ZYNQ里,音频AD用的是WM87825,主模式24bit,I2S格式,工作得还比较稳定。

`timescale 1ns / 1ps
`define LEFT_CHANNEL 1'b0
`define RIGHT_CHANNEL 1'b1

`define FIRST_SIDE  `LEFT_CHANNEL
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2023/03/09 16:44:04
// Design Name: 
// Module Name: i2s
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////

module i2s(
    input sys_clk,
    input rst_n,
    input i2s_mclk,
    input i2s_sdout,
    input i2s_lrck,
    input i2s_brck,
    output reg ch_l_valid,
    output reg ch_r_valid,
    output data_valid,
    output reg [23:0] ch_l_data,
    output reg [23:0] ch_r_data
);


    reg[4:0] timer_cnt_buf = 5'b0;


    reg [23:0] ch_l_data_buf;
    reg [23:0] ch_r_data_buf;
    reg [1:0] i2s_brck_buf;
    reg [1:0] i2s_lrck_buf;
   
    reg ch_lrck = 0;

    reg i2s_brck_d;
    always@(i2s_brck) begin
        i2s_brck_d <= i2s_brck;
    end

    always@(posedge sys_clk or negedge rst_n)
    begin
        if(!rst_n)
            begin
                i2s_brck_buf <= 2'b00;
                i2s_lrck_buf <= 2'b00;
            end
        else begin
            if(i2s_brck_d == 1'b1) begin
                i2s_brck_buf <= {i2s_brck_buf[0], 1'b1};
            end
            else begin
                i2s_brck_buf <= {i2s_brck_buf[0], 1'b0};
            end
            //i2s_brck_buf <= {i2s_brck_buf[0], i2s_brck}; 


            if(i2s_lrck == `LEFT_CHANNEL) begin
                ch_lrck <= `LEFT_CHANNEL;
                i2s_lrck_buf <= {i2s_lrck_buf[0], `LEFT_CHANNEL};
            end
            else begin
                i2s_lrck_buf <= {i2s_lrck_buf[0], `RIGHT_CHANNEL};
                ch_lrck <= `RIGHT_CHANNEL;
            end
        end
    end

    wire ch_brck, ch_brck_shift, lrch_reset, sample_reset;
    assign ch_brck = (i2s_brck_buf == 2'b01);
    assign ch_brck_shift = !ch_brck;
    assign lrch_reset = (i2s_lrck_buf == 2'b01) || (i2s_lrck_buf == 2'b10);
    assign sample_reset = ((i2s_lrck_buf == 2'b10));

    always@(posedge lrch_reset or posedge ch_brck_shift or negedge rst_n    )
    begin
        if(!rst_n) begin
             timer_cnt_buf <= 5'h1f;
        end
        else if(lrch_reset == 1'b1) begin
            timer_cnt_buf <= 5'b0;
        end
        else begin
            if(ch_brck_shift == 1'b1) begin
                timer_cnt_buf <= timer_cnt_buf + 1'b1;
            end
            else begin
                timer_cnt_buf <= timer_cnt_buf;
            end
        end
    end

    always@(posedge ch_brck or negedge rst_n or posedge lrch_reset or posedge sample_reset)
    begin
        if(!rst_n)
            begin
                ch_l_data_buf <= 24'b0;
                ch_r_data_buf <= 24'b0;

                ch_r_valid <= 1'b0;
                ch_l_valid <= 1'b0;

            end
        else if(lrch_reset == 1'b1)
         begin
            ch_l_data_buf <= ch_l_data_buf;
            ch_r_data_buf <= ch_r_data_buf;

            
            if(sample_reset == 1'b1)
          begin
              ch_r_valid <= 1'b0;
              ch_l_valid <= 1'b0;
          end
          else begin
            ch_r_valid <= ch_r_valid;
            ch_l_valid <= ch_l_valid;
          end
        end      
        else
            begin
                if(timer_cnt_buf <= 5'd24 && timer_cnt_buf >= 5'd1)
                begin
                    if(ch_lrck == `LEFT_CHANNEL)
                        begin
                            ch_l_data_buf <= {ch_l_data_buf[22:0],  i2s_sdout};
                            ch_r_data_buf <= ch_r_data_buf;

                            ch_r_valid <= ch_r_valid;
                            ch_l_valid <= 1'b0;
                        end
                    else
                        begin
                            ch_r_data_buf <= {ch_r_data_buf[22:0],  i2s_sdout};
                            ch_l_data_buf <= ch_l_data_buf;

                            ch_r_valid <= 1'b0;
                            ch_l_valid <= ch_l_valid;
                        end
                end
                else if(timer_cnt_buf > 5'd24)
                    begin
                        if(ch_lrck == `LEFT_CHANNEL)
                        begin
                            ch_l_valid <= 1'b1;
                            ch_r_valid <= 1'b0;
                        end
                        else begin
                            ch_l_valid <= ch_l_valid;
                            ch_r_valid <= 1'b1;
                        end
                        
                        ch_l_data_buf <= ch_l_data_buf;
                        ch_r_data_buf <= ch_r_data_buf;
                    end
                else
                    begin
                        ch_l_data_buf <= ch_l_data_buf;
                        ch_r_data_buf <= ch_r_data_buf;

                        ch_r_valid <= ch_r_valid;
                        ch_l_valid <= ch_l_valid;
                    end
            end
    end

    wire sample_valid;
    assign sample_valid = (ch_r_valid & ch_l_valid);
    always@(posedge sample_valid or negedge rst_n)
    begin
        if(!rst_n) begin
            ch_l_data <= 24'b0;
            ch_r_data <= 24'b0;
        end
        else if(sample_valid == 1'b1)
          begin
                ch_l_data <= ch_l_data_buf;
                ch_r_data <= ch_r_data_buf;
            end
        else
            begin
                ch_l_data <= ch_l_data;
                ch_r_data <= ch_r_data;
            end
    end


    assign data_valid = sample_valid;


endmodule