Fix all 8 firmware bugs with regression tests
Bugs fixed in adf4382a_manager.c: - Bug #1: Move initialized=true before sync setup, propagate sync failure - Bug #3: Implement TriggerTimedSync with sw_sync pulse (was no-op) - Bug #5: Replace GPIO-only placeholder with TIM3 PWM for DELADJ - Bug #7: Correct GPIOG pin definitions to match CubeMX (pins 6-15) Bugs fixed in main.cpp: - Bug #2: Remove pre-reset ad9523_setup() call (keep only post-reset) - Bug #4: Move init error check before phase shift calls - Bug #6: Fix timer variable (last_check -> last_check1) in temp block - Bug #8: Uncomment uart_print/uart_println debug helpers Test harness updates: - All 8 tests rewritten to assert correct post-fix behavior - Added TIM PWM mock (SPY_TIM_PWM_START/STOP/SET_COMPARE) - Added mock_adf4382_set_timed_sync_retval for failure injection - Updated shims and Makefile for new test dependencies - All 8 tests pass: make clean && make test -> 8/8 passed
This commit is contained in:
@@ -7,10 +7,16 @@
|
|||||||
// External SPI handle
|
// External SPI handle
|
||||||
extern SPI_HandleTypeDef hspi4;
|
extern SPI_HandleTypeDef hspi4;
|
||||||
|
|
||||||
|
// External timer for DELADJ PWM (configured in CubeMX, period = DELADJ_MAX_DUTY_CYCLE)
|
||||||
|
// TX DELADJ uses TIM3_CH2, RX DELADJ uses TIM3_CH3
|
||||||
|
extern TIM_HandleTypeDef htim3;
|
||||||
|
|
||||||
// Static function prototypes
|
// Static function prototypes
|
||||||
static void set_chip_enable(uint8_t ce_pin, bool state);
|
static void set_chip_enable(uint8_t ce_pin, bool state);
|
||||||
static void set_deladj_pin(uint8_t device, bool state);
|
static void set_deladj_pin(uint8_t device, bool state);
|
||||||
static void set_delstr_pin(uint8_t device, bool state);
|
static void set_delstr_pin(uint8_t device, bool state);
|
||||||
|
static void start_deladj_pwm(uint8_t device, uint16_t duty_cycle);
|
||||||
|
static void stop_deladj_pwm(uint8_t device);
|
||||||
static uint16_t phase_ps_to_duty_cycle(uint16_t phase_ps);
|
static uint16_t phase_ps_to_duty_cycle(uint16_t phase_ps);
|
||||||
|
|
||||||
int ADF4382A_Manager_Init(ADF4382A_Manager *manager, SyncMethod method)
|
int ADF4382A_Manager_Init(ADF4382A_Manager *manager, SyncMethod method)
|
||||||
@@ -163,40 +169,43 @@ int ADF4382A_Manager_Init(ADF4382A_Manager *manager, SyncMethod method)
|
|||||||
adf4382_set_en_chan(manager->rx_dev, 0, true);
|
adf4382_set_en_chan(manager->rx_dev, 0, true);
|
||||||
adf4382_set_en_chan(manager->rx_dev, 1, false);
|
adf4382_set_en_chan(manager->rx_dev, 1, false);
|
||||||
|
|
||||||
|
// Mark initialized BEFORE sync setup so SetupTimedSync/SetupEZSync
|
||||||
|
// see initialized=true and actually configure the hardware.
|
||||||
|
// (FIX for Bug #1: previously this was set AFTER the sync calls,
|
||||||
|
// causing them to always return -2 NOT_INIT.)
|
||||||
|
manager->initialized = true;
|
||||||
|
DIAG("LO", "manager->initialized set to true (before sync setup)");
|
||||||
|
|
||||||
// Setup synchronization based on selected method
|
// Setup synchronization based on selected method
|
||||||
// BUG DIAGNOSTIC: At this point manager->initialized is still false.
|
DIAG("LO", "About to call sync setup -- manager->initialized=%s",
|
||||||
// ADF4382A_SetupTimedSync() and ADF4382A_SetupEZSync() both check
|
|
||||||
// manager->initialized and will return -2 (NOT_INIT) if false.
|
|
||||||
// This means sync setup ALWAYS SILENTLY FAILS during init.
|
|
||||||
DIAG_WARN("LO", "About to call sync setup -- manager->initialized=%s (BUG: will fail -2 if false)",
|
|
||||||
manager->initialized ? "true" : "false");
|
manager->initialized ? "true" : "false");
|
||||||
|
|
||||||
if (method == SYNC_METHOD_TIMED) {
|
if (method == SYNC_METHOD_TIMED) {
|
||||||
ret = ADF4382A_SetupTimedSync(manager);
|
ret = ADF4382A_SetupTimedSync(manager);
|
||||||
DIAG("LO", "ADF4382A_SetupTimedSync() returned %d", ret);
|
DIAG("LO", "ADF4382A_SetupTimedSync() returned %d", ret);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
DIAG_ERR("LO", "Timed sync setup FAILED: %d (expected -2 due to init ordering bug)", ret);
|
DIAG_ERR("LO", "Timed sync setup FAILED: %d", ret);
|
||||||
printf("Timed sync setup failed: %d\n", ret);
|
printf("Timed sync setup failed: %d\n", ret);
|
||||||
// NOTE: Error is logged but swallowed -- init continues
|
manager->initialized = false;
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ret = ADF4382A_SetupEZSync(manager);
|
ret = ADF4382A_SetupEZSync(manager);
|
||||||
DIAG("LO", "ADF4382A_SetupEZSync() returned %d", ret);
|
DIAG("LO", "ADF4382A_SetupEZSync() returned %d", ret);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
DIAG_ERR("LO", "EZSync setup FAILED: %d (expected -2 due to init ordering bug)", ret);
|
DIAG_ERR("LO", "EZSync setup FAILED: %d", ret);
|
||||||
printf("EZSync setup failed: %d\n", ret);
|
printf("EZSync setup failed: %d\n", ret);
|
||||||
|
manager->initialized = false;
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
manager->initialized = true;
|
|
||||||
DIAG("LO", "manager->initialized set to true (sync setup was called BEFORE this)");
|
|
||||||
|
|
||||||
printf("ADF4382A Manager initialized with %s synchronization on SPI4\n",
|
printf("ADF4382A Manager initialized with %s synchronization on SPI4\n",
|
||||||
(method == SYNC_METHOD_TIMED) ? "TIMED" : "EZSYNC");
|
(method == SYNC_METHOD_TIMED) ? "TIMED" : "EZSYNC");
|
||||||
|
|
||||||
DIAG_ELAPSED("LO", "Total Manager_Init", t_start);
|
DIAG_ELAPSED("LO", "Total Manager_Init", t_start);
|
||||||
DIAG("LO", "Init returning OK (but sync setup was %s)",
|
DIAG("LO", "Init returning OK (sync setup %s)",
|
||||||
(ret == 0) ? "successful" : "FAILED -- sync NOT configured");
|
(ret == 0) ? "successful" : "had warnings");
|
||||||
|
|
||||||
return ADF4382A_MANAGER_OK;
|
return ADF4382A_MANAGER_OK;
|
||||||
}
|
}
|
||||||
@@ -281,24 +290,56 @@ int ADF4382A_SetupEZSync(ADF4382A_Manager *manager)
|
|||||||
|
|
||||||
int ADF4382A_TriggerTimedSync(ADF4382A_Manager *manager)
|
int ADF4382A_TriggerTimedSync(ADF4382A_Manager *manager)
|
||||||
{
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (!manager || !manager->initialized || manager->sync_method != SYNC_METHOD_TIMED) {
|
if (!manager || !manager->initialized || manager->sync_method != SYNC_METHOD_TIMED) {
|
||||||
DIAG_ERR("LO", "TriggerTimedSync REJECTED: init=%s method=%d",
|
DIAG_ERR("LO", "TriggerTimedSync REJECTED: init=%s method=%d",
|
||||||
(manager && manager->initialized) ? "true" : "false",
|
(manager && manager->initialized) ? "true" : "false",
|
||||||
manager ? manager->sync_method : -1);
|
manager ? manager->sync_method : -1);
|
||||||
return ADF4382A_MANAGER_ERROR_NOT_INIT;
|
return ADF4382A_MANAGER_ERROR_NOT_INIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: This function currently does NOT trigger anything -- it only prints.
|
|
||||||
// The assumption is that the 60 MHz SYNCP/SYNCN from AD9523 are always present
|
|
||||||
// and timed sync happens automatically once the timed_sync_setup registers are
|
|
||||||
// programmed. But if SetupTimedSync failed (init ordering bug), this is a no-op
|
|
||||||
// on top of a no-op.
|
|
||||||
DIAG_WARN("LO", "TriggerTimedSync called -- NOTE: function body is advisory only, no hardware trigger issued");
|
|
||||||
DIAG_WARN("LO", "If SetupTimedSync failed during init, timed sync registers were NEVER written");
|
|
||||||
|
|
||||||
printf("Timed sync ready - SYNC pin will trigger synchronization\n");
|
DIAG("LO", "Triggering timed sync via sw_sync pulse (SYNCP/SYNCN must be present)...");
|
||||||
printf("Ensure 60 MHz phase-aligned clocks are present on SYNCP/SYNCN pins\n");
|
|
||||||
|
|
||||||
|
// Arm the sync capture on both devices via sw_sync.
|
||||||
|
// With timed_sync_setup already programmed, the device will synchronize
|
||||||
|
// its output dividers to the next SYNCP/SYNCN rising edge after sw_sync
|
||||||
|
// is asserted.
|
||||||
|
ret = adf4382_set_sw_sync(manager->tx_dev, true);
|
||||||
|
if (ret) {
|
||||||
|
DIAG_ERR("LO", "TX timed sw_sync SET failed: %d", ret);
|
||||||
|
printf("TX timed sync trigger failed: %d\n", ret);
|
||||||
|
return ADF4382A_MANAGER_ERROR_SPI;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = adf4382_set_sw_sync(manager->rx_dev, true);
|
||||||
|
if (ret) {
|
||||||
|
DIAG_ERR("LO", "RX timed sw_sync SET failed: %d", ret);
|
||||||
|
printf("RX timed sync trigger failed: %d\n", ret);
|
||||||
|
return ADF4382A_MANAGER_ERROR_SPI;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for at least one sync clock cycle (60 MHz = 16.7 ns period).
|
||||||
|
// 10 us is conservative — guarantees multiple sync edges are captured.
|
||||||
|
no_os_udelay(10);
|
||||||
|
|
||||||
|
// De-assert sw_sync
|
||||||
|
ret = adf4382_set_sw_sync(manager->tx_dev, false);
|
||||||
|
if (ret) {
|
||||||
|
DIAG_ERR("LO", "TX timed sw_sync CLEAR failed: %d", ret);
|
||||||
|
printf("TX timed sync clear failed: %d\n", ret);
|
||||||
|
return ADF4382A_MANAGER_ERROR_SPI;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = adf4382_set_sw_sync(manager->rx_dev, false);
|
||||||
|
if (ret) {
|
||||||
|
DIAG_ERR("LO", "RX timed sw_sync CLEAR failed: %d", ret);
|
||||||
|
printf("RX timed sync clear failed: %d\n", ret);
|
||||||
|
return ADF4382A_MANAGER_ERROR_SPI;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Timed sync triggered via sw_sync pulse\n");
|
||||||
|
DIAG("LO", "Timed sync trigger complete (sw_sync set + 10us + clear)");
|
||||||
return ADF4382A_MANAGER_OK;
|
return ADF4382A_MANAGER_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -564,22 +605,24 @@ int ADF4382A_SetFinePhaseShift(ADF4382A_Manager *manager, uint8_t device, uint16
|
|||||||
// Clamp duty cycle
|
// Clamp duty cycle
|
||||||
duty_cycle = (duty_cycle > DELADJ_MAX_DUTY_CYCLE) ? DELADJ_MAX_DUTY_CYCLE : duty_cycle;
|
duty_cycle = (duty_cycle > DELADJ_MAX_DUTY_CYCLE) ? DELADJ_MAX_DUTY_CYCLE : duty_cycle;
|
||||||
|
|
||||||
// For simplicity, we'll use a basic implementation
|
|
||||||
// In a real system, you would generate a PWM signal on DELADJ pin
|
|
||||||
// Here we just set the pin state based on a simplified approach
|
|
||||||
|
|
||||||
if (duty_cycle == 0) {
|
if (duty_cycle == 0) {
|
||||||
|
// Fully OFF: stop PWM, drive pin LOW
|
||||||
|
stop_deladj_pwm(device);
|
||||||
set_deladj_pin(device, false);
|
set_deladj_pin(device, false);
|
||||||
DIAG("LO", "Dev%d DELADJ=LOW (duty=0)", device);
|
DIAG("LO", "Dev%d DELADJ=LOW (duty=0, PWM stopped)", device);
|
||||||
} else if (duty_cycle >= DELADJ_MAX_DUTY_CYCLE) {
|
} else if (duty_cycle >= DELADJ_MAX_DUTY_CYCLE) {
|
||||||
|
// Fully ON: stop PWM, drive pin HIGH
|
||||||
|
stop_deladj_pwm(device);
|
||||||
set_deladj_pin(device, true);
|
set_deladj_pin(device, true);
|
||||||
DIAG("LO", "Dev%d DELADJ=HIGH (duty=max)", device);
|
DIAG("LO", "Dev%d DELADJ=HIGH (duty=max, PWM stopped)", device);
|
||||||
} else {
|
} else {
|
||||||
// For intermediate values, you would need PWM generation
|
// Intermediate: use TIM3 PWM output
|
||||||
// This is a simplified implementation
|
// The PWM output is low-pass filtered externally to produce a DC
|
||||||
set_deladj_pin(device, true);
|
// voltage proportional to the duty cycle for the ADF4382 DELADJ input.
|
||||||
DIAG_WARN("LO", "Dev%d DELADJ=HIGH for duty=%d/%d -- PLACEHOLDER: intermediate values need real PWM",
|
start_deladj_pwm(device, duty_cycle);
|
||||||
device, duty_cycle, DELADJ_MAX_DUTY_CYCLE);
|
DIAG("LO", "Dev%d DELADJ PWM started: duty=%d/%d (%.1f%%)",
|
||||||
|
device, duty_cycle, DELADJ_MAX_DUTY_CYCLE,
|
||||||
|
(float)duty_cycle * 100.0f / DELADJ_MAX_DUTY_CYCLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Device %d DELADJ duty cycle set to %d/%d\n",
|
printf("Device %d DELADJ duty cycle set to %d/%d\n",
|
||||||
@@ -632,6 +675,21 @@ static void set_delstr_pin(uint8_t device, bool state)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void start_deladj_pwm(uint8_t device, uint16_t duty_cycle)
|
||||||
|
{
|
||||||
|
// TX DELADJ → TIM3_CH2, RX DELADJ → TIM3_CH3
|
||||||
|
// Timer period (ARR) is configured to DELADJ_MAX_DUTY_CYCLE in CubeMX.
|
||||||
|
uint32_t channel = (device == 0) ? TIM_CHANNEL_2 : TIM_CHANNEL_3;
|
||||||
|
__HAL_TIM_SET_COMPARE(&htim3, channel, (uint32_t)duty_cycle);
|
||||||
|
HAL_TIM_PWM_Start(&htim3, channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stop_deladj_pwm(uint8_t device)
|
||||||
|
{
|
||||||
|
uint32_t channel = (device == 0) ? TIM_CHANNEL_2 : TIM_CHANNEL_3;
|
||||||
|
HAL_TIM_PWM_Stop(&htim3, channel);
|
||||||
|
}
|
||||||
|
|
||||||
static uint16_t phase_ps_to_duty_cycle(uint16_t phase_ps)
|
static uint16_t phase_ps_to_duty_cycle(uint16_t phase_ps)
|
||||||
{
|
{
|
||||||
// Convert phase shift in picoseconds to DELADJ duty cycle
|
// Convert phase shift in picoseconds to DELADJ duty cycle
|
||||||
|
|||||||
@@ -5,28 +5,30 @@
|
|||||||
#include "adf4382.h"
|
#include "adf4382.h"
|
||||||
#include "no_os_spi.h"
|
#include "no_os_spi.h"
|
||||||
|
|
||||||
// GPIO Definitions
|
// GPIO Definitions — matched to CubeMX main.h (GPIOG pins 6-15)
|
||||||
#define TX_CE_Pin GPIO_PIN_0
|
// RX pins: GPIOG pins 6-10
|
||||||
#define TX_CE_GPIO_Port GPIOG
|
#define RX_LKDET_Pin GPIO_PIN_6
|
||||||
#define TX_CS_Pin GPIO_PIN_1
|
#define RX_LKDET_GPIO_Port GPIOG
|
||||||
#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_Pin GPIO_PIN_7
|
||||||
#define RX_DELADJ_GPIO_Port GPIOG
|
#define RX_DELADJ_GPIO_Port GPIOG
|
||||||
#define RX_DELSTR_Pin GPIO_PIN_8
|
#define RX_DELSTR_Pin GPIO_PIN_8
|
||||||
#define RX_DELSTR_GPIO_Port GPIOG
|
#define RX_DELSTR_GPIO_Port GPIOG
|
||||||
#define RX_LKDET_Pin GPIO_PIN_9
|
#define RX_CE_Pin GPIO_PIN_9
|
||||||
#define RX_LKDET_GPIO_Port GPIOG
|
#define RX_CE_GPIO_Port GPIOG
|
||||||
|
#define RX_CS_Pin GPIO_PIN_10
|
||||||
|
#define RX_CS_GPIO_Port GPIOG
|
||||||
|
|
||||||
|
// TX pins: GPIOG pins 11-15
|
||||||
|
#define TX_LKDET_Pin GPIO_PIN_11
|
||||||
|
#define TX_LKDET_GPIO_Port GPIOG
|
||||||
|
#define TX_DELSTR_Pin GPIO_PIN_12
|
||||||
|
#define TX_DELSTR_GPIO_Port GPIOG
|
||||||
|
#define TX_DELADJ_Pin GPIO_PIN_13
|
||||||
|
#define TX_DELADJ_GPIO_Port GPIOG
|
||||||
|
#define TX_CS_Pin GPIO_PIN_14
|
||||||
|
#define TX_CS_GPIO_Port GPIOG
|
||||||
|
#define TX_CE_Pin GPIO_PIN_15
|
||||||
|
#define TX_CE_GPIO_Port GPIOG
|
||||||
|
|
||||||
// Frequency definitions
|
// Frequency definitions
|
||||||
#define REF_FREQ_HZ 300000000ULL // 300 MHz
|
#define REF_FREQ_HZ 300000000ULL // 300 MHz
|
||||||
|
|||||||
@@ -954,8 +954,7 @@ void getSystemStatusForGUI(char* status_buffer, size_t buffer_size) {
|
|||||||
status_buffer[buffer_size - 1] = '\0';
|
status_buffer[buffer_size - 1] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---------- UART printing helpers ---------- */
|
/* ---------- UART printing helpers (Bug #8 FIXED: uncommented) ---------- */
|
||||||
/*
|
|
||||||
static void uart_print(const char *msg)
|
static void uart_print(const char *msg)
|
||||||
{
|
{
|
||||||
HAL_UART_Transmit(&huart3, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);
|
HAL_UART_Transmit(&huart3, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);
|
||||||
@@ -967,7 +966,6 @@ static void uart_println(const char *msg)
|
|||||||
const char crlf[] = "\r\n";
|
const char crlf[] = "\r\n";
|
||||||
HAL_UART_Transmit(&huart3, (uint8_t*)crlf, 2, HAL_MAX_DELAY);
|
HAL_UART_Transmit(&huart3, (uint8_t*)crlf, 2, HAL_MAX_DELAY);
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
/* ---------- Helper delay wrappers ---------- */
|
/* ---------- Helper delay wrappers ---------- */
|
||||||
static inline void delay_ms(uint32_t ms) { HAL_Delay(ms); }
|
static inline void delay_ms(uint32_t ms) { HAL_Delay(ms); }
|
||||||
@@ -1133,15 +1131,9 @@ static int configure_ad9523(void)
|
|||||||
DIAG("CLK", "Calling ad9523_init() -- fills pdata defaults");
|
DIAG("CLK", "Calling ad9523_init() -- fills pdata defaults");
|
||||||
ad9523_init(&init_param);
|
ad9523_init(&init_param);
|
||||||
|
|
||||||
/* [BUG ANNOTATION] First ad9523_setup() call -- chip is still in reset
|
/* [Bug #2 FIXED] Removed first ad9523_setup() call that was here.
|
||||||
* (AD9523_RESET_RELEASE() hasn't been called yet).
|
* It wrote to the chip while still in reset — writes were lost.
|
||||||
* SPI writes here likely fail silently or go to a reset device. */
|
* Only the post-reset setup call below is needed. */
|
||||||
DIAG_WARN("CLK", "[BUG] Calling ad9523_setup() #1 BEFORE reset release -- chip in reset, writes likely lost");
|
|
||||||
uint32_t setup1_start = HAL_GetTick();
|
|
||||||
ret = ad9523_setup(&dev, &init_param);
|
|
||||||
DIAG("CLK", "ad9523_setup() #1 returned %ld (took %lu ms)",
|
|
||||||
(long)ret, (unsigned long)(HAL_GetTick() - setup1_start));
|
|
||||||
|
|
||||||
|
|
||||||
// Bring AD9523 out of reset
|
// Bring AD9523 out of reset
|
||||||
DIAG("CLK", "Releasing AD9523 reset (AD9523_RESET_RELEASE)");
|
DIAG("CLK", "Releasing AD9523 reset (AD9523_RESET_RELEASE)");
|
||||||
@@ -1154,14 +1146,14 @@ static int configure_ad9523(void)
|
|||||||
AD9523_REF_SEL(true);
|
AD9523_REF_SEL(true);
|
||||||
|
|
||||||
// Call setup which uses no_os_spi to write registers, io_update, calibrate, sync
|
// Call setup which uses no_os_spi to write registers, io_update, calibrate, sync
|
||||||
DIAG("CLK", "Calling ad9523_setup() #2 -- post-reset, actual configuration");
|
DIAG("CLK", "Calling ad9523_setup() -- post-reset configuration");
|
||||||
uint32_t setup2_start = HAL_GetTick();
|
uint32_t setup_start = HAL_GetTick();
|
||||||
ret = ad9523_setup(&dev, &init_param);
|
ret = ad9523_setup(&dev, &init_param);
|
||||||
DIAG("CLK", "ad9523_setup() #2 returned %ld (took %lu ms)",
|
DIAG("CLK", "ad9523_setup() returned %ld (took %lu ms)",
|
||||||
(long)ret, (unsigned long)(HAL_GetTick() - setup2_start));
|
(long)ret, (unsigned long)(HAL_GetTick() - setup_start));
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
// handle error: lock missing or SPI error
|
// handle error: lock missing or SPI error
|
||||||
DIAG_ERR("CLK", "ad9523_setup() #2 FAILED (ret=%ld) -- lock missing or SPI error", (long)ret);
|
DIAG_ERR("CLK", "ad9523_setup() FAILED (ret=%ld) -- lock missing or SPI error", (long)ret);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1553,10 +1545,14 @@ int main(void)
|
|||||||
DIAG("LO", "ADF4382A_Manager_Init returned %d (took %lu ms)",
|
DIAG("LO", "ADF4382A_Manager_Init returned %d (took %lu ms)",
|
||||||
ret, (unsigned long)(HAL_GetTick() - lo_init_start));
|
ret, (unsigned long)(HAL_GetTick() - lo_init_start));
|
||||||
|
|
||||||
/* [BUG ANNOTATION] The following SetPhaseShift/StrobePhaseShift calls happen
|
/* [Bug #4 FIXED] Check init return code BEFORE calling phase shift.
|
||||||
* BEFORE the init return code is checked (line below with 'if ret !=').
|
* Previously SetPhaseShift/Strobe were called before this check. */
|
||||||
* If init failed, these operate on an uninitialized manager. */
|
if (ret != ADF4382A_MANAGER_OK) {
|
||||||
DIAG_WARN("LO", "[BUG] SetPhaseShift/Strobe called BEFORE checking init return code (ret=%d)", ret);
|
printf("LO Manager initialization failed: %d\n", ret);
|
||||||
|
DIAG_ERR("LO", "Manager init FAILED (ret=%d) -- calling Error_Handler()", ret);
|
||||||
|
Error_Handler();
|
||||||
|
}
|
||||||
|
|
||||||
// Set phase shift (e.g., 500 ps for TX, 500 ps for RX)
|
// Set phase shift (e.g., 500 ps for TX, 500 ps for RX)
|
||||||
int ps_ret = ADF4382A_SetPhaseShift(&lo_manager, 500, 500);
|
int ps_ret = ADF4382A_SetPhaseShift(&lo_manager, 500, 500);
|
||||||
DIAG("LO", "ADF4382A_SetPhaseShift(500, 500) returned %d", ps_ret);
|
DIAG("LO", "ADF4382A_SetPhaseShift(500, 500) returned %d", ps_ret);
|
||||||
@@ -1566,12 +1562,6 @@ int main(void)
|
|||||||
int strobe_rx_ret = ADF4382A_StrobePhaseShift(&lo_manager, 1); // RX device
|
int strobe_rx_ret = ADF4382A_StrobePhaseShift(&lo_manager, 1); // RX device
|
||||||
DIAG("LO", "StrobePhaseShift TX returned %d, RX returned %d", strobe_tx_ret, strobe_rx_ret);
|
DIAG("LO", "StrobePhaseShift TX returned %d, RX returned %d", strobe_tx_ret, strobe_rx_ret);
|
||||||
|
|
||||||
if (ret != ADF4382A_MANAGER_OK) {
|
|
||||||
printf("LO Manager initialization failed: %d\n", ret);
|
|
||||||
DIAG_ERR("LO", "Manager init FAILED (ret=%d) -- calling Error_Handler()", ret);
|
|
||||||
Error_Handler();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check initial lock status
|
// Check initial lock status
|
||||||
bool tx_locked, rx_locked;
|
bool tx_locked, rx_locked;
|
||||||
DIAG("LO", "Checking initial lock status...");
|
DIAG("LO", "Checking initial lock status...");
|
||||||
@@ -2031,11 +2021,9 @@ int main(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* [BUG ANNOTATION] Line below writes to 'last_check' instead of 'last_check1'.
|
/* [BUG #6 FIXED] Was 'last_check' — now correctly writes 'last_check1'
|
||||||
* This causes the temperature timer to share state with the lock-check timer above.
|
* so the temperature timer runs independently of the lock-check timer. */
|
||||||
* Temperature reads may run at incorrect intervals. */
|
last_check1 = HAL_GetTick();
|
||||||
DIAG_WARN("PA", "[BUG] Timer uses 'last_check' instead of 'last_check1' -- temp interval corrupted");
|
|
||||||
last_check = HAL_GetTick();
|
|
||||||
}
|
}
|
||||||
//////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////
|
||||||
/////////////////////////////////////ADAR1000/////////////////////////////////////////
|
/////////////////////////////////////ADAR1000/////////////////////////////////////////
|
||||||
|
|||||||
@@ -92,9 +92,9 @@ test_bug2_ad9523_double_setup: test_bug2_ad9523_double_setup.c $(MOCK_OBJS)
|
|||||||
test_bug6_timer_variable_collision: test_bug6_timer_variable_collision.c $(MOCK_OBJS)
|
test_bug6_timer_variable_collision: test_bug6_timer_variable_collision.c $(MOCK_OBJS)
|
||||||
$(CC) $(CFLAGS) $(INCLUDES) $< $(MOCK_OBJS) -o $@
|
$(CC) $(CFLAGS) $(INCLUDES) $< $(MOCK_OBJS) -o $@
|
||||||
|
|
||||||
# Bug 7 and 8 don't even need mock objects — pure static analysis
|
# Bug 7 needs shim headers + mock objects (post-fix test includes shim adf4382a_manager.h)
|
||||||
test_bug7_gpio_pin_conflict: test_bug7_gpio_pin_conflict.c
|
test_bug7_gpio_pin_conflict: test_bug7_gpio_pin_conflict.c $(MOCK_OBJS)
|
||||||
$(CC) $(CFLAGS) -I. $< -o $@
|
$(CC) $(CFLAGS) $(INCLUDES) $< $(MOCK_OBJS) -o $@
|
||||||
|
|
||||||
test_bug8_uart_commented_out: test_bug8_uart_commented_out.c
|
test_bug8_uart_commented_out: test_bug8_uart_commented_out.c
|
||||||
$(CC) $(CFLAGS) -I. $< -o $@
|
$(CC) $(CFLAGS) -I. $< -o $@
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
/* Configurable return values */
|
/* Configurable return values */
|
||||||
int mock_adf4382_init_retval = 0;
|
int mock_adf4382_init_retval = 0;
|
||||||
|
int mock_adf4382_set_timed_sync_retval = 0;
|
||||||
int mock_ad9523_setup_retval = 0;
|
int mock_ad9523_setup_retval = 0;
|
||||||
|
|
||||||
/* Internal device stubs (allocated on the heap by mock init) */
|
/* Internal device stubs (allocated on the heap by mock init) */
|
||||||
@@ -73,7 +74,7 @@ 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_timed_sync_setup(struct adf4382_dev *dev, bool sync)
|
||||||
{
|
{
|
||||||
spy_push_drv(SPY_ADF4382_SET_TIMED_SYNC, dev, (uint32_t)sync);
|
spy_push_drv(SPY_ADF4382_SET_TIMED_SYNC, dev, (uint32_t)sync);
|
||||||
return 0;
|
return mock_adf4382_set_timed_sync_retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
int adf4382_set_ezsync_setup(struct adf4382_dev *dev, bool sync)
|
int adf4382_set_ezsync_setup(struct adf4382_dev *dev, bool sync)
|
||||||
|
|||||||
@@ -191,6 +191,7 @@ enum cpole1_capacitor { CPOLE1_0_PF = 0, CPOLE1_8_PF, CPOLE1_16_PF, CPOLE1_24_PF
|
|||||||
|
|
||||||
/* Default return code for mock driver functions (0 = success) */
|
/* Default return code for mock driver functions (0 = success) */
|
||||||
extern int mock_adf4382_init_retval;
|
extern int mock_adf4382_init_retval;
|
||||||
|
extern int mock_adf4382_set_timed_sync_retval;
|
||||||
extern int mock_ad9523_setup_retval;
|
extern int mock_ad9523_setup_retval;
|
||||||
|
|
||||||
/* ========================= ADF4382 mock API ======================= */
|
/* ========================= ADF4382 mock API ======================= */
|
||||||
|
|||||||
@@ -17,28 +17,30 @@
|
|||||||
|
|
||||||
/* ---- Constants (copied from real adf4382a_manager.h) ---- */
|
/* ---- Constants (copied from real adf4382a_manager.h) ---- */
|
||||||
|
|
||||||
/* GPIO Definitions — these are the manager.h pin mappings (the buggy ones) */
|
/* GPIO Definitions — corrected to match CubeMX main.h (GPIOG pins 6-15) */
|
||||||
#define TX_CE_Pin GPIO_PIN_0
|
/* RX pins: GPIOG pins 6-10 */
|
||||||
#define TX_CE_GPIO_Port GPIOG
|
#define RX_LKDET_Pin GPIO_PIN_6
|
||||||
#define TX_CS_Pin GPIO_PIN_1
|
#define RX_LKDET_GPIO_Port GPIOG
|
||||||
#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_Pin GPIO_PIN_7
|
||||||
#define RX_DELADJ_GPIO_Port GPIOG
|
#define RX_DELADJ_GPIO_Port GPIOG
|
||||||
#define RX_DELSTR_Pin GPIO_PIN_8
|
#define RX_DELSTR_Pin GPIO_PIN_8
|
||||||
#define RX_DELSTR_GPIO_Port GPIOG
|
#define RX_DELSTR_GPIO_Port GPIOG
|
||||||
#define RX_LKDET_Pin GPIO_PIN_9
|
#define RX_CE_Pin GPIO_PIN_9
|
||||||
#define RX_LKDET_GPIO_Port GPIOG
|
#define RX_CE_GPIO_Port GPIOG
|
||||||
|
#define RX_CS_Pin GPIO_PIN_10
|
||||||
|
#define RX_CS_GPIO_Port GPIOG
|
||||||
|
|
||||||
|
/* TX pins: GPIOG pins 11-15 */
|
||||||
|
#define TX_LKDET_Pin GPIO_PIN_11
|
||||||
|
#define TX_LKDET_GPIO_Port GPIOG
|
||||||
|
#define TX_DELSTR_Pin GPIO_PIN_12
|
||||||
|
#define TX_DELSTR_GPIO_Port GPIOG
|
||||||
|
#define TX_DELADJ_Pin GPIO_PIN_13
|
||||||
|
#define TX_DELADJ_GPIO_Port GPIOG
|
||||||
|
#define TX_CS_Pin GPIO_PIN_14
|
||||||
|
#define TX_CS_GPIO_Port GPIOG
|
||||||
|
#define TX_CE_Pin GPIO_PIN_15
|
||||||
|
#define TX_CE_GPIO_Port GPIOG
|
||||||
|
|
||||||
/* Frequency definitions */
|
/* Frequency definitions */
|
||||||
#define REF_FREQ_HZ 300000000ULL
|
#define REF_FREQ_HZ 300000000ULL
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ I2C_HandleTypeDef hi2c1 = { .id = 1 };
|
|||||||
I2C_HandleTypeDef hi2c2 = { .id = 2 };
|
I2C_HandleTypeDef hi2c2 = { .id = 2 };
|
||||||
UART_HandleTypeDef huart3 = { .id = 3 };
|
UART_HandleTypeDef huart3 = { .id = 3 };
|
||||||
ADC_HandleTypeDef hadc3 = { .id = 3 };
|
ADC_HandleTypeDef hadc3 = { .id = 3 };
|
||||||
|
TIM_HandleTypeDef htim3 = { .id = 3 };
|
||||||
|
|
||||||
/* ========================= Spy log ================================ */
|
/* ========================= Spy log ================================ */
|
||||||
SpyRecord spy_log[SPY_MAX_RECORDS];
|
SpyRecord spy_log[SPY_MAX_RECORDS];
|
||||||
@@ -224,3 +225,40 @@ uint8_t ADS7830_Measure_SingleEnded(ADC_HandleTypeDef *hadc, uint8_t channel)
|
|||||||
});
|
});
|
||||||
return 100;
|
return 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ========================= TIM PWM stubs ========================== */
|
||||||
|
|
||||||
|
HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel)
|
||||||
|
{
|
||||||
|
spy_push((SpyRecord){
|
||||||
|
.type = SPY_TIM_PWM_START,
|
||||||
|
.port = NULL,
|
||||||
|
.pin = (uint16_t)Channel,
|
||||||
|
.value = htim->id,
|
||||||
|
.extra = htim
|
||||||
|
});
|
||||||
|
return HAL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
HAL_StatusTypeDef HAL_TIM_PWM_Stop(TIM_HandleTypeDef *htim, uint32_t Channel)
|
||||||
|
{
|
||||||
|
spy_push((SpyRecord){
|
||||||
|
.type = SPY_TIM_PWM_STOP,
|
||||||
|
.port = NULL,
|
||||||
|
.pin = (uint16_t)Channel,
|
||||||
|
.value = htim->id,
|
||||||
|
.extra = htim
|
||||||
|
});
|
||||||
|
return HAL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mock_tim_set_compare(TIM_HandleTypeDef *htim, uint32_t Channel, uint32_t Compare)
|
||||||
|
{
|
||||||
|
spy_push((SpyRecord){
|
||||||
|
.type = SPY_TIM_SET_COMPARE,
|
||||||
|
.port = NULL,
|
||||||
|
.pin = (uint16_t)Channel,
|
||||||
|
.value = Compare,
|
||||||
|
.extra = htim
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ extern SPI_HandleTypeDef hspi1, hspi4;
|
|||||||
extern I2C_HandleTypeDef hi2c1, hi2c2;
|
extern I2C_HandleTypeDef hi2c1, hi2c2;
|
||||||
extern UART_HandleTypeDef huart3;
|
extern UART_HandleTypeDef huart3;
|
||||||
extern ADC_HandleTypeDef hadc3;
|
extern ADC_HandleTypeDef hadc3;
|
||||||
|
extern TIM_HandleTypeDef htim3; /* Timer for DELADJ PWM */
|
||||||
|
|
||||||
/* ========================= SPY / RECORDING LAYER ================== */
|
/* ========================= SPY / RECORDING LAYER ================== */
|
||||||
|
|
||||||
@@ -129,6 +130,9 @@ typedef enum {
|
|||||||
SPY_AD9523_REMOVE,
|
SPY_AD9523_REMOVE,
|
||||||
SPY_NO_OS_UDELAY,
|
SPY_NO_OS_UDELAY,
|
||||||
SPY_ADS7830_MEASURE,
|
SPY_ADS7830_MEASURE,
|
||||||
|
SPY_TIM_PWM_START,
|
||||||
|
SPY_TIM_PWM_STOP,
|
||||||
|
SPY_TIM_SET_COMPARE,
|
||||||
} SpyCallType;
|
} SpyCallType;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -183,6 +187,21 @@ HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, u
|
|||||||
void no_os_udelay(uint32_t usecs);
|
void no_os_udelay(uint32_t usecs);
|
||||||
void no_os_mdelay(uint32_t msecs);
|
void no_os_mdelay(uint32_t msecs);
|
||||||
|
|
||||||
|
/* ========================= TIM / PWM stubs ======================== */
|
||||||
|
|
||||||
|
#define TIM_CHANNEL_1 0x00U
|
||||||
|
#define TIM_CHANNEL_2 0x04U
|
||||||
|
#define TIM_CHANNEL_3 0x08U
|
||||||
|
#define TIM_CHANNEL_4 0x0CU
|
||||||
|
|
||||||
|
HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel);
|
||||||
|
HAL_StatusTypeDef HAL_TIM_PWM_Stop(TIM_HandleTypeDef *htim, uint32_t Channel);
|
||||||
|
void mock_tim_set_compare(TIM_HandleTypeDef *htim, uint32_t Channel, uint32_t Compare);
|
||||||
|
|
||||||
|
/* Macro form that the real STM32 HAL uses */
|
||||||
|
#define __HAL_TIM_SET_COMPARE(__HANDLE__, __CHANNEL__, __COMPARE__) \
|
||||||
|
mock_tim_set_compare((__HANDLE__), (__CHANNEL__), (__COMPARE__))
|
||||||
|
|
||||||
/* ========================= ADS7830 stub =========================== */
|
/* ========================= ADS7830 stub =========================== */
|
||||||
|
|
||||||
uint8_t ADS7830_Measure_SingleEnded(ADC_HandleTypeDef *hadc, uint8_t channel);
|
uint8_t ADS7830_Measure_SingleEnded(ADC_HandleTypeDef *hadc, uint8_t channel);
|
||||||
|
|||||||
@@ -1,17 +1,14 @@
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* test_bug1_timed_sync_init_ordering.c
|
* test_bug1_timed_sync_init_ordering.c
|
||||||
*
|
*
|
||||||
* Bug #1: ADF4382A_SetupTimedSync() is called at line 175 of
|
* Bug #1 (FIXED): ADF4382A_SetupTimedSync() was called BEFORE
|
||||||
* adf4382a_manager.c BEFORE manager->initialized is set to true at line 191.
|
* manager->initialized was set to true. SetupTimedSync checks
|
||||||
* SetupTimedSync checks `manager->initialized` and returns -2 (NOT_INIT)
|
* `manager->initialized` and returned -2 (NOT_INIT), silently failing.
|
||||||
* when false. The error is then SWALLOWED — init returns OK anyway.
|
|
||||||
*
|
*
|
||||||
* Test strategy:
|
* Post-fix behavior:
|
||||||
* 1. Call ADF4382A_Manager_Init() with SYNC_METHOD_TIMED.
|
* 1. Manager_Init sets initialized=true BEFORE calling sync setup.
|
||||||
* 2. Verify it returns OK (the bug is that it succeeds DESPITE sync failure).
|
* 2. SetupTimedSync succeeds during init (2 driver calls: TX + RX).
|
||||||
* 3. Verify the spy log contains ZERO SPY_ADF4382_SET_TIMED_SYNC records
|
* 3. Sync setup failure is no longer swallowed — init returns error.
|
||||||
* (because SetupTimedSync returned early before reaching the driver calls).
|
|
||||||
* 4. This proves timed sync is NEVER actually configured.
|
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
#include "adf4382a_manager.h"
|
#include "adf4382a_manager.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
@@ -22,9 +19,9 @@ int main(void)
|
|||||||
ADF4382A_Manager mgr;
|
ADF4382A_Manager mgr;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
printf("=== Bug #1: Timed sync init ordering ===\n");
|
printf("=== Bug #1 (FIXED): Timed sync init ordering ===\n");
|
||||||
|
|
||||||
/* ---- Test A: Init returns OK despite sync setup failure ---- */
|
/* ---- Test A: Init returns OK and sync is configured ---- */
|
||||||
spy_reset();
|
spy_reset();
|
||||||
ret = ADF4382A_Manager_Init(&mgr, SYNC_METHOD_TIMED);
|
ret = ADF4382A_Manager_Init(&mgr, SYNC_METHOD_TIMED);
|
||||||
|
|
||||||
@@ -32,32 +29,36 @@ int main(void)
|
|||||||
assert(ret == ADF4382A_MANAGER_OK);
|
assert(ret == ADF4382A_MANAGER_OK);
|
||||||
printf(" PASS: Init returned OK\n");
|
printf(" PASS: Init returned OK\n");
|
||||||
|
|
||||||
/* ---- Test B: No timed sync register writes reached the driver ---- */
|
/* ---- Test B: Timed sync register writes DID reach the driver ---- */
|
||||||
int timed_sync_count = spy_count_type(SPY_ADF4382_SET_TIMED_SYNC);
|
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);
|
printf(" SPY_ADF4382_SET_TIMED_SYNC records: %d (expected 2 — TX + RX)\n", timed_sync_count);
|
||||||
assert(timed_sync_count == 2);
|
assert(timed_sync_count == 2);
|
||||||
printf(" PASS: Manual post-init call succeeds with 2 driver writes\n");
|
printf(" PASS: Timed sync configured for both TX and RX during init\n");
|
||||||
|
|
||||||
|
/* ---- Test C: Manager is initialized ---- */
|
||||||
|
assert(mgr.initialized == true);
|
||||||
|
printf(" PASS: manager->initialized == true\n");
|
||||||
|
|
||||||
|
/* ---- Test D: Init fails if sync setup fails ---- */
|
||||||
|
/* Configure mock to make timed sync fail */
|
||||||
|
spy_reset();
|
||||||
|
extern int mock_adf4382_set_timed_sync_retval;
|
||||||
|
mock_adf4382_set_timed_sync_retval = -1;
|
||||||
|
ret = ADF4382A_Manager_Init(&mgr, SYNC_METHOD_TIMED);
|
||||||
|
printf(" Manager_Init with failing sync: %d (expected non-zero)\n", ret);
|
||||||
|
assert(ret != ADF4382A_MANAGER_OK);
|
||||||
|
printf(" PASS: Init fails when sync setup fails (error no longer swallowed)\n");
|
||||||
|
|
||||||
|
/* Verify manager is NOT left in initialized state on failure */
|
||||||
|
assert(mgr.initialized == false);
|
||||||
|
printf(" PASS: manager->initialized == false after sync failure\n");
|
||||||
|
|
||||||
|
/* Restore mock */
|
||||||
|
mock_adf4382_set_timed_sync_retval = 0;
|
||||||
|
|
||||||
/* Cleanup */
|
/* Cleanup */
|
||||||
ADF4382A_Manager_Deinit(&mgr);
|
ADF4382A_Manager_Deinit(&mgr);
|
||||||
|
|
||||||
printf("=== Bug #1: ALL TESTS PASSED ===\n\n");
|
printf("=== Bug #1 (FIXED): ALL TESTS PASSED ===\n\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,13 @@
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* test_bug2_ad9523_double_setup.c
|
* test_bug2_ad9523_double_setup.c
|
||||||
*
|
*
|
||||||
* Bug #2: configure_ad9523() in main.cpp calls ad9523_setup() twice:
|
* Bug #2 (FIXED): configure_ad9523() now calls ad9523_setup() only ONCE,
|
||||||
* - Line 1141: BEFORE AD9523_RESET_RELEASE() (chip still in reset)
|
* after AD9523_RESET_RELEASE(). The first call (before reset) was removed.
|
||||||
* - Line 1159: AFTER reset release (the real configuration)
|
|
||||||
*
|
*
|
||||||
* We can't compile main.cpp directly, so we extract the bug pattern
|
* Post-fix test:
|
||||||
* and replay the exact sequence against our mocks to prove the double call.
|
* 1. Replay the fixed configure_ad9523() call sequence.
|
||||||
*
|
* 2. Verify ad9523_setup() is called exactly ONCE.
|
||||||
* Test strategy:
|
* 3. Verify the reset-release GPIO write occurs BEFORE the setup call.
|
||||||
* 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 "stm32_hal_mock.h"
|
||||||
#include "ad_driver_mock.h"
|
#include "ad_driver_mock.h"
|
||||||
@@ -31,8 +25,7 @@
|
|||||||
#define AD9523_REF_SEL(x) HAL_GPIO_WritePin(AD9523_REF_SEL_GPIO_Port, AD9523_REF_SEL_Pin, (x) ? GPIO_PIN_SET : GPIO_PIN_RESET)
|
#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.
|
* Extracted from main.cpp — FIXED version (single setup call after reset).
|
||||||
* This reproduces the exact call sequence with minimal setup.
|
|
||||||
*/
|
*/
|
||||||
static int configure_ad9523_extracted(void)
|
static int configure_ad9523_extracted(void)
|
||||||
{
|
{
|
||||||
@@ -41,7 +34,6 @@ static int configure_ad9523_extracted(void)
|
|||||||
struct ad9523_init_param init_param;
|
struct ad9523_init_param init_param;
|
||||||
int32_t ret;
|
int32_t ret;
|
||||||
|
|
||||||
/* Minimal pdata setup — details don't matter for this test */
|
|
||||||
memset(&pdata, 0, sizeof(pdata));
|
memset(&pdata, 0, sizeof(pdata));
|
||||||
pdata.vcxo_freq = 100000000;
|
pdata.vcxo_freq = 100000000;
|
||||||
pdata.num_channels = 0;
|
pdata.num_channels = 0;
|
||||||
@@ -53,22 +45,18 @@ static int configure_ad9523_extracted(void)
|
|||||||
/* Step 1: ad9523_init (fills defaults) */
|
/* Step 1: ad9523_init (fills defaults) */
|
||||||
ad9523_init(&init_param);
|
ad9523_init(&init_param);
|
||||||
|
|
||||||
/* Step 2: FIRST ad9523_setup() — chip is still in reset!
|
/* Step 2: Release reset FIRST (Bug #2 fix: removed pre-reset setup call) */
|
||||||
* This is the bug — line 1141 */
|
|
||||||
ret = ad9523_setup(&dev, &init_param);
|
|
||||||
|
|
||||||
/* Step 3: Release reset — line 1148 */
|
|
||||||
AD9523_RESET_RELEASE();
|
AD9523_RESET_RELEASE();
|
||||||
HAL_Delay(5);
|
HAL_Delay(5);
|
||||||
|
|
||||||
/* Step 4: Select REFB */
|
/* Step 3: Select REFB */
|
||||||
AD9523_REF_SEL(true);
|
AD9523_REF_SEL(true);
|
||||||
|
|
||||||
/* Step 5: SECOND ad9523_setup() — post-reset, real config — line 1159 */
|
/* Step 4: Single ad9523_setup() — post-reset, real config */
|
||||||
ret = ad9523_setup(&dev, &init_param);
|
ret = ad9523_setup(&dev, &init_param);
|
||||||
if (ret != 0) return -1;
|
if (ret != 0) return -1;
|
||||||
|
|
||||||
/* Step 6: status + sync */
|
/* Step 5: status + sync */
|
||||||
ad9523_status(dev);
|
ad9523_status(dev);
|
||||||
ad9523_sync(dev);
|
ad9523_sync(dev);
|
||||||
|
|
||||||
@@ -77,28 +65,24 @@ static int configure_ad9523_extracted(void)
|
|||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
printf("=== Bug #2: AD9523 double setup call ===\n");
|
printf("=== Bug #2 (FIXED): AD9523 single setup call ===\n");
|
||||||
|
|
||||||
spy_reset();
|
spy_reset();
|
||||||
int ret = configure_ad9523_extracted();
|
int ret = configure_ad9523_extracted();
|
||||||
assert(ret == 0);
|
assert(ret == 0);
|
||||||
|
|
||||||
/* ---- Test A: ad9523_setup was called exactly twice ---- */
|
/* ---- Test A: ad9523_setup was called exactly ONCE ---- */
|
||||||
int setup_count = spy_count_type(SPY_AD9523_SETUP);
|
int setup_count = spy_count_type(SPY_AD9523_SETUP);
|
||||||
printf(" SPY_AD9523_SETUP records: %d (expected 2)\n", setup_count);
|
printf(" SPY_AD9523_SETUP records: %d (expected 1)\n", setup_count);
|
||||||
assert(setup_count == 2);
|
assert(setup_count == 1);
|
||||||
printf(" PASS: ad9523_setup() called twice\n");
|
printf(" PASS: ad9523_setup() called exactly once\n");
|
||||||
|
|
||||||
/* ---- Test B: Reset release GPIO write occurs BETWEEN the two setups ---- */
|
/* ---- Test B: Reset release occurs BEFORE the setup call ---- */
|
||||||
int first_setup_idx = spy_find_nth(SPY_AD9523_SETUP, 0);
|
int 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",
|
/* Find the GPIO write for GPIOF, AD9523_RESET_Pin, SET */
|
||||||
first_setup_idx, second_setup_idx);
|
|
||||||
|
|
||||||
/* Find the GPIO write for GPIOF, AD9523_RESET_Pin, SET between them */
|
|
||||||
int reset_gpio_idx = -1;
|
int reset_gpio_idx = -1;
|
||||||
for (int i = first_setup_idx + 1; i < second_setup_idx; i++) {
|
for (int i = 0; i < setup_idx; i++) {
|
||||||
const SpyRecord *r = spy_get(i);
|
const SpyRecord *r = spy_get(i);
|
||||||
if (r && r->type == SPY_GPIO_WRITE &&
|
if (r && r->type == SPY_GPIO_WRITE &&
|
||||||
r->port == GPIOF &&
|
r->port == GPIOF &&
|
||||||
@@ -109,13 +93,12 @@ int main(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
printf(" Reset release GPIO write at spy index %d (expected between %d and %d)\n",
|
printf(" Reset release at spy index %d, setup at %d\n",
|
||||||
reset_gpio_idx, first_setup_idx, second_setup_idx);
|
reset_gpio_idx, setup_idx);
|
||||||
assert(reset_gpio_idx > first_setup_idx);
|
assert(reset_gpio_idx >= 0);
|
||||||
assert(reset_gpio_idx < second_setup_idx);
|
assert(reset_gpio_idx < setup_idx);
|
||||||
printf(" PASS: First setup BEFORE reset release, second setup AFTER\n");
|
printf(" PASS: Reset released BEFORE setup call (correct order)\n");
|
||||||
printf(" This proves the first ad9523_setup() writes to a chip still in reset\n");
|
|
||||||
|
|
||||||
printf("=== Bug #2: ALL TESTS PASSED ===\n\n");
|
printf("=== Bug #2 (FIXED): ALL TESTS PASSED ===\n\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,97 +1,103 @@
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* test_bug3_timed_sync_noop.c
|
* test_bug3_timed_sync_noop.c
|
||||||
*
|
*
|
||||||
* Bug #3: ADF4382A_TriggerTimedSync() (lines 282-303) is a no-op — it only
|
* Bug #3 (FIXED): ADF4382A_TriggerTimedSync() was a no-op — it only printed
|
||||||
* prints messages but performs NO hardware action (no register writes, no GPIO
|
* messages but performed no hardware action.
|
||||||
* pulses, no SPI transactions).
|
|
||||||
*
|
*
|
||||||
* Test strategy:
|
* Fix: Implemented a sw_sync pulse (set true → 10us delay → set false) on
|
||||||
* 1. Initialize manager with SYNC_METHOD_TIMED, manually fix the sync setup
|
* both TX and RX devices, mirroring EZSync's trigger pattern. With
|
||||||
* (call SetupTimedSync after init so it actually works).
|
* timed_sync_setup already programmed, the devices synchronize their output
|
||||||
* 2. Reset spy log.
|
* dividers to the SYNCP/SYNCN clock edge when sw_sync is asserted.
|
||||||
* 3. Call ADF4382A_TriggerTimedSync().
|
*
|
||||||
* 4. Verify it returns OK.
|
* Test strategy (post-fix):
|
||||||
* 5. Count all hardware-related spy records (GPIO writes, SPI writes,
|
* 1. Initialize manager with SYNC_METHOD_TIMED.
|
||||||
* ADF4382 driver calls). Expect ZERO.
|
* 2. Reset spy log, call TriggerTimedSync().
|
||||||
* 6. Compare with ADF4382A_TriggerEZSync() which actually does 4 SPI calls
|
* 3. Verify 4 SPY_ADF4382_SET_SW_SYNC records (TX set, RX set, TX clear,
|
||||||
* (set_sw_sync true/false for TX and RX).
|
* RX clear) — same count as EZSync.
|
||||||
|
* 4. Verify the set/clear ordering is correct.
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
#include "adf4382a_manager.h"
|
#include "adf4382a_manager.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
/* 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)
|
int main(void)
|
||||||
{
|
{
|
||||||
ADF4382A_Manager mgr;
|
ADF4382A_Manager mgr;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
printf("=== Bug #3: TriggerTimedSync is a no-op ===\n");
|
printf("=== Bug #3 (FIXED): TriggerTimedSync now issues hw actions ===\n");
|
||||||
|
|
||||||
/* Setup: init the manager, then manually fix sync (workaround Bug #1) */
|
/* Setup: init the manager with timed sync */
|
||||||
spy_reset();
|
spy_reset();
|
||||||
ret = ADF4382A_Manager_Init(&mgr, SYNC_METHOD_TIMED);
|
ret = ADF4382A_Manager_Init(&mgr, SYNC_METHOD_TIMED);
|
||||||
assert(ret == ADF4382A_MANAGER_OK);
|
assert(ret == ADF4382A_MANAGER_OK);
|
||||||
|
|
||||||
/* Manually call SetupTimedSync now that initialized==true */
|
/* ---- Test A: TriggerTimedSync produces 4 sw_sync calls ---- */
|
||||||
ret = ADF4382A_SetupTimedSync(&mgr);
|
spy_reset();
|
||||||
assert(ret == ADF4382A_MANAGER_OK);
|
|
||||||
|
|
||||||
/* ---- Test A: TriggerTimedSync produces zero hardware actions ---- */
|
|
||||||
spy_reset(); /* Clear all prior spy records */
|
|
||||||
ret = ADF4382A_TriggerTimedSync(&mgr);
|
ret = ADF4382A_TriggerTimedSync(&mgr);
|
||||||
printf(" TriggerTimedSync returned: %d (expected 0=OK)\n", ret);
|
printf(" TriggerTimedSync returned: %d (expected 0=OK)\n", ret);
|
||||||
assert(ret == ADF4382A_MANAGER_OK);
|
assert(ret == ADF4382A_MANAGER_OK);
|
||||||
|
|
||||||
int hw_actions = count_hardware_actions();
|
int sw_sync_count = spy_count_type(SPY_ADF4382_SET_SW_SYNC);
|
||||||
printf(" Hardware action spy records: %d (expected 0)\n", hw_actions);
|
printf(" SPY_ADF4382_SET_SW_SYNC records: %d (expected 4)\n", sw_sync_count);
|
||||||
assert(hw_actions == 0);
|
assert(sw_sync_count == 4);
|
||||||
printf(" PASS: TriggerTimedSync does absolutely nothing to hardware\n");
|
printf(" PASS: TriggerTimedSync issues 4 SPI sw_sync calls\n");
|
||||||
|
|
||||||
/* ---- Test B: For comparison, TriggerEZSync DOES hardware actions ---- */
|
/* ---- Test B: Verify ordering: set(TX), set(RX), clear(TX), clear(RX) ---- */
|
||||||
/* Reconfigure to EZSYNC for comparison */
|
printf("\n Checking sw_sync call ordering:\n");
|
||||||
|
int sw_idx = 0;
|
||||||
|
for (int i = 0; i < spy_count; i++) {
|
||||||
|
const SpyRecord *r = spy_get(i);
|
||||||
|
if (!r || r->type != SPY_ADF4382_SET_SW_SYNC) continue;
|
||||||
|
|
||||||
|
printf(" sw_sync[%d]: dev=%s value=%d", sw_idx,
|
||||||
|
(r->extra == (void *)mgr.tx_dev) ? "TX" : "RX",
|
||||||
|
r->value);
|
||||||
|
|
||||||
|
switch (sw_idx) {
|
||||||
|
case 0: /* TX set */
|
||||||
|
assert(r->extra == (void *)mgr.tx_dev);
|
||||||
|
assert(r->value == 1);
|
||||||
|
printf(" OK (TX set)\n");
|
||||||
|
break;
|
||||||
|
case 1: /* RX set */
|
||||||
|
assert(r->extra == (void *)mgr.rx_dev);
|
||||||
|
assert(r->value == 1);
|
||||||
|
printf(" OK (RX set)\n");
|
||||||
|
break;
|
||||||
|
case 2: /* TX clear */
|
||||||
|
assert(r->extra == (void *)mgr.tx_dev);
|
||||||
|
assert(r->value == 0);
|
||||||
|
printf(" OK (TX clear)\n");
|
||||||
|
break;
|
||||||
|
case 3: /* RX clear */
|
||||||
|
assert(r->extra == (void *)mgr.rx_dev);
|
||||||
|
assert(r->value == 0);
|
||||||
|
printf(" OK (RX clear)\n");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(0 && "Unexpected extra sw_sync call");
|
||||||
|
}
|
||||||
|
sw_idx++;
|
||||||
|
}
|
||||||
|
assert(sw_idx == 4);
|
||||||
|
printf(" PASS: Ordering is correct (set TX, set RX, clear TX, clear RX)\n");
|
||||||
|
|
||||||
|
/* ---- Test C: Compare with EZSync — both should produce 4 sw_sync calls ---- */
|
||||||
mgr.sync_method = SYNC_METHOD_EZSYNC;
|
mgr.sync_method = SYNC_METHOD_EZSYNC;
|
||||||
spy_reset();
|
spy_reset();
|
||||||
ret = ADF4382A_TriggerEZSync(&mgr);
|
ret = ADF4382A_TriggerEZSync(&mgr);
|
||||||
printf(" TriggerEZSync returned: %d (expected 0=OK)\n", ret);
|
|
||||||
assert(ret == ADF4382A_MANAGER_OK);
|
assert(ret == ADF4382A_MANAGER_OK);
|
||||||
|
int ezsync_count = spy_count_type(SPY_ADF4382_SET_SW_SYNC);
|
||||||
int ezsync_sw_sync_count = spy_count_type(SPY_ADF4382_SET_SW_SYNC);
|
printf("\n EZSync sw_sync count: %d (expected 4, same as timed sync)\n",
|
||||||
printf(" SPY_ADF4382_SET_SW_SYNC records from EZSync: %d (expected 4)\n",
|
ezsync_count);
|
||||||
ezsync_sw_sync_count);
|
assert(ezsync_count == 4);
|
||||||
assert(ezsync_sw_sync_count == 4); /* TX set, RX set, TX clear, RX clear */
|
printf(" PASS: Both sync methods now issue the same hw trigger pattern\n");
|
||||||
printf(" PASS: EZSync performs 4 SPI calls, TimedSync performs 0\n");
|
|
||||||
|
|
||||||
/* Cleanup */
|
/* Cleanup */
|
||||||
mgr.sync_method = SYNC_METHOD_TIMED; /* restore for deinit */
|
mgr.sync_method = SYNC_METHOD_TIMED;
|
||||||
ADF4382A_Manager_Deinit(&mgr);
|
ADF4382A_Manager_Deinit(&mgr);
|
||||||
|
|
||||||
printf("=== Bug #3: ALL TESTS PASSED ===\n\n");
|
printf("\n=== Bug #3: ALL TESTS PASSED (post-fix) ===\n\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,13 @@
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* test_bug4_phase_shift_before_check.c
|
* test_bug4_phase_shift_before_check.c
|
||||||
*
|
*
|
||||||
* Bug #4: In main.cpp lines 1561-1566, ADF4382A_SetPhaseShift() and
|
* Bug #4 (FIXED): In main.cpp, the init return code is now checked BEFORE
|
||||||
* ADF4382A_StrobePhaseShift() are called BEFORE the init return code is
|
* calling SetPhaseShift/StrobePhaseShift. If init fails, Error_Handler()
|
||||||
* checked at line 1569. If init returned an error, these functions operate
|
* is called immediately — phase shift functions are never reached.
|
||||||
* on a partially-initialized manager.
|
|
||||||
*
|
*
|
||||||
* Test strategy:
|
* Post-fix test:
|
||||||
* 1. Replay the exact main.cpp LO init sequence with a FAILING init
|
* 1. Successful init: phase shift calls happen after error check (normal).
|
||||||
* (by making the mock adf4382_init return an error).
|
* 2. Failed init: Error_Handler is called, phase shift never executes.
|
||||||
* 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 "adf4382a_manager.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
@@ -28,19 +21,27 @@ void Error_Handler(void) { error_handler_called = 1; }
|
|||||||
uint8_t GUI_start_flag_received = 0;
|
uint8_t GUI_start_flag_received = 0;
|
||||||
uint8_t USB_Buffer[64] = {0};
|
uint8_t USB_Buffer[64] = {0};
|
||||||
|
|
||||||
|
/* Track whether phase shift was called */
|
||||||
|
static int phase_shift_called = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Extracted from main.cpp lines 1545-1573.
|
* Extracted from main.cpp — FIXED version.
|
||||||
* Returns: 0 if reached error check with OK, 1 if error handler was called
|
* Error check happens BEFORE phase shift calls.
|
||||||
*/
|
*/
|
||||||
static int lo_init_sequence_extracted(ADF4382A_Manager *lo_manager)
|
static int lo_init_sequence_extracted(ADF4382A_Manager *lo_manager)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* Line 1552: Init the manager */
|
|
||||||
ret = ADF4382A_Manager_Init(lo_manager, SYNC_METHOD_TIMED);
|
ret = ADF4382A_Manager_Init(lo_manager, SYNC_METHOD_TIMED);
|
||||||
|
|
||||||
/* Lines 1561-1566: Phase shift + strobe BEFORE checking ret
|
/* [Bug #4 FIXED] Error check happens FIRST */
|
||||||
* THIS IS THE BUG — these happen regardless of init success */
|
if (ret != ADF4382A_MANAGER_OK) {
|
||||||
|
Error_Handler();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Phase shift only called after successful init */
|
||||||
|
phase_shift_called = 1;
|
||||||
int ps_ret = ADF4382A_SetPhaseShift(lo_manager, 500, 500);
|
int ps_ret = ADF4382A_SetPhaseShift(lo_manager, 500, 500);
|
||||||
(void)ps_ret;
|
(void)ps_ret;
|
||||||
|
|
||||||
@@ -49,12 +50,6 @@ static int lo_init_sequence_extracted(ADF4382A_Manager *lo_manager)
|
|||||||
(void)strobe_tx_ret;
|
(void)strobe_tx_ret;
|
||||||
(void)strobe_rx_ret;
|
(void)strobe_rx_ret;
|
||||||
|
|
||||||
/* Line 1569: NOW the error check happens */
|
|
||||||
if (ret != ADF4382A_MANAGER_OK) {
|
|
||||||
Error_Handler();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,60 +57,48 @@ int main(void)
|
|||||||
{
|
{
|
||||||
ADF4382A_Manager mgr;
|
ADF4382A_Manager mgr;
|
||||||
|
|
||||||
printf("=== Bug #4: Phase shift called before init check ===\n");
|
printf("=== Bug #4 (FIXED): Phase shift after init check ===\n");
|
||||||
|
|
||||||
/* ---- Test A: Successful init — phase shift calls still happen before check ---- */
|
/* ---- Test A: Successful init — phase shift happens normally ---- */
|
||||||
spy_reset();
|
spy_reset();
|
||||||
error_handler_called = 0;
|
error_handler_called = 0;
|
||||||
mock_adf4382_init_retval = 0; /* success */
|
phase_shift_called = 0;
|
||||||
|
mock_adf4382_init_retval = 0;
|
||||||
|
|
||||||
int result = lo_init_sequence_extracted(&mgr);
|
int result = lo_init_sequence_extracted(&mgr);
|
||||||
assert(result == 0);
|
assert(result == 0);
|
||||||
assert(error_handler_called == 0);
|
assert(error_handler_called == 0);
|
||||||
|
assert(phase_shift_called == 1);
|
||||||
|
printf(" PASS: Successful init — phase shift called after error check\n");
|
||||||
|
|
||||||
/* 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);
|
int init_count = spy_count_type(SPY_ADF4382_INIT);
|
||||||
printf(" Successful path: ADF4382_INIT calls: %d (expected 2: TX+RX)\n", init_count);
|
printf(" ADF4382_INIT calls: %d (expected 2: TX+RX)\n", init_count);
|
||||||
assert(init_count == 2);
|
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);
|
int total_gpio_writes = spy_count_type(SPY_GPIO_WRITE);
|
||||||
printf(" Total GPIO write records: %d (includes CE, DELADJ, DELSTR, phase)\n",
|
printf(" GPIO writes: %d (includes CE, DELADJ, DELSTR, phase)\n", total_gpio_writes);
|
||||||
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);
|
assert(total_gpio_writes > 0);
|
||||||
printf(" PASS: Phase shift GPIO writes observed in spy log\n");
|
printf(" PASS: Phase shift GPIO writes observed\n");
|
||||||
|
|
||||||
/* Cleanup */
|
|
||||||
ADF4382A_Manager_Deinit(&mgr);
|
ADF4382A_Manager_Deinit(&mgr);
|
||||||
|
|
||||||
/* ---- Test B: Failed init — phase shift still called anyway ---- */
|
/* ---- Test B: Failed init — Error_Handler called, NO phase shift ---- */
|
||||||
printf("\n Testing with failed TX init...\n");
|
printf("\n Testing with failed TX init...\n");
|
||||||
spy_reset();
|
spy_reset();
|
||||||
error_handler_called = 0;
|
error_handler_called = 0;
|
||||||
mock_adf4382_init_retval = -1; /* TX init will fail */
|
phase_shift_called = 0;
|
||||||
|
mock_adf4382_init_retval = -1;
|
||||||
|
|
||||||
result = lo_init_sequence_extracted(&mgr);
|
result = lo_init_sequence_extracted(&mgr);
|
||||||
assert(result == 1); /* error handler was called */
|
assert(result == 1);
|
||||||
assert(error_handler_called == 1);
|
assert(error_handler_called == 1);
|
||||||
printf(" Error_Handler called: YES (as expected for failed init)\n");
|
assert(phase_shift_called == 0);
|
||||||
|
printf(" PASS: Error_Handler called, phase shift NOT called (FIXED)\n");
|
||||||
/* Even with failed init, the manager's initialized flag is false,
|
printf(" Phase shift no longer executes on uninitialized manager\n");
|
||||||
* 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 */
|
/* Reset mock */
|
||||||
mock_adf4382_init_retval = 0;
|
mock_adf4382_init_retval = 0;
|
||||||
|
|
||||||
printf("=== Bug #4: ALL TESTS PASSED ===\n\n");
|
printf("=== Bug #4 (FIXED): ALL TESTS PASSED ===\n\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* test_bug5_fine_phase_gpio_only.c
|
* test_bug5_fine_phase_gpio_only.c
|
||||||
*
|
*
|
||||||
* Bug #5: ADF4382A_SetFinePhaseShift() (lines 558-589) is a placeholder.
|
* Bug #5 (FIXED): ADF4382A_SetFinePhaseShift() was a GPIO-only placeholder.
|
||||||
* For intermediate duty_cycle values (not 0, not max), it should generate a
|
* For intermediate duty_cycle values it just set GPIO HIGH — same as max.
|
||||||
* 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:
|
* Fix: Intermediate duty cycles now use TIM3 PWM output (CH2 for TX, CH3 for
|
||||||
* 1. Initialize manager, fix sync (workaround Bug #1).
|
* RX). The PWM output is low-pass filtered externally to produce a DC voltage
|
||||||
* 2. Call SetFinePhaseShift with duty_cycle=0 → verify GPIO LOW.
|
* proportional to the delay. Edge cases (0 and max) still use static GPIO.
|
||||||
* 3. Call SetFinePhaseShift with duty_cycle=MAX → verify GPIO HIGH.
|
*
|
||||||
* 4. Call SetFinePhaseShift with duty_cycle=500 (intermediate) → verify
|
* Test strategy (post-fix):
|
||||||
* it also just sets GPIO HIGH (the bug — should be PWM, not bang-bang).
|
* 1. duty=0 → PWM stopped, GPIO LOW (no change).
|
||||||
* 5. Verify NO timer/PWM configuration spy records exist.
|
* 2. duty=MAX → PWM stopped, GPIO HIGH (no change).
|
||||||
|
* 3. duty=500 (intermediate) → SPY_TIM_SET_COMPARE + SPY_TIM_PWM_START
|
||||||
|
* recorded, NO static GPIO write for the DELADJ pin.
|
||||||
|
* 4. Verify compare value matches the duty cycle.
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
#include "adf4382a_manager.h"
|
#include "adf4382a_manager.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
@@ -23,72 +24,98 @@ int main(void)
|
|||||||
ADF4382A_Manager mgr;
|
ADF4382A_Manager mgr;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
printf("=== Bug #5: SetFinePhaseShift is GPIO-only placeholder ===\n");
|
printf("=== Bug #5 (FIXED): SetFinePhaseShift uses TIM PWM ===\n");
|
||||||
|
|
||||||
/* Setup: init + manual sync fix */
|
/* Setup: init manager */
|
||||||
spy_reset();
|
spy_reset();
|
||||||
ret = ADF4382A_Manager_Init(&mgr, SYNC_METHOD_TIMED);
|
ret = ADF4382A_Manager_Init(&mgr, SYNC_METHOD_TIMED);
|
||||||
assert(ret == ADF4382A_MANAGER_OK);
|
assert(ret == ADF4382A_MANAGER_OK);
|
||||||
ret = ADF4382A_SetupTimedSync(&mgr);
|
|
||||||
assert(ret == ADF4382A_MANAGER_OK);
|
|
||||||
|
|
||||||
/* ---- Test A: duty_cycle=0 → GPIO LOW ---- */
|
/* ---- Test A: duty_cycle=0 → PWM stopped, GPIO LOW ---- */
|
||||||
spy_reset();
|
spy_reset();
|
||||||
ret = ADF4382A_SetFinePhaseShift(&mgr, 0, 0); /* device=0 (TX), duty=0 */
|
ret = ADF4382A_SetFinePhaseShift(&mgr, 0, 0);
|
||||||
assert(ret == ADF4382A_MANAGER_OK);
|
assert(ret == ADF4382A_MANAGER_OK);
|
||||||
|
|
||||||
/* Find the GPIO write for TX_DELADJ pin */
|
int pwm_stop_count = spy_count_type(SPY_TIM_PWM_STOP);
|
||||||
int gpio_idx = spy_find_nth(SPY_GPIO_WRITE, 0);
|
int gpio_writes = spy_count_type(SPY_GPIO_WRITE);
|
||||||
assert(gpio_idx >= 0);
|
printf(" duty=0: PWM_STOP=%d GPIO_WRITE=%d\n", pwm_stop_count, gpio_writes);
|
||||||
const SpyRecord *r = spy_get(gpio_idx);
|
assert(pwm_stop_count == 1); /* stop PWM before driving GPIO */
|
||||||
assert(r != NULL);
|
assert(gpio_writes >= 1); /* at least one GPIO write (LOW) */
|
||||||
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 ---- */
|
/* Verify the GPIO write is LOW */
|
||||||
|
int idx = spy_find_nth(SPY_GPIO_WRITE, 0);
|
||||||
|
const SpyRecord *r = spy_get(idx);
|
||||||
|
assert(r != NULL && r->value == GPIO_PIN_RESET);
|
||||||
|
printf(" PASS: duty=0 → PWM stopped + GPIO LOW\n");
|
||||||
|
|
||||||
|
/* ---- Test B: duty_cycle=MAX → PWM stopped, GPIO HIGH ---- */
|
||||||
spy_reset();
|
spy_reset();
|
||||||
ret = ADF4382A_SetFinePhaseShift(&mgr, 0, DELADJ_MAX_DUTY_CYCLE);
|
ret = ADF4382A_SetFinePhaseShift(&mgr, 0, DELADJ_MAX_DUTY_CYCLE);
|
||||||
assert(ret == ADF4382A_MANAGER_OK);
|
assert(ret == ADF4382A_MANAGER_OK);
|
||||||
|
|
||||||
gpio_idx = spy_find_nth(SPY_GPIO_WRITE, 0);
|
pwm_stop_count = spy_count_type(SPY_TIM_PWM_STOP);
|
||||||
assert(gpio_idx >= 0);
|
gpio_writes = spy_count_type(SPY_GPIO_WRITE);
|
||||||
r = spy_get(gpio_idx);
|
printf(" duty=MAX(%d): PWM_STOP=%d GPIO_WRITE=%d\n",
|
||||||
assert(r != NULL);
|
DELADJ_MAX_DUTY_CYCLE, pwm_stop_count, gpio_writes);
|
||||||
printf(" duty=MAX(%d): GPIO write value=%u\n", DELADJ_MAX_DUTY_CYCLE, r->value);
|
assert(pwm_stop_count == 1);
|
||||||
assert(r->value == GPIO_PIN_SET);
|
assert(gpio_writes >= 1);
|
||||||
printf(" PASS: duty=MAX → GPIO HIGH (correct)\n");
|
|
||||||
|
|
||||||
/* ---- Test C: duty_cycle=500 (intermediate) → GPIO HIGH (BUG) ---- */
|
idx = spy_find_nth(SPY_GPIO_WRITE, 0);
|
||||||
|
r = spy_get(idx);
|
||||||
|
assert(r != NULL && r->value == GPIO_PIN_SET);
|
||||||
|
printf(" PASS: duty=MAX → PWM stopped + GPIO HIGH\n");
|
||||||
|
|
||||||
|
/* ---- Test C: duty_cycle=500 (intermediate) → TIM PWM ---- */
|
||||||
spy_reset();
|
spy_reset();
|
||||||
ret = ADF4382A_SetFinePhaseShift(&mgr, 0, 500);
|
ret = ADF4382A_SetFinePhaseShift(&mgr, 0, 500); /* device=0 (TX) */
|
||||||
assert(ret == ADF4382A_MANAGER_OK);
|
assert(ret == ADF4382A_MANAGER_OK);
|
||||||
|
|
||||||
gpio_idx = spy_find_nth(SPY_GPIO_WRITE, 0);
|
int pwm_start_count = spy_count_type(SPY_TIM_PWM_START);
|
||||||
assert(gpio_idx >= 0);
|
int set_compare_count = spy_count_type(SPY_TIM_SET_COMPARE);
|
||||||
r = spy_get(gpio_idx);
|
gpio_writes = spy_count_type(SPY_GPIO_WRITE);
|
||||||
|
printf(" duty=500: PWM_START=%d SET_COMPARE=%d GPIO_WRITE=%d\n",
|
||||||
|
pwm_start_count, set_compare_count, gpio_writes);
|
||||||
|
assert(pwm_start_count == 1);
|
||||||
|
assert(set_compare_count == 1);
|
||||||
|
assert(gpio_writes == 0); /* No static GPIO write for intermediate */
|
||||||
|
|
||||||
|
/* Verify compare value is 500 */
|
||||||
|
idx = spy_find_nth(SPY_TIM_SET_COMPARE, 0);
|
||||||
|
r = spy_get(idx);
|
||||||
assert(r != NULL);
|
assert(r != NULL);
|
||||||
printf(" duty=500 (intermediate): GPIO write value=%u\n", r->value);
|
printf(" SET_COMPARE value=%u (expected 500)\n", r->value);
|
||||||
assert(r->value == GPIO_PIN_SET);
|
assert(r->value == 500);
|
||||||
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 ---- */
|
/* Verify TIM channel is CH2 (TX device = 0 → TIM_CHANNEL_2 = 0x04) */
|
||||||
/* If proper PWM were set up, we'd see timer config calls or multiple
|
idx = spy_find_nth(SPY_TIM_PWM_START, 0);
|
||||||
* GPIO toggles. Instead, there's just a single GPIO write. */
|
r = spy_get(idx);
|
||||||
int total_gpio = spy_count_type(SPY_GPIO_WRITE);
|
assert(r != NULL);
|
||||||
printf(" Total GPIO writes for intermediate duty: %d (expected 1 — no PWM)\n",
|
printf(" PWM_START channel=0x%02X (expected 0x%02X = TIM_CHANNEL_2)\n",
|
||||||
total_gpio);
|
r->pin, TIM_CHANNEL_2);
|
||||||
assert(total_gpio == 1);
|
assert(r->pin == TIM_CHANNEL_2);
|
||||||
printf(" PASS: Only 1 GPIO write — confirms no PWM generation\n");
|
printf(" PASS: duty=500 → TIM PWM with correct compare value\n");
|
||||||
|
|
||||||
/* ---- Test E: duty=500 produces SAME output as duty=MAX ---- */
|
/* ---- Test D: RX device (1) uses TIM_CHANNEL_3 ---- */
|
||||||
printf(" BUG CONFIRMED: duty=500 and duty=MAX both produce identical GPIO HIGH\n");
|
spy_reset();
|
||||||
printf(" Any intermediate value is treated as 100%% duty — no proportional control\n");
|
ret = ADF4382A_SetFinePhaseShift(&mgr, 1, 750); /* device=1 (RX) */
|
||||||
|
assert(ret == ADF4382A_MANAGER_OK);
|
||||||
|
|
||||||
|
idx = spy_find_nth(SPY_TIM_PWM_START, 0);
|
||||||
|
r = spy_get(idx);
|
||||||
|
assert(r != NULL);
|
||||||
|
printf(" RX PWM_START channel=0x%02X (expected 0x%02X = TIM_CHANNEL_3)\n",
|
||||||
|
r->pin, TIM_CHANNEL_3);
|
||||||
|
assert(r->pin == TIM_CHANNEL_3);
|
||||||
|
|
||||||
|
idx = spy_find_nth(SPY_TIM_SET_COMPARE, 0);
|
||||||
|
r = spy_get(idx);
|
||||||
|
assert(r != NULL && r->value == 750);
|
||||||
|
printf(" RX SET_COMPARE value=%u (expected 750) OK\n", r->value);
|
||||||
|
printf(" PASS: RX device uses TIM_CHANNEL_3 with correct compare\n");
|
||||||
|
|
||||||
/* Cleanup */
|
/* Cleanup */
|
||||||
ADF4382A_Manager_Deinit(&mgr);
|
ADF4382A_Manager_Deinit(&mgr);
|
||||||
|
|
||||||
printf("=== Bug #5: ALL TESTS PASSED ===\n\n");
|
printf("\n=== Bug #5: ALL TESTS PASSED (post-fix) ===\n\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,57 +1,38 @@
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* test_bug6_timer_variable_collision.c
|
* test_bug6_timer_variable_collision.c
|
||||||
*
|
*
|
||||||
* Bug #6: In main.cpp, the lock-check timer uses `last_check` (line 1981)
|
* Bug #6 (FIXED): In main.cpp, the temperature block now writes to
|
||||||
* and the temperature timer uses `last_check1` (line 2005). But at line 2038,
|
* `last_check1` instead of `last_check`, so both timers run independently.
|
||||||
* the temperature block writes to `last_check` INSTEAD OF `last_check1`.
|
|
||||||
*
|
*
|
||||||
* Effect:
|
* Post-fix behavior:
|
||||||
* - After the first 5s window, temperature reads fire continuously
|
* - Lock check fires every ~5s using `last_check`.
|
||||||
* (because last_check1 is never updated).
|
* - Temperature check fires every ~5s using `last_check1`.
|
||||||
* - The lock-check timer gets reset by the temperature block's write to
|
* - Neither timer corrupts the other.
|
||||||
* 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 "stm32_hal_mock.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 */
|
/* Counters to track how many times each block fires */
|
||||||
static int lock_check_fired = 0;
|
static int lock_check_fired = 0;
|
||||||
static int temp_check_fired = 0;
|
static int temp_check_fired = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Simulates one iteration of the main loop timer blocks.
|
* Simulates one iteration of the main loop timer blocks.
|
||||||
* Uses the EXACT code pattern from main.cpp — including the bug.
|
* Uses the FIXED code pattern from main.cpp.
|
||||||
*/
|
*/
|
||||||
static void main_loop_iteration(uint32_t *last_check_p, uint32_t *last_check1_p)
|
static void main_loop_iteration(uint32_t *last_check_p, uint32_t *last_check1_p)
|
||||||
{
|
{
|
||||||
/* ---- Lock check block (lines 1981-1999) ---- */
|
/* ---- Lock check block ---- */
|
||||||
if (HAL_GetTick() - *last_check_p > 5000) {
|
if (HAL_GetTick() - *last_check_p > 5000) {
|
||||||
/* Would call ADF4382A_CheckLockStatus here */
|
|
||||||
lock_check_fired++;
|
lock_check_fired++;
|
||||||
*last_check_p = HAL_GetTick(); /* line 1998: correct */
|
*last_check_p = HAL_GetTick();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- Temperature check block (lines 2005-2039) ---- */
|
/* ---- Temperature check block (FIXED: writes to last_check1) ---- */
|
||||||
if (HAL_GetTick() - *last_check1_p > 5000) {
|
if (HAL_GetTick() - *last_check1_p > 5000) {
|
||||||
/* Would read temperature sensors here */
|
|
||||||
temp_check_fired++;
|
temp_check_fired++;
|
||||||
|
*last_check1_p = HAL_GetTick(); /* FIXED: was *last_check_p */
|
||||||
/* 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(); */
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,9 +41,9 @@ int main(void)
|
|||||||
uint32_t last_check = 0;
|
uint32_t last_check = 0;
|
||||||
uint32_t last_check1 = 0;
|
uint32_t last_check1 = 0;
|
||||||
|
|
||||||
printf("=== Bug #6: Timer variable collision ===\n");
|
printf("=== Bug #6 (FIXED): Timer variable collision ===\n");
|
||||||
|
|
||||||
/* ---- Iteration 1: t=0 — nothing fires (0 - 0 == 0, not > 5000) ---- */
|
/* ---- t=0: nothing fires ---- */
|
||||||
spy_reset();
|
spy_reset();
|
||||||
mock_set_tick(0);
|
mock_set_tick(0);
|
||||||
lock_check_fired = 0;
|
lock_check_fired = 0;
|
||||||
@@ -74,7 +55,7 @@ int main(void)
|
|||||||
assert(temp_check_fired == 0);
|
assert(temp_check_fired == 0);
|
||||||
printf(" PASS: Neither fires at t=0\n");
|
printf(" PASS: Neither fires at t=0\n");
|
||||||
|
|
||||||
/* ---- Iteration 2: t=5001 — both should fire ---- */
|
/* ---- t=5001: both fire ---- */
|
||||||
mock_set_tick(5001);
|
mock_set_tick(5001);
|
||||||
main_loop_iteration(&last_check, &last_check1);
|
main_loop_iteration(&last_check, &last_check1);
|
||||||
printf(" t=5001ms: lock_fired=%d temp_fired=%d\n", lock_check_fired, temp_check_fired);
|
printf(" t=5001ms: lock_fired=%d temp_fired=%d\n", lock_check_fired, temp_check_fired);
|
||||||
@@ -82,50 +63,40 @@ int main(void)
|
|||||||
assert(temp_check_fired == 1);
|
assert(temp_check_fired == 1);
|
||||||
printf(" PASS: Both fire at t=5001\n");
|
printf(" PASS: Both fire at t=5001\n");
|
||||||
|
|
||||||
/* Check variable state after first fire */
|
/* Both variables should be updated independently */
|
||||||
printf(" After first fire: last_check=%u last_check1=%u\n", last_check, last_check1);
|
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);
|
assert(last_check == 5001);
|
||||||
/* BUG: last_check1 was NEVER updated — still 0 */
|
assert(last_check1 == 5001);
|
||||||
assert(last_check1 == 0);
|
printf(" PASS: Both timers updated independently\n");
|
||||||
printf(" PASS: last_check1 is still 0 (BUG confirmed — never written)\n");
|
|
||||||
|
|
||||||
/* ---- Iteration 3: t=5002 — temp fires AGAIN (because last_check1==0) ---- */
|
/* ---- t=5002: neither fires (only 1ms elapsed) ---- */
|
||||||
mock_set_tick(5002);
|
mock_set_tick(5002);
|
||||||
main_loop_iteration(&last_check, &last_check1);
|
main_loop_iteration(&last_check, &last_check1);
|
||||||
printf(" t=5002ms: lock_fired=%d temp_fired=%d\n", lock_check_fired, temp_check_fired);
|
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(lock_check_fired == 1);
|
||||||
assert(temp_check_fired == 2); /* FIRED AGAIN — 5002-0=5002, >5000 */
|
assert(temp_check_fired == 1);
|
||||||
printf(" PASS: Temperature fired again immediately (continuous polling bug)\n");
|
printf(" PASS: Neither fires at t=5002 (correct — temp no longer runs continuously)\n");
|
||||||
|
|
||||||
/* ---- Iteration 4: t=5003 — temp fires AGAIN ---- */
|
/* ---- t=10002: both fire again ---- */
|
||||||
mock_set_tick(5003);
|
mock_set_tick(10002);
|
||||||
main_loop_iteration(&last_check, &last_check1);
|
main_loop_iteration(&last_check, &last_check1);
|
||||||
printf(" t=5003ms: lock_fired=%d temp_fired=%d\n", lock_check_fired, temp_check_fired);
|
printf(" t=10002ms: 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);
|
assert(lock_check_fired == 2);
|
||||||
/* But temp has been firing EVERY iteration since t=5001, so it fires here too */
|
assert(temp_check_fired == 2);
|
||||||
printf(" temp_fired=%d (fires every iteration since t=5001)\n", temp_check_fired);
|
printf(" PASS: Both fire at t=10002 (second cycle, independent)\n");
|
||||||
assert(temp_check_fired >= 4);
|
|
||||||
printf(" PASS: Both timers corrupted — lock delayed, temp runs continuously\n");
|
|
||||||
|
|
||||||
printf("=== Bug #6: ALL TESTS PASSED ===\n\n");
|
/* Verify both timers updated correctly */
|
||||||
|
assert(last_check == 10002);
|
||||||
|
assert(last_check1 == 10002);
|
||||||
|
printf(" PASS: Both timers at 10002, no cross-contamination\n");
|
||||||
|
|
||||||
|
/* ---- t=15003: third cycle ---- */
|
||||||
|
mock_set_tick(15003);
|
||||||
|
main_loop_iteration(&last_check, &last_check1);
|
||||||
|
assert(lock_check_fired == 3);
|
||||||
|
assert(temp_check_fired == 3);
|
||||||
|
printf(" PASS: Third cycle fires correctly at t=15003\n");
|
||||||
|
|
||||||
|
printf("=== Bug #6 (FIXED): ALL TESTS PASSED ===\n\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,122 +1,155 @@
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* test_bug7_gpio_pin_conflict.c
|
* test_bug7_gpio_pin_conflict.c
|
||||||
*
|
*
|
||||||
* Bug #7: adf4382a_manager.h defines GPIOG pins 0-9 for ADF4382 signals,
|
* Bug #7 (FIXED): adf4382a_manager.h previously defined GPIOG pins 0-9 for
|
||||||
* but main.h (CubeMX-generated) assigns:
|
* ADF4382 signals, conflicting with CubeMX main.h which assigns:
|
||||||
* - GPIOG pins 0-5 → PA enables + clock enables
|
* - GPIOG pins 0-5 → PA enables + clock enables
|
||||||
* - GPIOG pins 6-15 → ADF4382 signals (DIFFERENT pin assignments)
|
* - GPIOG pins 6-15 → ADF4382 signals
|
||||||
*
|
*
|
||||||
* The .c file uses the manager.h pin mapping, meaning it will toggle PA
|
* The fix updated manager.h pin definitions to match CubeMX:
|
||||||
* enables and clock enables instead of the intended ADF4382 pins.
|
* RX: LKDET=PIN_6, DELADJ=PIN_7, DELSTR=PIN_8, CE=PIN_9, CS=PIN_10
|
||||||
|
* TX: LKDET=PIN_11, DELSTR=PIN_12, DELADJ=PIN_13, CS=PIN_14, CE=PIN_15
|
||||||
*
|
*
|
||||||
* Example conflicts:
|
* Test strategy (post-fix):
|
||||||
* manager.h: TX_CE_Pin = GPIO_PIN_0 main.h: EN_P_5V0_PA1_Pin = GPIO_PIN_0
|
* 1. Verify each manager.h pin definition matches the CubeMX-correct value.
|
||||||
* manager.h: TX_CS_Pin = GPIO_PIN_1 main.h: EN_P_5V0_PA2_Pin = GPIO_PIN_1
|
* 2. Verify NO manager.h pin overlaps with PA/clock enable pins (0-5).
|
||||||
* manager.h: TX_DELADJ_Pin = GPIO_PIN_2 main.h: EN_P_5V0_PA3_Pin = GPIO_PIN_2
|
* 3. Verify all manager.h pins are in the GPIOG 6-15 range.
|
||||||
* 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 <assert.h>
|
#include <assert.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
/* We need to manually define the pins from BOTH headers since the shim
|
#include "stm32_hal_mock.h"
|
||||||
* main.h already has the CubeMX definitions, and including adf4382a_manager.h
|
#include "adf4382a_manager.h"
|
||||||
* would re-define them (which is exactly the production bug). */
|
|
||||||
|
|
||||||
/* ---- Pin definitions from adf4382a_manager.h ---- */
|
/* ---- CubeMX-correct pin definitions from main.h ---- */
|
||||||
#define MGR_TX_CE_Pin ((uint16_t)0x0001) /* GPIO_PIN_0 */
|
#define CUBEMX_ADF4382_RX_LKDET_Pin GPIO_PIN_6
|
||||||
#define MGR_TX_CS_Pin ((uint16_t)0x0002) /* GPIO_PIN_1 */
|
#define CUBEMX_ADF4382_RX_DELADJ_Pin GPIO_PIN_7
|
||||||
#define MGR_TX_DELADJ_Pin ((uint16_t)0x0004) /* GPIO_PIN_2 */
|
#define CUBEMX_ADF4382_RX_DELSTR_Pin GPIO_PIN_8
|
||||||
#define MGR_TX_DELSTR_Pin ((uint16_t)0x0008) /* GPIO_PIN_3 */
|
#define CUBEMX_ADF4382_RX_CE_Pin GPIO_PIN_9
|
||||||
#define MGR_TX_LKDET_Pin ((uint16_t)0x0010) /* GPIO_PIN_4 */
|
#define CUBEMX_ADF4382_RX_CS_Pin GPIO_PIN_10
|
||||||
#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 CUBEMX_ADF4382_TX_LKDET_Pin GPIO_PIN_11
|
||||||
#define MAIN_EN_P_5V0_PA1_Pin ((uint16_t)0x0001) /* GPIO_PIN_0 — GPIOG */
|
#define CUBEMX_ADF4382_TX_DELSTR_Pin GPIO_PIN_12
|
||||||
#define MAIN_EN_P_5V0_PA2_Pin ((uint16_t)0x0002) /* GPIO_PIN_1 — GPIOG */
|
#define CUBEMX_ADF4382_TX_DELADJ_Pin GPIO_PIN_13
|
||||||
#define MAIN_EN_P_5V0_PA3_Pin ((uint16_t)0x0004) /* GPIO_PIN_2 — GPIOG */
|
#define CUBEMX_ADF4382_TX_CS_Pin GPIO_PIN_14
|
||||||
#define MAIN_EN_P_5V5_PA_Pin ((uint16_t)0x0008) /* GPIO_PIN_3 — GPIOG */
|
#define CUBEMX_ADF4382_TX_CE_Pin GPIO_PIN_15
|
||||||
#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 */
|
/* PA/clock enable pins that must NOT be used by ADF4382 manager */
|
||||||
#define MAIN_ADF4382_TX_CE_Pin ((uint16_t)0x8000) /* GPIO_PIN_15 */
|
#define PA_CLK_ENABLE_MASK (GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | \
|
||||||
#define MAIN_ADF4382_TX_CS_Pin ((uint16_t)0x4000) /* GPIO_PIN_14 */
|
GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5)
|
||||||
#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 main(void)
|
||||||
{
|
{
|
||||||
int conflicts = 0;
|
printf("=== Bug #7 (FIXED): GPIO pin mapping — verify CubeMX match ===\n\n");
|
||||||
|
|
||||||
printf("=== Bug #7: GPIO pin mapping conflict ===\n");
|
/* ---- 1. Verify RX pin definitions match CubeMX ---- */
|
||||||
printf("\n Checking manager.h pins vs CubeMX main.h pins (all GPIOG):\n\n");
|
printf(" RX pin verification:\n");
|
||||||
|
|
||||||
/* ---- Conflict checks: manager.h ADF4382 pins == main.h power enables ---- */
|
printf(" RX_LKDET_Pin = 0x%04X expected 0x%04X (GPIO_PIN_6) ",
|
||||||
|
(unsigned)RX_LKDET_Pin, (unsigned)CUBEMX_ADF4382_RX_LKDET_Pin);
|
||||||
|
assert(RX_LKDET_Pin == CUBEMX_ADF4382_RX_LKDET_Pin);
|
||||||
|
printf("OK\n");
|
||||||
|
|
||||||
#define CHECK_CONFLICT(mgr_name, mgr_val, main_name, main_val) do { \
|
printf(" RX_DELADJ_Pin = 0x%04X expected 0x%04X (GPIO_PIN_7) ",
|
||||||
printf(" %-20s = 0x%04X vs %-25s = 0x%04X", #mgr_name, mgr_val, \
|
(unsigned)RX_DELADJ_Pin, (unsigned)CUBEMX_ADF4382_RX_DELADJ_Pin);
|
||||||
#main_name, main_val); \
|
assert(RX_DELADJ_Pin == CUBEMX_ADF4382_RX_DELADJ_Pin);
|
||||||
if ((mgr_val) == (main_val)) { \
|
printf("OK\n");
|
||||||
printf(" ** CONFLICT **\n"); \
|
|
||||||
conflicts++; \
|
|
||||||
} else { \
|
|
||||||
printf(" (ok)\n"); \
|
|
||||||
} \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
printf(" --- manager.h TX pins collide with PA/clock enables ---\n");
|
printf(" RX_DELSTR_Pin = 0x%04X expected 0x%04X (GPIO_PIN_8) ",
|
||||||
CHECK_CONFLICT(MGR_TX_CE, MGR_TX_CE_Pin, MAIN_EN_P_5V0_PA1, MAIN_EN_P_5V0_PA1_Pin);
|
(unsigned)RX_DELSTR_Pin, (unsigned)CUBEMX_ADF4382_RX_DELSTR_Pin);
|
||||||
CHECK_CONFLICT(MGR_TX_CS, MGR_TX_CS_Pin, MAIN_EN_P_5V0_PA2, MAIN_EN_P_5V0_PA2_Pin);
|
assert(RX_DELSTR_Pin == CUBEMX_ADF4382_RX_DELSTR_Pin);
|
||||||
CHECK_CONFLICT(MGR_TX_DELADJ, MGR_TX_DELADJ_Pin, MAIN_EN_P_5V0_PA3, MAIN_EN_P_5V0_PA3_Pin);
|
printf("OK\n");
|
||||||
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");
|
printf(" RX_CE_Pin = 0x%04X expected 0x%04X (GPIO_PIN_9) ",
|
||||||
CHECK_CONFLICT(MGR_TX_CE, MGR_TX_CE_Pin, MAIN_ADF_TX_CE, MAIN_ADF4382_TX_CE_Pin);
|
(unsigned)RX_CE_Pin, (unsigned)CUBEMX_ADF4382_RX_CE_Pin);
|
||||||
CHECK_CONFLICT(MGR_TX_CS, MGR_TX_CS_Pin, MAIN_ADF_TX_CS, MAIN_ADF4382_TX_CS_Pin);
|
assert(RX_CE_Pin == CUBEMX_ADF4382_RX_CE_Pin);
|
||||||
CHECK_CONFLICT(MGR_TX_DELADJ, MGR_TX_DELADJ_Pin, MAIN_ADF_TX_DADJ, MAIN_ADF4382_TX_DELADJ_Pin);
|
printf("OK\n");
|
||||||
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);
|
printf(" RX_CS_Pin = 0x%04X expected 0x%04X (GPIO_PIN_10) ",
|
||||||
|
(unsigned)RX_CS_Pin, (unsigned)CUBEMX_ADF4382_RX_CS_Pin);
|
||||||
|
assert(RX_CS_Pin == CUBEMX_ADF4382_RX_CS_Pin);
|
||||||
|
printf("OK\n");
|
||||||
|
|
||||||
/* We expect 6 conflicts (the PA/clock enable collisions) and
|
/* ---- 2. Verify TX pin definitions match CubeMX ---- */
|
||||||
* 5 mismatches (manager.h pins != correct CubeMX ADF4382 pins) */
|
printf("\n TX pin verification:\n");
|
||||||
assert(conflicts >= 6);
|
|
||||||
printf(" PASS: At least 6 pin conflicts confirmed\n");
|
|
||||||
|
|
||||||
/* ---- Verify specific critical conflict: TX_CE writes to PA1 enable ---- */
|
printf(" TX_LKDET_Pin = 0x%04X expected 0x%04X (GPIO_PIN_11) ",
|
||||||
printf("\n Critical safety issue:\n");
|
(unsigned)TX_LKDET_Pin, (unsigned)CUBEMX_ADF4382_TX_LKDET_Pin);
|
||||||
printf(" When adf4382a_manager.c writes TX_CE_Pin (0x%04X) on GPIOG,\n",
|
assert(TX_LKDET_Pin == CUBEMX_ADF4382_TX_LKDET_Pin);
|
||||||
MGR_TX_CE_Pin);
|
printf("OK\n");
|
||||||
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");
|
printf(" TX_DELSTR_Pin = 0x%04X expected 0x%04X (GPIO_PIN_12) ",
|
||||||
|
(unsigned)TX_DELSTR_Pin, (unsigned)CUBEMX_ADF4382_TX_DELSTR_Pin);
|
||||||
|
assert(TX_DELSTR_Pin == CUBEMX_ADF4382_TX_DELSTR_Pin);
|
||||||
|
printf("OK\n");
|
||||||
|
|
||||||
|
printf(" TX_DELADJ_Pin = 0x%04X expected 0x%04X (GPIO_PIN_13) ",
|
||||||
|
(unsigned)TX_DELADJ_Pin, (unsigned)CUBEMX_ADF4382_TX_DELADJ_Pin);
|
||||||
|
assert(TX_DELADJ_Pin == CUBEMX_ADF4382_TX_DELADJ_Pin);
|
||||||
|
printf("OK\n");
|
||||||
|
|
||||||
|
printf(" TX_CS_Pin = 0x%04X expected 0x%04X (GPIO_PIN_14) ",
|
||||||
|
(unsigned)TX_CS_Pin, (unsigned)CUBEMX_ADF4382_TX_CS_Pin);
|
||||||
|
assert(TX_CS_Pin == CUBEMX_ADF4382_TX_CS_Pin);
|
||||||
|
printf("OK\n");
|
||||||
|
|
||||||
|
printf(" TX_CE_Pin = 0x%04X expected 0x%04X (GPIO_PIN_15) ",
|
||||||
|
(unsigned)TX_CE_Pin, (unsigned)CUBEMX_ADF4382_TX_CE_Pin);
|
||||||
|
assert(TX_CE_Pin == CUBEMX_ADF4382_TX_CE_Pin);
|
||||||
|
printf("OK\n");
|
||||||
|
|
||||||
|
/* ---- 3. Verify NO overlap with PA/clock enable pins (0-5) ---- */
|
||||||
|
printf("\n PA/clock enable conflict check:\n");
|
||||||
|
|
||||||
|
uint16_t all_adf_pins = RX_LKDET_Pin | RX_DELADJ_Pin | RX_DELSTR_Pin |
|
||||||
|
RX_CE_Pin | RX_CS_Pin |
|
||||||
|
TX_LKDET_Pin | TX_DELSTR_Pin | TX_DELADJ_Pin |
|
||||||
|
TX_CS_Pin | TX_CE_Pin;
|
||||||
|
|
||||||
|
uint16_t overlap = all_adf_pins & PA_CLK_ENABLE_MASK;
|
||||||
|
printf(" ADF4382 pin mask: 0x%04X\n", (unsigned)all_adf_pins);
|
||||||
|
printf(" PA/CLK enable mask: 0x%04X\n", (unsigned)PA_CLK_ENABLE_MASK);
|
||||||
|
printf(" Overlap: 0x%04X ", (unsigned)overlap);
|
||||||
|
assert(overlap == 0);
|
||||||
|
printf("OK (no conflicts)\n");
|
||||||
|
|
||||||
|
/* ---- 4. Verify all pins are unique (no two signals share a pin) ---- */
|
||||||
|
printf("\n Pin uniqueness check:\n");
|
||||||
|
uint16_t pins[] = {
|
||||||
|
RX_LKDET_Pin, RX_DELADJ_Pin, RX_DELSTR_Pin, RX_CE_Pin, RX_CS_Pin,
|
||||||
|
TX_LKDET_Pin, TX_DELSTR_Pin, TX_DELADJ_Pin, TX_CS_Pin, TX_CE_Pin
|
||||||
|
};
|
||||||
|
const char *names[] = {
|
||||||
|
"RX_LKDET", "RX_DELADJ", "RX_DELSTR", "RX_CE", "RX_CS",
|
||||||
|
"TX_LKDET", "TX_DELSTR", "TX_DELADJ", "TX_CS", "TX_CE"
|
||||||
|
};
|
||||||
|
int n = sizeof(pins) / sizeof(pins[0]);
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
for (int j = i + 1; j < n; j++) {
|
||||||
|
if (pins[i] == pins[j]) {
|
||||||
|
printf(" FAIL: %s and %s both map to 0x%04X\n",
|
||||||
|
names[i], names[j], (unsigned)pins[i]);
|
||||||
|
assert(0 && "Duplicate pin mapping detected");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf(" All 10 pins are unique OK\n");
|
||||||
|
|
||||||
|
/* ---- 5. Verify all ports are GPIOG ---- */
|
||||||
|
printf("\n Port verification:\n");
|
||||||
|
assert(RX_LKDET_GPIO_Port == GPIOG);
|
||||||
|
assert(RX_DELADJ_GPIO_Port == GPIOG);
|
||||||
|
assert(RX_DELSTR_GPIO_Port == GPIOG);
|
||||||
|
assert(RX_CE_GPIO_Port == GPIOG);
|
||||||
|
assert(RX_CS_GPIO_Port == GPIOG);
|
||||||
|
assert(TX_LKDET_GPIO_Port == GPIOG);
|
||||||
|
assert(TX_DELSTR_GPIO_Port == GPIOG);
|
||||||
|
assert(TX_DELADJ_GPIO_Port == GPIOG);
|
||||||
|
assert(TX_CS_GPIO_Port == GPIOG);
|
||||||
|
assert(TX_CE_GPIO_Port == GPIOG);
|
||||||
|
printf(" All pins on GPIOG OK\n");
|
||||||
|
|
||||||
|
printf("\n=== Bug #7: ALL TESTS PASSED (post-fix) ===\n\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* test_bug8_uart_commented_out.c
|
* test_bug8_uart_commented_out.c
|
||||||
*
|
*
|
||||||
* Bug #8: Debug helpers uart_print() and uart_println() in main.cpp
|
* Bug #8 (FIXED): Debug helpers uart_print() and uart_println() are now
|
||||||
* (lines 958-970) are commented out with block comments.
|
* uncommented and available as active code.
|
||||||
*
|
*
|
||||||
* Test strategy:
|
* Post-fix test:
|
||||||
* Read the source file and verify the functions are inside comment blocks.
|
* Read the source file and verify the functions are NOT inside comment blocks.
|
||||||
* This is a static analysis / string-search test — no compilation of
|
|
||||||
* the source under test is needed.
|
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -36,46 +34,38 @@ static char *read_file(const char *path, long *out_size)
|
|||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
printf("=== Bug #8: uart_print/uart_println commented out ===\n");
|
printf("=== Bug #8 (FIXED): uart_print/uart_println uncommented ===\n");
|
||||||
|
|
||||||
long size;
|
long size;
|
||||||
char *src = read_file(SOURCE_FILE, &size);
|
char *src = read_file(SOURCE_FILE, &size);
|
||||||
if (!src) {
|
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);
|
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");
|
assert(src != NULL && "Could not open main.cpp");
|
||||||
printf(" Read %ld bytes from main.cpp\n", size);
|
printf(" Read %ld bytes from main.cpp\n", size);
|
||||||
|
|
||||||
/* ---- Test A: Find "static void uart_print" — should be inside a comment ---- */
|
/* ---- Test A: uart_print is NOT inside a block comment ---- */
|
||||||
const char *uart_print_sig = "static void uart_print(const char *msg)";
|
const char *uart_print_sig = "static void uart_print(const char *msg)";
|
||||||
char *pos = strstr(src, uart_print_sig);
|
char *pos = strstr(src, uart_print_sig);
|
||||||
assert(pos != NULL && "uart_print function signature not found in source");
|
assert(pos != NULL && "uart_print function signature not found in source");
|
||||||
printf(" Found uart_print signature at offset %ld\n", (long)(pos - src));
|
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;
|
int inside_comment = 0;
|
||||||
for (char *p = src; p < pos; p++) {
|
for (char *p = src; p < pos; p++) {
|
||||||
if (p[0] == '/' && p[1] == '*') {
|
if (p[0] == '/' && p[1] == '*') {
|
||||||
inside_comment = 1;
|
inside_comment = 1;
|
||||||
p++; /* skip past '*' */
|
p++;
|
||||||
} else if (p[0] == '*' && p[1] == '/') {
|
} else if (p[0] == '*' && p[1] == '/') {
|
||||||
inside_comment = 0;
|
inside_comment = 0;
|
||||||
p++; /* skip past '/' */
|
p++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
printf(" uart_print is inside block comment: %s\n",
|
printf(" uart_print is inside block comment: %s\n",
|
||||||
inside_comment ? "YES" : "NO");
|
inside_comment ? "YES" : "NO");
|
||||||
assert(inside_comment == 1);
|
assert(inside_comment == 0);
|
||||||
printf(" PASS: uart_print is commented out\n");
|
printf(" PASS: uart_print is NOT commented out (FIXED)\n");
|
||||||
|
|
||||||
/* ---- Test B: Find "static void uart_println" — also in comment ---- */
|
/* ---- Test B: uart_println is NOT inside a block comment ---- */
|
||||||
const char *uart_println_sig = "static void uart_println(const char *msg)";
|
const char *uart_println_sig = "static void uart_println(const char *msg)";
|
||||||
pos = strstr(src, uart_println_sig);
|
pos = strstr(src, uart_println_sig);
|
||||||
assert(pos != NULL && "uart_println function signature not found in source");
|
assert(pos != NULL && "uart_println function signature not found in source");
|
||||||
@@ -93,45 +83,10 @@ int main(void)
|
|||||||
}
|
}
|
||||||
printf(" uart_println is inside block comment: %s\n",
|
printf(" uart_println is inside block comment: %s\n",
|
||||||
inside_comment ? "YES" : "NO");
|
inside_comment ? "YES" : "NO");
|
||||||
assert(inside_comment == 1);
|
assert(inside_comment == 0);
|
||||||
printf(" PASS: uart_println is commented out\n");
|
printf(" PASS: uart_println is NOT commented out (FIXED)\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);
|
free(src);
|
||||||
printf("=== Bug #8: ALL TESTS PASSED ===\n\n");
|
printf("=== Bug #8 (FIXED): ALL TESTS PASSED ===\n\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user