Task Management
The core of Nano-RK is a Real-Time scheduler that supports preemptive scheduling, task synchronization and deadlock prevention. This guarantees that even under worst case conditions tasks will meet deadlines. We now discuss the details of the scheduler’s ability to enforce timeliness guarantees as well as predicable lifetime.
Nano-RK saves energy by ensuring that tasks never poll for a resource. Instead, tasks block on certain events (such as being woken up at a certain point of time or on the arrival of a network packet) and can be unblocked when the events occur. If there are no tasks eligible to run, the system can be powered down.
Nano-RK uses priority-based preemptive scheduling and while we provide explicit support for periodic tasks, we also support aperiodic and sporadic tasks in our framework. The highest priority task that is eligible to run in the system is always scheduled by the operating system. A periodic task can suspend itself before its CPU reserve has expired and wait until its next scheduled period. This adds slack to the system that can be used by soft real-time tasks.
Task synchronization is important in any system with multiple tasks that access a shared resource. In order to bound the blocking time encountered by a higher priority task, we implement priority ceiling protocol (PCP) emulation (Highest Locker Priority). This prevents the phenomenon of priority inversion wherein a shared resource needed by the high-priority process is currently being used by a lower-priority process. In Nano-RK, each mutex is associated with a priority ceiling value. When a mutex is acquired, the priority of the task is elevated to the priority ceiling of the mutex. Once the mutex is released, the priority of the task reverts to its original level. PCP allows offline schedulability analysis to bound the priority inversion problem.
#include <nrk.h> #include <include.h> #include <ulib.h> #include <stdio.h> int main () { nrk_setup_ports(); nrk_setup_uart(UART_BAUDRATE_115K2); printf( "Starting up...\r\n" ); nrk_init(); nrk_time_set(0,0); nrk_create_taskset(); // User defined function application task config nrk_start(); return 0; }
Typical startup code required to setup and begin Nano-RK.
nrk_setup_ports
| void nrk_setup_ports( void ) |
| Parameters: none. |
| Return Values: none |
This function configures the cpu specific ports based on the current platform. For instance, it may configure UART0 or UART1 as the primary serial output based on which hardware you have selected in the makefile.
nrk_init
| void nrk_init( void ) |
| Parameters: none. |
| Return Values: none |
This should be called before nrk_start() and before any user tasks are defined. This function configures the kernel stack, resources, the idle task and default TCB parameters.
nrk_start
| void nrk_start( void ) |
| Parameters: none. |
| Return Values: none |
Call this function once user tasks have been defined to start Nano-RK. This function schedules the first high priority task and begins execution of the OS. This function will never return and any state currently associated with the kernel stack thus far will be lost.
nrk_activate_task
| nrk_status_t nrk_activate_task( nrk_task_type * ) |
| Parameters: nrk_task_type * task pointer |
| Return Values: nrk_status_t NRK_OK upon success and NRK_ERROR on failure |
This function makes a user defined task runnable upon calling nrk_start. It operates on the nrk_task_type structure defined below.
typedef struct task_type { void *Ptos; // Top of stack pointer void *Pbos; // Bottom of stack pointer void (*task)(); // Function pointer to task entry point uint8_t prio; // Task priority, higher value has greater priority uint8_t Type; // Type of task uint8_t SchType; // Type of scheduling nrk_time_t period; // Period of Task nrk_time_t cpu_reserve; // CPU Reserve of task nrk_time_t offset; // Starting offset phase } nrk_task_type;
Task parameters set by a user to define a task. Related links: nrk_time_t
NRK_STK Stack1[NRK_APP_STACKSIZE]; nrk_task_type TaskOne; void Task1(void); ... void nrk_create_taskset() { nrk_task_set_entry_function( &TaskOne, Task1); nrk_task_set_stk( &TaskOne, Stack1, NRK_APP_STACK_SIZE); TaskOne.prio = 2; TaskOne.FirstActivation = TRUE; TaskOne.Type = BASIC_TASK; TaskOne.SchType = PREEMPTIVE; TaskOne.period.secs = 0; TaskOne.period.nano_secs = 200*NANOS_PER_MS; TaskOne.cpu_reserve.secs = 0; TaskOne.cpu_reserve.nano_secs = 50*NANOS_PER_MS; TaskOne.offset.secs = 0; TaskOne.offset.nano_secs= 0; nrk_activate_task (&TaskOne); }
Example of how a user would create a task that executes every 200ms with a 50ms reserve. Notice the task entry point and the task’s stack are passed as functions to the task structure.
nrk_terminate_task
| nrk_status_t nrk_terminate_task() |
| Parameters: none |
| Return Values: nrk_status_t NRK_OK upon success and NRK_ERROR on failure |
This can be called by a task to terminate itself. After being called, the task will no longer be scheduled.
nrk_get_pid
| uint8_t nrk_get_pid() |
| Parameters: none |
| Return Values: uint8_t containing the current task PID |
This functions returns the task ID to the currently running task. The task is given a unique ID before it is executed once nrk_start() is called. Returns the current task PID.
nrk_wait_until_next_period
| uint8_t nrk_wait_until_next_period() |
| Parameters: none |
| Return Values: NRK_ERROR or NRK_OK |
This function suspends a task until the start of its next period. When the task is suspended, other tasks can execute. If the system has no more tasks to execute, it will automatically enter a power-down state to save energy. Returns 1 upon success and 0 upon failure.
nrk_halt
| uint8_t nrk_halt() |
| Parameters: none |
| Return Values: none |
This functions halts the OS forever. If NRK_WATCHDOG_TIMER is enabled and there is no HALT_ON_ERROR defined in nrk_cfg.h, then nrk_halt() will reboot the node by allowing the watchdog timer to expire.
| Contents | Static Configuration |
