RISC-V APLIC 中断模块总结——第二篇

03 · 关键寄存器详解

APLIC 寄存器按功能分成几大区。日常配置中断只用到其中一小部分,其余是版本/安全/诊断寄存器。先看"必须重点关注的",再附完整映射表。

基地址:APLIC1 = 0xF0A00000,APLIC2 = 0xF0A20000 每个 APLIC 实例都有独立的 M/S0/S1 三个域,所以同一个寄存器在三个域各有三份。


3.1 必须重点关注的寄存器(Top 8)

APLIC_CTL(偏移 0x0020,读/写,复位 0x0000_0000)

全局控制寄存器,初始化时配一次即可。

|---|-----------|----------------------------------------------------|
| 位 | 名称 | 说明 |
| 7 | PTY_M | 优先级仲裁模式:0=基于域优先级(默认);1=基于 Hart 公平轮询 |
| 1 | E_CPC | 使能 CPC 接口(基于 master ID 的寄存器访问保护):0=禁用(默认);1=启用 |

APLIC_M/S0/S1_DOMAIN_CFG(偏移 0x8000 / 0xC000 / 0x10000,复位 0x8000_0004)

每个域的"总开关"。

|---|--------|--------------------------------|
| 位 | 名称 | 说明 |
| 8 | IE | 该域所有中断的全局使能1=使能,0=关闭 |
| 2 | DM | 中断传递模式:1=MSI(本芯片固定) |
| 0 | BE | 大小端访问:0=小端,1=大端 |

代码里初始化时先写 0 关掉所有域,配完 CTL 后统一写 IE=1 使能。

APLIC_M/S0/S1_SOURCECFG_N(偏移 0x8000 + N*4,每个源一个)

配置中断源的核心寄存器,N 为中断源编号(APLIC1: 11023,APLIC2: 1256)。

|-----|--------|----------------------------------------|
| 位 | 名称 | 说明 |
| 10 | D | 委托位(只有 M 域有 ,S0/S1 域无此位):1=委托给子域 |
| 2:0 | SM | 见下表 |

SM 字段含义(D=0 时,设置触发方式):

|------|----------------------|
| SM 值 | 含义 |
| 0x0 | 中断无效(inactive) |
| 0x1 | 中断卸载(detached,不接任何域) |
| 0x4 | 上升沿触发(rising edge) |
| 0x5 | 下降沿触发(falling edge) |
| 0x6 | 高电平触发(high level) |
| 0x7 | 低电平触发(low level) |

SM 字段含义(D=1 时,设置委托目标):

|------|------------------------|
| SM 值 | 含义 |
| 0x0 | 委托到 Supervisor0 Domain |
| 0x1 | 委托到 Supervisor1 Domain |

⚠️ 手册位域说明里一句写"当 D 为 1 时,SM 比特位无效",另一句又写"D=1 时 SM 用于设置委托目标(0→S0、1→S1)",前后矛盾 。以代码为准:D=1 时 SM 的低位才是委托目标 (代码宏 APLIC_DELEGATE_M_TO_S0=0APLIC_DELEGATE_M_TO_S1=1)。

⚠️ 手册寄存器图把 SM 标成 SM[1:0](2 bit),但取值有 0x4~0x7,实为 3 bit 。以代码 aplic_reg_riscv.h 为准。

"委托"到底是什么意思?(结合 01 篇 1.4)

  • 委托是单向 的:只有 M 域SOURCECFG 有 D 位,所以只能 M → S0 或 M → S1 。S0/S1 域的 SOURCECFG 没有 D 位,不能继续往下委托。
  • 一个源被 M 域 D=1 委托后,它就不再在 M 域产生中断 (M 域的 pending 不再置位),而是流转到指定的子域(S0 或 S1),由该子域的 SOURCECFG.SM(触发方式)+ TARGET(目标)+ SETIENUM(使能)真正完成配置。
  • 换句话说:S0/S1 域能处理的中断,其源都必须先在 M 域被委托下来。M 域是"总闸",由最高特权级的 M 软件决定每个源归谁。

APLIC_M/S0/S1_TARGET_N(偏移 0xB000 + N*4

指定该中断源"投递给谁、以什么 EIID",也是配置中断的核心。

|-------|----------------|---------------------------------|
| 位 | 名称 | 说明 |
| 20:18 | Hart_Index | 目标 hart(有效 03,对应 Core0Core3) |
| 15:13 | Context | DMSI 上下文(Guest Index),普通场景填 0 |
| 7:0 | EIID | 投递的外部中断标识,同时决定该中断的优先级(越小越高) |

代码中只用到 HART_INDEX(3 bit)和 EIID(6 bit,值 1~63)两个字段,Context 填 0。

每个源都能投给任意 hart 吗? 能。每个源都有自己独立的 TARGET_NHart_Index 由软件写 0~3,APLIC 机制上不把源固定绑定到某个核(手册第 7.3.7.2 节的中断源表也只标注"所属 APLIC"和"内部中断号",没有"绑定核"字段)。实际使用只需满足两点:① 目标 hart 处于可接收 MSI 的状态(有 credit、未掉电);② 该 hart 上已正确配置了对应 EIID 的核内使能位和向量表条目。

⑤ 挂起位组 SETIPx / SETIPNUM / CLRIPx / CLRIPNUM

|----------------------------------------|-----------------------|
| 寄存器 | 作用 |
| SETIPx(0x9C00 + x*4,APLIC1 x=0~31) | 位图方式置挂起(1 bit = 1 个源) |
| SETIPNUM(0x9CDC) | 写中断编号,置单个挂起位 |
| CLRIPx(0x9D00 + x*4) | 位图方式清挂起 |
| CLRIPNUM(0x9DDC) | 写中断编号,清单个挂起位 |

软件可用 SETIPNUM 模拟一次"软件中断"。

⑥ 使能位组 SETIEx / SETIENUM / CLRIEx / CLRIENUM

|-------------------------|-----------------|
| 寄存器 | 作用 |
| SETIEx(0x9E00 + x*4) | 位图方式使能中断 |
| SETIENUM(0x9EDC) | 写中断编号,使能单个中断源 |
| CLRIEx(0x9F00 + x*4) | 位图方式关闭使能 |
| CLRIENUM(0x9FDC) | 写中断编号,关闭单个中断源使能 |

代码初始化时用 SETIENUM = 源编号 使能;Suspend/Resume 时用 CLRIENUM / SETIENUM

APLIC_M/S0/S1_GENMSI(偏移 0xB000)

软件主动生成一条 MSI(模拟向某个 hart 发中断)。

|-------|------------|-----------------|
| 位 | 名称 | 说明 |
| 20:18 | Hart_Index | 目标 hart |
| 15:13 | Context | Guest Index |
| 12 | BUSY | 写寄存器置 1,发送完成清 0 |
| 7:0 | EIID | 要发送的 EIID |

⑧ 安全寄存器块(偏移 0x4000 起,功能安全用)

|----------------|--------|-------------------------------------------------|
| 寄存器 | 偏移 | 说明 |
| SFTY_PASSWD | 0x4000 | 安全寄存器写保护口令 |
| SFTY_CTRL | 0x4004 | 锁步使能 LSE 等(代码初始化用 APLIC_SFTY_LOCKSTEP_ENABLE) |
| SFTY_DIAG | 0x4008 | 诊断错误注入控制 |
| SFTY_BUILD | 0x400C | 安全版本信息(只读) |
| SFTY_ERR_STA | 0x4040 | 安全错误状态(只读,粘性) |
| SFTY_LAT_ERR | 0x4044 | 潜在错误状态(只读) |


3.2 完整寄存器映射表(APLIC1 为例)

|----------------|----------------------|-----------------------------------------|-------------|
| 偏移 (Hex) | 寄存器 | 说明 | 复位值 |
| 0x0000 | APLIC_BUILD_1 | 版本(只读) | 0x0000_0001 |
| 0x0004 | APLIC_BUILD_2 | 能力:ND/MSTY/DCE/SFTE/CPCE/INTTP/NUM_IRQS | 0x03FF_10D2 |
| 0x0008 | APLIC_BUILD_3 | 能力:NUM_EIID/NUM_CNTXT/NUM_HART | 0x0020_4100 |
| 0x0020 | APLIC_CTL | PTY_M / E_CPC | 0x0000_0000 |
| 0x0030 | APLIC_NOC_ERR | NoC 接口错误状态 | 0x0000_0000 |
| 0x0060 | APLIC_DOMAIN_TDM | 域时间分配选择 | 0x0000_0000 |
| 0x0070 | APLIC_OVRRUN | 溢出状态(读/清) | 0x0000_0000 |
| 0x4000~0x4044 | 安全寄存器块 | 见 3.1 ⑧ | --- |
| M 域 | | | |
| 0x8000 | M_DOMAIN_CFG | 机器域配置 | 0x8000_0004 |
| 0x8000+N*4 | M_SOURCECFG_N | 源 N 配置(N=1~1023) | 0x0000_0000 |
| 0x9C00~ | M_SETIP0..31 | 置挂起位图 | 0x0000_0000 |
| 0x9CDC | M_SETIPNUM | 置挂起(编号) | 0x0000_0000 |
| 0x9D00~ | M_CLRIP0..31 | 清挂起位图 | 0x0000_0000 |
| 0x9DDC | M_CLRIPNUM | 清挂起(编号) | 0x0000_0000 |
| 0x9E00~ | M_SETIE0..31 | 使能位图 | 0x0000_0000 |
| 0x9EDC | M_SETIENUM | 使能(编号) | 0x0000_0000 |
| 0x9F00~ | M_CLRIE0..31 | 关使能位图 | 0x0000_0000 |
| 0x9FDC | M_CLRIENUM | 关使能(编号) | 0x0000_0000 |
| 0xB000 | M_GENMSI | 生成 MSI | 0x0000_0000 |
| 0xB000+N*4 | M_TARGET_N | 目标(N=1~1023) | 0x0000_0000 |
| S0 域 | (同 M 域结构,偏移 +0x4000) | | |
| 0xC000 | S0_DOMAIN_CFG | 监管者域0 配置 | 0x8000_0004 |
| 0xF000 | S0_GENMSI | 生成 MSI | 0x0000_0000 |
| S1 域 | (同 M 域结构,偏移 +0x8000) | | |
| 0x10000 | S1_DOMAIN_CFG | 监管者域1 配置 | 0x8000_0004 |
| 0x13000 | S1_GENMSI | 生成 MSI | 0x0000_0000 |

S0/S1 域的 SOURCECFG / SETIP / CLRIP / SETIE / CLRIE / TARGET 与 M 域同名同结构,仅偏移整体后移 0x4000 / 0x8000。完整三域结构见代码 aplic_reg_riscv.hstruct APLIC_tag


3.3 寄存器结构在代码中的对应

寄存器定义文件:Source/mcal/eclipse/plugins/Base_ARCH3DXXM10I0RX/Hardware/include/module_riscv/aplic_reg_riscv.h

复制代码
```c
struct APLIC_tag {
    APLIC_BUILD_1_tag BUILD_1;   /* 0x0000 */
    ...
    APLIC_CTL_tag CTL;           /* 0x0020 */
    ...
    struct { /* Machine Domain 0x8000-0xBFFF */
        APLIC_DOMAIN_CFG_tag DOMAIN_CFG;              /* 0x8000 */
        APLIC_SOURCECFG_N_tag SOURCECFG[1023];        /* 0x8004+... */
        APLIC_SETIP_X_tag SETIP[17];                  /* 0x9C00 */
        APLIC_SETIPNUM_tag SETIPNUM;                  /* 0x9CDC */
        APLIC_CLRIP_X_tag CLRIP[17];                  /* 0x9D00 */
        ...
        APLIC_GENMSI_tag GENMSI;                      /* 0xB000 */
        APLIC_TARGET_N_tag TARGET[1023];              /* 0xB004+... */
    } M_DOMAIN;
    struct { /* S0 Domain 0xC000-0xFFFF */ ... } S0_DOMAIN;
    struct { /* S1 Domain 0x10000-0x13FFF */ ... } S1_DOMAIN;
};
```

⚠️ 代码 SOURCECFG[1023]TARGET[1023] 是"上限 1023",但 4046S 的 APLIC2 实际只有

路源(APLIC1 才是 1023 路),配置时注意不要越界。

04 · 代码配置与常用方式

本篇基于 AUTOSAR 4.4.0 MCAL 的 Irq 模块Source/mcal/eclipse/plugins/Irq_ARCH3DXXM10I0RX/),讲清楚"怎么配一个中断、初始化流程长什么样、有哪几种常用方式"。


4.1 代码文件地图

|---------------------------------------------------|------------------------------------|
| 文件 | 作用 |
| include/Irq.h | Irq 模块头:API、配置结构体、APLIC 实例/域索引、锁步宏 |
| source/Irq.c | APLIC 驱动实现:初始化、源配置、Suspend/Resume |
| source/intc_lld.c / include/intc_lld.h | 老式 INTC(PPC 风格)驱动(嵌套兼容用) |
| Hardware/include/module_riscv/aplic_reg_riscv.h | APLIC 寄存器结构体定义 |
| Hardware/include/module_riscv/intc_reg_riscv.h | 老式 INTC 寄存器定义 |
| config/Irq_APLIC_ccrc4045s.xdm | APLIC 配置项定义(EB tresos) |
| mcal_cfg/CCRC4045S/source/Irq_Cfg.c | 实际生成的中断配置(源→EIID→handler 映射) |
| common/vector_riscv/major_interrupt.c | 外部中断向量入口(读 topei + 跳转) |


4.2 配置一个中断需要哪几个信息

每个中断源在配置里就是一个 Irq_SourceListConfigType 结构体,需要 4 个字段:

复制代码

|-----------------|--------------------|--------------------------------|
| 字段 | 取值 | 说明 |
| IrqEiid | M 模式 163,S 模式 1255 | 决定优先级,同一核内不能重复 |
| IrqSourceId | 1~1023 | 硬件中断源号,全芯片唯一,对应某个外设的一路中断请求 |
| IrqSourceMode | 见下 | 触发方式 |
| IrqHandler | 函数名 | 中断服务函数 |

IrqSourceId 不是"引脚编号"。它是 APLIC 的中断源编号 ,对应的是"某外设产生的某一路中断请求"(例如手册第 7.3.7.2 节的中断源表里,源 256 对应 PIT0 的 CH0 中断请求,源 990 对应 ADC01 的 GRP0)。一个外设往往有多路中断源(PIT 有多个通道、ADC 有多个组),每路各占一个源号,这些源号是芯片设计时固定分配好的,不是可自由接到任意引脚的。

IrqSourceMode 枚举值:

复制代码
```c
typedef enum {
    IRQ_SM_INACTIVE     = 0x0,  /* 无效 */
    IRQ_SM_DETACHED     = 0x1,  /* 卸载 */
    IRQ_SM_RISING_EDGE  = 0x4,  /* 上升沿 */
    IRQ_SM_FALLING_EDGE = 0x5,  /* 下降沿 */
    IRQ_SM_HIGH_LEVEL   = 0x6,  /* 高电平 */
    IRQ_SM_LOW_LEVEL    = 0x7   /* 低电平 */
} Irq_SourceModeType;
```

触发方式到底起什么作用? 它告诉 APLIC:这个外设的中断信号是什么形态 (电平还是边沿),APLIC 据此去采样并锁存 pending 位。配置必须与外设实际输出的中断信号形态匹配,否则会漏中断或反复触发:

|-----------|----------------------------------------|---------------------------------------------|
| 触发方式 | 适合的信号 | 行为 |
| 高电平 / 低电平 | 电平型中断(外设置标志位后,中断线一直保持有效电平,直到软件清标志) | 电平有效期间持续算"有中断";软件清 pending 后若电平仍有效会再次触发 |
| 上升沿 / 下降沿 | 脉冲/边沿型中断(一个短暂脉冲表示事件,脉冲很快消失) | 检测到跳变沿后锁存 pending,脉冲消失后 pending 仍保留,直到软件清 |

PIT 为例:PIT 计数到 0 时把中断标志位 TIF 置 1,这个标志保持高电平直到软件写 1 清除 (手册 40.4.2),所以 PIT 是电平型 中断。因此代码示例里 PIT 配的是 IRQ_SM_HIGH_LEVEL(高电平触发)------这是正确匹配。

如果给 PIT 配成"上升沿触发"会怎样? 边沿触发只认 0→1 的跳变瞬间:

  • 若 APLIC 使能该源时 TIF 已经是 1(比如中断早就产生了),就采不到这个上升沿,这次中断直接丢失;
  • 之后只有等标志被清成 0 再重新置 1,才会产生新的跳变沿触发。

所以电平型中断配成边沿触发是错误的(漏中断)。反过来,短暂脉冲型中断配成电平触发也可能漏(脉冲太短、电平期间没被采样到)。

上升沿和下降沿响应时间不一样吗? 不会。边沿/电平的响应延迟都取决于 APLIC 的采样同步时钟(几个时钟周期内识别),不取决于沿的方向 。上升沿 vs 下降沿只是选择"关注哪个跳变极性",用来匹配外设信号的极性(比如低有效的中断线用下降沿/低电平)。两者的真正区别是语义:边沿 = 事件(脉冲过后仍记住),电平 = 状态(持续有效),而不是"谁更快"。

实际配置示例(Irq_Cfg.c

复制代码
```c
Irq_SourceListConfigType Irq_Core0MmodeSrcListCfgArray[] = {
    { .IrqEiid = EIID_1,  .IrqSourceId = 256, .IrqSourceMode = IRQ_SM_HIGH_LEVEL, .IrqHandler = &PIT0_CH0_ISR },
    { .IrqEiid = EIID_52, .IrqSourceId = 990, .IrqSourceMode = IRQ_SM_HIGH_LEVEL, .IrqHandler = &ADC01_GRP0 },
    { .IrqEiid = EIID_53, .IrqSourceId = 992, .IrqSourceMode = IRQ_SM_HIGH_LEVEL, .IrqHandler = &ADC01_GRP2 },
    ...
};
```

含义:Core0 上,PIT0 通道 0 的中断源(硬件源 256)被赋予 EIID=1(最高优先级),高电平触发,ISR 是 PIT0_CH0_ISR


4.3 初始化流程(Irq_Init → 内部逐层配置)

Irq_Init() 调用链:Irq_InitIrq_sInitIrq_sCoreModeInitIrq_sInterruptSourceInit

硬件实例初始化(主核执行)

  1. 关掉 M/S0/S1 三个域的全局使能(DOMAIN_CFG = 0)。
  1. APLIC_CTLE_CPC(默认关)+ PTY_M(默认域优先级)。
  1. APLIC_SFTY_CTRL:锁步使能。
  1. 重新使能三个域(DOMAIN_CFG.IE = 1)。

每个中断源的初始化(Irq_sInterruptSourceInit

对每个配置的中断源,按以下顺序操作:

复制代码
```c
/* 1. 设置向量表:把 ISR 地址填到 m_AplicIsrVectorTable[core][eiid] */
m_AplicIsrVectorTable[CoreId][eiidIdx] = (uint32)IrqSrcListCfgPtr->IrqHandler;

/* 2. 在核内使能该 EIID(写核内 IMSIC 的使能位) */
Hart_M_ExtInterruptEnable(eiidIdx);

/* 3. 配置源触发方式(SOURCECFG.SM) */
APLIC1_PTR->M_DOMAIN.SOURCECFG[IrqSrcIdIdx - 1].R = sourceCfgVal;

/* 4. 配置目标(TARGET:HART_INDEX + EIID) */
APLIC1_PTR->M_DOMAIN.TARGET[IrqSrcIdIdx - 1].R = targetCfgVal;

/* 5. 使能该中断源 */
APLIC1_PTR->M_DOMAIN.SETIENUM.R = IrqSrcIdIdx;
```

关键代码片段(Irq.c):

复制代码
```c
sourceCfgVal = (uint32)((uint32)IrqSrcListCfgPtr->IrqSourceMode & APLIC_SOURCECFG_N_SM_MASK);
targetCfgVal = APLIC_TARGET_N_HART_INDEX(CoreId) | (eiidIdx & APLIC_TARGET_N_EIID_MASK);
...
if (IrqSrcIdIdx <= 512u) {                    /* 源 1~512 → APLIC1 */
    APLIC1_PTR->M_DOMAIN.SOURCECFG[IrqSrcIdIdx - 1].R = sourceCfgVal;
    APLIC1_PTR->M_DOMAIN.TARGET[IrqSrcIdIdx - 1].R = targetCfgVal;
    APLIC1_PTR->M_DOMAIN.SETIENUM.R = IrqSrcIdIdx;
} else if (IrqSrcIdIdx < 1024u) {             /* 源 513~1023 → APLIC2 */
    APLIC2_PTR->M_DOMAIN.SOURCECFG[IrqSrcIdIdx - 512 - 1].R = sourceCfgVal;
    APLIC2_PTR->M_DOMAIN.TARGET[IrqSrcIdIdx - 512 - 1].R = targetCfgVal;
    APLIC2_PTR->M_DOMAIN.SETIENUM.R = IrqSrcIdIdx - 512u;
}
```

⚠️ 这里的 512 分界是代码(面向 4045S/4086S)写死的;4046S 手册是 APLIC1=1023 / APLIC2=256,需核对是否适配。

Suspend / Resume

复制代码
```c
void Irq_SuspendInterruptSource(Irq_InterruptSrcIdType SourceId);  /* 关闭某中断源 */
void Irq_ResumeInterruptSource (Irq_InterruptSrcIdType SourceId);  /* 恢复某中断源 */
```

内部对应写 CLRIENUM(关)和 SETIENUM(开),并维护 Irq_InterruptSourceStatus[] 状态数组。


4.4 硬件实例配置项

复制代码
```c
Irq_HwInstConfigType Irq_HwInstConfig = {
    .IrqMasterCore   = IRQ_MASTER_COREID,                    /* 主核:负责配 APLIC 全局寄存器 */
    .IrqCpcEn        = FALSE,                                /* CPC 访问保护:默认关 */
    .IrqPriorityMode = IRQ_PRIORITY_MODE_DOMAIN_PRIORITY,    /* 域优先级仲裁(默认) */
    .IrqLockstepEn   = TRUE                                  /* 锁步比较:默认开 */
};
```

|-------------------|---------------------------------------------------|
| 配置项 | 含义 |
| IrqCpcEn | 是否启用 CPC(master ID 访问保护),普通场景 FALSE |
| IrqPriorityMode | DOMAIN_PRIORITY(域优先)或 HART_FAIR_RR(hart 公平轮询) |
| IrqLockstepEn | 是否开启锁步比较(功能安全) |


4.5 三种常用配置方式

方式一:M 模式直接处理(最常用,默认)

中断直接归属 M 域 ,ISR 在 M 模式运行。代码里 IrqTargetCoreMode = HART_M_MODE

复制代码
```c
/* 源 → M 域 SOURCECFG 直接配触发方式,TARGET 配 hart+EIID,SETIENUM 使能 */
APLIC1_PTR->M_DOMAIN.SOURCECFG[src-1].R = sourceCfgVal;
APLIC1_PTR->M_DOMAIN.TARGET[src-1].R   = targetCfgVal;
APLIC1_PTR->M_DOMAIN.SETIENUM.R        = src;
```

方式二:S 模式处理(需要委托 M→S0)

中断先归 M 域,再委托给 S0 域 ,让 S 模式(通常是 OS)接管。代码里 IrqTargetCoreMode = HART_S_MODE

复制代码
```c
/* M 域:置 D 位 + SM=0,表示"委托到 S0" */
APLIC1_PTR->M_DOMAIN.SOURCECFG[src-1].R = APLIC_SOURCECFG_N_D_MASK | APLIC_DELEGATE_M_TO_S0;
/* S0 域:真正配置触发方式、目标、使能 */
APLIC1_PTR->S0_DOMAIN.SOURCECFG[src-1].R = sourceCfgVal;
APLIC1_PTR->S0_DOMAIN.TARGET[src-1].R   = targetCfgVal;
APLIC1_PTR->S0_DOMAIN.SETIENUM.R        = src;
```

委托本质:M 域里把该源标记为"委派给子域"(D=1,SM 指定 S0 或 S1),真正的触发/目标/使能配置在 S0/S1 域完成。

委托后它还"不可屏蔽"吗? 不再"不可屏蔽"。手册里"不可屏蔽"指的是------对 S 模式软件而言,M 域的中断请求是不可屏蔽的 (因为 M 特权级更高)。一旦源被 D=1 委托到 S0/S1,它就变成了普通的 S 模式外部中断(SEI) ,受 S 模式的 sstatus.SIEsieeithreshold 等控制,可以像普通中断一样被 S 模式屏蔽/降级。所以"委托"= 放弃 M 级的不可屏蔽属性,把处理权下放给 S 模式。

方式三:嵌套老式 INTC(兼容 NXP 软件)

芯片为了兼容 NXP PPC 老代码,在 APLIC 里"嵌套"了 10 个老式 INTC 实例(INTC_0~INTC_9)。这些 INTC 实例作为 APLIC 的中断源(源号 1、7、13、19、25、31、37、43、49、55)接入,各自有一个 handler:

复制代码
```c
{ .IrqEiid = EIID_54, .IrqSourceId = 1,  .IrqHandler = &Irq_AplicIntc0_Handler },
{ .IrqEiid = EIID_55, .IrqSourceId = 7,  .IrqHandler = &Irq_AplicIntc1_Handler },
...
```

老式 INTC 的初始化走 Irq_AplicIntcInit(),内部写 PSR(优先级 + PRC_SELN)、CPR,响应时读 IACKR 得向量、写 EOIR 结束------这就是 NXP 5777M 那套机制。

代码注释明确说:"The design of nesting the INTC module within APLIC will be removed in the next design version."(下个版本会移除嵌套 INTC),新代码应直接走 APLIC(方式一/二)。


4.6 向量表与 ISR 的衔接

ISR 函数会被填入按"核 × EIID"组织的向量表:

复制代码
```c
extern volatile uint32 m_AplicIsrVectorTable[RESOURCE_CHIP_CORE_NUM][HART_M_EIID_MAX];
```

外部中断入口(major_interrupt.cMI11_Handler):

复制代码
```asm
csrrw t1, mtopei, t0        ; 读 M 模式顶部 EIID,同时写 0
ANDI  t1, t1, 0x00ff        ; 取低 8 位(EIID)
slli  t1, t1, 0x2           ; ×4(32 位表项)
la    t2, m_AplicIsrVectorTable
ADD   t2, t2, t4            ; + hart 偏移(每核 64×4=256 字节)
ADD   t2, t2, t1            ; + EIID 偏移
lw    t3, 0(t2)             ; 取 ISR 地址
jalr  t3                    ; 跳转执行
```

05 · 与 NXP MPC5777M 的 INTC 模块对比

NXP MPC5777M 是 Power Architecture(e200z7 双核)MCU,其中断控制器是经典的 PPC INTC 。CCRC4046S 是 RISC-V(ARC-V 内核)MCU,中断用 RISC-V AIA(APLIC + IMSIC + CLINT)。两者理念完全不同,本篇逐项对比,并给出迁移要点。


5.1 一句话总结差异

|----------|-------------------------------|-------------------------------------------------|
| 维度 | NXP MPC5777M (INTC) | RISC-V (APLIC) |
| 架构 | Power Architecture | RISC-V AIA |
| 控制器构成 | 单个 INTC(1024 源) | APLIC1+APLIC2 级联 + 每核 IMSIC + CLINT |
| 优先级配置 | 每源独立 PRI 字段(PSR.PRIN) | 无独立优先级寄存器,EIID 越小优先级越高 |
| 中断响应握手 | 读 IACKR 拿向量 → 处理 → 写 EOIR | 读 mtopei/stopei 拿 EIID → 处理 → 写 0 到 topei |
| 向量机制 | 软件向量 / 硬件向量(IVPR + IACKR) | 向量表按 hart × EIID 索引 |
| 抢占 | CPR(当前优先级寄存器)+ 阈值 | eithreshold + 硬件阈值栈 |
| 投递方式 | 电平/引脚直连 | DMSI 消息总线投递 |
| 域(特权级隔离) | 无 | M / S0 / S1 三域 + 委托机制 |
| 功能安全 | 独立安全机制 | ASIL-D(锁步/ECC/EDC/E2E) |


5.2 逐项详细对比

5.2.1 中断源与规模

  • MPC5777M INTC:单一 INTC,支持约 1024 个中断源(软件向量模式),每核一套 CPR/IACKR/EOIR。
  • CCRC4046S APLIC:两个 APLIC 级联(1023 + 256 = 1279 路),投递给 4 个 hart(Core0~Core3)。

5.2.2 优先级机制(差异最大)

MPC5777M :每个中断源的 PSR.PRIN(6 bit,0~15 实际可用)单独配优先级,越大越高;CPR 记录当前服务中断的优先级,用于抢占判断。

CCRC4046S没有"优先级寄存器"这个字段 。优先级直接等于 EIID(编号),越小越高。给中断"更高优先级"的唯一办法是给它分配更小的 EIID。

|-------|----------------------|----------------------|
| | MPC5777M | CCRC4046S |
| 优先级来源 | PSR.PRIN(0~15,大者优先) | EIID(1~63/255,小者优先) |
| 优先级范围 | 16 级(实际常 0~15) | M 63 级 / S 255 级 |
| 抢占依据 | CPR + 阈值 | eithreshold + 硬件阈值栈 |

5.2.3 中断响应握手

MPC5777M(三段式握手):

复制代码
```c
// 1. 读 IACKR 得到中断向量号(硬件向量模式)或读当前向量
// 2. 执行 ISR
// 3. 写 EOIR 结束中断,恢复 CPR
```

CCRC4046S(两段式):

复制代码
```c
// 1. 读 mtopei/stopei CSR,得到 EIID(读取本身可同时"完成")
// 2. 执行 ISR(结束时可再向 topei 写 0,弹阈值栈)
```

关键区别:NXP 用内存映射寄存器 IACKR/EOIR 做握手;CCRC4046 用CSR(mtopei/stopei),且"读即认领"。

5.2.4 投递与拓扑

  • MPC5777M:外设中断引脚直接汇总到 INTC,INTC 内部仲裁后拉高核的中断请求线。
  • CCRC4046S :APLIC 采样后打包成 DMSI 消息,经 DMSI 总线投递到每个核的 IMSIC 中断文件,再由核响应。多了一级"消息化"抽象。

5.2.5 特权级/域隔离

  • MPC5777M:无域概念(单核/双核对称,各自处理)。
  • CCRC4046S:M/S0/S1 三域,可把中断委托给 S 模式(OS)处理,天然支持 M/S 特权隔离和虚拟化(VS/guest)。

5.2.6 向量表

  • MPC5777MIVPR 指向向量基址,硬件向量模式下由 INTC 直接算出向量地址;软件向量模式由 IACKR.INTVEC 索引。
  • CCRC4046Sm_AplicIsrVectorTable[hart][EIID],外部中断入口读 topei 得 EIID 后直接查表跳转。

5.3 寄存器对照表(迁移速查)

|---------------------|----------|-------------------------------------|
| NXP 5777M INTC 寄存器 | 作用 | CCRC4046S 对应 |
| INTC_BCR | 硬件向量模式开关 | (无直接对应,由内核配置决定向量模式) |
| INTC_MPROT | 主保护 | APLIC_CTL.E_CPC(CPC 访问保护) |
| INTC_CPR | 当前优先级 | 核内 eithreshold + 硬件阈值栈 |
| INTC_IACKR | 读中断向量 | 读 mtopei/stopei CSR(得 EIID) |
| INTC_EOIR | 结束中断 | 写 0 到 mtopei/stopei |
| INTC_PSR.PRIN | 配置优先级 | 无对应(EIID 即优先级) |
| INTC_PSR.PRC_SELN | 选择核 | APLIC_TARGET.Hart_Index |
| INTC_SSCIR | 软件触发/清除 | APLIC_SETIPNUM / APLIC_CLRIPNUM |
| (无) | --- | APLIC_SOURCECFG(触发方式 + 委托) |
| (无) | --- | APLIC_TARGET.EIID(目标编号 + 优先级) |
| (无) | --- | APLIC_SETIENUM/CLRIENUM(源使能开关) |


5.4 迁移要点清单

  1. 改优先级配置方式 :原来"给源设 PRI=15",现在改成"给源分配一个较小的 EIID"。注意 EIID 越小优先级越高(与 NXP 的 PRI 越大越高相反)。
  1. 改中断结束方式:把"写 EOIR"改成"写 0 到 topei"。且 RISC-V AIA 里读 topei 时往往已经认领了中断。
  1. 改向量表组织 :原来是单一向量表 + IACKR 索引;现在是 hart × EIID 二维表,且每个 EIID 独立向量。
  1. 改投递认知:不用再关心"中断引脚直连 INTC",理解"外设 → APLIC → DMSI → IMSIC → 核"这条消息链。
  1. 利用域的隔离:如果需要把中断交给 OS(S 模式),用委托机制(M 域 D 位 + S0 域配置),这是 NXP INTC 没有的能力。
  1. 短期兼容 :芯片在 APLIC 里嵌套了 10 个老式 INTC 实例(走 Irq_AplicIntcInit + IACKR/EOIR),可以让老代码原样跑 ,但该设计下版本会移除,新代码应直接用 APLIC

5.5 对比结论

|--------|----------------------------------------|
| 结论 | 说明 |
| 更灵活 | APLIC 支持 1279 路源、三域、委托、虚拟化,远超 NXP INTC |
| 更"反直觉" | 优先级"隐含"在编号里(越小越高),没有显式优先级寄存器 |
| 投递更抽象 | 消息化(DMSI)替代了电平直连,理解成本更高 |
| 迁移有桥 | 嵌套 INTC 提供兼容层,但只是过渡方案 |

相关推荐
无损检测小白白1 小时前
【嵌入式面试题一】
c语言·单片机
Messy create2 小时前
【BMS-Pack检测-短路检测】
单片机·嵌入式硬件·能源
yutian06062 小时前
TI 280049 GPIO 相关笔记
嵌入式硬件·c2000
星一工作室3 小时前
【STM32模块教程】-TT马达(直流电机)
单片机·嵌入式硬件
jianqiang.xue3 小时前
ESP-IDF保姆级入门19|低功耗模式全解与工程化设计:分级休眠/唤醒源配置/功耗调优/外设适配,实现工业级功耗性能平衡
stm32·单片机·物联网·架构·esp32
单片机仿真设计4 小时前
【proteus仿真】基于 STM32 单片机汽车安全监控装置设计(仿真 + 源码)
stm32·单片机·嵌入式硬件·proteus·毕设
单片机仿真设计4 小时前
【proteus仿真】基于 STM32 单片机的智能浴室系统设计(仿真图+程序)
stm32·单片机·嵌入式硬件·proteus·毕设
天空'之城4 小时前
单片机基础核心知识点汇总(二十六)
单片机·嵌入式硬件
新晨单片机设计4 小时前
S005A-基于STM32单片机震动防盗报警器【Proteus仿真+Keil程序+原理图】
stm32·单片机·proteus