寄存器模型

为什么要进行寄存器验证?

1 验证过程中不但要对DUT功能进行验证

2 还要对DUT中的所包含的存储单元(寄存器和存储器)进行验证

寄存器:存储配置信息、状态信息

存储器:芯片处理时需要的中间数据

最简单验证寄存器的方法?

1 要往寄存器中写入和读取数据,可以启动用于读写数据的sequence来完成

2 sequence产生的读写事务(sequence item)会通过sequencer传递到driver中

3 driver将对应的信号驱动给DUT

4 DUT根据驱动信号作出相应的动作(改变寄存器的值?返回寄存器的值?)

5 monitor从接口捕获相应信息(判断driver操作的是哪个寄存器以及其对应的值),然后送入scoreboard进行对比

寄存器模型(RAL)的必要性?

建立寄存器模型实例化在env当中,作为平台组件的一部分,这样scoreboard可以通过寄存器模型提供的API间接访问DUT的寄存器和存储器模型,这样scoreboard完全与sequence分开,访问的结果也由寄存器模型返回,可以使用read、write读取或者向寄存器中写入值。

事务转换器:寄存器模型要想访问DUT中的寄存器还需要事务转换器Adapter,因为寄存器模型产生的类型与sequencer能接收的类型可能不同,这个转换器Adapter需要自己完成。将寄存器模型与sequencer连接起来,寄存器模型就可以与DUT进行通讯。

前门访问和后门访问?

frontdoor:寄存器模型通过产生事务,并借助agent来访问DUT中的存储器,这种模式称为frontdoor模式

backdoor模式:通过DUT的设计层次路径直接访问寄存器

寄存器模型的构成?

1 register model

UVM中用为寄存器建立模型,这个模型在env中实例化,其他组件通过在内部建立句柄进行引用

内部构成:1个block块中有多个reg、多个block、几个memory以及map,1个reg中包含多个field

寄存器模型是寄存器的存储器和寄存器的镜像

2 adapter

从基类扩展,需要用到两个方法:

reg2bus:寄存器模型产生的uvm_reg_bus_op类型转换为自定义类,转换后的自动发往sequencer

bus2reg:自定义类转换为uvm_reg_bus_op类型的事务对象,转换后的事务对象被寄存器模型分析,更新寄存器模型中相关寄存器的值

3 寄存器模型、adapter与sequencer连接

借助寄存器模型中的map

  • env中的adapter对象赋值给句柄
  • agent中的sequencer对象赋值给句柄
  • 其他任务和函数

寄存器模型嵌入测试平台?

假设测试平台的其他部分已经创建完成

1 为DUT创建寄存器模型

假设DUT包含3个寄存器:config_reg、mode_reg、data_mem

1.1 寄存器config_reg

class config_reg_c extends uvm_reg;   //config_reg寄存器创建的类命名congif_reg_c ,从uvm_reg扩展

    rand uvm_reg_field f1;    //为寄存器添加第一个域f1 , 域的类型为uvm_reg_field (UVM内建类)
    rand uvm_reg_field f2;    //域2
    rand uvm_reg_field f3;    //域3
    rand uvm_reg_field f4;    //域4

    virtual function void build();    //build函数中实例化4个域
        
       f1 = uvm_reg_field::type_id::creat("f1");
       f2 = uvm_reg_field::type_id::creat("f2");
       f3 = uvm_reg_field::type_id::creat("f3");
       f4 = uvm_reg_field::type_id::creat("f4");
       
       f1.configure(this,1,0,"RW",0,h0,1,1,1);   //调用域函数config,配置域在寄存器中的属性
       f2.configure(this,1,1,"RO",0,h0,1,1,1);   //位于哪个寄存器、位宽、初始位、属性
       f3.configure(this,5,2,"RW",0,h0,1,1,1);   //复位值
       f4.configure(this,1,7,"RW",0,h0,1,1,1);   //是否可以被复位、随机化、单独存取
    
     endfunction
     
     'uvm_object_untils(config_reg_c)
     function new(string name = "config_reg_c");
        super.new(new,8,UVM_NO_CONVERAGE);    //super.new函数:对象名、寄存器总位宽、寄存器是否支持覆盖率统计
     end

endclass
1.2 寄存器mode_reg

class mode_reg_c extends uvm_reg;
    rand uvm_reg_field data;
    
    virtual function void build();
        data = uvm_reg_field::type_id::creat("data");
        fi.configure(this,8,0,"RW",0,'h0,1,1,1);
    endfunction

 'uvm_obhect_utils(mode_reg_c)
  function new(string name = "mode_reg_c");
      super.new(name,8,UVM_NO_CONVERAGE);
  end

endclass
1.3 存储器模型data_mem

仅需要在构造函数指定大小和位宽

class data_mem_c extends uvm_mem;   //寄存器继承于uvm_mem类
    'uvm_object_untils(data_mem_c)

    function new(string name = "data_mem_c");
      super.new(name,512,16);
    end
endclass
1.4 封装起来整个寄存器模型

uvm_reg_block基类扩展:包含两个寄存器和一个存储器,并进行了映射,这些地址为以后的操作提供了信息

class reg_model_c extends uvm_reg_block;

   rand config_reg_c config;
   rand mode_reg_c mode_reg;
   data_mem_c data_mem;

virtual function void build();
    config_reg = config_reg_c::type_id::creat("config_reg", ,get_full_name());
    

1.5 为实现前门操作创建转换器Adapter

 

前门访问工作流程

文件: src/ch7/section7.2/my_adapter.sv
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

过程:

从基类uvm_reg_adapter类扩展
文件: src/ch7/section7.2/my_adapter.sv
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

需要对此类中的两个函数进行重载:

一 reg2bus

寄存器模型(通过sequence发送的)uvm_reg_bus_op转换为bus_sequencer能接受的事务对象bus transaction

function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);  //返回uvm_sequence_item类型句柄
      bus_transaction tr;      //创建bus_transaction对象
      tr = new("tr"); 
      tr.addr = rw.addr;       //将rw地址赋值给tr的addr成员
      tr.bus_op = (rw.kind == UVM_READ) ? BUS_RD: BUS_WR;   //根据 rw 的操作类型设置 tr 的 bus_op 成员
      if (tr.bus_op == BUS_WR)   //如果 tr 是写操作(BUS_WR),则将 rw 的数据赋值给 tr 的 wr_data 成员
         tr.wr_data = rw.data; 
      return tr;
   endfunction : reg2bus

返回uvm_sequence_item类型的句柄,该句柄指向转换之后的事务对象

输入参数为需要进行转换的事务对象uvm_reg_bus_op:结构类型为uvm内建的结构体

 

二 bus2reg

当检测到总线有操作,将收集的transaction转换为寄存器模型能接受的形式,以便寄存器模型能更新寄存器的值


   function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);  
      bus_transaction tr;
//使用 $cast 函数尝试将 bus_item 转换为 bus_transaction 类型
      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;   //如果转换成功,将 tr.bus_op 的值用于设置 rw.kind
      rw.addr = tr.addr;
      rw.byte_en = 'h3;
      rw.data = (tr.bus_op == BUS_RD) ? tr.rd_data : tr.wr_data;  //根据 tr.bus_op 的值,将 tr.rd_data 或 tr.wr_data 的值赋给 rw.data
      rw.status = UVM_IS_OK;
   endfunction : bus2reg

1.6 在测试平台中例化寄存器模型、转换器

文件: src/ch7/section7.2/base_test.sv
class base_test extends uvm_test;

   my_env         env;
   my_vsqr        v_sqr;
   reg_model      rm;
   my_adapter     reg_sqr_adapter;

   function new(string name = "base_test", uvm_component parent = null);
      super.new(name,parent);
   endfunction
   
   extern virtual function void build_phase(uvm_phase phase);
   extern virtual function void connect_phase(uvm_phase phase);
   extern virtual function void report_phase(uvm_phase phase);
   `uvm_component_utils(base_test)
endclass


function void base_test::build_phase(uvm_phase phase);
//调用父类(通常是 uvm_test)的 build_phase 方法
   super.build_phase(phase);   
//实例化环境(env)、虚拟序列生成器(v_sqr)和寄存器模型(rm):
   env  =  my_env::type_id::create("env", this); 
   v_sqr =  my_vsqr::type_id::create("v_sqr", this);
   rm = reg_model::type_id::create("rm", this);
//实例化reg_model后的几个函数:congigure函数、build函数、lock_model函数、reset函数
   rm.configure(null, "");
   rm.build();
   rm.lock_model();
   rm.reset();
//创建一个寄存器适配器(reg_sqr_adapter)的实例
   reg_sqr_adapter = new("reg_sqr_adapter");
//将寄存器模型(rm)的实例赋值给环境(env)的 p_rm 成员。这允许环境访问寄存器模型。
   env.p_rm = this.rm;
endfunction

function void base_test::connect_phase(uvm_phase phase);
   super.connect_phase(phase);
   v_sqr.p_my_sqr = env.i_agt.sqr;
   v_sqr.p_bus_sqr = env.bus_agt.sqr;
   v_sqr.p_rm = this.rm;
   rm.default_map.set_sequencer(env.bus_agt.sqr, reg_sqr_adapter);
   rm.default_map.set_auto_predict(1);
endfunction

function void base_test::report_phase(uvm_phase phase);
   uvm_report_server server;
   int err_num;
   super.report_phase(phase);

   server = get_report_server();
   err_num = server.get_severity_count(UVM_ERROR);

   if (err_num != 0) begin
      $display("TEST CASE FAILED");
   end
   else begin
      $display("TEST CASE PASSED");
   end
endfunction

1.7 在验证平台中使用寄存器模型

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值