In ChibiOS/RT (and most other RTOSes) there are two distinct asynchronous contexts: threads and ISRs, we need to handle synchronizations among those.
You may remember that in ChibiOS API the function suffix indicates the compatible context for functions, a runtime "state checker" makes sure that functions are called from the proper context. In Safer C the compiler should be able to do the very same thing at compile time without runtime overhead.
Let's define two asynchronous contexts:
Threads
The class name would be "thread", all thread functions would be declared as follow:
Code: Select all
root async <thread> void my_thread(void *p) {
/* Thread code.*/
}
Any RTOS function meant to be called from thread context would be declared as follow:
Code: Select all
async <thread> msg_t chSemWaitTimeout(semaphore_t *semp, sysinterval_t timeout) {
/* Semaphore code.*/
}
The compatibility mechanism makes sure that the function chSemWaitTimeout() can be called only from a thread asynchronous functions. This is equivalent to the ChibiOS/RT functions without suffix but checked at compile time instead of runtime.
ISRs
ISRs would have asynchronous class "isr" and be declared as:
Code: Select all
async <isr> void my_isr(void) {
/* ISR code.*/
}
Kernel
This context defines a kernel critical zones, in ChibiOS/RT those are implemented by calling chSysLock(), chSysLockFromISR() etc. Let's define a construct for this.
Code: Select all
cdef async <thread> <isr> kernel {
async_enter <thread>:
chSysLock();
async_leave <thread>:
chSysUnlock();
async_enter <isr>:
chSysLockFromISR();
async_leave <isr>:
chSysUnlockFromISR();
};
From this "kernel" construct it is possible to access functions and data structures marked as "kernel". Let's try with semaphores. The construct can only be used by "isr" and "thread" classes asynchronous functions. Note that the implementation of the critical zone is different if used from "thread" or "isr" functions.
Code: Select all
/* The semaphore data structure can only be manipulated from a "kernel" context.*/
typedef kernel struct {
/* Semaphore data.*/
} semaphore_t;
/* The function can only be called from thread context, the semaphore data is accessed
from kernel context. Note that returning from within a kernel {} construct implicitly
leaves the compound statement, it is not possible to forget a chSysUnlock().*/
async <thread> msg_t chSemWaitTimeout(semaphore_t *semp, sysinterval_t timeout) {
context kernel {
if (--sp->cnt < (cnt_t)0) {
if (TIME_IMMEDIATE == timeout) {
sp->cnt++;
return MSG_TIMEOUT;
}
currp->u.wtsemp = sp;
sem_insert(currp, &sp->queue);
return chSchGoSleepTimeoutS(CH_STATE_WTSEM, timeout);
}
return MSG_OK;
}
}
/* Can only be called from "kernel" context (within a critical zone).*/
kernel void chSemSignalI(semaphore_t *semp) {
if (++sp->cnt <= (cnt_t)0) {
thread_t *tp = queue_fifo_remove(&sp->queue);
tp->u.rdymsg = MSG_OK;
(void) chSchReadyI(tp);
}
}
/* Variant to be called from "isr" class functions.*/
async <isr> chSemSignalFromISR(semaphore_t *semp) {
context kernel {
chSemSignalI(semp);
}
}
Note how context compatibility is enforced at compile time rather than at runtime, critical data access (the semaphore) is enforced to be performed from within a critical zone, it cannot be manipulated in other ways.
TO BE CONTINUED