在公司写代码的时候发现前辈有一段这样的代码:
- ....//其他transaction
- `uvm_create(trans);
- ....
- ....
- `uvm_send(trans);
- tmp_id = trans.get_transaction_id();
- get_response(rsp,tmp_id);
如果前面有其他transaction,这段代码里的get_response不带id的话,就会错误地get到前面transaction的response,有点好奇原理,就去看了看源码。
先从driver入手,如果要返回response的话,要先new一个rsp,然后set_id_info,再put_response:
- //参考张强白皮书
- seq_item_port.get_next_item(req);
- drive_one_pkt(req);
- rsp = new("rsp");
- rsp.set_id_info(req);
- seq_item_port.put_response(rsp);
- seq_item_port.item_done();
先看set_id_info这个函数,在uvm_sequence_item.svh里面,作用就是将req的两个id set到rsp里,我们主要关注transaction_id,继续往后走。
- function void set_id_info(uvm_sequence_item item);
- if (item == null) begin
- uvm_report_fatal(get_full_name(), "set_id_info called with null parameter", UVM_NONE);
- end
- this.set_transaction_id(item.get_transaction_id());
- this.set_sequence_id(item.get_sequence_id());
- endfunction
get_transaction_id在uvm_transaction.svh里面,返回了m_transaction_id,这个id是uvm_transaction里面的一个local integer变量,默认值为-1。这个id的更改在其他地方,稍后再讲解。
- function void uvm_transaction::set_transaction_id(integer id);
- m_transaction_id = id;
- endfunction
- function integer uvm_transaction::get_transaction_id();
- return (m_transaction_id);
- endfunction
然后看put_response函数,在uvm_sequence.svh里,调用了put_base_response函数。
- virtual function void put_response(uvm_sequence_item response_item);
- RSP response;
- if (!$cast(response, response_item)) begin
- uvm_report_fatal("PUTRSP", "Failure to cast response in put_response", UVM_NONE);
- end
- put_base_response(response_item);
- endfunction
put_base_response在uvm_sequence_base.svh里,作用是将response压入队列中,队列默认长度为8,即response_queue_depth=8,当response在队列中的数量为8还继续压入队列后就会报错,一般发生在没有取response的情况下。
- virtual function void put_base_response(input uvm_sequence_item response);
- if ((response_queue_depth == -1) ||
- (response_queue.size() < response_queue_depth)) begin
- response_queue.push_back(response);
- return;
- end
- if (response_queue_error_report_disabled == 0) begin
- uvm_report_error(get_full_name(), "Response queue overflow, response was dropped", UVM_NONE);
- end
- endfunction
接下来到sequence端,我们看看transaction的id和response是怎么get的。transaction_id,在uvm_transaction.svh里,就是返回m_transaction_id。get_response在uvm_sequence.svh里,调用了get_base_response,这里的参数列表里就有transaction_id了。
- virtual task get_response(output RSP response, input int transaction_id = -1);
- uvm_sequence_item rsp;
- get_base_response( rsp, transaction_id);
- $cast(response,rsp);
- endtask
get_base_response在uvm_sequence_base.svh里,可以看到在没有指定transaction_id(id为默认值)的时候,返回的response是从队列直接pop的,指定了以后就遍历队列找id对应的response,这里就是最前面代码的机制。
- virtual task get_base_response(output uvm_sequence_item response, input int transaction_id = -1);
-
- int queue_size, i;
-
- if (response_queue.size() == 0)
- wait (response_queue.size() != 0);
-
- if (transaction_id == -1) begin
- response = response_queue.pop_front();
- return;
- end
-
- forever begin
- queue_size = response_queue.size();
- for (i = 0; i < queue_size; i++) begin
- if (response_queue[i].get_transaction_id() == transaction_id)
- begin
- $cast(response,response_queue[i]);
- response_queue.delete(i);
- return;
- end
- end
- wait (response_queue.size() != queue_size);
- end
- endtask
但是到目前为止,response阶段没有看到id是怎么来的,我们从transaction的create和send阶段找一下。这些宏在uvm_sequence_defines.svh里,可以看到create阶段不涉及id的操作,send里面调了函数,可能会有。因为我们send的是transaction,是uvm_sequence_item类型,uvm_sequence_base是其子类,所以在uvm_send_pri里case不会成功,首先进入start_item里。
- `define uvm_create(SEQ_OR_ITEM) \
- `uvm_create_on(SEQ_OR_ITEM, m_sequencer)
- `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
-
- `define uvm_send(SEQ_OR_ITEM) \
- `uvm_send_pri(SEQ_OR_ITEM, -1)
- `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
start_item在uvm_sequence_base.svh里,貌似也不涉及id的操作,再看finish_item,也在vm_sequence_base.svh里。finish_item调用了sequencer的send_request函数,进去看看。
- 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
-
- item.set_item_context(this, sequencer);
-
- 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
-
- 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在uvm_sequencer_param_base里,可以看到req的transaction_id就是在这里设置的,值为m_next_transaction_id,且每次设置完就自加1,这个值在uvm_sequence_base.svh里,初值为1。
- 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(), "Concurrent calls to get_next_item() not supported. Consider using a semaphore to ensure that concurrent processes take turns in the driver", UVM_NONE);
- end
-
- m_num_reqs_sent++;
- // Grant any locks as soon as possible
- grant_queued_locks();
- endfunction
这下搞清楚了,transaction_id初值为1,且一个sequence里面每个transaction_id的值均不相同,每发一个req,id都会加一,通过这个id可以来区分不同的transaction!