Once we have some Vult code written it’s time to generate some C/C++ code and run it on a target.
If you have followed the installation steps show in https://github.com/modlfo/vult you will have an executable called
vultc (if you compiled it by yourself it will be
vultc.byte). This is a simple command line application that we will use to generate the code.
Here is the full code of the oversampled lowpass filter which we are gonna save in a file called
Next we are gonna call the Vult compiler as follows:
$ ./vultc -ccode filter.vult
The compiler receives the flag
-ccode which instruct the compiler to generate C/C++ code. This command will print to the standard output the generated code. If we want to save it to a file we have to call the compiler as follows:
$ ./vultc -ccode filter.vult -o filter
This will generate three files:
filter_tables.h. Vult generates many auxiliary functions and types. For each function with memory (for example a function called
foo in the file
Bar.vult) there’s gonna be:
In the case of the filter example the names are:
To use this code in a file you need to:
In order to compile this code you need to add an include directory pointing to the location of the file
vultin.h. This file is located in the source tree under the folder
runtime. To link you will need to compile and link the file
vultin.c which is located in the same place (https://github.com/modlfo/vult/tree/master/runtime)
One thing to notice is that every function with memory will have as first argument a reference to a value of it’s corresponding type. In the above case, the function
Filter_lowpass_2x returns only one value, therefore the C/C++ will return a value. If the functions return more than one value Vult will automatically generate structures for the types. Here are few Vult functions an their corresponding C/C++ functions:
C/C++ declarations of the functions above:
In the case of functions returning multiple values, the context type is always generated. In order to get the values from the context we can use the generated functions ending with
X is the position of the returned value. For example, the functions returning multiple values show above generate:
To call the function
Example_foo2 from C++ you have to write:
When generating C/C++ code Vult by default uses floating point arithmetic (
float numbers). Floating point code is very efficient in big processors like the x86. However when compiled to small microcontrollers (like the ones found in Arduinos or some ARM processors) the code can be very inefficient because these processors do not have a dedicated floating point arithmetic unit.
Alternatively, fixed-point calculations can be used to perform computations with decimals (like
2.0*1.5). Fixed-point arithmetic encodes the decimal numbers as integers and uses the integer arithmetic unit in small processors to perform calculations (https://en.wikipedia.org/wiki/Fixed-point_arithmetic). The result is that the operations can be performed efficiently at the expense of numeric precision.
Vult can generate all operations with real numbers as fixed-point with the format q16.16. This means that 16 bits a used to represent integers and 16 bits for decimals. This implies that the largest number that can be represented is
32767.0 and the smallest
0.0000152588 (the values are signed). Therefore, when generating code with fixed-point numbers one needs to be careful of not going beyond this numbers.
To generate code with fixed-point numbers we need to call Vult as follows:
$ ./vultc -ccode -real fixed filter.vult -o filter
This command will generate use the type
fix16_t instead of
float. For example, the declaration of the function
lowpass_2x changes to:
vultin.h provides functions to convert among