ADC数据接入到AXI-Steam Interface

作者:叫什么好呢啊 来源:根究FPGA微信公众号

如果说要在AXI、AXI-Lite、AXI-Stream中选一种最喜欢的类型,我选择Stream总线,因为这是最简单的类型,而且使用起来非常方便,五个通道就剩数据传输,就像网络通信中的TCP与UDP,UDP用起来更简洁。

AXI4-Stream 不再有地址概念,而是一种点对点(或者一点对多点)数据流通信的协议。打个比方,AXI4 适合访问诸如 RAM 等有地址概念的存储介质,而 Stream 协议则适合访问诸如 FIFO 这样没有地址概念的存储介质。(AXI总线在FIXED模式下也可以实现该功能,就是有点麻烦)
没有了地址概念,自然也没有突发传输的概念。所有的数据都是点对点(点对多点)传输,可以理解为始终不断地对一个地址读写(或者是多个接收端设备各自的固定接收地址)。

如此方便的数据传输机制如何用于做ADC数据传输呢?
换个角度来说,也就是如何控制tvlalid和tready信号,以最近分析项目中的一部分为例:

在上层代码中接收AXI-Stream从机的tready信号,即从机准备好接收,使能ADC采集数据,通过上层模块的使能信号adc_capture_en_i控制主机(master的tvaild)信号。

接下来的设计中,就是通过发送使能信号adc_capture_en_i、adc数据有效信号adc_data_valid_i的控制,已经从机tready信号的控制,来控制master的tvalid信号,在上述三者有效的情况下将主机的tvalid信号置一,同时将数据放置到AXI-Stream写数据通道的数据总线上,跳转到下一状态。

在下一状态中,在每个时钟信号的上升沿对从机的ready信号和ADC数据的是否有效进行检查,即判断(s_axis_adc_tready&& adc_data_valid_i)信号组合,等到发送完指定数据的数据后将tlast信号拉高,同时跳转到下一状态。

接下来的状态中,第一个时钟周期肯定是要发送上个状态结束时的tlast信号,所以首先要对tready信号进行判断,在tready无效时需要等到tready有效,然后将数据和last信号传递给从机。else在进入该状态时检测到从机的ready信号为1说明数据和last信号已经成功传递给从机,所以可以直接将master的valid信号拉低,返回到idle状态,等到下一次的触发条件满足即可。

最终将数据传入到block design中的模块中:

转换代码:

module adc_to_axistream
(
  input            clk_i,
  input          reset_i,
  input            adc_capture_en_i,
  input[127:0]     adc_data_i,
  input            adc_data_valid_i,
  
  input              s_axis_adc_tready,
  output reg           s_axis_adc_tvalid,
  output reg[127:0]   s_axis_adc_tdata,
  output wire[15:0]   s_axis_adc_tkeep,
  output reg          s_axis_adc_tlast
    );
    
reg [15:0]  data_cnt;
reg[1:0]    state;

assign s_axis_adc_tkeep = 16'hffff;
always@(posedge clk_i)
 begin
     if(reset_i) begin
         s_axis_adc_tvalid <= 1'b0;
     s_axis_adc_tdata <= 128'd0;
         data_cnt <= 16'd0;
         s_axis_adc_tlast <= 1'b0;
         state <=0;
     end
     else begin
        case(state)
          0: begin
              if(adc_capture_en_i && adc_data_valid_i && s_axis_adc_tready) begin
                 s_axis_adc_tvalid <= 1'b1;
         s_axis_adc_tdata <= adc_data_i;
                 state <= 1;
              end
              else begin
                 s_axis_adc_tvalid <= 1'b0;
         s_axis_adc_tdata <= 128'd0;
                 state <= 0;
              end
            end
          1:begin
               if(s_axis_adc_tready && adc_data_valid_i) begin
           s_axis_adc_tdata <= adc_data_i;
           s_axis_adc_tvalid <= 1'b1;   
                   data_cnt <= data_cnt + 1'b1;
                   if(data_cnt == 16'd1021) begin
                      s_axis_adc_tlast <= 1'b1;
                      state <= 2;
                   end
                   else begin
                      s_axis_adc_tlast <= 1'b0;
                      state <= 1;
                   end
               end
               else begin
            s_axis_adc_tdata<= s_axis_adc_tdata;
          s_axis_adc_tlast <= 1'b0;
          s_axis_adc_tvalid <= 1'b0;
                  data_cnt <= data_cnt;                   
                  state <= 1;
               end
            end       
          2:begin
               if(!s_axis_adc_tready) begin
                  s_axis_adc_tvalid <= 1'b1;
          s_axis_adc_tdata<= s_axis_adc_tdata;
                  s_axis_adc_tlast <= 1'b1;
                  data_cnt <= data_cnt;
                  state <= 2;
               end
               else begin
                  s_axis_adc_tvalid 

推荐阅读