[UVM] ral model 中的write和read

在使用ral model时,会用到write 和read 操作,今天就讲一下~~

首先讲一下task write & read:

 virtual task write (output uvm_status_e      status,
                    input  uvm_reg_data_t    value,
                    input  uvm_path_e        path = UVM_DEFAULT_PATH,
                    input  uvm_reg_map       map = null,
                    input  uvm_sequence_base parent = null,
                    input  int               prior = -1,
                    input  uvm_object        extension = null,
                    input  string            fname = "",
                    input  int               lineno = 0);

    // create an abstract transaction for this operation
   uvm_reg_item rw;

   XatomicX(1);

   set(value);

   rw = uvm_reg_item::type_id::create("write_item",,get_full_name());
   rw.element      = this;
   rw.element_kind = UVM_REG;
   rw.kind         = UVM_WRITE;
   rw.value[0]     = value;
   rw.path         = path;
   rw.map          = map;
   rw.parent       = parent;
   rw.prior        = prior;
   rw.extension    = extension;
   rw.fname        = fname;
   rw.lineno       = lineno;

   do_write(rw);

   status = rw.status;

   XatomicX(0);

endtask

write task 会调用do_write,其中path default value是UVM_FRONTDOOR,如果是UVM_BACKDOOR,在找不到对应的reg时,会使用FRONTDOOR访问该register。


task uvm_reg_map::do_write(uvm_reg_item rw);

  uvm_reg_map system_map = get_root_map();
  uvm_reg_adapter adapter = system_map.get_adapter();
  uvm_sequencer_base sequencer = system_map.get_sequencer();

  if (adapter != null && adapter.parent_sequence != null) begin
    uvm_object o;
    uvm_sequence_base seq;
    o = adapter.parent_sequence.clone();
    assert($cast(seq,o));
    seq.set_parent_sequence(rw.parent);
    rw.parent = seq;
  end
  if (rw.parent == null)
     rw.parent = new("default_parent_seq");

  if (adapter == null) begin
    rw.set_sequencer(sequencer);
    rw.parent.start_item(rw,rw.prior);
    rw.parent.finish_item(rw);
    rw.end_event.wait_on();
  end
  else begin
    do_bus_write(rw, sequencer, adapter);
  end

endtask

do_wtite 会调用do_bus_write;


ask uvm_reg_map::do_bus_write (uvm_reg_item rw,
                                uvm_sequencer_base sequencer,
                                uvm_reg_adapter adapter);

  uvm_reg_addr_t     addrs[$];
  uvm_reg_map        system_map = get_root_map();
  int unsigned       bus_width  = get_n_bytes();
  uvm_reg_byte_en_t  byte_en    = -1;
  uvm_reg_map_info   map_info;
  int                n_bits;
  int                lsb;
  int                skip;
  int unsigned       curr_byte;
  int                n_access_extra, n_access;

  Xget_bus_infoX(rw, map_info, n_bits, lsb, skip);
  `UVM_DA_TO_QUEUE(addrs,map_info.addr)

  // if a memory, adjust addresses based on offset
  if (rw.element_kind == UVM_MEM)
    foreach (addrs[i])
      addrs[i] = addrs[i] + map_info.mem_range.stride * rw.offset;

  foreach (rw.value[val_idx]) begin: foreach_value

     uvm_reg_data_t value = rw.value[val_idx];

    /* calculate byte_enables */
    if (rw.element_kind == UVM_FIELD) begin
      int temp_be;
      int idx;
      n_access_extra = lsb%(bus_width*8);                
      n_access = n_access_extra + n_bits;
      temp_be = n_access_extra;
      value = value << n_access_extra;
      while(temp_be >= 8) begin
         byte_en[idx++] = 0;
         temp_be -= 8;
      end                        
      temp_be += n_bits;
      while(temp_be > 0) begin
         byte_en[idx++] = 1;
         temp_be -= 8;
      end
      byte_en &= (1<<idx)-1;
      for (int i=0; i<skip; i++)
        void'(addrs.pop_front());
      while (addrs.size() > (n_bits/(bus_width*8) + 1))
        void'(addrs.pop_back());
    end

    foreach(addrs[i]) begin: foreach_addr

      uvm_sequence_item bus_req;
      uvm_reg_bus_op rw_access;
      uvm_reg_data_t data;


      data = (value >> (curr_byte*8)) & ((1'b1 << (bus_width * 8))-1);

      `uvm_info(get_type_name(),
         $sformatf("Writing 'h%0h at 'h%0h via map \"%s\"...",
              data, addrs[i], rw.map.get_full_name()), UVM_FULL);

      if (rw.element_kind == UVM_FIELD) begin
        for (int z=0;z<bus_width;z++)
          rw_access.byte_en[z] = byte_en[curr_byte+z];
      end

      rw_access.kind    = rw.kind;
      rw_access.addr    = addrs[i];
      rw_access.data    = data;
      rw_access.n_bits  = (n_bits > bus_width*8) ? bus_width*8 : n_bits;
      rw_access.byte_en = byte_en;

      adapter.m_set_item(rw);
      bus_req = adapter.reg2bus(rw_access);//通过调用reg2bus,把reg item转化为要发送的bus item
      adapter.m_set_item(null);

      if (bus_req == null)
        `uvm_fatal("RegMem",{"adapter [",adapter.get_name(),"] didnt return a bus transaction"});

      bus_req.set_sequencer(sequencer);
      rw.parent.start_item(bus_req,rw.prior);//把bus item do 给sequencer

      if (rw.parent != null && i == 0)
        rw.parent.mid_do(rw);

      rw.parent.finish_item(bus_req);
      bus_req.end_event.wait_on();

      if (adapter.provides_responses) begin
        uvm_sequence_item bus_rsp;
        uvm_access_e op;
        // TODO: need to test for right trans type, if not put back in q
        rw.parent.get_base_response(bus_rsp);
        adapter.bus2reg(bus_rsp,rw_access);
      end
      else begin
        adapter.bus2reg(bus_req,rw_access);
      end

      if (rw.parent != null && i == addrs.size()-1)
        rw.parent.post_do(rw);

      rw.status = rw_access.status;

      `uvm_info(get_type_name(),
         $sformatf("Wrote 'h%0h at 'h%0h via map \"%s\": %s...",
            data, addrs[i], rw.map.get_full_name(), rw.status.name()), UVM_FULL)

      if (rw.status == UVM_NOT_OK)
         break;

      curr_byte += bus_width;
      n_bits -= bus_width * 8;

    end: foreach_addr

    foreach (addrs[i])
      addrs[i] = addrs[i] + map_info.mem_range.stride;

  end: foreach_value

endtask: do_bus_write

其中,provides_responses的作用介绍一下:
对于支持outstanding的bus agent,需要将provides_responses置为1,对于读操作ral model需要获得data,所以需要等response回过来才能采到数据。


task uvm_reg_map::do_bus_read (uvm_reg_item rw,
                               uvm_sequencer_base sequencer,
                               uvm_reg_adapter adapter);

  uvm_reg_addr_t addrs[$];
  uvm_reg_map        system_map = get_root_map();
  int unsigned       bus_width  = get_n_bytes();
  uvm_reg_byte_en_t  byte_en    = -1;
  uvm_reg_map_info   map_info;
  int                size, n_bits;
  int                skip;
  int                lsb;
  int unsigned       curr_byte;
  int n_access_extra, n_access;

  Xget_bus_infoX(rw, map_info, n_bits, lsb, skip);
  `UVM_DA_TO_QUEUE(addrs,map_info.addr)
  size = n_bits;

  // if a memory, adjust addresses based on offset
  if (rw.element_kind == UVM_MEM)
    foreach (addrs[i])
      addrs[i] = addrs[i] + map_info.mem_range.stride * rw.offset;

  foreach (rw.value[val_idx]) begin: foreach_value

    /* calculate byte_enables */
    if (rw.element_kind == UVM_FIELD) begin
      int temp_be;
      int idx;
      n_access_extra = lsb%(bus_width*8);                
      n_access = n_access_extra + n_bits;
      temp_be = n_access_extra;
      while(temp_be >= 8) begin
         byte_en[idx++] = 0;
         temp_be -= 8;
      end                        
      temp_be += n_bits;
      while(temp_be > 0) begin
         byte_en[idx++] = 1;
         temp_be -= 8;
      end
      byte_en &= (1<<idx)-1;
      for (int i=0; i<skip; i++)
        void'(addrs.pop_front());
      while (addrs.size() > (n_bits/(bus_width*8) + 1))
        void'(addrs.pop_back());
    end
    rw.value[val_idx] = 0;

    foreach (addrs[i]) begin

      uvm_sequence_item bus_req;
      uvm_reg_bus_op rw_access;
      uvm_reg_data_logic_t data;


      `uvm_info(get_type_name(),
         $sformatf("Reading address 'h%0h via map \"%s\"...",
                   addrs[i], get_full_name()), UVM_FULL);

      if (rw.element_kind == UVM_FIELD)
        for (int z=0;z<bus_width;z++)
          rw_access.byte_en[z] = byte_en[curr_byte+z];

      rw_access.kind = rw.kind;
      rw_access.addr = addrs[i];
      rw_access.data = 'h0;
      rw_access.byte_en = byte_en;
      rw_access.n_bits = (n_bits > bus_width*8) ? bus_width*8 : n_bits;

      adapter.m_set_item(rw);
      bus_req = adapter.reg2bus(rw_access);
      adapter.m_set_item(null);
      if (bus_req == null)
        `uvm_fatal("RegMem",{"adapter [",adapter.get_name(),"] didnt return a bus transaction"});

      bus_req.set_sequencer(sequencer);
      rw.parent.start_item(bus_req,rw.prior);

      if (rw.parent != null && i == 0) begin
        rw.parent.mid_do(rw);
      end

      rw.parent.finish_item(bus_req);
      bus_req.end_event.wait_on();

      if (adapter.provides_responses) begin
        uvm_sequence_item bus_rsp;
        uvm_access_e op;
        // TODO: need to test for right trans type, if not put back in q
        rw.parent.get_base_response(bus_rsp);//等待response回过来
        adapter.bus2reg(bus_rsp,rw_access);
      end
      else begin
        adapter.bus2reg(bus_req,rw_access);
      end

      data = rw_access.data & ((1<<bus_width*8)-1);

      rw.status = rw_access.status;

      if (rw.status == UVM_IS_OK && (^data) === 1'bx)
        rw.status = UVM_HAS_X;

      `uvm_info(get_type_name(),
         $sformatf("Read 'h%0h at 'h%0h via map \"%s\": %s...", data,
                   addrs[i], get_full_name(), rw.status.name()), UVM_FULL);

      if (rw.status == UVM_NOT_OK)
         break;

      rw.value[val_idx] |= data << curr_byte*8;

      if (rw.parent != null && i == addrs.size()-1)
        rw.parent.post_do(rw);

      curr_byte += bus_width;
      n_bits -= bus_width * 8;
    end

    if (rw.element_kind == UVM_FIELD)
       rw.value[val_idx] = (rw.value[val_idx] >> (n_access_extra)) & ((1<<size)-1);
  end

endtask: do_bus_read

summary:
write –> do_write –> do_bus_write –> reg2bus –> do item –> wait response –>bus2reg

backdoor的用法

加入hdl path的方法
法1:
1. 在reg_block中设置好uvm_reg的configure函数的第三个参数.

class reg_model extends uvm_reg_block;

  rand reg_invert invert;
  rand reg_counter_high counter_high;
  rand reg_counter_low counter_low;

  virtual function void build();
    invert.configure(this,null,"invert");
    counter_high.configure(this,null,"counter[31:16]");
    counter_low.configure(this,null,"counter[15:0]");

  endfunction

endclass
  1. 在ral model集成到验证平台时,设置好根路径.
function void base_test::build_phase(uvm_phase phase);
  rm = reg_model::type_id::create("rm",this);
  rm.configure(null,"");
  rm.build();
  rm.lock_model();
  rm.reset();
  rm.set_hdl_path_root("top.my_dut");
endfunction

法2:
1. 在reg_block中设置uvm_reg的configure函数的第三个参数.

class reg_model extends uvm_reg_block;

  rand reg_invert invert;
  rand reg_counter_high counter_high;
  rand reg_counter_low counter_low;

  virtual function void build();
    invert.configure(this,null,"");//第三个参数为空
    counter_high.configure(this,null,"");
    counter_low.configure(this,null,"");

    add_hdl_path("top.my_dut");
    invert.add_hdl_path_slice("invert"0,16);

    //invert.add_hdl_path('{ '{"invert", -1, -1} });

    counter_high.add_hdl_path_slice("count[31:16]",16,16);
    counter_low.add_hdl_path_slice("count[15:0]",0,16);
  endfunction

endclass

关于add_hdl_path

function void add_hdl_path (    uvm_hdl_path_slice  slices[],       
string  kind     =  "RTL"   )

Add an HDL path
Add the specified HDL path to the register instance for the specified design abstraction. This method may be called more than once for the same design abstraction if the register is physically duplicated in the design abstractild be specified using the following literal value

       1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0
Bits:  5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
      +-+---+-------------+---+-------+
      |A|xxx|      B      |xxx|   C   |
      +-+---+-------------+---+-------+
add_hdl_path('{ '{"A_reg", 15, 1},
                '{"B_reg",  6, 7},
                '{'C_reg",  0, 4} 
              }
              );

If the register is implementd using a single HDL variable, The array should specify a single slice with its offset and size specified as -1. For example:

r1.add_hdl_path('{ '{"r1", -1, -1} });

uvm_reg::peek()/poke()

这两个方法只针对后门访问。其不管DUT的行为,比如一个只读的寄存器进行写操作,其可以写进去。

class reg_seq extends uvm_sequence;
  task body();
    uvm_status_e status;
    uvm_reg_data_t data;
    p_sequencer.p_rm.counter_low.poke(status,16'hFFFD);
    p_sequencer.p_rm.counter_low.peek(status,data);
  endtask
endclass

猜你喜欢

转载自blog.csdn.net/lbt_dvshare/article/details/82492644
今日推荐