The UVM Primer -- Chapter14&15 Talking to multiple objcts

Chapter 15 Talking to Multiple Objects

既然我们使用OOP编写程序,那么就应该充分利用OOP编程的优势。我们希望在测试平台中,避免每个模块都读取BFM备份,然后根据clock信号一拍拍的采集数据,进行数据处理。我们希望可以让objects之间可以通信,后面我们将逐步介绍两种object communication的方式:

  • Single-Thread Communication: 同线程内的对象调用本线程内的其他对象的方法;
  • Dual-Thread Communication:两个不同线程内的对象之间的通信;
    本章首先通过色子实验介绍single-thread,下一章介绍uvm_testbench中的单线程通信。

掷骰子实验

  • 要求: 写一段程序实现以下功能,掷2个骰子:
  1. 记录两个骰子的和的平均值;
  2. 记录两个骰子之和的频数;
  3. 记录两个骰子之和在2-12之间的覆盖率;

15.1 不使用object间的通信

我们以求平均值的功能为例,首先声明名称为average的类,average扩展自uvm_component(average需要是验证环境中验证组件),其中:average中通过write函数计算所有掷骰子的结果的和,并统计掷骰子的次数,分别存入dice_total,count; 并在report_phase中打印输出的结果。average类的代码如下:

class average extends uvm_component;
   `uvm_component_utils(average);

   protected real dice_total;         //protected variables, 不希望在类外直接访问的数据
   protected real count;

   function new(string name, uvm_component parent = null);
      super.new(name,parent);
      dice_total = 0.0;               //构造函数中给变量赋初值
      count = 0.0;
   endfunction : new

   function void write(int t);        
      dice_total = dice_total + t;    //计算掷骰子结果之和
      count++;                        //统计掷骰子的次数
   endfunction : write

   function void report_phase(uvm_phase phase);
      $display ("DICE AVERAGE: %2.1f",dice_total/count);     //打印掷骰子的平均值
   endfunction : report_phase
endclass : average

对应的,我们定义histogram和coverage类,并在其中定义write函数进行数据处理,在report_phase函数中打印结果;
定义完测试平台中的structure(uvm_components),我们就可以定义dice_test类,因为本章主要关注线程内通信的特性,所有没有使用uvm_env将各组件打包。我们之间在dice_test中例化环境组件,代码如下:

class dice_test extends uvm_test;
   `uvm_component_utils(dice_test);

   dice_roller dice_roller_h;                       //stimulus,类似此前的add_tester,也是uvm_component
   coverage coverage_h;                         
   histogram histogram_h;
   average average_h;

   function void build_phase(uvm_phase phase);      //在build_phase中例化环境中的组件
      coverage_h = new("coverage_h", this);         //没有使用factory机制例化
      histogram_h = new("histogram_h",this);
      average_h     = new("average_h",this);
      dice_roller_h = new("dice_roller_h",this);
   endfunction : build_phase

   task run_phase(uvm_phase phase);                 //在run_phase中调用其他组件的write方法
      int the_roll;
      phase.raise_objection(this);
      repeat (20) begin                             //重复20次
         the_roll = dice_roller_h.two_dice();       //the_roll返回int类型的,随机掷2个骰子之和
         coverage_h.write(the_roll);                //将掷骰子的结果写入coverage
         histogram_h.write(the_roll);               //将掷骰子的结果写入histogram
         average_h.write(the_roll);                 //将指投资的结果写入average函数
      end
      phase.drop_objection(this);
   endtask : run_phase
   
   function new(string name, uvm_component parent);
      super.new(name,parent);
   endfunction : new
   
endclass : dice_test

以上就是我们按照现有的UVM知识实现的掷骰子功能,(除了未使用env将测试平台的structure和stimulus分开),但是我们没有用到connect的概念,我们仍然需要在dice_test的run_phase中手动将dice_roller的结果写入average,histogram中。这样的代码可以实现功能,但是代码不够简单,结构不够清晰,不能称之为优秀的代码。
为了优化以上代码,UVM提供类似Observer design pattern的模式,使用uvm_analysis_port.

15.2 使用uvm_analysis_port实现objects间的通信

  • Observer design pattern类似于朋友圈模式,在朋友圈中我们只需要编辑内容,点击发送就可以完成。我们不需要告诉我们的朋友我发了一条状态,他们就可以接受到我们分享的信息,此外,朋友圈中的每个朋友都有可以得到我们分享信息的一份备份,并对该信息进行评论回复。甚至,即使我们朋友圈中没有好友我们也可以分享状态。observer design patter的本质和朋友圈类似,在这种设计模式下,一个object(比如dice_roller)可以创建信息并向整个平台分享,无论他的平台中有多少个其他object observer(朋友),平台中其他object可以得到dice_roller发出的信息的备份,可以对此信息进行任何处理。

  • 在掷骰子实验中,dice_roller就是分享信息的object,而其他的object observer都在关注dice_roller发出的信息。但是在UVM中不叫observer而是叫做subscriber;

  • UVM通过两个类实现observer design pattern设计模式:

    • uvm_analysis_port: 为类似dice_roller的object提供向observer广播数据的通道;
      • 在dice_roller中定义analysis变量,并指定需要传输的数据类型;
      • 在build_phase例化analysis port变量;
      • 通过write()函数向analysis port中写数据,write函数就像发状态时按下发送键;
      • 以上三步就是实现analysis port的过程;
      • 一旦通过write()函数向analysis port写数据,数据就会达到所有subscriber;
      • analysis port也包含connect函数,我们可以在env或test的connect_phase中将port连接至所有subscriber;connect函数的的参数是analysis_export类型的变量;
    • uvm_subscriber:uvm_component的子类,允许其子类订阅dice_roller,监控其发出的信息。
      • uvm_subscriber为我们提供了一些的功能,但也提出了一些需求;
      • uvm_subscriber为我们提供了analysis_export, 我们可以通过这个export接受analysis的数据;
      • uvm_subscriber要求我们重载write()函数,在write函数中对接受的数据进行处理;

现在我们使用UVM提供的uvm_analysis_port和uvm_subscriber重新撰写掷骰子程序。

  • 在dice_roller中使用uvm_analysis_port
class dice_roller extends uvm_component;
   `uvm_component_utils(dice_roller);

   uvm_analysis_port #(int) roll_ap;           //1. 定义uvm_analysis_port变量

   function void build_phase (uvm_phase phase);
      
      roll_ap = new("roll_ap",this);           //2. 在build_phase中例化analysis变量
      
   endfunction : build_phase
   
   rand byte die1;
   rand byte die2;
   
   constraint d6 { die1 >= 1; die1 <= 6; 
                   die2 >= 1; die2 <= 6; }

   task run_phase(uvm_phase phase);
      int the_roll;
      phase.raise_objection(this);
      void'(randomize());
      repeat (20) begin
         void'(randomize());
         the_roll = die1 + die2;
         roll_ap.write(the_roll);              //3. 在run_phase中调用write函数
      end                                      //不需要关心到底哪些组件在处理这些数据
      phase.drop_objection(this);
   endtask : run_phase
   
   function new(string name, uvm_component parent);
      super.new(name,parent);
   endfunction : new
   
endclass : dice_roller
  • 使用uvm_subscriber扩展average,histogram等组件(扩展时需要制定analysis_export传输的变量类型)
class average extends uvm_subscriber #(int);  //参数化的类subscriber,扩展式需要指定类型
   `uvm_component_utils(average);

   real dice_total;
   real count;
   //1. 虽然没写出来,但average从uvm_subscriber中继承了analysis_export变量、
   //此变量通常作为connect函数的参数

   function new(string name, uvm_component parent = null);
      super.new(name,parent);
      dice_total = 0.0;
      count = 0.0;
   endfunction : new

   function void write(int t);        //2. 使用write函数处理analysis接受到的数据
      dice_total = dice_total + t;    
      count++;
   endfunction : write

   function void report_phase(uvm_phase phase);
      
      $display ("DICE AVERAGE: %2.1f",dice_total/count);
   endfunction : report_phase
   
endclass : average
  • 在uvm_test或uvm_env的connect_phase中将analysis_port和export连接起来
class dice_test extends uvm_test;
   `uvm_component_utils(dice_test);

   dice_roller dice_roller_h;
   coverage coverage_h;
   histogram histogram_h;
   average average_h;

   function void connect_phase(uvm_phase phase);
      dice_roller_h.roll_ap.connect(coverage_h.analysis_export);    //在connect_phase调用analysis_port的connect函数,并将各个observer中的analysis_export传入    
      dice_roller_h.roll_ap.connect(histogram_h.analysis_export);   //conect_phase调用顺序 bottom-up   
      dice_roller_h.roll_ap.connect(average_h.analysis_export);     //先调用coverage,在调用uvm_test 
   endfunction : connect_phase
   
   function new(string name, uvm_component parent);
      super.new(name,parent);
   endfunction : new

   function void build_phase(uvm_phase phase);       //build_phase调用顺序 top-down
                                                     //先调用test的build_phase,在调用coverage的build_phase
      dice_roller_h = new("dice_roller_h", this);
      coverage_h    = new("coverage_h", this);
      histogram_h   = new("histogram_h",this);
      average_h     = new("average_h",this);

   endfunction : build_phase

endclass : dice_test
  • 使用UVM的analysis_port和subscriber类之后,我们的测试平台环境如下图所示:
    在这里插入图片描述
  • 我们通过observer design pattern将测试平台中各个object连接起来,当我们需要新增class处理数据时,我们只需要新写一个subscriber扩展的class,然后将其连接到dice_roller的analysis port上,其他不用作任何改动。下一章介绍如何将UVM的analysis_port和subscriber类加入我们此前的测试平台。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值