ASIC Verification: Specman Tutorial
Showing posts with label Specman Tutorial. Show all posts
Showing posts with label Specman Tutorial. Show all posts

Thursday, March 13, 2008

Hands on Training in Specman - 2

Today is the last day of our Specman Tutorial. The previous section explains the complete verification process of a simple USB packet ID decoder design. Topics discussed are design specification, verification components, verification plan, and test plan. This section completes the example with an explanation of the actual 'e' code for each component required for the verification of the PID Decoder design.

Defining the packet data item

This section shows the 'e' code for the packet data item.

//==========================================================================

<'
-- Define an enumerated type packet_id.
-- The token type is either IN, OUT or SETUP

type
packet_id : [IN, OUT, SETUP, ERR_PID];

-- Define a struct called hub_packet

struct hub_packet
{

-- Define a field syn; The value of sync field is constant

%syn : uint(bits:32);
-- SYNC pattern is 32'h8000_000;
keep soft syn == 32'h8000_0000;
--Define a field pid
%pid : packet_id;

pid_type : uint(bits:8);
-- Endpoint Number
%ep_no : uint(bits:4);
keep soft ep_no in [1..15]; -- Device address
%dev_add : uint(bits:7);

keep soft dev_add == 10;
%data : list of uint(bits:16); -- Packet length pid=8bits; dev_add=7bits; ep_nu=4bits;
keep pid == IN => pid_type == 8'h69;

keep pid == OUT => pid_type == 8'hE1;

keep pid == SETUP => pid_type == 8'h2D;
keep pid == ERR_PID => pid_type == 8'h11;
// Pid error
%crc_5 : uint(bits:5);

keep crc_5 == 5'b10101;

// Keep the CRC fixed as of now
%eop : byte;
keep soft eop == 8'hFE;

event coverage_chk;

};

extend sys
{
packets : list of hub_packet;
keep packets.size() == 2;
};
// End of extend
'>
//==========================================================================

Defining the Transaction Generator

This section shows the 'e' code for the Transaction generator.
//==========================================================================
-- Transaction Generator
//==========================================================================

<'
import hub_packet;
import hub_driver;
unit hub_txgen
{
// Instantiate the struct hub_packet, the static verification object
hub_object : hub_packet;

// Instantiate the unit hub_driver, the dynamic verification object
hub_driver : hub_driver is instance;
keep hub_driver.hdl_path() == "~/pid_dec_TB";

// Define a positive edge of the clock
event posedge_clk is rise('pid_dec_TB.clock_in') @sim;

// Define a TCM, gen_transaction which will start another TCM
gen_transaction()@ posedge_clk is
{
// Start to generate and drive the USB packet
start hub_driver.gen_and_drive();
};
// End of gen_transaction
};
// End of unit
'>
//==========================================================================

Defining the Hub driver

This section shows the 'e' code for the hub driver.
//==========================================================================
// Hub Driver
//==========================================================================
<'
import hub_packet;
-- import the hub_packet here

unit hub_driver
{
!current_packet : hub_packet;
!delay : uint;
keep soft delay in [1..10];
no_of_packets : uint;

event rise_clk is rise ('clock_in') @sim;
event fall_reset is fall ('reset_in') @sim;
event sync_on_bus;
event pid_on_bus;
event packet_ended;

sent_signals() @rise_clk is
{
'rx_last_byte' = 0;
'rx_bs_err' = 0;
'rx_valid' = 0;

wait @sync_on_bus;
'rx_valid' = 1;

wait @pid_on_bus;
'rx_last_byte' = 1;
'rx_valid' = 1;

wait;
'rx_last_byte' = 0;
'rx_bs_err' = 0;
'rx_valid' = 0;
};
// End of sent_signals() method

gen_and_drive () @ rise_clk is
{
for i from 0 to no_of_packets-1
{
gen delay;
wait [delay];
gen current_packet;

wait true ('reset_in' == 0);
drive_packet (current_packet);
};
-- end of for loop
};
-- end of generate_and_drive method

drive_packet (packet : hub_packet) @rise_clk is
{
var packet_packed : list of uint(bits:16);
emit packet.coverage_chk;

packet_packed = pack (packing.low, packet.syn[15:0], packet.syn[31:16], packet.ep_no[0:0], packet.dev_add[6:0], packet.pid_type, packet.crc_5, packet.ep_no[3:1],packet.eop);

for each
(packet_16bit) in packet_packed
{
'data_in' = packet_16bit;
start sent_signals();
wait cycle;

if('data_in' == 16'h8000)
{
emit sync_on_bus;
};

if('data_in'[15:8] == 8'h69 || 'data_in'[15:8] == 8'hE1 || 'data_in'[15:8] == 8'h2D || 'data_in'[15:8] == 8'h11)
{
emit pid_on_bus;
};
};
emit packet_ended;
};
-- end drive_packet
};
'>
//==========================================================================

Defining the hub output monitor

This section shows the 'e' code for the hub's output monitor.
//==========================================================================
// Hub output monitor
//==========================================================================
<'
import hub_env;
unit hub_op_mon
{
!rcv_packet : hub_packet;
rcv_delay : uint;

keep soft rcv_delay == 10;

event posedge_clk is rise ('clock_in') @sim;
event in_token_rcvd is rise ('in_token_out')@ posedge_clk;
event out_token_rcvd is rise ('out_token_out')@ posedge_clk;
event setup_token_rcvd is rise ('setup_token_out')@ posedge_clk;

output_mon() @ posedge_clk is
{
while(TRUE)
{
wait cycle;
if(('in_token_out' == 1 ) || ('out_token_out' == 1) || ('setup_token_out' == 1) || ('pid_error_out' == 1) )
{
if('in_token_out' == 1) then
{
out("IN TOKEN RECEIVED SUCCESSFULLY at ", sys.time);
}
else if ('out_token_out' == 1) then
{
out("OUT TOKEN RECEIVED SUCCESSFULLY at ", sys.time);
}
else if('setup_token_out') then
{
out("SETUP TOKEN RECEIVED SUCCESSFULLY at ", sys.time);
}
else
{
out("PID Error is Occured at ", sys.time); };};};};};
'>
//==========================================================================

Defining the coverage item

This section shows the 'e' code for the coverage item.
//==========================================================================
// Coverage
//==========================================================================
<'
import
hub_env;
import
hub_packet;

extend
hub_packet
{
cover coverage_chk using
count_only,
radix = HEX,
weight = 10 is
{
item pid;
item
ep_no;
cross
pid, ep_no;
};
// End of cover
};
// End of extend
'>
//==========================================================================

Defining the Top level hierarchy


This section shows the 'e' code for the top level verification hierarchy.
//==========================================================================
-- Top level of 'e' verification hierarchy
//==========================================================================

<'
import
hub_packet;
import hub_driver;
import tx_gen;
import hub_cover;
import hub_output_mon;

unit hub_env
{
-- Instantiate hub_tx_gen
hub_tx_gen : hub_txgen is instance;

// Instantiate hub_driver

hub_driver : hub_driver is instance;
keep hub_driver.hdl_path() == "~/pid_dec_TB";

-- Instantiate hub_output_monitor
hub_op_monitor : hub_op_mon is instance;
keep hub_op_monitor.hdl_path() == "~/pid_dec_TB";

event posedge_clk is rise('pid_dec_TB.clock_in') @sim;

// Define a TCM, start_tb which will start all the TCMs
start_tb() @ posedge_clk is
{
start hub_op_monitor.output_mon();
// start output mon
start hub_tx_gen.gen_transaction();
// start method

wait[1200];
stop_run();
// call method
};
// End of start_tb()
};
// End of unit

-- Create an instance of hub_env object in top level
extend sys
{
hub_env : hub_env is instance;
run() is also
{
start hub_env.start_tb();
};
// End of run()
};
// End of extend

extend sys
{
setup() is also
{
set_config( print, scale, ps);
set_config( cover, mode, on);
};
// End of setup()
};
// End of extend

'>
//==========================================================================

Test case 1

This section shows the 'e' code for the test case.
//==========================================================================
//Test

//==========================================================================
<'
import
hub_env;

extend hub_packet
{


keep soft dev_add in [1..10];

keep soft pid == select
{
20: [IN];
30: [OUT];
20: [SETUP];
20: [ERR_PID];
};

extend sys
{
keep hub_env.hub_driver.no_of_packets == 5 ;
};
'>
//==========================================================================

Procedure to run the test case

Create the simulation directory
mkdir sim

Go to the simulation directory
cd sim

Create the work library
vlib work


Create the stubs file in hdl directory
$SPECMAN_HOME -command "write stubs -verilog ../hdl/specman.v"


Compile the stubs and design files
vlog -work ./work ../hdl/specman.v
vlog -wrok ./work ../hdl/pid_dec.v
vlog -wrok ./work ../hdl/pid_dec_TB.v

Run the simulation
Invoking Specman and Modelsim
$SPECMAN_HOME../sn/bin/specview -p "load ../e/test1; test" vsim -pli ../../libmti_sn_boot.so -lib ./work pid_dec_TB &

//==========================================================================


Wednesday, March 12, 2008

Hands on Training in Specman - 1

In the previous sections, we focussed mainly on introductory and syntax aspects of 'e'. However, it is important for a verification engineer to understand how to build a complete verification system with 'e'. This section discusses the complete verification process of a simple USB packet's pid decoder. This section also discusses the specification of USB's packet ID decoder, verification components, verification plan and test plan.

DUT Specification

This section describes the complete DUT specification of a simple USB packet ID decoder. Figure shows the input/output specification of the DUT. The PID Decoder accepts data packet on a single 8-bit input port called 'data_in' and in combination with the transceiver signals - rxvalid, rxlastbyte and rxbserror - it decodes the packet ID.

Data Packet Description


A token packet is a sequence of bytes with first 4 bytes containing sync field, the next set of bytes containing data and the last byte containing CRC. The packet format has the following characteristics.
  • The 'SYNC' consists of 32 bits - 32'h8000_0000 ( The LSB is transmitted first)
  • PID is 8 bits. The first 4 bits are 'type' field and the next 4 bits are compliment of 'type'
  • Address is 7 bits.
  • Endpoint is 4 bits.
  • CRC5 is 5 bits
All packets begin with a synchronization (SYNC) field. A SYNC from an initial transmitter is defined to be 32 bits for high-speed. SYNC serves only as a synchronization mechanism. The last two bits in the SYNC field are a marker that is used to identify the end of the SYNC field and the start of the PID.

A packet identifier (PID) immediately follows the SYNC field of every USB packet. A PID consists of a four-bit packet type field followed by a four-bit check field. The PID indicates the type of packet. The four-bit check field of the PID ensures reliable decoding of the PID so that the remainder of the packet is interpreted correctly. The PID check field is generated by performing a one' s complement of the packet type field. A PID error exists if the four PID check bits are not complements of their respective packet identifier bits.

DUT Input Protocol

Figure shows the input protocol of the DUT. The characteristics of the DUT input protocol are as follows:

  • All input signals are active high and are synchronized to the rising edge of the clock. Therefore, any signal that is an input to the DUT is driven at the rising edge of the clock.
  • The rx_valid signal has to be asserted on the same clock after the sync packet is transmitted.
  • The rx_last_byte signal has to be asserted if the EOP is on the higher order byte and valid data is on the lower order byte.

DUT Output Protocol

Figure shows the output protocol of DUT. The characteristics of the DUT output protocol are as follows:

  • All output signals are active high and are synchronized to the rising edge of the clock. Therefore, any signal that is an output from the DUT is sampled at the rising edge of the clock.
  • The Decoder asserts the 'in_token' when it detects '69' in the 'PID' field & the edge of rx_valid signal.
  • The Decoder asserts the 'out_token' when it detects 'E1' in the 'PID' field & the edge of rx_valid signal.
  • The Decoder asserts the 'setup_token' when it detects '2D' in the 'PID' field & the edge of rx_valid signal.
  • The Decoder asserts the 'pid_err_out' when 'type' field and its compliment doesn't match & the edge of rx_valid signal.

DUT HDL Source Code

Example shows the verilog source code used to describe the DUT PID Decoder.


//***************************************************************************************************************
// Block : pid_dec
// Description : This block decodes the pid and asserts the corresponding
// tokens and also asserts the pid error if the last four bits
// are not the compliment of the first four bits
//***************************************************************************************************************
module pid_dec
(

// Inputs

clock_in,
reset_in,

rx_valid,
rx_last_byte,
rx_bs_err,
data_in,

// Outputs

in_token_out,
out_token_out,
setup_token_out,
pid_error_out,
invalid_token_out
);


// Inputs

input clock_in;
input reset_in;
input[15:0] data_in;

input rx_valid;
input rx_last_byte;
input rx_bs_err;

// Outputs

output in_token_out;
output out_token_out;
output setup_token_out;
output invalid_token_out;
output pid_error_out;

reg in_token_out;
reg out_token_out;
reg setup_token_out;
reg invalid_token_out;
reg pid_error_out;


// Signal Declarations

reg next_in;
reg next_out;
reg next_setup;
reg next_invalid;
reg next_pid_err;
reg rx_valid_r;

wire rx_valid_edge;


assign rx_valid_edge = rx_valid & ~rx_valid_r;

// Signal Assignments .


//***************************************************************************************************************
// Process for PID Decode
//***************************************************************************************************************
always @(data_in)
begin : hpdc_main
next_in = 1'b0 ;
next_out = 1'b0 ;
next_setup = 1'b0 ;
next_invalid = 1'b0 ;
next_pid_err = 1'b0 ;

// Here last four bits are taken and compared with its
// compliment inorder to verify whether the pid and
// its complement are proper

case (data_in[11:8])
4'b0001 :
// Decoding OUT PID
begin
if ((data_in[15:12] == ~(data_in[11:8])) && rx_valid_edge)
begin
next_out = 1'b1 ;
next_pid_err = 1'b0 ;
end
else if ((data_in[15:12] != ~(data_in[11:8])) && rx_valid_edge)
begin
next_pid_err = 1'b1 ;
end
else
next_pid_err = 1'b0 ;

end

4'b1001 :
// Decoding IN PID
begin
if ((data_in[15:12] == ~(data_in[11:8])) && rx_valid_edge)
begin
next_in = 1'b1 ;
next_pid_err = 1'b0 ;
end
else if ((data_in[15:12] != ~(data_in[11:8])) && rx_valid_edge)
begin
next_pid_err = 1'b1 ;
end
else
next_pid_err = 1'b0 ;
end
4'b1101 :
// Decoding SETUP PID
begin
if ((data_in[15:12] == ~(data_in[11:8])) && rx_valid_edge)
begin
next_setup = 1'b1 ;
next_pid_err = 1'b0 ;
end
else if ((data_in[15:12] != ~(data_in[11:8])) && rx_valid_edge)
begin
next_pid_err = 1'b1 ;
end
else
next_pid_err = 1'b0 ;
end

// Incase the incoming bit stream contains anything other than
// the expected value then this default state is entered

default :
begin
next_invalid = 1'b1 ;
end
endcase
end


//***************************************************************************************************************
// Process for latching the Decoded PID
//***************************************************************************************************************
always @(negedge reset_in or posedge clock_in)
begin : hpdc_regs
if (reset_in)
begin
in_token_out <= 1'b0 ;
out_token_out <= 1'b0 ;
setup_token_out <= 1'b0 ;
invalid_token_out <= 1'b0 ;
pid_error_out <= 1'b0 ;
rx_valid_r <= 1'b0;
end
else
begin
begin
in_token_out <= next_in ;
out_token_out <= next_out ;
setup_token_out <= next_setup ;
invalid_token_out <= next_invalid ;
pid_error_out <= next_pid_err ;
rx_valid_r <= rx_valid;
end
end
end
endmodule

Test bench

module pid_dec_TB;

reg clock_in;
reg reset_in;


reg [15:0] data_in;

reg rx_valid;
reg rx_last_byte;
reg rx_bs_err;
wire in_token_out;
wire out_token_out;
wire setup_token_out;
wire pid_error_out;
wire invalid_token_out;


pid_dec inst_pid_dec
(

.clock_in(clock_in),
.reset_in(reset_in),
.data_in(data_in),
.rx_valid(rx_valid),
.rx_last_byte(rx_last_byte),
.rx_bs_err(rx_bs_err),
.in_token_out(in_token_out),
.out_token_out(out_token_out),
.setup_token_out(setup_token_out),
.pid_error_out(pid_error_out),
.invalid_token_out(invalid_token_out)
);


specman sn();

// clock_in generation logic

initial
begin
clock_in = 1'b0;
reset_in = 1'b1;
forever
#5 clock_in = ~clock_in;

end

initial
begin
#100 reset_in = 1'b0;
end

endmodule // pid_dec_TB


Verification Plan

A verification plan is required to describe what is to be verified and how it will be verified. It also should address 3 aspects of verification.

  1. Coverage measuresment
  2. Stimulus Generation
  3. Response Checking

The verification will be developed in 'e'.

Test Plan

Tests are derived from test plan. A test plan contains all tests that are to be run to verify the DUT. A test plan contains an exhaustive list of items to be tested. In 'e' tests are simply the extensions of existing structs and unit definitions.

Test1

Create the packets with a certain probability distribution.


Monday, March 10, 2008

Execution Flow

In the first chapter of the specman tutorial, we saw the 'e' verification components. All the 'e' verification components - Generator, Driver, Collector, Data checker and Monitor - were directly instantiated under 'sys'. As the verification environment that is designed for a module level will be needed at the chip level, this is going to be a problematic. All the 'e' verification components discussed in this tutorial should be automatically transported to a different level of verification. Therefore, instead of directly instantiating all these components under 'sys', it is better to create an intermediate level of hierarchy called 'env' under 'sys'. All the verification components are instantiated under 'env'. Thus one can transport 'env' to any level of hierarchy without having to transport each individual components.

Execution Flow

To understand how 'e' code are executed, it is necessary to understand the execution phases. Upon issuance of 'test' command, Specman Elite goes through five execution phases. Each execution phase is called in a specific order and is represented by a pre-defined method in Specman Elite.

Test Phases

Initialization - General Preparations and Configurations

The initialization phase calls the sys.setup() method. Any 'e' simulator configuration options are specified by the extension of sys.setup() method in this phase. For example, the seed value for the generation is set and the coverage mode is also set by the extension of this method call.

extend sys {
setup() is also {
set_config(cover, mode, on); // Turn On the coverage
set_config(print, scale, ns); // To scale the time portion of the message to ns
};
};

Pre Run Generation - Memory allocation, Instantiation and Generation of data elements

  • The Pre-Run generation phase calls the init() and pre_generate() methods for every struct instance under 'sys'.
  • Then the generation of data elements occurs.
  • The Pre-Run generation phase calls post_generate() method for every struct instance under 'sys'.

Each of these methods can be extended to include the method calls. Any computations that are done - after the generation of data elements - must be done by the extension of post_generate() method.

Simulation Run - Actual Simulation Occurrence

Any TCMs are executed in this phase. The pre-defined run() method is called for every struct and unit in the 'sys' hierarchy. One can initiate the simulation time activity by starting a TCM from the run() method of a struct or unit. For any simulation, at least one main TCM must be started from run() method of a top level unit. Since, run() method is not a TCM, you can't call a TCM from run(), instead you can only start a TCM. This main TCM then starts/calls other TCMs to control the execution.

Post Run Check - Preparation for Checking & Performs Checking

Finalization - Simulation Clean up, Coverage and Reporting

Simulator Interface

In this section, we are going to see how the specman elite 'e' simulator is interfaced with HDL simulator. Following are some of the features of specman elite 'e' simulator.

  • Specman knows and understands the current simulation time
  • Specman can read the values of the HDL signals and assign these signals with new values
  • Specman can wait for a specific signal to get a specific value when a specific event occurs

Any verification tool must be able to write data to DUT inputs or to internal memories, read data from DUT outputs or from internal signals such as state machine registers and respond to specific events that happen on the DUT interfaces.

Compiling 'e' files

First step for any test bench is to make sure that your e code is compilable, to check this, you need to execute command as below:

specman -c "load test1.e"

This will basically check your code of syntax, but will not check any null objects (no run time errors are checked).

Linking Specman Elite with Modelsim

Once we have checked syntax of 'e' code, we can proceed to linking. In order to run Specman Elite with ModelSim, you must load the ModelSim Specman Elite boot library into the simulator at run time. The boot library is available at:

$SPECMAN_HOME/platform/libmti_sn_boot.so

To load libmti_sn_boot.ext into the ModelSim simulator, type

vsim -c -pli $SPECMAN_HOME/platform/libmti_sn_boot.so


Connecting to HDL signals

Here are some guidelines that would help you to minimize the infiltration of HDL signal names into your e code:

(a) Always use relative HDL signal path names - changes in the DUT structure and hierarchy, are by far the most common case of signal names change. Therefore, you should never use a full path to access a specific signal. The use of e units would help you access HDL signals using relative path names.

(b) The actual name of an HDL signal should not be mentioned more than once in your code, at some point where you define this signal. In any other place you should access this signal using some sort of e element - a defined name, a string, or better yet, a port.

(c) Concentrate all specific references to HDL names in a single file.

Writing e code that complies with these three principles was greatly facilitated after ports were introduced into the language.

Here is an example that will clarify how this is done in e.
====================================================================
<'
unit hub_signal_map
{
// An event port emits an event whenever a change in the specified
// direction occurs on the signal that is connected to it.
clk_in : in event_port of bit is instance;
keep bind(clk_in, external);

// reset_n active low reset
reset_in : in simple_port of bit is instance;
keep bind(reset_in, external);

// Data input to the DUT - Bus
data_in : out simple_port of uint(bits :16) is instance;
keep bind(data_in, external);

nak_handshake : out simple_port of bit is instance;
keep bind(nak_handshake, external);

rx_valid : out simple_port of bit is instance;
keep bind(rx_valid, external);

rx_last_byte : out simple_port of bit is instance;
keep bind(rx_last_byte, external);

rx_bs_err : out simple_port of bit is instance;
keep bind(rx_bs_err, external);

// IN token from the DUT
in_token_out : in simple_port of bit is instance;
keep bind(in_token_out, external);

// OUT token from the DUT
out_token_out : in simple_port of bit is instance;
keep bind(out_token_out, external);

// SETUP token from the DUT
setup_token_out : in simple_port of bit is instance;
keep bind(setup_token_out, external);

// Invalid token from the DUT
invalid_token_out : in simple_port of bit is instance;
keep bind(invalid_token_out, external);

// pid_err token from the DUT
pid_error_out : in simple_port of bit is instance;
keep bind(pid_error_out, external);
};


unit hub_env
{
-- Instantiate hub_driver
hub_driver : hub_driver is instance;
keep hub_driver.hdl_path() == "~/pid_dec_TB";
-- Create hub_recevier
hub_receiver : hub_receiver is instance;
keep hub_receiver.hdl_path() == "~/pid_dec_TB";

hub_signal_map : hub_signal_map is instance;
// The port data of hub_signal_map will be connected to the hdl signal
// 'pid_dec_TB/rxvalid'.
// This is a concatenation of the unit hdl path ('pid_dec_TB') and of the signal
// name. The use of relative path names minimizes changes in case of a change
// in the DUT hierarchy. For example in order to change the signal name to
// 'pid_dec_TB/input_level/rxvalid' all you will need to change is the single line above.
keep hub_signal_map.rx_valid.hdl_path() == "rxvalid";

};
// End of unit

-- Create an instance of hub_env object in top level

extend sys {
hub_env : hub_env is instance;
};

'>
====================================================================
(a) is achieved by using units and their hdl_path() property.
(b) is achieved by using ports. You can see the the code uses only the internal names (the port names) and not the actual HDL names. The actual name appears only once where the port is connected to its corresponding HDL signal. Your topmost unit (in this case hub_env) will do quite well as a main depot for the actual connections to the HDL signals, hence achieving (c) as well.

Synchronization

Synchronizing Specman means coordinating its activities with the state of the simulator. Specman and the simulator work together - whenever the simulator reaches a certain time or the DUT arrives at a specific state, the simulator will pass the control over to Specman, so that Specman can do whatever you've told it that it should do. After Specman is done, it returns the control to the simulator, and wait for the next simulation event. Technically speaking, if anyone should care about that, the simulator hands Specman the control by calling a callback function via its PLI interface and it gets back the control when the callback function returns.


Sunday, March 9, 2008

Coverage in Specman

Every verification engineers faces this question. How much verification is enough? The answer to this question would most probably provided by coverage. Coverage also provide the information about where the verification engineer's energy should be focussed in the design. In this section we are going to see the description of functional coverage, what are the coverage groups and coverage items.

Functional coverage checks whether all stimulus scenarios, corner cases and protocols are covered in the verification environment. It also measures if all the important and interesting combination of stimulus have been exercised. So the functional coverage is important, because

  • It allows the engineer to focus on key areas of the design
  • It tells the engineer how much verification is enough
  • It improves the efficiency of stimulus generation
Following example shows the definition of coverage groups.

===========================================================================
<'
type usb_transaction : [CONTROL, BULK, ISO, INTERRUPT];
type hpie_state : [IDLE, TOKEN, DATA, HANDSHAKE, CRC];
struct usb
{
transaction_done : bool; // Set by some struct in sys
transaction : usb_transaction; // Define enumerated type
event packet_is_xmitted; // Declare an event
// Define coverage group
cover packet_is_xmitted using
// This option reduces the memory consumption
// Because the data collected for this group is reduced
count_only,
// A text description for the coverage group.
// This can be quoted string, The text is shown at the
// beginning of the info. for the group in the coverage report.
text = " Checking ISO transaction",
//The radix value for the buckets names to be displayed.
radix = HEX,
// Specifies the grading weight of the current group relative
// to the other group
weight = 10,
// Coverage groups are sampled only when
// this boolean expression is true
when = (transaction_done == TRUE) is
{
item transaction; // Item transaction is to be covered
// If there is any state machine in the DUT, coverage of
// Statemachine is done using
item state : hpie_state = '~/top/hub/hpie/current_state';
};

run() is also
{
emit packet_is_xmitted;
};// End of run()
}; // End of Struct
===========================================================================
Basic Coverage Item

A basic coverage item can be any one of the following elements

  • A field in the local struct
  • A field in the another struct in the hierarchy path
  • An HDL signal
====================================================================
<'
type up_opcode : [ADD, SUB];
type up_reg : [reg0, reg1];

struct basic_coverage
{
opcode : up_opcode;
op1: up_reg;
op2: byte;
event rdy;

cover rdy is
{
// op1 is in local struct
item op1 using radix=DEC;
// item op2 is in another struct
item op3 : bool = sys.header.op3;
// An HDL signal
item packet_rdy : int = '~/top/hub/ready';
}; // End of cover
}; // End of struct

Anybody knows more about the "Basic coverage item ranges" can contribute to this section.

====================================================================
Transition Coverage Item

Transition coverage items record the change in the item's value between two consecutive samplings of the item. Transition items are very useful for covering DUT state machines. The coverage options for the transition coverage items are similar to the Basic coverage items. However, the ranges option is not available for with transition coverage items.
====================================================================
<'
type hpie_state : [IDLE, TOKEN, DATA, HANDSHAKE];

struct hpie_sm
{
event r_clk is rise ('~/top/clock');

cover r_clk is
{
item state : hpie_state = '~/top/hub/hpie/present_state';

// Illegal option prints an error message
// when the boolean expression is true.

transition state using illegal =
not (( prev_st == IDLE and current_state == DATA) or
(prev_st == IDLE and current_state == HANDSHAKE) or
(prev_st == TOKEN and current_state == HANDSHAKE) );

}; // End of cover
}; // End of struct
====================================================================
Cross Coverage Item

Cross coverage items provide a cross product of two or more previously declared basic items. It is possible to create a cross of any number of basic or transition items declared in the same coverage group.

Latency Coverage

Latency coverage requires comparison of the time an activity ends with the time that activity started. There are no built-in constructs provided by e for latency coverage. However, it is easily possible to perform latency coverage using e.
====================================================================
<'
struct latency
{
!first_occurance : time;
!latency : time;

event start;
event end;

// Take a snapshot of system time on start
on start { first_occurance = sys.time};

// Take a snapshot of system time on end and compute the latency
on end { latency = sys.time - first_occurance};

cover end is { // Set up the coverage when the packet finishes
item latency;
}; // End of cover
}; // End of struct
====================================================================
Turn ON the Coverage

Use the cover configuration option to turn on coverage. If cover groups are defined then coverage is automatically turned on. The set_config() method is called to turn on the coverage. The set_config() method has a different option. Based on the user's requirement, they can include the different modes.
====================================================================
<'
struct coverage
{
field1 : int;
legal : bool;
keep field1 == 5;
}; // End of struct

extend sys
{
setup() is also // Extend the setup() of sys
{
set_config(cover, mode, on); // Turn On the coverage
}; // End of sys()
}; // End of extend
====================================================================
Viewing Coverage Results

After the e-based simulation is completed, coverage output is produced. Coverage output from Specman Elite can be viewed graphically or in a text file. Coverage output can also be accumulated over multiple test runs to view cumulative coverage.