Plis in Verification Environment: Surekha Sonawane Rohit Gupta Jitendra Puri
Plis in Verification Environment: Surekha Sonawane Rohit Gupta Jitendra Puri
Plis in Verification Environment: Surekha Sonawane Rohit Gupta Jitendra Puri
Surekha Sonawane
Rohit Gupta
Jitendra Puri
surekha.sonawane@dcmtech.co.in
rohit.gupta@dcmtech.co.in
jitendra@dcmtech.co.in
ABSTRACT
Verification of complex system-on-chips (SOCs) and multi-million gate ASICs has become
extremely difficult. Top level testing of such huge projects require innovative efforts in the
process of data handling and manipulation. Use of PLIs is a good alternative to the conventional
methods to handle these complex requirements. This paper presents a simple and efficient way to
customize your verification environment using PLIs in C. It further details out how this approach
takes care of massive data storage and its processing.
1.0 Introduction
The amount of data processing required in the verification process has been steadily increasing
with the size and complexity of the design. In such large projects, the implementation of efficient
tasks or functions for data handling and manipulation through Verilog becomes a very tedious
activity. Programming Language Interface (PLI) provides a simple and efficient workaround to
these problems. This paper addresses such issues related to verification cycle with the help of
PLIs. The document covers a brief introduction to PLIs in C and its usage in the verification
components like Bus Functional Models (BFMs).
2.1
Access routines
Access routines provide access to information about the internal data representation of an HDL
design. These routines can read/write information about particular objects like modules,
primitives, nets, registers, parameters, path delays and timing checks. Access routines can also be
used to monitor the changes in the value of these objects.
All access routines start with the prefix acc_ and the name indicates the type of information
the routine reads or writes.
The following example uses two PLIs to maintain the transaction queue and to fetch the cycle
information.
/* new_cycle adds information about a cycle in the transaction queue. The parameters passed to
this PLI are - sequence_id, command, address, data and parity in the same order */
void new_cycle()
{
/* This is the first node pointing to start of queue*/
struct cycle_info *temp_node1 = start_q;
/* New node to be added */
struct cycle_info *temp_node2add;
/* Allocating memory to the new node */
temp_node2add = (struct cycle_info *) malloc (sizeof(struct cycle_info));
/* Accessing integer variable sequence_id using acc_fetch_tfarg() routine
This is the 1st argument passed to the PLI */
temp_node2add->sequence_id = acc_fetch_tfarg(1);
/* Accessing character variables command, address and data using tc_strgetp() routine.
These are the 2nd, 3rd and 4th arguments passed to the PLI. b indicates binary format of the
returned parameters */
temp_node2add->command = (char*)tf_strgetp(2,b);
temp_node2add->address = (char *)tf_strgetp(3,b);
temp_node2add->data = (char *)tf_strgetp(4,b);
/* Accessing parity using acc_fetch_tfarg routine */
temp_node2add->parity = acc_fetch_tfarg(5);
/* Adding the new cycle to the link list. add_link function appends this new cycle
information to the last node in the link list */
add_link(temp_node2add);
/* get_cycle fetches cycle information from the transaction queue and deletes the node. The input
to this PLI is the sequence_id of the cycle to be fetched. The parameters returned are cycle
command, address, data and parity */
void get_cycle()
{
struct cycle_info *temp_node = start_q;
struct cycle_info *temp_node2get;
int sequence_id, len;
/* Fetching sequence id of the cycle to be executed */
sequence_id = acc_fetch_tfarg(1);
/* Searching for node in the link list. search_in_q function searches node with sequence_id as
key */
temp_node2get = search_in_q(sequence_id);
/* Get size of second argument, i.e. command */
len = tf_sizep(2);
/* Writing command in the second argument. tf_strdelputp takes argument number, its bit
length, format string and value as parameters. The last two parameters are delay and delay type*/
tf_strdelputp(2, len, b, temp_node2get->command, 0, 0);
len = tf_sizep(3);
tf_strdelputp(3, len, b, temp_node2get->address, 0, 0);
len = tf_sizep(4);
tf_strdelputp(4, len, b, temp_node2get->data, 0, 0);
/* Single bit parity is written using routine tf_putp */
tf_putp(5,temp_node2get->parity);
.
.
.
/* Deleting the cycle from the queue. del_link deletes a node from the link list using sequence
id */
del_link(sequence_id);
.
.
.
}
for(j=0;j<8;j++)
{
data[(i*8)+j]=data[(i*8)+j ]+1;
}
.
.
}
void dump_file()
{
FILE *ptr1;
int sequence_id;
char *addr, *data;
tf_asynchon();
sequence_id=acc_fetch_tfarg(1);
addr=(char *)tf_strgetp(2,b);
data=(char *)tf_strgetp(3,b);
ptr1=fopen(file_dump);
.
/*store it in the file using C file-handling functions*/
.
}
Compile &
Link Verilog
and C
Application
Associate
the routine
with a
System
Task
Run the
Modified
Verilog
Code
After writing the PLI function in C, we have to associate and link this routine with the
corresponding HDL system task name. This process is simulator specific. Verilog-XL, VCS etc.
each use slightly different method for linking PLIs. For Verilog-XL, edit the veriusertfs array
(Refer PLI 1.0 User Guide and Reference from Cadence) in the veriuser.c file and declare the
PLI function as an external reference.
extern int new_cycle();
After association, run vconfig script from Verilog tools directory. The vconfig utility creates a
shell script with the default name cr_vlog which will compile and link the PLI with Verilog-XL.
After linking, a PLI function can be called from the Verilog source code just like any other
system call.
For example, the following code will call the PLI new_cycle
module top_test;
reg a;
reg [31:0]data;
initial
begin
end
....
endmodule
In case of VCS, PLI tables have to be maintained in <filename>.tab file (See VCS Users Guide).
This PLI table identifies the user-defined tasks and functions, specify their Verilog syntax, and
relate them to particular C functions that compose the PLIs. This <filename>.tab file can be
linked using vcs compile command with -M option which generates the executable file. You
just need to run executable for simulation.
7.0 Acknowledgements
The authors wish to thank DCM PCI-X Core team for giving us time and support necessary to
write this paper. Special thanks to Mrs. Geeta Arora and Mr. Sanjay Budholiya for their valuable
guidance throughout.
8.0 References
[1] PLI 1.0 User Guide and Reference from Cadence.
[2] A Brief Introduction to PLI by Celia Clause.
[3] Sutherland HDL's The Verilog PLI Handbook.
[4] Verilog HDL, A Guide to Digital Design and Synthesis by Samir Palnitkar.
[5] VCS Users Guide from Synopsys.
10
9.0 Appendix
The following PLI routines have been used in this paper.
1) acc_fetch_tfarg: Fetches value of task argument associated with the routine.
Usage: acc_fetch_tfarg(argument_no)
argument_no is the position of argument in system task.
2) tf_strgetp: Gets argument value as a formatted character string.
Usage: tf_stregetp(argument_no, format_char)
argument_no is the position of the argument in system task,
format_char is the character specifying the format of argument value.
3) tf_sizep: Gets length of an argument in bits.
Usage: tf_sizep(argument_no)
argument_no is the position of the argument in the system task.
4) tf_strdelputp: Write a value to an argument with the specified delay.
Usage: tf_strdelputp(argument_no, bitlength, format_char, value, delay, delay_type)
argument_no is the position of the argument in the system task,
bitlength is size of the argument in bits,
format_char is the character specifying the format of argument value,
value is the value to be written to the argument,
delay is the delay before the written value is to take effect in Verilog simulation model,
delay_type is the type of delay.
5) tf_putp: Writes a 32-bit integer value to an argument.
Usage: tf_putp(argument_no, value)
argument_no is the position of the argument in the system task,
value is the value to be written to the argument.
6) tf_asynchon: Enables a PLI routine to be called whenever an argument changes value.
Usage: tf_asynchon()
7) tf_getcstringp: Gets an argument value as a C character string.
Usage: tf_getcstringp(argument_no)
argument_no is the position of the argument in the system task.
8) tf_error: Prints an error message.
Usage: tf_error(control_string, arg1, arg5)
control_string is the standard C printf data identifiers (%d, %c etc.) for each argument,
arg1arg5 are the variable components of the message corresponding to data identifiers.
9) tf_warning: Prints a warning message.
Usage: tf_warning(control_string, arg1, arg5)
control_string is the standard C printf data identifiers (%d, %c etc.) for each argument,
arg1arg5 are the variable components of the message corresponding to data identifiers.
10) tf_gettime: Gets current simulation time in integer form.
Usage: tf_gettime()
11) io_printf: Writes messages to the standard output and log file
Usage: io_printf(control_string, arg1,arg5)
control_string is the standard C printf data identifiers (%d, %c etc.) for each argument,
arg1arg5 are the variable components of the message corresponding to data identifiers.
11