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 == null || !__seq.do_not_randomize) && !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
uvm_create_on
`define uvm_create_on(SEQ_OR_ITEM, SEQR) \
begin \
uvm_object_wrapper w_; \
w_ = SEQ_OR_ITEM.get_type(); \
$cast(SEQ_OR_ITEM , create_item(w_, SEQR, `"SEQ_OR_ITEM`"));\
end
uvm_send_pri
`define uvm_send_pri(SEQ_OR_ITEM, PRIORITY) \
begin \
uvm_sequence_base __seq; \
if (!$cast(__seq,SEQ_OR_ITEM)) begin \
start_item(SEQ_OR_ITEM, PRIORITY);\
finish_item(SEQ_OR_ITEM, PRIORITY);\
end \
else __seq.start(__seq.get_sequencer(), this, PRIORITY, 0);\
end
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); \
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
//| sequencer.wait_for_grant(prior) (task) \ start_item \
//| parent_seq.pre_do(1) (task) / \
//| `uvm_do* macros
//| parent_seq.mid_do(item) (func) \ /
//| sequencer.send_request(item) (func) \finish_item /
//| sequencer.wait_for_item_done() (task) /
//| parent_seq.post_do(item) (func) /
上面的表述中其實沒有包含randomize,uvm_do macro中主要涉及三個task: create_item,start_item,finish_item.
這三個task均來自于uvm_sequence_base
class uvm_sequence_base extends uvm_sequence_item;
create_item 就是利用factory create item 或sequence
protected function uvm_sequence_item create_item(uvm_object_wrapper type_var,
uvm_sequencer_base l_sequencer,
string name);
uvm_factory f_ = uvm_factory::get();
$cast(create_item, f_.create_object_by_type( type_var, this.get_full_name(), name ));
create_item.set_item_context(this, l_sequencer);
endfunction
start_item
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 //此task只適用于item,不適用于sequence
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();//返回item的m_sequencer
if(sequencer == null)
sequencer = get_sequencer();//返回當前sequence的m_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_item_context(this, sequencer);
if (set_priority < 0)
set_priority = get_priority();
sequencer.wait_for_grant(this, set_priority);//向sequencer發起請求,等待ack
`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
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
start_item 重要的task是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);//设置当前sequence的sequence_id,下面会讲这个function
// 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
这个task主要做了两件事情:
1. 为当前sequence 分配一个sequence_id.为当前sequencer分配一个sequencer_id;
2. 把request 放入arb_sequence_q参与仲裁;
那怎么分配id呢?
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
每new一个sequencer ,其sequencer_id 就会从1开始自增(m_sequencer_id)
每do一个sequence通过start_item sequence_id就会从1开始自增(g_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
所有sequence共同维护两个哈希 :
m_sqr_seq_ids[sequencer_id] = sequence_id;
reg_sequences[sequence_id] = sequence_ptr;
其作用:
1. 便于知道一个sequencer上do sequence的数目;
2. driver回response时知道回给哪个sequence;
一笔transaction所包含的信息包括:
- transaction_id(在send_request 中设定)
- sequence_id(在m_register_sequence中设定)
- sequencer info
这些信息在start_item和finish_item已完成设定
finish_item中重要的两个task:send_request, wait_for_item_done()。
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++);//每do一个item,transaction_id即自增1
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));//设定sequence_id
t.set_sequencer(this);//设置sequencer
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
---------------------
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)//default 走这个分支 wait (m_wait_for_item_sequence_id == sequence_id);//等到对应sequence_id的item完成 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); //在driver中调用此函数 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();//返回当前item的sequence_id和transaction_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