最新の fsbl で Linux が起動しない理由を調べる のバックアップソース(No.7)

更新

[[公開メモ]]

* Petalinux 2016.4 の生成する fsbl が動作しない [#de4f661e]

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

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

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

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

そして、この問題は

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

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

* fsbl2 プロジェクトの作成 [#uf03a5ca]

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

- design_1_wrapper_hw_platform_0
- fsbl
- fsbl_bsp

が表示されました。

&ref(projects.png,,66%);

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

&ref(new-project.png,,66%);

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

- OS Platform は standalone にします

&ref(new-project2.png,,66%);

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

&ref(zynq-fsbl.png,,66%);

* コードの比較 [#w4ba4b71]

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

** Version & ChangeLog [#zb0103bc]

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 以上のシリコンバージョンに合わせて変更

となっています。

** 気づいたこと [#g44cec3a]

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

** 具体的な変更点 [#ie713420]

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

*** fsbl/src/main.c [#yf4185b6]

 + * 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 [#va5b0042]

 - #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 [#l2aa1566]

 - #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 カードが読めないらしい [#oe06a9fa]

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

曰く、

 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 [#w85c4e43]

 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 [#l1ec7a8e]

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 のエラー番号 [#k236af09]

 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 [#d7aee870]

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 [#rd174c31]

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

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

*** fsbl2_bsp\ps7_cortexa9_0\libsrc\sdps_v3_1\src\xsdps_options.c [#xbae5227]

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);

に直しちゃえば?

とのこと。

** その結果、起動した [#mbd375e8]

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

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

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

* まとめ [#b01a5ab2]

** UART のチャンネルを変更 [#f4325941]

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

 #define STDIN_BASEADDRESS 0xE0001000
 #define STDOUT_BASEADDRESS 0xE0001000

とする。

** SD カード関連ライブラリのバグ(?)を除去 [#k5175fe8]

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 メッセージを表示する [#h06838f8]

fsbl/src/fsbl_debug.h

 #define DEBUG_GENERAL	0x00000001

の前に

 #define	FSBL_DEBUG_INFO	1

を追加

* 質問・コメント [#z7ff73d4]

#article_kcaptcha


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