Wednesday, 21 October 2015

Use of polymorphism for System Verilog testbench development

Polymorphism is most common topic asked during interview of ASIC Verification Engineer. It is little bit difficult to understand concept and if you know it properly it will be very much useful in development of verification environment.

Here I have tried to explain polymorphism by taken example of System Verilog verification environment generation. 

Let's assume that we are building System Verilog testbench for RTL module name DUT. 

We need to create environment and test case. Now assume for now that we are using only one testcase to verify our DUT (Ideally you will never have such DUT). 

So our testcase will be written as follows. Name of the test is Sample test.
Here I have given simple code for System Verilog testbench .


// Defining base test
class base_test;
  dut_environment env;
  // Define build function to create env
  function void build();
    env = new("env");
  endfunction : build

   // Define run task to call run method of environment object
   task run();
     env.run();
   endtask : run
endclass : base_test


// TOP module
module top();
  // Instance of base_Test
  base_test b_test;
  initial begin
    // new for base_test object
    b_test = new();
    b_test.build();
    b_test.run();
  end
endmodule : top


Above testbench is shows that in our top simulation module instance of base_test is taken. In this base_test class build method creates env and run method generates our testbench stimulus. 

If we have only one testcase for our verification purpose than above testbench will work but generally this is not the case. Let's assume that we have two different testcase and in each of them we have to create env. The code for both testcase is as follows.  

class testcase1 extends base_test;
   task run();
     fork
       super.run();
     join_none
     // Case scenario1
   endtask : run
endclass :  testcase1


class testcase2 extends base_test;
   task run();
     fork
       super.run();
     join_none
     // Test case scenario2
   endtask : run
endclass :  testcase2


Now If we do not understand use of polymorphism much than we would write code to run above two testcase as follows.

// TOP module
module top();
  testcase1 test1;
  testcase2 test2;
  initial begin
    // If test argument to run test is testcase1 than run that case
    if($plus$argv("testcase1"))begin
      test1 = new();
      test1.build();
      test1.run();
    end
    // If test argument to run test is testcase2 than run that case
    if($plus$argv("testcase1"))begin
      test2 = new();
      test2.build();
      test2.run();
    end
  end
endmodule : top

Here we have taken simple example. If suppose we have 20 test cases and we have to call different function of the testcase like new, build, connect, run, report, etc. We have to write so many lines in our top modules to run those testcases and it is really difficult to manage when one function is added in test case. And it will be really mesh up when you have 10 different function available and each testcase uses limited number of testcase from that 10 test cases. To avoid such situations we need to use polymorphism concept of system verilog.


To use advantage of polymorphism define every function and task in your base test as virtual as shown below.

// Defining base test
class base_test;
  dut_environment env;
  // Define build function to create env
  virtual function void build();
    env = new("env");
  endfunction : build

   // Define run task to call run method of environment object
   virtual task run();
     env.run();
   endtask : run
endclass : base_test

Now you can see that we have defined virtual function and task in base class and we have same function and task overridden in derived class. So as per polimorphism concept the pointer of base class which points to derived class instance memory will call extend class's function only when it is called. 

So we can write our new top.sv file as follows with polymorphism concept.


// TOP module
module top();
  base_test b_test;
  initial begin
    // If test argument to run test is testcase1 than run that case
    if($plus$argv("testcase1"))begin
      b_test = new testcase1::new();
    end
    // If test argument to run test is testcase2 than run that case
    if($plus$argv("testcase2"))begin
      b_test = new testcase2::new();
    end
     b_test.build();
     b_test.run();
  end
endmodule : top

You can see that when we are using polymorphism concept for testcase writing it is very simple to add new test case. For example you have to add new testcase testcase3 you need to add just following 3 lines only.

   if($plus$argv("testcase3"))begin
      b_test = new testcase3::new();
    end
And you are done. As we know that b_test points to memory location which is allocated for extended testcase. whenever I am calling b_test.run() task it will call method of extend class if it is overridden in extended class otherwise it is going to call method of base class itself. Thus it will be very much easy to add new function in our testcases. There wont be much change in your top.sv file.

Suppose you have added report function which should be called after run() task gets completed in testcase. What you need to do is just add report() function in base class and write down following line bellow b_test.run() task.

      b_test.report();


If you override report() function in extended class than it will call overridden function and if you do not specify anything about it in extend function than it will call base class report() function. This way it is really easy to develop System Verilog testbench using OOP and Polymorphism.


This was use of polymorphism , This blog is still under development I need to change lot of formatting and   I will work on blog about polymorphism once I am done with this.

Thanks for reading it !!!