Rust Async and the Arm Generic Timer

hasheddan1 pts0 comments

JP's Website · 2026-05-17 · Finding the Time Part 2 - Rust Async and the Arm Generic Timer

Finding the Time Part 2 - Rust Async and the Arm Generic Timer

Posted on 2026-05-17

Contents

Intro

Timers

Arm SYSTICK

Arm CMSDK Timer

Arm Generic Timer

Embassy

RTIC

Intro

In the last article, I set out the various versions of the Arm Architecture, and ones I've been focussed on whilst developing the aarch32-rt and aarch32-cpu libraries.

Building an example that gets into fn main() and perhaps fires a few interrupts is fine and all that, but most people using these big processors are going to want a bit more - some sort of framework that lets them run multiple tasks concurrently (whether using pre-emptive task switching, or co-operative scheduling) and manage a list of upcoming events (like timeouts, or when the LED next needs to blink).

In Rust, many people use Embassy or RTIC for this. So how hard can be it to run these frameworks on, say, the Armv8-R AArch32 architecture?

Well, before we get to that, let's talk about some common timer peripherals.

Timers

I'm going to introduce three timer peripherals, so we can see how they are similar and how they are different. The first is found in all Arm Cortex-M based devices, but it's not very good as we'll see. You should note that if you have an STM32 it will have one (or more) timer peripherals from ST, and if you have an nRF52 it will have one (or more) timer peripherals from Nordic Semi. But let's stick to these Arm ones because I know we can emulate them in QEMU for testing, and the final we we're looking at is the one that comes for free in every Armv8-A/Armv8-R system (and some Armv7-A/Armv7-R systems).

Arm SYSTICK

The Arm SYSTICK is a standard feature of every version of the Arm Architecture M-Profile. That is, you will find the same one in a:

Cortex-M0

Cortex-M0+

Cortex-M1

Cortex-M3

Cortex-M4

Cortex-M7

Cortex-M23

Cortex-M33

Cortex-M55

Cortex-M85

The SYSTICK has a counter which counts down. When it hits zero the counter is automatically reset the value in the SysTick Reload Value Register, and the Arm SYSTICK exception is (optionally) fired.

The timer seemingly has one job, and that is to fire an interrupt at very regular intervals - such as 100 times per second, or 1000 times per second. This is exactly what most classic C RTOSes need to drive their scheduler - a timer tick is an opportunity to pre-empt the currently running thread and switch it out for another one that is waiting to run, allowing the threads to appear to execute concurrently even if both are busy doing work.

If you want a timer that can be used as a programmable alarm for some arbitrary moment in the future, this is not going to work very well. The counter is only 24-bits, and it is designed to be driven from your main SoC system clock. So, if you have a 480 MHz system clock, the maximum time you're going be able to have between timer ticks is $\frac{{2}^{24} - 1}{480 \mathrm{MHz}} = 34.95 \mathrm{ms}$. Fine if you want a 100 Hz (10 ms) tick, but if you want to wait longer than that, you'll need to set the SYSTICK counter to the maximum possible, and when it fires, work out how long is left until your event of interest, and set the timer again. If the event is an hour away and you're forced to tick every 35 ms or so until you get there, that's not ideal for battery life.

Luckily, most SoCs also include a somewhat more capable timer.

Arm CMSDK Timer

To help engineers design products using Cortex-M processors, Arm produce some standard peripherals as part of the Cortex-M System Design Kit (CMSDK). Arm also use these peripherals when building evaluation kits for their processors, and, as it happens QEMU emulates several of these boards - such as the Arm MPS2-AN385.

The FPGA based SoC on the MPS2-AN385 includes an Arm CMSDK APB Timer, and because QEMU is very useful for testing Rust firmware without real hardware needing to be plugged into Github CI, I think this is an interesting example to study.

The CMSDK APB Timer has a 32-bit counter and a 32-bit reload value, and not much else. But even so, that's already an improvement on the Arm SYSTICK and much more useful for setting alarms to fire some variable time in the future (as opposed to some fixed periodic tick interval). It also has an INTSTATUS register we can use to check if the alarm is currently ringing (i.e. did the counter value hit zero and reload) and an INTCLEAR register can use to silence the alarm (that is, to cancel the interrupt).

The APB CMSDK timer is usually run from a peripheral clock that is lower than the main system clock, but even at 480 MHz that 32-bit counter gives us a maximum of $\frac{{2}^{32} - 1}{480 \mathrm{MHz}} = 8.95 \mathrm{s}$ between alarms. Not perfect, but better for your battery life than the SYSTICK. And many SoCs will let you lower the timer's clock speed, extending its range.

Arm Generic Timer

The Arm Generic Timer is an optional feature on Armv7-A and...

timer cortex systick counter generic cmsdk

Related Articles