Peripheral Template Library - efficient solution for cross-MCU development

I'd like to introduce project called "Peripheral Template Library" (PTL), which is an (experimental) library to allow development of software/firmware portable across different boards and MCU types/architectures, without compromising efficiency. To achieve both, PTL heavily relies on compile-time metaprogramming techniques and compile-time optimizations targeting level of efficiency and performance higher than that of typical C lib, and closer towards Assembler.

People with solid software development background, when hearing about metaprogramming, definitely thought about LISP and its powerful macros. Indeed, in some parallel world an efficient MCU lib would be written in LISP (or Scheme). However, in our world LISP is niche language due to its syntax, and there're definitely no optimizing LISP compilers for simple MCUs. Fortunately, Alexandrescu et al. thought about giving metaprogramming into hands of C programmers, which is commonly known as "C++ Templates". So, that's it - PTL is C++ template library which depends on (usually simple) metaprogramming techniques to achieve desired efficiency. Note that C++ is still associated with Runtime Object-Oriented Programming (that's in particular how Arduino uses it), but that ~20-years old C++ usage. Modern C++ is actually multi-paradigm language, with metaprogramming being central feature of it.

PTL itself of course was inspired by (spirit of) Arduino. With Arduino, we essentially have def-facto standard API for programming microcontrollers. Unfortunately, it's well-known fact that standard implementation of Arduino API is far from being efficient, with pin access methods being dozen if not hundred times slower than allowed by hardware. Reuse of components is another problem - there're oftentimes problem using different libs accessing same bus/pins, and typical solution to that problem is also sacrifices efficiency. Finally, while Arduino API was ported to many popular architectures (MSP430 with Energia, STM32 with Maple, etc.), overall support and compatibility is rather sketchy and porting to new platform or chip variant is cumbersome, and - again - result usually inefficient.

So, target audience of PTL would be people who are "growing out" of Arduino and want to freely explore new chips and board, and write code, which can be directly turned into "production" and optimized. For example, if it fits within ATtiny13, you don't need to put entire Arduino to it. And if you have app which works well on Arduino, you can put your shiny new ARM Cortex-M board into use by running it there too, without it collecting dust on your shelf, while you try to find time to delve into that beautiful and frightening world of Cortex-M...

PTL is available here: GitHub - pfalcon/PeripheralTemplateLibrary: Cross-platform, cross-MCU C++ template library for microcontrollers and peripheral devices

Currently, PTL supports following MCU architectures and boards:

    * TI MSP430
        * MSP430 Launchpad
    * Atmel AVR
        * Arduino Duemilanove
    * ARM Cortex-M
        * ST STM32
            * STM32VLDISCOVERY
        * Energy Micro EFM32
            * EFM32GG-STK3700
        * TI Tiva C TM4 (former Stellaris LM4)
            * Stellaris/Tiva Launchpad

Here's expected LED blinker example in PTL:

#include <gpio.hpp>
#include <cpu.hpp>
#include <board.hpp>
#include <delay_static.hpp>
#include <delay_time.hpp>

using namespace PTL;

typedef TimeDelay<board::freq, StaticDelay> delayer;

int main()
{
    cpu::init(cpu::DEFAULT);
    board::LED::port::enable();
    board::LED::output();
    while (true) {
        board::LED::high();
        delayer::delay_ms(500);
        board::LED::low();
        delayer::delay_ms(500);
    }
}

Again, this example can be compiled to any of the boards listed above without any changes and will "just work", and at the same time will contain only as little code as needed to implement the functionality.

Nice project!
Let me kindly ask you following. I am working with Win XP and today I am interested in STM32 as I have here few VLDisco boards gathering dust. I may use for example Keil, IAR, CodeSourcery, GNU Tools ARM Embedded under XP. What shall I do now in order to build blink example? P.

Thanks for looking, pito.

One area where PTL lacks is docs. I try to provide bunch of simple examples to get people started, but there definitely should be a "getting started" doc. That's especially true for ARM Cortex-M support, as setup is a bit more involved comparing to AVR or MSP430 (that's of course because there're many ARM variants and many companies makes them, and we want to treat them in general manner).

Also, my development system is Ubuntu, and when I'm thinking how to support other OSes, I lean towards just recommending running Ubuntu in a virtual machine, which seems to be more and more common recommendation these days.

You of course don't have to use VM, PTL is written in portable manner and uses basic GNU make based build system. It's just I can't test all possible OSes and environment variants, so whoever wants to use it on other distro/OS would just needs to have some experience with development setup and resolving issues.

So, how I'd do it Windows (and I actually spent quite a lot of older years there):

  1. Install Cygwin for GNU/POSIX environment. Mingw MSYS might be enough too, but I didn't have experience with it.
  2. CodeSourcery toolchain sounds good, actually, that's what I use on Ubuntu so far. But GNU Arm Embedded Toolchain in Launchpad is getting critical community acclaim, and I'm going to recommend that as soon as I actually tried it (that provides fresh toolchain for all of Linux, Windows, MacOS).
  3. git is needed for sure
  4. For STM Discovery flashing, I use GitHub - stlink-org/stlink: Open source STM32 MCU programming toolset . My guess is that by googling you could find Windows binary for it. Or, you can use any other flashing tool by pointing it to a compiled binary.

I hope that above may give some ideas to consider/research for Windows-based devenv in the meantime. And I began with "getting started" doc, though it will take few sessions to complete it. The draft is here: PeripheralTemplateLibrary/getting_started.markdown at master · pfalcon/PeripheralTemplateLibrary · GitHub

  1. And you need to have all those CMSIS, Startup, Device, System, etc. files for your specific MCU and edit all the relevant paths in the makefiles.. Maybe (just an idea comes across after my attempt to build the STM32VL demo) you may create a fixed directory tree for all those MCU related files, where we can copy the files from somewhere(?, may be you can include few to the PTL distro), without elaborating all the paths to these 3rd party files in the makefiles. For example:
    /PTL/MYMCU/CMSIS
    /PTL/MYMCU/SYSTEM
    /PTL/MYMCU/DEVICE
    /PTL/MYMCU/STARTUP
    etc.
    So we define MYMCU in the makefile once and we can build the stuff..
  1. And you need to have all those CMSIS, Startup, Device, System

I knew folks who would be interested in PTL would be able to figure it out. That's not exactly "5th step", because I tried to list just Windows-specific generic build env setup, whereas sorting out CMSIS stuff needed on any OS, and I hoped describe that straight in Getting Started.

So, to give a perspective on Cortex-M support in PTL: STM32 VLDiscovery was the first Cortex-M target supported, and initially was done with http://libopencm3.org. But as I was learning more about Cortex-M, I figured that libopencm3 just adds extra layer between PTL and hardware, and also doesn't support all targets I was interested in.

So, I went for CMSIS, but of course found all that vendor mess and inconsistency. I kept learning, in particular I figured out that writing startup for Cortex-M is oh-so-damn-easy, you don't even need asm for that, it was specifically designed to make it possible to write startup/interrupt handlers in pure C. That's how GitHub - pfalcon/cortex-uni-startup: Unified startup code and link scripts for Cortex-M microcontrollers was born. It was split from PTL to release it as public domain and to raise awareness of how simple Cortex-M is (note that code is based on other guy's samples who agreed it makes sense it to be just public domain).

One issue down from CMSIS, but there were still bunch of others, including licensing - most of CMSIS implementations from vendors come with proprietary license, which compromise OpenSource projects which use them. I remembered some guy on libopencm3 list talked about libopencmsis, and tracked it down. He was interested in EFM32, and did some bindings for it, but later EnergyMicro released its CMSIS under BSD license, which was first big win, so that guy gave up on his headers (which was generated from metadata) and even removed all his stuff from libopencmsis. I asked if he'd be willing under such circumstances to re-release it under BSD. He said yes, and that's how GitHub - pfalcon/libperipha: Grand unified collection of headers to access various hardware chips and components was born. Soon after I started, there was second big win - ARM itself released CMSIS for Cortex-M standard core as BSD. That's now integrated into libperipha.

Please note that all of PTL, cortex-uni-startup, libperipha don't try to wrap all the features of each MCU. The aim is actual orthogonal - to support core features which any MCU supports, across as many MCUs as possible. The idea is that you can take any CPU (any board) and get typical (Arduino-level) app running on any of them (w/o any changes, that's the aim). If users (me or someone else) is satisfied with some MCU, they can go on and wrap more its functionality. The core MCU functionality we're talking here about is GPIO and timing support (using CPU and freerunning timer).

So, that's it - currently, PTL doesn't depend on vendor CMSIS for any supported targets, because I added (basic!) GPIO binding for all of supported chips to libperipha. All Cortex-M have SysTick timer, so that's covered too. Vendor CMSIS can be used too if more complete target coverage is desired on-spot (but I don't poke at their stuff to stay clean-room, so there may be discrepancies, and I'd need a bugreport to fix that).

I updated GettingStarted with instructions for installing cortex-uni-startup/libperipha and actual example app building.

Some news: I recently added bunch of (Cortex-M) MCU-specific interrupt definitions to GitHub - pfalcon/libperipha: Grand unified collection of headers to access various hardware chips and components (STM32, EFM32, TM4, NRF51), and started to look into how to generalize previous work done on IRQ dispatching in Peripheral Template Library itself.

IRQ handling is one area which is pretty hard to abstract in MCU-independent way (in highly efficient manner, with as low overheard as possible - remember, those are the main goals of PTL). Some previous considerations were noted here: PeripheralTemplateLibrary/irq_handling.txt at master · pfalcon/PeripheralTemplateLibrary · GitHub . I thought of with some more or less general and efficient approach, but implementation existed only for MSP430, so I wasn't sure if it's portable to other MCUs. Finally, after stream of refactors, I believe I was able to come up with the scheme which can be reused across MCUs. As an early example, this app: PeripheralTemplateLibrary/irq_dispatch_simple.cpp at master · pfalcon/PeripheralTemplateLibrary · GitHub can be compiled (without any changes!) for both MSP430 and Stellaris Launchpad.

More work and testing is definitely to be done (including porting to AVR), but I find it pretty exciting that it's possible to hide dirty guts of MCU-specific IRQ handling and write portable interrupt-driven apps - without losing any efficiency!

If there're folks who're interested in this area, I'd appreciate peer review or feedback.

very interesting work!!!
I was wondering if is already possible to use a VCP in an example and how eventually. Do you have any advices?

Thanks. And what's a VCP?