UVM 中 m_sequencer 与 p_sequencer 的关系及应用

m_sequencer :

如图所示,m_sequencer 是一个 uvm_sequencer_base 类型的变量,定义在 uvm_sequence_item 中。通常来说, uvm_sequence_item 是用户自定义 transaction 的基类,并且具有控制 sequence-sequencer 机制的能力。简单来说,就是使动态生命周期的uvm_sequence/uvm_sequence_item (object) 可以与 UVM 静态框架结构中的 uvm_sequencer (component) 进行交互。所以,这个在 uvm_sequence_item 中定义的 uvm_sequencer_base 类型变量 m_sequencer,就承担起桥梁作用。因为 sequencer 本质上就是由 uvm_sequencer_base 派生而来,m_sequencer 就是一个基类句柄,可以指向任何扩展类对象(用户定义的sequencer)。

一句话概括:m_sequencer 是一个定义在用户构建的 sequence/sequence_item 基类里的 sequencer 基类句柄,通过这种方式,建立起 sequence - sequencer 之间的连接,使 sequence 有通过 m_sequencer 句柄来访问 sequencer 变量的能力。

p_sequencer :

 如图所示,p_sequencer 来自于宏定义 `uvm_declare_p_sequencer(SEQUENCER),p_sequencer 的类型是传入的参数 SEQUENCER 类型,通常是用户自定义的某个 sequencer 。这个宏内部实现了一个函数 m_set_p_sequencer();

一句话概括:p_sequencer 是一个 uvm_sequencer 类型 (通常情况) 的句柄,由用户在调用宏 `uvm_declare_p_sequencer(SEQUENCER)  时指定。

在清楚 m_seuqencer 和 p_sequencer 的来源后,问题就在于 m_sequencer 和 p_sequencer 之间的关系是什么呢?或者说,既然 m_sequencer 已经在底层止中建立了 sequence-sequencer 之间的连接关系,为什么还需要 p_sequencer 呢?

这就需要从 sequence 以及 sequence_item 的发送机制说起:

以最基础的情况为例,在 run-time phase 调用 start(); 任务启动一个 sequence:

 如图所示的 start(); 任务源码中,会调用函数 set_item_context();

以其中一种情况为例,start(); 任务启动顶层 sequence,传入该顶层 sequence 挂载到的 sequencer 作为 start 的第一个参数,那么在函数 set_item_context(); 中就会调用函数 set_sequencer();

此时,m_sequencer 句柄指向该 sequence 挂载到的 sequencer 对象。由于 m_sequencer 的类型为 uvm_sequencer_base,而用户自定义 sequencer 的类型通常为 uvm_sequencer,所以此时存在基类句柄 m_sequencer 指向扩展类对象的问题;即 m_sequencer 句柄并没有办法访问扩展类对象中的全部变量和方法,这显然与我们所期望的能通过 sequence 中的 uvm_sequencer_base 类型句柄来访问 sequencer 内变量和方法的初衷不符。

这时候就需要通过 p_sequencer 的加入来解决这个问题,宏 `uvm_declare_p_sequencer(SEQUENCER) 展开后的结构如下:

 在这个宏中有一步关键的操作,即在函数 m_set_p_sequencer(); 中进行了一次动态类型转化:$cast(p_sequencer,  m_sequencer)

将基类句柄通过 $cast 转化为扩展类句柄需要满足如下要求:基类句柄必须指向扩展类对象,否则将为非法操作

在上边提到的 set_sequencer(); 函数中,我们可以看到,基类句柄 m_sequencer 指向的对象正是扩展类 sequencer 的对象,所以只要保证通过 `uvm_declare_p_sequencer 宏指定的 sequencer 与 start(); 任务所挂载到的 sequencer 一致,那么这个动态类型转化便可以成功完成。

而实现这个动态类型转化的函数 m_set_p_sequencer(); 在执行 set_sequencer(); 函数时由于多态虚方法实现了同名方法的动态调用;即在执行 set_sequencer(); 时,完成了基类句柄  m_sequencer 向扩展类句柄 p_sequencer 的转化。

经过这样一个过程,在某个 sequence 中通过宏定义的 p_sequencer 句柄指向的对象就是该 sequence 所挂载到的 sequencer 对象,并且由于 p_sequencer 句柄类型就是 sequence 挂载到的 sequencer 类型,通过 p_sequencer 就可以访问对应 sequencer 中的全部变量。

总结:m_sequencer 的存在建立了 sequencer-sequence 之间的桥梁,但是由于 m_sequencer 句柄类型相较于实际用户构建的 sequencer 来说都是基于更底层的 uvm_sequencer_base 类型。虽然可以保证 m_sequencer 句柄可以指向用户构建的 sequencer,但是 m_sequencer 能访问到的变量和方法仍具有局限性 (基类句柄指向扩展类对象,但只能访问扩展类中属于基类的变量和方法)。因此需要引入一个类型与用户构建的 sequencer 一致的扩展类句柄 p_sequencer,通过动态类型转化,让 p_sequencer 与 m_sequencer 都指向用户构建的 sequencer 对象,并且由于 p_sequencer 是扩展类类型的句柄,可以通过 p_sequencer 句柄,在 sequence 中访问到属于 sequencer 的内容。最终 m_sequencer 与 p_sequencer 指向对象都为用户构建的 sequencer,只是可访问的范围不一致。

附一个 p_sequencer 的使用示例:

在用户构建的 fsm_sequencer 中,有变量 int  num_tran; 假定这个变量通过 config_db 从 用户构建的 test 组件传递过来:

class fsm_sequencer extends uvm_sequencer #(fsm_sequence_item);

`uvm_component_utils(fsm_sequencer) 

	int num_tran;
  
	function new(string name = "fsm_sequencer", uvm_component parent);
    	super.new(name,parent);
	endfunction  

	virtual function void build_phase(uvm_phase phase);
		super.build_phase(phase);
		if(!uvm_config_db #(int)::get(this, "", "times", num_tran)) begin
			`uvm_fatal("seqr", "failed to get num_tran")
		end
	endfunction

endclass

在用户构建的 fsm_sequence 中,通过宏 `uvm_declare_p_sequencer(fsm_sequencer) 指定 p_sequencer 类型为 fsm_sequencer。并且通过 p_sequencer 访问 fsm_sequencer 内部的变量 int  num_tran;用来指定 for 循环执行次数。

class fsm_sequence extends uvm_sequence #(fsm_sequence_item);

`uvm_object_utils(fsm_sequence)
`uvm_declare_p_sequencer(fsm_sequencer)

extern function new(string name = "fsm_sequence");
extern virtual task body();

endclass

//---------------------------------------------------------
function fsm_sequence::new(string name = "fsm_sequence");
		super.new(name);
endfunction

//---------------------------------------------------------
task fsm_sequence::body();

	for(int i = 0; i < p_sequencer.num_tran; i ++) begin
		fsm_sequence_item an_item = fsm_sequence_item::type_id::create("an_item");
		start_item(an_item);
		an_item.randomize();
		finish_item(an_item);
	end
		
endtask

并且在 uvm_test 中,将 fsm_sequence 通过 start(); 挂载到 fsm_sequencer上。

class base_test extends uvm_test;

`uvm_component_utils(base_test)

fsm_env       env;
fsm_sequence  seq;


......


extern function new(string name = "base_test", uvm_component parent = null);
extern virtual function void build_phase(uvm_phase phase);
extern virtual task run_phase(uvm_phase phase);
extern virtual task set_reset();
extern virtual function void end_of_elaboration_phase (uvm_phase phase);

endclass

......

task base_test::run_phase(uvm_phase phase);
		phase.raise_objection(this);
		set_reset();
		seq.start(env.agt.seqr);
		phase.drop_objection(this);
endtask
		
......

  • 22
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值