Subject: [sv-cc] Perspectives from a "user" of the SV DPI C-layer
From: Stickley, John (john_stickley@mentorg.com)
Date: Tue Apr 01 2003 - 10:05:21 PST
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 - 10:10:03 PST