基于PCIE接口的高速大容量数据采集——硬件篇

 2022-5-7     作者:Emtronix     [nemail]    
[lablebox]

  高速大容量数据采集方案是一个可实现是连续采集数据率超过10MB/s的应用方案,方案包括基于FPGA的硬件设计部分,以及以Linux DMA Engine为基础框架的应用软件部分。方案的总体介绍请参考《基于PCIE接口的高速大容量数据采集——总体方案》一文。本文将重点介绍采用Xilinx的FPGA芯片XC7A50T-2CSG325实现数据采集以及利用PCIE端点进行DMA传输的硬件机制。


  XC7A50T的设计工具为Xilinx公司的Vivado 2021.1。本文认为用户已了解Xilinx的FPGA芯片特性及设计工具的使用,这方面就不再赘述。


功能框图


  创建的eta750_pcie_xdma_dtaker1_5.xpr采用block design,其功能框图如下:


基于PCIE接口的高速大容量数据采集.png

图1 ETA750 Block Design Diagram


  图1中的实例xdma_0是Xilinx公司的PCIE/DMA IP模块,作为PCIE端点设备(PCIE Endpoint Device)。PCIE的配置在后面介绍。IP模块实现通过PCIE接口,把数据传送到系统的某个内存区域,原始数据则通过AXI-Stream接口的C2H通道,由自定义的数据采集模块DTaker送入。DTaker是整个硬件设计的重点,由Vivado工具的<Create and Package New IP>创建,然后采用verilog编写全部数字逻辑。注意axi_aclk和axi_aresetn分别为全局工作时钟和全局复位信号。


  图1框图中的主要对外管脚是PCIE×1的三对高速差分信号,以及用户前端数字接口,这里是AD7606接口,如图2所示。


基于PCIE接口的高速大容量数据采集.png

图2 ETA750主要外部信号管脚


  用户时钟user_clk是供用户前端逻辑的时钟,由用户根据需求配置,在ETA750中,选择50MHz时钟信号。外部硬件复位信号sys_rst_n,低电平有效,直接接入PCIE/DMA模块。


基于PCIE接口的高速大容量数据采集.png

图3 ETA750辅助外部信号管脚


  对Xilinx的PCIE/DMA IP模块的细节感兴趣的客户,请参考手册《DMA/Bridge Subsystem for PCI Express v4.1》。以下列出对PCIE的基本配置。


PCIE配置


基于PCIE接口的高速大容量数据采集.png

图4 PCIE接口配置


  作为PCIE Endpoint Device,其数据道数与ESMARC主板一致,为PCIE×1;因主板均支持PCIE2.0或以上标准,所以选择Maximum Link Speed为5.0Gbps。AXI总线是ARM公司的ABMA总线架构的一部分,是一种高性能同步总线,支持multi-initiator, multi-target通讯机制。Xilinx把AXI作为其FPGA的内部总线,后续自定义的IP核DTaker都是基于AXI的。网上有大量AXI的介绍。这里AXI的配置包括:64-bit数据宽度、62.5MHz的AXI_ACLK、DMA控制器数据接口采用AXI-Stream接口形式。


基于PCIE接口的高速大容量数据采集.png 

图5 PCI ID配置


  直接使用Xilinx的VID和PID,Linux的PCIE Device驱动将根据此动态加载。


基于PCIE接口的高速大容量数据采集.png

图6 PCI BAR配置


  PCIE BAR是一组配置PCIE设备占用系统存储器资源的寄存器。对搭载32-bit Linux系统的ESM7100主板,使用2个32-bit BAR,其中BAR0用于数据采集DTaker,BAR1用于DMA控制器IP核。


基于PCIE接口的高速大容量数据采集.png 

图7 PCIE中断配置


  基于XC7A50T的PCIE/DMA IP可支持最多4路DMA通道,分别为2路发送(H2C通道)和2路接收(C2H通道),加上用户前端逻辑,共有至少5个中断源。采用PCIE的MSI中断机制是解决多中断源的最好方式,所以配置8个中断矢量,实际使用5个。


基于PCIE接口的高速大容量数据采集.png 

图8 PCIE中断配置


  尽管本设计仅需要1路C2H DMA通道,即可实现。但考虑到DMA Engine驱动的完整性,DMA通道还是按最大化配置。


基于PCIE接口的高速大容量数据采集.png 

图9 PCIE BAR0配置


  PCIE BAR0分成2个64KB段,一段用于控制DTaker模块,一段可以读取DMA的运行信息。


DTaker自定义IP


  自定义IP DTaker是由Vivado的<Create and Package New IP>工具创建的一个AXI外设,其基本配置占用8个32-bit寄存器和1路用户中断,如图10所示:


基于PCIE接口的高速大容量数据采集.png 

图10 DTaker展开图


  Vivado会自动生成与AXI-Lite接口的寄存器读写的verilog代码,只需在此基础上增添相应的数据通道接口信号及实现代码。数据通道的数据输入端,就是用户前端数据接口,对ETA750模块,就是AD7606C的并行接口,通过该接口启动AD芯片的转换并读取转换的数据;数据通道的输出端口就是符合AXI-Stream的C2H接口。DTaker需要做的工作,就是把多通道16-bit AD数据,打包成64-bit的AXI-Stream接口数据,并发送给PCIE/DMA。AXI总线的数据收发,采用的是一种简单高效的同步握手机制<TVALID, TREADY>,其中数据发方输出TVALID,告知数据有效,然后数据收方输出TREADY应答,双方在AXI_ACLK时钟下,检测到<TVALID, TREADY>同时有效时,锁存数据,并同时撤销各自的握手信号,从而完成一次数据的传输。典型的握手时序如图11所示,T3上升沿为数据锁存时刻:


基于PCIE接口的高速大容量数据采集.png

图11 AXI同步握手时序


  Xilinx提供的PCIE/DMA IP(DMA/Bridge Subsystem for PCI Express v4.1),尽管支持多个描述符的多段buffer级联传输,但本质上仍然是单次传输结构,即每次传输完成后,需要系统驱动干预,启动下一次传输,两次传输间就必然存在一个时间间隔。为了实现连续采集,同时避免两次传输间的数据丢失,需要在用户前端逻辑和PCIE/DMA的AXI-Stream C2H接口间插入一个FIFO缓冲buffer,这样就可得到DTaker的功能框图如图12所示:


基于PCIE接口的高速大容量数据采集.png

图12 DTaker功能框图


  从图12框图可见DTaker大致由4个部分组成:(1)AXI-Lite控制状态寄存器;(2)FIFO Reader,这是一个状态机(FSM)控制器,实现AXIS的同步握手,把FIFO的数据提交给C2H接口;(3)FIFO Writer,也是一个FSM控制器,实现用户逻辑接口(User_Logic_Interface),把数据写入FIFO,处理overflow错误;(4)用户前端逻辑单元,这部分与具体的数据采集应用相关。在具体的Verilog实现中,则分成2个层次,如图13所示:


基于PCIE接口的高速大容量数据采集.png

图13 DTaker Verilog代码层次


  在dtaker1_5_v1_5_S00_AXI.v中,实现了DTaker框图中的(1)~(3)以及FIFO,FIFO模块直接采用Xilinx的XPM库XPM_FIFO_ASYNC,同时调用用户逻辑模块。在ETA750中,用户逻辑模块由独立的文件user_logic_ad7606_module.v实现,在实际应用中,用户可以编写面向自己应用的前端模块文件,代替作为参考案例的user_logic_ad7606_module.v,就可很快完成整个硬件设计。用户逻辑模块通过user_logic_interface与上层模块交互数据的,用户逻辑模块必须遵守其规范,以下详细介绍user_logic_interface。


用户逻辑接口


  用户逻辑接口遵从AXI架构的同步握手原则,从应用角度来看的user_logic_interface的各个信号如下:


信号名称信号方向信号描述
AXI_ACLKinput全局时钟,62.5Mhz
AXI_ARESETNinput全局复位信号,低电平有效
UL_CLKinput用户专用时钟,50Mhz
UL_DAQ_CFG[31:0]input数据采集配置信息,用户自定义
UL_EVENT_CFG[31:0]input事件配置信息,用户自定义,通常用于触发配置
UL_DATA[63:0]output用户数据总线
UL_DATA_VALIDoutput数据有效标志,与UL_DATA_READY实现同步握手
UL_DATA_READYinput数据读取标志,与UL_DATA_VALID实现同步握手
UL_EVENT_VALIDoutput事件有效标志,与UL_EVENT_READY实现同步握手
UL_EVENT_READYinput事件读取标志,与UL_EVENT_VALID实现同步握手
UL_EVENT_CODE[2:0]output事件编码,与UL_EVENT_VALID同步读取


  用户逻辑接口中,数据同步握手时序是必须实现的,事件同步握手是可选实现的。在范例模块user_logic_ad7606_module.v中,已有两套同步握手的实现代码。其中<UL_DATA_VALID, UL_DATA_READY>这对握手线,除了在正常的数据传输中遵守图11所示的同步握手方式外,还有其特殊性,这是因为数据采集是按一定时间周期进行的,比如1Msps采样率,代表1us的采样周期,UL_DATA_VALID不可能一直有效,超过其采样周期,那就会造成数据丢失,因此要求上层逻辑(在ETA750中,由dtaker1_5_v1_5_S00_AXI.v实现)在UL_DATA_VALID由高变低后的ACLK上升沿,必须至UL_DATA_READY为高,从而结束本次传输周期,同时设置overflow错误标志。其时序如图14所示:


基于PCIE接口的高速大容量数据采集.png

图14 用户逻辑接口overflow时序


  在上层dtaker1_5_v1_5_S00_AXI.v模块中,调用低层用户逻辑模块的代码如下:


// user logic implemented in user_logic_ad7606_module.v
user_logic_ad7606 ad7606_m0(
// global signals
.AXI_ACLK(S_AXI_ACLK),
.AXI_ARESETN(S_AXI_ARESETN),    //!fifo_reset),      
    
// user logic interface for PCIE/DMA
.UL_DAQ_CFG(slv_reg3),
.UL_EVENT_CFG(slv_reg4),
.UL_DATA(daq_data),
.UL_DATA_VALID(daq_data_valid),
.UL_DATA_READY(daq_data_ready),
.UL_EVENT_VALID(daq_event_valid),
.UL_EVEN_READY(daq_event_ready),
.UL_EVENT_CODE(daq_event_code),
.UL_CLK(U_CLK),
 
// externel ad7606 interface, oriented to application
.AD7606_DATA(M_AD7606_DATA),
.AD7606_BUSY(M_AD7606_BUSY),
.AD7606_FIRSTDATA(M_AD7606_FIRSTDATA),
.AD7606_OS(M_AD7606_OS),
.AD7606_CSN(M_AD7606_CSN),
.AD7606_RDN(M_AD7606_RDN),
.AD7606_RESET(M_AD7606_RESET),
.AD7606_CONVST(M_AD7606_CONVST)
);

 

  实现用户逻辑接口(user_logic_interface)握手的时序控制包含在FIFO Writer中:


// fifo_writer: a 2-bit FSM, fill raw data into fifo
assign overflow_irq_en = slv_reg0[5];
assign event_irq_en = slv_reg0[6];
assign daqen = slv_reg0[8];
assign cpu_data_valid = slv_reg_wren & (axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] == 3'h7);
assign cpu_data = {slv_reg7, slv_reg6};
assign raw_data_valid = (!daqen & cpu_data_valid) | (daqen & daq_data_valid);
assign raw_data = daqen ? daq_data : cpu_data;
 
// fifo_writer: 3-bit FSM as data filler of FIFO with overflow engine
always @( posedge S_AXI_ACLK )
begin
  if ( (S_AXI_ARESETN == 1'b0) | fifo_wr_rst_busy)
    begin
      fifo_Q <= 0;
    end 
  else
    begin
      case(fifo_Q)
        S0: begin
              if(raw_data_valid & !fifo_full)
                fifo_Q <= S1;
              else if(raw_data_valid & fifo_full)
                fifo_Q <= S3;  
              else
                fifo_Q <= S0;   
            end
        S1: begin       // fifo_wr_en = 1
              fifo_Q <= S2;  
            end
        S2: begin       // ack: raw_data_ready = 1       
              fifo_Q <= S0;   
            end
        S3: begin       // wait fifo_full becomes false
              if(raw_data_valid & !fifo_full)
                fifo_Q <= S1;
              else if(!raw_data_valid)
                fifo_Q <= S4;
              else
                fifo_Q <= S3;   
            end
        S4: begin       // overflow processing       
              fifo_Q <= S2;   
            end
        default:
            begin
              fifo_Q <= S0;   
            end
      endcase
    end
end    
 
// overflow processing 
always @( posedge S_AXI_ACLK )
begin
  if ( S_AXI_ARESETN == 1'b0 )
    begin
      overflow_err <= 1'b0;
      overflow_isr <= 1'b0;
    end
  else if(slv_reg_wren & (axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] == 3'h1))
    begin
      overflow_err <= (overflow_err & (overflow_err ^ S_AXI_WDATA[3]));
      overflow_isr <= (overflow_isr & (overflow_isr ^ S_AXI_WDATA[5]));
    end
  else if(fifo_Q == S4)
    begin
      overflow_err <= 1'b1;
      overflow_isr <= overflow_irq_en;
    end
  else
    begin
      overflow_err <= overflow_err;
      overflow_isr <= overflow_isr; 
    end
end    
// end of overflow processing
 
// decode of fifo_writer FSM
assign fifo_wr_en = (fifo_Q == S1);
assign raw_data_ready = (fifo_Q == S2);
assign daq_data_ready = daqen & raw_data_ready;


  在用户模块user_logic_ad7606_module.v中实现的用户逻辑接口(user_logic_interface)的握手时序如下:


// control variables
assign running = UL_DAQ_CFG[0];
 
// user data provider: 3-bit FSM handshaker 
always @( posedge AXI_ACLK )
begin
  if ( (AXI_ARESETN == 1'b0) | !running )
    begin
      UQ <= 0;
    end 
  else
    begin
        case(UQ)
          S0: begin
                if(data_update_strobe)
                  UQ <= S1;
                else
                  UQ <= S0;   
              end
          S1: begin       // UL_DATA_VALID = 1, raw_data0
                if(UL_DATA_READY)
                  UQ <= S2;
                else if(!data_update_window)
                  UQ <= S7;
                else
                  UQ <= S1;   
              end
          S2: begin
                UQ <= S3;
              end
          S3: begin
                UQ <= S4;
              end
          S4: begin       // UL_DATA_VALID = 1,raw_data1
                if(UL_DATA_READY)
                  UQ <= S5;
                else if(!data_update_window)
                  UQ <= S7;
                else
                  UQ <= S4;   
              end
          S5: begin
                UQ <= S6;
              end
          S6: begin       // wait until data_update_strobe = 0       
                if(data_update_strobe)
                  UQ <= S6;
                else
                  UQ <= S0;   
              end
          S7: begin       // update window closed, wait ACK
                if(UL_DATA_READY)
                  UQ <= S6;
                else
                  UQ <= S7;   
              end
        endcase
      end
end    
 
  // decode of UQ
  assign user_data1_enable = (UQ == S3) | (UQ == S4) | (UQ == S5);
  assign user_data0 = {ch3_data, ch2_data, ch1_data, ch0_data};
  assign user_data1 = {ch7_data, ch6_data, ch5_data, ch4_data};
  assign UL_DATA_VALID = running & ((UQ == S1) | (UQ == S4));
  assign UL_DATA = user_data1_enable ? user_data1 : user_data0;
  // end of user provider


  在ETA750模块中,每个采样周期产生2个64-bit数据,因此会有两次传输握手过程。控制信号running来自数据采集配置寄存器的D0 bit。用户逻辑需要根据自身的情况产生信号data_update_strobe表示当前采集数据准备就绪,以及信号data_update_window,为数据可传输标志。用户逻辑接口user_logic_interface的有限状态机(3-bit FSM)的状态转换表如下所示:


译码动作当前状态下一状态转移条件

S0S1data_update_strobe = 1
S0Otherwise
UL_DATA_VALID = 1S1S2UL_DATA_READY = 1
S7data_update_window = 0
S1Otherwise

S2S3unconditional
raw_data1_enable = 1S3S4unconditional

UL_DATA_VALID = 1

raw_data1_enable = 1

S4S5UL_DATA_READY = 1
S7data_update_window = 0
S4Otherwise
raw_data1_enable = 1S5S6unconditional

S6S6data_update_strobe = 1
S0Otherwise

S7S6UL_DATA_READY = 1
S7Otherwise


小结


  按照本文FPGA的时序设计,理论最快数据传输速率为125MB/s,实际数据的传输能力还与所处的Linux平台处理能力有关。应用程序如何使用ETA750模块,实现实时连续的数据采集和处理,将在《基于PCIE接口的高速大容量数据采集——软件篇》一文中介绍。


  对大多数中低速数据采集的应用,可利用英创主板的精简ISA总线接口,就可相对容易地实现。只有真正对高速大容量采集有需求,才需考虑PCIE/FPGA这样的方案。因为本方案需要客户具备FPGA的设计能力,同时能准确描述应用程序的需求,才能够在本方案的基础上快速实现自己的应用方案。对本方案感兴趣的客户可邮件或电话咨询进一步的技术细节及合作事宜。


  英创公司技术支持:support@emtronix.com,028-86180660

[lablebox]