Oberon RTK

Device Modules

Representation and set-up of peripheral devices in software

Introduction

Devices vs Peripherals

As systems-on-a-chip, MCUs comprise several function blocks that the reference manuals and datasheets often call peripherals or peripheral devices to add functionality to the processor cores, such as UART or SPI communication interfaces, as well as timers, and others. The RTK framework employs modules and related data structures to represent these blocks of functionality in the controller software. To distinguish the function block on the chip from the peripheral component it connects to, eg. an SPI sensor, RTK employs the term Device, which connects to a peripheral.

Such a Device[1] can describe:

  • a complete functional hardware block such as a UART or SPI interface, a timer, or the watchdog, delineated by their specific registers, and, for IO-focused items, their expression as GPIO function;

  • a part of a complete block, for example one of the four alarms of a TIMER on the RPs, or the clock functionality of the reset and clock controller RCC on the STM32s;

  • a combination of functional hardware blocks that are operated in unison, for example the clocks generators and the PLLs;

  • a "virtual device" implemented in software, for example an output buffer that can be written to just like a UART, providing the same API.

Device Module Structure

Each Device is abstracted as one module, which defines and exports the relevant constants and procedures for configuration and operation.

  • Of some Devices, there is only one run-time instance, for example the Global TrustZone Controller GTZC on the STM32s, or the Bus Controller BUSCTRL on the RPs. For this type of Device, the module itself represents the instance and its abstraction. This can include Devices that are a combination of actual hardware blocks that are always operated in unison. The procedures operate directly on the underlying hardware.

  • Of some Devices, there are several run-time instances, for example UARTs or SPIs. In this case, the module defines a record type Device that represents one such instance and its abstraction. This can include Devices that are part of actual hardware blocks. The procedures operate the underlying hardware via this Device structure, which is passed as parameter.

Bus Addresses and Device Parameters

  • The MCU's base architectures define a set of standard Devices: ARMv8-M for the Cortex-M33 based MCUs (RP2350, STM32U585, STM32H573), or ARMv6-M for the Cortex-M0+ based MCU (RP2040), such as NVIC, MPU, SCB, SysTick, and SAU. They are accessed via the CPU's own Private Peripheral Bus (PPB). The Devices' register addresses and other parameters are defined by the ARM architecture reference manuals.

  • The MCU extends these standard Devices with vendor-specific Devices such as UART, SPI, I2C, GPIO, timers, resets, clocks, and power management. They are accessed via the system buses (RP2040), and the system and instruction (code) buses AHB and APB (RP2350, STM32). The Devices' register addresses and other parameters are defined by the MCU's reference manuals or datasheets.

The Device modules encapsulate the raw address and parameters as named constants. More precisely, the constants are defined in separate, specific definition modules, which in turn are used by the Device modules.

Cortex-M Devices (PPB)

Since all MCUs of a specific Cortex-M architecture share the address values as defined in the architecture reference manual, the corresponding address constants are defined in a module shared by all corresponding MCU types.

<repo>/lib/v3.1/mcu/
  + m33/PPB.mod -- Cortex-M33: RP2350, STM32U585, STM32H573
  + m0p/PPB.mod -- Cortex-M0+: RP2040

MCU-specific Devices (AHB, APB)

The addresses and parameters for MCU-specific Devices are defined in definition modules in a directory base/src for each MCU product.

<repo>/lib/v3.1/mcu/
  + rpi
    + rp2040/base/src
      + RESETS_SYS.mod
      + UART_DEV.mod
      + ...
    + rp2350/base/src
      + RESETS_SYS.mod
      + UART_DEV.mod
      + ...
  + stm
    + stm32h573/base/src
      + RCC_SYS.mod
      + UART_DEV.mod
      + ...
    + stm32u585/base/src
      + RCC_SYS.mod
      + UART_DEV.mod
      + ...

As the examples above hint at, the modules are divided into:

  • *_SYS.mod: devices dedicated to the MCU itself
  • *_DEV.mod: devices dedicated to the control program

To avoid an explosion of definitions-only modules in our programs, there is the tool merge-defs, which consolidates all device definition modules into two modules per MCU.

<repo>/lib/v3.1/mcu/
  + rpi
    + rp2040/base/
      + SYS.mod
      + DEV.mod
    + rp2350/base/
      + SYS.mod
      + DEV.mod
  + stm
    + stm32h573/base/
      + SYS.mod
      + DEV.mod
    + stm32u585/base/
      + SYS.mod
      + DEV.mod

Address Aliases (Module BASE)

STM32

STM32 MCUs use different address ranges for Secure (S) and Non-secure (NS) access to the same device. While a Secure program can use Non-secure addresses, a Non-secure program attempting to access a device via a Secure address would fault.

The whole device register address range between 040000000H and 04FFFFFFFH for the NS world is mirrored (aliased) between 050000000H and 05FFFFFFFH for the S world. That is, device definition modules such as UART_DEV.mod can be used for both worlds, if the base address used is shifted accordingly.

This shift is achieved using two different modules BASE.mod that define the hardware device base addresses, one for the S and one for the NS program. The device definition modules' addresses are relative to the base addresses.

<repo>/lib/v3.1/mcu/base/
  + stm32h573/base/
    + base-s/BASE.mod
    + base-ns/BASE.mod
  + stm32u585/mcu/base/
    + base-s/BASE.mod
    + base-ns/BASE.mod

The two Astrobe configuration files for the Secure and the Non-secure programs ensure that the correct module BASE is used, achieving the necessary address shifts.

RP2350, RP2040

The RP2350 does not use address aliases for S/NS segregation. Hence we need only one BASE module. The RP2040 uses the same pattern.

<repo>/lib/v3.1/mcu/rpi/
  + rp2040/base/BASE.mod
  + rp2350/base/BASE.mod

Historical Note

Before lib/v3.1, all Device addresses and parameters were defined in a single definition module MCU2.mod, now replaced by PPB.mod and the various *_DEV.mod and *_SYS.mod modules.


  1. The term Device is loosely derived from Unix, where hardware is similarly described and referred to by device files, and devices can be software-only items as well (directory /dev). ↩︎

Last updated: 26 May 2026