概要
Verilatorは便利で高速なVerilog/SystemVerilogシミュレータですが、パラメタなどを変えた複数の実行ファイルを生成する場合はターゲットとソースの管理が煩雑になりえます。 本記事では、CMakeを用いて効率的にターゲットとソースを管理することを試みます。 また、GNU Make+GCCといった典型的なツールチェーンではなく、Ninja、Clang、Ccache、そしてmoldを採用する方法も調査します。
本編
本記事はVerilatorをインストール済みの読者を想定します。 まだインストールしていない方は、以下の記事などを参考にインストールします。
CMakeたちをインストールします。 筆者はUbuntuを用いるため、aptを使います。
sudo apt install -y cmake ninja-build mold clang-19 # ccacheは上記の記事でインストール済み
インストールが完了したら、Verilatorのチュートリアルに沿って環境を構築します。
プロジェクトのルートディレクトリにCMakeLists.txt
を作成し、以下を記述します。
cmake_minimum_required(VERSION 3.12) # CMake v3.12以降を指定 project(cmake_example) # プロジェクト名を設定 find_package(verilator HINTS $ENV{VERILATOR_ROOT}) # VerilatorのCMakeスクリプトを検索し統合 add_executable(Vour sim_main.cpp) # sim_main.cppというテストベンチからVourという実行ファイルを生成するルール verilate(Vour SOURCES our.v) # our.vをverilateしVourに統合
続いて、our.v
を作成します。
内容は問いませんが、ひとまずは32bitの加算器とします。
module our ( input [31:0] a, input [31:0] b, output [31:0] c ); assign c = a + b; endmodule
続いて、sim_main.cpp
を作成します。
こちらも内容は問いません。
our.v
のテストベンチを早速実装してもよいですが、ひとまずはHello, world!とします。
#include <iostream> int main() { std::cout << "Hello, world!\n"; return 0; }
3つのファイルができたら、build
ディレクトリを作成し、その中でCMakeを実行します。
そして、makeを実行します。
mkdir build cd build cmake .. # ルートディレクトリのCMakeLists.txtを参照 make
build
ディレクトリにVour
が生成されます。
それを実行すると、"Hello, world!"と表示されます (上記のソースを使った場合)。
続きまして、ビルドシステムをGNU MakeからNinjaへと変更します。
cd .. rm -rf build mkdir build cd build cmake -GNinja .. # -GオプションでNinjaを指定 ninja
同様に、Vour
実行ファイルが生成されます。
続いて、コンパイラにClangを指定します。
CMakeLists.txt
に以下を追記します。
+ set(CMAKE_C_COMPILER clang-19) # Cのコンパイラにclangを指定 + set(CMAKE_CXX_COMPILER clang++-19) # C++のコンパイラにclang++を指定
ここで、Ccacheが自動的に有効化されない場合があります。 それを避けるためには、以下を追記します。
+ set(CMAKE_CXX_COMPILER_LAUNCHER ccache) # ccacheがclangを呼ぶように設定
CMakeを再実行してNinjaのビルドスクリプトを作り直し、今度は詳細なログを出力しながらビルドします。
cmake -GNinja .. ninja -v
この結果から、NinjaがCcacheを経由してClangを実行しているとわかります。 以下に例を示します。
[8/11] /usr/bin/ccache clang++-19 -DVM_COVERAGE=0 -DVM_SC=0 -DVM_TRACE=0 -DVM_TRACE_FST=0 -DVM_TRACE_SAIF=0 -DVM_TRACE_VCD=0 -I/root/veri_cmake/build/CMakeFiles/Vour.dir/Vour.dir -I/usr/local/share/verilator/include -I/usr/local/share/verilator/include/vltstd -MD -MT CMakeFiles/Vour.dir/CMakeFiles/Vour.dir/Vour.dir/Vour___024root__Slow.cpp.o -MF CMakeFiles/Vour.dir/CMakeFiles/Vour.dir/Vour.dir/Vour___024root__Slow.cpp.o.d -o CMakeFiles/Vour.dir/CMakeFiles/Vour.dir/Vour.dir/Vour___024root__Slow.cpp.o -c /root/veri_cmake/build/CMakeFiles/Vour.dir/Vour.dir/Vour___024root__Slow.cpp [9/11] /usr/bin/ccache clang++-19 -DVM_COVERAGE=0 -DVM_SC=0 -DVM_TRACE=0 -DVM_TRACE_FST=0 -DVM_TRACE_SAIF=0 -DVM_TRACE_VCD=0 -I/root/veri_cmake/build/CMakeFiles/Vour.dir/Vour.dir -I/usr/local/share/verilator/include -I/usr/local/share/verilator/include/vltstd -MD -MT CMakeFiles/Vour.dir/usr/local/share/verilator/include/verilated_threads.cpp.o -MF CMakeFiles/Vour.dir/usr/local/share/verilator/include/verilated_threads.cpp.o.d -o CMakeFiles/Vour.dir/usr/local/share/verilator/include/verilated_threads.cpp.o -c /usr/local/share/verilator/include/verilated_threads.cpp [10/11] /usr/bin/ccache clang++-19 -DVM_COVERAGE=0 -DVM_SC=0 -DVM_TRACE=0 -DVM_TRACE_FST=0 -DVM_TRACE_SAIF=0 -DVM_TRACE_VCD=0 -I/root/veri_cmake/build/CMakeFiles/Vour.dir/Vour.dir -I/usr/local/share/verilator/include -I/usr/local/share/verilator/include/vltstd -MD -MT CMakeFiles/Vour.dir/usr/local/share/verilator/include/verilated.cpp.o -MF CMakeFiles/Vour.dir/usr/local/share/verilator/include/verilated.cpp.o.d -o CMakeFiles/Vour.dir/usr/local/share/verilator/include/verilated.cpp.o -c /usr/local/share/verilator/include/verilated.cpp
さて、ここまでで本記事の目的の大部分を達成しました。 以下では、オプションとしてコンパイラにフラグを渡したり、リンカを変更したりする方法を述べます。 これにより、シミュレーションの実行や再構築が高速化し、効率的に作業が行えることが期待されます。
CMakeLists.txt
に以下を追記します。
コンパイラフラグとして、実行中のCPU向けにコードを生成する (-march=native
) とともに、レベル3の最適化を行う (-O3
) ことを設定します。
リンカフラグとして、moldの使用を要求します。
+ set(CMAKE_CXX_FLAGS "-march=native -O3") # C++のコンパイラフラグを設定 + set(CMAKE_EXE_LINKER_FLAGS "-fuse-ld=mold") # リンカフラグを設定
これを用いてビルドを行うと、Clangにフラグが渡されます。
以下に、ninja -v
の実行結果の一部を示します。
最適化オプションやリンカフラグが渡されていることを確認できます。
[9/11] /usr/bin/ccache clang++-19 -DVM_COVERAGE=0 -DVM_SC=0 -DVM_TRACE=0 -DVM_TRACE_FST=0 -DVM_TRACE_SAIF=0 -DVM_TRACE_VCD=0 -I/root/veri_cmake/build/CMakeFiles/Vour.dir/Vour.dir -I/usr/local/share/verilator/include -I/usr/local/share/verilator/include/vltstd -march=native -O3 -MD -MT CMakeFiles/Vour.dir/CMakeFiles/Vour.dir/Vour.dir/Vour__Syms.cpp.o -MF CMakeFiles/Vour.dir/CMakeFiles/Vour.dir/Vour.dir/Vour__Syms.cpp.o.d -o CMakeFiles/Vour.dir/CMakeFiles/Vour.dir/Vour.dir/Vour__Syms.cpp.o -c /root/veri_cmake/build/CMakeFiles/Vour.dir/Vour.dir/Vour__Syms.cpp [10/11] /usr/bin/ccache clang++-19 -DVM_COVERAGE=0 -DVM_SC=0 -DVM_TRACE=0 -DVM_TRACE_FST=0 -DVM_TRACE_SAIF=0 -DVM_TRACE_VCD=0 -I/root/veri_cmake/build/CMakeFiles/Vour.dir/Vour.dir -I/usr/local/share/verilator/include -I/usr/local/share/verilator/include/vltstd -march=native -O3 -MD -MT CMakeFiles/Vour.dir/usr/local/share/verilator/include/verilated.cpp.o -MF CMakeFiles/Vour.dir/usr/local/share/verilator/include/verilated.cpp.o.d -o CMakeFiles/Vour.dir/usr/local/share/verilator/include/verilated.cpp.o -c /usr/local/share/verilator/include/verilated.cpp [11/11] : && clang++-19 -march=native -O3 -fuse-ld=mold CMakeFiles/Vour.dir/sim_main.cpp.o CMakeFiles/Vour.dir/CMakeFiles/Vour.dir/Vour.dir/Vour.cpp.o CMakeFiles/Vour.dir/CMakeFiles/Vour.dir/Vour.dir/Vour___024root__DepSet_hf7027e39__0.cpp.o CMakeFiles/Vour.dir/CMakeFiles/Vour.dir/Vour.dir/Vour___024root__DepSet_h637983f1__0.cpp.o CMakeFiles/Vour.dir/CMakeFiles/Vour.dir/Vour.dir/Vour___024root__Slow.cpp.o CMakeFiles/Vour.dir/CMakeFiles/Vour.dir/Vour.dir/Vour___024root__DepSet_hf7027e39__0__Slow.cpp.o CMakeFiles/Vour.dir/CMakeFiles/Vour.dir/Vour.dir/Vour___024root__DepSet_h637983f1__0__Slow.cpp.o CMakeFiles/Vour.dir/CMakeFiles/Vour.dir/Vour.dir/Vour__Syms.cpp.o CMakeFiles/Vour.dir/usr/local/share/verilator/include/verilated.cpp.o CMakeFiles/Vour.dir/usr/local/share/verilator/include/verilated_threads.cpp.o -o Vour -pthread -lpthread -latomic && :
さて、実際にmoldが使われていることを二つの方法で確認します。
まず、Clangに-###
オプションを渡しverboseにビルドを実行します。
その結果、ld.mold
が呼び出されています。
[11/11] : && clang++-19 -march=native -O3 -### -fuse-ld=mold CMakeFiles/Vour.dir/sim_main.cpp.o CMakeFiles/Vour.dir/CMakeFiles/Vour.dir/Vour.dir/Vour.cpp.o CMakeFiles/Vour.dir/CMakeFiles/Vour.dir/Vour.dir/Vour___024root__DepSet_hf7027e39__0.cpp.o CMakeFiles/Vour.dir/CMakeFiles/Vour.dir/Vour.dir/Vour___024root__DepSet_h637983f1__0.cpp.o CMakeFiles/Vour.dir/CMakeFiles/Vour.dir/Vour.dir/Vour___024root__Slow.cpp.o CMakeFiles/Vour.dir/CMakeFiles/Vour.dir/Vour.dir/Vour___024root__DepSet_hf7027e39__0__Slow.cpp.o CMakeFiles/Vour.dir/CMakeFiles/Vour.dir/Vour.dir/Vour___024root__DepSet_h637983f1__0__Slow.cpp.o CMakeFiles/Vour.dir/CMakeFiles/Vour.dir/Vour.dir/Vour__Syms.cpp.o CMakeFiles/Vour.dir/usr/local/share/verilator/include/verilated.cpp.o CMakeFiles/Vour.dir/usr/local/share/verilator/include/verilated_threads.cpp.o -o Vour -pthread -lpthread -latomic && : Ubuntu clang version 19.1.1 (1ubuntu1~24.04.2) Target: x86_64-pc-linux-gnu Thread model: posix InstalledDir: /usr/lib/llvm-19/bin "/usr/bin/ld.mold" "-z" "relro" "--hash-style=gnu" "--build-id" "--eh-frame-hdr" "-m" "elf_x86_64" "-pie" "-dynamic-linker" "/lib64/ld-linux-x86-64.so.2" "-o" "Vour" "/lib/x86_64-linux-gnu/Scrt1.o" "/lib/x86_64-linux-gnu/crti.o" "/usr/lib/gcc/x86_64-linux-gnu/13/crtbeginS.o" "-L/usr/lib/gcc/x86_64-linux-gnu/13" "-L/usr/lib/gcc/x86_64-linux-gnu/13/../../../../lib64" "-L/lib/x86_64-linux-gnu" "-L/lib/../lib64" "-L/usr/lib/x86_64-linux-gnu" "-L/usr/lib/../lib64" "-L/lib" "-L/usr/lib" "CMakeFiles/Vour.dir/sim_main.cpp.o" "CMakeFiles/Vour.dir/CMakeFiles/Vour.dir/Vour.dir/Vour.cpp.o" "CMakeFiles/Vour.dir/CMakeFiles/Vour.dir/Vour.dir/Vour___024root__DepSet_hf7027e39__0.cpp.o" "CMakeFiles/Vour.dir/CMakeFiles/Vour.dir/Vour.dir/Vour___024root__DepSet_h637983f1__0.cpp.o" "CMakeFiles/Vour.dir/CMakeFiles/Vour.dir/Vour.dir/Vour___024root__Slow.cpp.o" "CMakeFiles/Vour.dir/CMakeFiles/Vour.dir/Vour.dir/Vour___024root__DepSet_hf7027e39__0__Slow.cpp.o" "CMakeFiles/Vour.dir/CMakeFiles/Vour.dir/Vour.dir/Vour___024root__DepSet_h637983f1__0__Slow.cpp.o" "CMakeFiles/Vour.dir/CMakeFiles/Vour.dir/Vour.dir/Vour__Syms.cpp.o" "CMakeFiles/Vour.dir/usr/local/share/verilator/include/verilated.cpp.o" "CMakeFiles/Vour.dir/usr/local/share/verilator/include/verilated_threads.cpp.o" "-lpthread" "-latomic" "-lstdc++" "-lm" "-lgcc_s" "-lgcc" "-lpthread" "-lc" "-lgcc_s" "-lgcc" "/usr/lib/gcc/x86_64-linux-gnu/13/crtendS.o" "/lib/x86_64-linux-gnu/crtn.o"
続いて、生成されたELFファイルの.comment
領域を確認します。
やはりmoldが使われていると考えられます。
最後のGCCはなんでしょう?
readelf -p .comment Vour
String dump of section '.comment': [ 0] mold 2.30.0 (compatible with GNU ld) [ 25] Ubuntu clang version 19.1.1 (1ubuntu1~24.04.2) [ 54] GCC: (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0
以上で、Verilatorによるシミュレーションモデルの生成をCMake+Ninja+Clang+Ccache+moldで行えるようになりました。 今後は、これを用いて自作CPUの開発などを行いたいと思います。