Googleがオープンソースで公開した高位合成処理系XLSをためしてみました。サンプルは https://github.com/google/xls/tree/main/xls/examples にあるのですが、Goっぽい感じでハードウェアが設計できる処理系のようです。XLS Tools Quick Startがあるのですが、何ができるのかよくわからなかったので試してみました。
手元の Ubuntu 20.04で動作確認をしました。ちなみに Ubuntu 20.04でもVivadoは普通につかえています。
ビルド
リポジトリのREADME.md通りにビルドします。まずはInstalling Bazel on Ubuntuに従ってBazelのインストール。
$ sudo apt install curl gnupg
$ curl -fsSL https://bazel.build/bazel-release.pub.gpg | gpg --dearmor > bazel.gpg
$ sudo mv bazel.gpg /etc/apt/trusted.gpg.d/
$ echo "deb [arch=amd64] https://storage.googleapis.com/bazel-apt stable jdk1.8" | sudo tee /etc/apt/sources.list.d/bazel.list
$ sudo apt update && sudo apt install bazel
Bazelは3.6.0でした。
$ bazel --version ~/src/xls
bazel 3.6.0
必要なパッケージをaptで追加して、
sudo apt install python3-distutils python3-dev libtinfo5
pythonと実行したらpython3が呼ばれるように工夫して
$ mkdir -p $HOME/opt/bin/
$ ln -s $(which python3) $HOME/opt/bin/python
$ export PATH=$HOME/opt/bin:$PATH
bazeでビルド。Core i9-9900T@2.1GHzのマシンで45分くらいかかりました。
$ git clone https://github.com/google/xls.git
$ cd xls
$ bazel test -c opt ...
ビルドが終わるとbazel-bin(シンボリックリンク)の下にあれこれ成果物が生成されます。
クイックスタートに従って使ってみる
サンプル
ビルドが終わったらクイックスタートに従って使ってみます。サンプルはこんな感じ。
fn add(x: u32, y: u32) -> u32 {
x + y + u32:0 // Something to optimize.
}
test add {
assert_eq(add(u32:2, u32:3), u32:5)
}
これを、(ガイドとは違って)./tmp/simpl_add.xに保存します。
インタプリタ実行
$ ./bazel-bin/xls/dslx/interpreter/interpreter_main ./tmp/simple_add.x
で、インタプリタで実行できるようです。結果は次のよう。
[ RUN UNITTEST ] add
[ OK ] add
ここで、assert_eq
の第二引数(予期する答え?)を 6
にしてみると
$ ./bazel-bin/xls/dslx/interpreter/interpreter_main ./tmp/simple_add.x
[ RUN UNITTEST ] add
./tmp/simple_add.x:4-8
0004:
0005: test add {
* 0006: assert_eq(add(u32:2, u32:3), u32:6)
~~~~~~~~~~~^------------------------^ The program being interpreted failed!
lhs: 5
rhs: 6
were not equal
0007: }
0008:
[ FAILED ] add FailureError
と、間違ってることを教えてくれました。
IRへの変換と最適化
クイックスタートガイドによるとインタプリタで実行したあとはIRを作るようです。
$ ./bazel-bin/xls/dslx/ir_converter_main ./tmp/simple_add.x > ./tmp/simple_add.ir
実行するとIRがファイルに書き出されました。細かいところはおいておいて、雰囲気はわかる、感じです。
package simple_add
fn __simple_add__add(x: bits[32], y: bits[32]) -> bits[32] {
add.3: bits[32] = add(x, y, id=3, pos=0,1,4)
literal.4: bits[32] = literal(value=0, id=4, pos=0,1,14)
ret add.5: bits[32] = add(add.3, literal.4, id=5, pos=0,1,8)
}
IRを作って最適化するよう。コマンドは次の通り。
$ ./bazel-bin/xls/tools/opt_main ./tmp/simple_add.ir > ./tmp/simple_add.opt.ir
最適化の結果でてきたのは
package simple_add
fn __simple_add__add(x: bits[32], y: bits[32]) -> bits[32] {
ret add.3: bits[32] = add(x, y, id=3, pos=0,1,4)
}
こんな感じのコード。元のIRが、「足し算して、その結果を返す」みたいな感じだったのに対して、最適化のあとは、「足し算の結果を返す」みたいな感じですね。
Verilogファイル生成
IRからVerilogファイルを生成できます。
$ ./bazel-bin/xls/tools/codegen_main --pipeline_stages=1 --delay_model=unit ./tmp/simple_add.opt.ir > ./tmp/simple_add.v
生成されたVerilogは結構シンプルでいいですね。
module __simple_add__add(
input wire clk,
input wire [31:0] x,
input wire [31:0] y,
output wire [31:0] out
);
// ===== Pipe stage 0:
// Registers for pipe stage 0:
reg [31:0] p0_x;
reg [31:0] p0_y;
always_ff @ (posedge clk) begin
p0_x <= x;
p0_y <= y;
end
// ===== Pipe stage 1:
wire [31:0] p1_add_3_comb;
assign p1_add_3_comb = p0_x + p0_y;
// Registers for pipe stage 1:
reg [31:0] p1_add_3;
always_ff @ (posedge clk) begin
p1_add_3 <= p1_add_3_comb;
end
assign out = p1_add_3;
endmodule
ちなみに、最適化前のIRからVerilogコードを生成することもできます。こんな感じでした。
module __simple_add__add(
input wire clk,
input wire [31:0] x,
input wire [31:0] y,
output wire [31:0] out
);
// ===== Pipe stage 0:
// Registers for pipe stage 0:
reg [31:0] p0_x;
reg [31:0] p0_y;
always_ff @ (posedge clk) begin
p0_x <= x;
p0_y <= y;
end
// ===== Pipe stage 1:
wire [31:0] p1_add_3_comb;
wire [31:0] p1_add_5_comb;
assign p1_add_3_comb = p0_x + p0_y;
assign p1_add_5_comb = p1_add_3_comb + 32'h0000_0000;
// Registers for pipe stage 1:
reg [31:0] p1_add_5;
always_ff @ (posedge clk) begin
p1_add_5 <= p1_add_5_comb;
end
assign out = p1_add_5;
endmodule
比べてみると、
assign p1_add_3_comb = p0_x + p0_y;
assign p1_add_5_comb = p1_add_3_comb + 32'h0000_0000;
のあたりは最適化前、って感じに見えますね。(このくらいならその後の合成ツールにまかせてもいい気もしつつ)
IRの可視化
IRの可視化ツールも同梱されていて、次のようにして起動します。ローカルのWebサーバーが立ち上がりブラウザで結果を確認できます。
$ ./bazel-bin/xls/visualization/ir_viz/app --delay_model=unit --ir_path=./tmp/simple_add.ir
結果はこんな感じでした。
ノードを選択するとハイライトされたりします。HTMLを見ると、グラフの描画には、dagreを使ってるのかな?
ちなみに、最適化後のIRを可視化した結果は下のような感じでした。
まとめ
というわけで、とりあえず Google の XLS を使ってみました。他のサンプルも見ながら、どんな最適化を実装してるのか、なんかを深堀りしてみようかな、とか思ってます。