相关文章:
1.Altera的单时钟同步FIFO,带almost_empty和almost_full端口https://blog.csdn.net/qq_39485231/article/details/105345164
2.Altera的单时钟同步FIFO,带empty和full端口https://blog.csdn.net/qq_39485231/article/details/105351146
3.Altera的异步FIFO,读写用同频不同相时钟https://blog.csdn.net/qq_39485231/article/details/105352597
4.Altera的异步FIFO学习心得https://blog.csdn.net/qq_39485231/article/details/105364241
Altera的异步FIFO,读写用同频不同相时钟
程序
module ip_fifo //顶层模块
(
input sys_clk,
input sys_rst_n
);
wire wrreq;
wire [7:0] data;
wire wrempty;
wire wrfull;
wire [7:0] wrusedw;
wire rdreq;
wire [7:0] q;
wire rdempty;
wire rdfull;
wire [7:0] rdusedw;
wire rst_n;
wire locked;
assign rst_n = sys_rst_n & locked;
pll_clk u_pll_clk
(
.areset (~sys_rst_n),
.inclk0 (sys_clk),
.c0 (clk_50m_wr),
.c1 (clk_50m_180deg_rd),
.locked (locked)
);
//例化FIFO模块
fifo u_fifo(
.wrclk ( clk_50m_wr ), // 写时钟
.wrreq ( wrreq ), // 写请求
.data ( data ), // 写入FIFO的数据
.wrempty ( wrempty ), // 写空信号
.wrfull ( wrfull ), // 写满信号
.wrusedw ( wrusedw ), // 写侧数据量
.rdclk ( clk_50m_180deg_rd ), // 读时钟
.rdreq ( rdreq ), // 读请求
.q ( q ), // 从FIFO输出的数据
.rdempty ( rdempty ), // 读空信号
.rdfull ( rdfull ), // 读满信号
.rdusedw ( rdusedw ) // 读侧数据量
);
//例化写FIFO模块
fifo_wr u_fifo_wr(
.clk (clk_50m_wr ), // 写时钟
.rst_n (rst_n), // 复位信号
.wrreq (wrreq ), // 写请求
.data (data ), // 写入FIFO的数据
.wrempty (wrempty ), // 写空信号
.wrfull (wrfull ) // 写满信号
);
//例化读FIFO模块
fifo_rd u_fifo_rd(
.clk (clk_50m_180deg_rd ), // 读时钟
.rst_n (rst_n), // 复位信号
.rdreq (rdreq ), // 读请求
.data (q ), // 从FIFO输出的数据
.rdempty (rdempty ), // 读空信号
.rdfull (rdfull ) // 读满信号
);
endmodule
module fifo_rd //读fifo模块
(
input clk,
input rst_n,
input [7:0] data,
input rdfull,
input rdempty,
output reg rdreq
);
reg [7:0] data_fifo;
reg [1:0] flow_cnt;
always @ (posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
rdreq <= 1'b0;
data_fifo <= 8'b0;
flow_cnt <= 2'd0;
end
else
begin
case(flow_cnt)
2'd0:begin
if(rdfull)
begin
rdreq <= 1'b1;
flow_cnt <= flow_cnt + 1'b1;
end
else
flow_cnt <= flow_cnt;
end
2'd1:begin
if(rdempty)
begin
rdreq <= 1'b0;
flow_cnt <= 2'b0;
data_fifo <= 8'd0;
end
else
begin
data_fifo <= data;
rdreq <= 1'b1;
end
end
default: flow_cnt <= 2'd0;
endcase
end
end
endmodule
module fifo_wr //写fifo模块
(
input clk,
input rst_n,
input wrfull,
input wrempty,
output reg [7:0] data,
output reg wrreq
);
reg [1:0] flow_cnt;
always @ (posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
wrreq <= 1'b0;
data <= 8'd11;
flow_cnt <= 2'd0;
end
else
begin
case(flow_cnt)
2'd0:begin
if(wrempty)
begin
wrreq <= 1'b1;
flow_cnt <= flow_cnt + 1'b1;
end
else
flow_cnt <= flow_cnt;
end
2'd1:begin
if(wrfull)
begin
wrreq <= 1'b0;
flow_cnt <= 2'd0;
data <= 8'd22;
end
else
begin
data <= data + 1'b1;
wrreq <= 1'b1;
end
end
default: flow_cnt <= 2'd0;
endcase
end
end
endmodule
`timescale 1ns/1ns // tb程序
module tb; // 测试模块
//parameter define
parameter T = 20; // 时钟周期为20ns
//reg define
reg sys_clk; // 时钟信号
reg sys_rst_n; // 复位信号
//wire define
//*****************************************************
//** main code
//*****************************************************
//给输入信号初始值
initial begin
sys_clk = 1'b0;
sys_rst_n = 1'b0; // 复位
#(T+1) sys_rst_n = 1'b1; // 在第21ns的时候复位信号信号拉高
end
//50Mhz的时钟,周期则为1/50Mhz=20ns,所以每10ns,电平取反一次
always #(T/2) sys_clk = ~sys_clk;
//例化led模块
ip_fifo u0 (
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n)
);
endmodule
1. FIFO为空,开始写入时
- 可以看到在wrreq持续一个T后,wrempty信号拉低,证明此时有数据写进了FIFO(和同步FIFO一样,写入会延时一个T完成),但是此时的wrusedw仍然为0,按理此时已经成功写入,该值应该为1,所以wrusedw的更新延后了实际FIFO的数据量一个写周期。
- 在FIFO已经写入了几个数据之后,rdempty才拉低,证明此时才能读出,所以数据在刚写入FIFO后,不能立马读出。
- 当rdempty拉低的时刻,按同步FIFO的理解,应该读端有一个数据,但rdusedw还是为0,一个读周期后才为1,所以和写端的wrusedw一样,读端的rdusedw也比实际的FIFO数据量晚一个读周期更新。
2. FIFO为满,开始读出时
- 当wrusedw为255时,wrfull信号就拉高,其实此时FIFO的数据量实际上是满的,因为上面有讲到wrusedw延后了实际写FIFO数据量一个写周期。
- 由于程序设计的原因,wrreq延后wrfull一个写周期,所以在写满后,仍然写了一个数据,但这个数据其实没有写入FIFO中。所以在以后程序设计中,当检查到wrfull拉高以后立马要停止其他模块向FIFO传数据,不立马停止会导致个别数据丢失
- 上面有讲到在写入数据,一段时间后才会在读端更新,所以rdusedw是晚于wrusedw到达最大值。
- 当rdusedw为255时,rdfull信号就拉高,其实此时FIFO的数据量实际上是满的,因为上面有讲到rdusedw延后了实际读FIFO数据量一个读周期。
3.总结
- 在用异步FIFO时,假如要用他的empty和full信号。那么在向FIFO写数据时,用wrempty和wrfull信号控制。当wrempty信号拉高时,此刻FIFO的写端正好没有数据量;当wrfull信号拉高时,此刻FIFO的写端正好写满了。如果用xxempty和xxfull信号信号控制读写FIFO,那么在xxempty和xxfull发生变化后就要立即停止其他模块与FIFO的数据互动,不然有些数据会没能写进FIFO或者读出无效FIFO数据!!!
- 在用异步FIFO时,假如要用他的empty和full信号。那么在从FIFO读数据时,用rdempty和rdfull信号控制。当rdfull信号拉高时,此刻FIFO的读端正好是满的;当rdempty信号拉高时,此刻FIFO的读端正好读空了。
- 无论是wrusedw还是rdusedw他们都不能表示写端和读端的数据量,他们与真实数据量之间,都有相应时钟域的一个时钟周期的延迟。即此刻wrusedw的值实际上是上一个写时钟周期时写端FIFO的数据量,rdusedw同理。
- 最可靠的使用异步FIFO还是通过他的xxusedw来控制他,当读时用wrusedw,当写时用rdusedw。虽然xxusedw不能表示真实的数据量,但是与真实数据量相差也很小,只要留出一定的阈值就不会出现数据没写进去或者读出无效数据的情况。