Hello,
I'm new here, and I've started writing an operating system, a real-time hybrid cooperative/preemptive stack-stored context switch. The final target is an industrial-grade Programmable Logic Controller.
The current target is an 8-bit AVR ATmega microcontroller, specifically an Arduino board. I'm using the AVR toolchain without any Arduino software.
Right now, I have a basic system running with a preemptive scheduler and a real-time clock. I'm working on serial communication via USART for testing, debugging, and control purposes. I'm also considering the system's layer arrangement:
Layer arrangement:
4 - User tasks
3 - System calls
2 - System core + system services
1 - Drivers
0 - Hardware
Tasks can have direct access to system services and drivers unless restricted by the system core.
Critical features for an embedded operating system:
Robust stack overflow control
Prevention of software and hardware deadlocks (watchdog)
Resource access management (abstraction layers, system calls, mutexes)
Real-time capabilities
Error handling and recovery mechanisms
Task prioritization
Inter-task communication
Debugging capabilities (e.g., communication, dynamic task start/stop, driver management)
What else should I consider? Thanks!
Layers and critical features for embedded operating system
- Doul
- Posts: 3
- Joined: Sat Feb 15, 2025 3:56 pm
- GitHub: https://codeberg.org/Doul09/TaskMate
Layers and critical features for embedded operating system
INT 21 is my ex-girlfriend
- Doul
- Posts: 3
- Joined: Sat Feb 15, 2025 3:56 pm
- GitHub: https://codeberg.org/Doul09/TaskMate
Re: Layers and critical features for embedded operating system
Hello,
Following this site advice I set up a version control with git / Codeberg, now the code is licensed I can share it :
https://codeberg.org/Doul09/TaskMate
As the project progress, it shift from monolithic to hybrid microkernel design, without heavy weight server, just function call.
Slightly new layer configuration : The system services are pushed to layer 4, with difference form user task : they are intended to be a short cooperative program with direct access to drivers.
This new layers configuration is one step toward to portability : system call could be the Hardware Abstraction Layer. On the other hand there is already macro for pseudo code, these macro could be used with conditional compilation for different targets. An other point for portability is the driver generic layout formatting, you don't care about target, just call driver_xInit() or driver_xStop() or ..., this enable automatic drivers handling.
Following this site advice I set up a version control with git / Codeberg, now the code is licensed I can share it :
https://codeberg.org/Doul09/TaskMate
As the project progress, it shift from monolithic to hybrid microkernel design, without heavy weight server, just function call.
Slightly new layer configuration : The system services are pushed to layer 4, with difference form user task : they are intended to be a short cooperative program with direct access to drivers.
This new layers configuration is one step toward to portability : system call could be the Hardware Abstraction Layer. On the other hand there is already macro for pseudo code, these macro could be used with conditional compilation for different targets. An other point for portability is the driver generic layout formatting, you don't care about target, just call driver_xInit() or driver_xStop() or ..., this enable automatic drivers handling.
INT 21 is my ex-girlfriend
- Demindiro
- Member
- Posts: 111
- Joined: Fri Jun 11, 2021 6:02 am
- Libera.chat IRC: demindiro
- Location: Belgium
- Contact:
Re: Layers and critical features for embedded operating system
Instead of trying to manage stack overflow it might be more interesting to rule it out entirely.
Mainstream compilers don't have very good ways to measure the maximum amount of stack a program will use, but for C it appears you can use a combination of:
- GCC's -fstack-usage option, to get the amount of bytes used by each function
- cflow to determine the call graph
(source: https://stackoverflow.com/a/6392173)
You won't be able to use recursion directly however, which might make implementing some algorithms more annoying.
Re: Layers and critical features for embedded operating system
That is a possibility, if you know how to solve the halting problem on a Turing machine with interrupts. Because between dynamic code paths, recursion, and signal handlers, the amount of stack used is very hard to forecast. Just because a path wasn't taken in your last attempt doesn't mean it is impossible.
Very simple case study: musl's printf() has a subfunction that needs 8kB of stack. The compiler might have inlined that function into printf_core(), in which case the allocation happens for every call, but if not, then the allocation happens only if "%f" appears in the format string. Or with the newest versions, "%Lf". If you call printf() with a format string dynamically received from run-time data (e.g. a localization catalog) it becomes impossible to determine beforehand if that path is taken.
These 8k are well above the minimum stack requirement musl imposes on all thread and signal stacks, which is 2k. Good luck figuring that out.
Carpe diem!
- Demindiro
- Member
- Posts: 111
- Joined: Fri Jun 11, 2021 6:02 am
- Libera.chat IRC: demindiro
- Location: Belgium
- Contact:
Re: Layers and critical features for embedded operating system
If you have non-recursive interrupts you only need to reserve a fixed amount of stack space (if at all) for any data that might be pushed automatically, then switch to a separate stack.
Determining the amount of stack necessary is typically trivial for small programs. Note that the target in question is an 8-bit AVR microcontroller. If you look at the source the stack is at a fixed 256 bytes currently, not much fancy you're going to do with that.
Determining the amount of stack necessary is typically trivial for small programs. Note that the target in question is an 8-bit AVR microcontroller. If you look at the source the stack is at a fixed 256 bytes currently, not much fancy you're going to do with that.
- Doul
- Posts: 3
- Joined: Sat Feb 15, 2025 3:56 pm
- GitHub: https://codeberg.org/Doul09/TaskMate
Re: Layers and critical features for embedded operating system
Your messages make me think to implement a stack monitoring system to log maximum task usage, value fixed to <80%, otherwise I have to upgrade stack size. Monitoring is even more reliable than calculations in a nested interruptions/functions call through multiple layers.
For detecting stack overflow I plan to use : boundary checks and/or canary values.
To prevent stack overflow critical data corruption on other stacks, I think to spread stacks over all memory, without wasting memory, just interpose less critical data (or no data at all) between stacks.
Do you know other ways to detect or prevent stack overflow ?
For detecting stack overflow I plan to use : boundary checks and/or canary values.
To prevent stack overflow critical data corruption on other stacks, I think to spread stacks over all memory, without wasting memory, just interpose less critical data (or no data at all) between stacks.

INT 21 is my ex-girlfriend