uvm_primer ch18 put/get port的应用

本文详细探讨了如何使用uvm_primer中的uvm_put/get_port在uvm_sequence_driver和sequencer之间实现任务间与线程内的通信,通过实例代码揭示了两者连接时的模式区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

uvm_primer ch18 put/get port的应用

用来拆分出sequence

在这里插入图片描述

virtual class base_tester extends uvm_component;
`uvm_component_utils(base_tester)
   virtual tinyalu_bfm bfm;
   uvm_put_port #(command_s) command_port;//重点
   function void build_phase(uvm_phase phase);
      command_port = new("command_port", this);
   endfunction : build_phase
   pure virtual function operation_t get_op();
   pure virtual function byte get_data();
   task run_phase(uvm_phase phase);
      byte         unsigned        iA;
      byte         unsigned        iB;
      operation_t                  op_set;
      command_s    command;
      phase.raise_objection(this);
      command.op = rst_op;
      command_port.put(command); //重点
      repeat (1000) begin : random_loop
         command.op = get_op();
         command.A =  get_data();
         command.B =  get_data();
         command_port.put(command);
      end : random_loop
      #500;
      phase.drop_objection(this);
   endtask : run_phase
   function new (string name, uvm_component parent);
      super.new(name, parent);
   endfunction : new
endclass : base_tester


class driver extends uvm_component;
   `uvm_component_utils(driver)
   virtual tinyalu_bfm bfm;
   uvm_get_port #(command_s) command_port;  //重点
   function void build_phase(uvm_phase phase);
      if(!uvm_config_db #(virtual tinyalu_bfm)::get(null, "*","bfm", bfm))
        $fatal("Failed to get BFM");
      command_port = new("command_port",this);
   endfunction : build_phase
   task run_phase(uvm_phase phase);
      command_s    command;
      shortint     result;
      forever begin : command_loop
         command_port.get(command); //重点
         bfm.send_op(command.A, command.B, command.op, result);
      end : command_loop
   endtask : run_phase

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

endclass : driver

总是将export传入port的connect方法中;

driver 和sequencer连接

在这里插入图片描述

问题

driver侧,是线程间通信,
monitor侧是线程内通信, WHY???

<think>我们正在处理一个UVM相关的错误:在文件mcu_spi_regm_ext.svh的reset函数第118行出现"未初始化的虚拟接口对象"错误。根据引用[2]中的信息,这种错误通常是因为没有通过uvm_config_db::set设置虚拟接口,或者没有成功通过uvm_config_db::get获取虚拟接口。因此,我们需要检查两个方面:1.在测试环境中(通常在test或env中)是否使用uvm_config_db::set将虚拟接口设置到配置数据库中。2.在需要获取虚拟接口的组件(如寄存器模型)中,是否使用uvm_config_db::get获取虚拟接口,并且路径和名称是否正确。具体到错误发生在reset函数中,这意味着在调用reset时,虚拟接口对象还没有被初始化(即没有成功获取到)。解决步骤:1.确认在顶层测试中已经将虚拟接口设置到uvm_config_db中,例如在test的build_phase中:uvm_config_db#(virtualspi_if)::set(this,"*","spi_vif",spi_if_instance);这里假设接口类型为spi_if,实例名为spi_if_instance,设置的名字为"spi_vif"。2.检查寄存器模型(mcu_spi_regm_ext)中,在获取虚拟接口的地方(通常在build_phase或connect_phase中)是否正确获取:if(!uvm_config_db#(virtualspi_if)::get(this,"","spi_vif",spi_vif))`uvm_fatal("NO_VIF","CouldnotgetvirtualinterfaceforSPI")注意:如果寄存器模型不是组件(而是uvm_object),则不能使用this指针,因为this指针指向的对象可能不在配置数据库的上下文中。此时,应该使用全局的uvm_root或者通过父组件来获取。3.由于错误出现在reset函数中,可能是reset在获取虚拟接口之前被调用。因此,需要确保获取虚拟接口的操作在reset之前完成。通常,在UVM中,build_phase是最先执行的,所以建议在build_phase中获取虚拟接口。4.如果寄存器模型不是组件(即不是uvm_component派生类),那么它可能没有build_phase。在这种情况下,我们需要在创建寄存器模型后,通过外部传入虚拟接口,或者通过其他方式(如配置对象)传递接口。考虑到寄存器模型通常是uvm_object,它没有build_phase,所以不能在build_phase中自动获取。因此,常见的做法是在顶层环境中创建寄存器模型后,将虚拟接口直接赋值给寄存器模型,或者通过配置对象传递。例如,在env的build_phase中://创建寄存器模型reg_model=mcu_spi_regm_ext::type_id::create("reg_model");//设置寄存器模型的虚拟接口reg_model.spi_vif=spi_vif;//假设寄存器模型内部有一个spi_vif成员或者,在寄存器模型中,我们可以在reset函数被调用之前确保虚拟接口已经被设置。因此,我们需要检查寄存器模型中的虚拟接口是在何时被赋值的。根据错误信息,问题发生在mcu_spi_regm_ext.svh文件的reset函数第118行,那么我们可以查看该行代码,看看是哪个变量未初始化。很可能是访问了某个虚拟接口的成员,而该虚拟接口为null。因此,在寄存器模型中,我们应该在reset函数中使用虚拟接口之前检查其是否为空:if(spi_vif==null)`uvm_fatal("NO_VIF","Virtualinterfaceisnullinresetfunction")同时,确保在调用reset之前,虚拟接口已经被赋值。总结解决方案:1.确保虚拟接口在顶层测试中被正确设置到uvm_config_db中。2.如果寄存器模型是组件,则在build_phase中获取虚拟接口;如果不是,则通过外部传递(如在env中创建寄存器模型后直接赋值)。3.在寄存器模型的reset函数中,使用虚拟接口前检查其是否为空,并给出致命错误以方便调试。4.调整调用reset的时机,确保在虚拟接口赋值之后。此外,引用[3]提到关于`include"uvm_macros.svh"`的问题,虽然与当前错误不直接相关,但确保在编译单元中正确包含宏定义也是必要的。如果以上步骤都检查无误,但问题仍然存在,则可能需要检查路径设置是否正确(在set和get中的路径字符串是否匹配),以及是否在同一个测试平台中。由于错误信息指向reset函数,所以重点检查寄存器模型中的虚拟接口获取和赋值时机。下面是一个示例代码,展示如何在非组件的寄存器模型中设置虚拟接口:在env中:classmy_envextendsuvm_env;mcu_spi_regm_extreg_model;virtualspi_ifspi_vif;functionvoidbuild_phase(uvm_phasephase);super.build_phase(phase);//获取虚拟接口if(!uvm_config_db#(virtualspi_if)::get(this,"","spi_vif",spi_vif))`uvm_fatal("NO_VIF","env:Couldnotgetspi_vif")//创建寄存器模型reg_model=mcu_spi_regm_ext::type_id::create("reg_model");//将虚拟接口传递给寄存器模型reg_model.set_vif(spi_vif);endfunctionendclass在寄存器模型mcu_spi_regm_ext中:classmcu_spi_regm_extextendsuvm_reg_block;virtualspi_ifspi_vif;functionvoidset_vif(virtualspi_ifvif);spi_vif=vif;endfunctionfunctionvoidreset(stringkind="HARD");super.reset(kind);//使用spi_vif前检查if(spi_vif==null)`uvm_fatal("NO_VIF","spi_vifisnullinreset")//...其他复位操作,例如通过虚拟接口驱动信号endfunctionendclass这样,在env的build_phase中,我们获取了虚拟接口并传递给寄存器模型,然后才能调用reset(通常在复位阶段或配置阶段之后)。如果reset是在UVM的reset_phase中自动调用的,那么由于build_phase在reset_phase之前,所以虚拟接口已经被设置。请根据上述步骤检查你的代码。</think>### 解决UVM虚拟接口未初始化错误 #### 错误原因分析 在`mcu_spi_regm_ext.svh`的reset函数(第118行)出现未初始化的虚拟接口对象错误,核心原因是**虚拟接口未通过`uvm_config_db`正确传递到寄存器模型**。具体表现为: 1. **接口未设置**:未在Test层或Env层通过`uvm_config_db::set()`注册虚拟接口 2. **获取失败**:寄存器模型中的`uvm_config_db::get()`调用未成功获取接口 3. **时序问题**:在reset函数调用时,接口尚未初始化[^2] --- #### 解决方案(分步实现) ##### 步骤1:在Test层设置虚拟接口 ```systemverilog class base_test extends uvm_test; virtual spi_if spi_vif; // 声明虚拟接口 function void build_phase(uvm_phase phase); // 将物理接口绑定到虚拟接口 spi_vif = top_tb.spi_if_inst; // 关键步骤:将接口存入config_db uvm_config_db#(virtual spi_if)::set(this, "*.reg_model*", "spi_vif", spi_vif); endfunction endclass ``` **注意**:`"*.reg_model*"`中的通配符需匹配寄存器模型的实际层次路径[^2] ##### 步骤2:在寄存器模型中获取接口 ```systemverilog class mcu_spi_regm_ext extends uvm_reg_block; virtual spi_if spi_vif; // 声明虚拟接口句柄 function void build_phase(uvm_phase phase); // 关键步骤:从config_db获取接口 if(!uvm_config_db#(virtual spi_if)::get(null, "", "spi_vif", spi_vif)) begin `uvm_fatal("NO_VIF", "Virtual interface not found for register model!") end endfunction function void reset(string kind = "HARD"); super.reset(kind); // 现在可安全使用spi_vif spi_vif.reset <= 1'b1; // 第118行操作示例 endfunction endclass ``` ##### 步骤3:添加防御性检查(可选增强) ```systemverilog function void reset(string kind = "HARD"); super.reset(kind); // 添加接口空指针检查 if(spi_vif == null) begin `uvm_error("NULL_VIF", "Virtual interface is null in reset function") return; end spi_vif.reset <= 1'b1; // 原始第118行操作 endfunction ``` --- #### 关键验证点 1. **路径一致性**: - `set()`和`get()`中的接口名称必须一致(示例中均为`"spi_vif"`) - Test中的`set()`路径必须覆盖寄存器模型实例位置 2. **编译顺序**: - 确保包含虚拟接口定义的文件在`uvm_config_db::set`之前编译 - 检查是否在所有相关文件头部包含`import uvm_pkg::*;`[^3] 3. **运行时序**: - 寄存器模型的`build_phase`必须在调用`reset()`之前执行 - 使用`uvm_top.print_topology()`确认组件构建顺序 --- #### 典型错误场景排查表 | 现象 | 可能原因 | 解决方法 | |------|----------|----------| | `uvm_fatal("NO_VIF")` | 1. 接口未set<br>2. 路径不匹配 | 检查set/get的路径字符串一致性 | | reset中接口为null | 1. build_phase未执行<br>2. 接口获取代码被注释 | 取消get()代码注释[^2] | | 编译错误`undeclared identifier` | 未包含接口定义文件 | 在testbench顶层`include接口定义文件 | --- #### 扩展优化建议 1. **使用配置对象**: ```systemverilog // 创建包含vif的配置对象 uvm_config_db#(spi_config)::set(this, "*", "spi_config", cfg); ``` 2. **自动检查机制**: ```systemverilog if(!uvm_config_db#(virtual spi_if)::get(null, "", "spi_vif", spi_vif)) `uvm_fatal("FATAL_VIF", $sformatf("Interface path=%s", this.get_full_name())) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值