diff --git a/components/esp_driver_bitscrambler/src/bitscrambler.c b/components/esp_driver_bitscrambler/src/bitscrambler.c index aae1f96d37..814f18540f 100644 --- a/components/esp_driver_bitscrambler/src/bitscrambler.c +++ b/components/esp_driver_bitscrambler/src/bitscrambler.c @@ -224,7 +224,10 @@ esp_err_t bitscrambler_load_program(bitscrambler_handle_t bs, const void *progra //Set options from header bitscrambler_ll_set_lut_width(bs->hw, bs->cfg.dir, hdr.lut_width); bitscrambler_ll_enable_prefetch_on_reset(bs->hw, bs->cfg.dir, hdr.prefetch); - bitscrambler_ll_set_eof_mode(bs->hw, bs->cfg.dir, hdr.eof_on); + // the bitscrambler assembler (bsasm.py) treats 'eof_on=1' as "upstream", a.k.a, read from upstream + // quote from bsasm.py: {'op': 'eof_on', 'default': 1, 'enum': {'upstream': 1, 'downstream': 0}}, + bitscrambler_eof_mode_t eof_mode = hdr.eof_on ? BITSCRAMBLER_EOF_MODE_READ : BITSCRAMBLER_EOF_MODE_WRITE; + bitscrambler_ll_set_eof_mode(bs->hw, bs->cfg.dir, eof_mode); bitscrambler_ll_set_tailing_bits(bs->hw, bs->cfg.dir, hdr.trailing_bits); //fixed options bitscrambler_ll_set_dummy_mode(bs->hw, bs->cfg.dir, BITSCRAMBLER_DUMMY_MODE_DUMMY); diff --git a/components/esp_driver_bitscrambler/src/bitscrambler_loopback.c b/components/esp_driver_bitscrambler/src/bitscrambler_loopback.c index 68edb662a4..ecfe621c72 100644 --- a/components/esp_driver_bitscrambler/src/bitscrambler_loopback.c +++ b/components/esp_driver_bitscrambler/src/bitscrambler_loopback.c @@ -132,6 +132,7 @@ esp_err_t bitscrambler_loopback_create(bitscrambler_handle_t *handle, int attach gdma_strategy_config_t gdma_strategy_conf = { .auto_update_desc = true, .owner_check = false, + .eof_till_data_popped = true, }; gdma_apply_strategy(bs->rx_channel, &gdma_strategy_conf); gdma_apply_strategy(bs->tx_channel, &gdma_strategy_conf); diff --git a/components/esp_driver_bitscrambler/test_apps/bitscrambler/main/CMakeLists.txt b/components/esp_driver_bitscrambler/test_apps/bitscrambler/main/CMakeLists.txt index a7822833c8..d86e1c9cee 100644 --- a/components/esp_driver_bitscrambler/test_apps/bitscrambler/main/CMakeLists.txt +++ b/components/esp_driver_bitscrambler/test_apps/bitscrambler/main/CMakeLists.txt @@ -16,3 +16,5 @@ idf_component_register(SRCS ${srcs} target_bitscrambler_add_src("timeout.bsasm") target_bitscrambler_add_src("trivial.bsasm") +target_bitscrambler_add_src("eof_upstream.bsasm") +target_bitscrambler_add_src("eof_downstream.bsasm") diff --git a/components/esp_driver_bitscrambler/test_apps/bitscrambler/main/eof_downstream.bsasm b/components/esp_driver_bitscrambler/test_apps/bitscrambler/main/eof_downstream.bsasm new file mode 100644 index 0000000000..4f7d1c0290 --- /dev/null +++ b/components/esp_driver_bitscrambler/test_apps/bitscrambler/main/eof_downstream.bsasm @@ -0,0 +1,13 @@ +# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Unlicense OR CC0-1.0 + +cfg prefetch false # disable data prefetch +cfg eof_on downstream # set EOF on downstream +cfg trailing_bytes 4 + +loop: + read 8 # because of prefetch is disabled, we need to read 8 bits manually + set 0..15 L, # don't care what the input is, just pull up/down the output data + set 16..31 H, + write 32, + jmp loop diff --git a/components/esp_driver_bitscrambler/test_apps/bitscrambler/main/eof_upstream.bsasm b/components/esp_driver_bitscrambler/test_apps/bitscrambler/main/eof_upstream.bsasm new file mode 100644 index 0000000000..d11000931d --- /dev/null +++ b/components/esp_driver_bitscrambler/test_apps/bitscrambler/main/eof_upstream.bsasm @@ -0,0 +1,13 @@ +# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Unlicense OR CC0-1.0 + +cfg prefetch false # disable data prefetch +cfg eof_on upstream # set EOF on upstream +cfg trailing_bytes 1 + +loop: + read 8 # because of prefetch is disabled, we need to read 8 bits manually + set 0..15 L, # don't care what the input is, just pull up/down the output data + set 16..31 H, + write 32, + jmp loop diff --git a/components/esp_driver_bitscrambler/test_apps/bitscrambler/main/test_bitscrambler.c b/components/esp_driver_bitscrambler/test_apps/bitscrambler/main/test_bitscrambler.c index 4a3c1047f1..26cf777b66 100644 --- a/components/esp_driver_bitscrambler/test_apps/bitscrambler/main/test_bitscrambler.c +++ b/components/esp_driver_bitscrambler/test_apps/bitscrambler/main/test_bitscrambler.c @@ -13,6 +13,8 @@ BITSCRAMBLER_PROGRAM(bitscrambler_program_trivial, "trivial"); BITSCRAMBLER_PROGRAM(bitscrambler_program_timeout, "timeout"); +BITSCRAMBLER_PROGRAM(bitscrambler_program_eof_upstream, "eof_upstream"); +BITSCRAMBLER_PROGRAM(bitscrambler_program_eof_downstream, "eof_downstream"); TEST_CASE("Basic BitScrambler I/O", "[bs]") { @@ -54,3 +56,61 @@ TEST_CASE("Timeout on stuck program", "[bs]") free(data_in); free(data_out); } + +TEST_CASE("BitScrambler with EOF counted on upstream", "[bs]") +{ + const size_t len = 32; + uint8_t *data_in = heap_caps_aligned_calloc(8, 1, len, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); + uint32_t *data_out = heap_caps_aligned_calloc(8, 1, len * 4, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); + TEST_ASSERT_NOT_NULL(data_in); + TEST_ASSERT_NOT_NULL(data_out); + + bitscrambler_handle_t bs; + TEST_ESP_OK(bitscrambler_loopback_create(&bs, SOC_BITSCRAMBLER_ATTACH_I2S0, len * 4)); + TEST_ESP_OK(bitscrambler_load_program(bs, bitscrambler_program_eof_upstream)); + size_t res_len = 0; + TEST_ESP_OK(bitscrambler_loopback_run(bs, data_in, len, data_out, len * 4, &res_len)); + bitscrambler_free(bs); + + printf("BitScrambler program complete. Input %zu, output %zu bytes:\n", len, res_len); + for (size_t i = 0; i < res_len / 4; i++) { + printf("%08lX ", data_out[i]); + if (i % 4 == 3) { + printf("\n"); + } + TEST_ASSERT_EQUAL(0xFFFF0000, data_out[i]); + } + TEST_ASSERT_EQUAL(len * 4, res_len); + + free(data_in); + free(data_out); +} + +TEST_CASE("BitScrambler with EOF counted on downstream", "[bs]") +{ + const size_t len = 32; + uint8_t *data_in = heap_caps_aligned_calloc(8, 1, len, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); + uint32_t *data_out = heap_caps_aligned_calloc(8, 1, len * 4, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); + TEST_ASSERT_NOT_NULL(data_in); + TEST_ASSERT_NOT_NULL(data_out); + + bitscrambler_handle_t bs; + TEST_ESP_OK(bitscrambler_loopback_create(&bs, SOC_BITSCRAMBLER_ATTACH_I2S0, len * 4)); + TEST_ESP_OK(bitscrambler_load_program(bs, bitscrambler_program_eof_downstream)); + size_t res_len = 0; + TEST_ESP_OK(bitscrambler_loopback_run(bs, data_in, len, data_out, len * 4, &res_len)); + bitscrambler_free(bs); + + printf("BitScrambler program complete. Input %zu, output %zu bytes:\n", len, res_len); + for (size_t i = 0; i < res_len / 4; i++) { + printf("%08lX ", data_out[i]); + if (i % 4 == 3) { + printf("\n"); + } + TEST_ASSERT_EQUAL(0xFFFF0000, data_out[i]); + } + TEST_ASSERT_EQUAL(len * 4, res_len); + + free(data_in); + free(data_out); +} diff --git a/components/esp_driver_bitscrambler/test_apps/bitscrambler/main/timeout.bsasm b/components/esp_driver_bitscrambler/test_apps/bitscrambler/main/timeout.bsasm index b3554194e6..d1a27404ce 100644 --- a/components/esp_driver_bitscrambler/test_apps/bitscrambler/main/timeout.bsasm +++ b/components/esp_driver_bitscrambler/test_apps/bitscrambler/main/timeout.bsasm @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Unlicense OR CC0-1.0 + cfg trailing_bytes 0 #End program as soon as the input EOFs. cfg prefetch true #We expect M0/M1 to be filled cfg lut_width_bits 8 #Not really applicable here diff --git a/components/esp_driver_bitscrambler/test_apps/bitscrambler/main/trivial.bsasm b/components/esp_driver_bitscrambler/test_apps/bitscrambler/main/trivial.bsasm index 075385c7be..06aec3d9a0 100644 --- a/components/esp_driver_bitscrambler/test_apps/bitscrambler/main/trivial.bsasm +++ b/components/esp_driver_bitscrambler/test_apps/bitscrambler/main/trivial.bsasm @@ -1,8 +1,11 @@ +# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Unlicense OR CC0-1.0 + # Example bitscrambler program. Does nothing but forward all bytes. -cfg trailing_bytes 12 # Let M0/M1 empty when EOF on input is found +cfg eof_on upstream +cfg trailing_bytes 8 # Let M0/M1 empty when EOF on input is found cfg prefetch true # We expect M0/M1 to be filled -cfg lut_width_bits 8 # Not really applicable here loop: set 0..31 0..31, diff --git a/components/hal/esp32c5/include/hal/bitscrambler_ll.h b/components/hal/esp32c5/include/hal/bitscrambler_ll.h index e0bb8b6cde..16114f601f 100644 --- a/components/hal/esp32c5/include/hal/bitscrambler_ll.h +++ b/components/hal/esp32c5/include/hal/bitscrambler_ll.h @@ -280,6 +280,21 @@ static inline bitscrambler_state_t bitscrambler_ll_get_current_state(bitscramble return BITSCRAMBLER_STATE_UNKNOWN; } +/** + * @brief Return if the bitscrambler FIFO is ready + * + * @note For TX, means the outfifo is not empty, then we can start the peripheral to transmit the data + * For RX, means the infifo is not full, then we can start the peripheral to receive the data + * + * @param hw BitScrambler hardware instance address. + * @param dir Direction, BITSCRAMBLER_DIR_TX or BITSCRAMBLER_DIR_RX + * @return true if FIFO is ready, false otherwise + */ +static inline bool bitscrambler_ll_is_fifo_ready(bitscrambler_dev_t *hw, bitscrambler_direction_t dir) +{ + return hw->state[dir].fifo_empty; +} + /** * @brief Enable the bus clock for BitScrambler module */ diff --git a/components/hal/esp32p4/include/hal/bitscrambler_ll.h b/components/hal/esp32p4/include/hal/bitscrambler_ll.h index 647cd75a3f..be8f7201b0 100644 --- a/components/hal/esp32p4/include/hal/bitscrambler_ll.h +++ b/components/hal/esp32p4/include/hal/bitscrambler_ll.h @@ -280,6 +280,21 @@ static inline bitscrambler_state_t bitscrambler_ll_get_current_state(bitscramble return BITSCRAMBLER_STATE_UNKNOWN; } +/** + * @brief Return if the bitscrambler FIFO is ready + * + * @note For TX, means the outfifo is not empty, then we can start the peripheral to transmit the data + * For RX, means the infifo is not full, then we can start the peripheral to receive the data + * + * @param hw BitScrambler hardware instance address. + * @param dir Direction, BITSCRAMBLER_DIR_TX or BITSCRAMBLER_DIR_RX + * @return true if FIFO is ready, false otherwise + */ +static inline bool bitscrambler_ll_is_fifo_ready(bitscrambler_dev_t *hw, bitscrambler_direction_t dir) +{ + return hw->state[dir].fifo_empty; +} + /** * @brief Enable the bus clock for BitScrambler module */ diff --git a/components/hal/include/hal/bitscrambler_types.h b/components/hal/include/hal/bitscrambler_types.h index 56516fa14d..daf9f348c2 100644 --- a/components/hal/include/hal/bitscrambler_types.h +++ b/components/hal/include/hal/bitscrambler_types.h @@ -62,6 +62,9 @@ typedef enum { /** * @brief Commands to set the state of bitscrambler + * + * @note Pause->Run, bitscrambler can continue from the last instruction; + * Halt->Run, bitscrambler will start from the first instruction; */ typedef enum { BITSCRAMBLER_SET_STATE_RUN, /*!< Run */ diff --git a/docs/en/api-reference/peripherals/bitscrambler.rst b/docs/en/api-reference/peripherals/bitscrambler.rst index f09d9f30a9..014e014a23 100644 --- a/docs/en/api-reference/peripherals/bitscrambler.rst +++ b/docs/en/api-reference/peripherals/bitscrambler.rst @@ -163,7 +163,7 @@ Meta-instructions set global BitScrambler configuration. Meta-instructions are a Global configuration meta-instructions """""""""""""""""""""""""""""""""""""" -- ``cfg prefetch true|false``: If prefetch is set to ``true``, the BitScrambler will read 64 bits from the input DMA stream into the input register at startup. If set to ``false``, the input register is initialized to zero. This setting defaults to ``true`` if not specified. +- ``cfg prefetch true|false``: If prefetch is set to ``true``, the BitScrambler will read 64 bits from the input DMA stream into the input register at startup. If set to ``false``, the input register is initialized to zero. This setting defaults to ``true`` if not specified. Please note, if the prefetch is enabled while the input stream can't provide at least 64 bits of data, the BitScrambler will hang. - ``cfg eof_on upstream|downstream``: After the input stream ends, the BitScrambler will still process a certain amount of 'trailing' dummy bytes so it can flush any data contained in its registers. This setting indicates from where the data will be counted: ``upstream`` makes the bitscrambler count the bytes being read, ``downstream`` makes it count the bytes being written. This defaults to ``upstream`` if not specified. - ``cfg trailing_bytes N``: This indicates how many dummy bytes will be read or written (depending on the ``eof_on`` setting) before the BitScrambler indicates an end-of-stream on its output. This defaults to ``0`` if not specified. - ``cfg lut_width_bits 8|16|32``: This selects the bus width of the LUT output RAM, in bits. The LUT can be 2048x8 bits, 1024x16 bits or 512x32 bits in size. This defaults to ``32`` if not specified. diff --git a/docs/zh_CN/api-reference/peripherals/bitscrambler.rst b/docs/zh_CN/api-reference/peripherals/bitscrambler.rst index 343c44a1b9..94e4f07caf 100644 --- a/docs/zh_CN/api-reference/peripherals/bitscrambler.rst +++ b/docs/zh_CN/api-reference/peripherals/bitscrambler.rst @@ -163,7 +163,7 @@ 全局配置元指令 """"""""""""""" -- ``cfg prefetch true|false``:如果 ``prefetch`` 设置为 ``true``,则比特调节器启动时,从输入 DMA 流中读取 64 位数据到输入寄存器中。如果设置为 ``false``,输入寄存器将被初始化为零。默认为 ``true``。 +- ``cfg prefetch true|false``:如果 ``prefetch`` 设置为 ``true``,则比特调节器启动时会从输入 DMA 流中读取 64 位数据到输入寄存器中。如果设置为 ``false``,输入寄存器将被初始化为零。默认为 ``true``。请注意,如果启用了 prefetch 但是输入流无法提供至少 64 位的数据,比特调节器会发生挂起。 - ``cfg eof_on upstream|downstream``:输入流结束后,比特调节器仍会计算一定量的“尾随”填充字节,以便清空其寄存器中可能存储的数据。此设置表示的是尾随字节的来源:如果设置为 ``upstream``,比特调节器从输入流中读取一定数量的填充字节,如果设置为 ``downstream``,比特调节器会等待写入足够的字节。默认为 ``upstream``。 - ``cfg trailing_bytes N``:该设置指示比特调节器在指示输出流结束之前,需要读取或写入(取决于 ``eof_on`` 设置)多少个填充字节。默认值为 ``0``。 - ``cfg lut_width_bits 8|16|32``:该设置选择 LUT 输出 RAM 的总线宽度(单位:位)。LUT 的大小可以是 2048×8 位、1024×16 位或 512×32 位。默认值为 ``32``。 diff --git a/examples/peripherals/bitscrambler/main/bitscrambler_example_main.c b/examples/peripherals/bitscrambler/main/bitscrambler_example_main.c index c3cd1e630c..a4cfea8b94 100644 --- a/examples/peripherals/bitscrambler/main/bitscrambler_example_main.c +++ b/examples/peripherals/bitscrambler/main/bitscrambler_example_main.c @@ -31,12 +31,13 @@ void app_main(void) { ESP_LOGI(TAG, "BitScrambler example main"); - size_t test_data_len = sizeof(testdata); - uint8_t* result_buf = heap_caps_calloc(test_data_len, 1, MALLOC_CAP_DMA); + size_t input_data_len = sizeof(testdata); + size_t output_data_len = input_data_len; // because the example BitScrambler program doesn't increase or decrease the data length + uint8_t* result_buf = heap_caps_calloc(output_data_len, 1, MALLOC_CAP_DMA); assert(result_buf); size_t result_buf_size = heap_caps_get_allocated_size(result_buf); - assert(result_buf_size >= test_data_len); + assert(result_buf_size >= output_data_len); bitscrambler_handle_t bs; ESP_ERROR_CHECK(bitscrambler_loopback_create(&bs, SOC_BITSCRAMBLER_ATTACH_I2S0, result_buf_size)); @@ -44,10 +45,10 @@ void app_main(void) size_t result_len; // we can even use a const array as the input data because the DMA can read data from the Flash region - ESP_ERROR_CHECK(bitscrambler_loopback_run(bs, (void*)testdata, test_data_len, result_buf, result_buf_size, &result_len)); + ESP_ERROR_CHECK(bitscrambler_loopback_run(bs, (void*)testdata, input_data_len, result_buf, result_buf_size, &result_len)); bitscrambler_free(bs); - printf("BitScrambler program complete. Input %zu, output %zu bytes:\n", test_data_len, result_len); + printf("BitScrambler program complete. Input %zu, output %zu bytes:\n", input_data_len, result_len); for (size_t i = 0; i < result_len; i++) { printf("%02X ", result_buf[i]); if ((i & 7) == 7) { diff --git a/examples/peripherals/bitscrambler/main/example.bsasm b/examples/peripherals/bitscrambler/main/example.bsasm index 1c718f4d20..003aeb0a7a 100644 --- a/examples/peripherals/bitscrambler/main/example.bsasm +++ b/examples/peripherals/bitscrambler/main/example.bsasm @@ -7,10 +7,10 @@ # byte 2 etc. Output byte 1 consists of bit 1 of input byte 0, bit 1 of # input byte 1, bit 1 of input byte 2, etc. +cfg eof_on downstream cfg trailing_bytes 8 #If we have an EOF on the input, we still #need to process the 64 bits in M0/M1 cfg prefetch true #We expect M0/M1 to be filled -cfg lut_width_bits 8 #Not really applicable here loop: # Note: we start with 64 bits in M0 and M1, so we can immediately start outputting.