見出し画像

Google XLSを使ってみた

背景

Googleがオープンソースで公開した高位合成処理系(半導体設計ソフトウェア)XLSをインストールしてみた。

インストール環境

Ryzen5ノートPC
VertualBox
ubuntu

インストール

$ git clone https://github.com/google/xls.git xls
$ cd xls
$ bazel test -c opt ...

一晩ほどコンパイルにかかりました。VertualBoxの仮想環境なので動作策度が遅いです。

自宅のPCにインストールしてみました

Intel N100
ubuntu

バイナリを使えば、コンパイルするよりずっと容易にインストールできました。

# Determine the url of the latest release tarball.
LATEST_XLS_RELEASE_TARBALL_URL=$(curl -s -L \
  -H "Accept: application/vnd.github+json" \
  -H "X-GitHub-Api-Version: 2022-11-28" \
  https://api.github.com/repos/google/xls/releases | \
  grep -m 1 -o 'https://.*/releases/download/.*\.tar\.gz')

# Download the tarball and unpack it, observe the version numbers for each of the included tools.
curl -O -L ${LATEST_XLS_RELEASE_TARBALL_URL}
tar -xzvvf xls-*.tar.gz
cd xls-*/
./interpreter_main --version
./ir_converter_main --version
./opt_main --version
./codegen_main --version
./proto_to_dslx_main --version

動かしてみる

Hellow

Hellow XLSから

fn hello_xls(hello_string: u8[11]) {
    trace!(hello_string);
  }
#[test]
fn hello_test() {
  hello_xls("Hello, XLS!")
}

コマンドは
interpreter_main

nishimura@nishimura-Default-string:~$ ./xls-v0.0.0-6516-gf59d49721-linux-x64/interpreter_main hellow.x 
[ RUN UNITTEST  ] hello_test
[            OK ]
[===============] 1 test(s) ran; 0 failed; 0 skipped.

「hello_test」と表示されています。OK。

float_to_int

pub struct float32 {
    sign: u1,
    bexp: u8,
    fraction: u23,
}
  
fn unbias_exponent(exp: u8) -> s9 {
    exp as s9 - s9:127
}
  
pub fn float_to_int(x: float32) -> s32 {
    let exp = unbias_exponent(x.bexp);
  
    // Add the implicit leading one.
    // Note that we need to add one bit to the fraction to hold it.
    let fraction = u33:1 << 23 | (x.fraction as u33);
  
    // Shift the result to the right if the exponent is less than 23.
    let fraction =
        if (exp as u8) < u8:23 { fraction >> (u8:23 - (exp as u8)) }
        else { fraction };
  
    // Shift the result to the left if the exponent is greater than 23.
    let fraction =
        if (exp as u8) > u8:23 { fraction << ((exp as u8) - u8:23) }
        else { fraction };
  
    let result = fraction as s32;
    let result = if x.sign { -result } else { result };
    result
}

#[test]
fn float_to_int_test() {
  // 0xbeef in float32.
  let test_input = float32 {
    sign: u1:0x0,
    bexp: u8:0x8e,
    fraction: u23:0x3eef00
  };
  assert_eq(s32:0xbeef, float_to_int(test_input))
}

実行結果

nishimura@nishimura-Default-string:~$ ./xls-v0.0.0-6516-gf59d49721-linux-x64/interpreter_main float.x 
[ RUN UNITTEST  ] float_to_int_test
[            OK ]
[===============] 1 test(s) ran; 0 failed; 0 skipped.

テスト項目が不足な気がしますが、OKですね。

mul_add

fn muladd(a: u8, b: u8, c: u8) -> u8 {
    a * b + c
}

#[test]
fn muladd_test() {
  assert_eq(muladd(u8:1, u8:2, u8:3), u8:5);
  assert_eq(muladd(u8:4, u8:4, u8:4), u8:20);
  assert_eq(muladd(u8:127, u8:2, u8:2), u8:0);
}

実行結果

haruhiko@linux:~/xls$ ./bazel-bin/xls/dslx/interpreter_main mul_add.x 
[ RUN UNITTEST  ] muladd_test
[            OK ]
[===============] 1 test(s) ran; 0 failed; 0 skipped.

はい。OK。

ir表現

xlsコード→ir表現→Verilogと変換していきます。
ir表現とは中間コードです。

mul_addでは?

nishimura@nishimura-Default-string:~$ ./xls-v0.0.0-6516-gf59d49721-linux-x64/ir_converter_main --top=muladd mul_add.x 
package mul_add

file_number 0 "mul_add.x"

top fn __mul_add__muladd(a: bits[8] id=1, b: bits[8] id=2, c: bits[8] id=3) -> bits[8] {
  umul.4: bits[8] = umul(a, b, id=4, pos=[(0,1,4)])
  ret add.5: bits[8] = add(umul.4, c, id=5, pos=[(0,1,4)])
}

packegeの下がir表現です。
なんとなく処理は分かるよね。

最適化してみると 

nishimura@nishimura-Default-string:~$ ./xls-v0.0.0-6516-gf59d49721-linux-x64/opt_main mul_add.ir 
package mul_add

file_number 0 "mul_add.x"

top fn __mul_add__muladd(a: bits[8] id=1, b: bits[8] id=2, c: bits[8] id=3) -> bits[8] {
  umul.4: bits[8] = umul(a, b, id=4, pos=[(0,1,4)])
  ret add.5: bits[8] = add(umul.4, c, id=5, pos=[(0,1,4)])
}

Verilogに変換してみる

ishimura@nishimura-Default-string:~$ ./xls-v0.0.0-6516-gf59d49721-linux-x64/codegen_main --pipeline_stages=1 --delay_model=unit mul_add_opt.ir
module __mul_add__muladd(
  input wire clk,
  input wire [7:0] a,
  input wire [7:0] b,
  input wire [7:0] c,
  output wire [7:0] out
);
  // lint_off MULTIPLY
  function automatic [7:0] umul8b_8b_x_8b (input reg [7:0] lhs, input reg [7:0] rhs);
    begin
      umul8b_8b_x_8b = lhs * rhs;
    end
  endfunction
  // lint_on MULTIPLY

  // ===== Pipe stage 0:

  // Registers for pipe stage 0:
  reg [7:0] p0_a;
  reg [7:0] p0_b;
  reg [7:0] p0_c;
  always_ff @ (posedge clk) begin
    p0_a <= a;
    p0_b <= b;
    p0_c <= c;
  end

  // ===== Pipe stage 1:
  wire [7:0] p1_umul_15_comb;
  wire [7:0] p1_add_16_comb;
  assign p1_umul_15_comb = umul8b_8b_x_8b(p0_a, p0_b);
  assign p1_add_16_comb = p1_umul_15_comb + p0_c;

  // Registers for pipe stage 1:
  reg [7:0] p1_add_16;
  always_ff @ (posedge clk) begin
    p1_add_16 <= p1_add_16_comb;
  end
  assign out = p1_add_16;
endmodule

--pipeline_stages=1
これを2とか3にすると当然、結果が変わってきます。面白いですよ。
もっと複雑な回路なら人の手を超える最適化された記述になるのでしょうね。

所感

データフローをグラフ表現(私はまだ勉強していません)で表示して、最適化などを実施するのと、Verilogに変換するときにパイプラインの段数を指定することができます。ロジック技術者が経験と勘で、「この辺にレジスタを置いてパイプラインにするか?」の所を自動化するツールみたいです。
習得コスト(と時間)に見合った効果は???

沖縄でワーカホリックの私

いいなと思ったら応援しよう!