API GUIDE / DMA / HAL

G32S10 DMA HAL 接口

HAL 层接口:hal_dma_init() 是 controller 级、幂等的初始化:打开 PDMA0/PDMA1 时钟并建立 HAL 软件状态。

1. 资源模型与公开类型

1.1 HAL 全局生命周期

hal_dma_init() 是 controller 级、幂等的初始化:打开 PDMA0/PDMA1 时钟并建立 HAL 软件状态。hal_dma_open() 会自动调用它。

DMA controller 是全局共享资源,因此 HAL 没有 hal_dma_deinit():不能由一个模块在不 知道其他 channel 状态的情况下关闭 controller 时钟或撤销全局状态。hal_dma_stop() 归还 活动物理 channel,hal_dma_close() 释放一个 idle handle;controller 初始化会一直保留到 系统复位。上层模块必须自行确保多个使用者不会同时持有同一 PDMA controller 的 IRQ 或硬件资源。

1.2 HAL 运行状态

typedef enum {
    HAL_DMA_STATE_UNUSED = 0,
    HAL_DMA_STATE_READY,
    HAL_DMA_STATE_BUSY,
} hal_dma_state_t;

说明:

  • HAL_DMA_STATE_UNUSED:句柄未打开或已被清理。

  • HAL_DMA_STATE_READY:句柄已打开,可以启动传输。

  • HAL_DMA_STATE_BUSY:句柄正在执行 DMA 传输。

1.3 DMA 总线宽度

typedef enum {
    HAL_DMA_BUS_WIDTH_32BIT = 0,
    HAL_DMA_BUS_WIDTH_8BIT = 1,
    HAL_DMA_BUS_WIDTH_16BIT = 2,
} hal_dma_bus_width_t;

说明:

  • bus_width 同时用于源和目的端口宽度字段。

  • 外设 DMA 场景下应与外设 FIFO 或数据寄存器访问宽度匹配。

1.4 DMA 回调类型

typedef enum {
    HAL_DMA_CALLBACK_DESCRIPTOR_COMPLETE = 0,
    HAL_DMA_CALLBACK_TRANSFER_COMPLETE,
    HAL_DMA_CALLBACK_ERROR,
    HAL_DMA_CALLBACK_COUNT,
} hal_dma_callback_id_t;

typedef void (*hal_dma_callback_t)(hal_dma_handle_t *handle, void *user_ctx);
参数说明
handle完成传输的 DMA 句柄。
user_ctxhal_dma_user_data_set() 设置的上下文;未设置时为 NULL。

说明:

  • 回调按 descriptor complete、transfer complete、error 三个事件槽注册,使用 hal_dma_callback_register();回调本身不携带 event/status 参数。

  • hal_dma_user_data_set() 设置三个事件槽共享的上下文;地址错误和 halt 状态由 HAL 记录日志。

  • 回调运行在 PDMA ISR context,不能阻塞。

事件快照使用以下公开类型,通过 hal_dma_get_last_event() 读取:

typedef enum {
    HAL_DMA_EVENT_NONE = 0,
    HAL_DMA_EVENT_DESCRIPTOR_COMPLETE,
    HAL_DMA_EVENT_TRANSFER_COMPLETE,
    HAL_DMA_EVENT_ERROR,
} hal_dma_event_t;

typedef struct {
    hal_dma_event_t type;
    uint32_t descriptor_index;
    uint32_t descriptor_advance;
    bool descriptor_index_valid;
    bool descriptor_events_coalesced;
    bool normal_irq_pending;
    bool descriptor_irq_pending;
} hal_dma_event_info_t;

HAL_DMA_EVENT_NONE 表示当前没有已记录的事件。descriptor_advance 表示根据前后观测到的 descriptor index 计算出的最小推进数量,不保证等于实际完成数量;无法确认有效 index 时为 0,首次有效观测时为 1。其他字段及单 descriptor cyclic 的特殊规则见 4.5.1。

1.5 DMA 打开配置

typedef struct {
    hal_dma_request_t request;
    hal_dma_bus_width_t bus_width;
    uint32_t unit_size;
    void *descriptor;
    uint32_t descriptor_capacity;
} hal_dma_config_t;
字段说明
requestDMA 请求源,例如内存 auto-request、UART TX/RX、I2C TX/RX、CAN RX、AIC 等。
bus_width源和目的共享的 DMA 访问宽度。
unit_size每次 DMA 请求搬运的数据单元大小,单位为字节;HAL_DMA_UNIT_SIZE_AUTO 表示自主传输大小模式。
descriptordescriptor 模式使用的调用者提供 DMA descriptor buffer;只使用非 descriptor 模式时可为 NULL。
descriptor_capacitydescriptor buffer 中可用的 descriptor 数量;descriptor 为 NULL 时必须为 0。

关键约束:

  • request 必须是 HAL 当前支持的合法请求源。

  • bus_width 必须是 HAL_DMA_BUS_WIDTH_8BIT/16BIT/32BIT 之一。

  • unit_size 当前支持 HAL_DMA_UNIT_SIZE_AUTO(0)、1/2/4/8/16/32/64 或 >=128 字节;固定单元大小编码时,8 会按硬件语义归一到 4,>=128 归一到 128。

  • HAL_DMA_UNIT_SIZE_AUTO 要求 bus_width 为 HAL_DMA_BUS_WIDTH_32BIT;传输长度及 linked/cyclic 每段长度必须至少为 8 字节,且为 8 的倍数。

  • descriptor 为 NULL 时,descriptor_capacity 必须为 0,此时只能使用 hal_dma_start_nondescriptor()。

  • 使用 hal_dma_start()、hal_dma_start_linked() 或 hal_dma_start_cyclic() 时,descriptor 必须按 HAL_DMA_DESCRIPTOR_ALIGN 对齐,buffer 大小可用 HAL_DMA_DESCRIPTOR_BUFFER_SIZE(count) 计算。

  • linked/cyclic 传输还要求本次使用的 descriptor 数组位于同一 4 KiB DOA 编码窗口内;如果希望支持较大的 count,建议 descriptor buffer 使用 4 KiB 对齐。

  • descriptor 的生命周期由调用者保证,必须覆盖 hal_dma_open() 到 hal_dma_close() 期间的 DMA 使用。

1.6 DMA 句柄

typedef struct hal_dma_handle hal_dma_handle_t;
struct hal_dma_handle {
    hal_dma_request_t request;
    hal_dma_bus_width_t bus_width;
    uint32_t unit_size;
    hal_dma_callback_t callbacks[HAL_DMA_CALLBACK_COUNT];
    void *user_ctx;
    uint32_t controller_id;
    uint32_t channel_id;
    hal_dma_state_t state;
    void *descriptor;
    uint32_t descriptor_count;
    uint32_t descriptor_capacity;
    uintptr_t src_start;
    uintptr_t dst_start;
    uintptr_t src_end;
    uintptr_t dst_end;
    uint8_t last_event;
    uint8_t last_completed_descriptor;
    uint8_t descriptor_event_advance;
    uint8_t descriptor_event_flags;
};

说明:

  • 调用者提供 hal_dma_handle_t 存储空间,并通过 hal_dma_open() 初始化。

  • request、bus_width、unit_size 和 descriptor storage 来自 hal_dma_config_t;callback slot 和 user_ctx 只能通过显式注册接口配置。

  • descriptor 指向调用者提供的 descriptor buffer;descriptor_capacity 保存该 buffer 容量,descriptor_count 保存当前传输实际使用数量。

  • controller_id、channel_id、state、descriptor_count、descriptor_capacity、地址范围以及末尾四个事件状态字段由 HAL 管理。

  • HAL 内部字段不应由调用者直接修改或释放。

2. HAL 与 IRQ 集成接口规范

2.1 HAL 层实现边界

  • HAL 层公开接口使用 hal_dma_handle_t *handle 或 const hal_dma_handle_t *handle 传递软件句柄。

  • HAL 层使用 IM_HAL_PARAM_CHECK_RETURN 做参数合法性检查。

  • HAL 层普通可失败接口返回 error_code_t。

  • DMA HAL 负责 DMA 控制器侧通道、descriptor 填充、启动、停止、IRQ service 和回调。

  • DMA HAL 不注册、使能或释放 SoC IRQ;调用方必须为使用的 controller 接入对应 IRQ。

  • DMA HAL 不负责配置 UART、SSI、I2C、CAN、AIC、ADC、PWM 等外设自身的 DMA 使能位、FIFO 阈值或协议状态。

  • 数据 buffer 的 cache flush/invalidate 由调用方或外设驱动负责;HAL 只维护 descriptor cache。

2.2 HAL 初始化、通道生命周期和 IRQ 集成

2.2.1 hal_dma_init

error_code_t hal_dma_init(void);

功能说明:

初始化 PDMA0 和 PDMA1 两个 DMA 控制器,打开控制器时钟并清理 HAL 内部控制器状态。本接口不注册或使能任何 SoC IRQ。

参数表:

参数类型方向说明
无---

返回值表:

返回值说明
IM_OK初始化成功,或已经初始化。
其他错误码时钟使能失败。

补充说明:

  • 本接口可重复调用;已经初始化后直接返回 IM_OK。

  • hal_dma_open() 内部会自动调用本接口。

  • 若传输需要回调,调用方还必须接入对应 PDMA SoC IRQ,并在自己的 SoC IRQ entry 中调用hal_dma_irq_handler()。

2.2.2 hal_dma_irq_handler

void hal_dma_irq_handler(hal_dma_controller_t controller);

功能说明:

服务指定 controller 的 normal 与 descriptor pending DMA interrupt。HAL 读取DIRQP/DIP,定位 channel/handle,维护 event snapshot、通道状态和 callback。

补充说明:

  • 该接口由调用方的 SoC IRQ entry 调用;对于同一 controller,normal 和 descriptor IRQ 都必须路由到它。

  • HAL 只处理自己管理的 channel;不同调用方不能共享同一个 controller 的 channel 管理权。

  • callback 处于 PDMA ISR context,不能阻塞;terminal event callback 前 HAL 已释放通道,callback 可以启动下一笔传输。

2.2.3 hal_dma_open

error_code_t hal_dma_open(hal_dma_handle_t *handle,
                          const hal_dma_config_t *config);

功能说明:

初始化 DMA 句柄,保存请求源、总线宽度、单元大小和调用者提供的 descriptor buffer,并根据请求源确定控制器。

参数表:

参数类型方向说明
handlehal_dma_handle_t *输出DMA 句柄存储空间,不能为 NULL。
configconst hal_dma_config_t *输入DMA 打开配置,不能为 NULL。

返回值表:

返回值说明
IM_OK打开成功。
IM_ERR_INVALID_PARAMhandle/config 为空,请求源、总线宽度、单元大小、descriptor buffer 或 descriptor 对齐非法。
其他错误码hal_dma_init() 失败返回的错误码。

补充说明:

  • 本接口只初始化句柄,不立即占用硬件通道。

  • 硬件通道在 hal_dma_start*() 启动传输时分配。

  • HAL 分配通道时只依据内部软件占用位判断空闲状态;通道会在完成中断、hal_dma_stop() 或启动失败清理路径中释放。

  • 上层模块应在 open() 成功、第一次 start() 前使用 hal_dma_callback_register() 和 hal_dma_user_data_set() 设置所需事件槽。

2.2.4 hal_dma_close

error_code_t hal_dma_close(hal_dma_handle_t *handle);

功能说明:

关闭一个 DMA 句柄,清理运行时状态。HAL 不释放调用者提供的 descriptor buffer。

参数表:

参数类型方向说明
handlehal_dma_handle_t *输入/输出DMA 句柄,不能为 NULL。

返回值表:

返回值说明
IM_OK关闭成功。
IM_ERR_INVALID_PARAMhandle 为空。
IM_ERR_BUSY句柄仍处于忙状态。

补充说明:

  • 调用 hal_dma_close() 前应确认传输已完成,或先调用 hal_dma_stop()。

  • 关闭后句柄恢复为未使用状态,descriptor buffer 生命周期仍由调用者管理。

2.2.5 hal_dma_callback_register

error_code_t hal_dma_callback_register(hal_dma_handle_t *handle,
                                       hal_dma_callback_id_t callback_id,
                                       hal_dma_callback_t callback);

功能说明:

注册或更新一个 DMA 事件槽的回调。只能在已打开且处于 READY 状态的句柄上调用;传入 NULL callback 可取消该事件槽的回调。

参数表:

参数类型方向说明
handlehal_dma_handle_t *输入/输出已打开的空闲 DMA 句柄,不能为 NULL。
callback_idhal_dma_callback_id_t输入descriptor complete、transfer complete 或 error 事件槽;不能为 HAL_DMA_CALLBACK_COUNT。
callbackhal_dma_callback_t输入ISR context 中调用的回调,或用于取消注册的 NULL。

返回值表:

返回值说明
IM_OK回调槽更新成功。
IM_ERR_INVALID_PARAMhandle 为空或 callback_id 非法。
IM_ERR_NOT_READY句柄处于 UNUSED 状态。
IM_ERR_BUSY句柄已打开,但当前不是 READY 状态。

2.2.6 hal_dma_user_data_set

error_code_t hal_dma_user_data_set(hal_dma_handle_t *handle, void *user_ctx);

功能说明:

设置三个 DMA 回调槽共享的调用者上下文。只能在已打开且处于 READY 状态的句柄上调用。

参数表:

参数类型方向说明
handlehal_dma_handle_t *输入/输出已打开的空闲 DMA 句柄,不能为 NULL。
user_ctxvoid *输入传递给回调的上下文,允许为 NULL。

返回值表:

返回值说明
IM_OK上下文更新成功。
IM_ERR_INVALID_PARAMhandle 为空。
IM_ERR_NOT_READY句柄处于 UNUSED 状态。
IM_ERR_BUSY句柄已打开,但当前不是 READY 状态。

2.3 启动传输

2.3.1 hal_dma_start

error_code_t hal_dma_start(hal_dma_handle_t *handle,
                           void *src,
                           void *dst,
                           uint32_t len);

功能说明:

启动一次普通 DMA 传输。该接口使用单个 8-word descriptor 完成传输,适用于一次性内存到内存、内存到外设或外设到内存传输。

参数表:

参数类型方向说明
handlehal_dma_handle_t *输入/输出已通过 hal_dma_open() 打开的 DMA 句柄。
srcvoid *输入源地址,不能为 NULL。
dstvoid *输入目的地址,不能为 NULL。
lenuint32_t输入传输长度,单位为字节。

返回值表:

返回值说明
IM_OK启动成功。
IM_ERR_INVALID_PARAM句柄、地址、长度、descriptor buffer 或传输参数非法。
IM_ERR_BUSY无空闲通道或句柄状态不允许启动。

补充说明:

  • len 必须大于 0;固定单元大小模式须满足 unit_size 和 bus_width 对齐约束,AUTO 模式须至少为 8 字节且为 8 的倍数;计数范围见 7.3。

  • 该接口要求 hal_dma_open() 时提供有效 descriptor buffer。

  • 启动后句柄状态切为 HAL_DMA_STATE_BUSY。

  • 完成中断到来后,HAL 将状态切回 HAL_DMA_STATE_READY 并调用回调。

2.3.2 hal_dma_start_nondescriptor

error_code_t hal_dma_start_nondescriptor(hal_dma_handle_t *handle,
                                         void *src,
                                         void *dst,
                                         uint32_t len);

功能说明:

启动一次非 descriptor DMA 传输。该接口直接配置通道 DSA/DTA/DTC/DRT/DCM/DSD 寄存器,并通过 DCS_NDES_MASK | DCS_CTE_MASK 启动传输,不使用 descriptor buffer。

参数表:

参数类型方向说明
handlehal_dma_handle_t *输入/输出已通过 hal_dma_open() 打开的 DMA 句柄。
srcvoid *输入源地址,不能为 NULL。
dstvoid *输入目的地址,不能为 NULL。
lenuint32_t输入传输长度,单位为字节。

返回值表:

返回值说明
IM_OK启动成功。
IM_ERR_INVALID_PARAM句柄、地址、长度或传输参数非法。
IM_ERR_BUSY无空闲通道或句柄状态不允许启动。
IM_ERR_UNSUPPORTED当前请求源不支持非 descriptor 模式,例如 CAN RX。

补充说明:

  • len 必须大于 0;固定单元大小模式须满足 unit_size 和 bus_width 对齐约束,AUTO 模式须至少为 8 字节且为 8 的倍数;计数范围见 7.3。

  • 该接口不要求 hal_dma_open() 时提供 descriptor buffer。

  • 当前非 descriptor 模式暂不支持 HAL_DMA_REQUEST_CAN0_RX / HAL_DMA_REQUEST_CAN1_RX。

  • 完成中断到来后,HAL 将状态切回 HAL_DMA_STATE_READY 并调用回调。

2.3.3 hal_dma_start_linked

error_code_t hal_dma_start_linked(hal_dma_handle_t *handle,
                                  void *src,
                                  void *dst,
                                  uint32_t len,
                                  uint32_t count);

功能说明:

启动一次 linked descriptor DMA 传输。HAL 将总长度拆分为 count 个 descriptor,并按顺序链接执行。

参数表:

参数类型方向说明
handlehal_dma_handle_t *输入/输出已通过 hal_dma_open() 打开的 DMA 句柄。
srcvoid *输入源地址,不能为 NULL。
dstvoid *输入目的地址,不能为 NULL。
lenuint32_t输入总传输长度,单位为字节。
countuint32_t输入descriptor 数量。

返回值表:

返回值说明
IM_OK启动成功。
IM_ERR_INVALID_PARAM参数非法、descriptor buffer 容量不足或跨 4 KiB DOA 窗口、count 为 0、count > 128、len 不能被 count 整除或分段长度不满足约束。
IM_ERR_BUSY无空闲通道或句柄状态不允许启动。

补充说明:

  • len 必须能被 count 整除。

  • 固定单元大小模式下,每段长度必须不小于 unit_size,且能被 unit_size 整除;AUTO 模式下,每段长度必须至少为 8 字节且为 8 的倍数;计数范围见 7.3。

  • HAL 使用调用者提供的 descriptor buffer,并校验 buffer 容量。

  • 本次使用的 descriptor 数组必须位于同一 4 KiB DOA 编码窗口内,避免 DESC_DTC_DOA 8-bit offset wrap;代码使用 DESC_DTC_DOA_MASK 构造该字段;若不满足该约束,接口返回 IM_ERR_INVALID_PARAM。

  • 最后一个 descriptor 使能传输完成中断并清除 LINK 位。

2.3.4 hal_dma_start_cyclic

error_code_t hal_dma_start_cyclic(hal_dma_handle_t *handle,
                                  void *src,
                                  void *dst,
                                  uint32_t len,
                                  uint32_t count);

功能说明:

启动一次 cyclic descriptor DMA 传输。HAL 将多个 descriptor 首尾相连形成循环链,适用于持续接收或环形缓冲区场景。

参数表:

参数类型方向说明
handlehal_dma_handle_t *输入/输出已通过 hal_dma_open() 打开的 DMA 句柄。
srcvoid *输入源地址,不能为 NULL。
dstvoid *输入目的地址,不能为 NULL。
lenuint32_t输入总传输长度,单位为字节。
countuint32_t输入descriptor 数量。

返回值表:

返回值说明
IM_OK启动成功。
IM_ERR_INVALID_PARAM参数非法、descriptor buffer 容量不足或跨 4 KiB DOA 窗口、count 为 0、count > 128、len 不能被 count 整除或分段长度不满足约束。
IM_ERR_BUSY无空闲通道或句柄状态不允许启动。

补充说明:

  • 参数约束与 hal_dma_start_linked() 相同。

  • cyclic 使用的 descriptor 数组必须与 linked 一样位于同一 4 KiB DOA 编码窗口内;建议使用 4 KiB 对齐的 descriptor buffer。

  • 最后一个 descriptor 的 DESC_DTC_DOA 指回第一个 descriptor,并保持 LINK 位。

  • cyclic 的每个 descriptor 都启用 TIE。每次 descriptor 完成时,HAL 记录 HAL_DMA_EVENT_DESCRIPTOR_COMPLETE 并调用已注册的 descriptor callback,但保持 handle 为 HAL_DMA_STATE_BUSY。

  • 循环传输通常需要配合 hal_dma_stop_after_current_transfer() 或 hal_dma_stop() 停止。

2.4 停止传输

2.4.1 hal_dma_stop_after_current_transfer

error_code_t hal_dma_stop_after_current_transfer(hal_dma_handle_t *handle,
                                                 void *mem);

功能说明:

用于源地址递增的 linked/cyclic 传输场景。HAL 根据传入的源侧内存地址定位 descriptor,并清除该 descriptor 的 LINK 位,使 DMA 在完成该 descriptor 后停止。

参数表:

参数类型方向说明
handlehal_dma_handle_t *输入/输出正在执行 linked/cyclic 传输的 DMA 句柄。
memvoid *输入源侧内存地址,用于选择需要停止链接的 descriptor。

返回值表:

返回值说明
IM_OK设置成功。
IM_ERR_INVALID_PARAMhandle/mem 为空,或 mem 不在当前源地址范围内。
IM_ERR_NOT_READY句柄未处于忙状态,或当前不是 linked/cyclic 多 descriptor 传输。

补充说明:

  • 该接口只适用于 linked/cyclic 多 descriptor 传输;普通单 descriptor 传输会返回 IM_ERR_NOT_READY。

  • mem 使用源侧地址语义,而不是目的地址语义。

  • 固定源地址的外设 RX 不适用:此时 src_start == src_end,没有可接受的 mem 地址,接口返回 IM_ERR_INVALID_PARAM;应使用 hal_dma_stop() 停止。

  • HAL 会将 mem 转换为 DMA/物理地址后与 handle->src_start/src_end 比较。

  • 修改 descriptor 后会 flush descriptor cache。

2.4.2 hal_dma_stop

error_code_t hal_dma_stop(hal_dma_handle_t *handle);

功能说明:

停止当前 DMA 传输,释放当前硬件通道记录,并将句柄状态恢复为 READY。

参数表:

参数类型方向说明
handlehal_dma_handle_t *输入/输出DMA 句柄,不能为 NULL。

返回值表:

返回值说明
IM_OK停止成功,或句柄当前并非忙状态。
IM_ERR_INVALID_PARAMhandle 为空。

补充说明:

  • 忙状态下会读取当前源/目的地址并更新结束地址记录。

  • 本接口用于强制停止,外设 DMA 场景下外设驱动应先关闭外设侧 DMA 请求。

2.5 事件和当前地址查询

2.5.1 hal_dma_get_last_event

error_code_t hal_dma_get_last_event(const hal_dma_handle_t *handle,
                                    hal_dma_event_info_t *event_out);

功能说明:

读取 HAL 在最近一次 DMA callback 前记录的事件。callback 已按事件槽分发;此接口 用于读取 descriptor index、pending snapshot 和诊断信息。

HAL_DMA_EVENT_DESCRIPTOR_COMPLETE 只用于仍在运行的 descriptor 事件,尤其是 cyclic DMA;此时 handle 保持 HAL_DMA_STATE_BUSY。HAL_DMA_EVENT_TRANSFER_COMPLETE和 HAL_DMA_EVENT_ERROR 的 callback 到来前,handle 已转为 HAL_DMA_STATE_READY, 并且 channel_id 已失效。接口在 IRQ 临界区中复制完整 event snapshot;最推荐在 DMA callback 中读取。

一般情况下,descriptor_index 是由 DCS.CDOA、descriptor 物理 4 KiB 窗口基址和 16-byte offset 编码还原,并且按实际 descriptor 数组起点和 32-byte stride 校验后的 index。 当 descriptor_index_valid 为 false 时,该值为 UINT32_MAX。

单 descriptor cyclic 是例外:HAL 直接报告 descriptor_index=0、descriptor_advance=1、descriptor_index_valid=true,不依赖 CDOA 解码。

descriptor_events_coalesced 仅表示 HAL 能从 index 跳跃观察到事件合并;由于 DIP 是每通道 pending bit 而不是计数器,false 不能证明中断逐个送达。CDOA 无法通过 物理地址/stride 校验时只会使 descriptor_index_valid 为 false,不会被误报为 coalesced。

normal_irq_pending 与 descriptor_irq_pending 是 IRQ handler 入口时的 DIRQP/DIP 快照,可用于验证两个 pending 同时出现时仍只生成一个 callback。

2.5.2 hal_dma_get_src_addr

error_code_t hal_dma_get_src_addr(hal_dma_handle_t *handle,
                                  uintptr_t *addr_out);

功能说明:

读取当前 DMA 硬件正在访问的源地址。

参数表:

参数类型方向说明
handlehal_dma_handle_t *输入/输出DMA 句柄,不能为 NULL。
addr_outuintptr_t *输出当前源地址输出指针,不能为 NULL。

返回值表:

返回值说明
IM_OK读取成功。
IM_ERR_INVALID_PARAM参数为空,或忙状态句柄的控制器/通道号非法。
IM_ERR_HARDWARE硬件地址在 1024 次重试内始终未落入当前地址范围。

补充说明:

  • 忙状态下最多重试 1024 次读取硬件 DSA,读取到当前源地址范围内的地址即返回成功。

  • 非忙状态下返回 handle->src_end。

  • 重试耗尽仍未读取到合法地址时返回 IM_ERR_HARDWARE,不写入 addr_out;不使用基于时间的超时等待。

2.5.3 hal_dma_get_dst_addr

error_code_t hal_dma_get_dst_addr(hal_dma_handle_t *handle,
                                  uintptr_t *addr_out);

功能说明:

读取当前 DMA 硬件正在访问的目的地址。

参数表:

参数类型方向说明
handlehal_dma_handle_t *输入/输出DMA 句柄,不能为 NULL。
addr_outuintptr_t *输出当前目的地址输出指针,不能为 NULL。

返回值表:

返回值说明
IM_OK读取成功。
IM_ERR_INVALID_PARAM参数为空,或忙状态句柄的控制器/通道号非法。
IM_ERR_HARDWARE硬件地址在 1024 次重试内始终未落入当前地址范围。

补充说明:

  • 忙状态下最多重试 1024 次读取硬件 DTA,读取到当前目的地址范围内的地址即返回成功。

  • 非忙状态下返回 handle->dst_end。

  • 重试耗尽仍未读取到合法地址时返回 IM_ERR_HARDWARE,不写入 addr_out;不使用基于时间的超时等待。

2.6 调试诊断

2.6.1 hal_dma_dump_regs

error_code_t hal_dma_dump_regs(const hal_dma_handle_t *handle);

功能说明:

打印指定 DMA 句柄对应通道的寄存器信息,用于芯片调试、bring-up 或现场问题诊断。

参数表:

参数类型方向说明
handleconst hal_dma_handle_t *输入DMA 句柄,不能为 NULL。

返回值表:

返回值说明
IM_OKdump 成功。
IM_ERR_INVALID_PARAMhandle 为空或句柄内部控制器/通道信息非法。
IM_ERR_UNSUPPORTED未同时启用 CONFIG_SDK_ENABLE_DEBUG_HELPERS 和 CONFIG_DMA_ENABLE_DEBUG_HELPERS。

补充说明:

  • dump 内容包括通道 DSA/DTA/DTC/DRT/DCS/DCM/DDA/DSD,以及 SACIDX0/1、DACIDX0/1。

  • 接口声明始终存在,功能体由 debug helper 双开关控制。

2.6.2 hal_dma_dump_desc

error_code_t hal_dma_dump_desc(const hal_dma_handle_t *handle);

功能说明:

打印指定 DMA 句柄当前持有的 descriptor 信息,用于检查 descriptor 链、地址、传输长度和控制位配置。

参数表:

参数类型方向说明
handleconst hal_dma_handle_t *输入DMA 句柄,不能为 NULL。

返回值表:

返回值说明
IM_OKdump 成功。
IM_ERR_INVALID_PARAMhandle 为空或句柄未持有 descriptor。
IM_ERR_NOT_READY当前没有已配置的有效传输 descriptor。
IM_ERR_UNSUPPORTED未同时启用 CONFIG_SDK_ENABLE_DEBUG_HELPERS 和 CONFIG_DMA_ENABLE_DEBUG_HELPERS。

补充说明:

  • dump 内容包括每个 descriptor 的 dcm/dsa/dta/dtc/sd/drt。

  • descriptor_count 为 0 时,表示当前没有已配置的有效传输 descriptor,dump 返回 IM_ERR_NOT_READY。

3. 当前实现约束与行为说明

3.1 Descriptor 模式

  • 8-word descriptor 模式:单次传输使用 1 个 descriptor,linked/cyclic 传输使用 descriptor 数组。

  • 非 descriptor 模式:单次传输直接配置通道寄存器,不使用 descriptor buffer。

  • descriptor 路径和非 descriptor 路径分别在 hal_dma_init_desc() 与 hal_dma_init_nondescriptor_regs() 中构造 DCM 和 transfer count。

  • linked/cyclic descriptor 数组必须位于同一 4 KiB DOA 编码窗口内,避免 DESC_DTC_DOA 8-bit offset wrap;代码使用 DESC_DTC_DOA_MASK 构造该字段。

3.2 CAN RX SACIDX 行为

  • HAL_DMA_REQUEST_CAN0_RX 使用 SACIDX0。

  • HAL_DMA_REQUEST_CAN1_RX 使用 SACIDX1。

  • HAL 只在 linked/cyclic CAN RX 路径配置 SACIDX。

  • 普通 UART/SPI/I2C/ADC/AIC RX 不使用 SACIDX。

4. 典型用法

本节给出 HAL 的高级 bring-up/reference 用法。直接使用 HAL 的调用方必须确定实际使用 controller 的 normal/DIP IRQ 对的唯一 owner,并在自己的 SoC IRQ entry 中调用hal_dma_irq_handler();不得重复注册同一 IRQ。

4.1 外设 TX DMA

以外设发送为例,数据流通常为“内存 -> 外设 FIFO/数据寄存器”。外设驱动需要配置:

  • src:发送缓冲区地址。

  • dst:外设 TX FIFO 或 TX 数据寄存器地址。

  • request:对应外设 TX DMA 请求源,例如 HAL_DMA_REQUEST_UART1_TX。

  • 外设侧寄存器:TX FIFO 阈值、TX DMA 请求使能位、必要的错误中断策略。

外设驱动对象中应为每个可能并发使用的 DMA handle 准备独立 descriptor buffer:

typedef struct {
    hal_dma_handle_t tx_dma;
    hal_dma_handle_t rx_dma;
    uint8_t tx_dma_desc[HAL_DMA_DESCRIPTOR_BUFFER_SIZE(HAL_DMA_DESCRIPTOR_COUNT)]
        __attribute__((aligned(HAL_DMA_DESCRIPTOR_ALIGN)));
    uint8_t rx_dma_desc[HAL_DMA_DESCRIPTOR_BUFFER_SIZE(HAL_DMA_DESCRIPTOR_COUNT)]
        __attribute__((aligned(HAL_DMA_DESCRIPTOR_ALIGN)));
} uart_driver_t;
hal_dma_config_t dma_config = {
    .request = HAL_DMA_REQUEST_UART1_TX,
    .bus_width = HAL_DMA_BUS_WIDTH_8BIT,
    .unit_size = 1U,
    .descriptor = uart->tx_dma_desc,
    .descriptor_capacity = HAL_DMA_DESCRIPTOR_COUNT,
};

hal_dma_open(&uart->tx_dma, &dma_config);
hal_dma_user_data_set(&uart->tx_dma, uart);
hal_dma_callback_register(&uart->tx_dma,
                          HAL_DMA_CALLBACK_TRANSFER_COMPLETE,
                          uart_dma_tx_done);
hal_dma_callback_register(&uart->tx_dma, HAL_DMA_CALLBACK_ERROR, uart_dma_tx_done);

/* UART 驱动配置 TX FIFO 阈值,并打开 UART TX DMA 请求。 */

hal_dma_start(&uart->tx_dma,
              tx_buffer,
              (void *)uart_tx_fifo_addr,
              tx_len);

说明:

  • uart_tx_fifo_addr 应由 UART 驱动根据 UART 实例计算或定义。

  • uart->tx_dma_desc 应按 HAL_DMA_DESCRIPTOR_BUFFER_SIZE(HAL_DMA_DESCRIPTOR_COUNT) 分配,并按 HAL_DMA_DESCRIPTOR_ALIGN 对齐。

  • DMA HAL 不应猜测或配置 UART 寄存器。

4.2 外设 RX DMA

以外设接收为例,数据流通常为“外设 FIFO/数据寄存器 -> 内存”。外设驱动需要配置:

  • src:外设 RX FIFO 或 RX 数据寄存器地址。

  • dst:接收缓冲区地址。

  • request:对应外设 RX DMA 请求源,例如 HAL_DMA_REQUEST_UART1_RX。

  • 外设侧寄存器:RX FIFO 阈值、RX DMA 请求使能位、超时/错误中断策略。

hal_dma_config_t dma_config = {
    .request = HAL_DMA_REQUEST_UART1_RX,
    .bus_width = HAL_DMA_BUS_WIDTH_8BIT,
    .unit_size = 1U,
    .descriptor = uart->rx_dma_desc,
    .descriptor_capacity = HAL_DMA_DESCRIPTOR_COUNT,
};

hal_dma_open(&uart->rx_dma, &dma_config);
hal_dma_user_data_set(&uart->rx_dma, uart);
hal_dma_callback_register(&uart->rx_dma,
                          HAL_DMA_CALLBACK_TRANSFER_COMPLETE,
                          uart_dma_rx_done);
hal_dma_callback_register(&uart->rx_dma, HAL_DMA_CALLBACK_ERROR, uart_dma_rx_done);

/* UART 驱动配置 RX FIFO 阈值,并打开 UART RX DMA 请求。 */

hal_dma_start(&uart->rx_dma,
              (void *)uart_rx_fifo_addr,
              rx_buffer,
              rx_len);

说明:

  • DMA 完成只表示 FIFO/寄存器与内存之间的数据搬运完成,不一定表示外设协议层完全结束。

  • uart->rx_dma_desc 应按 HAL_DMA_DESCRIPTOR_BUFFER_SIZE(HAL_DMA_DESCRIPTOR_COUNT) 分配,并按 HAL_DMA_DESCRIPTOR_ALIGN 对齐。

  • 外设驱动需要在 DMA 完成后检查协议状态和错误状态。