2026 / 07 / 22
RA 系列:Cortex-M33 MCU 在未配置 MPU 的情况下启用 s-cache 时的潜在问题
Issue:
瑞萨的一些 Cortex-M33 MCU为了提升性能,配置了两个缓存。它们位于 Cortex-M33 和 AHB 总线矩阵之间,c-cache 位于指令总线上,s-cache 位于数据总线上。启用 s-cache 后,用户可能会遇到一些通信失败的情况,例如:
· 外部QSPI存储器
· 外部 OSPI 存储器
· 与 SRAM 中的 CPU 共享内存(以太网、Segger RTT viewer)
Answer:
有两种方法可以解决此问题:
1. 使用 s-cache 刷新(invalidate/clean) 指令。
2. 使用 MPU(内存保护单元)。
有关 s-cache 刷新的使用、DMA/DTC 的缓存一致性问题和 MPU 的更多信息,请参阅以下链接。
https://www.renesas.com/us/en/document/apn/ra-family-guidelines-using-s-cache-system-bus
第二种方法是使用 MPU,即仅在导致问题发生的用户可配置内存的特定区域中禁用 s-cache。下面我们说明如何通过配置 MPU 来解决这个问题。 有关每个 MPU 设置的详细信息,请参阅文末附上的参考文档。
以下定义将在所有要说明的 3 种情况下通用。
#define CTRL_PV 1
#define CTRL_HN 1
#define ARM_MPU_CTRL(PV, HN) \
(((PV) << MPU_CTRL_PRIVDEFENA_Pos) | \
(HN) << MPU_CTRL_HFNMIENA_Pos)
#define MPU_ARMV8M_MAIR_ATTR_DEVICE_VAL 0x04
#define MPU_ARMV8M_MAIR_ATTR_DEVICE_IDX 0
#define MPU_ARMV8M_MAIR_ATTR_CODE_VAL 0xAA
#define MPU_ARMV8M_MAIR_ATTR_CODE_IDX 1
#define MPU_ARMV8M_MAIR_ATTR_DATA_VAL 0xFF
#define MPU_ARMV8M_MAIR_ATTR_DATA_IDX 2
#define MPU_ARMV8M_MAIR_ATTR_DATANOCACHE_VAL 0x44
#define MPU_ARMV8M_MAIR_ATTR_DATANOCACHE_IDX 3
(1) 外部QSPI存储器
使用以下代码禁用 QSPI 内存位置上的 s-cache。如果您所用的 MCU 具有不同的 QSPI 地址映射,请在 “ARM_MPU_RBAR”和“ARM_MPU_RLAR”中更改相应的地址。
0x6000 0000 - 0x63FF FFFF:外部SPI设备
0x6400 0000 - 0x67FF FFFF:QSPI I/O寄存器
const ARM_MPU_Region_t mpuTable[2] = {
// BASE SH RO NP XN
{ .RBAR = ARM_MPU_RBAR(0x60000000UL, ARM_MPU_SH_NON, 0UL, 1UL, 1UL),
// LIMIT ATTR
.RLAR = ARM_MPU_RLAR(0x63FFFFFFUL, MPU_ARMV8M_MAIR_ATTR_DATANOCACHE_IDX) },
// BASE SH RO NP XN
{ .RBAR = ARM_MPU_RBAR(0x64000000UL, ARM_MPU_SH_NON, 0UL, 1UL, 1UL),
// LIMIT ATTR
.RLAR = ARM_MPU_RLAR(0x67FFFFFFUL, MPU_ARMV8M_MAIR_ATTR_DEVICE_IDX) }
};
void mpu_config_quadspi(void)
{
ARM_MPU_Disable();
ARM_MPU_SetMemAttr(MPU_ARMV8M_MAIR_ATTR_DEVICE_IDX, MPU_ARMV8M_MAIR_ATTR_DEVICE_VAL);
ARM_MPU_SetMemAttr(MPU_ARMV8M_MAIR_ATTR_CODE_IDX, MPU_ARMV8M_MAIR_ATTR_CODE_VAL);
ARM_MPU_SetMemAttr(MPU_ARMV8M_MAIR_ATTR_DATA_IDX, MPU_ARMV8M_MAIR_ATTR_DATA_VAL);
ARM_MPU_SetMemAttr(MPU_ARMV8M_MAIR_ATTR_DATANOCACHE_IDX, MPU_ARMV8M_MAIR_ATTR_DATANOCACHE_VAL);
ARM_MPU_Load(0, mpuTable, 2);
ARM_MPU_Enable(ARM_MPU_CTRL(CTRL_PV, CTRL_HN));
}
void hal_entry(void)
{
mpu_config_quadspi();
enable_s_cache();
qspi_init();
while(1)
{
(2) 外部 OSPI 存储器
使用以下代码禁用 OSPI 区域上的 s-cache。如果您的 MCU 具有不同的 OSPI 地址映射,请相应地更改“ARM_MPU_RBAR”和“ARM_MPU_RLAR”中的地址。
0x6800 0000 - 0x6FFF FFFF : CS0
0x7000 0000 - 0x7FFF FFFF : CS1
void mpu_config_octaspi(void)
{
ARM_MPU_Disable();
ARM_MPU_SetMemAttr(MPU_ARMV8M_MAIR_ATTR_DEVICE_IDX, MPU_ARMV8M_MAIR_ATTR_DEVICE_VAL);
ARM_MPU_SetMemAttr(MPU_ARMV8M_MAIR_ATTR_CODE_IDX, MPU_ARMV8M_MAIR_ATTR_CODE_VAL);
ARM_MPU_SetMemAttr(MPU_ARMV8M_MAIR_ATTR_DATA_IDX, MPU_ARMV8M_MAIR_ATTR_DATA_VAL);
ARM_MPU_SetMemAttr(MPU_ARMV8M_MAIR_ATTR_DATANOCACHE_IDX, MPU_ARMV8M_MAIR_ATTR_DATANOCACHE_VAL);
ARM_MPU_Load(0, mpuTable, 2);
ARM_MPU_Enable(ARM_MPU_CTRL(CTRL_PV, CTRL_HN));
}
void hal_entry(void)
{
mpu_config_octaspi();
enable_s_cache();
ospi_init();
while(1)
{
(3) SRAM中与CPU共享存储器
以太网 (EDMAC)
使用以下代码禁用 EDMAC 描述符和缓冲区区域上的 s-cache。e2 studio 项目的默认链接器脚本会将 EDMAC 描述符和缓冲区放在 “.ns_buffer.eth”区域,这些地址可能会随着代码更改而更改。因此,请相应地更改“ARM_MPU_RBAR”和“ARM_MPU_RLAR”的地址,或将 EDMAC 描述符和缓冲区放在有固定地址的区域。

图1 内存映射文件片段(EDMAC)
使用上面给定的内存映射文件片段示例后,覆盖了 EDMAC 描述符和缓冲区的内存区域将为:
起始地址:0x2003 CF80
结束地址:0x2003 FFFF (0x2003CF80 + 0x3080 – 1)
请注意,MPU 地址应为 32 字节的倍数。
const ARM_MPU_Region_t mpuTable[1] = {
// BASE SH RO NP XN
{ .RBAR = ARM_MPU_RBAR(0x2003CF80, ARM_MPU_SH_NON, 0UL, 1UL, 1UL),
// LIMIT ATTR
.RLAR = ARM_MPU_RLAR(0x2003FFFF, MPU_ARMV8M_MAIR_ATTR_DATANOCACHE_IDX) },
};
void mpu_config_eth(void)
{
ARM_MPU_Disable();
ARM_MPU_SetMemAttr(MPU_ARMV8M_MAIR_ATTR_DEVICE_IDX, MPU_ARMV8M_MAIR_ATTR_DEVICE_VAL);
ARM_MPU_SetMemAttr(MPU_ARMV8M_MAIR_ATTR_CODE_IDX, MPU_ARMV8M_MAIR_ATTR_CODE_VAL);
ARM_MPU_SetMemAttr(MPU_ARMV8M_MAIR_ATTR_DATA_IDX, MPU_ARMV8M_MAIR_ATTR_DATA_VAL);
ARM_MPU_SetMemAttr(MPU_ARMV8M_MAIR_ATTR_DATANOCACHE_IDX, MPU_ARMV8M_MAIR_ATTR_DATANOCACHE_VAL);
ARM_MPU_Load(0, mpuTable, 1);
ARM_MPU_Enable(ARM_MPU_CTRL(CTRL_PV, CTRL_HN));
}
void hal_entry(void)
{
mpu_config_eth();
enable_s_cache();
while(1)
{
Segger RTT Viewer
使用以下代码禁用 Segger RTT 区域上的 s-cache。Segger RTT 使用一部分用户 SRAM 来输出调试信息,以及接收用户的输入。以下 3 个区域的起始地址和结束地址可以在项目编译后的内存映射文件中找到,请在“ARM_MPU_RBAR”和“ARM_MPU_RLAR”中修改相应地址。
.bss._SEGGER_RTT
.bss._acUpBuffer
.bss._acDownBuffer

图2 内存映射文件片段(Segger RTT Viewer)
使用了上面给定的内存映射文件片段示例,覆盖了所有 3 个区域的内存区域将为:
起始地址:0x2000 01D4
结束地址:0x2000 128B
MPU 地址应为 32 字节的倍数,因此:
起始地址:0x2000 01C0
结束地址:0x2000 129F
const ARM_MPU_Region_t mpuTable[1] = {
// BASE SH RO NP XN
{ .RBAR = ARM_MPU_RBAR(0x200001C0UL, ARM_MPU_SH_NON, 0UL, 1UL, 1UL),
// LIMIT ATTR
.RLAR = ARM_MPU_RLAR(0x2000129FUL, MPU_ARMV8M_MAIR_ATTR_DATANOCACHE_IDX) },
};
void mpu_config_rtt(void)
{
ARM_MPU_Disable();
ARM_MPU_SetMemAttr(MPU_ARMV8M_MAIR_ATTR_DEVICE_IDX, MPU_ARMV8M_MAIR_ATTR_DEVICE_VAL);
ARM_MPU_SetMemAttr(MPU_ARMV8M_MAIR_ATTR_CODE_IDX, MPU_ARMV8M_MAIR_ATTR_CODE_VAL);
ARM_MPU_SetMemAttr(MPU_ARMV8M_MAIR_ATTR_DATA_IDX, MPU_ARMV8M_MAIR_ATTR_DATA_VAL);
ARM_MPU_SetMemAttr(MPU_ARMV8M_MAIR_ATTR_DATANOCACHE_IDX, MPU_ARMV8M_MAIR_ATTR_DATANOCACHE_VAL);
ARM_MPU_Load(0, mpuTable, 1);
ARM_MPU_Enable(ARM_MPU_CTRL(CTRL_PV, CTRL_HN));
}
void hal_entry(void)
{
mpu_config_rtt();
enable_s_cache();
SEGGER_RTT_printf(“HELLO”);
while(1)
{
Arm Cortex-M33 处理器技术参考手册
https://documentation-service.arm.com/static/5f15c42420b7cf4bc5247f3a
Armv8-M 架构参考手册
https://documentation-service.arm.com/static/5f8efe85f86e16515cdbe596
系统总线上 S 高速缓存的使用指南
https://www.renesas.com/us/en/document/apn/ra-family-guidelines-using-s-cache-system-bus