An RTOS made for STM32 microcontrollers, specifically tested on a STM32F411RE Nucleo Board.
.
|-- chip_headers/CMSIS # CMSIS, with only the required STM32 libraries and ARM v7 compiler files
|-- GCC_STM32CubeIDE/RTOS # Root directory of the actual RTOS, made in STM32CubeIDE
| |-- chip_headers # Local copy of CMSIS
| |-- Debug # Debug files autogenerated by CubeIDE
| |-- Inc # Header files
| |-- Src
| │ |-- led.c # LED driver
| │ |-- main.c # Main executable
| │ |-- osKernel.c # RTOS implementation
| │ |-- syscalls.c # STM32F4 syscalls, autogenerated by CubeIDE
| │ |-- sysmem.c # STM32F4 memory initialization, autogenerated by CubeIDE
| │ |-- timebase.c # Basic ddtimer handler for SysTick, not used in osKernel
| │ |-- uart.c # UART communcation setup (for debugging)
| |-- Startup # Startup routine autogenerated by CubeIDE
| |-- ... .launch # Individual binary launch routines autogenerated by CubeIDE
|-- ... .pdf # User manuals, data sheets for the hardware (development board, MCU, Cortex M4)
|-- README.md
The LED and UART drivers are implemented mostly for debugging purposes. The process to create the LED driver could be generalized to handle any GPIO input/output.
2024-01-01.19-59-03.mp4
A round robin scheduler is at the core of this kernel. Currently, the scheduler supports RR context switching three threads, with a specified time quanta. The task profiler indicates the progression of each task evenly.
2024-01-01.20-03-17.mp4
API Naturally, multithreading could imply generating race conditions. For example, two tasks printing different things through UART with a small time quanta (2ms) are racing to use the resource (the serial communication bus), so the output isn't as desired, as shown below.
2024-01-01.20-12-28.mp4
Semaphores can solve this, with the current implementation being a spinlock semaphore. A semaphore spins (osSemaphoreWait
) until it is able to use the resource, which is when it posts (osSemaphorePost
).
2024-01-01.20-19-45.mp4
There are other things left to implement, which include:
-
Add priority to RR scheduler. There are different ways to do this. The easiest way to give more resource time to a higher priority task would be to have priority as a multiplier to quanta; higher priority directly implies a task runs for longer. This, however, is bad for average response time (lower priority tasks would take longer to begin) and could also increase latency, overall not being a fair solution. A more common alternative is to have higher priority tasks run more often. FreeBSD implements this a multilevel feedback queue, which organizes groups of priorities into their own queues and does RR on them, allowing higher priority tasks to run more often.
-
Add dynamic priority to RR scheduler. This is a followup to the last point; it would be desirable for tasks not only to have priorities, but to also be able to change them at run time.
-
Add wait channels. The semaphore API currently implements busy waiting- a simple implementation that isn't too efficient since a thread would just be spinning until it obtains the resources. Wait channels are a more desirable solution, putting a thread that doesn't have a resource onto a wait channel so it can be notified by the currently running thread of when it's ready to relinquish the resource.
-
Add mutexes and condition variables. Basically, just implement all of
pthread
s. This would be a nice add-on after we have wait channels.
To have some context on how to work with this MCU, and how to develop an RTOS, I used the following sources:
- DigiKey's Intro to RTOS (FreeRTOS)
- Israel Gbati's RTOS on ARM Tutorial
- The four pdfs in this repo- STM32F411 DS/UM, Cortex M4 UM, Nucleo UM