由于项目需要,需要由FPGA把4路由ADC芯片采集的I2S数据进行合并成1路I2S,最后输出到主控CPU芯片,FPGA在这里起到数据中转的作用。项目的整体结构如下图
(待画)
参考文档:I2S bus specification.pdf 见下面博客
参考博客:I2S接口规范时序以及其同DSP的连接
I2S音频总线学习中的一、二、四
采样频率、时间、声道、量化与储存大学的关系
module I2S_64K
(
//main controller
input i_rst,
input i_bit_clk,
input i_fs_clk,
output o_data,
//adc
output reg o_bit_clk,
output reg o_fs_clk,
output o_main_clk, //---------------------------------->now, it have not be usedd
input i_data0,
input i_data1,
input i_data2,
input i_data3,
//input i_data4,
//input i_data5
//i2c_config_done
//input i2c_config_done
);
parameter DIVIDE=4; //共4路数据,所以4分频
//**********************************************************************************************************************
//************************ o_bit_clk = i_bit_clk / DIVIDE ; o_fs_clk = i_fs_clk / DIVIDE *************************
//**********************************************************************************************************************
//1.o_bit_clk = i_bit_clk / DIVIDE
reg [1:0] cnt_bit_divide;
always@(negedge i_bit_clk or negedge i_rst) //下降沿时钟触发是为了下降沿和o_fs_clk对齐
if(!i_rst)
begin
cnt_bit_divide <= 2'd0;
o_bit_clk <= 1'b0;
end
else if(cnt_bit_divide == (DIVIDE/2-1))
begin
cnt_bit_divide <= 2'd0;
o_bit_clk <= ~o_bit_clk;
end
else
begin
cnt_bit_divide <= cnt_bit_divide + 1'b1;
o_bit_clk <= o_bit_clk;
end
//**********************************************
//2.o_fs_clk = i_fs_clk / DIVIDE
reg [1:0] cnt_fs_divide;
always@(negedge i_fs_clk or negedge i_rst)
if(!i_rst)
begin
cnt_fs_divide <= 2'd0;
o_fs_clk <= 1'b1;
end
else if(cnt_fs_divide == (DIVIDE/2-1))
begin
cnt_fs_divide <= 2'd0;
o_fs_clk <= ~o_fs_clk;
end
else
begin
cnt_fs_divide <= cnt_fs_divide + 1'b1;
o_fs_clk <= o_fs_clk;
end
wire clkout_o,lock_o;
GW_PLL PLL_3(
.clkout(clkout_o), //output clkout
.lock(lock_o), //output lock
.clkin(i_bit_clk) //input clkin
);
assign o_main_clk = (lock_o == 1'b1) ? clkout_o : 1'b0;
// dectect o_fs_clk edge change
reg o_fs_clk1,o_fs_clk2;
always@(posedge o_bit_clk or negedge i_rst)
if(!i_rst)
begin
o_fs_clk1 <= 1'b0;
o_fs_clk2 <= 1'b0;
end
else
begin
o_fs_clk1 <= o_fs_clk;
o_fs_clk2 <= o_fs_clk1;
end
wire wsp = o_fs_clk1 ^ o_fs_clk2; // edge detect
//*******************************************
// SRAM store count register
reg [4:0] cnt_rec; //SRAM store count register
always@(negedge o_bit_clk or negedge i_rst) //下降沿时钟,wsp上升沿开始计时(see the I2S bus specification.pdf,the last two figure)
if(!i_rst)
begin
cnt_rec <= 5'd0;
end
else if(wsp) //
begin
cnt_rec <= 5'd1;
end
else
begin
cnt_rec <= cnt_rec + 1'b1;
end
//*******************************************
// SRAM write enable signal
wire cea1 = o_fs_clk;
wire cea2 = ~ o_fs_clk;
//*******************************************
//ADC data receive ,then store the data in SRAM ,
wire [3:0] data_adc;
//assign data_adc = {i_data0,i_data1,i_data2,i_data3};
assign data_adc = ((cnt_rec >= 5'd25) | (cnt_rec <= 5'd20)) ? {i_data0,i_data1,i_data2,i_data3} :
((cea1 == 1'b1) & (cnt_rec == 5'd21)) ? (4'b0000) :
((cea1 == 1'b1) & (cnt_rec == 5'd22)) ? (4'b0001) :
((cea1 == 1'b1) & (cnt_rec == 5'd23)) ? (4'b0110) :
((cea1 == 1'b1) & (cnt_rec == 5'd24)) ? (4'b1010) :
((cea1 == 1'b0) & (cnt_rec == 5'd21)) ? (4'b0001) :
((cea1 == 1'b0) & (cnt_rec == 5'd22)) ? (4'b1110) :
((cea1 == 1'b0) & (cnt_rec == 5'd23)) ? (4'b0110) :
((cea1 == 1'b0) & (cnt_rec == 5'd24)) ? (4'b1010) : (4'b0000);
reg i_fs_clk1,i_fs_clk2;
always@(posedge i_bit_clk or negedge i_rst)
if(!i_rst)
begin
i_fs_clk1 <= 1'b0;
i_fs_clk2 <= 1'b0;
end
else
begin
i_fs_clk1 <= i_fs_clk;
i_fs_clk2 <= i_fs_clk1;
end
wire wsp3 = i_fs_clk1 ^ i_fs_clk2; //边沿检测
reg [4:0] cnt_read; //计数器只是用来计数,当o_bit_clk变化时它就清零,然后依次加一
always@(negedge i_bit_clk or negedge i_rst) //the same resson as 99 line
if(!i_rst)
begin
cnt_read <= 5'd0;
end
else if(wsp3) //i_fs_clk edge detect
begin
cnt_read <= 5'd3; // attention !!! here is important, to keep same with the left-justified audio data
end
else
begin
cnt_read <= cnt_read + 1'b1;
end
reg [3:0] cnt_read_cnt; //这个累加器是为了把o_fs_clk分成四份,用来区别通道数的
always@(negedge i_bit_clk or negedge i_rst) //the same resson as 99 line
if(!i_rst)
begin
cnt_read_cnt <= 4'b0000;
end
else if((cnt_read == 5'd0) & (cnt_rec != 5'd31)) //
begin
cnt_read_cnt <= cnt_read_cnt << 1;
end
else if((cnt_read == 5'd0) & (cnt_rec == 5'd31)) //
begin
cnt_read_cnt <= 4'b0001; //attention !!! here is delay for a i_bit_clk period !
end
else
begin
cnt_read_cnt <= cnt_read_cnt;
end
reg ceb11,ceb22;
always@(negedge i_bit_clk or negedge i_rst) //the same resson as 99 line
if(!i_rst)
begin
ceb11 <= 1'b0;
ceb22 <= 1'b0;
end
else if((cnt_read == 5'd30) & (cnt_rec == 5'd31) & (o_fs_clk == 1'b1) ) // here is good ,and there are no delay ,
begin
ceb11 <= 1'b1;
ceb22 <= 1'b0;
end
else if((cnt_read == 5'd30) & (cnt_rec == 5'd31) & (o_fs_clk == 1'b0) ) //
begin
ceb11 <= 1'b0;
ceb22 <= 1'b1;
end
else
begin
ceb11 <= ceb11;
ceb22 <= ceb22;
end
GW_SDP sram_left(
.dout(data_sram1), //output [3:0] dout read
.clka(o_bit_clk), //input clka write
.cea(cea1), //input cea write
.reseta(!i_rst), //input reseta write
.clkb(!i_bit_clk), //input clkb read
.ceb(ceb11), //input ceb read
.resetb(!i_rst), //input resetb read
.oce(1'b0), //input oce
.ada(cnt_rec), //input [4:0] ada write
.din(data_adc), //input [3:0] din write
.adb(cnt_read) //input [4:0] adb read
);
GW_SDP sram_right(
.dout(data_sram2), //output [3:0] dout read
.clka(o_bit_clk), //input clka write
.cea(cea2), //input cea write
.reseta(!i_rst), //input reseta write
.clkb(!i_bit_clk), //input clkb read
.ceb(ceb22), //input ceb read
.resetb(!i_rst), //input resetb read
.oce(1'b0), //input oce
.ada(cnt_rec), //input [4:0] ada write
.din(data_adc), //input [3:0] din write
.adb(cnt_read) //input [4:0] adb read
);
assign o_data = (ceb11 & (cnt_read_cnt == 4'b0001))? data_sram1[3] :
(ceb11 & (cnt_read_cnt == 4'b0010))? data_sram1[2] :
(ceb11 & (cnt_read_cnt == 4'b0100))? data_sram1[1] :
(ceb11 & (cnt_read_cnt == 4'b1000))? data_sram1[0] :
(ceb22 & (cnt_read_cnt == 4'b0001))? data_sram2[3] :
(ceb22 & (cnt_read_cnt == 4'b0010))? data_sram2[2] :
(ceb22 & (cnt_read_cnt == 4'b0100))? data_sram2[1] :
(ceb22 & (cnt_read_cnt == 4'b1000))? data_sram2[0] : 1'b0;