最新の fsbl で Linux が起動しない理由を調べる の変更点
更新- 追加された行はこの色です。
- 削除された行はこの色です。
- 電気回路/zynq/最新の fsbl で Linux が起動しない理由を調べる へ行く。
- 電気回路/zynq/最新の fsbl で Linux が起動しない理由を調べる の差分を削除
[[公開メモ]]
* 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]]
#contents
* 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: 14540 (from 2010/06/03),
today: 2,
yesterday: 6