分享一个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