DM365启动之—RBL、UBL分析

DM365启动之—RBLUBL分析 

这段时间项目刚刚完成,下个项目还在等公司的安排,希望还是DAVINCI平台吧,其实呢,也无所谓,只是对DAVINCI还是稍稍有点感情,难得有点时间,鬼使神差的看起UBL的源代码来了,顺便做点笔记,看了总得留点痕迹吧,写的不好,大家多多指教哦,呵呵,费话有点多,转正题吧

系统加电或复位后,CPU通常都从某个由CPU制造商预先安排的地址上取指令。DM365提供了各种各样的启动方式,总体上可分为从外部存储器接口AEMIF(NOR Flash/OneNand)引导启动和从ARM内部ROMAIROM)引导启动两种模式。引导启动模式由BTSEL[2:0]引脚的设置状态。

BTSEL[2:0]=001时,系统加电/复位后ARM处理器从AEMIF 执行引导启动代码,即从外部存储器OneNandNor Flash启动。

AIROM支持以下启动模式

BTSEL[2:0]=000ARM NAND Boot

BTSEL[2:0]=010ARM MMC/SD Boot

BTSEL[2:0]=011ARM UART Boot

BTSEL[2:0]=100,  ARM USB Boot

BTSEL[2:0]=101ARM SPI Boot

BTSEL[2:0]=110ARM EMAC Boot

BTSEL[2:0]=111ARM HPI Boot 

ARM ROM bootloader (RBL) 为内部ROM固化的bootloader

 

xxxxxxxxxxxxxxxxxxxxxxxxx

 


由于DM365处理器AIRAM空间只有32KBNAND Flash不支持XIP技术等原因,所以DaVinci EVM采取AIROM启动方式,要想完成从NAND Flash引导启动,操作系统的任务至少需要三个阶段代码,启动流程见下图:     

xxxxxxxxxxxxxxxxxxxxxxx

 

ARM NAND Boot模式为例,下面为RBL详细流程:

1、初始化RAM1的高2K栈空间(0x7800-0x7fff)RAM的最后4个字节(0x7ffc-0x8000),来于写UBL的块号。

2、禁止所有中断,IRQFIQ

3、外部引脚DEEPSLEEPZ/GIO0在芯片为NAND模式启动复位时必须被置高。

4、从ROM的表中读出NAND的设备ID和参数。

5、根据NAND flash的参数初始化NAND区域。

6、从BLOCKSOnly Page 0 of blocks 1 to 24 will be read and searched for the

magic number)中搜索UBL 幻数magic number),读出幻数(magic number 是为了确保当前块可用,当找到有效的 UBL幻数magic number)后,把相应的块号(block number)写到ARM 内部RAM最后4个字节地址上(0x7ffc-0x8000)。

7UBL描述符由以下参数组成(所有UBL参数位宽都是32)

    -入口点地址:加载UBL之后的绝对入口点,必须在0x0020-0x781C之间。

    -UBL占用的NAND的页数:必须是相邻的页,可以跨越多个块,总字节数必须小于等于30KBIRAM大小为32K 最高2K用于栈空间)。

    -UBL的起始块:可以与UBL描述符在同一个块。

    -UBL的起始页:不可以与UBL描述符在同一页,因为加载的都是完整的页。

8、使能硬件ECC错误检测,复制UBLNAND flashIRAM,如果检测到一个4ECC的读错误,UBL将通过ECC纠正算法来纠正错误。如果是由其他的错误导致读失败的,指示符会继续在下一个新的块中搜索直到找到UBL描述符,一直搜索到block 24。没有的话就从SD卡启动。

9、在UBL入口处把控制权移交给UBL

10NAND的安全启动模式是在PLL旁通模式中完成的,它不使用快速EMIFDMA或者I-Cache。在其他模式下,使用以上的一种组合。例如,在UBL_MAGIC_PLL_DMA_IC_FAST模式下激活其他四个设置,它因该是最快的NAND启动模式。

以下是启动流图:

 xxxxxxxxxxx

        

UBL源码在PSP包里的board_utilities/flash_utils目录下,主要是COMMON目录和各子平台的目录如DM36x等,其中除了UBL的源码外还有CCSJTAG的擦除烧写源码,串口烧写源码等。

入门代码是汇编文件start.S,主要是切换操作模式,建立堆栈等,然后跳转到main函数,进入到board_utilities/flash_utils/Common/ubl/src目录下的C文件 ubl.c中。

main函数主要调用了LOCAL_boot函数来进行实质的引导功能,然后跳转到entrypoint执行Uboot。在LOCAL_boot函数中主要是Device_init()NANDBOOT_copy()两个函数。

Device_init()函数来进行平台的最底层初始化,包括电源域,时钟,DDREMIFUARTI2CTIMER 。首先屏蔽和清除中断,然后调用DEVICE_PSCInit函数实现对各模块的电源时钟使能,实质是调用PSC电源时钟管理模块的寄存器实现;然后调用DEVICE_pinmuxControl函数决定复用引脚的功能选择;接着调用DM36x/common/src/device.c下的DEVICE_PLL1Init函数、DEVICE_PLL1Init函数实现了PLL1PLL2的配置,预分频,倍频,后分频,分频到各个模块;接着调用DEVICE_DDR2Init函数来配置DDR控制器,这是UBL中重要的一部分,如果硬件电路需要更换内存芯片的话,需要在UBL中修改这个函数,即按照芯片手册来配置DDR控制寄存器中的相关参数,比如时序,BANK数,页大小等。而后调用DEVICE_EMIFInit函数来配置EMIF模块,调用DEVICE_UART0Init函数来配置串口0,调用DEVICE_TIMER0Init函数来配置TIMER0,调用 DEVICE_I2C0Init函数来配置I2C控制器,都是操作某一模块的控制寄存器实现。

当读取到启动模式配置寄存器(BOOTCFG)值时,如果BTSEL[2:0]=000,则调用NAND初始化函数NAND_init()及复制代码函数NANDBOOT_copy()将存储在NAND中紧随其后的Bootload代码复制到DDR2中。

下面为源代码分析。

注:在说明源代码的时候,为了缩短篇幅,删除了代码的部分DEBUG信息

 

// Main entry point

void main(void)

{

  // Call to real boot function code

  LOCAL_boot();

  // Jump to entry point

  APPEntry = (void (*)(void)) gEntryPoint;  /*UBL结束,交还入口地址,执行U-BOOT*/

  (*APPEntry)(); 

}

 

函数实体唯一调用了LOCAL_boot()函数,下面我们来看看,里面具体做了什么,下面为LOCAL_boot()函数源码。

static Uint32 LOCAL_boot(void)

{

  DEVICE_BootMode bootMode;

 

  // Read boot mode

  bootMode = DEVICE_bootMode();

  if (bootMode == DEVICE_BOOTMODE_UART) {

    // Wait until the RBL is done using the UART.

    while((UART0->LSR & 0x40) == 0 );

  }

 

  // Platform Initialization

  if ( DEVICE_init() != E_PASS ) {

    DEBUG_printString(" initialization failed!\r\n");

    asm(" MOV PC, #0");

  }else{

    //DEBUG_printString(" initialization passed!\r\n");

  }

 

  // Set RAM pointer to beginning of RAM space

  UTIL_setCurrMemPtr(0);

 

  // Select Boot Mode

#ifdefined(UBL_NAND) {

    // Copy binary image application from NAND to RAM

    if (NANDBOOT_copy() != E_PASS) {

      DEBUG_printString("NAND Boot failed.\r\n");

      LOCAL_bootAbort();

    }

  }

#elif defined(UBL_NOR) {

    // Copy binary application image from NOR to RAM

    if (NORBOOT_copy() != E_PASS){

      DEBUG_printString("NOR Boot failed.\r\n");

      LOCAL_bootAbort();

    }

  }

#elif defined(UBL_SD_MMC) {

    // Copy binary of application image from SD/MMC card to RAM

    if (SDMMCBOOT_copy() != E_PASS){

      DEBUG_printString("SD/MMC Boot failed.\r\n");

      LOCAL_bootAbort();

    }

  }

#else {

    UARTBOOT_copy();     

  }

#endif

 

  DEVICE_TIMER0Stop();

 

  return E_PASS;   

}

 

程序流程还是很简单,先通过调用DEVICE_bootMode函数来判断启动方式(通过读取BOOTCFG的值),而后调用了DEVICE_init函数来进行平台的最底层初始化,包括电源域,时钟,DDREMIFUARTI2CTIMER等,都是操作某一模块的控制寄存器实现。

     接着通过判断不同的引导方式,采取不同的处理办法,以 NAND启动为例,将调用NANDBOOT_copy函数。此函数将NAND中的某些内容(就是UBOOT)搬移到RAM中,而后 UBL结束,控制权正式交给UBOOT。接下来我们来分析下DEVICE_initNANDBOOT_copy的具体实现,DEVICE_init源代码如下:

Uint32 DEVICE_init()

{
    Uint32 status = E_PASS;
    
    // Mask all interrupts
    AINTC->INTCTL = 0x4;
    AINTC->EABASE = 0x0;
    AINTC->EINT0 = 0x0;
    AINTC->EINT1 = 0x0;        
    
    // Clear all interrupts
    AINTC->FIQ0 = 0xFFFFFFFF;
    AINTC->FIQ1 = 0xFFFFFFFF;
    AINTC->IRQ0 = 0xFFFFFFFF;
    AINTC->IRQ1 = 0xFFFFFFFF;
    
#ifndef SKIP_LOW_LEVEL_INIT
    POR_RESET();
    // System PSC setup - enable all
    DEVICE_PSCInit();
   /*
管脚复用的配置*/ 
   DEVICE_pinmuxControl(0,0xFFFFFFFF,0x00FD0000); // All Video Inputs
Y0-Y7全部作为video in(不作       GPIO),GIO43作为SD1clkMcBsp开启,MMCSD0关闭

   DEVICE_pinmuxControl(1,0xFFFFFFFF,0x00145555); // All Video Outputs,视频Cout0-Cout7作为色度信号输出使能,场消隐/行消隐同步信号使能,LCDOE功能关闭

   DEVICE_pinmuxControl(2,0xFFFFFFFF,0x000000DA); // EMIFA,总线使能,

   DEVICE_pinmuxControl(3,0xFFFFFFFF,0x00180000); // SPI0, SPI1, UART1, I2C, SD0, SD1, McBSP0, CLKOUTs,串口1使能,其他均作为GPIO,网卡没使能

   DEVICE_pinmuxControl(4,0xFFFFFFFF,0x55555555); //SI1-SPI4使能,MMCSD1 使能
    
    GPIO->DIR02 &= 0xfeffffff;
    GPIO->CLRDATA02 = 0x01000000;
    
    
// System PLL setup
    if (status == E_PASS) status |= DEVICE_PLL1Init(PLL1_Mult);
    // DDR PLL setup
    if (status == E_PASS) status |= DEVICE_PLL2Init();
    // DDR2 module setup
    if (status == E_PASS) status |= DEVICE_DDR2Init();
#endif
    
    // AEMIF Setup
    if (status == E_PASS) status |= DEVICE_EMIFInit();
    // UART0 Setup
    if (status == E_PASS) status |= DEVICE_UART0Init();
    // TIMER0 Setup
    if (status == E_PASS) status |= DEVICE_TIMER0Init();
    // I2C0 Setup
    if (status == E_PASS) status |= DEVICE_I2C0Init();
    
    return status;
}

 

接着我们来看NANDBOOT_copy()UBL的主要工作都在此函数中实现。

// Function to find out where the application is and copy to RAM

Uint32 NANDBOOT_copy()

{

  NAND_InfoHandle hNandInfo;

  Uint32 count,blockNum;

  Uint32 i;

  Uint32 magicNum;

  Uint8 *rxBuf;    // RAM receive buffer

  Uint32 block,page;

  Uint32 readError = E_FAIL;

  Bool failedOnceAlready = FALSE;

 

  // Maximum application size is 16 MBAllocate memory from the ad-hoc heap

  rxBuf = (Uint8*)UTIL_allocMem((APP_IMAGE_SIZE>>1));

  blockNum = DEVICE_NAND_UBL_SEARCH_START_BLOCK;

 

  // NAND Initialization打开 NandFlash设备,将NandFlash的硬件信息放在hNandInfo数据结构内。

  hNandInfo = NAND_open(EMIFStart, (Uint8) DEVICE_emifBusWidth());

  if (hNandInfo == NULL) {

       DEBUG_printString("hNandInfo == NULL\n");

    return E_FAIL;

  }

   

NAND_startAgain:

  if (blockNum > DEVICE_NAND_UBL_SEARCH_END_BLOCK) {

    return E_FAIL;  // NAND boot failed and return fail to main

  }

 

  /*Read data about Application starting at START_APP_BLOCK_NUM, Page 0 and possibly going until block END_APP_BLOCK_NUM, Page 0*/

   /*U-boot一般存在于DEVICE_NAND_UBL_SEARCH_START_BLOCK以后块的第0页,ubl试着从 DEVICE_NAND_UBL_SEARCH_START_BLOCK块向后搜索每块的第0页,找到后,前24个字节分别记录着u-boot描述,如入口函数等,并把记录存入rxBuf中。*/

  for(count=blockNum; count <= DEVICE_NAND_UBL_SEARCH_END_BLOCK; count++) {   

    if(NAND_readPage(hNandInfo,count,0,rxBuf) != E_PASS)

      continue;

    magicNum = ((Uint32 *)rxBuf)[0];

    /* Valid magic number found */

    if((magicNum & 0xFFFFFF00) == MAGIC_NUMBER_VALID) {

      blockNum = count;

      break;

    }

  }

 

  // Never found valid header in any page 0 of any of searched blocks

  if (count > DEVICE_NAND_UBL_SEARCH_END_BLOCK) {

    DEBUG_printString("No valid boot image found!\r\n");

    return E_FAIL;

  }

 

  // Fill in NandBoot header

  gNandBoot.entryPoint = *(((Uint32 *)(&rxBuf[4]))); /* The first "long" is entry point for Application*/

  gNandBoot.numPage = *(((Uint32 *)(&rxBuf[8])));  /* The second "long" is the number of pages */

  gNandBoot.block = *(((Uint32 *)(&rxBuf[12])));   /* The third "long" is the block where Application is stored in NAND */

  gNandBoot.page = *(((Uint32 *)(&rxBuf[16])));   /* The fourth "long" is the page number where Application is stored in NAND */

  gNandBoot.ldAddress = *(((Uint32 *)(&rxBuf[20])));   /* The fifth "long" is the Application load address */

 

  // If the application is already in binary format, then our received buffer can point to the specified load address

  // instead of the temp location used for storing an S-record

  // Checking for the UBL_MAGIC_DMA guarantees correct usage with the

  // Spectrum Digital CCS flashing tool, flashwriter_nand.out

  if ((magicNum == UBL_MAGIC_BIN_IMG) || (magicNum == UBL_MAGIC_DMA)) {

    // Set the copy location to final run location

    rxBuf = (Uint8 *)gNandBoot.ldAddress;

    // Free temp memory rxBuf used to point to

    UTIL_setCurrMemPtr((void *)((Uint32)UTIL_getCurrMemPtr() - (APP_IMAGE_SIZE>>1)));

  }

 

NAND_retry:

  /* initialize block and page number to be used for read */

  block = gNandBoot.block;

  page = gNandBoot.page;

 

// Perform the actual copying of the application from NAND to RAM

for(i=0;i

// if page goes beyond max number of pages increment block number and reset page number

if(page >= hNandInfo->pagesPerBlock) {

page = 0;

block++;

}

  readError = NAND_readPage(hNandInfo,block,page++,(&rxBuf[i*(hNandInfo->dataBytesPerPage)]));  /* Copy the data */

 

  // We attempt to read the app data twice.  If we fail twice then we go look for a new

  // application header in the NAND flash at the next block.

  if(readError != E_PASS) {

     if(failedOnceAlready) {

        blockNum++;

        goto NAND_startAgain;

      } else {

         failedOnceAlready = TRUE;

         goto NAND_retry;

      }

    }

}

 

// Application was read correctly, so set entrypoint

gEntryPoint = gNandBoot.entryPoint;

 

return E_PASS;

}

 

到此,UBL的工作全部完全,接下来的工作交给U-BOOT

你可能感兴趣的:(ARM,linux)