osal_rs/freertos/system.rs
1/***************************************************************************
2 *
3 * osal-rs
4 * Copyright (C) 2026 Antonio Salsi <passy.linux@zresa.it>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
18 *
19 ***************************************************************************/
20
21//! System-level functions and utilities for FreeRTOS.
22//!
23//! This module provides access to system-wide functionality including:
24//! - Scheduler control (start, stop, suspend, resume)
25//! - System time and delay functions
26//! - Thread enumeration and state inspection
27//! - Critical sections
28//! - Heap memory information
29
30use core::fmt::Debug;
31use core::ops::Deref;
32use core::time::Duration;
33
34use alloc::vec::Vec;
35
36use super::ffi::task::{BLOCKED, DELETED, READY, RUNNING, SUSPENDED};
37use super::ffi::{
38 TaskStatus, eTaskGetState, osal_rs_critical_section_enter, osal_rs_critical_section_exit, osal_rs_port_end_switching_isr, osal_rs_port_yield_from_isr, uxTaskGetNumberOfTasks, uxTaskGetSystemState, vTaskDelay, vTaskEndScheduler, vTaskStartScheduler, vTaskSuspendAll, xPortGetFreeHeapSize, xTaskDelayUntil, xTaskGetCurrentTaskHandle, xTaskGetTickCount, xTaskResumeAll, osal_rs_task_enter_critical, osal_rs_task_enter_critical_from_isr, osal_rs_task_exit_critical, osal_rs_task_exit_critical_from_isr
39};
40use super::thread::{ThreadState, ThreadMetadata};
41use super::types::{BaseType, TickType, UBaseType};
42use crate::tick_period_ms;
43use crate::traits::{SystemFn, ToTick};
44use crate::utils::{CpuRegisterSize::*, register_bit_size, OsalRsBool};
45
46/// Represents a snapshot of the system state including all threads.
47///
48/// Contains metadata for all threads in the system and total runtime statistics.
49/// This is useful for monitoring, debugging, and profiling.
50///
51/// # Examples
52///
53/// ```ignore
54/// use osal_rs::os::{System, SystemFn};
55///
56/// let state = System::get_all_thread();
57///
58/// println!("Total threads: {}", state.tasks.len());
59/// println!("Total runtime: {}", state.total_run_time);
60///
61/// for thread in &state.tasks {
62/// println!("Thread: {} - Priority: {} - State: {:?}",
63/// thread.name,
64/// thread.priority,
65/// thread.state
66/// );
67/// }
68/// ```
69#[derive(Debug, Clone)]
70pub struct SystemState {
71 /// List of all thread metadata in the system
72 pub tasks: Vec<ThreadMetadata>,
73 /// Total runtime counter across all threads (if enabled)
74 pub total_run_time: u32
75}
76
77/// Provides access to the task list as a slice.
78impl Deref for SystemState {
79 type Target = [ThreadMetadata];
80
81 fn deref(&self) -> &Self::Target {
82 &self.tasks
83 }
84}
85
86/// System-level operations and utilities.
87///
88/// Provides a collection of static methods for controlling the FreeRTOS scheduler
89/// and accessing system-wide information. All methods are static.
90///
91/// # Examples
92///
93/// ## Starting the scheduler
94///
95/// ```ignore
96/// use osal_rs::os::{System, SystemFn};
97///
98/// // Create threads, queues, etc.
99/// // ...
100///
101/// // Start the scheduler (never returns in normal operation)
102/// System::start();
103/// ```
104///
105/// ## Delays and timing
106///
107/// ```ignore
108/// use osal_rs::os::{System, SystemFn};
109/// use core::time::Duration;
110///
111/// // Simple delay
112/// System::delay_with_to_tick(Duration::from_millis(500));
113///
114/// // Get current system time
115/// let now = System::get_current_time_us();
116/// println!("Uptime: {:?}", now);
117///
118/// // Periodic execution using delay_until
119/// let mut last_wake = System::get_tick_count();
120/// loop {
121/// System::delay_until_with_to_tick(&mut last_wake, Duration::from_millis(100));
122/// println!("Periodic task");
123/// }
124/// ```
125///
126/// ## Critical sections
127///
128/// ```ignore
129/// use osal_rs::os::{System, SystemFn};
130///
131/// // Protect shared data
132/// System::critical_section_enter();
133/// // Access shared data here
134/// // ...
135/// System::critical_section_exit();
136/// ```
137///
138/// ## Thread enumeration
139///
140/// ```ignore
141/// use osal_rs::os::{System, SystemFn};
142///
143/// let count = System::count_threads();
144/// println!("Active threads: {}", count);
145///
146/// let state = System::get_all_thread();
147/// for thread in &state.tasks {
148/// println!("Thread: {} - Stack high water: {}",
149/// thread.name,
150/// thread.stack_high_water_mark
151/// );
152/// }
153/// ```
154///
155/// ## Heap monitoring
156///
157/// ```ignore
158/// use osal_rs::os::{System, SystemFn};
159///
160/// let free_heap = System::get_free_heap_size();
161/// println!("Free heap: {} bytes", free_heap);
162/// ```
163///
164/// ## Scheduler suspend/resume
165///
166/// ```ignore
167/// use osal_rs::os::{System, SystemFn};
168///
169/// // Suspend scheduler for atomic operations
170/// System::suspend_all();
171/// // Perform atomic operations
172/// // ...
173/// System::resume_all();
174/// ```
175/// System-level operations and scheduler control.
176///
177/// Provides static methods for controlling the RTOS scheduler, timing,
178/// and system-wide operations.
179pub struct System;
180
181impl System {
182 /// Delays execution using a type that implements `ToTick`.
183 ///
184 /// Convenience method that accepts `Duration` or other tick-convertible types.
185 ///
186 /// # Examples
187 ///
188 /// ```ignore
189 /// use osal_rs::os::{System, SystemFn};
190 /// use core::time::Duration;
191 ///
192 /// System::delay_with_to_tick(Duration::from_millis(100));
193 /// ```
194 #[inline]
195 pub fn delay_with_to_tick(ticks: impl ToTick){
196 Self::delay(ticks.to_ticks());
197 }
198
199 /// Delays until an absolute time point with tick conversion.
200 ///
201 /// Used for precise periodic timing.
202 ///
203 /// # Parameters
204 ///
205 /// * `previous_wake_time` - Previous wake time (updated by this function)
206 /// * `time_increment` - Time increment for next wake
207 ///
208 /// # Examples
209 ///
210 /// ```ignore
211 /// use osal_rs::os::{System, SystemFn};
212 /// use core::time::Duration;
213 ///
214 /// let mut last_wake = System::get_tick_count();
215 /// loop {
216 /// // Do work...
217 /// System::delay_until_with_to_tick(&mut last_wake, Duration::from_millis(100));
218 /// }
219 /// ```
220 #[inline]
221 pub fn delay_until_with_to_tick(previous_wake_time: &mut TickType, time_increment: impl ToTick) {
222 Self::delay_until(previous_wake_time, time_increment.to_ticks());
223 }
224}
225
226impl SystemFn for System {
227 /// Starts the RTOS scheduler.
228 ///
229 /// This function never returns if successful. All created threads will
230 /// begin execution according to their priorities.
231 ///
232 /// # Examples
233 ///
234 /// ```ignore
235 /// use osal_rs::os::{System, SystemFn, Thread};
236 ///
237 /// // Create threads...
238 /// let thread = Thread::new("worker", 2048, 5, || {
239 /// loop { /* work */ }
240 /// }).unwrap();
241 ///
242 /// thread.start().unwrap();
243 ///
244 /// // Start scheduler (does not return)
245 /// System::start();
246 /// ```
247 fn start() {
248 unsafe {
249 vTaskStartScheduler();
250 }
251 }
252
253 /// Gets the state of the currently executing thread.
254 ///
255 /// # Returns
256 ///
257 /// Current thread state enum value
258 ///
259 /// # Examples
260 ///
261 /// ```ignore
262 /// use osal_rs::os::{System, SystemFn, ThreadState};
263 ///
264 /// let state = System::get_state();
265 /// match state {
266 /// ThreadState::Running => println!("Currently running"),
267 /// _ => println!("Other state"),
268 /// }
269 /// ```
270 fn get_state() -> ThreadState {
271 use super::thread::ThreadState::*;
272 let state = unsafe { eTaskGetState(xTaskGetCurrentTaskHandle()) };
273 match state {
274 RUNNING => Running,
275 READY => Ready,
276 BLOCKED => Blocked,
277 SUSPENDED => Suspended,
278 DELETED => Deleted,
279 _ => Invalid, // INVALID or unknown state
280 }
281 }
282
283 /// Suspends all tasks in the scheduler.
284 ///
285 /// No context switches will occur until `resume_all()` is called.
286 /// Use this to create atomic sections spanning multiple operations.
287 ///
288 /// # Examples
289 ///
290 /// ```ignore
291 /// use osal_rs::os::{System, SystemFn};
292 ///
293 /// System::suspend_all();
294 /// // Perform critical operations
295 /// System::resume_all();
296 /// ```
297 fn suspend_all() {
298 unsafe {
299 vTaskSuspendAll();
300 }
301 }
302
303 /// Resumes all suspended tasks.
304 ///
305 /// # Returns
306 ///
307 /// Non-zero if a context switch should occur
308 ///
309 /// # Examples
310 ///
311 /// ```ignore
312 /// System::resume_all();
313 /// ```
314 fn resume_all() -> BaseType {
315 unsafe { xTaskResumeAll() }
316 }
317
318 /// Stops the RTOS scheduler.
319 ///
320 /// All threads will stop executing. Rarely used in embedded systems.
321 ///
322 /// # Examples
323 ///
324 /// ```ignore
325 /// System::stop();
326 /// ```
327 fn stop() {
328 unsafe {
329 vTaskEndScheduler();
330 }
331 }
332
333 /// Returns the current tick count.
334 ///
335 /// The tick count increments with each RTOS tick interrupt.
336 ///
337 /// # Returns
338 ///
339 /// Current tick count value
340 ///
341 /// # Examples
342 ///
343 /// ```ignore
344 /// use osal_rs::os::{System, SystemFn};
345 ///
346 /// let ticks = System::get_tick_count();
347 /// println!("Current ticks: {}", ticks);
348 /// ```
349 fn get_tick_count() -> TickType {
350 unsafe { xTaskGetTickCount() }
351 }
352
353 /// Returns the current system time as a `Duration`.
354 ///
355 /// Converts the current tick count to microseconds and returns it as
356 /// a standard `Duration` type.
357 ///
358 /// # Returns
359 ///
360 /// Current system uptime as `Duration`
361 ///
362 /// # Examples
363 ///
364 /// ```ignore
365 /// use osal_rs::os::{System, SystemFn};
366 ///
367 /// let uptime = System::get_current_time_us();
368 /// println!("System uptime: {:?}", uptime);
369 /// ```
370 fn get_current_time_us () -> Duration {
371 let ticks = Self::get_tick_count();
372 Duration::from_millis( 1_000 * ticks as u64 / tick_period_ms!() as u64 )
373 }
374
375 /// Converts a `Duration` to microsecond ticks.
376 ///
377 /// Helper function for converting duration values to system tick counts
378 /// in microsecond resolution.
379 ///
380 /// # Parameters
381 ///
382 /// * `duration` - Duration to convert
383 ///
384 /// # Returns
385 ///
386 /// Equivalent tick count in microseconds
387 ///
388 /// # Examples
389 ///
390 /// ```ignore
391 /// use osal_rs::os::{System, SystemFn};
392 /// use core::time::Duration;
393 ///
394 /// let duration = Duration::from_millis(100);
395 /// let us_ticks = System::get_us_from_tick(&duration);
396 /// ```
397 fn get_us_from_tick(duration: &Duration) -> TickType {
398 let millis = duration.as_millis() as TickType;
399 millis / (1_000 * tick_period_ms!() as TickType)
400 }
401
402 /// Returns the number of threads currently in the system.
403 ///
404 /// Includes threads in all states (running, ready, blocked, suspended).
405 ///
406 /// # Returns
407 ///
408 /// Total number of threads
409 ///
410 /// # Examples
411 ///
412 /// ```ignore
413 /// use osal_rs::os::{System, SystemFn};
414 ///
415 /// let count = System::count_threads();
416 /// println!("Total threads: {}", count);
417 /// ```
418 fn count_threads() -> usize {
419 unsafe { uxTaskGetNumberOfTasks() as usize }
420 }
421
422 /// Retrieves a snapshot of all threads in the system.
423 ///
424 /// Returns detailed metadata for every thread including state, priority,
425 /// stack usage, and runtime statistics.
426 ///
427 /// # Returns
428 ///
429 /// `SystemState` containing all thread information
430 ///
431 /// # Examples
432 ///
433 /// ```ignore
434 /// use osal_rs::os::{System, SystemFn};
435 ///
436 /// let state = System::get_all_thread();
437 ///
438 /// for thread in &state.tasks {
439 /// println!("Thread: {} - Stack remaining: {}",
440 /// thread.name,
441 /// thread.stack_high_water_mark
442 /// );
443 /// }
444 /// ```
445 fn get_all_thread() -> SystemState {
446 let threads_count = Self::count_threads();
447 let mut threads: Vec<TaskStatus> = Vec::with_capacity(threads_count);
448 let mut total_run_time: u32 = 0;
449
450 unsafe {
451 let count = uxTaskGetSystemState(
452 threads.as_mut_ptr(),
453 threads_count as UBaseType,
454 &raw mut total_run_time,
455 ) as usize;
456
457 // Set the length only after data has been written by FreeRTOS
458 threads.set_len(count);
459 }
460
461 let tasks = threads.into_iter()
462 .map(|task_status| {
463 ThreadMetadata::from((
464 task_status.xHandle,
465 task_status
466 ))
467 }).collect();
468
469 SystemState {
470 tasks,
471 total_run_time
472 }
473 }
474
475
476 /// Delays the current thread for the specified number of ticks.
477 ///
478 /// The thread will enter the Blocked state for the delay period.
479 ///
480 /// # Parameters
481 ///
482 /// * `ticks` - Number of ticks to delay
483 ///
484 /// # Examples
485 ///
486 /// ```ignore
487 /// use osal_rs::os::{System, SystemFn};
488 ///
489 /// System::delay(100); // Delay 100 ticks
490 /// ```
491 fn delay(ticks: TickType){
492 unsafe {
493 vTaskDelay(ticks);
494 }
495 }
496
497 /// Delays until an absolute time point.
498 ///
499 /// Used for creating precise periodic timing. The `previous_wake_time`
500 /// is updated automatically for the next period.
501 ///
502 /// # Parameters
503 ///
504 /// * `previous_wake_time` - Pointer to last wake time (will be updated)
505 /// * `time_increment` - Period in ticks
506 ///
507 /// # Examples
508 ///
509 /// ```ignore
510 /// use osal_rs::os::{System, SystemFn};
511 ///
512 /// let mut last_wake = System::get_tick_count();
513 /// loop {
514 /// // Periodic task code...
515 /// System::delay_until(&mut last_wake, 100); // 100 tick period
516 /// }
517 /// ```
518 fn delay_until(previous_wake_time: &mut TickType, time_increment: TickType) {
519 unsafe {
520 xTaskDelayUntil(
521 previous_wake_time,
522 time_increment,
523 );
524 }
525 }
526
527 /// Enters a critical section.
528 ///
529 /// Disables interrupts or increments the scheduler lock nesting count.
530 /// Must be paired with `critical_section_exit()`.
531 ///
532 /// # Examples
533 ///
534 /// ```ignore
535 /// use osal_rs::os::{System, SystemFn};
536 ///
537 /// System::critical_section_enter();
538 /// // Critical code - no task switches or interrupts
539 /// System::critical_section_exit();
540 /// ```
541 fn critical_section_enter() {
542 unsafe {
543 osal_rs_critical_section_enter();
544 }
545 }
546
547 /// Exits a critical section.
548 ///
549 /// Re-enables interrupts or decrements the scheduler lock nesting count.
550 ///
551 /// # Examples
552 ///
553 /// ```ignore
554 /// System::critical_section_exit();
555 /// ```
556 fn critical_section_exit() {
557 unsafe {
558 osal_rs_critical_section_exit();
559 }
560 }
561
562 /// Checks if a timer has elapsed.
563 ///
564 /// Compares the elapsed time since a timestamp against a target duration,
565 /// handling tick counter overflow correctly for both 32-bit and 64-bit systems.
566 ///
567 /// # Parameters
568 ///
569 /// * `timestamp` - Starting time reference
570 /// * `time` - Target duration to wait for
571 ///
572 /// # Returns
573 ///
574 /// * `OsalRsBool::True` - Timer has elapsed
575 /// * `OsalRsBool::False` - Timer has not yet elapsed
576 ///
577 /// # Examples
578 ///
579 /// ```ignore
580 /// use osal_rs::os::{System, SystemFn};
581 /// use core::time::Duration;
582 ///
583 /// let start = System::get_current_time_us();
584 /// let timeout = Duration::from_secs(1);
585 ///
586 /// // Later...
587 /// if System::check_timer(&start, &timeout).into() {
588 /// println!("Timeout occurred");
589 /// }
590 /// ```
591 fn check_timer(timestamp: &Duration, time: &Duration) -> OsalRsBool {
592 let temp_tick_time = Self::get_current_time_us();
593
594 let time_passing = if temp_tick_time >= *timestamp {
595 temp_tick_time - *timestamp
596 } else {
597 if register_bit_size() == Bit32 {
598 // Handle tick count overflow for 32-bit TickType
599 let overflow_correction = Duration::from_micros(0xff_ff_ff_ff_u64);
600 overflow_correction - *timestamp + temp_tick_time
601 } else {
602 // Handle tick count overflow for 64-bit TickType
603 let overflow_correction = Duration::from_micros(0xff_ff_ff_ff_ff_ff_ff_ff_u64);
604 overflow_correction - *timestamp + temp_tick_time
605 }
606 };
607
608 if time_passing >= *time {
609 OsalRsBool::True
610 } else {
611 OsalRsBool::False
612 }
613 }
614
615 /// Yields to a higher priority task from ISR context.
616 ///
617 /// Should be called when an ISR operation wakes a higher priority task.
618 ///
619 /// # Parameters
620 ///
621 /// * `higher_priority_task_woken` - pdTRUE if higher priority task was woken
622 ///
623 /// # Examples
624 ///
625 /// ```ignore
626 /// // In ISR:
627 /// let mut woken = pdFALSE;
628 /// // ... ISR operations that might wake a task ...
629 /// System::yield_from_isr(woken);
630 /// ```
631 fn yield_from_isr(higher_priority_task_woken: BaseType) {
632 unsafe {
633 osal_rs_port_yield_from_isr(higher_priority_task_woken);
634 }
635 }
636
637 /// Ends ISR and performs context switch if needed.
638 ///
639 /// This function should be called at the end of an interrupt service routine
640 /// to trigger a context switch if a higher priority task was woken during
641 /// the ISR.
642 ///
643 /// # Parameters
644 ///
645 /// * `switch_required` - `pdTRUE` if context switch is required, `pdFALSE` otherwise
646 ///
647 /// # Examples
648 ///
649 /// ```ignore
650 /// use osal_rs::os::{System, SystemFn};
651 /// use osal_rs::os::ffi::pdTRUE;
652 ///
653 /// // In ISR:
654 /// let mut switch_required = pdFALSE;
655 /// // ... ISR operations that might require context switch ...
656 /// System::end_switching_isr(switch_required);
657 /// ```
658 fn end_switching_isr( switch_required: BaseType ) {
659 unsafe {
660 osal_rs_port_end_switching_isr( switch_required );
661 }
662 }
663
664 /// Enters a critical section at task level.
665 ///
666 /// Disables scheduler and interrupts to protect shared resources.
667 /// Must be paired with [`exit_critical()`](Self::exit_critical).
668 /// This is the task-level version; for ISR context use
669 /// [`enter_critical_from_isr()`](Self::enter_critical_from_isr).
670 ///
671 /// # Examples
672 ///
673 /// ```ignore
674 /// use osal_rs::os::{System, SystemFn};
675 ///
676 /// System::enter_critical();
677 /// // Access shared resource safely
678 /// System::exit_critical();
679 /// ```
680 fn enter_critical() {
681 unsafe {
682 osal_rs_task_enter_critical();
683 }
684 }
685
686 /// Exits a critical section at task level.
687 ///
688 /// Re-enables scheduler and interrupts after [`enter_critical()`](Self::enter_critical).
689 /// Must be called from the same task that called `enter_critical()`.
690 ///
691 /// # Examples
692 ///
693 /// ```ignore
694 /// use osal_rs::os::{System, SystemFn};
695 ///
696 /// System::enter_critical();
697 /// // Critical section code
698 /// System::exit_critical();
699 /// ```
700 fn exit_critical() {
701 unsafe {
702 osal_rs_task_exit_critical();
703 }
704 }
705
706 /// Enters a critical section from an ISR context.
707 ///
708 /// ISR-safe version of critical section entry. Returns the interrupt mask state
709 /// that must be passed to [`exit_critical_from_isr()`](Self::exit_critical_from_isr).
710 /// Use this instead of [`enter_critical()`](Self::enter_critical) when in interrupt context.
711 ///
712 /// # Returns
713 ///
714 /// Saved interrupt status to be restored on exit
715 ///
716 /// # Examples
717 ///
718 /// ```ignore
719 /// use osal_rs::os::{System, SystemFn};
720 ///
721 /// // In an interrupt handler
722 /// let saved_status = System::enter_critical_from_isr();
723 /// // Critical ISR code
724 /// System::exit_critical_from_isr(saved_status);
725 /// ```
726 fn enter_critical_from_isr() -> UBaseType {
727 unsafe {
728 osal_rs_task_enter_critical_from_isr()
729 }
730 }
731
732 /// Exits a critical section from an ISR context.
733 ///
734 /// Restores the interrupt mask to the state saved by
735 /// [`enter_critical_from_isr()`](Self::enter_critical_from_isr).
736 ///
737 /// # Parameters
738 ///
739 /// * `saved_interrupt_status` - Interrupt status returned by `enter_critical_from_isr()`
740 ///
741 /// # Examples
742 ///
743 /// ```ignore
744 /// use osal_rs::os::{System, SystemFn};
745 ///
746 /// let saved = System::enter_critical_from_isr();
747 /// // Protected ISR operations
748 /// System::exit_critical_from_isr(saved);
749 /// ```
750 fn exit_critical_from_isr(saved_interrupt_status: UBaseType) {
751 unsafe {
752 osal_rs_task_exit_critical_from_isr(saved_interrupt_status);
753 }
754 }
755
756
757 /// Returns the amount of free heap space.
758 ///
759 /// Useful for monitoring memory usage and detecting leaks.
760 ///
761 /// # Returns
762 ///
763 /// Number of free heap bytes
764 ///
765 /// # Examples
766 ///
767 /// ```ignore
768 /// use osal_rs::os::{System, SystemFn};
769 ///
770 /// let free = System::get_free_heap_size();
771 /// println!("Free heap: {} bytes", free);
772 /// ```
773 fn get_free_heap_size() -> usize {
774 unsafe {
775 xPortGetFreeHeapSize()
776 }
777 }
778
779}
780