最新の fsbl で Linux が起動しない理由を調べる
Petalinux 2016.4 の生成する fsbl が動作しない†
電気回路/zynq/Petalinux のビルド#h00d6c35 で見たとおり、 何のメッセージも出ず固まってしまいます。。。
z-turn 付属の dvd にあった mys-xc7z020-trd というサンプルプロジェクト からビルドした fsbl ではうまく行きました。
以下、vivado 2016.4 に付属の xilinx SDK で fsbl プロジェクトを新規に作成して試しました。
結果として、vivado でも新規にプロジェクトを作成すると Petalinux と同様に そのままでは動きませんでした。
そして、この問題は
- UART のチャンネル違い
- SD カード関連ライブラリのバグ
が原因であったことと、その回避方法が分かりました。>> [まとめ]に飛ぶ
- Petalinux 2016.4 の生成する fsbl が動作しない
- fsbl2 プロジェクトの作成
- コードの比較
- Version & ChangeLog
- 気づいたこと
- 具体的な変更点
- fsbl/src/main.c
- fsbl_bsp\ps7_cortexa9_0\include\xparameters.h
- fsbl/src/fsbl_debug.h
- SD カードが読めないらしい
- fsbl/src/sd.c
- fsbl_bsp\ps7_cortexa9_0\libsrc\xilffs_v?_?\src\ff.c
- f_open のエラー番号
- fsbl_bsp\ps7_cortexa9_0\libsrc\xilffs_v?_?\src\diskio.c
- fsbl2_bsp\ps7_cortexa9_0\libsrc\sdps_v3_1\src\xsdps.c
- fsbl2_bsp\ps7_cortexa9_0\libsrc\sdps_v3_1\src\xsdps_options.c
- その結果、起動した
- まとめ
- 質問・コメント
fsbl2 プロジェクトの作成†
Xilinx SDK 2016.4 で Workspace に mys-xc7z020-trd.sdk を指定して開くと、 Project Explorer には
- design_1_wrapper_hw_platform_0
- fsbl
- fsbl_bsp
が表示されました。
[File]-[New]-[Project...] から [Xilinx]-[Application Project] を選び [Next]
適当な名前を付けて [Next]
- OS Platform は standalone にします
Templates から [Zynq FSBL] を選択して [finish]
コードの比較†
上記のようにして作った 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);
というコードがあって、こっちは古いコードと同様にうまく行くはず。
ここまで来て、ようやく以下の記事を見つけました。
曰く、
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
を追加
質問・コメント†
添付ファイル:



