Files
NawfalMotii79-PLFM_RADAR/9_Firmware/9_1_Microcontroller/tests/Makefile
T
Jason f28a0eaa80 fix(mcu): MCU-A7 — persist emergency state across MCU resets in BKPSRAM
Emergency_Stop's hold loop refreshed IWDG forever, so any reset path that
DID fire (SYSRESETREQ from another fault, brown-out) would re-run
startup and re-energize the PA rails — there was no record that the
system had been in emergency state. Watchdog defeat in the hold loop
masked the problem.

BKPSRAM gives us a flag that survives every reset path but is lost on
main-power removal — exactly the recovery semantics we want:
power-cycle is the deliberate operator action that clears emergency,
every other reset stays in safe-hold.

  - Added emergency_persist_set/check helpers (BKPSRAM @ 0x40024000,
    magic 0xDEAD5A5A); enable PWR + backup-access + BKPSRAM clock.
  - Emergency_Stop now writes the flag BEFORE the rail-cut sequence so
    even an interrupted shutdown still leaves the persisted state set.
  - main() checks the flag immediately after MX_IWDG_Init and before
    any PA enable code; if set, calls Emergency_Stop directly. GPIO
    init has already forced all PA enables LOW, so the safe-hold path
    is reached without a single PA rail going hot.

Hold-loop IWDG refresh kept intentionally: a healthy hold loop does not
need to cycle the MCU, but if the loop itself wedges (stack corruption,
bus fault), refresh stops, IWDG fires, and the persist flag routes the
reset right back into safe-hold.

Added test_mcu_a7_emergency_persist (6 cases) modelling BKPSRAM
persistence vs power-cycle, including a regression check that exercises
the pre-fix "no persistence" boot to confirm it would have re-energized
the PAs. MCU regression now 78/78.
2026-04-27 19:52:13 +05:45

302 lines
9.9 KiB
Makefile

################################################################################
# Makefile -- MCU firmware unit test harness for AERIS-10
#
# Builds and runs host-side (macOS) tests for all discovered firmware bugs.
# Uses mock HAL + spy/recording pattern to test real firmware code without
# hardware.
#
# Usage:
# make -- build and run all tests
# make build -- build all tests without running
# make test -- run all tests
# make clean -- remove build artifacts
# make test_bug1 -- build and run just bug1 test
#
# Requirements: Apple Clang or gcc (any C11-capable compiler)
################################################################################
CC := cc
CXX := c++
CFLAGS := -std=c11 -Wall -Wextra -Wno-unused-parameter -g -O0
CXXFLAGS := -std=c++17 -Wall -Wextra -Wno-unused-parameter -g -O0
# Shim headers come FIRST so they override real headers
INCLUDES := -Ishims -I. -I../9_1_1_C_Cpp_Libraries
# C++ library directory (AGC, ADAR1000 Manager)
CXX_LIB_DIR := ../9_1_1_C_Cpp_Libraries
CXX_SRCS := $(CXX_LIB_DIR)/ADAR1000_AGC.cpp $(CXX_LIB_DIR)/ADAR1000_Manager.cpp
CXX_OBJS := ADAR1000_AGC.o ADAR1000_Manager.o
# GPS driver source
GPS_SRC := ../9_1_3_C_Cpp_Code/um982_gps.c
GPS_OBJ := um982_gps.o
# Real source files compiled against mock headers
REAL_SRC := ../9_1_1_C_Cpp_Libraries/adf4382a_manager.c
# Mock/stub object files (shared across tests)
MOCK_SRCS := stm32_hal_mock.c ad_driver_mock.c
MOCK_OBJS := $(MOCK_SRCS:.c=.o)
# Real source compiled as object (for tests that need it)
REAL_OBJ := adf4382a_manager.o
# Platform source compiled with shim headers
PLATFORM_SRC := ../9_1_1_C_Cpp_Libraries/platform_noos_stm32.c
PLATFORM_OBJ := platform_noos_stm32.o
# Tests that link against real adf4382a_manager.c + mocks
TESTS_WITH_REAL := test_bug1_timed_sync_init_ordering \
test_bug3_timed_sync_noop \
test_bug4_phase_shift_before_check \
test_bug5_fine_phase_gpio_only \
test_bug9_platform_ops_null \
test_bug10_spi_cs_not_toggled \
test_bug15_htim3_dangling_extern
# Tests that only need mocks (extracted patterns / static analysis)
TESTS_MOCK_ONLY := test_bug2_ad9523_double_setup \
test_bug6_timer_variable_collision \
test_bug7_gpio_pin_conflict \
test_bug8_uart_commented_out \
test_bug14_diag_section_args \
test_gap3_emergency_stop_rails
# Tests that are standalone (no mocks needed, pure logic)
TESTS_STANDALONE := test_bug12_pa_cal_loop_inverted \
test_bug13_dac2_adc_buffer_mismatch \
test_bug16_runradar_shadows_globals \
test_mcu_a1_cooling_hysteresis \
test_mcu_a7_emergency_persist \
test_gap3_iwdg_config \
test_gap3_temperature_max \
test_gap3_idq_periodic_reread \
test_gap3_emergency_state_ordering \
test_gap3_overtemp_emergency_stop \
test_gap3_health_watchdog_cold_start
# Tests that need platform_noos_stm32.o + mocks
TESTS_WITH_PLATFORM := test_bug11_platform_spi_transmit_only
# C++ tests (AGC outer loop)
TESTS_WITH_CXX := test_agc_outer_loop
# GPS driver tests (need mocks + GPS source + -lm)
TESTS_GPS := test_um982_gps
ALL_TESTS := $(TESTS_WITH_REAL) $(TESTS_MOCK_ONLY) $(TESTS_STANDALONE) $(TESTS_WITH_PLATFORM) $(TESTS_WITH_CXX) $(TESTS_GPS)
.PHONY: all build test clean \
$(addprefix test_,bug1 bug2 bug3 bug4 bug5 bug6 bug7 bug8 bug9 bug10 bug11 bug12 bug13 bug14 bug15) \
test_gap3_estop test_gap3_iwdg test_gap3_temp test_gap3_idq test_gap3_order \
test_gap3_overtemp test_gap3_wdog
all: build test
build: $(ALL_TESTS)
test: build
@echo "==============================================="
@echo " Running all $(words $(ALL_TESTS)) bug tests..."
@echo "==============================================="
@pass=0; fail=0; \
for t in $(ALL_TESTS); do \
echo "--- Running $$t ---"; \
./$$t; \
if [ $$? -eq 0 ]; then \
pass=$$((pass + 1)); \
else \
fail=$$((fail + 1)); \
echo "*** FAILED: $$t ***"; \
fi; \
done; \
echo "==============================================="; \
echo " Results: $$pass passed, $$fail failed (of $(words $(ALL_TESTS)) total)"; \
echo "==============================================="; \
[ $$fail -eq 0 ]
# --- Object file rules ---
%.o: %.c
$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
# Real source compiled with shim headers
$(REAL_OBJ): $(REAL_SRC) $(MOCK_OBJS)
$(CC) $(CFLAGS) $(INCLUDES) -c $(REAL_SRC) -o $@
# Platform source compiled with shim headers
$(PLATFORM_OBJ): $(PLATFORM_SRC) $(MOCK_OBJS)
$(CC) $(CFLAGS) $(INCLUDES) -c $(PLATFORM_SRC) -o $@
# --- Test binary rules ---
# Tests that need real adf4382a_manager.o + mocks
$(TESTS_WITH_REAL): %: %.c $(MOCK_OBJS) $(REAL_OBJ)
$(CC) $(CFLAGS) $(INCLUDES) $< $(MOCK_OBJS) $(REAL_OBJ) -o $@
# Tests that only need mocks
test_bug2_ad9523_double_setup: test_bug2_ad9523_double_setup.c $(MOCK_OBJS)
$(CC) $(CFLAGS) $(INCLUDES) $< $(MOCK_OBJS) -o $@
test_bug6_timer_variable_collision: test_bug6_timer_variable_collision.c $(MOCK_OBJS)
$(CC) $(CFLAGS) $(INCLUDES) $< $(MOCK_OBJS) -o $@
# Bug 7 needs shim headers + mock objects (post-fix test includes shim adf4382a_manager.h)
test_bug7_gpio_pin_conflict: test_bug7_gpio_pin_conflict.c $(MOCK_OBJS)
$(CC) $(CFLAGS) $(INCLUDES) $< $(MOCK_OBJS) -o $@
test_bug8_uart_commented_out: test_bug8_uart_commented_out.c
$(CC) $(CFLAGS) -I. $< -o $@
test_bug14_diag_section_args: test_bug14_diag_section_args.c $(MOCK_OBJS)
$(CC) $(CFLAGS) $(INCLUDES) $< $(MOCK_OBJS) -o $@
# Standalone tests (pure logic, no mocks)
test_bug12_pa_cal_loop_inverted: test_bug12_pa_cal_loop_inverted.c
$(CC) $(CFLAGS) $< -lm -o $@
test_bug13_dac2_adc_buffer_mismatch: test_bug13_dac2_adc_buffer_mismatch.c
$(CC) $(CFLAGS) $< -lm -o $@
test_bug16_runradar_shadows_globals: test_bug16_runradar_shadows_globals.c
$(CC) $(CFLAGS) $< -o $@
test_mcu_a1_cooling_hysteresis: test_mcu_a1_cooling_hysteresis.c
$(CC) $(CFLAGS) $< -o $@
test_mcu_a7_emergency_persist: test_mcu_a7_emergency_persist.c
$(CC) $(CFLAGS) $< -o $@
# Gap-3 safety tests -- mock-only (needs spy log for GPIO sequence)
test_gap3_emergency_stop_rails: test_gap3_emergency_stop_rails.c $(MOCK_OBJS)
$(CC) $(CFLAGS) $(INCLUDES) $< $(MOCK_OBJS) -o $@
# Gap-3 safety tests -- standalone (pure logic)
test_gap3_iwdg_config: test_gap3_iwdg_config.c
$(CC) $(CFLAGS) $< -lm -o $@
test_gap3_temperature_max: test_gap3_temperature_max.c
$(CC) $(CFLAGS) $< -lm -o $@
test_gap3_idq_periodic_reread: test_gap3_idq_periodic_reread.c
$(CC) $(CFLAGS) $< -lm -o $@
test_gap3_emergency_state_ordering: test_gap3_emergency_state_ordering.c
$(CC) $(CFLAGS) $< -o $@
test_gap3_overtemp_emergency_stop: test_gap3_overtemp_emergency_stop.c
$(CC) $(CFLAGS) $< -o $@
test_gap3_health_watchdog_cold_start: test_gap3_health_watchdog_cold_start.c
$(CC) $(CFLAGS) $< -o $@
# Tests that need platform_noos_stm32.o + mocks
$(TESTS_WITH_PLATFORM): %: %.c $(MOCK_OBJS) $(PLATFORM_OBJ)
$(CC) $(CFLAGS) $(INCLUDES) $< $(MOCK_OBJS) $(PLATFORM_OBJ) -o $@
# --- C++ object rules ---
ADAR1000_AGC.o: $(CXX_LIB_DIR)/ADAR1000_AGC.cpp $(CXX_LIB_DIR)/ADAR1000_AGC.h
$(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@
ADAR1000_Manager.o: $(CXX_LIB_DIR)/ADAR1000_Manager.cpp $(CXX_LIB_DIR)/ADAR1000_Manager.h
$(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@
# --- C++ test binary rules ---
test_agc_outer_loop: test_agc_outer_loop.cpp $(CXX_OBJS) $(MOCK_OBJS)
$(CXX) $(CXXFLAGS) $(INCLUDES) $< $(CXX_OBJS) $(MOCK_OBJS) -o $@
# Convenience target
.PHONY: test_agc
test_agc: test_agc_outer_loop
./test_agc_outer_loop
# --- GPS driver rules ---
$(GPS_OBJ): $(GPS_SRC)
$(CC) $(CFLAGS) $(INCLUDES) -I../9_1_3_C_Cpp_Code -c $< -o $@
# Note: test includes um982_gps.c directly for white-box testing (static fn access)
test_um982_gps: test_um982_gps.c $(MOCK_OBJS)
$(CC) $(CFLAGS) $(INCLUDES) -I../9_1_3_C_Cpp_Code $< $(MOCK_OBJS) -lm -o $@
# Convenience target
.PHONY: test_gps
test_gps: test_um982_gps
./test_um982_gps
# --- Individual test targets ---
test_bug1: test_bug1_timed_sync_init_ordering
./test_bug1_timed_sync_init_ordering
test_bug2: test_bug2_ad9523_double_setup
./test_bug2_ad9523_double_setup
test_bug3: test_bug3_timed_sync_noop
./test_bug3_timed_sync_noop
test_bug4: test_bug4_phase_shift_before_check
./test_bug4_phase_shift_before_check
test_bug5: test_bug5_fine_phase_gpio_only
./test_bug5_fine_phase_gpio_only
test_bug6: test_bug6_timer_variable_collision
./test_bug6_timer_variable_collision
test_bug7: test_bug7_gpio_pin_conflict
./test_bug7_gpio_pin_conflict
test_bug8: test_bug8_uart_commented_out
./test_bug8_uart_commented_out
test_bug9: test_bug9_platform_ops_null
./test_bug9_platform_ops_null
test_bug10: test_bug10_spi_cs_not_toggled
./test_bug10_spi_cs_not_toggled
test_bug11: test_bug11_platform_spi_transmit_only
./test_bug11_platform_spi_transmit_only
test_bug12: test_bug12_pa_cal_loop_inverted
./test_bug12_pa_cal_loop_inverted
test_bug13: test_bug13_dac2_adc_buffer_mismatch
./test_bug13_dac2_adc_buffer_mismatch
test_bug14: test_bug14_diag_section_args
./test_bug14_diag_section_args
test_bug15: test_bug15_htim3_dangling_extern
./test_bug15_htim3_dangling_extern
test_gap3_estop: test_gap3_emergency_stop_rails
./test_gap3_emergency_stop_rails
test_gap3_iwdg: test_gap3_iwdg_config
./test_gap3_iwdg_config
test_gap3_temp: test_gap3_temperature_max
./test_gap3_temperature_max
test_gap3_idq: test_gap3_idq_periodic_reread
./test_gap3_idq_periodic_reread
test_gap3_order: test_gap3_emergency_state_ordering
./test_gap3_emergency_state_ordering
test_gap3_overtemp: test_gap3_overtemp_emergency_stop
./test_gap3_overtemp_emergency_stop
test_gap3_wdog: test_gap3_health_watchdog_cold_start
./test_gap3_health_watchdog_cold_start
# --- Clean ---
clean:
rm -f *.o $(ALL_TESTS)
@echo "Clean complete"