fpga项目: i2s代码学习 - minichao9901/TangNano-20k-Zynq-7020 GitHub Wiki

1) tangNano-20k i2s DAC例程代码:学习要点

  • 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

2) 仿真时序分析

用vivado进行仿真,把gaowin的pll替换为vivado的ip。vivado没有时钟分频器的ip,自己手写一个4分频了。

  1. 由时钟生成0-31计数器
  2. 由cnt=0||cnt=16生成req_r信号,这会延一拍clk1p5m(到cnt=1||17的位置)。图中1所示。
  3. 由req_r信号生成addr,会延迟一拍clk1p5m(到cnt=2||18的位置)。图中2所示。
  4. 由addr信号生成data,会延迟一拍clk50m(忽略忽略不计)
  5. 将req_r延迟一拍clk1p5m,生成req_r1,用于对齐取数据。图中3所示。
  6. 用req_r1来取data的数据,生成idata_r,延迟一拍clk1p5m(到cnt=3||18的位置)。图中6所示。
  7. HP_DIN用组合逻辑产生,无延时。
  8. HP_WS的开始时刻,要比idata_r开始shift数据开始提前一拍(根据标准i2s协议)。从时序图上看,它要用cnt==2||cnt==18来触发。产生的结果,会延迟一拍clk1p5m(到cnt=3||19的位置)。 HP_WS一共延迟了3拍clk1p5m。
  9. 因此HP_DIN真正的数据开始时刻,是从cnt==4||cnt==19开始的,一共延迟了4拍clk1p5m。

3)附录:代码

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