Intel PACでは,PCIeやオンボード上のメモリなどへのアクセスがBlue bitとして提供されていて開発者はGreen bitのみの設計に注力して,アクセラレータであるAFU(Application Function Unit)を開発できるようになっています.とっても便利なのですが,その反面,想定される開発フローに乗っかれるまでは,何をどうしたものかという状態に陥ってしまいます.
まずは,hello_afuにレジスタを追加することで,独自回路をPACで動かすまでの手法を確認します.なお,本来の開発ではシミュレーションを有効に活用すべきですが,ここではシミュレータは使いません.
おおまかな開発の流れ
おおまかには次のような流れで開発をします.
- ベースとするデザイン(今回はhello_afu)をビルドする
- Quartusでビルドしたデザインを開いて,モジュール構成を確認する
- 変更箇所に手を入れる/モジュールを追加する
- 変更後のデザインをビルドする
まずはhello_afuを合成して構成を確認
hello_afuは,PAC上に用意されたレジスタにソフトウェアから読み書きする,というサンプルです.
ビルド手順どおり,一度ビルドします.
% source ~/data/inteldevstack/init_env.sh # パスはIntel Dev Stackインストール先による
% cd ${OPAE_PLATFORM_ROOT}/hw/samples/hello_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に変更するとモジュール構成が確認できます.
ここではafuモジュールが,処理本体に相当しています.
hello_afuにレジスタを追加する
実際にhello_afuにレジスタを追加する一連の流れです.
ターゲットコードを変更する
ターゲットコードは,Quartusのインスタンスツリーから開いても構いませんし,直接エディタで開いても構いません.
追加するレジスタを定義して,
logic [63:0] scratch_reg;
logic [63:0] scratch_reg2; // 追加
読み出しに相当する部分に追加したレジスタへの書き込み部分を追加.わかりやすいように,書かれた値を2倍して保存することにします.
always_ff @(posedge clk)
begin
if (reset)
begin
scratch_reg <= '0;
scratch_reg2 <= '0;
end
else
begin
// set the registers on MMIO write request
// these are user-defined AFU registers at offset 0x40.
if (cp2af_sRxPort.c0.mmioWrValid == 1)
begin
case (mmioHdr.address)
16'h0020: scratch_reg <= cp2af_sRxPort.c0.data[63:0];
16'h0022: scratch_reg2 <= cp2af_sRxPort.c0.data[63:0] * 2; // 追加
endcase
end
end
end
ここで,アドレスを2個インクリメントしているのは,32bit単位だから(だと思う).読みだしに相当する部分も修正します.
//
// Handle MMIO reads.
//
always_ff @(posedge clk)
begin
if (reset)
begin
af2cp_sTxPort.c1.hdr <= '0;
af2cp_sTxPort.c1.valid <= '0;
af2cp_sTxPort.c0.hdr <= '0;
af2cp_sTxPort.c0.valid <= '0;
af2cp_sTxPort.c2.hdr <= '0;
af2cp_sTxPort.c2.mmioRdValid <= '0;
end
else
begin
...略...
// Scratch Register. Return the last value written
// to this MMIO address.
16'h0020: af2cp_sTxPort.c2.data <= scratch_reg;
16'h0022: af2cp_sTxPort.c2.data <= scratch_reg2; // 追加
...略...
end
end
もう一度ビルドする
Quartusを開いている場合,ここで一度Elaborate&Synthesisを通しておくことでしょうもないミスを防ぐことができるでしょう.Elaborate&Synthesisが通ったら,ターミナルに戻って,
% ${OPAE_PLATFORM_ROOT}/bin/run.sh
と,ビルドスクリプトを実行します.
動作確認
ソフトウェア側も修正が必要です.追加したレジスタに読み書きできるように,
/* test */
res = fpgaWriteMMIO64(afc_handle, 0, 0x88, 0xa5a5a5a5a5a5a5a);
res = fpgaReadMMIO64(afc_handle, 0, 0x88, &data);
printf("[test] Reading Scratch Register (Byte Offset=%08x) = %08lx\n", 0x88, data);
res = fpgaReadMMIO64(afc_handle, 0, 0x80, &data);
printf("[test] Reading Scratch Register (Byte Offset=%08x) = %08lx\n", 0x80, data);
というようにソフトウェアのコード ${OPAE_PLATFORM_ROOT}/hw/samples/hello_afu/sw/hello_afu.c
を修正します.アドレスはバイト単位で指定します.コードの修正が終わったら,makeでソフトウェアはビルドできます.
% cd ${OPAE_PLATFORM_ROOT}/hw/samples/hello_afu/sw
% make
合成が終わったbitファイル(gbsファイル)をFPGAにアップロードして,実行します.
% fpgaconf ../build_synth/*.gbs
% ./hello_afu
実行すると,
[miyoshi@localhost sw]$ ./hello_afu
Running Test
AFU DFH REG = 1000010000000000
AFU ID LO = 9722d43375b61c66
AFU ID HI = 850adcc26ceb4b22
AFU NEXT = 00000000
AFU RESERVED = 00000000
Reading Scratch Register (Byte Offset=00000080) = 00000000
MMIO Write to Scratch Register (Byte Offset=00000080) = 123456789abcdef
Reading Scratch Register (Byte Offset=00000080) = 123456789abcdef
[test] Reading Scratch Register (Byte Offset=00000088) = 14b4b4b4b4b4b4b4
[test] Reading Scratch Register (Byte Offset=00000080) = 123456789abcdef
Setting Scratch Register (Byte Offset=00000080) = 00000000
Reading Scratch Register (Byte Offset=00000080) = 00000000
Done Running Test
のように
0x88
(PAC上では0x22
)に0xa5a5a5a5a5a5a5a
を書いて読むと,2倍された0x14b4b4b4b4b4b4b4
が読める0x88
にデータを書いても,0x80
には影響ない
と,正しく動作することが確認できました.