fix(spi): test_apps mv idf_perform_test to esp_driver_spi domain

This commit is contained in:
wanlei
2024-03-07 18:16:27 +08:00
parent 0cf11e5b87
commit 8625ed252e
59 changed files with 141 additions and 110 deletions
@@ -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
1 # Special partition table for unit test app
2 #
3 # Name, Type, SubType, Offset, Size, Flags
4 # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
5 factory, 0, 0, 0x20000, 0x260000
6 # flash_test partition used for SPI flash tests, WL FAT tests, and SPIFFS tests
7 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