Close

Firmware overview

A project log for DSP PAW

Design, study, and analyze DSP algorithms from anywhere.

clyneClyne 10/08/2023 at 22:030 Comments

Most of the previous project logs have focused on the DSP PAW hardware and using the system and user interface to test algorithms. However, the firmware that makes this project possible is just as significant as these other portions and deserves just as much documentation. I have just added some documentation to the source code to start, but I will also take this log entry to go over the main components of the firmware and how they work together to get things done.

C++

The firmware is written in C++ rather than C. Using C++ actually makes a lot of sense for embedded systems so long as you avoid dynamic memory allocations and some other higher-level features (unless you have made the needed accommodations in your firmware). Classes and objects can neatly package data and functionality together, principles like Resource Allocation Is Initialization (RAII) can make code flow more concise and safe, and compile-time variables or functions can save on precious memory space.

Even simple features like the "auto" keyword just make your code easier to work with. With the growth of C++ both in general and specifically in the embedded domain, there are less and less reasons to make the change if you are still stuck on choosing C.

Real-time operating system (RTOS)

The DSP PAW firmware is built on top of an RTOS which provides the functionality to execute multiple services "at the same time", facilitate communication between these services, and guarantee as best as possible that code will never run behind schedule. "At the same time" is in quotes since the vast majority of microcontrollers are single-core, meaning that simultaneous execution is achieved by switching between different services or execution states at a milli- or micro-second scale.

The RTOS used for DSP PAW is called ChibiOS. This RTOS has spectacular support for STM32 microcontrollers, is available as free software under a GPL license, and has a great community that contributes extra hardware support and other bits of functionality. It's worth checking out if you have not heard of it before.

DSP PAW uses ChibiOS to run multiple services (threads) at once:

These threads use either "mailboxes" or service classes to send messages to each other when necessary. When none of the threads need to be active, ChibiOS enters an idle thread that allows the microcontroller to sleep.

Unprivileged (and safe) execution

The algorithm execution thread is unprivileged, meaning it is restricted in its ability to access certain registers or functions. Additionally, a hardware memory protection unit is used to prevent this thread from reaching beyond the memory it is allocated. Finally, a service call routine is made available through the RTOS to allow the algorithm access to just the few firmware functions that it needs to process data (e.g. large trigonometric functions, parameter knob reading, etc.)

This stack-up of protection keeps the firmware safe from user-loaded algorithms that misbehave. If an algorithm does something that it shouldn't, one of a few handler functions will be executed to clean up the mess, unload the algorithm, and notify the user that something has gone wrong. This means you can continue to use your DSP PAW hardware without needing to reset the microcontroller or worry about bad code causing unintentional damage.

Hardware abstraction

ChibiOS (and most other operating systems) include a hardware abstraction layer (HAL) so that your top-level code can avoid worrying about the nuances of your specific microcontroller and peripherals. By using ChibiOS's HAL, the firmware becomes much easier to port to other microcontrollers or development boards. At most, the programmer will need to handle some I/O pin numbers, the configuration structures and memory buffers for the ADC and DAC, and a couple of assembly instructions; the rest of the firmware is entirely independent of current choice of microcontroller.

Algorithm uploading

DSP algorithms designed by the user are compiled with a regular C++ compiler (GCC) to create an ELF binary. The ELF format is a common standard -- most Linux programs are compiled to this format. The algorithm ELF binary contains all of the user's code and has its entry pointer set to the start of the algorithm (the process_data function).

This binary is uploaded in full to the microcontroller over the USB connection. From there, the binary is parsed and prepared for execution: the entry pointer is saved, memory and variables are initialized, and code is copied to the memory address that it expects to execute from. When it's time for the algorithm to execute, the unprivileged thread simply makes a call to the entry pointer.

If the algorithms needs facilities from the C++ standard library, the necessary functions are compiled into the ELF binary by the user interface. Larger functions and firmware utilities like reading the parameter knobs are accessed through ARM service calls.

USB Communication

The firmware uses ChibiOS's serial USB driver to talk with computers and the DSP PAW user interface. "Serial" may make you think of UART and slow data transfer rates like 9600 baud; however, the driver takes full advantage of USB speeds to maximize data transfer without any complicated configuration.

Commands from the user interface control the hardware and either send or receive chunks of sample data. For simplicity, commands are identified by a single letter with additional parameters or data following if necessary. There may be some value in making this system more robust by using checksums and packet structures, though there have been no issues in communication due to this minimal format.

Signal data management

Processing signals in real-time requires a lot of care. To keep a continuous stream of data flowing, the input and output signal buffers are split in half: while one half is being streamed into or out of the microcontroller, the other half is being processed by the algorithm. A "SampleBuffer" class manages the firmware's split buffers and tracks the most recently modified buffer so the algorithm executor knows what memory to work with.

The firmware for the DSP PAW hardware is available here on the project's repository if you would like to take a closer look.

Discussions