(正确)ARM的流水线与PC值的关系-关于ARM的PC指针(什么时候PC+8,PC+4,PC-4,PC-8)

要理解PC指针,首先就要好好了解LR指针

 

连接寄存器LR(r14):用来保存和恢复PC寄存器的内容,它有两个特殊功能。

    (1)保存子程序返回地址。使用BL或BLX时,跳转指令自动把返回地址放入r14中;子程序通过把r14复制到PC来实现返回,通常用下列指令之一:
                        MOV PC, LR 
                        BX LR

             通常子程序这样写,保证了子程序中还可以调用子程序。
                         stmfd sp!, {lr}
                         ……
                         ldmfd sp!, {pc}

    (2)当异常发生时,异常模式的r14用来保存异常返回地址,将r14如栈可以处理嵌套中断。

 

程序计数器r15(PC):PC是有读写限制的。当没有超过读取限制的时候,读取的值是指令的地址加上8个字节,由于ARM指令总是以字对齐的,故bit[1:0]总是00。当用str或stm存储PC的时候,偏移量有可能是8或12等其它值。在V3及以下版本中,写入bit[1:0]的值将被忽略,而在V4及以上版本写入r15的bit[1:0]必须为00,否则后果不可预测。

 

知道PC寄存器和LR寄存器功能以后,再了解一下ARM处理器的三级流水线和多级流水线

 

首先,对于ARM7对应的流水线的执行情况,如下面这个图所示:

 

 

 

从图中可以看出,一条汇编指令的运行有三个步骤,取指、译码、执行,当第一条汇编指令取指完成后,紧接着就是第二条指令的取指,然后第三条...如此嵌套

 

其实很容易看出,第一条指令:

add r0, r1,$5

取指完成后,PC就指向了第二条指令,此时PC=PC+4

当第一条指令译码完成以后,此时PC=PC+8

所以第一条指令开始执行时,PC值已经加了8

所以必须记住这个前提,在arm中,每次该指令执行时,其实这时的PC值是PC=PC+8

而且这个前提也同样适合多级流水线,原因就不解释了

 

 

 

接下来谈谈我们在arm汇编时,什么时候需要PC-4, PC-8, PC什么都不减

这个取决于是在正常程序的跳转还是发生异常:

如果是使用BL执行了正常程序的跳转,那么执行这条BL指令时,由于是正常的跳转指令,所以cpu会将返回地址存放在LR中,即当前指令地址加4,当从子程序跳转回来的时候,那么就需要将保存在LR寄存器中的值恢复给PC寄存器,

mov PC, LR     这样的指令返回

 

 

IRQ异常发生时,因为这个异常是在指令执行时候发生的,PC的值等于当前执行指令加8,然后将这个值保存在LR中。但是LR寄存器中保存的是PC+8,指向的是后面的第二条指令,如果不进行减4处理,将会漏执行一条指令,所以PC恢复的时候就需要LR减4,所以正常从子程序返回的时候会使用如:

SUBS PC, LR,#4     返回到当前指令的下一条指令

未定义指令异常时,因为这个异常发生在指令译码阶段,所以,此时PC的值就是未定义指令加4,然后保存到LR(参考流水线图);因为该指令未定义,所以返回时就不应该返回到这条未定义指令,而是返回到它的下一条指令,R14中保存的刚好就是下一条指令的地址,所以就不用计算了,直接将R14赋值给PC就行了

预取指令异常是在流水线的执行阶段时才进入异常,所以PC的值是当前执行指令地址加8,所以返回时应该返回到下一条指令,所以PC恢复的时候就需要R14减4

数据中止异常,这个异常是在本指令执行完成后才发生的,表示当前存储器的访问不能完成,从流水线图可以看出,当第一条指令执行完成时,当前PC值已经指向了第一条指令地址加12的地址,LR中保存的其实是第四条指令的地址了,所以从异常返回时,需要从第一条指令的下一条指令(第二条指令)开始执行,所以PC恢复的时候就需要R14减8

 

 

下面简单总结一下:

 

1.SWI和和未定义指令异常中断的返回:
指令地址
A      PC-8    当前指令为SWI或未定义指令 此时发生中断.PC的值还没有更新.(不需要执行完此指 令,就跳到中断了)
A+4    PC-4  中断时处理器将PC-4保存到LR
A+8    PC

返回时,从发生中断的指令A(PC-8)的下一条指令A+4(PC-4)处开始执行,所以直接
把LR的值赋给PC就行了,具体指令为MOV PC,LR  @(PC=A+4=LR)

2,IRQ和FIQ异常中断处理的返回:
指令地址  对应于PC
A        PC-8     执行此指令完成后(!)查询IRQ及FIQ,如果有中断请求
                          则产生中断. .(需要执行完此指 令,再跳到中断)

A+4      PC-4
A+8      PC

(此时PC的值已经更新,指向A+12.将当前PC-4(即A+8)
保存到LR.返回时,要接着执行A+4(LR-4)处的指令,所以返回指令为
SUBS PC, LR,#4 @(PC=A+4=LR-4)

3,指令预取中止异常中断处理的返回:
指令地址
A    PC-8          执行本指令时发生中断,  
A+4  PC-4      处理器将A+4(PC-4)保存到LR. 
A+8   PC

返回时,发生指令预取中止的指令A(PC-8)处重新执行(A处的指令要重新执行),所以返回指令为
SUBS PC, LR,#4  @(PC=A=LR-4)

4,数据访问中止异常中断处理的返回:

指令地址
A         PC-8  本指令访问有问题的数据,产生中断时,PC的值已经更新   
A+4       PC-4  中断发生时PC=A+12,处理器将A+8(PC-4)保存到LR.
A+8       PC

返回时,要返回到A处继续执行,所以指令为SUBS PC,  LR,#8   @(PC=A=LR-8)

(A处的指令要重新执行),

5.正常程序跳转处理返

使用BL跳转时,它会自动将返回地址装入LR中,即将当前PC-4存入LR中,返回时不用对LR进行加减操作

指令地址 

对应于PC
A          PC-8    (A处的指令不要重新执行)
A+4      PC-4
A+8      PC


mov PC, LR @(PC=A+4=LR)  现在LR的值就是A+4的地址

 

-----------------------------------------------------------------------------------------------------------

根据《ARM微控制器基础与实践》第2页对指令流水线的说明ARM指令集中:
当前"取指PC"=当前PC
当前"译码PC"=当前PC-4
当前"执行PC"=当前PC-8

很容易理解ARM指令集中:
例程(1)
    BL  delay    ;LR=当前PC-4(即当前指令"执行PC"+8-4)
    mov r0,#0    ;
delay   mov PC,LR    ;子函数返回;即返回到调用时"当前PC"-4位置(即当前指令("执行PC"+8)-4);即返回到函数调用指令的下一条指令(mov r0,#0;)处

例程(2)
    mov r0,#1    ;当EINT1在此处发生中断,LR=当前PC(即当前指令"执行PC"+8)
    mov r0,#0    ;
EINT1_Handler  
    subs PC,LR,#4    ;LR=LR-4中断返回;即返回到中断时"当前PC"-4位置(即当前指令("执行PC"+8)-4);即返回到子函数调用指令的下一条指令(mov r0,#0;)处

但如何理解P393页软中断的汇编接口:
例程(3)
    TST    R3,#T_bit        ;判断是ARM指令集还是Thumb指令集
    LDRNEH    R0,[LR,#-2]        ;是Thumb指令集,取SWI中断时执行的指令(即C语言__SWI(0x00)指令)
    BICNE    R0,R0,#0XFF00        ;取软中断功能号
    LDREQ    R0,[LR,#-4]        ;是ARM指令集,取SWI中断时执行的指令(即C语言__SWI(0x00)指令)
    BIC    R0,R0,#0XFF000000    ;取软中断功能号
问题1:LDREQ    R0,[LR,#-4];取的是执行C语言__SWI(0x00)指令时"当前PC-4";而它指向的应该是SWI中断返回的位置,而不是SWI指令本身?
问题2:BIC    R0,R0,#0XFF000000    ;取软中断功能号;该指令的执行一定和生成ARM代码规则相关,但书中并无相关介绍 答 1:
re:当前"取指PC"=当前PC
当前"译码PC"=当前PC-4
当前"执行PC"=当前PC-8
=========
在三级流水线下上面的描述及其容易理解,流水线增多后似乎不太合适讲,但在ARM架构下记住这点就行了,也是最重要的:当前"执行PC"=当前PC-8。

问题1:LDREQ    R0,[LR,#-4];取的是执行C语言__SWI(0x00)指令时"当前PC-4";而它指向的应该是SWI中断返回的位置,而不是SWI指令本身?

:执行C语言__SWI(0x00)指令时,指令地址=当前PC-8
:保存LR时会有个自动调整:LR=PC-4(从你的举例1来看应该以及知道这一点了),所以LR=当前PC-4=SWI指令地址+8-4=当前PC-8__SWI(0x00)指令地址+4
:然后再取LR-4=当前PC-8__SWI(0x00)指令地址+4-4=SWI指令

问题2:BIC    R0,R0,#0XFF000000    ;取软中断功能号;该指令的执行一定和生成ARM代码规则相关,但书中并无相关介绍

:去看SWI指令格式编码,机器码编码中除了开头几位的条件标识和指令码之外,余下的就是软中断号了

 

 

异常中断返回的几种情况
ARM体系结构与编程 清华大学出版社 杜春雷 第九章异常中断处理

我的理解如下:

1.SWI和和未定义指令异常中断的返回:
指令地址
A       PC-8  当前指令为SWI或未定义指令 此时发生中断.PC的值还没有更新.
A+4     PC-4  中断时处理器将PC-4保存到LR
A+8     PC

返回时,从发生中断的指令A(PC-8)的下一条指令A+4(PC-4)处开始执行,所以直接
把LR的值赋给PC就行了,具体指令为MOV PC,LR  (PC=A+4=LR)

2,IRQ和FIQ异常中断处理的返回:
指令地址  对应于PC
A         PC-8      执行此指令完成后(!)查询IRQ及FIQ,如果有中断请求
                    则产生中断.
A+4       PC-4
A+8       PC
                    (此时PC的值已经更新,指向A+12.将当前PC-4(即A+8)
保存到LR.返回时,要接着执行A+4(LR-4)处的指令,所以返回指令为
SUBS PC, LR,#4(PC=A+4=LR-4)

3,指令预取中止异常中断处理的返回:
指令地址
A     PC-8       执行本指令时发生中断, 
A+4   PC-4       处理器将A+4(PC-4)保存到LR.
A+8   PC

返回时,发生指令预取中止的指令A(PC-8)处重新执行,所以返回指令为
SUBS PC, LR,#4(PC=A=LR-4)

4,数据访问中止异常中断处理的返回:

指令地址
A          PC-8   本指令访问有问题的数据,产生中断时,PC的值已经更新  
A+4        PC-4   中断发生时PC=A+12,处理器将A+8(PC-4)保存到LR.
A+8        PC

返回时,要返回到A处继续执行,所以指令为SUBS PC,  LR,#8.(PC=A=LR-8)
————————————————
版权声明:本文为CSDN博主「咕唧咕唧shuboLK」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/liukun321/java/article/details/5594342

posted on 2020-07-22 14:21  紫枫术河  阅读(2010)  评论(0)    收藏  举报

导航