[X]关闭

18 Verilog语法_FIFO设计

文档创建者:uisrc
浏览次数:392
最后更新:2024-01-07
FPGA基础知识
FPGA基础: FPGA编程语言 » Verilog编程入门
软件版本:无
操作系统:WIN10 64bit
硬件平台:适用所有系列FPGA
登录"米联客"FPGA社区-www.uisrc.com视频课程、答疑解惑!
1概述
本小节主要讲解Verilog语法的FIFO设计,需要掌握FIFO的基本原理,掌握同步FIFO和异步FIFO的结构。
2同步FIFO
FIFO表示先入先出,它是一种存储器结构。同步FIFO是使用单一时钟同时进行读取和写入操作的,数据流和相关的控制逻辑在同一个时钟域内处理。
同步FIFO的接口设计如下:
fifo_clk
fifo的时钟信号
fifo_rst_n
fifo的复位信号
fifo_wren
fifo的写有效信号
fifo_wrdata
fifo的写数据
fifo_rden
fifo的读有效信号
fifo_rddata
fifo的读数据
fifo_full
fifo的状态满信号
fifo_empty
fifo的状态空信号
fifo_afull
fifo的状态将满信号
fifo_aempty
fifo的状态将空信号
fifo_room_avail
fifo的状态可写入数信号
fifo_data_avail
fifo的状态可读出数信号


同步FIFO具体的设计如下:
module synch_fifo#(
    parameter FIFO_AFULL_SIZE   = 1,
    parameter FIFO_AEMPTY_SIZE  = 1,
    parameter FIFO_ADDR_WIDTH   = 4,  
    parameter FIFO_WIDTH        = 8,  
    parameter FIFO_DEPTH        = 2**FIFO_ADDR_WIDTH
) (
    fifo_clk       ,
    fifo_rst_n     ,
    fifo_wren      ,
    fifo_wrdata    ,
    fifo_rden      ,
    fifo_rddata    ,
    fifo_full      ,
    fifo_empty     ,
    fifo_afull     ,
    fifo_aempty    ,
    fifo_room_avail,
    fifo_data_avail
);

input   wire                    fifo_clk       ;
input   wire                    fifo_rst_n     ;
input   wire                    fifo_wren      ;
input   wire [FIFO_WIDTH-1:0]   fifo_wrdata    ;
input   wire                    fifo_rden      ;
output  wire [FIFO_WIDTH-1:0]   fifo_rddata    ;
output  reg                     fifo_full      ;
output  reg                     fifo_empty     ;
output  wire                    fifo_afull     ;
output  wire                    fifo_aempty    ;
output  reg  [FIFO_ADDR_WIDTH:0]        fifo_room_avail;
output  wire [FIFO_ADDR_WIDTH:0]        fifo_data_avail;

localparam FIFO_DEPTH_MINUS1 = FIFO_DEPTH - 1;

//****************REG**************************
reg     [FIFO_ADDR_WIDTH-1:0]  wr_ptr,wr_ptr_nxt;
reg     [FIFO_ADDR_WIDTH-1:0]  rd_ptr,rd_ptr_nxt;
reg     [FIFO_ADDR_WIDTH:0]    num_entries,num_entries_nxt;

wire                    fifo_full_nxt;
wire                    fifo_empty_nxt;
wire    [FIFO_ADDR_WIDTH:0] fifo_room_avail_nxt;

//write pointer control logic
//*********************************************
always@(*)
begin
    wr_ptr_nxt = wr_ptr;
    if(fifo_wren)
    begin
        if(wr_ptr == FIFO_DEPTH_MINUS1)
            wr_ptr_nxt = 'd0;
        else
            wr_ptr_nxt = wr_ptr + 1'b1;
    end
end

//read pointer control logic
//*********************************************
always@(*)
begin
    rd_ptr_nxt = rd_ptr;
    if(fifo_rden)
    begin
        if(rd_ptr == FIFO_DEPTH_MINUS1)
            rd_ptr_nxt = 'd0;
        else
            rd_ptr_nxt = rd_ptr + 1'b1;
    end
end

//calculate number of occupied entries in the fifo
//*********************************************
always@(*)
begin
    num_entries_nxt = num_entries;
    if (fifo_wren && fifo_rden)
    begin
        num_entries_nxt = num_entries;
    end
    else if (fifo_wren)
    begin
        num_entries_nxt = num_entries + 1'b1;
    end
    else if (fifo_rden)
    begin
        num_entries_nxt = num_entries - 1'b1;
    end
end

assign fifo_full_nxt       = (num_entries_nxt == FIFO_DEPTH);
assign fifo_empty_nxt      = (num_entries_nxt == 'd0);
assign fifo_data_avail     = num_entries;
assign fifo_room_avail_nxt = FIFO_DEPTH - num_entries_nxt;
assign fifo_afull          = (fifo_room_avail_nxt <= FIFO_AFULL_SIZE)? 1:0;
assign fifo_aempty         = (fifo_data_avail <= FIFO_AEMPTY_SIZE ) ? 1:0;

//the fifo output
//*********************************************
always@(posedge fifo_clk or negedge fifo_rst_n)
begin
    if (!fifo_rst_n)
    begin
        wr_ptr          <= 'd0;
        rd_ptr          <= 'd0;
        num_entries     <= 'd0;
        fifo_full       <= 1'b0;
        fifo_empty      <= 1'b1;
        fifo_room_avail <= FIFO_DEPTH;
    end
    else
    begin                  
        wr_ptr          <= wr_ptr_nxt;
        rd_ptr          <= rd_ptr_nxt;
        num_entries     <= num_entries_nxt;
        fifo_full       <= fifo_full_nxt;
        fifo_empty      <= fifo_empty_nxt;
        fifo_room_avail <= fifo_room_avail_nxt;
    end
end

//the ram instantiation sdp
//*********************************************

sdp_ram_sync #(      
    .DATA_W          (FIFO_WIDTH        ),
    .ADDR_WIDTH      (FIFO_ADDR_WIDTH   ),
    .REG_OUT         (0                 )
)u_ram               (
    .data_in         (fifo_wrdata   ),
    .wraddress       (wr_ptr        ),
    .wren            (fifo_wren     ),
    .clk             (fifo_clk      ),
    .data_out        (fifo_rddata   ),
    .rdaddress       (rd_ptr        ),
    .rden            (fifo_rden     ),
    .rst_n           (fifo_rst_n    )
);

endmodule

module sdp_ram_sync#(
    parameter DATA_W    = 1,
    parameter ADDR_WIDTH= 9,
    parameter REG_OUT   = 1,
    parameter U_DLY     = 1
)(
    input       [1              -1:0]   clk         ,
    input       [1              -1:0]   rst_n       ,  
    input       [1              -1:0]   wren        ,
    input       [ADDR_WIDTH     -1:0]   wraddress   ,  
    input       [DATA_W         -1:0]   data_in     ,  
    input       [1              -1:0]   rden        ,      
    input       [ADDR_WIDTH     -1:0]   rdaddress   ,
    output      [DATA_W         -1:0]   data_out
);

localparam     ADDR_NUM  = 2**ADDR_WIDTH;

reg [DATA_W         -1:0] mem [ADDR_NUM         -1:0];
reg [DATA_W         -1:0] q_tmp     ;
reg [DATA_W         -1:0] q_tmp_1d  ;

always@(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        mem[wraddress] <= #U_DLY 'd0;
    else if(wren==1'b1)
    begin
        mem[wraddress] <= #U_DLY data_in;
    end
end

always@(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        q_tmp <= #U_DLY 'd0;
    else if(rden==1'b1)
    begin
        q_tmp <= #U_DLY mem[rdaddress];
    end
end

always@(posedge clk )
begin
    q_tmp_1d <= #U_DLY q_tmp;
end

assign data_out =(REG_OUT == 1) ? q_tmp_1d : q_tmp ;

endmodule


仿真结果如图所示:
2504661-20240107124942072-336426033.jpg
3异步FIFO
在实际的应用中常常需要多个时钟域的数据传递,需要使用异步FIFO将数据从一个时钟域传递到另一个时钟域。
从原理上来讲,同步FIFO和异步FIFO类似,但是由于异步FIFO与两个时钟相关,电路复杂度变高。对于异步FIFO的写入操作和读出操作的方式与同步FIFO类似,写入和读出操作都有自己的信号集,其复杂度在于产生FIFO的状态信号。
通过比较写入指针和读取时钟的值,可以产生空,满等状态信号,指针的跨时钟传递经过了格雷码和二进制码的转换以及打两拍进行跨时钟的处理。如下图所示
2504661-20240107124942609-1999042975.jpg
写入指针首先被转换为格雷码,并被寄存到写入时钟域的触发器中。然后,再经过读取时钟域同步,打两拍,后面进行格雷码到二进制的转换电路,进行寄存。此时写入指针已被传送到读取时钟域,用于和读取时钟域的读指针进行比较,求得空状态信号。
当读取指针被传送到写入时钟域时,相对于读取时钟域中的读取指针将会有3-4个的时钟周期延迟。这意味着可用于写入数据的位置要比显示的可能要多三到四个。这是异步FIFO操作保守的一面,如此才不会有数据上溢出。当FIFO读取数据时,读指针数据在三到四个周期内将会跟上实际的读指针值。另外FIFO具有一定的深度,其中有足够多的数据被读取,因此不会影响数据传送的性能。例:
module asynch_fifo#(
    parameter FIFO_AFULL_SIZE   = 1,
    parameter FIFO_AEMPTY_SIZE  = 1,
    parameter FIFO_PTR          = 4,   
    parameter FIFO_WIDTH        = 8      
    // parameter FIFO_DEPTH = 16  
)(
    fifo_wrclk     ,
    fifo_wr_rst_n  ,
    fifo_wren      ,
    fifo_wrdata    ,
    reset_wrptr    ,
    fifo_rdclk     ,
    fifo_rd_rst_n  ,
    fifo_rden      ,
    fifo_rddata    ,
    reset_rdptr    ,
    fifo_full      ,
    fifo_empty     ,
    fifo_afull     ,
    fifo_aempty    ,
    fifo_room_avail,
    fifo_data_avail
);
input wire                      fifo_wrclk     ;
input wire                      fifo_wr_rst_n  ;
input wire                      fifo_wren      ;
input wire  [FIFO_WIDTH-1:0]    fifo_wrdata    ;
input wire                      reset_wrptr    ;
input wire                      fifo_rdclk     ;
input wire                      fifo_rd_rst_n  ;
input wire                      fifo_rden      ;
output wire [FIFO_WIDTH-1:0]    fifo_rddata    ;
input wire                      reset_rdptr    ;
output reg                      fifo_full      ;
output reg                      fifo_empty     ;
output wire                     fifo_afull     ;
output wire                     fifo_aempty    ;
output reg  [FIFO_PTR:0]        fifo_room_avail;
output reg  [FIFO_PTR:0]        fifo_data_avail;

localparam FIFO_DEPTH             = (1<<FIFO_PTR);
localparam FIFO_TWICEDEPTH_MINUS1 = 2*FIFO_DEPTH - 1;

//****************REG**************************
reg     [FIFO_PTR:0]    wr_ptr_wab,wr_ptr_wab_nxt;//extra wraparound bit
wire    [FIFO_PTR:0]    fifo_room_avail_nxt      ;
wire                    fifo_full_nxt            ;
wire    [FIFO_PTR:0]    wr_ptr                   ;//write ptr without wraparound bit
reg     [FIFO_PTR:0]    rd_ptr_wab,rd_ptr_wab_nxt;//extra wraparound bit
wire    [FIFO_PTR:0]    fifo_data_avail_nxt      ;
wire                    fifo_empty_nxt           ;
wire    [FIFO_PTR:0]    rd_ptr                   ;//read ptr without wraparound bit

reg     [FIFO_PTR:0]    wr_ptr_wab_gray          ;
wire    [FIFO_PTR:0]    wr_ptr_wab_gray_nxt      ;
reg     [FIFO_PTR:0]    wr_ptr_wab_gray_sync1    ;
reg     [FIFO_PTR:0]    wr_ptr_wab_gray_sync2    ;
reg     [FIFO_PTR:0]    wr_ptr_wab_rdclk         ;
wire    [FIFO_PTR:0]    wr_ptr_wab_rdclk_nxt     ;

reg     [FIFO_PTR:0]    rd_ptr_wab_gray          ;
wire    [FIFO_PTR:0]    rd_ptr_wab_gray_nxt      ;
reg     [FIFO_PTR:0]    rd_ptr_wab_gray_sync1    ;
reg     [FIFO_PTR:0]    rd_ptr_wab_gray_sync2    ;
reg     [FIFO_PTR:0]    rd_ptr_wab_wrclk         ;
wire    [FIFO_PTR:0]    rd_ptr_wab_wrclk_nxt     ;
//write pointer control logic
//*********************************************
always@(*)
begin
    wr_ptr_wab_nxt = wr_ptr_wab;
    if(reset_wrptr)
    begin
        wr_ptr_wab_nxt = 'd0;
    end
    else if(fifo_wren&&(wr_ptr_wab == FIFO_TWICEDEPTH_MINUS1))
    begin
        wr_ptr_wab_nxt = 'd0;
    end
    else if(fifo_wren)
    begin
        wr_ptr_wab_nxt = wr_ptr_wab + 1'b1;
    end
end

always@(posedge fifo_wrclk or negedge fifo_wr_rst_n)
begin
    if(!fifo_wr_rst_n)
    begin
        wr_ptr_wab            <= 'd0;
    end
    else
    begin
        wr_ptr_wab            <= wr_ptr_wab_nxt;
    end
end

//convert the binary wr_ptr to gray,flop it,and then pass it to read domain
//*********************************************
binary_to_gray#(.PTR(FIFO_PTR)) u_binary_to_gray_wr
(
    .binary_value   (wr_ptr_wab_nxt         ),
    .gray_value     (wr_ptr_wab_gray_nxt    )
);

always@(posedge fifo_wrclk or negedge fifo_wr_rst_n)
begin
    if(!fifo_wr_rst_n)
    begin
        wr_ptr_wab_gray       <= 'd0;
    end
    else
    begin
        wr_ptr_wab_gray       <= wr_ptr_wab_gray_nxt;
    end
end

//synchronize wr_ptr_wab_gray into read clock domain
//*********************************************
always@(posedge fifo_rdclk or negedge fifo_rd_rst_n)
begin
    if(!fifo_rd_rst_n)
    begin
        wr_ptr_wab_gray_sync1       <= 'd0;
        wr_ptr_wab_gray_sync2       <= 'd0;
    end
    else
    begin
        wr_ptr_wab_gray_sync1       <= wr_ptr_wab_gray;
        wr_ptr_wab_gray_sync2       <= wr_ptr_wab_gray_sync1;
    end
end

//convert wr_ptr_wab_gray_sync2 back to binary form
//*********************************************
gray_to_binary#(.PTR(FIFO_PTR)) u_gray_to_binary_wr
(
    .gray_value     (wr_ptr_wab_gray_sync2  ),
    .binary_value   (wr_ptr_wab_rdclk_nxt   )
);

always@(posedge fifo_rdclk or negedge fifo_rd_rst_n)
begin
    if(!fifo_rd_rst_n)
    begin
        wr_ptr_wab_rdclk       <= 'd0;
    end
    else
    begin
        wr_ptr_wab_rdclk       <= wr_ptr_wab_rdclk_nxt;
    end
end

//read pointer control logic
//*********************************************
always@(*)
begin
    rd_ptr_wab_nxt = rd_ptr_wab;
    if(reset_rdptr)
    begin
        rd_ptr_wab_nxt = 'd0;
    end
    else if(fifo_rden && (rd_ptr_wab == FIFO_TWICEDEPTH_MINUS1))
    begin
        rd_ptr_wab_nxt = 'd0;
    end
    else if(fifo_rden)
    begin
        rd_ptr_wab_nxt = rd_ptr_wab + 1'b1;
    end
end

always@(posedge fifo_rdclk or negedge fifo_rd_rst_n)
begin
    if(!fifo_rd_rst_n)
    begin
        rd_ptr_wab            <= 'd0;
    end
    else
    begin
        rd_ptr_wab            <= rd_ptr_wab_nxt;
    end
end

//convert the binary rd_ptr to gray and then pass it to write clock domain
//*********************************************
binary_to_gray#(.PTR(FIFO_PTR)) u_binary_to_gray_rd
(
    .binary_value   (rd_ptr_wab_nxt         ),
    .gray_value     (rd_ptr_wab_gray_nxt    )
);

always@(posedge fifo_rdclk or negedge fifo_rd_rst_n)
begin
    if(!fifo_rd_rst_n)
    begin
        rd_ptr_wab_gray         <= 'd0;
    end
    else
    begin
        rd_ptr_wab_gray         <= rd_ptr_wab_gray_nxt;
    end
end

//synchronize rd_ptr_wab_gray into write clock domain
//*********************************************
always@(posedge fifo_wrclk or negedge fifo_wr_rst_n)
begin
    if(!fifo_wr_rst_n)
    begin
        rd_ptr_wab_gray_sync1       <= 'd0;
        rd_ptr_wab_gray_sync2       <= 'd0;
    end
    else
    begin
        rd_ptr_wab_gray_sync1       <= rd_ptr_wab_gray;
        rd_ptr_wab_gray_sync2       <= rd_ptr_wab_gray_sync1;
    end
end

//convert rd_ptr_wab_gray_sync2 back to binary form
//*********************************************
gray_to_binary#(.PTR(FIFO_PTR)) u_gray_to_binary_rd
(
    .gray_value     (rd_ptr_wab_gray_sync2  ),
    .binary_value   (rd_ptr_wab_wrclk_nxt   )
);

always@(posedge fifo_rdclk or negedge fifo_rd_rst_n)
begin
    if(!fifo_rd_rst_n)
    begin
        rd_ptr_wab_wrclk       <= 'd0;
    end
    else
    begin
        rd_ptr_wab_wrclk       <= rd_ptr_wab_wrclk_nxt;
    end
end

assign wr_ptr = wr_ptr_wab[FIFO_PTR-1:0];
assign rd_ptr = rd_ptr_wab[FIFO_PTR-1:0];

//the ram instantiation sdp
//*********************************************

sdp_ram #(      
    .DATA_W         (FIFO_WIDTH      ),
    .ADDR_WIDTH     (FIFO_PTR        ),
    .REG_OUT        (0               )
)u_ram(
    .data_in        (fifo_wrdata    ),
    .wraddress      (wr_ptr         ),
    .wren           (fifo_wren      ),
    .clk_w          (fifo_wrclk     ),
    .data_out       (fifo_rddata    ),
    .rdaddress      (rd_ptr         ),
    .rden           (fifo_rden      ),
    .clk_r          (fifo_rdclk     ),
    .rst_n_w        (fifo_wr_rst_n  ),
    .rst_n_r        (fifo_rd_rst_n  )
);

//generate fifo_full:pointers equal,but the warp around bits are different
//*********************************************
assign fifo_full_nxt        =   (wr_ptr_wab_nxt[FIFO_PTR]     != rd_ptr_wab_wrclk_nxt[FIFO_PTR]) &&
                                (wr_ptr_wab_nxt[FIFO_PTR-1:0] == rd_ptr_wab_wrclk_nxt[FIFO_PTR-1:0]);

assign fifo_room_avail_nxt  =   (wr_ptr_wab[FIFO_PTR] == rd_ptr_wab_wrclk[FIFO_PTR])?
                                (FIFO_DEPTH - (wr_ptr_wab[FIFO_PTR-1:0] - rd_ptr_wab_wrclk[FIFO_PTR-1:0])):
                                (rd_ptr_wab_wrclk[FIFO_PTR-1:0] - wr_ptr_wab[FIFO_PTR-1:0]);

//generate fifo_empty:pointers are equal including the warp around bits
//*********************************************
assign fifo_empty_nxt       =   (rd_ptr_wab_nxt[FIFO_PTR:0] == wr_ptr_wab_rdclk_nxt[FIFO_PTR:0]);

assign fifo_data_avail_nxt  =   (rd_ptr_wab[FIFO_PTR] == wr_ptr_wab_rdclk[FIFO_PTR])?
                                (wr_ptr_wab_rdclk[FIFO_PTR-1:0] - rd_ptr_wab[FIFO_PTR-1:0]):
                                (FIFO_DEPTH - (rd_ptr_wab[FIFO_PTR-1:0] - wr_ptr_wab_rdclk[FIFO_PTR-1:0]));

assign fifo_afull           = (fifo_room_avail_nxt <= FIFO_AFULL_SIZE)? 1:0;
assign fifo_aempty          = (fifo_data_avail_nxt <= FIFO_AEMPTY_SIZE ) ? 1:0;

always@(posedge fifo_wrclk or negedge fifo_wr_rst_n)
begin
    if(!fifo_wr_rst_n)
    begin
        fifo_full             <= 1'b0;
        fifo_room_avail       <= 'd0;
    end
    else
    begin
        fifo_full             <= fifo_full_nxt;
        fifo_room_avail       <= fifo_room_avail_nxt;
    end
end

always@(posedge fifo_rdclk or negedge fifo_rd_rst_n)
begin
    if(!fifo_rd_rst_n)
    begin
        fifo_empty            <= 1'b1;
        fifo_data_avail       <= 'd0;
    end
    else
    begin
        fifo_empty            <= fifo_empty_nxt;
        fifo_data_avail       <= fifo_data_avail_nxt;
    end
end

endmodule

module binary_to_gray#(
    parameter PTR   = 4    //"1","0"   
)(
    binary_value   ,
    gray_value   
);

input   wire [PTR:0]    binary_value  ;
output  wire [PTR:0]    gray_value    ;

generate
    genvar i;
    for( i = 0 ; i < PTR ; i = i + 1 )
    begin
        assign gray_value = binary_value ^ binary_value[ i + 1 ];
    end

endgenerate

assign gray_value[PTR] = binary_value[PTR];

endmodule

module gray_to_binary#(
    parameter PTR   = 4    //"1","0"   
)(
    binary_value   ,
    gray_value   
);

input   wire [PTR:0]    gray_value    ;
output  wire [PTR:0]    binary_value  ;

generate
    genvar i;
    for( i = 0 ; i < PTR ; i = i + 1 )
    begin
        assign binary_value = binary_value[i + 1] ^ gray_value;
    end

endgenerate

assign binary_value[PTR] = gray_value[PTR];

endmodule

module sdp_ram#(
    parameter DATA_W    = 1,
    parameter ADDR_WIDTH= 9,
    parameter REG_OUT   = 1,
    parameter U_DLY     = 1
)(
    input       [1              -1:0]   clk_w       ,
    input       [1              -1:0]   rst_n_w     ,  
    input       [1              -1:0]   wren        ,
    input       [ADDR_WIDTH     -1:0]   wraddress   ,  
    input       [DATA_W         -1:0]   data_in     ,
    input       [1              -1:0]   clk_r       ,
    input       [1              -1:0]   rst_n_r     ,  
    input       [1              -1:0]   rden        ,      
    input       [ADDR_WIDTH     -1:0]   rdaddress   ,
    output      [DATA_W         -1:0]   data_out
);

localparam     ADDR_NUM  = 2**ADDR_WIDTH;

reg [DATA_W         -1:0] mem [ADDR_NUM         -1:0];
reg [DATA_W         -1:0] q_tmp     ;
reg [DATA_W         -1:0] q_tmp_1d  ;

always@(posedge clk_w or negedge rst_n_w)
begin
    if(!rst_n_w)
        mem[wraddress] <= #U_DLY 'd0;
    else if(wren==1'b1)
    begin
        mem[wraddress] <= #U_DLY data_in;
    end
end

always@(posedge clk_r or negedge rst_n_r)
begin
    if(!rst_n_r)
        q_tmp <= #U_DLY 'd0;
    else if(rden==1'b1)
    begin
        q_tmp <= #U_DLY mem[rdaddress];
    end
end

always@(posedge clk_r )
begin
    q_tmp_1d <= #U_DLY q_tmp;
end

assign data_out =(REG_OUT == 1) ? q_tmp_1d : q_tmp ;

endmodule


仿真结果如图所示:
2504661-20240107124943246-1911161285.jpg

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则