Cortex R5 timer interrupt happening just once

ferenczdr wrote on Thursday, November 14, 2019:

Hi

I’m trying to develop a PWM signal using the TTC1 timer interrupts. My development board is an ultra96 with Xilinx’s MPSoC and I’m building on top of the Xilinx’s freeRTOS Hello World example.

I have two really simple tasks that are running for testing - one initializes the timer and interrupt and prints with a delay, and the other one just prints with a delay.

void servoTask(){
	ttcInitialize();
	 const TickType_t xDelay = 200 / portTICK_PERIOD_MS;

	xil_printf("Servo task\r\n");
	for(;;) {
        vTaskDelay( xDelay );

    	xil_printf("Servo print\r\n");
	}
}

void dummyTask(){
	xil_printf("Dummy task\r\n");
	 const TickType_t xDelay = 2000 / portTICK_PERIOD_MS;

	for(;;) {
        vTaskDelay( xDelay );
    	xil_printf("dummy print\r\n");
	}
}

I have followed the demo for Cortex R5 and initialized my timer and interrupt using the

FreeRTOS XScuGic xInterruptController

/* Initialize TTC1 */
    XTtcPs ttc1Inst;			/* Driver instance */
    XTtcPs_Config* ttc1Conf;	/* TTC configuration */
    TimerSetup timerSetup;
    int Status;

    extern XScuGic xInterruptController;

    /* Lookup configuration */
    ttc1Conf = XTtcPs_LookupConfig(TTC_TICK_DEVICE_ID);

    /* Initialize TTC */
    Status = XTtcPs_CfgInitialize(&ttc1Inst, ttc1Conf, ttc1Conf->BaseAddress);
    if (Status != XST_SUCCESS){
    	return XST_FAILURE;
    }

    /* Set options */
    timerSetup.OutputHz = 1;	/* 50Hz to accomplish the 20ms period */
    timerSetup.Options = (XTTCPS_OPTION_INTERVAL_MODE | XTTCPS_OPTION_WAVE_DISABLE);

    XTtcPs_SetOptions(&ttc1Inst, timerSetup.Options);

    /* Calculate and set the interval and prescaler automatically */
    XTtcPs_CalcIntervalFromFreq(&ttc1Inst,
    							timerSetup.OutputHz,
								&(timerSetup.Interval),
								&(timerSetup.Prescaler));				/* TODO: PSC is calculated as disabled... why? Does not make sense to have such a fast clock */

    XTtcPs_SetInterval(&ttc1Inst, timerSetup.Interval);					/* Set interval */
    XTtcPs_SetPrescaler(&ttc1Inst, timerSetup.Prescaler);				/* Set prescaler */

	XScuGic_SetPriorityTriggerType( &xInterruptController, XPAR_XTTCPS_3_INTR, configMAX_API_CALL_INTERRUPT_PRIORITY << portPRIORITY_SHIFT, 3 );
	Status = XScuGic_Connect(&xInterruptController, XPAR_XTTCPS_3_INTR,
					(Xil_ExceptionHandler)IntCallback,
					&ttc1Inst);
	configASSERT( Status == XST_SUCCESS);

	XScuGic_Enable(&xInterruptController, XPAR_XTTCPS_3_INTR);

	/* Enable TTC and interrupt */
    XTtcPs_EnableInterrupts(&ttc1Inst, XTTCPS_IXR_INTERVAL_MASK);	/* Enable interrupt */

    /* Start timer*/
    XTtcPs_Start(&ttc1Inst);	/* Start timer */

    return XST_SUCCESS;

The 2 tasks are working correctly and printing with the delays given. However, the timer interrupt handler runs only once and then it stops being called again… In theory the handler should be called every one second, which does at the beginning, but then it is not called again. This is my handler

void IntCallback(void *CallBackRef){

	u32 StatusEvent = 0;
	XTtcPs* ttc1Inst = (XTtcPs*)CallBackRef;

	StatusEvent = XTtcPs_GetInterruptStatus(ttc1Inst);		/* Read and reset interrupt */
	XTtcPs_ClearInterruptStatus(ttc1Inst, StatusEvent);

	configASSERT( ( XTTCPS_IXR_INTERVAL_MASK & StatusEvent ) != 0 );

	xil_printf("Callback\r\n");

}

I thing the freeRTOS is disabling interrupts after the first run, maybe I’m not clearing the interrupt correctly?

Any thoughts about what could be causing this?

Thanks

rtel wrote on Thursday, November 14, 2019:

You are calling xil_printf() from two tasks - are you sure that is safe

  • specifically is xil_printf() thread safe? You are also calling
    xil_printf() from your interrupt handler which is almost certainly not safe.

I have a couple of suggestions -

  1. First start by confirming to yourself that the interrupt is working
    exactly as you expect outside of a FreeRTOS application - so just set it
    up from main() and let the interrupt handler execute to check its
    frequency before you make any FreeRTOS calls at all. If it works
    outside of a FreeRTOS application then we can look deeper to see what it
    is about the FreeRTOS app that is preventing it being re-armed.

  2. More of a general comment, keep all calls to print() inside a single
    task (you can have the other tasks send strings to a ‘print’ task to
    have that single task print them out), an especially remove the one from
    the interrupt. Alternatively you can use LEDs to indicate the tasks are
    running instead of print calls.

ferenczdr wrote on Thursday, November 14, 2019:

You are right with xil_printf. I shouldn’t be using them as they are not thread safe. However, I did try using LEDs and they were having the same issue. The interrupt works once - toggling the LEDs, but then it is never called again.

  1. I already built the app in bare metal and it works correctly. Also, in a freeRTOS environment if I initialize the interrupt controller XScuGic interruptInst instead of allowing freeRTOS to do so and not calling vTaskStartScheduler() it also works as intended.

  2. That makes sense, will follow that for future. And yes the LEDs are giving me the same problem as I said above

This is the initialization of the interrupt controller for the port Im using “portZynqUltrascale.c”. Tis is autogenerated code from the Xilinx SDK

static XTtcPs xTimerInstance;
XScuGic xInterruptController;
/*-----------------------------------------------------------*/

void FreeRTOS_SetupTickInterrupt( void )
{
XInterval usInterval;
uint8_t ucPrescaler;
int iStatus;
XTtcPs_Config *pxTimerConfig;
XScuGic_Config *pxInterruptControllerConfig;

	/* Initialize the interrupt controller driver. */
	pxInterruptControllerConfig = XScuGic_LookupConfig( configINTERRUPT_CONTROLLER_DEVICE_ID );
	XScuGic_CfgInitialize( &xInterruptController,
						   pxInterruptControllerConfig,
						   pxInterruptControllerConfig->CpuBaseAddress );

	/* Connect the interrupt controller interrupt handler to the hardware
	interrupt handling logic in the ARM processor. */
	Xil_ExceptionRegisterHandler( XIL_EXCEPTION_ID_IRQ_INT,
								( Xil_ExceptionHandler ) XScuGic_InterruptHandler,
								&xInterruptController);

	/* Enable interrupts in the ARM. */
	Xil_ExceptionEnable();

	/* Connect to the interrupt controller. */
	XScuGic_Connect( &xInterruptController,
					 configTIMER_INTERRUPT_ID,
					( Xil_InterruptHandler ) FreeRTOS_Tick_Handler,
					( void * ) &xTimerInstance );

	pxTimerConfig = XTtcPs_LookupConfig( configTIMER_ID );

	iStatus = XTtcPs_CfgInitialize( &xTimerInstance, pxTimerConfig, pxTimerConfig->BaseAddress );

	if( iStatus != XST_SUCCESS )
	{
		XTtcPs_Stop(&xTimerInstance);
		iStatus = XTtcPs_CfgInitialize( &xTimerInstance, pxTimerConfig, pxTimerConfig->BaseAddress );
		if( iStatus != XST_SUCCESS )
		{
			xil_printf( "In %s: Timer Cfg initialization failed...\r\n", __func__ );
			return;
		}
	}
	XTtcPs_SetOptions( &xTimerInstance, XTTCPS_OPTION_INTERVAL_MODE | XTTCPS_OPTION_WAVE_DISABLE );
	XTtcPs_CalcIntervalFromFreq( &xTimerInstance, configTICK_RATE_HZ, &usInterval, &ucPrescaler );
	XTtcPs_SetInterval( &xTimerInstance, usInterval );
	XTtcPs_SetPrescaler( &xTimerInstance, ucPrescaler );
	/* Enable the interrupt for timer. */
	XScuGic_EnableIntr( configINTERRUPT_CONTROLLER_BASE_ADDRESS, configTIMER_INTERRUPT_ID );
	XTtcPs_EnableInterrupts( &xTimerInstance, XTTCPS_IXR_INTERVAL_MASK );
	XTtcPs_Start( &xTimerInstance );

}

rtel wrote on Thursday, November 14, 2019:

Does this mean you have the solution? What is different when you
initialise the interrupt controller compared to when FreeRTOS does it?
I wouldn’t have thought it was as simple as the fact that FreeRTOS
clears the vectors (meaning you need to initialise the timer from a
FreeRTOS task rather than before the scheduler was started) as that
would prevent the timer interrupt from executing at all - it is
interesting that it executes once. Also, is the interrupt entry handler
the same in both cases? Maybe the entry handler you are using outside
of FreeRTOS is re-enabling interrupts - whereas in the FreeRTOS handler
it is up to individual handlers to re-enable interrupts if they want to
support interrupt nesting (don’t know that for sure, just trying to
think what would be different).

ferenczdr wrote on Thursday, November 14, 2019:

What is different when you
initialise the interrupt controller compared to when FreeRTOS does it?

There is no difference between interrupt controller initialization. So I believe I can use the FreeRTOS initialization and just reference the xTimerController when initializing my timer.

(meaning you need to initialise the timer from a
FreeRTOS task rather than before the scheduler was started) as that
would prevent the timer interrupt from executing at all - it is
interesting that it executes once.

Sorry I might have not explained myself right. I am initializing my timer and adding the interrupt insisde a task. Therefore, I run first the vTaskStartScheduler() - which intializes the tick for FreeRTOS and the interrupt controller, and then inside a task I initalize my timer and add it to the interrupt controller. But yes, it is odd that it executes once and then stops executing.

Also, is the interrupt entry handler
the same in both cases?

Interrupt handler is the same. Read the interrupt status, clear it and do some work

Maybe the entry handler you are using outside
of FreeRTOS is re-enabling interrupts - whereas in the FreeRTOS handler
it is up to individual handlers to re-enable interrupts if they want to
support interrupt nesting

I got a bit lost on this. what do you mean by the entry handle?
I checked the resgisters for my timer and it does not clear the interrupt enable bit. Does RTOS provide any way to reenable interrupts?

I still think that somehow after the first interrupt handler happens FreeRTOS is disabling my interrupt and somewhere inside the kernel.

rtel wrote on Thursday, November 14, 2019:

I got a bit lost on this. what do you mean by the entry handle?

All the interrupts have a single entry point, which in the FreeRTOS demo
in the FreeRTOS download is defined here:

https://sourceforge.net/p/freertos/code/HEAD/tree/tags/V10.2.1/FreeRTOS/Demo/CORTEX_R5_UltraScale_MPSoC/RTOSDemo_R5/src/FreeRTOS_tick_config.c#l113

Note that interrupts are re-enabled in the single entry point in the
linked implementation. Not all implementations do that - some leave it
up to the handler called from the single entry point to decide if they
want to re-enable interrupts.

ferenczdr wrote on Thursday, November 14, 2019:

I compared the entry point and it is the same.

I also found out this function call changes the way the interrupt behave

XScuGic_SetPriorityTriggerType( &xInterruptController, XPAR_XTTCPS_3_INTR, configMAX_API_CALL_INTERRUPT_PRIORITY << portPRIORITY_SHIFT, 3 );

I call this when I’m setting up my timer interrupt. The last parameter is the ‘Trigger’ option and 3 is Rising edge sensitive - if I change to 1 (Active HIGH level sensitive) it always calls the interrupt and never goes to the tasks running.

Im assuming timer triggers and it rises but stays high and thefore it just runs once?