最新の fsbl で Linux が起動しない理由を調べる のバックアップ差分(No.3)
更新- 追加された行はこの色です。
- 削除された行はこの色です。
[[公開メモ]] * Petalinux 2016.4 の生成する fsbl が動作しない [#de4f661e] [[電気回路/zynq/Petalinux のビルド#h00d6c35]] で見たとおり、 何のメッセージも出ず固まってしまいます。。。 z-turn 付属の dvd にあった mys-xc7z020-trd というサンプルプロジェクト からビルドした fsbl ではうまく行きました。 以下、vivado 2016.4 に付属の xilinx SDK で fsbl プロジェクトを新規に作成して試してみます。 以下、vivado 2016.4 に付属の xilinx SDK で fsbl プロジェクトを新規に作成して試しました。 結果として、 - 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 に書き換えて試すと、、、~ やっぱりうんともすんとも言わないか。 これを 0xE0001000 に書き換える。 *** main.c の ps7_init() の直後でメッセージを表示 [#i34c57ef] 何も表示されない *** 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 は復活させておく *** ps7_init() [#a1a9eac6] *** SD カードが読めないらしい [#oe06a9fa] - init_data にある初期化手順を実行する形になっている - ps7GetSiliconVersion() により初期化手順を選択する 上記の変更によりメッセージが表示されるようになった。 曰く、 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 unsigned long *ps7_mio_init_data = ps7_mio_init_data_3_0; unsigned long *ps7_pll_init_data = ps7_pll_init_data_3_0; unsigned long *ps7_clock_init_data = ps7_clock_init_data_3_0; unsigned long *ps7_ddr_init_data = ps7_ddr_init_data_3_0; unsigned long *ps7_peripherals_init_data = ps7_peripherals_init_data_3_0; int ps7_init() { unsigned long si_ver = ps7GetSiliconVersion (); int ret; if (si_ver == PCW_SILICON_VERSION_1) { ps7_mio_init_data = ps7_mio_init_data_1_0; ps7_pll_init_data = ps7_pll_init_data_1_0; ps7_clock_init_data = ps7_clock_init_data_1_0; ps7_ddr_init_data = ps7_ddr_init_data_1_0; ps7_peripherals_init_data = ps7_peripherals_init_data_1_0; } else if (si_ver == PCW_SILICON_VERSION_2) { ps7_mio_init_data = ps7_mio_init_data_2_0; ps7_pll_init_data = ps7_pll_init_data_2_0; ps7_clock_init_data = ps7_clock_init_data_2_0; ps7_ddr_init_data = ps7_ddr_init_data_2_0; ps7_peripherals_init_data = ps7_peripherals_init_data_2_0; } else { ps7_mio_init_data = ps7_mio_init_data_3_0; ps7_pll_init_data = ps7_pll_init_data_3_0; ps7_clock_init_data = ps7_clock_init_data_3_0; ps7_ddr_init_data = ps7_ddr_init_data_3_0; ps7_peripherals_init_data = ps7_peripherals_init_data_3_0; 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; } ret = ps7_config (ps7_mio_init_data); if (ret != PS7_INIT_SUCCESS) return ret; ret = ps7_config (ps7_pll_init_data); if (ret != PS7_INIT_SUCCESS) return ret; ret = ps7_config (ps7_clock_init_data); if (ret != PS7_INIT_SUCCESS) return ret; ret = ps7_config (ps7_ddr_init_data); if (ret != PS7_INIT_SUCCESS) return ret; ret = ps7_config (ps7_peripherals_init_data); if (ret != PS7_INIT_SUCCESS) return ret; return PS7_INIT_SUCCESS; } シリコンバージョンはメモリの値を読むだけなので失敗しようがないし、 そもそもこのあたりのコードはうまく行く方も行かない方も共通。 f_mount は fsbl_bsp\ps7_cortexa9_0\include\ff.h で定義されていて、 LANG:c unsigned long ps7GetSiliconVersion () { // Read PS version from MCTRL register [31:28] unsigned long mask = 0xF0000000; unsigned long *addr = (unsigned long*) 0XF8007080; unsigned long ps_version = (*addr & mask) >> 28; return ps_version; } - FRESULT f_mount (BYTE, FATFS*); + FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); LANG:c int ps7_config(unsigned long * ps7_config_init) のように変更されている。 *** 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)*/ ) { unsigned long *ptr = ps7_config_init; FATFS *rfs; unsigned long opcode; // current instruction .. unsigned long args[16]; // no opcode has so many args ... int numargs; // number of arguments of this instruction int j; // general purpose index 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; volatile unsigned long *addr; // some variable to make code readable unsigned long val,mask; // some variable to make code readable 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 int finish = -1 ; // loop while this is negative ! int i = 0; // Timeout variable while( finish < 0 ) { numargs = ptr[0] & 0xF; opcode = ptr[0] >> 4; 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; } for( j = 0 ; j < numargs ; j ++ ) args[j] = ptr[j+1]; ptr += numargs + 1; switch ( opcode ) { case OPCODE_EXIT: finish = PS7_INIT_SUCCESS; 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; case OPCODE_CLEAR: addr = (unsigned long*) args[0]; *addr = 0; break; case OPCODE_WRITE: addr = (unsigned long*) args[0]; val = args[1]; *addr = val; break; case OPCODE_MASKWRITE: addr = (unsigned long*) args[0]; mask = args[1]; val = args[2]; *addr = ( val & mask ) | ( *addr & ~mask); break; case OPCODE_MASKPOLL: addr = (unsigned long*) args[0]; mask = args[1]; i = 0; while (!(*addr & mask)) { if (i == PS7_MASK_POLL_TIME) { finish = PS7_INIT_TIMEOUT; break; } i++; } break; case OPCODE_MASKDELAY: addr = (unsigned long*) args[0]; mask = args[1]; int delay = get_number_of_cycles_for_delay(mask); perf_reset_and_start_timer(); while ((*addr < delay)) { } break; default: finish = PS7_INIT_CORRUPT; break; } DivCnt = DivCnt << 1; } return finish; } if(ClkLoopCnt == 9) { /* * No valid divisor found for given frequency */ Status = XST_FAILURE; goto RETURN_PATH; } #define PS7_MASK_POLL_TIME 100000000 で、ほとんど同じコードと言える。 *** fsbl_bsp\ps7_cortexa9_0\libsrc\standalone_v?_?\src\xil_printf.c [#l276479b] こちらは、 - InstancePtr->Config.InputClockHz = 125000000 - XSDPS_CC_MAX_NUM_OF_DIV = 8 - SelFreq = 25000000 最終的には outbyte を呼び出す なので、4 回のループで抜けている。 *** fsbl_bsp\ps7_cortexa9_0\libsrc\standalone_v?_?\src\outbyte.c [#m4edd7e6] エラーとなっているのは新しいコードの XSdPs_CfgInitialize 内の バージョン間で変更なし Status = XSdPs_Change_ClkFreq(InstancePtr, XSDPS_CLK_400_KHZ); LANG:c void outbyte(char c) { XUartPs_SendByte(STDOUT_BASEADDRESS, c); } なのだけれど、実は XSdPs_CardInitialize には *** fsbl_bsp\ps7_cortexa9_0\libsrc\uartps_v?_?\src\xuartps.c [#kcf29b72] 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] * まとめ [#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); に変更。 ** (オプション)すべての debug メッセージを表示する [#h06838f8] fsbl/src/fsbl_debug.h #define DEBUG_GENERAL 0x00000001 の前に #define FSBL_DEBUG_INFO 1 を追加 * 質問・コメント [#z7ff73d4] #article_kcaptcha
Counter: 13452 (from 2010/06/03),
today: 3,
yesterday: 0