白皮书《UVM实战》2.5.2代码详细注释(包括知识点对应页码)

1. my_driver.sv

`ifndef MY_DRIVER__SV
`define MY_DRIVER__SV
class my_driver extends uvm_driver#(my_transaction);

   virtual my_if vif;       //声明虚接口vif
   
   //使用uvm_component_utils宏注册到factory
   `uvm_component_utils(my_driver)
   
   //构造函数,uvm_component parent见my_env,见绿皮书P24
   function new(string name = "my_driver", uvm_component parent = null);
      super.new(name, parent);
   endfunction

   //build_phase为UVM内建的phase,在new函数之后main_phase之前执行(蓝皮书P18)
   //super.build_phase必须显式地调用执行它
   //build_phase不消耗仿真时间
   //uvm_config_db#(virtual my_if)是一个参数化的类,其参数就是要寄信的类型,这里是virtual my_if
   //config_db的set函数和get函数具体看(蓝皮书P19)
   //uvm_fatal打印打印东西的宏
   virtual function void build_phase(uvm_phase phase);  //接口对应
      super.build_phase(phase);   //P19父类的build_phase中执行了一些必要操作,这里必须显式地调用并执行它
      if(!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif))  //获取虚接口vif引用,P18,P87
         `uvm_fatal("my_driver", "virtual interface must be set for vif!!!")//如果获取失败,会报告错误
   endfunction

   extern task main_phase(uvm_phase phase);   //main_phase(蓝皮书P10)
   extern task drive_one_pkt(my_transaction tr);
endclass

task my_driver::main_phase(uvm_phase phase);   //my_driver类名,::作用域操作符
   vif.data <= 8'b0;
   vif.valid <= 1'b0;
   while(!vif.rst_n)
      @(posedge vif.clk);
   while(1) begin
      seq_item_port.get_next_item(req);//向sequencer申请新的req(transaction)(P46)
      drive_one_pkt(req);              //驱动req
      seq_item_port.item_done();       //驱动完成后调用item_done通知sequencer
   end
endtask

task my_driver::drive_one_pkt(my_transaction tr);
   byte unsigned     data_q[];
   int  data_size;
  
   //field_automation机制:调用pack_bytes将tr中所有的字段变成byte流放入data_q(绿皮书P40) 
   data_size = tr.pack_bytes(data_q) / 8; 
   `uvm_info("my_driver", "begin to drive one pkt", UVM_LOW);
   repeat(3) @(posedge vif.clk);
   for ( int i = 0; i < data_size; i++ ) begin
      @(posedge vif.clk);
      vif.valid <= 1'b1;
      vif.data <= data_q[i];   //驱动
   end

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


`endif

2. transaction.sv

`ifndef MY_TRANSACTION__SV
`define MY_TRANSACTION__SV

class my_transaction extends uvm_sequence_item;

   rand bit[47:0] dmac;         //48位以太网目的地址
   rand bit[47:0] smac;         //48位以太网源地址
   rand bit[15:0] ether_type;   //以太网类型
   rand byte      pload[];      //携带数据大小
   rand bit[31:0] crc;          //所有数据的校验值
   //定义了一个名为 pload_cons 的约束块,用于限制 pload 数组的大小在 46 到 1500 字节之间
   constraint pload_cons{
      pload.size >= 46;
      pload.size <= 1500;
   }

   function bit[31:0] calc_crc();//本应该是CRC的计算方法,但太复杂,所以改为0
      return 32'h0;     //32位16进制数0
   endfunction

   //post_randomize是SV中提供的一个函数,(绿皮书P21)
   function void post_randomize();  
      crc = calc_crc;
   endfunction

   //使用uvm_object_utils宏注册到factory,与my_driver(uvm_component_utils)不同
   //因为transaction派生自uvm_object的派生类uvm_sequence_item(蓝皮书P21)
   //使用uvm_object_utils_begin和uvm_object_utils_end注册后,可以直接
   //调用copy、compare等函数而无需自己定义,即field_automation机制(蓝皮书P39)
   `uvm_object_utils_begin(my_transaction)
      `uvm_field_int(dmac, UVM_ALL_ON)
      `uvm_field_int(smac, UVM_ALL_ON)
      `uvm_field_int(ether_type, UVM_ALL_ON)
      `uvm_field_array_int(pload, UVM_ALL_ON)
      `uvm_field_int(crc, UVM_ALL_ON)
   `uvm_object_utils_end
   
   //构造函数
   function new(string name = "my_transaction");
      super.new();
   endfunction

endclass
`endif

3. my_sequencer.sv

`ifndef MY_SEQUENCER__SV    // 检查是否定义MY_SEQUENCER__SV,如未定义执行下面语句
`define MY_SEQUENCER__SV    //定义MY_SEQUENCER__SV宏

class my_sequencer extends uvm_sequencer #(my_transaction);
   
   //如果你的基类构造函数有参数,那么扩展类必须有一个构造函数(new函数)
   //而且必须在构造函数的第一行调用基类的构造函数(绿皮书第三版P244)
   //下为构造函数目的是初始化my_sequencer,并调用基类uvm_sequencer
   function new(string name, uvm_component parent);   
      super.new(name, parent);
   endfunction 
   
   `uvm_component_utils(my_sequencer)    //通过uvm_component_utils宏注册到factory
endclass

`endif   //与开头的ifndef对应

4. my_agent.sv

`ifndef MY_AGENT__SV
`define MY_AGENT__SV


//把driver和monitor封装成agent后,在env中需要实例化agent,
//而不需要直接实例化driver和monitor
class my_agent extends uvm_agent ;
   my_sequencer  sqr;
   my_driver     drv;
   my_monitor    mon;
   
   //transaction级别的通信方式:uvm_blocking_get_port、uvm_analysis_port(P34)
   //uvm_analysis_port是一个参数化的类,参数是my_transaction
   uvm_analysis_port #(my_transaction)  ap;
   
   //构造函数
   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)
endclass 

//UVM_ACTIVE详解见P29
function void my_agent::build_phase(uvm_phase phase);
   super.build_phase(phase);
   if (is_active == UVM_ACTIVE) begin
      sqr = my_sequencer::type_id::create("sqr", this);  //实例化P24
      drv = my_driver::type_id::create("drv", this);
   end
   mon = my_monitor::type_id::create("mon", this);
endfunction 

function void my_agent::connect_phase(uvm_phase phase);
   super.connect_phase(phase);
   if (is_active == UVM_ACTIVE) begin
      drv.seq_item_port.connect(sqr.seq_item_export);  
      //drv的seq_item_port连接到sequencer的seq_item_exportP46
   end
   ap = mon.ap;
endfunction

`endif


5. my_monitor.sv

`ifndef MY_MONITOR__SV
`define MY_MONITOR__SV
class my_monitor extends uvm_monitor;

   virtual my_if vif;

   uvm_analysis_port #(my_transaction)  ap;    //端口
   
   `uvm_component_utils(my_monitor)
   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))
         `uvm_fatal("my_monitor", "virtual interface must be set for vif!!!")
      ap = new("ap", this);
   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
      tr = new("tr");
      collect_one_pkt(tr);   
      ap.write(tr);   //通过分析端口ap发出去
   end
endtask

task my_monitor::collect_one_pkt(my_transaction tr);
   byte unsigned data_q[$];
   byte unsigned data_array[];
   logic [7:0] data;
   logic valid = 0;
   int data_size;
   
   while(1) begin
      @(posedge vif.clk);
      if(vif.valid) break;    //等待有效信号vif.valid
   end
   
   `uvm_info("my_monitor", "begin to collect one pkt", UVM_LOW);
   while(vif.valid) begin
      data_q.push_back(vif.data);   //push_back用在数组末尾添加新元素
      @(posedge vif.clk);
   end
   data_size  = data_q.size();   
   data_array = new[data_size];     //创建了一个大小为 data_size 的动态数组 data_array
   for ( int i = 0; i < data_size; i++ ) begin
      data_array[i] = data_q[i];      //将队列转换为字节数组
   end
   tr.pload = new[data_size - 18]; //-18是为了剔除dmac,smac,ether_type,crc,只保留pload的空间
   data_size = tr.unpack_bytes(data_array) / 8; //(P41)将byte流转换为tr的各个字段
   `uvm_info("my_monitor", "end collect one pkt", UVM_LOW);
endtask


`endif

6. my_scoreboard.sv

`ifndef MY_SCOREBOARD__SV
`define MY_SCOREBOARD__SV
class my_scoreboard extends uvm_scoreboard;
   my_transaction  expect_queue[$];
   uvm_blocking_get_port #(my_transaction)  exp_port;
   uvm_blocking_get_port #(my_transaction)  act_port;
   `uvm_component_utils(my_scoreboard)

   extern function new(string name, uvm_component parent = null);
   extern virtual function void build_phase(uvm_phase phase);
   extern virtual task main_phase(uvm_phase phase);
endclass 

//构造函数
function my_scoreboard::new(string name, uvm_component parent = null);
   super.new(name, parent);
endfunction 

function void my_scoreboard::build_phase(uvm_phase phase);
   super.build_phase(phase);
   exp_port = new("exp_port", this);  // P104
   act_port = new("act_port", this);
endfunction 

task my_scoreboard::main_phase(uvm_phase phase);
   my_transaction  get_expect,  get_actual, tmp_tran;
   bit result;
 
   super.main_phase(phase);
   fork 
      while (1) begin
         exp_port.get(get_expect);    // 在my_env中将my_scoreboard的exp_port连接到my_model的ap
         expect_queue.push_back(get_expect);//把数据放入expect_queue
      end
      while (1) begin
         act_port.get(get_actual);
         if(expect_queue.size() > 0) begin
            tmp_tran = expect_queue.pop_front();//expect_queue弹出之前从exp_port获得的数据
            result = get_actual.compare(tmp_tran);//比较get_actual与tmp_tran是否一致
            if(result) begin 
               `uvm_info("my_scoreboard", "Compare SUCCESSFULLY", UVM_LOW);
            end
            else begin
               `uvm_error("my_scoreboard", "Compare FAILED");
               $display("the expect pkt is");
               tmp_tran.print();
               $display("the actual pkt is");
               get_actual.print();
            end
         end
         else begin
            `uvm_error("my_scoreboard", "Received from DUT, while Expect Queue is empty");
            $display("the unexpected pkt is");
            get_actual.print();
         end 
      end
   join
endtask
`endif

7. my_if.sv

`ifndef MY_IF__SV
`define MY_IF__SV

interface my_if(input clk, input rst_n);

   logic [7:0] data;
   logic valid;
endinterface

`endif

8. my_model.sv

`ifndef MY_MODEL__SV
`define MY_MODEL__SV//头文件保护宏

class my_model extends uvm_component;
   
   //transaction级别的通信方式:uvm_blocking_get_port、uvm_analysis_port(P34)
   //uvm_blocking_get_port是一个参数化的类,参数是my_transaction
   //uvm_analysis_port是一个参数化的类,参数是my_transaction
   uvm_blocking_get_port #(my_transaction)  port;  //从其他组件中获取事务数据(P35)
   uvm_analysis_port #(my_transaction)  ap;     //向其他组件发送事务数据

   extern function new(string name, uvm_component parent);
   extern function void build_phase(uvm_phase phase);
   extern virtual  task main_phase(uvm_phase phase);

   `uvm_component_utils(my_model) 
endclass 

//构造函数
function my_model::new(string name, uvm_component parent);
   super.new(name, parent);
endfunction 

//在 build 阶段初始化 port 和 ap 端口
function void my_model::build_phase(uvm_phase phase);
   super.build_phase(phase);
   port = new("port", this);
   ap = new("ap", this);
endfunction

task my_model::main_phase(uvm_phase phase);
   my_transaction tr;  //在my_monitor中实例化
   my_transaction new_tr;
   super.main_phase(phase);
   while(1) begin
      port.get(tr);//从 port 中获取 tr
      new_tr = new("new_tr");//
      new_tr.copy(tr);//将tr复制到new_tr
      `uvm_info("my_model", "get one transaction, copy and print it:", UVM_LOW)
      new_tr.print();
      ap.write(new_tr);//通过 ap 端口发送 new_tr
   end
endtask
`endif

9. my_env.sv

`ifndef MY_ENV__SV
`define MY_ENV__SV

class my_env extends uvm_env;

   my_agent   i_agt;
   my_agent   o_agt;
   my_model   mdl;
   my_scoreboard scb;
   
   uvm_tlm_analysis_fifo #(my_transaction) agt_scb_fifo;  //P124使用fifo通信
   uvm_tlm_analysis_fifo #(my_transaction) agt_mdl_fifo;
   uvm_tlm_analysis_fifo #(my_transaction) mdl_scb_fifo;
   
   //构造函数
   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);
      //把driver和monitor封装成agent后,在env中需要实例化agent,而不需要直接实例化driver和monitor
      i_agt = my_agent::type_id::create("i_agt", this);  //factory机制的独特实例化:type_name::type_id::create
      o_agt = my_agent::type_id::create("o_agt", this);  //my_agent实例化传递两个参数,o_agt是名称,
      i_agt.is_active = UVM_ACTIVE;                      //this是指针,指my_env即对应my_driver的parent(P24)
      o_agt.is_active = UVM_PASSIVE;
      mdl = my_model::type_id::create("mdl", this);
      scb = my_scoreboard::type_id::create("scb", this);
      agt_scb_fifo = new("agt_scb_fifo", this);
      agt_mdl_fifo = new("agt_mdl_fifo", this);
      mdl_scb_fifo = new("mdl_scb_fifo", this);

   endfunction

   extern virtual function void connect_phase(uvm_phase phase);
   
   //注册到factory
   `uvm_component_utils(my_env)
endclass

//connect_phase在build_phase之后执行,从树叶到树根(P35)
function void my_env::connect_phase(uvm_phase phase);
   super.connect_phase(phase);
   i_agt.ap.connect(agt_mdl_fifo.analysis_export);   // 加fifo详细原因P35
   mdl.port.connect(agt_mdl_fifo.blocking_get_export);
   mdl.ap.connect(mdl_scb_fifo.analysis_export);
   scb.exp_port.connect(mdl_scb_fifo.blocking_get_export);
   o_agt.ap.connect(agt_scb_fifo.analysis_export);
   scb.act_port.connect(agt_scb_fifo.blocking_get_export); 
endfunction

`endif

10. my_case1.sv

`ifndef MY_CASE1__SV
`define MY_CASE1__SV
class case1_sequence extends uvm_sequence #(my_transaction);
   my_transaction m_trans;
   
   //构造函数
   function  new(string name= "case1_sequence");
      super.new(name);
   endfunction 

   virtual task body();
      if(starting_phase != null)       //测试用例是否在某个测试阶段中运行(P49)
         starting_phase.raise_objection(this);//告诉 UVM 框架测试用例已经开始执行
      repeat (10) begin
         `uvm_do_with(m_trans, { m_trans.pload.size() == 60;})   //P45,P54
         //uvm_do_with 宏会根据给定的约束条件(m_trans.pload.size() == 60)来生成事务
         //实例化,随机化,将其送给sequencer         
      end
      #100;
      if(starting_phase != null) 
         starting_phase.drop_objection(this);  //告诉 UVM 框架测试用例已经执行完毕
   endtask

   //注册
   `uvm_object_utils(case1_sequence)
endclass

class my_case1 extends base_test;
   
   //构造函数
   function new(string name = "my_case1", uvm_component parent = null);
      super.new(name,parent);
   endfunction 
   
   extern virtual function void build_phase(uvm_phase phase); 
   `uvm_component_utils(my_case1)
endclass


function void my_case1::build_phase(uvm_phase phase);
   super.build_phase(phase);
   //蓝皮书P19,P48default_sequence的使用
   uvm_config_db#(uvm_object_wrapper)::set(this, 
                                           "env.i_agt.sqr.main_phase", 
                                           "default_sequence", 
                                           case1_sequence::type_id::get());
endfunction

`endif

11. my_case0.sv

//一种激励就是一个测试用例,不同的激励就是不同的测试用例
`ifndef MY_CASE0__SV
`define MY_CASE0__SV
class case0_sequence extends uvm_sequence #(my_transaction);
   my_transaction m_trans;
   
   //构造函数
   function  new(string name= "case0_sequence");
      super.new(name);
   endfunction 
   
   virtual task body();
      if(starting_phase != null) 
         starting_phase.raise_objection(this);
      repeat (10) begin
         `uvm_do(m_trans)
      end
      #100;
      if(starting_phase != null) 
         starting_phase.drop_objection(this);
   endtask

   `uvm_object_utils(case0_sequence)
endclass


class my_case0 extends base_test;
   
   //构造函数
   function new(string name = "my_case0", uvm_component parent = null);
      super.new(name,parent);
   endfunction 
   extern virtual function void build_phase(uvm_phase phase); 
   `uvm_component_utils(my_case0)
endclass


function void my_case0::build_phase(uvm_phase phase);
   super.build_phase(phase);

   uvm_config_db#(uvm_object_wrapper)::set(this, 
                                           "env.i_agt.sqr.main_phase", 
                                           "default_sequence", 
                                           case0_sequence::type_id::get());
endfunction

`endif

12. base_test.sv

`ifndef BASE_TEST__SV
`define BASE_TEST__SV

class base_test extends uvm_test;

   my_env         env;
   
   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 report_phase(uvm_phase phase);
   `uvm_component_utils(base_test)
endclass


function void base_test::build_phase(uvm_phase phase);
   super.build_phase(phase);
   env  =  my_env::type_id::create("env", this); 
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

`endif

13. top_tb.sv

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

import uvm_pkg::*;
`include "my_if.sv"
`include "my_transaction.sv"
`include "my_sequencer.sv"
`include "my_driver.sv"
`include "my_monitor.sv"
`include "my_agent.sv"
`include "my_model.sv"
`include "my_scoreboard.sv"
`include "my_env.sv"
`include "base_test.sv"
`include "my_case0.sv"
`include "my_case1.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();   //P14,P54run_test创建一个my_driver的实例,并且会自动调用my_driver的main_phase
end

//通过config_db机制将top_tb和my_driver对应
//将input_if和output_if传递给两个munitor(绿皮书P28)
initial begin
   uvm_config_db#(virtual my_if)::set(null, "uvm_test_top.env.i_agt.drv", "vif", input_if);
   uvm_config_db#(virtual my_if)::set(null, "uvm_test_top.env.i_agt.mon", "vif", input_if);
   uvm_config_db#(virtual my_if)::set(null, "uvm_test_top.env.o_agt.mon", "vif", output_if);
end

endmodule

你可能感兴趣的:(fpga开发)