ZYNQ的AMP模式下,一种可以在线调试的重启CPU1的方法

问题背景

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

你可能感兴趣的:(zynq)