【休眠唤醒】多核异构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休眠唤醒的一种实现方法

posted @ 2024-12-18 23:34  Emma1111  阅读(12)  评论(0编辑  收藏  举报