FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

Espressoを使ってSystem Verilogの Decoder Wrapperを作ってみる (1)

EspressoというのはEspresso Logic Minimizerというソフトウェアで、いわゆる学校で習うカルノー図のように、論理を簡単化するためのソフトウェアである。おそらくこの問題には常に最適な解を算出するアルゴリズムというのは存在しないので、Heuristicなアルゴリズムという説明が付いている。

en.wikipedia.org

このツールの使い方については、以前以下のブログで紹介した。

msyksphinz.hatenablog.com

で、これをもう少し使いやすくするためにWrapperを作ることにした。具体的には、以下のようなJSONファイルを読み込んで(いつも使っているRISC-Vのデコード用ファイル)、System Verilogデコーダを自動的に生成するようなラッパーを書きたい。

[
    {
        "name":"lui        r[11:7],h[31:12]",
        "length":"32", "dlength":"32",
        "field": ["XXXXX", "XX", "XXXXX", "XXXXX", "XXX", "XXXXX", "01101", "11"],
        "category":"ALU", "func_suffix":"",
        "impl":[""],
        "inst_ctrl":["RD_R3", "OP_SIGN_ADD", "IMM_U"]
    },
    {
        "name":"auipc      r[11:7],h[31:12]",
        "length":"32", "dlength":"32",
        "field": ["XXXXX", "XX", "XXXXX", "XXXXX", "XXX", "XXXXX", "00101", "11"],
        "category":"ALU",
        "func_suffix":"",
        "impl":[""],
        "inst_ctrl":["R1_PC", "RD_R3", "OP_SIGN_ADD", "IMM_U"]
    },
    {
...

まず、JSONを受け取ってinst_ctrlフィールドを取り出し、すべての制御信号情報を読み取って、どの命令でどのフィールドを出力するのか指定する。例えば、5つの命令から12個の制御信号を生成するために、Espresso用の以下の入力ファイルを作り上げる。

type fd
.i 32
.o 12
.ilb inst[0] inst[1] inst[2] inst[3] inst[4] inst[5] inst[6] inst[7] inst[8] inst[9] inst[10] inst[11] inst[12] inst[13] inst[14] inst[15] inst[16] inst[17] inst[18] inst[19] inst[20] inst[21] inst[22]
inst[23] inst[24] inst[25] inst[26] inst[27] inst[28] inst[29] inst[30] inst[31]
.ob RD_R3 OP_SIGN_ADD IMM_U R1_PC R1_R1 R2_R2 SIZE_DW SIGN_S MEM_L IMM_I MEM_S IMM_S
-------------------------0110111 111000000000
-------------------------0010111 111100000000
0000000----------000-----0110011 110011000000
-----------------011-----0000011 100011111100
-----------------011-----0100011 000011110011
.e

そしてこれをEspressoに流し込むわけだ。結果を回収し、それを加工する。かなり適当だが以下のようにした。

puts "module decoder ();"

result_line.each_with_index{|line, index|
  inst_field = line.split(' ')[0]

  print "wire tmp_" + index.to_s + " = "
  inst_field.chars.each_with_index{|char, ch_idx|
    inst_index = inst_length - ch_idx - 1
    if char == '0' then
      print "!inst[" + inst_index.to_s + "] & "
    elsif char == '1' then
      print "inst[" + inst_index.to_s + "] & "
    end
  }
  print "1'b1;\n"
}

ctrl_fields.each_with_index{|ctrl, ct_index|
  print "assign " + ctrl + " = "
  result_line.each_with_index{|res, res_index|
    ctrl_field = res.split(' ')[1]
    if ctrl_field[ct_index] == '1' then
      print "tmp_" + res_index.to_s + " | "
    end
  }
  print "1'b0;\n"
}

puts "endmodule"

以下のようなSystemVerilogのデコーダを出力する。コンパイルしていないけど(っていうかインタフェースを整えていないのでまだシミュレーションできないのだけれども)、ひな型としては良さそうだ。

module decoder ();
wire tmp_0 = !inst[31] & !inst[30] & !inst[29] & !inst[28] & !inst[27] & !inst[26] & !inst[25] & !inst[14] & !inst[13] & !inst[12] & !inst[6] & inst[5] & inst[4] & !inst[3] & !inst[2] & inst[1] & inst[0] & 1'b1;
wire tmp_1 = !inst[6] & !inst[5] & inst[4] & !inst[3] & inst[2] & inst[1] & inst[0] & 1'b1;
wire tmp_2 = !inst[14] & inst[13] & inst[12] & !inst[6] & inst[5] & !inst[4] & !inst[3] & !inst[2] & inst[1] & inst[0] & 1'b1;
wire tmp_3 = !inst[14] & inst[13] & inst[12] & !inst[6] & !inst[5] & !inst[4] & !inst[3] & !inst[2] & inst[1] & inst[0] & 1'b1;
wire tmp_4 = !inst[6] & inst[4] & !inst[3] & inst[2] & inst[1] & inst[0] & 1'b1;
assign RD_R3 = tmp_0 | tmp_3 | tmp_4 | 1'b0;
assign OP_SIGN_ADD = tmp_0 | tmp_4 | 1'b0;
assign IMM_U = tmp_4 | 1'b0;
assign R1_PC = tmp_1 | 1'b0;
assign R1_R1 = tmp_0 | tmp_2 | tmp_3 | 1'b0;
assign R2_R2 = tmp_0 | tmp_2 | tmp_3 | 1'b0;
assign SIZE_DW = tmp_2 | tmp_3 | 1'b0;
assign SIGN_S = tmp_2 | tmp_3 | 1'b0;
assign MEM_L = tmp_3 | 1'b0;
assign IMM_I = tmp_3 | 1'b0;
assign MEM_S = tmp_2 | 1'b0;
assign IMM_S = tmp_2 | 1'b0;
endmodule