写点什么

嵌入式 ARM 设计编程 (四) ARM 启动过程控制

作者:timerring
  • 2023-02-17
    山东
  • 本文字数:3826 字

    阅读完需:约 13 分钟

文章和代码已归档至【Github 仓库:hardware-tutorial】,需要的朋友们自取。或者关注公众号【AIShareLab】,回复 嵌入式 也可获取。


一、实验目的


(1) 掌握建立基本完整的 ARM 工程,包含启动代码,C 语言程序等;


(2) 了解 ARM 启动过程,学会编写简单的 C 语言程序和汇编启动代码并进行调试;


(3) 掌握如何指定代码入口地址与入口点;


(4) 掌握通过 memory/register/watch/variable 窗口分析判断结果。


二、实验环境


硬件:PC 机。


软件:ADS1.2 集成开发环境


三、实验内容


使用汇编语言编写初始化程序,并引导至 C 语言 main 函数,用汇编语言编写延时函数实现毫秒级的延时,在 C 语言中调用延时函数,实现 1s 钟定时。


四、实验要求


(1) 在 ADS 下创建一个工程 armasmc,编写 3 个文件,如下图所示:



其中一个初始化汇编语言文件 Init.s,该文件中主要完成异常矢量表的建立,模式堆栈初始化,并将程序引导至 C 语言的 main 函数。


C 语言程序保存为 armasmc.c。 C 语言中调用汇编语言文件 delay.s 中的毫秒延时程序 delayxms,C 语言将延时的毫秒数通过参数传递到汇编语言,汇编语言完成延时,然后返回 C 语言函数。


通过 AXD 运用单步执行方式调试程序。观察程序执行过程中的寄存器及存储器的变化情况。


(2)实验过程中请记录并思考以下内容:


1)如何建立异常矢量入口表?


2)如何在汇编语言中切换至 C 语言的 main 函数?。


3)如何在 C 语言中调用汇编语言函数,并完成参数传递?


4)汇编语言函数中用到的寄存器如何保护与恢复,为什么要保护参考程序中的 R11?


5)将 delay.s 中的 R11 改成 R4,并将两条 R11 的保护与恢复语句 stmfd sp!,{r11} 和 ldmfd sp!,{r11}删掉,在 C 语言程序中的语句 i--处设置端点,观察运行过程中变量 i 的变化情况,并解释其中的原因。


五、实验情况:


1、实验源代码(含注释):


Init.s 代码:


;************************ entry.s **************************** IMPORT Main  ;在汇编程序调用该c程序前要在汇编语言程序中使用IMPORT伪操作来声明该c程序
area Init,code,readonly ;定义CODE片段Init 只读 entry ;设置程序入口伪指令 code32 ;以下为32位的ARM程序; *********** Setup interrupt/exception vector *******************start b Reset_Handler ;异常矢量表,根据异常矢量表进入不同模式的中断程序 Undefined_Handler b Undefined_HandlerSWI_Handler b SWI_HandlerPrefetch_handler b Prefetch_handlerAbort_Handler b Abort_Handler nop ;Reserved vectorIRQ_Handler b IRQ_HandlerFIQ_Handler b FIQ_Handler
Reset_Handler ;Reset中断,为整个中断的实际入口点 bl initstack ;初始化各模式下的堆栈指针 ;切换至用户模式堆 msr cpsr_c,#0xd0 ;110 10000 bl Main
halt b halt
initstack mov r0,lr ;r0<--lr,因为各种模式下r0是相同的而各个模式? ;设置管理模式堆栈 msr cpsr_c,#0xd3 ;110 10011 ldr sp,stacksvc ;设置中断模式堆栈 msr cpsr_c,#0xd2 ;110 10010 ldr sp,stackirq ;设置快速中断模式堆栈 msr cpsr_c,#0xd1 ;110 10001 ldr sp,stackfiq ;设置中止模式堆栈 msr cpsr_c,#0xd7 ;110 10111 ldr sp,stackabt ;设置未定义模式堆栈 msr cpsr_c,#0xdb ;110 11011 ldr sp,stackund ;设置系统模式堆栈 msr cpsr_c,#0xdf ;110 11111 ldr sp,stackusr mov pc,r0 ;返回 LTORG
stackusr dcd usrstackspace+128stacksvc dcd svcstackspace+128stackirq dcd irqstackspace+128stackfiq dcd fiqstackspace+128stackabt dcd abtstackspace+128stackund dcd undstackspace+128
area Interrupt,data,READWRITE ;分配堆栈空间usrstackspace space 128svcstackspace space 128irqstackspace space 128fiqstackspace space 128abtstackspace space 128undstackspace space 128 end
复制代码


delay.s 代码:


;************************* delay.s *****************************  EXPORT delayxms ;EXPORT伪指令用于在程序中声明一个全局的标号,该标号可在其他的文件中引用  area delay,code,readonly  ;定义code片段delay只读  code32  ;以下为32位的ARM程序  ;下面是延时若干ms的子程序      delayxms     stmfd sp!,{r11} ; 寄存器入栈     sub r0,r0,#1 ;r0=r0-1     ldr r11,=1000 ;加载至r11中loop2     sub r11,r11,#1 ;每次将r11自减一     cmp r11,#0x0 ;将r11与0比较     bne loop2  ;比较的结果不为0,则继续调用loop2       cmp r0,#0x0    ;将r0与0比较     bne delayxms   ;比较的结果不为0,则继续调用delayxms     ldmfd sp!,{r11};     mov pc,lr;返回          end
复制代码


armasmc.c 代码:


//*************************armasmc.c******************************#include <stdio.h>int Main(){    extern void delayxms(int xms);  //在C程序调用汇编程序之前需要在C语言程序中使用extern关键词来声明该汇编程序    int i=100;    while(1)  {     delayxms(1000); // 调用delayxms汇编程序     i--;     if(i==0)       i=100;  }  return 0;}
复制代码


2、实验过程(含结果截图及相应文字解释):


1.如何建立异常矢量入口表?


答:建立异常矢量入口表需要设置中断类型号,并且要设置中断服务子程序段地址,以根据异常矢量表进入不同模式的中断程序。在实验程序中也有定义:



2.如何在汇编语言中切换至 C 语言的 main 函数?



答:由上代码可知,为保证程序调用时参数的正确传递,汇编程序设计要遵守 ATPCS(ARM-Thumb Produce Call Standard),它是 ARM 程序和 Thumb 程序中子程序调用的基本规则,目的是为了使单独编译的 C 语言程序和汇编程序之间能够相互调用。这些基本规则包括子程序调用过程中寄存器的使用规则、数据栈的使用规则和参数的传递规则。在 C 程序中不需要任何关键字来声明将被汇编语言调用的 C 程序,但需要在汇编语言程序之前使用 IMPORT 伪操作来声明该 C 程序。在汇编程序中通过 BL 指令来调用子程序。同时,汇编程序可以通过地址间接访问在 C 语言程序中声明的全局变量。通过使用 IMPORT 关键词引入全局变量,并利用 LDR 和 STR 指令根据全局变量的地址可以访问它们。


3.如何在 C 语言中调用汇编语言函数,并完成参数传递?




答:为了保证程序调用时参数的正确传递,汇编程序设计要遵守 ATPCS。在汇编程序中需要使用 EXPORT 伪操作来声明,同时,在 C 程序中调用该汇编程序之前需要在 C 语言程序中使用 extern 关键词来声明该汇编程序。


4.汇编语言函数中用到的寄存器如何保护与恢复,为什么要保护参考程序中的 R11?


答:汇编语言函数中用到的寄存器通过压栈来保护,出栈来恢复。根据 ATPCS 规则,R11 对应 ARM 状态局部变量寄存器 8,R11 中含有循环次数的重要参量,因此要保护 R11 避免在程序运行与调用过程中受到影响而导致程序异常。


5.将 delay.s 中的 R11 改成 R4,并将两条 R11 的保护与恢复语句 stmfd sp!,{r11} 和 ldmfd sp!,{r11}删掉,在 C 语言程序中的语句 i--处设置端点,观察运行过程中变量 i 的变化情况,并解释其中的原因。


修改程序如下:



答:由上可知 R4 对应局部变量寄存器 1,即变量 i,因此在子程序 delay.s 中,R4 的值减为 0,若不进行保护,则返回 C 程序后自减-1,导致变量 i 的值变为-1,此时将无法满足 0 的条件,也就无法执行 if,导致 i 会一直递减下去,最终无法停止。


六、总结


本次有关汇编与 C 语言相互调用的部分,建立异常矢量入口表的方法,即需要设置中断类型号,并且要设置中断服务子程序段地址,以根据异常矢量表进入不同模式的中断程序。此外,还有 ARM 程序和 Thumb 程序中子程序调用的基本规则 ATPCS(ARM-Thumb Produce Call Standard),目的是为了使单独编译的 C 语言程序和汇编程序之间能够相互调用。这些基本规则包括子程序调用过程中寄存器的使用规则、数据栈的使用规则和参数的传递规则,为调用提供了相关的规范。其中汇编程序访问全局 C 变量的方法是:汇编程序可以通过地址间接访问在 C 语言程序中声明的全局变量。通过使用 IMPORT 关键词引入全局变量,并利用 LDR 和 STR 指令根据全局变量的地址可以访问它们。在 C 语言程序中调用汇编程序的方法是:在汇编程序中需要使用 EXPORT 伪操作来声明,使得本程序可以被其它程序调用。同时,在 C 程序调用该汇编程序之前需要在 C 语言程序中使用 extern 关键词来声明该汇编程序。而在汇编程序中调用 C 语言程序的方法是:在 C 程序中不需要使用任何关键字来声明将被汇编语言调用的 C 程序,但是在汇编程序调用该 C 程序之前需要在汇编语言程序中使用 IMPORT 伪操作来声明该 C 程序。在汇编程序中通过 BL 指令来调用子程序。




欢迎关注公众号【AIShareLab】,一起交流更多相关知识,前沿算法,Paper 解读,项目源码,面经总结。

发布于: 刚刚阅读数: 3
用户头像

timerring

关注

公众号【AIShareLab】 2022-07-14 加入

公众号【AIShareLab】

评论

发布
暂无评论
嵌入式ARM设计编程(四) ARM启动过程控制_FPGA_timerring_InfoQ写作社区