Multiple tasks not responding when mixed with a semaphore

Hello,

I have 4 tasks with different delays working as expected. Then, I add a 5th task to handle interrupts synchronized with two binary semaphores to send and receive data via DMA.
Individually the 4 tasks work as one after the other depending on their delays when the interrupt task is not initialized. On the other hand, the task with the semaphores works fine when the first 4 tasks are initialized.
The problem comes when I want to have all five tasks together. Each of them execute once and then loops around portTASK_FUNCTION from tasks.c. More precisely, inside an infinite for loop, in if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > ( UBaseType_t ) 1 ) which if it is true, it calls taskYIELD()which is never the case. That way, all 5 tasks are only executed once and then the OS stays in that infinite for loop.

The simple tasks (without interrupt or semaphore) all look like this but with different values in xDelay:

static void task1(void *p)
{
	const TickType_t xDelay = 250 / portTICK_PERIOD_MS;
	while(1){
		printf("Task 1\n");
		vTaskDelay(xDelay);
	}
}

And the task with the interrupts is:

xSemaphoreHandle SemPLtoPS = NULL;
xSemaphoreHandle SemPStoPL = NULL;
#define SemTime				0xffff
static void InterruptSemaphoreTask(void *p)
{
	const TickType_t xDelay = 250 / portTICK_PERIOD_MS;

	int Status;
	XAxiDma_Config *Config;
	int Index;
	u8 *TxBufferPtr;
	u8 *RxBufferPtr;
	u8 Value;

	TxBufferPtr = (u8 *)TX_BUFFER_BASE ;
	RxBufferPtr = (u8 *)RX_BUFFER_BASE;

	Config = XAxiDma_LookupConfig(DMA_DEV_ID);
	if (!Config) {
		xil_printf("No config found for %d\r\n", DMA_DEV_ID);

		return XST_FAILURE;
	}

	/* Initialize DMA engine */
	Status = XAxiDma_CfgInitialize(&AxiDma, Config);

	if (Status != XST_SUCCESS) {
		xil_printf("Initialization failed %d\r\n", Status);
		return XST_FAILURE;
	}

	if(XAxiDma_HasSg(&AxiDma)){
		xil_printf("Device configured as SG mode \r\n");
		return XST_FAILURE;
	}

	/* Set up Interrupt system  */
	Status = SetupIntrSystem(&Intc, &AxiDma, TX_INTR_ID, RX_INTR_ID);
	if (Status != XST_SUCCESS) {

		xil_printf("Failed intr setup\r\n");
		return XST_FAILURE;
	}

	/* Disable all interrupts before setup */

	XAxiDma_IntrDisable(&AxiDma, XAXIDMA_IRQ_ALL_MASK,
						XAXIDMA_DMA_TO_DEVICE);

	XAxiDma_IntrDisable(&AxiDma, XAXIDMA_IRQ_ALL_MASK,
				XAXIDMA_DEVICE_TO_DMA);

	/* Enable all interrupts */
	XAxiDma_IntrEnable(&AxiDma, XAXIDMA_IRQ_ALL_MASK,
							XAXIDMA_DMA_TO_DEVICE);


	XAxiDma_IntrEnable(&AxiDma, XAXIDMA_IRQ_ALL_MASK,
							XAXIDMA_DEVICE_TO_DMA);

	/* Initialize flags before start transfer test  */
	TxDone = 0;
	RxDone = 0;
	Error = 0;

	unsigned char stream[68]={0x03,0x3f,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0xac,0x8f,0xa6,0x5e,0x3e,0xf6,0xa0,0x07,0x0a,0x00,0x00,0x00,0x74,0x65,0x73,0x74,0x5f,0x69,0x6d,0x61,0x67,0x65,0x02,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x72,0x67,0x62,0x38,0x01,0x06,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x11,0x12,0x13,0x21,0x22,0x23,0x31,0x32,0x33,0x41,0x42,0x43};
	for(int i=0; i<MAX_PKT_LEN; i++)
		TxBufferPtr[i] = stream[i];

	/* Flush the SrcBuffer before the DMA transfer, in case the Data Cache
	 * is enabled
	 */
	Xil_DCacheFlushRange((UINTPTR)TxBufferPtr, MAX_PKT_LEN);
	Xil_DCacheFlushRange((UINTPTR)RxBufferPtr, MAX_PKT_LEN);

	// Create semaphores
	SemPLtoPS = xSemaphoreCreateBinary();
	if(SemPLtoPS==NULL){
		xil_printf("Could not create semaphore SemPLtoPS\n");
		return XST_FAILURE;
	}
	SemPStoPL = xSemaphoreCreateBinary();
	if(SemPStoPL==NULL){
		xil_printf("Could not create semaphore SemPStoPL\n");
		return XST_FAILURE;
	}



	while(1){
		Status = XAxiDma_SimpleTransfer(&AxiDma,(UINTPTR) RxBufferPtr,
					MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA);

		if (Status != XST_SUCCESS) {
			return XST_FAILURE;
		}

		Status = XAxiDma_SimpleTransfer(&AxiDma,(UINTPTR) TxBufferPtr,
					MAX_PKT_LEN, XAXIDMA_DMA_TO_DEVICE);

		if (Status != XST_SUCCESS) {
			return XST_FAILURE;
		}


		if((xSemaphoreTake(SemPLtoPS, SemTime)== pdTRUE) && (xSemaphoreTake(SemPStoPL, SemTime)== pdTRUE)){
			if (Error) {
				xil_printf("Failed test transmit%s done, "
				"receive%s done\r\n", TxDone? "":" not",
								RxDone? "":" not");
			}

			for(int i=0; i<MAX_PKT_LEN; i++)
			{
				printf("0x%.2x ", RxBufferPtr[i]);
			}
			printf("\n");
		}
		vTaskDelay(xDelay);
	}
}

I have 2 callbacks as I am sending and receiving data just looping around, in which I call xSemaphoreGiveFromISR(SemPStoPL, NULL); and xSemaphoreGiveFromISR(SemPLtoPS, NULL); when data is properly sent/received.

static void TxIntrHandler(void *Callback)
{

	u32 IrqStatus;
	int TimeOut;
	XAxiDma *AxiDmaInst = (XAxiDma *)Callback;
	static BaseType_t xHigherPriorityTaskWoken;

	xHigherPriorityTaskWoken = pdFALSE;

	/* Read pending interrupts */
	IrqStatus = XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DMA_TO_DEVICE);

	/* Acknowledge pending interrupts */


	XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DMA_TO_DEVICE);

	/*
	 * If no interrupt is asserted, we do not do anything
	 */
	if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK)) {

		return;
	}

	/*
	 * If error interrupt is asserted, raise error flag, reset the
	 * hardware to recover from the error, and return with no further
	 * processing.
	 */
	if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK)) {

		Error = 1;

		/*
		 * Reset should never fail for transmit channel
		 */
		XAxiDma_Reset(AxiDmaInst);

		TimeOut = RESET_TIMEOUT_COUNTER;

		while (TimeOut) {
			if (XAxiDma_ResetIsDone(AxiDmaInst)) {
				break;
			}

			TimeOut -= 1;
		}

		return;
	}

	/*
	 * If Completion interrupt is asserted, then set the TxDone flag
	 */
	if ((IrqStatus & XAXIDMA_IRQ_IOC_MASK)) {

		TxDone = 1;
		xSemaphoreGiveFromISR(SemPStoPL, &xHigherPriorityTaskWoken);
	}
	portYIELD_FROM_ISR( xHigherPriorityTaskWoken);
}

static void RxIntrHandler(void *Callback)
{
	u32 IrqStatus;
	int TimeOut;
	XAxiDma *AxiDmaInst = (XAxiDma *)Callback;
	//static signed BaseType_t xHigherPriorityTaskWoken;
	static BaseType_t xHigherPriorityTaskWoken;

	xHigherPriorityTaskWoken = pdFALSE;

	/* Read pending interrupts */
	IrqStatus = XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DEVICE_TO_DMA);

	/* Acknowledge pending interrupts */
	XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DEVICE_TO_DMA);

	/*
	 * If no interrupt is asserted, we do not do anything
	 */
	if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK)) {
		return;
	}

	/*
	 * If error interrupt is asserted, raise error flag, reset the
	 * hardware to recover from the error, and return with no further
	 * processing.
	 */
	if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK)) {

		Error = 1;

		/* Reset could fail and hang
		 * NEED a way to handle this or do not call it??
		 */
		XAxiDma_Reset(AxiDmaInst);

		TimeOut = RESET_TIMEOUT_COUNTER;

		while (TimeOut) {
			if(XAxiDma_ResetIsDone(AxiDmaInst)) {
				break;
			}

			TimeOut -= 1;
		}

		return;
	}

	/*
	 * If completion interrupt is asserted, then set RxDone flag
	 */
	if ((IrqStatus & XAXIDMA_IRQ_IOC_MASK)) {

		RxDone = 1;
		xSemaphoreGiveFromISR(SemPLtoPS, &xHigherPriorityTaskWoken);
	}
	portYIELD_FROM_ISR( xHigherPriorityTaskWoken);
}

The example from the documentation does not have a call to vTaskDelay though. If I don’t include it, only the task with the semaphores executes. That is why I added it to have a context switching to the other tasks.

What am I doing wrong that I cannot get all 4 simple tasks to execute while the 5th one waits for to get the semaphore via the interrupts?

Thanks for the help.

Which function are they looping in? portTASK_FUNCTION is a macro used to qualify a function, and in your case is probably #defined to nothing anyway. Which function is that macro on? Please link to the line here: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/master/tasks.c

[oops posted too soon]

From this:

I assume you are actually in the idle task, which is the function you will normally see running if you pause on the debugger when your application tasks spend most of their time in the Blocked state (as yours do). If this line is never yielding it just means there are no other idle priority tasks ready to run.

What is the difference between your printf() and xil_printf() implementations? Are they thread safe? Your issue could just be that you are trying to print from multiple threads.

Have you been through the items here regarding using configASSERT() and enabling stack overflow protection? https://www.freertos.org/FAQHelp.html

Also, have you read this section https://www.freertos.org/Using-FreeRTOS-on-Cortex-A-Embedded-Processors.html#interrupt-handling of the page describing how to use FreeRTOS on a Cortex-A?

Thank you @rtel for the answer.

If all the “simple tasks”'s delays are “timeouted”, shouldn’t they be ready to execute?

The difference is: “This function is similar to printf but much smaller in size (only 1KB) . It does not have support for floating point numbers. xil_printf also does not support printing of long long (i.e 64 bit numbers).” [1]

The tasks get interrupted, at least once so should it still be an issues attributed to incorrect interrupt priority assignment?. As I am using also the lwIP, I increased all the values to avoid any issues. This is my FreeRTOSConfig.h

#define configUSE_PREEMPTION 1

#define configUSE_MUTEXES 1

#define configUSE_RECURSIVE_MUTEXES 1

#define configUSE_COUNTING_SEMAPHORES 1

#define configUSE_TIMERS 1

#define configUSE_IDLE_HOOK 0

#define configUSE_TICK_HOOK 0

#define configUSE_MALLOC_FAILED_HOOK 1

#define configUSE_TRACE_FACILITY 1

#define configUSE_16_BIT_TICKS 0

#define configUSE_APPLICATION_TASK_TAG 0

#define configUSE_CO_ROUTINES 0

#define configTICK_RATE_HZ (100)

#define configMAX_PRIORITIES (8)

#define configMAX_CO_ROUTINE_PRIORITIES 2

#define configMINIMAL_STACK_SIZE ( ( unsigned short ) 200)

#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 524288 ) )

#define configMAX_TASK_NAME_LEN 10

#define configIDLE_SHOULD_YIELD 1

#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES - 1)

#define configTIMER_QUEUE_LENGTH 10

#define configTIMER_TASK_STACK_DEPTH ((configMINIMAL_STACK_SIZE) * 2)

#define configASSERT( x ) if( ( x ) == 0 ) vApplicationAssert( __FILE__, __LINE__ )

#define configUSE_QUEUE_SETS 1

#define configCHECK_FOR_STACK_OVERFLOW 2

#define configQUEUE_REGISTRY_SIZE 10

#define configUSE_STATS_FORMATTING_FUNCTIONS 1

#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 0

#define configUSE_TICKLESS_IDLE	0
#define configTASK_RETURN_ADDRESS    NULL
#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_pcTaskGetTaskName            1
#define configTIMER_ID XPAR_XTTCPS_0_DEVICE_ID

#define configTIMER_BASEADDR XPAR_XTTCPS_0_BASEADDR

#define configTIMER_INTERRUPT_ID XPAR_XTTCPS_0_INTR

#define configUNIQUE_INTERRUPT_PRIORITIES 32

#define configINTERRUPT_CONTROLLER_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID

#define configINTERRUPT_CONTROLLER_BASE_ADDRESS XPAR_SCUGIC_0_DIST_BASEADDR

#define configINTERRUPT_CONTROLLER_CPU_INTERFACE_OFFSET 0x10000

void vApplicationAssert( const char *pcFile, uint32_t ulLine );
void FreeRTOS_SetupTickInterrupt( void );
#define configSETUP_TICK_INTERRUPT() FreeRTOS_SetupTickInterrupt()

void FreeRTOS_ClearTickInterrupt( void );
#define configCLEAR_TICK_INTERRUPT()	FreeRTOS_ClearTickInterrupt()

#define configGENERATE_RUN_TIME_STATS 0

#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()

#define portGET_RUN_TIME_COUNTER_VALUE()

#define configCOMMAND_INT_MAX_OUTPUT_SIZE 2096

#define recmuCONTROLLING_TASK_PRIORITY ( configMAX_PRIORITIES - 2 )

#define fabs( x ) __builtin_fabs( x )

#define configMAX_API_CALL_INTERRUPT_PRIORITY (18)

#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1

I haven’t checked that. I will.

Besides these links, do I need to call vTaskDelay() at the end of the while loop in the task that takes the semaphore? Do I also need in that task to call xSemaphoreGive()?

I tried this solution but that didn’t work either. However I am not sure if that would help as the interrupt occur and they triggers the callback functions.

Also following this I checked that configMAX_API_CALL_INTERRUPT_PRIORITY is defined in FreeRTOSConfig.h and it is set to 18

Lastly, to clarify, I am using a Xilinx’s Ultrascale. I first tested the interrupt example in a standalone and added it to a FreeRTOS application.

The things I would check (in no particular order):

  1. Are you sure your interrupts are a lower priority than configMAX_API_CALL_INTERRUPT_PRIORITY ? (How many interrupt priorities does your processor support?)

  2. A stack overflow in one of the tasks. Quite possibly the interrupt task, but could be any. Try increasing all the task stack sizes considerably and see if the problem goes away, then fault find it if it does.

  3. Are you sure the interrupt stack has been assigned and is large enough? (and the other system stacks).

What processor core you running on (Cortex-A53 etc?)

From here I can confirm then (if I understood correctly) that the interrupt priorities are in my case 121 and 122. configMAX_API_CALL_INTERRUPT_PRIORITY is 18 so that could be an issue, right? However, I cannot change the IDs (121 and 122) but only configMAX_API_CALL_INTERRUPT_PRIORITY. What is the maximum number it can take then? Should it be higher than 122? If so, this solution is similar to what you are saying but actually setting the priorities to be higher (+1) than configMAX_API_CALL_INTERRUPT_PRIORITY. Shouldn’t it be then -1? If I have more interrupts (2 in my case), can they have the same priority? From the HW side, the documentation states that “Interrupts of equal priority are resolved by selecting the lowest ID .” (121 and 122 in my case)

I increased all of them 5 times and the result was the same. Each task executed only once. Could it be something related to their priorities?

I am creating them (following Xilinx’s example) as follows:

sys_thread_new("interrupt Semaphore Task",
	InterruptSemaphoreTask, // Function to establish communication with master
	NULL,	// No need to pass parameters to the thread
	TCP_SERVER_THREAD_STACKSIZE*5,
	DEFAULT_THREAD_PRIO);

sys_thread_new("PS to PL",
	ps_to_pl_improved, // Function to establish communication with master
	NULL,	// No need to pass parameters to the thread
	TCP_SERVER_THREAD_STACKSIZE*5,
	DEFAULT_THREAD_PRIO);

sys_thread_new("PL to PS",
	pl_to_ps_improved, // Function to establish communication with master
	NULL,	// No need to pass parameters to the thread
	TCP_SERVER_THREAD_STACKSIZE*5,
	DEFAULT_THREAD_PRIO);

/***** Tasks handling the communication with TCPIP (FPGA<->World) *****/
sys_thread_new("FPGA to World",
	fpga_to_world_improved, // Function to establish communication with master
	NULL,	// No need to pass parameters to the thread
	TCP_SERVER_THREAD_STACKSIZE*5,
	DEFAULT_THREAD_PRIO);

sys_thread_new("World to FPGA",
	world_to_fpga_improved, // Function to establish communication with master
	NULL,	// No need to pass parameters to the thread
	TCP_SERVER_THREAD_STACKSIZE*5,
	DEFAULT_THREAD_PRIO);

Where sys_tread_new is:

/*---------------------------------------------------------------------------*
 * Routine:  sys_thread_new
 *---------------------------------------------------------------------------*
 * Description:
 *      Starts a new thread with priority "prio" that will begin its
 *      execution in the function "thread()". The "arg" argument will be
 *      passed as an argument to the thread() function. The id of the new
 *      thread is returned. Both the id and the priority are system
 *      dependent.
 * Inputs:
 *      char *name              -- Name of thread
 *      void (* thread)(void *arg) -- Pointer to function to run.
 *      void *arg               -- Argument passed into function
 *      int stacksize           -- Required stack amount in bytes
 *      int prio                -- Thread priority
 * Outputs:
 *      sys_thread_t            -- Pointer to per-thread timeouts.
 *---------------------------------------------------------------------------*/
sys_thread_t sys_thread_new( const char *pcName, void( *pxThread )( void *pvParameters ), void *pvArg, int iStackSize, int iPriority )
{
xTaskHandle xCreatedTask;
portBASE_TYPE xResult;
sys_thread_t xReturn;

	xResult = xTaskCreate( pxThread, ( const char * const) pcName, iStackSize, pvArg, iPriority, &xCreatedTask );

	if( xResult == pdPASS )
	{
		xReturn = xCreatedTask;
	}
	else
	{
		xReturn = NULL;
	}

	return xReturn;
}

As you can see, they all have the same priority (DEFAULT_THREAD_PRIO=2).

How can I check that?

Yes, it is a Cortex-A53

Thanks @RealtimeRik for the help.

The A53 uses a GIC-400. From looking at the reference manual I found:

The GIC-400 implements 32 priority levels in Secure state and 16 priority states in Non-secure state.

This means you have valid priorities of 0 (highest) to 31 (lowest). Therefore if configMAX_API_CALL_INTERRUPT_PRIORITY is set to 21, your interrupts which call FreeRTOS must have a priority in the range of 21-31. The interrupt ID is not its priority, but is only used to arbitrate in the case of identical priorities (lowest ID is higher). Try and see what priority your interrupts are set to? (maybe post the interrupt setup code)

I think you can rule out the task stacks overflowing.

Your various system stacks will be probably be setup in the startup code (SVC, IRQ, SYS etc). I don’t know your startup code or how it is configured. I would imagine that it is okay but it is worth checking the various stack pointers are sensible and don’t run into each other or stamp over some other data.

Originally the interrupts’ priorities were set to 0xA so they were lower than configMAX_API_CALL_INTERRUPT_PRIORITY=18

XScuGic_SetPriorityTriggerType(IntcInstancePtr, TxIntrId, 0xA0, 0x3);
XScuGic_SetPriorityTriggerType(IntcInstancePtr, RxIntrId, 0xA0, 0x3);

Where TxIntrId=121 and RxIntrId=122

The initialization code for the interrupts is taken from an example (working as a standalone or even when it is only the interrupt task declared)

    /*
	 * Initialize the interrupt controller driver so that it is ready to
	 * use.
	 */
	IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
	if (NULL == IntcConfig) {
		return XST_FAILURE;
	}

	Status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,
					IntcConfig->CpuBaseAddress);
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	XScuGic_SetPriorityTriggerType(IntcInstancePtr, TxIntrId, 0xA0, 0x3);
	XScuGic_SetPriorityTriggerType(IntcInstancePtr, RxIntrId, 0xA0, 0x3);
	/*
	 * Connect the device driver handler that will be called when an
	 * interrupt for the device occurs, the handler defined above performs
	 * the specific interrupt processing for the device.
	 */
	Status = XScuGic_Connect(IntcInstancePtr, TxIntrId,
				(Xil_InterruptHandler)TxIntrHandler,
				AxiDmaPtr);
	if (Status != XST_SUCCESS) {
		return Status;
	}

	Status = XScuGic_Connect(IntcInstancePtr, RxIntrId,
				(Xil_InterruptHandler)RxIntrHandler,
				AxiDmaPtr);
	if (Status != XST_SUCCESS) {
		return Status;
	}

	XScuGic_Enable(IntcInstancePtr, TxIntrId);
	XScuGic_Enable(IntcInstancePtr, RxIntrId);

	/* Enable interrupts from the hardware */

	Xil_ExceptionInit();
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
			(Xil_ExceptionHandler)INTC_HANDLER,
			(void *)IntcInstancePtr);

	Xil_ExceptionEnable();

	return XST_SUCCESS;
}

This does look okay. Your interrupt priorities are 20 and configMAX_API_CALL_INTERRUPT_PRIORITY is 18.

Looks like this is not the issue. What happens if you remove the printf’s from all the tasks?

Actually they are set to 0xA0 so they are larger than configMAX_API_CALL_INTERRUPT_PRIORITY. If I understood correctly, the priorities have to be smaller than configMAX_API_CALL_INTERRUPT_PRIORITY, right? So I could set the priorities for Tx and Rx handlers as configMAX_API_CALL_INTERRUPT_PRIORITY-1, correct?

Removing all printfs also is not the issue. Still all tasks are executed only once

Lower number is higher priority in ARM interrupts. 0xA0 is a priority of 20 as it is the top 5 bits.

So you could do ( configMAX_API_CALL_INTERRUPT_PRIORITY+1 ) << 3 which is 0x98.

You could try setting them to 0xFF. That would definitely ensure they are lower priority regardless of the number of priority bits.

What is pxCurrentTCB pointing to when its stuck?

I would try removing stuff from the interrupt handlers and see if you can find the problem.

A quick test with a timed task to give the semaphore (replacing the interrupts) and one with the semaphore works as expected. I even added the extra 4 tasks and still works. Therefore, the issue is with the interrupts.

Task creation:

		sys_thread_new("Timed Task",
			timed_task, // Function to establish communication with master
			NULL,	// No need to pass parameters to the thread
			TCP_SERVER_THREAD_STACKSIZE,
			DEFAULT_THREAD_PRIO);

		sys_thread_new("Semaphore Task",
			SemaphoreTask, // Function to establish communication with master
			NULL,	// No need to pass parameters to the thread
			TCP_SERVER_THREAD_STACKSIZE,
			DEFAULT_THREAD_PRIO);

Tasks:

xSemaphoreHandle SemTimed = NULL;
static void SemaphoreTask(void *p)
{
	// Create semaphores
	SemTimed = xSemaphoreCreateBinary();
	if(SemTimed==NULL){
		xil_printf("Could not create semaphore SemPLtoPS\n");
		return XST_FAILURE;
	}

	while(1){
		if(xSemaphoreTake(SemTimed, portMAX_DELAY)== pdTRUE){
			xil_printf("Semaphore task\n");
		}
	}
}

static void timed_task(void *p)
{
	//const TickType_t xDelay = 500 / portTICK_PERIOD_MS;
	static BaseType_t xHigherPriorityTaskWoken;
	xHigherPriorityTaskWoken = pdFALSE;

	while(1){
		printf("Timed task\n");
		//vTaskDelay(xDelay);
		if(SemTimed!=NULL){
			xSemaphoreGiveFromISR(SemTimed, &xHigherPriorityTaskWoken);
			portYIELD_FROM_ISR( xHigherPriorityTaskWoken);
		}
		vTaskDelay(pdMS_TO_TICKS(500));
	}
}

The problem comes when using the interrupt

I added a new task to the two ones from my previous post to include line by line the interrupt functions and it got stuck when I added the call for the Interrupt system:

xSemaphoreHandle SemDMATx = NULL;
xSemaphoreHandle SemDMARx = NULL;
static void DMATask(void *p)
{
	int Status;
	XAxiDma_Config *Config;
	u8 *TxBufferPtr;
	u8 *RxBufferPtr;

	// Create semaphores
	SemDMATx = xSemaphoreCreateBinary();
	if(SemDMATx==NULL){
		xil_printf("Could not create semaphore SemDMATx\n");
		return XST_FAILURE;
	}
	SemDMARx = xSemaphoreCreateBinary();
	if(SemDMARx==NULL){
		xil_printf("Could not create semaphore SemDMARx\n");
		return XST_FAILURE;
	}

	TxBufferPtr = (u8 *)TX_BUFFER_BASE;
	RxBufferPtr = (u8 *)RX_BUFFER_BASE;

	Config = XAxiDma_LookupConfig(DMA_DEV_ID);
	if (!Config) {
		xil_printf("No config found for %d\r\n", DMA_DEV_ID);

		return XST_FAILURE;
	}

	/* Initialize DMA engine */
	Status = XAxiDma_CfgInitialize(&AxiDma, Config);

	if (Status != XST_SUCCESS) {
		xil_printf("Initialization failed %d\r\n", Status);
		return XST_FAILURE;
	}

	if(XAxiDma_HasSg(&AxiDma)){
		xil_printf("Device configured as SG mode \r\n");
		return XST_FAILURE;
	}

	/* Set up Interrupt system  */
	Status = SetupIntrSystem(&Intc, &AxiDma, TX_INTR_ID, RX_INTR_ID);
	if (Status != XST_SUCCESS) {

		xil_printf("Failed intr setup\r\n");
		return XST_FAILURE;
	}

	while(1){
		vTaskDelay(pdMS_TO_TICKS(500));
	}
}

Which looks like this:

/*****************************************************************************/
/*
*
* This function setups the interrupt system so interrupts can occur for the
* DMA, it assumes INTC component exists in the hardware system.
*
* @param	IntcInstancePtr is a pointer to the instance of the INTC.
* @param	AxiDmaPtr is a pointer to the instance of the DMA engine
* @param	TxIntrId is the TX channel Interrupt ID.
* @param	RxIntrId is the RX channel Interrupt ID.
*
* @return
*		- XST_SUCCESS if successful,
*		- XST_FAILURE.if not succesful
*
* @note		None.
*
******************************************************************************/
static int SetupIntrSystem(INTC * IntcInstancePtr,
			   XAxiDma * AxiDmaPtr, u16 TxIntrId, u16 RxIntrId)
{
	int Status;
	XScuGic_Config *IntcConfig;


	/*
	 * Initialize the interrupt controller driver so that it is ready to
	 * use.
	 */
	IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
	if (NULL == IntcConfig) {
		return XST_FAILURE;
	}

	Status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,
					IntcConfig->CpuBaseAddress);
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	// https://forums.xilinx.com/t5/Embedded-Development-Tools/FreeRTOS-interrupt-priority/m-p/1046663#M51190
	//XScuGic_SetPriorityTriggerType(IntcInstancePtr, TxIntrId, 0xA0, 0x3);
	XScuGic_SetPriorityTriggerType(IntcInstancePtr, TxIntrId, (configMAX_API_CALL_INTERRUPT_PRIORITY + 1) << portPRIORITY_SHIFT, 0x3);

	// https://forums.xilinx.com/t5/Embedded-Development-Tools/FreeRTOS-interrupt-priority/m-p/1046663#M51190
	//XScuGic_SetPriorityTriggerType(IntcInstancePtr, RxIntrId, 0xA0, 0x3);
	XScuGic_SetPriorityTriggerType(IntcInstancePtr, RxIntrId, (configMAX_API_CALL_INTERRUPT_PRIORITY + 1) << portPRIORITY_SHIFT, 0x3);
	/*
	 * Connect the device driver handler that will be called when an
	 * interrupt for the device occurs, the handler defined above performs
	 * the specific interrupt processing for the device.
	 */
	Status = XScuGic_Connect(IntcInstancePtr, TxIntrId,
				(Xil_InterruptHandler)TxIntrHandler,
				AxiDmaPtr);
	if (Status != XST_SUCCESS) {
		return Status;
	}

	Status = XScuGic_Connect(IntcInstancePtr, RxIntrId,
				(Xil_InterruptHandler)RxIntrHandler,
				AxiDmaPtr);
	if (Status != XST_SUCCESS) {
		return Status;
	}

	XScuGic_Enable(IntcInstancePtr, TxIntrId);
	XScuGic_Enable(IntcInstancePtr, RxIntrId);

	/* Enable interrupts from the hardware */

	Xil_ExceptionInit();
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
			(Xil_ExceptionHandler)INTC_HANDLER,
			(void *)IntcInstancePtr);

	Xil_ExceptionEnable();

	return XST_SUCCESS;
}

@RealtimeRik If I understand correctly, it points to the IDLE task:

If you break in debugger, what is the code doing when it appears stuck? Do you mean that the call to SetupIntrSystem never succeeds or it succeeds and the other tasks which are woken from ISR are not getting executed? If later is the case, would you please if xHigherPriorityTaskWoken is getting set to pdTRUE.

Have you defined configASSERT in your FreeRTOSConfig.h?

Thanks.

The problem was initializing the interrupts after FreeRTOS did it and also how the priorities were set (rather the priority controller variable). Therefore, this is how tasks should be to make everything work. The idea is to have one timed task via vTaskDelay to give the semaphore every 500ms to a second task, that sends data over the DMA (mm2s). A third task waits for a semaphore that is given by an interrupt (s2mm) to display the incoming data (printf didn’t cause any issues). In case it helps someone else, here is what made it work:

Tasks declaration (all with same priority):

		sys_thread_new("Timed Task",
			timed_task, // Function to establish communication with master
			NULL,	// No need to pass parameters to the thread
			TCP_SERVER_THREAD_STACKSIZE,
			DEFAULT_THREAD_PRIO);

		sys_thread_new("Semaphore Task",
			SemaphoreTask, // Function to establish communication with master
			NULL,	// No need to pass parameters to the thread
			TCP_SERVER_THREAD_STACKSIZE,
			DEFAULT_THREAD_PRIO);

		sys_thread_new("DMA Task",
			DMATask, // Function to establish communication with master
			NULL,	// No need to pass parameters to the thread
			TCP_SERVER_THREAD_STACKSIZE,
			DEFAULT_THREAD_PRIO);

Tasks:

xSemaphoreHandle SemTimed = NULL;
XAxiDma_Config *Config;
u8 *TxBufferPtr;
u8 *RxBufferPtr;
XAxiDma_Config *Config;
static void SemaphoreTask(void *p)
{
	int Status;
	unsigned char stream[68]={0x03,0x3f,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0xac,0x8f,0xa6,0x5e,0x3e,0xf6,0xa0,0x07,0x0a,0x00,0x00,0x00,0x74,0x65,0x73,0x74,0x5f,0x69,0x6d,0x61,0x67,0x65,0x02,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x72,0x67,0x62,0x38,0x01,0x06,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x11,0x12,0x13,0x21,0x22,0x23,0x31,0x32,0x33,0x41,0x42,0x43};

	// Create semaphores
	SemTimed = xSemaphoreCreateBinary();
	if(SemTimed==NULL){
		xil_printf("Could not create semaphore SemPLtoPS\n");
		return XST_FAILURE;
	}

	TxBufferPtr = (u8 *)TX_BUFFER_BASE;
	RxBufferPtr = (u8 *)RX_BUFFER_BASE;
	Config = XAxiDma_LookupConfig(DMA_DEV_ID);
	if (!Config) {
		xil_printf("No config found for %d\r\n", DMA_DEV_ID);

		return XST_FAILURE;
	}

	/* Initialize DMA engine */
	Status = XAxiDma_CfgInitialize(&AxiDma, Config);

	if (Status != XST_SUCCESS) {
		xil_printf("Initialization failed %d\r\n", Status);
		return XST_FAILURE;
	}

	if(XAxiDma_HasSg(&AxiDma)){
		xil_printf("Device configured as SG mode \r\n");
		return XST_FAILURE;
	}

	/* Set up Interrupt system  */
	Status = SetupIntrSystem(&Intc, &AxiDma, TX_INTR_ID, RX_INTR_ID);
	if (Status != XST_SUCCESS) {

		//xil_printf("Failed intr setup\r\n");
		return XST_FAILURE;
	}

	/* Disable all interrupts before setup */

	XAxiDma_IntrDisable(&AxiDma, XAXIDMA_IRQ_ALL_MASK,
						XAXIDMA_DMA_TO_DEVICE);

	XAxiDma_IntrDisable(&AxiDma, XAXIDMA_IRQ_ALL_MASK,
				XAXIDMA_DEVICE_TO_DMA);

	/* Enable all interrupts */
	XAxiDma_IntrEnable(&AxiDma, XAXIDMA_IRQ_ALL_MASK,
							XAXIDMA_DMA_TO_DEVICE);


	XAxiDma_IntrEnable(&AxiDma, XAXIDMA_IRQ_ALL_MASK,
							XAXIDMA_DEVICE_TO_DMA);

	while(1){
		if(xSemaphoreTake(SemTimed, portMAX_DELAY)== pdTRUE){
			//xil_printf("Semaphore task\n");
			for(int i=0; i<MAX_PKT_LEN; i++)
				TxBufferPtr[i] = stream[i];
			/* Flush the SrcBuffer before the DMA transfer, in case the Data Cache
			 * is enabled
			 */
			Xil_DCacheFlushRange((UINTPTR)TxBufferPtr, MAX_PKT_LEN);
			Xil_DCacheFlushRange((UINTPTR)RxBufferPtr, MAX_PKT_LEN);

			Status = XAxiDma_SimpleTransfer(&AxiDma,(UINTPTR) RxBufferPtr,
						MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA);

			if (Status != XST_SUCCESS) {
				return XST_FAILURE;
			}

			Status = XAxiDma_SimpleTransfer(&AxiDma,(UINTPTR) TxBufferPtr,
						MAX_PKT_LEN, XAXIDMA_DMA_TO_DEVICE);

			if (Status != XST_SUCCESS) {
				return XST_FAILURE;
			}
		}
	}
}

static void timed_task(void *p)
{
	//const TickType_t xDelay = 500 / portTICK_PERIOD_MS;
	static BaseType_t xHigherPriorityTaskWoken;
	xHigherPriorityTaskWoken = pdFALSE;

	while(1){
		printf("Timed task\n");
		//vTaskDelay(xDelay);
		if(SemTimed!=NULL){
			xSemaphoreGiveFromISR(SemTimed, &xHigherPriorityTaskWoken);
			portYIELD_FROM_ISR( xHigherPriorityTaskWoken);
		}
		vTaskDelay(pdMS_TO_TICKS(500));
	}
}

xSemaphoreHandle SemDMATx = NULL;
xSemaphoreHandle SemDMARx = NULL;
static void DMATask(void *p)
{
	int Status;

	// Create semaphores
	SemDMATx = xSemaphoreCreateBinary();
	if(SemDMATx==NULL){
		xil_printf("Could not create semaphore SemDMATx\n");
		return XST_FAILURE;
	}
	SemDMARx = xSemaphoreCreateBinary();
	if(SemDMARx==NULL){
		xil_printf("Could not create semaphore SemDMARx\n");
		return XST_FAILURE;
	}

	/* Set up Interrupt system  */
	/*
	 * Initialize the interrupt controller driver so that it is ready to
	 * use.
	 */
	Status = SetupIntrSystem(&Intc, &AxiDma, TX_INTR_ID, RX_INTR_ID);
	if (Status != XST_SUCCESS) {

		xil_printf("Failed intr setup\r\n");
		return XST_FAILURE;
	}


	while(1){
		if((xSemaphoreTake(SemDMATx, portMAX_DELAY)== pdTRUE) && (xSemaphoreTake(SemDMARx, portMAX_DELAY)== pdTRUE)){
			if (Error) {
				xil_printf("Failed test transmit%s done, "
				"receive%s done\r\n", TxDone? "":" not",
								RxDone? "":" not");
			}

			for(int i=0; i<MAX_PKT_LEN; i++)
			{
				printf("0x%.2x ", RxBufferPtr[i]);
			}
			printf("\n");
		}
	}
}

Interrupt related functions and callbacks:

/*****************************************************************************/
/*
*
* This is the DMA TX Interrupt handler function.
*
* It gets the interrupt status from the hardware, acknowledges it, and if any
* error happens, it resets the hardware. Otherwise, if a completion interrupt
* is present, then sets the TxDone.flag
*
* @param	Callback is a pointer to TX channel of the DMA engine.
*
* @return	None.
*
* @note		None.
*
******************************************************************************/
static void TxIntrHandler(void *Callback)
{

	u32 IrqStatus;
	int TimeOut;
	XAxiDma *AxiDmaInst = (XAxiDma *)Callback;
	static BaseType_t xHigherPriorityTaskWoken;

	xHigherPriorityTaskWoken = pdFALSE;

	/* Read pending interrupts */
	IrqStatus = XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DMA_TO_DEVICE);

	/* Acknowledge pending interrupts */


	XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DMA_TO_DEVICE);

	/*
	 * If no interrupt is asserted, we do not do anything
	 */
	if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK)) {

		return;
	}

	/*
	 * If error interrupt is asserted, raise error flag, reset the
	 * hardware to recover from the error, and return with no further
	 * processing.
	 */
	if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK)) {

		Error = 1;

		/*
		 * Reset should never fail for transmit channel
		 */
		XAxiDma_Reset(AxiDmaInst);

		TimeOut = RESET_TIMEOUT_COUNTER;

		while (TimeOut) {
			if (XAxiDma_ResetIsDone(AxiDmaInst)) {
				break;
			}

			TimeOut -= 1;
		}

		return;
	}

	/*
	 * If Completion interrupt is asserted, then set the TxDone flag
	 */
	if ((IrqStatus & XAXIDMA_IRQ_IOC_MASK)) {

		TxDone = 1;
		//xSemaphoreGiveFromISR(SemPStoPL, &xHigherPriorityTaskWoken);
		xSemaphoreGiveFromISR(SemDMARx, &xHigherPriorityTaskWoken);
	}
	portYIELD_FROM_ISR( xHigherPriorityTaskWoken);
}

/*****************************************************************************/
/*
*
* This is the DMA RX interrupt handler function
*
* It gets the interrupt status from the hardware, acknowledges it, and if any
* error happens, it resets the hardware. Otherwise, if a completion interrupt
* is present, then it sets the RxDone flag.
*
* @param	Callback is a pointer to RX channel of the DMA engine.
*
* @return	None.
*
* @note		None.
*
******************************************************************************/
static void RxIntrHandler(void *Callback)
{
	u32 IrqStatus;
	int TimeOut;
	XAxiDma *AxiDmaInst = (XAxiDma *)Callback;
	//static signed BaseType_t xHigherPriorityTaskWoken;
	static BaseType_t xHigherPriorityTaskWoken;

	xHigherPriorityTaskWoken = pdFALSE;

	/* Read pending interrupts */
	IrqStatus = XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DEVICE_TO_DMA);

	/* Acknowledge pending interrupts */
	XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DEVICE_TO_DMA);

	/*
	 * If no interrupt is asserted, we do not do anything
	 */
	if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK)) {
		return;
	}

	/*
	 * If error interrupt is asserted, raise error flag, reset the
	 * hardware to recover from the error, and return with no further
	 * processing.
	 */
	if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK)) {

		Error = 1;

		/* Reset could fail and hang
		 * NEED a way to handle this or do not call it??
		 */
		XAxiDma_Reset(AxiDmaInst);

		TimeOut = RESET_TIMEOUT_COUNTER;

		while (TimeOut) {
			if(XAxiDma_ResetIsDone(AxiDmaInst)) {
				break;
			}

			TimeOut -= 1;
		}

		return;
	}

	/*
	 * If completion interrupt is asserted, then set RxDone flag
	 */
	if ((IrqStatus & XAXIDMA_IRQ_IOC_MASK)) {

		RxDone = 1;
		//xSemaphoreGiveFromISR(SemPLtoPS, &xHigherPriorityTaskWoken);
		xSemaphoreGiveFromISR(SemDMATx, &xHigherPriorityTaskWoken);
	}
	portYIELD_FROM_ISR( xHigherPriorityTaskWoken);
}

/*****************************************************************************/
/*
*
* This function setups the interrupt system so interrupts can occur for the
* DMA, it assumes INTC component exists in the hardware system.
*
* @param	IntcInstancePtr is a pointer to the instance of the INTC.
* @param	AxiDmaPtr is a pointer to the instance of the DMA engine
* @param	TxIntrId is the TX channel Interrupt ID.
* @param	RxIntrId is the RX channel Interrupt ID.
*
* @return
*		- XST_SUCCESS if successful,
*		- XST_FAILURE.if not succesful
*
* @note		None.
*
******************************************************************************/
static int SetupIntrSystem(INTC * IntcInstancePtr,
			   XAxiDma * AxiDmaPtr, u16 TxIntrId, u16 RxIntrId)
{
	int Status;
    extern XScuGic xInterruptController;

    XScuGic_SetPriorityTriggerType(&xInterruptController, TxIntrId, (configMAX_API_CALL_INTERRUPT_PRIORITY + 1) << portPRIORITY_SHIFT, 0x3);
    XScuGic_SetPriorityTriggerType(&xInterruptController, RxIntrId, (configMAX_API_CALL_INTERRUPT_PRIORITY + 1) << portPRIORITY_SHIFT, 0x3);

	/*
	 * Connect the device driver handler that will be called when an
	 * interrupt for the device occurs, the handler defined above performs
	 * the specific interrupt processing for the device.
	 */
	Status = XScuGic_Connect(&xInterruptController, TxIntrId,
				(Xil_InterruptHandler)TxIntrHandler,
				AxiDmaPtr);
	if (Status != XST_SUCCESS) {
		return Status;
	}

	Status = XScuGic_Connect(&xInterruptController, RxIntrId,
				(Xil_InterruptHandler)RxIntrHandler,
				AxiDmaPtr);
	if (Status != XST_SUCCESS) {
		return Status;
	}

	XScuGic_Enable(&xInterruptController, TxIntrId);
	XScuGic_Enable(&xInterruptController, RxIntrId);

	/* Enable interrupts from the hardware */

	Xil_ExceptionInit();
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
			(Xil_ExceptionHandler)INTC_HANDLER,
			(void *)&xInterruptController);

	Xil_ExceptionEnable();

	return XST_SUCCESS;
}


/*****************************************************************************/
/**
*
* This function disables the interrupts for DMA engine.
*
* @param	IntcInstancePtr is the pointer to the INTC component instance
* @param	TxIntrId is interrupt ID associated w/ DMA TX channel
* @param	RxIntrId is interrupt ID associated w/ DMA RX channel
*
* @return	None.
*
* @note		None.
*
******************************************************************************/
static void DisableIntrSystem(INTC * IntcInstancePtr,
					u16 TxIntrId, u16 RxIntrId)
{
#ifdef XPAR_INTC_0_DEVICE_ID
	/* Disconnect the interrupts for the DMA TX and RX channels */
	XIntc_Disconnect(IntcInstancePtr, TxIntrId);
	XIntc_Disconnect(IntcInstancePtr, RxIntrId);
#else
	XScuGic_Disconnect(IntcInstancePtr, TxIntrId);
	XScuGic_Disconnect(IntcInstancePtr, RxIntrId);
#endif
}

/*******************************************************************************/

Solution proposed here: https://forums.xilinx.com/t5/Embedded-Development-Tools/FreeRTOS-multiple-tasks-not-responding-when-mixed-with-a/m-p/1128721/highlight/true#M54385

1 Like

Glad that it worked. Thank you for taking time to share your solution.