diff --git a/doc/vhpidirect/examples/cinterface.rst b/doc/vhpidirect/examples/cinterface.rst new file mode 100644 index 00000000..7cd21226 --- /dev/null +++ b/doc/vhpidirect/examples/cinterface.rst @@ -0,0 +1,10 @@ +.. program:: ghdl +.. _COSIM:VHPIDIRECT:Examples:cinterface: + +C interface +########### + +:cosimtree:`demo ` +********************************************** + +This example is a reference and a test suite for the helper C headers provided in :cosimtree:`vhpidirect`. These headers are a reference of GHDL's ABI, and can be imported to easily convert non-trivial data types between C and VHDL. However, the ABI is not settled, so it might change without prior notice. diff --git a/doc/vhpidirect/examples/index.rst b/doc/vhpidirect/examples/index.rst index 2edaac05..4942b536 100644 --- a/doc/vhpidirect/examples/index.rst +++ b/doc/vhpidirect/examples/index.rst @@ -8,4 +8,5 @@ Examples quickstart shared arrays + cinterface other diff --git a/test.py b/test.py index c1892e91..113194ec 100644 --- a/test.py +++ b/test.py @@ -85,5 +85,8 @@ def test_vhpidirect_arrays_matrices_vunit_axis_vcs(self): def test_vhpidirect_arrays_matrices_framebuffer(self): check_call(self.shell + [str(self.vhpidirect / 'arrays' / 'matrices' / 'framebuffer' / 'run.sh')], shell=True) + def test_vhpidirect_cinterface_demo(self): + check_call([executable, str(self.vhpidirect / 'cinterface' / 'demo' / 'run.sh')], shell=True) + def test_vpi_quickstart(self): check_call(self.shell + [str(self.vpi / 'quickstart' / 'run.sh')], shell=True) diff --git a/vhpidirect/cinterface/demo/main.c b/vhpidirect/cinterface/demo/main.c new file mode 100644 index 00000000..b4e0c478 --- /dev/null +++ b/vhpidirect/cinterface/demo/main.c @@ -0,0 +1,160 @@ +#include +#include +#include +#include +#include +#include + +#include + +typedef struct rec_t { + char r_char; + int32_t r_int; +} rec_t; + +typedef enum {standby, start, busy, done} enum_t; + +void testCinterface( + char v_char, + int32_t v_int, + uint32_t v_nat, + uint32_t v_pos, + double v_real, + bool v_bool, + bool v_bit, + int64_t v_time, + rec_t* v_rec, + uint8_t v_enum, + ghdl_NaturalDimArr_t* v_str, + ghdl_NaturalDimArr_t* v_vec_int, + ghdl_NaturalDimArr_t* v_vec_real, + ghdl_NaturalDimArr_t* v_vec_bool, + ghdl_NaturalDimArr_t* v_vec_bit, + ghdl_NaturalDimArr_t* v_vec_phy, + ghdl_NaturalDimArr_t* v_vec_rec, + ghdl_NaturalDimArr_t* v_vec_enum, + ghdl_NaturalDimArr_t* v_2vec_real +) { + assert(v_char == 'k'); + printf("v_char : %c\n", v_char); + + assert(v_int == -6); + printf("v_int : %d\n", v_int); + + assert(v_nat == 9); + printf("v_nat : %d\n", v_nat); + + assert(v_pos == 3); + printf("v_pos : %d\n", v_pos); + + assert(v_real == 3.34); + printf("v_real : %f\n", v_real); + + assert(v_bool == true); + printf("v_bool : %d\n", v_bool); + + assert(v_bit == true); + printf("v_bit : %d\n", v_bit); + + assert(v_time == 20e6); + printf("v_time : %d\n", v_time); + + assert(v_rec != NULL); + assert(v_rec->r_char == 'y'); + assert(v_rec->r_int == 5); + printf("v_rec : %p %c %d\n", v_rec, v_rec->r_char, v_rec->r_int); + + assert(v_enum == busy); + printf("v_enum : %d %d\n", v_enum, busy); + + char* str = ghdlToString(v_str); + printf("v_str : %p '%s' [%d]\n", v_str->array, str, strlen(str)); + + int* len = malloc(2 * sizeof(int)); + + int32_t* vec_int; + ghdlToArray(v_vec_int, (void**)&vec_int, len, 1); + assert(vec_int[0] == 11); + assert(vec_int[1] == 22); + assert(vec_int[2] == 33); + assert(vec_int[3] == 44); + assert(vec_int[4] == 55); + printf("v_vec_int : %p [%d]\n", vec_int, len[0]); + + double* vec_real; + ghdlToArray(v_vec_real, (void**)&vec_real, len, 1); + assert(vec_real[0] == 0.5); + assert(vec_real[1] == 1.75); + assert(vec_real[2] == 3.33); + assert(vec_real[3] == -0.125); + assert(vec_real[4] == -0.67); + assert(vec_real[5] == -2.21); + printf("v_vec_real : %p [%d]\n", vec_real, len[0]); + + bool* vec_bool; + ghdlToArray(v_vec_bool, (void**)&vec_bool, len, 1); + assert(vec_bool[0] == 0); + assert(vec_bool[1] == 1); + assert(vec_bool[2] == 1); + assert(vec_bool[3] == 0); + printf("v_vec_bool : %p [%d]\n", vec_bool, len[0]); + + bool* vec_bit; + ghdlToArray(v_vec_bit, (void**)&vec_bit, len, 1); + assert(vec_bit[0] == 1); + assert(vec_bit[1] == 0); + assert(vec_bit[2] == 1); + assert(vec_bit[3] == 0); + printf("v_vec_bit : %p [%d]\n", vec_bit, len[0]); + + int64_t* vec_phy; + ghdlToArray(v_vec_phy, (void**)&vec_phy, len, 1); + assert(vec_phy[0] == 1e6); + assert(vec_phy[1] == 50e3); + assert(vec_phy[2] == 1.34e9); + printf("v_vec_phy : %p [%d]\n", vec_phy, len[0]); + + rec_t* vec_rec; + ghdlToArray(v_vec_rec, (void**)&vec_rec, len, 1); + assert(vec_rec[0].r_char == 'x'); + assert(vec_rec[0].r_int == 17); + assert(vec_rec[1].r_char == 'y'); + assert(vec_rec[1].r_int == 25); + printf("v_vec_rec : %p [%d]\n", vec_rec, len[0]); + + uint8_t* vec_enum; + ghdlToArray(v_vec_enum, (void**)&vec_enum, len, 1); + assert(vec_enum[0] == start); + assert(vec_enum[1] == busy); + assert(vec_enum[2] == standby); + printf("v_vec_enum : %p [%d]\n", vec_enum, len[0]); + + double* vec2_real_base; + ghdlToArray(v_2vec_real, (void**)&vec2_real_base, len, 2); + double (*vec2_real)[len[0]] = (double(*)[len[0]])vec2_real_base; + assert(vec2_real[0][0] == 0.1); + assert(vec2_real[0][1] == 0.25); + assert(vec2_real[0][2] == 0.5); + assert(vec2_real[1][0] == 3.33); + assert(vec2_real[1][1] == 4.25); + assert(vec2_real[1][2] == 5.0); + printf("v_2vec_real : %p [%d, %d]\n", vec_enum, len[1], len[0]); +} + +void getString(ghdl_NaturalDimArr_t* ptr) { + *ptr = ghdlFromString("HELLO WORLD"); +} + +void getIntVec(ghdl_NaturalDimArr_t* ptr) { + int32_t vec[6] = {11, 22, 33, 44, 55}; + int32_t len[1] = {5}; + int x; + for ( x=0 ; x) of integer; + type real_vec_t is array(natural range <>) of real; + type bool_vec_t is array(natural range <>) of boolean; + type time_vec_t is array(natural range <>) of time; + type rec_vec_t is array(natural range <>) of rec_t; + type enum_vec_t is array(natural range <>) of enum_t; + + type real_2vec_t is array (natural range <>, natural range <>) of real; + +begin + process + + procedure testCinterface( + v_char : character := 'k'; + v_int : integer := -6; + v_nat : natural := 9; + v_pos : positive := 3; + v_real : real := 3.34; + v_bool : boolean := true; + v_bit : bit := '1'; + v_time : time := 20 ns; + v_rec : rec_t := ('y', 5); + v_enum : enum_t := busy; + v_str : string := "hellostr"; + v_vec_int : int_vec_t := (11, 22, 33, 44, 55); + v_vec_real : real_vec_t := (0.5, 1.75, 3.33, -0.125, -0.67, -2.21); + v_vec_bool : bool_vec_t := (false, true, true, false); + v_vec_bit : bit_vector := ('1', '0', '1', '0'); + v_vec_time : time_vec_t := (1 ns, 50 ps, 1.34 us); + v_vec_rec : rec_vec_t := (('x', 17),('y', 25)); + v_vec_enum : enum_vec_t := (start, busy, standby); + v_2vec_real : real_2vec_t := ((0.1, 0.25, 0.5),(3.33, 4.25, 5.0)) + ) is + begin assert false report "VHPIDIRECT testCinterface" severity failure; end; + attribute foreign of testCinterface : procedure is "VHPIDIRECT testCinterface"; + + function getString return string is + begin assert false report "VHPIDIRECT getString" severity failure; end; + attribute foreign of getString : function is "VHPIDIRECT getString"; + + function getIntVec return int_vec_t is + begin assert false report "VHPIDIRECT getIntVec" severity failure; end; + attribute foreign of getIntVec : function is "VHPIDIRECT getIntVec"; + + function getLine return line is + begin assert false report "VHPIDIRECT getLine" severity failure; end; + attribute foreign of getLine : function is "VHPIDIRECT getLine"; + + constant g_str: string := getString; + constant g_int_vec: int_vec_t := getIntVec; + + variable g_line: line := getLine; + + begin + + testCinterface( + v_char => 'k', + v_int => -6, + v_nat => 9, + v_pos => 3, + v_real => 3.34, + v_bool => true, + v_bit => '1', + v_time => 20 ns, + v_rec => ('y', 5), + v_enum => busy, + v_str => "hellostr", + v_vec_int => (11, 22, 33, 44, 55), + v_vec_real => (0.5, 1.75, 3.33, -0.125, -0.67, -2.21), + v_vec_bool => (false, true, true, false), + v_vec_bit => ('1', '0', '1', '0'), + v_vec_time => (1 ns, 50 ps, 1.34 us), + v_vec_rec => (('x', 17),('y', 25)), + v_vec_enum => (start, busy, standby), + v_2vec_real => ((0.1, 0.25, 0.5),(3.33, 4.25, 5.0)) + ); + + report "g_str'length: " & integer'image(g_str'length) severity note; + if g_str'length /= 0 then + report "g_str: " & g_str severity note; + end if; + report "string: " & getString severity note; + + report "g_int_vec'length: " & integer'image(g_int_vec'length) severity note; + for x in g_int_vec'range loop + report integer'image(x) & ": " & integer'image(g_int_vec(x)) severity note; + assert g_int_vec(x) = 11*(x+1) severity warning; + end loop; + + report "g_line: " & g_line.all severity note; + report "getLine: " & getLine.all severity note; + assert getLine.all = "HELLO WORLD" severity failure; + + wait; + end process; +end; diff --git a/vhpidirect/ghdl.h b/vhpidirect/ghdl.h new file mode 100644 index 00000000..23e06fb9 --- /dev/null +++ b/vhpidirect/ghdl.h @@ -0,0 +1,224 @@ +#ifndef GHDL_TYPES_H +#define GHDL_TYPES_H + +#include +#include +#include +#include + +// Range/bounds of a dimension of an unconstrained array with dimensions of type 'natural' +typedef struct { + int32_t left; + int32_t right; + int32_t dir; + int32_t len; +} range_t; + +// Range/bounds of an unconstrained array with 1, 2 or 3 dimensions of type 'natural' +typedef struct { + range_t dim_1; +} bounds_t; +typedef struct { + range_t dim_1; + range_t dim_2; +} bounds2D_t; +typedef struct { + range_t dim_1; + range_t dim_2; + range_t dim_3; +} bounds3D_t; + +// Unconstrained array with dimensions of type 'natural' +typedef struct { + void* array; + bounds_t* bounds; +} ghdl_NaturalDimArr_t; + +// Access to an unconstrained array with 1 dimension of type 'natural' +typedef struct { + range_t range; + uint8_t array[]; +} ghdl_AccNaturalDimArr_t; + +/* +* Print custom types +*/ + +void print(ghdl_NaturalDimArr_t* ptr) { + printf("array: %p\n", ptr->array); + printf("bounds: %p\n", ptr->bounds); + printf("bounds.left: %d\n", ptr->bounds->dim_1.left); + printf("bounds.right: %d\n", ptr->bounds->dim_1.right); + printf("bounds.dir: %d\n", ptr->bounds->dim_1.dir); + printf("bounds.len: %d\n", ptr->bounds->dim_1.len); +} + +/* +* Convert a fat pointer of an unconstrained string, to a (null terminated) C string +*/ + +// @umarcor +char* ghdlToString(ghdl_NaturalDimArr_t* ptr) { + assert(ptr != NULL); + assert(ptr->bounds != NULL); + int len = ptr->bounds->dim_1.len; + char* str = malloc(sizeof(char) * len + 1); + strncpy(str, ptr->array, len); + str[len] = '\0'; + return str; +} + +// In the prototype, Bradley declares a value instead of a reference. Why? + +// @bradleyharden +/* +char* ghdl_array_to_string(array_t array) { + // Add a null character, because GHDL strings are not null-terminated + char *string = malloc(array.range->len + 1); + strncpy(string, array.array, array.range->len); + string[array.range->len] = '\0'; + return string; +} +*/ + +/* +* Convert a fat pointer of an uncontrained array with (up to 3) dimensions of type 'natural', to C types +*/ + +void ghdlToArray(ghdl_NaturalDimArr_t* ptr, void** vec, int* len, int num) { + assert(ptr != NULL); + assert(ptr->bounds != NULL); + *vec = ptr->array; + + void* b = ptr->bounds; + switch (num) { + case 1: + len[0] = ((bounds_t*)b)->dim_1.len; + break; + case 2: + len[0] = ((bounds2D_t*)b)->dim_2.len; + len[1] = ((bounds2D_t*)b)->dim_1.len; + break; + case 3: + len[0] = ((bounds3D_t*)b)->dim_3.len; + len[1] = ((bounds3D_t*)b)->dim_2.len; + len[2] = ((bounds3D_t*)b)->dim_1.len; + break; + } +} + +/* +* Convert a (null terminated) C string, to a fat pointer of an unconstrained string +*/ + +// @umarcor +/* +ghdl_NaturalDimArr_t* ghdlFromString(char* str) { + uint32_t len = strlen(str); + ghdl_NaturalDimArr_t* ptr = malloc(sizeof(ghdl_NaturalDimArr_t)); + ptr->array = malloc(sizeof(char) * len); + strncpy((char*)(ptr->array), str, len); + ptr->bounds = malloc(sizeof(bounds_t)); + bounds_t* b = ptr->bounds; + b->dim_1.left = 1; + b->dim_1.right = len; + b->dim_1.dir = 0; + b->dim_1.len = len; + return ptr; +} +*/ + +// Again, the prototype I had (above) returns a reference instead of a value (Bradley's below) + +// @bradleyharden +ghdl_NaturalDimArr_t ghdlFromString(char *string) { + range_t *range = malloc(sizeof(range_t)); + assert(range != NULL); + uint32_t len = strlen(string); + range->left = 1; + range->right = len; + range->dir = 0; + range->len = len; + // Don't bother copying the string, because GHDL will do that anyway + return (ghdl_NaturalDimArr_t){.array=string, .bounds=(bounds_t*)range}; +} + +/* +* Convert C types representing an unconstrained array with a dimension of type 'natural', to a fat pointer +*/ + +ghdl_NaturalDimArr_t ghdlFromArray(void* vec, int* len, int num) { + bounds_t* b = malloc(sizeof(bounds_t)); + assert(b != NULL); + switch (num) { + case 3: + // TODO + case 2: + // TODO + case 1: + b->dim_1.left = 0; + b->dim_1.right = len[0]-1; + b->dim_1.dir = 0; + b->dim_1.len = len[0]; + } + return (ghdl_NaturalDimArr_t){.array=vec, .bounds=b}; +} + +/* +* Convert an access to an unconstrained string, to a (null terminated) C string +*/ + +char* ghdlAccToString(ghdl_AccNaturalDimArr_t *line) { + // Add a null character, because GHDL strings are not null-terminated + char *string = malloc(line->range.len + 1); + strncpy(string, line->array, line->range.len); + string[line->range.len] = '\0'; +} + +/* +* Convert C types representing an unconstrained array with a dimension of type 'natural', to an access +*/ + +// TODO: support 2 and 3 dimensions +ghdl_AccNaturalDimArr_t* ghdlAccFromArray(uint32_t length, size_t bytes) { + ghdl_AccNaturalDimArr_t *access = malloc(sizeof(ghdl_AccNaturalDimArr_t) + length * bytes); + assert(access != NULL); + access->range.left = 0; + access->range.right = length - 1; + access->range.dir = 0; + access->range.len = length; + return access; +} + +/* +* Convert a (null terminated) C string, to an access to an unconstrained string +*/ + +/* +// @umarcor +ghdl_AccNaturalDimArr_t* ghdlLineFromString(char *str) { + uint32_t len = strlen(str); + ghdl_AccNaturalDimArr_t *line = malloc(sizeof(ghdl_AccNaturalDimArr_t) + sizeof(char) * len); + line->bounds.left = 1; + line->bounds.right = len; + line->bounds.dir = 0; + line->bounds.len = len; + strncpy(line->array, str, len); + return line; +} +*/ + +// @bradleyharden +ghdl_AccNaturalDimArr_t* ghdlAccFromString(char *string) { + uint32_t length = strlen(string); + ghdl_AccNaturalDimArr_t *line = ghdlAccFromArray(length, 1); + // New access objects default to numbering from 0, + // but VHDL strings must be numbered from 1 + line->range.left++; + line->range.right++; + // Don't copy the null termination + strncpy(line->array, string, length); + return line; +} + +#endif