IC:

概述

全局直接内存访问(GDMA)控制器,也称DMAC,主要用于通过AXI/OCP总线在内存与外设之间传输数据,无需CPU介入,从而减轻CPU的负担。

该GDMA控制器包含8个通道:

  • 通道0和通道1配备128字节的FIFO缓冲区

  • 其余通道则使用32字节的FIFO缓冲区

GDMA采用双AXI/OCP主总线架构,并提供一个从接口用于配置和编程。它支持基于硬件的优先级仲裁和可编程优先级,以管理并发的DMA请求。

GDMA 性能

GDMA 的数据传输效率受多种因素影响,包括时钟同步、通道 FIFO 深度、传输类型、握手效率和 GDMA 从机接口配置等。

以下数据基于通道0单个数据块传输模式的实验结果得出,未包含 GDMA 周转时间。

Slave

时钟 (Hz)

写 64 字节

读 64 字节

SRAM

250M

(64*8)/(280ns)= 1828.57Mbps

(64*8)/(240ns)= 2133.33Mbps

PSRAM

250M

(64*8)/(350ns)= 1462.86Mbps

(64*8)/(360ns)= 1422.22Mbps

Audio

40M

(64*8)/(1050ns)= 487.62Mbps

(64*8)/(470ns)=1089.36Mbps

SPI

100M

(64*8)/(710ns)= 721.13Mbps

(64*8)/(670ns)= 764.18Mbps

GDMA 配置

DMA 块大小的框图如下所示:

../../_images/dma_block_size_diagram.svg

数据大小

上图阐述了GDMA传输数据大小的设置。block_ts 表示将在单个数据块中传输的数据量,需要设置为 总数据量/SRC_TR_WIDTH,最大值是 0xFFFF。

传输方向和流控制器

目前共有四种传输方向和两种流控制器设置,共八种可用配置。

  • 当外设作为流控制器时,DMA将根据外设发出的单次/突发请求进行数据传输。

  • 当 DMAC 作为流控制器时,所有来自外设的请求都将按照配置的请求进行处理。

CTLx 寄存器的 TT_FC[2:0] 字段(x 为通道号)

传输方向

流控制器

000

内存到内存

DMAC

001

内存到外设 ········

DMAC

010

外设到内存 ········

DMAC

011

外设到外设··············

DMAC

100

外设到内存 ········

外设

101

外设到外设 ··············

源外设 ···········

110

内存到外设 ······· ·

外设 ······

111

外设到外设 ··············

目标外设

备注

仅当 DMAC 用作流控制器时,才能设置 block_ts 参数。

传输 msize

每次传输的长度可配置:

  • msize > 1:突发传输

  • msize = 1:单次传输

CTLx 寄存器的 SRC_MSIZE[2:0]/DEST_MSIZE[2:0] 字段

传输 msize

000

1

001

4

010

8

011

16

100 及以上

不支持

传输宽度

GDMA 支持以下传输宽度:

CTLx 寄存器的 SRC_TR_WIDTH[2:0]/DST_TR_WIDTH[2:0] 字段

传输宽度(字节)

000

1

001

2

010

4

011 及以上

不支持

备注

  • 读写外设时: SRC_TR_WIDTH/DST_TR_WIDTH 完全由外设的数据宽度决定,不可手动配置。

  • 读写内存时:

    • 如果缓存被禁用,内存地址无需对齐,但需确保总数据量能被 SRC_TR_WIDTH 整除,以保证 block_ts 为整数。

    • 如果缓存被启用,内存地址必须满足缓冲区边界对齐,且需按缓存行对齐。

  • 目标为内存时(如P2M、M2M模式): DST_TR_WIDTH 参数将被忽略,实际写入操作始终基于总线宽度(默认为32位,即4字节)。

传输类型

单块

单块 DMA 传输 – 包含单个数据块

多块

多块 DMA 传输 – 可能包含多个 RTK_DMAC 块。其传输类型包括:

  • 自动重加载(Auto-reloading)模式

  • Linked list 模式

Auto-reloading 模式

在 auto-reloading 模式下,源和目标可分别选择使用的方法。

Auto-reloading 传输类型

设置

说明

Src auto reload

PGDMA_InitTypeDef->GDMA_ReloadSrc = 1

PGDMA_InitTypeDef->GDMA_ReloadDst = 0

For multi-block transfers, the SAR register can be auto-reloaded from the initial value at the end of each block,

and DST address is contiguous, as shown in Multi-block DMA transfer with source address auto-reloaded and contiguous destination address.

Dst auto reload

PGDMA_InitTypeDef->GDMA_ReloadSrc = 0

PGDMA_InitTypeDef->GDMA_ReloadDst = 1

For multi-block transfers, the DAR register can be auto-reloaded from its initial value at

the end of each block, and the SRC address is contiguous.

Src & Dst auto reload

PGDMA_InitTypeDef->GDMA_ReloadSrc = 1

PGDMA_InitTypeDef->GDMA_ReloadDst = 1

For multi-block transfers, the SAR and DAR register can be auto-reloaded from its initial value at the end of each

block, as shown in Multi-block DMA transfer with source and destination address auto-reloaded.

../../_images/mbd_source_auto_dest_cont.png

Multi-block DMA transfer with source address auto-reloaded and contiguous destination address

../../_images/mbd_source_dest_auto.png

Multi-block DMA transfer with source and destination address auto-reloaded

Linked list 模式

在 linked list 模式下,数据块之间的地址无需连续。

Link list 传输类型

设置

说明

Src: Continue address

Dst: Link list

PGDMA_InitTypeDef->GDMA_SrcAddr = pSrc

PGDMA_InitTypeDef->GDMA_LlpDstEn = 1

Source memory is a continuous data block, while destination data blocks are organized in linked list.

Src: Auto-reloading

Dst: Link list

PGDMA_InitTypeDef->GDMA_ReloadSrc = 1

PGDMA_InitTypeDef->GDMA_SrcAddr = pSrc

PGDMA_InitTypeDef->GDMA_LlpDstEn = 1

In source, SAR register can be auto-reloaded from the initial value at the end of each

block, as shown in Multi-block DMA transfer with source address auto-reloaded and linked list destination address.

Src: Link list

Dst: Continue address

PGDMA_InitTypeDef->GDMA_LlpSrcEn = 1

PGDMA_InitTypeDef->GDMA_DstAddr = pDst

Source memory is organized in the form of a linked list, and destination memory is

a continuous data block, as shown in Multi-block DMA transfer with linked list source address and contiguous destination address.

Src: Link list

Dst: Auto-reloading

PGDMA_InitTypeDef->GDMA_LlpSrcEn = 1

PGDMA_InitTypeDef->GDMA_DstAddr = pDst

PGDMA_InitTypeDef->GDMA_ReloadDst = 1

The source data blocks are organized in a linked list, and the destination data blocks are auto-reloading.

Src: Link list

Dst: Link list

PGDMA_InitTypeDef->GDMA_LlpSrcEn = 1

PGDMA_InitTypeDef->GDMA_LlpDstEn = 1

Both source and destination data blocks are organized in linked lists,

as shown in Multi-block DMA transfer with linked address for source and destination.

If both the destination and the source are continuous data blocks, multi-block transmission should not be used, and single-block transmission is more appropriate.

地址递增类型

源地址递增

包含两种模式:

  • Increment: Indicates whether to increment the source address on every source transfer. Incrementing is done for alignment to the next CTLx.SRC_TR_WIDTH boundary.

  • No change: If the device is fetching data from a source peripheral FIFO with a fixed address, then set this field to No change.

目标地址递增

包含两种模式:

  • Increment: indicates whether to increment destination address on every destination transfer. Incrementing is done for alignment to the next CTLx.DST_TR_WIDTH boundary.

  • No change: If the device is writing data to a destination peripheral FIFO with a fixed address, then set this field to No change.

实时状态获取

GDMA支持实时获取当前传输的源地址、目标地址和已传输数据量。

需调用对应API读取。

备注

若要获取已传输数据量,block_ts 必须至少大于 768,且不可在中断函数中读取;否则,获取的值始终为 0。

中断类型

DMAC 支持多种中断类型,这些中断可独立使用或组合使用。

中断类型

说明

块中断

单个数据块传输完成触发

传输中断

所有数据块传输完毕时产生

错误中断

传输过程中发生错误

备注

  • 多块传输时,若 auto-reload 模式中的块传输被中断,数据将在中断处理函数完成后继续传输。

  • inked list 模式的传输完成条件为:最后一个数据块的下一数据块指针为空。

  • linked list 模式下,即使产生块中断,数据传输仍会继续进行。

安全机制

要启用安全传输,用户需在寄存器中配置安全通道控制位:

  • 当安全位使能时,主从接口的访问均为安全访问。

  • 安全通道仅可在安全域中配置,且安全通道可访问安全内存和非安全内存。

  • 非安全通道仅能访问非安全内存。

PGDMA_InitTypeDef->SecureTransfer = 1;

挂起和终止

GDMA支持通道的挂起恢复和强制终止。

  • 挂起通道:直接配置 CFGx.CH_SUSP,但不保证当前数据传输已完成。需结合 CFGx.INACTIVE 状态检测,方可安全暂停且不丢失数据。

  • 恢复传输:清除 CFGx.CH_SUSP 即可继续进行数据传输。

  • 终止传输:需持续轮询 CFGx.INACTIVE 直至该位设为1,方可终止传输。

备注

通道进入非活跃状态的场景:

  • CFGx.INACTIVE 仅可在内存写入操作完成后被激活,且需手动取消。

  • 外设数据为 4 字节,但 DMAC FIFO 仅 2 字节。若当前无写入操作,则直接激活 CFGx.INACTIVE

优先级

DMAC 支持两种通道优先级:

  • 软件:各通道优先级可通过 CFGx.CH_PRIOR 配置,有效值范围为 0 ~ (DMAC_NUM_CHANNELS-1)。其中, DMAC_NUM_CHANNELS 为最低优先级。

  • 硬件:若两个通道请求的软件优先级相同,或未配置软件优先级,通道编号较小的通道优先级更高(例如:通道2优先级高于通道4)。

缓存

当 DMA 从设备类型为内存时,需要特别注意缓存操作:每次 DMA 传输启动前,必须调用 DCache_CleanInvalidate() 执行清理缓存。

执行 DMA Rx 与 Tx 时,需添加以下步骤:

操作

步骤

DMA Rx

  1. Prepare DST buffer

  2. Do DCache_CleanInvalidate() to avoid cache flush during DMA Rx

  3. Do DMA Rx configuration

  4. Trigger DMA Rx interrupt

  5. Do DCache_Invalidate() in Rx Done Handler to clean the old data, to avoid the problem that the data in the cache is inconsistent.

    with the data in the memory after Rx done if the CPU read or write allocate the DST buffer during GDMA transmission.

备注

During GDMA transmission, it is forbidden to write or cache flush DST buffer. (Taking

{SDK}\component\example\peripheral\raw\uart\uart_dma_stream\src\main.c for example, uart_recv_string_done is DMA Rx Done Interrupt Handler)

u32 uart_recv_string_done(void * data)
{
   UNUSED(data);
   // To solve the cache consistency problem, DMA mode needs it
   DCache_Invalidate((u32)rx_buf, SRX_BUF_SZ);
   dma_free();
   rx_done = 1;
   return 0;
}
  1. CPU reads DST buffer

DMA Tx

  1. CPU prepares SRC buffer data

  2. Do DCache_CleanInvalidate() for SRC buffer to synchronize the data

  3. Do DMA Tx configuration

  4. Trigger DMA Tx interrupt

Aligning the buffer address with the cache line will reduce the problem of inconsistent cache and memory data, and details can be referred to Section 使用DMA时的缓存一致性.

DMAC 示例

单块

  1. 分配一个空闲通道

    ch_num = GDMA_ChnlAlloc(gdma.index, (IRQ_FUN) Dma_memcpy_int, (u32)(&gdma), 3);
    

    这个函数同时包含以下操作:

    • 注册 IRQ 处理程序(如果使用中断模式)

    • 启用 NVIC 中断

    • 注册要使用的 GDMA 通道

  2. 配置中断类型

    PGDMA_InitTypeDef->GDMA_IsrType = (TransferType | ErrType);
    
  3. 配置中断处理函数

    在中断处理函数中清除挂起的中断。

    GDMA_ClearINT(0, PGDMA_InitTypeDef->GDMA_ChNum);
    
  4. 传输配置

    PGDMA_InitTypeDef->GDMA_SrcMsize   = MsizeEight;
    PGDMA_InitTypeDef->GDMA_SrcDataWidth = TrWidthFourBytes;
    PGDMA_InitTypeDef->GDMA_DstMsize = MsizeEight;
    PGDMA_InitTypeDef->GDMA_DstDataWidth = TrWidthFourBytes;
    PGDMA_InitTypeDef->GDMA_BlockSize = DMA_CPY_LEN >> 2;
    PGDMA_InitTypeDef->GDMA_DstInc = IncType; // if dst type is peripheral:no change
    PGDMA_InitTypeDef->GDMA_SrcInc = IncType; // if src type is peripheral:no change
    
  5. 配置硬件握手接口(如果从机是外设)

    GDMA_InitStruct->GDMA_SrcHandshakeInterface= GDMA_HANDSHAKE_INTERFACE_AUDIO_RX;
    

    GDMA_InitStruct->GDMA_DstHandshakeInterface = GDMA_HANDSHAKE_INTERFACE_AUDIO_TX;
    
  6. 配置传输地址

    PGDMA_InitTypeDef->GDMA_SrcAddr = (u32)BDSrcTest;
    PGDMA_InitTypeDef->GDMA_DstAddr = (u32)BDDstTest;
    
  7. 使用 GDMA_Init() 函数设置 GDMA索引、GDMA 通道、数据宽度、msize、传输方向、地址递增模式、硬件握手接口、重载控制、中断类型、块大小、多块配置,以及源地址和目标地址

    GDMA_Init(gdma.index, gdma.ch_num, PGDMA_InitTypeDef);
    
  8. 清理缓存

    DCache_CleanInvalidate();
    
  9. 启用 GDMA 通道

GDMA_Cmd(gdma.index, gdma.ch_num, ENABLE);

多块

这是 SRC 自动重载模式的示例。与单块相比,多块的 步骤 2 ~ 步骤 4 不同。

  1. 分配一个空闲通道

    ch_num = GDMA_ChnlAlloc(gdma.index, (IRQ_FUN) Dma_memcpy_int, (u32)(&gdma), 3);
    

    这个函数同时包含以下操作:

    • 注册 IRQ 处理程序(如果使用中断模式)

    • 启用 NVIC 中断

    • 注册要使用的 GDMA 通道

  2. 配置中断类型

    PGDMA_InitTypeDef->GDMA_IsrType = (BlockType | TransferType | ErrType);
    
  3. 配置中断处理函数

    1. 清除中断

      GDMA_ClearINT(0, GDMA_InitStruct->GDMA_ChNum);
      
    2. 在最后一个数据块开始之前清除自动重载模式

      GDMA_ChCleanAutoReload(0, GDMA_InitStruct->GDMA_ChNum, CLEAN_RELOAD_SRC);
      
  4. 传输配置

    PGDMA_InitTypeDef->GDMA_SrcMsize   = MsizeEight;
    PGDMA_InitTypeDef->GDMA_SrcDataWidth = TrWidthFourBytes;
    PGDMA_InitTypeDef->GDMA_DstMsize = MsizeEight;
    PGDMA_InitTypeDef->GDMA_DstDataWidth = TrWidthFourBytes;
    PGDMA_InitTypeDef->GDMA_BlockSize = DMA_CPY_LEN >> 2;
    PGDMA_InitTypeDef->GDMA_DstInc = IncType; // If DST type is peripheral: no change
    PGDMA_InitTypeDef->GDMA_SrcInc = IncType; // If SRC type is peripheral: no change
    PGDMA_InitTypeDef->GDMA_ReloadSrc = 1;
    PGDMA_InitTypeDef->GDMA_ReloadDst = 0;
    
  5. 配置硬件握手接口(如果从机是外设)

    GDMA_InitStruct->GDMA_SrcHandshakeInterface= GDMA_HANDSHAKE_INTERFACE_AUDIO_RX;
    

    GDMA_InitStruct->GDMA_DstHandshakeInterface = GDMA_HANDSHAKE_INTERFACE_AUDIO_TX;
    
  6. 配置传输地址

    PGDMA_InitTypeDef->GDMA_SrcAddr = (u32)BDSrcTest;
    PGDMA_InitTypeDef->GDMA_DstAddr = (u32)BDDstTest;
    
  7. 使用 GDMA_Init() 函数设置 GDMA索引、GDMA 通道、数据宽度、msize、传输方向、地址递增模式、硬件握手接口、重载控制、中断类型、块大小、多块配置,以及源地址和目标地址

    GDMA_Init(gdma.index, gdma.ch_num, PGDMA_InitTypeDef);
    
  8. 清理缓存

    DCache_CleanInvalidate();
    
  9. 启用 GDMA 通道

    GDMA_Cmd(gdma.index, gdma.ch_num, ENABLE);