feat(rmt): introduce RMT_ENCODER_FUNC_ATTR for encoder functions

Closes https://github.com/espressif/esp-idf/issues/15832
This commit is contained in:
morris
2025-04-23 15:37:54 +08:00
parent 182b33efb2
commit ae5f8e550c
19 changed files with 88 additions and 33 deletions

View File

@@ -1,12 +1,16 @@
menu "ESP-Driver:RMT Configurations"
depends on SOC_RMT_SUPPORTED
config RMT_ENCODER_FUNC_IN_IRAM
bool "Place RMT encoder function in IRAM"
default y
help
Place RMT encoder function into IRAM for better performance and fewer cache misses.
config RMT_TX_ISR_HANDLER_IN_IRAM
bool "Place RMT TX ISR handler in IRAM to reduce latency"
default y
select RMT_OBJ_CACHE_SAFE
select GDMA_CTRL_FUNC_IN_IRAM if SOC_RMT_SUPPORT_DMA
select BITSCRAMBLER_CTRL_FUNC_IN_IRAM if SOC_BITSCRAMBLER_SUPPORTED && SOC_RMT_SUPPORT_DMA
help
Place RMT TX ISR handler in IRAM to reduce latency caused by cache miss.
@@ -21,14 +25,13 @@ menu "ESP-Driver:RMT Configurations"
bool "Place RMT receive function in IRAM"
default n
select RMT_OBJ_CACHE_SAFE
select GDMA_CTRL_FUNC_IN_IRAM if SOC_RMT_SUPPORT_DMA
help
Place RMT receive function into IRAM for better performance and fewer cache misses.
config RMT_TX_ISR_CACHE_SAFE
bool "Allow RMT TX ISR to execute when cache is disabled"
bool "Allow RMT TX ISR to execute when cache is disabled" if !SPI_FLASH_AUTO_SUSPEND
select RMT_TX_ISR_HANDLER_IN_IRAM
select GDMA_ISR_HANDLER_IN_IRAM if SOC_RMT_SUPPORT_DMA
select RMT_ENCODER_FUNC_IN_IRAM
default n
help
Enable this option to allow the RMT TX Interrupt Service Routine (ISR)
@@ -36,9 +39,8 @@ menu "ESP-Driver:RMT Configurations"
might be turned off, but the RMT TX functionality is still required to operate correctly.
config RMT_RX_ISR_CACHE_SAFE
bool "Allow RMT RX ISR to execute when cache is disabled"
bool "Allow RMT RX ISR to execute when cache is disabled" if !SPI_FLASH_AUTO_SUSPEND
select RMT_RX_ISR_HANDLER_IN_IRAM
select GDMA_ISR_HANDLER_IN_IRAM if SOC_RMT_SUPPORT_DMA
default n
help
Enable this option to allow the RMT RX Interrupt Service Routine (ISR)
@@ -63,7 +65,7 @@ menu "ESP-Driver:RMT Configurations"
Please enable this option by caution, as it will increase the binary size.
config RMT_ISR_IRAM_SAFE
bool "RMT ISR IRAM-Safe (Deprecated)"
bool "RMT ISR IRAM-Safe (Deprecated)" if !SPI_FLASH_AUTO_SUSPEND
select RMT_TX_ISR_CACHE_SAFE
select RMT_RX_ISR_CACHE_SAFE
default n

View File

@@ -7,7 +7,9 @@
#pragma once
#include <stdint.h>
#include "sdkconfig.h"
#include "esp_err.h"
#include "esp_attr.h"
#include "hal/rmt_types.h"
#include "driver/rmt_types.h"
@@ -69,6 +71,12 @@ struct rmt_encoder_t {
esp_err_t (*del)(rmt_encoder_t *encoder);
};
#if CONFIG_RMT_ENCODER_FUNC_IN_IRAM
#define RMT_ENCODER_FUNC_ATTR IRAM_ATTR
#else
#define RMT_ENCODER_FUNC_ATTR
#endif
/**
* @brief Callback for simple callback encoder
*

View File

@@ -8,13 +8,6 @@ entries:
rmt_tx: rmt_tx_do_transaction (noflash)
rmt_tx: rmt_encode_check_result (noflash)
rmt_tx: rmt_tx_mark_eof (noflash)
rmt_encoder: rmt_encoder_reset (noflash)
rmt_encoder_bytes: rmt_encode_bytes (noflash)
rmt_encoder_bytes: rmt_bytes_encoder_reset (noflash)
rmt_encoder_copy: rmt_encode_copy (noflash)
rmt_encoder_copy: rmt_copy_encoder_reset (noflash)
rmt_encoder_simple: rmt_encode_simple (noflash)
rmt_encoder_simple: rmt_simple_encoder_reset (noflash)
if SOC_RMT_SUPPORT_TX_LOOP_COUNT = y:
rmt_tx: rmt_isr_handle_tx_loop_end (noflash)
@@ -22,9 +15,8 @@ entries:
if SOC_RMT_SUPPORT_DMA = y:
rmt_tx: rmt_dma_tx_eof_cb (noflash)
if SOC_BITSCRAMBLER_SUPPORTED = y:
rmt_encoder_bs: rmt_encode_bs (noflash)
rmt_encoder_bs: rmt_bs_encoder_reset (noflash)
if RMT_ENCODER_FUNC_IN_IRAM = y:
rmt_encoder: rmt_encoder_reset (noflash)
if RMT_RX_ISR_HANDLER_IN_IRAM = y:
rmt_rx: rmt_rx_default_isr (noflash)

View File

@@ -14,6 +14,7 @@ typedef struct rmt_bs_encoder_t {
bitscrambler_handle_t bs; // BitScrambler handle
} rmt_bs_encoder_t;
RMT_ENCODER_FUNC_ATTR
static esp_err_t rmt_bs_encoder_reset(rmt_encoder_t *encoder)
{
rmt_bs_encoder_t *bs_encoder = __containerof(encoder, rmt_bs_encoder_t, base);
@@ -23,14 +24,7 @@ static esp_err_t rmt_bs_encoder_reset(rmt_encoder_t *encoder)
return ESP_OK;
}
static esp_err_t rmt_del_bs_encoder(rmt_encoder_t *encoder)
{
rmt_bs_encoder_t *bs_encoder = __containerof(encoder, rmt_bs_encoder_t, base);
bitscrambler_free(bs_encoder->bs);
free(bs_encoder);
return ESP_OK;
}
RMT_ENCODER_FUNC_ATTR
static size_t rmt_encode_bs(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *input_raw,
size_t data_size, rmt_encode_state_t *ret_state)
{
@@ -109,11 +103,24 @@ static size_t rmt_encode_bs(rmt_encoder_t *encoder, rmt_channel_handle_t channel
return copy_len;
}
static esp_err_t rmt_del_bs_encoder(rmt_encoder_t *encoder)
{
rmt_bs_encoder_t *bs_encoder = __containerof(encoder, rmt_bs_encoder_t, base);
bitscrambler_free(bs_encoder->bs);
free(bs_encoder);
return ESP_OK;
}
esp_err_t rmt_new_bitscrambler_encoder(const rmt_bs_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder)
{
esp_err_t ret = ESP_OK;
rmt_bs_encoder_t *encoder = NULL;
ESP_GOTO_ON_FALSE(config && ret_encoder, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(config && ret_encoder, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
// bitscrambler function is used under RMT TX ISR context
// if the cache is disabled, all functions called by ISR must be in IRAM
#if CONFIG_RMT_TX_ISR_CACHE_SAFE && !CONFIG_BITSCRAMBLER_CTRL_FUNC_IN_IRAM
ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_STATE, TAG, "CONFIG_BITSCRAMBLER_CTRL_FUNC_IN_IRAM must be enabled");
#endif
encoder = rmt_alloc_encoder_mem(sizeof(rmt_bs_encoder_t));
ESP_GOTO_ON_FALSE(encoder, ESP_ERR_NO_MEM, err, TAG, "no mem for bitscrambler encoder");

View File

@@ -18,6 +18,7 @@ typedef struct rmt_bytes_encoder_t {
} flags;
} rmt_bytes_encoder_t;
RMT_ENCODER_FUNC_ATTR
static esp_err_t rmt_bytes_encoder_reset(rmt_encoder_t *encoder)
{
rmt_bytes_encoder_t *bytes_encoder = __containerof(encoder, rmt_bytes_encoder_t, base);
@@ -27,6 +28,7 @@ static esp_err_t rmt_bytes_encoder_reset(rmt_encoder_t *encoder)
return ESP_OK;
}
RMT_ENCODER_FUNC_ATTR
static size_t rmt_encode_bytes(rmt_encoder_t *encoder, rmt_channel_handle_t channel,
const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)
{

View File

@@ -12,6 +12,7 @@ typedef struct rmt_copy_encoder_t {
size_t last_symbol_index; // index of symbol position in the primary stream
} rmt_copy_encoder_t;
RMT_ENCODER_FUNC_ATTR
static esp_err_t rmt_copy_encoder_reset(rmt_encoder_t *encoder)
{
rmt_copy_encoder_t *copy_encoder = __containerof(encoder, rmt_copy_encoder_t, base);
@@ -19,6 +20,7 @@ static esp_err_t rmt_copy_encoder_reset(rmt_encoder_t *encoder)
return ESP_OK;
}
RMT_ENCODER_FUNC_ATTR
static size_t rmt_encode_copy(rmt_encoder_t *encoder, rmt_channel_handle_t channel,
const void *input_symbols, size_t data_size, rmt_encode_state_t *ret_state)
{

View File

@@ -19,6 +19,7 @@ typedef struct rmt_simple_encoder_t {
bool callback_done; //true if we can't call the callback for more data anymore.
} rmt_simple_encoder_t;
RMT_ENCODER_FUNC_ATTR
static esp_err_t rmt_simple_encoder_reset(rmt_encoder_t *encoder)
{
rmt_simple_encoder_t *simple_encoder = __containerof(encoder, rmt_simple_encoder_t, base);
@@ -29,6 +30,7 @@ static esp_err_t rmt_simple_encoder_reset(rmt_encoder_t *encoder)
return ESP_OK;
}
RMT_ENCODER_FUNC_ATTR
static size_t rmt_encode_simple(rmt_encoder_t *encoder, rmt_channel_handle_t channel,
const void *data, size_t data_size, rmt_encode_state_t *ret_state)
{

View File

@@ -180,7 +180,13 @@ esp_err_t rmt_new_rx_channel(const rmt_rx_channel_config_t *config, rmt_channel_
ESP_RETURN_ON_FALSE(GPIO_IS_VALID_GPIO(config->gpio_num), ESP_ERR_INVALID_ARG, TAG, "invalid GPIO number %d", config->gpio_num);
ESP_RETURN_ON_FALSE((config->mem_block_symbols & 0x01) == 0 && config->mem_block_symbols >= SOC_RMT_MEM_WORDS_PER_CHANNEL,
ESP_ERR_INVALID_ARG, TAG, "mem_block_symbols must be even and at least %d", SOC_RMT_MEM_WORDS_PER_CHANNEL);
#if !SOC_RMT_SUPPORT_DMA
#if SOC_RMT_SUPPORT_DMA
if (config->flags.with_dma) {
#if CONFIG_RMT_RX_ISR_CACHE_SAFE && (!CONFIG_GDMA_ISR_HANDLER_IN_IRAM || !CONFIG_GDMA_CTRL_FUNC_IN_IRAM)
ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_STATE, TAG, "CONFIG_GDMA_ISR_HANDLER_IN_IRAM and CONFIG_GDMA_CTRL_FUNC_IN_IRAM must be enabled");
#endif
}
#else
ESP_RETURN_ON_FALSE(config->flags.with_dma == 0, ESP_ERR_NOT_SUPPORTED, TAG, "DMA not supported");
#endif // SOC_RMT_SUPPORT_DMA

View File

@@ -243,7 +243,13 @@ esp_err_t rmt_new_tx_channel(const rmt_tx_channel_config_t *config, rmt_channel_
ESP_RETURN_ON_FALSE(GPIO_IS_VALID_OUTPUT_GPIO(config->gpio_num), ESP_ERR_INVALID_ARG, TAG, "invalid GPIO number %d", config->gpio_num);
ESP_RETURN_ON_FALSE((config->mem_block_symbols & 0x01) == 0 && config->mem_block_symbols >= SOC_RMT_MEM_WORDS_PER_CHANNEL,
ESP_ERR_INVALID_ARG, TAG, "mem_block_symbols must be even and at least %d", SOC_RMT_MEM_WORDS_PER_CHANNEL);
#if !SOC_RMT_SUPPORT_DMA
#if SOC_RMT_SUPPORT_DMA
if (config->flags.with_dma) {
#if CONFIG_RMT_TX_ISR_CACHE_SAFE && (!CONFIG_GDMA_ISR_HANDLER_IN_IRAM || !CONFIG_GDMA_CTRL_FUNC_IN_IRAM)
ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_STATE, TAG, "CONFIG_GDMA_ISR_HANDLER_IN_IRAM and CONFIG_GDMA_CTRL_FUNC_IN_IRAM must be enabled");
#endif
}
#else
ESP_RETURN_ON_FALSE(config->flags.with_dma == 0, ESP_ERR_NOT_SUPPORTED, TAG, "DMA not supported");
#endif

View File

@@ -97,6 +97,13 @@ TEST_CASE("rmt TX with bitscrambler", "[rmt]")
}, 5, &transmit_config));
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(1000)));
TEST_ESP_OK(rmt_receive(rx_channel, symbols, sizeof(symbols), &receive_config));
printf("transmit again!\r\n");
TEST_ESP_OK(rmt_transmit(tx_channel, bs_encoder, (uint8_t[]) {
0x12, 0x34, 0x56, 0x78, 0x9a, // dummy test values, will be further processed by bitscrambler program
}, 5, &transmit_config));
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(1000)));
printf("disable tx+rx channel\r\n");
TEST_ESP_OK(rmt_disable(tx_channel));
TEST_ESP_OK(rmt_disable(rx_channel));

View File

@@ -20,7 +20,8 @@ typedef struct {
rmt_symbol_word_t reset_code;
} rmt_led_strip_encoder_t;
IRAM_ATTR static size_t rmt_encode_led_strip(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)
RMT_ENCODER_FUNC_ATTR
static size_t rmt_encode_led_strip(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)
{
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
rmt_encode_state_t session_state = RMT_ENCODING_RESET;
@@ -62,7 +63,8 @@ static esp_err_t rmt_del_led_strip_encoder(rmt_encoder_t *encoder)
return ESP_OK;
}
IRAM_ATTR static esp_err_t rmt_led_strip_encoder_reset(rmt_encoder_t *encoder)
RMT_ENCODER_FUNC_ATTR
static esp_err_t rmt_led_strip_encoder_reset(rmt_encoder_t *encoder)
{
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
rmt_encoder_reset(led_encoder->bytes_encoder);
@@ -113,7 +115,8 @@ typedef struct {
int state;
} rmt_nec_protocol_encoder_t;
IRAM_ATTR static size_t rmt_encode_nec_protocol(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)
RMT_ENCODER_FUNC_ATTR
static size_t rmt_encode_nec_protocol(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)
{
rmt_nec_protocol_encoder_t *nec_encoder = __containerof(encoder, rmt_nec_protocol_encoder_t, base);
rmt_encode_state_t session_state = RMT_ENCODING_RESET;
@@ -177,7 +180,8 @@ static esp_err_t rmt_del_nec_protocol_encoder(rmt_encoder_t *encoder)
return ESP_OK;
}
IRAM_ATTR static esp_err_t rmt_nec_protocol_encoder_reset(rmt_encoder_t *encoder)
RMT_ENCODER_FUNC_ATTR
static esp_err_t rmt_nec_protocol_encoder_reset(rmt_encoder_t *encoder)
{
rmt_nec_protocol_encoder_t *nec_encoder = __containerof(encoder, rmt_nec_protocol_encoder_t, base);
rmt_encoder_reset(nec_encoder->copy_encoder);

View File

@@ -3,6 +3,9 @@ CONFIG_RMT_TX_ISR_CACHE_SAFE=y
CONFIG_RMT_RX_ISR_CACHE_SAFE=y
CONFIG_RMT_RECV_FUNC_IN_IRAM=y
CONFIG_GPIO_CTRL_FUNC_IN_IRAM=y
CONFIG_BITSCRAMBLER_CTRL_FUNC_IN_IRAM=y
CONFIG_GDMA_ISR_HANDLER_IN_IRAM=y
CONFIG_GDMA_CTRL_FUNC_IN_IRAM=y
CONFIG_COMPILER_OPTIMIZATION_NONE=y
# place non-ISR FreeRTOS functions in Flash
CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y

View File

@@ -589,6 +589,8 @@ There is a Kconfig option :ref:`CONFIG_RMT_TX_ISR_CACHE_SAFE` and :ref:`CONFIG_R
This Kconfig option allows the interrupt handler to run while the cache is disabled but comes at the cost of increased IRAM consumption.
Please note, when :ref:`CONFIG_RMT_TX_ISR_CACHE_SAFE` is enabled, you must also place the encoder functions (mainly the :cpp:member:`rmt_encoder_t::encode` and :cpp:member:`rmt_encoder_t::reset`) into IRAM. You can use :c:macro:`RMT_ENCODER_FUNC_ATTR` to decorate your encoder functions.
Another Kconfig option :ref:`CONFIG_RMT_RECV_FUNC_IN_IRAM` can place :cpp:func:`rmt_receive` into the IRAM as well. So that the receive function can be used even when the flash cache is disabled.
.. _rmt-thread-safety:

View File

@@ -589,6 +589,8 @@ Cache 安全
启用该选项可以保证 cache 禁用时的中断运行,但会相应增加 IRAM 占用。
请注意,当 :ref:`CONFIG_RMT_TX_ISR_CACHE_SAFE` 使能后,你必须将编码器函数 (主要是 :cpp:member:`rmt_encoder_t::encode`:cpp:member:`rmt_encoder_t::reset`) 放进 IRAM 中。建议你使用 :c:macro:`RMT_ENCODER_FUNC_ATTR` 来装饰你的编码器函数。
另外一个 Kconfig 选项 :ref:`CONFIG_RMT_RECV_FUNC_IN_IRAM` 可以将 :cpp:func:`rmt_receive` 函数放进内部的 IRAM 中,从而当 flash cache 被关闭的时候,这个函数也能够被使用。
.. _rmt-thread-safety:

View File

@@ -45,6 +45,7 @@ static void make_dshot_frame(dshot_esc_frame_t *frame, uint16_t throttle, bool t
frame->val = ((val & 0xFF) << 8) | ((val & 0xFF00) >> 8);
}
RMT_ENCODER_FUNC_ATTR
static size_t rmt_encode_dshot_esc(rmt_encoder_t *encoder, rmt_channel_handle_t channel,
const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)
{
@@ -97,6 +98,7 @@ static esp_err_t rmt_del_dshot_encoder(rmt_encoder_t *encoder)
return ESP_OK;
}
RMT_ENCODER_FUNC_ATTR
static esp_err_t rmt_dshot_encoder_reset(rmt_encoder_t *encoder)
{
rmt_dshot_esc_encoder_t *dshot_encoder = __containerof(encoder, rmt_dshot_esc_encoder_t, base);

View File

@@ -18,6 +18,7 @@ typedef struct {
int state;
} rmt_ir_nec_encoder_t;
RMT_ENCODER_FUNC_ATTR
static size_t rmt_encode_ir_nec(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)
{
rmt_ir_nec_encoder_t *nec_encoder = __containerof(encoder, rmt_ir_nec_encoder_t, base);
@@ -85,6 +86,7 @@ static esp_err_t rmt_del_ir_nec_encoder(rmt_encoder_t *encoder)
return ESP_OK;
}
RMT_ENCODER_FUNC_ATTR
static esp_err_t rmt_ir_nec_encoder_reset(rmt_encoder_t *encoder)
{
rmt_ir_nec_encoder_t *nec_encoder = __containerof(encoder, rmt_ir_nec_encoder_t, base);

View File

@@ -17,6 +17,7 @@ typedef struct {
rmt_symbol_word_t reset_code;
} rmt_led_strip_encoder_t;
RMT_ENCODER_FUNC_ATTR
static size_t rmt_encode_led_strip(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)
{
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
@@ -62,6 +63,7 @@ static esp_err_t rmt_del_led_strip_encoder(rmt_encoder_t *encoder)
return ESP_OK;
}
RMT_ENCODER_FUNC_ATTR
static esp_err_t rmt_led_strip_encoder_reset(rmt_encoder_t *encoder)
{
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);

View File

@@ -15,6 +15,7 @@ typedef struct {
uint32_t resolution;
} rmt_musical_score_encoder_t;
RMT_ENCODER_FUNC_ATTR
static size_t rmt_encode_musical_score(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)
{
rmt_musical_score_encoder_t *score_encoder = __containerof(encoder, rmt_musical_score_encoder_t, base);
@@ -41,6 +42,7 @@ static esp_err_t rmt_del_musical_score_encoder(rmt_encoder_t *encoder)
return ESP_OK;
}
RMT_ENCODER_FUNC_ATTR
static esp_err_t rmt_musical_score_encoder_reset(rmt_encoder_t *encoder)
{
rmt_musical_score_encoder_t *score_encoder = __containerof(encoder, rmt_musical_score_encoder_t, base);

View File

@@ -27,6 +27,7 @@ typedef struct {
rmt_symbol_word_t curve_table[];
} rmt_stepper_curve_encoder_t;
RMT_ENCODER_FUNC_ATTR
static size_t rmt_encode_stepper_motor_curve(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)
{
rmt_stepper_curve_encoder_t *motor_encoder = __containerof(encoder, rmt_stepper_curve_encoder_t, base);
@@ -53,6 +54,7 @@ static esp_err_t rmt_del_stepper_motor_curve_encoder(rmt_encoder_t *encoder)
return ESP_OK;
}
RMT_ENCODER_FUNC_ATTR
static esp_err_t rmt_reset_stepper_motor_curve_encoder(rmt_encoder_t *encoder)
{
rmt_stepper_curve_encoder_t *motor_encoder = __containerof(encoder, rmt_stepper_curve_encoder_t, base);