PROPOSAL: Re: ISSUE:DirectC:DirectC i/f should support mechanism for calling Verilog task/function from a DirectC


Subject: PROPOSAL: Re: ISSUE:DirectC:DirectC i/f should support mechanism for calling Verilog task/function from a DirectC
From: Stickley, John (john_stickley@mentorg.com)
Date: Fri Oct 25 2002 - 08:53:27 PDT


Fellow SV-CC'ers,

I've been reading with interest some of the suggestions for the DirectC
interface - particularly the recent "ISSUE" e-mails from Swapnajit
and Kevin.

We've been looking into developing an interface based on DirectC
for some time and have developed extensions that address some
of the things we view to be limitations of the current DirectC
donation.

In particular we've been doing some considerable investigation
into coming up with an HDL-to-C and C-to-HDL function
calling interface that is particularly well suited to a multi-threaded
C testbench environments such as SystemC or TestBuilder.

We think that DirectC is a good start to this but there are some
simple extensions that we think can make DirectC particularly
well suited to any object oriented, multi-threaded C or C++
modeling environment.

I decided it would be a good time to put forth some concrete
proposals in the context of the recent discussion thread started
by Swapnajit and added to by Kevin.

Particularly I'm describing our solution for context sensitive
name binding for both the HDL-to-C and C-to-HDL function
call directions as well as showing a specific suggested syntax
for declaring HDL functions that are callable from C.

Swapnajit Mittra wrote:

o The called Verilog task or function can be specified from
the DirectC domain by Verilog full pathname. (This will
allow us to call a specific Verilog task or function in
the design hierarchy. We need to think about the actual
syntax of this).
The simplest, easiest to use type of name binding is, of course,
simple static name match as per the current DirectC donation.
Here, as stated in the donation spec,

1. The user defines a C function.
2. The user calls a specially declared function of the same name
    from the HDL side.

The infrastructure takes care that the proper binding and linking
is done based on name match.

However, as I will describe below, this has some limitations
- particularly in multi-instanced and multi-threaded environments.
This is when it makes sense to get instance specific pathnames
involved in the binding process as is suggested above by Swapnajit.

For this I would recommend a special way of associating
a user specified context pointer (that can denote, for example
a user's C model object) with a function call being called from
a specific HDL module.

Kevin suggests something similar ...

Kevin Cameron x3251 wrote:

I would suggest considering module instances as being like class
instances in C++. You could get the handle to a module instance
using the standard PLI mechanisms and then use it in C++ as a
class instance pointer with the tasks and functions of the module
as virtual functions.

Doing this stuff in C rather than C++ is awkward because C doesn't
support overloading of names, so you would have to come up with
some name mangling scheme.

Our proposal accomplishes the same thing but uses user defined
context pointers rather than handles denoting HDL module
instances - at least for the HDL-to-C function call direction.
(I'll show later that for C-to-HDL, Kevin's approach is right
on the mark.).

We've also found that this approach works equally well for
C or C++ environments and does not require C++ class
support.

For the context sensitive name binding, a special API call is
required to bind the function caller to the callee. For simple
name binding no such call is required.

The following sections describe the two mechanisms and
the 2nd section illustrates the use of the instance specific name
binding mechanism with a real-life 4 port ethernet packet router example.

HDL-to-C Function Calls: Simple Context Neutral HDL Caller Bindings

Simple caller bindings are by far the easiest to use. The current DirectC
donation specifies simple caller binding. In this use model, all the user
has to do is define his or her C functions and link them into the executable.
The DirectC support infrastructure (a.k.a. SystemVerilog kernel) will
worry about making the calls at run-time.

But, in addition to passing all the DirectC data type arguments
(either by direct access or by abstract access means), we suggest
adding an additional argument that always gets passed into the C function
as the first argument, called a context pointer.

Consider the following C call that is callable from HDL,

    void putAtmCell(           // User supplied C function.
        void *context,
        vec32 *atmCellHeader,  // input [0:39]
        vec32 *atmCellPayload, // input [0:383]
        vec32 *payloadCRC )    // output [31:0]
    { ... }

In the simple caller binding use model, the C functions are all
context neutral. In this case, the context pointer passed in to the
C function from the run-time infrastructure will be NULL and ignored
by the user code. This means that the function is essentially a free
standing function that is not associated with any user object or model
instance. The function merely performs its job and returns.

HDL-to-C Function Calls: Context Pointers for Instance Specific HDL Caller Bindings

For simple applications the simple HDL caller binding model is adequate.
But more complex applications may need to tie into multi-threaded,
object oriented environments with multiple concurrent instances of user
defined models, as is possible in a SystemC or TestBuilder testbench
modeling environment. In this case, the user may very much care about
which specific instances of hardware models are making the function call
and for those instances the user may wish to specify a context pointer that
refers to a specific user C++ model or object instance that interfaces
with the specific HDL module instance making the call.

In this case the user's context object would be passed into the call as the
void *context argument whenever the call from an associated HDL model
instance is made.

In order to support instance specific function calls, the following API function
is added to the DirectC interface:

    void BindHDLCaller(
     const char *callerInstanceName,
     const char *functionName,
     void *context );

This call simply associates a local, user defined context pointer with an instance
of an HDL module making a function call, i.e. whenever that particular HDL
instance makes the call, the user-defined context pointer is passed back to
the user's C function.

This may seem like a rather trivial feature but the ramifications of what it buys us is
significant. While our simple caller binding use model gives us the simplicity and
convenience of the CBlend and DirectC use model, what those use models are
significantly lacking is a provision for a good interface between HDL models and
specific instances of objects that represent C++ models. A simple feature of
being able to associate user defined C++ model contexts with specific instances of HDL
models allows for extremely flexible interconnect between software and hardware
models. It facilitates multiple instances of a given software model type and
connections between those software instances and their associated hardware
instances. For example, consider the verification scenario listed in the following figure:

A typical way of modeling this in a C++ testbench is to define a class representing the
software model, EthPortWrapper. The user will then want to instantiate 4 copies of the same
model to take maximum advantage of model reuse. Suppose the pointers to these 4
model instances are context1, context2, context3, and context4. It may also be the
case that 4 independent threads are driving each EthPortWrapper.

On the hardware side, the user will have 4 instances of the Verilog module EthPort,
namely, top.u1, top.u2, top.u3, and top.u4.

Now suppose each EthPort module declares an HDL caller function to ask for a packet
from its associated wrapper in the testbench. The user may define a C function called
getPacket(). When this C function is called how will the user know which
EthPortWrapper object is to provide the packet? This is where the context argument
can be used. The C function can downcast the context pointer to a specific EthPortWrapper
object instance to which it now gains access. In C++, the C function can be declared a
static member function with private access privileges into the class so that, for example,
getPacket() could be a static member function of EthPortWrapper that has full access
privileges to all its private data members. For C applications, the context pointer
can just be a pointer to a struct representing the EthPortWrapper.

Prior to simulation, the user can make the following calls to associate EthPortWrapper
context pointers with the correct EthPort module instance:

    void *context1 = (void *)new EthPortWrapper();
    void *context2 = (void *)new EthPortWrapper();
    void *context3 = (void *)new EthPortWrapper();
    void *context4 = (void *)new EthPortWrapper();
    ...
    BindHDLCaller( "top.u1", "getPacket", context1 );
    BindHDLCaller( "top.u2", "getPacket", context2 );
    BindHDLCaller( "top.u3", "getPacket", context3 );
    BindHDLCaller( "top.u4", "getPacket", context4 );

Now, whenever the C function is called the correct context pointer
is passed in and the C function knows which C or C++ object is
to be associated with which instance of an HDL caller is calling it.

With this simple solution we have completely generalized the DirectC
interface to handle multi-model instance bindings. This feature buys a
lot in object oriented, multi-threaded C++ testbench modeling
environments like SystemC, TestBuilder, CynLibs, etc.

It even applies to home-brewed single threaded pure C or C++ test
environments which happen to use multiple instances of classes associated
with the hardware.

Calling SystemVerilog functions from C (C-to-HDL direction)

For the C-to-HDL direction we have proposed a syntax that is
somewhat the inverse of the syntax proposed for the HDL-to-C
function calls in the DirectC donation.

Here's an example:

module AtmCellGenerator
    ...
    reg [0:423] atmCell;
    reg [31:0] computedCRC;
    ...

    extern “HDL” function getAtmCell;

    function [0:423] getAtmCell;
        input [7:0] VPI;
        input [15:0] VCI;

        reg [0:39] header;
        reg [0:383] payload;

        begin
        // Access ATM cell header and payload based on VPI and VCI
        ...
        getAtmCell = { header, payload };
        end
    endfunction
    ...

    extern “HDL” task putAtmCell;

    task putAtmCell;
        input [0:39] atmCellHeader;
        input [0:383] atmCellPayload
        output [31:0] payloadCRC;

        atmCell <= { atmCellHeader, atmCellPayload };
        payloadCRC = computedCRC;
    endtask
    ...
endmodule

The HDL callees can either be functions or tasks. But they should obey
0-time semantics (Verilog already requires that function calls do - but
not tasks). They are declared with an "extern" syntax very similar
to that proposed for HDL-to-C calls in the DirectC donation, except
that the extern "C" becomes extern "HDL" (or could be extern "SystemVerilog").

This is a declaration that states that the function or task is callable from C.

On the C side the calls would look something like this:

extern vec32 *getAtmCell(         // Infrastructure generated C proxy function for HDL callee.
        void *context,
        vec32 *VPI,
        vec32 *VCI );

extern void putAtmCell(           // Infrastructure generated C proxy function for HDL callee.
        void *context,
        vec32 *atmCellHeader, // input [0:39]
        vec32 *atmCellPayload, // input [0:383]
        vec32 *payloadCRC );   // output [31:0]

// Some user code ...
{
    vec32 VPI;
    vec32 VCI;

    // Fill out VPI and VCI arguments here.

    // Now make the call to the HDL side ...
    vec32 *cellPayload = getAtmCell( getAtmCellContext, &VPI, &VCI );

   // Do something with returned cellPayload ...
}

// Some other user code ...
{
    vec32 atmCellHeader;
    vec32 atmCellPayload;
    vec32 payloadCRC;

    // Fill out header and payload info here ...

    // Now make the call to the HDL side ...
    getAtmCell( putAtmCellContext,
        &atmCellHeader, &atmCellPayload, &payloadCRC );

    // Check the returned CRC for correctness ...
}

Note the context arguments. Context binding can occur in a reverse
sense than for the context binding mechanism described for the
HDL-to-C direction. The following sections describe it.

C-to-HDL Function Calls: Simple Context Neutral HDL Callee Bindings

As with HDL callers, simple HDL callee bindings are by far the easiest to use.
In this use model, all the user has to do is define his or her HDL callee functions
and run the whatever compiler or linker is supplied with the vendor tool environment
that is being used.

The tool generates "proxy" functions that serve as local C proxies for the
actual HDL functions being called.

The user links the tool generated proxy functions into the executable, and calls
them from various call sites in the C code during run-time. The user can use
a header file that may be automatically generated by the tool compiler to declare the
prototypes for the proxy functions. In the simple HDL callee binding use model,
the C proxy functions can be called with context neutral semantics. In this case,
the user must pass a NULL context pointer to the proxy function.

Note that this will only work for free standing functions on the HDL side
such as one might see in a VHDL package. (Does such a thing exist in
SystemVerilog ?)

For tasks or functions that are defined in modules, a context pointer must
be used to identify the module instance. For this the user must
must use the instance specific callee binding use model described next.

C-to-HDL Function Calls: Context Pointers for Instance Specific HDL Callee Bindings

In the case where the user cares about which specific instances of HDL callee
functions are being called (which is probably most of the time), for those instances
the user will want to pass to the C proxy function, a context pointer that refers to a
specific HDL module instance that contains the HDL callee function.
[Note: This is exactly what Kevin C. suggested in the previous quoted text above.]

In this case a handle representing the HDL callee instance would be passed into
the proxy function call as the void *context argument whenever the call from a
C model is made.

In order to support instance specific HDL callee function calls, the following
API function is added to the DirectC API interface to obtain an HDL callee
instance handle:

    void *
    BindHDLCallee(
        const char *callerInstanceName,
        const char *functionName );

The handle returned can then be passed by the C caller code to future calls of the
C proxy function.

So, getting back to our ATM cell example described above,
the following code is an example of how we might generate
the context handle representing a specific instance of a
function call in a particular module instance:

void *getAtmCellContext = BindHDLCallee( "top.u1", "getAtmCell" );
void *putAtmCellContext = BindHDLCallee( "top.u2", "putAtmCell" );

These getAtmCellContext and putAtmCellContext pointers are then
the pointers that the user must pass into the calls to the HDL functions shown
previously. The runtime system interprets the context pointer and routes the
function call to the appropriate HDL code.

-- johnS
                                                           __
                       ______                             |  \
______________________/      \__                         /    \
                                \       H  Dome      ___/     |
John Stickley                 E |       a  __    ___/  /       \____
Principal Engineer            l |       l  | \  /
Verification Solutions Group    |       f  |  \/     ____
Mentor Graphics Corp. - MED   C \         --  /     /
17 E. Cedar Place             a  \     __/   /     /
Ramsey, NJ  07446             p  |    /        ___/
                                 |   /        /
mailto:John_Stickley@mentor.com  \           /
Phone: (201)818-2585              \         /
                                   ---------


C:_DOCUME_1_johns_LOCALS_1_Temp_nsmailLQ.jpeg



This archive was generated by hypermail 2b28 : Fri Oct 25 2002 - 08:58:37 PDT