• uvm中transaction的response和id的解读


    在公司写代码的时候发现前辈有一段这样的代码:

    1. ....//其他transaction
    2. `uvm_create(trans);
    3. ....
    4. ....
    5. `uvm_send(trans);
    6. tmp_id = trans.get_transaction_id();
    7. get_response(rsp,tmp_id);

    如果前面有其他transaction,这段代码里的get_response不带id的话,就会错误地get到前面transaction的response,有点好奇原理,就去看了看源码。

    先从driver入手,如果要返回response的话,要先new一个rsp,然后set_id_info,再put_response:

    1. //参考张强白皮书
    2. seq_item_port.get_next_item(req);
    3. drive_one_pkt(req);
    4. rsp = new("rsp");
    5. rsp.set_id_info(req);
    6. seq_item_port.put_response(rsp);
    7. seq_item_port.item_done();

    先看set_id_info这个函数,在uvm_sequence_item.svh里面,作用就是将req的两个id set到rsp里,我们主要关注transaction_id,继续往后走。

    1. function void set_id_info(uvm_sequence_item item);
    2. if (item == null) begin
    3. uvm_report_fatal(get_full_name(), "set_id_info called with null parameter", UVM_NONE);
    4. end
    5. this.set_transaction_id(item.get_transaction_id());
    6. this.set_sequence_id(item.get_sequence_id());
    7. endfunction

    get_transaction_id在uvm_transaction.svh里面,返回了m_transaction_id,这个id是uvm_transaction里面的一个local integer变量,默认值为-1。这个id的更改在其他地方,稍后再讲解。

    1. function void uvm_transaction::set_transaction_id(integer id);
    2. m_transaction_id = id;
    3. endfunction
    4. function integer uvm_transaction::get_transaction_id();
    5. return (m_transaction_id);
    6. endfunction

    然后看put_response函数,在uvm_sequence.svh里,调用了put_base_response函数。

    1. virtual function void put_response(uvm_sequence_item response_item);
    2. RSP response;
    3. if (!$cast(response, response_item)) begin
    4. uvm_report_fatal("PUTRSP", "Failure to cast response in put_response", UVM_NONE);
    5. end
    6. put_base_response(response_item);
    7. endfunction

    put_base_response在uvm_sequence_base.svh里,作用是将response压入队列中,队列默认长度为8,即response_queue_depth=8,当response在队列中的数量为8还继续压入队列后就会报错,一般发生在没有取response的情况下。

    1. virtual function void put_base_response(input uvm_sequence_item response);
    2. if ((response_queue_depth == -1) ||
    3. (response_queue.size() < response_queue_depth)) begin
    4. response_queue.push_back(response);
    5. return;
    6. end
    7. if (response_queue_error_report_disabled == 0) begin
    8. uvm_report_error(get_full_name(), "Response queue overflow, response was dropped", UVM_NONE);
    9. end
    10. endfunction

    接下来到sequence端,我们看看transaction的id和response是怎么get的。transaction_id,在uvm_transaction.svh里,就是返回m_transaction_id。get_response在uvm_sequence.svh里,调用了get_base_response,这里的参数列表里就有transaction_id了

    1. virtual task get_response(output RSP response, input int transaction_id = -1);
    2. uvm_sequence_item rsp;
    3. get_base_response( rsp, transaction_id);
    4. $cast(response,rsp);
    5. endtask

    get_base_response在uvm_sequence_base.svh里,可以看到在没有指定transaction_id(id为默认值)的时候,返回的response是从队列直接pop的,指定了以后就遍历队列找id对应的response,这里就是最前面代码的机制。

    1. virtual task get_base_response(output uvm_sequence_item response, input int transaction_id = -1);
    2. int queue_size, i;
    3. if (response_queue.size() == 0)
    4. wait (response_queue.size() != 0);
    5. if (transaction_id == -1) begin
    6. response = response_queue.pop_front();
    7. return;
    8. end
    9. forever begin
    10. queue_size = response_queue.size();
    11. for (i = 0; i < queue_size; i++) begin
    12. if (response_queue[i].get_transaction_id() == transaction_id)
    13. begin
    14. $cast(response,response_queue[i]);
    15. response_queue.delete(i);
    16. return;
    17. end
    18. end
    19. wait (response_queue.size() != queue_size);
    20. end
    21. 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里。

    1. `define uvm_create(SEQ_OR_ITEM) \
    2. `uvm_create_on(SEQ_OR_ITEM, m_sequencer)
    3. `define uvm_create_on(SEQ_OR_ITEM, SEQR) \
    4. begin \
    5. uvm_object_wrapper w_; \
    6. w_ = SEQ_OR_ITEM.get_type(); \
    7. $cast(SEQ_OR_ITEM , create_item(w_, SEQR, `"SEQ_OR_ITEM`"));\
    8. end
    9. `define uvm_send(SEQ_OR_ITEM) \
    10. `uvm_send_pri(SEQ_OR_ITEM, -1)
    11. `define uvm_send_pri(SEQ_OR_ITEM, PRIORITY) \
    12. begin \
    13. uvm_sequence_base __seq; \
    14. if (!$cast(__seq,SEQ_OR_ITEM)) begin \
    15. start_item(SEQ_OR_ITEM, PRIORITY);\
    16. finish_item(SEQ_OR_ITEM, PRIORITY);\
    17. end \
    18. else __seq.start(__seq.get_sequencer(), this, PRIORITY, 0);\
    19. end

    start_item在uvm_sequence_base.svh里,貌似也不涉及id的操作,再看finish_item,也在vm_sequence_base.svh里。finish_item调用了sequencer的send_request函数,进去看看。

    1. virtual task start_item (uvm_sequence_item item,
    2. int set_priority = -1,
    3. uvm_sequencer_base sequencer=null);
    4. uvm_sequence_base seq;
    5. if(item == null) begin
    6. uvm_report_fatal("NULLITM",
    7. {"attempting to start a null item from sequence '",
    8. get_full_name(), "'"}, UVM_NONE);
    9. return;
    10. end
    11. if($cast(seq, item)) begin
    12. uvm_report_fatal("SEQNOTITM",
    13. {"attempting to start a sequence using start_item() from sequence '",
    14. get_full_name(), "'. Use seq.start() instead."}, UVM_NONE);
    15. return;
    16. end
    17. if (sequencer == null)
    18. sequencer = item.get_sequencer();
    19. if(sequencer == null)
    20. sequencer = get_sequencer();
    21. if(sequencer == null) begin
    22. uvm_report_fatal("SEQ",{"neither the item's sequencer nor dedicated sequencer has been supplied to start item in ",get_full_name()},UVM_NONE);
    23. return;
    24. end
    25. item.set_item_context(this, sequencer);
    26. if (set_priority < 0)
    27. set_priority = get_priority();
    28. sequencer.wait_for_grant(this, set_priority);
    29. `ifndef UVM_DISABLE_AUTO_ITEM_RECORDING
    30. void'(sequencer.begin_child_tr(item, m_tr_handle, item.get_root_sequence_name()));
    31. `endif
    32. pre_do(1);
    33. endtask
    34. virtual task finish_item (uvm_sequence_item item,
    35. int set_priority = -1);
    36. uvm_sequencer_base sequencer;
    37. sequencer = item.get_sequencer();
    38. if (sequencer == null) begin
    39. uvm_report_fatal("STRITM", "sequence_item has null sequencer", UVM_NONE);
    40. end
    41. mid_do(item);
    42. sequencer.send_request(this, item);
    43. sequencer.wait_for_item_done(this, -1);
    44. `ifndef UVM_DISABLE_AUTO_ITEM_RECORDING
    45. sequencer.end_tr(item);
    46. `endif
    47. post_do(item);
    48. endtask

    send_request在uvm_sequencer_param_base里,可以看到req的transaction_id就是在这里设置的,值为m_next_transaction_id,且每次设置完就自加1,这个值在uvm_sequence_base.svh里,初值为1。

    1. function void uvm_sequencer_param_base::send_request(uvm_sequence_base sequence_ptr,
    2. uvm_sequence_item t,
    3. bit rerandomize = 0);
    4. REQ param_t;
    5. if (sequence_ptr == null) begin
    6. uvm_report_fatal("SNDREQ", "Send request sequence_ptr is null", UVM_NONE);
    7. end
    8. if (sequence_ptr.m_wait_for_grant_semaphore < 1) begin
    9. uvm_report_fatal("SNDREQ", "Send request called without wait_for_grant", UVM_NONE);
    10. end
    11. sequence_ptr.m_wait_for_grant_semaphore--;
    12. if ($cast(param_t, t)) begin
    13. if (rerandomize == 1) begin
    14. if (!param_t.randomize()) begin
    15. uvm_report_warning("SQRSNDREQ", "Failed to rerandomize sequence item in send_request");
    16. end
    17. end
    18. if (param_t.get_transaction_id() == -1) begin
    19. param_t.set_transaction_id(sequence_ptr.m_next_transaction_id++);
    20. end
    21. m_last_req_push_front(param_t);
    22. end else begin
    23. uvm_report_fatal(get_name(),$sformatf("send_request failed to cast sequence item"), UVM_NONE);
    24. end
    25. param_t.set_sequence_id(sequence_ptr.m_get_sqr_sequence_id(m_sequencer_id, 1));
    26. t.set_sequencer(this);
    27. if (m_req_fifo.try_put(param_t) != 1) begin
    28. 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);
    29. end
    30. m_num_reqs_sent++;
    31. // Grant any locks as soon as possible
    32. grant_queued_locks();
    33. endfunction

    这下搞清楚了,transaction_id初值为1,且一个sequence里面每个transaction_id的值均不相同,每发一个req,id都会加一,通过这个id可以来区分不同的transaction!

  • 相关阅读:
    基于SpringBoot的单应用项目模板(考虑到后期做微服务,内部已经做了拆分)
    MIPI CSI接口调试方法: data rate计算
    提升用户体验,Xinstall智能判定拉起技术来袭
    HARDVS: Revisiting Human Activity Recognition with Dynamic Vision Sensors
    高德地图爬虫实践:Java多线程并发处理策略
    C语言常用基础知识总结
    腾讯出品小程序自动化测试框架【Minium】系列(七)测试框架的设计和开发
    golang——win10环境protobuf的使用
    【跟小嘉学 Rust 编程】二十四、内联汇编(inline assembly)
    易仓科技×OceanBase:打造跨境行业全生态链的新零售SaaS
  • 原文地址:https://blog.csdn.net/Kizuna_AI/article/details/133684332