|
以下作為我個(gè)使用cotex M7內(nèi)核統(tǒng)計(jì)的一些特性:
一 內(nèi)核行為分析
實(shí)例代碼1:
FPU_Test: //double FPU_Test (double a_mul,double b_mul,double c_add);
PUSH{R4-R7}
MOV R0, #1024 //減少循環(huán)次數(shù),避免緩存影響
// 初始化完全獨(dú)立的寄存器組
LDR R1,=8234
MOV R6,R0
MOV R7,R1
PLI [LR]
FPU_Test_Loop_Opt:
// 8組完全獨(dú)立的 VFMA 操作
VFMA.F32 S2, S1, S0 // 組1
SMLAL R3,R2,R1,R0
VFMA.F32 S3, S1, S0 // 組2 - 完全獨(dú)立
SUBS R4,R4, #1
SHADD16 R5,R5,R0
SMLAL R3,R2,R1,R0
VFMA.F32 S4, S1, S0 // 組3 - 完全獨(dú)立
SHADD16 R4,R4,R1
SUBS R5,R5, #1
SMLAL R3,R2,R1,R0
VFMA.F32 S5, S1, S0 // 組4 - 完全獨(dú)立
SHADD16 R5,R5,R0
SUBS R4,R4, #1
SMLAL R3,R2,R1,R0
VFMA.F32 S6, S1, S0 // 組5 - 完全獨(dú)立
SUBS R0,R0, #1
BNE FPU_Test_Loop_Opt
POP{R4-R7}
BX LR
以上為keilv5 MDK V5.23 編譯器語法
1:使用VFMA(浮點(diǎn)區(qū)域指令)與使用SMLAL(整數(shù)區(qū)域指令)能夠讓處理器進(jìn)行雙發(fā)射(在cotex M7權(quán)威手冊里亦有記載)
但是,注意需注意順序即:
VFMA.F32 S2, S1, S0 // 組1
SMLAL R3,R2,R1,R0
VFMA.F32 S4, S1, S0 // 組3 - 完全獨(dú)立
SHADD16 R4,R4,R1
是能夠正常雙發(fā)射
VFMA.F32 S2, S1, S0 // 組1
SMLAL R3,R2,R1,R0
SHADD16 R4,R4,R1
VFMA.F32 S4, S1, S0 // 組3 - 完全獨(dú)立
以上只能單發(fā)射,而不能雙發(fā)射
SHADD16 R4,R4,R1
SUBS R5,R5, #1
SMLAL R3,R2,R1,R0
VFMA.F32 S5, S1, S0 // 組4 - 完全獨(dú)立
以上可以雙發(fā)射
總結(jié)為:在使用浮點(diǎn)+整數(shù)指令時(shí),下一條指令如果需要使用整數(shù)并且需要使用寄存器,那么MCU則不支持雙發(fā)射,如果為浮點(diǎn),即可成功雙發(fā)射.
若持續(xù)為整數(shù)指令,那么在數(shù)據(jù)無依賴的情況下,即可雙發(fā)射,否者只能單發(fā)射,或者阻塞 (均不包含除法指令)
1 針對于除法指令,在mcu上能不用就不用,無符號/有符號整數(shù)除法平均會消耗10個(gè)周期左右,除非你的結(jié)果較小,例如小于256.那么可以在較短的時(shí)間里得出結(jié)果,對于雙精度浮點(diǎn)除法,通常需要14-16個(gè)時(shí)鐘周期,單精度需要8-12個(gè)周期.除法指令是阻塞運(yùn)行的,不支持單周期的吞吐量.除法比較特殊,即使數(shù)據(jù)無依賴也不行
2 在cotex-m7內(nèi)核上,大部分都會有支持雙精度浮點(diǎn),但是,雙精度浮點(diǎn)一般比單精度慢2-8倍,不同指令有著不同的效率,如VADD.F64就最快,2周期的吞吐量,基本與VADD.F32 的單周期差不了太多,對于像VFMA.F32(實(shí)例代碼中的指令)為單周期吞吐量. VFMA.F64不支持單周期吞吐量,執(zhí)行一條需要7個(gè)周期,并且不支持與其他指令雙發(fā)射,包括大部分的.F64的運(yùn)算指令(像VMOV.F64這種執(zhí)行時(shí)間為2周期,與雙精度與單精度無關(guān),執(zhí)行周期按位寬/32bit)都不支持與其他指令雙發(fā)射
二 性能優(yōu)化:
對于需要性能優(yōu)化的場景來說,手動添加(C#) __ASM volatile{ "PLI [這里填C里的一個(gè)變量]");用于提前預(yù)加載(提示內(nèi)核等會兒要使用)需要執(zhí)行的指令,可以在跳轉(zhuǎn)后更快的執(zhí)行,通常用于執(zhí)行動態(tài)代碼,例如:
__ASM volatile (
"push{r0,r1}\n"
"MOV R0,0X01 \n"
"ISB SY\n"
"PLI [R0]\n"
"ISB SY\n"
"pop {r0,r1}\n"
);
__ASM volatile (
"push {r0-r3,r12,lr} \n"
"MOV R0,0X01 \n"
"BLX R0 \n"
"pop {r0-r3,r12,lr} \n"
);
中,將動態(tài)代碼存放于ITCM內(nèi)存中,代碼就是實(shí)例代碼1,存放地址0x00
對于需要跳轉(zhuǎn)動態(tài)代碼時(shí),保存寄存器是一個(gè)必不可少的的操作,通常使用堆棧保存,通常建議堆棧始終為8字節(jié)對齊(在cotex M系列內(nèi)核權(quán)威手冊里亦有記載) 所以在使用push的時(shí)候,建議一次性壓入兩個(gè)寄存器(64位)保持一直為8字節(jié)對齊.對于未對其的情況下,我也測試過了,首先效率會下降,對齊的情況,入棧8個(gè)字節(jié)僅需一個(gè)周期,無阻塞的發(fā)射,即可執(zhí)行接下來的指令,未對其情況下,與使用STM SP!{ }是等效的,需要1個(gè)周期解析指令+2個(gè)周期存入數(shù)據(jù).
其次未8字節(jié)對齊容易導(dǎo)致未定義行為,進(jìn)入HardFault_Handler,這個(gè)問題隱藏的很隱蔽,使用Jlink單步調(diào)試是無法復(fù)現(xiàn)問題.(我沒記錯(cuò)的話,權(quán)威手冊里應(yīng)該有記載,不過容易忽略,這里就提個(gè)醒)
對于地址跳轉(zhuǎn),一定要保證,需要跳轉(zhuǎn)的地址的最低位一定要為1, 例如你需要跳轉(zhuǎn)到0x0800346,那么你實(shí)際要寫入PC指針的地址一定是0x0800347,而不是0x0800346,否則也會導(dǎo)致未定義行為,進(jìn)入HardFault_Handler,同樣使用jlink無法復(fù)現(xiàn),單步無法發(fā)現(xiàn)問題.
先記錄到這里,如有新的勘誤點(diǎn)會在更新,附件為cotex M4系列內(nèi)核匯編編程手冊,全冊中文,對于初學(xué)者來說很有用處
|