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/3] 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/3] 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/3] 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