diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 425ecfe60..9b199de33 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1629,6 +1629,31 @@ UT_012_05: - UT_T1_1 - 8Mpsram +UT_013_01: + <<: *unit_test_template + tags: + - ESP32_IDF + - Example_SPI_Multi_device + +UT_013_02: + <<: *unit_test_template + tags: + - ESP32_IDF + - Example_SPI_Multi_device + +UT_013_03: + <<: *unit_test_template + tags: + - ESP32_IDF + - Example_SPI_Multi_device + +UT_013_04: + <<: *unit_test_template + tags: + - ESP32_IDF + - Example_SPI_Multi_device + - psram + UT_014_01: <<: *unit_test_template tags: diff --git a/components/driver/test/include/test/test_common_spi.h b/components/driver/test/include/test/test_common_spi.h new file mode 100644 index 000000000..38fa59237 --- /dev/null +++ b/components/driver/test/include/test/test_common_spi.h @@ -0,0 +1,187 @@ +#ifndef _TEST_COMMON_SPI_H_ +#define _TEST_COMMON_SPI_H_ + +#include +#include "driver/spi_master.h" +#include "freertos/FreeRTOS.h" +#include "freertos/ringbuf.h" +#include "freertos/task.h" +#include "unity.h" +#include "test_utils.h" +#include +#include "param_test.h" + +// All the tests using the header should use this definition as much as possible, +// so that the working host can be changed easily in the future. +#define TEST_SPI_HOST HSPI_HOST +#define TEST_SLAVE_HOST VSPI_HOST + +#define FUNC_SPI 1 +#define FUNC_GPIO 2 + +//Delay information +#define ESP_SPI_SLAVE_TV (12.5*3.5) +#define GPIO_DELAY (12.5*2) +#define WIRE_DELAY 12.5 +#define TV_INT_CONNECT_GPIO (ESP_SPI_SLAVE_TV+GPIO_DELAY) +#define TV_INT_CONNECT (ESP_SPI_SLAVE_TV) +//when connecting to another board, the delay is usually increased by 12.5ns +#define TV_WITH_ESP_SLAVE_GPIO (TV_INT_CONNECT_GPIO+WIRE_DELAY) +#define TV_WITH_ESP_SLAVE (TV_INT_CONNECT+WIRE_DELAY) + +//currently ESP32 slave only supports up to 20MHz, but 40MHz on the same board +#define ESP_SPI_SLAVE_MAX_FREQ SPI_MASTER_FREQ_20M +#define ESP_SPI_SLAVE_MAX_FREQ_SYNC SPI_MASTER_FREQ_40M + +#define MAX_TEST_SIZE 16 ///< in this test we run several transactions, this is the maximum trans that can be run +#define PSET_NAME_LEN 30 ///< length of each param set name + +//test low frequency, high frequency until freq limit for worst case (both GPIO) +#define TEST_FREQ_DEFAULT(){\ + 1*1000*1000, \ + SPI_MASTER_FREQ_8M , \ + SPI_MASTER_FREQ_9M , \ + SPI_MASTER_FREQ_10M, \ + SPI_MASTER_FREQ_11M, \ + SPI_MASTER_FREQ_13M, \ + SPI_MASTER_FREQ_16M, \ + SPI_MASTER_FREQ_20M, \ + SPI_MASTER_FREQ_26M, \ + SPI_MASTER_FREQ_40M, \ + SPI_MASTER_FREQ_80M, \ + 0,\ + } + +#define PIN_NUM_MISO HSPI_IOMUX_PIN_NUM_MISO +#define PIN_NUM_MOSI HSPI_IOMUX_PIN_NUM_MOSI +#define PIN_NUM_CLK HSPI_IOMUX_PIN_NUM_CLK +#define PIN_NUM_CS HSPI_IOMUX_PIN_NUM_CS + +//default bus config for tests +#define SPI_BUS_TEST_DEFAULT_CONFIG() {\ + .miso_io_num=PIN_NUM_MISO, \ + .mosi_io_num=PIN_NUM_MOSI,\ + .sclk_io_num=PIN_NUM_CLK,\ + .quadwp_io_num=-1,\ + .quadhd_io_num=-1\ + } + +//default device config for master devices +#define SPI_DEVICE_TEST_DEFAULT_CONFIG() {\ + .clock_speed_hz=10*1000*1000,\ + .mode=0,\ + .spics_io_num=PIN_NUM_CS,\ + .queue_size=16,\ + .pre_cb=NULL, \ + .cs_ena_pretrans = 0,\ + .cs_ena_posttrans = 0,\ + .input_delay_ns = 62.5,\ + } + +//default device config for slave devices +#define SPI_SLAVE_TEST_DEFAULT_CONFIG() {\ + .mode=0,\ + .spics_io_num=PIN_NUM_CS,\ + .queue_size=3,\ + .flags=0,\ + } + +typedef enum { + FULL_DUPLEX = 0, + HALF_DUPLEX_MISO = 1, + HALF_DUPLEX_MOSI = 2, +} spi_dup_t; + +/*-------- slave task related stuff -----------*/ +typedef struct { + uint32_t len; + uint8_t* tx_start; + uint8_t data[1]; +} slave_rxdata_t; + +typedef struct { + uint32_t len; + const uint8_t *start; +} slave_txdata_t; + +typedef struct { + spi_host_device_t spi; + RingbufHandle_t data_received; + QueueHandle_t data_to_send; +} spi_slave_task_context_t; + +// test data for master and slave +extern uint8_t spitest_master_send[]; +extern uint8_t spitest_slave_send[]; + +//tags for master and slave app +extern const char MASTER_TAG[]; +extern const char SLAVE_TAG[]; + +//parameter set definition +typedef struct { + const char pset_name[PSET_NAME_LEN]; + /*The test work till the frequency below, + *set the frequency to higher and remove checks in the driver to know how fast the system can run. + */ + const int *freq_list; // list of tested frequency, terminated by 0 + int freq_limit; //freq larger (not equal) than this will be ignored + spi_dup_t dup; + int mode; + bool length_aligned; + int test_size; + + int master_limit; // the master disable dummy bits and discard readings over this freq + bool master_iomux; + int master_dma_chan; + + bool slave_iomux; + int slave_dma_chan; + int slave_tv_ns; + bool slave_unaligned_addr; +} spitest_param_set_t; + +//context definition for the parameterized test +typedef struct { + uint8_t master_rxbuf[480]; + spi_transaction_t master_trans[MAX_TEST_SIZE]; + TaskHandle_t handle_slave; + spi_slave_task_context_t slave_context; + slave_txdata_t slave_trans[MAX_TEST_SIZE]; +} spitest_context_t; + +// fill default value of spitest_param_set_t +void spitest_def_param(void* arg); + +// functions for slave task +esp_err_t init_slave_context(spi_slave_task_context_t *context); +void deinit_slave_context(spi_slave_task_context_t *context); +void spitest_slave_task(void* arg); + +//called by slave, pull-up all pins used by slave +void slave_pull_up(const spi_bus_config_t* cfg, int spics_io_num); + +// to access data of pre-defined transactions. +void spitest_init_transactions(const spitest_param_set_t *cfg, spitest_context_t* context); + +// print data from a transaction +void spitest_master_print_data(spi_transaction_t *t, int rxlength); +void spitest_slave_print_data(slave_rxdata_t *t, bool print_rxdata); +// Check whether master and slave data match +esp_err_t spitest_check_data(int len, spi_transaction_t *master_t, slave_rxdata_t *slave_t, bool check_master_data, bool check_slave_len, bool check_slave_data); + +static inline int get_trans_len(spi_dup_t dup, spi_transaction_t *master_t) +{ + if (dup!=HALF_DUPLEX_MISO) { + return master_t->length; + } else { + return master_t->rxlength; + } +} +//remove device from bus and free the bus +void master_free_device_bus(spi_device_handle_t spi); + +//use this function to fix the output source when assign multiple funcitons to a same pin +void spitest_gpio_output_sel(uint32_t gpio_num, int func, uint32_t signal_idx); + +#endif //_TEST_COMMON_SPI_H_ \ No newline at end of file diff --git a/components/driver/test/test_common_spi.c b/components/driver/test/test_common_spi.c new file mode 100644 index 000000000..ed0a681ca --- /dev/null +++ b/components/driver/test/test_common_spi.c @@ -0,0 +1,192 @@ +#include "test/test_common_spi.h" +#include "driver/spi_slave.h" +#include "esp_log.h" + +int test_freq_default[]=TEST_FREQ_DEFAULT(); + +const char MASTER_TAG[] = "test_master"; +const char SLAVE_TAG[] = "test_slave"; + +DRAM_ATTR uint8_t spitest_master_send[] = { + 0x93, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0xaa, 0xcc, 0xff, 0xee, 0x55, 0x77, 0x88, 0x43, + 0x74, + 0x93, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0xaa, 0xcc, 0xff, 0xee, 0x55, 0x77, 0x88, 0x43, + 0x74, + 0x93, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0xaa, 0xcc, 0xff, 0xee, 0x55, 0x77, 0x88, 0x43, + 0x74, + }; +DRAM_ATTR uint8_t spitest_slave_send[] = { + 0xaa, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x13, 0x57, 0x9b, 0xdf, 0x24, 0x68, 0xac, 0xe0, + 0xda, + 0xaa, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x13, 0x57, 0x9b, 0xdf, 0x24, 0x68, 0xac, 0xe0, + 0xda, + 0xaa, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x13, 0x57, 0x9b, 0xdf, 0x24, 0x68, 0xac, 0xe0, + 0xda, + }; + +void spitest_def_param(void* arg) +{ + spitest_param_set_t *param_set=(spitest_param_set_t*)arg; + param_set->test_size = 8; + if (param_set->freq_list==NULL) param_set->freq_list = test_freq_default; +} + +/********************************************************************************** + * functions for slave task + *********************************************************************************/ +esp_err_t init_slave_context(spi_slave_task_context_t *context) +{ + context->data_to_send = xQueueCreate( 16, sizeof( slave_txdata_t )); + if ( context->data_to_send == NULL ) { + return ESP_ERR_NO_MEM; + } + context->data_received = xRingbufferCreate( 1024, RINGBUF_TYPE_NOSPLIT ); + if ( context->data_received == NULL ) { + return ESP_ERR_NO_MEM; + } + context->spi=VSPI_HOST; + return ESP_OK; +} + +void deinit_slave_context(spi_slave_task_context_t *context) +{ + TEST_ASSERT( context->data_to_send != NULL ); + vQueueDelete( context->data_to_send ); + context->data_to_send = NULL; + TEST_ASSERT( context->data_received != NULL ); + vRingbufferDelete( context->data_received ); + context->data_received = NULL; +} + +/* The task requires a queue and a ringbuf, which should be initialized before task starts. + Send ``slave_txdata_t`` to the queue to make the task send data; + the task returns data got to the ringbuf, which should have sufficient size. +*/ +void spitest_slave_task(void* arg) +{ + spi_slave_task_context_t* context = (spi_slave_task_context_t*) arg; + QueueHandle_t queue = context->data_to_send; + RingbufHandle_t ringbuf = context->data_received; + uint8_t recvbuf[320+8]; + slave_txdata_t txdata; + + ESP_LOGI( SLAVE_TAG, "slave up" ); + //never quit, but blocked by the queue, waiting to be killed, when no more send from main task. + while( 1 ) { + BaseType_t ret = xQueueReceive( queue, &txdata, portMAX_DELAY ); + assert(ret); + + spi_slave_transaction_t t = {}; + t.length = txdata.len; + t.tx_buffer = txdata.start; + t.rx_buffer = recvbuf+8; + //loop until trans_len != 0 to skip glitches + do { + TEST_ESP_OK( spi_slave_transmit( context->spi, &t, portMAX_DELAY ) ); + } while ( t.trans_len <= 2 ); + memcpy(recvbuf, &t.trans_len, sizeof(uint32_t)); + *(uint8_t**)(recvbuf+4) = (uint8_t*)txdata.start; + ESP_LOGI( SLAVE_TAG, "received: %d", t.trans_len ); + xRingbufferSend( ringbuf, recvbuf, 8+(t.trans_len+7)/8, portMAX_DELAY ); + } +} + +void slave_pull_up(const spi_bus_config_t* cfg, int spics_io_num) +{ + gpio_set_pull_mode(cfg->mosi_io_num, GPIO_PULLUP_ONLY); + gpio_set_pull_mode(cfg->sclk_io_num, GPIO_PULLUP_ONLY); + gpio_set_pull_mode(spics_io_num, GPIO_PULLUP_ONLY); +} + +/********************************************************************************** + * functions for slave task + *********************************************************************************/ + +static int test_len[] = {1, 3, 5, 7, 9, 11, 33, 64}; + +void spitest_init_transactions(const spitest_param_set_t *cfg, spitest_context_t* context) +{ + spi_transaction_t* trans = context->master_trans; + uint8_t *rx_buf_ptr = context->master_rxbuf; + const spi_dup_t dup = cfg->dup; + + for (int i = 0; i < cfg->test_size; i++) { + const void* tx_buffer = spitest_master_send + i%8; + int length = 8*test_len[i]; + if (cfg->length_aligned) length = (length+31)&(~31); + + if (dup == HALF_DUPLEX_MISO) { + trans[i] = (spi_transaction_t) { + .rx_buffer = rx_buf_ptr, + .rxlength = length, + }; + } else if (dup == HALF_DUPLEX_MOSI) { + trans[i] = (spi_transaction_t) { + .tx_buffer = tx_buffer, + .length = length, + }; + } else { + trans[i] = (spi_transaction_t) { + .tx_buffer = tx_buffer, + .length = length, + .rx_buffer = rx_buf_ptr, + }; + } + rx_buf_ptr = (uint8_t*)( (uint32_t)(rx_buf_ptr + (length+7)/8 + 3) & (~3)); + + const void* slave_tx = spitest_slave_send + (cfg->slave_unaligned_addr? i%3: (i%3)*4); + //prepare slave tx data + context->slave_trans[i] = (slave_txdata_t) { + .start = slave_tx, + .len = 512, + }; + if (cfg->slave_dma_chan != 0) context->slave_trans[i].len = 1024; + } +} + +void spitest_master_print_data(spi_transaction_t *t, int rxlength) +{ + if (t->tx_buffer) ESP_LOG_BUFFER_HEX( "master tx", t->tx_buffer, t->length/8 ); + if (t->rx_buffer) ESP_LOG_BUFFER_HEX( "master rx", t->rx_buffer, rxlength/8 ); +} + +void spitest_slave_print_data(slave_rxdata_t *t, bool print_rxdata) +{ + int rcv_len = (t->len+7)/8; + ESP_LOGI(SLAVE_TAG, "trans_len: %d", t->len); + ESP_LOG_BUFFER_HEX("slave tx", t->tx_start, rcv_len); + if (print_rxdata) ESP_LOG_BUFFER_HEX("slave rx", t->data, rcv_len); +} + +esp_err_t spitest_check_data(int len, spi_transaction_t *master_t, slave_rxdata_t *slave_t, bool check_master_data, bool check_slave_len, bool check_slave_data) +{ + //currently the rcv_len can be in range of [t->length-1, t->length+3] + if (check_slave_len) { + uint32_t rcv_len = slave_t->len; + TEST_ASSERT(rcv_len >= len - 1 && rcv_len <= len + 4); + } + + //if (dup!=HALF_DUPLEX_MOSI) { + if (check_master_data) { + TEST_ASSERT_EQUAL_HEX8_ARRAY(slave_t->tx_start, master_t->rx_buffer, (len + 7) / 8); + } + + //if (dup!=HALF_DUPLEX_MISO) { + if (check_slave_data) { + TEST_ASSERT_EQUAL_HEX8_ARRAY(master_t->tx_buffer, slave_t->data, (len + 7) / 8); + } + return ESP_OK; +} + + +void master_free_device_bus(spi_device_handle_t spi) +{ + TEST_ESP_OK( spi_bus_remove_device(spi) ); + TEST_ESP_OK( spi_bus_free(TEST_SPI_HOST) ); +} + +void spitest_gpio_output_sel(uint32_t gpio_num, int func, uint32_t signal_idx) +{ + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[gpio_num], func); + GPIO.func_out_sel_cfg[gpio_num].func_sel=signal_idx; +} \ No newline at end of file diff --git a/components/driver/test/test_spi_master.c b/components/driver/test/test_spi_master.c index 313c7edc9..5f6da2570 100644 --- a/components/driver/test/test_spi_master.c +++ b/components/driver/test/test_spi_master.c @@ -20,41 +20,13 @@ #include "esp_heap_caps.h" #include "esp_log.h" #include "soc/spi_periph.h" -#include "freertos/ringbuf.h" +#include "test_utils.h" +#include "test/test_common_spi.h" #include "soc/gpio_periph.h" #include "sdkconfig.h" -#include "test_utils.h" const static char TAG[] = "test_spi"; -#define SPI_BUS_TEST_DEFAULT_CONFIG() {\ - .miso_io_num=PIN_NUM_MISO, \ - .mosi_io_num=PIN_NUM_MOSI,\ - .sclk_io_num=PIN_NUM_CLK,\ - .quadwp_io_num=-1,\ - .quadhd_io_num=-1\ -} - -#define SPI_DEVICE_TEST_DEFAULT_CONFIG() {\ - .clock_speed_hz=10*1000*1000,\ - .mode=0,\ - .spics_io_num=PIN_NUM_CS,\ - .queue_size=16,\ - .pre_cb=NULL, \ - .cs_ena_pretrans = 0,\ - .cs_ena_posttrans = 0,\ - .input_delay_ns = 62.5,\ -} - -#define FUNC_SPI 1 -#define FUNC_GPIO 2 - -void gpio_output_sel(uint32_t gpio_num, int func, uint32_t signal_idx) -{ - PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[gpio_num], func); - GPIO.func_out_sel_cfg[gpio_num].func_sel=signal_idx; -} - static void check_spi_pre_n_for(int clk, int pre, int n) { esp_err_t ret; @@ -145,7 +117,7 @@ static spi_device_handle_t setup_spi_bus(int clkspeed, bool dma) { ret=spi_bus_add_device(HSPI_HOST, &devcfg, &handle); TEST_ASSERT(ret==ESP_OK); //connect MOSI to two devices breaks the output, fix it. - gpio_output_sel(26, FUNC_GPIO, HSPID_OUT_IDX); + spitest_gpio_output_sel(26, FUNC_GPIO, HSPID_OUT_IDX); printf("Bus/dev inited.\n"); return handle; } @@ -203,17 +175,6 @@ static int spi_test(spi_device_handle_t handle, int num_bytes) { return success; } -static void destroy_spi_bus(spi_device_handle_t handle) { - esp_err_t ret; - ret=spi_bus_remove_device(handle); - TEST_ASSERT(ret==ESP_OK); - ret=spi_bus_free(HSPI_HOST); - TEST_ASSERT(ret==ESP_OK); -} - - -#define TEST_LEN 111 - TEST_CASE("SPI Master test", "[spi]") { bool success = true; @@ -228,7 +189,7 @@ TEST_CASE("SPI Master test", "[spi]") success &= spi_test(handle, 4096-1); //multiple descs, edge case 2 success &= spi_test(handle, 4096*3); //multiple descs - destroy_spi_bus(handle); + master_free_device_bus(handle); printf("Testing bus at 80KHz, non-DMA\n"); handle=setup_spi_bus(80000, false); @@ -240,22 +201,21 @@ TEST_CASE("SPI Master test", "[spi]") success &= spi_test(handle, 63); //small success &= spi_test(handle, 64); //small, unaligned - destroy_spi_bus(handle); - + master_free_device_bus(handle); printf("Testing bus at 26MHz\n"); handle=setup_spi_bus(20000000, true); success &= spi_test(handle, 128); //DMA, aligned success &= spi_test(handle, 4096*3); //DMA, multiple descs - destroy_spi_bus(handle); + master_free_device_bus(handle); printf("Testing bus at 900KHz\n"); handle=setup_spi_bus(9000000, true); success &= spi_test(handle, 128); //DMA, aligned success &= spi_test(handle, 4096*3); //DMA, multiple descs - destroy_spi_bus(handle); + master_free_device_bus(handle); TEST_ASSERT(success); } @@ -299,7 +259,7 @@ TEST_CASE("SPI Master test, interaction of multiple devs", "[spi]") { ret=spi_bus_remove_device(handle2); TEST_ASSERT(ret==ESP_OK); - destroy_spi_bus(handle1); + master_free_device_bus(handle1); TEST_ASSERT(success); } @@ -522,22 +482,6 @@ static const uint8_t data_drom[320+3] = { 0x70, 0x22, 0x7D, 0x0A, 0x6D, 0xD3, 0x77, 0x73, 0xD0, 0xF4, 0x06, 0xB2, 0x19, 0x8C, 0xFF, 0x58, 0xE4, 0xDB, 0xE9, 0xEC, 0x89, 0x6A, 0xF4, 0x0E, 0x67, 0x12, 0xEC, 0x11, 0xD2, 0x1F, 0x8D, 0xD7, }; -#if 1 //HSPI -#define PIN_NUM_MISO HSPI_IOMUX_PIN_NUM_MISO -#define PIN_NUM_MOSI HSPI_IOMUX_PIN_NUM_MOSI -#define PIN_NUM_CLK HSPI_IOMUX_PIN_NUM_CLK -#define PIN_NUM_CS HSPI_IOMUX_PIN_NUM_CS -#elif 1 //VSPI -#define PIN_NUM_MISO VSPI_IOMUX_PIN_NUM_MISO -#define PIN_NUM_MOSI VSPI_IOMUX_PIN_NUM_MOSI -#define PIN_NUM_CLK VSPI_IOMUX_PIN_NUM_CLK -#define PIN_NUM_CS VSPI_IOMUX_PIN_NUM_CS -#endif - -#define PIN_NUM_DC 21 -#define PIN_NUM_RST 18 -#define PIN_NUM_BCKL 5 - TEST_CASE("SPI Master DMA test, TX and RX in different regions", "[spi]") { #ifdef CONFIG_SPIRAM_SUPPORT @@ -581,7 +525,7 @@ TEST_CASE("SPI Master DMA test, TX and RX in different regions", "[spi]") ret=spi_bus_add_device(HSPI_HOST, &devcfg, &spi); TEST_ASSERT(ret==ESP_OK); //connect MOSI to two devices breaks the output, fix it. - gpio_output_sel(buscfg.mosi_io_num, FUNC_GPIO, HSPID_OUT_IDX); + spitest_gpio_output_sel(buscfg.mosi_io_num, FUNC_GPIO, HSPID_OUT_IDX); #define TEST_REGION_SIZE 5 static spi_transaction_t trans[TEST_REGION_SIZE]; @@ -663,7 +607,7 @@ TEST_CASE("SPI Master DMA test: length, start, not aligned", "[spi]") TEST_ASSERT(ret==ESP_OK); //connect MOSI to two devices breaks the output, fix it. - gpio_output_sel(buscfg.mosi_io_num, FUNC_GPIO, HSPID_OUT_IDX); + spitest_gpio_output_sel(buscfg.mosi_io_num, FUNC_GPIO, HSPID_OUT_IDX); memset(rx_buf, 0x66, 320); @@ -705,124 +649,6 @@ TEST_CASE("SPI Master DMA test: length, start, not aligned", "[spi]") TEST_ASSERT(spi_bus_free(HSPI_HOST) == ESP_OK); } -static const char MASTER_TAG[] = "test_master"; -static const char SLAVE_TAG[] = "test_slave"; -DRAM_ATTR static uint8_t master_send[] = { - 0x93, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0xaa, 0xcc, 0xff, 0xee, 0x55, 0x77, 0x88, 0x43, - 0x74, - 0x93, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0xaa, 0xcc, 0xff, 0xee, 0x55, 0x77, 0x88, 0x43, - 0x74, - 0x93, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0xaa, 0xcc, 0xff, 0xee, 0x55, 0x77, 0x88, 0x43, - 0x74, - }; -DRAM_ATTR static uint8_t slave_send[] = { - 0xaa, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x13, 0x57, 0x9b, 0xdf, 0x24, 0x68, 0xac, 0xe0, - 0xda, - 0xaa, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x13, 0x57, 0x9b, 0xdf, 0x24, 0x68, 0xac, 0xe0, - 0xda, - 0xaa, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x13, 0x57, 0x9b, 0xdf, 0x24, 0x68, 0xac, 0xe0, - 0xda, - }; - - -static void master_deinit(spi_device_handle_t spi) -{ - TEST_ESP_OK( spi_bus_remove_device(spi) ); - TEST_ESP_OK( spi_bus_free(HSPI_HOST) ); -} - -#define SPI_SLAVE_TEST_DEFAULT_CONFIG() {\ - .mode=0,\ - .spics_io_num=PIN_NUM_CS,\ - .queue_size=3,\ - .flags=0,\ -} - -static void slave_pull_up(const spi_bus_config_t* cfg, int spics_io_num) -{ - gpio_set_pull_mode(cfg->mosi_io_num, GPIO_PULLUP_ENABLE); - gpio_set_pull_mode(cfg->sclk_io_num, GPIO_PULLUP_ENABLE); - gpio_set_pull_mode(spics_io_num, GPIO_PULLUP_ENABLE); -} - -typedef struct { - uint32_t len; - uint8_t *start; -} slave_txdata_t; - -typedef struct { - uint32_t len; - uint8_t* tx_start; - uint8_t data[1]; -} slave_rxdata_t; - -typedef struct { - spi_host_device_t spi; - RingbufHandle_t data_received; - QueueHandle_t data_to_send; -} spi_slave_task_context_t; - -esp_err_t init_slave_context(spi_slave_task_context_t *context) -{ - context->data_to_send = xQueueCreate( 16, sizeof( slave_txdata_t )); - if ( context->data_to_send == NULL ) { - return ESP_ERR_NO_MEM; - } - context->data_received = xRingbufferCreate( 1024, RINGBUF_TYPE_NOSPLIT ); - if ( context->data_received == NULL ) { - return ESP_ERR_NO_MEM; - } - context->spi=VSPI_HOST; - return ESP_OK; -} - -void deinit_slave_context(spi_slave_task_context_t *context) -{ - TEST_ASSERT( context->data_to_send != NULL ); - vQueueDelete( context->data_to_send ); - context->data_to_send = NULL; - TEST_ASSERT( context->data_received != NULL ); - vRingbufferDelete( context->data_received ); - context->data_received = NULL; -} - -/* The task requires a queue and a ringbuf, which should be initialized before task starts. - Send ``slave_txdata_t`` to the queue to make the task send data; - the task returns data got to the ringbuf, which should have sufficient size. -*/ -static void task_slave(void* arg) -{ - spi_slave_task_context_t* context = (spi_slave_task_context_t*) arg; - QueueHandle_t queue = context->data_to_send; - RingbufHandle_t ringbuf = context->data_received; - uint8_t recvbuf[320+8]; - slave_txdata_t txdata; - - ESP_LOGI( SLAVE_TAG, "slave up" ); - //never quit, but blocked by the queue, waiting to be killed, when no more send from main task. - while( 1 ) { - xQueueReceive( queue, &txdata, portMAX_DELAY ); - - ESP_LOGI( "test", "to send: %p", txdata.start ); - spi_slave_transaction_t t = {}; - t.length = txdata.len; - t.tx_buffer = txdata.start; - t.rx_buffer = recvbuf+8; - //loop until trans_len != 0 to skip glitches - memset(recvbuf, 0x66, sizeof(recvbuf)); - do { - TEST_ESP_OK( spi_slave_transmit( context->spi, &t, portMAX_DELAY ) ); - } while ( t.trans_len == 0 ); - memcpy(recvbuf, &t.trans_len, sizeof(uint32_t)); - *(uint8_t**)(recvbuf+4) = txdata.start; - ESP_LOGI( SLAVE_TAG, "received: %d", t.trans_len ); - xRingbufferSend( ringbuf, recvbuf, 8+(t.trans_len+7)/8, portMAX_DELAY ); - } -} - -#define TEST_SPI_HOST HSPI_HOST -#define TEST_SLAVE_HOST VSPI_HOST - static uint8_t bitswap(uint8_t in) { uint8_t out = 0; @@ -849,15 +675,15 @@ void test_cmd_addr(spi_slave_task_context_t *slave_context, bool lsb_first) TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &devcfg, &spi)); //connecting pins to two peripherals breaks the output, fix it. - gpio_output_sel(buscfg.mosi_io_num, FUNC_GPIO, spi_periph_signal[TEST_SPI_HOST].spid_out); - gpio_output_sel(buscfg.miso_io_num, FUNC_GPIO, spi_periph_signal[TEST_SLAVE_HOST].spiq_out); - gpio_output_sel(devcfg.spics_io_num, FUNC_GPIO, spi_periph_signal[TEST_SPI_HOST].spics_out[0]); - gpio_output_sel(buscfg.sclk_io_num, FUNC_GPIO, spi_periph_signal[TEST_SPI_HOST].spiclk_out); + 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); for (int i= 0; i < 8; i++) { //prepare slave tx data slave_txdata_t slave_txdata = (slave_txdata_t) { - .start = slave_send, + .start = spitest_slave_send + 4*(i%3), .len = 256, }; xQueueSend(slave_context->data_to_send, &slave_txdata, portMAX_DELAY); @@ -947,7 +773,7 @@ TEST_CASE("SPI master variable cmd & addr test","[spi]") esp_err_t err = init_slave_context( &slave_context ); TEST_ASSERT( err == ESP_OK ); TaskHandle_t handle_slave; - xTaskCreate( task_slave, "spi_slave", 4096, &slave_context, 0, &handle_slave); + xTaskCreate( spitest_slave_task, "spi_slave", 4096, &slave_context, 0, &handle_slave); //initial slave, mode 0, no dma int dma_chan = 0; @@ -971,354 +797,6 @@ TEST_CASE("SPI master variable cmd & addr test","[spi]") ESP_LOGI(MASTER_TAG, "test passed."); } -/******************************************************************************** - * Test Timing By Internal Connections - ********************************************************************************/ -typedef enum { - FULL_DUPLEX = 0, - HALF_DUPLEX_MISO = 1, - HALF_DUPLEX_MOSI = 2, -} spi_dup_t; - -static int timing_speed_array[]={/**/ - SPI_MASTER_FREQ_8M , - SPI_MASTER_FREQ_9M , - SPI_MASTER_FREQ_10M, - SPI_MASTER_FREQ_11M, - SPI_MASTER_FREQ_13M, - SPI_MASTER_FREQ_16M, - SPI_MASTER_FREQ_20M, - SPI_MASTER_FREQ_26M, - SPI_MASTER_FREQ_40M, - SPI_MASTER_FREQ_80M, -}; - -typedef struct { - uint8_t master_rxbuf[320]; - spi_transaction_t master_trans[16]; - TaskHandle_t handle_slave; - spi_slave_task_context_t slave_context; - slave_txdata_t slave_trans[16]; -} timing_context_t; - -void master_print_data(spi_transaction_t *t, spi_dup_t dup) -{ - if (t->tx_buffer) { - ESP_LOG_BUFFER_HEX( "master tx", t->tx_buffer, t->length/8 ); - } else { - ESP_LOGI( "master tx", "no data" ); - } - - int rxlength; - if (dup!=HALF_DUPLEX_MISO) { - rxlength = t->length/8; - } else { - rxlength = t->rxlength/8; - } - if (t->rx_buffer) { - ESP_LOG_BUFFER_HEX( "master rx", t->rx_buffer, rxlength ); - } else { - ESP_LOGI( "master rx", "no data" ); - } -} - -void slave_print_data(slave_rxdata_t *t) -{ - int rcv_len = (t->len+7)/8; - ESP_LOGI(SLAVE_TAG, "trans_len: %d", t->len); - ESP_LOG_BUFFER_HEX( "slave tx", t->tx_start, rcv_len); - ESP_LOG_BUFFER_HEX( "slave rx", t->data, rcv_len); -} - -esp_err_t check_data(spi_transaction_t *t, spi_dup_t dup, slave_rxdata_t *slave_t) -{ - int length; - if (dup!=HALF_DUPLEX_MISO) { - length = t->length; - } else { - length = t->rxlength; - } - TEST_ASSERT(length!=0); - - //currently the rcv_len can be in range of [t->length-1, t->length+3] - uint32_t rcv_len = slave_t->len; - TEST_ASSERT(rcv_len >= length-1 && rcv_len <= length+3); - - //the timing speed is temporarily only for master - if (dup!=HALF_DUPLEX_MISO) { -// TEST_ASSERT_EQUAL_HEX8_ARRAY(t->tx_buffer, slave_t->data, (t->length+7)/8); - } - if (dup!=HALF_DUPLEX_MOSI) { - TEST_ASSERT_EQUAL_HEX8_ARRAY(slave_t->tx_start, t->rx_buffer, (length+7)/8); - } - return ESP_OK; -} - -int test_len[] = {1, 3, 5, 7, 9, 11, 33, 64}; - -static void timing_init_transactions(spi_dup_t dup, timing_context_t* context) -{ - spi_transaction_t* trans = context->master_trans; - uint8_t *rx_buf_ptr = context->master_rxbuf; - if (dup==HALF_DUPLEX_MISO) { - for (int i = 0; i < 8; i++ ) { - trans[i] = (spi_transaction_t) { - .flags = 0, - .rxlength = 8*test_len[i], - .rx_buffer = rx_buf_ptr, - }; - rx_buf_ptr += ((context->master_trans[i].rxlength + 31)/8)&(~3); - } - } else if (dup==HALF_DUPLEX_MOSI) { - for (int i = 0; i < 8; i++ ) { - trans[i] = (spi_transaction_t) { - .flags = 0, - .length = 8*test_len[i], - .tx_buffer = master_send+i, - }; - } - } else { - for (int i = 0; i < 8; i++ ) { - trans[i] = (spi_transaction_t) { - .flags = 0, - .length = 8*test_len[i], - .tx_buffer = master_send+i, - .rx_buffer = rx_buf_ptr, - }; - rx_buf_ptr += ((context->master_trans[i].length + 31)/8)&(~3); - } - } - //prepare slave tx data - for (int i = 0; i < 8; i ++) { - context->slave_trans[i] = (slave_txdata_t) { - .start = slave_send + 4*(i%3), - .len = 512, - }; - } -} - -typedef struct { - const char cfg_name[30]; - /*The test work till the frequency below, - *set the frequency to higher and remove checks in the driver to know how fast the system can run. - */ - int freq_limit; - spi_dup_t dup; - bool master_iomux; - bool slave_iomux; - int slave_tv_ns; -} test_timing_config_t; - -#define ESP_SPI_SLAVE_TV (12.5*3) -#define GPIO_DELAY (12.5*2) -#define SAMPLE_DELAY 12.5 - -#define TV_INT_CONNECT_GPIO (ESP_SPI_SLAVE_TV+GPIO_DELAY) -#define TV_INT_CONNECT (ESP_SPI_SLAVE_TV) -#define TV_WITH_ESP_SLAVE_GPIO (ESP_SPI_SLAVE_TV+SAMPLE_DELAY+GPIO_DELAY) -#define TV_WITH_ESP_SLAVE (ESP_SPI_SLAVE_TV+SAMPLE_DELAY) - -//currently ESP32 slave only supports up to 20MHz, but 40MHz on the same board -#define ESP_SPI_SLAVE_MAX_FREQ SPI_MASTER_FREQ_20M -#define ESP_SPI_SLAVE_MAX_FREQ_SYNC SPI_MASTER_FREQ_40M - - -static test_timing_config_t timing_master_conf_t[] = { - { .cfg_name = "FULL_DUP, MASTER IOMUX", - .freq_limit = SPI_MASTER_FREQ_13M, - .dup = FULL_DUPLEX, - .master_iomux = true, - .slave_iomux = false, - .slave_tv_ns = TV_INT_CONNECT_GPIO, - }, - { .cfg_name = "FULL_DUP, SLAVE IOMUX", - .freq_limit = SPI_MASTER_FREQ_13M, - .dup = FULL_DUPLEX, - .master_iomux = false, - .slave_iomux = true, - .slave_tv_ns = TV_INT_CONNECT, - }, - { .cfg_name = "FULL_DUP, BOTH GPIO", - .freq_limit = SPI_MASTER_FREQ_10M, - .dup = FULL_DUPLEX, - .master_iomux = false, - .slave_iomux = false, - .slave_tv_ns = TV_INT_CONNECT_GPIO, - }, - { .cfg_name = "HALF_DUP, MASTER IOMUX", - .freq_limit = ESP_SPI_SLAVE_MAX_FREQ_SYNC, - .dup = HALF_DUPLEX_MISO, - .master_iomux = true, - .slave_iomux = false, - .slave_tv_ns = TV_INT_CONNECT_GPIO, - }, - { .cfg_name = "HALF_DUP, SLAVE IOMUX", - .freq_limit = ESP_SPI_SLAVE_MAX_FREQ_SYNC, - .dup = HALF_DUPLEX_MISO, - .master_iomux = false, - .slave_iomux = true, - .slave_tv_ns = TV_INT_CONNECT, - }, - { .cfg_name = "HALF_DUP, BOTH GPIO", - .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, - }, - { .cfg_name = "MOSI_DUP, MASTER IOMUX", - .freq_limit = ESP_SPI_SLAVE_MAX_FREQ_SYNC, - .dup = HALF_DUPLEX_MOSI, - .master_iomux = true, - .slave_iomux = false, - .slave_tv_ns = TV_INT_CONNECT_GPIO, - }, - { .cfg_name = "MOSI_DUP, SLAVE IOMUX", - .freq_limit = ESP_SPI_SLAVE_MAX_FREQ_SYNC, - .dup = HALF_DUPLEX_MOSI, - .master_iomux = false, - .slave_iomux = true, - .slave_tv_ns = TV_INT_CONNECT, - }, - { .cfg_name = "MOSI_DUP, BOTH GPIO", - .freq_limit = ESP_SPI_SLAVE_MAX_FREQ_SYNC, - .dup = HALF_DUPLEX_MOSI, - .master_iomux = false, - .slave_iomux = false, - .slave_tv_ns = TV_INT_CONNECT_GPIO, - }, -}; - -//this case currently only checks master read -TEST_CASE("test timing_master","[spi][timeout=120]") -{ - timing_context_t context; - - //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); - - context.slave_context = (spi_slave_task_context_t){}; - esp_err_t err = init_slave_context( &context.slave_context ); - TEST_ASSERT( err == ESP_OK ); - - xTaskCreate( task_slave, "spi_slave", 4096, &context.slave_context, 0, &context.handle_slave); - - const int test_size = sizeof(timing_master_conf_t)/sizeof(test_timing_config_t); - for (int i = 0; i < test_size; i++) { - test_timing_config_t* conf = &timing_master_conf_t[i]; - - spi_device_handle_t spi; - - timing_init_transactions(conf->dup, &context); - - ESP_LOGI(MASTER_TAG, "****************** %s ***************", conf->cfg_name); - for (int j=0; j conf->freq_limit) break; - ESP_LOGI(MASTER_TAG, "======> %dk", timing_speed_array[j]/1000); - - //master config - const int master_mode = 0; - spi_bus_config_t buscfg=SPI_BUS_TEST_DEFAULT_CONFIG(); - spi_device_interface_config_t devcfg=SPI_DEVICE_TEST_DEFAULT_CONFIG(); - devcfg.mode = master_mode; - if (conf->dup==HALF_DUPLEX_MISO||conf->dup==HALF_DUPLEX_MOSI) { - devcfg.cs_ena_pretrans = 20; - devcfg.flags |= SPI_DEVICE_HALFDUPLEX; - } else { - devcfg.cs_ena_pretrans = 1; - } - devcfg.cs_ena_posttrans = 20; - devcfg.input_delay_ns = conf->slave_tv_ns; - devcfg.clock_speed_hz = timing_speed_array[j]; - - //slave config - int slave_mode = 0; - spi_slave_interface_config_t slvcfg=SPI_SLAVE_TEST_DEFAULT_CONFIG(); - slvcfg.mode = slave_mode; - - //pin config & initialize - //we can't have two sets of iomux pins on the same pins - assert(!conf->master_iomux || !conf->slave_iomux); - if (conf->slave_iomux) { - //only in this case, use VSPI iomux pins - buscfg.miso_io_num = VSPI_IOMUX_PIN_NUM_MISO; - buscfg.mosi_io_num = VSPI_IOMUX_PIN_NUM_MOSI; - buscfg.sclk_io_num = VSPI_IOMUX_PIN_NUM_CLK; - devcfg.spics_io_num = VSPI_IOMUX_PIN_NUM_CS; - slvcfg.spics_io_num = VSPI_IOMUX_PIN_NUM_CS; - } else { - buscfg.miso_io_num = HSPI_IOMUX_PIN_NUM_MISO; - buscfg.mosi_io_num = HSPI_IOMUX_PIN_NUM_MOSI; - buscfg.sclk_io_num = HSPI_IOMUX_PIN_NUM_CLK; - devcfg.spics_io_num = HSPI_IOMUX_PIN_NUM_CS; - slvcfg.spics_io_num = HSPI_IOMUX_PIN_NUM_CS; - } - slave_pull_up(&buscfg, slvcfg.spics_io_num); - - //this does nothing, but avoid the driver from using iomux pins if required - buscfg.quadhd_io_num = (!conf->master_iomux && !conf->slave_iomux? VSPI_IOMUX_PIN_NUM_MISO: -1); - TEST_ESP_OK(spi_bus_initialize(HSPI_HOST, &buscfg, 0)); - TEST_ESP_OK(spi_bus_add_device(HSPI_HOST, &devcfg, &spi)); - //slave automatically use iomux pins if pins are on VSPI_* pins - buscfg.quadhd_io_num = -1; - TEST_ESP_OK( spi_slave_initialize(VSPI_HOST, &buscfg, &slvcfg, 0) ); - - //initialize master and slave on the same pins break some of the output configs, fix them - if (conf->master_iomux) { - gpio_output_sel(buscfg.mosi_io_num, FUNC_SPI, HSPID_OUT_IDX); - gpio_output_sel(buscfg.miso_io_num, FUNC_GPIO, VSPIQ_OUT_IDX); - gpio_output_sel(devcfg.spics_io_num, FUNC_SPI, HSPICS0_OUT_IDX); - gpio_output_sel(buscfg.sclk_io_num, FUNC_SPI, HSPICLK_OUT_IDX); - } else if (conf->slave_iomux) { - gpio_output_sel(buscfg.mosi_io_num, FUNC_GPIO, HSPID_OUT_IDX); - gpio_output_sel(buscfg.miso_io_num, FUNC_SPI, VSPIQ_OUT_IDX); - gpio_output_sel(devcfg.spics_io_num, FUNC_GPIO, HSPICS0_OUT_IDX); - gpio_output_sel(buscfg.sclk_io_num, FUNC_GPIO, HSPICLK_OUT_IDX); - } else { - gpio_output_sel(buscfg.mosi_io_num, FUNC_GPIO, HSPID_OUT_IDX); - gpio_output_sel(buscfg.miso_io_num, FUNC_GPIO, VSPIQ_OUT_IDX); - gpio_output_sel(devcfg.spics_io_num, FUNC_GPIO, HSPICS0_OUT_IDX); - gpio_output_sel(buscfg.sclk_io_num, FUNC_GPIO, HSPICLK_OUT_IDX); - } - - //clear master receive buffer - memset(context.master_rxbuf, 0x66, sizeof(context.master_rxbuf)); - - //prepare slave tx data - for (int k = 0; k < 8; k ++) xQueueSend( context.slave_context.data_to_send, &context.slave_trans[k], portMAX_DELAY ); - - for( int k= 0; k < 8; k ++ ) { - //wait for both master and slave end - ESP_LOGI( MASTER_TAG, "=> test%d", k ); - //send master tx data - vTaskDelay(9); - - spi_transaction_t *t = &context.master_trans[k]; - TEST_ESP_OK (spi_device_transmit( spi, t) ); - master_print_data(t, conf->dup); - - size_t rcv_len; - slave_rxdata_t *rcv_data = xRingbufferReceive( context.slave_context.data_received, &rcv_len, portMAX_DELAY ); - slave_print_data(rcv_data); - - //check result - TEST_ESP_OK(check_data(t, conf->dup, rcv_data)); - //clean - vRingbufferReturnItem(context.slave_context.data_received, rcv_data); - } - master_deinit(spi); - TEST_ASSERT(spi_slave_free(VSPI_HOST) == ESP_OK); - } - } - - vTaskDelete( context.handle_slave ); - context.handle_slave = 0; - - deinit_slave_context(&context.slave_context); - - ESP_LOGI(MASTER_TAG, "test passed."); -} - /******************************************************************************** * Test SPI transaction interval ********************************************************************************/ @@ -1340,12 +818,6 @@ static void speed_setup(spi_device_handle_t* spi, bool use_dma) TEST_ASSERT(ret==ESP_OK); } -static void speed_deinit(spi_device_handle_t spi) -{ - TEST_ESP_OK( spi_bus_remove_device(spi) ); - TEST_ESP_OK( spi_bus_free(HSPI_HOST) ); -} - static void sorted_array_insert(uint32_t* array, int* size, uint32_t item) { int pos; @@ -1424,7 +896,8 @@ TEST_CASE("spi_speed","[spi]") //release the bus spi_device_release_bus(spi); - speed_deinit(spi); + master_free_device_bus(spi); + speed_setup(&spi, !use_dma); //record flight time by isr, without DMA @@ -1454,7 +927,7 @@ TEST_CASE("spi_speed","[spi]") //release the bus spi_device_release_bus(spi); - speed_deinit(spi); + master_free_device_bus(spi); } typedef struct { diff --git a/components/driver/test/test_spi_param.c b/components/driver/test/test_spi_param.c new file mode 100644 index 000000000..87036e0cd --- /dev/null +++ b/components/driver/test/test_spi_param.c @@ -0,0 +1,969 @@ +#include "test/test_common_spi.h" +#include "driver/spi_master.h" +#include "driver/spi_slave.h" +#include "esp_log.h" +#include "soc/spi_periph.h" +#include "test/test_common_spi.h" + +/******************************************************************************** + * 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_LOCAL(name, param_set, "[spi][timeout=120]", &local_test_func) + +static void local_test_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_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 = VSPI_IOMUX_PIN_NUM_MISO; + buscfg.mosi_io_num = VSPI_IOMUX_PIN_NUM_MOSI; + buscfg.sclk_io_num = VSPI_IOMUX_PIN_NUM_CLK; + devcfg.spics_io_num = VSPI_IOMUX_PIN_NUM_CS; + slvcfg.spics_io_num = VSPI_IOMUX_PIN_NUM_CS; + } else { + buscfg.miso_io_num = HSPI_IOMUX_PIN_NUM_MISO; + buscfg.mosi_io_num = HSPI_IOMUX_PIN_NUM_MOSI; + buscfg.sclk_io_num = HSPI_IOMUX_PIN_NUM_CLK; + devcfg.spics_io_num = HSPI_IOMUX_PIN_NUM_CS; + slvcfg.spics_io_num = HSPI_IOMUX_PIN_NUM_CS; + } + //this does nothing, but avoid the driver from using iomux pins if required + buscfg.quadhd_io_num = (!pset->master_iomux && !pset->slave_iomux ? VSPI_IOMUX_PIN_NUM_MISO : -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); + + TEST_ESP_OK(spi_bus_initialize(HSPI_HOST, &buscfg, pset->master_dma_chan)); + TEST_ESP_OK(spi_bus_add_device(HSPI_HOST, &devcfg, spi)); + + //slave automatically use iomux pins if pins are on VSPI_* pins + buscfg.quadhd_io_num = -1; + TEST_ESP_OK(spi_slave_initialize(VSPI_HOST, &buscfg, &slvcfg, pset->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, HSPID_OUT_IDX); + spitest_gpio_output_sel(buscfg.miso_io_num, FUNC_GPIO, VSPIQ_OUT_IDX); + spitest_gpio_output_sel(devcfg.spics_io_num, FUNC_SPI, HSPICS0_OUT_IDX); + spitest_gpio_output_sel(buscfg.sclk_io_num, FUNC_SPI, HSPICLK_OUT_IDX); + } else if (pset->slave_iomux) { + spitest_gpio_output_sel(buscfg.mosi_io_num, FUNC_GPIO, HSPID_OUT_IDX); + spitest_gpio_output_sel(buscfg.miso_io_num, FUNC_SPI, VSPIQ_OUT_IDX); + spitest_gpio_output_sel(devcfg.spics_io_num, FUNC_GPIO, HSPICS0_OUT_IDX); + spitest_gpio_output_sel(buscfg.sclk_io_num, FUNC_GPIO, HSPICLK_OUT_IDX); + } else { + spitest_gpio_output_sel(buscfg.mosi_io_num, FUNC_GPIO, HSPID_OUT_IDX); + spitest_gpio_output_sel(buscfg.miso_io_num, FUNC_GPIO, VSPIQ_OUT_IDX); + spitest_gpio_output_sel(devcfg.spics_io_num, FUNC_GPIO, HSPICS0_OUT_IDX); + spitest_gpio_output_sel(buscfg.sclk_io_num, FUNC_GPIO, HSPICLK_OUT_IDX); + } + + //prepare slave tx data + for (int k = 0; k < pset->test_size; k++) + xQueueSend(context->slave_context.data_to_send, &context->slave_trans[k], portMAX_DELAY); + + //clear master receive buffer + memset(context->master_rxbuf, 0x66, sizeof(context->master_rxbuf)); + +} + +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, "======> %dk", freq / 1000); + local_test_start(&spi, freq, pset, context); + + for (int k = 0; k < pset->test_size; k++) { + //wait for both master and slave end + ESP_LOGI(MASTER_TAG, "=> test%d", k); + //send master tx data + vTaskDelay(9); + + spi_transaction_t *t = &context->master_trans[k]; + TEST_ESP_OK(spi_device_transmit(spi, t)); + int len = get_trans_len(pset->dup, t); + 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); + spitest_slave_print_data(rcv_data, true); + + //check result + bool check_master_data = (pset->dup!=HALF_DUPLEX_MOSI && + (pset->master_limit==0 || freq <= pset->master_limit)); + bool check_slave_data = (pset->dup!=HALF_DUPLEX_MISO); + const bool check_len = true; + if (!check_master_data) ESP_LOGI(MASTER_TAG, "skip master data check"); + if (!check_slave_data) ESP_LOGI(SLAVE_TAG, "skip slave data check"); + + 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); + TEST_ASSERT(spi_slave_free(VSPI_HOST) == ESP_OK); + } +} + +/************ Timing Test ***********************************************/ +static spitest_param_set_t timing_pgroup[] = { + { .pset_name = "FULL_DUP, MASTER IOMUX", + .freq_limit = ESP_SPI_SLAVE_MAX_FREQ_SYNC, + .master_limit = SPI_MASTER_FREQ_13M, + .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 = SPI_MASTER_FREQ_13M, + .dup = FULL_DUPLEX, + .master_iomux = false, + .slave_iomux = true, + .slave_tv_ns = TV_INT_CONNECT, + }, + { .pset_name = "FULL_DUP, BOTH GPIO", + .freq_limit = ESP_SPI_SLAVE_MAX_FREQ_SYNC, + .master_limit = SPI_MASTER_FREQ_10M, + .dup = FULL_DUPLEX, + .master_iomux = false, + .slave_iomux = false, + .slave_tv_ns = TV_INT_CONNECT_GPIO, + }, + { .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+12.5, + //for freq lower than 20M, the delay is 60(62.5)ns, however, the delay becomes 75ns over 26M + }, + { .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+12.5, + //for freq lower than 20M, the delay is 60(62.5)ns, however, the delay becomes 75ns over 26M + + }, + { .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+12.5, + //for freq lower than 20M, the delay is 60(62.5)ns, however, the delay becomes 75ns over 26M + + }, + { .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, + }, + { .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 SPI_MASTER_FREQ_16M +static int test_freq_mode_local[]={ + 1*1000*1000, + SPI_MASTER_FREQ_9M, //maximum freq MISO stable before next latch edge + SPI_MASTER_FREQ_13M, + SPI_MASTER_FREQ_16M, + SPI_MASTER_FREQ_20M, + SPI_MASTER_FREQ_26M, + SPI_MASTER_FREQ_40M, + 0, +}; + +static spitest_param_set_t mode_pgroup[] = { + { .pset_name = "Mode 0", + .freq_list = test_freq_mode_local, + .master_limit = SPI_MASTER_FREQ_13M, + .dup = FULL_DUPLEX, + .mode = 0, + .master_iomux = false, + .slave_iomux = true, + .slave_tv_ns = TV_INT_CONNECT, + }, + { .pset_name = "Mode 1", + .freq_list = test_freq_mode_local, + .freq_limit = SPI_MASTER_FREQ_26M, + .master_limit = SPI_MASTER_FREQ_13M, + .dup = FULL_DUPLEX, + .mode = 1, + .master_iomux = false, + .slave_iomux = true, + .slave_tv_ns = TV_INT_CONNECT, + }, + { .pset_name = "Mode 2", + .freq_list = test_freq_mode_local, + .master_limit = SPI_MASTER_FREQ_13M, + .dup = FULL_DUPLEX, + .mode = 2, + .master_iomux = false, + .slave_iomux = true, + .slave_tv_ns = TV_INT_CONNECT, + }, + { .pset_name = "Mode 3", + .freq_list = test_freq_mode_local, + .freq_limit = SPI_MASTER_FREQ_26M, + .master_limit = SPI_MASTER_FREQ_13M, + .dup = FULL_DUPLEX, + .mode = 3, + .master_iomux = false, + .slave_iomux = true, + .slave_tv_ns = TV_INT_CONNECT, + }, + { .pset_name = "Mode 0, DMA", + .freq_list = test_freq_mode_local, + .master_limit = SPI_MASTER_FREQ_13M, + .dup = FULL_DUPLEX, + .mode = 0, + .slave_dma_chan = 2, + .master_iomux = false, + .slave_iomux = true, + .slave_tv_ns = TV_INT_CONNECT, //at 16M, the MISO delay (-0.5T+(3+2)apb) equals to non-DMA mode delay (3apb). + .length_aligned = true, + }, + { .pset_name = "Mode 1, DMA", + .freq_list = test_freq_mode_local, + .freq_limit = SPI_MASTER_FREQ_26M, + .master_limit = SPI_MASTER_FREQ_13M, + .dup = FULL_DUPLEX, + .mode = 1, + .slave_dma_chan = 2, + .master_iomux = false, + .slave_iomux = true, + .slave_tv_ns = TV_INT_CONNECT, + .length_aligned = true, + }, + { .pset_name = "Mode 2, DMA", + .freq_list = test_freq_mode_local, + .master_limit = SPI_MASTER_FREQ_13M, + .dup = FULL_DUPLEX, + .mode = 2, + .slave_dma_chan = 2, + .master_iomux = false, + .slave_iomux = true, + .slave_tv_ns = TV_INT_CONNECT, //at 16M, the MISO delay (-0.5T+(3+2)apb) equals to non-DMA mode delay (3apb). + .length_aligned = true, + }, + { .pset_name = "Mode 3, DMA", + .freq_list = test_freq_mode_local, + .freq_limit = SPI_MASTER_FREQ_26M, + .master_limit = SPI_MASTER_FREQ_13M, + .dup = FULL_DUPLEX, + .mode = 3, + .slave_dma_chan = 2, + .master_iomux = false, + .slave_iomux = true, + .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 = true, + .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 = true, + .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 = true, + .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 = true, + .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 = 2, + .master_iomux = false, + .slave_iomux = true, + .slave_tv_ns = TV_INT_CONNECT+12.5, //at 16M, the MISO delay (-0.5T+(3+2)apb) equals to non-DMA mode delay (3apb). + .length_aligned = true, + }, + { .pset_name = "MISO, Mode 1, DMA", + .freq_list = test_freq_mode_local, + .dup = HALF_DUPLEX_MISO, + .mode = 1, + .slave_dma_chan = 2, + .master_iomux = false, + .slave_iomux = true, + .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 = 2, + .master_iomux = false, + .slave_iomux = true, + .slave_tv_ns = TV_INT_CONNECT+12.5, //at 16M, the MISO delay (-0.5T+(3+2)apb) equals to non-DMA mode delay (3apb). + .length_aligned = true, + }, + { .pset_name = "MISO, Mode 3, DMA", + .freq_list = test_freq_mode_local, + .dup = HALF_DUPLEX_MISO, + .mode = 3, + .slave_dma_chan = 2, + .master_iomux = false, + .slave_iomux = true, + .slave_tv_ns = TV_INT_CONNECT, + .length_aligned = true, + }, +}; +TEST_SPI_LOCAL(MODE, mode_pgroup) + +/******************************************************************************** + * Test By Master & Slave (2 boards) + ********************************************************************************/ +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, +}; + +#define TEST_SPI_MASTER_SLAVE(name, param_group) \ + PARAM_GROUP_DECLARE(name, param_group) \ + TEST_MASTER_SLAVE(name, param_group, "[spi_ms][test_env=Example_SPI_Multi_device][timeout=120]", &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_ASSERT(err == ESP_OK); +} + +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(); + buspset.miso_io_num = HSPI_IOMUX_PIN_NUM_MISO; + buspset.mosi_io_num = HSPI_IOMUX_PIN_NUM_MOSI; + buspset.sclk_io_num = HSPI_IOMUX_PIN_NUM_CLK; + //this does nothing, but avoid the driver from using native pins + if (!pset->master_iomux) buspset.quadhd_io_num = VSPI_IOMUX_PIN_NUM_MISO; + spi_device_interface_config_t devpset=SPI_DEVICE_TEST_DEFAULT_CONFIG(); + devpset.spics_io_num = HSPI_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; + TEST_ESP_OK(spi_bus_initialize(HSPI_HOST, &buspset, pset->master_dma_chan)); + TEST_ESP_OK(spi_bus_add_device(HSPI_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); + 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 ); + 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_ASSERT( err == ESP_OK ); + + 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(); + slv_buscfg.miso_io_num = VSPI_IOMUX_PIN_NUM_MISO; + slv_buscfg.mosi_io_num = VSPI_IOMUX_PIN_NUM_MOSI; + slv_buscfg.sclk_io_num = VSPI_IOMUX_PIN_NUM_CLK; + //this does nothing, but avoid the driver from using native pins + if (!pset->slave_iomux) slv_buscfg.quadhd_io_num = HSPI_IOMUX_PIN_NUM_CLK; + spi_slave_interface_config_t slvcfg=SPI_SLAVE_TEST_DEFAULT_CONFIG(); + slvcfg.spics_io_num = VSPI_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); + + TEST_ESP_OK( spi_slave_initialize(VSPI_HOST, &slv_buscfg, &slvcfg, pset->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); + 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 ); + 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(VSPI_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 = SPI_MASTER_FREQ_16M, + .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 = SPI_MASTER_FREQ_11M, + .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 = SPI_MASTER_FREQ_11M, + .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 = SPI_MASTER_FREQ_9M, + .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 SPI_MASTER_FREQ_16M +//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, + SPI_MASTER_FREQ_8M, //maximum freq MISO stable before next latch edge + SPI_MASTER_FREQ_9M, //maximum freq MISO stable before next latch edge + SPI_MASTER_FREQ_10M, + SPI_MASTER_FREQ_11M, + SPI_MASTER_FREQ_13M, + SPI_MASTER_FREQ_16M, + SPI_MASTER_FREQ_20M, + 0, +}; +static int test_freq_20M_only[]={ + SPI_MASTER_FREQ_20M, + 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 = 1, + .slave_dma_chan = 1, + .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 = 1, + .slave_dma_chan = 1, + .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 = 1, + .slave_dma_chan = 1, + .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 = 1, + .slave_dma_chan = 1, + .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 = 1, + .slave_dma_chan = 1, + }, + { .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 = 1, + .slave_dma_chan = 1, + }, + { .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 = 1, + .slave_dma_chan = 1, + }, + { .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 = 1, + .slave_dma_chan = 1, + }, +}; +TEST_SPI_MASTER_SLAVE(MODE, mode_conf)