-
Notifications
You must be signed in to change notification settings - Fork 0
fpga项目: i2s代码学习
minichao9901 edited this page Dec 17, 2023
·
2 revisions
- rom表是直接用case语句产生的,不是用bram产生的。对于容量不大的,综合后的硬逻辑连线,更方便。
- 注意这个时钟是50MHz的高频时钟,从addr产生到data输出虽然会延迟一个clk,但是是高频的,可以忽略不记。
module rom_save_sin(
input clk,
input rst_n,
input [7:0] addr,
output reg [15:0] data
);
always@(posedge clk or negedge rst_n)
if(!rst_n)
data <= 'd0 ;
else begin
case(addr)
8'd0 :data <= 16'd0;
8'd1 :data <= 16'd804;
......
8'd254 :data <= 16'd63929;
8'd255 :data <= 16'd64732;
default: data <= 16'd0;
endcase
end
endmodule
- rom的地址是如何产生的?它是由req信号触发产生的。只有req信号脉冲来才地址加1,否则地址维持。另外地址的范围是0-255。
- 注意这个时钟是1.5MHz
always@(posedge clk_1p5m_w or negedge rst_n)
if(!rst_n)
addr_r <= 10'd0;
else if(addr_r <= 'd255)
addr_r <= req_w?addr_r+1'b1:addr_r;
else
addr_r <= 10'd0;
- 4分频器如何实现?
reg [1:0] cnt;
always @(posedge clk_6m_w or negedge rst_n)
if(rst_n==0)
cnt<=2'b00;
else
cnt<=cnt+1;
assign clk_1p5m_w=(cnt<=1)? 1:0;
- 整个延时一共有4拍,为了数据对齐,输出HP_WS的产生取的是cnt==2||cnt==18作为条件。(备注:原始程序有bug,不满足标准i2s协议,我做了修改)
- 根据标准i2s协议,HP_WS要比HP_DIN提前一拍
always@(posedge clk_1p536m or negedge rst_n)
begin
if(!rst_n)
HP_WS_r <= 1'b0;
else
// HP_WS_r <= (b_cnt == 5'd3)?1'b0: ((b_cnt == 5'd19)?1'b1:HP_WS_r);//对齐数据。原始程序有bug,不满足标准i2s协议,修改如下
HP_WS_r <= (b_cnt == 5'd2)?1'b0: ((b_cnt == 5'd18)?1'b1:HP_WS_r);//对齐数据
end
endmodule
用vivado进行仿真,把gaowin的pll替换为vivado的ip。vivado没有时钟分频器的ip,自己手写一个4分频了。
![image](https://private-user-images.githubusercontent.com/61445559/291060240-7f63a435-a357-4c2b-a466-e7fe317d16ea.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MzkyMDM0NzcsIm5iZiI6MTczOTIwMzE3NywicGF0aCI6Ii82MTQ0NTU1OS8yOTEwNjAyNDAtN2Y2M2E0MzUtYTM1Ny00YzJiLWE0NjYtZTdmZTMxN2QxNmVhLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTAyMTAlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwMjEwVDE1NTkzN1omWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWJiZTZiOWJhMjJhMGVhOWNmODEzNWVhMGY3ZDUzODI3OTNlZGZhOTI4Y2FhYTQyOGY0ODI0NmFiNDYxZTlhMzcmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.wcVUkmO6B8v6HX7lHoVpUByxKuREfPm1YcVF86jU8rs)
- 由时钟生成0-31计数器
- 由cnt=0||cnt=16生成req_r信号,这会延一拍clk1p5m(到cnt=1||17的位置)。图中1所示。
- 由req_r信号生成addr,会延迟一拍clk1p5m(到cnt=2||18的位置)。图中2所示。
- 由addr信号生成data,会延迟一拍clk50m(忽略忽略不计)
- 将req_r延迟一拍clk1p5m,生成req_r1,用于对齐取数据。图中3所示。
- 用req_r1来取data的数据,生成idata_r,延迟一拍clk1p5m(到cnt=3||18的位置)。图中6所示。
- HP_DIN用组合逻辑产生,无延时。
- HP_WS的开始时刻,要比idata_r开始shift数据开始提前一拍(根据标准i2s协议)。从时序图上看,它要用cnt==2||cnt==18来触发。产生的结果,会延迟一拍clk1p5m(到cnt=3||19的位置)。 HP_WS一共延迟了3拍clk1p5m。
- 因此HP_DIN真正的数据开始时刻,是从cnt==4||cnt==19开始的,一共延迟了4拍clk1p5m。
module top(
input clk ,
input rst , //S1按键
//audio接口
output HP_BCK , //同clk_1p536m
output HP_WS , //左右声道切换信号,低电平对应左声道
output HP_DIN , //dac串行数据输入信号
output PA_EN , //音频功放使能,高电平有效
output reg led
);
wire clk_6m_w;//6MHz,为产生1.5MHz
wire clk_1p5m_w;//1.536MHz近似时钟
wire req_w;//读请求
wire [15:0] q_w;//rom读出的数据
reg [9:0] addr_r;//rom地址
assign PA_EN = 1'b1;//PA常开
assign rst_n = !rst ;
always@(posedge clk_1p5m_w or negedge rst_n)
if(!rst_n)
addr_r <= 10'd0;
else if(addr_r <= 'd255)
addr_r <= req_w?addr_r+1'b1:addr_r;
else
addr_r <= 10'd0;
Gowin_rPLL pll_27m_6m (
.clkout(clk_6m_w),
.reset(~rst_n),
.clkin(clk)
);
Gowin_CLKDIV clk_div4(
.clkout(clk_1p5m_w), //output clkout
.hclkin(clk_6m_w), //input hclkin
.resetn(rst_n) //input resetn
);
rom_save_sin rom_save_sin_inst(
.clk(clk),
.rst_n(rst_n),
.addr(addr_r),
.data(q_w)
);
//音频DAC驱动
audio_drive u_audio_drive_0(
.clk_1p536m(clk_1p5m_w),//bit时钟,每个采样点占32个clk_1p536m(左右声道各16)
.rst_n (rst_n),//低电平有效异步复位信号
//用户数据接口
.idata (q_w),
.req (req_w),//数据请求信号,可接外部FIFO的读请求(为避免空读,尽量和!fifo_empty相与后作为fifo_rd)
//audio接口
.HP_BCK (HP_BCK),//同clk_1p536m
.HP_WS (HP_WS),//左右声道切换信号,低电平对应左声道
.HP_DIN (HP_DIN)//dac串行数据输入信号
);
reg [23:0] counter; //定义一个变量来计数
always @(posedge clk or negedge rst_n) begin // Counter block
if (!rst_n)
counter <= 24'd0;
else if (counter < 24'd1349_9999) // 0.5s delay
counter <= counter + 1'b1;
else
counter <= 24'd0;
end
always @(posedge clk or negedge rst_n) begin // Toggle LED
if (!rst_n)
led <= 1'b1;
else if (counter == 24'd1349_9999) // 0.5s delay
led <= ~led; // ToggleLED
end
endmodule
//audio驱动
module audio_drive(
input clk_1p536m,//bit时钟,每个采样点占32个clk_1p536m(左右声道各16)
input rst_n ,//低电平有效异步复位信号
//用户数据接口
input [15:0] idata ,
output req ,//数据请求信号,可接外部FIFO的读请求(为避免空读,尽量和!fifo_empty相与后作为fifo_rd)
//audio接口
output HP_BCK ,//同clk_1p536m
output HP_WS ,//左右声道切换信号,低电平对应左声道
output HP_DIN //dac串行数据输入信号
);
reg [4:0] b_cnt;
reg req_r,req_r1;//req_r1延迟req_r一个时钟
reg [15:0] idata_r;//暂存idata,用于移位并转串时的中间变量
reg HP_WS_r,HP_DIN_r;
assign HP_BCK = clk_1p536m;
assign HP_WS = HP_WS_r ;
assign HP_DIN = HP_DIN_r ;
assign req = req_r ;
//b_cnt
always@(posedge clk_1p536m or negedge rst_n)
begin
if(!rst_n)
b_cnt <= 5'd0;
else
b_cnt <= b_cnt+1'b1;
end
//req_r
always@(posedge clk_1p536m or negedge rst_n)
begin
if(!rst_n)
req_r <= 1'b0;
else
req_r <= (b_cnt == 5'd0) || (b_cnt == 5'd16);//每16个时钟读入一个数据
end
//idata_r
always@(posedge clk_1p536m or negedge rst_n)
begin
if(!rst_n)
begin
req_r1 <= 1'b0;
idata_r <= 16'd0;
end
else
begin
req_r1 <= req_r;
idata_r <= req_r1?idata:idata_r<<1;
end
end
//HP_DIN_r
always@(posedge clk_1p536m or negedge rst_n)
begin
if(!rst_n)
HP_DIN_r <= 1'b0;
else
HP_DIN_r <= idata_r[15];
end
//HP_WS_r
always@(posedge clk_1p536m or negedge rst_n)
begin
if(!rst_n)
HP_WS_r <= 1'b0;
else
// HP_WS_r <= (b_cnt == 5'd3)?1'b0: ((b_cnt == 5'd19)?1'b1:HP_WS_r);//对齐数据(这一句有bug,不满足标准i2s协议,需要延迟1bit,需要修改如下)
HP_WS_r <= (b_cnt == 5'd2)?1'b0: ((b_cnt == 5'd18)?1'b1:HP_WS_r);//对齐数据
end
endmodule
module rom_save_sin(
input clk,
input rst_n,
input [7:0] addr,
output reg [15:0] data
);
always@(posedge clk or negedge rst_n)
if(!rst_n)
data <= 'd0 ;
else begin
case(addr)
8'd0 :data <= 16'd0;
8'd1 :data <= 16'd804;
8'd2 :data <= 16'd1607;
8'd3 :data <= 16'd2410;
8'd4 :data <= 16'd3211;
8'd5 :data <= 16'd4011;
8'd6 :data <= 16'd4807;
8'd7 :data <= 16'd5601;
8'd8 :data <= 16'd6392;
8'd9 :data <= 16'd7179;
8'd10 :data <= 16'd7961;
8'd11 :data <= 16'd8739;
8'd12 :data <= 16'd9511;
8'd13 :data <= 16'd10278;
8'd14 :data <= 16'd11039;
8'd15 :data <= 16'd11792;
8'd16 :data <= 16'd12539;
8'd17 :data <= 16'd13278;
8'd18 :data <= 16'd14009;
8'd19 :data <= 16'd14732;
8'd20 :data <= 16'd15446;
8'd21 :data <= 16'd16151;
8'd22 :data <= 16'd16845;
8'd23 :data <= 16'd17530;
8'd24 :data <= 16'd18204;
8'd25 :data <= 16'd18867;
8'd26 :data <= 16'd19519;
8'd27 :data <= 16'd20159;
8'd28 :data <= 16'd20787;
8'd29 :data <= 16'd21402;
8'd30 :data <= 16'd22005;
8'd31 :data <= 16'd22594;
8'd32 :data <= 16'd23170;
8'd33 :data <= 16'd23731;
8'd34 :data <= 16'd24279;
8'd35 :data <= 16'd24811;
8'd36 :data <= 16'd25329;
8'd37 :data <= 16'd25832;
8'd38 :data <= 16'd26319;
8'd39 :data <= 16'd26790;
8'd40 :data <= 16'd27245;
8'd41 :data <= 16'd27683;
8'd42 :data <= 16'd28105;
8'd43 :data <= 16'd28510;
8'd44 :data <= 16'd28898;
8'd45 :data <= 16'd29268;
8'd46 :data <= 16'd29621;
8'd47 :data <= 16'd29956;
8'd48 :data <= 16'd30273;
8'd49 :data <= 16'd30571;
8'd50 :data <= 16'd30852;
8'd51 :data <= 16'd31113;
8'd52 :data <= 16'd31356;
8'd53 :data <= 16'd31580;
8'd54 :data <= 16'd31785;
8'd55 :data <= 16'd31971;
8'd56 :data <= 16'd32137;
8'd57 :data <= 16'd32285;
8'd58 :data <= 16'd32412;
8'd59 :data <= 16'd32521;
8'd60 :data <= 16'd32609;
8'd61 :data <= 16'd32678;
8'd62 :data <= 16'd32728;
8'd63 :data <= 16'd32757;
8'd64 :data <= 16'd32767;
8'd65 :data <= 16'd32757;
8'd66 :data <= 16'd32728;
8'd67 :data <= 16'd32678;
8'd68 :data <= 16'd32609;
8'd69 :data <= 16'd32521;
8'd70 :data <= 16'd32412;
8'd71 :data <= 16'd32285;
8'd72 :data <= 16'd32137;
8'd73 :data <= 16'd31971;
8'd74 :data <= 16'd31785;
8'd75 :data <= 16'd31580;
8'd76 :data <= 16'd31356;
8'd77 :data <= 16'd31113;
8'd78 :data <= 16'd30852;
8'd79 :data <= 16'd30571;
8'd80 :data <= 16'd30273;
8'd81 :data <= 16'd29956;
8'd82 :data <= 16'd29621;
8'd83 :data <= 16'd29268;
8'd84 :data <= 16'd28898;
8'd85 :data <= 16'd28510;
8'd86 :data <= 16'd28105;
8'd87 :data <= 16'd27683;
8'd88 :data <= 16'd27245;
8'd89 :data <= 16'd26790;
8'd90 :data <= 16'd26319;
8'd91 :data <= 16'd25832;
8'd92 :data <= 16'd25329;
8'd93 :data <= 16'd24811;
8'd94 :data <= 16'd24279;
8'd95 :data <= 16'd23731;
8'd96 :data <= 16'd23170;
8'd97 :data <= 16'd22594;
8'd98 :data <= 16'd22005;
8'd99 :data <= 16'd21402;
8'd100 :data <= 16'd20787;
8'd101 :data <= 16'd20159;
8'd102 :data <= 16'd19519;
8'd103 :data <= 16'd18867;
8'd104 :data <= 16'd18204;
8'd105 :data <= 16'd17530;
8'd106 :data <= 16'd16845;
8'd107 :data <= 16'd16151;
8'd108 :data <= 16'd15446;
8'd109 :data <= 16'd14732;
8'd110 :data <= 16'd14009;
8'd111 :data <= 16'd13278;
8'd112 :data <= 16'd12539;
8'd113 :data <= 16'd11792;
8'd114 :data <= 16'd11039;
8'd115 :data <= 16'd10278;
8'd116 :data <= 16'd9511;
8'd117 :data <= 16'd8739;
8'd118 :data <= 16'd7961;
8'd119 :data <= 16'd7179;
8'd120 :data <= 16'd6392;
8'd121 :data <= 16'd5601;
8'd122 :data <= 16'd4807;
8'd123 :data <= 16'd4011;
8'd124 :data <= 16'd3211;
8'd125 :data <= 16'd2410;
8'd126 :data <= 16'd1607;
8'd127 :data <= 16'd804;
8'd128 :data <= 16'd0;
8'd129 :data <= 16'd64732;
8'd130 :data <= 16'd63929;
8'd131 :data <= 16'd63126;
8'd132 :data <= 16'd62325;
8'd133 :data <= 16'd61525;
8'd134 :data <= 16'd60729;
8'd135 :data <= 16'd59935;
8'd136 :data <= 16'd59144;
8'd137 :data <= 16'd58357;
8'd138 :data <= 16'd57575;
8'd139 :data <= 16'd56797;
8'd140 :data <= 16'd56025;
8'd141 :data <= 16'd55258;
8'd142 :data <= 16'd54497;
8'd143 :data <= 16'd53744;
8'd144 :data <= 16'd52997;
8'd145 :data <= 16'd52258;
8'd146 :data <= 16'd51527;
8'd147 :data <= 16'd50804;
8'd148 :data <= 16'd50090;
8'd149 :data <= 16'd49385;
8'd150 :data <= 16'd48691;
8'd151 :data <= 16'd48006;
8'd152 :data <= 16'd47332;
8'd153 :data <= 16'd46669;
8'd154 :data <= 16'd46017;
8'd155 :data <= 16'd45377;
8'd156 :data <= 16'd44749;
8'd157 :data <= 16'd44134;
8'd158 :data <= 16'd43531;
8'd159 :data <= 16'd42942;
8'd160 :data <= 16'd42366;
8'd161 :data <= 16'd41805;
8'd162 :data <= 16'd41257;
8'd163 :data <= 16'd40725;
8'd164 :data <= 16'd40207;
8'd165 :data <= 16'd39704;
8'd166 :data <= 16'd39217;
8'd167 :data <= 16'd38746;
8'd168 :data <= 16'd38291;
8'd169 :data <= 16'd37853;
8'd170 :data <= 16'd37431;
8'd171 :data <= 16'd37026;
8'd172 :data <= 16'd36638;
8'd173 :data <= 16'd36268;
8'd174 :data <= 16'd35915;
8'd175 :data <= 16'd35580;
8'd176 :data <= 16'd35263;
8'd177 :data <= 16'd34965;
8'd178 :data <= 16'd34684;
8'd179 :data <= 16'd34423;
8'd180 :data <= 16'd34180;
8'd181 :data <= 16'd33956;
8'd182 :data <= 16'd33751;
8'd183 :data <= 16'd33565;
8'd184 :data <= 16'd33399;
8'd185 :data <= 16'd33251;
8'd186 :data <= 16'd33124;
8'd187 :data <= 16'd33015;
8'd188 :data <= 16'd32927;
8'd189 :data <= 16'd32858;
8'd190 :data <= 16'd32808;
8'd191 :data <= 16'd32779;
8'd192 :data <= 16'd32769;
8'd193 :data <= 16'd32779;
8'd194 :data <= 16'd32808;
8'd195 :data <= 16'd32858;
8'd196 :data <= 16'd32927;
8'd197 :data <= 16'd33015;
8'd198 :data <= 16'd33124;
8'd199 :data <= 16'd33251;
8'd200 :data <= 16'd33399;
8'd201 :data <= 16'd33565;
8'd202 :data <= 16'd33751;
8'd203 :data <= 16'd33956;
8'd204 :data <= 16'd34180;
8'd205 :data <= 16'd34423;
8'd206 :data <= 16'd34684;
8'd207 :data <= 16'd34965;
8'd208 :data <= 16'd35263;
8'd209 :data <= 16'd35580;
8'd210 :data <= 16'd35915;
8'd211 :data <= 16'd36268;
8'd212 :data <= 16'd36638;
8'd213 :data <= 16'd37026;
8'd214 :data <= 16'd37431;
8'd215 :data <= 16'd37853;
8'd216 :data <= 16'd38291;
8'd217 :data <= 16'd38746;
8'd218 :data <= 16'd39217;
8'd219 :data <= 16'd39704;
8'd220 :data <= 16'd40207;
8'd221 :data <= 16'd40725;
8'd222 :data <= 16'd41257;
8'd223 :data <= 16'd41805;
8'd224 :data <= 16'd42366;
8'd225 :data <= 16'd42942;
8'd226 :data <= 16'd43531;
8'd227 :data <= 16'd44134;
8'd228 :data <= 16'd44749;
8'd229 :data <= 16'd45377;
8'd230 :data <= 16'd46017;
8'd231 :data <= 16'd46669;
8'd232 :data <= 16'd47332;
8'd233 :data <= 16'd48006;
8'd234 :data <= 16'd48691;
8'd235 :data <= 16'd49385;
8'd236 :data <= 16'd50090;
8'd237 :data <= 16'd50804;
8'd238 :data <= 16'd51527;
8'd239 :data <= 16'd52258;
8'd240 :data <= 16'd52997;
8'd241 :data <= 16'd53744;
8'd242 :data <= 16'd54497;
8'd243 :data <= 16'd55258;
8'd244 :data <= 16'd56025;
8'd245 :data <= 16'd56797;
8'd246 :data <= 16'd57575;
8'd247 :data <= 16'd58357;
8'd248 :data <= 16'd59144;
8'd249 :data <= 16'd59935;
8'd250 :data <= 16'd60729;
8'd251 :data <= 16'd61525;
8'd252 :data <= 16'd62325;
8'd253 :data <= 16'd63126;
8'd254 :data <= 16'd63929;
8'd255 :data <= 16'd64732;
default: data <= 16'd0;
endcase
end
endmodule