ZynqのベアメタルアプリケーションでSDカードの内容を読み込む

例えば画像処理回路でフィルタ係数をデータとして持っておき動的に変えたい場合など、
その係数データはDRAMに保存され、必要に応じてPLに読み出される構成になると思います。
しかし、この係数データはどのように用意すればよいのか、という問題に突き当たります。
開発中ならSDKのXMDからデータを書き込めばよいですが、最終的にはそのようにはできません。
ここでは、データをSDカードに持っておき、それを読みだしてDRAMに展開することを考えます。

SDカードからブートするとき、FSBLがFATでフォーマットされたSDカードから"BOOT.BIN"というファイルを読み出し、DRAMに展開します。
FSBLは、SDKでアプリケーションプロジェクトの一種として作成されることからもわかるとおり、
（たぶん）結局普通のベアメタルアプリケーションのようなものなので、
FSBLのコードからSDカードを読み出している部分を頂いてくれば、SDカードからの読み出しが可能になります。

# FSBLの準備
適当なプロジェクト（SDカードにアクセスできるPSがあればOKなはず）でSDKを開いて、
メニューのFile -> New -> Application Project からFSBLを作成します。
名前は適当にここでは`zynq_fsbl_0`とかにしました。
2つ目のページで`Zynq FSBL`を選んでFinish。

また、コードをいろいろいじるために使い捨てるFSBLプロジェクトをもうひとつ作っておきます。
`fsbl_test`とか。

# コード・リーディング
`fsbl_test`のコードをいじくってみます。

まず、`main.c`の`main()`関数を

```c
int main(void)
{
	printf("Hello, world!\r\n");
	return XST_SUCCESS;
}
```
に変えてみます。
これで実行すると、普通にコンソールに
`Hello, world!`が出力されます。FSBLのコードはベアメタルアプリとしても実行できるみたいです。

main()関数を元に戻し、中を見てみます。
今回はSDカードを読み込む処理を抜き出したいので、`BOOT.BIN`でファイル内検索すると、

```c

		/*
		 * SD initialization returns file open error or success
		 */
		Status = InitSD("BOOT.BIN");
		if (Status != XST_SUCCESS) {
			fsbl_printf(DEBUG_GENERAL,"SD_INIT_FAIL\r\n");
			OutputStatus(SD_INIT_FAIL);
			FsblFallback();
		}
		MoveImage = SDAccess;
```

というコードが見つかります。
`InitSD()`が怪しいですね。
SDKの関数ジャンプ機能でInitSD()の定義に飛んでみます。
`cd.c`に定義されていますね。
`f_open`, `f_seek`, `f_read`などなどそれっぽい関数が使われています。
結局、`sd.c`内の`InitSD()`と`SDAccess()`の処理をパクればSDカードからのファイル読み込みが可能です。

# SDカードから読んでみる
`fsbl_test`のコードを書き換えてSDから読むアプリケーションにしてみます。
まず、`main.c`以外のファイルを削除します。
次に`main.c`を以下のように書き換えます。

```c
#include "xparameters.h"
#include "xstatus.h"
#include "ff.h"

char *strcpy_rom(char *Dest, const char *Src)
{
	unsigned i;
	for (i=0; Src[i] != '\0'; ++i)
		Dest[i] = Src[i];
	Dest[i] = '\0';
	return Dest;
}

u32 loadFileFromSD(char filename[], u32 *dst)
{
    FIL fil;		/* File object */
    FATFS fatfs;
    char buffer[32];
    char *boot_file = buffer;

    // Copy from InitSD
	FRESULT rc;
	TCHAR *path = "0:/"; /* Logical drive number is 0 */

	/* Register volume work area, initialize device */
	rc = f_mount(&fatfs, path, 0);
	xil_printf("SD: rc= %.8x\n\r", rc);

	if (rc != FR_OK) {
		return XST_FAILURE;
	}

	strcpy_rom(buffer, filename);
	boot_file = (char *)buffer;

	rc = f_open(&fil, boot_file, FA_READ);
	if (rc) {
		xil_printf("SD: Unable to open file %s: %d\n", boot_file, rc);
		return XST_FAILURE;
	}

    // Copy from SDAccess
	UINT br;

	rc = f_lseek(&fil, 0);
	if (rc) {
		xil_printf("SD: Unable to seek to %x\n", 0);
		return XST_FAILURE;
	}

	rc = f_read(&fil, (void*)dst, fil.fsize, &br);

	if (rc) {
		xil_printf("*** ERROR: f_read returned %d\r\n", rc);
        return XST_FAILURE;
	}

    Xil_DCacheInvalidateRange(dst, fil.fsize);

	return XST_SUCCESS;
}

int main(void)
{
    printf("Loading test.dat\r\n");
    Status = loadFileFromSD("test.dat", ((volatile unsigned int *) 0x04000000));
    if ( Status != XST_SUCCESS ) {
        printf("Fail\r\n");
    }
    printf("loaded\r\n");

    return XST_SUCCESS;
}
```

SDカードに`test.dat`というファイル（適当に作ってください）を入れてZedBoardに挿し、このコードを実行すると、SDカード内の`test.dat`がDDRメモリの`0x04000000`番地に読み込まれます。
PLからの利用を想定し、`InitSD()`と`SDAccess()`からコピーしたコードに加えて、`Xil_DCacheInvalidateRange()`を呼んでいることに注意してください。
ホストPC(Linux)で`hexdump`コマンドで`test.dat`をダンプした結果と、SDKのXDMコンソールで`mrd 0x04000000 10`してメモリ内容をダンプした結果が一致すれば成功です。

# 他のプロジェクトでもこの関数を使う
上記コードの`loadFileFromSD()`関数（と`stycpy_rom()`）をコピーすれば他のプロジェクトでもSDからの読み込みを実現できます。
ただし、これらはFSBLと同時に作られたBSPプロジェクト内のファイルをインクルードおよびリンクしないといけないので、そこの設定だけ気をつけてください。

具体的には、以下の手順で設定します。
1. この関数を使いたいプロジェクトの上で右クリックし、コンテキストメニューから`Properties`を選択。
2. `C/C++ General` -> `Paths and Symbols`を選択
3. 右ペインの`Includes`タブで`Add...`を押し、`zynq_fsbl_0_bsp/ps7_cortexa9_0/include`を追加
4. さらに`Libraru Paths`タブで`Add...`を押し、`zynq_fsbl_0_bsp/ps7_cortexa9_0/lib`を追加
5. `C/C++ Build` -> `Settings`を選び、右ペインの`Libraries`を選択、`Libraries (-l)`に`xilffs`を追加