--- tags: ARM 架構, 進階電腦系統理論與實作, NCKU Linux Kernel Internals, 作業系統 --- # 基礎指令和開發環境 contributed by <`RusselCK` > ###### tags: `RusselCK` ## [基礎指令和開發環境](https://youtu.be/yHAdlr4pF30) [(ARM 指令)](https://beta.hackfoldr.org/arm/https%253A%252F%252Fhackmd.io%252Fs%252FBkGRdKmsg) ### ARM 與 MIPS ![](https://i.imgur.com/Tauxa6R.png) > 2012年,[MIPS](https://en.wikipedia.org/wiki/MIPS_architecture) 被 [Imagination](https://en.wikipedia.org/wiki/Imagination_Technologies) 買走了 > [CoreMarks](https://en.wikipedia.org/wiki/Coremark) : a benchmark that measures the performance of central processing units (CPU) used in embedded systems. - [ ] ==[ARMv8 中文翻譯](http://wiki.csie.ncku.edu.tw/embedded/ARMv8)== ### ARM 的命名方式 (Cortex系列以前) - ARMxyzTDMIEJFS (在 ARM Cortex-A/R/M之前的 "ARM Classic") * x: 處理器系列 * y: 記憶體管理單元 (MMU) * z: cache * T: 支援 Thumb 指令集 * D: 支援 debugger * M: 支援快速乘法 ( Multiplier ) * I: 支援 [Embedded ICE](https://en.wikipedia.org/wiki/In-circuit_emulation) (built-in debugger hardware) * E: 支援增強型 DSP 指令 (Enhanced instruction) * J: 支援 [Jazelle ](https://en.wikipedia.org/wiki/Jazelle)(JVM) * F: 具備向量浮點單元 VFP ( Floating-point) * -S: Synthesizible version (source code version for EDA tools) 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](http://www.arm.com/products/processors/cortex-m/cortex-m7-processor.php) 開始提供 **cache 和 TCM** * ARM7TDMI (armv4) * 3 stages pipeline (fetch/decode/execute) * 高密度程式/低功耗 * One of the most used ARM-version (for low-end systems) * 在 ARM7TDMI 之後版本的所有 ARM cores 都具備 TDMI * [von Neumann architecture](https://en.wikipedia.org/wiki/Von_Neumann_architecture) * ARM9TDMI (armv4) * 與 ARM7   * 5 stages (fetch/decode/execute/**memory/write**) * Separate instruction and data cache * modified [Harvard architecture](https://en.wikipedia.org/wiki/Harvard_architecture) * ARM11 * ARMv6 架構,是Cortex-A的基礎 ### ARM 採用 RISC ARM architecture 從 Berkeley RISC design合併了幾個特點,但也有些並無採用。 * Features used * a load-store architecture * 固定長度 32-bit instructions * 3-address instruction 形式 * Features rejected * register window * 主要原因在於 register window是一塊由許多暫存器所佔據的、很大的chip 空間 * delayed branches * 主要原因在於 delayed branches remove the atomicity of individual instructions.在super-scalar 和 branch prediction mechanisms 無法好好相互配合 * single-cycle execution of all instructions * 還是有些指令需要超過2個或2個以上的cycle執行 ### ARM 的架構 **Data flow** ![](https://i.imgur.com/EYpymaP.png) * Barrel shifter * MAC = [Multiply–accumulate operation](https://en.wikipedia.org/wiki/Multiply%E2%80%93accumulate_operation) **Memory** ![](https://i.imgur.com/sxzP6k2.png) * CPSR (Current Processor Status Register) ### ARM Register (for ARM classic) 以下是 ARM 的 register,在 User/System Mode 時,可以使用 r0~r15 其中 r13 為 stack pointer,**r14 為 link register**,r15 為 program counter ![](https://i.imgur.com/CzS2qrX.png) :::danger * 上圖不適用於 Cortex-M 系列 * No FIQ * No CPSR ::: **IFQ (Fast Interrupt Request) mode** ![](https://i.imgur.com/tkbhDVY.png) * Nested Interrupt ![](https://i.imgur.com/nG2dU8S.png) ### ARM CPSR & Processor modes **Current Program Status Register(CPSR)** * 在 user-level 時,用於存取 condition code bits **Saved Processor Status Register (SPSR)** * 在中斷時用來自動儲存CPSR的暫存器 ![](https://i.imgur.com/DcDdRlG.png) * **conditionalized** N : Negative / Z : Zero / C : Carry / V : oVerflow * I : Interrupt / F : FIQ / T : Thumb #### ARM Processor modes 在 ARM 架構中支援了 ( 7+1 ) 種模式 : ![](https://i.imgur.com/m6Ag6bz.png) #### Instruction sets 裡面有 ARM / Thumb / Jazelle ,下圖為簡單介紹: (實際上 ARM 的 extension 遠比以下列出的多) ![](https://i.imgur.com/daSNkyJ.png) ### ARM Pipeline 在執行時, PC 是 8 bytes ahead,也就是說 Pipeline 準備要做 Execute級( 位址是 0x8000)時,要讀取的下一個位置是 ==PC + 8== 的位置(從範例中可清楚看見,即 DCD 指令的位址 0x8008) ![](https://i.imgur.com/ewhsmy9.png) 他有以下幾點特點: * 當 Pipeline 在做 branch 或者直接修改 PC 值的話, 會造成 ARM core flush 他的 pipeline * ARM10 開始使用 branch prediction * **即使發生中斷,也會將 Pipeline 中所有指令執行完才會去做中斷的事情** :::warning - [x] 為何 ARM 的 `PC` 是指向下兩條指令? * ARM pipeline stages: * **從 EXE stage 往回看 FETCH stage** : 正在 <u>fetch下兩條指令</u> * 舊的 MIPS 架構 : * **fetch 之後, PC = PC + 4, 且一路往下傳遞到接下來的 stages** ![](https://hackpad-attachments.s3.amazonaws.com/embedded2015.hackpad.com_7ZSsa98cSKw_p.299401_1447080573311_800px-Pipeline_MIPS.png) - [ ] [关于ARM的PC指针(什么时候PC+8,PC+4,PC-4,PC-8)](https://blog.csdn.net/lee244868149/article/details/49488575) ::: :::warning - [x] 如上面所說 PC 指向正在執行的後 2 條指令 (+8),但若某道正在執行指令遇到中斷,這時候的 PC 會如何變化? * 這個問題要分兩個面向來答覆,一個是 interrupt 的處理機制,另一個則是 PC 的計算方式 * 以 ARM Cortex-M3/M4 來說,不再需要像 ARM7 那樣手動調整返回的 program counter 值,而是以 Cortex-M 硬體給定 `EXC_RETURN` 作為新的 program counter,過程中<u>原有的 pc 值會由硬體重新計算</u>,一旦返回到原有程式時,仍以 +4/+8 (ARM) 作為位移量 - [ ] 《[The Definitive Guide to the ARM Cortex-M3](https://www.eecs.umich.edu/courses/eecs373/labs/refs/M3%20Guide.pdf) 》 ::: ### Exception / Interrupt / Trap > 在 ARM 中, Exception = Interrupt = Trap 當發生 exception 或 interrupt 時,會觸發 Interrupt handler,此時,他會尋找 **vector table** 去做中斷時所要處理的 routine. ![](https://i.imgur.com/8Cw2oE4.png) 下圖為中斷定義及其跳躍的起始位址 (for Classic ARM) : ![](https://i.imgur.com/Psu6YVt.png) * Software interrupt : 實作 System call * 唯一可用軟體決定時序的 interrupt ## ARM instruction set - [ ] ==[ARM Instruction Set](https://www.csie.ntu.edu.tw/~cyy/courses/assembly/12fall/lectures/handouts/lec09_ARMisa.pdf)== ### Features 傳統 RISC 都有的特色 : * Load-store architecture * [3-address instructions](https://en.wikipedia.org/wiki/Three-address_code)  ARM 獨有的特色 : * 每個指令都可以做 Conditional execution * 可以一次 load / store 多個暫存器 * 指令可以結合 shift 和 ALU operations (像是 add, sub) * Barrier shift : **提升指令的密度** ### 分類 可分成三大項: 1. Data processing * move * arithmetic * logical * comparison * multiply 2. Data movement (memory access) 3. Flow control ### 1. Data processing 資料處理指令包含對資料做 移動、算數、邏輯、比較、乘法 的指令,且大部分的 data processing instructions 可以對一個 operand 做 shift : ![](https://i.imgur.com/g9wv10d.png) #### Arithmetic operations | Mnemonic | Description | Syntax | Result | |:--------:|:------------------------------ |:---------------- |:----------------------- | | ADD | simple addition | `ADD r0, r1, r2` | `r0 := r1 + r2` | | ADC | add with carry | `ADC r0, r1, r2` | `r0 := r1 + r2 + C` | | SUB | subtract | `SUB r0, r1, r2` | `r0 := r1 - r2` | | SBC | subtract with carry | `SBC r0, r1, r2` | `r0 := r1 - r2 + C - 1` | | RSB | reverse subtraction | `RSB r0, r1, r2` | `r0 := r2 - r1` | | RSC | reverse subtraction with carry | `RSC r0, r1, r2` | `r0 := r2 - r1 + C - 1` | :::warning * `( + c - 1 )` = `- !c` ![](https://i.imgur.com/QtcipTa.png) ::: #### Bit-wise logical operations ```c 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 ``` * BIC : bit clear #### Register movement operations * **暫存器間**互相傳送資料 ```c MOV r0, r2 // r0 := r2 NVN r0, r2 // r0 := nor r2 ``` #### Comparison operations 只設定在CPSR的 condition code bits (N, Z, C and V) * `CC` : CPSR 的 **condition code bits** | Mnemonic | Description | Syntax | Result | |:--------:|:--------------- |:------------ |:--------------------- | | CMP | compare | `CMP r1, r2` | set CC on `r1 - r2` | | CMN | compare negated | `CMN r1, r2` | set CC on `r1 + r2` | | TST | (bit) test | `TST r1, r2` | set CC on `r1 and r2` | | TEQ | test equal | `TEQ r1, r2` | set CC on `r1 xor r2` | #### Immediate operands * `#1` = (1)~10~ * `#&ff` = `0xff` ```c ADD r3, r3, #1 // r3 := r3 + 1 AND r8, r7, #&ff // r8 := r7[7:0] ``` #### Shifted register operands 舉例來說 : ```c ADD r3, r2, r1, LSL #3 // r3 := r2 + ( r1 << 3 ) ``` ![](https://i.imgur.com/oaQlV3Y.png) ### Barrier shift ( for Classic ARM & ARMv7 ) ```c= mov r0, #8000000F // 將數值 0x8000000F 放入暫存器 r0 中 mov r1, r0, LSL #1 // r0 左移 1 bit 後,將值放入 r1 中 ``` `#1` : `r0` = `1000 0000 0000 0000 0000 0000 0000 1111` `#2` : `r1` = `0000 0000 0000 0000 0000 0000 0001 1110` * fast multiply : ```c mov r1, r0, lsl #2 => r1 = (int) (r0 << 2) add r1, r0, r0, lsl #2 => r1 = (int) (r0 << 2) + r0 => r1 = r0 * 5 ``` * Barrier shift 在 ARMv8 的 A64 之後被去除 > 但 A32 仍可使用 (向下相容) **Condition flags** If ==**`S`**== is specified, these instructions update the condition code bits (N, Z, C or V) ```c ADDS r2, r2, r0 // 32-bit carry out -> C ``` ### ARM 如何載入 32-bit 常數 ? 在 r0 暫存器中,存放 `0xDEADBEEF` > [Hexspeak](https://en.wikipedia.org/wiki/Hexspeak) * 直覺的作法: ```c mov r0, 0xDEADBEEF ``` :::warning 技術上來說,這是不可能的!為何? * ARMv7 (含) 之前,所有的指令都是 32 bit (opcode + operand) * 以 mov  來說,所有的 32 bit 自然包含 **mov 指令本身**、**目標 register**,以及**目標值** 因此,不可能將任意的 32-bit 編碼的值存放到  32 bit 指令中 ::: * MIPS 的作法 : [Loading a 32 bit Immediate](https://courses.cs.washington.edu/courses/cse378/01au/files/pdf/378-ln8.pdf) ```c lui $s0, 0xDEAD ori $s0, $s0, 0xBEEF ``` * ARMv7 之前的作法 : ```c load_32bit: ldr r0, [pc #0] // pc 位於目前位址向前 8 bytes 的地方 bx lr // 趕快跳走,不要讓下一行被執行 .word 0xDEADBEEF // 這行不是 ARM 組合語言的指令 ``` * 在 ARMv7 後,引入兩個步驟的指令來載入數值: `movw`, `movt` ```c movw r0, #0xbeef // r0 = 0x0000beef movt r0, #0xdead // r0 = 0xdeadbeef ``` * GNU as 給予便利的寫法 ```c .equ label, 0xDEADBEEF movw r0, #:lower16:label movt r0, #:upper16:label ``` ### 3. Flow control instructions * Determine the instruction to be executed next ![](https://i.imgur.com/q0MJPwj.png) ![](https://i.imgur.com/qeNejIJ.png) ![](https://i.imgur.com/Rjm6yWV.png) ### 2. Data transfer instructions * **暫存器與 memory 間**互相傳遞資料 > Data processing 的 `mov` 指令是**暫存器間**互相傳送資料 三種基本形式: 1. Single register load/store ,也就是對單一暫存器做 load/store 2. Multiple register load/store,可以對多個暫存器做 load/store 3. Single register swap: SWP(B), atomic instruction for semaphore ### Single register load/store ![](https://i.imgur.com/hFHOipX.png) ![](https://i.imgur.com/E2CBe6C.png) :::warning * 沒有 **STRSB/STRSH** 是因為 STRB/STRH 儲存 signed/unsigned 到記憶體位置 * 存進去就不管他是有號無號,讀出來才要判別 ::: **Addressing mode** * Memory 定址可以透過 **暫存器** 和 **offset** ```c LDR R0, [R1] // mem[R1] ``` **3 ways to specify offsets:** | Way | Example | Result | |:------------------ |:--------------------------:|:---------------------------- | | 1. Immediate | `LDR R0, [R1, #4]` | `// mem[R1+4]` | | 2. Register | `LDR R0, [R1, R2]` | `// mem[R1 + R2]` | | 3. Scaled register | `LDR R0, [R1, R2, LSL #2]` | `// mem[ R1 + 4 * R2 ]` | **三種 Addressing mode:** ![](https://i.imgur.com/L3MNhk2.png) | Pre-index | Auto-index | Post-index | |:------------------------------------:|:------------------------------------:|:------------------------------------:| | ![](https://i.imgur.com/lZXHhjG.png) | ![](https://i.imgur.com/ZK2r3OS.png) | ![](https://i.imgur.com/G1Tso16.png) | | ![](https://i.imgur.com/T5U5o1l.png) | ![](https://i.imgur.com/vMEag9g.png) | ![](https://i.imgur.com/aMs3s4L.png) | :::warning * [LDR pseudo-instruction](https://developer.arm.com/documentation/dui0204/j/arm-and-thumb-instructions/pseudo-instructions/ldr-pseudo-instruction) ::: ### Multiple register load/store ( for 32-bit arch. ) * 一次可以存取很多個暫存器的值 ```c LDMIA R0, {R1, R2, R3} // R1 := mem[R0] // R2 := mem[R0 + 4] // R3 := mem[R0 + 8] ``` | Mnemonic | Description | | suffix | meaning | |:--------:|:------------------------ | --- |:------:| ------- | | **LDM** | load multiple registers | | **IA** | increase after | | **STM** | store multiple registers | | **IB** | increase before | | | | | **DA** | decrease after | | | | | **DB** | decrease before | **Addressing mode** ![](https://i.imgur.com/7HukE3p.png) ==`!` : 代表要存取後會 register 進行更新== | `LDMIA Rn, {R1-R3}` | `LDMIB Rn, {R1-R3}` | `LDMDA Rn, {R1-R3}` | `LDMDB Rn, {R1-R3}` | |:------------------------------------:|:------------------------------------:|:------------------------------------:|:------------------------------------:| | ![](https://i.imgur.com/U4MqRU7.png) | ![](https://i.imgur.com/Rrwq0xI.png) | ![](https://i.imgur.com/dGt9Xy1.png) | ![](https://i.imgur.com/iW2Estr.png) | :::success 學組語的目的,不見得是為了改善效能,而是判斷 optimizing compiler 產生的機械碼是否正確 * gcc 和 clang/llvm 引入大量的最佳化技術,已很難光看原始程式碼,去推知最終生成的機械碼 * 從 Google 搜尋偷到的程式是否有效益 (千萬不要人云亦云,要有判斷能力) ::: ### Copy a block of memory 複製一塊 block 的 memory * R9: address of the source * R10: address of the destination * R11: end address of the source ```c loop: LDMIA R9!, {R0-R7} STMIA R10!, {R0-R7} CMP R9, R11 BNE loop ``` ![](https://i.imgur.com/uhW9tS2.png) ### Stack * full : pointing to the last used * ascending : grow towards increasing memory addresses ```c STMFD R13!, {R2-R9} // used for ATPCS … // modify R2-R9 LDMFD R13!, {R2-R9} ``` ![](https://i.imgur.com/FliYvpV.png) ![](https://i.imgur.com/CDlBxMu.png) ## Introduction to ARM Architecture > 影片 2:07:00 - [ ] [Introduction to ARM Architecture](https://docs.google.com/presentation/d/1cFBRICktpVQAOLzE5eDKD-OM4ckJuncFsn39Wg8aLZI/edit#slide=id.p14) - [ ] ==[程式練習](https://hackmd.io/s/HkcOofY2x)== > eabi : 2 進制 > hf : hard floating **Example 5** ```c int main(void) { int a, b, x, d; a = 8; b = 9; asm("mrs %[result], apsr" : [result] "=r" (x) : ); d = (a ^ b) > 0 ? add(a,b) : subtract(b,a); printf("a & b is %d\n", d); printf("Before operation, apsr was %x\n",x); asm("mrs %[result], apsr" : [result] "=r" (x) : ); printf("After operation, apsr was %x\n",x); ... ``` ![](https://i.imgur.com/ILPzrgw.png) - [ ] [Cortex --暫存器組](https://www.itread01.com/content/1546802165.html) --- **`objdump`** * 可以顯示所有的object file的資訊 * 支援格式非常多: ELF, a.out * 是 GNU Toolchain 一部分, 可以反組譯 * 使用時要指定架構,如: _arm-linux-gnueabihf-objdump_) * objdump 和 readelf 相輔相成,可以做到類似的功能,但做法不同(底層library實作不同): BFD * 可以用兩個不同工具產生的結果來互相對照 * <main> -> C語言的進入點 * 但要有一個人跳進去執行他,所以是從 ELF <start>開始在跳到 <main> => crt (C runtime) :::warning - [ ] 不知如何用 `objdump` 得到類似結果? 試了幾種參數感覺跟投影片上都有落差 * 使用 objdump -D 可以得到部份[相似內容](https://embedded2015.hackpad.com/objdump-test-5fD74TMv0Fc) * -D = disassembler * 因為可能有最佳化 * -j 看 text section ::: --- **Versatile Express board** * Versatile Express 是 ARM  提供的開發硬體(讓客戶廠商的參考硬體),價格較高(19萬~3X萬 NTD),但ARM官方正式支援,而且 QEMU 也支援 :::warning **QEMU模擬的時間不是 Cycle accurate** * Cycle accurate : Load、Store、Add、Sub 執行的Cycle數會跟理論上的不一樣 * 所以 Benchmark 會不精確,只能看到趨勢變化 * 需要真正硬體才能得知正確效能 ::: --- **ARMv8 (64-bit) 移除 LD/STR mutiple** * 有 LD/STR 跟 3 stage pipeline 變成 5 stage pipeline 有關 * 增加了 MEM、WB * 因為記憶體開銷實在太大 * 一般 LD/STR 2個 Clock cycle * LD/STR multiple 4個 Clock cycle * 一次做很多個 LD/STR * 提升程式碼密度 * 但花的時間可能差不多 * pipeline 設計的好 ,multiple LD/STR 就能發揮效益 * ARMv8 (64-bit) 移除 LD/STR mutiple,因為要考量到 Prediction,一a次失敗會浪費掉太多資源 --- * CPU modes, exception modes, processor modes 對ARM來講都是一模一樣 * 以 ARM 來說,會導致 CPU 變更 execution mode 改變,就是 exception 使然,所以等價,當然,如果我們可用同樣的術語,對簡報陳述較好 * ARM切換不同模式會 exception --- **為了降低  context switch 成本,ARM降低通用暫存器的數量** * context switch -> reserve/restore registers (GPR) * context switch 要保存暫存器 --- - [ ] [Importance of Q(Saturation Flag) in ARM](http://stackoverflow.com/questions/19557338/importance-of-qsaturation-flag-in-arm) **V 和 Q 差在哪裡??** * V – Overflow flag * Q – Sticky overflo * 簡而言之就是飽和操作如果遇到 overflow 則會讓 Q = 1 ---