[英]How do AVR Assembly BRNE delay loops work?
對於運行在 16MHz 的芯片,在線延遲循環生成器為我提供了 0.5 秒的運行時間延遲循環。
我心中的問題是:
一開始加載的值究竟是如何計算的?
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
... 這太多了,但現在我們可以通過將r19
和r20
設置為特定值來減少額外的周期,以微調延遲。 那么要剃多少呢? 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.