我在ARM Cortex M4 程式集中遇到了 LDR 和 STR 指令問題。出于某種原因,它們在記憶體中寫入/讀取某些部分比其他部分花費更長的時間。
為了說明這一點,我設定了這個簡單的示例:
我創建了一個專案,其中包含一個主 C 檔案,以及一個包含匯編代碼的相鄰“.S”檔案。我使用“extern”物件將匯編函式包含到我的 C 檔案中。
//Add the asm functions to our C code
extern "C" void LoadTest(uint32_t *memory_adress);
extern "C" void LoadTestLoop(uint32_t *memory_adress);
這是程式的作用:
void perform_test()
{
//Time
register uint32_t register_before_time=before_time;
register uint32_t register_after_time=after_time;
register uint32_t* input_address=0x400E9000;
register_before_time=ARM_DWT_CYCCNT;
//Time measurment occurs in here!
LoadTestLoop(input_address);
register_after_time=ARM_DWT_CYCCNT;
Serial.print(" Time: ");
Serial.println(register_after_time-register_before_time-time_error);
}
它向我們展示了在 “register_before_time=ARM_DWT_CYCCNT;”之間執行某些操作所花費的時間。和“register_after_time=ARM_DWT_CYCCNT;” 線。
以下是我們將測驗其速度的匯編子程式:
.global LoadTest
LoadTest:
ldr r1, [r0] /*Load value into r1 from memory_address*/
orr r1, #0xC0 /*OR bits 7,6 to be on.*/
str r1, [r0] /*Store the changed value back into memory_address*/
bx lr
.global LoadTestLoop
LoadTestLoop:
mov r2, #255 /* Set r2 to be 255 for the loop*/
TestLoop: /*Same code as before*/
ldr r1, [r0]
orr r1, #0xC0
str r1, [r0]
subs r2, r2, #1 /*Decrement r2 set Z flag if it's zero*/
bne TestLoop /*Repeat until r2==0*/
bx lr
LoadTest – 從我們給它的地址加載一個值。將值與 0xC0 進行或運算,然后將其存盤回同一地址。
LoadTestLoop – 做同樣的事情,但是,在一個回圈中執行 255 次,這樣我們可以得到一個回圈迭代所花費的平均時間,并最大限度地減少進出函式的分支指令的時間測量誤差。
注意:為了最大限度地減少測量誤差,在時間測量區域之外的 input_address 指標中為兩個函式提供了要處理的地址。
register uint32_t* input_address=0x400E9000;
測驗結果及問題:
我為兩個普通的 C 變數運行了這兩個測驗
uint32_t test_value=255;
register uint32_t* input_address=&test_value;
對于微控制器內部的配置暫存器。請注意,在資料表中,它們僅顯示為記憶體。
register uint32_t* input_address=0x400E9000;
平均而言,標準變數的 LoadTest 需要 9 個周期來執行,但控制暫存器的執行周期要長得多,為 27 個周期。LoadTestLoop 測驗加強了這一點,標準變數平均占用 1541 個周期(每次迭代 6 個周期),控制暫存器達到驚人的 12227 個周期,每次迭代達到瘋狂的 47 個周期!
為什么會這樣?
為什么 LDR 和 STR 有時需要更長的時間來執行?
有誰知道為什么會這樣?我被這個問題困擾了很長時間,真的很想知道。
感謝您的幫助
uj5u.com熱心網友回復:
這是完全正常的。
通常,從記憶體中加載所需的時間與需要的時間一樣長。時序不受 CPU 本身的控制,因此參考的周期數只能代表“最佳情況”。如果 CPU 不能從它自己的內部結構(例如存盤緩沖區或 L1 快取)中完成負載,那么它只需將請求放在記憶體總線上并停止直到記憶體子系統回應。(或者繼續亂序執行后面的指令,如果這樣配備并且它可以找到一些不依賴于加載結果的指令。)
實際花費的時間可能變化很大,例如取決于負載是否命中或未命中 L2 或 L3 快取,另一個內核或外部設備是否持有總線鎖等。如果機器沒有快取并且所有記憶體都是快速 SRAM,那么時間可能會很穩定。
但在您的情況下,您正在加載的地址實際上映射到硬體設備。所以你根本不是在讀 RAM,你是在做 I/O。在這種情況下,回應必須來自設備本身,并且設備基本上可以根據需要花費盡可能長的時間。如果您需要能夠預測時間,那么您需要查看該設備的檔案(以及介于兩者之間的任何介面硬體),而不是 CPU 手冊中的周期計數。
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/463860.html
下一篇:SQLite:資料庫被鎖定C#