はじめに
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などのプロダクトとしてリリースして、なおかつ、ライセンスは無償にしたのは大きなポイントだと思います。
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 にした時に生成されるファイルをみていきます。