UVM(一)

UVM

  • 验证平台中所有的组件应该派生自UVM中的类;
    • 每一个派生自uvm_component或其派生类的类在其new函数中要指明两个参数:name和parent,这是uvm_component类的一大特征。
      • uvm_driver是一个派生自uvm_component的类,所以也会有这两个参数。
      • 一个是string类型的name;
      • 一个是uvm_component类型的parent;
  • 所有派生自uvm_component及其派生类的类都应该使用uvm_component_utils宏注册
    • 在UVM验证平台中,只 要一个类使用uvm_component_utils注册且此类被实例化了,那么这个类的main_phase就会自动被调用。
  • UVM中通过objection机制来控制验证平台的关闭。
    • 在每个phase中,UVM会检查是否有objection被提起 (raise_objection);
    • 如果有,那么等待这个objection被撤销(drop_objection)后停止仿真;
    • 如果没有,则马上结束当前phase;
    • 这个起效的前提是使用run_test来运行;
  • raise_objection语句必须在main_phase中第一个消耗仿真时间的语句之前。
    • 例如,display,`uvm_info;这些语句可以定义在raise_objection之前
    • 如果在raise_objection前使用了延时语句,raise_objection将可能失效;
  • 从根本上来说,应该尽量杜绝在验证平台中使用绝对路径。
  • 在UVM中,所有的transaction都 要从uvm_sequence_item派生;
    • 只有从uvm_sequence_item派生的transaction才可以使用后文讲述的UVM中强大的sequence机制。
  • 从本质上来说,my_transaction与 my_driver是有区别的:
    • 在整个仿真期间,my_driver是一直存在的;
    • my_transaction不同,它有生命周期;
      • 它在仿真的某一时间产生,经过driver驱动,再经过reference model处理,最终由scoreboard比较完成后,其生命周期就结束了。
  • transaction类都是派生自uvm_object或者uvm_object的派生类,uvm_sequence_item的祖先就是uvm_object。
    • UVM中具有这种特征的类都要使用 uvm_object_utils宏来实现。
  • 验证平台中的组件在实例化时都应该使用 type_name::type_id::create的方式
    • 即通过注册在factory中注册表的代理句柄,调用代理类的create方法来实现创建对象;
    • 这个过程中type_id是通过宏定义`uvm_component_utils使用typedef定义在每一个需要调用或创建的组件类之中的;
    • create传递两个参数,第一个参数是名字name,用于构造函数中划定层次,第二个参数传递的是parent;
      • 每一个派生自uvm_component或其派生类的类在其new函数中要指明两个参数:name和parent,这是uvm_component类的一大特征。
      • parent并不是其父类,而是一个父结点;
        • new函数有两个参数,第一个参数是实例的名字,第二个则是parent。
        • 由于driver在env中实例化,所以driver 的父结点(parent)就是env。
        • 通过parent的形式,UVM建立起了树形的组织结构。
        • 在这种树形的组织结构中,由run_test创建 的实例是树根(这里是env),并且树根的名字是固定的,为uvm_test_top
        • 在树根之后会生长出枝叶,长出枝叶的过程需要在env的build_phase中手动实现。
        • 无论是树根还是树叶,都必须由 uvm_component或者其派生类继承而来。整棵UVM树的结构如图所示。
        • image

           

        • 只有uvm_component才能作为树的结点;

          • transaction这种使用uvm_object_utils宏实现的类是不能作为UVM树的结点的;

        •  在my_env的build_phase中,创建i_agt和o_agt的实例是在 build_phase中;在agent中,创建driver和monitor的实例也是在build_phase中。

        • 按照前文所述的build_phase的从树根到树叶的执行顺序,可以建立一棵完整的UVM树。

        • UVM要求UVM树最晚在build_phase时段完成;
          • 因此,在build_phase后某个phase实例化一个 component,例如在main_phase中调用type_name::type_id::create
          • 这种情况会报错;
        • build_phase在new之后,main之前执行,因此可以在构造函数里使用type_name::type_id::create;
        • 是UVM中约定俗成的还是在build_phase中完成实例化工作。因此,强烈建议仅在build_phase中完成实例化。
  • 所有的monitor类应该派生自uvm_monitor;
    • 与driver类似,在monitor中也需要有一个 virtual if;
    • uvm_monitor在整个仿真中是一直存在的,所以它是一个component,要使用uvm_component_utils宏注册;
    • 由于monitor需要时刻收集数据,永不停歇,所以在main_phase中使用while(1)循环来实现这一目的。
  • 上级类(结点类)中build_phase阶段主要负责创建组件类,组件类的build_phase可能用于传递接口;
    • 具体传递虚拟接口的过程还是需要在外部通过config db set传递进去;
  • driver和monitor之间的代码高度相似。本质是因为二者 处理的是同一种协议,在同样一套既定的规则下做着不同的事情。
  • 由于二者的这种相似性,UVM中通常将二者封装在一起,成为 一个agent。
    • 因此,不同的agent就代表了不同的协议。
  • 所有的agent都要派生自uvm_agent类,且其本身是一个component,应该使用uvm_component_utils宏来实现factory注册。
  • 在UVM中,通常使用TLM(Transaction Level Modeling)实现component之间transaction级别 的通信。
  • connect_phase也是UVM内建的一个phase,它在build_phase执行 完成之后马上执行;
    • 但是与build_phase不同的是,它的执行顺序并不是从树根到树叶,而是从树叶到树根
      • 先执行driver和 monitor的connect_phase,再执行agent的connect_phase,最后执行env的connect_phase。
  • 为什么这里需要一个fifo呢?
    • 不能直接把my_monitor中的analysis_port和my_model中的blocking_get_port相连吗?
    • 由于 analysis_port是非阻塞性质的,ap.write函数调用完成后马上返回,不会等待数据被接收。
    • 假如当write函数调用时, blocking_get_port正在忙于其他事情,而没有准备好接收新的数据时,
    • 此时被write函数写入的my_transaction就需要一个暂存的位置,这就是fifo。
    • 与monitor中的ap不同的是,不需要对agent中的ap进行实例化,而只需要在agent的connect_phase中将monitor的值赋给它,换句话说,这相当于是一个指向monitor的ap的指针:

uvm_info宏

  • 这个宏的功能与Verilog中display语句的功能类似,但是它比display语句更加强大。
  • 它有三个参数,第一个参数是字符串,用于把打印的信息归类;第二个参数也是字符串,是具体需要打印的信息;第三个参数则是冗余级别。
  • 在验证平台中,某些信息是非常关键的,这样的信息可以设置为UVM_LOW,而有些信息可有可无,就可以设置为 UVM_HIGH,介于两者之间的就是UVM_MEDIUM。
  • UVM默认只显示UVM_MEDIUM或者UVM_LOW的信息
  • `uvm_info("my_driver", "data is drived", UVM_LOW)
  • 在uvm_info宏打印的结果中有如下几项:
    • UVM_INFO关键字:表明这是一个uvm_info宏打印的结果。除了uvm_info宏外,还有uvm_error宏、uvm_warning宏。
    • my_driver.sv(20):指明此条打印信息的来源,其中括号里的数字表示原始的uvm_info打印语句在my_driver.sv中的行号。
    • 48500000:表明此条信息的打印时间。
    • drv:这是driver在UVM树中的路径索引。(这里打印出来的是构造函数中,传向超类的name)
      • UVM采用树形结构,对于树中任何一个结点,都有一个与其相应的字符串类型的 路径索引。
      • 路径索引可以通过get_full_name函数来获取;
      • 把下列代码加入任何UVM树的结点中就可以得知当前结点的路径索引:(这里打印出来的也是构造函数中,传向超类的name)
      • $display("the full name of current component is: %s", get_full_name());
    • [my_driver]:方括号中显示的信息即调用uvm_info宏时传递的第一个参数;
    • data is drived:表明宏最终打印的信息。
    • `timescale 1ns/1ps
      `include "uvm_macros.svh"
      
      import uvm_pkg::*;
      `include "my_driver.sv"
      
      module top_tb;
      
      reg clk;
      reg rst_n;
      reg[7:0] rxd;
      reg rx_dv;
      wire[7:0] txd;
      wire tx_en;
      
      dut my_dut(.clk(clk),
                 .rst_n(rst_n),
                 .rxd(rxd),
                 .rx_dv(rx_dv),
                 .txd(txd),
                 .tx_en(tx_en));
      
      initial begin
         my_driver drv;
         drv = new("drv", null);
         drv.main_phase(null);
         $finish();
      end
      
      initial begin
         clk = 0;
         forever begin
            #100 clk = ~clk;
         end
      end
      
      initial begin
         rst_n = 1'b0;
         #1000;
         rst_n = 1'b1;
      end
      
      endmodule
      

        

宏定义

`uvm_component_utils(class_name)

  传递一个类名;

  这个宏通向代理类和工厂类;

  默认情况下代理类拥有一个代理类类型的悬空句柄,使用静态声明;在仿真开始的0时刻,编译器对部分静态变量初始化,对该句柄创建对象;

  同时,引动工厂类创建对象;

  创建完两个对象后,代理类对象调用工厂类对象,讲代理类的句柄存入工程类的关联数组中,索引名为class_name的字符串格式;

  当外界需要调用该对象时,会通过代理类创建对象,具体过程为,外界通过调用工程类的创建对象方法,工程类则根据传入的类名从关联数组中搜寻存好的代理类,并调用该代理类的创建对象方法;

  最终返回需要调用对象的句柄。

task uvm_root::run_test(string test_name="");

virtual task run_test (
 string test_name = "")

  将所有组件按注册阶段依次执行。

  如果提供了可选的test_name参数,或在命令行中检测到额外参数参数+UVM_TESTNAME=TEST_NAME,则会在阶段执行前创建指定组件。

  该测试可能包含新的验证组件或整个测试平台,通过命令行即可选择测试内容与测试平台,无需重新编译。

  若全局(包)变量finish_on_completion被设置,阶段执行完成后将调用$finish结束仿真。

  使用前提:该类需要通过`uvm_component_utils完成在factory内的注册;

  作用:

    1.创建指定类名的实例;

    2.调用该实例的main_phase方法;

    ***3.run_test语句本质上例化了以后超脱了top_tb层次结构的实例,建立了一个新的层次结构。

      这使得我们无法观测,利用通过传递类名创建的句柄;也无法通过这个句柄去操作其中的成员变量、调用其中的成员方法;

      取而代之的交互手段是config_db机制;

task uvm_root::
task uvm_root::run_test(string test_name="");
(string test_name=""); uvm_factory factory= uvm_factory::get(); bit testname_plusarg; int test_name_count; string test_names[$]; string msg; uvm_component uvm_test_top; process phase_runner_proc; // store thread forked below for final cleanup testname_plusarg = 0; // Set up the process that decouples the thread that drops objections from // the process that processes drop/all_dropped objections. Thus, if the // original calling thread (the "dropper") gets killed, it does not affect // drain-time and propagation of the drop up the hierarchy. // Needs to be done in run_test since it needs to be in an // initial block to fork a process. uvm_objection::m_init_objections(); `ifndef UVM_NO_DPI // Retrieve the test names provided on the command line. Command line // overrides the argument. test_name_count = clp.get_arg_values("+UVM_TESTNAME=", test_names); // If at least one, use first in queue. if (test_name_count > 0) begin test_name = test_names[0]; testname_plusarg = 1; end // If multiple, provided the warning giving the number, which one will be // used and the complete list. if (test_name_count > 1) begin string test_list; string sep; for (int i = 0; i < test_names.size(); i++) begin if (i != 0) sep = ", "; test_list = {test_list, sep, test_names[i]}; end uvm_report_warning("MULTTST", $sformatf("Multiple (%0d) +UVM_TESTNAME arguments provided on the command line. '%s' will be used. Provided list: %s.", test_name_count, test_name, test_list), UVM_NONE); end `else // plusarg overrides argument if ($value$plusargs("UVM_TESTNAME=%s", test_name)) begin `uvm_info("NO_DPI_TSTNAME", "UVM_NO_DPI defined--getting UVM_TESTNAME directly, without DPI", UVM_NONE) testname_plusarg = 1; end `endif // if test now defined, create it using common factory if (test_name != "") begin if(m_children.exists("uvm_test_top")) begin uvm_report_fatal("TTINST", "An uvm_test_top already exists via a previous call to run_test", UVM_NONE); #0; // forces shutdown because $finish is forked end $cast(uvm_test_top, factory.create_component_by_name(test_name, "", "uvm_test_top", null)); if (uvm_test_top == null) begin msg = testname_plusarg ? {"command line +UVM_TESTNAME=",test_name} : {"call to run_test(",test_name,")"}; uvm_report_fatal("INVTST", {"Requested test from ",msg, " not found." }, UVM_NONE); end end if (m_children.num() == 0) begin uvm_report_fatal("NOCOMP", {"No components instantiated. You must either instantiate", " at least one component before calling run_test or use", " run_test to do so. To run a test using run_test,", " use +UVM_TESTNAME or supply the test name in", " the argument to run_test(). Exiting simulation."}, UVM_NONE); return; end begin if(test_name=="") uvm_report_info("RNTST", "Running test ...", UVM_LOW); else if (test_name == uvm_test_top.get_type_name()) uvm_report_info("RNTST", {"Running test ",test_name,"..."}, UVM_LOW); else uvm_report_info("RNTST", {"Running test ",uvm_test_top.get_type_name()," (via factory override for test \"",test_name,"\")..."}, UVM_LOW); end // phase runner, isolated from calling process fork begin // spawn the phase runner task phase_runner_proc = process::self(); uvm_phase::m_run_phases(); end join_none #0; // let the phase runner start wait (m_phase_all_done == 1); // clean up after ourselves phase_runner_proc.kill(); report_summarize(); if (finish_on_completion) $finish; endtask

 

main_phase

  组成:

    1.phase.raise_objection(this);

    2.实现业务代码;

    3.phase.drop_objection(this);

objection机制

  • UVM中通过objection机制来控制验证平台的关闭。
  • 阶段图中基于任务的阶段节点提供了一个基于uvm_objection的界面,用于延长阶段的执行时间。
  • 所有其他阶段类型不包含异议,如果用户尝试加注、放弃或get_objection_count,则会报告致命错误。
  • 每个phase中,UVM会检查是否有objection被提起(raise_objection):
    • 如果有,则等待当前objection被撤销(drop_objection)后结束仿真;
    • 如果没有,则立即结束当前的phase。
  • 在这个过程中,objection的零延时语句可能在一开始就执行完成;

raise_objection

  • virtual function void raise_objection ( uvm_object obj, string description = "", int count = 1 )
  • 对结束此阶段提出异议 为组件提供对阶段流的更大控制,以应对非该阶段的隐式反对者的进程。
  • raise_objection语句必须在main_phase中第一个消耗仿真时间的语句之前。

drop_objection

  • 【撤销对结束当前阶段的异议】该撤销操作应与之前提出的异议对应。
virtual function void drop_objection (
 uvm_object obj, string description = "", int count = 1
)

 

UVM Resource Database

基本介绍:

  • uvm_resource_db类为resource机制提供了便捷的操作接口。
  • 在许多情况下,若直接使用uvm_resource_base或uvm_resource#(T)中的接口,创建和设置资源或获取资源等基础操作可能需要多行代码。
  • 而uvm_resource_db中的便捷层将这些操作简化为单行代码。
  • 若在命令行中指定运行时选项+UVM_RESOURCE_DB_TRACE,系统将显示所有资源数据库的访问记录(包括读写操作)。

uvm_resource_db

  •  uvm_resource_db#(T)中的所有函数均为静态方法,因此必须使用::操作符调用。例如:
  • uvm_resource_db#(int)::set("A", "*", 17, this);

 

  • 参数值"int"表明该资源类型为uvm_resource#(int),因此资源容器中的对象类型为int。这确保了资源操作的类型安全特性。

 

 

UVM Configuration Database——config_db机制——传递虚拟接口virtual interface

基本介绍:

  • uvm_config_db类在uvm_resource_db的基础上提供了更便捷的接口,用于简化uvm_component实例的配置操作。
  • 若在运行时指定命令行选项+UVM_CONFIG_DB_TRACE,系统将记录并显示所有配置数据库的访问信息(包括读取和写入操作)。
  • uvm_config_db#(T)中的所有函数均为静态方法,因此必须使用::操作符进行调用。例如:
  • uvm_config_db#(int)::set(this, "*", "A");
  • 参数值"int"表示该配置类型为int属性。
  • set和get方法提供的API接口和语义规则与uvm_component中的set/get_config_*系列函数完全一致。

作用:

  • 操作脱离了了top_tb层次结构的实例的手段;
  • 在config_db机 制中,分为set和get两步操作。
  • set操作,读者可以简单地理解成是“寄信”,
  • get操作,则相当于是“收信”。
initial begin
    uvm_config_db#(virtual my_if)::set(null, "uvm_test_top", "vif", input_if);
end

 get

static function bit get(
    uvm_component cntxt, 
    string inst_name, 
    string field_name, 
    inout T value
)
  • 获取 inst_name 中 field_name 的配置值(以组件 cntxt 作为搜索起点)。
  • inst_name 是相对于 cntxt 的显式实例名称,
  • 如果 cntxt 就是配置对象所应用的实例,
  • 则 inst_name 可为空字符串。
  • field_name 表示要搜索的特定字段。
  • cntxt 是 uvm_config_db 配置方法中的一个关键参数,全称为 "context"(上下文),通常指代一个 uvm_component 类型的组件实例。
    • 它的作用是为配置操作提供层级搜索的起点,决定了配置项的查找范围和作用域。
      • uvm_config_db#(int)::set(parent_env, "child_comp", "field_name", config_value);

      • cntxt = parent_env:从 parent_env 开始查找 child_comp

      • inst_name = "child_comp":目标实例的相对路径。

  • uvm_component 中的基本 get_config_* 方法映射到此函数如下:
    • get_config_int(...) => uvm_config_db#(uvm_bitstream_t)::get(cntxt,...)
    • get_config_string(...) => uvm_config_db#(string)::get(cntxt,...)
    • get_config_object(...) => uvm_config_db#(uvm_object)::get(cntxt,...)

非官方描述:

  uvm_config_db是UVM (Universal Verification Methodology)中用于配置共享机制的一个重要类,它提供了一种在测试环境中不同组件之间传递配置信息的标准方式。

  其中get方法是从配置数据库中检索值的关键方法。

static function bit uvm_config_db#(type T=int)::get(
    uvm_component cntxt,
    string inst_name,
    string field_name,
    inout T value
);
1.cntxt (uvm_component):
    指定搜索的上下文组件层次结构
    通常使用this表示当前组件
    如果为null,则搜索全局配置空间
2.inst_name (string):
    指定实例名称路径
    可以使用""表示当前实例
    支持通配符匹配
3.field_name (string):
    要获取的配置字段名称
    必须与set时使用的名称匹配
4.value (inout T):
    用于存储检索到的值的变量
    类型参数T必须与set时使用的类型匹配
返回值
    返回1表示成功找到并检索到值
    返回0表示未找到匹配的配置
 
书中的例子:
if(!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif))
    `uvm_fatal("my_driver", "virtual interface must be set for vif!!!")

  this 索引的区域

  ""表示当前实例

 "vif"表示索引字段;
 vif是当前实例的成员属性,其由外部(顶层TB,或调用了本组件类的上级类通过uvm_config_db#(virtual my_if)::set)完成对虚拟接口的传入

set

static function void set(
    uvm_component cntxt, 
    string inst_name, 
    string field_name, 
    T value
)

简介:此函数用于在UVM验证环境中创建或更新配置设置。配置信息将存储在UVM配置数据库中,供其他组件获取使用。

功能说明:

  该函数用于在指定上下文cntxt中为inst_name实例的field_name字段创建或更新配置项。

  配置的完整作用域路径为{cntxt,".",inst_name}

  当cntxt为null时,inst_name需提供完整的配置作用域路径。

  配置字段名和目标实例名均支持通配符和正则表达式格式。

参数说明
cntxt
    上下文组件,作为配置设置的起点
    若为null,则inst_name必须提供完整路径
    决定配置的作用域和优先级
inst_name
    相对于cntxt的目标实例路径
    支持glob风格或正则表达式
    可以为空字符串(""),表示配置目标就是cntxt本身
field_name
    要设置的字段名
    同样支持glob风格或正则表达式
value
    要设置的配置值,类型由模板参数T决定
作用域规则
    完整作用域路径为:{cntxt, ".", inst_name}
    当cntxt为null时,inst_name必须提供完整路径
优先级规则
    构建阶段(build time)
        配置优先级由cntxt的层级决定
        更高层级的组件设置的配置具有更高优先级
        同级配置遵循"最后设置生效"原则
        优先级数值:
            uvm_top使用默认优先级(uvm_resource_base::default_precedence)
            每下降一个层级,优先级值减1
    运行阶段(run time)
        所有配置使用默认优先级
        完全遵循"最后设置生效"原则
        运行时设置的配置会覆盖之前的所有设置
    类型便捷方法
        UVM提供了以下类型特定的便捷方法,它们都映射到uvm_config_db::set函数:
 
书中的例子:
文件:src/ch2/section2.2/2.2.4/top_tb.sv
44 initial begin
45     uvm_config_db#(virtual my_if)::set(null, "uvm_test_top", "vif", input_if);
46 end
  1. uvm_config_db#(virtual my_if):
    • 指定配置数据库存储的类型是my_if虚拟接口
  2. ::set:
    • 调用静态set方法将配置存入数据库
  3. 参数分析:
    • null: 上下文参数设为null表示全局上下文
    • "uvm_test_top": 目标实例路径,UVM自动创建的默认测试顶层
    • "vif": 配置字段名称,后续可以通过这个名称获取
    • input_if: 要存储的实际接口实例

 

  static function void set(uvm_component cntxt,
                           string inst_name,
                           string field_name,
                           T value);

    uvm_root top;
    uvm_phase curr_phase;
    uvm_resource#(T) r;
    bit exists;
    string lookup;
    uvm_pool#(string,uvm_resource#(T)) pool;
    string rstate;
    uvm_coreservice_t cs = uvm_coreservice_t::get();
     
    //take care of random stability during allocation
    process p = process::self();
    if(p != null) 
          rstate = p.get_randstate();
          
    top = cs.get_root();

    curr_phase = top.m_current_phase;

    if(cntxt == null) 
      cntxt = top;
    if(inst_name == "") 
      inst_name = cntxt.get_full_name();
    else if(cntxt.get_full_name() != "") 
      inst_name = {cntxt.get_full_name(), ".", inst_name};

    if(!m_rsc.exists(cntxt)) begin
      m_rsc[cntxt] = new;
    end
    pool = m_rsc[cntxt];

    // Insert the token in the middle to prevent cache
    // oddities like i=foobar,f=xyz and i=foo,f=barxyz.
    // Can't just use '.', because '.' isn't illegal
    // in field names
    lookup = {inst_name, "__M_UVM__", field_name};

    if(!pool.exists(lookup)) begin
       r = new(field_name, inst_name);
       pool.add(lookup, r);
    end
    else begin
      r = pool.get(lookup);
      exists = 1;
    end
      
    if(curr_phase != null && curr_phase.get_name() == "build")
      r.precedence = uvm_resource_base::default_precedence - (cntxt.get_depth());
    else
      r.precedence = uvm_resource_base::default_precedence;

    r.write(value, cntxt);

    if(exists) begin
      uvm_resource_pool rp = uvm_resource_pool::get();
      rp.set_priority_name(r, uvm_resource_types::PRI_HIGH);
    end
    else begin
      //Doesn't exist yet, so put it in resource db at the head.
      r.set_override();
    end

    //trigger any waiters
    if(m_waiters.exists(field_name)) begin
      m_uvm_waiter w;
      for(int i=0; i<m_waiters[field_name].size(); ++i) begin
        w = m_waiters[field_name].get(i);
        if(uvm_re_match(uvm_glob_to_re(inst_name),w.inst_name) == 0)
           ->w.trigger;  
      end
    end

    if(p != null)
        p.set_randstate(rstate);

    if(uvm_config_db_options::is_tracing())
      m_show_msg("CFGDB/SET", "Configuration","set", inst_name, field_name, cntxt, r);
  endfunction

 跨进程通信手段

  UVM将一些跨进程通信手段封装成了更易用的类。

  uvm_analysis_port(UVM中发送数据的手段之一)

  uvm_analysis_port是一个参数化的类,其参数就是这个analysis_port需要传递的数据的类型

  声明了ap后,需要在monitor的build_phase中将其实例化:

  virtual function void build_phase(uvm_phase phase);
      ap = new("ap", this);
  endfunction

  在main_phase中,当收集完一个transaction后,需要将其写入ap中:

task my_monitor::main_phase(uvm_phase phase);
    my_transaction tr;
    while(1) begin
        tr = new("tr");
        collect_one_pkt(tr);
        ap.write(tr);
    end
endtask    

  write是uvm_analysis_port的一个内建函数。

  uvm_blocking_get_port(UVM中接收数据的手段之一)

uvm_blocking_get_port #(my_transaction) port;
    port.get(tr);

  uvm_analysis_port和uvm_blocking_get_port 通过uvm_tlm_analysis_fifo连接;

  uvm_analysis_port和uvm_blocking_get_port 类 都提供了connect方法,他们需要使用uvm_tlm_analysis_fifo类提供的成员进行连接;

  例如:

    

uvm_tlm_analysis_fifo #(my_transaction) agt_mdl_fifo;
uvm_blocking_get_port #(my_transaction) port;
uvm_analysis_port #(my_transaction) ap;

function void my_model::build_phase(uvm_phase phase);
    super.build_phase(phase);
    port = new("port", this);
    ap = new("ap", this);
endfunction



agt_mdl_fifo = new("agt_mdl_fifo", this);

function void my_env::connect_phase(uvm_phase phase);
    super.connect_phase(phase);
    i_agt.ap.connect(agt_mdl_fifo.analysis_export);
    mdl.port.connect(agt_mdl_fifo.blocking_get_export);
 endfunction

field_automation机制

用uvm_field系列宏

 用于注册transaction类中的字段;

uvm_field系列宏随着transaction成员变量的不同而不同;

`ifndef MY_TRANSACTION__SV
`define MY_TRANSACTION__SV

class my_transaction extends uvm_sequence_item;

   rand bit[47:0] dmac;
   rand bit[47:0] smac;
   rand bit[15:0] ether_type;
   rand byte      pload[];
   rand bit[31:0] crc;

   constraint pload_cons{
      pload.size >= 46;
      pload.size <= 1500;
   }

   function bit[31:0] calc_crc();
      return 32'h0;
   endfunction

   function void post_randomize();
      crc = calc_crc;
   endfunction

   `uvm_object_utils_begin(my_transaction)
      `uvm_field_int(dmac, UVM_ALL_ON)
      `uvm_field_int(smac, UVM_ALL_ON)
      `uvm_field_int(ether_type, UVM_ALL_ON)
      `uvm_field_array_int(pload, UVM_ALL_ON)
      `uvm_field_int(crc, UVM_ALL_ON)
   `uvm_object_utils_end

   function new(string name = "my_transaction");
      super.new();
   endfunction

endclass
`endif

当使用上述宏注册之后,可以直接调用copy、compare、print等函数,而无需自己定义。这极大地简化了验证平台的搭建,提高了效率。

image

 

此处的unpack其实是反序化过程,这个过程要求接收端字段已经准备好空间;
所以必须new了才能完成字段还原。

 uvm_do

①创建一个my_transaction的实例m_trans;

②将其随机化;

③最终将其送给 sequencer。

image

 sequence具有重传机制。

 

posted @ 2025-07-23 19:38  NoNounknow  阅读(70)  评论(0)    收藏  举报