armtech
ARMv8 中文翻譯可見此:http://wiki.csie.ncku.edu.tw/embedded/ARMv8
A64 is a new 32-bit fixed length instruction set to support the AArch64 execution state. The following is a summary of the A64 ISA features.
ARM, generically known as A32, is a fixed-length (32-bit) instruction set. It is the base 32-bit ISA used in the ARMv4T, ARMv5TEJ and ARMv6 architectures. In these architectures it is used in applications requiring high performance, or for handling hardware exceptions such as interrupts and processor start-up.
The ARM ISA is also supported in the Cortex™-A and Cortex-R profiles of the Cortex architecture for performance critical applications, and for legacy code. Most of its functionality is subsumed into the Thumb instruction set with the introduction of Thumb-2 technology. Thumb (T32) benefits from improved code density.
ARM instructions are 32-bits wide, and are aligned on 4-byte boundaries.
Most ARM instructions can be "conditionalized" to only execute when previous instructions have set a particular condition code. This means that instructions only have their normal effect on the programmers’ model operation, memory and coprocessors if the N, Z, C and V flags in the Application Program Status Register satisfy a condition specified in the instruction. If the flags do not satisfy this condition, the instruction acts as a NOP, that is, execution advances to the next instruction as normal, including any relevant checks for exceptions being taken, but has no other effect. This conditionalization of instructions allows small sections of if- and while-statements to be encoded without the use of branch instructions.
==>
A64’s Key differences from A32 are:
ARM 是在全球中被廣泛使用的 processor cores,像是 PDA、手機、多媒體播放器、數位電視、相機等等。ARM processors 有許多 family,如 ARM7、 ARM9、 ARM11、ARMv7,同一個 family 的設計使用相似的設計原則及同一個common instruction set。他的設計哲學是用有限的硬體( 有限的 memory 和 physical size restrictions) 資源達到 high code density。
ARM 的命名方式 (Cortex系列以前) - ARMxyzTDMIEJFS
(在 ARM Cortex-A/R/M之前的 "ARM Classic")
ps : TDMI 這四項基本功能成了任何新產品的標準配備,於是就不再使用這4個後綴。但是新的後綴不斷加入,包括定義存儲器界面的,定義高速快取的,以及定義"緊耦合存儲器(TCM)"的,於是形成了新一套命名法,這套命名法一直使用至今。比如ARM1176JZF-S,它實際上預設就支持TDMI功能,除此之外還支持JZF。
這套命名機制在 ARM Cortex-A/R/M 之後,徹底棄置。以 MMU 來說,ARM Cortex-A 系列都有 MMU,而 Cortex-M0/M0+/M3/M4 均缺乏 MMU,僅有選擇性的 MPU。Cortex-M7 開始提供 cache 和 TCM
ARM Classic
(在 ARM Cortex 系列出現之前)
ARM 採用 RISC
ARM architecture 從 Berkeley RISC design合併了幾個特點,但也有些並無採用。
ARM 的架構
註: MAC = Multiply–accumulate operation
ARM Register
以下是ARM的 register,在User/System Mode時,可以使用r0~r15,其中r13為stack pointer,r14為link register,r15為program counter
Current Program Status Register(CPSR) : 在user-level時,用於存取condition code bits
溢位複習:
i)如果是加法操作,結果不正確,結果溢出
ii)如果是減法操作,結果正確,結果未溢出
b) C=0
i)如果是加法操作,結果正確,結果未溢出
ii)如果是減法操作,結果不正確,結果未溢出。在這種情況下,結果是負數。然而,在無符號整數世界裡,負數不存在,我們認識這樣的操作是非法的。當然,如果認為答案是以有符號整數補碼的形式出現,則結果正確。
a) V=1,結果不正確,結果溢出
b) V=0,結果正確,結果未溢出
通用暫存器有6種 data types ( signed / unsigned) ( word /Half word/ byte),而在所有ARM的運算為32-bit,比較小的資料型態只有在資料傳送的運算中被支援。
Program Counter ( PC ) 是儲存要被執行的位址,而所有指令皆為 32-bit wide 且 word aligned
在 ARM 架構中支援了 ( 7+1 )種模式,如圖:
Instruction sets
裡面有 ARM / Thumb / Jazelle ,下圖為簡單介紹:
(實際上 ARM 的 extension 遠比以下列出的多)
Pipeline
在執行時, PC 是 8 bytes ahead,也就是說 Pipeline 準備要做 Execute級( 位址是 0x8000)時,要讀取的下一個位置是 PC + 8 的位置(從範例中可清楚看見,即 DCD 指令的位址 0x8008)
他有以下幾點特點:
Interrupts
當發生 exception 或 interrupt 時,會觸發 Interrupt handler,此時,他會尋找 vector table 去做中斷時所要處理的 routine.
下圖為中斷定義及其跳躍的起始位址:
在 user-mode program 中,可以看到 15 個 32-bit general purpose registers (R0-R14), program counter (PC) 及 CPSR,在指令集中有定義一些指令可以改變 state。
在開始深入探討前,先來看看他的 Memory system吧~
Memory system
剛剛有提到 Little Endian,他其實是 Byte ordering 的其中一種方式,endian指的是當物理上的最小單元比邏輯上的最小單元還要更小時,邏輯單元對映到物理單元的排佈關係。舉例來說:
如果你在文件上看到一個雙字組的data,Ex: long MyData=0x12345678
,要寫到從0x0000開始的記憶體位址時。
比較的結果就是這樣:
big-endian | little-endian | |
0x0000 | 0x12 | 0x78 |
0x0001 | 0x34 | 0x56 |
0x0002 | 0x56 | 0x34 |
0x0003 | 0x78 | 0x12 |
Features of ARM instruction set
Instruction set
可分成三大項:
Data processing
資料處理指令包含對資料做 移動、算數、邏輯、比較、乘法 的指令,且大部分的 data processing instructions 可以對一個 operand 做 shift,如圖:
Arithmetic operations:
ADD r0, r1, r2 ; r0 := r1 + r2
ADC r0, r1, r2 ; r0 := r1 + r2 + C
SUB r0, r1, r2 ; r0 := r1 - r2
SBC r0, r1, r2 ; r0 := r1 - r2 + C - 1
RSB r0, r1, r2 ; r0 := r2 - r1
RSC r0, r1, r2 ; r0 := r2 - r1 + C - 1
Bit-wise logical operations:
AND r0, r1, r2 ; r0 := r1 and r2
ORR r0, r1, r2 ; r0 := r1 or r2
EOR r0, r1, r2 ; r0 := r1 xor r2
BIC r0, r1, r2 ; r0 := r1 and not r2
Register movement operations:
Comparison operations:
只設定在CPSR的 condition code bits(N, Z, C and V)
CMP r1, r2 ; set CC on r1 - r2
CMN r1, r2 ; set CC on r1 + r2
TST r1, r2 ; set CC on r1 and r2
TEQ r1, r2 ; set CC on r1 xor r2
這裡的CC代表CPSR的 condition code bits
Immediate operands:
ADD r3, r3, #1 ; r3 := r3 + 1
AND r8, r7, #&ff ; r8 := r7[7:0]
#
後放置數字,則表示十進位數值; 而在 #
後放置 &
符號,可表示十六進位數值mov r0, 0xDEADBEEF
load_32bit:
ldr r0, [pc #0] ;請注意: pc 位於目前位址向前 8 bytes 的地方
bx lr
.word 0xDEADBEEF
movw r0, #0xbeef ; r0 = 0x0000beef
movt r0, #0xdead ; r0 = deadbeef
.equ label, 0xDEADBEEF
movw r0, #:lower16:label
movt r0, #:upper16:label
Shifted register operands:
舉例來說:
ADD r3, r2, r1, LSL #3 ; r3 := r2 + ( r1 << 3 )
LSL : logical shift left by 0 to 31 places; fill the vacated bits at the least significant end of the word with zeros
LSR : lgoical shift right by 0 to 32 places; fill the vacated bits at the most significant end of the word with zeros
ASL : arithmetic shift left; this is a synonym for LSL
ASR : arithmetic shift right by 0 to 32 places; Register contents are treated as two’s complement signed integers.; fill the vacated bits at the most significant end of the word with the sign bit
ROR : rotate right. Provides the value of the contents of a register rotated by a value. The bits that are rotated off the right end are inserted into the vacated bit positions on the left.
RRX : provides the value of the contents of a register shifted right one bit. The old carry flag is shifted into bit[31]. If the S suffix is present, the old bit[0] is placed in the carry flag.
位移 (shift) 主要分兩種:
簡單來說,邏輯移位無論左移右移,一律都是把多出來的位數填零。算術運算左移跟邏輯移位一樣填零,右移需要考慮到 singed 的屬性,假若最高位是 1,則填補1,反之若最高位是 0,則填補0。
範例:
signed int a = 1234;
signed int b = 6;
signed int r = a >> b ; / * 邏輯平移運算 */
unsigned int a = 1234 ;
int b = 6 ;
unsigned int r = a >> 6 ; /* 算數平移運算 */
注意: ARM Toolchain (包含 arm-none-eabi/linux-gnueabi) 預設將 "int" 視為 "unsigned"
mov r0, #8000000F
0x8000000F
放入暫存器 r0 中<r0> | 1 0 0 0 | 0 0 0 0 | 0 0 0 0 | 0 0 0 0 | 0 0 0 0 | 0 0 0 0 | 0 0 0 0 | 1 1 1 1 |
mov r1, r0, LSL #1
<r1> | 0 0 0 0 | 0 0 0 0 | 0 0 0 0 | 0 0 0 0 | 0 0 0 0 | 0 0 0 0 | 0 0 0 1 | 1 1 1 0 |
向左位移後,讀取 r0 的值,經過 barrier shifter 後,數值變成 0x1E
bit 31 向左位移後,超過 32 bit 範圍,會被捨棄,而 bit 4, 3, 2, 1 分別位移到 bit 5, 4, 3, 2
mov r1, r0, lsl #2
等價於 r1 = (int) (r0 << 2)
If S is specified, these instructions update the condition code bits
Ex:
這裡可以看第 25-bit(#) 決定 operand 2 是以何種格式,如果 # = 1 就如圖下只有 1 種對應格式,如果 # = 0就需要再比對位置在第四個bit ( 決定2種格式 )
Flow Control
決定哪一條指令將被執行
B | branch | pc = label pc-relative offset within 32MB |
BL | branch with link | pc = label |
BX | branch exchange | pc = Rm & 0xfffffffe, T = Rm & 1 |
BLX | branch exchange with link | pc = label, T = 1 |
Mnemonic | Name | Condition flags |
EQ | equal | Z |
NE | not equal | z |
CS HS | carry set/unsigned higher or the same | C |
CC LO | carry clear/unsigned lower | c |
MI | minus/negative | N |
PL | plus/positive or zero | n |
VS | overflow | V |
VC | no overflow | v |
HI | unsigned higer | zC |
LS | unsigned lower or same | Z or c |
GE | signed greater than or equal | NV or nv |
LT | signed less than | Nv or nV |
GT | signed greater than | NzV or nzv |
LE | signed less than or equal | Z or Nv or nV |
AL | always (unconditional) | ingnored |
Branch | Interpretation | Normal uses |
B BAL | Unconditional | Always take this branch |
BEQ | Equal | Comparison equal or zero result |
BNE | Not equal | Comparison not eaual or non-zero result |
BPL | Plus | Result positive or zero |
BMI | Minus | Result minus or zero |
BLO | Lower | Unsigned comparison gave lower |
BHS | or the same | Unsigned comparison gave higher or same |
BVC | Overflow clear | Signed integer operation; no overflow occured |
BVS | Overflow set | Signed integer operation; overflow occured |
BGT | Greater than | Signed integer comparison gave greater than |
BGE | Greater than or equal | Signed integer comparison gave greater than or equal |
BLT | Less than | Signed integer comparison gave less than |
BLE | Less or equal | Signed integer comparison gave less than or equal |
BHI | Higher | Unsigned comparison gave higher |
BLS | Lower or the same | Unsigned comparison gave lower or same |
Data transfer instructions
(許願: 希望能有餐廳結帳和超商為何有提款機的故事的文字版)
data processing 的 mov 指令是暫存器間互相傳送資料,而 data transfer instructions 是暫存器與 memory 間互相傳遞資料,而 data transfer instructions 有三種基本形式
2
LDR | load word into a register | Rd <-- mem32[address] |
STR | save byte or word from a register | Rd --> mem32[address] |
LDRB | load byte into a register | Rd <-- mem8[address] |
STRB | save byte from a register | Rd --> mem8[address] |
LDRH | load halfword into a register | Rd <-- mem16[address] |
STRH | save halfword into a register | Rd --> mem16[address] |
LDRSB | load signed byte into a register | Rd <-- SignExtend |
LDRSH | load signed halfword into a register | Rd <-- SignExtend |
ps. 沒有 STRSB/STRSH 是因為 STRB/STRH 儲存 signed/unsigned 到記憶體位置( 存進去就不管他是有號無號,讀出來才要判別)
Memory 定址可以透過暫存器和 offset,例:
他有三種 Addressing modes
Index method | Data | Base address register | Example |
Preindex with writeback | mem[ base + offset ] | base + offset | LDR r0,[r1, #4]! |
Preindex | mem[ base + offset ] | not updated | LDR r0, [r1, #4] |
Postindex | mem[ base ] | base + offset | LDR r0, [r1], #4 |
以上是有 offset 的三種 addressing modes,接下來來看看 Register 的 addressing modes
Pre-indexed addressing
LDR R0, [R1, R2] @ R0 = mem[ R1 + R2 ]
@ R1 unchanged
Auto-indexed addressing
LDR R0, [R1, R2]! @ R0 = mem[ R1 + R2 ]
@ R1=R1+R2
Post-indexed addressing
LDR R0, [R1], R2 @ R0=mem[ R1 ]
@ R1 = R1 + R2
ps.有一個 pseudo instruction ADR 可以 load and address 到一個暫存器裡
意思就是一次可以存取很多個暫存器的值
簡單的範例:
<LDM|STM>{<cond>}<addressing mode> Rn{!},<registers>{^}
驚嘆號(!)代表要存取後會 register 進行更新(後面有範例)
Addressing mode | Description | Start address | End address | Rn! |
IA | increase after | Rn | Rn + 4*N - 4 | Rn + 4*N |
IB | increase before | Rn + 4 | Rn + 4*N | Rn + 4*N |
DA | decrease after | Rn - 4*N +4 | Rn | Rn - 4*N |
DB | decrease before | Rn -4*N | Rn - 4 | Rn - 4*N |
用上表有點難看出變化,以下用圖來表示
其中 R1 = 10, R2 = 20, R3 = 30,更新後的 R0 = 0x01c
從這裡可以看到依序從最低位開始存,存完才將R0 + 4 ,最後,當存完R3的值之後,R0更新成 0x01c,因為上面指令有 !
,所以將最後值存在 R0中
ps.: 如果沒有!
,則最後R0 = 0x010,後面的範例也是如此
其中 R1 = 20, R2 = 30, R3 = 40,更新後的 R0 = 0x01c
從這裡可以看到先將R0 + 4 後才開始存,然後依序往上加 ,最後,當R0更新成 0x01c也存完R3的值之後,因為上面指令有 !
,所以將最後值存在 R0中
其中 R1 = 40, R2 = 50, R3 = 60,更新後的 R0 = 0x018
從這裡可以看到依序從最高位開始存,存完才將R0 - 4 ,最後,當存完R1的值之後,R0更新成 0x018,因為上面指令有 !
,所以將最後值存在 R0中
其中 R1 = 30, R2 = 40, R3 = 50,更新後的 R0 = 0x018
從這裡可以看到先將R0 - 4 後才開始存 ,最後,當R0更新成 0x018也存完R1的值之後,因為上面指令有 !
,所以將最後值存在 R0中
可用來複製一塊 block 的 memory
loop: LDMIA R9!, {R0-R7}
STMIA R10!, {R0-R7}
CMP R9, R11
BNE loop
mode | POP | =LDM | PUSH | =STM |
Full ascending (FA) | LDMFA | LDMDA | STMFA | STMIB |
Full descending (FD) | LDMFD | LDMIA | STMFD | STMDB |
Empty ascending (EA) | LDMEA | LDMDB | STMEA | STMIA |
Empty descending (ED) | LDMED | LDMIB | STMED | STMDA |
為何ARM的 PC
是指向下兩條指令?
參考: 舊的 MIPS 架構: fetch 之後, PC = PC + 4, 且一路往下傳遞到接下來的 stages
https://commons.wikimedia.org/wiki/File:Pipeline_MIPS.png
如上面所說 PC 指向正在執行的後 2 條指令 (+8),但若某道正在執行指令遇到中斷,這時候的 PC 會如何變化?
這個問題要分兩個面向來答覆,一個是 interrupt 的處理機制,另一個則是 PC 的計算方式
引述《The Definitive Guide to the ARM Cortex-M3 》Page 287:
"Interrupt handler and interrupt return: In the ARM7, the first instruction of the interrupt handler is in the vector table, which normally contains a branch instruction to the actual interrupt handler. In the Cortex-M3, this step is no longer needed. For interrupt returns, the ARM7 relies on manual adjustment of the return program counter. In the Cortex-M3, the correctly adjusted program counter is saved into the stack and the interrupt return is triggered by loading EXC_RETURN into the program counter. Instructions, such as MOVS and SUBS, should not be used as interrupt returns on the Cortex-M3. Because of these differences, interrupt handlers and interrupt return codes need modification during porting."
以 ARM Cortex-M3/M4 來說,不再需要像 ARM7 那樣手動調整返回的 program counter 值,而是以 Cortex-M 硬體給定 EXC_RETURN
作為新的 program counter,過程中原有的 pc 值會由硬體重新計算,一旦返回到原有程式時,仍以 +4/+8 (ARM) 作為位移量
學組語的目的,不見得是為了改善效能,而是:
判斷 optimizing compiler 產生的機械碼是否正確
從 Google 搜尋偷到的程式是否有效益 (千萬不要人云亦云,要有判斷能力)
( 11:00-12:00 )
[ Introduction to ARM Architecture ]
objdump
得到類似結果? 試了幾種參數感覺跟投影片上都有落差
Versatile Express board 是 ?
為什麼Variable cycle instructions (LD/STR multiple)可以提高效能?
–Auto-increment/decrement addressing modes
這個的意思是?
後面會提到 STM 跟 LDM 的東西,那時候會有介紹
ARMv8 (64-bit) 移除 LD/STR mutiple
有 LD/STR 跟 3 stage pipeline 變成 5 stage pipeline 有關
因為記憶體開銷實在太大
在ARM系統開發者指南中將 exception modes 翻譯成處理器模式
,感覺跟 exception 這個字的意思差很多,不知怎麼翻譯成處理器模式的 ?
CPU modes, exception modes, processor modes 對ARM來講都是一模一樣
ARM wiki 是寫 CPU modes 還是這邊的 exception modes不是指 CPU modes?
以 ARM 來說,會導致 CPU 變更 execution mode 改變,就是 exception 使然,所以等價,當然,如果我們可用同樣的術語,對簡報陳述較好
ARM切換不同模式會 exception
5 SPSR
Several exception modes
g(x), f(x) (g of f)(x); Proc 是退化的 func,對應到 ISA 的 branch/jump
e.g. C 語言只能 return一個值
procedure輸出域很窄
function 有輸入域和輸出域
p. 11 ARM vs. x86
p. 26 Conditional Flags
V – Overflow flag
Q – Sticky overflow
http://stackoverflow.com/questions/19557338/importance-of-qsaturation-flag-in-arm
簡而言之就是飽和操作如果遇到 overflow 則會讓 Q = 1
p. 29 PUSH operation
stack pointer 是紀錄堆疊最後的位置
- NOT(C)
怎麼做? 為什麼 After Operation CPSR 沒有變化?
- NOT(C) 怎麼做
是什麼意思?p. 37 的重點在? 看不出來上下兩個指令有甚麼差別在這頁有甚麼差別
有號無號對於乘除是個大問題,不能像加減法用 2’s complement 跟 Flag 輕鬆解決
p. 43 last bit off right is Carry 這句話是最後一個向右移的位元是進位?
[ 16bit ] [c]
0101010101010101 0
ROR 1
1010101010101010 1
最後一個 bit 會被送到 carry flag,也會放到最左邊的 bit 上
Barrel Shifter 可提升程式碼密度 (空間複雜度)
p. 44 RTFM = Read The F*cking Manual
p. 47 ASR 如何運作
LDR -> http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0041c/Babbfdih.html
asrs r0, r0, #1
p. 43 ASR – Arithmetic Shift Right (MSB copied at left, last bit off right is Carry)
MSB 是?
most significant bit 最高位的 bit
ASR,MSB 保持不變,其他 bit 向右 shift
可以用 QEMU + GDB 進行單步執行
用 objdump 去看 machine code
對 compiler 可以設定參數,進行最佳化
asr 可以當成是有號的右移,等於是有號的 2^n 除法
在右移的時候,左邊補進來的數字也會是有號的,於是正數還是正,負依舊是負
而在此圖的案例當中,必須要用 2’s complement 來探討
add.w r0,r0,r0,lsr #31
asrs r0,r0,#1
為什麼要先做 add.w r0,r0,r0,lsr #31 ?
意思是指先把自己跟自己的 sign 相加,也就是如果是負數,就加一,正數則不變
這要從二的補數來看,當你右移 #1 之後,相當於除以二
此時最右邊的那個 bit 是 "直接消失" 對於正數不會有任何影響
但對於負數來說等於是多進了一位,假設以下例子
-3 / 2 ===> 1101 >> 1 (asr) , answer = 1110 = -2
因為二補數表示法的負數相當於"與 0 的距離"
所以對於負的奇數來說
消失的最右邊那個bit,等於是間接幫他進位(與 0 的距離變大)ex. -3 ==> -4
因此必須要先做 add.w r0,r0,r0,lsr #31 來消除掉這個進位
-3 / 2 ===> (-3+1)/2 ===> 1110 >> 1 (asr) , answer = 1111 = -1
結論就是compiler太聰明了!
為什麼Lab38中 example 3 執行完會是0?
p. 48 RRX 如何運作
[c] [ 8bit ]
1 01010100
RRX 1
[c] [ 8bit ]
0 10101010
把 c 加入,上圖所示,把 8bit 用 c 延長成 9bit 再去 rotate
p. 50 movw ??
movw
followed by a movt
is a common way to load a 32-bit value into a register.
比較新的指令集才有,跟ARM版本有關(ARMv7)
-> http://community.arm.com/groups/processors/blog/2010/07/27/how-to-load-constants-in-assembly-for-arm-architecture
p. 93 STMDB equals to push operation
p. 94 LDMIA euals to pop operation
ex. in fib.s
use stmdb sp!, {r4, r5, lr}
instead of push {r4, r5, lr}
and ldmia sp!, {r4, r5, lr}
instead of pop {r4, r5, lr}
p.121 gdb arm 的 objectfile 有需要注意的事項嗎?
gdb example1
, b main
, r
then I get warning: Unable to find dynamic linker breakpoint function.
GDB will be unable to debug shared library initializers
gdb gcc出來的code倒是可以正常 break
如果你的 example1
是 arm binary, 你應該無法這樣執行才對 (除非你的 Linux Distro 有對 binfmt 作些設定)
謝謝! It works like a charm!! 我在step2用gdb-multiarch
也行的樣子(不知為何沒有裝到arm-linux-gnueabihf-gdb
剛看一下 gdb 編譯參數,他可以加上 --enable-targets=all
這個選項,我想你用的 linux distro 應該是編譯 gdb 時有打開這個參數,所以就不需要 arm-linux-gnueabihf-gdb
了
感謝! so上有說到這個, 跟我用的distro有關(lubuntu 15.04)
針對 qemu-user 執行的程式使用 gdb
qemu-arm -L /usr/arm-linux-gnueabihf -g 1234 ./example1
arm-linux-gnueabihf-gdb
or gdb-multiarch
file example1
target remote [localhost]:1234
c
to continue program or add your breakpoint, e.g. b main