赞
踩
设计一个读写时钟不同的异步FIFO。
FIFO设计的重难点是如何产生空满标志,在之前的同步FIFO设计中采用的是将地址扩展一位,通过比较最高位以及其他低位来得到空满标志,当最高位相反低位相同时说明写指针追了读指针一圈,FIFO写满;当所有位相同时,已将FIFO内所有数据读出,此时为空。
而异步FIFO也将采取这种思路,但是由于涉及到写指针在写时钟域下产生,读指针在读时钟域产生,比较时我们需要将两者放在一个时候域下进行比较,那此时就分为如下两种情况:
1.在读时钟域下读写指针比较,产生empty标志;
2.在写时钟域下读写指针比较,产生empty标志;
这么做的原因在于,将写指针同步到读时钟域后,此时和读指针比较其实已经不是现在实时的写指针了,意味着此时同步过来的写指针可能是5,而实际的已经变为了7,但是进行比较之后如果读指针也恰为5则产生empty,相当于更加“保险”,避免了读“空”的情况。将读指针同步到写时钟域下,读指针会滞后实际值几个周期,但这样避免了写满之后再写的情况。
考虑到多bit数据(即异步FIFO中的读写地址)跨时钟域时出现亚稳态概率高,再由于读写地址每次加1为连续变化,因此在将读写地址同步到另一个时钟域时先对其转换变为格雷码,因为格雷码每次只变化一位,可以有效改善亚稳态情况。
所以设计过程一共分为如下几个部分:
①读写地址产生;
②读写地址二进制转格雷码;
③读写地址同步到对方时钟域;
④空满标志判断;
信号 | 方向 | 说明 |
---|---|---|
w_ck | in | 写时钟 |
r_clk | in | 读时钟 |
rst_n | in | 复位信号 |
wr_en | in | 写使能 |
rd_en | in | 读使能 |
w_data[7:0] | out | 8bit写数据 |
r_data[7:0] | out | 8bit读数据 |
full | out | 满信号 |
empty | out | 空信号 |
module asy_fifo
#(
parameter WIDTH = 8,
parameter DEPTH = 16
)
(
input w_clk,
input r_clk,
input rst_n,
input wr_en,
input rd_en,
input [WIDTH-1:0] w_data,
output reg [WIDTH-1:0] r_data,
output full,
output empty
);
localparam addr_width = $clog2(DEPTH);
reg w_rst_n;
reg r_rst_n;
reg r_ff1;
reg w_ff1;
reg [WIDTH-1:0] fifo_data[DEPTH-1:0];
wire [addr_width-1:0] waddr;
reg [addr_width:0] waddr_bin;
wire [addr_width:0] waddr_gray;
reg [addr_width:0] waddr_gray_f1;
reg [addr_width:0] waddr_gray_f2;
//reg [addr_width:0] waddr_bin_2;
//复位信号处理
always @(posedge w_clk or negedge rst_n) begin
if(~rst_n) begin
{w_rst_n,w_ff1} <= 2'b0;
end else begin
{w_rst_n,w_ff1} <= {w_ff1,1'b1};
end
end
always @(posedge w_clk or negedge rst_n) begin
if(~rst_n) begin
{r_rst_n,r_ff1} <= 2'b0;
end else begin
{r_rst_n,r_ff1} <= {r_ff1,1'b1};
end
end
assign waddr = waddr_bin[addr_width-1:0];
//写地址产生
always @(posedge w_clk or negedge w_rst_n) begin
if(~w_rst_n) begin
waddr_bin <= 0;
end else if(wr_en & ~full)begin
waddr_bin <= waddr_bin + 1'b1;
end
end
//写地址转换成格雷码
assign waddr_gray = waddr_bin ^ (waddr_bin>>1);
//同步处理
always @(posedge r_clk or negedge r_rst_n) begin
if(~r_rst_n) begin
waddr_gray_f1 <= 0;
waddr_gray_f1 <= 0;
end else begin
waddr_gray_f1 <= waddr_gray;
waddr_gray_f2 <= waddr_gray_f1;
end
end
wire [addr_width-1:0] raddr;
reg [addr_width:0] raddr_bin;
wire [addr_width:0] raddr_gray;
reg [addr_width:0] raddr_gray_f1;
reg [addr_width:0] raddr_gray_f2;
//reg [addr_width-1:0] raddr_bin_2;
assign raddr = raddr_bin[addr_width-1:0];
//读地址产生
always @(posedge r_clk or negedge r_rst_n) begin
if(~r_rst_n) begin
raddr_bin <= 0;
end else if(rd_en & ~empty)begin
raddr_bin <= raddr_bin + 1'b1;
end
end
//读地址转换成格雷码
assign raddr_gray = raddr_bin ^ (raddr_bin>>1);
//同步处理
always @(posedge w_clk or negedge w_rst_n) begin
if(~w_rst_n) begin
raddr_gray_f1 <= 0;
raddr_gray_f2 <= 0;
end else begin
raddr_gray_f1 <= raddr_gray;
raddr_gray_f2 <= raddr_gray_f1;
end
end
//产生空满标志
assign empty = (waddr_gray_f2 == raddr_gray)? 1'b1:1'b0;
assign full = (raddr_gray_f2[addr_width:addr_width-1] == ~waddr_gray[addr_width:addr_width-1] && raddr_gray_f2[addr_width-2:0]==waddr_gray[addr_width-2:0] )? 1'b1:1'b0;
//写数据
integer i;
always @(posedge w_clk or negedge w_rst_n) begin
if(~w_rst_n) begin
for(i=0;i<DEPTH;i=i+1)
fifo_data[i] <= 0;
end
else if(wr_en & (~full)) begin
fifo_data[waddr] <= w_data;
end
end
//读数据
always @(posedge r_clk or negedge r_rst_n) begin
if(~r_rst_n) begin
r_data <= 0;
end else if(rd_en & (~empty)) begin
r_data <= fifo_data[raddr];
end
end
endmodule
module tb_ast_fifo();
reg w_clk;
reg r_clk;
reg rst_n;
reg wr_en;
reg rd_en;
reg [7:0] w_data;
wire [7:0] r_data;
wire full;
wire empty;
always#15 w_clk = ~w_clk;
always#10 r_clk = ~r_clk;
always#30 w_data = $random;
initial begin
rst_n = 0;
w_clk = 0;
r_clk = 0;
#10 rst_n =1;
#40 wr_en = 1;
rd_en = 0;
#500 rd_en = 1;
#2000 $stop;
end
asy_fifo #(
.WIDTH(8),
.DEPTH(16)
) inst_asy_fifo (
.w_clk (w_clk),
.r_clk (r_clk),
.rst_n (rst_n),
.wr_en (wr_en),
.rd_en (rd_en),
.w_data (w_data),
.r_data (r_data),
.full (full),
.empty (empty)
);
endmodule
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。