最新の fsbl で Linux が起動しない理由を調べる の履歴(No.2)
更新Petalinux 2016.4 の生成する fsbl が動作しない†
電気回路/zynq/Petalinux のビルド#h00d6c35 で見たとおり、 何のメッセージも出ず固まってしまいます。。。
z-turn 付属の dvd にあった mys-xc7z020-trd というサンプルプロジェクト からビルドした fsbl ではうまく行きました。
以下、vivado 2016.4 に付属の xilinx SDK で fsbl プロジェクトを新規に作成して試してみます。
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 に書き換えて試すと、、、
やっぱりうんともすんとも言わないか。
main.c の ps7_init() の直後でメッセージを表示†
何も表示されない
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 は復活させておく
ps7_init()†
- init_data にある初期化手順を実行する形になっている
- ps7GetSiliconVersion() により初期化手順を選択する
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; } 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; }
シリコンバージョンはメモリの値を読むだけなので失敗しようがないし、 そもそもこのあたりのコードはうまく行く方も行かない方も共通。
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; }
LANG:c int ps7_config(unsigned long * ps7_config_init) { unsigned long *ptr = ps7_config_init; 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 volatile unsigned long *addr; // some variable to make code readable unsigned long val,mask; // some variable to make code readable int finish = -1 ; // loop while this is negative ! int i = 0; // Timeout variable while( finish < 0 ) { numargs = ptr[0] & 0xF; opcode = ptr[0] >> 4; for( j = 0 ; j < numargs ; j ++ ) args[j] = ptr[j+1]; ptr += numargs + 1; switch ( opcode ) { case OPCODE_EXIT: finish = PS7_INIT_SUCCESS; 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; } } return finish; }
#define PS7_MASK_POLL_TIME 100000000
fsbl_bsp\ps7_cortexa9_0\libsrc\standalone_v?_?\src\xil_printf.c†
最終的には outbyte を呼び出す
fsbl_bsp\ps7_cortexa9_0\libsrc\standalone_v?_?\src\outbyte.c†
バージョン間で変更なし
LANG:c void outbyte(char c) { XUartPs_SendByte(STDOUT_BASEADDRESS, c); }
fsbl_bsp\ps7_cortexa9_0\libsrc\uartps_v?_?\src\xuartps.c†
かなりいろいろ変更されていて厳しい、が、どうやらキャストを増やしてたりする感じ。