Vengineerの妄想

人生を妄想しています。

Xilinx Vitis の中を調べる(その6)

はじめに

Xilinx Vitis の中を調べるのその6。

今回は、C/RTL Cosimulation のテストベンチの構造をみてみます。

トップテストベンチ

トップテストベンチは、

  • multi_apuint.autotb.v

のようです。

multi_apuint.autotb.v の中を覗いてみたら、下記のようになっています。トップテストベンチのモジュール名は、AUTOTB_TOP マクロで、AUTOTB_TOPマクロは、apatb_multi_apuint_top です。

// ==============================================================
// Vitis HLS - High-Level Synthesis from C, C++ and OpenCL v2022.1 (64-bit)
// Tool Version Limit: 2022.04
// Copyright 1986-2022 Xilinx, Inc. All Rights Reserved.
// ==============================================================
 `timescale 1ns/1ps

`define AUTOTB_DUT      multi_apuint
`define AUTOTB_DUT_INST AESL_inst_multi_apuint
`define AUTOTB_TOP      apatb_multi_apuint_top
.... 途中略

module `AUTOTB_TOP;

ちょっと下にいくと、DUTがあります。DUTは、AUTOTB_DUTマクロのモジュールをAUTOTB_DUT_INSTマクロのインスタンスです。

  • `AUTOTB_DUT => multi_apuint
  • `AUTOTB_DUT_INST => AESL_inst_multi_apuint

です。インスタンス名の prefix な文字が AESL は、Vitis (旧Vivado HLS/SDSoC)の元になった高位合成ツールベンダーの AutoESL からきているんだと思います。この部分は今でもそのままなんですね。

XilinxがAutoESLを買収したのは、2011年1月なんですね。XilinxがAutoESLを買収し、SDSoC/Vivado HLSなどのプロダクトとしてリリースして、なおかつ、ライセンスは無償にしたのは大きなポイントだと思います。

vengineer.hatenablog.com

DUTは C => Verilog HDL に変換された multi_apuint.v です。Vitis でCプログラムを高位合成すると下記のように、ap_xxx という制御信号部と、入力・出力信号に分けてVerilog HDLのコードとして生成されます。入力・出力は、簡単な valid/ack のハンドシェークにて制御されています。

wire ap_clk;
wire ap_rst;
wire ap_rst_n;

`AUTOTB_DUT `AUTOTB_DUT_INST(
    .ap_clk(ap_clk),
    .ap_rst(ap_rst),
    .ap_start(ap_start),
    .ap_done(ap_done),
    .ap_idle(ap_idle),
    .ap_ready(ap_ready),
    .multi_in1_ap_vld(multi_in1_ap_vld),
    .multi_in0_ap_vld(multi_in0_ap_vld),
    .multi_out_ap_ack(multi_out_ap_ack),
    .multi_in0(multi_in0),
    .multi_in0_ap_ack(multi_in0_ap_ack),
    .multi_in1(multi_in1),
    .multi_in1_ap_ack(multi_in1_ap_ack),
    .multi_out(multi_out),
    .multi_out_ap_vld(multi_out_ap_vld));

制御信号は、下記のように AESL_xxx と接続しています。AESL_xxx はこのトップテストベンチ内で制御されているものです。

// Assignment for control signal
assign ap_clk = AESL_clock;
assign ap_rst = dut_rst;
assign ap_rst_n = ~dut_rst;
assign AESL_reset = rst;
assign ap_start = AESL_start;
assign AESL_start = start;
assign AESL_done = ap_done;
assign AESL_idle = ap_idle;
assign AESL_ready = ap_ready;

DUTの入力信号の multi_in0_xxx、multi_in1_xxx もこのトップテストベンチ内で制御されています。

multi_in0 に関する部分は、下記の read_file_process_multi_in0 というラベルが付いている initial 文で行われています。

reg AESL_REG_multi_in0_ap_vld;
// The signal of port multi_in0
reg [7: 0] AESL_REG_multi_in0 = 0;
assign multi_in0 = AESL_REG_multi_in0;
assign multi_in0_ap_vld = AESL_REG_multi_in0_ap_vld;
initial begin : read_file_process_multi_in0
    integer fp;
    integer err;
    integer ret;
    integer proc_rand;
    reg [151  : 0] token;
    integer i;
    reg transaction_finish;
    integer transaction_idx;
    transaction_idx = 0;
    AESL_REG_multi_in0_ap_vld <= 0;
    wait(AESL_reset === 0);
    fp = $fopen(`AUTOTB_TVIN_multi_in0,"r");
    if(fp == 0) begin       // Failed to open file
        $display("Failed to open file \"%s\"!", `AUTOTB_TVIN_multi_in0);
        $display("ERROR: Simulation using HLS TB failed.");
        $finish;
    end
    read_token(fp, token);
    if (token != "[[[runtime]]]") begin
        $display("ERROR: Simulation using HLS TB failed.");
        $finish;
    end
    read_token(fp, token);
    while (token != "[[[/runtime]]]") begin
        if (token != "[[transaction]]") begin
            $display("ERROR: Simulation using HLS TB failed.");
              $finish;
        end
        read_token(fp, token);  // skip transaction number
          read_token(fp, token);
            if(multi_in0_ap_ack === 1)
                AESL_REG_multi_in0_ap_vld <= 0;
            # 0.2;
            while(ready_wire !== 1) begin
                @(posedge AESL_clock);
                if(multi_in0_ap_ack === 1)
                    AESL_REG_multi_in0_ap_vld <= 0;
                # 0.2;
            end
        if(token != "[[/transaction]]") begin
            AESL_REG_multi_in0_ap_vld <= 1;
            ret = $sscanf(token, "0x%x", AESL_REG_multi_in0);
              if (ret != 1) begin
                  $display("Failed to parse token!");
                $display("ERROR: Simulation using HLS TB failed.");
                  $finish;
              end
            @(posedge AESL_clock);
              read_token(fp, token);
        end
          read_token(fp, token);
    end
    $fclose(fp);
  while (AESL_REG_multi_in0_ap_vld == 1) begin
      if (multi_in0_ap_ack == 1) begin
          AESL_REG_multi_in0_ap_vld <= 0;
      end
      @(posedge AESL_clock);
  end
end

入力データの multi_in0 は、`AUTOTB_TVIN_multi_in0 マクロで定義されている

`define AUTOTB_TVIN_multi_in0  "../tv/cdatafile/c.multi_apuint.autotvin_multi_in0.dat"

ファイルから読み出しています。このファイルの中身は、下記のようになっています。

[[[runtime]]]
[[transaction]]           0
0x00
[[/transaction]]
[[transaction]]           1
0x01
[[/transaction]]
[[transaction]]           2
0x02
[[/transaction]]
[[transaction]]           3
0x03
[[/transaction]]
[[transaction]]           4
0x04
[[/transaction]]
[[transaction]]           5
0x05
[[/transaction]]
[[transaction]]           6
0x06
[[/transaction]]
[[transaction]]           7
0x07
[[/transaction]]
[[transaction]]           8
0x08
[[/transaction]]
[[transaction]]           9
0x09
[[/transaction]]
[[[/runtime]]]

入力データの multi_in1 は、`AUTOTB_TVIN_multi_in1 マクロで定義されている。ファイルの内容も multi_in0 と同じような感じです。

`define AUTOTB_TVIN_multi_in1  "../tv/cdatafile/c.multi_apuint.autotvin_multi_in1.dat"

multi_in1 に関する部分は、下記の read_file_process_multi_in1 というラベルが付いている initial 文で行われています。

出力データの multi_out は、`AUTOTB_TVOUT_multi_out_out_wrapc マクロでていぎされています。

`define AUTOTB_TVOUT_multi_out_out_wrapc  "../tv/rtldatafile/rtl.multi_apuint.autotvout_multi_out.dat"

出力ファイルの中身は次のようになっています。

[[[runtime]]]
[[transaction]]           0
0x0000
[[/transaction]]
[[transaction]]           1
0x0002
[[/transaction]]
[[transaction]]           2
0x0006
[[/transaction]]
[[transaction]]           3
0x000c
[[/transaction]]
[[transaction]]           4
0x0014
[[/transaction]]
[[transaction]]           5
0x001e
[[/transaction]]
[[transaction]]           6
0x002a
[[/transaction]]
[[transaction]]           7
0x0038
[[/transaction]]
[[transaction]]           8
0x0048
[[/transaction]]
[[transaction]]           9
0x005a
[[/transaction]]
[[[/runtime]]]

multi_out に関する部分は、下記の read_file_process_multi_out というラベルが付いている initial 文で行われています。このブロックの中で、multi_out の出力データを`AUTOTB_TVOUT_multi_out_out_wrapc マクロのファイルに書き出しています。

initial begin : write_file_process_multi_out
    integer fp;
    integer fp_size;
    integer err;
    integer ret;
    integer i;
    integer hls_stream_size;
    integer proc_rand;
    integer multi_out_count;
    reg [151:0] token;
    integer transaction_idx;
    reg [8 * 5:1] str;
    wait(AESL_reset === 0);
    fp = $fopen(`AUTOTB_TVOUT_multi_out_out_wrapc,"w");
    if(fp == 0) begin       // Failed to open file
        $display("Failed to open file \"%s\"!", `AUTOTB_TVOUT_multi_out_out_wrapc);
        $display("ERROR: Simulation using HLS TB failed.");
        $finish;
    end
    $fdisplay(fp,"[[[runtime]]]");
    transaction_idx = 0;
    while (transaction_idx != AUTOTB_TRANSACTION_NUM) begin
        @(posedge AESL_clock);
          while(AESL_done !== 1) begin
              @(posedge AESL_clock);
          end
        # 0.4;
        $fdisplay(fp,"[[transaction]] %d", transaction_idx);
        if(AESL_REG_multi_out_ap_vld)  begin
          $fdisplay(fp,"0x%x", AESL_REG_multi_out);
        AESL_REG_multi_out_ap_vld = 0;
        end
    transaction_idx = transaction_idx + 1;
      $fdisplay(fp,"[[/transaction]]");
    end
    $fdisplay(fp,"[[[/runtime]]]");
    $fclose(fp);
end

出力結果の比較

  • C Simulation の出力結果は、tv/cdatafile/c.multi_apuint.autotvout_multi_out.dat
  • C/RTL Simulation の結果は、tv/rtldatafile/rtl.multi_apuint.autotvout_multi_out.dat

2つのファイルの diff コマンドを比較すると、一致しています。

diff tv/cdatafile/c.multi_apuint.autotvout_multi_out.dat tv/rtldatafile/rtl.multi_apuint.autotvout_multi_out.dat

全体像

下図は、上記のトップテストベンチ内の構造を図示したものです。

おわりに

6回に分けて、Xilinx Vitis が生成するファイルをみてみました。結構なファイルを生成するんですね。

次回からは、C/RTL cosimulation にて、Random Stall オプションを ON にした時に生成されるファイルをみていきます。