如果说要在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