verilog实例—sync_fifo


一、FIFO

  • 同步FIFO 即时钟同步
  • FIFO是什么,有什么用?
    First Input First Output的缩写,先入先出队列,这是一种传统的按序执行方法,先进入的指令先完成并引退,跟着才执行第二条指令。
  • 端口
    在这里插入图片描述
    在这里插入图片描述

二、skills

1、读写地址变化方式

1、关于写地址的变化,只需考虑we有效的情况:

只写不读时:若FIFO满,则写失败,写地址不变;若FIFO不满,则写一定成功,写地址加1。
又写又读时:若FIFO满,读写为同一单元,因读操作只需读取旧数据,所以读写都成功,写地址加1;若FIFO不满,则进行读写操作的是两个不同单元,所以写一定成功,所以写地址也加1。

总结,wr_en && (!full || (full && rd_en))时,写地址加1;其他情况,写地址不变。因为只有写成功(写地址加1)的情况,才能让RAM的写使能有效,所以RAM的写使能也为wr_en && (!full || (full && rd_en))。

2、关于读地址的变化,只需考虑re有效的情况:

只读不写时:若FIFO空,则读失败,读地址不变;若FIFO不空则读一定成功,读地址加1.。
又读又写时:若FIFO空,则读写为同一单元,由于单元内无旧数据,读失败,只有写成功,此时读地址不变;若FIFO不空,则进行读写操作的是两个不同单元,所以读一定成功,读地址加1。

总结,(re有效&&empty标志无效)时,读地址加1;其他情况,读地址不变。因为只有读成功(读地址加1)的情况,才能让RAM的读使能有效,所以RAM的读使能也为(re有效&&empty标志无效)。

2、notes

  • FIFO满时,和空时,读写指针指向同一地址
  • 满时,读写使能为1,可同时读写
  • 空时,读写使能为1,只能写

三、RTL design

module sync_fifo(
        clk,
        rst,    //async
        wr_data,
        wr_en,
        rd_data,
        rd_en,
        full,
        empty
        );
    
    parameter   DATA_WIDTH = 32,
                DATA_DEPTH = 8,
                ADD_WIDTH  = 3;

    input       clk;
    input       rst;
    input       wr_data;
    input       wr_en;
    input       rd_en;

    output      rd_data;
    output      full;
    output      empty;
    
    reg [DATA_WIDTH-1:0]    wr_data;
    reg [DATA_WIDTH-1:0]    rd_data;
    reg [DATA_WIDTH-1:0]    ram_32_8[DATA_DEPTH-1:0];
    reg [ADD_WIDTH-1:0]     wr_add;
    reg [ADD_WIDTH-1:0]     rd_add;
    reg [ADD_WIDTH-1+1:0]   data_count;
    
    wire         full;
    wire        empty;
    reg         wr_enbuf;
    reg         rd_enbuf;

    assign wr_enbuf = wr_en && (!full || (full && rd_en));
    assign rd_enbuf = rd_en && !empty;
    //写指针移动
    always@(posedge clk or negedge rst)begin
        if(!rst)
            wr_add <= 3'b0;
        else if(wr_enbuf)
            wr_add <= wr_add + 1'b1;
        end

    //读指针移动
    always@(posedge clk or negedge rst)begin
        if(!rst)
            rd_add <= 3'b0;
        else if(rd_enbuf)
            rd_add <= rd_add + 1'b1;
        end
    //数据计数器
    always@(posedge clk or negedge rst)begin//case is good
        if(!rst)
            data_count <= 4'b0;
        else begin 
            if(rd_enbuf && wr_enbuf) //writing while reading
                data_count <= data_count;
            else if(wr_enbuf)     //writing
                data_count <= data_count + 1'b1;
            else if(rd_enbuf)    //reading
                data_count <= data_count - 1'b1;
        end
    end

    //满空判断
//    always@(posedge clk or negedge rst)begin
//        if(!rst)
//            full <= 1'b0;
//        else 
//            //full <= (!rd_enbuf && data_count == 4'b1000)||(!rd_enbuf && data_count == 4'b0111 && wr_enbuf)||(rd_enbuf && wr_enbuf && data_count == 4'b1000);
//    end
    assign full = data_count == 4'b1000;


//    always@(posedge clk or negedge rst)begin
//        if(!rst)
//            empty <= 1'b1;
//        else 
//            //empty <= (!wr_enbuf && data_count == 3'b000)||(!wr_enbuf && data_count ==3'b001 && rd_enbuf);
//        end
    assign empty = data_count == 0;

    //ram写读数据
    always@(posedge clk or negedge rst)begin
        if(!rst)begin
            integer i ;
            for(i=0;i < DATA_DEPTH;i=i+1)
                begin
                ram_32_8[i] <= 32'b0;
                end
        end
        else if(wr_enbuf)begin
                ram_32_8[wr_add] <= wr_data;
        end
    end


    always@(posedge clk or negedge rst)begin
        if(!rst)begin
            rd_data <= 32'b0;
        end
        else if(rd_enbuf)begin
            rd_data <= ram_32_8[rd_add];
        end
    end

endmodule

  • tb
`timescale 1ps/1ps
module test;
    //data type spicifiction
    reg         clk;
    reg         rst;
    reg [31:0]  wr_data;
    reg         wr_en;
    reg         rd_en;

    wire [31:0] rd_data;
    wire        full;
    wire        empty;

    //reset all
    initial begin
        clk   = 0;
        rst   = 1;

        wr_en = 0;
        rd_en = 0;

        wr_data = 32'b0;

     #2 rst   = 0;
     #5 rst   = 1;

    end
    
    //clock genarating
    always #0.5 clk = ~clk;
        
    //stimulate
    initial begin
        #10 wr_en = 1;
            rd_en = 0;
        #10 wr_en = 0;
            rd_en = 1;
        #10 wr_en = 1;
            rd_en = 0;
        #3  rd_en = 1;
        #10 
        repeat(100) begin
            #5 wr_en = {
    
    $random}%2;
               rd_en = {
    
    $random}%2;
        end
    end
    
    always #1 wr_data = {
    
    $random}%10;
    
    //module instantiation
     sync_fifo u_sync_fifo(
        .clk            (clk    ),
        .rst            (rst    ),    
        .wr_data        (wr_data),
        .wr_en          (wr_en  ),
        .rd_data        (rd_data),
        .rd_en          (rd_en  ),
        .full           (full   ),
        .empty          (empty  ) 
        );

    //stop
    initial begin
        forever begin
            #100
            //$display("---gyc---:%d",$time);
            if($time >= 2000)begin
                $finish;
            end
        end
    end
endmodule

4、veidi graph

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_70829439/article/details/126707432