Getting started with syfala and Faust

Introduction

This tutorial is intended for beginner-users with no background in FPGA or C++ development. Only basic knowledge of Linux and using the terminal is necessary. Prior experience using the Faust language is also preferable, but not required.

Minimum hardware/software requirements

One of the following development boards:

Objectives

In this tutorial, we will cover the essential topics to get started with the Faust programming language, syfala, and audio programming on FPGAs in general. We will start by briefly presenting all of these programs and environments, and then we will get our hands on a very concrete audio program example that we're going to build, flash on our FPGA development board, and control remotely, using different protocols, such as MIDI, OSC and HTTP.

Faust

Faust (Functional Audio Stream) is a functional programming language for sound synthesis and audio processing with a strong focus on the design of synthesizers, musical instruments, audio effects, etc. created at the GRAME-CNCM Research Department.

In order to get you started using Faust, we recommend that you follow this tutorial : https://faustdoc.grame.fr/manual/quick-start

Syfala

About syfala (and FPGAs)

Syfala is a set of command-line tools & scripts aiming to facilitate audio programming on FPGAs. It currently targets AMD-Xilinx devices, such as the Digilent Zybo Z7-10 & Z7-20, or the Genesys ZU-3EG. It takes a Faust .dsp file or a C++ .cpp file as input, and then configures and calls all the AMD-Xilinx softwares that will compile all the required binaries to be flashed on device.

The repository itself is composed of a few different elements:

Installing the toolchain

Dependencies

Please follow the instructions in the file dependencies.md in order to install the AMD-Xilinx toolchain and various other dependencies.

Installing Syfala

git clone https://github.com/inria-emeraude/syfala.git syfala
cd syfala
make install

You'll also have to add the following environment variable to your shell resource file (~/.bashrc / ~/.zshrc)

export XILINX_ROOT_DIR=/my/path/to/Xilinx/root/directory

where XILINX_ROOT_DIR is the root directory where all of the AMD-Xilinx tools (Vivado, Vitis, Vitis_HLS) are installed.

Your first example

Building

syfala <faust-file.dsp> [options]
# Depending on the development board that you have, the command will be:
syfala examples/faust/virtualAnalog.dsp --board Z10
                                        --board Z20
                                        --board GENESYS

This will run the full syfala toolchain on the virtualAnalog.dsp Faust file, which will then be ready to be flashed on your board. Under the hood, syfala will follow multiple steps, which you'll be able to monitor from the console log:

1 - Sources

It will call Faust to generate the C++ code from the virtualAnalog.dsp file, with a custom-made syfala architecture file. You'll be able to see the resulting code in build/syfala_ip/syfala_ip_preprocessed.cpp.

2 - HLS

Once the C++ code is generated, it will call Vitis HLS, which will translate it into a self-contained Hardware Description Language (HDL) program, which we're going in this tutorial to call the DSP kernel. The resulting code of this kernel can be viewed in the build/syfala_ip/syfala/impl/vhdl/syfala.vhd VHDL file.

3 - Project

Here, the DSP kernel generated by Vitis HLS is going to be imported into a more global Vivado design, which will include the processing system (PS), our custom-made Integrated Interchip Sound (i²s) transceiver, and various other modules as well. The resulting code can be viewed in build/syfala_project/syfala_project.gen/sources_1/bd/main/hdl/main_wrapper.vhd

4 - Synthesis & implementation

From here, Vivado will 'compile' the project's final design into what we call a bitstream, which is a hardware configuration file: it includes the description of the hardware logic, routing, and initial values for both registers and on-chip memory (e.g. LUT).

5 - ARM control executable

Finally, we will also need to cross-compile an ARM elf executable to take care of all initialization and control-rate computations, which are done on the CPU, and exchanged with the DSP kernel using a specific set of buses.

Reading performance reports

If the build went well, you should be able to see on your terminal a recap of the build's configuration and an overview of its performance reports. Since the HLS report is only an estimate, the Vivado summary is the one that you should really pay attention to.

virtualAnalog Z20

- DSP:  15% (35)
- Reg:  9% (9730)
- LUT:  16% (8820)
- BRAM:  0% (0)

Tot. 344 Cycles, 2.799us
Max. 2604 Cycles, 20,8333us
Lat. 13%
Resources

Here we can see that, on our Zybo Z7-20 development board, the final design doesn't take a lot of FPGA resources, so there's potentially room to add more cool stuff to our Faust program, if the latency is sufficiently low as well.

Latency

The report shows that the DSP kernel's computations are done in 2.799 microseconds (344 FPGA clock cycles at 122 MHz): that is way below the maximum allowed for the computation of a single sample, which is defined by the reciprocal of the sample-rate (1/48000 in our case, which equals to 20,8333 microseconds).

Exporting and re-importing your builds

Before flashing, or doing anything else, it's generally a good idea to save and export your build outputs, especially when compilation times are really long, like ours. You can do this with the following syfala command:

syfala export my-faust-virtual-analog-build
# output in 'export/my-faust-virtual-analog-build.zip'

The resulting .zip file is then going to be available in the repository's export directory. Later, you'll be able to re-import it by doing:

syfala import export/my-faust-virtual-analog-build.zip

Flashing

Now we have our program fully compiled and exported, we can try it on our development Board. In order to do so, two options:

  1. Flash it from USB (with JTAG), by connecting an USB cable to the board's UART port.
  2. Flash it from a bootable SD card.
Hardware configuration

In both cases, you'll have first to make sure that the board is set up with this configuration:

USB (JTAG)

You can then quickly flash your program by entering the following syfala command from your terminal:

syfala flash

Keep in mind that when you shutdown your device and power it up again, it won't start your program again automatically (because it is not kept in memory), so you'll have to enter the syfala flash command again. This is why working with SD cards is going to be a better solution if you have the need to reload your program every time your power up the board.

SD card

To generate the bootable binary file boot.bin, you can add the --boot flag to your syfala command :

syfala examples/faust/virtualAnalog.dsp --board Z20 --boot

It won't re-compile the whole project, it will just take the hardware and software outputs of your build and package them into a single bootable binary file (boot.bin), which you'll find in the build/sw_export directory. You can then just copy this file into your SD card, and insert it in the board's SD card socket, and then power the board up with SW4.

Monitoring and controlling the board remotely

Once your build is flashed on your board, you'll first want to make sure that the program has been loaded and executed properly. You can obviously plug in a headset or a pair of speakers to check its audio output, but there are also a couple of ways of getting debug information, which you'll need in case the program is not working the way it is supposed to:

LEDs

Right after flashing your build, you can directly look at the board's LD12 LED, which is supposed to turn green when a bitstream has been properly loaded. Then, you can look at the LD5 RGB LED, which will go from blue to green during the ARM control program execution. Audio will usually start right after this LED turns green.

If an error occurs, and LD12 is still off, or LD5 turns red, the next step would be to check the board's console output, which is accessible through UART.

UART console

In order to view the board's console output, you will need to install a serial device i/o tool, such as minicom or tio. For this tutorial, we will use tio. Once the board is connected with an USB cable (PROG UART port) to your laptop or desktop computer, you can enter the following command:

tio -b 115200 /dev/ttyUSB1

This will tell tio to connect to the ttyUSB1 serial device interface with a baud rate of 115200. Once it is done and the board has been powered-up again, you should see debug information being displayed on the console.

Remote control of Faust parameters

USB-UART control

All of the user-interface elements declared in the Faust DSP code are controllable. In baremetal mode, the preferred way of doing this remotely from your machine would be using the available GUI to Serial interface, that you can start with the following command, once the DSP program is running on your board:

syfala start-gui

Syfala should now be building the user-interface after having retrieved the .dsp file's control parameters, and display it with a GTK interface when it's ready. You should now be able to play with the different buttons, sliders and knobs that have been defined in your Faust file.

Now, GTK-based sliders and buttons are perfectly fine for testing your program, but you'll probably want at some point to have a nicer way to control its parameters, by using for instance MIDI or OSC interfaces. For now, in baremetal mode, it is not possible (yet) to plug a MIDI controller directly on the board's USB port and control your Faust parameters, because MIDI-USB baremetal drivers are unfortunately not yet implemented in syfala. The same goes for Ethernet-OSC. To remedy this problem, we offer at the moment two solutions :

  1. The Embedded Linux for syfala (tutorials/embedded-linux-getting-started.md)
  2. On Linux, MIDI, OSC & HTTP libraries are available, and ready-to-use with syfala.
  3. MIDI, OSC, HTTP layers on top of the Serial interface.
  4. MIDI, OSC & HTTP interfaces will be used on your machine, and translated/channeled into the Serial interface to be transmitted to the board. This is not ideal and will introduce some more control latency to the setup, but is easy to use.
MIDI control

In order to build a target with MIDI-Serial support, you can add the --midi flag to the command line, and reload the start-gui command:

syfala examples/faust/virtualAnalogMIDI.dsp --board Z20 --midi
syfala start-gui

You'll also have to explicitly map in your Faust code the controls (sliders, knobs, etc.) to a MIDI control element (note, cc, pressure, etc.). An example of MIDI-mapping can be viewed in examples/faust/virtualAnalogMIDI.dsp, and the Faust MIDI-mapping process is fully explained here : https://faustdoc.grame.fr/manual/midi

OSC control

In order to build a target with OSC support, you can add the --osc flag to the command line:

syfala examples/faust/virtualAnalog.dsp --board Z20 --osc
syfala start-gui

The GUI control application will create an Open Sound Control-compliant UDP server. You'll be then able to control remotely the Faust DSP parameters by sending OSC messages like so:

More on: https://faustdoc.grame.fr/manual/osc

HTTP control

In order to build a target with HTTP support, you can add the --http flag to the command line:

syfala examples/faust/virtualAnalog.dsp --board Z20 --http
syfala start-gui

The GUI control application will create a HTTP server allowing users to control the Faust DSP parameters remotely (given that you are on the same network as your FPGA board).

At runtime, when executed, the application will print the device's current network IP (IPv4), and the port used by the HTTP server. You can then use any web browser, and control the application by entering the server's URL, for example http://192.168.0.1:5510

SPI-based controllers

[...]

Going further

Embedded Linux

Note: only available for Zybo Z7-10/Z7-20 boards (Genesys not yet supported) .

Starting from syfala 0.8, you can choose to build our custom-made embedded Linux, which will provide, in addition to all the things an OS has to offer (SSH control over Ethernet or Wi-Fi, package manager, etc.), the following features:

From the syfala command line interface, all you need to do is add the --linux flag:

syfala examples/virtualAnalog.dsp --linux

For the rest of the procedure, you can follow the linux getting-started tutorial

Using syfala with C++

If you'd rather write your DSP code directly in C++, or you have a more imperative need for performance and optimization, you can follow our C++ tutorial here (advanced users).