The System Layer is the most fundamental part of the RT kernel, it lies just above the port layers and provides a series of important services:
This service handles the system initialization:
chSysInit() | Starts the RT kernel. This function must be called once from the main() function. |
The RT kernel is always initialized as follow:
#include "ch.h" void main(void) { /* * System initializations. * - Kernel initialization, the main() function becomes a thread and the * RTOS is active. Interrupts are enabled on chSysInit() exit. */ chSysInit(); . . . }
Note that the main() function is not meant to return, the system would stop.
This service handles the system panic stop, a reason can be provided for post mortem inspection or logging:
CH_CFG_SYSTEM_HALT_HOOK(reason) | Hook macro for inserting a custom action before the system halt. |
chSysHalt(const char *reason) | Stops the system, the behavior of the function can be overridden using a hook. |
A panic is usually used in response of a unexpected non-recoverable condition.
#include "ch.h" void main(void) { . . . chSysHalt("unexpected condition #12"); }
This service offers ISR abstraction and IRQ handling functions.
CH_IRQ_HANDLER() | Declaration of a normal interrupt handler. |
CH_IRQ_PROLOGUE() | ISR prologue code. |
CH_IRQ_EPILOGUE() | ISR epilogue code. |
CH_FAST_IRQ_HANDLER() | Declaration of a non-OS (fast) interrupt handler. |
chSysEnable() | Enables all interrupts. |
chSysSuspend() | Disables normal interrupts, fast interrupts are kept enabled. |
chSysDisable() | Disables all interrupts. |
ISRs can be written in an architecture/compiler independent form, myISR is the name of the interrupt vector function. Note that there is an ISR declaration macro and two macros that mark the beginning and the end of the ISR, all the RTOS-specific code is hidden within the macros.
CH_IRQ_HANDLER(myISR) { CH_IRQ_PROLOGUE(); /* ISR code.*/ ...; CH_IRQ_EPILOGUE(); }
Interrupts that are not meant to interact with the Kernel can be written using this lighter form. No OS-related overhead is added to the ISR code path:
CH_FAST_IRQ_HANDLER(myISR) { /* Fast ISR code.*/ ...; }
This service handles critical sections in various flavors:
chSysLock() | Enters a thread-level critical section. |
chSysUnlock() | Leaves a thread-level critical section. |
chSysLockFromISR() | Enters an ISR-level critical section. |
chSysUnlockFromISR() | Leaves an ISR-level critical section. |
chSysUnconditionalLock() | Enters a thread-level critical section regardless the previous state. |
chSysUnconditionalUnlock() | Leaves a thread-level critical section regardless the previous state. |
chSysGetStatusAndLockX() | Enters a critical section from any context returning the previous state. |
chSysRestoreStatusX() | Restores a critical section status saved by chSysGetStatusAndLockX(), a reschedule operation is performed if required and applicable. |
THD_FUNCTION(myThread, arg) { /* Thread code.*/ ...; /* Entering a critical section.*/ chSysLock(); /* Protected code.*/ ...; /* Leaving the critical section.*/ chSysUnlock(); /* More thread code.*/ ...; }
CH_IRQ_HANDLER(myISR) { CH_IRQ_PROLOGUE(); /* ISR code.*/ ...; /* Entering a critical section.*/ chSysLockFromISR(); /* Protected code.*/ ...; /* Leaving the critical section.*/ chSysUnlockFromISR(); /* More ISR code.*/ ...; CH_IRQ_EPILOGUE(); }
/* * This function can be called from within or outside critical sections and * either in thread or ISR context. */ void myFunc(void) { /* Protected or unprotected code.*/ ...; /* Conditionally entering a critical section.*/ syssts_t sts = chSysGetStatusAndLockX(); /* Protected code.*/ ...; /* Conditionally leaving the critical section.*/ chSysRestoreStatusX(sts); /* Protected or unprotected code.*/ ...; }
The kernel does not handle power management directly however there are features meant to allow the implementation of any kind of power management in response of important kernel actions. Another important power-related feature is the Tickless Mode that will be discussed in the Virtual Timers chapter.
CH_CFG_IDLE_ENTER_HOOK() | This hook is invoked when the system is going to switch into the idle thread. It can be used to enter a power saving mode. |
CH_CFG_IDLE_LEAVE_HOOK() | This hook is invoked when the system is going to switch out of idle thread. It can be used to leave a power saving mode. |
CH_CFG_IDLE_LOOP_HOOK() | Hook invoked from within the idle thread loop. |
Examples are not provided for the above hooks because any implementation would be necessarily architecture-dependent.
The realtime counter is an optional RT kernel feature. It is present only if the underlying architecture supports the required feature: a very fast counter driver increased by the system clock. The realtime counter is used for:
PORT_SUPPORTS_RT | If defined, this macro indicates that the current kernel port supports the Realtime Counter feature. It can be used for conditional code or compile time errors. |
S2RTC() | Converts from seconds to number of RT clocks pulses. |
MS2RTC() | Converts from milliseconds to number of RT clocks pulses. |
US2RTC() | Converts from microseconds to number of RT clocks pulses. |
RTC2S() | Converts from number of RT clocks pulses to seconds. |
RTC2MS() | Converts from number of RT clocks pulses to milliseconds. |
RTC2US() | Converts from number of RT clocks pulses to microseconds. |
chSysIsCounterWithinX() | Determines if the specified counter value is within the specified interval. |
chSysPolledDelayX() | Inserts an accurate delay into execution of the current thread or ISR. |
This is a common need when dealing with HW devices, it is often required to do something after a small amount of time. Such delays cannot be reliably implemented as software loops. An interesting thing is that the function can be preempted and the ISRs/threads execution is accounted as part of the delays not as an added extra delay improving accuracy.
void start_conversion(void) { /* Activate peripheral.*/ ADC->CR1 |= ADC_CR1_ACTIVE; /* Wait at least 12uS before the ADC can be used.*/ chSysPolledDelayX(US2RTC(SYSCLK, 12)); /* Starts the conversion.*/ ADC->CR1 |= ADC_CR_START; }
Another common need is to have an escape way for software loops:
void wait_conversion(void) { /* Wait the ADC to complete the operation, after 10mS a failure is assumed.*/ rtcnt_t start = chSysGetRealtimeCounterX(); rtcnt_t end = start + MS2RTC(10); do { if ((ADC->SR & ADC_SR_DONE) != 0) return; } while (chSysIsCounterWithinX(chSysGetRealtimeCounterX(), start, end)); /* This is unexpected, probably an ADC failure.*/ chSysHalt("ADC failure"); }