verilog异步FIFO外部读写时序分析与设计。
一、时序分析
1.在读写之前需要将使能信号端拉高;
2.1写时序:
写满信号为0,w_clk上升,将数据写入mem,地址指向下一个位置。
2.2读时序:
读空信号为0,先将当前地址数据读出,后r_clk上升,地址加一,等待下次读出数据。
二、接口声明
input rst;
input w_clk;
input [7:0] dat_in;
input w_en;
output reg wfull;//写满标志
input r_clk;
output [7:0] dat_out;
input r_en;
output reg rempty;//读空标志
二、跨时钟域同步
1.目的
在我们需要读数据或写数据时,我们需要判断FIFO中存储器里面有没有数据,或者数据已经写满。从而需要引出写满标志和读空标志信号,而这些信号的判断都是根据内部的读写指针来判断的。进行跨时钟域同步的目的就是考虑满和空标志能正确的表示,以保证系统能够正确的将数据写入与读出。
在此我们需要了解寄存器的亚稳态的知识(自行查阅),由于实际中寄存器电平跳转需要一定的延时,假设在写入第一个数据到FIFO中,我们需要保证数据完全写入并正确表示之后,才能将读空标志拉低,让读数据端将数据读出,那这个时候我们就需要进行一定的延时;同理在数据写满之后,我们在读数据的时候也要保证数据能够完全正确的读出,因此也需要一定的延时,
延时的方法就是再加两级寄存器,延时两个时钟周期。
2.思路
这部分可以参考链接: link这个博客
主要看转换格雷码跟亚稳态寄存器延时,不然代码看不懂的。
三、代码
my_fifo.v
//写数据时,先给dat_in,w_en拉高,w_clk上升沿数据采集写入,之后改变数据,等待下次写使能。
module my_fifo(rst,w_clk,dat_in,w_en,wfull,r_clk,dat_out,r_en,rempty);
input rst;
input w_clk;
input [7:0] dat_in;
input w_en;
output reg wfull;
input r_clk;
output [7:0] dat_out;
input r_en;
output reg rempty;
//变量声明
reg [5:0] wbin,rbin;//地址寄存器
wire [5:0] rbin_next,wbin_next,wbin_gray,rbin_gray;
reg [7:0] mem[31:0];
wire [4:0] w_addr,r_addr;//地址线
reg [5:0] wp,wp1,wp2,rp,rp1,rp2;
wire wfull_fla,rempty_fla;
assign dat_out = mem[r_addr]; //读数据
//写数据
always@(posedge w_clk)
begin
if(w_en & ~wfull_fla)
mem[w_addr] <= dat_in;
end
//写地址与指针控制
assign w_addr = wbin[4:0];
assign wbin_next = wbin + (w_en & !wfull_fla);
always@(posedge w_clk or posedge rst)
begin
if(rst)
begin
wbin <= 6'b0;
wp <= 6'b0;
end
else
begin
wbin <= wbin_next;
wp <= wbin_gray;
end
end
assign wbin_gray = wbin_next^(wbin_next >> 1);
//读指针同步到写时钟域
always@(posedge w_clk or posedge rst)
if(rst)
{rp2,rp1} <= 0;
else
{rp2,rp1} <= {rp1,rp};
//写满标志
assign wfull_fla = ((rp2[5:4] == ~wp[5:4]) && rp2[3:0] == wp[3:0]);
always@(posedge w_clk or posedge rst)
if(rst)
wfull <= 1'b0;
else
wfull <= wfull_fla;
//读地址与指针控制
assign r_addr = rbin[4:0];
assign rbin_next = rbin + (r_en & !rempty_fla);
always@(posedge r_clk or posedge rst)
begin
if(rst)
begin
rbin <= 6'b0;
rp <= 6'b0;
end
else
begin
rbin <= rbin_next;
rp <= rbin_gray;
end
end
assign rbin_gray = rbin_next^(rbin_next >> 1);
//写指针同步
always@(posedge r_clk or posedge rst)
if(rst)
{wp2,wp1} <= 0;
else
{wp2,wp1} <= {wp1,wp};
//读空标志
assign rempty_fla = (wp2 == rp);
always@(posedge r_clk or posedge rst)
if(rst)
rempty <= 1'b0;
else
rempty<= rempty_fla;
endmodule
my_fifo.vt(测试代码)
`timescale 1 ns/ 1 ns
module my_fifo_vlg_tst();
reg [7:0] dat_in;
reg r_clk;
reg r_en;
reg rst;
reg w_clk;
reg w_en;
// wires
wire [7:0] dat_out;
wire rempty;
wire wfull;
my_fifo i1 (
.dat_in(dat_in),
.dat_out(dat_out),
.r_clk(r_clk),
.r_en(r_en),
.rempty(rempty),
.rst(rst),
.w_clk(w_clk),
.w_en(w_en),
.wfull(wfull)
);
initial
begin
dat_in = 8'b1100_0101;
rst = 1;
w_clk = 0;
r_clk = 0;
w_en = 0 ;
r_en = 0;
#20 rst = 0;
//write
#20 w_en = 1;
r_en = 1;
end
always #10 w_clk = ~w_clk;
always #7 r_clk = ~r_clk;
endmodule