Tuesday, July 7, 2009

On Methods Associated with Data Generation

Tasks and functions form the core of the code in various functional verification environments. For example, for bus functional models, you will need tasks such as read, write, request, and grant working over the interface. For data such as packets, cells, and frame to be delivered over an interface defined through a bus functional model, you need methods to copy and compare the data, and to display the data as and when appropriate.

We go back to the previous blog article that talks about efficient data generation using the VMM approach, and now talk about some of the methods associated with data item like the ATM cell.

One of the first methods that we need is called the constructor method for the ATM cell object; this is needed in object-oriented programming to be able to create a new instance of the object such as the ATM cell. Here, we just need to call its parent class’ constructor method called new() which is predefined for us. So defining the constructor methods is fairly mechanical and looks something like this:

//constructor method for the atm_cell class
function new();
super.new(log);
endfunction: new

There is an allocate() method for vmm_data to create a new instance of data transaction. It allocates a new instance of the same type as the object instance and returns a reference to the instance.

//allocate() method for the atm_cell class
function vmm_data allocate(vmm_data to = null);
atm_cell transaction = new;
return transaction;
endfunction: allocate

Typically, for any data item that we create, we will need a printing utility to able to display the relevant portions of the data item being generated. We can define a display method for printing information regarding the ATM cell being generated.

//display method for the atm_cell class
//to print a prefix message followed by vpi and vci fields
function string psdisplay (string prefix-msg);
$sformat (psdisplay, “ %s : [%d :%d]”,
prefix-msg, this.vpi, this.vci);
endfunction: psdisplay

Just like the constructor and allocate methods, the definition of the copy method is fairly mechanical and only has a section of code that is specific to the data item being defined. It copies the current value of the object instance into the specified object instance. If no object instance is specified then a new instance is allocated. The copy method for the ATM cell looks as follows:

//copy method for the atm_cell class
function vmm_data copy (vmm_data to = null);
atm_cell cell_copy;
if (to == null)
cell_copy = new();
else if (!$cast(cell_copy, to)) begin
‘vmm_fatal (this.log, “Not an ATM cell”);
cell_copy = null;
return;
end
super.copy_data(cell_copy);
cell_copy.vpi = this.vpi;
cell_copy.vci = this.vci;
cell_copy.pt = this.pt;
cell_copy.clp = this.clp;
cell_copy.hec = this.hec;
for (i=0; i < 48; i++)
cell_copy.payload[i] = this.payload[i];

copy = cell_copy;

endfunction: copy

The ATM cell specific parts of the copy code are highlighted in red and the rest of the code remains the same for all data copy methods. These are carried out after a call to the base class copy_data method which takes care of all of base member copying. The copy method can create a duplicate of the main transaction. Often, you create a copy of the transaction and alter some fields for generating appropriate transactions.

Similar to the copy method, each data item typically should provide a compare method to allow comparison of data items. It compares the current value of the object instance with the current value of specified object instance and returns TRUE if identical and FALSE if different. If case the value is different then a descriptive text of first difference found is also returned in the specified string. The kind argument may be used to implement different comparison functions i.e., full compare, compare of random fields etc.

//compare method for the atm_cell class
function vmm_data compare (input vmm_data to,
output string diff,
input int kind = -1);
atm_cell cell;
if (to == null) begin
‘vmm_fatal(log, “Cannot compare a NULL reference”);
return 0;
end
else if (!$cast(cell, to) begin
‘vmm_fatal(log, “Not an ATM cell instance compare”);
return 0;
end
if (cell.vpi != this.vpi) begin
$sformat(diff, “vpi %0d != %0d”, this.vpi, cell.vpi);
return 0;
end
if (cell.vci != this.vci) begin
$sformat(diff, “vci %0d != %0d”, this.vci, cell.vci);
return 0;
end
if (cell.pt != this.pt) begin
$sformat(diff, “pt %0d != %0d”, this.pt, cell.pt);
return 0;
end
if (cell.clp != this.clp) begin
$sformat(diff, “clp %0d != %0d”, this.clp, cell.clp);
return 0;
end
if (cell.hec != this.hec) begin
$sformat(diff, “hec %0d != %0d”, this.hec, cell.hec);
return 0;
end
for (i=0; i < 48; i++) begin
if (cell.payload[i] != this.payload[i])
$sformat(
diff, “payload[%0d] %0d != %0d”, i, this.payload[i], cell.payload[i]);
return 0;
end
return 1;
endfunction: compare


Once you have added these properties and methods, a vmm_data class would look something as listed below. Once the data object is created and filled up appropriately to provide a sequence of ATM cells, how do we transfer that information to the DUT? Also, we will need to be able to collect data coming out of DUT into a vmm_data object for further analysis to help validation at higher-level. These bring in the concepts of transactors and channel. We will continue our discussions in upcoming blog articles on verification methodology keeping RTL engineers in mind.


class atm_cell extends vmm_data;

//create a log class for printing facilities
static vmm_log log = new (“ATM_CELL”,“ATM_CELL”);
rand bit [11:0] vpi;
rand bit [15:0] vci;
rand bit [2:0] pt;
rand bit clp;
rand bit [7:0] hec;
rand bit [7:0] payload[48];

//add a constraint to pt
constraint pt_constraint1 {pt > 3;}




//constructor method for the atm_cell class
function new();
super.new(log);
endfunction: new

//allocate() method for the atm_cell class
function vmm_data allocate(vmm_data to = null);
atm_cell transaction = new;
return transaction;
endfunction: allocate

//display method for the atm_cell class
//to print a prefix message followed by vpi and //vci fields
function string psdisplay (string prefix-msg);
$sformat (psdisplay, “ %s : [%d :%d]”,
prefix-msg, this.vpi, this.vci);
endfunction: psdisplay

//copy method for the atm_cell class
function vmm_data copy (vmm_data to = null);
atm_cell cell_copy;
if (to == null)
cell_copy = new();
else if (!$cast(cell_copy, to)) begin
‘vmm_fatal (this.log, “Not an ATM cell”);
cell_copy = null;
return;
end
super.copy_data(cell_copy);
cell_copy.vpi = this.vpi;
cell_copy.vci = this.vci;
cell_copy.pt = this.pt;
cell_copy.clp = this.clp;
cell_copy.hec = this.hec;
for (i=0; i < 48; i++)
cell_copy.payload[i] = this.payload[i];
copy = cell_copy;

endfunction: copy

//compare method for the atm_cell class
function vmm_data compare (input vmm_data to,
output string diff,
input int kind = -1);
atm_cell cell;
if (to == null) begin
‘vmm_fatal(log, “Cannot compare a NULL reference”);
return 0;
end
else if (!$cast(cell, to) begin
‘vmm_fatal(log, “Not an ATM cell instance compare”);
return 0;
end
if (cell.vpi != this.vpi) begin
$sformat(diff, “vpi %0d != %0d”, this.vpi, cell.vpi);
return 0;
end
if (cell.vci != this.vci) begin
$sformat(diff, “vci %0d != %0d”, this.vci, cell.vci);
return 0;
end
if (cell.pt != this.pt) begin
$sformat(diff, “pt %0d != %0d”, this.pt, cell.pt);
return 0;
end
if (cell.clp != this.clp) begin
$sformat(diff, “clp %0d != %0d”, this.clp, cell.clp);
return 0;
end
if (cell.hec != this.hec) begin
$sformat(diff, “hec %0d != %0d”, this.hec, cell.hec);
return 0;
end
for (i=0; i < 48; i++) begin
if (cell.payload[i] != this.payload[i])
$sformat(diff, “payload[%0d] %0d != %0d”, i, this.payload[i], cell.payload[i]);
return 0;
end
return 1;
endfunction: compare


endclass: atm_cell