ZYNQ的AMP模式开发中,常常会需要对CPU1进行software Reset ,reset之后的引导是一个比较麻烦的问题。
按照官方手册的介绍,software Reset之后,CPU1会跳到0x00地址处执行。正常工作的情况下,0x00处加载的是FSBL的代码,由BSP中的boot.s来实现CPU ID的识别和代码跳转。当需要XSDK通过JTAG在线调试时,FSBL并不会加载,此时0x00处的代码未知,若对CPU1执行Reset,将无法正确实现跳转。
按照XAPP1079的处理方式,在POR复位整个系统时,bootrom会使CPU1进入WFE状态,不断检查0xFFFFFFF0处的值是否为0,若不为0则跳转到该值指示的地址执行。所以此时只需向0xFFFFFFF0写入CPU1的代码起始地址并发送sev即可实现CPU1的重新引导。
遗憾的是,Software Reset会使CPU1直接从0x00处执行,并不会走bootrom进入WFE状态。所以思路是模仿bootrom的这一套流程,在Reset CPU1之前,通过用户代码向0x00等区域写入跳转代码,实现重启后进入WFE状态的功能。
//重启CPU1之前执行,为进入WFE做准备
void CPU1_WfePre() {
//向0xFFFFFF00起始的地址写入WFE指令
Xil_Out32(0xFFFFFF00, 0xe3e0000f);
Xil_Out32(0xFFFFFF04, 0xe3a01000);
Xil_Out32(0xFFFFFF08, 0xe5801000);
Xil_Out32(0xFFFFFF0C, 0xe320f002);
Xil_Out32(0xFFFFFF10, 0xe5902000);
Xil_Out32(0xFFFFFF14, 0xe1520001);
Xil_Out32(0xFFFFFF18, 0x0afffffb);
Xil_Out32(0xFFFFFF1C, 0xe1a0f002);
//向0x00写入跳转代码指向0xFFFFFF00
Xil_Out32(0x00000000, 0xe3e0f0ff);
//清0xFFFFFFF0
Xil_Out32(0xFFFFFFF0, 0x00000000);
dmb();
}
其中,向0xFFFFFF00处写入数据对应的指令为:
ffffff00: mvn r0, #15
ffffff04: mov r1, #0
ffffff08: str r1, [r0]
ffffff0c: wfe
ffffff10: ldr r2, [r0]
ffffff14: cmp r2, r1
ffffff18: beq -20 ; addr=0xffffff0c
ffffff1c: mov pc, r2
可见实际就是由这段代码实现的检查0xFFFFFFF0的内容并跳转
重启CPU1的代码,参考XAPP1079即可
{
/*
* Reset and start CPU1
* - Application for cpu1 exists at 0x00000000 per cpu1 linkerscript
*
*/
#include "xil_misc_psreset_api.h"
#include "xil_io.h"
#define A9_CPU_RST_CTRL (XSLCR_BASEADDR + 0x244)
#define A9_RST1_MASK 0x00000002
#define A9_CLKSTOP1_MASK 0x00000020
#define CPU1_CATCH 0x00000024
#define XSLCR_LOCK_ADDR (XSLCR_BASEADDR + 0x4)
#define XSLCR_LOCK_CODE 0x0000767B
u32 RegVal;
/*
* Setup cpu1 catch address with starting address of app_cpu1. The FSBL initialized the vector table at 0x00000000
* using a boot.S that checks for cpu number and jumps to the address stored at the
* end of the vector table in cpu0_catch and cpu1_catch entries.
* Note: Cache has been disabled at the beginning of main(). Otherwise
* a cache flush would have to be issued after this write
*/
Xil_Out32(CPU1_CATCH, APP_CPU1_ADDR);
/* Unlock the slcr register access lock */
Xil_Out32(XSLCR_UNLOCK_ADDR, XSLCR_UNLOCK_CODE);
// the user must stop the associated clock, de-assert the reset, and then restart the clock. During a
// system or POR reset, hardware automatically takes care of this. Therefore, a CPU cannot run the code
// that applies the software reset to itself. This reset needs to be applied by the other CPU or through
// JTAG or PL. Assuming the user wants to reset CPU1, the user must to set the following fields in the
// slcr.A9_CPU_RST_CTRL (address 0xF8000244) register in the order listed:
// 1. A9_RST1 = 1 to assert reset to CPU0
// 2. A9_CLKSTOP1 = 1 to stop clock to CPU0
// 3. A9_RST1 = 0 to release reset to CPU0
// 4. A9_CLKSTOP1 = 0 to restart clock to CPU0
/* Assert and deassert cpu1 reset and clkstop using above sequence*/
RegVal = Xil_In32(A9_CPU_RST_CTRL);
RegVal |= A9_RST1_MASK;
Xil_Out32(A9_CPU_RST_CTRL, RegVal);
RegVal |= A9_CLKSTOP1_MASK;
Xil_Out32(A9_CPU_RST_CTRL, RegVal);
RegVal &= ~A9_RST1_MASK;
Xil_Out32(A9_CPU_RST_CTRL, RegVal);
RegVal &= ~A9_CLKSTOP1_MASK;
Xil_Out32(A9_CPU_RST_CTRL, RegVal);
/* lock the slcr register access */
Xil_Out32(XSLCR_LOCK_ADDR, XSLCR_LOCK_CODE);
}
参考:What is the state of CPU1 after a software reset through SLCR.A9_CPU_RST_CTRL