ERROR: Calls ECALL on function prvProcessTimerOrBlockTask

Hi, I’m trying to run FreeRTOS on my 32-bit RISC-V core. The RISC-V core has ZiCSR extension and CLINT. I have succesfully compiled FreeRTOS and I’m now running it on the core. xTaskCreate works properly but when vTaskStartScheduler is added on the code, it suddenly ends under the function prvProcessTimerOrBlockTask. Below is the object dump and highlighted in read is where it stops running (specifically at ECALL):

Here are my observations:

  • Right after entering and exiting xTaskResumeAll (address 0x3aa4), it then copies the value of baseregister x10 to x15 (address 0x3aa8). And if x15 is zero it will go straight to ECALL (address 0x3aac)
  • When I checked the object dump of function xTaskResumeAll, x10 is somehow the return value of that function (which is then passed to prvProcessTimerOrBlockTask when it goes back). That means xTaskResumeAll returns zero which caused prvProcessTimerOrBlockTask to go straight to ECALL
  • This is the first and last occurence that this happened on the whole run

I think if xTaskResumeAll did not returned zero, this will not happen so I would like to ask under what occurence that xTaskResumeAll returns zero and why is that it must never be zero? Or is there other problem?

Here is the code I’m trying to run:

/* Standard includes. */
#include <stdint.h>
#include "rv32i.h"

/* Kernel includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"


void vApplicationTickHook( void );
void vUartSend_1( void *pvParameters );
void vUartSend_2( void *pvParameters );



extern void freertos_risc_v_trap_handler( void );


void main( void )
{
    
    uart_print("FreeRTOS DEMO\n");
    csr_write(MTVEC, (uint32_t)&freertos_risc_v_trap_handler);

	xTaskCreate( vUartSend_1,				           
				"UART_SEND", 						
				configMINIMAL_STACK_SIZE * 2U, 		
				NULL, 								
				1, 	                              
				NULL );								
				
    xTaskCreate( vUartSend_2,				          
			"UART_SEND", 						
			configMINIMAL_STACK_SIZE * 2U, 		
			NULL, 								
			2, 	                            
			NULL );	


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

	uart_print("ERROR: You reached past the vTaskStartScheduler()");

}

void vUartSend_1( void *pvParameters ){
    while(1){
        uart_print("This is the first line\n");
        vTaskDelay(100);
        uart_print("This is the second line\n");
        vTaskDelay(100);
        
    }
    
}

void vUartSend_2( void *pvParameters ){
    while(1){
        uart_print("This is the THIRD line\n");
        vTaskDelay(50);
        uart_print("This is the FOURTH line\n");
        vTaskDelay(50);

    }
}



void SystemIrqHandler( uint32_t mcause )
{
  uart_print("freeRTOS: Unknown interrupt \n");
}

void vApplicationTickHook( void ){
}

void vApplicationMallocFailedHook( void )
{
	taskDISABLE_INTERRUPTS();
    uart_print("FreeRTOS_FAULT: vApplicationMallocFailedHook (solution: increase 'configTOTAL_HEAP_SIZE' in FreeRTOSConfig.h)\n");
    __asm volatile( "nop" );
	__asm volatile( "ebreak" );
	for( ;; );
}

void vApplicationIdleHook( void )
{
	uart_print("FreeRTOS_FAULT: vApplicationStackOverflowHook\n");
}


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

	taskDISABLE_INTERRUPTS();
    uart_print("FreeRTOS_FAULT: vApplicationStackOverflowHook\n");
    __asm volatile( "nop" );
    __asm volatile( "nop" );
	__asm volatile( "ebreak" );
	for( ;; );
}

And here is the FreeRTOSConfig.h:

#define configMTIME_BASE_ADDRESS 	( 0x80000000UL  )
#define configMTIMECMP_BASE_ADDRESS ( 0x80000008UL  )

#define configISR_STACK_SIZE_WORDS ( 128 )

#define configUSE_PREEMPTION			1
#define configUSE_IDLE_HOOK				1
#define configUSE_TICK_HOOK				1
#define configCPU_CLOCK_HZ				100000000 
#define configTICK_RATE_HZ				( ( TickType_t ) 1000 )
#define configMAX_PRIORITIES			( 5 ) 
#define configMINIMAL_STACK_SIZE		( ( unsigned short ) 128 ) 
#define configSUPPORT_DYNAMIC_ALLOCATION  1
#define configTOTAL_HEAP_SIZE			( ( size_t ) ( 8* 1024 ) )
#define configMAX_TASK_NAME_LEN			( 16 ) 
#define configUSE_TRACE_FACILITY		1
#define configUSE_16_BIT_TICKS			0 
#define configIDLE_SHOULD_YIELD			0 
#define configUSE_MUTEXES				1
#define configQUEUE_REGISTRY_SIZE		8
#define configCHECK_FOR_STACK_OVERFLOW	2
#define configUSE_RECURSIVE_MUTEXES		1
#define configUSE_MALLOC_FAILED_HOOK	1
#define configUSE_APPLICATION_TASK_TAG	0
#define configUSE_COUNTING_SEMAPHORES	1
#define configGENERATE_RUN_TIME_STATS	0
#define configTASK_NOTIFICATION_ARRAY_ENTRIES 4
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1

/* Co-routine definitions. */
#define configUSE_CO_ROUTINES 			0
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )

/* Software timer definitions. */
#define configUSE_TIMERS				1
#define configTIMER_TASK_PRIORITY		( configMAX_PRIORITIES - 1 )
#define configTIMER_QUEUE_LENGTH		4
#define configTIMER_TASK_STACK_DEPTH	( configMINIMAL_STACK_SIZE )

/* Set the following definitions to 1 to include the API function, or zero
to exclude the API function. */
#define INCLUDE_vTaskPrioritySet			1
#define INCLUDE_uxTaskPriorityGet			1
#define INCLUDE_vTaskDelete					1
#define INCLUDE_vTaskCleanUpResources		1
#define INCLUDE_vTaskSuspend				1
#define INCLUDE_vTaskDelayUntil				1
#define INCLUDE_vTaskDelay					1
#define INCLUDE_eTaskGetState				1
#define INCLUDE_xTimerPendFunctionCall		1
#define INCLUDE_xTaskAbortDelay				1
#define INCLUDE_xTaskGetHandle				1
#define INCLUDE_xSemaphoreGetMutexHolder	1


#define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); __asm volatile( "ebreak" ); for( ;; ); }

#endif /* FREERTOS_CONFIG_H */

I would really appreciate all advice, answers, or hints you might provide.

How do you know your calls to xTaskCreate() succeeded? What kind of memory management do you use?

As a side note, you should not use serial output in an isr.

In addition

is printing a wrong error message and shouldn’t print anything anyway.

When I removed the xTaskStartScheduler, it reaches the end of the main statement properly so I think xTaskCreateworks properly. The memory management used here is heap_4.c

Here is the description of xTaskResumeAll return value - https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/include/task.h#L1384

ecall is used by the port to request a context switch - why does the execution stop in your case at ecall? Can you verify that freertos_risc_v_trap_handler is correctly installed. One quick way to do that would be to put a breakpoint and see if it is getting invoked.

Hi, so ECALL was not meant to exit the program unceremoniously but was actually part of the code? Actually, It was me who set the testbench to end the execution at ECALL because I really thought that ECALL was meant to end the execution (like an environment call when there is a problem on the code and it needs to end it abruptly). If ECALL was not meant to stop the execution code, then was it EBREAK (not ECALL) that sets the program to stop?

ps. I’m running this on a 32-bit Integer-Zicsr RISC-V core.

Yes, that is right. And you will see that ebreak is not used in the kernel code but only in the demo to stop the code in case of error.

THIS IS VERY HELPFUL! So it was a big misunderstanding. I will try to run this again when I got home, I will update you later (and close this thread if this fixes the issue). Thanks!

This is a wrong assumption. Please read the docs carefully. You must check the return value of every FreeRTOS API function explicitly. Also, if your system is set up correctly, the call to vTaskStartScheduler() will never return, even if no tasks have been created, so the code jumping past it is a clear indication of a configuration problem.

Setting the stop point at EBREAK (instead of ECALL and EBREAK) solves the issue. Thanks!