TI论坛
直播中

李波

8年用户 1522经验值
私信 关注

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

本帖最后由 一只耳朵怪 于 2018-6-22 09:23 编辑


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_configarmv5下对应的编译器类型目录下的init.asm文件,
  1. Entry:
  2. ;
  3. ; Set up the Stack for Undefined mode
  4. ;
  5.          LDR   r0, _stackptr                   ; Read and align the stack pointer
  6.          SUB   r0, r0, #8
  7.          BIC   r0, r0, #7
  8.          MSR   cpsr_c, #MODE_UND|I_F_BIT       ; switch to undef  mode
  9.          MOV   sp,r0                           ; write the stack pointer
  10.          SUB   r0, r0, #UND_STACK_SIZE         ; give stack space
  11. ;
  12. ; Set up the Stack for abort mode
  13. ;
  14.          MSR   cpsr_c, #MODE_ABT|I_F_BIT       ; Change to abort mode
  15.          MOV   sp, r0                          ; write the stack pointer
  16.          SUB   r0,r0, #ABT_STACK_SIZE          ; give stack space
  17. ;
  18. ; Set up the Stack for FIQ mode
  19. ;
  20.          MSR   cpsr_c, #MODE_FIQ|I_F_BIT       ; change to FIQ mode
  21.          MOV   sp,r0                           ; write the stack pointer
  22.          SUB   r0,r0, #FIQ_STACK_SIZE          ; give stack space
  23. ;
  24. ; Set up the Stack for IRQ mode
  25. ;
  26.          MSR   cpsr_c, #MODE_IRQ|I_F_BIT       ; change to IRQ mode
  27.          MOV   sp,r0                           ; write the stack pointer
  28.          SUB   r0,r0, #IRQ_STACK_SIZE          ; give stack space
  29. ;
  30. ; Set up the Stack for SVC mode
  31. ;
  32.          MSR   cpsr_c, #MODE_SVC|I_F_BIT       ; change to SVC mode
  33.          MOV   sp,r0                           ; write the stack pointer
  34.          SUB   r0,r0, #SVC_STACK_SIZE          ; give stack space
  35. ;
  36. ; Set up the Stack for USer/System mode
  37. ;
  38.          MSR   cpsr_c, #MODE_SYS|I_F_BIT       ; change to system mode
  39.        MOV   sp,r0                           ; write the stack pointer

  40. 原来在这里初始化了各模式下的堆栈,顺便在最后设定在了system mode。再往下看:
  41. ;
  42. ; Clear the BSS section here
  43. ;
  44. Clear_Bss_Section:

  45.          LDR   r0, _bss_start                 ; Start address of BSS
  46.          LDR   r1, _bss_end                   ; End address of BSS
  47.          SUB   r1,r1,#4
  48.          MOV   r2, #0
  49. Loop:
  50.          STR   r2, [r0], #4                    ; Clear one word in BSS
  51.          CMP   r0, r1
  52.          BLE   Loop                            ; Clear till BSS end

  53.          BL    __TI_auto_init                  ; Call TI auto init

  54. ;
  55. ; Enter the start_boot function. The execution still happens in system mode
  56. ;
  57.          LDR   r10, _start_boot                 ; Get the address of start_boot
  58.          MOV   lr,pc                           ; Dummy return
  59.          BX    r10                             ; Branch to start_boot
  60.          SUB   pc, pc, #0x08                   ; looping

  61. ;         MSR   cpsr_c, #MODE_SVC|I_F_BIT       ; change to SVC mode
  62. ;         BX   lr
  63. ;
  64. ; End of the file
  65. 上面的_TI_auto_init还是一样调用的RTS库的,然后调用了start_boot(startup.c),
  66. unsigned int start_boot(void)
  67. [

  68.     /* Enable write-protection for registers of SYSCFG module. */
  69.     SysCfgRegistersLock();

  70.     /* Disable write-protection for registers of SYSCFG module. */
  71.     SysCfgRegistersUnlock();

  72.     PSCModuleControl(SOC_PSC_1_REGS, HW_PSC_UART2, 0, PSC_MDCTL_NEXT_ENABLE);

  73.     PSCModuleControl(SOC_PSC_0_REGS, HW_PSC_AINTC, 0, PSC_MDCTL_NEXT_ENABLE);
  74.     /* Initialize the vector table with opcodes */
  75.     CopyVectorTable();

  76.    /* Calling the main */
  77.     main();

  78.     while(1);
  79. ]
  80. 上面在调用main之前这一步很关键了。将异常向量表搬到了0xFFFF0000开始处(在L138上规定ARM的异常向量表只能放这,可以看一下L138 TRM手册的2.4节)。
  81. static unsigned int const vecTbl[14]=
  82. [
  83.     0xE59FF018,   // 0x00: RESET
  84.     0xE59FF018,   // 0x04: Undefined
  85.     0xE59FF018,   // 0x08:  SWI
  86.     0xE59FF018,   // 0x0C: Abort prefetch
  87.     0xE59FF014,   // 0x10: Abort Data
  88.     0xE24FF008,   // 0x14: reserved
  89.     0xE59FF010,   // 0x18: IRQ
  90.     0xE59FF010,   // 0x1C: FIQ
  91.     (unsigned int)Entry,             //0x20
  92.     (unsigned int)UndefInstHandler,  //0x24
  93.     (unsigned int)SWIHandler,        //0x28
  94.     (unsigned int)AbortHandler,      //0x2C
  95.     (unsigned int)IRQHandler,        //0x30
  96.     (unsigned int)FIQHandler         //0x34
  97. ];
  98. static void CopyVectorTable(void)
  99. [
  100.     unsigned int *dest = (unsigned int *)0xFFFF0000;
  101.     unsigned int *src =  (unsigned int *)vecTbl;
  102.     unsigned int count;

  103.     for(count = 0; count < sizeof(vecTbl)/sizeof(vecTbl[0]); count++)
  104.     [
  105.         dest[count] = src[count];
  106.     ]
  107. ]

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


                                                                          http://processors.wiki.ti.com/index.php/Main_Page
Think Over Before Asking.
http://www.catb.org/~esr/faqs/smart-questions.html#goal

回帖(9)

张小林

2018-6-21 00:44:07
好贴,加精。
只可惜附件工程导入失败:提示
See details below.
Error: Import failed for project 'arm9_previlege_Mode' because its meta-data cannot be interpreted. Please contact support.
举报

李波

2018-6-21 00:58:44
引用: xxsxsjd 发表于 2018-6-21 00:44
好贴,加精。
只可惜附件工程导入失败:提示
See details below.

我是用CCSV6.  Version: 6.1.2.00015建的工程。目前电脑没装CCSV5.
                                                                         http://processors.wiki.ti.com/index.php/Main_Page
Think Over Before Asking.
http://www.catb.org/~esr/faqs/smart-questions.html#goal
举报

张小林

2018-6-21 01:15:55
引用: lifei639156 发表于 2018-6-21 00:58
我是用CCSV6.  Version: 6.1.2.00015建的工程。目前电脑没装CCSV5.
                                                                         http://processors.wiki.ti.com/index.php/Main_Page
Think Over Before Asking.

上传一个我的CCS V5.5下面的最小工程,供大家参考
其中:
arm端,使用定时器点了个灯,一秒一次。
dsp端,带dsp/bios系统,点灯,同样一秒一次。
举报

欧丽娜

2018-6-21 01:25:34
学习了。
另外:
  文中提到的,spru151L文档 应该是spnu151L(ARM Optimizing C/C++ Compiler v15.12.0.LTS  User's Guide)吧。
举报

更多回帖

×
20
完善资料,
赚取积分