[UVM源代码研究] 关于default_sequence和p_sequencer的一点思考

文章探讨了在UVM虚拟序列(virtual_sequence)构造函数中能否访问p_sequencer的问题,发现p_sequencer的赋值在sequence启动后,而在构造函数执行前。作者通过源代码分析揭示了UVM的内部机制,包括phase机制和config_db设置顺序。

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

[UVM源代码研究] 关于default_sequence和p_sequencer的一点思考

1. 引言

今天在用virtual_sequence和virtual_sequencer的时候碰到了一个以前没太留意的问题,就是说在virtual_sequence的构造函数new()中能否引用p_sequencer中的内容,换句话说,在virtual_sequence的new()函数中p_sequencer有没有完成创建。

如图1所示,这样调用是否会报错。

图1 virtual_sequence的new()函数中引用p_sequencer句柄

在这里插入图片描述

答案是会报错,如图2所示

图2 图1代码log结果

在这里插入图片描述

意思就是说p_sequencer还没有被创建就引用了。

这里就涉及到一个问题,p_sequencer所指向的virtual_sequencer什么时候才被创建了?

我们实际项目中一般的做法都是在test_base或者env的build_phase创建virtual_sequencer,如下图3所示

图3 virtual_sequencer的创建

在这里插入图片描述

然后virtual_sequence在testcase的build_phase被设为virtual_sequencer的main_phase上的default_sequence,如图4所示

图4 virtual_sequence被set为default_sequence

在这里插入图片描述

那么问题就转化为了两个时间点执行的先后顺序问题,这两个时间点分别是:

  1. 我们执行的virtual_sequence的构造函数是在什么时间点(t1)执行的呢?
  2. uvm_declare_psequencer()所赋值的p_sequencer又是在什么时间点(t2)指向了我们实际的virtual_sequencer实例的呢?

结论我们不难得出,之所以出现图2的错误,一定是t1在t2之前执行了,导致p_sequencer还是没有指向任何实例的空句柄。

下面我们从UVM源代码执行顺序的角度来看看t1和t2分别是什么时候被执行的。

2. 源代码分析

首先我们来看宏 `uvm_declare_p_sequencer 都干了啥。

查找宏 `uvm_declare_p_sequencer 的定义如图5所示:

图5 src/macros/uvm_sequence_defines.svh中的代码

在这里插入图片描述

`uvm_declare_p_sequencer可以认为就是定义一个SEQUENCER类型的变量p_sequencer,并且定义一个函数m_set_p_sequencer()(这个函数在uvm_sequence类中肯定以经存在了,否则还需要用户再手动调用,而我们从来没有自己调用过,所以UVM源代码中原本应该已经实现了该函数的调用了,这里要做的就是override覆盖原有函数定义),该函数将m_sequencer $cast给了p_sequencer,由此我们关注的点变成了m_set_p_sequencer()这个函数在什么时候被调用的。

源代码目录查找m_set_p_sequencer()信息,如图6所示,m_set_p_sequencer()在set_sequencer()中被调用,

图6 src/seq/uvm_sequence_item.svh中的代码截图

在这里插入图片描述

同一个文件继续搜索如图7所示显示set_sequencer()在set_item_context()中被调用

图7 src/seq/uvm_sequence_item.svh中的代码截图

在这里插入图片描述

源代码目录搜索set_item_context()如图8所示,set_item_context()在start()中被调用。

图8 src/seq/uvm_sequence_base.svh中的代码截图

在这里插入图片描述

即sequence启动的时候才会调用m_set_p_sequencer()函数,完成p_sequencer的赋值。

而virtual_sequence中的构造函数呢?

答案不言而喻,肯定是在创建virtual_sequence的时候被调用的,那结论是不是很显而易见了,肯定是先创建sequence才会启动sequence,所以t1肯定在t2前面执行,所以肯定不能在virtual_sequence的构造函数里引用p_sequencer。

下面我们再做一些额外的探索,就是上面提到的virtual_sequence在testcase的build_phase被设为virtual_sequencer的main_phase上的default_sequence,那么这个default_sequence在UVM源代码中又是在when & where被创建执行的呢?

源代码目录搜索一下"default_sequence",在src/seq/uvm_sequencer_base.svh文件中找到如图9所示的内容(继承关系为uvm_sequencer extends uvm_sequencer_param_base extends uvm_sequencer_base)

图9 src/seq/uvm_sequencer_base.svh中的代码截图

在这里插入图片描述

但是这里使用的是get_config_string来获取的,而我们在set的时候传递的是uvm_object_wrapper类型,所以这里由于类型不一致并不会get到我们set的内容。

继续往下查看,如图10所示。

图10 src/seq/uvm_sequencer_base.svh中的代码截图

在这里插入图片描述

可以看到1373行get的类型跟我们get的类型是一致的,并且会在1376行创建我们传递过来的virtual_sequence类型的实例,这便是我们virtual_sequence中的构造函数new调用的地方,至于这里的1367行声明的uvm_factory类型的变量f以及1376行通过调用f.create_object_by_type()创建virtual_sequence这部分内容之前在讲factory的时候有提到过,这里就不细讲了,可以简单认为是virtual_sequence中通过`uvm_object_utils宏将virtual_sequence类注册到factory中,这样uvm_factory就可以通过查找类型wrapper来创建对应的变量。(这个wrapper的类型是通过testcase的build_phase中set过来的virtual_sequence::type_id::get()获取的,也就是说set的是一个uvm_object_wrapper类型,而不是一个实例,uvm_object_wrapper作为一个UVM源代码中定义的基类可以接受任何类型)

1394行主动调用set_sequencer()将virtual_sequencer设置为virtual_sequence启动的sequencer(因为virtual_sequence被set到了virtual_sequencer的main_phase,所以这里的this指的就是virtual_sequencer)。根据图6的分析,其实这一步就已经实现了p_sequencer的赋值的。

1408行启动virtual_sequence。

这个start_phase_sequence是在如图11所示代码中被调用的。由之前我们在 数字验证大头兵:[UVM源代码研究] 浅谈UVM PHASE机制的运行 中所讲的内容可知,在每一个task_phase都会调用该函数,这样其实我们在build_phase除了把这个virtual_sequence设置到virtual_seuquencer的main_phase之外,另外11中runtime_phase和run_phase都是可以的,就看你想让这个virtual_sequence什么时间点被执行起来。

图11 src/base/uvm_task_phase.svh中的代码截图

在这里插入图片描述

这里再做一点引申,由图10的1370行可以看出,除了使用与1373行代码对应的

uvm_config_db#(uvm_object_wrapper)::set(this, "env.v_sqr.main_phase", "default_sequence", virtual_sequence::type_id::get());

的方法set default_sequence,还可以用与1370行对应的代码的另一种方法(直接set virtual_sequence的实例):

virtual_sequence myseq = new("myseq");
uvm_config_db #(uvm_sequence_base)::set(null, "env.v_sqr.main_phase", "default_sequence", myseq);

3. 总结

本文通过工作过程中使用virtual_sequence和virtual_sequencer时遇到的一个bug,引申出了UVM源代码中`uvm_declare_psequencer()宏和default_sequence相关代码的实现机制,对virtual_sequence®的使用相信会有更深刻的认识。

回到我们文章一开始遇到的那个bug问题,如果我们想在virtual_sequence的主体代码body()之前做一些预处理工作(这也是为什么会在new()函数里引用p_sequencer的原因),可以把这部分工作放在pre_body()里完成。

UVM(Universal Verification Methodology)1.2版本中,`default_sequence``starting_phase`的设置方式有一些变化。如果你在使用`default_sequence`时发现`starting_phase`为空,可能是因为配置方式或版本差异导致的。以下是一些可能的解决方案解释: 1. **配置方式**: - 确保在`uvm_config_db`中正确配置了`default_sequence``starting_phase`。例如: ```systemverilog class my_env extends uvm_env; my_sequencer sequencer; `uvm_component_utils(my_env) function void build_phase(uvm_phase phase); super.build_phase(phase); // 配置default_sequence uvm_config_db#(uvm_object_wrapper)::set(this, "sequencer.run_phase", "default_sequence", my_sequence::type_id::get()); // 配置starting_phase uvm_config_db#(uvm_phase)::set(this, "sequencer.run_phase", "starting_phase", phase); endfunction endclass ``` 2. **版本差异**: - 在UVM 1.2中,`starting_phase`的使用方式可能与之前版本有所不同。确保你查阅了UVM 1.2的官方文档,了解最新的配置方法。 3. **调试**: - 使用`uvm_info`打印调试信息,确保配置信息已经正确设置。例如: ```systemverilog `uvm_info("DEBUG", $sformatf("default_sequence: %0s", uvm_config_db#(uvm_object_wrapper)::get(this, "sequencer.run_phase", "default_sequence", temp)), UVM_MEDIUM) `uvm_info("DEBUG", $sformatf("starting_phase: %0s", uvm_config_db#(uvm_phase)::get(this, "sequencer.run_phase", "starting_phase", temp_phase)), UVM_MEDIUM) ``` 4. **示例代码**: - 以下是一个完整的示例,展示了如何在UVM 1.2中配置`default_sequence``starting_phase`: ```systemverilog class my_sequence extends uvm_sequence #(my_transaction); `uvm_object_utils(my_sequence) `uvm_declare_p_sequencer(my_sequencer) function new(string name = "my_sequence"); super.new(name); endfunction task body; // 序列逻辑 endtask endclass class my_env extends uvm_env; my_sequencer sequencer; `uvm_component_utils(my_env) function void build_phase(uvm_phase phase); super.build_phase(phase); sequencer = my_sequencer::type_id::create("sequencer", this); // 配置default_sequence uvm_config_db#(uvm_object_wrapper)::set(this, "sequencer.run_phase", "default_sequence", my_sequence::type_id::get()); // 配置starting_phase uvm_config_db#(uvm_phase)::set(this, "sequencer.run_phase", "starting_phase", phase); endfunction endclass class my_test extends uvm_test; my_env env; `uvm_component_utils(my_test) function void build_phase(uvm_phase phase); super.build_phase(phase); env = my_env::type_id::create("env", this); endfunction endclass ``` 通过这些方法,你应该能够在UVM 1.2中正确配置`default_sequence``starting_phase`。如果问题依然存在,建议查阅UVM 1.2的官方文档或社区资源,获取更多帮助。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值