Introduction
Today’s Embedded systems have become quite complex, often containing multiple processors and software. As such, the risk of a system locking up becomes more of a concern. To that end, watchdog timer peripherals have become an integral part of these systems. A hardware watchdog timer is a type of timer that, once enabled, requires regular “kicking” or “petting” (resetting the count) to prevent it from triggering a system reset when the timer expires.
The Zynq Ultrascale+ MPSoC, embracing this paradigm, integrates three distinct internal watchdog timers. The first timer is dedicated to the CSU (boot and device security), the second to the FPD (full power domain) including the APU, and the third to the LPD (low power domain) including the RPU. In this article, we will delve into the configuration and enablement of the FPD and LPD watchdog timers within your heterogeneous embedded system.
The Challenge
While the watchdog timers on the ZU+ resemble those found in many SoCs, their implementation differs slightly due to the presence of the PMU (platform management unit). The PMU handles system-level tasks like power management and error detection, among other functions. Unlike other SoCs, the FPD and LPD watchdog timers on the ZU+ do not directly trigger a system reset. Instead, they generate error interrupts on the PMU, which then determines the appropriate action. The PMU firmware is responsible for carrying out a specific action, such as resetting the system, when a watchdog timer times out.
This design nuance is not immediately evident, especially for those unfamiliar with the ZU+ architecture. Additionally, the default PMU ROM code and stock PMU firmware lack this functionality. To enable it, you must include the “Error Management” module of the PMU firmware. This can lead to confusion for users who expect the hardware watchdog to reset the system automatically.
To enable these watchdog features, we will begin by activating error management in the PMU firmware. This ensures that a watchdog timeout triggers a system reset. While other error handling actions exist, we will focus on the traditional watchdog method of resetting the system for simplicity.
Enabling PMU Error Management
In order to enable this functionality, we must enable two preprocessor symbols for the PMU firmware: ENABLE_EM ENABLE_SCHEDULER
and ENABLE_SCHEDULER
. This is documented in the Xilinx wiki. While PMU firmware can be compiled in Vitis, we will assume for simplicity that the user will be enabling it in PetaLinux here. As such, we can simply make a bbappend in our layer for the pmu-firmware recipe:
# project-spec/meta-user/recipes-bsp/embeddedsw/pmu-firmware_%.bbappend
YAML_COMPILER_FLAGS:append = " -DENABLE_EM -DENABLE_SCHEDULER"
After compiling PetaLinux again, the generated PMU firmware will contain the necessary functionality to generate a system reset upon either FPD or LPD watchdog timeout.
Next, custom software is required to enable these features.
FPD Watchdog
Since the FPD includes the APU, this watchdog is typically being configured and controlled within Linux on the APU. The device has a dedicated Linux driver and is configured via device tree. Typically, the PCW (pin configuration wizard) device tree generation scripts are going to handle enabling this automatically in the generated .dtsi files. However, for reference, the complete device tree snippet needed for this watchdog is below:
watchdog0: watchdog@fd4d0000 {
compatible = "cdns,wdt-r1p2";
status = "okay";
interrupt-parent = <&gic>;
interrupts = <0 113 1>;
reg = <0x0 0xfd4d0000 0x0 0x1000>;
timeout-sec = <60>;
reset-on-timeout;
};
The “reset-on-timeout” is needed to configure the watchdog to generate a reset signal (which is routed to the PMU) rather than simply an interrupt (which allows for software intervention by the CPU).
To determine if this is working, you can do something as follows in the Linux system, and after 60 seconds, the system should reset:
echo 1 > /dev/watchdog0
For complete control of the watchdog timer in Linux, please see our blog article “Leveraging Systemd for Hardware Watchdog Control in Embedded Linux.”
LPD Watchdog
The LPD includes the RPU, and thus, this watchdog is typically being configured and controlled by the RPU in either bare-metal or RTOS software. This watchdog instance can also be configured and controlled in Linux. However, we will be showing how to control it with the RPU. By default, and at the time of writing this post, the device tree generator appears to enable this in Linux by default. Thus it may be necessary to override this in your system-user.dtsi file with statement like the following:
&lpd_watchdog {
status = "disabled";
};
Then, within your RPU code, you can use something like the following to configure, enable and “pet” the LPD watchdog in your system:
#include <stdio.h>
#include "xil_printf.h"
#include "xparameters.h"
#include "xwdtps.h"
#define WDT_CLK_FREQ XPAR_PSU_WDT_0_WDT_CLK_FREQ_HZ
#define WDT_DEVICE_ID XPAR_PSU_WDT_0_DEVICE_ID
#define WDT_EXPIRE_TIME 60
static XWdtPs Watchdog;
u32 ConvertTime_WdtCounter(u32 seconds)
{
double time = 0.0;
double CounterValue;
u32 Crv = 0;
u32 Prescaler,PrescalerValue;
Prescaler = XWdtPs_GetControlValue(&Watchdog, XWDTPS_CLK_PRESCALE);
if (Prescaler == XWDTPS_CCR_PSCALE_0008)
PrescalerValue = 8;
if (Prescaler == XWDTPS_CCR_PSCALE_0064)
PrescalerValue = 64;
if (Prescaler == XWDTPS_CCR_PSCALE_4096)
PrescalerValue = 4096;
time = (double)(PrescalerValue) / (double)WDT_CLK_FREQ;
CounterValue = seconds / time;
Crv = (u32)CounterValue;
Crv >>= WDT_CRV_SHIFT;
return Crv;
}
int main(void)
{
init_platform();
u32 Status = XST_SUCCESS;
XWdtPs_Config *ConfigPtr; /* Config structure of the WatchDog Timer */
u32 CounterValue = 1;
ConfigPtr = XWdtPs_LookupConfig(WDT_DEVICE_ID);
Status = XWdtPs_CfgInitialize(&Watchdog,
ConfigPtr,
ConfigPtr->BaseAddress);
if (Status != XST_SUCCESS) {
xil_printf("Watchdog Driver init Failed \n\r");
return XST_FAILURE;
}
/*
* Setting the divider value
*/
XWdtPs_SetControlValue(&Watchdog,
XWDTPS_CLK_PRESCALE,
XWDTPS_CCR_PSCALE_4096);
/*
* Convert time to Watchdog counter reset value
*/
CounterValue = ConvertTime_WdtCounter(WDT_EXPIRE_TIME);
/*
* Set the Watchdog counter reset value
*/
XWdtPs_SetControlValue(&Watchdog,
XWDTPS_COUNTER_RESET,
CounterValue);
/*
* enable reset output, as we are only using this as a basic counter
*/
XWdtPs_EnableOutput(&Watchdog, XWDTPS_RESET_SIGNAL);
/*
* Start the Watchdog timer
*/
XWdtPs_Start(&Watchdog);
XWdtPs_RestartWdt(&Watchdog);
while(1) {
sleep(1);
/* Pet the watchdog */
XWdtPs_RestartWdt(&Watchdog);
}
return XST_SUCCESS;
}
You can test this by either pausing the execution with JTAG, or commenting out the XWdtPs_RestartWdt call within the while loop. After 60 seconds, the watchdog should reset the system.
Summary
In this article, we discussed the importance of using watchdog timers, how they are implemented in the Zynq Ultrascale+, and finally how to realize these timers within your heterogeneous application in both Linux and bare metal. We uncovered a non obvious nuance of Xilinx’s implementation of the watchdog timers in the system which often becomes a stumbling block for new users to this platform.
We hope you have found this article insightful and informative. Feel free to ask any questions below. If you or your team face challenges in implementing a robust watchdog solution, Cornersoft Solutions can assist and guide you through the process. Contact us today to schedule a complimentary consultation and learn how we can help your team design and implement a more robust embedded system!