风过空庭,字句正徐来。
关于关于本站关于我给我点钱
更多时间线友链文件服务wiki
联系写留言发邮件GitHub
© 2024-2026 yono. | RSS 订阅 | 站点地图 | | Stay hungry. Stay foolish.
Powered by Mix Space&
白い
.
| 粤 ICP 备2024284785号-1 |
正在被0人看爆
纸白微明,未成篇章。

arm cache 属性的影响

(已编辑)
/
49
1
AI·GEN

关键洞察

这篇文章上次修改于,可能部分内容已经不适用,如有疑问可询问作者。

arm cache 属性的影响

  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • 前言

    最近希望将 ADC 性能跑到尽可能的极限,所以研究了一下相关的外设功能。

    其中 cache 和 MPU 属性适配的部分我以前一直没搞太懂,这次总算弄得比较清楚了。在具有 cache 功能的 arm 芯片中,一定要配合 MPU 外设功能配置才可以使用 cache 功能。

    • cache:缓冲区主要是为了处理器内核可以更连续地操作内存,毕竟一些内存操作涉及到跨总线,cache 可以在读取时对命中的内存附近进行预取,以及写入时一定程度的延迟凑连续,最主要的是可以减少内部总线的冲突以加速数据的处理。
    • MPU(Memory Protection Unit):内存保护单元是 arm 芯片的内存保护机制,主要用于配置某段内存区的 cache 属性。 当然可以有其他用途但是恕我直言没有其他用途,例如在不同的任务线程加载中,对内存区的配置可以精细化不同,实现类似 MMU 的功能但是正常人不会做这么复杂的应用的。
    • DMA(Direct Memory Access):直接寄存器访问,可以以没有处理器算力参与的形式,将外设寄存器中的数值拷贝到内存中,在拷贝一定数量(例如1024个)后产生中断通知,供我们对这段数据进行处理。

    MPU 与 MMU 的主要区别

    MPU MMU
    缓冲命中周期 固定1指令周期 1~20周期不等
    管理精细度 有限的区域(十数个)管理 可以 RAM 逐页精细化
    多任务支持 划分物理区域进行有限隔离 虚拟地址进程完全隔离
    说白了 做实时性应用的 搞 linux 的

    其他很多芯片当然都有类似的 cache 和 MPU 功能,但是这里主要讨论的 arm 芯片,他们的功能都是类似的,只是管理粒度、划区数量、或者不可管理固定属性,其中的属性和对应功能都是类似的。

    总线的理解

    MCU 芯片的外设分布在不同总线上,而同总线的外设和内存间读写相对会快。我们仅就 ADC 、DMA、以及内存的配合,理解总线。

    在我的这个应用中,因为 DMA 只能操作同总线上的外设和内存,所以使用到了 ADC1/2/3、DMA1/2、BDMA1。

    结合以下的总线示意图理解。ADC1 结合 DMA1 采样同时占用部分 D2 域内存、ADC3 结合 BDMA 采样同时占用部分 D3 域内存,并最终将数据初期处理后将采样值放在 D1 域内存供 CPU 后续计算。这样分配尽可能减少跨域的性能损耗。

    用到的总线

    用到的总线

    cache 属性的配置

    MPU 配置控制 cache 属性,有最重要的 3 个配置项如下,我有一些自己的简单总结

    • IsCacheable 决定是否开启 cache。
    • IsBufferable 决定是否将写入指令也使用 cache 缓冲后统一写入内存。如果不开启 IsCache 那么没有意义
    • IsShareable 决定是否采用严格的时序控制,这在外设的外设,例如 FMC 访问外部内存时非常重要,因为资源跨芯片了,需要开启严格时序。

    以及对应的 chat GPT 解释,比我说的清楚详细。

    -

    IsCacheable 控制是否使用 D-Cache,以及在 M7 上配合 TEX/C/B 决定缓存策略。

    • IsBufferable 允许写缓冲(write buffering/combining),可能延后对外可见时间,以换取更高吞吐。
    • IsShareable 标记该区“可被多个主机共享”,在 M7 上还会改变缓存策略:Cacheable+Shareable 会强制为 Write-Through/No-Write-Allocate。

    详细说明

    • IsCacheable
      • 关心点:是否走 D-Cache;配合 TEX/C/B 决定是 Write-Back/Write-Allocate 还是 Write-Through/No-Allocate。
      • 影响:CPU访问延迟与带宽、总线流量;不直接改变外设采样速率,但影响CPU处理速度。
    • IsBufferable
      • 含义:允许将写入先进入写缓冲并可能合并,再异步刷到内存/总线,减少总线事务、提高吞吐。
      • 影响:
        • 性能:连续写(memcpy、流式写)更快。
        • 可见性/顺序:写入对其他主机(DMA/外设)的“可见时刻”可能延后;必要时用屏障指令保证顺序。
      • 典型用法:Normal 内存常设为 BUFFERABLE;Device/MMIO 通常不设以避免重排/合并。
    • IsShareable
      • 含义:该区域可能被多个主机共享(CPU、DMA等)。在 Cortex-M7 上对 Cacheable 的 Normal 区有特别影响:
        • Cacheable + Shareable = 写直达(Write-Through)、不写分配(No-Write-Allocate),减少其他主机看到旧数据的窗口,但CPU侧性能较 WB/WA 略降。
        • Cacheable + Non-shareable = 通常为 Write-Back/Write-Allocate(配合 TEX=1,C=1,B=1),CPU性能最佳。
      • 影响:
        • 顺序与可见性语义更保守,有利于与其他主机协作。
        • 不等于“硬件缓存一致性”,但改变了缓存写策略与内存屏障语义范围。

    常见组合速览(Normal 内存)

    • B=1, C=1, Shareable=0, TEX=1 → Write-Back, Write-Allocate(CPU最快,适合大多数计算缓冲)
    • B=1, C=1, Shareable=1, TEX=1 → Write-Through, No-Write-Allocate(更保守的共享语义)
    • B=0/1, C=0 → Non-cacheable(可选择是否允许写缓冲;B=0 更严格时序)

    见此前的总线示意,由于 cache 实际属于 CPU 的一部分,所以 DMA 获得的内存数据(D2/D3域内存区)是必然跨域的。在跨域的应用中,必须解决内存一致性问题,这需要一个内存区大小的 while 循环来强制命中解决一致性问题,反而引起不必要的消耗。

    所以在 MPU 配置中,D2、D3域对应的地址范围不开启 Cache,对应上述的第三个组合。D1域的常用内存区则可以开启 IsCache、开启 IsBuffer、关闭 IsShare 以获得最大的性能,对应上述的第一个组合。

    而上述的第二个组合,我暂时没有遇到过使用场景,也想象不出来。

    以下是上述配置的匹配代码,可供参考以及我自己回档。

    CodeBlock Loading...
    /**
      * @brief MPU配置
      * @param None
      * @retval None
    */
    static void MPUInit(void)
    {
        MPU_Region_InitTypeDef MPU_InitStruct;
    
        /* 禁止 MPU */
        HAL_MPU_Disable( );
    
        /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate
            最佳性能,用于CPU处理计算 */
        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_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_LEVEL1;
        MPU_InitStruct.SubRegionDisable = 0x00;
        MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    
        HAL_MPU_ConfigRegion(&MPU_InitStruct);
    
        /* 配置D2域MPU 
            D2域外设的DMA使用 禁止cache */
        MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
        MPU_InitStruct.BaseAddress      = 0x30000000;
        MPU_InitStruct.Size             = MPU_REGION_SIZE_256KB;
        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_NOT_SHAREABLE;
        MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
        MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
        MPU_InitStruct.SubRegionDisable = 0x00;
        MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_DISABLE;
    
        HAL_MPU_ConfigRegion(&MPU_InitStruct);
    
        /* 配置以太网收发描述符部分为Strongly Ordered */
        MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
        MPU_InitStruct.BaseAddress      = 0x30040000;
        MPU_InitStruct.Size             = MPU_REGION_SIZE_32KB;
        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_NOT_SHAREABLE;
        MPU_InitStruct.Number           = MPU_REGION_NUMBER2;
        MPU_InitStruct.SubRegionDisable = 0x0;
        MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
        MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_DISABLE;
    
        HAL_MPU_ConfigRegion(&MPU_InitStruct);
    
        /* 配置D3域MPU 
            D3域外设的DMA使用 禁止cache */
        MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
        MPU_InitStruct.BaseAddress      = 0x38000000;
        MPU_InitStruct.Size             = MPU_REGION_SIZE_64KB;
        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_NOT_SHAREABLE;
        MPU_InitStruct.Number           = MPU_REGION_NUMBER3;
        MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
        MPU_InitStruct.SubRegionDisable = 0x00;
        MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_DISABLE;
    
        HAL_MPU_ConfigRegion(&MPU_InitStruct);
    
        /* 配置FMC 片选3 的支持*/
        MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
        MPU_InitStruct.BaseAddress      = 0x68000000;
        MPU_InitStruct.Size             = MPU_REGION_SIZE_256B;
        MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
        MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;
        MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE; // 此外设需配置为无cache,否则会重复片选和读写使能
        MPU_InitStruct.IsShareable      = MPU_ACCESS_SHAREABLE;
        MPU_InitStruct.Number           = MPU_REGION_NUMBER4;
        MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
        MPU_InitStruct.SubRegionDisable = 0x00;
        MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    
        HAL_MPU_ConfigRegion(&MPU_InitStruct);
    
        /*使能 MPU */
        HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
    }