完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
ARM的发展过程
对于ARM公司来讲,ARM公司只做CPU设计,采用出售IP的方式运营,半导体产商无需自己设计CPU,是生产关系的革命,提高了生产力。下面这张图ARM核的多个系列,我们可以看到ARM从V7核开始,就分为了A,R和M三个系列,分别对应高端的多媒体计算,中间的实时性系统以及低端的微控制器,而为控制器对应着我们学习的这个领域,Cortex-M系列的来讲,它保持了高度的兼容性。 32位 ARM Cortex M位单片机寄存器组 在数据和地址的通用寄存器角度,ARM Cortex M 系列的单片机都基本上有如下所示的寄存器组,从R0-R15一共16个寄存器构成了单片机的内部寄存器组 从上图可以看到从R0-R12是单片机的通用寄存器组,,R13,R14和R15是特殊功能的寄存器。
对于通用计算机CPU模型的时候,会存在一个状态寄存器,来保存CPU在运算时的得0,得1,溢出的这些状态。另外,对于CPU来讲,我们还需要有一些设置的功能,对于通常的8位或者16位CPU来讲,通常都会把设置的这个功能合在一个寄存器里,包括开关中断,包括运算状态,而在ARM的体系结构里,ARM把刚刚所述的归总在下图所示的5个寄存器中,图中最上面的xPSR是用来保存CPU设置状态的,第二个PRIMASK是用来设置中断的开和关的,它是一个中断开关,最下面的CONTROL寄存器,它只有1-2个bit可以使用,是用来设置CPU的实际工作模式和状态的。第三个寄存器FAULTMASK的功能是用来配置不可屏蔽中断的开和关,BASEPRI寄存器具备按优先级关闭中断的功能。 上图所示的寄存器在ARM Cortex M系列是一致兼容的,主要的区别在于对ARM Cortex M3和 ARM Cortex M3的单片机来讲,FAULTMASK和BASEPRI两个寄存器是可以使用的,但是对于M0的核是没有的。 xPSR寄存器 xPSR是一个32位的寄存器,每一位都有特定的功能,便于程序员对其进行访问,并获得CPU此刻的状态。为了编程的方便,这个寄存器有三个别名,分别是APSR,IPSR,EPSR.我们用三个别名来加以访问的时候,只关注和读取其中特定的字段来实现单一的功能。 APSR(应用程序状态寄存器) 当我们使用APSR来访问这个寄存器的时候,读到的主要是高四位的值,后面如图所示是Reserve的。那么这高四位的值入如所示,存的分别是: N:是否有得0 Z:是否有负数 C:是否有借位进位 Q:是否有溢出 这些位随着指令的运行会被CPU自动的更新,而又可能被后续的指令拿来作为程序判断和跳转的依据。 IPSR(中断程序状态寄存器) 从图中可以看到他的高位是Reserve的,我们主要读到的是后面的这几位,这几位保留的是当发生中断的时候,发生异常的时候,这个中断的中断号。 EPSR(可执行程序状态寄存器) 这一位能读的只是T这一位,来记录单片机是否发生了异常或者是否发生了中断。 PRIMASK寄存器 由图中可以看到,这是一个只有一位可读的寄存器,当他置1时,就关闭掉了所有的可屏蔽的异常,换句话说,他能够控制所有中断的使能或者关闭,是中断的总开关。 CONTROL 寄存器 这个寄存器有两个比特可以使用,分别是nPRIV和SPSEL,对于nPRIV来说,它决定了CPU是工作在用户态还是特权态,在M0和M0+这两种核上,CPU是不支持这两种状态的。对于SPSEL来说,它的功能是规定了单片机所使用的堆栈是主堆栈还是进程堆栈。对于主对栈指针和进程堆栈指针来说,当我们不使用操作系统的时候,它只有一个,当我们使用操作系统的时候,堆栈指针就出现主堆栈指针和进程堆栈指针,他们分别指向内存的两个不同的区域,因此这两个区域一个给我们的操作系统的内核来使用,一个区域给用户程序来使用,也就是任务来使用,这样当任务跑飞的时候,对堆栈使用出现问题的时候,不会使得整个操作系统崩溃。 CPU的工作状态 上图表示了CPU的工作状态,对于M0 和M0+的CPU来讲,它的工作模式只有图中左边的那一列。 线程模式 它工作在跑普通的程序,我们称是程序的线程模式,也就是说,当单片机在跑我们所写的main函数或者是main函数所调用的子函数的时候,我们认为是工作在线程模式之下的。 handler模式 handler它对应的是当它发生中断的时候,去响应中断的那个状态 特权级和用户级 当使用的是M3或者M4的CPU的时候,为了考虑实时操作系统的使用,考虑到实时操作系统内核和用户程序的区分,因此出现了用户态和特权态。用户态就是操作系统的事件运行所在的状态,而特权态就是运行操作系统内核时单片机所处的状态,而这种特权态到用户态,用户态到特权态的这种跳转,需要一次特殊的中断调用才能得以实现。 不同内核之间的指令集的差异 从图中我们可以看到最中间的绿色的指令集是ARM Cortex M0和M1的指令集,蓝色的区域是ARM Cortex M3的指令,紫色的是ARM Cortex M4的指令集,粉色的是ARM Cortex M4F的指令集,我们可以看到这些指令集是向下兼容的。 ARM架构单片机运行详细例子 例子中涉及到的汇编指令:
第一步运行 蓝色箭头指向的是当前已经执行完了的指令,经过这条指令,NOP,什么也没做,但是相应的PC指针要发生一定的变化,PC指针代表的是指令中即将运行的下一条指令的地址。因此涉及到的变量变化为: SP = 0x20002ff8 PC = 0x00000804 r4 = xx r5 = xx LR = xx 内存空间的变化如下图所示: 第二步运行 这条指令的意思是将18这个十进制数放入r4寄存器中,执行这条指令之后,所涉及的相关变量有了如下变化: SP = 0x2000 2ff8 PC = 0x0000 0806 r4 = 0x0000 0012(12是十进制18的十六进制转换) r5 = xx LR = xx 第三步运行 这条指令的意思是将十进制52复制给r5这个寄存器,指令执行完毕后设计的相关变量变化如下: SP = 0x2000 2ff8 PC = 0x0000 0808 r4 = 0x0000 0012 r5 = 0x0000 0034(十进制的52) 对应的内存空间没有发生改变。 第四步运行 这条语句的意思是将r4寄存器的值进行压栈,执行这个操作后涉及的变量发生了如下的变化: SP = 0x2000 2ff4 PC = 0x0000 080a r4 = 0x0000 0012 r5 = 0x0000 0034 LR = xx 我们可以看到SP指针的值由于堆栈有新值得压入而减小了4个字节的大小,对应的内存空间发生了如下的变化: 第五步运行 将r5寄存器的值压入堆栈,相应涉及到的变量发生了如下的变化: SP = 0x2000 2ff0 PC = 0x0000 080c r4 = 0x0000 0012 r5 = 0x0000 0034 LR = xx 从上述变化的值我们可以看到因为r5寄存器值压入堆栈,从而导致了堆栈寄存器的值又减少了4,对应的内存空间变化如下: 第六步运行 第六条指令的意思是跳转到子函数的执行,执行这条指令后,涉及到的变量变化为: SP = 0x2000 2ff0 PC = 0x0000 0814 r4 = 0x0000 0012 r5 = 0x0000 0811 LR = 0x0000 0811 上述变量的变化,我们可以看到PC指针指向了子函数的第一条指令的地址,相对应的堆栈指针的值没有发生变化,这是因为在ARM32位处理器架构上,当函数发生一级调用的时候,函数的返回地址并不进行压栈,而是将返回地址保存在LR寄存器上,也就是R14寄存器上。因此LR的值发生改变,但是改变的值却是0x0000 0811,但是对于ARM32位的处理器来说,它的地址全是偶数,这是因为ARM 指令的一个向下兼容性,当这个保存的返回地址的最低位是1的时候,函数跳转和任何函数返回的时候,CPU仍然工作在Thumb指令集状态下,而如果变成了0就告诉CPU在返回的时候应该切换到ARM指令集的模式,因为现在使用的是ARM Cortex M的系列,所以函数返回的时候,自动的把最低位置为1,而在返回的时候,这一位被忽略,只取它偶数的部分作为我们的返回地址,存的信息是0811,实际的信息是返回Thumb模式,回到0x0000 0810 这个地址。 第七步运行 这条指令的作用是什么也没干,涉及到的相关变量发生了如下的变化: SP = 0x2000 2ff0 PC = 0x0000 0816 r4 = 0x0000 0012 r5 = 0x0000 0034 LR = 0x0000 0811 可以看到上述寄存器中,发生变化的寄存器是PC指针寄存器,寄存器的值变为了子函数的下一条指令。 对应的内存空间没有发生变化。 第八步运行 这条指令的意思是从子函数中返回,并把LR寄存器中的值赋值给PC指针,因此涉及到的变量发生了如下的变化: SP = 0x2000 2ff0 PC = 0x0000 0810 r4 = 0x0000 0012 r5 = 0x0000 0034 LR = 0x0000 0811 可以看到此时PC指针的值就是之前放入LR寄存器中的值(去除末尾的1). 对应的内存空间没有发生变化。 第九步运行 上述的指令的意思是从堆栈中弹出一个数并赋值给r4寄存器,经过这条指令后,涉及到的相关变量发生了如下的变化: SP = 0x2000 2ff4 PC = 0x0000 0812 r4 = 0x0000 0034 r5 = 0x0000 0034 LR = 0x0000 0811 首先因为堆栈中的数据被弹出,因此堆栈指针要加4,指令运行结束,PC指针的值也要进行更新,而刚才的指令是把堆栈中的值弹出来赋值给r4寄存器,因此r4寄存器的值也进行了更新。 相应的内存空间发生了如下的变化: 第十步运行 在这一步指令的作用是堆栈中弹出一个值然后赋值给r5寄存器,所涉及到的变量变化分别为: SP = 0x2000 2ff0 PC = 0x0000 0814 r4 = 0x0000 0034 r5 = 0x0000 0012 LR = 0x0000 0811 由此可知,当执行最后一条指令时,堆栈中的数据被弹出赋值给r5,PC指针寄存器的值发生变化,SP指针的值加4,对应的内存空间如下图所示: 总结 回顾上面的十个过程,我们会发现,指令运行结束之后,r4和r5寄存器的值发生了调换,发生调换的原因是因为我们使用了堆栈这样的一种机制,堆栈的特性是先入后出的,因此最开始把r4和r5寄存器的值依次压入堆栈,取的时候的顺序和压入堆栈的顺序却是刚好相反的,因此也就造成了最终的r4和r5寄存器的值发生了调换。 |
|
|
|
只有小组成员才能发言,加入小组>>
2514 浏览 0 评论
1092浏览 2评论
703浏览 1评论
456浏览 0评论
200浏览 0评论
341浏览 0评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-24 17:11 , Processed in 1.198609 second(s), Total 80, Slave 62 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号