###################################################################### # # DESCRIPTION: CMake configuration file for Verilator # # Include it in your CMakeLists.txt using: # # find_package(verilate) # # This script adds a verilate function. # # add_executable(simulator ) # verilate(simulator SOURCES ) # # Copyright 2003-2024 by Wilson Snyder. This program is free software; you # can redistribute it and/or modify it under the terms of either the GNU # Lesser General Public License Version 3 or the Perl Artistic License # Version 2.0. # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 # ###################################################################### cmake_minimum_required(VERSION 3.13) # Prefer VERILATOR_ROOT from environment if(DEFINED ENV{VERILATOR_ROOT}) set(VERILATOR_ROOT "$ENV{VERILATOR_ROOT}" CACHE PATH "VERILATOR_ROOT") endif() set(VERILATOR_ROOT "${CMAKE_CURRENT_LIST_DIR}" CACHE PATH "VERILATOR_ROOT") find_program( VERILATOR_BIN NAMES verilator_bin verilator_bin.exe HINTS ${VERILATOR_ROOT}/bin ENV VERILATOR_ROOT NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH ) if(NOT VERILATOR_ROOT) message( FATAL_ERROR "VERILATOR_ROOT cannot be detected. Set it to the appropriate directory (e.g. /usr/share/verilator) as an environment variable or CMake define." ) endif() if(NOT VERILATOR_BIN) message(FATAL_ERROR "Cannot find verilator_bin excecutable.") endif() set(verilator_FOUND 1) include(CheckCXXSourceCompiles) function(_verilator_check_cxx_libraries LIBRARIES RESVAR) # Check whether a particular link option creates a valid executable set(_VERILATOR_CHECK_CXX_LINK_OPTIONS_SRC "int main() {return 0;}\n") set(CMAKE_REQUIRED_FLAGS) set(CMAKE_REQUIRED_DEFINITIONS) set(CMAKE_REQUIRED_INCLUDES) set(CMAKE_REQUIRED_LINK_OPTIONS) set(CMAKE_REQUIRED_LIBRARIES ${LIBRARIES}) set(CMAKE_REQUIRED_QUIET) check_cxx_source_compiles( "${_VERILATOR_CHECK_CXX_LINK_OPTIONS_SRC}" "${RESVAR}" ) set("${RESVAR}" "${${RESVAR}}" PARENT_SCOPE) endfunction() # Check compiler flag support. Skip on MSVC, these are all GCC flags. if(NOT CMAKE_CXX_COMPILER_ID MATCHES MSVC) if(NOT DEFINED VERILATOR_CFLAGS OR NOT DEFINED VERILATOR_MT_CFLAGS) include(CheckCXXCompilerFlag) foreach(FLAG @CFG_CXX_FLAGS_CMAKE@) string(MAKE_C_IDENTIFIER ${FLAG} FLAGNAME) check_cxx_compiler_flag(${FLAG} ${FLAGNAME}) if(${FLAGNAME}) list(APPEND VERILATOR_CFLAGS ${FLAG}) endif() endforeach() foreach(FLAG @CFG_LDFLAGS_THREADS_CMAKE@) string(MAKE_C_IDENTIFIER ${FLAG} FLAGNAME) _verilator_check_cxx_libraries("${FLAG}" ${FLAGNAME}) if(${FLAGNAME}) list(APPEND VERILATOR_MT_CFLAGS ${FLAG}) endif() endforeach() endif() endif() if(${CMAKE_CXX_COMPILER_ID} STREQUAL "AppleClang") add_link_options(-Wl,-U,__Z15vl_time_stamp64v,-U,__Z13sc_time_stampv) endif() define_property( TARGET PROPERTY VERILATOR_THREADED BRIEF_DOCS "Deprecated and has no effect (ignored)" FULL_DOCS "Deprecated and has no effect (ignored)" ) define_property( TARGET PROPERTY VERILATOR_TRACE_THREADED BRIEF_DOCS "Verilator multithread tracing enabled" FULL_DOCS "Verilator multithread tracing enabled" ) define_property( TARGET PROPERTY VERILATOR_TIMING BRIEF_DOCS "Verilator timing enabled" FULL_DOCS "Verilator timing enabled" ) define_property( TARGET PROPERTY VERILATOR_COVERAGE BRIEF_DOCS "Verilator coverage enabled" FULL_DOCS "Verilator coverage enabled" ) define_property( TARGET PROPERTY VERILATOR_TRACE BRIEF_DOCS "Verilator trace enabled" FULL_DOCS "Verilator trace enabled" ) define_property( TARGET PROPERTY VERILATOR_TRACE_VCD BRIEF_DOCS "Verilator VCD trace enabled" FULL_DOCS "Verilator VCD trace enabled" ) define_property( TARGET PROPERTY VERILATOR_TRACE_FST BRIEF_DOCS "Verilator FST trace enabled" FULL_DOCS "Verilator FST trace enabled" ) define_property( TARGET PROPERTY VERILATOR_SYSTEMC BRIEF_DOCS "Verilator SystemC enabled" FULL_DOCS "Verilator SystemC enabled" ) define_property( TARGET PROPERTY VERILATOR_TRACE_STRUCTS BRIEF_DOCS "Verilator trace structs enabled" FULL_DOCS "Verilator trace structs enabled" ) function(verilate TARGET) cmake_parse_arguments( VERILATE "COVERAGE;TRACE;TRACE_FST;SYSTEMC;TRACE_STRUCTS" "PREFIX;TOP_MODULE;THREADS;TRACE_THREADS;DIRECTORY" "SOURCES;VERILATOR_ARGS;INCLUDE_DIRS;OPT_SLOW;OPT_FAST;OPT_GLOBAL" ${ARGN} ) if(NOT VERILATE_SOURCES) message(FATAL_ERROR "Need at least one source") endif() if(NOT VERILATE_PREFIX) list(LENGTH VERILATE_SOURCES NUM_SOURCES) if(${NUM_SOURCES} GREATER 1) message(WARNING "Specify PREFIX if there are multiple SOURCES") endif() list(GET VERILATE_SOURCES 0 TOPSRC) get_filename_component(_SRC_NAME ${TOPSRC} NAME_WE) string(MAKE_C_IDENTIFIER V${_SRC_NAME} VERILATE_PREFIX) endif() if(VERILATE_TOP_MODULE) list(APPEND VERILATOR_ARGS --top ${VERILATE_TOP_MODULE}) endif() if(VERILATE_THREADS) list(APPEND VERILATOR_ARGS --threads ${VERILATE_THREADS}) endif() if(VERILATE_TRACE_THREADS) list(APPEND VERILATOR_ARGS --trace-threads ${VERILATE_TRACE_THREADS}) endif() if(VERILATE_COVERAGE) list(APPEND VERILATOR_ARGS --coverage) endif() if(VERILATE_TRACE AND VERILATE_TRACE_FST) message(FATAL_ERROR "Cannot have both TRACE and TRACE_FST") endif() if(VERILATE_TRACE) list(APPEND VERILATOR_ARGS --trace) endif() if(VERILATE_TRACE_FST) list(APPEND VERILATOR_ARGS --trace-fst) endif() if(VERILATE_SYSTEMC) list(APPEND VERILATOR_ARGS --sc) else() list(APPEND VERILATOR_ARGS --cc) endif() if(VERILATE_TRACE_STRUCTS) list(APPEND VERILATOR_ARGS --trace-structs) endif() foreach(INC ${VERILATE_INCLUDE_DIRS}) list(APPEND VERILATOR_ARGS -y "${INC}") endforeach() string(TOLOWER ${CMAKE_CXX_COMPILER_ID} COMPILER) if(COMPILER STREQUAL "appleclang") set(COMPILER clang) elseif(NOT COMPILER MATCHES "^msvc$|^clang$") set(COMPILER gcc) endif() set(OBJCACHE "" CACHE STRING "Path for ccache, auto-detected if empty") option(OBJCACHE_ENABLED "Compile Verilator with ccache" ON) if(OBJCACHE_ENABLED) if(OBJCACHE STREQUAL "") find_program(OBJCACHE_PATH ccache) if(OBJCACHE_PATH STREQUAL "OBJCACHE_PATH-NOTFOUND") set(OBJCACHE_PATH "") endif() else() set(OBJCACHE_PATH "${OBJCACHE}") endif() if(NOT OBJCACHE_PATH STREQUAL "") execute_process( COMMAND "${OBJCACHE_PATH}" --version OUTPUT_VARIABLE objcache_version ) string( REGEX MATCH "[^\n\r]+" objcache_version "${objcache_version}" ) message( STATUS "Found ccache: ${OBJCACHE_PATH} (\"${objcache_version}\")" ) set(CMAKE_CXX_COMPILER_LAUNCHER "${OBJCACHE_PATH}") endif() endif() get_target_property(BINARY_DIR "${TARGET}" BINARY_DIR) get_target_property(TARGET_NAME "${TARGET}" NAME) set(VDIR "${BINARY_DIR}/CMakeFiles/${TARGET_NAME}.dir/${VERILATE_PREFIX}.dir" ) if(VERILATE_DIRECTORY) set(VDIR "${VERILATE_DIRECTORY}") endif() file(MAKE_DIRECTORY ${VDIR}) set(VERILATOR_COMMAND "${CMAKE_COMMAND}" -E env "VERILATOR_ROOT=${VERILATOR_ROOT}" "${VERILATOR_BIN}" --compiler ${COMPILER} --prefix ${VERILATE_PREFIX} --Mdir ${VDIR} --make cmake ${VERILATOR_ARGS} ${VERILATE_VERILATOR_ARGS} ${VERILATE_SOURCES} ) set(VARGS_FILE "${VDIR}/verilator_args.txt") set(VCMAKE "${VDIR}/${VERILATE_PREFIX}.cmake") set(VCMAKE_COPY "${VDIR}/${VERILATE_PREFIX}_copy.cmake") if(NOT EXISTS "${VARGS_FILE}" OR NOT EXISTS "${VCMAKE_COPY}") set(VERILATOR_OUTDATED ON) else() file(READ "${VARGS_FILE}" PREVIOUS_VERILATOR_COMMAND) if(NOT VERILATOR_COMMAND STREQUAL PREVIOUS_VERILATOR_COMMAND) set(VERILATOR_OUTDATED ON) endif() endif() if(VERILATOR_OUTDATED) message(STATUS "Executing Verilator...") execute_process( COMMAND ${VERILATOR_COMMAND} WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" RESULT_VARIABLE _VERILATOR_RC OUTPUT_VARIABLE _VERILATOR_OUTPUT ERROR_VARIABLE _VERILATOR_OUTPUT ) if(_VERILATOR_RC) string( REPLACE ";" " " VERILATOR_COMMAND_READABLE "${VERILATOR_COMMAND}" ) message("Verilator command: \"${VERILATOR_COMMAND_READABLE}\"") message("Output:\n${_VERILATOR_OUTPUT}") message( FATAL_ERROR "Verilator command failed (return code=${_VERILATOR_RC})" ) endif() execute_process( COMMAND "${CMAKE_COMMAND}" -E copy "${VCMAKE}" "${VCMAKE_COPY}" ) endif() file(WRITE "${VARGS_FILE}" "${VERILATOR_COMMAND}") include("${VCMAKE_COPY}") set(GENERATED_C_SOURCES ${${VERILATE_PREFIX}_CLASSES_FAST} ${${VERILATE_PREFIX}_CLASSES_SLOW} ${${VERILATE_PREFIX}_SUPPORT_FAST} ${${VERILATE_PREFIX}_SUPPORT_SLOW} ) # No need for .h's as the .cpp will get written same time set(GENERATED_SOURCES ${GENERATED_C_SOURCES}) add_custom_command( OUTPUT ${GENERATED_SOURCES} "${VCMAKE}" COMMAND ${VERILATOR_COMMAND} WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" DEPENDS "${VERILATOR_BIN}" ${${VERILATE_PREFIX}_DEPS} VERBATIM ) # Reconfigure if file list has changed # (check contents rather than modified time to avoid unnecessary reconfiguration) add_custom_command( OUTPUT "${VCMAKE_COPY}" COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${VCMAKE}" "${VCMAKE_COPY}" DEPENDS "${VCMAKE}" VERBATIM ) if(${VERILATE_PREFIX}_COVERAGE) # If any verilate() call specifies COVERAGE, define VM_COVERAGE in the final build set_property(TARGET ${TARGET} PROPERTY VERILATOR_COVERAGE ON) endif() if(${VERILATE_PREFIX}_TRACE_VCD) # If any verilate() call specifies TRACE, define VM_TRACE in the final build set_property(TARGET ${TARGET} PROPERTY VERILATOR_TRACE ON) set_property(TARGET ${TARGET} PROPERTY VERILATOR_TRACE_VCD ON) endif() if(${VERILATE_PREFIX}_TRACE_FST) # If any verilate() call specifies TRACE_FST, define VM_TRACE_FST in the final build set_property(TARGET ${TARGET} PROPERTY VERILATOR_TRACE ON) set_property(TARGET ${TARGET} PROPERTY VERILATOR_TRACE_FST ON) endif() if(${VERILATE_PREFIX}_SC) # If any verilate() call specifies SYSTEMC, define VM_SC in the final build set_property(TARGET ${TARGET} PROPERTY VERILATOR_SYSTEMC ON) endif() if(${VERILATE_PREFIX}_TRACE_STRUCTS) set_property(TARGET ${TARGET} PROPERTY VERILATOR_TRACE_STRUCTS ON) endif() # Add the compile flags only on Verilated sources target_include_directories(${TARGET} PUBLIC ${VDIR}) target_sources( ${TARGET} PRIVATE ${GENERATED_SOURCES} "${VCMAKE_COPY}" ${${VERILATE_PREFIX}_GLOBAL} ${${VERILATE_PREFIX}_USER_CLASSES} ) foreach(_VSOURCE ${VERILATE_SOURCES} ${${VERILATE_PREFIX}_DEPS}) get_filename_component(_VSOURCE "${_VSOURCE}" ABSOLUTE BASE_DIR) list(APPEND VHD_SOURCES "${_VSOURCE}") endforeach() target_sources(${TARGET} PRIVATE ${VHD_SOURCES}) # Add the compile flags only on Verilated sources foreach( VSLOW ${${VERILATE_PREFIX}_CLASSES_SLOW} ${${VERILATE_PREFIX}_SUPPORT_SLOW} ) foreach(OPT_SLOW ${VERILATE_OPT_SLOW} ${${VERILATE_PREFIX}_USER_CFLAGS}) set_property( SOURCE "${VSLOW}" APPEND_STRING PROPERTY COMPILE_FLAGS " ${OPT_SLOW}" ) endforeach() endforeach() foreach( VFAST ${${VERILATE_PREFIX}_CLASSES_FAST} ${${VERILATE_PREFIX}_SUPPORT_FAST} ) foreach(OPT_FAST ${VERILATE_OPT_FAST} ${${VERILATE_PREFIX}_USER_CFLAGS}) set_property( SOURCE "${VFAST}" APPEND_STRING PROPERTY COMPILE_FLAGS " ${OPT_FAST}" ) endforeach() endforeach() foreach(VGLOBAL ${${VERILATE_PREFIX}_GLOBAL}) foreach( OPT_GLOBAL ${VERILATE_OPT_GLOBAL} ${${VERILATE_PREFIX}_USER_CFLAGS} ) set_property( SOURCE "${VGLOBAL}" APPEND_STRING PROPERTY COMPILE_FLAGS " ${OPT_GLOBAL}" ) endforeach() endforeach() target_include_directories( ${TARGET} PUBLIC "${VERILATOR_ROOT}/include" "${VERILATOR_ROOT}/include/vltstd" ) target_compile_definitions( ${TARGET} PRIVATE VM_COVERAGE=$> VM_SC=$> VM_TRACE=$> VM_TRACE_VCD=$> VM_TRACE_FST=$> ) target_link_libraries(${TARGET} PUBLIC ${${VERILATE_PREFIX}_USER_LDLIBS}) target_link_libraries(${TARGET} PUBLIC ${VERILATOR_MT_CFLAGS}) target_compile_features(${TARGET} PRIVATE cxx_std_11) if(${VERILATE_PREFIX}_TIMING) check_cxx_compiler_flag(-fcoroutines-ts COROUTINES_TS_FLAG) target_compile_options( ${TARGET} PRIVATE $,-fcoroutines-ts,-fcoroutines> ) endif() endfunction() function(_verilator_find_systemc) if(NOT TARGET Verilator::systemc) # Find SystemC include file "systemc.h" in the following order: # 1. SYSTEMC_INCLUDE (environment) variable # 2. SYSTEMC_ROOT (environment) variable # 3. SYSTEMC (environment) variable # 4. Use CMake module provided by SystemC installation # (eventually requires CMAKE_PREFIX_PATH set) find_path( SYSTEMC_INCLUDEDIR NAMES systemc.h HINTS "${SYSTEMC_INCLUDE} " ENV SYSTEMC_INCLUDE ) find_path( SYSTEMC_INCLUDEDIR NAMES systemc.h HINTS "${SYSTEMC_ROOT}" ENV SYSTEMC_ROOT PATH_SUFFIXES include ) find_path( SYSTEMC_INCLUDEDIR NAMES systemc.h HINTS "${SYSTEMC}" ENV SYSTEMC PATH_SUFFIXES include ) # Find SystemC library in the following order: # 1. SYSTEMC_LIBDIR (environment) variable # 2. SYSTEMC_ROOT (environment) variable # 3. SYSTEMC (environment) variable # 4. Use CMake module provided by SystemC installation # (eventually requires CMAKE_PREFIX_PATH set) # Find SystemC using include and library paths find_library( SYSTEMC_LIBRARY NAMES systemc HINTS "${SYSTEMC_LIBDIR}" ENV SYSTEMC_LIBDIR ) find_library( SYSTEMC_LIBRARY NAMES systemc HINTS "${SYSTEMC_ROOT}" ENV SYSTEMC_ROOT PATH_SUFFIXES lib ) find_library( SYSTEMC_LIBRARY NAMES systemc HINTS "${SYSTEMC}" ENV SYSTEMC PATH_SUFFIXES lib ) if(SYSTEMC_INCLUDEDIR AND SYSTEMC_LIBRARY) add_library(Verilator::systemc INTERFACE IMPORTED) set_target_properties( Verilator::systemc PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${SYSTEMC_INCLUDEDIR}" INTERFACE_LINK_LIBRARIES "${SYSTEMC_LIBRARY}" ) return() endif() find_package(SystemCLanguage QUIET) if(SystemCLanguage_FOUND) add_library(Verilator::systemc INTERFACE IMPORTED) set_target_properties( Verilator::systemc PROPERTIES INTERFACE_LINK_LIBRARIES "SystemC::systemc" ) return() endif() message( "SystemC not found. This can be fixed by doing either of the following steps:" ) message( "- set the SYSTEMC_INCLUDE and SYSTEMC_LIBDIR (environment) variables; or" ) message("- set SYSTEMC_ROOT (environment) variable; or") message("- set SYSTEMC (environment) variable; or") message( "- use the CMake module of your SystemC installation (may require CMAKE_PREFIX_PATH)" ) message(FATAL_ERROR "SystemC not found") endif() endfunction() function(verilator_link_systemc TARGET) _verilator_find_systemc() target_link_libraries("${TARGET}" PUBLIC Verilator::systemc) target_compile_options( ${TARGET} PRIVATE $ENV{SYSTEMC_CXX_FLAGS} ${SYSTEMC_CXX_FLAGS} ) endfunction() function(verilator_generate_key OUTPUT_VARIABLE) execute_process( COMMAND ${VERILATOR_BIN} --generate-key OUTPUT_VARIABLE KEY_VAL RESULT_VARIABLE KEY_RET ) if(KEY_RET) message(FATAL_ERROR "verilator --generate-key failed") endif() string(STRIP ${KEY_VAL} KEY_VAL) set(${OUTPUT_VARIABLE} ${KEY_VAL} PARENT_SCOPE) endfunction()