树莓派ARM汇编语言编程十讲(第2讲)

内容简介

树莓派单板机(Raspberry Pi Single Computer)是一种极了不起的产品,用户可以以非常低的成本获得一个Linux环境并带GPIO硬件扩展的迷你计算机系统。新一代树莓派4B还提供了良好的工业物联网和AIoT支持。树莓派单板机拥有完整的生态链,软硬件资源丰富,是嵌入式系统开发和智能硬件产品创新的很好选择。
作为嵌入式系统与智能硬件开发基础中的基础,汇编语言是许多从事信息科学和工程领域的技术人员应该掌握的一项基本技能。目前,市场上针对树莓派单板机系统介绍C、Scratch、Python等编程语言与实践方面的资源很多,但鲜有系统针对树莓派单板机ARM汇编语言编程方面的介绍。这里以袁志勇主编的《嵌入式系统原理与应用技术》(北京航空航天大学出版社2019年1月第3版)一书中ARM汇编语言编程知识为基础,采用树莓派单板机及Linux操作系统验证平台,较系统地介绍树莓派ARM汇编语言编程技术与示例。由于准备仓促,不妥之处,还请各位不吝赐教。

第2讲:树莓派系列ARM处理器及使用GNU GDB命令调试ARM汇编程序

第2讲目录
·树莓派系列ARM处理器简介
·大端存储和小端存储
·ARM处理器内部寄存器
·使用GNU GDB命令调试ARM汇编程序及观察树莓派ARM寄存器

一、树莓派系列ARM处理器介绍

ARM体系结构RISC处理器系列的一些典型芯片型号见图1所示。从图1(红色字体的文字)可知,第1代树莓派到目前的第4代树莓派均采用了博通公司定制的ARM处理器芯片,第3代树莓派3B和第4代树莓派4B是64位处理器,第1代树莓派B+和第2代树莓派2B是32位处理器。这里要说明的是,64位处理器可运行64位操作系统或32位操作系统,而32位处理器不能运行64位操作系统。目前,官方随树莓派3B和树莓派4B发行的一般是32位操作系统。
在这里插入图片描述
图1 典型的ARM体系结构同ARM处理器系列的对应关系
在32位操作系统中,int类型和long类型一般都是4字节;在64位操作系统中,int类型还是4字节,但是long变成了8字节。在Linux系统中,可用getconf WORD_BIT命令和getconf LONG_BIT命令分别获得word和long的位数。64位操作系统中,应该分别得到数值32和数值64。对于安装Linux操作系统的树莓派,在Linux终端输入getconf LONG_BIT命令(见图2),这里采用的是树莓派3B,可判断其使用了32位Linux操作系统。
在这里插入图片描述
图2 树莓派的Linux操作系统位数显示

二、大端存储和小端存储

对于树莓派3B使用的BCM2837 ARM处理器(Cortex A53×4, 主频1.2GHz)或树莓派使用的BCM2711 ARM处理器(Cortex A72×4,主频1.5GHz)均使用了ARM v8架构, 所引入的ARM v8架构支持包括:64位通用寄存器、SP(堆栈指针)、PC(程序计数器)及64位数据处理和扩展的虚拟寻址。ARM v8架构有两种主要执行状态:1. AArch64—64位执行状态,包括该状态的异常模型、内存模型、程序员模型和指令集支持;2.AArch32—32位执行状态,包括该状态的异常模型、内存模型、程序员模型和指令集支持。AArch32执行状态可看成是一种工作在32位的ARM处理器(32位操作系统支持此执行状态),存储器的地址空间可被看成是从0地址单元开始的字节的线性组合,即一个地址对应于一个存储字节。通常字节地址是无符号整数,则字节地址范围是0~2^32
(16进制地址范围:0x00000000~0xffffffff,存储容量:2^32=4GB字节)。
ARM体系结构允许使用现有的各种存储器和I/O器件进行存储器系统设计。若无特别说明,本讲座ARM处理器默认是仅针对AArch32执行状态或者是32位ARM处理器。
若将地址空间看作由2^30个32位的字组成。
每个字的地址按字对齐,则地址可被4整除。也就是说,若第1个字在第0个地址对应的单元(32位),那么第2个字则在第4个地址对应的单元,第3个字在第8个地址对应的单元,依此类推。字对齐地址是X(X能被4整除)的字由地址为X、X+1、X+2、X+3的4字节组成。当然,地址空间也可看作由2^31个16位的半字组成。每个半字的地址是半字对齐的(可被2整除)。半字对齐是X(X能被2整除)的半字由地址为X和X+1的2字节组成。
由于32位ARM采用32位程序计数器PC,而地址通常是无符号整数的形式,因此在计算目的地址时会产生在地址空间中上溢或下溢的情况。若计算目的地址时产生地址上溢或下溢,则PC寄存器中的值应该从0x00000000开始。
程序计数器PC总是指向取指的指令,而不是指向正在执行的指令或正在译码的指令。一般情况下,人们总是习惯于把正在执行的指令作为参考点,称为当前第1条指令,当程序是顺序执行时,PC总是指向第3条指令。因此,在程序顺序执行的情况下,对于ARM指令,PC寄存器中的值=当前执行的指令地址+8;对于Thumb指令,PC寄存器中的值=当前执行的指令地址+4。
若程序执行中遇到分支,大多数分支指令是通过把指令中指定的偏移量加到PC寄存器中的值上来计算目的地址,然后将结果写回到PC寄存器。此时,PC寄存器中的值就不再是顺序的,从而实现了程序分支。这时,目的地址计算公式如下:
目的地址=当前执行的指令地址+8+偏移量
若计算结果在地址空间中上溢或下溢,则程序分支将不可控制。因此,向前转移时,目的地址不能超出0xffffffff;向后转移时,目的地址不应超出0x00000000。
ARM体系结构可以有两种格式存储字数据,分别称为大端格式(big-endian)和小端格式(low-endian),见图3(a)和(b)所示。
在这里插入图片描述
图3 大端和小端存储格式
在大端存储格式中,字的地址对应的是该字中最高有效字节所对应的地址;半字的地址对应的是该半字中最高有效字节所对应的地址。通俗地说,在大端存储格式中,32位字数据的最高字节存储在低字节地址中,而其最低字节则存储在高字节地址中。
在小端存储格式中,字的地址对应的是该字中最低有效字节所对应的地址;半字的地址对应的是该半字中最低有效字节所对应的地址。通俗地说,在小端存储格式中,32位字数据的最高字节存储在高字节地址中,而其最低字节则存储在低字节地址中。 小端存储格式是ARM默认的格式。
ARM体系结构对于存储器单元的访问需要适当地对齐,即访问字存储单元时,字地址应该是字对齐(地址能被4整除);访问半字存储单元时,半字地址应该半字对齐(地址能被2整除)。如果不按对齐的方式访问存储单元,称作非对齐的存储器访问。非对齐的存储器访问可能会导致不可预知的状态。

三、ARM处理器内部寄存器

ARM处理器内部共有37个32位寄存器,可分成通用寄存器和状态寄存器两大类。其中,通用寄存器用于保存数据或地址;状态寄存器用来标识或设置存储器的工作模式或工作状态等功能。ARM9处理器的37个寄存器中,31个用作通用寄存器,6个用作状态寄存器,每个状态寄存器只使用了其中的12位。这37个寄存器根据处理器的工作状态及工作模式的不同而被分成不同的组(见图4)。程序代码运行时涉及的工作寄存器组是由ARM处理器的工作模式确定的。

在这里插入图片描述
图4 ARM状态下寄存器的组织
1.通用寄存器
通用寄存器用于保存数据或地址,用字母R前缀加该寄存器的序号来标识。通用寄存器包括R0~R15寄存器,可分成未分组寄存器、分组寄存器及程序计数器三种。
(1)未分组寄存器R0~R7
未分组寄存器包括R0~R7,在所有工作模式下,它们在物理上是同一个寄存器。也就是说,不管在哪种工作模式下,若访问R0寄存器,访问到的是同一个32位的物理寄存器R0;若访问R1寄存器,访问到的是同一个32位的物理寄存器R1;依次类推。由于不同的处理器工作模式均使用相同的未分组寄存器,可能会造成寄存器中数据的破坏。
(2)分组寄存器R8~R14
分组寄存器包括R8~R14。对于分组寄存器,它们每一次所访问的物理寄存器与处理器当前的工作模式有关,如图2.5所示。对于R8~R12寄存器,每个寄存器对应两个不同的物理寄存器。当使用fiq(快速中断)模式时,访问寄存器R8_fiq~R12_fiq;当使用fiq模式以外的其他模式时,访问寄存器R8_usr~R12_usr。
对于R13、R14寄存器而言,每个寄存器对应6个不同的物理寄存器,其中的1个是用户模式与系统模式共用;另外5个物理寄存器对应于其他5种不同的工作模式,采用R13_或R14_记号来区分不同的物理寄存器,其中,为usr(用户)、fiq(快速中断)、irq(普通中断)、svc(管理)、abt(中止)、und(未定义) 六种模式之一。
R13寄存器在ARM指令中常用作堆栈指针,又称为SP( Stack Pointer),但这只是一种习惯用法,用户也可使用其他寄存器作为堆栈指针。而在Thumb指令集中,某些指令强制性地要求使用R13作为堆栈指针。
R14寄存器可用作子程序链接寄存器(Subroutine Link Register)或链接寄存器LR(Link Register)。当ARM处理器执行带链接的分支指令BL时,R14中保存R15(程序计数器PC)的备份。当发生中断或异常时,对应的分组寄存器R14_fiq、R14_irq、R14_svc、R14_abt、R14_und用于保存R15的返回值。其他情况下,R14用作通用寄存器。即,R14有两种特殊功能:一是每种工作模式下所对应的那个R14可用于保存子程序的返回地址;二是当异常发生时,该异常模式下的那个R14被设置成异常返回地址。
(3)程序计数器R15(PC)
R15寄存器的用途是程序计算器(PC),用于控制程序中指令的执行顺序。在ARM状态下,R15的位[1:0]是0,位[31:2]保存PC的值;在Thumb状态下,位[0]为0,位[31:1]保存PC的值。读R15寄存器的结果是读到的值为该指令地址加8(ARM状态)或加4(Thumb状态)。R15虽然也可用作通用寄存器,但一般不这么使用,因为对R15的使用有一些特殊的限制,当违反了这些限制时,程序的执行结果是未知的。由于ARM指令始终是字对齐的,所以读出R15的结果值的位[1:0]总是0。读R15的主要作用是快速地对临近的指令和数据进行位置无关寻址,包括程序中的位置无关转移。写R15的通常结果是将写到R15中的值作为指令地址,并以此地址发生转移。由于ARM指令要求字对齐,通常希望写到R15中值的位[1:0]=0b00。
2.程序状态寄存器
在这里插入图片描述
图5 程序状态寄存器格式
ARM体系结构包含1个当前程序状态寄存器CPSR(Current Program Status Register)和5个备份的程序状态寄存器SPSR(Saved Program Status Register),CPSR又称为R16。在任何工作模式下,CPSR都是同一个物理寄存器,它保存了程序运行的当前状态,如条件码标志、控制允许和禁止中断、设置处理器的工作模式以及其他状态和控制信息等。每种异常模式都有一个备份的程序状态寄存器SPSR;当异常发生时,SPSR用于保留CPSR的状态。
程序状态寄存器基本格式如图5所示。
(1)条件码标志
CPSR寄存器的高4位是N、Z、C、V(Negative、Zero、Carry、oVerflow),称为条件码标志位。它们的内容可被算术或逻辑运算的结果所改变,并且可以决定某条指令是否被执行。CPSR中的条件码标志可由大多数指令检测以决定指令是否执行。在ARM状态下,绝大多数的指令都是有条件执行的。在Thumb状态下,仅有分支指令是有条件执行的。通常条件码标志可以通过执行比较指令(CMN、CMP、TEQ、TST)、一些算术运算、逻辑运算和传送指令进行修改。条件码标志位的具体含义如表1所示。
在这里插入图片描述
(2)控制位
CPSR寄存器的低8位是I、F、T和M[4:0],称为控制位。当发生异常时,这些位可以被改变;当处理器运行在特权模式时,这些位也可以由程序修改。
 中断禁止位:包括I和F,用来禁止或允许IRQ和FIQ两类中断。当I=1时,表示禁止IRQ中断;I=0时,表示允许IRQ中断。当F=1时,表示禁止FIQ中断;F=0时,表示允许FIQ中断。
 T标志位:T标志位用于标识/设置处理器的工作状态。对于ARM体系结构v4及以上版本的T系列处理器,当T=1时,表示程序运行于Thumb状态;当T=0时,表示程序运行于ARM状态。ARM指令集和Thumb指令集均有切换处理器状态的指令,这些指令通过修改T位的值为1或0来实现两种工作状态之间的切换,但ARM处理器在开始执行代码时,应该处于ARM状态。
 工作模式位:工作模式位(M[4:0])用于标识或设置处理器的工作模式。M4、M3、M2、M1、M0决定了处理器的工作模式,具体含义如表2所示。需强调的是,表2中未列出的模式位的组合是不可用的。
在这里插入图片描述
例1:设在程序运行某时刻, CPSR寄存器的值如图6所示。试说明处理器的条件标志、中断允许情况、工作状态以及工作模式。
在这里插入图片描述
图6 CPSR的值
上图的条件标志用符号可表示为nzCvq, 即C标志位置1,其他标志位为0。因为bit[7-6]为iF,所以IRQ中断被使能,即允许处理器响应IRQ中断,FIQ中断被禁止。因为bit[5]为t,所以处理器工作在ARM状态。因为bit[4-0]为b10011,由表2可判断出系统工作于管理模式(svc)。
(3)保留位
CPSR寄存器中的其余位是保留位,当改变CPSR中的条件码标志位或者控制位时,保留位不需要被改变,在程序中也不要使用保留位来存储数据。保留位主要用于ARM版本的扩展。
除系统模式和用户模式外,其他5种工作模式都有一个对应的专用SPSR寄存器。当异常发生时,SPSR用于保存CPSR的当前值,从异常退出时则可由SPSR来恢复CPSR。由于用户模式和系统模式不属于异常模式,它们没有SPSR;在这两种情况下访问SPSR,结果是未知的。CPSR和SPSR要通过特殊的指令进行访问。

四、使用GNU GDB命令调试ARM汇编程序及观察树莓派ARM寄存器

在树莓派Linux终端,可采用GNU GDB调试命令调试ARM汇编程序及观察ARM内部寄存器。本讲使用的几个GNU GDB命令功能说明如下:
(1) b(reak):设置中断程序执行的ARM汇编源程序行号(即设置断点)
(2) i(nfo):显示所有的ARM寄存器®或断点(b)
(3) l(ist):显示带行号的ARM汇编源程序
(4) run:运行程序并在下一个断点处暂停
(5) set:设置一个新的数值并加载到寄存器
还记得在第1讲中的两个数做加法运算的树莓派ARM汇编程序举例吧?当两个操作数相加超过255时,在树莓派Linux终端用echo $?显示命令只能显示出ARM寄存器R0的最低字节,即运算结果超出数字0xFF(255)时,显示结果会不正确。下面在树莓派Linux终端,用GNU GDB命令观察ARM内部寄存器,并使用GDB命令修改树莓派中的ARM内部寄存器。
例2:在树莓派Linux终端用GNU nano编辑器编辑一个ARM汇编程序并汇编、链接生成一个可执行程序,用GNU GDB调试命令对其进行调试,观察树莓篇ARM内部寄存器的变化。
本例add_exp.s ARM汇编源程序清单,以及对汇编、链接命令序列见图7所示。
在这里插入图片描述
图7 树莓派ARM汇编程序汇编、链接命令序列
要说明的是,本例as汇编命令对ARM汇编源程序进行汇编时增加了g选项,它表示汇编链接生成的add_exp文件是带调试信息的可执行程序,以便能使用GNU GDB调试命令对应用程序进行调试。
下面展示使用GDB GDB调试命令调试add_exp应用程序以及显示和修改ARM内部寄存器的过程。
首先,执行GDB add_exp命令启动应用程序调试后,显示GNU GDB版本号等信息,在(gdb)命令状态,输入list命令显示带行号的ARM汇编源程序(见图8)。
在这里插入图片描述
图8 执行GDB add_exp命令启动应用程序调试
然后,用B 7命令设置断点为第7行的mov R7,#1指令;用run命令执行程序到第7行的指令处停止,显示断点所在的行及指令;用i r命令显示ARM内部各寄存器及其内容(见图9)。
在这里插入图片描述
图9 设置断点、运行程序到断点处、显示ARM内部各寄存器
从图9可以看出,执行add R0,R1指令后,显示R0中的结果为0x101(对应十进制为257)。
在这里插入图片描述
图10 修改寄存器并查看寄存器中的内容
在GDB调试状态还可使用 “set $ri=新值”命令修改寄存器中的内容。这里使用命令set $r0=5和set $r1=10分别对寄存器r0和r1中的内容进行修改,再次用i r命令查看ARM内部各寄存器,发现r0和r1寄存器中的内容得到更新(见图10)。
End of This Lecture.
(作者Email联系: yuanzywhu@163.com)
发布时间:2020年3月14日
上一讲链接
下一讲链接

  • 10
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

袁易学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值