This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

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

Other Parts Discussed in Thread: OMAP-L138

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),

unsigned int start_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节)。

static unsigned int const 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

    (unsigned int)Entry,             //0x20

    (unsigned int)UndefInstHandler,  //0x24

    (unsigned int)SWIHandler,        //0x28

    (unsigned int)AbortHandler,      //0x2C

    (unsigned int)IRQHandler,        //0x30

    (unsigned int)FIQHandler         //0x34

};

static void CopyVectorTable(void)

{

    unsigned int *dest = (unsigned int *)0xFFFF0000;

    unsigned int *src =  (unsigned int *)vecTbl;

    unsigned int 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简化过来的工程。

 

 

Starterware工程与自建工程默认ARM Mode的区别由来.pdf
  • 好贴,加精。

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

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

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

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

    其中:

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

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

    ccs5omap_test.rar
  • 学习了。

    另外:

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

  • 谢谢提醒,已更正。

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

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

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

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

  • 我在我自己新建的工程里面添加的 -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 remain
    remark #10371-D: (ULP 1.1) Detected no uses of low power mode state changing instructions
    warning #10063-D: entry-point symbol other than "_c_int00" specified:  "Entry"
    error #10010: errors encountered during linking; "test.out" not built
    >> Compilation failure
    gmake: *** [test.out] Error 1
    gmake: Target `all' not remade because of errors.
    **** Build Finished ****