最新の fsbl で Linux が起動しない理由を調べる

(146d) 更新

公開メモ

Petalinux 2016.4 の生成する fsbl が動作しない

電気回路/zynq/Petalinux のビルド#h00d6c35 で見たとおり、 何のメッセージも出ず固まってしまいます。。。

z-turn 付属の dvd にあった mys-xc7z020-trd というサンプルプロジェクト からビルドした fsbl ではうまく行きました。

以下、vivado 2016.4 に付属の xilinx SDK で fsbl プロジェクトを新規に作成して試しました。

結果として、vivado でも新規にプロジェクトを作成すると Petalinux と同様に そのままでは動きませんでした。

そして、この問題は

  • UART のチャンネル違い
  • SD カード関連ライブラリのバグ

が原因であったことと、その回避方法が分かりました。>> [まとめ]に飛ぶ

fsbl2 プロジェクトの作成

Xilinx SDK 2016.4 で Workspace に mys-xc7z020-trd.sdk を指定して開くと、 Project Explorer には

  • design_1_wrapper_hw_platform_0
  • fsbl
  • fsbl_bsp

が表示されました。

projects.png

[File]-[New]-[Project...] から [Xilinx]-[Application Project] を選び [Next]

new-project.png

適当な名前を付けて [Next]

  • OS Platform は standalone にします

new-project2.png

Templates から [Zynq FSBL] を選択して [finish]

zynq-fsbl.png

コードの比較

上記のようにして作った fsbl2 と、z-turn 付属の fsbl のコードを比較しました。

Version & ChangeLog

z-turn 付属のコードは "11.00a kv 10/08/14"
2016.4 で作成のコードは "16.00a gan 08/02/16" でした。

その間の ChangeLog は、

* 12.00a ssc 12/11/14    839182 - FSBL -In the file sd.c, f_mount is called with
*                       two arguments but f_mount is expecting the 3 arguments
*                       from build 2015.1_1210_1, causing compilation error.
*                        Resolution: Arguments for f_mount in InitSD() are
*                        changed as per new signature.
* 13.00a ssc 04/10/15    846899 - FSBL -In the file pcap.c, to clear DMA done
*                       count, devcfg.INT_STS register is written to, which is
*                       not correct.
*                       Resolution: Corresponding fields in the devcfg.STATUS
*                       register are written to, for clearing DMA done count.
* 14.00a gan 01/13/16   869081 -(2016.1)FSBL -In qspi.c, FSBL picks the qspi
*                        read command from LQSPI_CFG register instead of hard
*                           coded read command (0x6B).
* 15.00a gan 07/21/16   953654 -(2016.3)FSBL -In pcap.c/pcap.h/main.c,
*                         Fabric Initialization sequence is modified to check
*                         the PL power before sequence starts and checking INIT_B
*                         reset status twice in case of failure.
* 16.00a gan 08/02/16   Fix for CR# 955897 -(2016.3)FSBL -
*                         In pcap.c, check pl power through MCTRL register
*                         for 3.0 and later versions of silicon.

重要そうなところを抜き出すと、

  • 13.00a : pcap.c で DMA done count をクリアする方法がおかしかったのを直した
  • 15.00a : pcap.c/pcap.h/main.c にてエラーチェックを厳しくした
  • 16.00a : pcap.c における PL Power チェック方法を 3.0 以上のシリコンバージョンに合わせて変更

となっています。

気づいたこと

  • 元々の fsbl プロジェクトは Debug モードでビルドされていた
  • 全体を通じて、
    fsbl_printf(DEBUG_INFO, ... で %08x だったところが %08lx に変更されている

具体的な変更点

2つのプロジェクトの差分を取って比べてみます。

fsbl/src/main.c

+ * 15.00a gan 07/21/16   Fix for CR# 953654 -(2016.3)FSBL -
+ * 			In pcap.c/pcap.h/main.c,
+ * 			Fabric Initialization sequence
+ * 			is modified to check the PL power
+ * 			before sequence starts and checking
+ * 			INIT_B reset status twice in case
+ * 			of failure.
  ...
  #ifdef STDOUT_BASEADDRESS
+ #ifdef XPAR_XUARTPS_0_BASEADDR
  #include "xuartps_hw.h"
+ #endif
  #endif
  ...

-			FabricInit();
+ 			Status = FabricInit();
+			if(Status != XST_SUCCESS){
+				ClearFSBLIn();
+				FsblHookFallback();
+			}
  ...

  void OutputStatus(u32 State)
  {
  #ifdef STDOUT_BASEADDRESS
+ #ifdef XPAR_XUARTPS_0_BASEADDR
  	u32 UartReg = 0;
+ #endif
  
  	fsbl_printf(DEBUG_GENERAL,"FSBL Status = 0x%.4lx\r\n", State);
  	/*
  	 * The TX buffer needs to be flushed out
  	 * If this is not done some of the prints will not appear on the
  	 * serial output
  	 */
+ #ifdef XPAR_XUARTPS_0_BASEADDR
  	UartReg = Xil_In32(STDOUT_BASEADDRESS + XUARTPS_SR_OFFSET);
  	while ((UartReg & XUARTPS_SR_TXEMPTY) != XUARTPS_SR_TXEMPTY) {
  		UartReg = Xil_In32(STDOUT_BASEADDRESS + XUARTPS_SR_OFFSET);
  	}
+ #endif
  #endif
  }

どうやら #ifdef XPAR_XUARTPS_0_BASEADDR があちこちに追加されているのだけれど、 今の場合は fsbl_bsp\ps7_cortexa9_0\include\xparameters.h で、

/* Canonical definitions for peripheral PS7_UART_0 */
#define XPAR_XUARTPS_0_DEVICE_ID XPAR_PS7_UART_0_DEVICE_ID
#define XPAR_XUARTPS_0_BASEADDR 0xE0000000
#define XPAR_XUARTPS_0_HIGHADDR 0xE0000FFF
#define XPAR_XUARTPS_0_UART_CLK_FREQ_HZ 100000000
#define XPAR_XUARTPS_0_HAS_MODEM 0

/* Canonical definitions for peripheral PS7_UART_1 */
#define XPAR_XUARTPS_1_DEVICE_ID XPAR_PS7_UART_1_DEVICE_ID
#define XPAR_XUARTPS_1_BASEADDR 0xE0001000
#define XPAR_XUARTPS_1_HIGHADDR 0xE0001FFF
#define XPAR_XUARTPS_1_UART_CLK_FREQ_HZ 100000000
#define XPAR_XUARTPS_1_HAS_MODEM 0

と定義されていて、事前に main.c で読み込まれているため、問題にはならなさそう。

FabricInit() の戻り値がチェックされるようになったのだけれど、 これはすでに FsblFallback の中なので、ここに到達する時点でエラーが生じているはず。 これもあまり意味が無い。

main.c の流れを追うと、

  • main() の中で、
    • ps7_init(); // PCW initialization for MIO,PLL,CLK and DDR
    • // この時点で fsbl_printf を利用可能
    • SlcrUnlock(); // Unlock SLCR for SLCR register write
    • Xil_DCacheFlush(); // Flush the Caches
    • Xil_DCacheDisable(); // Disable Data Cache
    • RegisterHandlers(); // Register the Exception handlers
    • fsbl_printf(DEBUG_GENERAL,"\n\rXilinx First Stage Boot Loader \n\r");

なので、ここまで順調に来ていれば初期メッセージが表示されるはず。

fsbl_bsp\ps7_cortexa9_0\include\xparameters.h

- #define STDIN_BASEADDRESS 0xE0001000
- #define STDOUT_BASEADDRESS 0xE0001000
+ #define STDIN_BASEADDRESS 0xE0000000
+ #define STDOUT_BASEADDRESS 0xE0000000

これ、ダメだ。

直前で見たとおり、
0xE0001000 は XUARTPS_1 のアドレス、
0xE0000000 は XUARTPS_0 のアドレス、
なので、

これを 0xE0001000 に書き換える。

fsbl/src/fsbl_debug.h

- #define	FSBL_DEBUG_INFO	1
  #define DEBUG_GENERAL	0x00000001    /* general debug  messages */
  #define DEBUG_INFO	0x00000002    /* More debug information */
  
  #if defined (FSBL_DEBUG_INFO)
  #define fsbl_dbg_current_types ((DEBUG_INFO) | (DEBUG_GENERAL))
  #elif defined (FSBL_DEBUG)
  #define fsbl_dbg_current_types (DEBUG_GENERAL)
  #else
  #define fsbl_dbg_current_types 0
  #endif
  
  #ifdef STDOUT_BASEADDRESS
  #define fsbl_printf(type,...) \
  		if (((type) & fsbl_dbg_current_types))  {xil_printf (__VA_ARGS__); }
  #else
  #define fsbl_printf(type, ...)
  #endif

さしあたってはこれが原因では無さそうだけれど、

#define FSBL_DEBUG_INFO 1 は復活させておく

SD カードが読めないらしい

上記の変更によりメッセージが表示されるようになった。

曰く、

LANG:console
Xilinx First Stage Boot Loader
Release 2016.4  Mar 22 2017-23:06:54
Devcfg driver initialized
Silicon Version 3.1
Boot mode is SD
SD: rc= 0
SD: Unable to open file BOOT.BIN: 3
                                   SD_INIT_FAIL
FSBL Status = 0xA009

This Boot Mode Doesn't Support Fallback
In FsblHookFallback function

SD カードが読めないらしい。

古いコードでうまく行っているときは、

LANG:console
Xilinx First Stage Boot Loader
Release 2014.4  Aug 11 2015-18:06:38
Devcfg driver initialized
Silicon Version 3.1
Boot mode is SD
SD: rc= 0
SD Init Done
Flash Base Address: 0xE0100000
Reboot status register: 0x60600000
Multiboot Register: 0x0000C000
Image Start Address: 0x00000000
Partition Header Offset:0x00000C80
Partition Count: 2
Partition Number: 1

となる。

コード上では "Boot mode is SD" の後は InitSD が呼ばれる。

fsbl/src/sd.c

LANG:c
  u32 InitSD(const char *filename)
  {
  
      FRESULT rc;
+     TCHAR *path = "0:/"; /* Logical drive number is 0 */
  
      /* Register volume work area, initialize device */
-     rc = f_mount(0, &fatfs);
+     rc = f_mount(&fatfs, path, 0);
      fsbl_printf(DEBUG_INFO,"SD: rc= %.8x\n\r", rc);
  
      if (rc != FR_OK) {
          return XST_FAILURE;
      }
  
      strcpy_rom(buffer, filename);
      boot_file = (char *)buffer;
      FlashReadBaseAddress = XPAR_PS7_SD_0_S_AXI_BASEADDR;
  
      rc = f_open(&fil, boot_file, FA_READ);
      if (rc) {
          fsbl_printf(DEBUG_GENERAL,"SD: Unable to open file %s: %d\n", boot_file, rc);
          return XST_FAILURE;
      }
  
      return XST_SUCCESS;
  
  }

f_mount は fsbl_bsp\ps7_cortexa9_0\include\ff.h で定義されていて、

LANG:c
- FRESULT f_mount (BYTE, FATFS*);	
+ FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt);

のように変更されている。

fsbl_bsp\ps7_cortexa9_0\libsrc\xilffs_v?_?\src\ff.c

f_mount は ff.c にある。

Changelog によれば差分は、

/ Jan 15,'11 R0.08b Fast seek feature is also applied to f_read() and f_write().
/                   f_lseek() reports required table size on creating CLMP.
/                   Extended format syntax of f_printf().
/                   Ignores duplicated directory separators in given path name.
/
/ Sep 06,'11 R0.09  f_mkfs() supports multiple partition to complete the multiple partition feature.
/                   Added f_fdisk().
/ Aug 27,'12 R0.09a Changed f_open() and f_opendir() reject null object pointer to avoid crash.
/                   Changed option name _FS_SHARE to _FS_LOCK.
/                   Fixed assertion failure due to OS/2 EA on FAT12/16 volume.
/ Jan 24,'13 R0.09b Added f_setlabel() and f_getlabel().
/
/ Oct 02,'13 R0.10  Added selection of character encoding on the file. (_STRF_ENCODE)
/                   Added f_closedir().
/                   Added forced full FAT scan for f_getfree(). (_FS_NOFSINFO)
/                   Added forced mount feature with changes of f_mount().
/                   Improved behavior of volume auto detection.
/                   Improved write throughput of f_puts() and f_printf().
/                   Changed argument of f_chdrive(), f_mkfs(), disk_read() and disk_write().
/                   Fixed f_write() can be truncated when the file size is close to 4GB.
/                   Fixed f_open(), f_mkdir() and f_setlabel() can return incorrect error code.
/ Jan 15,'14 R0.10a Added arbitrary strings as drive number in the path name. (_STR_VOLUME_ID)
/                   Added a configuration option of minimum sector size. (_MIN_SS)
/                   2nd argument of f_rename() can have a drive number and it will be ignored.
/                   Fixed f_mount() with forced mount fails when drive number is >= 1.
/                   Fixed f_close() invalidates the file object without volume lock.
/                   Fixed f_closedir() returns but the volume lock is left acquired.
/                   Fixed creation of an entry with LFN fails on too many SFN collisions.
/ May 19,'14 R0.10b Fixed a hard error in the disk I/O layer can collapse the directory entry.
/                   Fixed LFN entry is not deleted on delete/rename an object with lossy converted SFN.

とのこと。

FRESULT f_mount (
    BYTE vol,       /* Logical drive number to be mounted/unmounted */
    FATFS *fs       /* Pointer to new file system object (NULL for unmount)*/
)
{
    FATFS *rfs;

    if (vol >= _VOLUMES)            /* Check if the drive number is valid */
        return FR_INVALID_DRIVE;
    rfs = FatFs[vol];               /* Get current fs object */

だったところが、

FRESULT f_mount (
    FATFS* fs,          /* Pointer to the file system object (NULL:unmount)*/
    const TCHAR* path,  /* Logical drive number to be mounted/unmounted */
    BYTE opt            /* 0:Do not mount (delayed mount), 1:Mount immediately */
)
{
    FATFS *cfs;
    s32 vol;
    FRESULT res;
    const TCHAR *rp = path;

    vol = get_ldnumber(&rp);
    if (vol < 0) {return FR_INVALID_DRIVE;}
    cfs = FatFs[vol];                   /* Pointer to fs object */

になっている。

InitSD からの呼び出しが opt = 0 すなわち Do not mount (delayed mount) になっているのは問題ないんだろうか?

これを 1 にしたところ、

LANG:console
Xilinx First Stage Boot Loader
Release 2016.4  Mar 22 2017-23:54:30
Devcfg driver initialized
Silicon Version 3.1
Boot mode is SD
SD: rc= 3
SD_INIT_FAIL
FSBL Status = 0xA009

This Boot Mode Doesn't Support Fallback
In FsblHookFallback function

となって、どうやら悪化したため、元に戻した。

f_open のエラー番号

SD: Unable to open file BOOT.BIN: 3

の 3 が f_open のエラー番号で、ここでは FR_NOT_READY にあたる。

コードとしては、

	/* Get disk statics */
	stat = disk_initialize(pdrv);
	if (stat & STA_NOINIT) {
		return FR_NOT_READY;
	}

なので、disk_initialize が失敗している。

fsbl_bsp\ps7_cortexa9_0\libsrc\xilffs_v?_?\src\diskio.c

disk_initialize では、どんなエラーでも STA_NOINIT が立つので、 これだけでは何がいけないのかよく分からない。

xil_printf を入れまくってエラー箇所を探したところ、

XSdPs_CfgInitialize(&SdInstance[pdrv], SdConfig, SdConfig->BaseAddress);

がエラーコード 1 を吐いている。とはいえこれは XST_FAILURE なので、原因は・・・不明?

fsbl2_bsp\ps7_cortexa9_0\libsrc\sdps_v3_1\src\xsdps.c

どうやら XSdPs_Change_ClkFreq が エラーコード 1 を吐いている。

この関数は古いコードではここに無かった。

fsbl2_bsp\ps7_cortexa9_0\libsrc\sdps_v3_1\src\xsdps_options.c

XSdPs_Change_ClkFreq のエラーは No valid divisor found for given frequency なので、計算できないというのはかなりおかしい。

LANG:c
        /* Calculate divisor */
        DivCnt = 0x1U;
        while (DivCnt <= XSDPS_CC_MAX_DIV_CNT) {
            if (((InstancePtr->Config.InputClockHz) / DivCnt) <= SelFreq) {
                Divisor = DivCnt / 2U;
                break;
            }
            DivCnt = DivCnt << 1U;
        }

        if (DivCnt > XSDPS_CC_MAX_DIV_CNT) {
            /* No valid divisor found for given frequency */
            Status = XST_FAILURE;
            goto RETURN_PATH;
        }

の部分で、

  • InstancePtr->Config.InputClockHz = 125000000
  • XSDPS_CC_MAX_DIV_CNT = 256
  • SelFreq = 400000

となってエラーが出ていた。

古いコードでは

LANG:c
    DivCnt = 0x1;
    for(ClkLoopCnt = 0; ClkLoopCnt < XSDPS_CC_MAX_NUM_OF_DIV;
        ClkLoopCnt++) {
        if( ((InstancePtr->Config.InputClockHz)/DivCnt) <= SelFreq) {
            Divisor = DivCnt/2;
            Divisor = Divisor << XSDPS_CC_DIV_SHIFT;
            break;
        }
        DivCnt = DivCnt << 1;
    }

    if(ClkLoopCnt == 9) {
        /*
         * No valid divisor found for given frequency
         */
        Status = XST_FAILURE;
        goto RETURN_PATH;
    }

で、ほとんど同じコードと言える。

こちらは、

  • InstancePtr->Config.InputClockHz = 125000000
  • XSDPS_CC_MAX_NUM_OF_DIV = 8
  • SelFreq = 25000000

なので、4 回のループで抜けている。

エラーとなっているのは新しいコードの XSdPs_CfgInitialize 内の

	Status = XSdPs_Change_ClkFreq(InstancePtr, XSDPS_CLK_400_KHZ);

なのだけれど、実は XSdPs_CardInitialize には

		if (InstancePtr->HC_Version == XSDPS_HC_SPEC_V3)
			InstancePtr->BusSpeed = SD_CLK_19_MHZ;
		else
			InstancePtr->BusSpeed = SD_CLK_25_MHZ;
		Status = XSdPs_Change_ClkFreq(InstancePtr, InstancePtr->BusSpeed);

というコードがあって、こっちは古いコードと同様にうまく行くはず。

ここまで来て、ようやく以下の記事を見つけました。

https://forums.xilinx.com/t5/Embedded-Development-Tools/Zynq-FSBL-won-t-read-boot-bin-from-SD-card-with-CD-disabled/td-p/572037

曰く、

	Status = XSdPs_Change_ClkFreq(InstancePtr, XSDPS_CLK_400_KHZ);

	Status = XSdPs_Change_ClkFreq(InstancePtr, SD_CLK_25_MHZ);

に直しちゃえば?

とのこと。

その結果、起動した

後から分かったことには、ライブラリソースをいじる代わりに、

http://nahitafu.cocolog-nifty.com/nahitafu/2014/08/zynqfsbl-5f71.html

のように、PS ブロックの設定で SDIO のクロックを例えば 50MHz まで下げることでこのバグ(?)に対応することも可能なようでした。

まとめ

UART のチャンネルを変更

UART のチャンネルが 0 でなく 1 なのに対応して、
fsbl_bsp\ps7_cortexa9_0\include\xparameters.h にて、

#define STDIN_BASEADDRESS 0xE0001000
#define STDOUT_BASEADDRESS 0xE0001000

とする。

SD カード関連ライブラリのバグ(?)を除去

fsbl2_bsp\ps7_cortexa9_0\libsrc\sdps_v3_1\src\xsdps.c にて

	Status = XSdPs_Change_ClkFreq(InstancePtr, XSDPS_CLK_400_KHZ);

	Status = XSdPs_Change_ClkFreq(InstancePtr, SD_CLK_25_MHZ);

に変更。

あるいは、

http://nahitafu.cocolog-nifty.com/nahitafu/2014/08/zynqfsbl-5f71.html

のように、PS ブロックの設定で SDIO のクロックを例えば 50MHz まで下げる。

(オプション)すべての debug メッセージを表示する

fsbl/src/fsbl_debug.h

#define DEBUG_GENERAL	0x00000001

の前に

#define	FSBL_DEBUG_INFO	1

を追加

質問・コメント





添付ファイル: filezynq-fsbl.png 38件 [詳細] filenew-project2.png 49件 [詳細] filenew-project.png 43件 [詳細] fileprojects.png 47件 [詳細]

Counter: 569 (from 2010/06/03), today: 1, yesterday: 0