【嵌入式开发】ARM 代码搬移 ( ARM 启动流程 | 代码搬移 起点 终点 | 链接地址 | 汇编代码 )
生活随笔
收集整理的這篇文章主要介紹了
【嵌入式开发】ARM 代码搬移 ( ARM 启动流程 | 代码搬移 起点 终点 | 链接地址 | 汇编代码 )
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
文章目錄
- 一. ARM 啟動(dòng)流程
- 1. 各種類型開發(fā)板啟動(dòng)流程
- ( 1 ) 2440 開發(fā)板啟動(dòng)流程簡(jiǎn)介 ( ① Nand Flash 拷貝 4 KB -> SRAM 墊腳石 | ② PC 指向 0 地址 即 SRAM 起始地址執(zhí)行 | ③ 初始化內(nèi)存 拷貝 后續(xù)指令到內(nèi)存執(zhí)行 )
- ( 2 ) 6410 開發(fā)板啟動(dòng)流程簡(jiǎn)介 ( ① 上電 運(yùn)行 SROM 的 BL0 程序 | ② NandFlash -> SRAM 拷貝 8KB 指令 | ③ 執(zhí)行 SRAM 指令 拷貝其余 BL 指令到內(nèi)存中執(zhí)行 )
- ( 3 ) 210 開發(fā)板啟動(dòng)流程簡(jiǎn)介 ( ① 上電 運(yùn)行 SROM 的 BL0 程序 | ② NandFlash -> IRAM 墊腳石 拷貝 96KB 指令 | ③ 執(zhí)行 IRAM 指令 拷貝其余 BL 指令到內(nèi)存中執(zhí)行 )
- 二. 代碼搬移 簡(jiǎn)介
- 1. 從 SRAM -> 內(nèi)存
- ( 1 ) 代碼搬移 簡(jiǎn)介 ( ① 代碼搬移起點(diǎn) | ② 從 SRAM 搬移 的 原因 | ③ 不搬移也可正常運(yùn)行 )
- ( 2 ) 代碼搬移 起點(diǎn) ( SRAM 首地址 文檔中查詢 | 6410 開發(fā)板 : 0x0C00_0000 )
- ( 3 ) 鏈接地址 簡(jiǎn)介 ( 鏈接起始地址 | 反匯編 | _start 入口函數(shù) | 指令匯編地址 )
- ( 4 ) 鏈接地址 作用 ( C 語(yǔ)言 函數(shù)調(diào)用 | 匯編 ldr 修改 PC 指針 )
- ( 5 ) 指令 跳轉(zhuǎn) ( 相對(duì)跳轉(zhuǎn) | 絕對(duì)跳轉(zhuǎn) )
- ( 6 ) 代碼搬移終點(diǎn) ( 鏈接器腳本首地址 | 6410 代碼搬移終點(diǎn)首地址 0x50008000)
- 三. 代碼搬移 匯編代碼
- 1. 匯編代碼編寫
- ( 1 ) 代碼搬移 匯編代碼
- ( 2 ) 匯編完整代碼
- ( 3 ) mem.S ( 內(nèi)存初始化 )
- ( 4 ) u-boot.lds ( 鏈接器腳本 )
- ( 5 ) Makefile ( 編譯腳本 )
- 2. 編譯輸出可執(zhí)行文件
- 3. 燒寫代碼到開發(fā)板并執(zhí)行
- ( 1 ) OK6410 開發(fā)板啟動(dòng)切換方式
- ( 2 ) 制作 SD 卡啟盤 并 準(zhǔn)備程序
- ( 3 ) SecureCRT 連接開發(fā)板并燒寫程序
本博客的參考文章及相關(guān)資料下載 :
- 1.本博客代碼及參考手冊(cè)下載 : https://download.csdn.net/download/han1202012/11183302
一. ARM 啟動(dòng)流程
ARM 開發(fā)板啟動(dòng)方式 : 可以選擇從 NorFlash , NandFlash , SD 卡 啟動(dòng) 三種方式 , 這里我們著重介紹 NandFlash 啟動(dòng)的情況 ;
1. 各種類型開發(fā)板啟動(dòng)流程
( 1 ) 2440 開發(fā)板啟動(dòng)流程簡(jiǎn)介 ( ① Nand Flash 拷貝 4 KB -> SRAM 墊腳石 | ② PC 指向 0 地址 即 SRAM 起始地址執(zhí)行 | ③ 初始化內(nèi)存 拷貝 后續(xù)指令到內(nèi)存執(zhí)行 )
2440 NandFlash 啟動(dòng) :
- 1.墊腳石 ( SRAM ) 簡(jiǎn)介 : 2440 Nand Flash 啟動(dòng) , 需要依賴于 很重要的片內(nèi)部件 -> SRAM , 這個(gè)部件 又叫 墊腳石 , 其 位于 ARM 地址空間 的 0 地址處 , 其容量大小是 4KB ;
- 2.拷貝最前面 4KB : Nand Flash -> SRAM : 處理器上電之后 , 2440 會(huì)自動(dòng) 從 Nand Flash 中拷貝 出 最前端 4 KB 的內(nèi)容 , 復(fù)制進(jìn) SRAM 墊腳石 中 ;
- 3.執(zhí)行 拷貝的 SRAM 中的 4KB 指令 : PC 指針 指向 0 地址 , 即 指向 墊腳石 SRAM 中的第一條指令 , 然后開始執(zhí)行 ;
- 4.SRAM 大小 4KB 局限性 : 這里 注意 , 只能 拷貝 4KB 的指令 , 對(duì)于小的 bootloader 程序足夠用 , 但對(duì)于 u-boot 這種重量級(jí)的程序 編譯出來(lái) 有 100多KB , 顯然 4KB 就不夠用了 ;
- 5.代碼搬移策略 : 先拷貝 4KB 到 SRAM 中 , 執(zhí)行這 4KB 指令 , 在這些指令中 , 先 把內(nèi)存初始化好 , 然后 將后續(xù)指令從 Nand Flash 拷貝到內(nèi)存中執(zhí)行 ;
( 2 ) 6410 開發(fā)板啟動(dòng)流程簡(jiǎn)介 ( ① 上電 運(yùn)行 SROM 的 BL0 程序 | ② NandFlash -> SRAM 拷貝 8KB 指令 | ③ 執(zhí)行 SRAM 指令 拷貝其余 BL 指令到內(nèi)存中執(zhí)行 )
6410 NandFlash 啟動(dòng) :
- 1.BL0 程序 : 6410 上電之后 , 首先去運(yùn)行 SROM 中的 Bootloader 0 , BL 0 是 芯片出廠就已經(jīng) 燒寫到 SROM 中的程序 ;
- 2.BL 0 的 作用 : 從 Nand Flash 中拷貝 8KB 的指令 到 SRAM ( 墊腳石 ) 中 運(yùn)行 , 顯然 8KB 無(wú)法滿足 大型 Bootloader 程序的要求 ;
- 3.拷貝代碼 : 利用 這 8KB 的程序 , 初始化內(nèi)存 , 將剩下的 Bootloader 拷貝到 內(nèi)存中 運(yùn)行 ;
( 3 ) 210 開發(fā)板啟動(dòng)流程簡(jiǎn)介 ( ① 上電 運(yùn)行 SROM 的 BL0 程序 | ② NandFlash -> IRAM 墊腳石 拷貝 96KB 指令 | ③ 執(zhí)行 IRAM 指令 拷貝其余 BL 指令到內(nèi)存中執(zhí)行 )
210 NandFlash 啟動(dòng) :
- 1.BL0 程序 : 210 上電之后 , 首先去運(yùn)行 SROM 中的 Bootloader 0 , BL 0 是 芯片出廠就已經(jīng) 燒寫到 SROM 中的程序 ;
- 2.BL 0 的 作用 : 從 Nand Flash 中拷貝 96KB 的指令 到 IRAM ( 墊腳石 ) 中 運(yùn)行 , 如果 96 KB 大小不夠用 , 就要將剩余的 BL 拷貝到內(nèi)存中 ;
二. 代碼搬移 簡(jiǎn)介
1. 從 SRAM -> 內(nèi)存
( 1 ) 代碼搬移 簡(jiǎn)介 ( ① 代碼搬移起點(diǎn) | ② 從 SRAM 搬移 的 原因 | ③ 不搬移也可正常運(yùn)行 )
代碼搬移 簡(jiǎn)介 :
- 1.代碼搬移起點(diǎn) : 代碼搬移是 將 Nand Flash 中的 BL 搬移到內(nèi)存中 , 其 起點(diǎn) 應(yīng)該是 Nand Flash , 本博客 講解的 代碼拷貝的起點(diǎn) 改成 SRAM ( 墊腳石 ) ;
- 2.從 SRAM 搬移 的 原因 : 從 Nand Flash 讀取數(shù)據(jù) , 需要對(duì) Nand Flash 進(jìn)行初始化 ; 代碼 搬移 的重要原因是 BL 大小 大于 其 墊腳石 大小 , 當(dāng)前的指令編譯完成后 僅有 不到 1KB 大小 , 其在上電后 , 會(huì)將整個(gè)的 BL 拷貝到 SRAM 墊腳石 中 ;
- 3.不搬移也可正常運(yùn)行 : 代碼在 SRAM 中 可以運(yùn)行完畢 , 不拷貝到內(nèi)存中也可以正常運(yùn)行 , 此處只是最代碼搬移進(jìn)行介紹 ;
( 2 ) 代碼搬移 起點(diǎn) ( SRAM 首地址 文檔中查詢 | 6410 開發(fā)板 : 0x0C00_0000 )
各個(gè)開發(fā)板 代碼搬移 的 起點(diǎn) :
- 1.2440 中 , SRAM ( 墊腳石 ) 起始地址 是 0x0 ;
- 2.6410 SRAM ( 墊腳石 ) 起始地址是 0x0C00_0000 ; 6410 手冊(cè) Page 116 ;
- 3.210 開發(fā)板 IRAM ( 墊腳石 ) 地址起始地址 0xD002_0000 ; 210 手冊(cè) Page 30 ;
( 3 ) 鏈接地址 簡(jiǎn)介 ( 鏈接起始地址 | 反匯編 | _start 入口函數(shù) | 指令匯編地址 )
鏈接地址 :
- 1.鏈接起始地址 : 在之前寫的 鏈接器腳本中 寫的 鏈接器 起始地址 . = 0x50008000; ;
- 2.反匯編程序 : 對(duì)程序進(jìn)行反匯編 , 在代碼編譯目錄中 , 執(zhí)行 arm-linux-objdump -D -S u-boot.elf > dump 命令 , 將反匯編內(nèi)容輸出到 dump 文件中 ( 前提是 有 編譯好的 可執(zhí)行 文件 ) ;
- 3.查看匯編文件 : 打開 匯編 文件 ;
- ① _start 入口函數(shù) : 匯編代碼的 入口 是 _start 標(biāo)號(hào) , 查看反匯編之后的代碼 , 可以看到 在 _start 標(biāo)號(hào)前 看到地址 0x50008000 , 該地址 是 整個(gè)程序的起始地址 , 即 SRAM 的起始地址 ;
- ② 每行指令都有相應(yīng)地址 : 每行代碼都有一個(gè)鏈接地址 , 可以看到 反匯編 文件中 每行 前面都有一個(gè) 鏈接地址 ;
( 4 ) 鏈接地址 作用 ( C 語(yǔ)言 函數(shù)調(diào)用 | 匯編 ldr 修改 PC 指針 )
鏈接地址 作用 :
- 1.C 語(yǔ)言程序 : 調(diào)用 reset() 函數(shù) , 調(diào)用之后 , PC 指針 會(huì)被 重新賦值 , 去執(zhí)行 reset() 函數(shù) , 這個(gè) PC 指針 被賦予的 值 就是 reset 標(biāo)號(hào) 前 的 鏈接地址 ; 如 PC 指針 被賦值成 0x50008058 , 該地址就是 reset() 函數(shù)的鏈接地址 ;
- 2.匯編指令 : 使用 ldr 偽指令 修改 PC 指針 , 如 ldr PC , reset , 讓 PC 指針 執(zhí)行 reset 函數(shù) , 此時(shí) PC 指針會(huì)被賦值成 0x50008058 地址 ;
( 5 ) 指令 跳轉(zhuǎn) ( 相對(duì)跳轉(zhuǎn) | 絕對(duì)跳轉(zhuǎn) )
指令 跳轉(zhuǎn) :
- 1.PC 指針跳轉(zhuǎn) : 開發(fā)板上電后 , PC 指針 首先 指向 0 , 但是 匯編程序的入口 _start 標(biāo)號(hào)的 地址 是 0x50008058 , PC 指針 被賦值為 0x50008058 ;
- 2.相對(duì)跳轉(zhuǎn) :
- ① 相對(duì)跳轉(zhuǎn)指令 : 使用 b , bl 等指令 產(chǎn)生的跳轉(zhuǎn) , 就是 相對(duì)跳轉(zhuǎn) ;
- ② 相對(duì)跳轉(zhuǎn)過(guò)程 : 在跳轉(zhuǎn)過(guò)程中 , 不是將對(duì)應(yīng) 標(biāo)號(hào)的 鏈接地址 , 直接賦值給 PC 指針 , 而是采用 跳轉(zhuǎn)前的 PC 指針值 + 當(dāng)前指針 與 要跳轉(zhuǎn)的標(biāo)號(hào) 位置之間的 差值 ;
- ③ 相對(duì)跳轉(zhuǎn)舉例 : 如 PC 指針在入口處 _start 地址為 0x50008000 , 如果要執(zhí)行 reset 標(biāo)號(hào)處的代碼 , 需要 跳轉(zhuǎn)到 0x50008058 中 , PC 指針 從 0x50008000 跳轉(zhuǎn)到 0x50008058 中 , 這里只需要 相對(duì)跳轉(zhuǎn) 0x58 地址增量 即可 ;
- 3.絕對(duì)跳轉(zhuǎn) : C 語(yǔ)言中 調(diào)用函數(shù) , 或者 修改 PC 指針值 , 的情況 是 絕對(duì)跳轉(zhuǎn) ;
( 6 ) 代碼搬移終點(diǎn) ( 鏈接器腳本首地址 | 6410 代碼搬移終點(diǎn)首地址 0x50008000)
代碼搬移 終點(diǎn) :
- 1.內(nèi)存首地址 : 鏈接起始地址 決定了 程序第一行代碼的鏈接地址 , 即 第一行代碼 在 內(nèi)存中出現(xiàn)的位置 , 如 6410 的第一行代碼 的 內(nèi)存 地址是 0x50008000 ;
- 2.拷貝終點(diǎn) : 代碼 從 SRAM 拷貝到 內(nèi)存中 , 這個(gè)內(nèi)存的位置 0x50008000 就是 第一行 代碼 被拷貝到的位置 ;
- 3.拷貝過(guò)程 : 代碼拷貝的時(shí)候 , 需要從 代碼的起始地址開始拷貝 , 之后的代碼 以此類推 , 拷貝到后續(xù)指定標(biāo)號(hào)地址處 , 都要拷貝到對(duì)應(yīng)的位置中 ;
- 4.6410開發(fā)板 拷貝 終點(diǎn) : 0x50008000 是 6410 開發(fā)板 代碼拷貝終點(diǎn) 的 第一行 指令的地址 ;
三. 代碼搬移 匯編代碼
1. 匯編代碼編寫
( 1 ) 代碼搬移 匯編代碼
匯編代碼 :
copy_to_ram:ldr r0, =0x0c000000 @ 設(shè)置 代碼搬移 起始地址 首地址 , 即 SRAM 墊腳石的 首地址 , 將改地址存放在 r0 寄存器中l(wèi)dr r1, =0x50008000 @ 設(shè)置 代碼搬移 終點(diǎn) 首地址 , 即 內(nèi)存的首地址 , 將該地址存放在 r1 寄存器中add r3, r0, #1024*4 @ 設(shè)置 復(fù)制多少指令到 內(nèi)存中 , 這里復(fù)制 4KB 數(shù)據(jù) 從 SRAM 到 內(nèi)存 中 ; copy_loop: @ 循環(huán)復(fù)制代碼ldr r2, [r0], #4 @ 取出 r0 寄存器的地址 ( 即 SRAM 中的地址 ) 中的數(shù)據(jù) 放入 r2 寄存器 , 讀取完畢后 , r0 中的地址 累加 4str r2, [r1], #4 @ 將 r2 中的內(nèi)容 , 寫入到 r1 寄存器對(duì)應(yīng)的地址中 , 寫出完畢后 , r1 中的地址 累加 4 cmp r0, r3 @ 查看 r0 地址 增量 是否 增加了 4KB , 到了代碼搬移的末尾bne copy_loop @ 如果 r0 r3 地址不一致 , 說(shuō)明還沒(méi)拷貝完畢 , 繼續(xù) 跳轉(zhuǎn)回 copy_loop 標(biāo)號(hào) 拷貝mov pc, lr( 2 ) 匯編完整代碼
@**************************** @File:start.S @ @BootLoader 初始化代碼 @**************************** .text @ 宏 指明代碼段 .global _start @ 偽指令聲明全局開始符號(hào) _start: @ 程序入口標(biāo)志 b reset @ reset 復(fù)位異常 ldr pc, _undefined_instruction @ 未定義異常, 將 _undefined_instruction 值裝載到 pc 指針中 ldr pc, _software_interrupt @ 軟中斷異常 ldr pc, _prefetch_abort @ 預(yù)取指令異常 ldr pc, _data_abort @ 數(shù)據(jù)讀取異常 ldr pc, _not_used @ 占用 0x00000014 地址 ldr pc, _irq @ 普通中斷異常 ldr pc, _fiq @ 軟中斷異常 _undefined_instruction: .word undefined_instruction @ _undefined_instruction 標(biāo)號(hào)存放了一個(gè)值, 該值是 32 位地址 undefined_instruction, undefined_instruction 是一個(gè)地址 _software_interrupt: .word software_interrupt @ 軟中斷異常 _prefetch_abort: .word prefetch_abort @ 預(yù)取指令異常 處理 _data_abort: .word data_abort @ 數(shù)據(jù)讀取異常 _not_used: .word not_used @ 空位處理 _irq: .word irq @ 普通中斷處理 _fiq: .word fiq @ 快速中斷處理 undefined_instruction: @ undefined_instruction 地址存放要執(zhí)行的內(nèi)容 nop software_interrupt: @ software_interrupt 地址存放要執(zhí)行的內(nèi)容 nop prefetch_abort: @ prefetch_abort 地址存放要執(zhí)行的內(nèi)容 nop data_abort: @ data_abort 地址存放要執(zhí)行的內(nèi)容 nop not_used: @ not_used 地址存放要執(zhí)行的內(nèi)容 nop irq: @ irq 地址存放要執(zhí)行的內(nèi)容 nop fiq: @ fiq 地址存放要執(zhí)行的內(nèi)容 nop reset: @ reset 地址存放要執(zhí)行的內(nèi)容 bl set_svc @ 跳轉(zhuǎn)到 set_svc 標(biāo)號(hào)處執(zhí)行bl set_serial_port @ 設(shè)置外設(shè)基地址端口初始化bl disable_watchdog @ 跳轉(zhuǎn)到 disable_watchdog 標(biāo)號(hào)執(zhí)行, 關(guān)閉看門狗bl disable_interrupt @ 跳轉(zhuǎn)到 disable_interrupt 標(biāo)號(hào)執(zhí)行, 關(guān)閉中斷bl disable_mmu @ 跳轉(zhuǎn)到 disable_mmu 標(biāo)號(hào)執(zhí)行, 關(guān)閉 MMU bl init_clock @ 跳轉(zhuǎn)到 init_clock 標(biāo)號(hào), 執(zhí)行時(shí)鐘初始化操作bl mem_init @ 跳轉(zhuǎn)到 mem_init 標(biāo)號(hào) , 執(zhí)行內(nèi)存初始化操作 , 該段代碼定義在 mem.S 文件中bl light_led @ 打開開發(fā)板上的 LED 發(fā)光二極管bl copy_to_ram @ 代碼搬移 , 從 SRAM 到 內(nèi)存中set_svc:mrs r0, cpsr @ 將 CPSR 寄存器中的值 導(dǎo)出到 R0 寄存器中bic r0, r0, #0x1f @ 將 R0 寄存器中的值 與 #0x1f 立即數(shù) 進(jìn)行與操作, 并將結(jié)果保存到 R0 寄存器中, 實(shí)際是將寄存器的 0 ~ 4 位 置 0orr r0, r0, #0xd3 @ 將 R0 寄存器中的值 與 #0xd3 立即數(shù) 進(jìn)行或操作, 并將結(jié)果保存到 R0 寄存器中, 實(shí)際是設(shè)置 0 ~ 4 位 寄存器值 的處理器工作模式代碼msr cpsr, r0 @ 將 R0 寄存器中的值 保存到 CPSR 寄存器中mov pc, lr @ 返回到 返回點(diǎn)處 繼續(xù)執(zhí)行后面的代碼#define pWTCON 0x7e004000 @ 定義看門狗控制寄存器 地址 ( 6410開發(fā)板 ) disable_watchdog: ldr r0, =pWTCON @ 先將控制寄存器地址保存到通用寄存器中mov r1, #0x0 @ 準(zhǔn)備一個(gè) 0 值, 看門狗控制寄存器都設(shè)置為0 , 即看門狗也關(guān)閉了str r1, [r0] @ 將 0 值 設(shè)置到 看門狗控制寄存器中 mov pc, lr @ 返回到 返回點(diǎn)處 繼續(xù)執(zhí)行后面的代碼disable_interrupt:mvn r1,#0x0 @ 將 0x0 按位取反, 獲取 全 1 的數(shù)據(jù), 設(shè)置到 R1 寄存器中l(wèi)dr r0,=0x71200014 @ 設(shè)置第一個(gè)中斷屏蔽寄存器, 先將 寄存器 地址裝載到 通用寄存器 R0 中 str r1,[r0] @ 再將 全 1 的值設(shè)置到 寄存器中, 該寄存器的內(nèi)存地址已經(jīng)裝載到了 R0 通用寄存器中l(wèi)dr r0,=0x71300014 @ 設(shè)置第二個(gè)中斷屏蔽寄存器, 先將 寄存器 地址裝載到 通用寄存器 R0 中 str r1,[r0] @ 再將 全 1 的值設(shè)置到 寄存器中, 該寄存器的內(nèi)存地址已經(jīng)裝載到了 R0 通用寄存器中mov pc, lr @ 返回到 返回點(diǎn)處 繼續(xù)執(zhí)行后面的代碼disable_mmu : mcr p15,0,r0,c7,c7,0 @ 設(shè)置 I-Cache 和 D-Cache 失效mrc p15,0,r0,c1,c0,0 @ 將 c1 寄存器中的值 讀取到 R0 通用寄存器中bic r0, r0, #0x00000007 @ 使用 bic 位清除指令, 將 R0 寄存器中的 第 0, 1, 2 三位 設(shè)置成0, 代表 關(guān)閉 MMU 和 D-Cachemcr p15,0,r0,c1,c0,0 @ 將 R0 寄存器中的值寫回到 C1 寄存器中mov pc, lr @ 返回到 返回點(diǎn)處 繼續(xù)執(zhí)行后面的代碼set_serial_port : ldr r0, =0x70000000 @ 將基地址裝載到 r0 寄存器中, 該基地址 在 arm 核 手冊(cè)中定義orr r0, r0, #0x13 @ 設(shè)置初始化基地址的范圍, 將 r0 中的值 與 0x13 立即數(shù) 進(jìn)行或操作, 將結(jié)果存放到 r0 中mcr p15, 0, r0, c15, c2, 4 @ 將 r0 中的值設(shè)置給 c15 協(xié)處理器 mov pc, lr#define CLK_DIV0 0x7E00F020 @ 定義 CLK_DIV0 寄存器地址, 時(shí)鐘的分頻參數(shù)都是通過(guò)該寄存器進(jìn)行設(shè)置的 #define OTHERS 0x7E00F900 @ 定義 OTHERS 寄存器地址, 用于設(shè)置 CPU 異步工作模式 #define CLK_VAL ( (0x0 << 0) | (0x1 << 9) | (0x1 << 8) | (0x3 << 12) ) @ 設(shè)置 CLK_DIV0 寄存器的值, 即 各個(gè)時(shí)鐘分頻器的參數(shù) #define MPLL_CON 0x7E00F010 @ 定義 MPLL_CON 寄存器地址常量 #define APLL_CON 0x7E00F00C @ 定義 APLL_CON 寄存器地址常量 #define PLL_VAL ( (0x1 << 31) | (266 << 16) | (3 << 8) | (1 << 0) ) @ 設(shè)置 PLL 控制寄存器的值 #define CLK_SRC 0x7E00F01C @ 定義 CLK_SRC 時(shí)鐘源控制寄存器的地址常量 init_clock : ldr r0, =CLK_DIV0 @ 將 CLK_DIV0 的地址裝載到 r0 通用寄存器中l(wèi)dr r1, =CLK_VAL @ 將 要設(shè)置給 CLK_DIV0 寄存器的值 CLK_VAL 立即數(shù) 裝載到 r1 通用寄存器中; str r1, [r0] @ 將 r1 寄存器中的內(nèi)容 存儲(chǔ)到 r0 存儲(chǔ)的地址 指向的內(nèi)存中l(wèi)dr r0, =OTHERS @ 將 OTHERS 寄存器地址存到 r0 通用寄存器中l(wèi)dr r1, [r0] @ 將 r0 寄存器存儲(chǔ)的地址指向的寄存器中的值讀取到 r1 通用寄存器中bic r1, r1, #0xc0 @ 將 r1 寄存器中的值的 第 6 位 和 第 7 位 設(shè)置成 0str r1, [r0] @ 將 r1 寄存器中的值 寫出到 r0 寄存器存儲(chǔ)的地址指向的內(nèi)存位置 即 OTHERS 寄存器ldr r0, =APLL_CON @ 將 APLL_CON 寄存器地址存到 r0 通用寄存器中l(wèi)dr r1, =PLL_VAL @ 將 要設(shè)置給 APLL_CON 寄存器的值 PLL_VAL 立即數(shù) 裝載到 r1 通用寄存器中;str r1, [r0] @ 將 r1 寄存器中的內(nèi)容 存儲(chǔ)到 r0 存儲(chǔ)的地址 指向的內(nèi)存中, 即 將 PLL_VAL 的值 設(shè)置到 APLL_CON 寄存器中l(wèi)dr r0, =MPLL_CON @ 將 MPLL_CON 寄存器地址存到 r0 通用寄存器中l(wèi)dr r1, =PLL_VAL @ 將 要設(shè)置給 MPLL_CON 寄存器的值 PLL_VAL 立即數(shù) 裝載到 r1 通用寄存器中;str r1, [r0] @ 將 r1 寄存器中的內(nèi)容 存儲(chǔ)到 r0 存儲(chǔ)的地址 指向的內(nèi)存中, 即 將 PLL_VAL 的值 設(shè)置到 MPLL_CON 寄存器中l(wèi)dr r0, =CLK_SRC @ 將 CLK_SRC 寄存器地址設(shè)置到 r0 通用寄存器中mov r1, #0x3 @ 將 0x3 立即數(shù)設(shè)置給 r1 寄存器str r1, [r0] @ 將 r1 中存儲(chǔ)的立即數(shù)設(shè)置給 r0 寄存器存儲(chǔ)的地址指向的內(nèi)存中, 即 CLK_SRC 寄存器中mov pc, lrcopy_to_ram:ldr r0, =0x0c000000 @ 設(shè)置 代碼搬移 起始地址 首地址 , 即 SRAM 墊腳石的 首地址 , 將改地址存放在 r0 寄存器中l(wèi)dr r1, =0x50008000 @ 設(shè)置 代碼搬移 終點(diǎn) 首地址 , 即 內(nèi)存的首地址 , 將該地址存放在 r1 寄存器中add r3, r0, #1024*4 @ 設(shè)置 復(fù)制多少指令到 內(nèi)存中 , 這里復(fù)制 4KB 數(shù)據(jù) 從 SRAM 到 內(nèi)存 中 ; copy_loop: @ 循環(huán)復(fù)制代碼ldr r2, [r0], #4 @ 取出 r0 寄存器的地址 ( 即 SRAM 中的地址 ) 中的數(shù)據(jù) 放入 r2 寄存器 , 讀取完畢后 , r0 中的地址 累加 4str r2, [r1], #4 @ 將 r2 中的內(nèi)容 , 寫入到 r1 寄存器對(duì)應(yīng)的地址中 , 寫出完畢后 , r1 中的地址 累加 4 cmp r0, r3 @ 查看 r0 地址 增量 是否 增加了 4KB , 到了代碼搬移的末尾bne copy_loop @ 如果 r0 r3 地址不一致 , 說(shuō)明還沒(méi)拷貝完畢 , 繼續(xù) 跳轉(zhuǎn)回 copy_loop 標(biāo)號(hào) 拷貝mov pc, lr#define GPBCON 0x7F008820 #define GPBDAT 0x7F008824 light_led : ldr r0, =GPBCON @ 將 0x7F008820 GPM 控制寄存器的地址 0x7F008820 裝載到 r0 寄存器中l(wèi)dr r1, =0x1111 @ 設(shè)置 GPM 控制寄存器的行為 為 Output 輸出, 即每個(gè)對(duì)應(yīng)引腳的設(shè)置為 0b0001 值str r1, [r0] @ 將 r1 中的值 存儲(chǔ)到 r0 指向的 GPBCON 0x7F008820 地址的內(nèi)存中l(wèi)dr r0, =GPBDAT @ 將 GPBDAT 0x7F008824 地址值 裝載到 r0 寄存器中l(wèi)dr r1, =0b110101 @ 計(jì)算 GPM 數(shù)據(jù)寄存器中的值, 設(shè)置 0 為 低電平, 設(shè)置 1 為高電平, 這里設(shè)置 0 ~ 3 位為低電平, 其它為高電平str r1, [r0] @ 將 r1 中的值 存儲(chǔ)到 r0 指向的 GPBDAT 0x7F008824 地址的內(nèi)存中mov pc, lr
( 3 ) mem.S ( 內(nèi)存初始化 )
@**************************** @File:mem.S @ @內(nèi)存 初始化代碼 @**************************** .text @ 宏 指明代碼段 .global mem_init @ 偽指令 mem.S 可以理解成一個(gè)函數(shù) , 該函數(shù)由 start.S 進(jìn)行調(diào)用 , 它必須是一個(gè)全局的 mem_init: @ 定義內(nèi)存初始化的標(biāo)號(hào) , 在 start.S 中進(jìn)行調(diào)用@ 設(shè)置 MEM_SYS_CFG 寄存器中的 [ 7 ] 位 , 設(shè)置 XmlDATA [31 : 16] pin 腳作用@ 這些 pin 腳 用于作為 內(nèi)存輸出的 數(shù)據(jù)線 的@ 如果 該位 為 0 , 那么 就作為 [ 31 : 16 ] 位的數(shù)據(jù)線引腳 , 這里設(shè)置為 0 即可ldr r0, =0x7e00f120 mov r1, #0x0str r1, [r0]@ 步驟一 : DRAM 控制器進(jìn)入配置狀態(tài)ldr r0, =0x7e001004 @ 將 DRAM CONTROLLER COMMAND REGISTER 寄存器地址裝在到 r0 中mov r1, #0x4 @ 設(shè)置 DRAM 控制命令寄存器 ( DRAM CONTROLLER COMMAND REGISTER ) 值 0x4 進(jìn)入配置 ( Config ) 狀態(tài)str r1, [r0] @ 將 r1 裝載到 r0 所指向的內(nèi)存地址對(duì)應(yīng)的空間中@ 步驟二 : 設(shè)置一系列寄存器ldr r0, =0x7e001010 @刷新寄存器地址ldr r1, =( ( 7800 / ( 1000000000/133000000 ) + 1 ) ) @設(shè)置刷新時(shí)間str r1, [r0]ldr r0, =0x7e001014 @CAS latency寄存器mov r1, #(3 << 1)str r1, [r0]ldr r0, =0x7e001018 @t_DQSS寄存器mov r1, #0x1str r1, [r0]ldr r0, =0x7e00101c @T_MRD寄存器mov r1, #0x2str r1, [r0]ldr r0, =0x7e001020 @t_RAS寄存器ldr r1, =( ( 45 / ( 1000000000 / 133000000 ) + 1 ) )str r1, [r0]ldr r0, =0x7e001024 @t_RC寄存器ldr r1, =( ( 68 / ( 1000000000 / 133000000 ) + 1 ) )str r1, [r0]ldr r0, =0x7e001028 @t_RCD寄存器ldr r1, =( ( 23 / ( 1000000000 / 133000000 ) + 1 ) )str r1, [r0]ldr r0, =0x7e00102c @t_RFC寄存器ldr r1, =( ( 80 / ( 1000000000 / 133000000 ) + 1 ) )str r1, [r0]ldr r0, =0x7e001030 @t_RP寄存器ldr r1, =( ( 23 / ( 1000000000 / 133000000 ) + 1 ) )str r1, [r0]ldr r0, =0x7e001034 @t_rrd寄存器ldr r1, =( ( 15 / ( 1000000000 / 133000000 ) + 1 ) )str r1, [r0]ldr r0, =0x7e001038 @t_wr寄存器ldr r1, =( ( 15 / ( 1000000000 / 133000000 ) + 1 ) )@ ldr r2, [r0]str r1, [r0]ldr r0, =0x7e00103c @t_wtr寄存器mov r1, #0x07str r1, [r0]ldr r0, =0x7e001040 @t_xp寄存器mov r1, #0x02str r1, [r0]ldr r0, =0x7e001044 @t_xsr寄存器ldr r1, =( ( 120 / ( 1000000000 / 133000000 ) + 1 ) )str r1, [r0]ldr r0, =0x7e001048 @t_esr寄存器ldr r1, =( ( 120 / ( 1000000000 / 133000000 ) + 1 ) )str r1, [r0]ldr r0, =0x7e00100c @內(nèi)存控制配置寄存器ldr r1, =0x00010012 @配置控制器str r1, [r0]ldr r0, =0x7e00104c @32位DRAM配置控制寄存器ldr r1, =0x0b45str r1, [r0]ldr r0, =0x7e001200 @片選寄存器ldr r1, =0x150f8str r1, [r0]ldr r0, =0x7e001304 @用戶配置寄存器mov r1, #0x0str r1, [r0] @ 步驟三 : 可以不執(zhí)行 , 等待 電壓 和 時(shí)鐘穩(wěn)定下來(lái) , 但是電壓和時(shí)鐘本來(lái)就是穩(wěn)定的@ 步驟四 : 內(nèi)存初始化@ 步驟四 : 內(nèi)存初始化 1. 寫入 NOP 命令 : @ 執(zhí)行過(guò)程 : 向 Direct Command Register 的 Memory Command [19:18] 域中寫入 0b11@ 整體值為 0b 11 00 0000 0000 0000 0000 , 轉(zhuǎn)為 16 進(jìn)制為 0xC0000ldr r0, =0x7e001008 @ 裝載 DIRECT COMMAND REGISTER 寄存器 地址到 r0 寄存器中l(wèi)dr r1, =0xc0000 @ 裝載 要寫入的值 到 r1 寄存器中str r1, [r0] @ 將 r1 裝載到 r0 所指向的內(nèi)存地址對(duì)應(yīng)的空間中@ 步驟四 : 內(nèi)存初始化 2. 寫入 Precharge All 命令 : @ 執(zhí)行過(guò)程 : 向 Direct Command Register 的 Memory Command [19:18] 域中寫入 0b00@ 整體值為 0b 00 00 0000 0000 0000 0000 , 轉(zhuǎn)為 16 進(jìn)制為 0x0ldr r0, =0x7e001008 @ 裝載 DIRECT COMMAND REGISTER 寄存器 地址到 r0 寄存器中l(wèi)dr r1, =0x0 @ 裝載 要寫入的值 到 r1 寄存器中str r1, [r0] @ 將 r1 裝載到 r0 所指向的內(nèi)存地址對(duì)應(yīng)的空間中@ 步驟四 : 內(nèi)存初始化 3 , 4 . 寫入 Autorefresh 命令 : 該步驟執(zhí)行兩次@ 執(zhí)行過(guò)程 : 向 Direct Command Register 的 Memory Command [19:18] 域中寫入 0b01@ 整體值為 0b 01 00 0000 0000 0000 0000 , 轉(zhuǎn)為 16 進(jìn)制為 0x40000ldr r0, =0x7e001008 @ 裝載 DIRECT COMMAND REGISTER 寄存器 地址到 r0 寄存器中l(wèi)dr r1, =0x40000 @ 裝載 要寫入的值 到 r1 寄存器中str r1, [r0] @ 將 r1 裝載到 r0 所指向的內(nèi)存地址對(duì)應(yīng)的空間中 ldr r0, =0x7e001008 @ 裝載 DIRECT COMMAND REGISTER 寄存器 地址到 r0 寄存器中l(wèi)dr r1, =0x40000 @ 裝載 要寫入的值 到 r1 寄存器中str r1, [r0] @ 將 r1 裝載到 r0 所指向的內(nèi)存地址對(duì)應(yīng)的空間中 @ 步驟四 : 內(nèi)存初始化 5. 寫入 MRS 命令 : ( EMRS )@ 執(zhí)行過(guò)程 : 向 Direct Command Register 的 Memory Command [19:18] 域中寫入 0b10@ 同時(shí)還需要設(shè)置 Bank Address , 該步驟設(shè)置的是 EMRS 的 Bank Address@ 整體值 轉(zhuǎn)為 16 進(jìn)制為 0xa0000ldr r0, =0x7e001008 @ 裝載 DIRECT COMMAND REGISTER 寄存器 地址到 r0 寄存器中l(wèi)dr r1, =0xa0000 @ 裝載 要寫入的值 到 r1 寄存器中str r1, [r0] @ 將 r1 裝載到 r0 所指向的內(nèi)存地址對(duì)應(yīng)的空間中 @ 步驟四 : 內(nèi)存初始化 6. 寫入 MRS 命令 : ( MRS )@ 執(zhí)行過(guò)程 : 向 Direct Command Register 的 Memory Command [19:18] 域中寫入 0b10, @ 同時(shí)還需要設(shè)置 Bank Address , 該步驟設(shè)置的是 MRS 的 Bank Address@ 整體值 轉(zhuǎn)為 16 進(jìn)制為 0x80032ldr r0, =0x7e001008 @ 裝載 DIRECT COMMAND REGISTER 寄存器 地址到 r0 寄存器中l(wèi)dr r1, =0x80032 @ 裝載 要寫入的值 到 r1 寄存器中str r1, [r0] @ 將 r1 裝載到 r0 所指向的內(nèi)存地址對(duì)應(yīng)的空間中 @ 步驟五 : DRAM 控制器進(jìn)入 Ready 狀態(tài)ldr r0, =0x7e001004 @ 將 DRAM CONTROLLER COMMAND REGISTER 寄存器地址裝在到 r0 中mov r1, #0x0 @ 設(shè)置 DRAM 控制命令寄存器 ( DRAM CONTROLLER COMMAND REGISTER ) 值 0x0 進(jìn)入配置 ( Ready ) 狀態(tài)str r1, [r0] @ 將 r1 裝載到 r0 所指向的內(nèi)存地址對(duì)應(yīng)的空間中@ 步驟六 : 檢查 DRAM 控制器 是否 進(jìn)入 Ready 狀態(tài)check_ready:ldr r0, =0x7e001000 @ 將 DRAM CONTROLLER STATUS REGISTER 地址 裝載到 r0 寄存器中l(wèi)dr r1, [r0] @ 將 r0 寄存器存儲(chǔ)的地址對(duì)應(yīng)的內(nèi)存中的內(nèi)容裝載到 r1 寄存器中 , 這個(gè) DRAM CONTROLLER STATUS REGISTER 寄存器的值就獲取到了mov r2, #0x3 @ 將 立即數(shù) 3 設(shè)置給 r2 寄存器中, 用于 與操作 , 獲取最后的 兩位 二進(jìn)制數(shù)值and r1, r1, r2 @ 將 r1 ( 第二個(gè) ) 與 r2 進(jìn)行 與 操作 , 將結(jié)果放入 r1 ( 第一個(gè) ) 寄存器中cmp r1, #0x1 @ 將 與 結(jié)果 與 0x1 進(jìn)行比較 , 如果相等 繼續(xù)執(zhí)行 , 如果不相等, 跳轉(zhuǎn)到 check_ready 繼續(xù)執(zhí)行判定操作bne check_ready @ 如果不相等, 跳轉(zhuǎn)到 check_ready 繼續(xù)執(zhí)行判定操作nopmov pc, lr
( 4 ) u-boot.lds ( 鏈接器腳本 )
OUTPUT_ARCH(arm) ENTRY(_start) SECTIONS {. = 0x50008000;. = ALIGN(4);.text :{start.o (.text)*(.text)}. = ALIGN(4);.data : {*(.data)}. = ALIGN(4);bss_start = .;.bss : {*(.bss) }bss_end = .; }
( 5 ) Makefile ( 編譯腳本 )
all: start.o mem.oarm-linux-ld -Tu-boot.lds -o u-boot.elf $^ arm-linux-objcopy -O binary u-boot.elf u-boot.bin %.o : %.Sarm-linux-gcc -g -c $^%.o : %.carm-linux-gcc -g -c $^.PHONY: clean clean:rm *.o *.elf *.bin
2. 編譯輸出可執(zhí)行文件
編譯過(guò)程 :
- 1.文件準(zhǔn)備 : 將 匯編代碼 ( start.S ) 鏈接器腳本 ( gboot.lds ) makefile 文件 拷貝到編譯目錄 ;
- 2.執(zhí)行編譯命令 : make ;
- 3.編譯結(jié)果 : 可以看到 生成了 編譯目標(biāo)文件 start.o, 鏈接文件 u-boot.elf, 可執(zhí)行的二進(jìn)制文件 u-boot.bin ;
3. 燒寫代碼到開發(fā)板并執(zhí)行
( 1 ) OK6410 開發(fā)板啟動(dòng)切換方式
OK6410 開發(fā)板啟動(dòng)切換方式 : 通過(guò)控制 開發(fā)板右側(cè)的 8個(gè)開關(guān)來(lái)設(shè)置啟動(dòng)來(lái)源;
- 1.sd 卡啟動(dòng) : (1~8) 位置 : 0, 0, 0, 1, 1, 1, 1, 1;
- 2.nand flash 啟動(dòng) : (1~8) 位置 : x, x, x, 1, 1, 0, 0, 1;
- 3.nor flash 啟動(dòng) : (1~8) 位置 : x, x, x, 1, 0, 1, 0, x;
( 2 ) 制作 SD 卡啟盤 并 準(zhǔn)備程序
制作 SD 卡啟動(dòng)盤 :
- 1.找到開發(fā)板的燒寫工具 : OK6410-A 開發(fā)板的燒寫工具 在開發(fā)光盤 A 的 OK6410-A-1G用戶光盤(A)-20160812\Linux-3.0.1\Linux燒寫工具 目錄下, 開發(fā)板光盤資料下載地址 ;
- 2.設(shè)置 SD_Writer.exe 屬性 ( win10系統(tǒng)需要進(jìn)行的設(shè)置 ) : 右鍵點(diǎn)擊屬性, 在兼容性一欄, 設(shè)置 以 Windows 7 兼容模式運(yùn)行, 并設(shè)置 以管理員身份運(yùn)行此程序 ; 注意 一定要 使用管理員身份 運(yùn)行 , 否則報(bào)錯(cuò) , 報(bào)錯(cuò)信息 Select Volume Error , 無(wú)法格式化SD卡 , 無(wú)法燒寫 程序 ;
- 3.先格式化 SD 卡 : 注意這里要使用 SD_Writer 中的 format 功能進(jìn)行格式化 , 按照下面的步驟, 一步一步點(diǎn)擊確定執(zhí)行 ;
- 4.選擇要燒寫的文件 : 這里選擇 OK6410-A-1G用戶光盤(A)-20160812\Linux-3.0.1\Linux燒寫工具\(yùn)mmc_ram256.bin 文件;
- 5.燒寫文件到 SD 卡中 : 直接點(diǎn)擊 Program 按鈕, 就將啟動(dòng)程序燒寫到了 SD 卡中;
- 6.準(zhǔn)備 LED 燈程序 : 將編譯出的 gboot.bin 文件名修改成 u-boot.bin, 必須修改成該文件名, 否則無(wú)法燒寫上去;
- 7.將程序拷貝到 SD 卡中 : 將程序直接拷貝到 SD 卡中即可;
參考資料 : OK6410燒寫裸板程序方法
這是之前寫過(guò)的博客, 僅作為參考;
( 3 ) SecureCRT 連接開發(fā)板并燒寫程序
SecureCRT 連接開發(fā)板并燒寫程序 步驟 :
- 1.硬件連接操作 : 使用 USB 轉(zhuǎn) 串口工具 將電腦 與 開發(fā)板鏈接, USB 插在電腦端, 串口端插在 開發(fā)板上, 插上電源適配器, 但是不要打開電源開關(guān);
- 2.開發(fā)板設(shè)置 : 將開發(fā)板右側(cè)的開關(guān)設(shè)置成 SD 卡啟動(dòng), 即 (1~8) 位置 : 0, 0, 0, 1, 1, 1, 1, 1; 該步驟很重要;
- 2.查詢串口端口號(hào) : 在設(shè)備管理器中查看串口端口號(hào), 這里可以看到是 COM9;
- 3.SecureCRT 連接串口 : 打開 SecureCRT 軟件, 點(diǎn)擊快速連接, 然后在彈出的對(duì)話框中按照下面進(jìn)行配置, ① 首先要選擇 Serial 協(xié)議, ② 然后選擇端口, 這個(gè)端口從設(shè)備管理器中查看, ③ 波特率選擇 115200, ④ 取消 RTS/CTS 選項(xiàng);
- 4.打開開發(fā)板 ( 很重要 ) : 選中 SecureCRT 軟件, 然后按住空格鍵不放, 這個(gè)操作很重要, 打開開發(fā)板開關(guān), ① 先按住空格鍵, ②再打開開關(guān);
- 5.首先格式化 Nand Flash : 選擇 [1] 選項(xiàng), 格式化 Nand Flash;
- 6.選擇從 SD 卡中燒寫 : 選擇 [2] Burn image from SD card 選項(xiàng), 從 SD 卡中向開發(fā)板燒寫程序;
- 7.選擇燒寫 u-boot : 選擇 [2] Flash u-boot, 燒寫 u-boot, 會(huì)從 SD 卡中查找 u-boot.bin 文件, 然后燒寫到 nand flash 中, 如果 SD 卡中 沒(méi)有 u-boot.bin 會(huì)報(bào)錯(cuò);
- 8.設(shè)置從 Nand Flash 啟動(dòng) : 設(shè)置開發(fā)板上的啟動(dòng)開關(guān), (1~8) 位置 : x, x, x, 1, 1, 0, 0, 1; 此時(shí) 四個(gè) LED 全亮;
- 9.效果展示 : 設(shè)置的 GPBDAT 寄存器值為 0b110000, 四個(gè) LED 燈都亮起來(lái);
- 10.修改 LED 燈顯示參數(shù)后顯示結(jié)果 : 設(shè)置 GPBDAT 寄存器中值為 0b110101 是 第一個(gè) 和 第三個(gè) LED 亮起來(lái);
總結(jié)
以上是生活随笔為你收集整理的【嵌入式开发】ARM 代码搬移 ( ARM 启动流程 | 代码搬移 起点 终点 | 链接地址 | 汇编代码 )的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 【音频处理】Polyphone 样本编辑
- 下一篇: 【组合数学】生成函数 简要介绍 ( 生