FreeRTOS notes from official books
- Each combination of processor and compiler is a port
- FreeRTOSConfig.h and other key concepts/files
- task
- queue
- list
- semphr
- timers
- event_groups
- heap
- Portmacro.h
- TickType_t - tick count value - 16/32 bit. Use 32 for 32-bit uC
- BaseType_t - Most efficient data type. 32-bit on a 32-bit uC
- Naming convention
- Variables prefixed with their type
- 'c' for char, 's' for short (int16), 'l' for long (int_32), 'x' for BaseType_t
- in addition, 'u' if unsigned, 'p' if pointer
- Functions are prefixed with the type they return and the file they are in
- vTaskPrioritySet() returns void and is in task.c
- pvTimerGetTimerID() returns pointer to void and is in timers.c
- File scope private functions are prefixed with 'prv'
- Macros are in uppercase, prefixed with lower case indicating where they are defined
- portMAX_DELAY is in portable.h or portmacro.h
- taskENTER_CRITICAL is in task.h
- pdTRUE is in projdefs.h
- Heaps
- Each task needs a control block and stack from the heap
- pvPortMalloc(), pvPortFree()
- Heap_1 to 5: 1 does not free memory, 2 is replaced by 4, 3 is same as C
- v9/10 does not require explicit settings (may update after reading v9/10)
- Fragmentation
- Clash with linker, reserve memory for heap after leaving out for linker
- Task Management
- Each task is a program in itself with infinite loop, will not exit, to delete (vTaskDelete)
- Multiple task instances from same task definition. Each with it's own stack
- Static variables
- Only one copy, shared between all instances
- Not on stack (i.e., auto allocated memory)
- Are on statically allocated memory
- Constant variables are also not on stack
- If one core: a task can be 'running' or 'not running'
- BaseType_t xTaskCreate (TaskFunction_t pvTaskCode, const char * const pcName, unit16_t usStackDepth, void *pvParameters, UBaseType_t uxPriority, TaskHandle_t *pxCreatedTask)
- Returns pdPASS/FAIL. App should check to ensure task created/not
- vTaskScheduler()
- Tick interrupt - frequency/time period. configTICK_RATE_HZ (100 typical)
- Time between 2 tick interrupts = tick period = 1 time slice
- Scheduler must execute itself at end of each time slice, to choose task
- If a higher priority task is 'ready', it will be executed - every time
- Not-running can be
- Blocked (timer / synch data events)
- Suspended
- Ready
- vTaskDelay() moves running --> blocked
- vTaskDelayUntil() for periodic. More reliable (fixed delay between start of task)
- Idle task
- auto created, least priority
- responsible for cleaning kernel resources after vTaskDelete so should not be 'starved' (never getting priority to execute)
- Idle task hook
- auto called once per iteration of idle task loop
- for low priority tasks, measure capacity, placing processor in low power mode
- must not block/suspend, if vTaskDelete is used return quickly
- Change priority - vTaskPrioritySet()
- Summary on page 117
- Scheduling algorithms and configurations
- configUSE_PREEMPTION (P), configUSE_TIMESLICING (T)
- configUSE_TIMELESS_IDLE (for low power consumption)
- Fixed priority, pre-emptive scheduling with time slice (P=1, T=1). Most common.
- In P, higher priority ready task will run immediately (without waiting for tick period to finish)
- configIDLE_SHOULD_YIELD, if 1, will run for 1 iteration then yield to same priority task
- Prioritised, pre-emptive scheduling without time slice (P=1, T=0). Can save scheduler overhead but can result in equal priority task getting very different processing times
- Co-operative (P=0, B=any). Next task only if running task is blocked/yields. Easier to manage sharing resources (e.g. non-corruption of data to UART). System less responsive (e.g. Idle time taking long processing time, not yielding to higher priority tasks)
- Queue management
- Finite # of elements, fixed length of each
- FIFO: write to end, read from front, can also write at front
- Q by copy concept (not reference/pointer)
- For reading, when data becomes available, highest priority task will be unblocked automatically. Similar for write, when q becomes available
- QueueHandle_t xQueueCreate ( UBaseType_t uxQueueLength, UBaseType_t uxItemSize)
- Returns NULL if not enough memory (for data and structures) in heap
- xQueueReset()
- xQueueSend=SendToBack, xQueueSendToFront
- BaseType_t xQueueSendToBack (QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait)
- Returns Pass/Full/Empty
- BaseType_t xQueueReceive (QueueHandle_t xQueue, void * pvBuffer, TickType_t xTicksToWait)
- Generally 1 receiver, multiple senders are common
- Static function: can only be called in it's own file
- Static specifier link, const specifier link
- Extra infinite loop (for;;) in main if things fail due to memory
- If receiver priority > sender, q will not have > 1 item, sender's block time can be 0
- If sender priority > receiver, q will always be full, receiver's block time can be 0
- Timer management
- schedule a function execution at a set time (one shot) in future or periodically (auto-reload)
- configUSE_TIMERS
- void ATimerCallback (TimerHandle_t xTimer)
- should be short, must not block calling task
- Dormant / running
- xTimerCreate(), xTimerStart(), xTimerReset(), xTimerChangePeriod(), xTimerStop(), xTimerDelete()
- RTOS daemon (timer service) task. configTIMER_TASK_PRIORITY, config_TIMER_TASK_STACK_DEPTH
- timer API functions send commands from calling task to the daemon task on 'timer command queue' (config_TIMER_QUEUE_LENGTH)
- Command on timer queue contain time-stamp - to account for time passed b/w command sent and command processed
- TimerID - useful when same callback function is used by multiple timers
- Get/Set timer ID do not use the queue, access the timer directly
- xTimerChangePeriod used to change the frequency with which the callback function is called e.g. to toggle an LED
- xTimerReset will also start a task if in dormant state - mobile backlight example
- Interrupt management
- ISR as short as possible
- Task is unrelated to hardware, ISR is a hardware feature
- ISRs have higher priority than all tasks
- ISRs can only call functions with _FROM_ISR in their name
- No automatic yield (as in tasks), can portYIELD_FROM_ISR with HigherPriorityTaskWoken=pdTRUE, task after ISR can be different from task before ISR in this case
- Deferred interrupt processing using higher priority task + yielding + binary semaphores (a queue of length 1)
- Alternatively, use timer queue xTimerPendFunctionCallFromISR()
- xSemaphoreGiveFromISR, xSemaphoreTake
- Introduce measures to ensure handler/deferred function processes all interrupts if interrupt speed is higher than handler processing speed
- configMAX_SYSCALL_INTERRUPT_PRIORITY - highest priority from where API functions can be called (set to 11 or 191 in our case)
- configKERNEL_INTERRUPT_PRIORITY - lowest possible (0 or 255)
- Numeric/Logical priority, note on ARM Cortex M where higher the number, lower the priority
- [VIP] Interrupts with priority > configMAX_SYSCALL_INTERRUPT_PRIORITY can not be delayed by anything kernel is doing & cannot call API functions
- Resource management
- Thread-safe functions: use only stack variables so each instance has it's own copy of those variables. Also called re-entrant. Not using global/static variables
- Mutual exclusion for shared resources to save them from corruption
- Critical section - taskENTER_CRITICAL, taskEXIT_CRITICAL, only VIP interrupts can delay code within this, no other interrupts/tasks
- Scheduler suspension: xTaskSuspendAll, xTaskResumeALL, only stops tasks from interfering, all interrupts can
- Mutexes: take and return binary semaphore
- priority inversion: HP waiting for LP since LP reserved resource first; solution: priority inheritance. LP's priority raised to HPs for required time
- Deadlock: HP waiting for LP, LP waiting for HP; solution: do not wait indefinitely
- Self-deadlock: HP called another function internally that waits for HP; solution: Recursive mutex (other set of APIs)
- Same priority tasks in deadlock; solution: explicit Yield; task1 may run for 'longer period' but at least task2 will get to run on next tick
- Same priority tasks, task 1 gives and takes back semaphore within that 'longer period'; solution: explicit yield if tickcount has increased
- All above are complex and hence should be avoided in design
- Gatekeeper task: everyone uses a shared resource via gatekeeper task (example includes a tick hook interrupt)
- Event groups
- Multiple tasks linked with multiple events
- Controlled by bits in a variable (of size 32)
- create/set/get bits functions
- setBitsFromISR uses RTOS daemon task since ISR not allowed to trigger a non-deterministic operation (don't know how many tasks are going to get unblocked!)
- xEventGroupWaitBits: arguments include 'BitstoWaitFor', 'WaitForAllBits' (one or all), 'ClearOnExit'. Return value will be the pre-cleared value
- Using event groups for task synch: xEventGroupSync. Using bits of the variable to indicate 'tasks' (not 'events')
- Task notifications
- Direct task to task - no intermediary (queue/semaphore/event group)
- configUSE_TASK_NOTIFICATIONS (not set to 1 in case of ESP8266!)
- Notification state - pending (received notification) / not pending (reads value)
- Notification value - number of notifications/data
- Faster and uses less RAM but can't notify from task->ISR, enable/send to >1 task, send > 1 data item, wait in blocked state for a task that is unable to receive notification
- xTaskNotifyGive(), ulTaskNotifyTake()
- if xClearCountOnExit is pdTRUE, all available events are to be processed between each call to ulTaskNotifyTake(); if FALSE, only one event can be processed
- Dev support
- configAssert() - to verify an assumption made by the program, no execution past a line that has failed assertion
- Trace: diagnostic, percepio
- Debug hooks (callbacks): Malloc failed, stack overflow
- View run time and task state info: uxTaskSystemState(), vTaskGetRunTimeStats(), vTaskList()
- Run-time stats clock: 10-100 times faster than tick interrupt freqeuncy
- Trace hook macros - includes ISRs too
- Troubleshooting
- Incorrect interrupt priority: don't leave un-initialised, note if numerical priority is inverse to logical priority, special care of priority w.r.t configMAX_SYSCALL_INTERRUPT_PRIORITY
- Stack overflow - uxTaskGetStackHighWaterMark() - amount of stack that remains unused when stack usage is at it's greatest; configCHECK_FOR_STACK_OVERFLOW. If 1, checks only at context switch, if 2, checks last 20 bytes change at all times
- Wrong use of printf: can hugely increase size, only use function specially designed for 'embedded systems', printf-stdarg.c
- V9 additions
- Completely statically equivalent systems - APIs, xTaskcreateStatic etc.
- Forcing task to leave blocked state - xTaskAbortDelay etc.
- If one task deletes another, memory freed immediately
- Link for complete changes
- V10 additions
- Stream buffers: inter process communication with 1 reader, 1 writer e.g. isr to task or one processor core to another, continuous stream of bytes
- message buffers: discrete messages of varying length
- Link for complete changes
No comments:
Post a Comment