mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-03 03:31:41 +03:00
fix(spi): test_apps mv idf_perform_test to esp_driver_spi domain
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
# This is the project CMakeLists.txt file for the test subproject
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||
set(COMPONENTS main)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(spi_master_test)
|
||||
|
||||
if(CONFIG_COMPILER_DUMP_RTL_FILES)
|
||||
add_custom_target(check_test_app_sections ALL
|
||||
COMMAND ${PYTHON} $ENV{IDF_PATH}/tools/ci/check_callgraph.py
|
||||
--rtl-dirs ${CMAKE_BINARY_DIR}/esp-idf/esp_driver_spi/,${CMAKE_BINARY_DIR}/esp-idf/hal/
|
||||
--elf-file ${CMAKE_BINARY_DIR}/spi_master_test.elf
|
||||
find-refs
|
||||
--from-sections=.iram0.text
|
||||
--to-sections=.flash.text,.flash.rodata
|
||||
--exit-code
|
||||
DEPENDS ${elf}
|
||||
)
|
||||
endif()
|
||||
@@ -0,0 +1,2 @@
|
||||
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | -------- |
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
set(srcs
|
||||
"test_app_main.c"
|
||||
"test_spi_master.c"
|
||||
"test_spi_sio.c"
|
||||
"test_spi_bus_lock.c"
|
||||
)
|
||||
|
||||
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
|
||||
# the component can be registered as WHOLE_ARCHIVE
|
||||
idf_component_register(
|
||||
SRCS ${srcs}
|
||||
PRIV_REQUIRES esp_driver_spi spi_flash
|
||||
WHOLE_ARCHIVE
|
||||
)
|
||||
@@ -0,0 +1,7 @@
|
||||
dependencies:
|
||||
test_utils:
|
||||
path: ${IDF_PATH}/tools/unit-test-app/components/test_utils
|
||||
test_driver_utils:
|
||||
path: ${IDF_PATH}/components/driver/test_apps/components/test_driver_utils
|
||||
spi_bench_mark:
|
||||
path: ${IDF_PATH}/components/esp_driver_spi/test_apps/components/spi_bench_mark
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "unity.h"
|
||||
#include "unity_test_utils.h"
|
||||
#include "esp_heap_caps.h"
|
||||
|
||||
// iterator to load partition tables in `test spi bus lock, with flash` will lead memory not free
|
||||
#define TEST_MEMORY_LEAK_THRESHOLD (350)
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
unity_utils_record_free_mem();
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
esp_reent_cleanup(); //clean up some of the newlib's lazy allocations
|
||||
unity_utils_evaluate_leaks_direct(TEST_MEMORY_LEAK_THRESHOLD);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
// ____ ____ ___ __ __ _
|
||||
// / ___|| _ \_ _| | \/ | __ _ ___| |_ ___ _ __
|
||||
// \___ \| |_) | | | |\/| |/ _` / __| __/ _ \ '__|
|
||||
// ___) | __/| | | | | | (_| \__ \ || __/ |
|
||||
// |____/|_| |___| |_| |_|\__,_|___/\__\___|_|
|
||||
|
||||
printf("\n");
|
||||
printf(" ____ ____ ___ __ __ _ \n");
|
||||
printf(" / ___|| _ \\_ _| | \\/ | __ _ ___| |_ ___ _ __ \n");
|
||||
printf(" \\___ \\| |_) | | | |\\/| |/ _` / __| __/ _ \\ '__|\n");
|
||||
printf(" ___) | __/| | | | | | (_| \\__ \\ || __/ | \n");
|
||||
printf(" |____/|_| |___| |_| |_|\\__,_|___/\\__\\___|_| \n");
|
||||
|
||||
unity_run_menu();
|
||||
}
|
||||
@@ -0,0 +1,350 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_log.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_flash_spi_init.h"
|
||||
#include "test_utils.h"
|
||||
#include "test_spi_utils.h"
|
||||
#include "spi_flash_mmap.h"
|
||||
#include "unity.h"
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
// The VSPI pins on UT_T1_ESP_FLASH are connected to a external flash
|
||||
#define TEST_BUS_PIN_NUM_MISO VSPI_IOMUX_PIN_NUM_MISO
|
||||
#define TEST_BUS_PIN_NUM_MOSI VSPI_IOMUX_PIN_NUM_MOSI
|
||||
#define TEST_BUS_PIN_NUM_CLK VSPI_IOMUX_PIN_NUM_CLK
|
||||
#define TEST_BUS_PIN_NUM_CS VSPI_IOMUX_PIN_NUM_CS
|
||||
#define TEST_BUS_PIN_NUM_WP VSPI_IOMUX_PIN_NUM_WP
|
||||
#define TEST_BUS_PIN_NUM_HD VSPI_IOMUX_PIN_NUM_HD
|
||||
|
||||
#else
|
||||
#define TEST_BUS_PIN_NUM_MISO SPI2_IOMUX_PIN_NUM_MISO
|
||||
#define TEST_BUS_PIN_NUM_MOSI SPI2_IOMUX_PIN_NUM_MOSI
|
||||
#define TEST_BUS_PIN_NUM_CLK SPI2_IOMUX_PIN_NUM_CLK
|
||||
#define TEST_BUS_PIN_NUM_CS SPI2_IOMUX_PIN_NUM_CS
|
||||
#define TEST_BUS_PIN_NUM_WP SPI2_IOMUX_PIN_NUM_WP
|
||||
#define TEST_BUS_PIN_NUM_HD SPI2_IOMUX_PIN_NUM_HD
|
||||
|
||||
#endif
|
||||
|
||||
#define TEST_FLASH_FREQ_MHZ 5
|
||||
|
||||
typedef struct {
|
||||
union {
|
||||
spi_device_handle_t handle;
|
||||
esp_flash_t* chip;
|
||||
};
|
||||
bool finished;
|
||||
} task_context_t;
|
||||
|
||||
#if !(CONFIG_SPIRAM && CONFIG_IDF_TARGET_ESP32)
|
||||
|
||||
const static char TAG[] = "test_spi";
|
||||
|
||||
void spi_task1(void* arg)
|
||||
{
|
||||
//task1 send 50 polling transactions, acquire the bus and send another 50
|
||||
int count = 0;
|
||||
spi_transaction_t t = {
|
||||
.flags = SPI_TRANS_USE_TXDATA,
|
||||
.tx_data = { 0x80, 0x12, 0x34, 0x56 },
|
||||
.length = 4 * 8,
|
||||
};
|
||||
spi_device_handle_t handle = ((task_context_t*)arg)->handle;
|
||||
for (int j = 0; j < 50; j ++) {
|
||||
TEST_ESP_OK(spi_device_polling_transmit(handle, &t));
|
||||
ESP_LOGI(TAG, "task1:%d", count++);
|
||||
}
|
||||
TEST_ESP_OK(spi_device_acquire_bus(handle, portMAX_DELAY));
|
||||
for (int j = 0; j < 50; j ++) {
|
||||
TEST_ESP_OK(spi_device_polling_transmit(handle, &t));
|
||||
ESP_LOGI(TAG, "task1:%d", count++);
|
||||
}
|
||||
spi_device_release_bus(handle);
|
||||
ESP_LOGI(TAG, "task1 terminates");
|
||||
((task_context_t*)arg)->finished = true;
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void spi_task2(void* arg)
|
||||
{
|
||||
int count = 0;
|
||||
//task2 acquire the bus, send 50 polling transactions and then 50 non-polling
|
||||
spi_transaction_t t = {
|
||||
.flags = SPI_TRANS_USE_TXDATA,
|
||||
.tx_data = { 0x80, 0x12, 0x34, 0x56 },
|
||||
.length = 4 * 8,
|
||||
};
|
||||
spi_transaction_t *ret_t;
|
||||
spi_device_handle_t handle = ((task_context_t*)arg)->handle;
|
||||
TEST_ESP_OK(spi_device_acquire_bus(handle, portMAX_DELAY));
|
||||
|
||||
for (int i = 0; i < 50; i ++) {
|
||||
TEST_ESP_OK(spi_device_polling_transmit(handle, &t));
|
||||
ESP_LOGI(TAG, "task2: %d", count++);
|
||||
}
|
||||
|
||||
for (int j = 0; j < 50; j ++) {
|
||||
TEST_ESP_OK(spi_device_queue_trans(handle, &t, portMAX_DELAY));
|
||||
}
|
||||
for (int j = 0; j < 50; j ++) {
|
||||
TEST_ESP_OK(spi_device_get_trans_result(handle, &ret_t, portMAX_DELAY));
|
||||
assert(ret_t == &t);
|
||||
ESP_LOGI(TAG, "task2: %d", count++);
|
||||
}
|
||||
spi_device_release_bus(handle);
|
||||
vTaskDelay(1);
|
||||
ESP_LOGI(TAG, "task2 terminates");
|
||||
((task_context_t*)arg)->finished = true;
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void spi_task3(void* arg)
|
||||
{
|
||||
//task3 send 30 polling transactions, acquire the bus, send 20 polling transactions and then 50 non-polling
|
||||
int count = 0;
|
||||
spi_transaction_t t = {
|
||||
.flags = SPI_TRANS_USE_TXDATA,
|
||||
.tx_data = { 0x80, 0x12, 0x34, 0x56 },
|
||||
.length = 4 * 8,
|
||||
};
|
||||
spi_transaction_t *ret_t;
|
||||
spi_device_handle_t handle = ((task_context_t*)arg)->handle;
|
||||
|
||||
for (int i = 0; i < 30; i ++) {
|
||||
TEST_ESP_OK(spi_device_polling_transmit(handle, &t));
|
||||
ESP_LOGI(TAG, "task3: %d", count++);
|
||||
}
|
||||
|
||||
TEST_ESP_OK(spi_device_acquire_bus(handle, portMAX_DELAY));
|
||||
for (int i = 0; i < 20; i ++) {
|
||||
TEST_ESP_OK(spi_device_polling_transmit(handle, &t));
|
||||
ESP_LOGI(TAG, "task3: %d", count++);
|
||||
}
|
||||
|
||||
for (int j = 0; j < 50; j++) {
|
||||
TEST_ESP_OK(spi_device_queue_trans(handle, &t, portMAX_DELAY));
|
||||
}
|
||||
for (int j = 0; j < 50; j++) {
|
||||
TEST_ESP_OK(spi_device_get_trans_result(handle, &ret_t, portMAX_DELAY));
|
||||
assert(ret_t == &t);
|
||||
ESP_LOGI(TAG, "task3: %d", count++);
|
||||
}
|
||||
spi_device_release_bus(handle);
|
||||
|
||||
ESP_LOGI(TAG, "task3 terminates");
|
||||
((task_context_t*)arg)->finished = true;
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void write_large_buffer(esp_flash_t *chip, const esp_partition_t *part, const uint8_t *source, size_t length)
|
||||
{
|
||||
printf("Erasing chip %p, %d bytes\n", chip, length);
|
||||
|
||||
TEST_ESP_OK(esp_flash_erase_region(chip, part->address, (length + SPI_FLASH_SEC_SIZE) & ~(SPI_FLASH_SEC_SIZE - 1)));
|
||||
|
||||
printf("Writing chip %p, %d bytes from source %p\n", chip, length, source);
|
||||
// note writing to unaligned address
|
||||
TEST_ESP_OK(esp_flash_write(chip, source, part->address + 1, length));
|
||||
|
||||
printf("Write done.\n");
|
||||
}
|
||||
|
||||
static void read_and_check(esp_flash_t *chip, const esp_partition_t *part, const uint8_t *source, size_t length)
|
||||
{
|
||||
printf("Checking chip %p, %d bytes\n", chip, length);
|
||||
uint8_t *buf = malloc(length);
|
||||
TEST_ASSERT_NOT_NULL(buf);
|
||||
TEST_ESP_OK(esp_flash_read(chip, buf, part->address + 1, length));
|
||||
TEST_ASSERT_EQUAL_HEX8_ARRAY(source, buf, length);
|
||||
free(buf);
|
||||
|
||||
// check nothing was written at beginning or end
|
||||
uint8_t ends[8];
|
||||
|
||||
TEST_ESP_OK(esp_flash_read(chip, ends, part->address, sizeof(ends)));
|
||||
TEST_ASSERT_EQUAL_HEX8(0xFF, ends[0]);
|
||||
TEST_ASSERT_EQUAL_HEX8(source[0], ends[1]);
|
||||
|
||||
TEST_ESP_OK(esp_flash_read(chip, ends, part->address + length, sizeof(ends)));
|
||||
|
||||
TEST_ASSERT_EQUAL_HEX8(source[length - 1], ends[0]);
|
||||
TEST_ASSERT_EQUAL_HEX8(0xFF, ends[1]);
|
||||
TEST_ASSERT_EQUAL_HEX8(0xFF, ends[2]);
|
||||
TEST_ASSERT_EQUAL_HEX8(0xFF, ends[3]);
|
||||
}
|
||||
|
||||
void spi_task4(void* arg)
|
||||
{
|
||||
esp_flash_t *chip = ((task_context_t*)arg)->chip;
|
||||
|
||||
// buffer in RAM
|
||||
const int test_len = 16400;
|
||||
uint8_t *source_buf = heap_caps_malloc(test_len, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
||||
TEST_ASSERT_NOT_NULL(source_buf);
|
||||
|
||||
srand(676);
|
||||
for (int i = 0; i < test_len; i++) {
|
||||
source_buf[i] = rand();
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Testing chip %p...", chip);
|
||||
const esp_partition_t *part = get_test_data_partition();
|
||||
TEST_ASSERT(part->size > test_len + 2 + SPI_FLASH_SEC_SIZE);
|
||||
|
||||
write_large_buffer(chip, part, source_buf, test_len);
|
||||
read_and_check(chip, part, source_buf, test_len);
|
||||
|
||||
free(source_buf);
|
||||
|
||||
ESP_LOGI(TAG, "task4 terminates");
|
||||
((task_context_t*)arg)->finished = true;
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void test_bus_lock(bool test_flash)
|
||||
{
|
||||
task_context_t context1 = {};
|
||||
task_context_t context2 = {};
|
||||
task_context_t context3 = {};
|
||||
task_context_t context4 = {};
|
||||
TaskHandle_t task1, task2, task3, task4;
|
||||
esp_err_t ret;
|
||||
spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG();
|
||||
buscfg.miso_io_num = TEST_BUS_PIN_NUM_MISO;
|
||||
buscfg.mosi_io_num = TEST_BUS_PIN_NUM_MOSI;
|
||||
buscfg.sclk_io_num = TEST_BUS_PIN_NUM_CLK;
|
||||
|
||||
spi_device_interface_config_t devcfg = SPI_DEVICE_TEST_DEFAULT_CONFIG();
|
||||
devcfg.queue_size = 100;
|
||||
|
||||
//Initialize the SPI bus and 3 devices
|
||||
TEST_ESP_OK(spi_bus_initialize(TEST_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO));
|
||||
TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &devcfg, &context1.handle));
|
||||
TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &devcfg, &context2.handle));
|
||||
|
||||
//only have 3 cs pins, leave one for the flash
|
||||
devcfg.spics_io_num = -1;
|
||||
TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &devcfg, &context3.handle));
|
||||
esp_flash_spi_device_config_t flash_cfg = {
|
||||
.host_id = TEST_SPI_HOST,
|
||||
.cs_id = 2,
|
||||
.cs_io_num = TEST_BUS_PIN_NUM_CS,
|
||||
.io_mode = SPI_FLASH_DIO,
|
||||
.freq_mhz = TEST_FLASH_FREQ_MHZ,
|
||||
.input_delay_ns = 0,
|
||||
};
|
||||
|
||||
//Clamp the WP and HD pins to VDD to make it work in DIO mode
|
||||
gpio_set_direction(TEST_BUS_PIN_NUM_HD, GPIO_MODE_OUTPUT);
|
||||
gpio_set_direction(TEST_BUS_PIN_NUM_WP, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(TEST_BUS_PIN_NUM_HD, 1);
|
||||
gpio_set_level(TEST_BUS_PIN_NUM_WP, 1);
|
||||
|
||||
esp_flash_t *chip;
|
||||
(void) chip;
|
||||
if (test_flash) {
|
||||
ret = spi_bus_add_flash_device(&chip, &flash_cfg);
|
||||
TEST_ESP_OK(ret);
|
||||
ret = esp_flash_init(chip);
|
||||
TEST_ESP_OK(ret);
|
||||
context4.chip = chip;
|
||||
}
|
||||
ESP_LOGI(TAG, "Start testing...");
|
||||
|
||||
xTaskCreate(spi_task1, "task1", 4096, &context1, 0, &task1);
|
||||
xTaskCreate(spi_task2, "task2", 4096, &context2, 0, &task2);
|
||||
xTaskCreate(spi_task3, "task3", 4096, &context3, 0, &task3);
|
||||
if (test_flash) {
|
||||
xTaskCreate(spi_task4, "task4", 2048, &context4, 0, &task4);
|
||||
} else {
|
||||
context4.finished = true;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
vTaskDelay(10);
|
||||
if (context1.finished && context2.finished && context3.finished && context4.finished) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_ESP_OK(spi_bus_remove_device(context1.handle));
|
||||
TEST_ESP_OK(spi_bus_remove_device(context2.handle));
|
||||
TEST_ESP_OK(spi_bus_remove_device(context3.handle));
|
||||
if (test_flash) {
|
||||
TEST_ESP_OK(spi_bus_remove_flash_device(chip));
|
||||
}
|
||||
TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST));
|
||||
}
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
// no need this case in other target, only esp32 need buslock to split MSPI and GPSPI2 action
|
||||
TEST_CASE("spi bus lock, with flash", "[spi][test_env=external_flash]")
|
||||
{
|
||||
test_bus_lock(true);
|
||||
}
|
||||
#endif //CONFIG_IDF_TARGET_ESP32
|
||||
|
||||
TEST_CASE("spi bus lock", "[spi]")
|
||||
{
|
||||
test_bus_lock(false);
|
||||
}
|
||||
|
||||
#if !SOC_MEMSPI_IS_INDEPENDENT
|
||||
//disable, SPI1 is not available for GPSPI usage on chips later than ESP32
|
||||
static IRAM_ATTR esp_err_t test_polling_send(spi_device_handle_t handle)
|
||||
{
|
||||
for (int i = 0; i < 10; i++) {
|
||||
spi_transaction_t trans = {
|
||||
.length = 16,
|
||||
.flags = SPI_TRANS_USE_TXDATA | SPI_TRANS_USE_RXDATA,
|
||||
};
|
||||
esp_err_t err = spi_device_polling_transmit(handle, &trans);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static IRAM_ATTR NOINLINE_ATTR void test_acquire(spi_device_handle_t handle)
|
||||
{
|
||||
esp_err_t err = spi_device_acquire_bus(handle, portMAX_DELAY);
|
||||
if (err == ESP_OK) {
|
||||
err = test_polling_send(handle);
|
||||
spi_device_release_bus(handle);
|
||||
}
|
||||
|
||||
TEST_ESP_OK(err);
|
||||
}
|
||||
|
||||
TEST_CASE("spi master can be used on SPI1", "[spi]")
|
||||
{
|
||||
spi_device_interface_config_t dev_cfg = {
|
||||
.mode = 1,
|
||||
.clock_speed_hz = 1 * 1000 * 1000,
|
||||
.spics_io_num = -1,
|
||||
.queue_size = 1,
|
||||
};
|
||||
spi_device_handle_t handle;
|
||||
esp_err_t err;
|
||||
err = spi_bus_add_device(SPI1_HOST, &dev_cfg, &handle);
|
||||
TEST_ESP_OK(err);
|
||||
|
||||
err = test_polling_send(handle);
|
||||
TEST_ESP_OK(err);
|
||||
test_acquire(handle);
|
||||
|
||||
err = spi_bus_remove_device(handle);
|
||||
TEST_ESP_OK(err);
|
||||
}
|
||||
#endif //disable, SPI1 is not available for GPSPI usage on chips later than ESP32
|
||||
|
||||
//TODO: add a case when a non-polling transaction happened in the bus-acquiring time and then release the bus then queue a new trans
|
||||
|
||||
#endif //!(CONFIG_SPIRAM && CONFIG_IDF_TARGET_ESP32)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,327 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
/*
|
||||
Tests for the spi sio mode
|
||||
*/
|
||||
|
||||
#include <esp_types.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "unity.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "driver/spi_slave.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_log.h"
|
||||
#include "soc/spi_periph.h"
|
||||
#include "test_utils.h"
|
||||
#include "test_spi_utils.h"
|
||||
#include "soc/gpio_periph.h"
|
||||
|
||||
#include "hal/spi_ll.h"
|
||||
|
||||
#if (TEST_SPI_PERIPH_NUM >= 2)
|
||||
//These will be only enabled on chips with 2 or more SPI peripherals
|
||||
|
||||
/********************************************************************************
|
||||
* Test SIO
|
||||
********************************************************************************/
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#define MASTER_DIN_SIGNAL HSPID_IN_IDX
|
||||
#elif CONFIG_IDF_TARGET_ESP32P4
|
||||
#define MASTER_DIN_SIGNAL SPI2_D_PAD_IN_IDX
|
||||
#else
|
||||
#define MASTER_DIN_SIGNAL FSPID_IN_IDX
|
||||
#endif
|
||||
static void inner_connect(spi_bus_config_t bus)
|
||||
{
|
||||
//Master MOSI(spid_out) output to `mosi_num`
|
||||
spitest_gpio_output_sel(bus.mosi_io_num, FUNC_GPIO, spi_periph_signal[TEST_SPI_HOST].spid_out);
|
||||
//Slave MOSI(spid_in) input to `mosi_num`
|
||||
spitest_gpio_input_sel(bus.mosi_io_num, FUNC_GPIO, spi_periph_signal[TEST_SLAVE_HOST].spid_in);
|
||||
|
||||
//Master MOSI input(spid_in) to `miso_num`, due to SIO mode, we use Master's `spid_in` to receive data
|
||||
spitest_gpio_input_sel(bus.miso_io_num, FUNC_GPIO, spi_periph_signal[TEST_SPI_HOST].spid_in);
|
||||
//Slave MISO output(spiq_out)
|
||||
spitest_gpio_output_sel(bus.miso_io_num, FUNC_GPIO, spi_periph_signal[TEST_SLAVE_HOST].spiq_out);
|
||||
//Force this signal goes through gpio matrix
|
||||
GPIO.func_in_sel_cfg[MASTER_DIN_SIGNAL].sig_in_sel = 1;
|
||||
}
|
||||
|
||||
TEST_CASE("SPI Single Board Test SIO", "[spi]")
|
||||
{
|
||||
//Master init
|
||||
spi_device_handle_t spi;
|
||||
spi_bus_config_t bus_cfg = SPI_BUS_TEST_DEFAULT_CONFIG();
|
||||
|
||||
TEST_ESP_OK(spi_bus_initialize(TEST_SPI_HOST, &bus_cfg, SPI_DMA_DISABLED));
|
||||
|
||||
spi_device_interface_config_t dev_cfg = SPI_DEVICE_TEST_DEFAULT_CONFIG();
|
||||
dev_cfg.flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_3WIRE;
|
||||
dev_cfg.clock_speed_hz = 4 * 1000 * 1000;
|
||||
TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &dev_cfg, &spi));
|
||||
|
||||
//Slave init
|
||||
bus_cfg.flags = 0;
|
||||
spi_slave_interface_config_t slv_cfg = SPI_SLAVE_TEST_DEFAULT_CONFIG();
|
||||
TEST_ESP_OK(spi_slave_initialize(TEST_SLAVE_HOST, &bus_cfg, &slv_cfg, SPI_DMA_DISABLED));
|
||||
|
||||
same_pin_func_sel(bus_cfg, dev_cfg, 0);
|
||||
inner_connect(bus_cfg);
|
||||
|
||||
WORD_ALIGNED_ATTR uint8_t master_rx_buffer[320];
|
||||
WORD_ALIGNED_ATTR uint8_t slave_rx_buffer[320];
|
||||
|
||||
spi_transaction_t mst_trans;
|
||||
spi_slave_transaction_t slv_trans;
|
||||
spi_slave_transaction_t *ret;
|
||||
|
||||
for (int i = 0; i < 8; i ++) {
|
||||
int tlen = i * 2 + 1;
|
||||
int rlen = 9 - i;
|
||||
|
||||
ESP_LOGI("spi", "=========== TEST(%d) Master TX, Slave RX ==========", i);
|
||||
|
||||
//Slave RX
|
||||
memset(&slv_trans, 0x0, sizeof(spi_slave_transaction_t));
|
||||
memset(slave_rx_buffer, 0x66, sizeof(slave_rx_buffer));
|
||||
slv_trans.length = tlen * 8;
|
||||
slv_trans.rx_buffer = slave_rx_buffer + tlen * 8;
|
||||
TEST_ESP_OK(spi_slave_queue_trans(TEST_SLAVE_HOST, &slv_trans, portMAX_DELAY));
|
||||
|
||||
//Master TX
|
||||
memset(&mst_trans, 0x0, sizeof(spi_transaction_t));
|
||||
mst_trans.length = tlen * 8;
|
||||
mst_trans.tx_buffer = spitest_master_send;
|
||||
TEST_ESP_OK(spi_device_transmit(spi, &mst_trans));
|
||||
|
||||
TEST_ESP_OK(spi_slave_get_trans_result(TEST_SLAVE_HOST, &ret, portMAX_DELAY));
|
||||
TEST_ASSERT(ret == &slv_trans);
|
||||
|
||||
ESP_LOG_BUFFER_HEXDUMP("master tx", mst_trans.tx_buffer, tlen, ESP_LOG_INFO);
|
||||
ESP_LOG_BUFFER_HEXDUMP("slave rx", slv_trans.rx_buffer, tlen, ESP_LOG_INFO);
|
||||
TEST_ASSERT_EQUAL_HEX8_ARRAY(mst_trans.tx_buffer, slv_trans.rx_buffer, tlen);
|
||||
|
||||
ESP_LOGI("spi", "=========== TEST(%d) Master RX, Slave TX ==========", i);
|
||||
//Slave TX
|
||||
memset(&slv_trans, 0x0, sizeof(spi_slave_transaction_t));
|
||||
|
||||
slv_trans.length = rlen * 8;
|
||||
slv_trans.tx_buffer = spitest_slave_send + rlen * 8;
|
||||
TEST_ESP_OK(spi_slave_queue_trans(TEST_SLAVE_HOST, &slv_trans, portMAX_DELAY));
|
||||
|
||||
//Master RX
|
||||
memset(&mst_trans, 0x0, sizeof(spi_transaction_t));
|
||||
memset(master_rx_buffer, 0x66, sizeof(master_rx_buffer));
|
||||
mst_trans.rxlength = rlen * 8;
|
||||
mst_trans.rx_buffer = master_rx_buffer;
|
||||
TEST_ESP_OK(spi_device_transmit(spi, &mst_trans));
|
||||
|
||||
TEST_ESP_OK(spi_slave_get_trans_result(TEST_SLAVE_HOST, &ret, portMAX_DELAY));
|
||||
TEST_ASSERT(ret == &slv_trans);
|
||||
|
||||
ESP_LOG_BUFFER_HEXDUMP("slave tx", slv_trans.tx_buffer, rlen, ESP_LOG_INFO);
|
||||
ESP_LOG_BUFFER_HEXDUMP("master rx", mst_trans.rx_buffer, rlen, ESP_LOG_INFO);
|
||||
|
||||
TEST_ASSERT_EQUAL_HEX8_ARRAY(slv_trans.tx_buffer, mst_trans.rx_buffer, rlen);
|
||||
}
|
||||
|
||||
spi_slave_free(TEST_SLAVE_HOST);
|
||||
master_free_device_bus(spi);
|
||||
}
|
||||
#endif //#if (TEST_SPI_PERIPH_NUM >= 2)
|
||||
|
||||
#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32P4) //IDF-7503 slave support
|
||||
/********************************************************************************
|
||||
* Test SIO Master
|
||||
* SIO Slave is not suported, and one unit test is limited to one feature, so,,,
|
||||
* sio master test can be splited to singal-input and single-output
|
||||
*
|
||||
* for single-output: master slave
|
||||
* cs-----cs ------------- cs
|
||||
* clk----clk ------------- clk
|
||||
* d------mosi------------- mosi
|
||||
* q miso------------- miso
|
||||
* master can get input on mosi pin after output finish in sio mode, but in this
|
||||
* case, master can get no data from slave, so check assert on the slave.
|
||||
*
|
||||
* ------------------------------------------------------------------------------
|
||||
* for single-input: master slave
|
||||
* cs-----cs ------------- cs
|
||||
* clk----clk ------------- clk
|
||||
* d-\ mosi------------- mosi
|
||||
* q \\--miso------------- miso
|
||||
* In this case, master can get input data from slave after output finish, but
|
||||
* slave can get no data from master due to internal broke, besides output data
|
||||
* from both master and slave on miso line will get conflict in master's output
|
||||
* frame.
|
||||
********************************************************************************/
|
||||
#define TRANS_LEN 1024
|
||||
#define MAX_TRANS_BUFF 64
|
||||
#define TEST_NUM 8
|
||||
|
||||
WORD_ALIGNED_ATTR uint8_t sio_master_rx_buff[TRANS_LEN];
|
||||
WORD_ALIGNED_ATTR uint8_t sio_slave_rx_buff [TRANS_LEN];
|
||||
|
||||
void test_sio_master_trans(bool sio_master_in)
|
||||
{
|
||||
spi_device_handle_t dev_0;
|
||||
uint8_t *master_tx_max = heap_caps_calloc(TRANS_LEN * 2, 1, MALLOC_CAP_DMA);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(master_tx_max, "malloc failed, exit.\n");
|
||||
|
||||
// write somethin to a long buffer for test long transmition
|
||||
for (uint16_t i = 0; i < TRANS_LEN; i++) {
|
||||
master_tx_max[i] = i;
|
||||
master_tx_max[TRANS_LEN * 2 - i - 1] = i;
|
||||
}
|
||||
|
||||
spi_bus_config_t bus_cfg = SPI_BUS_TEST_DEFAULT_CONFIG();
|
||||
if (sio_master_in) {
|
||||
// normally, spi read data from port Q and write data to port D
|
||||
// test master input from port D (output default.), so link port D (normally named mosi) to miso pin.
|
||||
bus_cfg.mosi_io_num = bus_cfg.miso_io_num;
|
||||
printf("\n====================Test sio master input====================\n");
|
||||
} else {
|
||||
printf("\n============Test sio master output, data checked by slave.=============\n");
|
||||
}
|
||||
bus_cfg.miso_io_num = -1;
|
||||
TEST_ESP_OK(spi_bus_initialize(TEST_SPI_HOST, &bus_cfg, SPI_DMA_CH_AUTO));
|
||||
|
||||
spi_device_interface_config_t dev_cfg = SPI_DEVICE_TEST_DEFAULT_CONFIG();
|
||||
dev_cfg.flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_3WIRE;
|
||||
dev_cfg.clock_speed_hz = 1 * 1000 * 1000;
|
||||
TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &dev_cfg, &dev_0));
|
||||
printf("CS:CLK:MO:MI: %d\t%d\t%d\t%d\n", dev_cfg.spics_io_num, bus_cfg.sclk_io_num, bus_cfg.mosi_io_num, bus_cfg.miso_io_num);
|
||||
|
||||
unity_send_signal("Master ready");
|
||||
for (int i = 0; i < TEST_NUM; i ++) {
|
||||
spi_transaction_t trans = {};
|
||||
if (sio_master_in) {
|
||||
// master input only section
|
||||
trans.rxlength = (i + 1) * 8 * 8;
|
||||
// test a huge data for last transmition
|
||||
if (i >= TEST_NUM - 1) {
|
||||
trans.rxlength = TRANS_LEN * 8;
|
||||
}
|
||||
trans.rx_buffer = sio_master_rx_buff;
|
||||
trans.length = 0;
|
||||
trans.tx_buffer = NULL;
|
||||
memset(sio_master_rx_buff, 0, sizeof(sio_master_rx_buff));
|
||||
} else {
|
||||
// master output only section
|
||||
trans.length = MAX_TRANS_BUFF / (i + 1) * 8;
|
||||
// test a huge data for last transmition
|
||||
if (i >= TEST_NUM - 1) {
|
||||
trans.length = TRANS_LEN * 8;
|
||||
}
|
||||
trans.tx_buffer = master_tx_max;
|
||||
trans.rxlength = 0;
|
||||
trans.rx_buffer = NULL;
|
||||
// use some differnt data
|
||||
trans.tx_buffer += (i % 2) ? TRANS_LEN : 0;
|
||||
}
|
||||
|
||||
//get signal
|
||||
unity_wait_for_signal("Slave ready");
|
||||
|
||||
TEST_ESP_OK(spi_device_transmit(dev_0, &trans));
|
||||
if (sio_master_in) {
|
||||
ESP_LOG_BUFFER_HEXDUMP("master rx", trans.rx_buffer, trans.rxlength / 8, ESP_LOG_INFO);
|
||||
TEST_ASSERT_EQUAL_HEX8_ARRAY(master_tx_max + i, trans.rx_buffer, trans.rxlength / 8);
|
||||
} else {
|
||||
printf("%d master output\n", trans.length / 8);
|
||||
ESP_LOG_BUFFER_HEXDUMP("master tx", trans.tx_buffer, trans.length / 8, ESP_LOG_INFO);
|
||||
}
|
||||
}
|
||||
|
||||
free(master_tx_max);
|
||||
master_free_device_bus(dev_0);
|
||||
}
|
||||
|
||||
void test_sio_slave_emulate(bool sio_master_in)
|
||||
{
|
||||
uint8_t *slave_tx_max = heap_caps_calloc(TRANS_LEN * 2, 1, MALLOC_CAP_DMA);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(slave_tx_max, "malloc failed, exit.\n");
|
||||
|
||||
// write somethin to a long buffer for test long transmition
|
||||
for (uint16_t i = 0; i < TRANS_LEN; i++) {
|
||||
slave_tx_max[i] = i;
|
||||
slave_tx_max[TRANS_LEN * 2 - i - 1] = i;
|
||||
}
|
||||
|
||||
if (sio_master_in) {
|
||||
printf("\n==================Test sio master input.================\n");
|
||||
} else {
|
||||
printf("\n==================Test sio master output.=================\n");
|
||||
}
|
||||
|
||||
spi_bus_config_t bus_cfg = SPI_BUS_TEST_DEFAULT_CONFIG();
|
||||
spi_slave_interface_config_t slv_cfg = SPI_SLAVE_TEST_DEFAULT_CONFIG();
|
||||
TEST_ESP_OK(spi_slave_initialize(TEST_SLAVE_HOST, &bus_cfg, &slv_cfg, SPI_DMA_CH_AUTO));
|
||||
printf("CS:CLK:MO:MI: %d\t%d\t%d\t%d\n", slv_cfg.spics_io_num, bus_cfg.sclk_io_num, bus_cfg.mosi_io_num, bus_cfg.miso_io_num);
|
||||
|
||||
unity_wait_for_signal("Master ready");
|
||||
for (int i = 0; i < TEST_NUM; i++) {
|
||||
spi_slave_transaction_t trans = {};
|
||||
if (sio_master_in) {
|
||||
// slave output only section
|
||||
trans.length = (i + 1) * 8 * 8;
|
||||
// test a huge data for last transmition
|
||||
if (i >= TEST_NUM - 1) {
|
||||
trans.length = TRANS_LEN * 8;
|
||||
}
|
||||
trans.tx_buffer = slave_tx_max + i;
|
||||
trans.rx_buffer = NULL;
|
||||
} else {
|
||||
// slave input only section
|
||||
trans.length = MAX_TRANS_BUFF / (i + 1) * 8;
|
||||
// test a huge data for last transmition
|
||||
if (i >= TEST_NUM - 1) {
|
||||
trans.length = TRANS_LEN * 8;
|
||||
}
|
||||
trans.tx_buffer = NULL;
|
||||
trans.rx_buffer = sio_slave_rx_buff;
|
||||
memset(sio_slave_rx_buff, 0, sizeof(sio_slave_rx_buff));
|
||||
}
|
||||
|
||||
TEST_ESP_OK(spi_slave_queue_trans(TEST_SLAVE_HOST, &trans, portMAX_DELAY));
|
||||
unity_send_signal("Slave ready");
|
||||
|
||||
spi_slave_transaction_t *p_slave_ret;
|
||||
TEST_ESP_OK(spi_slave_get_trans_result(TEST_SLAVE_HOST, &p_slave_ret, portMAX_DELAY));
|
||||
|
||||
if (sio_master_in) {
|
||||
ESP_LOG_BUFFER_HEXDUMP("Slave tx", trans.tx_buffer, trans.length / 8, ESP_LOG_INFO);
|
||||
} else {
|
||||
ESP_LOG_BUFFER_HEXDUMP("Slave rx", trans.rx_buffer, trans.length / 8, ESP_LOG_INFO);
|
||||
TEST_ASSERT_EQUAL_HEX8_ARRAY(slave_tx_max + TRANS_LEN * (i % 2), trans.rx_buffer, trans.length / 8);
|
||||
}
|
||||
}
|
||||
|
||||
free(slave_tx_max);
|
||||
spi_slave_free(TEST_SLAVE_HOST);
|
||||
}
|
||||
|
||||
void test_master_run(void)
|
||||
{
|
||||
test_sio_master_trans(false);
|
||||
test_sio_master_trans(true);
|
||||
}
|
||||
|
||||
void test_slave_run(void)
|
||||
{
|
||||
test_sio_slave_emulate(false);
|
||||
test_sio_slave_emulate(true);
|
||||
}
|
||||
|
||||
TEST_CASE_MULTIPLE_DEVICES("SPI_Master:Test_SIO_Mode_Multi_Board", "[spi_ms][test_env=generic_multi_device]", test_master_run, test_slave_run);
|
||||
#endif //p4 slave support
|
||||
@@ -0,0 +1,8 @@
|
||||
# Special partition table for unit test app
|
||||
#
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
|
||||
factory, 0, 0, 0x20000, 0x260000
|
||||
|
||||
# flash_test partition used for SPI flash tests, WL FAT tests, and SPIFFS tests
|
||||
flash_test, data, fat, , 528K
|
||||
|
@@ -0,0 +1,49 @@
|
||||
# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
import pytest
|
||||
|
||||
|
||||
# If `test_env` is define, should not run on generic runner
|
||||
@pytest.mark.temp_skip_ci(targets=['esp32p4'], reason='p4 support TBD') # TODO: IDF-8942
|
||||
@pytest.mark.supported_targets
|
||||
@pytest.mark.esp32h2
|
||||
@pytest.mark.generic
|
||||
@pytest.mark.parametrize('config', ['defaults', 'release', 'freertos_compliance', 'freertos_flash',], indirect=True)
|
||||
def test_master_single_dev(case_tester) -> None: # type: ignore
|
||||
for case in case_tester.test_menu:
|
||||
if 'test_env' in case.attributes:
|
||||
continue
|
||||
case_tester.run_normal_case(case=case, reset=True)
|
||||
|
||||
|
||||
# Job for test_env `external_flash` just for esp32 only
|
||||
@pytest.mark.esp32
|
||||
@pytest.mark.flash_multi
|
||||
@pytest.mark.parametrize('config', ['defaults',], indirect=True)
|
||||
def test_master_esp_flash(case_tester) -> None: # type: ignore
|
||||
for case in case_tester.test_menu:
|
||||
# test case `spi_bus_lock_with_flash` use difference test env
|
||||
if case.attributes.get('test_env') == 'external_flash':
|
||||
case_tester.run_normal_case(case=case, reset=True)
|
||||
|
||||
|
||||
# if `test_env` not defined, will run on `generic_multi_device` by default
|
||||
@pytest.mark.temp_skip_ci(targets=['esp32p4'], reason='p4 support TBD') # TODO: IDF-8942
|
||||
@pytest.mark.supported_targets
|
||||
@pytest.mark.esp32h2
|
||||
@pytest.mark.generic_multi_device
|
||||
@pytest.mark.parametrize(
|
||||
'count, config',
|
||||
[
|
||||
(2, 'defaults',),
|
||||
(2, 'release',),
|
||||
(2, 'freertos_compliance',),
|
||||
(2, 'freertos_flash',),
|
||||
(2, 'iram_safe'),
|
||||
],
|
||||
indirect=True
|
||||
)
|
||||
def test_master_multi_dev(case_tester) -> None: # type: ignore
|
||||
for case in case_tester.test_menu:
|
||||
if case.attributes.get('test_env', 'generic_multi_device') == 'generic_multi_device':
|
||||
case_tester.run_multi_dev_case(case=case, reset=True)
|
||||
@@ -0,0 +1,2 @@
|
||||
# don't delete.
|
||||
# used for CI to compile a default config when 'sdkconfig.ci.xxxx' is exist
|
||||
@@ -0,0 +1 @@
|
||||
CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE=y
|
||||
@@ -0,0 +1 @@
|
||||
CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y
|
||||
@@ -0,0 +1,8 @@
|
||||
CONFIG_COMPILER_DUMP_RTL_FILES=y
|
||||
CONFIG_SPI_MASTER_ISR_IN_IRAM=y
|
||||
CONFIG_SPI_MASTER_IN_IRAM=y
|
||||
CONFIG_SPI_SLAVE_ISR_IN_IRAM=n
|
||||
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_NONE=y
|
||||
# silent the error check, as the error string are stored in rodata, causing RTL check failure
|
||||
CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y
|
||||
@@ -0,0 +1,3 @@
|
||||
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
|
||||
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
|
||||
@@ -0,0 +1,2 @@
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
CONFIG_ESP_TASK_WDT=n
|
||||
@@ -0,0 +1,8 @@
|
||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
|
||||
CONFIG_XTAL_FREQ_AUTO=y
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
|
||||
CONFIG_SPI_FLASH_SHARE_SPI1_BUS=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partition_table_esp32_flash.csv"
|
||||
CONFIG_PARTITION_TABLE_FILENAME="partition_table_esp32_flash.csv"
|
||||
CONFIG_PARTITION_TABLE_OFFSET=0x8000
|
||||
@@ -0,0 +1,2 @@
|
||||
# test_case `spi_speed` need freq 240M
|
||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
|
||||
@@ -0,0 +1,2 @@
|
||||
# test_case `spi_speed` need freq 240M
|
||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
|
||||
Reference in New Issue
Block a user