如前文U-boot-2009.03移植之四中所講,第一階段,也就是支持2440的階段,首先要做的事情就是修改/cpu/arm920t/start.s
start.s 源碼註解參考
http://blog.chinaunix.net/u1/34474/showart.php?id=2217073
如何添加支持2440修改參考
http://zjbintsystem.blog.51cto.com/964211/211345
(針對UBOOT從Nand啓動的支持部分,參考這個網頁修改,實際上這個網頁給出的移植攻略很全,可以多看。)
值得注意的是,在以前版本的Uboot都是從nor啓動,所以在拷貝代碼那部分的源碼跟我們貼出來的這個修改後的源碼不同,原因就是那些支持從nor啓動的代碼已經被刪除,取而代之的是我們加上了從nand啓動的代碼
http://blog.csdn.net/Charistain_huang/archive/2010/04/09/5469007.aspx
(針對u-boot-2009,但是有不少錯誤,尤其是對於start.s的解釋,更多要結合下面這一篇看)
http://home.eeworld.com.cn/my/space.php?uid=135723&do=blog&id=25347
(針對u-boot-1.3.4,但是個人認爲是寫的最好最全的一篇,與2009非常相近)
需要注意的是,兩片文章在“代碼搬移”這部分代碼的區別,個人認爲1.3.4的是正確的。
我在這裏只附上修改後的源碼並做簡要解釋,源碼如下:
註釋部分由“//hadise=========”開頭
#include <config.h>
#include <version.h>
//hadise===========#include <status_led.h> 已經被刪除掉 /*這是針對AT91RM9200DK開發板的,刪掉。*/
/*
*************************************************************************
*
* Jump vector table as in table 3.1 in [1]
*
*************************************************************************
*/
.globl _start
_start: b start_code
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
_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
//hadise=========以上幾句是裝載特定的地址到PC寄存器,當發生特殊的事件時候,程序會跳轉到這幾句話中的某一句,裝載
//地址到PC中,實際上就是跳轉到相應的地址指向特定的代碼
.balignl 16,0xdeadbeef //hadise=====4字節對齊,0xdeadbeef是magic碼
/*
*************************************************************************
*
* Startup Code (called from the ARM reset exception vector)
*
* do important init only if we don't start from memory!
* relocate armboot to ram
* setup stack
* jump to second stage
*
*************************************************************************
*/
_TEXT_BASE:
.word TEXT_BASE
.globl _armboot_start
_armboot_start:
.word _start
/*
* These are defined in the board-specific linker script.
*/
.globl _bss_start
_bss_start:
.word __bss_start
.globl _bss_end
_bss_end:
.word _end
#ifdef CONFIG_USE_IRQ
/* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START
IRQ_STACK_START:
.word 0x0badc0de
/* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START
FIQ_STACK_START:
.word 0x0badc0de
#endif
/*
* the actual start code
*/
start_code:
/*
* set the cpu to SVC32 mode
*/
mrs r0,cpsr //hadise複製cpsr 的內容到r0中
bic r0,r0,#0x1f //hadise==bic是位清除指令,這句話的意思是,讓r0與立即數#0x1f相與,返回r0。實質是取低5位
orr r0,r0,#0xd3 //hadise==因爲低5位的值代表了ARM狀態,其中,d3這個值,代表了svc32模式
msr cpsr,r0 //hadise==然後把設置好的狀態,寫回到cpsr
//hadise=======以上4句話,是要設置ARM狀態爲SVC32模式,這裏說點小知識:
ARM6開始有了32位模式,32 位的與 26 位的不同是:
PC (R15)是完全的 32 位寬,並只用做程序計數器。
PSR 包含在它自己的寄存器 CPSR(當前程序狀態寄存器) 中。
每個有特權的模式都有一個專有的 SPSR 寄存器,用來保存 CPSR。
這裏有兩個新的特權模式,每個有特權的模式都有 R13 和 R14 的專有復件。
這就造成對於R15的操作,也就是對於PC的操作,不能用以前的指令,要用專用的指令 mrs 和 msr
@bl coloured_LED_init
@bl red_LED_on
//hadise========以上兩句是針對AT91RM9200DK開發板的,因此刪除
#if defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK)
/*
* relocate exception table
*/
ldr r0, =_start
ldr r1, =0x0
mov r2, #16
copyex:
subs r2, r2, #1
ldr r3, [r0], #4
str r3, [r1], #4
bne copyex
#endif
//hadise=========針對AT91RM9200DK開發板的代碼
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440)
/* turn off the watchdog */
# if defined(CONFIG_S3C2400)
# define pWTCON 0x15300000
# define INTMSK 0x14400008 /* Interupt-Controller base addresses */
# define CLKDIVN 0x14800014 /* clock divisor register */
#else
# define pWTCON 0x53000000
# define INTMOD 0x4A000004
# define INTMSK 0x4A000008 /* Interupt-Controller base addresses */
# define INTSUBMSK 0x4A00001C
# define CLKDIVN 0x4C000014 /* clock divisor register */
# endif
//hadise========以上這裏有自己修改,針對2440和2410的不同,設置了相應寄存器的地址
#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]
//hadise====以上3句關閉看門狗
/*
* mask all IRQs by setting all bits in the INTMR - default
*/
mov r1, #0xffffffff
ldr r0, =INTMSK
str r1, [r0]
//hadise=====以上3句關閉所有中斷
# if defined(CONFIG_S3C2410)
ldr r1, =0x3ff
ldr r0, =INTSUBMSK
str r1, [r0]
# endif
# if defined(CONFIG_S3C2440)
ldr r1, =0x7fff
ldr r0, =INTSUBMSK
str r1, [r0]
# endif
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
@ldr r0, =CLKDIVN
@mov r1, #3
@str r1, [r0]
//hadise=====以上屏蔽了時鐘的修改,我們在以後的函數中再修改
# if defined(CONFIG_S3C2440) /* FCLK:HCLK:PCLK = 1:4:8 */
ldr r0, =CLKDIVN
mov r1, #5
str r1, [r0]
mrc p15, 0, r1, c1, c0, 0 /*read ctrl register */
orr r1, r1, #0xc0000000 /*Asynchronous */
mcr p15, 0, r1, c1, c0, 0 /*write ctrl register */ /*now, CPU clock is 405.00 Mhz */
mov r1, #CLK_CTL_BASE /* */
mov r2, #MDIV_405 /* mpll_405mhz */
add r2, r2, #PSDIV_405 /* mpll_405mhz */
str r2, [r1, #0x04] /* MPLLCON */
//hadise======================================以上設置時鐘的代碼很重要,很多時候UBOOT移植不成功就是時鐘沒設置對
#else /* FCLK:HCLK:PCLK = 1:2:4 */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
mrc p15, 0, r1, c1, c0, 0 /*read ctrl register */
orr r1, r1, #0xc0000000 /*Asynchronous */
mcr p15, 0, r1, c1, c0, 0 /*write ctrl register */ /*now, CPU clock is 202.8 Mhz */
mov r1, #CLK_CTL_BASE /* */
mov r2, #MDIV_200 /* mpll_200mhz */
add r2, r2, #PSDIV_200 /* mpll_200mhz */
str r2, [r1, #0x04] # endif
#endif /* CONFIG_S3C2400 || CONFIG_S3C2410 || defined(CONFIG_S3C2440)*/
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
adr r0,_start
ldr r1,_TEXT_BASE
cmp r0,r1
blne cpu_init_crit
#endif
//hadise===============比較_start和_TEXT_BASE,以判斷當前UBOOT是在flash中運行還是內存中運行,如果不等於,表示在
//hadise===============flash中運行,那麼,跳轉到cpu_init_crit
//hadise=====由board/samsung/mini2440/u-boot.lds鏈接文件中可以看出,testbase就是start.o開始的地方,如果是在flash中,
//haidise===則應該等於0,否則等於內存起始物理地址,對於mini2440來說,應該是0x30000000,而start則絕對的是0x0000000.
//hadise====跳入cpu_init_crit ,這是一個系統初始化函數,他還會調用board/*/lowlevel_init.S中的lowlevel_init函數。
主要是對系統總線的初始化,初始化了連接存儲器的位寬、速度、刷新率等重要參數。經過這個函數的正確初始化,Nor Flash、SDRAM纔可以被系統使用。下面的代碼重定向就依
賴它。
/*
* we do sys-critical inits only at reboot,
* not when booting from ram!
*/
@#ifdef CONFIG_S3C2440_NAND_BOOT
#define NAND_CTL_BASE 0x4E000000/* Offset */
#define oNFCONF 0x00
#define oNFCONT 0x04
#define oNFCMD 0x08
#define oNFSTAT 0x20
#define LENGTH_UBOOT 0x40000
@ reset NAND
/*往下四段內容都是針對S3C2440的關於NAND-FLASH的寄存器的設置,具體有什麼作用,看了datasheet,有些明白有些不明白*/
mov r1, #NAND_CTL_BASE
ldr r2, =( (7<<12)|(7<<8)|(7<<4)|(0<<0) )
str r2, [r1, #oNFCONF] /*這些宏是在include/configs/qljt2440.h中被定義的*/
ldr r2, [r1, #oNFCONF] /*還是弄不懂爲什麼上面一句str以後還要有這句的ldr命令?why?難道是多餘的?*/
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]
/*delay一段時間*/
mov r3, #0 @ wait
nand1:
add r3, r3, #0x1
cmp r3, #0xa
blt nand1
/*等待nand-flash的復位完畢信號*/
nand2:
ldr r2, [r1, #oNFSTAT] @ wait ready
tst r2, #0x4
beq nand2
ldr r2, [r1, #oNFCONT]
orr r2, r2, #0x2 @ Flash Memory Chip Disable /*在這裏先Display fansh CE先,在C函數中對falsh進行*/
str r2, [r1, #oNFCONT] /*操作的時候才enable,爲什麼這樣操作不太清楚*/
/*下面這段用來初始化棧指針sp和幀指針fp,至於它們的定義和作用參考文件夾” 棧指針sp和幀指針fp”裏面的內容
記住它們都是與函數調用時候相關的。簡單來講就是子函數被調用以後是通過指針的相對位置來查找調用參數和局部變量的,但是由於sp經常變化,所以需要fp來協助。*/
@ get ready to call C functions (for nand_read())
ldr sp, DW_STACK_START @ setup stack pointer /*sp 是指堆棧指針*/
mov fp, #0 @ no previous frame, so fp=0
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
adr r0,_start
ldr r1,_TEXT_BASE
cmp r0,r1
beq stack_setup
@@@@@@@@@@@@@@@@@@@@@@@@@@@
//這部分做了特殊記號,是因爲當初吃了虧,忘記了加這部分。參考網中提到的攻略裏面沒有寫到這一部分,但是一定要加上這幾句話。
作用是,如果不是在ram中運行,則要先跳到初始化堆棧的代碼部分,在stack_setup代碼段中,完成堆棧初始化,然後調用_start_armboot進入第二階段C語言部分,如果不加這句話,則會繼續把代碼從nand搬運到ram中,不必要!
@ copy U-Boot to RAM /*vivi裏面應該是有一段是針對gpio的程序,也許使用來debug用的信號燈,這裏省略了*/
/* TEXT_BASE 是uboot自己的入口地址,在u-boot-1.3.4-board/qljt/qljt2440的config.mk中定義
有趣的是外國人的逆向思維很厲害,它們很靈活地把它放在SDRAM的最後0x80000地方,也就是0x33F80000
*/
ldr r0, =TEXT_BASE /*r0 : 把u-boot複製到ram的那個位置*/
mov r1, #0x0 /*r1 : 從falsh的那個位置開始複製*/
mov r2, #0x20000 /*r2 : 複製多大的內容*/
bl nand_read_ll /*跳到執行uboot複製的程序入口,這個函數從哪裏來?也是來自vivi的,沒辦法*/
tst r0, #0x0 /*這裏特別注意r0的值是指nand_read_ll 執行完以後的返回值,而不是上面
ldr r0, =TEXT_BASE 的值,初學者往往在這裏想不通*/
beq ok_nand_read
bad_nand_read: /*如果讀nand_read失敗的話,那麼sorry,重來,或者檢查硬件*/
loop2: b loop2 @ infinite loop
ok_nand_read:
@ verify
/*計算機就是好,很容易就可以檢測我們放在SDRAM中的u-boot是不是flash中的uboot。
本開發板使用的是nand-falsh的啓動方式,板子一上電並不是馬上進入SDRAM執行程序的。是這樣的:板子一上電,S3C2440自動把nand-falsh中從0地址開始的4Kbytes複製到
S3C2440集成的某個緩衝區裏面(起始地址是0x00),從那裏開始執行,那4K程序負責把整個uboot複製到SDRAM,然後才跳到SDRAM開始正真的UBOOT(這個技術是有個專業名字叫做
stepingstone),*/
/*下面這段程序的作用就是用開始執行的4Kbytes程序跟我們複製到SDRAM中的uboot的前4K程序進行比較,從而校驗*/
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
beq stack_setup
bne go_next
notmatch:
loop3: b loop3 @ infinite loop
//=========================================================
/* Set up the stack */
stack_setup:
ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
sub r0, r0, #CONFIG_SYS_MALLOC_LEN /* malloc area */
sub r0, r0, #CONFIG_SYS_GBL_DATA_SIZE /* bdinfo */
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12 /* leave 3 words for abort-stack */
clear_bss:
ldr r0, _bss_start /* find start of bss segment */
ldr r1, _bss_end /* stop here */
mov r2, #0x00000000 /* clear */
clbss_l:str r2, [r0] /* clear loop... */
add r0, r0, #4
cmp r0, r1
ble clbss_l
ldr pc, _start_armboot
_start_armboot: .word start_armboot
#define STACK_BASE 0x33f00000
#define STACK_SIZE 0x10000
.align 2
DW_STACK_START: .word STACK_BASE+STACK_SIZE-4
/*
*************************************************************************
*
* CPU_init_critical registers
*
* setup important registers
* setup memory timing
*
*************************************************************************
*/
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
cpu_init_crit:
/*
* flush v4 I/D caches
*/
mov r0, #0
mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */
mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */
/*
* disable MMU stuff and caches
*/
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
mcr p15, 0, r0, c1, c0, 0
/*
* before relocating, we have to setup RAM timing
* because memory timing is board-dependend, you will
* find a lowlevel_init.S in your board directory.
*/
mov ip, lr
#if defined(CONFIG_AT91RM9200EK)
#else
bl lowlevel_init
//hadise=========================這一句很重要,跳轉到相應板子的lowlevel_init.S,注意看上面的英文解釋
#endif
mov lr, ip
mov pc, lr
#endif /* CONFIG_SKIP_LOWLEVEL_INIT */
/*
*************************************************************************
*
* Interrupt handling
*
*************************************************************************
*/
@
@ IRQ stack frame.
@
#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
/*
* use bad_save_user_regs for abort/prefetch/undef/swi ...
* use irq_save_user_regs / irq_restore_user_regs for IRQ/FIQ handling
*/
.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) @ set base 2 words into abort stack
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 @ return & move spsr_svc into cpsr
.endm
.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) @ reserved a couple spots in abort stack
str lr, [r13] @ save caller lr / spsr
mrs lr, spsr
str lr, [r13, #4]
mov r13, #MODE_SVC @ prepare SVC-Mode
@ msr spsr_c, r13
msr spsr, r13
mov lr, pc
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
/*
* exception handlers
*/
.align 5
undefined_instruction:
get_bad_stack
bad_save_user_regs
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:
get_irq_stack
irq_save_user_regs
bl do_irq
irq_restore_user_regs
.align 5
fiq:
get_fiq_stack
/* someone ought to write a more effiction fiq_save_user_regs */
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