Skip to content

Commit

Permalink
Merge branch 'feature/add_spi_ckl_source_selectable' into 'master'
Browse files Browse the repository at this point in the history
spi_master: add feature spi periph clk source selectable

Closes IDF-6289

See merge request espressif/esp-idf!21324
  • Loading branch information
wanckl committed Jan 18, 2023
2 parents 2f1d30d + 1841458 commit e7c520e
Show file tree
Hide file tree
Showing 46 changed files with 546 additions and 142 deletions.
9 changes: 9 additions & 0 deletions components/driver/Kconfig
Expand Up @@ -111,6 +111,15 @@ menu "Driver Configurations"
Also you can forbid the ISR being disabled during flash writing
access, by add ESP_INTR_FLAG_IRAM when initializing the driver.

config SPI_SUPPRESS_FREQ_MACRO_DEPRECATE_WARN
bool "Suppress SPI_MASTER_FREQ_nM deprecation warning"
default n
help
Select this option to suppress the deprecation warning when using
`SPI_MASTER_FREQ_nM` macros.
SPI clock source can be set into different clock sources. These
macros are no longer guaranteed to be accurate.

endmenu # SPI Configuration

menu "TWAI Configuration"
Expand Down
26 changes: 21 additions & 5 deletions components/driver/include/driver/spi_master.h
Expand Up @@ -8,12 +8,25 @@

#include "esp_err.h"
#include "freertos/FreeRTOS.h"
#include "hal/spi_types.h"
//for spi_bus_initialization funcions. to be back-compatible
#include "driver/spi_common.h"

/** SPI master clock is divided by 80MHz apb clock. Below defines are example frequencies, and are accurate. Be free to specify a random frequency, it will be rounded to closest frequency (to macros below if above 8MHz).
* 8MHz
/** SPI master clock is divided by clock source. Below defines are example frequencies. Be free to specify a random frequency, it will be rounded to closest frequency (to macros below if above 8MHz).
*/
#if !CONFIG_SPI_SUPPRESS_FREQ_MACRO_DEPRECATE_WARN
#define SPI_MASTER_FREQ_8M _Pragma("GCC warning \"'SPI_MASTER_FREQ_xxM' macro is deprecated\"") (APB_CLK_FREQ/10)
#define SPI_MASTER_FREQ_9M _Pragma("GCC warning \"'SPI_MASTER_FREQ_xxM' macro is deprecated\"") (APB_CLK_FREQ/9) ///< 8.89MHz
#define SPI_MASTER_FREQ_10M _Pragma("GCC warning \"'SPI_MASTER_FREQ_xxM' macro is deprecated\"") (APB_CLK_FREQ/8) ///< 10MHz
#define SPI_MASTER_FREQ_11M _Pragma("GCC warning \"'SPI_MASTER_FREQ_xxM' macro is deprecated\"") (APB_CLK_FREQ/7) ///< 11.43MHz
#define SPI_MASTER_FREQ_13M _Pragma("GCC warning \"'SPI_MASTER_FREQ_xxM' macro is deprecated\"") (APB_CLK_FREQ/6) ///< 13.33MHz
#define SPI_MASTER_FREQ_16M _Pragma("GCC warning \"'SPI_MASTER_FREQ_xxM' macro is deprecated\"") (APB_CLK_FREQ/5) ///< 16MHz
#define SPI_MASTER_FREQ_20M _Pragma("GCC warning \"'SPI_MASTER_FREQ_xxM' macro is deprecated\"") (APB_CLK_FREQ/4) ///< 20MHz
#define SPI_MASTER_FREQ_26M _Pragma("GCC warning \"'SPI_MASTER_FREQ_xxM' macro is deprecated\"") (APB_CLK_FREQ/3) ///< 26.67MHz
#define SPI_MASTER_FREQ_40M _Pragma("GCC warning \"'SPI_MASTER_FREQ_xxM' macro is deprecated\"") (APB_CLK_FREQ/2) ///< 40MHz
#define SPI_MASTER_FREQ_80M _Pragma("GCC warning \"'SPI_MASTER_FREQ_xxM' macro is deprecated\"") (APB_CLK_FREQ/1) ///< 80MHz

#else
#define SPI_MASTER_FREQ_8M (APB_CLK_FREQ/10)
#define SPI_MASTER_FREQ_9M (APB_CLK_FREQ/9) ///< 8.89MHz
#define SPI_MASTER_FREQ_10M (APB_CLK_FREQ/8) ///< 10MHz
Expand All @@ -24,6 +37,8 @@
#define SPI_MASTER_FREQ_26M (APB_CLK_FREQ/3) ///< 26.67MHz
#define SPI_MASTER_FREQ_40M (APB_CLK_FREQ/2) ///< 40MHz
#define SPI_MASTER_FREQ_80M (APB_CLK_FREQ/1) ///< 80MHz
#endif //!CONFIG_SPI_SUPPRESS_FREQ_MACRO_DEPRECATE_WARN

#ifdef __cplusplus
extern "C"
{
Expand Down Expand Up @@ -63,8 +78,9 @@ typedef struct {
- 2: (1, 0)
- 3: (1, 1)
*/
uint16_t duty_cycle_pos; ///< Duty cycle of positive clock, in 1/256th increments (128 = 50%/50% duty). Setting this to 0 (=not setting it) is equivalent to setting this to 128.
uint16_t cs_ena_pretrans; ///< Amount of SPI bit-cycles the cs should be activated before the transmission (0-16). This only works on half-duplex transactions.
spi_clock_source_t clock_source;///< Select SPI clock source, `SPI_CLK_SRC_DEFAULT` by default.
uint16_t duty_cycle_pos; ///< Duty cycle of positive clock, in 1/256th increments (128 = 50%/50% duty). Setting this to 0 (=not setting it) is equivalent to setting this to 128.
uint16_t cs_ena_pretrans; ///< Amount of SPI bit-cycles the cs should be activated before the transmission (0-16). This only works on half-duplex transactions.
uint8_t cs_ena_posttrans; ///< Amount of SPI bit-cycles the cs should stay active after the transmission (0-16)
int clock_speed_hz; ///< Clock speed, divisors of 80MHz, in Hz. See ``SPI_MASTER_FREQ_*``.
int input_delay_ns; /**< Maximum data valid time of slave. The time required between SCLK and MISO
Expand Down Expand Up @@ -356,7 +372,7 @@ esp_err_t spi_device_get_actual_freq(spi_device_handle_t handle, int* freq_khz);
*
* @return Actual working frequency that most fit.
*/
int spi_get_actual_clock(int fapb, int hz, int duty_cycle);
int spi_get_actual_clock(int fapb, int hz, int duty_cycle) __attribute__((deprecated("Please use spi_device_get_actual_freq instead")));

/**
* @brief Calculate the timing settings of specified frequency and settings.
Expand Down
36 changes: 23 additions & 13 deletions components/driver/spi_master.c
Expand Up @@ -113,16 +113,14 @@ We have two bits to control the interrupt:
#include <string.h>
#include "esp_private/spi_common_internal.h"
#include "driver/spi_master.h"

#include "clk_tree.h"
#include "esp_log.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "soc/soc_memory_layout.h"
#include "driver/gpio.h"
#include "hal/spi_hal.h"
#include "esp_heap_caps.h"
//Temporarily include esp_clk.h, will be replaced by clock tree API
#include "esp_private/esp_clk.h"


typedef struct spi_device_t spi_device_t;
Expand Down Expand Up @@ -156,6 +154,7 @@ typedef struct {

struct spi_device_t {
int id;
int real_clk_freq_hz;
QueueHandle_t trans_queue;
QueueHandle_t ret_queue;
spi_device_interface_config_t cfg;
Expand Down Expand Up @@ -286,17 +285,28 @@ static esp_err_t spi_master_deinit_driver(void* arg)

void spi_get_timing(bool gpio_is_used, int input_delay_ns, int eff_clk, int* dummy_o, int* cycles_remain_o)
{
#ifdef CONFIG_IDF_TARGET_ESP32
int timing_dummy;
int timing_miso_delay;

spi_hal_cal_timing(eff_clk, gpio_is_used, input_delay_ns, &timing_dummy, &timing_miso_delay);
spi_hal_cal_timing(APB_CLK_FREQ, eff_clk, gpio_is_used, input_delay_ns, &timing_dummy, &timing_miso_delay);
if (dummy_o) *dummy_o = timing_dummy;
if (cycles_remain_o) *cycles_remain_o = timing_miso_delay;
#else
//TODO: IDF-6578
ESP_LOGW(SPI_TAG, "This func temporary not supported for current target!");
#endif
}

int spi_get_freq_limit(bool gpio_is_used, int input_delay_ns)
{
#ifdef CONFIG_IDF_TARGET_ESP32
return spi_hal_get_freq_limit(gpio_is_used, input_delay_ns);
#else
//TODO: IDF-6578
ESP_LOGW(SPI_TAG, "This func temporary not supported for current target!");
return 0;
#endif
}

/*
Expand All @@ -320,9 +330,9 @@ esp_err_t spi_bus_add_device(spi_host_device_t host_id, const spi_device_interfa
spi_host_t *host = bus_driver_ctx[host_id];
const spi_bus_attr_t* bus_attr = host->bus_attr;
SPI_CHECK(dev_config->spics_io_num < 0 || GPIO_IS_VALID_OUTPUT_GPIO(dev_config->spics_io_num), "spics pin invalid", ESP_ERR_INVALID_ARG);
uint32_t apb_clk_freq_hz = esp_clk_apb_freq();
assert((apb_clk_freq_hz == 80 * 1000 * 1000) || (apb_clk_freq_hz == 40 * 1000 * 1000) || (apb_clk_freq_hz == 48 * 1000 * 1000));
SPI_CHECK((dev_config->clock_speed_hz > 0) && (dev_config->clock_speed_hz <= apb_clk_freq_hz) , "invalid sclk speed", ESP_ERR_INVALID_ARG);
uint32_t clock_source_hz;
clk_tree_src_get_freq_hz((dev_config->clock_source == 0)?SPI_CLK_SRC_DEFAULT:dev_config->clock_source, CLK_TREE_SRC_FREQ_PRECISION_APPROX, &clock_source_hz);
SPI_CHECK((dev_config->clock_speed_hz > 0) && (dev_config->clock_speed_hz <= clock_source_hz), "invalid sclk speed", ESP_ERR_INVALID_ARG);
#ifdef CONFIG_IDF_TARGET_ESP32
//The hardware looks like it would support this, but actually setting cs_ena_pretrans when transferring in full
//duplex mode does absolutely nothing on the ESP32.
Expand Down Expand Up @@ -355,10 +365,9 @@ esp_err_t spi_bus_add_device(spi_host_device_t host_id, const spi_device_interfa
int duty_cycle = (dev_config->duty_cycle_pos==0) ? 128 : dev_config->duty_cycle_pos;
int use_gpio = !(bus_attr->flags & SPICOMMON_BUSFLAG_IOMUX_PINS);
spi_hal_timing_param_t timing_param = {
.clk_src_hz = esp_clk_apb_freq(),
.clk_sel = SPI_CLK_APB, //Currently, SPI driver only set SPI to APB clock. SPI is not supposed to be used during sleep modes.
.half_duplex = half_duplex,
.no_compensate = no_compensate,
.clk_src_hz = clock_source_hz,
.expected_freq = dev_config->clock_speed_hz,
.duty_cycle = duty_cycle,
.input_delay_ns = dev_config->input_delay_ns,
Expand All @@ -369,6 +378,7 @@ esp_err_t spi_bus_add_device(spi_host_device_t host_id, const spi_device_interfa
spi_hal_timing_conf_t temp_timing_conf;
int freq;
esp_err_t ret = spi_hal_cal_clock_conf(&timing_param, &freq, &temp_timing_conf);
temp_timing_conf.clock_source = dev_config->clock_source;
SPI_CHECK(ret==ESP_OK, "assigned clock speed not supported", ret);

//Allocate memory for device
Expand All @@ -395,6 +405,7 @@ esp_err_t spi_bus_add_device(spi_host_device_t host_id, const spi_device_interfa
//We want to save a copy of the dev config in the dev struct.
memcpy(&dev->cfg, dev_config, sizeof(spi_device_interface_config_t));
dev->cfg.duty_cycle_pos = duty_cycle;
dev->real_clk_freq_hz = freq;
// TODO: if we have to change the apb clock among transactions, re-calculate this each time the apb clock lock is locked.

//Set CS pin, CS options
Expand Down Expand Up @@ -476,10 +487,7 @@ esp_err_t spi_device_get_actual_freq(spi_device_handle_t handle, int* freq_khz)
return ESP_ERR_INVALID_ARG;
}

int dev_required_freq = ((spi_device_t*)handle)->cfg.clock_speed_hz;
int dev_duty_cycle = ((spi_device_t*)handle)->cfg.duty_cycle_pos;
*freq_khz = spi_get_actual_clock(esp_clk_apb_freq(), dev_required_freq, dev_duty_cycle);

*freq_khz = handle->real_clk_freq_hz / 1000;
return ESP_OK;
}

Expand Down Expand Up @@ -846,6 +854,8 @@ esp_err_t SPI_MASTER_ATTR spi_device_queue_trans(spi_device_handle_t handle, spi
if (ret != ESP_OK) return ret;

#ifdef CONFIG_PM_ENABLE
// though clock source is selectable, read/write reg and mem of spi peripherial still use APB
// and dma still use APB, so pm_lock is still needed
esp_pm_lock_acquire(host->bus_attr->pm_lock);
#endif
//Send to queue and invoke the ISR.
Expand Down
Expand Up @@ -145,25 +145,25 @@
#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 ESP_SPI_SLAVE_MAX_FREQ 20 * 1000 * 1000
#define ESP_SPI_SLAVE_MAX_FREQ_SYNC 40 * 1000 * 1000

#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, \
1 * 1000 * 1000, \
8 * 1000 * 1000, \
9 * 1000 * 1000, \
10 * 1000 * 1000, \
11 * 1000 * 1000, \
13 * 1000 * 1000, \
16 * 1000 * 1000, \
20 * 1000 * 1000, \
26 * 1000 * 1000, \
40 * 1000 * 1000, \
80 * 1000 * 1000, \
0,\
}

Expand Down
85 changes: 62 additions & 23 deletions components/driver/test_apps/spi/master/main/test_spi_master.c
Expand Up @@ -19,6 +19,7 @@
#include "esp_private/spi_common_internal.h"
#include "esp_private/esp_clk.h"
#include "esp_heap_caps.h"
#include "clk_tree.h"
#include "esp_log.h"
#include "test_utils.h"
#include "test_spi_utils.h"
Expand All @@ -29,14 +30,15 @@ const static char TAG[] = "test_spi";
// There is no input-only pin except on esp32 and esp32s2
#define TEST_SOC_HAS_INPUT_ONLY_PINS (CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2)

static void check_spi_pre_n_for(int clk, int pre, int n)
static void check_spi_pre_n_for(spi_clock_source_t clock_source, int clk, int pre, int n)
{
spi_device_handle_t handle;

spi_device_interface_config_t devcfg = {
.command_bits = 0,
.address_bits = 0,
.dummy_bits = 0,
.clock_source = clock_source,
.clock_speed_hz = clk,
.duty_cycle_pos = 128,
.mode = 0,
Expand All @@ -63,41 +65,78 @@ static void check_spi_pre_n_for(int clk, int pre, int n)
TEST_ESP_OK(spi_bus_remove_device(handle));
}

#define TEST_CLK_TIMES 8
/**
* In this test, SPI Clock Calculation:
* Fspi = Fclk_spi_mst / (pre + n)
*
* For each item:
* {freq, pre, n}
*/
#define TEST_CLK_PARAM_APB_80 {{1, SOC_SPI_MAX_PRE_DIVIDER, 64}, {100000, 16, 50}, {333333, 4, 60}, {800000, 2, 50}, {900000, 2, 44}, {8000000, 1, 10}, {20000000, 1, 4}, {26000000, 1, 3} }
#define TEST_CLK_PARAM_APB_40 {{1, SOC_SPI_MAX_PRE_DIVIDER, 64}, {100000, 8, 50}, {333333, 2, 60}, {800000, 1, 50}, {900000, 1, 44}, {8000000, 1, 5}, {10000000, 1, 4}, {20000000, 1, 2} }
#define TEST_CLK_TIMES 8
struct test_clk_param_group_t
{
uint32_t clk_param_80m[TEST_CLK_TIMES][3];
uint32_t clk_param_40m[TEST_CLK_TIMES][3];
uint32_t clk_param_17m[TEST_CLK_TIMES][3];
} test_clk_param = {
{{1, SOC_SPI_MAX_PRE_DIVIDER, 64}, {100000, 16, 50}, {333333, 4, 60}, {800000, 2, 50}, {900000, 2, 44}, {8000000, 1, 10}, {20000000, 1, 4}, {26000000, 1, 3} },
{{1, SOC_SPI_MAX_PRE_DIVIDER, 64}, {100000, 8, 50}, {333333, 2, 60}, {800000, 1, 50}, {900000, 1, 44}, {8000000, 1, 5}, {10000000, 1, 4}, {20000000, 1, 2} },
{{1, SOC_SPI_MAX_PRE_DIVIDER, 64}, {100000, 5, 35}, {333333, 1, 53}, {800000, 1, 22}, {900000, 1, 19}, {8000000, 1, 2}, {10000000, 1, 2}, {15000000, 1, 1} },
};


TEST_CASE("SPI Master clockdiv calculation routines", "[spi]")
{
spi_bus_config_t buscfg = {
.mosi_io_num = PIN_NUM_MOSI,
.miso_io_num = PIN_NUM_MISO,
.sclk_io_num = PIN_NUM_CLK,
.quadwp_io_num = -1,
.quadhd_io_num = -1
};
spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG();
TEST_ESP_OK(spi_bus_initialize(TEST_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO));
uint32_t clock_source_hz;
// Test main clock source
#if SOC_SPI_SUPPORT_CLK_PLL_F80M
clk_tree_src_get_freq_hz(SPI_CLK_SRC_PLL_F80M, CLK_TREE_SRC_FREQ_PRECISION_APPROX, &clock_source_hz);
printf("\nTest clock source PLL_80M = %ld\n", clock_source_hz);
TEST_ASSERT((80 * 1000 * 1000) == clock_source_hz);
for (int i = 0; i < TEST_CLK_TIMES; i++) {
check_spi_pre_n_for(SPI_CLK_SRC_PLL_F80M, test_clk_param.clk_param_80m[i][0], test_clk_param.clk_param_80m[i][1], test_clk_param.clk_param_80m[i][2]);
}
#endif

uint32_t apb_freq_hz = esp_clk_apb_freq();
if (apb_freq_hz == (80 * 1000 * 1000)) {
uint32_t clk_param[TEST_CLK_TIMES][3] = TEST_CLK_PARAM_APB_80;
for (int i = 0; i < TEST_CLK_TIMES; i++) {
check_spi_pre_n_for(clk_param[i][0], clk_param[i][1], clk_param[i][2]);
}
} else {
TEST_ASSERT(apb_freq_hz == (40 * 1000 * 1000));
uint32_t clk_param[TEST_CLK_TIMES][3] = TEST_CLK_PARAM_APB_40;
for (int i = 0; i < TEST_CLK_TIMES; i++) {
check_spi_pre_n_for(clk_param[i][0], clk_param[i][1], clk_param[i][2]);
}
#if SOC_SPI_SUPPORT_CLK_PLL_F40M
clk_tree_src_get_freq_hz(SPI_CLK_SRC_PLL_F40M, CLK_TREE_SRC_FREQ_PRECISION_APPROX, &clock_source_hz);
printf("\nTest clock source PLL_40M = %ld\n", clock_source_hz);
TEST_ASSERT((40 * 1000 * 1000) == clock_source_hz);
for (int i = 0; i < TEST_CLK_TIMES; i++) {
check_spi_pre_n_for(SPI_CLK_SRC_PLL_F40M, test_clk_param.clk_param_40m[i][0], test_clk_param.clk_param_40m[i][1], test_clk_param.clk_param_40m[i][2]);
}
#endif

#if SOC_SPI_SUPPORT_CLK_APB
clk_tree_src_get_freq_hz(SPI_CLK_SRC_APB, CLK_TREE_SRC_FREQ_PRECISION_APPROX, &clock_source_hz);
printf("\nTest clock source APB = %ld\n", clock_source_hz);
TEST_ASSERT((80 * 1000 * 1000) == clock_source_hz);
for (int i = 0; i < TEST_CLK_TIMES; i++) {
check_spi_pre_n_for(SPI_CLK_SRC_APB, test_clk_param.clk_param_80m[i][0], test_clk_param.clk_param_80m[i][1], test_clk_param.clk_param_80m[i][2]);
}
#endif

// Test XTAL clock source
#if SOC_SPI_SUPPORT_CLK_XTAL
clk_tree_src_get_freq_hz(SPI_CLK_SRC_XTAL, CLK_TREE_SRC_FREQ_PRECISION_APPROX, &clock_source_hz);
printf("\nTest clock source XTAL = %ld\n", clock_source_hz);
TEST_ASSERT((40 * 1000 * 1000) == clock_source_hz);
for (int i = 0; i < TEST_CLK_TIMES; i++) {
check_spi_pre_n_for(SPI_CLK_SRC_XTAL, test_clk_param.clk_param_40m[i][0], test_clk_param.clk_param_40m[i][1], test_clk_param.clk_param_40m[i][2]);
}
#endif

// Test RC fast osc clock source
#if SOC_SPI_SUPPORT_CLK_RC_FAST
clk_tree_src_get_freq_hz(SPI_CLK_SRC_RC_FAST, CLK_TREE_SRC_FREQ_PRECISION_APPROX, &clock_source_hz);
printf("\nTest clock source RC_FAST = %ld\n", clock_source_hz);
TEST_ASSERT((17500000) == clock_source_hz);
for (int i = 0; i < TEST_CLK_TIMES; i++) {
check_spi_pre_n_for(SPI_CLK_SRC_RC_FAST, test_clk_param.clk_param_17m[i][0], test_clk_param.clk_param_17m[i][1], test_clk_param.clk_param_17m[i][2]);
}
#endif

TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST));
}
Expand Down

0 comments on commit e7c520e

Please sign in to comment.