簡體   English   中英

AVR Assembly BRNE 延遲循環如何工作?

[英]How do AVR Assembly BRNE delay loops work?

對於運行在 16MHz 的芯片,在線延遲循環生成器為我提供了 0.5 秒的運行時間延遲循環。

我心中的問題是:

  1. 如果寄存器變為負數,分支是否繼續分支?
  2. 一開始加載的值究竟是如何計算的?

     ldi r18, 41 ldi r19, 150 ldi r20, 128 L1: dec r20 brne L1 dec r19 brne L1 dec r18 brne L1

要准確回答您的問題:

1: DEC指令不知道“有符號”數字,它只是遞減一個 8 位寄存器。 二進制補碼算法的奇跡使這項工作在環繞(0x00 -> 0xFF,與 0 -> -1 的位模式相同)。 DEC 指令還在狀態寄存器中設置 Z 標志, BRNE使用它來確定是否應該發生分支。

2:從AVR手冊中可以看出DEC是單周期指令。 BRNE 不分支時也是單周期,分支時2個周期。 因此,要計算循環時間,您需要計算每條路徑將被采用的次數。

考慮單個 DEC/BRNE 循環:

    ldi r8 0
L1: dec r8
    brne L1

該循環將執行 256 次,即 DEC 的 256 個周期和 BRNE 的 512 個周期,總共 768 個周期。 在 16MHz,那是 48us。

將其包裝在外部延遲循環中:

    ldi r7 10
    ldi r8 0
L1: dec r8
    brne L1
    dec r7
    brne L1

您可以看到,每次內循環計數器達到 0 時,外循環計數器都會遞減。因此,在我們的示例中,外循環 DEC/BRNE 將發生 10 次(768 個周期),而內循環將發生 10 x 256 次,因此此循環的總時間為 10 x 48us + 48us 為 528us。 同樣對於 3 個嵌套循環。

從這里開始,計算每個循環應該執行多少次以實現所需的延遲是微不足道的。 這是外循環可以執行的迭代次數少於所需時間的最大次數,然后抽出該時間,對下一個嵌套循環執行相同操作,依此類推,直到最內循環填滿剩余的少量。

一開始加載的值究竟是如何計算的?

計算總周期數 => 0.5s * 16000000 = 8000000

知道 r20 和 r19 循環的總周期(從零到零),AVR 寄存器是 8 位,所以一個完整的循環是 256 次( dec 0 = 255 )。 dec是 1 個周期。 brne條件(分支)發生時為 2 個周期,條件(分支)發生時為 1 個周期。

所以最內層的循環:

L1: dec  r20
    brne L1

從零到零( r20=0 ):255 * (1+2) + 1 * (1+1) = 767 個周期(255 次分支被采用,1 次通過)。


使用r19的第二個環繞循環是: 255 * (767+1+2) + 1 * (767+1+1) = 197119 個周期

采取分支時的單個r18循環是 197119+1+2 = 197122 個周期。 (197121 當不采用分支時 = 延遲循環的最終退出,我將在下一步中通過一個技巧來避免這個 -1)。

現在這幾乎足以計算初始r18 ,讓我們首先通過 O(1) 代碼調整總周期,這是ldi指令的三倍,需要 1 個周期: total2 = 8000000 - (1+1+1) + 1 = 7999998 ...等等,那里的最后一個 +1 是什么? 這是要延遲的假附加循環,使最終的r18循環假裝它的成本與非最終循環相同,即 197122 個循環。

就是這樣,初始r18必須足以等待至少7999998 個周期: r18 = (7999998 + 197122 - 1) div 197122 = 41 “+ 197122 - 1”部分將確保豐富的周期符合約束: 0 <= abundant_cycles < 197122 (剩余的197122除法)。

41 * 197122 = 8082002 ... 這太多了,但現在我們可以通過將r19r20設置為特定值來減少額外的周期,以微調延遲。 那么要剃多少呢? 8082002 - 7999998 = 82004個周期。

單個r19循環在分支時需要 770 個循環,在退出時需要 769 個循環,所以讓我們再次通過將 82004 調整為僅 82003 來避免 769 被剃掉。 82003 div 770 = 106 :可以跳過 106 個r19循環, r19 = 256 - 106 = 150 現在這將減少 81620 個周期,因此要減少 82003 - 81620 = 383 個周期。

單個r20循環在分支時需要 3 個周期,退出時需要 2 個周期。 我將再次考慮到退出循環只有 2 個周期 -> 383 => 382 來剃掉。 382 div 3 = 127 ,余數 1. r20 = 256 - 127 = 129並減少一個以減少額外的 3 個周期(以覆蓋該余數)= 128。然后缺少 2 個周期 (3-1) 等待以使其成為足足800萬。

所以:

    ldi  r18, 41
    ldi  r19, 150
    ldi  r20, 128
L1: dec  r20
    brne L1
    dec  r19
    brne L1
    dec  r18
    brne L1

根據我的計算,應該正好等待 8000000-2 個周期(如果沒有被其他東西打斷的話)。

讓我們嘗試驗證:

初始r20 : 127 3 + 1 2 = 383 個周期
初始r19 : 1*(383+1+2) + 148*(767+1+2) + 1*(767+1+1) = 115115 個循環(即初始r20不完整循環一次,然后 149 次全時r20由於退出brne循環最后一個為 -1 )
r18總計:1*(115115+1+2) + 39*(197119+1+2) + 1*(197119+1+1) = 7999997 個周期。

而三個ldi是+3個周期=7999997+3=8000000。

丟失的 2 個周期無處可見,所以我在某個地方犯了一個錯誤。

正如你所看到的,背后的數學相當簡單,但手工完成卻很普通,而且容易出錯……

啊,我想我知道我在哪里做錯了。 當我削減大量周期時,不涉及終止循環(這是實際延遲過程的一部分),所以我不應該將 to_shave_off 周期調整為 -1。 然后在r19 = 106我仍然需要r19 = 106 384 個周期,這正是 384/3 = 128 個循環來r20 = 256-128 = 128 無余數,無漏循環,完美800萬。

如果您無法按照此反向計算進行,請嘗試其他方式,想象 2 位寄存器(僅 0..3 個值),並在紙上使用 r18=r19=r20=2 進行類似的循環,並手動計數循環以查看它是如何發展的。 .. 即 3x ldi = +3, dec r20,brne,dec r20,brne(skip) = +5 個周期, dec r19, brne = +3, ... 等等。

編輯:Jester 之前在他的鏈接中對此進行了解釋。 而且我懶得把它整理成一些簡單的公式來創建自己的在線計算器。

暫無
暫無

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

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