四极管:Windows CE stepldr源码----基于Windows CE 5.0 S3C2440 BSP

 

一、nboot与stepldr:

国内很多人做WinCE都是使用Samsung的2410或者2440入门的,所以对nboot和eboot是最熟悉的。eboot是微软在WinCE里面提供的开放源代码的一个bootloader的框架,因为它默认的是使用ethernet从PC下载image而得名,使用该框架,根据自己的硬件做一些修改就可以直接拿来用了,那么nboot又是怎么回事呢?

 

之所以需要nboot(注:三星的后续产品中,nboot已经改名为stepldr,ldr是looder的缩写,step是stepstone的意思,这是三星系列CPU为解决nand启动而内置的一小块RAM),是和硬件紧密相关的。我们在设计硬件的时候,ROM部分可以只使用norflash,也可以使用1片小容量的norflash+大容量的nandflash,还可以只使用nandflash。第一种方案,可以不用bootloader,也可以只使用eboot;第二种方案,把eboot放到norflash中,image放在nandflash中,并将硬件设置为norflash启动模式,也不用nboot。只有第3种方案,才需要使用nboot,这是为什么呢?

 

我们知道nandflash本身不能运行程序,它里面的内容必须拷贝到RAM中才能运行,但是CPU上电后,RAM中是空的,谁来执行这个拷贝的工作呢?三星的解决方案,就是内置了一小块RAM(stepstone),然后从硬件上实现CPU上电后先读取nand flash最开始的一段代码到stepstone中去(当然,要设置硬件为nandflash启动方式),然后从stepstone起始处(被设置为RAM的0地址)去执行。这个stepstone一般很小(2410,2440是4K),所以它没办法放下一个功能复杂的bootloader(比如eboot),只能放一个功能很简单的,这就是需要nboot的原因了。nboot的功能十分单一,就是从nandflash复制image到RAM中去,然后跳转执行。这里的image可以是eboot的(一般开发阶段这样做),也可以是OS的。

 

优龙的开发板提供了一种叫做BIOS的bootloader,它远远超出了4K的限制,但是还可以在nandflash启动方式下正常运行,这是为什么呢?原来,它实现了2次加载,也就是说CPU上电后自动加载了4K代码,这4K代码又将整个bootloader重新拷贝到RAM中再执行,要实现这样的功能要对链接器做一些设置,使“拷贝”功能的代码必须放到前4K里面去。

 

(以上stepldr的描述摘自:http://www.cnblogs.com/yashi88/archive/2010/02/11/1667548.html

 

S3C2440的DATESHEET中描述如下:

目前的NOR Flash 存储器价格较高,相对而言SDRAM 和NAND Flash 存储器更经济,这样促使了一些用户在NAND Flash 中执行引导代码,在SDRAM 中执行主代码。
S3C2440A引导代码可以在外部NAND Flash 存储器上执行。为了支持NAND Flash 的BootLoader,S3C2440A配备了一个内置的SRAM 缓冲器,叫做“Steppingstone”。引导启动时,NAND Flash 存储器的开始4K 字节将被加载到Steppingstone 中并且执行加载到Steppingstone 的引导代码。
通常引导代码会复制NAND Flash 的内容到SDRAM 中。通过使用硬件ECC,有效地检查NAND Flash 数据。在复制完成的基础上,将在SDRAM 中执行主程序。

当自动引导启动期间,ECC 不会去检测,所以,NAND Flash 的开始4KB 不应当包含位相关的错误。

二、Windows CE 5.0的启动过程:

stepldr----eboot----nk,CPU上电后先读取nand flash最开始的一段代码(stepldr)拷贝到S3C2440配备的名为“stepping stone”的SRAM中,然后从stepstone起始处(被设置为RAM的0地址)去执行。stepldr的功能十分单一,就是从nandflash复制eboot的image到RAM中去,然后跳转执行eboot。而eboot执行它的功能代码后跳转到OS的启动代码。

 

三、stepldr代码分析:

stepldr的代码位于{$BSP} /Src/Bootloader/Stepldr目录下。

先看sources文件的如下内容:

TARGETNAME=stepldr

TARGETTYPE=PROGRAM

RELEASETYPE=PLATFORM

EXEENTRY=StartUp

NOMIPS16CODE=1

 

从EXEENTRY=StartUp可知StartUp为入口函数。它的实现代码在/Src/Bootloader/Stepldr/startup.s中:

    STARTUPTEXT

    LEAF_ENTRY StartUp

   

    b     ResetHandler 

    b     .

    b     .

    b     .            

    b     .            

    b     .            

    b     .                   

b     .

 

STARTUPTEXT和LEAF_ENTRY:

这是两个宏,在Kxarm.h中有其定义。说实话,这汇编代码我对着指令手册也看不太懂。

 

STARTUPTEXT大概是指定一个段(CODE表示这个段式代码段),名称为.astart,2字节对齐。字符串变量AreaName的值为"|.astart|"。

       MACRO

       STARTUPTEXT

       AREA |.astart|,ALIGN=2,CODE

AreaName SETS "|.astart|"

       MEND

 

LEAF_ENTRY大概是赋值FuncName,PrologName,FuncEndName几个字符串变量,指定2字节对齐,导出FuncName,,然后$ FuncName的有效范围结束。VBar是一个全局变量,可是VBar:CC:"$Name":CC:VBar是什么意思啊?

       MACRO

       LEAF_ENTRY       $Name

FuncName SETS    VBar:CC:"$Name":CC:VBar

PrologName SETS "Invalid Prolog"

FuncEndName SETS    VBar:CC:"$Name":CC:"_end":CC:VBar

       ALIGN    2

       EXPORT $FuncName

$FuncName

       ROUT

       MEND

 

网上别人都说这就开始了StartUp,就先按这个理解,接着往下看吧。

b     ResetHandler

跳转到ResetHandler执行,其代码如下:

……

ResetHandler

 

    ldr          r0, =WTCON       ; disable the watchdog timer.

    ldr          r1, =0x0        

    str          r1, [r0]

 

    ldr          r0, =INTMSK      ; mask all first-level interrupts.

    ldr          r1, =0xffffffff

    str          r1, [r0]

 

    ldr          r0, =INTSUBMSK   ; mask all second-level interrupts.

    ldr          r1, =0x7fff

    str          r1, [r0]

 

 

    ldr   r0, = INTMOD

    mov       r1, #0x0          ; set all interrupt as IRQ (not FIQ)

    str   r1, [r0]

 

    ; CLKDIVN

    ldr   r0,=CLKDIVN

    ldr   r1,=0x5     ; 0x0 = 1:1:1  ,  0x1 = 1:1:2 , 0x2 = 1:2:2  ,  0x3 = 1:2:4,  0x4 = 1:4:4,  0x5 = 1:4:8, 0x6 = 1:3:3, 0x7 = 1:3:6

    str   r1,[r0]

 

       ldr   r1, =MISCCR                   ; MISCCR's Bit [22:20] -> 100

       ldr          r0, [r1]

       bic          r0, r0, #(7 << 20)

       orr          r0, r0, #(4 << 20)

       str          r0, [r1]

 

    ; MMU_SetAsyncBusMode FCLK:HCLK= 1:2

    ands       r1, r1, #0xe

    beq        %F5

    bl            MMU_SetAsyncBusMode

 

    

5

 

; TODO: to reduce PLL lock time, adjust the LOCKTIME register.

    ldr          r0, =LOCKTIME

    ldr          r1, =0xffffff

    str          r1, [r0]

 

    ; Configure the clock PLL.

    ;     

      [ {TRUE}

      

       ldr          r0, =UPLLCON         

    ldr          r1, =((26<<12)+(0x4<<4)+0x1)  ; Fin=16MHz, Fout=48MHz.    ;//c ksk 20051111

    str          r1, [r0]

 

       nop

       nop

       nop

       nop

       nop

       nop

       nop

       nop

 

       ldr          r0, =MPLLCON         

    ldr          r1, =((M_MDIV<<12)+(M_PDIV<<4)+M_SDIV)  ; Fin=16.9344MHz, Fout=399.58MHz

    str          r1, [r0]

 

    mov     r0, #0x2000

10  

    subs    r0, r0, #1

    bne     %B10

       ]

 

    ; Are we waking up from a suspended state?

    ;

    ldr          r1, =GSTATUS2

    ldr          r0, [r1]

    tst           r0, #0x2

    ; Yes?  Then go to the resume handler code...

    bne         WAKEUP_POWER_OFF

 

    ; Set up the memory control registers.

    ;

    add     r0, pc, #SMRDATA - (. + 8)

    ldr          r1, =BWSCON            ; BWSCON Address.

    add         r2, r0, #52           ; End address of SMRDATA.

15      

    ldr          r3, [r0], #4   

    str          r3, [r1], #4   

    cmp        r2, r0            

    bne         %B15

 

 

 

  ; LED_ON (0x1<<7)

    ; If this is a cold boot or a warm reset, clear RAM because the RAM filesystem may be

    ; bad.  If this is a software reboot (triggered by the watchdog timer), don't clear RAM.

    ;

    ldr          r1, =GSTATUS2   ; Determine why we're in the startup code.

    ldr          r10, [r1]       ;

    str         r10, [r1]       ; Clear GPSTATUS2.

    tst           r10, #0x4       ; Watchdog (software) reboot?  Skip code that clears RAM.

    bne         %F40

 

;;;;;;;;;

;NOTE

;;;;;;;;

;this code must run in the flash,not in the ram

    ; Clear RAM.

    ; Clear RAM.

    ;

    mov       r1,#0

    mov       r2,#0

    mov       r3,#0

    mov       r4,#0

    mov       r5,#0

    mov       r6,#0

    mov       r7,#0

    mov       r8,#0

      

    ldr          r0,=0x30000000   ; Start address (physical 0x3000.0000).

    ldr          r9,=0x04000000   ; 64MB of RAM.

20   

    stmia       r0!, {r1-r8}

    subs r9, r9, #32

    bne         %B20

;    ldr         r0,=0x30200000   ; Start address (physical 0x3000.0000).

;    ldr         r9,=0x00100000   ; 1MB of RAM.

;21  

;    stmia      r0!, {r1-r8}

;     subs     r9, r9, #32

;    bne        %B21

 

;    ldr         r0,=0x30038000   ; Start address (physical 0x3000.0000).

;    ldr         r9,=0x00004000   ; 256KB of RAM.

;22  

;    stmia      r0!, {r1-r8}

;     subs     r9, r9, #32

;    bne        %B22

 

    ; Initialize stacks.

    ;

30

    mrs         r0, cpsr

    bic          r0, r0, #MODEMASK|NOINT

    orr          r1, r0, #SVCMODE

    msr         cpsr_cxsf, r1           ; SVCMode.

    ldr          sp, =SVCStack

      

    ; Jump to main C routine.

    ;

       ;LED_ON (0x1<<8)

   

    bl            main

 

    ;//+ ksk 20060404  for s/w reset

40

;    ldr         r4, =0x22B784

;   ldr           r4, =0x200000

;   add          r4, r4, #0x30000000

;    mov              pc, r4

 

; *******  add by zwj **********

       ldr   r0, =GPADAT ;yz2440 powerdown operation

       ldr   r1, =0x0

       str   r1, [r0]

      

       ldr   r0, =GPACON

       ldr   r1, =0x0

       str   r1, [r0]

      

       mov        pc, r1

      

; *******  end  **********    

    b .

……

 

代码看得头很大,一直到

bl            main

这一句,表示跳转到main函数,好了,可以进入到熟悉的C代码中去了。实现代码在Main.c中。我添加的注释也在代码中,main函数大致的功能是:

1. Port_Init();// 端口初始化。

2. Uart_Init()函数来初始化串口,编译debug。

3. NF_Init()函数来初始化nand flash控制器。

4. ReadFlashID()来读取当前nand flash的ID。(前面3个还好理解,nand flash这部分现在还不懂,得找个时间专门学习一下nand flash后写个blog)

5. ((PFN_IMAGE_LAUNCH)(LOAD_ADDRESS_PHYSICAL))();跳转到0x30038000去执行,即eboot。

 

void main(void)

{

    register nBlock;

    register nPage;

    register nBadBlocks;

    volatile BYTE *pCopyPtr;

             

    // Set up copy section (initialized globals).

    //

    // NOTE: after this call, globals become valid.

    //

       SetupCopySection(pTOC);// 这是个空函数

 

    // Enable the ICache.

    //

    MMU_EnableICache();// 在Startup.s中实现

   

    // Set up clock and PLL.

    //

 

#if 0

    ChangeClockDivider(3, 1);          // 1:3:6.

    ChangeMPllValue(0x61, 0x1, 0x2);  // Fin=16MHz FCLK=296.352MHz.

// ChangeMPllValue(229, 0x5, 0x0);  // Fin=12Mhz

#endif

 

 

 

 

    // Set up all GPIO ports.

    //

     Port_Init();// 端口初始化

      

    // Turn the LEDs off.

    //

    //Led_Display(LED_ON);

      

    Uart_Init();//串口初始化,以后就可以使用串口打印了

       Uart_SendString("Step ldr/r/n");

 

    // Initialize the NAND flash interface.

    //

    NF_Init();//NandFlash初始化

      

 

       // Copy image from NAND flash to RAM.

    //

    pCopyPtr = (BYTE *)LOAD_ADDRESS_PHYSICAL;

 

    // NOTE: we assume that this Steppingstone loader occupies *part* the first (good) NAND flash block.  More

    // specifically, the loader takes up 4096 bytes (or 8 NAND pages) of the first block.  We'll start our image

    // copy on the very next page.

 

       //  读nandflash的ID

    if (NF_ReadID() != TRUE)

       {

              Uart_SendString("This NandFlash Type is not supported!!/r/n Booting is failure /r/n");

              while(1)

              {

              }

       }

   

    nBadBlocks = 0;

    for (nPage = NAND_COPY_PAGE_OFFSET ; nPage < (LOAD_SIZE_PAGES + NAND_COPY_PAGE_OFFSET) ; nPage++)

    {

        nBlock = ((nPage / NAND_PAGES_PER_BLOCK) + nBadBlocks);

 

        if (!NF_ReadPage(nBlock, (nPage % NAND_PAGES_PER_BLOCK), pCopyPtr))

        {

            if ((nPage % NAND_PAGES_PER_BLOCK) != 0)

            {

              //  Led_Display(0x1E8);    // real ECC Error.

 

                // Spin forever...

                while(1)

                {

                }

            }

 

            // ECC error on a block boundary is (likely) a bad block - retry the page 0 read on the next block.

            nBadBlocks++;

            nPage--;

            continue;

        }

 

        pCopyPtr += NAND_PAGE_SIZE_BYTES;

    }

 

       Uart_SendString("here");

    // Jump to the image...

    //

    ((PFN_IMAGE_LAUNCH)(LOAD_ADDRESS_PHYSICAL))();

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值