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
......