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.native`

or `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 `filter.vult`

.

// returns true when the input changes
fun change(current:real) : bool {
mem previous;
val result = current <> previous;
previous = current;
return result;
}
fun biquad(x0, b0, b1, b2 ,a1 ,a2) : real {
mem w1, w2;
val w0 = x0 - a1*w1 - a2*w2;
val y0 = b0*w0 + b1*w1 + b2*w2;
w2, w1 = w1, w0;
return y0;
}
fun lowpass(x,w0,q) {
mem b0,b1,b2,a1,a2;
if(change(w0) || change(q)) {
val cos_w = cos(w0);
val alpha = sin(w0)/(2.0*q);
val den = 1.0 + alpha;
a1 = (-2.0*cos_w)/den;
a2 = (1.0-alpha)/den;
b0 = (1.0-cos_w)/(2.0*den);
b1 = (1.0-cos_w)/den;
b2 = (1.0-cos_w)/(2.0*den);
}
return biquad(x,b0,b1,b2,a1,a2);
}
fun lowpass_2x(x,w0,q) {
val fixed_w0 = w0/2.0;
// first call to lowpass with context 'inst'
_ = inst:lowpass(x,fixed_w0,q);
// second call to lowpass with the same context 'inst'
val y = inst:lowpass(x,fixed_w0,q);
return y;
}

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.h`

, `filter.cpp`

and `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:

- a type with the name
`Bar_foo_type`

- a initialization function with the name
`Bar_foo_init`

- a function with the body of the original Vult function called
`Bar_foo`

In the case of the filter example the names are: `Filter_lowpass_2x_type`

, `Filter_lowpass_2x_init`

and `Filter_lowpass_2x`

.

To use this code in a file you need to:

- create a value of type
`Filter_lowpass_2x_type`

- initialize it with the function
`Filter_lowpass_2x_init`

- use it with the original function
`Filter_lowpass_2x`

For example:

// file main.cpp
#include "filter.h"
int main(void) {
Filter_lowpass_2x_type filter;
// initialization
Filter_lowpass_2x_init(filter);
// inputs to the function
float x = 0.0f;
float w = 1.0f;
float q = 1.0f;
// calling the function
float result = Filter_lowpass_2x(filter,x,w,q);
return 0;
}

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:

// example.vult
// single value return, no memory
fun foo1(x:real) {
return x;
}
// single value return, with memory
fun bar1(x:real) {
mem y = x;
return x;
}
// multiple value return, no memory
fun foo2(x:real) {
return x,x;
}
// multiple value return, with memory
fun bar2(x:real) {
mem y = x;
return x,x;
}

C/C++ declarations of the functions above:

// single value return, no memory
float Example_foo1(float x);
// single value return, with memory
float Example_bar1(Example__ctx_type_1 &_ctx, float x);
// multiple value return, no memory
void Example_foo2(Example__ctx_type_2 &_ctx, float x);
// multiple value return, with memory
void Example_bar2(Example__ctx_type_3 &_ctx, float x);

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 `_ret_X`

where `X`

is the position of the returned value. For example, the functions returning multiple values show above generate:

// Gets the first returned value of function Example_foo2
float Example_foo2_ret_0(Example__ctx_type_2 &_ctx);
// Gets the second returned value of function Example_foo2
float Example_foo2_ret_1(Example__ctx_type_2 &_ctx);
// Gets the first returned value of function Example_bar2
float Example_bar2_ret_0(Example__ctx_type_3 &_ctx);
// Gets the second returned value of function Example_bar2
float Example_bar2_ret_1(Example__ctx_type_3 &_ctx);

To call the function `Example_foo2`

from C++ you have to write:

// Declare and initialize the instance
Example_foo2_type inst;
Example_foo2_init(inst);
// Call the function
Example_foo2(inst, 0.0);
// Retrieve the result
float value0 = Example_foo2_ret_0(inst);
float value1 = Example_foo2_ret_1(inst);

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:

fix16_t Filter_lowpass_2x(Filter__ctx_type_3 &_ctx, fix16_t x,
fix16_t w0, fix16_t q);

The file `vultin.h`

provides functions to convert among `float`

`fix16_t`

and `int`

values.