联发科中断控制器(GIC):从架构到实战
各位同学,今天我们来聊聊联发科平台上的中断控制器——GIC。说实话,中断系统是整个嵌入式系统的“神经系统”,而GIC就是那个“大脑”。我在联发科做项目时,遇到过不少因为中断配置不当导致的诡异问题,比如系统莫名其妙卡死、外设响应延迟忽高忽低。嗯,今天我就把这些经验揉碎了讲给你听。
一、GIC架构:中断的“交通枢纽”
GIC的全称是Generic Interrupt Controller,通用中断控制器。它负责接收来自各个外设、处理器核甚至软件自身的中断请求,然后按照优先级、亲和性等规则,分发给对应的CPU核心去处理。
我个人习惯把GIC想象成一个“智能分拣中心”:
- 中断源:好比是各个快递网点(外设、定时器、DMA等)
- 分发器(Distributor):负责接收所有中断,按优先级排队,决定发给哪个CPU
- CPU接口(CPU Interface):每个CPU核都有一个,负责把中断信号“喂”给核
你想想看,如果没有GIC,每个外设直接连到CPU的中断引脚,那CPU得有多少个引脚才够用?而且多个中断同时到来时,谁先谁后?所以GIC的出现,解决了中断管理的核心问题。
核心要点:GIC负责中断的收集、优先级仲裁、分发和屏蔽。它是CPU与外设之间的“中间人”。
二、GIC版本演进:v2/v3/v4,你该选哪个?
GIC从诞生到现在,经历了多个版本。我在联发科做MTK平台适配时,从v2一路用到v4,每个版本都有它的“脾气”。
| 版本 | 特点 | 典型应用 |
|---|---|---|
| GICv2 | 经典架构,最多支持8个CPU核,中断ID范围0-1019 | Cortex-A7/A9,老款手机、IoT设备 |
| GICv3 | 支持更多CPU核(最多256个),引入LPI中断,中断ID扩展到16位 | Cortex-A53/A72,中高端SoC |
| GICv4 | 在v3基础上优化虚拟化支持,引入直接注入(Direct Injection) | 虚拟化场景、服务器级芯片 |
GICv2 是最常见的版本。我记得在MT6739项目上,用的就是GICv2。它的配置相对简单,寄存器也少,适合入门学习。但缺点也很明显——中断号有限,而且不支持虚拟化。
GICv3 是现在的主流。它最大的变化是引入了“中断重定向表”(ITS),可以处理海量中断。我在MT6885(天玑1000)上调试时,发现v3的亲和性配置比v2灵活得多,可以精确地把某个中断绑定到特定CPU核上。
GICv4 主要用于虚拟化场景。比如你在手机上跑多个虚拟机,每个虚拟机都需要独立的中断管理。v4支持“直接注入”,虚拟机可以直接接收中断,不需要Hypervisor每次都介入。嗯,这个在联发科的车载芯片上用得比较多。
避坑指南:我曾经在GICv2上遇到一个坑——中断号超过1023时,系统直接挂掉。后来查手册才发现v2只支持到1019。所以选型时一定要确认你的外设中断号是否在范围内。
三、中断类型:SPI、PPI、SGI、LPI
GIC把中断分成了几种类型。说白了,就是给中断贴标签,方便管理。我刚开始接触时也容易搞混,后来用了一个比喻就记住了:
- SPI(共享外设中断):好比是“公共电话”,谁都能打,但一次只能一个人用。比如UART、I2C、SPI这些外设的中断,都是SPI。它们可以被路由到任意一个CPU核。
- PPI(私有外设中断):好比是“私人电话”,每个CPU核独享一个。比如每个核的本地定时器、性能监测单元(PMU)中断。PPI的中断号是固定的,每个核都一样。
- SGI(软件生成中断):CPU自己给自己或别的核发的中断。常用于核间通信(IPI)。比如Linux内核里的IPI_CALL_FUNCTION,就是通过SGI实现的。
- LPI(本地特定中断):GICv3/v4新增的类型。它不通过传统的寄存器配置,而是通过内存表(ITS)来管理。适合大量中断的场景,比如PCIe MSI/MSI-X中断。
记忆口诀:SPI是共享的,PPI是私有的,SGI是软件发的,LPI是海量的。
在联发科平台上,SPI和PPI用得最多。比如你在MTK平台上配置一个GPIO中断,它通常就是SPI。而每个CPU核的Timer中断,就是PPI。SGI在Linux内核调度中很常见,比如唤醒某个核上的进程。
四、实战经验:中断配置的“坑”与“解”
讲完理论,我分享几个实际项目中踩过的坑。
案例1:SPI中断优先级反转
有一次在MT6765上调试音频驱动,发现播放音乐时偶尔会有“咔哒”声。查了半天,发现是SPI中断优先级设置不当——音频DMA中断优先级低于某个不重要的外设中断,导致音频数据被延迟处理。解决办法很简单:把音频相关中断的优先级提到最高。
// 设置SPI中断优先级(GICv2示例)
GICD_IPRIORITYR[irq_num] = 0xA0; // 优先级值越小,优先级越高
案例2:PPI中断亲和性绑定
在MT6873(天玑1200)上做功耗优化时,我把所有中断都绑定到大核上,结果小核空闲了,大核忙死。后来我调整策略:把延迟敏感的中断(如触摸屏)绑到大核,把吞吐量型的中断(如网络)绑到小核。这样功耗和性能都兼顾了。
// 设置中断亲和性(GICv3示例)
// 将中断绑定到CPU0和CPU1
GICD_IROUTER[irq_num] = 0x00010001; // bit[7:0]表示CPU0,bit[15:8]表示CPU1
重要提醒:GICv2和GICv3的亲和性配置方式完全不同。v2是通过寄存器组(GICD_ITARGETSR)配置,v3是通过独立的路由寄存器(GICD_IROUTER)配置。千万别搞混了,否则中断可能发到错误的核上。
五、总结:GIC的核心要点
好了,今天的内容就到这里。我帮你梳理一下重点:
- GIC是中断系统的“大脑”,负责分发和管理中断
- v2适合小系统,v3是主流,v4面向虚拟化
- SPI是共享的,PPI是私有的,SGI是软件发的,LPI是海量的
- 配置中断时,注意优先级、亲和性和中断号范围
下一章我们会深入GIC的寄存器编程,手把手教你配置一个完整的中断服务。到时候我会拿联发科的某个具体芯片做例子,咱们一起把代码跑起来。
嗯,今天就先聊到这儿。有问题随时问我。