TI中文支持网
TI专业的中文技术问题搜集分享网站

Starterware工程与自建工程默认ARM Mode的区别由来

Starterware工程与自建工程默认ARM Mode的区别由来

3-22-2016 Tony Tang

针对有不少用户对TI提供的starterware工程,以及自建的CCS工程里的ARM模式存在疑问,觉得有必要做个简单总结,以理清思路,节省大家的时间为目的。

#1. ARMV4以上的内核有7个不同的模式,其状态可以通过CPSR寄存器确定。(至于为什么要分这么多种模式,不是本小结的讨论内容)

(1) User Mode:用户模式。操作系统的Task一般以这种模式执行。User Mode是ARM唯一的非特权模式,这表示如果CPU处于这种模式下,很多指令将不能够执行,因此操作系统的资源得以保护。     (2) System Mode:这是V4及其以上版本所引入的特权模式。、     (3) IRQ Mode:中断模式。中断(不包括软中断)处理函数在这种模式下执行。     (4) FIQ Mode:快速中断模式。除了多了几个寄存器外,其他同IRQ一样。     (5) Supervisor Mode:管理模式。软中断(SWI)处理函数在这种模式下执行。     (6) Abort Mode:所有同内存保护相关的异常均在这种模式下执行。     (7) Undefined Mode:处理无效指令的异常处理函数在这种模式下执行

#2. ARM在不同的模式下有不同的权限,以OMAP-L138为例,修改系统寄存器需要在supervisor或system mode这种privilege模式下才行,比如配置PLL,PINMUX等。

#3. 大家发现用starterware提供的工程,一切都是那么顺利,似乎这些模式与自己无关,所以从未关心过。但是一旦自己用CCS新建一个工程,就什么也配置不了了,一看CPSR原来是在用户模式,但怎么进入privilege模式呢~~~

#4. 百度~~~ARM在user mode下要通过SWI才能进入supervisor模式,spnu151L文档里也提供了call_swi()函数接口,还可以带个参数,可是为什么一调用就跑飞了呢?还有这个参数是啥意思?

#5. 不管查什么资料,基本上都说ARM上电启动后是supervisor模式,而且上电后连上仿真器,也可以看到CPSR显示的是supervisor模式。可是运行到main就变成user mode了,那么在main之前干了啥~~~

#6. C工程从来都不是从main开始运行的,注意到map文件没,里面的entry_point是_c_int00,  如果不知道它是干啥的,看一下spnu151l的第6章,简而言之,就是C环境初始化,(写汇编可从来没这个说话). 还是看看_c_int00这个函数做了啥吧(这个函数在RTS库里,源文件在CCS的编译器安装目录下boot.asm)。

;*——————————————————

        ;* SET TO USER MODE

        ;*——————————————————

        MRS     r0, cpsr

        BIC     r0, r0, #0x1F  ; CLEAR MODES

        ORR     r0, r0, #0x10  ; SET USER MODE

        MSR     cpsr_cf, r0

简单来看,就在这里切换到了user mode,然后初始化了user mode的堆栈,再是对初始化段做初始化(__TI_auto_init),再就跳到我们的main了

 

        ;*——————————————————

        ;* INITIALIZE THE USER MODE STACK

        ;*——————————————————

        .if __TI_AVOID_EMBEDDED_CONSTANTS

        MOVW    sp, __stack

        MOVT    sp, __stack

        MOVW    r0, __STACK_SIZE

        MOVT    r0, __STACK_SIZE

        .else

        LDR     sp, c_stack

        LDR     r0, c_STACK_SIZE

        .endif

        ADD     sp, sp, r0

 

        ;*—————————————————–

        ;* ALIGN THE STACK TO 64-BITS IF EABI.

        ;*—————————————————–

        .if __TI_EABI_ASSEMBLER

        BIC     sp, sp, #0x07  ; Clear upper 3 bits for 64-bit alignment.

        .endif

 

        ;*—————————————————–

        ;* SAVE CURRENT STACK POINTER FOR SDP ANALYSIS

        ;*—————————————————–

        .if __TI_AVOID_EMBEDDED_CONSTANTS

        MOVW    r0, MAIN_FUNC_SP

        MOVT    r0, MAIN_FUNC_SP

        .else

        LDR     r0, c_mf_sp

        .endif

        STR     sp, [r0]

 

        ;*——————————————————

        ;* Perform all the required initilizations:

        ;*   – Process BINIT Table

        ;*   – Perform C auto initialization

        ;*   – Call global constructors

        ;*——————————————————

        BL      __TI_auto_init

 

        ;*——————————————————

        ;* CALL APPLICATION

        ;*——————————————————

        BL      ARGS_MAIN_RTN

 

#6. 怎么办,最简单的办法就是把这个文件加到工程里去,然后把切换模式这段代码删掉就行了(因为在工程里源文件与库文件存在同样的lable,会优先调用源文件的),但是后面想切换到user,又想从user切换到supervisor怎么办呢,再说吧~~~。

#7. 现在再来看看starterware的工程,注意一下cmd文件,里面都加了一句-e Entry,也就是说starterware的工程不是从_c_int00开始执行的,所以编译输出信息里也相应多了一句警告,这没关系,只要在main之前把该做的做了就行,_c_int00只是提供了一个简便的现成的函数,因为通常都是做同样的事件,没必要大家每次自己写一遍嘛。

#8. 对比一下Entry与_c_int00的区别吧。Entry在starterware的system_config\armv5\下对应的编译器类型目录下的init.asm文件,

Entry:

;

; Set up the Stack for Undefined mode

;

         LDR   r0, _stackptr                   ; Read and align the stack pointer

        SUB   r0, r0, #8

         BIC   r0, r0, #7

         MSR   cpsr_c, #MODE_UND|I_F_BIT       ; switch to undef  mode

        MOV   sp,r0                           ; write the stack pointer

        SUB   r0, r0, #UND_STACK_SIZE         ; give stack space

;

; Set up the Stack for abort mode

;

         MSR   cpsr_c, #MODE_ABT|I_F_BIT       ; Change to abort mode

        MOV   sp, r0                          ; write the stack pointer

        SUB   r0,r0, #ABT_STACK_SIZE          ; give stack space

;

; Set up the Stack for FIQ mode

;

         MSR   cpsr_c, #MODE_FIQ|I_F_BIT       ; change to FIQ mode

        MOV   sp,r0                           ; write the stack pointer

        SUB   r0,r0, #FIQ_STACK_SIZE          ; give stack space

;

; Set up the Stack for IRQ mode

;

         MSR   cpsr_c, #MODE_IRQ|I_F_BIT       ; change to IRQ mode

        MOV   sp,r0                           ; write the stack pointer

        SUB   r0,r0, #IRQ_STACK_SIZE          ; give stack space

;

; Set up the Stack for SVC mode

;

         MSR   cpsr_c, #MODE_SVC|I_F_BIT       ; change to SVC mode

        MOV   sp,r0                           ; write the stack pointer

        SUB   r0,r0, #SVC_STACK_SIZE          ; give stack space

;

; Set up the Stack for USer/System mode

;

         MSR   cpsr_c, #MODE_SYS|I_F_BIT       ; change to system mode

      MOV   sp,r0                           ; write the stack pointer

 

原来在这里初始化了各模式下的堆栈,顺便在最后设定在了system mode。再往下看:

;

; Clear the BSS section here

;

Clear_Bss_Section:

 

         LDR   r0, _bss_start                 ; Start address of BSS

         LDR   r1, _bss_end                   ; End address of BSS

        SUB   r1,r1,#4

        MOV   r2, #0

Loop:

         STR   r2, [r0], #4                    ; Clear one word in BSS

        CMP   r0, r1

         BLE   Loop                            ; Clear till BSS end

 

         BL    __TI_auto_init                  ; Call TI auto init

 

;

; Enter the start_boot function. The execution still happens in system mode

;

         LDR   r10, _start_boot                 ; Get the address of start_boot

        MOV   lr,pc                           ; Dummy return

         BX    r10                             ; Branch to start_boot

        SUB   pc, pc, #0x08                   ; looping

 

;         MSR   cpsr_c, #MODE_SVC|I_F_BIT       ; change to SVC mode

;         BX   lr

;

; End of the file

上面的_TI_auto_init还是一样调用的RTS库的,然后调用了start_boot(startup.c),

unsignedintstart_boot(void)

{

 

    /* Enable write-protection for registers of SYSCFG module. */

   SysCfgRegistersLock();

 

    /* Disable write-protection for registers of SYSCFG module. */

   SysCfgRegistersUnlock();

 

   PSCModuleControl(SOC_PSC_1_REGS,HW_PSC_UART2, 0, PSC_MDCTL_NEXT_ENABLE);

 

   PSCModuleControl(SOC_PSC_0_REGS,HW_PSC_AINTC, 0, PSC_MDCTL_NEXT_ENABLE);

    /* Initialize the vector table with opcodes */

    CopyVectorTable();

 

   /* Calling the main */

    main();

 

   while(1);

}

上面在调用main之前这一步很关键了。将异常向量表搬到了0xFFFF0000开始处(在L138上规定ARM的异常向量表只能放这,可以看一下L138 TRM手册的2.4节)。

staticunsignedintconst vecTbl[14]=

{

    0xE59FF018,   // 0x00: RESET

    0xE59FF018,   // 0x04: Undefined

    0xE59FF018,   // 0x08:  SWI

    0xE59FF018,   // 0x0C: Abort prefetch

    0xE59FF014,   // 0x10: Abort Data

    0xE24FF008,   // 0x14: reserved

    0xE59FF010,   // 0x18: IRQ

    0xE59FF010,   // 0x1C: FIQ

    (unsignedint)Entry,             //0x20

    (unsignedint)UndefInstHandler,  //0x24

    (unsignedint)SWIHandler,        //0x28

    (unsignedint)AbortHandler,      //0x2C

    (unsignedint)IRQHandler,        //0x30

    (unsignedint)FIQHandler         //0x34

};

staticvoidCopyVectorTable(void)

{

   unsignedint *dest = (unsignedint *)0xFFFF0000;

   unsignedint *src =  (unsignedint *)vecTbl;

   unsignedint count;

 

   for(count = 0; count <sizeof(vecTbl)/sizeof(vecTbl[0]); count++)

    {

        dest[count] = src[count];

    }

}

#9. 至于上面那个数组的trick,在内存上显示其代表的汇编就明白了,它巧妙的在向量表上加上了跳转到各异常向量的处理handler(这要通过对指定格式的了解才能整出这么个数值来):

后面带着system mode进入main后,就由用户自由发挥了。但是这里跟前面那个改boot.asm进入main后的区别是什么呢?前面我也提到如果想进切换模式怎么办。

现在的基于starterware的工程可以调用cpu.c文件里的函数进行模式切换,但是前面那种修改的工程是不行了,一调用就跑飞。原因在于,在调用swi时,程序会跳转到SWI位置即0xFFFF0008, 然后跳转到SWIHandle(见exceptionhandler.asm),

SWIHandler:

        STMFD    r13!, {r0-r1, r14}       ; Save context in SVC stack

        LDR      r0, [r14, #-4]           ; R0 points to SWI instruction

        BIC      r0, r0, #MASK_SWI_NUM    ; Get the SWI number

       CMP      r0, #458752

        MRSEQ    r1, spsr                 ; Copy SPSR 

        ORREQ    r1, r1, #0x1F            ; Change the mode to System

        MSREQ    spsr_cf, r1              ; Restore SPSR

        LDMFD    r13!, {r0-r1, pc}^       ; Restore registers from IRQ stack

 

这里就进入了supervisor mode,而且对传进来的参数做了对比确认,理论上可以做一长串的参数对比,做不同分支的处理。

为什么自己建的CCS工程不行呢,因为还没有做异常向量表的初始化,调用SWI时,系统自动跳转到0xFFFF0008,谁知道这时候这地方是一条什么指令呢,所以在调用之前必需要先初始化异常向量表等等,有兴趣自己实现吧。

附上我基于starterware简化过来的工程。

 

 

weihua li:

好贴,加精。

只可惜附件工程导入失败:提示

See details below. Error: Import failed for project 'arm9_previlege_Mode' because its meta-data cannot be interpreted. Please contact support.

Tony Tang:

回复 weihua li:

我是用CCSV6.  Version: 6.1.2.00015建的工程。目前电脑没装CCSV5.

weihua li:

回复 Tony Tang:

上传一个我的CCS V5.5下面的最小工程,供大家参考

其中:

arm端,使用定时器点了个灯,一秒一次。

dsp端,带dsp/bios系统,点灯,同样一秒一次。

Zhihua Ge:

学习了。

另外:

  文中提到的,spru151L文档 应该是spnu151L(ARM Optimizing C/C++ Compiler v15.12.0.LTS  User's Guide)吧。

Tony Tang:

回复 Zhihua Ge:

谢谢提醒,已更正。

luo qi:

写的非常好,如果早3年看到这篇文章,当时就不会绕太多弯路了

user1552761:

你好,我将startware的工程移植到 CCS3.3环境下,编译提示 找不到 __TI_auto_init  ,请问是要添加哪个库呢?

Tony Tang:

回复 user1552761:

上面总结里说了,这是RTS库里的函数,了解一下什么是RTS库:

上面的_TI_auto_init还是一样调用的RTS库的,然后调用了start_boot(startup.c),

user4699890:

我在我自己新建的工程里面添加的 -e Entry 编译出错,请问是什么原因?

**** Build of configuration Debug for project test ****
"C:\\ti\\ccsv5\\utils\\bin\\gmake" -k all 'Building target: test.out''Invoking: ARM Linker'"C:/ti/ti-cgt-arm_16.9.3.LTS/bin/armcl" -mv5e –code_state=32 -me –advice:power="all" –define=omapl132 -g –diag_warning=225 –diag_wrap=off –display_error_number –abi=eabi -z -m"test.map" –heap_size=0x800 –stack_size=0x800 -i"C:/ti/ti-cgt-arm_16.9.3.LTS/lib" -i"C:/ti/OMAPL138_StarterWare_1_10_04_01/binary/armv5/cgt_ccs/omapl138/system_config/Debug" -i"C:/ti/OMAPL138_StarterWare_1_10_04_01/binary/armv5/cgt_ccs/utils/Debug" -i"C:/ti/OMAPL138_StarterWare_1_10_04_01/binary/armv5/cgt_ccs/omapl138/drivers/Debug" -i"C:/ti/ti-cgt-arm_16.9.3.LTS/include" –reread_libs –diag_wrap=off –display_error_number –warn_sections –xml_link_info="test_linkInfo.xml" –rom_model -o "test.out"  "./main.obj" "../OMAPL132.cmd" -l"libc.a" -l"system_config.lib" -lutils.lib -ldrivers.lib <Linking>
 undefined             first referenced                                                                                                        symbol                   in file                                                                                                            ———             —————-                                                                                                       SysCfgRegistersLock   C:/ti/OMAPL138_StarterWare_1_10_04_01/binary/armv5/cgt_ccs/omapl138/system_config/Debug/system_config.lib<startup.obj> SysCfgRegistersUnlock C:/ti/OMAPL138_StarterWare_1_10_04_01/binary/armv5/cgt_ccs/omapl138/system_config/Debug/system_config.lib<startup.obj>
error #10234-D: unresolved symbols remainremark #10371-D: (ULP 1.1) Detected no uses of low power mode state changing instructionswarning #10063-D: entry-point symbol other than "_c_int00" specified:  "Entry"error #10010: errors encountered during linking; "test.out" not built
>> Compilation failuregmake: *** [test.out] Error 1gmake: Target `all' not remade because of errors.
**** Build Finished ****

赞(0)
未经允许不得转载:TI中文支持网 » Starterware工程与自建工程默认ARM Mode的区别由来
分享到: 更多 (0)