Bug #9: Both TX and RX SPI init params had platform_ops = NULL, causing adf4382_init() -> no_os_spi_init() to fail with -EINVAL. Fixed by setting platform_ops = &stm32_spi_ops and passing stm32_spi_extra with correct CS port/pin for each device. Bug #10: stm32_spi_write_and_read() never toggled chip select. Since TX and RX ADF4382A share SPI4, every register write hit both PLLs. Rewrote stm32_spi.c to assert CS LOW before transfer and deassert HIGH after, using stm32_spi_extra metadata. Backward-compatible: legacy callers (e.g., AD9523) with cs_port=NULL skip CS management. Also widened chip_select from uint8_t to uint16_t in no_os_spi.h since STM32 GPIO_PIN_xx values (e.g., GPIO_PIN_14=0x4000) overflow uint8_t. 10/10 tests pass (8 original + 2 new regression tests).
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
#include "adf4382a_manager.h"
|
||||
#include "stm32_spi.h"
|
||||
#include "diag_log.h"
|
||||
#include "no_os_delay.h"
|
||||
#include <stdio.h>
|
||||
@@ -12,7 +13,7 @@ extern SPI_HandleTypeDef hspi4;
|
||||
extern TIM_HandleTypeDef htim3;
|
||||
|
||||
// Static function prototypes
|
||||
static void set_chip_enable(uint8_t ce_pin, bool state);
|
||||
static void set_chip_enable(uint16_t ce_pin, bool state);
|
||||
static void set_deladj_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);
|
||||
@@ -24,6 +25,10 @@ int ADF4382A_Manager_Init(ADF4382A_Manager *manager, SyncMethod method)
|
||||
struct adf4382_init_param tx_param, rx_param;
|
||||
int ret;
|
||||
uint32_t t_start = HAL_GetTick();
|
||||
|
||||
/* Platform SPI extras carry HAL handle + software CS port/pin */
|
||||
static stm32_spi_extra spi_tx_extra;
|
||||
static stm32_spi_extra spi_rx_extra;
|
||||
|
||||
DIAG_SECTION("ADF4382A LO MANAGER INIT");
|
||||
DIAG("LO", "Init called with sync_method=%d (%s)",
|
||||
@@ -48,14 +53,23 @@ int ADF4382A_Manager_Init(ADF4382A_Manager *manager, SyncMethod method)
|
||||
memset(&manager->spi_tx_param, 0, sizeof(manager->spi_tx_param));
|
||||
memset(&manager->spi_rx_param, 0, sizeof(manager->spi_rx_param));
|
||||
|
||||
// Setup platform SPI extras with software CS for each device
|
||||
spi_tx_extra.hspi = &hspi4;
|
||||
spi_tx_extra.cs_port = TX_CS_GPIO_Port;
|
||||
spi_tx_extra.cs_pin = TX_CS_Pin;
|
||||
|
||||
spi_rx_extra.hspi = &hspi4;
|
||||
spi_rx_extra.cs_port = RX_CS_GPIO_Port;
|
||||
spi_rx_extra.cs_pin = RX_CS_Pin;
|
||||
|
||||
// Setup TX SPI parameters for SPI4
|
||||
manager->spi_tx_param.device_id = ADF4382A_SPI_DEVICE_ID;
|
||||
manager->spi_tx_param.max_speed_hz = ADF4382A_SPI_SPEED_HZ;
|
||||
manager->spi_tx_param.mode = NO_OS_SPI_MODE_0;
|
||||
manager->spi_tx_param.chip_select = TX_CS_Pin;
|
||||
manager->spi_tx_param.bit_order = NO_OS_SPI_BIT_ORDER_MSB_FIRST;
|
||||
manager->spi_tx_param.platform_ops = NULL;
|
||||
manager->spi_tx_param.extra = &hspi4;
|
||||
manager->spi_tx_param.platform_ops = &stm32_spi_ops;
|
||||
manager->spi_tx_param.extra = &spi_tx_extra;
|
||||
|
||||
// Setup RX SPI parameters for SPI4
|
||||
manager->spi_rx_param.device_id = ADF4382A_SPI_DEVICE_ID;
|
||||
@@ -63,11 +77,12 @@ int ADF4382A_Manager_Init(ADF4382A_Manager *manager, SyncMethod method)
|
||||
manager->spi_rx_param.mode = NO_OS_SPI_MODE_0;
|
||||
manager->spi_rx_param.chip_select = RX_CS_Pin;
|
||||
manager->spi_rx_param.bit_order = NO_OS_SPI_BIT_ORDER_MSB_FIRST;
|
||||
manager->spi_rx_param.platform_ops = NULL;
|
||||
manager->spi_rx_param.extra = &hspi4;
|
||||
manager->spi_rx_param.platform_ops = &stm32_spi_ops;
|
||||
manager->spi_rx_param.extra = &spi_rx_extra;
|
||||
|
||||
DIAG("LO", "SPI4 params: TX_CS=0x%04X RX_CS=0x%04X speed=%lu Hz",
|
||||
TX_CS_Pin, RX_CS_Pin, (unsigned long)ADF4382A_SPI_SPEED_HZ);
|
||||
DIAG("LO", "SPI4 params: TX_CS=0x%04X RX_CS=0x%04X speed=%lu Hz platform_ops=%p",
|
||||
TX_CS_Pin, RX_CS_Pin, (unsigned long)ADF4382A_SPI_SPEED_HZ,
|
||||
(const void*)manager->spi_tx_param.platform_ops);
|
||||
|
||||
// Configure TX parameters (10.5 GHz)
|
||||
memset(&tx_param, 0, sizeof(tx_param));
|
||||
@@ -651,7 +666,7 @@ int ADF4382A_StrobePhaseShift(ADF4382A_Manager *manager, uint8_t device)
|
||||
|
||||
// Static helper functions
|
||||
|
||||
static void set_chip_enable(uint8_t ce_pin, bool state)
|
||||
static void set_chip_enable(uint16_t ce_pin, bool state)
|
||||
{
|
||||
GPIO_TypeDef* port = (ce_pin == TX_CE_Pin) ? TX_CE_GPIO_Port : RX_CE_GPIO_Port;
|
||||
HAL_GPIO_WritePin(port, ce_pin, state ? GPIO_PIN_SET : GPIO_PIN_RESET);
|
||||
|
||||
@@ -130,8 +130,8 @@ struct no_os_spi_init_param {
|
||||
uint32_t device_id;
|
||||
/** maximum transfer speed */
|
||||
uint32_t max_speed_hz;
|
||||
/** SPI chip select */
|
||||
uint8_t chip_select;
|
||||
/** SPI chip select (widened to uint16_t for STM32 GPIO_PIN_xx masks) */
|
||||
uint16_t chip_select;
|
||||
/** SPI mode */
|
||||
enum no_os_spi_mode mode;
|
||||
/** SPI bit order */
|
||||
@@ -184,8 +184,8 @@ struct no_os_spi_desc {
|
||||
uint32_t device_id;
|
||||
/** maximum transfer speed */
|
||||
uint32_t max_speed_hz;
|
||||
/** SPI chip select */
|
||||
uint8_t chip_select;
|
||||
/** SPI chip select (widened to uint16_t for STM32 GPIO_PIN_xx masks) */
|
||||
uint16_t chip_select;
|
||||
/** SPI mode */
|
||||
enum no_os_spi_mode mode;
|
||||
/** SPI bit order */
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
#include "stm32_spi.h"
|
||||
#include "no_os_error.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* @brief Detect whether 'extra' points to a stm32_spi_extra struct (with
|
||||
* cs_port != NULL) or is a bare SPI_HandleTypeDef* (legacy path).
|
||||
*
|
||||
* Heuristic: if cs_port is a valid-looking pointer (non-NULL, non-trivial)
|
||||
* we treat extra as stm32_spi_extra. Legacy callers pass &hspi4 directly,
|
||||
* whose first field (Instance) is a peripheral base address — never a small
|
||||
* number, but also never a GPIO_TypeDef*. We use the struct's own cs_port
|
||||
* field to discriminate: stm32_spi_extra always sets cs_port explicitly.
|
||||
*/
|
||||
|
||||
int32_t stm32_spi_init(struct no_os_spi_desc **desc,
|
||||
const struct no_os_spi_init_param *param)
|
||||
@@ -12,10 +24,30 @@ int32_t stm32_spi_init(struct no_os_spi_desc **desc,
|
||||
if (!*desc)
|
||||
return -ENOMEM;
|
||||
|
||||
/* store platform handle (HAL SPI_HandleTypeDef*) in extra */
|
||||
(*desc)->extra = param->extra;
|
||||
/*
|
||||
* If the caller provides a stm32_spi_extra with cs_port set, allocate
|
||||
* a copy so the descriptor owns the data. Otherwise, store the raw
|
||||
* SPI_HandleTypeDef* for backward compatibility.
|
||||
*/
|
||||
const stm32_spi_extra *in_extra = (const stm32_spi_extra *)param->extra;
|
||||
if (in_extra && in_extra->cs_port != NULL) {
|
||||
/* Caller provided full stm32_spi_extra with software CS */
|
||||
stm32_spi_extra *own = calloc(1, sizeof(stm32_spi_extra));
|
||||
if (!own) {
|
||||
free(*desc);
|
||||
*desc = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
memcpy(own, in_extra, sizeof(stm32_spi_extra));
|
||||
(*desc)->extra = own;
|
||||
} else {
|
||||
/* Legacy: extra is a bare SPI_HandleTypeDef* */
|
||||
(*desc)->extra = param->extra;
|
||||
}
|
||||
|
||||
(*desc)->max_speed_hz = param->max_speed_hz;
|
||||
(*desc)->mode = param->mode;
|
||||
(*desc)->chip_select = param->chip_select;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -27,11 +59,39 @@ int32_t stm32_spi_write_and_read(struct no_os_spi_desc *desc,
|
||||
if (!desc || !data || bytes_number == 0)
|
||||
return -EINVAL;
|
||||
|
||||
SPI_HandleTypeDef *hspi = (SPI_HandleTypeDef *)desc->extra;
|
||||
SPI_HandleTypeDef *hspi;
|
||||
GPIO_TypeDef *cs_port = NULL;
|
||||
uint16_t cs_pin = 0;
|
||||
|
||||
/*
|
||||
* Determine HAL handle and optional CS info.
|
||||
* If extra is a stm32_spi_extra with cs_port set, use its fields.
|
||||
* Otherwise treat extra as a bare SPI_HandleTypeDef*.
|
||||
*/
|
||||
const stm32_spi_extra *sx = (const stm32_spi_extra *)desc->extra;
|
||||
if (sx && sx->cs_port != NULL) {
|
||||
hspi = sx->hspi;
|
||||
cs_port = sx->cs_port;
|
||||
cs_pin = sx->cs_pin;
|
||||
} else {
|
||||
hspi = (SPI_HandleTypeDef *)desc->extra;
|
||||
}
|
||||
|
||||
if (!hspi)
|
||||
return -EINVAL;
|
||||
|
||||
if (HAL_SPI_TransmitReceive(hspi, data, data, bytes_number, 200) != HAL_OK)
|
||||
/* Assert CS (active low) */
|
||||
if (cs_port)
|
||||
HAL_GPIO_WritePin(cs_port, cs_pin, GPIO_PIN_RESET);
|
||||
|
||||
HAL_StatusTypeDef hal_ret;
|
||||
hal_ret = HAL_SPI_TransmitReceive(hspi, data, data, bytes_number, 200);
|
||||
|
||||
/* Deassert CS */
|
||||
if (cs_port)
|
||||
HAL_GPIO_WritePin(cs_port, cs_pin, GPIO_PIN_SET);
|
||||
|
||||
if (hal_ret != HAL_OK)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
@@ -41,6 +101,17 @@ int32_t stm32_spi_remove(struct no_os_spi_desc *desc)
|
||||
{
|
||||
if (!desc)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* If we allocated a stm32_spi_extra copy during init, free it.
|
||||
* Detect by checking cs_port (same heuristic as init).
|
||||
*/
|
||||
if (desc->extra) {
|
||||
const stm32_spi_extra *sx = (const stm32_spi_extra *)desc->extra;
|
||||
if (sx->cs_port != NULL)
|
||||
free(desc->extra);
|
||||
}
|
||||
|
||||
free(desc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,24 @@
|
||||
#include "no_os_spi.h"
|
||||
#include "stm32f7xx_hal.h"
|
||||
|
||||
/**
|
||||
* @struct stm32_spi_extra
|
||||
* @brief Platform-specific SPI data for STM32.
|
||||
*
|
||||
* When software chip-select is needed (e.g. multiple devices sharing one SPI
|
||||
* bus with GPIO-managed CS), set cs_port to the GPIO port and cs_pin to the
|
||||
* GPIO pin mask. stm32_spi_write_and_read() will assert CS LOW before the
|
||||
* transfer and deassert CS HIGH after.
|
||||
*
|
||||
* If cs_port is NULL (legacy usage where the caller passes a bare
|
||||
* SPI_HandleTypeDef* as the extra pointer), CS management is skipped.
|
||||
*/
|
||||
typedef struct {
|
||||
SPI_HandleTypeDef *hspi; /**< HAL SPI handle */
|
||||
GPIO_TypeDef *cs_port; /**< GPIO port for software CS (NULL = no SW CS) */
|
||||
uint16_t cs_pin; /**< GPIO pin mask for software CS */
|
||||
} stm32_spi_extra;
|
||||
|
||||
extern const struct no_os_spi_platform_ops stm32_spi_ops;
|
||||
|
||||
#endif /* _STM32_SPI_H_ */
|
||||
|
||||
@@ -11,3 +11,5 @@ test_bug5_fine_phase_gpio_only
|
||||
test_bug6_timer_variable_collision
|
||||
test_bug7_gpio_pin_conflict
|
||||
test_bug8_uart_commented_out
|
||||
test_bug9_platform_ops_null
|
||||
test_bug10_spi_cs_not_toggled
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
################################################################################
|
||||
# Makefile -- MCU firmware unit test harness for AERIS-10
|
||||
#
|
||||
# Builds and runs host-side (macOS) tests for the 8 discovered firmware bugs.
|
||||
# Builds and runs host-side (macOS) tests for the 10 discovered firmware bugs.
|
||||
# Uses mock HAL + spy/recording pattern to test real firmware code without
|
||||
# hardware.
|
||||
#
|
||||
@@ -34,7 +34,9 @@ REAL_OBJ := adf4382a_manager.o
|
||||
TESTS_WITH_REAL := test_bug1_timed_sync_init_ordering \
|
||||
test_bug3_timed_sync_noop \
|
||||
test_bug4_phase_shift_before_check \
|
||||
test_bug5_fine_phase_gpio_only
|
||||
test_bug5_fine_phase_gpio_only \
|
||||
test_bug9_platform_ops_null \
|
||||
test_bug10_spi_cs_not_toggled
|
||||
|
||||
# Tests that only need mocks (extracted patterns / static analysis)
|
||||
TESTS_MOCK_ONLY := test_bug2_ad9523_double_setup \
|
||||
@@ -44,7 +46,7 @@ TESTS_MOCK_ONLY := test_bug2_ad9523_double_setup \
|
||||
|
||||
ALL_TESTS := $(TESTS_WITH_REAL) $(TESTS_MOCK_ONLY)
|
||||
|
||||
.PHONY: all build test clean $(addprefix test_,bug1 bug2 bug3 bug4 bug5 bug6 bug7 bug8)
|
||||
.PHONY: all build test clean $(addprefix test_,bug1 bug2 bug3 bug4 bug5 bug6 bug7 bug8 bug9 bug10)
|
||||
|
||||
all: build test
|
||||
|
||||
@@ -52,7 +54,7 @@ build: $(ALL_TESTS)
|
||||
|
||||
test: build
|
||||
@echo "==============================================="
|
||||
@echo " Running all 8 bug tests..."
|
||||
@echo " Running all 10 bug tests..."
|
||||
@echo "==============================================="
|
||||
@pass=0; fail=0; \
|
||||
for t in $(ALL_TESTS); do \
|
||||
@@ -125,6 +127,12 @@ test_bug7: test_bug7_gpio_pin_conflict
|
||||
test_bug8: test_bug8_uart_commented_out
|
||||
./test_bug8_uart_commented_out
|
||||
|
||||
test_bug9: test_bug9_platform_ops_null
|
||||
./test_bug9_platform_ops_null
|
||||
|
||||
test_bug10: test_bug10_spi_cs_not_toggled
|
||||
./test_bug10_spi_cs_not_toggled
|
||||
|
||||
# --- Clean ---
|
||||
|
||||
clean:
|
||||
|
||||
@@ -47,7 +47,7 @@ struct no_os_spi_platform_ops {
|
||||
struct no_os_spi_init_param {
|
||||
uint32_t device_id;
|
||||
uint32_t max_speed_hz;
|
||||
uint8_t chip_select;
|
||||
uint16_t chip_select;
|
||||
enum no_os_spi_mode mode;
|
||||
enum no_os_spi_bit_order bit_order;
|
||||
enum no_os_spi_lanes lanes;
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/* shim: stm32_spi.h -- provides stm32_spi_extra type and stm32_spi_ops mock
|
||||
*
|
||||
* The real stm32_spi.h includes stm32f7xx_hal.h which we can't use in tests.
|
||||
* This shim provides the stm32_spi_extra struct and a mock stm32_spi_ops
|
||||
* extern so that adf4382a_manager.c compiles against test infrastructure.
|
||||
*/
|
||||
#ifndef STM32_SPI_H_SHIM
|
||||
#define STM32_SPI_H_SHIM
|
||||
|
||||
#include "stm32_hal_mock.h"
|
||||
#include "ad_driver_mock.h"
|
||||
|
||||
/**
|
||||
* @struct stm32_spi_extra
|
||||
* @brief Platform-specific SPI data for STM32 (test mock version).
|
||||
*/
|
||||
typedef struct {
|
||||
SPI_HandleTypeDef *hspi; /**< HAL SPI handle */
|
||||
GPIO_TypeDef *cs_port; /**< GPIO port for software CS (NULL = no SW CS) */
|
||||
uint16_t cs_pin; /**< GPIO pin mask for software CS */
|
||||
} stm32_spi_extra;
|
||||
|
||||
/* Mock stm32_spi_ops -- declared in stm32_hal_mock.c */
|
||||
extern const struct no_os_spi_platform_ops stm32_spi_ops;
|
||||
|
||||
#endif /* STM32_SPI_H_SHIM */
|
||||
@@ -2,6 +2,7 @@
|
||||
* stm32_hal_mock.c -- Spy/recording implementation of STM32 HAL stubs
|
||||
******************************************************************************/
|
||||
#include "stm32_hal_mock.h"
|
||||
#include "ad_driver_mock.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
@@ -262,3 +263,14 @@ void mock_tim_set_compare(TIM_HandleTypeDef *htim, uint32_t Channel, uint32_t Co
|
||||
.extra = htim
|
||||
});
|
||||
}
|
||||
|
||||
/* ========================= Mock stm32_spi_ops ===================== */
|
||||
|
||||
/* Stub SPI platform ops -- real adf4382a_manager.c references &stm32_spi_ops.
|
||||
* In tests, adf4382_init() is mocked so no_os_spi_init() is never called.
|
||||
* We provide a non-NULL struct so tests can assert platform_ops != NULL. */
|
||||
static int mock_spi_init_stub(void) { return 0; }
|
||||
|
||||
const struct no_os_spi_platform_ops stm32_spi_ops = {
|
||||
.init = mock_spi_init_stub,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
/*******************************************************************************
|
||||
* test_bug10_spi_cs_not_toggled.c
|
||||
*
|
||||
* Bug #10 (FIXED): stm32_spi_write_and_read() never toggled chip select.
|
||||
* Since TX and RX ADF4382A share SPI4, every register write hit BOTH PLLs
|
||||
* simultaneously.
|
||||
*
|
||||
* Post-fix behavior:
|
||||
* 1. Manager_Init creates stm32_spi_extra structs with the correct CS
|
||||
* port (GPIOG) and pin for each device (TX_CS_Pin, RX_CS_Pin).
|
||||
* 2. spi_param.extra points to a stm32_spi_extra with cs_port != NULL,
|
||||
* so stm32_spi_write_and_read() will assert/deassert CS around
|
||||
* each transfer.
|
||||
*
|
||||
* Note: The actual SPI CS toggling is in stm32_spi.c at the platform level.
|
||||
* This test verifies that the manager correctly provisions the CS metadata
|
||||
* that stm32_spi.c uses.
|
||||
******************************************************************************/
|
||||
#include "adf4382a_manager.h"
|
||||
#include "stm32_spi.h"
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
ADF4382A_Manager mgr;
|
||||
int ret;
|
||||
|
||||
printf("=== Bug #10 (FIXED): SPI CS not toggled ===\n");
|
||||
|
||||
/* ---- Test A: Init succeeds ---- */
|
||||
spy_reset();
|
||||
ret = ADF4382A_Manager_Init(&mgr, SYNC_METHOD_TIMED);
|
||||
|
||||
printf(" Manager_Init returned: %d (expected 0=OK)\n", ret);
|
||||
assert(ret == ADF4382A_MANAGER_OK);
|
||||
printf(" PASS: Init returned OK\n");
|
||||
|
||||
/* ---- Test B: TX extra is non-NULL ---- */
|
||||
printf(" spi_tx_param.extra = %p (expected non-NULL)\n", mgr.spi_tx_param.extra);
|
||||
assert(mgr.spi_tx_param.extra != NULL);
|
||||
printf(" PASS: TX extra is non-NULL\n");
|
||||
|
||||
/* ---- Test C: TX extra has correct CS port and pin ---- */
|
||||
stm32_spi_extra *tx_extra = (stm32_spi_extra *)mgr.spi_tx_param.extra;
|
||||
printf(" TX cs_port = %p (expected GPIOG = %p)\n", (void *)tx_extra->cs_port, (void *)GPIOG);
|
||||
assert(tx_extra->cs_port == GPIOG);
|
||||
printf(" PASS: TX cs_port == GPIOG\n");
|
||||
|
||||
printf(" TX cs_pin = 0x%04X (expected TX_CS_Pin = 0x%04X)\n", tx_extra->cs_pin, TX_CS_Pin);
|
||||
assert(tx_extra->cs_pin == TX_CS_Pin); /* GPIO_PIN_14 = 0x4000 */
|
||||
printf(" PASS: TX cs_pin == TX_CS_Pin (GPIO_PIN_14)\n");
|
||||
|
||||
/* ---- Test D: TX extra has correct SPI handle ---- */
|
||||
printf(" TX hspi = %p (expected &hspi4 = %p)\n", (void *)tx_extra->hspi, (void *)&hspi4);
|
||||
assert(tx_extra->hspi == &hspi4);
|
||||
printf(" PASS: TX hspi == &hspi4\n");
|
||||
|
||||
/* ---- Test E: RX extra is non-NULL ---- */
|
||||
printf(" spi_rx_param.extra = %p (expected non-NULL)\n", mgr.spi_rx_param.extra);
|
||||
assert(mgr.spi_rx_param.extra != NULL);
|
||||
printf(" PASS: RX extra is non-NULL\n");
|
||||
|
||||
/* ---- Test F: RX extra has correct CS port and pin ---- */
|
||||
stm32_spi_extra *rx_extra = (stm32_spi_extra *)mgr.spi_rx_param.extra;
|
||||
printf(" RX cs_port = %p (expected GPIOG = %p)\n", (void *)rx_extra->cs_port, (void *)GPIOG);
|
||||
assert(rx_extra->cs_port == GPIOG);
|
||||
printf(" PASS: RX cs_port == GPIOG\n");
|
||||
|
||||
printf(" RX cs_pin = 0x%04X (expected RX_CS_Pin = 0x%04X)\n", rx_extra->cs_pin, RX_CS_Pin);
|
||||
assert(rx_extra->cs_pin == RX_CS_Pin); /* GPIO_PIN_10 = 0x0400 */
|
||||
printf(" PASS: RX cs_pin == RX_CS_Pin (GPIO_PIN_10)\n");
|
||||
|
||||
/* ---- Test G: RX extra has correct SPI handle ---- */
|
||||
printf(" RX hspi = %p (expected &hspi4 = %p)\n", (void *)rx_extra->hspi, (void *)&hspi4);
|
||||
assert(rx_extra->hspi == &hspi4);
|
||||
printf(" PASS: RX hspi == &hspi4\n");
|
||||
|
||||
/* ---- Test H: TX and RX extras are DIFFERENT instances ---- */
|
||||
printf(" TX extra = %p, RX extra = %p (expected different)\n",
|
||||
(void *)tx_extra, (void *)rx_extra);
|
||||
assert(tx_extra != rx_extra);
|
||||
printf(" PASS: TX and RX have separate stm32_spi_extra instances\n");
|
||||
|
||||
/* ---- Test I: TX and RX have DIFFERENT CS pins ---- */
|
||||
assert(tx_extra->cs_pin != rx_extra->cs_pin);
|
||||
printf(" PASS: TX and RX CS pins differ (individual addressing)\n");
|
||||
|
||||
/* Cleanup */
|
||||
ADF4382A_Manager_Deinit(&mgr);
|
||||
|
||||
printf("=== Bug #10 (FIXED): ALL TESTS PASSED ===\n\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*******************************************************************************
|
||||
* test_bug9_platform_ops_null.c
|
||||
*
|
||||
* Bug #9 (FIXED): Both TX and RX SPI init params had platform_ops = NULL.
|
||||
* adf4382_init() calls no_os_spi_init() which checks
|
||||
* if (!param->platform_ops) return -EINVAL;
|
||||
* so SPI init always silently failed.
|
||||
*
|
||||
* Post-fix behavior:
|
||||
* 1. Manager_Init sets platform_ops = &stm32_spi_ops for both TX and RX.
|
||||
* 2. platform_ops is non-NULL and points to the STM32 SPI platform ops.
|
||||
******************************************************************************/
|
||||
#include "adf4382a_manager.h"
|
||||
#include "stm32_spi.h"
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
ADF4382A_Manager mgr;
|
||||
int ret;
|
||||
|
||||
printf("=== Bug #9 (FIXED): platform_ops was NULL ===\n");
|
||||
|
||||
/* ---- Test A: Init succeeds ---- */
|
||||
spy_reset();
|
||||
ret = ADF4382A_Manager_Init(&mgr, SYNC_METHOD_TIMED);
|
||||
|
||||
printf(" Manager_Init returned: %d (expected 0=OK)\n", ret);
|
||||
assert(ret == ADF4382A_MANAGER_OK);
|
||||
printf(" PASS: Init returned OK\n");
|
||||
|
||||
/* ---- Test B: TX platform_ops is non-NULL ---- */
|
||||
printf(" spi_tx_param.platform_ops = %p (expected non-NULL)\n",
|
||||
(void *)mgr.spi_tx_param.platform_ops);
|
||||
assert(mgr.spi_tx_param.platform_ops != NULL);
|
||||
printf(" PASS: TX platform_ops is non-NULL\n");
|
||||
|
||||
/* ---- Test C: RX platform_ops is non-NULL ---- */
|
||||
printf(" spi_rx_param.platform_ops = %p (expected non-NULL)\n",
|
||||
(void *)mgr.spi_rx_param.platform_ops);
|
||||
assert(mgr.spi_rx_param.platform_ops != NULL);
|
||||
printf(" PASS: RX platform_ops is non-NULL\n");
|
||||
|
||||
/* ---- Test D: Both point to stm32_spi_ops ---- */
|
||||
printf(" &stm32_spi_ops = %p\n", (void *)&stm32_spi_ops);
|
||||
assert(mgr.spi_tx_param.platform_ops == &stm32_spi_ops);
|
||||
printf(" PASS: TX platform_ops == &stm32_spi_ops\n");
|
||||
|
||||
assert(mgr.spi_rx_param.platform_ops == &stm32_spi_ops);
|
||||
printf(" PASS: RX platform_ops == &stm32_spi_ops\n");
|
||||
|
||||
/* Cleanup */
|
||||
ADF4382A_Manager_Deinit(&mgr);
|
||||
|
||||
printf("=== Bug #9 (FIXED): ALL TESTS PASSED ===\n\n");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user