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.
© Vineyard Research and Swapnajit Mittra, 2000
VCS © is a copyrighted product of Synopsys Inc.
|