Deferred interrupt technique working only after power reset

Hi,

I am trying the deferred interrupt handling method on a Zybo Z7 board. The setup is as follows :

  • A task of priority 1 keeps incrementing a counter
  • An interrupt hits every 1s. The handler then writes a value to a queue.
  • The deferred interrupt handler task (having priority 2) executes to received data from the queue the ISR wrote to and sends it on another queue for the primary task to receive.
  • After the deferred interrupt task blocks to wait for new data from the ISR, the first task runs, retrieves the value from the queue written to by the deferred ISR handler task and resets the counter.

This scheme works when I power-cycle the board. However, if I don’t power cycle the board, but just reflash with updated software, I get the following error in the call SendToBackFromISR() : Assert failed in file queue.c, line 930. Would appreciate some help in figuring this.

For ref : Here are the incrementer task function, ISR and the deferred ISR task :

static void myTimer_interrupt_handler(void *CallBackRef){

	u32 StatusEvent;
	XTtcPs *myTimer_local = (XTtcPs*)CallBackRef; // 'myTimer_local' points to 'myTimer' object in main() due to CallBackRef
	StatusEvent = XTtcPs_GetInterruptStatus(myTimer_local);
	xil_printf("Inside interrupt handler!\r\n");
	u32 reset_value = 0;
	BaseType_t xHigherPriorityTaskWoken = pdFALSE;
	xQueueSendToBackFromISR(xISRQueue, &reset_value, &xHigherPriorityTaskWoken); // Send data to queue
	portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
	XTtcPs_ClearInterruptStatus(myTimer_local, StatusEvent);

}

void vTaskFunction(void *pvParameters){

	char *TaskString = "Task is running";
	u32 received_value;
	BaseType_t xstatus;

	for(;;){

		xstatus = xQueueReceive(xTaskQueue, &received_value, 0);
		if (xstatus == pdPASS){
			xil_printf("Received %ld from ISR via deferred task. Resetting...\r\n", received_value);
			counter = received_value; // reset counter
		}
		counter++;
		xil_printf("%s | counter = %ld\r\n", TaskString, counter);
	}
}

void vTaskDeferredInterruptHandler(void *pvParameters){

	u32 received_value_from_isr;
	for(;;){

		// Get value from ISR Queue
		xQueueReceive(xISRQueue, &received_value_from_isr, portMAX_DELAY); // Block indefinitely for data

		// Send value to Task Queue
		xQueueSendToBack(xTaskQueue, &received_value_from_isr, 0);

		xil_printf("Received %ld from ISR Queue and sent to Task Queue\r\n", received_value_from_isr);
	}
}

Thanks
Yogesh

Are you sure that’s it’s allowed/possible to use xil_printf in an ISR ?
Usually printf family functions can’t be used in ISRs due to various reasons including their stack usage (and internal heap usage of some implementations).
Try to omit the debug output in ISR. It might be just luck, that the application sometimes runs, sometimes not e.g. due to a stack overflow.
Besides using configASSERT did you also enable stack overflow checking for development/debugging ?
If referring to an assert it might be better to post the corresponding code snippet of the FreeRTOS version/code you’re using. Or tell the FreeRTOS version at least.
(Please enclose code blocks by 3 tildas (~~~) or backticks (```) for better readability.)

Before trying out the FreeRTOS implementation, I tried a similar thing in a baremetal setting. I did not get any warnings about the use of xil_printf. The code also worked on repeated power cycling as well just reflashing.

I am using FreeRTOS v10.0.0. This is the console output. The configASSERT( pxQueue ); fails inside the xQueueGenericSendFromISR() function
img2

Thank you for the response.

Bare metal can be different regarding memory mapping and maybe the xil_printf implementation.
However, what is the assert telling exactly ? Do you have a debugger attached to stop (there) and check the call stack / backtrace ?
I afraid that not many download FreeRTOS 10.0.0 trying to find the source location you mentioned. I’d propose you just post the relevant code block.

Can you elaborate on this? By doing something similar in bare metal are you saying you used xil_printf() in an interrupt in bare metal and that worked? First as per hs2 - printing is rarely (stopping short of saying never) a good idea from an interrupt for the reasons already stated - plus it can take too long for an ISR and cause other havoc downstream. In this case though I’m not sure its the source of your issue.

I’m not sure what that means - what would give you a warning about using it?

Please post a link to the assert that is failing. You can do that by going to the git repo https://github.com/FreeRTOS/FreeRTOS-Kernel, selecting the tag for the version you are using, navigate to the source file, then click the line of code and cut and paste the resultant URL - which will look something like this (which is just a random line I clicked, not relevant to your post) https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/queue.c#L269

If this is a Zynq board then the boot sequence is different between running via the debugger and running ‘stand alone’ (power cycling). Specifically when you run through the debugger the debugger runs a macro called something like ps1_init() (can’t recall the actual name) to setup the hardware ready to run your application. When you build standalone you have to instead call the Xilinx provided function ps1_init() (again not necessarily the real name) from your startup code. Therefore a single build is not compatible with both running via the debugger and running via a power cycle.

1 Like

I had a quick look on GitHub and xQueueGenericSendFromISR asserts that the given queue handle is not NULL, but it is in your case and the assert fails.
That’s often caused by interrupts occurring too early before your code has properly setup all required FreeRTOS resources (used in ISRs) before enabling the corresponding interrupts. This in turn might be caused by the slightly different startup sequence with or without debugger as Richard pointed out.

Hello,

After poking around with the debugger for a bit, it seems this is indeed a case of premature interrupt triggering. Upon a board reset, things work as expected. Reflashing the board with the software resulted in the ISR being triggered before the queue got created, resulting in the assert above. I tried moving the queue creation before any interrupt setup and things started working as expected.

On trying to look for a cause, I found this thread : https://forums.xilinx.com/t5/Embedded-Development-Tools/Zynq-TTC/m-p/910331/highlight/true#M47513 . It appears the so-called EOI register needs to be cleared, and this stops the premature interrupt. So, as of now, things seem to work as expected, but a more precise explanation as to why this was not necessary in a baremetal setting would be useful.

Yes, I used xil_printf() in both baremetal and with FreeRTOS in the ISR and it works, although that might be because the application is very simple.

Thanks Hartmut Schaefer and Richard for your help.

-Yogesh

1 Like