π STM32L475 Custom RTOS Scheduler (Bare-Metal)
This repository contains a from-scratch implementation of a preemptive Round-Robin scheduler for the ARM Cortex-M4 (STM32L475VG). The project demonstrates low-level context switching, hardware timer configuration, and memory management without the use of a commercial RTOS or high-level HAL libraries. β
π 1. Project Structure & File Purpose
|.vscode/
β βββ launch.json # Debugging configuration for WSL + GDB
02_Scheduler/
βββ cmsis/
β βββinc/
β β βββm-profile/
β β β βββarmv7m_mpu.h # Copied from CMSIS_6-6.3.0
β β β βββcmsis_gcc_m.h # Copied from CMSIS_6-6.3.0
β β βββcmsis_compiler.h # Copied from CMSIS_6-6.3.0
β β βββcmsis_gcc.h # Copied from CMSIS_6-6.3.0
β β βββcmsis_version.h # Copied from CMSIS_6-6.3.0
β β βββcmsis_cm4.h # Copied from CMSIS_6-6.3.0
β β βββstartup_stm32l475xx.s # Vector table and Reset Handler, copied from cmsis-device-l4-1.7.5
β β βββstm32l475xx.h # CMSIS Peripheral Access Layer, copied from cmsis-device-l4-1.7.5
β βββsrc/
β startup_stm32l475xx.s # Startup file, copied from cmsis-device-l4-1.7.5
βββinc/
β βββ scheduler.c # Structure definitions (TCB_t)
βββsrc/
β βββ context_switch.s # Assembly implementation of PendSV switch
β βββ main.c # Hardware Init, Clock Config, and Task logic
β βββ scheduler.c # TCB management and Stack Watermarking
βββ linker.ld # Custom linker script for memory mapping
βββ makefile # custom Makefile to build using arm-none-eabi-gcc compiler
NOTE : further project structure will not be updated in here, refer latest directory structure for more details
π οΈ 2. CMSIS & Startup Integration
To ensure professional-grade register access while staying βNo-HAL,β the project utilizes:
CMSIS Headers: stm32l475xx.h was sourced from the official STMicroelectronics CMSIS device repository. This provides the standardized structures used for direct register manipulation (e.g., GPIOA->BSRR).
Startup Code: The startup_stm32l475xx.s file was adapted from the GCC toolchain templates. It was modified to ensure the PendSV_Handler is globally accessible and properly linked to our custom assembly implementation.
ποΈ 3. Linker Script Construction
The linker.ld was manually crafted to align with the Cortex-M4 memory map:
Memory Definitions: Specifically mapped FLASH (1024K) and SRAM1 (96K).
Section Placement:
.isr_vector: Placed at the very top of Flash to ensure the CPU finds the Stack Pointer and Reset Handler on boot.
.text: Aggregates code from all object files.
.data / .bss: Configured for proper initialization of global variables during the startup sequence.
Stack Alignment: Enforced 8-byte alignment for task stacks to comply with the ARM Procedure Call Standard (AAPCS).
βοΈ 4. Technical Implementation Tasks
Task 1: Clock & Timer Configuration The system was moved from the default 4MHz MSI clock to a stable 16MHz configuration. The SysTick timer was configured to generate an interrupt every 1ms to serve as the OS heartbeat.
Task 2: Task Stack Initialization Each task is assigned a private stack. During initialization, we βfakeβ an exception frame. When the scheduler first switches to a task, the CPU βreturnsβ from an interrupt that never actually happened, popping our dummy values into the registers and starting the task function.
Task 3: Preemptive Context Switching The switching logic is split between C and Assembly:
SysTick (C): Increments the global tick counter and pends the PendSV interrupt.
PendSV (Assembly):
-
Saves R4-R11 of the current task to its stack.
-
Updates the currentTask pointer to the next TCB in the circular linked list.
-
Restores R4-R11 from the new taskβs stack and updates the Process Stack Pointer (PSP).
Task 4: Stack Watermarking To detect potential memory corruptionβa critical requirement in SSD firmwareβwe implemented stack painting. The stack is filled with 0xDEADBEEF, and a background function calculates the unused space by searching for the βpristineβ magic numbers from the bottom up.
π 5. How to Build & Debug (WSL)
USB Bridge: Use usbipd in Windows PowerShell to attach the ST-Link to WSL:
PowerShell usbipd attach --wsl --busid <bus-id> Compile: Use the arm-none-eabi-gcc toolchain via the provided Makefile.
Debug: Launch the Cortex-Debug configuration in VS Code. The launch.json is configured to use gdb-multiarch and openocd.
Refer STM32 Development & Debugging on WSL for more details
π 6. Project Results
Multi-Tasking: Two independent tasks toggling PA5 and PB14 LEDs at different frequencies (500ms vs 1000ms).
Preemption: Demonstrated that the system can swap tasks even during a blocking Delay_ms loop.
Stability: Verified 16-byte stack alignment and FPU-safe standard stacking.
7. Run time changes in folder structure (IMPORTANT TO BUILD CORRECT MODULE)
To build multiple module using same set of linker, startup, cmsis files, we have to make certain changes as below,
- In Makefile we have added
MODULE ?= <Folder name here>. - Type correct folder name for project you want to build from
0-Modulesfolder so that it can generate itβs relevantfinal.binandfinal.elffiles inreleasefolder. - With usual F5, now you can debug the correct active files, or you can load
final.bindirectly.