<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          s3c6410 uboot代碼分析

          作者: 時(shí)間:2016-11-09 來(lái)源:網(wǎng)絡(luò) 收藏
          以下用以記錄uboot代碼的分析過(guò)程,目標(biāo)是s3c6410,如有錯(cuò)誤,歡迎指正。

          強(qiáng)調(diào),內(nèi)容與三星原廠提供的uboot-1.1.6有更改的地方,因?yàn)橥饨油庠O(shè)的區(qū)別,特別是nand_flash、外接網(wǎng)卡芯片和LCD芯片

          本文引用地址:http://www.ex-cimer.com/article/201611/317904.htm

          以下純代碼情景分析,請(qǐng)結(jié)合uboot的功能結(jié)構(gòu)圖和內(nèi)存分布圖查看代碼,這樣會(huì)更加容易理解。

          s3c-u-boot-1.1.6源代碼可以在三星下面的網(wǎng)站獲得,但前提是你有官方的email。

          還是百度google搜一下吧,當(dāng)然我這也是有的哦。

          http://www.samsung.com/global/business/semiconductor/productInfo.do?fmly_id=835&partnum=S3C6410




          功能結(jié)構(gòu)圖(上圖) uboot內(nèi)存分布圖(上圖)

          1.start.s代碼分析(第一階段)

          /*以下是具有arm特色的異常向量表,為中斷異常準(zhǔn)備 */

          --------------------

          .globl _start
          _start: breset
          ldrpc, _undefined_instruction
          ldrpc, _software_interrupt
          ldrpc, _prefetch_abort
          ldrpc, _data_abort
          ldrpc, _not_used
          ldrpc, _irq
          ldrpc, _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
          _pad:
          .word 0x12345678 /* now 16*4=64 */
          .global _end_vect
          _end_vect:

          .balignl 16,0xdeadbeef

          --------------------

          /*當(dāng)發(fā)生中斷異常時(shí),pc會(huì)跳轉(zhuǎn)到.word的后面地址處 處理異常,

          undefined異常由arm核譯碼單元檢測(cè),并觸發(fā)未定義指令異常請(qǐng)求,硬件設(shè)置pc的值為0x4,強(qiáng)制程序從內(nèi)存0x4地址執(zhí)行指令;

          0x8存放軟件中斷處理指令,arm中使用swi指令時(shí)觸發(fā)軟件中斷,硬件設(shè)置PC的值為0x8,同時(shí)進(jìn)入系統(tǒng)模式,多用在系統(tǒng)庫(kù)的編寫(xiě);

          prefetch異常,預(yù)取指中止異常,導(dǎo)致正在取的指令無(wú)法正常取出,這里需要注意流水線造成的pc值;

          data中止,無(wú)法獲取數(shù)據(jù),產(chǎn)生的原因有可能是內(nèi)存未準(zhǔn)備好、內(nèi)存無(wú)讀或?qū)憴?quán)限等一些原因產(chǎn)生的異常;

          0x14暫時(shí)未使用;

          0x18提供系統(tǒng)硬件中斷跳轉(zhuǎn)接口,一般我們的處理器都會(huì)引出很多的外部中斷線,在這里能做的就是判斷系統(tǒng)中斷線產(chǎn)生的中斷,注冊(cè)中斷,初始化中斷,調(diào)用中斷函數(shù)等等;

          0x1c地址為_(kāi)fiq快速中斷,一個(gè)系統(tǒng)在中斷流水線上可能產(chǎn)生很多中斷,但快中斷只會(huì)有一個(gè)

          */

          --------------------

          _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
          _pad:
          .word 0x12345678 /* now 16*4=64 */
          .global _end_vect
          _end_vect:

          .balignl 16,0xdeadbeef

          --------------------

          /*

          _TEXT_BASE標(biāo)號(hào)所代表的是uboot代碼的運(yùn)行地址,對(duì)于s3c6410

          系統(tǒng)來(lái)說(shuō),如果nand flash啟動(dòng)方式,系統(tǒng)會(huì)把0xc000000里面前4KB的內(nèi)容映射到引導(dǎo)鏡像區(qū),即0x0地址,但是我們需要把

          uboot代碼放到我們的SDRAM,原因是我們代碼里面需要對(duì)變量做更改并且增加代碼執(zhí)行效率等

          下面代碼的含義是定義uboot程序執(zhí)行的運(yùn)行地址,值為0xc7e00000,.word后面的值TEXT_BASE在編譯的時(shí)候,

          通過(guò)向編譯器傳遞參數(shù)獲得,-DTEXT_BASE方式向編譯器傳遞宏參,在編譯的時(shí)候可以注意下編譯的時(shí)候都會(huì)指定它的值,值得定義在

          config.mk中,Makefile會(huì)包含它。

          */

          --------------------

          _TEXT_BASE:
          .wordTEXT_BASE

          --------------------

          /*

          在uboot里面會(huì)開(kāi)啟MMU,下面是在MMU開(kāi)啟前uboot在內(nèi)存存放的真實(shí)物理地址,值為0x57e00000。強(qiáng)調(diào)一下,我們做的開(kāi)發(fā)板的SDRAM在DMC1上,即訪問(wèn)物理內(nèi)存的實(shí)際物理地址從0x50000000開(kāi)始,SDRAM的大小為256M,正好是一個(gè)DMC1,所以內(nèi)存的訪問(wèn)地址就是0x50000000-0x6FFFFFFF之間了。

          */

          --------------------

          _TEXT_PHY_BASE:
          .wordCFG_PHY_UBOOT_BASE

          --------------------

          /*

          這個(gè)不解釋也是可以的,但是還是要解釋。很多人對(duì)_start的值有疑惑,認(rèn)為是0x0,因?yàn)榭吹絖start的標(biāo)號(hào)在代碼段最開(kāi)始處,其實(shí)是錯(cuò)誤的,匯編代碼里面的標(biāo)號(hào)是和編譯時(shí)指定的運(yùn)行地址有關(guān)系的。我們?cè)诰幾g程序的時(shí)候會(huì)通過(guò)-DTEXT_BASE=0xc7e00000參數(shù)告訴編譯器我們程序?qū)?huì)運(yùn)行在0xc7e00000地址,那么自然編譯器會(huì)認(rèn)為代碼開(kāi)始的時(shí)候就運(yùn)行在這個(gè)地址,那么_start的值自然就是0xc7e00000了??偨Y(jié)之,標(biāo)號(hào)的值與編譯時(shí)指定的程序地址有關(guān)系,而與程序?qū)嶋H存放在內(nèi)存出的位置無(wú)關(guān)。小心使用哦。特別在使用一些偽指令的時(shí)候

          */

          --------------------

          .globl _armboot_start
          _armboot_start:
          .word _start

          --------------------

          /*

          下面的代碼__bss_start的值是在u-boot.lds腳本里面定義的,雖然沒(méi)給值,但是你要知道文件的大小和位置是由

          編譯器指定的,那么還需要我們告訴它值嗎?所以沒(méi)值勝有值啦,由編譯時(shí)編譯器決定它們的值

          */

          --------------------

          .globl _bss_start
          _bss_start:
          .word __bss_start

          .globl _bss_end
          _bss_end:
          .word _end

          --------------------

          /*

          uboot開(kāi)始執(zhí)行的第二條代碼處即在這里了,下面的代碼使得cpu的模式為管理模式,如果想使得為cpu為管理模式,需要保證cpsr寄存

          器的最低5位為10011,下面是把0xd3的值賦值給cpsr,0xd3即1101 0011,最高兩位置1的意思為關(guān)閉中斷和快中斷,這是為了防止代碼

          正在執(zhí)行時(shí),產(chǎn)生外部中斷,導(dǎo)致程序跳轉(zhuǎn)到異常向量表而無(wú)法正常按順序執(zhí)行。5位為0的意思是cpu的狀態(tài)為arm狀態(tài),如果是1則cpu進(jìn)入thumb態(tài),thumb態(tài)處理16位指令代碼和數(shù)據(jù)。

          */

          --------------------

          reset:

          mrsr0,cpsr
          bicr0,r0,#0x1f
          orrr0,r0,#0xd3
          msrcpsr,r0

          --------------------

          /* 以下標(biāo)號(hào)所在處的代碼比較多,將做逐步分析,這段代碼主要的工作也就是改了一些硬件寄存器和內(nèi)存初始化工作 */

          --------------------

          cpu_init_crit:

          --------------------

          /*
          指令的含義為刷新指令和數(shù)據(jù)緩存。mcr的意思是把a(bǔ)rm寄存器的值賦值給coprocesser寄存器,拿第一條指令來(lái)說(shuō),

          p15代表協(xié)處理器,0為一定的值,指令中0b0000四位來(lái)表示,現(xiàn)在無(wú)具體作用,如果不是0則結(jié)果未知,后面的r0是即將寫(xiě)入

          c7目標(biāo)寄存器中的值,后面還有個(gè)c7所代表的意思為額外操作碼,如果不是c0,則表示的是同一個(gè)寄存器的不同物理寄存器,因?yàn)?/p>

          同一個(gè)寄存器的名字并不代碼通一個(gè)物理內(nèi)存,我們?cè)趯W(xué)rpsr的時(shí)候應(yīng)該知道這點(diǎn),最后的0提供附加信息,用于區(qū)分同一寄存器的

          不同物理寄存器,如無(wú)附加信息,請(qǐng)保持為0值,否則結(jié)果不可預(yù)測(cè)

          下面三行代碼不難看出,c7、c8的值被清為0,為什么要清為零呢,你需要去看arm1176jzf-s芯片手冊(cè)了,其中是有說(shuō)明的,不再累述,

          arm1176jzf-sarm核芯片手冊(cè)下載地址httop://www.arm.com,也可以與本人聯(lián)系獲取。
          */

          --------------------
          movr0, #0
          mcrp15, 0, r0, c7, c7, 0/* flush v3/v4 cache */
          mcrp15, 0, r0, c8, c7, 0/* flush v4 TLB */

          --------------------

          /*
          實(shí)在不想解釋這段,因?yàn)橐郧翱催^(guò)芯片手冊(cè)的解釋,且不止一遍,對(duì)于這里面要更改的內(nèi)容就是不能全部記下來(lái),和工作有關(guān)了,

          不能全心搞這塊內(nèi)容,最多2個(gè)月左右的時(shí)間能回來(lái)回顧一下了??傊€是去查arm11核芯片手冊(cè),因?yàn)橐韵赂牡膬?nèi)容是協(xié)處理器

          c1,那么你就該去查c1是用來(lái)干什么的。查看得知,是控制寄存器,查看手冊(cè)是online books12.2.2 Primary register allocation

          一節(jié),其中13,9,8位為V、R、S:V位是對(duì)高端異常向量表的支持,如果選擇0異常向量表為0x00000000-0x0000001c,如果選擇

          1異常向量表就是FFFF0000-FFFF001c;R位用于ROM保護(hù)的,具體的還要與c5里面的配合,這都是MMU惹的禍,很煩,但是現(xiàn)在

          我們還沒(méi)有講到MMU,所以為什么這樣做,也必須到講到MMU的時(shí)候才見(jiàn)分曉了,S在這里面的意思也是用于系統(tǒng)保護(hù)的,和MMU

          又是有很大的關(guān)系,好吧,后面會(huì)找MMU算賬的,這里就先不深入了,接下來(lái)再分析下下面的指令含義

          bicr0, r0, #0x00000087@ clear bits 7, 2:0(B--- -CAM) 的B位為0表示支持小little-endian,1表示支持big-endian格式的系統(tǒng)內(nèi)存

          CAM為第三位,M為0代表禁止MMU,反之打開(kāi),A代表地址對(duì)齊檢查,0代表禁止,C代表指令數(shù)據(jù)cache控制,0為禁止

          orrr0, r0, #0x00000002@ set bit 2 (A) Align 這段指令又比較犯賤了,打開(kāi)地址對(duì)齊檢查了,這是應(yīng)該的O(∩_∩)O~,后面又

          設(shè)置12位為1,含義是如果數(shù)據(jù)cache和指令cache是分開(kāi)的話,這里面置1的含義將會(huì)打開(kāi)指令緩存

          */

          --------------------
          mrcp15, 0, r0, c1, c0, 0
          bicr0, r0, #0x00002300@ clear bits 13, 9:8 (--V- --RS)
          bicr0, r0, #0x00000087@ clear bits 7, 2:0 (B--- -CAM)
          orrr0, r0, #0x00000002@ set bit 2 (A) Align
          orrr0, r0, #0x00001000@ set bit 12 (I) I-Cache
          mcrp15, 0, r0, c1, c0, 0

          --------------------

          /*

          以下代碼的作用是為了給256M的內(nèi)存在MMU開(kāi)啟的時(shí)候把0x70000000作為重映射的基地址

          c15協(xié)處理器寄存器在s3c6410上有特殊作用,它是外部?jī)?nèi)存端口映射寄存器,32位,在開(kāi)關(guān)MMU的時(shí)候發(fā)生作用,且優(yōu)先級(jí)最高

          這里的0x70000000為外部端口的基地址,0x13的二進(jìn)制為0x10011,0x10011的意思為256M,代表映射的

          大小為256M,0x10010為128M。假如你沒(méi)開(kāi)MMU,PHY和Peri port映射的地址將相同。通過(guò)下面的內(nèi)容后,我們知道我們?cè)瓉?lái)uboot

          代碼是放置到0x57e00000的,現(xiàn)在便只能通過(guò)0x57e00000+0x70000000虛擬地址來(lái)訪問(wèn)uboot起始地址了。

          使用C15的方法是:

          1.Opcode_1 set to 0

          2.CRn set to c15

          3.CRm set to c2

          4.Opcode_2 set to 4

          還有問(wèn)題請(qǐng)參考arm1176jzfs芯片手冊(cè)



          */

          --------------------

          /* Peri port setup */
          ldrr0, =0x70000000
          orrr0, r0, #0x13
          mcrp15,0,r0,c15,c2,4 @ 256M(0x70000000-0x7fffffff)

          --------------------

          /*

          下面是一條跳轉(zhuǎn)指令,代碼這里不貼,但是其中的代碼很重要,在lowlevel.S中實(shí)現(xiàn)比如說(shuō)點(diǎn)亮LED燈、關(guān)閉watchdog、關(guān)閉中斷、系統(tǒng)

          時(shí)鐘初始、nand flash初始化、內(nèi)存控制器初始化。不過(guò)說(shuō)實(shí)在的,去仔細(xì)分析這些初始化的過(guò)程,對(duì)于你對(duì)如何控制硬件有很大的幫

          助,對(duì)于這個(gè)函數(shù),所要說(shuō)的東西太多,會(huì)在后面的文章中單獨(dú)分析它,現(xiàn)在先知道功能就好,沒(méi)有它代碼無(wú)法啟動(dòng)。

          */

          --------------------

          bllowlevel_init

          --------------------

          /*跳轉(zhuǎn)出來(lái)以后,繼續(xù)執(zhí)行下面的代碼,下面的代碼是判斷程序是否已經(jīng)在ram中了,在的話就不拷貝,直接跳轉(zhuǎn)到after_copy了,否則

          繼續(xù)執(zhí)行下面的代碼*/

          --------------------

          ldrr0, =0xff000fff
          bicr1, pc, r0/* r0 <- current base addr of code */
          ldrr2, _TEXT_PHY_BASE/* r1 <- original base addr in ram */
          bicr2, r2, r0/* r0 <- current base addr of code */
          cmp r1, r2 /* compare r0, r1 */
          beq after_copy/* r0 == r1 then skip flash copy */

          --------------------

          /*

          下面代碼通過(guò)函數(shù)copy_from_nand函數(shù)把代碼拷貝到ram中。steppingstone只能拷貝4KB,我們需要把所有的代碼搬運(yùn)到內(nèi)存中哦

          我們知道s3c6410可以通過(guò)SD、onenand、nand啟動(dòng),但是我們這里做了簡(jiǎn)化,先只從nand啟動(dòng),以后會(huì)再增加SD卡啟動(dòng)

          copy_from_nand代碼也在start.S中,做了修改以適合大頁(yè)訪問(wèn),如有需要請(qǐng)留言告知,將添加copy_from_nand代碼分析

          */

          --------------------

          #ifdef CONFIG_BOOT_NAND
          movr0, #0x1000
          blcopy_from_nand
          #endif

          --------------------

          /*SD卡啟動(dòng)方式,這個(gè)宏我沒(méi)有定義,先保留吧*/

          --------------------

          #ifdef CONFIG_BOOT_MOVINAND
          ldrsp, _TEXT_PHY_BASE
          blmovi_bl2_copy
          bafter_copy
          #endif

          --------------------

          /*這里我啥都沒(méi)做*/

          after_copy:

          /*

          打開(kāi)MMU功能

          協(xié)處理器c3的作用是存儲(chǔ)的保護(hù)和控制,用在MMU中為內(nèi)存的域訪問(wèn)控制

          c3為32位寄存器,每?jī)晌粸橐粋€(gè)訪問(wèn)控制特權(quán),0x00代表沒(méi)有訪問(wèn)權(quán)限,這時(shí)候訪問(wèn)將失效;0x01為客戶類型,將根據(jù)

          地址變換條目中的訪問(wèn)控制位決定是否允許特定內(nèi)存訪問(wèn);0x10是保留的,暫時(shí)沒(méi)有使用,0x11為管理者權(quán)限,不考慮

          地址變換條目中的權(quán)限控制位,將不會(huì)訪問(wèn)內(nèi)存失效。

          ldrr5, =0x0000ffff
          mcrp15, 0, r5, c3, c0, 0,代碼的含義為設(shè)置高8個(gè)域無(wú)訪問(wèn)權(quán)限,低8個(gè)域?yàn)楣芾碚邫?quán)限。

          接著下面通過(guò)mcrp15, 0, r1, c2, c0, 0指令給c2賦值,c2用于保存頁(yè)表基地址。所謂頁(yè)表基地址即是虛實(shí)轉(zhuǎn)換的內(nèi)存頁(yè)表的首地址。

          這里r1的值賦值給了c2,r1的值為0x57exxxxx,c2高14位是儲(chǔ)存頁(yè)表的基地址

          最后代碼很簡(jiǎn)單,打開(kāi)MMU。

          */

          --------------------

          #ifdef CONFIG_ENABLE_MMU
          enable_mmu:
          /* enable domain access */
          ldrr5, =0x0000ffff
          mcrp15, 0, r5, c3, c0, 0@ load domain access register

          /* Set the TTB register */
          ldrr0, _mmu_table_base
          ldrr1, =CFG_PHY_UBOOT_BASE
          ldrr2, =0xfff00000
          bicr0, r0, r2
          orrr1, r0, r1
          mcrp15, 0, r1, c2, c0, 0

          /* Enable the MMU */
          mmu_on:
          mrcp15, 0, r0, c1, c0, 0
          orrr0, r0, #1/* Set CR_M to enable MMU */
          mcrp15, 0, r0, c1, c0, 0
          nop
          nop
          nop
          nop
          #endif

          --------------------

          /*

          堆棧初始化代碼,我們?cè)谶@里定義了CONFIG_MEMORY_UPPER_CODE

          sp的值為0xC7FFFFE8

          */

          --------------------

          skip_hw_init:
          /* Set up the stack */
          stack_setup:
          #ifdef CONFIG_MEMORY_UPPER_CODE
          ldrsp, =(CFG_UBOOT_BASE + CFG_UBOOT_SIZE - 0xc)
          #else
          ldrr0, _TEXT_BASE/* upper 128 KiB: relocated uboot */
          subr0, r0, #CFG_MALLOC_LEN/* malloc area */
          subr0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */
          #ifdef CONFIG_USE_IRQ
          subr0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
          #endif
          subsp, r0, #12/* leave 3 words for abort-stack */

          #endif

          --------------------

          /*清零BSS段內(nèi)容為0 */

          --------------------

          clear_bss:
          ldrr0, _bss_start/* find start of bss segment */
          ldrr1, _bss_end/* stop here */
          mov r2, #0x00000000/* clear */

          clbss_l:
          strr2, [r0]/* clear loop... */
          addr0, r0, #4
          cmpr0, r1
          bleclbss_l

          --------------------

          /*跳轉(zhuǎn)到uboot代碼的第二個(gè)階段,第二階段基本上都是用C實(shí)現(xiàn)的,幸好前面sp的值已經(jīng)設(shè)置好了 */

          --------------------

          ldrpc, _start_armboot

          _start_armboot:
          .word start_armboot

          --------------------

          2.第二階段代碼分析(代碼在lib_arm目錄下的board.c里面,start_armboot函數(shù))

          1)初始化CPU及外圍硬件


          init_fnc_t init_fnc_ptr;
          char *s;
          #ifndef CFG_NO_FLASH
          ulong size;
          #endif

          #if defined(CONFIG_VFD) || defined(CONFIG_LCD)
          unsigned long addr;
          #endif

          #if defined(CONFIG_BOOT_MOVINAND)
          uint *magic = (uint *) (PHYS_SDRAM_1);
          #endif

          /* Pointer is writable since we allocated a register for it */
          #ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */
          ulong gd_base;

          gd_base = CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE - sizeof(gd_t);
          #ifdef CONFIG_USE_IRQ
          gd_base -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ);
          #endif
          gd = (gd_t*)gd_base;
          #else
          gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
          #endif

          /* compiler optimization barrier needed for GCC >= 3.4 */
          __asm__ __volatile__("": : :"memory");

          memset ((void*)gd, 0, sizeof (gd_t));
          gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
          memset (gd->bd, 0, sizeof (bd_t));

          monitor_flash_len = _bss_start - _armboot_start;

          for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
          if ((*init_fnc_ptr)() != 0) {
          hang ();
          }
          }

          解釋:定義二級(jí)指針init_fnc_ptr指向一個(gè)存放函數(shù)指針的數(shù)組,init_fnc_ptr是typedef int (init_fnc_t) (void)類型,即函數(shù)類型,init_fnc_ptr可以指向一個(gè)沒(méi)有參數(shù),返回值為int型的函數(shù)指針的地址(很繞哦,),我們看上面代碼最后的for循環(huán)init_fnc_ptr = init_sequence,if中會(huì)使用(*init_fnc_ptr)()方式調(diào)用init_sequence中的函數(shù)(函數(shù)名可以看為一個(gè)地址),如果返回值不是0,則執(zhí)行hang報(bào)錯(cuò)。

          因?yàn)槲覀兌x了CONFIG_MEMORY_UPPER_CODE宏,所以gd = (gd_t*)gd_base,由gd_base的值我們知道,malloc區(qū)域、stack區(qū)域、bdinfo數(shù)據(jù)在內(nèi)存的位置是放在upper of uboot。

          __asm__ __volatile__("": : :"memory");這條是內(nèi)嵌匯編,請(qǐng)查看另一篇介紹內(nèi)嵌匯編的博文。

          gd->bd指針指向數(shù)據(jù)類型為bd_t的結(jié)構(gòu)體,bd_t結(jié)構(gòu)體記錄開(kāi)發(fā)板的參數(shù),例如串口波特率、ip地址、機(jī)器類型、啟動(dòng)參數(shù)、環(huán)境變量位置等。

          下面分析for循環(huán)執(zhí)行的函數(shù):

          cpu_init:因?yàn)槲覀儧](méi)有定義CONFIG_USE_IRQ,所以這個(gè)函數(shù)直接返回0

          board_init:


          函數(shù)內(nèi)首先執(zhí)行dm9000_pre_init()函數(shù),因?yàn)槲覀儼袲M9000AEP網(wǎng)卡映射到內(nèi)存的Xm0CSn[1]上,所以我們要設(shè)置訪問(wèn)CSn[1]的方式,SROM_BW_REG &= ~(0xf << 4);SROM_BW_REG |= (1<<7) | (1<<6) | (1<<4);兩條代碼的含義為設(shè)置0x70000000控制器的CSn[1]訪問(wèn)方式為nBE enable、wait enable、16位數(shù)據(jù)總線訪問(wèn)模式,SROM_BC1_REG = ((DM9000_Tacs<<28)+(DM9000_Tcos<<24)+(DM9000_Tacc<<16)+(DM9000_Tcoh<<12)+(DM9000_Tah<<8)+(DM9000_Tacp<<4)+(DM9000_PMC));這句話的意思是設(shè)置訪問(wèn)的時(shí)序,s3c6410 datasheet中已經(jīng)給出了時(shí)序代碼,歡迎查看哦。因?yàn)槲业膠c6410開(kāi)發(fā)箱接了4.3的TFT LCD,所以在代碼中增加了對(duì)LCD的配置工作,代碼如下:

          writel(readl(MIFPCON) & (~(1 << 3)), MIFPCON);

          writel(readl(MIFPCON) & (~(3 << 0)) | 0x1, SPCON);

          writel(0xaaaaaaaa, GPICON);
          writel(0xaaaaaa, GPJCON);

          以上四行代碼分別對(duì)MIFPCON、SPCON、GPICON、GPJCON四個(gè)寄存器賦值。為什么賦值?查看s3c6410 datasheet手冊(cè)14.5.1節(jié),給出了DisplayController的引腳配置,MIFPCON的[3]位設(shè)置為0(normal mode)instead of “1”(by-pass mode),SPCON[1:0]位的值設(shè)置為“01”(useRGB I/F Style)or “00” to use Host I/F Style,我們?cè)O(shè)置的是01。GPICON、GPJCON賦值的原因請(qǐng)看下面圖:

          第一張圖是LCD控制器接口連接原理圖,后面的是圖是芯片手冊(cè),通過(guò)兩個(gè)圖我們就知道為什么要寫(xiě)后面兩行代碼了吧。

          好了,73-74行一個(gè)是記錄機(jī)器類型,一個(gè)是指定向內(nèi)核傳參的地址。

          interrupt_init:


          184行:值0x0101的含義是設(shè)置env_init:如下圖,因?yàn)闆](méi)有定義ENV_IS_EMBEDDED,所有只是執(zhí)行了142-143,把環(huán)境變量的首地址賦值給gd->env_addr。

          init_baudrate:

          139行使用getenv_r函數(shù)在default_environment里找baudrate關(guān)鍵字,找到后把“=”號(hào)后面的值賦值給gd->baudrate,然后

          再放到gd->bd->bi_baudrate里面。simple_strtoul是uboot實(shí)現(xiàn)的字符串轉(zhuǎn)UL類型。

          serial_init:什么都沒(méi)做,保持默認(rèn)的8位數(shù)據(jù)、無(wú)奇偶校驗(yàn)、1 停止位、無(wú)開(kāi)始位。

          console_init_f:gd->have_console = 1就這一句話

          display_banner:串口打印uboot信息,就是uboot啟動(dòng)的時(shí)候我們看到的信息,這里使用的是printf,但是我們追進(jìn)去后,關(guān)注的函數(shù)

          應(yīng)該是serial_putc,它是真實(shí)向串口輸出一個(gè)字符的函數(shù),這個(gè)函數(shù)會(huì)遞歸調(diào)用,應(yīng)該說(shuō)自己調(diào)用自己,遇到n結(jié)束。

          print_cpuinfo:打印CPU信息,CPU型號(hào)和速度 CPU:...

          checkboard:打印開(kāi)發(fā)板信息 BOARD:...


          dram_init:

          記錄dram的起始地址,0x50000000,size為256M

          display_dram_config:因?yàn)闆](méi)有定義DEBUG,所以打印DRAM:256M

          2)配置malloc空間

          (因?yàn)镃ONFIG_LCD、CONFIG_VFD沒(méi)有定義,所以跳過(guò)這一部分)


          我們定義了UPPER_CODE,所以執(zhí)行第一個(gè)mem_malloc_init。這個(gè)函數(shù)的作用是記錄堆??臻g的起始地址、結(jié)束地址、當(dāng)前地址。

          3)啟動(dòng)設(shè)備初始化(SD、NAND、ONENAND)

          系統(tǒng)一開(kāi)始嘗試從SD卡啟動(dòng),因?yàn)楸酒榻B的是NANDFLASH啟動(dòng)方式,所以SD卡部分暫不分析,會(huì)單獨(dú)開(kāi)辟章節(jié)介紹s3c6410

          的SD卡啟動(dòng)方式(包括windows下SD flasher應(yīng)用程序的編寫(xiě)、SD卡硬件電路分析、SD寄存器操作和啟動(dòng)流程)。

          下面分析nand啟動(dòng)方式,我們?cè)谧约旱念^文件里定義了CONFIG_COMMANDS和CFG_CMD_NAND,所以會(huì)執(zhí)行nand_init函數(shù)

          分析代碼中的368行nand_init函數(shù),我們知道在uboot啟動(dòng)起來(lái)之后,會(huì)顯示NAND: 512M(你nandflash的大?。圆浑y想象,

          nand_init會(huì)向終端打印NAND大小的信息,以下是nand_init的實(shí)現(xiàn):


          nand_init函數(shù)的實(shí)現(xiàn)體在drivers/nand/nand.c中,nand_init函數(shù)不僅會(huì)打印出nandflash的大小,還會(huì)初始化描述nand的結(jié)構(gòu)體

          nand_info以及代表“nand”的設(shè)備結(jié)構(gòu)體nand_chip,這兩個(gè)結(jié)構(gòu)體前者是mtd層對(duì)設(shè)備的抽象和對(duì)塊設(shè)備的接口統(tǒng)一,后者是

          設(shè)備的實(shí)體,所有對(duì)設(shè)備的讀寫(xiě)控制操作都最終通過(guò)這個(gè)結(jié)構(gòu)體完成,下面我們開(kāi)始分析nand_init函數(shù):

          64~69行:從外表看,最后會(huì)執(zhí)行size += nand_info[i].size,由此最起碼可以猜測(cè)到這個(gè)函數(shù)會(huì)計(jì)算出nand的大小。那么是怎樣計(jì)算出

          來(lái)的呢,我們需要看nand_init_chip函數(shù),注意,在進(jìn)入這個(gè)函數(shù)之前,先看一下傳入的三個(gè)參數(shù),前兩個(gè)參數(shù)我們已經(jīng)介紹過(guò),第三

          個(gè)參數(shù)是nand的數(shù)據(jù)寄存器,訪問(wèn)地址為0x70200010。nand_init_chip函數(shù)會(huì)根據(jù)我們傳入的參數(shù),去查找對(duì)應(yīng)的nand設(shè)備,并初

          始化一些功能接口為以后對(duì)nand操作做準(zhǔn)備,下面看圖:

          47行:mtd->priv實(shí)現(xiàn)了mtd中間層對(duì)底層nand設(shè)備的接口,我們以后在訪問(wèn)nand硬件時(shí),通過(guò)mtd的priv成員可以快速找到我們的

          nand設(shè)備。

          49行:nand成員中保存了讀寫(xiě)nand數(shù)據(jù)的數(shù)據(jù)寄存器基地址,我們通過(guò)讀寫(xiě)base_addr中的數(shù)據(jù),實(shí)現(xiàn)對(duì)nand中數(shù)據(jù)的讀和寫(xiě),

          后面的__iomem是個(gè)宏定義,這樣定義的#define __iomem,只定義并沒(méi)有給值,所以沒(méi)有任何功能意義,但是對(duì)于我們?cè)诳创a

          的時(shí)候,很容易能判斷出后面的變量是IO地址空間的寄存器地址。

          50行:是對(duì)nand設(shè)備的初始化操作,我們進(jìn)入函數(shù)體

          820~823行:判斷是否是從nand_flash啟動(dòng)??磗3c6410寄存器就會(huì)明白:

          820行:NFCONF定義的宏,其實(shí)是取0x70200000地址里面的內(nèi)容,那么如果我們把OM跳線設(shè)置為nand啟動(dòng),這個(gè)[31]

          位的值就會(huì)為1,這樣的話NFCONF & 0x80000000的值就是1了,因而boot_nand的值為1;

          825行:清除0x70200004的[16]位,關(guān)閉軟件鎖存,如果此位設(shè)置為1,則NFSBLK(0x70200020)到NFEBLK

          (0x70200024)-1被開(kāi)啟,除了這部分區(qū)域,寫(xiě)或擦除命令是無(wú)效的,只有讀命令是有效的(NFSBLK和NFEBLK)為可編程

          可編程開(kāi)始和結(jié)束塊地址寄存器;

          826~827行:我們?cè)谶M(jìn)入這個(gè)函數(shù)的時(shí)候就做過(guò)了;

          828行:nand->cmd_ctrl = s3c_nand_hwcontrol,這個(gè)函數(shù)是用于向nand硬件發(fā)送命令的,比如發(fā)送00h,代表的是讀命令。

          829行:nand->dev_ready = s3c_nand_device_ready,是用于判斷nand芯片處于忙/可讀狀態(tài)的。s3c_nand_device_ready

          用一個(gè)循環(huán)去判斷NFSTAT(0x70000028)的最低位(RnB輸入引腳狀態(tài)),如果是1表示現(xiàn)在nand可讀,0代表正在忙不可讀

          830行:bbt(bad block table)壞塊表,因?yàn)槲覀儧](méi)用到,所以s3c_nand_scan_bbt函數(shù)會(huì)直接返回;

          我們沒(méi)有定義宏CFG_NAND_FLASH_BBT,所以nand->options|= NAND_SKIP_BBTSCAN,后面宏的含義代表在初始化期間

          將跳過(guò)壞塊掃描;

          839行:CFG_NAND_HWECC需要我們自己定義,含義代表使用錯(cuò)誤糾錯(cuò)碼;

          840行:代表使用NAND_FLASH模塊內(nèi)部的ECC模塊產(chǎn)生糾錯(cuò)碼;

          841行:nand->ecc.hwctl= s3c_nand_enable_hwecc,設(shè)置對(duì)ECC的控制,這個(gè)函數(shù)應(yīng)該在產(chǎn)生ECC編碼前被調(diào)用。這個(gè)函數(shù)的

          功能為確認(rèn)SLC FLASH或是MLC FLASH,SLC代表single layer cell,MCL為multi-level cell,有關(guān)SLC和MLC的區(qū)別在容量、可

          讀寫(xiě)總次數(shù)、讀寫(xiě)速度上,SLC的讀寫(xiě)速度要快于MLC,但MCL的容量要比SLC大很多,因?yàn)?cell可以容納4bits,有興趣可以查閱

          相關(guān)手冊(cè)。此函數(shù)還有一個(gè)功能為:1.初始化主區(qū)ECC解碼器/編碼器(向0x70200004的[5]位寫(xiě)1)2.開(kāi)啟主區(qū)ECC

          (向0x70200004的[7]位寫(xiě)0);

          842行:nand->ecc.calculate= s3c_nand_calculate_ecc,用于存儲(chǔ)產(chǎn)生的校驗(yàn)糾錯(cuò)碼;

          843行:nand->ecc.correct= s3c_nand_correct_data,用生成的ECC碼檢測(cè)是否有錯(cuò)誤,沒(méi)有則返回,具體內(nèi)容看函數(shù)說(shuō)明就好;

          845~849行:向nand發(fā)送4條命令,849行為等待設(shè)備準(zhǔn)備好;

          851~852行:將讀取到nand芯片的廠商信息和芯片ID編號(hào);

          854~859行:nand_flash_ids結(jié)構(gòu)體保存了很多公司生產(chǎn)的nand芯片信息和編號(hào),for循環(huán)將通過(guò)ID找到和我們板子匹配的NAND芯片;

          再往下雖然代碼挺多的,但不用擔(dān)心,只會(huì)執(zhí)行877行的nand->ecc.layout = &s3c_nand_oob_16,這是在定義oob信息;

          以下是nand芯片可以處理的命令以及命令的含義(下面是三星K9F4G08U0A-PCB0芯片的命令集)


          分析完board_nand_init函數(shù)后,我們繼續(xù)看nand_init_chip第52行,nand_scan函數(shù):

          這個(gè)函數(shù)的主要功能就是2768行的nand_scan_ident函數(shù),功能是填充mtd結(jié)構(gòu)體,配置對(duì)nand的接口。這樣下次在訪問(wèn)設(shè)備時(shí),可

          通過(guò)mtd層找到對(duì)應(yīng)的底層設(shè)備,我們看下nand_scan_ident函數(shù):


          到此,我們不再往下追函數(shù)了,

          2501行在設(shè)置mtd設(shè)備層的接口函數(shù),

          2504行nand_get_flash_type函數(shù)代碼比較多,主要的功能還是在獲得nand芯片的廠商信息和ID,并判斷是否支持,如果支持

          為這個(gè)nand設(shè)備和mtd填充一些功能接口函數(shù),

          我們?cè)賮?lái)看nand_scan_ident函數(shù)的后面代碼,2512行是for循環(huán),maxchips的值是nand_scan函數(shù)傳遞進(jìn)來(lái)的1,所以我們最后

          看到的i值為1,在2526行chip->numchips=1,mtd->size=512(我的nandflash型號(hào)是K9F4G08U0A-PCB0,大小512M,

          我們?cè)赽oard_nand_init函數(shù)中已經(jīng)在結(jié)構(gòu)體nand_flash_ids中找到我們的nand型號(hào),chipsize的值為512)。

          nand初始化分析完畢。。。


          4)環(huán)境變量初始化

          環(huán)境變量初始化,即start_armboot函數(shù)第379行的env_relocate ()函數(shù),這個(gè)函數(shù)實(shí)現(xiàn)體在env_common.c中,我們看真相:

          這個(gè)函數(shù)的功能其實(shí)就是讓env_ptr指向存放環(huán)境變量的首地址,并且填充env_ptr->data成員變量。

          208和212的兩個(gè)宏我們沒(méi)有定義,所以直接看223行,223行malloc一個(gè)CFG_ENV_SIZE的空間用于存放環(huán)境變量

          230行是在環(huán)境變量被遷移到內(nèi)存后,我們可以使用內(nèi)存中的環(huán)境變量

          由于gd->env_valid我們前面沒(méi)有賦值,所以232行if會(huì)執(zhí)行,打印236行的內(nèi)容

          240~244行是判斷默認(rèn)的環(huán)境變量是否大于我們限定的環(huán)境變量大小

          247行是把uboot代碼中已經(jīng)存在的default_environment指針?biāo)赶虻沫h(huán)境變量拷貝到env_ptr->data中

          253行是crc32的校驗(yàn),會(huì)對(duì)環(huán)境變量的內(nèi)容使用CRC32校驗(yàn)表做每8字節(jié)的DO1校驗(yàn),校驗(yàn)表定義在crc32.c的77行

          259行是把內(nèi)存中可操作的環(huán)境變量記錄在全局變量


          5)網(wǎng)絡(luò)初始化

          網(wǎng)絡(luò)初始化分為IP地址初始化和MAC地址初始化,看下圖:


          387行:IP地址初始化,getenv_IPaddr會(huì)返回IPaddr_t(unsigned long)類型的IP地址賦值給gd->bd->bi_ip_addr全局變量。

          進(jìn)入getenv_IPaddr函數(shù)會(huì)執(zhí)行return (string_to_ip(getenv(var))),我們先看getenv(var):

          497行:是給軟件狗準(zhǔn)備的,我的ZC6410板子上有硬件狗,所以這個(gè)WATCHDOG_RESET其實(shí)就是一個(gè)空的macro。

          499~510行:會(huì)查找環(huán)境變量中的name,name是傳遞進(jìn)來(lái)的ipaddr,進(jìn)入查找旅程env_get_char函數(shù),如果我們沒(méi)有忘記的話

          我們?cè)趀nv_relocate ()函數(shù)的230行執(zhí)行過(guò)env_get_char = env_get_char_memory,所以會(huì)執(zhí)行env_get_char_memory函數(shù),

          這個(gè)函數(shù)在env_common.c中實(shí)現(xiàn):


          186行的值同樣在env_relocate正確執(zhí)行過(guò)后,被賦值為1,返回gd->env_addr+index,gd->env_addr指向default_environment

          因?yàn)閐efault_environment是個(gè)指針數(shù)組,index代表數(shù)組的索引,即環(huán)境變量名得索引,看default_environment數(shù)組就能夠明白

          如果你已經(jīng)自己看過(guò)了這兩段for循環(huán),你可能知道外層for循環(huán)在遍歷default_environment數(shù)組,內(nèi)層判斷是否超出環(huán)境變量的

          大小。后面外層循環(huán)會(huì)執(zhí)行507行的envmatch函數(shù),這個(gè)函數(shù)是真正匹配getenv傳遞進(jìn)去的ipaddr參數(shù)的,里面使用while循環(huán)

          去找環(huán)境變量中的‘=’號(hào)前面的是否有ipaddr字符串,有則返回ipaddr在環(huán)境變量中的索引,比如ipaddr=“。。。”看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();