1. 程式人生 > >vivi原始碼最為詳細分析(一)

vivi原始碼最為詳細分析(一)

通過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各個功能的實現細節。這份文件算是自己的學習筆記。 三、bootloader stage1:【arch/s3c2410/head.S】     首先解決一個問題,就是為什麼使用head.S而不是用head.s?有了GNU AS和GNU GCC的基礎,不難理解主要原因就是為了使用C前處理器的巨集替換檔案包含功能(GNU AS的預處理無法完成此項功能)。可以參考前面的總結部分。這樣的好處就是可以使用C前處理器的功能來提高ARM彙編的程式設計環境,更加方便。但是因為ARM彙編和C在巨集替換的細節上有所不同
,為了區分,引入了__ASSEMBLY__這個變數,這是通過Makefile中AFLAGS來引入的(一般在頂層Makefile中定義),具體如下:

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_ */

    可見,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   //_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: //上電後cpu會從0x0地址讀取指令執行,也就是b 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]  //0x4A000008為中斷遮蔽暫存器,將裡面賦全1,表示遮蔽這32箇中斷源
        ldr r2, =0x7ff
        str r2, [r1, #0x1C]  //0x4A00001C為中斷子遮蔽暫存器,裡面低11位也用來表示遮蔽掉這11箇中斷源

        @ initialise system clocks
        mov r1, #0x4C000000
        mvn r2, #0xff000000 //MVN指令可完成從另一個暫存器、被移位的暫存器、或將一個立即數載入到目的暫存器。與MOV指令不同之處是在傳送之前按位被取反了,即把一個被取反的值傳送到目的暫存器中。也就是r2=0x00ffffff
        str r2, [r1, #0x00] //我們可以在程式開頭啟動MPLL,在設定MPLL的幾個暫存器後,需要等待一段 時間(Lock Time),  MPLL的輸出才穩定。在這段時間(Lock Time)內,FCLK停振,CPU停止工作。Lock Time的長短由暫存器LOCKTIME設定,Lock Time之後,MPLL輸出正常,CPU工作在新的FCLK下,前面說過,MPLL啟動後需要等待一段時間(Lock Time),使得其輸出穩定。
位[23:12]用於UPLL,位[11:0]用於MPLL。本實驗使用確省值0x00ffffff。


        @ldr r2, mpll_50mhz
        @str r2, [r1, #0x04]

        @ 1:2:4
        mov r1, #0x4C000000
        mov r2, #0x3
        str r2, [r1, #0x14] //0x4C000014為分頻暫存器,用於設定FCLK、HCLK、PCLK三者的比例

        bit[2] — — HDIVN1,若為1,則 bit[1:0] 必 須 設 為 0b00 , 此 時FCLK:HCLK:PCLK=1:1/4:1/4;若為0,三者比例由bit[1:0]確定 bit[1]——HDIVN,0:HCLK=FCLK;1:HCLK=FCLK/2 bit[0]——PDIVN,0:PCLK=HCLK;1:PCLK=HCLK/2
 本實驗設為0x03,則FCLK:HCLK:PCLK=1:1/2:1/4


        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

         上面這三句程式碼的意思是切換模式:If HDIVN = 1, the CPU bus mode has to be changed from the fast bus
mode
to the asynchronous bus mode using following instructions:

MMU_SetAsyncBusMode
mrc  p15, 0, r0, c1, c0, 0
orr  r0, r0, #R1_nF:OR:R1_iA
mcr  p15,0, r0, c1, c0, 0
 其中的“R1_nF:OR:R1_iA”等於0xc0000000。意思就是說,當HDIVN = 1時,CPU bus mode需要從原來的“fast bus mode”改為“asynchronous bus mode”。 



        @ now, CPU clock is 200 Mhz
        mov r1, #0x4C000000
        ldr r2, mpll_200mhz
        str r2, [r1, #0x04] //0x4C000004為MPLLCON暫存器, 對於MPLLCON暫存器,[19:12]為MDIV,[9:4]為PDIV,[1:0]為SDIV。有如下計算公式:
  MPLL(FCLK) = (m * Fin)/(p * 2^s) 其中: m = MDIV + 8, p = PDIV + 2 對於本開發板,Fin =  12MHz,MPLLCON設為0x5c0040,可以計算出FCLK=200MHz,再由CLKDIVN的設定可知:HCLK=100MHz,PCLK=50MHz。

# 164 "arch/s3c2410/head.S"
        bl memsetup

        @ Check if this is a wake-up from sleep
        ldr r1, PMST_ADDR  //0x560000B4為GSTATUS2暫存器,裡面[0:2]三位有效
        ldr r0, [r1] //將該暫存器中的值取出來儲存到r0中
        tst r0, #((1 << 1)) //測試r0的第一位。這位是Power_OFF reset. The reset after the wakeup from Power_OFF mode.The setting is cleared by writing "1" to this bit.TST指令用於把一個暫存器的內容和另一個暫存器的內容或立即數進行按位的與運算,並根據運算結果更新CPSR中條件標誌位的值。
        bne WakeupStart

        @ All LED on 這裡就需要對GPIO口進行控制
        mov r1, #0x56000000 
        add r1, r1, #0x50 //0x56000050是GPFCON用來配置port F埠
        ldr r2,=0x55aa
        str r2, [r1, #0x0] //設定為0101010110101010因為每兩位用來控制一個引腳,也就是將GPF4-GPF7設定為輸出,將GPF0-GPF3設定為中斷
        mov r2, #0xff
        str r2, [r1, #0x8]  //0x56000058為GPFUP為port F的上拉暫存器,全設定為1表示禁止上拉功能
        mov r2, #0x00
        str r2, [r1, #0x4]  //0x56000054是GPFDAT,總共8位有效,每位控制一個引腳,主要是將GPF4-GPF7資料位全設定為0而這四個引腳是用來控制板子上4個LED,置0則表示亮。
# 230 "arch/s3c2410/head.S"
        @ set GPIO for UART
        mov r1, #0x56000000
        add r1, r1, #0x70 //0x56000070為GPHCON 用來配置port H 而port H主要就是來控制UART的各個引腳如:UART中接收和傳送埠RXDn和TXDn,當然還有自動流控制模式的nRTS0和nCTS0埠
        ldr r2, gpio_con_uart  //我們可以看到 gpio_con_uart: .long vGPHCON  gpio_up_uart: .long vGPHUP 而在platform中的smdk2410.h中定義了這兩個值#define vGPHCON   0x0016faaa 表示GPHCON控制11個引腳,如GPH2若設定為10則表示TXD0.類似,具體的檢視資料手冊
#define vGPHUP   0x000007ff //同樣將這11位的引腳上拉禁止

        str r2, [r1, #0x0]
        ldr r2, gpio_up_uart
        str r2, [r1, #0x8] //上面也是來配置串列埠所用到的GPIO口,因為串列埠的輸入輸出口都是利用到GPIO
        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開始呼叫C函式之前就需要將一些引數準備好,如堆疊要準備好函式呼叫時需要進出棧
        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:  //對ENTRY(memsetup)巨集的展開
        @ initialise the static memory
        //同樣在這裡是對記憶體控制中用到的13個暫存器進行初始化
        @ 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
@下面的將vivi從flash中搬移到sdram中來執行,很重要
copy_myself:
        mov r10, lr  //將返回地址儲存到r10,為了執行完後返回到主程式

        @ reset NAND
        mov r1, #0x4E000000
        ldr r2, =0xf830 @ initial value 初始化NFCONF暫存器,至於為什麼設定為0xf830前面在NAND裡面講過
        str r2, [r1, #0x00]
        ldr r2, [r1, #0x00]
        bic r2, r2, #0x800 @ enable chip 也就是相當於NFCONF &= ~0x800 將第15為置為1使能NAND FLASH
        str r2, [r1, #0x00]
        mov r2, #0xff @ RESET command
        strb r2, [r1, #0x04] //向NFCMD暫存器中置0xff也就是reset命令
        mov r3, #0 @ wait //下面幾句是一個延時程式用來等待幾秒,等到NAND 準備好
1: add r3, r3, #0x1
        cmp r3, #0xa
        blt 1b
2: ldr r2, [r1, #0x10] @ wait ready
        tst r2, #0x1 //檢查NFSTAT暫存器的第0位是否為1也就是是否準備好
        beq 2b //沒有準備好則繼續等待
        ldr r2, [r1, #0x00]
        orr r2, r2, #0x800 @ disable chip 相當於NFCONF |= 0x800 禁止掉NAND FLASH等到要使用FLASH時再使能
        str r2, [r1, #0x00]

        @ get read to call C functions (for nand_read())
        ldr sp, DW_STACK_START @ setup stack pointer //每次需要從彙編調到C函式時 都需要設定好堆疊
        mov fp, #0 @ no previous frame, so fp=0

        @ copy vivi to RAM 之前都是一些初始化,下面才開始利用C函式來真正開始拷貝
        ldr r0, =(0x30000000 + 0x04000000 - 0x00100000)//DRAM_BASE + DRAM_SIZE - VIVI_RAM_SIZE,為什麼這裡不是將vivi拷貝到sdram的起始地址而是拷貝到64MB的sdram的最後1M的區域,可能是這裡的sdram採用高階模式,將映象從高地址向低地址存放
        mov r1, #0x0 //r1則是vivi的起始地址,也就是flash上的0x0地址
        mov r2, #0x20000 //上面三句都是用來為呼叫nand_read_ll函式準備好引數,r2表示拷貝大小
        bl nand_read_ll //這個c函式在arch/s3c2410/nand_read.c中 nand_read_ll就不具體分析了在nand裡面有講過

        tst r0, #0x0  //為什麼要比較r0與上0,等於0的話 則去執行ok_nand_read,在這裡r0是nand_read_ll函式的返回值,拷貝成功則返回0,所以這就是為什麼將r0和0比較
        beq ok_nand_read //ok_nand_read子程式用來比較拷貝到sdram最後1MB的vivi和原始的存放在flash上的vivi是否相同,檢查拷貝是否成功,vivi在sdram中的位置也就是離0x34000000地址前1MB的位置也就是0x33f00000
# 441 "arch/s3c2410/head.S"
ok_nand_read:

        @ verify
        mov r0, #0  //r0這裡為flash上vivi的起始地址
        ldr r1, =0x33f00000 //r1這裡為拷貝到sdram上vivi的起始地址
        mov r2, #0x400 @ 4 bytes * 1024 = 4K-bytes //要比較多少個位元組數
go_next:
        ldr r3, [r0], #4 //將r0對應地址的值取出來儲存到r3中,然後r0自加4個位元組
        ldr r4, [r1], #4
        teq r3, r4  //測試r3和r4是否相等
        bne notmatch //若不相等,則跳到notmarch處
        subs r2, r2, #4 //將比較總位元組數減去4個位元組,已經比較了4個位元組
        beq done_nand_read //若r2為0,則表示已經比較完畢,跳轉到done_nand_read處
        bne go_next //r2若還不等於0則繼續取值比較
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 r0!,{r2-r9} //將r2-r9也就是0賦值給從r0為記憶體起始地址的連續的8*4個位元組中
        subs r1, r1, #(8 * 4) //每次清除32個位元組
        bne clear_loop


        mov pc, lr
# 613 "arch/s3c2410/head.S"
@ Initialize UART
@
@ r0 = number of UART port
InitUART: //這裡也不細講了,在UART章節中已經詳細的講解了每個暫存器的設定
        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】則是利用條件編譯來選擇適合自己開發板的標頭檔案,如: #ifdef CONFIG_S3C2410_SMDK
#include "platform/smdk2410.h"
#endif
本開發板的標頭檔案是【include/platform/smdk2410.h】,主要是一些暫存器的初始值(以v開頭)和一些相關的地址等等的定義。一般開發板不同,都是修改此檔案相應的部分。這個標頭檔案很重要,因為我們一般要將vivi移植到某個類似於smdk2410的開發板時都只是在這個標頭檔案中修改一些暫存器的初始值和一些暫存器的地址值還有記憶體分配的地址值等等 2、關於中斷向量表     開始