UVM学习之寄存器读写

本文详细剖析了UVM (Universal Verification Methodology) 中的reg_model如何处理后门路径和前门写操作,涉及uvm_reg_item、uvm_reg_map、uvm_reg_item::do_write流程,以及如何在无后门情况下选择前门。重点讲解了get_backdoor、get_local_map和get_reg_map_info等关键函数的作用。
摘要由CSDN通过智能技术生成

task uvm_reg::write(output uvm_status_e      status....)

写操作通过set函数将期望值设置为value

随后将write函数传入的参数抽象成uvm_reg_item类

调用do_write函数

调用Xcheck_accessX,如果存在后门路径,则为rw.map设置为uvm_reg_map::backdoor();,如果找不到后门路径则用前门替代,get_backdoor返回m_backdoor(应该是用来存放后门路径的变量),m_backdoor为null则逐层向上找blk的后门路径

m_regfile_parent在configure来配置,我们环境里配置为null

has_hdl_path还没仔细看,下次补充

   if (rw.path == UVM_BACKDOOR) begin
      if (get_backdoor() == null && !has_hdl_path()) begin
         `uvm_warning("RegModel",
            {"No backdoor access available for register '",get_full_name(),
            "' . Using frontdoor instead."})
         rw.path = UVM_FRONTDOOR;
      end
      else
        rw.map = uvm_reg_map::backdoor();
   end

如果不是后门访问,uvm_reg的m_maps代表该reg在哪些map中add了,在map的add_reg时同时也调用了reg的add_map,填充了m_maps数组。

该函数会返回get_default_map,获得m_maps中索引

rw.local_map = get_local_map(rw.map,caller);

function uvm_reg_map uvm_reg::get_local_map(uvm_reg_map map, string caller="");
   if (map == null)
     return get_default_map();
   if (m_maps.exists(map))
     return map; 
   foreach (m_maps[l]) begin
     uvm_reg_map local_map=l;
     uvm_reg_map parent_map = local_map.get_parent_map();

     while (parent_map != null) begin
       if (parent_map == map)
         return local_map;
       parent_map = parent_map.get_parent_map();
     end
   end
   `uvm_warning("RegModel", 
       {"Register '",get_full_name(),"' is not contained within map '",map.get_full_name(),"'",
        (caller == "" ? "": {" (called from ",caller,")"}) })
   return null;
endfunction

获取map_info,在map中add_reg的同时会将该reg的信息譬如offset,access等记录在m_regs_info关联数组中,返回该result,

map_info = rw.local_map.get_reg_map_info(this);

function uvm_reg_map_info uvm_reg_map::get_reg_map_info(uvm_reg rg, bit error=1);
  uvm_reg_map_info result;
  if (!m_regs_info.exists(rg)) begin
    if (error)
      `uvm_error("REG_NO_MAP",{"Register '",rg.get_name(),"' not in map '",get_name(),"'"})
    return null;
  end
  result = m_regs_info[rg];
  if(!result.is_initialized)
    `uvm_warning("RegModel",{"map '",get_name(),"' does not seem to be initialized correctly, check that the top register model is locked()"})
    
  return result;
endfunction

 在添加reg info时 frontdoor并没有声明但是unmapped声明为0,为什么呢?unmapped含义?

   // If ~unmapped~ is TRUE, the register does not occupy any

   // physical addresses and the base address is ignored.

   // Unmapped registers require a user-defined ~frontdoor~ to be specified.

     if (map_info.frontdoor == null && map_info.unmapped) begin
          `uvm_error("RegModel", {"Register '",get_full_name(),
             "' unmapped in map '",
             (rw.map==null)? rw.local_map.get_full_name():rw.map.get_full_name(),
             "' and does not have a user-defined frontdoor"})
          rw.status = UVM_NOT_OK;
          return 0;
     end

前门写

map_info的frontdoor环境中没有设置,所以不会使用USER FRONTDOOR

调用local_map的do_write方法

      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
            uvm_status_e status;
            if (rw.status != UVM_NOT_OK) begin
               sample(value, -1, 0, rw.map);
               m_parent.XsampleX(map_info.offset, 0, rw.map);
            end

            status = rw.status; // do_predict will override rw.status, so we save it here
            do_predict(rw, UVM_PREDICT_WRITE);
            rw.status = status;
         end
      end
      
   endcase

 adapter映射到最顶层的map

adapter.parent_sequence没看懂,以后补充

task uvm_reg_map::do_write(uvm_reg_item rw);

  uvm_sequence_base tmp_parent_seq;
  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;
    tmp_parent_seq = seq;
  end

  if (rw.parent == null) begin
     rw.parent = new("default_parent_seq");
     tmp_parent_seq = rw.parent;
  end

  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

  if (tmp_parent_seq != null)
    sequencer.m_sequence_exiting(tmp_parent_seq);

endtask

最后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;
  int               n_bits_init;
  uvm_reg_bus_op    accesses[$];

  Xget_bus_infoX(rw, map_info, n_bits_init, lsb, skip);
  addrs=map_info.addr;

  // if a memory, adjust addresses based on offset
  if (rw.element_kind == UVM_MEM)


  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
      。。。。。。。。。。。
    end
    curr_byte=0;
    n_bits= n_bits_init;     
              
    accesses.delete();         
    foreach(addrs[i]) begin: foreach_addr
      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;

      accesses.push_back(rw_access); 

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

    end: foreach_addr
    
    // if set utilizy the order policy
    if(policy!=null)
        policy.order(accesses);
    
    // perform accesses
    foreach(accesses[i]) begin     
      uvm_reg_bus_op rw_access=accesses[i];  
      uvm_sequence_item bus_req;
        
      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)
        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...",
            rw_access.data, addrs[i], rw.map.get_full_name(), rw.status.name()), UVM_FULL)

      if (rw.status == UVM_NOT_OK)
         break;
        
    end

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

  end: foreach_value

endtask: do_bus_write

 Xget_bus_infoX输出reg的bits,map_info

function void uvm_reg_map::Xget_bus_infoX(uvm_reg_item rw,
                                          output uvm_reg_map_info map_info,
                                          output int size,
                                          output int lsb,
                                          output int addr_skip);

  if (rw.element_kind == UVM_MEM) begin
    uvm_mem mem;
    if(rw.element == null || !$cast(mem,rw.element))
      `uvm_fatal("REG/CAST", {"uvm_reg_item 'element_kind' is UVM_MEM, ",
                 "but 'element' does not point to a memory: ",rw.get_name()})
    map_info = get_mem_map_info(mem);
    size = mem.get_n_bits();
  end
  else if (rw.element_kind == UVM_REG) begin
    uvm_reg rg;
    if(rw.element == null || !$cast(rg,rw.element))
      `uvm_fatal("REG/CAST", {"uvm_reg_item 'element_kind' is UVM_REG, ",
                 "but 'element' does not point to a register: ",rw.get_name()})
    map_info = get_reg_map_info(rg);
    size = rg.get_n_bits();
  end
  else if (rw.element_kind == UVM_FIELD) begin
    uvm_reg_field field;
    if(rw.element == null || !$cast(field,rw.element))
      `uvm_fatal("REG/CAST", {"uvm_reg_item 'element_kind' is UVM_FIELD, ",
                 "but 'element' does not point to a field: ",rw.get_name()})
    map_info = get_reg_map_info(field.get_parent());
    size = field.get_n_bits();
    lsb = field.get_lsb_pos();
    addr_skip = lsb/(get_n_bytes()*8);
  end
endfunction

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值