Subject: DirectC: C layer
From: Andrzej Litwiniuk (Andrzej.Litwiniuk@synopsys.com)
Date: Fri Dec 06 2002 - 23:47:29 PST
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_0 0
#define sv_1 1
#define sv_z 2 /* representation of 4-st scalar z */
#define sv_x 3 /* 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.
This archive was generated by hypermail 2b28 : Fri Dec 06 2002 - 23:48:06 PST