异构多核通信 (IPC)

概述

随着信息技术的发展与需求的提升,传统单核 SoC 不能满足需求,促进了同构多核与异构多核的发展,在异构多核框架中,整个系统中由多个不同的处理器、性能与用途格不相同的多个核心组成,各个核心发挥各自的计算优势,实现资源的最佳配置,很好的提升了整体性能,降低功耗,简化开发模式,因此多核间的通信机制,以及软件编程模型对多核性能的发挥尤为重要。本文介绍异构核间通信机制,考虑的因素有:

  • 轻量级,可应用于 NOS / RTOS 的应用场景
  • 简化各核心的开发模式
  • 针对核间协同工作的特点,划分为:通用计算、并行计算、流式数据、帧式数据,应用于多种异构场景
  • 可移植性,框架可以方便移植到 Linux,不同的 RTOS 中

异构多核处理器大多采用主从式的结构。主从式结构根据不同核的功能把处理器核分为主核和从核。主核的结构和功能一般较为复杂,负责全局资源、任务的管理和调度并完成从核的引导加载。从核主要接受主核的管理,负责运行主核分配的任务,并具有本地任务调度与管理功能。在多核处理器中,根据不同核的结构,各个核可运行相同或不同的操作系统。

异构多核实现原理

@平台接入示意图

从上图可知,为实现异构多核之间的通信,芯片设计时,通过中断向另一个核发送中断请求,执行相应的中断处理程序进入地址传递,使用共享一块内存区域,实现数据传递,各核心对外设的访问,通过控制总线来配置。因此,要实现异构核间通信,各核心须有以下功能:

  • 主处理器对从处理器进行管理
  • 内部处理器之间的信息传输与交换

核间中断

共享内存

核间通信协议

IPC架构

Channel (通道)

Channel 实现多核之间的数据交互的通信机制,

struct channel {
    slist_t    next;
    channel_cb cb;
    void      *priv;
    uint32_t   des_id;
    void      *context;
};

Event (事件)

Event 是对核间中断的抽象,定义为事件,系统中可以定义32个事件,事件是有优先级的,EventId越小优先级越高,事件0的优先级最高,随着EventId增大优先级依次递减;当多个事件被触发,优先级最高的会最先响应。

MessageQ (消息队列)

MessageQ(目前暂未实现),基于队列的消息传递,有以下特点:

实现了处理期间变长消息的传递;消息的传递都是通过操作消息队列来实现的;每个消息队列可以有多个写者,但只能有一个读者;每个任务(task)可以对多个消息队列进行读写;一个宿主在准备接收消息时,必须先创建消息队列,而在发送消息前,需要打开预定的接收消息队列。

FrameQ (帧缓冲队列)

FrameQ(目前暂未实现)是专门为传递视频帧而设计出来的组件。FrameQ的基本数据结构是可用于queue/dequeue数据的数据队列,封装了视频帧缓存指针、帧数据类型、帧宽、帧高、时间戳等信息。

对于FrameQ模块具有以下特征:支持多个读者,但写者唯一;可以分配和释放Frames;可以对指向同一块内存区多次分配和初始化成新的帧buffer;FrameQ允许有多个队列,在多通道的运用中,视频帧会根据通道号被分配到相应的帧队列中。

RingQ (环形缓冲区)

RingIO(目前暂未实现)是基于数据流的环形缓冲buffer,而且针对于音视频数据的特性做了优化。

RingIO支持一下特性:仅支持一个读者和一个写者;读写相对独立,可以在不同的进程或者处理器中同时进行读写操作。

接口定义

初始化ipc

ipc_t *ipc_get(int des_cpu_id);

初始化与目标cpu的通道,获取ipc句柄

  • 参数
    • des_cpu_id:目标cpu id
  • 返回值:
    • 成功返回ipc_t 指针,失败返回NULL

发送ipc消息

int ipc_message_send(ipc_t *ipc, message_t *msg, int timeout_ms);

IPC 消息发送,将消息通过 channel 发送到远程 IPC。当 msg 为同步消息时,该函数等待对方应答后才会返回,当 msg 异步消息发送完后直接返回。

typedef struct message_msg message_t;
struct message_msg {
    uint8_t         flag;            /** flag for MESSAGE_SYNC and MESSAGE_ACK */
    uint8_t         service_id;      /** service id for the service want to comtunicate */
    uint16_t        command;         /** command id the service provide */
    uint32_t        seq;             /** message seq */
    void           *req_data;        /** message request data */
    int             req_len;         /** message request data len */
    aos_queue_t     queue;           /** queue for SYNC MESSAGE */
    void           *resp_data;         /** message response data */
    int             resp_len;         /** message response data len*/
};
  • 参数
    • ipc:ipc 句柄
    • msg:ipc 消息
    • timeout_ms:超时时间,单位ms
  • 返回值:
    • 成功返回0,失败返回-1

回复ipc消息

int ipc_message_ack(ipc_t *ipc, message_t *msg, int timeout_ms);

当 msg 为同步消息时,调用该函数

  • 参数
    • ipc:ipc 句柄
    • msg:ipc 消息
    • timeout_ms:超时时间,单位ms
  • 返回值:
    • 成功返回0,失败返回-1

添加服务

int ipc_add_service(ipc_t *ipc, int service_id, ipc_process_t cb, void *priv);

在ipc中增加一个服务

typedef struct ipc ipc_t;
typedef void (*ipc_process_t)(ipc_t *ipc, message_t *msg, void *priv);
  • 参数
    • ipc:ipc 句柄
    • service_id:服务号
    • cb:用户自定义ipc服务处理函数
    • priv:用户自定义参数
  • 返回值:
    • 成功返回0,失败返回-1

lpm设置

int ipc_lpm(int cpu_id, int state);
  • 参数:

    • cpu_id: 目标cpu id。
    • state: 状态。
  • 返回值:

    • 0: 成功;-1: 失败。

示例代码

  • AP
#define IPC_BUF_LEN (4096*2)

typedef struct {
    ipc_t *ipc;
    char data[IPC_BUF_LEN] __attribute__ ((aligned(64)));
} ipc_test_t;

ipc_test_t g_test[2];

#define TAG "AP"
/*ipc 同步调用示例*/
int ipc_sync(void)
{
    message_t msg;

    /* 设置message的command为103 */
    msg.command = 103;
    /* 设置同步标志 */
    msg.flag    = MESSAGE_SYNC;
    /* 设置message的service_id为20 */
    msg.service_id = 20;
    msg.resp_data = NULL;
    msg.resp_len = 0;

    /* 设置一次发送数据量 */
    int snd_len = 4096;
    int offset = 0;
    char *send = (char *)src_audio_music_raw;


    while (offset < src_audio_music_raw_len) {
        /* 设置message的请求data指针 */
        msg.req_data    = send + offset;

        /* 设置message的请求data的长度 */
        snd_len = 4096 < (src_audio_music_raw_len - offset)? 4096 : (src_audio_music_raw_len - offset);
        msg.req_len     = snd_len;
        /* 发送message*/
        ipc_message_send(g_test[0].ipc, &msg, AOS_WAIT_FOREVER);
        /* 发送成功后将文件偏移发送数据量*/
        offset += snd_len;
    }

    printf("ipc sync done\n");
    return 0;
}

// async message demo

static char *s[] = {
    "00000000",
    "11111111",
    "22222222",
    "33333333",
    "44444444",
    "55555555",
    "66666666",
    "77777777",
    "88888888",
    "99999999",
};
/*ipc 异步调用示例*/
int ipc_async(void)
{
    message_t msg;
    memset(&msg, 0, sizeof(message_t));

    /* 设置message的service_id为20 */
    msg.service_id = 20;
    /* 设置message的command为104 */
    msg.command = 104;
    /* 设置异步标志*/
    msg.flag   = MESSAGE_ASYNC;

    msg.service_id = 20;

    for (int i = 0; i < 100; i++) {
        msg.req_data    = s[i%10];
        msg.req_len     = strlen(s[i%10]) + 1;

        /* 发送 message */
        ipc_message_send(g_test[0].ipc, &msg, AOS_WAIT_FOREVER);
    }

    printf("ipc async done\n");

    return 0;
}

int ipc_server_init(void)
{
    ipc_test_t *i = &g_test[0];
    /* 获取ipc (cpu_id:2)*/
    i->ipc = ipc_get(2);

    /* 添加ipc服务(service_id:20)*/
    ipc_add_service(i->ipc, 20, NULL, NULL);

    return 0;
}
  • CP
#define TAG "CP"
#define IPC_BUF_LEN (1024)

typedef struct {
    ipc_t *ipc;
    char data[IPC_BUF_LEN] __attribute__ ((aligned(64)));
} ipc_test_t;

ipc_test_t g_test[2];

static char *s[] = {
    "00000000",
    "11111111",
    "22222222",
    "33333333",
    "44444444",
    "55555555",
    "66666666",
    "77777777",
    "88888888",
    "99999999",
};

static void cli_ipc_process(ipc_t *ipc, message_t *msg, void *priv)
{
    switch (msg->command) {

        case 104: {
            /* 异步cmd处理 */
            static int offset = 0;
            /* 比较异步发送的数据是否正确*/
            int ret = memcmp(s[offset % 10], msg->req_data, msg->req_len);

            offset ++;

            if (ret != 0) {
                printf("ipc async err!!!\n");
            }

            if (offset == 100) {
                printf("ipc async ok!!!\n");
                offset = 0;
            }
            break;
        }

        case 103: {
            char *music = (char *)src_audio_music_raw;
            static int music_len = 0;

            /* 比较同步发送的数据是否正确*/
            int ret = memcmp(music + music_len, msg->req_data, msg->req_len);
            music_len += msg->req_len;

            if (ret != 0) {
                printf("ipc sync err!!!\n");
            }
            if (music_len == src_audio_music_raw_len) {
                /* 若文件发送完毕则打印成功*/
                printf("music recv ok, total:(%d)\n", music_len);
                music_len = 0;
            }
            /* 回复同步message的ack*/
            ipc_message_ack(ipc, msg, AOS_WAIT_FOREVER);

        }

        default :
            break;
    }
}

int ipc_cli_init(void)
{
    ipc_test_t *i = &g_test[1];
    /* 获取ipc (cpu_id:0)*/
    i->ipc = ipc_get(0);

    /* 添加ipc服务(service_id:20)*/
    ipc_add_service(i->ipc, 20, cli_ipc_process, i);

    return 0;
}

results matching ""

    No results matching ""