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/.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/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/__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/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: 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/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) 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/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 01646ad..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) }}" @@ -37,7 +33,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 +49,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 +77,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 +93,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 +114,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 +140,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/src/peakrdl_halcpp/templates/test.cpp.j2 b/src/peakrdl_halcpp/templates/test.cpp.j2 new file mode 100644 index 0000000..cd55854 --- /dev/null +++ b/src/peakrdl_halcpp/templates/test.cpp.j2 @@ -0,0 +1,37 @@ +#include "mock_io.h" +#include "{{ top.orig_type_name }}_hal.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 () { + +{% 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 %} + {% 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_ref }}, "{{ field.get_path() }}"); + {% elif field.is_sw_readable and not field.is_sw_writable %} + test_ro_field({{ field_ref }}); + {% elif not field.is_sw_readable and field.is_sw_writable %} + test_wo_field({{ field_ref }}); + {% 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..c2b5679 --- /dev/null +++ b/src/peakrdl_halcpp/templates/test_CMakeLists.txt.j2 @@ -0,0 +1,36 @@ +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 + ) + + target_compile_options(${TEST_NAME} PUBLIC + -fno-delete-null-pointer-checks + -Wall -Wextra -Wpedantic -Werror + ) + + 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..85069be --- /dev/null +++ b/src/peakrdl_halcpp/test_generator.py @@ -0,0 +1,146 @@ +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, Node, RegNode +from systemrdl.walker import RDLListener, RDLWalker, WalkerAction + + +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 + 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) + +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: + + 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, + "regs": field_listener.regs, + } + 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, + "resolve_path_array_refs": resolve_path_array_refs + }) + # 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_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_data/test_utils/CMakeLists.txt b/src/peakrdl_halcpp/test_generator_data/test_utils/CMakeLists.txt new file mode 100644 index 0000000..86c8ee8 --- /dev/null +++ b/src/peakrdl_halcpp/test_generator_data/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_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_data/test_utils/field_test_utils.h b/src/peakrdl_halcpp/test_generator_data/test_utils/field_test_utils.h new file mode 100644 index 0000000..2e16a31 --- /dev/null +++ b/src/peakrdl_halcpp/test_generator_data/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_data/test_utils/mock_io.h b/src/peakrdl_halcpp/test_generator_data/test_utils/mock_io.h new file mode 100644 index 0000000..f18d322 --- /dev/null +++ b/src/peakrdl_halcpp/test_generator_data/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_data/test_utils/reg_test_utils.h b/src/peakrdl_halcpp/test_generator_data/test_utils/reg_test_utils.h new file mode 100644 index 0000000..0fbcfb9 --- /dev/null +++ b/src/peakrdl_halcpp/test_generator_data/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_data/test_utils/test_utils.h b/src/peakrdl_halcpp/test_generator_data/test_utils/test_utils.h new file mode 100644 index 0000000..3c580fe --- /dev/null +++ b/src/peakrdl_halcpp/test_generator_data/test_utils/test_utils.h @@ -0,0 +1,20 @@ +#ifndef __TEST_UTILS_H_ +#define __TEST_UTILS_H_ +#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); +} + + +#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 new file mode 100644 index 0000000..9a3905b --- /dev/null +++ b/tests/cmake/add_hal_test.cmake @@ -0,0 +1,26 @@ +include_guard(GLOBAL) + +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() + + 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 new file mode 100644 index 0000000..8774c5d --- /dev/null +++ b/tests/cmake/deps.cmake @@ -0,0 +1,8 @@ +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 2b88773d2f21) +FetchContent_MakeAvailable(SoCMake) + +include("${CMAKE_CURRENT_LIST_DIR}/add_hal_test.cmake") 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; +}; +