UVM实例之APB总线

1.定义transaction

`ifndef APB_RW_SV
`define APB_RW_SV

import uvm_pkg::*;

//apb_rw sequence item derived from base uvm_sequence_item
class apb_rw extends uvm_sequence_item;
 
   //typedef for READ/Write transaction type
   typedef enum {READ, WRITE} kind_e;
   rand bit   [31:0] addr;      //Address
   rand logic [31:0] data;     //Data - For write or read response
   rand kind_e  apb_cmd;       //command type

    //Register with factory for dynamic creation
   `uvm_object_utils(apb_rw)
  
   
   function new (string name = "apb_rw");
      super.new(name);
   endfunction

   function string convert2string();
     return $psprintf("kind=%s addr=%0h data=%0h ",apb_cmd,addr,data);
   endfunction

endclass: apb_rw

`endif

2.定义sequence

`ifndef APB_SEQUENCES_SV
`define APB_SEQUENCES_SV

//------------------------
//Base APB sequence derived from uvm_sequence and parameterized with sequence item of type apb_rw
//------------------------
class apb_base_seq extends uvm_sequence#(apb_rw);

  `uvm_object_utils(apb_base_seq)

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


  //Main Body method that gets executed once sequence is started
  task body();//在body中启动item
     apb_rw rw_trans;
     //Create 10 random APB read/write transaction and send to driver
     repeat(10) begin
       rw_trans = apb_rw::type_id::create(.name("rw_trans"),.contxt(get_full_name()));
       start_item(rw_trans);
       assert (rw_trans.randomize());
       finish_item(rw_trans);
     end
  endtask
  
endclass



`endif

3.定义sequencer

class apb_sequencer extends uvm_sequencer #(apb_rw);

   `uvm_component_utils(apb_sequencer)
 
   function new(input string name, uvm_component parent=null);
      super.new(name, parent);
   endfunction : new

endclass : apb_sequencer

4.定义接口

`ifndef APB_IF_SV
`define APB_IF_SV

interface apb_if(input bit pclk);
   wire [31:0] paddr;
   wire        psel;
   wire        penable;
   wire        pwrite;
   wire [31:0] prdata;
   wire [31:0] pwdata;


   //Master Clocking block - used for Drivers
   clocking master_cb @(posedge pclk);//master接口
      output paddr, psel, penable, pwrite, pwdata;
      input  prdata;
   endclocking: master_cb

   //Slave Clocking Block - used for any Slave BFMs
   clocking slave_cb @(posedge pclk);//slave接口
      input  paddr, psel, penable, pwrite, pwdata;
      output prdata;
   endclocking: slave_cb

   //Monitor Clocking block - For sampling by monitor components
   clocking monitor_cb @(posedge pclk);
      input paddr, psel, penable, pwrite, prdata, pwdata;
   endclocking: monitor_cb

  modport master(clocking master_cb);
  modport slave(clocking slave_cb);
  modport passive(clocking monitor_cb);

endinterface: apb_if

`endif

5.定义driver

 

`ifndef APB_DRV_SEQ_MON_SV
`define APB_DRV_SEQ_MON_SV

typedef apb_config;
typedef apb_agent;

//---------------------------------------------
// APB master driver Class  
//---------------------------------------------
class apb_master_drv extends uvm_driver#(apb_rw);
  
  `uvm_component_utils(apb_master_drv)
   
   virtual apb_if vif;
   apb_config cfg;

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

   //Build Phase
   //Get the virtual interface handle form the agent (parent ) or from config_db
   function void build_phase(uvm_phase phase);
     apb_agent agent;
     super.build_phase(phase);
     if ($cast(agent, get_parent()) && agent != null) begin
         vif = agent.vif;
     end
     else begin
         if (!uvm_config_db#(virtual apb_if)::get(this, "", "vif", vif)) begin
            `uvm_fatal("APB/DRV/NOVIF", "No virtual interface specified for this driver instance")
         end
     end
   endfunction

   //Run Phase
   //Implement the Driver -Sequencer API to get an item
   //Based on if it is Read/Write - drive on APB interface the corresponding pins
   virtual task run_phase(uvm_phase phase);
     super.run_phase(phase);
     this.vif.master_cb.psel    <= '0;
     this.vif.master_cb.penable <= '0;

     forever begin
       apb_rw tr;
       @ (this.vif.master_cb);
       //First get an item from sequencer
       seq_item_port.get_next_item(tr);//获取下一个item
       @ (this.vif.master_cb);
       uvm_report_info("APB_DRIVER ", $psprintf("Got Transaction %s",tr.convert2string()));
       //Decode the APB Command and call either the read/write function
       case (tr.apb_cmd)
         apb_rw::READ:  drive_read(tr.addr, tr.data);  
         apb_rw::WRITE: drive_write(tr.addr, tr.data);
       endcase
       //Handshake DONE back to sequencer
       seq_item_port.item_done();
     end
   endtask: run_phase

   virtual protected task drive_read(input  bit   [31:0] addr,
                                     output logic [31:0] data);
     this.vif.master_cb.paddr   <= addr;
     this.vif.master_cb.pwrite  <= '0;
     this.vif.master_cb.psel    <= '1;
     @ (this.vif.master_cb);
     this.vif.master_cb.penable <= '1;
     @ (this.vif.master_cb);
     data = this.vif.master_cb.prdata;
     this.vif.master_cb.psel    <= '0;
     this.vif.master_cb.penable <= '0;
   endtask: drive_read

   virtual protected task drive_write(input bit [31:0] addr,
                                input bit [31:0] data);
      this.vif.master_cb.paddr   <= addr;
      this.vif.master_cb.pwdata  <= data;
      this.vif.master_cb.pwrite  <= '1;
      this.vif.master_cb.psel    <= '1;
      @ (this.vif.master_cb);
      this.vif.master_cb.penable <= '1;
      @ (this.vif.master_cb);
      this.vif.master_cb.psel    <= '0;
      this.vif.master_cb.penable <= '0;
   endtask: drive_write

endclass: apb_master_drv

 6.定义monitor

class apb_monitor extends uvm_monitor;

  virtual apb_if.passive vif;

  //Analysis port -parameterized to apb_rw transaction
  ///Monitor writes transaction objects to this port once detected on interface
  uvm_analysis_port#(apb_rw) ap;

  //config class handle
  apb_config cfg;

  `uvm_component_utils(apb_monitor)

   function new(string name, uvm_component parent = null);
     super.new(name, parent);
     ap = new("ap", this);
   endfunction: new

   //Build Phase - Get handle to virtual if from agent/config_db
   virtual function void build_phase(uvm_phase phase);
     apb_agent agent;
     if ($cast(agent, get_parent()) && agent != null) begin
       vif = agent.vif;
     end
     else begin
       virtual apb_if tmp;
       if (!uvm_config_db#(virtual apb_if)::get(this, "", "apb_if", tmp)) begin
         `uvm_fatal("APB/MON/NOVIF", "No virtual interface specified for this monitor instance")
       end
       vif = tmp;
     end
   endfunction

   virtual task run_phase(uvm_phase phase);
     super.run_phase(phase);
     forever begin
       apb_rw tr;
       // Wait for a SETUP cycle
       do begin
         @ (this.vif.monitor_cb);
         end
         while (this.vif.monitor_cb.psel !== 1'b1 ||
                this.vif.monitor_cb.penable !== 1'b0);
         //create a transaction object
         tr = apb_rw::type_id::create("tr", this);
        
         //populate fields based on values seen on interface
         tr.apb_cmd = (this.vif.monitor_cb.pwrite) ? apb_rw::WRITE : apb_rw::READ;
         tr.addr = this.vif.monitor_cb.paddr;

         @ (this.vif.monitor_cb);
         if (this.vif.monitor_cb.penable !== 1'b1) begin
            `uvm_error("APB", "APB protocol violation: SETUP cycle not followed by ENABLE cycle");
         end
         tr.data = (tr.apb_cmd == apb_rw::READ) ? this.vif.monitor_cb.prdata : '0;
         tr.data = (tr.apb_cmd == apb_rw::WRITE) ? this.vif.monitor_cb.pwdata : '0;
         uvm_report_info("APB_MONITOR", $psprintf("Got Transaction %s",tr.convert2string()));
         //Write to analysis port
         ap.write(tr);
      end
   endtask: run_phase

endclass: apb_monitor

`endif

 7.定义agent

class apb_agent extends uvm_agent;

   //Agent will have the sequencer, driver and monitor components for the APB interface
   apb_sequencer sqr;
   apb_master_drv drv;
   apb_monitor mon;

   virtual apb_if  vif;

   `uvm_component_utils_begin(apb_agent)
      `uvm_field_object(sqr, UVM_ALL_ON)
      `uvm_field_object(drv, UVM_ALL_ON)
      `uvm_field_object(mon, UVM_ALL_ON)
   `uvm_component_utils_end
   
   function new(string name, uvm_component parent = null);
      super.new(name, parent);
   endfunction

   //Build phase of agent - construct sequencer, driver and monitor
   //get handle to virtual interface from env (parent) config_db
   //and pass handle down to srq/driver/monitor
   virtual function void build_phase(uvm_phase phase);
      sqr = apb_sequencer::type_id::create("sqr", this);
      drv = apb_master_drv::type_id::create("drv", this);
      mon = apb_monitor::type_id::create("mon", this);
      
      if (!uvm_config_db#(virtual apb_if)::get(this, "", "vif", vif)) begin
         `uvm_fatal("APB/AGT/NOVIF", "No virtual interface specified for this agent instance")
      end
     uvm_config_db#(virtual apb_if)::set( this, "sqr", "vif", vif);
     uvm_config_db#(virtual apb_if)::set( this, "drv", "vif", vif);
     uvm_config_db#(virtual apb_if)::set( this, "mon", "vif", vif);
   endfunction: build_phase

   //Connect - driver and sequencer port to export
   virtual function void connect_phase(uvm_phase phase);
      drv.seq_item_port.connect(sqr.seq_item_export);
      uvm_report_info("apb_agent::", "connect_phase, Connected driver to sequencer");
   endfunction
endclass: apb_agent

8.定义env

class apb_env  extends uvm_env;
 
   `uvm_component_utils(apb_env);

   //ENV class will have agent as its sub component
   apb_agent  agt;
   //virtual interface for APB interface
   virtual apb_if  vif;

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

   //Build phase - Construct agent and get virtual interface handle from test  and pass it down to agent
   function void build_phase(uvm_phase phase);
     agt = apb_agent::type_id::create("agt", this);
     if (!uvm_config_db#(virtual apb_if)::get(this, "", "vif", vif)) begin
         `uvm_fatal("APB/AGT/NOVIF", "No virtual interface specified for this env instance")
     end
     uvm_config_db#(virtual apb_if)::set( this, "agt", "vif", vif);
   endfunction: build_phase
  
endclass : apb_env  

9.定义test

class apb_base_test extends uvm_test;

  //Register with factory
  `uvm_component_utils(apb_base_test);
  
  apb_env  env;
  apb_config cfg;
  virtual apb_if vif;
  
  function new(string name = "apb_base_test", uvm_component parent = null);
    super.new(name, parent);
  endfunction

  //Build phase - Construct the cfg and env class using factory
  //Get the virtual interface handle from Test and then set it config db for the env component
  function void build_phase(uvm_phase phase);
    cfg = apb_config::type_id::create("cfg", this);
    env = apb_env::type_id::create("env", this);
    //
    if (!uvm_config_db#(virtual apb_if)::get(this, "", "vif", vif)) begin
       `uvm_fatal("APB/DRV/NOVIF", "No virtual interface specified for this test instance")
    end 
    uvm_config_db#(virtual apb_if)::set( this, "env", "vif", vif);
  endfunction

  //Run phase - Create an abp_sequence and start it on the apb_sequencer
  task run_phase( uvm_phase phase );
    apb_base_seq apb_seq;
    apb_seq = apb_base_seq::type_id::create("apb_seq");
    phase.raise_objection( this, "Starting apb_base_seqin main phase" );
    $display("%t Starting sequence apb_seq run_phase",$time);
    apb_seq.start(env.agt.sqr);
    #100ns;
    phase.drop_objection( this , "Finished apb_seq in main phase" );
  endtask: run_phase
  
  
endclass


`endif

10.顶层模块

import uvm_pkg::*;
`include "uvm_macros.svh"

 //Include all files
`include "apb_if.svh"
`include "apb_rw.svh"
`include "apb_driver_seq_mon.svh"
`include "apb_agent_env_config.svh"
`include "apb_sequences.svh"
`include "apb_test.svh"

//--------------------------------------------------------
//Top level module that instantiates  just a physical apb interface
//No real DUT or APB slave as of now
//--------------------------------------------------------
module test;

   logic pclk;
  
   logic [31:0] paddr;
   logic        psel;
   logic        penable;
   logic        pwrite;
   logic [31:0] prdata;
   logic [31:0] pwdata;

   initial begin
      pclk=0;
   end

    //Generate a clock
   always begin
      #10 pclk = ~pclk;
   end
 
   //Instantiate a physical interface for APB interface
  apb_if  apb_if(.pclk(pclk));
  
  initial begin
    //Pass this physical interface to test top (which will further pass it down to env->agent->drv/sqr/mon
    uvm_config_db#(virtual apb_if)::set( null, "uvm_test_top", "vif", apb_if);
    //Call the test - but passing run_test argument as test class name
    //Another option is to not pass any test argument and use +UVM_TEST on command line to sepecify which test to run
    run_test("apb_base_test");
  end
  
  
endmodule

 

  • 11
    点赞
  • 92
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值