Skip to content

Commit 3428044

Browse files
committed
Merge branch 'feat/bitscrambler_rmt' into 'master'
feat(rmt): play the BitScrambler as an RMT encoder Closes IDF-12018 See merge request espressif/esp-idf!37542
2 parents a70fe45 + c3f4223 commit 3428044

File tree

20 files changed

+457
-110
lines changed

20 files changed

+457
-110
lines changed

components/esp_driver_bitscrambler/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ endif()
1111

1212
idf_component_register(SRCS ${srcs}
1313
PRIV_REQUIRES "esp_mm"
14-
INCLUDE_DIRS "include")
14+
INCLUDE_DIRS "include"
15+
LDFRAGMENTS "linker.lf")
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
menu "BitScrambler Configurations"
2+
depends on SOC_BITSCRAMBLER_SUPPORTED
3+
config BITSCRAMBLER_CTRL_FUNC_IN_IRAM
4+
bool "Place BitScrambler control functions in IRAM"
5+
default n
6+
select BITSCRAMBLER_OBJ_CACHE_SAFE
7+
help
8+
Place BitScrambler control functions into IRAM for better performance and fewer cache misses.
9+
10+
config BITSCRAMBLER_OBJ_CACHE_SAFE
11+
bool
12+
default n
13+
help
14+
This will ensure the BitScrambler object will not be allocated from a memory region
15+
where its cache can be disabled.
16+
endmenu # BitScrambler Configurations
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[mapping:bitscrambler_driver]
2+
archive: libesp_driver_bitscrambler.a
3+
entries:
4+
if BITSCRAMBLER_CTRL_FUNC_IN_IRAM = y:
5+
bitscrambler: bitscrambler_reset (noflash)
6+
bitscrambler: bitscrambler_start (noflash)

components/esp_driver_bitscrambler/src/bitscrambler.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,20 @@
66
#include <string.h>
77
#include <stdatomic.h>
88
#include "esp_log.h"
9+
#include "esp_heap_caps.h"
910
#include "driver/bitscrambler.h"
1011
#include "bitscrambler_private.h"
1112
#include "hal/bitscrambler_ll.h"
1213
#include "esp_private/periph_ctrl.h"
1314

1415
static const char *TAG = "bitscrambler";
1516

17+
#if CONFIG_BITSCRAMBLER_OBJ_CACHE_SAFE
18+
#define BITSCRAMBLER_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
19+
#else
20+
#define BITSCRAMBLER_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
21+
#endif
22+
1623
#define BITSCRAMBLER_BINARY_VER 1 //max version we're compatible with
1724
#define BITSCRAMBLER_HW_REV 0
1825

@@ -153,8 +160,8 @@ esp_err_t bitscrambler_new(const bitscrambler_config_t *config, bitscrambler_han
153160
if (!handle) {
154161
return ESP_ERR_INVALID_ARG;
155162
}
156-
// Allocate memory for private data
157-
bitscrambler_t *bs = calloc(1, sizeof(bitscrambler_t));
163+
// Allocate memory for the BitScrambler object from internal memory
164+
bitscrambler_t *bs = heap_caps_calloc(1, sizeof(bitscrambler_t), BITSCRAMBLER_MEM_ALLOC_CAPS);
158165
if (!bs) {
159166
return ESP_ERR_NO_MEM;
160167
}
@@ -173,7 +180,7 @@ esp_err_t bitscrambler_new(const bitscrambler_config_t *config, bitscrambler_han
173180
return r;
174181
}
175182

176-
// Done.
183+
// Return the handle
177184
*handle = bs;
178185
return ESP_OK;
179186
}

components/esp_driver_rmt/CMakeLists.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,14 @@ if(CONFIG_SOC_RMT_SUPPORTED)
1212
"src/rmt_tx.c")
1313
endif()
1414

15+
if(CONFIG_SOC_BITSCRAMBLER_SUPPORTED AND CONFIG_SOC_RMT_SUPPORT_DMA)
16+
list(APPEND srcs "src/rmt_encoder_bs.c")
17+
endif()
18+
1519
if(${target} STREQUAL "linux")
1620
set(priv_requires "")
1721
else()
18-
set(priv_requires esp_pm esp_driver_gpio esp_mm)
22+
set(priv_requires esp_pm esp_driver_gpio esp_driver_bitscrambler esp_mm)
1923
endif()
2024

2125
idf_component_register(SRCS ${srcs}

components/esp_driver_rmt/Kconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ menu "ESP-Driver:RMT Configurations"
55
bool "Place RMT TX ISR handler in IRAM to reduce latency"
66
default y
77
select RMT_OBJ_CACHE_SAFE
8+
select GDMA_CTRL_FUNC_IN_IRAM if SOC_RMT_SUPPORT_DMA
9+
select BITSCRAMBLER_CTRL_FUNC_IN_IRAM if SOC_BITSCRAMBLER_SUPPORTED && SOC_RMT_SUPPORT_DMA
810
help
911
Place RMT TX ISR handler in IRAM to reduce latency caused by cache miss.
1012

@@ -19,6 +21,7 @@ menu "ESP-Driver:RMT Configurations"
1921
bool "Place RMT receive function in IRAM"
2022
default n
2123
select RMT_OBJ_CACHE_SAFE
24+
select GDMA_CTRL_FUNC_IN_IRAM if SOC_RMT_SUPPORT_DMA
2225
help
2326
Place RMT receive function into IRAM for better performance and fewer cache misses.
2427

components/esp_driver_rmt/include/driver/rmt_encoder.h

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -26,6 +26,7 @@ typedef enum {
2626
RMT_ENCODING_RESET = 0, /*!< The encoding session is in reset state */
2727
RMT_ENCODING_COMPLETE = (1 << 0), /*!< The encoding session is finished, the caller can continue with subsequent encoding */
2828
RMT_ENCODING_MEM_FULL = (1 << 1), /*!< The encoding artifact memory is full, the caller should return from current encoding session */
29+
RMT_ENCODING_WITH_EOF = (1 << 2), /*!< The encoding session has inserted the EOF marker to the symbol stream */
2930
} rmt_encode_state_t;
3031

3132
/**
@@ -126,6 +127,13 @@ typedef struct {
126127
typedef struct {
127128
} rmt_copy_encoder_config_t;
128129

130+
/**
131+
* @brief BitScrambler encoder configuration
132+
*/
133+
typedef struct {
134+
const void *program_bin; /*!< BitScrambler program */
135+
} rmt_bs_encoder_config_t;
136+
129137
/**
130138
* @brief Simple callback encoder configuration
131139
*/
@@ -169,6 +177,8 @@ esp_err_t rmt_bytes_encoder_update_config(rmt_encoder_handle_t bytes_encoder, co
169177
/**
170178
* @brief Create RMT copy encoder, which copies the given RMT symbols into RMT memory
171179
*
180+
* @note When transmitting using a copy encoder, ensure that the input data is already formatted as `rmt_symbol_word_t`.
181+
*
172182
* @param[in] config Copy encoder configuration
173183
* @param[out] ret_encoder Returned encoder handle
174184
* @return
@@ -179,6 +189,22 @@ esp_err_t rmt_bytes_encoder_update_config(rmt_encoder_handle_t bytes_encoder, co
179189
*/
180190
esp_err_t rmt_new_copy_encoder(const rmt_copy_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder);
181191

192+
/**
193+
* @brief Create RMT BitScrambler encoder
194+
*
195+
* @note The BitScrambler encoder is used to encode the user data into RMT symbols by providing the BitScrambler assembly program.
196+
* The BitScrambler program is a binary blob, it should take control of the whole encoding stuffs, including inserting the EOF marker.
197+
*
198+
* @param[in] config BitScrambler encoder configuration
199+
* @param[out] ret_encoder Returned encoder handle
200+
* @return
201+
* - ESP_OK: Create RMT BitScrambler encoder successfully
202+
* - ESP_ERR_INVALID_ARG: Create RMT BitScrambler encoder failed because of invalid argument
203+
* - ESP_ERR_NO_MEM: Create RMT BitScrambler encoder failed because out of memory
204+
* - ESP_FAIL: Create RMT BitScrambler encoder failed because of other error
205+
*/
206+
esp_err_t rmt_new_bitscrambler_encoder(const rmt_bs_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder);
207+
182208
/**
183209
* @brief Create RMT simple callback encoder, which uses a callback to convert incoming
184210
* data into RMT symbols.

components/esp_driver_rmt/linker.lf

Lines changed: 8 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,24 @@ entries:
88
rmt_tx: rmt_tx_do_transaction (noflash)
99
rmt_tx: rmt_encode_check_result (noflash)
1010
rmt_tx: rmt_tx_mark_eof (noflash)
11+
rmt_encoder: rmt_encoder_reset (noflash)
1112
rmt_encoder_bytes: rmt_encode_bytes (noflash)
13+
rmt_encoder_bytes: rmt_bytes_encoder_reset (noflash)
1214
rmt_encoder_copy: rmt_encode_copy (noflash)
15+
rmt_encoder_copy: rmt_copy_encoder_reset (noflash)
1316
rmt_encoder_simple: rmt_encode_simple (noflash)
17+
rmt_encoder_simple: rmt_simple_encoder_reset (noflash)
1418

1519
if SOC_RMT_SUPPORT_TX_LOOP_COUNT = y:
1620
rmt_tx: rmt_isr_handle_tx_loop_end (noflash)
1721

1822
if SOC_RMT_SUPPORT_DMA = y:
1923
rmt_tx: rmt_dma_tx_eof_cb (noflash)
2024

25+
if SOC_BITSCRAMBLER_SUPPORTED = y:
26+
rmt_encoder_bs: rmt_encode_bs (noflash)
27+
rmt_encoder_bs: rmt_bs_encoder_reset (noflash)
28+
2129
if RMT_RX_ISR_HANDLER_IN_IRAM = y:
2230
rmt_rx: rmt_rx_default_isr (noflash)
2331
rmt_rx: rmt_isr_handle_rx_done (noflash)
@@ -30,51 +38,3 @@ entries:
3038

3139
if RMT_RECV_FUNC_IN_IRAM = y:
3240
rmt_rx: rmt_receive (noflash)
33-
34-
[mapping:rmt_driver_gdma]
35-
archive: libesp_hw_support.a
36-
entries:
37-
if RMT_TX_ISR_HANDLER_IN_IRAM = y && SOC_RMT_SUPPORT_DMA = y:
38-
gdma: gdma_reset (noflash)
39-
gdma: gdma_start (noflash)
40-
gdma: gdma_append (noflash)
41-
42-
if RMT_RECV_FUNC_IN_IRAM = y && SOC_RMT_SUPPORT_DMA = y:
43-
gdma: gdma_reset (noflash)
44-
gdma: gdma_start (noflash)
45-
46-
[mapping:rmt_driver_hal]
47-
archive: libhal.a
48-
entries:
49-
if RMT_TX_ISR_HANDLER_IN_IRAM = y:
50-
if SOC_RMT_SUPPORT_DMA = y:
51-
gdma_hal_top: gdma_hal_append (noflash)
52-
gdma_hal_top: gdma_hal_reset (noflash)
53-
gdma_hal_top: gdma_hal_start_with_desc (noflash)
54-
55-
# GDMA implementation layer for AHB-DMA version 1
56-
if SOC_AHB_GDMA_VERSION = 1:
57-
gdma_hal_ahb_v1: gdma_ahb_hal_append (noflash)
58-
gdma_hal_ahb_v1: gdma_ahb_hal_reset (noflash)
59-
gdma_hal_ahb_v1: gdma_ahb_hal_start_with_desc (noflash)
60-
61-
# GDMA implementation layer for AHB-DMA version 2
62-
if SOC_AHB_GDMA_VERSION = 2:
63-
gdma_hal_ahb_v2: gdma_ahb_hal_append (noflash)
64-
gdma_hal_ahb_v2: gdma_ahb_hal_reset (noflash)
65-
gdma_hal_ahb_v2: gdma_ahb_hal_start_with_desc (noflash)
66-
67-
if RMT_RECV_FUNC_IN_IRAM = y:
68-
if SOC_RMT_SUPPORT_DMA = y:
69-
gdma_hal_top: gdma_hal_reset (noflash)
70-
gdma_hal_top: gdma_hal_start_with_desc (noflash)
71-
72-
# GDMA implementation layer for AHB-DMA version 1
73-
if SOC_AHB_GDMA_VERSION = 1:
74-
gdma_hal_ahb_v1: gdma_ahb_hal_reset (noflash)
75-
gdma_hal_ahb_v1: gdma_ahb_hal_start_with_desc (noflash)
76-
77-
# GDMA implementation layer for AHB-DMA version 2
78-
if SOC_AHB_GDMA_VERSION = 2:
79-
gdma_hal_ahb_v2: gdma_ahb_hal_reset (noflash)
80-
gdma_hal_ahb_v2: gdma_ahb_hal_start_with_desc (noflash)
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include "driver/rmt_encoder.h"
8+
#include "driver/bitscrambler.h"
9+
#include "rmt_private.h"
10+
11+
typedef struct rmt_bs_encoder_t {
12+
rmt_encoder_t base; // the base "class", declares the standard encoder interface
13+
size_t last_byte_index; // index of the encoding byte in the primary stream
14+
bitscrambler_handle_t bs; // BitScrambler handle
15+
} rmt_bs_encoder_t;
16+
17+
static esp_err_t rmt_bs_encoder_reset(rmt_encoder_t *encoder)
18+
{
19+
rmt_bs_encoder_t *bs_encoder = __containerof(encoder, rmt_bs_encoder_t, base);
20+
bs_encoder->last_byte_index = 0;
21+
bitscrambler_reset(bs_encoder->bs);
22+
bitscrambler_start(bs_encoder->bs);
23+
return ESP_OK;
24+
}
25+
26+
static esp_err_t rmt_del_bs_encoder(rmt_encoder_t *encoder)
27+
{
28+
rmt_bs_encoder_t *bs_encoder = __containerof(encoder, rmt_bs_encoder_t, base);
29+
bitscrambler_free(bs_encoder->bs);
30+
free(bs_encoder);
31+
return ESP_OK;
32+
}
33+
34+
static size_t rmt_encode_bs(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *input_raw,
35+
size_t data_size, rmt_encode_state_t *ret_state)
36+
{
37+
rmt_bs_encoder_t *bs_encoder = __containerof(encoder, rmt_bs_encoder_t, base);
38+
rmt_tx_channel_t *tx_chan = __containerof(channel, rmt_tx_channel_t, base);
39+
uint8_t *input_bytes = (uint8_t *)input_raw;
40+
rmt_encode_state_t state = RMT_ENCODING_RESET;
41+
rmt_dma_descriptor_t *desc0 = NULL;
42+
rmt_dma_descriptor_t *desc1 = NULL;
43+
44+
// bitscrambler encoder must be used with a TX channel with DMA enabled
45+
assert(tx_chan->base.dma_chan != NULL);
46+
47+
size_t byte_index = bs_encoder->last_byte_index;
48+
// how many bytes will be copied by the encoder
49+
size_t mem_want = (data_size - byte_index);
50+
// how many bytes we can save for this round
51+
size_t mem_have = tx_chan->mem_end * sizeof(rmt_symbol_word_t) - tx_chan->mem_off_bytes;
52+
// target memory buffer to copy to
53+
uint8_t *mem_to_nc = (uint8_t*)tx_chan->dma_mem_base_nc;
54+
// how many bytes will be copied in this round
55+
size_t copy_len = MIN(mem_want, mem_have);
56+
bool encoding_truncated = mem_have < mem_want;
57+
bool encoding_space_free = mem_have > mem_want;
58+
59+
// mark the start descriptor
60+
if (tx_chan->mem_off_bytes < tx_chan->ping_pong_symbols * sizeof(rmt_symbol_word_t)) {
61+
desc0 = &tx_chan->dma_nodes_nc[0];
62+
} else {
63+
desc0 = &tx_chan->dma_nodes_nc[1];
64+
}
65+
66+
size_t len = copy_len;
67+
while (len > 0) {
68+
mem_to_nc[tx_chan->mem_off_bytes++] = input_bytes[byte_index++];
69+
len--;
70+
}
71+
72+
// mark the end descriptor
73+
if (tx_chan->mem_off_bytes < tx_chan->ping_pong_symbols * sizeof(rmt_symbol_word_t)) {
74+
desc1 = &tx_chan->dma_nodes_nc[0];
75+
} else {
76+
desc1 = &tx_chan->dma_nodes_nc[1];
77+
}
78+
79+
// cross line, means desc0 has prepared with sufficient data buffer
80+
if (desc0 != desc1) {
81+
desc0->dw0.length = tx_chan->ping_pong_symbols * sizeof(rmt_symbol_word_t);
82+
desc0->dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA;
83+
}
84+
85+
if (encoding_truncated) {
86+
// this encoding has not finished yet, save the truncated position
87+
bs_encoder->last_byte_index = byte_index;
88+
} else {
89+
// reset internal index if encoding session has finished
90+
bs_encoder->last_byte_index = 0;
91+
// bitscrambler program will take care of the EOF marker by itself
92+
// so we don't rely on the TX driver to inject an EOF marker
93+
state |= RMT_ENCODING_COMPLETE | RMT_ENCODING_WITH_EOF;
94+
}
95+
96+
if (!encoding_space_free) {
97+
// no more free memory, the caller should yield
98+
state |= RMT_ENCODING_MEM_FULL;
99+
}
100+
101+
// reset offset pointer when exceeds maximum range
102+
if (tx_chan->mem_off_bytes >= tx_chan->ping_pong_symbols * 2 * sizeof(rmt_symbol_word_t)) {
103+
desc1->dw0.length = tx_chan->ping_pong_symbols * sizeof(rmt_symbol_word_t);
104+
desc1->dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA;
105+
tx_chan->mem_off_bytes = 0;
106+
}
107+
108+
*ret_state = state;
109+
return copy_len;
110+
}
111+
112+
esp_err_t rmt_new_bitscrambler_encoder(const rmt_bs_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder)
113+
{
114+
esp_err_t ret = ESP_OK;
115+
rmt_bs_encoder_t *encoder = NULL;
116+
ESP_GOTO_ON_FALSE(config && ret_encoder, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
117+
encoder = rmt_alloc_encoder_mem(sizeof(rmt_bs_encoder_t));
118+
ESP_GOTO_ON_FALSE(encoder, ESP_ERR_NO_MEM, err, TAG, "no mem for bitscrambler encoder");
119+
120+
bitscrambler_config_t bs_config = {
121+
.dir = BITSCRAMBLER_DIR_TX,
122+
.attach_to = SOC_BITSCRAMBLER_ATTACH_RMT,
123+
};
124+
ESP_GOTO_ON_ERROR(bitscrambler_new(&bs_config, &encoder->bs), err, TAG, "create bitscrambler failed");
125+
126+
// load the bitscrambler program
127+
ESP_GOTO_ON_ERROR(bitscrambler_load_program(encoder->bs, config->program_bin), err, TAG, "load bitscrambler program failed");
128+
129+
encoder->base.encode = rmt_encode_bs;
130+
encoder->base.del = rmt_del_bs_encoder;
131+
encoder->base.reset = rmt_bs_encoder_reset;
132+
// return general encoder handle
133+
*ret_encoder = &encoder->base;
134+
ESP_LOGD(TAG, "new bitscrambler encoder @%p", encoder);
135+
return ESP_OK;
136+
err:
137+
if (encoder) {
138+
if (encoder->bs) {
139+
bitscrambler_free(encoder->bs);
140+
}
141+
}
142+
return ret;
143+
}

0 commit comments

Comments
 (0)