【1】ARM相關理論基礎介紹
1、ARM相關的概念
指令:就是一條匯編代碼,可以完成一個特定的功能
指令就是一堆CMOS組成的可以完成特定功能的電路圖
CMOS:柵極 源極 漏極 開關特性
NMOS:柵極為高電平,源極和漏極導通
柵極為低電平,源極和漏極截止
PMOS:柵極為低電平,源極和漏極導通
柵極為高電平,源極和漏極截止

內核(ARM內核) = 運算器 + 控制器 + 存儲器(寄存器)
運算器:匯編指令
控制器:控制程序運行
存儲器:數據
ARM公司將ARM內核授權給芯片廠家,使用ARM的內核生產對應的芯片
ARM指令集的架構(ARM-V1 到 ARM-V8)
1.此架構支持的是32位ARM指令集,還有64位的ARM指令集
ARM-V1 ~ ARM-V7架構屬於32位ARM指令集
ARM-V8機構屬於64位ARM指令集
2.架構的版本越高,支持的指令個數越多
指令實現的功能越復雜
總結:指令集->架構->內核->SOC
2、ARM的發展史
1. 1978年,CPU公司
Cambridge processing Unit
2. 1979年 Acorn
3. 1985年, 32位,8MHz,
使用的精簡指令集RISC
芯片的名字ARM
4.1990年,轉折點
iphone 150萬英鎊 VLSI: 25萬英鎊
ARM公司12工程師+技術專利:150萬英鎊
ARM公司-》 Advanced RISC Machine
ARM公司不生產芯片,做技術的授權,
提供解決方案。芯片廠家拿到技術授權之后,
根據需求設計生產自己的SOC。
5. 2016年,日本軟銀收購
6. 2020年,英偉達(未收購成功)
ARM指令集
RISC-V指令:完全開源
3、精簡指令集和復雜指令集的區別
精簡指令集(RISC)ARM架構
1.精簡指令集從復雜指令集中提取一些使用頻率較高,或者相對簡單的指令組合成精簡指令集
2.精簡指令集的指令周期和指令寬度都是固定的
3.指令的周期:執行一條指令所需要的時間
4.指令的寬度:一條指令編譯器成機器碼之后,占用內存空間的大小
復雜指令集(CISC)X86架構
1.復雜指令集更加注重指令的功能性和指令的運算能力
2.復雜指令集的指令寬度和指令的周期不固定
4、ARM產品的分布
cortex-A:高端處理器,可以運行linux系統(手機、平板)
cortex-R:主要針對的實時處理(汽車電子,攝像機,照相機)
cortex-M:低端電子產品,運行邏輯程序(物聯網設備開發)
stm32/stm8(意法半導體公司)
可以運行實時的操作系統
arm內核命令的規范:
早期:ARM7 ARM9 ARM10 ARM11
從ARM11之后ARM內核的命名調整為Cortex-A
5、ARM數據的約定
ARM-V7約定:
char :8bits
half word:16bits
word:32bits
double word :64bits(cortex-a系列才支持)
ARM-V8約定:
char :8bits
half word:16bits
word:32bits
double word :64bits(cortex-a系列才支持)
quad word:128bits(ARM-V8架構才支持)
6、ARM處理器的工作模式
ARM處理器的工作模式就是不同的代碼執行在不同的工作模式下,完成特定的功能,ARM處理器的7種工作模式
1.ARM7 ARM9 ARM11有7種工作模式
2.Cortex-A系列有8種工作模式,多了monitor(安全監控模式)
用戶模式 |
user |
應用層 |
系統模式 |
system |
user的特權模式 |
普通中斷模式 |
IRQ |
外設中斷 |
快速中斷模式 |
FIQ |
高速數據 |
未定義模式 |
Undef |
指令未定義 |
中止模式 |
Abort |
取指/取數據中止 |
管理模式 |
SVC |
復位/軟中斷 |
7、ARM寄存器組織
8、特殊的寄存器
1.r13 -->別名sp:the stack pointer 棧指針寄存器
主要存放的是棧頂的地址
2.r14 -->別名lr:the linking register 鏈接寄存器
主要用於保存函數返回地址的
3.r15 -->別名pc:the program counter 程序計數寄存器
存儲的是當前取指指令的地址
取完一條指令之后,PC中的值會自動加4指向下一條指令,繼續取指
4.cpsr -->current program status register
當前程序狀態寄存器
保存的是當前程序的運行狀態,如工作模式
5.spsr -->saved program status register
保存程序狀態寄存器
專門用於保存CPSR中的值到SPSR中
9、三級流水線
1.取指器:根據PC寄存器中的地址,取對應的指令
2.譯碼器:翻譯指令的功能,交給對應的執行器執行
3.執行器:執行指令,並將指令的執行結果寫回到寄存器中
以上三個期間都是獨立的硬件,工作互不干擾
三個器件都屬於單周期的器件,及一個時鍾周期可以完成一件事
【2】ARM匯編指令集
1、匯編代碼(.s)中的主要符號
1.指令:編譯器將指令編譯生成32位機器碼, 執行時可以完成特定的功能2.偽指令:偽指令本身不是指令,編譯器可以將偽指令編譯生成多條指令,共同完成一條偽指令的功能3.偽操作:偽操作不占用任何的代碼段的空間,指導編譯器對代碼如何進行編譯的(.text .global .end)以.開頭的都屬於偽操作4.注釋:單行注釋:@ 多行注釋:/**/
2、map.lds鏈接腳本文件分析
//1.鏈接腳本文件:告訴編譯器如何對代碼進行編譯的//2.輸出可執行文件為:32位 elf文件 小端對齊OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")/*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*///3.輸出文件的架構:arm架構的可執行程序OUTPUT_ARCH(arm)//4.程序的入口是:_startENTRY(_start)//5.可執行程序中各個段的排布方式SECTIONS{ . = 0x00000000;//程序的入口地址 . = ALIGN(4);//對齊2^4:占位 .text ://代碼段 { //代碼段開始的位置必須放start.o的代碼 ./Objects/start.o(.text) //其他的.o文件編譯器看着辦 *(.text) } . = ALIGN(4); .rodata : //只讀數據段 { *(.rodata) } . = ALIGN(4); .data : //全局數據段 { *(.data) } . = ALIGN(4); __bss_start = .; .bss ://未初始化的全局變量 { *(.bss) } __bss_end__ = .;}
3、指令的基本語法格式
<opcode><cond>{S} Rd, Rn, oprand2
<opcode>:指令的名字
<cond>:條件碼
{s}:狀態位,加S,指令的執行結構影響CPSR的NZCV位
Rd:目標寄存器
Rn:第一個操作寄存器
oprand2:第二個操作數
1>可以是一個普通的寄存器
2>可以是一個立即數
3>可以是一個經過移位操作的寄存器
注意:
1><opcode><cond>{S} :連到一起寫
2>指令和目標寄存器之間使用空格隔開
3>寄存器之間使用“,”隔開
4>一條指令獨占一行
5>指令中不區分大小寫
mov r0,#0xff
mov R0,#0xFF
【3】數據操作指令(最簡單、最常用)
1、數據搬移指令mov / mvn
語法格式:opcode <cond> {s} Rd,oprand2
mov r0, #0x000000FF @ r0 = 0xFF
mov r1, r0 @ r1 = r0
mvn r2, #0x000000FF @ r2 = ~0xFF
@ mov r0, #0xFFF @ error
@ mov r1, #0xFFFF @ error
mov r2, #0xFF000000 @ 0xFF >> 8 ==> 4FF
@ mov r3, #0xFFFF0000 @error
mov r4, #0xFFFFFF00 @ mvn r4, #0xFF
@ 偽指令 ldr
ldr r5, =0x12345678 @// r5 = 0x12345678

2、移位操作指令
語法格式:opcode <cond> {s} Rd,Rn,oprand2lsl:邏輯左移(無符號數左移)高位移出,低位補0lsr:邏輯右移(無符號右移)低位移出,高位補0asr:算數右移(有符號數右移)低位移出,高位補符號位ror:循環右移 低位移出,補到高位mov r0,#0xFF00lsl r1,r0 #0x4 @ r1 = r0 << 4lsr r2,r0,#0x4 @ r2 = r0 >> 4asr r3,r0,#0x4 @ r3 = r0 >> 4
3、算數運算指令
語法格式:opcode <cond> {s} Rd,Rn,oprand2add:普通加法指令adc:帶進位的加法指令sub:普通的減法指令subc:帶借位的減法指令mul:乘法指令注:arm-v7架構之前都沒有除法指令 arm-v8架構才支持除法指令 div
/*假設2個64位的數相加第一個64位的數R0存放低32位,R1存放高32位第二個64位數R2存放低32位,R3存放高32位結果R4存放低32位,R5存放高32位*/mov r0,#0xfffffffemov r1,#0x3mov r2,#0x4mov r3,#0x5adds r4,r0,r2adc r5,r1,r3 @ r5 = r1 + r3 + C
/*假設2個64位的數相減第一個64位的數R0存放低32位,R1存放高32位第二個64位數R2存放低32位,R3存放高32位結果R4存放低32位,R5存放高32位*/mov r0,#0x2mov r1,#0x9mov r2,#0x4mov r3,#0x5subs r4,r0,r2sbc r5,r1,r3 @ r5 = r1 -r3 -!c
mov r0,#3mov r1,#4mul r2,r0,r1
4、位運算指令
位運算指令:and orr eor bic << >> ~語法格式:<opcode><cond>{S} Rd, Rn, oprand2 Rd = Rn # oprand2 ldr r0,=0x12345678@ 1> 將R0寄存器中的,第[3]位置1,保證其他位不變orr r0,r0,#(0x1 << 3)orr r0,r0.#0x8@ 2> 將R0寄存器中的,第[10]位清0,保證其他位不變and r0,r0,#(~(0x1 << 10))@ 3> 將R0寄存器中的,第[7:4]位置1,保證其他位不變orr r0,r0,#(0xf << 4)@ 4> 將R0寄存器中的,第[23:20]位清0,保證其他位不變and r0,r0,#(~(0xf << 20))@ 5> 將R0寄存器中的,第[15:10]位設置為0b101010,保證其他位不變①先清0and r0,r0,#(~(0x3f << 10))②在把相應的位置1orr r0,r0,#(0x2a << 10)@ 6> 將R0寄存器中的,第[31:28]位設置為0b1100,第[11:8]位設置為1011,保證其他位不變and r0,r0,#(~(0xf << 28))orr r0,r0,#(0xc << 28)and r0,r0,#(~(0xf << 8))orr r0,r0,#(0xb << 8)@ 7> bic 位清除指令 給對應的位寫1,就可以清0@ 將R0寄存器中的,第[23:20]位清0,保證其他位不變bic r0,r0,#(0xf << 20)
5、比較指令
比較指令:cmp
用於比較兩個數的大小
格式:cmp Rn,oprand2
1.沒有目標寄存器
2.比較的結果影響的是CPSR的NZCV位
3.指令后邊不需要加S
4.本質:做減法運算
cmp指令常跟條件碼一起使用

mov r0,#8
mov r1,#15
cmp r0,r1
subhi r0,r0,r1
subcc r1,r1,r0
【4】跳轉指令
1、b跳轉指令
b:不帶保存返回地址的跳轉指令
格式:b <cond> Label
跳轉到Label標簽下的第一條指令出執行
標簽:label
場合:有去無回
2、bl跳轉指令
bl:帶保存返回地址的跳轉指令格式:bl <cond> Label 跳轉到Label標簽下的第一條指令出執行 同時保存跳轉指令的下一條的地址到lr寄存器中標簽:label場合:有去有回總結:跳轉指令的本質就是修改PC中的值mov r0,#4 mov r1,#5 bl add_func mov r2,#6 b stopadd_func: add r0,r0,r1 mov pc,lr
/*實現1~100之間所有數相加之和*/for(i=1;i<=100;i++){ sum = sum + i;}sum = r0i = r1for(1;2;3){ 4;}{1;2}{4;3;2}{4;3;2}{4;3;2}
/*求最大公約數*/ mov r0,#0x5 mov r1,#0x8 loop: cmp r0,r1 beq stop subhi r0,r0,r1 subcc r1,r1,r0 b loop
【5】特殊功能寄存器操作指令
1、mrs和msr操作指令
msr cpsr,Rn @cpsr = Rn 寫操作 實際操作是修改cpsr中的值
mrs Rn,cpsr @Rn = cpsr 讀操作 實際操作是將cpsr中的值讀出來,不對cpsr中的值進行比較
2、從SVC模式切換到USER模式
/*第一種方式*/
msr cpsr,#0xD0
/*第二種方式*/
mrs r0,cpsr @將CPSR中的值讀到普通寄存器中
bic r0,r0,#0x1f @清除模式位
orr r0,r0,#0x10 @修改模式位對應位為1
msr cpsr,r0 @在寫到cpsr中
【6】load/store內存讀寫指令
1、單寄存器操作指令
單寄存器操作指令:ldr str
格式:ldr/str<cond> Rn,[Rm]
Rn:Rn中的數據看成一個普通的數據
Rm:Rm中的數據看成內存的地址
ldr:將Rm指向的內存地址中的內容,讀到Rn寄存器中
str:將Rn寄存器中的內容,寫到Rm指向的地址空間中
ldr r0,=0x40000800
ldr r1,=0x12345678
str r1,[r0] 將r1寄存器中的值,寫到r0指向的內存地址中的內容
ldr r2,[r0] 將r0指向的內存地址中的內容,讀到r2寄存器中
ldr r0,=0x40000800
ldr r1,=0x11111111
ldr r2,=0x22222222
ldr r3,=0x33333333
@將r1中的值存到r0+4指向的地址中,r0中的值不變
str r1,[r0,#4]
@將r2中的值存到r0指向的地址中,同時r0 = r0 + 4
str r2,[r0],#4
@將r3中的值存到r0+4指向的地址中,同時r0 = r0 + 4
@!:更新地址
str r3,[r0,#4]!
2、測試arm的大小端
問:首先要知道什么是大端存儲,什么是小端存儲?
答:小端存儲低地址中存放低字節內容,高地址存放高字節內容
大端存儲高地址存放低字節內容,低地址存放高字節內容
思考:如何用匯編代碼,編寫測試大小端,使用字節進行讀取數據就可以了ldr r0,=0x40000800ldr r1,=0x12345678str r1,[r0]ldrb r2,[r0]ARM是小端存儲方式
@使用ldrh讀出地址中4字節的數據@並將讀到的數據合並的r9寄存器中ldrh r3,[r0]ldrh r4,[r0,#2]orr r9,r3,r4,lsl #16
3、多寄存器操作指令
多寄存器操作指令:ldm stmstm/ldm Rm, {寄存器列表}stm:將寄存器列表中的所有寄存器中的內容,存到Rm指向的地址連續空間中ldm:將Rm指向的連續的內存空間中的內容,讀到寄存器列表中的所有寄存器中注意:1.如果寄存器列表中的寄存器連續,則使用“-”隔開,不連續使用“,”隔開2.寄存器列表中的寄存器編號順序,不管怎么寫,永遠都是地地址對應小編號的寄存器,高地址對應大編號的寄存器
ldr r0,=0x40000800ldr r1,=0x11111111ldr r2,=0x22222222ldr r3,=0x33333333ldr r4,=0x44444444ldr r5,=0x55555555stm r0,{r1-r4,r5}ldm r0,{r6-r10}或者如下寫法:stm r0, {r5,r4,r3,r2,r1}ldm r0, {r10,r9,r8,r7,r6}
【7】棧操作指令
1、棧的4種類型(sp棧指針)
滿棧:sp指向的棧空間有有效的數據,需先移動棧指針,在存儲數據空棧:sp指向的棧空間沒有有效的數據,先存儲數據,在將棧指針移動到一個空的空 間增棧:棧指針向高地址方向移動減棧:棧指針向低地址方向移動
2、棧的4種操作方式
滿增棧:stmfa/ldmfa full ascending 滿減棧:stmfd/ldmfd full descending 空增棧:stmea/ldmea empty ascending空減棧:stmed/ldmed empty descending
3、ARM處理器采用滿減棧
語法格式:
stmfd/ldmfd sp!, {寄存器列表}
!:更新棧指針的地址
ldr sp,=0x40000820
ldr r0,=0x11111111
ldr r1,=0x22222222
ldr r2,=0x33333333
ldr r3,=0x44444444
ldr r4,=0x55555555
stmfd sp!, {r0-r4}
ldmfd sp!, {r5-r9}
ldr sp,=0x40000820
mov r0,#3
mov r1,#4
bl add_func
add r2,r0,r1 @r2 = 0x7
b stop
add_func:
@保存現場
stmfd sp!,{r0-r1,lr}
mov r0,#5
mov r1,#6
bl sub_func
add r3,r0,r1 @ r3 = 0xb
@恢復現場
ldmfd sp!,{r0-r1,pc}
sub_func:
stmfd sp!,{r0-r1,lr}
mov r0,#9
mov r1,#7
sub r4,r0,r1 @r4 = r0-r1 = 0x2
ldmfd sp!,{r0-r1,pc}
【8】混合編程
ATPCS規范(ARM規定)1.參數的傳遞通過r0-r3傳遞2.返回值通過r0返回3.參數的個數如果超過4個通過壓棧的方法進行傳遞4.返回值大於4個字節通過r0,r1返回
1、匯編調用c代碼
.text /* .text 代碼段 .data 數據段*/.global _start /* 將_start聲明為全局的函數 */ _start: /* 標簽:理解成C語言的函數名 */ @ 匯編調用C @ C代碼必須有棧 ldr sp, =0x40000820 @ 初始化棧 mov r0, #3 @ 指定實參值 mov r1, #4 bl addfunc @ 調用C函數 @ 返回通過R0返回 stop: /* while(1) {} */ @ 匯編指令 b stop /* 指令:跳轉指令 調用stop函數 */.end /* 結束 */
int addfunc(int a, int b){ return a + b;}
2、c調用匯編
.text /* .text 代碼段 .data 數據段*/
.global _start /* 將_start聲明為全局的函數 */
_start: /* 標簽:理解成C語言的函數名 */
@ 啟動文件,在匯編的階段完成棧的初始化
@ C代碼必須有棧
ldr sp, =0x40000820 @ 初始化棧
b main
.end /* 結束 */
main.c文件
//1.需要使用extern將add_func函數進行聲明
extern int add_func(int a,int b,int c,int d);
int sum = 0;
int main()
{
sum = add_func(1,2,3,4);
return 0;
}
add_func.s文件
.text
@ 2. 將匯編的函數聲明成一個全局的函數
.global add_func
add_func:
add r0, r0, r1
add r0, r0, r2
add r0, r0, r3
mov pc, lr
.end
3、內聯匯編
格式:
asm volatile(
"匯編指令\n\t"
:輸出列表
:輸入列表
:破壞列表
);
//混合編程
int add_func2(int a,int b,int c,int d)
{
int sum;
asm volatile(
"add r0,r0,r1\n\t" //指令列表
"add r0,r0,r2\n\t"
"add r0,r0,r3\n\t"
:"=r"(sum) //輸出列表
:"r"(a),"r"(b),"r"(c),"r"(d) //輸入列表
: //破壞列表
);
return sum;
}