mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 03:00:34 +03:00
feat(gdma): support hardware crc calculation
On ESP32-P4, the GDMA peripherals support CRC calculating.
This commit is contained in:
@@ -257,7 +257,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");
|
||||
@@ -404,6 +404,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");
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user