[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::do_write (uvm_reg_item rw);

   uvm_reg_cb_iter  cbs = new(this);
   uvm_reg_map_info map_info;
   uvm_reg_data_t   value; 

   m_fname  = rw.fname;
   m_lineno = rw.lineno;

   if (!Xcheck_accessX(rw,map_info,"write()"))
     return;

   XatomicX(1);

   m_write_in_progress = 1'b1;

   rw.value[0] &= ((1 << m_n_bits)-1);
   value = rw.value[0];

   rw.status = UVM_IS_OK;

   // PRE-WRITE CBS - FIELDS
   begin : pre_write_callbacks
      uvm_reg_data_t  msk;
      int lsb;

      foreach (m_fields[i]) begin
         uvm_reg_field_cb_iter cbs = new(m_fields[i]);
         uvm_reg_field f = m_fields[i];
         lsb = f.get_lsb_pos();
         msk = ((1<> lsb;
         f.pre_write(rw);
         for (uvm_reg_cbs cb=cbs.first(); cb!=null; cb=cbs.next()) begin
            rw.element = f;
            rw.element_kind = UVM_FIELD;
            cb.pre_write(rw);
         end

         value = (value & ~msk) | (rw.value[0] << lsb);
      end
   end
   rw.element = this;
   rw.element_kind = UVM_REG;
   rw.value[0] = value;

   // PRE-WRITE CBS - REG
   pre_write(rw);
   for (uvm_reg_cbs cb=cbs.first(); cb!=null; cb=cbs.next())
      cb.pre_write(rw);

   if (rw.status != UVM_IS_OK) begin
     m_write_in_progress = 1'b0;

     XatomicX(0);
         
     return;
   end
         
   // EXECUTE WRITE...
   case (rw.path)
      
      // ...VIA USER BACKDOOR
      UVM_BACKDOOR: begin
         uvm_reg_data_t final_val;
         uvm_reg_backdoor bkdr = get_backdoor();

         value = rw.value[0];

         // Mimick the final value after a physical read
         rw.kind = UVM_READ;
         if (bkdr != null)
           bkdr.read(rw);
         else
           backdoor_read(rw);

         if (rw.status == UVM_NOT_OK) begin
           m_write_in_progress = 1'b0;
           return;
         end

         begin
            foreach (m_fields[i]) begin
               uvm_reg_data_t field_val;
               int lsb = m_fields[i].get_lsb_pos();
               int sz  = m_fields[i].get_n_bits();
               field_val = m_fields[i].XpredictX((rw.value[0] >> lsb) & ((1<> lsb) & ((1<> f.get_lsb_pos()) & ((1<

uvm_reg中的do_write会调用uvm_reg_map中的do_write

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();//如果bus driver需要特定的item type,可在adapter中設定parent_sequence;否則是uvm_sequence_base 類型
    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;


task 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< (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*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);  //設定item的sequencer,這樣start_item才知道do給哪個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 item do 给sequencer
      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< (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> (n_access_extra)) & ((1<

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

你可能感兴趣的:(UVM)