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

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

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

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

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

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

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

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

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

當(dāng)需要改變程序狀態(tài)寄存器的內(nèi)容時,可用MRS將程序狀態(tài)寄存器的內(nèi)容讀入通用寄存器,修改后再寫回程序狀態(tài)寄存器。當(dāng)在異常處理或進(jìn)程切換時,需要保存程序狀態(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ù)。<域>用于設(shè)置程序狀態(tài)寄存器中需要操作的位,32位的程序狀態(tài)寄存器可分為4個域:

位[31:24]為條件標(biāo)志位域,用f表示;

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

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

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

該指令通常用于恢復(fù)或改變程序狀態(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中的控制位域
應(yīng)用舉例使能中斷

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

要將寄存器CPSR的bit[7]設(shè)置為0,但是不能影響其他位,所以必須先用msr讀取出cpsr的值到通用寄存器Rn(n取值0~8),然后修改bit[7]設(shè)置為0,再將該寄存器的值設(shè)置到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í)行】:

當(dāng)前模式時SVC ,因為開機(jī)商店屬于reset異常,而該異常會自動進(jìn)入svc模式CPSR的值是0X000000D39行

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

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

msr cpsr_c,r0 將構(gòu)造好的值寫回CPSR,此時CPSR的I 位已經(jīng)為0從而實現(xiàn)了中斷使能禁止中斷同理,我們要關(guān)閉中斷,只需要將CPSR的I位設(shè)置為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
設(shè)置各模式的棧地址要想初始化各個模式的棧地址,必須首先切換到對應(yīng)的模式,然后再將棧地址設(shè)置到寄存器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 ; ??臻g為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行

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

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

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

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

二、尋址方式

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

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

1. 加載存儲指令

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

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

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

訪問外存LDR指令

LDR指令的格式為:

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

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

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

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

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

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

LDRB指令

LDRB指令的格式為:

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

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

「指令示例:」

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位的半字?jǐn)?shù)據(jù)傳送到目的寄存器中,同時將寄存器的高16位清零。該指令通常用于從存儲器中讀取16位的半字?jǐn)?shù)據(jù)到通用寄存器,然后對數(shù)據(jù)進(jìn)行處理。

「指令示例:」

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

如上圖所示:

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

如上圖所示:

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

「擴(kuò)展:」比如有以下c代碼

int *ptr;
x = *ptr++;

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

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

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

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

在以上兩條指令中,第二個源操作數(shù)即為立即數(shù),要求以“?!睘榍熬Y,對于以十六進(jìn)制表示的立即數(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的當(dāng)前值為基地址,指令中的地址標(biāo)號作為偏移量,將兩者相加之后得到操作數(shù)的有效地址。以下程序段完成子程序的調(diào)用和返回,跳轉(zhuǎn)指令BL采用了相對尋址方式:

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

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

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

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

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

LDM(或STM){條件}{類型} 基址寄存器{!},寄存器列表{∧}

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

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

「【特別注意】」

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

如:

STMFD R13!,{R0,R4-R12,LR} ;將寄存器列表中的寄存器(R0,R4到R12,LR)存入堆棧,向低地址方向生長。
LDMFD R13!,{R0,R4-R12,PC} ;將堆棧內(nèi)容恢復(fù)到寄存器(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) 停止,進(jìn)入死循環(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ù)緩沖池技術(shù)
; dcd 機(jī)器碼
array
dcd 0x11
dcd 0x22
dcd 0

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

由上圖可知:

ldr r0,=array,編譯器會計算出array標(biāo)號的地址0x0018,注意該值是偏移當(dāng)前指令所在內(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

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

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

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

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

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

先將棧地址設(shè)置為將要壓棧的數(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)容對應(yīng)低編號寄存器
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)用

當(dāng)有多級函數(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ù)里運(yùn)算需要的值,
;;通常需要把r0-r12全都保存,為了安全和程序通用性應(yīng)該這么做
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};;對應(yīng)的,在返回到自己的父函數(shù)之前將自己出棧
mov pc, lr
end

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

四、ldrex 和 strex1. LDREX

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

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

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

2. STREX

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

條件具體如下:

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

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

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

如果物理地址有共享TLB屬性,但沒有標(biāo)記為由執(zhí)行處理器獨(dú)占訪問,那么不會進(jìn)行存儲,且會在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)的目標(biāo)寄存器。
Rt
是要加載或存儲的寄存器。
Rt2
為進(jìn)行雙字加載或存儲時要用到的第二個寄存器。
Rn
是內(nèi)存地址所基于的寄存器。
offset
為應(yīng)用于 Rn 中的值的可選偏移量。offset 只可用于 Thumb-2 指令中。如果省略 offset,則認(rèn)為偏移量為 0。
實現(xiàn)原子操作

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

原理

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

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

typedef struct {
int counter;
} atomic_t;

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

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

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

unsigned long tmp;
int result;
// 使用獨(dú)占指令讀取,然后執(zhí)行加操作,獨(dú)占寫失敗時就重新執(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) ---對應(yīng)%0,%1,%2
: "r" (&v->counter), "Ir" (i) ----對應(yīng)%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,原子操作就是通過關(guān)閉CPU中斷來完成的。

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

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

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

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

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

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

從0學(xué)arm系列合集

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

2. 嵌入式工程師到底要不要學(xué)習(xí)ARM匯編指令?

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

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

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

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

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

    關(guān)注

    134

    文章

    9097

    瀏覽量

    367602
  • 可編程邏輯
    +關(guān)注

    關(guān)注

    7

    文章

    515

    瀏覽量

    44087
  • MRS
    MRS
    +關(guān)注

    關(guān)注

    0

    文章

    7

    瀏覽量

    7628
  • MSR
    MSR
    +關(guān)注

    關(guān)注

    0

    文章

    18

    瀏覽量

    8003
收藏 人收藏

    評論

    相關(guān)推薦

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

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

    ARM處理寄存器組織及功能

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

    ARM寄存器的分類及功能

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

    通用寄存器是什么意思

    在計算機(jī)體系結(jié)構(gòu)中,通用寄存器是中央處理(CPU)內(nèi)部最為核心和基礎(chǔ)的組成部分之一。它們被設(shè)計為能夠存儲和傳輸各種類型的數(shù)據(jù)和指令,是CPU進(jìn)行數(shù)據(jù)處理和運(yùn)算的關(guān)鍵工具。通用寄存器
    的頭像 發(fā)表于 09-05 14:13 ?959次閱讀

    寄存器的類型和作用

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

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

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

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

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

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

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

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

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

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

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

    什么是寄存器?有哪些功能和應(yīng)用?

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

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

    內(nèi)核寄存器與外設(shè)寄存器: 內(nèi)核寄存器與外設(shè)寄存器是完全不同的概念。內(nèi)核寄存器是指 CPU 內(nèi)部的寄存器
    發(fā)表于 04-17 11:47 ?3699次閱讀
    干貨滿滿:<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 ?957次閱讀

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

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

    labview移位寄存器如何清

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