mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
feat(driver_twai): new driver sleep retention support
This commit is contained in:
@@ -286,6 +286,16 @@ static void _node_destroy(twai_onchip_ctx_t *twai_ctx)
|
||||
if (twai_ctx->pm_lock) {
|
||||
esp_pm_lock_delete(twai_ctx->pm_lock);
|
||||
}
|
||||
#endif
|
||||
#if SOC_TWAI_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
|
||||
const sleep_retention_module_t retention_id = twai_reg_retention_info[twai_ctx->ctrlr_id].module_id;
|
||||
if (sleep_retention_is_module_created(retention_id)) {
|
||||
assert(sleep_retention_is_module_inited(retention_id));
|
||||
sleep_retention_module_free(retention_id);
|
||||
}
|
||||
if (sleep_retention_is_module_inited(retention_id)) {
|
||||
sleep_retention_module_deinit(retention_id);
|
||||
}
|
||||
#endif
|
||||
if (twai_ctx->intr_hdl) {
|
||||
esp_intr_free(twai_ctx->intr_hdl);
|
||||
@@ -632,13 +642,25 @@ static esp_err_t _node_parse_rx(twai_node_handle_t node, twai_frame_t *rx_frame)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#if SOC_TWAI_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
|
||||
static esp_err_t _node_create_sleep_retention_cb(void *obj)
|
||||
{
|
||||
twai_onchip_ctx_t *twai_ctx = (twai_onchip_ctx_t *)obj;
|
||||
return sleep_retention_entries_create(twai_reg_retention_info[twai_ctx->ctrlr_id].entry_array,
|
||||
twai_reg_retention_info[twai_ctx->ctrlr_id].array_size,
|
||||
REGDMA_LINK_PRI_TWAI,
|
||||
twai_reg_retention_info[twai_ctx->ctrlr_id].module_id);
|
||||
}
|
||||
#endif
|
||||
/* --------------------------------- Public --------------------------------- */
|
||||
esp_err_t twai_new_node_onchip(const twai_onchip_node_config_t *node_config, twai_node_handle_t *node_ret)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_RETURN_ON_FALSE((node_config->tx_queue_depth > 0) || node_config->flags.enable_listen_only, ESP_ERR_INVALID_ARG, TAG, "tx_queue_depth at least 1");
|
||||
ESP_RETURN_ON_FALSE(!node_config->intr_priority || (BIT(node_config->intr_priority) & ESP_INTR_FLAG_LOWMED), ESP_ERR_INVALID_ARG, TAG, "Invalid intr_priority level");
|
||||
|
||||
#if !SOC_TWAI_SUPPORT_SLEEP_RETENTION
|
||||
ESP_RETURN_ON_FALSE(!node_config->flags.sleep_allow_pd, ESP_ERR_NOT_SUPPORTED, TAG, "sleep retention is not supported on this target");
|
||||
#endif
|
||||
// Allocate TWAI node from internal memory because it contains atomic variable
|
||||
twai_onchip_ctx_t *node = heap_caps_calloc(1, sizeof(twai_onchip_ctx_t) + twai_hal_get_mem_requirment(), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
||||
ESP_RETURN_ON_FALSE(node, ESP_ERR_NO_MEM, TAG, "No mem");
|
||||
@@ -679,6 +701,25 @@ esp_err_t twai_new_node_onchip(const twai_onchip_node_config_t *node_config, twa
|
||||
#endif
|
||||
node->timestamp_freq_hz = node_config->timestamp_resolution_hz;
|
||||
}
|
||||
#if SOC_TWAI_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
|
||||
const sleep_retention_module_t retention_id = twai_reg_retention_info[node->ctrlr_id].module_id;
|
||||
sleep_retention_module_init_param_t init_param = {
|
||||
.cbs = {
|
||||
.create = {
|
||||
.handle = _node_create_sleep_retention_cb,
|
||||
.arg = node,
|
||||
},
|
||||
},
|
||||
.depends = RETENTION_MODULE_BITMAP_INIT(CLOCK_SYSTEM)
|
||||
};
|
||||
if (sleep_retention_module_init(retention_id, &init_param) == ESP_OK) {
|
||||
if ((node_config->flags.sleep_allow_pd) && (sleep_retention_module_allocate(retention_id) != ESP_OK)) {
|
||||
ESP_LOGW(TAG, "sleep retention prepare failed, power will hold on");
|
||||
}
|
||||
} else {
|
||||
ESP_LOGW(TAG, "sleep retention init failed, twai may offline after sleep");
|
||||
}
|
||||
#endif // SOC_TWAI_SUPPORT_SLEEP_RETENTION
|
||||
_lock_release(&s_platform.intr_mutex);
|
||||
|
||||
#if CONFIG_PM_ENABLE
|
||||
@@ -732,10 +773,9 @@ esp_err_t twai_new_node_onchip(const twai_onchip_node_config_t *node_config, twa
|
||||
|
||||
*node_ret = &node->api_base;
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
if (node) {
|
||||
_lock_release(&s_platform.intr_mutex);
|
||||
_node_destroy(node);
|
||||
}
|
||||
_lock_release(&s_platform.intr_mutex);
|
||||
_node_destroy(node);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ typedef struct {
|
||||
uint32_t enable_loopback: 1; /**< The TWAI controller receives back frames that it sends out, but does not acknowledge them */
|
||||
uint32_t enable_listen_only: 1; /**< No transmissions or acknowledgements. The controller only monitors the bus without participating */
|
||||
uint32_t no_receive_rtr: 1; /**< Don't receive remote frames */
|
||||
uint32_t sleep_allow_pd: 1; /**< Allow power down during sleep to save power, driver will backup/restore the TWAI registers to guarantee the peripheral features. */
|
||||
} flags; /**< Misc configuration flags */
|
||||
} twai_onchip_node_config_t;
|
||||
|
||||
|
||||
@@ -2,6 +2,9 @@ set(srcs "test_app_main.c")
|
||||
|
||||
if(CONFIG_SOC_TWAI_SUPPORTED)
|
||||
list(APPEND srcs "test_twai_common.cpp" "test_twai_network.cpp")
|
||||
if(CONFIG_SOC_LIGHT_SLEEP_SUPPORTED)
|
||||
list(APPEND srcs "test_twai_sleep.c")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_TWAI_FD_SUPPORTED)
|
||||
@@ -10,6 +13,6 @@ endif()
|
||||
|
||||
idf_component_register(
|
||||
SRCS ${srcs}
|
||||
PRIV_REQUIRES esp_driver_twai esp_timer esp_driver_uart esp_psram esp_driver_gpio
|
||||
PRIV_REQUIRES esp_driver_twai esp_timer esp_driver_uart esp_psram esp_driver_gpio esp_pm
|
||||
WHOLE_ARCHIVE
|
||||
)
|
||||
|
||||
@@ -19,12 +19,17 @@
|
||||
#include "esp_twai.h"
|
||||
#include "esp_twai_onchip.h"
|
||||
#include "hal/twai_periph.h"
|
||||
#include "esp_private/gpio.h"
|
||||
#include "soc/gpio_sig_map.h"
|
||||
#include "esp_private/gpio.h"
|
||||
#include "driver/uart.h" // for baudrate detection
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32H4
|
||||
#define TEST_TX_GPIO GPIO_NUM_2
|
||||
#define TEST_RX_GPIO GPIO_NUM_3
|
||||
#else
|
||||
#define TEST_TX_GPIO GPIO_NUM_4
|
||||
#define TEST_RX_GPIO GPIO_NUM_5
|
||||
#endif
|
||||
#define TEST_TWAI_QUEUE_DEPTH 5
|
||||
#define TEST_TRANS_LEN 100
|
||||
#define TEST_FRAME_LEN 7
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include "unity.h"
|
||||
#include "test_utils.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_pm.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_rom_sys.h"
|
||||
#include "esp_private/esp_pmu.h"
|
||||
#include "esp_private/sleep_cpu.h"
|
||||
#include "esp_private/esp_sleep_internal.h"
|
||||
#include "esp_private/gpio.h"
|
||||
#include "hal/twai_periph.h"
|
||||
#include "esp_twai.h"
|
||||
#include "esp_twai_onchip.h"
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32H4
|
||||
#define TEST_TX_GPIO GPIO_NUM_2
|
||||
#define TEST_RX_GPIO GPIO_NUM_3
|
||||
#else
|
||||
#define TEST_TX_GPIO GPIO_NUM_4
|
||||
#define TEST_RX_GPIO GPIO_NUM_5
|
||||
#endif
|
||||
#define TEST_TWAI_QUEUE_DEPTH 5
|
||||
#define TEST_TRANS_LEN 100
|
||||
#define TEST_FRAME_LEN 7
|
||||
#define TEST_FRAME_NUM howmany(TEST_TRANS_LEN, TEST_FRAME_LEN)
|
||||
|
||||
static IRAM_ATTR bool test_sleep_rx_cb(twai_node_handle_t handle, const twai_rx_done_event_data_t *edata, void *user_ctx)
|
||||
{
|
||||
twai_frame_t *rx_frame = (twai_frame_t *)user_ctx;
|
||||
if (ESP_OK == twai_node_receive_from_isr(handle, rx_frame)) {
|
||||
ESP_EARLY_LOGI("RX", "timestamp %llu frame %x [%d] %s", rx_frame->header.timestamp, rx_frame->header.id, rx_frame->header.dlc, rx_frame->buffer);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
TEST_CASE("twai sleep retention", "[twai]")
|
||||
{
|
||||
// Prepare a TOP PD sleep
|
||||
TEST_ESP_OK(esp_sleep_enable_timer_wakeup(1 * 1000 * 1000));
|
||||
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU
|
||||
TEST_ESP_OK(sleep_cpu_configure(true));
|
||||
#endif
|
||||
esp_sleep_context_t sleep_ctx = {};
|
||||
esp_sleep_set_sleep_context(&sleep_ctx);
|
||||
|
||||
twai_node_handle_t node_hdl[SOC_TWAI_CONTROLLER_NUM];
|
||||
twai_onchip_node_config_t node_config = {};
|
||||
node_config.io_cfg.quanta_clk_out = GPIO_NUM_NC;
|
||||
node_config.io_cfg.bus_off_indicator = GPIO_NUM_NC;
|
||||
node_config.bit_timing.bitrate = 800000;
|
||||
node_config.timestamp_resolution_hz = 1000000;
|
||||
node_config.tx_queue_depth = TEST_FRAME_NUM;
|
||||
node_config.flags.enable_loopback = true;
|
||||
node_config.flags.enable_self_test = true;
|
||||
|
||||
twai_frame_t tx_frame = {};
|
||||
tx_frame.buffer = (uint8_t *)"sleep";
|
||||
tx_frame.buffer_len = strlen((const char *)tx_frame.buffer);
|
||||
|
||||
uint8_t rx_buffer[TWAI_FRAME_MAX_LEN] = {0};
|
||||
twai_frame_t rx_frame = {};
|
||||
rx_frame.buffer = rx_buffer;
|
||||
rx_frame.buffer_len = sizeof(rx_buffer);
|
||||
|
||||
twai_event_callbacks_t user_cbs = {};
|
||||
user_cbs.on_rx_done = test_sleep_rx_cb;
|
||||
for (int power = 0; power < 2; power++) {
|
||||
node_config.flags.sleep_allow_pd = power;
|
||||
#if !SOC_TWAI_SUPPORT_SLEEP_RETENTION
|
||||
node_config.flags.sleep_allow_pd = false;
|
||||
#endif
|
||||
printf("\n===> Testing sleep power %s\n", node_config.flags.sleep_allow_pd ? "DOWN" : "HOLD");
|
||||
for (int node = 0; node < SOC_TWAI_CONTROLLER_NUM; node++) {
|
||||
node_config.io_cfg.tx = TEST_TX_GPIO;
|
||||
node_config.io_cfg.rx = TEST_TX_GPIO; // Using same pin for test without transceiver
|
||||
TEST_ESP_OK(twai_new_node_onchip(&node_config, &node_hdl[node]));
|
||||
TEST_ESP_OK(twai_node_register_event_callbacks(node_hdl[node], &user_cbs, &rx_frame));
|
||||
}
|
||||
|
||||
for (int node = 0; node < SOC_TWAI_CONTROLLER_NUM; node++) {
|
||||
printf("\nTWAI%d\n", node);
|
||||
// fix signal connections after all nodes are created
|
||||
gpio_matrix_input(TEST_TX_GPIO, twai_periph_signals[node].rx_sig, false);
|
||||
gpio_matrix_output(TEST_TX_GPIO, twai_periph_signals[node].tx_sig, false, false);
|
||||
|
||||
// test with 2 transactions
|
||||
for (int i = 1; i < 3; i++) {
|
||||
printf("Go to sleep ...\n");
|
||||
uint64_t time_before = esp_timer_get_time();
|
||||
TEST_ESP_OK(esp_light_sleep_start());
|
||||
#if SOC_TWAI_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
|
||||
printf("Waked up! sleep %d power down %ld\n", !sleep_ctx.sleep_request_result, sleep_ctx.sleep_flags & PMU_SLEEP_PD_TOP);
|
||||
// check if the peripheral is powered down as expected
|
||||
TEST_ASSERT_EQUAL(node_config.flags.sleep_allow_pd ? PMU_SLEEP_PD_TOP : 0, sleep_ctx.sleep_flags & PMU_SLEEP_PD_TOP);
|
||||
#endif
|
||||
TEST_ESP_OK(twai_node_enable(node_hdl[node]));
|
||||
|
||||
tx_frame.header.id = i + node * 0x10;
|
||||
memset(&rx_frame.header, 0, sizeof(rx_frame.header));
|
||||
memset(rx_buffer, 0, sizeof(rx_buffer));
|
||||
// Waiting 1 second after node_enable to check timestamp correctness after wakeup
|
||||
esp_rom_delay_us(1000 * 1000);
|
||||
TEST_ESP_OK(twai_node_transmit(node_hdl[node], &tx_frame, 100));
|
||||
TEST_ESP_OK(twai_node_transmit_wait_all_done(node_hdl[node], -1));
|
||||
TEST_ASSERT_EQUAL_HEX8_ARRAY(tx_frame.buffer, rx_buffer, tx_frame.buffer_len);
|
||||
TEST_ASSERT_EQUAL_UINT32(tx_frame.header.id, rx_frame.header.id);
|
||||
// sleep 1s, wait 1s, so diff should be 2000000us
|
||||
printf("Time diff: %llu, expect 2000000\n", rx_frame.header.timestamp - time_before);
|
||||
TEST_ASSERT_INT32_WITHIN(10000, 2000000, rx_frame.header.timestamp - time_before);
|
||||
time_before = rx_frame.header.timestamp;
|
||||
TEST_ESP_OK(twai_node_disable(node_hdl[node]));
|
||||
}
|
||||
}
|
||||
for (int node = 0; node < SOC_TWAI_CONTROLLER_NUM; node++) {
|
||||
TEST_ESP_OK(twai_node_delete(node_hdl[node]));
|
||||
}
|
||||
}
|
||||
|
||||
esp_sleep_set_sleep_context(NULL);
|
||||
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU
|
||||
TEST_ESP_OK(sleep_cpu_configure(false));
|
||||
#endif
|
||||
}
|
||||
|
||||
#if SOC_TWAI_SUPPORT_SLEEP_RETENTION
|
||||
static void test_random_trans_generator(twai_node_handle_t node_hdl, uint32_t trans_num)
|
||||
{
|
||||
uint8_t send_pkg_ptr[TWAI_FRAME_MAX_LEN];
|
||||
twai_frame_t tx_msg = {};
|
||||
tx_msg.buffer = send_pkg_ptr;
|
||||
|
||||
printf("Sending %ld random trans ...\n", trans_num);
|
||||
for (uint32_t tx_cnt = 0; tx_cnt < trans_num; tx_cnt++) {
|
||||
tx_msg.header.id = tx_cnt | 0xf000 | (tx_cnt << 16);
|
||||
tx_msg.header.ide = !!(tx_cnt % 2);
|
||||
tx_msg.header.rtr = !!(tx_cnt % 3);
|
||||
tx_msg.buffer_len = tx_cnt % TWAI_FRAME_MAX_LEN;
|
||||
TEST_ESP_OK(twai_node_transmit(node_hdl, &tx_msg, 0));
|
||||
TEST_ESP_OK(twai_node_transmit_wait_all_done(node_hdl, 8));
|
||||
}
|
||||
}
|
||||
|
||||
static IRAM_ATTR bool test_filter_rx_done_cb(twai_node_handle_t handle, const twai_rx_done_event_data_t *edata, void *user_ctx)
|
||||
{
|
||||
uint8_t *test_ctrl = (uint8_t *)user_ctx;
|
||||
uint8_t recv_pkg_ptr[TWAI_FRAME_MAX_LEN];
|
||||
twai_frame_t rx_frame = {};
|
||||
rx_frame.buffer = recv_pkg_ptr;
|
||||
rx_frame.buffer_len = TWAI_FRAME_MAX_LEN;
|
||||
|
||||
if (ESP_OK == twai_node_receive_from_isr(handle, &rx_frame)) {
|
||||
ESP_EARLY_LOGI("Recv", "RX id 0x%4x len %2d ext %d rmt %d", rx_frame.header.id, twaifd_dlc2len(rx_frame.header.dlc), rx_frame.header.ide, rx_frame.header.rtr);
|
||||
switch (test_ctrl[0]) {
|
||||
case 0: // receive something
|
||||
TEST_ASSERT(rx_frame.header.id >= 0x10);
|
||||
TEST_ASSERT(!rx_frame.header.ide);
|
||||
break;
|
||||
case 1: // receive all
|
||||
case 2: break; // receive none
|
||||
default: TEST_ASSERT(false);
|
||||
}
|
||||
test_ctrl[1] ++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
TEST_CASE("twai mask filter sleep retention", "[twai]")
|
||||
{
|
||||
// Prepare a TOP PD sleep
|
||||
TEST_ESP_OK(esp_sleep_enable_timer_wakeup(1 * 1000 * 1000));
|
||||
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU
|
||||
TEST_ESP_OK(sleep_cpu_configure(true));
|
||||
#endif
|
||||
esp_sleep_context_t sleep_ctx = {};
|
||||
esp_sleep_set_sleep_context(&sleep_ctx);
|
||||
|
||||
twai_node_handle_t node_hdl;
|
||||
twai_onchip_node_config_t node_config = {};
|
||||
node_config.io_cfg.tx = TEST_TX_GPIO;
|
||||
node_config.io_cfg.rx = TEST_TX_GPIO; // Using same pin for test without transceiver
|
||||
node_config.io_cfg.quanta_clk_out = GPIO_NUM_NC;
|
||||
node_config.io_cfg.bus_off_indicator = GPIO_NUM_NC;
|
||||
node_config.bit_timing.bitrate = 1000000;
|
||||
node_config.tx_queue_depth = TEST_TWAI_QUEUE_DEPTH;
|
||||
node_config.flags.enable_self_test = true;
|
||||
node_config.flags.enable_loopback = true;
|
||||
node_config.flags.sleep_allow_pd = true;
|
||||
TEST_ESP_OK(twai_new_node_onchip(&node_config, &node_hdl));
|
||||
|
||||
uint8_t test_ctrl[2] = {0, 0};
|
||||
twai_event_callbacks_t user_cbs = {};
|
||||
user_cbs.on_rx_done = test_filter_rx_done_cb;
|
||||
TEST_ESP_OK(twai_node_register_event_callbacks(node_hdl, &user_cbs, test_ctrl));
|
||||
|
||||
twai_mask_filter_config_t mfilter_cfg = {};
|
||||
mfilter_cfg.id = 0x10,
|
||||
mfilter_cfg.mask = 0xf0,
|
||||
mfilter_cfg.is_ext = false,
|
||||
printf("Testing mask filter: id 0x%lx mask 0x%lx is_ext %d\n", mfilter_cfg.id, mfilter_cfg.mask, mfilter_cfg.is_ext);
|
||||
TEST_ESP_OK(twai_node_config_mask_filter(node_hdl, 0, &mfilter_cfg));
|
||||
|
||||
printf("Go to sleep ...\n");
|
||||
TEST_ESP_OK(esp_light_sleep_start());
|
||||
printf("Waked up! sleep %d power down %ld\n", !sleep_ctx.sleep_request_result, sleep_ctx.sleep_flags & PMU_SLEEP_PD_TOP);
|
||||
#if SOC_TWAI_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
|
||||
TEST_ASSERT_EQUAL(PMU_SLEEP_PD_TOP, sleep_ctx.sleep_flags & PMU_SLEEP_PD_TOP);
|
||||
#endif
|
||||
TEST_ESP_OK(twai_node_enable(node_hdl));
|
||||
test_random_trans_generator(node_hdl, 30);
|
||||
TEST_ASSERT_EQUAL(7, test_ctrl[1]); // must receive 7 of 30 frames under filter config
|
||||
|
||||
TEST_ESP_OK(twai_node_disable(node_hdl));
|
||||
TEST_ESP_OK(twai_node_delete(node_hdl));
|
||||
esp_sleep_set_sleep_context(NULL);
|
||||
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU
|
||||
TEST_ESP_OK(sleep_cpu_configure(false));
|
||||
#endif
|
||||
}
|
||||
#endif //SOC_TWAI_SUPPORT_SLEEP_RETENTION
|
||||
@@ -1,4 +1,4 @@
|
||||
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import subprocess
|
||||
@@ -17,11 +17,21 @@ from pytest_embedded_idf.utils import soc_filtered_targets
|
||||
# ---------------------------------------------------------------------------
|
||||
@pytest.mark.generic
|
||||
@pytest.mark.parametrize('config', ['release', 'cache_safe'], indirect=True)
|
||||
@idf_parametrize('target', soc_filtered_targets('SOC_TWAI_SUPPORTED == 1'), indirect=['target'])
|
||||
@idf_parametrize(
|
||||
'target', soc_filtered_targets('SOC_TWAI_SUPPORTED == 1 and IDF_TARGET not in ["esp32c5"]'), indirect=['target']
|
||||
)
|
||||
def test_driver_twai_loopbk(dut: Dut) -> None:
|
||||
dut.run_all_single_board_cases(group='twai', reset=True)
|
||||
|
||||
|
||||
@pytest.mark.generic
|
||||
@pytest.mark.esp32c5_eco3
|
||||
@pytest.mark.parametrize('config', ['release', 'cache_safe'], indirect=True)
|
||||
@idf_parametrize('target', ['esp32c5'], indirect=['target'])
|
||||
def test_driver_twai_loopbk_c5eco3(dut: Dut) -> None:
|
||||
dut.run_all_single_board_cases(group='twai', reset=True)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Helper Functions
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
CONFIG_ESP_TASK_WDT_INIT=n
|
||||
# primitives for checking sleep internal state
|
||||
CONFIG_ESP_SLEEP_DEBUG=y
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -30,3 +30,75 @@ const twai_signal_conn_t twai_periph_signals[2] = {
|
||||
.stand_by_sig = -1,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* TWAI Registers to be saved during sleep retention
|
||||
* - TWAIFD_MODE_SETTINGS_REG
|
||||
* - TWAIFD_STATUS_REG
|
||||
* - TWAIFD_COMMAND_REG
|
||||
* - TWAIFD_INT_ENA_SET_REG
|
||||
* - TWAIFD_INT_ENA_CLR_REG
|
||||
* - TWAIFD_BTR_REG
|
||||
* - TWAIFD_BTR_FD_REG
|
||||
* - TWAIFD_EWL_ERP_FAULT_STATE_REG
|
||||
* - TWAIFD_REC_TEC_REG
|
||||
* - TWAIFD_ERR_NORM_ERR_FD_REG
|
||||
* - TWAIFD_FILTER_A_MASK_REG
|
||||
* - TWAIFD_FILTER_A_VAL_REG
|
||||
* - TWAIFD_FILTER_B_MASK_REG
|
||||
* - TWAIFD_FILTER_B_VAL_REG
|
||||
* - TWAIFD_FILTER_C_MASK_REG
|
||||
* - TWAIFD_FILTER_C_VAL_REG
|
||||
* - TWAIFD_FILTER_RAN_LOW_REG
|
||||
* - TWAIFD_FILTER_RAN_HIGH_REG
|
||||
* - TWAIFD_FILTER_CONTROL_FILTER_STATUS_REG
|
||||
* - TWAIFD_RX_STATUS_RX_SETTINGS_REG
|
||||
* - TWAIFD_TX_COMMAND_TXTB_INFO_REG
|
||||
* - TWAIFD_TX_PRIORITY_REG
|
||||
* - TWAIFD_TRV_DELAY_SSP_CFG_REG
|
||||
* - TWAIFD_RX_FR_CTR_REG
|
||||
* - TWAIFD_TX_FR_CTR_REG
|
||||
*/
|
||||
#define TWAI_RETENTION_REGS_CNT 25
|
||||
#define TWAI_RETENTION_REGS_BASE(i) REG_TWAI_BASE(i)
|
||||
static const uint32_t twai_regs_map[4] = {0xb27fdf37, 0x3, 0x0, 0x0};
|
||||
|
||||
// twai timer registers is far away from twai registers, so we need a separate link
|
||||
// - TWAIFD_TIMER_INT_ENA_REG
|
||||
// - TWAIFD_TIMER_CFG_REG
|
||||
#define TWAI_TIMER_RETENTION_REGS_CNT 2
|
||||
#define TWAI_TIMER_RETENTION_REGS_BASE(i) (REG_TWAI_BASE(i) + 0xfe0)
|
||||
static const uint32_t twai_timer_regs_map[4] = {0x5, 0x0, 0x0, 0x0};
|
||||
|
||||
#define TWAI_SLEEP_RETENTION_ENTRIES(id) { \
|
||||
[0] = { .config = REGDMA_LINK_ADDR_MAP_INIT( \
|
||||
REGDMA_TWAI_LINK(0x00), \
|
||||
TWAI_RETENTION_REGS_BASE(id), TWAI_RETENTION_REGS_BASE(id), \
|
||||
TWAI_RETENTION_REGS_CNT, 0, 0, \
|
||||
twai_regs_map[0], twai_regs_map[1], \
|
||||
twai_regs_map[2], twai_regs_map[3]), \
|
||||
.owner = ENTRY(0) | ENTRY(2) }, \
|
||||
[1] = { .config = REGDMA_LINK_ADDR_MAP_INIT( \
|
||||
REGDMA_TWAI_LINK(0x01), \
|
||||
TWAI_TIMER_RETENTION_REGS_BASE(id), TWAI_TIMER_RETENTION_REGS_BASE(id), \
|
||||
TWAI_TIMER_RETENTION_REGS_CNT, 0, 0, \
|
||||
twai_timer_regs_map[0], twai_timer_regs_map[1], \
|
||||
twai_timer_regs_map[2], twai_timer_regs_map[3]), \
|
||||
.owner = ENTRY(0) | ENTRY(2) }, \
|
||||
}
|
||||
|
||||
static const regdma_entries_config_t twai0_regs_retention[] = TWAI_SLEEP_RETENTION_ENTRIES(0);
|
||||
static const regdma_entries_config_t twai1_regs_retention[] = TWAI_SLEEP_RETENTION_ENTRIES(1);
|
||||
|
||||
const twai_reg_retention_info_t twai_reg_retention_info[2] = {
|
||||
[0] = {
|
||||
.module_id = SLEEP_RETENTION_MODULE_TWAI0,
|
||||
.entry_array = twai0_regs_retention,
|
||||
.array_size = ARRAY_SIZE(twai0_regs_retention)
|
||||
},
|
||||
[1] = {
|
||||
.module_id = SLEEP_RETENTION_MODULE_TWAI1,
|
||||
.entry_array = twai1_regs_retention,
|
||||
.array_size = ARRAY_SIZE(twai1_regs_retention)
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -20,3 +20,69 @@ const twai_signal_conn_t twai_periph_signals[1] = {
|
||||
.stand_by_sig = -1,
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* TWAI Registers to be saved during sleep retention
|
||||
* - TWAIFD_MODE_SETTINGS_REG
|
||||
* - TWAIFD_STATUS_REG
|
||||
* - TWAIFD_COMMAND_REG
|
||||
* - TWAIFD_INT_ENA_SET_REG
|
||||
* - TWAIFD_INT_ENA_CLR_REG
|
||||
* - TWAIFD_BTR_REG
|
||||
* - TWAIFD_BTR_FD_REG
|
||||
* - TWAIFD_EWL_ERP_FAULT_STATE_REG
|
||||
* - TWAIFD_REC_TEC_REG
|
||||
* - TWAIFD_ERR_NORM_ERR_FD_REG
|
||||
* - TWAIFD_FILTER_A_MASK_REG
|
||||
* - TWAIFD_FILTER_A_VAL_REG
|
||||
* - TWAIFD_FILTER_B_MASK_REG
|
||||
* - TWAIFD_FILTER_B_VAL_REG
|
||||
* - TWAIFD_FILTER_C_MASK_REG
|
||||
* - TWAIFD_FILTER_C_VAL_REG
|
||||
* - TWAIFD_FILTER_RAN_LOW_REG
|
||||
* - TWAIFD_FILTER_RAN_HIGH_REG
|
||||
* - TWAIFD_FILTER_CONTROL_FILTER_STATUS_REG
|
||||
* - TWAIFD_RX_STATUS_RX_SETTINGS_REG
|
||||
* - TWAIFD_TX_COMMAND_TXTB_INFO_REG
|
||||
* - TWAIFD_TX_PRIORITY_REG
|
||||
* - TWAIFD_TRV_DELAY_SSP_CFG_REG
|
||||
* - TWAIFD_RX_FR_CTR_REG
|
||||
* - TWAIFD_TX_FR_CTR_REG
|
||||
*/
|
||||
#define TWAI_RETENTION_REGS_CNT 25
|
||||
#define TWAI_RETENTION_REGS_BASE(i) DR_REG_TWAIFD_BASE
|
||||
static const uint32_t twai_regs_map[4] = {0xb27fdf37, 0x3, 0x0, 0x0};
|
||||
|
||||
// twai timer registers is far away from twai registers, so we need a separate link
|
||||
// - TWAIFD_TIMER_INT_ENA_REG
|
||||
// - TWAIFD_TIMER_CFG_REG
|
||||
#define TWAI_TIMER_RETENTION_REGS_CNT 2
|
||||
#define TWAI_TIMER_RETENTION_REGS_BASE(i) (DR_REG_TWAIFD_BASE + 0xfe0)
|
||||
static const uint32_t twai_timer_regs_map[4] = {0x5, 0x0, 0x0, 0x0};
|
||||
|
||||
#define TWAI_SLEEP_RETENTION_ENTRIES(id) { \
|
||||
[0] = { .config = REGDMA_LINK_ADDR_MAP_INIT( \
|
||||
REGDMA_TWAI_LINK(0x00), \
|
||||
TWAI_RETENTION_REGS_BASE(id), TWAI_RETENTION_REGS_BASE(id), \
|
||||
TWAI_RETENTION_REGS_CNT, 0, 0, \
|
||||
twai_regs_map[0], twai_regs_map[1], \
|
||||
twai_regs_map[2], twai_regs_map[3]), \
|
||||
.owner = ENTRY(0) | ENTRY(2) }, \
|
||||
[1] = { .config = REGDMA_LINK_ADDR_MAP_INIT( \
|
||||
REGDMA_TWAI_LINK(0x01), \
|
||||
TWAI_TIMER_RETENTION_REGS_BASE(id), TWAI_TIMER_RETENTION_REGS_BASE(id), \
|
||||
TWAI_TIMER_RETENTION_REGS_CNT, 0, 0, \
|
||||
twai_timer_regs_map[0], twai_timer_regs_map[1], \
|
||||
twai_timer_regs_map[2], twai_timer_regs_map[3]), \
|
||||
.owner = ENTRY(0) | ENTRY(2) }, \
|
||||
}
|
||||
|
||||
static const regdma_entries_config_t twai0_regs_retention[] = TWAI_SLEEP_RETENTION_ENTRIES(0);
|
||||
|
||||
const twai_reg_retention_info_t twai_reg_retention_info[1] = {
|
||||
[0] = {
|
||||
.module_id = SLEEP_RETENTION_MODULE_TWAI0,
|
||||
.entry_array = twai0_regs_retention,
|
||||
.array_size = ARRAY_SIZE(twai0_regs_retention)
|
||||
},
|
||||
};
|
||||
|
||||
@@ -59,7 +59,10 @@ bool peripheral_domain_pd_allowed(void)
|
||||
// ESP32C5 supports temperature sensor sleep retention
|
||||
RETENTION_MODULE_BITMAP_SET(&mask, SLEEP_RETENTION_MODULE_TEMP_SENSOR);
|
||||
|
||||
// ESP32C5 does not support TWAI sleep retention yet: IDF-13001
|
||||
// ESP32C5 supports TWAI sleep retention
|
||||
RETENTION_MODULE_BITMAP_SET(&mask, SLEEP_RETENTION_MODULE_TWAI0);
|
||||
RETENTION_MODULE_BITMAP_SET(&mask, SLEEP_RETENTION_MODULE_TWAI1);
|
||||
/* ESP32C5 only supports TWAI0 and TWAI1 */
|
||||
|
||||
// ESP32C5 supports PARLIO sleep retention
|
||||
RETENTION_MODULE_BITMAP_SET(&mask, SLEEP_RETENTION_MODULE_PARLIO0);
|
||||
|
||||
@@ -1075,6 +1075,10 @@ config SOC_TWAI_RANGE_FILTER_NUM
|
||||
int
|
||||
default 1
|
||||
|
||||
config SOC_TWAI_SUPPORT_SLEEP_RETENTION
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_EFUSE_DIS_PAD_JTAG
|
||||
bool
|
||||
default y
|
||||
|
||||
@@ -45,6 +45,8 @@ typedef enum periph_retention_module {
|
||||
SLEEP_RETENTION_MODULE_LEDC = 21,
|
||||
SLEEP_RETENTION_MODULE_MCPWM0 = 22,
|
||||
SLEEP_RETENTION_MODULE_SDM0 = 23,
|
||||
SLEEP_RETENTION_MODULE_TWAI0 = 24,
|
||||
SLEEP_RETENTION_MODULE_TWAI1 = 25,
|
||||
|
||||
/* modem module, which includes WiFi, BLE and 802.15.4 */
|
||||
SLEEP_RETENTION_MODULE_WIFI_MAC = 26,
|
||||
@@ -82,6 +84,8 @@ typedef enum periph_retention_module {
|
||||
: ((m) == SLEEP_RETENTION_MODULE_LEDC) ? true \
|
||||
: ((m) == SLEEP_RETENTION_MODULE_MCPWM0) ? true \
|
||||
: ((m) == SLEEP_RETENTION_MODULE_SDM0) ? true \
|
||||
: ((m) == SLEEP_RETENTION_MODULE_TWAI0) ? true \
|
||||
: ((m) == SLEEP_RETENTION_MODULE_TWAI1) ? true \
|
||||
: false)
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -443,9 +443,10 @@
|
||||
#define SOC_MWDT_SUPPORT_SLEEP_RETENTION (1)
|
||||
|
||||
/*-------------------------- TWAI CAPS ---------------------------------------*/
|
||||
#define SOC_TWAI_CONTROLLER_NUM 2
|
||||
#define SOC_TWAI_MASK_FILTER_NUM 3
|
||||
#define SOC_TWAI_RANGE_FILTER_NUM 1U
|
||||
#define SOC_TWAI_CONTROLLER_NUM 2
|
||||
#define SOC_TWAI_MASK_FILTER_NUM 3
|
||||
#define SOC_TWAI_RANGE_FILTER_NUM 1U
|
||||
#define SOC_TWAI_SUPPORT_SLEEP_RETENTION 1
|
||||
|
||||
/*-------------------------- eFuse CAPS----------------------------*/
|
||||
#define SOC_EFUSE_DIS_PAD_JTAG 1
|
||||
|
||||
@@ -807,6 +807,10 @@ config SOC_TWAI_RANGE_FILTER_NUM
|
||||
int
|
||||
default 1
|
||||
|
||||
config SOC_TWAI_SUPPORT_SLEEP_RETENTION
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_EFUSE_DIS_DOWNLOAD_ICACHE
|
||||
bool
|
||||
default n
|
||||
|
||||
@@ -398,9 +398,10 @@
|
||||
#define SOC_MWDT_SUPPORT_XTAL (1)
|
||||
|
||||
/*-------------------------- TWAI CAPS ---------------------------------------*/
|
||||
#define SOC_TWAI_CONTROLLER_NUM 1U
|
||||
#define SOC_TWAI_MASK_FILTER_NUM 3
|
||||
#define SOC_TWAI_RANGE_FILTER_NUM 1U
|
||||
#define SOC_TWAI_CONTROLLER_NUM 1U
|
||||
#define SOC_TWAI_MASK_FILTER_NUM 3U
|
||||
#define SOC_TWAI_RANGE_FILTER_NUM 1U
|
||||
#define SOC_TWAI_SUPPORT_SLEEP_RETENTION 1
|
||||
|
||||
/*-------------------------- eFuse CAPS----------------------------*/
|
||||
#define SOC_EFUSE_DIS_DOWNLOAD_ICACHE 0
|
||||
|
||||
@@ -334,6 +334,15 @@ Power Management
|
||||
|
||||
When power management is enabled via :ref:`CONFIG_PM_ENABLE`, the system may adjust or disable clock sources before entering sleep mode, which could cause TWAI to malfunction. To prevent this, the driver manages a power management lock internally. This lock is acquired when calling :cpp:func:`twai_node_enable`, ensuring the system does not enter sleep mode and TWAI remains functional. To allow the system to enter a low-power state, call :cpp:func:`twai_node_disable` to release the lock. During sleep, the TWAI controller will also stop functioning.
|
||||
|
||||
.. only:: SOC_TWAI_SUPPORT_SLEEP_RETENTION
|
||||
|
||||
About Sleep Retention
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
{IDF_TARGET_NAME} supports powering down the TWAI controller during **Light Sleep** to further reduce power consumption and automatically restore after waking up. This means the application does not need to reconfigure TWAI after **Light Sleep** wake up.
|
||||
|
||||
Enable the option :ref:`CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP`, and set :cpp:member:`twai_onchip_node_config_t::flags::sleep_allow_pd` to ``true`` when initializing the TWAI node to enable this feature. Otherwise, the TWAI controller will remain powered during **Light Sleep**. This feature helps reduce power consumption during light sleep but requires additional storage to save register configurations.
|
||||
|
||||
Cache Safety
|
||||
------------
|
||||
|
||||
|
||||
@@ -334,6 +334,15 @@ TWAI控制器能够检测由于总线干扰产生的/损坏的不符合帧格式
|
||||
|
||||
当启用电源管理 :ref:`CONFIG_PM_ENABLE` 时,系统在进入睡眠模式前可能会调整或关闭时钟源,从而导致 TWAI 出错。为了防止这种情况发生,驱动内部使用电源锁管理。当调用 :cpp:func:`twai_node_enable` 函数后,该锁将被激活,确保系统不会进入睡眠模式,从而保持 TWAI 功能正常。如果需要降低功耗,可以调用 :cpp:func:`twai_node_disable` 函数来释放电源管理锁,使系统能够进入睡眠模式,睡眠期间 TWAI 控制器也将停止工作。
|
||||
|
||||
.. only:: SOC_TWAI_SUPPORT_SLEEP_RETENTION
|
||||
|
||||
关于睡眠保留
|
||||
^^^^^^^^^^^^
|
||||
|
||||
{IDF_TARGET_NAME} 支持在 **Light Sleep** 期间将 TWAI 控制器断电以进一步降低功耗,并在唤醒后自动恢复。即程序不需要在 **Light Sleep** 唤醒后重新配置 TWAI。
|
||||
|
||||
启用选项 :ref:`CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP`,并在初始化 TWAI 节点时,将 :cpp:member:`twai_onchip_node_config_t::flags::sleep_allow_pd` 设置为 ``true`` 即可启用该功能,否则 TWAI 控制器在 **Light Sleep** 期间将保持供电。它可以帮助降低轻度睡眠时的功耗,但需要花费一些额外的存储来保存寄存器的配置。
|
||||
|
||||
关于 Cache 安全
|
||||
---------------
|
||||
|
||||
|
||||
Reference in New Issue
Block a user