Showing posts with label ovm. Show all posts
Showing posts with label ovm. Show all posts

Friday, October 28, 2016

Randomization.


  • $random : return an 32-bit signed random number.
  • $urandom : return an 32-bit unsigned random number.
  • $srandom(seed) : set random seed for $urandm.
  • use run-time switch : +ntb_random_seed=seed_value
  • $urandom_range(min, max)
  • randcase

randcase
   10 : f1();
   20 : f2();
   30 : x = 100;
   50 : randcase ... endcase; // nested
endcase

Wednesday, October 12, 2016

OVM - Agent, Monitor, Subscriber.


Monitors are for coverage collections and checkings.
A monitor is a passive component that can only observe.
A monitor (standard write method) must not modify the transaction or behavior.
A monitor write method must not block or consume any time.

OVM/UVM OOP Goal and Purpose:

  • Standard strutural way to build test environment with standard components
  • Reusable VIP
  • Layered sequences
  • Transaction-level communication
  • Separate tests from test bench
  • Configurable test bench


Connecting Monitor through Analysis_port
//
// can extends ovm_component too
// but choose a standard ovm_monitor helps in readability
//
class my_monitor extends ovm_monitor;
 
   `ovm_component_utils(my_monitor) 

   ovm_analysis_port #(my_transaction) aport;

   virtual dut_if dut_vi;

   function new ...

   function void build; 
      super.build();
      aport = new("aport", this);
   endfunction : build

   // Standard run task includes,
   // 1. monitor/sampling the pins on DUT, reading the 
   // 2. assembling the transaction
   // 3. send them to the rest of the verification environment
   task run; forever
      begin
         my_transaction tx; // handle to transaction

         // synchronize tx with clock used in DUT
         @(posedge dut_vi.clock);
         // factory call to create and populate the individual field with tx
         tx = my_transaction::type_id::create("tx"); 
         tx.cmd = dut_vi.cmd;
         tx.addr = dut_vi.addr;
         tx.data = dut_vi.data;

         // Sends tx through analysis port out to any subscribers registered
         // in ovm environment
         aport.write(tx);
      end
   endtask // run

endclass : my_monitor

class my_agent extends ovm_agent; 
   ...
   ovm_analysis_port #(my_transaction) aport;
   ...
   function void build;
      super.build();
      aport = new("aport", this);

      // use factory method to create child component for this parent
      my_sequencer_h = my_sequencer::type_id::create ... 
      my_driver_h    = my_driver ::type_id::create ... 
      my_monitor_h   = my_monitor ::type_id::create ...
   endfunction: build

   function void connect;
      ...
      // child to parent connection to the analysis port
      my_monitor_h.aport.connect( aport );

  endfunction: connect

// Top level verification environment
class my_env extends ovm_env;
   ...

   my_agent_h.aport.connect(
   my_subscriber_h.analysis_export );
   ...

   function void build;
      super.build();
      my_agent_h = my_agent ::type_id::create ... 
      my_subscriber_h = my_subscriber::type_id::create ...
   endfunction: build

   function void connect;
      my_agent_h.aport.connect( my_subscriber_h.analysis_export );
   endfunction: connect

endclass : my_agent

// A subscriber need to implement a "write" method/function
// log transactions
// collect coverage information
// check and compare to expected behaviors 
class my_subscriber extends ovm_subscriber #(my_transaction);

   `ovm_component_utils(my_subscriber) 
   ...

   function void write (my_transaction t);
      ...

      // Coverage registers 
      bit cmd;
      int addr;
      int data;

      covergroup cover_bus;
         coverpoint cmd;
         coverpoint addr { bins a[16] = {[0:255]}; } 
         coverpoint data { bins d[16] = {[0:255]}; }
      endgroup: cover_bus

   endfunction: write

   // use standard reporting to report the observation
   function void write(my_transaction t); ...
      cmd = t.cmd;
      addr = t.addr; data = t.data; 
      cover_bus.sample();

      ovm_report_info("ja", "Transaction received");
   //       ^           ^               ^
   //    Severity     Originator/ID    Text

   endfunction: write


endclass : my_subscriber




Tuesday, October 11, 2016

OVM - Transactions.


ovm_sequence -> ovm_sequence_item -> ovm_transaction

   ovm_sequence
   The base class for combinations of transactions.
   It can be nested, layered or virtual sequences.
   It can be constrained random sequence of transactions.
   It drive transactions into DUT.

   ovm_sequence_item
   The base class for user-defined sequence items and also the base class for the
   ovm_sequence class.  (help)

   ovm_transaction
   The base class for all user-defined transactions.


Example
// To make transaction part of the sequence,
// we use ovm_sequence_item to extend transaction

class my_transaction extends ovm_sequence_item;
   `ovm_object_utils(my_transaction)

   rand bit cmd;
   rand int addr;
   rand int data;

   constraint c_addr { addr >= 0; addr < 256; } 
   constraint c_data { data >= 0; data < 256; }

   // transaction isn't part of the component family
   // it doesn't have the "parent" parameter
   function new (string name = "");
      super.new(name);
   endfunction: new

endclass : my_transaction

//--------------------------------------------------------------
// SV parameterized class, same as class template in C++
// use parameter to modify the properties later when used
// usually in sequencer, you can change the type or the way 
// transactions are generated at specific time.
//--------------------------------------------------------------

class my_sequencer extends ovm_sequencer #(my_transaction);

   `ovm_component_utils(my_sequencer)

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

endclass : my_sequencer

//--------------------------------------------------------------
// Sequencer component will run multiple sequence which is 
// composed of transactions and generated data
//--------------------------------------------------------------

class my_sequence extends ovm_sequence #(my_transaction);

   `ovm_object_utils(my_sequence)

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

   // The standard method "body" used only in ovm_sequence
   // to define the main behavior of the sequence,
   // It generates a continuous stream of transactions and data 
   // It is a method but similar to a phase in ovm_component

   task body; 
      forever
      begin
         my_transaction tx;

         // Use factory method to instantiate the transaction
         tx = my_transaction::type_id::create("tx");

         // the start and finish items will evoke the machinary 
         // to communicate with the driver down stream
         start_item(tx);
         assert( tx.randomize() );  // randomize transactions
         finish_item(tx);
      end
   endtask: body

endclass : my_sequence

// Driver is what drives the data into pins
class my_driver extends ovm_driver #(my_transaction); 

   `ovm_component_utils(my_driver)

   virtual dut_if dut_vi;

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

   function void build;
      super.build();
      ...

   task run;
      repeat(4)
      begin
         my_transaction tx;
         @(posedge dut_vi.clock);

         // Driver consumes the transactions generated by
         // my_transaction and wiggles the pins on the DUT
         seq_item_port.get(tx);

         // Pin Wiggling
         dut_vi.cmd  = tx.cmd;
         dut_vi.addr = tx.addr;
         dut_vi.data = tx.data;

      end
      @(posedge dut_vi.clock) ovm_top.stop_request();

   endtask: run

endclass : my_driver


Example of ovm_sequence with constrained random sequence of transactions

class read_modify_write extends ovm_sequence #(my_transaction);

   `ovm_object_utils(read_modify_write)

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

   task body;
      my_transaction tx;
      int a;
      int d;

      // create transaction using factory method
      tx = my_transaction::type_id::create("tx"); 
      start_item(tx);

      // read
      // inline constraint to read with random data
      assert( tx.randomize() with { cmd == 0; } ); 

      // modify
      a = tx.addr;
      d = tx.data;
      ++d;

      // write
      tx = my_transaction::type_id::create("tx"); 
      start_item(tx);
      assert( tx.randomize() with {
         cmd == 1; addr == a; data == d; } );

      finish_item(tx);
endclass : read_modify_write

class seq_of_commands extends ovm_sequence #(my_transaction);

   `ovm_object_utils(seq_of_commands)
   rand int n;
   // a control knob that can be controlled from outside
   constraint how_many { n inside {[2:4]}; } 
   ...

   task body;
      repeat(n)
      begin
         read_modify_write seq;
         seq = read_modify_write::type_id::create("seq"); 
         start_item(seq);
         finish_item(seq);
      end
   endtask: body
endclass : seq_of_commands

package my_seq_library;
   import ovm_pkg::*;

   class read_modify_write extends ovm_sequence #(my_transaction);
      ...

   class seq_of_commands extends ovm_sequence #(my_transaction);
      ...

endpackage

class test1 extends ovm_test; 

   `ovm_component_utils(test1)
   my_env my_env_h;
      ...

   task run;
      read_modify_write seq;
      seq = read_modify_write::type_id::create("seq");
      seq.start( my_env_h.my_agent_h.my_sequencer_h );
   endtask : run

endclass : test1

class test2 extends ovm_test; 

   `ovm_component_utils(test2)
   my_env my_env_h;
      ...

   task run;
      seq_of_commands seq;
      seq = seq_of_commands::type_id::create("seq");

      assert( seq.randomize() );
      seq.start( my_env_h.my_agent_h.my_sequencer_h );

   endtask : run

endclass : test2

class test3 extends ovm_test; 

   `ovm_component_utils(test3)
   my_env my_env_h;
      ...

   task run;
      seq_of_commands seq;
      seq = seq_of_commands::type_id::create("seq");
      seq.how_many.constraint_mode(0);

      assert( seq.randomize() with {
              seq.n > 10 && seq.n < 20; } );
      seq.start( my_env_h.my_agent_h.my_sequencer_h );

   endtask : run

endclass : test3

module top;
   ...

   initial
   begin: blk
      ...
      run_test("test3"); 
   end

endmodule: top


Command line when leaving run_test(""); blank
% vsim +OVM_TESTNAME=test3

OVM 101 - Standard Component Phases.


In OVM, agents usually contain driver, monitor and sequencer.

Example

class my_agent extends ovm_agent; 
   `ovm_component_utils(my_agent) // register for type_id

   my_sequencer my_sequencer_h;
   my_driver my_driver_h;

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

   //------------------------------------------------------------
   // Using Factory methods allows them to be overridden in tests
   //      to change behavior without modifying the source code
   // Build create the instances of the sequencer and driver
   // Connect picks up the handle of ports of children and connect
   //      them
   //------------------------------------------------------------

   function void build;
      super.build(); 
      my_sequencer_h = 
         my_sequencer::type_id::create("my_sequencer_h", this);
                                     // instance name,   parent
      my_driver_h =
         my_driver::type_id::create  ("my_driver_h" , this);

   endfunction: build

   // Lower level connection in another function is good practice
   // seq_item_port is the handler of the ports

   function void connect; 
      my_driver_h.seq_item_port.connect(
         my_sequencer_h.seq_item_export ); 
   endfunction: connect

endclass : my_agent

Standard Phases in Component Class

// OVM component is the base class for many other classes
// my_component can be my_driver, my_monitor, my_agent and so on

// use standard phases to coordinate test activities of multiple 
// components and make them re-usable

class my_component extends ovm_component;

   `ovm_component_utils(my_component) // standard factory registration

   virtual dut_if dut_if_h; // External interfaces

   // Internal component handles
   my_sequencer my_sequencer_h; 
   my_driver my_driver_h;

   // phase 0, constructor new method
   function new(string name, ovm_component parent); 
      super.new(name, parent);
   endfunction: new

   // phase 1
   // Build from top-down
   function void build;
      super.build();
   endfunction : build

   // Connect from bottom-up
   // Connect child components
   function void connect;
   endfunction : connect

   // phase to run simulation, callback, open simulation files
   function void start_simulation;
   endfunction : start_simulation

   // Only task can execute event controlled statements
   // with time signatures 
   task run;
   endtask : run

   function check;
   endfunction : check

   function report;
   endfunction : report

endclass : my_component


Friday, October 7, 2016

OVM 101 - part 2.

Test Environment Nitty Gritty Code
  • Configuration - Connecting virtual interface from top level to DUT.
  • Use configuration table to store the wrapper(s) of virtual interface properties.
  • This way, the test bench can be used with different configurations, changing wrappers.
  • Configuration using "set_config_object" and "get_config_object" overrides top-down.
    ( from ovm_test -> ovm_env -> ovm_component)
// Top level "module" contains class based test environment
//        and structural interface that drives DUT (module)
module top;

import ovm_pkg::*;
import my_pkg::*;

...
dut_if dut_if_inst1 ();
dut dut_inst0 (._if (dut_if_inst1) );

initial begin: blk
    dut_if_wrapper if_wrapper = new ("if_wrapper", dut_if_inst1);
                   // path  field_name        value      0: don't clone
    set_config_object("*", "dut_if_wrapper", if_wrapper, 0);

    run_test ("my_test_set");
end

endmodule : top

class dut_if_wrapper extends ovm_object;

    virtual dut_if dut_vi;
    function new (string s, virtual dut_if if_arg); 
        super.new(s);
        dut_vi = if_arg;
    endfunction : new

endclass : dut_if_wrapper

// ovm classes
// usually classes of test environment that includes fixed test bench
// and variable test sets

`include "ovm_macros.svh"

package my_package;
import ovm_pkg::*;

// Fixed test environment
class my_test_env extends ovm_env;
    `ovm_component_utils (my_test_env)

    virtual dut_if dut_virtual_if_inst;

    // constructor
    function new (string s, ovm_component inst_parent);
        super.new (s, inst_parent);
    endfunction : new

    function void build;
        super.build ();
        begin
            ovm_object obj;
            dut_if_wrapper if_wrapper; 
            get_config_object("dut_if_wrapper", obj, 0);
            assert( $cast(if_wrapper, obj) );
            dut_virtual_if_inst = if_wrapper.dut_vi;
        end
    endfunction : build

    task run;
        #10 dut_virtual_if_inst.data = 0;
        #10 dut_virtual_if_inst.data = 1;
        #10 ovm_top.stop_request();
    endtask : run

endclass : my_test_env

class my_test_set extends ovm_test;
    `ovm_component_utils (my_test_set)

    my_test_env my_test_env_handle;

    // constructor
    function new(string s, ovm_component inst_parent);
        super.new(s, inst_parent);
    endfunction : new

    function void build;
        super.build();
        my_test_env_handle = my_test_env::type_id::create
            ("my_test_env_handle", this);
    endfunction : build

endclass : my_test_set

// interface dut_if(); -> between test components and DUT
interface dut_if ();

    logic clock, reset;
    logic data_input;

endinterface : dut_if

// Design under test
module DUT (dut_if _if);

    always @ (posedge _if.clock)
    begin
    end

endmodule : dut

// Higher level overriding lower level configuration
// Overriding Example:
// in ovm_test
set_config_object ("*", "data", x);
// in ovm_env
get_config_object ("opt", y);
set_config_object ("*", "data", y);
// in ovm_component
get_config_object ("data", obj); // obj = x


OVM 101.

Test Environment Structure
diagram
// Top level "module" contains class based test environment
//        and structural interface that drives DUT (module)
module top;

import ovm_pkg::*;
import my_pkg::*;

...
dut_if dut_if_inst0 ();
dut dut_inst0 (._if (dut_if_inst0) );

initial
begin
    run_test ("my_test_set");
end

endmodule : top

// ovm classes
// usually classes of test environment that includes fixed test bench
// and variable test sets

`include "ovm_macros.svh"

package my_package;
import ovm_pkg::*;

// Fixed test environment
class my_test_env extends ovm_env;
    `ovm_component_utils (my_test_env)

    // constructor
    function new (string s, ovm_component inst_parent);
        super.new (s, inst_parent);
    endfunction : new

    function void build;
        super.build ();
    endfunction : build

    task run;
        #10 ovm_top.stop_request();
    endtask : run

endclass : my_test_env

class my_test_set extends ovm_test;
    `ovm_component_utils (my_test_set)

    my_test_env my_test_env_handle;

    // constructor
    function new(string s, ovm_component inst_parent); 
        super.new(s, inst_parent);
    endfunction : new

    function void build;
        super.build();
        my_test_env_handle = my_test_env::type_id::create 
            ("my_test_env_handle", this);
    endfunction : build

endclass : my_test_set

// interface dut_if(); -> between test components and DUT
interface dut_if ();
...
endinterface : dut_if

// Design under test
module DUT (dut_if _if); 
endmodule : dut


UVM/OVM primer.


Basic Concepts:
  • Constrained random, coverage-driven verification
  • Configurable, flexible, test benches
  • Verification IP reuse
  • Separation of tests from test bench
  • TLM communication
  • Layered sequential stimulus
  • Standardized messaging
  • Register layer (UVM)

UVM Class Hierarchy
  • uvm_object

OVM Class Hierarchy
  • ovm_object
    • obm_report_object
      • ovm_component
        • ovm_env
        • ovm_test
        • ovm_agent
        • ovm_sequencer
        • ovm_driver
        • ovm_monitor
        • ovm_scoreboard
  • ovm_agent usually contains driver / monitor and sequencer

Prerequisite:
  • Verification Planning
  • Constrained Random Verification
  • SystemVerilog
  • UVM/OVM Class Library, OOP, TLM

Useful Links: