Cortex_m7內核cache深入了解和應用


 

 

 

一,cache概述

從下圖可以看出,從M7內核才開始有的cache,這對於從M0,M3,M4一路走來的小伙伴來說,多了一個cache就多了一個障礙。

Cortex-M7 core with 32K/32K L1 I/D-Cache!這提供了極高的性能,代碼無論是從芯片上的內存,外部閃存,還是外部內存運行!介紹種類包括:L1 cache, memory types, attributes and MPU(Memory Protection Unit). 指導用戶如何使用緩存開發以正確和高性能方式運行應用程序。

 

cache價值:由於對這些存儲器的子系統,比如 OCRAMFlexSPISEMC的訪問可能需要多個周期(特別是在具有多個等待狀態的外部存儲器接口上),因此L1緩存的設計目的是加快對存儲器的讀/寫操作。這帶來了很大的性能提升。

 

I/DTCM(FlexRAM banks configured as TCM)CPU核直接訪問,繞過L1緩存。因此,建議將關鍵代碼和數據放入TCM中,就像向量表vector table一樣

 

flush:在有的書中稱作invalidate

存貯器結構如下圖:

 

 

 

Cortex-M7是6級雙發射流水線,下圖是一個示意圖。

 

高速緩存cache基礎知識:

程序執行的局部規律性包括時間局部規律性和空間局部規律性。

時間局部規律性:在程序執行過程中,剛剛被訪問的信息可能很快被再次訪問,典型情況是程序中存在大量的循環。

空間局部規律性:在程序執行過程中,那些與被訪問的地址相臨近的信息也有可能很快被訪問。典型情況是程序中存在大量的順序執行。

按照程序執行的“局部性規律”,程序中的數據或者代碼被訪問后,該數據和代碼以及鄰近的數據代碼近期再被訪問的概率要遠大於,近期未被訪問的數據或者代碼被訪問的概率。因此,當數據或代碼被訪問后,被認為是經常訪問的數據和代碼,將被存入到cache,當再次訪問該數據或者代碼時直接從該cache讀取數據或者代碼的值,而不是到內存中重新讀取。

有了cache后,core對內存中的數據訪問流程如下圖所示:

二.Cortex-M7內核的L1 Cache

 

 

L1 Cache由多行內存區組成,每行有32字節,每行都配有一個地址標簽。

數據緩沖DCache:是每4行為一組,稱為4-way set associative

指令緩沖區ICache:是2行為一組, 稱為2-way set-associative

這樣節省地址標簽,不用每個行都標記一個地址。

 

Cache hit:要訪問的數據/指令在cache里面.

Cache miss:要訪問的數據/指令不在cache里面.

 

Core cache時:

hit,則直接從cache讀出數據即可。

miss,有兩種處理方式:

>read through ,直接從內存中讀出

>read allocate,先把數據讀到cache,再從cache讀出。如果 CPU 要讀取的 SRAM 區數據在 Cache 中已經加載好,就可以直接從 Cache 里面讀取。如果沒有,就用到配置 read allocate 了,意思就是在 Cache 里面開辟區域,將 SRAM 區數據加載進來,后續的操作,CPU 可以直接從 Cache 里面讀取,從而時間加速。

 

 

Core cache時:

hit,兩種處理方式:

>write-through模式:

可以直接寫到內存中同時放到Cache里面,優點是內存和cache同步更新,沒有多總線訪問造成的數據一致性問題,缺點是無法在寫操作上面發揮性能。

 

>Write back模式:Cache line會被標為dirty,等到此行被evicted(驅逐,趕出, flush)時,才會執行實際的寫操作,將Cache Line里面的數據寫入到相應的存 儲區。Write back安全隱患:如果 Cache 命中的情況下,此時僅 Cache 更新了,而 SRAMSDRAM 沒有更新,那么 DMA 直接從 SRAM 里面讀出來的就是錯誤的。

 

 

miss,兩種處理方式:

>write-allocate:先把要寫的數據載入到cache,寫cache,然后再flush進內存。

>no-write-allocate:直接寫入內存。

 

Cache 命中是訪問的地址落在了給定的 Cache Line 里面,所以硬件需要做少量的地址比較工作,以檢查此地址是否被緩存。如果命中了,將用於緩存讀操作或者寫操作。如果沒有命中,則分配和標記新行,填充新的讀寫操作。如果所有行都分配完畢了,Cache 控制器將支持 eviction 操作。根據 Cache Line 替換算法,一行將被清除 Clean,無效化 Invalid 或者重新配置。數據緩存和指令緩存是采用的偽隨機替換算法。Cache支持的4種基本操作,1.使能,2.禁止,3.清空,4.無效化。

 

 

Clean清空操作是將Cache Line中標記為dirty的數據寫入到內存里面,無效化Invalid是將 Cache Line 標記為無效,等同於刪除操作。這樣Cache 空間就都騰出來了,可以加載新的指令/數據。

三. cache配合MPU使用

MPU(memory protection unit),首先需要通過MPU配置相應memory的屬性(normal, strongly-ordered, device, XN etc.)

RT1052存儲器默認映射和屬性

 

 

 

四.什么是 cache 一致性問題

對於指令緩沖I-Cache,用戶不用管,這里主要說的是數據緩存 D-Cache。

所謂的 Cache 一致性問題, 主要指的是由於 D-cache 存在時,表現在有多個 Host(典型的如 MCU 的 Core, DMA 等)訪問同一塊內存時, 由於數據會緩存在 D-cache 中而沒有更新實際的物理內存。

在實際應用中,有以下兩種情況:

4.1

第一種情況是當有core寫物理內存(SRAM,0x20200000)的指令時,(對應SDK例程:*(uint8_t *)(startAddr + count) = 0xffu;)Core 會先去更新相應的 cache-line(Write-back 策略),在沒有 clean 的情況下,會導致其對應的實際物理內存中的數據並沒有被更新,如果這個時候有其它的 Host(如 DMA)訪問這段內存時,就會出現問題(由於實際物理內存並未被更新,和 D-cache 中的不一致),如果clean一下(對應SDK例程:L1CACHE_CleanDCacheByRange(startAddr, MEM_DMATRANSFER_LEN);)這就是所謂的 cache 一致性的問題。

4.2

第二種情況是 DMA 更新了某段物理內存(DMA 和 cache 直接沒有直接通道),而這個時候 Core 再讀取這段內存的時候,由於相對應地址的 cache-line 沒有被 invalidate,導致 Core 讀到的是 cache-line 中的數據,而非被 DMA 更新過的實際物理內存的數據。

五。解決 cache 一致性問題

有兩種可選方案:

一.所有的共享存儲器都定義為共享屬性

  • 這些區域將默認不被緩存到 D-Cache。

  • 所有的操作都直接針對二級存儲器(內部Flash,外部存儲器),性能降低。

  • 因為緩存對這些區域是透明的,寫軟件更容易

二.通過軟件進行cache的維護

1)Cortex-M7 的寫操作要是全局可見的

    • 使用透寫屬性(通過 MPU 設置)。

    • 使用 SIWT@CACR(Shared = Write Through)。

    • 通過指令清 D-cache,然后所有更新位置禁止 D-Cache操作

      這種情況,在DMA從SRAM搬運數據到SDRAM時,需要先執行clean。

          L1CACHE_CleanInvalidateDCache();

2)其他主設備的寫操作要對 Cortex-M7 可見

    • 比如作廢 Cortex-M7 Dache 中數據

       這種情況,在DMA從SDRAM搬運數據到SRAM時,需要搬運后執行Invalidate。

     L1CACHE_CleanInvalidateDCache()

 

六.常見函數解釋

 SCB_EnableICache() SCB_EnableDCache()

  使能 I-cache D-cache

 

SCB_DisableICache() SCB_DisableDCache()

  禁用 I-cache D-cache

 

SCB_InvalidateICache()

  使 I-cache 無效,I-cache invalidate 之后,當讀取指令時,會忽略相應的 cache-line 中的內容(因為被 validate 了),而從真實的物理地址中去獲取相應的指令

 

SCB_InvalidateDCache()

  使 D-cache 無效,D-cache invalidate 之后,當有 Host(如 coreDMA 等)讀取數據時,會忽略相應的 cache-line 中的內容( 因為被 validate 了),從真實的物理地址中去獲取相應的數據

 

SCB_InvalidateDCache_by_Addr()

 

  根據地址信息無效其對應的 cache-line

 

 

 

SCB_CleanDCache()  

 

  Clean 所有的 cache-line,即將 dirty cache-line 全部寫到 cache line 對應的真實的物理地址中所謂的 drity 屬性,

即寫操作時, 更新了相應的 cache-line,但是沒有更新到真實的物理地址,而這個 clean 的動作, 就是將 cache 中的內容更新到真實的物理地址中。

 

SCB_CleanDCache_by_Addr()

 

  根據地址信息 clean 其對應的 cache-line

 

SCB_CleanInvalidateDCache_by_Addr()

 

  根據地址信息 clean invalidate 其對應的 cache-line

 

SCB_CleanInvalidateDCache()

七. 其他指令解釋

功能描述
DMB
數據存儲器隔離。DMB 指令保證: 僅當所有在它前面的存儲器訪問操作
都執行完畢后,才提交(commit)在它后面的存儲器訪問操作。

 

DSB
數據同步隔離。比 DMB 嚴格: 僅當所有在它前面的存儲器訪問操作
都執行完畢后,才執行在它后面的指令(亦即任何指令都要等待存儲器訪 問操作——譯者注)

ISB
指令同步隔離。最嚴格:它會清洗流水線,以保證所有它前面的指令都執
行完畢之后,才執行它后面的指令。

對於高級底層技巧:“自我更新”(self-mofifying)代碼,非常有用。舉例 來說,如果某個程序從下一條要執行的指令處更新了自己,

但是先前的舊指令已經被預取到流水線 中去了,此時就必須清洗流水線,把舊版本的指令洗出去,再預取新版本的指令。因此,必須在被 更新代碼段的前面使用 ISB,以保證舊的代碼從流水線中被清洗出去,不再有機會執行

 

Bootloader在跳轉到app之前的清洗,就是1個例子,如下:

--------------------------------------------

 

  LPUART_Deinit(LPUART1);

 

    vSceneRenew();

__ISB();

    __DSB();

    /* Enable I cache and D cache */

    SCB_DisableDCache();

    SCB_DisableICache();

vControlSwitch();

 

 

 --------------------------------------------------------------------

L1CACHE_InvalidateICacheByRange函數也是1個例子:

/**Invalidate cortex-m7 L1 instruction cache by range.***/

 

void L1CACHE_InvalidateICacheByRange(uint32_t address, uint32_t size_byte)

{

#if (__DCACHE_PRESENT == 1U)

 uint32_t addr = address & (uint32_t)~(FSL_FEATURE_L1ICACHE_LINESIZE_BYTE - 1);

    int32_t size = size_byte + address - addr;

    uint32_t linesize = 32U;

 __DSB();

 while (size > 0)

    {

        SCB->ICIMVAU = addr;

        addr += linesize;

        size -= linesize;

  }

    __DSB();

    __ISB();

#endif    

}

以上內容參考了網上很多博客,還有官方文檔,再次表示感謝,若有侵權,請聯系刪除,謝謝!

技術交流微信 18124528727.

-------------------------------------------------END---------------------------------------------------------------


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM