忍者ブログ

NOROMA CLUB -日記-

2022年05月21日 Pythonで高位合成

PythonのプログラムからVerilogのRTLに変換できる高位合成環境が
あるようなので試しに使ってみました。

以下は、いろいろメモです。

polyphonyをインストールする。
% pip3 install polyphony

高位合成実行。
% polyphony -vm -vd fact.py
で、Verilogファイルが生成できました。

結果的にはすごく簡単なのですが、参考にしたWebページのとおりにやっても
うまく動かなかったりで、途中過程は意外と苦労しました。。。

ちなみに、変換前後のコードは最後の方に貼り付けておきます。
可読性はあまり良くないですが、一応それらしい回路が出力されているようです。


あと、「Icarus Verilog」という、OSSのVerilogシミュレータも入れてみました。

Icarus Verilogをインストールする。
sudo apt install iverilog
sudo apt install gtkwave

SIM実行
iverilog -o sim -s test test.v polyphony_out.v
./sim

波形確認
gtkwave test.vcd  &
でGUIが立ち上がるので、適当に操作して波形表示

ちなみに、今回はUbuntu(Jetson Nano)で試していますが、
Windows版もこちらにあるようです(試していませんが)。

ひとまず簡単なプログラムであれば、Verilogに変換できることはわかったのですが、
深堀できていないので、使い物になるのかどうかはまだよくわかりません。

とりあえずNumPy とかの便利なライブラリは使えないようです(コンパイルでエラーになるので)。
あとは、ネットで検索しても、polyphonyについて詳しく説明しているページがあまりなさそうで、
polyphony公式のGitHubのサンプルプログラムを見ながら試していくしかなさそうな感じですかね。


以下、階乗計算プログラムの変換前後のコード例です。


Pythonプログラム
from polyphony import testbench
def fact(x):
    y = 1
    for i in range(1, x+1):
        y = y * i
    return y
@testbench
def test():
    for i in range(0,10):
        fact(i)
test()

Verilog RTL(本体)
module fact
  (
    input wire clk,
    input wire rst,
    input wire fact_ready,
    input wire fact_accept,
    output reg fact_valid,
    input wire signed [31:0] fact_in_x,
    output reg signed [31:0] fact_out_0
  );
  //localparams
  localparam fact_b1_INIT = 0;
  localparam fact_b1_S0 = 1;
  localparam fact_b1_S1 = 2;
  localparam fact_forelse5_FINISH = 3;
  localparam fact_L1_fortest2_S0 = 4;
  localparam fact_L1_forbody3_S0 = 5;
  localparam fact_L1_forbody3_S1 = 6;
  
  //signals: 
  wire c71;
  reg        [2:0] fact_state;
  wire signed [31:0] i3;
  wire signed [31:0] t65;
  wire signed [31:0] y3;
  reg signed [31:0] i2;
  reg signed [31:0] x;
  reg signed [31:0] y2;
  //combinations: 
  assign c71 = (i2 < t65);
  assign i3 = (i2 + 1);
  assign t65 = (x + 1);
  assign y3 = (y2 * i2);
  
  always @(posedge clk) begin
    if (rst) begin
      fact_out_0 <= 0;
      i2 <= 0;
      x <= 0;
      y2 <= 0;
      fact_state <= fact_b1_INIT;
    end else begin //if (rst)
      case(fact_state)
      fact_b1_INIT: begin
        fact_valid <= 0;
        if (fact_ready == 1) begin
          x <= fact_in_x;
          i2 <= 1;
          y2 <= 1;
          fact_state <= fact_b1_S1;
        end
      end
      fact_b1_S1: begin
        /* t65 <= (x + 1); */
        fact_state <= fact_L1_fortest2_S0;
      end
      fact_L1_fortest2_S0: begin
        /* c71 <= (i2 < t65); */
        if (c71) begin
          /* y3 <= (y2 * i2); */
          /* i3 <= (i2 + 1); */
          i2 <= i3;
          y2 <= y3;
          fact_state <= fact_L1_fortest2_S0;
        end else begin
          fact_valid <= 1;
          if (fact_accept == 1) begin
            fact_state <= fact_b1_INIT;
          end
          fact_out_0 <= y2;
        end
      end
      endcase
    end
  end
  
endmodule

Verilog RTL(テストベンチ)
module test
  (
    
  );
  //localparams
  localparam CLK_PERIOD = 10;
  localparam CLK_HALF_PERIOD = 5;
  localparam INITIAL_RESET_SPAN = 100;
  localparam test_b1_INIT = 0;
  localparam test_b1_S1 = 1;
  localparam test_forelse5_S0 = 2;
  localparam test_forelse5_FINISH = 3;
  localparam test_L1_fortest2_S0 = 4;
  localparam test_L1_forbody3_S0 = 5;
  localparam test_L1_forbody3_S1 = 6;
  localparam test_L1_forbody3_S2 = 7;
  localparam test_L1_forbody3_S3 = 8;
  localparam test_L1_forbody3_S4 = 9;
  localparam test_L1_forbody3_S5 = 10;
  localparam test_L1_forbody3_S6 = 11;
  localparam test_L1_forbody3_S7 = 12;
  
  //signals: 
  wire c69;
  wire fact_0_valid;
  reg clk;
  reg fact_0_accept;
  reg fact_0_ready;
  reg rst;
  reg        [3:0] test_state;
  reg signed [31:0] i2;
  reg signed [31:0] i3;
  //signals: in_x
  reg        [31:0] fact_0_in_x;
  //signals: out_0
  wire        [31:0] fact_0_out_0;
  //combinations: 
  assign c69 = (i2 < 10);
  //sub modules
  //fact_0 instance
  fact fact_0(
    .clk(clk),
    .rst(rst),
    .fact_ready(fact_0_ready),
    .fact_accept(fact_0_accept),
    .fact_valid(fact_0_valid),
    .fact_in_x(fact_0_in_x),
    .fact_out_0(fact_0_out_0)
  );
  
  
  initial begin
    $monitor("%5t:fact_0_in_x=%4d, fact_0_out_0=%4d", $time, fact_0_in_x, fact_0_out_0);
    $dumpfile("test.vcd");
    $dumpvars(0, test);
  end
  initial begin
    clk = 0;
    #CLK_HALF_PERIOD
    forever #CLK_HALF_PERIOD clk = ~clk;
  end
  initial begin
    rst <= 1;
    #INITIAL_RESET_SPAN
    rst <= 0;
  end
  
  always @(posedge clk) begin
    if (rst) begin
      fact_0_accept <= 0;
      fact_0_ready <= 0;
      i2 <= 0;
      i3 <= 0;
      test_state <= test_b1_INIT;
    end else begin //if (rst)
      case(test_state)
      test_b1_INIT: begin
        i2 <= 0;
        test_state <= test_L1_fortest2_S0;
      end
      test_forelse5_FINISH: begin
        $display("%5t:finish", $time);
        $finish();
      end
      test_L1_fortest2_S0: begin
        /* c69 <= (i2 < 10); */
        if (c69) begin
          fact_0_ready <= 1;
          fact_0_in_x <= i2;
          test_state <= test_L1_forbody3_S1;
        end else begin
          test_state <= test_forelse5_FINISH;
        end
      end
      test_L1_forbody3_S1: begin
        fact_0_ready <= 0;
        test_state <= test_L1_forbody3_S2;
      end
      test_L1_forbody3_S2: begin
        if (fact_0_valid == 1) begin
          fact_0_accept <= 1;
          test_state <= test_L1_forbody3_S4;
        end
      end
      test_L1_forbody3_S4: begin
        fact_0_accept <= 0;
        test_state <= test_L1_forbody3_S5;
      end
      test_L1_forbody3_S5: begin
        i3 <= (i2 + 1);
        test_state <= test_L1_forbody3_S6;
      end
      test_L1_forbody3_S6: begin
        i2 <= i3;
        test_state <= test_L1_fortest2_S0;
      end
      endcase
    end
  end
  
endmodule

拍手[0回]

PR