当前课程知识点:智能车制作:嵌入式系统 > 第三章 MCU基础 > 3.3.2 ARM的体系结构-头脑体操 > Video
各位同学大家好
我是清华大学工程物理系的曾鸣老师
欢迎大家回到我们
ARM微控制器与嵌入式系统的慕课课堂
我们来继续进行第三章
特别是这个单元
关于ARM的微处理器的
微控制器的CPU内核的学习
在上个单元里头
我们已经看了ARM Cortex M系列
从M0到M3 M4的
片内的编程模型寄存器组
特别是草草的看一下
它的指令集的相互之间子集的关系
我相信绝大多数同学看完了的感觉呢
是有点晕
所以这个单元没关系
我们来做一个头脑体操
就跟前面几个单元
对16位的MCU做了个头脑体操一样
我们做完这个头脑体操
看看真正的汇编程序
在ARM Cortex M的CPU上怎么运行
相信大家对它的理解就会上一个层次
我们同样来看这样一段程序
在这样一段程序里头
左边是一条条的指令
每条指令放在它对应的地址上
跟我们上一次做头脑体操是一样的
那么右边呢我们最上面有一段内存空间
这段内存空间每一个地址里头可以存一个
32位的数对吧
那么所以我们的地址大家注意
是以4来累加的
分别是8 C 0这样的数往下累加
然后右下角不太起眼的地方
是我们刚才所提到的
我们的编程模型寄存器组
我们会有一个堆栈指针寄存器
我们会有一个PC指针寄存器
然后我们还会有另外三个我们没有用到的
但是可以使用的通用的数据寄存器
比如说R4 R5和R6
那么这段程序在运行之前
我们可以发现蓝色的箭头
总是指向我们上一条执行完了的指令
红色的箭头呢
总是指向我们当前堆栈
已经使用到的使用过的内存地址
那么对于ARM这样一种体结构
堆栈的使用
跟我们之前举起的例子很像
就是堆栈总是指向这个内存
已经使用的地址
也就是说它的下一个地址
是我们进行压栈的时候可以使用的
空白的内存空间
那么我们在这样一段汇编程序里
大家会非常敏锐地发现
我们同样使用了
汇编语言的助记符
这些助记符
也就是我们的汇编指令
汇编程序每一条
都跟一组0和1对应的ARM的指令相呼应
所以大家如果想
深入理解这些指令其实不多
就五十多条
可以去看一看ARM专门的
这个CPU的手册
而对于这些指令
它的含义我们可以非常简单得用几句话
都给大家给出来
而跟着我执行的过程当中
大家可以非常容易理解
它都很简单
我们来一条一条的对这些程序加以运行
我们进行一个人脑计算机来头脑体操
看看一个ARM Cortex M0的CPU上
这么一段程序
它的运行过程是什么样
来把我们刚才学的知识用上
那么我们刚刚第一条指令
也就是在当前这个状态下
刚刚执行完了的一条指令
是在02这个地址上
是nop指令 nop指令在很多MCU里都有
我们之前16位也讲过
是no operation
什么都不做
也就是我们上一条指令
什么都没有做
那么上一条指令
执行完了的结果
我们这些寄存器当中
最重要的一件事情是PC指针寄存器
那么这个PC指针寄存器的值呢
因为是在上一条指令执行过程当中
我们把它给标红了的
那么他现在的地址
指向了多少呢我们仔细看一看
发现它指向了0000804这个地址
这个地址我们到左边的程序里去看
会发现他就是我们接下来要执行的
下一条指令的地址
这是PC指针的功能
我们还记得吧
它总是指向下一条地址
每次运行完指令它会自动的累加
使我们的程序 一条条指令往下走
那下一条指令是是movs r4#18
那么这条指令的含义非常简单是个MOV指令
把后面18这样一个数 #表示十进制
十进制的18给R4这个寄存器存进去
就这么简单
那么来把它运行一下看一下
这条指令运行我们发现整个CPU
哪些东西发生了变化呢
首先我们蓝色的箭头往下移了一位
这是我们自己挪的
就表示这条指令已经执行完了
那么在CPU内部我们看到的是什么呢
首先PC指针寄存器的值进行了更新
它现在指向的是806
也就是下一条指令
那么我们的R4也随之在刚才那条执行的
结果上被赋上了一个新值
赋的值是多少呢
是0x0000 0012
为什么是12啊
因为18是个十进制数赋给了寄存器
十六进制就是十六进制的12对吧
那么我们来看下一条要执行的指令
对应的指令是什么呢
是上面左边这个第三行movs r5 #52
立即数 十进制的52
也就是把52这个数
赋给R5寄存器
好那我们没有执行
同学已经很快能想到这条指令执行效果是什么了
对吧 它非常清楚就是个赋值
我们来执行一下
大家会发现红色区域
就是我又标识发生了变化的寄存器
那么在这条指令执行完了以后
我们的PC指针寄存器再一次的向下移了两位
也就指向了8008 这个地址
也就是下一条要运行的指令
同时发生变化的另外一个CPU内部的寄存器呢
就是我们的R5寄存器
它现在被赋上了一个新的值
对应的是十进制的52
那么它在这里写的是十六进制
也就是我们这个赋值成功的完成了
那么我们再看看下一条指令
也就是现在PC指针寄存器所指向的
8008这个地址
这条指令是什么
是push {r4}
这条指令的意思我们都有个印象
堆栈只有两种操作
一个是push压栈
把一个数据放入堆栈里
一个呢是pull
或者叫pop把堆栈里的一个数给拿出来
大家还记得吗
小猴子拿饼干对吧
所以呢
这条指令是把R4寄存器的值放入堆栈
那我提前问一句堆栈在哪里
堆栈在内存里对不对
那堆栈怎么找
堆栈根据SP堆栈指针寄存器的值
它所存的那个地址
指向的内存空间是我们要用的堆栈
所以我们现在回过头来看看这个寄存器里
SP这个堆栈指针寄存器存的值
是我们堆栈的底部
也是堆栈现在用到的位置
所以我们要压栈的时候
像这个水桶也好
像小猴子拿饼干也好
我们只能往它上面的一个地址
自动的放入一个数
也就是我们现在内存空间灰色的
可以用这些格子当中
最下面的一个是我们待会要用的
所以我们来执行这条指令
执行完了以后
我们发现若干个值发生了变化
哪些值发生了变化呢
首先我们发现
PC指针寄存器的值再次发生了累加
指向了下一条指令
这个时候堆栈指针
SP寄存器的值发生了变化
它减少了四
为什么呢
因为我们的堆栈看上面的内存区域
被用掉了四个字节一个格子对吧
我们R4寄存器的值
也就是刚才的那个0x0000 0012
这个时候被放入了堆栈用掉了四个字节
堆栈指针寄存器的值水位
顺着往上长了四个字节
这是堆栈的使用对吧
那么我们的下一条指令是什么呢
是push {r5} 把R5寄存器的值也放入堆栈
有了刚才的经验
我们也对堆栈又回忆起来了
那会发生什么事呢
我们想想
是不是 PC指针寄存器的值会累加
指向再下一条指令
堆栈指针寄存器的值会再减少
再往上水位
涨4个字节
堆栈也会再填入一个格子
填入R5的值对不对
我们来看运行的结果
确实就是这样吧
指令又往下走了一步
堆栈里多了一个数
这个数是刚才R5寄存器里头的值
这就是我们整个程序执行的
这样一个结果
所以这个值是十六进制的0x34
那么我们下一条指令
也就是现在PC指针寄存器指向的
再往下一条指令
也就是80c这样指令
它对应的是什么呢
我们发现是bl SubFunc
这是一个跳转指令
B是branch的缩写
那么它是一个子函数
或者叫子程序的调用指令
那么它的含义是
跳转到下面SubFunc这个函数
来进行执行调用子函数了对吧
那么我们前面
做过16位的头脑体操
我们现在想想
我们现在调用这个函数会发生什么事情
我们PC指针寄存器应该跳转到SubFunc
所指向的这个子函数第一条指令的地址
确保CPU接下来执行这个子函数
同时呢我们应该有一个机制
保存这个函数
执行完以后应该返回的地址
是不是这么两件事儿
我们来看CPU怎么处理的
那么这件事情执行完了以后
我们会发现因为我们这是一个ARM的CPU
而且这是一个一级的函数调用
我们会发现在这次调动发生以后
我们的PC指针寄存器
果不其然
被更新到了SubFunc的入口地址
也就是底下的145结尾的这个地址上
那么接下来我们
就该执行这个子函数了
同时我们发现
在这个CPU上在这样一级调用里
我们堆栈暂时没有用
堆栈没有动
返回地址到哪了呢
到了我刚才讲的CPU体结构所讲的
Link Register里头
这个里头保存了一个地址
这个地址稍微有一点点特殊性
为什么呢
因为这个地址大家仔细看
它存的是0x0000 0811
大家仔细看
我们程序里头所有的地址在左边
有一个什么规律啊
这些地址全是偶数
因为我们ARM Cortex M 的指令
都是16比特或者32比特宽的
所以 指令占的存储空间
都是两个 两个 四个 四个在用的
所以地址都是偶数
那么为什么存过来的
这个返回地址变成0811
待会返回的时候应该返回哪呢
这个地方告诉大家
这个就是ARM这个体系结构的一个
向下兼容的特殊性
当这个保存的返回地址的最低位是1的时候
函数调用和任何跳转返回的时候
CPU仍然工作在我们前面所讲过的
Thumb指令集的状态下
而如果这个最低位
变成了0就表示告诉CPU在返回的时候
应该切换到ARM指令集的模式
因为我们现在使用的是Cortex M的
这样一种CPU体系结构
所以所有的这样的
跳转的返回地址自动的把最低位给置1
而在返回的时候
这一位被忽略
只许它偶数的部分做我们的返回地址
所以存的是040811
实际的信息是返回Thumb模式
回到0x0000 0810这个地址
那0810这个地址是哪一个呢
回到左边的程序看一看
就是我们调用子函数的下一条指令对吧
所以这个地址是正确的
那么在这个过程中
没有用到堆栈
好 我们接着再往下执行
我们会发现PC指针指向的
已经是子函数的第一个指令了
它这条指令非常好
又是NOP指令什么都不干
那么想想
这条指令执行下去会发生什么事儿
很显然什么事都不会发生
只有PC指针寄存器
更新到下一条指令流水
这个车轮继续往前滚动
然后我们会发现
果然这条指令执行完了被执行的指令
是这个nop指令
而我们的PC指针寄存器
指向了这个nop下的下一条指令
也就是bx lr这样一条指令
那么这条指令的含义
是根据link register
也就是我们刚才右边
LR寄存器里头的值
进行跳转函数的返回
回到我们调用的主函数里头去
就这么一个功能
那么这条指令我们执行下来
大家看一看 会发现
这条指令一旦执行完
发生什么事了呢
其它的堆栈内存都没有使用
我们刚才的LR寄存器的值0x0811
是不是如我所说
最低位的那个1
被用来给CPU判断应该在什么指令集
我们因为都是Thumb指令集所以它就被使用了
而它的偶数部分被取出来
还给了PC指针寄存器
也就是说
PC指针寄存器还原成了程序调用完函数以后
应该返回的地址
也就是我们刚才函数调用的
bl SubFunc的下一条指令
POP指令这个地方
所以程序是不是从主程序
调用了一个函数
又从函数成功的返回了
主程序接着往下跑啦
这接下来就应该执行
0x0810的这条POP指令
那POP指令的含义非常的简单
从堆栈弹出一个数
大家注意啊
回忆我们最开始堆栈的使用
是忽略地址的
它用堆栈指针
来指向这个水位
所以对它操作非常简单
只是放进去拿出来对吧
现在要拿出来了
POP弹出一个值放回给r4寄存器
那么这个时候弹出的这个值R4寄存器呢
是我们堆栈当前可以使用的最上面这一个
内存单元的值也就是这个0x34
所以这条指令执行完以后
这个值就应该赋给R4数据寄存器
我们来看看效果
果然执行完以后
发生了很多变化对不对
不像我刚才说那么简单
我们仔细看一看会发现哪些东西发生变化
首先堆栈里头水位下降了一位为什么下降了
我这里标的是蓝颜色
对于计算机来讲
是堆栈指针寄存器的地址值
这个右边CPU里头加了4
表示我可以使用的堆栈往下降了一格
释放了四个字节
虽然值还在那
但它已经对我没有用了
下次可以被覆盖掉
而现在有效的堆栈使用的位置已经降下来了
同时呢这个值到哪去了呢
它被写给了R4寄存器
R4寄存器的值发生了变化
被弹出的3 4覆盖了r4寄存器原来的值
然后同时还有个变化的
就是我们的PC指针寄存器顺序加二
又执行到了下一条指令
下一条指令就是我们最后一条指令
是什么是POP R5
把堆栈里头弹出一个值
取出一个值
取出最后一个值
给r5数据寄存器
那么它执行的结果
我们已经非常清楚把这个值拿出来
还给R5寄存器
运行的效果就是这个样子
会发现我们堆栈里头所有刚才压进去的
两个数已经都拿出来了
所以堆栈指针再次回到了初始的位置
也就是这段内存的最底部
而PC指针寄存器也顺序累加执行到了
我们现在看不见的
再下一条指令那去了
而堆栈里最后一个值
这个值0x0000 0012
被取出来覆盖了R5寄存器原来的值
这样就形成了一个堆栈的使用
我们脑袋头脑体操
运行完了这一段汇编程序
那么这段程序汇编运行完了以后
我们想想在这里头
回忆一下
我们会领悟点什么呢
我想我们领悟到这么几件事
第一这个程序的功能是什么
跟我们上一次的头脑体操一样
这个程序的功能
就是把R4 R5寄存器的值换了个位置
对不对
一开始的时候是你是1 2 我是3 4
现在运行完了
变成我的3 4 你是1 2
互换了位置
第二它们为什么会换个位置呢
因为我们在这个过程当中使用了堆栈
堆栈的数据结构能够保持数据的顺序
所以我们是按R4 R5的顺序压入堆栈的
然后呢堆栈要求后入必须先出
所以数据在里头是有顺序的
而我们再次按R4 R5的顺序弹出
是不是把原来R5的给了R4 R4的给了R5
这就是堆栈的理解
然后最后呢
我们还领悟到了什么
这就是刚才
讲了一个单元十分钟好像很复杂的
ARM Cortex M的CPU内部的指令寄存器
编程模型汇编程序的运行流程
我们用人脑也能运行
那你是不就突然觉得小菜一碟
并不是那么困难了呢
如果有这种感觉
那我觉得就对了
那么有兴趣的同学你可以倒过来
把这个头脑体操自己再多做几遍
或者日后自己写一小段C语言或者汇编语言
用单步调试去看一看这些指令的运行
在这门课的范畴里具备汇编语言阅读的能力
我觉得是一个非常非常重要和有意义的能力
希望大家加以锻炼
如果大家觉得
ARM Cortex M的处理器你已经了然于胸
从M0到M4好像都很简单
我多说两句
我们这门课程所学习的是M0+的处理器
它跟M0有什么区别呢
大家日后会慢慢理解
我在这里点一下
一个是它将三节流水减成了两节
所以我们这样一款
学习的微处理器是一个非常简单
而且是目前地球上功耗最低的微处理器
此外呢
它还有若干个可选的功能
加强原来M0处理器的一些基本的能力
比如它有单指令周期的IO访问
比如说它有更加强大的调试能力等等
这样一些的扩充都是我们对于M0处理器
跟M0+这样一种处理器的差异
那么如果大家想更加深入地学习
M0处理器和M0+处理器
它的内部的指令集
包括我刚才所讲到这些体系架构
我给大家推荐一本书是叫Joseph Yiu
这是一个华裔的学者写的
那么这本书有一个中文版
叫《ARM Cortex-M0权威指南》
讲了里头很多详细的架构
那么他有个新版
目前只有英文版是ARM Cortex-M0
和ARM Cortex-M0+的权威指南是英文版的
这两本书任何一个都可以
有兴趣同学可以加以学习
-1.1 课程概览
--Video
-1.2 进入嵌入式系统的世界
--Video
-1.3 如何学好嵌入式系统
--Video
-2.1 计算机的基本概念、发展历史
--Video
-2.2 从晶体管到CPU
--Video
-2.3 概念CPU、微控制器MCU和嵌入式系统
--Video
-2.4 八卦计算机史
--Video
-2.5 不同领域、不同系列的嵌入式系统
--Video
-2.6 ARM历史与MKL25Z128 MCU
--Video
-3.1 CPU的基本结构和运行机制
--Video
-3.2.1 堆栈的概念
--Video
-3.2.2 堆栈的概念-头脑体操
--Video
-3.3.1 ARM的体系结构
--Video
-3.3.2 ARM的体系结构-头脑体操
--Video
-3.4 中断的概念和机制
--Video
-3.5 中断子程的概念和编程
--Video
-3.6 复位、时钟、存储器和总线
--Video
-3.7 小结:MCU的总体结构和程序运行机制
--Video
-4.1 第一种外设:IO
--Video
-4.2 IO外设的编程实操-点亮LED
--Video
-4.3 IO外设的进阶知识
--Video
-4.4 嵌入式开发的基本概念与工具链
--Video
-4.5 嵌入式开发的进阶知识
--Video
-4.6 嵌入式开发中的C语言(上)
--Video
-4.7 嵌入式开发中的C语言(下)
--Video
-E0.1 实验零 开发板的初步认识与工具链的安装
--Video
-E0.2 实验零 体验一个例程的编译与下载
--Video
-E0.3 实验零 编写第一个程序:点亮核心板LED
--Video
-E1 实验一 点灯秘籍
--Video
-5 智能车视角的嵌入式设计
--Video