17.arm架构的异常与中断

注:以下内容学习于韦东山老师arm裸机第一期视频教程


一.arm架构的异常与中断的处理

   1.1 ARM对异常的处理流程

        1.1.1 软件初始化

            a. 设置中断源,让他可以产生中断

            b. 设置中断优先级

            c. 设置中断总开关

            

        1.1.2 正常执行程序

            对于不同的异常跳去不同的地址执行程序(这些地址一般是排在一起的,叫做异常向量),

           这些地址上只是一条跳转指令,跳去执行其它函数,如下表

        

        1.1.6 这些函数做什么事情?

            a.保存现场(各种寄存器)

            b.调用不同的处理函数

                分辨中断源

                调用不同处理函数

            c.恢复现场

            

    1.2 ARM异常种类

        在uboot 1.1.6中有如下代码


 
  1. _start: b reset /* 0地址,复位异常 */

  2. ldr pc, _undefined_instruction /* 4地址,未定义指令 */

  3. ldr pc, _software_interrupt /* 8地址,软中断指令 */

  4. ldr pc, _prefetch_abort /* 12地址,指令预取异常,如果处理器预取的指令不存在,或者该地址不允许当前指令访问 */

  5. ldr pc, _data_abort /* 14地址,数据访问终止,如果数据访问指令的目标地址不存在,或者该地址不允许当前指令访问 */

  6. ldr pc, _not_used

  7. ldr pc, _irq /* 24地址,中断异常 */

  8. ldr pc, _fiq /* 28地址,快中断异常 */

  9.  
  10. _undefined_instruction: .word undefined_instruction

  11. _software_interrupt: .word software_interrupt

  12. _prefetch_abort: .word prefetch_abort

  13. _data_abort: .word data_abort

  14. _not_used: .word not_used

  15. _irq: .word irq

  16. _fiq: .word fiq

        中断发生时CPU会强制调到0x18(24)的地方,然后跳转到其它函数进行处理

        

二.CPU模式与状态

    2.1 CPU的State

        2.1.1 ARM State

              在这种状态下,使用arm指令集,每个指令占据4字节(被编译成的机器码占据4个字节)

        2.1.2 Thumb State

              在这种状态下,使用thumb指令集,每个指令占据2字节(被编译成的机器码占据2个字节)

              thumb指令集是arm指令集压缩形式的子集,thumb更加高效,可以减少存储空间,对于嵌入式系统nor,sdram很大,不需要节省这点空间

              Thumb 不是一个完整的体系结构,不能指望处理只执行Thumb 指令而不支持 ARM 指令集.因此,Thumb 指令只需要支持通用功能,必要时可以借助于完善的 ARM 指令集

        

        2.1.3 将一个程序使用Thumb编译示例

            对于C程序,在编译时添加-mthumb即可

                修改Makefile如下:


 
  1. objs = uart.o main.o start.o SetTacc.o my_printf.o lib1funcs.o init.o

  2. A = test

  3.  
  4. all:$(objs)

  5. #arm-linux-ld -Ttext 0 -Tdata 0x800 $^ -o $(A).elf

  6. arm-linux-ld -T sdram.lds $^ -o $(A).elf

  7. arm-linux-objcopy -O binary -S $(A).elf $(A).bin

  8. arm-linux-objdump -D $(A).elf > $(A).dis

  9.  
  10. %.o:%.c

  11. arm-linux-gcc -mthumb -c -o $@ $<

  12.  
  13. %.o:%.S

  14. arm-linux-gcc -c -o $@ $<

  15.  
  16. clean:

  17. rm *.o *.elf *.bin

            对于.S程序,需要在代码中指定,在前面添加.code 32表示后续的指令使用arm指令集,在sdram_init前面添加.code 16表示下面的都使用thumb指令集,在前面需要使用bx指令进行状态切换,bx跳转的地址bit0等于1时就会进行状态的切换

            代码如下:


 
  1. .text

  2. .code 32 /* 表示下面的代码使用arm指令集 */

  3. .global _start

  4.  
  5. _start:

  6. /* 省略一些初始化代码 */

  7.  
  8. /* 获得thumb_func地址 */

  9. adr r0, thumb_func

  10. /* 由于地址是4字节对齐,最后一位等于0,因此设置最后一位等于1,切换到thumb指令集 */

  11. add r0, r0,#1

  12. bx r0

  13.  
  14. .code 16 /* 表示下面的代码使用thumb指令集 */

  15. thumb_func:

  16. bl SdramInit

  17.  
  18. mov r0, #0

  19. ldr r1, =_start

  20. ldr r2, =bss_start

  21. sub r2, r2, r1

  22. bl Copy2Sdram

  23.  
  24.  
  25. ldr r0, =bss_start

  26. ldr r1, =bss_end

  27. bl CleanBss

  28.  
  29. //bl main

  30. //ldr pc, =main

  31. /* 对thumb指令无法直接执行ldr pc, =main */

  32. ldr r0, =main

  33. mov pc, r0

  34. halt:

  35. b halt

                编译出来可以发现bin文件变小了很多    

                                                                                                                                           

    2.2 CPU的工作模式

        2.2.1 用户模式->正常程序执行的模式

        2.2.2 系统模式

        2.2.3 异常模式

                a.未定义指令模式

                b.管理模式

                c.中止模式

                    c.1 指令预取终止(CPU是流水线操作的,在执行当前指令的时候读取第二条指令,执行第三条指令,第三条指令的读取叫做预取,可能会出错)

                    c.2 数据访问终止

                d.中断模式

                e.快中断模式    

        2.2.4 快速中断模式

        

        其中,除了用户模式的其它六种模式又称为特权模式(Privileged Modes),在这六种模式下可以操作CPSR寄存器任意切换其它模式,

        但是在用户模式下不可以直接进入其它模式

        

    2.3 每种工作模式的差别

        3.3.1 主要是寄存器之间的差别,见下图

        

        3.3.2 未备份寄存器: r0-r7

                 备份寄存器:   r8-14

                 程序计数器:    r15

            

              每个模式下都有自己的r13,r14寄存器sp与lr

            对于快中断模式,有自己的备份寄存器r8-r14,在中断发生时不需要保存r8-r14这些寄存器,因此可以加快处理速度

            在linux系统中并不会使用快中断模式

              

        3.3.3 程序状态寄存器CPSR,SPSR,格式如下图

            CPSR寄存器

              a.M0-M4表示CPU当前处于哪种工作模式,可以读取进行修改(如果处于用户模式则无法修改),见下图

              

              b.bit5-T->表示当前使用的指令集是ARM还是thmub

              c.bit6-F->表示快中断使能

              d.bit7-I->表示中断使能

              e.bit8-bit27->保留位

              f.bit28-bit31->状态位

                    例如 cmp R0,R1 (如果相等会将bit30位置1)

                         beq xxx   (会去判断bit30是不是为1,是1的话则会跳转)      

          SPSR寄存器

                        发生异常时,这个寄存器会来保存被中断模式下的CPSR寄存器

                

    2.4 异常的处理流程

        2.4.1 进入异常时硬件完成:

            1.将下一条指令的地址保存在lr寄存器中,即lr_异常 = 被中断模式的下一条指令的地址(pc + 4 / pc + 8)

            2.将CPSR寄存器保存在异常模式下的SPSR,即SPSR_异常 = CPSR

            3.修改CPSR的模式位(M0-M4),进入异常模式

            4.调到向量表

            

        2.4.2 退出异常时硬件完成:

            1.PC = lr_异常 - offset,具体见下表

            

            例如发生swi异常时,可以PC = lr_svc返回

                发生fiq异常时,可以执行PC = lr_fiq - 4返回

            2. 恢复CPSR的值,CPSR = SPSR_异常

            3. 清中断(如果发生中断)

            

三.und异常模式程序示例

    


 
  1. .text

  2. .global _start

  3.  
  4. _start:

  5. b reset /* vector 0 : reset */

  6. b do_und /* vector 4 : und */

  7.  
  8. do_und:

  9. /* 执行到这里之前:

  10. * 1. lr_und保存有被中断模式中的下一条即将执行的指令的地址

  11. * 2. SPSR_und保存有被中断模式的CPSR

  12. * 3. CPSR中的M4-M0被设置为11011, 进入到und模式

  13. * 4. 跳到0x4的地方执行程序

  14. */

  15.  
  16. /* sp_und未设置, 先设置它 */

  17. ldr sp, =0x34000000

  18.  
  19. /* 在und异常处理函数中有可能会修改r0-r12, 所以先保存 */

  20. /* lr是异常处理完后的返回地址, 也要保存 */

  21. stmdb sp!, {r0-r12, lr}

  22.  
  23. /* 保存现场 */

  24. /* 处理und异常 */

  25. mrs r0, cpsr

  26. ldr r1, =und_string

  27. bl printException

  28.  
  29. /* 恢复现场 */

  30. ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */

  31.  
  32. und_string:

  33. .string "undefined instruction exception"

  34.  
  35.  
  36. reset:

  37. /* 关闭看门狗 */

  38. ldr r0, =0x53000000

  39. ldr r1, =0

  40. str r1, [r0]

  41.  
  42. /* 设置MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m */

  43. /* LOCKTIME(0x4C000000) = 0xFFFFFFFF */

  44. ldr r0, =0x4C000000

  45. ldr r1, =0xFFFFFFFF

  46. str r1, [r0]

  47.  
  48. /* CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8 */

  49. ldr r0, =0x4C000014

  50. ldr r1, =0x5

  51. str r1, [r0]

  52.  
  53. /* 设置CPU工作于异步模式 */

  54. mrc p15,0,r0,c1,c0,0

  55. orr r0,r0,#0xc0000000 //R1_nF:OR:R1_iA

  56. mcr p15,0,r0,c1,c0,0

  57.  
  58. /* 设置MPLLCON(0x4C000004) = (92<<12)|(1<<4)|(1<<0)

  59. * m = MDIV+8 = 92+8=100

  60. * p = PDIV+2 = 1+2 = 3

  61. * s = SDIV = 1

  62. * FCLK = 2*m*Fin/(p*2^s) = 2*100*12/(3*2^1)=400M

  63. */

  64. ldr r0, =0x4C000004

  65. ldr r1, =(92<<12)|(1<<4)|(1<<0)

  66. str r1, [r0]

  67.  
  68. /* 一旦设置PLL, 就会锁定lock time直到PLL输出稳定

  69. * 然后CPU工作于新的频率FCLK

  70. */

  71.  
  72.  
  73.  
  74. /* 设置内存: sp 栈 */

  75. /* 分辨是nor/nand启动

  76. * 写0到0地址, 再读出来

  77. * 如果得到0, 表示0地址上的内容被修改了, 它对应ram, 这就是nand启动

  78. * 否则就是nor启动

  79. */

  80. mov r1, #0

  81. ldr r0, [r1] /* 读出原来的值备份 */

  82. str r1, [r1] /* 0->[0] */

  83. ldr r2, [r1] /* r2=[0] */

  84. cmp r1, r2 /* r1==r2? 如果相等表示是NAND启动 */

  85. ldr sp, =0x40000000+4096 /* 先假设是nor启动 */

  86. moveq sp, #4096 /* nand启动 */

  87. streq r0, [r1] /* 恢复原来的值 */

  88.  
  89. bl sdram_init

  90. //bl sdram_init2 /* 用到有初始值的数组, 不是位置无关码 */

  91.  
  92. /* 重定位text, rodata, data段整个程序 */

  93. bl copy2sdram

  94.  
  95. /* 清除BSS段 */

  96. bl clean_bss

  97.  
  98. bl uart0_init

  99.  
  100. /* 故意加入一条未定义指令 */

  101. und_code:

  102. /* 注意,必须是未定义指令,已定义指令见下表 */

  103. .word 0xdeadc0de /* 未定义指令 */

  104.  
  105. //bl main /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */

  106. ldr pc, =main /* 绝对跳转, 跳到SDRAM */

  107.  
  108. halt:

  109. b halt

 

三.und异常模式程序的改进

    3.1 问题引入及改进

        a. 在上面的代码中,未定义指令放在了代码重定位之后,如果执行到异常处理函数时,一旦发生未定义异常,程序将会跳转(相对跳转)

           反汇编代码如下,由于使用的的b相对跳转,程序并不会调到30000008的地址,而是会跳转到0x8的地址,一旦前面的程序过多,跳转地址的偏移较高,超过4096时,

           若是NAND启动便会出错,因为RAM只有4K


 
  1. 30000000 <_start>:

  2. 30000000: ea00000e b 30000040 <reset>

  3. 30000004: eaffffff b 30000008 <do_und>

  4.  
  5. 30000008 <do_und>:

改进方法:使用绝对跳转,跳转到sdram中去,相关代码如下:


 
  1. .text

  2. .global _start

  3.  
  4. _start:

  5. b reset /* vector 0 : reset */

  6. /* 注意,不可以使用ldr pc, =und_addr,这样会将und_addr的值存放在某个内存再去读出来,有可能存放的地方超过4K

  7. * 而且,一般放在汇编文件的最后边去,然后去读取,如果汇编文件超过4K也会出错

  8. ldr pc, und_addr /* vector 4 : und */ /* 从und_addr地址处读值赋给PC,会读到do_und的地址 */

  9.  
  10. und_addr:

  11. .word do_und

  12.  
  13. do_und:

            对应反汇编如下:          


 
  1. 30000000 <_start>:

  2. 30000000: ea000012 b 30000050 <reset>

  3. 30000004: e51ff004 ldr pc, [pc, #-4] ; 30000008 <und_addr>

  4.  
  5. 30000008 <und_addr>:

  6. 30000008: 3000000c andcc r0, r0, ip

  7.  
  8. 3000000c <do_und>:

                            

        b.在重定位完之后,后面的代码都可能超过4K,因此要跳到sdram中去执行,代码如下


 
  1. ldr pc, =sdram

  2. sdram:

  3. bl uart0_init

  4.  
  5. bl print1

  6. /* 故意加入一条未定义指令 */

  7. und_code:

  8. .word 0xdeadc0de /* 未定义指令 */

  9. bl print2

  10.  
  11. //bl main /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */

  12. ldr pc, =main /* 绝对跳转, 跳到SDRAM */

  13.  
  14. halt:

  15. b halt

        c.在定义字符串时有可能字符串的长度不是4字节对齐,因此在后面要调价.align = 4,如下:    


 
  1. und_string:

  2. .string "undefined instruction exception"


 
  1. .text

  2. .global _start

  3.  
  4. _start:

  5. b reset /* vector 0 : reset */

  6. ldr pc, und_addr /* vector 4 : und */

  7.  
  8. und_addr:

  9. .word do_und

  10.  
  11. do_und:

  12. /* 执行到这里之前:

  13. * 1. lr_und保存有被中断模式中的下一条即将执行的指令的地址

  14. * 2. SPSR_und保存有被中断模式的CPSR

  15. * 3. CPSR中的M4-M0被设置为11011, 进入到und模式

  16. * 4. 跳到0x4的地方执行程序

  17. */

  18.  
  19. /* sp_und未设置, 先设置它 */

  20. ldr sp, =0x34000000

  21.  
  22. /* 在und异常处理函数中有可能会修改r0-r12, 所以先保存 */

  23. /* lr是异常处理完后的返回地址, 也要保存 */

  24. stmdb sp!, {r0-r12, lr}

  25.  
  26. /* 保存现场 */

  27. /* 处理und异常 */

  28. mrs r0, cpsr

  29. ldr r1, =und_string

  30. bl printException

  31.  
  32. /* 恢复现场 */

  33. ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */

  34.  
  35. und_string:

  36. .string "undefined instruction exception"

  37.  
  38. .align 4

  39.  
  40. reset:

  41. /* 关闭看门狗 */

  42. ldr r0, =0x53000000

  43. ldr r1, =0

  44. str r1, [r0]

  45.  
  46. /* 设置MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m */

  47. /* LOCKTIME(0x4C000000) = 0xFFFFFFFF */

  48. ldr r0, =0x4C000000

  49. ldr r1, =0xFFFFFFFF

  50. str r1, [r0]

  51.  
  52. /* CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8 */

  53. ldr r0, =0x4C000014

  54. ldr r1, =0x5

  55. str r1, [r0]

  56.  
  57. /* 设置CPU工作于异步模式 */

  58. mrc p15,0,r0,c1,c0,0

  59. orr r0,r0,#0xc0000000 //R1_nF:OR:R1_iA

  60. mcr p15,0,r0,c1,c0,0

  61.  
  62. /* 设置MPLLCON(0x4C000004) = (92<<12)|(1<<4)|(1<<0)

  63. * m = MDIV+8 = 92+8=100

  64. * p = PDIV+2 = 1+2 = 3

  65. * s = SDIV = 1

  66. * FCLK = 2*m*Fin/(p*2^s) = 2*100*12/(3*2^1)=400M

  67. */

  68. ldr r0, =0x4C000004

  69. ldr r1, =(92<<12)|(1<<4)|(1<<0)

  70. str r1, [r0]

  71.  
  72. /* 一旦设置PLL, 就会锁定lock time直到PLL输出稳定

  73. * 然后CPU工作于新的频率FCLK

  74. */

  75.  
  76.  
  77.  
  78. /* 设置内存: sp 栈 */

  79. /* 分辨是nor/nand启动

  80. * 写0到0地址, 再读出来

  81. * 如果得到0, 表示0地址上的内容被修改了, 它对应ram, 这就是nand启动

  82. * 否则就是nor启动

  83. */

  84. mov r1, #0

  85. ldr r0, [r1] /* 读出原来的值备份 */

  86. str r1, [r1] /* 0->[0] */

  87. ldr r2, [r1] /* r2=[0] */

  88. cmp r1, r2 /* r1==r2? 如果相等表示是NAND启动 */

  89. ldr sp, =0x40000000+4096 /* 先假设是nor启动 */

  90. moveq sp, #4096 /* nand启动 */

  91. streq r0, [r1] /* 恢复原来的值 */

  92.  
  93. bl sdram_init

  94. //bl sdram_init2 /* 用到有初始值的数组, 不是位置无关码 */

  95.  
  96. /* 重定位text, rodata, data段整个程序 */

  97. bl copy2sdram

  98.  
  99. /* 清除BSS段 */

  100. bl clean_bss

  101.  
  102.  
  103. ldr pc, =sdram

  104. sdram:

  105. bl uart0_init

  106.  
  107. bl print1

  108. /* 故意加入一条未定义指令 */

  109. und_code:

  110. .word 0xdeadc0de /* 未定义指令 */

  111. bl print2

  112.  
  113. //bl main /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */

  114. ldr pc, =main /* 绝对跳转, 跳到SDRAM */

  115.  
  116. halt:

  117. b halt

 

四.swi异常模式程序示例

    4.1 swi异常介绍

        在linux系统中,app运行于用户模式,这种模式受限制,不可访问硬件,当应用程序想要访问硬件时,必须切换模式,怎么切换?

        swi就是软件中断,由于用户模式不能够修改cpsr寄存器来切换模式.因此,执行swi指令 + val来发生异常,可以根据val值判断为什么发生异常

    

    4.2 程序示例

        


 
  1. .text

  2. .global _start

  3.  
  4. _start:

  5. b reset /* vector 0 : reset */

  6. ldr pc, und_addr /* vector 4 : und */

  7. ldr pc, swi_addr /* vector 8 : swi */

  8.  
  9. und_addr:

  10. .word do_und

  11.  
  12. swi_addr:

  13. .word do_swi

  14.  
  15. do_und:

  16. /* 执行到这里之前:

  17. * 1. lr_und保存有被中断模式中的下一条即将执行的指令的地址

  18. * 2. SPSR_und保存有被中断模式的CPSR

  19. * 3. CPSR中的M4-M0被设置为11011, 进入到und模式

  20. * 4. 跳到0x4的地方执行程序

  21. */

  22.  
  23. /* sp_und未设置, 先设置它 */

  24. ldr sp, =0x34000000

  25.  
  26. /* 在und异常处理函数中有可能会修改r0-r12, 所以先保存 */

  27. /* lr是异常处理完后的返回地址, 也要保存 */

  28. stmdb sp!, {r0-r12, lr}

  29.  
  30. /* 保存现场 */

  31. /* 处理und异常 */

  32. mrs r0, cpsr

  33. ldr r1, =und_string

  34. bl printException

  35.  
  36. /* 恢复现场 */

  37. ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */

  38.  
  39. und_string:

  40. .string "undefined instruction exception"

  41.  
  42. .align 4

  43.  
  44. do_swi:

  45. /* 执行到这里之前:

  46. * 1. lr_svc保存有被中断模式中的下一条即将执行的指令的地址

  47. * 2. SPSR_svc保存有被中断模式的CPSR

  48. * 3. CPSR中的M4-M0被设置为10011, 进入到svc模式

  49. * 4. 跳到0x08的地方执行程序

  50. */

  51.  
  52. /* sp_svc未设置, 先设置它 */

  53. ldr sp, =0x33e00000

  54.  
  55. /* 在swi异常处理函数中有可能会修改r0-r12, 所以先保存 */

  56. /* lr是异常处理完后的返回地址, 也要保存 */

  57. stmdb sp!, {r0-r12, lr}

  58.  
  59. /* 保存现场 */

  60. /* 处理swi异常 */

  61. mrs r0, cpsr

  62. ldr r1, =swi_string

  63. bl printException

  64.  
  65. /* 恢复现场 */

  66. ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */

  67.  
  68. swi_string:

  69. .string "swi exception"

  70.  
  71. .align 4

  72.  
  73. reset:

  74. /* 关闭看门狗 */

  75. ldr r0, =0x53000000

  76. ldr r1, =0

  77. str r1, [r0]

  78.  
  79. /* 设置MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m */

  80. /* LOCKTIME(0x4C000000) = 0xFFFFFFFF */

  81. ldr r0, =0x4C000000

  82. ldr r1, =0xFFFFFFFF

  83. str r1, [r0]

  84.  
  85. /* CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8 */

  86. ldr r0, =0x4C000014

  87. ldr r1, =0x5

  88. str r1, [r0]

  89.  
  90. /* 设置CPU工作于异步模式 */

  91. mrc p15,0,r0,c1,c0,0

  92. orr r0,r0,#0xc0000000 //R1_nF:OR:R1_iA

  93. mcr p15,0,r0,c1,c0,0

  94.  
  95. /* 设置MPLLCON(0x4C000004) = (92<<12)|(1<<4)|(1<<0)

  96. * m = MDIV+8 = 92+8=100

  97. * p = PDIV+2 = 1+2 = 3

  98. * s = SDIV = 1

  99. * FCLK = 2*m*Fin/(p*2^s) = 2*100*12/(3*2^1)=400M

  100. */

  101. ldr r0, =0x4C000004

  102. ldr r1, =(92<<12)|(1<<4)|(1<<0)

  103. str r1, [r0]

  104.  
  105. /* 一旦设置PLL, 就会锁定lock time直到PLL输出稳定

  106. * 然后CPU工作于新的频率FCLK

  107. */

  108.  
  109.  
  110.  
  111. /* 设置内存: sp 栈 */

  112. /* 分辨是nor/nand启动

  113. * 写0到0地址, 再读出来

  114. * 如果得到0, 表示0地址上的内容被修改了, 它对应ram, 这就是nand启动

  115. * 否则就是nor启动

  116. */

  117. mov r1, #0

  118. ldr r0, [r1] /* 读出原来的值备份 */

  119. str r1, [r1] /* 0->[0] */

  120. ldr r2, [r1] /* r2=[0] */

  121. cmp r1, r2 /* r1==r2? 如果相等表示是NAND启动 */

  122. ldr sp, =0x40000000+4096 /* 先假设是nor启动 */

  123. moveq sp, #4096 /* nand启动 */

  124. streq r0, [r1] /* 恢复原来的值 */

  125.  
  126. bl sdram_init

  127. //bl sdram_init2 /* 用到有初始值的数组, 不是位置无关码 */

  128.  
  129. /* 重定位text, rodata, data段整个程序 */

  130. bl copy2sdram

  131.  
  132. /* 清除BSS段 */

  133. bl clean_bss

  134.  
  135. /* 复位之后, cpu处于svc模式

  136. * 现在, 切换到usr模式

  137. */

  138. mrs r0, cpsr /* 读出cpsr */

  139. bic r0, r0, #0xf /* 修改M4-M0为0b10000, 进入usr模式 */

  140. msr cpsr, r0

  141.  
  142. /* 设置 sp_usr */

  143. ldr sp, =0x33f00000

  144.  
  145. ldr pc, =sdram

  146. sdram:

  147. bl uart0_init

  148.  
  149. bl print1

  150. /* 故意加入一条未定义指令 */

  151. und_code:

  152. .word 0xdeadc0de /* 未定义指令 */

  153. bl print2

  154.  
  155. swi 0x123 /* 执行此命令, 触发SWI异常, 进入0x8执行 */

  156.  
  157. //bl main /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */

  158. ldr pc, =main /* 绝对跳转, 跳到SDRAM */

  159.  
  160. halt:

  161. b halt


            

        注意,CPU一上电处于管理模式,切换到用户模式再来执行SWI指令(在别的模式也可以执行,这么做只是为了测试用户模式),修改CPSR寄存器的M4-M0为0b10000即可进入用户模式

        读出swi + val的val值,由于在发生异常时,lr寄存器会保存异常的下一条指令的地址,因此发生swi异常指令的地址就是lr-4,根据地址及机器码格式便可以找出val值

        代码如下:

            


 
  1. .text

  2. .global _start

  3.  
  4. _start:

  5. b reset /* vector 0 : reset */

  6. ldr pc, und_addr /* vector 4 : und */

  7. ldr pc, swi_addr /* vector 8 : swi */

  8.  
  9. und_addr:

  10. .word do_und

  11.  
  12. swi_addr:

  13. .word do_swi

  14.  
  15. do_und:

  16. /* 执行到这里之前:

  17. * 1. lr_und保存有被中断模式中的下一条即将执行的指令的地址

  18. * 2. SPSR_und保存有被中断模式的CPSR

  19. * 3. CPSR中的M4-M0被设置为11011, 进入到und模式

  20. * 4. 跳到0x4的地方执行程序

  21. */

  22.  
  23. /* sp_und未设置, 先设置它 */

  24. ldr sp, =0x34000000

  25.  
  26. /* 在und异常处理函数中有可能会修改r0-r12, 所以先保存 */

  27. /* lr是异常处理完后的返回地址, 也要保存 */

  28. stmdb sp!, {r0-r12, lr}

  29.  
  30. /* 保存现场 */

  31. /* 处理und异常 */

  32. mrs r0, cpsr

  33. ldr r1, =und_string

  34. bl printException

  35.  
  36. /* 恢复现场 */

  37. ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */

  38.  
  39. und_string:

  40. .string "undefined instruction exception"

  41.  
  42. .align 4

  43.  
  44. do_swi:

  45. /* 执行到这里之前:

  46. * 1. lr_svc保存有被中断模式中的下一条即将执行的指令的地址

  47. * 2. SPSR_svc保存有被中断模式的CPSR

  48. * 3. CPSR中的M4-M0被设置为10011, 进入到svc模式

  49. * 4. 跳到0x08的地方执行程序

  50. */

  51.  
  52. /* sp_svc未设置, 先设置它 */

  53. ldr sp, =0x33e00000

  54.  
  55. /* 保存现场 */

  56. /* 在swi异常处理函数中有可能会修改r0-r12, 所以先保存 */

  57. /* lr是异常处理完后的返回地址, 也要保存 */

  58. stmdb sp!, {r0-r12, lr}

  59.  
  60. /* 由于先调用了printException函数,会使用r0-r3来传递参数,并且使用完不会恢复,因此不能用r0-r3保存lr寄存器的值,r4-r8也会被使用,但是使用完后寄存器的值被恢复原来的值 */

  61. mov r4, lr

  62.  
  63. /* 处理swi异常 */

  64. mrs r0, cpsr

  65. ldr r1, =swi_string

  66. bl printException

  67.  
  68. sub r0, r4, #4

  69. bl printSWIVal

  70.  
  71. /* 恢复现场 */

  72. ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */

  73.  
  74. swi_string:

  75. .string "swi exception"

  76.  
  77. .align 4

  78.  
  79. reset:

  80. /* 关闭看门狗 */

  81. ldr r0, =0x53000000

  82. ldr r1, =0

  83. str r1, [r0]

  84.  
  85. /* 设置MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m */

  86. /* LOCKTIME(0x4C000000) = 0xFFFFFFFF */

  87. ldr r0, =0x4C000000

  88. ldr r1, =0xFFFFFFFF

  89. str r1, [r0]

  90.  
  91. /* CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8 */

  92. ldr r0, =0x4C000014

  93. ldr r1, =0x5

  94. str r1, [r0]

  95.  
  96. /* 设置CPU工作于异步模式 */

  97. mrc p15,0,r0,c1,c0,0

  98. orr r0,r0,#0xc0000000 //R1_nF:OR:R1_iA

  99. mcr p15,0,r0,c1,c0,0

  100.  
  101. /* 设置MPLLCON(0x4C000004) = (92<<12)|(1<<4)|(1<<0)

  102. * m = MDIV+8 = 92+8=100

  103. * p = PDIV+2 = 1+2 = 3

  104. * s = SDIV = 1

  105. * FCLK = 2*m*Fin/(p*2^s) = 2*100*12/(3*2^1)=400M

  106. */

  107. ldr r0, =0x4C000004

  108. ldr r1, =(92<<12)|(1<<4)|(1<<0)

  109. str r1, [r0]

  110.  
  111. /* 一旦设置PLL, 就会锁定lock time直到PLL输出稳定

  112. * 然后CPU工作于新的频率FCLK

  113. */

  114.  
  115.  
  116.  
  117. /* 设置内存: sp 栈 */

  118. /* 分辨是nor/nand启动

  119. * 写0到0地址, 再读出来

  120. * 如果得到0, 表示0地址上的内容被修改了, 它对应ram, 这就是nand启动

  121. * 否则就是nor启动

  122. */

  123. mov r1, #0

  124. ldr r0, [r1] /* 读出原来的值备份 */

  125. str r1, [r1] /* 0->[0] */

  126. ldr r2, [r1] /* r2=[0] */

  127. cmp r1, r2 /* r1==r2? 如果相等表示是NAND启动 */

  128. ldr sp, =0x40000000+4096 /* 先假设是nor启动 */

  129. moveq sp, #4096 /* nand启动 */

  130. streq r0, [r1] /* 恢复原来的值 */

  131.  
  132. bl sdram_init

  133. //bl sdram_init2 /* 用到有初始值的数组, 不是位置无关码 */

  134.  
  135. /* 重定位text, rodata, data段整个程序 */

  136. bl copy2sdram

  137.  
  138. /* 清除BSS段 */

  139. bl clean_bss

  140.  
  141. /* 复位之后, cpu处于svc模式

  142. * 现在, 切换到usr模式

  143. */

  144. mrs r0, cpsr /* 读出cpsr */

  145. bic r0, r0, #0xf /* 修改M4-M0为0b10000, 进入usr模式 */

  146. msr cpsr, r0

  147.  
  148. /* 设置 sp_usr */

  149. ldr sp, =0x33f00000

  150.  
  151. ldr pc, =sdram

  152. sdram:

  153. bl uart0_init

  154.  
  155. bl print1

  156. /* 故意加入一条未定义指令 */

  157. und_code:

  158. .word 0xdeadc0de /* 未定义指令 */

  159. bl print2

  160.  
  161. swi 0x123 /* 执行此命令, 触发SWI异常, 进入0x8执行 */

  162.  
  163. //bl main /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */

  164. ldr pc, =main /* 绝对跳转, 跳到SDRAM */

  165.  
  166. halt:

  167. b halt

  168.  
  169. printSWIVal函数如下,其中swi指令格式见下图,取出低24位即可:

  170.  
  171. void printSWIVal(unsigned int *pSWI)

  172. {

  173. puts("SWI val = ");

  174. printHex(*pSWI & ~0xff000000);

  175. puts("\n\r");

  176.  
阅读更多

没有更多推荐了,返回首页