uvm1.1之后,结束仿真的机制有且只有一种,那就是利用objection挂起机制来控制仿真结束
在进入某一phase时,UVM会收集此phase提出的所有objection,并且实时监测所有的objection是否已经被撤销,当发现所有objection都已经撤销后,关闭此phase,进入下一phase,当所有phase执行完毕后,就会调用$finish关掉整个验证平台
一般来说,在一个实际的验证平台中,通常会在以下两种objection的控制策略中选择一种:
第一种是在scoreboard中进行控制。scoreboard的main_phase一般会被做成一个无限循环,如果要在scoreboard中控制objection,则需要去除这个无限循环,通过config_db::set的方式设置收集到的transaction的数量pkt_num(可以在sequence层级设置,sequence层级设置时,scb中get要有一定延迟;或者是在test层级直接set),当收集到指定数量pkt_num的transaction后跳出循环。
第二种则是在sequence中raise sequencer的objection,当sequence完成后,再drop此objection ,以上两种方式在验证平台中都有应用。
其中用得最多的是第二种,这种方式是UVM提倡的方式。UVM的设计哲学就是全部由sequence来控制激励的生成,因此一般情况下只在sequence中控制objection。
如果用的是UVM-1.2,则可以在new函数中打开自动控制objection的开关,UVM将会自动控制objection的raise和drop;
如果用的是UVM-1.1,则分别在pre_start和post_start任务中去raise/drop objection,当然在pre body和post body 或者 body中都是可以的
class base_sequence extends uvm_sequence; // other code not shown
function new(string name = "base_sequence");
super.new(name);
`ifdef UVM_VERSION_1_2
set_automatic_phase_objection(1); // UVM-1.2 & IEEE UVM Only!
`endif
endfunction : new
virtual task pre_start();
`ifdef UVM_VERSION_1_1
if (get_parent_sequence() == null && starting_phase != null)
starting_phase.raise_objection(this); // UVM-1.1 ONLY!
`endif
endtask : pre_start
virtual task post_start();
`ifdef UVM_VERSION_1_1
if (get_parent_sequence() == null && starting_phase != null)
starting_phase.drop_objection(this); // UVM-1.1 ONLY!
`endif
endtask : post_start
virtual task body();
endtask : body
endclass : base_sequence
自己编写执行的sequence只需要从这个基类中继承过来,在body的task中写测试激励就可以了
class test_sequence extends base_sequence;
function new(string name = "test_sequence");
super.new(name);
endfunction : new
virtual task body();
//TODO:
endtask : body
endclass : test_sequence
为什么可以在类型为object的seq中控制整个验证平台component的phase运行?
因为在seq中raise和drop objection本质是在sqr中main_phase中raise和drop,sqr为componet类型,自然也就可以控制整个环境生命周期
验证平台一般会在test层通过config_db的方式指定要执行的default_seq,在启动seq的时候,会执行start函数,uvm_sequence_base中的虚函数start()调用了pre_start()→pre_body()→body()→post_body()→post_start() 函数
根据上文得知,在pre_start中会raise_objection,post_start中drop_objection,因此sqr的main_phase的生命周期相当于seq的生命周期,从而控制整个验证平台component的phase执行
class my_test extends base_test;
function new(string name = "my_test");
super.new(name);
endfunction : new
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
uvm_config_db#(uvm_object_wrapper)::set(this,
"vsqr.main_phase","default_sequence",
my_seq::type_id::get());
endfunction
endclass : my_test
UVM源码:
virtual task start (uvm_sequencer_base sequencer,
uvm_sequence_base parent_sequence = null,
int this_priority = -1,
bit call_pre_post = 1);
bit old_automatic_phase_objection;
set_item_context(parent_sequence, sequencer);
if (!(m_sequence_state inside {UVM_CREATED,UVM_STOPPED,UVM_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
integer handle;
uvm_tr_stream stream;
if (m_parent_sequence == null) begin
stream = m_sequencer.get_tr_stream(get_name(), "Transactions");
handle = m_sequencer.begin_tr(this, get_name());
m_tr_recorder = uvm_recorder::get_recorder_from_handle(handle);
end else begin
stream = m_sequencer.get_tr_stream(get_root_sequence_name(), "Transactions");
handle = m_sequencer.begin_child_tr(this,
(m_parent_sequence.m_tr_recorder == null) ? 0 : m_parent_sequence.m_tr_recorder.get_handle(),
get_root_sequence_name());
m_tr_recorder = uvm_recorder::get_recorder_from_handle(handle);
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 = UVM_PRE_START;
fork
begin
m_sequence_process = process::self();
// absorb delta to ensure PRE_START was seen
#0;
// Raise the objection if enabled
// (This will lock the uvm_get_to_lock_dap)
if (get_automatic_phase_objection()) begin
m_safe_raise_starting_phase("automatic phase objection");
end
pre_start();
if (call_pre_post == 1) begin
m_sequence_state = UVM_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 = UVM_BODY;
#0;
body();
m_sequence_state = UVM_ENDED;
#0;
if (parent_sequence != null) begin
parent_sequence.post_do(this);
end
if (call_pre_post == 1) begin
m_sequence_state = UVM_POST_BODY;
#0;
post_body();
end
m_sequence_state = UVM_POST_START;
#0;
post_start();
其他
- 关于本文,您有什么想法均可在评论区留言交流。
- 自身能力不足,如有错误还请多多指出!