Warning: session_start(): Cannot send session cookie - headers already sent by (output started at /home2/aefody/public_html/specman-verification.com/scripts/sb_utility.php:873) in /home2/aefody/public_html/specman-verification.com/index.php on line 11

Warning: session_start(): Cannot send session cache limiter - headers already sent (output started at /home2/aefody/public_html/specman-verification.com/scripts/sb_utility.php:873) in /home2/aefody/public_html/specman-verification.com/index.php on line 11

Warning: strpos(): needle is not a string or an integer in /home2/aefody/public_html/specman-verification.com/index.php on line 35

Warning: strpos(): needle is not a string or an integer in /home2/aefody/public_html/specman-verification.com/index.php on line 47

Warning: strpos(): needle is not a string or an integer in /home2/aefody/public_html/specman-verification.com/index.php on line 59

Warning: strpos(): needle is not a string or an integer in /home2/aefody/public_html/specman-verification.com/index.php on line 71
Tips for HVL and HDL users with special emphasis on Specman-e, SystemVerilog and Questa
Specman Verification
Verification engineer? Verifigen has the right project for you!
No comments! 
Monday, August 17, 2009, 11:04 AM - Hardcore verification
Due to an annoying spam comments attack I've decided to disable comments for a while. Please feel free to contact me directly with anything you would like to say at avidan_e at yahoo dot com.
  |  permalink
Do you believe in life after „when“ inheritance? 
Saturday, August 1, 2009, 10:13 AM - Hardcore verification
e „when“ inheritance has something in common with the internet, the cell-phone, the TV, the reality show and all of these other great inventions: Everybody that was born into the world after they were invented has a very hard time imagining life without it. This disbelief persists even in the face of overwhelming proofs that life can and do exist without these inventions, such as the shocking personal testimonies of people who used to have books in their living room instead of a HD flat screen. Going back to the verification world, people who have used “when” are often convinced that it is impossible to really verify a DUT without this feature, no matter how many one spin tape-outs to the contrary you present them with.

As one of the few people who have gone back in time from a world with “when” to a world without it, I think I have quite a comprehensive view with regards to the needs and requirements that “when” inheritance fulfils, the alternatives to “when” inheritance that SV/OVM has to offer, and the shortcomings of these alternatives. For the sake of my impatient readers, I’ll start with the bottom line: working with a language that doesn’t support “when”, will make your code uglier, and your debugging harder. However, it is definitely not the end of the world.

“when” inheritance is very strong where transactions of different types take place on a single channel completely at random. This somewhat lame description can be resumed in one word: packets. Packet traffic is often made of several types of packets that can appear on a single channel completely at random. When you’re dealing with this kind of traffic, using “when” inheritance will make your code cleaner and easier to debug.

In order to understand why, let us have a look at the two common solutions to generating such packet traffic in SV/OVM. In the first solution, often referred to as “the factory” solution, you would have a different class for each packet type. To generate the packet traffic, you have to go in two stages: First you randomly select the type of the packet you would like to generate next, then you generate an instance of the corresponding class. The code at the end of this entry (which can be used by OVM newbies also as a complete sequence example) demonstrates this solution.

At the height of the e-SV wars, some years ago, this solution was strongly promoted as a “when” replacement. Personally, I don’t like it at all, mainly because of (A) the two step randomization proecess, which limits the use of bidirectional constraints and (B) the fact that the type of the packet, which in my eyes is a property of the packet, is actually moved out of the packet to the factory that generates it (in the case of my example, the factory is located in the sequence). If you would like to constrain all packets to be of a certain type, you have to add a constraint to the factory, not to the packet as you would do in the case of “when”. This means that some of the packet properties are constrained through the packet object itself, while others are constrained through the factory. Ugly. It was also said at the time that this solution might lead to a “class explosion” if you have more then one “when” determinant. From my experience, this doesn’t happen too often in real life projects, so this objection belongs more in the theoretical realm.

If you would like to keep all the packet properties in one single packet class, like me, and still be able to generate different kinds of packets, at a distribution that can be easily constrained from the test by the user, the easiest solution is simply to have a packet class which is the aggregation of all the subtypes. This means that the packet class has all the fields of all the subtypes, but for a specific subtype, only some of these fields are actually valid/used. Obviously, the downside of this approach is that you have many garbage fields in your class, and this might make debugging more complex. When you open your packet in a watch window, you will have to search a bit for the fields that are actually relevant for you. However, this pain can be somewhat reduced by providing type-specific print functions.

So, as said above, verification without “when” is possible, at the price of some ugliness of code, or some extra effort in debugging and if this takes several thousand dollars off your license fees, it is worth considering. What prevents many e people from seeing this simple truth is, I believe the fact that “when” inheritance is very often used at places where it is not really required. Take for example the vr_ad register package: is it really required to define each register as a “when” subtype, or could they also be defined as normal derived classes of a register base class? In my opinion, “when” has little added value here. It is very rare that you just choose to read or write a random register, and if you know which register you’re reading or writing upfront, why don’t you just instantiate the register class you want? In this case, instantiating a specific class and randomizing it means almost no additional pain. Where the type and the transaction are completely random, “when” is a big help. Where the type is not random, and the transaction is, you can do just as well with normal OO.

You can read some more on "when" inheritance in SV here

Factory example:

package packet_pkg;



import ovm_pkg::*;

`include "ovm_macros.svh"



typedef enum {A, B} packet_type_t;



class packet_base extends ovm_sequence_item;

   `ovm_object_utils(packet_base);



   function new(string name = "");     

      super.new(name);     

   endfunction

endclass



class packet_A extends packet_base;

   

   rand int a;



   `ovm_object_utils_begin(packet_A);

   `ovm_field_int(a, OVM_ALL_ON)

   `ovm_object_utils_end

   

   function new(string name = "");     

      super.new(name);     

   endfunction

endclass



class packet_B extends packet_base;



   rand int b;



   `ovm_object_utils_begin(packet_B);

   `ovm_field_int(b, OVM_ALL_ON)

   `ovm_object_utils_end

   

   function new(string name = "");     

      super.new(name);     

   endfunction

endclass



class packet_sequencer extends ovm_sequencer#(packet_base, packet_base);

   `ovm_sequencer_utils(packet_sequencer);

   

   function new(string name, ovm_component parent);   

      super.new(name, parent);

      `ovm_update_sequence_lib

   endfunction

endclass



class packet_driver extends ovm_driver#(packet_base, packet_base);

  `ovm_component_utils(packet_driver)



  function new(string name, ovm_component parent);     

    super.new(name,parent);

  endfunction



  task run();

     packet_base next_packet;



     // This driver does nothing except for printing

     forever begin

        seq_item_port.get_next_item(next_packet);

        ovm_report_info("NEXT_PACKET",{"The next packet is:\n", next_packet.sprint()});

        seq_item_port.item_done(next_packet);

     end                

  endtask 

endclass



class packet_env extends ovm_env;

   `ovm_component_utils(packet_env);



   packet_sequencer sequencer;

   packet_driver driver;

   

   function new(string name, ovm_component parent);   

      super.new(name, parent);

   endfunction // new



   function void build();

      super.build();

      sequencer = packet_sequencer::type_id::create("packet_sequencer", this);

      driver = packet_driver::type_id::create("packet_driver", this);

   endfunction

   

   function void connect();

      super.connect();

      driver.seq_item_port.connect(sequencer.seq_item_export);

      driver.rsp_port.connect(sequencer.rsp_export);

   endfunction

endclass



class packet_seq_base extends ovm_sequence#(packet_base);



  `ovm_sequence_utils(packet_seq_base, packet_sequencer)



   // constrain this field to control packet_type

   rand packet_type_t packet_type;

   

   packet_A p_A;

   packet_B p_B;

   

  function new(string name = "packet_seq_base");

    super.new(name);

  endfunction

endclass

endpackage



package test_pkg;

import ovm_pkg::*;

import packet_pkg::*;

`include "ovm_macros.svh"



class specific_seq extends packet_seq_base;

   `ovm_sequence_utils(specific_seq, packet_sequencer)



   function new(string name = "specific_seq");

      super.new(name);

   endfunction // new



   // in a specific sequence:

   // 1. randomize to choose the next packet

   // 2. use the sequencer built-in factory

   // to create the item, randomize and send it

   

   task body();  

     forever begin



        randomize();

        

        case(packet_type)

          A : `ovm_do_with(p_A, {p_A.a == 5;}) 

          B : `ovm_do_with(p_B, {p_B.b == 6;}) 

        endcase



        #1;

     end

  endtask

endclass  



class test extends ovm_test;

   `ovm_component_utils(test);



   packet_env env;

   

   function new(string name = "test", ovm_component parent=null);

      super.new(name,parent);

   endfunction

   

  virtual function void build();

     super.build();



     env = packet_env::type_id::create("env", this);

     set_config_string("env.sequencer","default_sequence", "specific_seq");

  endfunction

endclass                                 

endpackage



module top;

   import ovm_pkg::*;

   import packet_pkg::*;

   import test_pkg::*;



   initial begin      

      run_test();

   end

endmodule

  |  permalink
OVM FIFO should be the last choice 
Saturday, July 18, 2009, 11:24 AM - Hardcore verification
OVM users tend to use FIFOs just about everywhere. This is largely due to two reasons: (A) using a FIFO sometime simplifies the OVM code that is required to create the actual connection and (B) This solution is frequently promoted in the OVM reference material. FIFOs, however, come with a significant price, which is often forgotten during coding, but then fires back painfully during debugging: they break the data flow. When you insert a FIFO between two elements you are actually creating, without even noticing it, two independent processes – one which does the put(), and another which does the get(). This means, for example, that if during debugging you want to follow your transaction across a FIFO, then you have to place one breakpoint on the put(), then, when execution stops, go to the other end of the FIFO, and place another breakpoint after the get(). Or it means that you won’t be able to use the function call stack window, to trace back the origins of a specific transaction. To see this live, just run the following example, and follow the instructions in the comments.


package vip_pkg;



import ovm_pkg::*;

`include "ovm_macros.svh"



class tr extends ovm_object;

   int serial_num;

   

   `ovm_object_utils_begin(tr)

   `ovm_field_int(serial_num, OVM_ALL_ON);

   `ovm_object_utils_end



   function new(string name ="");

      super.new(name);

   endfunction

endclass



// a : put side of the fifo

// b : get side of the fifo

// env : instantiates the fifo and connects

// it between a and b



// The port at the beginning

class a extends ovm_component;

   `ovm_component_utils(a)



   ovm_blocking_put_port#(tr) put_port;



   function new(string name, ovm_component parent);

      super.new(name,parent);

      put_port = new("put_port", this);

   endfunction



   task run();

      int serial_num;

      forever begin

         tr tr_i;

         

         $cast(tr_i, create_object("tr", "tr_i"));

         tr_i.serial_num = serial_num++; 

         // To follow a transaction place a breakpoint

         // on the line below.

         // When it breaks, go to "b" and place a

         // breakpoint at the designated point.

         // This is the only way you can follow a

         // transaction across a FIFO.

         put_port.put(tr_i);

         #1;

      end

   endtask

endclass 



// The implementation at the end

class b extends ovm_component;

   `ovm_component_utils(b)

   

   ovm_blocking_get_port#(tr) get_port; 



   function new(string name, ovm_component parent);

      super.new(name,parent);

      get_port = new("get_port",this);

   endfunction



   task run();

      tr tr_i;

      forever

        begin

           get_port.get(tr_i);

           // Place a breakpoint on the line below

           // to see the transaction come out of

           // the fifo.

           // Once it breaks, open the stack window.

           // You'll see that a is not anywhere there,

           // although that would be the most helpful

           // hint.

           tr_i.print();

        end

   endtask

endclass



// The env connects the port to the export

class env extends ovm_env;

   `ovm_component_utils(env)



   a a_i;

   b b_i;

   tlm_fifo#(tr) tlm_fifo_i;



   function new(string name, ovm_component parent);

      super.new(name,parent);

   endfunction



   function void build();

      super.build();

      $cast(a_i, create_component("a", "a_i"));

      $cast(b_i, create_component("b", "b_i"));

      tlm_fifo_i = new("tlm_fifo_i",this);

   endfunction // void



   function void connect();

      super.connect();

      a_i.put_port.connect(tlm_fifo_i.blocking_put_export);

      b_i.get_port.connect(tlm_fifo_i.blocking_get_export);

   endfunction

endclass // env



endpackage // vip



package tests;



import ovm_pkg::*;

import vip_pkg::*;

`include "ovm_macros.svh"



class test extends ovm_test;

   `ovm_component_utils(test);



   env env_i;

   

   function new(string name = "test", ovm_component parent=null);

      super.new(name,parent);

   endfunction // new



   function void build();      

      super.build();

      $cast(env_i, create_component("env", "env_i"));

   endfunction

endclass

endpackage // z



module top();

   import ovm_pkg::*;

   import vip_pkg::*;

   import tests::*;

   



   initial

     run_test();

endmodule


Note how much effort you need in order to trace a single transaction across one FIFO. If you have several instances of “a” and “b” this will become even worse. Also note, that between the break at the “put()” and the break at the “get()” you’ll have at least 1 delta delay difference. Much better to avoid such garbage delays…Where you really need a FIFO, this pain can’t be avoided. However, in the majority of cases you can simply use a normal port to imp connection as shown below:


package vip_pkg;



import ovm_pkg::*;

`include "ovm_macros.svh"



class tr extends ovm_object;

   int serial_num;

   

   `ovm_object_utils_begin(tr)

   `ovm_field_int(serial_num, OVM_ALL_ON);

   `ovm_object_utils_end



   function new(string name ="");

      super.new(name);

   endfunction

endclass // tr



// a : the port at the beginning

// b : the implementation at the end



// The port at the beginning

class a extends ovm_component;

   `ovm_component_utils(a)



   ovm_blocking_put_port#(tr) put_port;



   function new(string name, ovm_component parent);

      super.new(name,parent);

      put_port = new("put_port", this);

   endfunction



   task run();

      int serial_num;

      forever begin

         tr tr_i;

         

         $cast(tr_i, create_object("tr", "tr_i"));

         tr_i.serial_num = serial_num++;      

         put_port.put(tr_i);

         #1;

      end

   endtask

endclass 



// The implementation at the end

class b extends ovm_component;

   `ovm_component_utils(b)

   

   ovm_blocking_put_imp#(tr,b) put_imp;



   function new(string name, ovm_component parent);

      super.new(name,parent);

      put_imp = new("put_imp",this);

   endfunction



   task put(tr tr_i);

      tr_i.print();    

   endtask

endclass



// The env connects the port to the export

class env extends ovm_env;

   `ovm_component_utils(env)



   a a_i;

   b b_i;



   function new(string name, ovm_component parent);

      super.new(name,parent);

   endfunction



   function void build();

      super.build();

      $cast(a_i, create_component("a", "a_i"));

      $cast(b_i, create_component("b", "b_i"));      

   endfunction // void



   function void connect();

      super.connect();

      a_i.put_port.connect(b_i.put_imp);

   endfunction

endclass // env



endpackage // vip



package tests;



import ovm_pkg::*;

import vip_pkg::*;

`include "ovm_macros.svh"



class test extends ovm_test;

   `ovm_component_utils(test);



   env env_i;

   

   function new(string name = "test", ovm_component parent=null);

      super.new(name,parent);

   endfunction // new



   function void build();      

      super.build();

      $cast(env_i, create_component("env", "env_i"));

   endfunction

endclass

endpackage // z



module top();

   import ovm_pkg::*;

   import vip_pkg::*;

   import tests::*;

   



   initial

     run_test();

endmodule


If you place a breakpoint on the print() at “b” now, and open the stack window, you’ll see “a”, which is probably what you want.

The port to imp connection, however, can become complicated to use if “b” has to connect to two different data sources. This is common in scoreboards that have to look at DUT input data and DUT output data. The solution is simply to tweak the OVM imp class a little bit so that it sends its own name, along with the transaction, thereby enabling its parent component to know where the transaction came from. For example, if a scoreboard has “dut_input” and “dut_output” ports, then the transactions coming from the “dut_input” port will come along with a string whose value is, surprisingly, “dut_input”. Here are the tweaked versions of the two most popular imps, namely, blocking_put_imp and analysis_imp. You can easily rewrite in the same way any other OVM imp when required.


package multiple_port_imps_pkg;

import ovm_pkg::*;

`include "ovm_macros.svh"



`define TLM_BLOCKING_PUT_MASK (1<<0)

`define TLM_ANALYSIS_MASK (1<<8)



class ovm_blocking_multiple_put_imp #(type T=int, type IMP=int)

  extends ovm_port_base #(tlm_if_base #(T,T));

   local IMP m_imp; 

   

   function new (string name, IMP imp); 

      super.new (name, imp, OVM_IMPLEMENTATION, 1, 1); 

      m_imp = imp; 

      m_if_mask = `TLM_BLOCKING_PUT_MASK;

   endfunction // new

   

   virtual function string get_type_name();

      return "ovm_blocking_multiple_put_imp";

   endfunction // string



   task put (T t);

      m_imp.put(get_name(),t);

   endtask

endclass



class ovm_multiple_analysis_imp #(type T=int, type IMP=int)

  extends ovm_port_base #(tlm_if_base #(T,T));

   local IMP m_imp; 

   

   function new (string name, IMP imp); 

      super.new (name, imp, OVM_IMPLEMENTATION, 1, 1); 

      m_imp = imp; 

      m_if_mask = `TLM_ANALYSIS_MASK;

   endfunction // new

   

   virtual function string get_type_name();

      return "ovm_multiple_analysis_imp";

   endfunction // string



   function void write (input T t);

      m_imp.write(get_name(),t);

   endfunction

endclass



endpackage


And now you can use these imps to connect “a1” and “a2” to “b”, without any FIFOs.


package vip_pkg;



import ovm_pkg::*;

import multiple_port_imps_pkg::*;

`include "ovm_macros.svh"



class tr extends ovm_object;

   int serial_num;

   

   `ovm_object_utils_begin(tr)

   `ovm_field_int(serial_num, OVM_ALL_ON);

   `ovm_object_utils_end



   function new(string name ="");

      super.new(name);

   endfunction

endclass // tr



// a1,a2 : the two ports at the beginning

// b : the implementation at the end



// The port at the beginning (both are identical)

class a extends ovm_component;

   `ovm_component_utils(a)



   ovm_blocking_put_port#(tr) put_port;



   function new(string name, ovm_component parent);

      super.new(name,parent);

      put_port = new("put_port", this);

   endfunction



   task run();

      int serial_num;

      forever begin

         tr tr_i;

         

         $cast(tr_i, create_object("tr", "tr_i"));

         tr_i.serial_num = serial_num++;      

         put_port.put(tr_i);

         #1;

      end

   endtask

endclass



// The implementation at the end

class b extends ovm_component;

   `ovm_component_utils(b)

   

   ovm_blocking_multiple_put_imp#(tr,b) put_imp_source1;

   ovm_blocking_multiple_put_imp#(tr,b) put_imp_source2;



   function new(string name, ovm_component parent);

      super.new(name,parent);

      put_imp_source1 = new("put_imp_source1",this);

      put_imp_source2 = new("put_imp_source2",this);

   endfunction

   

   task put(string port_name, tr tr_i);

      case(port_name)

        "put_imp_source1" : put_source1(tr_i);

        "put_imp_source2" : put_source2(tr_i);

      endcase

   endtask // put



   task put_source1(tr tr_i);

      tr_i.print();

   endtask // put_source1



   task put_source2(tr tr_i);

      tr_i.print();

   endtask

endclass



// The env connects the port to the export

class env extends ovm_env;

   `ovm_component_utils(env)



   a a1_i;

   a a2_i;

   b b_i;



   function new(string name, ovm_component parent);

      super.new(name,parent);

   endfunction



   function void build();

      super.build();

      $cast(a1_i, create_component("a", "a1_i"));

      $cast(a2_i, create_component("a", "a2_i"));

      $cast(b_i, create_component("b", "b_i"));      

   endfunction // void



   function void connect();

      super.connect();

      a1_i.put_port.connect(b_i.put_imp_source1);

      a2_i.put_port.connect(b_i.put_imp_source2);

   endfunction

endclass // env



endpackage // vip



package tests;



import ovm_pkg::*;

import vip_pkg::*;

`include "ovm_macros.svh"



class test extends ovm_test;

   `ovm_component_utils(test);



   env env_i;

   

   function new(string name = "test", ovm_component parent=null);

      super.new(name,parent);

   endfunction // new



   function void build();      

      super.build();

      $cast(env_i, create_component("env", "env_i"));

   endfunction

endclass

endpackage // z



module top();

   import ovm_pkg::*;

   import vip_pkg::*;

   import tests::*;

   



   initial

     run_test();

endmodule




  |  permalink

Back Next