コスモリサーチの凄腕エンジニアたちが日々、難題と格闘した記録
Copyright Cosmo Research Corp.

Xilinx ZynqのSDカードイメージ(BOOT.bin)の構造

Vivado 2017.2でなぜかQSPIブートできない現象にハマった際のメモをここで供養する。
QSPIブートできないのはfsblのバグ(後述)で、おそらく2017.4では修正されているはず。

実際に参考にしたBOOT.binはxilinxが提供しているxilinx-zc702-v2017.2-final.bspというファイルのpre-buildに入っているもの。

BootROM

参考資料1(UG585)の6.3.2 BootROM ヘッダーに記載。以下、表6-5を抜粋。

ヘッダーアドレス 説明 道祖土コメント
0x000 - 0x01F Interrupt Table for Execution-in-Place
0x020 Width Detection 0xAA995566固定
0x024 Image Identification 'XLNX'固定
0x028 Encryption Status
0x02C FSBL/ユーザー定義
0x030 Source Offset
0x034 Length of Image
0x038 0
0x03C Start of Execution
0x040 Total Image Length
0x044 0 実際は1
0x048 Header Checksum
0x04C - 0x09F FSBL/ユーザー定義 (84バイト)
0x0A0 - 0x89F Register Initialization (2048バイト) ALL 0xFFFF0000
0x8A0 - 0x8BF FSBL/ユーザー定義 ALL 0xFFFFFFFF
0x8C0 - FSBLイメージまたはユーザーコード イメージヘッダー

以下でファイルの内容を見てみる。

od -tx4 -v -Ax --endian little BOOT.BIN | less

先頭からBootROMヘッダーが始まっている。

000000 eafffffe eafffffe eafffffe eafffffe
000010 eafffffe eafffffe eafffffe eafffffe
000020 aa995566 584c4e58 00000000 01010000
000030 00001700 00018008 00000000 00000000
000040 00018008 00000001 fc164530 00000000
000050 00000000 00000000 00000000 00000000
000060 00000000 00000000 00000000 00000000
000070 00000000 00000000 00000000 00000000
000080 00000000 00000000 00000000 00000000
000090 00000000 00000000 000008c0 00000c80

イメージヘッダーテーブル

参考資料2(UG821)の付録A: Bootgenの使用の表A-4を抜粋。

オフセット 名前 注記
0x0 バージョン 0x01020000
0x4 イメージヘッダー数
0x8 パーティションヘッダーのワードオフセット
0xC 最初のイメージヘッダーのワードオフセット
0x10 ヘッダー認証のワードオフセット
0x1C パディング 64バイト境界までALL 0xFF

0x000008A0からイメージヘッダーが始まると書いているが、実際のBOOT.binでは0x000008C0から始まっている。 BootROMヘッダーの説明(UG585)ではBootROMヘッダー自体のサイズが0x8C0と書かれているので誤記だと思う。

BootROMと同様に、odでBOOT.binを見てみる。

0008c0 01020000 00000003 00000320 00000240
0008d0 00000000 ffffffff ffffffff ffffffff
0008e0 ffffffff ffffffff ffffffff ffffffff
0008f0 ffffffff ffffffff ffffffff ffffffff
  • イメージヘッダー数: 3
  • パーティションヘッダーのワードオフセット: 0x320(バイトでは0xC80)
  • 最初のイメージヘッダーのワードオフセット: 0x240(バイトでは0x900)

イメージヘッダー

イメージヘッダーテーブルのあとにはBootgenの作ったイメージヘッダーが並ぶ。

参考資料2(UG821)の付録A: Bootgenの使用の表A-7を抜粋。

オフセット 名前
0x0 次のイメージヘッダーのワードオフセット
0x4 最初のパーティションヘッダーのワードオフセット
0x8 0固定
0xC 1固定(パーティション数)
0x10 ここからファイル名 + 0x00000000
0x10 + ファイル名分 + 0x4 64バイト境界までALL 0xFF

先程と同様にodの出力を見てみる。

000900 00000250 00000320 00000000 00000001
000910 7a796e71 5f667362 6c2e656c 66000000 # zynq_fsbl.elf
000920 00000000 ffffffff ffffffff ffffffff
000930 ffffffff ffffffff ffffffff ffffffff
000940 00000260 00000330 00000000 00000001
000950 646f776e 6c6f6164 2e626974 00000000 # download.bit
000960 00000000 ffffffff ffffffff ffffffff
000970 ffffffff ffffffff ffffffff ffffffff
000980 00000000 00000340 00000000 00000001
000990 752d626f 6f742e65 6c660000 00000000 # u-boot.elf

なので、このBOOT.binでは以下の位置にパーティションヘッダーがいる事がわかる。

  • zynq_fsbl.elf: 0x320(バイトで0xC80)
  • download.bit: 0x330(バイトで0xCC0)
  • u-boot.elf: 0x340(バイトで0xD00)

パーティションヘッダーテーブル

参考資料2(UG821)の付録A: Bootgenの使用の表A-5を抜粋。

オフセット 名前
0x0 Partition Data Word Length (Encrypted)
0x4 Extracted Data Word Length (Unencrypted)
0x8 Total Partition Word Length
0x0C Destination Load Address
0x10 Destination Execution Address
0x14 Data Word Offset in Image
0x18 Attribute Bits
0x1C Section Count
0x20 Checksum Word Offset
0x24 Image Header Word Offset
0x28 Authentication Certification Word Offset
0x2C unused (Must be 0x00000000)
0x30 unused (Must be 0x00000000)
0x34 unused (Must be 0x00000000)
0x38 unused (Must be 0x00000000)
0x3C Header Checksum

例によって、odでBOOT.binを見てみる。

000c80 00006002 00006002 00006002 00000000 # 0x00 (zynq_fsbl.elf)
000c90 00000000 000005c0 00000010 00000001 # 0x10 (zynq_fsbl.elf)
000ca0 00000000 00000240 00000000 00000000 # 0x20 (zynq_fsbl.elf)
000cb0 00000000 00000000 00000000 fffed7e8 # 0x30 (zynq_fsbl.elf)

000cc0 000f6ec0 000f6ec0 000f6ec0 00000000 # 0x00 (download.bit)
000cd0 00000000 000065d0 00000020 00000001 # 0x10 (download.bit)
000ce0 00000000 00000250 00000000 00000000 # 0x20 (download.bit)
000cf0 00000000 00000000 00000000 ffd14b7e # 0x30 (download.bit)

000d00 00019090 00019090 00019090 00400000 # 0x00 (u-boot.elf)
000d10 00400000 000fd490 00000010 00000001 # 0x10 (u-boot.elf)
000d20 00000000 00000260 00000000 00000000 # 0x20 (u-boot.elf)
000d30 00000000 00000000 00000000 ff6b774e # 0x30 (u-boot.elf)

特に変なところもなく、以下のことが分かる。

  • u-bootだけ0x0C/0x10のロード/実行アドレスが0x00400000
  • 0x18のAttributeは、bitファイルだけPL(0x00000020)、その他はPS(0x00000010)

(参考)QSPIブートできない現象のデバッグ

s25fl256s1というFlashメモリで起動しようとすると、以下の表示になる。(fsblでシンボルFSBL_DEBUG_INFOを有効にしてシリアル出力させる)

Xilinx First Stage Boot Loader
Release 2017.1  Jul  7 2017-16:40:46
Devcfg driver initialized
Silicon Version 3.1
Boot mode is QSPI
Single Flash Information
FlashID=0x1 0x2 0x19
SPANSION 256M Bits
QSPI is in single flash connection
QSPI Init Done
Flash Base Address: 0xFC000000
Reboot status register: 0x60400000
Multiboot Register: 0x0000C000
Image Start Address: 0x00000000
Partition Header Offset:0x00000880          <==== 0xC80のはず
Partition Count: 14
Invalid Partition Count
Partition Header Load Failed
FSBL Status = 0xA00E
...(以下Fallback bootの結果)

u-bootやlinuxでQSPIの中身を読んでみると正常に書き込めている。 fsblでは、polled transferをしているようなので、xqspips_flash_polled_example.c(サンプルデザイン)を実行してみる。 printf()爆弾を仕込んだxqspips_flash_polled_example.cで動作確認したところ、

#define FAST_READ_CMD       0x0B
#define DUAL_READ_CMD      0x3B
#define QUAD_READ_CMD      0x6B

のうち、QUAD_READ_CMDがNGであることがわかった。

fsbl内で同様の操作をしている箇所は、qspi.cのFlashRead()であるので、 リードコマンドになるであろう、WriteBuffer[COMMAND_OFFSET]をモニタしてみたところ、 0x04とかいう謎の値が入っていた。

これを以下のように、FAST_READ_CMDにハードコードすることで読み出しできるようになった。

diff -r e93b7f0ac8b0 xsdk/fsbl/src/qspi.c
--- a/xsdk/fsbl/src/qspi.c    Fri Jul 07 16:23:45 2017 +0900
+++ b/xsdk/fsbl/src/qspi.c    Mon Jul 10 09:11:51 2017 +0900
@@ -91,7 +91,9 @@
  * The following constants define the commands which may be sent to the FLASH
  * device.
  */
-#define QUAD_READ_CMD     0x6B
+#define FAST_READ_CMD      0x0B // s25fl256s1 OK
+#define DUAL_READ_CMD      0x3B // s25fl256s1 OK
+#define QUAD_READ_CMD      0x6B // s25fl256s1 NG
 #define READ_ID_CMD            0x9F
 
 #define WRITE_ENABLE_CMD   0x06
@@ -441,12 +443,8 @@
     * Setup the write command with the specified address and data for the
     * FLASH
     */
-  u32 LqspiCrReg;
-  u8  ReadCommand;
-
-  LqspiCrReg = XQspiPs_GetLqspiConfigReg(QspiInstancePtr);
-  ReadCommand = (u8) (LqspiCrReg & XQSPIPS_LQSPI_CR_INST_MASK);
-  WriteBuffer[COMMAND_OFFSET]   = ReadCommand;
+   /* XQspiPs_GetLqspiConfigReg()で得られる値がおかしい */
+   WriteBuffer[COMMAND_OFFSET]   = FAST_READ_CMD; // for s25fl256s1
    WriteBuffer[ADDRESS_1_OFFSET] = (u8)((Address & 0xFF0000) >> 16);
    WriteBuffer[ADDRESS_2_OFFSET] = (u8)((Address & 0xFF00) >> 8);
    WriteBuffer[ADDRESS_3_OFFSET] = (u8)(Address & 0xFF);