This shows you the differences between two versions of the page.
|
pm:prj2026:bianca.popa1106:mihai.zegheru [2026/05/07 23:52] mihai.zegheru |
pm:prj2026:bianca.popa1106:mihai.zegheru [2026/05/21 00:45] (current) mihai.zegheru [Block Diagram] |
||
|---|---|---|---|
| Line 1: | Line 1: | ||
| + | |||
| ====== athreads - A Preemptive MCU Scheduler ====== | ====== athreads - A Preemptive MCU Scheduler ====== | ||
| Line 5: | Line 6: | ||
| **athreads** is a small preemptive scheduling library for 8-bit AVR microcontrollers, developed and demonstrated on the ATmega2560. | **athreads** is a small preemptive scheduling library for 8-bit AVR microcontrollers, developed and demonstrated on the ATmega2560. | ||
| - | The project implements a lightweight threading runtime in C and AVR assembly. It supports timer-driven preemption, thread context switching, per-thread time quanta, sleeping threads, and basic CPU usage accounting. | + | The project implements a lightweight threading runtime in C and AVR assembly. It supports timer-driven preemption, context switching, per-thread time quanta, sleeping threads, runtime stack allocation from a stack pool, and basic CPU usage accounting. |
| - | The original idea was to better understand how operating systems schedule tasks, but in a very constrained embedded environment where there is no operating system underneath. Instead of only simulating scheduling on a PC, this project runs directly on a microcontroller and exposes its behavior through hardware and profiling tools. | + | The original idea was to understand how operating systems schedule tasks, but in a constrained embedded environment where there is no operating system underneath. Instead of only simulating scheduling on a PC, this project runs directly on a microcontroller and exposes scheduler behavior through hardware UI and serial profiling. |
| - | The project is useful because it shows how a small preemptive runtime can be built from low-level primitives: interrupts, stacks, registers, timers, and context switching. It can be used as an educational tool for operating systems, embedded systems, real-time programming, and low-level performance profiling. | + | The project is useful as an educational low-level systems project because it shows how a preemptive runtime can be built from interrupts, stacks, registers, timers, and explicit context switching. |
| + | |||
| + | [[https://github.com/MihaiZegheru/MegaRT|Project repository]] | ||
| ===== General Description ===== | ===== General Description ===== | ||
| - | The project is centered around the **athreads** scheduler library. Around it, I built optional modules for visualization and profiling. | + | The project is centered around the **athreads** scheduler library. The scheduler runs on the ATmega2560 and uses a hardware timer interrupt to periodically preempt the currently running thread. |
| - | The scheduler runs on the ATmega2560 and uses a hardware timer interrupt to periodically preempt the currently running thread. Each thread has its own saved stack pointer and execution context. When a context switch happens, the current CPU state is saved and another thread is restored. | + | Each thread has: |
| + | |||
| + | * a thread descriptor; | ||
| + | * a saved stack pointer; | ||
| + | * a runtime-allocated stack region from the scheduler stack pool; | ||
| + | * a state; | ||
| + | * a time quantum; | ||
| + | * CPU tick accounting information. | ||
| The demo application adds: | The demo application adds: | ||
| - | * an SPI OLED screen for displaying running threads; | + | * an SPI TFT color display for showing running threads; |
| - | * a rotary encoder for selecting a thread and changing its time quantum; | + | * a rotary encoder for selecting a thread and changing its quantum; |
| * USART telemetry for sending CPU usage information to a PC; | * USART telemetry for sending CPU usage information to a PC; | ||
| - | * a Python desktop profiler that displays live per-thread CPU usage. | + | * a Python Task Manager-style profiler that displays live per-thread CPU usage. |
| ==== Block Diagram ==== | ==== Block Diagram ==== | ||
| - | <code> | + | {{ :pm:prj2026:bianca.popa1106:athreads_block_diagram.jpg?700 |}} |
| - | +-------------------------------------------------------------+ | + | |
| - | | PC / Laptop | | + | |
| - | | | | + | |
| - | | +-------------------------------+ | | + | |
| - | | | Python CPU Profiler | | | + | |
| - | | | - reads USART packets | | | + | |
| - | | | - plots per-thread CPU usage | | | + | |
| - | | +---------------^---------------+ | | + | |
| - | | | USB Serial | | + | |
| - | +------------------|------------------------------------------+ | + | |
| - | | | + | |
| - | +------------------v------------------------------------------+ | + | |
| - | | Arduino Mega 2560 | | + | |
| - | | | | + | |
| - | | +-------------------------------------------------------+ | | + | |
| - | | | athreads Scheduler | | | + | |
| - | | | | | | + | |
| - | | | - thread table | | | + | |
| - | | | - per-thread stack/context | | | + | |
| - | | | - round-robin scheduling | | | + | |
| - | | | - per-thread quantum | | | + | |
| - | | | - sleeping thread wakeup | | | + | |
| - | | | - CPU tick accounting | | | + | |
| - | | +-----------^--------------------^----------------------+ | | + | |
| - | | | | | | + | |
| - | | Timer1 interrupt Timer2 uptime tick | | + | |
| - | | preemption sleep/encoder timing | | + | |
| - | | | | + | |
| - | | +------------------+ +------------------+ | | + | |
| - | | | OLED UI Thread | | Encoder Thread | | | + | |
| - | | | process menu | | input handling | | | + | |
| - | | +--------^---------+ +--------^---------+ | | + | |
| - | | | | | | + | |
| - | | SPI OLED Rotary Encoder | | + | |
| - | | | | + | |
| - | | +-------------------------------------------------------+ | | + | |
| - | | | Worker Threads | | | + | |
| - | | | synthetic workloads used for profiling/demo purposes | | | + | |
| - | | +-------------------------------------------------------+ | | + | |
| - | +-------------------------------------------------------------+ | + | |
| - | </code> | + | |
| ===== Hardware Design ===== | ===== Hardware Design ===== | ||
| Line 75: | Line 44: | ||
| The hardware used for the demo consists of: | The hardware used for the demo consists of: | ||
| - | * Arduino Mega 2560 / ATmega2560; | + | * Arduino Mega 2560 board with ATmega2560 microcontroller; |
| - | * Waveshare 0.96 inch OLED display using SPI; | + | * 1.8 inch SPI TFT color display with ST7735 controller; |
| - | * rotary encoder with push button; | + | * rotary encoder module with push button; |
| * jumper wires and breadboard; | * jumper wires and breadboard; | ||
| - | * USB cable for programming and USART communication. | + | * USB cable for programming and serial communication. |
| - | ==== OLED Wiring ==== | + | ==== Component Documentation / Datasheets ==== |
| - | The OLED is configured in 4-wire SPI mode. | + | ^ Component ^ Documentation / Datasheet ^ |
| + | | Arduino Mega 2560 Rev3 | [[https://docs.arduino.cc/hardware/mega-2560/|Arduino Mega 2560 documentation]] | | ||
| + | | Arduino Mega 2560 Rev3 board datasheet | [[https://docs.arduino.cc/resources/datasheets/A000067-datasheet.pdf|Arduino Mega 2560 Rev3 datasheet PDF]] | | ||
| + | | Arduino Mega 2560 schematic | [[https://www.arduino.cc/en/uploads/Main/arduino-mega2560-schematic.pdf|Arduino Mega 2560 schematic PDF]] | | ||
| + | | ATmega2560 microcontroller | [[https://www.microchip.com/wwwproducts/en/atmega2560?tab=documents|Microchip ATmega2560 documentation and datasheet]] | | ||
| + | | ST7735R TFT controller | [[https://www.alldatasheet.com/datasheet-pdf/pdf/1179046/SITRONIX/ST7735R.html|Sitronix ST7735R datasheet]] | | ||
| + | | KY-040 rotary encoder module | [[https://components101.com/sites/default/files/component_datasheet/KY-04-Rotary-Encoder-Datasheet.pdf|KY-040 rotary encoder datasheet PDF]] | | ||
| - | ^ OLED Pin ^ Arduino Mega 2560 Pin ^ | + | ==== TFT Display Wiring ==== |
| - | | VCC | 3.3V | | + | |
| - | | GND | GND | | + | The current display is a 1.8 inch SPI TFT color display using an ST7735 controller. |
| - | | NC | Not connected | | + | |
| - | | DIN | D51 / MOSI | | + | ^ TFT Pin ^ Arduino Mega 2560 Pin ^ |
| - | | CLK | D52 / SCK | | + | | SCL / SCK / CLK | D52 / SCK | |
| + | | SDA / MOSI / DIN | D51 / MOSI | | ||
| + | | RST / RES | D48 | | ||
| + | | DC / A0 | D49 | | ||
| | CS | D53 | | | CS | D53 | | ||
| - | | DC | D49 | | + | | VCC | 5V | |
| - | | RES | D48 | | + | | GND | GND | |
| - | | BS0 | GND | | + | | BL / LED | 5V | |
| - | | BS1 | GND | | + | |
| + | The module used in this project is compatible with 5V systems, so it can be connected directly to the Arduino Mega 2560 pins. | ||
| + | |||
| + | <note important> | ||
| + | The ST7735 module used during testing required display inversion mode. The firmware enables ''INVON'', and the driver uses pre-inverted RGB565 color constants so that the physical display appears in dark mode correctly. | ||
| + | </note> | ||
| ==== Rotary Encoder Wiring ==== | ==== Rotary Encoder Wiring ==== | ||
| ^ Encoder Pin ^ Arduino Mega 2560 Pin ^ | ^ Encoder Pin ^ Arduino Mega 2560 Pin ^ | ||
| + | | SW | D47 | | ||
| + | | DT | D45 | | ||
| + | | CLK | D43 | | ||
| | + | 5V | | | + | 5V | | ||
| | GND | GND | | | GND | GND | | ||
| - | | CLK | D43 | | ||
| - | | DT | D45 | | ||
| - | | SW | D47 | | ||
| ==== Hardware Role ==== | ==== Hardware Role ==== | ||
| - | The OLED and rotary encoder are not required by the scheduler itself. They are used to demonstrate that the scheduler can run multiple independent threads and expose their state interactively. | + | The TFT display and rotary encoder are not required by the scheduler library itself. They are part of the demonstration layer. |
| - | The OLED displays the current thread list and time quantum values. The encoder allows the user to navigate between threads and modify a selected thread's quantum while the system is running. | + | The TFT shows the currently running threads and their quantum values. The encoder allows the user to navigate through the thread list, enter edit mode, and modify the selected thread's quantum while the scheduler is running. |
| ===== Software Design ===== | ===== Software Design ===== | ||
| Line 131: | Line 114: | ||
| platform/ USART, uptime, and debug headers | platform/ USART, uptime, and debug headers | ||
| profiling/ CPU statistics, tracing, and worker demo headers | profiling/ CPU statistics, tracing, and worker demo headers | ||
| - | ui/ OLED and encoder headers | + | ui/ TFT display and encoder headers |
| src/ | src/ | ||
| Line 137: | Line 120: | ||
| platform/ USART and millisecond uptime support | platform/ USART and millisecond uptime support | ||
| profiling/ CPU sampling, trace hooks, and demo workloads | profiling/ CPU sampling, trace hooks, and demo workloads | ||
| - | ui/ SPI OLED driver and rotary encoder input | + | ui/ ST7735 SPI TFT driver and rotary encoder input |
| main.c Demo firmware entry point | main.c Demo firmware entry point | ||
| Line 148: | Line 131: | ||
| ==== Scheduler Design ==== | ==== Scheduler Design ==== | ||
| - | The scheduler keeps a table of thread descriptors. Each descriptor contains information such as: | + | The scheduler keeps a table of thread descriptors. Each descriptor stores: |
| * thread ID; | * thread ID; | ||
| Line 158: | Line 141: | ||
| * scheduling metadata. | * scheduling metadata. | ||
| - | Each thread has its own stack. When a thread is created, the scheduler prepares its initial context so that it can later be started by the context switcher. | + | Threads are created with an explicit stack size. The scheduler allocates stack memory from a static stack pool at runtime. |
| - | Timer1 is used for preemption. When the timer interrupt fires, the running thread's quantum is updated. If the quantum expires, the scheduler selects another ready thread and switches context. | + | This avoids hardcoding one global array per thread, such as: |
| - | Timer2 is used for millisecond uptime and periodic support tasks, such as waking sleeping threads and debouncing the rotary encoder in the demo application. | + | <code c> |
| + | static uint8_t main_stack[MAIN_STACK_SIZE]; | ||
| + | static uint8_t worker_stack[WORK_STACK_SIZE]; | ||
| + | </code> | ||
| + | |||
| + | Instead, the scheduler owns a single pool: | ||
| + | |||
| + | <code c> | ||
| + | static uint8_t stack_pool[ATHREAD_STACK_POOL_SIZE]; | ||
| + | static uint16_t stack_pool_used; | ||
| + | </code> | ||
| + | |||
| + | When a thread is created, the scheduler reserves a slice of this pool and stores the stack boundaries in the thread descriptor. | ||
| + | |||
| + | ==== Preemption ==== | ||
| + | |||
| + | Timer1 is used for preemption. When the Timer1 compare interrupt fires, the scheduler updates the running thread's quantum counter. If the quantum expires, the current thread context is saved and another ready thread is selected. | ||
| + | |||
| + | Timer2 is used for millisecond uptime and periodic support work, including sleeping-thread wakeups and encoder debouncing. | ||
| ==== Public Scheduler API ==== | ==== Public Scheduler API ==== | ||
| ^ Function ^ Description ^ | ^ Function ^ Description ^ | ||
| - | | ''athread_init()'' | Initializes scheduler state, creates the idle thread, and prepares the scheduler before application threads are created. | | + | | ''athread_init()'' | Initializes scheduler state, creates the idle thread, and resets the stack pool. | |
| | ''athread_start()'' | Starts the scheduler. After this call, execution is controlled by the scheduler and the function does not normally return. | | | ''athread_start()'' | Starts the scheduler. After this call, execution is controlled by the scheduler and the function does not normally return. | | ||
| - | | ''athread_create(entry, info)'' | Creates a new thread with the given entry function and argument pointer. Returns the thread ID or ''ATHREAD_INVALID_TID'' on failure. | | + | | ''athread_create(entry, info, stack_size)'' | Creates a new thread, allocates ''stack_size'' bytes from the scheduler stack pool, initializes its context, and returns the thread ID or ''ATHREAD_INVALID_TID'' on failure. | |
| | ''athread_yield()'' | Voluntarily gives up the CPU and allows another ready thread to run. | | | ''athread_yield()'' | Voluntarily gives up the CPU and allows another ready thread to run. | | ||
| | ''athread_sleep_ticks(ticks)'' | Puts the current thread to sleep for a number of scheduler ticks. | | | ''athread_sleep_ticks(ticks)'' | Puts the current thread to sleep for a number of scheduler ticks. | | ||
| Line 177: | Line 178: | ||
| | ''athread_get_cpu_ticks(out_ticks, max_ticks)'' | Copies per-thread CPU tick counters for profiling and diagnostics. | | | ''athread_get_cpu_ticks(out_ticks, max_ticks)'' | Copies per-thread CPU tick counters for profiling and diagnostics. | | ||
| | ''athread_get_current_tid()'' | Returns the ID of the currently running thread. | | | ''athread_get_current_tid()'' | Returns the ID of the currently running thread. | | ||
| - | | ''athread_tick()'' | Advances scheduler timing state, including sleeping thread wakeups. Used by the platform timer code. | | + | | ''athread_tick()'' | Advances scheduler timing state, including sleeping-thread wakeups. Used by the platform timer code. | |
| | ''athread_bootstrap()'' | Internal startup wrapper used when a thread begins execution. | | | ''athread_bootstrap()'' | Internal startup wrapper used when a thread begins execution. | | ||
| + | |||
| + | Example thread creation: | ||
| + | |||
| + | <code c> | ||
| + | uint8_t tid = athread_create(worker_thread, worker_info, 512); | ||
| + | </code> | ||
| ==== Quantum ==== | ==== Quantum ==== | ||
| Line 190: | Line 197: | ||
| * a larger quantum gives a thread longer uninterrupted execution; | * a larger quantum gives a thread longer uninterrupted execution; | ||
| * a smaller quantum makes scheduling more responsive, but increases context switching overhead. | * a smaller quantum makes scheduling more responsive, but increases context switching overhead. | ||
| + | |||
| + | ==== TFT User Interface ==== | ||
| + | |||
| + | The TFT display shows a dark-mode process menu. It displays thread IDs, names, quantum values, and small quantum bars. | ||
| + | |||
| + | The rotary encoder is used as follows: | ||
| + | |||
| + | * rotate to move through the thread list; | ||
| + | * press to enter or leave quantum edit mode; | ||
| + | * rotate in edit mode to increase or decrease the selected thread's quantum. | ||
| + | |||
| + | The display driver uses ST7735 SPI commands and RGB565 color values. Because this module required inversion mode, the driver enables ''INVON'' and uses pre-inverted colors internally. | ||
| ==== Profiling ==== | ==== Profiling ==== | ||
| Line 203: | Line 222: | ||
| </code> | </code> | ||
| - | The port can be changed depending on where the Arduino Mega appears. | + | If the default Python installation does not include Tkinter, a Python installation with Tkinter support must be used. |
| ==== Demo Threads ==== | ==== Demo Threads ==== | ||
| Line 213: | Line 232: | ||
| | T1 | MAIN | Main application coordinator | | | T1 | MAIN | Main application coordinator | | ||
| | T2 | ENC | Rotary encoder input handling | | | T2 | ENC | Rotary encoder input handling | | ||
| - | | T3 | OLED | OLED process menu | | + | | T3 | TFT | TFT process menu | |
| | T4 | WRK1 | Synthetic worker workload | | | T4 | WRK1 | Synthetic worker workload | | ||
| | T5 | WRK2 | Synthetic worker workload | | | T5 | WRK2 | Synthetic worker workload | | ||
| Line 227: | Line 246: | ||
| * multiple independent threads can run on an 8-bit AVR microcontroller; | * multiple independent threads can run on an 8-bit AVR microcontroller; | ||
| * threads are preempted using a hardware timer interrupt; | * threads are preempted using a hardware timer interrupt; | ||
| - | * each thread has its own saved execution context; | + | * thread contexts are saved and restored using AVR assembly; |
| + | * stacks are allocated from a scheduler-managed stack pool; | ||
| + | * thread stack sizes are selected when creating each thread; | ||
| * threads can voluntarily yield or sleep; | * threads can voluntarily yield or sleep; | ||
| * time quanta can be changed at runtime; | * time quanta can be changed at runtime; | ||
| * per-thread CPU usage can be measured; | * per-thread CPU usage can be measured; | ||
| * scheduler behavior can be visualized live on a PC; | * scheduler behavior can be visualized live on a PC; | ||
| - | * thread state can also be inspected and modified from the OLED/encoder hardware UI. | + | * thread state can be inspected and modified using the TFT and encoder UI. |
| The profiling viewer makes it possible to observe how scheduling decisions affect CPU distribution between threads. | The profiling viewer makes it possible to observe how scheduling decisions affect CPU distribution between threads. | ||
| Line 238: | Line 259: | ||
| ===== Conclusions ===== | ===== Conclusions ===== | ||
| - | This project helped me understand preemptive scheduling from the hardware level upward. Implementing context switching on a microcontroller made the relationship between stacks, registers, interrupts, and scheduling much clearer than a high-level simulation would have. | + | This project helped me understand preemptive scheduling from the hardware level upward. Implementing context switching on a microcontroller made the relationship between stacks, registers, interrupts, timers, and scheduling much clearer than a high-level simulation would have. |
| - | The most challenging parts were the AVR assembly context switch, stack initialization for newly created threads, and making profiling work without disturbing the system too much. | + | The most challenging parts were the AVR assembly context switch, stack initialization for newly created threads, runtime stack pool management, and making profiling work without disturbing the system too much. |
| - | The final result is a small but functional preemptive scheduling library, with optional profiling tools that make the runtime behavior visible and easier to reason about. | + | The final result is a small but functional preemptive scheduling library, with optional profiling and hardware visualization tools that make the runtime behavior visible and easier to reason about. |
| Possible future improvements include: | Possible future improvements include: | ||
| Line 250: | Line 271: | ||
| * mutexes and synchronization primitives; | * mutexes and synchronization primitives; | ||
| * better stack usage diagnostics; | * better stack usage diagnostics; | ||
| + | * stack overflow detection; | ||
| * portability to other AVR boards or other MCU families. | * portability to other AVR boards or other MCU families. | ||
| ===== Download ===== | ===== Download ===== | ||
| - | The project source code, firmware, tools, and documentation are available in the repository. | + | The project source code, firmware, tools, and documentation are available in the repository: |
| - | Recommended archive contents: | + | [[https://github.com/MihaiZegheru/MegaRT|athreads project repository]] |
| - | + | ||
| - | * firmware source code; | + | |
| - | * public headers; | + | |
| - | * PlatformIO configuration; | + | |
| - | * Python profiling tools; | + | |
| - | * README file; | + | |
| - | * project images; | + | |
| - | * generated documentation or exported PDF. | + | |
| Build command: | Build command: | ||
| Line 278: | Line 292: | ||
| </code> | </code> | ||
| - | ===== Journal ===== | + | Run the CPU profiler: |
| - | ^ Date ^ Progress ^ | + | <code> |
| - | | Week 1 | Chose the project idea and target platform. Started studying AVR context switching and timer interrupts. | | + | python ./tools/cpu_task_manager.py --port COM3 |
| - | | Week 2 | Implemented the first version of the thread table and cooperative thread switching. | | + | </code> |
| - | | Week 3 | Added Timer1-based preemption and AVR assembly context save/restore. | | + | |
| - | | Week 4 | Added sleeping threads, uptime support, and basic scheduler API cleanup. | | + | |
| - | | Week 5 | Integrated the SPI OLED screen and displayed running threads. | | + | |
| - | | Week 6 | Added rotary encoder input and runtime quantum modification. | | + | |
| - | | Week 7 | Added USART CPU telemetry and Python live profiling viewer. | | + | |
| - | | Week 8 | Improved worker workloads, cleaned project structure, and documented the project. | | + | |
| ===== Bibliography / Resources ===== | ===== Bibliography / Resources ===== | ||
| - | ==== Software Resources ==== | + | ==== Hardware Resources ==== |
| - | * ATmega2560 datasheet | + | * [[https://docs.arduino.cc/hardware/mega-2560/|Arduino Mega 2560 Rev3 documentation]] |
| - | * AVR instruction set manual | + | * [[https://docs.arduino.cc/resources/datasheets/A000067-datasheet.pdf|Arduino Mega 2560 Rev3 datasheet]] |
| - | * PlatformIO documentation | + | * [[https://www.arduino.cc/en/uploads/Main/arduino-mega2560-schematic.pdf|Arduino Mega 2560 schematic]] |
| - | * avr-gcc documentation | + | * [[https://www.microchip.com/wwwproducts/en/atmega2560?tab=documents|Microchip ATmega2560 documentation and complete datasheet]] |
| - | * Python Tkinter documentation | + | * [[https://www.alldatasheet.com/datasheet-pdf/pdf/1179046/SITRONIX/ST7735R.html|Sitronix ST7735R TFT controller datasheet]] |
| + | * [[https://components101.com/sites/default/files/component_datasheet/KY-04-Rotary-Encoder-Datasheet.pdf|KY-040 rotary encoder module datasheet]] | ||
| - | ==== Hardware Resources ==== | + | ==== Software Resources ==== |
| - | * Arduino Mega 2560 documentation | + | * [[https://docs.platformio.org/|PlatformIO documentation]] |
| - | * Waveshare 0.96 inch OLED documentation | + | * [[https://gcc.gnu.org/wiki/avr-gcc|avr-gcc documentation]] |
| - | * SSD1306 OLED controller documentation | + | * [[https://docs.python.org/3/library/tkinter.html|Python Tkinter documentation]] |
| - | * Rotary encoder reference material | + | * [[https://onlinedocs.microchip.com/|Microchip AVR documentation portal]] |
| <html><a class="media mediafile mf_pdf" href="?do=export_pdf">Export to PDF</a></html> | <html><a class="media mediafile mf_pdf" href="?do=export_pdf">Export to PDF</a></html> | ||