首页 > 编程知识 正文

ARM GICv3中断控制器,中断允许控制寄存器

时间:2023-05-04 01:56:48 阅读:184697 作者:138

changelog:
2019年02月17日 初稿
2020年03月1日 fix typos以及增加中断路由

1. 前言

GIC,Generic Interrupt Controller。是ARM公司提供的一个通用的中断控制器。主要作用为:
接受硬件中断信号,并经过一定处理后,分发给对应的CPU进行处理。

当前GIC 有四个版本,GIC v1~v4, 主要区别如下表:

本文主要介绍GIC v3控制器, 基于linux kernel 4.19.0。

2. GIC v3中断类别

GICv3定义了以下中断类型:
SPI (Shared Peripheral Interrupt)
公用的外部设备中断,也定义为共享中断。可以多个Cpu或者说Core处理,不限定特定的Cpu。比如按键触发一个中断,手机触摸屏触发的中断。
PPI (Private Peripheral Interrupt)
私有外设中断。这是每个核心私有的中断。PPI会送达到指定的CPU上,应用场景有CPU本地时钟。
SGI (Software Generated Interrupt)
软件触发的中断。软件可以通过写GICD_SGIR寄存器来触发一个中断事件,一般用于核间通信。
LPI (Locality-specific Peripheral Interrupt)
LPI是GICv3中的新特性,它们在很多方面与其他类型的中断不同。LPI始终是基于消息的中断,它们的配置保存在表中而不是寄存器。比如PCIe的MSI/MSI-x中断。

硬件中断号中断类型0-15SGI16 - 31PPI32 - 1019SPI1020 - 1023用于指示特殊情况的特殊中断1024 - 8191Reservd8192 - MAXLPI3. GIC v3组成


GICv3控制器由以下部分组成:
distributor: SPI中断的管理,将中断发送给redistributor
redistributor: PPI,SGI,LPI中断的管理,将中断发送给cpu interface
cpu interface: 传输中断给core
ITS: Interrupt Translation Service, 用来解析LPI中断
其中,cpu interface是实现在core内部的,distributor,redistributor,ITS是实现在gic内部的.

Distributor 详述
Distributor的主要的作用是检测各个interrupt source的状态,控制各个interrupt source的行为,分发各个interrupt source产生的中断事件分发到指定的一个或者多个CPU interface上。虽然Distributor可以管理多个interrupt source,但是它总是把优先级最高的那个interrupt请求送往CPU interface。
Distributor对中断的控制包括:
(1)中断enable或者disable的控制。Distributor对中断的控制分成两个级别。一个是全局中断的控制(GIC_DIST_CTRL)。一旦disable了全局的中断,那么任何的interrupt source产生的interrupt event都不会被传递到CPU interface。另外一个级别是对针对各个interrupt source进行控制(GIC_DIST_ENABLE_CLEAR),disable某一个interrupt source会导致该interrupt event不会分发到CPU interface,但不影响其他interrupt source产生interrupt event的分发。
(2)控制将当前优先级最高的中断事件分发到一个或者一组CPU interface。当一个中断事件分发到多个CPU interface的时候,GIC的内部逻辑应该保证只assert 一个CPU。
(3)优先级控制。
(4)interrupt属性设定。例如是level-sensitive还是edge-triggered
(5)interrupt group的设定
Distributor可以管理若干个interrupt source,这些interrupt source用ID来标识,我们称之interrupt ID。

Redistributor详述
对于每个连接的PE,都有一个Redistributor.
该block的主要功能包括:
(1)启用和禁用SGI和PPI。
(2)设置SGI和PPI的优先级。
(3)将每个PPI设置为电平触发或边缘触发。
(4)将每个SGI和PPI分配给中断组。
(5)控制SGI和PPI的状态。
(6)内存中数据结构的基址控制,支持LPI的相关中断属性和挂起状态。
(7)电源管理支持。

CPU interface详述
CPU interface这个block主要用于和process进行接口。
该block的主要功能包括:
(a)enable或者disable CPU interface向连接的CPU assert中断事件。对于ARM,CPU interface block和CPU之间的中断信号线是nIRQCPU和nFIQCPU。如果disable了中断,那么即便是Distributor分发了一个中断事件到CPU interface,但是也不会assert指定的nIRQ或者nFIQ通知processor。
(b)ackowledging中断。processor会向CPU interface block应答中断(应答当前优先级最高的那个中断),中断一旦被应答,Distributor就会把该中断的状态从pending状态修改成active或者pending and active(这是和该interrupt source的信号有关,例如如果是电平中断并且保持了该asserted电平,那么就是pending and active)。processor ack了中断之后,CPU interface就会deassert nIRQCPU和nFIQCPU信号线。
(c)中断处理完毕的通知。当interrupt handler处理完了一个中断的时候,会向写CPU interface的寄存器从而通知GIC CPU已经处理完该中断。做这个动作一方面是通知Distributor将中断状态修改为deactive,另外一方面,CPU interface会priority drop,从而允许其他的pending的interrupt向CPU提交。
(d)设定priority mask。通过priority mask,可以mask掉一些优先级比较低的中断,这些中断不会通知到CPU。
(e)设定preemption的策略
(f)在多个中断事件同时到来的时候,选择一个优先级最高的通知processor

3. 中断路由

gicv3使用hierarchy来标识一个具体的core, 如下图是一个4层的结构(aarch64)

<affinity level 3>.<affinity level 2>.<affinity level 1>.<affinity level 0> 组成一个PE的路由。
每一个core的affnity值可以通过MPDIR_EL1寄存器获取, 每一个affinity占用8bit.
配置对应core的MPIDR值,可以将中断路由到该core上。

各个affinity的定义是根据SOC自己的定义
比如可能affinity3代表socketid,affinity2 代表clusterid, affnity1代表coreid, affnity0代表thread id.

以gic 设置中断路由为例:
中断亲和性的设置的通用函数为irq_set_affinity, 具体调用如下:

+-> irq_set_affinity()...+-> irq_do_set_affinity()+-> chip->set_affnity()+->gic_set_affinity() static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, bool force){/* If interrupt was enabled, disable it first */enabled = gic_peek_irq(d, GICD_ISENABLER); -------- (1)if (enabled)gic_mask_irq(d);reg = gic_dist_base(d) + GICD_IROUTER + (gic_irq(d) * 8);val = gic_mpidr_to_affinity(cpu_logical_map(cpu)); --------- (2)gic_write_irouter(val, reg); ------ (3)irq_data_update_effective_affinity(d, cpumask_of(cpu));return IRQ_SET_MASK_OK_DONE;}

gic_set_affinity先判断当前中断是否使能,如果使能则disable掉该中断;

然后根据gic_mpidr_to_affinity函数获取需要绑定中断到对应core的路由,

static u64 gic_mpidr_to_affinity(unsigned long mpidr){u64 aff;aff = ((u64)MPIDR_AFFINITY_LEVEL(mpidr, 3) << 32 | MPIDR_AFFINITY_LEVEL(mpidr, 2) << 16 | MPIDR_AFFINITY_LEVEL(mpidr, 1) << 8 | MPIDR_AFFINITY_LEVEL(mpidr, 0));return aff;}

aff 就是通过对应core的MPIDR_EL1寄存器获取affinity0~3的值,并组成一个新的32bit value;

最后将获取的value写进gic irouter寄存器并更新中断的亲和性配置。

4. 中断处理流程


从上图可以看出,中断处理可以分成两类:
(1)中断要通过distributor的中断流程
–>外设发起中断,发送给distributor
–>distributor将该中断,分发给合适的re-distributor
–>re-distributor将中断信息,发送给cpu interface。
–>cpu interface产生合适的中断异常给处理器
–>处理器接收该异常,并且软件处理该中断

它的中断状态机如下图所示

有四种中断状态:

中断状态描述Inactive中断即没有Pending也没有ActivePending由于外设硬件产生了中断事件(或者软件触发)该中断事件已经通过硬件信号通知到GIC,等待GIC分配的那个CPU进行处理ActiveCPU已经应答(acknowledge)了该interrupt请求,并且正在处理中Active and Pending当一个中断源处于Active状态的时候,同一中断源又触发了中断,进入pending状态

processor ack了一个中断后,该中断会被设定为active。当处理完成后,仍然要通知GIC,中断已经处理完毕了。这时候,如果没有pending的中断,GIC就会将该interrupt设定为inactive状态。操作GIC中的End of Interrupt Register可以完成end of interrupt事件通知。

(2)中断不通过distributor,比如LPI中断
外设发起中断,发送给ITS
–>ITS分析中断,决定将来发送的re-distributor
–>ITS将中断发送给合适的re-distributor

4. LPI

LPI是基于消息的中断。中断信息不在通过中断线进行传递,而是通过memory。
gic内部提供一个寄存器,当外设往这个地址写入数据时,就往gic发送了一个中断。
在soc系统中,外设想要发送中断给gic,是需要一根中断线的。如果现在一个外设,需要增加一个中断,那么就要增加一根中断线,然后连接到gic。这样就需要修改设计。而引入了LPI之后,当外设需要增加中断,只需要使用LPI方式,传输中断即可,不需要修改soc设计。

传统的GIC流程:

在传统的GIC流程中如上图,外围设备的中断触发线是引出到GIC上的,这样可以理解为一个物理的SIGNAL,比如一个高电平信号,边沿触发信号。

消息中断流程:

使用消息将中断从外设转发到中断控制器,无需每个中断源提供专用信号。 这样的一个好处是,可以减少中断线的个数。

在GICv3中,SPI可以是基于消息的中断,但LPI始终是基于消息的中断。

5.ITS

引入了LPI之后,gicv3中,还加入了ITS组件,interrupt translation service。ITS将接收到的LPI中断,进行解析,然后发送到对应的redistributor,再由redistributor将中断信息,发送给cpu interface。

外设,通过写GITS_TRANSLATER寄存器,发起LPI中断。写操作,给ITS提供2个信息:
EventID: 值保存在GITS_TRANSLATER寄存器中,表示外设发送中断的事件类型
DeviceID: 表示哪一个外设发起LPI中断。该值的传递,是实现自定义,例如,可以使用AXI的user信号来传递。
ITS将DeviceID和eventID,通过一系列查表,得到LPI中断号,再使用LPI中断号查表,得到该中断的目标cpu。

ITS将LPI中断号,LPI中断对应的目标cpu,发送给对应的redistributor。redistributor再将该中断信息,发送给CPU。

6.参考资料

GICv3_Software_Overview_Official_Release_B

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。