Help us understand the problem. What is going on with this article?

FPGA+SoC+Linux+Device Tree Overlay+FPGA Region(SDSoC対応編)

More than 1 year has passed since last update.

はじめに

次の記事で Linux Kernel 4.10 から新たにサポートされた FPGA Region を紹介しました。

また、次の記事で ZYBO、ZYBO-Z7、PYNQ で動作するブートイメージを提供している事を紹介しました。

この記事では、上記の環境の上で SDSoC で作った実行ファイルと FPGA のビットストリームを動かす方法について説明します。

背景

実は筆者は SDSoC に関しては詳しくありません。ディスク容量の関係で PC にインストールすらしていません。『FPGAの部屋』の @marsee101 さんが twitter で次のようにつぶやいたことから、ちょっと試してみた結果です。ちなみに SDSoC のサンプルも @marsee101 さんの提供です。

SDSoC に関しては @Vengineer さんの資料がとても参考になりました。あわせてご覧ください。

お断り

今回はあくまでも、ちょっとやってみました的なトライアルの記事です。本格的に動かしたらボロが出そうな感じがします。情報が追加されしだい、追記または削除します。ご了承ください。

Xilinx APF Accelerator driver

SDSoC で作った実行ファイルを動かすには Linux のデバイスドライバである Xilinx APF Accelerator driver(/dev/xlnk) が必要です。このデバイスドライバは、メインラインの Linux Kernel には含まれず、Xilinx が提供している https://github/xilinx/linux-xlnk に含まれています。まずは、このデバイスドライバを https://github.com/ikwzm/FPGA-SoC-Linux で動かすことにしましょう。幸いな事に Xilinx APF Accelerator driver は GNU General Public License version 2 で公開されているので、別の Linux Kernel で動かすことは問題ないはずです。

Xilinx APF Accelerator driver をビルドする際の基本的なフロー

ここでは Xilinx APF Accelerator driver をビルドする際の基本的なやり方について説明します。

ソースコードを得る

Xilinx APF Accelerator driver のソースコードは linux-xlnk の drivers/staging/apf にあります。linux-xlnk はいろいろなブランチやタグがあってどこからとってきてよいか判りませんが、とりあえず最新っぽい ebb848efc1cf6a6d63565e09888e56d9928965f6 を checkout しました。

shell$ mkdir xlnk-kernel-module
shell$ mkdir xlnk-kernel-module/build
shell$ git clone git://github.com/xilinx/linux-xlnk
shell$ cd linux-xlnk
shell$ git checkout ebb848efc1cf6a6d63565e09888e56d9928965f6
shell$ cp drivers/staging/apf/*  ../xlnk-kernel-module/build/
shell$ cd ../xlnk-kernel-module/build

arch_setup_dma_ops.c を追加する

前節で得たソースコードをコンパイルしようとすると、arch_setup_dma_ops() が未定義でコンパイルに失敗します。この関数はカーネル内で定義されている関数ですが、EXPORT_SYMBOL されていないため、カーネルモジュールからは呼び出せません。幸い、この関数の機能制限版である of_dma_configure() が EXPORT_SYMBOL されているのでこれで代用します。具体的には次のような arch_setup_dma_ops.c を一緒にコンパイルするようにします。

arch_setup_dma_ops.c
#include <linux/device.h>
#include <linux/types.h>
#include <linux/of.h>
#include <linux/of_device.h>

void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size,
                        const struct iommu_ops *iommu, bool coherent)
{
        of_dma_configure(dev, NULL);
}

Makefile を差し替える

カーネルモジュールをコンパイルするように Makefile を次の用に書き換えます。

Makefile
HOST_ARCH       ?= $(shell uname -m | sed -e s/arm.*/arm/ -e s/aarch64.*/arm64/)
ARCH            ?= $(shell uname -m | sed -e s/arm.*/arm/ -e s/aarch64.*/arm64/)
KERNEL_SRC_DIR  ?= /lib/modules/$(shell uname -r)/build

ifeq ($(ARCH), arm)
 ifneq ($(HOST_ARCH), arm)
   CROSS_COMPILE  ?= arm-linux-gnueabihf-
 endif
endif
ifeq ($(ARCH), arm64)
 ifneq ($(HOST_ARCH), arm64)
   CROSS_COMPILE  ?= aarch64-linux-gnu-
 endif
endif

obj-m := xlnk-apf.o xlnk-eng.o xlnk-dma.o
xlnk-apf-objs = xlnk.o xlnk-config.o 
xlnk-dma-objs = xilinx-dma-apf.o arch_setup_dma_ops.o

ccflags-y += -DCONFIG_XILINX_APF=m
ccflags-y += -DCONFIG_XILINX_DMA_APF=m

all:
    make -C $(KERNEL_SRC_DIR) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) M=$(PWD) modules

clean:
    make -C $(KERNEL_SRC_DIR) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) M=$(PWD) clean

この Makefile を使って、ARCH変数 や KERNEL_SRC_DIR変数を適切に設定すればコンパイルできました。

https://github.com/ikwzm/xlnk-kernel-module

前節で説明したやり方をいちいち実行するのは面倒なので、次の URL にリポジトリを用意しました。このリポジトリには、Xilinx APF Accelerator driver のソースコード一式とビルドとインストールするための Makefile 、Debian パッケージを作るためのスクリプト、linux kernel 4.14.21 用にコンパイル済みの Debian パッケージが含まれています。

ダウンロード

shell$ git clone git://github.com/ikwzm/xlnk-kernel-module
shell$ cd xlnk-kernel-module

Checkout 4.14.21-armv7-fpga

shell$ git checkout 4.14.21-armv7-fpga

ターゲット上でのセルフビルド

ターゲット上でのセルフビルドするためには、ターゲットとなるシステムに Linux Kernel のカーネルモジュール/デバイスドライバをビルドできる環境が必要です。具体的には、コンパイラや Linux Kernel のヘッダーファイルがインストールされている必要があります。Linux Kernel のヘッダーファイルのインストールに関しては、次の記事も参考にしてください。

ビルド

shell$ make
cd ./build ; make ARCH=arm KERNEL_SRC_DIR=/lib/modules/4.14.21-armv7-fpga/build all   ; cd /root/work/xlnk-kernel-module
make[1]: Entering directory '/root/work/xlnk-kernel-module/build'
make -C /lib/modules/4.14.21-armv7-fpga/build ARCH=arm CROSS_COMPILE= M=/root/work/xlnk-kernel-module/build modules
make[2]: Entering directory '/usr/src/linux-headers-4.14.21-armv7-fpga'
  CC [M]  /root/work/xlnk-kernel-module/build/xlnk.o
  CC [M]  /root/work/xlnk-kernel-module/build/xlnk-config.o
  LD [M]  /root/work/xlnk-kernel-module/build/xlnk-apf.o
  CC [M]  /root/work/xlnk-kernel-module/build/xlnk-eng.o
  CC [M]  /root/work/xlnk-kernel-module/build/xilinx-dma-apf.o
  CC [M]  /root/work/xlnk-kernel-module/build/arch_setup_dma_ops.o
  LD [M]  /root/work/xlnk-kernel-module/build/xlnk-dma.o
  Building modules, stage 2.
  MODPOST 3 modules
  CC      /root/work/xlnk-kernel-module/build/xlnk-apf.mod.o
  LD [M]  /root/work/xlnk-kernel-module/build/xlnk-apf.ko
  CC      /root/work/xlnk-kernel-module/build/xlnk-dma.mod.o
  LD [M]  /root/work/xlnk-kernel-module/build/xlnk-dma.ko
  CC      /root/work/xlnk-kernel-module/build/xlnk-eng.mod.o
  LD [M]  /root/work/xlnk-kernel-module/build/xlnk-eng.ko
make[2]: Leaving directory '/usr/src/linux-headers-4.14.21-armv7-fpga'
make[1]: Leaving directory '/root/work/xlnk-kernel-module/build'

インストール

shell$ sudo make install
cd ./build ; make ARCH=arm KERNEL_SRC_DIR=/lib/modules/4.14.21-armv7-fpga/build all   ; cd /root/work/xlnk-kernel-module
make[1]: Entering directory '/root/work/xlnk-kernel-module/build'
make -C /lib/modules/4.14.21-armv7-fpga/build ARCH=arm CROSS_COMPILE= M=/root/work/xlnk-kernel-module/build modules
make[2]: Entering directory '/usr/src/linux-headers-4.14.21-armv7-fpga'
  Building modules, stage 2.
  MODPOST 3 modules
make[2]: Leaving directory '/usr/src/linux-headers-4.14.21-armv7-fpga'
make[1]: Leaving directory '/root/work/xlnk-kernel-module/build'
install -d /lib/modules/4.14.21-armv7-fpga/xilinx
for ko in build/xlnk-apf.ko build/xlnk-dma.ko build/xlnk-eng.ko; do install -m 0644 $ko /lib/modules/4.14.21-armv7-fpga/xilinx ; done
install -d /etc/systemd/system
install -m 0644 systemd/xilinx-apf-driver.service /etc/systemd/system/
shell$ sudo depmod
shell$ sudo systemctl start  xilinx-apf-driver.service
shell$ sudo systemctl enable xilinx-apf-driver.service
Created symlink /etc/systemd/system/multi-user.target.wants/xilinx-apf-driver.service → /etc/systemd/system/xilinx-apf-driver.service.

dpkg によるインストール/アンインストール

checkout した https://github.com/ikwzm/xlnk-kernel-module の 4.14.21-armv7-fpgaには、linux kernel 4.14.21 用にコンパイル済みの Debian パッケージがあります。このパッケージを使ってインストールする場合は、コンパイラや Linux Kernel のヘッダーファイルは必要ありません。

dpkg によるインストール

shell$ dpkg -i xlnk-kernel-module-4.14.21-armv7-fpga_0.0.1-1_arm.deb
(Reading database ... 94083 files and directories currently installed.)
Preparing to unpack xlnk-kernel-module-4.14.21-armv7-fpga_0.0.1-1_armhf.deb ...
Unpacking xlnk-kernel-module-4.14.21-armv7-fpga (0.0.1-1) over (0.0.1-1) ...
Setting up xlnk-kernel-module-4.14.21-armv7-fpga (0.0.1-1) ...
Created symlink /etc/systemd/system/multi-user.target.wants/xilinx-apf-driver.service → /etc/systemd/system/xilinx-apf-driver.service.

shell$ lsmod
Module                  Size  Used by
xlnk_apf              135168  0
xlnk_dma               20480  1 xlnk_apf
xlnk_eng               16384  0

shell$ systemctl status xilinx-apf-driver.service
● xilinx-apf-driver.service - Xilinx APF Driver Service.
   Loaded: loaded (/etc/systemd/system/xilinx-apf-driver.service; enabled; vendo
   Active: active (exited) since Thu 2018-03-29 01:11:17 JST; 45s ago
  Process: 4212 ExecStart=/sbin/modprobe xlnk-apf (code=exited, status=0/SUCCESS
  Process: 4208 ExecStart=/sbin/modprobe xlnk-dma (code=exited, status=0/SUCCESS
  Process: 4204 ExecStart=/sbin/modprobe xlnk-eng (code=exited, status=0/SUCCESS
 Main PID: 4212 (code=exited, status=0/SUCCESS)

Mar 29 01:11:17 debian-fpga systemd[1]: Starting Xilinx APF Driver Service....
Mar 29 01:11:17 debian-fpga systemd[1]: Started Xilinx APF Driver Service..

dpkg によるアンインストール

アンインストールする際は dpkg -r を使います。

shell$ dpkg -r xlnk-kernel-module-4.14.21-armv7-fpga
(Reading database ... 94083 files and directories currently installed.)
Removing xlnk-kernel-module-4.14.21-armv7-fpga (0.0.1-1) ...
Removed /etc/systemd/system/multi-user.target.wants/xilinx-apf-driver.service.

SDSoC のサンプルを動かしてみる

必要なファイル

SDSoC でビルドすると、Debug/sd_card ディレクトリに次のようなファイルが出来ます。

  • Debug/sd_card/
    • BOOT.BIN
    • README.txt
    • _sds/
      • _p0_.bin
    • image.ub
    • application.elf

BOOT.BINfsbl.elfu-boot.elf が入っています。image.ub は Linux Kernel と Device Tree と RootFS が入っています。今回はこの BOOT.BINimage.ub は使いません。

_sds/_p0_.bin が FPGA のビットストリームファイル、application.elf(application には作ったアプリケーションの名前が入る) が実行ファイルです。今回必要なのはこの二つのファイルです。

PL(Programmable Logic)部のコンフィギュレーション

_sds/_p0.bin が FPGA のビットストリームファイルです。これを Device Tree Overlay と FPGA Region を使って PL に書き込みます。

具体的には、まず次のような Device Tree ファイルを用意します。

_p0_.dts
/dts-v1/;
/ {
        fragment@0 {
                target-path = "/amba/fpga-region0";
                #address-cells = <0x1>;
                #size-cells = <0x1>;

                __overlay__ {
                        #address-cells = <0x1>;
                        #size-cells = <0x1>;

                        firmware-name = "_p0_.bin";

                };
        } ;
} ;

次に _sds/_p0_.bin/lib/firmware にコピーします。

shell$ sudo cp _sds/_p0_.bin /lib/firmware

最後に dtbocfg.rb を使って _p0_.dts を Device Tree に追加(Overlay)します。

shell$ sudo dtbocfg.rb --install _p0_ --dts _p0_.dts
/config/device-tree/overlays/_p0_/dtbo: Warning (unit_address_vs_reg): Node /fragment@0 has a unit name, but no reg property
[ 6040.868556] fpga_manager fpga0: writing _p0_.bin to Xilinx Zynq FPGA Manager

fpga_manager が writing p0.bin to Xilinx Zynq FPGA Manager というメッセージを出して書き込んだことを示します。

Xilinx APF Accelerator driver を有効にする

カーネルモジュール(xlnk-apf.ko、xlnk-dma.ko、xlnk-eng.ko)をインストールしただけでは Xilinx APF Accelerator driver は有効になりません。有効にするためには次のような Device Tree ファイルを用意して Device Tree に追加(Overlay)します。

xlnk_dts
/dts-v1/; /plugin/;
/ {
        fragment@0 {
                target-path = "/";
                __overlay__ {
                        xlnk {
                                compatible = "xlnx,xlnk-1.0";
                                clock-names = "xclk0", "xclk1", "xclk2", "xclk3";
                                clocks = <&clkc 0xf &clkc 0x10 &clkc 0x11 &clkc 0x12>;
                        };
                };
        };
};
shell$ sudo dtbocfg.rb --install xlnk --dts xlnk.dts
/config/device-tree/overlays/xlnk/dtbo: Warning (unit_address_vs_reg): Node /fragment@0 has a unit name, but no reg property
/config/device-tree/overlays/xlnk/dtbo: Warning (clocks_property): Property 'clocks', cell 1 is not a phandle reference in /fragment@0/__overlay__/xlnk
/config/device-tree/overlays/xlnk/dtbo: Warning (clocks_property): Could not get phandle node for /fragment@0/__overlay__/xlnk:clocks(cell 1)
[ 6052.415866] xlnk xlnk: Major 242
[ 6052.422045] xlnk xlnk: xlnk driver loaded
[ 6052.426222] xlnk xlnk: xlnk_pdev is not null

ちょっとクロックまわりで警告が出ていますが、とりあえず無視します。と言うのも、ソースコードを見る限りデバイスドライバが clocks プロパティを参照しているとは思えないからです。

アプリケーションを実行してみる(1)

今回は @marsee101 さんの提供による multi_apuinit.elf を実行した例を示します。もともとのソースコードは次のようなものでした。

multi_apuint.cpp
// multi_apuint.cpp

#include <ap_int.h>

void multi_apuint(ap_uint<8> multi_in0, ap_uint<8> multi_in1,
        ap_uint<16> *multi_out){
    *multi_out = multi_in0 * multi_in1;
}
multi_apuint_tb.cpp
// multi_apuint_tb.cpp

#include <stdio.h>
#include <string.h>
#include <ap_int.h>

int multi_apuint(ap_uint<8> multi_in0, ap_uint<8> multi_in1, ap_uint<16> *multi_out);

int main(){
    using namespace std;

    ap_uint<8> multi_in0;
    ap_uint<8> multi_in1;
    ap_uint<16> multi_out;

    multi_in0 = 10;
    multi_in1 = 10;
    multi_apuint(multi_in0, multi_in1, &multi_out);
    cout << "multi_out = " << multi_out << endl;

    if (multi_out == (multi_in0*multi_in1))
        return(0);
    else
        return(1);
}

これを SDSoC でビルドして次のようなファイル達を作ります。

  • Debug/sd_card/
    • BOOT.BIN
    • README.txt
    • _sds/
      • _p0_.bin
    • image.ub
    • multi_apuinit.elf

前節で示したように _sds/_p0_.bin を PL 部にコンフィギュレーションして、Xilinx APF Accelerator driver を有功にしてから、実行ファイルを起動してみました。

shell$ cd Debug/sd_card/
shell$ sudo ./multi_apuint.elf
multi_out = 100

なんか動いているっぽいです。ホントかな~。なんか AXI Master 使うようなやつだと失敗しそうな予感。。。

アプリケーションを実行してみる(2)

前節で「AXI Master 使うような奴だと失敗しそうな予感」と書いたところ、@marsee101 さんが DMA を使うアプリケーションを提供してくださいました。

DMA_pow.cpp
#pragma SDS data zero_copy(in[0:10])
#pragma SDS data zero_copy(out[0:10])

int DMA_pow2(int *in, int *out){

    int i;

    for (i=0; i<10; i++){
        int temp = *in++;
        *out++ =temp * temp;
    }

    return(0);
}
DMA_pow_tb.cpp
#include <stdio.h>
#include <stdlib.h>
#include "sds_lib.h"

int DMA_pow2(int *in, int *out);

int main(){
    int *data;
    int *result;
    int i;

    if((data=(int *)sds_alloc(sizeof(int)*10)) == NULL){
        fprintf(stderr, "Can't allocate data[10]\n");
        exit(1);
    }

    if((result=(int *)sds_alloc(sizeof(int)*10)) == NULL){
        fprintf(stderr, "Can't allocate result[10]\n");
        exit(1);
    }

    for(int i=0; i<10; i++){
        data[i] = i;
    }

    DMA_pow2(data, result);

    for(i=0; i<10; i++){
        printf("data[%d] = %d, result[%d] = %d\n", i, data[i], i, result[i]);
    }
}
shell$ cd DMA_Pow/Debug/sd_card/
shell$ sudo ./DMA_Pow.elf
[  328.444034] xlnk_eng_probe ...
[  328.447026] uio name xilinx-xlnk-eng.0
[  328.450775] xilinx-xlnk-eng xilinx-xlnk-eng.0: physical base : 0x83c00000
[  328.457580] xilinx-xlnk-eng xilinx-xlnk-eng.0: register range : 0x10000
[  328.464171] xilinx-xlnk-eng xilinx-xlnk-eng.0: base remapped to: 0xf0e40000
[  328.471404] xilinx-xlnk-eng xilinx-xlnk-eng.0: xilinx-xlnk-eng uio registered
[  328.481808] xilinx-xlnk-eng xilinx-xlnk-eng.0: xilinx-xlnk-eng uio unregistered
data[0] = 0, result[0] = 0
data[1] = 1, result[1] = 1
data[2] = 2, result[2] = 4
data[3] = 3, result[3] = 9
data[4] = 4, result[4] = 16
data[5] = 5, result[5] = 25
data[6] = 6, result[6] = 36
data[7] = 7, result[7] = 49
data[8] = 8, result[8] = 64
data[9] = 9, result[9] = 81

なんかマジで動いているっぽいです。 @marsee101 さん、感謝。

参考

ikwzm
元へっぽこ電子回路エンジニア。現在隠居中。どちらかというとVHDL派。最近はFPGA+SoC でいろいろやってます。github でもいろいろと公開してます。 https://github.com/ikwzm
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away