Vengineerの妄想(準備期間)

人生は短いけど、長いです。人生を楽しみましょう!

BluespecのAWSteria_Infraを調べる(その4)

はじめに

の続き、

今日は、HW側(Verilator)の中を調べていきます。

HW側(Verilator)

HW側(Verilatorのコードは、ここ にあります。BluespecからVerilog HDLに変換しています。

トップ階層は、Platform_Sim/HW/Include_Common.mk のTOPFILEとして、Platform_Sim/HW/Top_HW_Side.bsv になります。

# ----------------
# Top-level file and module

TOPFILE   ?= $(AWSTERIA_INFRA_REPO)/Platform_Sim/HW/Top_HW_Side.bsv
TOPMODULE ?= mkTop_HW_Side

BluespecからVerilatorへの変換は、Include_Verilator.mk 内で下記のようにcompileターゲットを実行するようになっています。bsc コマンドに -verilog オプションにて、Verilog_RTLディレクトリにVerilog HDLファイルを生成しています。

RTL_GEN_DIRS = -vdir Verilog_RTL  -bdir build_dir  -info-dir build_dir

compile:  build_dir  Verilog_RTL
    @echo  "INFO: Verilog RTL generation ..."
    bsc -u -elab -verilog  $(RTL_GEN_DIRS)  $(BSC_COMPILATION_FLAGS)  -p $(BSC_PATH)  $(TOPFILE)
    @echo  "INFO: Verilog RTL generation finished"

Verilog_RTLディレクトリを見てみます。Verilog HDLコードだけでなく、C言語のソースファイルやヘッダーファイルも生成されています。C言語のファイルには、vpi_wrapperというファイル名が付いています。この vpi_wrapper は、Verilog HDLのPLI(Programming Language Interface)のWrapperになっているようです。

$ cd TestApp/HW/build_Verilator
$ ls
Makefile  Verilator_Make  Verilator_RTL  Verilog_RTL  build_dir  exe_HW_sim
$ ls Verilog_RTL
mkAWSteria_HW.v                       vpi_wrapper_c_host_send.h
mkAWSteria_System.v                   vpi_wrapper_c_host_send2.c
mkAXI4L_S_to_AXI4_M_Adapter_synth.v   vpi_wrapper_c_host_send2.h
mkAXI4_16_64_512_0_Fabric_2_N.v       vpi_wrapper_c_host_send_put_byte_j.c
mkBytevec.v                           vpi_wrapper_c_host_send_put_byte_j.h
mkDDR_A_Model.v                       vpi_wrapper_c_host_try_accept.c
mkDDR_B_Model.v                       vpi_wrapper_c_host_try_accept.h
mkDDR_C_Model.v                       vpi_wrapper_c_putchar.c
mkDDR_D_Model.v                       vpi_wrapper_c_putchar.h
mkTop_HW_Side.v                       vpi_wrapper_c_start_timing.c
vpi_wrapper_c_end_timing.c            vpi_wrapper_c_start_timing.h
vpi_wrapper_c_end_timing.h            vpi_wrapper_c_trace_file_close.c
vpi_wrapper_c_host_disconnect.c       vpi_wrapper_c_trace_file_close.h
vpi_wrapper_c_host_disconnect.h       vpi_wrapper_c_trace_file_load_byte_in_buffer.c
vpi_wrapper_c_host_listen.c           vpi_wrapper_c_trace_file_load_byte_in_buffer.h
vpi_wrapper_c_host_listen.h           vpi_wrapper_c_trace_file_load_word64_in_buffer.c
vpi_wrapper_c_host_recv.c             vpi_wrapper_c_trace_file_load_word64_in_buffer.h
vpi_wrapper_c_host_recv.h             vpi_wrapper_c_trace_file_open.c
vpi_wrapper_c_host_recv2.c            vpi_wrapper_c_trace_file_open.h
vpi_wrapper_c_host_recv2.h            vpi_wrapper_c_trace_file_write_buffer.c
vpi_wrapper_c_host_recv_get_byte_j.c  vpi_wrapper_c_trace_file_write_buffer.h
vpi_wrapper_c_host_recv_get_byte_j.h  vpi_wrapper_c_trygetchar.c
vpi_wrapper_c_host_send.c             vpi_wrapper_c_trygetchar.h

simulatorターゲットを実行することで生成したVerilog HDLコードとPLI Wrapperファイルをverilatorでビルドします。Verilog_RTLディレクトリ内のVerilog HDLコードをVerilator_RTLディレクトリにコピーし、ちょっと加工しています。Verilog_RTLディレクトリに生成されたPLI Wrapperファイルは使わずに、Platform_Sim/HW/C_Imported_Functions.cを使っています。そして、Platform_Sim/HW/Verilator_resources/sim_main.cpp も呼んでいます。

simulator:
    @echo "----------------"
    @echo "INFO: Preparing RTL files for verilator"
    @echo "Copying all Verilog files from Verilog_RTL/ to Verilator_RTL"
    mkdir -p Verilator_RTL
    cp -p  Verilog_RTL/*.v  Verilator_RTL/
    @echo "Copying boilerplate Verilog files to Verilator_RTL"
    cp -p  $(VERILATOR_RESOURCES)/ClockDiv.v  Verilator_RTL/
    @echo "----------------"
    @echo "INFO: Editing Verilog_RTL/$(TOPMODULE).v -> Verilator_RTL/$(TOPMODULE).v for DPI-C"
    sed  -f $(VERILATOR_RESOURCES)/sed_script.txt  Verilog_RTL/$(TOPMODULE).v  > tmp1.v
    cat  $(VERILATOR_RESOURCES)/verilator_config.vlt \
         $(VERILATOR_RESOURCES)/import_DPI_C_decls.v \
         tmp1.v                                          > Verilator_RTL/$(TOPMODULE).v
    rm   -f  tmp1.v
    @echo "----------------"
    @echo "INFO: Editing Verilog_RTL/$(EDIT_MODULE2).v -> Verilator_RTL/$(EDIT_MODULE2).v for DPI-C"
    sed  -f $(VERILATOR_RESOURCES)/sed_script.txt  Verilog_RTL/$(EDIT_MODULE2).v  > Verilator_RTL/$(EDIT_MODULE2).v
    @echo "----------------"
    @echo "INFO: Verilating Verilog files (in $(VERILATOR_MAKE_DIR))"
    verilator \
        -IVerilator_RTL \
        $(VERILATOR_FLAGS) \
        --cc  --exe --build -j 4 -o $(SIM_EXE_FILE)  $(TOPMODULE).v \
        --top-module $(TOPMODULE) \
        $(VERILATOR_RESOURCES)/sim_main.cpp \
        $(AWSTERIA_INFRA_REPO)/Platform_Sim/HW/C_Imported_Functions.c
    mv  $(VERILATOR_MAKE_DIR)/$(SIM_EXE_FILE)  .
    @echo "----------------"
    @echo "INFO: Created verilator executable:    $(SIM_EXE_FILE)"

sim_main.cpp は、下記のようになっています。下記のように、Verilatorのmain関数を定義しています。

// Copyright (c) 2018-2022 Bluespec, Inc. All Rights Reserved

// Top-level driver for "verilated" objects (Verilog compiled with verilator)

#include <verilated.h>

#include <sys/stat.h>  // for 'mkdir'

#include "VmkTop_HW_Side.h"

// If "verilator --trace" is used, include the tracing class
#if VM_TRACE
# include <verilated_vcd_c.h>
#endif

vluint64_t main_time = 0;    // Current simulation time

double sc_time_stamp () {    // Called by $time in Verilog
    return main_time;
}

int main (int argc, char **argv, char **env) {
    Verilated::commandArgs (argc, argv);    // remember args

    VmkTop_HW_Side* mkTop_HW_Side = new VmkTop_HW_Side;    // create instance of model

#if VM_TRACE
    // If verilator was invoked with --trace argument,
    // and if at run time passed the +trace argument, turn on tracing
    VerilatedVcdC* tfp = NULL;
    const char* flag = Verilated::commandArgsPlusMatch("trace");
    if (flag && 0==strcmp(flag, "+trace")) {
        Verilated::traceEverOn(true);  // Verilator must compute traced signals
        VL_PRINTF("Enabling waves into vcd/vlt_dump.vcd...\n");
        tfp = new VerilatedVcdC;
        mkTop_HW_Side->trace(tfp, 99);  // Trace 99 levels of hierarchy
        mkdir("vcd", 0777);
        tfp->open("vcd/vlt_dump.vcd");  // Open the dump file
    }
#endif

    // initial conditions in order to generate appropriate edges on
    // reset
    mkTop_HW_Side->RST_N = 1;
    mkTop_HW_Side->CLK = 0;

    while (! Verilated::gotFinish ()) {

    if (main_time == 2) {
        mkTop_HW_Side->RST_N = 0;    // assert reset
    }
    else if (main_time == 7) {
        mkTop_HW_Side->RST_N = 1;    // Deassert reset
    }

    // Toggle clock
    if ((main_time % 10) == 5) {
        mkTop_HW_Side->CLK = 1;
    }
    else if ((main_time % 10) == 0) {
        mkTop_HW_Side->CLK = 0;
    }

#if VM_TRACE
    if (tfp)
        tfp->dump(main_time);
#endif

    mkTop_HW_Side->eval ();
    main_time++;
    }

    mkTop_HW_Side->final ();    // Done simulating

    // Close trace if opened
#if VM_TRACE
    if (tfp) { tfp->close(); }
#endif

    delete mkTop_HW_Side;
    mkTop_HW_Side = NULL;

    exit (0);
}

おわりに

BluespecからVerilog HDLに変換し、Verilator(v5)でシミュレーションできるのいいですね。ここまではオープンソースのみで動くんですよね。