第10章
高位合成に関するメモ
II: Initiation Interval
開始間隔。II=1なら毎サイクルデータを受付可能
レイテンシ
入力を与えてから結果が表れるまでのサイクル数
Vitis HLSが生成可能なインタフェース
- 無手順
- aclk,validによるハンドシェイク
- メモリI/O
- AXI
高位合成対象
- main関数は合成対象ではなく、テンスとベンチの位置づけ
- 合成対象は1つの関数のみ。サブルーチンを含めて合成される
- 最上位関数の引数がポートになる。値渡し、参照渡し、配列に対応
- 最上位関数の戻り値もポートになる
インタフェース
- ブロックレベルインタフェース
- 合成したモジュールの動作に関する制御を行う
- 開始信号を入力し、処理が終わったら終了信号とともに戻り値を出力する
- 戻り値は計算結果ではなく、正常終了や異常終了ということ?→そうでもない
- ポートレベルインタフェース
- 個々の引数に対する制御を行う
- 値渡し、ポインタ渡しの入力は無手順
- ポインタ渡しの出力はvld信号付きのインタフェース。計算結果は基本的に参照渡しで
*C = A + B;のようにする?- 例題だと最終的にAXI4-Liteのラッパーをかぶせてしまうので、よくわからない。結果が単一の時は戻り値で、複数の場合はポインタで返している
- 後述の比較結果より、あまり大差はなさそう。C/RTL協調シミュレーションではポインタ渡しのほうが1アクセス分かかる
- 配列はメモリになる。アドレス、CE、WEで制御する
Vitis HLS
ソリューション
- 同じソースコードで、ターゲットデバイスや周波数、ディレクティブを変更してカットアンドトライするための階層
- ソリューション自体はソースコードを持たない
- ディレクティブの挿入には、ソースコードに直接記述or別ファイルで指定の2通りがあるが、複数ソリューションを試す場合は後者
IP化
- IP化して、VivadoのIPインテグレータから呼び出しできるようにする
- APIも自動で生成される
- Export RTLでエラーが発生。自動で採番されるIPリビジョン番号がsigned intの値域を超えることが原因らしい
- 参考サイト:https://phys-higashi.com/2669/
- 日本語版のサポートページではzipファイルが見つからなかったため、US版の記事からパッチをダウンロードして適用した
- https://adaptivesupport.amd.com/s/article/76960?language=en_US
- 書籍でも言及されているが、APIのMakefileにバグ?に関してちょっとはまったのでメモ
- ソースコードエディタで
muladd_ip\muladd\solution4\impl\ip\drivers\muladd_v1_0\src\Makefileを修正 - muladd.vitisを削除して、再度空箱で作成。こうしないとbsp, fslb両方のMakefileを修正することになる
- Vivadoのブロックデザインを開いてIPをアップデート、bitstream生成、エクスポート(もしかしたらエクスポートだけでもいいのかも)
- muladd.vitis以下を再度構成
- ソースコードエディタで
読み出し関数の違い
- XAvgminmax_Get_avg(&Instance);
- XAvgminmax_ReadReg(InstancePtr->Control_BaseAddress, XAVGMINMAX_CONTROL_ADDR_AVG_DATA);
- 平均値自体を読み出す関数
- XAvgminmax_Get_avg_vld(&Instance);
- XAvgminmax_ReadReg(InstancePtr->Control_BaseAddress, XAVGMINMAX_CONTROL_ADDR_AVG_CTRL);
- 平均値の有効フラグを返す関数
結果を戻り値でもらうorポインタでもらう
muladdはa[i]*b[i]の累積値を一つ返すため、書籍の通り戻り値でもらってもよいし、ポインタでもらってもよい。どのような差異が発生するかを確認する。
ポインタで結果を受け取る場合の回路記述は以下。
void muladd(int a[SIZE], int b[SIZE], int *result)
{
int i, temp = 0;
muladd_loop:for (i=0; i<SIZE; i++) {
temp += a[i] * b[i];
}
*result = temp;
}
ディレクティブはresultに対してINTERFACE=s_axiliteを追加した。
set_directive_top -name muladd "muladd"
set_directive_interface -mode s_axilite "muladd"
set_directive_interface -mode s_axilite "muladd" a
set_directive_interface -mode s_axilite "muladd" b
set_directive_interface -mode s_axilite "muladd" result
set_directive_pipeline "muladd/muladd_loop"
AXI4-Liteラッパーなし
トップの端子が異なるだけで、リソースやタイミング系は同じ。ポインタで返す場合はvalid信号が追加される。
module muladd (
ap_clk,
ap_rst,
ap_start,
ap_done,
ap_idle,
ap_ready,
a_address0,
a_ce0,
a_q0,
b_address0,
b_ce0,
b_q0,
ap_return
);
module muladd (
ap_clk,
ap_rst,
ap_start,
ap_done,
ap_idle,
ap_ready,
a_address0,
a_ce0,
a_q0,
b_address0,
b_ce0,
b_q0,
result,
result_ap_vld
);
AXI4-Liteラッパーあり
合成結果はリソースのみが変わり、AXI4-LiteインタフェースのFFが6個増加。
| Design | Instance | Module | BRAM_18K | DSP | FF | LUT | URAM |
|---|---|---|---|---|---|---|---|
| 戻り値 | control_s_axi_U | control_s_axi | 4 | 0 | 228 | 244 | 0 |
| ポインタ | control_s_axi_U | control_s_axi | 4 | 0 | 234 | 244 | 0 |
また、AXI4-Liteの端子を見ると、
input wire [31:0] ap_return,
input wire [31:0] result,
input wire result_ap_vld,
のようにユーザロジックからの結果のもらい方が異なる。戻り値の場合は戻り値の変数名によらずap_return, ポインタの場合はユーザが定義したポインタ名の信号+Valid信号
さらにレジスタマップは、
| offset | 戻り値 | ポインタ |
|---|---|---|
| 0x00 | Control signals | ← |
| 0x04 | Global Interrupt Enable Register | ← |
| 0x08 | IP Interrupt Enable Register | ← |
| 0x0c | IP Interrupt Status Register | ← |
| 0x10 | Data signal of ap_return | Data signal of result |
| 0x14 | Reserved | Control signal of result |
| 0x40-0x7f | Memory 'a' | ← |
| 0x80-0xbf | Memory 'b' | ← |
のようにvalid信号に相当するレジスタが追加されていた。
最後に結果を読み出すドライバ関数を確認する。予想通り、
u32 XMuladd_Get_return(XMuladd *InstancePtr) {
u32 Data;
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
Data = XMuladd_ReadReg(InstancePtr->Control_BaseAddress, XMULADD_CONTROL_ADDR_AP_RETURN);
return Data;
}
u32 XMuladd_Get_result(XMuladd *InstancePtr) {
u32 Data;
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
Data = XMuladd_ReadReg(InstancePtr->Control_BaseAddress, XMULADD_CONTROL_ADDR_RESULT_DATA);
return Data;
}
u32 XMuladd_Get_result_vld(XMuladd *InstancePtr) {
u32 Data;
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
Data = XMuladd_ReadReg(InstancePtr->Control_BaseAddress, XMULADD_CONTROL_ADDR_RESULT_CTRL);
return Data & 0x1;
}
演算完了タイミングはどちらの回路も同じだが、ポインタで結果を返す場合、ap_done(0x00)を読んだ後にresult信号がvalidであることを確認(0x14)してからresult信号をReadしている(0x10)。そのため1転送分余分に時間を要する。

結局
使い分けとしては、
- 戻り値の場合は、ap_done時に結果がvalidであることが保証されていて、
- ポインタの場合は、valid信号であるかどうかを明示的に確認する必要がある?ところで、validにならないことってあるのだろうか
第11章
- INTERFACE=m_axi
- AXIマスターとしてインタフェースが生成される。offset=slaveを設定するとAXI4-Liteスレーブとしてアドレス指定レジスタが生成される(未指定の場合0x0番地からアクセスしてしまう)
- LOOP_TRIPCOUNT
- ループ回数が変数で与えられる場合に、ループ回数の最大・最小・平均をこのディレクティブで与えることで、規模の最適化を行う
