UVM入门实验5

一、寄存器模型的完善和嵌入

定义uvm_reguvm_reg_block的以及mcdf_rgm寄存器模块。

  // Dedicated register description [write-read reg] with uvm_reg type
  class ctrl_reg extends uvm_reg;
    `uvm_object_utils(ctrl_reg)
    uvm_reg_field reserved;
    rand uvm_reg_field pkt_len;
    rand uvm_reg_field prio_level;
    rand uvm_reg_field chnl_en;

    covergroup value_cg;
      option.per_instance = 1;
      reserved: coverpoint reserved.value[25:0];
      pkt_len: coverpoint pkt_len.value[2:0];
      prio_level: coverpoint prio_level.value[1:0];
      chnl_en: coverpoint chnl_en.value[0:0];
    endgroup

    function new(string name = "ctrl_reg");
      super.new(name, 32, UVM_CVR_ALL);
      void'(set_coverage(UVM_CVR_FIELD_VALS));
      if(has_coverage(UVM_CVR_FIELD_VALS)) begin
        value_cg = new();
      end
    endfunction
    
    virtual function void build();
      reserved = uvm_reg_field::type_id::create("reserved");
      pkt_len = uvm_reg_field::type_id::create("pkt_len");
      prio_level = uvm_reg_field::type_id::create("prio_level");
      chnl_en = uvm_reg_field::type_id::create("chnl_en");
    
      reserved.configure(this, 26, 6, "RO", 0, 26'h0, 1, 0, 0);
      pkt_len.configure(this, 3, 3, "RW", 0, 3'h0, 1, 1, 0);
      prio_level.configure(this, 2, 1, "RW", 0, 2'h3, 1, 1, 0);
      chnl_en.configure(this, 1, 0, "RW", 0, 1'h1, 1, 1, 0);
    endfunction

    function void sample(
      uvm_reg_data_t data,
      uvm_reg_data_t byte_en,
      bit            is_read,
      uvm_reg_map    map
    );
      super.sample(data, byte_en, is_read, map);
      sample_values(); 
    endfunction

    function void sample_values();
      super.sample_values();
      if (get_coverage(UVM_CVR_FIELD_VALS)) begin
        value_cg.sample();
      end
    endfunction
  endclass

    // Dedicated register description [read-only reg] with uvm_reg type
  class stat_reg extends uvm_reg;
    `uvm_object_utils(stat_reg)
    uvm_reg_field reserved;
    rand uvm_reg_field fifo_avail;

    
    covergroup value_cg;
      option.per_instance = 1;
      reserved: coverpoint reserved.value[23:0];
      fifo_avail: coverpoint fifo_avail.value[7:0];
    endgroup

    function new(string name = "stat_reg");
      super.new(name, 32, UVM_CVR_ALL);
      void'(set_coverage(UVM_CVR_FIELD_VALS));
      if(has_coverage(UVM_CVR_FIELD_VALS)) begin
        value_cg = new();
      end
    endfunction

    virtual function void build();
      reserved = uvm_reg_field::type_id::create("reserved");
      fifo_avail = uvm_reg_field::type_id::create("fifo_avail");

      reserved.configure(this, 24, 8, "RO", 0, 24'h0, 1, 0, 0);
      fifo_avail.configure(this, 8, 0, "RO", 0, 8'h20, 1, 1, 0);
    endfunction

    function void sample(
      uvm_reg_data_t data,
      uvm_reg_data_t byte_en,
      bit            is_read,
      uvm_reg_map    map
    );
      super.sample(data, byte_en, is_read, map);
      sample_values(); 
    endfunction

    function void sample_values();
      super.sample_values();
      if (get_coverage(UVM_CVR_FIELD_VALS)) begin
        value_cg.sample();
      end
    endfunction
  endclass

    //MCDF top register block which includes child registers and the address map
  class mcdf_rgm extends uvm_reg_block;
    `uvm_object_utils(mcdf_rgm)
    rand ctrl_reg chnl0_ctrl_reg;
    rand ctrl_reg chnl1_ctrl_reg;
    rand ctrl_reg chnl2_ctrl_reg;
    rand stat_reg chnl0_stat_reg;
    rand stat_reg chnl1_stat_reg;
    rand stat_reg chnl2_stat_reg;

    uvm_reg_map map;

    function new(string name = "mcdf_rgm");
      super.new(name, UVM_NO_COVERAGE);
    endfunction

    virtual function void build();
      chnl0_ctrl_reg = ctrl_reg::type_id::create("chnl0_ctrl_reg");
      chnl0_ctrl_reg.configure(this);
      chnl0_ctrl_reg.build();

      chnl1_ctrl_reg = ctrl_reg::type_id::create("chnl1_ctrl_reg");
      chnl1_ctrl_reg.configure(this);
      chnl1_ctrl_reg.build();

      chnl2_ctrl_reg = ctrl_reg::type_id::create("chnl2_ctrl_reg");
      chnl2_ctrl_reg.configure(this);
      chnl2_ctrl_reg.build();

      chnl0_stat_reg = stat_reg::type_id::create("chnl0_stat_reg");
      chnl0_stat_reg.configure(this);
      chnl0_stat_reg.build();

      chnl1_stat_reg = stat_reg::type_id::create("chnl1_stat_reg");
      chnl1_stat_reg.configure(this);
      chnl1_stat_reg.build();

      chnl2_stat_reg = stat_reg::type_id::create("chnl2_stat_reg");
      chnl2_stat_reg.configure(this);
      chnl2_stat_reg.build();

      // map name, offset, number of bytes, endianess
      map = create_map("map", 'h0, 4, UVM_LITTLE_ENDIAN);

      map.add_reg(chnl0_ctrl_reg, 32'h00000000, "RW");
      map.add_reg(chnl1_ctrl_reg, 32'h00000004, "RW");
      map.add_reg(chnl2_ctrl_reg, 32'h00000008, "RW");
      map.add_reg(chnl0_stat_reg, 32'h00000010, "RO");
      map.add_reg(chnl1_stat_reg, 32'h00000014, "RO");
      map.add_reg(chnl2_stat_reg, 32'h00000018, "RO");

      // specify HDL path
      chnl0_ctrl_reg.add_hdl_path_slice($sformatf("mem[%0d]", `SLV0_RW_REG), 0, 32);
      chnl1_ctrl_reg.add_hdl_path_slice($sformatf("mem[%0d]", `SLV1_RW_REG), 0, 32);
      chnl2_ctrl_reg.add_hdl_path_slice($sformatf("mem[%0d]", `SLV2_RW_REG), 0, 32);
      chnl0_stat_reg.add_hdl_path_slice($sformatf("mem[%0d]", `SLV0_R_REG ), 0, 32);
      chnl1_stat_reg.add_hdl_path_slice($sformatf("mem[%0d]", `SLV1_R_REG ), 0, 32);
      chnl2_stat_reg.add_hdl_path_slice($sformatf("mem[%0d]", `SLV2_R_REG ), 0, 32);

      add_hdl_path("tb.dut.ctrl_regs_inst");

      lock_model();
    endfunction
  endclass

实现reg2mcdf_adapter类的方法reg2bus以及bus2reg

  class reg2mcdf_adapter extends uvm_reg_adapter;
    `uvm_object_utils(reg2mcdf_adapter)
    function new(string name = "reg2mcdf_adapter");
      super.new(name);
      provides_responses = 1;
    endfunction
    function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);		//返回值类型是uvm_sequence_item,完成了子类句柄到父类句柄的转换
      reg_trans t = reg_trans::type_id::create("t");
      t.cmd = (rw.kind == UVM_WRITE) ? `WRITE : `READ;
      t.addr = rw.addr;
      t.data = rw.data;
      return t;
    endfunction
    function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
      reg_trans t;
      if (!$cast(t, bus_item)) begin		//父类句柄到子类句柄的转换
        `uvm_fatal("CASTFAIL","Provided bus_item is not of the correct type")
        return;
      end
      rw.kind = (t.cmd == `WRITE) ? UVM_WRITE : UVM_READ;
      rw.addr = t.addr;
      rw.data = t.data;
      rw.status = UVM_IS_OK;
    endfunction
  endclass

mcdf_env中分别声明register blockadapterpredictor,并完成例化,同时在connect阶段中完成句柄的连接。

  // MCDF top environment
  class mcdf_env extends uvm_env;
    chnl_agent chnl_agts[3];
    reg_agent reg_agt;
    fmt_agent fmt_agt;
    mcdf_checker chker;
    mcdf_coverage cvrg;
    mcdf_virtual_sequencer virt_sqr;
    //declare the mcdf_rgm handle, reg2mcdf_adapter handle and the
    //predictory handle
    mcdf_rgm rgm;
    reg2mcdf_adapter adapter;
    uvm_reg_predictor #(reg_trans) predictor;

    `uvm_component_utils(mcdf_env)
    ...

	function void build_phase(uvm_phase phase);
      super.build_phase(phase);
      this.chker = mcdf_checker::type_id::create("chker", this);
      foreach(chnl_agts[i]) begin
        this.chnl_agts[i] = chnl_agent::type_id::create($sformatf("chnl_agts[%0d]",i), this);
      end
      this.reg_agt = reg_agent::type_id::create("reg_agt", this);
      this.fmt_agt = fmt_agent::type_id::create("fmt_agt", this);
      this.cvrg = mcdf_coverage::type_id::create("cvrg", this);
      virt_sqr = mcdf_virtual_sequencer::type_id::create("virt_sqr", this);
      //instantiate those objects
      //  -mcdf_rgm object
      //  -reg2mcdf_adapter object
      //  -predictory object
      //and finish necessary configuration 
      rgm = mcdf_rgm::type_id::create("rgm", this);
      rgm.build();
      adapter = reg2mcdf_adapter::type_id::create("adapter", this);
      predictor = uvm_reg_predictor#(reg_trans)::type_id::create("predictor", this);
    endfunction
   
    function void connect_phase(uvm_phase phase);
      super.connect_phase(phase);
      chnl_agts[0].monitor.mon_bp_port.connect(chker.chnl0_bp_imp);
      chnl_agts[1].monitor.mon_bp_port.connect(chker.chnl1_bp_imp);
      chnl_agts[2].monitor.mon_bp_port.connect(chker.chnl2_bp_imp);
      reg_agt.monitor.mon_bp_port.connect(chker.reg_bp_imp);
      fmt_agt.monitor.mon_bp_port.connect(chker.fmt_bp_imp);
      virt_sqr.reg_sqr = reg_agt.sequencer;
      virt_sqr.fmt_sqr = fmt_agt.sequencer;
      foreach(virt_sqr.chnl_sqrs[i]) virt_sqr.chnl_sqrs[i] = chnl_agts[i].sequencer;
      // Link the register model with the adapter and the predictor
      rgm.map.set_sequencer(reg_agt.sequencer, adapter);
      reg_agt.monitor.mon_ana_port.connect(predictor.bus_in);
      predictor.map = rgm.map;
      predictor.adapter = adapter;
      virt_sqr.rgm = rgm;
    endfunction

二、寄存器模型的使用

connect阶段实现register block句柄的传递。

// connect the virtual sequencer's rgm handle with rgm object
      virt_sqr.rgm = rgm;
    virtual task body();
      `uvm_info(get_type_name(), "=====================STARTED=====================", UVM_LOW)
      // connect rgm handle
      rgm = p_sequencer.rgm;		//virt_seq在执行body()时,通过p_sequencer拿到virt_sqr里面的rgm句柄

      this.do_reg();
      this.do_formatter();
      this.do_data();

      `uvm_info(get_type_name(), "=====================FINISHED=====================", UVM_LOW)
    endtask

在这里插入图片描述
mcdf_data_consistence_basic_virtual_sequence原有的由总线sequence实现的寄存器读写,改为由寄存器模型操作的寄存器读写方式。

    task do_reg();
      bit[31:0] wr_val, rd_val;
      uvm_status_e status;
      // slv0 with len=8,  prio=0, en=1
      wr_val = (1<<3)+(0<<1)+1;
      rgm.chnl0_ctrl_reg.write(status, wr_val);
      rgm.chnl0_ctrl_reg.read(status, rd_val);
      void'(this.diff_value(wr_val, rd_val, "SLV0_WR_REG"));

      // slv1 with len=16, prio=1, en=1
      wr_val = (2<<3)+(1<<1)+1;
      rgm.chnl1_ctrl_reg.write(status, wr_val);
      rgm.chnl1_ctrl_reg.read(status, rd_val);
      void'(this.diff_value(wr_val, rd_val, "SLV1_WR_REG"));

      // slv2 with len=32, prio=2, en=1
      wr_val = (3<<3)+(2<<1)+1;
      rgm.chnl2_ctrl_reg.write(status, wr_val);
      rgm.chnl2_ctrl_reg.read(status, rd_val);
      void'(this.diff_value(wr_val, rd_val, "SLV2_WR_REG"));

      // send IDLE command
      `uvm_do_on(idle_reg_seq, p_sequencer.reg_sqr)
    endtask

mcdf_full_random_virtual_sequence原有的由总线sequence实现的寄存器读写,改为由寄存器模型预先设置寄存器值,再统一做总线寄存器更新的方式,并且由后门读取的方式取得寄存器值。

    task do_reg();
      bit[31:0] ch0_wr_val;
      bit[31:0] ch1_wr_val;
      bit[31:0] ch2_wr_val;
      uvm_status_e status;

      //reset the register block
      rgm.reset();

      //slv0 with len={4,8,16,32},  prio={[0:3]}, en={[0:1]}
      ch0_wr_val = ($urandom_range(0,3)<<3)+($urandom_range(0,3)<<1)+$urandom_range(0,1);
      ch1_wr_val = ($urandom_range(0,3)<<3)+($urandom_range(0,3)<<1)+$urandom_range(0,1);
      ch2_wr_val = ($urandom_range(0,3)<<3)+($urandom_range(0,3)<<1)+$urandom_range(0,1);

      //set all value of WR registers via uvm_reg::set() 
      rgm.chnl0_ctrl_reg.set(ch0_wr_val);
      rgm.chnl1_ctrl_reg.set(ch1_wr_val);
      rgm.chnl2_ctrl_reg.set(ch2_wr_val);

      //update them via uvm_reg_block::update()
      rgm.update(status);

      //wait until the registers in DUT have been updated
      #100ns;

      //compare all of write value and read value
      rgm.chnl0_ctrl_reg.mirror(status, UVM_CHECK, UVM_BACKDOOR);
      rgm.chnl1_ctrl_reg.mirror(status, UVM_CHECK, UVM_BACKDOOR);
      rgm.chnl2_ctrl_reg.mirror(status, UVM_CHECK, UVM_BACKDOOR);

      // send IDLE command
      `uvm_do_on(idle_reg_seq, p_sequencer.reg_sqr)
    endtask

三、寄存器内建序列的应用

mcdf_reg_builtin_virtual_sequence类中,使用uvm_reg_hw_reset_sequvm_reg_bit_bash_sequvm_reg_access_seq对MCDF寄存器模块展开全面测试。

task do_reg();
      uvm_reg_hw_reset_seq reg_rst_seq = new(); 
      uvm_reg_bit_bash_seq reg_bit_bash_seq = new();
      uvm_reg_access_seq reg_acc_seq = new();

      // wait reset asserted and release
      @(negedge p_sequencer.intf.rstn);
      @(posedge p_sequencer.intf.rstn);

      `uvm_info("BLTINSEQ", "register reset sequence started", UVM_LOW)
      rgm.reset();
      reg_rst_seq.model = rgm;
      reg_rst_seq.start(p_sequencer.reg_sqr);
      `uvm_info("BLTINSEQ", "register reset sequence finished", UVM_LOW)

      `uvm_info("BLTINSEQ", "register bit bash sequence started", UVM_LOW)
      // reset hardware register and register model
      p_sequencer.intf.rstn <= 'b0;
      repeat(5) @(posedge p_sequencer.intf.clk);
      p_sequencer.intf.rstn <= 'b1;
      rgm.reset();
      reg_bit_bash_seq.model = rgm;
      reg_bit_bash_seq.start(p_sequencer.reg_sqr);
      `uvm_info("BLTINSEQ", "register bit bash sequence finished", UVM_LOW)

      `uvm_info("BLTINSEQ", "register access sequence started", UVM_LOW)
      // reset hardware register and register model
      p_sequencer.intf.rstn <= 'b0;
      repeat(5) @(posedge p_sequencer.intf.clk);
      p_sequencer.intf.rstn <= 'b1;
      rgm.reset();
      reg_acc_seq.model = rgm;
      reg_acc_seq.start(p_sequencer.reg_sqr);
      `uvm_info("BLTINSEQ", "register access sequence finished", UVM_LOW)
    endtask

仿真结果:
在这里插入图片描述
在这里插入图片描述

  • 12
    点赞
  • 57
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 《UVM入门进阶实验1文档》是一份用于帮助初学者进一步理解和学习UVM实验指导文档。该文档提供了UVM编程框架的第一个实验,旨在帮助读者熟悉UVM的基本概念和用法。 实验1的目标是创建一个简单的UVM测试环境,并展示如何使用UVM框架进行测试。实验包括以下几个主要步骤: 1. 创建一个UVM测试环境:在这一步骤中,我们需要定义测试环境的结构和组件。包括创建一个顶层测试模块、一个顶层环境模块,以及其他必要的组件,如驱动器、监视器、生成器等。 2. 编写测试用例:在这一步骤中,我们需要编写一个简单的测试用例来验证被测设计的功能。测试用例需要继承自UVM的`uvm_test_case`类,并在`run_phase`中定义测试过程。 3. 编写环境配置:在这一步骤中,我们需要将测试用例和测试环境进行连接,并设置一些必要的运行时参数。通过配置对象的方式,我们可以很方便地配置测试环境中的各个组件。 4. 运行仿真:在这一步骤中,我们需要运行仿真并观察测试结果。通过在测试用例中创建一个sequence对象,我们可以在运行时动态生成测试序列。 《UVM入门进阶实验1文档》详细说明了每一步的具体实现方法,并提供了代码示例和可参考的资源链接。 通过完成实验1,读者可以对UVM的基本概念和使用方法有一个更深入的了解。这将为进一步学习和掌握UVM提供坚实的基础,并为以后的工作打下良好的基础。同时,实验1还可以帮助读者培养UVM编程的思维方式和调试技巧,提高工作效率。 总之,《UVM入门进阶实验1文档》是一份非常有价值的学习资料,通过按照文档的指导完成实验,读者可以在短时间内快速入门并掌握UVM的基本用法。 ### 回答2: 《UVM入门进阶实验1文档》是一本介绍UVM(Universal Verification Methodology)的入门实验指导书。UVM是一种用于验证硬件设计的方法学,它提供了一套面向对象的验证框架,可以用于设计验证的自动化和重用。 本文档首先简要介绍了UVM的概念和特点,然后详细讲解了实验1的内容。实验1主要涉及到UVM中最基础的概念和类的使用方法。首先,介绍了UVM中的基础类,如uvm_component、uvm_object和uvm_sequence等,以及它们的继承关系和功能。然后,介绍了如何创建和管理UVM环境,并讲解了如何使用UVM Testbench中的各种组件来进行设计验证。最后,讲解了一些常用的调试技巧和工具,如波形查看器和消息记录器等。 在实验1中,学员将通过几个简单的示例,来熟悉UVM的基本概念和使用方法。例如,学员将学习如何创建一个简单的UVM Testbench,并使用UVM的配置机制来对其进行配置。此外,学员还将学习如何创建和管理UVM Sequences,并在Testbench中使用它们来生成随机的输入数据。最后,学员将学习如何使用UVM里的Transaction来封装输入输出数据,以及如何使用Scoreboard来进行结果验证。 通过完成实验1,学员将掌握UVM中最基本的概念和使用方法,为后续的进阶实验打下基础。同时,学员将对UVM的工作原理和设计验证的流程有一个清晰的认识,为进一步深入学习和应用UVM提供了基础。 ### 回答3: 《UVM入门进阶实验1文档》是一份详细介绍了如何使用UVM进行验证的教程。UVM是一种用于硬件验证的开放式框架,能够帮助工程师更高效地开发和执行验证环境。 该文档首先简要介绍了UVM的背景和原理,包括UVM Testbench的组成结构和工作流程。然后,文档逐步指导读者完成实验1,并提供了实验所需的样例代码和测试平台。 在实验1中,文档首先指导读者创建一个简单的UVM环境,并介绍了UVM的基本类和功能。然后,通过一个简单的例子演示了如何创建一个UVM测试,包括定义测试类、产生和驱动测试向量、分析和比较结果等。读者可以按照文档提供的步骤和示例代码,逐步完成实验。 在实验进行过程中,文档还不断提供了一些常见问题和解决方法。这些问题和解决方法能够帮助读者更好地理解和应用UVM,解决遇到的困惑和难题。 该文档还包括对实验的详细说明和解析,比如UVM环境的搭建、测试向量的生成和分析等。通过这些详细说明和解析,读者可以更深入地理解UVM的工作原理和实现方式。 总之,《UVM入门进阶实验1文档》是一份非常实用的教程,帮助读者快速入门和进阶使用UVM进行硬件验证。通过该文档的学习和实验,读者能够掌握UVM的基本概念和使用方法,为日后的硬件验证工作打下坚实的基础。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值