A month later, much has happened! I’ve implemented drivers for nearly all of the RP2040’s peripherals, Fabien Chouteau contributed an I2C driver, and Daniel King has ported bb-runtimes, including multiprocessing support. Let’s get that blink example updated!

There are lots of pieces that can be abstracted away and made more flexible. For example, Ticks shouldn’t be a public variable that can be modified from anywhere. I should also define a new Time type to differentiate it from other Integers. If you follow these changes to their logical conclusion, you get something that looks like Ada.Real_Time which already exists in the Ravenscar runtimes.

The Ada language provides some fairly high level constructs related to tasking, memory management, and timing that aren’t easy or practical to implement on every platform and the use of some of those features may violate a project’s certification requirements. For this reason, there several runtime profiles with varying levels of functionality. So far, I’ve been using a Zero Footprint (ZFP) runtime, which provides only the bare minimum to allocate some stack space, pass arguments, and call a procedure. No batteries included. The next step up would be the Ravenscar profile, which allows a broader set of builtin functionality, including tasking and synchronization constructs you’d expect to find in an RTOS. An open source implementation of Ravenscar is available in the bb-runtimes repository, though porting it to a new chip is not a small task. There are other runtime implementations that wrap existing RTOS libraries like FreeRTOS and RTEMS. GNAT GCC also includes runtimes for Linux, FreeBSD, Solaris, HP-UX, VxWorks, LynxOS, QNX, and Windows that implement the full set of libraries in the Ada language specification.

I’ll port our blink example to a Ravenscar RTS (Run-Time System) from bb-runtimes. As the RP2040 is still a new platform, its runtimes haven’t been merged yet and aren’t distributed in the GNAT Community 2020 bundle so I’ll need to build it from source.


git clone -b rpi-pico https://github.com/damaki/bb-runtimes
cd bb-runtimes
./build_rts.py --build rpi-pico-mp
cd ../02-ravenscar-blink
gprbuild -P rpsimple.gpr

You’ll notice that the boot2, crt0, and linker script aren’t needed anymore as they’re included with the runtime. I’ve replaced all of the SysTick stuff with a single delay 1.0; statement. The RTS has configured the PLLs and the TIMER peripheral, so I now have microsecond resolution tickless operation. We can still do better! The single-cycle loop still takes time to execute, and waking from sleep takes a few cycles too, so the delay between blinks is still not precisely one second.

In 03-realtime-blink I’ve imported Ada.Real_Time and declared a Next_Blink variable with type Time. Time is a private type as the storage representation of time is a non-portable implementation detail. Initializing Next_Blink with a call to the Clock function means that time doesn’t even have to start at 0!

Next_Blink : Time := Clock;

Now that I know when the next blink should happen, I can use a delay until statement for precision waiting.

   SIO_Periph.GPIO_OUT_XOR.GPIO_OUT_XOR := Pin_Mask;
   delay until Next_Blink;
   Next_Blink := Next_Blink + Seconds (1);
end loop;

Source code