/* * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include "esp_log.h" #include "esp_attr.h" #include "soc/spi_periph.h" #include "sdkconfig.h" #include "test_utils.h" #include "test_spi_utils.h" #include "driver/spi_master.h" #include "driver/spi_slave.h" #include "spi_performance.h" #if SOC_SPI_SUPPORT_SLAVE_HD_VER2 #include "esp_serial_slave_link/essl_spi.h" #include "driver/spi_slave_hd.h" #endif #if (TEST_SPI_PERIPH_NUM >= 2) //These will only be enabled on chips with 2 or more SPI peripherals #ifndef MIN #define MIN(a, b)((a) > (b)? (b): (a)) #endif /******************************************************************************** * Test By Internal Connections ********************************************************************************/ static void local_test_init(void **context); static void local_test_deinit(void *context); static void local_test_loop(const void *test_param, void *context); static const ptest_func_t local_test_func = { .pre_test = local_test_init, .post_test = local_test_deinit, .loop = local_test_loop, .def_param = spitest_def_param, }; #define TEST_SPI_LOCAL(name, param_set) \ PARAM_GROUP_DECLARE(name, param_set) \ TEST_SINGLE_BOARD(SPI_##name, param_set, "[spi][timeout=120]", &local_test_func) static void local_test_init(void **arg) { esp_log_level_set("gpio", ESP_LOG_WARN); TEST_ASSERT(*arg == NULL); *arg = malloc(sizeof(spitest_context_t)); spitest_context_t *context = (spitest_context_t *)*arg; TEST_ASSERT(context != NULL); context->slave_context = (spi_slave_task_context_t) {}; esp_err_t err = init_slave_context(&context->slave_context, TEST_SLAVE_HOST); TEST_ASSERT(err == ESP_OK); xTaskCreate(spitest_slave_task, "spi_slave", 4096, &context->slave_context, 0, &context->handle_slave); } static void local_test_deinit(void *arg) { spitest_context_t *context = arg; vTaskDelete(context->handle_slave); context->handle_slave = 0; deinit_slave_context(&context->slave_context); } static void local_test_start(spi_device_handle_t *spi, int freq, const spitest_param_set_t *pset, spitest_context_t *context) { //master config spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG(); spi_device_interface_config_t devcfg = SPI_DEVICE_TEST_DEFAULT_CONFIG(); spi_slave_interface_config_t slvcfg = SPI_SLAVE_TEST_DEFAULT_CONFIG(); //pin config & initialize //we can't have two sets of iomux pins on the same pins assert(!pset->master_iomux || !pset->slave_iomux); if (pset->slave_iomux) { //only in this case, use VSPI iomux pins buscfg.miso_io_num = SLAVE_IOMUX_PIN_MISO; buscfg.mosi_io_num = SLAVE_IOMUX_PIN_MOSI; buscfg.sclk_io_num = SLAVE_IOMUX_PIN_SCLK; devcfg.spics_io_num = SLAVE_IOMUX_PIN_CS; slvcfg.spics_io_num = SLAVE_IOMUX_PIN_CS; } else { buscfg.miso_io_num = MASTER_IOMUX_PIN_MISO; buscfg.mosi_io_num = MASTER_IOMUX_PIN_MOSI; buscfg.sclk_io_num = MASTER_IOMUX_PIN_SCLK; devcfg.spics_io_num = MASTER_IOMUX_PIN_CS; slvcfg.spics_io_num = MASTER_IOMUX_PIN_CS; } //this does nothing, but avoid the driver from using iomux pins if required buscfg.quadhd_io_num = (!pset->master_iomux && !pset->slave_iomux ? UNCONNECTED_PIN : -1); devcfg.mode = pset->mode; const int cs_pretrans_max = 15; if (pset->dup == HALF_DUPLEX_MISO) { devcfg.cs_ena_pretrans = cs_pretrans_max; devcfg.flags |= SPI_DEVICE_HALFDUPLEX; } else if (pset->dup == HALF_DUPLEX_MOSI) { devcfg.cs_ena_pretrans = cs_pretrans_max; devcfg.flags |= SPI_DEVICE_NO_DUMMY; } else { devcfg.cs_ena_pretrans = cs_pretrans_max; } const int cs_posttrans_max = 15; devcfg.cs_ena_posttrans = cs_posttrans_max; devcfg.input_delay_ns = pset->slave_tv_ns; devcfg.clock_speed_hz = freq; if (pset->master_limit != 0 && freq > pset->master_limit) { devcfg.flags |= SPI_DEVICE_NO_DUMMY; } //slave config slvcfg.mode = pset->mode; slave_pull_up(&buscfg, slvcfg.spics_io_num); int dma_chan = (pset->master_dma_chan == 0) ? 0 : SPI_DMA_CH_AUTO; TEST_ESP_OK(spi_bus_initialize(TEST_SPI_HOST, &buscfg, dma_chan)); TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &devcfg, spi)); //slave automatically use iomux pins if pins are on VSPI_* pins buscfg.quadhd_io_num = -1; int slave_dma_chan = (pset->slave_dma_chan == 0) ? 0 : SPI_DMA_CH_AUTO; TEST_ESP_OK(spi_slave_initialize(TEST_SLAVE_HOST, &buscfg, &slvcfg, slave_dma_chan)); //initialize master and slave on the same pins break some of the output configs, fix them if (pset->master_iomux) { spitest_gpio_output_sel(buscfg.mosi_io_num, FUNC_SPI, spi_periph_signal[TEST_SPI_HOST].spid_out); spitest_gpio_output_sel(buscfg.miso_io_num, FUNC_GPIO, spi_periph_signal[TEST_SLAVE_HOST].spiq_out); spitest_gpio_output_sel(devcfg.spics_io_num, FUNC_SPI, spi_periph_signal[TEST_SPI_HOST].spics_out[0]); spitest_gpio_output_sel(buscfg.sclk_io_num, FUNC_SPI, spi_periph_signal[TEST_SPI_HOST].spiclk_out); } else if (pset->slave_iomux) { spitest_gpio_output_sel(buscfg.mosi_io_num, FUNC_GPIO, spi_periph_signal[TEST_SPI_HOST].spid_out); spitest_gpio_output_sel(buscfg.miso_io_num, FUNC_SPI, spi_periph_signal[TEST_SLAVE_HOST].spiq_out); spitest_gpio_output_sel(devcfg.spics_io_num, FUNC_GPIO, spi_periph_signal[TEST_SPI_HOST].spics_out[0]); spitest_gpio_output_sel(buscfg.sclk_io_num, FUNC_GPIO, spi_periph_signal[TEST_SPI_HOST].spiclk_out); } else { spitest_gpio_output_sel(buscfg.mosi_io_num, FUNC_GPIO, spi_periph_signal[TEST_SPI_HOST].spid_out); spitest_gpio_output_sel(buscfg.miso_io_num, FUNC_GPIO, spi_periph_signal[TEST_SLAVE_HOST].spiq_out); spitest_gpio_output_sel(devcfg.spics_io_num, FUNC_GPIO, spi_periph_signal[TEST_SPI_HOST].spics_out[0]); spitest_gpio_output_sel(buscfg.sclk_io_num, FUNC_GPIO, spi_periph_signal[TEST_SPI_HOST].spiclk_out); } if (context) { //clear master receive buffer memset(context->master_rxbuf, 0x66, sizeof(context->master_rxbuf)); } } static void local_test_end(spi_device_handle_t spi) { master_free_device_bus(spi); TEST_ASSERT(spi_slave_free(TEST_SLAVE_HOST) == ESP_OK); } static void local_test_loop(const void *arg1, void *arg2) { const spitest_param_set_t *pset = arg1; spitest_context_t *context = arg2; spi_device_handle_t spi; spitest_init_transactions(pset, context); const int *timing_speed_array = pset->freq_list; ESP_LOGI(MASTER_TAG, "****************** %s ***************", pset->pset_name); for (int i = 0; ; i++) { const int freq = timing_speed_array[i]; if (freq == 0) { break; } if (pset->freq_limit && freq > pset->freq_limit) { break; } ESP_LOGI(MASTER_TAG, "==> %dkHz", freq / 1000); bool check_master_data = (pset->dup != HALF_DUPLEX_MOSI && (pset->master_limit == 0 || freq <= pset->master_limit)); if (!check_master_data) { ESP_LOGI(MASTER_TAG, "skip master data check"); } bool check_slave_data = (pset->dup != HALF_DUPLEX_MISO); if (!check_slave_data) { ESP_LOGI(SLAVE_TAG, "skip slave data check"); } local_test_start(&spi, freq, pset, context); for (int k = 0; k < pset->test_size; k++) { WORD_ALIGNED_ATTR uint8_t recvbuf[320 + 8]; slave_txdata_t *txdata = &context->slave_trans[k]; spi_slave_transaction_t slave_trans = { .tx_buffer = txdata->start, .rx_buffer = recvbuf, .length = txdata->len, }; esp_err_t err = spi_slave_queue_trans(TEST_SLAVE_HOST, &slave_trans, portMAX_DELAY); TEST_ESP_OK(err); //wait for both master and slave end spi_transaction_t *t = &context->master_trans[k]; int len = get_trans_len(pset->dup, t); ESP_LOGI(MASTER_TAG, " ==> #%d: len: %d", k, len); //send master tx data err = spi_device_transmit(spi, t); TEST_ESP_OK(err); spi_slave_transaction_t *ret_trans; err = spi_slave_get_trans_result(TEST_SLAVE_HOST, &ret_trans, 5); TEST_ESP_OK(err); TEST_ASSERT_EQUAL(&slave_trans, ret_trans); uint32_t rcv_len = slave_trans.trans_len; bool failed = false; //check master data if (check_master_data && memcmp(slave_trans.tx_buffer, t->rx_buffer, (len + 7) / 8) != 0) { failed = true; } //check slave data and length //currently the rcv_len can be in range of [t->length-1, t->length+3] if (rcv_len < len - 1 || rcv_len > len + 4) { failed = true; } if (check_slave_data && memcmp(t->tx_buffer, slave_trans.rx_buffer, (len + 7) / 8) != 0) { failed = true; } if (failed) { ESP_LOGI(SLAVE_TAG, "slave_recv_len: %" PRIu32, rcv_len); spitest_master_print_data(t, len); ESP_LOG_BUFFER_HEX("slave tx", slave_trans.tx_buffer, len); ESP_LOG_BUFFER_HEX("slave rx", slave_trans.rx_buffer, len); //already failed, try to use the TEST_ASSERT to output the reason... TEST_ASSERT_EQUAL_HEX8_ARRAY(slave_trans.tx_buffer, t->rx_buffer, (len + 7) / 8); TEST_ASSERT_EQUAL_HEX8_ARRAY(t->tx_buffer, slave_trans.rx_buffer, (len + 7) / 8); TEST_ASSERT(rcv_len >= len - 1 && rcv_len <= len + 4); } } local_test_end(spi); } } /************ Timing Test ***********************************************/ //TODO: esp32s2 has better timing performance static spitest_param_set_t timing_pgroup[] = { //signals are not fed to peripherals through iomux if the functions are not selected to iomux #if !DISABLED_FOR_TARGETS(ESP32S2, ESP32S3) { .pset_name = "FULL_DUP, MASTER IOMUX", .freq_limit = ESP_SPI_SLAVE_MAX_FREQ_SYNC, .master_limit = 13 * 1000 * 1000, .dup = FULL_DUPLEX, .master_iomux = true, .slave_iomux = false, .slave_tv_ns = TV_INT_CONNECT_GPIO, }, { .pset_name = "FULL_DUP, SLAVE IOMUX", .freq_limit = ESP_SPI_SLAVE_MAX_FREQ_SYNC, .master_limit = 13 * 1000 * 1000, .dup = FULL_DUPLEX, .master_iomux = false, .slave_iomux = true, .slave_tv_ns = TV_INT_CONNECT, }, #endif { .pset_name = "FULL_DUP, BOTH GPIO", .freq_limit = ESP_SPI_SLAVE_MAX_FREQ_SYNC, .master_limit = 10 * 1000 * 1000, .dup = FULL_DUPLEX, .master_iomux = false, .slave_iomux = false, .slave_tv_ns = TV_INT_CONNECT_GPIO, }, //signals are not fed to peripherals through iomux if the functions are not selected to iomux #if !DISABLED_FOR_TARGETS(ESP32S2, ESP32S3) { .pset_name = "MISO_DUP, MASTER IOMUX", .freq_limit = ESP_SPI_SLAVE_MAX_FREQ_SYNC, .master_limit = ESP_SPI_SLAVE_MAX_FREQ_SYNC, .dup = HALF_DUPLEX_MISO, .master_iomux = true, .slave_iomux = false, .slave_tv_ns = TV_INT_CONNECT_GPIO, }, { .pset_name = "MISO_DUP, SLAVE IOMUX", .freq_limit = ESP_SPI_SLAVE_MAX_FREQ_SYNC, //.freq_limit = ESP_SPI_SLAVE_MAX_FREQ_SYNC, .dup = HALF_DUPLEX_MISO, .master_iomux = false, .slave_iomux = true, .slave_tv_ns = TV_INT_CONNECT, }, #endif { .pset_name = "MISO_DUP, BOTH GPIO", .freq_limit = ESP_SPI_SLAVE_MAX_FREQ_SYNC, //.freq_limit = ESP_SPI_SLAVE_MAX_FREQ_SYNC, .dup = HALF_DUPLEX_MISO, .master_iomux = false, .slave_iomux = false, .slave_tv_ns = TV_INT_CONNECT_GPIO, }, //signals are not fed to peripherals through iomux if the functions are not selected to iomux #if !DISABLED_FOR_TARGETS(ESP32S2, ESP32S3) { .pset_name = "MOSI_DUP, MASTER IOMUX", .freq_limit = ESP_SPI_SLAVE_MAX_FREQ_SYNC, //.freq_limit = ESP_SPI_SLAVE_MAX_READ_FREQ, //ESP_SPI_SLAVE_MAX_FREQ_SYNC, .dup = HALF_DUPLEX_MOSI, .master_iomux = true, .slave_iomux = false, .slave_tv_ns = TV_INT_CONNECT_GPIO, }, { .pset_name = "MOSI_DUP, SLAVE IOMUX", .freq_limit = ESP_SPI_SLAVE_MAX_FREQ_SYNC, //.freq_limit = ESP_SPI_SLAVE_MAX_READ_FREQ, //ESP_SPI_SLAVE_MAX_FREQ_SYNC, .dup = HALF_DUPLEX_MOSI, .master_iomux = false, .slave_iomux = true, .slave_tv_ns = TV_INT_CONNECT, }, #endif { .pset_name = "MOSI_DUP, BOTH GPIO", .freq_limit = ESP_SPI_SLAVE_MAX_FREQ_SYNC, //.freq_limit = ESP_SPI_SLAVE_MAX_READ_FREQ, //ESP_SPI_SLAVE_MAX_FREQ_SYNC, .dup = HALF_DUPLEX_MOSI, .master_iomux = false, .slave_iomux = false, .slave_tv_ns = TV_INT_CONNECT_GPIO, }, }; TEST_SPI_LOCAL(TIMING, timing_pgroup) /************ Mode Test ***********************************************/ #define FREQ_LIMIT_MODE 16 * 1000 * 1000 static int test_freq_mode_local[] = { 1 * 1000 * 1000, 9 * 1000 * 1000, //maximum freq MISO stable before next latch edge 13 * 1000 * 1000, 16 * 1000 * 1000, 20 * 1000 * 1000, 26 * 1000 * 1000, 40 * 1000 * 1000, 0, }; //signals are not fed to peripherals through iomux if the functions are not selected to iomux #ifdef CONFIG_IDF_TARGET_ESP32 #define LOCAL_MODE_TEST_SLAVE_IOMUX true /* * When DMA is enabled in mode 0 and 2, an special workaround is used. The MISO (slave's output) is * half an SPI clock ahead, but then delay 3 apb clocks. * Compared to the normal timing, the MISO is not slower than when the frequency is below 13.3MHz, * under which there's no need for the master to compensate the MISO signal. However compensation * is required when the frequency is beyond 16MHz, at this time, an extra positive delay is added * to the normal delay (3 apb clocks). * * It's is hard to tell the master driver that kind of delay logic. This magic delay value happens * to compensate master timing beyond 16MHz. * * If the master or slave's timing is changed again, and the test no longer passes, above 16MHz, * it's OK to use `master_limit` to disable master data check or skip the test above some * frequencies above 10MHz (the design target value). */ #define SLAVE_EXTRA_DELAY_DMA 12.5 #else #define LOCAL_MODE_TEST_SLAVE_IOMUX false #define SLAVE_EXTRA_DELAY_DMA 0 #endif static spitest_param_set_t mode_pgroup[] = { { .pset_name = "Mode 0", .freq_list = test_freq_mode_local, .master_limit = 13 * 1000 * 1000, .dup = FULL_DUPLEX, .mode = 0, .master_iomux = false, .slave_iomux = LOCAL_MODE_TEST_SLAVE_IOMUX, .slave_tv_ns = TV_INT_CONNECT, }, { .pset_name = "Mode 1", .freq_list = test_freq_mode_local, .freq_limit = 26 * 1000 * 1000, .master_limit = 13 * 1000 * 1000, .dup = FULL_DUPLEX, .mode = 1, .master_iomux = false, .slave_iomux = LOCAL_MODE_TEST_SLAVE_IOMUX, .slave_tv_ns = TV_INT_CONNECT, }, { .pset_name = "Mode 2", .freq_list = test_freq_mode_local, .master_limit = 13 * 1000 * 1000, .dup = FULL_DUPLEX, .mode = 2, .master_iomux = false, .slave_iomux = LOCAL_MODE_TEST_SLAVE_IOMUX, .slave_tv_ns = TV_INT_CONNECT, }, { .pset_name = "Mode 3", .freq_list = test_freq_mode_local, .freq_limit = 26 * 1000 * 1000, .master_limit = 13 * 1000 * 1000, .dup = FULL_DUPLEX, .mode = 3, .master_iomux = false, .slave_iomux = LOCAL_MODE_TEST_SLAVE_IOMUX, .slave_tv_ns = TV_INT_CONNECT, }, { .pset_name = "Mode 0, DMA", .freq_list = test_freq_mode_local, .master_limit = 13 * 1000 * 1000, .dup = FULL_DUPLEX, .mode = 0, .slave_dma_chan = SPI_DMA_CH_AUTO, .master_iomux = false, .slave_iomux = LOCAL_MODE_TEST_SLAVE_IOMUX, .slave_tv_ns = TV_INT_CONNECT, .length_aligned = true, }, { .pset_name = "Mode 1, DMA", .freq_list = test_freq_mode_local, .freq_limit = 26 * 1000 * 1000, .master_limit = 13 * 1000 * 1000, .dup = FULL_DUPLEX, .mode = 1, .slave_dma_chan = SPI_DMA_CH_AUTO, .master_iomux = false, .slave_iomux = LOCAL_MODE_TEST_SLAVE_IOMUX, .slave_tv_ns = TV_INT_CONNECT, .length_aligned = true, }, { .pset_name = "Mode 2, DMA", .freq_list = test_freq_mode_local, .master_limit = 13 * 1000 * 1000, .dup = FULL_DUPLEX, .mode = 2, .slave_dma_chan = SPI_DMA_CH_AUTO, .master_iomux = false, .slave_iomux = LOCAL_MODE_TEST_SLAVE_IOMUX, .slave_tv_ns = TV_INT_CONNECT, .length_aligned = true, }, { .pset_name = "Mode 3, DMA", .freq_list = test_freq_mode_local, .freq_limit = 26 * 1000 * 1000, .master_limit = 13 * 1000 * 1000, .dup = FULL_DUPLEX, .mode = 3, .slave_dma_chan = SPI_DMA_CH_AUTO, .master_iomux = false, .slave_iomux = LOCAL_MODE_TEST_SLAVE_IOMUX, .slave_tv_ns = TV_INT_CONNECT, .length_aligned = true, }, /////////////////////////// MISO //////////////////////////////////// { .pset_name = "MISO, Mode 0", .freq_list = test_freq_mode_local, .dup = HALF_DUPLEX_MISO, .mode = 0, .master_iomux = false, .slave_iomux = LOCAL_MODE_TEST_SLAVE_IOMUX, .slave_tv_ns = TV_INT_CONNECT, }, { .pset_name = "MISO, Mode 1", .freq_list = test_freq_mode_local, .dup = HALF_DUPLEX_MISO, .mode = 1, .master_iomux = false, .slave_iomux = LOCAL_MODE_TEST_SLAVE_IOMUX, .slave_tv_ns = TV_INT_CONNECT, }, { .pset_name = "MISO, Mode 2", .freq_list = test_freq_mode_local, .dup = HALF_DUPLEX_MISO, .mode = 2, .master_iomux = false, .slave_iomux = LOCAL_MODE_TEST_SLAVE_IOMUX, .slave_tv_ns = TV_INT_CONNECT, }, { .pset_name = "MISO, Mode 3", .freq_list = test_freq_mode_local, .dup = HALF_DUPLEX_MISO, .mode = 3, .master_iomux = false, .slave_iomux = LOCAL_MODE_TEST_SLAVE_IOMUX, .slave_tv_ns = TV_INT_CONNECT, }, { .pset_name = "MISO, Mode 0, DMA", .freq_list = test_freq_mode_local, .dup = HALF_DUPLEX_MISO, .mode = 0, .slave_dma_chan = SPI_DMA_CH_AUTO, .master_iomux = false, .slave_iomux = LOCAL_MODE_TEST_SLAVE_IOMUX, .slave_tv_ns = TV_INT_CONNECT + SLAVE_EXTRA_DELAY_DMA, .length_aligned = true, }, { .pset_name = "MISO, Mode 1, DMA", .freq_list = test_freq_mode_local, .dup = HALF_DUPLEX_MISO, .mode = 1, .slave_dma_chan = SPI_DMA_CH_AUTO, .master_iomux = false, .slave_iomux = LOCAL_MODE_TEST_SLAVE_IOMUX, .slave_tv_ns = TV_INT_CONNECT, .length_aligned = true, }, { .pset_name = "MISO, Mode 2, DMA", .freq_list = test_freq_mode_local, .dup = HALF_DUPLEX_MISO, .mode = 2, .slave_dma_chan = SPI_DMA_CH_AUTO, .master_iomux = false, .slave_iomux = LOCAL_MODE_TEST_SLAVE_IOMUX, .slave_tv_ns = TV_INT_CONNECT + SLAVE_EXTRA_DELAY_DMA, .length_aligned = true, }, { .pset_name = "MISO, Mode 3, DMA", .freq_list = test_freq_mode_local, .dup = HALF_DUPLEX_MISO, .mode = 3, .slave_dma_chan = SPI_DMA_CH_AUTO, .master_iomux = false, .slave_iomux = LOCAL_MODE_TEST_SLAVE_IOMUX, .slave_tv_ns = TV_INT_CONNECT, .length_aligned = true, }, }; TEST_SPI_LOCAL(MODE, mode_pgroup) /**********************SPI master slave transaction length test*************/ /* Test SPI slave can receive different length of data in all 4 modes (permutations of * CPOL/CPHA and when DMA is used or not). * Length from 1 to 16 bytes are tested. */ #define MASTER_DATA_RAND_SEED 123 #define SLAVE_DATA_RAND_SEED 456 TEST_CASE("Slave receive correct data", "[spi]") { // Initialize device handle and spi bus unsigned int master_seed_send = MASTER_DATA_RAND_SEED; unsigned int slave_seed_send = SLAVE_DATA_RAND_SEED; unsigned int master_seed_cmp = slave_seed_send; unsigned int slave_seed_cmp = master_seed_send; const int buf_size = 20; WORD_ALIGNED_ATTR uint8_t slave_sendbuf[buf_size]; WORD_ALIGNED_ATTR uint8_t slave_recvbuf[buf_size]; WORD_ALIGNED_ATTR uint8_t master_sendbuf[buf_size]; WORD_ALIGNED_ATTR uint8_t master_recvbuf[buf_size]; uint8_t master_cmpbuf[buf_size]; uint8_t slave_cmpbuf[buf_size]; for (int spi_mode = 0; spi_mode < 4; spi_mode++) { for (int dma_chan = 0; dma_chan < 2; dma_chan++) { spi_device_handle_t spi; spitest_param_set_t test_param = { .dup = FULL_DUPLEX, .mode = spi_mode, .master_iomux = false, .slave_iomux = false, .master_dma_chan = 0, .slave_dma_chan = (dma_chan ? SPI_DMA_CH_AUTO : 0), }; ESP_LOGI(SLAVE_TAG, "Test slave recv @ mode %d, dma enabled=%d", spi_mode, dma_chan); local_test_start(&spi, 1000 * 1000, &test_param, NULL); for (int round = 0; round < 20; round++) { // printf("trans %d\n", round); int master_trans_len = round + 1; const int slave_trans_len = 16; memset(master_sendbuf, 0xcc, buf_size); memset(slave_sendbuf, 0x55, buf_size); memset(master_recvbuf, 0xaa, buf_size); memset(slave_recvbuf, 0xbb, buf_size); for (int i = 0; i < master_trans_len; i++) { master_sendbuf[i] = rand_r(&master_seed_send); slave_sendbuf[i] = rand_r(&slave_seed_send); } spi_slave_transaction_t slave_trans = { .length = slave_trans_len * 8, .tx_buffer = slave_sendbuf, .rx_buffer = slave_recvbuf }; esp_err_t ret = spi_slave_queue_trans(TEST_SLAVE_HOST, &slave_trans, portMAX_DELAY); TEST_ESP_OK(ret); spi_transaction_t master_trans = { .length = 8 * master_trans_len, .tx_buffer = master_sendbuf, .rx_buffer = master_recvbuf }; ret = spi_device_transmit(spi, &master_trans); TEST_ESP_OK(ret); spi_slave_transaction_t *out_trans; ret = spi_slave_get_trans_result(TEST_SLAVE_HOST, &out_trans, portMAX_DELAY); TEST_ESP_OK(ret); TEST_ASSERT_EQUAL_HEX32(&slave_trans, out_trans); for (int i = 0; i < master_trans_len; i++) { master_cmpbuf[i] = rand_r(&master_seed_cmp); slave_cmpbuf[i] = rand_r(&slave_seed_cmp); } // esp_log_buffer_hex("master_send", master_sendbuf, buf_size); // esp_log_buffer_hex("slave_recv", slave_recvbuf, buf_size); // esp_log_buffer_hex("slave_send", slave_sendbuf, buf_size); // esp_log_buffer_hex("master_recv", master_recvbuf, buf_size); int master_expected_len = MIN(master_trans_len, slave_trans_len); TEST_ASSERT_EQUAL_HEX8_ARRAY(master_cmpbuf, master_recvbuf, master_expected_len); int slave_expected_len; if (dma_chan) { slave_expected_len = (master_expected_len & (~3)); } else { slave_expected_len = master_expected_len; } if (slave_expected_len) { TEST_ASSERT_EQUAL_HEX8_ARRAY(slave_cmpbuf, slave_recvbuf, slave_expected_len); } } local_test_end(spi); } } } #if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S2, ESP32S3, ESP32C3, ESP32C2) //These tests are ESP32 only due to lack of runners /******************************************************************************** * Test By Master & Slave (2 boards) * * Wiring: * | Master | Slave | * | ------ | ----- | * | 12 | 19 | * | 13 | 23 | * | 14 | 18 | * | 15 | 5 | * | GND | GND | * ********************************************************************************/ static void test_master_init(void **context); static void test_master_deinit(void *context); static void test_master_loop(const void *test_cfg, void *context); static const ptest_func_t master_test_func = { .pre_test = test_master_init, .post_test = test_master_deinit, .loop = test_master_loop, .def_param = spitest_def_param, }; static void test_slave_init(void **context); static void test_slave_deinit(void *context); static void test_slave_loop(const void *test_cfg, void *context); static const ptest_func_t slave_test_func = { .pre_test = test_slave_init, .post_test = test_slave_deinit, .loop = test_slave_loop, .def_param = spitest_def_param, }; //temporarily close mass data print to avoid pytest run too busy to timeout #define TEST_LOG_DBUG false #define TEST_SPI_MASTER_SLAVE(name, param_group, extra_tag) \ PARAM_GROUP_DECLARE(name, param_group) \ TEST_MASTER_SLAVE(name, param_group, "[spi_ms][test_env=generic_multi_device][timeout=120]"extra_tag, &master_test_func, &slave_test_func) /************ Master Code ***********************************************/ static void test_master_init(void **arg) { TEST_ASSERT(*arg == NULL); *arg = malloc(sizeof(spitest_context_t)); spitest_context_t *context = *arg; TEST_ASSERT(context != NULL); context->slave_context = (spi_slave_task_context_t) {}; esp_err_t err = init_slave_context(&context->slave_context, TEST_SPI_HOST); TEST_ASSERT(err == ESP_OK); unity_send_signal("Master ready"); } static void test_master_deinit(void *arg) { spitest_context_t *context = (spitest_context_t *)arg; deinit_slave_context(&context->slave_context); } static void test_master_start(spi_device_handle_t *spi, int freq, const spitest_param_set_t *pset, spitest_context_t *context) { //master config spi_bus_config_t buspset = SPI_BUS_TEST_DEFAULT_CONFIG(); //this does nothing, but avoid the driver from using native pins if (!pset->master_iomux) { buspset.quadhd_io_num = UNCONNECTED_PIN; } spi_device_interface_config_t devpset = SPI_DEVICE_TEST_DEFAULT_CONFIG(); devpset.spics_io_num = SPI2_IOMUX_PIN_NUM_CS; devpset.mode = pset->mode; const int cs_pretrans_max = 15; if (pset->dup == HALF_DUPLEX_MISO) { devpset.cs_ena_pretrans = cs_pretrans_max; devpset.flags |= SPI_DEVICE_HALFDUPLEX; } else if (pset->dup == HALF_DUPLEX_MOSI) { devpset.cs_ena_pretrans = cs_pretrans_max; devpset.flags |= SPI_DEVICE_NO_DUMMY; } else { devpset.cs_ena_pretrans = cs_pretrans_max;//20; } const int cs_posttrans_max = 15; devpset.cs_ena_posttrans = cs_posttrans_max; devpset.input_delay_ns = pset->slave_tv_ns; devpset.clock_speed_hz = freq; if (pset->master_limit != 0 && freq > pset->master_limit) { devpset.flags |= SPI_DEVICE_NO_DUMMY; } int dma_chan = (pset->master_dma_chan == 0) ? 0 : SPI_DMA_CH_AUTO; TEST_ESP_OK(spi_bus_initialize(TEST_SPI_HOST, &buspset, dma_chan)); TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &devpset, spi)); //prepare data for the slave for (int i = 0; i < pset->test_size; i ++) { /* in the single board, the data is send to the slave task, then to the driver. * However, in this test we don't know the data received by the slave. * So we send to the return queue of the slave directly. */ //xQueueSend( slave_context.data_to_send, &slave_txdata[i], portMAX_DELAY ); uint8_t slave_buffer[320 + 8]; int length; if (pset->dup != HALF_DUPLEX_MISO) { length = context->master_trans[i].length; } else { length = context->master_trans[i].rxlength; } uint32_t *ptr = (uint32_t *)slave_buffer; ptr[0] = length; ptr[1] = (uint32_t)context->slave_trans[i].start; if (context->master_trans[i].tx_buffer != NULL) { memcpy(ptr + 2, context->master_trans[i].tx_buffer, (context->master_trans[i].length + 7) / 8); } //Send to return queue directly xRingbufferSend(context->slave_context.data_received, slave_buffer, 8 + (length + 7) / 8, portMAX_DELAY); } memset(context->master_rxbuf, 0x66, sizeof(context->master_rxbuf)); } static void test_master_loop(const void *arg1, void *arg2) { const spitest_param_set_t *test_cfg = (spitest_param_set_t *)arg1; spitest_context_t *context = (spitest_context_t *)arg2; spi_device_handle_t spi; spitest_init_transactions(test_cfg, context); const int *timing_speed_array = test_cfg->freq_list; ESP_LOGI(MASTER_TAG, "****************** %s ***************", test_cfg->pset_name); for (int i = 0; ; i++) { const int freq = timing_speed_array[i]; if (freq == 0) { break; } if (test_cfg->freq_limit && freq > test_cfg->freq_limit) { break; } ESP_LOGI(MASTER_TAG, "==============> %dk", freq / 1000); test_master_start(&spi, freq, test_cfg, context); unity_wait_for_signal("Slave ready"); for (int j = 0; j < test_cfg->test_size; j ++) { //wait for both master and slave end ESP_LOGI(MASTER_TAG, "=> test%d", j); //send master tx data vTaskDelay(20); spi_transaction_t *t = &context->master_trans[j]; TEST_ESP_OK(spi_device_transmit(spi, t)); int len = get_trans_len(test_cfg->dup, t); if (TEST_LOG_DBUG) { spitest_master_print_data(t, len); } size_t rcv_len; slave_rxdata_t *rcv_data = xRingbufferReceive(context->slave_context.data_received, &rcv_len, portMAX_DELAY); if (TEST_LOG_DBUG) { spitest_slave_print_data(rcv_data, false); } //check result bool check_master_data = (test_cfg->dup != HALF_DUPLEX_MOSI && (test_cfg->master_limit == 0 || freq <= test_cfg->master_limit)); const bool check_slave_data = false; const bool check_len = false; if (!check_master_data) { ESP_LOGI(MASTER_TAG, "skip data check due to duplex mode or freq."); } else { TEST_ESP_OK(spitest_check_data(len, t, rcv_data, check_master_data, check_len, check_slave_data)); } //clean vRingbufferReturnItem(context->slave_context.data_received, rcv_data); } master_free_device_bus(spi); } } /************ Slave Code ***********************************************/ static void test_slave_init(void **arg) { TEST_ASSERT(*arg == NULL); *arg = malloc(sizeof(spitest_context_t)); spitest_context_t *context = (spitest_context_t *)*arg; TEST_ASSERT(context != NULL); context->slave_context = (spi_slave_task_context_t) {}; esp_err_t err = init_slave_context(&context->slave_context, TEST_SPI_HOST); TEST_ASSERT(err == ESP_OK); unity_wait_for_signal("Master ready"); xTaskCreate(spitest_slave_task, "spi_slave", 4096, &context->slave_context, 0, &context->handle_slave); } static void test_slave_deinit(void *arg) { spitest_context_t *context = (spitest_context_t *)arg; vTaskDelete(context->handle_slave); context->handle_slave = 0; deinit_slave_context(&context->slave_context); } static void timing_slave_start(int speed, const spitest_param_set_t *pset, spitest_context_t *context) { //slave config spi_bus_config_t slv_buscfg = SPI_BUS_TEST_DEFAULT_CONFIG(); //this does nothing, but avoid the driver from using native pins if (!pset->slave_iomux) { slv_buscfg.quadhd_io_num = UNCONNECTED_PIN; } spi_slave_interface_config_t slvcfg = SPI_SLAVE_TEST_DEFAULT_CONFIG(); slvcfg.spics_io_num = SPI2_IOMUX_PIN_NUM_CS; slvcfg.mode = pset->mode; //Enable pull-ups on SPI lines so we don't detect rogue pulses when no master is connected. slave_pull_up(&slv_buscfg, slvcfg.spics_io_num); int slave_dma_chan = (pset->slave_dma_chan == 0) ? 0 : SPI_DMA_CH_AUTO; TEST_ESP_OK(spi_slave_initialize(TEST_SPI_HOST, &slv_buscfg, &slvcfg, slave_dma_chan)); //prepare data for the master for (int i = 0; i < pset->test_size; i++) { if (pset->dup == FULL_DUPLEX) { memcpy(context->master_trans[i].rx_buffer, context->slave_trans[i].start, (context->master_trans[i].length + 7) / 8); } else if (pset->dup == HALF_DUPLEX_MISO) { memcpy(context->master_trans[i].rx_buffer, context->slave_trans[i].start, (context->master_trans[i].rxlength + 7) / 8); } } } static void test_slave_loop(const void *arg1, void *arg2) { const spitest_param_set_t *pset = (spitest_param_set_t *)arg1; spitest_context_t *context = (spitest_context_t *)arg2; ESP_LOGI(SLAVE_TAG, "****************** %s ***************", pset->pset_name); spitest_init_transactions(pset, context); const int *timing_speed_array = pset->freq_list; for (int i = 0; ; i++) { const int freq = timing_speed_array[i]; if (freq == 0) { break; } if (pset->freq_limit != 0 && freq > pset->freq_limit) { break; } ESP_LOGI(MASTER_TAG, "==============> %dk", timing_speed_array[i] / 1000); //Initialize SPI slave interface timing_slave_start(freq, pset, context); //prepare slave tx data for (int i = 0; i < pset->test_size; i ++) { xQueueSend(context->slave_context.data_to_send, &context->slave_trans[i], portMAX_DELAY); //memcpy(context->master_trans[i].rx_buffer, context->slave_trans[i].start, (context->master_trans[i].length+7)/8); } vTaskDelay(50 / portTICK_PERIOD_MS); unity_send_signal("Slave ready"); for (int i = 0; i < pset->test_size; i ++) { //wait for both master and slave end ESP_LOGI(MASTER_TAG, "===== test%d =====", i); //send master tx data vTaskDelay(20); spi_transaction_t *t = &context->master_trans[i]; int len = get_trans_len(pset->dup, t); if (TEST_LOG_DBUG) { spitest_master_print_data(t, FULL_DUPLEX); } size_t rcv_len; slave_rxdata_t *rcv_data = xRingbufferReceive(context->slave_context.data_received, &rcv_len, portMAX_DELAY); if (TEST_LOG_DBUG) { spitest_slave_print_data(rcv_data, true); } //check result const bool check_master_data = false; bool check_slave_data = (pset->dup != HALF_DUPLEX_MISO); const bool check_len = true; TEST_ESP_OK(spitest_check_data(len, t, rcv_data, check_master_data, check_len, check_slave_data)); //clean vRingbufferReturnItem(context->slave_context.data_received, rcv_data); } TEST_ASSERT(spi_slave_free(TEST_SPI_HOST) == ESP_OK); } } /************ Timing Test ***********************************************/ static spitest_param_set_t timing_conf[] = { { .pset_name = "FULL_DUP, BOTH IOMUX", .freq_limit = ESP_SPI_SLAVE_MAX_FREQ, .master_limit = 16 * 1000 * 1000, .dup = FULL_DUPLEX, .master_iomux = true, .slave_iomux = true, .slave_tv_ns = TV_WITH_ESP_SLAVE, }, { .pset_name = "FULL_DUP, MASTER IOMUX", .freq_limit = ESP_SPI_SLAVE_MAX_FREQ, .master_limit = 11 * 1000 * 1000, .dup = FULL_DUPLEX, .master_iomux = true, .slave_iomux = false, .slave_tv_ns = TV_WITH_ESP_SLAVE_GPIO, }, { .pset_name = "FULL_DUP, SLAVE IOMUX", .freq_limit = ESP_SPI_SLAVE_MAX_FREQ, .master_limit = 11 * 1000 * 1000, .dup = FULL_DUPLEX, .master_iomux = false, .slave_iomux = true, .slave_tv_ns = TV_WITH_ESP_SLAVE, }, { .pset_name = "FULL_DUP, BOTH GPIO", .freq_limit = ESP_SPI_SLAVE_MAX_FREQ, .master_limit = 9 * 1000 * 1000, .dup = FULL_DUPLEX, .master_iomux = false, .slave_iomux = false, .slave_tv_ns = TV_WITH_ESP_SLAVE_GPIO, }, { .pset_name = "MOSI_DUP, BOTH IOMUX", .freq_limit = ESP_SPI_SLAVE_MAX_FREQ, .dup = HALF_DUPLEX_MOSI, .master_iomux = true, .slave_iomux = true, .slave_tv_ns = TV_WITH_ESP_SLAVE, }, { .pset_name = "MOSI_DUP, MASTER IOMUX", .freq_limit = ESP_SPI_SLAVE_MAX_FREQ, .dup = HALF_DUPLEX_MOSI, .master_iomux = true, .slave_iomux = false, .slave_tv_ns = TV_WITH_ESP_SLAVE_GPIO, }, { .pset_name = "MOSI_DUP, SLAVE IOMUX", .freq_limit = ESP_SPI_SLAVE_MAX_FREQ, .dup = HALF_DUPLEX_MOSI, .master_iomux = false, .slave_iomux = true, .slave_tv_ns = TV_WITH_ESP_SLAVE, }, { .pset_name = "MOSI_DUP, BOTH GPIO", .freq_limit = ESP_SPI_SLAVE_MAX_FREQ, .dup = HALF_DUPLEX_MOSI, .master_iomux = false, .slave_iomux = false, .slave_tv_ns = TV_WITH_ESP_SLAVE_GPIO, }, { .pset_name = "MISO_DUP, BOTH IOMUX", .freq_limit = ESP_SPI_SLAVE_MAX_FREQ, .dup = HALF_DUPLEX_MISO, .master_iomux = true, .slave_iomux = true, .slave_tv_ns = TV_WITH_ESP_SLAVE, }, { .pset_name = "MISO_DUP, MASTER IOMUX", .freq_limit = ESP_SPI_SLAVE_MAX_FREQ, .dup = HALF_DUPLEX_MISO, .master_iomux = true, .slave_iomux = false, .slave_tv_ns = TV_WITH_ESP_SLAVE_GPIO, }, { .pset_name = "MISO_DUP, SLAVE IOMUX", .freq_limit = ESP_SPI_SLAVE_MAX_FREQ, .dup = HALF_DUPLEX_MISO, .master_iomux = false, .slave_iomux = true, .slave_tv_ns = TV_WITH_ESP_SLAVE, }, { .pset_name = "MISO_DUP, BOTH GPIO", .freq_limit = ESP_SPI_SLAVE_MAX_FREQ, .dup = HALF_DUPLEX_MISO, .master_iomux = false, .slave_iomux = false, .slave_tv_ns = TV_WITH_ESP_SLAVE_GPIO, }, }; TEST_SPI_MASTER_SLAVE(TIMING, timing_conf, "") /************ Mode Test ***********************************************/ #define FREQ_LIMIT_MODE 16 * 1000 * 1000 //Set to this input delay so that the master will read with delay until 7M #define DELAY_HCLK_UNTIL_7M 12.5*3 static int test_freq_mode_ms[] = { 100 * 1000, 6 * 1000 * 1000, 7 * 1000 * 1000, 8 * 1000 * 1000, //maximum freq MISO stable before next latch edge 9 * 1000 * 1000, //maximum freq MISO stable before next latch edge 10 * 1000 * 1000, 11 * 1000 * 1000, 13 * 1000 * 1000, 16 * 1000 * 1000, 20 * 1000 * 1000, 0, }; static int test_freq_20M_only[] = { 20 * 1000 * 1000, 0, }; spitest_param_set_t mode_conf[] = { //non-DMA tests { .pset_name = "mode 0, no DMA", .freq_list = test_freq_mode_ms, .master_limit = FREQ_LIMIT_MODE, .dup = FULL_DUPLEX, .master_iomux = true, .slave_iomux = true, .slave_tv_ns = TV_WITH_ESP_SLAVE, .mode = 0, }, { .pset_name = "mode 1, no DMA", .freq_list = test_freq_mode_ms, .master_limit = FREQ_LIMIT_MODE, .dup = FULL_DUPLEX, .master_iomux = true, .slave_iomux = true, .slave_tv_ns = TV_WITH_ESP_SLAVE, .mode = 1, }, { .pset_name = "mode 2, no DMA", .freq_list = test_freq_mode_ms, .master_limit = FREQ_LIMIT_MODE, .dup = FULL_DUPLEX, .master_iomux = true, .slave_iomux = true, .slave_tv_ns = TV_WITH_ESP_SLAVE, .mode = 2, }, { .pset_name = "mode 3, no DMA", .freq_list = test_freq_mode_ms, .master_limit = FREQ_LIMIT_MODE, .dup = FULL_DUPLEX, .master_iomux = true, .slave_iomux = true, .slave_tv_ns = TV_WITH_ESP_SLAVE, .mode = 3, }, //the master can only read to 16MHz, use half-duplex mode to read at 20. { .pset_name = "mode 0, no DMA, 20M", .freq_list = test_freq_20M_only, .dup = HALF_DUPLEX_MISO, .master_iomux = true, .slave_iomux = true, .slave_tv_ns = TV_WITH_ESP_SLAVE, .mode = 0, }, { .pset_name = "mode 1, no DMA, 20M", .freq_list = test_freq_20M_only, .dup = HALF_DUPLEX_MISO, .master_iomux = true, .slave_iomux = true, .slave_tv_ns = TV_WITH_ESP_SLAVE, .mode = 1, }, { .pset_name = "mode 2, no DMA, 20M", .freq_list = test_freq_20M_only, .dup = HALF_DUPLEX_MISO, .master_iomux = true, .slave_iomux = true, .slave_tv_ns = TV_WITH_ESP_SLAVE, .mode = 2, }, { .pset_name = "mode 3, no DMA, 20M", .freq_list = test_freq_20M_only, .dup = HALF_DUPLEX_MISO, .master_iomux = true, .slave_iomux = true, .slave_tv_ns = TV_WITH_ESP_SLAVE, .mode = 3, }, //DMA tests { .pset_name = "mode 0, DMA", .freq_list = test_freq_mode_ms, .master_limit = FREQ_LIMIT_MODE, .dup = FULL_DUPLEX, .master_iomux = true, .slave_iomux = true, .slave_tv_ns = DELAY_HCLK_UNTIL_7M, .mode = 0, .master_dma_chan = SPI_DMA_CH_AUTO, .slave_dma_chan = SPI_DMA_CH_AUTO, .length_aligned = true, }, { .pset_name = "mode 1, DMA", .freq_list = test_freq_mode_ms, .master_limit = FREQ_LIMIT_MODE, .dup = FULL_DUPLEX, .master_iomux = true, .slave_iomux = true, .slave_tv_ns = TV_WITH_ESP_SLAVE, .mode = 1, .master_dma_chan = SPI_DMA_CH_AUTO, .slave_dma_chan = SPI_DMA_CH_AUTO, .length_aligned = true, }, { .pset_name = "mode 2, DMA", .freq_list = test_freq_mode_ms, .master_limit = FREQ_LIMIT_MODE, .dup = FULL_DUPLEX, .master_iomux = true, .slave_iomux = true, .slave_tv_ns = DELAY_HCLK_UNTIL_7M, .mode = 2, .master_dma_chan = SPI_DMA_CH_AUTO, .slave_dma_chan = SPI_DMA_CH_AUTO, .length_aligned = true, }, { .pset_name = "mode 3, DMA", .freq_list = test_freq_mode_ms, .master_limit = FREQ_LIMIT_MODE, .dup = FULL_DUPLEX, .master_iomux = true, .slave_iomux = true, .slave_tv_ns = TV_WITH_ESP_SLAVE, .mode = 3, .master_dma_chan = SPI_DMA_CH_AUTO, .slave_dma_chan = SPI_DMA_CH_AUTO, .length_aligned = true, }, //the master can only read to 16MHz, use half-duplex mode to read at 20. { .pset_name = "mode 0, DMA, 20M", .freq_list = test_freq_20M_only, .dup = HALF_DUPLEX_MISO, .master_iomux = true, .slave_iomux = true, .slave_tv_ns = TV_WITH_ESP_SLAVE, .mode = 0, .master_dma_chan = SPI_DMA_CH_AUTO, .slave_dma_chan = SPI_DMA_CH_AUTO, }, { .pset_name = "mode 1, DMA, 20M", .freq_list = test_freq_20M_only, .dup = HALF_DUPLEX_MISO, .master_iomux = true, .slave_iomux = true, .slave_tv_ns = TV_WITH_ESP_SLAVE, .mode = 1, .master_dma_chan = SPI_DMA_CH_AUTO, .slave_dma_chan = SPI_DMA_CH_AUTO, }, { .pset_name = "mode 2, DMA, 20M", .freq_list = test_freq_20M_only, .dup = HALF_DUPLEX_MISO, .master_iomux = true, .slave_iomux = true, .slave_tv_ns = TV_WITH_ESP_SLAVE, .mode = 2, .master_dma_chan = SPI_DMA_CH_AUTO, .slave_dma_chan = SPI_DMA_CH_AUTO, }, { .pset_name = "mode 3, DMA, 20M", .freq_list = test_freq_20M_only, .dup = HALF_DUPLEX_MISO, .master_iomux = true, .slave_iomux = true, .slave_tv_ns = TV_WITH_ESP_SLAVE, .mode = 3, .master_dma_chan = SPI_DMA_CH_AUTO, .slave_dma_chan = SPI_DMA_CH_AUTO, }, }; TEST_SPI_MASTER_SLAVE(MODE, mode_conf, "") #endif // !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S2, ESP32S3, ESP32C3, ESP32C2) #endif // #if (TEST_SPI_PERIPH_NUM >= 2) #define TEST_STEP_LEN 96 #define TEST_STEP 2 static int s_spi_bus_freq[] = { IDF_PERFORMANCE_MAX_SPI_CLK_FREQ / 10, IDF_PERFORMANCE_MAX_SPI_CLK_FREQ / 7, IDF_PERFORMANCE_MAX_SPI_CLK_FREQ / 4, IDF_PERFORMANCE_MAX_SPI_CLK_FREQ / 2, IDF_PERFORMANCE_MAX_SPI_CLK_FREQ, }; //------------------------------------------- Full Duplex with DMA Freq test -------------------------------------- static void test_master_fd_dma(void) { spi_device_handle_t dev0; uint8_t *master_send = heap_caps_malloc(TEST_STEP_LEN, MALLOC_CAP_DMA); uint8_t *master_recive = heap_caps_malloc(TEST_STEP_LEN, MALLOC_CAP_DMA); uint8_t *master_expect = heap_caps_malloc(TEST_STEP_LEN, MALLOC_CAP_DEFAULT); for (uint8_t is_gpio = 0; is_gpio < 2; is_gpio++) { for (uint8_t mode = 0; mode < 4; mode++) { spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG(); if (is_gpio) { buscfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS; } TEST_ESP_OK(spi_bus_initialize(TEST_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO)); for (uint8_t speed_level = 0; speed_level < sizeof(s_spi_bus_freq) / sizeof(int); speed_level++) { spi_device_interface_config_t devcfg = { .mode = mode, .spics_io_num = PIN_NUM_CS, .queue_size = 16, .clock_speed_hz = s_spi_bus_freq[speed_level], }; #if CONFIG_IDF_TARGET_ESP32 if (is_gpio && (s_spi_bus_freq[speed_level] >= 10 * 1000 * 1000)) { continue; //On esp32 with GPIO Matrix, clk freq <= 10MHz } devcfg.cs_ena_pretrans = 2; devcfg.input_delay_ns = 12.5 * 2; #endif TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &devcfg, &dev0)); printf("Next trans: %s\tmode:%d\t@%.2f MHz\n", (is_gpio) ? "GPIO_Matrix" : "IOMUX", mode, s_spi_bus_freq[speed_level] / 1000000.f); unity_send_signal("Master ready"); for (int i = 0; i < TEST_STEP; i++) { memset(master_recive, 0x00, TEST_STEP_LEN); test_fill_random_to_buffers_dualboard(119 + mode + speed_level + i, master_send, master_expect, TEST_STEP_LEN); uint32_t test_trans_len = TEST_STEP_LEN; spi_transaction_t trans_cfg = { .tx_buffer = master_send, .rx_buffer = master_recive, .length = test_trans_len * 8, }; unity_wait_for_signal("Slave ready"); TEST_ESP_OK(spi_device_transmit(dev0, &trans_cfg)); ESP_LOG_BUFFER_HEX("master tx", master_send, test_trans_len); ESP_LOG_BUFFER_HEX_LEVEL("master rx", master_recive, test_trans_len, ESP_LOG_DEBUG); ESP_LOG_BUFFER_HEX_LEVEL("master exp", master_expect, test_trans_len, ESP_LOG_DEBUG); spitest_cmp_or_dump(master_expect, master_recive, test_trans_len); } TEST_ESP_OK(spi_bus_remove_device(dev0)); } TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST)); } } free(master_send); free(master_recive); free(master_expect); } static void test_slave_fd_dma(void) { uint8_t *slave_send = heap_caps_malloc(TEST_STEP_LEN, MALLOC_CAP_DMA); uint8_t *slave_recive = heap_caps_malloc(TEST_STEP_LEN, MALLOC_CAP_DMA); uint8_t *slave_expect = heap_caps_malloc(TEST_STEP_LEN, MALLOC_CAP_DEFAULT); for (uint8_t is_gpio = 0; is_gpio < 2; is_gpio++) { spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG(); if (is_gpio) { buscfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS; } for (uint8_t mode = 0; mode < 4; mode++) { spi_slave_interface_config_t slvcfg = SPI_SLAVE_TEST_DEFAULT_CONFIG(); slvcfg.mode = mode; TEST_ESP_OK(spi_slave_initialize(TEST_SPI_HOST, &buscfg, &slvcfg, SPI_DMA_CH_AUTO)); for (uint8_t speed_level = 0; speed_level < sizeof(s_spi_bus_freq) / sizeof(int); speed_level++) { #if CONFIG_IDF_TARGET_ESP32 if (is_gpio && (s_spi_bus_freq[speed_level] >= 10 * 1000 * 1000)) { continue; //On esp32 with GPIO Matrix, clk freq <= 10MHz } #endif printf("Next trans: %s\tmode:%d\t@%.2f MHz\n", (is_gpio) ? "GPIO_Matrix" : "IOMUX", mode, s_spi_bus_freq[speed_level] / 1000000.f); unity_wait_for_signal("Master ready"); for (int i = 0; i < TEST_STEP; i++) { memset(slave_recive, 0x00, TEST_STEP_LEN); test_fill_random_to_buffers_dualboard(119 + mode + speed_level + i, slave_expect, slave_send, TEST_STEP_LEN); uint32_t test_trans_len = TEST_STEP_LEN; spi_slave_transaction_t trans_cfg = { .tx_buffer = slave_send, .rx_buffer = slave_recive, .length = test_trans_len * 8, .flags = SPI_SLAVE_TRANS_DMA_BUFFER_ALIGN_AUTO, }; unity_send_signal("Slave ready"); TEST_ESP_OK(spi_slave_transmit(TEST_SPI_HOST, &trans_cfg, portMAX_DELAY)); ESP_LOG_BUFFER_HEX("slave tx", slave_send, test_trans_len); ESP_LOG_BUFFER_HEX_LEVEL("slave rx", slave_recive, test_trans_len, ESP_LOG_DEBUG); ESP_LOG_BUFFER_HEX_LEVEL("slave exp", slave_expect, test_trans_len, ESP_LOG_DEBUG); spitest_cmp_or_dump(slave_expect, slave_recive, test_trans_len); } } TEST_ESP_OK(spi_slave_free(TEST_SPI_HOST)); } } free(slave_send); free(slave_recive); free(slave_expect); } TEST_CASE_MULTIPLE_DEVICES("TEST_SPI_Freq_FD_DMA", "[spi_ms][timeout=30]", test_master_fd_dma, test_slave_fd_dma); //------------------------------------------- Full Duplex no DMA Freq test -------------------------------------- static void test_master_fd_no_dma(void) { spi_device_handle_t dev0; uint8_t *master_send = heap_caps_malloc(SOC_SPI_MAXIMUM_BUFFER_SIZE, MALLOC_CAP_DMA); uint8_t *master_recive = heap_caps_malloc(SOC_SPI_MAXIMUM_BUFFER_SIZE, MALLOC_CAP_DMA); uint8_t *master_expect = heap_caps_malloc(SOC_SPI_MAXIMUM_BUFFER_SIZE, MALLOC_CAP_DEFAULT); for (uint8_t is_gpio = 0; is_gpio < 2; is_gpio++) { for (uint8_t mode = 0; mode < 4; mode++) { spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG(); if (is_gpio) { buscfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS; } TEST_ESP_OK(spi_bus_initialize(TEST_SPI_HOST, &buscfg, SPI_DMA_DISABLED)); for (uint8_t speed_level = 0; speed_level < sizeof(s_spi_bus_freq) / sizeof(int); speed_level++) { spi_device_interface_config_t devcfg = { .mode = mode, .spics_io_num = PIN_NUM_CS, .queue_size = 16, .clock_speed_hz = s_spi_bus_freq[speed_level], }; #if CONFIG_IDF_TARGET_ESP32 if (is_gpio && (s_spi_bus_freq[speed_level] >= 10 * 1000 * 1000)) { continue; //On esp32 with GPIO Matrix, clk freq <= 10MHz } devcfg.cs_ena_pretrans = 2, devcfg.input_delay_ns = 12.5 * 2, #endif TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &devcfg, &dev0)); printf("Next trans: %s\tmode:%d\t@%.2f MHz\n", (is_gpio) ? "GPIO_Matrix" : "IOMUX", mode, s_spi_bus_freq[speed_level] / 1000000.f); unity_send_signal("Master ready"); for (int i = 0; i < TEST_STEP; i++) { memset(master_recive, 0x00, SOC_SPI_MAXIMUM_BUFFER_SIZE); test_fill_random_to_buffers_dualboard(211 + mode + speed_level + i, master_send, master_expect, SOC_SPI_MAXIMUM_BUFFER_SIZE); uint32_t test_trans_len = SOC_SPI_MAXIMUM_BUFFER_SIZE; spi_transaction_t trans_cfg = { .tx_buffer = master_send, .rx_buffer = master_recive, .length = test_trans_len * 8, }; unity_wait_for_signal("Slave ready"); TEST_ESP_OK(spi_device_transmit(dev0, &trans_cfg)); ESP_LOG_BUFFER_HEX("master tx", master_send, test_trans_len); ESP_LOG_BUFFER_HEX_LEVEL("master rx", master_recive, test_trans_len, ESP_LOG_DEBUG); ESP_LOG_BUFFER_HEX_LEVEL("master exp", master_expect, test_trans_len, ESP_LOG_DEBUG); spitest_cmp_or_dump(master_expect, master_recive, test_trans_len); } TEST_ESP_OK(spi_bus_remove_device(dev0)); } TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST)); } } free(master_send); free(master_recive); free(master_expect); } static void test_slave_fd_no_dma(void) { uint8_t *slave_send = heap_caps_malloc(SOC_SPI_MAXIMUM_BUFFER_SIZE, MALLOC_CAP_DMA); uint8_t *slave_recive = heap_caps_malloc(SOC_SPI_MAXIMUM_BUFFER_SIZE, MALLOC_CAP_DMA); uint8_t *slave_expect = heap_caps_malloc(SOC_SPI_MAXIMUM_BUFFER_SIZE, MALLOC_CAP_DEFAULT); for (uint8_t is_gpio = 0; is_gpio < 2; is_gpio++) { spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG(); if (is_gpio) { buscfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS; } for (uint8_t mode = 0; mode < 4; mode++) { spi_slave_interface_config_t slvcfg = SPI_SLAVE_TEST_DEFAULT_CONFIG(); slvcfg.mode = mode; TEST_ESP_OK(spi_slave_initialize(TEST_SPI_HOST, &buscfg, &slvcfg, SPI_DMA_DISABLED)); for (uint8_t speed_level = 0; speed_level < sizeof(s_spi_bus_freq) / sizeof(int); speed_level++) { #if CONFIG_IDF_TARGET_ESP32 if (is_gpio && (s_spi_bus_freq[speed_level] >= 10 * 1000 * 1000)) { continue; //On esp32 with GPIO Matrix, clk freq <= 10MHz } #endif printf("Next trans: %s\tmode:%d\t@%.2f MHz\n", (is_gpio) ? "GPIO_Matrix" : "IOMUX", mode, s_spi_bus_freq[speed_level] / 1000000.f); unity_wait_for_signal("Master ready"); for (int i = 0; i < TEST_STEP; i++) { memset(slave_recive, 0x00, SOC_SPI_MAXIMUM_BUFFER_SIZE); test_fill_random_to_buffers_dualboard(211 + mode + speed_level + i, slave_expect, slave_send, SOC_SPI_MAXIMUM_BUFFER_SIZE); uint32_t test_trans_len = SOC_SPI_MAXIMUM_BUFFER_SIZE; spi_slave_transaction_t trans_cfg = { .tx_buffer = slave_send, .rx_buffer = slave_recive, .length = test_trans_len * 8, }; unity_send_signal("Slave ready"); TEST_ESP_OK(spi_slave_transmit(TEST_SPI_HOST, &trans_cfg, portMAX_DELAY)); ESP_LOG_BUFFER_HEX("slave tx", slave_send, test_trans_len); ESP_LOG_BUFFER_HEX_LEVEL("slave rx", slave_recive, test_trans_len, ESP_LOG_DEBUG); ESP_LOG_BUFFER_HEX_LEVEL("slave exp", slave_expect, test_trans_len, ESP_LOG_DEBUG); spitest_cmp_or_dump(slave_expect, slave_recive, test_trans_len); } } TEST_ESP_OK(spi_slave_free(TEST_SPI_HOST)); } } free(slave_send); free(slave_recive); free(slave_expect); } TEST_CASE_MULTIPLE_DEVICES("TEST_SPI_Freq_FD_no_DMA", "[spi_ms][timeout=30]", test_master_fd_no_dma, test_slave_fd_no_dma); #if SOC_SPI_SUPPORT_SLAVE_HD_VER2 //------------------------------------------- Half Duplex with DMA Freq test -------------------------------------- static void test_master_hd_dma(void) { spi_device_handle_t dev0; uint8_t *master_send = heap_caps_malloc(TEST_STEP_LEN, MALLOC_CAP_DMA); uint8_t *master_recive = heap_caps_malloc(TEST_STEP_LEN, MALLOC_CAP_DMA); uint8_t *master_expect = heap_caps_malloc(TEST_STEP_LEN, MALLOC_CAP_DEFAULT); for (uint8_t is_gpio = 0; is_gpio < 2; is_gpio++) { for (uint8_t mode = 0; mode < 4; mode++) { spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG(); if (is_gpio) { buscfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS; } TEST_ESP_OK(spi_bus_initialize(TEST_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO)); for (uint8_t speed_level = 0; speed_level < sizeof(s_spi_bus_freq) / sizeof(int); speed_level++) { spi_device_interface_config_t devcfg = SPI_SLOT_TEST_DEFAULT_CONFIG(); devcfg.mode = mode; devcfg.flags = SPI_DEVICE_HALFDUPLEX; devcfg.clock_speed_hz = s_spi_bus_freq[speed_level]; TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &devcfg, &dev0)); printf("Next trans: %s\tmode:%d\t@%.2f MHz\n", (is_gpio) ? "GPIO_Matrix" : "IOMUX", mode, s_spi_bus_freq[speed_level] / 1000000.f); unity_send_signal("Master ready"); for (int i = 0; i < TEST_STEP; i++) { memset(master_recive, 0x00, TEST_STEP_LEN); test_fill_random_to_buffers_dualboard(985 + mode + speed_level + i, master_send, master_expect, TEST_STEP_LEN); uint32_t test_trans_len = TEST_STEP_LEN; unity_wait_for_signal("Slave ready"); TEST_ESP_OK(essl_spi_rddma(dev0, master_recive, test_trans_len, -1, 0)); TEST_ESP_OK(essl_spi_wrdma(dev0, master_send, test_trans_len, -1, 0)); ESP_LOG_BUFFER_HEX("master tx", master_send, test_trans_len); ESP_LOG_BUFFER_HEX_LEVEL("master rx", master_recive, test_trans_len, ESP_LOG_DEBUG); ESP_LOG_BUFFER_HEX_LEVEL("master exp", master_expect, test_trans_len, ESP_LOG_DEBUG); spitest_cmp_or_dump(master_expect, master_recive, test_trans_len); } TEST_ESP_OK(spi_bus_remove_device(dev0)); } TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST)); } } free(master_send); free(master_recive); free(master_expect); } static void test_slave_hd_dma(void) { uint8_t *slave_send = heap_caps_malloc(TEST_STEP_LEN, MALLOC_CAP_DMA); uint8_t *slave_recive = heap_caps_malloc(TEST_STEP_LEN, MALLOC_CAP_DMA); uint8_t *slave_expect = heap_caps_malloc(TEST_STEP_LEN, MALLOC_CAP_DEFAULT); for (uint8_t is_gpio = 0; is_gpio < 2; is_gpio++) { spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG(); if (is_gpio) { buscfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS; } for (uint8_t mode = 0; mode < 4; mode++) { spi_slave_hd_slot_config_t hd_slvcfg = SPI_SLOT_TEST_DEFAULT_CONFIG(); hd_slvcfg.mode = mode; hd_slvcfg.dma_chan = SPI_DMA_CH_AUTO; TEST_ESP_OK(spi_slave_hd_init(TEST_SPI_HOST, &buscfg, &hd_slvcfg)); for (uint8_t speed_level = 0; speed_level < sizeof(s_spi_bus_freq) / sizeof(int); speed_level++) { printf("Next trans: %s\tmode:%d\t@%.2f MHz\n", (is_gpio) ? "GPIO_Matrix" : "IOMUX", mode, s_spi_bus_freq[speed_level] / 1000000.f); unity_wait_for_signal("Master ready"); for (int i = 0; i < TEST_STEP; i++) { memset(slave_recive, 0x00, TEST_STEP_LEN); test_fill_random_to_buffers_dualboard(985 + mode + speed_level + i, slave_expect, slave_send, TEST_STEP_LEN); uint32_t test_trans_len = TEST_STEP_LEN; spi_slave_hd_data_t *ret_trans, slave_trans = { .data = slave_send, .len = test_trans_len, .flags = SPI_SLAVE_HD_TRANS_DMA_BUFFER_ALIGN_AUTO, }; unity_send_signal("Slave ready"); TEST_ESP_OK(spi_slave_hd_queue_trans(TEST_SPI_HOST, SPI_SLAVE_CHAN_TX, &slave_trans, portMAX_DELAY)); slave_trans.data = slave_recive; TEST_ESP_OK(spi_slave_hd_queue_trans(TEST_SPI_HOST, SPI_SLAVE_CHAN_RX, &slave_trans, portMAX_DELAY)); TEST_ESP_OK(spi_slave_hd_get_trans_res(TEST_SPI_HOST, SPI_SLAVE_CHAN_RX, &ret_trans, portMAX_DELAY)); ESP_LOG_BUFFER_HEX("slave tx", slave_send, test_trans_len); ESP_LOG_BUFFER_HEX_LEVEL("slave rx", slave_recive, test_trans_len, ESP_LOG_DEBUG); ESP_LOG_BUFFER_HEX_LEVEL("slave exp", slave_expect, test_trans_len, ESP_LOG_DEBUG); spitest_cmp_or_dump(slave_expect, slave_recive, test_trans_len); } } TEST_ESP_OK(spi_slave_hd_deinit(TEST_SPI_HOST)); } } free(slave_send); free(slave_recive); free(slave_expect); } TEST_CASE_MULTIPLE_DEVICES("TEST_SPI_Freq_HD_DMA", "[spi_ms][timeout=30]", test_master_hd_dma, test_slave_hd_dma); //------------------------------------------- Half Duplex no DMA Freq test -------------------------------------- static void test_master_hd_no_dma(void) { spi_device_handle_t dev0; uint8_t *master_send = heap_caps_malloc(SOC_SPI_MAXIMUM_BUFFER_SIZE, MALLOC_CAP_DMA); uint8_t *master_recive = heap_caps_malloc(SOC_SPI_MAXIMUM_BUFFER_SIZE, MALLOC_CAP_DMA); uint8_t *master_expect = heap_caps_malloc(SOC_SPI_MAXIMUM_BUFFER_SIZE, MALLOC_CAP_DEFAULT); for (uint8_t is_gpio = 0; is_gpio < 2; is_gpio++) { for (uint8_t mode = 0; mode < 4; mode++) { spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG(); if (is_gpio) { buscfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS; } TEST_ESP_OK(spi_bus_initialize(TEST_SPI_HOST, &buscfg, SPI_DMA_DISABLED)); for (uint8_t speed_level = 0; speed_level < sizeof(s_spi_bus_freq) / sizeof(int); speed_level++) { spi_device_interface_config_t devcfg = SPI_SLOT_TEST_DEFAULT_CONFIG(); devcfg.mode = mode; devcfg.flags = SPI_DEVICE_HALFDUPLEX; devcfg.clock_speed_hz = s_spi_bus_freq[speed_level]; TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &devcfg, &dev0)); printf("Next trans: %s\tmode:%d\t@%.2f MHz\n", (is_gpio) ? "GPIO_Matrix" : "IOMUX", mode, s_spi_bus_freq[speed_level] / 1000000.f); unity_send_signal("Master ready"); for (int i = 0; i < TEST_STEP; i++) { memset(master_recive, 0x00, SOC_SPI_MAXIMUM_BUFFER_SIZE); test_fill_random_to_buffers_dualboard(911 + mode + speed_level + i, master_send, master_expect, SOC_SPI_MAXIMUM_BUFFER_SIZE); uint32_t test_trans_len = SOC_SPI_MAXIMUM_BUFFER_SIZE; unity_wait_for_signal("Slave ready"); TEST_ESP_OK(essl_spi_rddma(dev0, master_recive, test_trans_len, -1, 0)); TEST_ESP_OK(essl_spi_wrdma(dev0, master_send, test_trans_len, -1, 0)); ESP_LOG_BUFFER_HEX("master tx", master_send, test_trans_len); ESP_LOG_BUFFER_HEX_LEVEL("master rx", master_recive, test_trans_len, ESP_LOG_DEBUG); ESP_LOG_BUFFER_HEX_LEVEL("master exp", master_expect, test_trans_len, ESP_LOG_DEBUG); spitest_cmp_or_dump(master_expect, master_recive, test_trans_len); } TEST_ESP_OK(spi_bus_remove_device(dev0)); } TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST)); } } free(master_send); free(master_recive); free(master_expect); } static void test_slave_hd_no_dma(void) { uint8_t *slave_send = heap_caps_malloc(SOC_SPI_MAXIMUM_BUFFER_SIZE, MALLOC_CAP_DMA); uint8_t *slave_recive = heap_caps_malloc(SOC_SPI_MAXIMUM_BUFFER_SIZE, MALLOC_CAP_DMA); uint8_t *slave_expect = heap_caps_malloc(SOC_SPI_MAXIMUM_BUFFER_SIZE, MALLOC_CAP_DEFAULT); for (uint8_t is_gpio = 0; is_gpio < 2; is_gpio++) { spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG(); if (is_gpio) { buscfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS; } for (uint8_t mode = 0; mode < 4; mode++) { spi_slave_hd_slot_config_t hd_slvcfg = SPI_SLOT_TEST_DEFAULT_CONFIG(); hd_slvcfg.mode = mode; hd_slvcfg.dma_chan = SPI_DMA_CH_AUTO; //slave hd use dma mandatory, test no dma on master TEST_ESP_OK(spi_slave_hd_init(TEST_SPI_HOST, &buscfg, &hd_slvcfg)); for (uint8_t speed_level = 0; speed_level < sizeof(s_spi_bus_freq) / sizeof(int); speed_level++) { printf("Next trans: %s\tmode:%d\t@%.2f MHz\n", (is_gpio) ? "GPIO_Matrix" : "IOMUX", mode, s_spi_bus_freq[speed_level] / 1000000.f); unity_wait_for_signal("Master ready"); for (int i = 0; i < TEST_STEP; i++) { memset(slave_recive, 0x00, SOC_SPI_MAXIMUM_BUFFER_SIZE); test_fill_random_to_buffers_dualboard(911 + mode + speed_level + i, slave_expect, slave_send, SOC_SPI_MAXIMUM_BUFFER_SIZE); uint32_t test_trans_len = SOC_SPI_MAXIMUM_BUFFER_SIZE; spi_slave_hd_data_t *ret_trans, slave_trans = { .data = slave_send, .len = test_trans_len, .flags = SPI_SLAVE_HD_TRANS_DMA_BUFFER_ALIGN_AUTO, }; unity_send_signal("Slave ready"); TEST_ESP_OK(spi_slave_hd_queue_trans(TEST_SPI_HOST, SPI_SLAVE_CHAN_TX, &slave_trans, portMAX_DELAY)); slave_trans.data = slave_recive; TEST_ESP_OK(spi_slave_hd_queue_trans(TEST_SPI_HOST, SPI_SLAVE_CHAN_RX, &slave_trans, portMAX_DELAY)); TEST_ESP_OK(spi_slave_hd_get_trans_res(TEST_SPI_HOST, SPI_SLAVE_CHAN_RX, &ret_trans, portMAX_DELAY)); ESP_LOG_BUFFER_HEX("slave tx", slave_send, test_trans_len); ESP_LOG_BUFFER_HEX_LEVEL("slave rx", slave_recive, test_trans_len, ESP_LOG_DEBUG); ESP_LOG_BUFFER_HEX_LEVEL("slave exp", slave_expect, test_trans_len, ESP_LOG_DEBUG); spitest_cmp_or_dump(slave_expect, slave_recive, test_trans_len); } } TEST_ESP_OK(spi_slave_hd_deinit(TEST_SPI_HOST)); } } free(slave_send); free(slave_recive); free(slave_expect); } TEST_CASE_MULTIPLE_DEVICES("TEST_SPI_Freq_HD_no_DMA", "[spi_ms][timeout=30]", test_master_hd_no_dma, test_slave_hd_no_dma); #endif // SOC_SPI_SUPPORT_SLAVE_HD_VER2 #if CONFIG_IDF_TARGET_ESP32 // item num should same as `s_spi_bus_freq` static int s_master_input_delay[] = {12.5, 12.5 * 2, 12.5 * 2, 12.5 * 5, 12.5 * 5}; #endif //------------------------------------------- SIO with DMA Freq test -------------------------------------- static void test_master_sio_dma(void) { spi_device_handle_t dev0; uint8_t *master_send = heap_caps_malloc(TEST_STEP_LEN, MALLOC_CAP_DMA); uint8_t *master_recive = heap_caps_malloc(TEST_STEP_LEN, MALLOC_CAP_DMA); uint8_t *master_expect = heap_caps_malloc(TEST_STEP_LEN, MALLOC_CAP_DEFAULT); for (uint8_t sio_master_in = 0; sio_master_in < 2; sio_master_in++) { for (uint8_t mode = 0; mode < 4; mode++) { spi_bus_config_t buscfg = 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. buscfg.mosi_io_num = buscfg.miso_io_num; printf("\n========================Test sio master input==========================\n"); } else { printf("\n============Test sio master output, data checked by slave.=============\n"); } buscfg.miso_io_num = -1; TEST_ESP_OK(spi_bus_initialize(TEST_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO)); for (uint8_t speed_level = 0; speed_level < sizeof(s_spi_bus_freq) / sizeof(int); speed_level++) { spi_device_interface_config_t devcfg = { .mode = mode, .spics_io_num = PIN_NUM_CS, .queue_size = 16, .clock_speed_hz = s_spi_bus_freq[speed_level], .flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_3WIRE, }; #if CONFIG_IDF_TARGET_ESP32 devcfg.cs_ena_pretrans = 2; devcfg.input_delay_ns = s_master_input_delay[speed_level]; #endif TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &devcfg, &dev0)); printf("Next trans: %s\tmode:%d\t@%.2f MHz\n", (sio_master_in) ? "SingleIn" : "SongleOut", mode, s_spi_bus_freq[speed_level] / 1000000.f); unity_send_signal("Master ready"); for (int i = 0; i < TEST_STEP; i++) { memset(master_recive, 0x00, TEST_STEP_LEN); test_fill_random_to_buffers_dualboard(110 + mode + speed_level + i, master_send, master_expect, TEST_STEP_LEN); spi_transaction_t trans = {}; if (sio_master_in) { // master input only trans.rxlength = TEST_STEP_LEN * 8; trans.rx_buffer = master_recive; trans.length = 0; trans.tx_buffer = NULL; } else { // master output only trans.length = TEST_STEP_LEN * 8; trans.tx_buffer = master_send; trans.rxlength = 0; trans.rx_buffer = NULL; } unity_wait_for_signal("Slave ready"); TEST_ESP_OK(spi_device_transmit(dev0, &trans)); if (sio_master_in) { ESP_LOG_BUFFER_HEX_LEVEL("master rx", trans.rx_buffer, TEST_STEP_LEN, ESP_LOG_DEBUG); ESP_LOG_BUFFER_HEX_LEVEL("master exp", master_expect, TEST_STEP_LEN, ESP_LOG_DEBUG); spitest_cmp_or_dump(master_expect, master_recive, TEST_STEP_LEN); } else { ESP_LOG_BUFFER_HEX("master tx", trans.tx_buffer, TEST_STEP_LEN); } } TEST_ESP_OK(spi_bus_remove_device(dev0)); } TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST)); } } free(master_send); free(master_recive); free(master_expect); } static void test_slave_sio_dma(void) { uint8_t *slave_send = heap_caps_malloc(TEST_STEP_LEN, MALLOC_CAP_DMA); uint8_t *slave_recive = heap_caps_malloc(TEST_STEP_LEN, MALLOC_CAP_DMA); uint8_t *slave_expect = heap_caps_malloc(TEST_STEP_LEN, MALLOC_CAP_DEFAULT); for (uint8_t sio_master_in = 0; sio_master_in < 2; sio_master_in++) { if (sio_master_in) { printf("\n======================Slave Tx only====================\n"); } else { printf("\n==================Slave Rx, Check data=================\n"); } for (uint8_t mode = 0; mode < 4; mode++) { spi_bus_config_t bus_cfg = SPI_BUS_TEST_DEFAULT_CONFIG(); #if !CONFIG_IDF_TARGET_ESP32 bus_cfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS; #endif spi_slave_interface_config_t slv_cfg = SPI_SLAVE_TEST_DEFAULT_CONFIG(); slv_cfg.mode = mode; TEST_ESP_OK(spi_slave_initialize(TEST_SPI_HOST, &bus_cfg, &slv_cfg, SPI_DMA_CH_AUTO)); for (uint8_t speed_level = 0; speed_level < sizeof(s_spi_bus_freq) / sizeof(int); speed_level++) { printf("Next trans: %s\tmode:%d\t@%.2f MHz\n", (sio_master_in) ? "SingleIn" : "SongleOut", mode, s_spi_bus_freq[speed_level] / 1000000.f); unity_wait_for_signal("Master ready"); for (int i = 0; i < TEST_STEP; i++) { memset(slave_recive, 0x00, TEST_STEP_LEN); test_fill_random_to_buffers_dualboard(110 + mode + speed_level + i, slave_expect, slave_send, TEST_STEP_LEN); spi_slave_transaction_t trans = { .length = TEST_STEP_LEN * 8, .tx_buffer = slave_send, .rx_buffer = slave_recive, .flags = SPI_SLAVE_TRANS_DMA_BUFFER_ALIGN_AUTO, }; unity_send_signal("Slave ready"); TEST_ESP_OK(spi_slave_transmit(TEST_SPI_HOST, &trans, portMAX_DELAY)); if (sio_master_in) { ESP_LOG_BUFFER_HEX("Slave tx", trans.tx_buffer, TEST_STEP_LEN); } else { ESP_LOG_BUFFER_HEX_LEVEL("Slave rx", trans.rx_buffer, TEST_STEP_LEN, ESP_LOG_DEBUG); ESP_LOG_BUFFER_HEX_LEVEL("Slave exp", slave_expect, TEST_STEP_LEN, ESP_LOG_DEBUG); spitest_cmp_or_dump(slave_expect, slave_recive, TEST_STEP_LEN); } } } TEST_ESP_OK(spi_slave_free(TEST_SPI_HOST)); } } free(slave_send); free(slave_recive); free(slave_expect); } TEST_CASE_MULTIPLE_DEVICES("TEST_SPI_Freq_SIO_DMA", "[spi_ms][timeout=30]", test_master_sio_dma, test_slave_sio_dma); //------------------------------------------- SIO no DMA Freq test -------------------------------------- static void test_master_sio_no_dma(void) { spi_device_handle_t dev0; uint8_t *master_send = heap_caps_malloc(SOC_SPI_MAXIMUM_BUFFER_SIZE, MALLOC_CAP_DMA); uint8_t *master_recive = heap_caps_malloc(SOC_SPI_MAXIMUM_BUFFER_SIZE, MALLOC_CAP_DMA); uint8_t *master_expect = heap_caps_malloc(SOC_SPI_MAXIMUM_BUFFER_SIZE, MALLOC_CAP_DEFAULT); for (uint8_t sio_master_in = 0; sio_master_in < 2; sio_master_in++) { for (uint8_t mode = 0; mode < 4; mode++) { spi_bus_config_t buscfg = 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. buscfg.mosi_io_num = buscfg.miso_io_num; printf("\n========================Test sio master input==========================\n"); } else { printf("\n============Test sio master output, data checked by slave.=============\n"); } buscfg.miso_io_num = -1; TEST_ESP_OK(spi_bus_initialize(TEST_SPI_HOST, &buscfg, SPI_DMA_DISABLED)); for (uint8_t speed_level = 0; speed_level < sizeof(s_spi_bus_freq) / sizeof(int); speed_level++) { spi_device_interface_config_t devcfg = { .mode = mode, .spics_io_num = PIN_NUM_CS, .queue_size = 16, .cs_ena_pretrans = 2, .clock_speed_hz = s_spi_bus_freq[speed_level], .flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_3WIRE, }; #if CONFIG_IDF_TARGET_ESP32 devcfg.cs_ena_pretrans = 2; devcfg.input_delay_ns = s_master_input_delay[speed_level]; #endif TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &devcfg, &dev0)); printf("Next trans: %s\tmode:%d\t@%.2f MHz\n", (sio_master_in) ? "SingleIn" : "SongleOut", mode, s_spi_bus_freq[speed_level] / 1000000.f); unity_send_signal("Master ready"); for (int i = 0; i < TEST_STEP; i++) { memset(master_recive, 0x00, SOC_SPI_MAXIMUM_BUFFER_SIZE); test_fill_random_to_buffers_dualboard(122 + mode + speed_level + i, master_send, master_expect, SOC_SPI_MAXIMUM_BUFFER_SIZE); spi_transaction_t trans = {}; if (sio_master_in) { // master input only trans.rxlength = SOC_SPI_MAXIMUM_BUFFER_SIZE * 8; trans.rx_buffer = master_recive; trans.length = 0; trans.tx_buffer = NULL; } else { // master output only trans.length = SOC_SPI_MAXIMUM_BUFFER_SIZE * 8; trans.tx_buffer = master_send; trans.rxlength = 0; trans.rx_buffer = NULL; } unity_wait_for_signal("Slave ready"); TEST_ESP_OK(spi_device_transmit(dev0, &trans)); if (sio_master_in) { ESP_LOG_BUFFER_HEX_LEVEL("master rx", trans.rx_buffer, SOC_SPI_MAXIMUM_BUFFER_SIZE, ESP_LOG_DEBUG); ESP_LOG_BUFFER_HEX_LEVEL("master exp", master_expect, SOC_SPI_MAXIMUM_BUFFER_SIZE, ESP_LOG_DEBUG); spitest_cmp_or_dump(master_expect, master_recive, SOC_SPI_MAXIMUM_BUFFER_SIZE); } else { ESP_LOG_BUFFER_HEX("master tx", trans.tx_buffer, SOC_SPI_MAXIMUM_BUFFER_SIZE); } } TEST_ESP_OK(spi_bus_remove_device(dev0)); } TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST)); } } free(master_send); free(master_recive); free(master_expect); } static void test_slave_sio_no_dma(void) { uint8_t *slave_send = heap_caps_malloc(SOC_SPI_MAXIMUM_BUFFER_SIZE, MALLOC_CAP_DMA); uint8_t *slave_recive = heap_caps_malloc(SOC_SPI_MAXIMUM_BUFFER_SIZE, MALLOC_CAP_DMA); uint8_t *slave_expect = heap_caps_malloc(SOC_SPI_MAXIMUM_BUFFER_SIZE, MALLOC_CAP_DEFAULT); for (uint8_t sio_master_in = 0; sio_master_in < 2; sio_master_in++) { if (sio_master_in) { printf("\n======================Slave Tx only====================\n"); } else { printf("\n==================Slave Rx, Check data=================\n"); } for (uint8_t mode = 0; mode < 4; mode++) { spi_bus_config_t bus_cfg = SPI_BUS_TEST_DEFAULT_CONFIG(); #if !CONFIG_IDF_TARGET_ESP32 bus_cfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS; #endif spi_slave_interface_config_t slv_cfg = SPI_SLAVE_TEST_DEFAULT_CONFIG(); slv_cfg.mode = mode; TEST_ESP_OK(spi_slave_initialize(TEST_SPI_HOST, &bus_cfg, &slv_cfg, SPI_DMA_DISABLED)); for (uint8_t speed_level = 0; speed_level < sizeof(s_spi_bus_freq) / sizeof(int); speed_level++) { printf("Next trans: %s\tmode:%d\t@%.2f MHz\n", (sio_master_in) ? "SingleIn" : "SongleOut", mode, s_spi_bus_freq[speed_level] / 1000000.f); unity_wait_for_signal("Master ready"); for (int i = 0; i < TEST_STEP; i++) { memset(slave_recive, 0x00, SOC_SPI_MAXIMUM_BUFFER_SIZE); test_fill_random_to_buffers_dualboard(122 + mode + speed_level + i, slave_expect, slave_send, SOC_SPI_MAXIMUM_BUFFER_SIZE); spi_slave_transaction_t trans = { .length = SOC_SPI_MAXIMUM_BUFFER_SIZE * 8, .tx_buffer = slave_send, .rx_buffer = slave_recive, }; unity_send_signal("Slave ready"); TEST_ESP_OK(spi_slave_transmit(TEST_SPI_HOST, &trans, portMAX_DELAY)); if (sio_master_in) { ESP_LOG_BUFFER_HEX("Slave tx", trans.tx_buffer, SOC_SPI_MAXIMUM_BUFFER_SIZE); } else { ESP_LOG_BUFFER_HEX_LEVEL("Slave rx", trans.rx_buffer, SOC_SPI_MAXIMUM_BUFFER_SIZE, ESP_LOG_DEBUG); ESP_LOG_BUFFER_HEX_LEVEL("Slave exp", slave_expect, SOC_SPI_MAXIMUM_BUFFER_SIZE, ESP_LOG_DEBUG); spitest_cmp_or_dump(slave_expect, slave_recive, SOC_SPI_MAXIMUM_BUFFER_SIZE); } } } TEST_ESP_OK(spi_slave_free(TEST_SPI_HOST)); } } free(slave_send); free(slave_recive); free(slave_expect); } TEST_CASE_MULTIPLE_DEVICES("TEST_SPI_Freq_SIO_no_DMA", "[spi_ms][timeout=30]", test_master_sio_no_dma, test_slave_sio_no_dma);