Design and verification of asynchronous fifo

Keywords: C Verilog Single-Chip Microcomputer

The book continues from the above. The previous article introduced the slightly simple synchronous fifo, and then began the more complex asynchronous fifo.

1. Difference between synchronous fifo and asynchronous fifo

When there is only one clock in the design, whether all registers use the same one, there will be no transmission speed mismatch; However, when there are multiple clock signals in the design and data is transmitted in these clock domains, data loss may occur due to clock signal mismatch. At this time, asynchronous fifo needs to cache data to ensure normal data transmission. Therefore, generally asynchronous fifo will include a dual port ram

2. Asynchronous fifo composition

Figure 1. Asynchronous fifo

Module composition (6): top-level module, dual port RAM, 2 cross clock domain synchronous pointer modules, null judgment logic and full judgment logic.

3. Precautions

1) Avoid using binary counters to implement pointers, because if the current value is carried by one, all bits may change, and the error rate may increase. eg:0111 plus 1 will become 1000, and three of them will change, so the error rate will be increased.

2) To solve the above problems, it is recommended to directly use gray code counting.

3) Define more than one bit of the pointer's bit width. For example, suppose you want to design an asynchronous FIFO with a depth of 8. At this time, only 3 bits (2 ^ 3 = 8) are needed to define the read-write pointer. However, we design the pointer's bit width to 4 bits in the design. The function of the highest bit is to distinguish whether to read null or write full. Specific theory 1   As follows, when the highest bit is the same, the other bits are the same, it is considered to be read empty; When the highest bit is different and the other bits are the same, it is considered to be full

Design concept of gray code counter: Gray code to binary converter, adder, binary code to gray code converter, register for saving gray value

3. Corresponding module code segment

1) Top level module

```//************Top level module*************************
`timescale 1 ns/ 1 ps
module asyn_fifo_top #(
parameter DATA_WIDTH = 8,
parameter RAM_DEPTH = 16
)
(
input 		              WCLK,
input                         WRSTn,
input                         RCLK,
input                         RRSTn,
input                         write,
input  [ DATA_WIDTH - 1 : 0 ] wdata,
output [ DATA_WIDTH - 1 : 0 ] rdata,
output                        full,
output                        empty
);

wire [ ADDR_WIDTH : 0 ]    wpt;
wire [ ADDR_WIDTH : 0 ]    rpt;
wire [ ADDR_WIDTH : 0 ]    rp2_wpt;
wire [ ADDR_WIDTH : 0 ]    wp2_rpt;

double_ram#(.DATA_WIDTH(DATA_WIDTH),
.RAM_DEPTH(RAM_DEPTH))
ram_module (.WCLK(WCLK),
.write(write),
.wdata(wdata),
.rdata(rdata));
r2w_module (.WCLK(WCLK),
.WRSTn(WRSTn),
.rpt(rpt),
.rp2_wpt(rp2_wpt));
w2r_module (.RCLK(RCLK),
.RRSTn(RRSTn),
.wpt(wpt),
.wp2_rpt(wp2_rpt));
full_module(.WCLK(WCLK),
.WRSTn(WRSTn),
.write(write),
.rp2_wpt(rp2_wpt),
.wpt(wpt),
.full(full));
empty_module(.RCLK(RCLK),
.RRSTn(RRSTn),
.wp2_rpt(wp2_rpt),
.rpt(rpt),
.empty(empty));

endmodule```

3.2 RAM module

```//******************Dual interface RAM******************
module double_ram#(
parameter DATA_WIDTH = 8,
parameter RAM_DEPTH = 16
)
(
input                       WCLK,   //write clk
input 					    write,  //write en
input  [ DATA_WIDTH - 1:0 ] wdata,  //write data
output  [ DATA_WIDTH - 1:0 ] rdata   //read data
);

reg [ DATA_WIDTH - 1:0 ] RAM [ RAM_DEPTH - 1:0 ];  //double Port ram

always @ ( posedge WCLK )
begin
if ( write == 1'b1 )
begin
RAM [ waddr ] <= wdata;
end
else
begin
end
end

assign rdata = RAM [ raddr ];

endmodule
```

3) Judge full logic module

```//*****************Judge full logic****************
module full#(
)
(
input                           WRSTn,
input                           WCLK,
input                           write,
input      [ ADDR_WIDTH : 0 ]   rp2_wpt,
output reg [ ADDR_WIDTH : 0 ]   wpt,
output reg                      full
);

reg  [ ADDR_WIDTH :0 ] wbin;
wire [ ADDR_WIDTH :0 ] wbin_next;
wire [ ADDR_WIDTH :0 ] wgray_next;
wire                   full_reg;

always @ ( posedge WCLK or negedge WRSTn )
begin
if (!WRSTn)
begin
wpt <= 0;
wbin <= 0;
end
else
begin
wpt <= wgray_next;
wbin <= wbin_next;
end
end

assign wbin_next = ( !full ) ? ( wbin + write ) : wbin;
assign wgray_next = ( wbin_next >> 1 ) ^ wbin_next;

always @ ( posedge WCLK or negedge WRSTn )
begin
if (!WRSTn)
begin
full <= 0;
end
else
begin
full <= full_reg;
end
end

assign full_reg = ( wgray_next == { -rp2_wpt [ ADDR_WIDTH : ADDR_WIDTH - 1],rp2_wpt [ ADDR_WIDTH - 2:0]});

endmodule ```

4) Determine whether it is a null pointer

```//********************Judge whether it is empty logic************
module empty#(
)
(
input                           RRSTn,
input                           RCLK,
input      [ ADDR_WIDTH : 0 ]   wp2_rpt,
output reg [ ADDR_WIDTH : 0 ]   rpt,
output reg                      empty
);

reg  [ ADDR_WIDTH :0 ] rbin;
wire [ ADDR_WIDTH :0 ] rbin_next;
wire [ ADDR_WIDTH :0 ] rgray_next;
wire                   empty_reg;

always @ ( posedge RCLK or negedge RRSTn )
begin
if (!RRSTn)
begin
rpt <= 0;
rbin <= 0;
end
else
begin
rpt <= rgray_next;
rbin <= rbin_next;
end
end

assign rbin_next = ( !empty ) ? ( rbin + read ) : rbin;
assign rgray_next = ( rbin_next >> 1 ) ^ rbin_next;

always @ ( posedge RCLK or negedge RRSTn )
begin
begin
end

end

always @ ( posedge RCLK or negedge RRSTn )
begin
if (!RRSTn)
begin
empty <= 0;
end
else
begin
empty <= empty_reg;
end
end

assign empty_reg = ( rgray_next == wp2_rpt );

endmodule ```

5) Read write cross clock domain

```//****************Read write cross clock domain****************
module r2w_sync#(
)
(
input 						  WRSTn,    //write RSTn
input 					      WCLK,     //write CLK
input      [ ADDR_WIDTH : 0 ] rpt,      //output to write port gray
output reg [ ADDR_WIDTH : 0 ] rp2_wpt   //D trigger sync with two levels,second level
);

reg [ ADDR_WIDTH : 0 ] rp1_wpt;         //frist level
//reg [ ADDR_WIDTH : 0 ] rp2_wpt;

always @ ( posedge WCLK or negedge WRSTn )
begin
if ( !WRSTn )
begin
{ rp2_wpt,rp1_wpt } <= 0;
end
else
begin
{ rp2_wpt,rp1_wpt } <= { rp1_wpt,rpt };
end
end

endmodule```

6) Write read cross clock domain

```/**Write to Read Sync module**/
module w2r_sync#(
)
(
input 						  RCLK,     //reaf CLK
input      [ ADDR_WIDTH : 0 ] wpt,      //output to read port gray
output reg [ ADDR_WIDTH : 0 ] wp2_rpt   //D trigger sync with two levels,second level
);

reg [ ADDR_WIDTH : 0 ] wp1_rpt;         //frist level
//reg [ ADDR_WIDTH : 0 ] wp2_rpt;

always @ ( posedge RCLK or negedge RRSTn )
begin
if ( !RRSTn )
begin
{ wp2_rpt,wp1_rpt } <= 0;
end
else
begin
{ wp2_rpt,wp1_rpt } <= { wp1_rpt,wpt};
end
end

endmodule
```

4.tb

````timescale 1 ns/ 1 ps
module asyn_fifo_tb();

parameter DATA_WIDTH = 8;

reg                         WCLK;
reg                         WRSTn;
reg                         RCLK;
reg                         RRSTn;
reg                         write;
reg  [ DATA_WIDTH - 1 : 0 ] wdata;
wire [ DATA_WIDTH - 1 : 0 ] rdata;
wire                        full;
wire                        empty;
//integer                     i = 0;

asyn_fifo_top asyn_fifo(.WCLK(WCLK),
.WRSTn(WRSTn),
.RCLK(RCLK),
.RRSTn(RRSTn),
.write(write),
.wdata(wdata),
.rdata(rdata),
.full(full),
.empty(empty));
initial
begin
WCLK <= 0;
forever #100 WCLK = ~WCLK;
end

initial
begin
RCLK <= 0;
forever #200 RCLK = ~RCLK;
end

initial
begin
WRSTn = 0;
wdata = 0;
#100 WRSTn = 1;
end

initial
begin
RRSTn = 0;
#100 RRSTn = 1;
#10000;
\$finish();
end

always @ ( posedge WCLK or negedge WRSTn )
begin
wdata <= wdata + 1'b1;
end

//always  @(posedge WCLK or negedge WRSTn)
//begin
//      if(WRSTn==1'b0)
//      begin
//         i <= 0;
//      end
//      else if(!full)
//      begin
//         i = i+1;
//      end
//      else begin
//         i <= i;
//      end
//end

//always @ (*)
//begin
//    if (!full)
//        wdata = i;
//    else
//        wdata = 0;
//end

always @ ( full or WRSTn )
begin
if (!WRSTn)
begin
write <= 0;
end
else if (!full)
begin
write <= 1;
end
else
begin
write <= 0;
end
end

always @ ( empty or RRSTn )
begin
if (!RRSTn)
begin

end

else if (!empty)
begin
end
else
begin
end
end

initial begin
\$fsdbDumpfile("jacky");
\$fsdbDumpvars;
\$vcdpluson;
end

endmodule
```

2, Another design idea (from csdn:FPGA)

```//asyn_fifo code
module asyn_fifo #(
parameter data_width = 16;
parameter data_depth = 8;
parameter ram_depth = 256;
)
(
input       rst_n,

input                       wr_clk,         //Write clock domain signal
input                        wr_en,
input   [data_width-1:0]    data_in,
output                      full,

input                       rd_clk,         //Clock reading domain signal
input                       rd_en,
input   [data_width-1:0]    data_out,
output                      empty,
)

reg [data_depth:0]              wr_adr_ptr ;    //Defines the pointer used for comparison

wire [data_depth:0]             wr_adr_gray;    //Convert the pointer used for comparison to gray code
reg [data_depth:0]              wr_adr_gray1 ;  //Used in two shots
wire [data_depth:0]             rd_adr_gray;    //Convert the pointer used for comparison to gray code
reg [data_depth:0]              rd_adr_gray1 ;  //Used in two shots

//*********************Data read / write module defined in dual port ram******************
assign wr_adr  =  wr_adr_ptr[data_depth-1:0]    //Defines the relationship between the comparison pointer and the read / write pointer

integer i;                                      //Instantiate a RAM
reg [data_width-1:0]  ram_fifo [data_depth-1:0] ;
//Write in ram
always @ (posedge wr_clk or negedge rst_n)
begin
if(!rst_n)
begin                                   //Set all ram to zero
for(i;i<ram_depth;i=i+1)
ram_fifo[i] <= 'd0;
end
else if (wr_en && ~full)
else
end

always @ (posedge rd_clk or rst_n)
begin
if (!rst_n)
data_out <= 'd0;
else if (rd_en && ~empty)
else
data_out <= 'd0;
end
always @ (posedge wr_clk or !rst_n)
begin
if (!rst_n)
else if (wr_en && ~full)
else
end
always @ (posedge rd_clk or !rst_n)
begin
if (!rst_n)
else if (rd_en && ~empty)
else
end

//**********************Convert comparison pointer to gray code*******************

//*************************Compare the size of gray code************
//When judging whether it is empty, judge whether the converted gray codes are equal in the time reading clock field
//When judging whether it is full, compare the highest bits of gray code and the remaining bits in the write clock field

//Make two beats of the comparison gray code under the corresponding clock to synchronize the clock domain
always @ (posedge wr_clk or !rst_n)
begin
if (!rst_n)
begin
end
else begin
end
end
always @ (posedge rd_clk or !rst_n)
begin
if (!rst_n)
begin
end
else begin
end
end

//Judge empty and full

endmodule```

tb

```//testbench for asynchronous fifo
`timescale 1ns/1ps

module asyn_fifo_tb;

reg                 rst_n;

reg                 wr_clk;
reg                 wr_en;
reg     [15:0]      data_in;
wire                full;

reg                 rd_clk;
reg                 rd_en;
reg     [15:0]      data_out;
wire                empty;

asyn_fifo asyn_fifo_inst
(
.rst_n      (rst_n),

.wr_clk     (wr_clk),
.wr_en      (wr_en),
.data_in    (data_in),
.full       (full),

.rd_clk     (rd_clk),
.rd_en       (rd_en),
.data_out       (data_out),
.empty      (empty)
)

//Define clock
initial begin
wr_clk = 0;
forever #10 wr_clk = ~wr_clk;
end

initial begin
rd_clk = 0 ;
forever  #30 rd_clk = ~rd_clk;
end

// Define input data_in
always @ (posedge wr_clk or negedge  rst_n)
begin
if (!rst_n)
data_in <= 'd0;
else if (wr_en)
data_in <= data_in + 1'b1;
else
data_in <= data_in;
end
//Define other input signals

initial begin
rst_n = 0;
wr_en =0;
rd_en = 0;
#200
rst_n = 1;
wr_en = 1;
#20000
wr_en = 0;
rd_en = 1;
#20000
rd_en = 0;

\$stop
end
endmodule```

Posted by Sooz719 on Tue, 30 Nov 2021 12:26:46 -0800