DSP

基于Zynq的数据采集系统设计与调试(三) —— FIFO的使用

2019-07-13 20:24发布

前言:     FIFO是数据采集系统中必不可少的环节,AD采回来的数据要送至ARM/DSP处理,或将采回来的数据写到本地,都需要解决读写速度匹配问题,解决这类问题,首选FIFO。    在我们的设计当中,使用的是ADI公司的AD7989,18bit,100KSPS,采用三线SPI数据传输模式。采用两级FIFO,第一级FIFO用于缓存AD采样点数据,第二级FIFO用于DMA数据传输。 一. FIFO的使用     在本设计中,将数据这样打包:一个package包含1024个字(4096Byte)其中1020个AD采样数据点(AD采样数据扩展成32bit),4个字的数据包信息:通道号(有两个通道)、块号(package编号)、触发信号位置、CRC校验码。     第一级FIFO(FIFO1)缓存AD采样点,时钟与AD时钟相同,都是50MHz,第二级FIFO(FIFO2)从第一级FIFO取数据,当取到1020个数据时,就往FIFO2中写4个字的数据包信息,第二级FIFO用于DMA将数据流送至内存DDR3的数据缓存。FIFO2采用异步时钟,因为触发信号(Trig)是一个脉冲,触发信号模块使用的时钟也是50MHz,因此写时钟仍然是50MHz,读时钟采用DMA时钟(200MHz)。FIFO都是Xilinx Vivado下自带的IP core。     FIFO1的写使能信号是ad模块的ad_data_rdy,当FIFO1中有数据时,读使能信号就有效(rd_en = !fifo_empty); FIFO2的写使能信号在FIFO1数出数据有效后一个时钟有效, 读使能是FIFO2的prog_empty来控制的。关于prog_empty信号,一开始的时候对Empty Threshold Assert Value和Empty Threshold Negate Value理解有误,后来查看了Xilinx FIFO ipCore手册pg057-fifo-generator.pdf的pg105-106得知:
    Forbuilt-in FIFOs, the number of entries in the FIFO must be greater than thethreshold value + 1 before prog_empty is deasserted. For non built-inFIFOs(block RAM, distributed RAM, and so forth), if the number of entries inthe FIFO is greater than threshold value, prog_empty is deasserted. 本设计使用的是Block RAM,对于Block RAM FIFO而言,当FIFO中的数据个数大于Negate Value时,prog_empty=0,当FIFO中数据个数小于等于Assert Value时,prog_empty=1。本设计中,FIFO_RD_EN = prog_empty,下图是第二级FIFO读写数据时序图,wr_fifo2_count表示写入到FIFO中数据个数,read_pointer表示从FIFO中读出的数据个数,FIFO_ALMOST_EMPTY即prog_empty信号。
    左边红线可看出,本设计中设置的Empty Threshold Negate Value=16,当FIFO中有17(>= Negate Value)个数据时,prog_empty=0。本设计中设置的Empty Threshold Assert Value=2,所以当FIFO中数据个数小于等于2时,prog_empty要变为1。对应到右边红线,在这个时钟边沿,FIFO中正好还有2个数据,因此prog_empty由0变为1,而FIFO_RD_EN = !prog_empty,此时仍有效,因此会继续从FIFO中读出一个数据。
二. FIFO的配置     1. FIFO1的配置如下:         1) 选型: Common Clock Block RAM
        
        
       
        2) 设置数据深度:因ad7989采样率为100KSPS,每隔10us转换得到1个数据,DAM时钟是200M,相对而言AD速度较慢,因此深度设为64就满足设计要求,但考虑到资源使用情况都是1个18K BRAM,所以设置为128

      
          3) 其他为默认设置
    2. FIFO2的配置如下:         1) 选型: Independent Clock Block RAM
        2) 数据深度设置: 此处设为1个package的数据深度(可能优化的时候会更改,但1024足够满足本设计要求的)

        3) 设置prog_empty(很关键):Empty Threshold Assert Value=2,Empty Threshold Negate Value=16
          为什么要用prog_empty信号来作为FIFO2的读使能控制信号(rd_en = !prog_empty)呢? 因为DMA的MAX Burst Length = 16,关于DMA的Burst传输我是这样理解的,(如果有误,还请指点哈!) 对于Write Channel(write channel --- Receive packet --- S2MM),dma端先送TREADY信号, 等待M_AXIS_S2MM主机的TVALID和TDATA,每接收一个数据内部计数器加一,当计数器达到16时,TREADY拉低,dma申请一次总线,然后把接收到的数据写入到DDR3(或者内存)中,即每接收burst length个数据,申请一次总线,这样就不会太占用系统资源。使用prog_empty作为FIFO读使能控制,可保证连续读出Burst Length个数据,这样就不会每次占用总线太长时间。         
        4) 其他采用默认设置
三. ad7989_fifo模块的实现 [plain] view plain copy
  1. `timescale 1ns / 1ps  
  2. //////////////////////////////////////////////////////////////////////////////////  
  3. // Company:   
  4. // Engineer:   
  5. //   
  6. // Create Date: 2016/06/20 11:03:04  
  7. // Design Name:   
  8. // Module Name: ad7989_fifo  
  9. // Project Name:   
  10. // Target Devices:   
  11. // Tool Versions:   
  12. // Description:   
  13. //   
  14. // Dependencies:   
  15. //   
  16. // Revision:  
  17. // Revision 0.01 - File Created  
  18. // Additional Comments:  
  19. //   
  20. //////////////////////////////////////////////////////////////////////////////////  
  21.   
  22.   
  23. module ad7989_fifo(  
  24.                      input    ad_clk,rst_n,ad_start,//ad_start: ad_start是由PS部分发送过来的ad采样控制信号   
  25.                     // input    trig_in,//trig_in由DA部分(DDS)传过来脉冲信号,启动ad转换  
  26.                      // AD SPI port  
  27.                      input    ad_sdo, //ad转换串型数据  
  28.                      output   ad_cnv,  
  29.                      output   ad_sclk,       
  30.                      // FIFO2 port  
  31.                      input    rd_clk,    
  32.                      input    rd_en,  
  33.                      output   empty,  
  34.                      output   full,  
  35.                      output   almost_empty, //Empty Threshold Assert Valie = 16,Empty Threshold Negate Valie = 17,  
  36.                      output   [31:0] fifo_dout               
  37.                    );  
  38.     // intern signal                 
  39.     wire    ad_data_rdy;  
  40.     wire    [17:0] ad_data;  
  41.     wire    [31:0] sample_data;  
  42.     wire    [31:0] fifo1_dout;  
  43.     wire    [31:0] fifo2_din;  
  44.     wire    fifo1_empty;  
  45.     wire    wr_fifo1_en;  
  46.     wire    rd_fifo1_en;  
  47.     wire    wr_fifo2_en;  
  48.     wire    trigger;  
  49.     wire    trig_in;  
  50.       
  51.     // reg variable  
  52.     reg    [31:0] block_num = 0; //块序列号  
  53.     reg    [31:0] trig_position = 32'd0; //触发信号来时对应的AD数据位置信息  
  54.     reg    [31:0] channel_num = 32'd1; //通道号: 1表示1倍频通道采集的数据,2表示2倍频采集的数据  
  55.     reg    [31:0] crc_code = 32'd0;  //crc校验码  
  56.     // trig_in detect  
  57.     reg    trig_reg1 = 0;  
  58.     reg    trig_reg2 = 0;  
  59.     // FIFO_1  
  60.     reg    [9:0] wr_fifo1_count = 0; //Sample data 计数器 4096Byte数据(1020次)一次循环  
  61. //    reg    rd_fifo1_en_tmp;  
  62.     // FIFO_2  
  63.     reg    wr_fifo2_en_tmp1;  
  64.     reg    wr_fifo2_en_tmp2;  
  65.     reg    [10:0]  wr_fifo2_count = 0;  
  66.     reg    [31:0]  fifo2_din_reg;    
  67.           
  68.              
  69.     assign    sample_data = ({{15{ad_data[17]}},ad_data[16:0]});  //  
  70.   
  71. //    always @(posedge ad_clk) begin  
  72. //      rd_fifo1_en_tmp <= wr_fifo1_en;  
  73. //    end  
  74. //    assign   rd_fifo1_en = rd_fifo1_en_tmp;  
  75.   
  76.     assign    wr_fifo1_en = ad_data_rdy;   
  77.     assign    rd_fifo1_en = !fifo1_empty;  
  78.       
  79.       
  80.     //=========== fifo1_data count ===========  
  81.     always @(posedge ad_clk) begin  
  82.       if(wr_fifo1_count == 11'd1020) wr_fifo1_count <= 11'd0; //一个packet包含1020个ADC数据  
  83.       else if(wr_fifo1_en) wr_fifo1_count <= wr_fifo1_count + 1'b1;         
  84.       else wr_fifo1_count <= wr_fifo1_count;  
  85.     end  
  86.       
  87.     //========== trig_position ==========  
  88.     always @(posedge ad_clk) begin  
  89.       trig_reg1 <= trig_in;  
  90.       trig_reg2 <= trig_reg1;  
  91.     end  
  92.     assign trigger = trig_reg1 & (!trig_reg2);  
  93.       
  94.     always @(posedge ad_clk) begin // trig_in是边沿信号  
  95.       if(trigger == 1) trig_position <= wr_fifo1_count + 1; //trig_position   
  96.       else if(wr_fifo2_count == 11'd1024) trig_position <= 0; //wr_fifo2_count = 1022时,将trig_position写入fifo中  
  97.       else trig_position <= trig_position;  
  98.     end  
  99.       
  100.     // ========== block_num ==========  
  101.     always @(posedge ad_clk) begin  
  102.       if(wr_fifo2_count == 11'd1024 && wr_fifo2_en == 1) block_num <= block_num + 1'b1;  
  103.       else block_num <= block_num;  
  104.     end  
  105.       
  106.     // ========== crc_code ==========  
  107.     always @(posedge ad_clk) begin  
  108.       if(!rst_n) crc_code <= 0;  
  109.       else if(wr_fifo2_count < 11'd1020 && wr_fifo2_en == 1) crc_code <= crc_code ^ fifo2_din_reg;  
  110.       else case(wr_fifo2_count)  
  111.         11'd1020: crc_code <= crc_code ^ block_num;  
  112.         11'd1021: crc_code <= crc_code ^ trig_position;  
  113.         11'd1022: crc_code <= crc_code ^ channel_num;  
  114.         11'd1023: crc_code <= crc_code;  
  115.         11'd1024: crc_code <= 0;  
  116.       endcase        
  117.     end   
  118.       
  119.     // ========== wr_fifo2_en ==========  
  120.     always @(posedge ad_clk) begin  
  121.       wr_fifo2_en_tmp1 <= rd_fifo1_en;  
  122.       wr_fifo2_en_tmp2 <= wr_fifo2_en_tmp1;  
  123.     end  
  124.      
  125.     assign    wr_fifo2_en = (wr_fifo2_count <= 11'd1020) ? wr_fifo2_en_tmp2 : 1'b1; //连续写4个字的控制信息: 块序列号、crc校验码、触发信号来时对应的AD数据位置信息、通道号  
  126.   
  127.     // ========== fifo2 data count ==========    
  128.     always @(posedge ad_clk) begin  
  129.       if(wr_fifo2_count == 11'd1024) wr_fifo2_count <= 0;  
  130.       else if(wr_fifo2_en == 1) wr_fifo2_count <= wr_fifo2_count + 1'b1;  
  131.       else if(wr_fifo2_count>= 11'd1020) wr_fifo2_count <= wr_fifo2_count + 1'b1;  
  132.       else wr_fifo2_count <= wr_fifo2_count;  
  133.     end  
  134.       
  135.     // ========== fifo2_din ==========  
  136.     assign  fifo2_din = fifo2_din_reg;  
  137.       
  138.     always @(posedge ad_clk) begin  
  139.         case(wr_fifo2_count)   
  140.           11'd1020: fifo2_din_reg <= block_num;  
  141.           11'd1021: fifo2_din_reg <= trig_position;  
  142.           11'd1022: fifo2_din_reg <= channel_num;  
  143.           11'd1023: fifo2_din_reg <= crc_code;  
  144.           default: fifo2_din_reg <= fifo1_dout;  
  145.         endcase  
  146.     end  
  147.   
  148.                   
  149.     ad7989_dev_if   U_ad(  
  150.                           .ad_clk(ad_clk),  
  151.                           .rst_n(rst_n),  
  152.                           .ad_start(ad_start),//ad_start: ad_start是由PS部分发送过来的ad采样控制信号   
  153.                           .ad_sdo(ad_sdo),                      //ad转换串型数据  
  154.                           .ad_cnv(ad_cnv),  
  155.                           .ad_sclk(ad_sclk),                         
  156.                           .ad_data(ad_data),  
  157.                           .ad_data_rdy(ad_data_rdy)   // data is available  
  158.                          );     
  159.     trig_generator    U_trig(  
  160.                               .ad_clk(ad_clk),  
  161.                               .rst_n(rst_n),  
  162.                               .ad_data_rdy(ad_data_rdy),  
  163.                               .trig_signal(trig_in)  
  164.                             );  
  165.                            
  166.     fifo_sample_data    U_ad_samle_fifo (  
  167.                                            .clk(ad_clk),      // input wire clk  
  168.                                            .srst(!rst_n),    // input wire srst  
  169.                                            .din(sample_data),      // input wire [31 : 0] din  
  170.                                            .wr_en(wr_fifo1_en),  // input wire wr_en  
  171.                                            .rd_en(rd_fifo1_en),  // input wire rd_en  
  172.                                            .dout(fifo1_dout),    // output wire [31 : 0] dout  
  173.                                            .full(),    // output wire full  
  174.                                            .empty(fifo1_empty)  // output wire empty  
  175.                                          );      
  176.                                            
  177.     fifo_dma_stream_0    U_dma_fifo (  
  178.                                        .rst(!rst_n),        // input wire rst  
  179.                                        .wr_clk(ad_clk),  // input wire wr_clk  
  180.                                        .rd_clk(rd_clk),  // input wire rd_clk  
  181.                                        .din(fifo2_din),        // input wire [31 : 0] din  
  182.                                        .wr_en(wr_fifo2_en),    // input wire wr_en  
  183.                                        .rd_en(rd_en),    // input wire rd_en  
  184.                                        .dout(fifo_dout),      // output wire [31 : 0] dout  
  185.                                        .full(full),      // output wire full  
  186.                                        .empty(empty),    // output wire empty  
  187.                                        .prog_empty(almost_empty)   
  188.                                      );  
  189.          
  190. endmodule