首先看一些uvm_do宏的定义
`define uvm_do(SEQ_OR_ITEM) \
`uvm_do_on_pri_with(SEQ_OR_ITEM, m_sequencer, -1, {})
其实uvm_do系列大部分都是调用了`uvm_do_on_pri_with,再看一下`uvm_do_on_pri_with做了什么
`define uvm_do_on_pri_with(SEQ_OR_ITEM, SEQR, PRIORITY, CONSTRAINTS) \
begin \
uvm_sequence_base __seq; \
`uvm_create_on(SEQ_OR_ITEM, SEQR) \
if (!$cast(__seq,SEQ_OR_ITEM)) start_item(SEQ_OR_ITEM, PRIORITY);\
if (!SEQ_OR_ITEM.randomize() with CONSTRAINTS ) begin \
`uvm_warning("RNDFLD", "Randomization failed in uvm_do_with action") \
end\
if (!$cast(__seq,SEQ_OR_ITEM)) finish_item(SEQ_OR_ITEM, PRIORITY); \
else __seq.start(SEQR, this, PRIORITY, 0); \
end
其中就出现了熟悉的start_item/finish_item和start,该宏对第一个argument进行类型转换来进行sequence和sequence_item的鉴别,如果do的是item那么自然无法与uvm_sequence_base进行转换,所以会调用执行item对应的start_item/finish_item。否则调用start
uvm_sequence_base::start_item
// Function: start_item
//
// ~start_item~ and <finish_item> together will initiate operation of
// a sequence item. If the item has not already been
// initialized using create_item, then it will be initialized here to use
// the default sequencer specified by m_sequencer. Randomization
// may be done between start_item and finish_item to ensure late generation
//
virtual task start_item (uvm_sequence_item item,
int set_priority = -1,
uvm_sequencer_base sequencer=null);
uvm_sequence_base seq;
if(item == null) begin
uvm_report_fatal("NULLITM",
{"attempting to start a null item from sequence '",
get_full_name(), "'"}, UVM_NONE);
return;
end
if($cast(seq, item)) begin
uvm_report_fatal("SEQNOTITM",
{"attempting to start a sequence using start_item() from sequence '",
get_full_name(), "'. Use seq.start() instead."}, UVM_NONE);
return;
end
if (sequencer == null)
sequencer = item.get_sequencer();
if(sequencer == null)
sequencer = get_sequencer();
if(sequencer == null) begin
uvm_report_fatal("SEQ",{"neither the item's sequencer nor dedicated sequencer has been supplied to start item in ",get_full_name()},UVM_NONE);
return;
end
if (sequencer == null)
sequencer = item.get_sequencer();
if (sequencer == null) begin
uvm_report_fatal("STRITM", "sequence_item has null sequencer", UVM_NONE);
end
item.set_use_sequence_info(1);
item.set_sequencer(sequencer);
item.set_parent_sequence(this);
item.reseed();
if (set_priority < 0)
set_priority = get_priority();
sequencer.wait_for_grant(this, set_priority);
`ifndef UVM_DISABLE_AUTO_ITEM_RECORDING
void'(sequencer.begin_child_tr(item, m_tr_handle, item.get_root_sequence_name()));
`endif
pre_do(1);
endtask
start_item不可启动seq,启动seq需要使用start。
如果没有传入sqr,调用item的get_sequencer获得sqr
如果item的sqr为null,调用get_sequencer获得sqr
set_use_sequence_info具体不了解
set_sequencer为item指定sqr
virtual function void set_sequencer(uvm_sequencer_base sequencer);
m_sequencer = sequencer;
m_set_p_sequencer();
endfunction
reseed:不理解
重新为随机数生成器分配seed,默认采用uvm seed生成机制,即将type_name & inst_name传递给uvm_create_random_seed,生成的seed再通过srandom传递给随机数生成器。若不使用uvm seed,该函数不执行任何操作
传入的set_priority默认为-1,则会继承当前seq的优先级
wait_for_grant
// wait_for_grant
// --------------
task uvm_sequencer_base::wait_for_grant(uvm_sequence_base sequence_ptr,
int item_priority = -1,
bit lock_request = 0);
uvm_sequence_request req_s;
int my_seq_id;
if (sequence_ptr == null)
uvm_report_fatal("uvm_sequencer",
"wait_for_grant passed null sequence_ptr", UVM_NONE);
my_seq_id = m_register_sequence(sequence_ptr);
// If lock_request is asserted, then issue a lock. Don't wait for the response, since
// there is a request immediately following the lock request
if (lock_request == 1) begin
req_s = new();
req_s.grant = 0;
req_s.sequence_id = my_seq_id;
req_s.request = SEQ_TYPE_LOCK;
req_s.sequence_ptr = sequence_ptr;
req_s.request_id = g_request_id++;
arb_sequence_q.push_back(req_s);
end
// Push the request onto the queue
req_s = new();
req_s.grant = 0;
req_s.request = SEQ_TYPE_REQ;
req_s.sequence_id = my_seq_id;
req_s.item_priority = item_priority;
req_s.sequence_ptr = sequence_ptr;
req_s.request_id = g_request_id++;
arb_sequence_q.push_back(req_s);
m_update_lists();
// Wait until this entry is granted
// Continue to point to the element, since location in queue will change
m_wait_for_arbitration_completed(req_s.request_id);
// The wait_for_grant_semaphore is used only to check that send_request
// is only called after wait_for_grant. This is not a complete check, since
// requests might be done in parallel, but it will catch basic errors
req_s.sequence_ptr.m_wait_for_grant_semaphore++;
endtask
其中核心函数是 m_register_sequence(sequence_ptr)
function int uvm_sequencer_base::m_register_sequence(uvm_sequence_base sequence_ptr);
if (sequence_ptr.m_get_sqr_sequence_id(m_sequencer_id, 1) > 0)
return sequence_ptr.get_sequence_id();
sequence_ptr.m_set_sqr_sequence_id(m_sequencer_id, g_sequence_id++);
reg_sequences[sequence_ptr.get_sequence_id()] = sequence_ptr;
return sequence_ptr.get_sequence_id();
endfunction
m_sequencer_id如下,其中g_sequencer_id是静态变量,环境中每例化一次sqr,该sqr的m_sequencer_id就会自加,sqr的id各不相同
function uvm_sequencer_base::new (string name, uvm_component parent);
super.new(name, parent);
m_sequencer_id = g_sequencer_id++;
m_lock_arb_size = -1;
endfunction
m_get_sqr_sequence_id/m_set_sqr_sequence_id如下
function int m_get_sqr_sequence_id(int sequencer_id, bit update_sequence_id);
if (m_sqr_seq_ids.exists(sequencer_id)) begin
if (update_sequence_id == 1) begin
set_sequence_id(m_sqr_seq_ids[sequencer_id]);
end
return m_sqr_seq_ids[sequencer_id];
end
if (update_sequence_id == 1)
set_sequence_id(-1);
return -1;
endfunction
// m_set_sqr_sequence_id
// ---------------------
function void m_set_sqr_sequence_id(int sequencer_id, int sequence_id);
m_sqr_seq_ids[sequencer_id] = sequence_id;
set_sequence_id(sequence_id);
endfunction
如果m_sqr_seq_ids存在该sqr_id,现在先学习一个sqr do一个seq,因为一个sqr do多个seq,m_sqr_seq_ids就会多次写入同一键
m_set_sqr_sequence_id每在该sqr上do一次seq,g_sequence_id就会自加
reg_sequences键为sqr_id,索引为seq_id
再次为seq set seq id
wait_for_grant会为所发送的seq打上seq id
arb_sequence_q中放入req_s
function void uvm_sequencer_base::m_update_lists();
m_lock_arb_size++;
endfunction
每do一个item,m_lock_arb_size就是自加
m_wait_for_arbitration_completed等待仲裁,以req_s的seq id来进行仲裁,仲裁结束才可以继续执行
task uvm_sequencer_base::m_wait_for_arbitration_completed(int request_id);
int lock_arb_size;
// Search the list of arb_wait_q, see if this item is done
forever
begin
lock_arb_size = m_lock_arb_size;
if (arb_completed.exists(request_id)) begin
arb_completed.delete(request_id);
return;
end
wait (lock_arb_size != m_lock_arb_size);
end
endtask
首先需要arb_completed里装有req_id,这一步是在m_set_arbitration_completed中实现的,在m_select_sequence中调用该函数,后续再看,假设已经存在该id,存在的话delete后就可跳出forever
wait_for_grant的最后m_wait_for_grant_semaphore++
随后看一下finish_item
virtual task finish_item (uvm_sequence_item item,
int set_priority = -1);
uvm_sequencer_base sequencer;
sequencer = item.get_sequencer();
if (sequencer == null) begin
uvm_report_fatal("STRITM", "sequence_item has null sequencer", UVM_NONE);
end
mid_do(item);
sequencer.send_request(this, item);
sequencer.wait_for_item_done(this, -1);
`ifndef UVM_DISABLE_AUTO_ITEM_RECORDING
sequencer.end_tr(item);
`endif
post_do(item);
endtask
send_request
function void uvm_sequencer_param_base::send_request(uvm_sequence_base sequence_ptr,
uvm_sequence_item t,
bit rerandomize = 0);
REQ param_t;
if (sequence_ptr == null) begin
uvm_report_fatal("SNDREQ", "Send request sequence_ptr is null", UVM_NONE);
end
if (sequence_ptr.m_wait_for_grant_semaphore < 1) begin
uvm_report_fatal("SNDREQ", "Send request called without wait_for_grant", UVM_NONE);
end
sequence_ptr.m_wait_for_grant_semaphore--;
if ($cast(param_t, t)) begin
if (rerandomize == 1) begin
if (!param_t.randomize()) begin
uvm_report_warning("SQRSNDREQ", "Failed to rerandomize sequence item in send_request");
end
end
if (param_t.get_transaction_id() == -1) begin
param_t.set_transaction_id(sequence_ptr.m_next_transaction_id++);
end
m_last_req_push_front(param_t);
end else begin
uvm_report_fatal(get_name(),$sformatf("send_request failed to cast sequence item"), UVM_NONE);
end
param_t.set_sequence_id(sequence_ptr.m_get_sqr_sequence_id(m_sequencer_id, 1));
t.set_sequencer(this);
if (m_req_fifo.try_put(param_t) != 1) begin
uvm_report_fatal(get_full_name(),
$sformatf("Sequencer send_request not able to put to fifo, depth; %0d", m_req_fifo.size()), UVM_NONE);
end
m_num_reqs_sent++;
// Grant any locks as soon as possible
grant_queued_locks();
endfunction
send_request做的是将req放入m_req_fifo,driver调用get_next_item就是从这个fifo获取req
wait_for_item_done
task uvm_sequencer_base::wait_for_item_done(uvm_sequence_base sequence_ptr,
int transaction_id);
int sequence_id;
sequence_id = sequence_ptr.m_get_sqr_sequence_id(m_sequencer_id, 1);
m_wait_for_item_sequence_id = -1;
m_wait_for_item_transaction_id = -1;
if (transaction_id == -1)
wait (m_wait_for_item_sequence_id == sequence_id);
else
wait ((m_wait_for_item_sequence_id == sequence_id &&
m_wait_for_item_transaction_id == transaction_id));
endtask
function void uvm_sequencer::item_done(RSP item = null);
REQ t;
// Set flag to allow next get_next_item or peek to get a new sequence_item
sequence_item_requested = 0;
get_next_item_called = 0;
if (m_req_fifo.try_get(t) == 0) begin
uvm_report_fatal(get_full_name(), {"Item_done() called with no outstanding requests.",
" Each call to item_done() must be paired with a previous call to get_next_item()."});
end else begin
m_wait_for_item_sequence_id = t.get_sequence_id();
m_wait_for_item_transaction_id = t.get_transaction_id();
end
if (item != null) begin
seq_item_export.put_response(item);
end
// Grant any locks as soon as possible
grant_queued_locks();
endfunction
当打完包之后driver会调用item_done。将sequence-base的变量m_wait_for_item_sequence_id/m_wait_for_item_transaction_id都设置成m_req_fifo的seq id 和transaction_id,那么在wait_for_item_done时,就会相等。
接下来看一下driver和sqr的交互,上述的都是seq和sqr的交互
task uvm_sequencer::get_next_item(output REQ t)
task uvm_sequencer::get_next_item(output REQ t);
REQ req_item;
// If a sequence_item has already been requested, then get_next_item()
// should not be called again until item_done() has been called.
if (get_next_item_called == 1)
uvm_report_error(get_full_name(),
"Get_next_item called twice without item_done or get in between", UVM_NONE);
if (!sequence_item_requested)
m_select_sequence();
// Set flag indicating that the item has been requested to ensure that item_done or get
// is called between requests
sequence_item_requested = 1;
get_next_item_called = 1;
m_req_fifo.peek(t);
endtask
如果多次调用get_next_item且没有调用item_done,那么就会报错
task uvm_sequencer_base::m_select_sequence();
int selected_sequence;
// Select a sequence
do begin
wait_for_sequences();
selected_sequence = m_choose_next_request();
if (selected_sequence == -1) begin
m_wait_for_available_sequence();
end
end while (selected_sequence == -1);
// issue grant
if (selected_sequence >= 0) begin
m_set_arbitration_completed(arb_sequence_q[selected_sequence].request_id);
arb_sequence_q.delete(selected_sequence);
m_update_lists();
end
endtask
wait_for_sequences等待seq发送有效的item
m_choose_next_request
m_set_arbitration_completed就是在arb_completed放入索引
先看简单一些的start方法
virtual task start (uvm_sequencer_base sequencer,
uvm_sequence_base parent_sequence = null,
int this_priority = -1,
bit call_pre_post = 1);
if (parent_sequence != null) begin
set_parent_sequence(parent_sequence);
set_use_sequence_info(1);
if (sequencer == null) sequencer = parent_sequence.get_sequencer();
reseed();
end
set_sequencer(sequencer);
start可以传入parent_sequence参数作为该sequence的parent,同时set_sequencer一个挂载的sequencer,set_parent_sequence简单地将parent_sequence赋值给m_parent_sequence, set_use_sequence_info会对m_use_sequence_info进行设置,设置为1代表该sequence的信息可以被拷贝以及打印,如果没有传入sequencer,会将parent_sequence的sequencer作为该sequence的sequencer
reseed同上
if (this_priority < -1) begin
uvm_report_fatal("SEQPRI", $psprintf("Sequence %s start has illegal priority: %0d",
get_full_name(),
this_priority), UVM_NONE);
if (this_priority < 0) begin
if (parent_sequence == null) this_priority = 100;
else this_priority = parent_sequence.get_priority();
end
优先级默认-1,且不能小于-1,
// Check that the response queue is empty from earlier runs
clear_response_queue();
// Function: clear_response_queue
//
// Empties the response queue for this sequence.
virtual function void clear_response_queue();
response_queue.delete();
endfunction
clear_response_queue不理解后续补充
// 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();
不理解后续补充
后续在执行body前会分别执行pre_start/pre_start/pre_do/mid_do,pre_do/mid_do记得是可以通过开关来进行选择是否执行
其次看一下start_item方法
virtual task start_item (uvm_sequence_item item,
int set_priority = -1,
uvm_sequencer_base sequencer=null);
uvm_sequence_base seq;
if (sequencer == null)
sequencer = item.get_sequencer();
if(sequencer == null)
sequencer = get_sequencer();
sequencer.wait_for_grant(this, set_priority);
首先检查item是否存在sequencer,再检查parent_sequence的sequencer
其核心方法是wait_for_grant,接下来学习一下wait_for_grant
task uvm_sequencer_base::wait_for_grant(uvm_sequence_base sequence_ptr,
int item_priority = -1,
bit lock_request = 0);
int my_seq_id;
my_seq_id = m_register_sequence(sequence_ptr);
// m_register_sequence
// -------------------
function int uvm_sequencer_base::m_register_sequence(uvm_sequence_base sequence_ptr);
if (sequence_ptr.m_get_sqr_sequence_id(m_sequencer_id, 1) > 0)
return sequence_ptr.get_sequence_id();
sequence_ptr.m_set_sqr_sequence_id(m_sequencer_id, g_sequence_id++);
reg_sequences[sequence_ptr.get_sequence_id()] = sequence_ptr;
return sequence_ptr.get_sequence_id();
endfunction
function void uvm_sequencer_param_base::send_request(uvm_sequence_base sequence_ptr,
uvm_sequence_item t,
bit rerandomize = 0);
//...
if (m_req_fifo.try_put(param_t) != 1) begin
uvm_report_fatal(get_full_name(),
$sformatf("Sequencer send_request not able to put to fifo, depth; %0d", m_req_fifo.size()), UVM_NONE);
end
send_request会往m_req_fifo put REQ