新千葉 ガーベージ・コレクション

FPGA マガジンやインターフェースで書けなかったこと等をちょぼちょぼ書いてます。@ryos36

hls::stream + axiu + SDSoC の実験

f:id:ryos36:20170106155608p:plain

SDSoC (2016.2) のサンプルで pacektize して minmax を使うものがある。

Xilinx/SDSoC/2016.2/samples/platforms/zc702_axis_io/samples/stream

これに自分の stream を差し込んでみた。xilinx のサンプルは ap_axiu<32,1,1,1> を直接つかっていて hls::stream を使っていない。これだと不便だ。

SDSoC の sample をみると
samples/hls_if/hls_stream/
とちゃんと hls_stream を使ったものがある。なので、これをうまく差し込めれば自由に stream を使えることになる。でうまく組み合わせたのが、冒頭の絵になる。

で、これを差し込んだだけ?でうまくいくかというとさにあらず。hls_stream のサンプルでは

void bytestrm_dwordproc(
      hls::stream<uint8_t> &strm_out,
      hls::stream<uint8_t> &strm_in,
      uint16_t strm_len
      )
{
//HLS:  #pragma HLS interface axis port=strm_in
#pragma HLS interface ap_fifo port=strm_in
//HLS:  #pragma HLS interface axis port=strm_out
#pragma HLS interface ap_fifo port=strm_out

となっている。これだとうまくいかない。ap_fifo は恐らく Vivado HLS で fifo のインタフェースをつくってしまう。そして、SDSoC では stub で

#ifndef __SDSVHLS__
void bytestrm_dwordproc(
      unsigned char *strm_out,
      unsigned char *strm_in,
      unsigned short strm_len
      )
{
  // Prototype used by SDSoC to generate stub for hardware function
  // HLS function does not cross-compile with arm-xilinx*-gnueabi-g++
}
#else

とつくっている。完全にダミールーチン。C++ 側のスタブを作るためだけにある。Vivado HLS の機能をフルに使おうとすると、g++ の用意する(最終的にコンパイラは g++だから。フロントエンドは llvm + clang)言語体系とうまくあわない。なので、このように

#ifdef __SDSVHLS__
Vivado HLS の関数
#else
g++ の関数
#endif

と両方書いて、うまくその整合性を合わせるのは使う側の責任だ。で、ここの情報が不足している。簡単に破たんする。破綻すると opt が死んだり、謎のエラーが出たりする。
でもサンプルをよく見ると

//HLS:  #pragma HLS interface axis port=strm_in

コメントアウトされている。ありがとう。優秀なプログラマ。ヒントを残してくれてたんだね。ということで、ap_fifo ではなく axis を指定することにする。これで、完全にハードの世界になる。が、これで終わらない。

void
my_p2p(hls::stream <ap_axiu<32,1,1,1> > & strm_in, hls::stream <ap_axiu<32,1,1,1> > & strm_out)
{
//#pragma HLS pipeline enable_flush
#pragma HLS interface axis port=strm_in
#pragma HLS interface axis port=strm_out
//#pragma HLS interface ap_ctrl_none port=return // これが重要だった。

ソース上にすでにコメントで答えが書いてあるが、 ap_ctrl_none が必要だった。my_p2p の引数がすべてハードになってしまったために、C++ とのインタフェースを失ってしまったらしい。通常あるはずの C++ のインターフェスに追加する形で戻り値のインタフェースを追加する(のでしょう)ので、そのインタフェースがないと return のインターフェースが追加できずに合成時にエラーになる。
ということで、上の ap_ctrl_none を追加することでひとまず解決。

で、次のステップとして引数を増やして、stream が流れて行った後の統計情報などをとりたい。(実際にやろうと思っているのは width と height の取得)そこで、

void
my_p2p(uint32_t *temp, hls::stream <ap_axiu<32,1,1,1> > & strm_in, hls::stream <ap_axiu<32,1,1,1> > & strm_out)

に変更したら、こんどは temp のインタフェースの offset が特定できずに opt の起動でエラー。opt と偉そうに書いているが何をしているかは不明。どうやら sds++ でコンパイルすると、インタフェースの情報を *.o の中に埋め込む(objdump でみると通常は見られないヘッダーがついている)。opt はそれをみて、どうやら tcl やら xci をつくる。その tcl と xci をみて vivado が IP コアを生成しているようだ。
上記の記述だと hls::stream が邪魔して temp の offset がわからないようだ。ということで、明示的に追加。この辺は SDSoC のサンプルになかったので完全に試行錯誤。
っていうか、ちゃんとサンプル用意してよ。これ、言語仕様なんだから。試行錯誤する時間が馬鹿らしい。

FPGA の部屋ありがとう。ここの情報と twitter の情報で突破で来た。

void
my_p2p(uint32_t *temp, hls::stream <ap_axiu<32,1,1,1> > & strm_in, hls::stream <ap_axiu<32,1,1,1> > & strm_out)
{
#pragma HLS pipeline enable_flush
#pragma HLS interface axis port=strm_in
#pragma HLS interface axis port=strm_out
#pragma HLS interface ap_ctrl_none port=return
#pragma HLS interface s_axilite port=temp offset=0x0

こんな感じ。あとは自分で offset を考えて書けばいいんだね。あーこれでたぶん SDSoC 2016.2 がもっている uint16_t の issue (バグじゃねぇか?)も回避できそうだ。まぁ 2016.3 に移行すれば問題ないんだけど。

こんなに苦労したけど、すべて 2016.2 です。2016.3 でどうなっているかは不明。全部、フィックスしてたりして。あと、最終動作確認もまだ。

sds_alloc と CMA

Linux で sds_alloc をすると、どうも CMA の領域を使うらしい。CMA は .config で 256 に設定してある(Mバイト)。つまり、私のシステムでは 0x30000000 からなのだが、VRAM の領域をとることができない。連続して取れる領域の上限があるみたい。(たぶん Linux 側の問題。.config で変えられるかも、、、、)しかも、仮にとれたとしても、物理アドレスがわからない。
vdma を uio 経由で使おうと思ったら物理アドレスが必要なんだよね。
sds_mmap を使う。これは”物理アドレス”を直接使っちゃう。0x30000000 からマップしたら見事にシステムがこけました。どうやら 0x30000000 の先頭はシステム(Linuxカーネル?) が使っているらしい。CMA としてつかっているのか XILINXLinux ドライバが使っているのかは不明。使っている領域をマップしようとしたらエラーになってくれればいいのに、、、、
今は強引に 0x38000000 からマップしてます。いずれ sds_alloc とバッティングするでしょう。物理アドレスとの整合性を /dev/fb なみにしてほしい。おそらく ioctl で用意しているとは思うけど、現時点ではユーザに見せていない。

SDSoC のプラットフォームと Vivado の階層

どうも、SDSoC 2016.2 は Vivado の階層に対応してないみたい。階層の stream の口を指定したプラットフォームを用意して SDSoC でコンパイルすると、エラーになる。意味不明のエラーでなんだかわらない。しかたがないので、階層の横に stream の FIFO かなにか”よけいな"IPコアを一個用意する予定。合成時に時間がかかっちゃうな、、、、Vivado の最適化で取り除かれるようにしたい。あーでも OOC つかっていると IP コアごとにコンパイルするから取り除かれないかも。

いろいろ障害はあるけれど

いろいろ障害はあったけど、stream とりわけ hls::stream が使えるようになると便利になる(個人的にはね)。C++ のテンプレートがばしばしつかえるのもよい(個人的にはね)。

こういった情報交換が母国語(日本語)でできるしあわせをかみしめつつ。誰かのお役に立てれば。