0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

FPGAプログラム大全やりなおし#6

0
Last updated at Posted at 2025-09-15

第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の値域を超えることが原因らしい
  • 書籍でも言及されているが、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
);

image.png

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転送分余分に時間を要する。
image.png

結局

使い分けとしては、

  • 戻り値の場合は、ap_done時に結果がvalidであることが保証されていて、
  • ポインタの場合は、valid信号であるかどうかを明示的に確認する必要がある?ところで、validにならないことってあるのだろうか

第11章

  • INTERFACE=m_axi
    • AXIマスターとしてインタフェースが生成される。offset=slaveを設定するとAXI4-Liteスレーブとしてアドレス指定レジスタが生成される(未指定の場合0x0番地からアクセスしてしまう)
  • LOOP_TRIPCOUNT
    • ループ回数が変数で与えられる場合に、ループ回数の最大・最小・平均をこのディレクティブで与えることで、規模の最適化を行う
0
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?