Verilog实现异步FIFO

空满标志产生:
满标志:“写时钟阈写指针格雷码” 与 “同步到写时钟阈的读指针格雷码” 高两位不同,其余低位相同即为满。其中高两位中已经包含了折返标志位。
空标志:“读时钟阈读指针格雷码” 与 “同步到读时钟阈的写指针格雷码” 全等即为空。

直接上源码

module FIFO #(
    parameter data_width = 16, 
    parameter data_depth = 8,
    parameter addr_width = 4
)
(
    input clk_write,
    input clk_read,
    input rst_n,
    input write_a, //write avalible
    input read_a,  //read avalible
    input [data_width : 0] data_in,

    output reg [data_width : 0] data_out,
    output reg full,
    output reg empty
);

reg  [addr_width-1 : 0] write_addr;
wire [addr_width-1 : 0] write_addr_gray;
reg  [addr_width-1 : 0] w2r_addr_gray1;
reg  [addr_width-1 : 0] w2r_addr_gray2;

reg  [addr_width-1 : 0] read_addr;
wire [addr_width-1 : 0] read_addr_gray;
reg  [addr_width-1 : 0] r2w_addr_gray1;
reg  [addr_width-1 : 0] r2w_addr_gray2;

reg  [data_width-1 : 0] FIFO [data_depth-1 : 0];

//写时钟阈
always@(posedge clk_write or negedge rst_n) begin
    if(!rst_n)
       write_addr <= 0;
    else if(write_a & !full) begin      //写使能和非满时才可向FIFO写入数据
        write_addr <= write_addr + 1;
        FIFO[write_addr[addr_width-2 : 0]] <= data_in;
    end
    else begin
        write_addr <= write_addr;
        FIFO[write_addr[addr_width-2 : 0]] <= FIFO[write_addr[addr_width-2 : 0]];  //注意此处写FIFO的地址是除去折返标志位(最高位)的地址
    end
end

//读时钟阈
always @(posedge clk_read or negedge rst_n) begin
    if(!rst_n)
       read_addr <= 0;
    else if(read_a & !empty ) begin    //读使能和非空时才可从FIFO读出数据
        read_addr <= read_addr + 1;
        data_out <= FIFO[read_addr[addr_width-2 : 0]];
    end
    else begin
        read_addr <= read_addr;
    end
end

//写指针同步到读时钟阈
assign write_addr_gray = (write_addr >> 1) ^ write_addr;
always @(posedge clk_read ) begin
    w2r_addr_gray1 <= write_addr_gray;
    w2r_addr_gray2 <= w2r_addr_gray1;       //两级寄存器减少亚稳态 
end

//读指针同步带写时钟阈
assign read_addr_gray = (read_addr >> 1) ^ read_addr;
always @(posedge clk_write ) begin
    r2w_addr_gray1 <= read_addr_gray;
    r2w_addr_gray2 <= r2w_addr_gray1;       //两级寄存器减少亚稳态 
end

//FIFO空信号产生,在读时钟阈判断
always@(*) begin
    if (w2r_addr_gray2 == read_addr_gray) begin
        empty = 'b1;
    end
    else begin
        empty = 'b0;
    end
end

//FIFO满信号产生,在写时钟阈判断,高两位不同,其余低位相同
always @(*) begin
    if({~r2w_addr_gray2[addr_width-1 : addr_width-2],r2w_addr_gray2[addr_width-3 : 0]} ==  write_addr_gray) begin
        full = 'b1;
    end
    else begin
        full = 'b0;
    end
end

endmodule

你可能感兴趣的:(一位数字ICer的成长之路,fpga开发,单片机,嵌入式硬件)