上节课我们介绍了,同步fifo,感觉就是在双口异步RAM中进行了一些简单的外围操作,加了一些空满标志,内部用指针来进行寻址,从而取消了外部的地址接口。FIFO的一侧是读。一侧是写。所以具有了''wr_en"和"rd_en",一边是写数据,一边是读数据,所以就有了“wr_data”和“rd_data”,写会写满,读会读空所以具有了“empty”和“full”标志位。同步的fifo就是这么点东西。那么对于异步fifo 又该怎么样呢?
一、分析异步FIFO
由于是异步FIFO的设计,读写时钟不一样,在产生读空信号和写满信号时,会涉及到跨时钟域的问题。此处的跨时钟主要是读指针属于读时终域的,写指针属于写时钟域的。而异步FIFO的读写时钟是不同的。这种异步情况就会导致我们在处理空满标志的时候出现错误的判断。所以必须对其进行同步化处理后在进行空满标志的判断。同步化解决的方案是:加两级寄存器同步 + 格雷码(目的都是消除亚稳态)
1、加寄存器法
使用异步信号进行使用的时候,好的设计都会对异步信号进行同步处理,同步一般采用多级D触发器级联处理,如上图。这种模型大部分资料都说的是第一级寄存器产生亚稳态后,第二级寄存器稳定输出概率为90%,第三极寄存器稳定输出的概率为99%,如果亚稳态跟随电路一直传递下去,那就会另自我修护能力较弱的系统直接溃。转化为代码:
1 // 加两级寄存器同步 2 always @( sclk_rd )begin 3 if(!rd_rst)begin 4 wr_ptr_gray_d1 <= 0; 5 wr_ptr_gray_d2 <= 0; 6 end 7 else begin 8 wr_ptr_gray_d1 <= wr_ptr_gray ; 9 wr_ptr_gray_d2 <= wr_ptr_gray_d1 ; 10 end 11 end 12 13 always @( sclk_wr )begin 14 if(!wr_rst)begin 15 rd_ptr_gray_d1 <= 0 ; 16 rd_ptr_gray_d2 <= 0 ; 17 end 18 else begin 19 rd_ptr_gray_d1 <= rd_ptr_gray ; 20 rd_ptr_gray_d2 <= rd_ptr_gray_d1; 21 end 22 end
2、将一个二进制的计数值从一个时钟域同步到另一个时钟域的时候很容易出现问题,因为采用二进制计数器时所有位都可能同时变化,在同一个时钟沿同步多个信号的变化会产生亚稳态问题。而使用格雷码只有一位变化,因此在两个时钟域间同步多个位不会产生问题。所以需要一个二进制到gray码的转换电路,将地址值转换为相应的gray码,然后将该gray码同步到另一个时钟域进行对比,作为空满状态的检测。
1 assign wr_ptr_gray = wr_pointer ^(wr_pointer>>>1); 2 assign rd_ptr_gray = rd_pointer ^(rd_pointer>>>1); 二进制转换为格雷码。
3、在格雷码的域进行空满标志的判断
同步FIFO可以使用计数方式来判断空满,但是异步FIFO不能,因为写指针和读指针根本不在同一个时钟域,计数器无法处理这样的计数。那么如何对独处的数据和写入的数据进行判断。并给出空满标志呢?解决方案:对读写指针的位宽多添1位,这样可以在读写指针相等时,表示FIFO空,而在写指针和读指针最高位不同,而其他位相等时,也即写指针大于读指针一个FIFO深度的数值,表示FIFO满,这不就是意味着写指针绕了一圈,又追上了读指针了吗?恰是如此,用来解决不用计数而具体判断FIFO空满的问题。
1 //full 2 always @(posedge sclk_wr)begin 3 if(!wr_rst)begin 4 full <= 1'b0 ; 5 end 6 else if((rd_ptr_gray_d2[$clog2(DATA_DEPTH)-1:0]==wr_ptr_gray[$clog2(DATA_DEPTH)-1:0]) 7 && (rd_ptr_gray_d2[$clog2(DATA_DEPTH)]!=wr_ptr_gray[$clog2(DATA_DEPTH)]))begin 8 full <= 1'b1 ; 9 end 10 else 11 full <= 1'b0 ; 12 end 13 //empty 14 always @(posedge sclk_rd)begin 15 if(!rd_rst)begin 16 empty <= 1'b0 ; 17 end 18 else if(rd_ptr_gray==wr_ptr_gray_d2)begin 19 empty <= 1'b1 ; 20 end 21 else 22 empty <= 1'b0 ; 23 end
二、Verilog实现如下
1 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2 // Project Name : 3 // Website : https://home.cnblogs.com/lgy-gdeu/ 4 // Author : LGY GUET Uiversity 5 // Weixin : li15226499835 6 // Email : 15277385992@163.com 7 // File : 8 // Create : 2020 9 // Revise : 10 // Editor : sublime text{SUBLIME_VERSION}, tab size ({TABS}) 11 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 12 // Modification History: 13 // Date By Version Change Description 14 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 15 // {DATE} {TIME} LGY 1.0 ++++++++++++++++ 16 // ********************************************************************************* 17 `timescale 1ns/1ns 18 module asyn_fifo #( 19 parameter DATA_WIDTH = 8, 20 parameter DATA_DEPTH = 32 21 ) 22 23 ( 24 input wire sclk_wr , 25 input wire wr_rst , 26 input wire wr_en , 27 input wire[DATA_WIDTH-1:0] wr_data , 28 output reg full , 29 input wire sclk_rd , 30 input wire rd_en , 31 input wire rd_rst , 32 output reg [DATA_WIDTH-1:0] rd_data , 33 output reg empty 34 ); 35 36 //========================================================================\ 37 // ################ Define Parameter and Internal signals ################ 38 //========================================================================/ 39 reg [DATA_DEPTH-1:0] fifo_buffer [0:DATA_WIDTH-1] ;//space 40 reg [$clog2(DATA_DEPTH):0] wr_pointer ;//zhi zheng 41 reg [$clog2(DATA_DEPTH):0] rd_pointer ; 42 wire [$clog2(DATA_DEPTH):0] wr_ptr_gray ;//geleijishu 43 wire [$clog2(DATA_DEPTH):0] rd_ptr_gray ; 44 reg [$clog2(DATA_DEPTH):0] wr_ptr_gray_d1 ; 45 reg [$clog2(DATA_DEPTH):0] wr_ptr_gray_d2 ; 46 reg [$clog2(DATA_DEPTH):0] rd_ptr_gray_d1 ; 47 reg [$clog2(DATA_DEPTH):0] rd_ptr_gray_d2 ; 48 //============================================================================= 49 //+++++++++++++++++++++++++ Main Code +++++++++++++++++++++++++++++++ 50 //============================================================================= 51 //wr_pointer 52 always @(posedge sclk_wr)begin 53 if(!wr_rst)begin 54 wr_pointer <= 0 ; 55 end 56 else if(wr_en)begin 57 wr_pointer <= wr_pointer + 1'b1 ; 58 fifo_buffer[wr_pointer] <= wr_data ; 59 end 60 end 61 62 //rd_pointer 63 always @(posedge sclk_rd )begin 64 if(!rd_rst)begin 65 rd_pointer <= 0 ; 66 end 67 else if(rd_en)begin 68 rd_pointer <= rd_pointer+ 1'b1; 69 rd_data <= fifo_buffer[rd_pointer]; 70 end 71 72 end 73 //wr_ptr_gray,rd_ptr_gray 74 assign wr_ptr_gray = wr_pointer ^(wr_pointer>>>1); 75 assign rd_ptr_gray = rd_pointer ^(rd_pointer>>>1); 76 77 // 加两级寄存器同步 78 always @( sclk_rd )begin 79 if(!rd_rst)begin 80 wr_ptr_gray_d1 <= 0; 81 wr_ptr_gray_d2 <= 0; 82 end 83 else begin 84 wr_ptr_gray_d1 <= wr_ptr_gray ; 85 wr_ptr_gray_d2 <= wr_ptr_gray_d1 ; 86 end 87 end 88 89 always @( sclk_wr )begin 90 if(!wr_rst)begin 91 rd_ptr_gray_d1 <= 0 ; 92 rd_ptr_gray_d2 <= 0 ; 93 end 94 else begin 95 rd_ptr_gray_d1 <= rd_ptr_gray ; 96 rd_ptr_gray_d2 <= rd_ptr_gray_d1; 97 end 98 end 99 100 //full 101 always @(posedge sclk_wr)begin 102 if(!wr_rst)begin 103 full <= 1'b0 ; 104 end 105 else if((rd_ptr_gray_d2[$clog2(DATA_DEPTH)-1:0]==wr_ptr_gray[$clog2(DATA_DEPTH)-1:0]) 106 && (rd_ptr_gray_d2[$clog2(DATA_DEPTH)]!=wr_ptr_gray[$clog2(DATA_DEPTH)]))begin 107 full <= 1'b1 ; 108 end 109 else 110 full <= 1'b0 ; 111 end 112 //empty 113 always @(posedge sclk_rd)begin 114 if(!rd_rst)begin 115 empty <= 1'b0 ; 116 end 117 else if(rd_ptr_gray==wr_ptr_gray_d2)begin 118 empty <= 1'b1 ; 119 end 120 else 121 empty <= 1'b0 ; 122 end 123 124 125 126 endmodule
tb测试代码:
1 `timescale 1ns / 1ps 2 ////////////////////////////////////////////////////////////////////////////////// 3 // Company: 4 // Engineer: 5 // 6 // Create Date: 2020/06/27 22:23:25 7 // Design Name: 8 // Module Name: asyn_fifo_tb 9 // Project Name: 10 // Target Devices: 11 // Tool Versions: 12 // Description: 13 // 14 // Dependencies: 15 // 16 // Revision: 17 // Revision 0.01 - File Created 18 // Additional Comments: 19 // 20 ////////////////////////////////////////////////////////////////////////////////// 21 22 23 module asyn_fifo_tb(); 24 parameter DATA_WIDTH = 8; 25 26 parameter DATA_DEPTH = 16; 27 28 reg wr_clk; 29 reg wr_rst; 30 reg wr_en; 31 reg [DATA_WIDTH - 1 : 0] wr_data; 32 wire full; 33 34 reg rd_clk; 35 reg rd_rst; 36 reg rd_en; 37 wire [DATA_WIDTH - 1 : 0] rd_data; 38 wire empty; 39 40 initial begin 41 wr_clk = 0; 42 forever begin 43 #5 wr_clk = ~wr_clk; 44 end 45 end 46 47 initial begin 48 rd_clk = 0; 49 forever begin 50 #10 rd_clk = ~rd_clk; 51 end 52 end 53 54 initial begin 55 wr_rst = 0; 56 rd_rst = 0; 57 wr_en = 0; 58 rd_en = 0; 59 #30 60 wr_rst = 1; 61 rd_rst = 1; 62 63 //write data into fifo buffer 64 @(negedge wr_clk) 65 wr_data = $random; 66 wr_en = 1; 67 68 repeat(7) begin 69 @(negedge wr_clk) 70 wr_data = $random; // write into fifo 8 datas in all; 71 end 72 73 // read parts 74 @(negedge wr_clk) 75 wr_en = 0; 76 77 @(negedge rd_clk) 78 rd_en = 1; 79 80 repeat(7) begin 81 @(negedge rd_clk); // read empty 82 end 83 @(negedge rd_clk) 84 rd_en = 0; 85 86 //write full 87 # 150 88 89 @(negedge wr_clk) 90 wr_en = 1; 91 wr_data = $random; 92 93 repeat(15) begin 94 @(negedge wr_clk) 95 wr_data = $random; 96 end 97 98 @(negedge wr_clk) 99 wr_en = 0; 100 101 102 #50 $finish; 103 end 104 105 asyn_fifo #( 106 .DATA_WIDTH(DATA_WIDTH), 107 .DATA_DEPTH(DATA_DEPTH) 108 )asyn_fifo_inst ( 109 .sclk_wr (wr_clk), 110 .wr_rst (wr_rst), 111 .wr_en (wr_en), 112 .wr_data (wr_data), 113 .full (full), 114 .sclk_rd (rd_clk), 115 .rd_en (rd_en), 116 .rd_rst (rd_rst), 117 .rd_data (rd_data), 118 .empty (empty) 119 ); 120 endmodule
1、系统函数$clog2();求对数函数。使用方法很简单,主要求指针的位宽来使用。例如:
1 parameter DATA_WIDTH = 8; 2 parameter DATA_DEPTH = 8; 3 4 reg [DATA_WIDTH - 1 : 0] fifo_buffer[0 : DATA_DEPTH - 1]; 5 6 reg [$clog2(DATA_DEPTH) - 1 : 0] wr_pointer = 0; 7 reg [$clog2(DATA_DEPTH) - 1 : 0] rd_pointer = 0; 8 9 $clog2(DATA_DEPTH) // = 3; 10 11 reg [$clog2(DATA_DEPTH) - 1 : 0] wr_pointer = 0; 12 reg [$clog2(DATA_DEPTH) - 1 : 0] rd_pointer = 0;
2、综合属性控制资源使用
1 (*ram_style = "distributed"*) reg [DATA_WIDTH - 1 : 0] fifo_buffer[0 : DATA_DEPTH - 1]; 则使用分布式ram来搭建存储空间。
1 (*ram_style = "block"*) reg [DATA_WIDTH - 1 : 0] fifo_buffer[0 : DATA_DEPTH - 1]; 则使用块ram来搭建存储空间。
三、参考文献
1、https://www.cnblogs.com/ylsm-kb/p/9068449.html
2、https://so.csdn.net/so/search/s.do?q=%E5%BC%82%E6%AD%A5FIFO&t=blog&u=Reborn_Lee