【VItisHLS】内部信号にhls::stream< hls::axis<hoge_t , ... > >は使えない

# やりたいこと

AXI4-Streamの入出力を持つやつを繋げたものをIPをラップしたIPを作りたい。

# コード

https://github.com/walk-to-work/Qiita_Sample_0.git

```cpp:sample.hpp
#ifndef SAMPLE_HPP
#define SAMPLE_HPP

#include <iostream>
#include <ap_int.h>
#include <random>
#include "hls_stream.h"
#include "ap_axi_sdata.h"

const uint32_t W_USR = 32;
typedef hls::axis< uint32_t, W_USR, 0, 0> my_axis;

void top_func(
    hls::stream<my_axis>& in_a, 
    hls::stream<my_axis>& in_b , 
    hls::stream<my_axis>& in_c , 
    hls::stream<my_axis>& out_res  
);

#endif // SAMPLE_HPP
```


```cpp:sample.cpp
#include "sample.hpp"

// 内部IP 1
void add_st (
    hls::stream<my_axis>& in_a,
    hls::stream<my_axis>& in_b,
    hls::stream<my_axis>& out_res
){
    my_axis a, b, res;
    in_a.read(a);
    in_b.read(b);
    res.data = a.data + b.data;
    res.user = a.user;
    res.last = a.last;
    out_res.write( res );
}

// 内部IP 2
void sub_st (
    hls::stream<my_axis>& in_a,
    hls::stream<my_axis>& in_b,
    hls::stream<my_axis>& out_res
){
    my_axis a, b, res;
    in_a.read(a);
    in_b.read(b);
    res.data = a.data - b.data;
    res.user = a.user;
    res.last = a.last;
    out_res.write( res );
}

// wrapするトップモジュール
void top_func(
    hls::stream<my_axis>& in_a, 
    hls::stream<my_axis>& in_b, 
    hls::stream<my_axis>& in_c, 
    hls::stream<my_axis>& out_res
){

    #pragma HLS INTERFACE axis port=in_a
    #pragma HLS INTERFACE axis port=in_b
    #pragma HLS INTERFACE axis port=in_c
    #pragma HLS INTERFACE axis port=out_res
    #pragma HLS DATAFLOW

    hls::stream<my_axis> a_plus_b_st;

    add_st( in_a , in_b , a_plus_b_st );
    sub_st( a_plus_b_st , in_c , out_res );
}
```

```cpp:tb_sample.cpp
#include "sample.hpp"

int main(){

    hls::stream<my_axis> in_a, in_b, in_c, out_exp_res, out_act_res;
    
    std::mt19937 engine(0);
    const uint32_t TEST_SIZE = 1024;
    const uint32_t RAND_LIM  = 10000;

    for( uint32_t i = 0 ; i < TEST_SIZE ; ++i ){

        my_axis a,b,c,res;

        a.data = engine() % RAND_LIM ;
        b.data = engine() % RAND_LIM ;
        c.data = engine() % RAND_LIM ;

        a.user = i;
        b.user = i + 1000;
        c.user = i + 2000;

        a.last = (i == TEST_SIZE - 1) ? 1 : 0;

        in_a.write(a);
        in_b.write(b);
        in_c.write(c);

        res.data = a.data + b.data - c.data;
        res.last = a.last;

        out_exp_res.write( res );
    }

    for( int i = 0 ; i < TEST_SIZE ; ++i ){
        top_func( in_a, in_b, in_c, out_act_res );
    }

    for( int i = 0 ; i < TEST_SIZE ; ++i ) {
        my_axis act_res, exp_res;
        out_act_res.read( act_res );
        out_exp_res.read( exp_res );

        if( act_res.data != exp_res.data ) {
            printf("result is not same\n");
            return 1;
        }

        if( act_res.last != exp_res.last ) {
            printf("TLAST timing is not same\n");
            printf("TEST FAILED\n");
            return 1;
        }

        if( act_res.last == 1 )
            printf("TLAST is detected at %d\n" , i );
    }

    printf("TEST PASSED\n");

    return 0;
}
```

# エラー内容

これらを用いてIPを生成しようとすると`C Simulation`は通るが、`C Synthsis`で以下のエラーが出る。

```tcl
ERROR: [HLS 214-208] The ap_axis|ap_axiu|qdma_axis|hls::axis data types must only be used for AXI-Stream ports in the interface. 'a_plus_b_st' is not an AXI-Stream and/or is not a port in the interface
ERROR: [HLS 200-1715] Encountered problem during source synthesis
```

エラー内容で検索すると、それっぽい質問が出た[^2]。

# 解決方法

__外部ポートを持たない`hls::stream`には`hls::axis`を用いない。__


参考にした記事[^1]によると、Vitis HLS 2020.2より外部ポートを持たない`hls::stream<hls::axis<hoge_t, ... >>`は弾かれるようになったらしい。そのため以下のように`hls::axis`を用いない形に変える必要がある。

```cpp:解決方法（tuserやtlastを用いない場合）
hls::stream<uint32_t> a_plus_b_st;
```

今回の場合は`tuser, tlast`を用いているため、次のようにすることで解決した。

```cpp:解決方法（tuserやtlastを用いる場合）
struct internal_st{
    uint32_t       data;
    ap_uint<W_USR> user;
    bool           last;
};

hls::stream<internal_st> a_plus_b_st;
```

--- 

最初に出た記事の

>Because B is not an external interface, you cannot define it as an axis interface. You can simply declare it as an hls::stream\<int32_t\> to stream data from block_1 to block_2. 

を, 『内部信号に`hls::stream`を使うな』に勘違いして永遠にエラーを出していた（馬鹿すぎる）。
marseeさんに感謝。



[^1]:[Vitis HLS 2020.2 で hls::stream の ap_axiu , ap_axis データタイプが AXI4-Stream ポートでしか使用できない / FPGAの部屋](https://marsee101.blog.fc2.com/blog-entry-5118.html) 

[^2]:[[HLS 214-208] The ap_axis|ap_axiu|qdma_axis|hls::axis data types must only be used for AXI-Stream ports in the interface/FPGA](https://support.xilinx.com/s/question/0D52E00006hpQKOSA2/hls-214208-the-apaxisapaxiuqdmaaxishlsaxis-data-types-must-only-be-used-for-axistream-ports-in-the-interface?language=ja)