Intel PACのAFUを開発する その2

Pocket

hello_afuより少し複雑なサンプルとしてDMAを使ったサンプルに手をいれてみることにします.DMAを使ったサンプルはユーザロジックがQsysを使ってデザインされています.そこで,今回はQsys上に新しいコンポーネントを追加する開発フローを試してみます.

おおまかな開発の流れ

おおまかには次のような流れで開発をします.

  1. ベースとするデザイン(今回はdma_afu)をビルドする
  2. Quartusでビルドしたデザインを開いて,モジュール構成を確認する
  3. ユーザロジックのQsysファイルをQsysで開く
  4. Qsysで新しいコンポーネントを作成
  5. Qsysで作成したコンポーネントの中身を実装
  6. 変更後のデザインをビルドする

dma_afuを合成して構成を確認

dma_afuは,(1)ソフトウェアでホストPCからPACボード上のメモリにデータを転送,(2)DMAエンジンで転送されたデータを別のメモリにコピー,(3)コピーされたデータをホストPCから読み出す,というサンプルです.

ビルド手順どおり,一度ビルドします.

% source ~/data/inteldevstack/init_env.sh # パスはIntel Dev Stackインストール先による
% cd ${OPAE_PLATFORM_ROOT}/hw/samples/dma_afu
% afu_synth_setup --source hw/rtl/filelist.txt build_synth
% cd build_synth
% ${OPAE_PLATFORM_ROOT}/bin/run.sh

合成が終わると,Quartusで,build/dcp.qpfを開いてモジュール構成を確認することができます.Project Navigatorにdcp_topしか表示されていない時は(多分,そうだと思う),afu_fitをafu_synthに変更するとモジュール構成が確認できます.

ここではgreen_bs→platform_shim_ccip_std_afu→afu_inst→u0の下のdma_test_system.qsysが,処理本体に相当しています.

Qsysでdma_test_system.qsysファイルを開く

新しいモジュールを処理本体に追加するために,dma_test_system.qsysをQsysで開きます.

Qsysで新しいコンポーネントを作成する

Qsysで新しいコンポーネントを作る方法の一つは,ファイルメニューのNew Component…で開くコンポーネント作成ダイアログを使う方法です.ファイル作成ダイアログは次のような感じ.

このダイアログで,Avalon MM-Slave I/F,クロック,リセットを持つコンポーネントを作ります.

作成したインターフェースを持ったVerilogモジュールの雛形を作ることができます.

インターフェースをセット,ファイルを作ればコンポーネントの作成は完了.

新しいコンポーネントをQsysブロックにくみこむ

IPカタログに作成したコンポーネントがリストアップされるので,選択→ダブルクリックしてQsysファイルに組み込みます.今回は,ソフトウェアからのアクセスを受け付ける dma_test_system_mm_bridge_0 のAvalon MM-Masterに,作成したコンポーネントのAvalon MM-Slaveをぶら下げて,ソフトウェアから読み書きできるようにします.

すでに登録されているコンポーネントとアドレスで区別するために,適切なアドレスを設定する必要があります.

作成したコンポーネントの中身を実装する

作成した雛形ファイルを開いて,ロジックを実装します.たとえば,こんな感じにしてみました.

`timescale 1 ps / 1 ps
module miyo_test_component (
                input  wire [3:0] avalon_slave_1_address,   // avalon_slave.address
                input  wire        avalon_slave_1_read,      //             .read
                output wire [63:0] avalon_slave_1_readdata,  //             .readdata
                input  wire        avalon_slave_1_write,     //             .write
                input  wire [63:0] avalon_slave_1_writedata, //             .writedata
                input  wire        clock_sink_clk,           //   clock_sink.clk
                input  wire        reset_sink_reset          //   reset_sink.reset
        );

        // TODO: Auto-generated HDL template

        reg [63:0] d0, d1;
        reg [63:0] q;
   assign avalon_slave_1_readdata = q;

        always @(posedge clock_sink_clk)
        begin
                if (avalon_slave_1_write == 1'b1) begin
                        case(avalon_slave_1_address)
                        4'h0 : d0 <= avalon_slave_1_writedata * 2;
                        4'h2 : d1 <= avalon_slave_1_writedata * 3;
                        endcase
                end
        end

        always @(posedge clock_sink_clk)
        begin
                if (avalon_slave_1_read == 1'b1) begin
                        case(avalon_slave_1_address)
                        4'h0 : q <= d0;
                        4'h2 : q <= d1;
                        endcase
                end
        end

endmodule

もう一度ビルドする

QsysでGenerate,Quartusで一度Elaborate & Synthesisを通しておきましょう.Elaborate & Synthesisが通ったら,ターミナルに戻ってビルドします.が,ビルドする前に,新しく作成したコンポーネント用のファイルをビルドスクリプトに読んでもらう必要がありますので,ファイルセット定義を修正する必要があります.

% cd ${OPAE_PLATFORM_ROOT}/hw/samples/dma_afu

で作業フォルダを移動して,hw/rtl/filelist.txt を開きます.追加したコンポーネント名に応じたipファイルを,filelist.txtに追加します.たとえば,私の場合は, qsys/${OPAE_PLATFORM_FPGA_FAMILY}/ip/dma_test_system/dma_test_system_miyo_test_component_0.ip でした.これを,filelist.txtの末尾に追加しました.

dma_afu.json

# CCI-P avmm shim
QI:BBB_ccip_avmm/hw/par/ccip_avmm_addenda.qsf
SI:BBB_ccip_avmm/hw/sim/ccip_avmm_sim_addenda.txt

# MPF
+define+MPF_PLATFORM_DCP_PCIE=1
QI:BBB_cci_mpf/hw/par/qsf_cci_mpf_PAR_files.qsf
SI:BBB_cci_mpf/hw/sim/cci_mpf_sim_addenda.txt

...略...

qsys/${OPAE_PLATFORM_FPGA_FAMILY}/ip/msgdma_bbb/msgdma_bbb_pipe_stage_0.ip
qsys/${OPAE_PLATFORM_FPGA_FAMILY}/ip/msgdma_bbb/msgdma_bbb_pipe_stage_m.ip
qsys/${OPAE_PLATFORM_FPGA_FAMILY}/ip/msgdma_bbb/msgdma_bbb_magic_number_rom_0.ip
qsys/${OPAE_PLATFORM_FPGA_FAMILY}/ip/dma_test_system/dma_test_system_miyo_test_component_0.ip

ファイルリストを更新したら,

% afu_synth_setup --force --source hw/rtl/filelist.txt build_synth
% cd build_synth
% ${OPAE_PLATFORM_ROOT}/bin/run.sh

と,ビルドスクリプトを実行します.

動作確認

ソフトウェア側も修正が必要です.追加したレジスタに読み書きできるように,

        uint64_t data;
        res = fpgaWriteMMIO64(afc_h, 0, 0x80, 0x1212121212121212);
        res = fpgaReadMMIO64(afc_h, 0, 0x80, &data);
        printf("[test] Reading Scratch Register (Byte Offset=%08x) = %08lx\n", 0x80, data);

        res = fpgaWriteMMIO64(afc_h, 0, 0x90, 0x3434343434343434);
        res = fpgaReadMMIO64(afc_h, 0, 0x90, &data);
        printf("[test] Reading Scratch Register (Byte Offset=%08x) = %08lx\n", 0x90, data);

        res = fpgaReadMMIO64(afc_h, 0, 0x80, &data);
        printf("[test] Reading Scratch Register (Byte Offset=%08x) = %08lx\n", 0x80, data);

        res = fpgaReadMMIO64(afc_h, 0, 0x90, &data);
        printf("[test] Reading Scratch Register (Byte Offset=%08x) = %08lx\n", 0x90, data);

というようにソフトウェアのコード ${OPAE_PLATFORM_ROOT}/hw/samples/dma_afu/sw/dma_afu_test.c に追加したコンポーネントにアクセスするコードを追加します.アドレスはバイト単位で指定します.コードの修正が終わったら,makeでソフトウェアはビルドできます.

% cd ${OPAE_PLATFORM_ROOT}/hw/samples/dma_afu/sw
% make 

合成が終わったbitファイル(gbsファイル)をFPGAにアップロードして,実行します.

% fpgaconf ../build_synth/*.gbs
% ./dma_afu_test

実行すると,DMAテストの後で,

[test] Reading Scratch Register (Byte Offset=00000080) = 2424242424242424
[test] Reading Scratch Register (Byte Offset=00000090) = 9c9c9c9c9c9c9c9c
[test] Reading Scratch Register (Byte Offset=00000080) = 2424242424242424
[test] Reading Scratch Register (Byte Offset=00000090) = 9c9c9c9c9c9c9c9c

のように

  • 0x80(PAC上ではレジスタアドレス0)に0x1212121212121212を書いて読むと,2倍された 0x2424242424242424 が読める
  • 0x90(PAC上ではレジスタアドレス2)に0x3434343434343434を書いて読むと,3倍された 0x9c9c9c9c9c9c9c9c が読める

と,正しく動作することが確認できました.

関連情報