DM365是的启动方式有两种,通过BOOTSEL[2:0]引脚决定。当其为001时,直接从AEMIF上启动,比如NOR和OneNAND。除此之外
皆是从RBL启动,顺序为RBL-UBL-UBOOT-KERNEL,比如NAND,串口,SD卡等。RBL会搜寻block1到block24去找
UBL,关于RBL启动的详细细节可以参考用户指南关于ARM子系统的那篇文档,很详尽,下面只分析UBL的源码。
UBL源码在PSP包里的board_utilities/flash_utils目录下,主要是COMMON目录和各子平台的目录如DM36x等,内中除了UBL的源码外还有CCS下JTAG的擦除烧写源码,串口烧写源码等(flash_utils\DM36x\CCS下包括NANDWriter和UBL两个文件夹,分别对应针对IPNC的CCS的project)。下面只分析UBL的启动代码。
入门代码是汇编文件start.S,主要是切换操作模式,建立堆栈等,然后跳转到main函数,进入到board_utilities/flash_utils/Common/ubl/src目录下的C文件
ubl.c中。main函数如下:
// Main entry
point
void
main(void) { // Call to real boot function code
LOCAL_boot(); // Jump to entry point
DEBUG_printString("\r\nJumping to entry point at "); DEBUG_printHexInt(gEntryPoint); DEBUG_printString(".\r\n"); APPEntry = (void (*)(void)) gEntryPoint; /* UBL结束,gEntryPoint将u-boot入口 交给APPEntry */ (*APPEntry)(); }
|
U-boot一般存在于DEVICE_NAND_UBL_SEARCH_START_BLOCK以后块的第0页,ubl试着从
DEVICE_NAND_UBL_SEARCH_START_BLOCK块向后搜索每块的第0页,找到后,前24个字节分别记录着u-boot描述,如入
口函数等
//DM36x/CCS/NANDWriter/src/nandwriter.c nandwriter()函数中
// Fill in
NandBoot header,rxBuf为刚搜到的页 gNandBoot.entryPoint =
*(((Uint32 *)(&rxBuf[4])));/* The first
"long" is entry point for Application ,u-boot主程序将从这里开始执行*/ gNandBoot.numPage = *(((Uint32 *)(&rxBuf[8]))); /* The second "long" is the number of pages
,代码总页数,有多少数据要载入到DDR*/ 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 ,一般情况,程序放在这个地址开始加载*/
|
根据以上信息一次读入每一页,然后将u-boot入口传递给gEntryPoint。
main函数主要调用了LOCAL_boot函数来进行实质的引导功能,下面是此函数的内容:
static Uint32
LOCAL_boot(void) { DEVICE_BootMode
bootMode; // Read boot mode 从BOOTCFG系统寄存器读出,得到目前BOOTSEL[0..2]
bootMode
= DEVICE_bootMode();
if (bootMode == DEVICE_BOOTMODE_UART) { // Wait until the RBL is done using the UART.
//对于通过UART启动的,这些代码应该是存在于uart设备上
while((UART0->LSR &
0x40) == 0
); } // Platform Initialization
if ( DEVICE_init() != E_PASS ) /* DEVICE_init函数来进行平台的最底层初始化,包括电源域,时钟,DDR,EMIF,UART,I2C,TIMER等*/ { DEBUG_printString(devString); DEBUG_printString(" initialization failed!\r\n"); asm(" MOV PC, #0"); } else { DEBUG_printString(devString); DEBUG_printString(" initialization passed!\r\n"); } // Set RAM pointer to beginning of RAM
space
UTIL_setCurrMemPtr(0); /* 对全局变量currMemPtr赋值*/ // Send some information to host
DEBUG_printString("TI UBL Version: "); DEBUG_printString(UBL_VERSION_STRING); DEBUG_printString("\r\nBooting
Catalog Boot Loader\r\nBootMode = "); // Select Boot Mode
#if defined(UBL_NAND) { //Report Bootmode to host
DEBUG_printString("NAND\r\n"); // Copy binary image application from
NAND to RAM // NANDBOOT_copy()首先打开
NandFlash设备,将NandFlash的硬件信息放在hNandInfo数据结构内。移植ubl时
需要修改与hNandInfo数据结构相关的参数,比如EMIF地址(EMIFStart =
0x02000000 在ubl.cmd;) DEVICE_NAND_CHIP_infoTable结构中的页大小、块数、块中的页数、...
if
(NANDBOOT_copy() != E_PASS) { DEBUG_printString("NAND Boot failed.\r\n"); LOCAL_bootAbort(); } } #elif defined(UBL_NOR) { //Report Bootmode to host
DEBUG_printString("NOR \r\n"); // 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) { //Report Bootmode to host
DEBUG_printString("SD/MMC \r\n"); // 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 { //Report Bootmode to host
DEBUG_printString("UART\r\n"); UARTBOOT_copy(); } #endif DEBUG_printString(" DONE"); UTIL_waitLoop(10000); DEVICE_TIMER0Stop();
return E_PASS; }
|
先通过调用DEVICE_bootMode函数来判断启动方式(通过读取SYS寄存器实现),而后调用了DEVICE_init函数来进行平台的最底层初
始化,包括电源域,时钟,DDR,EMIF,UART,I2C,TIMER等。
而后通过UTIL_setCurrMemPtr函数对全局变量currMemPtr赋值,以后用到。接着通过判断不同的引导方式,采取不同的处理办法,以
NAND启动为例,将调用NANDBOOT_copy函数。此函数将NAND中的某些内容(就是UBOOT)搬移到RAM中,而后
UBL结束,控制权正式交给UBOOT。
看看UBL对平台的初始化,主要是调用了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作为SD1的clk,McBsp开启,MMCSD0关闭
DEVICE_pinmuxControl(1,0xFFFFFFFF,0x00145555); // All Video
Outputs,视频Cout0-Cout7作为色度信号输出使能,场消隐/行消隐同步信号使能,LCD的OE功能关闭
DEVICE_pinmuxControl(2,0xFFFFFFFF,0x000000DA); //
EMIFA,总线使能,但是CE0没有使能,0xDA可能在合众达板子上运行有问题,因为他将ce0设置成GPIO,这样nandflash失效了,我想
可能是TI原版有个cpld,在合众达的测试程序,值为0x55。
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; }
|
首先屏蔽和清除中断,然后调用DEVICE_PSCInit函数实现对各模块的电源时钟使能,实质是调用PSC电源时钟管理模块的寄存器实现,函数内容如
下:
void
DEVICE_PSCInit() { unsigned
char i=0; unsigned char lpsc_start; unsigned
char lpsc_end,lpscgroup,lpscmin,lpscmax; unsigned int
PdNum = 0; lpscmin =0; lpscmax =2; for(lpscgroup=lpscmin
; lpscgroup <=lpscmax;
lpscgroup++) { if(lpscgroup==0) { lpsc_start
= 0; //
Enabling LPSC 3 to 28 SCR first
lpsc_end = 28; } else if (lpscgroup
== 1) { /* Skip locked LPSCs [29-37] */ lpsc_start
= 38; lpsc_end = 47; } else { lpsc_start
= 50; lpsc_end = 51; } //NEXT=0x3, Enable LPSC's for(i=lpsc_start; i<=lpsc_end; i++) { PSC->MDCTL[i] |= 0x3; } //Program goctl to start transition
sequence for LPSCs PSC->PTCMD = (1<<PdNum); //Wait for GOSTAT = NO TRANSITION from
PSC for Pdomain 0 while(! (((PSC->PTSTAT >> PdNum) & 0x00000001) == 0)); //Wait for MODSTAT = ENABLE from
LPSC's for(i=lpsc_start; i<=lpsc_end; i++) { while(!((PSC->MDSTAT[i] & 0x0000001F) == 0x3));
} } }
|
然后调用DEVICE_pinmuxControl函数决定复用引脚的功能选择,详见数据手册查看引脚功能。
接着调用DM36x/common/src/device.c下的DEVICE_PLL1Init函数实现了PLL1的配置,预分频,倍频,后分频,分频到各个模块,其设置顺序可以参看用户指南ARM子系统文
档,有详细的介绍,PLL2类似,函数内容如下:
Uint32 DEVICE_PLL1Init(Uint32 PLLMult) { unsigned int
CLKSRC=0x0; unsigned int j; /*Power up the PLL*/ PLL1->PLLCTL &= 0xFFFFFFFD; PLL1->PLLCTL
&= 0xFFFFFEFF;
PLL1->PLLCTL |=
CLKSRC<<8; /*Set
PLLENSRC '0', PLL Enable(PLLEN) selection is controlled through MMR*/ PLL1->PLLCTL &= 0xFFFFFFDF; /*Set PLLEN=0 => PLL BYPASS MODE*/ PLL1->PLLCTL &= 0xFFFFFFFE; UTIL_waitLoop(150);
//
PLLRST=1(reset assert) PLL1->PLLCTL |= 0x00000008; UTIL_waitLoop(300); /*Bring PLL out of Reset*/ PLL1->PLLCTL &= 0xFFFFFFF7; //Program the Multiper and Pre-Divider for PLL1 PLL1->PLLM =
0x51; // VCO will 24*2M/N+1 = 486Mhz
PLL1->PREDIV =
0x8000|0x7; PLL1->SECCTL =
0x00470000; // Assert TENABLE = 1, TENABLEDIV = 1,
TINITZ = 1 PLL1->SECCTL = 0x00460000;
// Assert TENABLE = 1,
TENABLEDIV = 1, TINITZ = 0 PLL1->SECCTL
= 0x00400000; // Assert TENABLE = 0, TENABLEDIV = 0, TINITZ = 0 PLL1->SECCTL =
0x00410000; // Assert TENABLE = 0, TENABLEDIV = 0,
TINITZ = 1 //Program the PostDiv for PLL1 PLL1->POSTDIV =
0x8000; // Post divider setting for PLL1 PLL1->PLLDIV2 =
0x8001; PLL1->PLLDIV3 =
0x8001; // POST DIV 486/2 -> MJCP/HDVICP PLL1->PLLDIV4 =
0x8003; // POST DIV 486/4 -> EDMA/EDMA CFG PLL1->PLLDIV5 =
0x8001; // POST DIV 486/2 -> VPSS PLL1->PLLDIV6 =
0x8011; // 27Mhz POST DIV 486/18 -> VENC PLL1->PLLDIV7 =
0x8000; // POST DIV 486/2 -> DDR PLL1->PLLDIV8 =
0x8003; // POST DIV 486/4 -> MMC0/SD0 PLL1->PLLDIV9 =
0x8001; // POST DIV 486/2 -> CLKOUT
UTIL_waitLoop(300); /*Set the GOSET bit */ PLL1->PLLCMD =
0x00000001; // Go
UTIL_waitLoop(300); /*Wait for PLL to LOCK */ while(! (((SYSTEM->PLL0_CONFIG) & 0x07000000)
== 0x07000000)); /*Enable the PLL Bit of PLLCTL*/ PLL1->PLLCTL |= 0x00000001; //
PLLEN=0 return
E_PASS; }
Uint32 DEVICE_PLL2Init() {
...
// Post divider
setting for PLL2 PLL2->PLLDIV2 = 0x8001; //
594/2 =297 Mhz -> ARM PLL2->PLLDIV4
= 0x801C; //
POST DIV 594/29 = 20.48 -> VOICE PLL2->PLLDIV5 =
0x8007;
...
}
|
继续在DEVICE_init函数中,下面是调用DEVICE_DDR2Init函数来配置DDR控制器,这是UBL中重要的一部分,如果硬件电路需要更
换内存芯片的话,需要在UBL中修改这个函数,即按照芯片手册来配置DDR控制寄存器中的相关参数,比如时序,BANK数,页大小等。这个函数主要是操作
SYS模块和DDR模块的相关寄存器来配置内存,函数中调用的DEVICE_LPSCTransition函数用来实现模块的电源时钟状态的改变,函数内
容如下:
Uint32 DEVICE_DDR2Init() { DEVICE_LPSCTransition(LPSC_DDR2,0,PSC_ENABLE); SYSTEM->VTPIOCR = (SYSTEM->VTPIOCR) &
0xFFFF9F3F; // Set bit CLRZ (bit 13) SYSTEM->VTPIOCR
= (SYSTEM->VTPIOCR) | 0x00002000; // Check VTP READY Status while(
!(SYSTEM->VTPIOCR &
0x8000)); // Set bit VTP_IOPWRDWN bit 14 for DDR
input buffers) //SYSTEM->VTPIOCR
= SYSTEM->VTPIOCR | 0x00004000;
// Set bit LOCK(bit7) and PWRSAVE
(bit8) SYSTEM->VTPIOCR = SYSTEM->VTPIOCR
| 0x00000080; // Powerdown VTP as it is locked (bit
6) // Set bit
VTP_IOPWRDWN bit 14 for DDR input buffers) SYSTEM->VTPIOCR
= SYSTEM->VTPIOCR | 0x00004040; // Wait for calibration to complete UTIL_waitLoop( 150 ); // Set the DDR2 to synreset, then
enable it again DEVICE_LPSCTransition(LPSC_DDR2,0,PSC_SYNCRESET); DEVICE_LPSCTransition(LPSC_DDR2,0,PSC_ENABLE); DDR->DDRPHYCR = 0x000000C5; DDR->SDBCR =
0x08D34832; //Program SDRAM Bank Config Register DDR->SDBCR =
0x0853C832; DDR->SDTIMR =0x3C934B51; //Program SDRAM Timing Control Register1 DDR->SDTIMR2 =0x4221C72; //Program SDRAM Timing Control Register2 DDR->PBBPR =
0x000000FE; DDR->SDBCR =
0x08534832; //Program SDRAM Bank Config Register DDR->SDRCR =
0x00000768; //Program SDRAM Refresh Control
Register DEVICE_LPSCTransition(LPSC_DDR2,0,PSC_SYNCRESET); DEVICE_LPSCTransition(LPSC_DDR2,0,PSC_ENABLE); return
E_PASS; }
void DEVICE_LPSCTransition(Uint8 module, Uint8 domain,
Uint8 state) { // Wait for any outstanding transition to complete while ( (PSC->PTSTAT) & (0x00000001 << domain) ); // If we are already in that state,
just return if
(((PSC->MDSTAT[module]) & 0x1F) == state) return; // Perform transition PSC->MDCTL[module] = ((PSC->MDCTL[module]) & (0xFFFFFFE0)) | (state); PSC->PTCMD |= (0x00000001 <<
domain); // Wait for transition to complete while ( (PSC->PTSTAT) & (0x00000001 << domain) ); // Wait and verify the state while (((PSC->MDSTAT[module]) & 0x1F) != state); }
|
而后调用DEVICE_EMIFInit函数来配置EMIF模块,这个模块用来接外存,比如NAND,NOR等。DM365有两个片选空间,如果某一空间
配置成NAND,则需要在寄存器中设置,其函数内容如下:
Uint32 DEVICE_EMIFInit() { AEMIF->AWCCR =
0xff; AEMIF->A1CR =
0x40400204; AEMIF->NANDFCR |= 1; AEMIF->A2CR = 0x00a00505; return E_PASS; }
|
而后调用DEVICE_UART0Init函数来配置串口0,调用DEVICE_TIMER0Init函数来配置TIMER0,调用
DEVICE_I2C0Init函数来配置I2C控制器,都是操作某一模块的控制寄存器实现,具体如何设置可以参考相关模块的手册,这三个函数的内容如
下:
Uint32 DEVICE_UART0Init() { UART0->PWREMU_MGNT =
0; // Reset UART TX & RX components
UTIL_waitLoop( 100 ); UART0->MDR = 0x0; UART0->DLL
= 0xd; // Set
baud rate UART0->DLH = 0; UART0->FCR =
0x0007; // Clear UART TX & RX FIFOs UART0->FCR =
0x0000; // Non-FIFO mode UART0->IER =
0x0007; // Enable interrupts
UART0->LCR =
0x0003; // 8-bit words // 1 STOP bit generated, // No Parity, No Stick paritiy, // No Break control UART0->MCR =
0x0000; // RTS & CTS disabled, // Loopback mode disabled, // Autoflow disabled UART0->PWREMU_MGNT =
0xE001; // Enable TX & RX componenets return E_PASS; }
Uint32
DEVICE_I2C0Init() { I2C0->ICMDR = 0;
// Reset I2C I2C0->ICPSC = 26; // Config prescaler for 27MHz I2C0->ICCLKL =
20; // Config clk LOW for 20kHz I2C0->ICCLKH =
20; // Config clk HIGH for 20kHz I2C0->ICMDR |= I2C_ICMDR_IRS; // Release I2C from reset return E_PASS; }
Uint32
DEVICE_TIMER0Init() { // Put
timer into reset TIMER0->EMUMGT_CLKSPD
= 0x00000003; TIMER0->TCR
= 0x00000000; // Enable TINT0, TINT1 interrupt TIMER0->INTCTL_STAT =
0x00000001; // Set to 64-bit GP Timer mode, enable
TIMER12 & TIMER34 TIMER0->TGCR = 0x00000003; // Reset timers to zero TIMER0->TIM12 =
0x00000000; TIMER0->TIM34 =
0x00000000; // Set timer period (5 second timeout =
(24000000 * 5) cycles = 0x07270E00) TIMER0->PRD34 =
0x00000000; TIMER0->PRD12 =
0x07270E00; return E_PASS; }
|
至此,DEVICE_init函数结束,程序返回至LOCAL_boot函数中,接着就调用NANDBOOT_copy函数了。
阅读(594) | 评论(0) | 转发(0) |