UVM知识点7-Clocking block在验证中的正确使用

Clocking blocks在UVM的验证中使用非常重要,主要用于对输入的激励驱动和对输出的采样,但是很多人可能都不知道。

如下图所示,理想情况下,我们会在图中蓝线处进行驱动或者采样,从而尽可能避免竞争冒险。
在这里插入图片描述

SystemVerilog考虑到了这一点,可以参考寄存器时序逻辑运行的方式,即将所有的输出端都进行寄存来尽可能避免验证环境中的竞争冒险。因为毕竟建立时间和保持时间等时序方面的验证不该由DV来负责,也就是说说默认情况下,我们DV要假定该RTL将来综合后的电路在timing上没有问题,因此我们可以考虑利用Clocking blocks来构造一个理想的驱动和采样interface的环境。
在这里插入图片描述

上图所示,举个例子,一般情况下一个cycle中大部分,比如80%的时间用于输入端的组合逻辑的路径延迟,剩余的20%的cycle时间用于寄存和输出。

那么以上面为例,如何来使用Clocking blocks呢?

直接来看代码:

clocking cb1 @(posedge clk)
    default input #1step output (`CYCLE * 0.2);
    input <list of all inputs> ;
    output <list of all outputs>;
endclocking

在我们的env环境中,寄存输出的值即是我们要驱动到DUT interface上的激励,而env环境中的输入,则需要在interface上采样并且被传递到testbench中。

最佳采样DUT interface端数据的时间,是刚好在下一个时钟跳变沿改变输出端数据之前进行采样,这个时候应该是最稳定的时候,只是这里的input我们使用了#1step的延迟,即相当于仿真时间精度。

最佳驱动时间,应该是在时钟跳变沿之前的10%~20%,因为那个时候激励也最稳定,要在DUT采样interface之前预留一些时间。

下面来看个例子,首先看这里的interface。

interface dut_if (input clk);
    logic [15:0] dout;
    logic [15:0] din;
    logic ld, inc, rst_n;

    clocking cb1 @(posedge clk);
        default input #1step output `Tdrive;
        input dout;
        output din;
        output ld, inc, rst_n;
    endclocking
endinterface

驱动interface
下面来看如何在driver里将transaction驱动到interface上。

task drive_tr (trans1 tr);
@vif.cb1;
vif.cb1.din <= tr.din;
vif.cb1.ld <= tr.ld;
vif.cb1.inc <= tr.inc;
vif.cb1.rst_n <= tr.rst_n;
endtask
很简单,我们通过Clocking blocks来完成输出端的驱动。

通常我们在driver里驱动之前会对interface做一下初始化,比如这里一般会使用initialize方法。

如果是前序模块给的输出的话,那么需要置成对应的有效值,否则初始化为x不定态来模拟最真实的场景,注意这需要EDA工具提供进一步的支持。

class tb_driver extends uvm_driver #(trans1);
    `uvm_component_utils(tb_driver)
    virtual dut_if vif;

    function new (string name, uvm_component parent);
        super.new(name, parent);
    endfunction

    task run_phase(uvm_phase phase);
        trans1 tr;
        initialize();
        forever begin
        seq_item_port.get_next_item(tr);
        drive_tr(tr);
        seq_item_port.item_done();
        end
    endtask

    task initialize(); // @0 - Does not use clocking block
        vif.rst_n <= '0;
        vif.ld <= '1;
        vif.inc <= '1;
        vif.din <= 'dx;
    endtask

    task drive_tr (trans1 tr);
        @vif.cb1;
        vif.cb1.din <= tr.din;
        vif.cb1.ld <= tr.ld;
        vif.cb1.inc <= tr.inc;
        vif.cb1.rst_n <= tr.rst_n;
    endtask
endclass

监测interface
下面再来看如何采样interface上的信号。

class tb_monitor extends uvm_monitor;
    `uvm_component_utils(tb_monitor)
    virtual dut_if vif;
    uvm_analysis_port #(trans1) aport;

    function new (string name, uvm_component parent);
    super.new(name, parent);
    endfunction

    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        aport = new("aport", this); // build the analysis port
    endfunction

    task run_phase(uvm_phase phase);
        trans1 tr;
        tr = trans1::type_id::create("tr");
        //---------------------------------------
        forever begin
            sample_dut(tr);
            aport.write(tr);
        end
    endtask
    //-----------------------------------------------
    // sample_dut assumed to be synced to posedge clk
    // except for first sample at time-0
    //-----------------------------------------------
    task sample_dut (output trans1 tr);
        trans1 t = trans1::type_id::create("t");
        //---------------------------------------------
        // Sample DUT synchronous inputs on posedge clk.
        // DUT inputs should have been valid for most
        // of the previous clock cycle
        //---------------------------------------------
        t.din = vif.din;
        t.ld = vif.ld;
        t.inc = vif.inc;
        t.rst_n = vif.rst_n;
        //---------------------------------------------
        // Wait for posdege clk and sample outputs #1step before.
        // Also re-sample and check async control input signals
        //---------------------------------------------
        @vif.cb1;
        if (!vif.rst_n) t.rst_n = '0; // async reset
        t.dout = vif.cb1.dout;
        //---------------------------------------------
        tr = t;
    endtask
endclass

如果monitor的信号在clocking block是output方向的话(对应的即RTL的input方向),则使用t.xxx = vif.xxx的语句先monitor到interface上的信号值,然后等时钟沿@vif.cb1来进行同步,那么可以理解为在时钟上升沿到来之前,output方向的信号已经稳定(意味着RTL的输入端激励在时钟上升沿到来之前已经稳定),所以在时钟沿之前进行阻塞完成赋值。

然后对于input方向的信号dout(对应RTL的output方向),则使用cb1的三段式来赋值,即t.dout = vif.cb1.dout,主要是考虑RTL的output应该在时钟上升沿之后才会将有效的结果输出值驱动到interface上,因此这里需要等待1step的延迟之后通过阻塞赋值来monitor到RTL输出端驱动到interface上的稳定的值。

因此需要区分不同interface的类型,采用不同的写法。

但问题来了,如下图所示,如果这个rst_n是个异步复位且刚好在一个cycle之间进行了异步复位,那么如何monitor到该动作呢?
在这里插入图片描述

那么首先我们需要对interface做如下修改:

interface dut_if (input clk);
    logic [15:0] dout;
    logic [15:0] din;
    logic ld, inc, rst_n;
    logic reset_n;
    //----------------------------------------------------
    // Sticky reset_n signal to capture short rst_n pulses
    //----------------------------------------------------
    always_ff @(posedge clk, negedge rst_n)
        if (!rst_n) reset_n <= '0;
        else reset_n <= '1;
    clocking cb1 @(posedge clk);
        default input #1step output `Tdrive;
        input dout;
        output din;
        output ld, inc, rst_n;
        input reset_n;
    endclocking
endinterface

然后再来修改monitor。

class tb_monitor extends uvm_monitor;
    `uvm_component_utils(tb_monitor)
    ...
    task run_phase(uvm_phase phase);
        trans1 tr;
        tr = trans1::type_id::create("tr");
        //---------------------------------------
        forever begin
            sample_dut(tr);
            aport.write(tr);
        end
    endtask

    task sample_dut (output trans1 tr);
        trans1 t = trans1::type_id::create("t");
        t.din = vif.din;
        t.ld = vif.ld;
        t.inc = vif.inc;
        t.rst_n = vif.rst_n;
        //---------------------------------------------
        // ...
        // Sample the sticky-bit reset_n to update rst_n if needed
        //---------------------------------------------
        @vif.cb1;
        if (!vif.cb1.reset_n) t.rst_n = '0; // async reset
        t.dout = vif.cb1.dout;
        //---------------------------------------------
        tr = t;
    endtask
endclass

https://zhuanlan.zhihu.com/p/382248330

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值