RISCV 中断控制器 PLIC & APLIC (非MSI部分)

以下包含了 PLIC & APLIC
着重解释 APLIC 部分
参考 github 中 riscv-aia 规范1.0 ,第四章 APLIC
注:关于MSI的部分简略(等后续搞清楚)
本人处于学习阶段,不清晰的地方,请见谅

一、关于 PLIC 配置

1、PLIC 代码描述

// PLIC 全局配置
struct plic_global_hw {
    uint32_t prio[PLIC_NUM_PRIO_REGS];               	        // 优先级
    uint32_t pend[PLIC_NUM_PEND_REGS];                        	// 待处理
    uint32_t enbl[PLIC_PLAT_CNTXT_NUM][PLIC_NUM_ENBL_REGS];     // 使能寄存器
} __attribute__((__packed__, aligned(PAGE_SIZE)));

// 每个 hart 私有的 PLIC 配置
struct plic_hart_hw {
    uint32_t threshold;											// 阈值
    union {
        uint32_t claim;											// 中断响应
        uint32_t complete;										// 处理完成
    };
    uint8_t res[0x1000 - 0x0008];								// 保留
} __attribute__((__packed__, aligned(PAGE_SIZE)));

这里有的内容不是说 PLIC 硬件真的有,而是为了对齐,方便操纵,就比如 uint32_t pend[PLIC_NUM_PEND_REGS],实际上 pend 不需要这么多,只是为了能够让后面的 enbl 对齐,采用了这样的操作,中间采取类似plic_hart_hw中的uint8_t res[0x1000 - 0x0008]操作,可以达到同样的效果

这里将plic_global_hwplic_hart_hw分开,使得更灵活,如果能在一个结构体里面对齐,也是可以的

2、PLIC Memory Map

以下的这个内容出自 https://github.com/riscv/riscv-plic-spec/blob/master/riscv-plic-1.0.0.pdf

这是 PLIC 规范中对于内存布局的描述,这个也就决定了上面的代码描述中对齐的方式

base + 0x000000: Reserved (interrupt source 0 does not exist)
base + 0x000004: Interrupt source 1 priority
base + 0x000008: Interrupt source 2 priority
...
base + 0x000FFC: Interrupt source 1023 priority
base + 0x001000: Interrupt Pending bit 0-31
base + 0x00107C: Interrupt Pending bit 992-1023
...
base + 0x002000: Enable bits for sources 0-31 on context 0
base + 0x002004: Enable bits for sources 32-63 on context 0
...
base + 0x00207C: Enable bits for sources 992-1023 on context 0
base + 0x002080: Enable bits for sources 0-31 on context 1
base + 0x002084: Enable bits for sources 32-63 on context 1
...
base + 0x0020FC: Enable bits for sources 992-1023 on context 1
base + 0x002100: Enable bits for sources 0-31 on context 2
base + 0x002104: Enable bits for sources 32-63 on context 2
...
base + 0x00217C: Enable bits for sources 992-1023 on context 2
...
base + 0x1F1F80: Enable bits for sources 0-31 on context 15871
base + 0x1F1F84: Enable bits for sources 32-63 on context 15871
base + 0x1F1FFC: Enable bits for sources 992-1023 on context 15871
...
base + 0x1FFFFC: Reserved
base + 0x200000: Priority threshold for context 0
base + 0x200004: Claim/complete for context 0
base + 0x200008: Reserved
...
base + 0x200FFC: Reserved
base + 0x201000: Priority threshold for context 1
base + 0x201004: Claim/complete for context 1
...
base + 0x3FFF000: Priority threshold for context 15871
base + 0x3FFF004: Claim/complete for context 15871
base + 0x3FFF008: Reserved
...
base + 0x3FFFFFC: Reserved

3、建立映射关系

使得在代码中可以直接 MMIO 方式读写 PLIC 硬件相关内容

// PLIC 全局配置
plic_global = (void*)mem_alloc_map_dev(&cpu()->as, SEC_HYP_GLOBAL, INVALID_VA,
    platform.arch.irqc.plic.base, NUM_PAGES(sizeof(struct plic_global_hw)));

// 每个 hart 私有的配置信息
plic_hart = (void*)mem_alloc_map_dev(&cpu()->as, SEC_HYP_GLOBAL, INVALID_VA,
    platform.arch.irqc.plic.base + HART_REG_OFF,
    NUM_PAGES(sizeof(struct plic_hart_hw) * IRQC_HART_INST));
// 优先级默认全部初始化为 0, 表示关闭中断或者不会触发中断
for (size_t i = 0; i <= PLIC_IMPL_INTERRUPTS; i++) {
        plic_global->prio[i] = 0;
}
// 关闭所有中断使能
for (size_t i = 0; i < PLIC_PLAT_CNTXT_NUM; i++) {
    for (size_t j = 0; j < PLIC_NUM_ENBL_REGS; j++) {
        plic_global->enbl[i][j] = 0;
    }
}
// 取 hart 的 S 态的硬件上下文
cpu()->arch.plic_cntxt = plic_plat_cntxt_to_id((struct plic_cntxt){ cpu()->id, PRIV_S });
// 初始化阈值为 0, 允许所有
plic_hart[cpu()->arch.plic_cntxt].threshold = 0;
static inline void irqc_config_irq(irqid_t int_id, bool en)
{
    plic_set_enbl(cpu()->arch.plic_cntxt, int_id, en);		// 使能
    plic_set_prio(int_id, 0xFE);							// 设置优先级
}

优先级值 0 被保留表示“从不中断”,并且中断优先级随着整数值的增加而增加。

如果 PLIC 支持中断优先级,则可以通过写入其 32 位内存映射优先级寄存器来为每个 PLIC 中断源分配优先级。 优先级值 0 被保留,表示“从不中断”并有效地禁用中断。 优先级 1 是最低的活动优先级,而最高优先级取决于 PLIC 的实现。 相同优先级的全局中断之间的联系由中断 ID 断开; ID 最低的中断具有最高的有效优先级。

虽然 PLIC 的 notification 是多播,但多个中断目标只有一个可以拿到“真的”中断 ID,后发起 claim 的所有中断目标拿到的 ID 都是 0,因为先 claim 的中断目标会清除外部中断源的 pending 状态,使其退出仲裁队列,如果在只有一个外部中断请求的场景中,就会处于没有任何中断请求处于仲裁状态,所有此时 claim,拿到的 ID 是 0, 即没有中断请求待处理。

PLIC 硬件设计不复杂,但预留给软件较大的配置空间,即软件可以通过寄存器访问,实现中断请求过滤,中断响应分配,链式中断响应,嵌套中断响应等等。

二、APLIC 的配置

AIA 新内容,这里暂时不包括 MSI

介绍

在 RISC-V 系统中,【平台级中断控制器(PLIC)负责处理通过导线而非 MSI 发出信号的外部中断】。当系统中的 RISC-V 硬件没有 IMSIC 时,硬件本身也不支持 MSI,此类硬件的所有外部中断都必须通过 PLIC。但是,【即使在有 IMSIC 且大多数中断都通过 MSI 通信的计算机中,某些设备中断仍通过专用线路发出信号的情况也很常见】。特别是对于不需要在系统中启动总线事务的设备(或设备控制器)来说,支持 MSI 的成本尤其高昂,因此有线中断是一种省钱的替代方案。与 MSI 不同的是,有线中断继续得到当前所有计算机平台的普遍支持,这也是许多商品设备或控制器选择有线中断而非 MSI 的另一个原因,除非实施的标准(如 PCI Express)要求使用 MSI。

**本章介绍高级 PLIC(APLIC),它与早期的 RISC-V PLIC 并不向后兼容。**完全符合高级中断体系结构需要 APLIC。不过,如果只对 Harts(而非 MSI)进行有线中断,也可以用较早的 PLIC 代替,建立一个可行的系统。

【在没有 IMSIC 的机器中,每个 RISC-V hart 都接受来自一个 PLIC 或 APLIC 的中断,该 PLIC 或 APLIC 是 hart 的外部中断控制器】。Hart 的外部中断控制器(PLIC 或 APLIC)通过专用连接(通常是导线)向 Hart 发送中断信号,以满足 Hart 可能接收中断的每个特权级(图1.1)。【没有 IMSIC 的系统通常只有一个 PLIC 或 APLIC,作为所有 RISC-V 硬件的外部中断控制器】。
在这里插入图片描述

【采用 IMSIC 作为外部中断控制器的 RISC-V Harts 只能以 MSI 的形式接收外部中断】。在这种情况下,APLIC 的作用就是将有线中断转换为 MSI,供 Harts 使用(图 1.2)。可以说,APLIC 通过向 Harts 发送 MSI,将传入的有线信号中断转发给 Harts。

在这里插入图片描述

当 Harts 拥有支持 MSI 的 IMSIC 时,系统很容易包含多个 APLIC,用于将有线中断转换为 MSI,每个 APLIC 转发来自不同设备子集的中断。当设备组之间物理距离较远,甚至可能位于不同芯片上(包括多芯片模块中的芯片组)时,可能更容易出现多个 APLIC。

中断源和标识

【单个 APLIC 支持固定数量的中断源,与 APLIC 的物理输入中断线完全对应。通常情况下,每个中断源的输入线都与单个设备或设备控制器的输出中断线相连】。(对于电平敏感型中断,可将多个设备或控制器的中断输出组合起来,以驱动 APLIC 上单个中断源的输入线。举例来说,如果中断源始终被配置为 Detached,那么中断源的输入线也可以简单地绑定为高电平或低电平。有关源模式的说明,请参见第 4.5.2 节)。

APLIC 的每个中断源都有一个固定的唯一标识号,范围从 1 到 N,其中 N 是 APLIC 的中断源总数。【在 APLIC 中,0 不是有效的中断标识号。APLIC 最多可支持 1023 个中断源】。

当 APLIC 直接向给定特权级的 Hart 发送中断(而不是作为 MSI 转发中断)时,APLIC 就是该特权级 Hart 的外部中断控制器,APLIC 的中断标识直接成为 Hart 外部中断的次要标识(minor identities)。

另一方面,**当 APLIC 通过 MSI 转发中断时,软件会为每个源的传出 MSI 配置新的中断标识号。**因此,在这种情况下,特定 APLIC 中的源标识号只能区分 APLIC 中的传入中断,与 APLIC 外部无关。

中断域

【APLIC 支持一个或多个中断域,每个中断域与一个特权级(Machine或Supervisor)的 RISC-V Harts 子集相关联】。一个中断域内的 Harts 是该域在相应特权级下可以中断的 Harts。【每个域在机器地址空间中都有自己的内存映射控制区,看似控制一个完整、独立的 APLIC,但实际上所有域接口都一起访问一个组合中断控制器】。

eg: Qemu 中内存布局中包含了 APLIC M-mode 以及 APLIC S-mode,两个中断域

图 4.1 至图 4.3 描述了 RISC-V 系统中 APLIC 可能实现的中断域层次结构。

第一张图表示一个最小的系统,该系统只有一个不支持监督模式的 Hart,该 Hart 上的机器级只有一个中断域。下图 4.2 显示的是为对称多处理(SMP)设计的较大系统的基本安排,该系统有多个均实现了Supervisor模式的 hart。在这种情况下,如图所示,APLIC 通常会为 Supervisor 提供一个单独的中断域。【这种Supervisor中断域允许在多处理器上以 S 模式运行的操作系统直接控制其接收的中断,而无需调用 M 模式来行使控制权】

在这里插入图片描述

在这里插入图片描述

**【APLIC 的中断域以树状层次结构排列,根域始终处于机器级】。**传入的中断线首先到达根域。然后,【每个域可选择性地将全部或部分中断源委托给其在层次结构中的子域】。在给定的 APLIC 中,所有域的中断源编号都是不变的,因此源标识号 i 在每个域中总是指相同的源,与传入线编号 i 相对应。对于根节点以下的中断域,未下放至该域的中断源在该域中显示为未实现。

图 4.3 显示了三个中断域的层次结构,其中两个在Machine级,一个在Supervisor级。图中的安排与 PMP(物理内存保护)相结合,使机器级软件可以隔离一些中断,专供 Hart 0 使用,即使在机器级也不受四个应用 Hart 的影响。

在这里插入图片描述

APLIC 的中断域层次结构符合这些规则:

  • 根域位于机器级。

  • 任何Supervisor级中断域的父域都是一个Machine级中断域,至少包括相同的 harts(但显然是机器级的)。父域在机器级可能有更大的 Harts 集。

  • 【对于每个中断域,来自该中断域的中断信号都是通过线或 MSI 以相同的方式发送给各中断单元的,而不是通过各中断单元之间的混合方式发送】。

当 RISC-V hart 的外部中断控制器是 APLIC 而不是 IMSIC 时,在每个特权级的 hart 处于该 APLIC 的一个中断域中。

另一方面,外部中断控制器为 IMSIC 的 hart 在每个特权级别都可能处于多个 APLIC 中断域中,甚至是同一 APLIC 的中断域中,并有可能接收来自机器中多个不同 APLIC 的 MSI。

平台可能会为软件提供一种方法,在任何给定 APLIC 的多个中断域层次之间进行选择。任何此类可配置性都超出了本规范的范围,但应仅适用于机器级。

单域中断控制概述

每个 APLIC 实现的中断域都有自己独立的物理控制接口,该接口在机器的地址空间中进行内存映射,允许通过 PMP(物理内存保护)和基于页面的地址转换轻松地对每个域的访问进行调控。所有中断域的控制接口都具有相同的结构**。在大多数方面,每个域对软件而言都表现为一个根域,不可见于其上层层次结构中的域。

中断域中的每个中断源具有以下组件:

  • 源配置。这确定了特定中断源源在中断域中是否处于活动状态,以及如果是活动状态,则如何解释传入的信号线,例如电平敏感或边沿敏感。对于在中断域中处于非活动状态的中断源,源配置控制任何委托到子域的情况。
  • 中断挂起和中断使能位。对于非活动源,这两个位都是只读的零。否则,挂起位记录了尚未被发出或转发的到达的中断,而使能位确定当前是否应该传递来自该源的中断,或者应该保持挂起状态。
  • 目标选择。对于一个活动的源,目标选择确定了接收中断的 hart,并且在将其转发为 MSI 时确定中断的优先级或新的中断标识。

对于直接将中断传递给 harts 而不是通过 MSIs 转发的中断域,该域具有一组最终的组件,用于控制将中断传递给 harts,每个域中的 hart 都有一个实例。

(*)APLIC 中断域的内存映射控制区域

【APLIC 支持的每个中断域都有一个专用的内存映射控制区,用于管理该域中的中断】。该控制区域的大小是 4 KiB 的倍数,并与 4 KiB 地址边界对齐。最小的有效控制区域为 16 KiB。中断域的控制区域由一组 32 位寄存器组成。前 16 KiB 包含下表中所列出的寄存器。

【从偏移量 0x4000 开始,中断域的控制区域可选择包含一个中断发送控制(IDC)结构数组】,从 0 到某个最大值(至少与中断域的最大 hart 索引号一样大)范围内的每个潜在 hart 索引号都有一个 IDC 结构。【IDC 结构仅在中断域配置为直接向 hart 发送中断而非由 MSI 转发时使用】。仅支持由 MSI 转发中断而不支持由 APLIC 直接发送中断的中断域不需要在其控制区域中使用 IDC 结构。

第一个 IDC 结构(如果有)用于索引号为 0 的 hart;第二个 IDC 结构用于索引号为 1 的 hart;以此类推。每个 IDC 结构由 32 个字节组成,并具有以下定义的寄存器:

在这里插入图片描述

IDC 结构是连续打包的,每个结构 32 个字节,因此从中断域控制区域的开头到其第二个 IDC 结构(hart 索引 1)(如果存在)的偏移量为 0x4020; 第三个 IDC 结构(hart 索引 2)的偏移量(如果存在)为 0x4040; ETC。

IDC 结构的数组可能包括一些潜在的 hart 索引号,这些索引号并不是域中实际的 hart 索引号。例如,第一个 IDC 结构总是针对 hart 索引 0,但 0 不一定是域中任何 hart 的有效索引号。对于数组中每个不对应于域中有效 hart 索引号的 IDC 结构,IDC 结构的寄存器可能(或可能不)全为只读的零。

除了表 4.1 中的寄存器和上面列出的 IDC 结构寄存器之外,中断域控制区域中的所有其他字节都被保留并实现为只读零。

中断域的控制区域内仅支持自然对齐的 32 位简单读取和写入。 对只读字节的写入将被忽略。 对于其他形式的访问(其他大小、未对齐的访问或 AMO),实现最好应该报告访问错误或总线错误,但必须忽略该访问。

前16 KiB 的中断域控制区域(除IDC结构外)的寄存器如下。IDC 结构将在第 4.8 节“ 由APLIC直接进行中断传递 ”中详细说明。

(*)APLIC 的 MMIO 代码描述
// APLIC 前 16KB 区域
struct aplic_control_hw {
    uint32_t domaincfg;
    uint32_t sourcecfg[APLIC_NUM_SRCCFG_REGS];
    uint8_t reserved1[0x1C00 - 0x1000];
    uint32_t setip[APLIC_NUM_SETIx_REGS];
    uint8_t reserved2[0x1CDC - 0x1C80];
    uint32_t setipnum;
    uint8_t reserved3[0x1D00 - 0x1CE0];
    uint32_t in_clrip[APLIC_NUM_CLRIx_REGS];
    uint8_t reserved4[0x1DDC - 0x1D80];
    uint32_t clripnum;
    uint8_t reserved5[0x1E00 - 0x1DE0];
    uint32_t setie[APLIC_NUM_SETIx_REGS];
    uint8_t reserved6[0x1EDC - 0x1E80];
    uint32_t setienum;
    uint8_t reserved7[0x1F00 - 0x1EE0];
    uint32_t clrie[APLIC_NUM_CLRIx_REGS];
    uint8_t reserved8[0x1FDC - 0x1F80];
    uint32_t clrienum;
    uint8_t reserved9[0x2000 - 0x1FE0];
    uint32_t setipnum_le;
    uint32_t setipnum_be;
    uint8_t reserved10[0x3000 - 0x2008];
    uint32_t genmsi;
    uint32_t target[APLIC_NUM_TARGET_REGS];
} __attribute__((__packed__, aligned(PAGE_SIZE)));

struct aplic_idc_hw {
    uint32_t idelivery;
    uint32_t iforce;
    uint32_t ithreshold;
    uint8_t reserved[0x18 - 0x0C];
    uint32_t topi;
    uint32_t claimi;
} __attribute__((__packed__, aligned(APLIC_IDC_SIZE))); // IDC structure CANNOT
                                                        // be page aligned.

下面是对每部分的介绍:(主要是和直接传递中断方式相关的部分,MSI后续有机会再更新)

1、Domain configuration (domaincfg)

四字节:32位

格式如下:

在这里插入图片描述

所有其他寄存器位都被保留,并读取为零。

【位 IE(中断使能)是该中断域中所有活动中断源的全局使能】。只有当 IE = 1 时,pending-and-enabled的中断才会实际发出信号或转发给 Harts。

【位 DM(发送模式)为 WARL,决定了该中断域向 Harts 发送中断的方式】。DM 的两个可能值是:
0 = 直接发送模式
1 = MSI 发送模式

**在直接发送模式下,中断由 APLIC 自身确定优先级并直接向 Harts 发出信号。**在 MSI 发送模式下,APLIC 将中断作为 MSI 转发到 harts,以便这些 harts 的 IMSIC 进一步处理。给定的 APLIC 实现可为每个中断域支持上述两种或其中一种传递模式。

如果中断域的 hart 具有 IMSIC,则除非这些 IMSIC 的相关中断文件支持寄存器 eidelivery 值 0x40000000,否则将 DM 设置为零(直接传送模式)将与将 IE 设置为零具有相同的效果。 参见第 3.8.1 节和第 4.8.2 节。

【位 BE(大端)是一个 WARL 字段,它确定中断域内存映射控制区域中大多数寄存器的字节顺序】。**如果 BE = 0,则字节顺序为小端,如果 BE = 1,则为大端。**对于仅支持小端的 RISC-V 系统,BE 可能是只读的0,而对于仅支持大端的系统,BE 可能是只读的1。对于双端系统,BE 是可写的。

字段 BE 影响对域配置寄存器的访问字节顺序,就像影响中断域控制区域中其他寄存器一样。为了应对这一事实,在 domaincfg 的最高有效字节,即位 31:24 中的只读值,有两个用途。首先,对于 domaincfg 的任何读取,可以轻松地从获取的四字节值中确定寄存器的正确字节顺序:在正确字节顺序中解释时,位 31 是 1,在错误的顺序中,位 31 是 0。其次,如果 BE 的值不确定(可能在软件初始化中断域之前),则可以通过写入 (x << 24) | x 来安全地将一个 8 位值 x 写入 domaincfg,其中 << 24 表示向左移动 24 位,竖线 (|) 表示按位逻辑或。在 domaincfg 被写入一次后,BE 的值应该是已知的,因此后续的写入不需要重复相同的技巧。

**【在系统复位时,domaincfg 中的所有可写位都被初始化为零,包括 IE】。**如果一个实现支持 APLIC 的其他形式的复位,那么这些其他复位如何影响 domaincfg 是由实现定义的(或可能是由平台定义的)。

2、Source configurations (sourcecfg[1]–sourcecfg[1023])

每个中断源 4 字节,32 位

【对于每个可能的中断源 i,寄存器 sourcecfg[i] 控制该中断域中源 i 的源模式(source mode)以及该源到子域的任何委托】。当 source i 未实现,或出现在该域中未实现时,sourcecfg[i] 为只读零。 如果中断源 i 未委托给该域,然后被更改(在父域)以委托给该域,则 sourcecfg[i] 保持为零,直到使用非零值成功写入为止。

【sourcecfg[i] 的 bit 10 是一个 1 位字段,称为 D(委托)】如果 D = 1,则源 i 被委托给子域,如果 D = 0,则不被委托给子域。 sourcecfg[i] 其余部分的解释取决于字段 D。

当中断源 i 被委托给子域时,sourcecfg[i] 具有以下格式:

​ bit 10 D = 1
​ bits 9:0 Child Index(WLRL)

所有其他寄存器位均被保留并读为零。

【Child Index 是一个 WLRL 字段,它指定该源被委托到的中断域】。 对于具有 C 个子域的中断域,该字段必须能够保存 0 到 C − 1 范围内的整数值。每个中断域都有从这些索引号到子域的固定映射。

如果中断域在域层次结构中没有子域,则该域的任何 sourcecfg 寄存器中的 bit D 不能设置为 1。 对于这样的叶域,尝试使用 bit 10 = 1 的值写入 sourcecfg 寄存器会导致整个寄存器设置为零。

当中断源 i 未委托给子域时,sourcecfg[i] 具有以下格式:

​ bit 10 D = 0
​ bits 2:0 SM (WARL)

【SM(源模式)字段是 WARL,控制中断源在此域中是否处于活动状态】,如果是,则传入线路上的哪些值或转换被解释为中断。 SM 允许的值及其含义列于表 4.2 中。 字段 SM 始终支持非活动(零)。 实现可以独立地为每个中断源自由选择 SM 支持哪些其他值。
在这里插入图片描述

【如果中断源被委派给子域 (D = 1) 或未委派 (D = 0) 并且 SM 处于非活动状态,则中断源在中断域中处于非活动状态】。 每当中断源 i 在中断域中处于非活动状态时,该域内相应的中断待处理位和中断使能位都是只读零,并且寄存器 target[i] 也是只读零。 如果源 i 从非活动模式更改为活动模式,则中断源的挂起位和使能位保持为零,除非由于本节后面或第 4.7 节中指定的原因自动设置,并且 target[i] 的定义子字段获得未指定的值 。

当源配置为 Deteched 时,其连线输入将被忽略; 然而,中断挂起位仍可以通过写入 setip 或 setipnum 寄存器来设置。 (例如,此模式对于接收 MSI 很有用

边沿敏感源可配置为识别上升沿(低到高转换)或下降沿(高到低转换)上的传入中断。 当配置为下降沿(模式 Edge0)时,源被称为反转。

电平敏感源可配置为将线路上的高电平 (1) 或低电平 (0) 解释为中断的断言。 当配置为低电平(模式 Level0)时,源被称为反转。

对于配置为边沿敏感或电平敏感的中断源,定义:

rectified input value = (incoming wire value) XOR (source is inverted).

对于非活动或 Detached 的源,整流后的输入值为零。

如果在新源模式下整流输入值为高电平 (= 1),则对 sourcecfg 寄存器的任何写入都可能(或可能不会)导致相应的中断挂起位设置为 1。 对 sourcecfg 寄存器的写入本身不会导致挂起位被清除,除非源处于非活动状态。 (但请参阅第 4.7 节。)

3、Set interrupt-pending bits (setip[0]–setip[31])

每个中断源 1 位,四字节(32位),对应32个中断源

读或写寄存器 setip[k] 读取或可能修改中断源 k × 32 到 k × 32 + 31 的 pending 位。对于该范围内实现的中断源 i,源 i 的挂起位与寄存器位 (i mod 32)。

【读取 setip 寄存器会返回相应中断源的待处理位】。 结果值中与所实现的中断源不对应的位位置(例如 setip[0] 的bit 0)为零。

在写入 setip 寄存器时,对于写入的 32 位值中为 1 的每一位,如果该位位置对应于活动中断源,则该源的中断挂起位将设置为 1(如果可能)。 请参阅第 4.7 节,了解何时可以通过写入 setip 寄存器来设置挂起位。

4、Set interrupt-enable bits (setie[0]–setie[31])

每个中断源 1 位,四字节(32位),对应32个中断源

读或写寄存器 setie[k] 读取或可能修改中断源 k × 32 到 k × 32 + 31 的使能位。对于该范围内实现的中断源 i,源 i 的使能位与寄存器位 (i mod 32)。

【读取 setie 寄存器将返回相应中断源的使能位】。 结果值中不对应于已实现的中断源的位位置(例如 setie[0] 的位 0)为零。

在写入 setie 寄存器时,对于写入的 32 位值中为 1 的每一位,如果该位位置对应于活动中断源,则该源的中断使能位设置为 1。

5、Interrupt targets (target[1]-target[1023])

每个中断源一个,四字节,32位

如果中断源 i 在此域中不活动,则寄存器 target[i] 为只读零。 【如果源 i 处于活动状态,则 target[i] 确定将来自源的中断发信号通知或转发到哪个 hart】。 target[i]的准确解释取决于寄存器domaincfg的字段DM配置的传递模式。 如果更改了domaincfg.DM,则域内所有活动中断源的目标寄存器将在为新传送模式定义的所有字段中获取未指定的值。

Active source, direct delivery mode

对于活动中断源 i,如果域配置为直接传递模式 (domaincfg.DM = 0),则寄存器 target[i] 具有以下格式:

​ bits 31:18 Hart Index (WLRL)
​ bits 7:0 IPRIO (WARL)
所有其他寄存器位均被保留并读为零。

【Hart Index 是一个 WLRL 字段,指定来自该源的中断将传送到的 Hart】

【IPRIO(中断优先级)字段指定中断源的优先级编号】。 该字段是 IPRIOLEN 位的 WARL 无符号整数,其中 IPRIOLEN 是给定 APLIC 的常量参数,范围为 1 到 8。 IPRIO 只允许使用值 1 到 2^IPRIOLEN - 1,而不是零。 对目标寄存器的写入将 IPRIO 设置为等于写入的 32 位值的位 (IPRIOLEN - 1):0,除非这些位全部为零,在这种情况下,优先级编号将设置为 1。 (如果 IPRIOLEN = 1,这些规则会导致 IPRIO 实际上为只读,值为 1。)

【较小的优先级数字表示较高的优先级。 当中断源具有相同的优先级编号时,具有最低标识号的源具有最高优先级】。

6、Set interrupt-pending bit by number (setipnum)

如果 i 是域中的活动中断源编号,则将 32 位值 i 写入寄存器 setipnum 会导致源 i 的挂起位设置为 1(如果可能)。 请参阅第 4.7 节,了解何时可以通过写入 setipnum 来设置挂起位。

如果写入的值不是域中的活动中断源编号,则对 setipnum 的写入将被忽略。 读取 setipnum 始终返回零。

7、Rectified inputs, clear interrupt-pending bits (in_clrip[0]–in_clrip[31])

读取 in_clrip[k] 中的寄存器会返回中断源 k × 32 到 k × 32 + 31 的校正输入值(第 4.5.2 节),而写入 in_clrip[k] 可能会修改相同源的待处理位。 对于指定范围内已实现的中断源 i,源 i 对应于寄存器位 (i mod 32)。

读取 in_clrip 寄存器会返回相应中断源的校正输入值。 结果值中不对应于已实现的中断源的位位置(例如 in_clrip[0] 中的位 0)为零。

在写入 in_clrip 寄存器时,对于写入的 32 位值中的每一位,如果该位位置对应于活动中断源,则在可能的情况下清除该源的中断挂起位。 请参阅第 4.7 节,了解何时可以通过写入 in_clrip 寄存器来清除挂起位。

8、Clear interrupt-pending bit by number (clripnum)

如果 i 是域中的活动中断源编号,则将 32 位值 i 写入寄存器 clripnum 会导致源 i 的挂起位(如果可能)被清除。 请参阅第 4.7 节,了解何时可以通过写入 clripnum 来清除挂起位。

如果写入的值不是域中的活动中断源编号,则忽略对 clripnum 的写入。 读取 clripnum 始终返回零。

9、Clear interrupt-enable bits (clrie[0]–clrie[31])

写入寄存器 clrie[k] 可能会修改中断源 k × 32 到 k × 32 + 31 的使能位。对于在该范围内实现的中断源 i,源 i 的使能位与寄存器位 (i mod 32) 相对应。

在写入 clrie 寄存器时,对于写入的 32 位值中的每一位,该源的中断启用位都会被清除。

读取 clrie 寄存器始终返回零。

10、Clear interrupt-enable bit by number (clrienum)

如果 i 是域中的活动中断源编号,则将 32 位值 i 写入寄存器 clrienum 会导致源 i 的使能位被清除。

如果写入的值不是域中的活动中断源编号,则忽略对 clrienum 的写入。 读取 clrienum 始终返回零。

11、Set interrupt-pending bit by number, little-endian (setipnum_le)

寄存器 setipnum_le 的行为与 setipnum 相同,只是字节顺序始终为小端,就好像寄存器 domaincfg 的字段 BE(大端)为 0 一样。

对于仅使用 big-endian 的系统,domaincfg.BE 硬连线为 1,不需要实现 setipnum_le,在这种情况下,该偏移处的四个字节只是只读零,就像其他保留字节一样。

setipnum_le 可用作 MSI 的写入端口。

12、Set interrupt-pending bit by number, big-endian (setipnum_be)

寄存器 setipnum_be 的作用与 setipnum 相同,只是字节顺序始终是大端字节序,就好像寄存器 domaincfg 的字段 BE(大端字节序)是 1 一样。

对于仅使用小端序的系统,domaincfg.BE 硬连线为 0,不需要实现 setipnum_be,在这种情况下,该偏移处的四个字节只是只读零,就像其他保留字节一样。

对于主要为大端字节顺序构建的系统,setipnum_be 作为某些设备的 MSI 的写入端口可能很有用。

Precise effects on interrupt-pending bits

对中断挂起位的精确影响

【尝试通过写入中断域控制区域中的寄存器来设置或清除中断源的挂起位可能会成功,也可能不会成功,具体取决于相应的中断源模式、中断域的传送模式以及源整流输入的状态值】(第 4.5.2 节中定义)。 下面列举了针对给定源模式设置或清除待处理位时的所有情况。

如果源模式为Detached:

  • 仅通过对 setip 或 setipnum 寄存器进行相关写入,挂起位才会设置为 1。
  • 当在 APLIC 处声明中断或由 MSI 转发中断,或通过对 in_clrip 寄存器或 clripnum 进行相关写入时,将清除挂起位。

如果源模式为 Edge1 或 Edge0:

  • 通过整流输入值中从低到高的转换,或者通过对 setip 或 setipnum 寄存器进行相关写入,将挂起位设置为 1。
  • 当在 APLIC 处声明中断或由 MSI 转发中断,或通过对 in_clrip 寄存器或 clripnum 进行相关写入时,将清除挂起位。

如果源模式为Level1或Level0并且中断域配置为直接传递模式(domaincfg.DM = 0)

  • 【每当整流输入值为高时,待处理位就设置为 1。 无法通过写入 setip 或 setipnum 寄存器来设置挂起位】。
  • 只要整流后的输入值较低,待处理位就会被清除。 挂起位不能通过 APLIC 处的中断声明来清除,也不能通过写入 in_clrip 寄存器或 clripnum 来清除。

如果源模式为 Level1 或 Level0 并且中断域配置为 MSI 传递模式 (domaincfg.DM = 1):

  • 待处理位通过整流输入值从低到高的转变设置为 1。 当经整流的输入值较高时,但当经整流的输入值较低时,也可以通过对 setip 或 setipnum 寄存器的相关写入来设置挂起位。
  • 每当整流后的输入值较低、由 MSI 转发中断或通过对 in_clrip 寄存器或 clripnum 进行相关写入时,挂起位就会被清除。

除了上述规则之外,对 sourcecfg 寄存器的写入可能会导致源的中断挂起位设置为 1,如第 4.5.2 节中所述。

由 APLIC 直接中断传送

【当中断域处于直接传递模式 (domaincfg.DM = 0) 时,中断通过每个 hart 的唯一信号(通常是专用线路)从 APLIC 传递到 hart。在这种情况下,域的内存映射控制区域在末尾包含一组中断传送控制 (IDC) 结构,每个潜在的 hart 索引一个 IDC 结构】。 第一个IDC结构是索引为0的域的hart; 第二个是索引为 1 的 hart; ETC。

Interrupt delivery control (IDC) structure

每个 IDC 结构都是 32 字节(自然对齐到 32 字节地址边界)并具有以下定义的寄存器:

在这里插入图片描述

如果 IDC 结构针对的 hart 索引号对于中断域中的任何实际 hart 都无效,则这些寄存器可以选择全部为只读零。 否则,寄存器将在下面单独记录。

Interrupt delivery enable (idelivery)

【idelivery 是一个 WARL 寄存器,用于控制是否将针对相应 hart 的中断传递到 hart】,以便它们在 hart 的 mip CSR 中显示为待处理中断。 当前仅为 idelivery 定义了两个值:

​ 0 = interrupt delivery is disabled
​ 1 = interrupt delivery is enabled

如果 IDC 结构用于不存在的 hart(即,对应于对中断域中的任何实际 hart 无效的 hart 索引号),则将 idelivery 设置为 1 不会将中断传送到任何 hart。

Interrupt force (iforce)

【iforce 是一个可用于测试的 WARL 寄存器】。 仅允许使用值 0 和 1。 【只要 domaincfg 的 IE 字段为1并且 idelivery 寄存器启用到 hart 的中断传送,设置 iforce = 1 就会强制向相应的 hart 发出中断】。 当 topi 为零,这会为 hart 产生一个虚假的外部中断。

当读取寄存器 claimi 返回中断标识零(指示虚假中断)时,iforce 自动清零。

Interrupt enable threshold (ithreshold)

【ithreshold 是一个 WLRL 寄存器,用于确定要向相应 hart 发出信号的中断的最小中断优先级(最大优先级编号)】。 寄存器 ithreshold 精确实现 IPRIOLEN 位,因此能够保存从 0 到 2^IPRIOLEN − 1 的所有优先级编号。

当 ithreshold 为非零值 P 时,优先级编号为 P 及更高的中断源不会向 hart 发送中断信号,就好像这些源未启用一样,无论其中断启用位的设置如何。 【当 ithreshold 为零时,所有启用的中断源都可以向 hart 发出中断信号】

小心求证,PLIC 中断优先级和 APLIC 的中断优先级是反过来的,PLIC 编号越大,优先级越高,APLIC 编号越大,优先级越低

Top interrupt (topi)

【topi 是一个只读寄存器,其值指示当前针对此 hart 的最高优先级 pending-and-enabled 的中断,该中断也超过由 ithreshold 指定的优先级阈值(如果不为零)】。

如果没有针对此 hart 的中断 pending 且 enabled,或者如果 ithreshold 不为零并且没有针对此 hart 的 pending-and-enabled 的中断的优先级编号小于 ithreshold 的值,则 topi 的读取返回零 。 否则,从 topi 读取返回的值具有以下格式:

​ bits 25:16 Interrupt identity (source number)
​ bits 7:0 Interrupt priority

所有其他寄存器位均被保留并读为零。

topi 中报告的中断标识是目标 hart 处的外部中断的次要标识。

对 topi 的写入将被忽略。

Claim top interrupt (claimi)

【寄存器claimi 与topi 具有相同的值】。 当该值不为零时,如果可能的话,读取 Claimi 会同时产生清除所报告中断标识的挂起位的副作用。 请参阅第 4.7 节,了解读取 Claimi 时清除挂起位的确切时间。

从 claimi 读取返回零值的同时会产生将 iforce 寄存器设置为零的副作用。

对 claimi 的写入将被忽略。

Interrupt delivery and handling

当配置中断域以使 APLIC 直接向 HART 传送中断时(domaincfg 的字段 DM 为零),APLIC 在域的特权级别为该域的所有 HART 提供外部中断信号,只要其中之一 以下情况属实:(a) hart 没有 IMSIC,或 (b) 相关 IMSIC 中断文件的 eidelivery 寄存器设置为 0x40000000(第 3.8.1 节)。 对于机器级域,来自 APLIC 的中断信号在每个 hart 的 mip CSR 中显示为位 MEIP(机器外部中断挂起)。 对于superviosr级域,中断信号在每个 hart 的 mip 和 sip CSR 中显示为 SEIP 位(主管外部中断挂起)。 每个中断信号可以任意延迟从 APLIC 传输到适当的硬件。

在 APLIC,发送给 hart 的每个中断信号均源自寄存器 domaincfg 的 IE 字段以及该域的内存映射控制区域中 hart IDC 结构的当前状态。 如果domaincfg.IE = 0 或通过idelivery 寄存器禁用到hart 的中断传送(idelivery = 0),则中断信号将保持无效状态。 当domaincfg.IE = 1并且中断传递被使能(idelivery = 1)时,只要寄存器iforce或topi不为零,中断信号就会被置位。

由于 APLIC 和 Hart 之间的通信可能存在延迟,因此可能会发生外部中断陷阱,但当实际发生对 Hart 的 claimi 寄存器的读取时,并没有为 Hart 挂起和启用任何中断。 在这种情况下,声明报告的中断标识将为零,从而导致来自 APLIC 的明显虚假中断。 便携式软件必须做好应对 APLIC 上可能出现的虚假中断的准备,这种情况可以安全地被忽略,而且应该很少见。 出于测试目的,可以通过将 IDC 结构的 iforce 寄存器设置为 1 来触发 Hart 的虚假中断。

仅用于通过 APLIC 进行外部中断的陷阱处理程序可大致编写如下:

save processor registers
i = read register claimi from the hart’s IDC structure at the APLIC
i = i>>16
call the interrupt handler for external interrupt i (minor identity)
restore processor registers
return from trap

为了解决虚假中断,该伪代码假设有一个“外部中断 0”的中断处理程序,但它不执行任何操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值