上节课我们介绍了,同步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 :
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
View Code
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];
三、参考文献