上一篇讲到,DSP的BOOT流程是:
上电 =》从0x3F FFC0开始取复位中断向量=》跳转到InitBoot =》启动系统boot (StartSystemBoot)。
在系统boot里面,如果配置选择的是waitBoot模式,就会停在一个死循环中。那么,如果不是waitBoot模式,而是FLASH_BOOT模式呢?接下来继续分析。
FLASH_BOOT模式
首先,看看CPU1BROM_startSystemBoot()函数的最后:
//
// Enter specified wait boot or enable watchdog, then branch to address
//
if(CPU1BROM_bootMode == WAIT_BOOT)
{
SysCtl_enableWatchdog();
for(;;)
{
}
}
else if(CPU1BROM_bootMode == WAIT_BOOT_ALT1)
{
for(;;)
{
}
}
else
{
SysCtl_enableWatchdog();
return(entryAddress);
}
如果不是WAIT_BOOT(或者WAIT_BOOT_ALT1),则进入else分析,开启开门狗,返回程序入口地址。如果是FLASH_BOOT,则entryAddress = 0x 0008 0000。这个值作为函数的返回值,保存在ACC寄存器里。后面会用到的!
CPU1BROM_startSystemBoot函数被调用的地方在这里:
cpu1brom_init_boot.asm文件中: LCR CPU1BROM_startSystemBoot
;
; Branch to system initialization to run final initializations and execute boot mode
;
LCR CPU1BROM_startSystemBoot
;
; Function: ExitPBISTLoc
;
; Cleanup and exit after PBIST. At this point the EntryAddr
; is located in the ACC register
;
ExitPBISTLoc:
BF ExitBoot,UNC
因此,CPU1BROM_startSystemBoot函数返回后继续执行: BF ExitBoot,UNC
退出系统BOOT
ExitBoot函数如下:
;
; Function: ExitBoot
;
; This module cleans up after boot
;
; 1) Make sure the stack is re-initialized
; 2) Push 0 onto the stack so RPC will be
; 0 after using LRETR to jump to the
; entry point
; 2) Load RPC with the entry point
; 3) Clear all XARn registers
; 4) Clear ACC, P and XT registers
; 5) LRETR - this will also clear the RPC
; register since 0 was on the stack
;
ExitBoot:
;
; Insure that the stack is re-initialized
;
MOV SP,#__stack
;
; Clear the bottom of the stack. This will endup
; in RPC when we are finished
;
MOV *SP++,#0
MOV *SP++,#0
;
; Load RPC with the entry point as determined
; by the boot mode. This address will be returned
; in the ACC register.
;
PUSH ACC
POP RPC
;
; Put registers back in their reset state.
;
; Clear all the XARn, ACC, XT, and P and DP
; registers
;
; NOTE: Leave the device in C28x operating mode
; (OBJMODE = 1, AMODE = 0)
;
ZAPA
MOVL XT,ACC
MOVZ AR0,AL
MOVZ AR1,AL
MOVZ AR2,AL
MOVZ AR3,AL
MOVZ AR4,AL
MOVZ AR5,AL
MOVZ AR6,AL
MOVZ AR7,AL
MOVW DP, #0
;
; Restore ST0 and ST1. Note OBJMODE is
; the only bit not restored to its reset state.
; OBJMODE is left set for C28x object operating
; mode.
;
; ST0 = 0x0000 ST1 = 0x0A0B
; 15:10 OVC = 0 15:13 ARP = 0
; 9: 7 PM = 0 12 XF = 0
; 6 V = 0 11 M0M1MAP = 1
; 5 N = 0 10 reserved
; 4 Z = 0 9 OBJMODE = 1
; 3 C = 0 8 AMODE = 0
; 2 TC = 0 7 IDLESTAT = 0
; 1 OVM = 0 6 EALLOW = 0
; 0 SXM = 0 5 LOOP = 0
; 4 SPA = 0
; 3 VMAP = 1
; 2 PAGE0 = 0
; 1 DBGM = 1
; 0 INTM = 1
;
MOV *SP++,#0
MOV *SP++,#0x0A0B
POP ST1
POP ST0
;
; Jump to the EntryAddr as defined by the
; boot mode selected and continue execution
;
LRETR
最关键的是下面几句:
PUSH ACC
POP RPC
LRETR
前面提到了,CPU1BROM_startSystemBoot函数返回时,把下一段程序的入口地址保存在ACC寄存器里。这里先把ACC压栈,再出栈到RPC里!到这时,RPC就保存了下一段程序的地址,就是FLASH的入口地址0x0008 0000。
跳转到FLASH入口
当执行LRETR时,RPC的值就会加载到PC中,CPU跳转到FLASH的入口开始执行。
现在,调试器也显示到f2838x_codestartbranch.asm文件中的code_start了:
到这个时候,已经脱离了厂家内置在CPU中的boot了,运行到用户领空。后面的代码跟用户及编译器有关了。
跳转到_c_int00
再往下,就是关闭看门狗,跳转到_c_int00。
.if WD_DISABLE == 1
.text
wd_disable:
SETC OBJMODE ;Set OBJMODE for 28x object code
EALLOW ;Enable EALLOW protected register access
MOVZ DP, #7029h>>6 ;Set data page for WDCR register
MOV @7029h, #0068h ;Set WDDIS bit in WDCR to disable WD
EDIS ;Disable EALLOW protected register access
LB _c_int00 ;Branch to start of boot._asm in RTS library
.endif
_c_int00也是有源代码的:boot28.asm。这个文件位置跟编译器的安装路径和编译器的版本有关。
比如,我使用的编译器版本是V16.9.1,对应的boot28.asm文件在:
D:\ti\ccs1040\ccs\tools\compiler\ti-cgt-c2000_16.9.1.LTS\lib\src\boot28.asm
主要功能是初始化全局变量:c_int, DO_BINIT, DO_PINT, _const_init
当所有的初始化完成之后,马上就要到main函数了。稍等一下,在进入main之前,还有最后一道关:args_main
调用__args_main
最后的关键时刻,先调用__args_main。如果函数返回了,则再调用_exit。
BYPASS_AUTO_INIT:
****************************************************************************
* CALL USER'S PROGRAM *
****************************************************************************
LCR __args_main ; execute main()
LCR _exit
.endasmfunc
__args_main函数是C代码,在args_main.c文件中:
int _args_main()
{
#pragma diag_suppress 1107,173
register ARGS *pargs = (ARGS*)_symval(&__c_args__);
#pragma diag_default 1107,173
register int argc = 0;
register char **argv = 0;
if (_symval(&__c_args__) != NO_C_ARGS)
{ argc = pargs->argc; argv = pargs->argv; }
return main(argc, argv);
}
至此,已经完整地走完了孕育过程,main函数即将降临!让我们一起期待main函数的精彩表现吧!