Subject: Re: DirectC: C layer
From: Kevin Cameron (Kevin.Cameron@nsc.com)
Date: Wed Dec 11 2002 - 16:31:28 PST
Francoise Martinolle wrote:
> Andrzej,
>
> It was very difficult today to place any word during the meeting so I am
> gathering my issues/comments in this email.
>
> I have 3 main issues with the proposal.
>
> .....
>
> I believe that the only sound technical solution is to do copy in and copy out. Then you don't
> have the 3 issues I mentioned. Calling a directC function is in line with the copy-in copy-out rules
> of Verilog tasks and functions, commonly understood by Verilog users.
> You solve the fanout/force problem, fanout occurs when the function returns, update occurs after the
> function returns.
> The transformation of the value between SV and C is done on the simulator side rather than by
> a C user function, the simulator knows how much he needs to allocate.
> And as a bonus this solution is always binary compatible if we agree on the canonical representation
> of the copyout value on the C side, which I think should be in a VPI vecval structure format.
> There is no C API function to manipulate the values that are passed.
>
> I would like to know what you think about these issues.
I agree. There's a proposal for copy-in and copy-out (pass-by-value) returns for SV types in
my document. Copy-out is limited to return values because that's the only pass-by-value output
of a C call.
BTW, if it is possible to call back into SV from C a lot of the extra functionality can be written in SV
rather than doing it in C anyway, e.g.:
SV:
module foo (input index);
integer index;
struct bar {
logic l;
bit b;
} arr[99:0]; /* big array of mixed type */
task update(int i,bar data)
begin
arr[i] = data;
end
endtask
export update;
function fetch(int i);
fetch = arr[i];
endfunction
export fetch;
extern context void eval();
always @ (clock) eval(index);
C:
typedef struct { /* canonical C version of SV struct */
svcInteger l;
unsigned int b;
}bar;
typedef struct { /* use context to save exported task/func */
void (*update)(bar);
bar (*fetch)(int);
}my_cntxt; /* bound at link time (not shown) */
void eval(handle ip,svcContext *pc,svcInteger index) {
my_cntxt *mc = (my_cntxt *)pc->user_context.ptr;
bar data = (*mc->fetch)(ip,index.value); /* get entry */
.... /* do something with data */
(*mc->update)(ip,data); /* write it back */
}
- no references/pointers need to be used for C to update the SV object, all
array access is done in SV. No PLI/VPI required other than the instance handle.
Regards,
Kev.
>
>
> Francoise
> '
>
> At 02:47 AM 12/7/2002 -0500, Andrzej Litwiniuk wrote:
>
>> Team,
>>
>> As promised, I'm sending the proposal for the C layer of the interface.
>>
>> The proposal is unfinished. This is the best I managed to do before
>> the deadline Friday midnight PST (technically speaking, I still have
>> 15 minutes :-)
>>
>> I hope this could be a base for a complete solution.
>>
>> Regards,
>> Andrzej
>>
>> ----------------------------------------------------------------------------
>>
>>
>> C layer of SV DirectC Interface
>> -------------------------------
>>
>> Preface.
>> ========
>>
>> The enclosed proposal addresses only restricted a subset of SV types.
>> I failed to find/invent a solution that would be efficient and general at
>> the same time.
>>
>> The proposed solution offers very efficient access, with no or very little
>> overhead, although only for restricted a subset of types.
>>
>> I believe that such approach seems to be justified by the requirements
>> for DirectC interface. Should a general solution be used, VPI or PLI
>> may serve as the last resort.
>>
>> Let me start with presenting the reasoning that has led to the proposed
>> solution.
>>
>> It's obvious that for each SV data type passed from SV to C, the matching
>> C type has to be used in C code, if that data is to be directly accessed
>> in C. An abstract access would also require a similar mapping, though perhaps
>> a simpler one.
>>
>> The DirectC interface or an implementation, or both, must define the mapping
>> from SV datypes onto C types. (The reverse mapping is by and large easier,
>> assuming the existance of generic pointers.)
>>
>> Consider the following structure:
>>
>> struct {
>> int a;
>> T b; /* T is a data type */
>> int C;
>> }
>>
>> A compiler cannot process such a type and determine the relative offset of 'b'
>> without knowing at least the size of 'T'.
>>
>> So, if the above structure is defined in SV, what should be the corresponding
>> data type to be used in C?
>>
>> The answer is simple and obvious if the type 'T' has a natural mapping onto
>> some C type, like the basic types 'int', 'byte' or 'real'.
>>
>> But what is the "natural mapping" for, say, 'logic [2:3][1:3][2:0] b [1:10]',
>> i.e. an unpacked 10-element array of packed array 2 by 3 by 3 bits, 4-state
>> each?
>> A single element of unpacked array is 18 bits; how many bytes or words
>> will it take to represent it? Verilog simulator very likely will use 2*3 bytes
>> per each 18-bit element of an array; C implementation would rather use 2 words
>> per element.
>>
>> So the key question is what to do with SV specific data types?
>>
>> A few solutions have been proposed, but none of them seems satisfactory
>> and working.
>>
>> For example, SV compiler might generate C types for SV types (similarly
>> to VCS generating headers for the external functions).
>> This would solve the problem of writing a matching declaration in C; such
>> declaration would be automatically generated.
>> (Note the use model: user will have to start with a SV definition and then
>> use a SV compiler in order to get the corresponding C definition.)
>> This would not help, however, with accessing a packed part of an element
>> in C code. (C does not support multi-indexing on bits level.)
>>
>> Another discussed solution is to standarize the representation.
>>
>> Any standarization of the internal data representation used by a simulator
>> will sacrify the performance.
>> May I here recall that we accepted 13c from "17 items"?
>> [ 13c:"The layout of 2- or 4-state vectors (i.e. packed structs and arrays)
>> is implementation- and platform- dependent. ]
>>
>> So instead we might standarize the representation visible to the C user,
>> by introducing something like avalue/bvalue known from PLI.
>> Actually, this idea is partly employed in my proposal.
>>
>> But again, what would be the "standard" representation for the example of array
>> considered above?
>>
>>
>> Yet another approach is to replace troublesome SV-specific data types
>> with a handle and use the abstract access mode to access such troublesome data
>> solely thru a handle.
>>
>> With that approach the structure
>>
>> 'struct { int a; T b; int c; }', where T is a troublesome data type
>>
>> would be seen in C as
>>
>> 'struct { int a; sv_handle b; int c; }'
>>
>> where sv_handle is a pointer to some descriptor of the value of type T.
>> A descriptor could be standarized or left to vendors.
>>
>> Unfortunately, this will not work for calling SV functions from C.
>> The actual arguments exist on the caller's side and it's the caller
>> who creates them. How would the caller in C code create a handle for SV data
>> object and how such object would be created in C?
>>
>> Sure, there may be some conceivable solutions. For example, if SV code exports
>> a function, then SV compiler might also provide constructor functions, to be
>> called from C, for creating handles for those involved types of arguments that
>> require a handle.
>> But such solution's going to be pretty complicated and the use model may got
>> confusing.
>>
>>
>> Either approach, i.e. a standarized API representation or a handle, will
>> require the translation between the two representations: the one used by
>> a simulator and the other one visible to C.
>> Such translation may be quite expensive, if an array of structures is passed.
>>
>> Let me summarize the downsides of the above solutions:
>>
>> - both approaches will impose an overhead
>> - standarized API representation will not help with complex SV types
>> - handle will not work for calling SV functions from C
>>
>>
>> Having said that, I propose a modest yet pragmatic solution.
>> (It's pragmatic because it's doable :-)
>>
>>
>> Overview of C layer
>> ===================
>>
>> Only restricted and 'normalized' SV specific types will be supported.
>>
>> For example, if 'logic [2:3][1:3][2:0] b [1:10]' is used in SV type,
>> it will have to be defined in C as if it were declared in SV in the following
>> normalized form: 'logic [17:0] b [0:9]'.
>> That practically means that the packed part must be linearized and the ranges
>> must be normalized.
>> (Well, perhaps the range normalization requirement could be relaxed.)
>>
>> By and large the actual arguments will be passed by reference, with a few
>> exceptions (e.g. small input arguments).
>>
>> There will be no copying of arguments (other than resulting from coercing)
>> Please recall item 10 from "17 items"!
>> [10a:"xf must not modify the values of its input args'
>> 10b:"The initial values of formal output args are unspecified and may be
>> implementation dependent."]
>>
>> The actual arguments passed by reference by and large will be passed as they are, without changing their representation from the one used by a simulator.
>> (Again, there are some exceptions, mainly for the 'open' alias 'unsized' arrays.)
>>
>> Therefore there will be no overhead on argument passing because no copying or
>> translation between different representations will be required.
>>
>> C data types will be directly accessible.
>>
>> SV specific types will be accessible both via the interface library functions,
>> what grants the compatibility, and directly thru pointers, what allows for
>> the simulator-specific tuning of the application.
>>
>> DirectC interface defines the canonical API representation of packed 2-state
>> and 4-state arrays. (Actually based on PLI's avalue/bvalue for 4-state.)
>>
>> Library functions will provide the translation between the representation
>> used in a simulator and the canonical API representation.
>>
>> 'Open' alias 'unsized' arrays will be accessible through the abstract access
>> mode, i.e. via the interface library functions.
>>
>> Depending on the data types used for the external (or exported) functions,
>> either binary level or C source level compatibility is granted.
>>
>> The binary level compatibility is granted for all data types that do not mix
>> C types with SV specific types, though either category separately is fine.
>> For example, if a formal argument type is a C structure or a packed array
>> of bit or logic, then binary level compatibility is granted.
>> On the other hand, if a packed structure is embedded in an unpacked data
>> type, then only a source level compatibility is granted.
>> (Binary level compatibility means that an application compiled for a given
>> platform shall work with every SV simulator on that platform.
>> Source level compatibility means that an application will have to be re-compiled
>> for each SV simulator and that the implementation specific definitions
>> will be required for the compilation.)
>>
>>
>>
>> Data type mapping
>> =================
>>
>> The basic SV data types will be represented as follows:
>>
>> int->int
>> real->double
>> shortreal->float
>>
>> etc. (Actually, LRM shall provide a complete list.)
>>
>> SV specific data types like packed bit and logic vectors, will be discussed
>> separately.
>>
>>
>> Argument passing
>> =================
>>
>> Actual arguments generally are passed by reference or by value and will
>> be directly accessible in C code.
>>
>> In some cases, defined separately, an argument will be passed by handle and will
>> be accessible via library functions (abstract acess mode).
>>
>> For the arguments passed by reference, their original simulator-specific
>> representation will be used and a reference to the original data object
>> will be passed.
>>
>> If an argument of type 'T' is passed by reference, than the formal argument
>> shall be of the type 'T *'.
>>
>> Output and inout arguments are passed by reference.
>>
>> Input arguments are passed by value or by reference, depending on the size.
>> 'Small' values of formal input arguments are passed by value.
>> The following data types are considered 'small':
>>
>> - char, byte, int, real, shortreal (have I omitted something?)
>> - pointer, string
>> - bit (i.e. 2-state) vectors up to 32-bit; canonical representation will
>> be used, similarly for function result
>>
>> Input arguments of other types are passed by reference.
>>
>>
>>
>> Include files
>> =================
>>
>> (All names are provisional and subject to discussion and improvment.)
>>
>> The C layer of SV DirectC interface defines two include files corresponding to
>> the two levels of compatibility (binary level and source code level):
>> - sverilog.h
>> - directc.h
>>
>> "sverilog.h" is fully defined by the interface and is implementation independent.
>> It defines the canonical API representation of 2-state (bit) and 4-state (logic)
>> values, defines the types used for passing references to SV data objects,
>> provides function headers and defines a number of helper macros and constants.
>>
>> "directc.h" provides implementation dependent definitions.
>> The contens of this file, i.e. what symbols are defined (constants, macros,
>> typedefs), is defined by the interface.
>> The actual definitions of the symbols, however, are implementation specific and
>> will be provided by the vendors.
>> "directc.h" defines data structures for implementation specific representation of
>> 2-state and 4-state vectors, as well as a number of helper macros and constants.
>>
>> User's applications that require "directc.h" file will be only source-level
>> compatible, i.e. they will have to be compiled with the version of "directc.h"
>> provided for a particular implementation of SV.
>>
>> The applications that use only "sverilog.h" will be binary compatible with
>> all SV simulators.
>>
>>
>> The values of C-like types may be directly accessed via the corresponding
>> C type definitions.
>>
>> The values of SV specific types, like packed arrays of bit or logic, may
>> be accessed via interface functions using the canonical representation of
>> 2-state and 4-state vectors.
>> They also may be directly accessed using the implementation representation.
>> The former mode will assure binary level compatibility, the later one
>> will allow for tool-specific performance oriented tuning of an application.
>>
>> There will be no confusion whether passing a particular data type
>> is binary compatible or only source level compatible.
>> The rule is straightforward: if a correcponding type definition can be
>> written in C without the need to include "directc.h" file, then the binary
>> compatibility is granted. Everything that requires "directc.h" is not binary
>> compatible and needs recompilation for each simulator of choice.
>>
>> Applications that pass solely C compatible data types or standalone packed arrays
>> (both 2-st and 4-st) will require only "sverilog.h" and therefore will be binary
>> compatible will all simulators.
>>
>> Applications that pass complex data types that contain at the same time
>> packed arrays and C-compatible types, will require also "directc.h" file and
>> therefore will not be binary compatible will all simulators. The source level
>> compatibility is, however, granted.
>>
>>
>>
>> "sverilog.h"
>> =================
>>
>> This file contains the following definitions:
>>
>> /* canonical API representation */
>>
>> #define sv_00
>> #define sv_11
>> #define sv_z2/* representation of 4-st scalar z */
>> #define sv_x3/* representation of 4-st scalar x */
>>
>> typedef unsigned char svBit;/* scalar */
>> typedef unsigned char svLogic;/* scalar */
>>
>> /* 2-state and 4-state vectors, modelled upon PLI's avalue/bvalue */
>> #define VEC32_NEEDED(WIDTH) (((WIDTH)+31)>>5)
>>
>> typedef unsigned int
>> svBitVec32;/* (a chunk of) packed bit array */
>>
>> typedef struct { unsigned int c; unsigned int d;}
>> svLogicVec32; /* (a chunk of) packed logic array */
>>
>> /* reference to a standalone packed array */
>> typedef void* svBitPackedArr;
>> typedef void* svLogicPackedArr;
>>
>> /* a handle to a generic object (actually, unsized array) */
>> typedef void* svHandle;
>>
>> /* functions for translation between simulator's and canonical representations */
>>
>> /* s=source, d=destination, w=width */
>>
>> /* actual <-- canonical */
>> void vcPutBitVec32 (svBitPackedArr d, svBitVec32* s, int w);
>> void vcPutLogicVec32 (svLogicPackedArr d, svLogicVec32* s, int w);
>>
>> /* canonical <-- actual */
>> void vcGetBitVec32 (svBitVec32* d, svBitPackedArr s, int w);
>> void vcGetLogicVec32 (svLogicVec32* d, svLogicPackedArr s, int w);
>>
>> The above functions copy the whole array in either direction. User is responsible for providing the correct width and for allocating an array in the canonical
>> representation.
>>
>> More functions may be added, for accesing individual bits or small (<=32)
>> part-selects.
>>
>> Similarly, functions for converting packed arrays into char* strings (for
>> printing) or other way round (for reading) may be provided, like in VCS
>> (cf. VCS DirectC.h).
>>
>>
>> "directc.h"
>> ===========
>>
>> This file provides implementation specific definitions.
>> In particular, it defines the macros for specifying variables representing
>> SV packed arrays:
>>
>> #define BIT_PACKED_ARRAY(WIDTH,NAME) ...
>> #define LOGIC_PACKED_ARRAY(WIDTH,NAME) ...
>>
>> (For example, VCS might defined the later macro as follows:
>> #define LOGIC_PACKED_ARRAY(WIDTH,NAME) vec32 NAME [ VEC32_NEEDED(WIDTH) ]
>> )
>>
>>
>> Example 1 - binary compatible application
>> =========================================
>>
>> SV:
>> typedef struct {int a; int b;} pair;
>> extern void foo(input int i1, pair i2, output logic [63:0] o3);
>>
>> C:
>> #include "sverilog.h"
>>
>> typedef struct {int a; int b;} pair;
>> void foo(int i1, pair *i2, svLogicPackedArr o3)
>> {
>> svLogicVec32 arr[VEC32_NEEDED(64)]; /* 2 chunks needed */
>>
>> printf("%d\n", i1);
>> arr[1].c = i2->a;
>> arr[1].d = 0;
>> arr[2].c = i2->b;
>> arr[2].d = 0;
>> vcPutLogicVec32 (o3, arr, 64);
>> }
>>
>>
>> Example 2 - source level compatible application
>> =========================================
>>
>> SV:
>> typedef struct {int a; bit [6:1][1:8] b [65:2]; int c;} triple;
>> // troublesome mix od C types and packed arrays
>> extern void foo(input triple i);
>>
>> C:
>> #include "sverilog.h"
>> #include "directc.h"
>>
>> typedef struct {
>> int a;
>> BIT_PACKED_ARRAY(6*8, b) [64];
>> int c;
>> } triple;
>>
>> /* Note that 'b' is defined as for 'bit [6*8-1:0] b [63:0]' */
>>
>> void foo(triple *i)
>> {
>> int j;
>> svBitVec32 arr[VEC32_NEEDED(6*8)]; /* 6*8 packed bits */
>>
>> printf("%d %d\n", i->a, i->c);
>> for (j=0; j<64; j++) {
>> vcGetBitVec32(arr, (svBitPackedArr)&(i->b[j]), 6*8);
>> ...
>> }
>> }
>>
>> Note that 'a', 'b', 'c' are directly accessed as fields in a structure.
>> In the case of 'b', which represents unpacked array of packed arrays,
>> individual element is accessed via library function vcGetBitVec32(),
>> by passing its address to the function.
>>
>> ---------------------------------------------------------------------------
>>
>>
>> This is all that I managed to describe before midnight PST, Friday.
>>
>>
>> The representation of open/unsized arrays and access to them (via handle,
>> abstract access mode) is still missing.
>>
>>
>> I suppose that the functions for accessing arrays may be modelled upon SV array
>> querying functions: $left, $right, $dimension, etc.
>
-- National Semiconductor, Tel: (408) 721 3251 2900 Semiconductor Drive, Mail Stop D3-500, Santa Clara, CA 95052-8090
This archive was generated by hypermail 2b28 : Wed Dec 11 2002 - 16:32:08 PST