簡體   English   中英

ARMv7使用的指令數

[英]Number of instructions used ARMv7

我試圖弄清楚將使用多少CPU周期來執行延遲功能

delay:
 subs r0, #1
 bmi end_delay
 b delay
 end_delay:
 bx lr

我覺得直覺上每個指令應該使用1個CPU周期,所以如果我們從r0 = 4開始,那么需要11個CPU周期才能完成以下代碼是正確的嗎?

我覺得直覺上每個指令應該使用1個CPU周期,所以如果我們從r0 = 4開始,那么需要11個CPU周期才能完成以下代碼是正確的嗎?

鑒於大多數ARM CPU具有3-8個流水線級,很難說大多數指令需要1個CPU周期才能完成。 理想情況下,在流水線型CPU中​​,應該有一條指令在每個時鍾周期退出,但由於上面的代碼具有分支語句,因此很難判斷每條指令何時退出。 原因是我們不知道如何處理分支將取決於處理器設計中存在的分支預測器算法。 因此,如果預測是正確的,那么管道中不會插入任何氣泡,但如果它被正確預測,那么它將取決於內部管道結構將插入多少氣泡。 對於理想的5級流水線,每次誤預測都會插入2個氣泡。 但這又取決於內部微架構的實現。 因此,很難准確地預測上述代碼將采用多少周期。

cortex-m與微芯片pic芯片(或z80和其他一些)不同,你不能用這個指令集以這種方式創建可預測的延遲。 你可以確保它會在一個時間點(時鍾)處於或低位但不正常。

0000009c <hello>:
  9c:   3801        subs    r0, #1
  9e:   d1fd        bne.n   9c <hello>

你的循環在那里有一個分支決策,更多的指令和更多的路徑基本上所以執行時間變化的機會變得更糟。

00000090 <delay>:
  90:   3801        subs    r0, #1
  92:   d400        bmi.n   96 <end_delay>
  94:   e7fc        b.n 90 <delay>

00000096 <end_delay>:

所以如果我們專注於這三個指令。

一些cortex-ms有一個構建(邏輯)時間選項,每個指令或每個單詞取,cortex-m4文檔說:

所有提取都是全字的。

所以我們希望半字對齊不會影響性能。 根據這些說明,我們不一定希望看到差異。 對於全尺寸的手臂,提取是多個單詞,因此您肯定會看到提取線(大小)影響。

執行在很大程度上取決於實現。 cortex-m只是手臂核心,芯片的其余部分來自芯片供應商,購買IP或內置或組合(很可能是后者)。 ARM不制造芯片(除了可能用於驗證),他們制造出他們銷售的IP。

芯片供應商確定閃存(和ram)實現,通常使用這些類型的芯片,閃存速度等於或低於CPU速度,這意味着它可能需要兩個時鍾來獲取一條指令,這意味着你永遠不會像cpu那樣快速地提供cpu。它可以去。 有些人喜歡ST有一個他們放入的緩存,你不能(據我所知)關閉,所以很難看到這種效果(但仍然可能),我使用的特定芯片說:

8.2.3.1預取緩沖區Flash存儲器控制器有一個預取緩沖區,當CPU頻率大於40 MHz時自動使用。 在此模式下,閃存以系統時鍾的一半運行。 預取緩沖區每個時鍾取出兩個32位字,允許在代碼線性執行時讀取沒有等待狀態的指令。 獲取緩沖區包括分支推測機制,該機制識別分支並通過不讀取下一個字對來避免額外的等待狀態。 此外,短循環分支通常留在緩沖區中。 因此,某些分支可以在沒有等待狀態的情況下執行。 其他分支機構會產生一個等待狀態。

當然,就像ST一樣,他們並沒有真正告訴你整個故事。 所以我們進去嘗試一下。 如果需要,您可以使用調試計時器,但是systick運行相同的時鍾並為您提供相同的結果

00000086 <test>:
  86:   f3bf 8f4f   dsb sy
  8a:   f3bf 8f6f   isb sy
  8e:   680a        ldr r2, [r1, #0]

00000090 <delay>:
  90:   3801        subs    r0, #1
  92:   d400        bmi.n   96 <end_delay>
  94:   e7fc        b.n 90 <delay>

00000096 <end_delay>:
  96:   680b        ldr r3, [r1, #0]
  98:   1ad0        subs    r0, r2, r3
  9a:   4770        bx  lr

所以我讀了CCR和CPUID

00000200 CCR
410FC241 CPUID

只因為。 然后運行三次測試代碼

00000015
00000015
00000015

這些數字是十六進制的,因此是21條指令。 每次執行時間相同,因此沒有緩存或分支預測緩存效果。 我沒有看到任何與皮質-m4相關的分支預測相關的其他皮質-ms確實有分支預測(可能只有m7)。 我關閉了I和D緩存,它們當然會隨着對齊而大大影響執行時間(並且該時間可能會隨着應用程序的運行而變化)。

我更改了對齊方式(在此代碼前添加或刪除nops)

0000008a <delay>:
  8a:   3801        subs    r0, #1
  8c:   d400        bmi.n   90 <end_delay>
  8e:   e7fc        b.n 8a <delay>

它沒有影響執行時間。

使用此處理器的AFAIK我們無法直接更改閃存等待狀態設置它是基於時鍾設置自動運行,因此以不同的時鍾速度運行,高於40Mhz標記我得到

0000001E                                                                                         
0000001E                                                                                         
0000001E 

對於相同的機器代碼,現在相同的對齊30個時鍾而不是21個。

通常ram更快,沒有等待狀態(理解這些總線每次事務需要幾個時鍾,所以它不像過去那樣,但是你仍然可以檢測到延遲),所以在ram中運行這些指令應該告訴我們一些事情

for(rb=0;rb<0x20;rb+=2)
{

    hexstrings(rb);
    ra=0x20001000+rb;
    PUT16(ra,0x680a); ra+=2;
    hexstrings(ra);
    PUT16(ra,0x3801); ra+=2;
    PUT16(ra,0xd400); ra+=2;
    PUT16(ra,0xe7fc); ra+=2;
    PUT16(ra,0x680b); ra+=2;
    PUT16(ra,0x1ad0); ra+=2;
    PUT16(ra,0x4770); ra+=2;

    PUT16(ra,0x46c0); ra+=2;
    PUT16(ra,0x46c0); ra+=2;
    PUT16(ra,0x46c0); ra+=2;
    PUT16(ra,0x46c0); ra+=2;
    PUT16(ra,0x46c0); ra+=2;
    PUT16(ra,0x46c0); ra+=2;
    hexstring(BRANCHTO(4,STCURRENT,0x20001001+rb)&STMASK);
}

這當然有趣......

00000000 20001002 00000026                                                                       
00000002 20001004 00000020                                                                       
00000004 20001006 00000026                                                                       
00000006 20001008 00000020                                                                       
00000008 2000100A 00000026                                                                       
0000000A 2000100C 00000020                                                                       
0000000C 2000100E 00000026                                                                       
0000000E 20001010 00000020                                                                       
00000010 20001012 00000026                                                                       
00000012 20001014 00000020                                                                       
00000014 20001016 00000026                                                                       
00000016 20001018 00000020                                                                       
00000018 2000101A 00000026                                                                       
0000001A 2000101C 00000020                                                                       
0000001C 2000101E 00000026                                                                       
0000001E 20001020 00000020 

首先是32或38個時鍾,第二個是對齊效果

armv7-m CCR顯示了一個分支預測位,但是trm和供應商文檔沒有顯示它,因此它可能是一個通用的東西,並非所有內核都支持。

因此對於特定的cortex-m4芯片,執行循環的時間在21到38個時鍾之間,如果我願意的話,我可能會讓它變慢。 我不認為我可以在這個籌碼上降到11。

如果您正在進行i2c比特敲擊,您可以使用類似這樣的延遲,它將工作正常,不會是最佳的,但會工作得很好。 如果您需要在一個時間窗口內更精確,但不大於此時使用定時器(並理解輪詢或中斷您的准確性將有一些錯誤)如果定時器外圍設備或其他可以生成您想要的信號然后得到一個時鍾准確的波形(如果這是你的延遲)。

另外一個cortex-m4預計會有不同的結果,我希望stm32能讓sram與flash相同或更快,而不是像這種情況那樣慢。 如果您依靠其他人來設置您的芯片,那么您可以使用初始化代碼來處理這些設置,這會影響執行時間。

編輯

我不知道我在哪里得到了一個cortex-m4這是一個armv7-m的想法,所以我沒有一個朴素pi 2方便,但有一個pi3,並運行在aarch32模式,32位指令。 我不知道這會讓計時器運行然后啟用緩存有多少工作。 pi用盡了dram,即使是裸露的金屬也非常不一致。 所以我想我會啟用l1緩存,並且在第一次運行之后它應該全部在緩存中並且是一致的。 現在我想到它有四個核心,每個都運行,不知道如何禁用它們,其他三個正在循環旋轉等待郵箱寄存器告訴他們運行什么代碼。 也許我需要將它們分支到某個地方並且用完l1緩存...不確定l1是每個核心還是共享,我想我在某一點看起來。

無論如何代碼正在測試中

000080c8 <COUNTER>:
    80c8:   ee192f1d    mrc 15, 0, r2, cr9, cr13, {0}

000080cc <delay>:
    80cc:   e2500001    subs    r0, r0, #1
    80d0:   4a000000    bmi 80d8 <end_delay>
    80d4:   eafffffc    b   80cc <delay>

000080d8 <end_delay>:
    80d8:   ee193f1d    mrc 15, 0, r3, cr9, cr13, {0}
    80dc:   e0430002    sub r0, r3, r2
    80e0:   e12fff1e    bx  lr

並且打孔線用於該對齊,第一列是r0通過,接下來的三個是三次運行,最后一列是否有從先前運行到當前的增量(r0中額外計數值的成本)

00000000 0000000A 0000000A 0000000A 
00000001 00000014 00000014 00000014 0000000A 
00000002 0000001E 0000001E 0000001E 0000000A 
00000003 00000028 00000028 00000028 0000000A 
00000004 00000032 00000032 00000032 0000000A 
00000005 0000003C 0000003C 0000003C 0000000A 
00000006 00000046 00000046 00000046 0000000A 
00000007 00000050 00000050 00000050 0000000A 
00000008 0000005A 0000005A 0000005A 0000000A 
00000009 00000064 00000064 00000064 0000000A 
0000000A 0000006E 0000006E 0000006E 0000000A 
0000000B 00000078 00000078 00000078 0000000A 
0000000C 00000082 00000082 00000082 0000000A 
0000000D 0000008C 0000008C 0000008C 0000000A 
0000000E 00000096 00000096 00000096 0000000A 
0000000F 000000A0 000000A0 000000A0 0000000A 
00000010 000000AA 000000AA 000000AA 0000000A 
00000011 000000B4 000000B4 000000B4 0000000A 
00000012 000000BE 000000BE 000000BE 0000000A 
00000013 000000C8 000000C8 000000C8 0000000A 

如果對上面的代碼(第一列中的地址)和r0為四的結果嘗試不同的對齊,那么最終我不需要做對齊檢查。

00010000 00000032 00010004 0000002D 00010008 00000032 0001000C 0000002D

這重復到地址0x101FC

如果我在編譯的測試中更改對齊方式

000080cc <COUNTER>:
    80cc:   ee192f1d    mrc 15, 0, r2, cr9, cr13, {0}

000080d0 <delay>:
    80d0:   e2500001    subs    r0, r0, #1
    80d4:   4a000000    bmi 80dc <end_delay>
    80d8:   eafffffc    b   80d0 <delay>

000080dc <end_delay>:
    80dc:   ee193f1d    mrc 15, 0, r3, cr9, cr13, {0}
    80e0:   e0430002    sub r0, r3, r2
    80e4:   e12fff1e    bx  lr

然后它會慢一點。

00000000 00000009 00000009 00000009 
00000001 00000012 00000012 00000012 00000009 
00000002 0000001B 0000001B 0000001B 00000009 
00000003 00000024 00000024 00000024 00000009 
00000004 0000002D 0000002D 0000002D 00000009 
00000005 00000036 00000036 00000036 00000009 
00000006 0000003F 0000003F 0000003F 00000009 
00000007 00000048 00000048 00000048 00000009 
00000008 00000051 00000051 00000051 00000009 
00000009 0000005A 0000005A 0000005A 00000009 
0000000A 00000063 00000063 00000063 00000009 
0000000B 0000006C 0000006C 0000006C 00000009 
0000000C 00000075 00000075 00000075 00000009 
0000000D 0000007E 0000007E 0000007E 00000009 
0000000E 00000087 00000087 00000087 00000009 
0000000F 00000090 00000090 00000090 00000009 
00000010 00000099 00000099 00000099 00000009 
00000011 000000A2 000000A2 000000A2 00000009 
00000012 000000AB 000000AB 000000AB 00000009 
00000013 000000B4 000000B4 000000B4 00000009 

如果我把它改成函數調用

000080cc <COUNTER>:
    80cc:   e92d4001    push    {r0, lr}
    80d0:   ee192f1d    mrc 15, 0, r2, cr9, cr13, {0}
    80d4:   eb000003    bl  80e8 <delay>
    80d8:   ee193f1d    mrc 15, 0, r3, cr9, cr13, {0}
    80dc:   e8bd4001    pop {r0, lr}
    80e0:   e0430002    sub r0, r3, r2
    80e4:   e12fff1e    bx  lr

000080e8 <delay>:
    80e8:   e2500001    subs    r0, r0, #1
    80ec:   4a000000    bmi 80f4 <end_delay>
    80f0:   eafffffc    b   80e8 <delay>

000080f4 <end_delay>:
    80f4:   e12fff1e    bx  lr

00000000 0000001A 0000001A 0000001A 
00000001 00000023 00000023 00000023 00000009 
00000002 0000002C 0000002C 0000002C 00000009 
00000003 00000035 00000035 00000035 00000009 
00000004 0000003E 0000003E 0000003E 00000009 
00000005 00000047 00000047 00000047 00000009 
00000006 00000050 00000050 00000050 00000009 
00000007 00000059 00000059 00000059 00000009 
00000008 00000062 00000062 00000062 00000009 
00000009 0000006B 0000006B 0000006B 00000009 
0000000A 00000074 00000074 00000074 00000009 
0000000B 0000007D 0000007D 0000007D 00000009 
0000000C 00000086 00000086 00000086 00000009 
0000000D 0000008F 0000008F 0000008F 00000009 
0000000E 00000098 00000098 00000098 00000009 
0000000F 000000A1 000000A1 000000A1 00000009 
00000010 000000AA 000000AA 000000AA 00000009 
00000011 000000B3 000000B3 000000B3 00000009 
00000012 000000BC 000000BC 000000BC 00000009 
00000013 000000C5 000000C5 000000C5 00000009 

每個計數的成本是相同的,但是呼叫開銷更昂貴

這允許我使用拇指模式只是為了好玩,以避免添加鏈接器的模式更改我使它更快(和一致)。

000080cc <COUNTER>:
    80cc:   e92d4001    push    {r0, lr}
    80d0:   e59f103c    ldr r1, [pc, #60]   ; 8114 <edel+0x2>
    80d4:   e59fe03c    ldr lr, [pc, #60]   ; 8118 <edel+0x6>
    80d8:   ee192f1d    mrc 15, 0, r2, cr9, cr13, {0}
    80dc:   e12fff11    bx  r1

000080e0 <here>:
    80e0:   ee193f1d    mrc 15, 0, r3, cr9, cr13, {0}
    80e4:   e8bd4001    pop {r0, lr}
    80e8:   e0430002    sub r0, r3, r2
    80ec:   e12fff1e    bx  lr

000080f0 <delay>:
    80f0:   e2500001    subs    r0, r0, #1
    80f4:   4a000000    bmi 80fc <end_delay>
    80f8:   eafffffc    b   80f0 <delay>

000080fc <end_delay>:
    80fc:   e12fff1e    bx  lr
    8100:   e1a00000    nop         ; (mov r0, r0)
    8104:   e1a00000    nop         ; (mov r0, r0)
    8108:   e1a00000    nop         ; (mov r0, r0)

0000810c <del>:
    810c:   3801        subs    r0, #1
    810e:   d400        bmi.n   8112 <edel>
    8110:   e7fc        b.n 810c <del>

00008112 <edel>:
    8112:   4770        bx  lr

00000000 000000F4 0000001B 0000001B 
00000001 00000024 00000024 00000024 00000009 
00000002 0000002D 0000002D 0000002D 00000009 
00000003 00000036 00000036 00000036 00000009 
00000004 0000003F 0000003F 0000003F 00000009 
00000005 00000048 00000048 00000048 00000009 
00000006 00000051 00000051 00000051 00000009 
00000007 0000005A 0000005A 0000005A 00000009 
00000008 00000063 00000063 00000063 00000009 
00000009 0000006C 0000006C 0000006C 00000009 
0000000A 00000075 00000075 00000075 00000009 
0000000B 0000007E 0000007E 0000007E 00000009 
0000000C 00000087 00000087 00000087 00000009 
0000000D 00000090 00000090 00000090 00000009 
0000000E 00000099 00000099 00000099 00000009 
0000000F 000000A2 000000A2 000000A2 00000009 
00000010 000000AB 000000AB 000000AB 00000009 
00000011 000000B4 000000B4 000000B4 00000009 
00000012 000000BD 000000BD 000000BD 00000009 
00000013 000000C6 000000C6 000000C6 00000009

這種對齊方式

0000810e <del>:
    810e:   3801        subs    r0, #1
    8110:   d400        bmi.n   8114 <edel>
    8112:   e7fc        b.n 810e <del>

00008114 <edel>:
    8114:   4770        bx  lr


00000000 0000007E 0000001C 0000001C 
00000001 00000026 00000026 00000026 0000000A 
00000002 00000030 00000030 00000030 0000000A 
00000003 0000003A 0000003A 0000003A 0000000A 
00000004 00000044 00000044 00000044 0000000A 
00000005 0000004E 0000004E 0000004E 0000000A 
00000006 00000058 00000058 00000058 0000000A 
00000007 00000062 00000062 00000062 0000000A 
00000008 0000006C 0000006C 0000006C 0000000A 
00000009 00000076 00000076 00000076 0000000A 
0000000A 00000080 00000080 00000080 0000000A 
0000000B 0000008A 0000008A 0000008A 0000000A 
0000000C 00000094 00000094 00000094 0000000A 
0000000D 0000009E 0000009E 0000009E 0000000A 
0000000E 000000A8 000000A8 000000A8 0000000A 
0000000F 000000B2 000000B2 000000B2 0000000A 
00000010 000000BC 000000BC 000000BC 0000000A 
00000011 000000C6 000000C6 000000C6 0000000A 
00000012 000000D0 000000D0 000000D0 0000000A 
00000013 000000DA 000000DA 000000DA 0000000A 

所以在這個處理器上的某個理想世界中假設緩存命中延遲代碼

00000004 00000032 00000032 00000032 0000000A 
00000004 0000002D 0000002D 0000002D 00000009 
00000004 0000003E 0000003E 0000003E 00000009 
00000004 0000003F 0000003F 0000003F 00000009 
00000004 00000044 00000044 00000044 0000000A 

在0x2D和0x44時鍾之間以r0 = 4運行該循環

實際上在這個平台上沒有啟用緩存和/或如果你得到緩存未命中,你會看到什么。

00000000 0000030B 000002B7 000002ED 
00000001 0000035B 00000389 000003E9 
00000002 000003FB 00000439 0000041B 
00000003 0000058F 000004E7 0000055B 
00000004 000005FF 0000069D 000006D1 
00000005 00000745 00000733 000006F7 
00000006 00000883 00000817 00000801 
00000007 00000873 00000853 0000089B 
00000008 00000923 00000B05 0000092F 
00000009 00000A3F 000009A9 00000B4D 
0000000A 00000B79 00000BA9 00000C57 
0000000B 00000C21 00000D13 00000B51 
0000000C 00000C0B 00000E91 00000DE9 
0000000D 00000D97 00000E0D 00000E81 
0000000E 00000E5B 0000100B 00000F25 
0000000F 00001097 00001095 00000F37 
00000010 000010DB 000010FD 0000118B 
00000011 00001071 0000114D 0000123F 
00000012 000012CF 0000126D 000011DB 
00000013 0000140D 0000143D 0000141B 
000002B7 0000143D 

r0 = 4行

00000004 000005FF 0000069D 000006D1 

這就是很多cpu計數......

希望我把這個話題放到床上。 雖然有趣的是嘗試假設代碼運行速度有多快或有多少計數等等......在這些類型的處理器,流水線,高速緩存,分支預測,復雜的系統總線上,使用通用核心並不是那么簡單。各種芯片實現,其中芯片供應商管理與處理器IP供應商代碼分開的存儲器/閃存。

在第二個實驗中我沒有弄亂分支預測,如果我這樣做,那么對齊就不會那么一致,這取決於分支預測是如何實現的,它可以根據分支相對於獲取線的位置來改變其有用性作為下一個獲取已啟動或未啟動或是某種方式,當分支預測器確定它不需要執行該提取和/或啟動分支提取時,在這種情況下,分支是兩個提前,所以你可能看不到這個代碼,你會想要在它們之間插入一些nops,以便bmi目的地位於單獨的提取行中(以便查看差異)。

這是操作的簡單方法,使用相同的機器代碼序列,並通過我們看到的內容看到執行時間的變化。 在0x3F和0x6D1之間,對於相同的機器代碼,最快和最慢之間的差異超過27倍。 通過一條指令改變代碼的對齊(在不相關的代碼中的其他地方有一個或多一個來自先前構建的指令)是5個計數差異。

公平地說,測試結束時的mrc可能是時間的一部分

000080c8 <COUNTER>:
    80c8:   ee192f1d    mrc 15, 0, r2, cr9, cr13, {0}
    80cc:   ee193f1d    mrc 15, 0, r3, cr9, cr13, {0}
    80d0:   e0430002    sub r0, r3, r2
    80d4:   e12fff1e    bx  lr

導致計數為1,兩者都對齊。 所以並不能保證測量中只有一個錯誤計數,但可能不是十幾個。

無論如何,我希望這有助於你理解。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM