### Build Options Configuration Source: https://github.com/mirekez/cpphdl/blob/main/CMakeLists.txt Configures build options for HDLCPP converter, examples, tests, and Tribe CPU models. ```cmake option(CPPHDL_BUILD_HDLCPP "Build the hdlcpp SystemVerilog-to-CppHDL converter" OFF) option(CPPHDL_BUILD_EXAMPLES "Build CppHDL examples" ON) option(CPPHDL_BUILD_TESTS "Build CppHDL standalone tests" ON) option(CPPHDL_BUILD_TRIBE "Build Tribe CPU models and tests" OFF) ``` -------------------------------- ### Declare logic<> Port Source: https://github.com/mirekez/cpphdl/blob/main/doc/spec.md Example of declaring a logic<> port. The port is initialized to nullptr. ```cpp _PORT(logic) data_in = nullptr; ``` -------------------------------- ### Basic CMake Project Setup Source: https://github.com/mirekez/cpphdl/blob/main/hdlcpp/CMakeLists.txt Sets the minimum CMake version, project name, and C++ standard. Ensures C++23 is used. ```cmake cmake_minimum_required(VERSION 3.26...3.30) project(hdlcpp LANGUAGES CXX) set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED OFF) ``` -------------------------------- ### Environment Setup and Build Source: https://github.com/mirekez/cpphdl/blob/main/README.md Commands to create a conda environment, update dependencies, configure CMake, and build the project for both Windows and Linux. ```bash conda create -p ./.conda; source activate base; conda activate ./.conda; conda env update --file requirements.yaml mkdir build; cd build; cmake -DCMAKE_BUILD_TYPE=Release -G "Unix Makefiles" ..; make ``` -------------------------------- ### SystemVerilog Sequential Logic Example Source: https://github.com/mirekez/cpphdl/blob/main/doc/best_practice.md A standard SystemVerilog implementation of a counter with reset and enable. ```systemverilog always_ff @(posedge clk) begin if (reset) begin count <= '0; end else if (enable) begin count <= count + 1; end end assign done = (count == LIMIT); ``` -------------------------------- ### Declare logic<> Variable Source: https://github.com/mirekez/cpphdl/blob/main/doc/spec.md Example of declaring a logic<> variable for output. Ensure MEM_WIDTH_BYTES is defined. ```cpp logic data_out_comb; ``` -------------------------------- ### CMake Project Setup and Source Discovery Source: https://github.com/mirekez/cpphdl/blob/main/examples/CMakeLists.txt Initializes the CMake project, sets the C++ standard, and finds all C++ source files within the current directory and its subdirectories. It also filters out temporary or backup files. ```cmake cmake_minimum_required(VERSION 3.15) project(examples LANGUAGES CXX) set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED OFF) file(GLOB_RECURSE EXAMPLE_SOURCES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp" ) list(FILTER EXAMPLE_SOURCES EXCLUDE REGEX "/[.#][^/]*\.cpp$") list(FILTER EXAMPLE_SOURCES EXCLUDE REGEX "~$”) ``` -------------------------------- ### Test All Script Generation Source: https://github.com/mirekez/cpphdl/blob/main/CMakeLists.txt Generates a bash script to run all examples and tests, with support for a '--noveril' option. ```cmake set(CPPHDL_TEST_ALL_SCRIPT "${CMAKE_BINARY_DIR}/.test_all.sh") file(WRITE "${CPPHDL_TEST_ALL_SCRIPT}" [=[ #!/usr/bin/env bash set -euo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}" )" && pwd)" cd "$ROOT" ARGS=() for ARG in "$@"; do case "$ARG" in --noveril) ARGS+=("$ARG") ;; -h|--help) echo "usage: $0 [--noveril]" exit 0 ;; *) echo "Unknown option: $ARG" >&2 echo "usage: $0 [--noveril]" >&2 exit 2 ;; esac done for DIR in examples tests; do if [[ ! -d "$DIR" ]]; then continue fi while IFS= read -r -d '' EXE; do echo "==> ${EXE}" EXE_DIR="$(dirname "$EXE")" EXE_NAME="$(basename "$EXE")" (cd "$EXE_DIR" && "./$EXE_NAME" "${ARGS[@]}") done < <(find "$DIR" -maxdepth 1 -type f -perm -111 ! -name '*.elf' -print0 | sort -z) done ]=]) file(CHMOD "${CPPHDL_TEST_ALL_SCRIPT}" PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) ``` -------------------------------- ### CppHDL Sequential Logic Equivalent Source: https://github.com/mirekez/cpphdl/blob/main/doc/best_practice.md Translates the SystemVerilog counter example to CppHDL, using `reg` for state and `_work`/`_strobe` for logic. ```cpp reg> count; _PORT(bool) enable_in; _PORT(bool) done_out = _ASSIGN(count == LIMIT); void _work(bool reset) { if (reset) { count.clr(); return; } if (enable_in()) { count._next = count + u<8>(1); } } void _strobe() { count.strobe(); } ``` -------------------------------- ### Implement Memory Module Source: https://github.com/mirekez/cpphdl/blob/main/doc/spec.md This example demonstrates a generic Memory module using the memory<> type for efficient registered memory access. It supports one read and one write port per clock cycle. ```cpp #pragma once #include "cpphdl.h" #include using namespace cpphdl; template class Memory : public Module { reg> data_out_reg; memory buffer; size_t i; public: _PORT(u) write_addr_in; _PORT(bool) write_in; _PORT(logic) write_data_in; _PORT(logic) write_mask_in; _PORT(u) read_addr_in; _PORT(bool) read_in; _PORT(logic) read_data_out = _ASSIGN_REG( data_out_comb_func() ); bool debugen_in; void _assign() {} logic data_out_comb; logic& data_out_comb_func() { if (SHOWAHEAD) { data_out_comb = buffer[read_addr_in()]; } else { data_out_comb = data_out_reg; } return data_out_comb; } logic mask; void _work(bool reset) { if (write_in()) { mask = 0; for (i=0; i < MEM_WIDTH_BYTES; ++i) { mask.bits((i+1)*8-1,i*8) = write_mask_in()[i] ? 0xFF : 0 ; } buffer[write_addr_in()] = (buffer[write_addr_in()]&~mask) | (write_data_in()&mask); } if (!SHOWAHEAD) { data_out_reg._next = buffer[read_addr_in()]; } if (debugen_in) { std::print("{:s}: input: ({}){}@{}({}), output: ({}){}@{} \n", __inst_name, (int)write_in(), write_data_in(), write_addr_in(), write_mask_in(), (int)read_in(), read_data_out(), read_addr_in()); } } void _strobe() { buffer.apply(); data_out_reg.strobe(); } }; ``` -------------------------------- ### Compiling Example Executables with C++ Standard Flags Source: https://github.com/mirekez/cpphdl/blob/main/examples/CMakeLists.txt Iterates through discovered source files, creates an executable for each, and attempts to set the C++ standard to C++26 or C++2c if supported by the compiler. It also includes a placeholder for optimization flags. ```cmake foreach(src ${EXAMPLE_SOURCES}) get_filename_component(name ${src} NAME_WE) # prefix with relative path for uniqueness file(RELATIVE_PATH rel_path ${CMAKE_CURRENT_SOURCE_DIR} ${src}) string(REPLACE "/" "_" target_name ${rel_path}) string(REPLACE ".cpp" "" target_name ${target_name}) add_executable(${target_name} ${src}) include(CheckCXXCompilerFlag) foreach(flag -std=c++26 -std=c++2c) check_cxx_compiler_flag("${flag}" HAS_FLAG) if(HAS_FLAG) target_compile_options(${target_name} PRIVATE "${flag}") message(STATUS "Using ${flag}") break() endif() endforeach() cpphdl_link_stdcxxexp_if_available(${target_name}) # target_compile_options(${target_name} PRIVATE "-O2") set_target_properties(${target_name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/examples RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/examples RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/examples ) include_directories("../include") # verilator tests add_custom_command( TARGET ${target_name} POST_BUILD COMMAND ${CMAKE_COMMAND} -E env bash -c "../cpphdl ${CMAKE_CURRENT_SOURCE_DIR}/${rel_path} -I${CMAKE_SOURCE_DIR}/include ${CPPHDL_TOOLCHAIN_ARGS}" COMMAND ${CMAKE_COMMAND} -E env bash -c "cp ${CMAKE_CURRENT_SOURCE_DIR}/tuple/rv32i.bin ${CMAKE_BINARY_DIR}/examples/" COMMAND ${CMAKE_COMMAND} -E env bash -c "cp ${CMAKE_CURRENT_SOURCE_DIR}/tuple/rv32i.log ${CMAKE_BINARY_DIR}/examples/" WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/examples ) cpphdl_add_host_opt_flags(${target_name}) target_compile_options(${target_name} PRIVATE "-fno-strict-aliasing") add_dependencies(${target_name} cpphdl) endforeach() ``` -------------------------------- ### Find LLVM/Clang from Prefixes Source: https://github.com/mirekez/cpphdl/blob/main/CMakeLists.txt Searches for LLVM and Clang installations in a predefined list of common prefixes and adds them to CMAKE_PREFIX_PATH if found and compatible. ```cmake foreach(CPPHDL_LLVM_PREFIX "${CMAKE_SOURCE_DIR}/.conda" "/usr/lib/llvm-20" "/usr/lib/llvm-19" "/usr/lib/llvm-18" "/usr/lib/llvm-17" "/usr/lib/llvm-16" "/usr/lib/llvm-15") if((CPPHDL_USE_LOCAL_CONDA OR NOT CPPHDL_LLVM_PREFIX STREQUAL "${CMAKE_SOURCE_DIR}/.conda") AND EXISTS "${CPPHDL_LLVM_PREFIX}/include/clang/Tooling/Tooling.h") cpphdl_llvm_prefix_works("${CPPHDL_LLVM_PREFIX}" CPPHDL_PREFIX_WORKS) if(CPPHDL_PREFIX_WORKS) list(APPEND CMAKE_PREFIX_PATH "${CPPHDL_LLVM_PREFIX}") list(APPEND CPPHDL_LLVM_PREFIXES "${CPPHDL_LLVM_PREFIX}") endif() endif() endforeach() if(CPPHDL_LLVM_PREFIXES) find_package(LLVM REQUIRED CONFIG PATHS ${CPPHDL_LLVM_PREFIXES} NO_DEFAULT_PATH) find_package(Clang REQUIRED CONFIG PATHS ${CPPHDL_LLVM_PREFIXES} NO_DEFAULT_PATH) include_directories(${LLVM_INCLUDE_DIRS}) include_directories(${CLANG_INCLUDE_DIRS}) ``` -------------------------------- ### Custom SystemVerilog Module Generation with CPPHDL_REPLACEMENT Source: https://github.com/mirekez/cpphdl/blob/main/doc/spec.md Example of using the `[[clang::annotate("CPPHDL_REPLACEMENT=...")]]` attribute to provide custom SystemVerilog code for a `cpphdl::Module`. This bypasses normal generation for the annotated module. ```cpp class [[clang::annotate("CPPHDL_REPLACEMENT=" "`default_nettype none\n" "\n" "module AnnotateReplacement (\n" " input wire clk\n" ", input wire reset\n" ", input wire[8-1:0] value_in\n" ", output wire[8-1:0] value_out\n" ");\n" " assign value_out = value_in ^ 8'hA5;\n" "endmodule\n" ";"`)]] AnnotateReplacement : public Module { public: _PORT(u<8>) value_in; _PORT(u<8>) value_out = _ASSIGN_REG(value_comb_func()); private: u<8> value_comb; u<8>& value_comb_func() { return value_comb = value_in() ^ u<8>(0xa5); } public: void _work(bool reset) {} void _strobe() {} void _assign() {} }; ``` -------------------------------- ### Declare Unsigned Variable Source: https://github.com/mirekez/cpphdl/blob/main/doc/spec.md Example of declaring an unsigned variable of a specific width. Supports math operators and casting to logic<>. ```cpp u cmd_steps; ``` -------------------------------- ### Check LLVM Prefix Compatibility Source: https://github.com/mirekez/cpphdl/blob/main/CMakeLists.txt Verifies if a given LLVM installation prefix is functional by attempting to compile and run a small test program that uses LLVM's raw_ostream. ```cmake function(cpphdl_llvm_prefix_works prefix out_var) set(${out_var} TRUE PARENT_SCOPE) if(NOT EXISTS "${prefix}/lib/libLLVM.so") return() endif() set(test_dir "${CMAKE_BINARY_DIR}/CMakeFiles/cpphdl-llvm-prefix-check") file(MAKE_DIRECTORY "${test_dir}") file(WRITE "${test_dir}/llvm_runtime_check.cpp" [=[ #include "llvm/Support/raw_ostream.h" int main() { llvm::outs() << ""; return 0; } ]=]) execute_process( COMMAND "${CMAKE_CXX_COMPILER}" "${test_dir}/llvm_runtime_check.cpp" "-I${prefix}/include" "-L${prefix}/lib" "-Wl,-rpath,${prefix}/lib" "-lLLVM" "-o" "${test_dir}/llvm_runtime_check" RESULT_VARIABLE compile_result OUTPUT_VARIABLE compile_output ERROR_VARIABLE compile_error ) if(NOT compile_result EQUAL 0) message(STATUS "Skipping LLVM prefix ${prefix}: runtime probe did not link") set(${out_var} FALSE PARENT_SCOPE) return() endif() execute_process( COMMAND "${test_dir}/llvm_runtime_check" RESULT_VARIABLE run_result OUTPUT_VARIABLE run_output ERROR_VARIABLE run_error ) if(NOT run_result EQUAL 0) string(STRIP "${run_output}\n${run_error}" runtime_error) message(STATUS "Skipping LLVM prefix ${prefix}: runtime probe failed: ${runtime_error}") set(${out_var} FALSE PARENT_SCOPE) endif() endfunction() ``` -------------------------------- ### logic<> Slice Assignment from Source Source: https://github.com/mirekez/cpphdl/blob/main/doc/spec.md Example of assigning slices from a source logic<> variable to smaller logic<> variables. The slice width is implied by the expression shape. ```cpp direct_comb = source_comb.bits(word * 16 + 15, word * 16); ``` ```cpp byte_comb = source_comb.bits(word * 8 + 7, word * 8); ``` -------------------------------- ### Concatenate for Register Next-State Source: https://github.com/mirekez/cpphdl/blob/main/doc/spec.md Example of using Cat{...} for a register's next-state assignment. The destination type must be wide enough for the concatenated result. ```cpp packet_reg._next = Cat{reg_byteenable, reg_address, reg_data}; ``` -------------------------------- ### Build Instructions for CppHDL (Linux) Source: https://github.com/mirekez/cpphdl/blob/main/README.md Steps to clone the repository and set up the build environment on Linux using Miniconda. ```bash git clone ssh://github.com/mirekez/cpphdl; cd cpphdl wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh; ./Miniconda3-latest-Linux-x86_64.sh source ~/miniconda3/bin/activate; conda init ``` -------------------------------- ### Build Instructions for CppHDL (Windows) Source: https://github.com/mirekez/cpphdl/blob/main/README.md Steps to clone the repository and set up the build environment on Windows using msys2 and Miniconda. ```bash git clone https://github.com/mirekez/cpphdl; cd cpphdl ``` -------------------------------- ### Fetch and Configure LLVM/Clang Source: https://github.com/mirekez/cpphdl/blob/main/CMakeLists.txt Fetches LLVM/Clang from a Git repository using FetchContent and configures build settings for C++17 and specific compiler flags. ```cmake elseif(CPPHDL_FETCH_LLVM) set(CPPHDL_USING_FETCHED_LLVM TRUE) message(STATUS "CppHDL using fetched LLVM/Clang") include(FetchContent) set(LLVM_ENABLE_PROJECTS clang CACHE STRING "" FORCE) set(LLVM_TARGETS_TO_BUILD Native CACHE STRING "" FORCE) set(LLVM_INCLUDE_BENCHMARKS OFF CACHE BOOL "" FORCE) set(LLVM_INCLUDE_DOCS OFF CACHE BOOL "" FORCE) set(LLVM_INCLUDE_EXAMPLES OFF CACHE BOOL "" FORCE) set(LLVM_INCLUDE_TESTS OFF CACHE BOOL "" FORCE) set(LLVM_INCLUDE_TOOLS ON CACHE BOOL "" FORCE) set(LLVM_INCLUDE_UTILS OFF CACHE BOOL "" FORCE) set(LLVM_ENABLE_BINDINGS OFF CACHE BOOL "" FORCE) set(LLVM_ENABLE_TERMINFO OFF CACHE BOOL "" FORCE) set(LLVM_ENABLE_LIBXML2 OFF CACHE BOOL "" FORCE) set(LLVM_ENABLE_ZLIB OFF CACHE BOOL "" FORCE) set(LLVM_ENABLE_ZSTD OFF CACHE BOOL "" FORCE) set(LLVM_ENABLE_RTTI ON CACHE BOOL "" FORCE) set(CLANG_BUILD_TOOLS OFF CACHE BOOL "" FORCE) set(CLANG_INCLUDE_DOCS OFF CACHE BOOL "" FORCE) set(CLANG_INCLUDE_TESTS OFF CACHE BOOL "" FORCE) FetchContent_Declare(cpphdl_llvm_project GIT_REPOSITORY https://github.com/llvm/llvm-project.git GIT_TAG ${CPPHDL_FETCH_LLVM_TAG} GIT_SHALLOW TRUE SOURCE_SUBDIR llvm ) set(CPPHDL_SAVED_CMAKE_CXX_STANDARD "${CMAKE_CXX_STANDARD}") set(CPPHDL_SAVED_CMAKE_CXX_STANDARD_REQUIRED "${CMAKE_CXX_STANDARD_REQUIRED}") set(CPPHDL_SAVED_CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") set(CPPHDL_SAVED_CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=x86-64 -mtune=generic") ``` -------------------------------- ### Run Tests with CTest Source: https://github.com/mirekez/cpphdl/blob/main/README.md Navigate to the build directory and execute CTest to run the project's tests. Specific environment variables and scripts may be required for CPU tests. ```bash cd build ctest # be sure you provided RISCV_HOME=<> path to riscv-gnu-toolchain build if you want to run CPU tests # you need also to run .load_sail_riscv_sim.sh and .load_spike.sh in tribe/tests/ if you want to run CPU tests ``` -------------------------------- ### CppHDL Output Port Initialization Source: https://github.com/mirekez/cpphdl/blob/main/doc/best_practice.md Shows different methods for initializing output ports, including registered and assigned values. ```cpp reg ready_reg; _PORT(bool) ready_out = _ASSIGN_REG(ready_reg); _PORT(bool) empty_out = _ASSIGN(count == 0); ``` -------------------------------- ### CppHDL Port Declaration and Usage Source: https://github.com/mirekez/cpphdl/blob/main/doc/best_practice.md Demonstrates declaring input ports and reading their values within logic. ```cpp _PORT(bool) valid_in; _PORT(logic<32>) data_in; if (valid_in()) { word._next = data_in(); } ``` -------------------------------- ### Setting Up Spike Fragments Test Source: https://github.com/mirekez/cpphdl/blob/main/tribe/tests/CMakeLists.txt Configures a test to run spike fragments using a Python script. It specifies the working directory and sets properties like skip return code and timeout. ```cmake add_test( NAME ${prefix}_rv32_spike_fragments COMMAND "${TRIBE_RISCV_TOOLCHAIN_RUNNER}" "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/scripts/run_spike_fragments.py" "$" "${TRIBE_SPIKE_WORK_DIR}-${work_suffix}" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) set_tests_properties(${prefix}_rv32_spike_fragments PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 180 ) ``` -------------------------------- ### Configure riscv-arch-test with Verilator Source: https://github.com/mirekez/cpphdl/blob/main/tribe/tests/CMakeLists.txt Sets up properties for the riscv-arch-test with Verilator backend, including skip return codes, timeouts, and environment variables. ```cmake set_tests_properties(${prefix}_riscv_arch_test_verilator PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 1200 ENVIRONMENT "UV_PYTHON=3.10;MISE_DATA_DIR=${CMAKE_BINARY_DIR}/mise-data;MISE_CACHE_DIR=${CMAKE_BINARY_DIR}/mise-cache;MISE_CONFIG_DIR=${CMAKE_BINARY_DIR}/mise-config;MISE_STATE_DIR=${CMAKE_BINARY_DIR}/mise-state;XDG_DATA_HOME=${CMAKE_BINARY_DIR}/xdg-data;XDG_CACHE_HOME=${CMAKE_BINARY_DIR}/xdg-cache;TRIBE_ARCH_TEST_BACKEND=verilator;TRIBE_ARCH_TEST_VERILATOR_BIN=${verilator_bin};CPPHDL_TOOLCHAIN_ARGS=${CPPHDL_TOOLCHAIN_ARGS}" ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:${CMAKE_BINARY_DIR}/pydeps/bin" ) ``` -------------------------------- ### Executable Target and Linking Source: https://github.com/mirekez/cpphdl/blob/main/hdlcpp/CMakeLists.txt Defines the main 'hdlcpp' executable, sets include directories, and links against the 'slang' library. This is the final step in building the project. ```cmake add_executable(hdlcpp hdlcpp.cpp) target_include_directories(hdlcpp PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/..") target_link_libraries(hdlcpp PRIVATE slang::slang) ``` -------------------------------- ### Access logic<> Bits and Slices Source: https://github.com/mirekez/cpphdl/blob/main/doc/spec.md Demonstrates accessing individual bits using operator[] and partial bitmaps using the .bits(hi,lo) method. The .bits() method supports indexed slices. ```cpp buffer1_byteenable._next[addr_sub+i] = 1; ``` ```cpp host_addr.bits(39,32) = s_writedata_in() >> 32; ``` -------------------------------- ### SystemVerilog Continuous Assignments Source: https://github.com/mirekez/cpphdl/blob/main/doc/spec.md Illustrates SystemVerilog continuous assignments for output ports. ```systemverilog output wire out ); assign out = a + b; assign valid_out = valid; ``` -------------------------------- ### Using Bit Slicing with Logic Types Source: https://github.com/mirekez/cpphdl/blob/main/doc/best_practice.md Demonstrates how to use `.bits(hi, lo)` and `operator[]` for accessing specific bit ranges within a `logic<64>` type. ```cpp logic<64> word; word.bits(31, 0) = low_word; word.bits(63, 32) = high_word; word[0] = parity_bit; ``` -------------------------------- ### Configuring CMake for CppHDL Project Source: https://github.com/mirekez/cpphdl/blob/main/doc/best_practice.md Shows the command to configure the build system for a CppHDL project using CMake. ```bash cmake -S . -B build ``` -------------------------------- ### Configure riscv-dv test with Verilator Source: https://github.com/mirekez/cpphdl/blob/main/tribe/tests/CMakeLists.txt Sets up properties for the riscv-dv test with Verilator backend, including skip return codes, timeouts, and environment variables. ```cmake set_tests_properties(${prefix}_riscv_dv_verilator PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 120 ENVIRONMENT "TRIBE_RISCV_DV_PYTHON=/usr/bin/python3;TRIBE_RISCV_DV_ISA=rv32imac_zicsr_zifencei;TRIBE_RISCV_DV_BACKEND=verilator;TRIBE_RISCV_DV_VERILATOR_BIN=${verilator_bin};CPPHDL_TOOLCHAIN_ARGS=${CPPHDL_TOOLCHAIN_ARGS}" ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:/usr/bin" ) ``` -------------------------------- ### CppHDL Work Method for Sequential Logic Source: https://github.com/mirekez/cpphdl/blob/main/doc/spec.md Maps SystemVerilog always_ff blocks to a CppHDL _work method for sequential logic. ```cpp reg> count_reg; reg valid_reg; void _work(bool reset) { if (reset) { count_reg.clr(); valid_reg.clr(); return; } if (enable_in()) { count_reg._next = count_reg + u<8>(1); } valid_reg._next = enable_in(); } ``` -------------------------------- ### Configure riscv-dv test Source: https://github.com/mirekez/cpphdl/blob/main/tribe/tests/CMakeLists.txt Sets up properties for the riscv-dv test, including skip return codes, timeouts, and environment variables. ```cmake set_tests_properties(${prefix}_riscv_dv PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 120 ENVIRONMENT "TRIBE_RISCV_DV_PYTHON=/usr/bin/python3;TRIBE_RISCV_DV_ISA=rv32imac_zicsr_zifencei" ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:/usr/bin" ) ``` -------------------------------- ### Project Definition Source: https://github.com/mirekez/cpphdl/blob/main/CMakeLists.txt Defines the project name, version, and supported languages (C, C++). ```cmake project(cpphdl VERSION 0.8 LANGUAGES C CXX) ``` -------------------------------- ### Running Native CppHDL Simulation Source: https://github.com/mirekez/cpphdl/blob/main/doc/best_practice.md Command to execute a CppHDL test in native mode, bypassing Verilator. ```bash ./build/tests/interface_ValidReady --noveril ``` -------------------------------- ### Building a Specific CppHDL Test Target Source: https://github.com/mirekez/cpphdl/blob/main/doc/best_practice.md Demonstrates how to build a single executable target for a CppHDL test using CMake. ```bash cmake --build build --target interface_ValidReady ``` -------------------------------- ### VRResponder Module Implementation Source: https://github.com/mirekez/cpphdl/blob/main/doc/spec.md Implements a responder module for the DataValidReady interface. It drives the ready signal and captures data when valid and ready are asserted. ```cpp template class VRResponder : public Module { public: DataValidReadyIf sink_in; private: reg ready_reg; reg> last_data_reg; public: void _assign() { sink_in.ready_out = _ASSIGN_REG(ready_reg); } void _work(bool reset) { if (reset) { ready_reg.clr(); last_data_reg.clr(); return; } ready_reg._next = 1; if (sink_in.valid_in() && sink_in.ready_out()) { last_data_reg._next = sink_in.data_in(); } } void _strobe() { ready_reg.strobe(); last_data_reg.strobe(); } }; ``` -------------------------------- ### CppHDL Indexed Port Assignment in Loops Source: https://github.com/mirekez/cpphdl/blob/main/doc/best_practice.md Illustrates using `_ASSIGN_I` within a loop to assign values to indexed ports. ```cpp for (i = 0; i < N; ++i) { out[i].valid_in = _ASSIGN_I(sel == i ? input.valid_in() : 0); } ``` -------------------------------- ### Static Build Definition Source: https://github.com/mirekez/cpphdl/blob/main/CMakeLists.txt Defines the STATIC_BUILD preprocessor macro. ```cmake add_definitions(-DSTATIC_BUILD) ``` -------------------------------- ### Running Full CppHDL Test with Verilator Source: https://github.com/mirekez/cpphdl/blob/main/doc/best_practice.md Command to run a CppHDL test, including the Verilator-based simulation if supported by the inline test. ```bash ./build/tests/interface_ValidReady ``` -------------------------------- ### CppHDL Command Line Syntax Source: https://github.com/mirekez/cpphdl/blob/main/doc/spec.md The basic command-line syntax for invoking the cpphdl tool, including source files and compilation parameters. It leverages the LLVM clang frontend. ```bash cpphdl ... [compilation parameters] ``` -------------------------------- ### TestValidReady Module for Interface Connection Source: https://github.com/mirekez/cpphdl/blob/main/doc/spec.md A test module that instantiates and connects VRDriver and VRResponder modules using the `assignIf` function. This demonstrates bidirectional interface signal handling. ```cpp class TestValidReady : public Module { VRDriver<32> driver; VRResponder<32> responder; public: void _assign() { driver.__inst_name = __inst_name + "/driver"; responder.__inst_name = __inst_name + "/responder"; assignIf(driver, responder, driver.source_out, responder.sink_in); } }; ``` -------------------------------- ### VRDriver Module Implementation Source: https://github.com/mirekez/cpphdl/blob/main/doc/spec.md Implements a driver module for the DataValidReady interface. It manages the valid and data signals and interacts with the ready signal from the interface. ```cpp template class VRDriver : public Module { public: DataValidReadyIf source_out; private: reg valid_reg; reg> data_reg; public: void _assign() { source_out.valid_in = _ASSIGN_REG(valid_reg); source_out.data_in = _ASSIGN_REG(data_reg); } void _work(bool reset) { if (reset) { valid_reg.clr(); data_reg.clr(); return; } valid_reg._next = 1; if (!valid_reg || source_out.ready_out()) { data_reg._next = data_reg + logic(1); } } void _strobe() { valid_reg.strobe(); data_reg.strobe(); } }; ``` -------------------------------- ### CppHDL Sequential Logic Work and Strobe Source: https://github.com/mirekez/cpphdl/blob/main/doc/best_practice.md Implements sequential logic by defining next-state updates in `_work` and committing them in `_strobe`. ```cpp void _work(bool reset) { if (reset) { valid_reg.clr(); return; } valid_reg._next = next_valid; data_reg._next = next_data; } void _strobe() { valid_reg.strobe(); data_reg.strobe(); } ``` -------------------------------- ### Conditional Compiler Configuration Source: https://github.com/mirekez/cpphdl/blob/main/CMakeLists.txt Sets C and C++ compilers and generator for Windows if not supported yet, using LLVM. ```cmake #good take for windows while not supported yet #if(WIN32) #set (CMAKE_LINKER "ld64.lld.exe") #set (CMAKE_C_COMPILER "clang") #set (CMAKE_CXX_COMPILER "clang++") #set (CMAKE_GENERATOR "Unix Makefiles") #set (CMAKE_MAKE_PROGRAM make) #set (CMAKE_C_COMPILER_FORCED "TRUE") #set (CMAKE_CXX_COMPILER_FORCED "TRUE") #endif() ``` -------------------------------- ### CppHDL Interface Assignment using assignIf Source: https://github.com/mirekez/cpphdl/blob/main/doc/best_practice.md Connects two interface instances (driver and responder) using `assignIf` for bidirectional signal assignment. ```cpp VRDriver<32> driver; VRResponder<32> responder; void _assign() { assignIf(driver, responder, driver.source_out, responder.sink_in); } ``` -------------------------------- ### Configure RISC-V Opcodes for Spec Decode Tests Source: https://github.com/mirekez/cpphdl/blob/main/tribe/CMakeLists.txt Configures the build to generate RISC-V opcode cases for spec decode tests. It finds the Python 3 interpreter and uses Python scripts to ensure opcode data and generate header files. ```cmake if(BUILD_TESTING) add_subdirectory(tests) set(RISCV_OPCODES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tests/riscv-opcodes" CACHE PATH "Path to a local riscv/riscv-opcodes checkout") if(NOT RISCV_OPCODES_DIR) set(RISCV_OPCODES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tests/riscv-opcodes") endif() add_executable(tribe_spec_decode_tests tests/spec_decode_tests.cpp ) find_package(Python3 REQUIRED COMPONENTS Interpreter) set(TRIBE_RISCV_OPCODE_CASES_HEADER "${CMAKE_CURRENT_BINARY_DIR}/generated/riscv_opcode_cases.generated.h" ) add_custom_command( OUTPUT "${TRIBE_RISCV_OPCODE_CASES_HEADER}" COMMAND Python3::Interpreter "${CMAKE_CURRENT_SOURCE_DIR}/tests/scripts/ensure_riscv_opcodes.py" "${RISCV_OPCODES_DIR}" COMMAND Python3::Interpreter "${CMAKE_CURRENT_SOURCE_DIR}/tests/scripts/generate_riscv_opcode_cases.py" "${RISCV_OPCODES_DIR}" "${TRIBE_RISCV_OPCODE_CASES_HEADER}" DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/tests/scripts/ensure_riscv_opcodes.py" "${CMAKE_CURRENT_SOURCE_DIR}/tests/scripts/generate_riscv_opcode_cases.py" COMMENT "Generating tribe spec decode opcode cases from riscv-opcodes" VERBATIM ) target_sources(tribe_spec_decode_tests PRIVATE "${TRIBE_RISCV_OPCODE_CASES_HEADER}") target_include_directories(tribe_spec_decode_tests PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/generated" ) target_include_directories(tribe_spec_decode_tests PRIVATE "../include" "spec" ) target_compile_options(tribe_spec_decode_tests PRIVATE "-fno-strict-aliasing" ) add_test( NAME tribe_spec_decode_tests COMMAND Python3::Interpreter "${CMAKE_CURRENT_SOURCE_DIR}/tests/scripts/run_spec_decode_tests.py" "${RISCV_OPCODES_DIR}" "$" ) endif() ``` -------------------------------- ### Mapping SystemVerilog Continuous Assignments to CppHDL Source: https://github.com/mirekez/cpphdl/blob/main/doc/best_practice.md Directly maps SystemVerilog 'assign' statements and module port connections to CppHDL's `_assign()` section. Use `_ASSIGN` for expressions and `_ASSIGN_REG` for direct storage bindings. This section runs once before the work cycle. ```systemverilog assign out = a + b; child.valid_i = valid; child.data_i = data[i]; child.result_i[i] = result[i] ^ mask; ``` ```cpp _PORT(u<32>) out = _ASSIGN(a_in() + b_in()); void _assign() { child.valid_in = _ASSIGN(valid_reg); for (int i = 0; i < LANES; i++) { child.data_in[i] = _ASSIGN_I(data_reg[i]); child.result_in[i] = _ASSIGN_I(result_reg[i] ^ mask_reg); } } ``` -------------------------------- ### LLVM Fetching and Compiler Conflict Check Source: https://github.com/mirekez/cpphdl/blob/main/CMakeLists.txt Checks for conflicts when fetching LLVM and a local compiler is already configured. ```cmake if(CPPHDL_FETCH_LLVM) foreach(CPPHDL_COMPILER_VAR CMAKE_C_COMPILER CMAKE_CXX_COMPILER) if(DEFINED ${CPPHDL_COMPILER_VAR} AND "${${CPPHDL_COMPILER_VAR}}" MATCHES "^${CMAKE_SOURCE_DIR}/\.conda/") message(FATAL_ERROR "CPPHDL_FETCH_LLVM is enabled, but ${CPPHDL_COMPILER_VAR} is cached as " "${${CPPHDL_COMPILER_VAR}}. Remove the build directory or configure with " "CC=/usr/bin/cc CXX=/usr/bin/c++ so fetched LLVM tools can run on this CPU.") endif() endforeach() endif() ``` -------------------------------- ### Set C++ Standard and Required Source: https://github.com/mirekez/cpphdl/blob/main/CMakeLists.txt Configures the C++ standard to C++23 and specifies whether it's strictly required. ```cmake set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED OFF) ``` -------------------------------- ### Baremetal Build Script Generation Source: https://github.com/mirekez/cpphdl/blob/main/tribe/CMakeLists.txt Generates a bash script for building baremetal RISC-V executables. This script is invoked after the main target is built. ```cmake set(TRIBE_BUILD_BAREMETAL_SCRIPT "${CMAKE_BINARY_DIR}/tribe_build_baremetal.sh") file(WRITE "${TRIBE_BUILD_BAREMETAL_SCRIPT}" [=[ #!/usr/bin/env bash set -euo pipefail SRC_DIR="$1" OUT_DIR="$2" find_tool() { local name="$1" if [[ -n "${RISCV_HOME:-}" && -x "${RISCV_HOME}/bin/${name}" ]]; then printf '%s\n' "${RISCV_HOME}/bin/${name}" return 0 fi if command -v "${name}" >/dev/null 2>&1; then command -v "${name}" return 0 fi if [[ -n "${RISCV:-}" && -x "${RISCV}/bin/${name}" ]]; then printf '%s\n' "${RISCV}/bin/${name}" return 0 fi if [[ -x "${HOME}/riscv/bin/${name}" ]]; then printf '%s\n' "${HOME}/riscv/bin/${name}" return 0 fi return 1 } GCC="$(find_tool riscv32-unknown-elf-gcc" || { echo "missing RISC-V compiler: riscv32-unknown-elf-gcc" >&2 exit 1 }) mkdir -p "${OUT_DIR}" "${GCC}" -march=rv32im_zicsr -mabi=ilp32 \ -O2 -g -ffreestanding -fno-builtin -msmall-data-limit=0 -mno-relax \ -nostdlib -nostartfiles -Wl,-Ttext=0 \ -I "${SRC_DIR}/code" \ "${SRC_DIR}/code/rv32i.c" \ -o "${OUT_DIR}/rv32i.bin" "${GCC}" -march=rv32im_zicsr -mabi=ilp32 \ -nostdlib -nostartfiles -Wl,-Ttext=0 \ "${SRC_DIR}/code/uart.S" \ -o "${OUT_DIR}/uart.elf" cp "${SRC_DIR}/code/rv32i.c" "${OUT_DIR}/" cp "${SRC_DIR}/code/rv32i.log" "${OUT_DIR}/" cp "${SRC_DIR}/code/uart.S" "${OUT_DIR}/" cp "${SRC_DIR}/code/uart.log" "${OUT_DIR}/" ]=]) file(CHMOD "${TRIBE_BUILD_BAREMETAL_SCRIPT}" PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) ``` -------------------------------- ### C++ Defines and Structures in CppHDL Source: https://github.com/mirekez/cpphdl/blob/main/doc/spec.md This snippet demonstrates the usage of C++ defines and structures within a CppHDL source file. It includes necessary headers and defines constants and a packed structure for configuration. ```cpp #pragma once #include "cpphdl.h" #include "PrjConfig.h" using namespace cpphdl; #define CMD_IDLE 0 #define CMD_RESET 1 #define CMD_CONFIG 2 struct CmdConfig { unsigned cmd_id; unsigned units:6; unsigned flags:2; unsigned address; }__PACKED; static_assert (sizeof(CmdConfig) == 4, "struct CmdConfig size is not correct"); ``` -------------------------------- ### Test All Script Generation Source: https://github.com/mirekez/cpphdl/blob/main/tribe/tests/CMakeLists.txt Generates a bash script to run all tests. It supports a --noveril flag and handles unknown options. ```cmake set(TRIBE_TEST_ALL_SCRIPT "${CMAKE_BINARY_DIR}/tribe/.test_all.sh") file(WRITE "${TRIBE_TEST_ALL_SCRIPT}" [=[#!/usr/bin/env bash set -euo pipefail ROOT=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\ncd "$ROOT" ARGS=() for ARG in "$@"; do case "$ARG" in --noveril) ARGS+=("$ARG") ;; -h|--help) echo "usage: $0 [--noveril]" exit 0 ;; *) echo "Unknown option: $ARG" >&2 echo "usage: $0 [--noveril]" >&2 exit 2 ;; esac done DIR="tests" if [[ ! -d "$DIR" ]]; then echo "Missing tests directory: $ROOT/$DIR" >&2 exit 1 fi BUILD_DIR=\"$(cd \"$ROOT/..\" && pwd)\"\nwhile IFS= read -r -d '' EXE; do TARGET_NAME=\"$(basename \"$EXE\")\" make -C \"$BUILD_DIR\" \"$TARGET_NAME\" done < <(find "$DIR" -maxdepth 1 -type f -perm -111 ! -name '*.elf' ! -name 'Linux_test' -print0 | sort -z) while IFS= read -r -d '' EXE; do echo "==> ${EXE}" EXE_DIR=\"$(dirname \"$EXE\")\" EXE_NAME=\"$(basename \"$EXE\")\" (cd \"$EXE_DIR\" && "./$EXE_NAME" "${ARGS[@]}") done < <(find "$DIR" -maxdepth 1 -type f -perm -111 ! -name '*.elf' ! -name 'Linux_test' -print0 | sort -z) ]=]) file(CHMOD "${TRIBE_TEST_ALL_SCRIPT}" PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) ``` -------------------------------- ### Array in C++ Struct to SystemVerilog Source: https://github.com/mirekez/cpphdl/blob/main/doc/spec.md Demonstrates the conversion of a C++ struct containing a `cpphdl::array` to a SystemVerilog packed struct. Note the alignment fields and reversed field order. ```cpp struct ArrayPayload { unsigned prefix:4; array bytes; unsigned mid:3; array halfs; unsigned tail:5; } __PACKED; ``` ```systemverilog package ArrayPayload_pkg; typedef struct packed { logic[3-1:0] _align0; logic[5-1:0] tail; logic[1-1:0][16-1:0] halfs; logic[5-1:0] _align2; logic[3-1:0] mid; logic[3-1:0][8-1:0] bytes; logic[4-1:0] _align1; logic[4-1:0] prefix; } ArrayPayload; endpackage ``` -------------------------------- ### SystemVerilog Always Comb Block Source: https://github.com/mirekez/cpphdl/blob/main/doc/spec.md Demonstrates a SystemVerilog always_comb block for combinational logic. ```systemverilog always_comb begin hit = valid && tag == req_tag; read_data = hit ? line[word] : '0; end ``` -------------------------------- ### C Compiler Flags Initialization Source: https://github.com/mirekez/cpphdl/blob/main/CMakeLists.txt Initializes C compiler flags for different build types (Debug, MinSizeRel, Release, RelWithDebInfo). ```cmake SET (CMAKE_C_FLAGS_DEBUG_INIT "-g") SET (CMAKE_C_FLAGS_MINSIZEREL_INIT "-Os -DNDEBUG -g") SET (CMAKE_C_FLAGS_RELEASE_INIT "-O2 -DNDEBUG -g") SET (CMAKE_C_FLAGS_RELWITHDEBINFO_INIT "-O2") ``` -------------------------------- ### Executable Test Target Creation Source: https://github.com/mirekez/cpphdl/blob/main/tribe/tests/CMakeLists.txt Defines a CMake executable target for each test source file, setting its runtime output directory. ```cmake foreach(src ${TRIBE_TEST_SOURCES}) get_filename_component(target_name ${src} NAME_WE) add_executable(${target_name} ${src}) set_target_properties(${target_name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_CURRENT_BINARY_DIR} RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_CURRENT_BINARY_DIR} ) target_include_directories(${target_name} PRIVATE "${CMAKE_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/.." "${CMAKE_CURRENT_SOURCE_DIR}/../common" "${CMAKE_CURRENT_SOURCE_DIR}/../cache" "${CMAKE_CURRENT_SOURCE_DIR}/../devices" "${CMAKE_CURRENT_SOURCE_DIR}/../verif" "${CMAKE_CURRENT_SOURCE_DIR}/../spec" "${CMAKE_SOURCE_DIR}/examples/axi" ) cpphdl_add_host_opt_flags(${target_name}) target_compile_options(${target_name} PRIVATE "-fno-strict-aliasing" ) set(tribe_main_smoke_tests Accelerator_test CLINT_test CPU_test Checkpoint_test Cpp_test EthGigDMA_test EthGigCPU_test EthGigMac_test IOUART_test MMU_TLB_Test NS16550A_test ) set(test_ram_bytes ${TRIBE_RAM_BYTES_CONFIG}) set(test_io_region_size ${TRIBE_IO_REGION_SIZE_CONFIG}) set(test_l2_axi_width "") if(target_name STREQUAL "Linux_test") set(test_ram_bytes 33554432) set(test_io_region_size 1048576) set(test_l2_axi_width 64) set(test_args --noveril --prepare-only) else() if(target_name STREQUAL "Checkpoint_test") # Checkpoint_test drives UART RX through the PLIC. The UART sits at # the IO base, but the PLIC claim/complete register is over 2 MiB # into IO space, so the default 64 KiB test aperture is too small. set(test_io_region_size 4194304) endif() if(target_name IN_LIST tribe_main_smoke_tests) # Standalone Tribe ELF smoke tests use the 64-bit cache/memory path. # The generated SystemVerilog and the Verilator harness must agree. set(test_l2_axi_width 64) endif() set(test_args --noveril) endif() if(test_l2_axi_width) target_compile_definitions(${target_name} PRIVATE L2_AXI_WIDTH=${test_l2_axi_width} TRIBE_RAM_BYTES_CONFIG=${test_ram_bytes} ``` -------------------------------- ### Incrementing sys_clock in Simulation Cycle Source: https://github.com/mirekez/cpphdl/blob/main/doc/best_practice.md Illustrates the correct placement of `++sys_clock;` within the simulation cycle, ensuring proper caching and refreshing of combinational values. ```cpp dut._assign(); dut._work(reset); ++sys_clock; dut._strobe(); ``` -------------------------------- ### Find stdc++exp Library Source: https://github.com/mirekez/cpphdl/blob/main/CMakeLists.txt Finds the stdc++exp library, with a fallback to searching within the .conda/lib directory. ```cmake find_library(CPPHDL_STDCXXEXP stdc++exp) if(NOT CPPHDL_STDCXXEXP) file(GLOB CPPHDL_STDCXXEXP_CANDIDATES "${CMAKE_SOURCE_DIR}/.conda/lib/gcc/*/*/libstdc++exp.a") if(CPPHDL_STDCXXEXP_CANDIDATES) list(GET CPPHDL_STDCXXEXP_CANDIDATES 0 CPPHDL_STDCXXEXP) endif() endif() ``` -------------------------------- ### CTest Integration Source: https://github.com/mirekez/cpphdl/blob/main/CMakeLists.txt Includes the CTest module for running tests. ```cmake include(CTest) ```