Skip to content

Commit

Permalink
Merge branch 'bugfix/rmt_iram_safe_test' into 'master'
Browse files Browse the repository at this point in the history
rmt: add iram safe test cases

Closes IDFGH-7974

See merge request espressif/esp-idf!19413
  • Loading branch information
suda-morris committed Aug 12, 2022
2 parents cdd50af + 6f2ac1c commit e51aee4
Show file tree
Hide file tree
Showing 27 changed files with 447 additions and 120 deletions.
16 changes: 6 additions & 10 deletions components/driver/deprecated/rmt_legacy.c
Expand Up @@ -936,13 +936,9 @@ esp_err_t rmt_driver_uninstall(rmt_channel_t channel)
RMT_ENTER_CRITICAL();
// check channel's working mode
if (p_rmt_obj[channel]->rx_buf) {
rmt_ll_enable_interrupt(rmt_contex.hal.regs, RMT_LL_EVENT_RX_DONE(RMT_DECODE_RX_CHANNEL(channel)), false);
rmt_ll_enable_interrupt(rmt_contex.hal.regs, RMT_LL_EVENT_RX_ERROR(RMT_DECODE_RX_CHANNEL(channel)), false);
#if SOC_RMT_SUPPORT_RX_PINGPONG
rmt_ll_enable_interrupt(rmt_contex.hal.regs, RMT_LL_EVENT_RX_THRES(RMT_DECODE_RX_CHANNEL(channel)), false);
#endif
rmt_ll_enable_interrupt(rmt_contex.hal.regs, RMT_LL_EVENT_RX_MASK(RMT_DECODE_RX_CHANNEL(channel)) | RMT_LL_EVENT_RX_ERROR(RMT_DECODE_RX_CHANNEL(channel)), false);
} else {
rmt_ll_enable_interrupt(rmt_contex.hal.regs, RMT_LL_EVENT_TX_DONE(channel) | RMT_LL_EVENT_TX_ERROR(channel) | RMT_LL_EVENT_TX_THRES(channel), false);
rmt_ll_enable_interrupt(rmt_contex.hal.regs, RMT_LL_EVENT_TX_MASK(channel) | RMT_LL_EVENT_TX_ERROR(channel), false);
}
RMT_EXIT_CRITICAL();

Expand Down Expand Up @@ -1001,10 +997,10 @@ esp_err_t rmt_driver_install(rmt_channel_t channel, size_t rx_buf_size, int intr
}

#if CONFIG_RINGBUF_PLACE_ISR_FUNCTIONS_INTO_FLASH
if (intr_alloc_flags & ESP_INTR_FLAG_IRAM ) {
ESP_LOGE(TAG, "ringbuf ISR functions in flash, but used in IRAM interrupt");
return ESP_ERR_INVALID_ARG;
}
if (intr_alloc_flags & ESP_INTR_FLAG_IRAM ) {
ESP_LOGE(TAG, "ringbuf ISR functions in flash, but used in IRAM interrupt");
return ESP_ERR_INVALID_ARG;
}
#endif

#if !CONFIG_SPIRAM_USE_MALLOC
Expand Down
2 changes: 1 addition & 1 deletion components/driver/mcpwm/mcpwm_cap.c
Expand Up @@ -317,7 +317,7 @@ esp_err_t mcpwm_capture_channel_register_event_callbacks(mcpwm_cap_channel_handl

// lazy install interrupt service
if (!cap_channel->intr) {
// we want the interrupt servie to be enabled after allocation successfully
// we want the interrupt service to be enabled after allocation successfully
int isr_flags = MCPWM_INTR_ALLOC_FLAG & ~ ESP_INTR_FLAG_INTRDISABLED;
ESP_RETURN_ON_ERROR(esp_intr_alloc_intrstatus(mcpwm_periph_signals.groups[group_id].irq_id, isr_flags,
(uint32_t)mcpwm_ll_intr_get_status_reg(hal->dev), MCPWM_LL_EVENT_CAPTURE(cap_chan_id),
Expand Down
2 changes: 1 addition & 1 deletion components/driver/mcpwm/mcpwm_cmpr.c
Expand Up @@ -160,7 +160,7 @@ esp_err_t mcpwm_comparator_register_event_callbacks(mcpwm_cmpr_handle_t cmpr, co

// lazy install interrupt service
if (!cmpr->intr) {
// we want the interrupt servie to be enabled after allocation successfully
// we want the interrupt service to be enabled after allocation successfully
int isr_flags = MCPWM_INTR_ALLOC_FLAG & ~ ESP_INTR_FLAG_INTRDISABLED;
ESP_RETURN_ON_ERROR(esp_intr_alloc_intrstatus(mcpwm_periph_signals.groups[group_id].irq_id, isr_flags,
(uint32_t)mcpwm_ll_intr_get_status_reg(hal->dev), MCPWM_LL_EVENT_CMP_EQUAL(oper_id, cmpr_id),
Expand Down
2 changes: 1 addition & 1 deletion components/driver/mcpwm/mcpwm_fault.c
Expand Up @@ -243,7 +243,7 @@ esp_err_t mcpwm_fault_register_event_callbacks(mcpwm_fault_handle_t fault, const

// lazy install interrupt service
if (!gpio_fault->intr) {
// we want the interrupt servie to be enabled after allocation successfully
// we want the interrupt service to be enabled after allocation successfully
int isr_flags = MCPWM_INTR_ALLOC_FLAG & ~ESP_INTR_FLAG_INTRDISABLED;
ESP_RETURN_ON_ERROR(esp_intr_alloc_intrstatus(mcpwm_periph_signals.groups[group_id].irq_id, isr_flags,
(uint32_t)mcpwm_ll_intr_get_status_reg(hal->dev), MCPWM_LL_EVENT_FAULT_MASK(fault_id),
Expand Down
2 changes: 1 addition & 1 deletion components/driver/mcpwm/mcpwm_oper.c
Expand Up @@ -233,7 +233,7 @@ esp_err_t mcpwm_operator_register_event_callbacks(mcpwm_oper_handle_t oper, cons

// lazy install interrupt service
if (!oper->intr) {
// we want the interrupt servie to be enabled after allocation successfully
// we want the interrupt service to be enabled after allocation successfully
int isr_flags = MCPWM_INTR_ALLOC_FLAG & ~ ESP_INTR_FLAG_INTRDISABLED;
ESP_RETURN_ON_ERROR(esp_intr_alloc_intrstatus(mcpwm_periph_signals.groups[group_id].irq_id, isr_flags,
(uint32_t)mcpwm_ll_intr_get_status_reg(hal->dev), MCPWM_LL_EVENT_OPER_MASK(oper_id),
Expand Down
2 changes: 1 addition & 1 deletion components/driver/pulse_cnt.c
Expand Up @@ -98,7 +98,7 @@ struct pcnt_unit_t {

struct pcnt_chan_t {
pcnt_unit_t *unit; // pointer to the PCNT unit where it derives from
uint32_t channel_id; // channel ID, index from 0
int channel_id; // channel ID, index from 0
int edge_gpio_num;
int level_gpio_num;
};
Expand Down
7 changes: 5 additions & 2 deletions components/driver/test_apps/gptimer/main/CMakeLists.txt
@@ -1,6 +1,9 @@
set(srcs "test_app_main.c"
"test_gptimer.c"
"test_gptimer_iram.c")
"test_gptimer.c")

if(CONFIG_GPTIMER_ISR_IRAM_SAFE)
list(APPEND srcs "test_gptimer_iram.c")
endif()

# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
# the component can be registered as WHOLE_ARCHIVE
Expand Down
86 changes: 21 additions & 65 deletions components/driver/test_apps/gptimer/main/test_gptimer_iram.c
Expand Up @@ -5,102 +5,58 @@
*/

#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include <inttypes.h>
#include "unity.h"
#include "unity_test_utils.h"
#include "esp_attr.h"
#include "driver/gptimer.h"
#include "spi_flash_mmap.h"
#include "esp_flash.h"
#include "soc/soc_caps.h"

#if CONFIG_GPTIMER_ISR_IRAM_SAFE

typedef struct {
size_t buf_size;
uint8_t *buf;
size_t flash_addr;
size_t repeat_count;
SemaphoreHandle_t done_sem;
} read_task_arg_t;

typedef struct {
size_t delay_time_us;
size_t repeat_count;
} block_task_arg_t;

static bool IRAM_ATTR on_gptimer_alarm_cb(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx)
{
block_task_arg_t *arg = (block_task_arg_t *)user_ctx;
esp_rom_delay_us(arg->delay_time_us);
arg->repeat_count++;
uint32_t *alarm_counts = (uint32_t *)user_ctx;
(*alarm_counts)++;
return false;
}

static void flash_read_task(void *varg)
static void IRAM_ATTR test_delay_post_cache_disable(void *args)
{
read_task_arg_t *arg = (read_task_arg_t *)varg;
for (size_t i = 0; i < arg->repeat_count; i++) {
TEST_ESP_OK(esp_flash_read(NULL, arg->buf, arg->flash_addr, arg->buf_size));
}
xSemaphoreGive(arg->done_sem);
vTaskDelete(NULL);
esp_rom_delay_us(1000);
}

TEST_CASE("gptimer_iram_interrupt_safe", "[gptimer]")
TEST_CASE("gptimer_interrupt_iram_safe", "[gptimer]")
{
gptimer_handle_t gptimer = NULL;
const size_t size = 128;
uint8_t *buf = malloc(size);
TEST_ASSERT_NOT_NULL(buf);
SemaphoreHandle_t done_sem = xSemaphoreCreateBinary();
TEST_ASSERT_NOT_NULL(done_sem);
read_task_arg_t read_arg = {
.buf_size = size,
.buf = buf,
.flash_addr = 0,
.repeat_count = 1000,
.done_sem = done_sem,
};

block_task_arg_t block_arg = {
.repeat_count = 0,
.delay_time_us = 100,
};

gptimer_config_t timer_config = {
.clk_src = GPTIMER_CLK_SRC_DEFAULT,
.direction = GPTIMER_COUNT_UP,
.resolution_hz = 1 * 1000 * 1000,
.resolution_hz = 1 * 1000 * 1000, // 1MHz, 1 tick = 1us
};
TEST_ESP_OK(gptimer_new_timer(&timer_config, &gptimer));
gptimer_event_callbacks_t cbs = {
.on_alarm = on_gptimer_alarm_cb,
};
uint32_t alarm_counts = 0;
TEST_ESP_OK(gptimer_register_event_callbacks(gptimer, &cbs, &alarm_counts));
gptimer_alarm_config_t alarm_config = {
.reload_count = 0,
.alarm_count = 120,
.alarm_count = 100, // 100us per alarm event
.flags.auto_reload_on_alarm = true,
};
TEST_ESP_OK(gptimer_set_alarm_action(gptimer, &alarm_config));
TEST_ESP_OK(gptimer_register_event_callbacks(gptimer, &cbs, &block_arg));
TEST_ESP_OK(gptimer_enable(gptimer));
TEST_ESP_OK(gptimer_start(gptimer));

xTaskCreatePinnedToCore(flash_read_task, "read_flash", 2048, &read_arg, 3, NULL, portNUM_PROCESSORS - 1);
// wait for task done
xSemaphoreTake(done_sem, portMAX_DELAY);
printf("alarm callback runs %d times\r\n", block_arg.repeat_count);
TEST_ASSERT_GREATER_THAN(1000, block_arg.repeat_count);
vTaskDelay(pdMS_TO_TICKS(10));

printf("disable flash cache and check the alarm events are still in working\r\n");
for (int i = 0; i < 10; i++) {
unity_utils_run_cache_disable_stub(test_delay_post_cache_disable, NULL);
}
printf("alarm counts: %"PRIu32"\r\n", alarm_counts);
TEST_ASSERT_GREATER_THAN(150, alarm_counts);

// delete gptimer
TEST_ESP_OK(gptimer_stop(gptimer));
TEST_ESP_OK(gptimer_disable(gptimer));
TEST_ESP_OK(gptimer_del_timer(gptimer));
vSemaphoreDelete(done_sem);
free(buf);
// leave time for IDLE task to recycle deleted task
vTaskDelay(2);
}

#endif // CONFIG_GPTIMER_ISR_IRAM_SAFE
Expand Up @@ -3,4 +3,3 @@ set(srcs "test_app_main.c"

idf_component_register(SRCS ${srcs}
WHOLE_ARCHIVE)
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")
Expand Up @@ -13,8 +13,8 @@
#include "esp_log.h"
#include "esp_cpu.h"
#include "unity.h"
#include "unity_test_utils.h"
#include "esp_rom_gpio.h"

#include "ir_tools.h"
#include "driver/rmt.h"

Expand Down Expand Up @@ -295,7 +295,7 @@ static void do_nec_tx_rx(uint32_t flags)
// build NEC codes
cmd = 0x20;
while (cmd <= 0x30) {
ESP_LOGI(TAG, "Send command 0x%x to address 0x%x", cmd, addr);
ESP_LOGI(TAG, "Send command 0x%"PRIx32" to address 0x%"PRIx32, cmd, addr);
// Send new key code
TEST_ESP_OK(s_ir_builder->build_frame(s_ir_builder, addr, cmd));
TEST_ESP_OK(s_ir_builder->get_result(s_ir_builder, &items, &length));
Expand All @@ -315,7 +315,7 @@ static void do_nec_tx_rx(uint32_t flags)
length /= 4; // one RMT = 4 Bytes
if (s_ir_parser->input(s_ir_parser, items, length) == ESP_OK) {
if (s_ir_parser->get_scan_code(s_ir_parser, &addr, &cmd, &repeat) == ESP_OK) {
ESP_LOGI(TAG, "Scan Code %s --- addr: 0x%04x cmd: 0x%04x", repeat ? "(repeat)" : "", addr, cmd);
ESP_LOGI(TAG, "Scan Code %s --- addr: 0x%04"PRIx32" cmd: 0x%04"PRIx32, repeat ? "(repeat)" : "", addr, cmd);
}
}
vRingbufferReturnItem(rb, (void *) items);
Expand Down Expand Up @@ -397,7 +397,7 @@ TEST_CASE("RMT TX stop", "[rmt]")
vTaskDelay(pdMS_TO_TICKS(1000));

// build NEC codes
ESP_LOGI(TAG, "Plan to send command 0x%x~0x%x to address 0x%x", cmd, cmd + count, addr);
ESP_LOGI(TAG, "Plan to send command 0x%"PRIx32"~0x%"PRIx32" to address 0x%"PRIx32, cmd, cmd + count, addr);
for (int i = 0; i <= count; i++) {
TEST_ESP_OK(s_ir_builder->build_frame(s_ir_builder, addr, cmd));
cmd++;
Expand All @@ -417,7 +417,7 @@ TEST_CASE("RMT TX stop", "[rmt]")
length /= 4; // one RMT = 4 Bytes
if (s_ir_parser->input(s_ir_parser, frames, length) == ESP_OK) {
if (s_ir_parser->get_scan_code(s_ir_parser, &addr, &cmd, &repeat) == ESP_OK) {
ESP_LOGI(TAG, "Scan Code %s --- addr: 0x%04x cmd: 0x%04x", repeat ? "(repeat)" : "", addr, cmd);
ESP_LOGI(TAG, "Scan Code %s --- addr: 0x%04"PRIx32"cmd: 0x%04"PRIx32, repeat ? "(repeat)" : "", addr, cmd);
num++;
}
}
Expand Down Expand Up @@ -527,12 +527,14 @@ TEST_CASE("RMT TX simultaneously", "[rmt]")
TEST_ESP_OK(rmt_wait_tx_done(channel0, portMAX_DELAY));
TEST_ESP_OK(rmt_wait_tx_done(channel1, portMAX_DELAY));

ESP_LOGI(TAG, "tx_end_time0=%u, tx_end_time1=%u", tx_end_time0, tx_end_time1);
ESP_LOGI(TAG, "tx_end_time0=%"PRIu32", tx_end_time1=%"PRIu32, tx_end_time0, tx_end_time1);
TEST_ASSERT_LESS_OR_EQUAL_UINT32(2000, tx_end_time1 - tx_end_time0);

TEST_ESP_OK(rmt_remove_channel_from_group(channel0));
TEST_ESP_OK(rmt_remove_channel_from_group(channel1));

rmt_register_tx_end_callback(NULL, NULL);

TEST_ESP_OK(rmt_driver_uninstall(channel0));
TEST_ESP_OK(rmt_driver_uninstall(channel1));

Expand Down Expand Up @@ -568,7 +570,7 @@ TEST_CASE("RMT TX loop", "[rmt]")
// register callback functions, invoked when tx loop count to ceiling
rmt_register_tx_end_callback(rmt_tx_loop_end, NULL);
// build NEC codes
ESP_LOGI(TAG, "Send command 0x%x to address 0x%x", cmd, addr);
ESP_LOGI(TAG, "Send command 0x%"PRIx32" to address 0x%"PRIx32, cmd, addr);
// Send new key code
TEST_ESP_OK(s_ir_builder->build_frame(s_ir_builder, addr, cmd));
TEST_ESP_OK(s_ir_builder->get_result(s_ir_builder, &items, &length));
Expand All @@ -582,7 +584,7 @@ TEST_CASE("RMT TX loop", "[rmt]")
if (s_ir_parser->input(s_ir_parser, items, length) == ESP_OK) {
if (s_ir_parser->get_scan_code(s_ir_parser, &addr, &cmd, &repeat) == ESP_OK) {
count++;
ESP_LOGI(TAG, "Scan Code %s --- addr: 0x%04x cmd: 0x%04x", repeat ? "(repeat)" : "", addr, cmd);
ESP_LOGI(TAG, "Scan Code %s --- addr: 0x%04"PRIx32" cmd: 0x%04"PRIx32, repeat ? "(repeat)" : "", addr, cmd);
}
}
vRingbufferReturnItem(rb, (void *) items);
Expand All @@ -593,6 +595,42 @@ TEST_CASE("RMT TX loop", "[rmt]")
}

TEST_ASSERT_EQUAL(10, count);
rmt_register_tx_end_callback(NULL, NULL);
rmt_clean_testbench(tx_channel, rx_channel);
}
#endif

static void IRAM_ATTR test_delay_post_cache_disable(void *args)
{
esp_rom_delay_us(10000);
}

TEST_CASE("RMT Interrupt IRAM Safe", "[rmt]")
{
rmt_config_t tx = {
.channel = RMT_CHANNEL_0,
.gpio_num = 0,
.mem_block_num = 1,
.clk_div = 40,
.rmt_mode = RMT_MODE_TX,
};
TEST_ESP_OK(rmt_config(&tx));
TEST_ESP_OK(rmt_set_source_clk(tx.channel, RMT_BASECLK_APB));
// install interrupt with IRAM safe
TEST_ESP_OK(rmt_driver_install(tx.channel, 0, ESP_INTR_FLAG_IRAM));

// send a large buffer, ensure the RMT hardware is still in work when we disable the flash cache afterwords
rmt_item32_t items[256] = {};
for (int i = 0; i < 256; i++) {
items[i].level0 = 0;
items[i].duration0 = 1;
items[i].level1 = 1;
items[i].duration1 = 1;
}
rmt_write_items(RMT_CHANNEL_0, items, 256, false);

unity_utils_run_cache_disable_stub(test_delay_post_cache_disable, NULL);

TEST_ESP_OK(rmt_wait_tx_done(RMT_CHANNEL_0, portMAX_DELAY));
TEST_ESP_OK(rmt_driver_uninstall(RMT_CHANNEL_0));
}
11 changes: 4 additions & 7 deletions components/driver/test_apps/mcpwm/main/test_mcpwm_iram.c
Expand Up @@ -9,9 +9,9 @@
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "unity.h"
#include "unity_test_utils.h"
#include "soc/soc_caps.h"
#include "esp_private/esp_clk.h"
#include "esp_private/spi_flash_os.h"
#include "driver/mcpwm_cap.h"
#include "driver/mcpwm_sync.h"
#include "driver/gpio.h"
Expand All @@ -28,15 +28,12 @@ static bool IRAM_ATTR test_capture_callback_iram_safe(mcpwm_cap_channel_handle_t
return false;
}

static void IRAM_ATTR test_mcpwm_capture_gpio_simulate(int gpio_sig)
static void IRAM_ATTR test_simulate_input_post_cache_disable(void *args)
{
// disable flash cache
spi_flash_guard_get()->start();
int gpio_sig = (int)args;
gpio_set_level(gpio_sig, 1);
esp_rom_delay_us(1000);
gpio_set_level(gpio_sig, 0);
// enable flash cache
spi_flash_guard_get()->end();
}

TEST_CASE("mcpwm_capture_iram_safe", "[mcpwm]")
Expand Down Expand Up @@ -77,7 +74,7 @@ TEST_CASE("mcpwm_capture_iram_safe", "[mcpwm]")
TEST_ESP_OK(mcpwm_capture_timer_start(cap_timer));

printf("disable cache, simulate GPIO capture signal\r\n");
test_mcpwm_capture_gpio_simulate(cap_gpio);
unity_utils_run_cache_disable_stub(test_simulate_input_post_cache_disable, (void *)cap_gpio);

printf("capture value: Pos=%"PRIu32", Neg=%"PRIu32"\r\n", cap_value[0], cap_value[1]);
// Capture timer is clocked from APB by default
Expand Down
7 changes: 5 additions & 2 deletions components/driver/test_apps/pulse_cnt/main/CMakeLists.txt
@@ -1,7 +1,10 @@
set(srcs "test_app_main.c"
"test_pulse_cnt_simulator.c"
"test_pulse_cnt.c"
"test_pulse_cnt_iram.c")
"test_pulse_cnt.c")

if(CONFIG_PCNT_ISR_IRAM_SAFE)
list(APPEND srcs "test_pulse_cnt_iram.c")
endif()

# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
# the component can be registered as WHOLE_ARCHIVE
Expand Down

0 comments on commit e51aee4

Please sign in to comment.