Interface logicielle (API) - Cœur - Chien de garde (Watchdog)

De Wiki Techno-Innov
< Technique‎ | Logiciel‎ | API‎ | Use‎ | Core
Aller à la navigation Aller à la recherche


Tant que le chien de garde est nourri régulièrement, tout se passe bien. Sinon, il aboie !

Présentation

#include "core/watchdog.h"

Le mécanisme de "Watchdog" ou "Chien de garde" est un mécanisme de protection contre les erreurs de programmation (principalement) ou les éventuelles erreurs d'exécution du micro-contrôleur (plus rare).
Les erreurs de programmation concernées sont du type "dead lock" (attente d'un événement qui ne se produira jamais), cas non prévu lors de l'étude, mais qui se produit quand même, ou cas non testé, et non supporté.
Les erreurs d'exécution du micro-contrôleur sont liées à l'exécution d'instructions erronées, ou des accès à des adresses inexistantes, dues à une chute de tension ou une perturbation électromagnétique externe qui se produit lors d'une lecture en Flash ou pendant l'exécution.

Attention, le chien de garde ne protège pas contre "tout".
Son fonctionnement est basé sur le principe de ré-activation régulière d'un timer pour qu'il n'expire jamais. Si le timer n'est pas ré-activé dans un délai donné, il provoque le reset du micro-contrôleur.
Ainsi, une corruption mémoire qui ferait exécuter une boucle une fois de trop, tout en restant dans le délai imparti, ne sera pas détectée.

Fonctionnement sans Watchdog

void startup_watchdog_disable(void);

Dans certains cas, comme par exemple pour le développement, pour une application non critique, pour une application devant être mise en veille régulièrement pour de longues durées, il est nécessaire de désactiver complètement le Watchdog.

L'appel à cette fonction doit être fait au plus tôt, car certains micro-contrôleurs (le LPC122x par exemple) démarrent avec le Watchdog actif.

Configuration

struct wdt_config {
   /* clk_sel is either 0 (IRC) or 1 (WDTCLK). The corresponding clock source will be powered on. */
   int clk_sel;
   int intr_mode_only; /* If set to 1, a watchdog timeout will trigger an interrupt instead of a reset */
   void (*callback)(void);
   uint32_t locks; /* Bitfield from WDT_*_LOCK defined in watchdog.h */
   /* Number of clk_src clocks before the watchdog timer times out. Will be divided by 4 to give
    *   the watchdog reload value */
   uint32_t nb_clk;  /* 0x3FF to 0x03FFFFFF */
   /* The next two values are relative to the timer value, not the number of clk_src clocks. */
   uint32_t wdt_window; /* Watchdog window value : 0x100 to 0x00FFFFFF */
   uint16_t wdt_warn; /* 0x00 to 0x3FF */
};

void watchdog_config(const struct wdt_config* wd_conf);
#define WDT_CLK_IRC
#define WDT_CLK_WDTCLK

void watchdog_set_timer_val(uint32_t nb_clk);

La configuration du Watchdog passe par une structure "wdt_config", qui permet de réaliser l'ensemble de la configuration à l'aide de la fonction watchdog_config() qui prend en unique paramètre un pointeur vers une structure wdt_config. Il reste possible de modifier cette configuration initiale tant que les éléments concernés n'ont pas été protégés contre les modifications.

Structure wdt_config

La structure wdt_config contient les champs suivants :

  • clk_sel : Sélection de l'horloge source lorsque c'est possible : soit l'oscilateur interne (WDT_CLK_IRC) ou l'horloge spécifique (WDT_CLK_WDTCLK).
  • intr_mode_only : Lorsque le micro-contrôleur le supporte, si "intr_mode_only" vaut "1" le Watchdog est configuré pour générer une interruption plutôt que de redémarrer le micro-contrôleur. Ceci est utile pour le debug, ou pour utiliser une procédure de ré-initialisation plus rapide qu'un re-démarrage complet.
  • callback : La fonction à appeler dans le cas ou le Watchdog est configuré pour générer une interruption.
  • locks : Les éléments de la configuration du Watchdog à protéger contre la modification. (Voir "Protection de la configuration" ci-dessous).
  • nb_clk : Nombre de coups d'horloge (de clk_src) avant un timeout du timer interne du Watchdog. "nb_clk" est une valeur entre 0x3FF et 0x03FFFFFF.
  • wdt_window : Lorsque le micro-contrôleur le supporte, ce champ sert à définir le début de la fenêtre de rechargement du Watchdog. Cette fonctionnalité empêche un rechargement trop fréquent du Watchdog.
  • wdt_warn : Cette valeur définit la valeur du timer interne du Watchdog à laquelle est générée une interruption si le micro-contrôleur supporte cette fonctionnalité. Cette valeur est entre 0x00 et 0x3FF.

Procédure de mise en œuvre

La partie la plus compliquée concernant la procédure de mise en œuvre est la détermination des différents paramètres de configuration (délais, mode de fonctionnement, verrouillage).

Une fois les éléments de configuration déterminés, soit l'ensemble de la configuration peut être appliquée dés le début, auquel cas un unique appel à la fonction watchdog_config() est suffisant, soit la procédure de démarrage implique des délais importants (temps de réveil d'un capteur ou autre périphérique externe) et il y a alors deux solutions qui sont la configuration partielle du watchdog en début de procédure d'initialisation, avec un délai long avant l'expiration du timer, puis une re-configuration en fin de procédure d'initialisation, ou, la configuration immédiate avec les paramètres finaux, et l'utilisation de fonctions de rappel (callbacks) dont l'exécution est programmée après un délai donné en utilisant les fonctionnalités du driver Systick.

Protection de la configuration

#define WDT_CLK_POWER_LOCK
#define WDT_CLK_SRC_LOCK
#define WDT_EN_LOCK
#define WDT_TIMER_VAL_LOCK
#define WDT_POWER_DOWN_LOCK

void watchdog_lock_clk_src(void);
void watchdog_lock_clk_src_power(void);
void watchdog_lock_timer_val(void);
void watchdog_lock_enable(void);
void watchdog_disable_power_down(void);
void watchdog_lock_full(void);

La configuration du Watchdog peut être protégée contre les modifications ultérieures (accidentelles ou non), soit directement lors de la configuration initiale en utilisant une combinaison des définitions listées ci-dessus (WDT_*_LOCK) pour le champ "locks" de la structure wdt_config, soit à postériori, en utilisant les fonctions watchdog_lock_*() et watchdog_disable_power_down().

Il y a 5 éléments pouvant être protégés contre la modification (selon les micro-contrôleurs certains peuvent être absents) :

  • L'horloge source : WDT_CLK_SRC_LOCK et watchdog_lock_clk_src()
  • L'alimentation de l'horloge source : WDT_CLK_POWER_LOCK et watchdog_lock_clk_src_power()
  • L'activation du watchdog : WDT_EN_LOCK et watchdog_lock_enable()
  • La valeur de ré-initialisation du timer : WDT_TIMER_VAL_LOCK et watchdog_lock_timer_val()
  • L'extinction du micro-contrôleur (Deep Power Down) : WDT_POWER_DOWN_LOCK et watchdog_disable_power_down()

Chacun des verrous ci-dessus empêche la modification du paramètre protégé par le verrou, ou l'arrêt du micro-contrôleur pour le cas du WDT_POWER_DOWN_LOCK.

La fonction watchdog_lock_full() permet la mise en place de la totalité des verrous supportés par le micro-contrôleur.

Utilisation (Feed the dog)

void watchdog_feed(void);

Pour que le timer du Watchdog n'expire pas, il faut le ré-initialiser régulièrement, avec une procédure particulière.

Cette procédure est implémentée dans la fonction watchdog_feed(), qui doit être appelée au cours du programme, dans la boucle principale (et ailleurs si-besoin), de façon à ce que le timer soit ré-initialisé à chaque itération de la boucle principale.

Il est fortement déconseillé de mettre cet appel de fonction dans les boucles qui risqueraient de bloquer indéfiniment le reste du programme (autres que la boucle principale), puisque le chien de garde est justement fait pour redémarrer le micro-contrôleur si un tel cas se produit.

Pour éviter de rester indéfiniment en attente d'un événement en bloquant le reste du programme il est possible de fonctionner soit sur interruption dans le cas d'un besoin de traitement très rapide, soit sur exécution périodique (voir la page Systick), soit par un test inclus dans la boucle principale du programme.

Spécificités par micro-contrôleur

LPC82x

LPC11A04

LPC122x

LPC176x