背景
FPGA 向けの高位合成 (HLS) は長年 C/C++ がデファクトです。AMD/Xilinx の Vitis HLS は、C++ のサブセットでベンダ独自の #pragma HLS ... ディレクティブ及びap_uint<N> / hls::stream のような型を組み合わせて回路を記述します。しかし C/C++ でカーネルを書くこと自体に開発者の負担が大きいという課題があります。具体的には、(i) C/C++ による記述の煩雑さ、(ii) C/C++ 由来のメモリ安全性問題 (バッファオーバーフロー、未初期化メモリ 等) がカーネルコードに持ち込まれること、などが挙げられます。
他言語向けの HLS コンパイラは過去にいくつも提案されています。しかしその多くがバックエンドを独自実装しています。そのため、ベンダがチップごとに施している専用最適化や新しい IP コアの取り込みに、個々のプロジェクトが追随することは難しいのが現状です。
一方で Vitis HLS のバックエンドは LLVM IR を入力にしており、frontend を差し替えれば言語側の改造だけで RTL までたどり着けます。実際 Fortran で書いたカーネルを Vitis HLS に流す試み (Fortran HLS) も存在し、バックエンドの最適化や IP の恩恵をそのまま受けられます。
Rust は型システムと所有権モデルが堅牢で、メモリ安全性に起因するバグをコンパイル時に検出できる言語です。null や例外に頼らない安全なエラー処理、解放後アクセスや並行アクセスの自動検出といった仕組みを言語レベルで備えています。cargo によるパッケージ管理エコシステムも充実しています。
Rust コンパイラは LLVM でできており、LLVM IR を生成します。Vitis HLS のバックエンドも LLVM IR を入力にすることから、両者を LLVM IR で接続すれば「Rust で書いたカーネルを Vitis HLS で合成する」高位合成系が成立するはずです。これが実現すれば、Rust の安全性とエコシステムをそのまま FPGA 開発に持ち込めるうえに、Vitis HLS の最適化や IP 取り込みの恩恵もそのまま享受できます。
目的
本取り組みの目的は、Vitis HLS のバックエンドを共通基盤として活用しながら、高位合成の言語選択肢に Rust を加えることです。これによって、既存の Rust プログラム資産を FPGA で容易にアクセラレーションできるようにすることを目指します。
方法
Vitis HLS の内部構造
Vitis HLS の合成フローは大きく分けると次の 4 段階です。
注目すべきは バックエンドが LLVM IR を入力にしている という点です。フロントエンドが C++ 専用 (clang) なのは表面的な構成であり、内部処理は LLVM IR を介して行われます。つまり同等の IR を生成できる別言語のコンパイラを用意すれば、バックエンドはそのまま再利用できます。
LLVM IR ベースで言語を導入するための要素
C/C++ 以外の言語を Vitis HLS のバックエンドに流し込むには、次の 4 つの要素が必要になります。
1. コンパイラフロントエンド (言語ソース → LLVM IR)
Rust は LLVM ベースで開発されているため,高位合成用のフロントエンドには公式の Rust コンパイラを導入するのが現実的です。FPGA 向けには次の追加が必要です。
-
FPGA target の登録:
fpga32-unknown-unknown/fpga64-unknown-unknownのような target triple を追加し、Xilinx FPGA バックエンドのcomputeDataLayoutから data layout を持ってくる -
任意 bit 幅整数の native 表現:
ap_uint<N>相当の型をiN(LLVM の任意 bit 幅整数) として emit できる仕組み。
2. LLVM のバージョン整合
Vitis HLS のバックエンドが想定するのは LLVM 11 系の IR です。フロントエンド側の LLVM がそれより新しいと、IR の attribute / metadata / opcode が合わず reject されます。
Rust の場合、コンパイラに同梱される LLVM のバージョンはリリースごとに更新されており、1.51 が LLVM 11 を同梱した最後のバージョン、最新の安定版は LLVM 20 系以降です。LLVM 11 IR を得るには、LLVM 11 を同梱した Rust 1.51 を使うか、新しい LLVM が生成した IR を LLVM 11 にダウングレードする変換ツールを別途用意することになります。例えば先行例の Fortran HLS は、flang が出力する新しい LLVM IR を LLVM 11 まで下げるダウングレーダを実装しています。
3. IR 整形 pass
言語フロントエンドが生成する IR と、Vitis HLS バックエンドが期待する IR には微妙な差があります。例えば次のような構造の不一致を埋める変換が必要です。
- 言語独自の struct layout / packing → HLS が認識する形への書き戻し
- 言語固有の bounds check や panic 経路の除去
- 言語側の stream / FIFO 型 → HLS の
ap_fifointrinsic への置換 - カーネル引数の interface ヒント (m_axi / axis / s_axilite) を表す関数呼び出しの注入
これらを LLVM の pass として実装し、フロントエンドとバックエンドの間に挿入します。
4. 言語側の HLS API
ユーザがカーネルを書くための型と関数を、言語側のライブラリとして提供します。
- 任意 bit 幅整数型 (
ApUint<N>/ApInt<N>等) - ストリーム型 (
Stream<T>) - pragma に相当する marker 関数 (
pipeline(II)、unroll(factor)、dataflow()等) もしくは属性 - top function を示す属性
これらはフロントエンドを改造することなく、ライブラリとして実装できます。
Rust で導入した例
上記 4 要素を Rust 1.51 をベースに実装しました。
通常の Rust ビルドでは、rustc が LLVM IR を生成し、LLVM が x86 / ARM / RISC-V の機械語に変換します。
ここに「FPGA 向けの target」を差し込み、Vitis HLS に流し込みます。IR 整形 pass は別の opt プラグイン (.so) ではなく rustc 本体に組み込んで static link しているため、配布は単一の toolchain で完結します。
カーネル例
合成対象となる Rust カーネルは次のような形になります (ベクトル加算)。
#![no_std]
use barista_hls::ApInt;
#[barista_hls::top]
fn vector_add(
a: &[ApInt<32>; 16],
b: &[ApInt<32>; 16],
out: &mut [ApInt<32>; 16],
) {
for i in 0..16 {
out[i] = a[i] + b[i];
}
}
#[barista_hls::top] 属性が合成対象 (= top function) を示します。ApInt<N> で任意 bit 幅の符号付き整数を表現でき、C++ の ap_int<N> に相当します。
テストベンチ例
テストベンチも host 側の Rust コードとして書きます。
use my_kernel::vector_add;
use barista_hls::ApInt;
fn main() {
let mut a = [ApInt::<32>::new(0); 16];
let mut b = [ApInt::<32>::new(0); 16];
let mut out = [ApInt::<32>::new(0); 16];
for i in 0..16 {
a[i] = ApInt::new(i as i128);
b[i] = ApInt::new((i * 2) as i128);
}
vector_add(&a, &b, &mut out);
for i in 0..16 {
assert_eq!(out[i].as_i32(), (i + i * 2) as i32);
}
}
vector_add は通常の Rust 関数として呼び出せるので、ホスト上で期待値と比較する形のテストが書けます。同じ関数が合成段階では Vitis HLS の入力カーネルとして扱われます。
結果 (Rust 導入の検証)
環境
- OS: Ubuntu 22.04
- CPU: AMD Ryzen 7 3700X
- Vitis: 2025.2
Interface の一致
Vitis HLS 公式 examples (Xilinx/Vitis-HLS-Introductory-Examples) の全カーネルを Rust に移植し、export_design で生成した IP-XACT (component.xml) の port name + direction を C++ baseline と比較しました。その結果,98 例でポートが完全一致しました。(残り 1 例 (handling_deadlock) は意図的に deadlock する教材例で C++ でも IP-XACT component を export しないため、ポート一致の比較対象外)つまり「同じピン配置で、同じ bus interface (AXI Stream / m_axi / AXI Lite) で、同じ bit 幅を持つ IP」が C++ と Rust の両方から生成できます。
性能
csynth_design 後の *_csynth.xml から、Clock / Latency / II / LUT / FF / BRAM / DSP を全 99 例について抽出しました。
Clock period / LUT / FF の比較
各 example の *_csynth.xml (最新 sweep の合成結果) から、Rust を C++ baseline で割った比 (Rust ÷ C++) を log スケールの横棒で示します。緑は完全一致 (Rust = C++)、青は Rust が小さい、橙は Rust が大きい (≤4×)、赤は外れ値 (>4×) です。
クロック周期は多くの例で C++ と完全一致 (緑) または C++ より小さい (青) 範囲に収まっています。LUT / FF は Task-Level Parallelism 系で完全一致が多い一方、Interface / DSP 系の一部では Rust 側がリソースを多く使う (橙) ケースや、桁の異なる外れ値 (赤) も見られます。これらはコンパイラフロントエンドと Rust ライブラリの差に起因するものであると考えています。
なお 99 例中 96 例を図示しています。残り 3 例 (handling_deadlock / using_C++_templates / tcl_scripts) は C++ baseline 側に 1:1 比較可能な top csynth が無い (dataflow で複数プロセスに分割される、または baseline 合成レポートが生成されない) ため除外しました。BRAM / DSP も 0 を使う例が多く、比が定義できないため、本図では省略しています。
機能確認
Rust testbench + Rust kernel.bc の組み合わせで Vitis HLS の cosim を回し、99/99 例で全て PASS しました。RTL シミュレーションレベルで カーネルが想定通りに動作することも確認しています。
まとめ
Vitis HLS のバックエンドが LLVM 11 IR を入力にしていることに着目し、コンパイラフロントエンド・LLVM バージョン整合・IR 整形 pass・言語側 HLS API の 4 要素を整備することで、C/C++ 以外の言語をフロントエンドとして導入できることを示しました。具体的には Rust 1.51 と Xilinx HLS パッチ済 LLVM 11 をベースに、IR 整形 pass を rustc 本体へ統合し、barista-hls (Rust 側の HLS API) を組み合わせた構成で実装しました。Vitis HLS 公式 examples 99 例のうち 98 例で C++ baseline とインタフェースが完全一致しています (残り 1 例はポート比較の対象外)。同じ構成は LLVM ベースの他言語にも同様に適用できると考えています。
今後の課題として、C++ と Rust から合成した回路の機能的等価性の検証が残っています。本記事ではインタフェース一致と合成リソースの比較までを確認しましたが、両者の RTL に同じテストパターンを与えて出力を比較する形の機能等価性検証は別途必要です。
関連リポジトリ
- rustc: hls-rs/rust
- LLVM (Xilinx HLS バックポート): hls-rs/llvm-project
- Rust カーネル API: barista-rs/barista-hls
- プロジェクトテンプレート (cargo-generate): barista-rs/barista-template
- Rust 移植版 examples: barista-rs/Vitis-HLS-Introductory-Examples-rs
参考文献
- AMD, Vitis High-Level Synthesis User Guide (UG1399), https://docs.amd.com/r/en-US/ug1399-vitis-hls
- Xilinx, HLS LLVM (公開された HLS LLVM パッチ), https://github.com/Xilinx/HLS
- CERL, Fortran HLS (Vitis HLS への非 C++ 言語導入の先行例), https://gitlab.com/cerl/fortran-hls
- AMD, Vitis HLS Methodology Guide (UG1448), https://docs.amd.com/r/en-US/ug1448-hls-guidance
- The LLVM Project, https://llvm.org
- The Rust Programming Language, https://www.rust-lang.org
