DM365启动之—RBL、UBL分析
DM365启动之—RBL、UBL分析? 这段时间项目刚刚完成,下个项目还在等公司的安排,希望还是DAVINCI平台吧,其实呢,也无所谓,只是对DAVINCI还是稍稍有点感情,难得有点时间,鬼使神差的看起UBL的源代码来了,顺便做点笔记,看了总得留点痕迹吧,写的不好,大家多多指教哦,呵呵,费话有点多,转正题吧 系统加电或复位后,CPU通常都从某个由CPU制造商预先安排的地址上取指令。DM365提供了各种各样的启动方式,总体上可分为从外部存储器接口AEMIF(NOR Flash/OneNand)引导启动和从ARM内部ROM(AIROM)引导启动两种模式。引导启动模式由BTSEL[2:0]引脚的设置状态。 BTSEL[2:0]=001时,系统加电/复位后ARM处理器从AEMIF?执行引导启动代码,即从外部存储器OneNand或Nor Flash启动。 AIROM支持以下启动模式 BTSEL[2:0]=000,ARM NAND Boot BTSEL[2:0]=010,ARM MMC/SD Boot BTSEL[2:0]=011,ARM UART Boot BTSEL[2:0]=100,??ARM USB Boot BTSEL[2:0]=101,ARM SPI Boot BTSEL[2:0]=110,ARM EMAC Boot BTSEL[2:0]=111,ARM HPI Boot? ARM ROM bootloader (RBL)?为内部ROM固化的bootloader。 ? xxxxxxxxxxxxxxxxxxxxxxxxx ? 由于DM365处理器AIRAM空间只有32KB及NAND Flash不支持XIP技术等原因,所以DaVinci EVM采取AIROM启动方式,要想完成从NAND Flash引导启动,操作系统的任务至少需要三个阶段代码,启动流程见下图:?????
xxxxxxxxxxxxxxxxxxxxxxx ? 以ARM NAND Boot模式为例,下面为RBL详细流程: 1、初始化RAM1的高2K栈空间(0x7800-0x7fff)。RAM的最后4个字节(0x7ffc-0x8000),来于写UBL的块号。 2、禁止所有中断,IRQ和FIQ。 3、外部引脚DEEPSLEEPZ/GIO0在芯片为NAND模式启动复位时必须被置高。 4、从ROM的表中读出NAND的设备ID和参数。 5、根据NAND flash的参数初始化NAND区域。 6、从BLOCKS(Only 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)。 7、UBL描述符由以下参数组成(所有UBL参数位宽都是32位): ??? -入口点地址:加载UBL之后的绝对入口点,必须在0x0020-0x781C之间。 ??? -UBL占用的NAND的页数:必须是相邻的页,可以跨越多个块,总字节数必须小于等于30KB(IRAM大小为32K,?最高2K用于栈空间)。 ??? -UBL的起始块:可以与UBL描述符在同一个块。 ??? -UBL的起始页:不可以与UBL描述符在同一页,因为加载的都是完整的页。 8、使能硬件ECC错误检测,复制UBL从NAND flash到IRAM,如果检测到一个4位ECC的读错误,UBL将通过ECC纠正算法来纠正错误。如果是由其他的错误导致读失败的,指示符会继续在下一个新的块中搜索直到找到UBL描述符,一直搜索到block 24。没有的话就从SD卡启动。 9、在UBL入口处把控制权移交给UBL。 10、NAND的安全启动模式是在PLL旁通模式中完成的,它不使用快速EMIF,DMA或者I-Cache。在其他模式下,使用以上的一种组合。例如,在UBL_MAGIC_PLL_DMA_IC_FAST模式下激活其他四个设置,它因该是最快的NAND启动模式。 以下是启动流图: ?xxxxxxxxxxx
???????? UBL源码在PSP包里的board_utilities/flash_utils目录下,主要是COMMON目录和各子平台的目录如DM36x等,其中除了UBL的源码外还有CCS下JTAG的擦除烧写源码,串口烧写源码等。 入门代码是汇编文件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()函数来进行平台的最底层初始化,包括电源域,时钟,DDR,EMIF,UART,I2C,TIMER等?。首先屏蔽和清除中断,然后调用DEVICE_PSCInit函数实现对各模块的电源时钟使能,实质是调用PSC电源时钟管理模块的寄存器实现;然后调用DEVICE_pinmuxControl函数决定复用引脚的功能选择;接着调用DM36x/common/src/device.c下的DEVICE_PLL1Init函数、DEVICE_PLL1Init函数实现了PLL1、PLL2的配置,预分频,倍频,后分频,分频到各个模块;接着调用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!rn"); ????asm(" MOV PC,#0"); ??}else{ ????//DEBUG_printString(" initialization passed!rn"); ??} ? ??// 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.rn"); ??????LOCAL_bootAbort(); ????} ??} #elif defined(UBL_NOR) { ????// Copy binary application image from NOR to RAM ????if (NORBOOT_copy() != E_PASS){ ??????DEBUG_printString("NOR Boot failed.rn"); ??????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.rn"); ??????LOCAL_bootAbort(); ????} ??} #else { ????UARTBOOT_copy();????? ??} #endif ? ??DEVICE_TIMER0Stop(); ??return E_PASS;??? ? 程序流程还是很简单,先通过调用DEVICE_bootMode函数来判断启动方式(通过读取BOOTCFG的值),而后调用了DEVICE_init函数来进行平台的最底层初始化,包括电源域,时钟,DDR,EMIF,UART,I2C,TIMER等,都是操作某一模块的控制寄存器实现。 ?????接着通过判断不同的引导方式,采取不同的处理办法,以?NAND启动为例,将调用NANDBOOT_copy函数。此函数将NAND中的某些内容(就是UBOOT)搬移到RAM中,而后?UBL结束,控制权正式交给UBOOT。接下来我们来分析下DEVICE_init与NANDBOOT_copy的具体实现,DEVICE_init源代码如下: Uint32 DEVICE_init() { ?? DEVICE_pinmuxControl(1,0x00145555); // All Video Outputs,视频Cout0-Cout7作为色度信号输出使能,场消隐/行消隐同步信号使能,LCD的OE功能关闭?? DEVICE_pinmuxControl(2,0x000000DA); // EMIFA,总线使能,?? DEVICE_pinmuxControl(3,0x00180000); // SPI0,SPI1,UART1,I2C,SD0,SD1,McBSP0,CLKOUTs,串口1使能,其他均作为GPIO,网卡没使能?? DEVICE_pinmuxControl(4,0x55555555); //SI1-SPI4使能,MMCSD1?使能 ? 接着我们来看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 MB,Allocate 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 == NULLn"); ????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,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!rn"); ????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<gNandBoot.numPage;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。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |