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