UVM学习之路(5)— 完整的UVM验证平台

一、前言

一个完整的UVM验证平台还应该加入寄存器模型,对应的设计文件中也应该存在寄存器及其控制端口, 通过该控制端口可以配置DUT中的寄存器。

二、设计模块

设计文件添加寄存器模块后信号列表如下所示:

python生成uvm测试平台 如何用uvm搭建验证平台_git


寄存器列表如下所示

python生成uvm测试平台 如何用uvm搭建验证平台_python生成uvm测试平台_02

三、验证环境

基于UVM搭建的验证环境如下所示

python生成uvm测试平台 如何用uvm搭建验证平台_uvm_03


其中sequence组织方式如下所示

python生成uvm测试平台 如何用uvm搭建验证平台_编写代码_04

四、基本任务

寄存器模型提供了两个基本的readwrite任务, 来设置寄存器模型和dut中寄存器的值,如下所示:

/*case0.sv*/
	    virtual task body();
        uvm_status_e status;
        uvm_reg_data_t value;

        if(starting_phase != null)
            starting_phase.raise_objection(this);
        
        #1000;
        // set value of registers via uvm_reg::write()
        p_sequencer.p_rm.invert.write(status, {15'h00, 1'b1});
        p_sequencer.p_rm.counter.write(status, 32'h2000ff);

        // read out the value form register
        p_sequencer.p_rm.invert.read(status, value);
        `uvm_info("case0_cfg_vseq", $sformatf("after write, invert's value is %0h", value), UVM_LOW)
        p_sequencer.p_rm.counter.read(status, value);
        `uvm_info("case0_cfg_vseq", $sformatf("after write, counter's value is %0h", value), UVM_LOW)
    
        #5000;
        if(starting_phase != null)
            starting_phase.drop_objection(this);
    endtask

然后我们可以通过打印信息以及波形看到, invert的值写入成功了,而counter的值没有设置为32'h2000ff,这实际上是正确的,因为counter为写1清零寄存器。

python生成uvm测试平台 如何用uvm搭建验证平台_git_05

python生成uvm测试平台 如何用uvm搭建验证平台_git_06

五、后门访问

UVM提供两类后门访问的函数:

  • UVM_BACKDOOR形式的read和write;
  • peek和poke函数。

这两类函数的区别是, 第一类会在进行操作时模仿DUT的行为, 第二类则完全不管DUT的行为。
如对一个只读的寄存器进行写操作, 那么第一类由于要模拟DUT的只读行为, 所以是写不进去的, 但是使用第二类可以写进去。编写代码如下所示:

/*case1.sv*/
	virtual task body();
        uvm_status_e status;
        uvm_reg_data_t value;

        if(starting_phase != null)
            starting_phase.raise_objection(this);
        
        #1000;

        // set value of registers via uvm_reg::poke() for UVM_BACKDOOR
        p_sequencer.p_rm.invert.poke(status, {15'h00, 1'b0});
        p_sequencer.p_rm.counter.poke(status, {15'h00, 32'h2000f0});
        // read out the value form register
        p_sequencer.p_rm.invert.peek(status, value);
        `uvm_info("case1_cfg_vseq", $sformatf("after poke, invert's value is %0h", value), UVM_LOW)
        p_sequencer.p_rm.counter.peek(status, value);
        `uvm_info("case1_cfg_vseq", $sformatf("after poke, counter's value is %0h", value), UVM_LOW)

        // set value of registers via uvm_reg::write() for UVM_BACKDOOR
        p_sequencer.p_rm.invert.write(status, {15'h00, 1'b1}, UVM_BACKDOOR);
        p_sequencer.p_rm.counter.write(status, 32'h2000f0, UVM_BACKDOOR);
        // read out the value form register
        p_sequencer.p_rm.invert.read(status, value);
        `uvm_info("case1_cfg_vseq", $sformatf("after UVM_BACKDOOR write, invert's value is %0h", value), UVM_LOW)
        p_sequencer.p_rm.counter.read(status, value);
        `uvm_info("case1_cfg_vseq", $sformatf("after UVM_BACKDOOR write, counter's value is %0h", value), UVM_LOW)

        #5000;
        if(starting_phase != null)
            starting_phase.drop_objection(this);
    endtask

然后我们可以通过打印信息以及波形看到, 通过poke,invertcounter的值都被直接改变了,而通过write的UVM_BACKDOOR访问,invert的值写入成功了,而counter的值被写1清零了。

python生成uvm测试平台 如何用uvm搭建验证平台_git_07


python生成uvm测试平台 如何用uvm搭建验证平台_uvm_08

六、期望值与镜像值

镜像值(mirrored value): 寄存器模型中都会有一个专门的变量用于最大可能地与DUT保持同步, 这个变量在寄存器模型中称为DUT的镜像值;
期望值(desired value): 当希望向某个寄存器中写入一个值时,例如'h1,我们就可以通过set函数将期望值设置为’h1( 此时镜像值依然为0), 之后调用update任务, update任务会检查期望值和镜像值是否一致, 如果不一致, 那么将会把期望值写入DUT中, 并且更新镜像值。

另外,在调用write任务时, 期望值与镜像值都会更新。
编写代码如下所示:

/*case2.sv*/
	virtual task body();
        uvm_status_e status;
        uvm_reg_data_t value;

		if(starting_phase != null)
			starting_phase.raise_objection(this);
        
        #1000;

        p_sequencer.p_rm.invert.set(16'h1);
        value = p_sequencer.p_rm.invert.get();
        `uvm_info("case2_cfg_vseq", $sformatf("invert's desired value is %0h", value), UVM_LOW)
        
        value = p_sequencer.p_rm.invert.get_mirrored_value();
        `uvm_info("case2_cfg_vseq", $sformatf("invert's mirrored value is %0h", value), UVM_LOW)
        
        p_sequencer.p_rm.invert.update(status, UVM_FRONTDOOR);
        value = p_sequencer.p_rm.invert.get();
        `uvm_info("case2_cfg_vseq", $sformatf("invert's desired value is %0h", value), UVM_LOW)

        value = p_sequencer.p_rm.invert.get_mirrored_value();
        `uvm_info("case2_cfg_vseq", $sformatf("invert's mirrored value is %0h", value), UVM_LOW)
        p_sequencer.p_rm.invert.read(status, value);
        `uvm_info("case2_cfg_vseq", $sformatf("invert's actual value is %0h", value), UVM_LOW)
		
        #5000;
		if(starting_phase != null)
			starting_phase.drop_objection(this);
	endtask

打印信息如下所示

python生成uvm测试平台 如何用uvm搭建验证平台_uvm_09

七、附录

本篇中的UVM验证平台源码:https://gitee.com/william_william/uvm-s07.git