From 4c7afa447d6d340f591bfdd13764d259c0d13460 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Risto=20Peja=C5=A1inovi=C4=87?= Date: Tue, 9 Sep 2025 23:31:53 +0200 Subject: [PATCH 1/9] First attempt at #10 --- .gitignore | 1 + src/peakrdl_halcpp/halnode.py | 49 +++- src/peakrdl_halcpp/include/fixp_field_node.h | 243 +++++++++++++++++++ src/peakrdl_halcpp/include/halcpp_base.h | 1 + src/peakrdl_halcpp/templates/addrmap.h.j2 | 26 +- tests/cmake/add_hal_test.cmake | 36 +++ tests/cmake/deps.cmake | 18 ++ tests/cmake/fw_utils.cmake | 19 ++ tests/cmake/toolchains/riscv_toolchain.cmake | 119 +++++++++ tests/cross_compile/CMakeLists.txt | 37 +++ tests/cross_compile/main.cpp | 20 ++ tests/simple/CMakeLists.txt | 25 ++ tests/simple/fixp.rdl | 19 ++ tests/simple/simple.rdl | 24 ++ tests/simple/simple_ro.rdl | 9 + tests/simple/test_fixp.cpp | 14 ++ tests/simple/test_simple.cpp | 23 ++ tests/simple/test_simple_ro.cpp | 21 ++ tests/test_utils/CMakeLists.txt | 5 + tests/test_utils/mock_io.h | 34 +++ tests/test_utils/test_utils.h | 32 +++ 21 files changed, 763 insertions(+), 12 deletions(-) create mode 100644 src/peakrdl_halcpp/include/fixp_field_node.h create mode 100644 tests/cmake/add_hal_test.cmake create mode 100644 tests/cmake/deps.cmake create mode 100644 tests/cmake/fw_utils.cmake create mode 100644 tests/cmake/toolchains/riscv_toolchain.cmake create mode 100644 tests/cross_compile/CMakeLists.txt create mode 100644 tests/cross_compile/main.cpp create mode 100644 tests/simple/CMakeLists.txt create mode 100644 tests/simple/fixp.rdl create mode 100644 tests/simple/simple.rdl create mode 100644 tests/simple/simple_ro.rdl create mode 100644 tests/simple/test_fixp.cpp create mode 100644 tests/simple/test_simple.cpp create mode 100644 tests/simple/test_simple_ro.cpp create mode 100644 tests/test_utils/CMakeLists.txt create mode 100644 tests/test_utils/mock_io.h create mode 100644 tests/test_utils/test_utils.h diff --git a/.gitignore b/.gitignore index 28749b2..b07cf70 100644 --- a/.gitignore +++ b/.gitignore @@ -162,3 +162,4 @@ cython_debug/ # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ +tests/cmake/_deps diff --git a/src/peakrdl_halcpp/halnode.py b/src/peakrdl_halcpp/halnode.py index 91f41eb..e32ff4d 100644 --- a/src/peakrdl_halcpp/halnode.py +++ b/src/peakrdl_halcpp/halnode.py @@ -90,7 +90,10 @@ def get_docstring(self) -> str: def _halfactory(inst: Node, env: 'RDLEnvironment', parent: Optional['Node'] = None) -> Optional['Node']: """HAL node factory method adapted from systemrdl Node class.""" if isinstance(inst, FieldNode): - return HalFieldNode(inst) + if inst.get_property("intwidth", default=None) or inst.get_property("fracwidth", default=None): + return HalFixpFieldNode(inst) + else: + return HalFieldNode(inst) elif isinstance(inst, RegNode): return HalRegNode(inst) elif isinstance(inst, RegfileNode): @@ -243,6 +246,50 @@ def get_enums(self): return False, None, None, None, None, None +class HalFixpFieldNode(HalFieldNode, FieldNode): + """HalFixpFieldNode class inheriting from HalBaseNode class and systemrdl FieldNode class. + + Class methods: + + - :func:`get_enums` + """ + + @property + def fracwidth(self) -> int: + intwidth: int|None = self.get_property("intwidth", default=None) + fracwidth: int|None = self.get_property("fracwidth", default=None) + + if fracwidth: + return int(fracwidth) + elif intwidth: + return self.width - intwidth + + raise ValueError("One of intwidth or fracwidth properties need to be defined fro FixpFieldNode") + + @property + def intwidth(self) -> int: + intwidth: int|None = self.get_property("intwidth", default=None) + fracwidth: int|None = self.get_property("fracwidth", default=None) + + if intwidth: + return int(intwidth) + elif fracwidth: + return self.width - fracwidth + + raise ValueError("One of intwidth or fracwidth properties need to be defined fro FixpFieldNode") + + @property + def cpp_access_type(self) -> str: + """C++ access right template selection.""" + if self.is_sw_readable and self.is_sw_writable: + return "FixpFieldRW" + elif self.is_sw_writable and not self.is_sw_readable: + return "FixpFieldWO" + elif self.is_sw_readable: + return "FixpFieldRO" + else: + raise ValueError(f'Node field access rights are not found \ + {self.inst.inst_name}') class HalRegNode(HalBaseNode, RegNode): """HalRegNode class inheriting from HalBaseNode class and systemrdl RegNode class. diff --git a/src/peakrdl_halcpp/include/fixp_field_node.h b/src/peakrdl_halcpp/include/fixp_field_node.h new file mode 100644 index 0000000..eaaf3dd --- /dev/null +++ b/src/peakrdl_halcpp/include/fixp_field_node.h @@ -0,0 +1,243 @@ +#ifndef _FIXP_FIELD_NODE_H_ +#define _FIXP_FIELD_NODE_H_ + +#include +#include +#include +#include "halcpp_utils.h" + +namespace halcpp +{ + template + class FixpFieldBase + { + public: + /** + * @brief Return the absolute address of the node. + * + */ + static constexpr uint32_t get_abs_addr() { return PARENT_TYPE().get_abs_addr(); } + + /** + * @brief Return the width of the field noe. + * + */ + static constexpr uint32_t get_width() { return width; } + static constexpr uint32_t get_int_width() { return width_int; } + static constexpr uint32_t get_frac_width() { return width_frac; } + + /** + * @brief Calculate the field mask. + * + * Example: START_BIT = 3, width = 4 => field_mask = 0000 0000 0111 1000 + */ + static constexpr uint32_t field_mask() + { + static_assert(width <= 32, "Register cannot be bigger than 32 bits"); + return (~0u) ^ (bit_mask() << START_INT_BIT); + } + + /** + * @brief Calculate the bit mask. + * + * Example: width = 4 => bit_mask = 0000 0000 0000 1111 + */ + static constexpr uint32_t bit_mask() + { + return (((1u << (width)) - 1)); + } + + + + protected: + using parent_type = PARENT_TYPE; + + static constexpr uint32_t start_bit = START_INT_BIT; + static constexpr uint32_t start_int_bit = START_INT_BIT; + static constexpr uint32_t end_int_bit = END_INT_BIT; + static constexpr uint32_t start_frac_bit = START_FRAC_BIT; + static constexpr uint32_t end_frac_bit = END_FRAC_BIT; + static constexpr uint32_t end_bit = END_FRAC_BIT; + + static constexpr uint32_t width = end_bit - start_bit + 1; + static constexpr uint32_t width_int = end_int_bit - start_int_bit + 1; + static constexpr uint32_t width_frac = end_frac_bit - start_frac_bit + 1; + + + using dataType = float; + }; + + template + class FixpFieldWrMixin : public BASE_TYPE + { + public: + using parent = typename BASE_TYPE::parent_type; + + /** + * @brief Check if the field has a set operation (write capability). + */ + static constexpr bool has_set() { return true; }; + + /** + * @brief Set the value of the field. + * + * @param val The value to set. + */ + static inline void set(typename BASE_TYPE::dataType val) + { + uint32_t fixp_val = float_to_fixp(val); + if constexpr (node_has_get_v) + parent::set((parent::get() & BASE_TYPE::field_mask()) | ((fixp_val & BASE_TYPE::bit_mask()) << BASE_TYPE::start_bit)); + else + parent::set(fixp_val << BASE_TYPE::start_bit); + } + + static inline uint32_t float_to_fixp(typename BASE_TYPE::dataType val){ + return val * (1< + static inline void set(const Const &a) + { + static_assert(CONST_WIDTH == BASE_TYPE::width, "Constant is not the same width as field"); + set((typename BASE_TYPE::dataType)a.val); + } + }; + + template + class FixpFieldRdMixin : public BASE_TYPE + { + private: + using parent = typename BASE_TYPE::parent_type; + + public: + /** + * @brief Check if the field has a get operation (read capability). + */ + static constexpr bool has_get() { return true; }; + + /** + * @brief Return the value of the field. + */ + static typename BASE_TYPE::dataType get() + { + // Read the full register, mask, and shift it to get the field value + uint32_t fixp_val = (parent::get() & ~BASE_TYPE::field_mask()) >> BASE_TYPE::start_bit; + + return fixp_to_float(fixp_val); + } + + static inline float fixp_to_float(uint32_t val){ + return (float)val / (1< + inline operator const T() + { + return static_cast(get()); + } + }; + + + template // TODO merge this with RegNode? + class FixpFieldNode : public FieldMixins... + { + private: + template + void call_set([[maybe_unused]] const DT &val) const + { + if constexpr (node_has_set_v) + T::set(val); + } + + template + void call_set(const DT val) const + { + call_set(val); + call_set(val); + } + + public: + template + typename std::enable_if...>, T>::type + operator=(Tp val) + { + call_set(val); + } + + // template + // static constexpr auto at() + // { + // // Get the type of the first mixin from the tuple of FieldMixins types + // using first_mixin = typename std::tuple_element<0, std::tuple>::type; + // // Define the parent type using the parent type of the first mixin + // using parent_type = typename first_mixin::parent; + // + // // Check if the given index is within the bounds of the width of the first mixin + // static_assert(IDX < static_cast(first_mixin::width)); + // static_assert(IDX >= -1); + // // Calculate the index to be used for accessing the mixin based on the given index + // constexpr uint32_t idx = IDX == -1 ? first_mixin::width - 1 : IDX; + // + // // Calculate the number of mixins in the tuple + // constexpr std::size_t num_of_mixins = sizeof...(FieldMixins); + // + // // Define the base type using the calculated index and parent type + // using base_type = FieldBase; + // + // // Check if there's only one mixin + // if constexpr (num_of_mixins == 1) + // { + // // If the mixin has a 'get' function, return a FieldNode with a read mixin + // if constexpr (node_has_get_v) + // return FieldNode>(); + // // If the mixin has a 'set' function, return a FieldNode with a write mixin + // if constexpr (node_has_set_v) + // return FieldNode>(); + // } + // // If there are two mixins + // else if constexpr (num_of_mixins == 2) + // { + // // Return a FieldNode with both write and read mixins + // return FieldNode, FieldRdMixin>(); + // } + // } + }; + + template + using FixpFieldRO = FixpFieldNode>>; + + /** + * @brief Alias for FieldNode representing a write-only field. + */ + template + using FixpFieldWO = FixpFieldNode>>; + + /** + * @brief Alias for FieldNode representing a read-write field. + */ + template + using FixpFieldRW = FixpFieldNode>, + FixpFieldRdMixin>>; +} + + +#endif + diff --git a/src/peakrdl_halcpp/include/halcpp_base.h b/src/peakrdl_halcpp/include/halcpp_base.h index afd8a5e..94141b3 100644 --- a/src/peakrdl_halcpp/include/halcpp_base.h +++ b/src/peakrdl_halcpp/include/halcpp_base.h @@ -5,6 +5,7 @@ #include #include "halcpp_utils.h" #include "field_node.h" +#include "fixp_field_node.h" #include "reg_node.h" #include "regfile_node.h" #include "array_nodes.h" diff --git a/src/peakrdl_halcpp/templates/addrmap.h.j2 b/src/peakrdl_halcpp/templates/addrmap.h.j2 index 01646ad..a1e3ef8 100644 --- a/src/peakrdl_halcpp/templates/addrmap.h.j2 +++ b/src/peakrdl_halcpp/templates/addrmap.h.j2 @@ -37,7 +37,7 @@ namespace {{ halnode.orig_type_name }}_nm { public: {% for s, v, d in enum_strings|zip(enum_values, enum_desc) %} - static const halcpp::Const<{{ const_width }}, {{ v }}> {{ s }}; // {{ d }} + static inline const halcpp::Const<{{ const_width }}, {{ v }}> {{ s }}; // {{ d }} {% endfor %} }; {% endif %} @@ -53,7 +53,11 @@ namespace {{ halnode.orig_type_name }}_nm {# Add the fields to the register #} {% for f in r.halchildren(children_type=HalFieldNode, skip_buses=skip_buses) %} - static halcpp::{{ f.cpp_access_type }}<{{ f.low }}, {{ f.high }}, TYPE> {{ f.inst_name }}; + {% if f.__class__.__name__ == "HalFixpFieldNode" %} + static inline halcpp::{{ f.cpp_access_type }}<{{ f.low }}, {{ f.low + f.intwidth-1 }}, {{ f.low + f.intwidth }}, {{ f.high }}, TYPE> {{ f.inst_name }}; + {% else %} + static inline halcpp::{{ f.cpp_access_type }}<{{ f.low }}, {{ f.high }}, TYPE> {{ f.inst_name }}; + {% endif %} {% endfor %} {# Inherit the overloaded '=' operator from the base class if register can be write from software #} {% if r.has_sw_writable %} @@ -77,7 +81,7 @@ namespace {{ halnode.orig_type_name }}_nm { public: {% for s, v, d in enum_strings|zip(enum_values, enum_desc) %} - static const halcpp::Const<{{ const_width }}, {{ v }}> {{ s }}; // {{ d }} + static inline const halcpp::Const<{{ const_width }}, {{ v }}> {{ s }}; // {{ d }} {% endfor %} }; {% endif %} @@ -93,7 +97,7 @@ namespace {{ halnode.orig_type_name }}_nm {# Add the fields to the register #} {% for f in r.halchildren(children_type=HalFieldNode, skip_buses=skip_buses) %} - static halcpp::{{ f.cpp_access_type }}<{{ f.low }}, {{ f.high }}, TYPE> {{ f.inst_name }}; + static inline halcpp::{{ f.cpp_access_type }}<{{ f.low }}, {{ f.high }}, TYPE> {{ f.inst_name }}; {% endfor %} {# Inherit the overloaded '=' operator from the base class if register can be write from software #} {% if r.has_sw_writable %} @@ -114,7 +118,7 @@ namespace {{ halnode.orig_type_name }}_nm using TYPE = {{ rf.orig_type_name|upper }}{{ rf.get_cls_tmpl_params() }}; {% for c in rf.halchildren((HalRegNode, HalRegfileNode), skip_buses=skip_buses) %} - static {{ c.orig_type_name|upper }}<0x{{ "%0x"|format(c.address_offset|int) }}, {{ c.width }}, TYPE> {{ c.inst_name }}; + static inline {{ c.orig_type_name|upper }}<0x{{ "%0x"|format(c.address_offset|int) }}, {{ c.width }}, TYPE> {{ c.inst_name }}; {% endfor %} }; {% endfor %} @@ -140,17 +144,17 @@ public: {% for c in halnode.halchildren(skip_buses=skip_buses) %} {% if c.__class__.__name__ == "HalRegNode" and c.is_array %} - static halcpp::RegArrayNode<{{ halnode.orig_type_name }}_nm::{{ c.orig_type_name|upper }}, 0x{{ "%0x"|format(c.address_offset|int) }}, {{ c.width }}, {{ c.array_stride }}, TYPE , {{ c.array_dimensions|join(', ') }}> {{ c.inst_name }}; + static inline halcpp::RegArrayNode<{{ halnode.orig_type_name }}_nm::{{ c.orig_type_name|upper }}, 0x{{ "%0x"|format(c.address_offset|int) }}, {{ c.width }}, {{ c.array_stride }}, TYPE , {{ c.array_dimensions|join(', ') }}> {{ c.inst_name }}; {% elif c.__class__.__name__ == "HalRegNode" %} - static {{ halnode.orig_type_name }}_nm::{{ c.orig_type_name|upper }}<0x{{ "%0x"|format(c.address_offset|int) }}, {{ c.width }}, TYPE> {{ c.inst_name }}; + static inline {{ halnode.orig_type_name }}_nm::{{ c.orig_type_name|upper }}<0x{{ "%0x"|format(c.address_offset|int) }}, {{ c.width }}, TYPE> {{ c.inst_name }}; {% elif c.__class__.__name__ == "HalRegfileNode" and c.is_array %} - static halcpp::RegfileArrayNode<{{ halnode.orig_type_name }}_nm::{{ c.orig_type_name|upper }}, 0x{{ "%0x"|format(c.address_offset|int) }}, {{ c.array_stride }}, TYPE , {{ c.array_dimensions|join(', ') }}> {{ c.inst_name }}; + static inline halcpp::RegfileArrayNode<{{ halnode.orig_type_name }}_nm::{{ c.orig_type_name|upper }}, 0x{{ "%0x"|format(c.address_offset|int) }}, {{ c.array_stride }}, TYPE , {{ c.array_dimensions|join(', ') }}> {{ c.inst_name }}; {% elif c.__class__.__name__ == "HalRegfileNode" %} - static {{ halnode.orig_type_name }}_nm::{{ c.orig_type_name|upper }}<0x{{ "%0x"|format(c.address_offset|int) }}, TYPE> {{ c.inst_name }}; + static inline {{ halnode.orig_type_name }}_nm::{{ c.orig_type_name|upper }}<0x{{ "%0x"|format(c.address_offset|int) }}, TYPE> {{ c.inst_name }}; {% elif c.__class__.__name__ == "HalMemNode" %} - static {{ halnode.orig_type_name }}_nm::{{ c.parent.orig_type_name|upper }}<0x{{ "%0x"|format(c.address_offset|int) }}, {{ c.size }}, TYPE> {{ c.inst_name }}; + static inline {{ halnode.orig_type_name }}_nm::{{ c.parent.orig_type_name|upper }}<0x{{ "%0x"|format(c.address_offset|int) }}, {{ c.size }}, TYPE> {{ c.inst_name }}; {% else %} - static {{ halutils.get_extern(c)|upper }}<0x{{ "%0x"|format(c.address_offset|int) }}, TYPE> {{ c.inst_name }}; + static inline {{ halutils.get_extern(c)|upper }}<0x{{ "%0x"|format(c.address_offset|int) }}, TYPE> {{ c.inst_name }}; {% endif %} {% endfor %} }; diff --git a/tests/cmake/add_hal_test.cmake b/tests/cmake/add_hal_test.cmake new file mode 100644 index 0000000..3f4f8c4 --- /dev/null +++ b/tests/cmake/add_hal_test.cmake @@ -0,0 +1,36 @@ +include_guard(GLOBAL) + +macro(add_hal_test TEST_NAME) + cmake_parse_arguments(ARG "" "" "SYSTEMRDL;CPP" ${ARGN}) + if(ARG_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION} passed unrecognized argument " "${ARG_UNPARSED_ARGUMENTS}") + endif() + + add_ip(${TEST_NAME}_ip) + + ip_sources(${IP} SYSTEMRDL + ${ARG_SYSTEMRDL} + ) + + peakrdl_halcpp(${IP}) + + add_executable(${TEST_NAME} + ${ARG_CPP} + ) + + target_include_directories(${TEST_NAME} PUBLIC + ${CMAKE_CURRENT_LIST_DIR} + ) + + target_link_libraries(${TEST_NAME} + ${IP} + GTest::gtest_main + test_utils + ) + + enable_testing() + include(GoogleTest) + gtest_discover_tests(${TEST_NAME}) + + +endmacro() diff --git a/tests/cmake/deps.cmake b/tests/cmake/deps.cmake new file mode 100644 index 0000000..b058604 --- /dev/null +++ b/tests/cmake/deps.cmake @@ -0,0 +1,18 @@ +set(FETCHCONTENT_BASE_DIR "${CMAKE_CURRENT_LIST_DIR}/_deps") +include(FetchContent) +FetchContent_Declare(SoCMake + GIT_REPOSITORY "https://github.com/HEP-SoC/SoCMake.git" + GIT_TAG 40feb77) +FetchContent_MakeAvailable(SoCMake) + +if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") + FetchContent_Declare(googletest + URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip + ) + FetchContent_MakeAvailable(googletest) + add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/../test_utils/" "test_utils") + include("${CMAKE_CURRENT_LIST_DIR}/add_hal_test.cmake") +endif() + +include("${CMAKE_CURRENT_LIST_DIR}/fw_utils.cmake") + diff --git a/tests/cmake/fw_utils.cmake b/tests/cmake/fw_utils.cmake new file mode 100644 index 0000000..07facf3 --- /dev/null +++ b/tests/cmake/fw_utils.cmake @@ -0,0 +1,19 @@ + +function(firmware_size EXE) + message("EXECUTING ${CMAKE_SIZE} $ --format=sysv") + add_custom_command(TARGET ${EXE} POST_BUILD + COMMAND ${CMAKE_SIZE} $ --format=sysv + COMMENT "Printing size" + ) +endfunction() + + +function(firmware_disasemble EXE) + get_target_property(BINARY_DIR ${EXE} BINARY_DIR) + set(ASM_FILE "${BINARY_DIR}/${EXE}.asm") + add_custom_command(TARGET ${EXE} POST_BUILD + BYPRODUCTS ${ASM_FILE} + COMMAND ${CMAKE_OBJDUMP} -DgrwCS $ > ${ASM_FILE} + COMMENT "GEnerating disasembly" + ) +endfunction() diff --git a/tests/cmake/toolchains/riscv_toolchain.cmake b/tests/cmake/toolchains/riscv_toolchain.cmake new file mode 100644 index 0000000..88a2ecd --- /dev/null +++ b/tests/cmake/toolchains/riscv_toolchain.cmake @@ -0,0 +1,119 @@ +set(FETCHCONTENT_BASE_DIR ${CMAKE_CURRENT_LIST_DIR}/../_deps) +include(FetchContent) +FetchContent_Declare(toolchain + URL "https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack/releases/download/v13.2.0-2/xpack-riscv-none-elf-gcc-13.2.0-2-linux-x64.tar.gz" +) +FetchContent_MakeAvailable(toolchain) +set(RISCV_GNU_PATH ${toolchain_SOURCE_DIR}) + +set(CMAKE_SYSTEM_PROCESSOR riscv) +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_SYSTEM_ABI elf) + +set(TOOLCHAIN_PREFIX "${CMAKE_SYSTEM_PROCESSOR}-none-${CMAKE_SYSTEM_ABI}") + +find_program(RISCV_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc HINTS ${RISCV_GNU_PATH}/bin) +find_program(RISCV_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++ HINTS ${RISCV_GNU_PATH}/bin) +find_program(RISCV_AR ${TOOLCHAIN_PREFIX}-ar HINTS ${RISCV_GNU_PATH}/bin) +find_program(RISCV_ASM ${TOOLCHAIN_PREFIX}-as HINTS ${RISCV_GNU_PATH}/bin) +find_program(RISCV_LINKER ${TOOLCHAIN_PREFIX}-ld HINTS ${RISCV_GNU_PATH}/bin) +find_program(RISCV_OBJCOPY ${TOOLCHAIN_PREFIX}-objcopy HINTS ${RISCV_GNU_PATH}/bin) +find_program(RISCV_OBJDUMP ${TOOLCHAIN_PREFIX}-objdump HINTS ${RISCV_GNU_PATH}/bin) +find_program(RISCV_RANLIB ${TOOLCHAIN_PREFIX}-ranlib HINTS ${RISCV_GNU_PATH}/bin) +find_program(RISCV_SIZE ${TOOLCHAIN_PREFIX}-size HINTS ${RISCV_GNU_PATH}/bin) +find_program(RISCV_STRIP ${TOOLCHAIN_PREFIX}-strip HINTS ${RISCV_GNU_PATH}/bin) + +set(CMAKE_C_COMPILER ${RISCV_C_COMPILER}) +set(CMAKE_CXX_COMPILER ${RISCV_CXX_COMPILER}) +set(CMAKE_ASM ${RISCV_ASM}) +set(CMAKE_AR ${RISCV_AR}) +set(CMAKE_LINKER ${RISCV_LINKER}) +set(CMAKE_OBJCOPY ${RISCV_OBJCOPY}) +set(CMAKE_OBJDUMP ${RISCV_OBJDUMP}) +set(CMAKE_RANLIB ${RISCV_RANLIB}) +set(CMAKE_SIZE ${RISCV_SIZE}) +set(CMAKE_STRIP ${RISCV_STRIP}) + + +get_filename_component(RISCV_TOOLCHAIN_PATH ${RISCV_CXX_COMPILER} DIRECTORY CACHE) +set(RISCV_TOOLCHAIN_PREFIX "${TOOLCHAIN_PREFIX}-" CACHE STRING "") + +set(CMAKE_C_FLAGS "") +set(CMAKE_CXX_FLAGS "") +set(CMAKE_EXE_LINKER_FLAGS "") + +############################# +# Machine-Dependent Options # +############################# +# RV32 +# i : Integer +# m : Integer Multiplication and Division +# a : Atomic instructions +# c : Compressed instructions +# zicsr : CSR Instructions (explicitely required with latest specs) +string(APPEND CMAKE_C_FLAGS " -march=rv32imc") +# int and pointers are 32bit, long 64bit, char 8bit, short 16bit +string(APPEND CMAKE_C_FLAGS " -mabi=ilp32") + +string(APPEND CMAKE_ASM_FLAGS " -mabi=ilp32") +string(APPEND CMAKE_ASM_FLAGS " -march=rv32imc") + +################################ +# Options for Directory Search # +################################ +# Add the directory dir to the list of directories to be searched for header files during preprocessing +# string(APPEND CMAKE_C_FLAGS " -I${RISCV_GNU_PATH}/${TOOLCHAIN_PREFIX}/include/") + +##################################### +# Options that Control Optimization # +##################################### +# Place each function or data item into its own section in the output file +# if the target supports arbitrary sections. The name of the function or +# the name of the data item determines the section name in the output file. +# string(APPEND CMAKE_C_FLAGS " -ffunction-sections") +# string(APPEND CMAKE_C_FLAGS " -fdata-sections") + +# Optimize for size by default +string(APPEND CMAKE_C_FLAGS " -ffreestanding -nostdlib") + +# Pass common flags for c++ compilation flow +set(CMAKE_CXX_FLAGS ${CMAKE_C_FLAGS}) +set(CMAKE_ASM_FLAGS ${CMAKE_C_FLAGS}) + +####################### +# Options for Linking # +####################### +# Do not use the standard system startup +string(APPEND CMAKE_EXE_LINKER_FLAGS " -ffreestanding -nostdlib") +# Prevents linking with the shared libraries +string(APPEND CMAKE_EXE_LINKER_FLAGS " -static") + +# Print memory usage +string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,--print-memory-usage") +# Generate executable map file +string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-Map=map_file.map") + +########################################## +# Options Controlling the Kind of Output # +########################################## +# Use embedded class libnano_c +string(APPEND CMAKE_EXE_LINKER_FLAGS " -specs=nano.specs") + +################################ +# Options for Directory Search # +################################ +# Add directory dir to the list of directories to be searched for -l +# string(APPEND CMAKE_EXE_LINKER_FLAGS " -L${RISCV_GNU_PATH}/${TOOLCHAIN_PREFIX}/lib") + +# Search the library named library when linking. +# string(APPEND CMAKE_EXE_LINKER_FLAGS " -lc -lgcc -lm") + + +# set(CMAKE_C_FLAGS_DEBUG ) # TODO +# set(CMAKE_C_FLAGS_RELEASE ) +# set(CMAKE_CXX_FLAGS_DEBUG ${CXX_FLAGS}) +# set(CMAKE_CXX_FLAGS_RELEASE ${CXX_FLAGS}) + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/tests/cross_compile/CMakeLists.txt b/tests/cross_compile/CMakeLists.txt new file mode 100644 index 0000000..9b070b1 --- /dev/null +++ b/tests/cross_compile/CMakeLists.txt @@ -0,0 +1,37 @@ +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(CMAKE_CXX_STANDARD 17) +cmake_minimum_required(VERSION 3.25) +project(test_cross_compile CXX) + +message("PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}") +include("../cmake/deps.cmake") + + +add_ip(${PROJECT_NAME}_ip) + +ip_sources(${IP} SYSTEMRDL + ../simple/simple.rdl +) + +peakrdl_halcpp(${IP}) + +add_executable(${PROJECT_NAME} + ./main.cpp +) + +target_include_directories(${PROJECT_NAME} PUBLIC + ${CMAKE_CURRENT_LIST_DIR} +) + +target_link_libraries(${PROJECT_NAME} + ${IP} + ) + +target_compile_options(${PROJECT_NAME} PUBLIC + -Os + -fno-delete-null-pointer-checks +) + +firmware_size(${PROJECT_NAME}) +firmware_disasemble(${PROJECT_NAME}) + diff --git a/tests/cross_compile/main.cpp b/tests/cross_compile/main.cpp new file mode 100644 index 0000000..f40442a --- /dev/null +++ b/tests/cross_compile/main.cpp @@ -0,0 +1,20 @@ +#include "simple_hal.h" + +int main () { + + volatile SIMPLE_HAL<0> soc; + + float num = 4.8; + soc.r1.f1 = num; + + float read_num = soc.r1.f1; + // soc.r1 = 2; + // soc.r1.f2 = 10; + // + // for(volatile int i =0; i< 10; i++){ + // soc.r1 = i; + // } + + + return 0; +} diff --git a/tests/simple/CMakeLists.txt b/tests/simple/CMakeLists.txt new file mode 100644 index 0000000..4867dc4 --- /dev/null +++ b/tests/simple/CMakeLists.txt @@ -0,0 +1,25 @@ +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(CMAKE_CXX_STANDARD 17) +cmake_minimum_required(VERSION 3.25) +project(test_simple CXX) + +message("PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}") + +include("../cmake/deps.cmake") + +# add_hal_test(test_simple +# SYSTEMRDL ./simple.rdl +# CPP ./test_simple.cpp +# ) + +# add_hal_test(test_simple_ro +# SYSTEMRDL ./simple_ro.rdl +# CPP ./test_simple_ro.cpp +# ) + +add_hal_test(test_fixp + SYSTEMRDL ./fixp.rdl + CPP ./test_fixp.cpp + ) + + diff --git a/tests/simple/fixp.rdl b/tests/simple/fixp.rdl new file mode 100644 index 0000000..5278be0 --- /dev/null +++ b/tests/simple/fixp.rdl @@ -0,0 +1,19 @@ +property intwidth { + type = longint unsigned; + component = field; +}; + +property fracwidth { + type = longint unsigned; + component = field; +}; + +addrmap fixp_simple { + reg { + field { + intwidth=16; + fracwidth=16; + sw=rw; + } f1[31:0] = 0; + }r1 ; +}; diff --git a/tests/simple/simple.rdl b/tests/simple/simple.rdl new file mode 100644 index 0000000..9f7ee4f --- /dev/null +++ b/tests/simple/simple.rdl @@ -0,0 +1,24 @@ +addrmap simple { + reg { + field { + } f1[1:0] = 0; + + field { + desc = "These bits select the transfer mode"; + } f2[3:2] = 0; + + field { + hw=rw; we; + } f3[4:4] = 0; + + field { + } f4[31:5] = 0; + + } r1; + + reg { + + field {} f5[15:0] = 0; + field {} f6[31:16] = 0; + } r2; +}; diff --git a/tests/simple/simple_ro.rdl b/tests/simple/simple_ro.rdl new file mode 100644 index 0000000..b0b1283 --- /dev/null +++ b/tests/simple/simple_ro.rdl @@ -0,0 +1,9 @@ +addrmap simple_ro { + reg { + field { + sw=r; + hw=rw; + } f1[1:0] = 0; + } r1; +}; + diff --git a/tests/simple/test_fixp.cpp b/tests/simple/test_fixp.cpp new file mode 100644 index 0000000..a1cfaff --- /dev/null +++ b/tests/simple/test_fixp.cpp @@ -0,0 +1,14 @@ +#include +#include +#include "mock_io.h" +#include "fixp_simple_hal.h" +#include +#include "test_utils.h" + +inline constexpr FIXP_SIMPLE_HAL<0> hal{}; + +TEST(FixpTest, Basic) { + hal.r1.f1 = 15.6123; + + std::cout << "Float value: " << (float)hal.r1.f1 << "\n"; +} diff --git a/tests/simple/test_simple.cpp b/tests/simple/test_simple.cpp new file mode 100644 index 0000000..8b39042 --- /dev/null +++ b/tests/simple/test_simple.cpp @@ -0,0 +1,23 @@ +#include +#include +#include "mock_io.h" +#include "simple_hal.h" +#include +#include "test_utils.h" + +inline constexpr SIMPLE_HAL<0> hal{}; + +using HalFields = ::testing::Types< + FieldReference, + FieldReference, + FieldReference, + FieldReference +>; + +TYPED_TEST_SUITE(HalFieldTest, HalFields); + +TYPED_TEST(HalFieldTest, TestRWFields) { + auto& field = TestFixture::field(); + test_rw_field_range(field); + test_rw_field_overflow(field); +} diff --git a/tests/simple/test_simple_ro.cpp b/tests/simple/test_simple_ro.cpp new file mode 100644 index 0000000..ca2271c --- /dev/null +++ b/tests/simple/test_simple_ro.cpp @@ -0,0 +1,21 @@ +#include +#include +#include "mock_io.h" +#include "simple_ro_hal.h" +#include +#include "test_utils.h" + +inline constexpr SIMPLE_RO_HAL<0> hal{}; + +using HalFields = ::testing::Types< + FieldReference +>; + +TYPED_TEST_SUITE(HalFieldTest, HalFields); + +TYPED_TEST(HalFieldTest, TestRWFields) { + auto& field = TestFixture::field(); + test_rw_field_range(field); + test_rw_field_overflow(field); +} + diff --git a/tests/test_utils/CMakeLists.txt b/tests/test_utils/CMakeLists.txt new file mode 100644 index 0000000..86c8ee8 --- /dev/null +++ b/tests/test_utils/CMakeLists.txt @@ -0,0 +1,5 @@ +add_library(test_utils INTERFACE) +target_include_directories(test_utils INTERFACE + ${CMAKE_CURRENT_LIST_DIR} +) + diff --git a/tests/test_utils/mock_io.h b/tests/test_utils/mock_io.h new file mode 100644 index 0000000..59b585c --- /dev/null +++ b/tests/test_utils/mock_io.h @@ -0,0 +1,34 @@ +#ifndef _ARCH_IO_H_ +#define _ARCH_IO_H_ + +#include +#include +#include +#include + + + +class MockMemIO +{ +public: + static inline std::array mem; + + static inline uint32_t read32(uint32_t addr) { + // std::cout << "Read32 at addr 0x" << std::hex << addr << " Read data: 0x" << std::hex << MockMemIO::mem[addr] << std::endl; + return MockMemIO::mem[addr]; + } + + static inline void write32(uint32_t addr, uint32_t val) { + // std::cout << "Write32 at addr 0x" << std::hex << addr << " Write data: 0x" << std::hex << val << std::endl; + MockMemIO::mem[addr] = val; + } +}; + +class ArchIoNode : public MockMemIO +{ +public: +}; + +#endif // !_ARCH_IO_H_ + + diff --git a/tests/test_utils/test_utils.h b/tests/test_utils/test_utils.h new file mode 100644 index 0000000..fe6edce --- /dev/null +++ b/tests/test_utils/test_utils.h @@ -0,0 +1,32 @@ +#include + +template +struct FieldReference { + static auto& get() { return Field; } +}; + +template +class HalFieldTest : public ::testing::Test { +protected: + static auto& field() { return FieldT::get(); } +}; + +template +void test_rw_field(uint32_t val, int32_t exp, FIELD_T &field) { + field = val; + EXPECT_EQ((uint32_t)field, exp); +} + +template +void test_rw_field_range(FIELD_T &field) { + int width = field.get_width(); + for (int i = 0; i < (1 << width); i++) { + test_rw_field(i, i, field); + } +} + +template +void test_rw_field_overflow(FIELD_T &field) { + int width = field.get_width(); + test_rw_field((1 << width), 0, field); +} From 41cae170c2e0df0f1a0c5df377c69eb0cf63c23a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Risto=20Peja=C5=A1inovi=C4=87?= Date: Tue, 9 Sep 2025 23:37:31 +0200 Subject: [PATCH 2/9] Add fixp RISCV cross compile test --- tests/cross_compile/{ => fixp}/CMakeLists.txt | 7 ++-- tests/cross_compile/fixp/main.cpp | 13 +++++++ tests/cross_compile/simple/CMakeLists.txt | 38 +++++++++++++++++++ tests/cross_compile/{ => simple}/main.cpp | 1 + 4 files changed, 56 insertions(+), 3 deletions(-) rename tests/cross_compile/{ => fixp}/CMakeLists.txt (84%) create mode 100644 tests/cross_compile/fixp/main.cpp create mode 100644 tests/cross_compile/simple/CMakeLists.txt rename tests/cross_compile/{ => simple}/main.cpp (99%) diff --git a/tests/cross_compile/CMakeLists.txt b/tests/cross_compile/fixp/CMakeLists.txt similarity index 84% rename from tests/cross_compile/CMakeLists.txt rename to tests/cross_compile/fixp/CMakeLists.txt index 9b070b1..c343d26 100644 --- a/tests/cross_compile/CMakeLists.txt +++ b/tests/cross_compile/fixp/CMakeLists.txt @@ -3,14 +3,13 @@ set(CMAKE_CXX_STANDARD 17) cmake_minimum_required(VERSION 3.25) project(test_cross_compile CXX) -message("PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}") -include("../cmake/deps.cmake") +include("../../cmake/deps.cmake") add_ip(${PROJECT_NAME}_ip) ip_sources(${IP} SYSTEMRDL - ../simple/simple.rdl + ../../simple/fixp.rdl ) peakrdl_halcpp(${IP}) @@ -35,3 +34,5 @@ target_compile_options(${PROJECT_NAME} PUBLIC firmware_size(${PROJECT_NAME}) firmware_disasemble(${PROJECT_NAME}) + + diff --git a/tests/cross_compile/fixp/main.cpp b/tests/cross_compile/fixp/main.cpp new file mode 100644 index 0000000..41f4916 --- /dev/null +++ b/tests/cross_compile/fixp/main.cpp @@ -0,0 +1,13 @@ +#include "fixp_simple_hal.h" + +int main () { + + FIXP_SIMPLE_HAL<0> soc; + + soc.r1.f1 = 4.8; + + float read_num = soc.r1.f1; + + return 0; +} + diff --git a/tests/cross_compile/simple/CMakeLists.txt b/tests/cross_compile/simple/CMakeLists.txt new file mode 100644 index 0000000..c343d26 --- /dev/null +++ b/tests/cross_compile/simple/CMakeLists.txt @@ -0,0 +1,38 @@ +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(CMAKE_CXX_STANDARD 17) +cmake_minimum_required(VERSION 3.25) +project(test_cross_compile CXX) + +include("../../cmake/deps.cmake") + + +add_ip(${PROJECT_NAME}_ip) + +ip_sources(${IP} SYSTEMRDL + ../../simple/fixp.rdl +) + +peakrdl_halcpp(${IP}) + +add_executable(${PROJECT_NAME} + ./main.cpp +) + +target_include_directories(${PROJECT_NAME} PUBLIC + ${CMAKE_CURRENT_LIST_DIR} +) + +target_link_libraries(${PROJECT_NAME} + ${IP} + ) + +target_compile_options(${PROJECT_NAME} PUBLIC + -Os + -fno-delete-null-pointer-checks +) + +firmware_size(${PROJECT_NAME}) +firmware_disasemble(${PROJECT_NAME}) + + + diff --git a/tests/cross_compile/main.cpp b/tests/cross_compile/simple/main.cpp similarity index 99% rename from tests/cross_compile/main.cpp rename to tests/cross_compile/simple/main.cpp index f40442a..3d4f8f9 100644 --- a/tests/cross_compile/main.cpp +++ b/tests/cross_compile/simple/main.cpp @@ -18,3 +18,4 @@ int main () { return 0; } + From bfc304bf7276ad3a6cb6fa64e6bde56d7d2152c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Risto=20Peja=C5=A1inovi=C4=87?= Date: Tue, 9 Sep 2025 23:43:22 +0200 Subject: [PATCH 3/9] Uncomment tests --- tests/simple/CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/simple/CMakeLists.txt b/tests/simple/CMakeLists.txt index 4867dc4..be84924 100644 --- a/tests/simple/CMakeLists.txt +++ b/tests/simple/CMakeLists.txt @@ -7,10 +7,10 @@ message("PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}") include("../cmake/deps.cmake") -# add_hal_test(test_simple -# SYSTEMRDL ./simple.rdl -# CPP ./test_simple.cpp -# ) +add_hal_test(test_simple + SYSTEMRDL ./simple.rdl + CPP ./test_simple.cpp + ) # add_hal_test(test_simple_ro # SYSTEMRDL ./simple_ro.rdl From f25d43264f57613449f98adeab74b23ae8f63e06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Risto=20Peja=C5=A1inovi=C4=87?= Date: Sun, 14 Sep 2025 00:52:38 +0200 Subject: [PATCH 4/9] Fix the FieldNode at() accessor not accessing the correct bits --- src/peakrdl_halcpp/include/field_node.h | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/peakrdl_halcpp/include/field_node.h b/src/peakrdl_halcpp/include/field_node.h index 8e36e44..3d0f46d 100644 --- a/src/peakrdl_halcpp/include/field_node.h +++ b/src/peakrdl_halcpp/include/field_node.h @@ -35,6 +35,18 @@ namespace halcpp */ static constexpr uint32_t get_width() { return width; } + /** + * + * @brief Return the index of first bit of the field in the parent register + */ + static constexpr uint32_t get_start_bit() { return start_bit; } + + /** + * + * @brief Return the index of last bit of the field in the parent register + */ + static constexpr uint32_t get_end_bit() { return end_bit; } + /** * @brief Calculate the field mask. * @@ -125,10 +137,8 @@ namespace halcpp template class FieldRdMixin : public BASE_TYPE { - private: - using parent = typename BASE_TYPE::parent_type; - public: + using parent = typename BASE_TYPE::parent_type; /** * @brief Check if the field has a get operation (read capability). */ @@ -249,12 +259,13 @@ namespace halcpp static_assert(IDX >= -1); // Calculate the index to be used for accessing the mixin based on the given index constexpr uint32_t idx = IDX == -1 ? first_mixin::width - 1 : IDX; + constexpr uint32_t start_bit = first_mixin::start_bit; // Calculate the number of mixins in the tuple constexpr std::size_t num_of_mixins = sizeof...(FieldMixins); // Define the base type using the calculated index and parent type - using base_type = FieldBase; + using base_type = FieldBase; // Check if there's only one mixin if constexpr (num_of_mixins == 1) From c261f184b5dfe0957c87324df697889a09280a37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Risto=20Peja=C5=A1inovi=C4=87?= Date: Sun, 14 Sep 2025 00:55:08 +0200 Subject: [PATCH 5/9] Fix wrong include files --- src/peakrdl_halcpp/exporter.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/peakrdl_halcpp/exporter.py b/src/peakrdl_halcpp/exporter.py index 1564135..9c84978 100644 --- a/src/peakrdl_halcpp/exporter.py +++ b/src/peakrdl_halcpp/exporter.py @@ -119,12 +119,12 @@ def export(self, text = self.process_template(context) # Addrmaps for memories use the original type name - if halnode.is_mem_addrmap: - out_file = os.path.join( - outdir, halnode.orig_type_name_hal.lower() + ".h") - else: - out_file = os.path.join( - outdir, halnode.inst_name_hal.lower() + ".h") + # if halnode.is_mem_addrmap: + out_file = os.path.join( + outdir, halnode.orig_type_name_hal.lower() + ".h") + # else: + # out_file = os.path.join( + # outdir, halnode.inst_name_hal.lower() + ".h") # Generate the files if --list-files parameter is not set if list_files: From 9a73d1ce41299509d3a25c8ac49679774d615712 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Risto=20Peja=C5=A1inovi=C4=87?= Date: Sun, 14 Sep 2025 00:56:18 +0200 Subject: [PATCH 6/9] Initial testing infrastructure --- src/peakrdl_halcpp/__peakrdl__.py | 21 +- src/peakrdl_halcpp/templates/test.cpp.j2 | 23 ++ .../templates/test_CMakeLists.txt.j2 | 31 +++ src/peakrdl_halcpp/test_generator.py | 118 ++++++++++ .../test_generator/test_utils/CMakeLists.txt | 5 + .../test_generator/test_utils/mock_io.h | 32 +++ .../test_generator/test_utils/test_utils.h | 220 ++++++++++++++++++ tests/cross_compile/simple/CMakeLists.txt | 3 +- tests/cross_compile/simple/main.cpp | 4 +- tests/soc/CMakeLists.txt | 18 ++ tests/soc/ips/CMakeLists.txt | 3 + tests/soc/ips/gpio/CMakeLists.txt | 4 + tests/soc/ips/gpio/gpio.rdl | 25 ++ tests/soc/ips/i2c/CMakeLists.txt | 4 + tests/soc/ips/i2c/i2c.rdl | 28 +++ tests/soc/soc.rdl | 8 + tests/test_utils/CMakeLists.txt | 1 - tests/test_utils/test_utils.h | 36 ++- 18 files changed, 577 insertions(+), 7 deletions(-) create mode 100644 src/peakrdl_halcpp/templates/test.cpp.j2 create mode 100644 src/peakrdl_halcpp/templates/test_CMakeLists.txt.j2 create mode 100644 src/peakrdl_halcpp/test_generator.py create mode 100644 src/peakrdl_halcpp/test_generator/test_utils/CMakeLists.txt create mode 100644 src/peakrdl_halcpp/test_generator/test_utils/mock_io.h create mode 100644 src/peakrdl_halcpp/test_generator/test_utils/test_utils.h create mode 100644 tests/soc/CMakeLists.txt create mode 100644 tests/soc/ips/CMakeLists.txt create mode 100644 tests/soc/ips/gpio/CMakeLists.txt create mode 100644 tests/soc/ips/gpio/gpio.rdl create mode 100644 tests/soc/ips/i2c/CMakeLists.txt create mode 100644 tests/soc/ips/i2c/i2c.rdl create mode 100644 tests/soc/soc.rdl diff --git a/src/peakrdl_halcpp/__peakrdl__.py b/src/peakrdl_halcpp/__peakrdl__.py index 6ee080b..f1b3de7 100644 --- a/src/peakrdl_halcpp/__peakrdl__.py +++ b/src/peakrdl_halcpp/__peakrdl__.py @@ -1,6 +1,9 @@ from typing import TYPE_CHECKING +import pathlib -from peakrdl.plugins.exporter import ExporterSubcommandPlugin # pylint: disable=import-error +from peakrdl.plugins.exporter import ExporterSubcommandPlugin + +from peakrdl_halcpp.test_generator import TestGenerator # pylint: disable=import-error from .exporter import HalExporter @@ -42,6 +45,14 @@ def add_exporter_arguments(self, arg_group: 'argparse.ArgumentParser') -> None: passing --skip-buses flag." ) + arg_group.add_argument( + "--generate-tests", + dest="generate_tests", + default=False, + action="store_true", + help="Generate tests" + ) + def do_export(self, top_node: 'AddrmapNode', options: 'argparse.Namespace') -> None: """Plugin entry function.""" hal = HalExporter() @@ -52,3 +63,11 @@ def do_export(self, top_node: 'AddrmapNode', options: 'argparse.Namespace') -> N ext_modules=options.ext, skip_buses=options.skip_buses, ) + + if options.generate_tests: + tests = TestGenerator() + + test_outdir = pathlib.Path(options.output) / "tests" + test_outdir.mkdir(parents=True, exist_ok=True) + tests.export(node=top_node, + outdir=str(test_outdir)) diff --git a/src/peakrdl_halcpp/templates/test.cpp.j2 b/src/peakrdl_halcpp/templates/test.cpp.j2 new file mode 100644 index 0000000..277f446 --- /dev/null +++ b/src/peakrdl_halcpp/templates/test.cpp.j2 @@ -0,0 +1,23 @@ +#include "mock_io.h" +#include "{{ top.orig_type_name }}_hal.h" +#include "test_utils.h" + +inline constexpr {{ top.orig_type_name|upper }}_HAL<0> {{ top.orig_type_name }}{}; + +int main (int argc, char *argv[]) { + +{% for field in fields %} + test_generic_field({{ field.get_path() }}, {{ field.width }}, {{ field.parent.absolute_address }}, {{ field.low }}, {{ field.high }}, "{{ field.get_path() }}"); + {% if field.is_sw_readable and field.is_sw_writable %} + test_rw_field({{ field.get_path() }}, "{{ field.get_path() }}"); + {% elif field.is_sw_readable and not field.is_sw_writable %} + test_ro_field({{ field.get_path() }}, "{{ field.get_path() }}"); + {% elif not field.is_sw_readable and field.is_sw_writable %} + test_wo_field({{ field.get_path() }}, "{{ field.get_path() }}"); + {% endif %} + +{% endfor %} + + return 0; +} + diff --git a/src/peakrdl_halcpp/templates/test_CMakeLists.txt.j2 b/src/peakrdl_halcpp/templates/test_CMakeLists.txt.j2 new file mode 100644 index 0000000..3560525 --- /dev/null +++ b/src/peakrdl_halcpp/templates/test_CMakeLists.txt.j2 @@ -0,0 +1,31 @@ +cmake_minimum_required(VERSION 3.25) +project(test_hal CXX) + +add_subdirectory(test_utils) + +enable_testing() + +function(hal_test TEST_NAME) + add_executable(${TEST_NAME} + ./${TEST_NAME}.cpp + ) + + target_include_directories(${TEST_NAME} PUBLIC + ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/include + ${CMAKE_CURRENT_LIST_DIR}/.. + ) + + target_link_libraries(${TEST_NAME} + test_utils + ) + + add_test( + NAME ${TEST_NAME} + COMMAND $ + ) +endfunction() + +{% for addrmap in addrmaps %} +hal_test(test_{{ addrmap.get_path("_") }}) +{% endfor %} diff --git a/src/peakrdl_halcpp/test_generator.py b/src/peakrdl_halcpp/test_generator.py new file mode 100644 index 0000000..9c4c75a --- /dev/null +++ b/src/peakrdl_halcpp/test_generator.py @@ -0,0 +1,118 @@ +from pathlib import Path +import shutil +import jinja2 as jj +import os +from typing import Any, Dict, Optional +from systemrdl.node import AddrmapNode, FieldNode +from systemrdl.walker import RDLListener, RDLWalker, WalkerAction + + +class FieldListener(RDLListener): + + def __init__(self) -> None: + self.fields: list[FieldNode] = [] + self.curr_addrmap: AddrmapNode|None = None + + def enter_Field(self, node: FieldNode) -> Optional[WalkerAction]: + self.fields.append(node) + + def enter_Addrmap(self, node: AddrmapNode) -> Optional[WalkerAction]: + if self.curr_addrmap is None: + self.curr_addrmap = node + else: + return WalkerAction.SkipDescendants + +class TestGeneratorListener(RDLListener): + + def __init__(self) -> None: + self.addrmaps: list[AddrmapNode] = [] + + def enter_Addrmap(self, node: AddrmapNode) -> Optional[WalkerAction]: + self.addrmaps.append(node) + + +class TestGenerator: + + def __init__(self) -> None: + pass + + def export(self, + node: AddrmapNode, + outdir: str) -> None: + + walker = RDLWalker(unroll=True) + listener = TestGeneratorListener() + walker.walk(node, listener) + + + for addrmap in listener.addrmaps: + field_listener = FieldListener() + walker.walk(addrmap, field_listener) + + context: dict[str, Any] = { + "top": node, + "fields": field_listener.fields, + } + test_content: str = self.process_template(context, "test.cpp.j2") + + out_file = os.path.join( + outdir, f"test_{addrmap.get_path('_')}.cpp") + + with open(out_file, "w") as f: + f.write(test_content) + + cmake_context: dict[str, Any] = { + "addrmaps": listener.addrmaps, + } + cmake_content: str = self.process_template(cmake_context, "test_CMakeLists.txt.j2") + cmake_out_file = os.path.join( + outdir, "CMakeLists.txt") + + with open(cmake_out_file, "w") as f: + f.write(cmake_content) + + + self.copy_test_directory(outdir) + + + + def process_template(self, + context: Dict, + template: str, + ) -> str: + """Generates a C++ header file based on a jinja2 template. + + Parameters + ---------- + context: Dict + Dictionary containing a HalAddrmapNode and the HalUtils object + (and other variables) passed to the jinja2 env + + Returns + ------- + str + Text of the generated C++ header for a given HalAddrmap node. + """ + # Create a jinja2 env with the template contained in the templates + # folder located in the same directory than this file + env = jj.Environment( + loader=jj.FileSystemLoader( + '%s/templates/' % os.path.dirname(__file__)), + trim_blocks=True, + lstrip_blocks=True) + # Add the base zip function to the env + env.filters.update({ + 'zip': zip, + }) + # Render the C++ header text using the jinja2 template and the + # specific context + cpp_header_text = env.get_template(template).render(context) + return cpp_header_text + + def copy_test_directory(self, outdir: str): + curr_dir: Path = Path(__file__).resolve().parent + src: Path = curr_dir / "test_generator" + dst = Path(outdir) + + shutil.copytree(src, dst, dirs_exist_ok=True) + diff --git a/src/peakrdl_halcpp/test_generator/test_utils/CMakeLists.txt b/src/peakrdl_halcpp/test_generator/test_utils/CMakeLists.txt new file mode 100644 index 0000000..86c8ee8 --- /dev/null +++ b/src/peakrdl_halcpp/test_generator/test_utils/CMakeLists.txt @@ -0,0 +1,5 @@ +add_library(test_utils INTERFACE) +target_include_directories(test_utils INTERFACE + ${CMAKE_CURRENT_LIST_DIR} +) + diff --git a/src/peakrdl_halcpp/test_generator/test_utils/mock_io.h b/src/peakrdl_halcpp/test_generator/test_utils/mock_io.h new file mode 100644 index 0000000..f18d322 --- /dev/null +++ b/src/peakrdl_halcpp/test_generator/test_utils/mock_io.h @@ -0,0 +1,32 @@ +#ifndef _ARCH_IO_H_ +#define _ARCH_IO_H_ + +#include +#include +#include +#include + + + +class MockMemIO +{ +public: + static inline std::array mem; + + static inline uint32_t read32(uint32_t addr) { + // std::cout << "Read32 at addr 0x" << std::hex << addr << " Read data: 0x" << std::hex << MockMemIO::mem[addr] << std::endl; + return MockMemIO::mem[addr]; + } + + static inline void write32(uint32_t addr, uint32_t val) { + // std::cout << "Write32 at addr 0x" << std::hex << addr << " Write data: 0x" << std::hex << val << std::endl; + MockMemIO::mem[addr] = val; + } +}; + +class ArchIoNode : public MockMemIO +{ +public: +}; + +#endif // !_ARCH_IO_H_ diff --git a/src/peakrdl_halcpp/test_generator/test_utils/test_utils.h b/src/peakrdl_halcpp/test_generator/test_utils/test_utils.h new file mode 100644 index 0000000..31984ae --- /dev/null +++ b/src/peakrdl_halcpp/test_generator/test_utils/test_utils.h @@ -0,0 +1,220 @@ +#include +#include +#include + +/****************************/ +/** Compile time for loop ***/ +/****************************/ + +template +constexpr void for_sequence(std::integer_sequence, F f) { + (static_cast(f(std::integral_constant{})), ...); +} + +template +constexpr void for_sequence(F f) { + for_sequence(std::make_integer_sequence{}, f); +} + +/****************************/ +/***** Field Width test *****/ +/****************************/ + +void assert_field_width(uint32_t expected_width, + uint32_t actual_width, + const char *field_path){ + if(expected_width != actual_width){ + std::cerr << "Field width test error for " << field_path; + std::cerr << "\n\tExpected width: " << std::dec << expected_width; + std::cerr << "\n\tActual width: " << std::dec << actual_width << std::endl; + assert(expected_width == actual_width); + } +} + +template +void test_field_width(FIELD_T &field, uint32_t width, const char* field_path){ + assert_field_width(width, field.get_width(), field_path); +} + +/**********************************/ +/***** Field Base address test ****/ +/**********************************/ + +void assert_field_base_address(uint32_t expected_base_address, + uint32_t actual_base_address, + const char *field_path){ + if(expected_base_address != actual_base_address){ + std::cerr << "Field width test error for " << field_path; + std::cerr << "\n\tExpected width: " << std::dec << expected_base_address; + std::cerr << "\n\tActual width: " << std::dec << actual_base_address << std::endl; + assert(expected_base_address == actual_base_address); + } +} + +template +void test_field_base_address(FIELD_T &field, uint32_t base_address, const char* field_path){ + assert_field_width(base_address, field.get_abs_addr(), field_path); +} + +/**********************************/ +/*** Field start/end bit test *****/ +/**********************************/ + +void assert_field_start_end_bit(uint32_t expected_start_bit, + uint32_t actual_start_bit, + uint32_t expected_end_bit, + uint32_t actual_end_bit, + const char *field_path){ + if((expected_start_bit != actual_start_bit) or + (expected_end_bit != actual_end_bit)){ + std::cerr << "Field start/end test error for " << field_path; + std::cerr << "\n\tExpected start bit index: " << std::dec << expected_start_bit; + std::cerr << "\n\tActual start bit index: " << std::dec << actual_start_bit; + std::cerr << "\n\tExpected end bit index: " << std::dec << expected_end_bit; + std::cerr << "\n\tActual end bit index: " << std::dec << actual_end_bit << std::endl; + assert(expected_start_bit == actual_start_bit); + assert(expected_end_bit == actual_end_bit); + } +} + +template +void test_field_start_end_bit(FIELD_T &field, uint32_t start_bit, uint32_t end_bit, const char* field_path){ + uint32_t read_start_bit = field.get_start_bit(); + uint32_t read_end_bit = field.get_end_bit(); + assert_field_start_end_bit(start_bit, read_start_bit, end_bit, read_end_bit, field_path); +} + +/****************************/ +/****** RW Field test *******/ +/****************************/ + +void assert_rw(uint32_t exp, uint32_t rec, const char* field_path){ + if(exp != rec){ + std::cerr << "Field RW test error for " << field_path; + std::cerr << "\n\tWrote: 0x" << std::hex << exp; + std::cerr << "\n\tRead: 0x" << std::hex << rec << "\n"; + assert(exp == rec); + } +} + +template +void test_rw_field(uint32_t val, uint32_t exp, FIELD_T &field, const char* field_path){ + field = val; + + uint32_t result = field; + assert_rw(exp, result, field_path); +} + +template +void test_rw_field_marching_ones(FIELD_T &field, const char* field_path){ + uint32_t width = field.get_width(); + test_rw_field(0, 0, field, field_path); + for (int i = 0; i < width; i++) { + uint32_t write_val = 1 << i; + test_rw_field(write_val, write_val, field, field_path); + } +} + +template +void test_rw_field_overflow(FIELD_T &field, const char* field_path){ + uint32_t width = field.get_width(); + test_rw_field((1 << width), 0, field, field_path); +} + +void assert_rw_at(uint32_t bit_idx, uint32_t exp_bit_val, uint32_t exp_field_val, uint32_t actual_field_val, const char* field_path) { + uint32_t mask = 1u << bit_idx; + uint32_t rec_bit = (actual_field_val & mask) ? 1u : 0u; + + if ((exp_bit_val != rec_bit) or + (exp_field_val != actual_field_val)) { + std::cerr << "Field RW at() accessor test error for " << field_path; + std::cerr << "\n\tBit index: " << std::dec << bit_idx; + std::cerr << "\n\tExpected: " << exp_bit_val; + std::cerr << "\n\tBut read: " << rec_bit; + std::cerr << "\n\tExpected field value: 0x" << std::hex << exp_field_val; + std::cerr << "\n\tActual field value: 0x" << std::hex << actual_field_val << "\n"; + assert(exp_bit_val == rec_bit); + assert(exp_field_val == actual_field_val); + } +} + +template +void test_rw_field_at_accessor(FIELD_T &field, const char* field_path){ + test_rw_field(0, 0, field, field_path); + uint32_t exp_val = 0; + for_sequence([&field, &exp_val, &field_path](auto i) { + field.template at() = 1; + exp_val = exp_val | (1 << i); + assert_rw_at(i, 1, exp_val, field.get(), field_path); + }); + + // Set the last bit of the field to 0, (currently all bits are 1) + field.template at<-1>() = 0; + exp_val = (1 << (WIDTH-1)) - 1; + assert_rw_at(WIDTH-1, 0, exp_val, field.get(), field_path); +} + +template +void test_rw_field(FIELD_T &field, + const char* field_path){ + test_rw_field_marching_ones(field, field_path); + test_rw_field_overflow(field, field_path); + test_rw_field_at_accessor(field, field_path); +} + + +/****************************/ +/****** RO Field test *******/ +/****************************/ + +template +void test_ro_field_at_accessor(FIELD_T &field, const char* field_path){ + for_sequence([&field, &field_path](auto i) { + volatile uint32_t val = field.template at(); + }); + volatile uint32_t val = field.template at<-1>().get(); +} + +template +void test_ro_field(FIELD_T &field, + const char* field_path){ + volatile uint32_t read = field; + test_ro_field_at_accessor(field, field_path); +} + +/****************************/ +/****** WO Field test *******/ +/****************************/ + +template +void test_wo_field_at_accessor(FIELD_T &field, const char* field_path){ + for_sequence([&field, &field_path](auto i) { + field.template at() = 1; + }); + field.template at<-1>() = 0; +} + +template +void test_wo_field(FIELD_T &field, + const char* field_path){ + field = 1; + test_wo_field_at_accessor(field, field_path); +} + +/****************************/ +/**** Generic Field test ****/ +/****************************/ + +template +void test_generic_field(FIELD_T &field, + uint32_t width, + size_t base_address, + uint32_t start_bit, + uint32_t end_bit, + const char* field_path){ + + test_field_width(field, width, field_path); + test_field_base_address(field, base_address, field_path); + test_field_start_end_bit(field, start_bit, end_bit, field_path); + +} diff --git a/tests/cross_compile/simple/CMakeLists.txt b/tests/cross_compile/simple/CMakeLists.txt index c343d26..8ae8bea 100644 --- a/tests/cross_compile/simple/CMakeLists.txt +++ b/tests/cross_compile/simple/CMakeLists.txt @@ -9,7 +9,7 @@ include("../../cmake/deps.cmake") add_ip(${PROJECT_NAME}_ip) ip_sources(${IP} SYSTEMRDL - ../../simple/fixp.rdl + ../../simple/simple.rdl ) peakrdl_halcpp(${IP}) @@ -29,6 +29,7 @@ target_link_libraries(${PROJECT_NAME} target_compile_options(${PROJECT_NAME} PUBLIC -Os -fno-delete-null-pointer-checks + -Wall -Wextra -Wpedantic -Werror ) firmware_size(${PROJECT_NAME}) diff --git a/tests/cross_compile/simple/main.cpp b/tests/cross_compile/simple/main.cpp index 3d4f8f9..a7f1f51 100644 --- a/tests/cross_compile/simple/main.cpp +++ b/tests/cross_compile/simple/main.cpp @@ -4,10 +4,10 @@ int main () { volatile SIMPLE_HAL<0> soc; - float num = 4.8; + int num = 4; soc.r1.f1 = num; - float read_num = soc.r1.f1; + [[maybe_unused]] int read_num = soc.r1.f1; // soc.r1 = 2; // soc.r1.f2 = 10; // diff --git a/tests/soc/CMakeLists.txt b/tests/soc/CMakeLists.txt new file mode 100644 index 0000000..427bfab --- /dev/null +++ b/tests/soc/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.25) +project(soc NONE) + +include("../cmake/deps.cmake") + +add_ip(soc) +ip_sources(${IP} SYSTEMRDL + ./soc.rdl +) + +add_subdirectory(ips/) + +ip_link(${IP} + gpio + i2c +) + +peakrdl_halcpp(${IP} GENERATE_TESTS) diff --git a/tests/soc/ips/CMakeLists.txt b/tests/soc/ips/CMakeLists.txt new file mode 100644 index 0000000..cfa31a1 --- /dev/null +++ b/tests/soc/ips/CMakeLists.txt @@ -0,0 +1,3 @@ + +add_subdirectory(gpio) +add_subdirectory(i2c) diff --git a/tests/soc/ips/gpio/CMakeLists.txt b/tests/soc/ips/gpio/CMakeLists.txt new file mode 100644 index 0000000..17f0b6d --- /dev/null +++ b/tests/soc/ips/gpio/CMakeLists.txt @@ -0,0 +1,4 @@ +add_ip(gpio) +ip_sources(${IP} SYSTEMRDL + ./gpio.rdl +) diff --git a/tests/soc/ips/gpio/gpio.rdl b/tests/soc/ips/gpio/gpio.rdl new file mode 100644 index 0000000..d044a1f --- /dev/null +++ b/tests/soc/ips/gpio/gpio.rdl @@ -0,0 +1,25 @@ +addrmap gpio { + reg { + field { + } f1[1:0] = 0; + + field { + desc = "These bits select the transfer mode"; + } f2[3:2] = 0; + + field { + hw=rw; we; + } f3[4:4] = 0; + + field { + } f4[31:5] = 0; + + } r1; + + reg { + + field {} f5[15:0] = 0; + field {} f6[31:16] = 0; + } r2; +}; + diff --git a/tests/soc/ips/i2c/CMakeLists.txt b/tests/soc/ips/i2c/CMakeLists.txt new file mode 100644 index 0000000..d42fce2 --- /dev/null +++ b/tests/soc/ips/i2c/CMakeLists.txt @@ -0,0 +1,4 @@ +add_ip(i2c) +ip_sources(${IP} SYSTEMRDL + ./i2c.rdl +) diff --git a/tests/soc/ips/i2c/i2c.rdl b/tests/soc/ips/i2c/i2c.rdl new file mode 100644 index 0000000..1e59150 --- /dev/null +++ b/tests/soc/ips/i2c/i2c.rdl @@ -0,0 +1,28 @@ +addrmap i2c { + reg { + field { + sw=w; + } f1[1:0] = 0; + + field { + desc = "These bits select the transfer mode"; + sw=r; + } f2[3:2] = 0; + + field { + hw=rw; we; + } f3[4:4] = 0; + + field { + } f4[16:5] = 0; + + } r1; + + reg { + + field {} f5[15:0] = 0; + field {} f6[31:16] = 0; + } r2; +}; + + diff --git a/tests/soc/soc.rdl b/tests/soc/soc.rdl new file mode 100644 index 0000000..e1a6b6f --- /dev/null +++ b/tests/soc/soc.rdl @@ -0,0 +1,8 @@ +addrmap soc{ + + gpio gpio_1; + gpio gpio_2; + gpio gpio_3; + gpio gpio_4; + i2c i2c_1; +}; diff --git a/tests/test_utils/CMakeLists.txt b/tests/test_utils/CMakeLists.txt index 86c8ee8..5d25f54 100644 --- a/tests/test_utils/CMakeLists.txt +++ b/tests/test_utils/CMakeLists.txt @@ -2,4 +2,3 @@ add_library(test_utils INTERFACE) target_include_directories(test_utils INTERFACE ${CMAKE_CURRENT_LIST_DIR} ) - diff --git a/tests/test_utils/test_utils.h b/tests/test_utils/test_utils.h index fe6edce..1ca0a97 100644 --- a/tests/test_utils/test_utils.h +++ b/tests/test_utils/test_utils.h @@ -11,6 +11,36 @@ class HalFieldTest : public ::testing::Test { static auto& field() { return FieldT::get(); } }; +/***********************/ +/*** WO field tests ****/ +/***********************/ + +template +void test_w_field(uint32_t val, FIELD_T &field) { + field = val; +} + +template +void test_w_field_range(FIELD_T &field) { + int width = field.get_width(); + for (int i = 0; i < (1 << width); i++) { + test_w_field(i, i, field); + } +} + +/***********************/ +/*** RO field tests ****/ +/***********************/ + +template +void test_r_field(int32_t exp, FIELD_T &field) { + EXPECT_EQ((uint32_t)field, exp); +} + +/***********************/ +/*** RW field tests ****/ +/***********************/ + template void test_rw_field(uint32_t val, int32_t exp, FIELD_T &field) { field = val; @@ -18,10 +48,12 @@ void test_rw_field(uint32_t val, int32_t exp, FIELD_T &field) { } template -void test_rw_field_range(FIELD_T &field) { +void test_rw_field_marching_ones(FIELD_T &field) { + test_rw_field(0, 0, field); int width = field.get_width(); for (int i = 0; i < (1 << width); i++) { - test_rw_field(i, i, field); + uint32_t write_val = 1 << i; + test_rw_field(write_val, write_val, field); } } From 7fb25b643e3f0f83049fc55e9d784891c944c536 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Risto=20Peja=C5=A1inovi=C4=87?= Date: Sun, 14 Sep 2025 23:49:00 +0200 Subject: [PATCH 7/9] fix RegNode operator const --- src/peakrdl_halcpp/include/reg_node.h | 6 +++--- src/peakrdl_halcpp/templates/addrmap.h.j2 | 4 ---- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/peakrdl_halcpp/include/reg_node.h b/src/peakrdl_halcpp/include/reg_node.h index cdd210e..ee46c86 100644 --- a/src/peakrdl_halcpp/include/reg_node.h +++ b/src/peakrdl_halcpp/include/reg_node.h @@ -106,12 +106,12 @@ namespace halcpp * @return operator const T() The value of the register converted to type T. */ template - inline operator const T() - { + inline operator T() const { static_assert(std::is_integral::value, "T must be an integral type."); static_assert( - sizeof(T) >= (float)(BASE_TYPE::width / 8), + sizeof(T) >= (BASE_TYPE::width / 8), "T must be smaller than or equal to Field width, otherwise data will be lost"); + return static_cast(get()); } }; diff --git a/src/peakrdl_halcpp/templates/addrmap.h.j2 b/src/peakrdl_halcpp/templates/addrmap.h.j2 index a1e3ef8..8ddb597 100644 --- a/src/peakrdl_halcpp/templates/addrmap.h.j2 +++ b/src/peakrdl_halcpp/templates/addrmap.h.j2 @@ -8,10 +8,6 @@ #include #include "include/halcpp_base.h" -#if defined(__clang__) -#pragma clang diagnostic ignored "-Wundefined-var-template" -#endif - {# Include the child addrmap nodes #} {% for child_addrmap in halnode.halchildren(children_type=HalAddrmapNode, skip_buses=skip_buses, unique_orig_type=True) %} #include "{{ halutils.get_include_file(child_addrmap) }}" From 87b9f3b63fbcf3dd104625b924903d8187a2b63d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Risto=20Peja=C5=A1inovi=C4=87?= Date: Sun, 14 Sep 2025 23:49:52 +0200 Subject: [PATCH 8/9] Add regression test --- src/peakrdl_halcpp/templates/test.cpp.j2 | 26 ++- .../templates/test_CMakeLists.txt.j2 | 5 + src/peakrdl_halcpp/test_generator.py | 30 ++- .../test_utils/field_test_utils.h | 210 ++++++++++++++++++ .../test_utils/reg_test_utils.h | 81 +++++++ .../test_generator/test_utils/test_utils.h | 208 +---------------- tests/CMakeLists.txt | 24 ++ tests/cmake/add_hal_test.cmake | 50 ++--- tests/cmake/deps.cmake | 12 +- tests/cmake/fw_utils.cmake | 19 -- tests/cmake/toolchains/riscv_toolchain.cmake | 119 ---------- tests/cross_compile/fixp/CMakeLists.txt | 38 ---- tests/cross_compile/fixp/main.cpp | 13 -- tests/cross_compile/simple/CMakeLists.txt | 39 ---- tests/cross_compile/simple/main.cpp | 21 -- tests/rdl_lib/CMakeLists.txt | 45 ++++ tests/rdl_lib/addrmap_hier_mixed.rdl | 13 ++ tests/rdl_lib/addrmap_hier_mixed2.rdl | 11 + tests/rdl_lib/addrmap_hier_ro.rdl | 7 + tests/rdl_lib/addrmap_hier_rw.rdl | 6 + tests/rdl_lib/addrmap_hier_rw_reg_arrays.rdl | 5 + tests/rdl_lib/addrmap_hier_wo.rdl | 8 + tests/rdl_lib/addrmap_mixed_access.rdl | 11 + tests/rdl_lib/addrmap_ro.rdl | 11 + tests/rdl_lib/addrmap_rw.rdl | 13 ++ tests/rdl_lib/addrmap_rw_reg_1d_array.rdl | 6 + tests/rdl_lib/addrmap_rw_reg_2d_array.rdl | 7 + tests/rdl_lib/addrmap_rw_reg_4d_array.rdl | 6 + tests/rdl_lib/addrmap_wo.rdl | 12 + tests/simple/CMakeLists.txt | 25 --- tests/simple/fixp.rdl | 19 -- tests/simple/simple.rdl | 24 -- tests/simple/simple_ro.rdl | 9 - tests/simple/test_fixp.cpp | 14 -- tests/simple/test_simple.cpp | 23 -- tests/simple/test_simple_ro.cpp | 21 -- tests/soc/CMakeLists.txt | 18 -- tests/soc/ips/CMakeLists.txt | 3 - tests/soc/ips/gpio/CMakeLists.txt | 4 - tests/soc/ips/gpio/gpio.rdl | 25 --- tests/soc/ips/i2c/CMakeLists.txt | 4 - tests/soc/ips/i2c/i2c.rdl | 28 --- tests/soc/soc.rdl | 8 - tests/test_utils/CMakeLists.txt | 4 - tests/test_utils/mock_io.h | 34 --- tests/test_utils/test_utils.h | 64 ------ 46 files changed, 555 insertions(+), 828 deletions(-) create mode 100644 src/peakrdl_halcpp/test_generator/test_utils/field_test_utils.h create mode 100644 src/peakrdl_halcpp/test_generator/test_utils/reg_test_utils.h create mode 100644 tests/CMakeLists.txt delete mode 100644 tests/cmake/fw_utils.cmake delete mode 100644 tests/cmake/toolchains/riscv_toolchain.cmake delete mode 100644 tests/cross_compile/fixp/CMakeLists.txt delete mode 100644 tests/cross_compile/fixp/main.cpp delete mode 100644 tests/cross_compile/simple/CMakeLists.txt delete mode 100644 tests/cross_compile/simple/main.cpp create mode 100644 tests/rdl_lib/CMakeLists.txt create mode 100644 tests/rdl_lib/addrmap_hier_mixed.rdl create mode 100644 tests/rdl_lib/addrmap_hier_mixed2.rdl create mode 100644 tests/rdl_lib/addrmap_hier_ro.rdl create mode 100644 tests/rdl_lib/addrmap_hier_rw.rdl create mode 100644 tests/rdl_lib/addrmap_hier_rw_reg_arrays.rdl create mode 100644 tests/rdl_lib/addrmap_hier_wo.rdl create mode 100644 tests/rdl_lib/addrmap_mixed_access.rdl create mode 100644 tests/rdl_lib/addrmap_ro.rdl create mode 100644 tests/rdl_lib/addrmap_rw.rdl create mode 100644 tests/rdl_lib/addrmap_rw_reg_1d_array.rdl create mode 100644 tests/rdl_lib/addrmap_rw_reg_2d_array.rdl create mode 100644 tests/rdl_lib/addrmap_rw_reg_4d_array.rdl create mode 100644 tests/rdl_lib/addrmap_wo.rdl delete mode 100644 tests/simple/CMakeLists.txt delete mode 100644 tests/simple/fixp.rdl delete mode 100644 tests/simple/simple.rdl delete mode 100644 tests/simple/simple_ro.rdl delete mode 100644 tests/simple/test_fixp.cpp delete mode 100644 tests/simple/test_simple.cpp delete mode 100644 tests/simple/test_simple_ro.cpp delete mode 100644 tests/soc/CMakeLists.txt delete mode 100644 tests/soc/ips/CMakeLists.txt delete mode 100644 tests/soc/ips/gpio/CMakeLists.txt delete mode 100644 tests/soc/ips/gpio/gpio.rdl delete mode 100644 tests/soc/ips/i2c/CMakeLists.txt delete mode 100644 tests/soc/ips/i2c/i2c.rdl delete mode 100644 tests/soc/soc.rdl delete mode 100644 tests/test_utils/CMakeLists.txt delete mode 100644 tests/test_utils/mock_io.h delete mode 100644 tests/test_utils/test_utils.h diff --git a/src/peakrdl_halcpp/templates/test.cpp.j2 b/src/peakrdl_halcpp/templates/test.cpp.j2 index 277f446..cd55854 100644 --- a/src/peakrdl_halcpp/templates/test.cpp.j2 +++ b/src/peakrdl_halcpp/templates/test.cpp.j2 @@ -1,19 +1,33 @@ #include "mock_io.h" #include "{{ top.orig_type_name }}_hal.h" -#include "test_utils.h" +#include "field_test_utils.h" +#include "reg_test_utils.h" inline constexpr {{ top.orig_type_name|upper }}_HAL<0> {{ top.orig_type_name }}{}; -int main (int argc, char *argv[]) { +int main () { + +{% for reg in regs %} + test_generic_reg({{ reg|resolve_path_array_refs }}, {{ reg.absolute_address }}, "{{ reg.get_path() }}"); + {% if reg.has_sw_readable and reg.has_sw_writable %} + test_rw_reg({{ reg|resolve_path_array_refs }}, "{{ reg.get_path() }}"); + {% elif reg.has_sw_readable and not reg.has_sw_writable %} + test_ro_reg({{ reg|resolve_path_array_refs }}); + {% elif not reg.has_sw_readable and reg.has_sw_writable %} + test_wo_reg({{ reg|resolve_path_array_refs }}); + {% endif %} + +{% endfor %} {% for field in fields %} - test_generic_field({{ field.get_path() }}, {{ field.width }}, {{ field.parent.absolute_address }}, {{ field.low }}, {{ field.high }}, "{{ field.get_path() }}"); + {% set field_ref = field|resolve_path_array_refs %} + test_generic_field({{ field_ref }}, {{ field.width }}, {{ field.parent.absolute_address }}, {{ field.low }}, {{ field.high }}, "{{ field.get_path() }}"); {% if field.is_sw_readable and field.is_sw_writable %} - test_rw_field({{ field.get_path() }}, "{{ field.get_path() }}"); + test_rw_field({{ field_ref }}, "{{ field.get_path() }}"); {% elif field.is_sw_readable and not field.is_sw_writable %} - test_ro_field({{ field.get_path() }}, "{{ field.get_path() }}"); + test_ro_field({{ field_ref }}); {% elif not field.is_sw_readable and field.is_sw_writable %} - test_wo_field({{ field.get_path() }}, "{{ field.get_path() }}"); + test_wo_field({{ field_ref }}); {% endif %} {% endfor %} diff --git a/src/peakrdl_halcpp/templates/test_CMakeLists.txt.j2 b/src/peakrdl_halcpp/templates/test_CMakeLists.txt.j2 index 3560525..c2b5679 100644 --- a/src/peakrdl_halcpp/templates/test_CMakeLists.txt.j2 +++ b/src/peakrdl_halcpp/templates/test_CMakeLists.txt.j2 @@ -20,6 +20,11 @@ function(hal_test TEST_NAME) test_utils ) + target_compile_options(${TEST_NAME} PUBLIC + -fno-delete-null-pointer-checks + -Wall -Wextra -Wpedantic -Werror + ) + add_test( NAME ${TEST_NAME} COMMAND $ diff --git a/src/peakrdl_halcpp/test_generator.py b/src/peakrdl_halcpp/test_generator.py index 9c4c75a..e2ffa24 100644 --- a/src/peakrdl_halcpp/test_generator.py +++ b/src/peakrdl_halcpp/test_generator.py @@ -1,9 +1,10 @@ from pathlib import Path +import re import shutil import jinja2 as jj import os from typing import Any, Dict, Optional -from systemrdl.node import AddrmapNode, FieldNode +from systemrdl.node import AddrmapNode, FieldNode, Node, RegNode from systemrdl.walker import RDLListener, RDLWalker, WalkerAction @@ -11,11 +12,15 @@ class FieldListener(RDLListener): def __init__(self) -> None: self.fields: list[FieldNode] = [] + self.regs: list[RegNode] = [] self.curr_addrmap: AddrmapNode|None = None def enter_Field(self, node: FieldNode) -> Optional[WalkerAction]: self.fields.append(node) + def enter_Reg(self, node: RegNode) -> Optional[WalkerAction]: + self.regs.append(node) + def enter_Addrmap(self, node: AddrmapNode) -> Optional[WalkerAction]: if self.curr_addrmap is None: self.curr_addrmap = node @@ -30,6 +35,27 @@ def __init__(self) -> None: def enter_Addrmap(self, node: AddrmapNode) -> Optional[WalkerAction]: self.addrmaps.append(node) +def transform_systemrdl_array_indices_to_halcpp(s): + # Match identifier followed by group of indexes reg0[0][2][4] + pattern = re.compile(r'([a-zA-Z_]\w*)((?:\[\d+\])+)' ) + + def repl(match): + ident = match.group(1) + brackets = match.group(2) + # extract numbers from [n] + nums = re.findall(r'\[(\d+)\]', brackets) + # replace it with .at<0, 2, 4>() + return f"{ident}.at<{','.join(nums)}>()" + + return re.sub(pattern, repl, s) + +def resolve_path_array_refs(node: Node) -> str: + path = node.get_path() + # path = path.replace("[", ".template at<") + # path = path.replace("]", ">()") + path = transform_systemrdl_array_indices_to_halcpp(path) + + return path class TestGenerator: @@ -52,6 +78,7 @@ def export(self, context: dict[str, Any] = { "top": node, "fields": field_listener.fields, + "regs": field_listener.regs, } test_content: str = self.process_template(context, "test.cpp.j2") @@ -103,6 +130,7 @@ def process_template(self, # Add the base zip function to the env env.filters.update({ 'zip': zip, + "resolve_path_array_refs": resolve_path_array_refs }) # Render the C++ header text using the jinja2 template and the # specific context diff --git a/src/peakrdl_halcpp/test_generator/test_utils/field_test_utils.h b/src/peakrdl_halcpp/test_generator/test_utils/field_test_utils.h new file mode 100644 index 0000000..2e16a31 --- /dev/null +++ b/src/peakrdl_halcpp/test_generator/test_utils/field_test_utils.h @@ -0,0 +1,210 @@ +#ifndef __FIELD_TEST_UTILS_H_ +#define __FIELD_TEST_UTILS_H_ + +#include +#include +#include +#include "test_utils.h" + +/****************************/ +/***** Field Width test *****/ +/****************************/ + +void assert_field_width(uint32_t expected_width, + uint32_t actual_width, + const char *field_path){ + if(expected_width != actual_width){ + std::cerr << "Field width test error for " << field_path; + std::cerr << "\n\tExpected width: " << std::dec << expected_width; + std::cerr << "\n\tActual width: " << std::dec << actual_width << std::endl; + assert(expected_width == actual_width); + } +} + +template +void test_field_width(FIELD_T &field, uint32_t width, const char* field_path){ + assert_field_width(width, field.get_width(), field_path); +} + +/**********************************/ +/***** Field Base address test ****/ +/**********************************/ + +void assert_field_base_address(uint32_t expected_base_address, + uint32_t actual_base_address, + const char *field_path){ + if(expected_base_address != actual_base_address){ + std::cerr << "Field base address test error for " << field_path; + std::cerr << "\n\tExpected base address: 0x" << std::hex << expected_base_address; + std::cerr << "\n\tActual base address: 0x" << std::hex << actual_base_address << std::endl; + assert(expected_base_address == actual_base_address); + } +} + +template +void test_field_base_address(FIELD_T &field, uint32_t base_address, const char* field_path){ + assert_field_width(base_address, field.get_abs_addr(), field_path); +} + +/**********************************/ +/*** Field start/end bit test *****/ +/**********************************/ + +void assert_field_start_end_bit(uint32_t expected_start_bit, + uint32_t actual_start_bit, + uint32_t expected_end_bit, + uint32_t actual_end_bit, + const char *field_path){ + if((expected_start_bit != actual_start_bit) or + (expected_end_bit != actual_end_bit)){ + std::cerr << "Field start/end test error for " << field_path; + std::cerr << "\n\tExpected start bit index: " << std::dec << expected_start_bit; + std::cerr << "\n\tActual start bit index: " << std::dec << actual_start_bit; + std::cerr << "\n\tExpected end bit index: " << std::dec << expected_end_bit; + std::cerr << "\n\tActual end bit index: " << std::dec << actual_end_bit << std::endl; + assert(expected_start_bit == actual_start_bit); + assert(expected_end_bit == actual_end_bit); + } +} + +template +void test_field_start_end_bit(FIELD_T &field, uint32_t start_bit, uint32_t end_bit, const char* field_path){ + uint32_t read_start_bit = field.get_start_bit(); + uint32_t read_end_bit = field.get_end_bit(); + assert_field_start_end_bit(start_bit, read_start_bit, end_bit, read_end_bit, field_path); +} + +/****************************/ +/****** RW Field test *******/ +/****************************/ + +void assert_rw(uint32_t exp, uint32_t rec, const char* field_path){ + if(exp != rec){ + std::cerr << "Field RW test error for " << field_path; + std::cerr << "\n\tWrote: 0x" << std::hex << exp; + std::cerr << "\n\tRead: 0x" << std::hex << rec << "\n"; + assert(exp == rec); + } +} + +template +void test_rw_field(uint32_t val, uint32_t exp, FIELD_T &field, const char* field_path){ + field = val; + + uint32_t result = field; + assert_rw(exp, result, field_path); +} + +template +void test_rw_field_marching_ones(FIELD_T &field, const char* field_path){ + uint32_t width = field.get_width(); + test_rw_field(0, 0, field, field_path); + for (uint32_t i = 0; i < width; i++) { + uint32_t write_val = 1 << i; + test_rw_field(write_val, write_val, field, field_path); + } +} + +template +void test_rw_field_overflow(FIELD_T &field, const char* field_path){ + uint32_t width = field.get_width(); + test_rw_field((1 << width), 0, field, field_path); +} + +void assert_rw_at(uint32_t bit_idx, uint32_t exp_bit_val, uint32_t exp_field_val, uint32_t actual_field_val, const char* field_path) { + uint32_t mask = 1u << bit_idx; + uint32_t rec_bit = (actual_field_val & mask) ? 1u : 0u; + + if ((exp_bit_val != rec_bit) or + (exp_field_val != actual_field_val)) { + std::cerr << "Field RW at() accessor test error for " << field_path; + std::cerr << "\n\tBit index: " << std::dec << bit_idx; + std::cerr << "\n\tExpected: " << exp_bit_val; + std::cerr << "\n\tBut read: " << rec_bit; + std::cerr << "\n\tExpected field value: 0x" << std::hex << exp_field_val; + std::cerr << "\n\tActual field value: 0x" << std::hex << actual_field_val << "\n"; + assert(exp_bit_val == rec_bit); + assert(exp_field_val == actual_field_val); + } +} + +template +void test_rw_field_at_accessor(FIELD_T &field, const char* field_path){ + test_rw_field(0, 0, field, field_path); + uint32_t exp_val = 0; + for_sequence([&field, &exp_val, &field_path](auto i) { + field.template at() = 1; + exp_val = exp_val | (1 << i); + assert_rw_at(i, 1, exp_val, field.get(), field_path); + }); + + // Set the last bit of the field to 0, (currently all bits are 1) + field.template at<-1>() = 0; + exp_val = (1 << (WIDTH-1)) - 1; + assert_rw_at(WIDTH-1, 0, exp_val, field.get(), field_path); +} + +template +void test_rw_field(FIELD_T &field, + const char* field_path){ + test_rw_field_marching_ones(field, field_path); + test_rw_field_overflow(field, field_path); + test_rw_field_at_accessor(field, field_path); +} + + +/****************************/ +/****** RO Field test *******/ +/****************************/ + +template +void test_ro_field_at_accessor(FIELD_T &field){ + for_sequence([&field](auto i) { + [[maybe_unused]] volatile uint32_t val = field.template at(); + }); + [[maybe_unused]] volatile uint32_t val = field.template at<-1>().get(); +} + +template +void test_ro_field(FIELD_T &field){ + [[maybe_unused]] volatile uint32_t read = field; + test_ro_field_at_accessor(field); +} + +/****************************/ +/****** WO Field test *******/ +/****************************/ + +template +void test_wo_field_at_accessor(FIELD_T &field){ + for_sequence([&field](auto i) { + field.template at() = 1; + }); + field.template at<-1>() = 0; +} + +template +void test_wo_field(FIELD_T &field){ + field = 1; + test_wo_field_at_accessor(field); +} + +/****************************/ +/**** Generic Field test ****/ +/****************************/ + +template +void test_generic_field(FIELD_T &field, + uint32_t width, + size_t base_address, + uint32_t start_bit, + uint32_t end_bit, + const char* field_path){ + + test_field_width(field, width, field_path); + test_field_base_address(field, base_address, field_path); + test_field_start_end_bit(field, start_bit, end_bit, field_path); + +} + +#endif diff --git a/src/peakrdl_halcpp/test_generator/test_utils/reg_test_utils.h b/src/peakrdl_halcpp/test_generator/test_utils/reg_test_utils.h new file mode 100644 index 0000000..0fbcfb9 --- /dev/null +++ b/src/peakrdl_halcpp/test_generator/test_utils/reg_test_utils.h @@ -0,0 +1,81 @@ +#ifndef __REG_TEST_UTILS_H_ +#define __REG_TEST_UTILS_H_ + +#include +#include +#include + +/**********************************/ +/****** Reg base address test *****/ +/**********************************/ + +void assert_reg_base_address(uint32_t expected_base_address, + uint32_t actual_base_address, + const char *reg_path){ + if(expected_base_address != actual_base_address){ + std::cerr << "Reg base address test error for " << reg_path; + std::cerr << "\n\tExpected base address: 0x" << std::hex << expected_base_address; + std::cerr << "\n\tActual base address: 0x" << std::hex << actual_base_address << std::endl; + assert(expected_base_address == actual_base_address); + } +} + +template +void test_reg_base_address(REG_T ®, uint32_t base_address, const char* reg_path){ + assert_reg_base_address(base_address, reg.get_abs_addr(), reg_path); +} + +/****************************/ +/**** Generic Reg test ******/ +/****************************/ + +template +void test_generic_reg(const REG_T ®, + size_t base_address, + const char* reg_path){ + + test_reg_base_address(reg, base_address, reg_path); +} + +/**********************************/ +/*********** RW reg test **********/ +/**********************************/ + +void assert_rw_reg(uint32_t expected_data, + uint32_t actual_data, + const char *reg_path){ + if(expected_data != actual_data){ + std::cerr << "RW Reg test error for " << reg_path; + std::cerr << "\n\tExpected data: 0x" << std::hex << expected_data; + std::cerr << "\n\tActual data: 0x" << std::hex << actual_data << std::endl; + assert(expected_data == actual_data); + } +} + +template +void test_rw_reg(REG_T reg, const char* reg_path){ + reg = reg.get_abs_addr(); + assert_rw_reg(reg.get_abs_addr(), reg.get(), reg_path); +} + +/**********************************/ +/*********** RO reg test **********/ +/**********************************/ + +template +void test_ro_reg(const REG_T ®){ + [[maybe_unused]] uint32_t val = reg; +} + +/**********************************/ +/*********** WO reg test **********/ +/**********************************/ + +template +void test_wo_reg(REG_T ®){ + reg = 1; +} + +#endif // !__REG_TEST_UTILS_H_ + + diff --git a/src/peakrdl_halcpp/test_generator/test_utils/test_utils.h b/src/peakrdl_halcpp/test_generator/test_utils/test_utils.h index 31984ae..3c580fe 100644 --- a/src/peakrdl_halcpp/test_generator/test_utils/test_utils.h +++ b/src/peakrdl_halcpp/test_generator/test_utils/test_utils.h @@ -1,6 +1,6 @@ -#include -#include -#include +#ifndef __TEST_UTILS_H_ +#define __TEST_UTILS_H_ +#include /****************************/ /** Compile time for loop ***/ @@ -16,205 +16,5 @@ constexpr void for_sequence(F f) { for_sequence(std::make_integer_sequence{}, f); } -/****************************/ -/***** Field Width test *****/ -/****************************/ - -void assert_field_width(uint32_t expected_width, - uint32_t actual_width, - const char *field_path){ - if(expected_width != actual_width){ - std::cerr << "Field width test error for " << field_path; - std::cerr << "\n\tExpected width: " << std::dec << expected_width; - std::cerr << "\n\tActual width: " << std::dec << actual_width << std::endl; - assert(expected_width == actual_width); - } -} - -template -void test_field_width(FIELD_T &field, uint32_t width, const char* field_path){ - assert_field_width(width, field.get_width(), field_path); -} - -/**********************************/ -/***** Field Base address test ****/ -/**********************************/ - -void assert_field_base_address(uint32_t expected_base_address, - uint32_t actual_base_address, - const char *field_path){ - if(expected_base_address != actual_base_address){ - std::cerr << "Field width test error for " << field_path; - std::cerr << "\n\tExpected width: " << std::dec << expected_base_address; - std::cerr << "\n\tActual width: " << std::dec << actual_base_address << std::endl; - assert(expected_base_address == actual_base_address); - } -} - -template -void test_field_base_address(FIELD_T &field, uint32_t base_address, const char* field_path){ - assert_field_width(base_address, field.get_abs_addr(), field_path); -} - -/**********************************/ -/*** Field start/end bit test *****/ -/**********************************/ - -void assert_field_start_end_bit(uint32_t expected_start_bit, - uint32_t actual_start_bit, - uint32_t expected_end_bit, - uint32_t actual_end_bit, - const char *field_path){ - if((expected_start_bit != actual_start_bit) or - (expected_end_bit != actual_end_bit)){ - std::cerr << "Field start/end test error for " << field_path; - std::cerr << "\n\tExpected start bit index: " << std::dec << expected_start_bit; - std::cerr << "\n\tActual start bit index: " << std::dec << actual_start_bit; - std::cerr << "\n\tExpected end bit index: " << std::dec << expected_end_bit; - std::cerr << "\n\tActual end bit index: " << std::dec << actual_end_bit << std::endl; - assert(expected_start_bit == actual_start_bit); - assert(expected_end_bit == actual_end_bit); - } -} - -template -void test_field_start_end_bit(FIELD_T &field, uint32_t start_bit, uint32_t end_bit, const char* field_path){ - uint32_t read_start_bit = field.get_start_bit(); - uint32_t read_end_bit = field.get_end_bit(); - assert_field_start_end_bit(start_bit, read_start_bit, end_bit, read_end_bit, field_path); -} - -/****************************/ -/****** RW Field test *******/ -/****************************/ - -void assert_rw(uint32_t exp, uint32_t rec, const char* field_path){ - if(exp != rec){ - std::cerr << "Field RW test error for " << field_path; - std::cerr << "\n\tWrote: 0x" << std::hex << exp; - std::cerr << "\n\tRead: 0x" << std::hex << rec << "\n"; - assert(exp == rec); - } -} - -template -void test_rw_field(uint32_t val, uint32_t exp, FIELD_T &field, const char* field_path){ - field = val; - - uint32_t result = field; - assert_rw(exp, result, field_path); -} - -template -void test_rw_field_marching_ones(FIELD_T &field, const char* field_path){ - uint32_t width = field.get_width(); - test_rw_field(0, 0, field, field_path); - for (int i = 0; i < width; i++) { - uint32_t write_val = 1 << i; - test_rw_field(write_val, write_val, field, field_path); - } -} - -template -void test_rw_field_overflow(FIELD_T &field, const char* field_path){ - uint32_t width = field.get_width(); - test_rw_field((1 << width), 0, field, field_path); -} - -void assert_rw_at(uint32_t bit_idx, uint32_t exp_bit_val, uint32_t exp_field_val, uint32_t actual_field_val, const char* field_path) { - uint32_t mask = 1u << bit_idx; - uint32_t rec_bit = (actual_field_val & mask) ? 1u : 0u; - if ((exp_bit_val != rec_bit) or - (exp_field_val != actual_field_val)) { - std::cerr << "Field RW at() accessor test error for " << field_path; - std::cerr << "\n\tBit index: " << std::dec << bit_idx; - std::cerr << "\n\tExpected: " << exp_bit_val; - std::cerr << "\n\tBut read: " << rec_bit; - std::cerr << "\n\tExpected field value: 0x" << std::hex << exp_field_val; - std::cerr << "\n\tActual field value: 0x" << std::hex << actual_field_val << "\n"; - assert(exp_bit_val == rec_bit); - assert(exp_field_val == actual_field_val); - } -} - -template -void test_rw_field_at_accessor(FIELD_T &field, const char* field_path){ - test_rw_field(0, 0, field, field_path); - uint32_t exp_val = 0; - for_sequence([&field, &exp_val, &field_path](auto i) { - field.template at() = 1; - exp_val = exp_val | (1 << i); - assert_rw_at(i, 1, exp_val, field.get(), field_path); - }); - - // Set the last bit of the field to 0, (currently all bits are 1) - field.template at<-1>() = 0; - exp_val = (1 << (WIDTH-1)) - 1; - assert_rw_at(WIDTH-1, 0, exp_val, field.get(), field_path); -} - -template -void test_rw_field(FIELD_T &field, - const char* field_path){ - test_rw_field_marching_ones(field, field_path); - test_rw_field_overflow(field, field_path); - test_rw_field_at_accessor(field, field_path); -} - - -/****************************/ -/****** RO Field test *******/ -/****************************/ - -template -void test_ro_field_at_accessor(FIELD_T &field, const char* field_path){ - for_sequence([&field, &field_path](auto i) { - volatile uint32_t val = field.template at(); - }); - volatile uint32_t val = field.template at<-1>().get(); -} - -template -void test_ro_field(FIELD_T &field, - const char* field_path){ - volatile uint32_t read = field; - test_ro_field_at_accessor(field, field_path); -} - -/****************************/ -/****** WO Field test *******/ -/****************************/ - -template -void test_wo_field_at_accessor(FIELD_T &field, const char* field_path){ - for_sequence([&field, &field_path](auto i) { - field.template at() = 1; - }); - field.template at<-1>() = 0; -} - -template -void test_wo_field(FIELD_T &field, - const char* field_path){ - field = 1; - test_wo_field_at_accessor(field, field_path); -} - -/****************************/ -/**** Generic Field test ****/ -/****************************/ - -template -void test_generic_field(FIELD_T &field, - uint32_t width, - size_t base_address, - uint32_t start_bit, - uint32_t end_bit, - const char* field_path){ - - test_field_width(field, width, field_path); - test_field_base_address(field, base_address, field_path); - test_field_start_end_bit(field, start_bit, end_bit, field_path); - -} +#endif // !__TEST_UTILS_H_ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..2d74b92 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.25) +project(halcpp_tests) + +include("./cmake/deps.cmake") +add_subdirectory("./rdl_lib/") + +include(CTest) +enable_testing() + +add_hal_test(addrmap_rw) +add_hal_test(addrmap_ro) +add_hal_test(addrmap_wo) +add_hal_test(addrmap_mixed_access) + +add_hal_test(addrmap_hier_rw) +add_hal_test(addrmap_hier_ro) +add_hal_test(addrmap_hier_wo) +add_hal_test(addrmap_hier_mixed) +add_hal_test(addrmap_hier_mixed2) + +add_hal_test(addrmap_rw_reg_1d_array) +add_hal_test(addrmap_rw_reg_2d_array) +add_hal_test(addrmap_rw_reg_4d_array) +add_hal_test(addrmap_hier_rw_reg_arrays) diff --git a/tests/cmake/add_hal_test.cmake b/tests/cmake/add_hal_test.cmake index 3f4f8c4..9a3905b 100644 --- a/tests/cmake/add_hal_test.cmake +++ b/tests/cmake/add_hal_test.cmake @@ -1,36 +1,26 @@ include_guard(GLOBAL) -macro(add_hal_test TEST_NAME) - cmake_parse_arguments(ARG "" "" "SYSTEMRDL;CPP" ${ARGN}) +function(add_hal_test IP_LIB) + cmake_parse_arguments(ARG "" "" "" ${ARGN}) if(ARG_UNPARSED_ARGUMENTS) message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION} passed unrecognized argument " "${ARG_UNPARSED_ARGUMENTS}") endif() - add_ip(${TEST_NAME}_ip) - - ip_sources(${IP} SYSTEMRDL - ${ARG_SYSTEMRDL} - ) - - peakrdl_halcpp(${IP}) - - add_executable(${TEST_NAME} - ${ARG_CPP} - ) - - target_include_directories(${TEST_NAME} PUBLIC - ${CMAKE_CURRENT_LIST_DIR} - ) - - target_link_libraries(${TEST_NAME} - ${IP} - GTest::gtest_main - test_utils - ) - - enable_testing() - include(GoogleTest) - gtest_discover_tests(${TEST_NAME}) - - -endmacro() + alias_dereference(IP_LIB ${IP_LIB}) + set(OUTDIR "${CMAKE_BINARY_DIR}/${IP_LIB}_halcpp") + + peakrdl_halcpp(${IP_LIB} GENERATE_TESTS + OUTDIR ${OUTDIR}) + + include(ExternalProject) + ExternalProject_Add(test_${IP_LIB}_hal + SOURCE_DIR ${OUTDIR}/tests + DOWNLOAD_COMMAND "" + TEST_COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure + INSTALL_COMMAND "" + DEPENDS ${IP_LIB}_halcpp + BUILD_ALWAYS TRUE + CMAKE_ARGS + -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} + ) +endfunction() diff --git a/tests/cmake/deps.cmake b/tests/cmake/deps.cmake index b058604..f614581 100644 --- a/tests/cmake/deps.cmake +++ b/tests/cmake/deps.cmake @@ -5,14 +5,4 @@ FetchContent_Declare(SoCMake GIT_TAG 40feb77) FetchContent_MakeAvailable(SoCMake) -if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") - FetchContent_Declare(googletest - URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip - ) - FetchContent_MakeAvailable(googletest) - add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/../test_utils/" "test_utils") - include("${CMAKE_CURRENT_LIST_DIR}/add_hal_test.cmake") -endif() - -include("${CMAKE_CURRENT_LIST_DIR}/fw_utils.cmake") - +include("${CMAKE_CURRENT_LIST_DIR}/add_hal_test.cmake") diff --git a/tests/cmake/fw_utils.cmake b/tests/cmake/fw_utils.cmake deleted file mode 100644 index 07facf3..0000000 --- a/tests/cmake/fw_utils.cmake +++ /dev/null @@ -1,19 +0,0 @@ - -function(firmware_size EXE) - message("EXECUTING ${CMAKE_SIZE} $ --format=sysv") - add_custom_command(TARGET ${EXE} POST_BUILD - COMMAND ${CMAKE_SIZE} $ --format=sysv - COMMENT "Printing size" - ) -endfunction() - - -function(firmware_disasemble EXE) - get_target_property(BINARY_DIR ${EXE} BINARY_DIR) - set(ASM_FILE "${BINARY_DIR}/${EXE}.asm") - add_custom_command(TARGET ${EXE} POST_BUILD - BYPRODUCTS ${ASM_FILE} - COMMAND ${CMAKE_OBJDUMP} -DgrwCS $ > ${ASM_FILE} - COMMENT "GEnerating disasembly" - ) -endfunction() diff --git a/tests/cmake/toolchains/riscv_toolchain.cmake b/tests/cmake/toolchains/riscv_toolchain.cmake deleted file mode 100644 index 88a2ecd..0000000 --- a/tests/cmake/toolchains/riscv_toolchain.cmake +++ /dev/null @@ -1,119 +0,0 @@ -set(FETCHCONTENT_BASE_DIR ${CMAKE_CURRENT_LIST_DIR}/../_deps) -include(FetchContent) -FetchContent_Declare(toolchain - URL "https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack/releases/download/v13.2.0-2/xpack-riscv-none-elf-gcc-13.2.0-2-linux-x64.tar.gz" -) -FetchContent_MakeAvailable(toolchain) -set(RISCV_GNU_PATH ${toolchain_SOURCE_DIR}) - -set(CMAKE_SYSTEM_PROCESSOR riscv) -set(CMAKE_SYSTEM_NAME Generic) -set(CMAKE_SYSTEM_ABI elf) - -set(TOOLCHAIN_PREFIX "${CMAKE_SYSTEM_PROCESSOR}-none-${CMAKE_SYSTEM_ABI}") - -find_program(RISCV_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc HINTS ${RISCV_GNU_PATH}/bin) -find_program(RISCV_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++ HINTS ${RISCV_GNU_PATH}/bin) -find_program(RISCV_AR ${TOOLCHAIN_PREFIX}-ar HINTS ${RISCV_GNU_PATH}/bin) -find_program(RISCV_ASM ${TOOLCHAIN_PREFIX}-as HINTS ${RISCV_GNU_PATH}/bin) -find_program(RISCV_LINKER ${TOOLCHAIN_PREFIX}-ld HINTS ${RISCV_GNU_PATH}/bin) -find_program(RISCV_OBJCOPY ${TOOLCHAIN_PREFIX}-objcopy HINTS ${RISCV_GNU_PATH}/bin) -find_program(RISCV_OBJDUMP ${TOOLCHAIN_PREFIX}-objdump HINTS ${RISCV_GNU_PATH}/bin) -find_program(RISCV_RANLIB ${TOOLCHAIN_PREFIX}-ranlib HINTS ${RISCV_GNU_PATH}/bin) -find_program(RISCV_SIZE ${TOOLCHAIN_PREFIX}-size HINTS ${RISCV_GNU_PATH}/bin) -find_program(RISCV_STRIP ${TOOLCHAIN_PREFIX}-strip HINTS ${RISCV_GNU_PATH}/bin) - -set(CMAKE_C_COMPILER ${RISCV_C_COMPILER}) -set(CMAKE_CXX_COMPILER ${RISCV_CXX_COMPILER}) -set(CMAKE_ASM ${RISCV_ASM}) -set(CMAKE_AR ${RISCV_AR}) -set(CMAKE_LINKER ${RISCV_LINKER}) -set(CMAKE_OBJCOPY ${RISCV_OBJCOPY}) -set(CMAKE_OBJDUMP ${RISCV_OBJDUMP}) -set(CMAKE_RANLIB ${RISCV_RANLIB}) -set(CMAKE_SIZE ${RISCV_SIZE}) -set(CMAKE_STRIP ${RISCV_STRIP}) - - -get_filename_component(RISCV_TOOLCHAIN_PATH ${RISCV_CXX_COMPILER} DIRECTORY CACHE) -set(RISCV_TOOLCHAIN_PREFIX "${TOOLCHAIN_PREFIX}-" CACHE STRING "") - -set(CMAKE_C_FLAGS "") -set(CMAKE_CXX_FLAGS "") -set(CMAKE_EXE_LINKER_FLAGS "") - -############################# -# Machine-Dependent Options # -############################# -# RV32 -# i : Integer -# m : Integer Multiplication and Division -# a : Atomic instructions -# c : Compressed instructions -# zicsr : CSR Instructions (explicitely required with latest specs) -string(APPEND CMAKE_C_FLAGS " -march=rv32imc") -# int and pointers are 32bit, long 64bit, char 8bit, short 16bit -string(APPEND CMAKE_C_FLAGS " -mabi=ilp32") - -string(APPEND CMAKE_ASM_FLAGS " -mabi=ilp32") -string(APPEND CMAKE_ASM_FLAGS " -march=rv32imc") - -################################ -# Options for Directory Search # -################################ -# Add the directory dir to the list of directories to be searched for header files during preprocessing -# string(APPEND CMAKE_C_FLAGS " -I${RISCV_GNU_PATH}/${TOOLCHAIN_PREFIX}/include/") - -##################################### -# Options that Control Optimization # -##################################### -# Place each function or data item into its own section in the output file -# if the target supports arbitrary sections. The name of the function or -# the name of the data item determines the section name in the output file. -# string(APPEND CMAKE_C_FLAGS " -ffunction-sections") -# string(APPEND CMAKE_C_FLAGS " -fdata-sections") - -# Optimize for size by default -string(APPEND CMAKE_C_FLAGS " -ffreestanding -nostdlib") - -# Pass common flags for c++ compilation flow -set(CMAKE_CXX_FLAGS ${CMAKE_C_FLAGS}) -set(CMAKE_ASM_FLAGS ${CMAKE_C_FLAGS}) - -####################### -# Options for Linking # -####################### -# Do not use the standard system startup -string(APPEND CMAKE_EXE_LINKER_FLAGS " -ffreestanding -nostdlib") -# Prevents linking with the shared libraries -string(APPEND CMAKE_EXE_LINKER_FLAGS " -static") - -# Print memory usage -string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,--print-memory-usage") -# Generate executable map file -string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-Map=map_file.map") - -########################################## -# Options Controlling the Kind of Output # -########################################## -# Use embedded class libnano_c -string(APPEND CMAKE_EXE_LINKER_FLAGS " -specs=nano.specs") - -################################ -# Options for Directory Search # -################################ -# Add directory dir to the list of directories to be searched for -l -# string(APPEND CMAKE_EXE_LINKER_FLAGS " -L${RISCV_GNU_PATH}/${TOOLCHAIN_PREFIX}/lib") - -# Search the library named library when linking. -# string(APPEND CMAKE_EXE_LINKER_FLAGS " -lc -lgcc -lm") - - -# set(CMAKE_C_FLAGS_DEBUG ) # TODO -# set(CMAKE_C_FLAGS_RELEASE ) -# set(CMAKE_CXX_FLAGS_DEBUG ${CXX_FLAGS}) -# set(CMAKE_CXX_FLAGS_RELEASE ${CXX_FLAGS}) - -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/tests/cross_compile/fixp/CMakeLists.txt b/tests/cross_compile/fixp/CMakeLists.txt deleted file mode 100644 index c343d26..0000000 --- a/tests/cross_compile/fixp/CMakeLists.txt +++ /dev/null @@ -1,38 +0,0 @@ -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -set(CMAKE_CXX_STANDARD 17) -cmake_minimum_required(VERSION 3.25) -project(test_cross_compile CXX) - -include("../../cmake/deps.cmake") - - -add_ip(${PROJECT_NAME}_ip) - -ip_sources(${IP} SYSTEMRDL - ../../simple/fixp.rdl -) - -peakrdl_halcpp(${IP}) - -add_executable(${PROJECT_NAME} - ./main.cpp -) - -target_include_directories(${PROJECT_NAME} PUBLIC - ${CMAKE_CURRENT_LIST_DIR} -) - -target_link_libraries(${PROJECT_NAME} - ${IP} - ) - -target_compile_options(${PROJECT_NAME} PUBLIC - -Os - -fno-delete-null-pointer-checks -) - -firmware_size(${PROJECT_NAME}) -firmware_disasemble(${PROJECT_NAME}) - - - diff --git a/tests/cross_compile/fixp/main.cpp b/tests/cross_compile/fixp/main.cpp deleted file mode 100644 index 41f4916..0000000 --- a/tests/cross_compile/fixp/main.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "fixp_simple_hal.h" - -int main () { - - FIXP_SIMPLE_HAL<0> soc; - - soc.r1.f1 = 4.8; - - float read_num = soc.r1.f1; - - return 0; -} - diff --git a/tests/cross_compile/simple/CMakeLists.txt b/tests/cross_compile/simple/CMakeLists.txt deleted file mode 100644 index 8ae8bea..0000000 --- a/tests/cross_compile/simple/CMakeLists.txt +++ /dev/null @@ -1,39 +0,0 @@ -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -set(CMAKE_CXX_STANDARD 17) -cmake_minimum_required(VERSION 3.25) -project(test_cross_compile CXX) - -include("../../cmake/deps.cmake") - - -add_ip(${PROJECT_NAME}_ip) - -ip_sources(${IP} SYSTEMRDL - ../../simple/simple.rdl -) - -peakrdl_halcpp(${IP}) - -add_executable(${PROJECT_NAME} - ./main.cpp -) - -target_include_directories(${PROJECT_NAME} PUBLIC - ${CMAKE_CURRENT_LIST_DIR} -) - -target_link_libraries(${PROJECT_NAME} - ${IP} - ) - -target_compile_options(${PROJECT_NAME} PUBLIC - -Os - -fno-delete-null-pointer-checks - -Wall -Wextra -Wpedantic -Werror -) - -firmware_size(${PROJECT_NAME}) -firmware_disasemble(${PROJECT_NAME}) - - - diff --git a/tests/cross_compile/simple/main.cpp b/tests/cross_compile/simple/main.cpp deleted file mode 100644 index a7f1f51..0000000 --- a/tests/cross_compile/simple/main.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "simple_hal.h" - -int main () { - - volatile SIMPLE_HAL<0> soc; - - int num = 4; - soc.r1.f1 = num; - - [[maybe_unused]] int read_num = soc.r1.f1; - // soc.r1 = 2; - // soc.r1.f2 = 10; - // - // for(volatile int i =0; i< 10; i++){ - // soc.r1 = i; - // } - - - return 0; -} - diff --git a/tests/rdl_lib/CMakeLists.txt b/tests/rdl_lib/CMakeLists.txt new file mode 100644 index 0000000..353fec9 --- /dev/null +++ b/tests/rdl_lib/CMakeLists.txt @@ -0,0 +1,45 @@ +add_ip(addrmap_rw) +ip_sources(${IP} SYSTEMRDL addrmap_rw.rdl) + +add_ip(addrmap_ro) +ip_sources(${IP} SYSTEMRDL addrmap_ro.rdl) + +add_ip(addrmap_wo) +ip_sources(${IP} SYSTEMRDL addrmap_wo.rdl) + +add_ip(addrmap_mixed_access) +ip_sources(${IP} SYSTEMRDL addrmap_mixed_access.rdl) + + +add_ip(addrmap_rw_reg_1d_array) +ip_sources(${IP} SYSTEMRDL ./addrmap_rw_reg_1d_array.rdl) + +add_ip(addrmap_rw_reg_2d_array) +ip_sources(${IP} SYSTEMRDL ./addrmap_rw_reg_2d_array.rdl) + +add_ip(addrmap_rw_reg_4d_array) +ip_sources(${IP} SYSTEMRDL ./addrmap_rw_reg_4d_array.rdl) + +add_ip(addrmap_hier_rw) +ip_sources(${IP} SYSTEMRDL ./addrmap_hier_rw.rdl) +ip_link(${IP} addrmap_rw) + +add_ip(addrmap_hier_ro) +ip_sources(${IP} SYSTEMRDL ./addrmap_hier_ro.rdl) +ip_link(${IP} addrmap_ro) + +add_ip(addrmap_hier_wo) +ip_sources(${IP} SYSTEMRDL ./addrmap_hier_wo.rdl) +ip_link(${IP} addrmap_wo) + +add_ip(addrmap_hier_mixed) +ip_sources(${IP} SYSTEMRDL ./addrmap_hier_mixed.rdl) +ip_link(${IP} addrmap_ro addrmap_wo addrmap_rw) + +add_ip(addrmap_hier_mixed2) +ip_sources(${IP} SYSTEMRDL ./addrmap_hier_mixed2.rdl) +ip_link(${IP} addrmap_ro addrmap_wo addrmap_rw addrmap_mixed_access) + +add_ip(addrmap_hier_rw_reg_arrays) +ip_sources(${IP} SYSTEMRDL ./addrmap_hier_rw_reg_arrays.rdl) +ip_link(${IP} addrmap_rw_reg_1d_array addrmap_rw_reg_2d_array addrmap_rw_reg_4d_array) diff --git a/tests/rdl_lib/addrmap_hier_mixed.rdl b/tests/rdl_lib/addrmap_hier_mixed.rdl new file mode 100644 index 0000000..8ee1e94 --- /dev/null +++ b/tests/rdl_lib/addrmap_hier_mixed.rdl @@ -0,0 +1,13 @@ +addrmap addrmap_hier_mixed{ + addrmap_rw rw_ip0; + addrmap_wo wo_ip0; + addrmap_ro ro_ip0; + + addrmap_rw rw_ip1; + addrmap_wo wo_ip1; + addrmap_ro ro_ip1; +}; + + + + diff --git a/tests/rdl_lib/addrmap_hier_mixed2.rdl b/tests/rdl_lib/addrmap_hier_mixed2.rdl new file mode 100644 index 0000000..619bb60 --- /dev/null +++ b/tests/rdl_lib/addrmap_hier_mixed2.rdl @@ -0,0 +1,11 @@ +addrmap addrmap_hier_mixed2{ + addrmap_rw rw_ip0; + addrmap_mixed_access mixed_ip0; + addrmap_wo wo_ip0; + addrmap_ro ro_ip0; + + addrmap_rw rw_ip1; + addrmap_mixed_access mixed_ip1; + addrmap_wo wo_ip1; + addrmap_ro ro_ip1; +}; diff --git a/tests/rdl_lib/addrmap_hier_ro.rdl b/tests/rdl_lib/addrmap_hier_ro.rdl new file mode 100644 index 0000000..783a8c2 --- /dev/null +++ b/tests/rdl_lib/addrmap_hier_ro.rdl @@ -0,0 +1,7 @@ +addrmap addrmap_hier_ro{ + addrmap_ro ro_ip0; + addrmap_ro ro_ip1; + addrmap_ro ro_ip2; +}; + + diff --git a/tests/rdl_lib/addrmap_hier_rw.rdl b/tests/rdl_lib/addrmap_hier_rw.rdl new file mode 100644 index 0000000..03217db --- /dev/null +++ b/tests/rdl_lib/addrmap_hier_rw.rdl @@ -0,0 +1,6 @@ +addrmap addrmap_hier_rw{ + addrmap_rw ip0; + addrmap_rw ip1; + addrmap_rw ip2; +}; + diff --git a/tests/rdl_lib/addrmap_hier_rw_reg_arrays.rdl b/tests/rdl_lib/addrmap_hier_rw_reg_arrays.rdl new file mode 100644 index 0000000..51cf675 --- /dev/null +++ b/tests/rdl_lib/addrmap_hier_rw_reg_arrays.rdl @@ -0,0 +1,5 @@ +addrmap addrmap_hier_rw_reg_arrays{ + addrmap_rw_reg_1d_array rw_1d_ip0; + addrmap_rw_reg_2d_array rw_2d_ip0; + addrmap_rw_reg_4d_array rw_4d_ip0; +}; diff --git a/tests/rdl_lib/addrmap_hier_wo.rdl b/tests/rdl_lib/addrmap_hier_wo.rdl new file mode 100644 index 0000000..7856128 --- /dev/null +++ b/tests/rdl_lib/addrmap_hier_wo.rdl @@ -0,0 +1,8 @@ +addrmap addrmap_hier_wo{ + addrmap_wo wo_ip0; + addrmap_wo wo_ip1; + addrmap_wo wo_ip2; +}; + + + diff --git a/tests/rdl_lib/addrmap_mixed_access.rdl b/tests/rdl_lib/addrmap_mixed_access.rdl new file mode 100644 index 0000000..27274c1 --- /dev/null +++ b/tests/rdl_lib/addrmap_mixed_access.rdl @@ -0,0 +1,11 @@ +addrmap addrmap_mixed_access { + reg { + field {sw=rw;} f1[3:0] = 0; + field {sw=r;} f2[7:4] = 0; + field {sw=w;} f3[11:8] = 0; + field {sw=rw;} f5[15:12] = 0; + field {sw=r;} f6[19:16] = 0; + field {sw=w;} f7[23:20] = 0; + } r1; +}; + diff --git a/tests/rdl_lib/addrmap_ro.rdl b/tests/rdl_lib/addrmap_ro.rdl new file mode 100644 index 0000000..bdcf922 --- /dev/null +++ b/tests/rdl_lib/addrmap_ro.rdl @@ -0,0 +1,11 @@ +addrmap addrmap_ro { + reg { + field {sw=r;} f1[4:0] = 0; + field {sw=r;} f2[8:5] = 0; + } r1; + + reg { + field {sw=r;} f5[15:0] = 0; + field {sw=r;} f6[31:16] = 0; + } r2; +}; diff --git a/tests/rdl_lib/addrmap_rw.rdl b/tests/rdl_lib/addrmap_rw.rdl new file mode 100644 index 0000000..0109f25 --- /dev/null +++ b/tests/rdl_lib/addrmap_rw.rdl @@ -0,0 +1,13 @@ +addrmap addrmap_rw { + reg { + field {} f1[1:0] = 0; + field {} f2[3:2] = 0; + field {} f3[4:4] = 0; + field {} f4[31:5] = 0; + } r1; + + reg { + field {} f5[15:0] = 0; + field {} f6[31:16] = 0; + } r2; +}; diff --git a/tests/rdl_lib/addrmap_rw_reg_1d_array.rdl b/tests/rdl_lib/addrmap_rw_reg_1d_array.rdl new file mode 100644 index 0000000..1d8e5a9 --- /dev/null +++ b/tests/rdl_lib/addrmap_rw_reg_1d_array.rdl @@ -0,0 +1,6 @@ +addrmap addrmap_rw_reg_1d_array { + reg { + field {sw=rw;} f1[4:0] = 0; + field {sw=rw;} f2[8:5] = 0; + } r1[5]; +}; diff --git a/tests/rdl_lib/addrmap_rw_reg_2d_array.rdl b/tests/rdl_lib/addrmap_rw_reg_2d_array.rdl new file mode 100644 index 0000000..374c6d5 --- /dev/null +++ b/tests/rdl_lib/addrmap_rw_reg_2d_array.rdl @@ -0,0 +1,7 @@ +addrmap addrmap_rw_reg_2d_array { + reg { + field {sw=rw;} f1[4:0] = 0; + field {sw=rw;} f2[8:5] = 0; + } r1[3][2]; +}; + diff --git a/tests/rdl_lib/addrmap_rw_reg_4d_array.rdl b/tests/rdl_lib/addrmap_rw_reg_4d_array.rdl new file mode 100644 index 0000000..e8840fa --- /dev/null +++ b/tests/rdl_lib/addrmap_rw_reg_4d_array.rdl @@ -0,0 +1,6 @@ +addrmap addrmap_rw_reg_4d_array { + reg { + field {sw=rw;} f1[4:0] = 0; + field {sw=rw;} f2[8:5] = 0; + } r1[2][2][1][2]; +}; diff --git a/tests/rdl_lib/addrmap_wo.rdl b/tests/rdl_lib/addrmap_wo.rdl new file mode 100644 index 0000000..a3e1eb7 --- /dev/null +++ b/tests/rdl_lib/addrmap_wo.rdl @@ -0,0 +1,12 @@ +addrmap addrmap_wo { + reg { + field {sw=w;} f1[4:0] = 0; + field {sw=w;} f2[7:5] = 0; + } r1; + + reg { + field {sw=w;} f5[15:0] = 0; + field {sw=w;} f6[31:16] = 0; + } r2; +}; + diff --git a/tests/simple/CMakeLists.txt b/tests/simple/CMakeLists.txt deleted file mode 100644 index be84924..0000000 --- a/tests/simple/CMakeLists.txt +++ /dev/null @@ -1,25 +0,0 @@ -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -set(CMAKE_CXX_STANDARD 17) -cmake_minimum_required(VERSION 3.25) -project(test_simple CXX) - -message("PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}") - -include("../cmake/deps.cmake") - -add_hal_test(test_simple - SYSTEMRDL ./simple.rdl - CPP ./test_simple.cpp - ) - -# add_hal_test(test_simple_ro -# SYSTEMRDL ./simple_ro.rdl -# CPP ./test_simple_ro.cpp -# ) - -add_hal_test(test_fixp - SYSTEMRDL ./fixp.rdl - CPP ./test_fixp.cpp - ) - - diff --git a/tests/simple/fixp.rdl b/tests/simple/fixp.rdl deleted file mode 100644 index 5278be0..0000000 --- a/tests/simple/fixp.rdl +++ /dev/null @@ -1,19 +0,0 @@ -property intwidth { - type = longint unsigned; - component = field; -}; - -property fracwidth { - type = longint unsigned; - component = field; -}; - -addrmap fixp_simple { - reg { - field { - intwidth=16; - fracwidth=16; - sw=rw; - } f1[31:0] = 0; - }r1 ; -}; diff --git a/tests/simple/simple.rdl b/tests/simple/simple.rdl deleted file mode 100644 index 9f7ee4f..0000000 --- a/tests/simple/simple.rdl +++ /dev/null @@ -1,24 +0,0 @@ -addrmap simple { - reg { - field { - } f1[1:0] = 0; - - field { - desc = "These bits select the transfer mode"; - } f2[3:2] = 0; - - field { - hw=rw; we; - } f3[4:4] = 0; - - field { - } f4[31:5] = 0; - - } r1; - - reg { - - field {} f5[15:0] = 0; - field {} f6[31:16] = 0; - } r2; -}; diff --git a/tests/simple/simple_ro.rdl b/tests/simple/simple_ro.rdl deleted file mode 100644 index b0b1283..0000000 --- a/tests/simple/simple_ro.rdl +++ /dev/null @@ -1,9 +0,0 @@ -addrmap simple_ro { - reg { - field { - sw=r; - hw=rw; - } f1[1:0] = 0; - } r1; -}; - diff --git a/tests/simple/test_fixp.cpp b/tests/simple/test_fixp.cpp deleted file mode 100644 index a1cfaff..0000000 --- a/tests/simple/test_fixp.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include -#include -#include "mock_io.h" -#include "fixp_simple_hal.h" -#include -#include "test_utils.h" - -inline constexpr FIXP_SIMPLE_HAL<0> hal{}; - -TEST(FixpTest, Basic) { - hal.r1.f1 = 15.6123; - - std::cout << "Float value: " << (float)hal.r1.f1 << "\n"; -} diff --git a/tests/simple/test_simple.cpp b/tests/simple/test_simple.cpp deleted file mode 100644 index 8b39042..0000000 --- a/tests/simple/test_simple.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include -#include -#include "mock_io.h" -#include "simple_hal.h" -#include -#include "test_utils.h" - -inline constexpr SIMPLE_HAL<0> hal{}; - -using HalFields = ::testing::Types< - FieldReference, - FieldReference, - FieldReference, - FieldReference ->; - -TYPED_TEST_SUITE(HalFieldTest, HalFields); - -TYPED_TEST(HalFieldTest, TestRWFields) { - auto& field = TestFixture::field(); - test_rw_field_range(field); - test_rw_field_overflow(field); -} diff --git a/tests/simple/test_simple_ro.cpp b/tests/simple/test_simple_ro.cpp deleted file mode 100644 index ca2271c..0000000 --- a/tests/simple/test_simple_ro.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include -#include -#include "mock_io.h" -#include "simple_ro_hal.h" -#include -#include "test_utils.h" - -inline constexpr SIMPLE_RO_HAL<0> hal{}; - -using HalFields = ::testing::Types< - FieldReference ->; - -TYPED_TEST_SUITE(HalFieldTest, HalFields); - -TYPED_TEST(HalFieldTest, TestRWFields) { - auto& field = TestFixture::field(); - test_rw_field_range(field); - test_rw_field_overflow(field); -} - diff --git a/tests/soc/CMakeLists.txt b/tests/soc/CMakeLists.txt deleted file mode 100644 index 427bfab..0000000 --- a/tests/soc/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -cmake_minimum_required(VERSION 3.25) -project(soc NONE) - -include("../cmake/deps.cmake") - -add_ip(soc) -ip_sources(${IP} SYSTEMRDL - ./soc.rdl -) - -add_subdirectory(ips/) - -ip_link(${IP} - gpio - i2c -) - -peakrdl_halcpp(${IP} GENERATE_TESTS) diff --git a/tests/soc/ips/CMakeLists.txt b/tests/soc/ips/CMakeLists.txt deleted file mode 100644 index cfa31a1..0000000 --- a/tests/soc/ips/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ - -add_subdirectory(gpio) -add_subdirectory(i2c) diff --git a/tests/soc/ips/gpio/CMakeLists.txt b/tests/soc/ips/gpio/CMakeLists.txt deleted file mode 100644 index 17f0b6d..0000000 --- a/tests/soc/ips/gpio/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -add_ip(gpio) -ip_sources(${IP} SYSTEMRDL - ./gpio.rdl -) diff --git a/tests/soc/ips/gpio/gpio.rdl b/tests/soc/ips/gpio/gpio.rdl deleted file mode 100644 index d044a1f..0000000 --- a/tests/soc/ips/gpio/gpio.rdl +++ /dev/null @@ -1,25 +0,0 @@ -addrmap gpio { - reg { - field { - } f1[1:0] = 0; - - field { - desc = "These bits select the transfer mode"; - } f2[3:2] = 0; - - field { - hw=rw; we; - } f3[4:4] = 0; - - field { - } f4[31:5] = 0; - - } r1; - - reg { - - field {} f5[15:0] = 0; - field {} f6[31:16] = 0; - } r2; -}; - diff --git a/tests/soc/ips/i2c/CMakeLists.txt b/tests/soc/ips/i2c/CMakeLists.txt deleted file mode 100644 index d42fce2..0000000 --- a/tests/soc/ips/i2c/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -add_ip(i2c) -ip_sources(${IP} SYSTEMRDL - ./i2c.rdl -) diff --git a/tests/soc/ips/i2c/i2c.rdl b/tests/soc/ips/i2c/i2c.rdl deleted file mode 100644 index 1e59150..0000000 --- a/tests/soc/ips/i2c/i2c.rdl +++ /dev/null @@ -1,28 +0,0 @@ -addrmap i2c { - reg { - field { - sw=w; - } f1[1:0] = 0; - - field { - desc = "These bits select the transfer mode"; - sw=r; - } f2[3:2] = 0; - - field { - hw=rw; we; - } f3[4:4] = 0; - - field { - } f4[16:5] = 0; - - } r1; - - reg { - - field {} f5[15:0] = 0; - field {} f6[31:16] = 0; - } r2; -}; - - diff --git a/tests/soc/soc.rdl b/tests/soc/soc.rdl deleted file mode 100644 index e1a6b6f..0000000 --- a/tests/soc/soc.rdl +++ /dev/null @@ -1,8 +0,0 @@ -addrmap soc{ - - gpio gpio_1; - gpio gpio_2; - gpio gpio_3; - gpio gpio_4; - i2c i2c_1; -}; diff --git a/tests/test_utils/CMakeLists.txt b/tests/test_utils/CMakeLists.txt deleted file mode 100644 index 5d25f54..0000000 --- a/tests/test_utils/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -add_library(test_utils INTERFACE) -target_include_directories(test_utils INTERFACE - ${CMAKE_CURRENT_LIST_DIR} -) diff --git a/tests/test_utils/mock_io.h b/tests/test_utils/mock_io.h deleted file mode 100644 index 59b585c..0000000 --- a/tests/test_utils/mock_io.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef _ARCH_IO_H_ -#define _ARCH_IO_H_ - -#include -#include -#include -#include - - - -class MockMemIO -{ -public: - static inline std::array mem; - - static inline uint32_t read32(uint32_t addr) { - // std::cout << "Read32 at addr 0x" << std::hex << addr << " Read data: 0x" << std::hex << MockMemIO::mem[addr] << std::endl; - return MockMemIO::mem[addr]; - } - - static inline void write32(uint32_t addr, uint32_t val) { - // std::cout << "Write32 at addr 0x" << std::hex << addr << " Write data: 0x" << std::hex << val << std::endl; - MockMemIO::mem[addr] = val; - } -}; - -class ArchIoNode : public MockMemIO -{ -public: -}; - -#endif // !_ARCH_IO_H_ - - diff --git a/tests/test_utils/test_utils.h b/tests/test_utils/test_utils.h deleted file mode 100644 index 1ca0a97..0000000 --- a/tests/test_utils/test_utils.h +++ /dev/null @@ -1,64 +0,0 @@ -#include - -template -struct FieldReference { - static auto& get() { return Field; } -}; - -template -class HalFieldTest : public ::testing::Test { -protected: - static auto& field() { return FieldT::get(); } -}; - -/***********************/ -/*** WO field tests ****/ -/***********************/ - -template -void test_w_field(uint32_t val, FIELD_T &field) { - field = val; -} - -template -void test_w_field_range(FIELD_T &field) { - int width = field.get_width(); - for (int i = 0; i < (1 << width); i++) { - test_w_field(i, i, field); - } -} - -/***********************/ -/*** RO field tests ****/ -/***********************/ - -template -void test_r_field(int32_t exp, FIELD_T &field) { - EXPECT_EQ((uint32_t)field, exp); -} - -/***********************/ -/*** RW field tests ****/ -/***********************/ - -template -void test_rw_field(uint32_t val, int32_t exp, FIELD_T &field) { - field = val; - EXPECT_EQ((uint32_t)field, exp); -} - -template -void test_rw_field_marching_ones(FIELD_T &field) { - test_rw_field(0, 0, field); - int width = field.get_width(); - for (int i = 0; i < (1 << width); i++) { - uint32_t write_val = 1 << i; - test_rw_field(write_val, write_val, field); - } -} - -template -void test_rw_field_overflow(FIELD_T &field) { - int width = field.get_width(); - test_rw_field((1 << width), 0, field); -} From 9c3c8e9f0db876822ba63e97065dbfc278fbc3ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Risto=20Peja=C5=A1inovi=C4=87?= Date: Sun, 14 Sep 2025 23:56:00 +0200 Subject: [PATCH 9/9] Add ci test Change SoCMake commit hash fix CI fix test add __init__.py Move test_generator to test_generator_data test Fix not included test_generator_data files fix try fix fix fix again --- .github/workflows/test.yml | 44 +++++++++++++++++++ MANIFEST.in | 2 + src/peakrdl_halcpp/test_generator.py | 2 +- .../test_generator_data/__init__.py | 0 .../test_utils/CMakeLists.txt | 0 .../test_utils/__init__.py | 0 .../test_utils/field_test_utils.h | 0 .../test_utils/mock_io.h | 0 .../test_utils/reg_test_utils.h | 0 .../test_utils/test_utils.h | 0 tests/cmake/deps.cmake | 2 +- 11 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/test.yml create mode 100644 src/peakrdl_halcpp/test_generator_data/__init__.py rename src/peakrdl_halcpp/{test_generator => test_generator_data}/test_utils/CMakeLists.txt (100%) create mode 100644 src/peakrdl_halcpp/test_generator_data/test_utils/__init__.py rename src/peakrdl_halcpp/{test_generator => test_generator_data}/test_utils/field_test_utils.h (100%) rename src/peakrdl_halcpp/{test_generator => test_generator_data}/test_utils/mock_io.h (100%) rename src/peakrdl_halcpp/{test_generator => test_generator_data}/test_utils/reg_test_utils.h (100%) rename src/peakrdl_halcpp/{test_generator => test_generator_data}/test_utils/test_utils.h (100%) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..aba4df8 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,44 @@ +name: PeakRDL-halcpp test + +on: + push: + branches: + - "*" +jobs: + build-and-test: + runs-on: ubuntu-latest + strategy: + matrix: + compiler: [ {cpp: g++, c: gcc}, {cpp: clang++, c: clang} ] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install dependencies + run: | + sudo apt-get remove --purge man-db -y + sudo apt-get update + sudo apt-get install -y \ + build-essential \ + cmake \ + make \ + clang \ + lld \ + gcc g++ \ + tree \ + python3 python3-pip + pip install . + + - name: Configure CMake + run: | + cmake -S tests -B build \ + -DCMAKE_C_COMPILER=${{ matrix.compiler.c }} \ + -DCMAKE_CXX_COMPILER=${{ matrix.compiler.cpp }} + + - name: Build + run: cmake --build build + + - name: Run tests + working-directory: build + run: ctest --output-on-failure diff --git a/MANIFEST.in b/MANIFEST.in index b930561..277fba7 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,4 @@ recursive-include src/peakrdl_halcpp/templates *.j2 recursive-include src/peakrdl_halcpp/include *.h +recursive-include src/peakrdl_halcpp/test_generator_data/test_utils *.h +recursive-include src/peakrdl_halcpp/test_generator_data/test_utils *.txt diff --git a/src/peakrdl_halcpp/test_generator.py b/src/peakrdl_halcpp/test_generator.py index e2ffa24..85069be 100644 --- a/src/peakrdl_halcpp/test_generator.py +++ b/src/peakrdl_halcpp/test_generator.py @@ -139,7 +139,7 @@ def process_template(self, def copy_test_directory(self, outdir: str): curr_dir: Path = Path(__file__).resolve().parent - src: Path = curr_dir / "test_generator" + src: Path = curr_dir / "test_generator_data" dst = Path(outdir) shutil.copytree(src, dst, dirs_exist_ok=True) diff --git a/src/peakrdl_halcpp/test_generator_data/__init__.py b/src/peakrdl_halcpp/test_generator_data/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/peakrdl_halcpp/test_generator/test_utils/CMakeLists.txt b/src/peakrdl_halcpp/test_generator_data/test_utils/CMakeLists.txt similarity index 100% rename from src/peakrdl_halcpp/test_generator/test_utils/CMakeLists.txt rename to src/peakrdl_halcpp/test_generator_data/test_utils/CMakeLists.txt diff --git a/src/peakrdl_halcpp/test_generator_data/test_utils/__init__.py b/src/peakrdl_halcpp/test_generator_data/test_utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/peakrdl_halcpp/test_generator/test_utils/field_test_utils.h b/src/peakrdl_halcpp/test_generator_data/test_utils/field_test_utils.h similarity index 100% rename from src/peakrdl_halcpp/test_generator/test_utils/field_test_utils.h rename to src/peakrdl_halcpp/test_generator_data/test_utils/field_test_utils.h diff --git a/src/peakrdl_halcpp/test_generator/test_utils/mock_io.h b/src/peakrdl_halcpp/test_generator_data/test_utils/mock_io.h similarity index 100% rename from src/peakrdl_halcpp/test_generator/test_utils/mock_io.h rename to src/peakrdl_halcpp/test_generator_data/test_utils/mock_io.h diff --git a/src/peakrdl_halcpp/test_generator/test_utils/reg_test_utils.h b/src/peakrdl_halcpp/test_generator_data/test_utils/reg_test_utils.h similarity index 100% rename from src/peakrdl_halcpp/test_generator/test_utils/reg_test_utils.h rename to src/peakrdl_halcpp/test_generator_data/test_utils/reg_test_utils.h diff --git a/src/peakrdl_halcpp/test_generator/test_utils/test_utils.h b/src/peakrdl_halcpp/test_generator_data/test_utils/test_utils.h similarity index 100% rename from src/peakrdl_halcpp/test_generator/test_utils/test_utils.h rename to src/peakrdl_halcpp/test_generator_data/test_utils/test_utils.h diff --git a/tests/cmake/deps.cmake b/tests/cmake/deps.cmake index f614581..8774c5d 100644 --- a/tests/cmake/deps.cmake +++ b/tests/cmake/deps.cmake @@ -2,7 +2,7 @@ set(FETCHCONTENT_BASE_DIR "${CMAKE_CURRENT_LIST_DIR}/_deps") include(FetchContent) FetchContent_Declare(SoCMake GIT_REPOSITORY "https://github.com/HEP-SoC/SoCMake.git" - GIT_TAG 40feb77) + GIT_TAG 2b88773d2f21) FetchContent_MakeAvailable(SoCMake) include("${CMAKE_CURRENT_LIST_DIR}/add_hal_test.cmake")