有时候我们会使用uvm_sequence_library去随机启动加载到它内部的各个子sequence,昨天帮同事debug了1个问题。他是将一些子sequence里的操作放到pre_body()方法里去执行,然后用uvm_sequence_library去调用它们,但最终发现这些pre_body()方法里的代码没有被执行起来。我分析了下原因,是因为uvm_sequence_library里execute(xxx)方法里是使用`uvm_rand_send(seq_or_item)方式来启动子sequence的。uvm_rand_send宏定义如下:
`define uvm_rand_send(SEQ_OR_ITEM) \
`uvm_rand_send_pri_with(SEQ_OR_ITEM, -1, {})
`uvm_rand_send_pri_with宏定义如下:
`define uvm_rand_send_pri_with(SEQ_OR_ITEM, PRIORITY, CONSTRAINTS) \
begin \
uvm_sequence_base __seq; \
if (!$cast(__seq,SEQ_OR_ITEM)) start_item(SEQ_OR_ITEM, PRIORITY);\
else __seq.set_item_context(this,SEQ_OR_ITEM.get_sequencer()); \
if ((__seq == null || !__seq.do_not_randomize) && !SEQ_OR_ITEM.randomize() with CONSTRAINTS ) begin \
`uvm_warning("RNDFLD", "Randomization failed in uvm_rand_send_with action") \
end\
if (!$cast(__seq,SEQ_OR_ITEM)) finish_item(SEQ_OR_ITEM, PRIORITY);\
else __seq.start(__seq.get_sequencer(), this, PRIORITY, 0);\
end
根据uvm_rand_send_pri_with宏可以看出,它调用sequence的start(xxx)方法时,传进去的第4个参数是0。我们来看下start(xxx)方法的代码为:
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
if (m_parent_sequence != null) begin
m_parent_sequence.children_array[this] = 1;
end
if (this_priority < -1) begin
uvm_report_fatal("SEQPRI", $sformatf("Sequence %s start has illegal priority: %0d",
get_full_name(),
this_priority), UVM_NONE);
end
if (this_priority < 0) begin
if (parent_sequence == null) this_priority = 100;
else this_priority = parent_sequence.get_priority();
end
// Check that the response queue is empty from earlier runs
clear_response_queue();
m_priority = this_priority;
if (m_sequencer != null) begin
if (m_parent_sequence == null) begin
m_tr_handle = m_sequencer.begin_tr(this, get_name());
end else begin
m_tr_handle = m_sequencer.begin_child_tr(this, m_parent_sequence.m_tr_handle,
get_root_sequence_name());
end
end
// Ensure that the sequence_id is intialized in case this sequence has been stopped previously
set_sequence_id(-1);
// Remove all sqr_seq_ids
m_sqr_seq_ids.delete();
// Register the sequence with the sequencer if defined.
if (m_sequencer != null) begin
void'(m_sequencer.m_register_sequence(this));
end
// Change the state to PRE_START, do this before the fork so that
// the "if (!(m_sequence_state inside {...}" works
m_sequence_state = PRE_START;
fork
begin
m_sequence_process = process::self();
// absorb delta to ensure PRE_START was seen
#0;
pre_start();
if (call_pre_post == 1) begin
m_sequence_state = PRE_BODY;
#0;
pre_body();
end
if (parent_sequence != null) begin
parent_sequence.pre_do(0); // task
parent_sequence.mid_do(this); // function
end
m_sequence_state = BODY;
#0;
body();
m_sequence_state = ENDED;
#0;
if (parent_sequence != null) begin
parent_sequence.post_do(this);
end
if (call_pre_post == 1) begin
m_sequence_state = POST_BODY;
#0;
post_body();
end
m_sequence_state = POST_START;
#0;
post_start();
m_sequence_state = FINISHED;
#0;
end
join
if (m_sequencer != null) begin
m_sequencer.end_tr(this);
end
// Clean up any sequencer queues after exiting; if we
// were forcibly stoped, this step has already taken place
if (m_sequence_state != STOPPED) begin
if (m_sequencer != null)
m_sequencer.m_sequence_exiting(this);
end
#0; // allow stopped and finish waiters to resume
if ((m_parent_sequence != null) && (m_parent_sequence.children_array.exists(this))) begin
m_parent_sequence.children_array.delete(this);
end
endtask
根据以上代码可以看出start(xxx)方法的第4个参数是call_pre_post,因为它被uvm_rand_send_pri_with宏赋值为0,所以可以看出子sequence里的pre_body()和post_body()方法都将不会被执行。UVM里提供的启动sequence的宏,有一些是把call_pre_post指为0的,因此为了防止出现这种意想不到的错误,建议放到pre_start()和post_start()里。