什么是ARM中的SP(堆棧)和LR?


LR是用於保存函數調用的返回地址的link register

SP是堆棧指針。堆棧通常用於在函數調用中保存”automatic”變量和上下文/參數。從概念上講,您可以將”stack”視為您”pile”您的數據的地方。您將”stacking”保留在一個數據之上,堆棧指針告訴您”high”的數據是”stack”。您可以從”stack”的”top”中刪除數據並縮短。

從ARM架構參考:

SP, the Stack Pointer

Register R13 is used as a pointer to the active stack.

In Thumb code, most instructions cannot access SP. The only instructions that can access SP are those designed to use SP as a stack pointer. The use of SP for any purpose other than as a stack pointer is deprecated. Note Using SP for any purpose other than as a stack pointer is likely to break the requirements of operating systems, debuggers, and other software systems, causing them to malfunction.

LR, the Link Register

Register R14 is used to store the return address from a subroutine. At other times, LR can be used for other purposes.

When a BL or BLX instruction performs a subroutine call, LR is set to the subroutine return address. To perform a subroutine return, copy LR back to the program counter. This is typically done in one of two ways, after entering the subroutine with a BL or BLX instruction:

• Return with a BX LR instruction.

• On subroutine entry, store LR to the stack with an instruction of the form: PUSH {,LR} and use a matching instruction to return: POP {,PC} …

This link gives an example of a trivial subroutine.

Here is an example of how registers are saved on the stack prior to a call and then popped back to restore their content.

 

次佳解決方案

SP是堆棧寄存器,用於鍵入r13的快捷方式。 LR是r14的快捷方式。 PC是程序計數器,用於輸入r15的快捷鍵。

當執行一個調用,稱為分支鏈接指令bl時,返回地址放在r14(鏈接寄存器)中。程序計數器pc更改為您要分支的地址。

傳統的ARM內核中有幾個堆棧指針(cortex-m系列是一個例外),當您打中斷時,例如使用與前台運行時不同的堆棧,您不必更改代碼,只需使用sp或r13正常情況下,硬件已經為您完成了開關,並在解碼指令時使用正確的開關。

傳統的ARM指令集(而不是Thump指令集)使您可以自由使用從低地址到更高地址的堆棧,或者從高地址到低地址的增長。編譯器和大多數人將堆棧指針設置為高電平,並將其從高地址下降到較低的地址。例如,例如,您可以將ram從0x20000000設置為0x20008000,您可以設置鏈接描述文件來構建程序來運行/使用0x20000000,並將啟動代碼中的堆棧指針設置為0x20008000,至少要將系統/用戶堆棧指針分開其他堆棧的內存,如果你需要/使用它們。

堆棧只是 memory 。處理器通常具有基於PC的特殊存儲器讀/寫指令,一些是基於堆棧的。堆棧的最小值通常被稱為push和pop,但不一定是(和傳統的arm指令一樣)。

如果你去http://github.com/lsasim我創建了一個教學處理器,並有一個匯編語言教程。在那里我會經歷關於堆棧的討論。它不是一個ARM處理器,但故事是一樣的,它應該直接轉換到你想要在ARM或大多數其他處理器上理解。

例如,您的程序中需要20個變量,但只有16個寄存器減去至少三個(sp,lr,pc),這些是特殊用途。你將不得不將一些變量保留在ram中。讓我們說r5擁有一個你經常使用的變量,你不想保持在ram中,但是有一段代碼,你真的需要另一個注冊表來做某事,r5沒有被使用,你可以保存r5堆棧以最小的努力,而您重用r5的其他東西,然后,很容易,恢復它。

傳統(不一定都回到起初)ARM語法:

...
stmdb r13!,{r5}
...temporarily use r5 for something else...
ldmia r13!,{r15}
..

 

stm是存儲多個,一次可以保存多個寄存器,直到所有這些都在一個指令中。

db表示之前遞減,這是從高地址到較低地址的向下移動堆棧。

您可以使用r13或sp來指示堆棧指針。該特定指令不限於堆棧操作,可用於其他操作。

的!意味着在完成后用新地址更新r13寄存器,這里再次使用stm可以用於non-stack操作,因此您可能不想更改基地址寄存器,離開!在這種情況下。

然后在括號{}中列出要保存的寄存器,以逗號分隔。

ldmia是相反的,ldm表示加載多個。 ia表示遞增,其余與stm相同

所以如果你的堆棧指針在0x20008000,當你打到stmdb指令看到,因為列表中有一個32位寄存器,它將在它使用它之前減少r13中的值,所以0x20007FFC然后它在存儲器中寫入r5到0x20007FFC,並保存值0x137FFC在r13。后來,假設你沒有錯誤,當你得到ldmia指令r13有0x20007FFC在其中有一個單一的注冊表在列表r5。所以它在0x20007FFC讀取內存將該值放在r5中,ia表示增量后,0x20007FFC將一個寄存器大小增加到0x20008000,而!意味着將該號碼寫入r13以完成指令。

你為什么要使用堆棧而不是固定的內存位置?那么上面的美妙之處在於,當您運行該代碼或0x20002000或其他任何代碼仍然可以運行時,r13可以是0x20007654,如果您在循環中使用該代碼,或者在循環中使用該代碼,或者對於每個級別您遞交的遞歸保存r5的新副本,您可能有30個保存的副本,具體取決於您在該循環中的位置。並且當它展開時,將所有副本放回所需的位置。單個固定內存位置不起作用。這將直接轉換為C代碼作為示例:

void myfun ( void )
{
   int somedata;
}

在這樣的C程序中,變量some​​data存在於堆棧中,如果您遞歸調用myfun,則根據遞歸的深度,您將有多個副本的somedata值。此外,由於該變量僅在函數內部使用,並且不需要其他位置,那么您可能不想在程序的生命周期內為該變量刻錄一定量的系統內存,只需要在該函數中使用這些字節,並釋放該內存不在那個功能。這就是堆棧的用途。

在堆棧中找不到全局變量

回去…

說你想實現和調用這個函數,你會在調用myfun函數時有一些代碼/函數。 myfun函數希望使用r5和r6,當它正在操作的東西,但它不想垃圾的任何人稱它是使用r5和r6這樣的持續時間的 myfun()你想要保存在堆棧上的這些寄存器。同樣,如果您查看分支鏈接指令(b1)和鏈接寄存器lr(r14),則只有一個鏈接寄存器,如果從函數調用函數,則需要在每次調用時保存鏈接寄存器,否則您無法返回。

...
bl myfun
    <--- the return from my fun returns here
...
 
 
myfun:
stmdb sp!,{r5,r6,lr}
sub sp,#4 <--- make room for the somedata variable
...
some code here that uses r5 and r6
bl more_fun <-- this modifies lr, if we didnt save lr we wouldnt be able to return from myfun
   <---- more_fun() returns here
...
add sp,#4 <-- take back the stack memory we allocated for the somedata variable
ldmia sp!,{r5,r6,lr}
mov pc,lr <---- return to whomever called myfun.

所以希望你可以看到堆棧的使用和鏈接寄存器。其他處理器以不同的方式做同樣的事情。例如有些將把返回值放在堆棧上,當你執行返回函數時,它通過從棧中拉一個值來知道在哪里返回。編譯器C /C++等通常會有一個”calling convention”或應用程序接口(ABI和EABI是ARM定義的名稱)。如果每個函數遵循調用約定,則將參數傳遞給在正確的寄存器或堆棧中被調用的函數。並且每個函數遵循規則,關於什么寄存器不必保留其內容和什么寄存器來保存內容,那么你可以使用函數調用函數調用函數,並執行遞歸和各種事情,只要堆棧不會太深,以至於它運行到用於全局變量和堆的內存中,所以您可以調用函數並從整個日期返回。 myfun的上述實現與編譯器生成的內容非常相似。

ARM現在有很多核心和一些指令集,cortex-m系列的工作原理有所不同,只要沒有一堆模式和不同的堆棧指針。並且在拇指模式下執行拇指指令時,您可以使用推送和彈出指令,這些指令不會讓您自由使用任何類似stm的寄存器,它只使用r13(sp),而且您無法僅將所有寄存器保存在其特定子集中。流行的ARM組裝人員允許您使用

push {r5,r6}
...
pop {r5,r6}

ARM代碼以及拇指代碼。對於arm代碼,它編碼適當的stmdb和ldmia。 (在縮略圖模式下,您也不必選擇使用db的時間和位置,之前遞減,ia,后增加)。

不,您絕對不必使用相同的寄存器,您不必配對相同數量的寄存器。

push {r5,r6,r7}
...
pop {r2,r3}
...
pop {r1}

假設在這些指令之間沒有其他堆棧指針修改,如果你記得sp將被遞減12個字節的推送,我們說從0x1000到0x0FF4,r5將被寫入0xFF4,r6到0xFF8和r7到0xFFC堆棧指針將變為0x0FF4。第一個pop將取值為0x0FF4,並將其放在r2中,然后將值置於0x0FF8,並將其置於r3中,堆棧指針將獲取值0x0FFC。稍后最后一個pop,sp為0x0FFC,讀取的值為r1,然后堆棧指針的值為0x1000,在那里開始。

ARM ARM,ARM架構參考手冊(infocenter.arm.com,參考手冊,找到適用於ARMv5並下載的手冊,這是ARM ARM與ARM和Thumb指令的傳統ARM),包含ldm和stm ARM的偽代碼關於這些如何使用的完整圖片。同樣,整本書都是關於ARM和如何編程的。在程序員模型章節前面將介紹所有模式下的所有寄存器等。

如果您正在編程ARM處理器,您應該首先確定(芯片廠商應該告訴您,ARM不會使芯片使芯片廠商的芯片成為芯片廠商的核心)。然后去arm站,找到那個家族的ARM ARM,找到特定內核的TRM(技術參考手冊),包括修正版本(如果供應商提供的)(r2p0表示版本2.0(二點零,2p0)),甚至如果存在較新的轉速,請使用與設計中使用的供應商所使用的手冊。不是每個核心都支持每個指令或模式,TRM告訴您ARM ARM支持的模式和指令,總結了核心所處的整個處理器系列的功能。請注意,ARM7TDMI不是ARMv7,而是ARMv7 ARM9不是ARMv9。 ARMvNUMBER是家族名稱ARM7,ARM11沒有v是核心名稱。較新的內核具有像Cortex和mpcore這樣的名稱,而不是ARMNUMBER的東西,這減少了混亂。當然,他們不得不通過制造一個非常不同的系列的ARMv7-m(cortex-MNUMBER)和ARMv7-a(Cortex-ANUMBER)來增加混亂,一個用於重負載,台式機,筆記本電腦等,另一個是微控制器,時鍾並在咖啡壺和類似的東西上閃爍的燈光。谷歌beagleboard(Cortex-A)和stm32值行發現板(Cortex-M)得到感覺的差異。或者甚至使用多於千兆赫茲的多核的open-rd.org板,或者來自nvidia的更新的tegra 2,相同的交易超級定標器,多核,多吉赫茲。 cortex-m幾乎沒有制動100MHz的屏障,並且以千字節測量的內存,盡管如果你想要一個cortex-a的地方,它可能會運行一個電池幾個月。

對於很長的帖子很抱歉,希望它是有用的。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM