ATSAM4S FreeRTOS Interrupt handling

m2b821 wrote on Monday, October 30, 2017:

Hello, I have a problem with my code. I run a several tasks on FreeRTOS (v7.3, supplied from Atmel ASF) and added a set of new tasks for ADC conversion. I tested the ADC part before without FreeRTOS and it worked fine. But after calling NVIC_EnableIRQ(ADC_IRQn);, FreeRTOS scheduling ends (no task is called again) while the ADC interrupts remain. I have the standard configuration from Atmel, which is using 4 bits for the priorities. I tried to call also moving the call to NVIC_EnableIRQ(ADC_IRQn); before creating any tasks, but this resulted in the same behaviour. Can someone please explain me why, as I am pretty sure that NVIC_EnableIRQ causes the crash, this stops the FreeRTOS kernel. I have already read http://www.freertos.org/RTOS-Cortex-M3-M4.html and used configASSERT on all semaphores and tasks related to this issue.
The ADC interrupts simply loads the result of from two ADC channels and stores them in a local variable. Afterwards it calls the corresponding task through releasing a binary semaphore. The ADC is operating hardware controlled using timer 0 with 10Hz. I understand FreeRTOS this way, the timer 0 and 1 are not consumed by it (it uses sysclk, correct?). As I have other components which will be integrated in my main application with FreeRTOS, I fear that similar errors might occur, as I2C and USB CDC also use hardware interrupts with NVIC_EnableIRQ(...).
The semaphore is all 10Hz released, but no task is ever called after startup (where the ADC task is once called):

long prio = pdFALSE;
xSemaphoreGiveFromISR(*(getSemaphoreHandlerADCDriver()), &prio);
portEND_SWITCHING_ISR(prio);	

Thank you!

rtel wrote on Monday, October 30, 2017:

Hello, I have a problem with my code. I run a several tasks on FreeRTOS
(v7.3, supplied from Atmel ASF)

Grr, so old.

and added a set of new tasks for ADC
conversion. I tested the ADC part before without FreeRTOS and it worked
fine. But after calling |NVIC_EnableIRQ(ADC_IRQn);|, FreeRTOS scheduling
ends (no task is called again) while the ADC interrupts remain.

Do you mean no tasks run because 100% of time is spend processing the
interrupt?

I have
the standard configuration from Atmel, which is using 4 bits for the
priorities. I tried to call also moving the call to
|NVIC_EnableIRQ(ADC_IRQn);| before creating any tasks, but this resulted
in the same behaviour. Can someone please explain me why, as I am pretty
sure that NVIC_EnableIRQ causes the crash, this stops the FreeRTOS
kernel. I have already read
RTOS for ARM Cortex-M and used configASSERT on
all semaphores and tasks related to this issue.

Not sure how much help configASSERT() will be in V7.3. Try using the
head revision from SVN, which has many more assert points specifically
to catch Cortex-M mis-configurations.

The ADC interrupts simply loads the result of from two ADC channels and
stores them in a local variable. Afterwards it calls the corresponding
task through releasing a binary semaphore. The ADC is operating hardware
controlled using timer 0 with 10Hz.

All sounds fine.

I understand FreeRTOS this way, the
timer 0 and 1 are not consumed by it (it uses sysclk, correct?).

Correct.

As I
have other components which will be integrated in my main application
with FreeRTOS, I fear that similar errors might occur, as I2C and USB
CDC also use hardware interrupts with |NVIC_EnableIRQ(…)|.
The semaphore is all 10Hz released, but no task is ever called after
startup (where the ADC task is once called):

I need to understand exactly what that means. If you call FreeRTOS API
functions before the scheduler has started (creating tasks, etc.) then
interrupt below the max syscall interrupt will be left disabled -
deliberately so because you don’t want the interrupt’s service routines
to try using FreeRTOS functions before the scheduler has started.
Interrupts are then fully enabled when the first task starts. If you
call API functions, then enable the interrupt but don’t start the
scheduler, and interrupts are firing at that time, then it sounds like
the priority is wrong - and if the ISR is trying to give a semaphore
before the scheduler has started then even worst (that is exactly the
reason interrupts are left disabled at that time).

FreeRTOS V7 is so old though that I’m not going to remember exactly what
it does in these regards.

m2b821 wrote on Tuesday, October 31, 2017:

Hello, I have updated to kernel version 9.0 (why is the version in Atmel Studio ASF so old?). I also changed all old type names to the new ones. In compliance with your last explanation, I checked that the ADC unit is initialize and the ADC interrupt enable with the first call of adc handling task which also creates another task to take the data from the ADC. This second task is blocked using a binary semaphore as already said. When I am now debugging, the ADC interrupt is only called once but afterwards, the debugger stops always in portmarco.h at portFORCE_INLINE static void vPortRaiseBASEPRI(void). The code of this function is below. I found that this occurs when calling xSemaphoreGiveFromISR(…). So I checked in the debugger that the semaphore is initialized (and also with configASSERT before calling xSemaphoreGiveFromISR). And it is initialized, the debugger reports SemaphoreHandle_t with value 0x200031e0 <ucHeap+11424>. So my question is now why this now working and why is this even blocking the hardware timer triggered AD conversion? I suspect that the next AD conversion only starts if the previous interrupt handler has been executed, which is prevented by this vPortRaiseBASEPRI.

portFORCE_INLINE static void vPortRaiseBASEPRI( void )
{
uint32_t ulNewBASEPRI;

	__asm volatile
	(
		"	mov %0, %1												\n"	\
		"	msr basepri, %0											\n" \
		"	isb														\n" \
		"	dsb														\n" \
		:"=r" (ulNewBASEPRI) : "i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY )
	);
}

The blocked ADC task is also called once after beginning (and blocked successfully by the semaphore). What I find interesting is, that I cannot break at the call to NVIC_EnableIRQ in the debugger, only in the line before or after. When I step through, it is also not possible to stop at this line. Is this a normal behaviour (I said the optimiziation to debugging level (-Og).

m2b821 wrote on Wednesday, November 01, 2017:

Hello, my answer is below (my mistake not to answer directly). I followed your guidelines and it seems that I am now closer to the source of the problem. Could you please tell me what vPoirtRaiseBASEPRI does? I have the semaphore fully initialized and I have a pointer to it, which I use (after dereferencing).

rtel wrote on Wednesday, November 01, 2017:

Where/why is NVIC_EnableIRQ being called. Can you show the call?

what vPoirtRaiseBASEPRI does

BASEPRI is a register in the Cortex-M hardware - you can read about it in the hardware manuals from ARM (not in the SAM4 manuals). It sets the interrupt priority mask level. The function in question raises the mask to whatever you have cnofigMAX_SYSCALL_INTERRUPT_PRIORITY set to, so only interrupts above that priority will execute until BASEPRI is set back to 0.

m2b821 wrote on Wednesday, November 01, 2017:

NVIC_EnableIRQ is called to enable the ADC_IRQn interrupt, the following code is called in the ADC task after scheduling has started. Without calling NVIC_EnableIRQ, I do not get any ADC interrupts.

//! Init ADC unit, using Timer0 with channel 1
void adc_control_init() {
	// Configure timer, we want to have 10Hz
	sysclk_enable_peripheral_clock(ID_TC1);
	uint32_t ul_div = 0;
	uint32_t ul_tc_clks = 0;
	uint32_t ul_sysclk = sysclk_get_cpu_hz();
	tc_find_mck_divisor(10,  ul_sysclk, &ul_div, &ul_tc_clks, ul_sysclk);
	// Init timer clock 0 channel 1 with the found timings and set up counter which resets/clears on reaching the target
	tc_init(TC0, 1, ul_tc_clks|TC_CMR_WAVE|TC_CMR_WAVSEL_UP_RC|TC_CMR_ACPA_CLEAR|TC_CMR_ACPC_SET);
	tc_write_rc(TC0, 1, (ul_sysclk / ul_div));
	tc_write_ra(TC0, 1, (ul_sysclk / ul_div) / 2);	
	sysclk_enable_peripheral_clock(ID_ADC);
	adc_init(ADC, sysclk_get_cpu_hz(), 5000000UL, ADC_MR_STARTUP_SUT512);
	adc_configure_timing(ADC, 0, ADC_MR_SETTLING_AST9, 2);
	adc_set_resolution(ADC, ADC_MR_LOWRES_BITS_12);
	adc_enable_tag(ADC);
	NVIC_EnableIRQ(ADC_IRQn);
}

m2b821 wrote on Wednesday, November 01, 2017:

Hello, I tried a different use cases, which uses the USB controller. Again, I got stuck in vPortRaiseBASEPRI. I think it might be a problem with my configuration, which I imported from the reference project. I would appreciate any idea to fix this, because I cannot use any of the semaphore (I only use them in interrupts), should xSemaphoreGiveFromISR not be working in case of functions called in my interrupt routine?
Note: Both of my used tasks where running succesfully, but blocked through the semaphores.
My FreeRTOS config:

#include <stdint.h>
extern uint32_t SystemCoreClock;

#define configUSE_PREEMPTION					1
#define configUSE_PORT_OPTIMISED_TASK_SELECTION	1
#define configUSE_IDLE_HOOK						0
#define configCPU_CLOCK_HZ						( SystemCoreClock )
#define configTICK_RATE_HZ						( ( TickType_t ) 1000 )
#define configMAX_PRIORITIES					( 5 )
#define configMINIMAL_STACK_SIZE				( ( unsigned short ) 130 )
#define configTOTAL_HEAP_SIZE					( ( size_t ) ( 40960 ) )
#define configMAX_TASK_NAME_LEN					( 50 )
#define configUSE_TRACE_FACILITY				0
#define configUSE_16_BIT_TICKS					0
#define configIDLE_SHOULD_YIELD					1
#define configUSE_MUTEXES						1
#define configQUEUE_REGISTRY_SIZE				20
#define configUSE_RECURSIVE_MUTEXES				1
#define configUSE_APPLICATION_TASK_TAG			0
#define configUSE_COUNTING_SEMAPHORES			1
#define configUSE_QUEUE_SETS					1
#define configGENERATE_RUN_TIME_STATS			0
#define configUSE_APPLICATION_TICK_HOOK			0
#define configUSE_TICK_HOOK						0
#define configCHECK_FOR_STACK_OVERFLOW			0
#define configUSE_MALLOC_FAILED_HOOK			0

/* 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		( 2 )
#define configTIMER_QUEUE_LENGTH		5
#define configTIMER_TASK_STACK_DEPTH	( configMINIMAL_STACK_SIZE * 2 )

/* 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

/* Cortex-M specific definitions. */
#ifdef __NVIC_PRIO_BITS
	/* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
	#define configPRIO_BITS       		__NVIC_PRIO_BITS
#else
	#define configPRIO_BITS       		4        /* 15 priority levels */
#endif

/* The lowest interrupt priority that can be used in a call to a "set priority"
function. */
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY			0x0f

#define configENABLE_BACKWARD_COMPATIBILITY 0

/* The highest interrupt priority that can be used by any interrupt service
routine that makes calls to interrupt safe FreeRTOS API functions.  DO NOT CALL
INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
PRIORITY THAN THIS! (higher priorities are lower numeric values. */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY	10

/* Interrupt priorities used by the kernel port layer itself.  These are generic
to all Cortex-M ports, and do not rely on any particular library functions. */
#define configKERNEL_INTERRUPT_PRIORITY 		( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 	( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )

/* Normal assert() semantics without relying on the provision of an assert.h
header file. */
#define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); }

/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS
standard names. */
#define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler
#define xPortSysTickHandler SysTick_Handler

#endif /* FREERTOS_CONFIG_H */

m2b821 wrote on Friday, November 03, 2017:

Hi, I have now set the interrupt priority for the ADC interrupt to be 13, but the problem is still the same. I am not sure what the priority was before. Interesting is that the first time, xSemaphoreGiveFromISR is executed, then nothing happens and in the next interupt, configASSERT fails. Is configASSERT allowed in interrupts, I think it is? The result is, that my application is stuck in startup_sam4s.c: Dummy_Handler. Maybe my interrupts were deactived by the vPortRaiseBASEPRI?

rtel wrote on Friday, November 03, 2017:

If the code is in the dummy_handler routine then an interrupt or
exception has occurred for which a specific handler was not installed.
I’m going to guess it is a hardfault handler. Have you installed a
handler for faults? It is always best to have individual handlers for
all fault exceptions so you know right away when one gets called. Go to
the ‘my application does not run, what could be wrong’ page on the
FreeRTOS.org site and follow your nose to find a page that describes how
to debug hard faults, and how to find out which fault or interrupt
actually executed.