Friday, October 14, 2011

Simple testbench done in System Verilog with OVM

The testbench described here (done in OVM)  has the same functionality or behavior  as a testbenches done without OVM, described in the blog post :  Simple testbench (done in both System Verilog and Verilog HDL) for a Digital design block (Verilog HDL) verification  

Simulate the source code bellow with Cadence tools (using OVM ) with the command:

irun +incdir+<a path to OVM directories>/ovm-2.1.2/src -access +r -gui -ovm +OVM_TESTNAME=test1 top.sv

        

Source code        

Testcase Source code

// the name of the test is test1
        
        
// Define a testcase test1 by extending ovm_test class.

class test1 extends ovm_test;

    `ovm_component_utils(test1)
        
        
////////////////////////////////////////////////////////////////////
// Take the instance of Environment: HINT: it has to be class "Environment"  // defined same where in the code
////////////////////////////////////////////////////////////////////////////

    Environment t_env ;

        
        
//////////////////////////////////////////////////////////////////////////////
// Define the constructor method:
// In this method, construct the environment class object and don't forget to // pass the parent argument(t_env)
//////////////////////////////////////////////////////////////////////////////

    function new (string name="test1", ovm_component parent=null);
       super.new (name, parent);
       t_env = new("t_env",this);
    endfunction : new
        
        
/////////////////////////////////////////////////////////////////////////////
// run() method is the only task which is time consuming.
// After completing the start_of_simulation() phase , this method is called.
/////////////////////////////////////////////////////////////////////////////
    task run ();
     
// the same simple stimulus from testbench of SV without OVM
// except this time we are directly driving interface: intf_tb signal: read
// interface intf_tb is defined in configuration: cfg.
// HINT: it has to be an defined somewhere else  interface: intf_tb and
// configuration: cfg

    cfg.intf_tb.address = 0 ;
cfg.intf_tb.data_in = 0 ;
cfg.intf_tb.read_write = 0 ;
cfg.intf_tb.chip_en = 0 ;

repeat(3) begin
#100
cfg.intf_tb.chip_en = 1 ;
cfg.intf_tb.address = $random ;
cfg.intf_tb.data_in = $random ;
cfg.intf_tb.read_write = 1 ;

#100
cfg.intf_tb.read_write = 0 ;
end
#100
cfg.intf_tb.chip_en = 0 ;
     
    #1000;
        
        
// To terminate this task, we will use global_stop_request()
    global_stop_request();
    endtask : run



endclass : test1

        
        

Environment.sv


`ifndef GUARD_ENV
`define GUARD_ENV

        
        
// Extend ovm_env class to define Environment class
// We will not implement all the ovm_env virtual methods in this phase but will we print messages from these methods

class Environment extends ovm_env;

    `ovm_component_utils(Environment)
        
        
// Define the constructor

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

        
        
/////////////////////////////////////////////
// Define build method: just print messages
// Build is the first phase in simulation
//////////////////////////////////////////////

    function void build();
       super.build();
      
       ovm_report_info(get_full_name(),"START of build ",OVM_LOW);
     
       ovm_report_info(get_full_name(),"END of build ",OVM_LOW);
     
    endfunction
   
        
        
//////////////////////////////////////////////
// Define connect method: just print messages
// This method is called automatically after the build() method is called
//////////////////////////////////////////////

    function void connect();
       super.connect();
       ovm_report_info(get_full_name(),"START of connect ",OVM_LOW);
   
       ovm_report_info(get_full_name(),"END of connect ",OVM_LOW);
    endfunction


endclass : Environment

`endif

        
        
Configuration.sv


`ifndef GUARD_CONFIGURATION
`define GUARD_CONFIGURATION


class Configuration extends ovm_object;
        
        
// Declare All the interfaces which are required in this verification
// environment        
// HINT: It has to be defined before interface intf with modport tb

     virtual intf.tb              intf_tb ;


                 
// construct a new object of configuration class and update all the important // fields and return it.

    virtual function ovm_object create(string name="");
       Configuration t = new();


         t.intf_tb     =   this.intf_tb ;

       return t;
    endfunction : create

endclass : Configuration

`endif

Interface ( intf.sv )
`ifndef GUARD_INTERFACE
`define GUARD_INTERFACE
interface intf ( input clk );
logic read_write, chip_en ;
logic[7:0] address, data_in ;
logic[7:0] data_out ;

modport tb (output read_write, chip_en, address, data_in, input data_out);
endinterface :intf
`endif


///////////////////////////////////////////////////////////////////////////////
// top module: integrating Dut and it’s Testbench
// and OVM configuration
///////////////////////////////////////////////////////////////////////////////
`ifndef GUARD_TOP
`define GUARD_TOP
`include "ovm.svh"

module top();
  
  `include "Configuration.sv"
  `include "Environment.sv"
  `include "test.sv"

    bit clk;
   
    initial
       begin
             #20;
             forever #10 clk = ~clk;
     end

    intf      bus_if(clk);  //interface instantiation
   



/////////////////////////////////////////////////////
// Creat Configuration and Strart the run_test//
/////////////////////////////////////////////////////


    Configuration cfg;

initial begin
    cfg = new();

     cfg.intf_tb = bus_if ;
  
    run_test();
end

    memory d (
.address ( bus_if.address ), // connect the verilog
.data_in ( bus_if.data_in ), // using interface hierarchy signal name.
.data_out ( bus_if.data_out ),
.read_write ( bus_if.read_write ),
.chip_en ( bus_if.chip_en )
);

endmodule  : top

`endif







Simple testbench (done in both System Verilog and Verilog HDL) for a Digital design block (Verilog HDL) verification

Lets assume that we have  to verify a basic memory design block done in behavior Verilog HDL (memory.v ) .

memory.v  is done here in Verilog HDL and NOT in System Verilog for legacy reasons:
  • it is certain that we can synthesise Verilog HDL RTL design and gate level design that is result of synthesis will also be in Verilog HDL.


/////////////////
/// DUT
///////////////
module memory(
address,
data_in,
data_out,
read_write,
chip_en
);

input [7:0] address, data_in;
output[7:0] data_out;
input read_write, chip_en;

reg [7:0] data_out ;
reg [7:0] mem [0:255];

always @ (address or data_in or read_write or chip_en)
if (read_write == 1 && chip_en == 1) begin
 mem[address] = data_in;
end

always @ (read_write or chip_en or address)
if (read_write == 0 && chip_en)
 data_out = mem[address];
else
 data_out = 0;

endmodule
    
Here is a simple testbench enviroment first done in Verilog HDL and in System Verilog.

Testbench enviroment done in Verilog HDL

////////////////////////////
// Testbench
////////////////////////////
module Testbench(
                             address,
            data_in,
            data_out,
            read_write,
            chip_en
           );

output [7:0] address, data_in;
input  [7:0] data_out;
output       read_write, chip_en;

reg [7:0] address, data_in;
reg       read_write, chip_en;

initial
     begin
        address           = 0 ;
        data_in             = 0 ;
        read_write = 0 ;
        chip_en            = 0 ;

        repeat(3)
            begin
              
               #100  
               chip_en            = 1 ;
               address           = $random ;
               data_in             = $random  ;  
               read_write  =  1’b1 ;
              
               #100
                read_write  =  1’b0 ;
             end

#100
chip_en            = 0 ;

#100
        $finish;

     end // initial

  endmodule

        
        
///////////////////////////////////////////////////////////////////////////////
// top module: integrating Dut and it’s Testbench
///////////////////////////////////////////////////////////////////////////////

module top();
       wire [7:0] address, data_in;
  wire [7:0] data_out;
  wire       read_write, chip_en;

       memory D (
                    .address    ( address    ),
        .data_in    ( data_in    ),
         .data_out   ( data_out   ),
         .read_write ( read_write ),
         .chip_en    ( chip_en    )
               );

     Testbench tb (
                         .address    ( address    ),
               .data_in    ( data_in    ),
               .data_out   ( data_out   ),
               .read_write ( read_write ),
               .chip_en    ( chip_en     )
           );
           
endmodule


Testbench enviroment done in System Verilog

Simulate with Cadence tools  with the command:

irun -access +r -gui  top.sv




////////////////////////////////////////////////////////////////////////////////////////////////
// Interface: for communication between Dut and it’s Testbench
////////////////////////////////////////////////////////////////////////////////////////////////

`ifndef GUARD_INTERFACE
`define GUARD_INTERFACE
interface intf ();
logic read_write, chip_en ;
logic[7:0] address, data_in ;
logic[7:0] data_out ;
modport tb (output read_write, chip_en, address, data_in, input data_out);
endinterface :intf
`endif

///////////////////////////
// Testbench
////////////////////////////
module Testbench(intf tb_if);
initial
begin
tb_if.address = 0 ;
tb_if.data_in = 0 ;
tb_if.read_write = 0 ;
tb_if.chip_en = 0 ;


repeat(3) begin
#100
tb_if.chip_en = 1 ;
tb_if.address = $random ;
tb_if.data_in = $random ;
tb_if.read_write = 1 ;


#100
tb_if.read_write = 0 ;
end


#100
tb_if.chip_en = 0 ;


#1000
$finish;
end
endmodule


///////////////////////////////////////////////////////////////////////////////
// top module ( top.sv ): integrating Dut and it’s Testbench
////////////////////////////////////////////////////////////////////////////
        
        
`ifndef GUARD_TOP
`define GUARD_TOP
 
module top();
 
intf bus_if(); //interface instantiation
Testbench tb (
.tb_if( bus_if )
        ) ; // Pass the modport into the module
memory d (
.address ( bus_if.address ), // connect the verilog
.data_in ( bus_if.data_in ), // RTL port using interface hierarchy signal name.
.data_out ( bus_if.data_out ),
.read_write ( bus_if.read_write ),
.chip_en ( bus_if.chip_en )
);
endmodule : top
`endif