Home Download Documentation FAQ Report a Bug SourceForge page
Previous Contents Next

8. PLI 1.0 (acc_/tf_) routines

8.1 PLI differences

At this point we support PLI 1.0. We also don't fully support the PLI routines that support:
  • $save/$restart (tf_write_save/tf_read_restart)
  • specify blocks (many acc_* routines)
  • CLI support - (acc_*_interactive* etc)
We do support $dumpvars and friends but you must include "-P /usr/local/lib/vcomp/vcd.tab" in your vcomp command line if you wish to use them.

8.2 Writing your own PLIs

Writing your own PLIs is relatively simple and encouraged. Simply compile up you PLI on our system using GCC to make a .o file (use the veriuser.h and acc_user.h that we provide in /usr/include). Include the name of the .o file in the vcomp command line along with your source files.

Next create a PLI table file to describe each PLI routine's interface, our file's format is compatible with those used by VCS® - include this file in the vcomp command line introduced with a '-P' flag. You can include as many -P files as you wish.

PLI table files have one line for each routine, it starts with the full name of the PLI routine ('$' included), this is followed by one or more of the following attributes:

  • data=<number> - an integer value that's passed as the second parameter to PLI callback routines
  • check=<entry-point> - entry point for the 'check' routines - these are called at the beginning of time prior to the start of simulation
  • call=<entry-point> - call routine
  • misc=<entry-point> - misc routine
  • vpi=<entry-point> - vpi init routine (non-standard - vcomp only, not VCS®)
  • size=<number> - size (in bits) of the value returned by a real function
  • size=r - indicates that the routine is a function that returns a real result
  • signed - indicates that the routine returns a signed value (required for v2k - non-standard - vcomp only, not VCS®)
  • nocallback - add this is you will not need to schedule value change callbacks on the parameters passed to instances of this task - we recommend that you always add this if possible - because not adding it means that many compiler optimizations are inhibited for any variable passed to an instance
A <entry-point> above is the name of a C-language routine you have compiled into the .o file you created. Entry points you don't declare will never get called.

Finally the line can optionally contains an access capabilities field that describes what sort of simulation accesses (via tf_* or acc_* routines) that a PLI task may wish to accomplish. The format of this field looks like one of the following:

  • acc+=<access list> - add to the global access list
  • acc-=<access list> - remove from the access list (seldom used)
  • acc:=<access list> - set the global access list (also seldom used)
The <access list> part consists of a comma separated list of capabilities followed by a colon and then a comma separated list of scopes to which they will apply. The capabilities selected can grossly change the amount of information collected for use at run time, the amount of storage required for variables at runtime and most importantly the number of different optimizations that the compiler can perform. For this reason you should specify the minimum capabilities that you need, those marked '[*]' in the list below should be particularly avoided. The supported capabilities are:
  • read - you wish to be able to read values in the specified scopes
  • read_write - you wish to be able to read and write values in the specified scopes
  • callback - you wish to be able to have value change callbacks for all variables in the specified scopes [*]
  • callback_all - you wish to be able to have value change callbacks for all variables and primitive terminals in the specified scopes [*]
  • force - you wish to be able to force variables in the specified scopes [*]
  • timing_check_backannotation - you wish to be able to do timing check back annotation for variables in the specified scopes (not yet supported)
  • gate_backannotation - you wish to be able to do gate back annotation for variables in the specified scopes (not yet supported)
  • module_path_backannotation - you wish to be able to do module path back annotation for variables in the specified scopes (not yet supported)
  • module_input_port_backannotation - you wish to be able to do module input port back annotation for variables in the specified scopes (not yet supported)
  • module_input_port__bitbackannotation - you wish to be able to do module input port bit back annotation for variables in the specified scopes (not yet supported)
A scope can be the name of an instance, an instance name followed by a '+' indicating that instance an all it's children, '*' indicating all module instances, '%TASK' indicating only the modules in which this PLI task or function is used or %CELL meaning only those modules tagged with a `celldefine macro.

8.3 An example

Here's a simple example of a couple of PLI routines, how to add them to our environment and how to use them.

First the C source of the PLI routines in test.c:

	#include 
	#include 
	#include 
	
	//
	//      first a simple example showing a simple C call
	//      it prints out the 'data' value of 1234 declared in the table
	//
	int
	call_hello_world(int reason, int data)
	{
        	printf("hello world\n");        // simple version
        	tf_message(ERR_MESSAGE, "User", "Hi Mom", "Hello world - data was %d\n", data);
	}


	//
	//      Now a more complex version - it attaches a VCD callback
	//      to the parameter passed and reports when it changes
	//

	int
	check_listen(int reason, int data)
	{
        	if (tf_nump() != 1) {
                	tf_message(ERR_ERROR, "User", "BAD", "must pass 1 parameter to $listen");
        	}
	}

	int
	call_listen(int reason, int data)
	{
        	tf_asynchon();  // enable async callbacks when parameters change
	}

	
	int
	misc_listen(int reason, int data, int param)
	{
        	switch (reason) {
        	case reason_paramvc:
                	tf_rosynchronize();     // wait until it stops changing at the end of the
                	break;                  // current time period
        	case reason_rosynch:
                	printf("value changed to %d\n", tf_getp(1));
                	break;
        	}
	}
                     
Compile it with "gcc -c test.c". Next create a PLI table file 'test.tab' containing:
	$hello_world call=call_hello_world data=1234 nocallback
	$listen check=check_listen call=call_listen misc=misc_listen acc+=callback:*          
And a verilog® test bench in test.v:
	module test;

        	reg     [7:0]a;
        	integer i;
	
        	initial begin
                	a = 0;
                	$hello_world;
                	$listen(a);
                	for (i = 0; i < 10; i=i+1)
                        	#10 a = a + 1;
        	end
	endmodule           

Now use vcomp to compile them with the command "vcomp -o test test.v test.o -P test.tab".

Finally run the binary 'test' to give:

	hello world
	MESSAGE   Hello world - data was 1234		[User-Hi Mom]
	          File 'pt.v', line 8
	value changed to 1
	value changed to 2
	value changed to 3
	value changed to 4
	value changed to 5
	value changed to 6
	value changed to 7
	value changed to 8
	value changed to 9
	value changed to 10                                                                           
Notice how $hello_world printed out 'hello world' two different ways, while $listen attached a value-change callback to the variable 'a' and printed out it's new value every time it changed.
Previous Contents Next