• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

SOC/IP验证工程师

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

UVM中response的使用详解

put_response与get_response

sequence机制提供了一种sequence→sequencer→driver的单向数据传输机制。但是在复杂的验证平台中,sequence需要根据driver对transaction的反应来决定接下来要发送的transaction,换言之,sequence需要得到driver的一个反馈。sequence机制提供对这种反馈的支持,它允许driver将一个response返回给sequence。
如果需要使用response,那么在sequence中需要使用get_response任务:

//my_case0.sv
3 class case0_sequence extends uvm_sequence #(my_transaction);
…
10   virtual task body();
…
13   repeat (10) begin
14     `uvm_do(m_trans)
15     get_response(rsp);  //
16     `uvm_info("seq", "get one response", UVM_MEDIUM)
17     rsp.print();        
18   end
或  repeat (10) begin
      `uvm_create(m_trans)//可以利用uvm_create和uvm_send的优点
      //assert(ip_tr.randomize() with {ip_tr.src_ip == 'h9999; ip_tr.dest_ip == 'h10000;}) //
      assert(m_trans.randmoize());
      p_sz = m_trans.pload.size();
      {m_trans.pload[p_sz-2],
       m_trans.pload[p_sz-1]}
      = num;
      `uvm_send(m_trans)
    end
…
22 endtask
23
24 `uvm_object_utils(case0_sequence)
25 endclass

在driver中,则需要使用put_response任务:

//my_driver.sv
22 task my_driver::main_phase(uvm_phase phase);
…
27   while(1) begin
28     seq_item_port.get_next_item(req); //seq_item_port.get_next_item(req);
29     drive_one_pkt(req);               //drive_one_pkt(req); 
30     rsp = new("rsp");                 //void'($cast(rsp, req.clone()));
31     rsp.set_id_info(req);             //rsp.set_sequence_id(req.get_sequence_id());
32     seq_item_port.put_response(rsp);  //seq_item_port.put_response(rsp); 
33     seq_item_port.item_done();        //seq_item_port.item_done();
34   end
35 endtask

这里的关键是设置set_id_info函数,它将req的id等信息复制到rsp中。由于可能存在多个sequence在同一个sequencer上启动的情
况,只有设置了rsp的id等信息,sequencer才知道将response返回给哪个sequence。
除了使用put_response外,UVM还支持直接将response作为item_done的参数:

while(1) begin
  seq_item_port.get_next_item(req);
  drive_one_pkt(req);
  rsp = new("rsp");            //
  rsp.set_id_info(req);        //
  seq_item_port.item_done(rsp);//
end

response的数量问题

通常来说,一个transaction对应一个response,但是事实上,UVM也支持一个transaction对应多个response的情况,在这种情况
下,在sequence中需要多次调用get_response,而在driver中,需要多次调用put_response:

task my_driver::main_phase(uvm_phase phase);
  while(1) begin
    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); //1
    seq_item_port.put_response(rsp); //2
    seq_item_port.item_done();
  end
endtask

class case0_sequence extends uvm_sequence #(my_transaction);
  virtual task body();
    repeat (10) begin
      `uvm_do(m_trans)
      get_response(rsp);  //1
      rsp.print();
      get_response(rsp);  //2
      rsp.print();
    end
  endtask
endclass

当存在多个response时,将response作为item_done参数的方式就不适用了。由于一个transaction只能对应一个item_done,所以
使用多次item_done(rsp)是会出错的。
response机制的原理是driver将rsp推送给sequencer,而sequencer内部维持一个队列,当有新的response进入时,就推入此队
列。但是此队列的大小并不是无限制的,在默认情况下,其大小为8。当队列中有8个response时,如果driver再次向此队列推送新
的response,UVM就会给出如下错误提示:
UVM_ERROR @ 1753500000: uvm_test_top.env.i_agt.sqr@@case0_sequence [uvm_test_top.env.i_agt.sqr.case0_sequence] Response queue overflow, response was dropped
因此,如果在driver中每个transaction后都发送一个response,而sequence又没能及时get_response,sequencer中的response队列
就存在溢出的风险。

response handler与另类的response

前面讲述的get_response和put_response是一一对应的。当在sequence中启动get_response时,进程就会阻塞在那里,一直到
response_queue中被放入新的记录。如果driver能够马上将response通过put_response的方式传回sequence,那么sequence被阻塞的进
程就会得到释放,可以接着发送下一个transaction给driver。但是假如driver需要延时较长的一段时间才能将transaction传回,在此期
间,driver希望能够继续从sequence得到新的transaction并驱动它,但是由于sequence被阻塞在了那里,根本不可能发出新的
transaction。
发生上述情况的主要原因为sequence中发送transaction与get_response是在同一个进程中执行的,假如将二者分离开来,在不同
的进程中运行将会得到不同的结果。在这种情况下需要使用response_handler:

//my_case0.sv
3 class case0_sequence extends uvm_sequence #(my_transaction);
…
10   virtual task pre_body();
11     use_response_handler(1);  //打开response的response_handler功能
12   endtask
13
14   virtual function void response_handler(uvm_sequence_item response);  //当打开response_handler功能后,用户需要
15     if(!$cast(rsp, response))   //重载虚函数response_handler。此函数的参数是一个uvm_sequence_item类型的指针,需要                                         
16       `uvm_error("seq", "can't cast")//首先将其通过cast转换为my_transactiono类型,之后就可以根据rsp的值来决定后续
17     else begin                       //sequence的行为。
18       `uvm_info("seq", "get one response", UVM_MEDIUM)
19       rsp.print();
20     end
21   endfunction                                                          
22
23   virtual task body();
24     if(starting_phase != null)
25       starting_phase.raise_objection(this);
26     repeat (10) begin
27       `uvm_do(m_trans)
28     end
29     #100;
30     if(starting_phase != null)
31       starting_phase.drop_objection(this);
32   endtask
33
34   `uvm_object_utils(case0_sequence)
35 endclass

由于response handler功能默认是关闭的,所以要使用response_handler,首先需要调用use_response_handler函数,打开sequence
的response handler功能。
当打开response handler功能后,用户需要重载虚函数response_handler。此函数的参数是一个uvm_sequence_item类型的指针,
需要首先将其通过cast转换变成my_transaction类型,之后就可以根据rsp的值来决定后续sequence的行为。
无论是put/get_response或者response_handler,都是新建了一个transaction,并将其返回给sequence。事实上,当一个uvm_do语
句执行完毕后,其第一个参数并不是一个空指针,而是指向刚刚被送给driver的transaction。利用这一点,可以实现一种另类的
response:

//my_driver.sv
22 task my_driver::main_phase(uvm_phase phase);
…
27   while(1) begin
28     seq_item_port.get_next_item(req);
29     drive_one_pkt(req);
30     req.frm_drv = "this is information from driver";  //
31     seq_item_port.item_done();
32   end
33 endtask

driver中向req中的成员变量赋值,而sequence则检测这个值:

//my_case0.sv
3 class case0_sequence extends uvm_sequence #(my_transaction);
…
10   virtual task body();
…
13     repeat (10) begin
14       `uvm_do(m_trans)
15       `uvm_info("seq", $sformatf("get information from driver: %0s", m_trans.frm_drv), UVM_MEDIUM)  //
16     end
…
20   endtask
21
22   `uvm_object_utils(case0_sequence)
23 endclass

这种另类的response在很多总线的driver中用到。读者可以参考7.1.1节的内容。

rsp与req类型不同

前面所有的例子中,response的类型都与req的类型完全相同。UVM也支持response与req类型不同的情况。
uvm_driver、uvm_sequencer与uvm_sequence的原型分别是:

class uvm_driver #(type REQ=uvm_sequence_item,type RSP=REQ) extends uvm_component;
class uvm_sequencer #(type REQ=uvm_sequence_item, RSP=REQ) extends uvm_sequencer_param_base #(REQ, RSP);
virtual class uvm_sequence #(type REQ = uvm_sequence_item, type RSP = REQ) extends uvm_sequence_base;

在前面章节的例子中只向它们传递了一个参数,因此response与req的类型是一样的。如果要使用不同类型的rsp与req,那么
driver、sequencer与sequence在定义时都要传入两个参数:

class my_driver extends uvm_driver#(my_transaction, your_transaction);
class my_sequencer extends uvm_sequencer #(my_transaction, your_transaction);
class case0_sequence extends uvm_sequence #(my_transaction, your_transaction);

之后,可以使用put_response来发送response:
//my_driver.sv

22 task my_driver::main_phase(uvm_phase phase);
…
27   while(1) begin
28     seq_item_port.get_next_item(req);
29     drive_one_pkt(req);
30     rsp = new("rsp");                      //
31     rsp.set_id_info(req);                  //
32     rsp.information = "driver information";//
33     seq_item_port.put_response(rsp);       //
34     seq_item_port.item_done();
35   end
36 endtask

使用get_response来接收response:
//my_case0.sv

3 class case0_sequence extends uvm_sequence #(my_transaction, your_transaction);
…
10   virtual task body();
…
13     repeat (10) begin
14       `uvm_do(m_trans)
15       get_response(rsp);  //
16       `uvm_info("seq", $sformatf("response information is: %0s", rsp.information), UVM_MEDIUM)
17     end
…
21   endtask
22
23   `uvm_object_utils(case0_sequence)
24 endclass

除了put/get_response外,也可以使用response handler,这与req及rsp类型相同时完全一样。

关于response的函数:

  // Function: use_response_handler
  //
  // When called with enable set to 1, responses will be sent to the response
  // handler. Otherwise, responses must be retrieved using get_response.
  //
  // By default, responses from the driver are retrieved in the sequence by
  // calling get_response.
  //
  // An alternative method is for the sequencer to call the response_handler
  // function with each response.

  function void use_response_handler(bit enable);
    m_use_response_handler = enable;
  endfunction


  // Function: get_use_response_handler
  //
  // Returns the state of the use_response_handler bit.

  function bit get_use_response_handler();
    return m_use_response_handler;
  endfunction


  // Function: response_handler
  //
  // When the use_response_handler bit is set to 1, this virtual task is called
  // by the sequencer for each response that arrives for this sequence.

  virtual function void response_handler(uvm_sequence_item response);
    return;
  endfunction


  // Function: set_response_queue_error_report_disabled
  //
  // By default, if the response_queue overflows, an error is reported. The
  // response_queue will overflow if more responses are sent to this sequence
  // from the driver than get_response calls are made. Setting value to 0////
  // disables these errors, while setting it to 1 enables them.////

  function void set_response_queue_error_report_disabled(bit value);
    response_queue_error_report_disabled = value;
  endfunction

  
  // Function: get_response_queue_error_report_disabled
  //
  // When this bit is 0 (default value), error reports are generated when
  // the response queue overflows. When this bit is 1, no such error
  // reports are generated.

  function bit get_response_queue_error_report_disabled();
    return response_queue_error_report_disabled;
  endfunction


  // Function: set_response_queue_depth
  //
  // The default maximum depth of the response queue is 8. These method is used////
  // to examine or change the maximum depth of the response queue.////
  //
  // Setting the response_queue_depth to -1 indicates an arbitrarily deep////
  // response queue.  No checking is done.////

  function void set_response_queue_depth(int value);
    response_queue_depth = value;
  endfunction  


  // Function: get_response_queue_depth
  //
  // Returns the current depth setting for the response queue.////

  function int get_response_queue_depth();
    return response_queue_depth;
  endfunction  


  // Function: clear_response_queue
  //
  // Empties the response queue for this sequence.////

  virtual function void clear_response_queue();
    response_queue.delete();
  endfunction
  // channel driver
  class chnl_driver extends uvm_driver #(chnl_trans);
    local virtual chnl_intf intf;

    `uvm_component_utils(chnl_driver)
  
    function new (string name = "chnl_driver", uvm_component parent);
      super.new(name, parent);
    endfunction
  
    function void set_interface(virtual chnl_intf intf);
      if(intf == null)
        $error("interface handle is NULL, please check if target interface has been intantiated");
      else
        this.intf = intf;
    endfunction

    task run_phase(uvm_phase phase);
      fork
       this.do_drive();
       this.do_reset();
      join
    endtask

    task do_reset();
      forever begin
        @(negedge intf.rstn);
        intf.ch_valid <= 0;
        intf.ch_data <= 0;
        intf.ch_data_p <= 0;
      end
    endtask

    task do_drive();
      chnl_trans req, rsp;
      @(posedge intf.rstn);
      forever begin
        seq_item_port.get_next_item(req);
        this.chnl_write(req);
        void'($cast(rsp, req.clone()));//////
        rsp.rsp = 1;
        rsp.set_sequence_id(req.get_sequence_id());//////
        seq_item_port.item_done(rsp);//////
      end
    endtask
  
    task chnl_write(input chnl_trans t);
      foreach(t.data[i]) begin
        @(posedge intf.clk);
        intf.drv_ck.ch_valid <= 1;
        intf.drv_ck.ch_data <= t.data[i];
        intf.drv_ck.ch_data_p <= get_parity(t.data[i]);
        @(negedge intf.clk);
        wait(intf.ch_wait === 'b0);
        `uvm_info(get_type_name(), $sformatf("sent data 'h%8x", t.data[i]), UVM_HIGH)
        repeat(t.data_nidles) chnl_idle();
      end
      repeat(t.pkt_nidles) chnl_idle();
    endtask
    
    task chnl_idle();
      @(posedge intf.clk);
      intf.drv_ck.ch_valid <= 0;
      intf.drv_ck.ch_data <= 0;
      intf.drv_ck.ch_data_p <= 0;
    endtask

    function get_parity(bit[31:0] data);
      return ^data;
    endfunction
  endclass: chnl_driver
  class chnl_data_sequence extends uvm_sequence #(chnl_trans);
    rand int pkt_id = 0;
    rand int ch_id = -1;
    rand int data_nidles = -1;
    rand int pkt_nidles = -1;
    rand int data_size = -1;
    rand int ntrans = 10;
    rand int data[];
    constraint cstr{
      soft pkt_id == 0;
      soft ch_id == -1;
      soft data_nidles == -1;
      soft pkt_nidles == -1;
      soft data_size == -1;
      soft ntrans == 10;
      soft data.size() == data_size;
      foreach(data[i]) soft data[i] == -1;
    };
    `uvm_object_utils_begin(chnl_data_sequence)
      `uvm_field_int(pkt_id, UVM_ALL_ON)
      `uvm_field_int(ch_id, UVM_ALL_ON)
      `uvm_field_int(data_nidles, UVM_ALL_ON)
      `uvm_field_int(pkt_nidles, UVM_ALL_ON)
      `uvm_field_int(data_size, UVM_ALL_ON)
      `uvm_field_int(ntrans, UVM_ALL_ON)
    `uvm_object_utils_end
    `uvm_declare_p_sequencer(chnl_sequencer)
    function new (string name = "chnl_data_sequence");
      super.new(name);
    endfunction

    task body();
      repeat(ntrans) send_trans();
    endtask

    task send_trans();
      chnl_trans req, rsp;
      `uvm_do_with(req, {local::ch_id >= 0 -> ch_id == local::ch_id; 
                         local::pkt_id >= 0 -> pkt_id == local::pkt_id;
                         local::data_nidles >= 0 -> data_nidles == local::data_nidles;
                         local::pkt_nidles >= 0 -> pkt_nidles == local::pkt_nidles;
                         local::data_size >0 -> data.size() == local::data_size; 
                         foreach(local::data[i]) local::data[i] >= 0 -> data[i] == local::data[i];
                         })
      this.pkt_id++;
      `uvm_info(get_type_name(), req.sprint(), UVM_HIGH)
      get_response(rsp);//////
      `uvm_info(get_type_name(), rsp.sprint(), UVM_HIGH)
      assert(rsp.rsp)
        else $error("[RSPERR] %0t error response received!", $time);
    endtask

    function void post_randomize();
      string s;
      s = {s, "AFTER RANDOMIZATION \n"};
      s = {s, "=======================================\n"};
      s = {s, "chnl_data_sequence object content is as below: \n"};
      s = {s, super.sprint()};
      s = {s, "=======================================\n"};
      `uvm_info(get_type_name(), s, UVM_HIGH)
    endfunction
  endclass: chnl_data_sequence


posted on 2022-07-25 23:11  SOC验证工程师  阅读(6241)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3