ARM3級流水和5級流水為什么都是PC=PC+8
arm7采用三級流水
(1)取指(fetch)
取指級的任務是從程序存儲器中讀取指令。
(2)譯碼(decode)
譯碼級完成對指令的分析,并為下一個周期準備數(shù)據(jù)路徑需要的控制信號。在這一級,指令占用譯碼邏輯,不占用數(shù)據(jù)通路。
(3)執(zhí)行(excute)
完成指令要求的操作,并根據(jù)需要將結(jié)果寫回寄存器。指令占用數(shù)據(jù)路徑,寄存器堆被讀取,操作數(shù)在桶行移位器中被移位。運算器產(chǎn)生運算結(jié)果并回寫到目的寄存器中,運算器根據(jù)指令需求和運輸結(jié)果更改狀態(tài)寄存器的條件位。
arm9采用五級流水
(1)取指(fetch)
從存儲器中取出指令,并將其放入指令流水線。
(2)譯碼(decode)
指令被譯碼,從寄存器堆中讀取寄存器操作數(shù)。在寄存器堆中有3個操作數(shù)讀端口,因此大多數(shù)ARM指令能在1個周期內(nèi)讀取其操作數(shù)。
(3)執(zhí)行(execute)
將其中一個操作數(shù)移位,并在ALU中產(chǎn)生結(jié)果。如果指令是Load或Store指令,則在ALU中計算存儲器的地址。
(4)緩沖/數(shù)據(jù)(buffer/data)
如果需要則訪問數(shù)據(jù)存儲器,否則ALU只是簡單地緩沖一個時鐘周期,以便是所有的指令具有同樣的流水線流程。
(5)回寫(write-back)寄存器堆
------------------------------------------------
注意,arm7中執(zhí)行和取指是隔了一級譯碼級,那一級不讀取數(shù)據(jù),當前PC=原PC+8, 正常
arm9中執(zhí)行和取指之間的譯碼級不再老實,譯碼級已經(jīng)開始從寄存器堆中讀取寄存器操作數(shù),這樣的話,
讀取到得就是PC+4,不是PC+8,執(zhí)行級又不會再讀一次,那將導致執(zhí)行級的PC還是=PC+4
為了保持向下兼容,在ARM9的5級流水線上,取指級增加的PC值被直接送到譯碼級的寄存器,穿過了兩級之間的流水線寄存器,這樣譯碼級得到的PC值就是下一條指令的PC+4,等于當前指令的PC+8,
等到了執(zhí)行級時,PC寄存器的值=當前指令地址+8
當使用指令STR或STM對R15進行保存時,保存的可能是當前指令地址加8或當前指令地址加12。
到底是哪種方式,取決于芯片的具體設計方式。當然,在同一個芯片中,只能采用一種方式。要么保存當前指令地址加8,要么保存當前指令地址加12。程序開發(fā)人員應盡量避免使用STR或STM指令來對R15進行操作。當不可避免要使用這種方式時,可以先通過一小段程序來確定所使用的芯片是使用哪種方式實現(xiàn)的。例如:
SUB R1,PC, #4 ;R1中存放STR指令地址
STR PC,[R0] ;用STR指令將PC保存到R0指向的地址單元中,
;PC=STR指令地址+偏移量(偏移量為8或者12)。
LDR R0,[R0] ;讀取STR指令地址+偏移量的值
SUB R0,R0,R1 ; STR指令地址+偏移量的值減去STR指令的地址,
;得到偏移量值(8或者12)。
=============================
ARM7采用三級流水線的馮·諾伊曼結(jié)構(gòu),ARM9采用五級流水線的哈佛結(jié)構(gòu)。
ARM7流水線包括取指(fetch)、譯碼(decode)、執(zhí)行(excute)。ARM7流水線在譯碼階段不讀取操作數(shù)寄存器,因此執(zhí)行階段的PC值和取指階段的PC值關(guān)系為:PC(excute)=PC(fetch)+8。
ARM9流水線包括取指(fetch)、譯碼(decode)、執(zhí)行(excute)、緩沖/數(shù)據(jù)(buffer/data)、回寫(write-back)寄存器堆。ARM9流水線在譯碼階段已經(jīng)開始讀取操作數(shù)寄存器,因此譯碼階段的PC值和取指階段的PC值關(guān)系為:PC(decode)=PC(fetch)+4。因此執(zhí)行階段的PC值和譯碼階段的PC值關(guān)系為:PC(excute)=PC(decode)+4。
為了保證ARM9流水線和ARM7流水線兼容,ARM9流水線將取指階段的PC值跨過取指和譯碼流水線寄存器,直接送往譯碼階段寄存器,這樣仍然保證執(zhí)行階段的PC值和取指階段的PC值關(guān)系為:PC(excute)=PC(fetch)+8。以下面uboot中的start.S的最開始的匯編代碼為例來進行解釋:
00000000 <_start>:
0: ea000014 b 58
4: e59ff014 ldr pc, [pc, #20] ; 20 <_undefined_instruction>
8: e59ff014 ldr pc, [pc, #20] ; 24 <_software_interrupt>
c: e59ff014 ldr pc, [pc, #20] ; 28 <_prefetch_abort>
10: e59ff014 ldr pc, [pc, #20] ; 2c <_data_abort>
14: e59ff014 ldr pc, [pc, #20] ; 30 <_not_used>
18: e59ff014 ldr pc, [pc, #20] ; 34 <_irq>
1c: e59ff014 ldr pc, [pc, #20] ; 38 <_fiq>
00000020 <_undefined_instruction>:
20: 00000120 .word0x00000120
復制代碼
下面對每一個指令周期,CPU做了哪些事情,分別詳細進行闡述:
在看下面具體解釋之前,有一句話要牢記,那就是:
PC不是指向你正在運行的指令,而是
PC始終指向你要取的指令的地址。
認識清楚了這個前提,后面的舉例講解,就容易懂了。
指令周期Cycle1
(1)取指:
PC總是指向?qū)⒁x取的指令的地址(即我們常說的,指向下一條指令的地址),而當前PC=4,
所以去取物理地址為4對對應的指令“ldr pc,[pc,#20]”,其對應二進制代碼為e59ff014。
此處取指完之后,自動更新PC的值,即PC=PC+4(單個指令占4字節(jié),所以加4)=4+4=8
指令周期Cycle2
(1)譯指:翻譯指令e59ff014;
(2)同時再去取指:
PC總是指向?qū)⒁x取的指令的地址(即我們常說的,指向下一條指令的地址),而當前PC=8,
所以去物理地址為8所對應的指令“ldr pc,[pc,#20]”其對應二進制代碼為e59ff014。
此處取指完之后,自動更新PC的值,即PC=PC+4=8+4=12=0xc
指令周期Cycle3
(1)執(zhí)行(指令):執(zhí)行“e59ff014”,即“ldr pc,[pc,#20]”所對表達的含義,即
PC
= PC + 20
= 12 + 20
= 32
= 0x20
此處,只是計算出待會要賦值給PC的值是0x20,這個0x20還只是放在執(zhí)行單元中內(nèi)部的緩沖中。
(2)譯指:翻譯e59ff014。
(3)取指:
此步驟由于是和上面(1)中的執(zhí)行同步做的,所以,未受到影響,繼續(xù)取指,而取指的那一時刻,PC為上一Cycle
更新后的值,即PC=0xc,所以是去取物理地址為0xc所對應的指令”ldr pc,[pc,#20]”,對應二進制為e59ff014。
評論