mini2440---start.S注釋
#include <common.h>
#include <config.h>
.globl _start//@_start是GNU彙編器的預設入口标簽========系統複位位置,整個程式入口
_start: b start_code//ARM體系結構規定在上電複位後的起始位置,必須有8條連續的跳轉指令,通過硬體實作。
//相當于中斷向量表
ldr pc, _undefined_instruction//後面若出現異常,則列印消息,停留
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
//@.word為GNU ARM彙編特有的僞操,作為配置設定一段字記憶體單元(配置設定的單元為字對齊的),可以使用.word把标志符作為常量使用。
//如_fiq:.word fiq即把fiq存入記憶體變量_fiq中,也即是把fiq放到位址_fiq中。
_undefined_instruction: .word undefined_instruction
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq
_fiq: .word fiq
.balignl 16,0xdeadbeef
.balignl 16,0xdeadbeef //0x3c=60 4*(8+7)=60 //‘.balign 16’向後移動位置計數器直至計數器的值等于16的倍數。且使用0xdeadbeef來填充空白區
//該數字是個魔數。通常用于在啟動時讀取它并修改它為0,并與原值比較
//以判斷它是從nor nand啟動
_TEXT_BASE:
//
_TEXT_BASE: //代碼重定位後所在的位址
.word TEXT_BASE
.globl _armboot_start
_armboot_start:
.word _start
//在_armboot_start标号處,儲存了_start的值,也就是說,_armboot_start是存放_start的位址。
.globl _bss_start//@__bss_start定義在和開發闆相關的u-boot.lds中,_bss_start儲存的是__bss_start标号所在的位址。
_bss_start:
.word __bss_start
.globl _bss_end
_bss_end:
.word _end
#ifdef CONFIG_USE_IRQ
.globl IRQ_STACK_START
IRQ_STACK_START:
.word 0x0badc0de
.globl FIQ_STACK_START
FIQ_STACK_START:
.word 0x0badc0de
#endif
//上面這段代碼,主要儲存一些全局變量,用于BOOT程式從FLASH拷貝到RAM,或者其它的使用。
start_code:
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3 1101,0011
msr cpsr, r0 //11關中斷,0,thumb模式位
@ bl coloured_LED_init
@ bl red_LED_on
//AT91RM9200 cpu
//#include <common.h> CONFIG_AT91RM9200DK,有沒有定義,看包含的頭檔案
//#include <config.h>
#if defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK) //未定義
ldr r0, =_start
ldr r1, =0x0
mov r2, #16
copyex:
subs r2, r2, #1//sub帶上了s用來更改進位标志,對于sub來說,若發生借位則C标志置0,沒有則為1,這跟adds指令相反!要注意。
ldr r3, [r0], #4
str r3, [r1], #4
bne copyex
#endif
//========針對S3C2400和S3C2410進行特殊處理==========
#ifdef CONFIG_S3C24X0
//S3C2440A datasheet.pdf
# if defined(CONFIG_S3C2400)//未定義
# define pWTCON 0x15300000//關閉看門狗定時器的自動複位功能并屏蔽所有中斷,上電後看門狗為開,中斷為關
# define INTMSK 0x14400008
# define CLKDIVN 0x14800014
#else//執行
# define pWTCON 0x53000000
# define INTMSK 0x4A000008
# define INTSUBMSK 0x4A00001C
# define CLKDIVN 0x4C000014
# endif
#define CLK_CTL_BASE 0x4C000000
#define MDIV_405 0x7f << 12
#define PSDIV_405 0x21
#define MDIV_200 0xa1 << 12
#define PSDIV_200 0x31
//關閉看門狗
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]//對于S3C2440和S3C2410的WTCON寄存器的[0]控制允許或禁止看門狗定時器的複位輸出功能,設定為“0”禁止複位功能。
//到C第二階段有enable_interrupts (); 來使能中斷
//關閉總中斷
mov r1, #0xffffffff 關閉所有中斷服務
ldr r0, =INTMSK
str r1, [r0]
//關閉子中斷
# if defined(CONFIG_S3C2410)//十一個。。。。。。未定義
ldr r1, =0x7ff//INTMSK寄存器的32位和INTSUBMSK寄存器的低11位每一位對應一個中斷
ldr r0, =INTSUBMSK
str r1, [r0]
# endif
#if defined(CONFIG_S3C2440)//十五個。。。。。。
ldr r1, =0x7fff
ldr r0, =INTSUBMSK
str r1, [r0]
#endif
//設定分頻比。主FCLK=MCLK=400MHZ,HCLK=100,PCLK=50,是以是1:4:8
// 因為FCLK != HCLK,是以為異步模式,要協處理器
#if defined(CONFIG_S3C2440)
ldr r0, =CLKDIVN
mov r1, #5//101
str r1, [r0]
// 協處理器,異步模式的時候
mrc p15, 0, r1, c1, c0, 0 //P215
orr r1, r1, #0xc0000000
mcr p15, 0, r1, c1, c0, 0
//主時鐘 MPLL
mov r1, #CLK_CTL_BASE
mov r2, #MDIV_405
add r2, r2, #PSDIV_405
str r2, [r1, #0x04]
#else
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
mrc p15, 0, r1, c1, c0, 0
orr r1, r1, #0xc0000000
mcr p15, 0, r1, c1, c0, 0
mov r1, #CLK_CTL_BASE
mov r2, #MDIV_200
add r2, r2, #PSDIV_200
str r2, [r1, #0x04]
#endif
#endif
//在重新定位之前,要進行RAM初始化
//如果沒有定義的話。執行ifndef
//用bl 會把下一條要 執行的位址放在lr.
#ifndef CONFIG_SKIP_LOWLEVEL_INIT//mini2440.h===undef
bl cpu_init_crit//bl 帶傳回的跳轉指令
#endif
//考慮要不要搬運代碼
//比較 _start 和_TEXT_BASE,如果兩者相等,說明是直接加載到記憶體中的自主啟動
// 如果不等,則說明是nand或者nor啟動
adr r0, _start //位址為0
ldr r1, _TEXT_BASE //?board/mini2440/config.mk==0x33F80000
cmp r0, r1
beq stack_setup //痰進過重定位代碼
//相等的話是加載啟動,直接跳到設定堆棧;不相等的話判斷是nand或者是nor
//不相等的話,判斷是nor或者是nand啟動
//從nor啟動時0位址是NOR,0x40000000是SRAM
//從nand啟動時,0x40000000是SRAM的位址,0位址也是SRAM的位址
//這裡把0x4000003C位址的内容改為
//0,看#0x3c位址的内容是否也該為0.如果是改為0則是NAND啟動
//0x3c位址放的是一個魔數,如0xdeadbeef
ldr r1, =( (4<<28)|(3<<4)|(3<<2) )
mov r0, #0
str r0, [r1]
mov r1, #0x3c
ldr r0, [r1]
cmp r0, #0
bne relocate //不相等,則是從nor啟動
ldr r0, =(0xdeadbeef)//相等的話是nand啟動,把之前那個 0x4000003C上的資料還原
ldr r1, =( (4<<28)|(3<<4)|(3<<2) )
str r0, [r1]
#define LENGTH_UBOOT 0x60000
#define NAND_CTL_BASE 0x4E000000
#ifdef CONFIG_S3C2440
#define oNFCONF 0x00
#define oNFCONT 0x04
#define oNFCMD 0x08
#define oNFSTAT 0x20
@ reset NAND
//初始化nand
mov r1, #NAND_CTL_BASE
ldr r2, =( (7<<12)|(7<<8)|(7<<4)|(0<<0) )
str r2, [r1, #oNFCONF]
ldr r2, [r1, #oNFCONF]
ldr r2, =( (1<<4)|(0<<1)|(1<<0) ) @ Active low CE Control
str r2, [r1, #oNFCONT]
ldr r2, [r1, #oNFCONT]
ldr r2, =(0x6) @ RnB Clear
str r2, [r1, #oNFSTAT]
ldr r2, [r1, #oNFSTAT]
mov r2, #0xff @ RESET command
strb r2, [r1, #oNFCMD]
mov r3, #0 @ wait
nand1:
add r3, r3, #0x1
cmp r3, #0xa
blt nand1
nand2:
ldr r2, [r1, #oNFSTAT] @ wait ready
tst r2, #0x4
beq nand2
ldr r2, [r1, #oNFCONT]
orr r2, r2, #0x2 @ Flash Memory Chip Disable
str r2, [r1, #oNFCONT]
@ get read to call C functions (for nand_read())
ldr sp, DW_STACK_START @ setup stack pointer
mov fp, #0 @ no previous frame, so fp=0
@ copy U-Boot to RAM //調用C函數把代碼從NAND拷貝到記憶體
ldr r0, =TEXT_BASE //目的位址U-BOOT加載到記憶體的位址
mov r1, #0x0 //源位址//從0位址開始搬
mov r2, #LENGTH_UBOOT //長度 算一下U-BOOT的長度
bl nand_read_ll //跳到這個函數裡面去執行搬運
//傳回成功傳回給R0;
tst r0, #0x0 //拷貝成功傳回0
beq ok_nand_read//傳回成功
bad_nand_read: //拷貝不成功則停在這裡
loop2://搬運失敗;根據傳回值是0-成功,非0失敗
b loop2 @ infinite loop
//傳回成功還要進行校驗
ok_nand_read: //從0位址和TEXT_BASE分别取0x400内容出來對比驗證
@ verify
mov r0, #0
ldr r1, =TEXT_BASE
mov r2, #0x400 @ 4 bytes * 1024 = 4K-bytes
go_next:
ldr r3, [r0], #4
ldr r4, [r1], #4
teq r3, r4
bne notmatch//不相等,搬運有問題,跳到loop3
subs r2, r2, #4
beq stack_setup//搬完,跳回設定堆棧
bne go_next
notmatch://不相等
loop3:
b loop3 @ infinite loop
#endif
#ifdef CONFIG_S3C2410
#define oNFCONF 0x00
#define oNFCMD 0x04
#define oNFSTAT 0x10
@ reset NAND
mov r1, #NAND_CTL_BASE
ldr r2, =0xf830 @ initial value
str r2, [r1, #oNFCONF]
ldr r2, [r1, #oNFCONF]
bic r2, r2, #0x800 @ enable chip
str r2, [r1, #oNFCONF]
mov r2, #0xff @ RESET command
strb r2, [r1, #oNFCMD]
mov r3, #0 @ wait
nand1:
add r3, r3, #0x1
cmp r3, #0xa
blt nand1
nand2:
ldr r2, [r1, #oNFSTAT] @ wait ready
tst r2, #0x1
beq nand2
ldr r2, [r1, #oNFCONF]
orr r2, r2, #0x800 @ disable chip
str r2, [r1, #oNFCONF]
@ get read to call C functions (for nand_read())
ldr sp, DW_STACK_START @ setup stack pointer
mov fp, #0 @ no previous frame, so fp=0
@ copy U-Boot to RAM
ldr r0, =TEXT_BASE
mov r1, #0x0
mov r2, #LENGTH_UBOOT
bl nand_read_ll //跳到C語言那邊去搬運
tst r0, #0x0 //因為我們c那邊成功的話,傳回0,如果相等,則成功。
beq ok_nand_read
bad_nand_read: //拷貝不成功
loop2:
b loop2 @ infinite loop 死循環
ok_nand_read: //搬運成功
//把nand flash 前面的一小段代碼加載出來和加載到記憶體裡面的内容進行比較。
;//校驗其中一部分代碼
@ verify
mov r0, #0
ldr r1, =TEXT_BASE
mov r2, #0x400 @ 4 bytes * 1024 = 4K-bytes
go_next:
ldr r3, [r0], #4
ldr r4, [r1], #4
teq r3, r4
bne notmatch //如果不相等,搬運有問題。
subs r2, r2, #4 // r2為測試部分的大小 r2=0x400
beq stack_setup //跳到堆棧設定處 //搬運完
bne go_next //未搬完,繼續搬
notmatch:
loop3:
b loop3 @ infinite loop
#endif
//位址修正
relocate:
ldr r1, =(0xdeadbeef)
cmp r0, r1
bne loop3
adr r0, _start
ldr r1, _TEXT_BASE
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2
add r2, r0, r2
copy_loop:
ldmia r0!, {r3-r10}
stmia r1!, {r3-r10}
cmp r0, r2
ble copy_loop
//設定堆棧
stack_setup:
ldr r0, _TEXT_BASE
//預留一部分位址用來存放變量
sub r0, r0, #CONFIG_SYS_MALLOC_LEN
//@CFG_MALLOC_LEN=128*1024+CFG_ENV_SIZE=128*[email protected]=192K
sub r0, r0, #CONFIG_SYS_GBL_DATA_SIZE
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12
//該部分将未初始化資料段_bss_start----_bss_end中的資料清零
//u-boot.lds
//清楚BSS段
clear_bss:
ldr r0, _bss_start //u-boot.lds
ldr r1, _bss_end
mov r2, #0x00000000
clbss_l:str r2, [r0]
add r0, r0, #4
cmp r0, r1
ble clbss_l
//跳到階段二C語言中去
//這邊的ldr是指令,加載内容,僞指令,加載位址
ldr pc, _start_armboot//加載_start_armboot的内容。===_start_armboot的内容又是start_armboot
#if defined(CONFIG_MINI2440_LED)
#define GPIO_CTL_BASE 0x56000000
#define oGPIO_B 0x10
#define oGPIO_CON 0x0
#define oGPIO_DAT 0x4
#define oGPIO_UP 0x8
mov r1, #GPIO_CTL_BASE
add r1, r1, #oGPIO_B
ldr r2, =0x295551
str r2, [r1, #oGPIO_CON]
mov r2, #0xff
str r2, [r1, #oGPIO_UP]
ldr r2, =0x1c1
str r2, [r1, #oGPIO_DAT]
#endif
_start_armboot: .word start_armboot
#define STACK_BASE 0x33f00000
#define STACK_SIZE 0x10000
.align 2
DW_STACK_START: .word STACK_BASE+STACK_SIZE-4
//主要設定CP15,禁止MMU。
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
cpu_init_crit:
//初始化cache
mov r0, #0
mcr p15, 0, r0, c7, c7, 0 //C7高速緩存和寫緩存控制
mcr p15, 0, r0, c8, c7, 0 //C8 TLB控制(存儲保護和控制)
//關閉MMU
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS)
bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM)
orr r0, r0, #0x00000002 @ set bit 2 (A) Align
orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache //r0=****,****,****,****,**01,**00,0***,*010
mcr p15, 0, r0, c1, c0, 0
//C1是一個控制寄存器,它控制MMU(MPU)的使能,資料Cache或統一Cache的使能,指令Cache的使能,寫緩沖使能等
//設定記憶體區控制期。具體值由廠商或硬體工程師提供。在UBOOT中的設定檔案是
//board/gec2440/lowlevel_init.S
//先儲存之前的lr,防止在BL時,lr把之前的覆寫掉了
mov ip, lr //從子程式跳到孫程式 呵呵
bl lowlevel_init
mov lr, ip
mov pc, lr
#endif
@
@ IRQ stack frame. // 這些定義個别沒用到,發生異常時,ro r1 r2等現場保護的偏移量。如.macro bad_save_user_regs有使用
@
#define S_FRAME_SIZE 72
#define S_OLD_R0 68
#define S_PSR 64
#define S_PC 60
#define S_LR 56
#define S_SP 52
#define S_IP 48
#define S_FP 44
#define S_R10 40
#define S_R9 36
#define S_R8 32
#define S_R7 28
#define S_R6 24
#define S_R5 20
#define S_R4 16
#define S_R3 12
#define S_R2 8
#define S_R1 4
#define S_R0 0
#define MODE_SVC 0x13
#define I_BIT 0x80
// ;儲存中斷前的r0-r12,sp_SVC, lr_SVC, pc, cpsr
.macro bad_save_user_regs
sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ Calling r0-r12
ldr r2, _armboot_start
sub r2, r2, #(CONFIG_STACKSIZE)
sub r2, r2, #(CONFIG_SYS_MALLOC_LEN)
sub r2, r2, #(CONFIG_SYS_GBL_DATA_SIZE+8)
ldmia r2, {r2 - r3} @ get pc, cpsr
add r0, sp, #S_FRAME_SIZE @ restore sp_SVC
add r5, sp, #S_SP
mov r1, lr
stmia r5, {r0 - r3} @ save sp_SVC, lr_SVC, pc, cpsr
mov r0, sp
.endm
.macro irq_save_user_regs
sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ Calling r0-r12
add r7, sp, #S_PC
stmdb r7, {sp, lr}^ @ Calling SP, LR
str lr, [r7, #0] @ Save calling PC
mrs r6, spsr
str r6, [r7, #4] @ Save CPSR
str r0, [r7, #8] @ Save OLD_R0
mov r0, sp
.endm
.macro irq_restore_user_regs
ldmia sp, {r0 - lr}^ @ Calling r0 - lr
mov r0, r0
ldr lr, [sp, #S_PC] @ Get PC
add sp, sp, #S_FRAME_SIZE
subs pc, lr, #4
.endm
//;以_armboot_start為基址,向下作為堆棧,存放lr,spsr
.macro get_bad_stack
ldr r13, _armboot_start @ setup our mode stack
sub r13, r13, #(CONFIG_STACKSIZE)
sub r13, r13, #(CONFIG_SYS_MALLOC_LEN)
sub r13, r13, #(CONFIG_SYS_GBL_DATA_SIZE+8)
str lr, [r13] @ save caller lr / spsr
mrs lr, spsr
str lr, [r13, #4]
mov r13, #MODE_SVC @ //prepare SVC-Mode 10011 把spsr設定為SVC-Mode
@ msr spsr_c, r13
msr spsr, r13
mov lr, pc @//把下下個指令給lr
movs pc, lr
.endm
.macro get_irq_stack @ setup IRQ stack
ldr sp, IRQ_STACK_START
.endm
.macro get_fiq_stack @ setup FIQ stack
ldr sp, FIQ_STACK_START
.endm
.align 5
undefined_instruction: //未定義指令
get_bad_stack ;macro
bad_save_user_regs ;macro
bl do_undefined_instruction //僅僅是列印錯誤資訊和重新開機,沒做其他處理
.align 5
software_interrupt: //軟體中斷
get_bad_stack
bad_save_user_regs
bl do_software_interrupt
.align 5
prefetch_abort: //預取異常中止
get_bad_stack
bad_save_user_regs
bl do_prefetch_abort
.align 5
data_abort: //資料異常中止
get_bad_stack
bad_save_user_regs
bl do_data_abort
.align 5
not_used: //未利用
get_bad_stack
bad_save_user_regs
bl do_not_used
#ifdef CONFIG_USE_IRQ
.align 5 //中斷請求
irq:
//Apollo +
sub lr, lr, #4 @ the return address
ldr sp, IRQ_STACK_START @ the stack for irq
stmdb sp!, { r0-r12,lr } @ save registers
ldr lr, =int_return @ set the return addr
ldr pc, =IRQ_Handle @ call the isr
int_return:
ldmia sp!, { r0-r12,pc }^ @ return from interrupt
//Apollo -
.align 5
fiq: //快速中斷請求
get_fiq_stack
irq_save_user_regs
bl do_fiq
irq_restore_user_regs
#else
.align 5
irq:
get_bad_stack
bad_save_user_regs
bl do_irq
.align 5
fiq:
get_bad_stack
bad_save_user_regs
bl do_fiq
#endif