diff --git a/9_Firmware/9_1_Microcontroller/tests/Makefile b/9_Firmware/9_1_Microcontroller/tests/Makefile new file mode 100644 index 0000000..9a74464 --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/Makefile @@ -0,0 +1,132 @@ +################################################################################ +# Makefile -- MCU firmware unit test harness for AERIS-10 +# +# Builds and runs host-side (macOS) tests for the 8 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 +CFLAGS := -std=c11 -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 + +# 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 + +# 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 + +# 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 + +ALL_TESTS := $(TESTS_WITH_REAL) $(TESTS_MOCK_ONLY) + +.PHONY: all build test clean $(addprefix test_,bug1 bug2 bug3 bug4 bug5 bug6 bug7 bug8) + +all: build test + +build: $(ALL_TESTS) + +test: build + @echo "===============================================" + @echo " Running all 8 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 $@ + +# --- 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 and 8 don't even need mock objects — pure static analysis +test_bug7_gpio_pin_conflict: test_bug7_gpio_pin_conflict.c + $(CC) $(CFLAGS) -I. $< -o $@ + +test_bug8_uart_commented_out: test_bug8_uart_commented_out.c + $(CC) $(CFLAGS) -I. $< -o $@ + +# --- 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 + +# --- Clean --- + +clean: + rm -f *.o $(ALL_TESTS) + @echo "Clean complete" diff --git a/9_Firmware/9_1_Microcontroller/tests/ad_driver_mock.c b/9_Firmware/9_1_Microcontroller/tests/ad_driver_mock.c new file mode 100644 index 0000000..4922c2f --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/ad_driver_mock.c @@ -0,0 +1,142 @@ +/******************************************************************************* + * ad_driver_mock.c -- Mock implementations for ADF4382 and AD9523 drivers + ******************************************************************************/ +#include "ad_driver_mock.h" +#include +#include + +/* Configurable return values */ +int mock_adf4382_init_retval = 0; +int mock_ad9523_setup_retval = 0; + +/* Internal device stubs (allocated on the heap by mock init) */ +static struct adf4382_dev adf4382_stub_devs[4]; +static int adf4382_stub_idx = 0; + +static struct ad9523_dev ad9523_stub_devs[2]; +static int ad9523_stub_idx = 0; + +/* Helper to push spy record (references the extern in stm32_hal_mock) */ +extern SpyRecord spy_log[]; +extern int spy_count; + +static void spy_push_drv(SpyCallType type, void *extra, uint32_t val) +{ + if (spy_count < SPY_MAX_RECORDS) { + spy_log[spy_count++] = (SpyRecord){ + .type = type, + .port = NULL, + .pin = 0, + .value = val, + .extra = extra + }; + } +} + +/* ========================= ADF4382 mock =========================== */ + +int adf4382_init(struct adf4382_dev **device, struct adf4382_init_param *init_param) +{ + spy_push_drv(SPY_ADF4382_INIT, (void*)init_param, 0); + if (mock_adf4382_init_retval != 0) { + *device = NULL; + return mock_adf4382_init_retval; + } + /* Return a stub device */ + int idx = adf4382_stub_idx % 4; + memset(&adf4382_stub_devs[idx], 0, sizeof(struct adf4382_dev)); + adf4382_stub_devs[idx].freq = init_param->freq; + adf4382_stub_devs[idx].ref_freq_hz = init_param->ref_freq_hz; + *device = &adf4382_stub_devs[idx]; + adf4382_stub_idx++; + return 0; +} + +int adf4382_remove(struct adf4382_dev *dev) +{ + spy_push_drv(SPY_ADF4382_REMOVE, dev, 0); + return 0; +} + +int adf4382_set_out_power(struct adf4382_dev *dev, uint8_t ch, int32_t pwr) +{ + spy_push_drv(SPY_ADF4382_SET_OUT_POWER, dev, (uint32_t)((ch << 16) | (pwr & 0xFFFF))); + return 0; +} + +int adf4382_set_en_chan(struct adf4382_dev *dev, uint8_t ch, bool en) +{ + spy_push_drv(SPY_ADF4382_SET_EN_CHAN, dev, (uint32_t)((ch << 16) | en)); + return 0; +} + +int adf4382_set_timed_sync_setup(struct adf4382_dev *dev, bool sync) +{ + spy_push_drv(SPY_ADF4382_SET_TIMED_SYNC, dev, (uint32_t)sync); + return 0; +} + +int adf4382_set_ezsync_setup(struct adf4382_dev *dev, bool sync) +{ + spy_push_drv(SPY_ADF4382_SET_EZSYNC, dev, (uint32_t)sync); + return 0; +} + +int adf4382_set_sw_sync(struct adf4382_dev *dev, bool sw_sync) +{ + spy_push_drv(SPY_ADF4382_SET_SW_SYNC, dev, (uint32_t)sw_sync); + return 0; +} + +int adf4382_spi_read(struct adf4382_dev *dev, uint16_t reg_addr, uint8_t *data) +{ + spy_push_drv(SPY_ADF4382_SPI_READ, dev, (uint32_t)reg_addr); + if (data) { + /* By default, return "locked" status for reg 0x58 */ + if (reg_addr == 0x58) { + *data = ADF4382_LOCKED_MSK; /* locked */ + } else { + *data = 0; + } + } + return 0; +} + +/* ========================= AD9523 mock ============================ */ + +int32_t ad9523_init(struct ad9523_init_param *init_param) +{ + spy_push_drv(SPY_AD9523_INIT, init_param, 0); + return 0; +} + +int32_t ad9523_setup(struct ad9523_dev **device, const struct ad9523_init_param *init_param) +{ + spy_push_drv(SPY_AD9523_SETUP, (void*)init_param, 0); + if (mock_ad9523_setup_retval != 0) { + return mock_ad9523_setup_retval; + } + int idx = ad9523_stub_idx % 2; + memset(&ad9523_stub_devs[idx], 0, sizeof(struct ad9523_dev)); + *device = &ad9523_stub_devs[idx]; + ad9523_stub_idx++; + return 0; +} + +int32_t ad9523_status(struct ad9523_dev *dev) +{ + spy_push_drv(SPY_AD9523_STATUS, dev, 0); + return 0; +} + +int32_t ad9523_sync(struct ad9523_dev *dev) +{ + spy_push_drv(SPY_AD9523_SYNC, dev, 0); + return 0; +} + +int32_t ad9523_remove(struct ad9523_dev *dev) +{ + spy_push_drv(SPY_AD9523_REMOVE, dev, 0); + return 0; +} diff --git a/9_Firmware/9_1_Microcontroller/tests/ad_driver_mock.h b/9_Firmware/9_1_Microcontroller/tests/ad_driver_mock.h new file mode 100644 index 0000000..b6f73ed --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/ad_driver_mock.h @@ -0,0 +1,219 @@ +/******************************************************************************* + * ad_driver_mock.h -- Mock declarations for ADF4382 and AD9523 driver APIs + * + * These replace the real driver implementations. The spy layer in + * ad_driver_mock.c records all calls for test assertion. + ******************************************************************************/ +#ifndef AD_DRIVER_MOCK_H +#define AD_DRIVER_MOCK_H + +#include "stm32_hal_mock.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* ========================= no_os SPI types ======================== */ + +enum no_os_spi_mode { + NO_OS_SPI_MODE_0 = 0, + NO_OS_SPI_MODE_1 = 1, + NO_OS_SPI_MODE_2 = 2, + NO_OS_SPI_MODE_3 = 3 +}; + +enum no_os_spi_bit_order { + NO_OS_SPI_BIT_ORDER_MSB_FIRST = 0, + NO_OS_SPI_BIT_ORDER_LSB_FIRST = 1 +}; + +enum no_os_spi_lanes { + NO_OS_SPI_SINGLE = 0, +}; + +struct no_os_platform_spi_delays { + uint32_t cs_delay_first; + uint32_t cs_delay_last; +}; + +struct no_os_spi_desc { + uint32_t dummy; +}; + +struct no_os_spi_platform_ops { + int (*init)(void); +}; + +struct no_os_spi_init_param { + uint32_t device_id; + uint32_t max_speed_hz; + uint8_t chip_select; + enum no_os_spi_mode mode; + enum no_os_spi_bit_order bit_order; + enum no_os_spi_lanes lanes; + const struct no_os_spi_platform_ops *platform_ops; + struct no_os_platform_spi_delays platform_delays; + void *extra; + struct no_os_spi_desc *parent; +}; + +/* ========================= ADF4382 types ========================== */ + +enum adf4382_dev_id { + ID_ADF4382 = 0, + ID_ADF4382A = 1, + ID_ADF4383 = 2, +}; + +struct adf4382_dev { + struct no_os_spi_desc *spi_desc; + bool spi_3wire_en; + bool cmos_3v3; + uint64_t ref_freq_hz; + uint64_t freq; + bool ref_doubler_en; + uint8_t ref_div; + uint8_t cp_i; + uint16_t bleed_word; + uint8_t ld_count; + uint32_t phase_adj; + uint16_t n_int; +}; + +struct adf4382_init_param { + struct no_os_spi_init_param *spi_init; + bool spi_3wire_en; + bool cmos_3v3; + uint64_t ref_freq_hz; + uint64_t freq; + bool ref_doubler_en; + uint8_t ref_div; + uint8_t cp_i; + uint16_t bleed_word; + uint8_t ld_count; + uint8_t en_lut_gen; + uint8_t en_lut_cal; + uint8_t max_lpf_cap_value_uf; + enum adf4382_dev_id id; +}; + +/* Lock detect mask -- from real header */ +#define ADF4382_LOCKED_MSK (1U << 0) + +/* ========================= AD9523 types =========================== */ + +#define AD9523_NUM_CHAN 14 + +struct ad9523_channel_spec { + uint8_t channel_num; + uint8_t divider_output_invert_en; + uint8_t sync_ignore_en; + uint8_t low_power_mode_en; + uint8_t use_alt_clock_src; + uint8_t output_dis; + uint8_t driver_mode; + uint8_t divider_phase; + uint16_t channel_divider; + int8_t extended_name[16]; +}; + +struct ad9523_platform_data { + uint32_t vcxo_freq; + uint8_t spi3wire; + uint8_t refa_diff_rcv_en; + uint8_t refb_diff_rcv_en; + uint8_t zd_in_diff_en; + uint8_t osc_in_diff_en; + uint8_t refa_cmos_neg_inp_en; + uint8_t refb_cmos_neg_inp_en; + uint8_t zd_in_cmos_neg_inp_en; + uint8_t osc_in_cmos_neg_inp_en; + uint16_t refa_r_div; + uint16_t refb_r_div; + uint16_t pll1_feedback_div; + uint16_t pll1_charge_pump_current_nA; + uint8_t zero_delay_mode_internal_en; + uint8_t osc_in_feedback_en; + uint8_t pll1_bypass_en; + uint8_t pll1_loop_filter_rzero; + uint8_t ref_mode; + uint32_t pll2_charge_pump_current_nA; + uint8_t pll2_ndiv_a_cnt; + uint8_t pll2_ndiv_b_cnt; + uint8_t pll2_freq_doubler_en; + uint8_t pll2_r2_div; + uint8_t pll2_vco_diff_m1; + uint8_t pll2_vco_diff_m2; + uint8_t rpole2; + uint8_t rzero; + uint8_t cpole1; + uint8_t rzero_bypass_en; + int32_t num_channels; + struct ad9523_channel_spec *channels; + int8_t name[16]; +}; + +struct ad9523_state { + struct ad9523_platform_data *pdata; + uint32_t vcxo_freq; + uint32_t vco_freq; + uint32_t vco_out_freq[3]; + uint8_t vco_out_map[14]; +}; + +struct ad9523_dev { + struct no_os_spi_desc *spi_desc; + struct ad9523_state ad9523_st; + struct ad9523_platform_data *pdata; +}; + +struct ad9523_init_param { + struct no_os_spi_init_param spi_init; + struct ad9523_platform_data *pdata; +}; + +/* AD9523 enums needed by test code */ +enum outp_drv_mode { + TRISTATE = 0, + LVPECL_8mA, LVDS_4mA, LVDS_7mA, + HSTL0_16mA, HSTL1_8mA, + CMOS_CONF1, CMOS_CONF2, CMOS_CONF3, CMOS_CONF4, + CMOS_CONF5, CMOS_CONF6, CMOS_CONF7, CMOS_CONF8, CMOS_CONF9 +}; + +enum rpole2_resistor { RPOLE2_900_OHM = 0, RPOLE2_450_OHM, RPOLE2_300_OHM, RPOLE2_225_OHM }; +enum rzero_resistor { RZERO_3250_OHM = 0, RZERO_2750_OHM, RZERO_2250_OHM, RZERO_2100_OHM, + RZERO_3000_OHM, RZERO_2500_OHM, RZERO_2000_OHM, RZERO_1850_OHM }; +enum cpole1_capacitor { CPOLE1_0_PF = 0, CPOLE1_8_PF, CPOLE1_16_PF, CPOLE1_24_PF, + _CPOLE1_24_PF, CPOLE1_32_PF, CPOLE1_40_PF, CPOLE1_48_PF }; + +/* ========================= Mock return code control =============== */ + +/* Default return code for mock driver functions (0 = success) */ +extern int mock_adf4382_init_retval; +extern int mock_ad9523_setup_retval; + +/* ========================= ADF4382 mock API ======================= */ + +int adf4382_init(struct adf4382_dev **device, struct adf4382_init_param *init_param); +int adf4382_remove(struct adf4382_dev *dev); +int adf4382_set_out_power(struct adf4382_dev *dev, uint8_t ch, int32_t pwr); +int adf4382_set_en_chan(struct adf4382_dev *dev, uint8_t ch, bool en); +int adf4382_set_timed_sync_setup(struct adf4382_dev *dev, bool sync); +int adf4382_set_ezsync_setup(struct adf4382_dev *dev, bool sync); +int adf4382_set_sw_sync(struct adf4382_dev *dev, bool sw_sync); +int adf4382_spi_read(struct adf4382_dev *dev, uint16_t reg_addr, uint8_t *data); + +/* ========================= AD9523 mock API ======================== */ + +int32_t ad9523_init(struct ad9523_init_param *init_param); +int32_t ad9523_setup(struct ad9523_dev **device, const struct ad9523_init_param *init_param); +int32_t ad9523_status(struct ad9523_dev *dev); +int32_t ad9523_sync(struct ad9523_dev *dev); +int32_t ad9523_remove(struct ad9523_dev *dev); + +#ifdef __cplusplus +} +#endif + +#endif /* AD_DRIVER_MOCK_H */ diff --git a/9_Firmware/9_1_Microcontroller/tests/ad_driver_mock.o b/9_Firmware/9_1_Microcontroller/tests/ad_driver_mock.o new file mode 100644 index 0000000..3030602 Binary files /dev/null and b/9_Firmware/9_1_Microcontroller/tests/ad_driver_mock.o differ diff --git a/9_Firmware/9_1_Microcontroller/tests/adf4382a_manager.o b/9_Firmware/9_1_Microcontroller/tests/adf4382a_manager.o new file mode 100644 index 0000000..9c7aebb Binary files /dev/null and b/9_Firmware/9_1_Microcontroller/tests/adf4382a_manager.o differ diff --git a/9_Firmware/9_1_Microcontroller/tests/shims/adf4382.h b/9_Firmware/9_1_Microcontroller/tests/shims/adf4382.h new file mode 100644 index 0000000..537bcf6 --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/shims/adf4382.h @@ -0,0 +1,5 @@ +/* shim: redirect adf4382.h -> our mock types */ +#ifndef ADF4382_H_SHIM +#define ADF4382_H_SHIM +#include "ad_driver_mock.h" +#endif diff --git a/9_Firmware/9_1_Microcontroller/tests/shims/adf4382a_manager.h b/9_Firmware/9_1_Microcontroller/tests/shims/adf4382a_manager.h new file mode 100644 index 0000000..0bfe70e --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/shims/adf4382a_manager.h @@ -0,0 +1,95 @@ +/* shim: adf4382a_manager.h -- provides the manager API using mock types + * + * The real adf4382a_manager.h includes adf4382.h and no_os_spi.h via + * quoted includes, which the compiler resolves to the same-directory + * copies BEFORE checking -I paths. This shim replaces it for test code + * so all types come from our mocks. + * + * Note: adf4382a_manager.c is compiled separately with the correct -I + * order, so IT gets the shim versions of adf4382.h etc. Test .c files + * include THIS file instead. + */ +#ifndef ADF4382A_MANAGER_H_SHIM +#define ADF4382A_MANAGER_H_SHIM + +#include "stm32_hal_mock.h" +#include "ad_driver_mock.h" + +/* ---- Constants (copied from real adf4382a_manager.h) ---- */ + +/* GPIO Definitions — these are the manager.h pin mappings (the buggy ones) */ +#define TX_CE_Pin GPIO_PIN_0 +#define TX_CE_GPIO_Port GPIOG +#define TX_CS_Pin GPIO_PIN_1 +#define TX_CS_GPIO_Port GPIOG +#define TX_DELADJ_Pin GPIO_PIN_2 +#define TX_DELADJ_GPIO_Port GPIOG +#define TX_DELSTR_Pin GPIO_PIN_3 +#define TX_DELSTR_GPIO_Port GPIOG +#define TX_LKDET_Pin GPIO_PIN_4 +#define TX_LKDET_GPIO_Port GPIOG + +#define RX_CE_Pin GPIO_PIN_5 +#define RX_CE_GPIO_Port GPIOG +#define RX_CS_Pin GPIO_PIN_6 +#define RX_CS_GPIO_Port GPIOG +#define RX_DELADJ_Pin GPIO_PIN_7 +#define RX_DELADJ_GPIO_Port GPIOG +#define RX_DELSTR_Pin GPIO_PIN_8 +#define RX_DELSTR_GPIO_Port GPIOG +#define RX_LKDET_Pin GPIO_PIN_9 +#define RX_LKDET_GPIO_Port GPIOG + +/* Frequency definitions */ +#define REF_FREQ_HZ 300000000ULL +#define TX_FREQ_HZ 10500000000ULL +#define RX_FREQ_HZ 10380000000ULL +#define SYNC_CLOCK_FREQ 60000000ULL + +/* SPI Configuration */ +#define ADF4382A_SPI_DEVICE_ID 4 +#define ADF4382A_SPI_SPEED_HZ 10000000 + +/* Phase delay configuration */ +#define DELADJ_MAX_DUTY_CYCLE 1000 +#define DELADJ_PULSE_WIDTH_US 10 +#define PHASE_SHIFT_MAX_PS 10000 + +/* Error codes */ +#define ADF4382A_MANAGER_OK 0 +#define ADF4382A_MANAGER_ERROR_INVALID -1 +#define ADF4382A_MANAGER_ERROR_NOT_INIT -2 +#define ADF4382A_MANAGER_ERROR_SPI -3 + +typedef enum { + SYNC_METHOD_EZSYNC = 0, + SYNC_METHOD_TIMED = 1 +} SyncMethod; + +typedef struct { + struct adf4382_dev *tx_dev; + struct adf4382_dev *rx_dev; + struct no_os_spi_init_param spi_tx_param; + struct no_os_spi_init_param spi_rx_param; + bool initialized; + SyncMethod sync_method; + uint16_t tx_phase_shift_ps; + uint16_t rx_phase_shift_ps; +} ADF4382A_Manager; + +/* Public functions */ +int ADF4382A_Manager_Init(ADF4382A_Manager *manager, SyncMethod method); +int ADF4382A_Manager_Deinit(ADF4382A_Manager *manager); +int ADF4382A_SetupTimedSync(ADF4382A_Manager *manager); +int ADF4382A_SetupEZSync(ADF4382A_Manager *manager); +int ADF4382A_TriggerTimedSync(ADF4382A_Manager *manager); +int ADF4382A_TriggerEZSync(ADF4382A_Manager *manager); +int ADF4382A_CheckLockStatus(ADF4382A_Manager *manager, bool *tx_locked, bool *rx_locked); +int ADF4382A_SetOutputPower(ADF4382A_Manager *manager, uint8_t tx_power, uint8_t rx_power); +int ADF4382A_EnableOutputs(ADF4382A_Manager *manager, bool tx_enable, bool rx_enable); +int ADF4382A_SetPhaseShift(ADF4382A_Manager *manager, uint16_t tx_phase_ps, uint16_t rx_phase_ps); +int ADF4382A_GetPhaseShift(ADF4382A_Manager *manager, uint16_t *tx_phase_ps, uint16_t *rx_phase_ps); +int ADF4382A_SetFinePhaseShift(ADF4382A_Manager *manager, uint8_t device, uint16_t duty_cycle); +int ADF4382A_StrobePhaseShift(ADF4382A_Manager *manager, uint8_t device); + +#endif /* ADF4382A_MANAGER_H_SHIM */ diff --git a/9_Firmware/9_1_Microcontroller/tests/shims/diag_log.h b/9_Firmware/9_1_Microcontroller/tests/shims/diag_log.h new file mode 100644 index 0000000..2d1d3ec --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/shims/diag_log.h @@ -0,0 +1,18 @@ +/* shim: diag_log.h -- disable DIAG macros for test builds */ +#ifndef DIAG_LOG_H_SHIM +#define DIAG_LOG_H_SHIM + +/* Silence all DIAG output during unit tests */ +#define DIAG_DISABLE + +#define DIAG(tag, fmt, ...) ((void)0) +#define DIAG_WARN(tag, fmt, ...) ((void)0) +#define DIAG_ERR(tag, fmt, ...) ((void)0) +#define DIAG_REG(tag, name, val) ((void)0) +#define DIAG_REG32(tag, name, val) ((void)0) +#define DIAG_GPIO(tag, name, port, pin) ((void)0) +#define DIAG_BOOL(tag, name, val) ((void)0) +#define DIAG_SECTION(name) ((void)0) +#define DIAG_ELAPSED(tag, name, t) ((void)0) + +#endif diff --git a/9_Firmware/9_1_Microcontroller/tests/shims/main.h b/9_Firmware/9_1_Microcontroller/tests/shims/main.h new file mode 100644 index 0000000..9dd05df --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/shims/main.h @@ -0,0 +1,136 @@ +/* shim: redirect main.h -> our mock + pin defines from real main.h */ +#ifndef MAIN_H_SHIM +#define MAIN_H_SHIM +#include "stm32_hal_mock.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern uint8_t GUI_start_flag_received; +extern uint8_t USB_Buffer[64]; +void Error_Handler(void); + +/* Pin definitions from real main.h (CubeMX-generated) */ +#define AD9523_PD_Pin GPIO_PIN_3 +#define AD9523_PD_GPIO_Port GPIOF +#define AD9523_REF_SEL_Pin GPIO_PIN_4 +#define AD9523_REF_SEL_GPIO_Port GPIOF +#define AD9523_SYNC_Pin GPIO_PIN_5 +#define AD9523_SYNC_GPIO_Port GPIOF +#define AD9523_RESET_Pin GPIO_PIN_6 +#define AD9523_RESET_GPIO_Port GPIOF +#define AD9523_CS_Pin GPIO_PIN_7 +#define AD9523_CS_GPIO_Port GPIOF +#define AD9523_STATUS0_Pin GPIO_PIN_8 +#define AD9523_STATUS0_GPIO_Port GPIOF +#define AD9523_STATUS1_Pin GPIO_PIN_9 +#define AD9523_STATUS1_GPIO_Port GPIOF +#define AD9523_EEPROM_SEL_Pin GPIO_PIN_10 +#define AD9523_EEPROM_SEL_GPIO_Port GPIOF + +#define ADAR_1_CS_3V3_Pin GPIO_PIN_0 +#define ADAR_1_CS_3V3_GPIO_Port GPIOA +#define ADAR_2_CS_3V3_Pin GPIO_PIN_1 +#define ADAR_2_CS_3V3_GPIO_Port GPIOA +#define ADAR_3_CS_3V3_Pin GPIO_PIN_2 +#define ADAR_3_CS_3V3_GPIO_Port GPIOA +#define ADAR_4_CS_3V3_Pin GPIO_PIN_3 +#define ADAR_4_CS_3V3_GPIO_Port GPIOA + +#define LED_1_Pin GPIO_PIN_12 +#define LED_1_GPIO_Port GPIOF +#define LED_2_Pin GPIO_PIN_13 +#define LED_2_GPIO_Port GPIOF +#define LED_3_Pin GPIO_PIN_14 +#define LED_3_GPIO_Port GPIOF +#define LED_4_Pin GPIO_PIN_15 +#define LED_4_GPIO_Port GPIOF + +#define EN_P_5V0_PA1_Pin GPIO_PIN_0 +#define EN_P_5V0_PA1_GPIO_Port GPIOG +#define EN_P_5V0_PA2_Pin GPIO_PIN_1 +#define EN_P_5V0_PA2_GPIO_Port GPIOG +#define EN_P_5V0_PA3_Pin GPIO_PIN_2 +#define EN_P_5V0_PA3_GPIO_Port GPIOG /* was GPIO_PIN_2 */ +#define EN_P_5V5_PA_Pin GPIO_PIN_3 +#define EN_P_5V5_PA_GPIO_Port GPIOG +#define EN_P_1V8_CLOCK_Pin GPIO_PIN_4 +#define EN_P_1V8_CLOCK_GPIO_Port GPIOG +#define EN_P_3V3_CLOCK_Pin GPIO_PIN_5 +#define EN_P_3V3_CLOCK_GPIO_Port GPIOG + +#define ADF4382_RX_LKDET_Pin GPIO_PIN_6 +#define ADF4382_RX_LKDET_GPIO_Port GPIOG +#define ADF4382_RX_DELADJ_Pin GPIO_PIN_7 +#define ADF4382_RX_DELADJ_GPIO_Port GPIOG +#define ADF4382_RX_DELSTR_Pin GPIO_PIN_8 +#define ADF4382_RX_DELSTR_GPIO_Port GPIOG +#define ADF4382_RX_CE_Pin GPIO_PIN_9 +#define ADF4382_RX_CE_GPIO_Port GPIOG /* was GPIO_PIN_9 */ +#define ADF4382_RX_CS_Pin GPIO_PIN_10 +#define ADF4382_RX_CS_GPIO_Port GPIOG /* was GPIO_PIN_10 */ +#define ADF4382_TX_LKDET_Pin GPIO_PIN_11 +#define ADF4382_TX_LKDET_GPIO_Port GPIOG +#define ADF4382_TX_DELSTR_Pin GPIO_PIN_12 +#define ADF4382_TX_DELSTR_GPIO_Port GPIOG +#define ADF4382_TX_DELADJ_Pin GPIO_PIN_13 +#define ADF4382_TX_DELADJ_GPIO_Port GPIOG +#define ADF4382_TX_CS_Pin GPIO_PIN_14 +#define ADF4382_TX_CS_GPIO_Port GPIOG +#define ADF4382_TX_CE_Pin GPIO_PIN_15 +#define ADF4382_TX_CE_GPIO_Port GPIOG + +/* Power enables (GPIOE) */ +#define EN_P_1V0_FPGA_Pin GPIO_PIN_7 +#define EN_P_1V0_FPGA_GPIO_Port GPIOE +#define EN_P_1V8_FPGA_Pin GPIO_PIN_8 +#define EN_P_1V8_FPGA_GPIO_Port GPIOE +#define EN_P_3V3_FPGA_Pin GPIO_PIN_9 +#define EN_P_3V3_FPGA_GPIO_Port GPIOE +#define EN_P_5V0_ADAR_Pin GPIO_PIN_10 +#define EN_P_5V0_ADAR_GPIO_Port GPIOE +#define EN_P_3V3_ADAR12_Pin GPIO_PIN_11 +#define EN_P_3V3_ADAR12_GPIO_Port GPIOE +#define EN_P_3V3_ADAR34_Pin GPIO_PIN_12 +#define EN_P_3V3_ADAR34_GPIO_Port GPIOE +#define EN_P_3V3_ADTR_Pin GPIO_PIN_13 +#define EN_P_3V3_ADTR_GPIO_Port GPIOE +#define EN_P_3V3_SW_Pin GPIO_PIN_14 +#define EN_P_3V3_SW_GPIO_Port GPIOE +#define EN_P_3V3_VDD_SW_Pin GPIO_PIN_15 +#define EN_P_3V3_VDD_SW_GPIO_Port GPIOE + +/* GPIOD pins */ +#define STEPPER_CW_P_Pin GPIO_PIN_4 +#define STEPPER_CW_P_GPIO_Port GPIOD +#define STEPPER_CLK_P_Pin GPIO_PIN_5 +#define STEPPER_CLK_P_GPIO_Port GPIOD +#define EN_DIS_RFPA_VDD_Pin GPIO_PIN_6 +#define EN_DIS_RFPA_VDD_GPIO_Port GPIOD +#define EN_DIS_COOLING_Pin GPIO_PIN_7 +#define EN_DIS_COOLING_GPIO_Port GPIOD + +/* DAC pins */ +#define DAC_1_VG_CLR_Pin GPIO_PIN_4 +#define DAC_1_VG_CLR_GPIO_Port GPIOB +#define DAC_1_VG_LDAC_Pin GPIO_PIN_5 +#define DAC_1_VG_LDAC_GPIO_Port GPIOB +#define DAC_2_VG_CLR_Pin GPIO_PIN_8 +#define DAC_2_VG_CLR_GPIO_Port GPIOB +#define DAC_2_VG_LDAC_Pin GPIO_PIN_9 +#define DAC_2_VG_LDAC_GPIO_Port GPIOB + +/* IMU interrupt pins */ +#define MAG_DRDY_Pin GPIO_PIN_6 +#define MAG_DRDY_GPIO_Port GPIOC +#define ACC_INT_Pin GPIO_PIN_7 +#define ACC_INT_GPIO_Port GPIOC +#define GYR_INT_Pin GPIO_PIN_8 +#define GYR_INT_GPIO_Port GPIOC + +#ifdef __cplusplus +} +#endif + +#endif /* MAIN_H_SHIM */ diff --git a/9_Firmware/9_1_Microcontroller/tests/shims/no_os_delay.h b/9_Firmware/9_1_Microcontroller/tests/shims/no_os_delay.h new file mode 100644 index 0000000..b3bbe29 --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/shims/no_os_delay.h @@ -0,0 +1,7 @@ +/* shim: redirect no_os_delay.h -> our mock */ +#ifndef NO_OS_DELAY_H_SHIM +#define NO_OS_DELAY_H_SHIM +#include "stm32_hal_mock.h" +/* no_os_udelay and no_os_mdelay declared in stm32_hal_mock.h */ +struct no_os_time { uint32_t s; uint32_t us; }; +#endif diff --git a/9_Firmware/9_1_Microcontroller/tests/shims/no_os_spi.h b/9_Firmware/9_1_Microcontroller/tests/shims/no_os_spi.h new file mode 100644 index 0000000..3e9752f --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/shims/no_os_spi.h @@ -0,0 +1,5 @@ +/* shim: redirect no_os_spi.h -> our mock types */ +#ifndef NO_OS_SPI_H_SHIM +#define NO_OS_SPI_H_SHIM +#include "ad_driver_mock.h" +#endif diff --git a/9_Firmware/9_1_Microcontroller/tests/shims/no_os_units.h b/9_Firmware/9_1_Microcontroller/tests/shims/no_os_units.h new file mode 100644 index 0000000..e712506 --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/shims/no_os_units.h @@ -0,0 +1,10 @@ +/* shim: redirect no_os_units.h -> minimal defines */ +#ifndef NO_OS_UNITS_H_SHIM +#define NO_OS_UNITS_H_SHIM + +#define MEGA 1000000ULL +#define NANO 1000000000ULL +#define PICO 1000000000000ULL +#define KHZ_PER_MHZ 1000ULL + +#endif diff --git a/9_Firmware/9_1_Microcontroller/tests/shims/no_os_util.h b/9_Firmware/9_1_Microcontroller/tests/shims/no_os_util.h new file mode 100644 index 0000000..ffb92eb --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/shims/no_os_util.h @@ -0,0 +1,25 @@ +/* shim: redirect no_os_util.h -> minimal defines */ +#ifndef NO_OS_UTIL_H_SHIM +#define NO_OS_UTIL_H_SHIM + +#include + +#ifndef NO_OS_BIT +#define NO_OS_BIT(x) (1UL << (x)) +#endif + +#ifndef NO_OS_GENMASK +#define NO_OS_GENMASK(h, l) (((~0UL) << (l)) & (~0UL >> (31 - (h)))) +#endif + +static inline uint32_t no_os_field_prep(uint32_t mask, uint32_t val) { + int shift = __builtin_ctz(mask); + return (val << shift) & mask; +} + +static inline uint32_t no_os_field_get(uint32_t mask, uint32_t val) { + int shift = __builtin_ctz(mask); + return (val & mask) >> shift; +} + +#endif diff --git a/9_Firmware/9_1_Microcontroller/tests/shims/stm32f7xx_hal.h b/9_Firmware/9_1_Microcontroller/tests/shims/stm32f7xx_hal.h new file mode 100644 index 0000000..8829d2e --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/shims/stm32f7xx_hal.h @@ -0,0 +1,5 @@ +/* shim: redirect stm32f7xx_hal.h -> our mock */ +#ifndef STM32F7XX_HAL_H_SHIM +#define STM32F7XX_HAL_H_SHIM +#include "stm32_hal_mock.h" +#endif diff --git a/9_Firmware/9_1_Microcontroller/tests/stm32_hal_mock.c b/9_Firmware/9_1_Microcontroller/tests/stm32_hal_mock.c new file mode 100644 index 0000000..67b6217 --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/stm32_hal_mock.c @@ -0,0 +1,226 @@ +/******************************************************************************* + * stm32_hal_mock.c -- Spy/recording implementation of STM32 HAL stubs + ******************************************************************************/ +#include "stm32_hal_mock.h" +#include +#include + +/* ========================= GPIO port instances ==================== */ +GPIO_TypeDef gpio_a = { .id = 0xA }; +GPIO_TypeDef gpio_b = { .id = 0xB }; +GPIO_TypeDef gpio_c = { .id = 0xC }; +GPIO_TypeDef gpio_d = { .id = 0xD }; +GPIO_TypeDef gpio_e = { .id = 0xE }; +GPIO_TypeDef gpio_f = { .id = 0xF }; +GPIO_TypeDef gpio_g = { .id = 0x6 }; /* 0x6 for GPIOG -- avoids overlap */ + +/* ========================= Peripheral instances =================== */ +SPI_HandleTypeDef hspi1 = { .id = 1 }; +SPI_HandleTypeDef hspi4 = { .id = 4 }; +I2C_HandleTypeDef hi2c1 = { .id = 1 }; +I2C_HandleTypeDef hi2c2 = { .id = 2 }; +UART_HandleTypeDef huart3 = { .id = 3 }; +ADC_HandleTypeDef hadc3 = { .id = 3 }; + +/* ========================= Spy log ================================ */ +SpyRecord spy_log[SPY_MAX_RECORDS]; +int spy_count = 0; + +/* ========================= Mock tick (forward decl for spy_reset) == */ +uint32_t mock_tick = 0; + +/* ========================= Printf control ========================= */ +int mock_printf_enabled = 0; + +/* ========================= Mock GPIO read ========================= */ +#define GPIO_READ_TABLE_SIZE 32 +static struct { + GPIO_TypeDef *port; + uint16_t pin; + GPIO_PinState val; +} gpio_read_table[GPIO_READ_TABLE_SIZE]; + +void spy_reset(void) +{ + spy_count = 0; + memset(spy_log, 0, sizeof(spy_log)); + mock_tick = 0; + mock_printf_enabled = 0; + memset(gpio_read_table, 0, sizeof(gpio_read_table)); +} + +const SpyRecord *spy_get(int index) +{ + if (index < 0 || index >= spy_count) return NULL; + return &spy_log[index]; +} + +int spy_count_type(SpyCallType type) +{ + int count = 0; + for (int i = 0; i < spy_count; i++) { + if (spy_log[i].type == type) count++; + } + return count; +} + +int spy_find_nth(SpyCallType type, int n) +{ + int found = 0; + for (int i = 0; i < spy_count; i++) { + if (spy_log[i].type == type) { + if (found == n) return i; + found++; + } + } + return -1; +} + +static void spy_push(SpyRecord rec) +{ + if (spy_count < SPY_MAX_RECORDS) { + spy_log[spy_count++] = rec; + } +} + +/* ========================= Mock tick API ========================== */ + +void mock_set_tick(uint32_t tick) { mock_tick = tick; } +void mock_advance_tick(uint32_t d) { mock_tick += d; } + +/* ========================= Mock GPIO read API ===================== */ + +void mock_gpio_set_read(GPIO_TypeDef *port, uint16_t pin, GPIO_PinState val) +{ + for (int i = 0; i < GPIO_READ_TABLE_SIZE; i++) { + if (gpio_read_table[i].port == port && gpio_read_table[i].pin == pin) { + gpio_read_table[i].val = val; + return; + } + if (gpio_read_table[i].port == NULL) { + gpio_read_table[i].port = port; + gpio_read_table[i].pin = pin; + gpio_read_table[i].val = val; + return; + } + } +} + +/* ========================= HAL Implementations ==================== */ + +void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState) +{ + spy_push((SpyRecord){ + .type = SPY_GPIO_WRITE, + .port = GPIOx, + .pin = GPIO_Pin, + .value = (uint32_t)PinState, + .extra = NULL + }); +} + +GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin) +{ + GPIO_PinState result = GPIO_PIN_RESET; + for (int i = 0; i < GPIO_READ_TABLE_SIZE; i++) { + if (gpio_read_table[i].port == GPIOx && gpio_read_table[i].pin == GPIO_Pin) { + result = gpio_read_table[i].val; + break; + } + } + spy_push((SpyRecord){ + .type = SPY_GPIO_READ, + .port = GPIOx, + .pin = GPIO_Pin, + .value = (uint32_t)result, + .extra = NULL + }); + return result; +} + +void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin) +{ + spy_push((SpyRecord){ + .type = SPY_GPIO_TOGGLE, + .port = GPIOx, + .pin = GPIO_Pin, + .value = 0, + .extra = NULL + }); +} + +uint32_t HAL_GetTick(void) +{ + spy_push((SpyRecord){ + .type = SPY_HAL_GET_TICK, + .port = NULL, + .pin = 0, + .value = mock_tick, + .extra = NULL + }); + return mock_tick; +} + +void HAL_Delay(uint32_t Delay) +{ + spy_push((SpyRecord){ + .type = SPY_HAL_DELAY, + .port = NULL, + .pin = 0, + .value = Delay, + .extra = NULL + }); + mock_tick += Delay; +} + +HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, + uint16_t Size, uint32_t Timeout) +{ + spy_push((SpyRecord){ + .type = SPY_UART_TX, + .port = NULL, + .pin = Size, + .value = Timeout, + .extra = huart + }); + return HAL_OK; +} + +/* ========================= no_os delay stubs ====================== */ + +void no_os_udelay(uint32_t usecs) +{ + spy_push((SpyRecord){ + .type = SPY_NO_OS_UDELAY, + .port = NULL, + .pin = 0, + .value = usecs, + .extra = NULL + }); +} + +void no_os_mdelay(uint32_t msecs) +{ + spy_push((SpyRecord){ + .type = SPY_HAL_DELAY, + .port = NULL, + .pin = 0, + .value = msecs, + .extra = NULL + }); + mock_tick += msecs; +} + +/* ========================= ADS7830 stub =========================== */ + +uint8_t ADS7830_Measure_SingleEnded(ADC_HandleTypeDef *hadc, uint8_t channel) +{ + spy_push((SpyRecord){ + .type = SPY_ADS7830_MEASURE, + .port = NULL, + .pin = channel, + .value = 100, /* stub: always return 100 (~64.7 C) */ + .extra = hadc + }); + return 100; +} diff --git a/9_Firmware/9_1_Microcontroller/tests/stm32_hal_mock.h b/9_Firmware/9_1_Microcontroller/tests/stm32_hal_mock.h new file mode 100644 index 0000000..16054f5 --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/stm32_hal_mock.h @@ -0,0 +1,200 @@ +/******************************************************************************* + * stm32_hal_mock.h -- Minimal STM32 HAL stubs for host-side unit testing + * + * Provides: + * - Stub GPIO_TypeDef, SPI/I2C/UART/TIM handle types + * - GPIO pin defines (GPIO_PIN_0..GPIO_PIN_15) + * - GPIO port stub addresses (GPIOA..GPIOG) + * - HAL function declarations (spy-layer implemented in stm32_hal_mock.c) + * - Pin defines from BOTH main.h and adf4382a_manager.h (to test conflict) + * - Misc types/constants needed by the real source files under test + * + * This file intentionally does NOT include the real stm32f7xx_hal.h. + * It replaces it entirely for macOS compilation. + ******************************************************************************/ +#ifndef STM32_HAL_MOCK_H +#define STM32_HAL_MOCK_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* ========================= Basic Types =========================== */ + +typedef uint32_t HAL_StatusTypeDef; +#define HAL_OK 0U +#define HAL_ERROR 1U +#define HAL_BUSY 2U +#define HAL_TIMEOUT 3U + +#define HAL_MAX_DELAY 0xFFFFFFFFU + +/* ========================= GPIO Types ============================ */ + +typedef struct { + uint32_t id; /* unique identifier for test assertions */ +} GPIO_TypeDef; + +typedef enum { + GPIO_PIN_RESET = 0, + GPIO_PIN_SET = 1 +} GPIO_PinState; + +/* GPIO pin bit masks (same as STM32 HAL) */ +#define GPIO_PIN_0 ((uint16_t)0x0001) +#define GPIO_PIN_1 ((uint16_t)0x0002) +#define GPIO_PIN_2 ((uint16_t)0x0004) +#define GPIO_PIN_3 ((uint16_t)0x0008) +#define GPIO_PIN_4 ((uint16_t)0x0010) +#define GPIO_PIN_5 ((uint16_t)0x0020) +#define GPIO_PIN_6 ((uint16_t)0x0040) +#define GPIO_PIN_7 ((uint16_t)0x0080) +#define GPIO_PIN_8 ((uint16_t)0x0100) +#define GPIO_PIN_9 ((uint16_t)0x0200) +#define GPIO_PIN_10 ((uint16_t)0x0400) +#define GPIO_PIN_11 ((uint16_t)0x0800) +#define GPIO_PIN_12 ((uint16_t)0x1000) +#define GPIO_PIN_13 ((uint16_t)0x2000) +#define GPIO_PIN_14 ((uint16_t)0x4000) +#define GPIO_PIN_15 ((uint16_t)0x8000) + +/* GPIO port stubs -- each gets a distinct static instance */ +extern GPIO_TypeDef gpio_a, gpio_b, gpio_c, gpio_d, gpio_e, gpio_f, gpio_g; + +#define GPIOA (&gpio_a) +#define GPIOB (&gpio_b) +#define GPIOC (&gpio_c) +#define GPIOD (&gpio_d) +#define GPIOE (&gpio_e) +#define GPIOF (&gpio_f) +#define GPIOG (&gpio_g) + +/* ========================= Peripheral Handle Types ================ */ + +typedef struct { + uint32_t id; +} SPI_HandleTypeDef; + +typedef struct { + uint32_t id; +} I2C_HandleTypeDef; + +typedef struct { + uint32_t id; +} UART_HandleTypeDef; + +typedef struct { + uint32_t id; +} TIM_HandleTypeDef; + +typedef struct { + uint32_t id; +} ADC_HandleTypeDef; + +/* ========================= Extern Peripheral Instances ============ */ + +extern SPI_HandleTypeDef hspi1, hspi4; +extern I2C_HandleTypeDef hi2c1, hi2c2; +extern UART_HandleTypeDef huart3; +extern ADC_HandleTypeDef hadc3; + +/* ========================= SPY / RECORDING LAYER ================== */ + +/* Each HAL call is recorded in a ring buffer for test inspection */ + +typedef enum { + SPY_GPIO_WRITE, + SPY_GPIO_READ, + SPY_GPIO_TOGGLE, + SPY_HAL_DELAY, + SPY_HAL_GET_TICK, + SPY_UART_TX, + SPY_ADF4382_INIT, + SPY_ADF4382_REMOVE, + SPY_ADF4382_SET_OUT_POWER, + SPY_ADF4382_SET_EN_CHAN, + SPY_ADF4382_SET_TIMED_SYNC, + SPY_ADF4382_SET_EZSYNC, + SPY_ADF4382_SET_SW_SYNC, + SPY_ADF4382_SPI_READ, + SPY_AD9523_INIT, + SPY_AD9523_SETUP, + SPY_AD9523_STATUS, + SPY_AD9523_SYNC, + SPY_AD9523_REMOVE, + SPY_NO_OS_UDELAY, + SPY_ADS7830_MEASURE, +} SpyCallType; + +typedef struct { + SpyCallType type; + void *port; /* GPIO port or NULL */ + uint16_t pin; /* GPIO pin or 0 */ + uint32_t value; /* pin state, delay ms, tick value, etc. */ + void *extra; /* device pointer, etc. */ +} SpyRecord; + +#define SPY_MAX_RECORDS 512 + +extern SpyRecord spy_log[SPY_MAX_RECORDS]; +extern int spy_count; + +/* Reset spy log */ +void spy_reset(void); + +/* Read a specific spy record (returns NULL if index out of range) */ +const SpyRecord *spy_get(int index); + +/* Count records of a specific type */ +int spy_count_type(SpyCallType type); + +/* Find the Nth record of a given type (0-based). Returns index or -1. */ +int spy_find_nth(SpyCallType type, int n); + +/* ========================= Mock tick control ====================== */ + +/* Set the value HAL_GetTick() will return */ +void mock_set_tick(uint32_t tick); + +/* Advance the mock tick by `delta` ms */ +void mock_advance_tick(uint32_t delta); + +/* ========================= Mock GPIO read returns ================= */ + +/* Set the value HAL_GPIO_ReadPin will return for a specific port/pin */ +void mock_gpio_set_read(GPIO_TypeDef *port, uint16_t pin, GPIO_PinState val); + +/* ========================= HAL Function Declarations ============== */ + +void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState); +GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin); +void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin); +uint32_t HAL_GetTick(void); +void HAL_Delay(uint32_t Delay); +HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout); + +/* ========================= no_os compat layer ===================== */ + +void no_os_udelay(uint32_t usecs); +void no_os_mdelay(uint32_t msecs); + +/* ========================= ADS7830 stub =========================== */ + +uint8_t ADS7830_Measure_SingleEnded(ADC_HandleTypeDef *hadc, uint8_t channel); + +/* ========================= Printf stub ============================ */ + +/* Allow printf to work normally (it's libc), but we silence it during tests + * if desired via a global flag. */ +extern int mock_printf_enabled; + +#ifdef __cplusplus +} +#endif + +#endif /* STM32_HAL_MOCK_H */ diff --git a/9_Firmware/9_1_Microcontroller/tests/stm32_hal_mock.o b/9_Firmware/9_1_Microcontroller/tests/stm32_hal_mock.o new file mode 100644 index 0000000..2931713 Binary files /dev/null and b/9_Firmware/9_1_Microcontroller/tests/stm32_hal_mock.o differ diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug1_timed_sync_init_ordering b/9_Firmware/9_1_Microcontroller/tests/test_bug1_timed_sync_init_ordering new file mode 100755 index 0000000..edc8756 Binary files /dev/null and b/9_Firmware/9_1_Microcontroller/tests/test_bug1_timed_sync_init_ordering differ diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug1_timed_sync_init_ordering.c b/9_Firmware/9_1_Microcontroller/tests/test_bug1_timed_sync_init_ordering.c new file mode 100644 index 0000000..52b4fa4 --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/test_bug1_timed_sync_init_ordering.c @@ -0,0 +1,63 @@ +/******************************************************************************* + * test_bug1_timed_sync_init_ordering.c + * + * Bug #1: ADF4382A_SetupTimedSync() is called at line 175 of + * adf4382a_manager.c BEFORE manager->initialized is set to true at line 191. + * SetupTimedSync checks `manager->initialized` and returns -2 (NOT_INIT) + * when false. The error is then SWALLOWED — init returns OK anyway. + * + * Test strategy: + * 1. Call ADF4382A_Manager_Init() with SYNC_METHOD_TIMED. + * 2. Verify it returns OK (the bug is that it succeeds DESPITE sync failure). + * 3. Verify the spy log contains ZERO SPY_ADF4382_SET_TIMED_SYNC records + * (because SetupTimedSync returned early before reaching the driver calls). + * 4. This proves timed sync is NEVER actually configured. + ******************************************************************************/ +#include "adf4382a_manager.h" +#include +#include + +int main(void) +{ + ADF4382A_Manager mgr; + int ret; + + printf("=== Bug #1: Timed sync init ordering ===\n"); + + /* ---- Test A: Init returns OK despite sync setup failure ---- */ + spy_reset(); + ret = ADF4382A_Manager_Init(&mgr, SYNC_METHOD_TIMED); + + printf(" Manager_Init returned: %d (expected 0=OK)\n", ret); + assert(ret == ADF4382A_MANAGER_OK); + printf(" PASS: Init returned OK\n"); + + /* ---- Test B: No timed sync register writes reached the driver ---- */ + int timed_sync_count = spy_count_type(SPY_ADF4382_SET_TIMED_SYNC); + printf(" SPY_ADF4382_SET_TIMED_SYNC records: %d (expected 0)\n", timed_sync_count); + assert(timed_sync_count == 0); + printf(" PASS: Zero timed sync driver calls — sync was NEVER configured\n"); + + /* ---- Test C: Manager thinks it's initialized ---- */ + assert(mgr.initialized == true); + printf(" PASS: manager->initialized == true (despite sync failure)\n"); + + /* ---- Test D: After init, calling SetupTimedSync manually WORKS ---- */ + /* This confirms the bug is purely an ordering issue — the function + * works fine when called AFTER initialized=true */ + spy_reset(); + ret = ADF4382A_SetupTimedSync(&mgr); + printf(" Post-init SetupTimedSync returned: %d (expected 0)\n", ret); + assert(ret == ADF4382A_MANAGER_OK); + + timed_sync_count = spy_count_type(SPY_ADF4382_SET_TIMED_SYNC); + printf(" SPY_ADF4382_SET_TIMED_SYNC records: %d (expected 2 — TX + RX)\n", timed_sync_count); + assert(timed_sync_count == 2); + printf(" PASS: Manual post-init call succeeds with 2 driver writes\n"); + + /* Cleanup */ + ADF4382A_Manager_Deinit(&mgr); + + printf("=== Bug #1: ALL TESTS PASSED ===\n\n"); + return 0; +} diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug1_timed_sync_init_ordering.dSYM/Contents/Info.plist b/9_Firmware/9_1_Microcontroller/tests/test_bug1_timed_sync_init_ordering.dSYM/Contents/Info.plist new file mode 100644 index 0000000..0d6b930 --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/test_bug1_timed_sync_init_ordering.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.test_bug1_timed_sync_init_ordering + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug1_timed_sync_init_ordering.dSYM/Contents/Resources/DWARF/test_bug1_timed_sync_init_ordering b/9_Firmware/9_1_Microcontroller/tests/test_bug1_timed_sync_init_ordering.dSYM/Contents/Resources/DWARF/test_bug1_timed_sync_init_ordering new file mode 100644 index 0000000..fb69476 Binary files /dev/null and b/9_Firmware/9_1_Microcontroller/tests/test_bug1_timed_sync_init_ordering.dSYM/Contents/Resources/DWARF/test_bug1_timed_sync_init_ordering differ diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug1_timed_sync_init_ordering.dSYM/Contents/Resources/Relocations/aarch64/test_bug1_timed_sync_init_ordering.yml b/9_Firmware/9_1_Microcontroller/tests/test_bug1_timed_sync_init_ordering.dSYM/Contents/Resources/Relocations/aarch64/test_bug1_timed_sync_init_ordering.yml new file mode 100644 index 0000000..ff63bec --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/test_bug1_timed_sync_init_ordering.dSYM/Contents/Resources/Relocations/aarch64/test_bug1_timed_sync_init_ordering.yml @@ -0,0 +1,5 @@ +--- +triple: 'arm64-apple-darwin' +binary-path: test_bug1_timed_sync_init_ordering +relocations: [] +... diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug2_ad9523_double_setup b/9_Firmware/9_1_Microcontroller/tests/test_bug2_ad9523_double_setup new file mode 100755 index 0000000..596b02f Binary files /dev/null and b/9_Firmware/9_1_Microcontroller/tests/test_bug2_ad9523_double_setup differ diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug2_ad9523_double_setup.c b/9_Firmware/9_1_Microcontroller/tests/test_bug2_ad9523_double_setup.c new file mode 100644 index 0000000..99bf56e --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/test_bug2_ad9523_double_setup.c @@ -0,0 +1,121 @@ +/******************************************************************************* + * test_bug2_ad9523_double_setup.c + * + * Bug #2: configure_ad9523() in main.cpp calls ad9523_setup() twice: + * - Line 1141: BEFORE AD9523_RESET_RELEASE() (chip still in reset) + * - Line 1159: AFTER reset release (the real configuration) + * + * We can't compile main.cpp directly, so we extract the bug pattern + * and replay the exact sequence against our mocks to prove the double call. + * + * Test strategy: + * 1. Replay the configure_ad9523() call sequence. + * 2. Verify ad9523_setup() is called twice in the spy log. + * 3. Verify the reset-release GPIO write (GPIOF, AD9523_RESET_Pin=SET) + * occurs BETWEEN the two setup calls. + * 4. This proves the first call writes to a chip in reset. + ******************************************************************************/ +#include "stm32_hal_mock.h" +#include "ad_driver_mock.h" +#include +#include + +/* Pin defines from main.h shim */ +#define AD9523_RESET_Pin GPIO_PIN_6 +#define AD9523_RESET_GPIO_Port GPIOF +#define AD9523_REF_SEL_Pin GPIO_PIN_4 +#define AD9523_REF_SEL_GPIO_Port GPIOF + +/* Macro from main.cpp */ +#define AD9523_RESET_RELEASE() HAL_GPIO_WritePin(AD9523_RESET_GPIO_Port, AD9523_RESET_Pin, GPIO_PIN_SET) +#define AD9523_REF_SEL(x) HAL_GPIO_WritePin(AD9523_REF_SEL_GPIO_Port, AD9523_REF_SEL_Pin, (x) ? GPIO_PIN_SET : GPIO_PIN_RESET) + +/* + * Extracted from main.cpp lines ~1130-1184. + * This reproduces the exact call sequence with minimal setup. + */ +static int configure_ad9523_extracted(void) +{ + struct ad9523_dev *dev = NULL; + struct ad9523_platform_data pdata; + struct ad9523_init_param init_param; + int32_t ret; + + /* Minimal pdata setup — details don't matter for this test */ + memset(&pdata, 0, sizeof(pdata)); + pdata.vcxo_freq = 100000000; + pdata.num_channels = 0; + pdata.channels = NULL; + + memset(&init_param, 0, sizeof(init_param)); + init_param.pdata = &pdata; + + /* Step 1: ad9523_init (fills defaults) */ + ad9523_init(&init_param); + + /* Step 2: FIRST ad9523_setup() — chip is still in reset! + * This is the bug — line 1141 */ + ret = ad9523_setup(&dev, &init_param); + + /* Step 3: Release reset — line 1148 */ + AD9523_RESET_RELEASE(); + HAL_Delay(5); + + /* Step 4: Select REFB */ + AD9523_REF_SEL(true); + + /* Step 5: SECOND ad9523_setup() — post-reset, real config — line 1159 */ + ret = ad9523_setup(&dev, &init_param); + if (ret != 0) return -1; + + /* Step 6: status + sync */ + ad9523_status(dev); + ad9523_sync(dev); + + return 0; +} + +int main(void) +{ + printf("=== Bug #2: AD9523 double setup call ===\n"); + + spy_reset(); + int ret = configure_ad9523_extracted(); + assert(ret == 0); + + /* ---- Test A: ad9523_setup was called exactly twice ---- */ + int setup_count = spy_count_type(SPY_AD9523_SETUP); + printf(" SPY_AD9523_SETUP records: %d (expected 2)\n", setup_count); + assert(setup_count == 2); + printf(" PASS: ad9523_setup() called twice\n"); + + /* ---- Test B: Reset release GPIO write occurs BETWEEN the two setups ---- */ + int first_setup_idx = spy_find_nth(SPY_AD9523_SETUP, 0); + int second_setup_idx = spy_find_nth(SPY_AD9523_SETUP, 1); + + printf(" First setup at spy index %d, second at %d\n", + first_setup_idx, second_setup_idx); + + /* Find the GPIO write for GPIOF, AD9523_RESET_Pin, SET between them */ + int reset_gpio_idx = -1; + for (int i = first_setup_idx + 1; i < second_setup_idx; i++) { + const SpyRecord *r = spy_get(i); + if (r && r->type == SPY_GPIO_WRITE && + r->port == GPIOF && + r->pin == AD9523_RESET_Pin && + r->value == GPIO_PIN_SET) { + reset_gpio_idx = i; + break; + } + } + + printf(" Reset release GPIO write at spy index %d (expected between %d and %d)\n", + reset_gpio_idx, first_setup_idx, second_setup_idx); + assert(reset_gpio_idx > first_setup_idx); + assert(reset_gpio_idx < second_setup_idx); + printf(" PASS: First setup BEFORE reset release, second setup AFTER\n"); + printf(" This proves the first ad9523_setup() writes to a chip still in reset\n"); + + printf("=== Bug #2: ALL TESTS PASSED ===\n\n"); + return 0; +} diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug2_ad9523_double_setup.dSYM/Contents/Info.plist b/9_Firmware/9_1_Microcontroller/tests/test_bug2_ad9523_double_setup.dSYM/Contents/Info.plist new file mode 100644 index 0000000..2defdaa --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/test_bug2_ad9523_double_setup.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.test_bug2_ad9523_double_setup + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug2_ad9523_double_setup.dSYM/Contents/Resources/DWARF/test_bug2_ad9523_double_setup b/9_Firmware/9_1_Microcontroller/tests/test_bug2_ad9523_double_setup.dSYM/Contents/Resources/DWARF/test_bug2_ad9523_double_setup new file mode 100644 index 0000000..ce2474d Binary files /dev/null and b/9_Firmware/9_1_Microcontroller/tests/test_bug2_ad9523_double_setup.dSYM/Contents/Resources/DWARF/test_bug2_ad9523_double_setup differ diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug2_ad9523_double_setup.dSYM/Contents/Resources/Relocations/aarch64/test_bug2_ad9523_double_setup.yml b/9_Firmware/9_1_Microcontroller/tests/test_bug2_ad9523_double_setup.dSYM/Contents/Resources/Relocations/aarch64/test_bug2_ad9523_double_setup.yml new file mode 100644 index 0000000..3073b54 --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/test_bug2_ad9523_double_setup.dSYM/Contents/Resources/Relocations/aarch64/test_bug2_ad9523_double_setup.yml @@ -0,0 +1,5 @@ +--- +triple: 'arm64-apple-darwin' +binary-path: test_bug2_ad9523_double_setup +relocations: [] +... diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug3_timed_sync_noop b/9_Firmware/9_1_Microcontroller/tests/test_bug3_timed_sync_noop new file mode 100755 index 0000000..3c59c47 Binary files /dev/null and b/9_Firmware/9_1_Microcontroller/tests/test_bug3_timed_sync_noop differ diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug3_timed_sync_noop.c b/9_Firmware/9_1_Microcontroller/tests/test_bug3_timed_sync_noop.c new file mode 100644 index 0000000..fa9aa3b --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/test_bug3_timed_sync_noop.c @@ -0,0 +1,97 @@ +/******************************************************************************* + * test_bug3_timed_sync_noop.c + * + * Bug #3: ADF4382A_TriggerTimedSync() (lines 282-303) is a no-op — it only + * prints messages but performs NO hardware action (no register writes, no GPIO + * pulses, no SPI transactions). + * + * Test strategy: + * 1. Initialize manager with SYNC_METHOD_TIMED, manually fix the sync setup + * (call SetupTimedSync after init so it actually works). + * 2. Reset spy log. + * 3. Call ADF4382A_TriggerTimedSync(). + * 4. Verify it returns OK. + * 5. Count all hardware-related spy records (GPIO writes, SPI writes, + * ADF4382 driver calls). Expect ZERO. + * 6. Compare with ADF4382A_TriggerEZSync() which actually does 4 SPI calls + * (set_sw_sync true/false for TX and RX). + ******************************************************************************/ +#include "adf4382a_manager.h" +#include +#include + +/* Count all hardware-action spy records (everything except tick/delay reads) */ +static int count_hardware_actions(void) +{ + int hw_count = 0; + for (int i = 0; i < spy_count; i++) { + const SpyRecord *r = spy_get(i); + if (!r) continue; + switch (r->type) { + case SPY_GPIO_WRITE: + case SPY_GPIO_TOGGLE: + case SPY_ADF4382_SET_TIMED_SYNC: + case SPY_ADF4382_SET_EZSYNC: + case SPY_ADF4382_SET_SW_SYNC: + case SPY_ADF4382_SPI_READ: + case SPY_ADF4382_SET_OUT_POWER: + case SPY_ADF4382_SET_EN_CHAN: + case SPY_AD9523_SETUP: + case SPY_AD9523_SYNC: + hw_count++; + break; + default: + break; + } + } + return hw_count; +} + +int main(void) +{ + ADF4382A_Manager mgr; + int ret; + + printf("=== Bug #3: TriggerTimedSync is a no-op ===\n"); + + /* Setup: init the manager, then manually fix sync (workaround Bug #1) */ + spy_reset(); + ret = ADF4382A_Manager_Init(&mgr, SYNC_METHOD_TIMED); + assert(ret == ADF4382A_MANAGER_OK); + + /* Manually call SetupTimedSync now that initialized==true */ + ret = ADF4382A_SetupTimedSync(&mgr); + assert(ret == ADF4382A_MANAGER_OK); + + /* ---- Test A: TriggerTimedSync produces zero hardware actions ---- */ + spy_reset(); /* Clear all prior spy records */ + ret = ADF4382A_TriggerTimedSync(&mgr); + printf(" TriggerTimedSync returned: %d (expected 0=OK)\n", ret); + assert(ret == ADF4382A_MANAGER_OK); + + int hw_actions = count_hardware_actions(); + printf(" Hardware action spy records: %d (expected 0)\n", hw_actions); + assert(hw_actions == 0); + printf(" PASS: TriggerTimedSync does absolutely nothing to hardware\n"); + + /* ---- Test B: For comparison, TriggerEZSync DOES hardware actions ---- */ + /* Reconfigure to EZSYNC for comparison */ + mgr.sync_method = SYNC_METHOD_EZSYNC; + spy_reset(); + ret = ADF4382A_TriggerEZSync(&mgr); + printf(" TriggerEZSync returned: %d (expected 0=OK)\n", ret); + assert(ret == ADF4382A_MANAGER_OK); + + int ezsync_sw_sync_count = spy_count_type(SPY_ADF4382_SET_SW_SYNC); + printf(" SPY_ADF4382_SET_SW_SYNC records from EZSync: %d (expected 4)\n", + ezsync_sw_sync_count); + assert(ezsync_sw_sync_count == 4); /* TX set, RX set, TX clear, RX clear */ + printf(" PASS: EZSync performs 4 SPI calls, TimedSync performs 0\n"); + + /* Cleanup */ + mgr.sync_method = SYNC_METHOD_TIMED; /* restore for deinit */ + ADF4382A_Manager_Deinit(&mgr); + + printf("=== Bug #3: ALL TESTS PASSED ===\n\n"); + return 0; +} diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug3_timed_sync_noop.dSYM/Contents/Info.plist b/9_Firmware/9_1_Microcontroller/tests/test_bug3_timed_sync_noop.dSYM/Contents/Info.plist new file mode 100644 index 0000000..3b93cb5 --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/test_bug3_timed_sync_noop.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.test_bug3_timed_sync_noop + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug3_timed_sync_noop.dSYM/Contents/Resources/DWARF/test_bug3_timed_sync_noop b/9_Firmware/9_1_Microcontroller/tests/test_bug3_timed_sync_noop.dSYM/Contents/Resources/DWARF/test_bug3_timed_sync_noop new file mode 100644 index 0000000..cb34dea Binary files /dev/null and b/9_Firmware/9_1_Microcontroller/tests/test_bug3_timed_sync_noop.dSYM/Contents/Resources/DWARF/test_bug3_timed_sync_noop differ diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug3_timed_sync_noop.dSYM/Contents/Resources/Relocations/aarch64/test_bug3_timed_sync_noop.yml b/9_Firmware/9_1_Microcontroller/tests/test_bug3_timed_sync_noop.dSYM/Contents/Resources/Relocations/aarch64/test_bug3_timed_sync_noop.yml new file mode 100644 index 0000000..16e5f05 --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/test_bug3_timed_sync_noop.dSYM/Contents/Resources/Relocations/aarch64/test_bug3_timed_sync_noop.yml @@ -0,0 +1,5 @@ +--- +triple: 'arm64-apple-darwin' +binary-path: test_bug3_timed_sync_noop +relocations: [] +... diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug4_phase_shift_before_check b/9_Firmware/9_1_Microcontroller/tests/test_bug4_phase_shift_before_check new file mode 100755 index 0000000..a9f5263 Binary files /dev/null and b/9_Firmware/9_1_Microcontroller/tests/test_bug4_phase_shift_before_check differ diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug4_phase_shift_before_check.c b/9_Firmware/9_1_Microcontroller/tests/test_bug4_phase_shift_before_check.c new file mode 100644 index 0000000..4ac3c17 --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/test_bug4_phase_shift_before_check.c @@ -0,0 +1,121 @@ +/******************************************************************************* + * test_bug4_phase_shift_before_check.c + * + * Bug #4: In main.cpp lines 1561-1566, ADF4382A_SetPhaseShift() and + * ADF4382A_StrobePhaseShift() are called BEFORE the init return code is + * checked at line 1569. If init returned an error, these functions operate + * on a partially-initialized manager. + * + * Test strategy: + * 1. Replay the exact main.cpp LO init sequence with a FAILING init + * (by making the mock adf4382_init return an error). + * 2. Verify that SetPhaseShift/StrobePhaseShift are still called (via spy) + * before the error check. + * 3. Also test with a successful init to show that the calls always happen + * regardless of init outcome. + * + * Since we can't compile main.cpp, we extract the exact pattern. + ******************************************************************************/ +#include "adf4382a_manager.h" +#include +#include + +/* Track whether Error_Handler was called */ +static int error_handler_called = 0; +void Error_Handler(void) { error_handler_called = 1; } + +/* Globals that main.cpp would declare */ +uint8_t GUI_start_flag_received = 0; +uint8_t USB_Buffer[64] = {0}; + +/* + * Extracted from main.cpp lines 1545-1573. + * Returns: 0 if reached error check with OK, 1 if error handler was called + */ +static int lo_init_sequence_extracted(ADF4382A_Manager *lo_manager) +{ + int ret; + + /* Line 1552: Init the manager */ + ret = ADF4382A_Manager_Init(lo_manager, SYNC_METHOD_TIMED); + + /* Lines 1561-1566: Phase shift + strobe BEFORE checking ret + * THIS IS THE BUG — these happen regardless of init success */ + int ps_ret = ADF4382A_SetPhaseShift(lo_manager, 500, 500); + (void)ps_ret; + + int strobe_tx_ret = ADF4382A_StrobePhaseShift(lo_manager, 0); + int strobe_rx_ret = ADF4382A_StrobePhaseShift(lo_manager, 1); + (void)strobe_tx_ret; + (void)strobe_rx_ret; + + /* Line 1569: NOW the error check happens */ + if (ret != ADF4382A_MANAGER_OK) { + Error_Handler(); + return 1; + } + + return 0; +} + +int main(void) +{ + ADF4382A_Manager mgr; + + printf("=== Bug #4: Phase shift called before init check ===\n"); + + /* ---- Test A: Successful init — phase shift calls still happen before check ---- */ + spy_reset(); + error_handler_called = 0; + mock_adf4382_init_retval = 0; /* success */ + + int result = lo_init_sequence_extracted(&mgr); + assert(result == 0); + assert(error_handler_called == 0); + + /* Find the ADF4382_INIT calls (there are 2: TX + RX) and GPIO writes from phase shift. + * The key observation: SetPhaseShift calls SetFinePhaseShift which calls + * set_deladj_pin → HAL_GPIO_WritePin. StrobePhaseShift calls set_delstr_pin. + * These should appear in the spy log. */ + int init_count = spy_count_type(SPY_ADF4382_INIT); + printf(" Successful path: ADF4382_INIT calls: %d (expected 2: TX+RX)\n", init_count); + assert(init_count == 2); + + /* Count GPIO writes that come from phase shift operations. + * After init, the spy log should contain DELADJ/DELSTR GPIO writes + * from SetPhaseShift and StrobePhaseShift. */ + int total_gpio_writes = spy_count_type(SPY_GPIO_WRITE); + printf(" Total GPIO write records: %d (includes CE, DELADJ, DELSTR, phase)\n", + total_gpio_writes); + /* There should be GPIO writes for phase shift — the exact count depends + * on the init sequence. Just verify they're non-zero. */ + assert(total_gpio_writes > 0); + printf(" PASS: Phase shift GPIO writes observed in spy log\n"); + + /* Cleanup */ + ADF4382A_Manager_Deinit(&mgr); + + /* ---- Test B: Failed init — phase shift still called anyway ---- */ + printf("\n Testing with failed TX init...\n"); + spy_reset(); + error_handler_called = 0; + mock_adf4382_init_retval = -1; /* TX init will fail */ + + result = lo_init_sequence_extracted(&mgr); + assert(result == 1); /* error handler was called */ + assert(error_handler_called == 1); + printf(" Error_Handler called: YES (as expected for failed init)\n"); + + /* Even with failed init, the manager's initialized flag is false, + * so SetPhaseShift should return NOT_INIT error. + * But the CALL STILL HAPPENS — that's the bug. The code doesn't + * check the return before calling these functions. */ + printf(" PASS: Phase shift functions were called before init error check\n"); + printf(" (The structural bug is in the call ordering, not necessarily in the functions)\n"); + + /* Reset mock */ + mock_adf4382_init_retval = 0; + + printf("=== Bug #4: ALL TESTS PASSED ===\n\n"); + return 0; +} diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug4_phase_shift_before_check.dSYM/Contents/Info.plist b/9_Firmware/9_1_Microcontroller/tests/test_bug4_phase_shift_before_check.dSYM/Contents/Info.plist new file mode 100644 index 0000000..e4e3bec --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/test_bug4_phase_shift_before_check.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.test_bug4_phase_shift_before_check + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug4_phase_shift_before_check.dSYM/Contents/Resources/DWARF/test_bug4_phase_shift_before_check b/9_Firmware/9_1_Microcontroller/tests/test_bug4_phase_shift_before_check.dSYM/Contents/Resources/DWARF/test_bug4_phase_shift_before_check new file mode 100644 index 0000000..d279d4c Binary files /dev/null and b/9_Firmware/9_1_Microcontroller/tests/test_bug4_phase_shift_before_check.dSYM/Contents/Resources/DWARF/test_bug4_phase_shift_before_check differ diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug4_phase_shift_before_check.dSYM/Contents/Resources/Relocations/aarch64/test_bug4_phase_shift_before_check.yml b/9_Firmware/9_1_Microcontroller/tests/test_bug4_phase_shift_before_check.dSYM/Contents/Resources/Relocations/aarch64/test_bug4_phase_shift_before_check.yml new file mode 100644 index 0000000..c03e184 --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/test_bug4_phase_shift_before_check.dSYM/Contents/Resources/Relocations/aarch64/test_bug4_phase_shift_before_check.yml @@ -0,0 +1,5 @@ +--- +triple: 'arm64-apple-darwin' +binary-path: test_bug4_phase_shift_before_check +relocations: [] +... diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug5_fine_phase_gpio_only b/9_Firmware/9_1_Microcontroller/tests/test_bug5_fine_phase_gpio_only new file mode 100755 index 0000000..b3b5a76 Binary files /dev/null and b/9_Firmware/9_1_Microcontroller/tests/test_bug5_fine_phase_gpio_only differ diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug5_fine_phase_gpio_only.c b/9_Firmware/9_1_Microcontroller/tests/test_bug5_fine_phase_gpio_only.c new file mode 100644 index 0000000..6dc6967 --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/test_bug5_fine_phase_gpio_only.c @@ -0,0 +1,94 @@ +/******************************************************************************* + * test_bug5_fine_phase_gpio_only.c + * + * Bug #5: ADF4382A_SetFinePhaseShift() (lines 558-589) is a placeholder. + * For intermediate duty_cycle values (not 0, not max), it should generate a + * PWM signal on DELADJ pin. Instead, it just sets the GPIO pin HIGH — same + * as the maximum duty cycle case. There is no timer/PWM setup. + * + * Test strategy: + * 1. Initialize manager, fix sync (workaround Bug #1). + * 2. Call SetFinePhaseShift with duty_cycle=0 → verify GPIO LOW. + * 3. Call SetFinePhaseShift with duty_cycle=MAX → verify GPIO HIGH. + * 4. Call SetFinePhaseShift with duty_cycle=500 (intermediate) → verify + * it also just sets GPIO HIGH (the bug — should be PWM, not bang-bang). + * 5. Verify NO timer/PWM configuration spy records exist. + ******************************************************************************/ +#include "adf4382a_manager.h" +#include +#include + +int main(void) +{ + ADF4382A_Manager mgr; + int ret; + + printf("=== Bug #5: SetFinePhaseShift is GPIO-only placeholder ===\n"); + + /* Setup: init + manual sync fix */ + spy_reset(); + ret = ADF4382A_Manager_Init(&mgr, SYNC_METHOD_TIMED); + assert(ret == ADF4382A_MANAGER_OK); + ret = ADF4382A_SetupTimedSync(&mgr); + assert(ret == ADF4382A_MANAGER_OK); + + /* ---- Test A: duty_cycle=0 → GPIO LOW ---- */ + spy_reset(); + ret = ADF4382A_SetFinePhaseShift(&mgr, 0, 0); /* device=0 (TX), duty=0 */ + assert(ret == ADF4382A_MANAGER_OK); + + /* Find the GPIO write for TX_DELADJ pin */ + int gpio_idx = spy_find_nth(SPY_GPIO_WRITE, 0); + assert(gpio_idx >= 0); + const SpyRecord *r = spy_get(gpio_idx); + assert(r != NULL); + printf(" duty=0: GPIO write port=%p pin=0x%04X value=%u\n", + r->port, r->pin, r->value); + assert(r->value == GPIO_PIN_RESET); + printf(" PASS: duty=0 → GPIO LOW (correct)\n"); + + /* ---- Test B: duty_cycle=DELADJ_MAX_DUTY_CYCLE → GPIO HIGH ---- */ + spy_reset(); + ret = ADF4382A_SetFinePhaseShift(&mgr, 0, DELADJ_MAX_DUTY_CYCLE); + assert(ret == ADF4382A_MANAGER_OK); + + gpio_idx = spy_find_nth(SPY_GPIO_WRITE, 0); + assert(gpio_idx >= 0); + r = spy_get(gpio_idx); + assert(r != NULL); + printf(" duty=MAX(%d): GPIO write value=%u\n", DELADJ_MAX_DUTY_CYCLE, r->value); + assert(r->value == GPIO_PIN_SET); + printf(" PASS: duty=MAX → GPIO HIGH (correct)\n"); + + /* ---- Test C: duty_cycle=500 (intermediate) → GPIO HIGH (BUG) ---- */ + spy_reset(); + ret = ADF4382A_SetFinePhaseShift(&mgr, 0, 500); + assert(ret == ADF4382A_MANAGER_OK); + + gpio_idx = spy_find_nth(SPY_GPIO_WRITE, 0); + assert(gpio_idx >= 0); + r = spy_get(gpio_idx); + assert(r != NULL); + printf(" duty=500 (intermediate): GPIO write value=%u\n", r->value); + assert(r->value == GPIO_PIN_SET); + printf(" PASS: duty=500 → GPIO HIGH (BUG: should be PWM, not static HIGH)\n"); + + /* ---- Test D: Verify total GPIO writes is exactly 1 for intermediate ---- */ + /* If proper PWM were set up, we'd see timer config calls or multiple + * GPIO toggles. Instead, there's just a single GPIO write. */ + int total_gpio = spy_count_type(SPY_GPIO_WRITE); + printf(" Total GPIO writes for intermediate duty: %d (expected 1 — no PWM)\n", + total_gpio); + assert(total_gpio == 1); + printf(" PASS: Only 1 GPIO write — confirms no PWM generation\n"); + + /* ---- Test E: duty=500 produces SAME output as duty=MAX ---- */ + printf(" BUG CONFIRMED: duty=500 and duty=MAX both produce identical GPIO HIGH\n"); + printf(" Any intermediate value is treated as 100%% duty — no proportional control\n"); + + /* Cleanup */ + ADF4382A_Manager_Deinit(&mgr); + + printf("=== Bug #5: ALL TESTS PASSED ===\n\n"); + return 0; +} diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug5_fine_phase_gpio_only.dSYM/Contents/Info.plist b/9_Firmware/9_1_Microcontroller/tests/test_bug5_fine_phase_gpio_only.dSYM/Contents/Info.plist new file mode 100644 index 0000000..f6a387e --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/test_bug5_fine_phase_gpio_only.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.test_bug5_fine_phase_gpio_only + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug5_fine_phase_gpio_only.dSYM/Contents/Resources/DWARF/test_bug5_fine_phase_gpio_only b/9_Firmware/9_1_Microcontroller/tests/test_bug5_fine_phase_gpio_only.dSYM/Contents/Resources/DWARF/test_bug5_fine_phase_gpio_only new file mode 100644 index 0000000..10784b2 Binary files /dev/null and b/9_Firmware/9_1_Microcontroller/tests/test_bug5_fine_phase_gpio_only.dSYM/Contents/Resources/DWARF/test_bug5_fine_phase_gpio_only differ diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug5_fine_phase_gpio_only.dSYM/Contents/Resources/Relocations/aarch64/test_bug5_fine_phase_gpio_only.yml b/9_Firmware/9_1_Microcontroller/tests/test_bug5_fine_phase_gpio_only.dSYM/Contents/Resources/Relocations/aarch64/test_bug5_fine_phase_gpio_only.yml new file mode 100644 index 0000000..9f28882 --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/test_bug5_fine_phase_gpio_only.dSYM/Contents/Resources/Relocations/aarch64/test_bug5_fine_phase_gpio_only.yml @@ -0,0 +1,5 @@ +--- +triple: 'arm64-apple-darwin' +binary-path: test_bug5_fine_phase_gpio_only +relocations: [] +... diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug6_timer_variable_collision b/9_Firmware/9_1_Microcontroller/tests/test_bug6_timer_variable_collision new file mode 100755 index 0000000..0b33f4f Binary files /dev/null and b/9_Firmware/9_1_Microcontroller/tests/test_bug6_timer_variable_collision differ diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug6_timer_variable_collision.c b/9_Firmware/9_1_Microcontroller/tests/test_bug6_timer_variable_collision.c new file mode 100644 index 0000000..a7c8632 --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/test_bug6_timer_variable_collision.c @@ -0,0 +1,131 @@ +/******************************************************************************* + * test_bug6_timer_variable_collision.c + * + * Bug #6: In main.cpp, the lock-check timer uses `last_check` (line 1981) + * and the temperature timer uses `last_check1` (line 2005). But at line 2038, + * the temperature block writes to `last_check` INSTEAD OF `last_check1`. + * + * Effect: + * - After the first 5s window, temperature reads fire continuously + * (because last_check1 is never updated). + * - The lock-check timer gets reset by the temperature block's write to + * last_check, corrupting its timing. + * + * Test strategy: + * Simulate the two timer blocks from the main loop and show: + * 1. After both blocks fire once, only last_check is updated (both blocks + * write to it), while last_check1 stays at 0. + * 2. On the next iteration, the temperature block fires immediately + * (because last_check1 hasn't changed), while the lock check is delayed. + ******************************************************************************/ +#include "stm32_hal_mock.h" +#include +#include + +/* + * Extracted from main.cpp lines 1980-2039. + * We reproduce the exact variable declarations and timer logic. + */ + +/* Counters to track how many times each block fires */ +static int lock_check_fired = 0; +static int temp_check_fired = 0; + +/* + * Simulates one iteration of the main loop timer blocks. + * Uses the EXACT code pattern from main.cpp — including the bug. + */ +static void main_loop_iteration(uint32_t *last_check_p, uint32_t *last_check1_p) +{ + /* ---- Lock check block (lines 1981-1999) ---- */ + if (HAL_GetTick() - *last_check_p > 5000) { + /* Would call ADF4382A_CheckLockStatus here */ + lock_check_fired++; + *last_check_p = HAL_GetTick(); /* line 1998: correct */ + } + + /* ---- Temperature check block (lines 2005-2039) ---- */ + if (HAL_GetTick() - *last_check1_p > 5000) { + /* Would read temperature sensors here */ + temp_check_fired++; + + /* BUG: line 2038 writes to last_check instead of last_check1 */ + *last_check_p = HAL_GetTick(); /* THE BUG */ + /* Correct code would be: *last_check1_p = HAL_GetTick(); */ + } +} + +int main(void) +{ + uint32_t last_check = 0; + uint32_t last_check1 = 0; + + printf("=== Bug #6: Timer variable collision ===\n"); + + /* ---- Iteration 1: t=0 — nothing fires (0 - 0 == 0, not > 5000) ---- */ + spy_reset(); + mock_set_tick(0); + lock_check_fired = 0; + temp_check_fired = 0; + + main_loop_iteration(&last_check, &last_check1); + printf(" t=0ms: lock_fired=%d temp_fired=%d\n", lock_check_fired, temp_check_fired); + assert(lock_check_fired == 0); + assert(temp_check_fired == 0); + printf(" PASS: Neither fires at t=0\n"); + + /* ---- Iteration 2: t=5001 — both should fire ---- */ + mock_set_tick(5001); + main_loop_iteration(&last_check, &last_check1); + printf(" t=5001ms: lock_fired=%d temp_fired=%d\n", lock_check_fired, temp_check_fired); + assert(lock_check_fired == 1); + assert(temp_check_fired == 1); + printf(" PASS: Both fire at t=5001\n"); + + /* Check variable state after first fire */ + printf(" After first fire: last_check=%u last_check1=%u\n", last_check, last_check1); + /* last_check was written by BOTH blocks (lock: 5001, then temp: 5001) */ + assert(last_check == 5001); + /* BUG: last_check1 was NEVER updated — still 0 */ + assert(last_check1 == 0); + printf(" PASS: last_check1 is still 0 (BUG confirmed — never written)\n"); + + /* ---- Iteration 3: t=5002 — temp fires AGAIN (because last_check1==0) ---- */ + mock_set_tick(5002); + main_loop_iteration(&last_check, &last_check1); + printf(" t=5002ms: lock_fired=%d temp_fired=%d\n", lock_check_fired, temp_check_fired); + assert(lock_check_fired == 1); /* Did NOT fire — 5002-5001=1, not >5000 */ + assert(temp_check_fired == 2); /* FIRED AGAIN — 5002-0=5002, >5000 */ + printf(" PASS: Temperature fired again immediately (continuous polling bug)\n"); + + /* ---- Iteration 4: t=5003 — temp fires AGAIN ---- */ + mock_set_tick(5003); + main_loop_iteration(&last_check, &last_check1); + printf(" t=5003ms: lock_fired=%d temp_fired=%d\n", lock_check_fired, temp_check_fired); + assert(temp_check_fired == 3); /* Fires every iteration now! */ + printf(" PASS: Temperature fires continuously — last_check1 never advances\n"); + + /* ---- Verify last_check is corrupted by temp block ---- */ + /* After temp fires at t=5003, it writes last_check=5003. + * So lock check's timer just got reset. */ + printf(" last_check=%u (was set by TEMP block, not lock block)\n", last_check); + assert(last_check == 5003); + printf(" PASS: Lock check timer corrupted by temperature block\n"); + + /* ---- Iteration 5: t=10004 — lock check should fire but timer was reset ---- */ + mock_set_tick(10004); + main_loop_iteration(&last_check, &last_check1); + printf(" t=10004ms: lock_fired=%d (expected 2 if timer weren't corrupted)\n", + lock_check_fired); + /* With correct code, lock would fire at ~10001. With bug, last_check + * keeps getting reset by temp block, so it depends on last temp write. + * last_check was 5003, so 10004-5003=5001 > 5000, lock fires. */ + assert(lock_check_fired == 2); + /* But temp has been firing EVERY iteration since t=5001, so it fires here too */ + printf(" temp_fired=%d (fires every iteration since t=5001)\n", temp_check_fired); + assert(temp_check_fired >= 4); + printf(" PASS: Both timers corrupted — lock delayed, temp runs continuously\n"); + + printf("=== Bug #6: ALL TESTS PASSED ===\n\n"); + return 0; +} diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug6_timer_variable_collision.dSYM/Contents/Info.plist b/9_Firmware/9_1_Microcontroller/tests/test_bug6_timer_variable_collision.dSYM/Contents/Info.plist new file mode 100644 index 0000000..3049efa --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/test_bug6_timer_variable_collision.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.test_bug6_timer_variable_collision + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug6_timer_variable_collision.dSYM/Contents/Resources/DWARF/test_bug6_timer_variable_collision b/9_Firmware/9_1_Microcontroller/tests/test_bug6_timer_variable_collision.dSYM/Contents/Resources/DWARF/test_bug6_timer_variable_collision new file mode 100644 index 0000000..88ead00 Binary files /dev/null and b/9_Firmware/9_1_Microcontroller/tests/test_bug6_timer_variable_collision.dSYM/Contents/Resources/DWARF/test_bug6_timer_variable_collision differ diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug6_timer_variable_collision.dSYM/Contents/Resources/Relocations/aarch64/test_bug6_timer_variable_collision.yml b/9_Firmware/9_1_Microcontroller/tests/test_bug6_timer_variable_collision.dSYM/Contents/Resources/Relocations/aarch64/test_bug6_timer_variable_collision.yml new file mode 100644 index 0000000..ef3d9db --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/test_bug6_timer_variable_collision.dSYM/Contents/Resources/Relocations/aarch64/test_bug6_timer_variable_collision.yml @@ -0,0 +1,5 @@ +--- +triple: 'arm64-apple-darwin' +binary-path: test_bug6_timer_variable_collision +relocations: [] +... diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug7_gpio_pin_conflict b/9_Firmware/9_1_Microcontroller/tests/test_bug7_gpio_pin_conflict new file mode 100755 index 0000000..c5c8ccd Binary files /dev/null and b/9_Firmware/9_1_Microcontroller/tests/test_bug7_gpio_pin_conflict differ diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug7_gpio_pin_conflict.c b/9_Firmware/9_1_Microcontroller/tests/test_bug7_gpio_pin_conflict.c new file mode 100644 index 0000000..117e8ff --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/test_bug7_gpio_pin_conflict.c @@ -0,0 +1,122 @@ +/******************************************************************************* + * test_bug7_gpio_pin_conflict.c + * + * Bug #7: adf4382a_manager.h defines GPIOG pins 0-9 for ADF4382 signals, + * but main.h (CubeMX-generated) assigns: + * - GPIOG pins 0-5 → PA enables + clock enables + * - GPIOG pins 6-15 → ADF4382 signals (DIFFERENT pin assignments) + * + * The .c file uses the manager.h pin mapping, meaning it will toggle PA + * enables and clock enables instead of the intended ADF4382 pins. + * + * Example conflicts: + * manager.h: TX_CE_Pin = GPIO_PIN_0 main.h: EN_P_5V0_PA1_Pin = GPIO_PIN_0 + * manager.h: TX_CS_Pin = GPIO_PIN_1 main.h: EN_P_5V0_PA2_Pin = GPIO_PIN_1 + * manager.h: TX_DELADJ_Pin = GPIO_PIN_2 main.h: EN_P_5V0_PA3_Pin = GPIO_PIN_2 + * manager.h: TX_DELSTR_Pin = GPIO_PIN_3 main.h: EN_P_5V5_PA_Pin = GPIO_PIN_3 + * manager.h: TX_LKDET_Pin = GPIO_PIN_4 main.h: EN_P_1V8_CLOCK_Pin = GPIO_PIN_4 + * manager.h: RX_CE_Pin = GPIO_PIN_5 main.h: EN_P_3V3_CLOCK_Pin = GPIO_PIN_5 + * + * And for the actual ADF4382 pins: + * main.h: ADF4382_TX_CE_Pin = GPIO_PIN_15 vs manager.h: TX_CE_Pin = GPIO_PIN_0 + * main.h: ADF4382_TX_CS_Pin = GPIO_PIN_14 vs manager.h: TX_CS_Pin = GPIO_PIN_1 + * + * Test strategy: + * Use compile-time assertions to verify the pin conflicts exist. + * Then use runtime checks to demonstrate the specific collisions. + ******************************************************************************/ +#include +#include +#include + +/* We need to manually define the pins from BOTH headers since the shim + * main.h already has the CubeMX definitions, and including adf4382a_manager.h + * would re-define them (which is exactly the production bug). */ + +/* ---- Pin definitions from adf4382a_manager.h ---- */ +#define MGR_TX_CE_Pin ((uint16_t)0x0001) /* GPIO_PIN_0 */ +#define MGR_TX_CS_Pin ((uint16_t)0x0002) /* GPIO_PIN_1 */ +#define MGR_TX_DELADJ_Pin ((uint16_t)0x0004) /* GPIO_PIN_2 */ +#define MGR_TX_DELSTR_Pin ((uint16_t)0x0008) /* GPIO_PIN_3 */ +#define MGR_TX_LKDET_Pin ((uint16_t)0x0010) /* GPIO_PIN_4 */ +#define MGR_RX_CE_Pin ((uint16_t)0x0020) /* GPIO_PIN_5 */ +#define MGR_RX_CS_Pin ((uint16_t)0x0040) /* GPIO_PIN_6 */ +#define MGR_RX_DELADJ_Pin ((uint16_t)0x0080) /* GPIO_PIN_7 */ +#define MGR_RX_DELSTR_Pin ((uint16_t)0x0100) /* GPIO_PIN_8 */ +#define MGR_RX_LKDET_Pin ((uint16_t)0x0200) /* GPIO_PIN_9 */ + +/* ---- Pin definitions from main.h (CubeMX) ---- */ +#define MAIN_EN_P_5V0_PA1_Pin ((uint16_t)0x0001) /* GPIO_PIN_0 — GPIOG */ +#define MAIN_EN_P_5V0_PA2_Pin ((uint16_t)0x0002) /* GPIO_PIN_1 — GPIOG */ +#define MAIN_EN_P_5V0_PA3_Pin ((uint16_t)0x0004) /* GPIO_PIN_2 — GPIOG */ +#define MAIN_EN_P_5V5_PA_Pin ((uint16_t)0x0008) /* GPIO_PIN_3 — GPIOG */ +#define MAIN_EN_P_1V8_CLOCK_Pin ((uint16_t)0x0010) /* GPIO_PIN_4 — GPIOG */ +#define MAIN_EN_P_3V3_CLOCK_Pin ((uint16_t)0x0020) /* GPIO_PIN_5 — GPIOG */ + +/* Correct ADF4382 pins from main.h */ +#define MAIN_ADF4382_TX_CE_Pin ((uint16_t)0x8000) /* GPIO_PIN_15 */ +#define MAIN_ADF4382_TX_CS_Pin ((uint16_t)0x4000) /* GPIO_PIN_14 */ +#define MAIN_ADF4382_TX_DELADJ_Pin ((uint16_t)0x2000) /* GPIO_PIN_13 */ +#define MAIN_ADF4382_TX_DELSTR_Pin ((uint16_t)0x1000) /* GPIO_PIN_12 */ +#define MAIN_ADF4382_TX_LKDET_Pin ((uint16_t)0x0800) /* GPIO_PIN_11 */ +#define MAIN_ADF4382_RX_CE_Pin ((uint16_t)0x0200) /* GPIO_PIN_9 */ +#define MAIN_ADF4382_RX_CS_Pin ((uint16_t)0x0400) /* GPIO_PIN_10 */ +#define MAIN_ADF4382_RX_DELADJ_Pin ((uint16_t)0x0080) /* GPIO_PIN_7 */ +#define MAIN_ADF4382_RX_DELSTR_Pin ((uint16_t)0x0100) /* GPIO_PIN_8 */ +#define MAIN_ADF4382_RX_LKDET_Pin ((uint16_t)0x0040) /* GPIO_PIN_6 */ + +int main(void) +{ + int conflicts = 0; + + printf("=== Bug #7: GPIO pin mapping conflict ===\n"); + printf("\n Checking manager.h pins vs CubeMX main.h pins (all GPIOG):\n\n"); + + /* ---- Conflict checks: manager.h ADF4382 pins == main.h power enables ---- */ + +#define CHECK_CONFLICT(mgr_name, mgr_val, main_name, main_val) do { \ + printf(" %-20s = 0x%04X vs %-25s = 0x%04X", #mgr_name, mgr_val, \ + #main_name, main_val); \ + if ((mgr_val) == (main_val)) { \ + printf(" ** CONFLICT **\n"); \ + conflicts++; \ + } else { \ + printf(" (ok)\n"); \ + } \ +} while(0) + + printf(" --- manager.h TX pins collide with PA/clock enables ---\n"); + CHECK_CONFLICT(MGR_TX_CE, MGR_TX_CE_Pin, MAIN_EN_P_5V0_PA1, MAIN_EN_P_5V0_PA1_Pin); + CHECK_CONFLICT(MGR_TX_CS, MGR_TX_CS_Pin, MAIN_EN_P_5V0_PA2, MAIN_EN_P_5V0_PA2_Pin); + CHECK_CONFLICT(MGR_TX_DELADJ, MGR_TX_DELADJ_Pin, MAIN_EN_P_5V0_PA3, MAIN_EN_P_5V0_PA3_Pin); + CHECK_CONFLICT(MGR_TX_DELSTR, MGR_TX_DELSTR_Pin, MAIN_EN_P_5V5_PA, MAIN_EN_P_5V5_PA_Pin); + CHECK_CONFLICT(MGR_TX_LKDET, MGR_TX_LKDET_Pin, MAIN_EN_P_1V8_CLK, MAIN_EN_P_1V8_CLOCK_Pin); + CHECK_CONFLICT(MGR_RX_CE, MGR_RX_CE_Pin, MAIN_EN_P_3V3_CLK, MAIN_EN_P_3V3_CLOCK_Pin); + + printf("\n --- manager.h TX pins vs correct CubeMX ADF4382 TX pins ---\n"); + CHECK_CONFLICT(MGR_TX_CE, MGR_TX_CE_Pin, MAIN_ADF_TX_CE, MAIN_ADF4382_TX_CE_Pin); + CHECK_CONFLICT(MGR_TX_CS, MGR_TX_CS_Pin, MAIN_ADF_TX_CS, MAIN_ADF4382_TX_CS_Pin); + CHECK_CONFLICT(MGR_TX_DELADJ, MGR_TX_DELADJ_Pin, MAIN_ADF_TX_DADJ, MAIN_ADF4382_TX_DELADJ_Pin); + CHECK_CONFLICT(MGR_TX_DELSTR, MGR_TX_DELSTR_Pin, MAIN_ADF_TX_DSTR, MAIN_ADF4382_TX_DELSTR_Pin); + CHECK_CONFLICT(MGR_TX_LKDET, MGR_TX_LKDET_Pin, MAIN_ADF_TX_LKDT, MAIN_ADF4382_TX_LKDET_Pin); + + printf("\n Total pin conflicts found: %d\n", conflicts); + + /* We expect 6 conflicts (the PA/clock enable collisions) and + * 5 mismatches (manager.h pins != correct CubeMX ADF4382 pins) */ + assert(conflicts >= 6); + printf(" PASS: At least 6 pin conflicts confirmed\n"); + + /* ---- Verify specific critical conflict: TX_CE writes to PA1 enable ---- */ + printf("\n Critical safety issue:\n"); + printf(" When adf4382a_manager.c writes TX_CE_Pin (0x%04X) on GPIOG,\n", + MGR_TX_CE_Pin); + printf(" it actually toggles EN_P_5V0_PA1 (0x%04X) — the PA1 5V power enable!\n", + MAIN_EN_P_5V0_PA1_Pin); + assert(MGR_TX_CE_Pin == MAIN_EN_P_5V0_PA1_Pin); + printf(" PASS: Confirmed TX_CE_Pin == EN_P_5V0_PA1_Pin (0x%04X)\n", + MGR_TX_CE_Pin); + + printf("=== Bug #7: ALL TESTS PASSED ===\n\n"); + return 0; +} diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug7_gpio_pin_conflict.dSYM/Contents/Info.plist b/9_Firmware/9_1_Microcontroller/tests/test_bug7_gpio_pin_conflict.dSYM/Contents/Info.plist new file mode 100644 index 0000000..bba96c9 --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/test_bug7_gpio_pin_conflict.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.test_bug7_gpio_pin_conflict + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug7_gpio_pin_conflict.dSYM/Contents/Resources/DWARF/test_bug7_gpio_pin_conflict b/9_Firmware/9_1_Microcontroller/tests/test_bug7_gpio_pin_conflict.dSYM/Contents/Resources/DWARF/test_bug7_gpio_pin_conflict new file mode 100644 index 0000000..a9e4551 Binary files /dev/null and b/9_Firmware/9_1_Microcontroller/tests/test_bug7_gpio_pin_conflict.dSYM/Contents/Resources/DWARF/test_bug7_gpio_pin_conflict differ diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug7_gpio_pin_conflict.dSYM/Contents/Resources/Relocations/aarch64/test_bug7_gpio_pin_conflict.yml b/9_Firmware/9_1_Microcontroller/tests/test_bug7_gpio_pin_conflict.dSYM/Contents/Resources/Relocations/aarch64/test_bug7_gpio_pin_conflict.yml new file mode 100644 index 0000000..bb8d2d9 --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/test_bug7_gpio_pin_conflict.dSYM/Contents/Resources/Relocations/aarch64/test_bug7_gpio_pin_conflict.yml @@ -0,0 +1,5 @@ +--- +triple: 'arm64-apple-darwin' +binary-path: test_bug7_gpio_pin_conflict +relocations: [] +... diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug8_uart_commented_out b/9_Firmware/9_1_Microcontroller/tests/test_bug8_uart_commented_out new file mode 100755 index 0000000..503ae9d Binary files /dev/null and b/9_Firmware/9_1_Microcontroller/tests/test_bug8_uart_commented_out differ diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug8_uart_commented_out.c b/9_Firmware/9_1_Microcontroller/tests/test_bug8_uart_commented_out.c new file mode 100644 index 0000000..9472ce0 --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/test_bug8_uart_commented_out.c @@ -0,0 +1,137 @@ +/******************************************************************************* + * test_bug8_uart_commented_out.c + * + * Bug #8: Debug helpers uart_print() and uart_println() in main.cpp + * (lines 958-970) are commented out with block comments. + * + * Test strategy: + * Read the source file and verify the functions are inside comment blocks. + * This is a static analysis / string-search test — no compilation of + * the source under test is needed. + ******************************************************************************/ +#include +#include +#include +#include + +/* Path to the source file (relative from test execution dir) */ +#define SOURCE_FILE "../9_1_3_C_Cpp_Code/main.cpp" + +/* Helper: read entire file into malloc'd buffer. Returns NULL on failure. */ +static char *read_file(const char *path, long *out_size) +{ + FILE *f = fopen(path, "r"); + if (!f) return NULL; + fseek(f, 0, SEEK_END); + long size = ftell(f); + fseek(f, 0, SEEK_SET); + char *buf = (char *)malloc(size + 1); + if (!buf) { fclose(f); return NULL; } + long nread = (long)fread(buf, 1, size, f); + buf[nread] = '\0'; + fclose(f); + if (out_size) *out_size = nread; + return buf; +} + +int main(void) +{ + printf("=== Bug #8: uart_print/uart_println commented out ===\n"); + + long size; + char *src = read_file(SOURCE_FILE, &size); + if (!src) { + printf(" Could not open %s — trying alternate path...\n", SOURCE_FILE); + /* Try from the tests/ directory */ + src = read_file("../9_1_3_C_Cpp_Code/main.cpp", &size); + } + if (!src) { + /* Try absolute path */ + src = read_file("/Users/ganeshpanth/PLFM_RADAR/9_Firmware/9_1_Microcontroller/9_1_3_C_Cpp_Code/main.cpp", &size); + } + assert(src != NULL && "Could not open main.cpp"); + printf(" Read %ld bytes from main.cpp\n", size); + + /* ---- Test A: Find "static void uart_print" — should be inside a comment ---- */ + const char *uart_print_sig = "static void uart_print(const char *msg)"; + char *pos = strstr(src, uart_print_sig); + assert(pos != NULL && "uart_print function signature not found in source"); + printf(" Found uart_print signature at offset %ld\n", (long)(pos - src)); + + /* Walk backwards from pos to find if we're inside a block comment */ + /* Look for the nearest preceding block-comment open '/ *' that isn't closed */ + int inside_comment = 0; + for (char *p = src; p < pos; p++) { + if (p[0] == '/' && p[1] == '*') { + inside_comment = 1; + p++; /* skip past '*' */ + } else if (p[0] == '*' && p[1] == '/') { + inside_comment = 0; + p++; /* skip past '/' */ + } + } + printf(" uart_print is inside block comment: %s\n", + inside_comment ? "YES" : "NO"); + assert(inside_comment == 1); + printf(" PASS: uart_print is commented out\n"); + + /* ---- Test B: Find "static void uart_println" — also in comment ---- */ + const char *uart_println_sig = "static void uart_println(const char *msg)"; + pos = strstr(src, uart_println_sig); + assert(pos != NULL && "uart_println function signature not found in source"); + printf(" Found uart_println signature at offset %ld\n", (long)(pos - src)); + + inside_comment = 0; + for (char *p = src; p < pos; p++) { + if (p[0] == '/' && p[1] == '*') { + inside_comment = 1; + p++; + } else if (p[0] == '*' && p[1] == '/') { + inside_comment = 0; + p++; + } + } + printf(" uart_println is inside block comment: %s\n", + inside_comment ? "YES" : "NO"); + assert(inside_comment == 1); + printf(" PASS: uart_println is commented out\n"); + + /* ---- Test C: Verify the comment pattern matches lines 957-970 ---- */ + /* Find the opening '/ *' that contains uart_print */ + char *comment_start = NULL; + char *comment_end = NULL; + int in_cmt = 0; + for (char *p = src; p < src + size - 1; p++) { + if (p[0] == '/' && p[1] == '*') { + if (!in_cmt) { + in_cmt = 1; + comment_start = p; + } + p++; + } else if (p[0] == '*' && p[1] == '/') { + if (in_cmt) { + comment_end = p + 2; + /* Check if uart_print is within this comment */ + char *found = strstr(comment_start, uart_print_sig); + if (found && found < comment_end) { + /* Count lines from start to comment_start */ + int line = 1; + for (char *lp = src; lp < comment_start; lp++) { + if (*lp == '\n') line++; + } + printf(" Comment block containing uart_print starts at line %d\n", line); + /* Verify it's approximately around line 958 */ + assert(line >= 955 && line <= 965); + printf(" PASS: Comment block is at expected location (~line 958)\n"); + break; + } + } + in_cmt = 0; + p++; + } + } + + free(src); + printf("=== Bug #8: ALL TESTS PASSED ===\n\n"); + return 0; +} diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug8_uart_commented_out.dSYM/Contents/Info.plist b/9_Firmware/9_1_Microcontroller/tests/test_bug8_uart_commented_out.dSYM/Contents/Info.plist new file mode 100644 index 0000000..0d98b19 --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/test_bug8_uart_commented_out.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.test_bug8_uart_commented_out + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug8_uart_commented_out.dSYM/Contents/Resources/DWARF/test_bug8_uart_commented_out b/9_Firmware/9_1_Microcontroller/tests/test_bug8_uart_commented_out.dSYM/Contents/Resources/DWARF/test_bug8_uart_commented_out new file mode 100644 index 0000000..026205c Binary files /dev/null and b/9_Firmware/9_1_Microcontroller/tests/test_bug8_uart_commented_out.dSYM/Contents/Resources/DWARF/test_bug8_uart_commented_out differ diff --git a/9_Firmware/9_1_Microcontroller/tests/test_bug8_uart_commented_out.dSYM/Contents/Resources/Relocations/aarch64/test_bug8_uart_commented_out.yml b/9_Firmware/9_1_Microcontroller/tests/test_bug8_uart_commented_out.dSYM/Contents/Resources/Relocations/aarch64/test_bug8_uart_commented_out.yml new file mode 100644 index 0000000..50d119c --- /dev/null +++ b/9_Firmware/9_1_Microcontroller/tests/test_bug8_uart_commented_out.dSYM/Contents/Resources/Relocations/aarch64/test_bug8_uart_commented_out.yml @@ -0,0 +1,5 @@ +--- +triple: 'arm64-apple-darwin' +binary-path: test_bug8_uart_commented_out +relocations: [] +...