最新の 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

が表示されました。

projects.png

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

new-project.png

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

  • OS Platform は standalone にします

new-project2.png

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

zynq-fsbl.png

コードの比較

上記のようにして作った 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

かなりいろいろ変更されていて厳しい、が、どうやらキャストを増やしてたりする感じ。


Counter: 13435 (from 2010/06/03), today: 2, yesterday: 5