大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家講的是嵌入式開發裡的image檔案(.bin, .hex, .s19)。
今天這節課是痞子衡《ARM Cortex-M檔案那些事》主題系列的最後一節課(突然有點不捨,要告別的感覺,咳咳,讓痞子衡整理下情緒先)。今天痞子衡主要講的是工程開發最終的output檔案,即image檔案。image檔案也叫映象檔案,這個檔案主要包含的是隻有晶片能夠解釋執行的二進位制機器碼資料,這些資料其實在前面介紹的relocatable、list、executable檔案中出現過,在那些檔案裡我們還可以根據其他輔助資訊來分析機器碼資料的實際意義,但在image檔案裡,我們已經完全無法看懂這些機器碼了。所以image檔案主要是用來做大規模量產的。既要做大規模量產,由於各晶片廠家制定的標準不一,所以實際上image檔案有很多種格式,今天我們主要講的是其中最具有代表性也應用最廣泛的3種image檔案格式。
一、通用映象檔案bin
第一種格式叫binary,以.bin為檔案字尾,這種格式是一種通用image格式,其完全是機器碼裸資料的集合,沒有其他任何多餘資訊,這個資料可以直接被程式設計器/下載器下載到晶片內部非易失性儲存器裡,不需要任何額外的資料轉換,所見即所得。由於是純二進位制編碼的檔案,所以普通text編輯器無法正確檢視這個檔案,需要用專用的十六進位制編輯器(比如Hex Editor HxD)才能正常開啟。以本系列建立的demo工程的demo.bin檔案為例,用HxD開啟可見如下資料(僅擷取前後部分顯示,demo.bin共6780 bytes)。
offset(h)
00000000: 00 20 00 10 41 00 00 00 DB 18 00 00 CB 19 00 00
00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000020: 00 00 00 00 00 00 00 00 00 00 00 00 07 1A 00 00
00000030: 00 00 00 00 00 00 00 00 1D 1A 00 00 1F 1A 00 00
00000040: 72 B6 0E 48 0E 49 88 60 00 22 00 23 00 24 00 25
...
00001A30: 01 23 00 24 13 E0 0A 68 09 1D 1A 42 02 D0 4D 46
00001A40: 6D 1E 52 19 14 60 12 1D 00 1F 04 28 FA D2 15 00
00001A50: 86 07 01 D5 14 80 AD 1C 18 40 00 D0 2C 70 08 68
00001A60: 09 1D 00 28 E7 D1 08 00 70 BC 70 47 C1 FF FF FF
00001A70: 08 02 00 00 14 20 00 10 00 00 00 00 -- -- -- --
有細心的朋友可能會疑問,開啟bin檔案看資料都是按連續地址排列的,如果在應用設計中,我們在linker檔案裡給各個段分配的地址不是連續的,這在bin檔案裡是怎麼處理的?為了解決這個問題,bin檔案會在非有效地址區域插入無效位元組以保證有效地址處都是正確的資料,這在IDE裡或相關轉換工具裡都會有相應option,可以讓使用者設定填充位元組pattern(比如0x00,0xFF等)。
好,現在讓我們嘗試分析一下這個bin檔案,我們都知道ARM Cortex-M架構裡,image bin檔案前8個位元組應該是初始SP和PC的值,從前面map檔案裡我們知道SP=0x10002000,PC=0x00000041,來檢查一下bin檔案裡是不是這樣,前4個位元組分別是 00 20 00 10、看起來好像跟0x10002000資料是吻合的,但是這個資料排列方式看起來好像有點彆扭,怎麼回事?嵌入式老司機這時應該要莞爾一笑,是的,ARM Coretx-M預設採用的Little-Endian(小端)模式,即低位位元組排放在記憶體的低地址端,高位位元組排放在記憶體的高地址端。資料檢視地址顯示都是從低到高的(從左到右),所以SP的最低位元組應該顯示在最左邊(最低地址),而不是像檢視0x10002000那樣最低位元組在最右邊,這跟人的閱讀習慣是有點不吻合。
PS: 既有小端模式,那麼與其對應的也有大端模式(Big-Endian)-高位位元組排放在記憶體的低地址端,低位位元組排放在記憶體的高地址端。注意大端小端僅是針對以32bit為單元的資料排列方式差異,對於n個32bit資料,其都是統一的排列。
offset(h)
// 小端
00000000: 00 20 00 10 41 00 00 00
// 大端
00000000: 10 00 20 00 00 00 00 41
從上面對bin檔案的分析,我們知道bin檔案是不含地址資訊的,也就是說bin檔案資料應該被放在什麼地址處,我們僅從bin檔案本身是無法得知的。所以在使用程式設計器/下載器下載bin檔案時,使用者必須指定起始下載地址。由於bin檔案的這種侷限性,下面兩種帶地址資訊的image格式應運而生。
二、Intel映象檔案標準hex
第二種格式叫Intel hex,以.hex為檔案字尾,這種格式是Intel公司推行的一種image格式標準,其不僅含有機器碼裸資料還含有地址資訊等額外資訊,與bin檔案不同的是,hex檔案可以直接通用普通text編輯器開啟檢視,hex檔案採用的ASCII編碼,hex檔案內的機器碼資料不可以直接被下載進晶片內部,需要在幀資料解析的過程中進行轉換。
由於hex檔案並不是純機器碼檔案,還含有其他額外資訊,那麼hex檔案就需要按某種約定格式進行資料組織,資料組織方式叫幀格式,hex檔案是由n幀資料組成的。
2.1 hex幀格式
要想解析hex檔案,必須要先了解其幀格式,hex每幀都由下表列出的6部分組成:
幀段名 | Start code | Byte count | Address | Record type | Data | Checksum |
---|---|---|---|---|---|---|
幀段內容 | 固定前導碼':',十六進位制0x3A | 機器碼資料長度 | 機器碼資料儲存地址 | 幀型別"00"-"05",有6種幀 | 機器碼資料 | 校驗和 |
幀段長度(bytes) | 1 | 1*2 | 2*2 | 1*2 | (0-255)*2 | 1*2 |
程式設計器/下載器在解析hex檔案時,先找到幀前導碼,然後找到幀型別,如果該幀為資料幀,再根據幀機器碼長度,將該幀機器碼資料全部讀出放到快取裡,在做完幀和校驗後,如果沒有錯誤,最後根據幀機器碼儲存地址將幀機器碼資料下載到晶片指定儲存器地址處,至此一幀處理結束,進入下一幀,直到所有幀全部處理完。需要注意的是由於hex檔案是ASCII編碼,所以相比bin檔案長度至少大2倍以上,demo.hex檔案大小有19,106 bytes,後面我們會擷取部分hex檔案進行分析。
關於checksum的計算方法,其是將Byte count、Address、Record type、Data四個段內所有byte全部相加得到sum,擷取sum的LSB(最低位元組),再對該LSB取其補碼(預設該LSB為負數的低8bit資料位(注意8bit中沒有符號位),其反碼為原碼各bit取反,其補碼為反碼+1)得到checksum。比如LSB是0xA5,其反碼為0x5A,補碼為0x5B,則checksum為0x5B。
2.2 hex幀型別
前面說到一共有6種型別的幀,其中最重要也是數量最多的幀是資料幀,除了資料幀之外還有其他5種幀,下面來統一介紹:
幀型別碼 | 幀型別 | 幀描述 | 幀舉例 |
---|---|---|---|
"00" | 資料幀 | 以16bit地址描述開始的最大255個位元組有效機器碼的資料幀 | 含11bytes機器碼從0x0010地址處開始的資料幀:0B0010006164647265737320676170A7 |
"01" | 檔案結尾幀 | 用於表明hex檔案的結尾 | 統一的檔案結尾幀:00000001FF |
"02" | 擴充段地址幀 | 多用於80x86架構晶片,ARM Cortex-M架構不用 | :020000021200EA |
"03" | 起始段地址幀 | 多用於80x86架構晶片,ARM Cortex-M架構不用 | :0400000300003800C1 |
"04" | 擴充線性地址幀 | 用於32bit地址儲存空間晶片,與資料幀配合使用,指引程式設計器將資料下載到正確地址 | 標明擴充地址為0xFFFF的幀:02000004FFFFFC |
"05" | 起始程式地址(PC)幀 | 指示偵錯程式,程式初始PC地址,方便線上除錯 | 標明起始PC為0x000000CD的幀:04000005000000CD2A |
2.3 解析hex檔案
在瞭解hex檔案幀資料格式之後,讓我們開始嘗試解析demo.hex檔案(僅擷取前後部分,與前面擷取的bin檔案內容對應著一起分析)
:100000000020001041000000DB180000CB190000A8
:1000100000000000000000000000000000000000E0
:10002000000000000000000000000000071A0000AF
:1000300000000000000000001D1A00001F1A000050
:1000400072B60E480E498860002200230024002565
....
:101A30000123002413E00A68091D1A4202D04D4612
:101A40006D1E52191460121D001F0428FAD21500D1
:101A5000860701D51480AD1C184000D02C70086892
:101A6000091D0028E7D1080070BC7047C1FFFFFFC7
:0C1A70000802000014200010000000001C
:0400000500000041B6
:00000001FF
hex檔案前5幀均為資料幀,每幀包含16bytes機器碼資料,幀資料地址分別為0x0000, 0x0010, 0x0020, 0x0030, 0x0040,可見幀資料是連續的,並且5幀機器碼資料共80bytes與bin檔案前80bytes是一致的。
再來看最後7幀資料裡的前5個資料幀,除最後一幀資料只包含12bytes資料外,其餘資料幀均含有16bytes資料,5幀資料一共76bytes,幀資料地址從0x1A30 - 0x1A70。顯然這與bin檔案最後76bytes也是吻合的。
倒數第二幀是起始程式地址幀,其標明的程式起始PC是0x00000041,這與bin檔案裡第二個32bit資料(起始PC)是一致的。
倒數第一幀顯然是標準檔案結尾幀。
三、Motorola映象檔案標準S-Record
第三種格式叫Motorola S-Record,以.s19或.srec為檔案字尾,這種格式是Motorola公司推行的一種image格式標準,其與Intel hex檔案比較類似,都是ASCII編碼的檔案,可以通過普通text編輯器開啟檢視,其也由幀資料組成,只是幀格式與Intel hex有差別,還是按照介紹Intel hex檔案那樣先來看S-Record檔案的幀格式。
3.1 S-Record幀格式
S-Record每幀由下表列出的6部分組成:
幀段名 | Start code | Record type | Byte count | Address | Data | Checksum |
---|---|---|---|---|---|---|
幀段內容 | 固定前導碼'S',十六進位制0x53 | 幀型別'0'-'9',有10種幀 | 幀資料長度(包含後續段地址、資料、校驗和) | 機器碼資料儲存地址 | 機器碼資料 | 校驗和 |
幀段長度(bytes) | 1 | 1 | 1*2 | (2-4)*2 | (0-255)*2 | 1*2 |
程式設計器/下載器在解析S-Record檔案時,先找到幀前導碼,然後找到幀型別,如果該幀為資料幀,再根據幀長度,將該幀機器碼資料全部讀出放到快取裡,在做完幀和校驗後,如果沒有錯誤,最後根據幀機器碼儲存地址將幀機器碼資料下載到晶片指定儲存器地址處,至此一幀處理結束,進入下一幀,直到所有幀全部處理完。
關於checksum的計算方法,其是將Byte count、Address、Data三個段內所有byte全部相加得到sum,擷取sum的LSB(最低位元組),再對該LSB取其反碼(預設該LSB為負數的低8bit資料位(注意8bit中沒有符號位),其反碼為原碼各bit取反)得到checksum。比如LSB是0xA5,其反碼為0x5A,則checksum為0x5A。
3.2 S-Record幀型別
前面說到一共有10種型別的幀,其中最重要也是數量最多的幀是資料幀,資料幀按地址長度可分為16bit、24bit、32bit地址長度資料幀,除了資料幀,還有其他種類幀,下面來統一介紹:
幀型別碼 | 幀型別 | 幀描述 | 幀舉例 |
---|---|---|---|
'0' | 檔案起始幀 | 用於表明S-Record檔案的開始 | 標明檔名為HDR的檔案起始幀S00600004844521B |
'1' | 資料幀x16地址 | 以16bit地址描述開始的最大255個位元組有效機器碼的資料幀 | 含14bytes機器碼從0x0038地址處開始的資料幀S111003848656C6C6F20776F726C642E0A0042 |
'2' | 資料幀x24地址 | 以24bit地址描述開始的最大255個位元組有效機器碼的資料幀 | 含4bytes機器碼從0x100000地址處開始的資料幀S2081000000400FA05E5 |
'3' | 資料幀x32地址 | 以32bit地址描述開始的最大255個位元組有效機器碼的資料幀 | 含4bytes機器碼從0x13000160地址處開始的資料幀S309130001600400FA057F |
'4' | N/A | 未定義 | N/A |
'5' | 資料幀總數幀x16 | 用16bit count記錄資料幀總幀數的總數幀 | 標明總資料幀為4幀的總數幀S5030004F8 |
'6' | 資料幀總數幀x24 | 用24bit count記錄資料幀總幀數的總數幀 | 標明總資料幀為80000幀的總數幀S604080000F3 |
'7' | 起始程式地址(PC)幀x32 | 含32bit起始PC的幀 | 標明起始PC為0x10000000的幀S70510000000EA |
'8' | 起始程式地址(PC)幀x24 | 含24bit起始PC的幀 | 標明起始PC為0x100000的幀S804100000EB |
'9' | 起始程式地址(PC)幀x16 | 含16bit起始PC的幀 | 標明起始PC為0x0000的幀S9030000FC |
3.3 解析S-Record檔案
在瞭解S-Record檔案幀資料格式之後,讓我們開始嘗試解析demo.s19檔案(僅擷取前後部分,與前面擷取的bin檔案內容對應著一起分析)
S00B000064656D6F2E73313944
S11300000020001041000000DB180000CB190000A4
S113001000000000000000000000000000000000DC
S1130020000000000000000000000000071A0000AB
S113003000000000000000001D1A00001F1A00004C
S113004072B60E480E498860002200230024002561
....
S1131A300123002413E00A68091D1A4202D04D460E
S1131A406D1E52191460121D001F0428FAD21500CD
S1131A50860701D51480AD1C184000D02C7008688E
S1131A60091D0028E7D1080070BC7047C1FFFFFFC3
S10F1A7008020000142000100000000018
S9030041BB
S-Record檔案第一幀是檔案起始幀,幀資料64656D6F2E733139對應ASCII為demo.s19,即該檔名。
第2-6幀均為16bit地址的資料幀,每幀包含16bytes機器碼資料,幀資料地址分別為0x0000, 0x0010, 0x0020, 0x0030, 0x0040,可見幀資料是連續的,並且5幀機器碼資料共80bytes與bin檔案前80bytes是一致的。
再來看最後6幀資料裡的前5個資料幀,除最後一幀資料只包含12bytes資料外,其餘資料幀均含有16bytes資料,5幀資料一共76bytes,幀資料地址從0x1A30 - 0x1A70。顯然這與bin檔案最後76bytes也是吻合的。
倒數第一幀是起始程式地址幀,其標明的程式起始PC是0x0041,這與bin檔案裡第二個32bit資料(起始PC)是一致的。
番外一、一些image檔案輔助小工具
SRecordizer:專用S19檔案編輯器,可根據修改自動更新checksum,詳見網頁 https://srecordizer.codeplex.com/
SRecord專案:各種image檔案格式互轉的小工具合集,詳見網頁 http://srecord.sourceforge.net/
至此,嵌入式開發裡的image檔案(.bin, .hex, .s19)檔案痞子衡便介紹完畢了,掌聲在哪裡~~~