Loading

Chapter1. 同步异步FIFO专题

Chapter1. 同步异步FIFO专题

章节导图

同步FIFO

移位寄存器方法 单指针

module sync_fifo #(
    parameter DATA_WIDTH = 8
    parameter FIFO_DEPTH = 16,
)
(
    input clk,
    input rst_n,
    input [DATA_WIDTH-1:0] din,
    input push,
    input pop,
    output [DATA_WIDTH-1:0] dout,
    output reg full,
    output reg empty
);

    parameter A_FULL = FIFO_DEPTH - 2;

    reg [DATA_WIDTH-1:0] mem [FIFO_DEPTH-1:0];
    reg [$clog2(FIFO_DEPTH)-1:0] read_ptr;
    integer i;

    // shift to write data
    always @(posedge clk or negedge rst_n)begin
        if(push & !full)begin
            mem[0] <= din;
            for(i = 0; i < FIFO_DEPTH; i = i + 1)begin
                mem[i+1] <= mem[i];
            end
        end
    end 

    // read poniter
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)
            read_ptr <= FIFO_DEPTH - 1'b1;
        else if (push & !pop & !full)begin
            if(read_ptr == FIFO_DEPTH - 1'b1) 
                read_ptr <= 1'b0;
            else 
                read_ptr <= read_ptr + 1'b1;
        end
        else if (pop & !push & !empty)begin
            if(read_ptr == 1'b0)
                read_ptr <= FIFO_DEPTH - 1'b1;
            else
                read_ptr <= read_ptr - 1'b1;
        end
    end

    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)
            empty <= 1'b1;
        else if (~push & pop & (read_ptr == 0))
            empty <= 1'b1;
        else if (!pop & push)
            empty <= 1'b0;
    end

    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)
            full <= 1'b0;
        else if (~pop & push & (read_ptr == A_FULL))
            full <= 1'b1;
        else if (!push & pop)
            full <= 1'b0;
    end

    assign dout = (FIFO_DEPTH == 1) ? mem[0] : mem[read_ptr];

endmodule

双口RAM同步FIFO

module dual_port_RAM #(
    parameter WIDTH = 8,
    parameter DEPTH = 16
)
(
    input wclk,
    input wenc,
    input [$clog2(DEPTH)-1:0] waddr,
    input [DEPTH-1:0] wdata,
    input rclk,
    input renc,
    input [$clog2(DEPTH)-1:0] raddr,
    output reg [DEPTH-1:0] rdata,
);

    reg [WIDTH-1:0] mem [DEPTH-1:0];

    always @(posedge wclk)begin
        if(wenc)
            mem[waddr] <= wdata;
    end

    always @(posedge rclk) begin
        if(renc)
            rdata <= mem[raddr];
    end

endmodule

module sync_fifo #(
    parameter WIDTH = 8,
    parameter DEPTH = 16
)
(
    input clk,
    input rst_n,
    input winc,
    input rinc,
    input [WIDTH-1:0] datain,

    output reg empty,
    output reg full,
    output [WIDTH-1:0] dataout
);

    reg [$clog2(DEPTH):0] wr_ptr, rd_ptr;
    wire wenc, renc;

    assign wenc = !full & winc;
    assign renc = !empty & rinc;

    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            full <= 1'b0;
            empty <= 1'b0;
        end
        else begin
            full = (wr_ptr - rd_ptr) == DEPTH;
            empty = (wr_ptr == rd_ptr);
        end
    end

    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)
            wr_ptr <= 1'b0;
        else if (wenc)
            wr_ptr <= wr_ptr + 1'b1;
    end

    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)
            rd_ptr <= 1'b0;
        else if (renc)
            rd_ptr <= rd_ptr + 1'b1;
    end

    dual_port_RAM inst (clk, wenc, wr_ptr[$clog2(DEPTH)-1:0], wdata, clk, renc. rd_ptr[$clog2(DEPTH)-1:0], rdata);
endmodule

单口RAM同步FIFO

// 计算指针部分的模块是相同的,需要改变的仅仅是RAM部分
module single_port_RAM #(
    parameter WIDTH = 8,
    parameter DEPTH = 16
)
(
    input clk,
    input wenc,
    input renc,
    input [$clog2(DEPTH)-1:0] waddr,
    input [$clog2(DEPTH)-1:0] raddr,
    input [WIDTH-1:0] wdata,
    output reg [WIDTH-1:0] rdata
);

    reg [WIDTH-1:0] mem [DEPTH-1:0];

    // 写优先, 当读和写同时为高的情况下,只执行写操作,读操作不理会
    always @(posedge clk)begin
        if(wenc)
            mem[waddr] <= wdata;
        else if (renc)
            rdata <= mem[raddr];
    end

endmodule

异步FIFO

格雷码

如果异步FIFO是快到慢的cdc情况,那么可能出现格雷码变化不是连续的(漏采样)情况,会不会出现亚稳态,如果不会,为什么?

  • 答案:不会出现亚稳态,即便出现漏采样的情况,两次采样到的格雷码不是连续变化的,但也不会出现亚稳态,因为相邻格雷码只有一位改变,而多bit cdc出现亚稳态的原因是在同一个周期有多个bit发生变化,发生变化的时间是有差异的,漏采的情况中,虽然多bit发生了变化,但是并不是在同一个周期内发生的,因此直接打两拍不会出现亚稳态。

空满信号悲观是什么意思?

  • 答案:空信号是在读时钟域判断的,满信号是在写时钟域判断的;由于跨时钟域同步,因此可能会出现假空或者假满的情况,这是一种悲观的判断。

异步FIFO代码

module dual_port_RAM #(
    parameter WIDTH = 8,
    parameter DEPTH = 16
)
(
    input wclk,
    input wenc,
    input [$clog2(DEPTH)-1:0] waddr,
    input [DEPTH-1:0] wdata,
    input rclk,
    input renc,
    input [$clog2(DEPTH)-1:0] raddr,
    output reg [DEPTH-1:0] rdata
);

    reg [WIDTH-1:0] mem [DEPTH-1:0];

    always @(posedge wclk)begin
        if(wenc)
            mem[waddr] <= wdata;
    end

    always @(posedge rclk)begin
        if(renc)
            rdata = mem[raddr];
    end

endmodule 
module async_fifo #(
    parameter WIDTH = 8
    parameter DEPTH = 16,
)
(
    input wclk,
    input rclk,
    input wrst_n,
    input rrst_n,
    input winc,
    input rinc,
    input [WIDTH-1:0] wdata,

    output wfull,
    output rempty,
    output [WIDTH-1:0] rdata
);

    parameter ADDR_WIDTH = $clog2(DEPTH);

    reg [ADDR_WIDTH:0] wr_ptr, rd_ptr, wr_grey0, wr_grey1, wr_grey2;
    reg [ADDR_WIDTH:0] rd_grey0, rd_grey1, rd_grey2;
    wire [ADDR_WIDTH:0] wr_grey, rd_grey;
    wire wenc, renc;

    assign wenc = !wfull & winc;
    assign renc = !rempty & rinc;

    assign wr_grey = wr_ptr ^ (wr_ptr >> 1);
    assign rd_grey = rd_ptr ^ (rd_ptr >> 1);

    assign wfull = wr_grey0 == {~rd_grey2[ADDR_WIDTH:ADDR_WIDTH-1], rd_grey2[ADDR_WIDTH-2:0]};
    assign rempty = wr_grey2 == rd_grey0;

    always @(posedge wclk or negedge wrst_n)begin
        if(!wrst_n)
            wr_ptr <= 1'b0;
        else if (wenc)
            wr_ptr <= wr_ptr + 1'b1;
    end

    always @(posedge rclk or negedge rrst_n)begin
        if(!rrst_n)
            rd_ptr <= 1'b0;
        else if (renc)
            rd_ptr <= rd_ptr + 1'b1;
    end

    always @(posedge wclk or negedge wrst_n)begin
        if(!wrst_n)
            wr_grey0 <= 1'b0;
        else
            wr_grey0 <= wr_grey;
    end

    always @(posedge rclk or negedge rrst_n)begin
        if(!rrst_n)
            rd_grey0 <= 1'b0;
        else
            rd_grey0 <= rd_grey;
    end

    always @(posedge wclk or negedge wrst_n)begin
        if(!wrst_n)begin
            rd_grey1 <= 1'b0;
            rd_grey2 <= 1'b0;
        end
        else begin
            rd_grey1 <= rd_grey0;
            rd_grey2 <= rd_grey1;
        end
    end

    always @(posedge rclk or negedge rrst_n)begin
        if(!rrst_n)begin
            wr_grey1 <= 1'b0;
            wr_grey2 <= 1'b0;
        end
        else begin
            wr_grey1 <= wr_grey0;
            wr_grey2 <= wr_grey1;
        end
    end


    dual_port_RAM inst
    (
        wclk, wenc, wr_ptr[ADDR_WIDTH-1:0], wdata, rclk, rd_ptr[ADDR_WIDTH-1:0],
        rdata
    );

endmodule

posted @ 2022-08-21 16:50  pu1se  阅读(71)  评论(0)    收藏  举报