twai: add initial version of driver component test

This commit is contained in:
morris
2022-10-27 17:39:13 +08:00
parent a25123f703
commit a83165fea0
12 changed files with 417 additions and 0 deletions

View File

@@ -72,3 +72,7 @@ components/driver/test_apps/touch_sensor_v1:
components/driver/test_apps/touch_sensor_v2:
disable:
- if: SOC_TOUCH_VERSION_2 != 1
components/driver/test_apps/twai:
disable:
- if: SOC_TWAI_SUPPORTED != 1

View File

@@ -0,0 +1,18 @@
# This is the project CMakeLists.txt file for the test subproject
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(twai_test)
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-dir ${CMAKE_BINARY_DIR}/esp-idf/driver/
--elf-file ${CMAKE_BINARY_DIR}/twai_test.elf
find-refs
--from-sections=.iram0.text
--to-sections=.flash.text,.flash.rodata
--exit-code
DEPENDS ${elf}
)
endif()

View File

@@ -0,0 +1,8 @@
| Supported Targets | ESP32 | ESP32-C3 | ESP32-C6 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- |
# Enable Socket CAN Device with bitrate 250Kbps
```bash
sudo ip link set can0 up type can bitrate 250000 restart-ms 100
```

View File

@@ -0,0 +1,8 @@
set(srcs "test_app_main.c"
"test_twai_loop_back.c"
"test_twai_interactive.c")
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
# the component can be registered as WHOLE_ARCHIVE
idf_component_register(SRCS ${srcs}
WHOLE_ARCHIVE)

View File

@@ -0,0 +1,51 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "unity.h"
#include "unity_test_runner.h"
#include "esp_heap_caps.h"
// Some resources are lazy allocated in the TWAI driver, the threshold is left for that case
#define TEST_MEMORY_LEAK_THRESHOLD (-200)
static size_t before_free_8bit;
static size_t before_free_32bit;
static void check_leak(size_t before_free, size_t after_free, const char *type)
{
ssize_t delta = after_free - before_free;
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta);
TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
}
void setUp(void)
{
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
}
void tearDown(void)
{
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
check_leak(before_free_8bit, after_free_8bit, "8BIT");
check_leak(before_free_32bit, after_free_32bit, "32BIT");
}
void app_main(void)
{
// _______ ___ ___ _____ _
// |_ _\ \ / / \ |_ _| |_ _|__ ___| |_
// | | \ \ /\ / / _ \ | | | |/ _ \/ __| __|
// | | \ V V / ___ \ | | | | __/\__ \ |_
// |_| \_/\_/_/ \_\___| |_|\___||___/\__|
printf(" _______ ___ ___ _____ _\r\n");
printf("|_ _\\ \\ / / \\ |_ _| |_ _|__ ___| |_\r\n");
printf(" | | \\ \\ /\\ / / _ \\ | | | |/ _ \\/ __| __|\r\n");
printf(" | | \\ V V / ___ \\ | | | | __/\\__ \\ |_\r\n");
printf(" |_| \\_/\\_/_/ \\_\\___| |_|\\___||___/\\__|\r\n");
unity_run_menu();
}

View File

@@ -0,0 +1,85 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_attr.h"
#include "unity.h"
#include "unity_test_utils.h"
#include "driver/twai.h"
#include "soc/soc_caps.h"
#include "esp_attr.h"
#if CONFIG_TWAI_ISR_IN_IRAM
static void IRAM_ATTR test_delay_post_cache_disable(void *args)
{
esp_rom_delay_us(1000);
}
#endif
TEST_CASE("twai_listen_only", "[twai]")
{
twai_timing_config_t t_config = TWAI_TIMING_CONFIG_250KBITS();
twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();
twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(0, 2, TWAI_MODE_LISTEN_ONLY);
#if CONFIG_TWAI_ISR_IN_IRAM
g_config.intr_flags |= ESP_INTR_FLAG_IRAM;
#endif
// listen only mode doesn't need a tx queue
g_config.tx_queue_len = 0;
TEST_ESP_OK(twai_driver_install(&g_config, &t_config, &f_config));
TEST_ESP_OK(twai_start());
#if CONFIG_TWAI_ISR_IN_IRAM
printf("disable flash cache and check if we can still receive the frame\r\n");
for (int i = 0; i < 100; i++) {
unity_utils_run_cache_disable_stub(test_delay_post_cache_disable, NULL);
}
#endif
uint8_t expected_data[8] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88};
twai_message_t rx_msg;
TEST_ESP_OK(twai_receive(&rx_msg, portMAX_DELAY));
TEST_ASSERT_EQUAL(0x123, rx_msg.identifier);
for (int i = 0; i < rx_msg.data_length_code; i++) {
TEST_ASSERT_EQUAL_HEX8(expected_data[i], rx_msg.data[i]);
}
TEST_ESP_OK(twai_stop());
TEST_ESP_OK(twai_driver_uninstall());
}
TEST_CASE("twai_remote_request", "[twai]")
{
twai_timing_config_t t_config = TWAI_TIMING_CONFIG_250KBITS();
twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();
twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(0, 2, TWAI_MODE_NORMAL);
TEST_ESP_OK(twai_driver_install(&g_config, &t_config, &f_config));
TEST_ESP_OK(twai_start());
twai_message_t req_msg = {
.identifier = 0x6688,
.data_length_code = 8,
.rtr = true, // remote request
.extd = true,// extended ID
};
TEST_ESP_OK(twai_transmit(&req_msg, portMAX_DELAY));
uint8_t expected_data[8] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80};
twai_message_t res_msg;
TEST_ESP_OK(twai_receive(&res_msg, portMAX_DELAY));
TEST_ASSERT_EQUAL(0x6688, res_msg.identifier);
for (int i = 0; i < 8; i++) {
TEST_ASSERT_EQUAL_HEX8(expected_data[i], res_msg.data[i]);
}
TEST_ESP_OK(twai_stop());
TEST_ESP_OK(twai_driver_uninstall());
}

View File

@@ -0,0 +1,117 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "unity.h"
#include "driver/twai.h"
#include "soc/soc_caps.h"
#include "esp_attr.h"
TEST_CASE("driver_life_cycle", "[twai-loop-back]")
{
twai_timing_config_t t_config = TWAI_TIMING_CONFIG_100KBITS();
twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();
twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(0, 0, TWAI_MODE_NO_ACK);
printf("install driver\r\n");
TEST_ESP_OK(twai_driver_install(&g_config, &t_config, &f_config));
// can't install the driver multiple times
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, twai_driver_install(&g_config, &t_config, &f_config));
printf("start driver\r\n");
TEST_ESP_OK(twai_start());
// can't start the driver again if it's already in running state
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, twai_start());
// can't uninstall the driver before stopping it
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, twai_driver_uninstall());
printf("stop driver\r\n");
TEST_ESP_OK(twai_stop());
printf("uninstall driver\r\n");
TEST_ESP_OK(twai_driver_uninstall());
}
TEST_CASE("twai_bit_timing", "[twai-loop-back]")
{
twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();
twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(0, 0, TWAI_MODE_NO_ACK);
twai_timing_config_t t_config = {
.quanta_resolution_hz = 33333, // invalid resolution
.tseg_1 = 15,
.tseg_2 = 4,
.sjw = 1,
};
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, twai_driver_install(&g_config, &t_config, &f_config));
t_config.quanta_resolution_hz = 2000000;
TEST_ESP_OK(twai_driver_install(&g_config, &t_config, &f_config));
TEST_ESP_OK(twai_driver_uninstall());
}
TEST_CASE("twai_mode_std_no_ack_25kbps", "[twai-loop-back]")
{
twai_timing_config_t t_config = TWAI_TIMING_CONFIG_25KBITS();
twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();
// bind the TX and RX to the same GPIO to act like a loopback
twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(0, 0, TWAI_MODE_NO_ACK);
printf("install twai driver\r\n");
TEST_ESP_OK(twai_driver_install(&g_config, &t_config, &f_config));
TEST_ESP_OK(twai_start());
twai_message_t tx_msg = {
.identifier = 0x123,
.data_length_code = 8,
.data = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88},
.self = true, // Transmitted message will also received by the same node
};
printf("transmit message\r\n");
TEST_ESP_OK(twai_transmit(&tx_msg, pdMS_TO_TICKS(1000)));
printf("receive message\r\n");
twai_message_t rx_msg;
TEST_ESP_OK(twai_receive(&rx_msg, pdMS_TO_TICKS(1000)));
TEST_ASSERT_TRUE(rx_msg.data_length_code == 8);
for (int i = 0; i < 8; i++) {
TEST_ASSERT_EQUAL(tx_msg.data[i], rx_msg.data[i]);
}
TEST_ESP_OK(twai_stop());
TEST_ESP_OK(twai_driver_uninstall());
}
TEST_CASE("twai_mode_ext_no_ack_250kbps", "[twai-loop-back]")
{
twai_timing_config_t t_config = TWAI_TIMING_CONFIG_250KBITS();
twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();
// bind the TX and RX to the same GPIO to act like a loopback
twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(0, 0, TWAI_MODE_NO_ACK);
printf("install twai driver\r\n");
TEST_ESP_OK(twai_driver_install(&g_config, &t_config, &f_config));
TEST_ESP_OK(twai_start());
twai_message_t tx_msg = {
.identifier = 0x12345,
.data_length_code = 6,
.data = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66},
.self = true, // Transmitted message will also received by the same node
.extd = true, // Extended Frame Format (29bit ID)
};
printf("transmit message\r\n");
TEST_ESP_OK(twai_transmit(&tx_msg, pdMS_TO_TICKS(1000)));
printf("receive message\r\n");
twai_message_t rx_msg;
TEST_ESP_OK(twai_receive(&rx_msg, pdMS_TO_TICKS(1000)));
TEST_ASSERT_TRUE(rx_msg.data_length_code == 6);
for (int i = 0; i < 6; i++) {
TEST_ASSERT_EQUAL(tx_msg.data[i], rx_msg.data[i]);
}
TEST_ESP_OK(twai_stop());
TEST_ESP_OK(twai_driver_uninstall());
}

View File

@@ -0,0 +1,104 @@
# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import logging
from time import sleep
import pytest
from can import Bus, Message
from pytest_embedded import Dut
@pytest.mark.esp32
@pytest.mark.esp32c3
@pytest.mark.esp32c6
@pytest.mark.esp32s2
@pytest.mark.esp32s3
@pytest.mark.generic
@pytest.mark.parametrize(
'config',
[
'release',
],
indirect=True,
)
def test_twai_self(dut: Dut) -> None:
dut.expect_exact('Press ENTER to see the list of tests')
dut.write('[twai-loop-back]')
dut.expect_unity_test_output()
@pytest.fixture(name='socket_can', scope='module')
def fixture_create_socket_can() -> Bus:
# See README.md for instructions on how to set up the socket CAN with the bitrate
bus = Bus(interface='socketcan', channel='can0', bitrate=250000)
yield bus
bus.shutdown()
@pytest.mark.esp32
@pytest.mark.esp32c3
@pytest.mark.esp32c6
@pytest.mark.esp32s2
@pytest.mark.esp32s3
@pytest.mark.skip(reason='Runner not set up yet')
@pytest.mark.parametrize(
'config',
[
'iram_safe',
],
indirect=True,
)
def test_twai_listen_only(dut: Dut, socket_can: Bus) -> None:
dut.expect_exact('Press ENTER to see the list of tests')
# TEST_CASE("twai_listen_only", "[twai]")
dut.write('"twai_listen_only"')
# wait the DUT to block at the receive API
sleep(0.03)
message = Message(
arbitration_id=0x123,
is_extended_id=False,
data=[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88],
)
socket_can.send(message, timeout=0.2)
dut.expect_unity_test_output()
@pytest.mark.esp32
@pytest.mark.esp32c3
@pytest.mark.esp32c6
@pytest.mark.esp32s2
@pytest.mark.esp32s3
@pytest.mark.skip(reason='Runner not set up yet')
@pytest.mark.parametrize(
'config',
[
'release',
],
indirect=True,
)
def test_twai_remote_request(dut: Dut, socket_can: Bus) -> None:
dut.expect_exact('Press ENTER to see the list of tests')
# TEST_CASE("twai_remote_request", "[twai]")
dut.write('"twai_remote_request"')
while True:
req = socket_can.recv(timeout=0.2)
# wait for the remote request frame
if req is not None and req.is_remote_frame:
break
logging.info(f'Received message: {req}')
reply = Message(
arbitration_id=req.arbitration_id,
is_extended_id=req.is_extended_id,
data=[0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80],
)
socket_can.send(reply, timeout=0.2)
dut.expect_unity_test_output()

View File

@@ -0,0 +1,12 @@
CONFIG_COMPILER_DUMP_RTL_FILES=y
CONFIG_TWAI_ISR_IN_IRAM=y
CONFIG_COMPILER_OPTIMIZATION_NONE=y
# silent the error check, as the error string are stored in rodata, causing RTL check failure
CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y
# place non-ISR FreeRTOS functions in Flash
CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y
# twai driver uses assert in the ISR code path
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE=y

View File

@@ -0,0 +1,5 @@
CONFIG_PM_ENABLE=y
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y

View File

@@ -0,0 +1,2 @@
CONFIG_FREERTOS_HZ=1000
CONFIG_ESP_TASK_WDT=n

View File

@@ -17,3 +17,6 @@ netifaces
rangehttpserver
dbus-python; sys_platform == 'linux'
protobuf
# for twai tests, communicate with socket can device (e.g. Canable)
python-can