18 Verilog语法_FIFO设计

发布时间 2024-01-07 12:51:52作者: 米联客(milianke)

软件版本:无

操作系统: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

仿真结果如图所示:

3异步FIFO

在实际的应用中常常需要多个时钟域的数据传递,需要使用异步FIFO将数据从一个时钟域传递到另一个时钟域。

从原理上来讲,同步FIFO和异步FIFO类似,但是由于异步FIFO与两个时钟相关,电路复杂度变高。对于异步FIFO的写入操作和读出操作的方式与同步FIFO类似,写入和读出操作都有自己的信号集,其复杂度在于产生FIFO的状态信号。

通过比较写入指针和读取时钟的值,可以产生空,满等状态信号,指针的跨时钟传递经过了格雷码和二进制码的转换以及打两拍进行跨时钟的处理。如下图所示

写入指针首先被转换为格雷码,并被寄存到写入时钟域的触发器中。然后,再经过读取时钟域同步,打两拍,后面进行格雷码到二进制的转换电路,进行寄存。此时写入指针已被传送到读取时钟域,用于和读取时钟域的读指针进行比较,求得空状态信号。

当读取指针被传送到写入时钟域时,相对于读取时钟域中的读取指针将会有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[i] = binary_value[i] ^ 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[i] = binary_value[i + 1] ^ gray_value[i];

    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

仿真结果如图所示: