An often asked question about Verilog PLI is: Can I use C++ to write a PLI application ? This article answers this question and related issues.

Editor's note: This article assumes that you are familiar with C/C++/PLI/Verilog. We have used VCS for running the examples of this article, but the solution will not be significantly different if you use any other simulator. A special thank to all the readers of the newsgroup comp.lang.verilog who have contributed to this article by sending in their valuable suggestions.

The PLI is originally designed to be a C interface. But, as it turned out, C is a subset of C++ and C++ has a mechanism that allows you to use a C interface for C++ code. If you want to use a C++ program for your PLI, you need to use this interface. Or, in other words, there will be two interfaces to make the entire scheme work.

In this article we will take two examples that would walk us through the procedure.

In our first example we will create a system call $print_reg() that simply prints the value of a register. The  register is passed to the system call as its argumrnt. There is no particular reason to use C++ for this example, but it will explain the basic steps required for C++/Verilog interfacing. Before we delve into the details of how to create $print_reg(), first here is an example of a Verilog program which uses this call .

      
module cpp;
reg r1;           
                        
initial begin     
   r1 = 1'b1;     
   $print_reg(r1);
end               
endmodule         
      
      
To implement this system call, there are two obvious steps: first we need to get the value of the register and then print it. Although we can use any of tf_getp(), tf_strgetp() or tf_exprinfo() to get the value of the register, tf_getp()wins the race for its simplicity. We will use cout to print the value. (If you are asking 'why not io_printf() ?', the answer is: our C++ program has so few lines that without the use of cout, it can hardly qualify as a C++ program.)

Here is the C++ code using tf_getp():


#include <iostream.h>
#include "cplusplus_veriuser.h"
extern "C" void my_cpp_calltf() { 
   cout << "The value of the reg is: " << tf_getp(1) <<"\n";
}
       
      
Now let us look at some of the details of the code.

1. The header file "cplusplus_veriuser.h"  is almost similar to "veriuser.h" that you normally use except that it encloses all function prototype declarations with the following compiler directives.

      
#ifdef __cplusplus
extern "C" {
#endif
      
/* Utility routines */
      
extern char *tf_getinstnce();
extern int tf_nump();
extern int tf_inump();
 ...
#ifdef __cplusplus
}
#endif
      
      
Many simulator vendors (including Synopsys for VCS, the simulator that we used to try our test cases) actually provide this modified  version of the file (and also the  modified acc_user.h).

Also, note that, depending on your C++ compiler, you may face some problem if your header files are not compliant to ANSI C. Try different ABI options that your C++ compiler provides if you face this problem.

2. my_cpp_calltf() is our calltf routine that we have chosen to write using C++. Since Verilog PLI can handle only C function, the compiler has been instructed so using the declaration extern "C" before the function type (void).

And that's it! You do not need to a thing after this apart from remembering to use a C++ compiler rather than a C one when you compile your code. You do the compilation in two steps. First you compile your C++ code. Assuming you use CC as your C++ compiler and you have kept your C++ code in a file named simple.cc:

% CC -c simple.cc

This will generate an object file simple.o. Once you generate this object file, in VCS environment, you compile it along with other Verilog files. You must use CC as your linker.

% setenv VCS_CC CC
% vcs -ld CC -P pli.tab simple.o test.v
% simv

Our second example is also simple: it prints out the ANDed value of two registers. The registers are passed to the system call as parameters. In this example, we will use a rather more esoteric feature of C++, namely class. The main objective of this example is to show you that in spite of using more complex features of C++, the basic code structure remains the same as before.

The following code shows the implementation.

      
#include <iostream.h> 
#include "cplusplus_veriuser.h" 
      
extern "C" void my_cpp_and(); 
      
class and {
   public:
      char *do_and (int, int); 
};
      
extern "C" void my_cpp_and() {
   and myand;
   cout 
         << "The ANDed value of the inputs = "
         << myand.do_and(tf_getp(1), tf_getp(2))
         << "\n";
}
      
char * and::do_and (int a, int b)  {
       if ((a&b) == 1) return "1"; 
       else return "0";
}
       
      
The compilation is done in the same way as before.
Share/Save/Bookmark



Verification Management
Join Verification Management Group


Book of the Month


From Our Press