校招Verilog——同步FIFO和异步FIFO

一、同步FIFO

1、代码

  1 //**************************************************************************
  2 // *** 名称 : sFIFO.v
  3 // *** 作者 : xianyu_FPGA
  4 // *** 博客 : https://www.cnblogs.com/xianyufpga/
  5 // *** 日期 : 2020年6月
  6 // *** 描述 : 同步FIFO,读写位宽相同,默认写先读后
  7 //**************************************************************************
  8 module sFIFO
  9 //========================< 参数 >==========================================
 10 #(
 11 parameter DATA_W            = 8                 ,   //数据位宽
 12 parameter ADDR_W            = 4                     //地址位宽
 13 )
 14 //========================< 端口 >==========================================
 15 (
 16 input                       clk                 ,   //时钟
 17 input                       rst_n               ,   //复位
 18 input                       wr_en               ,   //写使能
 19 input       [DATA_W-1:0]    wr_data             ,   //写数据
 20 input                       rd_en               ,   //读使能
 21 output  reg [DATA_W-1:0]    rd_data             ,   //读数据
 22 output                      full                ,   //写满
 23 output                      empty                   //读空
 24 );
 25 //========================< 信号 >==========================================
 26 reg     [ADDR_W-1:0]        wr_addr             ;   //写地址
 27 reg     [ADDR_W-1:0]        rd_addr             ;   //读地址
 28 reg     [DATA_W-1:0]        ram[2**ADDR_W-1:0]  ;   //ram,地址位宽4,共16个
 29 reg     [ADDR_W  :0]        cnt                 ;  //计数器
 30 //==========================================================================
 31 //==    读写地址
 32 //==========================================================================
 33 //-- 写地址
 34 //---------------------------------------------------
 35 always @(posedge clk or negedge rst_n) begin
 36     if(!rst_n) begin
 37         wr_addr <= 0;
 38     end
 39     else if(wr_en & (!full)) begin
 40         wr_addr <= wr_addr + 1;
 41     end
 42 end
 43 
 44 //-- 读地址
 45 //---------------------------------------------------
 46 always @(posedge clk or negedge rst_n) begin
 47      if (!rst_n) begin
 48           rd_addr <= 0;
 49      end
 50      else if(rd_en & (!empty)) begin
 51           rd_addr <= rd_addr + 1;
 52      end
 53 end
 54 //==========================================================================
 55 //==    读写数据
 56 //==========================================================================
 57 //-- 写数据
 58 //---------------------------------------------------
 59 integer i;
 60 always @(posedge clk or negedge rst_n) begin
 61     if(!rst_n) begin
 62         for(i=0; i<16; i=i+1)
 63             ram[i] <= 0;
 64     end
 65     else if(wr_en) begin
 66             ram[wr_addr] <= wr_data;
 67     end
 68 end
 69 
 70 //-- 读数据
 71 //---------------------------------------------------
 72 always @(posedge clk or negedge rst_n) begin
 73     if (!rst_n) begin
 74         rd_data<=0;
 75     end
 76     else if (rd_en) begin
 77         rd_data <= ram[rd_addr];  
 78     end
 79 end
 80 //==========================================================================
 81 //==    空满标志
 82 //==========================================================================
 83 //-- 辅助计数
 84 //---------------------------------------------------
 85 always @(posedge clk or negedge rst_n) begin
 86     if (!rst_n) begin
 87         cnt <= 0;
 88     end
 89     else if(wr_en && (!rd_en) && (cnt!=16)) begin
 90         cnt <= cnt + 1;
 91     end
 92     else if(rd_en && (!wr_en) && (cnt!=0)) begin
 93         cnt <= cnt - 1;
 94     end
 95 end
 96 
 97 //-- 写满、读空
 98 //---------------------------------------------------
 99 assign full  = (cnt==16);
100 assign empty = (cnt==0 );
101 
102 
103 endmodule

2、仿真

 1 `timescale 1ns/1ns
 2 module sFIFO_tb;
 3 //========================< 参数 >==========================================
 4 parameter DATA_W            = 8                 ;   //数据位宽
 5 parameter ADDR_W            = 4                 ;   //地址位宽
 6 //========================< 信号 >==========================================
 7 reg                         clk                 ;
 8 reg                         rst_n               ;
 9 reg                         wr_en               ;
10 reg    [DATA_W-1:0]         wr_data             ;
11 reg                         rd_en               ;
12 //==========================================================================
13 //==    例化
14 //==========================================================================
15 sFIFO
16 #(
17     .DATA_W                 (DATA_W             ),
18     .ADDR_W                 (ADDR_W             )
19 )
20 u_sFIFO
21 (
22     .clk                    (clk                ),
23     .rst_n                  (rst_n              ),
24     .wr_en                  (wr_en              ),
25     .wr_data                (wr_data            ),
26     .rd_en                  (rd_en              ),
27     .rd_data                (                   ),
28     .full                   (                   ),
29     .empty                  (                   )
30 );
31 //==========================================================================
32 //==    时钟
33 //==========================================================================
34 always #10 clk=~clk;
35 //==========================================================================
36 //==    设计
37 //==========================================================================
38 initial  begin
39     clk     = 1;
40     rst_n   = 0;
41     wr_en   = 0;
42     wr_data = 0;
43     rd_en   = 0;
44     
45     #101;
46     rst_n   = 1;
47     
48     #20;
49     gen_data;
50     
51     @(posedge clk);
52     rd_en=1;
53     
54     repeat(16)@(posedge clk);
55     rd_en=0;
56 end
57 
58 task gen_data;
59     integer i;
60     begin
61         for(i=0; i<16; i=i+1) begin
62             wr_en   = 1;
63             wr_data = i;
64             #20;
65         end
66         wr_en   = 0;
67         wr_data = 0;
68     end
69 endtask
70 
71 
72 endmodule

 

二、异步FIFO

1、分析

(1)格雷码

  比较空满时,需要读写地址进行判断,二者属于跨时钟域,需要进行打拍的同步处理,未避免亚稳态,采用格雷码,因为格雷码相邻只有一位变化,这样同步多位时更不容易产生问题。

格雷码公式:gray = (binary>>1) ^ binary;

(2)读空判断

  默认是先写后读,读追上了写,之后就是读空了。因此读空标志为【读写地址相同】。

(3)写满判断

  默认是先写后读,写在前面,超过了一轮地址后,又追上了读,之后就是写满了。因此写满标志也是【读写地址相同】吗?

  答案错误!显然,这样的思维会导致写满和读空的标志相同,无法确定【读写地址相同】时到底是写满还是读空,因此可以设置一个写指针 wr_addr_ptr 和 读指针 rd_addr_ptr,其位宽比读写地址多1位,整个指针的长度是地址的 2 倍。假设前半段为 A,后半段为 B。

  • 读在 A,最高位为0,剩余位为100;
  • 写在 B,最高位为1,剩余位为100;

   我们便可以判定,这时写越过了一轮,又到了读的位置,这便是真正的写满标志。提炼一下就是:“读写的最高位不同,其余位相同”时,处于写满状态。

   以上是当地址编码为普通二进制码时的分析,但是格雷码是不一样的,他的排列有些不同,前半段 A 和后半段 B 是镜像对称的,如下图所示:

  通过观察格雷码的特点,我们可以这样判断:“读写的最高2位不同,其余位相同”时,处于写满状态。

2、代码

  1 //**************************************************************************
  2 // *** 名称 : asFIFO.v
  3 // *** 作者 : xianyu_FPGA
  4 // *** 博客 : https://www.cnblogs.com/xianyufpga/
  5 // *** 日期 : 2020年6月
  6 // *** 描述 : 异步FIFO,读写位宽相同,默认写先读后
  7 //**************************************************************************
  8 module asFIFO
  9 //========================< 参数 >==========================================
 10 #(
 11 parameter DATA_W            = 8                 ,   //数据位宽
 12 parameter ADDR_W            = 4                     //地址位宽
 13 )
 14 //========================< 端口 >==========================================
 15 (
 16 input                       rst_n               ,   //复位
 17 //FIFO写 ----------------------------------------
 18 input                       wr_clk              ,   //写时钟
 19 input                       wr_en               ,   //写使能
 20 input       [DATA_W-1:0]    wr_data             ,   //写数据
 21 output                      wr_full             ,   //写满
 22 //FIFO读 ----------------------------------------
 23 input                       rd_clk              ,   //读时钟
 24 input                       rd_en               ,   //读使能
 25 output  reg [DATA_W-1:0]    rd_data             ,   //读数据
 26 output                      rd_empty                //读空
 27 );
 28 //========================< 信号 >==========================================
 29 reg     [ADDR_W  :0]        wr_addr_ptr         ;   //写指针,多1位
 30 reg     [ADDR_W  :0]        rd_addr_ptr         ;   //读指针,多1位
 31 wire    [ADDR_W  :0]        wr_addr_gray        ;   //写地址_格雷码
 32 reg     [ADDR_W  :0]        wr_addr_gray_r      ;   //写地址打拍
 33 reg     [ADDR_W  :0]        wr_addr_gray_rr     ;   //写地址打拍
 34 wire    [ADDR_W  :0]        rd_addr_gray        ;   //读地址_格雷码
 35 reg     [ADDR_W  :0]        rd_addr_gray_r      ;   //读地址打拍
 36 reg     [ADDR_W  :0]        rd_addr_gray_rr     ;   //读地址打拍
 37 //-----------------------------------------------
 38 wire    [ADDR_W-1:0]        wr_addr             ;   //写地址
 39 wire    [ADDR_W-1:0]        rd_addr             ;   //读地址
 40 reg     [DATA_W-1:0]        ram[2**ADDR_W-1:0]  ;   //ram,地址位宽4,共16个
 41 //==========================================================================
 42 //==    地址指针
 43 //==========================================================================
 44 always @(posedge wr_clk or negedge rst_n) begin
 45     if(!rst_n) begin
 46         wr_addr_ptr <= 0;
 47     end
 48     else if(wr_en & (!wr_full)) begin
 49         wr_addr_ptr <= wr_addr_ptr + 1;
 50     end
 51 end
 52 
 53 always @(posedge rd_clk or negedge rst_n) begin
 54     if(!rst_n) begin
 55         rd_addr_ptr <= 0;
 56     end
 57     else if(rd_en & (!rd_empty)) begin
 58         rd_addr_ptr <= rd_addr_ptr + 1;
 59     end
 60 end
 61 //==========================================================================
 62 //==    空满信号
 63 //==========================================================================
 64 //-- 格雷码转换
 65 //---------------------------------------------------
 66 assign wr_addr_gray = (wr_addr_ptr>>1) ^ wr_addr_ptr;
 67 assign rd_addr_gray = (rd_addr_ptr>>1) ^ rd_addr_ptr;
 68 
 69 //-- 跨时钟域,打两拍
 70 //---------------------------------------------------
 71 always @(posedge wr_clk) begin
 72     rd_addr_gray_r  <= rd_addr_gray;
 73     rd_addr_gray_rr <= rd_addr_gray_r;
 74 end
 75 
 76 always @(posedge rd_clk) begin
 77     wr_addr_gray_r  <= wr_addr_gray;
 78     wr_addr_gray_rr <= wr_addr_gray_r;
 79 end
 80 
 81 //-- 写满标志:高2位不同,其余位相同
 82 //---------------------------------------------------
 83 assign wr_full  = (wr_addr_gray == ({~rd_addr_gray_rr[ADDR_W-:2],rd_addr_gray_rr[ADDR_W-2:0]}));
 84 
 85 //-- 读空标志:读写地址相同
 86 //---------------------------------------------------
 87 assign rd_empty = (rd_addr_gray == wr_addr_gray_rr);
 88 //==========================================================================
 89 //==    ram读写
 90 //==========================================================================
 91 //-- 读写地址
 92 //---------------------------------------------------
 93 assign wr_addr = wr_addr_ptr[ADDR_W-1:0];
 94 assign rd_addr = rd_addr_ptr[ADDR_W-1:0];
 95 
 96 //-- 写数据
 97 //---------------------------------------------------
 98 integer i;
 99 always @(posedge wr_clk or negedge rst_n) begin
100     if(!rst_n) begin
101         for(i=0; i<2**ADDR_W; i=i+1)
102             ram[i] <= 0;
103     end
104     else if(wr_en & (!wr_full)) begin
105             ram[wr_addr] <= wr_data;
106     end  
107 end
108 
109 //-- 读数据
110 //---------------------------------------------------
111 always @(posedge rd_clk or negedge rst_n) begin
112     if(!rst_n) begin
113         rd_data <= 0;
114     end
115     else if(rd_en & (!rd_empty)) begin
116         rd_data <= ram[rd_addr];
117     end
118 end
119 
120 
121 endmodule

3、仿真

 1 `timescale 1ns/1ns
 2 module asFIFO_tb;
 3 //========================< 参数 >==========================================
 4 parameter DATA_W            = 8                 ;   //数据位宽
 5 parameter ADDR_W            = 4                 ;   //地址位宽
 6 //========================< 信号 >==========================================
 7 reg                         wr_clk              ;
 8 reg                         rd_clk              ;
 9 reg                         rst_n               ;
10 reg                         wr_en               ;
11 reg      [DATA_W-1:0]       wr_data             ;
12 reg                         rd_en               ;
13 //==========================================================================
14 //==    例化
15 //==========================================================================
16 asFIFO
17 #(
18     .DATA_W                 (DATA_W             ),
19     .ADDR_W                 (ADDR_W             )
20 )
21 u_asFIFO
22 (
23     .wr_clk                 (wr_clk             ),
24     .rd_clk                 (rd_clk             ),
25     .rst_n                  (rst_n              ),
26     .wr_en                  (wr_en              ),
27     .wr_data                (wr_data            ),
28     .rd_en                  (rd_en              ),
29     .rd_data                (                   ),
30     .wr_full                (                   ),
31     .rd_empty               (                   )
32 );
33 //==========================================================================
34 //==    时钟
35 //==========================================================================
36 always #10 wr_clk=~wr_clk;
37 always #5  rd_clk=~rd_clk;
38 //==========================================================================
39 //==    数据
40 //==========================================================================
41 initial begin
42     wr_clk  = 1;
43     rd_clk  = 0;
44     rst_n   = 0;
45     wr_en   = 0;
46     wr_data = 0;
47     rd_en   = 0;
48     
49     #101;
50     rst_n = 1;
51     
52     #20;
53     gen_data;
54     
55     @(posedge rd_clk);
56     rd_en = 1;
57     
58     repeat(16)@(posedge rd_clk);
59     rd_en=0;
60 end
61 
62 task gen_data;
63     integer i;
64     begin
65         for(i=0; i<16; i=i+1) begin
66             wr_en   = 1;
67             wr_data = i;
68             #20;
69         end
70         wr_en   = 0;
71         wr_data = 0;
72     end
73 endtask
74 
75 
76 endmodule

 

异步FIFO设计可以查看文章:异步FIFO设计原理与设计方法以及重要问题汇总(包含verilog代码|Testbench|仿真结果)

posted @ 2020-08-16 16:37  咸鱼IC  阅读(3126)  评论(3编辑  收藏  举报