异步FIFO的设计
使用格雷码 判断 是否是真的满员
用了很巧的办法
使用gray码解决了一个问题,但同时也带来另一个问题,即在格雷码域如何判断空与满。
对于“空”的判断依然依据二者完全相等(包括MSB);
而对于“满”的判断,如下图,由于gray码除了MSB外,具有镜像对称的特点,当读指针指向7,写指针指向8时,除了MSB,其余位皆相同,不能说它为满。因此不能单纯的只检测最高位了,在gray码上判断为满必须同时满足以下3条:
wptr和同步过来的rptr的MSB不相等,因为wptr必须比rptr多折回一次。
wptr与rptr的次高位不相等,如上图位置7和位置15,转化为二进制对应的是0111和1111,MSB不同说明多折回一次,111相同代表同一位置。
剩下的其余位完全相等。
解释一下 :
我们来看 7是 0_100 要是用原来的判别 我们会发现 下一个8 是 1_100 除了 第一位不同 但是很明显 我们并没有相差一圈
接下来 我们 观察 15-7=8 其实 我们正需要的是这样的8 其实是我们设置的3位宽所能提供的最大的 深度 而我们将比照 格雷码
才得出的首位相同 次高位不同 接下来都相同
源码asychronous.v
// asynchronous
module asyn #(
parameter WIDTH = 8 ,
parameter DEEPTH = 16 , // I just try 16
parameter ADDR_WIDTH = clogb2(DEEPTH)
// just give up PROG_EMPTY
)(
input wr_clk ,
input rd_clk ,
input wr_en ,
input rd_en ,
input wr_rst_n ,
input rd_rst_n ,
input [WIDTH-1 : 0 ] din ,
output reg wfull ,
output reg wempty ,
output reg [WIDTH-1 : 0 ] dout
);
// ===================================================================================//
// define parameter and internal signals //
//====================================================================================//
reg [WIDTH-1 : 0] ram[DEEPTH-1 : 0] ; // this is ram
reg [ADDR_WIDTH : 0] wr_addr ; // we must set more bit wode add exact bit + address
reg [ADDR_WIDTH : 0] rd_addr ; // we must set more bit wode
wire [ADDR_WIDTH - 1 : 0] wr_addr1 ;
wire [ADDR_WIDTH - 1 : 0] rd_addr1 ;
assign wr_addr1 = wr_addr[ADDR_WIDTH - 1 : 0] ; // we must put address and addr_pointer together
assign rd_addr1 = rd_addr[ADDR_WIDTH - 1 : 0] ;
// gray
wire [ADDR_WIDTH : 0] wr_addr_gray ;
wire [ADDR_WIDTH : 0] rd_addr_gray ;
// B 2 Gray code
assign wr_addr_gray = wr_addr ^ (wr_addr >> 1) ;
assign rd_addr_gray = rd_addr ^ (rd_addr >> 1) ;
//synchronous
reg [ADDR_WIDTH : 0] wr_addr_gray1 ;
reg [ADDR_WIDTH : 0] wr_addr_gray2 ;
//synchronous
reg [ADDR_WIDTH : 0] rd_addr_gray1 ;
reg [ADDR_WIDTH : 0] rd_addr_gray2 ;
//======================================================================================//
// next is main code //
//========================================================================================//
function integer clogb2 ;
input [31:0] value ;
begin
value = value - 1 ;
for( clogb2 = 0 ; value > 0 ; clogb2 = clogb2 + 1)
value = value >>1 ;
end
endfunction
//----------------------------------------------------------------- wr_addr
//--the pointer is another thing
always@(posedge wr_clk or negedge wr_rst_n)
begin
if(wr_rst_n == 0)
begin
wr_addr <= 0 ;
end
else if( wr_en && !wfull)
begin
wr_addr <= wr_addr + 1'b1 ;
end
else
wr_addr <= wr_addr ;
end
//----------------------------------------------------------------------- rd_addr
always@(posedge rd_clk or negedge rd_rst_n)
begin
if(rd_rst_n == 0)
begin
rd_addr <= 0;
end
else if( rd_en && !wempty)
begin
rd_addr <= rd_addr + 1'b1 ;
end
else
rd_addr <= rd_addr ;
end
//-------------------------------------------------------------------------- write to ram
// wr_addr1
always@(posedge wr_clk or negedge wr_rst_n)
begin
if(wr_rst_n == 0 )
begin
ram[wr_addr1] <= 0 ;
end
else if(wr_en && !wfull)
begin
ram[wr_addr1] <= din ;
end
else
begin
ram[wr_addr1] <= ram[wr_addr1] ;
end
end
//------------------------------------------------------------------------------read to ram
// rd_addr1 [ADDR_WIDTH-1 : 0] this is used to which need reed
always@(posedge rd_clk or negedge rd_rst_n)
begin
if(rd_rst_n == 0)
begin
dout <= 0 ;
end
else if( rd_en && !wempty)
begin
dout <= ram[rd_addr1] ;
end
else
begin
dout <= dout ;
end
end
//----------------------------------------------------------------------------------this is what sysnchronus need
//----------------------------------------------------------------------------------------------------
//-- next is change to gary and send to the same time clock //
//------------------------------------------------------------------------------------------------------\
//synchronous
//reg [ADDR_WIDTH : 0] wr_addr_gray1 ;
//reg [ADDR_WIDTH : 0] wr_addr_gray2 ;
//synchronous
//reg [ADDR_WIDTH : 0] rd_addr_gray1 ;
// reg [ADDR_WIDTH : 0] rd_addr_gray2 ;
// this is pointer point
// full use reed synchronous write
// empty use write synchronous read
//------------------------------------------------------------------------------------------------------\
// write pointer synchronous read clk ----- empty
always@(posedge rd_clk or negedge rd_rst_n)
begin
if( rd_rst_n == 0)
begin
wr_addr_gray1 <= 0 ;
wr_addr_gray2 <= 0 ;
end
else
begin
wr_addr_gray1 <= wr_addr_gray ;
wr_addr_gray2 <= wr_addr_gray1 ;
end
end
// read pointer sunchronous write clk ----- full
always@(posedge wr_clk or negedge wr_rst_n)
begin
if( wr_rst_n == 0)
begin
rd_addr_gray1 <= 0 ;
rd_addr_gray2 <= 0 ;
end
else
begin
rd_addr_gray1 <= rd_addr_gray ;
rd_addr_gray2 <= rd_addr_gray1 ;
end
end
//--------------------------------------------------- empty full determine
//empty
always@(*)
begin
if( rd_rst_n == 0 )
begin
wempty <= 0 ;
end
else if(rd_addr_gray == wr_addr_gray2)
begin
wempty <= 1 ;
end
else
begin
wempty <= 0 ;
end
end
// full
always@(*)
begin
if(wr_rst_n == 0)
begin
wfull <= 0 ;
end
else if( wr_addr_gray == {~rd_addr_gray2[ADDR_WIDTH:ADDR_WIDTH-1],rd_addr_gray2[ADDR_WIDTH-2 : 0] } )
begin
wfull <= 1 ;
end
else
begin
wfull <= 0 ;
end
end
endmodule
asychro_tb.v
`timescale 1ns/1ps
module asych_tb #(
parameter WIDTH = 8 ,
parameter DEEPTH = 8 ,
parameter ADDR_WIDTH = 3
);
reg wr_clk ;
reg rd_clk ;
reg wr_en ;
reg rd_en ;
reg wr_rst_n ;
reg rd_rst_n ;
reg [WIDTH-1 : 0 ] din ;
wire wfull ;
wire wempty ;
wire [WIDTH-1 : 0 ] dout ;
asyn#(
.WIDTH ( WIDTH ),
.DEEPTH ( DEEPTH ),
.ADDR_WIDTH ( ADDR_WIDTH )
)u_asyn(
.wr_clk ( wr_clk ),
.rd_clk ( rd_clk ),
.wr_en ( wr_en ),
.rd_en ( rd_en ),
.wr_rst_n ( wr_rst_n ),
.rd_rst_n ( rd_rst_n ),
.din ( din ),
.wfull ( wfull ),
.wempty ( wempty ),
.dout ( dout )
);
always #10 wr_clk = ~wr_clk ;
always #5 rd_clk = ~rd_clk ;
initial
begin
wr_clk = 0 ;
wr_rst_n = 1 ;
wr_en = 0 ;
rd_clk = 0 ;
rd_rst_n = 1 ;
rd_en = 0 ;
#10
wr_rst_n = 0;
rd_rst_n = 0;
#10
wr_rst_n = 1;
rd_rst_n = 1;
// next is write
wr_en = 1;
rd_en = 0;
//only write
wr_en = 1;
rd_en = 0;
din = 1 ;
repeat(10)
begin
@(negedge wr_clk)
begin
din = {$random}%30 ;
end
end
//only read
wr_en = 0;
rd_en = 1;
repeat(10)
begin
@(negedge rd_clk) ;
end
rd_en = 0 ;
//read and write
wr_en = 0;
rd_en = 0;
#80;
wr_en = 1;
rd_en = 1;
repeat(20) begin
@(negedge wr_clk) begin
din = {$random}%30;
end
end
end
endmodule