Zynq SoC双Cortex-A9软复位后唤醒WFE模式的AMP实现问询
我之前在做Zynq AMP架构开发的时候,刚好碰到过和你完全一样的问题——软复位CPU1后怎么唤醒它的WFE状态,给你分享下我实际验证过的可行方案:
一、先明确CPU1的初始状态(对应官方文档描述)
BootROM会将CPU1置于WFE模式,此时仅维持WFE等待状态,无其他功能启用,通用寄存器只有少量被修改来保持这个状态。
这个状态下的CPU1相当于“待机休眠”,只有收到全局事件信号才会退出WFE,继续执行后续代码。
二、实现CPU1软复位的步骤
软复位CPU1需要操作Zynq的系统控制单元(SCU)寄存器,具体是CPU1 Reset Control Register,它负责触发CPU1的软复位流程:
#define SCU_BASE_ADDR 0xF8F00000 #define CPU1_RESET_REG (*(volatile unsigned int*)(SCU_BASE_ADDR + 0x0C)) // 触发CPU1软复位 void cpu1_soft_reset(void) { // 设置CPU1复位控制位(寄存器的bit 1) CPU1_RESET_REG |= (1 << 1); // 等待复位完成(可选,根据你的业务需求决定是否等待) while (CPU1_RESET_REG & (1 << 1)); }
简单说:给这个寄存器的bit1置1就会触发CPU1软复位,复位完成后该位会自动清零,所以可以用while循环等待复位结束。
三、唤醒WFE模式CPU1的核心操作
软复位完成后,CPU1会回到BootROM设置的WFE待机状态,这时候要唤醒它,需要做两件关键的事:指定CPU1的启动代码地址,然后发送全局唤醒信号(SEV)。
1. 先设置CPU1的启动地址
Zynq里CPU1的启动地址由SCU的CPU1 Boot Address Register控制,偏移地址是0x08:
#define CPU1_BOOT_ADDR_REG (*(volatile unsigned int*)(SCU_BASE_ADDR + 0x08)) // 设置CPU1的启动地址为你的应用代码入口 void set_cpu1_boot_addr(unsigned int addr) { CPU1_BOOT_ADDR_REG = addr; }
这里要注意:你传入的addr必须是内存中有效的代码地址——比如DDR或者片上内存(OCM)的地址,而且要确保你的CPU1应用代码已经提前加载到这个位置了,不然CPU1醒过来也没代码可跑。
2. 发送SEV信号唤醒WFE
唤醒WFE状态的CPU,最直接的方式就是发送SEV(Send Event)信号,有两种实现方式:
方法一:操作SCU寄存器触发SEV
#define SCU_SEV_REG (*(volatile unsigned int*)(SCU_BASE_ADDR + 0x10)) void send_sev(void) { SCU_SEV_REG = 0; // 写入任意值都会触发全局SEV信号 }
方法二:直接用汇编指令(更简洁)
在C代码里嵌入汇编指令sev,CPU0执行这个指令会直接触发全局事件,唤醒所有处于WFE状态的CPU:
static inline void sev(void) { __asm__ __volatile__ ("sev" : : : "memory"); }
四、完整的操作流程(CPU0侧)
把上面的步骤串起来,CPU0要按这个顺序操作:
- 提前将CPU1的应用代码加载到指定的内存地址(比如DDR的0x00100000)
- 调用
set_cpu1_boot_addr()设置CPU1的启动地址为代码入口
- 调用
- 调用
cpu1_soft_reset()触发CPU1软复位
- 调用
- 调用
sev()发送唤醒信号,让CPU1退出WFE开始执行代码
- 调用
五、需要注意的细节
- 确保SCU的时钟已经启用:Zynq默认SCU时钟是打开的,但如果你的系统有自定义时钟树配置,一定要确认SCU时钟正常工作,不然寄存器操作会无效
- 软复位后要及时发SEV:CPU1软复位后会立刻回到WFE状态,所以复位完成后要马上发送SEV,不然它会一直待机
- 启动地址必须有效:如果启动地址不对,CPU1即使被唤醒,也会因为找不到有效代码而出现异常
内容的提问来源于stack exchange,提问作者Yohboy

