【休眠唤醒】多核异构S32G休眠唤醒方案
问题背景
当前多核异构架构SoC使用越来越广泛,多核之间的同步与通信都是比较负载的问题,接下来分享一个多核异构SOC休眠唤醒的实现方案,此方案涉及Soc内部Core-A与Core-M的状态同步策略
SOC平台架构
本方案基于NXP S32G系列芯片验证,Soc内部由多个A53核心和多个M7核心组成,S32G的partition off流程
SOC平台休眠唤醒实现策略
当M核收到CAN总线上来的休眠请求报文后,通过向与A核约定好的SRAM地址上写入一个值,比如0xac,当A核上的应用检测到后,执行系统调用Linux系统调用power off,走Linux kernel的下电流程,正常下电后,会陷入到ATF中(前提BSP中需要有ATF),在陷入atf的接口函数中向SRAM上的同一写入0xbc,表示A核已完成下电,M核检测到0xbc后通过控制PMIC对A核实现掉电策略,随后M核也进入休眠状态
Core-A侧实现方案
1.需要在Linux内核中编写一个开机自启动的应用程序,它的功能是作为一个进程持续检测SRAM中0x24007400的值,如果一旦检测到此值变为0xac,则立刻执行power off系统调用,走kernel下电流程
#include <iostream>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#include <thread>
using namespace std;
/**
* @brief M --> A to Sleep, M Write 0xac to 0x24007400.
*
* @return int
*/
int TestCase2();
int MonitorSleep();
int TestCase2()
{
std::cout << "[" << __LINE__ << "] " << __FUNCTION__ << " in..." << std::endl;
thread th_mintor(MonitorSleep);
th_mintor.detach();
return 0;
}
int MonitorSleep()
{
int fd = open("/dev/mem", O_RDWR);
if (fd == -1) {
std::cerr << "无法打开 /dev/mem" << std::endl;
return 1;
}
unsigned long address = 0x24007400;
size_t pagesize = sysconf(_SC_PAGESIZE);
off_t offset = address % pagesize;
size_t mapsize = pagesize + offset;
void *mapped = mmap(NULL, mapsize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, address - offset);
if (mapped == MAP_FAILED) {
std::cerr << "内存映射失败" << std::endl;
close(fd);
return 1;
}
unsigned char value = 0;
while(1)
{
sleep(1);
value = *((unsigned char *)((size_t)mapped + offset));
std::cout << "检测内存地址 " << std::hex << address << " 的值为: " << (int)value << std::endl;
if (value == 0xac || value == 0xad)
{
*((unsigned char *)((size_t)mapped + offset)) = 0x0;
if(value == 0xac)
break;
else
{
system("dmesg -n 8");
system("echo mem > /sys/power/state");
}
}
}
munmap(mapped, mapsize);
close(fd);
return system("poweroff");
}
int main(int argc, char *argv[])
{
// std::cout << "Please Input your order: " << std::endl;
int order = stoi(argv[1]);
// cin >> order;
std::cout << "Your order: " << order << std::endl;
if (1 > order || order > 2)
{
std::cout << "Your order: " << order << " is invalid" << std::endl;
return 0;
}
switch (order)
{
case 2:
TestCase2();
break;
default:
break;
}
while (1)
{
sleep(1);
}
return 0;
}
2.kenel下电后就会陷入到atf中,具体陷入到s32_psci.c中的s32_psci_pm_ops的system_off中,所以需要重写.system_reset和.system_off
初始代码
static void __dead2 s32_system_reset(void)
{
NOTICE("S32 TF-A: %s\n", __func__);
if (is_scp_used()) {
scp_reset_platform();
} else {
s32_destructive_reset();
}
plat_panic_handler();
}
static void __dead2 s32_system_off(void)
{
if (is_scp_used()) {
scp_shutdown_platform();
} else {
pmic_system_off();
}
plat_panic_handler();
}
const plat_psci_ops_t s32_psci_pm_ops = {
....
.system_reset = s32_system_reset,
.system_off = s32_system_off,
};
修改后
static void __dead2 s32_system_die_in_wfi(void)
{
//NOTICE("S32 TF-A: %s\n", __func__);
for (int i = 0; i < 8; i++) { // 对于不同版本S32G,需要修改A53 数量定义PLATFORM_CORE_COUNT
gicv3_cpuif_disable(i);
}
volatile unsigned char *ptr;
ptr = (unsigned char *)0x24007400;
*ptr = 0x0a;
plat_panic_handler();
}
const plat_psci_ops_t s32_psci_pm_ops = {
....
.system_reset = s32_system_die_in_wfi,
.system_off = s32_system_die_in_wfi,
};
A核下电后就会陷入到atf中,执行s32_system_die_in_wfi函数,再向0x24007400写回0xbc,当M核检测到之后,再拉PMIC,整个SOC走下电流程
注意:选择SRAM同步标志的地址时,要在atf中去使能地址所在的内存块支持读写,否则可能会出现在atf中无法写入0xbc的情况
比如将s32_bl31.c中的
#if defined(S32G_SSRAM_BASE)
MAP_REGION_FLAT(S32G_SSRAM_BASE, S32G_SSRAM_LIMIT - S32G_SSRAM_BASE,
MT_MEMORY | MT_RW | MT_SECURE),
#endif
修改为
#if defined(S32G_SSRAM_BASE)
MAP_REGION_FLAT(S32G_SSRAM_BASE, S32G_SSRAM_LIMIT - S32G_SSRAM_BASE,
MT_NON_CACHEABLE | MT_RW | MT_SECURE),
#endif
总结
以上为一种多核异构SOC休眠唤醒的一种实现方法