zynq

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

More than 1 year has passed since last update.

例えば画像処理回路でフィルタ係数をデータとして持っておき動的に変えたい場合など、
その係数データは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.cmain()関数を

int main(void)
{
    printf("Hello, world!\r\n");
    return XST_SUCCESS;
}

に変えてみます。
これで実行すると、普通にコンソールに
Hello, world!が出力されます。FSBLのコードはベアメタルアプリとしても実行できるみたいです。

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

        /*
         * 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を以下のように書き換えます。

#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を追加