[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<<f.get_n_bits())-1) << lsb;
         rw.value[0] = (value & msk) >> 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<<sz)-1),
                                                 (value >> lsb) & ((1<<sz)-1),
                                                 rw.local_map);
               final_val |= field_val << lsb;
            end
         end
         rw.kind = UVM_WRITE;
         rw.value[0] = final_val;

         if (bkdr != null)
           bkdr.write(rw);
         else
           backdoor_write(rw);

         do_predict(rw, UVM_PREDICT_WRITE);
      end

      UVM_FRONTDOOR: begin

         uvm_reg_map system_map = rw.local_map.get_root_map();

         m_is_busy = 1;

         // ...VIA USER FRONTDOOR
         if (map_info.frontdoor != null) begin
            uvm_reg_frontdoor fd = map_info.frontdoor;
            fd.rw_info = rw;
            if (fd.sequencer == null)
              fd.sequencer = system_map.get_sequencer();
            fd.start(fd.sequencer, rw.parent);
         end

         // ...VIA BUILT-IN FRONTDOOR
         else begin : built_in_frontdoor

            rw.local_map.do_write(rw);

         end

         m_is_busy = 0;

         if (system_map.get_auto_predict()) begin//当调用rm.default_map.set_auto_predict(1)时,更新mirror值和期望值
            if (rw.status != UVM_NOT_OK) begin
               sample(value, -1, 0, rw.map);
               m_parent.XsampleX(map_info.offset, 0, rw.map);
            end

           do_predict(rw, UVM_PREDICT_WRITE);//更新mirror值和期望值
         end
      end
      
   endcase

   value = rw.value[0];

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

   // POST-WRITE CBS - FIELDS
   foreach (m_fields[i]) begin
      uvm_reg_field_cb_iter cbs = new(m_fields[i]);
      uvm_reg_field f = m_fields[i];
      
      rw.element = f;
      rw.element_kind = UVM_FIELD;
      rw.value[0] = (value >> f.get_lsb_pos()) & ((1<<f.get_n_bits())-1);
      
      for (uvm_reg_cbs cb=cbs.first(); cb!=null; cb=cbs.next())
         cb.post_write(rw);
      f.post_write(rw);
   end
   
   rw.value[0] = value;
   rw.element = this;
   rw.element_kind = UVM_REG;

   // REPORT
   if (uvm_report_enabled(UVM_HIGH, UVM_INFO, "RegModel")) begin
     string path_s,value_s;
     if (rw.path == UVM_FRONTDOOR)
       path_s = (map_info.frontdoor != null) ? "user frontdoor" :
                                               {"map ",rw.map.get_full_name()};
     else
       path_s = (get_backdoor() != null) ? "user backdoor" : "DPI backdoor";

     value_s = $sformatf("=0x%0h",rw.value[0]);

      uvm_report_info("RegModel", {"Wrote register via ",path_s,": ",
                                   get_full_name(),value_s}, UVM_HIGH);
   end

   m_write_in_progress = 1'b0;

   XatomicX(0);

endtask: do_write

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<<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);  //設定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<<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
  • 4
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值