Skip to main content

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