相关文章:
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,带almost_empty和almost_full端口
程序
module ip_fifo_a //顶层模块
(
input sys_clk,
input sys_rst_n
);
wire wrreq;
wire [7:0] data;
wire empty;
wire full;
wire [7:0] usedw;
wire almost_empty;
wire almost_full;
wire rdreq;
wire [7:0] q;
//例化FIFO模块
fifo u_fifo(
.clock (sys_clk),
.data (data),
.rdreq (rdreq),
.wrreq (wrreq),
.almost_empty (almost_empty),
.almost_full (almost_full),
.empty (empty),
.full (full),
.q (q),
.usedw (usedw)
);
//例化写FIFO模块
fifo_wr u_fifo_wr(
.clk (sys_clk ), // 写时钟
.rst_n (sys_rst_n), // 复位信号
.wrreq (wrreq ), // 写请求
.data (data ), // 写入FIFO的数据
.empty (almost_empty ), // 写空信号
.full (almost_full ) // 写满信号
);
//例化读FIFO模块
fifo_rd u_fifo_rd(
.clk (sys_clk ), // 读时钟
.rst_n (sys_rst_n), // 复位信号
.rdreq (rdreq ), // 读请求
.data (q ), // 从FIFO输出的数据
.empty (almost_empty ), // 读空信号
.full (almost_full ) // 读满信号
);
endmodule
module fifo_rd //读模块
(
input clk,
input rst_n,
input [7:0] data,
input full,
input empty,
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(full)
begin
rdreq <= 1'b1;
flow_cnt <= flow_cnt + 1'b1;
end
else
flow_cnt <= flow_cnt;
end
2'd1:begin
if(empty)
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 //写模块
(
input clk,
input rst_n,
input full,
input empty,
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'd0;
flow_cnt <= 2'd0;
end
else
begin
case(flow_cnt)
2'd0:begin
if(empty)
begin
wrreq <= 1'b1;
flow_cnt <= flow_cnt + 1'b1;
end
else
flow_cnt <= flow_cnt;
end
2'd1:begin
if(full)
begin
wrreq <= 1'b0;
flow_cnt <= 2'd0;
data <= 8'd0;
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; // 复位信号
//*****************************************************
//** 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_a u0 (
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n)
);
endmodule
单时钟同步FIFO,带almost_empty和almost_full端口
首先让Quartus根据我们的需要生成FIFO
现在来看一下almost_empty和almost_full的含义,almost_empty是usedw小于我们设定的50才会把almost_empty置位;almost_full是usedw大于等于我们设定的200才会把almost_full置位。区别是其中一个能取到等与。usedw指的是FIFO含有的数据量的多少。
1. almost_empty置位时
这里我们来看用ModelSim仿真的结果:
可以从图中发现当usedw小于50后,立马把almost_empty置位,与上面对almost_empty置位条件吻合。置位一个时钟周期后,wrreq被拉高,rdreq被拉低,这也与程序的定义吻合
所以可以看到在almost_empty拉高后,usedw仍然减小了,就是因为rdreq的延时变化造成的;在wrreq拉高后一个时钟周期usedw才增加1,所以数据写入FIFO其实会延后一个时钟周期。
2. almost_full置位时
可以从图中发现当usedw等于200后,立马把almost_full置位,与上面对almost_full置位条件吻合。置位一个时钟周期后,wrreq被拉低,rdreq被拉高,这也与程序的定义吻合。
所以可以看到在almost_full拉高后,usedw仍然增加了,就是因为wrreq的延时变化造成的;在rdreq拉高后一个时钟周期usedw才减少1,所以数据读出FIFO其实会延后一个时钟周期。
3. 总结
- 单时钟同步FIFO在读写时都有一个时钟周期延时,即wrreq置位后不能立马写入数据,一个T后,数据才会成功写进FIFO;当rdreq置位后不能立马读出数据,一个T后,数据才会成功从FIFO中读出,可以发现和RAM的机制类似。
- usedw和写入、读出数据是同步更新的,即每成功读出或写入一个数据,usedw也同步更新,usedw实时显示了FIFO的数据量。观察两个仿真图可以知道usedw在48-201之间变化,理论上是49-200之间变换,主要是程序设计wrreq和rdreq的延时变化造成的。
- almost_empty和almost_full是根据usedw的值来确定的。
- 在向FIFO写数据时,他的输出端q会保持最后一次输出的值。