作者:来源:根究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 <= 1'b0; s_axis_adc_tdata <= 128'd0; s_axis_adc_tlast <= 1'b0; data_cnt <= 16'd0; state <= 0; end end default: state <=0; endcase end end endmodule