関数インターフェイスで使用される構造体のサイズは、ループ本体のインターフェイスにアクセスできる関数のループのパイプライン処理に悪影響を与える可能性があります。2 つの M_AXI インターフェイスを持つ次のコード例を参照してください。
struct A { /* Total size = 192 bits (32 x 6) or 24 bytes */
int s_1;
int s_2;
int s_3;
int s_4;
int s_5;
int s_6;
};
void read(A *a_in, A buf_out[NUM]) {
READ:
for (int i = 0; i < NUM; i++)
{
buf_out[i] = a_in[i];
}
}
void compute(A buf_in[NUM], A buf_out[NUM], int size) {
COMPUTE:
for (int j = 0; j < NUM; j++)
{
buf_out[j].s_1 = buf_in[j].s_1 + size;
buf_out[j].s_2 = buf_in[j].s_2;
buf_out[j].s_3 = buf_in[j].s_3;
buf_out[j].s_4 = buf_in[j].s_4;
buf_out[j].s_5 = buf_in[j].s_5;
buf_out[j].s_6 = buf_in[j].s_6 % 2;
}
}
void write(A buf_in[NUM], A *a_out) {
WRITE:
for (int k = 0; k < NUM; k++)
{
a_out[k] = buf_in[k];
}
}
void dut(A *a_in, A *a_out, int size)
{
#pragma HLS INTERFACE m_axi port=a_in bundle=gmem0
#pragma HLS INTERFACE m_axi port=a_out bundle=gmem1
A buffer_in[NUM];
A buffer_out[NUM];
#pragma HLS dataflow
read(a_in, buffer_in);
compute(buffer_in, buffer_out, size);
write(buffer_out, a_out);
}
上記の例では、構造体 A
のサイズが 192 ビットであり、2 のべき乗ではありません。前述のように、AXI4 インターフェイスすべてのデフォルトのサイズは 2 です。Vitis HLS は 2 つの M_AXI インターフェイス (a_in
および a_out
) のサイズを自動的に 256 (192 ビットのサイズに最も近い 2 のべき乗) に設定します。これは、次に示すログ ファイルにレポートされます。
INFO: [HLS 214-241] Aggregating maxi variable 'a_out' with compact=none mode in
256-bits (example.cpp:49:0)
INFO: [HLS 214-241] Aggregating maxi variable 'a_in' with compact=none mode in 256-bits
(example.cpp:49:0)
これは、構造体データを書き出すときに、最初の書き込みは 1 サイクルで最初のバッファーに 24 バイトを書き込みますが、2 回目の書き込みは、最初のバッファーの残りの 8 バイトに 8 バイトを書き込んでから、2 つ目のバッファーに 16 バイトを書き込むので、2 つの書き込みになるということを意味します (次の図を参照)。
これにより、II = 1 ではなく II = 2 が必要になるため、write()
関数の WRITE ループの II に II 違反が発生します。読み出し時にも同様の動作が発生します。このため、read()
関数でも II = 2 が必要なので、II 違反が発生します。Vitis HLS は、read()
および write()
関数の II 違反に対して次の警告メッセージを表示します。
WARNING: [HLS 200-880] The II Violation in module 'read_r' (loop 'READ'): Unable
to enforce a carried dependence constraint (II = 1, distance = 1, offset = 1) between
bus read operation ('gmem0_addr_read_1', example.cpp:23) on port 'gmem0' (example.cpp:23)
and bus read operation ('gmem0_addr_read', example.cpp:23) on port 'gmem0' (example.cpp:23).
WARNING: [HLS 200-880] The II Violation in module 'write_Pipeline_WRITE' (loop 'WRITE'):
Unable to enforce a carried dependence constraint (II = 1, distance = 1, offset = 1)
between bus write operation ('gmem1_addr_write_ln44', example.cpp:44) on port 'gmem1'
(example.cpp:44) and bus write operation ('gmem1_addr_write_ln44', example.cpp:44) on
port 'gmem1' (example.cpp:44).
このような II の問題を修正するには、8 バイトの追加バイトで構造体 A
をパディングして、常に一度に 256 ビット (32 バイト) を書き込むか、次の表に示すほかの方法を使用します。これにより、スケジューラは、II=1 で READ/WRITE ループの読み出し/書き込みをスケジュールできるようになります。
コード ブロック | 詳細 |
---|---|
|
必要なパディング要素を追加して、構造体の合計サイズを 256 ビット (32 x 8) または 32 バイトとして定義します。 |
|
標準の __aligned__ 属性を使用します。 |
|
C++ 標準 alignas 型指示子を使用して、変数とユーザー定義型のカスタム アライメントを指定します。 |