Skip to content

Commit

Permalink
Merge branch 'feature/hw_crc_esp32p4' into 'master'
Browse files Browse the repository at this point in the history
feat(gdma): support hardware crc calculation on esp32p4

Closes IDF-7497

See merge request espressif/esp-idf!25307
  • Loading branch information
suda-morris committed Aug 31, 2023
2 parents 23ebfd1 + b962fde commit cf61f63
Show file tree
Hide file tree
Showing 24 changed files with 1,005 additions and 110 deletions.
69 changes: 66 additions & 3 deletions components/esp_hw_support/dma/gdma.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@

static const char *TAG = "gdma";

#if !SOC_RCC_IS_INDEPENDENT
// Reset and Clock Control registers are mixing with other peripherals, so we need to use a critical section
#define GDMA_RCC_ATOMIC() PERIPH_RCC_ATOMIC()
#else
#define GDMA_RCC_ATOMIC()
#endif

#define GDMA_INVALID_PERIPH_TRIG (0x3F)
#define SEARCH_REQUEST_RX_CHANNEL (1 << 0)
#define SEARCH_REQUEST_TX_CHANNEL (1 << 1)
Expand Down Expand Up @@ -257,7 +264,7 @@ esp_err_t gdma_connect(gdma_channel_handle_t dma_chan, gdma_trigger_t trig_perip
gdma_group_t *group = pair->group;
gdma_hal_context_t *hal = &group->hal;
bool periph_conflict = false;
//

if (trig_periph.bus_id != SOC_GDMA_BUS_ANY) {
ESP_RETURN_ON_FALSE(trig_periph.bus_id == group->bus_id, ESP_ERR_INVALID_ARG, TAG,
"peripheral and DMA system bus mismatch");
Expand Down Expand Up @@ -404,6 +411,57 @@ esp_err_t gdma_set_priority(gdma_channel_handle_t dma_chan, uint32_t priority)
return ESP_OK;
}

#if SOC_GDMA_SUPPORT_CRC
esp_err_t gdma_config_crc_calculator(gdma_channel_handle_t dma_chan, const gdma_crc_calculator_config_t *config)
{
ESP_RETURN_ON_FALSE(dma_chan && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
gdma_pair_t *pair = dma_chan->pair;
gdma_group_t *group = pair->group;
gdma_hal_context_t *hal = &group->hal;
switch (group->bus_id) {
#if SOC_AHB_GDMA_SUPPORTED
case SOC_GDMA_BUS_AHB:
ESP_RETURN_ON_FALSE(config->crc_bit_width <= GDMA_LL_AHB_MAX_CRC_BIT_WIDTH, ESP_ERR_INVALID_ARG, TAG, "invalid crc bit width");
break;
#endif // SOC_AHB_GDMA_SUPPORTED
#if SOC_AXI_GDMA_SUPPORTED
case SOC_GDMA_BUS_AXI:
ESP_RETURN_ON_FALSE(config->crc_bit_width <= GDMA_LL_AXI_MAX_CRC_BIT_WIDTH, ESP_ERR_INVALID_ARG, TAG, "invalid crc bit width");
break;
#endif // SOC_AXI_GDMA_SUPPORTED
default:
ESP_LOGE(TAG, "invalid bus id: %d", group->bus_id);
return ESP_ERR_INVALID_ARG;
}

// clear the previous CRC result
gdma_hal_clear_crc(hal, pair->pair_id, dma_chan->direction);

// set polynomial and initial value
gdma_hal_crc_config_t hal_config = {
.crc_bit_width = config->crc_bit_width,
.poly_hex = config->poly_hex,
.init_value = config->init_value,
.reverse_data_mask = config->reverse_data_mask,
};
gdma_hal_set_crc_poly(hal, pair->pair_id, dma_chan->direction, &hal_config);

return ESP_OK;
}

esp_err_t gdma_crc_get_result(gdma_channel_handle_t dma_chan, uint32_t *result)
{
ESP_RETURN_ON_FALSE(dma_chan && result, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
gdma_pair_t *pair = dma_chan->pair;
gdma_group_t *group = pair->group;
gdma_hal_context_t *hal = &group->hal;

*result = gdma_hal_get_crc_result(hal, pair->pair_id, dma_chan->direction);

return ESP_OK;
}
#endif // SOC_GDMA_SUPPORT_CRC

esp_err_t gdma_register_tx_event_callbacks(gdma_channel_handle_t dma_chan, gdma_tx_event_callbacks_t *cbs, void *user_data)
{
ESP_RETURN_ON_FALSE(dma_chan && cbs && dma_chan->direction == GDMA_CHANNEL_DIRECTION_TX, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
Expand Down Expand Up @@ -564,7 +622,9 @@ static void gdma_release_group_handle(gdma_group_t *group)

if (do_deinitialize) {
gdma_hal_deinit(&group->hal);
periph_module_disable(gdma_periph_signals.groups[group_id].module);
GDMA_RCC_ATOMIC() {
gdma_ll_enable_bus_clock(group_id, false);
}
free(group);
ESP_LOGD(TAG, "del group %d", group_id);
}
Expand Down Expand Up @@ -595,7 +655,10 @@ static gdma_group_t *gdma_acquire_group_handle(int group_id, void (*hal_init)(gd
group->group_id = group_id;
group->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
// enable APB to access GDMA registers
periph_module_enable(gdma_periph_signals.groups[group_id].module);
GDMA_RCC_ATOMIC() {
gdma_ll_enable_bus_clock(group_id, true);
gdma_ll_reset_register(group_id);
}
gdma_hal_config_t config = {
.group_id = group_id,
};
Expand Down
43 changes: 43 additions & 0 deletions components/esp_hw_support/include/esp_private/gdma.h
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,7 @@ esp_err_t gdma_append(gdma_channel_handle_t dma_chan);
*/
esp_err_t gdma_reset(gdma_channel_handle_t dma_chan);

#if SOC_GDMA_SUPPORT_ETM
/**
* @brief GDMA ETM event configuration
*/
Expand Down Expand Up @@ -400,6 +401,7 @@ typedef struct {
* - ESP_FAIL: Get ETM task failed because of other error
*/
esp_err_t gdma_new_etm_task(gdma_channel_handle_t dma_chan, const gdma_etm_task_config_t *config, esp_etm_task_handle_t *out_task);
#endif // SOC_GDMA_SUPPORT_ETM

/**
* @brief Get the mask of free M2M trigger IDs
Expand All @@ -417,6 +419,47 @@ esp_err_t gdma_new_etm_task(gdma_channel_handle_t dma_chan, const gdma_etm_task_
*/
esp_err_t gdma_get_free_m2m_trig_id_mask(gdma_channel_handle_t dma_chan, uint32_t *mask);

#if SOC_GDMA_SUPPORT_CRC
/**
* @brief CRC Calculator configuration
*/
typedef struct {
uint32_t init_value; /*!< CRC initial value */
uint32_t crc_bit_width; /*!< CRC bit width */
uint32_t poly_hex; /*!< Polynomial Formula, in hex */
bool reverse_data_mask; /*!< Reverse data mask, used when you want to reverse the input data (a.k.a, refin) */
} gdma_crc_calculator_config_t;

/**
* @brief Configure CRC Calculator
*
* @note This function must be called before `gdma_start`.
* @note The CRC Calculator will reset itself automatically if the DMA stops and starts again.
*
* @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_channel`
* @param[in] config CRC Calculator configuration
* @return
* - ESP_OK: Configure CRC Calculator successfully
* - ESP_ERR_INVALID_ARG: Configure CRC Calculator failed because of invalid argument
* - ESP_FAIL: Configure CRC Calculator failed because of other error
*/
esp_err_t gdma_config_crc_calculator(gdma_channel_handle_t dma_chan, const gdma_crc_calculator_config_t *config);

/**
* @brief Get CRC Calculator result
*
* @note You need to call this function before a new DMA transaction starts, otherwise the CRC results may be overridden.
*
* @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_channel`
* @param[out] result Returned CRC result
* @return
* - ESP_OK: Get CRC result successfully
* - ESP_ERR_INVALID_ARG: Get CRC result failed because of invalid argument
* - ESP_FAIL: Get CRC result failed because of other error
*/
esp_err_t gdma_crc_get_result(gdma_channel_handle_t dma_chan, uint32_t *result);
#endif // SOC_GDMA_SUPPORT_CRC

#ifdef __cplusplus
}
#endif
115 changes: 115 additions & 0 deletions components/esp_hw_support/test_apps/dma/main/test_gdma.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
Expand Down Expand Up @@ -299,3 +300,117 @@ TEST_CASE("GDMA M2M Mode", "[GDMA]")
TEST_ESP_OK(gdma_del_channel(rx_chan));
#endif // SOC_AXI_GDMA_SUPPORTED
}

#if SOC_GDMA_SUPPORT_CRC
typedef struct {
uint32_t init_value;
uint32_t crc_bit_width;
uint32_t poly_hex;
bool reverse_data_mask;
uint32_t expected_result;
} test_crc_case_t;
static test_crc_case_t crc_test_cases[] = {
// CRC8, x^8+x^2+x+1
[0] = {
.crc_bit_width = 8,
.init_value = 0x00,
.poly_hex = 0x07,
.expected_result = 0xC6,
},
[1] = {
.crc_bit_width = 8,
.init_value = 0x00,
.poly_hex = 0x07,
.reverse_data_mask = true, // refin = true
.expected_result = 0xDE,
},
// CRC16, x^16+x^12+x^5+1
[2] = {
.crc_bit_width = 16,
.init_value = 0xFFFF,
.poly_hex = 0x1021,
.expected_result = 0x5289,
},
// CRC32, x32+x26+x23+x22+x16+x12+x11+x10+x8+x7+x5+x4+x2+x+1
[3] = {
.crc_bit_width = 32,
.init_value = 0xFFFFFFFF,
.poly_hex = 0x04C11DB7,
.expected_result = 0x63B3E283,
}
};

// CRC online: https://www.lddgo.net/en/encrypt/crc
static void test_gdma_crc_calculation(gdma_channel_handle_t tx_chan, int test_num_crc_algorithm)
{
uint32_t crc_result = 0;
const char *test_input_string = "Share::Connect::Innovate";
size_t input_data_size = strlen(test_input_string);
printf("Calculate CRC value for string: \"%s\"\r\n", test_input_string);

gdma_trigger_t m2m_trigger = GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_M2M, 0);
// get a free DMA trigger ID
uint32_t free_m2m_id_mask = 0;
gdma_get_free_m2m_trig_id_mask(tx_chan, &free_m2m_id_mask);
m2m_trigger.instance_id = __builtin_ctz(free_m2m_id_mask);
TEST_ESP_OK(gdma_connect(tx_chan, m2m_trigger));

uint8_t *src_buf = heap_caps_aligned_calloc(64, 1, 256, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
TEST_ASSERT_NOT_NULL(src_buf);
dma_descriptor_align8_t *tx_descs = (dma_descriptor_align8_t *) src_buf;
uint8_t *src_data = src_buf + 64;
memcpy(src_data, test_input_string, input_data_size);

tx_descs->buffer = src_data;
tx_descs->dw0.size = 256 - 64;
tx_descs->dw0.length = input_data_size;
tx_descs->dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA;
tx_descs->dw0.suc_eof = 1;
tx_descs->next = NULL;

#if CONFIG_IDF_TARGET_ESP32P4
// do write-back for the buffer because it's in the cache
Cache_WriteBack_Addr(CACHE_MAP_L1_DCACHE, (uint32_t)src_buf, 256);
#endif

for (int i = 0; i < test_num_crc_algorithm; i++) {
gdma_crc_calculator_config_t crc_config = {
.crc_bit_width = crc_test_cases[i].crc_bit_width,
.init_value = crc_test_cases[i].init_value,
.poly_hex = crc_test_cases[i].poly_hex,
.reverse_data_mask = crc_test_cases[i].reverse_data_mask,
};
TEST_ESP_OK(gdma_config_crc_calculator(tx_chan, &crc_config));
TEST_ESP_OK(gdma_reset(tx_chan));
TEST_ESP_OK(gdma_start(tx_chan, (intptr_t)tx_descs));
// simply wait for the transfer done
vTaskDelay(pdMS_TO_TICKS(100));
TEST_ESP_OK(gdma_crc_get_result(tx_chan, &crc_result));
printf("CRC Result: 0x%"PRIx32"\r\n", crc_result);
TEST_ASSERT_EQUAL(crc_test_cases[i].expected_result, crc_result);
}

free(src_buf);
}

TEST_CASE("GDMA CRC Calculation", "[GDMA]")
{
gdma_channel_handle_t tx_chan = NULL;
gdma_channel_alloc_config_t tx_chan_alloc_config = {
.direction = GDMA_CHANNEL_DIRECTION_TX,
};
#if SOC_AHB_GDMA_SUPPORTED
printf("Test CRC calculation for AHB GDMA\r\n");
TEST_ESP_OK(gdma_new_ahb_channel(&tx_chan_alloc_config, &tx_chan));
test_gdma_crc_calculation(tx_chan, 4);
TEST_ESP_OK(gdma_del_channel(tx_chan));
#endif // SOC_AHB_GDMA_SUPPORTED

#if SOC_AXI_GDMA_SUPPORTED
printf("Test CRC calculation for AXI GDMA\r\n");
TEST_ESP_OK(gdma_new_axi_channel(&tx_chan_alloc_config, &tx_chan));
test_gdma_crc_calculation(tx_chan, 3);
TEST_ESP_OK(gdma_del_channel(tx_chan));
#endif // SOC_AXI_GDMA_SUPPORTED
}
#endif // SOC_GDMA_SUPPORT_CRC
4 changes: 4 additions & 0 deletions components/hal/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ if(NOT BOOTLOADER_BUILD)
list(APPEND srcs "gdma_hal_top.c")
endif()

if(CONFIG_SOC_GDMA_SUPPORT_CRC)
list(APPEND srcs "gdma_hal_crc_gen.c")
endif()

if(CONFIG_SOC_AHB_GDMA_VERSION EQUAL 1)
list(APPEND srcs "gdma_hal_ahb_v1.c")
endif()
Expand Down
29 changes: 29 additions & 0 deletions components/hal/esp32c2/include/hal/gdma_ll.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "hal/gdma_types.h"
#include "soc/gdma_struct.h"
#include "soc/gdma_reg.h"
#include "soc/system_struct.h"

#ifdef __cplusplus
extern "C" {
Expand Down Expand Up @@ -47,6 +48,34 @@ extern "C" {
#define GDMA_LL_AHB_TX_RX_SHARE_INTERRUPT 1 // TX and RX channel in the same pair will share the same interrupt source number

///////////////////////////////////// Common /////////////////////////////////////////

/**
* @brief Enable the bus clock for the DMA module
*/
static inline void gdma_ll_enable_bus_clock(int group_id, bool enable)
{
(void)group_id;
SYSTEM.perip_clk_en1.dma_clk_en = enable;
}

/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define gdma_ll_enable_bus_clock(...) (void)__DECLARE_RCC_ATOMIC_ENV; gdma_ll_enable_bus_clock(__VA_ARGS__)

/**
* @brief Reset the DMA module
*/
static inline void gdma_ll_reset_register(int group_id)
{
(void)group_id;
SYSTEM.perip_rst_en1.dma_rst = 1;
SYSTEM.perip_rst_en1.dma_rst = 0;
}

/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define gdma_ll_reset_register(...) (void)__DECLARE_RCC_ATOMIC_ENV; gdma_ll_reset_register(__VA_ARGS__)

/**
* @brief Force enable register clock
*/
Expand Down
29 changes: 29 additions & 0 deletions components/hal/esp32c3/include/hal/gdma_ll.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "hal/gdma_types.h"
#include "soc/gdma_struct.h"
#include "soc/gdma_reg.h"
#include "soc/system_struct.h"

#ifdef __cplusplus
extern "C" {
Expand Down Expand Up @@ -47,6 +48,34 @@ extern "C" {
#define GDMA_LL_AHB_TX_RX_SHARE_INTERRUPT 1 // TX and RX channel in the same pair will share the same interrupt source number

///////////////////////////////////// Common /////////////////////////////////////////

/**
* @brief Enable the bus clock for the DMA module
*/
static inline void gdma_ll_enable_bus_clock(int group_id, bool enable)
{
(void)group_id;
SYSTEM.perip_clk_en1.reg_dma_clk_en = enable;
}

/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define gdma_ll_enable_bus_clock(...) (void)__DECLARE_RCC_ATOMIC_ENV; gdma_ll_enable_bus_clock(__VA_ARGS__)

/**
* @brief Reset the DMA module
*/
static inline void gdma_ll_reset_register(int group_id)
{
(void)group_id;
SYSTEM.perip_rst_en1.reg_dma_rst = 1;
SYSTEM.perip_rst_en1.reg_dma_rst = 0;
}

/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define gdma_ll_reset_register(...) (void)__DECLARE_RCC_ATOMIC_ENV; gdma_ll_reset_register(__VA_ARGS__)

/**
* @brief Force enable register clock
*/
Expand Down
Loading

0 comments on commit cf61f63

Please sign in to comment.