(三)UVM验证平台搭建3

一、构建组件

在driver的基础上加入transaction、env、monitor,agent组件。

1、transaction

transaction指交易、事务,在协议中数据一般以包、帧形式传递而不是byte、bit的形式, transaction就表示一笔数据信息    

`ifndef MY_TRANSACTION__SV
`define MY_TRANSACTION__SV

class my_transaction extends uvm_sequence_item;//所有的transaction都要从uvm_sequence_item派生

   rand bit[47:0] dmac;//以太网目的地址
   rand bit[47:0] smac;//以太网源地址
   rand bit[15:0] ether_type;//以太网类型
   rand byte      pload[];//携带数据的大小
   rand bit[31:0] crc;
	//pload_cons约束
   constraint pload_cons{
      pload.size >= 46;
      pload.size <= 1500;
   }

   function bit[31:0] calc_crc();
      return 32'h0;
   endfunction

   function void post_randomize();//randomize函数被调用后,post_randomize会紧随其后无条件地调用
      crc = calc_crc;
   endfunction

   `uvm_object_utils(my_transaction)//生命周期不是一直存在的所以使用了uvm_object_utils宏来实现工厂机制

   function new(string name = "my_transaction");
      super.new();
   endfunction

   function void my_print();
      $display("dmac = %0h", dmac);
      $display("smac = %0h", smac);
      $display("ether_type = %0h", ether_type);
      for(int i = 0; i < pload.size; i++) begin
         $display("pload[%0d] = %0h", i, pload[i]);
      end
      $display("crc = %0h", crc);
   endfunction
endclass
`endif

说明:

  • 有生命周期的类都是派生自uvm_object或者uvm_object的派生类,uvm_sequence_item的祖先就是uvm_object。UVM中具有这种特征的类都要使用uvm_object_utils宏来实现
  • 只有从uvm_sequence_item派生的transaction才可以使用UVM的sequence机制

2、更新后的driver

`ifndef MY_DRIVER__SV
`define MY_DRIVER__SV
class my_driver extends uvm_driver;

   virtual my_if vif;

   `uvm_component_utils(my_driver)
   function new(string name = "my_driver", uvm_component parent = null);
      super.new(name, parent);
   endfunction

   virtual function void build_phase(uvm_phase phase);
      super.build_phase(phase);//完成一些uvm必要的操作
      if(!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif))
         `uvm_fatal("my_driver", "virtual interface must be set for vif!!!")
   endfunction

   extern task main_phase(uvm_phase phase);
   extern task drive_one_pkt(my_transaction tr);
endclass

task my_driver::main_phase(uvm_phase phase);
   my_transaction tr;
   phase.raise_objection(this);//提起objection
   vif.data <= 8'b0;
   vif.valid <= 1'b0;
   while(!vif.rst_n)
      @(posedge vif.clk);
   for(int i = 0; i < 2; i++) begin 
      tr = new("tr");
      assert(tr.randomize() with {pload.size == 200;});//随机化tr
      drive_one_pkt(tr);//打包驱动到DUT
   end
   repeat(5) @(posedge vif.clk);
   phase.drop_objection(this);//撤销objection
endtask

task my_driver::drive_one_pkt(my_transaction tr);
   bit [47:0] tmp_data;
   bit [7:0] data_q[$]; //打包成一个byte流的对列
  
   //push dmac to data_q
   tmp_data = tr.dmac;
   for(int i = 0; i < 6; i++) begin
      data_q.push_back(tmp_data[7:0]);//从对列后往前压入tmp_data
      tmp_data = (tmp_data >> 8);
   end
   //push smac to data_q
   tmp_data = tr.smac;
   for(int i = 0; i < 6; i++) begin
      data_q.push_back(tmp_data[7:0]);
      tmp_data = (tmp_data >> 8);
   end
   //push ether_type to data_q
   tmp_data = tr.ether_type;
   for(int i = 0; i < 2; i++) begin
      data_q.push_back(tmp_data[7:0]);
      tmp_data = (tmp_data >> 8);
   end
   //push payload to data_q
   for(int i = 0; i < tr.pload.size; i++) begin
      data_q.push_back(tr.pload[i]);
   end
   //push crc to data_q
   tmp_data = tr.crc;
   for(int i = 0; i < 4; i++) begin
      data_q.push_back(tmp_data[7:0]);
      tmp_data = (tmp_data >> 8);
   end

   `uvm_info("my_driver", "begin to drive one pkt", UVM_LOW);
   repeat(3) @(posedge vif.clk);

   while(data_q.size() > 0) begin
      @(posedge vif.clk);
      vif.valid <= 1'b1;
      vif.data <= data_q.pop_front(); //传递数据到dut
   end

   @(posedge vif.clk);
   vif.valid <= 1'b0;
   `uvm_info("my_driver", "end drive one pkt", UVM_LOW);
endtask


`endif

3、monitor

`ifndef MY_MONITOR__SV
`define MY_MONITOR__SV
class my_monitor extends uvm_monitor;//所有的monitor类应该派生自uvm_monitor

   virtual my_if vif;//定义虚接口

   `uvm_component_utils(my_monitor)//使用uvm_component_utils宏注册
   function new(string name = "my_monitor", uvm_component parent = null);
      super.new(name, parent);
   endfunction

   virtual function void build_phase(uvm_phase phase);
      super.build_phase(phase);
      if(!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif))//接收vif具体的内容
         `uvm_fatal("my_monitor", "virtual interface must be set for vif!!!")
   endfunction

   extern task main_phase(uvm_phase phase);
   extern task collect_one_pkt(my_transaction tr);
endclass

task my_monitor::main_phase(uvm_phase phase);
   my_transaction tr;
   while(1) begin    //while(1)表示monitor需要时刻收集数据
      tr = new("tr");
      collect_one_pkt(tr);//收集事务包tr
   end
endtask

task my_monitor::collect_one_pkt(my_transaction tr);
   bit[7:0] data_q[$]; 
   int psize;
   while(1) begin
      @(posedge vif.clk);
      if(vif.valid) break;//表示driver传递数据到dut时跳出循环,开始监测数据
   end

   `uvm_info("my_monitor", "begin to collect one pkt", UVM_LOW);
   while(vif.valid) begin
      data_q.push_back(vif.data);//收集dut输出的数据
      @(posedge vif.clk);
   end
   //pop dmac
   for(int i = 0; i < 6; i++) begin
      tr.dmac = {tr.dmac[39:0], data_q.pop_front()};
   end
   //pop smac
   for(int i = 0; i < 6; i++) begin
      tr.smac = {tr.smac[39:0], data_q.pop_front()};
   end
   //pop ether_type
   for(int i = 0; i < 2; i++) begin
      tr.ether_type = {tr.ether_type[7:0], data_q.pop_front()};
   end

   psize = data_q.size() - 4;
   tr.pload = new[psize];
   //pop payload
   for(int i = 0; i < psize; i++) begin
      tr.pload[i] = data_q.pop_front();
   end
   //pop crc
   for(int i = 0; i < 4; i++) begin
      tr.crc = {tr.crc[23:0], data_q.pop_front()};
   end
   `uvm_info("my_monitor", "end collect one pkt, print it:", UVM_LOW);
    tr.my_print();//打印收到的数据
endtask


`endif

4、agent

UVM中通常将driver、monitor二者封装在一起,成为一个agent,不同的agent就代表了不同的协议

`ifndef MY_AGENT__SV
`define MY_AGENT__SV

class my_agent extends uvm_agent ;//所有的agent都要派生自uvm_agent类
   my_driver     drv;
   my_monitor    mon;
   
   function new(string name, uvm_component parent);
      super.new(name, parent);
   endfunction 
   
   extern virtual function void build_phase(uvm_phase phase);
   extern virtual function void connect_phase(uvm_phase phase);

   `uvm_component_utils(my_agent)//agent本身是一个component,使用uvm_component_utils宏来注册factory
endclass 


function void my_agent::build_phase(uvm_phase phase);
   super.build_phase(phase);
   if (is_active == UVM_ACTIVE) begin//根据is_active这个变量的值来决定是否创建driver的实例
       drv = my_driver::type_id::create("drv", this);//前者drv只是一个句柄变量只是指向了该对象,后者drv才是真正的实例化对象名字
   end
   mon = my_monitor::type_id::create("mon", this);
endfunction 

function void my_agent::connect_phase(uvm_phase phase);
   super.connect_phase(phase);
endfunction

`endif

说明:

  • 在这儿由于my_driver在uvm_agent中实例化,所以my_driver的父结点(parent)就my_agent就是this。
  • is_active是uvm_agent的uvm_active_passive_enum定义的一个枚举类型的成员变量
  • 枚举变量仅有两个值:UVM_PASSIVE和UVM_ACTIVE。在uvm_agent中,is_active的值默认为UVM_ACTIVE
  • 在输出端口上不需要驱动任何信号,只需要监测信号,端口上是只需要monitor的,所以driver不用实例化

5、env

只使用run_test进行实例化显然是不行的,因为run_test函数虽然强大,但也只能实例化一个实例,要想例化driver、monitor、reference model和scoreboard等多个组件时定义一个env容器更方便,但在run_test传参时用env的参数,这样就只用run_test例化env

`ifndef MY_ENV__SV
`define MY_ENV__SV

class my_env extends uvm_env;//所有的env应该派生自uvm_env

   my_agent  i_agt;
   my_agent  o_agt;
   
   function new(string name = "my_env", uvm_component parent);
      super.new(name, parent);
   endfunction

   virtual function void build_phase(uvm_phase phase);
      super.build_phase(phase);
      i_agt = my_agent::type_id::create("i_agt", this);//创建输入的agent
      o_agt = my_agent::type_id::create("o_agt", this);//创建输出的agent
      i_agt.is_active = UVM_ACTIVE;
      o_agt.is_active = UVM_PASSIVE;//设置输出端口不实例化driver
   endfunction

   `uvm_component_utils(my_env)//容器类在仿真中也是一直存在的,使用uvm_component_utils宏来实现factory注册
endclass
`endif

说明:

  • type_name::type_id::create(name,parent):type_name表示实例化对象的类名字,两个参数name表示实例化对象的名字,parent表示该实例化对象的父结点
  • 在树形的组织结构中,由run_test创建的实例是树根(这里是my_env),并且树根的名字(实例化名字)是固定的,为uvm_test_top,从树根长出枝叶就是指其他组件的实例化对象
  • 无论是树根还是树叶,都必须由uvm_component或者其派生类继承而来,uvm_component的派生类会在uvm_component基础上增加一些功能,不同组件增加不同的功能
  • UVM的树形结构中,build_phase的执行遵照从树根到树叶的顺序,即先执行my_env的build_phase,再执行my_agent的build_phase,再执行my_driver的build_phase。当把整棵树的build_phase都执行完毕后,再执行后面的phase。
  • 不可以在build_phase外的phase实例化如main_phase等。
  • 还可以在new函数中执行实例化的动作比如driver和monitor,但会导致无法通过直接赋值的方式向uvm_agent传递is_active的值,要解决这个问题,可以在my_agent实例化之前使用config_db语句传递is_active的值,但UVM中约定俗成的在build_phase中完成实例化

6、top_tb

`timescale 1ns/1ps
`include "uvm_macros.svh"

import uvm_pkg::*;
`include "my_if.sv"
`include "my_transaction.sv"
`include "my_driver.sv"
`include "my_monitor.sv"
`include "my_agent.sv"
`include "my_env.sv"

module top_tb;

reg clk;
reg rst_n;
reg[7:0] rxd;
reg rx_dv;
wire[7:0] txd;
wire tx_en;

my_if input_if(clk, rst_n);
my_if output_if(clk, rst_n);

dut my_dut(.clk(clk),
           .rst_n(rst_n),
           .rxd(input_if.data),
           .rx_dv(input_if.valid),
           .txd(output_if.data),
           .tx_en(output_if.valid));

initial begin
   clk = 0;
   forever begin
      #100 clk = ~clk;
   end
end

initial begin
   rst_n = 1'b0;
   #1000;
   rst_n = 1'b1;
end

initial begin
   run_test("my_env");//传递参数表示了树根,要被实例化的类
end

initial begin
   uvm_config_db#(virtual my_if)::set(null, "uvm_test_top.i_agt.drv", "vif", input_if);//路径修改drv是在my_env的build_phase中实例化drv时传递过去的名字
   uvm_config_db#(virtual my_if)::set(null, "uvm_test_top.i_agt.mon", "vif", input_if);
   uvm_config_db#(virtual my_if)::set(null, "uvm_test_top.o_agt.mon", "vif", output_if);
end

endmodule
  • 由run_test自动创建的实例是树根(这里是my_env,但也并不是UVM真正的树根,真正树根是__top__暂时忽略这点),并且树根的名字(实例化名字)是固定的,为uvm_test_top。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值