GIC 模块 – ARM 系列
前言
文章主要描述GIC模块,其基于Gicv3的架构,从gic的由来、内部模块feature、软件驱动等角度来概述。
一、gic概述
gic的由来:
在整个soc系统中,会存在很多外设通过硬连线的方式把中断发送给core处理,
而core的端口只存在2个中断信号(nFIQ/nIRQ),为了处理多个中断信号,就设计了通用中断控制器(GIC)来减轻CPU对中断处理的负担,并实现对中断的管理、分组、路由、优先级配置等功能
gic的发展:
架构经历了gicv1/gicv2/gicv3/gicv4,下文主要以GICv3架构介绍
GICv3与GICV2的区别
- 使用属性层次(affinity hierarchies)来路由信息,可支持更多的core
- 将cpu interface独立出来,放再core内部,可提高对中断的响应速度
- 增加redistributor组件,用于连接distributor和cpu interface
- 增加了LPI,使用ITS来解析
- 对于cpu interface的寄存器,增加系统寄存器访问方式
GICv3与GICv4的功能基本相同,提高虚拟化的性能(该特性单独讨论)
概述:GICv2到GICv3的改动目的是为了提高中断的响应速度和支持更多的core接入gic网络,即在v3的架构中把cpu interface从gic中拿出来,并且新增加系统寄存器,使core通过cpu interface直接处理中断,而不需要像v2架构中处理中断需要走axi总线访问寄存器了。
二、GICv3 功能特性
1.模块特性
1.1模块组件
在GICv3架构中,将gic进行了模块化设计,生成了不同的组件,分别为distributor、redistributor、ITS和cpu interface
Distributor:实现对spi和sci中段优先级,路由信息的配置,对spi中断触发方式的配置,其中需要cfg的寄存器:、
1、中断使能寄存器:GICD_ISENABLER
2、清除gic状态寄存器:GICD_ICENABLER/GICD_ICPENDR/GICD_ICACTIVER
3、配置优先级寄存器:GICD_IPRIORITYR
4、中断触发方式: GICD_ICFGR
5、中断affinity值:GICD_IROUTER
6、中断安全分类,ARMv8架构中分为2种安全态(对应gic的3个group)和1种安全态(对应gic的2个group,正常来说 FIQ 是security 、IRQ是nonesecurity
中断安全分类,ARMv8架构中分为2种安全态(对应gic的3个group)和1种安全态(对应gic的2个group,正常来说 FIQ 是security 、IRQ是nonesecurity
Redistributor:位于Distributor和CPU interface之间,对ppi和sgi中段优先级,路由信息和触发方式的配置。相关配置的寄存器于distributor组件类似
Cpu interface:位于Distributor和CORE之间,用于对中断的响应和处理。
1、使能对应的group寄存器:ICC_IGRPEN0/1
2、清除对应的中断号:ICC_EOIR0/1
问题:1-N和N-N的类型是什么?
1-N类型:SGI和SPI中断会根据affinity信息路由到指定core
N-N类型:SGI和SPI中断会广播给所有的core
根据寄存器GICD_IROUTER的Interrupt_Routing_Mode来配置
1.2 状态机
链接:https://developer.arm.com/documentation/198123/0302/Arm-GIC-fundamentals
gic一共有四种状态机:
(1)inactive:中断未被触发,或者已经被cpu处理完成了
(2)pending:中断已经被硬件触发或软件产生(SGI),但cpu还没有应答中断
(3)active:cpu已经应答并正在处理中断
(4)active and pending:cpu已经应答中断并正在处理中断,此时又有新的中断被触发了
状态机的跳转流程:
- Inactive to pending:当相关中断被assert,其状态Inactive to pending
- Pending to active and pending:当core通过cpu interface读取中断响应寄存器时,状态进行跳转
- Active and pending:通过软件写寄存器来清外设寄存器时,状态进行跳转
- Active to inactive:core通过cpu interface写EOIRs寄存器来完成中断的响应处理
2.中断类型
SPI中断(shared peripheral interrupt)
该类型中断不与特定的cpu绑定,可以根据affinity配置被路由到任意cpu或一组特定的cpu上。如一般的外设中断都是通过SPI方式连接的。
PPI中断(private peripheral interrupt)
该类型中断是每个处理器私有的,即一个特定的中断只会被路由到特定的处理器上。且其同一个中断号在每个处理器上都可以有不同的中断,如对于一个拥有两个PE的smp系统,中断号16的PPI中断可以分别被注册为PE0和PE1的私有中断,它们可以被独立触发并被特定的PE独立处理。
一般core会固定定义相关core内部的私有timer中断
SGI中断(software generated interrupt)
该类型中断并没有实际的物理连线,而是由软件通过写寄存器方式触发,它只支持边沿触发。通常用于处理器之间的通信,如linux内核电源管理模块中调用的ipi中断就是通过SGI实现的
LPI中断(Locality-specific Peripheral Interrupt)
该类型中断是一种基于消息的中断,外设不需要通过硬件中断线连接到GIC上, 而可以向特定地址写入消息来触发中断。典型的应用为PCIe的MSI和MSI-X中断。
中断类型 | 中断号 | 属性 | 备注 |
---|---|---|---|
SGI | 0-15 | 边沿触发、电平触发 | |
PPI | 16-31 | 边沿触发、电平触发 | |
SPI | 32-1019 | 边沿触发、电平触发 | |
LPI | 8192- | message类型 |
3.中断功能
配置gic驱动流程,初始化函数如下:
//中断初始化函数
void gic_init(){
uint32_t gic_base_addr;
REG_WR(gic_base_addr + 0x184 ,0xffffffff); // GICD_ICENABLER
cl_printf("gic_base_addr = %x\n",gic_base_addr);
//init gic distribtor
for(uint32_t i=0;i<30;i++){
REG_WR(GIC_BASE_ADDR + 0x184 + 4*i,0xffffffff); // GICD_ICENABLER
REG_WR(GIC_BASE_ADDR + 0x284 + 4*i,0xffffffff); // GICD_ICPENDR
REG_WR(GIC_BASE_ADDR + 0x84 + 4*i,0xffffffff); // GICD_IGROUPR --> GROUP1
}
// cfg GICD_ICFGR pulse or high level , default 0
REG_WR(GIC_BASE_ADDR ,0x12); // GICD_CTLR enable group1
//init gic redistribtor
REG_WR(GIC_BASE_ADDR + 0x110000 + 0x14 ,0x4); // GICR_WAKER
REG_WR(GIC_BASE_ADDR + 0x110000 + 0x180,0xffffffff); // GICR_ICENABLER
REG_WR(GIC_BASE_ADDR + 0x110000 + 0x280,0xffffffff); // GICR_ICPENDR
REG_WR(GIC_BASE_ADDR + 0x110000 + 0x80 ,0xffffffff); // GICR_IGROUPR --> GROUP1
REG_WR(GIC_BASE_ADDR + 0x110000 + 0xc04,0x0); // GICR_ICFGR
asm("dsb");
//init cpu interface
asm("mov r0, #1");
asm("mcr p15,0,r0,c12,c12,6"); // Write ICC_IGRPEN0 (enable group 0)
asm("mcr p15,0,r0,c12,c12,7"); // Write ICC_IGRPEN1 (enable group 1)
asm("mov r0, #255 ");
asm("mcr p15,0,r0,c4,c6,0 "); // Write ICC_PMR (most permissive)
//asm("mov r0, #49");
//asm("mcr p15,0,r0,c12,c12,1"); // Write ICC_EOIR1 (enable group 0)
//
REG_WR(GIC_BASE_ADDR + 0x104 ,0x20000); // GICD_CTLR enable group1
}
- core在正常运行中,当收到中断后,硬件将自动保存现场(相关寄存器)
- 通过不同的中断进入异常向量表,并跳转到对应的异常处理入口
- 完成中断处理函数(如下所示),并返回现场
//中断响应函数
void do_process()
{
int id_num;
print_string("this is isq\n");
//get_intr_id(id_num);
//read intr
asm ("MRC p15,0,r0,c12,c12,0;" // 保存到r0寄存器
"mov %0, r0;" // r1 = r0
: "=r"(id_num) // 输出r1到result
: );
cl_printf("gic id_num = %d ,case is complete\n",id_num);
//print_string("startrelease\n");
//delay_cnt1(100);
//clean intr
asm ("mov r0, #49;"
"MCR p15,0,r0,c12,c12,1;"
);
print_string("finish\n");
}
4.虚拟化功能
暂不讨论