天天看點

vivi源代碼分析1

通過vivi研究bootloader有一段時間了,基本是在與之相關的基礎方面做工作,還沒有真正深入研究vivi。以後的學習重心就要放到研究vivi源代碼上面了。我想,真正細緻地弄清楚vivi實作的細節,對C語言水準的提高,對ARM體系結構的認識,對S3C2410的熟悉,對嵌入式bootloader相關技術,都能有很大的好處。學習的進度會慢一些,但是務求深入,并且打好相關的基礎。

  一、寫在前面的話

    嵌入式系統軟體開發主要包括五個方面:bootloader編寫(移植)、驅動程式編寫(移植)、作業系統裁減(移植)、檔案系統制作、應用程式編寫(移植)。嵌入式開發流程我已經熟悉,但是僅限于完成最為基本的工作,大部分是借助網絡資料,自己獨立解決的問題很有限。學習嵌入式系統已經一年了,算是入門了。然而,入門之後如何繼續深入學習嵌入式系統開發,如何提高自身的能力?

    我想,這也許是獨立摸索的學習者都會遇到的問題吧。思考之後有所得,核心就是一句話:務實,理論與實踐結合!具體說來,就是要不斷的認識自己,去了解自己最适合做什麼。這是最為重要的,如果不知道做什麼,就無法安排學習的重點。嵌入式開發的領域太廣,要想在方方面面都深入不太容易(當然牛人除外)。現在對自己的認識如下:大學有硬體、通信背景,但是沒有太多機會進行硬體設計。而硬體設計最為重要的就是經驗,動手能力,是以不打算把硬體設計作為學習的重點。底層軟體開發既需要對硬體熟悉,又需要軟體設計能力,正适合我。是以以後的學習,以底層軟體開發(bootloader設計、驅動程式設計)為重點,同時也要加強硬體學習。學習有重點,但是嵌入式開發的其他領域也要涉及,了解廣博才能更有助于設計。進展慢不要緊,關鍵是要深入,深入,再深入。真正地去了解這些技術,并且能夠熟練的應用。這半年的核心就是bootloader技術研究,打算先看vivi,然後看uboot。手頭上的闆子有s3c2410、at91rm9200,這些都可以拿來訓練,争取能夠通過bootloader技術的掌握,同時熟悉了ARM體系結構、ARM彙編、開發工具等等一系列相關内容,總結學習的方法,提高學習能力。   二、準備工作       在分析vivi源代碼的時候,不打算完全按照vivi的代碼來進行。我的思路是,以從nand flash啟動為主線,分析從上電到引導核心的過程。以自己的了解去實作vivi的源代碼,要自己手動編寫,即使與vivi的代碼相同。隻有這樣,才能從整體上了解vivi的設計方法,了解vivi各個功能的實作細節。這份文檔算是自己的學習筆記,盡可能詳細,同時希望有研究vivi的朋友一起交流,我的email:[email protected]。   三、bootloader stage1:【arch/s3c2410/head.S】       首先解決一個問題,就是為什麼使用head.S而不是用head.s?有了GNU AS和GNU Gcc的基礎,不難了解主要原因就是為了使用C預處理器的宏替換和檔案包含功能(GNU AS的預處理無法完成此項功能)。可以參考前面的總結部分。這樣的好處就是可以使用C預處理器的功能來提高ARM彙編的程式設計環境,更加友善。但是因為ARM彙編和C在宏替換的細節上有所不同,為了區分,引入了__ASSEMBLY__這個變量,這是通過Makefile中AFLAGS來引入的,具體如下:  

AFLAGS := -D__ASSEMBLY__ $(CPPFLAGS)

      在後面的頭檔案中,會看到很多#ifdef __ASSEMBLY__等的操作,就是用來區分這個細節的。在編譯彙編檔案時,加入AFLAGS選項,是以__ASSEMBLY__傳入,也就是定義了__ASSEMBLY__;在編譯C檔案時,沒有用AFLAGS選項,自然也就沒有定義__ASSEMBLY__。由此相應的問題就比較清晰了。這個小技巧也是值得學習和借鑒的。   1 首先關注一下開始的三個頭檔案。  

#include "config.h" #include "linkage.h" #include "machine.h"

  (1)利用source insight來檢視【include/config.h】。  

#ifndef _CONFIG_H_ #define _CONFIG_H_ #include "autoconf.h" #endif

      可見,config.h隻是包含一個autoconf.h。而關于autoconf.h的生成,在vivi配置檔案分析的時候也解釋的很清楚了,在這裡就不用再細分析了。需要解釋的一點是,如果寫一個專用的bootloader,不采用vivi的配置機制,那麼配置部分就沒有這麼複雜了,隻需要在include檔案夾中包含一個配置頭檔案即可。現在bootloader的設計有兩種趨勢,一種是針對特定應用,有特殊要求,也就是“專用”。那麼設計時,不需要過多的配置,隻需要簡單的完成引導核心的功能就可以了。二是普通應用,一般是對基本“通用”的bootloader,比如uboot等,然後根據相應的模版進行移植。這就需要了解uboot等的架構,可以進行定制和功能的增加。uboot完成的不僅僅是一個bootloader的功能,還可以提供調試等功能,是以其角色還包含駐留程式這個功能,也就是uboot真正的角色是monitor。當然,可以不加區分,統稱為bootloader。而分析vivi源代碼的實作,對這兩個方向都有幫助。   (2)【include/linkage.h】就是實作了ENTRY宏的封裝。其實這個頭檔案也僅僅為head.S提供了服務,實際上沒有必要寫的這麼複雜,可以簡化一些。比如,我修改了這個頭檔案,如下:  

[[email protected] include]$ cat linkage.h #ifndef _VIVI_LINKAGE_H #define _VIVI_LINKAGE_H #define SYMBOL_NAME(X) X #ifdef __STDC__         #define SYMBOL_NAME_LABEL(X) X##: #else         #define SYMBOL_NAME_LABEL(X) X: #endif #ifdef __ASSEMBLY__ #define ALIGN .align 0 #define ENTRY(name) /         .globl SYMBOL_NAME(name); /         ALIGN; /         SYMBOL_NAME_LABEL(name) #endif #endif

      在這裡,要加強一下C語言宏的設計和分析能力。下面就幾個點簡單的分析一下,後面專門就C宏部分做個總結。       關于__STDC__這個宏,是編譯器自動添加的,含義就是支援标準C。如果支援标準C,那麼##的作用就是“連接配接”,是以SYMBOL_NAME_LABEL(_start)宏展開為_start:,如果不支援标準C,則利用了C預處理器對注釋的處理方式,就是把替換為一個空格,可以測試一下。       另外,關于ENTRY宏的封裝,利用了GNU AS在ARM上的相關特點。首先,利用了分号作為三條語句的連接配接符,而分号是GNU AS彙編注釋符的一種(另外一種是@)。另外,關于ALIGN為什麼用.align 0。這可以參考GNU AS手冊,上面講解的比較清晰,主要是為了相容ARM本身的編譯器。了解了這個也就不難得出ENTRY(_start)宏展開後的形式了。有一個技巧就是可以通過下面的指令來檢測宏展開後的結果,比如:  

[[email protected] vivi_myboard]# gcc -E -D__ASSEMBLY__ -I./include arch/s3c2410/head.S >aaa

  可以檢視aaa檔案的顯示結果,做了一些注釋:  

# 1 "arch/s3c2410/head.S" # 1 "<built-in>" # 1 "<command line>" # 1 "arch/s3c2410/head.S" # 35 "arch/s3c2410/head.S" # 1 "include/config.h" 1 # 14 "include/config.h" # 1 "include/autoconf.h" 1 # 15 "include/config.h" 2 # 36 "arch/s3c2410/head.S" 2 # 1 "include/linkage.h" 1 # 37 "arch/s3c2410/head.S" 2 # 1 "include/machine.h" 1 # 19 "include/machine.h" # 1 "include/platform/smdk2410.h" 1 # 1 "include/s3c2410.h" 1 # 22 "include/s3c2410.h" # 1 "include/hardware.h" 1 # 23 "include/s3c2410.h" 2 # 1 "include/bitfield.h" 1 # 24 "include/s3c2410.h" 2 # 3 "include/platform/smdk2410.h" 2 # 1 "include/sizes.h" 1 # 8 "include/platform/smdk2410.h" 2 # 74 "include/platform/smdk2410.h" # 1 "include/architecture.h" 1 # 75 "include/platform/smdk2410.h" 2 # 20 "include/machine.h" 2 # 38 "arch/s3c2410/head.S" 2 @ Start of executable code 宏定義展開 .globl _start; .align 0; _start: .globl ResetEntryPoint; .align 0; ResetEntryPoint: 下面是裝載中斷向量表,ARM規定,在起始必須有8條跳轉指令,你可以用b,也可以用ldr pc,檔案名。這樣的8條規則的标志被arm定義為bootloader的識别标志,檢測到這樣的标志後,就可以從該位置啟動。這樣的做法是因為開始的時候不一定有bootloader,必須有一種識别機制,如果識别到bootloader,那麼就從bootloader啟動。 @ @ Exception vector table (physical address = 0x00000000) @ @ 0x00: Reset         b Reset @ 0x04: Undefined instruction exception UndefEntryPoint:         b HandleUndef @ 0x08: Software interrupt exception SWIEntryPoint:         b HandleSWI @ 0x0c: Prefetch Abort (Instruction Fetch Memory Abort) PrefetchAbortEnteryPoint:         b HandlePrefetchAbort @ 0x10: Data Access Memory Abort DataAbortEntryPoint:         b HandleDataAbort @ 0x14: Not used NotUsedEntryPoint:         b HandleNotUsed @ 0x18: IRQ(Interrupt Request) exception IRQEntryPoint:         b HandleIRQ @ 0x1c: FIQ(Fast Interrupt Request) exception FIQEntryPoint:         b HandleFIQ 下面是固定位置存放環境變量 @ @ VIVI magics @ @ 0x20: magic number so we can verify that we only put         .long 0 @ 0x24:         .long 0 @ 0x28: where this vivi was linked, so we can put it in memory in the right place         .long _start @ 0x2C: this contains the platform, cpu and machine id         .long ((1 << 24) | (6 << 16) | 193) @ 0x30: vivi capabilities         .long 0 @ 0x34:         b SleepRamProc @ @ Start VIVI head @ Reset:         @ disable watch dog timer         mov r1, #0x53000000         mov r2, #0x0         str r2, [r1] # 121 "arch/s3c2410/head.S"         @ disable all interrupts         mov r1, #0x4A000000         mov r2, #0xffffffff         str r2, [r1, #0x08]         ldr r2, =0x7ff         str r2, [r1, #0x1C]         @ initialise system clocks         mov r1, #0x4C000000         mvn r2, #0xff000000         str r2, [r1, #0x00]         @ldr r2, mpll_50mhz         @str r2, [r1, #0x04]         @ 1:2:4         mov r1, #0x4C000000         mov r2, #0x3         str r2, [r1, #0x14]         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 200 Mhz         mov r1, #0x4C000000         ldr r2, mpll_200mhz         str r2, [r1, #0x04] # 164 "arch/s3c2410/head.S"         bl memsetup         @ Check if this is a wake-up from sleep         ldr r1, PMST_ADDR         ldr r0, [r1]         tst r0, #((1 << 1))         bne WakeupStart         @ All LED on         mov r1, #0x56000000         add r1, r1, #0x50         ldr r2,=0x55aa         str r2, [r1, #0x0]         mov r2, #0xff         str r2, [r1, #0x8]         mov r2, #0x00         str r2, [r1, #0x4] # 230 "arch/s3c2410/head.S"         @ set GPIO for UART         mov r1, #0x56000000         add r1, r1, #0x70         ldr r2, gpio_con_uart         str r2, [r1, #0x0]         ldr r2, gpio_up_uart         str r2, [r1, #0x8]         bl InitUART # 259 "arch/s3c2410/head.S"         bl copy_myself         @ jump to ram         ldr r1, =on_the_ram         add pc, r1, #0         nop         nop 1: b 1b @ infinite loop on_the_ram: # 279 "arch/s3c2410/head.S"         @ get read to call C functions         ldr sp, DW_STACK_START @ setup stack pointer         mov fp, #0 @ no previous frame, so fp=0         mov a2, #0 @ set argv to NULL         bl main @ call main         mov pc, #0x00000000 @ otherwise, reboot @ @ End VIVI head @ 下面是子例程 @ @ Wake-up codes @ WakeupStart:         @ Clear sleep reset bit         ldr r0, PMST_ADDR         mov r1, #(1 << 1)         str r1, [r0]         @ Release the SDRAM signal protections         ldr r0, PMCTL1_ADDR         ldr r1, [r0]         bic r1, r1, #((1 << 19) | (1 << 18) | (1 << 17))         str r1, [r0]         @ Go...         ldr r0, PMSR0_ADDR @ read a return address         ldr r1, [r0]         mov pc, r1         nop         nop 1: b 1b @ infinite loop SleepRamProc:         @ SDRAM is in the self-refresh mode */         ldr r0, REFR_ADDR         ldr r1, [r0]         orr r1, r1, #(1 << 22)         str r1, [r0]         @ wait until SDRAM into self-refresh         mov r1, #16 1: subs r1, r1, #1         bne 1b         @ Set the SDRAM singal protections         ldr r0, PMCTL1_ADDR         ldr r1, [r0]         orr r1, r1, #((1 << 19) | (1 << 18) | (1 << 17))         str r1, [r0]         ldr r0, PMCTL0_ADDR         ldr r1, [r0]         orr r1, r1, #(1 << 3)         str r1, [r0] 1: b 1b # 379 "arch/s3c2410/head.S" .globl memsetup; .align 0; memsetup:         @ initialise the static memory         @ set memory control registers         mov r1, #0x48000000         adrl r2, mem_cfg_val         add r3, r1, #52 1: ldr r4, [r2], #4         str r4, [r1], #4         cmp r1, r3         bne 1b         mov pc, lr @ @ copy_myself: copy vivi to ram @ copy_myself:         mov r10, lr         @ reset NAND         mov r1, #0x4E000000         ldr r2, =0xf830 @ initial value         str r2, [r1, #0x00]         ldr r2, [r1, #0x00]         bic r2, r2, #0x800 @ enable chip         str r2, [r1, #0x00]         mov r2, #0xff @ RESET command         strb r2, [r1, #0x04]         mov r3, #0 @ wait 1: add r3, r3, #0x1         cmp r3, #0xa         blt 1b 2: ldr r2, [r1, #0x10] @ wait ready         tst r2, #0x1         beq 2b         ldr r2, [r1, #0x00]         orr r2, r2, #0x800 @ disable chip         str r2, [r1, #0x00]         @ 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 vivi to RAM         ldr r0, =(0x30000000 + 0x04000000 - 0x00100000)         mov r1, #0x0         mov r2, #0x20000         bl nand_read_ll         tst r0, #0x0         beq ok_nand_read # 441 "arch/s3c2410/head.S" ok_nand_read:         @ verify         mov r0, #0         ldr r1, =0x33f00000         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 done_nand_read         bne go_next notmatch: # 469 "arch/s3c2410/head.S" 1: b 1b done_nand_read:         mov pc, r10 @ clear memory @ r0: start address @ r1: length mem_clear:         mov r2, #0         mov r3, r2         mov r4, r2         mov r5, r2         mov r6, r2         mov r7, r2         mov r8, r2         mov r9, r2 clear_loop:         stmia {r2-r9}         subs r1, r1, #(8 * 4)         bne clear_loop

        mov pc, lr # 613 "arch/s3c2410/head.S" @ Initialize UART @ @ r0 = number of UART port InitUART:         ldr r1, SerBase         mov r2, #0x0         str r2, [r1, #0x08]         str r2, [r1, #0x0C]         mov r2, #0x3         str r2, [r1, #0x00]         ldr r2, =0x245         str r2, [r1, #0x04]         mov r2, #((50000000 / (115200 * 16)) - 1)         str r2, [r1, #0x28]         mov r3, #100         mov r2, #0x0 1: sub r3, r3, #0x1         tst r2, r3         bne 1b # 653 "arch/s3c2410/head.S"         mov pc, lr @ @ Exception handling functions @ HandleUndef: 1: b 1b @ infinite loop HandleSWI: 1: b 1b @ infinite loop HandlePrefetchAbort: 1: b 1b @ infinite loop HandleDataAbort: 1: b 1b @ infinite loop HandleIRQ: 1: b 1b @ infinite loop HandleFIQ: 1: b 1b @ infinite loop HandleNotUsed: 1: b 1b @ infinite loop @ @ Low Level Debug @ # 838 "arch/s3c2410/head.S" @ @ Data Area @ @ Memory configuration values .align 4 mem_cfg_val:         .long 0x22111110         .long 0x00000700         .long 0x00000700         .long 0x00000700         .long 0x00000700         .long 0x00000700         .long 0x00000700         .long 0x00018005         .long 0x00018005         .long 0x008e0459         .long 0xb2         .long 0x30         .long 0x30 @ Processor clock values .align 4 clock_locktime:         .long 0x00ffffff mpll_50mhz:         .long ((0x5c << 12) | (0x4 << 4) | (0x2)) mpll_200mhz:         .long ((0x5c << 12) | (0x4 << 4) | (0x0)) clock_clkcon:         .long 0x0000fff8 clock_clkdivn:         .long 0x3 @ initial values for serial uart_ulcon:         .long 0x3 uart_ucon:         .long 0x245 uart_ufcon:         .long 0x0 uart_umcon:         .long 0x0 @ inital values for GPIO gpio_con_uart:         .long 0x0016faaa gpio_up_uart:         .long 0x000007ff         .align 2 DW_STACK_START:         .word (((((0x30000000 + 0x04000000 - 0x00100000) - 0x00100000) - 0x00004000) - (0x00004000 + 0x00004000 + 0x00004000)) - 0x00008000)+0x00008000 -4 # 922 "arch/s3c2410/head.S" .align 4 SerBase:         .long 0x50000000 # 935 "arch/s3c2410/head.S" .align 4 PMCTL0_ADDR:         .long 0x4c00000c PMCTL1_ADDR:         .long 0x56000080 PMST_ADDR:         .long 0x560000B4 PMSR0_ADDR:         .long 0x560000B8 REFR_ADDR:         .long 0x48000024 [[email protected] vivi_myboard]#

       【include/machine.h】則是利用條件編譯來選擇适合自己開發闆的頭檔案,本開發闆的頭檔案是【include/platform/smdk2410.h】,主要是一些寄存器的初始值(以v開頭)和一些相關的位址等等的定義。一般開發闆不同,都是修改此檔案相應的部分。   2、關于中斷向量表       開始對中斷向量表很疑惑。現在的了解比較清晰了,在硬體實作上,會支援中斷機制,這個可以參考微機接口原理部分詳細了解。現在的中斷機制處理的比較智能,對每一種中斷會固定一個中斷向量,比如說,發生IRQ中斷,中斷向量位址為0x00000018(當然,這還與ARM9TDMI core有關,其中一個引腳可以把中斷向量表配置為高端啟動,或者低端啟動。你可以通過CP15的register 1的bit 13的V bit來設定,可以檢視Datasheet TABLE 2-10來解決。),那麼PC要執行的指令就是0x00000018。如果在這個位址放上一個跳轉指令(隻能使用b或者ldr),那麼就可以跳到實際功能代碼的實作區了。ARM體系結構規定在上電複位後的起始位置,必須有8條連續的跳轉指令,這是bootloader的識别入口,通過硬體實作。看一下vivi的中斷向量表:  

@ 0x00         b Reset @ 0x04 HandleUndef:         b HandleUndef @ 0x08 HandleSWI:         b HandleSWI @ 0x0c HandlePrefetchAbort:         b HandlePrefetchAbort @ 0x10 HandleDataAbort:         b HandleDataAbort @ 0x14 HandleNotUsed:         b HandleNotUsed @ 0x18 HandleIRQ:         b HandleIRQ @ 0x1c HandleFIQ:         b HandleFIQ

      注意,中斷向量表可以修改,也可以通過MMU實作位址重映射,來改變對應的實體媒體。如果不對每種中斷進行測試,可以采用下面的書寫方式。  

@ 0x00         b Reset @ 0x04 HandleUndef:         b . @ 0x08 HandleSWI:         b . @ 0x0c HandlePrefetchAbort:         b . @ 0x10 HandleDataAbort:         b . @ 0x14 HandleNotUsed:         b . @ 0x18 HandleIRQ:         b . @ 0x1c HandleFIQ:         b .

      其中,“.”表示目前位址,那麼很明顯,“b .”,就表示了死循環了。       如果增加中斷處理,則需要考慮使用b還是使用ldr。主要的差別在于b指令跳轉範圍有限,僅僅是+-32M。是以如果超出32M,那麼就要采用ldr了,基本的模式是:  

.globl _start _start: b reset         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

...

        .align  5 undefined_instruction:         get_bad_stack         bad_save_user_regs         bl      do_undefined_instruction

...

      這就是uboot采用的方式。這些技術是相當成熟的。現在在加速啟動上,就會考慮對中斷向量表作出一些相應的處理。比如,如果從nor flash啟動,啟動後把kernel等加載到sdram裡面運作。無疑,sdram裡面運作速度比nor flash裡面快。但是如果不對中斷向量表進行處理,那麼發生中斷時,首先還是得通路nor flash裡面的中斷向量表。然後跳轉到sdram相應的執行部分。是以,提高速度就是要解決不經過nor flash,完全在sdram裡面運作。采用的技術手段是通過重定向機制,有些SoC提供了這樣的硬體手段,但是有些沒有,比如S3C2410就沒有。但是S3C2410有MMU,可以通過MMU來改變映射關系實作雖然中斷向量還是0x00000018,但是實際的實體媒體是sdram,具體的方法可以參考MMU部分的總結來完成。如果采用nand flash啟動的話,就不必作出這樣的處理。本質上是因為nand flash并非記憶體映射,而且中斷向量表占用的是開始4K的steppingstone,是sram,其速度比sdram還要快,如果作出上面的處理,速度反而會下降了。       中斷向量表的出現也是具有曆史原因的,是解決特定問題采用的一種技術手段。為了了解中斷向量表的一些特殊用途,還應該了解加載域和運作域的不同。這樣就能從全局上把握處理的原則,相應的解決機制也比較容易了解了。   3、關于vivi magic number       接下來,vivi設定了自己的一些magic number。了解什麼是magic number,需要通過google來查找。本來wiki上有關于magic number(programming)的解釋,不過最近打不開了,找了一個替代網站,網址是 http://www.answers.com/,上面的内容是wiki的備份吧(好像可以這樣了解)。關于magic number(programming)的文章為 http://www.answers.com/topic/magic-number-programming。關于magic number(programming)的總結和解釋,專門拿出一篇來總結(基本上是上述英文文章的翻譯吧,不過講解的确實非常清晰,讀完之後對magic number就很明白了,而且在程式設計中,如何恰當的使用magic number,也會有一定的認識)。  

@ 0x20: magic number so we can verify that we only put         .long 0 @ 0x24:         .long 0 @ 0x28: where this vivi was linked, so we can put it in memory in the right place         .long _start @ 0x2C: this contains the platform, cpu and machine id         .long ARCHITECTURE_MAGIC @ 0x30: vivi capabilities         .long 0

      對vivi的這些magic number,雖然設計在這裡,不過大部分還是沒有使用的。其中0x20和0x24沒有使用,在0x2C處,倒是設計了一個magic number,組成的格式如下:bit[31:24]為platform,bit[23:16]為cpu type,bit[15:0]為machine id。關于ARCHITECTURE_MAGIC的定義,在【include/platform/smdk2410.h】,如下:  

#include "architecture.h" #define MACH_TYPE        193     #define ARCHITECTURE_MAGIC    ((ARM_PLATFORM << 24) | (ARM_S3C2410_CPU << 16) | /                  MACH_TYPE)

      具體的值的定義則在【include/architecture.h】裡面。很簡單的推理,可以計算出s3c2410的值0x10600c1。不過你可以嘗試把此部分去掉,編譯仍然沒有問題(在我的配置的前提下,因為還沒有分析完,是以還不好确定,在vivi的列印資訊裡面,隻有一個MACH_TYPE,實作的手段是通過上述宏,還是通過讀取記憶體提取字段,等分析到那個部分的時候再具體解決,總之,在這裡,這個并不成為問題。你可以有多種猜想,也可以按照自己的想法定制,本來magic number就是“幻數”,你當然也可以玩一下了。)   4、實際完成的主要任務:       ·關閉看門狗。上電複位後,看門狗預設是開啟的,vivi是不需要使用看門狗的,是以首先要關閉。     ·關閉所有中斷。上電複位後,所有中斷預設是關閉的,是以可以不需要代碼實作。當然,為了保險和了解上的友善,可以增加此部分代碼(vivi就是如此)。     ·初始化系統時鐘。參考clock部分。     ·記憶體設定。主要就是完成13個相關寄存器的設定,當然,正常情況下,加入記憶體檢測是必要的,如果記憶體不可用,或者設定有問題,那麼後續工作都是無法完成的。關于記憶體檢測算法,可以參考詹榮開的《嵌入式bootloader技術内幕》,主要分析了blob的記憶體檢測算法,這也具有一定的通用性。vivi采取了類似的算法。這裡需要了解的問題就是測試的時候為什麼要采用0xaa和0x55。寫成二進制就比較清晰了,它們正好是1和0交替,結合sdram的硬體特點,采用這樣的值能檢測出多種問題。我在用C8051F020寫sdram檢測時,就是采用了這樣的記憶體檢測算法。可以展開來分析研究。     ·初始化調試訓示燈(可選)     ·初始化UART,作為調試口(可選擇,這部分工作是可以放到stage 2來完成的,不過,如果對stage 1來進行調試,可以采用調試燈的方法,也可以初始化UART來完成調試資訊的列印,可以參考ARM調試總結部分)。     ·複制代碼到sdram中。     ·跳轉到main,進入stage 2。在這裡,詹榮開提到一種彈簧床的技術,實際實作比較簡單。在vivi中,可以用如下方法:  

@ jump to ram         @ a technology about trampoline         ldr r1, =on_the_ram         add pc, r1, #0         nop         nop 1:         b 1b on_the_ram:         @ setup by APCS         ldr sp, DW_STACK_START @ setup stack pointer         mov fp, #0 @ no previous frame, so fp=0         mov a2, #0 @ set argv to NULL         bl main @ call main         mov pc, #FLASH_BASE @ otherwise, reboot

      這裡有幾點需要說明,一是開始的幾處nop,實際上是考慮到流水線斷流問題而設計的。這部分我也了解的不是太清晰,可以參考毛德操的書來分析。但是可以按照自己想法做簡單的修改,也就是不考慮流水線問題,直接采用簡單的ldr加載(至于為什麼用ldr,而不是使用b,主要還是加載域和運作域的問題,參考前面總結),經過測試也沒有問題。二是關于fp等的設定,這裡主要是利用了APCS(ARM過程調用标準),具體可以參考相應的标準,同樣的道理,如果把這三條去掉,運作也是沒有問題的。加入這三條語句和去除這三條語句,在程式設計上,到底有什麼不同,可能造成的微影響是什麼,還有待研究。三是,如果調用出現問題,直接軟複位,這個那個彈簧床技術本質一緻,但是處理手段不同。當然,你也可以用b on_the_ram。       具體的每個階段的工作在前面的基礎實驗中都做了詳細的分析,具體可以參考那些總結部分,就不羅列在這裡了。       至此,bootloader(vivi)第一階段的分析就完成了。

轉自http://blog.chinaunix.net/u/21948/showart.php?id=376062