test(driver_twai): test app rename and add cache safe test

This commit is contained in:
wanckl
2025-04-27 15:56:43 +08:00
parent 8a60934c4f
commit 00c78197df
26 changed files with 686 additions and 365 deletions

View File

@@ -72,15 +72,7 @@ components/driver/test_apps/legacy_timer_driver:
depends_filepatterns:
- components/driver/deprecated/**/*timer*
components/driver/test_apps/touch_sensor_v1:
disable:
- if: SOC_TOUCH_SENSOR_VERSION != 1
components/driver/test_apps/touch_sensor_v2:
disable:
- if: SOC_TOUCH_SENSOR_VERSION != 2
components/driver/test_apps/twai:
components/driver/test_apps/legacy_twai:
disable:
- if: SOC_TWAI_SUPPORTED != 1 or SOC_TWAI_SUPPORT_FD == 1
reason: legacy driver doesn't support FD
@@ -88,3 +80,11 @@ components/driver/test_apps/twai:
- components/driver/twai/**/*
depends_components:
- esp_driver_gpio
components/driver/test_apps/touch_sensor_v1:
disable:
- if: SOC_TOUCH_SENSOR_VERSION != 1
components/driver/test_apps/touch_sensor_v2:
disable:
- if: SOC_TOUCH_SENSOR_VERSION != 2

View File

@@ -1,4 +1,4 @@
CONFIG_FREERTOS_HZ=1000
CONFIG_ESP_TASK_WDT=n
CONFIG_ESP_TASK_WDT_INIT=n
# primitives for checking sleep internal state
CONFIG_ESP_SLEEP_DEBUG=y

View File

@@ -1,6 +1,5 @@
components/esp_driver_twai/test_apps/twaifd_test:
components/esp_driver_twai/test_apps/test_twai:
disable:
- if: SOC_TWAI_SUPPORTED != 1 or SOC_TWAI_SUPPORT_FD != 1
reason: Only support FD targets now
- if: SOC_TWAI_SUPPORTED != 1
depends_components:
- esp_driver_twai

View File

@@ -5,9 +5,23 @@ cmake_minimum_required(VERSION 3.16)
set(COMPONENTS main)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(twaifd_test)
project(test_twai)
message(STATUS "Checking TWAI registers are not read-write by half-word")
include($ENV{IDF_PATH}/tools/ci/check_register_rw_half_word.cmake)
check_register_rw_half_word(SOC_MODULES "twai*" "pcr" "hp_sys_clkrst"
HAL_MODULES "twai*")
if(CONFIG_COMPILER_DUMP_RTL_FILES)
add_custom_target(
check_test_app_sections ALL
COMMAND ${PYTHON} $ENV{IDF_PATH}/tools/ci/check_callgraph.py
--rtl-dirs ${CMAKE_BINARY_DIR}/esp-idf/esp_driver_twai/,${CMAKE_BINARY_DIR}/esp-idf/hal/
--elf-file ${CMAKE_BINARY_DIR}/test_twai.elf
find-refs
--from-sections=.iram0.text
--to-sections=.flash.text,.flash.rodata
--exit-code
DEPENDS ${elf}
)
endif()

View File

@@ -0,0 +1,2 @@
| Supported Targets | ESP32 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | -------- |

View File

@@ -0,0 +1,15 @@
set(srcs "test_app_main.c")
if(CONFIG_SOC_TWAI_SUPPORTED)
list(APPEND srcs "test_twai_common.c")
endif()
if(CONFIG_SOC_TWAI_SUPPORT_FD)
list(APPEND srcs "test_twai_fd.c")
endif()
idf_component_register(
SRCS ${srcs}
PRIV_REQUIRES esp_driver_twai esp_timer
WHOLE_ARCHIVE
)

View File

@@ -19,6 +19,7 @@ void setUp(void)
void tearDown(void)
{
esp_reent_cleanup();
unity_utils_evaluate_leaks_direct(LEAKS);
}

View File

@@ -0,0 +1,427 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <sys/param.h>
#include "unity.h"
#include "unity_test_utils_cache.h"
#include "test_utils.h"
#include "esp_attr.h"
#include "esp_log.h"
#include "esp_heap_caps.h"
#include "freertos/FreeRTOS.h"
#include "esp_twai.h"
#include "esp_twai_onchip.h"
#define TEST_TX_GPIO 4
#define TEST_RX_GPIO 5
#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_driver_install_rx_cb(twai_node_handle_t handle, const twai_rx_done_event_data_t *edata, void *user_ctx)
{
twai_frame_t rx_frame;
if (ESP_OK == twai_node_receive_from_isr(handle, &rx_frame)) {
ESP_EARLY_LOGI("Recv ", "id 0x%lx rtr %d", rx_frame.header.id, rx_frame.header.rtr);
}
if (rx_frame.header.id != 0x100) {
TEST_FAIL(); //callback is unregistered, should not run here
}
return false;
}
TEST_CASE("twai install uninstall (loopback)", "[TWAI]")
{
esp_err_t ret;
twai_node_handle_t node_hdl[SOC_TWAI_CONTROLLER_NUM + 1];
twai_onchip_node_config_t node_config = {
.io_cfg.tx = TEST_TX_GPIO,
.io_cfg.rx = TEST_TX_GPIO, // Using same pin for test without transceiver
.bit_timing.bitrate = 1000000,
.data_timing.bitrate = 1000000,
.tx_queue_depth = TEST_TWAI_QUEUE_DEPTH,
.flags.enable_loopback = true,
.flags.enable_self_test = true,
};
// loop 10 times to check memory leak
for (uint8_t loop = 0; loop < 10; loop ++) {
for (uint8_t i = 0; i < SOC_TWAI_CONTROLLER_NUM + 1; i++) {
ret = twai_new_node_onchip(&node_config, &node_hdl[i]);
printf("Install TWAI%d return %s\n", i, esp_err_to_name(ret));
TEST_ASSERT(ret == ((i < SOC_TWAI_CONTROLLER_NUM) ? ESP_OK : ESP_ERR_NOT_FOUND));
}
// can't disable before enable
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, twai_node_disable(node_hdl[0]));
twai_event_callbacks_t user_cbs = {
.on_rx_done = test_driver_install_rx_cb,
};
TEST_ESP_OK(twai_node_register_event_callbacks(node_hdl[0], &user_cbs, NULL));
printf("Test unregister callback\n");
user_cbs.on_rx_done = NULL;
TEST_ESP_OK(twai_node_register_event_callbacks(node_hdl[0], &user_cbs, NULL));
twai_frame_t tx_frame = {
.header.id = 0x82,
.header.rtr = true,
};
printf("Test transmit before enable\n");
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, twai_node_transmit(node_hdl[0], &tx_frame, 0));
TEST_ESP_OK(twai_node_enable(node_hdl[0]));
TEST_ESP_OK(twai_node_disable(node_hdl[0]));
TEST_ESP_OK(twai_node_enable(node_hdl[0]));
TEST_ESP_OK(twai_node_transmit(node_hdl[0], &tx_frame, 0));
TEST_ESP_OK(twai_node_disable(node_hdl[0]));
TEST_ESP_OK(twai_node_delete(node_hdl[0]));
printf("Test install after delete\n");
TEST_ESP_OK(twai_new_node_onchip(&node_config, &node_hdl[SOC_TWAI_CONTROLLER_NUM]));
user_cbs.on_rx_done = test_driver_install_rx_cb,
TEST_ESP_OK(twai_node_register_event_callbacks(node_hdl[SOC_TWAI_CONTROLLER_NUM], &user_cbs, NULL));
TEST_ESP_OK(twai_node_enable(node_hdl[SOC_TWAI_CONTROLLER_NUM]));
tx_frame.header.id = 0x100;
TEST_ESP_OK(twai_node_transmit(node_hdl[SOC_TWAI_CONTROLLER_NUM], &tx_frame, 0));
twai_frame_t rx_frame;
printf("Test receive from task\n");
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, twai_node_receive_from_isr(node_hdl[SOC_TWAI_CONTROLLER_NUM], &rx_frame));
TEST_ESP_OK(twai_node_disable(node_hdl[SOC_TWAI_CONTROLLER_NUM]));
for (uint8_t i = 1; i <= SOC_TWAI_CONTROLLER_NUM; i++) {
printf("Uninstall TWAI%d\n", i - 1);
TEST_ESP_OK(twai_node_delete(node_hdl[i]));
}
}
}
static IRAM_ATTR bool test_enable_disable_rx_cb(twai_node_handle_t handle, const twai_rx_done_event_data_t *edata, void *user_ctx)
{
twai_frame_t *rx_frame = user_ctx;
if (ESP_OK == twai_node_receive_from_isr(handle, rx_frame)) {
ESP_EARLY_LOGI("Recv", "RX id 0x%x len %d ext %d brs %d esi %d", rx_frame->header.id, twaifd_dlc2len(rx_frame->header.dlc), rx_frame->header.ide, rx_frame->header.brs, rx_frame->header.esi);
rx_frame->buffer += rx_frame->buffer_len;
}
return false;
}
TEST_CASE("twai transmit stop resume (loopback)", "[TWAI]")
{
// prepare test memory
uint8_t *send_pkg_ptr = heap_caps_malloc(TEST_TRANS_LEN, MALLOC_CAP_8BIT);
uint8_t *recv_pkg_ptr = heap_caps_malloc(TEST_TRANS_LEN, MALLOC_CAP_8BIT);
TEST_ASSERT(send_pkg_ptr && recv_pkg_ptr);
printf("Transmit %d bytes package in %d frames\n", TEST_TRANS_LEN, TEST_FRAME_NUM);
twai_node_handle_t node_hdl;
twai_onchip_node_config_t node_config = {
.io_cfg.tx = TEST_TX_GPIO,
.io_cfg.rx = TEST_TX_GPIO, // Using same pin for test without transceiver
.bit_timing.bitrate = 200000,
.tx_queue_depth = TEST_TWAI_QUEUE_DEPTH,
.flags.enable_loopback = true,
.flags.enable_self_test = true,
};
TEST_ESP_OK(twai_new_node_onchip(&node_config, &node_hdl));
twai_frame_t rx_frame = {
.buffer = recv_pkg_ptr,
.buffer_len = TEST_FRAME_LEN,
};
twai_event_callbacks_t user_cbs = {
.on_rx_done = test_enable_disable_rx_cb,
};
TEST_ESP_OK(twai_node_register_event_callbacks(node_hdl, &user_cbs, &rx_frame));
TEST_ESP_OK(twai_node_enable(node_hdl));
//create and enqueue all transfers
twai_frame_t *tx_msgs = heap_caps_calloc(TEST_FRAME_NUM, sizeof(twai_frame_t), MALLOC_CAP_8BIT);
TEST_ASSERT(tx_msgs);
for (uint32_t tx_cnt = 0; tx_cnt < TEST_FRAME_NUM; tx_cnt++) {
tx_msgs[tx_cnt].header.id = tx_cnt | 0x400;
tx_msgs[tx_cnt].header.ide = !!(tx_cnt % 3);
tx_msgs[tx_cnt].buffer = send_pkg_ptr + tx_cnt * TEST_FRAME_LEN;
tx_msgs[tx_cnt].buffer_len = ((tx_cnt + 1) == TEST_FRAME_NUM) ? (TEST_TRANS_LEN - tx_cnt * TEST_FRAME_LEN) : TEST_FRAME_LEN;
TEST_ESP_OK(twai_node_transmit(node_hdl, &tx_msgs[tx_cnt], 500));
}
TEST_ESP_OK(twai_node_disable(node_hdl));
for (uint8_t i = 3; i > 0; i--) {
printf("interrupted, %d sec\n", i);
vTaskDelay(1000);
}
printf("continuing ...\n");
TEST_ESP_OK(twai_node_enable(node_hdl));
//waiting pkg receive finish
while (rx_frame.buffer < recv_pkg_ptr + TEST_TRANS_LEN) {
vTaskDelay(1);
}
free(tx_msgs);
// check if pkg receive correct
printf("pkg check %s!!\n", memcmp(recv_pkg_ptr, send_pkg_ptr, TEST_TRANS_LEN) ? "failed" : "ok");
TEST_ASSERT_EQUAL_HEX8_ARRAY(send_pkg_ptr, recv_pkg_ptr, TEST_TRANS_LEN);
free(send_pkg_ptr);
free(recv_pkg_ptr);
TEST_ESP_OK(twai_node_disable(node_hdl));
TEST_ESP_OK(twai_node_delete(node_hdl));
}
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 = {
.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_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));
vTaskDelay(8); //as async transaction, waiting trans done
}
}
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 = user_ctx;
uint8_t recv_pkg_ptr[TWAI_FRAME_MAX_LEN];
twai_frame_t rx_frame = {
.buffer = recv_pkg_ptr,
.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 (loopback)", "[TWAI]")
{
uint8_t test_ctrl[2];
twai_node_handle_t node_hdl;
twai_onchip_node_config_t node_config = {
.io_cfg.tx = TEST_TX_GPIO,
.io_cfg.rx = TEST_TX_GPIO, // Using same pin for test without transceiver
.bit_timing.bitrate = 1000000,
.tx_queue_depth = TEST_TWAI_QUEUE_DEPTH,
.flags.enable_loopback = true,
.flags.enable_self_test = true,
};
TEST_ESP_OK(twai_new_node_onchip(&node_config, &node_hdl));
twai_event_callbacks_t 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 = {0};
for (uint8_t i = 0; i < SOC_TWAI_MASK_FILTER_NUM; i++) {
printf("\n--------------------------------------\n");
test_ctrl[0] = 0;
test_ctrl[1] = 0;
mfilter_cfg.id = 0x10,
mfilter_cfg.mask = 0xf0,
mfilter_cfg.is_ext = false,
printf("Testing mask filter %d: id 0x%lx mask 0x%lx is_ext %d\n", i, mfilter_cfg.id, mfilter_cfg.mask, mfilter_cfg.is_ext);
TEST_ESP_OK(twai_node_config_mask_filter(node_hdl, i, &mfilter_cfg));
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_ctrl[0] = 1;
test_ctrl[1] = 0;
mfilter_cfg.id = 0;
mfilter_cfg.mask = 0;
printf("Change filter %d to receive ALL: id 0x%lx mask 0x%lx\n", i, mfilter_cfg.id, mfilter_cfg.mask);
TEST_ESP_OK(twai_node_disable(node_hdl));
TEST_ESP_OK(twai_node_config_mask_filter(node_hdl, i, &mfilter_cfg));
TEST_ESP_OK(twai_node_enable(node_hdl));
test_random_trans_generator(node_hdl, 20);
TEST_ASSERT_EQUAL(20, test_ctrl[1]);
test_ctrl[0] = 2;
test_ctrl[1] = 0;
mfilter_cfg.id = 0xFFFFFFFF;
mfilter_cfg.mask = 0xFFFFFFFF;
printf("Disable filter %d: id 0x%lx mask 0x%lx\n", i, mfilter_cfg.id, mfilter_cfg.mask);
TEST_ESP_OK(twai_node_disable(node_hdl));
TEST_ESP_OK(twai_node_config_mask_filter(node_hdl, i, &mfilter_cfg));
TEST_ESP_OK(twai_node_enable(node_hdl));
test_random_trans_generator(node_hdl, 40);
TEST_ASSERT_EQUAL(0, test_ctrl[1]);
TEST_ESP_OK(twai_node_disable(node_hdl));
}
TEST_ESP_OK(twai_node_delete(node_hdl));
}
//------------------ Dual Filter Test -------------------//
#if !SOC_TWAI_SUPPORT_FD
static IRAM_ATTR bool test_dual_filter_rx_done_cb(twai_node_handle_t handle, const twai_rx_done_event_data_t *edata, void *user_ctx)
{
uint8_t *test_ctrl = user_ctx;
uint8_t recv_pkg_ptr[TWAI_FRAME_MAX_LEN];
twai_frame_t rx_frame = {
.buffer = recv_pkg_ptr,
.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.ide);
TEST_ASSERT((rx_frame.header.id >= 0x10) && (rx_frame.header.id <= 0x2f));
break;
case 1: break; // receive none
default: TEST_ASSERT(false);
}
test_ctrl[1] ++;
}
return false;
}
TEST_CASE("twai dual 16bit mask filter (loopback)", "[TWAI]")
{
uint8_t test_ctrl[2];
twai_node_handle_t node_hdl;
twai_onchip_node_config_t node_config = {
.io_cfg.tx = TEST_TX_GPIO,
.io_cfg.rx = TEST_TX_GPIO, // Using same pin for test without transceiver
.bit_timing.bitrate = 1000000,
.tx_queue_depth = TEST_TWAI_QUEUE_DEPTH,
.flags.enable_loopback = true,
.flags.enable_self_test = true,
};
TEST_ESP_OK(twai_new_node_onchip(&node_config, &node_hdl));
twai_event_callbacks_t user_cbs = {
.on_rx_done = test_dual_filter_rx_done_cb,
};
TEST_ESP_OK(twai_node_register_event_callbacks(node_hdl, &user_cbs, test_ctrl));
printf("Testing dual filter: id1 0x%x mask1 0x%x, id2 0x%x mask2 0x%x\n", 0x020, 0x7f0, 0x013, 0x7f8);
test_ctrl[0] = 0;
test_ctrl[1] = 0;
// filter 1 receive only std id 0x02x
// filter 2 receive only std id 0x010~0x017
twai_mask_filter_config_t dual_config = twai_make_dual_filter(0x020, 0x7f0, 0x013, 0x7f8, false);
TEST_ESP_OK(twai_node_config_mask_filter(node_hdl, 0, &dual_config));
TEST_ESP_OK(twai_node_enable(node_hdl));
test_random_trans_generator(node_hdl, 50);
TEST_ASSERT_EQUAL(12, test_ctrl[1]); // must receive 12 of 50 frames under filter config
printf("Disable filter\n");
test_ctrl[0] = 1;
test_ctrl[1] = 0;
dual_config.id = 0xFFFFFFFF;
dual_config.mask = 0xFFFFFFFF;
TEST_ESP_OK(twai_node_disable(node_hdl));
TEST_ESP_OK(twai_node_config_mask_filter(node_hdl, 0, &dual_config));
TEST_ESP_OK(twai_node_enable(node_hdl));
test_random_trans_generator(node_hdl, 40);
TEST_ASSERT_EQUAL(0, test_ctrl[1]);
TEST_ESP_OK(twai_node_disable(node_hdl));
TEST_ESP_OK(twai_node_delete(node_hdl));
}
#endif
#if CONFIG_TWAI_ISR_CACHE_SAFE
static IRAM_ATTR bool test_iram_safe_rx_cb(twai_node_handle_t handle, const twai_rx_done_event_data_t *edata, void *user_ctx)
{
twai_frame_t *rx_frame = user_ctx;
if (ESP_OK == twai_node_receive_from_isr(handle, rx_frame)) {
esp_rom_printf(DRAM_STR("RX id 0x%x len %d ext %d brs %d esi %d\n"), rx_frame->header.id, twaifd_dlc2len(rx_frame->header.dlc), rx_frame->header.ide, rx_frame->header.brs, rx_frame->header.esi);
rx_frame->buffer += rx_frame->buffer_len;
}
return false;
}
static void IRAM_ATTR test_wait_trans_done_cache_disable(void *args)
{
twai_frame_t *rx_frame = ((twai_frame_t **)args)[0];
uint8_t *orig_buff = ((uint8_t **)args)[1];
esp_rom_printf(DRAM_STR("Cache disabled now !!!\n"));
//waiting pkg receive finish
while (rx_frame->buffer < orig_buff + TEST_TRANS_LEN) {
esp_rom_delay_us(1000);
}
}
TEST_CASE("twai driver cache safe (loopback)", "[TWAI]")
{
// prepare test memory
uint8_t *send_pkg_ptr = heap_caps_malloc(TEST_TRANS_LEN, MALLOC_CAP_8BIT);
uint8_t *recv_pkg_ptr = heap_caps_malloc(TEST_TRANS_LEN, MALLOC_CAP_8BIT);
TEST_ASSERT(send_pkg_ptr && recv_pkg_ptr);
printf("Transmit %d bytes package in %d frames\n", TEST_TRANS_LEN, TEST_FRAME_NUM);
twai_node_handle_t node_hdl;
twai_onchip_node_config_t node_config = {
.io_cfg.tx = TEST_TX_GPIO,
.io_cfg.rx = TEST_TX_GPIO, // Using same pin for test without transceiver
.bit_timing.bitrate = 50000, //slow bitrate to ensure cache disabled before tx_queue finish
.tx_queue_depth = TEST_FRAME_NUM,
.flags.enable_loopback = true,
.flags.enable_self_test = true,
};
TEST_ESP_OK(twai_new_node_onchip(&node_config, &node_hdl));
twai_frame_t rx_frame = {
.buffer = recv_pkg_ptr,
.buffer_len = TEST_FRAME_LEN,
};
twai_event_callbacks_t user_cbs = {
.on_rx_done = test_iram_safe_rx_cb,
};
TEST_ESP_OK(twai_node_register_event_callbacks(node_hdl, &user_cbs, &rx_frame));
TEST_ESP_OK(twai_node_enable(node_hdl));
//create and enqueue all transfers
twai_frame_t *tx_msgs = heap_caps_calloc(TEST_FRAME_NUM, sizeof(twai_frame_t), MALLOC_CAP_8BIT);
TEST_ASSERT(tx_msgs);
for (uint32_t tx_cnt = 0; tx_cnt < TEST_FRAME_NUM; tx_cnt++) {
tx_msgs[tx_cnt].header.id = tx_cnt | 0x400;
tx_msgs[tx_cnt].header.ide = !!(tx_cnt % 3);
tx_msgs[tx_cnt].buffer = send_pkg_ptr + tx_cnt * TEST_FRAME_LEN;
tx_msgs[tx_cnt].buffer_len = ((tx_cnt + 1) == TEST_FRAME_NUM) ? (TEST_TRANS_LEN - tx_cnt * TEST_FRAME_LEN) : TEST_FRAME_LEN;
TEST_ESP_OK(twai_node_transmit(node_hdl, &tx_msgs[tx_cnt], 0));
}
//disable cache immediately before tx_queue finish
void *user_data[2] = {&rx_frame, recv_pkg_ptr};
unity_utils_run_cache_disable_stub(test_wait_trans_done_cache_disable, user_data);
//if it is able to waiting finish, means pass the test
free(tx_msgs);
// check if pkg receive correct
printf("Transaction finish, pkg check %s!!\n", memcmp(recv_pkg_ptr, send_pkg_ptr, TEST_TRANS_LEN) ? "failed" : "ok");
TEST_ASSERT_EQUAL_HEX8_ARRAY(send_pkg_ptr, recv_pkg_ptr, TEST_TRANS_LEN);
free(send_pkg_ptr);
free(recv_pkg_ptr);
TEST_ESP_OK(twai_node_disable(node_hdl));
TEST_ESP_OK(twai_node_delete(node_hdl));
}
#endif //CONFIG_TWAI_ISR_CACHE_SAFE

View File

@@ -0,0 +1,206 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <sys/param.h>
#include "unity.h"
#include "esp_log.h"
#include "esp_timer.h"
#include "esp_heap_caps.h"
#include "freertos/FreeRTOS.h"
#include "test_utils.h"
#include "esp_twai.h"
#include "esp_twai_onchip.h"
#define TEST_TX_GPIO 4
#define TEST_RX_GPIO 5
#define TEST_TWAI_QUEUE_DEPTH 5
static void test_random_trans_generator(twai_node_handle_t node_hdl, uint32_t trans_num)
{
uint8_t send_pkg_ptr[TWAIFD_FRAME_MAX_LEN];
twai_frame_t 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_msg.header.ide = !!(tx_cnt % 2);
tx_msg.header.rtr = !!(tx_cnt % 3);
tx_msg.header.fdf = !!(tx_cnt % 5);
tx_msg.buffer_len = tx_msg.header.fdf ? (tx_cnt % TWAIFD_FRAME_MAX_LEN) : (tx_cnt % TWAI_FRAME_MAX_LEN);
TEST_ESP_OK(twai_node_transmit(node_hdl, &tx_msg, 0));
vTaskDelay(8); //as async transaction, waiting trans done
}
}
static IRAM_ATTR bool test_range_filter_rx_done_cb(twai_node_handle_t handle, const twai_rx_done_event_data_t *edata, void *user_ctx)
{
uint8_t *test_ctrl = user_ctx;
uint8_t recv_pkg_ptr[TWAIFD_FRAME_MAX_LEN];
twai_frame_t rx_frame = {
.buffer = recv_pkg_ptr,
.buffer_len = TWAIFD_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 fd %d", rx_frame.header.id, twaifd_dlc2len(rx_frame.header.dlc), rx_frame.header.ide, rx_frame.header.rtr, rx_frame.header.fdf);
switch (test_ctrl[0]) {
case 0: // enable range filter
TEST_ASSERT(!rx_frame.header.ide);
TEST_ASSERT((rx_frame.header.id >= 0x0a) && (rx_frame.header.id <= 0x15));
break;
case 1: break; // disable range filter
default: TEST_ASSERT(false);
}
test_ctrl[1] ++;
}
return false;
}
TEST_CASE("twai range filter (loopback)", "[TWAI]")
{
uint8_t test_ctrl[2];
twai_node_handle_t node_hdl;
twai_onchip_node_config_t node_config = {
.io_cfg.tx = TEST_TX_GPIO,
.io_cfg.rx = TEST_TX_GPIO, // Using same pin for test without transceiver
.bit_timing.bitrate = 1000000,
.tx_queue_depth = TEST_TWAI_QUEUE_DEPTH,
.flags.enable_loopback = true,
.flags.enable_self_test = true,
};
TEST_ESP_OK(twai_new_node_onchip(&node_config, &node_hdl));
twai_event_callbacks_t user_cbs = {
.on_rx_done = test_range_filter_rx_done_cb,
};
TEST_ESP_OK(twai_node_register_event_callbacks(node_hdl, &user_cbs, test_ctrl));
// disable mask filter 0 which enabled by default
twai_mask_filter_config_t mfilter_cfg = {
.id = 0xFFFFFFFF,
.mask = 0xFFFFFFFF,
};
TEST_ESP_OK(twai_node_config_mask_filter(node_hdl, 0, &mfilter_cfg));
test_ctrl[0] = 0;
test_ctrl[1] = 0;
twai_range_filter_config_t rfilter_cfg = {
.range_low = 0x0a,
.range_high = 0x15,
.is_ext = false,
};
printf("Config range filter 0: 0x%lx - 0x%lx\n", rfilter_cfg.range_low, rfilter_cfg.range_high);
TEST_ESP_OK(twai_node_config_range_filter(node_hdl, 0, &rfilter_cfg));
TEST_ESP_OK(twai_node_enable(node_hdl));
test_random_trans_generator(node_hdl, 30);
TEST_ASSERT_EQUAL(6, test_ctrl[1]); // must receive 6 of 30 frames under filter config
test_ctrl[0] = 1;
test_ctrl[1] = 0;
rfilter_cfg.range_low = 1;
rfilter_cfg.range_high = 0; // config invalid range to disable range filter
printf("Disable range filter 0: 0x%lx - 0x%lx\n", rfilter_cfg.range_low, rfilter_cfg.range_high);
TEST_ESP_OK(twai_node_disable(node_hdl));
TEST_ESP_OK(twai_node_config_range_filter(node_hdl, 0, &rfilter_cfg));
TEST_ESP_OK(twai_node_enable(node_hdl));
test_random_trans_generator(node_hdl, 30);
TEST_ASSERT_EQUAL(0, test_ctrl[1]);
TEST_ESP_OK(twai_node_disable(node_hdl));
TEST_ESP_OK(twai_node_delete(node_hdl));
}
#define TEST_TRANS_TIME_BUF_LEN 20000
static IRAM_ATTR bool test_fd_trans_time_rx_cb(twai_node_handle_t handle, const twai_rx_done_event_data_t *edata, void *user_ctx)
{
twai_frame_t *rx_frame = user_ctx;
uint32_t data_len;
if (ESP_OK == twai_node_receive_from_isr(handle, rx_frame)) {
data_len = MIN(twaifd_dlc2len(rx_frame->header.dlc), rx_frame->buffer_len);
ESP_EARLY_LOGD("Recv", "RX id 0x%x len %d brs %d", rx_frame->header.id, data_len, rx_frame->header.brs);
rx_frame->buffer += data_len;
// calc the `rx_frame->buffer_len` for last frame receive
if ((rx_frame->header.id - 0x400 + 1) * rx_frame->buffer_len > (TEST_TRANS_TIME_BUF_LEN - rx_frame->buffer_len)) {
rx_frame->buffer_len = TEST_TRANS_TIME_BUF_LEN % rx_frame->buffer_len;
}
}
return false;
}
TEST_CASE("twai fd transmit time (loopback)", "[TWAI]")
{
// prepare test memory
uint8_t *send_pkg_ptr = heap_caps_malloc(TEST_TRANS_TIME_BUF_LEN, MALLOC_CAP_8BIT);
uint8_t *recv_pkg_ptr = heap_caps_malloc(TEST_TRANS_TIME_BUF_LEN, MALLOC_CAP_8BIT);
TEST_ASSERT(send_pkg_ptr && recv_pkg_ptr);
twai_node_handle_t node_hdl;
twai_onchip_node_config_t node_config = {
.io_cfg.tx = TEST_TX_GPIO,
.io_cfg.rx = TEST_TX_GPIO, // Using same pin for test without transceiver
.bit_timing.bitrate = 1000000,
.data_timing.bitrate = 4000000,
.data_timing.ssp_permill = 700, // ssp 70.0%
.tx_queue_depth = TEST_TWAI_QUEUE_DEPTH,
.flags.enable_loopback = true,
.flags.enable_self_test = true,
};
TEST_ESP_OK(twai_new_node_onchip(&node_config, &node_hdl));
twai_frame_t rx_frame = {.buffer_len = 64};
twai_event_callbacks_t user_cbs = {
.on_rx_done = test_fd_trans_time_rx_cb,
};
TEST_ESP_OK(twai_node_register_event_callbacks(node_hdl, &user_cbs, &rx_frame));
TEST_ESP_OK(twai_node_enable(node_hdl));
printf("%-12s %-14s %-14s %-7s %-15s %s\n", "pkg_len", "frame_len", "frame_num", "brs", "trans_time/ms", "result");
printf("-------------------------------------------------------------------------\n");
uint64_t time1, time2;
for (uint8_t test_mode = 0; test_mode < 3; test_mode ++) {
//create and enqueue all transfers
uint8_t frame_len = test_mode >= 1 ? 64 : 8;
uint16_t trans_num = howmany(TEST_TRANS_TIME_BUF_LEN, frame_len);
rx_frame.buffer = recv_pkg_ptr;
rx_frame.buffer_len = frame_len;
memset(recv_pkg_ptr, 0xff, TEST_TRANS_TIME_BUF_LEN);
twai_frame_t *tx_msgs = heap_caps_calloc(trans_num, sizeof(twai_frame_t), MALLOC_CAP_8BIT);
TEST_ASSERT(tx_msgs);
time1 = esp_timer_get_time();
for (uint32_t tx_cnt = 0; tx_cnt < trans_num; tx_cnt++) {
tx_msgs[tx_cnt].header.id = tx_cnt | 0x400;
tx_msgs[tx_cnt].header.fdf = frame_len == 64;
tx_msgs[tx_cnt].header.brs = test_mode == 2;
tx_msgs[tx_cnt].buffer = send_pkg_ptr + tx_cnt * frame_len;
tx_msgs[tx_cnt].buffer_len = ((tx_cnt + 1) == trans_num) ? (TEST_TRANS_TIME_BUF_LEN - tx_cnt * frame_len) : frame_len;
TEST_ESP_OK(twai_node_transmit(node_hdl, &tx_msgs[tx_cnt], 1000));
}
//waiting pkg receive finish
while (rx_frame.buffer < recv_pkg_ptr + TEST_TRANS_TIME_BUF_LEN) {
vTaskDelay(1);
}
time2 = esp_timer_get_time();
free(tx_msgs);
// check if pkg receive correct
printf("%-12d %-14d %-14d %-7d %-15.2f %-s\n",
TEST_TRANS_TIME_BUF_LEN,
frame_len,
trans_num,
(test_mode == 2),
(time2 - time1) / 1000.f,
memcmp(recv_pkg_ptr, send_pkg_ptr, TEST_TRANS_TIME_BUF_LEN) ? "failed" : "ok");
TEST_ASSERT_EQUAL_HEX8_ARRAY(send_pkg_ptr, recv_pkg_ptr, TEST_TRANS_TIME_BUF_LEN);
}
printf("-------------------------------------------------------------------------\n");
free(send_pkg_ptr);
free(recv_pkg_ptr);
TEST_ESP_OK(twai_node_disable(node_hdl));
TEST_ESP_OK(twai_node_delete(node_hdl));
}

View File

@@ -8,7 +8,7 @@ from pytest_embedded_idf.utils import soc_filtered_targets
@pytest.mark.generic
@pytest.mark.parametrize('config', ['release'], indirect=True)
@idf_parametrize('target', soc_filtered_targets('SOC_TWAI_SUPPORT_FD == 1'), indirect=['target'])
@pytest.mark.parametrize('config', ['release', 'cache_safe'], indirect=True)
@idf_parametrize('target', soc_filtered_targets('SOC_TWAI_SUPPORTED == 1'), indirect=['target'])
def test_driver_twai_loopbk(dut: Dut) -> None:
dut.run_all_single_board_cases(reset=True)

View File

@@ -0,0 +1,6 @@
CONFIG_COMPILER_DUMP_RTL_FILES=y
CONFIG_TWAI_ISR_CACHE_SAFE=y
CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y
CONFIG_COMPILER_OPTIMIZATION_NONE=y
CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE=y

View File

@@ -1,2 +0,0 @@
| Supported Targets | ESP32-C5 |
| ----------------- | -------- |

View File

@@ -1,11 +0,0 @@
set(srcs "test_app_main.c")
if(CONFIG_SOC_TWAI_SUPPORT_FD)
list(APPEND srcs "test_twaifd.c")
endif()
idf_component_register(
SRCS ${srcs}
PRIV_REQUIRES esp_driver_twai
WHOLE_ARCHIVE
)

View File

@@ -1,336 +0,0 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <sys/param.h>
#include "unity.h"
#include "esp_log.h"
#include "esp_heap_caps.h"
#include "freertos/FreeRTOS.h"
#include "test_utils.h"
#include "esp_twai.h"
#include "esp_twai_onchip.h"
#define TEST_TX_GPIO 4
#define TEST_RX_GPIO 5
#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 bool test_driver_install_rx_cb(twai_node_handle_t handle, const twai_rx_done_event_data_t *edata, void *user_ctx)
{
twai_frame_header_t rx_header;
if (ESP_OK == twai_node_receive_from_isr(handle, &rx_header, NULL, 0, NULL)) {
ESP_EARLY_LOGI("Recv ", "id 0x%lx rtr %d", rx_header.id, rx_header.rtr);
}
if (rx_header.id != 0x100) {
TEST_FAIL(); //callback is unregistered, should not run here
}
return false;
}
TEST_CASE("twai install uninstall (loopback)", "[TWAI]")
{
esp_err_t ret;
twai_node_handle_t node_hdl[SOC_TWAI_CONTROLLER_NUM + 1];
twai_onchip_node_config_t node_config = {
.io_cfg.tx = TEST_TX_GPIO,
.io_cfg.rx = TEST_TX_GPIO, // Using same pin for test without transceiver
.bit_timing.bitrate = 1000000,
.data_timing.bitrate = 1000000,
.tx_queue_depth = TEST_TWAI_QUEUE_DEPTH,
.flags.enable_loopback = true,
.flags.enable_self_test = true,
};
// loop 10 times to check memory leak
for (uint8_t loop = 0; loop < 10; loop ++) {
for (uint8_t i = 0; i < SOC_TWAI_CONTROLLER_NUM + 1; i++) {
ret = twai_new_node_onchip(&node_config, &node_hdl[i]);
printf("Install TWAI%d return %s\n", i, esp_err_to_name(ret));
TEST_ASSERT(ret == ((i < SOC_TWAI_CONTROLLER_NUM) ? ESP_OK : ESP_ERR_NOT_FOUND));
}
// can't disable before enable
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, twai_node_disable(node_hdl[0]));
twai_event_callbacks_t user_cbs = {
.on_rx_done = test_driver_install_rx_cb,
};
TEST_ESP_OK(twai_node_register_event_callbacks(node_hdl[0], &user_cbs, NULL));
printf("Test unregister callback\n");
user_cbs.on_rx_done = NULL;
TEST_ESP_OK(twai_node_register_event_callbacks(node_hdl[0], &user_cbs, NULL));
twai_frame_t tx_frame = {
.header.id = 0x82,
.header.rtr = true,
};
printf("Test transmit before enable\n");
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, twai_node_transmit(node_hdl[0], &tx_frame, 0));
TEST_ESP_OK(twai_node_enable(node_hdl[0]));
TEST_ESP_OK(twai_node_disable(node_hdl[0]));
TEST_ESP_OK(twai_node_enable(node_hdl[0]));
TEST_ESP_OK(twai_node_transmit(node_hdl[0], &tx_frame, 0));
TEST_ESP_OK(twai_node_disable(node_hdl[0]));
TEST_ESP_OK(twai_node_delete(node_hdl[0]));
printf("Test install after delete\n");
TEST_ESP_OK(twai_new_node_onchip(&node_config, &node_hdl[SOC_TWAI_CONTROLLER_NUM]));
user_cbs.on_rx_done = test_driver_install_rx_cb,
TEST_ESP_OK(twai_node_register_event_callbacks(node_hdl[SOC_TWAI_CONTROLLER_NUM], &user_cbs, NULL));
TEST_ESP_OK(twai_node_enable(node_hdl[SOC_TWAI_CONTROLLER_NUM]));
tx_frame.header.id = 0x100;
TEST_ESP_OK(twai_node_transmit(node_hdl[SOC_TWAI_CONTROLLER_NUM], &tx_frame, 0));
twai_frame_t rx_frame;
printf("Test receive from task\n");
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, twai_node_receive_from_isr(node_hdl[SOC_TWAI_CONTROLLER_NUM], &rx_frame.header, rx_frame.buffer, rx_frame.buffer_len, NULL));
TEST_ESP_OK(twai_node_disable(node_hdl[SOC_TWAI_CONTROLLER_NUM]));
for (uint8_t i = 1; i <= SOC_TWAI_CONTROLLER_NUM; i++) {
printf("Uninstall TWAI%d\n", i - 1);
TEST_ESP_OK(twai_node_delete(node_hdl[i]));
}
}
}
static bool test_enable_disable_rx_cb(twai_node_handle_t handle, const twai_rx_done_event_data_t *edata, void *user_ctx)
{
twai_frame_t *rx_frame = user_ctx;
size_t ret_len;
if (ESP_OK == twai_node_receive_from_isr(handle, &rx_frame->header, rx_frame->buffer, rx_frame->buffer_len, &ret_len)) {
ESP_EARLY_LOGI("twai", "RX id 0x%x len %d ext %d fd %d brs %d esi %d", rx_frame->header.id, ret_len, rx_frame->header.ide, rx_frame->header.fdf, rx_frame->header.brs, rx_frame->header.esi);
rx_frame->buffer += rx_frame->buffer_len;
}
return false;
}
TEST_CASE("twai transmit stop resume (loopback)", "[TWAI]")
{
// prepare test memory
uint8_t *send_pkg_ptr = heap_caps_malloc(TEST_TRANS_LEN, MALLOC_CAP_8BIT);
uint8_t *recv_pkg_ptr = heap_caps_malloc(TEST_TRANS_LEN, MALLOC_CAP_8BIT);
TEST_ASSERT(send_pkg_ptr && recv_pkg_ptr);
printf("Transmit %d bytes package in %d frames\n", TEST_TRANS_LEN, TEST_FRAME_NUM);
twai_node_handle_t node_hdl;
twai_onchip_node_config_t node_config = {
.io_cfg.tx = TEST_TX_GPIO,
.io_cfg.rx = TEST_TX_GPIO, // Using same pin for test without transceiver
.bit_timing.bitrate = 20000,
.data_timing.bitrate = 4000000,
.data_timing.ssp_permill = 700, // ssp 70.0%
.tx_queue_depth = TEST_TWAI_QUEUE_DEPTH,
.flags.enable_loopback = true,
.flags.enable_self_test = true,
};
TEST_ESP_OK(twai_new_node_onchip(&node_config, &node_hdl));
// reconfig fd timing to 80M/(4+3+2+1)=8MHz, ssp=8/(4+3+2+1)=80%
twai_timing_advanced_config_t timing_fd = {
.brp = 1,
.prop_seg = 4,
.tseg_1 = 3,
.tseg_2 = 2,
.sjw = 2,
.ssp_offset = 8,
};
TEST_ESP_OK(twai_node_reconfig_timing(node_hdl, NULL, &timing_fd));
twai_frame_t rx_frame = {
.buffer = recv_pkg_ptr,
.buffer_len = TEST_FRAME_LEN,
};
twai_event_callbacks_t user_cbs = {
.on_rx_done = test_enable_disable_rx_cb,
};
TEST_ESP_OK(twai_node_register_event_callbacks(node_hdl, &user_cbs, &rx_frame));
TEST_ESP_OK(twai_node_enable(node_hdl));
//create and enqueue all transfers
twai_frame_t *tx_msgs = heap_caps_calloc(TEST_FRAME_NUM, sizeof(twai_frame_t), MALLOC_CAP_8BIT);
TEST_ASSERT(tx_msgs);
for (uint32_t tx_cnt = 0; tx_cnt < TEST_FRAME_NUM; tx_cnt++) {
tx_msgs[tx_cnt].header.id = tx_cnt | 0x400;
tx_msgs[tx_cnt].header.ide = !!(tx_cnt % 3);
tx_msgs[tx_cnt].header.fdf = !!(tx_cnt % 4);
tx_msgs[tx_cnt].header.brs = !!(tx_cnt % 2);
tx_msgs[tx_cnt].buffer = send_pkg_ptr + tx_cnt * TEST_FRAME_LEN;
tx_msgs[tx_cnt].buffer_len = ((tx_cnt + 1) == TEST_FRAME_NUM) ? (TEST_TRANS_LEN - tx_cnt * TEST_FRAME_LEN) : TEST_FRAME_LEN;
TEST_ESP_OK(twai_node_transmit(node_hdl, &tx_msgs[tx_cnt], 500));
}
TEST_ESP_OK(twai_node_disable(node_hdl));
for (uint8_t i = 3; i > 0; i--) {
printf("interrupted, %d sec\n", i);
vTaskDelay(1000);
}
printf("continuing ...\n");
TEST_ESP_OK(twai_node_enable(node_hdl));
//waiting pkg receive finish
while (rx_frame.buffer < recv_pkg_ptr + TEST_TRANS_LEN) {
vTaskDelay(1);
}
free(tx_msgs);
// check if pkg receive correct
printf("pkg check %s!!\n", memcmp(recv_pkg_ptr, send_pkg_ptr, TEST_TRANS_LEN) ? "failed" : "ok");
TEST_ASSERT_EQUAL_HEX8_ARRAY(send_pkg_ptr, recv_pkg_ptr, TEST_TRANS_LEN);
free(send_pkg_ptr);
free(recv_pkg_ptr);
TEST_ESP_OK(twai_node_disable(node_hdl));
TEST_ESP_OK(twai_node_delete(node_hdl));
}
static void test_random_trans_generator(twai_node_handle_t node_hdl, uint32_t trans_num)
{
uint8_t send_pkg_ptr[TWAIFD_FRAME_MAX_LEN];
twai_frame_t 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_msg.header.ide = !!(tx_cnt % 2);
tx_msg.header.rtr = !!(tx_cnt % 3);
tx_msg.header.fdf = !!(tx_cnt % 5);
tx_msg.buffer_len = tx_msg.header.fdf ? (tx_cnt % TWAIFD_FRAME_MAX_LEN) : (tx_cnt % TWAI_FRAME_MAX_LEN);
TEST_ESP_OK(twai_node_transmit(node_hdl, &tx_msg, 0));
vTaskDelay(8); //as async transaction, waiting trans done
}
}
static 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 = user_ctx;
size_t ret_len;
uint8_t recv_pkg_ptr[TWAIFD_FRAME_MAX_LEN];
twai_frame_t rx_frame = {
.buffer = recv_pkg_ptr,
.buffer_len = TWAIFD_FRAME_MAX_LEN,
};
if (ESP_OK == twai_node_receive_from_isr(handle, &rx_frame.header, rx_frame.buffer, rx_frame.buffer_len, &ret_len)) {
ESP_EARLY_LOGI("twai", "RX id 0x%4x len %2d ext %d rmt %d fd %d", rx_frame.header.id, ret_len, rx_frame.header.ide, rx_frame.header.rtr, rx_frame.header.fdf);
switch (test_ctrl[0]) {
case 0: //filter 0
TEST_ASSERT(rx_frame.header.id >= 0x10);
TEST_ASSERT(!rx_frame.header.ide);
break;
case 1:
case 2: break;
case 3: //filter 1
TEST_ASSERT((rx_frame.header.id & 0xfff0) == 0xf000);
TEST_ASSERT(rx_frame.header.ide && !rx_frame.header.fdf);
break;
case 4: //range filter 0
TEST_ASSERT(!rx_frame.header.ide && rx_frame.header.fdf);
break;
default: TEST_ASSERT(false);
}
test_ctrl[1] ++;
}
return false;
}
TEST_CASE("twai filter (loopback)", "[TWAI]")
{
uint8_t test_ctrl[2];
twai_node_handle_t node_hdl;
twai_onchip_node_config_t node_config = {
.io_cfg.tx = TEST_TX_GPIO,
.io_cfg.rx = TEST_TX_GPIO, // Using same pin for test without transceiver
.bit_timing.bitrate = 1000000,
.tx_queue_depth = TEST_TWAI_QUEUE_DEPTH,
.flags.enable_loopback = true,
.flags.enable_self_test = true,
};
TEST_ESP_OK(twai_new_node_onchip(&node_config, &node_hdl));
twai_event_callbacks_t user_cbs = {
.on_rx_done = test_filter_rx_done_cb,
};
TEST_ESP_OK(twai_node_register_event_callbacks(node_hdl, &user_cbs, test_ctrl));
//------------------ Filter 0 -------------------//
test_ctrl[0] = 0;
test_ctrl[1] = 0;
printf("Testing mask filter 0\n");
twai_mask_filter_config_t mfilter_cfg0 = {
.id = 0x10,
.mask = 0xf0,
.is_ext = false,
};
TEST_ESP_OK(twai_node_config_mask_filter(node_hdl, 0, &mfilter_cfg0));
TEST_ESP_OK(twai_node_enable(node_hdl));
test_random_trans_generator(node_hdl, 30);
TEST_ASSERT_UINT8_WITHIN(30 / 2 - 1, 30 / 2, test_ctrl[1]); //after filter, rx frame should less than tx num
printf("Change to receive ALL\n");
test_ctrl[0] = 1;
test_ctrl[1] = 0;
// set to receive ALL
mfilter_cfg0.id = 0;
mfilter_cfg0.mask = 0;
TEST_ESP_OK(twai_node_disable(node_hdl));
TEST_ESP_OK(twai_node_config_mask_filter(node_hdl, 0, &mfilter_cfg0));
TEST_ESP_OK(twai_node_enable(node_hdl));
test_random_trans_generator(node_hdl, 30);
TEST_ASSERT_EQUAL(30, test_ctrl[1]);
printf("Change to receive NONE\n");
test_ctrl[0] = 2;
test_ctrl[1] = 0;
// set to receive NONE
mfilter_cfg0.id = 0xFFFFFFFF;
mfilter_cfg0.mask = 0xFFFFFFFF;
TEST_ESP_OK(twai_node_disable(node_hdl));
TEST_ESP_OK(twai_node_config_mask_filter(node_hdl, 0, &mfilter_cfg0));
TEST_ESP_OK(twai_node_enable(node_hdl));
test_random_trans_generator(node_hdl, 30);
TEST_ASSERT_EQUAL(0, test_ctrl[1]);
//------------------ Filter 1 -------------------//
test_ctrl[0] = 3;
test_ctrl[1] = 0;
printf("Testing mask filter 1\n");
twai_mask_filter_config_t mfilter_cfg1 = {
.id = 0xf000,
.mask = 0xfff0,
.is_ext = true,
.no_fd = true,
};
TEST_ESP_OK(twai_node_disable(node_hdl));
TEST_ESP_OK(twai_node_config_mask_filter(node_hdl, 1, &mfilter_cfg1));
TEST_ESP_OK(twai_node_enable(node_hdl));
test_random_trans_generator(node_hdl, 30);
TEST_ASSERT_UINT8_WITHIN(30 / 2 - 1, 30 / 2, test_ctrl[1]);
// set to receive NONE
mfilter_cfg1.id = 0xFFFFFFFF;
mfilter_cfg1.mask = 0xFFFFFFFF;
TEST_ESP_OK(twai_node_disable(node_hdl));
TEST_ESP_OK(twai_node_config_mask_filter(node_hdl, 1, &mfilter_cfg1));
//------------------ Range Filter 0 -------------------//
test_ctrl[0] = 4;
test_ctrl[1] = 0;
printf("Testing range filter 0\n");
twai_range_filter_config_t rfilter_cfg0 = {
.range_low = 0,
.range_high = 0xFFFFFFFF,
.is_ext = false,
.no_classic = true,
};
TEST_ESP_OK(twai_node_config_range_filter(node_hdl, 0, &rfilter_cfg0));
TEST_ESP_OK(twai_node_enable(node_hdl));
test_random_trans_generator(node_hdl, 30);
TEST_ASSERT_UINT8_WITHIN(30 / 2 - 1, 30 / 2, test_ctrl[1]);
TEST_ESP_OK(twai_node_disable(node_hdl));
TEST_ESP_OK(twai_node_delete(node_hdl));
}