_abort_stack_end

I often meet a problem of “_abort_stack_end”.
Do you know what causes this problem and how to overcome it?

I am grateful for any help!

I’m afraid you are going to have to provide a lot more information before anybody can begin to offer suggestions. For example, which FreeRTOS and compiler port are you using as _abort_stack_end sounds like a linker variable (i.e. not necessarily anything to do with FreeRTOS). Under what circumstances does this happen (when you first turn it on, after a few months, after a particular event, etc.).

@rtel : I apologize for an unclear question.

I am running FreeRTOS on two Cortex A9 cores of Zynq 7000.
When I start software in a debug mode, I see one core doesn’t work as expected. I wonder why _abort_stack_end() happens as the following screenshot.

Thanks. If I understand correctly then, you get a Data Abort exception when running in debug mode.

Have you tried debugging the data abort to find where in the code it is caused?
Does this also happen in Release mode?
Does it work ok when you run the code ‘standalone’ rather than through the debugger (because the startup requirements on the Zynq are different when running in the debugger Vs standalone).
Does this happen immediately, or occasionally?
Have you seen the instructions regarding careful use of standard library functions that might unknowingly use floating point instructions?

@rtel: Thanks for your hints!

Yes, I have.
The program fails to create a task, then jumps to _abort_stack_end and Data Abort Exception.
The following screenshot shows information of pxIndex when a task cannot be created.

This screenshot shows information of pxIndex when a task is successfully created.

It happens similarly in Release Mode.

I don’t get what “standalone” means. Can you clarify it?

Both cores run FreeRTOS. This problem happens immediately on Core 0 when the software creates a task. However, Core 1 doesn’t experience this issue.

Floating point is not used.

Can you elaborate on ‘fails to create a task’? The image shows 0xa5a5a5a5 being dereferenced (which is the value used to fill the stack), which will be the cause of the data abort, but something went wrong before then. Does ‘failed to create a task’ mean it could not allocate the memory for the task? If so, how did it then get into vListInsertEnd() within vTaskCreate() without having first allocated the required memory?

Thanks for your guidance!

—> a task cannot be created.

	BaseType_t xTaskCreate(	TaskFunction_t pxTaskCode,
						const char * const pcName,		/*lint !e971 Unqualified char types are allowed for strings and single characters only. */
						const configSTACK_DEPTH_TYPE usStackDepth,
						void * const pvParameters,
						UBaseType_t uxPriority,
						TaskHandle_t * const pxCreatedTask )
{
     ...

    prvAddNewTaskToReadyList( pxNewTCB );

    ...
}

static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB )
{
      ...
   
      prvAddTaskToReadyList( pxNewTCB );

     ...
}

 #define prvAddTaskToReadyList( pxTCB )																\
traceMOVED_TASK_TO_READY_STATE( pxTCB );														\
taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority );												\
vListInsertEnd( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) ); \
tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB )


   void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
  {
       ...

  }

Exception: Cannot read target memory. Memory read error at 0xA5A5A5A5. PL AXI slave ports access is not allowed. This address has not been added to the memory map

This is value of pxList that is passed to the function vListInsertEnd()

I wonder why 0xa5a5a5a5 is dereferenced.

As per my last post - something went wrong before then. We need to find out why the task cannot be created. It looks like the task create function runs, errs, doesn’t notice it has erred, then tried to continue anyway with bad values that then lead to the abort - so the question is where did the bad value come from?

Sorry for the delay in replying!

When the program creates its first task.

static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB )
{
	/* Ensure interrupts don't access the task lists while the lists are being
	updated. */
	taskENTER_CRITICAL();
	{
		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;

			if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 )
			{
				/* This is the first task to be created so do the preliminary
				initialisation required.  We will not recover if this call
				fails, but we will report the failure. */
				prvInitialiseTaskLists();
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		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;
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}

		uxTaskNumber++;

		#if ( configUSE_TRACE_FACILITY == 1 )
		{
			/* Add a counter into the TCB for tracing only. */
			pxNewTCB->uxTCBNumber = uxTaskNumber;
		}
		#endif /* configUSE_TRACE_FACILITY */
		traceTASK_CREATE( pxNewTCB );

		prvAddTaskToReadyList( pxNewTCB );

		portSETUP_TCB( pxNewTCB );
	}
	taskEXIT_CRITICAL();

	if( xSchedulerRunning != pdFALSE )
	{
		/* If the created task is of a higher priority than the current task
		then it should run now. */
		if( pxCurrentTCB->uxPriority < pxNewTCB->uxPriority )
		{
			taskYIELD_IF_USING_PREEMPTION();
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}
}

uxCurrentNumberOfTasks has value of 0xa5a5a5a5.

However, it should be 0 because the first task is being created and its initial value is 0.
PRIVILEGED_DATA static volatile UBaseType_t uxCurrentNumberOfTasks = ( UBaseType_t ) 0U;

Values of pxCurrentTCB

Similarly, pxCurrentTCB should be NULL.

PRIVILEGED_DATA TCB_t * volatile pxCurrentTCB = NULL;





/*
 * Place the task represented by pxTCB into the appropriate ready list for
 * the task.  It is inserted at the end of the list.
 */
#define prvAddTaskToReadyList( pxTCB )																\
	traceMOVED_TASK_TO_READY_STATE( pxTCB );														\
	taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority );												\
	vListInsertEnd( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) ); \
	tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB )

pxReadyTasksLists has values

I haven’t found the reason why the bad value appears yet.

Hi XuanTran,

I’m afraid there is not a big value in posting screen shots of FreeRTOS code. Can you show us the code YOU wrote, in particular the sequence up to your first invocation of xTaskCreate()? Can you show us the contents of your interrupt vector table and your linker command file? It almost looks as if your startup stack gets corrupted.

The source code is as follows.

   //disable caching on shared memory
    Xil_SetTlbAttributes( 0x00000000  ,0x14DE2);
    Xil_SetTlbAttributes(OCM_SHMEM_LO_ADDR,0x14DE2);

    shared_data_ptr->shm_valid=0;

    // SHM core2core constructs.
    // XScuGic IntcInstance;   // scope: portZynq7000.c:xInterruptController
    extern XScuGic xInterruptController;
    static int InterruptCounter = 0;


    //Setup semaphore on shared memory and initialize it
    StaticSemaphore_t * xSemaphoreBuffer = (StaticSemaphore_t *)SHMEM_SEMPHR_ADDR;
    xSemaphore = xSemaphoreCreateBinaryStatic(xSemaphoreBuffer);
    xSemaphoreGive(xSemaphore);

    //Setup function call queue
    StaticQueue_t * rpcCallQueue = (StaticQueue_t *)SHMEM_FNCTN_QUEUE_ADDR;
    uint8_t * queueStorageArea = (uint8_t *)SHMEM_FNCTN_QUEUE_STORAGE_ADDR;
    xFnctnQueue = xQueueGenericCreateStatic(FNCTN_QUEUE_LENGTH,FNCTN_STRUCT_SIZE,queueStorageArea,rpcCallQueue,0);

    //Setup function return queue
    StaticQueue_t * rpcReturnQueue = (StaticQueue_t *)SHMEM_RETURN_QUEUE_ADDR;
    uint8_t * retQueueStorageArea = (uint8_t *)SHMEM_RETURN_QUEUE_STORAGE_ADDR;
    xReturnQueue = xQueueGenericCreateStatic(RETURN_QUEUE_LENGTH,RETURN_VALUE_SIZE,retQueueStorageArea,rpcReturnQueue,0);

    // init the UARTs before waking up CPU1
    u32 * uartAvailable = &shared_data_ptr->uart_available;
    (*uartAvailable) = 0;
    dmb();
	if ( lld_uart_init() == 0 ) {
		(*uartAvailable) = 2;      // send ownership to core 0
	};

    sleep(1);

    xil_printf("CPU0: writing startaddress for cpu1 and reset.\n\r");

    {
    	/*
    	 *  Reset and start CPU1
    	 *  - Application for cpu1 exists at APP_CPU1_ADDR per cpu1 linkerscript
    	 *
    	 */
		#include "xil_misc_psreset_api.h"
		#include "xil_io.h"

    	#define A9_CPU_RST_CTRL		(XSLCR_BASEADDR + 0x244)
		#define A9_RST1_MASK 		0x00000002
		#define A9_CLKSTOP1_MASK	0x00000020
		#define CPU1_CATCH			0x00000024

		#define XSLCR_LOCK_ADDR		(XSLCR_BASEADDR + 0x4)
		#define XSLCR_LOCK_CODE		0x0000767B

    	u32 RegVal;

    	/*
    	 * Setup cpu1 catch address with starting address of app_cpu1. The FSBL initialized the vector table at 0x00000000
    	 * using a boot.S that checks for cpu number and jumps to the address stored at the
    	 * end of the vector table in cpu0_catch and cpu1_catch entries.
    	 * Note: Cache has been disabled at the beginning of main(). Otherwise
		 * a cache flush would have to be issued after this write
    	 */
    	Xil_Out32(CPU1_CATCH, APP_CPU1_ADDR);

    	/* Unlock the slcr register access lock */
    	Xil_Out32(XSLCR_UNLOCK_ADDR, XSLCR_UNLOCK_CODE);

    	//    the user must stop the associated clock, de-assert the reset, and then restart the clock. During a
    	//    system or POR reset, hardware automatically takes care of this. Therefore, a CPU cannot run the code
    	//    that applies the software reset to itself. This reset needs to be applied by the other CPU or through
    	//    JTAG or PL. Assuming the user wants to reset CPU1, the user must to set the following fields in the
    	//    slcr.A9_CPU_RST_CTRL (address 0xF8000244) register in the order listed:
    	//    1. A9_RST1 = 1 to assert reset to CPU0
    	//    2. A9_CLKSTOP1 = 1 to stop clock to CPU0
    	//    3. A9_RST1 = 0 to release reset to CPU0
    	//    4. A9_CLKSTOP1 = 0 to restart clock to CPU0

    	/* Assert and deassert cpu1 reset and clkstop using above sequence*/
    	RegVal = 	Xil_In32(A9_CPU_RST_CTRL);
    	RegVal |= A9_RST1_MASK;
    	Xil_Out32(A9_CPU_RST_CTRL, RegVal);
    	RegVal |= A9_CLKSTOP1_MASK;
    	Xil_Out32(A9_CPU_RST_CTRL, RegVal);
    	RegVal &= ~A9_RST1_MASK;
		Xil_Out32(A9_CPU_RST_CTRL, RegVal);
    	RegVal &= ~A9_CLKSTOP1_MASK;
		Xil_Out32(A9_CPU_RST_CTRL, RegVal);

    	/* lock the slcr register access */
    	Xil_Out32(XSLCR_LOCK_ADDR, XSLCR_LOCK_CODE);
    }


    while(! (shared_data_ptr->uart_available == 1)){
    	// wait for core 1 to hand back UART handle (simple "alive" detection)
    }

    u32 reg_val;
    //ensure OCM is mapped low
    reg_val = Xil_In32(XPS_SYS_CTRL_BASEADDR + 0x910);
    if((reg_val & 0x000F) != 0) {
        Xil_Out32((XPS_SYS_CTRL_BASEADDR + 0x8),0xDF0D );
        Xil_Out32((XPS_SYS_CTRL_BASEADDR + 0x910),(reg_val & 0xFFF0));
        Xil_Out32((XPS_SYS_CTRL_BASEADDR + 0x4),0x767B );
    }


    TaskHandle_t xadcTask;

	/* Start the two tasks */
	xTaskCreate( ccLld_powerController, ( const char * ) "PWRCTL",
			configMINIMAL_STACK_SIZE, NULL,
			POWER_CONTROLLER_TASK_PRIORITY, &pctrlTask );
	xTaskCreate( ccLld_i2cHandler, ( const char * ) "I2C",
			configMINIMAL_STACK_SIZE, NULL,
			I2C_HANDLER_TASK_PRIORITY, &i2cHandlerTaskHandle);
	xTaskCreate( ccLld_pmbusHandler, ( const char *) "PMBUS",
			configMINIMAL_STACK_SIZE, NULL,
			PMBUS_HANDLER_TASK_PRIORITY , &pmbusHandlerTaskHandle);
	xTaskCreate( ccLld_xadcLoop, ( const char * ) "XADC",
			configMINIMAL_STACK_SIZE, NULL,
			XADC_TASK_PRIORITY, &xadcTask );
	xTaskCreate(ccLld_qziHandler, (const char *) "QZI",
			configMINIMAL_STACK_SIZE, NULL,
			QZI_HANDLER_TASK_PRIORITY, &qziHandlerTaskHandle);

	/* Start the tasks and timer running. */
	vTaskStartScheduler();

	/* If all is well, the scheduler will now be running, and the following line
	will never be reached.  If the following line does execute, then there was
	insufficient FreeRTOS heap memory available for the idle and/or timer tasks
	to be created.  See the memory management section on the FreeRTOS web site
	for more details. */
	for( ;; );

lscript.ld

_STACK_SIZE = DEFINED(_STACK_SIZE) ? _STACK_SIZE : 0x40000;
_HEAP_SIZE = DEFINED(_HEAP_SIZE) ? _HEAP_SIZE : 0x20000;

_ABORT_STACK_SIZE = DEFINED(_ABORT_STACK_SIZE) ? _ABORT_STACK_SIZE : 1024;
_SUPERVISOR_STACK_SIZE = DEFINED(_SUPERVISOR_STACK_SIZE) ? _SUPERVISOR_STACK_SIZE : 2048;
_IRQ_STACK_SIZE = DEFINED(_IRQ_STACK_SIZE) ? _IRQ_STACK_SIZE : 1024;
_FIQ_STACK_SIZE = DEFINED(_FIQ_STACK_SIZE) ? _FIQ_STACK_SIZE : 1024;
_UNDEF_STACK_SIZE = DEFINED(_UNDEF_STACK_SIZE) ? _UNDEF_STACK_SIZE : 1024;

/* Define Memories in the system */

MEMORY
{
   ps7_ddr_0_S_AXI_BASEADDR : ORIGIN = 0x01000000, LENGTH = 0x01000000
   ps7_qspi_linear_0_S_AXI_BASEADDR : ORIGIN = 0xFC000000, LENGTH = 0x1000000
   ps7_ram_0_S_AXI_BASEADDR : ORIGIN = 0x0, LENGTH = 0x30000
   ps7_ram_1_S_AXI_BASEADDR : ORIGIN = 0xFFFF0000, LENGTH = 0xFE00
}

/* Specify the default entry point to the program */

ENTRY(_vector_table)

/* Define the sections, and where they are mapped in memory */

SECTIONS
{
.text : {
   KEEP (*(.vectors))
   *(.boot)
   *(.text)
   *(.text.*)
   *(.gnu.linkonce.t.*)
   *(.plt)
   *(.gnu_warning)
   *(.gcc_execpt_table)
   *(.glue_7)
   *(.glue_7t)
   *(.vfp11_veneer)
   *(.ARM.extab)
   *(.gnu.linkonce.armextab.*)
} > ps7_ddr_0_S_AXI_BASEADDR

.init : {
   KEEP (*(.init))
} > ps7_ddr_0_S_AXI_BASEADDR

.fini : {
   KEEP (*(.fini))
} > ps7_ddr_0_S_AXI_BASEADDR

.rodata : {
   __rodata_start = .;
   *(.rodata)
   *(.rodata.*)
   *(.gnu.linkonce.r.*)
   __rodata_end = .;
} > ps7_ddr_0_S_AXI_BASEADDR

.rodata1 : {
   __rodata1_start = .;
   *(.rodata1)
   *(.rodata1.*)
   __rodata1_end = .;
} > ps7_ddr_0_S_AXI_BASEADDR

.sdata2 : {
   __sdata2_start = .;
   *(.sdata2)
   *(.sdata2.*)
   *(.gnu.linkonce.s2.*)
   __sdata2_end = .;
} > ps7_ddr_0_S_AXI_BASEADDR

.sbss2 : {
   __sbss2_start = .;
   *(.sbss2)
   *(.sbss2.*)
   *(.gnu.linkonce.sb2.*)
   __sbss2_end = .;
} > ps7_ddr_0_S_AXI_BASEADDR

.data : {
   __data_start = .;
   *(.data)
   *(.data.*)
   *(.gnu.linkonce.d.*)
   *(.jcr)
   *(.got)
   *(.got.plt)
   __data_end = .;
} > ps7_ddr_0_S_AXI_BASEADDR

.data1 : {
   __data1_start = .;
   *(.data1)
   *(.data1.*)
   __data1_end = .;
} > ps7_ddr_0_S_AXI_BASEADDR

.got : {
   *(.got)
} > ps7_ddr_0_S_AXI_BASEADDR

.ctors : {
   __CTOR_LIST__ = .;
   ___CTORS_LIST___ = .;
   KEEP (*crtbegin.o(.ctors))
   KEEP (*(EXCLUDE_FILE(*crtend.o) .ctors))
   KEEP (*(SORT(.ctors.*)))
   KEEP (*(.ctors))
   __CTOR_END__ = .;
   ___CTORS_END___ = .;
} > ps7_ddr_0_S_AXI_BASEADDR

.dtors : {
   __DTOR_LIST__ = .;
   ___DTORS_LIST___ = .;
   KEEP (*crtbegin.o(.dtors))
   KEEP (*(EXCLUDE_FILE(*crtend.o) .dtors))
   KEEP (*(SORT(.dtors.*)))
   KEEP (*(.dtors))
   __DTOR_END__ = .;
   ___DTORS_END___ = .;
} > ps7_ddr_0_S_AXI_BASEADDR

.fixup : {
   __fixup_start = .;
   *(.fixup)
   __fixup_end = .;
} > ps7_ddr_0_S_AXI_BASEADDR

.eh_frame : {
   *(.eh_frame)
} > ps7_ddr_0_S_AXI_BASEADDR

.eh_framehdr : {
   __eh_framehdr_start = .;
   *(.eh_framehdr)
   __eh_framehdr_end = .;
} > ps7_ddr_0_S_AXI_BASEADDR

.gcc_except_table : {
   *(.gcc_except_table)
} > ps7_ddr_0_S_AXI_BASEADDR

.mmu_tbl (ALIGN(16384)) : {
   __mmu_tbl_start = .;
   *(.mmu_tbl)
   __mmu_tbl_end = .;
} > ps7_ddr_0_S_AXI_BASEADDR

.ARM.exidx : {
   __exidx_start = .;
   *(.ARM.exidx*)
   *(.gnu.linkonce.armexidix.*.*)
   __exidx_end = .;
} > ps7_ddr_0_S_AXI_BASEADDR

.preinit_array : {
   __preinit_array_start = .;
   KEEP (*(SORT(.preinit_array.*)))
   KEEP (*(.preinit_array))
   __preinit_array_end = .;
} > ps7_ddr_0_S_AXI_BASEADDR

.init_array : {
   __init_array_start = .;
   KEEP (*(SORT(.init_array.*)))
   KEEP (*(.init_array))
   __init_array_end = .;
} > ps7_ddr_0_S_AXI_BASEADDR

.fini_array : {
   __fini_array_start = .;
   KEEP (*(SORT(.fini_array.*)))
   KEEP (*(.fini_array))
   __fini_array_end = .;
} > ps7_ddr_0_S_AXI_BASEADDR

.ARM.attributes : {
   __ARM.attributes_start = .;
   *(.ARM.attributes)
   __ARM.attributes_end = .;
} > ps7_ddr_0_S_AXI_BASEADDR

.sdata : {
   __sdata_start = .;
   *(.sdata)
   *(.sdata.*)
   *(.gnu.linkonce.s.*)
   __sdata_end = .;
} > ps7_ddr_0_S_AXI_BASEADDR

.sbss (NOLOAD) : {
   __sbss_start = .;
   *(.sbss)
   *(.sbss.*)
   *(.gnu.linkonce.sb.*)
   __sbss_end = .;
} > ps7_ddr_0_S_AXI_BASEADDR

.tdata : {
   __tdata_start = .;
   *(.tdata)
   *(.tdata.*)
   *(.gnu.linkonce.td.*)
   __tdata_end = .;
} > ps7_ddr_0_S_AXI_BASEADDR

.tbss : {
   __tbss_start = .;
   *(.tbss)
   *(.tbss.*)
   *(.gnu.linkonce.tb.*)
   __tbss_end = .;
} > ps7_ddr_0_S_AXI_BASEADDR

.bss (NOLOAD) : {
   __bss_start = .;
   *(.bss)
   *(.bss.*)
   *(.gnu.linkonce.b.*)
   *(COMMON)
   __bss_end = .;
} > ps7_ddr_0_S_AXI_BASEADDR

_SDA_BASE_ = __sdata_start + ((__sbss_end - __sdata_start) / 2 );

_SDA2_BASE_ = __sdata2_start + ((__sbss2_end - __sdata2_start) / 2 );

/* Generate Stack and Heap definitions */

.heap (NOLOAD) : {
   . = ALIGN(16);
   _heap = .;
   HeapBase = .;
   _heap_start = .;
   . += _HEAP_SIZE;
   _heap_end = .;
   HeapLimit = .;
} > ps7_ddr_0_S_AXI_BASEADDR

.stack (NOLOAD) : {
   . = ALIGN(16);
   _stack_end = .;
   . += _STACK_SIZE;
   . = ALIGN(16);
   _stack = .;
   __stack = _stack;
   . = ALIGN(16);
   _irq_stack_end = .;
   . += _IRQ_STACK_SIZE;
   . = ALIGN(16);
   __irq_stack = .;
   _supervisor_stack_end = .;
   . += _SUPERVISOR_STACK_SIZE;
   . = ALIGN(16);
   __supervisor_stack = .;
   _abort_stack_end = .;
   . += _ABORT_STACK_SIZE;
   . = ALIGN(16);
   __abort_stack = .;
   _fiq_stack_end = .;
   . += _FIQ_STACK_SIZE;
   . = ALIGN(16);
   __fiq_stack = .;
   _undef_stack_end = .;
   . += _UNDEF_STACK_SIZE;
   . = ALIGN(16);
   __undef_stack = .;
} > ps7_ddr_0_S_AXI_BASEADDR

_end = .;
}