- 

Created:2018-11-14  Last modified:2018-11-14


  1. Create

    C knowledge

    1. macro ##: concatenate variable name: e.g. #define con(a, b) a##b
      int con(x, y) = 10;
      printf("%d\n", xy);
    2. macro #: convert variable name to string: e.g. #define str(a) #a
      printf("%s\n", str(hello));
    3. define function pointer
      typedef void (*os_pthread) (void const *argument);
      os_pthread is a function type that takes a const * as argument and return void
    4. Code analysis Based on dynamic allocation, task stack is allocated by this function instead of allocated by application developers.
                          osThreadId DefaultTaskHandle;
                          void thread1(void const * argument);
                  
                          osThreadDef(DefaultTask, thread1, osPriorityNormal, 0, 128);
                          DefaultTaskHandle = osThreadCreate(osThread(DefaultTask), NULL);
                  
      
                      ///// equivalent
                          void * DefaultTaskHandle = xTaskCreate( thread1, // defined by FreeRTOS
                             "DefaultTask",
                             128, // 4x size
                              NULL,
                              UBaseType_t uxPriority,
                             // &DefaultTaskHandle )
      
                  
                          /////////////////////////// Inside /////////////////////////////////
                          
                          #define osThreadDef(name, thread, priority, instances, stacksz)  \
                          const osThreadDef_t os_thread_def_##name = \
                          { #name, (thread), (priority), (instances), (stacksz)}
                  
                          typedef struct os_thread_def  {
                              char                   *name;        /// Thread name 
                              os_pthread             pthread;      /// start address of thread function
                              osPriority             tpriority;    /// initial thread priority
                              uint32_t               instances;    /// maximum number of instances of that thread function
                              uint32_t               stacksize;    /// stack size requirements in bytes; 0 is default stack size
                            #if( configSUPPORT_STATIC_ALLOCATION == 1 )
                              uint32_t               *buffer;      /// stack buffer for static allocation; NULL for dynamic allocation
                              osStaticThreadDef_t    *controlblock;     /// control block to hold thread's data for static allocation; NULL for dynamic allocation
                            #endif
                            } osThreadDef_t;
                  
                          
                          
                          
                          osThreadDef(DefaultTask, thread1, osPriorityNormal, 0, 128);    /// =>
                          const osThreadDef_t os_thread_def_DefaultTask = {
                              "DefaultTask", 
                              thread1,
                              osPriorityNormal,
                              0,
                              128
                          };
                  
                  
                          typedef TaskHandle_t osThreadId; // defined by cmsis_os.h
                          typedef void * TaskHandle_t; // defined by FreeRTOS's task.h
                  
                          osThreadId osThreadCreate (const osThreadDef_t *thread_def, void *argument)
                          {
                              TaskHandle_t handle;
                              #else
                              if (xTaskCreate((TaskFunction_t)thread_def->pthread,(const portCHAR *)thread_def->name,
                                              thread_def->stacksize, argument, makeFreeRtosPriority(thread_def->tpriority),
                                              &handle) != pdPASS)  {
                                  return NULL;
                              }     
                              #endif
                              return handle;
                          }
                  
                          BaseType_t xTaskCreate(	TaskFunction_t pxTaskCode, // defined by FreeRTOS
                                              const char * const pcName,
                                              const uint16_t usStackDepth,
                                              void * const pvParameters,
                                              UBaseType_t uxPriority,
                                              TaskHandle_t * const pxCreatedTask ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
                          {
                          TCB_t *pxNewTCB;
                          BaseType_t xReturn;
                  
                              {
                              StackType_t *pxStack;
                  
                                  /* Allocate space for the stack used by the task being created. */
                                  // #define portSTACK_TYPE	uint32_t4
                                  // typedef portSTACK_TYPE StackType_t;
                                  pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
                  
                                  if( pxStack != NULL )
                                  {
                                      /* Allocate space for the TCB. */
                                      pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); /*lint !e961 MISRA exception as the casts are only redundant for some paths. */
                  
                                      if( pxNewTCB != NULL )
                                      {
                                          /* Store the stack location in the TCB. */
                                          pxNewTCB->pxStack = pxStack;
                                      }
                                      else
                                      {
                                          /* The stack cannot be used as the TCB was not created.  Free
                                          it again. */
                                          vPortFree( pxStack );
                                      }
                                  }
                                  else
                                  {
                                      pxNewTCB = NULL;
                                  }
                              }
                              #endif /* portSTACK_GROWTH */
                  
                              if( pxNewTCB != NULL )
                              {
                                  prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );
                                  prvAddNewTaskToReadyList( pxNewTCB );
                                  xReturn = pdPASS;
                              }
                              else
                              {
                                  xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
                              }
                  
                              return xReturn;
                          }
                  
  2. Details

    1. TCB structure

                              typedef tskTCB TCB_t;
                              /* the tskTCB is highly configured by macro (FreeRTOSConfig.h), additional functions cause additional fields in the TCB structure e.g. using mutex 
                              */
                              typedef struct tskTaskControlBlock{
                                  volatile StackType_t	*pxTopOfStack;
                                  ListItem_t			xStateListItem;	/* The list that the state list item of a task is reference from denotes the state of that task (Ready, Blocked, Suspended ). */
                                  ListItem_t			xEventListItem; /* e.g. Waiting Queue's data or lock*/
                                  UBaseType_t			uxPriority;		/* The priority of the task.  0 is the lowest priority. */
                                  StackType_t			*pxStack;		/* Points to the start of the stack. */
                                  char				pcTaskName[ configMAX_TASK_NAME_LEN ];
                                  #if ( configUSE_MUTEXES == 1 )
                                      UBaseType_t		uxBasePriority;
                                      UBaseType_t		uxMutexesHeld;
                                  #endif
                                  #if( configUSE_TASK_NOTIFICATIONS == 1 )
                                      volatile uint32_t ulNotifiedValue;
                                      volatile uint8_t ucNotifyState;
                                  #endif
                              }tskTCB;
                          
    2. Initialize TCB

      Initialized stack by port function, initialized TCB's name, priority, event list, state list

                              static void prvInitialiseNewTask( 	TaskFunction_t pxTaskCode,
      									const char * const pcName,
      									const uint32_t ulStackDepth,
      									void * const pvParameters,
      									UBaseType_t uxPriority,
      									TaskHandle_t * const pxCreatedTask,
      									TCB_t *pxNewTCB,
      									const MemoryRegion_t * const xRegions )
                              {
                                  StackType_t *pxTopOfStack;
                                  UBaseType_t x;
                                  /*config TCB including stack pointer, name, priority, optional mutex*/
      
                                  /*
                                  * Nan: the stack is allocated from heap. pxStack is the small address in this chuck of memory
                                  * But stack is used from higher address to lower address, so we need caculate the top of the address;
                                  * Moreover, alignment is required by the hardware.
                                  */ 
                                  pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );
      		                    pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); /*lint !e923 MISRA exception.  Avoiding casts between pointers and integers is not practical.  Size differences accounted for using portPOINTER_SIZE_TYPE type. */
                                  pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );
      		                    /* Check the alignment of the calculated top of stack is correct. */
                                  configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) );
                                  
                                  for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ )
                                  {
                                      pxNewTCB->pcTaskName[ x ] = pcName[ x ];
      
                                      /* Don't copy all configMAX_TASK_NAME_LEN if the string is shorter than
                                      configMAX_TASK_NAME_LEN characters just in case the memory after the
                                      string is not accessible (extremely unlikely). */
                                      if( pcName[ x ] == 0x00 )
                                      {
                                          break;
                                      }
                                  }
                                  pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0';
                                  if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES )
                                  {
                                      uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
                                  }
                                  pxNewTCB->uxPriority = uxPriority;
      
                                  /*Avoid priority inversion*/
                                  #if ( configUSE_MUTEXES == 1 )
                                  {
                                      pxNewTCB->uxBasePriority = uxPriority;
                                      pxNewTCB->uxMutexesHeld = 0;
                                  }
                                  *pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
                              }
                          
    3. Scheduler

      The scheduler defined a couple of global variables in the task.c source file.

                              // current running task 
                              TCB_t * volatile pxCurrentTCB = NULL;
                              PRIVILEGED_DATA static volatile UBaseType_t uxCurrentNumberOfTasks 	= ( UBaseType_t ) 0U;
                              PRIVILEGED_DATA static volatile TickType_t xTickCount 				= ( TickType_t ) 0U;
                              PRIVILEGED_DATA static volatile UBaseType_t uxTopReadyPriority 		= tskIDLE_PRIORITY;
                              PRIVILEGED_DATA static volatile BaseType_t xSchedulerRunning 		= pdFALSE;
                              PRIVILEGED_DATA static volatile UBaseType_t uxPendedTicks 			= ( UBaseType_t ) 0U;
                              PRIVILEGED_DATA static volatile BaseType_t xYieldPending 			= pdFALSE;
                              PRIVILEGED_DATA static volatile BaseType_t xNumOfOverflows 			= ( BaseType_t ) 0;
                              PRIVILEGED_DATA static UBaseType_t uxTaskNumber 					= ( UBaseType_t ) 0U;
                              PRIVILEGED_DATA static volatile TickType_t xNextTaskUnblockTime		= ( TickType_t ) 0U; /* Initialised to portMAX_DELAY before the scheduler starts. */
                              PRIVILEGED_DATA static TaskHandle_t xIdleTaskHandle					= NULL;			/* Holds the handle of the idle task.  The idle task is created automatically when the scheduler is started. */
      
                          
    4. STM32 stack initialize

      This part is ported by STM32

                                  StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
                                  {
                                      /* Simulate the stack frame as it would be created by a context switch
                                      interrupt. */
                                      pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */
                                      *pxTopOfStack = portINITIAL_XPSR;	/* xPSR */
                                      pxTopOfStack--;
                                      *pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK;	/* PC */
                                      pxTopOfStack--;
                                      *pxTopOfStack = ( StackType_t ) prvTaskExitError;	/* LR */
                                  
                                      pxTopOfStack -= 5;	/* R12, R3, R2 and R1. */
                                      *pxTopOfStack = ( StackType_t ) pvParameters;	/* R0 */
                                      pxTopOfStack -= 8;	/* R11, R10, R9, R8, R7, R6, R5 and R4. */
                                  
                                      return pxTopOfStack;
                                  }
                          
    5. Add TCB to ready list

      This step is done under a critical section

      The scheduler has a

                              static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB )
                              {
                                  taskENTER_CRITICAL(); // critical section
                                  {
                                      uxCurrentNumberOfTasks++;
                                      if( pxCurrentTCB == NULL )
                                      {
                                          /* There are no other tasks, or all the other tasks are in
                                          the suspended state - make this the current task. */
                                          pxCurrentTCB = pxNewTCB;
                                          prvInitialiseTaskLists();
                                      }
                                      else
                                      {
                                          /* If the scheduler is not already running, make this task the
                                          current task if it is the highest priority task to be created
                                          so far. */
                                          if( xSchedulerRunning == pdFALSE )
                                          {
                                              if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority )
                                              {
                                                  pxCurrentTCB = pxNewTCB;
                                              }
                                          }
                                      }
                                      uxTaskNumber++;
                                      prvAddTaskToReadyList( pxNewTCB );
                                      portSETUP_TCB( pxNewTCB );
                                  }
                                  taskEXIT_CRITICAL();
                              }
                          
                              // prvAddTaskToReadyList does two things, first record the used priority
                              ( uxReadyPriorities ) |= ( 1UL << ( uxPriority ) )
                              // second add to the corresponding priority ready list
                              vListInsertEnd( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) )
                          

    prvInitialiseNewTask

    prvAddNewTaskToReadyList

  3. References