FPGA_FIFO深度与宽度的配置及验证

FPGA_FIFO深度与宽度的配置及验证
fifo是先进先出的存储器,在FPGA中应用于跨时钟域的情景,此次实验用于记载fifo的深度与宽度的配置及验证过程。

实验大致流程:

在fifo_wr模块中以wr_en时钟向FIFO存储器写入一组数,通过fifo_rd模块以rd_en时钟读出这组数据并向串口发送这组数据。
先用用Quartus II生成FIFO_IP核:
在这里插入图片描述箭头1:设置FIFO的位宽,这里我们选择8bits。
箭头2:设置FIFO的深度,也就是能存放多少个指定位宽的数据,这里我们选择256words,这样设置 以后FIFO的容量大小为256个8bits。

箭头3:用于选择单时钟FIFO
箭头4:用于选择双时钟FIFO。
箭头5:选择不同的输出位宽(仅在双时钟时可选)。
此次实验我们选择双时钟FIFO,这时在箭头5处可以选择不同的输出位宽。这里我们采用默认方式输出数据与输入数据等宽。

跳到DCFIFO 2这里:
在这里插入图片描述rdfull和wrfull:FIFO满的标记信号,高电平时表示FIFO已满,此时不能再进行写操作。

rdempty和wrempty:FIFO空的标记信号,高电平时表示FIFO已空,此时不能再进行读操作。

rdusedw[]和wrusedw[]:显示存储在FIFO中数据个数的信号。

Add an extra MSB to usedw ports:将rdusedw和wrusedw数据位宽增加1位,用于保护FIFO在写满时不会翻转到0。

Asynchronous clear:异步复位信号,用于清空FIFO。

这里我选择输出读空、读满、写空、写满等信号以备后面实验,至于rdusedw和wrusedw位宽加1、异步复位信号就没点,到这里FIFO的配置已经完成了,下面还需要一个UART串口发送的模块来配合实验。

//串口发送模块

module    uart_send
(
   input    sys_clk  ,            //50Mhz系统时钟
   input    sys_rst_n,            //系统复位,低有效
    input    uart_en,             //发送使能信号
    input    [7:0]    uart_din,   //待发送数据
    input    rdempty,             //fifo读空标志
    output  reg  uart_txd,        //发送数据
    output  reg  led              //指示灯
);
`
parameter  CLK_FREQ = 50000000;                    //系统时钟频率
parameter  UART_BPS = 115200;                      //串口波特率
localparam BPS_CNT  = CLK_FREQ/UART_BPS;           //对系统时钟计数BPS_CNT次以得到指定波特率

reg  [15:0] clk_cnt;       //系统时钟计数器
reg  [7:0] uart_data;      //接收的数据
reg       uart_done;       //接收一帧数据完成标志信号

//reg define
reg        uart_en_d0; 
reg        uart_en_d1;  
reg [ 3:0] tx_cnt;                    //发送数据计数器
reg        tx_flag;                   //发送过程标志信号
reg [ 7:0] tx_data;                   //寄存发送数据
reg  [31:0] cnt;                      //时钟计数器
reg   tx_delay;//wire define
wire       en_flag;

wire            wrreq   ;         // 写请求信号
wire    [7:0]   data    ;         // 写入FIFO的数据
wire            wrempty ;         // 写侧空信号
wire            wrfull  ;         // 写侧满信号
wire            wrusedw ;         // 写侧FIFO中的数据量

wire            rdreq   ;         // 读请求信号
wire    [7:0]   q       ;         // 从FIFO输出的数据
//wire            rdempty ;         // 读侧空信号
wire            rdfull  ;         // 读侧满信号
wire            rdusedw ;         // 读侧FIFO中的数据量

//*****************************************************
//**                    main code
//*****************************************************
assign en_flag = (~uart_en_d1) & uart_en_d0;    //& (~tx_delay);//消抖//按键消抖300ms
always @(posedge sys_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n)
        cnt <= 0;
    else if(cnt >= 31'd15_000_000) begin
        led <= 0;
        cnt <= 0;
        tx_delay <= 0;
    end
    else if(tx_flag == 1) begin
        tx_delay <= 1;
        led <= 1;
    end
    else if(tx_delay == 1)
        cnt <= cnt + 1'b1;
end//对发送使能信号uart_en延迟两个时钟周期
always @(posedge sys_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n) begin
        uart_en_d0 <= 1'b0;                                  
        uart_en_d1 <= 1'b0;
    end                                                      
    else begin

        uart_en_d0 <= uart_en;                               
        uart_en_d1 <= uart_en_d0;                            
    end
end
//当脉冲信号en_flag到达时,寄存待发送的数据,并进入发送过程          
always @(posedge sys_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n) begin                                  
        tx_flag <= 1'b0;
        tx_data <= 8'd0;
    end 
    else if (en_flag) begin                 //检测到发送使能上升沿                      
            tx_flag <= 1'b1;                //进入发送过程,标志位tx_flag拉高
            tx_data <= uart_din;            //寄存待发送的数据
        end
        else 
        if ((tx_cnt == 4'd9)&&(clk_cnt == BPS_CNT/2))
        begin                               //计数到停止位中间时,停止发送过程
            tx_flag <= 1'b0;                //发送过程结束,标志位tx_flag拉低
            tx_data <= 8'd0;
        end
        else begin
            tx_flag <= tx_flag;
            tx_data <= tx_data;
        end 
end

//进入发送过程后,启动系统时钟计数器与发送数据计数器
always @(posedge sys_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n) begin                             
        clk_cnt <= 16'd0;                                  
        tx_cnt  <= 4'd0;
    end                                                      
    else if (tx_flag) begin                 //处于发送过程
        if (clk_cnt < BPS_CNT - 1) begin
            clk_cnt <= clk_cnt + 1'b1;
            tx_cnt  <= tx_cnt;
        end
        else begin
            clk_cnt <= 16'd0;               //对系统时钟计数达一个波特率周期后清零
            tx_cnt  <= tx_cnt + 1'b1;       //此时发送数据计数器加1
        end
    end
    else begin                              //发送过程结束
        clk_cnt <= 16'd0;
        tx_cnt  <= 4'd0;
    end
end

//根据发送数据计数器来给uart发送端口赋值
always @(posedge sys_clk or negedge sys_rst_n) begin        
    if (!sys_rst_n)  
        uart_txd <= 1'b1;        
    else if (tx_flag&&~rdempty)
        case(tx_cnt)
            4'd0: uart_txd <= 1'b0;         //起始位 
            4'd1: uart_txd <= tx_data[0];   //数据位最低位
            4'd2: uart_txd <= tx_data[1];
            4'd3: uart_txd <= tx_data[2];
            4'd4: uart_txd <= tx_data[3];
            4'd5: uart_txd <= tx_data[4];
            4'd6: uart_txd <= tx_data[5];
            4'd7: uart_txd <= tx_data[6];
            4'd8: uart_txd <= tx_data[7];   //数据位最高位
            4'd9: uart_txd <= 1'b1;         //停止位
            default: ;
        endcase
     else if(tx_flag&&rdempty)              //读区为空,输出8'hCC
            case(tx_cnt)
            4'd0: uart_txd <= 1'b0;         //起始位 
            4'd1: uart_txd <= 1'b0;         //数据位最低位
            4'd2: uart_txd <= 1'b0;
            4'd3: uart_txd <= 1'b1;
            4'd4: uart_txd <= 1'b1;
            4'd5: uart_txd <= 1'b0;
            4'd6: uart_txd <= 1'b0;
            4'd7: uart_txd <= 1'b1;
            4'd8: uart_txd <= 1'b1;         //数据位最高位
            4'd9: uart_txd <= 1'b1;         //停止位
            default: ;
        endcase
    else 
        uart_txd <= 1'b1;                   //空闲时发送端口为高电平
end

endmodule

在这里说明一下,当读空信号成立时串口会发送:16进制数CC。另外还需要读写模块,如下:

//写FIFO模块
module fifo_wr(
    //mudule clock
    input                   clk    ,        // 时钟信号
    input                   rst_n  ,        // 复位信号
    //user interface
    input                   wrempty,        // 写空信号
    input                   wrfull ,        // 写满信号
    output    reg  [7:0]    data   ,        // 写入FIFO的数据
    output    reg           wrreq           // 写请求
);

//reg define
reg   [1:0]         flow_cnt;               // 状态流转计数
reg   [31:0]        delay_a;

//*****************************************************
//**                    main code
//*****************************************************

//向FIFO中写入数据
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
         wrreq <= 1'b0;
            delay_a <= 1'b0;
            data <= 1'b0;
         flow_cnt <= 2'd0;
    end
    else if(delay_a == 31'd4) begin
        delay_a <= 1'b0;
        data <= data + 1'b1;
    end
    else begin
        case(flow_cnt)
            2'd0: begin 
                if(wrempty) begin     //写空时,写请求拉高,跳到下一个状态
                    wrreq <= 1'b1;
                    flow_cnt <= flow_cnt + 1'b1;
                end 
                else
                    flow_cnt <= flow_cnt;
            end 
            2'd1: begin               //写满时,写请求拉低,跳回上一个状态
                if(wrfull) begin
                    wrreq <= 1'b0;
                    flow_cnt <= 2'd0;
                end
                else begin            //没有写满的时候,写请求拉高,继续输入数据
                    wrreq <= 1'b1;
                    delay_a <= delay_a + 1'b1;
                end
            end 
            default: flow_cnt <= 2'd0;
        endcase
    end
end


endmodule
//读FIFO模块

module fifo_rd(
    //system clock
    input                    clk    ,        // 时钟信号
    input                    rst_n  ,        // 复位信号(低有效)

    //user interface
    input           [7:0]    data   ,        // 从FIFO输出的数据
    input                    rdfull ,        // 读满信号
    input                    rdempty,        // 读空信号
    output   reg             rdreq          // 读请求
);

//reg define
reg   [7:0]                  data_fifo;      // 读取的FIFO数据
reg   [1:0]                  flow_cnt ;      // 状态流转计数

//*****************************************************
//**                    main code
//*****************************************************

     
//从FIFO中读取数据
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        rdreq <= 1'b0;
        data_fifo <= 8'd0;
    end
    else begin
        case(flow_cnt)
            2'd0: begin
                if(rdfull) begin
                    rdreq <= 1'b1;
                    flow_cnt <= flow_cnt + 1'b1;
                end
                else
                    flow_cnt <= flow_cnt;
            end 
            2'd1: begin
                if(rdempty) begin
                    rdreq <= 1'b0;
                    data_fifo <= 8'd0;
                    flow_cnt  <= 2'd0;
                end
                else begin
                    rdreq <= 1'b1;
                    data_fifo <= data;
                end 
            end 
            default: flow_cnt <= 2'd0;
        endcase
    end
end

endmodule

读写模块需要注意,我是以写满和写空为写操作的判断基准的,读操作的判断也是一样的,当然正常使用FIFO是不能这样做的,此次仅仅为了验证FIFO的宽度和深度。

以下是顶层模块,其中锁相环PLL并没有用到:

//fifo串口消息发送


module    STM32_UART
(
   input    sys_clk  ,    //50Mhz系统时钟
   input    sys_rst_n,    //系统复位,低有效
    input    uart_en,     //发送使能信号
    output   uart_txd,    //发
    input    uart_rxd,    //收
    output   reg led      //指示灯
);

//reg define
reg             wr_en;
reg                    rd_en;
reg    [31:0]   cnt;
reg    [31:0]   cnt_rd;
reg    [31:0]   cnt_led/* synthesis preserve */;
reg    [7:0]   data;

//wire define
wire    [7:0]   uart_data_w ;     // 接收到的数据
wire            wrreq   ;         // 写请求信号
//wire    [7:0]   data    ;         // 写入FIFO的数据
wire            wrempty ;         // 写侧空信号
wire            wrfull  ;         // 写侧满信号
wire            wrusedw ;         // 写侧FIFO中的数据量

wire            rdreq   ;         // 读请求信号
wire    [3:0]   q       ;         // 从FIFO输出的数据
wire            rdempty ;         // 读侧空信号
wire            rdfull  ;         // 读侧满信号
wire            rdusedw ;         // 读侧FIFO中的数据量

wire            clk_0/*synthesis keep*/;
wire            clk_1/*synthesis keep*/;



always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n) begin
        led <= 1'b0;
        cnt_led <= 1'b0;
    end
    else if(cnt_led == 5-1) begin
        cnt_led <= 1'b0;
        led <= ~led;
    end
    else begin
        cnt_led <= cnt_led + 1'b1;
        led <= led;
    end
end


always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n) begin
        wr_en <= 1'b0;
        cnt <= 1'b0;
    end
    else if(cnt == 31'd1_000_000) begin
        cnt <= 1'b0;
        wr_en <= 1'b1;
    end
    else begin
        cnt <= cnt + 1'b1;
        wr_en <= 1'b0;
    end
end

always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n) begin
        rd_en <= 1'b0;
        cnt_rd <= 1'b0;
    end
    else if(cnt_rd == 31'd2_000_000) begin
        cnt_rd <= 1'b0;
        rd_en <= 1'b1;
    end
    else begin
        cnt_rd <= cnt_rd + 1'b1;
        rd_en <= 1'b0;
    end
end

always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n)
        data <= 1'b0;
    else if(q != 0&&rdreq)
        data <= q;
    else if(rdreq==0)
        data <= 8'hee;
    else
        data <= 8'hff;
end

uart_send u_uart_send(
    .sys_clk    (sys_clk),
    .sys_rst_n  (sys_rst_n),
     .uart_en    (wr_en),        //发送使能信号
     .rdempty    (rdempty),      //fifo读空信号
     .uart_din   (data),         //待发送数据
     .uart_txd   (uart_txd),     //发送数据
);

//锁相环
pll_clk u_pll_clk(
.areset (~sys_rst_n ),           //锁相环高电平复位,所以复位信号取反
.inclk0 (sys_clk ),
.c0 (clk_0 ),
.c1 (clk_1),
.locked (locked )
);

//例化FIFO模块
fifo u_fifo(
    .wrclk   ( wr_en ),           // 写时钟
    .wrreq   ( wrreq   ),         // 写请求
    .data    ( uart_data_w    ),         // 写入FIFO的数据
    .wrempty ( wrempty ),         // 写空信号
    .wrfull  ( wrfull  ),         // 写满信号
    .wrusedw ( wrusedw ),         // 写侧数据量
    
    .rdclk   ( rd_en ),           // 读时钟
    .rdreq   ( rdreq   ),         // 读请求
    .q       ( q       ),         // 从FIFO输出的数据
    .rdempty ( rdempty ),         // 读空信号
    .rdfull  ( rdfull  ),         // 读满信号
    .rdusedw ( rdusedw )          // 读侧数据量
);

//例化写FIFO模块
fifo_wr u_fifo_wr(
    .clk     (wr_en  ),           // 写时钟
    .rst_n   (sys_rst_n),         // 复位信号

    .wrreq   (wrreq   ),          // 写请求
    .data    (uart_data_w    ),          // 写入FIFO的数据
    .wrempty (wrempty ),          // 写空信号
    .wrfull  (wrfull  )           // 写满信号
);

//例化读FIFO模块
fifo_rd u_fifo_rd(
    .clk     (rd_en ),            // 读时钟
    .rst_n   (sys_rst_n),         // 复位信号

    .rdreq   (rdreq   ),          // 读请求
    .data    (q       ),          // 从FIFO输出的数据
    .rdempty (rdempty ),          // 读空信号
    .rdfull  (rdfull  )           // 读满信号
);

endmodule

顶层模块对读出的数据做了简单处理,读出数据为0时输出为FF,读请求为0时输出EE。

16进制显示串口打印结果如下:
在这里插入图片描述下面贴个工程连接:
https://download.csdn.net/download/weixin_44007438/12897761
感兴趣的可以试试啦,有帮助的可以打个赏啦

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: async_fifo是一种用于数据传输的FIFO(先进先出)队列,其在UVM验证环境中的验证是非常重要的。 在进行async_fifo的UVM验证过程中,需要创建一个验证组件(agent),该组件主要由sequencer、driver和monitor组成。其中,sequencer负责向FIFO队列中发出数据请求,driver负责处理这些请求并将数据发送到FIFO队列中,monitor负责监测FIFO队列的状态和数据。 首先,需要编写驱动程序(driver),该驱动程序会从随机产生的数据生成交易,并将其发送到FIFO队列中。驱动程序还会处理来自环境的输入交易,并将它们传递给FIFO队列。通过监控程序(monitor)可以实时监测FIFO队列中的数据状态以及数据的正确性。 其次,需要编写一个产生随机交易的生成器(generator),用于测试FIFO队列的各种边界条件和异常情况。生成器可以随机生成一系列输入交易,并将其提供给驱动程序。 然后,需要编写一个转发器(sequencer),用于将生成器产生的交易传递给驱动程序,并处理来自驱动程序的数据请求。转发器可以控制交易的发送顺序以及产生的时序等。 最后,需要编写一个检查器(checker),以验证FIFO队列的输出数据是否正确。检查器可以对接收到的数据进行校验,并与预期结果进行比较来判断测试是否通过。 在整个验证过程中,需要编写相应的验证测试用例,覆盖不同的情况和边界条件,以确保async_fifo的正确性和稳定性。 综上所述,async_fifo的UVM验证主要涉及到创建验证组件、编写驱动程序、生成器、转发器和检查器,并通过编写相应的测试用例来验证FIFO队列的功能和性能。这样可以保证async_fifo的正确性,并提高其在实际应用中的可靠性。 ### 回答2: async_fifo是一种异步先进先出(FIFO)队列,在UVM验证环境中使用它可以有效地进行一些验证任务。在验证过程中,我们经常需要将一些数据交换或传递给不同的模块或组件。使用FIFO队列可以帮助我们管理输入和输出数据的顺序,以确保正确性和一致性。 在UVM中验证async_fifo,我们可以创建一个自定义的验证组件,该组件包含了FIFO队列。我们可以使用UVM提供的uvm_queue类来实现FIFO功能,该类提供了插入和提取数据项的方法,以及一些其他操作。 为了验证async_fifo,我们需要编写一些验证代码,以测试FIFO的正确性和性能。我们可以创建信号生成器来生成一系列随机的输入数据项,并将其插入到FIFO队列中。然后,我们可以创建一个监控器,用于检查FIFO队列的输出数据,并与预期结果进行比较。通过这种比较,我们可以验证async_fifo是否按照预期的顺序提取数据项。 此外,我们还可以使用UVM的事务和驱动机制来验证async_fifo的性能。我们可以创建一个驱动来向FIFO队列中连续插入数据项,并创建一个监控器来检查每个数据项的提取时间。通过分析提取时间,我们可以评估FIFO队列的性能和延迟,并确定是否满足设计要求。 总而言之,验证async_fifo是一个重要的任务,可以通过创建自定义的验证组件、使用uvm_queue类实现FIFO功能以及使用UVM的事务和驱动机制来完成。通过验证FIFO的正确性和性能,我们可以确保它在实际应用中的可靠性和稳定性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值