The watchdog
Objectives
Section titled “Objectives”By the end of this lesson, you will be able to
- Explain what a watchdog does, and what happens if it isn’t fed within its set time.
- Identify the correct place to feed a watchdog in a multi-task program, and explain why feeding it from a timer’s ISR is a trap.
- Design reset-cause logging so you can tell when the watchdog fired.
Takes about 70 minutes (concepts 15 · practice 25 · lab 25 · check 5).
Before you start
Section titled “Before you start”Two review questions from earlier lessons.
- The SDK’s radar stall watchdog reports 0 stalls even while its task is stuck — where does it sit that causes this (lesson 3.2)?
- Does a timer’s ISR still run if every task is stuck in an endless loop (lessons 4.1 and 4.2)?
See it work first
Section titled “See it work first”Open examples/09_watchdog_sim.c. This simulated system has three tasks and a watchdog that resets when it goes unfed for more than 10 ticks. At tick 40, the sensor task hangs, and the example tries two strategies: A feeds from a timer’s ISR every tick; B has a supervisor feed it only once every task has reported progress. Predict before you run it: at which tick does strategy A reset?
gcc -std=c11 -Wall -Wextra -o watchdog_sim examples/09_watchdog_sim.c./watchdog_simStrategy A never resets at all, even after the task has been hung for 60 ticks, because the timer’s ISR neither knows nor cares what state the tasks are in. Strategy B resets within the set time, and can even say which task is missing. A watchdog fed from the wrong place is barely better than no watchdog at all.
Concepts
Section titled “Concepts”1. What a watchdog is, and how it works on PSOC™ Edge
Section titled “1. What a watchdog is, and how it works on PSOC™ Edge”A watchdog is a counter running on its own clock, separate from the CPU. Software must “feed” (clear) it periodically; if it isn’t fed before it reaches its set value, it resets the whole chip. The idea: if software can still feed it, it’s still running; if it can’t, it’s hung, and starting over is better than staying hung forever.
The PDL has a WDT driver in cy_wdt.h. This board’s BSP declares CY_IP_MXS22SRSS (cy_device_headers_ns.h), which enables using the PDL’s match-value style function set. The setup order per the driver’s documentation is:
| Step | Function |
|---|---|
| Unlock, then disable before changing it | Cy_WDT_Unlock() then Cy_WDT_Disable() |
| Choose the clock source (for MXS22SRSS, either PILO or clk_bak) | Cy_WDT_SetClkSource() |
| Set the timeout | Cy_WDT_SetMatch(), and Cy_WDT_SetIgnoreBits() if you need it shorter |
| Enable, then lock to prevent accidental changes | Cy_WDT_Enable() then Cy_WDT_Lock() |
| Feed it | Cy_WDT_ClearWatchdog(), whose documentation says “Clears (“feeds”) the watchdog, to prevent a XRES device reset” |
A warning from the PDL’s documentation, worth knowing before enabling this: the watchdog’s timeout must be longer than the chip’s boot time (“a WDT reset can be generated faster than a device start-up”), a changed value takes effect a couple of low-frequency clock cycles late, and a low-accuracy oscillator needs a generous margin (the documentation cites an ILO on another chip family that can be off by ±30%). For this chip’s PILO or clk_bak accuracy, check the chip’s datasheet, and remember that during debugging, halting the CPU at a breakpoint can cause the watchdog to reset partway through (lesson 3.1).
Status in the SDK’s template: we searched the mtb-only template’s source at this commit and found no code that enables the hardware watchdog (Cy_WDT_Enable or Cy_WDT_ClearWatchdog). The only call present is Cy_WDT_Unlock(), inside the BSP-generated clock setup code (cycfg_clocks.c line 584). What the SDK actually uses is one task’s software watchdog, which is the next topic.
2. Where to feed it: wherever progress is actually proven
Section titled “2. Where to feed it: wherever progress is actually proven”A watchdog can only check what “whoever feeds it” knows. If it’s fed from a timer’s ISR, that only proves the timer’s interrupt is still firing — which is usually still true even after every task has died. If each task feeds it itself, then whichever task happens to survive keeps feeding on behalf of the ones that died. The pattern that actually works is checking in with a single supervisor.
- Each important task sets its own bit when it makes real progress (successfully reads a sensor, successfully sends a message) — not just when it wakes up.
- A low-priority supervisor task checks periodically, feeds the watchdog only once every bit is set, then clears all the bits.
- The watchdog’s timeout is longer than the slowest task’s round, plus margin.
A low-priority supervisor also catches another symptom for free: a high-priority task stuck in a loop that never yields the CPU. The supervisor never gets to run, the watchdog never gets fed, and it resets.
The SDK has a real software-watchdog example: 08_deepcraft_link.c explains that the model link’s task was once found “asleep inside its own queue receive with a command outstanding” after switching models hundreds of times. deepcraft_task_watchdog() must be called at about 1 Hz “from a context that never blocks,” and it detects the stuck work and wakes the task to continue. The same comment admits plainly that “The watchdog does not fix the underlying fault” — a watchdog is recovery, not a bug fix, and every time it fires must be logged.
3. Logging the reset cause: knowing when the watchdog fired
Section titled “3. Logging the reset cause: knowing when the watchdog fired”A watchdog that resets silently makes a system look like it “sometimes just reboots on its own.” What’s needed is a record that survives across the reset.
- Read the cause.
Cy_SysLib_GetResetReason()returns a bitset, such asCY_SYSLIB_RESET_HWWDT(0x0001),CY_SYSLIB_RESET_SOFT(0x0010), andCY_SYSLIB_RESET_SWWDT0..3for the multi-counter watchdog (see the table incy_syslib.h). Test it with&, since several bits can be set at once. The SDK’s own BSP uses this function while setting up clocks, and annotates that a value of 0 means “POR, XRES, or BOD” (cycfg_clocks.c lines 571-575). After recording it, callCy_SysLib_ClearResetReason(), or the next boot will see the same old cause. - Keep a trace in RAM that survives boot without being zeroed. The SDK’s linker script has a
.noinitsection that start-up code never touches (pse84_ns_cm33.ld lines 339-343). The supervisor writes “which task hasn’t checked in” into it every round, so after a reset you know who was stuck. Right after power-up, whatever’s in that RAM is garbage, so you need a magic number to tell the two apart. The template’s CM55 side follows the same principle — it writes a fault marker to a fixed SRAM address, commented “survives until power cycle.” - Export it. Print it at boot, count occurrences, and if there’s connectivity, send it to a log-collecting system.
Worked example
Section titled “Worked example”examples/09_watchdog_sim.c runs in three parts.
- Part 1: each task runs one round then checks in by setting its bit (the
sensortask hangs starting at tick 40). - Part 2: strategy A feeds every tick regardless of state; strategy B feeds once every bit is set, then clears them.
- Part 3: a simulated watchdog keeps counting, and if it times out, prints that it reset, naming the task that didn’t check in.
Try changing things and predicting the result before you run it.
- In strategy B, remove the line
checkin = 0u;. Does the watchdog still catch the hang? Why? - Change
WDT_TIMEOUT_TICKSto 1. What does strategy B do? What does that tell you about choosing a watchdog’s timeout? - Make the
uitask only make progress every 5 ticks (checking in every 5 ticks), then find the smallestWDT_TIMEOUT_TICKSthat doesn’t reset incorrectly.
Practice
Section titled “Practice”Open practice/09_watchdog.c. There are 5 gaps to fill in: a check-in supervisor, and boot logging that survives across a reset.
wd_checkin()sets a task’s bit.supervisor_poll()does not feed while any task is still missing.supervisor_poll()clears the bits after feeding.boot_record_update()tells garbage in RAM apart using a magic number.boot_record_update()counts watchdog resets and records which task was missing.
gcc -std=c11 -Wall -Wextra -o watchdog practice/09_watchdog.c && ./watchdogSolution
Section titled “Solution”Try it yourself for at least 15 minutes first, then open solution/09_watchdog.c. Two things worth comparing: the solution tests the reset cause with &, not ==, and a comment warns that |= inside wd_checkin(), when called from several tasks at once on a real board, needs a critical section or an FreeRTOS event group instead — knowledge from lesson 1.3 turning up in an unexpected place.
Check your understanding
Section titled “Check your understanding”Answer the 5 questions in quiz.yaml (shown at the bottom of this page on the website), covering all three objectives. Getting 4 or more right counts as finishing the lesson.
Task: read a real board’s reset cause across several scenarios, and design watchdog feeding for the template.
- In your own project (on a new branch, per lesson 2.3), add these three lines to
proj_cm33_ns/main.c, right afterinit_retarget_io();(the SDK’s B1 documentation says the UART is ready from this point):Build and flash it.uint32_t reset_reason = Cy_SysLib_GetResetReason();printf("[RST] reason=0x%08lx%s\r\n", (unsigned long)reset_reason,(reset_reason & CY_SYSLIB_RESET_HWWDT) ? " HWWDT" : ""); - Record the printed value across four scenarios: unplugging and replugging, pressing the board’s reset button (if it has one), right after
make program, and unplugging and replugging again afterward. Which values are 0? Which have bits set? Does a bit persist across a reset? (This code doesn’t clear the cause yet — that’s part of what you should notice.) - Compare the values you got against the table in
cy_syslib.h. Which match what you expected, and which don’t? Write down what you actually observed, separately from what you inferred. - Design (on paper) enabling the hardware watchdog for the template: name at least three tasks that would need to check in, from the tasks the template creates (see
main()in proj_cm33_ns/main.c), the slowest round for each, the watchdog timeout you’d choose with margin, and where the supervisor would sit. (This course has not yet tested enabling the hardware watchdog on this template — if you try it for real, do it on a separate branch and record everything you find, including any effect on debugging.)
Evidence to keep in your portfolio: the diff of your change to main.c, a table of the reset-cause values across all four scenarios with explanations, and your watchdog design draft from step 4.
Going further
Section titled “Going further”- Read the Functional Description, Clearing WDT and Reset Detection sections in the header of
cy_wdt.h, and calculate how often you’d need to feed it using the documentation’s example match value. - Compare a windowed watchdog (which also forbids feeding it too soon) against an ordinary one, in Watchdog timer (Wikipedia). What extra kind of bug does a windowed watchdog catch?
Next lesson: lesson 4.4, DMA
Reflect
Section titled “Reflect”- Which system you’ve used before “sometimes just reboots on its own” — what would you now ask its developers?
- If a watchdog makes a product look like it never hangs, how would the team ever find out there’s a bug that still needs fixing?
References
Section titled “References”- SDK: cm55/edge_ai/08_deepcraft_link.c (deepcraft_task_watchdog)
- SDK: the BSP’s cycfg_clocks.c (using Cy_SysLib_GetResetReason and Cy_WDT_Unlock)
- Infineon mtb-pdl-cat1 (Peripheral Driver Library) @ release-v3.24.0
- Watchdog timer (Wikipedia)
Review questions
Answer on your own first, then open the answer.
-
If the firmware does not feed the hardware watchdog in time, what happens? (Objective 1)
- watchdog พิมพ์ข้อความเตือนลง console แล้วรอต่อ
- ชิปถูกรีเซ็ต และสาเหตุถูกบันทึกให้อ่านได้ด้วย Cy_SysLib_GetResetReason() หลังบูต
- task ที่ค้างถูกลบทิ้งอัตโนมัติ ส่วนที่เหลือทำงานต่อ
- ไม่มีอะไรเกิดจนกว่าจะถอดไฟ
Show answer
Answer: B. ชิปถูกรีเซ็ต และสาเหตุถูกบันทึกให้อ่านได้ด้วย Cy_SysLib_GetResetReason() หลังบูต
watchdog รีเซ็ตทั้งชิป ไม่เลือก task เอกสาร cy_wdt.h แนะนำให้ใช้ Cy_SysLib_GetResetReason() ตรวจว่า WDT เป็นสาเหตุหรือไม่ (บิต CY_SYSLIB_RESET_HWWDT)
-
Which cautions from the PDL docs apply when choosing a watchdog timeout? (choose all that apply) (Objective 1)
- เวลาของ watchdog ต้องยาวกว่าเวลาบูตของชิป
- oscillator ความถี่ต่ำอาจคลาดได้มาก ต้องเผื่อ margin
- การเปลี่ยนค่าของ WDT มีผลทันทีในคำสั่งถัดไปเสมอ
- ยิ่งสั้นยิ่งดี เพราะจับการค้างได้เร็ว
Show answer
Answer: A. เวลาของ watchdog ต้องยาวกว่าเวลาบูตของชิป · B. oscillator ความถี่ต่ำอาจคลาดได้มาก ต้องเผื่อ margin
เอกสารเตือนว่า WDT reset อาจเกิดเร็วกว่าเวลาบูต ความแม่นยำของ oscillator ต้องเผื่อ และการเปลี่ยนค่ามีผลช้าไปสองสามจังหวะ เวลาที่สั้นเกินจะทำให้รีเซ็ตผิดเมื่อระบบแค่ช้า
-
Why is feeding the watchdog from a timer ISR a trap? (Objective 2)
- เพราะ ISR ป้อน watchdog ไม่ได้ทางเทคนิค
- เพราะ ISR ของ timer ยังทำงานแม้ task ทุกตัวค้าง watchdog จึงไม่เคยรีเซ็ตทั้งที่ระบบตายแล้ว
- เพราะทำให้ timer ช้าลง
- เพราะ watchdog จะรีเซ็ตทุกครั้งที่ interrupt เกิด
Show answer
Answer: B. เพราะ ISR ของ timer ยังทำงานแม้ task ทุกตัวค้าง watchdog จึงไม่เคยรีเซ็ตทั้งที่ระบบตายแล้ว
คนป้อนต้องพิสูจน์ว่าระบบคืบหน้า ISR ของ timer พิสูจน์ได้แค่ว่า interrupt ยังทำงาน การจำลองในบทนี้แสดงว่ากลยุทธ์นี้ไม่รีเซ็ตเลยแม้ task ค้างไป 60 tick
-
Order one round of the check-in supervisor pattern. (Objective 2)
- ผู้ดูแลล้างบิตทั้งหมดสำหรับรอบหน้า
- แต่ละ task ตั้งบิตของตัวเองเมื่อทำงานคืบหน้าจริง
- ผู้ดูแลเรียก Cy_WDT_ClearWatchdog()
- ผู้ดูแลตรวจว่าบิตของทุก task ครบ
Show answer
Correct order: B. แต่ละ task ตั้งบิตของตัวเองเมื่อทำงานคืบหน้าจริง → D. ผู้ดูแลตรวจว่าบิตของทุก task ครบ → C. ผู้ดูแลเรียก Cy_WDT_ClearWatchdog() → A. ผู้ดูแลล้างบิตทั้งหมดสำหรับรอบหน้า
task รายงานความคืบหน้า ผู้ดูแลตรวจว่าครบ ป้อน แล้วล้าง ถ้าไม่ล้าง บิตเก่าจะป้อนแทน task ที่ตายไปแล้วตลอดไป
-
Designing so you know when the watchdog reset and which task caused it — which should you do? (choose all that apply) (Objective 3)
- อ่าน Cy_SysLib_GetResetReason() ตอนบูต ทดสอบบิต CY_SYSLIB_RESET_HWWDT ด้วย & แล้วล้างด้วย Cy_SysLib_ClearResetReason()
- ให้ผู้ดูแลเขียน task ที่ยังไม่ check in ลงบันทึกใน section .noinit ทุกรอบ พร้อม magic number
- เก็บบันทึกไว้ในตัวแปร .bss ธรรมดา เพราะ startup ไม่แตะมัน
- นับจำนวนครั้งที่ watchdog รีเซ็ต แล้วพิมพ์หรือส่งออกตอนบูต
Show answer
Answer: A. อ่าน Cy_SysLib_GetResetReason() ตอนบูต ทดสอบบิต CY_SYSLIB_RESET_HWWDT ด้วย & แล้วล้างด้วย Cy_SysLib_ClearResetReason() · B. ให้ผู้ดูแลเขียน task ที่ยังไม่ check in ลงบันทึกใน section .noinit ทุกรอบ พร้อม magic number · D. นับจำนวนครั้งที่ watchdog รีเซ็ต แล้วพิมพ์หรือส่งออกตอนบูต
.bss ถูกล้างเป็นศูนย์ตอนบูต บันทึกจึงหาย ต้องใช้ .noinit ที่ linker script ของ SDK เตรียมไว้ และต้องมี magic เพราะหลังเปิดไฟครั้งแรกค่าใน .noinit เป็นขยะ สาเหตุการรีเซ็ตเป็นชุดบิตจึงทดสอบด้วย &
Cite this lesson
If you teach from this lesson or reuse it in slides or documents, credit it with the text below. If you changed it, add (adapted) after the title.
"The watchdog" from TESA Open Knowledge by the Thai Embedded Systems Association (TESA), https://github.com/tesaiot/tesa-qualification-program, licensed under CC BY-NC 4.0
Thai attribution: "Watchdog" จาก TESA Open Knowledge โดยสมาคมสมองกลฝังตัวไทย (Thai Embedded Systems Association: TESA) https://github.com/tesaiot/tesa-qualification-program สัญญาอนุญาต CC BY-NC 4.0
Lesson link: https://tesaiot.github.io/tesa-qualification-program/en/courses/embedded-c-foundations/m04-peripherals/l03-watchdog/
This lesson adapts the source below; keep its credit too.
https://github.com/tesaiot/tesaiot-pse84-devkit-sdk/tree/ef72c1b658178eee8c38b1e47d28b006f80a59b5 · SDK examples and docs are linked at this commit, not copied into this course. Lessons quote short excerpts (at most 25 lines) with a link to the file at this commit and the credit (Apache-2.0, tesaiot-pse84-devkit-sdk).
TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · CC BY-NC 4.0
Content is licensed CC BY-NC 4.0. Reuse it non-commercially and credit the Thai Embedded Systems Association (TESA) every time. · How to cite TESA