在嵌入式开发中,随着处理器性能的提升,Cache成为提高系统效率的关键组件。然而,Cache的使用也带来了数据一致性的挑战,尤其是在STM32H7这类高性能MCU中。本文将从一个实际工程案例出发,详细讲解如何通过MPU正确配置Cache策略,解决开发过程中常见的数据同步问题。
假设我们正在开发一个基于STM32H7的嵌入式图形界面项目,使用外部SDRAM作为显存,通过DMA2D加速图像刷新。在开发过程中,可能会遇到以下现象:
这些问题的根源往往在于Cache与主存之间的数据不一致。当CPU在Cache中修改了数据,但未及时写回主存时,DMA控制器直接从主存读取的将是旧数据,导致显示异常。
典型错误配置示例:
c复制// 错误的MPU配置示例 - 对SDRAM区域使用Write-back策略
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0xC0000000; // SDRAM起始地址
MPU_InitStruct.Size = MPU_REGION_SIZE_16MB;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER0;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
这种配置会导致CPU对SDRAM的写入操作仅更新Cache,而不立即同步到物理内存,当DMA2D直接从SDRAM读取数据时,就会获取到未更新的旧值。
MPU(Memory Protection Unit)是ARM Cortex-M系列处理器中的硬件模块,主要功能包括:
在STM32H7中,MPU区域配置的关键参数:
| 参数 | 说明 | 典型取值 |
|---|---|---|
| BaseAddress | 区域起始地址 | 0xC0000000(SDRAM) |
| Size | 区域大小 | MPU_REGION_SIZE_16MB |
| AccessPermission | 访问权限 | MPU_REGION_FULL_ACCESS |
| TypeExtField | 内存类型 | MPU_TEX_LEVEL0 |
| IsCacheable | 是否可缓存 | MPU_ACCESS_CACHEABLE/NOT_CACHEABLE |
| IsBufferable | 是否缓冲 | MPU_ACCESS_BUFFERABLE/NOT_BUFFERABLE |
| IsShareable | 是否共享 | MPU_ACCESS_SHAREABLE/NOT_SHAREABLE |
STM32H7的Cache支持四种主要策略:
Write-through(透写):
Write-back(回写):
Write-around(绕写):
Non-cacheable(非缓存):
针对SDRAM显存与DMA2D配合的场景,推荐以下MPU配置:
c复制// 正确的SDRAM MPU配置 - Write-through策略
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0xC0000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_16MB;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE; // 必须共享
MPU_InitStruct.Number = MPU_REGION_NUMBER0;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
关键配置要点:
当使用内部SRAM(如AXI SRAM)作为DMA缓冲区时:
c复制// AXI SRAM配置示例 - Non-cacheable策略
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0x24000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_512KB;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER1;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
对于外设寄存器区域,必须配置为Strongly-ordered或Device类型:
c复制// 外设寄存器区域配置
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0x40000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_512MB;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER2;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1; // Device类型
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
对于存放程序代码的Flash或RAM区域,通常配置为Write-back策略:
c复制// Flash区域配置 - Write-back策略
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0x08000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_2MB;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER3;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
当Cache配置不当时,可能需要手动维护Cache一致性:
c复制// 清除数据Cache
SCB_CleanDCache();
// 无效化数据Cache
SCB_InvalidateDCache();
// 清除并无效化数据Cache
SCB_CleanInvalidateDCache();
使用场景示例:
问题1:数据写入后读取的值不正确
可能原因:
问题2:系统运行不稳定,随机崩溃
可能原因:
问题3:DMA传输性能低下
可能原因:
c复制// 将关键变量放在DTCM内存中(64KB)
__attribute__((section(".dtcm"))) uint32_t highSpeedBuffer[1024];
以下是一个典型的STM32H7 MPU配置示例,涵盖了常见内存区域:
c复制void MPU_Config(void)
{
MPU_Region_InitTypeDef MPU_InitStruct = {0};
// 禁用MPU
HAL_MPU_Disable();
// 配置Flash区域(Write-back)
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0x08000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_2MB;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER0;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
// 配置SRAM1区域(Write-back)
MPU_InitStruct.BaseAddress = 0x24000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_512KB;
MPU_InitStruct.Number = MPU_REGION_NUMBER1;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
// 配置SDRAM区域(Write-through)
MPU_InitStruct.BaseAddress = 0xC0000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_16MB;
MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER2;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
// 配置外设区域(Device)
MPU_InitStruct.BaseAddress = 0x40000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_512MB;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
MPU_InitStruct.Number = MPU_REGION_NUMBER3;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
// 启用MPU
HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}
在实际项目中,MPU配置需要根据具体硬件连接和应用需求进行调整。特别是在使用DMA、LCD控制器等外设时,正确的Cache配置对系统稳定性和性能至关重要。