Monday, 23 August 2021

FreeRTOS notes

FreeRTOS notes from official books

  1. Each combination of processor and compiler is a port
  2. FreeRTOSConfig.h and other key concepts/files
    • task
    • queue
    • list
    • semphr
    • timers
    • event_groups
    • heap
  3. 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
  4. 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
  5. 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
  6. 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)
  7. 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
  8. 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
  9. 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
  10. 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)
  11. 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')
  12. 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
  13. 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
  14. 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
  15. 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
  16. 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