API GUIDE / DMA / OVERVIEW
G32S10 DMA API 概述
模块概述:本文档定义 G32S10 SDK DMA 硬件抽象层和低层接口,覆盖以下两层:
1. 文档范围
本文档定义 G32S10 SDK DMA 硬件抽象层和低层接口,覆盖以下两层:
HAL DMA:供上层模块和高级用户使用的硬件操作层。LL DMA:面向 DMA 控制器寄存器访问的低层接口。
对应文件:
| 类型 | 路径 |
|---|---|
| HAL 头文件 | code/include/drivers/g32s10/dma/hal_dma.h |
| HAL 实现 | code/drivers/g32s10/dma/hal_dma.c |
| LL 头文件 | code/include/drivers/g32s10/dma/ll_dma.h |
| 寄存器定义 | code/include/drivers/g32s10/dma/dma_regs.h |
补充说明:
LL 层公开接口均为
static inline,不维护软件状态。LL 层不返回 SDK 错误码;需要读取寄存器值时返回
uint32_t。HAL 层负责参数校验、通道分配、descriptor 编排、非 descriptor 直写寄存器启动、状态管理、pending 扫描和统一返回码。
HAL 是高级用户或上层模块的硬件接口;上层负责自己的 DMA 请求生命周期和 SoC IRQ owner。
当前 DMA HAL 支持 8-word descriptor 模式和非 descriptor 单次传输模式。
2. 分层关系
Application / Advanced Example | v HAL DMA | v LL DMA | v PDMA0 / PDMA1 Registers Advanced caller ----------------------> HAL DMA ----> LL DMA
分层职责如下:
HAL DMA封装 DMA 控制器初始化、通道打开/关闭、普通传输、链式传输、循环传输、停止、当前地址查询和诊断 dump。
根据
hal_dma_request_t选择 DMA 控制器和通道。维护
hal_dma_handle_t中的请求源、总线宽度、单元大小、事件回调槽、通道号、descriptor 和传输地址范围。在调用方已接入 SoC IRQ 后,扫描 controller 的
DIRQP/DIPpending、分发 channel event 并调用用户回调。不注册、使能或释放 SoC IRQ。
LL DMA提供寄存器访问入口,例如全局使能、通道状态、descriptor 地址、doorbell、当前源/目的地址和诊断寄存器读取。
不做请求源分类、descriptor 构造、通道分配或状态管理。
公开接口首参数为
dma_regs_t *dma。dma_regs.h定义
PDMA0/PDMA1基地址实例、寄存器结构体布局、通道数量和寄存器位域 mask/shift。
3. 资源模型与公开类型
3.1 模块基地址与实例
| 实例 | 基地址宏 | 基地址 | 说明 |
|---|---|---|---|
PDMA0 | PDMA0_IOBASE | 0x13420000 | HP-domain PDMA 控制器。 |
PDMA1 | PDMA1_IOBASE | 0x13610000 | LP-domain PDMA 控制器。 |
dma_regs.h 中提供如下实例宏:
#define PDMA0 ((dma_regs_t *)(uintptr_t)PDMA0_IOBASE) #define PDMA1 ((dma_regs_t *)(uintptr_t)PDMA1_IOBASE)
说明:
实例宏使用
(uintptr_t)做整数地址到指针的中间转换,避免部分编译器对整数直接转指针产生告警。HAL 内部通过请求源选择
PDMA0或PDMA1,上层通常不直接操作dma_regs_t *。
3.2 DMA 硬件能力摘要
| 项目 | 说明 |
|---|---|
| 控制器数量 | 2 个,分别为 PDMA0 和 PDMA1。 |
| 每控制器通道数 | 32 个通道。 |
| 传输模式 | 支持 8-word descriptor 模式和非 descriptor 模式。 |
| 支持传输类型 | 非 descriptor 单次传输、普通单 descriptor、linked descriptor、cyclic descriptor。 |
| 请求源 | 覆盖 TPC、AES、HASH、MEM、UART、SSI、CAN、I2C、PWMA、ADC、AIC 等请求。 |
| 回调模型 | 调用方将 normal/descriptor IRQ 转交 HAL;HAL 在 ISR context 调用 hal_dma_callback_t 并记录 event snapshot。 |
| 当前地址读取 | 忙状态下会有限次重试读取硬件当前地址;持续异常返回 IM_ERR_HARDWARE,不会无限等待。 |
| cache 一致性 | HAL 维护 descriptor cache;调用方负责自己的数据 buffer cache 一致性。 |
3.3 HAL 公开宏
| 宏 | 使用说明 | 取值/约束 |
|---|---|---|
HAL_DMA_CHANNEL_COUNT | HAL 公开的单控制器通道数量。 | 等于 DMA_CHANNEL_COUNT,当前为 32。 |
HAL_DMA_DESCRIPTOR_COUNT | 普通单次传输默认 descriptor 数量。 | 1。 |
HAL_DMA_MAX_DESCRIPTOR_COUNT | linked/cyclic 当前支持的最大 descriptor 数量。 | 128。 |
HAL_DMA_UNIT_SIZE_AUTO | 选择自主传输大小模式,使用 DCM.TSZ=7、DCM.RDIL=5。 | 0;要求 32-bit 总线宽度,传输长度至少 8 字节且为 8 的倍数。 |
HAL_DMA_DESCRIPTOR_SIZE | 对外公开的单个 descriptor buffer 占用大小。 | 32 字节。 |
HAL_DMA_DESCRIPTOR_ALIGN | descriptor buffer 地址最低对齐要求。 | IM_CACHE_MAX_LINE_SIZE_BYTES。 |
HAL_DMA_DESCRIPTOR_BUFFER_SIZE(count) | 计算可容纳 count 个 descriptor 的 buffer 字节数。 | 结果按 HAL_DMA_DESCRIPTOR_ALIGN 对齐;不保证 linked/cyclic 的 4 KiB DOA 窗口约束。 |
CONFIG_DMA_ENABLE_DEBUG_HELPERS | DMA 模块 debug helper 默认开关。 | 未由 Kconfig 定义时默认 0U。 |
调试 dump 接口受双开关控制:
(CONFIG_SDK_ENABLE_DEBUG_HELPERS) && (CONFIG_DMA_ENABLE_DEBUG_HELPERS)
未同时启用时,dump API 仍然存在,但返回 IM_ERR_UNSUPPORTED。
3.4 HAL 返回状态约定
HAL 层普通可失败接口统一返回 SDK 公共错误码类型:
error_code_t
常用返回值如下:
| 返回值 | 说明 |
|---|---|
IM_OK | 操作成功。 |
IM_ERR_INVALID_PARAM | 参数为空、请求源非法、总线宽度非法、长度不满足约束或地址不在当前范围内。 |
IM_ERR_BUSY | 无空闲通道、目标资源忙或关闭忙状态句柄。 |
IM_ERR_NOT_READY | 句柄/descriptor 状态不满足当前操作要求。 |
IM_ERR_HARDWARE | 当前硬件地址在有限次重试内始终未落入 HAL 记录的地址范围。 |
IM_ERR_TIMEOUT | 当前 HAL 公开接口未主动使用;示例代码等待完成时可能使用。 |
IM_ERR_UNSUPPORTED | 当前配置不支持该操作,例如未启用 debug helper 时调用 dump。 |
3.5 DMA 请求源类型
typedef enum {
HAL_DMA_REQUEST_TPC_SHIFT_TX = 0x00U,
...
HAL_DMA_REQUEST_AIC_RX_LOOPBACK = 0x47U,
} hal_dma_request_t;请求源用于描述 DMA 硬件请求来源,并决定:
使用
PDMA0还是PDMA1控制器。源地址和目的地址是否按内存地址递增。
是否需要 CAN RX 的 SACIDX 源地址 wrap/compare 配置。
当前 HAL 控制器选择规则:
| 控制器 | 请求源范围 |
|---|---|
PDMA1 | AIC_TX/RX/RX_LOOPBACK、SSI0_TX/RX、I2C0_TX/RX、UART0_TX/RX、UART1_TX/RX、SSI_SLV0_TX/RX。 |
PDMA0 | 其他合法请求源,包括 HAL_DMA_REQUEST_MEM。 |
说明:
HAL_DMA_REQUEST_RESERVE不作为合法请求源开放。非法请求源会在
hal_dma_open()中返回IM_ERR_INVALID_PARAM。hal_dma_controller_t公开HAL_DMA_CONTROLLER_PDMA0和HAL_DMA_CONTROLLER_PDMA1,与PDMA0/PDMA1寄存器实例语义对应;它用于hal_dma_irq_handler()的 controller 参数。外设驱动必须根据自身实例和方向选择正确请求源,DMA HAL 不配置外设内部寄存器。
4. 当前实现约束与行为说明
4.1 地址语义
hal_dma_virt_to_phys()先调用im_mmio_virt_to_phys(),按IM_MMIO_PHYS_ADDR_MASK(0x1FFFFFFF)取得 DMA/物理地址,再转换为uint32_t。Descriptor 中的
dsa/dta和通道DDA使用 DMA/物理地址语义。handle->src_start/dst_start/src_end/dst_end记录 DMA/物理地址范围。如果后续引入 MMU、uncached alias 或地址映射,HAL 内部地址比较语义仍应保持一致。
4.2 传输长度和单元大小
len必须大于 0。固定单元大小模式下,
len必须不小于unit_size且能被其整除;unit_size必须不小于总线宽度对应的字节数,且能被该字节数整除。HAL_DMA_UNIT_SIZE_AUTO要求 32-bit 总线宽度,len必须至少为 8 字节且为 8 的倍数。linked/cyclic 模式中,
len必须能被count整除。linked/cyclic 单段长度在固定单元大小模式下必须不小于
unit_size且能被其整除;AUTO 模式下必须至少为 8 字节且为 8 的倍数。当前实现要求每个 descriptor 或非 descriptor 单次传输的
segment_len / normalized_unit_size < (1U << 24),不满足时返回IM_ERR_INVALID_PARAM。单 descriptor 和非 descriptor 的segment_len为len,linked/cyclic 为len / count;归一化单元大小在 AUTO 模式下为 1,固定大小8归一到4、>=128归一到128,其余保持原值。因此 AUTO 模式的单段长度还必须小于 16 MiB。AUTO 模式以及
HAL_DMA_REQUEST_AES0_TX、HAL_DMA_REQUEST_AES1_TX、HAL_DMA_REQUEST_HASH_TX请求均使用DCM.TSZ=7、DCM.RDIL=5,DTC按字节数写入segment_len;其他请求按segment_len / normalized_unit_size写入。AES/HASH 请求配置的固定unit_size仍参与前述长度和总线宽度校验。
4.3 中断和回调
HAL 不直接调用
im_irq_request()、im_irq_enable()或im_irq_release()。调用方必须为实际使用的 controller 注册并使能 normal 和 descriptor IRQ:PDMA0使用IRQ_PDMA0与IRQ_PDMA0_DIP,PDMA1使用IRQ_PDMA1与IRQ_PDMA1_DIP;每条 IRQ 都必须转交给对应的hal_dma_irq_handler()。同一 firmware image 中,每条 PDMA IRQ 和每个 controller 的 channel 管理权都必须有唯一 owner;调用方不得重复注册同一 IRQ 或与其他 owner 共享 channel。
hal_dma_irq_handler()同时处理DIRQP和 descriptorDIP。确认 channel 由 HAL 持有且 handle 有效后,先通过 LL 层同时清除两类 pending,再单次读取DCS分类;上层不得直接拼接 DIC 的反向 W0C 写值。HAL 只处理当前已分配的 channel。terminal 和 active-stop 路径会同时清除
DIRQP/DIP;其后的 CPU sibling IRQ 若仍到达,读取不到 controller pending 时不会产生 callback。终止后是否会出现新的 controller-levelDIPpending 仍需硅验证。DIP && !DCS.TT是 descriptor 完成事件:handle 保持 BUSY,descriptor callback 可通过hal_dma_get_last_event()读取最近完成的 descriptor index。DCS.TT是整次完成,优先于同一 IRQ 中的DIP/DIRQP,只回调一次;在 status 分类前两类 pending 均已清除,完成后 HAL 将句柄状态切回 READY 并释放通道。DCS.AR或DCS.HLT归类为HAL_DMA_EVENT_ERROR,完成 callback 前也会释放通道。DIP是每通道 pending bit 而非事件计数器。hal_dma_event_info_t.descriptor_events_coalesced仅报告能由 CDOA index 跳跃检测到的合并,false 不保证没有合并。
4.4 Debug helper 开关
hal_dma_dump_regs()和hal_dma_dump_desc()始终声明并实现。未同时启用
CONFIG_SDK_ENABLE_DEBUG_HELPERS和CONFIG_DMA_ENABLE_DEBUG_HELPERS时返回IM_ERR_UNSUPPORTED。DMA 模块 Kconfig 中的 debug helper 选项为
DMA_ENABLE_DEBUG_HELPERS。
4.5 文件组织
DMA 模块采用当前 SDK 的平铺结构:
code/drivers/g32s10/dma/CMakeLists.txt code/drivers/g32s10/dma/Kconfig code/drivers/g32s10/dma/hal_dma.c code/include/drivers/g32s10/dma/dma_regs.h code/include/drivers/g32s10/dma/hal_dma.h code/include/drivers/g32s10/dma/ll_dma.h
说明:
不使用
hal/或ll/子目录。DMA 示例代码不放入 DMA driver CMake 中长期编译。
code/examples/hal/dma_memcpy_example.c是高级 HAL bring-up/reference 示例。当前 memcpy 验证示例中 non-descriptor、single descriptor、linked 和 cyclic 模式均使用 4096 字节传输长度。
5. 典型用法
本节给出 HAL 的高级 bring-up/reference 用法。直接使用 HAL 的调用方必须确定实际使用
controller 的 normal/DIP IRQ 对的唯一 owner,并在自己的 SoC IRQ entry 中调用hal_dma_irq_handler();不得重复注册同一 IRQ。
5.1 内存到内存普通传输
static volatile bool dma_done;
static uint8_t dma_desc[HAL_DMA_DESCRIPTOR_BUFFER_SIZE(HAL_DMA_DESCRIPTOR_COUNT)]
__attribute__((aligned(HAL_DMA_DESCRIPTOR_ALIGN)));
static void dma_done_callback(hal_dma_handle_t *handle, void *user_ctx)
{
(void)handle;
(void)user_ctx;
dma_done = true;
}
error_code_t run_memcpy_dma(void *src, void *dst, uint32_t len)
{
hal_dma_handle_t dma_handle;
hal_dma_config_t dma_config = {
.request = HAL_DMA_REQUEST_MEM,
.bus_width = HAL_DMA_BUS_WIDTH_32BIT,
.unit_size = 4U,
.descriptor = dma_desc,
.descriptor_capacity = HAL_DMA_DESCRIPTOR_COUNT,
};
error_code_t ret;
/* The caller must route this controller's normal and DIP IRQs to HAL. */
ret = hal_dma_open(&dma_handle, &dma_config);
if (ret != IM_OK) {
return ret;
}
ret = hal_dma_callback_register(&dma_handle,
HAL_DMA_CALLBACK_TRANSFER_COMPLETE,
dma_done_callback);
if (ret != IM_OK) {
(void)hal_dma_close(&dma_handle);
return ret;
}
ret = hal_dma_callback_register(&dma_handle,
HAL_DMA_CALLBACK_ERROR,
dma_done_callback);
if (ret != IM_OK) {
(void)hal_dma_close(&dma_handle);
return ret;
}
dma_done = false;
ret = hal_dma_start(&dma_handle, src, dst, len);
if (ret != IM_OK) {
(void)hal_dma_close(&dma_handle);
return ret;
}
while (!dma_done) {
}
return hal_dma_close(&dma_handle);
}补充说明:
如果源/目的 buffer 经过 D-cache,调用方需要在 DMA 读源 buffer 前 flush,在 DMA 写目的 buffer 后 invalidate。
descriptorbuffer 由调用方提供,HAL 负责填写 descriptor 并 flush descriptor cache。HAL_DMA_DESCRIPTOR_ALIGN是 descriptor buffer 的最低对齐要求;linked/cyclic 还要求本次使用的 descriptor 数组不跨 4 KiB DOA 编码窗口。linked/cyclic 场景建议为 descriptor buffer 使用 4 KiB 对齐,避免跨 4 KiB DOA 窗口。
示例省略了超时处理,正式代码应结合业务场景增加等待超时。
5.2 内存到内存非 descriptor 传输
static volatile bool dma_done;
static void dma_done_callback(hal_dma_handle_t *handle, void *user_ctx)
{
(void)handle;
(void)user_ctx;
dma_done = true;
}
error_code_t run_memcpy_dma_nondescriptor(void *src, void *dst, uint32_t len)
{
hal_dma_handle_t dma_handle;
hal_dma_config_t dma_config = {
.request = HAL_DMA_REQUEST_MEM,
.bus_width = HAL_DMA_BUS_WIDTH_32BIT,
.unit_size = 4U,
.descriptor = NULL,
.descriptor_capacity = 0U,
};
error_code_t ret;
/* The caller must route this controller's normal and DIP IRQs to HAL. */
ret = hal_dma_open(&dma_handle, &dma_config);
if (ret != IM_OK) {
return ret;
}
ret = hal_dma_callback_register(&dma_handle,
HAL_DMA_CALLBACK_TRANSFER_COMPLETE,
dma_done_callback);
if (ret != IM_OK) {
(void)hal_dma_close(&dma_handle);
return ret;
}
ret = hal_dma_callback_register(&dma_handle,
HAL_DMA_CALLBACK_ERROR,
dma_done_callback);
if (ret != IM_OK) {
(void)hal_dma_close(&dma_handle);
return ret;
}
dma_done = false;
ret = hal_dma_start_nondescriptor(&dma_handle, src, dst, len);
if (ret != IM_OK) {
(void)hal_dma_close(&dma_handle);
return ret;
}
while (!dma_done) {
}
return hal_dma_close(&dma_handle);
}补充说明:
非 descriptor 模式不需要 descriptor buffer,
descriptor应为NULL,descriptor_capacity应为 0。如果源/目的 buffer 经过 D-cache,调用方仍需要在 DMA 读源 buffer 前 flush,在 DMA 写目的 buffer 后 invalidate。
当前非 descriptor 模式暂不支持 CAN RX 请求源。
5.3 Linked 内存压力传输
#define LINKED_DMA_DESC_COUNT 16U
#define LINKED_DMA_STRESS_SIZE 4096U
static uint8_t linked_dma_desc[HAL_DMA_DESCRIPTOR_BUFFER_SIZE(LINKED_DMA_DESC_COUNT)]
__attribute__((aligned(4096)));
hal_dma_config_t dma_config = {
.request = HAL_DMA_REQUEST_MEM,
.bus_width = HAL_DMA_BUS_WIDTH_32BIT,
.unit_size = 4U,
.descriptor = linked_dma_desc,
.descriptor_capacity = LINKED_DMA_DESC_COUNT,
};
ret = hal_dma_open(&dma_handle, &dma_config);
if (ret != IM_OK) {
return ret;
}
ret = hal_dma_callback_register(&dma_handle,
HAL_DMA_CALLBACK_TRANSFER_COMPLETE,
dma_done_callback);
if (ret != IM_OK) {
(void)hal_dma_close(&dma_handle);
return ret;
}
ret = hal_dma_start_linked(&dma_handle,
src_buffer,
dst_buffer,
LINKED_DMA_STRESS_SIZE,
LINKED_DMA_DESC_COUNT);适用场景:
需要使用多个 descriptor 压力验证分段搬运。
需要检查 descriptor 链配置或验证 DESC_DTC_DOA 链接行为。
约束:
LINKED_DMA_STRESS_SIZE必须能被LINKED_DMA_DESC_COUNT整除。LINKED_DMA_DESC_COUNT当前最大为 128。
5.4 Cyclic 压力传输并在 descriptor 边界停止
#define CYCLIC_DMA_DESC_COUNT 16U
#define CYCLIC_DMA_STRESS_SIZE 4096U
ret = hal_dma_start_cyclic(&dma_handle,
src_buffer,
dst_buffer,
CYCLIC_DMA_STRESS_SIZE,
CYCLIC_DMA_DESC_COUNT);
if (ret != IM_OK) {
return ret;
}
ret = hal_dma_stop_after_current_transfer(&dma_handle,
&src_buffer[CYCLIC_DMA_STRESS_SIZE -
(CYCLIC_DMA_STRESS_SIZE / CYCLIC_DMA_DESC_COUNT)]);适用场景:
使用多个 descriptor 压力验证环形缓冲区。
源地址递增的持续传输,希望在 descriptor 边界停止。
说明:
stop_src_addr必须是当前源侧地址范围内的地址。固定源地址的外设 RX 不支持通过本接口按 descriptor 边界停止;应使用
hal_dma_stop()。
6. 推荐关注的错误点
| 场景 | 建议检查项 |
|---|---|
hal_dma_open() 返回 IM_ERR_INVALID_PARAM | 检查 request 是否为合法请求源,bus_width、unit_size、descriptor buffer 指针、数量和对齐是否满足约束。 |
hal_dma_start*() 返回 IM_ERR_BUSY | 检查是否无空闲通道,或同一个句柄仍处于 BUSY。 |
hal_dma_start_nondescriptor() 返回 IM_ERR_UNSUPPORTED | 检查请求源是否为当前暂不支持的 CAN RX。 |
| descriptor 模式启动失败 | 检查 descriptor buffer 指针、数量和对齐是否满足约束。 |
| linked/cyclic 启动失败 | 检查 count、len % count、单段长度、descriptor buffer 容量和 4 KiB DOA 窗口。 |
| cyclic 无法停止 | 检查传入 hal_dma_stop_after_current_transfer() 的地址是否为源侧地址,且落在当前源地址范围内;固定源地址的外设 RX 应使用 hal_dma_stop()。 |
| 数据不一致 | 检查源 buffer flush、目的 buffer invalidate、地址方向、外设 FIFO 地址和 bus_width/unit_size。 |
dump 返回 IM_ERR_UNSUPPORTED | 检查是否同时启用 CONFIG_SDK_ENABLE_DEBUG_HELPERS 和 CONFIG_DMA_ENABLE_DEBUG_HELPERS。 |
当前地址读取返回 IM_ERR_HARDWARE | 硬件 DSA/DTA 在有限次重试内未落入 HAL 记录的地址范围;检查地址方向、DMA 状态和寄存器值。 |