FIFO 本质是由 RAM 加上读写逻辑构成的先入先出的数据缓冲器。与 RAM 的区别是 FIFO 没有外部读写地址线,顺序写入顺序读出数据,其数据地址是由内部读写指针自增完成,因此 FIFO 在读写时不需要考虑读写冲突的问题。
根据 FIFO 工作的时钟域,可以分为同步 FIFO 和异步 FIFO,同步 FIFO 的读时钟和写时钟是同一个时钟,常用于两边数据位宽不同的临时缓冲,异步 FIFO 的读时钟和写时钟不一致,常用于数据信号跨时钟域处理。
时序图#
同步 fifo#
初始状态 empty
信号是高电平,此时 fifo 为空,若此时对 fifo 发起读操作,则读取到的数据无效。当 wr_en
拉高后,开始向 fifo 内部写入数据,fifo 中有数据后,empty
信号就会拉低。后面同时发起读写操作后,因为是同步 fifo,所以标志位不会发生变化。
只写不读时,fifo 中存在两个及以上的数据,此时 almost empty
也会拉低。当 fifo 处于写满状态时,当 fifo 只能接受一次只写不读操作时,almost full
将会拉高,最后,在没有进行读操作的情况下,再进行了一次写操作,full
信号就会被拉高,说明此时的 FIFO 已经写满了,在发出读请求之前将无法再写入任何数据,如果此时再写入数据,数据就会丢失。
AXIS FIFO#
时序图如下:
写入数据时,s_axis_tvalid
为高电平,同时 full
为低电平,取反后为 s_axis_tready
为高电平,此时才能写入数据。
读数据时同理,只有 valid
和 ready
信号同时拉高时才能读出数据。
m_axis_tvalid
对应 fifo
的 wr_en
信号,m_axis_tready
对应 fifo
的 full
信号取反,s_axis_tvalid
对应 fifo
的 empty
信号取反,s_axis_tready
对应 fifo
的 rd_en
。
AXIS fifo#
Axis fifo 的 IP 核界面如下:
本次是学习 FIFO 的异步读写,因此将 independent clock
选为‘是’,并且不使能 packet 模式。Bd 框图如下:
其中 clk_wiz
的输出有两个,端口 1 的时钟速率为 $100MHz$,端口 2 的时钟速率为 $50MHz$,axis_data_source
和 axis_dest
的源代码如下:
module axis_data_source (
input s_axis_clk,
input s_axis_rstn,
input m_axis_tready,
output [7:0] m_axis_tdata,
output m_axis_tvalid
);
reg [7:0] cnt;
always @(posedge s_axis_clk or negedge s_axis_rstn) begin
if(!s_axis_rstn) begin
cnt<=0;
end
else begin
if(m_axis_tready) begin
if(cnt==255) begin
cnt<=0;
end
else begin
cnt<=cnt+1'b1;
end
end
end
end
assign m_axis_tdata=cnt;
assign m_axis_tvalid=1'b1;
endmodule
/*read fifo data*/
module axis_dest (
input s_axis_clk,
input s_axis_tvalid,
input [7:0] s_axis_tdata,
output s_axis_tready
);
assign s_axis_tready=1'b1;
endmodule
其中 data_source
以 $100MHz$ 的速率生成并输出 $0-0xFF$,axis_dest
一直以 $50MHz$ 的速率读取数据。时序图如下:
可以看到开始时 FIFO 未被写满,此时写入和读出互不干扰,随着数据不断写入而不能被马上读出,在一段时间后 FIFO 的 full
信号拉高,对应 m_axis_tready
信号拉低,axis_data_source
不在生成新的数据,仿真图如下:
可以看到此时写入和读出的速率经过 valid 和 ready 信号的使能而同步。