好久没动手了,使用Verilog编写一个AD7356的SPI驱动程序。
 AD7356是一个双通道、12位、低功耗的ADC。最高采样速率可达5MSPS,全功率输入带宽为110MHz。AD7356的引脚图如下。
 
 SPI的时序图如下,为了使单通道的采样速率达到最高的5MSPS,只能选择第一种时序。第二种时序,可以实现只使用一根数据线读取两个通道的数据的功能,但理论最高采样速率就减半了。
 
 下表是手册中SPI时序的约束。
 
 为了使采样速率达到5MSPS,必须在200ns内完成数据转换与数据读取。经分析,可以使用 
     
      
       
       
         f 
        
       
         = 
        
       
         160 
        
       
          MHz 
        
       
      
        f=160\text{ MHz} 
       
      
    f=160 MHz的时钟,周期为 
     
      
       
       
         t 
        
       
         = 
        
       
         6.25 
        
       
          ns 
        
       
      
        t=6.25\text{ ns} 
       
      
    t=6.25 ns,再使用该时钟分频出 
     
      
       
       
         80 
        
       
          MHz 
        
       
      
        80\text{ MHz} 
       
      
    80 MHz的SPI时钟,SPI时钟周期为 
     
      
       
        
        
          t 
         
         
         
           s 
          
         
           c 
          
         
           l 
          
         
           k 
          
         
        
       
         = 
        
       
         12.5 
        
       
          ns 
        
       
      
        t_{sclk}=12.5\text{ ns} 
       
      
    tsclk=12.5 ns。则
  
      
       
        
         
         
           t 
          
         
           CONVERT 
          
         
        
          = 
         
         
         
           t 
          
         
           2 
          
         
        
          + 
         
        
          13 
         
        
          × 
         
         
         
           t 
          
         
           sclk 
          
         
        
       
         t_{\text{CONVERT}}=t_2 + 13 \times t_{\text {sclk}} 
        
       
     tCONVERT=t2+13×tsclk
 取 
     
      
       
        
        
          t 
         
        
          2 
         
        
       
         = 
        
       
         t 
        
       
      
        t_2 = t 
       
      
    t2=t,则 
     
      
       
        
        
          t 
         
        
          CONVERT 
         
        
       
         = 
        
       
         168.75 
        
       
          ns 
        
       
      
        t_{\text{CONVERT}}=168.75 \text { ns} 
       
      
    tCONVERT=168.75 ns,再加上半个SPI时钟周期 
     
      
       
        
        
          t 
         
         
         
           s 
          
         
           c 
          
         
           l 
          
         
           k 
          
         
        
       
         / 
        
       
         2 
        
       
         = 
        
       
         6.25 
        
       
          ns 
        
       
      
        t_{sclk}/2=6.25 \text { ns} 
       
      
    tsclk/2=6.25 ns和最小 
     
      
       
        
        
          t 
         
        
          QUIT 
         
        
       
         = 
        
       
         t 
        
       
         = 
        
       
         6.25 
        
       
          ns 
        
       
      
        t_{\text{QUIT}}=t =6.25\text{ ns} 
       
      
    tQUIT=t=6.25 ns。则总时间为 
     
      
       
       
         181.25 
        
       
          ns 
        
       
      
        181.25 \text{ ns} 
       
      
    181.25 ns,可见在160MHz的时钟下实现该模块是可以满足需求的。
 同时,为了实现连续触发和单次触发的功能,加入采样触发信号,以触发信号的上升沿为一次数据读取的标志。程序如下:
module spi_80M_5M(
    input clk,   //160M
    input rst_n,
    input clk_5M,//5M  
    input sdata_A,
    input sdata_B,
    
    output cs,
    output sclk, //80M
    output [11:0] out_A,
    output [11:0] out_B, 
    output out_A_valid, 
    output out_B_valid
);
//上升沿检测
wire start_flag;
reg [1:0] start_flag_dly;
assign start_flag = ~start_flag_dly[1] & start_flag_dly[0];
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        start_flag_dly <= 2'd0;
    end
    else begin
        start_flag_dly <= {start_flag_dly[0], clk_5M};
    end
end
reg trans_valid;
reg [4:0] clk_cnt;
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        trans_valid <= 1'b0;
    end
    else if(start_flag == 1'b1) begin
        trans_valid <= 1'b1;
    end
    else if(clk_cnt == 5'd31) begin
        trans_valid <= 1'b0;
    end
    else begin
        trans_valid <= trans_valid;
    end
end
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        clk_cnt <= 5'd0;
    end
    else if(trans_valid == 1'b1) begin
        clk_cnt <= clk_cnt + 5'd1;
    end
    else begin
        clk_cnt <= clk_cnt;
    end
end
//sclk计数 0-31
reg [4:0] sclk_reg_cnt;
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        sclk_reg_cnt <= 5'd0;
    end
    else if(trans_valid == 1'b1) begin
        sclk_reg_cnt <= sclk_reg_cnt + 5'd1;
    end
    else begin
        sclk_reg_cnt <= sclk_reg_cnt;
    end
end
//CS控制
reg cs_reg;
assign cs = cs_reg;
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        cs_reg <= 1'b1;
    end
    else if(sclk_reg_cnt == 5'd0 && trans_valid == 1'b1) begin
        cs_reg <= 1'b0;
    end
    else if(sclk_reg_cnt == 5'd28 && trans_valid == 1'b1) begin
        cs_reg <= 1'b1;
    end
    else begin
        cs_reg <= cs_reg;
    end
end
//产生SPI串行时钟
reg sclk_reg;
assign sclk = (cs_reg == 1'b0) ? sclk_reg : 1'b1;
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        sclk_reg <= 1'b1;
    end 
    else if(sclk_reg_cnt[0] == 1'b1) begin
        sclk_reg <= 1'b0;
    end
    else if(sclk_reg_cnt[0] == 1'b0) begin
        sclk_reg <= 1'b1;
    end
    else begin
        sclk_reg <= 1'b1;
    end
end
//sclk上升沿更新数据
reg [13:0] data_A_reg;
reg [13:0] data_B_reg;
always @(negedge sclk or negedge rst_n) begin
    if(!rst_n) begin
        data_A_reg <= 'd0;
        data_B_reg <= 'd0;
    end 
    else begin
        data_A_reg <= {data_A_reg, sdata_A};
        data_B_reg <= {data_B_reg, sdata_B};
    end
end
//输出12位有效数据
assign out_A = ( (cs_reg == 1'b1) && (sclk_reg_cnt > 5'd29) ) ? data_A_reg[11:0] : 12'd0;
assign out_B = ( (cs_reg == 1'b1) && (sclk_reg_cnt > 5'd29) ) ? data_B_reg[11:0] : 12'd0;
assign out_A_valid = ( (cs_reg == 1'b1) && (sclk_reg_cnt > 5'd29) )? 1'b1 : 1'b0;
assign out_B_valid = ( (cs_reg == 1'b1) && (sclk_reg_cnt > 5'd29) )? 1'b1 : 1'b0;
endmodule
为了验证SPI时序的正确性,简单起见,芯片的连接方式如下。
 
 设置sclk、cs、sdataA、sdataB的引脚。vinA=2.5V,vinB = 0V。使用ila抓取数据如下。
 
 ila的时钟为160MHz,上图可以看出单次采样的整个时序的长度为32个时钟周期,即200ns。
 cs的下降沿到sclk的第一个下降沿的时间为6.25ns,大于时序约束中的 
     
      
       
        
        
          t 
         
        
          2 
         
        
       
      
        t_2 
       
      
    t2最小值5ns。
 sclk时钟为80MHz,小于sclk时钟的最大值100MHz。
  
     
      
       
        
        
          t 
         
        
          CONVERT 
         
        
       
      
        t_{\text{CONVERT}} 
       
      
    tCONVERT为27个ila时钟周期,即168.75 ns。 
     
      
       
        
        
          t 
         
        
          QUIT 
         
        
       
      
        t_{\text{QUIT}} 
       
      
    tQUIT 为4个ila时钟周期,即25 ns,大于时序约束最小值5 ns。
 最后在sclk的下降沿取1比特数据,构成12位采样数据输出。此时,out_A[11:0] = 4095,out_B[11:0] = 2047。AD7356的模拟输入与数字输出对应关系如下图。
 



















