Subject: Re: [sv-cc] Perspectives from a "user" of the SV DPI C-layer
From: Stickley, John (john_stickley@mentorg.com)
Date: Tue Apr 01 2003 - 15:10:14 PST
Andrzej,
Thanks for your good response. I'm much more educated now.
There was a gap in my understanding of the C-layer that I
feel much better about. Read on ...
Andrzej Litwiniuk wrote:
> 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
johnS:
Perhaps the misunderstanding is mine here. Are you saying that packed
bit vectors < 32 bits in length can be passed by value ? That would
be good news to me.
> result
> rather than as an output arg.
johnS:
I could have. I was just trying to demonstrate the use of
function arguments. Perhaps a better example could have
been used.
>
> 48-bit values will be more troublesome than 'small' values, anyway.
>
> Did you intend to use 2-state values or 4-state values?
johnS:
2 state.
> 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.
johnS:
No it was not intentional - I meant to convey 2 state bit types.
>
> 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.
johnS:
Again not my intent. I wanted to focus on the use of bit vector
arguments being passed to conventional style Verilog modules and
how that might be done. I see reuse of many existing Verilog modules
and IP in the new SystemVerilog simulators so they may deal largely
with bit vectors.
>
> 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 */
johnS:
Really ??? Again I was not aware that values < 32 bits are passed
by value or that they require canonical. That's actually good news.
I thought packed bit vectors of any size have to be passed as
svBitPackedArrRef's.
Sure enough it states it quite plainly in section A.7.7.
My apologies ! I was going to suggest this change but was hesitant to
rock the boat. This is great news and my misunderstanding.
Common sense would dictate this - I guess I should have realized it !
I see where the same applies with output arg values am I correct ?
The only difference is that we use canonical form by reference right ?
So, if I have an output arg it would be like this:
extern void GetCRC( svBitVec32 *crc ); /* output bit [31:0] crc */
Is that right ? This alleviates my concerns considerably for
small value bit vectors.
> 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.
johnS:
My main nit-pick was for the small bit vector values.
Now that I know they directly passed using the canonical form,
I don't think the thing with the bigger vectors is that bad.
As I said, mallocs can be done at init time and reused.
The only thing that would be nice is if somehow we could let
what we consider "small values" go all the way up to 64 bits.
In this particular example even the 48 bit args would be super
simple if that were the case.
>
>
> 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 );
> }
johnS:
Yes, I realize copy can be done but I want to avoid memory to
memory copies as they add too much overhead. I think the part
select accessors will be used most of the time as they are much
more efficient and only require assignment once, not twice.
>
>
>
> Regards,
> Andrzej
johnS:
Thanks for setting me straight on this stuff.
>
>
>
> > 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 email may contain material that is confidential, privileged and/or attorney work product for the sole use of the intended recipient. Any review, reliance or distribution by others or forwarding without express permission is strictly prohibited. If you are not the intended recipient, please contact the sender and delete all copies. __ ______ | \ ______________________/ \__ / \ \ 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 - 15:12:06 PST