The UVM Primer -- Chapter 18 Put and Get Ports in Action

本文档详细介绍了如何在TinyALU测试平台中应用UVM的Put和Get端口机制,将测试者拆分为专注指令生成的random_tester和与BFM交互的driver模块,以提高模块化和可重用性。章节涵盖了未修改的base_tester、修改后的base_tester、新添加的driver以及修改后的env结构。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Chapter 18 Put and Get Ports in Action

上一章,我们介绍了使用put和get port进行线程间通信的机制,本章我们将put/get port机制假如TinyALU测试平台。从学习UVM开始,UVM就不断地把测试平台拆解成一个个小的功能模块,这样易于debug和重用。本章我们将tester按照功能进行拆分。

  • tester的两个功能:
    • 选择操作类型op_type;
    • 将操作类型和操作数送至BFM;
  • 我们将tester分成两个部分,一个专注于产生操作类型的opcode,另一个专注于和BFM交互,此时测试环境的框图如下所示:
    在这里插入图片描述
  • 其中driver代表tester中和BFM交互的模块,driver从tester获取opcode,并以信号的方式送给BFM。random_tester个driver通过TLM FIFO连接,现在我们的tester被分为random_tester和driver两个部分。

18.1 未修改前的base_tester

  • 如我们在UVM_ENV中所记录一致,虚类base_tester中需要定义了get_op和get_data两个纯虚方法,出新方法必须在子类(如random_tester)中重载;
  • base_tester好处,我们只需要修改base中的run_phase,random_tester和add_tester中继承的run_phase也会更新,不需要再多处代码修改。
  • base_tester中bfm,操作类型,数据集中处理,功能划分不够清晰;
virtual class base_tester extends uvm_component;
`uvm_component_utils(base_tester)
   virtual tinyalu_bfm bfm;
   
   function void build_phase(uvm_phase phase);
      if(!uvm_config_db #(virtual tinyalu_bfm)::get(null, "*","bfm", bfm))
        $fatal("Failed to get BFM");               //在tester中get bfm
   endfunction : build_phase

   pure virtual function operation_t get_op();

   pure virtual function byte get_data();

   task run_phase(uvm_phase phase);
      byte         unsigned        iA;
      byte         unsigned        iB;
      operation_t                  op_set;
      shortint     result;
      
      phase.raise_objection(this);                 //同时完成:激励产生 & 数据传输
      bfm.reset_alu();   
      repeat (1000) begin : random_loop
         op_set = get_op();                        //产生op_type
         iA = get_data();                          //产生操作数A
         iB = get_data();                          //产生操作数B
         bfm.send_op(iA, iB, op_set, result);      //将产生的信号送往BFM       
      end : random_loop
      #500;
      phase.drop_objection(this);
   endtask : run_phase
   
   function new (string name, uvm_component parent);
      super.new(name, parent);
   endfunction : new

endclass : base_tester
  • 现在我们将tester分为tester和driver两个部分:

18.2 修改后的base_tester

  • 不再使用bfm,但是需要在tester中定义put port
  • 现在tester中只负责激励产生,并将数据送至put port端口,其他一概不负责;
  • base_tester和driver之间的port传输,不影响get_op和get_data,因此random_tester和add_tester不用更改
virtual class base_tester extends uvm_component;
`uvm_component_utils(base_tester)
   virtual tinyalu_bfm bfm;                     //这里不声明也可以,尊重原书保留
   
   uvm_put_port #(command_s) command_port;      //定义put port,指定传输的数据类型

   function void build_phase(uvm_phase phase);
      command_port = new("command_port", this); //build phase不使用bfm,所以只需要例化put ports
   endfunction : build_phase

   pure virtual function operation_t get_op();  //纯虚函数,在其子类中重载

   pure virtual function byte get_data();       //纯虚函数不改变,所以子类不用更改代码

   task run_phase(uvm_phase phase);             //run phase不再负责数据送至BFM的工作
      byte         unsigned        iA;
      byte         unsigned        iB;
      operation_t                  op_set;
      command_s    command;
      
      phase.raise_objection(this);
      command.op = rst_op;
      command_port.put(command);                //将reset opcode送至put port,blocking port
      repeat (1000) begin : random_loop
         command.op = get_op();
         command.A =  get_data();
         command.B =  get_data();
         command_port.put(command);             //将激励送至put port,数据进入TLM FIFO,再也不管这些数据了,这是其他人的工作
      end : random_loop                         //在比较复杂的系统中,tester只需要关注激励产生,不在关注信号级的数据传输工作
      #500;
      phase.drop_objection(this);
   endtask : run_phase                           

   function new (string name, uvm_component parent);
      super.new(name, parent);
   endfunction : new

endclass : base_tester

18.3 新增的driver

  • 新增的driver负责将tester产生的激励送至BFM,因此需要再次例化bfm;
  • 新增的dirver需要从TLM FIFO中取数据,需要例化uvm_get_port;
  • driver不负责生产激励,只是激励的搬运工;
  • driver模块代码如下:
class driver extends uvm_component;
   `uvm_component_utils(driver)

   virtual tinyalu_bfm bfm;                        //定义bfm

   uvm_get_port #(command_s) command_port;         //定义get port
   
   function void build_phase(uvm_phase phase);     //在build phase例化get port和bfm
      if(!uvm_config_db #(virtual tinyalu_bfm)::get(null, "*","bfm", bfm))
        $fatal("Failed to get BFM");               //通过configdb机制获得传入的bfm
      command_port = new("command_port",this);     //例化get port
   endfunction : build_phase

   task run_phase(uvm_phase phase);                //run phase中不进行数据处理,只能搬运数据
      command_s    command;
      shortint     result;
      forever begin : command_loop                 //forever循环,不断尝试从FIFO获取数据,送至bfm
         command_port.get(command);                //通过get port的get函数从TLM FIFO获取数据
         bfm.send_op(command.A, command.B, command.op, result);  //通过bfm的send_op函数将数据送至BFM
      end : command_loop
   endtask : run_phase

   function new (string name, uvm_component parent);
      super.new(name, parent);
   endfunction : new

endclass : driver

18.4 修改后的env

  • 既然测试环境增加了组件和FIFO,那么环境的structure必定需要重新连接,参考测试平台的框图。
  • 现在测试平台的env变得复杂了些,梳理一下:
    • 基本组件:random_tester, coverage, scoreboard
    • 和analysis port相关的组件:command_monitor, result_monitor
    • 和get port相关的组件:driver
    • 连接tester和driver的TLM FIFO
    • 注意:analysis fifo位于scoreboard内部,所以env是看不到的
  • uvm_env模块的代码如下:
class env extends uvm_env;
   `uvm_component_utils(env);

   random_tester     random_tester_h;           //环境组件定义
   driver     driver_h;
   uvm_tlm_fifo #(command_s) command_f;
   
   coverage   coverage_h;
   scoreboard scoreboard_h;
   command_monitor command_monitor_h;
   result_monitor  result_monitor_h;


   function void build_phase(uvm_phase phase);  //在build phase例化各个组件,factory机制
      command_f         = new("command_f", this);
      random_tester_h   = random_tester::type_id::create("random_tester_h",this);
      driver_h          = driver::type_id::create("drive_h",this);
    
      coverage_h        = coverage::type_id::create ("coverage_h",this);
      scoreboard_h      = scoreboard::type_id::create("scoreboard_h",this);
      command_monitor_h = command_monitor::type_id::create("command_monitor_h",this);
      result_monitor_h  = result_monitor::type_id::create("result_monitor_h",this);
   endfunction : build_phase

   function void connect_phase(uvm_phase phase);
      driver_h.command_port.connect(command_f.get_export);         //driver的get port <-> get export in TLM FIFO
      random_tester_h.command_port.connect(command_f.put_export);  //tester的put port <-> put export in TLM FIFO

      
      result_monitor_h.ap.connect(scoreboard_h.analysis_export);   //result monitor的analysis port <-> analysis export in scoreboard;
      
      command_monitor_h.ap.connect(scoreboard_h.cmd_f.analysis_export); //command monitor的analysis port <-> analysis export in uvm_tlm_analysis_fifo in scoreboard
      command_monitor_h.ap.connect(coverage_h.analysis_export);    //commad monitor的analysis port <-> analysis export in coverage 

   endfunction : connect_phase
   //记住所有连接比较难,只需要记住port总是连接在同类型的export上,export总是作为同类型的port的connect函数的参数。
   
   function new (string name, uvm_component parent);
      super.new(name,parent);
   endfunction : new

endclass
  • 本章中我们通过put/get port将tester分离为taster和driver两个部分,各部分实现单一的功能。现在测试平台被分解为:仿真&分析 两个层次,仿真又分为:激励产生(tester) & 激励传输(driver).
  • 现在我们可以更方便的驱动成千上万的激励到DUT,并通过analysis layer对DUT的行为进行分析。但是测试平台需要将DUT运行时的信息告诉我们。当然,我们可以使用SV的 d i s p l a y , display, display,error,$fatal等函数,但是当系统很复杂的时候,将会有海量信息需要我们处理,这是我们需要信息分级,SV内含的函数是帮不了我们的。
  • 因此UVM提供了reporting tools帮助我们解决这个问题,下一章我们学习UVM Reports。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值