Articles tagués “it

Cortex-M : comment savoir si on est sous IT ?

En écrivant des wrappers C++ pour FreeRTOS, j’ai eu besoin de savoir si le code était appelé depuis une interruption ou pas. En effet, plusieurs fonctions de FreeRTOS possède une variante avec le suffixe fromISR : il faut appeler la version normale ou la version fromISR aux bons moments. Quand on utilise un Cortex-M, il faut regarder du côté du System Control Block (SCB) et plus particulièrement l’Interrupt Control and State Register (ICSR). Voici une fonction tout simple, prise sur stackoverflow, et qui fait le taf :

bool isInInterrupt() {
    return (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) != 0;
}

Voici un exemple d’utilisation pour mon wrapper de sémaphore :

bool AbstractSemaphore::give() {
	BaseType_t result = isInInterrupt() ?
				xSemaphoreGiveFromISR(nativeHandle_m, nullptr) :
				xSemaphoreGive(nativeHandle_m);
	return result == pdTRUE;
}

Il m’est ainsi possible d’appeler la fonction give() sans me soucier de savoir si l’appel se fait depuis une IT ou pas. Attention, cela ne veut pas dire qu’il ne faut se soucier de rien ! En effet, il existe dans FreeRTOS un mécanisme empêchant l’appel des  fonctions dites syscalls depuis une IT dont la priorité est supérieure à un seuil. Ce seuil est configuré dans FreeRTOSConfig.h avec la constante configMAX_SYSCALL_INTERRUPT_PRIORITY (pour plus de détails, voir la documentation de FreeRTOS) :

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

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

En fait, la valeur vraiment utile est celle de configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY : 5. Pour rappel, plus la valeur est grande, plus la priorité est faible. Dans ma configuration actuelle, cela signifie que je peux appeler give() depuis mon IT d’external interrupt seulement si la priorité d’interruption est configurée pour x => 5 :

NVIC_SetPriority(EXTI15_10_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), x, 0));

Le cas échéant, par exemple si x == 3, une assertion de FreeRTOS bloque l’exécution :

A la ligne 742 de port.c, on trouve :

configASSERT( ucCurrentPriority >= ucMaxSysCallPriority );

Il est possible de rajouter sa propre assertion dans le wrapper C si on a une meilleure remontée des erreurs qu’un simple blocage de l’application. Il est aussi possible de redéfinir la macro configASSERT() dans FreeRTOSConfig.h (voir à nouveau la documentation), qui par défaut est définie ainsi :

#define configASSERT( x ) if ((x) == 0) {taskDISABLE_INTERRUPTS(); for( ;; );}