banner
yono

yono

哈喽~欢迎光临
follow
github

arm cache 屬性的影響

前言#

最近希望將 ADC 性能跑到盡可能的極限,所以研究了一下相關的外設功能。

其中 cache 和 MPU 屬性適配的部分我以前一直沒搞太懂,這次總算弄得比較清楚了。在具有 cache 功能的 arm 晶片中,一定要配合 MPU 外設功能配置才可以使用 cache 功能。

  • cache:緩衝區主要是為了處理器內核可以更連續地操作內存,畢竟一些內存操作涉及到跨總線,cache 可以在讀取時對命中的內存附近進行預取,以及寫入時一定程度的延遲湊連續,最主要的是可以減少內部總線的衝突以加速數據的處理。
  • MPU(Memory Protection Unit):內存保護單元是 arm 晶片的內存保護機制,主要用於配置某段內存區的 cache 屬性。
    當然可以有其他用途但是恕我直言沒有其他用途,例如在不同的任務線程加載中,對內存區的配置可以精細化不同,實現類似 MMU 的功能但是正常人不會做這麼複雜的應用的。
  • DMA(Direct Memory Access):直接寄存器訪問,可以以沒有處理器算力參與的形式,將外設寄存器中的數值拷貝到內存中,在拷貝一定數量 (例如 1024 個) 後產生中斷通知,供我們對這段數據進行處理。

MPU 與 MMU 的主要區別

MPUMMU
緩衝命中周期固定 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 以獲得最大的性能,對應上述的第一個組合。

而上述的第二個組合,我暫時沒有遇到過使用場景,也想像不出來。

以下是上述配置的匹配代碼,可供參考以及我自己回檔。

/**
  * @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);
}

此文由 Mix Space 同步更新至 xLog
原始鏈接為 https://www.yono233.cn/posts/novel/25_10_13_arm-cache_mpu


載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。