Saturday 20 August 2016

Tutorial on Portable Stimulus for SoC and System Verification/Validation

Portable Stimulus is one of the latest initiative by Accelerate to address stimulus reusability across the different axis like across different users (e.g. Architects, Design Engineer, Verification Engineer, Firmware development engineer), across different platforms (Simulation, Emulation, FPGA, Board) and across multiple integration level (IP, Subsystem, SoC, Firmware, OS). As the industry is adopting shift-left approach (i.e. bringing software development and testing early in the product cycle), portability of the stimulus id becoming more relevant and this is the need of an hour. If you are interested in knowing more about what's Cadence offerings in this exciting space, satay tuned for more as I am planning to write more on this in coming weeks.

DVCon India is here on 15-16 September, 2016 and I am going to talk about it in one of the tutorial "Using Portable Stimulus for SoC Verification as Applied on Mobile, Networking, and Server Designs". You can also explore Accellera tutorial "How Portable Stimulus Addresses Key Verification, Test Reuse, and Portability Challenges" for more on portable stimulus.

Tuesday 27 October 2015

Einstein’s Five House Riddle - Solution in 'e'

Over the weekend, I came across this riddle and thought of solving it using ‘e’. Later on I challenged our team members over the WhatsApp and they took up the challenge. They came up with their version of solution in SV and SVA. I thought of sharing this solution to show how constraints solver engine can solve these kind of riddles. During the course of solution, I observed specifics about constraint solving technology of Specman. But before that, let’s look at the riddle. It is called with different names like Zebra Riddle or Einstein’s Riddle. This riddle has different version of stories and none of it has any solid proof to prove its origin. I’ve taken this riddle from one of the student’s home page from Stanford University website. Here is his version of the riddle which we tried to solve.

Let us assume that there are five houses of different colors next to each other on the same road. In each house lives a man of a different nationality. Every man has his different favorite drink, his different favorite brand of cigarettes, and keeps different pets of a particular kind.
  1. The Englishman lives in the red house.
  2. The Swede keeps dogs.
  3. The Dane drinks tea.
  4. The green house is just to the left of the white one.
  5. The owner of the green house drinks coffee.
  6. The Pall Mall smoker keeps birds.
  7. The owner of the yellow house smokes Dunhills.
  8. The man in the center house drinks milk.
  9. The Norwegian lives in the first house.
  10. The Blend smoker has a neighbor who keeps cats.
  11. The man who smokes Blue Masters drinks bier.
  12. The man who keeps horses lives next to the Dunhill smoker.
  13. The German smokes Prince.
  14. The Norwegian lives next to the blue house.
  15. The Blend smoker has a neighbor who drinks water.
The question to be answered is: Who keeps fish?
Now to solve this riddle, I started thinking these clues as constraints in ‘e’. Let’s go step by step how I approached this problem.

Here, we have 5 men who has different characteristics like they live in specific house number which has specific color. Each one has unique nationality, favorite drink, favorite brand of cigarettes and pet. So, based on these attributes, we can create a struct which can have these 5 attributes. These five attributes can be enumerated and ‘man’ struct can be defined as shown below.

type nationality_t : [english, swede, dane, norweigian, german];
type house_color_t : [red, green, yellow, blue, white];
type cigarette_t : [dunhill, blend, blue_masters, prince, pall_mall];
type pet_t : [dog, bird, cat, horse, fish];
type drink_t : [tea, coffee, milk, bier, water];

struct man {
   nationality : nationality_t;
   house_color : house_color_t;
   cigarette   : cigarette_t;
   pet         : pet_t;
   drink       : drink_t;
};

Here, I’ve not captured the house number as an attribute since I thought I can create list of struct which automatically will fix the house number of each object.
Now, I’ve categorized the clues in 2 category. One category which does not have house number order dependency and the other category which has house number dependency. Like, clue number 1 “The Englishman lives in red house” does not have any reference to the number of house, whereas clue no. 4 “The gree house is just to the left of the white one” has reference to the house number order. Since I have not captured the house number as an attribute, I’ve to split the clues into two different categories of constraints. One without the reference of the house number can be part of the original struct definition. One with the house number dependency can go to the place where list of struct is created so that we have notion of the house orders using index number of the list.

Let’s first capture the clues without house number ordering as global constraints as part of struct declaration. Here is what I’ve captured.

extend man {
      //Englishman lives in the red house
      keep nationality == english      => house_color == red;

      //Swede keeps dog
      keep nationality == swede        => pet         == dog;

      //dane drinks tea
      keep nationality == dane         => drink       == tea;

      //owner of the green house drinks coffee
      keep house_color == green        => drink       == coffee;

      //Pall Mall smoker keeps bird
      keep cigarette   == pall_mall    => pet         == bird;

      //owner of the yellow house smokes dunhill
      keep house_color == yellow       => cigarette   == dunhill;

      //Man who smokes Blue Masters drinks bier
      keep cigarette   == blue_masters => drink       == bier;

      //German smokes prince
      keep nationality == german       => cigarette   == prince;

};

Now let’s move on to declaring this struct as 5 objects as shown below.

extend sys
{
   men : list of man;
     keep men.size() == 5;
};

Now over here, we can capture the clues with house number ordering as different constraints involving different objects. Here, we’ll go by clues.

Let’s look at the first constraint which has dependency on the house number ordering. This clue is clue number 4 “The green house is just to the left of the white one”. This can be captured as shown below. Here I’ve assumed house numbers starts from 0 to 4 runs from left to right. Due to this assumption, house number 0 does not have any house on left side of it.

extend sys {
   //The green house is just to the left of the white one.
   keep men[1].house_color == white => men[0].house_color == green;
   keep men[2].house_color == white => men[1].house_color == green;
   keep men[3].house_color == white => men[2].house_color == green;
   keep men[4].house_color == white => men[3].house_color == green;
};

Next clue which has dependency on the house number ordering is clue number 8 “The man in the center house drinks milk”. This can be captured easily as we know the house number 2 is in center.

extend sys {
   //The man in the center house drinks milk.
   keep men[2].drink       == milk;
};

Next such clue is clue number 9 “The Norwegian lives in the first house”. Again it is simple to capture.

extend sys {
   //Norweigian lives in the first house
   keep men[0].nationality == norweigian;
};

Next such clue is clue number 10 “The Blend smoker has a neighbor who keeps cats”. This is how I captured it. It is obvious that man staying in house 0 or 4 have only one neighbor and rest men have 2 neighbour.

extend sys {
   //The Blend smoker has a neighbor who keeps cats.
   keep men[0].pet         == cat   => men[1].cigarette == blend;
   keep men[1].pet         == cat => men[0].cigarette == blend or men[2].cigarette == blend;
   keep men[2].pet         == cat => men[1].cigarette == blend or men[3].cigarette == blend;
   keep men[3].pet         == cat => men[2].cigarette == blend or men[4].cigarette == blend;
   keep men[4].pet         == cat => men[3].cigarette == blend;
};

Next clue is clue number 12 “The man who keeps horses lives next to the Dunhill smoker”. Again it is captured just like the above constraints for clue number 10.

extend sys {
   //The man who keeps horses lives next to the Dunhill smoker.
   keep men[0].pet         == horse => men[1].cigarette == dunhill;
   keep men[1].pet         == horse => men[0].cigarette == dunhill or men[2].cigarette == dunhill;
   keep men[2].pet         == horse => men[1].cigarette == dunhill or men[3].cigarette == dunhill;
   keep men[3].pet         == horse => men[2].cigarette == dunhill or men[4].cigarette == dunhill;
   keep men[4].pet         == horse => men[3].cigarette == dunhill;
};

Next clue in this category is clue number 14 “The Norwegian lives next to the blue house”.  Since it is already declared in clue number 9 “The Norwegian lives in the first house”, it is obvious that the blue house is house number 1. The Here is how I’ve captured it.

extend sys {
   //The Norwegian lives next to the blue house.
   keep men[1].house_color == blue;
};

If we do not want to capture it by interpreting it by ourselves and want tool to interpret for us, we can capture it just like we captured clue number 10 and 12. Here is how clue number 14 can be captured in alternate way.

extend sys {
   //The Norwegian lives next to the blue house.
   keep men[0].nationality == norweigian => men[1].house_color == blue;
   keep men[1].nationality == norweigian => men[0].house_color == blue or men[2].house_color == blue;
   keep men[2].nationality == norweigian => men[1].house_color == blue or men[3].house_color == blue;
   keep men[3].nationality == norweigian => men[2].house_color == blue or men[4].house_color == blue;
   keep men[4].nationality == norweigian => men[2].house_color == blue or men[4].house_color == blue;
};

Last clue that is clue number 15 “The Blend smoker has a neighbor who drinks water” is again captured the way we have captured clue number 10 and 12.

extend sys {
   //The Blend smoker has a neighbor who drinks water.
   keep men[0].cigarette   == blend => men[1].drink == water;
   keep men[1].cigarette   == blend => men[0].drink == water or men[2].drink == water;
   keep men[2].cigarette   == blend => men[1].drink == water or men[3].drink == water;
   keep men[3].cigarette   == blend => men[2].drink == water or men[4].drink == water;
   keep men[4].cigarette   == blend => men[3].drink == water;
};

Now we have captured all the clues as constraints. But when we load this file into Specman and try to solve it, we get constraint error. The reason is that we have not capture an important piece of information into this solution, that is each man has unique values of each 5 attributes. Now capturing this and informing Specman to generate uniq values for all 5 objects of man struct is bit tricky in ‘e’. In system Verilog, randc operator helps us capture it very easily, but in ‘e’ we have to do it with lot of codes. Here is how I’ve done it.

extend sys {
   //Let us assume that there are five houses of different colors next to each other on the same road. In each house lives a man of a different nationality. Every man has his favorite drink, his favorite brand of cigarettes, and keeps pets of a particular kind.
   //Unique nationality, house_color, cigarette, pet and drink
   keep men[0].nationality != men[1].nationality;
   keep men[0].nationality != men[2].nationality;
   keep men[0].nationality != men[3].nationality;
   keep men[0].nationality != men[4].nationality;
   keep men[0].house_color != men[1].house_color;
   keep men[0].house_color != men[2].house_color;
   keep men[0].house_color != men[3].house_color;
   keep men[0].house_color != men[4].house_color;
   keep men[0].cigarette   != men[1].cigarette;
   keep men[0].cigarette   != men[2].cigarette;
   keep men[0].cigarette   != men[3].cigarette;
   keep men[0].cigarette   != men[4].cigarette;
   keep men[0].pet         != men[1].pet;
   keep men[0].pet         != men[2].pet;
   keep men[0].pet         != men[3].pet;
   keep men[0].pet         != men[4].pet;
   keep men[0].drink       != men[1].drink;
   keep men[0].drink       != men[2].drink;
   keep men[0].drink       != men[3].drink;
   keep men[0].drink       != men[4].drink;

   keep men[1].nationality != men[2].nationality;
   keep men[1].nationality != men[3].nationality;
   keep men[1].nationality != men[4].nationality;
   keep men[1].house_color != men[2].house_color;
   keep men[1].house_color != men[3].house_color;
   keep men[1].house_color != men[4].house_color;
   keep men[1].cigarette   != men[2].cigarette;
   keep men[1].cigarette   != men[3].cigarette;
   keep men[1].cigarette   != men[4].cigarette;
   keep men[1].pet         != men[2].pet;
   keep men[1].pet         != men[3].pet;
   keep men[1].pet         != men[4].pet;
   keep men[1].drink       != men[2].drink;
   keep men[1].drink       != men[3].drink;
   keep men[1].drink       != men[4].drink;

   keep men[2].nationality != men[3].nationality;
   keep men[2].nationality != men[4].nationality;
   keep men[2].house_color != men[3].house_color;
   keep men[2].house_color != men[4].house_color;
   keep men[2].cigarette   != men[3].cigarette;
   keep men[2].cigarette   != men[4].cigarette;
   keep men[2].pet         != men[3].pet;
   keep men[2].pet         != men[4].pet;
   keep men[2].drink       != men[3].drink;
   keep men[2].drink       != men[4].drink;

   keep men[3].nationality != men[4].nationality;
   keep men[3].house_color != men[4].house_color;
   keep men[3].cigarette   != men[4].cigarette;
   keep men[3].pet         != men[4].pet;
   keep men[3].drink       != men[4].drink;
};

Now we are ready to solve it. Let’s add some debug and solution print statements.

extend {
   post_generate() is also
   {
      for i from 0 to 4 {
         outf ("\n");
         outf ("men[%d].nationality = %s\n", i, men[i].nationality);
         outf ("men[%d].house_color = %s\n", i, men[i].house_color);
         outf ("men[%d].cigarette   = %s\n", i, men[i].cigarette  );
         outf ("men[%d].pet         = %s\n", i, men[i].pet        );
         outf ("men[%d].drink       = %s\n", i, men[i].drink      );
      };

      outf ("===================================\n");
      for i from 0 to 4 {
         if (men[i].pet == fish)
         {
            outf ("\nAnswer to the quesiton 'Who keeps fish?' is %s who lives in house no %d with house color %s and smokes %s and drinks %s has %s as pet...\n", men[i].nationality, i, men[i].house_color, men[i].cigarette, men[i].drink, men[i].pet);
            outf ("\n");
            outf ("men[%d].nationality = %s\n", i, men[i].nationality);
            outf ("men[%d].house_color = %s\n", i, men[i].house_color);
            outf ("men[%d].cigarette   = %s\n", i, men[i].cigarette  );
            outf ("men[%d].pet         = %s\n", i, men[i].pet        );
            outf ("men[%d].drink       = %s\n", i, men[i].drink      );

         };
      };
   }; //post_generate() is also
}; //extend sys

After loading it into Specman and solving it, we get following output.

Loading einsteins_puzle.e ...

Doing setup ...
Generating the test with IntelliGen using seed 1966611192...

men[0].nationality = norweigian
men[0].house_color = yellow
men[0].cigarette   = dunhill
men[0].pet         = cat
men[0].drink       = water

men[1].nationality = dane
men[1].house_color = blue
men[1].cigarette   = blend
men[1].pet         = horse
men[1].drink       = tea

men[2].nationality = english
men[2].house_color = red
men[2].cigarette   = pall_mall
men[2].pet         = bird
men[2].drink       = milk

men[3].nationality = german
men[3].house_color = green
men[3].cigarette   = prince
men[3].pet         = fish
men[3].drink       = coffee

men[4].nationality = swede
men[4].house_color = white
men[4].cigarette   = blue_masters
men[4].pet         = dog
men[4].drink       = bier
===================================

Answer to the quesiton 'Who keeps fish?' is german who lives in house no 3 with house color green and smokes prince and drinks coffee has fish as pet...

men[3].nationality = german
men[3].house_color = green
men[3].cigarette   = prince
men[3].pet         = fish
men[3].drink       = coffee

Starting the test ...
Running the test ...
No actual running requested.
Checking the test ...
Checking is complete - 0 DUT errors, 0 DUT warnings.

So the answer is “German keeps the Fish”. Now try to solve this solution multiple times with random seed and you will get the same answer. That way we can be sure that the constraints we’ve captured are correct.

One last observation I’ve made is that this solution can be solved in IntelliGen Specman solver only. It does not work with PGen Specman solver engine. The reason is quiet obvious. PGen depends on the order of constraints declaration whereas IntelliGen is truly bi-directional constraints solver which is natural way of solving all the constraints at the same time.

Hope you like this riddle and the solution. I am sure this is not the optimal way of solving the problem, but it’s my lazy weekend stuff, so I wrote whatever came to my mind first without thinking much about optimizing it. If you have any suggestion to improve it, please do comment. If you have solutions in other forms (XLS, Perl, tcl, python, C, C++, VB, PHP or any other your favorite language) then please do post it in the comment.

Tuesday 13 January 2015

A Basic Course on "Functional Hardware Verification" from Udacity

If you are new to Functional Verification and interested to learn about it, Udacity has a self paced online course available titled as "Functional Hardware Verification". Here is the link for the course.

Instructor Axel Scherer is a senior engineer and manager at Cadence Design Systems. Instructor Hannes Fröhlich is Member of Product Expert Team in Functional Verification R&D group of Cadence Design Systems.

Thursday 26 September 2013

UVM-Multi Language solution from Cadence

Today's complex SoC level verification environment are based on advanced verification methodology standards. But these standards usually meant for single language domain. But fact of the life is that the these SoC level testbenches are made of mix of language domains like legacy Specman/e environment along with newly developed UVM-SV and SystemC or C++. Usually, when these scenario arrives, verification teams typically comes up with their own solutions which is mainly short term solution keeping existing needs in to the consideration. To develop this kind of inter operable solutions, there are quite a few challenges involved. Few are listed below.

1) Connecting different components of different domains
2) Transferring data/events from different domains
3) Synchronizations of major phases


Having said that, industry standard solution is the need of an hour. Cadence's Multi Language solution also known as UVM-ML addresses the above mentioned multi language integration challenges. UVM-ML is in existence for quite a sometime but least known. It was initially developed to take care of e-SV and e-SC integrations. UVM-ML is based on a back-plane library responsible for connecting two or more domains. Different language domain connect to back-plane library via language specific adapter layer. Currenty this solution supports integration of SystemC, UVM-SV, UVM-e.

Here is an example of how UVM-SV based producer can communicate to SystemC based consumer. You can refere more examples in the Incisiv installation area at $CDNS_HOME/tools/uvm/uvm_lib/uvm_ml/examples/sc/ and $CDNS_HOME/tools/uvm/uvm_lib/additions/uvm_ml_examples/ex_e_sv_sc_tlm/


//-----------------------------------------
//File : producer.sv
//System Verilog Producer
//-----------------------------------------
module topmodule;
import uvm_pkg::*;
`include "uvm_macros.svh"

//Declare basic packet which will be passed to SC consumer
class packet extends uvm_object;
   rand int data;
endclass

//Declare SV producer which will communicate with SC consumer through out port
class producer extends uvm_component;
   //Declare out port. This port will be connected to SC consumer
   uvm_analysis_port #(packet) out;

   //constructor
   function new(string name"producer", uvm_component parent=null);

      super.new(name, parent);
      out = new("out", this);

      //Register out port with UVM-ML
      ml_uvm::extnernal_if(out, "packet");

   endfunction : new

   //Generate dummy packet and pass it to out port
   task run_phase (uvm_phase phase);
      packet pkt = packet::type_id::create("pkt");

      phase.raise_objection(this);
      for (int i=0; i<5 br="" i="">      begin
         bit res = pkt.randomize();
         `uvm_info("TEST", $sformatf("producer.pkt.data='h%h", pkt.data), UVM_LOW)
         //Send the generated packet to out port
         out.write(pkt);
         #10;
      end

      phase.drop_objection(this);
   endtask : run_phase
endclass : producer
endmodule

//-----------------------------------------
//File : consumer.cpp
//SystemC Consumer
//-----------------------------------------
#include "ml_uvm.h";
#include "uvm.h";

using namespace tlm;
using namespace uvm;

class packet : public uvm_object
{
   UVM_OBJECT_UTILS(packet)
   public:
      int data;
};

UVM_OBJECT_REGISTER(packet)

//System C Consumer with templated with data type T
template <typename T>
class consumer : public uvm_compnent, public tlm_analysis_if
{
   public:
      sc_export > in;

   //constructor
   consumer(sc_module_name nm) : uvm_component(nm), in("in")
   {
      in(*this);
      //Register the export for mixed language communication
      ml_uvm::ml_uvm_register(&in);
   }

   UVM_COMPONENT_UTILS(consumer)

   //export implementation
   virtual void write( const T& t )
   {
      cout << sc_time_stamp() << " SystemC consumer receive a packet with data " << t.data << endl;
   }
};

UVM_COMPONENT_REGISTER_T(consumer, packet)

//System C testbench
SC_MODULE(TB)
{
   consumer c;
   SC_CTOR(TB) : c("consumer")
   {
      //Connect multi language port and export
      ml_uvm::ml_uvm_connect("producer.out", cons.in.name());
   }
};

Output Log:

UVM_INFO producer.sv(55) @ 0: producer [TEST] producer.pkt.data='h00000008
0 s SC consumer got a packet with data 8
UVM_INFO producer.sv(55) @ 10: producer [TEST] producer.pkt.data='h00000008
10 ns SC consumer got a packet with data 8
UVM_INFO producer.sv(55) @ 20: producer [TEST] producer.pkt.data='h00000003
20 ns SC consumer got a packet with data 3
UVM_INFO producer.sv(55) @ 30: producer [TEST] producer.pkt.data='h00000005
30 ns SC consumer got a packet with data 5
UVM_INFO producer.sv(55) @ 40: producer [TEST] producer.pkt.data='h00000006
40 ns SC consumer got a packet with data 6

Wednesday 25 September 2013

UVM bidirectional port example using uvm_tlm_b_initiator_socket and uvm_tlm_b_target_socket ports

In your System Verilog UVM environment, sometimes you want few components to communicate to each other in both ways. For example, a master component can request a memory component to pass over the memory content of specific address. So, how can you impelent such a communication channel between two components? Well, there are multiple ways. You can have two one-directional ports like uvm_get_ports and uvm_put_ports. Initator can send the address through output port and receives the data from another input port. Or it can be implemented through mailbox. The other way to implement is to use uvm_tlm_b_initiator_socket and uvm_tlm_b_target_socket ports. Using this port, you initiator can send a packet with specific address. Same packet can be updated with data by the memory element and later on can be read by initiator. Thus, by using single port and packet, two elements can communicate with each other in bidirectional way. Since this is a blocking port, request information and response information should come in a single shot. Here is an example to illustrate it. You can download the full examples by clicking uvm_bidirectional_port_example.tar.gz


//Memory cell definition
class mem_cell extend umv_sequence_item;
   rand bit [1:0]  addr;
   rand bit [31:0] data;
endclass: mem_cell


//Memory component definition
class memory extends uvm_component;
   //Define local memory
   local bit [31:0] mem [3:0];

   function new(string name = "memory", uvm_component parent = null);
      super.new(name, parent);
      socket = new("socket", this);
      //Initialize the local memory
      mem[0] = 32'h00000000;
      mem[1] = 32'h11111111;
      mem[2] = 32'h22222222;
      mem[3] = 32'h33333333;
   endfunction : new

   //Declare blocking target socket port
   uvm_tlm_b_target_socket #(memory, mem_cell) socket;

   //Need to write implementation of b_transport() task
   task b_transport(mem_cell req, uvm_tlm_time delay);
      //Based on the req.addr, update the req.data which will be later read back by initiator
      case (req.addr)
         2'h0 : begin req.data = mem[0]; end
         2'h1 : begin req.data = mem[0]; end
         2'h2 : begin req.data = mem[0]; end
         2'h3 : begin req.data = mem[0]; end
      endcase

   endtask : b_transport

endclasss : memory


class initiator extend uvm_component'
  
   //Declare blocking transport socket port
   uvm_tlm_b_transport_socket #(mem_cell) socket;

   virtual task run_phase(uvm_phase phase);
     
      //instantiate a mem_cell
      mem_cell req;

      uvm_tlm_time delay = new;

      phase.raise_objection(this);

      //Create mem_cell
      req = mem_cell::type_id::create("req", , get_full_name());

      //set address and sent it across to memory component
      req.addr = 2'h0;
      socket.b_transport(req, delay);
      //memory element has now updated the req.data accordingly. it can be used now
      `uvm_info("TEST", $sformat(f("For req.add='h%h, received req.data='h%h", req.addr, req.data), UVM_LOW);

      //same object can be reused.
      //set address and sent it across to memory component
      req.addr = 2'h1;
      socket.b_transport(req, delay);
      //memory element has now updated the req.data accordingly. it can be used now
      `uvm_info("TEST", $sformatf("For req.add='h%h, received req.data='h%h", req.addr, req.data), UVM_LOW);

      phase.drop_objection(this);
   endtask : run_phase
  
endclass : initator


class env extends uvm_component;
   initiator init;
   memory   mem;

   //Connect two ports
   function void connect_phase(uvm_phase phase)
      init.socket.connect(mem.socket);
   endfunction

endclass : env


Log Output:

UVM_INFO initiator.sv(22) @ 10: env.init [TESTCASE] For req.add='h0 received  req.data='h00000000
UVM_INFO initiator.sv(26) @ 20: env.init [TESTCASE] For req.add='h1 received  req.data='h11111111



You can refer UVM User Guide 1.1 from http://www.accellera.org/community/uvm for more details about these ports.

Wednesday 29 February 2012

Automatic Coverage Weight Calculator Utility

If you want to get Specman coverage number in linear fashion (i.e. coverage numbers = (total buckets hit) / (total buckets in valid space) ), then you need to set the weight of individual items and coverage groups properly. Updating the code for proper weights to each item and cover group is tedious and error prone task. Maintaining the code is also a problem. What if you can do it automatically by using Coverage API? I hope people, who are looking such kind of solution will benefit from this post.

Specman overall coverage grade is calculated in hierarchical manner by default. It means that coverage is calculated from leaf level to the top level in following manner. I've assumed that weight is not set manually and default weight value 1 is applied here.

  1. Individual item grade is calculated by dividing buckets hit by total bucket for that item.
  2. Individual cover group grade is calculated by dividing total of all items grade by total number of items
  3. Overall coverage grade is calculated by dividing total of all cover groups graade by total number of cover groups

Take a look at the following example code.

<'
extend sys
{
config : config_s;
};
struct config_s
{
a : uint(bits: 3);
b : uint(bits: 3);
c : bool;
event cov1_e;
event cov2_e;
cover cov1_e is
{
item a using ignore = (a in [0, 1, 7, 8]);
item b using ignore = (b in [0, 1, 7, 8]);
cross a, b using
ignore =
(a in [2] and b in [4]) or
(a in [4] and b in [2] );
};
cover cov2_e is
{
item a using ignore = (a in [0, 1, 7, 8]);
item c using ignore = (c == TRUE);
cross a, c;
};
keep soft a == select
{
40 : 2;
10 : 3;
40 : 4;
10 : 5;
10 : 6;
};
keep soft b == select
{
40 : 2;
10 : 3;
40 : 4;
10 : 5;
10 : 6;
};
keep soft c == TRUE;
run() is also
{
for i from 1 to 10
{
gen a; gen b; gen c;
emit cov1_e;
emit cov2_e;
outf ("a=%d b=%d c=%s\n", a, b, c);
};
};
};
'>


When you run the above code with Intelligen with seed 1, Specman generates following combinations of a, b and c.

a=4 b=4 c=TRUE
a=4 b=4 c=TRUE
a=4 b=3 c=TRUE
a=2 b=2 c=TRUE
a=6 b=2 c=TRUE
a=2 b=4 c=TRUE
a=2 b=2 c=TRUE
a=2 b=4 c=TRUE
a=2 b=2 c=TRUE
a=4 b=6 c=TRUE

Let's analyze the coverage now.

ItemHitValid SpaceGrade
a3560.00% (hit/valid space)
b4580.00% (hit/valid space)
cross_a_b52321.74% (hit/valid space)

So, cov1_e cover group's grade = (grade of item a*weight + grade of item b*weight + grade of item cross_a_b*weight)/3 = 1.6174/3 = 53.91 %

ItemHitValid SpaceGrade
a3560.00% (hit/valid space)
c11100.00% (hit/valid space)
cross_a_c3560.00% (hit/valid space)

So, cov2_e cover group's grade = (grade of item a + grade of item c + grade of item cross_a_c)/3 = 2.20/3 = 73.33 %

So, overall grade = (cov1_e grade + cov2_grade)/2 = 1.2725/2 = 63.62 %

But, if you need the linear coverage, it should be
(Total number of hit buckets) / (Total number of buckets in valid space) = ((3+4+5) + (3+1+3))/((5+5+23) + (5+1+5)) = 43.18 %

This can be achieved by adjusting the weitage of individual item and cover group. If you set the weight of each item equal to the total number of valid buckets, linear coverage is achieved. Let's see how it works.

ItemHitValid SpaceWeightGrade
a35560.00 % (hit/valid space)
b45580.00 % (hit/valid space)
cross_a_b5232321.74 % (hit/valid space)

So, cov1_e cover group's grade = (grade of item a*weight + grade of item b*weight + grade of item cross_a_b*weight)/(weight of a + weight of b + weight of cross_a_b) = (3 + 4 + 5) / (5 + 5 + 23) = 36.36 %

ItemHitValid SpaceWeightGrade
a35560.00 % (hit/valid space)
c11180.00 % (hit/valid space)
cross_a_c35521.74 % (hit/valid space)

So, cov2_e cover group's grade = (grade of item a*weight + grade of item c*weight + grade of item cross_a_c*weight)/(weight of a + weight of c + weight of cross_a_c) = (3 + 1 + 3) / (5 + 1 + 5) = 63.64 %

So, overall grade = (cov1_e grade*weight + cov2_grade*weight)/(weight of cov1_e + weight of cov2_e) = (12+7)/(33+11) = 43.18%

This number looks linear as we can see we've calculated the coverage based on the number of buckets hit divided by total number of bucket for whole of the coverage space. So, setting the weight of the items based its number of buckets, it gives us the correct result.

Setting the weight manually is tedious process as you need to check the exact number of valid buckets of each item and coverage groups by keeping ignores and ranges into the mind. And even you do that, if you want to change the coverage model again, you might need to calculate the valid space again which is tedious and error prone. So, why not automate it using Coverage API calls?

You can use this small utility which uses coveage API to change the weight of all the items and cover group. It gives you the linear overall grade which is total number of buckets hit in valid coverage space divided by total number of buckets in valid coverage space. This utility can also be used in the post processing of coverage database. This utility uses coverage API to recursively parse the whole coverage space of a given unit which can be sys. It counts the total number of buckets of each item and coverage group and uses covers.set_weight() to set the weight of items and cover groups recursively.

You can use it after loading the linear_coverage.e. It defines a specman command using "define as" macro. Example is given below

Specman>load linear_coverage.e
Specman>adjust weight sys (for whole coverage space)
Specman>adjust weight config_s (for coverage space defined in config_s struct)


I hope this will help you. Your comments and thoughts are welcome.

-------------------------------------------
liner_coverage.e
-------------------------------------------
<'
define "adjust weight " as {
sys.adjust_weight = new;
sys.adjust_weight.main("<1>");
};

struct cover_info
{
item_name : string;
num_of_buckets: uint;
weight : uint;
};

struct bucket_cover_struct like user_cover_struct
{
collect: bool; // flag to skip specific item
cover_info : list (key: item_name) of cover_info;

main(items: string) is
{
if scan_cover(items) == 0 {
error("Adjust Weight Error: no cover items matching ", items," exist");
};
};

scan_bucket() is
{
if ((collect) and (cross_level == sub_items.size()-1)) then
{
var full_item_name := appendf ("%s.%s.%s", struct_name, group_name, item_name);
if (status in [hole, normal])
{
if cover_info.key_exists(full_item_name)
{
cover_info.first(.item_name == full_item_name).num_of_buckets = cover_info.first(.item_name == full_item_name).num_of_buckets + 1;
}
else
{
var temp_cov_info : cover_info = new;
temp_cov_info.item_name = full_item_name;
temp_cov_info.num_of_buckets = 1;
cover_info.add(temp_cov_info);
};
};
};
};

// Output a line of text for item:
end_item() is
{
if collect then
{
var full_item_name := appendf ("%s.%s.%s", struct_name, group_name, item_name);
var num_of_buckets := cover_info.first(.item_name == full_item_name).num_of_buckets;
outf ("%s - %d (weight=%d)\n", full_item_name, num_of_buckets, item_weight);
covers.set_weight(full_item_name, num_of_buckets, FALSE);
};
};

start_group() is
{
collect = (struct_name != "session");//skip session struct
if collect then
{
outf ("\n------------------------------------\n");
};
};

end_group() is
{
if collect then
{
var group_total : uint;
for each (cov_info) in (cover_info)
{
group_total = group_total + cov_info.num_of_buckets;
};
outf ("------------------------------------\n");
outf ("Group and Total Buckets ---> %s - %d (weight=%d)\n", group_name, group_total, group_weight);
covers.set_weight(appendf("%s.%s", struct_name, group_name), group_total, FALSE);
outf ("------------------------------------\n");
cover_info.clear();
};
};
};

// Define an instance of bucket_cover_struct:
extend sys
{
!adjust_weight: bucket_cover_struct;
init() is also { adjust_weight = new; };
setup() is also { set_config(cover, show_sub_holes, TRUE); };
};

'>