=============
异步 CRC 计算
=============

:link_to_translation:`en:[English]`

本文档介绍了 ESP-IDF 中异步 CRC (Async CRC) 驱动程序的功能。目录如下：

.. contents::
    :local:
    :depth: 2

概述
====

异步 CRC 驱动程序提供使用通用 DMA 外设的硬件加速 CRC 计算。它支持 AHB-GDMA 和 AXI-GDMA 后端，提供灵活的 CRC 计算，支持可配置的多项式、初始值、位反转选项和最终 XOR 处理。

主要功能包括：

- 使用通用 DMA 进行硬件加速的 CRC 计算
- 支持 8 位、16 位和 32 位 CRC 算法
- 带回调通知的异步 API
- 支持超时的阻塞 API
- 支持可配置积压大小的请求队列
- 支持 AHB 和 AXI DMA 后端

应用场景包括：

- 通信协议的数据完整性验证
- 文件和固件校验和计算
- 网络数据包验证
- 存储数据验证

快速开始
========

本节简要介绍如何使用异步 CRC 驱动程序。通过实际示例，演示如何初始化驱动程序、配置 CRC 参数以及执行异步和阻塞 CRC 计算。

异步 CRC 的典型使用流程如下：

.. blockdiag::
    :scale: 100%
    :caption: 异步 CRC 驱动程序的一般使用流程（点击放大）
    :align: center

    blockdiag {
        default_fontsize = 14;
        node_width = 250;
        node_height = 80;
        class emphasis [color = pink, style = dashed];

        install [label="esp_async_crc_install_gdma_*"];
        calc [label="esp_async_crc_calc"];
        wait [label="等待回调"];
        process [label="处理结果"];
        uninstall [label="esp_async_crc_uninstall"];

        install -> calc -> wait -> process;
        process -> calc [folded];
        calc -> uninstall [folded];
    }

创建和安装驱动程序
------------------

首先，您需要安装异步 CRC 驱动程序。该驱动程序根据芯片的功能支持 AHB-GDMA 和 AXI-GDMA 后端：

.. code:: c

    async_crc_handle_t crc_hdl = NULL;
    async_crc_config_t config = {
        .backlog = 8,          // 队列中最大挂起请求数
        .dma_burst_size = 16,  // DMA 突发传输大小（字节）
    };

    // 使用 AHB-GDMA 后端安装（如果可用）
    ESP_ERROR_CHECK(esp_async_crc_install_gdma_ahb(&config, &crc_hdl));

    // 或使用 AXI-GDMA 后端安装（如果可用）
    // ESP_ERROR_CHECK(esp_async_crc_install_gdma_axi(&config, &crc_hdl));

.. note::

    **选择 AHB-GDMA 和 AXI-GDMA 后端**

    后端选择取决于芯片的功能和性能要求：

    - **AHB-GDMA**：大多数 ESP 芯片均可使用。连接到 AHB 总线，适用于通用 DMA 操作。最适合：

      - 标准性能要求
      - 大多数 ESP 芯片变体的兼容性

    - **AXI-GDMA**：在支持 AXI 总线的高端 ESP 芯片上可用。为内存密集型操作提供更高的带宽和更好的性能。最适合：

      - 高吞吐量 CRC 计算
      - 处理大量数据
      - 需要最大性能的应用
      - 更高效地访问外部存储器（PSRAM）

创建驱动程序实例时，您需要配置：

- **backlog**：可排队等待的最大 CRC 请求数。较高的值使用更多内存，但在突发工作负载下提供更好的吞吐量。
- **intr_priority**：DMA 中断优先级。设置为 ``0`` 时使用默认的低/中优先级；设置为非零值时请求指定的中断优先级。
- **dma_burst_size**：DMA 传输突发大小（字节）。

驱动程序句柄 ``crc_hdl`` 是一个不透明指针，用于所有后续操作。

执行异步 CRC 计算
-----------------

异步 API 允许您在不阻塞的情况下排队 CRC 计算：

.. code:: c

    static bool crc_complete_callback(async_crc_handle_t crc_hdl, async_crc_event_data_t *edata, void *cb_args)
    {
        uint32_t result = edata->crc_result;
        // 进一步处理 CRC 结果
        // 例如，发送到任务队列，记录日志等。
        return false;
    }

    // 配置 CRC-32 的 CRC 参数
    async_crc_params_t params = {
        .width = 32,
        .polynomial = 0x04C11DB7,
        .init_value = 0xFFFFFFFF,
        .final_xor_value = 0xFFFFFFFF,
        .reverse_input = true,
        .reverse_output = true,
    };

    // 启动异步 CRC 计算
    const char *data = "Hello, World!";
    size_t data_len = strlen(data);

    ESP_ERROR_CHECK(esp_async_crc_calc(crc_hdl, data, data_len, &params, crc_complete_callback, NULL));

CRC 计算完成时会在中断上下文中调用回调函数。回调接收：

- **crc_hdl**：驱动程序句柄
- **edata**：包含 CRC 结果的事件数据
- **cb_args**：在 ``esp_async_crc_calc`` 期间传递的用户定义参数

执行阻塞 CRC 计算
-----------------

对于更简单的使用场景或不需要异步操作的情况，请使用阻塞 API：

.. code:: c

    uint32_t crc_result = 0;
    async_crc_params_t params = {
        .width = 32,
        .polynomial = 0x04C11DB7,
        .init_value = 0xFFFFFFFF,
        .final_xor_value = 0xFFFFFFFF,
        .reverse_input = true,
        .reverse_output = true,
    };

    const char *data = "Hello, World!";
    size_t data_len = strlen(data);

    // 阻塞 CRC，无限期等待
    ESP_ERROR_CHECK(esp_crc_calc_blocking(crc_hdl, data, data_len, &params, -1, &crc_result));

    printf("CRC 结果: 0x%08X\n", crc_result);

阻塞 API 仅支持 ``timeout_ms = -1``，表示无限期等待直到 CRC 计算完成。

卸载驱动程序
------------

当不再需要驱动程序时：

.. code:: c

    ESP_ERROR_CHECK(esp_async_crc_uninstall(crc_hdl));

如果存在挂起的操作或 CRC 引擎正忙，卸载函数将返回 :c:macro:`ESP_ERR_INVALID_STATE`。请在卸载前确保所有操作已完成。

CRC 参数配置
============

异步 CRC 驱动程序通过 :cpp:type:`async_crc_params_t` 结构支持灵活的 CRC 算法配置。

CRC 宽度
---------

:cpp:member:`async_crc_params_t::width` 字段指定 CRC 位宽度：

- **8**：8 位 CRC（如 CRC-8、CRC-8/MAXIM）
- **16**：16 位 CRC（如 CRC-16/CCITT、CRC-16/IBM）
- **32**：32 位 CRC（如 CRC-32、CRC-32/BZIP2）

多项式
--------

:cpp:member:`async_crc_params_t::polynomial` 字段以十六进制格式指定 CRC 多项式。常见的多项式值包括：

- CRC-32: ``0x04C11DB7``
- CRC-16/CCITT: ``0x1021``
- CRC-16/IBM: ``0x8005``
- CRC-8/MAXIM: ``0x31``

初始值
---------

:cpp:member:`async_crc_params_t::init_value` 字段设置处理前的初始 CRC 值。常见的初始值：

- ``0xFFFFFFFF`` 用于 CRC-32
- ``0x0000`` 用于许多 CRC-16 变体
- ``0x00`` 用于许多 CRC-8 变体

最终 XOR 值
-----------

:cpp:member:`async_crc_params_t::final_xor_value` 字段指定在与最终 CRC 结果进行异或之前的值。这通常是 CRC-32 的 ``0xFFFFFFFF``，但对于某些变体可以是 ``0x0000``。

位反转选项
-----------

- :cpp:member:`async_crc_params_t::reverse_input` 如果为 true，在处理前反转每个输入字节的位顺序
- :cpp:member:`async_crc_params_t::reverse_output` 如果为 true，在应用最终 XOR 之前反转最终 CRC 结果的位顺序

这些选项影响不同 CRC 算法的反射设置。

常见 CRC 配置
-------------

下表列出了常见的 CRC 配置：

+----------------+----------+---------------+---------------+------------------+---------------+---------------+
| CRC 算法       | 位宽     | 多项式        | 初始值        | 最终 XOR 值      | 反转输入      | 反转输出      |
+================+==========+===============+===============+==================+===============+===============+
| CRC-32         | 32       | 0x04C11DB7    | 0xFFFFFFFF    | 0xFFFFFFFF       | true          | true          |
+----------------+----------+---------------+---------------+------------------+---------------+---------------+
| CRC-16/CCITT   | 16       | 0x1021        | 0x0000        | 0x0000           | false         | false         |
+----------------+----------+---------------+---------------+------------------+---------------+---------------+
| CRC-16/IBM     | 16       | 0x8005        | 0x0000        | 0x0000           | true          | true          |
+----------------+----------+---------------+---------------+------------------+---------------+---------------+
| CRC-8/MAXIM    | 8        | 0x31          | 0x00          | 0x00             | true          | true          |
+----------------+----------+---------------+---------------+------------------+---------------+---------------+

线程安全
========

异步 CRC 驱动程序设计为线程安全的，可以从多个任务中使用。该驱动程序采用 **无竞争有限状态机（FSM）** 架构，确保线程安全并正确处理并发 CRC 请求。

线程安全保证
------------

- 所有公共 API 可以同时从不同任务调用
- 驱动程序对内部状态使用原子操作和临界区保护
- 请求队列确保并发调用被正确串行化

ISR 上下文限制
--------------

异步 API 和阻塞 API 都不能从中断上下文调用。具体来说：

- :cpp:func:`esp_async_crc_calc`：涉及内存分配/释放、DMA 准备工作和非 ISR 安全的日志函数
- :cpp:func:`esp_crc_calc_blocking`：使用可能阻塞的同步原语

回调限制
--------

回调函数（:cpp:type:`async_crc_isr_cb_t`）在中断上下文中执行。因此：

- 不要执行阻塞操作（如 ``vTaskDelay``、带超时的 ``xQueueSend``）
- 保持执行时间最小化，以免影响系统中断延迟
- 不要使用 ``malloc`` 或类似函数分配内存
- 只使用 ISR 安全的 FreeRTOS API（如 ``xQueueSendFromISR``, ``xSemaphoreGiveFromISR``）
- 如果回调唤醒了高优先级任务，返回 ``true``

使用队列的回调示例：

.. code:: c

    static bool crc_callback(async_crc_handle_t crc_hdl, async_crc_event_data_t *edata, void *cb_args)
    {
        QueueHandle_t queue = (QueueHandle_t)cb_args;
        BaseType_t high_task_awoken = pdFALSE;
        // 通过 ISR 安全队列将结果发送到任务
        xQueueSendFromISR(queue, &edata->crc_result, &high_task_awoken);
        return high_task_awoken == pdTRUE;
    }

缓冲区要求
==========

异步 CRC 驱动程序对数据缓冲区有特定要求。

内存类型
--------

数据缓冲区可以来自内部存储区域（DRAM/IRAM）或外部存储区域（PSRAM, Flash）中。驱动程序自动处理两者：

.. code:: c

    // 内部 RAM
    static char internal_data[] = "Data in internal RAM";
    esp_async_crc_calc(crc_hdl, internal_data, strlen(internal_data), &params, callback, NULL);

    // 外部 Flash
    static const char *flash_data = "Data in external Flash";
    esp_async_crc_calc(crc_hdl, flash_data, strlen(flash_data), &params, callback, NULL);

性能注意事项
============

积压配置
--------

``backlog`` 配置影响性能：

- 小积压（4-8）：内存使用量较低，高负载下可能会产生背压
- 大积压（16+）：突发工作负载的吞吐量更好，内存使用量更高

根据应用的内存约束和工作负载模式进行选择。

DMA 突发大小
------------

``dma_burst_size`` 影响 DMA 传输效率：

- 较大的突发大小可以提高吞吐量
- 典型值：16、32、64 字节

最佳值取决于芯片的 DMA 控制器功能。

应用示例
========

- :example:`peripherals/dma/async_crc` 演示了如何通过交互式控制台 CLI 使用异步 CRC 驱动程序。

API 参考
========

异步 CRC 驱动程序函数
---------------------

.. include-build-file:: inc/esp_async_crc.inc
