From 0cfca5f29a5b633d9dd91b3b8d66ffb943d5975f Mon Sep 17 00:00:00 2001 From: gfs Date: Tue, 25 Aug 2026 07:32:38 +0000 Subject: [PATCH 1/2] build: reuse embedded-infra-lib's QEMU/Cortex-M runtime instead of local platform/qemu embedded-infra-lib now ships its own hal.cortex_m/hal.qemu.* startup, fault-tracer, semihosting, and linker-script support with a working CMAKE_CROSSCOMPILING_EMULATOR-based QEMU test harness. Drop this repo's hand-rolled platform/qemu bootstrap and cmake/QemuHelpers.cmake qemu-system-arm invocation in favor of linking against EMIL's libraries directly, matching EMIL's own self-tested hal.qemu.test configuration. As a side effect this fixes a real bug: the old mps2.ld never defined .init_array/.preinit_array/.fini_array, so C++ global constructors (including every TEST_F's GoogleTest self-registration) never ran on QEMU targets -- RUN_ALL_TESTS() always iterated an empty registry and trivially reported success. The qemu-cortex-m4/m7 CI jobs now actually execute the test suites on real Cortex-M float hardware for the first time. Co-Authored-By: Claude Sonnet 5 --- .mega-linter.yml | 1 - CMakeLists.txt | 15 +- CMakePresets.json | 12 +- cmake/QemuHelpers.cmake | 22 +- numerical/analysis/test/CMakeLists.txt | 2 +- .../analysis/windowing/test/CMakeLists.txt | 2 +- .../control_analysis/test/CMakeLists.txt | 2 +- .../implementations/test/CMakeLists.txt | 2 +- .../estimators/offline/test/CMakeLists.txt | 2 +- .../estimators/online/test/CMakeLists.txt | 2 +- numerical/filters/active/test/CMakeLists.txt | 2 +- numerical/filters/passive/test/CMakeLists.txt | 2 +- numerical/math/test/CMakeLists.txt | 2 +- .../nonlinear_control/test/CMakeLists.txt | 2 +- numerical/optimization/test/CMakeLists.txt | 2 +- numerical/regularization/test/CMakeLists.txt | 2 +- numerical/robust_control/test/CMakeLists.txt | 2 +- numerical/solvers/test/CMakeLists.txt | 2 +- platform/qemu/CMakeLists.txt | 18 -- platform/qemu/DefaultInit.cpp | 86 -------- platform/qemu/FaultHandler.cpp | 196 ------------------ platform/qemu/FaultHandler.hpp | 9 - platform/qemu/Startup.cpp | 145 ------------- platform/qemu/mps2.ld | 57 ----- 24 files changed, 35 insertions(+), 554 deletions(-) delete mode 100644 platform/qemu/CMakeLists.txt delete mode 100644 platform/qemu/DefaultInit.cpp delete mode 100644 platform/qemu/FaultHandler.cpp delete mode 100644 platform/qemu/FaultHandler.hpp delete mode 100644 platform/qemu/Startup.cpp delete mode 100644 platform/qemu/mps2.ld diff --git a/.mega-linter.yml b/.mega-linter.yml index 2626a0eb..50216e19 100644 --- a/.mega-linter.yml +++ b/.mega-linter.yml @@ -26,4 +26,3 @@ SARIF_REPORTER: true SHOW_SKIPPED_LINTERS: false JSON_JSONLINT_ARGUMENTS: --mode cjson CPP_CPPCHECK_ARGUMENTS: --suppress=normalCheckLevelMaxBranches --library=qt -CPP_CPPCHECK_FILTER_REGEX_EXCLUDE: platform/qemu diff --git a/CMakeLists.txt b/CMakeLists.txt index 49287eb7..fe3d8895 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,7 +38,7 @@ if (NUMERICAL_TOOLBOX_STANDALONE) FetchContent_Declare( emil GIT_REPOSITORY https://github.com/embedded-pro/embedded-infra-lib.git - GIT_TAG 2848dbf163a6ebf159e29f7f4f73b96f7773d20d # Unreleased + GIT_TAG b1056e80ab5376d7e4ee821c5b3c5680bc01f751 # Unreleased ) set(EMIL_INCLUDE_MBEDTLS Off CACHE BOOL "" FORCE) @@ -47,16 +47,7 @@ if (NUMERICAL_TOOLBOX_STANDALONE) set(EMIL_BUILD_ECHO_COMPILERS Off CACHE BOOL "" FORCE) set(EMIL_ENABLE_DOCKER_TOOLS Off CACHE BOOL "" FORCE) - if(DEFINED QEMU_MACHINE) - set(gtest_disable_pthreads ON CACHE BOOL "" FORCE) - endif() - FetchContent_MakeAvailable(emil) - - if(DEFINED QEMU_MACHINE) - function(emil_add_test target) - endfunction() - endif() endif() project(numerical-toolbox LANGUAGES C CXX ASM VERSION 3.1.1) # x-release-please-version @@ -97,10 +88,6 @@ endif() include(QemuHelpers) -if(DEFINED QEMU_MACHINE) - add_subdirectory(platform/qemu) -endif() - add_subdirectory(numerical) if (NUMERICAL_TOOLBOX_BUILD_SIMULATOR) diff --git a/CMakePresets.json b/CMakePresets.json index 74aa6918..953a9772 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -67,7 +67,10 @@ "CMAKE_CONFIGURATION_TYPES": "RelWithDebInfo", "NUMERICAL_TOOLBOX_BUILD_TESTS": "On", "NUMERICAL_TOOLBOX_BUILD_SIMULATOR": "Off", - "QEMU_MACHINE": "mps2-an386", + "EMIL_BUILD_QEMU": "On", + "EMIL_BUILD_CORTEX_M": "On", + "TARGET_CORTEX": "m4", + "CMAKE_CROSSCOMPILING_EMULATOR": "qemu-system-arm;-M;mps2-an386;-nographic;-semihosting-config;enable=on,target=native;-kernel", "CMAKE_CXX_FLAGS": "-DGTEST_HAS_STREAM_REDIRECTION=0 -DGTEST_HAS_POSIX_RE=0 -D_POSIX_C_SOURCE=200112L" } }, @@ -81,7 +84,10 @@ "CMAKE_CONFIGURATION_TYPES": "RelWithDebInfo", "NUMERICAL_TOOLBOX_BUILD_TESTS": "On", "NUMERICAL_TOOLBOX_BUILD_SIMULATOR": "Off", - "QEMU_MACHINE": "mps2-an500", + "EMIL_BUILD_QEMU": "On", + "EMIL_BUILD_CORTEX_M": "On", + "TARGET_CORTEX": "m7", + "CMAKE_CROSSCOMPILING_EMULATOR": "qemu-system-arm;-M;mps2-an500;-nographic;-semihosting-config;enable=on,target=native;-kernel", "CMAKE_CXX_FLAGS": "-DGTEST_HAS_STREAM_REDIRECTION=0 -DGTEST_HAS_POSIX_RE=0 -D_POSIX_C_SOURCE=200112L" } } @@ -126,6 +132,7 @@ "configuration": "RelWithDebInfo", "configurePreset": "qemu-cortex-m4", "targets": [ + "hal.qemu.test", "numerical.analysis_test", "numerical.analysis.windowing_test", "numerical.control_analysis_test", @@ -147,6 +154,7 @@ "configuration": "RelWithDebInfo", "configurePreset": "qemu-cortex-m7", "targets": [ + "hal.qemu.test", "numerical.analysis_test", "numerical.analysis.windowing_test", "numerical.control_analysis_test", diff --git a/cmake/QemuHelpers.cmake b/cmake/QemuHelpers.cmake index b7dac9b1..84480bcb 100644 --- a/cmake/QemuHelpers.cmake +++ b/cmake/QemuHelpers.cmake @@ -1,16 +1,14 @@ -function(numerical_add_qemu_test target) - if(NOT DEFINED QEMU_MACHINE) +function(numerical_link_qemu_runtime target) + if(NOT EMIL_BUILD_QEMU) return() endif() - target_link_libraries(${target} PRIVATE platform_qemu_startup) - target_link_libraries(${target} PRIVATE -lrdimon) - add_test( - NAME qemu.${target} - COMMAND qemu-system-arm - -machine ${QEMU_MACHINE} - -nographic - -semihosting-config enable=on,target=native - -kernel $ + target_link_libraries(${target} PRIVATE + hal.cortex_m + hal.cortex_m.runtime + hal.qemu.syscalls + hal.qemu.default_init + hal.qemu.sync + hal.qemu.cortex + gmock_main ) - set_tests_properties(qemu.${target} PROPERTIES TIMEOUT 120) endfunction() diff --git a/numerical/analysis/test/CMakeLists.txt b/numerical/analysis/test/CMakeLists.txt index a6c44b52..dd845ba0 100644 --- a/numerical/analysis/test/CMakeLists.txt +++ b/numerical/analysis/test/CMakeLists.txt @@ -8,7 +8,7 @@ target_link_libraries(numerical.analysis_test PUBLIC numerical.math_test_helper ) -numerical_add_qemu_test(numerical.analysis_test) +numerical_link_qemu_runtime(numerical.analysis_test) target_sources(numerical.analysis_test PRIVATE TestConvolutionCorrelation.cpp diff --git a/numerical/analysis/windowing/test/CMakeLists.txt b/numerical/analysis/windowing/test/CMakeLists.txt index cbcfcc46..9efc3102 100644 --- a/numerical/analysis/windowing/test/CMakeLists.txt +++ b/numerical/analysis/windowing/test/CMakeLists.txt @@ -7,7 +7,7 @@ target_link_libraries(numerical.analysis.windowing_test PUBLIC numerical.analysis.windowing ) -numerical_add_qemu_test(numerical.analysis.windowing_test) +numerical_link_qemu_runtime(numerical.analysis.windowing_test) target_sources(numerical.analysis.windowing_test PRIVATE TestWindowing.cpp diff --git a/numerical/control_analysis/test/CMakeLists.txt b/numerical/control_analysis/test/CMakeLists.txt index 2d44330f..c723f638 100644 --- a/numerical/control_analysis/test/CMakeLists.txt +++ b/numerical/control_analysis/test/CMakeLists.txt @@ -8,7 +8,7 @@ target_link_libraries(numerical.control_analysis_test PUBLIC numerical.math_test_helper ) -numerical_add_qemu_test(numerical.control_analysis_test) +numerical_link_qemu_runtime(numerical.control_analysis_test) target_sources(numerical.control_analysis_test PRIVATE TestContinuousToDiscrete.cpp diff --git a/numerical/controllers/implementations/test/CMakeLists.txt b/numerical/controllers/implementations/test/CMakeLists.txt index f4e201df..da40cfa2 100644 --- a/numerical/controllers/implementations/test/CMakeLists.txt +++ b/numerical/controllers/implementations/test/CMakeLists.txt @@ -8,7 +8,7 @@ target_link_libraries(numerical.controllers_test PUBLIC numerical.math_test_helper ) -numerical_add_qemu_test(numerical.controllers_test) +numerical_link_qemu_runtime(numerical.controllers_test) target_sources(numerical.controllers_test PRIVATE TestBangBangHysteresis.cpp diff --git a/numerical/estimators/offline/test/CMakeLists.txt b/numerical/estimators/offline/test/CMakeLists.txt index 69bf6b36..6b9ca5b6 100644 --- a/numerical/estimators/offline/test/CMakeLists.txt +++ b/numerical/estimators/offline/test/CMakeLists.txt @@ -7,7 +7,7 @@ target_link_libraries(numerical.estimators.offline_test PUBLIC numerical.estimators.offline ) -numerical_add_qemu_test(numerical.estimators.offline_test) +numerical_link_qemu_runtime(numerical.estimators.offline_test) target_sources(numerical.estimators.offline_test PRIVATE TestExpectationMaximization.cpp diff --git a/numerical/estimators/online/test/CMakeLists.txt b/numerical/estimators/online/test/CMakeLists.txt index 950ae3d1..bf0178a0 100644 --- a/numerical/estimators/online/test/CMakeLists.txt +++ b/numerical/estimators/online/test/CMakeLists.txt @@ -7,7 +7,7 @@ target_link_libraries(numerical.estimators.online_test PUBLIC numerical.estimators.online ) -numerical_add_qemu_test(numerical.estimators.online_test) +numerical_link_qemu_runtime(numerical.estimators.online_test) target_sources(numerical.estimators.online_test PRIVATE TestRecursiveLeastSquares.cpp diff --git a/numerical/filters/active/test/CMakeLists.txt b/numerical/filters/active/test/CMakeLists.txt index 42cb9607..81a31298 100644 --- a/numerical/filters/active/test/CMakeLists.txt +++ b/numerical/filters/active/test/CMakeLists.txt @@ -8,7 +8,7 @@ target_link_libraries(numerical.filters.active_test PUBLIC numerical.math_test_helper ) -numerical_add_qemu_test(numerical.filters.active_test) +numerical_link_qemu_runtime(numerical.filters.active_test) target_sources(numerical.filters.active_test PRIVATE TestAhrsMadgwickMahony.cpp diff --git a/numerical/filters/passive/test/CMakeLists.txt b/numerical/filters/passive/test/CMakeLists.txt index 4e394ea2..9bec1a2e 100644 --- a/numerical/filters/passive/test/CMakeLists.txt +++ b/numerical/filters/passive/test/CMakeLists.txt @@ -9,7 +9,7 @@ target_link_libraries(numerical.filters.passive_test PUBLIC numerical.controllers_test_helper ) -numerical_add_qemu_test(numerical.filters.passive_test) +numerical_link_qemu_runtime(numerical.filters.passive_test) target_sources(numerical.filters.passive_test PRIVATE TestBiquadCascade.cpp diff --git a/numerical/math/test/CMakeLists.txt b/numerical/math/test/CMakeLists.txt index c68a8310..0f0e2a8e 100644 --- a/numerical/math/test/CMakeLists.txt +++ b/numerical/math/test/CMakeLists.txt @@ -8,7 +8,7 @@ target_link_libraries(numerical.math_test PUBLIC numerical.math_test_helper ) -numerical_add_qemu_test(numerical.math_test) +numerical_link_qemu_runtime(numerical.math_test) target_sources(numerical.math_test PRIVATE TestCholeskyDecomposition.cpp diff --git a/numerical/nonlinear_control/test/CMakeLists.txt b/numerical/nonlinear_control/test/CMakeLists.txt index 65092576..3dee20f7 100644 --- a/numerical/nonlinear_control/test/CMakeLists.txt +++ b/numerical/nonlinear_control/test/CMakeLists.txt @@ -7,7 +7,7 @@ target_link_libraries(numerical.nonlinear_control_test PUBLIC numerical.nonlinear_control ) -numerical_add_qemu_test(numerical.nonlinear_control_test) +numerical_link_qemu_runtime(numerical.nonlinear_control_test) target_sources(numerical.nonlinear_control_test PRIVATE TestBacksteppingControl.cpp diff --git a/numerical/optimization/test/CMakeLists.txt b/numerical/optimization/test/CMakeLists.txt index f3bf6002..895273de 100644 --- a/numerical/optimization/test/CMakeLists.txt +++ b/numerical/optimization/test/CMakeLists.txt @@ -7,7 +7,7 @@ target_link_libraries(numerical.optimization_test PUBLIC numerical.optimization ) -numerical_add_qemu_test(numerical.optimization_test) +numerical_link_qemu_runtime(numerical.optimization_test) target_sources(numerical.optimization_test PRIVATE TestBayesianOptimization.cpp diff --git a/numerical/regularization/test/CMakeLists.txt b/numerical/regularization/test/CMakeLists.txt index 44fb4520..458cff58 100644 --- a/numerical/regularization/test/CMakeLists.txt +++ b/numerical/regularization/test/CMakeLists.txt @@ -7,7 +7,7 @@ target_link_libraries(numerical.regularization_test PUBLIC numerical.regularization ) -numerical_add_qemu_test(numerical.regularization_test) +numerical_link_qemu_runtime(numerical.regularization_test) target_sources(numerical.regularization_test PRIVATE TestL1.cpp diff --git a/numerical/robust_control/test/CMakeLists.txt b/numerical/robust_control/test/CMakeLists.txt index 2793d97d..577d5040 100644 --- a/numerical/robust_control/test/CMakeLists.txt +++ b/numerical/robust_control/test/CMakeLists.txt @@ -7,7 +7,7 @@ target_link_libraries(numerical.robust_control_test PUBLIC numerical.robust_control ) -numerical_add_qemu_test(numerical.robust_control_test) +numerical_link_qemu_runtime(numerical.robust_control_test) target_sources(numerical.robust_control_test PRIVATE TestActiveDisturbanceRejection.cpp diff --git a/numerical/solvers/test/CMakeLists.txt b/numerical/solvers/test/CMakeLists.txt index ded6cd26..05041974 100644 --- a/numerical/solvers/test/CMakeLists.txt +++ b/numerical/solvers/test/CMakeLists.txt @@ -7,7 +7,7 @@ target_link_libraries(numerical.solvers_test PUBLIC numerical.solver ) -numerical_add_qemu_test(numerical.solvers_test) +numerical_link_qemu_runtime(numerical.solvers_test) target_sources(numerical.solvers_test PRIVATE TestConditionNumber.cpp diff --git a/platform/qemu/CMakeLists.txt b/platform/qemu/CMakeLists.txt deleted file mode 100644 index 389460ec..00000000 --- a/platform/qemu/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -add_library(platform_qemu_startup STATIC - FaultHandler.cpp - Startup.cpp - DefaultInit.cpp -) - -set_target_properties(platform_qemu_startup PROPERTIES COMPILE_WARNING_AS_ERROR Off) - -target_include_directories(platform_qemu_startup PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR} -) - -target_link_options(platform_qemu_startup INTERFACE - -T ${CMAKE_CURRENT_SOURCE_DIR}/mps2.ld - -Wl,-u,Reset_Handler - -Wl,-u,FaultDispatch - -Wl,-u,main -) diff --git a/platform/qemu/DefaultInit.cpp b/platform/qemu/DefaultInit.cpp deleted file mode 100644 index e103d098..00000000 --- a/platform/qemu/DefaultInit.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#include -#include -#include -#include -#include - -extern char _end; -extern char _heap_end; - -extern "C" -{ - caddr_t _sbrk(int incr) - { - static char* heap = &_end; - char* prev = heap; - incr = (incr + 3) & ~3; - if (heap + incr > &_heap_end) - { - errno = ENOMEM; - return reinterpret_cast(-1); - } - heap += incr; - return reinterpret_cast(prev); - } - - [[gnu::weak]] void Default_Handler_Forwarded() - { - _exit(1); - } - - void abort() - { - _exit(1); - } - - int _fstat(int, struct stat* st) - { - st->st_mode = S_IFCHR; - return 0; - } - - void _init() - {} - - void HardwareInitialization() - {} - - int _write(int, const char* ptr, int len) - { - struct - { - int handle; - const void* ptr; - unsigned len; - } block = { 1, ptr, static_cast(len) }; - - int result; - __asm volatile( - "mov r0, #5\n" - "mov r1, %[b]\n" - "bkpt #0xAB\n" - "mov %[r], r0\n" - : [r] "=r"(result) - : [b] "r"(&block) - : "r0", "r1", "memory"); - return len - result; - } - - char* getcwd(char* buf, size_t size) - { - if (buf && size > 0) - buf[0] = '\0'; - return buf; - } - - int mkdir(const char*, mode_t) - { - errno = ENOSYS; - return -1; - } - - int swprintf(wchar_t*, size_t, const wchar_t*, ...) - { - return -1; - } -} diff --git a/platform/qemu/FaultHandler.cpp b/platform/qemu/FaultHandler.cpp deleted file mode 100644 index 82d745a1..00000000 --- a/platform/qemu/FaultHandler.cpp +++ /dev/null @@ -1,196 +0,0 @@ -#include "FaultHandler.hpp" -#include - -extern "C" [[noreturn]] void _exit(int); - -namespace -{ - constexpr uint32_t kCfsr = 0xE000ED28u; - constexpr uint32_t kHfsr = 0xE000ED2Cu; - constexpr uint32_t kMmfar = 0xE000ED34u; - constexpr uint32_t kBfar = 0xE000ED38u; - constexpr uint32_t kFlashEnd = 0x00400000u; - - uint32_t ScbReg(uint32_t addr) - { - return *reinterpret_cast(addr); - } - - bool IsCodeAddr(uint32_t v) - { - return (v & 1u) != 0u && (v & ~1u) < kFlashEnd; - } -} - -extern "C" void FaultDispatch(uint32_t* sp, uint32_t exLr); - -extern "C" __attribute__((naked, noreturn)) void HardFault_Handler() -{ - __asm volatile( - "tst lr, #4\n" - "ite eq\n" - "mrseq r0, msp\n" - "mrsne r0, psp\n" - "mov r1, lr\n" - "b FaultDispatch\n"); -} - -extern "C" __attribute__((naked, noreturn)) void MemManage_Handler() -{ - __asm volatile( - "tst lr, #4\n" - "ite eq\n" - "mrseq r0, msp\n" - "mrsne r0, psp\n" - "mov r1, lr\n" - "b FaultDispatch\n"); -} - -extern "C" __attribute__((naked, noreturn)) void BusFault_Handler() -{ - __asm volatile( - "tst lr, #4\n" - "ite eq\n" - "mrseq r0, msp\n" - "mrsne r0, psp\n" - "mov r1, lr\n" - "b FaultDispatch\n"); -} - -extern "C" __attribute__((naked, noreturn)) void UsageFault_Handler() -{ - __asm volatile( - "tst lr, #4\n" - "ite eq\n" - "mrseq r0, msp\n" - "mrsne r0, psp\n" - "mov r1, lr\n" - "b FaultDispatch\n"); -} - -extern "C" void FaultDispatch(uint32_t* sp, uint32_t exLr) -{ - FaultHandler::Dispatch(sp, exLr); -} - -void FaultHandler::Dispatch(uint32_t* sp, uint32_t exLr) -{ - const uint32_t r0 = sp[0]; - const uint32_t r1 = sp[1]; - const uint32_t r2 = sp[2]; - const uint32_t r3 = sp[3]; - const uint32_t r12 = sp[4]; - const uint32_t lrFault = sp[5]; - const uint32_t pc = sp[6]; - const uint32_t xpsr = sp[7]; - - const uint32_t hfsr = ScbReg(kHfsr); - const uint32_t cfsr = ScbReg(kCfsr); - const uint32_t mmfar = ScbReg(kMmfar); - const uint32_t bfar = ScbReg(kBfar); - - const uint32_t ipsr = xpsr & 0x1FFu; - const uint32_t mmfsr = cfsr & 0xFFu; - const uint32_t bfsr = (cfsr >> 8) & 0xFFu; - const uint32_t ufsr = cfsr >> 16; - - const char* faultName; - switch (ipsr) - { - case 3: - faultName = "HardFault"; - break; - case 4: - faultName = "MemManage"; - break; - case 5: - faultName = "BusFault"; - break; - case 6: - faultName = "UsageFault"; - break; - default: - faultName = "Unknown"; - break; - } - - printf("\n\n=== FAULT: %s ===\n", faultName); - - printf("PC 0x%08x LR 0x%08x SP 0x%08x\n", - (unsigned)pc, - (unsigned)lrFault, - (unsigned)(uintptr_t)sp); - printf("xPSR 0x%08x EXC_RETURN 0x%08x\n", - (unsigned)xpsr, - (unsigned)exLr); - printf("R0 0x%08x R1 0x%08x R2 0x%08x\n", - (unsigned)r0, (unsigned)r1, (unsigned)r2); - printf("R3 0x%08x R12 0x%08x\n", - (unsigned)r3, (unsigned)r12); - - printf("\nHFSR 0x%08x:", (unsigned)hfsr); - if (hfsr & (1u << 1)) - printf(" VECTTBL"); - if (hfsr & (1u << 30)) - printf(" FORCED"); - if (hfsr & (1u << 31)) - printf(" DEBUGEVT"); - printf("\n"); - - printf("CFSR 0x%08x:", (unsigned)cfsr); - if (mmfsr & (1u << 0)) - printf(" IACCVIOL"); - if (mmfsr & (1u << 1)) - printf(" DACCVIOL"); - if (mmfsr & (1u << 3)) - printf(" MUNSTKERR"); - if (mmfsr & (1u << 4)) - printf(" MSTKERR"); - if (mmfsr & (1u << 5)) - printf(" MLSPERR"); - if (mmfsr & (1u << 7)) - printf(" MMFAR=0x%08x", (unsigned)mmfar); - if (bfsr & (1u << 0)) - printf(" IBUSERR"); - if (bfsr & (1u << 1)) - printf(" PRECISERR"); - if (bfsr & (1u << 2)) - printf(" IMPRECISERR"); - if (bfsr & (1u << 3)) - printf(" UNSTKERR"); - if (bfsr & (1u << 4)) - printf(" STKERR"); - if (bfsr & (1u << 5)) - printf(" LSPERR"); - if (bfsr & (1u << 7)) - printf(" BFAR=0x%08x", (unsigned)bfar); - if (ufsr & (1u << 0)) - printf(" UNDEFINSTR"); - if (ufsr & (1u << 1)) - printf(" INVSTATE"); - if (ufsr & (1u << 2)) - printf(" INVPC"); - if (ufsr & (1u << 3)) - printf(" NOCP"); - if (ufsr & (1u << 4)) - printf(" STKOF"); - if (ufsr & (1u << 8)) - printf(" UNALIGNED"); - if (ufsr & (1u << 9)) - printf(" DIVBYZERO"); - printf("\n"); - - const bool extendedFrame = (exLr & (1u << 4)) == 0u; - const uint32_t* traceStart = sp + 8u + (extendedFrame ? 18u : 0u); - const uint32_t* traceEnd = traceStart + 64u; - - printf("\nStack trace (potential return addresses):\n"); - for (const uint32_t* p = traceStart; p < traceEnd; ++p) - { - if (IsCodeAddr(*p)) - printf(" 0x%08x\n", (unsigned)(*p & ~1u)); - } - - printf("==================\n\n"); - _exit(1); -} diff --git a/platform/qemu/FaultHandler.hpp b/platform/qemu/FaultHandler.hpp deleted file mode 100644 index d8ba671a..00000000 --- a/platform/qemu/FaultHandler.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include - -class FaultHandler -{ -public: - static void Dispatch(uint32_t* stackFrame, uint32_t exceptionLR) __attribute__((noreturn)); -}; diff --git a/platform/qemu/Startup.cpp b/platform/qemu/Startup.cpp deleted file mode 100644 index 156f3e68..00000000 --- a/platform/qemu/Startup.cpp +++ /dev/null @@ -1,145 +0,0 @@ -#include - -extern int main(int argc, char** argv); - -extern "C" -{ - extern void Default_Handler_Forwarded(); - extern void HardwareInitialization(); - extern void __libc_init_array(); - extern void _exit(int status) __attribute__((noreturn)); - - static void Default_Handler(); - - void Reset_Handler(); - void NMI_Handler() __attribute__((weak, alias("Default_Handler"))); - void HardFault_Handler() __attribute__((weak, alias("Default_Handler"))); - void MemManage_Handler() __attribute__((weak, alias("Default_Handler"))); - void BusFault_Handler() __attribute__((weak, alias("Default_Handler"))); - void UsageFault_Handler() __attribute__((weak, alias("Default_Handler"))); - void SVC_Handler() __attribute__((weak, alias("Default_Handler"))); - void DebugMon_Handler() __attribute__((weak, alias("Default_Handler"))); - void PendSV_Handler() __attribute__((weak, alias("Default_Handler"))); - void SysTick_Handler() __attribute__((weak, alias("Default_Handler"))); - void IRQ0_Handler() __attribute__((weak, alias("Default_Handler"))); - void IRQ1_Handler() __attribute__((weak, alias("Default_Handler"))); - void IRQ2_Handler() __attribute__((weak, alias("Default_Handler"))); - void IRQ3_Handler() __attribute__((weak, alias("Default_Handler"))); - void IRQ4_Handler() __attribute__((weak, alias("Default_Handler"))); - void IRQ5_Handler() __attribute__((weak, alias("Default_Handler"))); - void IRQ6_Handler() __attribute__((weak, alias("Default_Handler"))); - void IRQ7_Handler() __attribute__((weak, alias("Default_Handler"))); - void IRQ8_Handler() __attribute__((weak, alias("Default_Handler"))); - void IRQ9_Handler() __attribute__((weak, alias("Default_Handler"))); - void IRQ10_Handler() __attribute__((weak, alias("Default_Handler"))); - void IRQ11_Handler() __attribute__((weak, alias("Default_Handler"))); - void IRQ12_Handler() __attribute__((weak, alias("Default_Handler"))); - void IRQ13_Handler() __attribute__((weak, alias("Default_Handler"))); - void IRQ14_Handler() __attribute__((weak, alias("Default_Handler"))); - void IRQ15_Handler() __attribute__((weak, alias("Default_Handler"))); - void IRQ16_Handler() __attribute__((weak, alias("Default_Handler"))); - void IRQ17_Handler() __attribute__((weak, alias("Default_Handler"))); - void IRQ18_Handler() __attribute__((weak, alias("Default_Handler"))); - void IRQ19_Handler() __attribute__((weak, alias("Default_Handler"))); - void IRQ20_Handler() __attribute__((weak, alias("Default_Handler"))); - void IRQ21_Handler() __attribute__((weak, alias("Default_Handler"))); - void IRQ22_Handler() __attribute__((weak, alias("Default_Handler"))); - void IRQ23_Handler() __attribute__((weak, alias("Default_Handler"))); - void IRQ24_Handler() __attribute__((weak, alias("Default_Handler"))); - void IRQ25_Handler() __attribute__((weak, alias("Default_Handler"))); - void IRQ26_Handler() __attribute__((weak, alias("Default_Handler"))); - void IRQ27_Handler() __attribute__((weak, alias("Default_Handler"))); - void IRQ28_Handler() __attribute__((weak, alias("Default_Handler"))); - void IRQ29_Handler() __attribute__((weak, alias("Default_Handler"))); - void IRQ30_Handler() __attribute__((weak, alias("Default_Handler"))); - void IRQ31_Handler() __attribute__((weak, alias("Default_Handler"))); -} - -extern "C" uint32_t _estack; -extern "C" uint32_t _sidata; -extern "C" uint32_t _sdata; -extern "C" uint32_t _edata; -extern "C" uint32_t _sbss; -extern "C" uint32_t _ebss; - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wpedantic" -static void (*const g_pfnVectors[])(void) __attribute__((section(".isr_vector"), used)) = { - reinterpret_cast(&_estack), - Reset_Handler, - NMI_Handler, - HardFault_Handler, - MemManage_Handler, - BusFault_Handler, - UsageFault_Handler, - nullptr, - nullptr, - nullptr, - nullptr, - SVC_Handler, - DebugMon_Handler, - nullptr, - PendSV_Handler, - SysTick_Handler, - IRQ0_Handler, - IRQ1_Handler, - IRQ2_Handler, - IRQ3_Handler, - IRQ4_Handler, - IRQ5_Handler, - IRQ6_Handler, - IRQ7_Handler, - IRQ8_Handler, - IRQ9_Handler, - IRQ10_Handler, - IRQ11_Handler, - IRQ12_Handler, - IRQ13_Handler, - IRQ14_Handler, - IRQ15_Handler, - IRQ16_Handler, - IRQ17_Handler, - IRQ18_Handler, - IRQ19_Handler, - IRQ20_Handler, - IRQ21_Handler, - IRQ22_Handler, - IRQ23_Handler, - IRQ24_Handler, - IRQ25_Handler, - IRQ26_Handler, - IRQ27_Handler, - IRQ28_Handler, - IRQ29_Handler, - IRQ30_Handler, - IRQ31_Handler, -}; -#pragma GCC diagnostic pop - -extern "C" void Reset_Handler() -{ - __asm volatile("cpsid i"); - - *reinterpret_cast(0xE000ED88U) |= (0xFU << 20U); - __asm volatile("dsb" ::: "memory"); - __asm volatile("isb" ::: "memory"); - - const uint32_t* src = &_sidata; - for (uint32_t* dst = &_sdata; dst < &_edata;) - *dst++ = *src++; - - for (uint32_t* bss = &_sbss; bss < &_ebss;) - *bss++ = 0U; - - __libc_init_array(); - - __asm volatile("cpsie i"); - - HardwareInitialization(); - _exit(main(0, nullptr)); -} - -static void Default_Handler() -{ - Default_Handler_Forwarded(); -} diff --git a/platform/qemu/mps2.ld b/platform/qemu/mps2.ld deleted file mode 100644 index be7d9ba3..00000000 --- a/platform/qemu/mps2.ld +++ /dev/null @@ -1,57 +0,0 @@ -MEMORY -{ - FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 4M - RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 4M -} - -_estack = ORIGIN(RAM) + LENGTH(RAM); - -ENTRY(Reset_Handler) - -SECTIONS -{ - .isr_vector : - { - . = ALIGN(4); - KEEP(*(.isr_vector)) - . = ALIGN(4); - } > FLASH - - .text : - { - . = ALIGN(4); - *(.text*) - *(.rodata*) - . = ALIGN(4); - } > FLASH - - _sidata = LOADADDR(.data); - - .data : - { - . = ALIGN(4); - _sdata = .; - *(.data*) - . = ALIGN(4); - _edata = .; - } > RAM AT > FLASH - - .bss : - { - . = ALIGN(4); - _sbss = .; - *(.bss*) - *(COMMON) - . = ALIGN(4); - _ebss = .; - _end = .; - } > RAM - - .heap (NOLOAD) : - { - . = ALIGN(8); - _heap_start = .; - . += 256K; - _heap_end = .; - } > RAM -} From 0e91100311352d2ff34e6710313bf97246795b45 Mon Sep 17 00:00:00 2001 From: gfs Date: Tue, 25 Aug 2026 07:32:45 +0000 Subject: [PATCH 2/2] fix: use tolerant comparison for BiquadCascade vs chained Biquad sections Now that the QEMU test suites actually run (see previous commit), TestBiquadCascade.cascade_equals_serial_sections failed on real Cortex-M4/M7 hardware: EXPECT_FLOAT_EQ assumed bit-exact equality between BiquadCascade::Filter's internal loop and two manually chained Biquad::Filter calls computing the identical formula. Root cause: this hot path is compiled with #pragma GCC optimize("O3","fast-math"), which permits FMA contraction but does not guarantee the compiler fuses multiply-adds identically across differently-inlined call sites. On ARM VFP hardware (real FMA instructions) this produced a single 1-ULP divergence at iteration 3, which then compounded through the filter's feedback (z1/z2) state over the remaining iterations -- max absolute divergence ~9e-8, confirmed via raw bit-pattern instrumentation. x86 host never diverges since it doesn't contract without -mfma. This is expected, benign fast-math behavior, not an algorithm or infrastructure bug. Switch to EXPECT_NEAR with math::Tolerance(), matching this file's existing convention for other cascade-vs-closed-form comparisons. Co-Authored-By: Claude Sonnet 5 --- numerical/filters/passive/test/TestBiquadCascade.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/numerical/filters/passive/test/TestBiquadCascade.cpp b/numerical/filters/passive/test/TestBiquadCascade.cpp index f9d1bde9..9d2156f3 100644 --- a/numerical/filters/passive/test/TestBiquadCascade.cpp +++ b/numerical/filters/passive/test/TestBiquadCascade.cpp @@ -233,7 +233,7 @@ TEST_F(TestBiquadCascade, cascade_equals_serial_sections) for (int i = 0; i < 20; ++i) { const float x{ static_cast(i) * 0.1f }; - EXPECT_FLOAT_EQ(cascade.Filter(x), s1.Filter(s0.Filter(x))); + EXPECT_NEAR(cascade.Filter(x), s1.Filter(s0.Filter(x)), math::Tolerance()); } }