Now that we are all set, we can create a new project using a template.

cargo generate --git

Listing: Generate a Bern RTOS based project.

The newly generated project resembles any embedded-hal based Rust project. However, there are some differences that we will take a look at. First up is the Carog manifest.


name = "blinky"
version = "0.1.0"
edition = "2021"

cortex-m-rt = "0.7"
panic-halt = "0.2.0"
bern-kernel = "0.3"1

# Adjust here and in `.cargo/conf.toml` for your device.
features = ["stm32f411"]
version = "0.13"

codegen-units = 1
opt-level = "s"

bern-conf = { path = "conf" }

Listing: Cargo manifest.

In the Cargo manifest in the listing the kernel is included at 1. The kernel selects the architecture support automatically based on the toolchain. This example uses a microcontroller HAL 2. Be sure to select your microcontroller HAL and change the target in [project]/.cargo/conf.toml accordingly.

Building Rust applications in debug mode leads to much larger binaries than in release mode. This affects the performance of the RTOS as well. In order to use debug symbols and compromise little on performance, we can enable optimization on the dependencies only 3.

Bern RTOS needs additional configuration values outside the Cargo manifest. This is done using a crate in the user application that replaces the default one. This patch is specified at 4.


pub const CONF: Conf<0> = Conf {
    kernel: Kernel1{
        priorities: 8,
        memory_size: Byte::from_kB(2),

    shared: Shared2{
        size: Byte::from_kB(1),

    memory_map: MemoryMap3{
        flash: Memory {
            link_name: "FLASH",
            start_address: 0x0800_0000,
            size: Byte::from_kB(512),
        sram: Memory {
            link_name: "RAM",
            start_address: 0x2000_0000,
            size: Byte::from_kB(128),
        peripheral: Memory {
            link_name: "",
            start_address: 0x4000_0000,
            size: Byte::from_MB(512),
        additional: [],

    data_placement: DataPlacement4{
        kernel: "RAM",
        processes: "RAM",
        shared: "RAM"

Listing: Bern kernel configuration.

the listing starts with the kernels internal parameters 1. By default, there are 8 priorities and 2kB reserved for the kernel. When libraries use static memory they will be placed in a shared section instead of a process. How much shared memory is accessible can be set at 2. This value is preferably kept at a minimum.

Memory protection is currently used to restrict access to stacks within one process only. Access to the flash memory and peripherals is granted to each thread. For the kernel to set appropriate memory protection rules, some memory addresses must be specified 3.

Some microcontrollers offer faster or external memory. We can select where to put the data structures at 4.