2. 异构多核基础
好,咱们今天来聊聊 Mailbox 的硬件。很多做 Linux 驱动的朋友,一开始接触 Mailbox 都觉得它像个黑盒子——不就是两个核之间传个消息嘛,能有多复杂?
但实际调过的人都知道,坑全在硬件细节里。我记得第一次调一个异构芯片的 Mailbox,门铃没配好,结果 A 核发消息,M 核死活收不到中断。查了两天才发现是门铃寄存器写错了地址。嗯,从那以后,我养成了一个习惯:拿到芯片手册,先把 Mailbox 的寄存器地图画出来。
2.1 Mailbox 的核心寄存器
一个标准的 Mailbox 硬件模块,通常包含以下几类寄存器。不同厂商命名可能不同,但功能大同小异。
| 寄存器类型 | 典型名称 | 作用 |
|---|---|---|
| 状态寄存器 | STAT / SR | 指示 mailbox 是否空闲、是否有新消息 |
| 控制寄存器 | CTRL / CR | 使能/禁止 mailbox、配置中断 |
| 消息寄存器 | MSG / DR | 存放实际要传递的数据(通常 32 位) |
| 门铃寄存器 | DOORBELL / DB | 触发中断通知对方 |
| 中断状态寄存器 | ISR | 标识哪个通道产生了中断 |
| 中断掩码寄存器 | IMR / IER | 控制哪些中断源可以上报 |
说白了,你写数据到 MSG 寄存器,然后敲一下门铃(写 DOORBELL),对方就会收到中断,来读 MSG。就这么简单。但实际项目中,往往有多个通道、多个中断源,这时候寄存器就变得复杂了。
关键点: 大多数 Mailbox 硬件支持 多通道。每个通道有自己独立的消息寄存器和门铃位。通道之间互不干扰,这为 RPMsg 的多端点通信提供了硬件基础。
2.2 中断机制:谁触发了谁?
中断是 Mailbox 的灵魂。没有中断,你就得轮询——那还不如用共享内存呢。
我见过一个常见的误解:以为写消息寄存器就会自动触发中断。其实不是。消息寄存器就是个数据容器,你写进去,对方不会知道。必须再写门铃寄存器,硬件才会产生中断信号。
中断流程大致如下:
- 发送方 把数据写入 MSG 寄存器。
- 发送方 写 DOORBELL 寄存器(置位对应通道的 bit)。
- 硬件 检测到门铃被敲,产生中断信号给接收方。
- 接收方 中断服务程序读取 ISR,判断是哪个通道。
- 接收方 读取 MSG 寄存器,获取数据。
- 接收方 清除中断标志(写 ISR 或清门铃位)。
这里有个坑:有些硬件清中断的方式是写 1 清除,有些是写 0 清除。我当年在 STM32MP1 上就踩过这个坑——手册没仔细看,按写 0 清的方式写了,结果中断一直挂在那,CPU 直接卡死。后来查勘误表才发现,那个 IP 核的设计确实反人类。
注意: 中断清除操作必须放在读取数据之后。如果先清中断再读数据,可能会丢失消息。因为有些硬件在清中断时会同时复位消息寄存器。
2.3 通道与门铃机制
通道(Channel)和门铃(Doorbell)是 Mailbox 里最容易混淆的两个概念。我简单解释一下。
通道 是逻辑上的通信管道。每个通道有自己独立的消息寄存器。比如通道 0 和通道 1,它们的数据是隔离的。你可以把通道理解成「不同的邮箱格子」。
门铃 是通知机制。每个通道对应一个门铃位。你往通道 0 的门铃位写 1,对方就知道通道 0 有消息来了。
为什么需要多个通道?因为 RPMsg 协议里,不同服务可能跑在不同的端点上。比如音频服务用端点 1,传感器服务用端点 2。如果只有一个通道,所有消息挤在一起,接收方还得解析消息头才知道是谁发的。有了多通道,硬件帮你做了第一层分流。
我的习惯: 在项目初期,我会把通道和端点的映射关系做成一张表。比如通道 0 对应 RPMsg 端点 1~4,通道 1 对应端点 5~8。这样驱动层写起来清晰,调试也方便。
2.4 硬件数据流分析
咱们来画一张数据流图,看看一个消息从 A 核发出到 B 核接收,到底经过了哪些环节。
这张图我画得比较简化。实际项目中,总线上可能还挂着多个 Mailbox 实例,每个实例又有多个通道。但核心逻辑不变:数据走消息寄存器,通知走门铃寄存器。
你想想看,如果两个核共享内存,没有 Mailbox,那接收方就得不停地轮询共享内存里的标志位。轮询间隔设短了,CPU 占用高;设长了,响应延迟大。Mailbox 用中断解决了这个矛盾——平时睡觉,有消息才醒。
2.5 实际项目中的配置要点
最后,我总结几个实际调板子时容易忽略的点:
- 门铃位是否自动清除? 有些硬件读完消息后门铃自动清,有些需要软件手动清。手册里通常叫「Auto-clear」或「SW-clear」。我建议一律按手动清来写驱动,兼容性最好。
- 消息寄存器深度。 有些 Mailbox 只有一个 32 位寄存器,一次只能传 4 字节。有些有 FIFO,可以一次传多个字。RPMsg 通常需要传可变长消息,所以底层要配合共享内存使用——Mailbox 只传指针和长度。
- 中断优先级。 如果两个核之间通信频繁,建议把 Mailbox 中断优先级设高一些。否则低优先级中断被其他任务抢占,可能导致消息处理延迟,甚至溢出。
- 多核间的 cache 一致性。 这个我后面会专门讲。简单说一句:Mailbox 本身不处理 cache,如果你用共享内存传大数据,一定要加 cache 刷新操作。
一句话总结: Mailbox 硬件就是「一个数据寄存器 + 一个门铃寄存器 + 中断逻辑」。理解了这个组合,你就能驾驭任何厂商的 Mailbox IP。