什么是IPC核间通信
讲到IPC可能很多同学想到的是InterProcess Communication进程间通信,但是本文主要是讲另一种Inter-processor communication,处理器间通信,也叫核间通信,名字很像不要搞混。
为什么需要核间通信
现在的芯片系统非常复杂,通常包含多个核,特别是片上系统(SoC),一颗芯片上不仅包含了很多个核心,并且不同核可能运行着不同的操作系统。例如手机里的芯片,就可能包含了CPU、GPU、ISP、NPU等不同的处理器单元。为了最大限度的发挥他们的性能,协同完成某一任务,不同的核心上面运行的系统可能各不相同,有些核心上面运行的通用系统例如Linux,另外一些核心上可能运行的就是实时操作系统(RTOS)等。这些不同架构的核心以及他们上面所运行的软件组合在一起,就成了异构多处理系统(Asymmetric Multiprocessing System)。
集成到一个芯片上的好处就是节省成本并且体积更小,能耗也更低,可谓是一举多得。但是多个核上的各种OS之间就需要进行通信,这也就是本文的主题:核间通信。
上图是一个在车载场景中的例子,主要功能是采集摄像头数据经过isp处理后输出yuv图片到A核,然后A核再把图片送去codec硬件进行编码,codec把编码后的h264/h265码流输出到A核,然后在A核进行后续处理的例子。这个例子中设计到A核和isp核之间的通信以及A核和codec核之间的通信。本例中涉及到大量的数据传送,往往需要使用共享内存和核间通信来实现高效的数据传送。
这里的共享内存是指A核,isp, codec三个核共享一个ddr内存。那么在传输数据的时候只要告诉对方数据在哪就可以了,因为大家都能访问共享内存。那么剩下的问题就是如何把数据地址通知给其他的核,这个就是核间通信要解决的问题。
mailbox硬件
为了实现高效的核间通信,通常要使用一种叫做mailbox的硬件,比如arm pl320.
本文就以pl320 为例要讲解核间通信。
pl320手册地址:pl320 data sheet
mailbox硬件它的功能简单来说很简单:把数据发送给其他的核,并且通知对方来取数据。
pl320是一款arm开发的专门用来做核间通信的ip。核间通信在arm里边叫做
Inter-Processor Communications Module,核间通信模块。
IPCM是一个高度可配置和可编程的模块,主要有三个可配置的参数:
- 1-32个邮箱, 核间通信的通道
- 0-7个数据寄存器,存放发送数据的寄存器
- 1-32个中断,使用中断来通知对方取数据
AHB接口:AHB接口允许从系统总线访问到IPCM寄存器。
邮箱和控制逻辑:邮箱和控制逻辑块包含所有的邮箱寄存器和控制逻辑。
中断生成逻辑:中断生成逻辑块从所有IPCM邮箱的当前状态生成IPCM中断输出。
AHB(Advanced High-performance
Bus)接口是一种在ARM处理器中使用的总线接口。它是一种高性能、低功耗的总线架构,用于连接处理器核心、内存、外设和其他系统组件。AHB接口支持高带宽、低延迟的数据传输,能够满足多种应用场景的需求。在ARM体系结构中,AHB接口被广泛应用于系统级总线连接和数据交换。
上图是core0和core1使用ipcm进行通信的例子。
core0和core都通过ahb总线和ipcm连接到一起,这样core0,core1能够通过操作ipcm的寄存器来对ipcm进行控制和传输数据。同时ipcm的输出中断又和core0,core1的中断控制器连接到一起,这个就是ipcm能够通知core0, core1的关键。
上图这个例子中,我们看一下一个可能的场景Core0是源,使用channel 1,使用邮箱0发消息给core1。Core1是目标,使用channel 2。
这个过程可以简单分类几步:
- 发送方占用channel,通过配置寄存器来指明谁是发送端(source),谁是接受端(target),
即消息从哪发到哪 - 发送方写入要发送的数据,然后触发中断通知接收方读取数据
- 接受方被中断唤醒后读取数据,并且通知发送方数据已经收到了
上面这个过程从芯片层面看其实就是一系列的寄存器配置和读取,下面具体来看一下详细的流程。
下图来自pl320芯片手册,横轴是步骤,表示的是各个步骤中各个寄存器的状态值。
在第0部,所有的寄存器值都是0。
步骤1,Core0获得对邮箱0的控制,并通过在IPCM0SOURCE注册器中设置位0来将自己标识为源核心,此时IPCM0SOURCE的值为1。
步骤2,Core0通过在IPCM0MSTATUS寄存器中设置位0和1来实现对核心0和核心1的中断,本例中只使用两个中断,设置之后IPCM0MSTATUS的值为3。
步骤3,Core0通过在IPCM0DSTATUS寄存器中设置位1来定义目标核心,定义接收方,即发送给哪一个核心。
步骤4,Core0写入发送数据,设置寄存器IPCM0DR0为DA7A0000。
步骤5,Core0设置邮箱发送寄存器IPCM0SEND位0(就是0x01),以触发邮箱0中断到核心1,即通知接受方取数据。
步骤6,Core1读取IPCMRIS1寄存器,以确定是哪个邮箱导致了中断。在本例中,只指示邮箱0,这样core1知道消息是core0发送过来。
步骤7,Core1读取数据有效负载IPCM0DR0。
步骤8,Core1可选择使用确认数据DA7A1111更新数据有效负载IPCM0DR0,这一步相对于是回复一个ack。
步骤9,Core1清除位0,并在IPCM0SEND寄存器中设置位1(值为2),以清除其中断,并将手动确认中断提供回核心0。
步骤10,Core0读取IPCMRIS0寄存器,以确定是哪个邮箱导致了中断。同样,只指示邮箱0。
步骤11,Core0读取确认有效负载数据IPCM0DR0。
步骤12,Core0清除邮箱发送寄存器IPCM0SEND中的位1,以清除其中断。
步骤13,Core0通过清除IPCM0SOURCE寄存器来释放邮箱的所有权,进而清除IPCM0DSTATUS、IPCM0MSTATUS和IPCM0DR0寄存器。
以上就是大致的通信流程。这个流程通常在Linux驱动中已经有实现了,下期再做一下驱动代码讲解
以下是芯片手册的一些相关的说明
定义source发送方
core必须获取邮箱才能发送消息。为此,core将其一个 Channel ID 写入 Mailbox Source Register,然后再次读取 Mailbox Source Register 以检查写入是否成功。Mailbox Source Register 必须只包含一个 one-hot 编码值,即单个 Channel ID。软件必须确保仅将 one-hot 编码数字写入 Mailbox Source Register。只能在编程后清除 Mailbox Source Register,意思是说 Mailbox Source Register一旦被设置非零值只能清除后才能设置其它的非零值。除 0x00000000 以外的任何写入都将被忽略。此机制保证在任何时候只有一个核心可以控制邮箱。当不再需要邮箱时,内核通过清除 Mailbox Source Register 来放弃邮箱。清除 Mailbox Source Register 也会清除邮箱中的所有其他寄存器。这保证了邮箱在新分配时始终处于清空状态
定义destination
Mailbox Destination Register 具有单独的 Set 和 Clear 写入位置,使您能够在 Mailbox Destination Register 中设置单个位,而无需使用读-修改-写传输。您可以通过将 Mailbox Destination Register 中的单个位写入 Destination Set Register 来设置该位。这会导致硬件将该位与当前 Mailbox Destination Register 值进行 OR 运算。同样,您可以通过将 Mailbox Destination Register 中的单个位写入 Destination Clear Register 来清除该位。当 source core 定义邮箱的模式时,它通过将所有 Channel ID 的 OR 编程到 Mailbox Destination Register 中来定义哪些其他内核接收消息。如果一个核心有多个 Channel ID,则每条消息只使用一个 Channel ID。只有在定义邮箱源寄存器后,才能写入邮箱目标寄存器
使用邮箱掩码寄存器
邮箱掩码寄存器使用单独的 Set 和 Clear 寄存器进行修改,类似于 Mailbox Destination Register。Mailbox Mask Register 启用中断输出。要为特定邮箱启用中断,核心服务器会将其 Channel ID 写入 Mask Set 位置。通过将相同的 Channel ID 写入 Mask Clear 位置,可以屏蔽该邮箱的中断。只有在定义 Mailbox Source Register 后,才能写入 Mailbox Mask Register 位置。
使用邮箱发送寄存器
通过设置邮箱发送寄存器的位 0 来发送消息。这将触发对目标内核的中断。清除此位将清除到目标内核的中断。通过设置 Mailbox Send Register 的位 1,将ACK发送到源内核。清除此位将清除对 source core 的中断。您可以使用一次写入来清除 Mailbox Send Register 中的位 0 并设置位 1,尽管这不是强制性的。您不能先设置位 1 然后清除位 0,因为 11 是 Mailbox Send Register 的无效值。只有在定义 Mailbox Source Register 之后,才能写入 Mailbox Send Register。
邮箱数据寄存器
邮箱数据寄存器是包含消息的通用 32 位寄存器,只有在定义邮箱源寄存器后才能写入。邮箱数据寄存器通常在发送消息之前写入。
设置模式
邮箱模式寄存器控制如何将确认中断发送回源内核,以及当前邮箱是否链接到 IPCM 中的下一个邮箱。Mailbox Mode Register 有两个位,只有在定义 Mailbox Source Register 之后才能写入它。
中断和状态寄存器
当内核接收到 IPCM 中断时,它通过读取与该中断行相关的 Masked Interrupt Status Register 来确定哪个邮箱触发了该中断。每个 Masked Interrupt Status Register 最多包含 32 位,每个位引用一个邮箱。如果核心服务器正在使用轮询模式下的邮箱,则可以使用 Raw Interrupt Status Register 来指示需要注意的邮箱。
pl320这款芯片共支持32个mailbox,每个mailbox包含7个data寄存器,这意味一次最多能发送28字节数据
每个邮箱最多可以生成 32 个中断,每个 Channel ID 一个中断。中断数定义 Mailbox Source Register、Mailbox Destination Register 和 Mailbox Mask Register 中的位数。例如,在图 2-4 中,IPCM 有 32 个中断输出。Mailbox0 生成 IPCMMIS0-31 总线的位 0,而 Mailbox31 生成 IPCMMIS0-31 总线的位 31。