0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內(nèi)不再提示

零基礎學ARM:程序狀態(tài)寄存器訪問指令解析

電子設計 ? 來源:電子設計 ? 作者:電子設計 ? 2020-12-24 13:36 ? 次閱讀

一、程序狀態(tài)寄存器訪問指令

ARM微處理器支持程序狀態(tài)寄存器訪問指令,用于在程序狀態(tài)寄存器和通用寄存器之間傳送數(shù)據(jù)。

MRSMRS{條件} 通用寄存器,程序狀態(tài)寄存器(CPSR或SPSR)

MRS指令用于將程序狀態(tài)寄存器的內(nèi)容傳送到通用寄存器中。該指令一般用在以下幾種情況:

當需要改變程序狀態(tài)寄存器的內(nèi)容時,可用MRS將程序狀態(tài)寄存器的內(nèi)容讀入通用寄存器,修改后再寫回程序狀態(tài)寄存器。當在異常處理或進程切換時,需要保存程序狀態(tài)寄存器的值,可先用該指令讀出程序狀態(tài)寄存器的值,然后保存。如:MRS R0,CPSR ;傳送CPSR的內(nèi)容到R0
MRS R0,SPSR ;傳送SPSR的內(nèi)容到R0
MSRMSR{條件} 程序狀態(tài)寄存器(CPSR或SPSR)_<域>,操作數(shù)

MSR指令用于將操作數(shù)的內(nèi)容傳送到程序狀態(tài)寄存器的特定域中。其中,操作數(shù)可以為通用寄存器或立即數(shù)。<域>用于設置程序狀態(tài)寄存器中需要操作的位,32位的程序狀態(tài)寄存器可分為4個域:

位[31:24]為條件標志位域,用f表示;

位[23:16]為狀態(tài)位域,用s表示;

位[15:8]為擴展位域,用x表示;

位[7:0]為控制位域,用c表示;

該指令通常用于恢復或改變程序狀態(tài)寄存器的內(nèi)容,在使用時,一般要在MSR指令中指明將要操作的域。如:

MSR CPSR,R0 ;傳送R0的內(nèi)容到CPSR

MSR SPSR,R0 ;傳送R0的內(nèi)容到SPSR

MSR CPSR_c,R0 ;傳送R0的內(nèi)容到SPSR,但僅僅修改CPSR中的控制位域
應用舉例使能中斷

要是能中斷,必須將寄存器CPSR的bit[7]設置為0

要將寄存器CPSR的bit[7]設置為0,但是不能影響其他位,所以必須先用msr讀取出cpsr的值到通用寄存器Rn(n取值0~8),然后修改bit[7]設置為0,再將該寄存器的值設置到CPSR中。

代碼如下:

area reset,code
code32
entry
start
bl enale_irq
enale_irq
mrs r0,cpsr
bic r0,r0,#0x80
msr cpsr_c,r0
mov pc,lr

執(zhí)行結(jié)果:

第8行【其實第8行還沒有執(zhí)行】:

當前模式時SVC ,因為開機商店屬于reset異常,而該異常會自動進入svc模式CPSR的值是0X000000D39行

mrs r0,cpsr 將cpsr的內(nèi)容讀取到寄存器r0中R0的值為0X000000D310行

bic r0,r0,#0x80 將r0的第7個bit位置設置為0(從低往高數(shù),0開始計數(shù))寄存器R0的值變成0x0000005311行

msr cpsr_c,r0 將構(gòu)造好的值寫回CPSR,此時CPSR的I 位已經(jīng)為0從而實現(xiàn)了中斷使能禁止中斷同理,我們要關閉中斷,只需要將CPSR的I位設置為1即可。 area reset,code
code32
entry
start
bl diable_irq
diable_irq
mrs r0,cpsr
orr r0,r0,#0x80
msr cpsr_c,r0
mov pc,lr
end
設置各模式的棧地址要想初始化各個模式的棧地址,必須首先切換到對應的模式,然后再將棧地址設置到寄存器sp即可。

代碼:

area reset,code
code32
entry
start
bl stack_init
stack_init ; 棧指針初始化函數(shù)
; @undefine_stack
msr cpsr_c,#0xdb ; 切換到未定義異常
ldr sp,=0x34000000 ; 棧指針為內(nèi)存最高地址,棧為倒生的棧
; ??臻g的最后1M 0x34000000~0x33f00000
; @abort_stack
msr cpsr_c,#0xd7 ; 切換到終止異常模式
ldr sp,=0x33f00000 ; 棧空間為1M,0x33f00000~0x33e00000
; @irq_stack
msr cpsr_c,#0xd2 ; 切換到中斷模式
ldr sp,=0x33e00000 ; ??臻g為1M,0x33e00000~0x33d00000
; @ sys_stack
msr cpsr_c,#0xdf ; 切換到系統(tǒng)模式
ldr sp,=0x33d00000 ; ??臻g為1M,0x33d00000~0x33c00000
msr cpsr_c,#0xd3 ; 切換回管理模式
mov pc,lr
end

「結(jié)果分析:」我們只分析undef棧的初始化。

8行

模式切換前,當前模式時svc模式,CPSR的值是0x000000D3注意看下SVC和undef模式的SP值都是09行

msr cpsr_c ,# 0xdb 直接對CPSR進行賦值,將當前模式設置為undef模式Current模式看到的LR寄存器值變成了0,因為模式切換成了undef模式,該模式下有自己的LR、SP寄存器SVC模式的私有寄存器SP和LR沒有改變12行

12行l(wèi)dr sp,=0x34000000 將常數(shù)裝載到寄存器sp中,(=表示這是一條偽指令)注意觀察,SVC模式的sp沒有變化,undef模式的SP被設置為 0x34000000

其他模式的棧初始化以此類推。

二、尋址方式

處理器根據(jù)指令中給出的地址信息來尋找物理地址的方式。

在講解尋址方式之前,我們首先來看下LDR、STR指令。

1. 加載存儲指令

ARM微處理器支持加載/存儲指令用于在寄存器和存儲器之間傳送數(shù)據(jù),加載指令用于將存儲器中的數(shù)據(jù)傳送到寄存器,存儲指令則完成相反的操作。

我們之前講的尋址方式都是直接對立即數(shù)或者寄存器尋址,如果我們想訪問外部存儲器的某個內(nèi)存地址或者一些外設的控制器寄存器該如何操作呢?

那就需要進行寄存器間接尋址。如下圖所示,訪問外存需要通過AHB、APB總線,所以往往需要幾個指令周期才能實現(xiàn)1個數(shù)據(jù)的讀寫。

訪問外存LDR指令

LDR指令的格式為:

LDR{條件} 目的寄存器,<存儲器地址>
LDR指令用于從存儲器中將一個32位的字數(shù)據(jù)傳送到目的寄存器中。

1) 用于從存儲器中讀取32位的字數(shù)據(jù)到通用寄存器,然后對數(shù)據(jù)進行處理。2) 當程序計數(shù)器PC作為目的寄存器時,指令從存儲器中讀取的字數(shù)據(jù)被當作目的地址,從而可以實現(xiàn)程序流程的跳轉(zhuǎn)。如:

LDR R0,[R1] ;將存儲器地址為R1的字數(shù)據(jù)讀入寄存器R0。
LDR R0,[R1,R2] ;將存儲器地址為R1+R2的字數(shù)據(jù)讀入寄存器R0。
LDR R0,[R1,#8] ;將存儲器地址為R1+8的字數(shù)據(jù)讀入寄存器R0。
LDR R0,[R1,R2] ! ;將存儲器地址為R1+R2的字數(shù)據(jù)讀入寄存器R0,并將
;新地址R1+R2寫入R1。
LDR R0,[R1,#8] ! ;將存儲器地址為R1+8的字數(shù)據(jù)讀入寄存器R0,并將新
;地址R1+8寫入R1。
LDR R0,[R1],R2 ;將存儲器地址為R1的字數(shù)據(jù)讀入寄存器R0,并將新地
;址R1+R2寫入R1。
LDR R0,[R1,R2,LSL#2]! ;將存儲器地址為R1+R2×4的字數(shù)據(jù)讀入寄存器R0,
;并將新地址R1+R2×4寫入R1。
LDR R0,[R1],R2,LSL#2 ;將存儲器地址為R1的字數(shù)據(jù)讀入寄存器R0,并將新地
;址R1+R2×4寫入R1。
STR指令

STR指令的格式為:STR{條件} 源寄存器,<存儲器地址>STR指令用于從源寄存器中將一個32位的字數(shù)據(jù)傳送到存儲器中。該指令在程序設計中比較常用,且尋址方式靈活多樣,使用方式可參考指令LDR。如:

STR R0,[R1],#8 ;將R0中的字數(shù)據(jù)寫入以R1為地址的存儲器中,并將新地址R1+8寫入R1。
STR R0,[R1,#8] ;將R0中的字數(shù)據(jù)寫入以R1+8為地址的存儲器中。

LDR/STR指令都可以加B、H、SB、SH的后綴,分別表示加載/存儲字節(jié)、半字、帶符號的字節(jié)、帶符號的半字。如LDRB指令表示從存儲器加載一個字節(jié)進寄存器。當使用這些后綴時,要注意所使用的存儲器要支持訪問的數(shù)據(jù)寬度。

LDRB指令

LDRB指令的格式為:

LDR{條件}B 目的寄存器,<存儲器地址>

LDRB指令用于從存儲器中將一個8位的字節(jié)數(shù)據(jù)傳送到目的寄存器中,同時將寄存器的高24位清零。該指令通常用于從存儲器中讀取8位的字節(jié)數(shù)據(jù)到通用寄存器,然后對數(shù)據(jù)進行處理。

「指令示例:」

LDRB R0,[R1] ;將存儲器地址為R1的字節(jié)數(shù)據(jù)讀入寄存器R0,并將R0的高24位清零。
LDRB R0,[R1,#8];將存儲器地址為R1+8的字節(jié)數(shù)據(jù)讀入寄存器R0,并將R0的高24位清零。
LDRH指令

LDRH指令的格式為:

LDR{條件}H 目的寄存器,<存儲器地址>

LDRH指令用于從存儲器中將一個16位的半字數(shù)據(jù)傳送到目的寄存器中,同時將寄存器的高16位清零。該指令通常用于從存儲器中讀取16位的半字數(shù)據(jù)到通用寄存器,然后對數(shù)據(jù)進行處理。

「指令示例:」

LDRH R0,[R1] ;將存儲器地址為R1的半字數(shù)據(jù)讀入寄存器R0,并將R0的高16位清零。
LDRH R0,[R1,R2];將存儲器地址為R1+R2的半字數(shù)據(jù)讀入寄存器R0,并將R0的高16位清零。
舉例1) STR r0,[r1,#12]

如上圖所示:

寄存器r0中的值是0x5,r1中的值是0x200將r1的值加上#12,得到地址0x20c將r0寄存器里的值發(fā)送給該地址對應的內(nèi)存,即向地址0x20c中賦值0x52) STR r0,[r1],#12

如上圖所示:

寄存器r0的值是0x5,r1中的值是0x200將r0寄存器里的值發(fā)送給該r1中的值對應的內(nèi)存,即向地址0x200中賦值0x5將r1的值加上#12并賦值給r1,r1的值就變成了0x20c

「擴展:」比如有以下c代碼

int *ptr;
x = *ptr++;

經(jīng)過編譯器編譯,可以將這兩行代碼編譯為一條單指令:

LDR r0, [r1], #4
2. 立即尋址

立即尋址也叫立即數(shù)尋址,這是一種特殊的尋址方式,操作數(shù)本身就在指令中給出,只要取出指令也就取到了操作數(shù)。這個操作數(shù)被稱為立即數(shù),對應的尋址方式也就叫做立即尋址。例如以下指令:

Add r0,r0,#1 ;R0=R0+1

在以上兩條指令中,第二個源操作數(shù)即為立即數(shù),要求以“#”為前綴,對于以十六進制表示的立即數(shù),還要求在“#”后加上“0x”或“&”。

3. 寄存器尋址

利用寄存器中的數(shù)值作為操作數(shù),這種尋址方式是各類微處理器經(jīng)常采用的一種方式,也是一種執(zhí)行效率較高的尋址方式。

Add R0 , R1,R2 ;R0=R1+R2

該指令的執(zhí)行效果是將寄存器R1和R2的內(nèi)容相加,其結(jié)果存放在寄存器R0中。

4. 寄存器間接尋址

以寄存器中的值作為操作數(shù)的地址,而操作數(shù)本身存放在存儲器中。例如以下指令:

Add R0,R1,[R2] ; R0=R1+[R2]
LDR R0,[R1] ; R0=[R1]

在第一條指令中,以寄存器R2的值作為操作數(shù)的地址,在存儲器中取得一個操作數(shù)后與R1相加,結(jié)果存入寄存器R0中。第二條指令將以R1的值為地址的存儲器中的數(shù)據(jù)傳送到R0中。

5. 基址變址尋址

將寄存器(該寄存器一般稱作基址寄存器)的內(nèi)容與指令中給出的地址偏移量相加,從而得到一個操作數(shù)的有效地址:

LDR R0,[R1,#4] ;R0=[R1+4]
LDR R0,[R1,#4] ! ;R0=[R1+4]、R1=R1+4
LDR R0,[R1],#4 ;R0=[R1] 、R1=R1+4
LDR R0,[R1,R2] ;R0=[R1+R2]
6. 多寄存器尋址

采用多寄存器尋址方式,一條指令可以完成多個寄存器值的傳送。這尋址方式可以用一條指令完成傳送最多16個通用寄存器的值。以下指令:

LDMIA R0,{R1,R2,R3,R4} ;R1=[R0] R2=[R0+4] R3=[R0+8] R4=[R0+12]

該指令的后綴IA表示在每次執(zhí)行完加載/存儲操作后,R0按字長度增加,因此,指令可將連續(xù)存儲單元的值傳送到R1~R4。

7. 相對尋址

與基址變址尋址方式相類似,相對尋址以程序計數(shù)器PC的當前值為基地址,指令中的地址標號作為偏移量,將兩者相加之后得到操作數(shù)的有效地址。以下程序段完成子程序的調(diào)用和返回,跳轉(zhuǎn)指令BL采用了相對尋址方式:

BL NEXT ;跳轉(zhuǎn)到子程序NEXT處執(zhí)行
……
NEXT
……
MOV PC,LR ;從子程序返回
8. 堆棧尋址、批量加載/存儲指令

堆棧是一種數(shù)據(jù)結(jié)構(gòu),按先進后出(First In Last Out,F(xiàn)ILO)的方式工作,使用一個稱作堆棧指針的專用寄存器指示當前的操作位置,堆棧指針總是指向棧頂。

批量數(shù)據(jù)加載/存儲指令可以一次在一片連續(xù)的存儲器單元和多個寄存器之間傳送數(shù)據(jù)。常用的加載存儲指令如下:

LDM批量數(shù)據(jù)加載指令
STM批量數(shù)據(jù)存儲指令

LDM(或STM)指令的格式為:

LDM(或STM){條件}{類型} 基址寄存器{?。拇嫫髁斜恚模?/p>

LDM(或STM)指令用于從由基址寄存器所指示的一片連續(xù)存儲器到寄存器列表所指示的多個寄存器之間傳送數(shù)據(jù),該指令的常見用途是將多個寄存器的內(nèi)容入?;虺鰲?。其中,{類型}為以下幾種情況:

IA 每次傳送后地址加1;
IB 每次傳送前地址加1;
DA 每次傳送后地址減1;
DB 每次傳送前地址減1;
FD 滿遞減堆棧; 向低地址方向生長
ED 空遞減堆棧;
FA 滿遞增堆棧; 向高地址方向生長
EA 空遞增堆棧;
【滿堆?!浚憾褩V羔楽P指向最后壓入堆棧的有效數(shù)據(jù)項
【空堆?!浚憾褩V羔樦赶蛳乱粋€要放入數(shù)據(jù)的空位置

「【特別注意】」

{?。秊榭蛇x后綴,若選用該后綴,則當數(shù)據(jù)傳送完畢之后,將最后的地址寫入基址寄存器,否則基址寄存器的內(nèi)容不改變。
基址寄存器不允許為R15,寄存器列表可以為R0~R15的任意組合。
{∧}為可選后綴,當指令為LDM且寄存器列表中包含R15,選用該后綴時表示:除了正常的數(shù)據(jù)傳送之外,還將SPSR復制到CPSR。同時,該后綴還表示傳入或傳出的是用戶模式下的寄存器,而不是當前模式下的寄存器。

如:

STMFD R13!,{R0,R4-R12,LR} ;將寄存器列表中的寄存器(R0,R4到R12,LR)存入堆棧,向低地址方向生長。
LDMFD R13!,{R0,R4-R12,PC} ;將堆棧內(nèi)容恢復到寄存器(R0,R4到R12,LR)。

【注意】要壓棧的寄存器順序可以亂序,但是實際壓棧和出棧仍然會將寄存器順序調(diào)整后再操作。

9. 舉例例1 數(shù)組求和

編寫一個ARM匯編程序,累加一個“數(shù)組”的所有元素,碰上0時停止。結(jié)果放入 r4。

「實在步驟如下:」1) 在源文件末尾按如下方式聲明“數(shù)組”:

array:
.word 0x11
.word 0x22
.word 0

2) 用r0指向“數(shù)組”的入口

LDR r0,=array

3) 使用LDR r1,[r0],#4從“數(shù)組”中裝載數(shù)據(jù)4) 累加并放入r45) 循環(huán),直到r1為06) 停止,進入死循環(huán)

代碼:

area first, code, readonly
code32
entry
start
ldr r0,=array
; adr r0,array ;ADR為小范圍的地址讀取偽指令
loop
ldr r1,[r0],#4
cmp r1,#0
addne r4,r4,r1
bne loop
stop
b stop
DCD 偽操作 數(shù)據(jù)緩沖池技術
; dcd 機器碼
array
dcd 0x11
dcd 0x22
dcd 0

我們看一下最終執(zhí)行代碼在內(nèi)存中的機器碼對比圖

由上圖可知:

ldr r0,=array,編譯器會計算出array標號的地址0x0018,注意該值是偏移當前指令所在內(nèi)存位置的偏移量,所以該指令最終被翻譯成ldr r0,[pc,#0x001c]

為什么是0x001c而不是0x0018呢?剛上電時此時pc的值是-4,因為下一條要執(zhí)行的指令的0x0000這個地址的指令

數(shù)組元素的3個值依次存放在0x0018、0x001c、0x001c這3個地址中l(wèi)dr r1,[r0],#4每次取出r0指向的內(nèi)存的值并寫入到r1,同時將r0值自加4bne loop 的loop被編譯器計算為地址0x0004例2 內(nèi)存數(shù)據(jù)讀寫

將某個整型值寫入到內(nèi)存0x40000000 中然后再將其讀出。

代碼:

area first, code, readonly
code32
entry
start
mov r0, #0x10000003
mov r1, #0x40000000 ; SAMSUNG 2410 , 2410 = > sram 0x40000000 0x3fffff00
str r0, [r1] ;內(nèi)存單元的地址r1寄存器的內(nèi)容指示
ldr r2,[r1]
stop
b stop
end

做這個實驗之前需要做以下設置。IRAM地址為0x40000000,size設置0x1000,就是我們測試用的IRAM地址范圍是0x40000000-0x40001000

注意,該內(nèi)存地址不是隨意設置的,查看S3C2440A用戶手冊【因為我們模擬的是S3C2440A這個soc】,從下圖可以清楚看到ram地址空間。

「數(shù)據(jù)寫入內(nèi)存:」

「從內(nèi)存讀取數(shù)據(jù):」

例3 數(shù)據(jù)壓棧退棧

先將棧地址設置為將要壓棧的數(shù)據(jù)存入寄存器r1-r5中,然后

area first, code, readonly
code32
entry
Start
;mov r0, #0x40000000
ldr sp, =0x40001000 ;注意地址
mov r1, #0x11
mov r2, #0x22
mov r3, #0x33
mov r5, #0x55
; 壓棧
stmfd sp!, {r1-r3, r5}
;stmia r0!, {r1-r3, r5} ; 加感嘆號是自動修改基地址
mov r1, #0
mov r2, #0
mov r3, #0
mov r5, #0
ldmfd sp!, {r1-r3, r5}
;ldmdb r0!, {r2,r1,r3, r5} ; 寄存列表書寫順序無所謂, 低地址內(nèi)容對應低編號寄存器
stop
b stop
end

在壓棧前,內(nèi)存0x40001000地址全為0。sp的值為0x40001000。

執(zhí)行命令ldmfd sp!, {r1-r3, r5}壓棧后,因為我們是滿遞減堆棧,并且SP后又!,所以內(nèi)存0x40000ff0地址開始的數(shù)據(jù)是0x11、0x22、0x33、0x44,sp的值修改為為0x40000ff0。以下是壓棧后內(nèi)存的數(shù)據(jù):

例4 函數(shù)嵌套調(diào)用

當有多級函數(shù)嵌套,函數(shù)返回值我們不可能都存儲在通用寄存器中,必須利用ldm將程序跳轉(zhuǎn)前的寄存器值以及函數(shù)的返回地址壓棧。

area first, code, readonly
code32
entry
start
ldr sp, =0x40002000
mov r1, #0x11
mov r2, #0x22
mov r3, #0x33
mov r5, #0x55
bl child_func1 ; 【先寫跳轉(zhuǎn)到 child_func1,再寫跳轉(zhuǎn)到child_func】
add r0, r1,r2
stop
b stop
; 非葉子函數(shù)
child_func
stmfd sp!, {r1-r3,r5,lr} ;;;在子函數(shù)里首先將所有寄存器值壓棧保存,
;;防止在子函數(shù)里篡改原本在主函數(shù)里運算需要的值,
;;通常需要把r0-r12全都保存,為了安全和程序通用性應該這么做
mov r1, #10 ;;在這里子函數(shù)想怎么做自己的事情就可以做自己的事情
bl child_func1
ldmfd sp!, {r1-r3,r5,lr};;;;; 放在主函數(shù)bl之后的第一句行嗎?
mov pc, lr
child_func1
stmfd sp!, {r1-r3,r5};;;不論嵌套多少層子函數(shù),都是先壓棧,
mov r1, #11
ldmfd sp!, {r1-r3,r5};;對應的,在返回到自己的父函數(shù)之前將自己出棧
mov pc, lr
end

讀者可以自己debug,查看內(nèi)存的內(nèi)容變化

四、ldrex 和 strex1. LDREX

LDREX可從內(nèi)存加載數(shù)據(jù)。

如果物理地址有共享TLB屬性,則「LDREX會將該物理地址標記為由當前處理器獨占訪問」,并且會「清除該處理器對其他任何物理地址的任何獨占訪問標記」。

否則,會標記:執(zhí)行處理器已經(jīng)標記了一個物理地址,但訪問尚未完畢。

2. STREX

STREX可在一定條件下向內(nèi)存存儲數(shù)據(jù)。

條件具體如下:

如果物理地址沒有共享TLB屬性,且執(zhí)行處理器有一個已標記但尚未訪問完畢的物理地址,那么將會進行存儲,清除該標記,并在Rd中返回值0。

如果物理地址沒有共享TLB屬性,且執(zhí)行處理器也沒有已標記但尚未訪問完畢的物理地址,那么將不會進行存儲,而會在Rd中返回值1。

如果物理地址有共享TLB屬性,且已被標記為由執(zhí)行處理器獨占訪問,那么將進行存儲,清除該標記,并在Rd中返回值0。

如果物理地址有共享TLB屬性,但沒有標記為由執(zhí)行處理器獨占訪問,那么不會進行存儲,且會在Rd中返回值1。

3. 語法LDREX{cond} Rt, [Rn {, #offset}]
STREX{cond} Rd, Rt, [Rn {, #offset}]
LDREXB{cond} Rt, [Rn] 字節(jié)加載
STREXB{cond} Rd, Rt, [Rn] 字節(jié)存儲
LDREXH{cond} Rt, [Rn] 半字加載
STREXH{cond} Rd, Rt, [Rn] 半字存儲
LDREXD{cond} Rt, Rt2, [Rn] 雙字加載
STREXD{cond} Rd, Rt, Rt2, [Rn] 雙字存儲

其中:

cond
是一個可選的條件代碼(請參閱條件執(zhí)行)。
Rd
是存放返回狀態(tài)的目標寄存器。
Rt
是要加載或存儲的寄存器。
Rt2
為進行雙字加載或存儲時要用到的第二個寄存器。
Rn
是內(nèi)存地址所基于的寄存器。
offset
為應用于 Rn 中的值的可選偏移量。offset 只可用于 Thumb-2 指令中。如果省略 offset,則認為偏移量為 0。
實現(xiàn)原子操作

利用 LDREX 和 STREX 可在多個處理器和共享內(nèi)存系統(tǒng)之前實現(xiàn)進程間通信

原理

將對一個內(nèi)存地址的原子操作拆分成兩個步驟,一起完成對內(nèi)存的原子操作??梢岳斫鉃閳?zhí)行LDREX Rd [Rs]指令會標記對[Rs]這個內(nèi)存地址的訪問是獨占狀態(tài)(exclusive state)。而執(zhí)行STREX R0 Rd [Rs]指令會讓先前處于獨占狀態(tài)的內(nèi)存地址[Rs]轉(zhuǎn)變?yōu)檎顟B(tài),并且設置R0為0。若執(zhí)行STREX R0 Rd [Rs]指令時,內(nèi)存地址[Rs]是正常狀態(tài),則指令的存儲動作會失敗,并且R0置為1。

linux 中原子操作對應的數(shù)據(jù)結(jié)構(gòu)為 atomic_t,定義如下:

typedef struct {
int counter;
} atomic_t;

本質(zhì)上就是一個整型變量。

比如我們要對原子變量實行加操作,使用獨占指令完成累加操作。

#if __LINUX_ARM_ARCH__ >= 6 ----(1)
static inline void atomic_add(int i, atomic_t *v)

unsigned long tmp;
int result;
// 使用獨占指令讀取,然后執(zhí)行加操作,獨占寫失敗時就重新執(zhí)行
prefetchw(&v->counter); ----(2)
__asm__ __volatile__(
"@ atomic_add" ----(3)
"1: ldrex %0, [%3]" ----(4)
" add %0, %0, %4" ----(5)
" strex %1, %0, [%3]" ----(6)
" teq %1, #0" ----(7)
" bne 1b"
: "=&r" (result), "=&r" (tmp), "+Qo" (v->counter) ---對應%0,%1,%2
: "r" (&v->counter), "Ir" (i) ----對應%3,%4
: "cc");

#else
#ifdef CONFIG_SMP

原理:

(1)ARMv6之前的CPU并不支持SMP,之后的ARM架構(gòu)都是支持SMP的(例如我們熟悉的ARMv7-A)。因此,對于ARM處理,其原子操作分成了兩個陣營,一個是支持SMP的ARMv6之后的CPU,另外一個就是ARMv6之前的,只有單核架構(gòu)的CPU。對于UP,原子操作就是通過關閉CPU中斷來完成的。

(2)這里的代碼和preloading cache相關。在strex指令之前將要操作的memory內(nèi)容加載到cache中可以顯著提高性能。

(3)其中%3就是input operand list中的”r” (&v->counter),r是限制符(constraint),用來告訴編譯器gcc,你看著辦吧,你幫我選擇一個通用寄存器保存該操作數(shù)吧。

%0 對應output openrand list中的”=&r” (result),= 表示該操作數(shù)是write only的,& 表示該操作數(shù)是一個earlyclobber operand,

編譯器在處理嵌入式匯編的時候,傾向使用盡可能少的寄存器,如果output operand沒有&修飾的話,匯編指令中的input和output操作數(shù)會使用同樣一個寄存器。因此,&確保了%3和%0使用不同的寄存器。

(5)完成步驟(4)后,%0這個output操作數(shù)已經(jīng)被賦值為atomic_t變量的old value,這里的操作是要給old value加上i。

這里%4對應”Ir” (i),這里“I”這個限制符對應ARM平臺,表示這是一個有特定限制的立即數(shù),該數(shù)必須是0~255之間的一個整數(shù)通過rotation的操作得到的一個32bit的立即數(shù)。

這是和ARM的data-processing instructions如何解析立即數(shù)有關的。每個指令32個bit,其中12個bit被用來表示立即數(shù),其中8個bit是真正的數(shù)據(jù),4個bit用來表示如何rotation。

(6)這一步將修改后的new value保存在atomic_t變量中。是否能夠正確的操作的狀態(tài)標記保存在%1操作數(shù)中,也就是”=&r” (tmp)。

(7)檢查memory update的操作是否正確完成,如果OK,皆大歡喜,如果發(fā)生了問題(有其他的內(nèi)核路徑插入),那么需要跳轉(zhuǎn)到lable 1那里,從新進行一次read-modify-write的操作。

從0學arm系列合集

1. 到底什么是Cortex、ARMv8、arm架構(gòu)、ARM指令集、soc?一文幫你梳理基礎概念【科普

2. 嵌入式工程師到底要不要學習ARM匯編指令?

3. 1. 從0開始學ARM-安裝Keil MDK uVision集成開發(fā)環(huán)境

4. 2. 從0開始學ARM-CPU原理,基于ARM的SOC講解

5. 3. 從0開始學ARM-ARM模式、寄存器、流水線

6. 4. 從0開始學ARM-ARM匯編指令其實很簡單

審核編輯:符乾江
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • ARM
    ARM
    +關注

    關注

    134

    文章

    9027

    瀏覽量

    366476
  • 可編程邏輯
    +關注

    關注

    7

    文章

    514

    瀏覽量

    44054
  • MRS
    MRS
    +關注

    關注

    0

    文章

    7

    瀏覽量

    7615
  • MSR
    MSR
    +關注

    關注

    0

    文章

    18

    瀏覽量

    7995
收藏 人收藏

    評論

    相關推薦

    接口的控制與狀態(tài)寄存器什么作用

    接口的控制與狀態(tài)寄存器(Control and Status Registers,簡稱CSR)是計算機系統(tǒng)中用于控制和監(jiān)控硬件設備操作的寄存器。它們是硬件設備與其驅(qū)動程序之間的橋梁,允
    的頭像 發(fā)表于 10-17 10:42 ?233次閱讀

    ARM處理寄存器組織及功能

    ARM處理寄存器組織是其核心架構(gòu)的重要組成部分,對于理解ARM處理的運行機制和性能特點具有重要意義。以下是對
    的頭像 發(fā)表于 09-10 11:11 ?892次閱讀

    ARM寄存器的分類及功能

    ARM寄存器ARM處理內(nèi)部的重要組成部分,它們在處理的運算、控制以及數(shù)據(jù)存儲等方面發(fā)揮著至關重要的作用。下面,我們將從
    的頭像 發(fā)表于 09-05 14:18 ?878次閱讀

    寄存器的類型和作用

    在計算機科學中,寄存器(Register)是一種高速存儲單元,它位于CPU內(nèi)部,與CPU的運算單元和邏輯控制單元緊密相連。寄存器的主要作用是暫時存儲指令、操作數(shù)和地址等臨時數(shù)據(jù),以便CPU快速
    的頭像 發(fā)表于 09-05 14:11 ?849次閱讀

    寄存器是什么意思?寄存器是如何構(gòu)成的?

    在計算機科學中,寄存器(Register)是一個高速存儲單元,它位于中央處理(CPU)內(nèi)部,用于存儲計算機程序執(zhí)行過程中所需要的數(shù)據(jù)、指令地址或
    的頭像 發(fā)表于 08-02 18:23 ?2925次閱讀
    <b class='flag-5'>寄存器</b>是什么意思?<b class='flag-5'>寄存器</b>是如何構(gòu)成的?

    寄存器尋址和直接尋址的區(qū)別

    寄存器尋址和直接尋址是計算機指令系統(tǒng)中的兩種基本尋址方式。它們在指令的執(zhí)行過程中起著至關重要的作用,決定了指令操作數(shù)的來源和目標。下面我們將介紹這兩種尋址方式的特點、區(qū)別以及在實際應用
    的頭像 發(fā)表于 07-12 10:42 ?1169次閱讀

    寄存器尋址的實現(xiàn)方式

    在計算機體系結(jié)構(gòu)中,寄存器尋址是一種常見的尋址方式,它允許程序直接訪問CPU內(nèi)部的寄存器寄存器尋址可以提高
    的頭像 發(fā)表于 07-12 10:36 ?511次閱讀

    寄存器分為基本寄存器和什么兩種

    ,它們用于存儲指令、數(shù)據(jù)和地址等信息?;?b class='flag-5'>寄存器的容量通常較小,但訪問速度非???,因為它們與CPU的執(zhí)行單元緊密相連。 基本寄存器的分類 基本寄存器
    的頭像 發(fā)表于 07-12 10:31 ?1012次閱讀

    PLC移位寄存器指令的工作過程

    在工業(yè)自動化領域,可編程序控制器(PLC)的應用日益廣泛。作為工業(yè)控制的核心設備,PLC不僅具備強大的數(shù)據(jù)處理能力,還擁有豐富的指令系統(tǒng),以滿足各種復雜的控制需求。其中,移位寄存器指令
    的頭像 發(fā)表于 06-15 17:37 ?1355次閱讀

    什么是寄存器?有哪些功能和應用?

    在計算機科學中,寄存器(Register)是一個高速存儲單元,用于存儲計算機程序執(zhí)行過程中所需要的數(shù)據(jù)、指令地址或狀態(tài)信息。它們是計算機體系結(jié)構(gòu)中至關重要的組成部分,對計算機的運算速度
    的頭像 發(fā)表于 05-30 17:14 ?1.1w次閱讀

    干貨滿滿:ARM的內(nèi)核寄存器講解

    內(nèi)核寄存器與外設寄存器: 內(nèi)核寄存器與外設寄存器是完全不同的概念。內(nèi)核寄存器是指 CPU 內(nèi)部的寄存器
    發(fā)表于 04-17 11:47 ?2731次閱讀
    干貨滿滿:<b class='flag-5'>ARM</b>的內(nèi)核<b class='flag-5'>寄存器</b>講解

    loop指令執(zhí)行時,隱含的寄存器

    寄存器的值是否為。如果不為,則跳轉(zhuǎn)到指定的循環(huán)體代碼塊執(zhí)行,并在執(zhí)行完循環(huán)體后再次執(zhí)行l(wèi)oop指令。如果CX寄存器的值為
    的頭像 發(fā)表于 02-14 16:15 ?819次閱讀

    arm三個寄存器在gdb調(diào)試時的作用

    arm三個寄存器在gdb調(diào)試時作用? ARM是一種廣泛使用的微處理架構(gòu),它廣泛應用于移動設備、嵌入式系統(tǒng)和其他高性能計算設備。當我們在使用gdb(GNU調(diào)試
    的頭像 發(fā)表于 01-31 10:44 ?859次閱讀

    labview移位寄存器如何清

    LabVIEW中的移位寄存器(Shift Register)是一種用于存儲和移動數(shù)據(jù)的功能性塊。它類似于傳統(tǒng)計算機中的寄存器,可以用于在循環(huán)中保留變量的狀態(tài)。但與傳統(tǒng)寄存器不同的是,移
    的頭像 發(fā)表于 01-05 13:49 ?2745次閱讀

    寄存器開發(fā),HAL開發(fā)和LL開發(fā)的區(qū)別和參考代碼

    1,寄存器開發(fā)【難度值:難】寄存器開發(fā)是一種直接訪問微控制寄存器的編程方法,它提供了對硬件的極大靈活性和直接控制。在
    的頭像 發(fā)表于 11-17 08:00 ?1248次閱讀
    <b class='flag-5'>寄存器</b>開發(fā),HAL開發(fā)和LL開發(fā)的區(qū)別和參考代碼