vPortStartFirstTask & prvTaskExitError

Hello!

I’m trying to port FreeRTOS on microAptiv
But can’t execute any task
After restore context of first task I’m going to portASSERT_IF_IN_ISR(with ra = vTaskEnterCritical )
And when Im going to vPortStartFirstTask ra becames equal to prvTaskExitError

I don’t know is it normal and I don’t understand is there any connection between that and portASSERT_IF_IN_ISR(as result)
Also I don’t understand is it normal that in my case, with my linker script, complier puts uxSavedTaskStackPointer and pxTopOfStack in bss section

So please help to understand this moments

Thanks in advance

@addi:
Can you specify which port you are using?

Are you building on a particular demo?

Can you share the code contained in the task you are attempting to run?

Thank you for your replay!
I’m using port for PIC32MZ, partly
My main is from demo:

/* Kernel includes. */

#include "common.h"
#include "runtime/mips_lib.h"
#include "periphery/eic.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"

unsigned char indi;


unsigned char indi;
/* The rate at which data is sent to the queue, specified in milliseconds. */
#define mainQUEUE_SEND_FREQUENCY_MS			( 10 )


/* The number of items the queue can hold.  This is 1 as the receive task
will remove items as they are added, meaning the send task should always find
the queue empty. */
#define mainQUEUE_LENGTH					( 16 )


/* Priorities at which the tasks are created. */
#define mainQUEUE_SEND_TASK_PRIORITY	( tskIDLE_PRIORITY + 1 )
#define mainQUEUE_RECEIVE_TASK_PRIORITY	( tskIDLE_PRIORITY + 2 )

//-----------------------------------------------------------

 // The tasks as described in the accompanying PDF application note.
 
 
static void CheckTask( void *pvParameters );
static void FillTask( void *pvParameters );

///-----------------------------------------------------------

// The queue used by both tasks. 
static xQueueHandle xQueue = NULL;

// One array position is used for each task created by this demo.  The
//variables in this array are set and cleared by the trace macros within
//FreeRTOS, and displayed on the logic analyzer window within the Keil IDE -
//the result of which being that the logic analyzer shows which task is
//running when. 
unsigned long ulTaskNumber[ 3 ];

uint8_t data[16];

//-----------------------------------------------------------
int main()
{
	BaseType_t xReturned1;
	BaseType_t xReturned2;

	SystemInit();


	// stop dc Cause Register
	__asm__ __volatile ("nop");
	__asm__ __volatile ("mfc0     $26,  $13,  0"  );
	__asm__ __volatile ("lui      $27,  0x0800");
	__asm__ __volatile ("or       $26,  $26,  $27");
	__asm__ __volatile ("mtc0     $26,  $13,   0");
	__asm__ __volatile ("nop");

	indi = 0xAA			;
	SetLeds(indi) 			; // indi(GPIO)

	xQueue = xQueueCreate( mainQUEUE_LENGTH, sizeof( unsigned long ) );
	if( xQueue != NULL )
	{
		xReturned1 = xTaskCreate( CheckTask, ( signed char * ) "CheckTask",	configMINIMAL_STACK_SIZE, NULL,  ( tskIDLE_PRIORITY + 2 ), NULL ); // tskIDLE_PRIORITY + 2 , NULL );
		xReturned2 =  xTaskCreate( FillTask, ( signed char * ) "FillTask", configMINIMAL_STACK_SIZE, NULL,  ( tskIDLE_PRIORITY + 2 ), NULL ); //tskIDLE_PRIORITY + 1 , NULL );
		if(( xReturned1 == pdPASS ) && (xReturned2 == pdPASS))
		{
			indi = 0xFF			;
			SetLeds(indi) 			; // indi(GPIO)
		}
		vTaskStartScheduler();
	}

	///OutString("WARNING! This point in main must not be reached!\n");
	// If all is well we will never reach here as the scheduler will now be
	//running.  If we do reach here then it is likely that there was insufficient
	//heap available for the idle task to be created. 
	for( ;; );
}

/*-----------------------------------------------------------*/
///

 void vApplicationTickHook( void )
 {
     for( ;; ){
         __asm__ ("nop");
     }
 }

/*-----------------------------------------------------------*/

static void FillTask( void *pvParameters )
{
	portTickType xNextWakeTime;
	const unsigned long ulValueToSend = 8;
    for(int i = 0; i < 16; i++)
    data[i] = i + 5;

	// Initialise xNextWakeTime - this only needs to be done once. 
	
	for(;;)
	{

	// Send to the queue - causing the queue receive task to unblock and
	//print out a message.  0 is used as the block time so the sending
	//operation will not block - it shouldn't need to block as the queue
	//should always be empty at this point in the code. 
	if( xQueueSend( xQueue, &ulValueToSend, 0 ) != pdPASS )
	{
 		indi = 0xF0			;
    	SetLeds(indi) 			; // indi(GPIO)
	}
}
	
}
/*-----------------------------------------------------------*/

static void CheckTask( void *pvParameters )
{
unsigned long ulReceivedValue;

	// Wait until something arrives in the queue - this task will block
	//indefinitely provided INCLUDE_vTaskSuspend is set to 1 in
	//FreeRTOSConfig.h. 
    for(;;){
	//To get here something must have been received from the queue, but
	//is it the expected value?  If it is, print out a pass message, if no,
	//print out a fail message. 
	xQueueReceive( xQueue, &ulReceivedValue, portMAX_DELAY );
    	if( ulReceivedValue == 8 )
    	{
			for(int i = 0;i < 16;i++)
                if(data[i] != i + 5){
                	indi = 0x0F			;
                    SetLeds(indi) 			; // indi(GPIO)
                }
        }
    }
}


////

void vApplicationStackOverflowHook( TaskHandle_t pxTask, char *pcTaskName )
{
	( void ) pcTaskName;
	( void ) pxTask;

	/* Run time task stack overflow checking is performed if
	configCHECK_FOR_STACK_OVERFLOW is defined to 1 or 2.  This hook	function is
	called if a task stack overflow is detected.  Note the system/interrupt
	stack is not checked. */
	taskDISABLE_INTERRUPTS();
	for( ;; );
}
////
/*-----------------------------------------------------------*/
///
void vApplicationMallocFailedHook( void )
{
	taskDISABLE_INTERRUPTS();
	for( ;; );
}
void vAssertCalled( const char *pcFile, unsigned long ulLine )
{
	volatile char *pcFileName;
	volatile unsigned long ulLineNumber;

	/* Prevent things that are useful to view in the debugger from being
	optimised away. */
	pcFileName = ( char * ) pcFile;
	( void ) pcFileName;
	ulLineNumber = ulLine;

	/* Set ulLineNumber to 0 in the debugger to break out of this loop and
	return to the line that triggered the assert. */
	while( ulLineNumber != 0 )
	{
		__asm volatile( "NOP" );
		__asm volatile( "NOP" );
		__asm volatile( "NOP" );
		__asm volatile( "NOP" );
		__asm volatile( "NOP" );
	}

}
///

initialization of stack:

StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
{

	
	/* Ensure 8 byte alignment is maintained when leaving this function. */
	pxTopOfStack--;
	pxTopOfStack--;

	*pxTopOfStack = (StackType_t) 0xDEADBEEF;
	pxTopOfStack--;

	*pxTopOfStack = (StackType_t) 0x12345678;	// Word to which the stack pointer will be left pointing after context restore.
	pxTopOfStack--;

	*pxTopOfStack = (StackType_t)  mips_getcr(); //_CP0_GET_CAUSE();
	pxTopOfStack--;

	*pxTopOfStack = (StackType_t) portINITIAL_SR;// CP0_STATUS
	pxTopOfStack--;

	*pxTopOfStack = (StackType_t) pxCode; 		 //CP0_EPC
	pxTopOfStack--;

	*pxTopOfStack = (StackType_t) portTASK_RETURN_ADDRESS;	//ra
	pxTopOfStack -= 15;

	*pxTopOfStack = (StackType_t) pvParameters; // Parameters to pass in.
	pxTopOfStack -= 15;

	*pxTopOfStack = (StackType_t) pdFALSE; //by default disable FPU context save on parts with FPU

	return pxTopOfStack;


}

Start of scheduler:


BaseType_t xPortStartScheduler( void )
{
extern void vPortStartFirstTask( void );
extern void *pxCurrentTCB;

	#if ( configCHECK_FOR_STACK_OVERFLOW > 2 )
	{
		/* Fill the ISR stack to make it easy to asses how much is being used. */
		memset( ( void * ) xISRStack, portISR_STACK_FILL_BYTE, sizeof( xISRStack ) );
	}
	#endif /* configCHECK_FOR_STACK_OVERFLOW > 2 */


	/* Kick off the highest priority task that has been created so far.
	Its stack location is loaded into uxSavedTaskStackPointer. */
	uxSavedTaskStackPointer = *( UBaseType_t * ) pxCurrentTCB;

	mips_eic0_setmask(9);
	//mips32_bissr(SR_IE);

	__asm__ __volatile ("mfc0     $26,  $12,   0");
	__asm__ __volatile ("lui      $27,  0x0000");
	__asm__ __volatile ("ori      $27,  $27, 0x0001");
	__asm__ __volatile ("or       $26,  $27");
	__asm__ __volatile ("mtc0     $26,  $12,   0");
	__asm__ __volatile ("nop");
    mips_setcount(0);

    mips_setcompare(0x3000);
    /* Start Counting */
   // mips_biscr(CR_DC);

    __asm__ __volatile ("mfc0     $26,  $13,  0");
    __asm__ __volatile ("lui      $27,  0x0800");
	__asm__ __volatile ("ori      $27,  $27, 0x0000");
	__asm__ __volatile ("nor      $27,  $0, $27");
	__asm__ __volatile ("and      $26,  $26,  $27");
	__asm__ __volatile ("mtc0     $26,  $13,   0");
	__asm__ __volatile ("nop");


	__asm__ __volatile ("ehb");



	vPortStartFirstTask();

	/* Should never get here as the tasks will now be executing!  Call the task
	exit error function to prevent compiler warnings about a static function
	not being called in the case that the application writer overrides this
	functionality by defining configTASK_RETURN_ADDRESS. */
	prvTaskExitError();

	return pdFALSE;
}

I’m using software interrupt to yeild


void vPortIncrementTick( void )
{
     if( xTaskIncrementTick() != pdFALSE )
    {
        /* Pend a context switch. */
        portYIELD();
    }
}

where

#define portYIELD()	\
{	\
	mips_eic0_seticureq(2); \
}
void mips_eic0_seticureq (const uint32_t val)
{
	*(volatile uint32_t* const)(EIC_IRQ_EN) = val;
}

For system tick(PortTick) I use coprocessor timer(in compare mode):


	.set		noat
	.ent		vPortTickInterruptHandler
	.global     vPortTickInterruptHandler

vPortTickInterruptHandler:
	di
	ehb

	#rdpgpr sp, sp
	#rdpgpr gp, gp

	mtc0	zero, C0_COUNT
	ehb
	mfc0    a0, C0_CAUSE
	li      a1, (1 << 27)
	or      a0, a0, a1
	mtc0    a0, C0_CAUSE
	ehb

	jal			vPortIncrementTick
	nop
	nop

	ei
	ehb
	eret
	nop
	nop
        nop 

	.end vPortTickInterruptHandler

First task:


.ent vPortStartFirstTask
vPortStartFirstTask:
	// Simply restore the context of the highest priority task that has been created so far.
	portRESTORE_CONTEXT
	nop
.end vPortStartFirstTask

portRESTORE_CONTEXT:


.macro		portRESTORE_CONTEXT
	la			s6, uxInterruptNesting
	lw			s6, (s6)
	addiu		s6, s6, -1
	bne			s6, zero, 1f
	nop


	la			s6, uxSavedTaskStackPointer
	lw			s5, (s6)

1:

	// Restore the context.


	// s6 is loaded as it was used as a scratch register and therefore saved as part of the interrupt context.
	lw			s7, 48(s5)
	lw			s6, 44(s5)
	lw			v0, 52(s5)
	lw			v1, 56(s5)
	lw			a0, 60(s5)
	lw			a1, 64(s5)
	lw			a2, 68(s5)
	lw			a3, 72(s5)
	lw			t0, 76(s5)
	lw			t1, 80(s5)
	lw			t2, 84(s5)
	lw			t3, 88(s5)
	lw			t4, 92(s5)
	lw			t5, 96(s5)
	lw			t6, 100(s5)
	lw			t7, 104(s5)
	lw			t8, 108(s5)
	lw			t9, 112(s5)
	lw			s8, 116(s5)
	lw			ra, 120(s5)

	// Protect access to the k registers, and others.
	di
	ehb

	// Decrement the nesting count.
	la			k0, uxInterruptNesting
	lw			k1, (k0)
	addiu		k1, k1, -1
	sw			k1, 0(k0)


		// Restore the frame when there is no hardware FP support.
		lw			k0, portSTATUS_STACK_LOCATION(s5)
		lw			k1, portEPC_STACK_LOCATION(s5)

		// Leave the stack in its original state.  First load sp from s5, then restore s5 from the stack.
		add			sp, zero, s5
		lw			s5, 40(sp)

		addiu		sp, sp,	portCONTEXT_SIZE


	mtc0		k0, C0_STATUS
	mtc0 		k1, C0_EPC
	ei
	ehb
	eret
	nop
	
	
	.endm

I suppose what scheduler is not run and can not run but I don’t understand the reason

Are you trying to adapt the PIC32MZ port to a new architecture? If that is correct, you need to take a systematic approach for debugging:

  • Create only one task in your application which only increments a variable.
  • Step through the assembly of vPortStartFirstTask and see if the instruction eret takes you to the application task.
  • Verify in the debugger that the variable that the task increments is incrementing.
  • Verify that the xTickCount is incrementing.
  • Next, create one more task which increments another variable.
  • Step through the context switch assembly and see if the the control goes to the next task.
  • Verify in the debugger that the variable that the second task increments is incrementing.

Thanks for your replay, I tried as you recomended but the result is the same
Also I checked this main demo on mips64(on which with your help I succsessfull finished the porting)

Step through the assembly of vPortStartFirstTask and see if the instruction eret takes you to the application task.

I checked and got the following:
Before going into vPortStartFirstTask() I see in ra address in several instruction before vPortStartFirstTask() But in first step into ra changes immediately to prvTaskExitError()

          vPortStartFirstTask:
a0002bc0:   lui     s6,0xa002 

ra:
	prvTaskExitError();
a0002a34:	0c000a34 	jal	a00028d0 <prvTaskExitError>

in in restore context of first task I see what:

	lw			ra, 120(s5)

stores to ra 0x0

at the end of restore context whithin first task I see in debug what k0 send to C0_EPC address of a000bd60 :

static portTASK_FUNCTION( prvTimerTask, pvParameters )

Also in debug I have noticed what the program call initialization of stack(pxPortInitialiseStack) four times, - two times before start scheduler(vTaskStartScheduler) and two times after
And wherein at first:

Name : pxCode
	Details:0xa000db9c <CheckTask>

after

Name : pxCode
	Details:0xa000dae4 <FillTask>

And after start of scheduler

Name : pxCode
	Details:0xa000976c <prvIdleTask>

and beyond

Name : pxCode
	Details:0xa000bd60 <prvTimerTask>

Wherein
pxPortInitialiseStack = 0xa00471e4
pxCurrentTCB = 0xa0047288
Consequently after Restore Context Im going to static portTASK_FUNCTION( prvTimerTask, pvParameters )
But after polling in cycles:

  • portASSERT_IF_IN_ISR();
  • vTaskPlaceOnEventListRestricted;
  • prvUnlockQueue;
  • vTaskEnterCritical;
  • vTaskExitCritical;

  • In here:

    taskEXIT_CRITICAL();
a0004fcc:	0c0028c6 	jal	a000a318 <vTaskExitCritical>
a0004fd0:	00000000 	nop
}
a0004fd4:	03c0e825 	move	sp,s8

im going to address of exception vector
and in exception I see what EPC consist of invalid address 0xff200208

Verify that the xTickCount is incrementing.

xTickCount is always 0 during all debug

Thus maybe my linker script is wrong?

OUTPUT_ARCH(mips)

__DYNAMIC  =  0;
GROUP(-lc -lgcc)

PROVIDE(	 _stack			= 0xA007FFF0  	);
_exception_handler_offset = 0x200;
_data_image_start = 0;
_data_image_end = 0;
_GEN_EXCPT_ADDR_offset      =  0x180 ;
_Interrupts_ADDR_offset      =  0x200 ;

PROVIDE(	 isr_start		= 0xA0001000 	); /*//EBASE*/
PROVIDE(	_comm_start		= 0xA0000000	);
PROVIDE(	_SRAM_ADDR_2_EX	= 0xA0000000	);
PROVIDE(	 _data_start	= 0xA0022000 	);
PROVIDE(	 _bss_start		= 0xA0042000	);
PROVIDE(	 _heap_start	= 0xA005FFF0	);

PROVIDE(_min_stack_size = 0x400 );

ENTRY(_startup)

SECTIONS
{

	. = _comm_start;
	
	.text.init :
	{
		_init_start = ALIGN(4);	
		*(.text.init)
		*(.text.init.*)
		_init_end = ALIGN(4);	
	} 
	. = _SRAM_ADDR_2_EX  + SIZEOF(.text.init);
	.text.me :
	{
		_me_start = ALIGN(4);	
		*(.text.me)
		*(.text.me.*)
		_me_end = ALIGN(4);
	} 
	. =  isr_start +  _GEN_EXCPT_ADDR_offset ;
	
	.text.gen_excp_handler :
	{
		_gen_excp_handler_start = ALIGN(4);
		*(.text.gen_excp_handler)
		*(.text.gen_excp_handler.*)
		/*KEEP(*(.SW_VEC1_Vector))
		KEEP(*(.CP0_TIM_VEC4_Handler))*/
		_gen_excp_handler_end = ALIGN(4);
	} 
   .text.simple_tlb_refill_vector :
	{
		_simple_tlb_refill_start = ALIGN(4);	
		*(.text.simple_tlb_refill_vector)
		*(.text.simple_tlb_refill_vector.*)
		_simple_tlb_refill_end = ALIGN(4);
	} 
	. = isr_start + _Interrupts_ADDR_offset; 
	.text.gen_handler :
	{
		_gen_handler_start = ALIGN(4);
		*(.text.gen_handler)
		*(.text.gen_handler.*)
		KEEP(*(.SW_VEC1_Vector))
		KEEP(*(.CP0_TIM_VEC4_Handler))
		_gen_handler_end = ALIGN(4);
	} 
	.text.SW_VEC1_Vector :
	{
		_SW_VEC1_Vector_start = ALIGN(4);
		*(.text.SW_VEC1_Vector)
		*(.text.SW_VEC1_Vector.*)
		_SW_VEC1_Vector_end = ALIGN(4);
	} 
   .text.CP0_TIM_VEC4_Vector :
	{
		_CP0_TIM_VEC4_Vector_start = ALIGN(4);
		*(.text.CP0_TIM_VEC4_Vector)
		*(.text.CP0_TIM_VEC4_Vector.*)
		_CP0_TIM_VEC4_Vector_end = ALIGN(4);
	} 
	.text : 
  	{	
		*(.text.init) *(.text .text.*)
		KEEP(*(.init))
		*(.text.me ) *(.text .text.*)
		KEEP(*(.me))

		*(.text.simple_tlb_refill_vector) *(.text .text.*)
		KEEP(*(.simple_tlb_refill_vector))

		*(.text.cache_err_vector) *(.text .text.*)
		KEEP(*(.cache_err_vector))
		
		*(.text.gen_handler) *(.text .text.*)
		KEEP(*(.gen_handler))
		
		*(.text.me) *(.text .text.*)
		KEEP(*(.me))
		
		*(.text)
   		*(.text.*)
			
		*(.gnu.linkonce.t.*)
		*(.mips16.fn.*)
		*(.mips16.call.*)
    	
  	}

	_etext = .;
	_text_end = ALIGN(4);
	. = _data_start ;
   	.data :
  	{
		_data_start = ALIGN(4);
    	*(.data)
    	*(.data.*)
		_data_end = ALIGN(4);	
  	} 
  _data_image_start = 0;
  _data_image_end = 0;
	 _gp = ALIGN(4) + 0x7f;
	.sdata ALIGN(4) :
  	{
    	_sdata_begin = . ;
    	. = ALIGN(4) ;
		*(.sdata)
    	*(.sdata.*)
    	_sdata_end = . ;
  	} 
	. = ALIGN(4);
  	PROVIDE (edata  =  .);
  		_edata  =  .;
  		_fbss = .;
   		_bss_begin = . ;
  	.sbss ALIGN(4) :
 	{
    	_sbss_begin = . ;
    	*(.dynsbss)
    	*(.sbss .sbss.* .gnu.linkonce.sb.*)
    	*(.scommon)
    	_sbss_end = . ;
    	. = ALIGN(4) ;
  	}  	
  	. = _bss_start; 
	.bss : 
	{
		_bss_start = ALIGN(4);
		*(.bss)	
		*(.bss.*)
		_bss_end = ALIGN(4) ;
	} 
	. = _heap_start; 
	.heap :
  	{

		_heap_start = ALIGN(4);
   		*(.heap)
   		*(.heap.*)
		_heap_end = ALIGN(4);
  	} 

 	_stack_bottom = .;
	 . += _min_stack_size;
	 _stack_top = .;
	 __stack = _stack;
	  .gcc_compiled_long32     0 : { KEEP(*(.gcc_compiled_long32)) }
  .gcc_compiled_long64     0 : { KEEP(*(.gcc_compiled_long64)) }
  

}

Please help!

Everything seems okay till this point.

Is this call stack at the point when the assert fires? If yes, is this the complete call stack?

Seems like memory corruption. You can try to increase the stack size of all tasks.

This means that your tick interrupt is not firing. You need to make it work - I am not familiar with this platform and therefore, cannot say why it is not working.

Thank you for your replay!

Not exactly, I gave a few functions in which I get in cycle whithin stepping debuging in eclipse
And cause of what cycle was very long I could’n wait so long so after run in debug I stopped in expception vector

Seems like memory corruption. You can try to increase the stack size of all tasks.

Is it really actual for my simple tasks?

Seems like memory corruption. You can try to increase the stack size of all tasks.

I have got :

#define configMINIMAL_STACK_SIZE				( 1024 )
#define configISR_STACK_SIZE					( 4096 )

maybe it is enogth for my tasks?
And could my linker script be the reason of that corruption??

This means that your tick interrupt is not firing. You need to make it work - I am not familiar with this platform and therefore, cannot say why it is not working.

Thank you, I will try to check it at first then