spi: add new test for timing and mode

New unit tests added
------------------------

**Local:** Local test uses the GPIO matrix to connect the master and the
slave on the same board. When the master needs the iomux, the master
uses the GPIOs of its own, the slave connect to the pins by GPIO matrix;
When the slave needs the iomux, the slave uses the GPIOs of its own, the
master connects to the pins by GPIO matrix.

- Provide a new unit test which performs freq scanning in mode 0. It
scans frequency of 1M, 8M, 9M and all frequency steps up to the maximum
frequency allowed.

**M & S**: Master & slave tests performs the test with two boards. The
master and slave use iomux or gpio matrix according to the config.

- Provide a new unit test which performs freq scanning in mode 0. It
scans frequency of 1M, 8M, 9M and all frequency steps up to the maximum
frequency allowed.

- Provide a new unit test which performs mode test with significant
frequencies. It tests mode 0,1,2,3 with low frequency, and the maximum
frequency allowed.
This commit is contained in:
michael 2018-09-21 16:48:04 +08:00 committed by Michael (XIAO Xufeng)
parent 155006243e
commit 41e58bc419
5 changed files with 1392 additions and 546 deletions

View File

@ -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:

View File

@ -0,0 +1,187 @@
#ifndef _TEST_COMMON_SPI_H_
#define _TEST_COMMON_SPI_H_
#include <esp_types.h>
#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 <string.h>
#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_

View File

@ -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;
}

View File

@ -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<sizeof(timing_speed_array)/sizeof(int); j++ ) {
if (timing_speed_array[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 {

View File

@ -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)