From 9c091fb9fe1b288f7fcaf2b3c8b701232970f655 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Risto=20Peja=C5=A1inovi=C4=87?= Date: Mon, 23 Dec 2024 01:06:14 +0100 Subject: [PATCH] Refactor GHDL support, add simple VHDL simulation example --- cmake/sim/cadence/xcelium.cmake | 9 +- cmake/sim/ghdl/ghdl.cmake | 237 +++++++++++++++------- cmake/sim/synopsys/vcs.cmake | 8 +- examples/simple_vhdl/CMakeLists.txt | 36 ++++ examples/simple_vhdl/adder/CMakeLists.txt | 7 + examples/simple_vhdl/adder/adder.vhdl | 16 ++ examples/simple_vhdl/tb.vhdl | 38 ++++ 7 files changed, 265 insertions(+), 86 deletions(-) create mode 100644 examples/simple_vhdl/CMakeLists.txt create mode 100644 examples/simple_vhdl/adder/CMakeLists.txt create mode 100644 examples/simple_vhdl/adder/adder.vhdl create mode 100644 examples/simple_vhdl/tb.vhdl diff --git a/cmake/sim/cadence/xcelium.cmake b/cmake/sim/cadence/xcelium.cmake index c2e0df4..9868d30 100644 --- a/cmake/sim/cadence/xcelium.cmake +++ b/cmake/sim/cadence/xcelium.cmake @@ -34,8 +34,7 @@ function(xcelium IP_LIB) # endif() if(NOT ARG_TOP_MODULE) - get_target_property(IP_NAME ${IP_LIB} IP_NAME) - set(ARG_TOP_MODULE ${IP_NAME}) + get_target_property(ARG_TOP_MODULE ${IP_LIB} IP_NAME) endif() if(ARG_XMVLOG_ARGS) @@ -125,8 +124,7 @@ function(__xcelium_compile_lib IP_LIB) # endif() if(NOT ARG_TOP_MODULE) - get_target_property(IP_NAME ${IP_LIB} IP_NAME) - set(ARG_TOP_MODULE ${IP_NAME}) + get_target_property(ARG_TOP_MODULE ${IP_LIB} IP_NAME) endif() # if(NOT ARG_OUTDIR) @@ -216,8 +214,7 @@ function(__xcelium_compile_lib IP_LIB) ${IP_LIB}_xcelium_complib DEPENDS ${STAMP_FILE} ${STAMP_FILE_VHDL} ${IP_LIB} ) - set_property(TARGET ${IP_LIB}_xcelium_complib PROPERTY - DESCRIPTION "Compile VHDL, SV, and Verilog files for ${IP_LIB} with xcelium in library ${LIBRARY}") + set_property(TARGET ${IP_LIB}_xcelium_complib PROPERTY DESCRIPTION ${DESCRIPTION}) endif() # set(__XCELIUM_IP_LIB_DIR ${OUTDIR} PARENT_SCOPE) diff --git a/cmake/sim/ghdl/ghdl.cmake b/cmake/sim/ghdl/ghdl.cmake index e5df2d8..0ca6dea 100644 --- a/cmake/sim/ghdl/ghdl.cmake +++ b/cmake/sim/ghdl/ghdl.cmake @@ -1,7 +1,19 @@ include_guard(GLOBAL) +function(__ghdl_get_standard_arg OUTVAR) + set(SUPPORTED_VHDL_STANDARDS 87 93c 93 00 02 08) + if(ARGN) + if(NOT ${ARGN} IN_LIST SUPPORTED_VHDL_STANDARDS) + message(FATAL_ERROR "VHDL standard not supported ${ARGN}, supported standards: ${ARGN}") + endif() + set(${OUTVAR} ${ARGN} PARENT_SCOPE) + else() + set(${OUTVAR} 93 PARENT_SCOPE) + endif() +endfunction() + function(ghdl IP_LIB) - cmake_parse_arguments(ARG "" "OUTDIR;TOP_MODULE;EXECUTABLE;STANDARD" "" ${ARGN}) + cmake_parse_arguments(ARG "TARGET_PER_IP;NO_RUN_TARGET;" "OUTDIR;TOP_MODULE;EXECUTABLE_NAME;STANDARD" "ANALYZE_ARGS;ELABORATE_ARGS;RUN_ARGS" ${ARGN}) if(ARG_UNPARSED_ARGUMENTS) message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION} passed unrecognized argument " "${ARG_UNPARSED_ARGUMENTS}") endif() @@ -12,105 +24,180 @@ function(ghdl IP_LIB) get_target_property(BINARY_DIR ${IP_LIB} BINARY_DIR) if(NOT ARG_OUTDIR) - set(OUTDIR ${BINARY_DIR}/ghdl) + set(OUTDIR ${BINARY_DIR}/${IP_LIB}_ghdl) else() set(OUTDIR ${ARG_OUTDIR}) endif() + file(MAKE_DIRECTORY ${OUTDIR}) - if(ARG_TOP_MODULE) - set(ARG_TOP_MODULE ${ARG_TOP_MODULE}) - else() + # get_target_property(LIBRARY ${IP_LIB} LIBRARY) + # if(NOT LIBRARY) + set(LIBRARY work) + # endif() + + if(NOT ARG_TOP_MODULE) get_target_property(ARG_TOP_MODULE ${IP_LIB} IP_NAME) endif() - set(SUPPORTED_VHDL_STANDARDS 87 93c 93 00 02 08) - if(ARG_STANDARD) - if(${ARG_STANDARD} IN_LIST SUPPORTED_VHDL_STANDARDS) - else() - message(FATAL_ERROR "VHDL standard not supported ${ARG_STANDARD}, supported standards: ${ARG_STANDARD}") - endif() - set(ARG_STANDARD --std=${ARG_STANDARD}) - else() - set(ARG_STANDARD --std=93) + __ghdl_get_standard_arg(STANDARD ${ARG_STANDARD}) + + if(ARG_ANALYZE_ARGS) + set(ARG_ANALYZE_ARGS ANALYZE_ARGS ${ARG_ANALYZE_ARGS}) endif() - if(NOT ARG_EXECUTABLE) - set(ARG_EXECUTABLE "${OUTDIR}/${IP_LIB}_ghdl_tb") + if(NOT ARG_EXECUTABLE_NAME) + set(ARG_EXECUTABLE_NAME "${IP_LIB}_ghdl_exec") endif() - get_filename_component(ARG_EXECUTABLE ${ARG_EXECUTABLE} ABSOLUTE) + set(SIM_EXEC_PATH "${OUTDIR}/${ARG_EXECUTABLE_NAME}") - get_ip_sources(VHDL_SOURCES ${IP_LIB} VHDL) - list(PREPEND SOURCES ${VHDL_SOURCES}) + get_ip_links(IPS_LIST ${IP_LIB}) - get_ip_include_directories(VHDL_INCLUDE_DIRS ${IP_LIB} VHDL) - set(INC_DIRS ${VHDL_INCLUDE_DIRS}) + if(ARG_TARGET_PER_IP) # In case TARGET_PER_IP is passed, a compile target is created per IP block + set(list_comp_libs ${IPS_LIST}) + set(__no_deps_arg NO_DEPS) + else() + set(list_comp_libs ${IP_LIB}) + unset(__no_deps_arg) + endif() - foreach(dir ${INC_DIRS}) - list(APPEND ARG_INCDIRS -P${dir}) + ##### GHDL Analyze + + unset(__comp_tgts) + foreach(ip ${list_comp_libs}) + get_target_property(ip_name ${ip} IP_NAME) + if(ip_name) # If IP_NAME IS set, its SoCMake's IP_LIBRARY + __ghdl_compile_lib(${ip} ${__no_deps_arg} + OUTDIR ${OUTDIR} + STANDARD ${STANDARD} + ${ARG_ANALYZE_ARGS} + ) + list(APPEND __comp_tgts ${ip}_ghdl_complib) + endif() endforeach() - get_ip_compile_definitions(COMP_DEFS ${IP_LIB} VHDL) - foreach(def ${COMP_DEFS}) - list(APPEND CMP_DEFS_ARG -D${def}) - endforeach() + ##### GHDL Elaborate + if(NOT TARGET ${IP_LIB}_ghdl) + get_ip_sources(VHDL_SOURCES ${IP_LIB} VHDL) + set(__ghdl_elab_cmd ghdl elaborate + --std=${STANDARD} + -fsynopsys + -o ${SIM_EXEC_PATH} + ${ARG_ELABORATE_ARGS} + # -P${OUTDIR} + ${__lib_args} + ${LIBRARY}.${ARG_TOP_MODULE} + ) + + ### Clean files + # * For elaborate "e~${ARG_EXECUTABLE_NAME}.o" and executable gets created + set(__clean_files "${OUTDIR}/e~${ARG_EXECUTABLE_NAME}.o") + + set(DESCRIPTION "Compile testbench ${IP_LIB} with ${CMAKE_CURRENT_FUNCTION}") + set(STAMP_FILE "${BINARY_DIR}/${IP_LIB}_ghdl.stamp") + add_custom_command( + OUTPUT ${SIM_EXEC_PATH} ${STAMP_FILE} + COMMAND ${__ghdl_elab_cmd} + COMMAND touch ${STAMP_FILE} + BYPRODUCTS ${__clean_files} + WORKING_DIRECTORY ${OUTDIR} + DEPENDS ${__comp_tgts} ${VHDL_SOURCES} + COMMENT ${DESCRIPTION} + ) - find_program(GHDL_EXECUTABLE ghdl - HINTS ${GHDL_HOME}/bin/ $ENV{GHDL_HOME}/bin/ - PATHS ${GHDL_EXECUTABLE} $ENV{GHDL_EXECUTABLE} + add_custom_target(${IP_LIB}_ghdl + DEPENDS ${STAMP_FILE} ${IP_LIB} ) + set_property(TARGET ${IP_LIB}_ghdl PROPERTY DESCRIPTION ${DESCRIPTION}) + endif() + + set(__ghdl_run_cmd ${SIM_EXEC_PATH} ${ARG_RUN_ARGS}) + if(NOT ARG_NO_RUN_TARGET) + if(NOT ARG_RUN_TARGET_NAME) + set(ARG_RUN_TARGET_NAME run_${IP_LIB}_${CMAKE_CURRENT_FUNCTION}) + endif() + set(DESCRIPTION "Run simulation on ${IP_LIB} with ${CMAKE_CURRENT_FUNCTION}") + add_custom_target(${ARG_RUN_TARGET_NAME} + COMMAND ${__ghdl_run_cmd} + COMMENT ${DESCRIPTION} + WORKING_DIRECTORY ${OUTDIR} + DEPENDS ${IP_LIB}_ghdl + ) + set_property(TARGET ${ARG_RUN_TARGET_NAME} PROPERTY DESCRIPTION ${DESCRIPTION}) + endif() + set(SIM_RUN_CMD ${__ghdl_run_cmd} PARENT_SCOPE) + +endfunction() - get_target_property(IPS ${IP_LIB} FLAT_GRAPH) +function(__ghdl_compile_lib IP_LIB) + cmake_parse_arguments(ARG "NO_DEPS" "OUTDIR;STANDARD" "ANALYZE_ARGS" ${ARGN}) + # Check for any unrecognized arguments + if(ARG_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION} passed unrecognized argument " "${ARG_UNPARSED_ARGUMENTS}") + endif() + + include("${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../../hwip.cmake") + + alias_dereference(IP_LIB ${IP_LIB}) + get_target_property(BINARY_DIR ${IP_LIB} BINARY_DIR) + + # get_target_property(LIBRARY ${IP_LIB} LIBRARY) + # if(NOT LIBRARY) + set(LIBRARY work) + # endif() + + if(NOT ARG_OUTDIR) + set(OUTDIR ${BINARY_DIR}/${IP_LIB}_ghdl) + else() + set(OUTDIR ${ARG_OUTDIR}) + endif() + file(MAKE_DIRECTORY ${OUTDIR}) + + if(ARG_NO_DEPS) + set(ARG_NO_DEPS NO_DEPS) + else() + unset(ARG_NO_DEPS) + endif() - unset(_ghdl_analyze_commands) - foreach(_ip ${IPS}) - get_target_property(_lib ${_ip} LIBRARY) - get_target_property(_lib_sources ${_ip} VHDL_SOURCES) + __ghdl_get_standard_arg(STANDARD ${ARG_STANDARD}) - list(APPEND _ghdl_analyze_commands - COMMAND ${GHDL_EXECUTABLE} analyze - --work=${_lib} + # VHDL files and arguments + get_ip_sources(VHDL_SOURCES ${IP_LIB} VHDL ${ARG_NO_DEPS}) + set(__ghdl_analyze_cmd COMMAND ghdl analyze + --std=${STANDARD} + -fsynopsys + ${ARG_ANALYZE_ARGS} + --work=${LIBRARY} --workdir=${OUTDIR} - ${ARG_STANDARD} - -P${OUTDIR} - ${_lib_sources} + ${VHDL_SOURCES} ) - endforeach() - get_target_property(WORK_LIB ${IP_LIB} LIBRARY) - - # TODO split custom_command into analysis commands per IP block, so only analysis of a IP block is done for the files changed of the given IP block, this should speed up for big designs - set(STAMP_FILE "${BINARY_DIR}/${IP_LIB}_${CMAKE_CURRENT_FUNCTION}.stamp") - add_custom_command( - OUTPUT ${ARG_EXECUTABLE} ${STAMP_FILE} - COMMAND ${CMAKE_COMMAND} -E make_directory ${OUTDIR} - ${_ghdl_analyze_commands} - COMMAND ${GHDL_EXECUTABLE} elaborate - -P${OUTDIR} - -o ${ARG_EXECUTABLE} - --work=${WORK_LIB} - --workdir=${OUTDIR} - ${ARG_STANDARD} - ${ARG_TOP_MODULE} - COMMAND touch ${STAMP_FILE} - DEPENDS ${SOURCES} - COMMENT "Running ${CMAKE_CURRENT_FUNCTION} on ${IP_LIB}" - ) - add_custom_target( - ${IP_LIB}_${CMAKE_CURRENT_FUNCTION} - DEPENDS ${ARG_EXECUTABLE} ${IP_LIB} ${STAMP_FILE} + ### Clean files: + # * All .o files from vhdl sources + unset(__clean_files) + foreach(source ${VHDL_SOURCES}) + get_filename_component(source_basename ${source} NAME_WLE) + list(APPEND __clean_files "${OUTDIR}/${source_basename}.o") + endforeach() + list(APPEND __clean_files "${OUTDIR}/${LIBRARY}-obj${STANDARD}.cf") + + if(NOT TARGET ${IP_LIB}_ghdl_complib) + set(DESCRIPTION "Compile VHDL for ${IP_LIB} with ghdl in library ${LIBRARY}") + set(STAMP_FILE "${BINARY_DIR}/${IP_LIB}_${CMAKE_CURRENT_FUNCTION}.stamp") + add_custom_command( + OUTPUT ${STAMP_FILE} + ${__ghdl_analyze_cmd} + COMMAND touch ${STAMP_FILE} + WORKING_DIRECTORY ${OUTDIR} + BYPRODUCTS ${__clean_files} + DEPENDS ${VHDL_SOURCES} + COMMENT ${DESCRIPTION} ) - get_filename_component(EXEC_FN ${ARG_EXECUTABLE} NAME) - get_filename_component(EXEC_DIR ${ARG_EXECUTABLE} DIRECTORY) - - set_property( - TARGET ${IP_LIB}_${CMAKE_CURRENT_FUNCTION} - APPEND PROPERTY ADDITIONAL_CLEAN_FILES - ${OUTDIR} - ${ARG_EXECUTABLE} - ${EXEC_DIR}/e~${EXEC_FN}.o + add_custom_target( + ${IP_LIB}_ghdl_complib + DEPENDS ${STAMP_FILE} ${IP_LIB} ) - - # add_dependencies(${IP_LIB} ${IP_LIB}_${CMAKE_CURRENT_FUNCTION}) + set_property(TARGET ${IP_LIB}_ghdl_complib PROPERTY DESCRIPTION ${DESCRIPTION}) + endif() endfunction() diff --git a/cmake/sim/synopsys/vcs.cmake b/cmake/sim/synopsys/vcs.cmake index a86393f..0964998 100644 --- a/cmake/sim/synopsys/vcs.cmake +++ b/cmake/sim/synopsys/vcs.cmake @@ -1,7 +1,7 @@ include_guard(GLOBAL) function(vcs IP_LIB) - cmake_parse_arguments(ARG "TARGET_PER_IP;NO_RUN_TARGET;GUI" "EXECUTABLE_NAME;RUN_TARGET_NAME" "VLOGAN_ARGS;VHDLAN_ARGS;VCS_ARGS;RUN_ARGS" ${ARGN}) + cmake_parse_arguments(ARG "TARGET_PER_IP;NO_RUN_TARGET;GUI" "OUTDIR;EXECUTABLE_NAME;RUN_TARGET_NAME" "VLOGAN_ARGS;VHDLAN_ARGS;VCS_ARGS;RUN_ARGS" ${ARGN}) if(ARG_UNPARSED_ARGUMENTS) message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION} passed unrecognized argument " "${ARG_UNPARSED_ARGUMENTS}") endif() @@ -24,8 +24,7 @@ function(vcs IP_LIB) # endif() if(NOT ARG_TOP_MODULE) - get_target_property(IP_NAME ${IP_LIB} IP_NAME) - set(ARG_TOP_MODULE ${IP_NAME}) + get_target_property(ARG_TOP_MODULE ${IP_LIB} IP_NAME) endif() if(ARG_VLOGAN_ARGS) @@ -145,8 +144,7 @@ function(__vcs_compile_lib IP_LIB) file(MAKE_DIRECTORY ${OUTDIR}) if(NOT ARG_TOP_MODULE) - get_target_property(IP_NAME ${IP_LIB} IP_NAME) - set(ARG_TOP_MODULE ${IP_NAME}) + get_target_property(ARG_TOP_MODULE ${IP_LIB} IP_NAME) endif() if(ARG_NO_DEPS) diff --git a/examples/simple_vhdl/CMakeLists.txt b/examples/simple_vhdl/CMakeLists.txt new file mode 100644 index 0000000..ea97661 --- /dev/null +++ b/examples/simple_vhdl/CMakeLists.txt @@ -0,0 +1,36 @@ +cmake_minimum_required(VERSION 3.25) +project(simple_mixed_language NONE) + +include("../../SoCMakeConfig.cmake") + +option_enum(SIMULATOR "Which simulator to use" "ghdl;questa;modelsim;xcelium;vcs;all" "ghdl") +if(SIMULATOR STREQUAL "all") + set(ALL_SIMS TRUE) +endif() + +add_ip(tb + DESCRIPTION "Simple verilog testbench") + +ip_sources(${IP} VHDL + tb.vhdl) + +add_subdirectory(adder) +ip_link(${IP} adder) + +if(SIMULATOR STREQUAL "questa" OR SIMULATOR STREQUAL "modelsim" OR ALL_SIMS) + modelsim(${IP}) +endif() + +if(SIMULATOR STREQUAL "xcelium" OR ALL_SIMS) + xcelium(${IP} XMVHDL_ARGS -V200x) +endif() + +if(SIMULATOR STREQUAL "vcs" OR ALL_SIMS) + vcs(${IP}) +endif() + +if(SIMULATOR STREQUAL "ghdl" OR ALL_SIMS) + ghdl(${IP}) +endif() + +help() diff --git a/examples/simple_vhdl/adder/CMakeLists.txt b/examples/simple_vhdl/adder/CMakeLists.txt new file mode 100644 index 0000000..5b4d612 --- /dev/null +++ b/examples/simple_vhdl/adder/CMakeLists.txt @@ -0,0 +1,7 @@ + +add_ip(adder + DESCRIPTION "Just a simple adder") + +ip_sources(${IP} VHDL + adder.vhdl + ) diff --git a/examples/simple_vhdl/adder/adder.vhdl b/examples/simple_vhdl/adder/adder.vhdl new file mode 100644 index 0000000..532d3ea --- /dev/null +++ b/examples/simple_vhdl/adder/adder.vhdl @@ -0,0 +1,16 @@ +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.STD_LOGIC_UNSIGNED.ALL; + +entity adder is + Port ( NUM1 : in STD_LOGIC_VECTOR (4 downto 0) := "00000"; + NUM2 : in STD_LOGIC_VECTOR (4 downto 0) := "00000"; + SUM : out STD_LOGIC_VECTOR (4 downto 0)); +end adder; + +architecture Behavioral of adder is +begin + + SUM <= NUM1 + NUM2; + +end Behavioral; diff --git a/examples/simple_vhdl/tb.vhdl b/examples/simple_vhdl/tb.vhdl new file mode 100644 index 0000000..13811fb --- /dev/null +++ b/examples/simple_vhdl/tb.vhdl @@ -0,0 +1,38 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity tb is +-- Testbench entity has no ports +end entity tb; + +architecture Behavioral of tb is + -- Signal declarations for testbench + signal a, b, o : std_logic_vector(4 downto 0); +begin + + -- Instantiation of the adder + adder_i : entity work.adder + port map ( + NUM1 => a, + NUM2 => b, + SUM => o + ); + + -- Testbench process + stim_proc : process + begin + -- Initialize values + a <= std_logic_vector(to_unsigned(5, 5)); + b <= std_logic_vector(to_unsigned(10, 5)); + wait for 1 ns; + + -- Display the result (VHDL equivalent to $display) + report "Hello world, from SoCMake build system"; + report integer'image(to_integer(unsigned(a))) & " + " & integer'image(to_integer(unsigned(b))) & " = " & integer'image(to_integer(unsigned(o))) ; + + -- Finish the simulation + wait; + end process stim_proc; + +end architecture Behavioral;