核间通信API的介绍和使用

ingenic_ipc核间通信实现原理

小核初始化时会设置两个环形缓冲区用于通信,分别命名为data_form_host和data_to_host,并且将缓冲区地址填写在resource_table中(代码位于remoteproc_rsc_table.h).加载小核固件后,大核解析镜像获取resource_table地址,经过地址转换后获取环形缓冲区地址.双方通过对上述环形缓冲区进行读写实现数据的收发.

大小核会分别注册一个中断,用于响应对方发送数据的通知.

加载并启动小核固件后,大核会注册一个字符设备作为通信节点,并通过设备文件/dev/ipc-dev-mcu提供操作接口,用户层可通过系统调用使用该节点进行大核端的通信.

当大核向小核发送数据时,用户层使用write系统调用传递发送命令给设备驱动,驱动会将消息放入环形缓冲区,并给小核发送一个中断,小核接收到中断后调用消息接收接口读取消息.

当小核向大核发送数据时,调用消息发送接口,传参消息地址和长度,小核驱动会直接将消息放入环形缓冲区,并给大核发送一个中断,大核接收到中断后使用read系统调用读取消息.

img_01.jpg

通信API介绍

host端通信

驱动程序注册了一个字符设备节点作为通信节点,并通过设备文件 /dev/ipc-dev-mcu 提供接口。用户层使用系统调用该节点进行数据的收发,详细介绍如下:

驱动代码路径:kernel/module_drivers/drivers/remoteproc/ingenic_ipc/

1. 打开设备文件

在进行任何通信之前,必须先打开设备文件以获得文件描述符。

fd = open("/dev/ipc-dev-mcu", O_RDWR);                                    
if (fd < 0) {
    perror("open ipc dev failed");
    return -1; 
}

2. 发送数据

调用write系统接口发送数据到目标处理器

// 设置写操作的数据
snprintf(msg, MSG_SIZE, "hello remote\n");
// 执行写操作
ret = write(fd, msg, MSG_SIZE);
if (ret == -1) {
    perror("host send data error");
    close(fd);
    return -1; 
}

3. 接收数据

调用read系统接口从目标处理器接收数据.

// 执行读操作
ret = read(fd, msg, msg_size);                                            
if (ret == -1) {
    perror("read error");
    return -1; 
}

4. 关闭设备文件

完成所有通信后,关闭设备文件以释放资源。

close(fd);

remote端通信API

本章节详细介绍了小核端核间通信的API。这些API提供了在小核上发送和接收数据的功能,并支持通过软中断通知主机端进行处理。API包括初始化、消息发送、消息接收以及设置接收回调函数等功能。

驱动代码路径:libbare-cpu/drivers/drivers-x16xx/src/x16xx_hal_ipc.c

1. HAL_IPC_MsgInit

【函数原型】void HAL_IPC_MsgInit(void);

【功能描述】初始化核间通信模块,注册软中断处理程序,必须在使用任何通信功能之前调用此函数。。

【参数说明】无

【返回值】 无

2. HAL_IPC_SetRecvCallback

【函数原型】HAL_IPC_SetRecvCallback

【功能描述】设置接收消息的回调函数。

【参数说明】

参数 描述
cb 指向回调函数的指针。每当有新消息到达时,软中断处理程序将调用此回调函数。

【返回值】 无

3. HAL_IPC_MsgSend

【函数原型】int HAL_IPC_MsgSend(void *buf, unsigned int size);

【功能描述】向主机端发送消息,该函数会等待主机端空闲,并通过环形缓冲区逐段写入数据。发送完成后,会通知主机端有新的数据可用。

【参数说明】

参数 描述
buf 指向要发送的数据缓冲区的指针。
size 要发送的数据长度(字节数)

【返回值】 成功:返回实际发送的字节数。 失败:返回0。

4. HAL_IPC_MsgRecv

【函数原型】int HAL_IPC_MsgRecv(void *buf, unsigned int size);

【功能描述】从主机端接收消息, 该函数从环形缓冲区中逐段读取数据到指定的缓冲区中,直到读取到所有数据或达到最大长度。

【参数说明】

参数 描述
buf 指向接收数据缓冲区的指针
size 期望接收的数据长度(字节数)

【返回值】 成功:返回实际接收到的字节数。 失败:返回0。

核间通信应用实例

host端软件实现

menuconfig

CONFIG_INGENIC_RPROC_IPC=y

host端测试用例实现

host端测试用例代码源码位置:

 libbare-cpu/projects/x16xx-halley6/Examples/ingenic_mcu_ipc/xburst2_app

该测试用例首先打开通信端点,分别测试一发一收的情况和连续发送的情况,最后销毁通信端点.

remote端软件实现

工程路径

libbare-cpu/projects/x16xx-halley6/Examples/ingenic_mcu_ipc

remote端测试用例实现

测试用例首先调用HAL_IPC_MsgInit接口进行通信的初始化,然后调用HAL_IPC_SetRecvCallback接口设计接收消息的回调函数,在回调函数中接收并打印收到的消息,然后向host端发出消息

实验步骤

1.编译mcu工程生成ingenic_mcu_ipc.bin

2.将固件放在文件系统/lib/firmware目录下并改名为libmcu-bare.bin

3.大核通过remoteproc状态控制启动小核

echo 1 > /sys/devices/platform/ahb2/13420000.mcu/load_fw

4.启动小核后出现如下打印,证明小核正常启动

mcu startup~

出现如下打印证明通信设备节点注册成功

ingenic ipc device regist OK!

此时会在/dev目录下生成ipc-dev-mcu节点.

5.编译工程中的xburst2_app测试用例,执行进行测试,测试log如下

# ./ingenic_mcu_ipc_test
===mcu receive a message===
hello remote

host recv msg: hello host!

===mcu receive a message===
hello remote 0!
               ===mcu receive a message===
hello remote 1!
               ===mcu receive a message===
hello remote 2!
               ===mcu receive a message===
hello remote 3!
               ===# mcu receive a message===
hello remote 4!
               mcu send data error!

6.关闭小核结束测试

echo 1 > /sys/devices/platform/ahb2/13420000.mcu/shutdown

大核首先给小核发送信息hello remote,小核接收并打印该信息,然后回消息hello host!给大核,大核打印该消息. 随后大核连续向小核发送消息5次,小核接收打印消息并回复,由于大核不读取小核发送的消息,小核会在发送buffer被填满后发生发送异常,报错mcu send data error.