Subject: Re: [sv-cc] Perspectives from a "user" of the SV DPI C-layer
From: Andrzej Litwiniuk (Andrzej.Litwiniuk@synopsys.com)
Date: Tue Apr 01 2003 - 13:38:56 PST
John and team,
The C code for your example does not need to be that complicated.
Besides, it seems to me that its binary compatible version has been coded
incorrectly. Specifically, I don't understand why small values of input
arguments (16 bits) are not passed 'by value' as expected, but rather
'by reference'. Then, 32-bit CRC can be returned directly as a function result
rather than as an output arg.
48-bit values will be more troublesome than 'small' values, anyway.
Did you intend to use 2-state values or 4-state values?
Functions svBit* seem to suggest your intent to use the type 'bit'.
On the other hand, an explicit type has not been provided in your SV declaration
of handlePacketHeader() and therefore the types of all args default to 'logic'.
Was it intentional? If so, then your C header and C code are incorrect.
I'm assuming that it was an overlook and actually 'bit' values were intended.
Yet another question is why you didn't define SV struct corresponding to
struct etherHeaderT.
I would say that the most natural approach would be like this:
----------- SV ---------
typedef struct {
unsigned int type;
unsigned int length;
unsigned longint dest_addr;
unsigned longint dest_addr;
unsigned int crc;
} etherHeaderT; // compatible with C type struct etherHeader
export "DPI" SendPacketHeader=handlePacketHeader;
function unsigned int handlePacketHeader(ref etherHeaderT) // returns crc
...
endfunction
------------ C ---------
extern unsigned int SendPacketHeader(struct etherHeader *);
void MyCModel::SendPacketHeader( const etherHeaderT *header ){
// Now call actual function.
unsigned int crc = SendPacketHeader( header );
// compare returned CRC to actual.
if( crc != header->crc ) error("mismatched crcs" );
}
------------------------
Ok, let's assume that for some reasons you prefer to pass individual
argumens instead of using a structure. May I keep a function result, please?
Anyway, troublesome 48-bit inputs do remain.
The exported SV function should be correctly declared in SV as follows
('bit' specified explicitly replacing default 'logic'):
export "DPI" SendPacketHeader=handlePacketHeader;
function bit [31:0] handlePacketHeader(
input bit [15:0] type,
input bit [15:0] length,
input bit [47:0] destAddr,
input bit [47:0] srcAddr,
// output [31:0] crc - replaced with function result
)
...
endfunction
The required prototype for the C function wrapper,
according to the current DPI should be as follows:
extern svBitVec32 SendPacketHeader(
svBitVec32 type, /* input bit [15:0] type - by value */
svBitVec32 length, /* input bit [15:0] length - by value */
svBitPackedArrRef destAddr, /* input bit [47:0] destAddr - by reference */
svBitPackedArrRef srcAddr, /* input bit [47:0] srcAddr - by reference */
); /* output bit [31:0] crc replaced with function result */
1. Binary compatible model
void MyCModel::SendPacketHeader( const etherHeaderT *header ){
// for binary compatible, implementation required space for each arg
static svBitPackedArrRef destAddr = NULL;
static svBitPackedArrRef srcAddr = NULL;
// Allocate once and keep for further re-use
if (!destAddr) destAddr = (svBitBackedArrRef)malloc(
svSizeOfBitPackedArr(48) );
if (!srcAddr) srcAddr = (svBitBackedArrRef)malloc(
svSizeOfBitPackedArr(48) );
// Pack args into allocated implementation required storage areas
svPutPartSelectBit( destAddr, (svBitVec32)(header->destAddr), 0, 32);
svPutPartSelectBit( destAddr, (svBitVec32)(header->destAddr>>32), 32, 16 );
svPutPartSelectBit( srcAddr, (svBitVec32)(header->srcAddr), 0, 32);
svPutPartSelectBit( srcAddr, (svBitVec32)(header->srcAddr>>32), 32, 16 );
// end of args packing
// Now call actual function and compare returned CRC to actual.
if( SendPacketHeader( header->type, header->length, destAddr, srcAddr)
!= header->crc ) error("mismatched crcs" );
// no need to free allocated implementation required storage areas
}
All in all, I don't think this is that cumbersome.
The section of the code that packs 48-bit argument using part selects
may be replaced with the following code, which uses canonical representation.
It's simply a matter of preferences.
{
svBitVec32 addr[2]; /* canonical representation */
addr[0] = (svBitVec32)(header->destAddr); /* lower 32 bits */
addr[1] = (svBitVec32)((header->destAddr>>32)&0xffff); /* higher 16 bits */
svPutBitVec32( destAddr, addr, 48 );
addr[0] = (svBitVec32)(header->srcAddr); /* lower 32 bits */
addr[1] = (svBitVec32)((header->srcAddr>>32)&0xffff); /* higher 16 bits */
svPutBitVec32( srcAddr, addr, 48 );
}
Regards,
Andrzej
> Team,
>
> I've tried to put on the hat of a user of the DPI C-layer
> use model and have come up with some possibly interesting
> perpectives.
>
> In the C-layer we've created two categories of applications
> in terms of compatibility across environments:
>
> 1. Binary compatible - those that only require svdpi.h
> 2. Source compatible - those that also require svdpi_src.h
>
> After attempting to use the interface, I'm beginning to
> think there will be a widely used 3rd model of compatiblity:
>
> 3. Binary compatible across all environments that chose
> to use svBitVec32[] as the implementation type for
> svBitPackedArrRef.
>
> This is because I've found use models for 1 and 2 tend to be
> quite awkward. Use model 3 is substantially less so.
>
> Let me show some examples. In the following 3 examples,
> an ethernet packet header is packed into input args for
> an exported function and a CRC is returned as an output
> arg. I will compare and contrast the same example used
> with each of the 3 compatability models shown above.
>
> Each example shows the following ethernet packet header
> being passed to a function that will pack the fields into
> DPI arguments which are then passed to an exported SV
> function to make the call to the SV side.
>
> struct etherHeaderT {
> public:
> unsigned type;
> unsigned length;
> unsigned long long dest_addr;
> unsigned long long dest_addr;
> unsigned crc;
> };
>
> The exported HDL function is declared on the SV side as follows:
>
> export "DPI" SendPacketHeader=handlePacketHeader;
> function void handlePacketHeader(
> input [15:0] type,
> input [15:0] length,
> input [47:0] destAddr,
> input [47:0] srcAddr,
> output [31:0] crc )
> ...
> endfunction
>
> The SV function will return a computed CRC as an output arg.
>
> The required prototype for the C function wrapper then,
> according to the current DPI is as follows:
>
> void SendPacketHeader(
> svBitPackedArrRef type, // input [15:0] type;
> svBitPackedArrRef length, // input [15:0] length;
> svBitPackedArrRef destAddr, // input [47:0] destAddr;
> svBitPackedArrRef srcAddr, // input [47:0] srcAddr;
> svBitPackedArrRef crc ); // output [31:0] crc;
>
> Here are 3 different scenarios for how this function is called
> from a C model depending on what compatibility model is used:
>
> 1. Binary compatible model
>
> void MyCModel::SendPacketHeader( const etherHeaderT *header ){
>
> // Allocate binary compatible, implementation required space for each arg
> svBitPackedArrRef type = svBitBackedArrRef)malloc(
> svSizeOfBitPackedArr(16) );
> svBitPackedArrRef length = svBitBackedArrRef)malloc(
> svSizeOfBitPackedArr(16) );
> svBitPackedArrRef destAddr = svBitBackedArrRef)malloc(
> svSizeOfBitPackedArr(48) );
> svBitPackedArrRef srcAddr = svBitBackedArrRef)malloc(
> svSizeOfBitPackedArr(48) );
> svBitPackedArrRef crc = svBitBackedArrRef)malloc(
> svSizeOfBitPackedArr(32) );
>
> // Pack args into allocated implementation required storage areas
> svPutPartSelectBit( type, (svBitVec32)(header->type, 0, 16 );
> svPutPartSelectBit( length, (svBitVec32)(header->type, 0, 16 );
> svPutPartSelectBit( destAddr, (svBitVec32)(header->destAddr), 0, 32);
> svPutPartSelectBit( destAddr, (svBitVec32)(header->destAddr>>32), 32, 16 );
> svPutPartSelectBit( srcAddr, (svBitVec32)(header->srcAddr), 0, 32);
> svPutPartSelectBit( srcAddr, (svBitVec32)(header->srcAddr>>32), 32, 16 );
>
> // Now call actual function.
> SendPacketHeader( type, length, destAddr, srcAddr, crc );
>
> // Unpack returned CRC from allocated implementation required storage area
> // and compare it to actual.
> svBitVec32 compareCrc = svGetBits( crc, 0, 32 );
> if( compareCrc != header->crc ) error("mismatched crcs" );
>
> // Free allocated implementation required storage areas
> free( type );
> free( length );
> free( destAddr );
> free( srcAddr );
> free( crc );
> }
>
> In addition to being pretty cumbersome, this is also pretty inefficient
> because it involves memory allocation and deallocation on each call just
> for 5 simple arguments. Alternatively the arguments could have been
> pre-allocated at construction time and placed as data members in MyCModel.
> But this is also a lot of trouble just for 5 simple arguments to a
> function call.
>
> 2. Source compatible model
>
> The source compatible model at least alleviates some of the argument
> allocation concerns described above since it can inline the declarations
> for the args right in the stack space of the call as automatic variables.
> But it is still somewhat awkward to use:
>
> void MyCModel::SendPacketHeader( const etherHeaderT *header ){
>
> // Declare source compatible, implementation required space for each arg
> SV_BIT_PACKED_ARRAY( 16, type );
> SV_BIT_PACKED_ARRAY( 16, length );
> SV_BIT_PACKED_ARRAY( 48, destAddr );
> SV_BIT_PACKED_ARRAY( 48, srcAddr );
> SV_BIT_PACKED_ARRAY( 32, crc );
>
> // Pack args into allocated implementation required storage areas
> svPutPartSelectBit( type, (svBitVec32)(header->type, 0, 16 );
> svPutPartSelectBit( length, (svBitVec32)(header->type, 0, 16 );
> svPutPartSelectBit( destAddr, (svBitVec32)(header->destAddr), 0, 32);
> svPutPartSelectBit( destAddr, (svBitVec32)(header->destAddr>>32), 32, 16 );
> svPutPartSelectBit( srcAddr, (svBitVec32)(header->srcAddr), 0, 32);
> svPutPartSelectBit( srcAddr, (svBitVec32)(header->srcAddr>>32), 32, 16 );
>
> // Now call actual function.
> SendPacketHeader( type, length, destAddr, srcAddr, crc );
>
> // Unpack returned CRC from allocated implementation required storage area
> // and compare it to actual.
> svBitVec32 compareCrc = svGetBits( crc, 0, 32 );
> if( compareCrc != header->crc ) error("mismatched crcs" );
> }
>
> 3. Binary compatible model across all environments that chose
> to use svBitVec32[] as the implementation type for
> svBitPackedArrRef
>
> void MyCModel::SendPacketHeader( const etherHeaderT *header ){
>
> // Declare svBitVec32 arrays for the 2 args exceeding 32 bits in width.
> svBitVec32 destAddr[2];
> svBitVec32 srcAddr[2];
> svBitVec32 crc;
>
> destAddr[0] = (svBitVec32)(header->destAddr);
> destAddr[1] = (svBitVec32)(header->destAddr>>32);
> srcAddr[0] = (svBitVec32)(header->srcAddr);
> srcAddr[1] = (svBitVec32)(header->srcAddr>>32);
>
> // Now call actual function.
> SendPacketHeader(
> &header->type, &header->length, &destAddr, &srcAddr, &crc );
>
> // Compare returned CRC to actual.
> if( crc!= header->crc ) error("mismatched crcs" );
> }
>
> This version has been significantly simplified and is *highly*
> efficient !
>
> It is my feeling that many implementors will choose svBitVec32[]
> as the implementation type and user of those products will
> benefit from the cleaner, simpler use model offered by #3 above.
>
> I'm not suggesting that we change our spec - let's just be aware
> that there may be a "de-facto" compatibility model that a set of
> vendors may choose just in the interest of making things easier
> for their users.
>
> -- 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 \ /
> ---------
>
This archive was generated by hypermail 2b28 : Tue Apr 01 2003 - 13:40:11 PST