zoukankan      html  css  js  c++  java
  • 异步FIFO

    学习文章:

    1、https://www.cnblogs.com/aslmer/p/6114216.html

    2、https://www.cnblogs.com/streetlive/p/12872619.html

    3、https://blog.csdn.net/dongdongnihao_/article/details/79873555

    原理分析:

    FIFO(First In First Out)先进先出,从字面意思就是只能够顺序写入数据,顺序读出数据,就不能够像普通存储器一样由地址线决定写入或读取某个指定地址。同样,不同宽度的数据接口也可以用FIFO。异步FIFO的本质是RAM,只不过就是读写操作由不同的时钟来控制。

    同步FIFO是指读写时钟为同一个时钟,异步FIFO是指读写时钟不一致,读写时钟相互独立的。

    FIFO的宽度是指FIFO一次读写操作的数据位,FIFO的深度是指FIFO可以存储多少个这样宽度的数据。

    如何解决跨时钟?

    处理跨时钟域的数据有单bit和多bits之分,而打两拍的方式常见于处理单bit数据的跨时钟域问题。打两拍本质就是定义两级寄存器对数据进行延迟。比如读指针同步到写时钟域,原理如图:

     代码实现:

    如何判断写满w_full?


     代码实现:

    综合代码:

    module fifo
    #(
    parameter width=8,
    parameter depth=4
    )
    (
    input w_clk,
    input w_rst_n,
    input r_clk,
    input r_rst_n,
    input w_en,
    input r_en,
    input [width-1:0]w_data,
    output [width-1:0]r_data,
    output reg w_full,
    output reg r_empty
    );
    
    wire [depth-1:0]w_addr,r_addr;//为什么地址这里是wire型
    
    reg [width-1:0]ram[0:(1<<depth)-1];
    reg [depth:0]rptr;//读指针
    reg [depth:0]rptr_to_wclk_1,rptr_to_wclk_2;//同步到写时钟域的读指针
    reg [depth:0]wptr;//写指针
    reg [depth:0]wptr_to_rclk_1,wptr_to_rclk_2;//同步到读时钟域的写指针
    
    assign r_data=ram[r_addr];
    always @(posedge w_clk)
    if(w_en && !w_full)
    ram[w_addr]<=w_data;
    
    //读指针同步到写时钟域,需要打两拍
    always @(posedge w_clk or negedge w_rst_n)
    if(!w_rst_n)begin
    rptr_to_wclk_1<=5'd0;
    rptr_to_wclk_2<=5'd0;
    end
    else begin
    rptr_to_wclk_1<=rptr;
    rptr_to_wclk_2<=rptr_to_wclk_1;
    end
    
    //写指针同步到读时钟域,需要打两拍
    always @(posedge r_clk or negedge r_rst_n)
    if(!r_rst_n)begin
    wptr_to_rclk_1<=5'd0;
    wptr_to_rclk_2<=5'd0;
    end
    else begin
    wptr_to_rclk_1<=wptr;
    wptr_to_rclk_2<=wptr_to_rclk_1;
    end
    
    //生成r_empty
    reg [depth:0]r_binary;
    wire [depth:0]r_gray,r_binary_next;
    always @(posedge r_clk or negedge r_rst_n)
    if(!r_rst_n)begin
    r_binary<=5'd0;
    rptr<=5'd0;
    end
    else begin
    r_binary<=r_binary_next;
    rptr<=r_gray;
    end
    assign r_addr=r_binary[depth-1:0];
    assign r_binary_next=r_binary + (r_en & ~r_empty);
    assign r_gray=(r_binary_next>>1) ^ r_binary_next;
    assign r_empty_t=(r_gray == wptr_to_rclk_2);
    always @(posedge r_clk or negedge r_rst_n)
    if(!r_rst_n)
    r_empty<=1'b1;
    else
    r_empty<=r_empty_t;
    
    //生成w_full
    reg [depth:0]w_binary;
    wire [depth:0]w_gray,w_binary_next;
    always @(posedge w_clk or negedge w_rst_n)
    if(!w_rst_n)begin
    w_binary<=5'd0;
    wptr<=5'd0;
    end
    else begin
    wptr<=w_gray;
    w_binary<=w_binary_next;
    end
    assign w_addr=w_binary[depth-1:0];
    assign w_binary_next=w_binary + (w_en & ~w_full);
    assign w_gray=(w_binary_next>>1) ^ w_binary_next;
    assign w_full_t=(w_gray == {~rptr_to_wclk_2[depth:depth-1],rptr_to_wclk_2[depth-2:0]});
    always @(posedge w_clk or negedge w_rst_n)
    if(!w_rst_n)
    w_full<=1'b0;
    else
    w_full<=w_full_t;
    
    endmodule 

    仿真代码:

    `timescale 1ns/1ns
    module fifo_tb;
    reg w_clk;
    reg w_rst_n;
    reg r_clk;
    reg r_rst_n;
    reg w_en;
    reg r_en;
    reg [7:0]w_data;
    wire [7:0]r_data;
    wire w_full;
    wire r_empty;
    
    fifo u_fifo(
    .w_clk (w_clk),
    .w_rst_n (w_rst_n),
    .r_clk (r_clk),
    .r_rst_n (r_rst_n),
    .w_en (w_en),
    .r_en (r_en),
    .w_data (w_data),
    .r_data (r_data),
    .w_full (w_full),
    .r_empty (r_empty)
    );
    
    //生成时钟
    initial begin
    w_clk=0;
    r_clk=0;
    end
    always #10 w_clk=~w_clk;
    always #20 r_clk=~r_clk;
    
    //产生复位信号
    initial begin
    w_rst_n=0;
    r_rst_n=0;
    #60;
    w_rst_n=1;
    r_rst_n=1;
    end
    
    always @(posedge w_clk or negedge w_rst_n)begin
    if(!w_rst_n)begin
    w_en<=1'b0;
    r_en<=1'b0;
    end
    else begin
    w_en<=$random;
    r_en<=$random;
    end
    end
    
    always @(posedge r_clk or negedge r_rst_n)begin
    if(!r_rst_n)
    r_en<=1'b0;
    else
    r_en<=$random;
    end
    
    always @(*)begin //这个模块只能在这个位置,不能放到写和读的两个always前
    if(w_en)
    w_data=$random;
    else
    w_data=0;
    end
    
    endmodule 

    仿真结果: 

  • 相关阅读:
    数据结构最小生成树两个算法
    hdu-1742Ellipse(自适应辛普森)
    hdu1251统计难题
    codeforces-551C GukiZ hates Boxes
    CodeForces
    codeforces-894C Marco and GCD Sequence
    codeforces-892B Wrath
    codeforces-894B Ralph And His Magic Field
    CodeForces
    CodeForces
  • 原文地址:https://www.cnblogs.com/FPGAer/p/13768977.html
Copyright © 2011-2022 走看看