Cortex-M4F: Alignment requirements for StaticTask_t and StackType_t

damien_d wrote on Monday, October 15, 2018:

Dear All,

I am looking to clarify if alignment is required when using statically allocated tasks and, by extension, statically allocated stacks for Idle and Timers.

I am currently using GCC with an NXP S32K144 EVK (ARM Cortex M4F).

For example, I am currently allocating my tasks using the following pattern, following the examplies in the documentation:

// Does either StaticTask_t or StackType_t need to be aligned on 8-bytes? 
// This:     #define ALIGN_8_BYTES
// or this?  #define ALIGN_8_BYTES __attribute__ ((aligned (8)))

#define STACK_DEPTH_FLASH_LED    256

static TaskHandle_t xTaskHandle_FlashLED;
static StaticTask_t ALIGN_8_BYTES m_xTaskBuffer_FlashLED;
static StackType_t  ALIGN_8_BYTES m_xStack_FlashLED[STACK_DEPTH_FLASH_LED];

#define NO_THREAD_PARAMETERS    NULL

xTaskHandle_FlashLED = xTaskCreateStatic(
            vTask_FlashLED,
            "flash_led",
            STACK_DEPTH_FLASH_LED,
            NO_THREAD_PARAMETERS,
            TASK_PRIORITY_FLASH_LED,
            m_xStack_FlashLED,
            &m_xTaskBuffer_FlashLED);

When looking at tasks.c for the dynamic case, the allocated memory will be aligned to (in the case of the CM4F) 8-byte boundaries, as guarunteed by pvPortAlloc:

{
		StackType_t *pxStack;

			/* Allocate space for the stack used by the task being created. */
			pxStack = pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack and this allocation is the stack. */

			if( pxStack != NULL )
			{
				/* Allocate space for the TCB. */
				pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); /*lint !e9087 !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack, and the first member of TCB_t is always a pointer to the task's stack. */

				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;
			}
		}

As I understand it, The CM4F requires that the stack is 8-byte aligned. However, since StackType_t is a uint32_t, then GCC will allocate this to a 4-byte boundary, not an 8-byte boundary, e.g.

 .bss.m_xTaskBuffer_FlashLED
                0x000000002000435c       0x4c /tmp/ccRQFTy2.ltrans0.ltrans.o

So, should the statically allocated stack also be 8-byte aligned using gcc’s attribute ((aligned (8))) to ensure alignment? What is the likely consequence if this is not the case. Certainly, this does indeed force aligment:

 *fill*         0x00000000200043c4        0x4 
 .bss.m_xTaskBuffer_FlashLED
                0x00000000200043c8       0x4c /tmp/ccPCqzB6.ltrans0.ltrans.o

And, would this be consequential for other statically alloctaed buffer, especially queues since the storage area is declared as a uint8_t:

For example:


#define DEBUG_UART_QUEUE_SIZE       32
#define DEBUG_UART_QUEUE_ITEM_SIZE  sizeof(SUARTDatagram)

static QueueHandle_t m_queue_DebugUART;

static ALIGN_8_BYTES uint8_t m_queue_DebugUART_storageArea[
        DEBUG_UART_QUEUE_SIZE * DEBUG_UART_QUEUE_ITEM_SIZE];

static ALIGN_8_BYTES StaticQueue_t m_queue_DebugUART_StaticQueue;

m_queue_DebugUART = xQueueCreateStatic(
            DEBUG_UART_QUEUE_SIZE,
            DEBUG_UART_QUEUE_ITEM_SIZE,
            m_queue_DebugUART_storageArea,
            &m_queue_DebugUART_StaticQueue);
            

Kind regards,
Damien.

rtel wrote on Monday, October 15, 2018:

You will probably find that the stack gets aligned within the
xTaskCreateStatic() function - I can’t check myself right now but will
do when I can. In the mean time if you see this is not the case then
please let us know.

rtel wrote on Monday, October 15, 2018:

Just verified - the stack is aligned inside prvInitialiseNewTask(), and
both xTaskCreate() and xTaskCreateStatic() call prvInitialiseNewTask().

damien_d wrote on Tuesday, October 16, 2018:

Thanks very much!
– Damien.