ZYNQ-Utlscale-RFSOC看门狗复位
ZYNQ-Utlscale-RFSOC 看门狗的程序网上里程很少,开源资料也是几乎没有,最近需要用到这个功能,来来回回搞了一周才搞定。刚开始参考ZYNQ7000的资源,发现MPSOC不适用。很感谢下面的几篇文章,给我很大的帮助,我也是整合他们的代码,完成了该功能。
- 首先是调试MPSOC中RESET软复位功能,通过arm来使系统复位,重启程序。但是是调试过程中发现,如果我要加更多的程序,系统掉电之后不能重新自启动。另外,如果系统跑飞、卡死,该功能也不能使系统复位(因为代码卡在了其他部分,不能执行复位的代码)。
- 之后,我参考米联客的开发手册,调试了SWDT看门狗功能。但是该工程也只是验证、学习了看门狗的功能,并没有做到使系统复位。
- 最后,看门狗实现软件复位。感谢该文章:记录一次ZYNQ Ultrascale+ MPSoC看门狗(WDTPS)填坑心得-CSDN博客 ,这篇文章很好整理了博主在开发的过程,并且最后附上了驱动代码。我用了该文章的代码,再根据前面的学习,给出了我自己的代码。
1.RESET复位程序
参考文章:Xilinx软件开发:如何通过软件复位MPSOC_mpsoc 复位-CSDN博客
zynq mpsoc 软复位 (xilinx.com)
我的代码:
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xil_io.h"
#include "sleep.h"
#define MPSOC_RESET_REASON_REG 0xFF5E0220 //16bit
#define MPSOC_RESET_CTRL_REG 0xFF5E0218 //寄存器RESET_CTRL
#define MPSOC_RESET_VALUE 0x11 //复位bit4 (Width=8)
void PsSoftwareReset(void)
{
Xil_Out8(MPSOC_RESET_CTRL_REG, MPSOC_RESET_VALUE); //复位
}
void getResetReason(void)
{
int status;
status = Xil_In16(MPSOC_RESET_REASON_REG);
xil_printf("\n\rResetReason = 0x%.8X\n\r", status);
if(status & 0x40)
xil_printf("6 Software Debugger Reset\n\r");
if(status & 0x20)
xil_printf("5 Software System Reset\n\r");
if(status & 0x10)
xil_printf("4 External System Reset\n\r");
if(status & 0x08)
xil_printf("3 PS-only Reset\n\r");
if(status & 0x04)
xil_printf("2 Internal System Reset\n\r");
if(status & 0x02)
xil_printf("1 A system error triggered a POR reset\n\r");
if(status & 0x01)
xil_printf("0 the PS_POR_B reset signal pin was asserted\n\r");
xil_printf("\n\r");
}
int main()
{
init_platform();
xil_printf("Hello World \n\r");
getResetReason();
sleep(2);
PsSoftwareReset();
print("PsSoftwareReset\n\r");
print("Successfully ran Hello World application");
cleanup_platform();
return 0;
}
将程序固化到板子,会循环打印以下信息:
PMU-FW is not running, certain applications may not be supported.
Hello World
PMU-FW is not running, certain applications may not be supported.
该提示可不用管
观察电流或板子上的指示灯,表明芯片在重复 上电执行程序,之后掉电。
可以将PS端程序重置
2.看门狗程序测试
参考:米联客相关开发手册
zynq配置:
可以接一个ILA,观察SWDT的reset输出信号:
在vitis中,导入例程:
选择 xwdtps_polled_example
将官方程序进行以下修改:
#include "xparameters.h"
#include "xwdtps.h"
#include "xil_printf.h"
#include "sleep.h"
#define WDT_DEVICE_ID XPAR_XWDTPS_0_DEVICE_ID
int WdtPsPolledExample(u16 DeviceId);
/************************** Variable Definitions *****************************/
XWdtPs Watchdog; /* Instance of WatchDog Timer */
int main(void)
{
int Status;
xil_printf("WDT Polled Mode Example Test\r\n");
Status = WdtPsPolledExample(WDT_DEVICE_ID);
if (Status != XST_SUCCESS) {
xil_printf("WDT Polled Mode Example Test Failed\r\n");
return XST_FAILURE;
}
xil_printf("Successfully ran WDT Polled Mode Example Test\r\n");
return XST_SUCCESS;
}
int WdtPsPolledExample(u16 DeviceId)
{
int Status;
u32 ExpiredTimeDelta1 = 0;
XWdtPs_Config *ConfigPtr;
u32 EffectiveAddress; /* This can be the virtual address */
/*
* Initialize the Watchdog Timer so that it is ready to use
*/
ConfigPtr = XWdtPs_LookupConfig(DeviceId);
/*
* This is where the virtual address would be used, this example
* uses physical address.
*/
EffectiveAddress = ConfigPtr->BaseAddress;
Status = XWdtPs_CfgInitialize(&Watchdog, ConfigPtr,
EffectiveAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/*
* 设置XWDTPS_CCR_OFFSET 中XWDTPS_CCR_CRV_MASK=5,因此看门狗重启后,计数器是5FFF
*/
XWdtPs_SetControlValue(&Watchdog,
(u8) XWDTPS_COUNTER_RESET, (u8) 5);
/*
* 设置看门狗预分频器4096分频.
*/
XWdtPs_SetControlValue(&Watchdog,
(u8) XWDTPS_CLK_PRESCALE,
(u8) XWDTPS_CCR_PSCALE_4096);
/*
* 使能复位,当看门狗计数到0产生一个复位.
*/
XWdtPs_EnableOutput(&Watchdog, XWDTPS_RESET_SIGNAL);
/*
* 启动看门狗定时器.
*/
XWdtPs_Start(&Watchdog);
/*
* 重启启动看门狗,看门狗计数器会以XWDTPS_CCR_OFFSET中设置的参数0xNNNFFF开始递减计数.
*/
XWdtPs_RestartWdt(&Watchdog);
/*
* Determine how long it takes for the Watchdog Timer to expire
* for later processing.
*/
while (1) {
xil_printf("WDT Restart ExpiredTimeDelta1=%d\r\n",ExpiredTimeDelta1);
XWdtPs_RestartWdt(&Watchdog); //重启启动看门狗,看门狗计数器会以XWDTPS_CCR_OFFSET中设置的参数0xNNNFFF开始递减计数
ExpiredTimeDelta1++;
sleep(1);
if(ExpiredTimeDelta1==20) //运行20S后强制进入5S等待,这样会产生超时,触发复位 PL端产生一个rst复位信号
sleep(5);
if (XWdtPs_IsWdtExpired(&Watchdog)){
xil_printf("WDT times out ExpiredTimeDelta1=%d\r\n",ExpiredTimeDelta1);
return XST_FAILURE;
}
}
return XST_SUCCESS;
}
程序分析:
1.XWdtPs_LookupConfig
函数:
ConfigPtr = XWdtPs_LookupConfig(DeviceId);
通过 ID 值找到 SWDT 的地址参数。
XWdtPs_Config *XWdtPs_LookupConfig(u16 DeviceId)
这个函数的分析方法和前面课程中类似函数分析方法类似,目的是为了获取地址参数。这个函数负责通过 ID 值确定外设的地址空间
2.XWdtPs_SetControlValue
函数:
XWdtPs_SetControlValue(&Watchdog, (u8) XWDTPS_COUNTER_RESET, (u8) 5);
这句是为了设置SWDT的24bit计数器的值。
XWdtPs_SetControlValue(&Watchdog, (u8) XWDTPS_CLK_PRESCALE, (u8) XWDTPS_CCR_PSCALE_4096);
这句是为了 设置分频系数。
3.XWdtPs_EnableOutput
函数:
XWdtPs_EnableOutput(&Watchdog, XWDTPS_RESET_SIGNAL);
这句是为了使能当 SWDT 看门狗计数器溢出后产生一个复位。这就是我们前面实验结果用逻辑分析看到的。因为这个复位信号持续的时间很短,所以只能用在ILA抓波形。
4.XWdtPs_Start
函数:
XWdtPs_Start(&Watchdog);
语句启动 SWDT 看门狗定时器。这个函数也是操作 XWDTPS_ZMR 寄存器,设置 XWDTPS_ZMR[0]为 1,使能 SWDT 运行
5.XWdtPs_RestartWdt
函数:
XWdtPs_RestartWdt(&Watchdog);
这句设置 SWDT 重启寄存器,实现重启看门狗定时器。
6.XWdtPs_IsWdtExpired
函数:
XWdtPs_IsWdtExpired(&Watchdog);
SWDT 的状态寄存器,查看定时计数器是否减计数到 0 了,通过判断状态寄存 器可以知道看门狗 SWDT 是否溢出了。
打印的结果:
WDT Polled Mode Example Test
WDT Restart ExpiredTimeDelta1=0
WDT Restart ExpiredTimeDelta1=1
WDT Restart ExpiredTimeDelta1=2
WDT Restart ExpiredTimeDelta1=3
WDT Restart ExpiredTimeDelta1=4
WDT Restart ExpiredTimeDelta1=5
WDT Restart ExpiredTimeDelta1=6
WDT Restart ExpiredTimeDelta1=7
WDT Restart ExpiredTimeDelta1=8
WDT Restart ExpiredTimeDelta1=9
WDT Restart ExpiredTimeDelta1=10
WDT Restart ExpiredTimeDelta1=11
WDT Restart ExpiredTimeDelta1=12
WDT Restart ExpiredTimeDelta1=13
WDT Restart ExpiredTimeDelta1=14
WDT Restart ExpiredTimeDelta1=15
WDT Restart ExpiredTimeDelta1=16
WDT Restart ExpiredTimeDelta1=17
WDT Restart ExpiredTimeDelta1=18
WDT Restart ExpiredTimeDelta1=19
WDT times out ExpiredTimeDelta1=20
WDT Polled Mode Example Test Failed
当打印到19时,等待5S,再打印出
WDT times out ExpiredTimeDelta1=20
WDT Polled Mode Example Test Failed
同时,FPGA的ILA采集的SWDT产生的reset信号:
以上,看门狗功能初步实现,下面就是将复位和计数等待结合起来。
3.看门狗实现软复位
参考文章:记录一次ZYNQ Ultrascale+ MPSoC看门狗(WDTPS)填坑心得-CSDN博客
文章写的很详细,我是参考这篇文章才测试出来的。
ZYNQMP的看门狗不能直接复位PS,而是通过PMU软件对PS进行复位
测试代码如下:
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xwdtps.h"
#include "xil_io.h"
#include "sleep.h"
/************************************Macros Definitions***************************************/
#define WDTPS_DEVICE_ID XPAR_XWDTPS_1_DEVICE_ID
#define WDTPS_CLK_RATE_HZ XPAR_XWDTPS_1_WDT_CLK_FREQ_HZ
#define WDTPS_CLK_PRESCALE XWDTPS_CCR_PSCALE_4096
/************************************Variable definitions*************************************/
XWdtPs gWdtPsIns;
/*PMU_GLOBAL Base Address*/
#define PMU_GLOBAL_BASEADDR 0xFFD80000
/*Register: PMU_GLOBAL_ERROR_SRST_EN_1 */
#define PMU_GLOBAL_ERROR_SRST_EN_1 (PMU_GLOBAL_BASEADDR + 0x0000056C)
#define PMU_GLOBAL_ERROR_SRST_EN_1_LPD_SWDT_MASK 0x00001000
#define PMU_GLOBAL_ERROR_SRST_EN_1_FPD_SWDT_MASK 0x00002000
/*Register: PMU_GLOBAL_ERROR_EN_1 */
#define PMU_GLOBAL_ERROR_EN_1 (PMU_GLOBAL_BASEADDR + 0x000005A0)
#define PMU_GLOBAL_ERROR_EN_1_LPD_SWDT_MASK 0x00001000
#define PMU_GLOBAL_ERROR_EN_1_FPD_SWDT_MASK 0x00002000
/*Register: PMU_GLOBAL_ERROR_SRST_DIS_1*/
#define PMU_GLOBAL_ERROR_SRST_DIS_1 (PMU_GLOBAL_BASEADDR + 0x00000570)
#define PMU_GLOBAL_ERROR_SRST_DIS_1_LPD_SWDT_MASK 0x00001000
#define PMU_GLOBAL_ERROR_SRST_DIS_1_FPD_SWDT_MASK 0x00002000
/*Register: PERS_GLOB_GEN_STORAGE0*/
#define PMU_GLOVAL_PERS_GLOB_GEN_STORAGE0 (PMU_GLOBAL_BASEADDR + 0x00000050)
/*Register: ERROR_STATUS_1*/
#define PMU_GLOBAL_ERROR_STATUS_1 (PMU_GLOBAL_BASEADDR + 0x00000530)
#define PMU_GLOBAL_ERROR_STATUS_1_LPD_SWDT_MASK 0x00001000
#define PMU_GLOBAL_ERROR_STATUS_1_FPD_SWDT_MASK 0x00002000
static uint32_t hw_wdtps_convertTimeToCounter(XWdtPs *ins, uint32_t mseconds)
{
double time;
double counter_value;
uint32_t crv;
uint32_t prescaler;
uint32_t prescaler_value;
prescaler = XWdtPs_GetControlValue(ins, XWDTPS_CLK_PRESCALE);
if (prescaler == XWDTPS_CCR_PSCALE_0008) {
prescaler_value = 8;
} else if (prescaler == XWDTPS_CCR_PSCALE_0064) {
prescaler_value = 64;
} else if (prescaler == XWDTPS_CCR_PSCALE_0512){
prescaler_value = 512;
} else if (prescaler == XWDTPS_CCR_PSCALE_4096) {
prescaler_value = 4096;
}else{
prescaler_value = 0U;
}
time = (double)(prescaler_value) / (double)(WDTPS_CLK_RATE_HZ);
counter_value = (double)mseconds / (double)1000.0f / time;
crv = (uint32_t)counter_value;
crv >>= 12; /*see ug1085 table 14-14 */
/*CRV: Counter restart value. The counter is restarted with
0xNFFF, where N is the value of this field.*/
return crv;
}
/*init*/
int hw_wdtps_init(uint32_t expire_time_ms, uint8_t test)
{
int ret = 0;
XWdtPs_Config *cfg;
uint32_t u32tmp;
memset(& gWdtPsIns, 0, sizeof(XWdtPs));
/*Config WDT*/
cfg = XWdtPs_LookupConfig(WDTPS_DEVICE_ID);
if(NULL == cfg){
ret = -1;
goto _end;
}
ret = XWdtPs_CfgInitialize(& gWdtPsIns, cfg, cfg->BaseAddress);
if(ret != XST_SUCCESS){
ret = -1;
goto _end;
}
ret = XWdtPs_SelfTest(& gWdtPsIns);
if(ret != XST_SUCCESS){
ret = -1;
goto _end;
}
/*Clear ERROR_STATUS_1 (PMU_GLOBAL) Register*/
/*This is very important, otherwise, the CPU will be continuously reset!!!!!*/
Xil_Out32(PMU_GLOBAL_ERROR_STATUS_1, PMU_GLOBAL_ERROR_STATUS_1_FPD_SWDT_MASK); /*write 1 to clear*/
/*Disable timer*/
XWdtPs_Stop(& gWdtPsIns);
/*Set prescale*/
XWdtPs_SetControlValue(& gWdtPsIns, (uint8_t)XWDTPS_CLK_PRESCALE, WDTPS_CLK_PRESCALE);
/*Set counter reset value*/
u32tmp = hw_wdtps_convertTimeToCounter(& gWdtPsIns, expire_time_ms);
XWdtPs_SetControlValue(& gWdtPsIns, (uint8_t)XWDTPS_COUNTER_RESET, (u16)0xff);
/*Output control*/
if(! test)
XWdtPs_EnableOutput(& gWdtPsIns, XWDTPS_RESET_SIGNAL);
else
XWdtPs_DisableOutput(& gWdtPsIns, XWDTPS_RESET_SIGNAL);
/*enable generation of system reset by PMU due to SWDT0/1 */
/*Write only register*/
Xil_Out32(PMU_GLOBAL_ERROR_SRST_EN_1, PMU_GLOBAL_ERROR_SRST_EN_1_FPD_SWDT_MASK);
/*enable SWDT0/1 System Watchdog Timer Error*/
/*RW register*/
u32tmp = Xil_In32(PMU_GLOBAL_ERROR_EN_1);
u32tmp |= PMU_GLOBAL_ERROR_EN_1_FPD_SWDT_MASK; /*Full power CPU core reset*/
Xil_Out32(PMU_GLOBAL_ERROR_EN_1, u32tmp);
/*start timer*/
XWdtPs_Start(& gWdtPsIns);
XWdtPs_RestartWdt(& gWdtPsIns);
_end :
return ret;
}
/*WDT feed*/
void hw_wdtps_feed(void)
{
XWdtPs_RestartWdt(& gWdtPsIns);
}
/*Stop WDT*/
int hw_wdtps_stop(void)
{
uint32_t u32tmp;
if(gWdtPsIns.IsReady){
XWdtPs_Stop(& gWdtPsIns);
}
/*Disable generation of system reset by PMU due to SWDT0/1 */
/*Write only register*/
Xil_Out32(PMU_GLOBAL_ERROR_SRST_DIS_1, PMU_GLOBAL_ERROR_SRST_DIS_1_FPD_SWDT_MASK);
/*Disable SWDT0/1 System Watchdog Timer Error*/
/*RW register*/
u32tmp = Xil_In32(PMU_GLOBAL_ERROR_EN_1);
u32tmp &= (~ PMU_GLOBAL_ERROR_EN_1_FPD_SWDT_MASK);
Xil_Out32(PMU_GLOBAL_ERROR_EN_1, u32tmp);
return 0;
}
/*WDT test*/
void hw_wdtps_test(void)
{
int cnt = 0;
while(1){
if(XWdtPs_IsWdtExpired(& gWdtPsIns)){
xil_printf("WDT Expired\n\r");
}
//XWdtPs_RestartWdt(& gWdtPsIns);
sleep(1);
xil_printf("time : %d\n", ++cnt);
}
}
int main()
{
init_platform();
print("Hello World\n\r");
print("Successfully ran Hello World application\n\r");
hw_wdtps_init(100,0);
hw_wdtps_test();
cleanup_platform();
return 0;
}
测试结果:串口每秒打印一个数字,持续43S后,系统断电。如果把程序固化进去,那么断电后会自动重启,重复执行以上结果。
代码分析:
XWdtPs_RestartWdt(& gWdtPsIns);
是喂狗程序,如果在 hw_wdtps_test()
函数中执行此函数,那么系统将不会断电。
#define WDTPS_CLK_PRESCALE XWDTPS_CCR_PSCALE_4096
分频设置,想要改变看门狗定时器的计数时间,可以改变这个参数。
如何运用:
看门狗就是为了防止程序跑飞、卡死在循环里,一般在自己的程序main()里执行 hw_wdtps_init(100,0);
,之后XWdtPs_RestartWdt(& gWdtPsIns);
喂狗。如果程序跑飞、卡死,那么就是在规定时间内没有喂狗,系统就会复位。