FC2カウンター FPGAの部屋 Karuta
fc2ブログ

FPGAやCPLDの話題やFPGA用のツールの話題などです。 マニアックです。 日記も書きます。

FPGAの部屋

FPGAの部屋の有用と思われるコンテンツのまとめサイトを作りました。Xilinx ISEの初心者の方には、FPGAリテラシーおよびチュートリアルのページをお勧めいたします。

高位合成言語+ツールのKaruta を使ってみる4(AXIインターフェースのサンプル1)

高位合成言語+ツールのKaruta を使ってみる3(生成されたVerilog ファイルをシミュレーション)”の続き。

前回は、Karuta で生成したVerilog ファイルをシミュレーションして、動作するのに 20 クロックかかるのが分かった。今回は、AXIインターフェースのサンプルをやってみよう。

FPGA向け論理回路設計のためのプログラミング言語処理系 Karuta の紹介”の”アクセラレータを作れるか?”から AXI インターフェースのサンプルをやってみる。

FPGA向け論理回路設計のためのプログラミング言語処理系 Karuta の紹介”の”アクセラレータを作れるか?”からソースコードを引用する。ファイル名は AXI_example.karuta とした。

@AxiSlave()
shared regs int[4]

@AxiMaster()
shared buf int[4]

func main() {
  // slave側へのアクセスを待つ
  regs.waitAccess()
  // slaveの0ワード目の値をアドレスとして使う
  var addr int = regs[0]
  // メモリ上のaddrから2ワードをbuf[0]以降に読み込む
  buf.load(addr, 2, 0)
  buf[2] = buf[0] * buf[0] + buf[1] * buf[1]
  // メモリ上のaddr + 8に1ワード分buf[2]以降から書き込む
  buf.store(addr + 8, 1, 2)
}


karuta AXI_example.karuta --compile
コマンドで、Verilog ファイルを生成した。
karuta_16_191023.png

AXI_example.v が生成された。AXI Slave とAXI Master のポートが確認できる。
karuta_17_191023.png

RTL シミュレーションしてみたいが、Verilog ファイルベースで接続を書くのがとっても面倒だ。
なんとかして、ソースコードレベルでAXIインターフェースの接続を書きたい。
配列のアドレスを取得できれば、そのアドレスをAXI Slave でセットしてDMA できるのだが、独自言語なので、アドレスの取得の仕方が分からない?そもそも配列のアドレスが取得できるのだろうか?

@neonlightdev さんから、”--compile --with_shell --vcd”オプションを教えてもらったので、やってみた。
karuta AXI_example.karuta --compile --with_shell --vcd
karuta_18_191023.png

生成された AXI_example.v はシミュレーション用のテストベンチが付いていたが、やはり、トランザクションは自分で書く必要があるかも知れない?
karuta_19_191023.png

一応、Vivado 2019.1 で AXI_example プロジェクトを作成して、AXI_example.v を入れた。
karuta_20_191023.png

PROJECT MANAGER から SIMULATION -> Run Simulation -> Run Behavioral Simulation を選択した。
更に、Run All (F3) ボタンをクリックして、$finish まで実行した。
karuta_21_191023.png

AXI のトランザクションは無かった。
karuta_22_191023.png

配列のアドレスの書き方覚えて、ソースコード上でスティミュラスを書きたい。。。
  1. 2019年10月23日 04:56 |
  2. Karuta
  3. | トラックバック:0
  4. | コメント:0

高位合成言語+ツールのKaruta を使ってみる3(生成されたVerilog ファイルをシミュレーション)

高位合成言語+ツールのKaruta を使ってみる2(Verilog ファイルを生成)”の続き。

前回は、Karuta でサンプルコードをコンパイルして、Verilog ファイルを生成した。今回は、Karuta で生成したVerilog ファイルをシミュレーションしてみよう。

シミュレーションはVivado 2019.1 でVivaoSim を使用してRTL シミュレーションを行う。
calc.v は clk と rst のみの外部端子なので、リセットを外して、クロックを入れれば動作するはずだ。
まずは calc プロジェクトを作成して、テストベンチの calc_tb.v を作成した。
calc_tb.v を貼っておく。

// calc_tb.v
// 2019/10/22 by marsee

`default_nettype none
`timescale 1ns / 1ps

module calc_tb;
    wire clk;
    wire rst;
    
    calc calc_inst(
        .clk(clk),
        .rst(rst)
    );
    
    // clk_gen のインスタンス(clk)
    clk_gen #(
        .CLK_PERIOD(10),    // 10 nsec, 100 MHz
        .CLK_DUTY_CYCLE(0.5),
        .CLK_OFFSET(0),
        .START_STATE(1'b0)
    ) ACLKi (
        .clk_out(clk)
    );

    // rst のインスタンス
    reset_gen #(
        .RESET_STATE(1'b1),
        .RESET_TIME(100)    // 100nsec
    ) RESETi (
        .reset_out(rst),
        .init_done()
    );
endmodule

module clk_gen #(
    parameter         CLK_PERIOD = 100,
    parameter real    CLK_DUTY_CYCLE = 0.5,
    parameter        CLK_OFFSET = 0,
    parameter        START_STATE    = 1'b0 )
(
    output    reg        clk_out
);
    begin
        initial begin
            #CLK_OFFSET;
            forever
            begin
                clk_out = START_STATE;
                #(CLK_PERIOD-(CLK_PERIOD*CLK_DUTY_CYCLE)) clk_out = ~START_STATE;
                #(CLK_PERIOD*CLK_DUTY_CYCLE);
            end
        end
    end
endmodule

module reset_gen #(
    parameter    RESET_STATE = 1'b1,
    parameter    RESET_TIME = 100 )
(
    output    reg        reset_out,
    output    reg        init_done
);
    begin
        initial begin
            reset_out = RESET_STATE;
            init_done = 1'b0;
            #RESET_TIME;
            reset_out = ~RESET_STATE;
            init_done = 1'b1;
        end
    end
endmodule

`default_nettype wire


karuta_12_191022.png

PROJECT MANAGER から SIMULATION -> Run Simulation -> Run Behavioral Simulation を選択した。
論理シミュレーションが成功した。
karuta_13_191022.png

波形をFloat した。
karuta_14_191022.png

リセットが外れたクロックの立ち上がりから sram_wdate_en5 が 1 になって、SRAM に 25 を書くまでの実行している部分を拡大した。この間は 200 ns なので、 20 クロック分ということになる。
karuta_15_191022.png
  1. 2019年10月22日 20:25 |
  2. Karuta
  3. | トラックバック:0
  4. | コメント:0

高位合成言語+ツールのKaruta を使ってみる2(Verilog ファイルを生成)

高位合成言語+ツールのKaruta を使ってみる1(Karuta をインストール)”の続き。

前回は、Karuta をインストールして、”FPGA向け論理回路設計のためのプログラミング言語処理系 Karuta の紹介”からサンプルコードを動作させてみた。今回は、そのサンプルコードをコンパイルして、Verilog ファイルを生成してみよう。

FPGA向け論理回路設計のためのプログラミング言語処理系 Karuta の紹介”から引用したソースコードを示す。

shared x int[4]

func main() {
  var t int = 0
  for var i int = 0; i < 2; ++i {
    t += x[i] * x[i]
  }
  x[2] = t
}
x[0] = 3
x[1] = 4
main()
print(x[2])


これを calc.karuta としてセーブしてある。前回、動作することを確認してある。

さて、コンパイルして、Verilog ファイルを生成しよう。
karuta calc.karuta --complie
を実行した。
karuta_11_191022.png

calc.iroha と calc.v が生成されている。
calc.iroha を貼っておく。

(PARAMS
 (RESET-POLARITY true))
(PLATFORM default
  (DEF
    (COND (AND))
    (VALUE (DELAY 1))))
(ARRAY-IMAGE 1 ()
 (3 4 25 0))
(MODULE 1 main
  (PARAMS)
  (TABLE 1 $thr_main
    (REGISTERS
      (REGISTER 1 anon_1
        CONST (UINT 32) 0
      )
      (REGISTER 2 anon_2
        CONST (UINT 32) 0
      )
      (REGISTER 3 anon_3
        CONST (UINT 32) 2
      )
      (REGISTER 4 anon_4
        CONST (UINT 32) 1
      )
      (REGISTER 5 anon_5
        CONST (UINT 32) 2
      )
      (REGISTER 7 main_t_1_8
        REG (UINT 32) ()
      )
      (REGISTER 8 main_i_2_9
        REG (UINT 32) ()
      )
      (REGISTER 9 main_3_10
        REG (UINT 0) ()
      )
      (REGISTER 10 main_4_11
        REG (UINT 32) ()
      )
      (REGISTER 11 main_5_12
        REG (UINT 32) ()
      )
      (REGISTER 12 main_6_13
        REG (UINT 32) ()
      )
      (REGISTER 13 main_7_14
        REG (UINT 32) ()
      )
      (REGISTER 6 main_5_12_w0i23
        WIRE (UINT 32) ()
      )
    )
    (RESOURCES
      (RESOURCE 1 tr
        () ()
        (PARAMS)
      )
      (RESOURCE 3 set
        () ()
        (PARAMS)
      )
      (RESOURCE 4 gt
        ((UINT 32) (UINT 32)) ((UINT 0))
        (PARAMS)
      )
      (RESOURCE 5 array
        () ()
        (PARAMS)
        (ARRAY 2 (UINT 32) INTERNAL RAM 1)
      )
      (RESOURCE 6 mul
        ((UINT 32) (UINT 32)) ((UINT 32))
        (PARAMS)
      )
      (RESOURCE 7 add
        ((UINT 32) (UINT 32)) ((UINT 32))
        (PARAMS)
      )
    )
    (INITIAL 1)
    (STATE 1
      (PROFILE 1 17)
      (INSN 1 tr 1 () (8) () () ())
      (INSN 4 set 3 () () (1) (7) ())
      (INSN 7 set 3 () () (2) (8) ())
    )
    (STATE 8
      (PROFILE 1 17)
      (INSN 11 tr 1 () (9) () () ())
      (INSN 10 gt 4 () () (3 8) (9) ())
    )
    (STATE 9
      (PROFILE 1 17)
      (INSN 12 tr 1 () (26 12) (9) () ())
    )
    (STATE 12
      (PROFILE 1 17)
      (INSN 16 tr 1 () (13) () () ())
      (INSN 15 array 5 (sram_read_address) () (8) () ())
    )
    (STATE 13
      (PROFILE 1 17)
      (INSN 18 tr 1 () (16) () () ())
      (INSN 17 array 5 (sram_read_data) () () (10) ())
    )
    (STATE 16
      (PROFILE 1 17)
      (INSN 22 tr 1 () (17) () () ())
      (INSN 21 array 5 (sram_read_address) () (8) () ())
    )
    (STATE 17
      (PROFILE 1 17)
      (INSN 24 tr 1 () (18) () () ())
      (INSN 23 array 5 (sram_read_data) () () (6) ())
      (INSN 25 mul 6 () () (10 6) (12) ())
      (INSN 31 add 7 () () (8 4) (8) ())
      (INSN 2 set 3 () () (6) (11) ())
    )
    (STATE 18
      (PROFILE 1 17)
      (INSN 26 tr 1 () (19) () () ())
      (INSN 27 add 7 () () (7 12) (13) ())
    )
    (STATE 19
      (PROFILE 1 17)
      (INSN 28 tr 1 () (8) () () ())
      (INSN 29 set 3 () () (13) (7) ())
    )
    (STATE 26
      (PROFILE 1 17)
      (INSN 38 tr 1 () (29) () () ())
      (INSN 37 array 5 (sram_write) () (5 7) () ())
    )
    (STATE 29
      (PROFILE 1 17)
    )
  )
)


次に calc.v を貼っておく。

// Generated from iroha-0.0.1.

// SRAM(1 port(s))
`ifndef SRAM_2_32_defined
 `define SRAM_2_32_defined
module SRAM_2_32(
  input clk,
  input rst,
  input [1:0] addr_i,
  output reg [31:0] rdata_o,
  input [31:0] wdata_i,
  input write_en_i);

  reg [31:0] data [0:3];

  always @(posedge clk) begin
    if (rst) begin
      data[0] <= 3;
      data[1] <= 4;
      data[2] <= 25;
      data[3] <= 0;
    end else begin
      if (write_en_i) begin
        data[addr_i] <= wdata_i;
      end
    end
  end
  // Read
  always @(addr_i or clk) begin
    rdata_o = data[addr_i];
  end
endmodule
`endif  // SRAM_2_32_defined


// Module 1;
module calc_main(
  input clk,
  input rst);

  // State decls
  // state names
  localparam S_1_1 = 1;
  localparam S_1_8 = 8;
  localparam S_1_9 = 9;
  localparam S_1_12 = 12;
  localparam S_1_13 = 13;
  localparam S_1_16 = 16;
  localparam S_1_17 = 17;
  localparam S_1_18 = 18;
  localparam S_1_19 = 19;
  localparam S_1_26 = 26;
  localparam S_1_29 = 29;
  reg [5:0] st_1;

  // Shared wires
  // Shared insn wires
  // Shared insn assigns

  // Registers for table 1
  reg  [31:0] main_t_1_8; // main_t_1_8
  reg  [31:0] main_i_2_9; // main_i_2_9
  reg  main_3_10; // main_3_10
  reg  [31:0] main_4_11; // main_4_11
  reg  [31:0] main_5_12; // main_5_12
  reg  [31:0] main_6_13; // main_6_13
  reg  [31:0] main_7_14; // main_7_14
  wire  [31:0] main_5_12_w0i23; // main_5_12_w0i23

  // Insn wires for table 1
  wire  [31:0] insn_o_1_4_0;
  wire  [31:0] insn_o_1_7_0;
  wire  insn_o_1_10_0;
  wire  [31:0] insn_o_1_17_0;
  wire  [31:0] insn_o_1_23_0;
  wire  [31:0] insn_o_1_25_0;
  wire  [31:0] insn_o_1_31_0;
  wire  [31:0] insn_o_1_2_0;
  wire  [31:0] insn_o_1_27_0;
  wire  [31:0] insn_o_1_29_0;

  // Insn values for table 1
  assign insn_o_1_10_0 = gt_1_4_d0;
  assign insn_o_1_17_0 = sram_rdata_5;
  assign insn_o_1_23_0 = sram_rdata_5;
  assign main_5_12_w0i23 = insn_o_1_23_0;
  assign insn_o_1_25_0 = mul_1_6_d0;
  assign insn_o_1_31_0 = add_1_7_d0;
  assign insn_o_1_27_0 = add_1_7_d0;

  // Resources for table 1
  // gt:4
  wire [31:0] gt_1_4_s0;
  assign gt_1_4_s0 = 32'd2;
  wire [31:0] gt_1_4_s1;
  assign gt_1_4_s1 = main_i_2_9;
  wire gt_1_4_d0;
  assign gt_1_4_d0 = gt_1_4_s0 > gt_1_4_s1;
  reg [1:0] sram_addr_5;
  wire [31:0] sram_rdata_5;
  reg [31:0] sram_wdata_5;
  reg sram_wdata_en_5;
  // mul:6
  wire [31:0] mul_1_6_s0;
  assign mul_1_6_s0 = main_4_11;
  wire [31:0] mul_1_6_s1;
  assign mul_1_6_s1 = main_5_12_w0i23;
  wire [31:0] mul_1_6_d0;
  assign mul_1_6_d0 = mul_1_6_s0 * mul_1_6_s1;
  // add:7
  wire [31:0] add_1_7_s0;
  assign add_1_7_s0 = (st_1 == S_1_18) ? main_t_1_8 : (main_i_2_9);
  wire [31:0] add_1_7_s1;
  assign add_1_7_s1 = (st_1 == S_1_18) ? main_6_13 : (32'd1);
  wire [31:0] add_1_7_d0;
  assign add_1_7_d0 = add_1_7_s0 + add_1_7_s1;

  // Table 1
  always @(posedge clk) begin
    if (rst) begin
      st_1 <= S_1_1;
    end else begin
      sram_wdata_en_5 <= (st_1 == 26);
      case (st_1)
        S_1_1: begin
          main_t_1_8 <= 32'd0;
          main_i_2_9 <= 32'd0;
          st_1 <= S_1_8;
        end
        S_1_8: begin
          main_3_10 <= insn_o_1_10_0;
          st_1 <= S_1_9;
        end
        S_1_9: begin
          if (main_3_10) begin
            st_1 <= S_1_12;
          end else begin
            st_1 <= S_1_26;
          end
        end
        S_1_12: begin
          sram_addr_5 <= main_i_2_9;
          st_1 <= S_1_13;
        end
        S_1_13: begin
          main_4_11 <= insn_o_1_17_0;
          st_1 <= S_1_16;
        end
        S_1_16: begin
          sram_addr_5 <= main_i_2_9;
          st_1 <= S_1_17;
        end
        S_1_17: begin
          main_5_12 <= main_5_12_w0i23;
          main_6_13 <= insn_o_1_25_0;
          main_i_2_9 <= insn_o_1_31_0;
          st_1 <= S_1_18;
        end
        S_1_18: begin
          main_7_14 <= insn_o_1_27_0;
          st_1 <= S_1_19;
        end
        S_1_19: begin
          main_t_1_8 <= main_7_14;
          st_1 <= S_1_8;
        end
        S_1_26: begin
          sram_addr_5 <= 32'd2;
          sram_wdata_5 <= main_t_1_8;
          st_1 <= S_1_29;
        end
        S_1_29: begin
        end
      endcase
    end
  end
  SRAM_2_32 SRAM_2_32_inst_5(.clk(clk), .rst(rst), .addr_i(sram_addr_5), .rdata_o(sram_rdata_5), .wdata_i(sram_wdata_5), .write_en_i(sram_wdata_en_5));

endmodule // main
module calc(
  input clk,
  input rst);
  calc_main calc_main_inst(.clk(clk), .rst(rst));

endmodule

// NOTE: Please copy the follwoing line to your design.
// calc calc_inst(.clk(), .rst());
// NOTE: This can be used by your script to auto generate the instantiation and connections.
// :connection: calc:calc_inst input:0::clk,input:0::rst

  1. 2019年10月22日 05:39 |
  2. Karuta
  3. | トラックバック:0
  4. | コメント:0

高位合成言語+ツールのKaruta を使ってみる1(Karuta をインストール)

高位合成言語+ツールのKaruta を使ってみようと思う。
Karuta の記事としては、次のものがある。
FPGA向け論理回路設計のためのプログラミング言語処理系 Karuta の紹介
Karuta言語でLチカ(LED点滅)を書いてみる

Karuta のGitHub が nlsynth/karuta にある。

まずは、私のパソコンのOS はUbutnu 18.04 なので、GitHub で紹介されている様にインストールする。
sudo snap install karuta
karuta_1_191021.png

インストールはとても簡単だ。

Karuta のソースコードや生成物を置くために Karuta_work を作った。
mkdir Karuta_work
cd Karuta_work


Karuta_work に”FPGA向け論理回路設計のためのプログラミング言語処理系 Karuta の紹介”からコードを引用する。まずは、hello.karuta を引用する。コードをコピー&ペーストした。
karuta_2_191021.png

func main() {
  print(“Hello, world!”)
}
main()


ターミナルで、次のコマンドを実行した。
karuta hello.karuta
karuta_3_191021.png

シンタックスエラーだった。
え〜。なんで? print 文だけにしてやってみた。
karuta_4_191021.png

print(“Hello, world!”)


やはりシンタックスエラーだった。
karuta_5_191021.png

ソースコードを見ているうちに、”がおかしいことに気がついた。あ〜、”が全角だ。。。すかさず半角に修正した。
karuta_6_191021.png

もう一度
karuta hello.karuta
を実行すると、”Hello, world!”が表示された。やはり、”が全角なのが問題のようだ。
karuta_7_191021.png

func main() {
    print("Hello, world!")
}
main()

も、”を全角から半角に変更したら、”Hello, world!”が表示された。

FPGA向け論理回路設計のためのプログラミング言語処理系 Karuta の紹介”の次の演算のチュートリアルをやってみよう。
calc.karuta とファイル名を付けた。ソースコードを引用する。
karuta_8_191021.png

shared x int[4]

func main() {
  var t int = 0
  for (var i int = 0; i < 2; ++i) {
    t += x[i] * x[i]
  }
  x[2] = t
}
x[0] = 3
x[1] = 4
main()
print(x[2])


karuta calc.karuta
を実行した。5行目でエラーになった。今度は何でエラーになるのだろう?
karuta_9_191021.png

(2019/10/22:追記)
Karuta の開発者の @neonlightdev さんに教えてもらったのですが、( ) が要らないそうです。

shared x int[4]

func main() {
  var t int = 0
  for var i int = 0; i < 2; ++i {
    t += x[i] * x[i]
  }
  x[2] = t
}
x[0] = 3
x[1] = 4
main()
print(x[2])


これで
karuta calc.karuta
すると、 25 という結果がでました。
karuta_10_191022.png
  1. 2019年10月21日 05:21 |
  2. Karuta
  3. | トラックバック:0
  4. | コメント:0