在看张强白皮书P189中对uvm_declare_p_sequencer的描述时有一个疑问,就是为什么通过sequencer的类名就能获取到sequencer,然后自己下来查了一下,理了一下关系才明白。
首先就是uvm_declare_p_sequencer这个宏,看了一下定义的地方,在uvm_sequence_defines.svh中:
`define uvm_declare_p_sequencer(SEQUENCER) \
SEQUENCER p_sequencer;\
virtual function void m_set_p_sequencer();\
super.m_set_p_sequencer(); \
if( !$cast(p_sequencer, m_sequencer)) \
`uvm_fatal("DCLPSQ", \
$sformatf("%m %s Error casting p_sequencer, please verify that this sequence/sequence item is intended to execute on this type of sequencer", get_full_name())) \
endfunction
书上一样,就是把m_sequencer转成了p_sequencer,这个m_set_p_sequencer函数是在uvm_sequence_item里面,最开始是空的,宏定义进行了扩展,具体细节往下。
//原始m_set_p_sequencer函数
virtual function void m_set_p_sequencer();
return;
endfunction
class uvm_sequence_item extends uvm_transaction;
local int m_sequence_id = -1;
protected bit m_use_sequence_info;
protected int m_depth = -1;
protected uvm_sequencer_base m_sequencer;
protected uvm_sequence_base m_parent_sequence;
static bit issued1,issued2;
bit print_sequence_info;
m_sequencer是在uvm_sequence_item里定义的,类型是uvm_sequencer_base,和书上一样。
virtual function void set_sequencer(uvm_sequencer_base sequencer);
m_sequencer = sequencer;
m_set_p_sequencer();
endfunction
m_sequencer是在uvm_sequence_item的set_sequencer里赋值的,然后我们继续找这个函数。
function void set_item_context(uvm_sequence_base parent_seq,
uvm_sequencer_base sequencer = null);
set_use_sequence_info(1);
if (parent_seq != null) set_parent_sequence(parent_seq);
if (sequencer == null && m_parent_sequence != null) sequencer = m_parent_sequence.get_sequencer();
set_sequencer(sequencer);
if (m_parent_sequence != null) set_depth(m_parent_sequence.get_depth() + 1);
reseed();
endfunction
set_sequencer是在uvm_sequence_item的set_item_context里调用,可以看见前面两个函数的参数都有一个sequencer,我们继续找这个是从哪来的。
virtual task start (uvm_sequencer_base sequencer,
uvm_sequence_base parent_sequence = null,
int this_priority = -1,
bit call_pre_post = 1);
set_item_context(parent_sequence, sequencer);
if (!(m_sequence_state inside {CREATED,STOPPED,FINISHED})) begin
uvm_report_fatal("SEQ_NOT_DONE",
{"Sequence ", get_full_name(), " already started"},UVM_NONE);
end
.....
可以看到,在uvm_sequence_base里面,start启动了这个函数,并且传了一个sequencer进去,这个sequencer是在sequence启动的时候指定的那个sequencer,这下就明白了,实际上被cast的m_sequencer传入的参数就是我们启动时候指定的sequencer。
总结一下,sequence不管是显式还是隐式启动,都会调用start函数指定需要在哪个sequencer上启动,指定的是实例化后的sequencer的对象,然后将sequencer传给set_item_context,set_item_context再传给set_sequencer,最后赋给m_sequencer,然后通过cast将m_sequencer转换到p_sequencer。