前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[十六]Cracking Digital VLSI Verification Interview

[十六]Cracking Digital VLSI Verification Interview

作者头像
空白的贝塔
发布2020-06-24 16:26:36
1.2K0
发布2020-06-24 16:26:36
举报
文章被收录于专栏:摸鱼范式摸鱼范式
[301] 什么是virtual sequence?如何使用virtual sequence?为什么要使用virtual sequence?

virtual sequence是控制多个sequencer中激励生成的序列。由于sequence,sequencer和driver集中在单个接口上,因此几乎所有测试平台都需要virtual sequence来协调不同接口之间的激励和交互。virtual sequence在子系统或系统级别的测试台上也很有用,可以使单元级别的sequence以协调的方式运行。下图从概念上展示了这一点,其中virtual sequence具有三个sequencer的句柄,这些sequencer连接到driver,以连接到DUT的三个独立接口。然后,virtual sequence可以在每个接口上生成subsequence,并在相应的subsequencer上运行它们。

[302] 如下所示,给定一个简单的单端口RAM,它可以读取或写入,请shiyong1UVM编写一个sequence和driver以测试他的读取和写入。假设read_enable = 1表示读取,而write_enable = 1表示写入。读或写操作只能在一个周期内发生。

要实现UVM driver和sequence,我们需要先定义sequence item类,然后sequence和driver类才能将其用作事务进行通信。以下是sequence item,sequence和driver类的示例代码。可以使用类似的方法使用UVM处理任何编程代码。

  • sequence item是用于sequence和driver之间通信的事务。应该抽象出最终需要在DUT上进行信号驱动的所有信息。
代码语言:javascript
复制
class rw_txn extends uvm_sequence_item;
    
    rand bit[7:0] addr; //address of transaction
    typedef enum {READ, WRITE} kind_e; //read or write type
    rand kind_e sram_cmd;
    rand bit[7:0] datain; //data
    
    //Register with factory for dynamic creation
    `uvm_object_utils(rw_txn)
    
    //constructor
    function new (string name = "rw_txn");
        super.new(name);
    endfunction
    
    //Print utility
    function string convert2string();
        return $psprintf("sram_cmd=%s addr=%0h datain=%0h,sram_cmd.name(),addr,datain);
    endfunction
                         
endclass
  • 生成上述类型的10个事务并发送给driver的sequence:
代码语言:javascript
复制
class sram_sequence extends uvm_sequence#(rw_txn) ; //Register with factory
    
    `uvm_object_utils(sram_sequence)
    
    function new(string name ="sram_sequence");
        super.new(name);
    endfunction
    
    //Main Body method that gets executed once sequence is started
    task body();
        rw_txn rw_trans; //Create 10 random SRAM read/write transaction and send to driver 
        repeat(10) begin
            rw_trans = rw_txn::type_id::create(.name("rw_trans"),.contxt(get_full_name())); 
            start_item(rw_trans); //start arbitration to sequence
            assert (rw_trans.randomize());//randomize item
            finish_item(rw_trans); //send to driver
        end
    endtask
    
endclass
  • driver代码,从sequence中接收上述事务并根据SRAM协议进行驱动。
代码语言:javascript
复制
class sram_driver extends uvm_driver#(rw_txn);
    `uvm_component_utils(sram_driver) 
    virtual sram_if vif; //Interface that groups dut signals
    function new(string name,uvm_component parent = null);
        super.new(name,parent);
    endfunction
    
    //Build Phase
    
    //Get the virtual interface handle from config_db
    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        if (!uvm_config_db#(virtual sram_if)::get(this, "", "sram_if", vif))
            begin
                `uvm_fatal("SRAM/DRV", "No virtual interface specified")
            end
    endfunction
    
    //Run Phase
    
    //Implement the Driver-Sequencer API to get an item
    //Based on if it is Read/Write - drive on SRAM interface the corresponding pins virtual 
    task run_phase(uvm_phase phase);
        super.run_phase(phase);
        this.vif.read_enable <= '0;
        this.vif.write_enable <= '0;
        forever begin
            rw_txn tr;
            @ (this.vif.master_cb);
            //First get an item from sequencer
            seq_item_port.get_next_item(tr);
            @ (this.vif.master_cb); //wait for a clock edge
            uvm_report_info("SRAM_DRIVER ", $psprintf("Got Transaction%s",tr.convert2string()));
            //Decode the SRAM Command and call either the read/write function 
            case (tr.sram_cmd)
                rw_txn::READ: drive_read(tr.addr, tr.dataout);
                rw_txn::WRITE: drive_write(tr.addr, tr.datain);
            endcase
            //Handshake DONE back to sequencer
            seq_item_port.item_done();
        end
     endtask: run_phase
                                                      
//Drive the SRAM signals needed for a Read
     virtual protected task drive_read(input bit [31:0] addr, output logic [31:0] data);
         this.vif.master_cb.addr <= addr;
         this.vif.master_cb.write_enable <= '0; 
         this.vif.master_cb.read_enable <= '1;
         @ (this.vif.master_cb);
         this.vif.master_cb.read_enable <= '0; 
         data = this.vif.master_cb.dataout;
     endtask: drive_read
                                                      
//Drive the SRAM signals needed for a Write
     virtual protected task drive_write(input bit [31:0] addr, input bit [31:0] data);
         this.vif.master_cb.addr <= addr; 
         this.vif.master_cb.write_enable <= '1; 
         this.vif.master_cb.read_enable <= '0;
         @ (this.vif.master_cb);
         this.vif.master_cb.write_enable <= '0;
     endtask: drive_write
endclass
[303] 什么是工厂?

UVM中的“工厂”是一个特殊的查找表,其中记录了所有UVM组件和事务。在UVM中创建组件和事务对象的推荐方法是使用工厂方法create()。使用工厂创建对象可以很方便地实现类型覆盖,而不必更改验证环境的结构或修改验证环境的代码。

[304] 使用new()和create()创建对象有什么区别?

UVM中用于创建组件或事务对象的推荐方法是使用内置方法::type_id::create(),而不是直接调用构造函数new()。

create方法在内部调用工厂,查找所请求的类型,然后调用构造函数new()来创建对象。这样可以轻松地重写类型,可以指定类的类型(基类,一个或派生类),并且所有其他测试平台组件将能够创建该类类型的对象而无需任何代码更改。

new()构造函数将仅创建给定类型的对象,因此使用new()将不允许在运行时更改类类型。因此,使用new()意味着测试平台代码将需要根据要使用的不同类型进行更改。

[305] 如何在工厂中注册uvm_component类和uvm_sequence类?

使用uvm_object_utils()宏注册uvm_sequence类,uvm_component_utils()宏注册uvm_component类,下面是示例代码

代码语言:javascript
复制
class test_seq_c extends uvm_sequence;
    `uvm_object_utils(test_seq_c)
    
class test_driver_c extends uvm_component;
    `uvm_component_utils(test_driver_c)
[306] 为什么要将类注册到工厂?

工厂是UVM中使用的一种特殊查找表,用于创建组件或事务类型的对象。使用工厂创建对象的好处是,测试平台构建可以在运行时决定创建哪种类型的对象。因此,一个类可以用另一个派生类替换,而无需任何实际代码更改。为确保此功能,建议所有类都在工厂注册。如果不注册到工厂,则将无法使用工厂方法::type_id::create()构造对象。

[307] 工厂覆盖(override)的意思是?

UVM工厂允许在构造时将一个类替换为另一个派生类。通过将一个类替换为另一个类而不需要编辑或重新编译测试平台代码,这对于控制测试平台的行为很有用。

[308] 工厂的实例覆盖(instance override)和类型覆盖(type override)有什么区别?

类型覆盖意味着每次在测试平台层次结构中创建组件类类型时,都会在其位置创建替代类型。这适用于该组件类型的所有实例。

另一方面,实例覆盖意味着仅覆盖组件类的特定实例。组件的特定实例由该组件在UVM组件层次结构中的位置进行索引。

由于只有UVM组件类可以在UVM测试平台中具有层次结构,因此实例覆盖只能作用于组件类,而sequence(或者说object)只能覆盖类型。

[309] 实例覆盖和类型覆盖都可以作用于UVM_component和transaction吗?

不,只有UVM_component类是UVM测试平台层次结构的一部分,从而可以使用实例覆盖。sequence_item或sequence不是UVM测试平台层次结构的一部分,因此只能使用类型覆盖来覆盖,类型覆盖将覆盖该类型的所有对象。

[310] uvm_obejction是什么?在哪里使用它们?

uvm_objection类提供了一种在多个组件和sequence之间共享计数器的方法。每个组件/sequence可以异步地"raise"和"drop" objections,这会增加或减少计数器值。当计数器达到零(从非零值开始)时,将发生"all dropped"情况。

objection机制最常用于UVM phase机制中,以协调每个run_time phase的结束。在phase中启动的用户进程会首先raise objections,并在进程完成后drop objections。当一个phase中的所有进程都放下objections时,该phase的objections计数器清零。这种“all dropped”的情况说明每个进程都同意结束该phase。

下面是一个示例,说明如何在sequencer(my_sequencer)上启动sequence(my_test_sequence)并在sequence执行后drop objections

代码语言:javascript
复制
task run_phase( uvm_phase phase);
    phase.raise_objection( this );
    my_test_sequence.start(my_sequencer);
    phase.drop_objection( this );
endtask
[311] 如何在UVM中实现仿真超时机制?

如果由于超出最大时间的某些错误而导致测试无法进行,那么仿真超时机制有助于停止仿真。在UVM中,set_global_timeout(timeout)是一个便捷函数,用于将uvm_top.phase_timeout变量设置为超时值。如果run()阶段在该这个时间内之前没有结束,则仿真将停止并报告错误。

在顶层模块中调用此函数,该模块会按以下方式启动测试

代码语言:javascript
复制
module test;
    initial begin
        set_global_timeout(1000ns);
    end
    
    initial begin
        run_test();
    end
endmodule
[312] uvm中的phase机制是什么意思?

与基于module的测试平台(所有module静态地存在于层次结构中)不同,基于类的测试平台需要管理不同对象的创建以及这些对象中各种task和function的执行。phase是基于类的测试平台中重要的概念,它具有一致的测试平台执行流程。从概念上讲,测试执行可以分为以下阶段-配置,创建测试平台组件,运行时激励和测试结束。UVM为每一个阶段中定义了标准phase。

[313] uvm_component有哪些phase?UVM的run_phase有哪些子phase?

UVM使用标准phase来排序仿真过程中发生的主要步骤。有三组阶段,按以下顺序执行。

  • Build phases-在Build phases;测试平台将会被配置和构建。它具有以下子phase,这些子phase都在uvm_component基类中作为虚方法实现。
    • build_phase()
    • connect_phase()
    • end_of_elaboration()
  • Run time phases-这些phase会消耗时间,这是大多数测试执行的地方。
    • pre_reset
    • reset
    • post_reset
    • pre_configure
    • configure
    • post_configure
    • pre_main
    • main
    • post_main
    • pre_shutdown
    • shutdown
    • post_shutdown
    • start_of_simulation()
    • run_phase(),run_phase()与12个子phase并行
  • Clean up phase -此phase 在测试结束后执行,用于收集并报告测试的结果和统计信息。包括以下子phase :
    • extract()
    • check()
    • report()
    • final()
[314] 为什么build_phase是自顶向下执行的?

在UVM中,所有组件(例如test,Env,Agent,Driver,Sequencer)都基于uvm_component类,并且组件始终具有层次结构。build_phase()方法是uvm_component类的一部分,用于从父组件构造所有子组件。因此,要构建测试平台层次结构,始终需要先拥有一个父对象,然后才能构造其子对象,并可以使用build_phase进一步构造其子对象。因此,build_phase()总是自顶向下执行。

例如:uvm_test类调用build_phase,构造该test的所有uvm_env组件,而每个uvm_env类的build_phase()应该构造属于该uvm_env的所有uvm_agent组件,然后继续。对于所有其他phase,调用顺序实际上并不重要。所有组件的run_phase()是并行运行的。

[315] uvm_component中的phase_ready_to_end()的作用是?

phase_ready_to_end(uvm_phase phase)是组件类的回调方法,当相应phase的所有objection均被放下并且该phase将要结束时,会调用该方法。组件类可以使用此回调方法来定义phase即将结束时需要执行的任何功能。

例如,如果某个组件希望将phase结束延迟到某个条件,甚至在所有objections均被放下之后,也可以使用此回调方法来完成。

再比如,如果一个激励或应答sequence正在运行,在主sequence结束之前,则可以使用main_phase()中的phase_ready_to_end()回调方法来停止那些激励或应答sequence。

[316] 什么是uvm_config_db?它的作用是?

uvm_config_db机制支持在不同的测试平台组件之间共享配置和参数。用名为uvm_config_db的配置数据库启用该功能。任何测试台组件都可以使用变量,参数,对象句柄等填充配置数据库。

其他测试平台组件可以从配置数据库访问这些变量,参数,对象句柄,而无需真正知道其在层次结构中的位置。

例如,顶层测试平台模块可以通过uvm_config_db储存虚接口句柄。然后,任何uvm_driver或uvm_monitor组件都可以查询uvm_config_db获取此虚接口的句柄,并将其用于通过接口实际访问信号。

[317] 如何使用uvm_config_db的get()和set()方法?

get()和set()是用于从uvm_config_db存储或检索信息的主要方法。任何验证组件都可以使用set()方法为config_db存储一些配置信息,还可以控制哪些其他组件对相同信息具有可见性。可以将其设置为具有全局可见性,或者仅对一个或多个特定测试平台组件可见。get()函数从数据库中检查与参数匹配的共享配置。

get()和set()方法的语法如下:

代码语言:javascript
复制
uvm_config_db#(<type>)::set(uvm_component context, string inst_name, string field_name,<type> value)
uvm_config_db#(<type>)::get(uvm_component context, string inst_name, string field_name, ref value)

context指定从中调用get / set的当前类或组件。inst_name是从中调用get / set的组件实例的名称。field_name是在config_db中设置/获取的对象或参数或变量的名称。<type>标识config_db中设置/获取的配置信息的类型。对于对象句柄,type是类名,而对于其他变量,type是数据类型名,代表了该变量的类型。

[318] 在验证平台层次结构中较低的组件是否可以使用get / set config方法将句柄传递给较高层次结构中的组件?

建议不要在UVM中这么做。通常,较高级别的组件使用句柄设置配置数据库,而较低级别的组件则使用get / set方法获取它们。

[319] 在UVM中,将虚接口分配给不同组件的最佳方法是什么?

实例化DUT和接口的顶级testbench模块在uvm_config_db中例化虚接口。然后,测试类或UVM组件层次结构中的任何其他组件可以使用get()方法查询uvm_config_db,获得此虚接口的句柄并将其用于访问信号。

下面展示了如何进行此操作。为APB总线master实例化了DUT和物理接口,然后,将虚接口句柄设置到uvm_config_db。

代码语言:javascript
复制
module test;
    logic pclk;
    logic [31:0] paddr;
    
    //Instantiate an APB bus master DUT
    apb_master apb_master(.pclk(pclk),*); 
    
    //Instantiate a physical interface for APB interface
    apb_if apb_if(.pclk(pclk), *); 
    
    initial begin //Pass this physical interface to test class top //which will further pass it down to env->agent->drv/sqr/mon
        uvm_config_db#(virtual apb_if)::set(null, "uvm_test_top", "vif",apb_if); 
    end 

endmodule

下面是APB Env类,该类使用uvm_config_db中的get()方法检索在顶层测试模块中设置的虚接口。

代码语言:javascript
复制
class apb_env extends uvm_env;
    `uvm_component_utils(apb_env); 
    
    //ENV class will have agent as its sub component
    apb_agent agt;
    
    //virtual interface for APB interface
    
    virtual apb_if vif; //Build phase - Construct agent and get virtual interface handle fromtest and pass it down to agent
    
    function void build_phase(uvm_phase phase);
        agt = apb_agent::type_id::create("agt", this);
        if (!uvm_config_db#(virtual apb_if)::get(this, "", "vif", vif))begin 
            `uvm_fatal("config_db_err", "No virtual interface specified forthis env instance") 
        end 
        uvm_config_db#(virtual apb_if)::set( this, "agt", "vif", vif); 
    endfunction: build_phase 

endclass : apb_env
[320] 在UVM中,如何结束仿真?

UVM具有phase机制,由一组构建阶段,运行阶段和检查阶段组成。在run()阶段进行实际的测试仿真,并且在此phase中,每个组件都可以在开始时raise_objection和drop_objection。一旦所有组件都drop_objection,则run_phase完成,然后所有组件的check_phase执行,然后测试结束。

这是正常仿真结束的方式,但是如果某些组件由于设计或测试平台中的错误而挂起,则仿真超时也可以终止run_phase。当run_phase启动时,并行超时计时器也会启动。如果在run_phase完成之前超时计时器达到指定的超时限制,则将发出一条错误消息,然后将执行run_phase之后的所有phase,最后测试结束。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-05-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 摸鱼范式 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • [301] 什么是virtual sequence?如何使用virtual sequence?为什么要使用virtual sequence?
  • [302] 如下所示,给定一个简单的单端口RAM,它可以读取或写入,请shiyong1UVM编写一个sequence和driver以测试他的读取和写入。假设read_enable = 1表示读取,而write_enable = 1表示写入。读或写操作只能在一个周期内发生。
  • [303] 什么是工厂?
  • [304] 使用new()和create()创建对象有什么区别?
  • [305] 如何在工厂中注册uvm_component类和uvm_sequence类?
  • [306] 为什么要将类注册到工厂?
  • [307] 工厂覆盖(override)的意思是?
  • [308] 工厂的实例覆盖(instance override)和类型覆盖(type override)有什么区别?
  • [309] 实例覆盖和类型覆盖都可以作用于UVM_component和transaction吗?
  • [310] uvm_obejction是什么?在哪里使用它们?
  • [311] 如何在UVM中实现仿真超时机制?
  • [312] uvm中的phase机制是什么意思?
  • [313] uvm_component有哪些phase?UVM的run_phase有哪些子phase?
  • [314] 为什么build_phase是自顶向下执行的?
  • [315] uvm_component中的phase_ready_to_end()的作用是?
  • [316] 什么是uvm_config_db?它的作用是?
  • [317] 如何使用uvm_config_db的get()和set()方法?
  • [318] 在验证平台层次结构中较低的组件是否可以使用get / set config方法将句柄传递给较高层次结构中的组件?
  • [319] 在UVM中,将虚接口分配给不同组件的最佳方法是什么?
  • [320] 在UVM中,如何结束仿真?
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档