はじめに
以前作成した記事 で Vitis HLS 向けの Rust フロントエンドコンパイラを紹介しました.本稿では,このコンパイラの導入から IP の生成までのフローを解説します.
登場するツール群は次の 4 つです.
| ツール | 役割 |
|---|---|
| hls-rs/rust | HLS 対応 Rust コンパイラ (rustc 1.51 ベース) |
| barista-hls | HLS 向け Rust ライブラリ |
| barista-template | プロジェクトテンプレート |
| Vitis HLS 2025.2 | 高位合成バックエンド (AMD 公式ツール) |
全体のフローは次の通りです.カーネルもテストベンチもすべて Rust で書き,make ターゲット 4 つで C シミュレーションから IP 化まで進みます.
環境
- OS: Ubuntu 22.04
- CPU: AMD Ryzen 7 3700X
- Vitis HLS: 2025.2
- rustup
1. インストール
ビルド済みの Rust HLS ツールチェーンを GitHub で公開しています.ダウンロードして展開し,rustup に登録してください.
$ mkdir -p ~/.hls-rs
$ curl -fsSL https://github.com/hls-rs/rust/releases/latest/download/hls-toolchain-x86_64-linux.tar.xz \
| tar xJ -C ~/.hls-rs --strip-components=1
$ rustup toolchain link hls ~/.hls-rs
以後 cargo +hls / rustc +hls で HLS 対応コンパイラを呼び出せます.動作確認として,バージョンと FPGA ターゲットの存在を確認しましょう.
$ rustc +hls -Vv
rustc 1.51.0-dev
...
LLVM version: 11.0.1
$ rustc +hls --print target-list | grep fpga
fpga32-unknown-unknown
fpga64-unknown-unknown
2. プロジェクトの作成
プロジェクトは cargo-generate のテンプレート barista-template から生成すると便利です.
$ cargo install cargo-generate # 未インストールなら
$ cargo generate --git https://github.com/barista-rs/barista-template \
--name my_kernel
生成時に 4 つの値を聞かれます (-d key=value で非対話指定も可能).
| placeholder | 意味 | 例 |
|---|---|---|
part_name |
ターゲット FPGA の part 名 | xczu7ev-ffvc1156-2-e |
clock_freq |
目標クロック | 100MHz |
top_fn |
カーネル関数の名前 | vector_add |
hls_toolchain |
rustup に登録したツールチェーン名 | hls |
上記の代わりに,下記のコマンドを実行しても構いません.
$ cargo generate --git https://github.com/barista-rs/barista-template \
--name my_kernel \
-d part_name=xczu7ev-ffvc1156-2-e -d clock_freq=100MHz \
-d top_fn=vector_add -d hls_toolchain=hls
生成されるプロジェクトは次の構成です.
my_kernel/
├── Cargo.toml
├── Makefile
├── build.rs # cosim 用 C++ プロキシの自動生成スクリプト
├── src/lib.rs # カーネル (合成対象)
├── src/main.rs # テストベンチ
└── tcl/ # vitis_hls 用スクリプト
3. カーネルとテストベンチの記述
カーネル (src/lib.rs)
テンプレートには 16 要素のベクトル加算カーネルが入っています.このまま合成できるので,まずはこれをそのまま使ってみましょう.
#![no_std]
#[barista_hls::top]
fn vector_add(a: &[i32; 16], b: &[i32; 16], c: &mut [i32; 16]) {
for i in 0..16 {
c[i] = a[i] + b[i];
}
}
- カーネル crate は
#![no_std]です.合成対象にヒープや OS 依存コードが入らないことをコンパイル時に保証します. -
#[barista_hls::top]はカーネル関数(合成対象)に付与します.
テストベンチ (src/main.rs)
テストベンチは通常のホスト側 Rust コードです.C++ HLS のテストベンチと同じ発想で,テストデータを作ってカーネルに入力します.
#![no_main]
#[barista_hls::testbench]
fn main() -> i32 {
let mut a = [0i32; 16];
let mut b = [0i32; 16];
let mut c = [0i32; 16];
for i in 0..16 {
a[i] = i as i32;
b[i] = 2 * i as i32;
}
my_kernel::vector_add(&a, &b, &mut c);
println!("csim ran: c[15]={}", c[15]);
0
}
4. シミュレーションから IP 化まで
make csim — C シミュレーション
以降は Vitis にパスが通っている前提で話を進めます.
プロジェクトで make csim を実行すると,C シミュレーションが実行できます.すなわちテストベンチ経由でカーネルをソフトウェアとして実行します.
$ make csim
Finished release [optimized] target(s) in 12.02s
Running `target/release/my_kernel`
csim ran: c[15]=45
make csynth — 高位合成
make csynth を実行すると,Rust カーネルが HDL に高位合成されます.
$ make csynth
ログは target_hls/csynth.log,合成レポートは proj/solution_base/syn/report/vector_add_csynth.rpt に出ます.
make cosim — co-simulation
make cosim を実行すると,先程合成した回路を RTL シミュレーションします.回路へのスティミュラスは Rust テストベンチの内容が使用されます.
$ make cosim
$ grep 'co-simulation finished' target_hls/cosim.log
INFO: [COSIM 212-1000] *** C/RTL co-simulation finished: PASS ***
csynth が生成した RTL を Vivado シミュレータ (xsim) で動かし,src/main.rs のテストベンチ (csim と同じもの) から駆動して,RTL の出力とソフトウェア実行の出力を突き合わせます.PASS が出れば「合成された回路が Rust コードと同じ振る舞いをする」ことが RTL レベルで確認できたことになります.
make export-ip — IP-XACT パッケージ
make export-ip を実行すると,合成済み RTL を IP-XACT 形式でパッケージします.生成された zip (または impl/ip/ ディレクトリ) を Vivado の IP Catalog に追加すれば,Vivado プロジェクトに Rust 製カーネルを回路として組み込むことが可能です.
$ make export-ip
$ ls proj/solution_base/impl/ip/
component.xml hdl/ xilinx_com_hls_vector_add_1_0.zip ...
おわりに
本稿では Vitis HLS 向けの Rust フロントエンドの使い方を紹介しました.C/C++ での高位合成開発を踏襲したフローで,高位合成に慣れた方にとって馴染みやすい手順で Rust による回路開発ができたと思います.
より発展的な内容として,pragma や Vitis HLS の C++ ライブラリを使いたい場合は barista-hls のドキュメント をお読みいただくか,Vitis HLS 公式サンプルの Rust 版 Vitis-HLS-Introductory-Examples-rs を参考にしてください.