概述
异步FIFO电路结构
双端口SRAM:
用来存储上游节点写入的数据wdata,下游节点用rdata将其读出。
SRAM的读写地址采用了每次只递增1的机制,保证了写入和读出按顺序进行,写和读到最高地址后,重新返回零地址。
满信号生成电路
在上游节点和SRAM之间有一个满信号生成电路。
这个电路通过判断写时钟域下,写指针和读指针的关系,然后实时生成满信号wfull,以通知上游节点停止写操作。
空信号生成电路
在下游节点和SRAM之间有一个空信号生成电路,
这个电路通过判断读时钟域下,写指针和读指针的关系,然后实时生成空信号rempty,以通知下游节点停止读操作。
注意:
将读指针传递到写时钟域才能产生满信号,将写指针传递到读时钟域才能产生空信号,因此,这里就涉及到如何处理信号传输的亚稳态问题。
使用同步器把信号传递到对面的时钟域进行同步处理,可以采用同步器(由2~3级FF组成)对单bit的信号进行同步操作。
FIFO空满状态的判断:
判断读指针rptr和写指针wptr的关系(格雷码):
读指针和写指针在地址位中添加一个额外的位(extra bit)。
“空”状态的判断:
读指针和写指针二者完全相等(包括MSB)
读指针:rptr[n-1]
写指针:wptr[n-1]
empty=(rptr == wptr)
“满”的判断:
- wptr和同步过来的rptr的MSB不相等, 因为wptr必须比rptr多折回一次
- wptr与rptr的次高位不相等(如上图位置 7和位置15,转化为二进制对应的是 0111和1111,MSB不同说明多折回一 次, 111相同代表同一位置。)
- 剩下的其余位完全相等
读指针:rptr[n-1]
写指针:wptr[n-1]
full= (rptr[n-1] != wptr[n-1] )&&(rptr[n-2:0] == wptr[n-2:0])
二进制码和格雷码的转换:
二进制码转化为格雷码:
从最右边第一位开始,依次将每一位与左邻 一位异或(XOR),作为对应格雷码该位的值,最左边一位不变。
转换公式:
二进制码:B[n-1:0]
格雷码: G[n-1:0]
//二进制转换成格雷码
G[n-1] = B[n-1]
G[i] = B[i+1]^B[i] //i=0、1、2、、、n-2
//格雷码的第i位的值等于二进制第i位和i+1位的异或
例子:
格雷码转化为二进制码:
从左边第二位起,将每位与左边一位解码后的 值异或(XOR),作为该位解码后的值(最左边一位依然不变)。
转换公式:
二进制码:B[n-1:0]
格雷码: G[n-1:0]
//格雷码转换成二进制
G[n-1] = B[n-1]
B[i] = G[i]^B[i+1] //i=0、1、2、、n-2
例子:
注意“虚空”、“虚满”
假设,读指针rptr地址为3,在写时钟控制下,同步两个时钟进入写时钟域,即信号rptr_sync_2。
这时,在写时钟域下,刚好满足“满”的条件,生成满信号。
但是,此时rptr的值已经更新为5。
即在FIFO认为自己已经满的时候,读地址,又从里面读走了两个数据。实际FIFO并没有真正的满,只是接近满“虚满”。
结论:
- 对于full信号的生成机制,同步后的读地址一定是小于或者等于当前的 读地址,所以此时判断FIFO为满不一定是真满,这样更保守;
- Empty信号的机制同样成立, “空”时,不一定是真“空”
总结:
异步FIFO通过比较读写地址进行满空判断,但是读写地址属于不同的时钟域,所以在比较之前需要先将读写地址进行同步处理,此机制保证 了FIFO在空满极限情况下,依然留有余量,存在一定的冗余空间。
这种方法使得FIFO不会发生写满溢出、读空多读的情况。
异步FIFO的Verilog代码描述
空满标志生成
以写地址同步到读时钟域为例,示意图:
二进制码地址生成格雷码地址
//write interface pointer
//生成写地址指针
always@(posedge wr_clk or negedge wr_rst_n_i )
if(!wr_rst_n_i)
wr_ptr<= {ADDR_WIDTH+1}{1'b0};
else if(wr_en_i && !full_o)
wr_ptr <=wr_ptr +1'b1;
else
wr_ptr <=wr_ptr;
//##############二进制码->格雷码###############
//#######格雷码写地址
assign gray_wr_ptr = (wr_ptr>>1)^wr_ptr ;
always@(*)begin
gray_wr_ptr_next= gray_wr_ptr;
end
//--------------------------------------------------------------------------------
//read interface pointer
//生成读地址指针
always@(posedge rd_clk or negedge rd_rst_n_i )
if(!rd_rst_n_i)
rd_ptr <= {ADDR_WIDTH+1}{1'b0};
else if(rd_en_i && !empty_o)
rd_ptr <=rd_ptr +1'b1;
else
rd_ptr <=rd_ptr;
//##############二进制码->格雷码###############
//#######格雷码读地址
assign gray_rd_ptr = (rd_ptr>>1)^rd_ptr ;
always@(*)begin
gray_rd_ptr_next= gray_rd_ptr;
end
格雷码地址同步到对方地址
//格雷码写地址同步两级 传递到读时钟域
always@(posedge rd_clk or negedge rd_rst_n)
begin
if(! rd_rst_n)
begin
gray_wr2rd_ptr_1 <= {ADDR_WIDTH+1}{1'b0};
gray_wr2rd_ptr_2 <= {ADDR_WIDTH+1}{1'b0};
end
else
begin
gray_wr2rd_ptr_1 <= gray_wr_ptr_next;
gray_wr2rd_ptr_2 <= gray_wr2rd_ptr_1;
end
end
//格雷码读地址同步两级 传递到写时钟域
always@(posedge wr_clk or negedge wr_rst_n)
begin
if(!wr_rst_n)
begin
gray_rd2wr_ptr_1 <= {ADDR_WIDTH+1}{1'b0};
gray_rd2wr_ptr_2 <= {ADDR_WIDTH+1}{1'b0};
end
else
begin
gray_rd2wr_ptr_1 <= gray_rd_ptr_next;
gray_rd2wr_ptr_2 <= gray_rd2wr_ptr_1;
end
end
生成空满标志
//#####空满标志产生
//满标志
assign full_comb =( {~gray_rd2wr_ptr_2[ADDR_WIDTH],gray_rd2wr_ptr_2[ADDR_WIDTH-1:0]}== gray_wr_ptr );
always@(posedge wr_clk or negedge wr_rst_n)
begin
if(!wr_rst_n)
full_o <=1'b0;
else
full_o <= full_comb;
end
//空标志
assign empty_comb= (gray_wr2rd_ptr_2==gray_rd_ptr );
always@(posedge rd_clk or negedge rd_rst_n)
begin
if(!rd_rst_n)
empty_o <=1'b0;
else
empty_o <= empty_comb;
end
读写数据
读数据
//read data
always@(posedge rd_clk or negedge rd_rst_n_i)
if(!rd_rst_n_i)
begin
rd_data_o<= 0;
end
esle if(rd_en_i && !empty_comb)//读使能,且存储单元没空
rd_data_o <= RAM[rd_ptr];
写数据
//write data
interger i;
always@(posedge wr_clk or negedge wr_rst_n_i )
if(!wr_rst_n_i)
begin
for(i=0;i<FIFO_DEPTH;i=i+1)begin
RAM[i] <= 0;
end
end
esle if(wr_en_i && !full_comb)
RAM[wr_ptr] <= wr_data_i;
完整代码
module async_fifo
#(parameter DATA_WIDTH=32, parameter ADDR_WIDTH=3)
(
//write interface
input wire wr_clk,
input wire wr_rst_n_i,
input wire wr_en_i,
input wire [DATA_WIDTH-1:0] wr_data_i,
//read interface
input wire rd_clk,
input wire rd_rst_n_i,
input wire rd_en_i,
output reg [DATA_WIDTH-1:0] wr_data_o,
//flags
output full_o,
output empty_o
);
wire FIFO_DEPTH = 1<< ADDR_WIDTH;//2^3=8
// RAM definition
reg [DATA_WIDTH-1:0] RAM [0:FIFO_DEPTH-1];
//fifo width :DATA_WIDTH 32bit; fifo depth:FIFO_DEPTH 8
//读写指针
reg [ADDR_WIDTH:0] wr_ptr; //写指针
reg [ADDR_WIDTH:0] rd_ptr; //读指针
//写指针 二进制->格雷码
//格雷码写地址
reg [ADDR_WIDTH:0] gray_wr_ptr; //格雷码写地址
reg [ADDR_WIDTH:0] gray_wr_ptr_next;//格雷码写地址同步1拍
// 格雷码写地址同步到读时钟
reg [ADDR_WIDTH:0] gray_wr2rd_ptr_1; //格雷码写地址同步到读时钟同步1拍
reg [ADDR_WIDTH:0] gray_wr2rd_ptr_2; //格雷码写地址同步到读时钟同步2拍
//读地址 二进制->格雷码
//格雷码读地址
reg [ADDR_WIDTH:0] gray_rd_ptr; //格雷码读地址
reg [ADDR_WIDTH:0] gray_rd_ptr_next; //格雷码读地址同步1拍
//格雷码读地址同步到写时钟
reg [ADDR_WIDTH:0] gray_rd2wr_ptr_1;//格雷码读地址同步到写时钟同步1拍
reg [ADDR_WIDTH:0] gray_rd2wr_ptr_2;//格雷码读地址同步到写时钟同步2拍
//组合逻辑空满标志
wire full_comb;
wire empty_comb;
//########读写地址指针产生####################
//write interface pointer
always@(posedge wr_clk or negedge wr_rst_n_i )
if(!wr_rst_n_i)
wr_ptr<= {ADDR_WIDTH+1}{1'b0};
else if(wr_en_i && !full_o)
wr_ptr <=wr_ptr +1'b1;
else
wr_ptr <=wr_ptr;
//read interface pointer
always@(posedge rd_clk or negedge rd_rst_n_i )
if(!rd_rst_n_i)
rd_ptr <= {ADDR_WIDTH+1}{1'b0};
else if(rd_en_i && !empty_o)
rd_ptr <=rd_ptr +1'b1;
else
rd_ptr <=rd_ptr;
//##############二进制码->格雷码###############
//#######格雷码写地址
assign gray_wr_ptr = (wr_ptr>>1)^wr_ptr ;
always@(*)begin
gray_wr_ptr_next= gray_wr_ptr;
end
//格雷码写地址同步两级 传递到读时钟域
always@(posedge rd_clk or negedge rd_rst_n)
begin
if(! rd_rst_n)
begin
gray_wr2rd_ptr_1 <= {ADDR_WIDTH+1}{1'b0};
gray_wr2rd_ptr_2 <= {ADDR_WIDTH+1}{1'b0};
end
else
begin
gray_wr2rd_ptr_1 <= gray_wr_ptr_next;
gray_wr2rd_ptr_2 <= gray_wr2rd_ptr_1;
end
end
//#######格雷码读地址
//#######格雷码读地址
assign gray_rd_ptr = (rd_ptr>>1)^rd_ptr ;
always@(*)begin
gray_rd_ptr_next= gray_rd_ptr;
end
//格雷码读地址同步两级 传递到写时钟域
always@(posedge wr_clk or negedge wr_rst_n)
begin
if(!wr_rst_n)
begin
gray_rd2wr_ptr_1 <= {ADDR_WIDTH+1}{1'b0};
gray_rd2wr_ptr_2 <= {ADDR_WIDTH+1}{1'b0};
end
else
begin
gray_rd2wr_ptr_1 <= gray_rd_ptr_next;
gray_rd2wr_ptr_2 <= gray_rd2wr_ptr_1;
end
end
//#####空满标志产生
//满标志
assign full_comb =( {~gray_rd2wr_ptr_2[ADDR_WIDTH],gray_rd2wr_ptr_2[ADDR_WIDTH-1:0]}== gray_wr_ptr );
always@(posedge wr_clk or negedge wr_rst_n)
begin
if(!wr_rst_n)
full_o <=1'b0;
else
full_o <= full_comb;
end
//空标志
assign empty_comb= (gray_wr2rd_ptr_2==gray_rd_ptr );
always@(posedge rd_clk or negedge rd_rst_n)
begin
if(!rd_rst_n)
empty_o <=1'b0;
else
empty_o <= empty_comb;
end
//##########################################
//write data
interger i;
always@(posedge wr_clk or negedge wr_rst_n_i )
if(!wr_rst_n_i)
begin
for(i=0;i<FIFO_DEPTH;i=i+1)begin
RAM[i] <= 0;
end
end
esle if(wr_en_i && !full_comb)
RAM[wr_ptr] <= wr_data_i;
//read data
always@(posedge rd_clk or negedge rd_rst_n_i)
if(!rd_rst_n_i)
begin
rd_data_o<= 0;
end
esle if(rd_en_i && !empty_comb)//读使能,且存储单元没空
rd_data_o <= RAM[rd_ptr];
endmodule
参考
芯动力——硬件加速设计方法(3.2、3.3、3.4)
异步FIFO编程练习
练习网站:
牛客网-在线编程-Verilog练习
里面还有其他人总结的各种题解,可以自己总结下思路。写完代码可以在线运行,检查自己的代码哪里有问题。
最后
以上就是怡然书包为你收集整理的异步FIFO异步FIFO电路结构FIFO空满状态的判断: 异步FIFO的Verilog代码描述异步FIFO编程练习的全部内容,希望文章能够帮你解决异步FIFO异步FIFO电路结构FIFO空满状态的判断: 异步FIFO的Verilog代码描述异步FIFO编程练习所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复