Configuration
An RTOS kernel has many settings to adjust the performance for a particular system. Consider a low power system running at 72 MHz with little RAM (e.g. 32 kB). There is no need for memory allocation or structures to store hundreds of threads. However, on a high performance microcontroller running 480 MHz, external memory and lots of interface the additional code size from allocation is not an issue.
Design
The kernel configuration consists of parameterizing internals, enabled optional modules and providing information about the hardware. Bern RTOS heavily relies on memory protection and linker sections to place static variables into process memory. As the kernel does not depend on microcontroller hardware specifics, the user has to provide memory layout information in the configuration.
An RTOS in C is typically configured using a header file. The preprocessor symbols are global during the build process. For example number of priorities is defined in the configuration header file in the user application and directly applied to the kernel.
In Rust those global symbols do not exist. One solution is to use constant generics in structures. For example, we could then set the number of priorities to 8 in the user application static SCHED: Scheduler<8> = Scheduler::new();
. At the moment, const generics can only take primitive types. For some parameters strong types are preferred. Another solution is to use environment variables in the build shell. This approach suffers from weak types even more.
There are many more solutions to this problem. For simplicity reason the kernel uses a cargo patch in the build process.
Figure: Kernel configuration dependencies.
The bern-kernel crate in the figure depends on the default bern-conf crate implementation. On the application, the bern-conf crate is implemented again and passed as a patch to the build system. Thus, there are no circular dependencies. More importantly, the build command is still cargo build
and does not require any external tools.
In addition to detailed adjustments in the configuration crate, some modules can be disabled altogether. System logs and tracing are not necessary for every application. These features are optional and configurable in the cargo manifest.
Usage
The API documentation lists the kernel features to enable optional modules. Configuring the memory layout and kernel parameters are done in the configuration crate.
[project]/conf/conf.rs
pub const1 CONF: Conf<1> = Conf {
kernel: Kernel {2
priorities: 8,
memory_size: Byte::from_kB(2),
},
shared: Shared {3
size: Byte::from_kB(8),
},
memory_map: MemoryMap4 {
flash: Memory {
link_name: "FLASH",
start_address: 0x0800_0000,
size: Byte::from_MB(1),
},
sram: Memory {
link_name: "RAM",
start_address: 0x2000_0000,
size: Byte::from_kB(512),
},
peripheral: Memory {
link_name: "",
start_address: 0x4000_0000,
size: Byte::from_MB(512),
},
additional:5 [
OptionalMemory {
memory_type: MemoryType::Ram,
location: MemoryLocation::External,
link_name: "XRAM",
start_address: 0xC000_0000,
size: Byte::from_MB(32),
}
],
},
data_placement: DataPlacement {6
kernel: "RAM",
processes: "RAM",
shared: "RAM"
}
};
Listing: Bern RTOS memory configuration.
The configuration is a constant structure
The shared region
The memory map configuration
Lastly, the user can select the memory component where data will be placed