Layers and critical features for embedded operating system

Discussions on more advanced topics such as monolithic vs micro-kernels, transactional memory models, and paging vs segmentation should go here. Use this forum to expand and improve the wiki!
Post Reply
User avatar
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

Post by Doul »

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!
INT 21 is my ex-girlfriend
User avatar
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

Post by Doul »

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 :
TaskMate_layers.png
TaskMate_layers.png (8.79 KiB) Viewed 107580 times
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
User avatar
Demindiro
Member
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

Post by Demindiro »

Doul wrote: Sat Feb 15, 2025 5:03 pm Robust stack overflow control
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.
My OS is Norost B (website, Github, sourcehut)
My filesystem is NRFS (Github, sourcehut)
^ defunct
nullplan
Member
Member
Posts: 1851
Joined: Wed Aug 30, 2017 8:24 am

Re: Layers and critical features for embedded operating system

Post by nullplan »

Demindiro wrote: Wed Mar 12, 2025 12:58 am Instead of trying to manage stack overflow it might be more interesting to rule it out entirely.
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!
User avatar
Demindiro
Member
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

Post by Demindiro »

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.
My OS is Norost B (website, Github, sourcehut)
My filesystem is NRFS (Github, sourcehut)
^ defunct
User avatar
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

Post by Doul »

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 ?
INT 21 is my ex-girlfriend
Post Reply