嵌入式軟件開(kāi)發(fā)之基于A(yíng)RM處理器的嵌入式系統(tǒng)設(shè)計(jì)方案解析
2017-10-19 |
rar |
0.6 MB |
次下載 |
1積分
資料介紹
本章主要介紹嵌入式應(yīng)用程序的設(shè)計(jì)方法。本章中的一些實(shí)例程序是以ARM公司的Realview2.2為開(kāi)發(fā)平臺(tái)。由于目前嵌入式應(yīng)用環(huán)境相差非常大,這里主要是通過(guò)這些實(shí)例程序來(lái)更直接地介紹嵌入式應(yīng)用系統(tǒng)的開(kāi)發(fā)方法,具體的代碼因具體的嵌入式環(huán)境不同而有所差異。
13.1 基于A(yíng)RM處理器的嵌入式系統(tǒng)設(shè)計(jì)
ARM系列處理器是RISC(Reducded Instruction Set Computing)處理器。很多基于A(yíng)RM的高效代碼的程序設(shè)計(jì)策略都源于RISC處理器。和很多RISC處理器一樣,ARM系列處理器的內(nèi)存訪(fǎng)問(wèn)也要求數(shù)據(jù)對(duì)齊,即存取“字(Word)”數(shù)據(jù)時(shí)要求四字節(jié)對(duì)齊,地址的bits[1:0]=0b00;存取“半字(Halfwords)”時(shí)要求兩字節(jié)對(duì)齊,地址的bit[0]=0b0;存取“字節(jié)(Byte)”數(shù)據(jù)時(shí)要求該數(shù)據(jù)按其自然尺寸邊界(Natural Size Boundary)定位。
ARM編譯程序通常將全局變量對(duì)齊到自然尺寸邊界上,以便通過(guò)使用 LDR和STR指令有效地存取這些變量。
這種內(nèi)存訪(fǎng)問(wèn)方式與多數(shù)CISC(Complex Instruction Set Computing)體系結(jié)構(gòu)不同,在CISC體系結(jié)構(gòu)下,指令直接存取未對(duì)齊的數(shù)據(jù)。因而,當(dāng)需要將代碼從CISC體系結(jié)構(gòu)向 ARM處理器移植時(shí),內(nèi)存訪(fǎng)問(wèn)的地址對(duì)齊問(wèn)題必須予以注意。在RISC體系結(jié)構(gòu)下,存取未對(duì)齊數(shù)據(jù)無(wú)論在代碼尺寸或是程序執(zhí)行效率上,都將付出非常大的代價(jià)。
注意在A(yíng)RM11處理器上,新增加了支持非內(nèi)存對(duì)齊數(shù)據(jù)訪(fǎng)問(wèn)的硬件,此結(jié)構(gòu)在本章中不作討論。
下面將從4個(gè)方面詳細(xì)討論在A(yíng)RM體系結(jié)構(gòu)下的程序設(shè)計(jì):
· 未對(duì)齊指針;
· 結(jié)構(gòu)體中的未對(duì)齊字段;
· 用于半字存取的Load指令;
· 移植代碼并檢測(cè)非對(duì)齊存取。
13.1.1 未對(duì)齊的數(shù)據(jù)指針
C和C++編程標(biāo)準(zhǔn)規(guī)定,指向某一數(shù)據(jù)類(lèi)型的指針,必須和該類(lèi)型的數(shù)據(jù)地址對(duì)齊方式一致,所以ARM編譯器期望程序中的C指針指向存儲(chǔ)器中字對(duì)齊地址,因?yàn)檫@可使編譯器生成更高效的代碼。
比如,如果定義一個(gè)指向int數(shù)據(jù)類(lèi)型的指針,用該指針讀取一個(gè)字,ARM 編譯器將使用LDR指令來(lái)完成此操作。如果讀取的地址為4的倍數(shù)(即在一個(gè)字的邊界)即能正確讀取。但是,如果該地址不是4的倍數(shù),那么,一條LDR指令返回一個(gè)循環(huán)移位結(jié)果,而不是執(zhí)行真正的未對(duì)齊字載入。循環(huán)移位結(jié)果取決于該地址相對(duì)于字的邊界的偏移量和系統(tǒng)所使用的端序(Endianness)。例如,如果代碼要求從指針指向的地址0x8006載入數(shù)據(jù),即要載入0x8006、0x8007、0x8008和0x8009 4個(gè)字節(jié)的內(nèi)容。但是,在A(yíng)RM處理器上,這個(gè)存取操作載入了0x8004、0x8005、0x8006和0x8007字節(jié)的內(nèi)容。這就是在未對(duì)齊的地址上使用指針存取所得到的循環(huán)移位結(jié)果。
因而,如果想將指針定義到一個(gè)指定地址(該地址為非自然邊界對(duì)齊),那么在定義該指針時(shí),必須使用__packed限定符來(lái)定義指針:
例如:
__packed int *pi; // 指針指向一個(gè)非字對(duì)其內(nèi)存地址
使用了__packed限定符限定之后,ARM編譯器將產(chǎn)生字節(jié)存取命令(LDRB或STRB指令)來(lái)存取內(nèi)存,這樣就不必考慮指針對(duì)齊問(wèn)題。所生成的代碼是字節(jié)存取的一個(gè)序列,或者取決于編譯選項(xiàng)、跟變量對(duì)齊相關(guān)的移位和屏蔽。但這會(huì)導(dǎo)致系統(tǒng)性能和代碼密度的損失。
值得注意的是,不能使用__packed限定的指針來(lái)存取存儲(chǔ)器映射的外圍寄存器,因?yàn)锳RM編譯程序可使用多個(gè)存儲(chǔ)器存取來(lái)獲取數(shù)據(jù)。因而,可以對(duì)實(shí)際存取地址附近的位置進(jìn)行存取,而這些附近的位置可能對(duì)應(yīng)于其他外部寄存器。當(dāng)使用了位字段(Bitfield)時(shí),ARM程序?qū)⒃L(fǎng)問(wèn)整個(gè)結(jié)構(gòu)體,而非指定字段。
13.1.2 結(jié)構(gòu)體中未對(duì)齊字段
與全局變量位于其自然尺寸邊界相同,結(jié)構(gòu)體(Structure)中的域字段(Filed)也如此。也就是說(shuō)編譯程序經(jīng)常要在字段間插入填充字節(jié)(Padding)來(lái)確保域字段對(duì)齊。當(dāng)編譯程序插入填充字節(jié)時(shí),編譯器將產(chǎn)生以下警告信息。
#1301-D: padding inserted in struct mystruct
可以使用-remark編譯選項(xiàng)使編譯器產(chǎn)生備份信息,或使用-diag_warning選項(xiàng)選擇編譯器產(chǎn)生的備份信息。
如果不希望編譯器產(chǎn)生填充字節(jié),可以使用__packed限定符來(lái)創(chuàng)建字段之間沒(méi)有填充字節(jié)的結(jié)構(gòu),且這些結(jié)構(gòu)需要非對(duì)齊存取。
如果ARM編譯器能夠確定所訪(fǎng)問(wèn)結(jié)構(gòu)體的對(duì)齊方式,那么它就可以自動(dòng)識(shí)別所存取結(jié)構(gòu)體中的字段的對(duì)齊方式。在這些情況下,編譯程序盡可能地采用更有效的對(duì)齊字或半字存取方式。否則,編譯器將使用多個(gè)對(duì)齊存儲(chǔ)器存?。↙DR、STR、LDM和STM)與固定移位和屏蔽相結(jié)合來(lái)存取存儲(chǔ)器中的字節(jié)。
對(duì)非對(duì)齊元素的存取是通過(guò)內(nèi)聯(lián)還是通過(guò)調(diào)用一個(gè)函數(shù)來(lái)完成,由編譯程序-Ospace(默認(rèn),調(diào)用一個(gè)函數(shù))和-Otime(執(zhí)行非對(duì)齊存取內(nèi)聯(lián))選項(xiàng)來(lái)控制。
例如:
創(chuàng)建一個(gè)名為foo.c源文件。
__packed struct mystruct {
int aligned_i;
short aligned_s;
int unaligned_i;
};
struct mystruct S1;
int foo (int a, short b)
{
S1.aligned_i=a;
S1.aligned_s=b;
return S1.unaligned_i;
}
使用armcc -c -Otime foo.c編譯。所生成的代碼為:
MOV r2,r0
LDR r0,|L1.84|
MOV r12,r2,LSR #8
STRB r2,[r0,#0]
STRB r12,[r0,#1]
MOV r12,r2,LSR #16
STRB r12,[r0,#2]
MOV r12,r2,LSR #24
STRB r12,[r0,#3]
MOV r12,r1,LSR #8
STRB r1,[r0,#4]
STRB r12,[r0,#5]
ADD r0,r0,#6
BIC r3,r0,#3
AND r0,r0,#3
LDMIA r3,{r3,r12}
MOV r0,r0,LSL #3
MOV r3,r3,LSR r0
RSB r0,r0,#0x20
ORR r0,r3,r12,LSL r0
BX lr
其中,“|L1.84|”為結(jié)構(gòu)體mystruct在內(nèi)存中的地址。
從上例可以看出,所有對(duì)結(jié)構(gòu)體域成員的訪(fǎng)問(wèn)都是通過(guò)字節(jié)訪(fǎng)問(wèn)實(shí)現(xiàn)的,所以這種不對(duì)齊內(nèi)存訪(fǎng)問(wèn)無(wú)論從代碼占用的存儲(chǔ)器空間,還是代碼的執(zhí)行時(shí)間上都要付出一定的代價(jià)。
然而,開(kāi)發(fā)者可以給編譯器提供更多的信息,使其知道結(jié)構(gòu)體內(nèi)哪個(gè)字段是對(duì)齊的,哪個(gè)字段不是。為此,必須將未對(duì)齊字段聲明為_(kāi)_packed,并從struct本身除去__packed屬性。通過(guò)這種方法可以保證對(duì)struct中自然對(duì)齊成員的快速訪(fǎng)問(wèn)。而且,哪個(gè)字段是未對(duì)齊的也更清楚,但這樣就增加了訪(fǎng)問(wèn)struct結(jié)構(gòu)的難度,當(dāng)用戶(hù)從結(jié)構(gòu)中增加或刪除字段時(shí)需要特別小心。
修改上例中結(jié)構(gòu)體的定義,來(lái)減少訪(fǎng)問(wèn)結(jié)構(gòu)體的開(kāi)銷(xiāo)。具體代碼如下所示。
struct mystruct {
int aligned_i;
short aligned_s;
__packed int unaligned_i;
};
struct mystruct S1;
對(duì)修改后的程序進(jìn)行編譯,產(chǎn)生的匯編代碼如下所示。
MOV r2,r0
LDR r0,|L1.32|
STR r2,[r0,#0]
STRH r1,[r0,#4]
LDMIB r0,{r3,r12}
MOV r0,r3,LSR #16
ORR r0,r0,r12,LSL #16
BX lr
從編譯后的匯編代碼不難看出,對(duì)結(jié)構(gòu)體內(nèi)符號(hào)自然邊界對(duì)齊的域,編譯器直接使用相應(yīng)的Load/Store指令進(jìn)行訪(fǎng)問(wèn),而只有那些非自然邊界對(duì)齊的域,編譯器才進(jìn)行附加處理。這樣,從時(shí)間和空間兩方面減小了程序的開(kāi)銷(xiāo)。
同一原理也適應(yīng)于聯(lián)合體結(jié)構(gòu)(unions)。使用在存儲(chǔ)器中未對(duì)齊的聯(lián)合組件的__packed屬性。
13.1.3 用于半字存取的非對(duì)齊 LDR指令
一些特殊情況下,ARM編譯程序可以生成非對(duì)齊LDR指令。特別是編譯程序從存儲(chǔ)器中載入半字時(shí)將使用該方法。這是因?yàn)?,通過(guò)使用相應(yīng)地址,所需的半字可以載入到寄存器的高半段(bits[31:16]),然后通過(guò)移位,將有效數(shù)據(jù)移到寄存器的低半段(bits[15:0])。這樣做的目的是通過(guò)減少內(nèi)存訪(fǎng)問(wèn)次數(shù)來(lái)減少程序的執(zhí)行時(shí)間。通過(guò)上面的方法,程序只需要一次存儲(chǔ)器的訪(fǎng)問(wèn),而使用LDRB指令做同樣的操作需要兩次存儲(chǔ)器的存取,而且還要為將這兩個(gè)字節(jié)合并在一起添加特殊的代碼。在A(yíng)RM體系結(jié)構(gòu)v3和其早期版本中,通常使用該方法進(jìn)行所有的半字載入。但在A(yíng)RMv4及其以后版本中,出現(xiàn)了專(zhuān)門(mén)的半字載入指令,這種方法逐漸被取代。但是,非對(duì)齊LDR指令仍可能會(huì)出現(xiàn),比如在一個(gè)充填結(jié)構(gòu)中存取一個(gè)非對(duì)齊short域類(lèi)型。
注意在RVCT中已經(jīng)不再支持ARMv3架構(gòu)。
13.1.4 移植代碼并檢測(cè)非對(duì)齊內(nèi)存訪(fǎng)問(wèn)
在非RISC體系結(jié)構(gòu)的處理器上執(zhí)行的代碼中,可能會(huì)存在使用指針訪(fǎng)問(wèn)非自然邊界對(duì)齊的數(shù)據(jù)類(lèi)型。這種操作,在A(yíng)RM體系結(jié)構(gòu)中是不允許的。這就給代碼的移植帶來(lái)很大困難。用戶(hù)必須識(shí)別并更改此類(lèi)內(nèi)存訪(fǎng)問(wèn)代碼才能使其在RISC體系結(jié)構(gòu)的處理器上正確執(zhí)行。
識(shí)別非對(duì)齊存取可能會(huì)很困難,因?yàn)槭褂梅菍?duì)齊地址進(jìn)行的載入或存儲(chǔ)操作會(huì)產(chǎn)生不正確的動(dòng)作。追蹤到底是哪部分的C源程序造成了這個(gè)問(wèn)題是很困難的。
具有完整存儲(chǔ)器管理單元(MMUs)的ARM處理器,例如ARM920TTM,支持內(nèi)存對(duì)齊檢測(cè)功能,用戶(hù)可以通過(guò)設(shè)置MMU使處理器檢測(cè)每一次的內(nèi)存訪(fǎng)問(wèn)以確保其被正確地對(duì)齊。如果出現(xiàn)非對(duì)齊內(nèi)存訪(fǎng)問(wèn),MMU將產(chǎn)生數(shù)據(jù)中斷。這樣就給追蹤出錯(cuò)代碼帶來(lái)了很大的方便。
對(duì)于一些簡(jiǎn)單的沒(méi)有MMU的內(nèi)核,如ARM7TDMI,最好的方法是在ASIC(Application Specific Integrated Circuit)/ASSP(Application Specific Standard Product)內(nèi)部實(shí)現(xiàn)對(duì)齊檢測(cè)??梢栽黾訉?zhuān)門(mén)的ARM內(nèi)核擴(kuò)展硬件,由其監(jiān)控每次數(shù)據(jù)的訪(fǎng)問(wèn)的內(nèi)存大小和存取地址總線(xiàn)的最低有效位。在非對(duì)齊存取的情況下,可以通過(guò)配置ASIC/ASSP產(chǎn)生中斷信號(hào)(ABORT)。ARM公司建議在需要運(yùn)行移植代碼設(shè)備中包含這樣的ASIC/ASSP邏輯。
如果在設(shè)計(jì)系統(tǒng)時(shí),將系統(tǒng)設(shè)計(jì)成為當(dāng)出現(xiàn)非對(duì)齊的內(nèi)存訪(fǎng)問(wèn)時(shí)產(chǎn)生異常,則必須安裝數(shù)據(jù)中斷異常處理程序(Data Abort Handler)。出現(xiàn)非對(duì)齊存取時(shí),程序進(jìn)入數(shù)據(jù)中斷處理程序,并由此識(shí)別位于返回地址(在LR中保存的地址)減8(r14-8)的出錯(cuò)數(shù)據(jù)存取指令。
一旦出現(xiàn)數(shù)據(jù)中斷異常,必須通過(guò)改變C源程序來(lái)修復(fù)非對(duì)齊的數(shù)據(jù)訪(fǎng)問(wèn)。使用下列指令可有條件地完成修復(fù):
#ifdef __arm
#define PACKED __packed
#else
#define PACKED
#endif
?。?br /> PACKED int *pi;
:
由于代碼大小和性能上的開(kāi)銷(xiāo),最好盡可能少采用存取非對(duì)齊數(shù)據(jù)。
ARM編譯器支持--pointer_alignment和--min_array_alignment與內(nèi)存對(duì)齊相關(guān)的編譯選項(xiàng),詳見(jiàn)ARM相關(guān)文檔。
?
13.1 基于A(yíng)RM處理器的嵌入式系統(tǒng)設(shè)計(jì)
ARM系列處理器是RISC(Reducded Instruction Set Computing)處理器。很多基于A(yíng)RM的高效代碼的程序設(shè)計(jì)策略都源于RISC處理器。和很多RISC處理器一樣,ARM系列處理器的內(nèi)存訪(fǎng)問(wèn)也要求數(shù)據(jù)對(duì)齊,即存取“字(Word)”數(shù)據(jù)時(shí)要求四字節(jié)對(duì)齊,地址的bits[1:0]=0b00;存取“半字(Halfwords)”時(shí)要求兩字節(jié)對(duì)齊,地址的bit[0]=0b0;存取“字節(jié)(Byte)”數(shù)據(jù)時(shí)要求該數(shù)據(jù)按其自然尺寸邊界(Natural Size Boundary)定位。
ARM編譯程序通常將全局變量對(duì)齊到自然尺寸邊界上,以便通過(guò)使用 LDR和STR指令有效地存取這些變量。
這種內(nèi)存訪(fǎng)問(wèn)方式與多數(shù)CISC(Complex Instruction Set Computing)體系結(jié)構(gòu)不同,在CISC體系結(jié)構(gòu)下,指令直接存取未對(duì)齊的數(shù)據(jù)。因而,當(dāng)需要將代碼從CISC體系結(jié)構(gòu)向 ARM處理器移植時(shí),內(nèi)存訪(fǎng)問(wèn)的地址對(duì)齊問(wèn)題必須予以注意。在RISC體系結(jié)構(gòu)下,存取未對(duì)齊數(shù)據(jù)無(wú)論在代碼尺寸或是程序執(zhí)行效率上,都將付出非常大的代價(jià)。
注意在A(yíng)RM11處理器上,新增加了支持非內(nèi)存對(duì)齊數(shù)據(jù)訪(fǎng)問(wèn)的硬件,此結(jié)構(gòu)在本章中不作討論。
下面將從4個(gè)方面詳細(xì)討論在A(yíng)RM體系結(jié)構(gòu)下的程序設(shè)計(jì):
· 未對(duì)齊指針;
· 結(jié)構(gòu)體中的未對(duì)齊字段;
· 用于半字存取的Load指令;
· 移植代碼并檢測(cè)非對(duì)齊存取。
13.1.1 未對(duì)齊的數(shù)據(jù)指針
C和C++編程標(biāo)準(zhǔn)規(guī)定,指向某一數(shù)據(jù)類(lèi)型的指針,必須和該類(lèi)型的數(shù)據(jù)地址對(duì)齊方式一致,所以ARM編譯器期望程序中的C指針指向存儲(chǔ)器中字對(duì)齊地址,因?yàn)檫@可使編譯器生成更高效的代碼。
比如,如果定義一個(gè)指向int數(shù)據(jù)類(lèi)型的指針,用該指針讀取一個(gè)字,ARM 編譯器將使用LDR指令來(lái)完成此操作。如果讀取的地址為4的倍數(shù)(即在一個(gè)字的邊界)即能正確讀取。但是,如果該地址不是4的倍數(shù),那么,一條LDR指令返回一個(gè)循環(huán)移位結(jié)果,而不是執(zhí)行真正的未對(duì)齊字載入。循環(huán)移位結(jié)果取決于該地址相對(duì)于字的邊界的偏移量和系統(tǒng)所使用的端序(Endianness)。例如,如果代碼要求從指針指向的地址0x8006載入數(shù)據(jù),即要載入0x8006、0x8007、0x8008和0x8009 4個(gè)字節(jié)的內(nèi)容。但是,在A(yíng)RM處理器上,這個(gè)存取操作載入了0x8004、0x8005、0x8006和0x8007字節(jié)的內(nèi)容。這就是在未對(duì)齊的地址上使用指針存取所得到的循環(huán)移位結(jié)果。
因而,如果想將指針定義到一個(gè)指定地址(該地址為非自然邊界對(duì)齊),那么在定義該指針時(shí),必須使用__packed限定符來(lái)定義指針:
例如:
__packed int *pi; // 指針指向一個(gè)非字對(duì)其內(nèi)存地址
使用了__packed限定符限定之后,ARM編譯器將產(chǎn)生字節(jié)存取命令(LDRB或STRB指令)來(lái)存取內(nèi)存,這樣就不必考慮指針對(duì)齊問(wèn)題。所生成的代碼是字節(jié)存取的一個(gè)序列,或者取決于編譯選項(xiàng)、跟變量對(duì)齊相關(guān)的移位和屏蔽。但這會(huì)導(dǎo)致系統(tǒng)性能和代碼密度的損失。
值得注意的是,不能使用__packed限定的指針來(lái)存取存儲(chǔ)器映射的外圍寄存器,因?yàn)锳RM編譯程序可使用多個(gè)存儲(chǔ)器存取來(lái)獲取數(shù)據(jù)。因而,可以對(duì)實(shí)際存取地址附近的位置進(jìn)行存取,而這些附近的位置可能對(duì)應(yīng)于其他外部寄存器。當(dāng)使用了位字段(Bitfield)時(shí),ARM程序?qū)⒃L(fǎng)問(wèn)整個(gè)結(jié)構(gòu)體,而非指定字段。
13.1.2 結(jié)構(gòu)體中未對(duì)齊字段
與全局變量位于其自然尺寸邊界相同,結(jié)構(gòu)體(Structure)中的域字段(Filed)也如此。也就是說(shuō)編譯程序經(jīng)常要在字段間插入填充字節(jié)(Padding)來(lái)確保域字段對(duì)齊。當(dāng)編譯程序插入填充字節(jié)時(shí),編譯器將產(chǎn)生以下警告信息。
#1301-D: padding inserted in struct mystruct
可以使用-remark編譯選項(xiàng)使編譯器產(chǎn)生備份信息,或使用-diag_warning選項(xiàng)選擇編譯器產(chǎn)生的備份信息。
如果不希望編譯器產(chǎn)生填充字節(jié),可以使用__packed限定符來(lái)創(chuàng)建字段之間沒(méi)有填充字節(jié)的結(jié)構(gòu),且這些結(jié)構(gòu)需要非對(duì)齊存取。
如果ARM編譯器能夠確定所訪(fǎng)問(wèn)結(jié)構(gòu)體的對(duì)齊方式,那么它就可以自動(dòng)識(shí)別所存取結(jié)構(gòu)體中的字段的對(duì)齊方式。在這些情況下,編譯程序盡可能地采用更有效的對(duì)齊字或半字存取方式。否則,編譯器將使用多個(gè)對(duì)齊存儲(chǔ)器存?。↙DR、STR、LDM和STM)與固定移位和屏蔽相結(jié)合來(lái)存取存儲(chǔ)器中的字節(jié)。
對(duì)非對(duì)齊元素的存取是通過(guò)內(nèi)聯(lián)還是通過(guò)調(diào)用一個(gè)函數(shù)來(lái)完成,由編譯程序-Ospace(默認(rèn),調(diào)用一個(gè)函數(shù))和-Otime(執(zhí)行非對(duì)齊存取內(nèi)聯(lián))選項(xiàng)來(lái)控制。
例如:
創(chuàng)建一個(gè)名為foo.c源文件。
__packed struct mystruct {
int aligned_i;
short aligned_s;
int unaligned_i;
};
struct mystruct S1;
int foo (int a, short b)
{
S1.aligned_i=a;
S1.aligned_s=b;
return S1.unaligned_i;
}
使用armcc -c -Otime foo.c編譯。所生成的代碼為:
MOV r2,r0
LDR r0,|L1.84|
MOV r12,r2,LSR #8
STRB r2,[r0,#0]
STRB r12,[r0,#1]
MOV r12,r2,LSR #16
STRB r12,[r0,#2]
MOV r12,r2,LSR #24
STRB r12,[r0,#3]
MOV r12,r1,LSR #8
STRB r1,[r0,#4]
STRB r12,[r0,#5]
ADD r0,r0,#6
BIC r3,r0,#3
AND r0,r0,#3
LDMIA r3,{r3,r12}
MOV r0,r0,LSL #3
MOV r3,r3,LSR r0
RSB r0,r0,#0x20
ORR r0,r3,r12,LSL r0
BX lr
其中,“|L1.84|”為結(jié)構(gòu)體mystruct在內(nèi)存中的地址。
從上例可以看出,所有對(duì)結(jié)構(gòu)體域成員的訪(fǎng)問(wèn)都是通過(guò)字節(jié)訪(fǎng)問(wèn)實(shí)現(xiàn)的,所以這種不對(duì)齊內(nèi)存訪(fǎng)問(wèn)無(wú)論從代碼占用的存儲(chǔ)器空間,還是代碼的執(zhí)行時(shí)間上都要付出一定的代價(jià)。
然而,開(kāi)發(fā)者可以給編譯器提供更多的信息,使其知道結(jié)構(gòu)體內(nèi)哪個(gè)字段是對(duì)齊的,哪個(gè)字段不是。為此,必須將未對(duì)齊字段聲明為_(kāi)_packed,并從struct本身除去__packed屬性。通過(guò)這種方法可以保證對(duì)struct中自然對(duì)齊成員的快速訪(fǎng)問(wèn)。而且,哪個(gè)字段是未對(duì)齊的也更清楚,但這樣就增加了訪(fǎng)問(wèn)struct結(jié)構(gòu)的難度,當(dāng)用戶(hù)從結(jié)構(gòu)中增加或刪除字段時(shí)需要特別小心。
修改上例中結(jié)構(gòu)體的定義,來(lái)減少訪(fǎng)問(wèn)結(jié)構(gòu)體的開(kāi)銷(xiāo)。具體代碼如下所示。
struct mystruct {
int aligned_i;
short aligned_s;
__packed int unaligned_i;
};
struct mystruct S1;
對(duì)修改后的程序進(jìn)行編譯,產(chǎn)生的匯編代碼如下所示。
MOV r2,r0
LDR r0,|L1.32|
STR r2,[r0,#0]
STRH r1,[r0,#4]
LDMIB r0,{r3,r12}
MOV r0,r3,LSR #16
ORR r0,r0,r12,LSL #16
BX lr
從編譯后的匯編代碼不難看出,對(duì)結(jié)構(gòu)體內(nèi)符號(hào)自然邊界對(duì)齊的域,編譯器直接使用相應(yīng)的Load/Store指令進(jìn)行訪(fǎng)問(wèn),而只有那些非自然邊界對(duì)齊的域,編譯器才進(jìn)行附加處理。這樣,從時(shí)間和空間兩方面減小了程序的開(kāi)銷(xiāo)。
同一原理也適應(yīng)于聯(lián)合體結(jié)構(gòu)(unions)。使用在存儲(chǔ)器中未對(duì)齊的聯(lián)合組件的__packed屬性。
13.1.3 用于半字存取的非對(duì)齊 LDR指令
一些特殊情況下,ARM編譯程序可以生成非對(duì)齊LDR指令。特別是編譯程序從存儲(chǔ)器中載入半字時(shí)將使用該方法。這是因?yàn)?,通過(guò)使用相應(yīng)地址,所需的半字可以載入到寄存器的高半段(bits[31:16]),然后通過(guò)移位,將有效數(shù)據(jù)移到寄存器的低半段(bits[15:0])。這樣做的目的是通過(guò)減少內(nèi)存訪(fǎng)問(wèn)次數(shù)來(lái)減少程序的執(zhí)行時(shí)間。通過(guò)上面的方法,程序只需要一次存儲(chǔ)器的訪(fǎng)問(wèn),而使用LDRB指令做同樣的操作需要兩次存儲(chǔ)器的存取,而且還要為將這兩個(gè)字節(jié)合并在一起添加特殊的代碼。在A(yíng)RM體系結(jié)構(gòu)v3和其早期版本中,通常使用該方法進(jìn)行所有的半字載入。但在A(yíng)RMv4及其以后版本中,出現(xiàn)了專(zhuān)門(mén)的半字載入指令,這種方法逐漸被取代。但是,非對(duì)齊LDR指令仍可能會(huì)出現(xiàn),比如在一個(gè)充填結(jié)構(gòu)中存取一個(gè)非對(duì)齊short域類(lèi)型。
注意在RVCT中已經(jīng)不再支持ARMv3架構(gòu)。
13.1.4 移植代碼并檢測(cè)非對(duì)齊內(nèi)存訪(fǎng)問(wèn)
在非RISC體系結(jié)構(gòu)的處理器上執(zhí)行的代碼中,可能會(huì)存在使用指針訪(fǎng)問(wèn)非自然邊界對(duì)齊的數(shù)據(jù)類(lèi)型。這種操作,在A(yíng)RM體系結(jié)構(gòu)中是不允許的。這就給代碼的移植帶來(lái)很大困難。用戶(hù)必須識(shí)別并更改此類(lèi)內(nèi)存訪(fǎng)問(wèn)代碼才能使其在RISC體系結(jié)構(gòu)的處理器上正確執(zhí)行。
識(shí)別非對(duì)齊存取可能會(huì)很困難,因?yàn)槭褂梅菍?duì)齊地址進(jìn)行的載入或存儲(chǔ)操作會(huì)產(chǎn)生不正確的動(dòng)作。追蹤到底是哪部分的C源程序造成了這個(gè)問(wèn)題是很困難的。
具有完整存儲(chǔ)器管理單元(MMUs)的ARM處理器,例如ARM920TTM,支持內(nèi)存對(duì)齊檢測(cè)功能,用戶(hù)可以通過(guò)設(shè)置MMU使處理器檢測(cè)每一次的內(nèi)存訪(fǎng)問(wèn)以確保其被正確地對(duì)齊。如果出現(xiàn)非對(duì)齊內(nèi)存訪(fǎng)問(wèn),MMU將產(chǎn)生數(shù)據(jù)中斷。這樣就給追蹤出錯(cuò)代碼帶來(lái)了很大的方便。
對(duì)于一些簡(jiǎn)單的沒(méi)有MMU的內(nèi)核,如ARM7TDMI,最好的方法是在ASIC(Application Specific Integrated Circuit)/ASSP(Application Specific Standard Product)內(nèi)部實(shí)現(xiàn)對(duì)齊檢測(cè)??梢栽黾訉?zhuān)門(mén)的ARM內(nèi)核擴(kuò)展硬件,由其監(jiān)控每次數(shù)據(jù)的訪(fǎng)問(wèn)的內(nèi)存大小和存取地址總線(xiàn)的最低有效位。在非對(duì)齊存取的情況下,可以通過(guò)配置ASIC/ASSP產(chǎn)生中斷信號(hào)(ABORT)。ARM公司建議在需要運(yùn)行移植代碼設(shè)備中包含這樣的ASIC/ASSP邏輯。
如果在設(shè)計(jì)系統(tǒng)時(shí),將系統(tǒng)設(shè)計(jì)成為當(dāng)出現(xiàn)非對(duì)齊的內(nèi)存訪(fǎng)問(wèn)時(shí)產(chǎn)生異常,則必須安裝數(shù)據(jù)中斷異常處理程序(Data Abort Handler)。出現(xiàn)非對(duì)齊存取時(shí),程序進(jìn)入數(shù)據(jù)中斷處理程序,并由此識(shí)別位于返回地址(在LR中保存的地址)減8(r14-8)的出錯(cuò)數(shù)據(jù)存取指令。
一旦出現(xiàn)數(shù)據(jù)中斷異常,必須通過(guò)改變C源程序來(lái)修復(fù)非對(duì)齊的數(shù)據(jù)訪(fǎng)問(wèn)。使用下列指令可有條件地完成修復(fù):
#ifdef __arm
#define PACKED __packed
#else
#define PACKED
#endif
?。?br /> PACKED int *pi;
:
由于代碼大小和性能上的開(kāi)銷(xiāo),最好盡可能少采用存取非對(duì)齊數(shù)據(jù)。
ARM編譯器支持--pointer_alignment和--min_array_alignment與內(nèi)存對(duì)齊相關(guān)的編譯選項(xiàng),詳見(jiàn)ARM相關(guān)文檔。
?
下載該資料的人也在下載
下載該資料的人還在閱讀
更多 >
- 嵌入式軟件開(kāi)發(fā)的特點(diǎn)、設(shè)計(jì)流程、嵌入式軟件的結(jié)構(gòu)
- 嵌入式軟件開(kāi)發(fā)做什么?嵌入式開(kāi)發(fā)培訓(xùn)學(xué)哪些
- 嵌入式軟件開(kāi)發(fā)是什么??
- 嵌入式軟件開(kāi)發(fā)環(huán)境
- 嵌入式控制系統(tǒng)的ARM處理器
- ARM處理器與嵌入式系統(tǒng)
- 基于ARM的嵌入式Web服務(wù)器設(shè)計(jì)方案 20次下載
- 嵌入式系統(tǒng)教程之嵌入式處理器的詳細(xì)資料說(shuō)明 18次下載
- 嵌入式教程之嵌入式系統(tǒng)基礎(chǔ)的詳細(xì)資料說(shuō)明 30次下載
- ARM主板,嵌入式主板,ARM工控主板,嵌入式工控機(jī)控制系統(tǒng)的人機(jī)界面組態(tài)軟件開(kāi)發(fā)方案 21次下載
- 嵌入式軟件開(kāi)發(fā)概述 1次下載
- 嵌入式軟件技術(shù)應(yīng)用設(shè)計(jì)方案匯總 4次下載
- ARM嵌入式軟件開(kāi)發(fā) 65次下載
- 嵌入式軟件開(kāi)發(fā)與Embedded-GIS 36次下載
- 基于ARM的嵌入式系統(tǒng)軟件設(shè)計(jì)
- 什么是嵌入式微處理器?嵌入式微處理器有哪些? 1069次閱讀
- 嵌入式軟件開(kāi)發(fā)和軟件開(kāi)發(fā)的區(qū)別 2357次閱讀
- 基于UML嵌入式軟件開(kāi)發(fā)環(huán)境的自動(dòng)取款機(jī)系統(tǒng)的應(yīng)用方案 3777次閱讀
- 基于嵌入式軟件的JNI技術(shù)應(yīng)用解析 1106次閱讀
- 關(guān)于嵌入式ARM多核處理器的并行方法 4279次閱讀
- 嵌入式軟件開(kāi)發(fā)編程規(guī)范介紹 4403次閱讀
- 嵌入式軟件工程師職業(yè)規(guī)劃必知 6620次閱讀
- 嵌入式開(kāi)發(fā)和底層開(kāi)發(fā)有什么區(qū)別 1.6w次閱讀
- 以MicroBlaze 處理器為內(nèi)核的嵌入式系統(tǒng)開(kāi)發(fā)過(guò)程詳解 2433次閱讀
- 液晶顯示屏設(shè)計(jì)方案:基于Nios嵌入式軟核處理器 1190次閱讀
- 嵌入式開(kāi)發(fā)工具有哪些_五款嵌入式開(kāi)發(fā)工具推薦 4w次閱讀
- 嵌入式處理器匯總_常見(jiàn)的嵌入式處理器對(duì)比分析 1.4w次閱讀
- 嵌入式開(kāi)發(fā)環(huán)境的搭建 8548次閱讀
- 32位嵌入式處理器與8位處理器應(yīng)用開(kāi)發(fā)的區(qū)別 1579次閱讀
- 基于ARM的嵌入式無(wú)線(xiàn)視頻采集系統(tǒng)設(shè)計(jì) 1888次閱讀
下載排行
本周
- 1TC358743XBG評(píng)估板參考手冊(cè)
- 1.36 MB | 330次下載 | 免費(fèi)
- 2開(kāi)關(guān)電源基礎(chǔ)知識(shí)
- 5.73 MB | 6次下載 | 免費(fèi)
- 3100W短波放大電路圖
- 0.05 MB | 4次下載 | 3 積分
- 4嵌入式linux-聊天程序設(shè)計(jì)
- 0.60 MB | 3次下載 | 免費(fèi)
- 5基于FPGA的光纖通信系統(tǒng)的設(shè)計(jì)與實(shí)現(xiàn)
- 0.61 MB | 2次下載 | 免費(fèi)
- 6基于FPGA的C8051F單片機(jī)開(kāi)發(fā)板設(shè)計(jì)
- 0.70 MB | 2次下載 | 免費(fèi)
- 751單片機(jī)窗簾控制器仿真程序
- 1.93 MB | 2次下載 | 免費(fèi)
- 8基于51單片機(jī)的RGB調(diào)色燈程序仿真
- 0.86 MB | 2次下載 | 免費(fèi)
本月
- 1OrCAD10.5下載OrCAD10.5中文版軟件
- 0.00 MB | 234315次下載 | 免費(fèi)
- 2555集成電路應(yīng)用800例(新編版)
- 0.00 MB | 33564次下載 | 免費(fèi)
- 3接口電路圖大全
- 未知 | 30323次下載 | 免費(fèi)
- 4開(kāi)關(guān)電源設(shè)計(jì)實(shí)例指南
- 未知 | 21548次下載 | 免費(fèi)
- 5電氣工程師手冊(cè)免費(fèi)下載(新編第二版pdf電子書(shū))
- 0.00 MB | 15349次下載 | 免費(fèi)
- 6數(shù)字電路基礎(chǔ)pdf(下載)
- 未知 | 13750次下載 | 免費(fèi)
- 7電子制作實(shí)例集錦 下載
- 未知 | 8113次下載 | 免費(fèi)
- 8《LED驅(qū)動(dòng)電路設(shè)計(jì)》 溫德?tīng)栔?/a>
- 0.00 MB | 6653次下載 | 免費(fèi)
總榜
- 1matlab軟件下載入口
- 未知 | 935054次下載 | 免費(fèi)
- 2protel99se軟件下載(可英文版轉(zhuǎn)中文版)
- 78.1 MB | 537796次下載 | 免費(fèi)
- 3MATLAB 7.1 下載 (含軟件介紹)
- 未知 | 420026次下載 | 免費(fèi)
- 4OrCAD10.5下載OrCAD10.5中文版軟件
- 0.00 MB | 234315次下載 | 免費(fèi)
- 5Altium DXP2002下載入口
- 未知 | 233046次下載 | 免費(fèi)
- 6電路仿真軟件multisim 10.0免費(fèi)下載
- 340992 | 191185次下載 | 免費(fèi)
- 7十天學(xué)會(huì)AVR單片機(jī)與C語(yǔ)言視頻教程 下載
- 158M | 183278次下載 | 免費(fèi)
- 8proe5.0野火版下載(中文版免費(fèi)下載)
- 未知 | 138040次下載 | 免費(fèi)
評(píng)論