Design of asynchronous FIFO
Use Gray code to determine whether it is really full
Used a very clever method
Using gray code solves one problem, but it also brings another problem, that is, how to judge empty and full in the Gray code domain.
The judgment of “empty” is still based on the fact that the two are completely equal (including MSB);
As for the judgment of “full”, as shown in the figure below, since the gray code has the characteristics of mirror symmetry except for the MSB, when the read pointer points to 7 and the write pointer points to 8, except for the MSB, the other bits are the same, so it cannot be said to be full. Therefore, we cannot simply detect the highest bit. To judge the gray code as full, the following three conditions must be met at the same time:
The MSBs of wptr and synchronized rptr are not equal because wptr must be folded back one more time than rptr.
The second highest bits of wptr and rptr are not equal. As shown in the figure above, positions 7 and 15 are converted into binary and correspond to 0111 and 1111. Different MSBs indicate one more fold, and the same 111 represents the same position.
The remaining bits are exactly equal.
explain :
Let’s see that 7 is 0_100. If we use the original judgment, we will find that the next 8 is 1_100 except that the first one is different, but it is obvious that we are not different from each other.
Next, we observe that 15-7=8. In fact, what we need is 8. In fact, it is the maximum depth that the 3-bit width we set can provide, and we will compare it with Gray code.
The first one we got is the same, the second highest one is different, and the next ones are the same.
Source code 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 wetty , 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 & amp; & amp; !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 & amp; & amp; !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 & amp; & amp; !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 & amp; & amp; !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 wetty <= 0 ; end else if(rd_addr_gray == wr_addr_gray2) begin wetty <= 1 ; end else begin wetty <= 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 full; wire wetty; 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}0; 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}0; end end end endmodule