diff --git a/doc/examples/README.rst b/doc/examples/README.rst index 921bcf2a38..17e2ea9bc9 100644 --- a/doc/examples/README.rst +++ b/doc/examples/README.rst @@ -3,10 +3,7 @@ Examples ######## -This sections contains advanced examples using specific features of the language, the tool, -or interaction with third-party projects. It is suggested for users who are new to either -`GHDL` or `VHDL` to read :ref:`USING:QuickStart` first. +It is suggested for users who are new to either `GHDL` or `VHDL` to read :ref:`USING:QuickStart` first. -.. toctree:: - - ../examples/VHPIDIRECT +Then :ref:`Examples:VHPIDIRECT` contains advanced examples using specific features of the language, the tool, +or interaction with third-party projects. diff --git a/doc/examples/quick_start/README.rst b/doc/examples/quick_start/README.rst index 7fb8f046a7..8e26361ca3 100644 --- a/doc/examples/quick_start/README.rst +++ b/doc/examples/quick_start/README.rst @@ -26,7 +26,8 @@ The following tips might be useful: * Use :option:`--ieee=synopsys <--ieee>` if your design depends on a non-standard implementation of the IEEE library. - * Use :option:`-fexplicit` and :option:`-frelaxed-rules` if needed. + * Use :option:`-fexplicit` and :option:`-frelaxed-rules` if needed. For instance when relaxing VHDL 2008's need for shared + variables to be protected types, you can use `--std=08 -frelaxed`. * Use :option:`--work=LIB_NAME <--work>` to analyze files into the ``LIB_NAME`` library. To use files analyzed to a different directory, give the path diff --git a/doc/examples/vhpidirect/Caccess/README.rst b/doc/examples/vhpidirect/Caccess/README.rst new file mode 100644 index 0000000000..77678793c2 --- /dev/null +++ b/doc/examples/vhpidirect/Caccess/README.rst @@ -0,0 +1,66 @@ +.. program:: ghdl +.. _VHPIDIRECT_Example:C_Access: + +`C Access` example +===================== + +The files +--------- + +The VHPIDIRECT method of accessing C-side variables and functions from VHDL requires these variables and functions to be declared +with the `foreign` attribute a certain way. It is set to highlight that the function has a foreign C-side definition, as per +:ref:`foreign_declarations`. + +The foreign functions can be declared HDL-side in a package as follows, or the declarative part of an architecture (as in :ref:`Examples:VHPIDIRECT:Demo`). +The difference being the scope of the declarations. + +This example starts with :file:`c_access.vhdl`: + +.. literalinclude:: cAccess.vhd + :language: vhdl + +Assuming, for now, that these foreign functions perform as their names indicate they should, the toplevel test bench is defined next (:file:`toplevel.vhdl`): + +.. literalinclude:: toplevel.vhd + :language: vhdl + +Perhaps this example's flow is clearer by now: the testbench will use an integer array from C, the size of which is defined in C. +It will use a C-side function to prompt the user to set the value for index zero and then print and adjust all of the other indices. +Then the user is prompted to conclude the simulation or repeat the process. + + +The C functions and variables that GHDL accesses are kept in a separate header/code pair. :file:`cSharedVar.h`: + +.. literalinclude:: cSharedVar.h + :language: c + +And :file:`cSharedVar.c`: + +.. literalinclude:: cSharedVar.c + :language: c + + +These are also exposed to the custom entry point in :file:`main.c`: + +.. literalinclude:: main.c + :language: c + +It is seen that the array's length is established, and its contents filled with square numbers, before the GHDL simulation happens. +After that, the array is read out. + +.. TIP:: + To pass GHDL runtime options, see the second hint under :ref:`Starting_a_simulation_from_a_foreign_program`. + +Compilation +----------- + +- Firstly, the HDL files must be analysed by GHDL, producing object files for each (:ref:`Analysis:command`). +- Before the elaboration step, the object files of all `.c` files being used are needed, so compile each with `gcc -c main.c -o main.o`. +- Elaborate with each C-side object file and finally the name of the toplevel entity. See :ref:`Starting_a_simulation_from_a_foreign_program` and :ref:`Linking_with_foreign_object_files`. + - The elaboration step is only possible with a GCC or LLVM backend. See :ref:`Elaboration:command`. +- Execute the produced executable. + +The compilation steps should look something like :file:`build.sh`: + +.. literalinclude:: build.sh + :language: sh \ No newline at end of file diff --git a/doc/examples/vhpidirect/Caccess/build.sh b/doc/examples/vhpidirect/Caccess/build.sh new file mode 100644 index 0000000000..901a07ffe9 --- /dev/null +++ b/doc/examples/vhpidirect/Caccess/build.sh @@ -0,0 +1,6 @@ +gcc -c cSharedVar.c -o cSharedVar.o && +gcc -c main.c -o main.o && +ghdl-llvm -a cAccess.vhd toplevel.vhd && +ghdl-llvm -e -Wl,main.o -Wl,cSharedVar.o toplevel +./toplevel +rm *.o work-obj*.cf toplevel diff --git a/doc/examples/vhpidirect/Caccess/cAccess.vhd b/doc/examples/vhpidirect/Caccess/cAccess.vhd new file mode 100644 index 0000000000..a2f26f532d --- /dev/null +++ b/doc/examples/vhpidirect/Caccess/cAccess.vhd @@ -0,0 +1,63 @@ +library ieee; +use ieee.std_logic_1164.all; + +package cAccess is + + type int_ptr is access integer; -- represented C-side with int + function c_intArrSize_ptr return int_ptr; -- represented C-side with int* + attribute foreign of c_intArrSize_ptr : + function is "VHPIDIRECT getIntArrSize"; -- getIntArrSize is the C-side function name + + shared variable c_sizeInt : int_ptr := c_intArrSize_ptr; + + type int_arr is array(0 to c_sizeInt.all-1) of integer; + type int_arr_ptr is access int_arr; -- represented C-side with int* + + + function c_intArr_ptr return int_arr_ptr; + attribute foreign of c_intArr_ptr : + function is "VHPIDIRECT getIntArr_ptr"; + procedure c_promptIndexValue(index: integer); + attribute foreign of c_promptIndexValue : + procedure is "VHPIDIRECT promptIndexValue"; + + shared variable c_intArr : int_arr_ptr := c_intArr_ptr; + + type char_ptr is access std_ulogic; -- represented C-side with char + function c_finished_ptr return char_ptr; + attribute foreign of c_finished_ptr : + function is "VHPIDIRECT getFinished_ptr"; + + procedure c_promptFinished; + attribute foreign of c_promptFinished : + procedure is "VHPIDIRECT promptFinished"; + + shared variable c_finished : char_ptr := c_finished_ptr; +end package cAccess; + +package body cAccess is + + function c_intArrSize_ptr return int_ptr is + begin + assert false report "c_intArrSize_ptr VHPI" severity failure; + end c_intArrSize_ptr; + + + function c_intArr_ptr return int_arr_ptr is + begin + assert false report "c_intArr_ptr VHPI" severity failure; + end c_intArr_ptr; + procedure c_promptIndexValue(index: integer) is + begin + assert false report "c_promptIndexValue VHPI" severity failure; + end c_promptIndexValue; + + function c_finished_ptr return char_ptr is + begin + assert false report "c_finished_ptr VHPI" severity failure; + end c_finished_ptr; + procedure c_promptFinished is + begin + assert false report "c_promptFinished VHPI" severity failure; + end c_promptFinished; +end package body cAccess; diff --git a/doc/examples/vhpidirect/Caccess/cSharedVar.c b/doc/examples/vhpidirect/Caccess/cSharedVar.c new file mode 100644 index 0000000000..dd1a7f2f71 --- /dev/null +++ b/doc/examples/vhpidirect/Caccess/cSharedVar.c @@ -0,0 +1,37 @@ +#include "cSharedVar.h" + +int* getIntArrSize(){ + return &sizeInt; +} + +int* getIntArr_ptr(){ + return intArray; +} + +void promptIndexValue(int index){ + char strIn[8]; + + printf("Enter a number for index %d: ", index); + fgets(strIn, sizeof strIn, stdin); + printf("\n"); + sscanf(strIn, "%d", &intArray[index]); +} + + +char* getFinished_ptr(){ + return &finishedChar; +} + +void promptFinished(){ + char strIn[3]; + + printf("Conclude simulation? (Y/n): "); + fgets(strIn, sizeof strIn, stdin); + printf("\n"); + if(strIn[0] == 'N' || strIn[0] == 'n'){ + finishedChar = VHDL_0; + } + else{ + finishedChar = VHDL_1; + } +} \ No newline at end of file diff --git a/doc/examples/vhpidirect/Caccess/cSharedVar.h b/doc/examples/vhpidirect/Caccess/cSharedVar.h new file mode 100644 index 0000000000..a3a6923704 --- /dev/null +++ b/doc/examples/vhpidirect/Caccess/cSharedVar.h @@ -0,0 +1,26 @@ +#include + +int sizeInt; +int* getIntArrSize(); + +int* intArray; +int* getintArray_ptr(); +void promptIndexValue(); + +char finishedChar; +char* getFinished_ptr(); +void promptFinished(); + +static const char VHDL_BIT_STATE[] = { 'U', 'X', '0', '1', 'Z', 'W', 'L', 'H', '-'}; + +enum VHDL_BIT_CHAR { +VHDL_U = 0, +VHDL_X = 1, +VHDL_0 = 2, +VHDL_1 = 3, +VHDL_Z = 4, +VHDL_W = 5, +VHDL_L = 6, +VHDL_H = 7, +VHDL_D = 8, +}; diff --git a/doc/examples/vhpidirect/Caccess/main.c b/doc/examples/vhpidirect/Caccess/main.c new file mode 100644 index 0000000000..bb5a8ef147 --- /dev/null +++ b/doc/examples/vhpidirect/Caccess/main.c @@ -0,0 +1,36 @@ +#include + +#include "cSharedVar.h" + +extern int ghdl_main(char argc, char* argv[]); + +int main(int argc, char const *argv[]) +{ + char strIn[3]; + printf("Enter the Integer Array length [1-9]: "); + fgets(strIn, sizeof strIn, stdin); + printf("\n"); + sscanf(strIn, "%d", &sizeInt); + + if(sizeInt < 1) + sizeInt = 1; + + intArray = malloc(sizeInt*sizeof(int)); + + for (int i = 0; i < sizeInt; i++) + { + intArray[i] = i*i; + } + + printf("ghdl_main return: %d\n", ghdl_main(0, NULL)); + printf("\n********************************\nghdl simulation completed.\n\n"); + + for (int i = 0; i < sizeInt; i++) + { + printf("intArray[%d] = %d\n", i, intArray[i]); + } + + return 0; +} + + diff --git a/doc/examples/vhpidirect/Caccess/toplevel.vhd b/doc/examples/vhpidirect/Caccess/toplevel.vhd new file mode 100644 index 0000000000..7675f43c3b --- /dev/null +++ b/doc/examples/vhpidirect/Caccess/toplevel.vhd @@ -0,0 +1,33 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +use work.cAccess.all; + +entity toplevel is +end entity toplevel; + +architecture RTL of toplevel is + +begin + + process + begin + report "array length: " & integer'image(c_intArr.all'length); + + c_promptIndexValue(0); + + for i in 1 to c_intArr.all'right loop + report "c_intArr[" & integer'image(i) &"] = " & integer'image(c_intArr.all(i)) & ". Set to: " & integer'image(2*c_intArr.all(i)); + c_intArr.all(i) := 2*c_intArr.all(i); + end loop; + + c_promptFinished; + if(c_finished.all = '1') then + wait; + end if; + report "c_finished = " & std_ulogic'image(c_finished.all); + end process; + + +end architecture RTL; diff --git a/doc/examples/VHPIDIRECT.rst b/doc/examples/vhpidirect/VHPIDIRECT.rst similarity index 73% rename from doc/examples/VHPIDIRECT.rst rename to doc/examples/vhpidirect/VHPIDIRECT.rst index f8eddf9112..248695f25a 100644 --- a/doc/examples/VHPIDIRECT.rst +++ b/doc/examples/vhpidirect/VHPIDIRECT.rst @@ -3,6 +3,16 @@ Data exchange through VHPIDIRECT ################################ +.. toctree:: + :hidden: + + Caccess/README + demo/README + +:ref:`VHPIDIRECT_Example:C_Access` is a straight forward example of executing the GHDL simulation from a personal entry point, while accessing variables (an int, int array and char). + +See :ref:`Examples:VHPIDIRECT:Demo` for a demo about how to pass different types of data to/from VHDL and C through VHPIDIRECT. + VUnit ===== @@ -16,3 +26,14 @@ ghdlex and netpp `ghdlex `_ is a set of C extensions to facilitate data exchange between a GHDL simulation and external applications. VHPIDIRECT mechanisms are used to wrap GHDL data types into structures usable from a C library. `ghdlex` uses the `netpp `_ library to expose virtual entities (such as pins or RAM) to the network. It also demonstrates simple data I/O through unix pipes. A few VHDL example entities are provided, such as a virtual console, FIFOs, RAM. The author of `netpp` and `ghdlex` is also working on `MaSoCist `_, a linux'ish build system for System on Chip designs, based on GHDL. It allows to handle more complex setup, e.g. how a RISC-V architecture (for example) is regress-tested using a virtual debug interface. + +SystemC and VHDL mixed simulation +================================= + +There is an example under `Demo of mixed vhdl + systemc simulation `_. + + +Examples Collection +=================== + +There is a collection of GHDL use cases under eine's `hwd-ide `_. \ No newline at end of file diff --git a/doc/examples/vhpidirect/demo/README.rst b/doc/examples/vhpidirect/demo/README.rst new file mode 100644 index 0000000000..5e4a21cdf2 --- /dev/null +++ b/doc/examples/vhpidirect/demo/README.rst @@ -0,0 +1,21 @@ +.. program:: ghdl +.. _Examples:VHPIDIRECT:Demo: + +VHPIDIRECT Demo +=============== + +The following sources show how to pass different types of data to/from VHDL and C through VHPIDIRECT. + +.. NOTE:: :file:`ghdl.h` is a reference of GHDL's ABI, which can be imported to easily convert data types. However, the ABI is not settled, so it might change without prior notice. + +.. literalinclude:: run.sh + :language: bash + +.. literalinclude:: tb.vhd + :language: vhdl + +.. literalinclude:: main.c + :language: c + +.. literalinclude:: ghdl.h + :language: c diff --git a/doc/examples/vhpidirect/demo/ghdl.h b/doc/examples/vhpidirect/demo/ghdl.h new file mode 100644 index 0000000000..4db4ce1909 --- /dev/null +++ b/doc/examples/vhpidirect/demo/ghdl.h @@ -0,0 +1,253 @@ +#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; +} bounds_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 { + bounds_t range; + uint8_t array[]; +} ghdl_AccNaturalDimArr_t; + +/* +* Print custom types +*/ + +void printAttributes(ghdl_NaturalDimArr_t* ptr, int dims) { + printf("array: %p\n", ptr->array); + printf("bounds: %p\n", ptr->bounds); + int i; + for(i = 0; i < dims; i++){ + printf("bounds[%d].left: %d\n", i+1, ptr->bounds[i].left); + printf("bounds[%d].right: %d\n", i+1, ptr->bounds[i].right); + printf("bounds[%d].dir: %d\n", i+1, ptr->bounds[i].dir); + printf("bounds[%d].len: %d\n", i+1, ptr->bounds[i].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[0].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; + + for (int i = 0; i < num; i++) + { + len[i] = ptr->bounds[num-i-1].len; + } +} + +/* +* 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) { + bounds_t *range = malloc(sizeof(bounds_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=range}; +} + +// @RocketRoss +/* +* Helper to setup the bounds_t for ghdlFromArray +*/ + +void ghdlSetRange(bounds_t* r, int len, bool reversed){ + if(!reversed){//to + r->left = 0; + r->right = len-1; + r->dir = 0; + r->len = len; + } + else{//downto + r->left = len-1; + r->right = 0; + r->dir = 1; + r->len = len; + } +} + +// @RocketRoss +/* +* Convert C types representing an unconstrained array with a dimension of type 'natural', to a fat pointer +*/ + +ghdl_NaturalDimArr_t ghdlFromPointer(void* vec, int* len, int dims) {//handled malloc'd pointer in freeCPointers() + bounds_t* b = malloc(sizeof(bounds_t)*dims); + assert(b != NULL); + + for (int i = 0; i < dims; i++) + { + ghdlSetRange(b+i, len[i], false); + } + + void *a = vec; + return (ghdl_NaturalDimArr_t){.array= a, .bounds=b}; +} + +ghdl_NaturalDimArr_t ghdlFromArray(void* vec, int* len, int dims, int sizeOfDataType) {//handled malloc'd pointer in freeCPointers() + bounds_t* b = malloc(sizeof(bounds_t)*dims); + int totalSize = 1; + for (int i = 0; i < dims; i++) + { + totalSize *= len[i]; + ghdlSetRange(b+i, len[i], false); + } + + void *a = malloc(sizeOfDataType * totalSize); + memcpy(a, vec, sizeOfDataType * totalSize); + + return (ghdl_NaturalDimArr_t){.array= a, .bounds=b}; +} + +/* +* Convert an access to an unconstrained string, to a (null terminated) C string +*/ + +char* ghdlAccToString(ghdl_AccNaturalDimArr_t *line) {//TODO Test //TODO handle malloc'd pointer + // 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) {//TODO handle malloc'd pointer + 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; +} + +/* +* Handle C char for the appropriate values in std_ulogic and std_logic. +*/ + +// @RocketRoss +static const char HDL_LOGIC_STATE[] = { 'U', 'X', '0', '1', 'Z', 'W', 'L', 'H', '-'}; + +enum HDL_LOGIC_CHAR { +HDL_U = 0, +HDL_X = 1, +HDL_0 = 2, +HDL_1 = 3, +HDL_Z = 4, +HDL_W = 5, +HDL_L = 6, +HDL_H = 7, +HDL_D = 8, +}; + +#endif diff --git a/doc/examples/vhpidirect/demo/main.c b/doc/examples/vhpidirect/demo/main.c new file mode 100644 index 0000000000..f50858d46d --- /dev/null +++ b/doc/examples/vhpidirect/demo/main.c @@ -0,0 +1,337 @@ +#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; + +int32_t* vec; +bounds_t* vec_bounds; +int32_t* mat; +bounds_t* mat_bounds; +int32_t* d3_ptr; +bounds_t* d3_bounds; +int* len; +int* len2; +int* len3; +bounds_t* string_bounds; +ghdl_AccNaturalDimArr_t* line; + +int getFlatArrayIndex(int* dimIndex, int* lens, int dims){ + if(dims == 1){ + return dimIndex[0]; + } + else{ + return dimIndex[dims-1] + (lens[dims-1]*getFlatArrayIndex(dimIndex, lens, dims-1)); + } +} + +void testCinterface( + char v_logic, + char v_ulogic, + 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, + ghdl_NaturalDimArr_t* v_mat_int, + ghdl_NaturalDimArr_t* v_3d_int +) { + assert(v_logic == HDL_H); + printf("v_logic : %c\n", HDL_LOGIC_STATE[v_logic]); + + assert(v_ulogic == HDL_Z); + printf("v_ulogic : %c\n", HDL_LOGIC_STATE[v_ulogic]); + + 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 : %ld\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' [%ld]\n", v_str->array, str, strlen(str)); + free(str); + + 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]); + + printf("\nVerify GHDL Matrix in C\n"); + printAttributes(v_mat_int, 2); + len2 = malloc(2 * sizeof(int)); + + int32_t* mat_int; + ghdlToArray(v_mat_int, (void**)&mat_int, len2, 2); + for (int i = 0; i < len2[0]; i++) + { + for (int j = 0; j < len2[1]; j++) + { + int ind[] = {i, j}; + int flatIndex = getFlatArrayIndex(ind, len2, 2); + printf("C assert: %d == (val: %d) @ [%d,%d](%d)\n", 11*(flatIndex+1), mat_int[flatIndex], i, j, flatIndex); + assert(mat_int[flatIndex] == 11*(flatIndex+1)); + } + } + printf("v_mat_int : %p [%d,%d]\n\n", mat_int, len2[0], len2[1]); + + printf("\nVerify the 3D GHDL array in C\n"); + printAttributes(v_3d_int, 3); + len3 = malloc(3 * sizeof(int)); + + int32_t* d3_int; + ghdlToArray(v_3d_int, (void**)&d3_int, len3, 3); + for(int i = 0; i < len3[0]; i++) + { + for (int j = 0; j < len3[1]; j++) + { + for (int k = 0; k < len3[2]; k++) + { + int ind[] = {i, j, k}; + int flatIndex = getFlatArrayIndex(ind, len3, 3); + printf("C assert: %d == (val: %d) @ [%d,%d,%d](%d)\n", 11*(flatIndex+1), d3_int[flatIndex], i, j, k, flatIndex); + assert(d3_int[flatIndex] == 11*(flatIndex+1)); + } + } + } + printf("v_3d_int : %p [%d,%d,%d]\n\n", d3_int, len3[0], len3[1], len3[2]); + + printf("end testCinterface\n\n"); +} + +void freePointers(){ + free(vec); + free(vec_bounds); + free(mat); + free(mat_bounds); + free(d3_ptr); + free(d3_bounds); + free(string_bounds); + free(line); + free(len); + free(len2); + free(len3); +} + +void getString(ghdl_NaturalDimArr_t* ptr) { + if(string_bounds != NULL){//this handles a second call//this handles a second call + free(string_bounds); + } + *ptr = ghdlFromString("HELLO WORLD"); + string_bounds = ptr->bounds; +} + +void getIntVec(ghdl_NaturalDimArr_t *ptr) {//Notice how similar this is to getIntMat() which is supposedly a 2D array (it is also actually just a flat (1D) array) + vec = malloc(2*3*sizeof(int32_t)); + int32_t len[1] = {2*3}; + int x, y; + for ( x=0 ; x<2 ; x++ ) { + for ( y=0 ; y<3 ; y++ ) { + int flatIndex = x*3+y; + vec[flatIndex] = 11*(flatIndex+1); + } + } + + *ptr = ghdlFromPointer((void *)vec, len, 1); + vec_bounds = ptr->bounds; + assert(ptr->array == vec); + + printf("\n1D Array values [%d]:\n", len[0]); + for ( x=0 ; xarray)[x]); + assert(vec[x] == ((int32_t*)ptr->array)[x]); + } + +} + +void getIntMat(ghdl_NaturalDimArr_t* ptr){ + mat = malloc(2*3*sizeof(int32_t)); + int32_t len[2] = {2, 3}; + int x, y, ind[2]; + for ( x=0 ; xbounds; + printf("\n2D Array values [%d,%d]:\n", len[0], len[1]); + for ( x=0 ; xarray)[flatIndex]); + } + printf("\n"); + } + +} + +void getInt3d(ghdl_NaturalDimArr_t* ptr){ + int32_t d3[2][4][3]; + int32_t len[3] = {2, 4, 3}; + int x, y, z, ind[3]; + for ( x=0 ; xarray; + d3_bounds = ptr->bounds; + printf("\n3D Array values [%d,%d,%d]:\n", len[0], len[1], len[2]); + for ( x=0 ; xarray)[flatIndex]); + } + printf("\n"); + } + printf("\n"); + } +} + +ghdl_AccNaturalDimArr_t* getLine() { + if(line != NULL){//this handles a second call + free(line); + } + line = ghdlAccFromString("HELLO WORLD"); + return line; +} + +int getLogicIntValue(char logic){ + return 0 + logic; +} \ No newline at end of file diff --git a/doc/examples/vhpidirect/demo/run.sh b/doc/examples/vhpidirect/demo/run.sh new file mode 100644 index 0000000000..282b42fd5b --- /dev/null +++ b/doc/examples/vhpidirect/demo/run.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env sh + +cd "$(dirname $0)" + +set -e + +ghdl -a -O0 -g tb.vhd +ghdl -e -O0 -g -Wl,-I./ -Wl,main.c tb && +./tb +#valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose --log-file=valgrind-out.txt ./tb && +#cat valgrind-out.txt | grep -A 4 "LEAK SUMMARY" diff --git a/doc/examples/vhpidirect/demo/tb.vhd b/doc/examples/vhpidirect/demo/tb.vhd new file mode 100644 index 0000000000..61f44970f0 --- /dev/null +++ b/doc/examples/vhpidirect/demo/tb.vhd @@ -0,0 +1,197 @@ +use std.textio.line; + +library ieee; +use ieee.std_logic_1164.all; + +entity tb is +end; + +architecture arch of tb is + + type rec_t is record + r_char: character; + r_int : integer; + end record; + + type enum_t is (standby, start, busy, done); + + type int_vec_t is array(natural range <>) 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; + + type int_2vec_t is array(natural range <>, natural range <>) of integer; + type int_3vec_t is array(natural range <>, natural range <>, natural range <>) of integer; +begin + process + + procedure testCinterface( + v_logic : std_logic := 'H'; + v_ulogic : std_ulogic := 'Z'; + 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)); + v_mat_int : int_2vec_t := ((11, 22, 33), (44, 55, 66)); + v_3d_int : int_3vec_t := ( ((11, 22, 33), (44, 55, 66)), ((77, 88, 99), (110, 121, 132)) ) + ) 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 getIntMat return int_2vec_t is + begin assert false report "VHPIDIRECT getIntMat" severity failure; end; + attribute foreign of getIntMat : function is "VHPIDIRECT getIntMat"; + + function getInt3d return int_3vec_t is + begin assert false report "VHPIDIRECT getInt3d" severity failure; end; + attribute foreign of getInt3d : function is "VHPIDIRECT getInt3d"; + + 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; + constant g_int_mat: int_2vec_t := getIntMat; + constant g_int_3d: int_3vec_t := getInt3d; + + variable g_line: line := getLine; + + function getLogicValue(logic : std_logic) return integer is + begin assert false report "VHPIDIRECT getLogicValue" severity failure; end; + attribute foreign of getLogicValue : function is "VHPIDIRECT getLogicIntValue"; + + function getUlogicValue(logic : std_ulogic) return integer is + begin assert false report "VHPIDIRECT getUlogicValue" severity failure; end; + attribute foreign of getUlogicValue : function is "VHPIDIRECT getLogicIntValue"; + + function getBitValue(bitVal : bit) return integer is + begin assert false report "VHPIDIRECT getBitValue" severity failure; end; + attribute foreign of getBitValue : function is "VHPIDIRECT getLogicIntValue"; + + procedure freeCPointers is + begin assert false report "VHPIDIRECT freeCPointers" severity failure; end; + attribute foreign of freeCPointers : procedure is "VHPIDIRECT freePointers"; + + variable spareInt: integer; + begin + + testCinterface( + v_logic => 'H', + v_ulogic => 'Z', + 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)), + v_mat_int => ((11, 22, 33), (44, 55, 66)), + v_3d_int => ( ((11, 22, 33), (44, 55, 66)), ((77, 88, 99), (110, 121, 132)) ) + ); + + 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;--g_str results from calling getString(), calling it again means a malloc'd pointer can be lost. + + 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;--g_line results from calling getLine(), calling it again means a malloc'd pointer can be lost. + assert getLine.all = "HELLO WORLD" severity failure; + + assert 0 = getLogicValue('U') severity error; + assert 1 = getLogicValue('X') severity error; + assert 2 = getLogicValue('0') severity error; + assert 3 = getLogicValue('1') severity error; + assert 4 = getLogicValue('Z') severity error; + assert 5 = getLogicValue('W') severity error; + assert 6 = getLogicValue('L') severity error; + assert 7 = getLogicValue('H') severity error; + assert 8 = getLogicValue('-') severity error; + + assert 0 = getUlogicValue('U') severity error; + assert 1 = getUlogicValue('X') severity error; + assert 2 = getUlogicValue('0') severity error; + assert 3 = getUlogicValue('1') severity error; + assert 4 = getUlogicValue('Z') severity error; + assert 5 = getUlogicValue('W') severity error; + assert 6 = getUlogicValue('L') severity error; + assert 7 = getUlogicValue('H') severity error; + assert 8 = getUlogicValue('-') severity error; + + assert 0 = getBitValue('0') severity error; + assert 1 = getBitValue('1') severity error; + + spareInt := 0; + report "g_int_mat'length: " & integer'image(g_int_mat'length) severity note; + for i in g_int_mat'range(1) loop + for j in g_int_mat'range(2) loop + spareInt := spareInt + 1; + report "Asserting Mat [" & integer'image(i) & "," & integer'image(j) & "]: " & integer'image(g_int_mat(i, j)) severity note; + assert g_int_mat(i, j) = 11*spareInt severity error; + end loop ; + end loop ; + + spareInt := 0; + report "g_int_3d'length: " & integer'image(g_int_3d'length) severity note; + for i in g_int_3d'range(1) loop + for j in g_int_3d'range(2) loop + for k in g_int_3d'range(3) loop + spareInt := spareInt + 1; + report "Asserting 3D [" & integer'image(i) & "," & integer'image(j) & "," & integer'image(k) & "]: " & integer'image(g_int_3d(i, j, k)) severity note; + assert g_int_3d(i, j, k) = 11*spareInt severity error; + end loop; + end loop ; + end loop ; + + freeCPointers; + report "No errors/failures. Concluding testbench." severity note; + wait; + end process; +end; diff --git a/doc/using/Foreign.rst b/doc/using/Foreign.rst index cdbeeace38..679e4d7c8b 100644 --- a/doc/using/Foreign.rst +++ b/doc/using/Foreign.rst @@ -24,6 +24,7 @@ You can define a subprogram in a foreign language (such as `C` or inspect the hierarchy, set callbacks and/or assign signals. GHDL does not support VHPI. For these kind of features, it is suggested to use VPI instead (see :ref:`VPI_build_commands`). +.. _foreign_declarations: .. ATTENTION:: As a consequence of the runtime copyright, you are not allowed to distribute an executable produced by GHDL without allowing access to the VHDL sources. See :ref:`INTRO:Copyrights`.