diff --git a/Makefile.include b/Makefile.include index f29184448ed0..6bd14bd40d39 100644 --- a/Makefile.include +++ b/Makefile.include @@ -416,6 +416,10 @@ TOOLCHAINS_SUPPORTED ?= gnu # Import all toolchain settings include $(RIOTMAKE)/toolchain/$(TOOLCHAIN).inc.mk +# Append ldscript path after importing CPU and board makefiles to allow +# overriding the core ldscripts +LINKFLAGS += -L$(RIOTBASE)/core/ldscripts + # Tell ccache to pass the original file to the compiler, instead of passing the # preprocessed code. Without this setting, the compilation will fail with # -Wimplicit-fallthrough warnings even when the fall through case is properly diff --git a/boards/native/Makefile.include b/boards/native/Makefile.include index 9c08c7d8b91f..fa5e2ad18612 100644 --- a/boards/native/Makefile.include +++ b/boards/native/Makefile.include @@ -59,6 +59,9 @@ else LINKFLAGS += -ldl endif +# XFA (cross file array) support +LINKFLAGS += -T$(RIOTBASE)/cpu/native/ldscripts/xfa.ld + # clean up unused functions CFLAGS += -ffunction-sections -fdata-sections ifeq ($(OS),Darwin) diff --git a/core/include/xfa.h b/core/include/xfa.h new file mode 100644 index 000000000000..29a798a3ec7a --- /dev/null +++ b/core/include/xfa.h @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2016 Kaspar Schleiser + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup core_util + * @brief Cross File Arrays + * @{ + * + * This macro, in combination with an entry in the linker scripts, allows the + * definition of constant arrays to be spread over multiple C compilation + * units. These arrays are called "cross-file arrays" or short xfa. + * + * @experimental This API is considered experimental and will probably change + * without notice! + * + * @file + * @author Kaspar Schleiser + */ + +#ifndef XFA_H +#define XFA_H + +/* + * Unfortunately, current gcc trips over accessing XFA's because of their + * zero-size start/end array that are used of symbol markers, with an "array + * index out of bounds" warning. So until a solution for that is found, we + * need to disable array bounds checks for files using XFAs. + */ +#ifndef DOXYGEN +_Pragma("GCC diagnostic ignored \"-Warray-bounds\"") +#endif + +/** + * @brief helper macro for other XFA_* macros + * + * @internal + */ +#define _XFA(name, prio) __attribute__((used, section(".xfa." #name "." #prio))) + +/** + * @brief helper macro for other XFA_* macros + * + * @internal + */ +#define _XFA_CONST(name, \ + prio) __attribute__((used, \ + section(".roxfa." #name "." #prio))) + +/** + * @brief Define a read-only cross-file array + * + * This macro defines the symbols necessary to use XFA_START() and XFA_END(). + * It needs to be part of one single compilation unit. + * + * The pragmas prevent these errors: + * + * error: ISO C forbids empty initializer braces + * error: ISO C forbids zero-size array ‘xfatest_const_end’ + * + * @param[in] type name of the cross-file array + * @param[in] name name of the cross-file array + */ +#define XFA_INIT_CONST(type, name) \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wpedantic\"") \ + _XFA_CONST(name, 0_) const volatile type name [0] = {}; \ + _XFA_CONST(name, 9_) const volatile type name ## _end [0] = {}; \ + _Pragma("GCC diagnostic pop") \ + extern const unsigned __xfa_dummy + +/** + * @brief Define a writable cross-file array + * + * This macro defines the symbols necessary to use XFA_START() and XFA_END(). + * It needs to be part of one single compilation unit. + * + * The pragmas prevent these errors: + * + * error: ISO C forbids empty initializer braces + * error: ISO C forbids zero-size array ‘xfatest_end’ + * + * @param[in] type name of the cross-file array + * @param[in] name name of the cross-file array + */ +#define XFA_INIT(type, name) \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wpedantic\"") \ + _XFA(name, 0_) type name [0] = {}; \ + _XFA(name, 9_) type name ## _end [0] = {}; \ + _Pragma("GCC diagnostic pop") \ + extern const unsigned __xfa_dummy + +/** + * @brief Declare an external read-only cross-file array + * + * This macro defines the symbols necessary to use XFA_START() and XFA_END(). + * Think of this as XFA_INIT() but with "extern" keyword. + * It is supposed to be used in compilation units where the cross file array is + * being accessed, but not defined using XFA_INIT. + * + * @param[in] type name of the cross-file array + * @param[in] name name of the cross-file array + */ +#define XFA_USE_CONST(type, name) \ + extern const type name []; \ + extern const type name ## _end [] + +/** + * @brief Declare an external writable cross-file array + * + * This macro defines the symbols necessary to use XFA_START() and XFA_END(). + * Think of this as XFA_INIT() but with "extern" keyword. + * It is supposed to be used in compilation units where the cross file array is + * being accessed, but not defined using XFA_INIT. + * + * @param[in] type name of the cross-file array + * @param[in] name name of the cross-file array + */ +#define XFA_USE(type, name) \ + extern type name []; \ + extern type name ## _end [] + +/** + * @brief Define variable in writable cross-file array + * + * Variables will end up sorted by prio. + * + * Add this to the type in a variable definition, e.g.: + * + * XFA(driver_params, 0) driver_params_t _onboard = { .pin=42 }; + * + * @param[in] xfa_name name of the xfa + * @param[in] prio priority within the xfa + */ +#define XFA(xfa_name, prio) _XFA(xfa_name, 5_ ## prio) + +/** + * @brief Define variable in read-only cross-file array + * + * Variables will end up sorted by prio. + * + * Add this to the type in a variable definition, e.g.: + * + * XFA(driver_params, 0) driver_params_t _onboard = { .pin=42 }; + * + * @param[in] xfa_name name of the xfa + * @param[in] prio priority within the xfa + */ +#define XFA_CONST(xfa_name, prio) _XFA_CONST(xfa_name, 5_ ## prio) + +/** + * @brief Add a pointer to cross-file array + * + * Pointers will end up sorted by prio. + * + * @param[in] xfa_name name of the xfa + * @param[in] prio priority within the xfa + * @param[in] name symbol name + * @param[in] entry pointer variable to add to xfa + */ +#define XFA_ADD_PTR(xfa_name, prio, name, entry) \ + _XFA_CONST(xfa_name, 5_ ## prio) \ + const typeof(entry) xfa_name ## _ ## prio ## _ ## name = entry + +/** + * @brief Calculate number of entries in cross-file array + */ +#define XFA_LEN(type, \ + name) (((const char *)name ## _end - (const char *)name) / \ + sizeof(type)) + +#ifdef __cplusplus +extern "C" { +#endif +/* making externc happy */ +#ifdef __cplusplus +} +#endif + +#endif /* XFA_H */ +/** @} */ diff --git a/cpu/atmega_common/ldscripts/xfa.ld b/cpu/atmega_common/ldscripts/xfa.ld new file mode 100644 index 000000000000..29b85699c9a9 --- /dev/null +++ b/cpu/atmega_common/ldscripts/xfa.ld @@ -0,0 +1,16 @@ +SECTIONS +{ + .data : + { + /* Special case for AVR (Harvard architecture) where .rodata is merged + * into .data by the toolchain default ldscripts. */ + KEEP (*(SORT(.roxfa.*))) + KEEP (*(SORT(.xfa.*))) + } + __data_start = ADDR(.data); + __data_load_start = LOADADDR(.data); + __data_end = (__data_start + SIZEOF(.data)); + __data_load_end = (__data_load_start + SIZEOF(.data)); +} + +INSERT AFTER .text; diff --git a/cpu/cortexm_common/ldscripts/cortexm_base.ld b/cpu/cortexm_common/ldscripts/cortexm_base.ld index de0dc81fd1d8..01360513e818 100644 --- a/cpu/cortexm_common/ldscripts/cortexm_base.ld +++ b/cpu/cortexm_common/ldscripts/cortexm_base.ld @@ -55,6 +55,7 @@ SECTIONS *(.text .text.* .gnu.linkonce.t.*) *(.glue_7t) *(.glue_7) *(.rodata .rodata* .gnu.linkonce.r.*) + KEEP (*(SORT(.roxfa.*))) *(.ARM.extab* .gnu.linkonce.armextab.*) /* Support C constructors, and C destructors in both user code @@ -167,6 +168,7 @@ SECTIONS _srelocate = .; *(.ramfunc .ramfunc.*); *(.data .data.*); + KEEP (*(SORT(.xfa.*))) KEEP (*(.openocd .openocd.*)) . = ALIGN(4); _erelocate = .; diff --git a/cpu/esp32/ld/esp32.common.ld b/cpu/esp32/ld/esp32.common.ld index 5d78197af266..277a31896efd 100644 --- a/cpu/esp32/ld/esp32.common.ld +++ b/cpu/esp32/ld/esp32.common.ld @@ -158,6 +158,7 @@ SECTIONS _data_start = ABSOLUTE(.); *(.data) *(.data.*) + KEEP (*(SORT(.xfa.*))) *(.gnu.linkonce.d.*) *(.data1) *(.sdata) @@ -243,6 +244,7 @@ SECTIONS _rodata_start = ABSOLUTE(.); *(.rodata) *(.rodata.*) + KEEP (*(SORT(.roxfa.*))) *(.irom1.text) /* catch stray ICACHE_RODATA_ATTR */ *(.gnu.linkonce.r.*) *(.rodata1) diff --git a/cpu/esp8266/Makefile.include b/cpu/esp8266/Makefile.include index e8c0b5719c51..4c8a32152e70 100644 --- a/cpu/esp8266/Makefile.include +++ b/cpu/esp8266/Makefile.include @@ -61,9 +61,10 @@ endif ARCHIVES += -lgcc -lwpa -lcore -lnet80211 -lphy -lpp -lstdc++ -LINKFLAGS += -T$(RIOTCPU)/$(CPU)/ld/esp8266.rom.ld -LINKFLAGS += -T$(RIOTCPU)/$(CPU)/ld/esp8266.riot-os.ld -LINKFLAGS += -T$(RIOTCPU)/$(CPU)/ld/esp8266.peripherals.ld +LINKFLAGS += -L$(RIOTCPU)/$(CPU)/ld +LINKFLAGS += -Tesp8266.rom.ld +LINKFLAGS += -Tesp8266.riot-os.ld +LINKFLAGS += -Tesp8266.peripherals.ld LINKFLAGS += -Wl,-wrap=pp_attach LINKFLAGS += -Wl,-wrap=pm_attach diff --git a/cpu/esp8266/ld/esp8266.riot-os.ld b/cpu/esp8266/ld/esp8266.riot-os.ld index 56ad0bac075f..e99aa855fe95 100644 --- a/cpu/esp8266/ld/esp8266.riot-os.ld +++ b/cpu/esp8266/ld/esp8266.riot-os.ld @@ -105,6 +105,7 @@ SECTIONS *(.sdata2.*) *(.gnu.linkonce.s2.*) *(.jcr) + KEEP (*(SORT(.xfa.*))) _data_end = ABSOLUTE(.); } >dram0_0_seg :dram0_0_phdr @@ -139,6 +140,7 @@ SECTIONS KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) KEEP (*(SORT(.dtors.*))) KEEP (*(.dtors)) + KEEP (*(SORT(.roxfa.*))) /* C++ exception handlers table: */ __XT_EXCEPTION_DESCS__ = ABSOLUTE(.); *(.xt_except_desc) diff --git a/cpu/lpc23xx/ldscripts/lpc23xx.ld b/cpu/lpc23xx/ldscripts/lpc23xx.ld index 48ef75e87c77..53156e7c8f83 100644 --- a/cpu/lpc23xx/ldscripts/lpc23xx.ld +++ b/cpu/lpc23xx/ldscripts/lpc23xx.ld @@ -65,6 +65,7 @@ SECTIONS KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) KEEP (*(SORT(.dtors.*))) KEEP (*crtend.o(.dtors)) + KEEP (*(SORT(.roxfa.*))) . = ALIGN(4); _efixed = .; /* End of text section */ @@ -163,6 +164,7 @@ SECTIONS *(.ramfunc .ramfunc.*); *(.data .data.*); KEEP (*(.openocd .openocd.*)) + KEEP (*(SORT(.xfa.*))) . = ALIGN(4); _erelocate = .; } > ram AT> rom diff --git a/cpu/mips_pic32_common/Makefile.include b/cpu/mips_pic32_common/Makefile.include index 421ce149958b..0b1918fd9ba6 100644 --- a/cpu/mips_pic32_common/Makefile.include +++ b/cpu/mips_pic32_common/Makefile.include @@ -6,7 +6,6 @@ include $(RIOTCPU)/mips32r2_common/Makefile.include CFLAGS += -D_SYS__PTHREADTYPES_H_ CFLAGS += -DCPU_FAM_$(call uppercase_and_underscore,$(CPU_FAM)) - +LINKFLAGS += -L$(RIOTCPU)/mips_pic32_common/ldscripts INCLUDES += -I$(RIOTCPU)/mips_pic32_common/include - DIRS += $(RIOTCPU)/$(CPU)/$(CPU_MODEL) diff --git a/cpu/mips_pic32_common/ldscripts/xfa.ld b/cpu/mips_pic32_common/ldscripts/xfa.ld new file mode 100644 index 000000000000..ef5e17a9a41b --- /dev/null +++ b/cpu/mips_pic32_common/ldscripts/xfa.ld @@ -0,0 +1,21 @@ +SECTIONS +{ + .data : + { + KEEP (*(SORT(.xfa.*))) + } + _fdata = ADDR(.data); + _edata = (_fdata + SIZEOF(.data)); +} + +INSERT BEFORE .sbss; + +SECTIONS +{ + .rodata : + { + KEEP (*(SORT(.roxfa.*))) + } +} + +INSERT AFTER .dtors; diff --git a/cpu/msp430_common/ldscripts/xfa.ld b/cpu/msp430_common/ldscripts/xfa.ld new file mode 100644 index 000000000000..7b115a66c172 --- /dev/null +++ b/cpu/msp430_common/ldscripts/xfa.ld @@ -0,0 +1,24 @@ +SECTIONS +{ + .rodata : + { + KEEP (*(SORT(.roxfa.*))) + } > ROM +} + +INSERT AFTER .rodata; + +SECTIONS +{ + .data : + { + KEEP (*(SORT(.xfa.*))) + } > RAM AT> ROM + + . = ALIGN(2); + _edata = .; + PROVIDE (edata = .); + PROVIDE (__dataend = .); +} + +INSERT AFTER .data; diff --git a/cpu/native/ldscripts/xfa.ld b/cpu/native/ldscripts/xfa.ld new file mode 100644 index 000000000000..d2b7d1d6dcf8 --- /dev/null +++ b/cpu/native/ldscripts/xfa.ld @@ -0,0 +1,19 @@ +SECTIONS +{ + .data : + { + KEEP (*(SORT(.xfa.*))) + } +} + +INSERT AFTER .text; + +SECTIONS +{ + .rodata : + { + KEEP (*(SORT(.roxfa.*))) + } +} + +INSERT AFTER .text; diff --git a/cpu/riscv_common/ldscripts/riscv_base.ld b/cpu/riscv_common/ldscripts/riscv_base.ld index 44b62ac8551e..906b2b16e8bc 100644 --- a/cpu/riscv_common/ldscripts/riscv_base.ld +++ b/cpu/riscv_common/ldscripts/riscv_base.ld @@ -61,6 +61,7 @@ SECTIONS *(.rdata) *(.rodata .rodata.*) *(.gnu.linkonce.r.*) + KEEP (*(SORT(.roxfa.*))) } >flash AT>flash :flash . = ALIGN(4); @@ -182,6 +183,7 @@ SECTIONS *(.srodata.cst4) *(.srodata.cst2) *(.srodata .srodata.*) + KEEP (*(SORT(.xfa.*))) } >ram AT>flash :ram_init . = ALIGN(4); diff --git a/makefiles/arch/atmega.inc.mk b/makefiles/arch/atmega.inc.mk index 2c2169323d9b..98c7d83c2513 100644 --- a/makefiles/arch/atmega.inc.mk +++ b/makefiles/arch/atmega.inc.mk @@ -9,8 +9,11 @@ CFLAGS_OPT ?= -Os CFLAGS += $(CFLAGS_CPU) $(CFLAGS_LINK) $(CFLAGS_DBG) $(CFLAGS_OPT) ASFLAGS += $(CFLAGS_CPU) $(CFLAGS_DBG) + +# needed for xfa support. Order is important. +LINKFLAGS += -T$(RIOTCPU)/atmega_common/ldscripts/xfa.ld + LINKFLAGS += $(CFLAGS_CPU) $(CFLAGS_DBG) $(CFLAGS_OPT) -static -lgcc -e reset_handler -Wl,--gc-sections -OFLAGS += -j .text -j .data # Use ROM_LEN and RAM_LEN during link $(if $(ROM_LEN),,$(error ROM_LEN is not defined)) diff --git a/makefiles/arch/mips.inc.mk b/makefiles/arch/mips.inc.mk index fc92ab78a318..93f3258a5e2a 100644 --- a/makefiles/arch/mips.inc.mk +++ b/makefiles/arch/mips.inc.mk @@ -64,6 +64,9 @@ LINKFLAGS += -L$(RIOTCPU)/$(CPU)/ldscripts LINKFLAGS += $(CFLAGS_CPU) $(CFLAGS_DBG) $(CFLAGS_OPT) LINKFLAGS += -Wl,--gc-sections +# XFA support +LINKFLAGS += -T$(RIOTCPU)/mips_pic32_common/ldscripts/xfa.ld + OPTIONAL_CFLAGS_BLACKLIST += -Wformat-overflow OPTIONAL_CFLAGS_BLACKLIST += -Wformat-truncation OPTIONAL_CFLAGS_BLACKLIST += -gz diff --git a/makefiles/arch/msp430.inc.mk b/makefiles/arch/msp430.inc.mk index 011e2778eaa7..801d155274c8 100644 --- a/makefiles/arch/msp430.inc.mk +++ b/makefiles/arch/msp430.inc.mk @@ -15,6 +15,8 @@ ASFLAGS += $(CFLAGS_CPU) --defsym $(CPU_MODEL)=1 $(CFLAGS_DBG) LINKFLAGS += $(CFLAGS_CPU) $(CFLAGS_DBG) $(CFLAGS_OPT) LINKFLAGS += -Wl,--gc-sections -Wl,-L$(MSP430_SUPPORT_FILES)/include +LINKFLAGS += -T $(MSP430_SUPPORT_FILES)/include/$(CPU_MODEL).ld +LINKFLAGS += $(RIOTCPU)/msp430_common/ldscripts/xfa.ld OPTIONAL_CFLAGS_BLACKLIST += -fdiagnostics-color OPTIONAL_CFLAGS_BLACKLIST += -Wformat-overflow diff --git a/tests/unittests/tests-core/tests-core-xfa-data1.c b/tests/unittests/tests-core/tests-core-xfa-data1.c new file mode 100644 index 000000000000..af8af9c07828 --- /dev/null +++ b/tests/unittests/tests-core/tests-core-xfa-data1.c @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2018 Eistec AB + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @addtogroup unittests + * @{ + * + * @file + * @brief Data elements for the core/xfa unit test + * + * @author Joakim Nohlgård + */ +#include "xfa.h" +#include "tests-core-xfa.h" + +XFA(xfatest, 0) xfatest_t _xfatest1 = { .val = 12345, .text = "xfatest1" }; +XFA_CONST(xfatest_const, 0) xfatest_t _xfatest_const1 = { .val = 0xcafe, .text = "xfatest_const1" }; + +XFA_INIT(xfatest_t, xfatest_use); +XFA_INIT_CONST(xfatest_t, xfatest_use_const); + +XFA(xfatest_use, 0) xfatest_t _xfatest_use1 = { .val = 3333, .text = "xfatest_use1" }; +XFA(xfatest_use, 0) xfatest_t _xfatest_use_again = { .val = 555, .text = "xfatest use again" }; +XFA_CONST(xfatest_use_const, 0) xfatest_t _xfatest_use_const1 = { .val = 4444, .text = "xfatest_use_const1" }; + +int hack1; +/** @} */ diff --git a/tests/unittests/tests-core/tests-core-xfa-data2.c b/tests/unittests/tests-core/tests-core-xfa-data2.c new file mode 100644 index 000000000000..01bb08ad1c82 --- /dev/null +++ b/tests/unittests/tests-core/tests-core-xfa-data2.c @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2018 Eistec AB + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @addtogroup unittests + * @{ + * + * @file + * @brief Data elements for the core/xfa unit test + * + * @author Joakim Nohlgård + */ +#include "xfa.h" +#include "tests-core-xfa.h" + +XFA(xfatest, 0) xfatest_t _xfatest2 = { .val = 0xbeef, .text = "another test string" }; +XFA_CONST(xfatest_const, 0) xfatest_t _xfatest_const2 = { .val = 32444, .text = "const string xfa 2" }; +XFA(xfatest_use, 0) xfatest_t _xfatest_use2 = { .val = 11111, .text = "xfatest_use2" }; +XFA_CONST(xfatest_use_const, 0) xfatest_t _xfatest_use_const2 = { .val = 22222, .text = "xfatest_use_const2" }; + +/** @} */ diff --git a/tests/unittests/tests-core/tests-core-xfa.c b/tests/unittests/tests-core/tests-core-xfa.c new file mode 100644 index 000000000000..1b0deb4d7b5a --- /dev/null +++ b/tests/unittests/tests-core/tests-core-xfa.c @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2018 Eistec AB + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +#include +#include "bitarithm.h" +#include "xfa.h" + +#include "embUnit.h" + +#include "tests-core.h" +#include "tests-core-xfa.h" + +XFA_INIT(xfatest_t, xfatest); +XFA_INIT_CONST(xfatest_t, xfatest_const); +XFA_USE(xfatest_t, xfatest_use); +XFA_USE_CONST(xfatest_t, xfatest_use_const); + +/* Verifying that cross file array linking is correct by iterating over an external array */ +static void test_xfa_data(void) +{ + unsigned n = XFA_LEN(xfatest_t, xfatest); + TEST_ASSERT_EQUAL_INT(2, n); + unsigned found = 0; + for (unsigned k = 0; k < n; ++k) { + /* we do not want to enforce the order of the data elements */ + switch (xfatest[k].val) { + case 12345: + /* tests-core-xfa-data1.c */ + TEST_ASSERT_EQUAL_STRING("xfatest1", xfatest[k].text); + TEST_ASSERT(!(found & BIT0)); + found |= BIT0; + break; + case 0xbeef: + /* tests-core-xfa-data2.c */ + TEST_ASSERT_EQUAL_STRING("another test string", xfatest[k].text); + TEST_ASSERT(!(found & BIT1)); + found |= BIT1; + break; + default: + break; + } + } + TEST_ASSERT_EQUAL_INT((1U << n) - 1, found); +} + +static void test_xfa_const_data(void) +{ + unsigned n = XFA_LEN(xfatest_t, xfatest_const); + TEST_ASSERT_EQUAL_INT(2, n); + unsigned found = 0; + for (unsigned k = 0; k < n; ++k) { + /* we do not want to enforce the order of the data elements */ + switch (xfatest_const[k].val) { + case 0xcafe: + /* tests-core-xfa-data1.c */ + TEST_ASSERT_EQUAL_STRING("xfatest_const1", xfatest_const[k].text); + ++found; + break; + case 32444: + /* tests-core-xfa-data2.c */ + TEST_ASSERT_EQUAL_STRING("const string xfa 2", xfatest_const[k].text); + ++found; + break; + default: + break; + } + } + TEST_ASSERT_EQUAL_INT(n, found); +} + +static void test_xfa_use_data(void) +{ + unsigned n = XFA_LEN(xfatest_t, xfatest_use); + TEST_ASSERT_EQUAL_INT(3, n); + unsigned found = 0; + for (unsigned k = 0; k < n; ++k) { + /* we do not want to enforce the order of the data elements */ + switch (xfatest_use[k].val) { + case 3333: + /* tests-core-xfa-data1.c */ + TEST_ASSERT_EQUAL_STRING("xfatest_use1", xfatest_use[k].text); + ++found; + break; + case 555: + /* tests-core-xfa-data1.c */ + TEST_ASSERT_EQUAL_STRING("xfatest use again", xfatest_use[k].text); + ++found; + break; + case 11111: + /* tests-core-xfa-data2.c */ + TEST_ASSERT_EQUAL_STRING("xfatest_use2", xfatest_use[k].text); + ++found; + break; + default: + break; + } + } + TEST_ASSERT_EQUAL_INT(n, found); +} + +static void test_xfa_use_const_data(void) +{ + unsigned n = XFA_LEN(xfatest_t, xfatest_use_const); + TEST_ASSERT_EQUAL_INT(2, n); + unsigned found = 0; + for (unsigned k = 0; k < n; ++k) { + /* we do not want to enforce the order of the data elements */ + switch (xfatest_use_const[k].val) { + case 4444: + /* tests-core-xfa-data1.c */ + TEST_ASSERT_EQUAL_STRING("xfatest_use_const1", xfatest_use_const[k].text); + ++found; + break; + case 22222: + /* tests-core-xfa-data2.c */ + TEST_ASSERT_EQUAL_STRING("xfatest_use_const2", xfatest_use_const[k].text); + ++found; + break; + default: + break; + } + } + TEST_ASSERT_EQUAL_INT(n, found); +} + +Test *tests_core_xfa_tests(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(test_xfa_data), + new_TestFixture(test_xfa_const_data), + new_TestFixture(test_xfa_use_data), + new_TestFixture(test_xfa_use_const_data), + }; + + EMB_UNIT_TESTCALLER(core_xfa_tests, NULL, NULL, + fixtures); + + return (Test *)&core_xfa_tests; +} diff --git a/tests/unittests/tests-core/tests-core-xfa.h b/tests/unittests/tests-core/tests-core-xfa.h new file mode 100644 index 000000000000..9efd5267f6c3 --- /dev/null +++ b/tests/unittests/tests-core/tests-core-xfa.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2018 Eistec AB + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @addtogroup unittests + * @{ + * + * @file + * @brief Declarations for the core/xfa unit test + * + * @author Joakim Nohlgård + */ +#ifndef TESTS_CORE_XFA_H +#define TESTS_CORE_XFA_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + unsigned val; + const char *text; +} xfatest_t; + +#ifdef __cplusplus +} +#endif + +#endif /* TESTS_CORE_XFA_H */ + +/** @} */ diff --git a/tests/unittests/tests-core/tests-core.c b/tests/unittests/tests-core/tests-core.c index 3551286e5d6e..fe7dfe2af539 100644 --- a/tests/unittests/tests-core/tests-core.c +++ b/tests/unittests/tests-core/tests-core.c @@ -19,4 +19,5 @@ void tests_core(void) TESTS_RUN(tests_core_priority_queue_tests()); TESTS_RUN(tests_core_byteorder_tests()); TESTS_RUN(tests_core_ringbuffer_tests()); + TESTS_RUN(tests_core_xfa_tests()); } diff --git a/tests/unittests/tests-core/tests-core.h b/tests/unittests/tests-core/tests-core.h index a22a58e4d8b1..294e888fe0d4 100644 --- a/tests/unittests/tests-core/tests-core.h +++ b/tests/unittests/tests-core/tests-core.h @@ -92,6 +92,13 @@ Test *tests_core_byteorder_tests(void); */ Test *tests_core_ringbuffer_tests(void); +/** + * @brief Generates tests for xfa.h + * + * @return embUnit tests if successful, NULL if not. + */ +Test *tests_core_xfa_tests(void); + #ifdef __cplusplus } #endif diff --git a/tests/xfa/Makefile b/tests/xfa/Makefile new file mode 100644 index 000000000000..a7e027850621 --- /dev/null +++ b/tests/xfa/Makefile @@ -0,0 +1,12 @@ +include ../Makefile.tests_common + +include $(RIOTBASE)/Makefile.include + +all: static-test +static-test: $(ELFFILE) + $(Q)TEST_STARTADDR=$$($(OBJDUMP) -t $< | grep -E '\sxfatest_const$$' | awk '{ printf "0x%s", $$1}'); \ + TEST_ENDADDR=$$($(OBJDUMP) -t $< | grep -E '\sxfatest_const_end$$' | awk '{ printf "0x%s", $$1}'); \ + if test ! $$((TEST_STARTADDR)) -lt $$((TEST_ENDADDR)); then \ + echo "Error: Static check of XFA linked const array failed, verify linker flags and try again" >&2; \ + exit 1; \ + fi diff --git a/tests/xfa/main.c b/tests/xfa/main.c new file mode 100644 index 000000000000..1bd38ce3810b --- /dev/null +++ b/tests/xfa/main.c @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2015 Kaspar Schleiser + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup test + * @{ + * + * @file + * @brief cross file array (XFA) test application + * + * @author Kaspar Schleiser + * + * @} + */ + +#include +#include + +#include "xfa.h" + +#include "xfatest.h" + +XFA_INIT(xfatest_t, xfatest); +XFA_INIT_CONST(xfatest_t, xfatest_const); + +int main(void) +{ + puts("Cross file array test"); + + unsigned n = XFA_LEN(xfatest_t, xfatest); + printf("xfatest[%u]:\n", n); + for (unsigned i = 0; i < n; i++) { + printf("[%u] = %u, \"%s\"\n", i, xfatest[i].val, xfatest[i].text); + } + n = XFA_LEN(xfatest_t, xfatest_const); + printf("xfatest_const[%u]:\n", n); + for (unsigned i = 0; i < n; i++) { + printf("[%u] = %u, \"%s\"\n", i, xfatest_const[i].val, xfatest_const[i].text); + } + + return 0; +} diff --git a/tests/xfa/tests/01-run.py b/tests/xfa/tests/01-run.py new file mode 100755 index 000000000000..88a6425ca4f3 --- /dev/null +++ b/tests/xfa/tests/01-run.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2021 Freie Universität Berlin +# 2021 Inria +# 2021 Kaspar Schleiser +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. + +import sys +from testrunner import run + + +def testfunc(child): + child.expect_exact('Cross file array test') + child.expect_exact('xfatest[2]:') + child.expect_exact('[0] = 1, "xfatest1"') + child.expect_exact('[1] = 2, "xfatest2"') + child.expect_exact('xfatest_const[2]:') + child.expect_exact('[0] = 123, "xfatest_const1"') + child.expect_exact('[1] = 45, "xfatest_const2"') + + +if __name__ == "__main__": + sys.exit(run(testfunc)) diff --git a/tests/xfa/xfatest.h b/tests/xfa/xfatest.h new file mode 100644 index 000000000000..1645123c0e36 --- /dev/null +++ b/tests/xfa/xfatest.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2016 Kaspar Schleiser + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +#ifndef XFATEST_H +#define XFATEST_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef DOXYGEN /* just a test header, please ignore */ + +typedef struct { + unsigned val; + const char *text; +} xfatest_t; + +#endif /* DOXYGEN */ + +#ifdef __cplusplus +} +#endif + +#endif /* XFATEST_H */ diff --git a/tests/xfa/xfatest1.c b/tests/xfa/xfatest1.c new file mode 100644 index 000000000000..ace77dc27284 --- /dev/null +++ b/tests/xfa/xfatest1.c @@ -0,0 +1,5 @@ +#include "xfa.h" +#include "xfatest.h" + +XFA(xfatest, 0) xfatest_t _xfatest1 = { .val = 1, .text = "xfatest1" }; +XFA_CONST(xfatest_const, 0) xfatest_t _xfatest_const1 = { .val = 123, .text = "xfatest_const1" }; diff --git a/tests/xfa/xfatest2.c b/tests/xfa/xfatest2.c new file mode 100644 index 000000000000..ebf6db651c5e --- /dev/null +++ b/tests/xfa/xfatest2.c @@ -0,0 +1,5 @@ +#include "xfa.h" +#include "xfatest.h" + +XFA(xfatest, 0) xfatest_t _xfatest2 = { .val = 2, .text = "xfatest2" }; +XFA_CONST(xfatest_const, 0) xfatest_t _xfatest_const2 = { .val = 45, .text = "xfatest_const2" };