We are having issues with the watchdog on our S32K146
We have a bootloader that very early on does the following (but in assembler)
In our main code we change the timeout to different values. Sometimes the write to TOVAL does not work and it keeps its old value.
Our testers were using a Lauterbach emulator and were trying to trace on writes to our watchdog kick function and the reset handler. As part of the test they broke at the kick and jumped over it (by changing PC) and expected to show the CPU reset by tracing/executing Reset_Handler. The CPU reset did happen but after about 100ms instead of the expected 31.25us. We did some debugging and found that TOVAL was not being changed from the long startup duration to the short normal operations duration. The watchdog was still working in that it reset the CPU but was far too slow (we have a need to reset very quickly on infinite loops). I then moved to using a PE micro debugger as it was usable in the S32DS debugging environment. This had the same problem, so it was not the Lauterbach environment.
During debugging with the PE micro debugger I noticed that if I ran some code things worked fine (apart from the wrong TOVAL value), but if I did a STEP OVER on function calls I would get a CPU reset. If I did a STEP INTO on the same function call I could execute all of the function, return back and continue further. This meant that STEP OVER should have run quicker than STEP INTO so it should not have CPU reset. That was weird.
We then also noticed that if we called our WdogInit(7) then on exit of that function we could read TOVAL as 7. However, if we called it with WdogInit(6) it maintained its old value. The function would unlock WDOG, wait until CS.ULK == 1, set WDOG.TOVAL = <parameter>, set WDOG.CS = CMD32EN(1) | CLK(1) | EN(1) | UPDATE(1), wait until CS.RCS == 1, then return.
We found that passing a paramter value of 7..1000+ worked but passing 1..6 did not work. That was weird.
Unfortunately our required timing is TOVAL = 4. This is why our testers spotted the problem.
I removed the bootloader TOVAL setting above and wrote the following test program and ran it on an S32K146EVB-Q144 board.
The first time through the loop it does not setup the WDOG (setups==0) so it records the initial watchdog settings. The second time through the loop is where the WDOG is setup.
#include "COMMON.h"
#include "HW_REGS.h"
typedef struct
{
uint32_t setups;
uint32_t timeout_count;
uint32_t loops;
} info_t;
typedef struct
{
bool failure;
uint32_t cvr;
uint32_t cs;
uint32_t toval;
uint32_t cnt;
uint32_t cvr2;
uint32_t cnt2;
} results_t;
#define MAX_INDEX (6)
static const info_t INFO_c[MAX_INDEX] =
{
{ .setups = 0, .timeout_count = 65535, .loops = 0 }, /* don't setup, get initial value results */
{ .setups = 1, .timeout_count = 40, .loops = 99 },
{ .setups = 1, .timeout_count = 10, .loops = 10 },
{ .setups = 1, .timeout_count = 7, .loops = 2 },
{ .setups = 1, .timeout_count = 6, .loops = 0 },
{ .setups = 1, .timeout_count = 4, .loops = 0 },
};
static results_t results_m[MAX_INDEX];
static const uint32_t CONTROL_c = WDOG_CS_CMD32EN(1u) /* 32-bit keyword control */
| WDOG_CS_CLK(1u) /* Clock Source 01 (128KHz LFO) */
| WDOG_CS_EN(1u) /* Wdog Enabled */
| WDOG_CS_UPDATE(1u);
bool started;
int main(void)
{
/* The bootloader has already performed the following
*
* WDOG.CNT = 0xD928C520 (unlock)
* WDOG.TOVAL = 0xFFFF (max timeout)
*
* Note: There was no wait for unlock, nor wait for config OK
* Section 23.5 say "the unlock sequence can be used at any time within the timeout limit to reconfigure the watchdog"
*/
/* Simulate bootloader settings */
WDOG.CNT = 0xD928C520; /* unlock */
WDOG.TOVAL = 0xFFFF; /* max timeout */
/* Kick WDOG */
WDOG.CNT = 0xB480A602;
for (uint32_t index = 0; index < MAX_INDEX; index++)
{
for (uint16_t i = 0; i < INFO_c[index].setups; i++)
{
/* Disable interrupts */
__asm__ volatile ("cpsid i" : : : "memory");
WDOG.CNT = 0xD928C520; /* unlock */
while ((WDOG.CS & WDOG_CS_ULK_MASK) == 0u)
{
/* Wait for unlock */
}
WDOG.TOVAL = INFO_c[index].timeout_count; /* Set timeout_count */
WDOG.WIN = 0;
WDOG.CS = CONTROL_c;
while ((WDOG.CS & WDOG_CS_RCS_MASK) == 0u)
{
/* Wait for config OK */
}
/* Enable interrupts */
__asm__ volatile ("cpsie i" : : : "memory");
}
/* Kick WDOG */
WDOG.CNT = 0xB480A602;
/* Read WDOG values */
results_m[index].cvr = S32_SysTick.CVR;
results_m[index].cs = WDOG.CS;
results_m[index].toval = WDOG.TOVAL;
results_m[index].cnt = WDOG.CNT;
results_m[index].failure = false;
if (INFO_c[index].timeout_count != results_m[index].toval)
{
results_m[index].failure = true;
}
for (uint32_t loop = 0; loop < INFO_c[index].loops; loop++)
{
/* Do nothing */
}
/* Read CVR and WDOG timer value to show it incrementing */
results_m[index].cvr2 = S32_SysTick.CVR;
results_m[index].cnt2 = WDOG.CNT;
}
/* Should never get here, so return non-zero value to indicate abnormal exit */
return (1);
}
The results are given in the attachment "Faulty TOVAL on results_m[1].jpg". The yellow highlight shows the incorrect TOVAL value (note also that failure==true) on the second time through the loop. The correct TOVAL should have been 40.
I then edited the code to change setups to 2 on the 2nd loop. The results are given in the attachment "Correct TOVAL on results_m[1] but only after setting it twice.jpg". The yellow highlight shows the updated setups. Now TOVAL is correctly set to 40.
I further edited the code to add "wait for CS_ULK == 1" in the "simulate bootloader settings" but it made no difference. I then added "wait for CS_RCS == 1" after setting TOVAL to 0xFFFF but this then caused a CPU reset (since the "simulate bootloader settings" does not change CS). I then added CS = CMD32EN(1) | CLK(1) | EN(1) | UPDATE(1) after setting TOVAL to 0xFFFF, so it now has the full sequence and then everything worked.
Have we mis-read section 23.5 and is setting UNLOCK, then TOVAL not acceptable? We didn't want to add waits on hardware since our standards then require software timeouts in the hardware waits.