UVM设计模式 (八)适配器模式、寄存器模型详解

适配器模式

适配器模式Adapter Pattern)是结构性设计模式,其目的是协调不兼容的结构,把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。
寄存器模型中的adapter就是一个适配器,提供了reg2busbus2reg的接口,充当uvm_reg_mapuvm_sequencer中的转换器。uvm_reg_map操作uvm_reg_bus_op类型的item,而uvm_sequencer操作uvm_sequence_item类型的item。

adapter

继承uvm_reg_adapter,重写reg2busbus2reg这两个pure virtual function。

class my_adapter extends uvm_reg_adapter;
    string tID = get_type_name();
    `uvm_object_utils(my_adapter)
   function new(string name="my_adapter");
      super.new(name);
   endfunction : new

   function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
      bus_transaction tr;
      tr = new("tr"); 
      tr.addr = rw.addr;
      tr.bus_op = (rw.kind == UVM_READ) ? BUS_RD: BUS_WR;
      if (tr.bus_op == BUS_WR)
         tr.wr_data = rw.data; 
      return tr;
   endfunction : reg2bus

   function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
      bus_transaction tr;
      if(!$cast(tr, bus_item)) begin
         `uvm_fatal(tID,
          "Provided bus_item is not of the correct type. Expecting bus_transaction")
          return;
      end
      rw.kind = (tr.bus_op == BUS_RD) ? UVM_READ : UVM_WRITE;
      rw.addr = tr.addr;
      rw.byte_en = 'h3;
      rw.data = (tr.bus_op == BUS_RD) ? tr.rd_data : tr.wr_data;
      rw.status = UVM_IS_OK;
   endfunction : bus2reg
endclass : my_adapter

在connect_phase中,adapter将uvm_reg_map和sequencer连接,实际就是调用set_sequencer给uvm_reg_map中的成员变量m_sequencer m_adapter赋值:

function void base_test::connect_phase(uvm_phase phase);
   super.connect_phase(phase);
   ......
   rm.default_map.set_sequencer(env.bus_agt.sqr, reg_sqr_adapter);
   rm.default_map.set_auto_predict(1);
endfunction

// uvm_reg_map.svh
function void uvm_reg_map::set_sequencer(uvm_sequencer_base sequencer,
                                         uvm_reg_adapter adapter=null);
   if (sequencer == null) begin
      `uvm_error("REG_NULL_SQR", "Null reference specified for bus sequencer");
      return;
   end
   if (adapter == null) begin
      `uvm_info("REG_NO_ADAPT", {"Adapter not specified for map '",get_full_name(),
        "'. Accesses via this map will send abstract 'uvm_reg_item' items to sequencer '",
        sequencer.get_full_name(),"'"},UVM_MEDIUM)
   end
   m_sequencer = sequencer;
   m_adapter = adapter;
endfunction

对adapter的调用发生在uvm_reg_map的内部,详细调用reg2busbus2reg的过程见下节:

寄存器模型

组成

uvm_reg_field: 包含uvm_reg_data_t类型的三个成员变量 value, m_mirrored, m_desire
uvm_reg: 包含成员变量m_fields,加入各个uvm_reg_field
uvm_reg_block: uvm_reg的集合,uvm_reg需要加入到uvm_reg_map中,且属于同一个uvm_reg_block。每个uvm_reg_block至少包含一个uvm_reg_map。uvm_reg_block中也可以再添加子uvm_reg_block,同时子uvm_reg_block的uvm_reg_map也需要添加到父uvm_reg_block的uvm_reg_map中。
uvm_reg_map: 包含成员变量m_adapter,m_sequencer,负责FRONTDOOR发送item给driver。
uvm_reg_item: 继承于uvm_sequence_item,可以看作是寄存器访问的transaction。
uvm_reg_bus_op: 在uvm_reg_map中由uvm_reg_item转化而来,是一个struct类型的变量。在这里插入图片描述

ACCESS方式
reg write
  1. 创建uvm_reg_item实例 rw,一般不用指定其 uvm_sequence_base型的parent。但是FRONTDOOR形式发送transaction需要通过sequence来协助完成,会自动创建一个sequence。
  2. 可以指定map,通过选定的map访问寄存器。举例:CPU的 IBUS 和 SBUS访问的地址范围不同,可以分别建立相对应的uvm_reg_map,通过map的set_sequencer设置对应BUS的sequencer。
  3. 调用uvm_regdo_write(rw)
// uvm_reg.svh
task uvm_reg::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
  1. local_map是uvm_reg,uvm_reg_filed,uvm_mem所在uvm_reg_map的指针。
  2. get_root_map获得最顶层的map,如果是套嵌的uvm_reg_block,uvm_reg_map也是套嵌的。
  3. 可以用户自己定义FORONTDOOR,一般使用内建的方式,调用rw.local_map.do_write(rw)
  4. 一般设置default_map.set_auto_predict(1);,这样当寄存器访问结束时,会调用do_predict,自动更新uvm_reg_filed中的三个变量值。
// uvm_reg.svh
// do_write
task uvm_reg::do_write (uvm_reg_item rw);
....
// EXECUTE WRITE...
case (rw.path)
   
   // ...VIA USER BACKDOOR
   UVM_BACKDOOR: begin
   ....
   end
   UVM_FRONTDOOR: begin

      uvm_reg_map system_map = rw.local_map.get_root_map();
      m_is_busy = 1;
      // ...VIA USER FRONTDOOR
      ....
      // ...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
  1. 自动创建一个sequence,作为rw的parent。
  2. 使用寄存器模型一般采用加入adapter的方式,所以走do_bus_write(rw,sequencer,adapter)的分支。
// uvm_reg_map.svh
// do_write(uvm_reg_item rw)

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 (rw.parent == null) begin
     rw.parent = new("default_parent_seq");
     tmp_parent_seq = rw.parent;
  end
  if (adapter == null) begin
    .....
  end
  else begin
    do_bus_write(rw, sequencer, adapter);
  end
  if (tmp_parent_seq != null)
    sequencer.m_sequence_exiting(tmp_parent_seq);
endtask

// get_root_map
function uvm_reg_map uvm_reg_map::get_root_map();
   return (m_parent_map == null) ? this : m_parent_map.get_root_map();
endfunction: get_root_map

// get_sequencer
function uvm_sequencer_base uvm_reg_map::get_sequencer(uvm_hier_e hier=UVM_HIER);
  if (hier == UVM_NO_HIER || m_parent_map == null)
    return m_sequencer;
  return m_parent_map.get_sequencer(hier);
endfunction

// get_adapter
function uvm_reg_adapter uvm_reg_map::get_adapter(uvm_hier_e hier=UVM_HIER);
  if (hier == UVM_NO_HIER || m_parent_map == null)
    return m_adapter;
  return m_parent_map.get_adapter(hier);
endfunction
  1. uvm_reg_map中的do_bus_write将rw转化成uvm_reg_bus_op类型的rw_access。然后在调用adapter.reg2bus(rw_access)转化得到bus sequencer可以接受的transaction类型。
  2. rw.parent.finish_item(bus_req) rw.parent.finish_item(bus_req)相当于平常调用sequence中的body()函数,完成与driver的握手。
  3. 可以手动设置response,也可以使用driver自动返回的response,根据status判断写状态。
// uvm_reg_map.svh
// 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;

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

  .........
    
    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);
      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...",
            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
reg read

read操作和write类似,寄存器模型对于一次读操作,会创建一个sequence,发起读命令,但是返回的读数据,是通过总线上的直接采样,利用driver中的item_done将读数据返回,使得finish_item(bus_req)中的bus_req包含读取的数据。(这一步骤对应下图中的红色虚线)。
在这里插入图片描述

API

寄存器模型中提供了丰富的API,详见:UVM — 寄存器模型相关的一些函数

封装API

寄存器模型内建的API,调用需要通过句柄的形式,对于包含多个uvm_reg_blcok或者field级别的访问,需要列出详细句柄结构,使用起来不够友好。代码如下:

p_sequencer.p_rm.gb_ins_invert.read(status,value,UVM_FRONTDOOR);
p_sequencer.p_rm.bb_ins.depth.write(status,value,UVM_FRONTDOOR);

封装原有API, 编写一个根据传入reg/field的string name来进行访问的函数,调用如下:

p_sequencer.p_rm.get_reg_value(value,"invert");
p_sequencer.p_rm.set_reg_value(value,"depth");
p_sequencer.p_rm.set_field_value(value,"fieldA");

实现源代码如下:

TODO


//..........................................................................
// helper wrapper functions and tasks for easy acces to registers and fileds
//..........................................................................

//..........................................................................
// resolve reg_block and register from field_name (which is the only
// compulsory parameter)
// @return 0 - OK
//	       1 - ERROR
//..........................................................................
function bit get_field_identity(ref string        block_name,
                                ref uvm_reg_block reg_block,
                                ref string        register_name,
                                ref uvm_reg       register,
                                input string      field_name,
                                ref uvm_reg_field field);

    if (block_name == "") begin
        if (register_name == "") begin
            // neither block_name or regsiter_name are known
            field = this.get_field_by_name(field_name);
            if (field == null) begin
                `uvm_error(get_type_name(), $sformatf("Unable to locate register field %s", field_name)) 
                return 1;
            end else begin
                register = field.get_parent();
                if (register == null) begin
                    `uvm_error(get_type_name(), $sformatf("Unable to locate register for field %s", field_name)) 
                    return 1;
            end
            register_name = register.get_name();
            reg_block = register.get_parent();
            if (reg_block == null) begin
                `uvm_error(get_type_name(), $sformatf("Unable to locate register block for %s.%s",register_name, field_name)) 
                return 1;
            end
            block_name = reg_block.get_name();
        end
        end else begin
            //register_name is known, block_name is unknown
            register = this.get_reg_by_name(register_name);
            if (register == null) begin
                `uvm_error(get_type_name(), $sformatf("Unable to locate register%s", register_name))
                return 1;
            end else begin
                reg_block = register.get_parent();
                if (reg_block == null) begin
                    `uvm_error(get_type_name(), $sformatf("Unable to locate register block for register %s",register_name)) 
                    return 1;
                end
                block_name = reg_block.get_name();
                field =register.get_field_by_name(field_name);
                if (field == null) begin
                    `uvm_error(get_type_name(), $sformatf("Unable to locate register field %s.%s.%s",block_name, register_name, field_name))
                    return 1;
                end
            end
        end
    end

    if (register_name == "") begin
        reg_block = this.get_block_by_name(block_name);
        if (reg_block == null) begin
            `uvm_error(get_type_name(), $sformatf("Unable to locate register block %s",block_name))
            return 1;
        end else begin
            field =reg_block.get_field_by_name(field_name);
            if (field == null) begin
                `uvm_error(get_type_name(), $sformatf("Unable to locate register field %s in block %s", field_name, block_name)) 
                return 1;
            end else begin
                register = field.get_parent();
                if (register == null) begin
                    `uvm_error(get_type_name(), $sformatf("Unable to locate register%s", register_name))
                    return 1;
                end
                register_name = register.get_name();
            end
        end
    end

    // this.get_field_by_name() can return the field at once, but in case there are two or more fields with
    // the same name in different registers, we use block_name and register_name to pinpoint the desired register
    reg_block = this.get_block_by_name(block_name);
    if (reg_block == null) begin
        `uvm_error(get_type_name(), $sformatf("Unable to locate register block %s",block_name))
        return 1;
    end else begin
        register = reg_block.get_reg_by_name(register_name);
        if (register == null) begin
            `uvm_error(get_type_name(), $sformatf("Unable to locate register%s.%s", block_name, register_name)) 
            return 1;
        end else begin
            field = register.get_field_by_name(field_name);
            if (field == null) begin
                `uvm_error(get_type_name(), $sformatf("Unable to locate register field %s.%s.%s",block_name, register_name, field_name))
                return 1;
            end
       end
    end

    return 0; //field found
endfunction: get_field_identity

//......................................................................
// resolve reg_block and register from register_name (which is the
// only compulsory parameter)
// @return	0	-	OK
//	        1	-	ERROR
//.....................................................................
function bit get_reg_identity(ref string	    block_name,
                              ref uvm_reg_block reg_block,
                              input string	    register_name,
                              ref uvm_reg	    register);

    if (block_name == "") begin
        register = this.get_reg_by_name(register_name);
        if (register == null) begin
            `uvm_error(get_type_name(), $sformatf("Unable to locate register%s", register_name)) 
            return 1;
        end
        reg_block = register.get_parent();
        if (reg_block == null) begin
            `uvm_error(get_type_name(), $sformatf("Unable to locate register block for register %s",register_name))
            return 1;
        end
        block_name = reg_block.get_name();
    end else begin
        reg_block = this.get_block_by_name(block_name);
        if (reg_block == null) begin
            `uvm_error(get_type_name(), $sformatf("Unable to locate register block %s",block_name)) 
            return 1;
        end else begin
            register = reg_block.get_reg_by_name(register_name);
            if (register == null) begin
                `uvm_error(get_type_name(), $sformatf("Unabletolocate register%s.%s", block_name, register_name)) 
                return 1;
            end
        end
    end

    return 0; //register found
endfunction: get_reg_identity

//...............................................................
// get value of specific register field. register_name and
// register_block are optional
//...............................................................
task get_field_value(ref uvm_reg_data_t data,
                     input string       field_name,
                     input string       register_name="",
                     input string       block_name="" );

    uvm_reg_block	reg_block;
    uvm_reg	        register;
    uvm_reg_field	field;
    bit [7:0]       lsb_pos;
    bit [7:0]       fld_width;
    bit [31:0]      mask;

    if (get_field_identity(block_name, reg_block, register_name, register, field_name, field))
        return; //field not found
    lsb_pos = field.get_lsb_pos();
    fld_width = field.get_n_bits();
    mask = ((2**fld_width)-1) << lsb_pos;
    register.read(status, data);
    data = (data & mask) >> lsb_pos;

    `uvm_info(get_type_name(),
    $sformatf("Value of register field %s.%s.%s is 0x%4x (lsb_pos: %2d fld_width: %2d mask: 0x%4x)", 
    block_name, register_name, field_name, data, lsb_pos, fld_width, mask), UVM_DEBUG)
endtask: get_field_value

//............................................................
// get value of specific register field. If block_name or register_name
// is not specified, the code will try to guess them
//............................................................
	task set_field_value(uvm_reg_data_t dataw,
		                 string	field_name,
		                 string	register_name="",
		                 string	block_name="" );
			
	uvm_reg_block	reg_block;	
    uvm_reg	        register;	
	uvm_reg_field	field;	
	bit [7:0]	lsb_pos;	
	bit [7:0]	fld_width;	
	uvm_reg_data_t	mask, data,	old_data;

	if (get_field_identity(block_name, reg_block, register_name, register, field_name, field))
	    return;//field not found
	lsb_pos =field.get_lsb_pos();
	fld_width = field.get_n_bits();
	mask = ((2**fld_width)-1) << lsb_pos;
	register.read(status, data);
	old_data = data;
	data = (data & ~mask) | ((dataw << lsb_pos) & mask);
	register.write(status, data);

	`uvm_info(get_type_name(),
	$sformatf("Writing 0x%0x to register field %s.%s.%s (new data: 0x%8x old data: 0x%8x lsb_pos: %2d fld_width: %2d mask: 0x%8x)",
	dataw, block_name, register_name, field_name, data, old_data, lsb_pos, fld_width, mask), UVM_DEBUG)
	endtask: set_field_value

	//......................................................................
	// set register value
	//......................................................................
	task set_reg_value(uvm_reg_data_t dataw, string register_name, string block_name="");
	    uvm_reg_block reg_block;
	    uvm_reg	register;

	    if (get_reg_identity(block_name, reg_block,	register_name, register))
	        return; //register not found
	    register.write(status, dataw);
	    `uvm_info(get_type_name(),
	    $sformatf("Writing 0x%0x to register%s.%s",dataw,block_name,register_name), UVM_DEBUG)
	endtask: set_reg_value

	//......................................................................
	// get register value
	//......................................................................
	task get_reg_value(ref uvm_reg_data_t data, input string register_name, string block_name="");
	uvm_reg_block reg_block;
	uvm_reg	register;

	if (get_reg_identity(block_name,reg_block,register_name,register))
	    return; //register not found
	register.read(status, data);
	`uvm_info(get_type_name(),
	$sformatf("Read 0x%0x from register%s.%s",data,	block_name,	register_name), UVM_DEBUG)
	endtask: get_reg_value

//......................................................................
// get mirrored register value
//......................................................................
function bit [31:0] get_mirrored_reg_value(string register_name, string block_name=""); 
    uvm_reg_block reg_block;
    uvm_reg	      register;
    bit [31:0]	  ret;

    if (get_reg_identity(block_name, reg_block, register_name, register)) 
        return 0; //register not found
    ret = register.get_mirrored_value();
    `uvm_info(get_type_name(),
    $sformatf("Read mirrored value 0x%0x from register %s.%s",ret, block_name, register_name), UVM_DEBUG)

    return ret;
endfunction : get_mirrored_reg_value

//........................................................................
// predict a field value in register
//........................................................................
function void predict_field_value(uvm_reg_data_t value,
                                  string field_name, 
                                  uvm_predict_e kind = UVM_PREDICT_READ, 
                                  string register_name="",
                                  string block_name="");
    uvm_reg_block  reg_block;
    uvm_reg_field  field;
    uvm_reg        register;

    if (get_field_identity(block_name, reg_block, register_name, register, field_name, field))
        return;

    assert(field.predict(.value(value), .kind(kind), .path(UVM_BACKDOOR)));
    `uvm_info(get_type_name(),
    $sformatf("Predicted value 0x%0x for field %s, in %s register", value, field_name, register_name), UVM_DEBUG)
endfunction : predict_field_value

predictor

对于只有一个总线访问寄存器且通过寄存器模型的方式,则使用reg_model.default_map.set_auto_predict(1)可以发起read/write操作后自动更新uvm_reg_field成员变量m_mirrored的值,完成预测。
如果不是通过寄存器模型访问总线则不能检测到,这时可以通过monitor检测总线,将采集到的tr发送给uvm_reg_perdictor。uvm_reg_predictor中调用adapter.bus2reg(tr,rw)获得uvm_reg_bus_op类型的rwrg = map.get_reg_by_offset(rw.addr, (rw.kind == UVM_READ));获得reg,最终调用uvm_reg_field的do_predict函数更新三个成员变量 value, m_mirrored, m_desire

env中集成perdictor需要在connect_pahes中添加两步:

  1. perdictor中的map,adapter赋值
  2. moniotr与perdicotr TLM端口连接
另一种 后门访问

寄存器模型内置了不消耗时间的BACKDOOR后门访问。
寄存器模型后门访问时会调用UVM提供的两个API uvm_hdl_read uvm_hdl_deposit。这两个API是以DPI的方式调用VPI,实现通过传入字符串格式的hierarchy路径,来访问simulation database,完成对变量的读取/赋值。
好处:代码放在package中也可以编译通过,因为路径是字符串,不影响编译。
坏处:路径错误时编译阶段无法发现,只有仿真调用时才会发现错误。如果大量调用后门访问,DPI可能会影响仿真器的运行速度。

vcs 寄存器模型生成工具ralgen,提供了一种以interface的形式来进行后门访问。
命令:ralgen -uvm -b -gen_vif_bkdir -t topname <RALF-FILE>
代码生成多了interface和后门调用的class内容:

//ral_host_reg_model_interface.sv:
interface ral_host_regmodel_intf;	
	import uvm_pkg::*; 	
	initial uvm_resource_db#(virtual ral_host_regmodel_intf)::set("*", "uvm_reg_bkdr_if",
						 			  interface::self()); 	
	task ral_host_regmodel_HOST_ID_bkdr_read(uvm_reg_item rw, int index = 0);r
		rw.value[0] = `HOST_REGMODEL_TOP_PATH.host_id;	
	endtask	
	task ral_host_regmodel_HOST_ID_bkdr_write(uvm_reg_item rw, int index = 0);
		`HOST_REGMODEL_TOP_PATH.host_id[31:0] = rw.value[0][31:0];	
	endtask
endinterface
//ral_host_reg_model.sv:
class ral_reg_host_regmodel_HOST_ID_bkdr extends uvm_reg_backdoor;	
	virtual ral_host_regmodel_intf __reg_vif;	
	function new(string name);		
		super.new(name);		
		uvm_resource_db#(virtualral_host_regmodel_intf)::read_by_name(get_full_name(),	"uvm_reg_bkdr_if", __reg_vif);	
	endfunction 	
	virtual task read(uvm_reg_item rw);		
		do_pre_read(rw);		
		__reg_vif.ral_host_regmodel_HOST_ID_bkdr_read(rw);		
		rw.status = UVM_IS_OK;		
		do_post_read(rw);	
	endtask 	
	virtual task write(uvm_reg_item rw);		
		do_pre_write(rw);		
		__reg_vif.ral_host_regmodel_HOST_ID_bkdr_write(rw);		
		rw.status = UVM_IS_OK;		
		do_post_write(rw);	
	endtask	
endclass

这种方式避免了原有后门访问的缺点。

这种方法也可以应用到我们日常开发UVC中,因为package封装的UVC,不可以直接force路径信号,但是可以在顶层做interface连接,然后config_db传入interface供UVC内部处理。

寄存器模型生成脚本

https://github.com/holdenQWER/python_for_IC/blob/main/xls2files/xls2file_gen.py

  • 9
    点赞
  • 80
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
UVM(Universal Verification Methodology)寄存器模型是一用于验证芯片寄存器功能的标准方法。它提供了一个统一的、可重用的框架,用于建立和管理寄存器模型,以及执行寄存器访问和验证。 UVM寄存器模型的主要组成部分包括寄存器模型寄存器层次结构、寄存器操作和寄存器验证环境。 1. 寄存器模型UVM寄存器模型是一个抽象的表示,用于描述芯片内部的寄存器寄存器字段。它提供了一种结构化的方式来定义寄存器的属性、寄存器字段的位宽和访问权限等。 2. 寄存器层次结构:UVM寄存器模型支持多层级的寄存器结构,可以通过层级关系来描述芯片内部的寄存器模块和子模块。这样可以更好地组织和管理寄存器模型,并提供寄存器之间的相互作用和访问。 3. 寄存器操作:UVM提供了一系列的API,用于执行寄存器读写操作。通过这些API,可以向寄存器模型发送读写请求,并获取响应。同时,还可以对寄存器的访问进行配置和控制,如重置、写入默认值等。 4. 寄存器验证环境:UVM寄存器模型可以与其他验证环境进行集成,以验证寄存器功能的正确性。通过使用事务级建模(TLM)接口,可以将寄存器操作与其他验证组件进行交互,并进行功能验证、覆盖率分析和错误注入等。 总之,UVM寄存器模型提供了一种规范化的方法来描述和验证芯片寄存器功能。它具有可重用性、灵活性和扩展性,并能与其他验证组件进行集成,从而提高验证效率和可靠性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值