1 Cache的原理 Cache即高速緩存,它的出現基于兩種因素:一、CPU的速度和性能提高很快,而主存速度較低且價格高;二、程序執行的局部性特點。將速度較快而容量有限的SRAM構成Cache,可以盡可能發揮CPU的高速度。CPU與外設交換數據時經常用到buffer(緩沖),這與緩存極其相似,只不過Cache是為了提高CPU和內存之間的數據交換速度而設計,而buffer是為了提高內存和硬盤(或其他I/O設備)之間的數據交換速度而設計的。 Baidu快照(cache.baidu.com)就是一個緩存的例子,其作用與計算機CPU緩存有類似之處。 Cache的原理如圖1所示。 在讀取內存數據的同時CPU將數據保存到Cache數據區,同時更新Cache映射表(保存地址信息,表示該地址的數據是否已在Cache數據區,即是否命中)。這樣,CPU再次讀取該地址數據時,就可以直接從Cache提取。讀Cache的時間遠小于直接讀內存,可提高CPU讀取數據的效率。 Cache數據區有成塊讀取的特性(Cache映射表保存的地址是塊地址,節省空間,也符合程序執行的局部性特點)。Cache數據區遠遠小于內存空間,就需要相應的替代算法。比如最近最少使用算法,可將新數據替代使用頻率低的數據,同時更新映射表信息。可以推想,Cache空間越大,命中率越高。 寫內存需要直接更新內存。如果映射表存在該地址信息,還需要同時更新Cache數據區。這種Cache訪問方式就稱作“直寫”,Samsung公司的ARM7微處理器S3CA510B就是這種方式。以下所討論的Cache問題除非特殊說明,否則都是“直寫”方式。 2 嵌入式編程時需注意的問題 2.1 訪問外設使用Cache的問題 在訪問內存時使用Cache是不會出現問題的,但如果訪問數據易變外設(數據不依賴于CPU寫操作而改變)時使用Cache就可能出現問題。問題在于外設數據的改變不僅僅依靠CPU寫操作,CPU第一次讀取外設數據時將外設的數據和地址信息保存到Cache,第二次讀取外設數據時就可能有問題出現。這是因為數據直接從Cache提取,而外設的數據可能有改變。 因此,在訪問易變外設時要禁止使能Cache,直接讀取外設數據到CPU,而不經過Cache的任何環節,即保證不改變Cache映射表和Cache數據區內容。 S3C4510B的SYSCFG SFR(特殊功能寄存器)有用來控制Cache使能或不使能的,通過對該SFR的設置可暫時禁止Cache或重新恢復Cache功能。這樣就可以在讀取外設前禁止Cache,讀取結束后重新使能Cache,保證了外設數據讀取的正確性。寫數據到外設時采用“直寫”方式,更沒有問題。 2.2 開關Cache引發的新問題 在Cache開關期間,如果有另一個進程/任務訪問內存,在此期間寫內存并且該內存在Cache中已有映射(注意,它也是被禁止Cache的,所以它不會同時更新Cache數據區的內容),那么在Cache重新使能之后Cache數據區的信息已經過時了,而Cache映射表還是Cache禁止之前的狀態,如果CPU此時讀數據就會得到過時的數據。這樣看來,引發的問題范圍更廣了,連內存的數據讀寫正確性都無法保證。與內存泄漏的影響來比較,內存泄漏如果是一顆定時炸彈,那么Cache問題就可以說是隨時隨地都可能踩上的雷區,因為程序一旦開始就可能引發爆炸。 如圖2所示,Cache使能時Cache映射表和Cache數據區保存了內存的數據信息,這是CPU訪問內存時通過圖中實線箭頭通路實現的。內存的信息可以與Cache的信息保持一致。 Cache禁止時的情況有所變化。由圖2中虛線箭頭通路直接進行內存訪問,且地址0x00處的數據由55變為AA,但Cache區的信息仍為之前的狀態。很明顯,Cache的數據是應該廢棄的,但是Cache映射表仍保存0x00的地址信息。Cache重新使能后,CPU再次讀取0x00地址的數據,由于Cache仍是命中,直接從Cache數據區中提取數據,這樣讀出來的數據就是0x55了。 由S3C4510B數據手冊第4節的第21頁可知:通過對SYSCFG寄存器的CE位置1或清0可使能/關閉Cache,但是Cache沒有內容自動刷新功能,在重新使能Cache時需考慮Cache數據的正確性。 為了證實以上說法,循環執行如下測試程序: 如果沒有Cache的影響,結果應該是55 aa aa。可見,Cache關閉再打開的確可造成Cache數據過時。 3 其他CPU解決方案 Atmel公司的AT91RM9200和Samsung公司的S3C44B0,用這兩種CPU先后移植過操作系統,且在對外設訪問的整個過程中Cache都是使能的。它們的解決方案是什么呢? AT91RM9200是ARM9系列帶有MMU的CPU。MMU對內存有分頁管理功能,可以實現多個進程的內存空間保護。Cache是通過MMU管理的,這也是Cache和MMU經常同時存在的原因。 S3C44B0和S3C4510B同樣都是Samsung公司產品,并且都不帶MMU。與S3CA510不同的是,S3C4480自帶的SFR可以配置非緩存范圍,即使Cache使能,所設置范圍的地址空間訪問也不通過Cache實現。這樣,可以很方便地實現內存是緩存區,其他外設是非緩存區。 這兩種方案對于S3C4510B都無法實現。網絡上有人用volatile關鍵字解決外設訪問問題。volatile關鍵字是在源代碼中給編譯器看的,它可能影響編譯器的編譯結果,但是最終CPU執行都體現到匯編語句,如果匯編語句都不能解決Cache問題,volatile語句也是不可能解決的。 對于易變數據的外設使用volatile關鍵字是應該的,可避免編譯器的優化,比如以下語句: 在兩次讀取PORTAdd地址的數據相同時等待,可以用到等待信號跳變的程序。如果將volatile關鍵字去除,有可能經編譯器優化,Value2不會從實際的portAdd地址讀取數據,而是利用Valuel讀取語句的中間寄存器直接獲得。 4 本文解決方案 由S3C4510B手冊上第5節的第4頁可知,可以通過兩種方式保證Cache數據的正確: ①對Cache映射表的Tag RAM數據清零。Cache映射表數據一般是通過上電復位清零的,如果Cache或內存段的設置被修改,則會造成Cache映射表數據廢棄,這時就需要通過程序對Cache映射表數據清0。 ②S3C4510B提供非Cache方式訪問控制位,控制位ADDR[26](地址線26位)為“1”時,按非Cache方式訪問。因此,Cache使能的情況下,地址0x000 0000~0x3FFFFFF按Cache方式訪問,而0x400 0000~0x7FF FFFF按非Cache方式訪問。實際上,0x000 0000+offset與0x400 0000+offset(offset在0x000 0000~0X3FF FFFF之間)是同一地址,不同的是Cache是否起作用。 可以得到兩種解決方案: (1)Cache映射表手動更新 既然在開關Cache之后內容過時,并且CPU不會自動刷新,可以通過手動更新的辦法來拋棄廢舊信息。也就是說,將Tag RAM區(前面所說的Cache映射表)清除,這樣所有Cache數據區的內容都不使能,再次讀取數據時同時更新Cache映射表和Cache數據區內容,之后才能使用。清除操作將Tag RAM的1 KB內容清零,需要消耗一定時間;并且這樣操作后Cache是0命中率的,只有一定訪問次數后Cache信息重新填滿,才能恢復正常的命中率。因此,頻繁地開關Cache時采用這種方案是不可取的。 (2)bit26位控制Cache使能 S3C4510B的地址線為26位(bit0~bit25),實際上CPU可訪問空間為32位(bit0~bit31)。一般我們都不使用bit26~bit31,不過S3C4510B的這些位有著特殊的控制功能。通過bit26的高電平可以禁止該地址的Cache功能,因此將外設的地址由原來的ADDR_PORT改為(ADDR PORT∣(15 修改程序后的試驗結果 修改Cache解決方案后,可以解決內存訪問錯誤的問題。經過測試,采用“bit26位控制Cache使能”的方案可以順利訪問外設,代碼執行始終是在Cache使能的情況下,并且不影響內存數據。若完全關閉Cache的程序,執行同樣代碼需要花費5~8倍的時間。 |