Ready-to-run snippets

Code Examples

Ready-to-run Verilog, SystemVerilog, and UVM examples. Click any to open it live in the IDE.

← All examples

Testbench Top

UVM

A reusable UVM APB master agent - config, packet, driver, monitor, sequencer - plus a base test, sequence and scoreboard driving the APB interface.

Open in IDE
Related tutorial:
testbench.sv
`include "uvm_macros.svh"
import uvm_pkg::*;

class apb_cfg extends uvm_object;
  `uvm_object_utils(apb_cfg)
  function new(string name = "apb_cfg");
    super.new(name);
  endfunction
  
  virtual apb_if 	m_apb_vif;
  bit 				m_vld_val_only;
endclass

class apb_pkt extends uvm_sequence_item;  
  function new(string name = "apb_pkt");
    super.new(name);
  endfunction
  
  rand bit [15:0] 	m_addr;
  rand bit [31:0] 	m_wdata;
       bit [31:0] 	m_rdata;
  rand bit 			m_write;
  
  `uvm_object_utils_begin(apb_pkt)
  	`uvm_field_int (m_addr, UVM_DEFAULT)
  	`uvm_field_int (m_wdata, UVM_DEFAULT)
  	`uvm_field_int (m_rdata, UVM_DEFAULT)
  	`uvm_field_int (m_write, UVM_DEFAULT)
  `uvm_object_utils_end
  
  virtual function string convert2string();
    return $sformatf("addr=0x%0h write=0x%0h wdata=0x%0h rdata=0x%0h",
                     m_addr, m_write, m_wdata, m_rdata);
  endfunction
  
endclass

class apb_driver extends uvm_driver #(apb_pkt);
  `uvm_component_utils(apb_driver)
  function new(string name="apb_driver", uvm_component parent=null);
    super.new(name, parent);
  endfunction
  
  virtual apb_if 	m_vif;
  apb_cfg 			m_cfg;
  
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
	m_vif = m_cfg.m_apb_vif;
  endfunction
  
  virtual task reset_phase(uvm_phase phase);
    super.reset_phase(phase);
    phase.raise_objection(this);
    m_vif.cb.paddr 		<= 0;
    m_vif.cb.psel 		<= 0;
    m_vif.cb.pwdata 	<= 0;
    m_vif.cb.penable 	<= 0;
    m_vif.cb.pwrite 	<= 0;
    phase.drop_objection(this);
  endtask
  
  virtual task main_phase(uvm_phase phase);
    super.main_phase(phase);
    
    forever begin
      seq_item_port.get_next_item(req);
      drive_item(req);
      seq_item_port.item_done();
    end
  endtask
  
  virtual task drive_item(REQ req);
    m_vif.cb.paddr 		<= req.m_addr;
    m_vif.cb.psel 		<= 1;
    m_vif.cb.pwrite 	<= req.m_write;
    m_vif.cb.pwdata 	<= req.m_wdata;
    m_vif.cb.penable 	<= 0;
    
    // At the next posedge, raise penable high
    @(m_vif.cb);
    m_vif.cb.penable 	<= 1;
    
    // Hold addr/data until pready is 1
    do begin
      @(m_vif.cb);
    end while (! m_vif.cb.pready);
    
    if (! req.m_write)
      req.m_rdata = m_vif.cb.prdata;
  endtask
  
endclass

class apb_monitor extends uvm_monitor;
  `uvm_component_utils(apb_monitor)
  function new(string name="apb_monitor", uvm_component parent=null);
    super.new(name, parent);
  endfunction
  
  apb_cfg 						m_cfg;
  virtual apb_if 				m_vif;
  uvm_analysis_port #(apb_pkt) 	m_analysis_port;
  
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    m_analysis_port = new("m_analysis_port", this);
    m_vif = m_cfg.m_apb_vif;
  endfunction

  virtual task main_phase(uvm_phase phase);
    super.main_phase(phase);
    forever begin
      @(m_vif.cb);
      if (m_vif.psel & m_vif.penable & m_vif.cb.pready & m_vif.presetn) begin
        apb_pkt m_apb_pkt = apb_pkt::type_id::create("m_apb_pkt");
        m_apb_pkt.m_addr = m_vif.paddr;
        m_apb_pkt.m_write = m_vif.pwrite;
        
        if (m_cfg.m_vld_val_only) begin
          if (m_apb_pkt.m_write)
            m_apb_pkt.m_wdata = m_vif.pwdata;
          else
        	m_apb_pkt.m_rdata = m_vif.cb.prdata;
        end else begin
          m_apb_pkt.m_wdata = m_vif.pwdata;
          m_apb_pkt.m_rdata = m_vif.cb.prdata;
        end
        
        m_analysis_port.write(m_apb_pkt);
      end
    end
  endtask
  
endclass

class apb_agent extends uvm_agent;
  `uvm_component_utils(apb_agent)
  function new(string name="apb_agent", uvm_component parent=null);
    super.new(name, parent);
  endfunction
  
  apb_cfg 					m_cfg;
  apb_driver 	 			m_drv;
  apb_monitor 	 			m_mon;
  uvm_sequencer #(apb_pkt) 	m_seqr;
  
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    
    if (!uvm_config_db#(apb_cfg)::get(this, "*", "m_apb_cfg", m_cfg))
      `uvm_fatal(get_type_name(), $sformatf("[CFG] Did not find m_apb_cfg in config_db"))
    
    if (get_is_active()) begin
      m_drv = apb_driver::type_id::create("m_drv", this);
      m_seqr = uvm_sequencer#(apb_pkt)::type_id::create("m_seqr", this);
    end
    
    m_mon = apb_monitor::type_id::create("m_mon", this);
    
    // Configuration might be needed to build it
    m_drv.m_cfg = m_cfg;
    m_mon.m_cfg = m_cfg;
  endfunction
  
  virtual function void connect_phase(uvm_phase phase);
    super.connect_phase(phase);
    
    if (get_is_active) begin
      m_drv.seq_item_port.connect(m_seqr.seq_item_export);
    end
  endfunction
endclass

class apb_seq extends uvm_sequence;
  `uvm_object_utils(apb_seq)
  function new(string name = "apb_seq");
    super.new(name);
  endfunction
  
  virtual task body();    
    `uvm_info(get_type_name(), $sformatf("Start sequence"), UVM_MEDIUM)
    repeat (10) begin
      	apb_pkt m_apb_pkt = apb_pkt::type_id::create("m_apb_pkt");
    	start_item(m_apb_pkt);
      	m_apb_pkt.randomize();
      	finish_item(m_apb_pkt);
    end
    `uvm_info(get_type_name(), $sformatf("End sequence"), UVM_MEDIUM)
  endtask  
endclass

class base_scoreboard extends uvm_scoreboard;
  `uvm_component_utils(base_scoreboard)
  function new(string name = "base_scoreboard", uvm_component parent=null);
    super.new(name, parent);
  endfunction
  
  uvm_analysis_imp #(apb_pkt, base_scoreboard) m_analysis_imp;
  apb_pkt 	m_apb_pkt_q[$];
  
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    m_analysis_imp = new("m_analysis_imp", this);
  endfunction
  
  virtual function write(apb_pkt l_apb_pkt);
    `uvm_info(get_type_name(), $sformatf("[write] Got packet %s", l_apb_pkt.convert2string()), UVM_MEDIUM)
    m_apb_pkt_q.push_back(l_apb_pkt);
  endfunction
endclass

class base_env extends uvm_env;
  `uvm_component_utils(base_env)
  function new(string name = "base_env", uvm_component parent=null);
    super.new(name, parent);
  endfunction
  
  apb_agent   		m_apb_agent;
  base_scoreboard 	m_base_scbd;
  
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    m_apb_agent = apb_agent::type_id::create("m_apb_agent", this);
    m_base_scbd = base_scoreboard::type_id::create("m_base_scbd", this);
  endfunction
  
  virtual function void connect_phase(uvm_phase phase);
    super.connect_phase(phase);
    m_apb_agent.m_mon.m_analysis_port.connect(m_base_scbd.m_analysis_imp);
  endfunction
  
endclass

class base_test extends uvm_test;
  `uvm_component_utils(base_test)
  function new(string name = "base_test", uvm_component parent=null);
    super.new(name, parent);
  endfunction
  
    virtual apb_if	m_apb_vif;
  
  	apb_cfg 	 	m_apb_cfg;
  	apb_seq 		m_apb_seq;
	base_env 		m_env;
  
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
   
    // Get virtual interface from tb_top
    if (!uvm_config_db #(virtual apb_if)::get(this, "", "m_apb_if", m_apb_vif))
        `uvm_fatal(get_type_name(), $sformatf("[CFG] Did not find m_apb_if in config_db"))
    
    // Set up APB agent configuration, assign virtual interface handle and set in config_db
    m_apb_cfg = apb_cfg::type_id::create("m_apb_cfg");
    m_apb_cfg.m_apb_vif = m_apb_vif;        
    uvm_config_db #(apb_cfg)::set(this, "*apb_agent*", "m_apb_cfg", m_apb_cfg);
        
    // Create base env, test sequence
    m_apb_seq = apb_seq::type_id::create("m_apb_seq");
    m_env = base_env::type_id::create("m_env", this);
  endfunction
  
  virtual task reset_phase(uvm_phase phase);
    super.reset_phase(phase);
    phase.raise_objection(this);
	m_apb_vif.presetn = 0;    
    repeat(5) @ (m_apb_vif.cb);
	m_apb_vif.presetn = 1;
    repeat(5) @ (m_apb_vif.cb);
    phase.drop_objection(this);
  endtask
  
  virtual task main_phase(uvm_phase phase);
    super.main_phase(phase);
    phase.raise_objection(this);
    m_apb_seq.start(m_env.m_apb_agent.m_seqr);
    phase.drop_objection(this);
  endtask
endclass

interface apb_if (input pclk);
  logic 		presetn;
  logic [15:0] 	paddr;
  logic 		psel;
  logic 		penable;
  logic 		pwrite;
  logic [31:0] 	pwdata;
  logic 		pready;
  logic [31:0] 	prdata;
  logic 		pslverr;
  
  clocking cb @ (posedge pclk);
    default input #1step output #1ns;
    input pready, prdata, pslverr;
    output paddr, psel, penable, pwrite, pwdata;
  endclocking
endinterface

module tb;
  bit clk;
  
  always #10 clk = ~clk;
  
  apb_if 	m_apb_if ( .pclk (clk));
  
  assign m_apb_if.pslverr = 0;
  
  initial begin
    m_apb_if.pready <= 0;
    forever begin
      repeat ($urandom_range(2,6)) @ (posedge clk);
      m_apb_if.pready <= ~m_apb_if.pready;
    end
  end
  
  initial begin
    m_apb_if.prdata <= 0;
    forever begin
      repeat ($urandom_range(2,6)) @ (posedge clk);
      m_apb_if.prdata <= $random;
    end
  end
    
  initial begin
    uvm_config_db #(virtual apb_if)::set(null, "uvm_test_top", "m_apb_if", m_apb_if);
    run_test("base_test");
  end
  
  initial begin
    $dumpvars(0, tb.m_apb_if);
    $dumpfile("dump.vcd");
  end
endmodule
design.sv
// Code your design here
Admin mode — edit and delete controls are visible on each row. Use the IDE to add new snippets via File → Save as Snippet.