Every real-time operating system (RTOS) is optimized for some use cases. Bern RTOS targets industrial connected embedded systems. These systems are subjected to dynamic load from network interfaces and must perform critical tasks on time. The RTOS is designed according to the following principles to meet the use cases.
An embedded application should never crash. This cannot solely be achieved in the kernel. The user of the RTOS has some responsibility too. The kernel design, however, must never panic and should help the user avoid bugs. If one part of the software crashes, the kernel must prevent a complete system crash.
An embedded system handles many tasks at the same time. Not all of those tasks have the same criticality. Bern RTOS should allow any type of task to run on the same system. The user splits critical and other parts of the software into processes. Each process contains one or more threads that run similar to tasks on other RTOS. Processes run isolated from each other.
For example, a user can limit the effect of an attack on a network interface on the critical parts of the system or use an existing library written in C that the Rust compiler cannot verify.
Bern RTOS should be adaptable to any project by providing configuration options for all components of the RTOS.
The kernel should only rely on the CPU core and leave the microcontroller peripherals to the user. Thus, any hardware-abstraction layer (HAL) or none at all can be used. Because many microcontrollers use the same CPU architecture, the efforts for porting the kernel is often little to none.
Most threads in embedded software can be strictly prioritized. Thus, high-priority threads should preempt lower priority ones. To share the CPU between threads of the same priority, threads should run for a maximum of one time unit, known as Round-Robin or time-slicing.
Microcontrollers are used to control hardware in real-time. The kernel overhead between an event on the hardware and executing the appropriate thread must be low. Repetitive threads are to run in a deterministic and timely manner.
If fast interrupt handling is crucial, the user must be able to bypass the kernel altogether. In a trade-off between safety and timeliness, the latter must be preferred.
Every thread has its own stack. By default, stacks should only be accessible from within a process. This prevents accidental or intentional manipulation of stack data from other processes. The compiler cannot foresee or prevent stack overflow. Therefore, the kernel must prevent corruption from overflow.