Skip to content

Robust Design Patterns - Part 1

Introduction

In this codelab, we address the problem of unresponsive applications. Unresponsive is referring to the situation a program installed on an MCU becomes unavailable due to various reasons (e.g. gobbler task, dead-lock, …).

What you’ll build

In this codelab, you will reuse the application used in previous codelabs (or that of the project) and add watchdog to it so as to ensure that we “always” return to a known, running state no matter what the issue in the SW is.

What you’ll learn

  • What type of Wathdogs the STM32 platform offers and know what their main differences are.
  • How to dimension and implement a Watchdog - the solution of last resort for safe and reliable systems.
  • The inner working of watchdog in the Cortex-M4 we are using as target.
  • How to make visible an idle system… 🙃

What you’ll need

Introduction to STM32 Watchdog

In the first part of the codelab, we learn about what watchdog options on our board there are. The board we are using for this course (STM32L475VG), offers an independent watchdog called IWDG and is used to detect and resolve malfunctions due to software failures. It functions by triggering a reset sequence when it is not refreshed within the expected time-window. This solution offers a wide range of timeout values: from 125 microseconds to 32 seconds.
Since IWDG clock is an independent 32-kHz low-speed internal RC oscillator (LSI), it remains active even if the main clock fails. Once enabled, it forces the activation of the low-speed internal oscillator, and it can only be disabled by a reset.

There exists a different type of watchdog on the same board called WWDG (Window Watchdog). You will find the official presentation here.

Questions

Can you explain :

  1. what the main differences between IWDG and WWDG are?
  2. what the rationale for using “IWDG” in this lab is? (hint: read below)
  3. how to avoid a reset while in debug and having WWDG activated?

Calculating the dimensioning of the watchdog

So, this first part of the codelab, we calculate the refresh period of the watchdog. That is, the maximum time allowed without a refresh from the application prior to issuing a reset. In this calculation the assumption is that the idle task shall run at least every second engine task period (see Phase A - Periodic tasks) because at design it was calculated that the system shall have spare capacity. So, not being able to run the idle task means that there is no such spare capacity and this is deemed an unsustainable situation.

The corresponding Block Diagram of the IWDG is the following:

The formula for calculating the maximum time allowed without a refresh is the following: \(time_{IWDG} = time_{LSI} * 4 * 2^{IWDG\_PR} * (RL_{reg} + 1)\)

where LSI clock is \(32 kHz\) (thus \(time_{LSI} = 1/32000 seconds\)), the IWDG_PR prescaler register can divide the LSI clock frequency by 4 up to 256. The watchdog counter reload value (\(RL_{reg}\)) is a 12-bit value written in the IWDG_RLR register.

Questions

First of all :

  • calculate the parameters of the watchdog for complying to above stated timing requirements

Secondly, although a watchdog is very important, it can be a burden while debugging as it triggers resets at speeds the human being cannot handle (in the milliseconds - single digit seconds range). So :

  • How could we possibly tackle this?

Note

With the present solution it is assumed that the application cannot
withstand a situation in which the idle task is not served for a whole period as this would imply the system has no free capacity left and at design this was deemed impossible. Obviously, this is not a case that will be applied oftentimes. In fact, the watchdog is more often used as a solution of last resort - thus its application will be scrutinized carefully and resulting in slower triggering of such measure. For instance applying other measures first to recover from the situation prior to resetting the system.

Implementing a watchdog

First of all, we will add a visual help to make sure our idle task runs for good. In order to do so, you will replace the function osRtxIdleThread (found normally under RTE/CMSIS/RTX_Config.c) by placing a new function in one of the files you created with the following content:

__NO_RETURN void osRtxIdleThread (void *argument) {
  (void)argument;

  static int active_flag = 1;
  for (;;) {
      if (active_flag) {
        vioSetSignal(vioLED0, vioLEDoff);    // Switch LED0 off
    }
      else {
        vioSetSignal(vioLED0, vioLEDon);     // Switch LED0 on
    }
      active_flag = ! active_flag;
      busy_wait_ms(50MS_DELAY);              // wait at least 50 ms

  }
}
From this point onwards, every time the idle task is active, you should be able to spot it as the led blinks 😎.

Warning

In order to make it work, you obviously need to :

  • define the constant 50MS_DELAY
  • add the necessary #include references as applicable

Question

What does __WEAK in __WEAK uint32_t osRtxErrorNotify mean?

So, with this change you can see the idle task running. Now, we want to add the watchdog to the application. For doing so, we first need to activate the IWDG component. This is done by :

  • activating the environment under “Manage Run-Time Environment”/”Device”/”STM32Cube HAL
  • ensure that the file RTE_Components.h has a #define RTE_DEVICE_HAL_IWDG active
  • add the activation of the watchdog to your main.c with the help of a local function System_IWDG_Init :
    static void System_IWDG_Init(void) {
      IWDG_HandleTypeDef hiwdg;

      hiwdg.Instance = IWDG;
      hiwdg.Init.Prescaler = IWDG_PRESCALER_32;
      hiwdg.Init.Reload    = 4095;
      hiwdg.Init.Window    = 4095;
      if (HAL_IWDG_Init(&hiwdg) != HAL_OK)
      {
        startup_error_handler();
      }
    }

Note

Should the different IWDG functions (e.g.HAL_IWDG_Init) not be available, make sure HAL_IWDG_MODULE_ENABLEDis enabled under RTE\Device\STM32L475VGTx\STCubeGenerated\Core\Inc\stm32l4xx_hal_conf.h.

Warning

You should use the above Reload and Prescaler values to ensure you see the behaviour of the watchdog once activated. However, remember to replace the values of Reload and Prescaler with those of your choice once ready for the real thing!

  • and finally adding the activation of IWDG shortly before the call to osKernelStart. In order to be able to see when it goes over reset (without needing an oscilloscope 😉), I suggest adding a waiting period so that visually the reset can be identified through lack of LED blinking like this :
   /* Start application watchdog */
   /* Long wait to realize, optically, that the board went over */
   /* reset */
   busy_wait_ms(500);
   System_IWDG_Init();

Questions

Now all has been setup for having a supervision of the application. One thing is still outstanding for avoiding troubles though! 😤

You need to implement the refresh of the watchdog - through HAL_IWDG_Refresh - so as to avoid going over reset on a regular basis like now. WARNING: your idle thread may be run later than thought…

Well, there is one more: deactivate the watchdog while debugging. But you found the response a few lines above… 😉

Going beyond