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

No comments:

Post a Comment