Process & Threads

Bern RTOS uses processes to split an application into loosely coupled parts. A process contains one or more threads. The threads can access all memory within a process, i.e., other tasks stack, the process static memory and the process allocator. However, a thread can only pass messages to a thread running in another process via the kernel.

[project]/main.rs

static PROC: &Process = bern_kernel::new_process!(my_process1, 8192);

#[entry]
fn main() -> ! {2
    let mut board = Board::new();

    bern_kernel::kernel::init();3
    bern_kernel::time::set_tick_frequency(1.kHz(), board.sysclock().Hz());

    let mut led = board.led;
    PROC.init(move |c| {4
        Thread::new(c)5
            .priority(Priority::new(0))
            .stack(Stack::try_new_in(c, 1024).unwrap())
            .spawn(move || {
                loop {6
                    led.set_high();
                    sleep(250);
                    led.set_low();
                    sleep(750);
                }
            });
    }).unwrap();

    bern_kernel::start();7
}

Listing: Blinky example using Bern RTOS.

In this example we will just use one process and one thread. A new process is created at 1. The name given is used to crate a linker section where we can put static variables into. The second parameter specifies the process memory size in bytes. Process memory contains static data and dynamically allocated data, which contains the thread stacks.

Bern RTOS is a library OS, which means that the user is responsible for the boot process and starting the kernel. In this case we use the cortex-m-rt crate to get to main 2. Then, the hardware is initialized.

Before any interaction with the RTOS, we have to initialize the kernel 3 and set the tick frequency. A tick is the time base for the kernel. In most applications, one tick should take 1 ms, i.e., a tick frequency of 1 kHz. As the kernel API calls are millisecond-based understanding time values is simple.

Threads can only be created inside a process. Process initialization is started at 4. Using the process context, we can create a new thread 5. The default priority is changed to zero, the highest priority. Next, we allocate a stack for the thread to run on. Finally, we write the entry function for the thread 6. It contains an infinite loop as the thread should run forever. The spawn method uses a closure so that resources are captured from the environment without having to specifically pass them to a thread.

Now that the system is completely initialized, we can start the kernel 7.