当前文档版本为 v0.5.0,您可以访问当前页面的 开发中 版本以获取最近可能的更新。

Driver: DMA M2M

1 功能概述

dma_m2m sampleMemory to Memory 传输为例,演示了 Zephyr DMA Driver 在 PAN1080 SoC 上的使用方法。

2 环境准备

  • PAN1080 EVB 一块

  • USB-TypeC 线一条(用于供电和查看串口打印 Log)

  • 硬件接线:

    • 使用USB线,将 PC USB 与 EVB USB-TypeC(USB->UART)相连

    • 使用杜邦线将 EVB 上的:

      • TX0 与 P00 相连

      • RX0 与 P01 相连

  • PC 软件: 串口调试助手(UartAssist)或终端工具(SecureCRT),波特率921600

3 编译和烧录

例程位置:zephyr\samples_panchip\drivers\dma_m2m

使用 ZAL 工具可以对其进行编译、烧录、打开 VS Code 调试等操作。关于 ZAL 工具的详细介绍请参考:Zephyr APP Launcher 工具介绍

4 演示说明

本例程中,进行了2次 DMA Block 传输:

  1. 首先从 Flash Address 1 中读取一个大数组,将其通过 DMA 的方式搬移的 SRAM 上,并确认搬移成功;

  2. 然后使用 dma_reload() 接口,重新配置源地址为 Flash Address 2,并将此地址对应的大数组通过 DMA 的方式搬移到同一块 SRAM 上,再确认是否搬移成功。

本例程涉及如下的 DMA Driver APIs:

  • dma_request_channel()

  • dma_release_channel()

  • dma_config()

  • dma_start()

  • dma_reload()

  • dma_get_status()

例程执行成功的 Log 如下所示:

*** Booting Zephyr OS build zephyr-v2.7.0-481-ge520fced9ef8  ***
[00:00:00.000,000] <inf> dma_dw: Device DMA_0 initialized
[00:00:00.000,000] <inf> app: DMA M2M (Memory to Memory) demo start..
[00:00:00.001,000] <inf> app: Configure and start the 1st DMA transfer procedure, chan_id=0
[00:00:00.001,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.001,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.001,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.001,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.001,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.001,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.001,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.001,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.002,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.002,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.002,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.002,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.002,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.002,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.002,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.002,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.003,000] <inf> app: dma_transfer_cb: DMA transfer done
[00:00:00.003,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.003,000] <inf> app: DMA status: direction=0, busy=0
[00:00:00.009,000] <inf> app: Reload and start the 2nd DMA transfer procedure, chan_id=0
[00:00:00.009,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.009,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.009,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.009,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.009,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.009,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.009,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.010,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.010,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.010,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.010,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.010,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.010,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.010,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.010,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.011,000] <inf> app: dma_transfer_cb: DMA transfer done
[00:00:00.011,000] <inf> app: DMA status: direction=0, busy=1
[00:00:00.011,000] <inf> app: DMA status: direction=0, busy=0
[00:00:00.017,000] <inf> app: DMA Data transfer OK, and channel 0 is now ready again for later use.
[00:00:00.017,000] <inf> app: DMA M2M (Memory to Memory) demo end.

5 开发者说明

5.1 PAN1080 DMA Controller 硬件特性介绍

  1. PAN1080 SoC 内置一个 DMA Controller (DMAC),其具有 3 个可用的通道(通道编号为 0 ~ 2),并支持通道优先级配置;

  2. PAN1080 DMAC 仅支持单 Block 传输,一个 Block 最大可传输 4095 个 Item,每个 Item 最大支持 4 字节数据(即 32-Bits,与系统总线宽度一致),换句话说,在一次典型的 DMA Memory-to-Memory 传输过程中,最大可以搬移 4095 * 4 = 16380 字节的数据(Peripheral-to-Memory 传输方式达不到此值);

    :PAN1080 DMAC 不支持 Chaining Multi-Block 传输和 Scatter/Gather 传输

  3. PAN1080 DMAC 支持 Memory-to-Memory、Memory-to-Peripheral、Peripheral-to-Memory、Peripheral-to-Peripheral 等四种传输方式;

  4. PAN1080 DMAC 支持寄存器配置数据传输源地址和目的地址,并支持地址递增、递减或不变;

  5. PAN1080 DMAC 每个通道均有一个深度为 4、宽度为 32-bit 的内部 FIFO,其内部逻辑支持自动包装或拆解数据以适应 FIFO 位宽;

  6. PAN1080 DMAC 支持硬件握手和软件握手接口,并支持 single transaction 和 burst transaction;

  7. PAN1080 DMAC 支持流控,但其只支持 DMA 作为流控,不支持 Peripheral 作为流控;

  8. PAN1080 DMAC 支持组合或分开的中断请求,支持中断使能与屏蔽,可在以下事件触发时产生中断:DMA Transfer 结束、Block Transfer 结束、Single/Burst Transaction 结束、传输出错;

5.2 Zephyr DMA Driver 配置项详解

Zephyr DMA Driver API (zephyr/include/drivers/dma.h) 中,有两个重要的结构体(dma_configdma_block_config)用于 DMA 配置,其为了做到通用,提供了非常多的配置参数,而对于 PAN1080 DMA 来说,只需用到其中的一部分即可。

struct dma_configstruct dma_block_configdma.h 中的定义如下:

/**
 * @struct dma_block_config
 * @brief DMA block configuration structure.
 *
 * @param source_address is block starting address at source
 * @param source_gather_interval is the address adjustment at gather boundary
 * @param dest_address is block starting address at destination
 * @param dest_scatter_interval is the address adjustment at scatter boundary
 * @param dest_scatter_count is the continuous transfer count between scatter
 *                    boundaries
 * @param source_gather_count is the continuous transfer count between gather
 *                     boundaries
 *
 * @param block_size is the number of bytes to be transferred for this block.
 *
 * @param config is a bit field with the following parts:
 *
 *     source_gather_en   [ 0 ]       - 0-disable, 1-enable.
 *     dest_scatter_en    [ 1 ]       - 0-disable, 1-enable.
 *     source_addr_adj    [ 2 : 3 ]   - 00-increment, 01-decrement,
 *                                      10-no change.
 *     dest_addr_adj      [ 4 : 5 ]   - 00-increment, 01-decrement,
 *                                      10-no change.
 *     source_reload_en   [ 6 ]       - reload source address at the end of
 *                                      block transfer
 *                                      0-disable, 1-enable.
 *     dest_reload_en     [ 7 ]       - reload destination address at the end
 *                                      of block transfer
 *                                      0-disable, 1-enable.
 *     fifo_mode_control  [ 8 : 11 ]  - How full  of the fifo before transfer
 *                                      start. HW specific.
 *     flow_control_mode  [ 12 ]      - 0-source request served upon data
 *                                        availability.
 *                                      1-source request postponed until
 *                                        destination request happens.
 *     reserved           [ 13 : 15 ]
 */
struct dma_block_config {
#ifdef CONFIG_DMA_64BIT     // PAN1080地址总线为32位,因此CONFIG_DMA_64BIT宏不会被定义
	uint64_t source_address;
	uint64_t dest_address;
#else
	uint32_t source_address;                // * 必须配置
	uint32_t dest_address;                  // * 必须配置
#endif
	uint32_t source_gather_interval;        // 不用配置(硬件不支持)
	uint32_t dest_scatter_interval;         // 不用配置(硬件不支持)
	uint16_t dest_scatter_count;            // 不用配置(硬件不支持)
	uint16_t source_gather_count;           // 不用配置(硬件不支持)
	uint32_t block_size;                    // * 必须配置
	struct dma_block_config *next_block;    // * 必须配置(固定为0)
	uint16_t  source_gather_en :  1;        // 不用配置(硬件不支持)
	uint16_t  dest_scatter_en :   1;        // 不用配置(硬件不支持)
	uint16_t  source_addr_adj :   2;        // 不用配置(硬件支持,但用户无需配置)
	uint16_t  dest_addr_adj :     2;        // 不用配置(硬件支持,但用户无需配置)
	uint16_t  source_reload_en :  1;        // 不用配置(硬件支持,但Driver未实现)
	uint16_t  dest_reload_en :    1;        // 不用配置(硬件支持,但Driver未实现)
	uint16_t  fifo_mode_control : 4;        // 不用配置(硬件支持,但用户无需配置)
	uint16_t  flow_control_mode : 1;        // 不用配置(硬件不支持)
	uint16_t  reserved :          3;
};

/**
 * @struct dma_config
 * @brief DMA configuration structure.
 *
 * @param dma_slot             [ 0 : 6 ]   - which peripheral and direction
 *                                           (HW specific)
 * @param channel_direction    [ 7 : 9 ]   - 000-memory to memory,
 *                                           001-memory to peripheral,
 *                                           010-peripheral to memory,
 *                                           011-peripheral to peripheral,
 *                                           ...
 * @param complete_callback_en [ 10 ]      - 0-callback invoked at completion only
 *                                           1-callback invoked at completion of
 *                                             each block
 * @param error_callback_en    [ 11 ]      - 0-error callback enabled
 *                                           1-error callback disabled
 * @param source_handshake     [ 12 ]      - 0-HW, 1-SW
 * @param dest_handshake       [ 13 ]      - 0-HW, 1-SW
 * @param channel_priority     [ 14 : 17 ] - DMA channel priority
 * @param source_chaining_en   [ 18 ]      - enable/disable source block chaining
 *                                           0-disable, 1-enable
 * @param dest_chaining_en     [ 19 ]      - enable/disable destination block
 *                                           chaining.
 *                                           0-disable, 1-enable
 * @param linked_channel       [ 20 : 26 ] - after channel count exhaust will
 *                                           initiate a channel service request
 *                                           at this channel
 * @param reserved             [ 27 : 31 ]
 * @param source_data_size     [ 0 : 15 ]  - width of source data (in bytes)
 * @param dest_data_size       [ 16 : 31 ] - width of dest data (in bytes)
 * @param source_burst_length  [ 0 : 15 ]  - number of source data units
 * @param dest_burst_length    [ 16 : 31 ] - number of destination data units
 * @param block_count  is the number of blocks used for block chaining, this
 *     depends on availability of the DMA controller.
 * @param source_peripheral_id  is the ID of a hardware peripheral which
 *     acts as DMA source of current channel, note this is only valid when
 *     the source_handshake bit is 0 (means HW handshake).
 * @param dest_peripheral_id  is the ID of a hardware peripheral which
 *     acts as DMA destination of current channel, note this is only valid when
 *     the dest_handshake bit is 0 (means HW handshake).
 * @param user_data  private data from DMA client.
 * @param dma_callback see dma_callback_t for details
 */
struct dma_config {
	uint32_t  dma_slot :             7;     // 不用配置(Driver不支持)
	uint32_t  channel_direction :    3;     // * 必须配置
	uint32_t  complete_callback_en : 1;     // # 可选配置(根据实际需要决定是否配置)
	uint32_t  error_callback_en :    1;     // 不用配置(Driver不支持)
	uint32_t  source_handshake :     1;     // 不用配置(硬件支持,但用户无需配置)
	uint32_t  dest_handshake :       1;     // 不用配置(硬件支持,但用户无需配置)
	uint32_t  channel_priority :     4;     // # 可选配置(根据实际需要决定是否配置)
	uint32_t  source_chaining_en :   1;     // 不用配置(硬件不支持)
	uint32_t  dest_chaining_en :     1;     // 不用配置(硬件不支持)
	uint32_t  linked_channel   :     7;     // 不用配置(硬件不支持)
	uint32_t  reserved :             5;
	uint32_t  source_data_size :    16;     // * 必须配置
	uint32_t  dest_data_size :      16;     // * 必须配置
	uint32_t  source_burst_length : 16;     // * 必须配置
	uint32_t  dest_burst_length :   16;     // * 必须配置
	uint32_t block_count;                   // * 必须配置(固定为1)
	uint16_t source_peripheral_id;          // # 可选配置(根据实际需要决定是否配置)
	uint16_t dest_peripheral_id;            // # 可选配置(根据实际需要决定是否配置)
	struct dma_block_config *head_block;    // * 必须配置
	void *user_data;                        // # 可选配置(根据实际需要决定是否配置)
	dma_callback_t dma_callback;            // # 可选配置(根据实际需要决定是否配置)
};

上述两个结构体定义中的参数,在 DMA 配置过程中关注以下原则即可:

  1. 标记为 必须配置 的参数,是必须正确填写的,否则 DMA 将无法正常工作;

  2. 标记为 可选配置 的参数,需要根据实际需求决定是否需要进行配置;

  3. 标记为 不用配置 的参数,无需关注之;

下面两个表格中详细列出了 struct dma_configstruct dma_block_config 中各个参数的详细含义及 PAN1080 DMAC 的使用说明:

dma_block_config 结构体参数详解

参数名称

重要程度

参数含义

PAN1080 DMA 说明

source_address

必须配置

DMA 源端地址

需要注意:

  • 若 DMA 源端是 Memory,则应填待传输 Memory 的起始地址,注意此地址有对齐要求,需能被 source_data_size 整除

  • 若 DMA 源端是 Peripheral,则应填此外设 Data FIFO 的寄存器地址,各寄存器定义详见: include\drivers\dma\dma_panchip.h

dest_address

必须配置

DMA 目的端地址

需要注意:

  • 若 DMA 目的端是 Memory,则应填待传输 Memory 的起始地址,注意此地址有对齐要求,需能被 dest_data_size 整除

  • 若 DMA 目的端是 Peripheral,则应填此外设 Data FIFO 的寄存器地址,各寄存器定义详见: include\drivers\dma\dma_panchip.h

source_gather_interval

不用配置

DMA Gather 源端地址到达边界后的调整值

PAN1080 DMAC 不支持 Gather 特性,此参数无意义

dest_scatter_interval

不用配置

DMA Scatter 目的端地址到达边界后的调整值

PAN1080 DMAC 不支持 Scatter 特性,此参数无意义

dest_scatter_count

不用配置

DMA Scatter 目的端地址调整次数

PAN1080 DMAC 不支持 Scatter 特性,此参数无意义

source_gather_count

不用配置

DMA Gather 源端地址调整次数

PAN1080 DMAC 不支持 Gather 特性,此参数无意义

block_size

必须配置

当前 DMA Block 预期传输的数据长度(字节)

需要注意此处待传输的数据长度必须同时满足以下几个条件:

  • block_sizesource_data_size 的整数倍

  • block_sizedest_data_size 的整数倍

  • block_size / dest_data_size 不可超过 4095

next_block

必须配置

指向下一个 DMA Block 的配置参数

PAN1080 DMAC 不支持 Chaining Multi-Block 特性,所以此参数必须配置为0

source_gather_en

不用配置

DMA Source Gather 使能控制

PAN1080 DMAC 不支持 Gather 特性,此参数无意义

dest_scatter_en

不用配置

DMA Destination Scatter 使能控制

PAN1080 DMAC 不支持 Scatter 特性,此参数无意义

source_addr_adj

不用配置

DMA 源端地址步进规则(递增、递减、或不变)

PAN1080 DMAC 支持此特性,但用户无需在此处配置,原因是 DMA Driver 会根据实际传输类型,自动按如下规则配置步进:

  • 若源端为 Memory,则 Source Address 步进会被配置为递增

  • 若源端为 Peripheral,则 Source Address 步进会被配置为不变

dest_addr_adj

不用配置

DMA 目的端地址步进规则(递增、递减、或不变)

PAN1080 DMAC 支持此特性,但用户无需在此处配置,原因是 DMA Driver 会根据实际传输类型,自动按如下规则配置步进:

  • 若目的端为 Memory,则 Destination Address 步进会被配置为递增

  • 若目的端为 Peripheral,则 Destination Address 步进会被配置为不变

source_reload_en

不用配置

DMA 源端地址自动重新加载使能控制

PAN1080 DMAC 支持此特性,但 PAN1080 DMA Driver 未实现此特性,因此无需配置

dest_reload_en

不用配置

DMA 目的端地址自动重新加载使能控制

PAN1080 DMAC 支持此特性,但 PAN1080 DMA Driver 未实现此特性,因此无需配置

fifo_mode_control

不用配置

决定 DMA 执行一个 Burst Transaction 之前,DMA FIFO 中需要有多少空间或数据

PAN1080 DMAC 支持此特性,但用户无需在此处配置,原因是 DMA Driver 强制将此特性配置为“只要 FIFO 空间或数据足够一次 Single Transfer,就可以传输”

flow_control_mode

不用配置

决定目的端 Peripheral 作为流控时,源外设如何给数据

PAN1080 DMAC 不支持 Peripheral 作为流控,自然也不支持此特性,此参数无意义

dma_config 结构体参数详解

参数名称

重要程度

参数含义

PAN1080 DMA 说明

dma_slot

不用配置

DMA Peripheral 传输方式下的外设 ID

PAN1080 DMAC 支持此特性,但 DMA Driver 使用另外两个单独的参数来分别指示源端和目的端外设 ID:

  • source_peripheral_id

  • dest_peripheral_id

channel_direction

必须配置

DMA 传输类型

PAN1080 DMAC 支持以下 4 种传输类型:

  • MEMORY_TO_MEMORY

  • MEMORY_TO_PERIPHERAL

  • PERIPHERAL_TO_MEMORY

  • PERIPHERAL_TO_PERIPHERAL

complete_callback_en

可选配置

决定 dma_callback 函数是在每个 Block 传输完成后触发,还是所有数据传输完成后触发

PAN1080 DMA Driver 支持此特性,但由于 PAN1080 DMAC 硬件仅支持单个 Block 传输,因此在概念上,“每个 Block 传输完成”与“所有数据传输完成”是相同的,在实际应用中,建议直接将此参数配置为0即可

error_callback_en

不用配置

DMA 传输出错的 Callback 函数使能控制

PAN1080 DMAC 支持Error中断,但 PAN1080 DMA Driver 未实现此特性,因此无需配置

source_handshake

不用配置

决定 DMA 源端握手方式为硬件握手还是软件握手

PAN1080 DMAC 支持此特性,但用户无需在此处配置,原因是 DMA Driver 强制将此特性配置为“硬件握手”

dest_handshake

不用配置

决定 DMA 目的端握手方式为硬件握手还是软件握手

PAN1080 DMAC 支持此特性,但用户无需在此处配置,原因是 DMA Driver 强制将此特性配置为“硬件握手”

channel_priority

可选配置

当前 DMA 通道的优先级

PAN1080 DMAC 支持此特性,3个通道的优先级可以独立配置,优先级可选范围为 0 ~ 3,其中数字越大表示优先级越高,在实际应用中,如果没有特殊需求,建议直接将此参数配置为0即可

source_chaining_en

不用配置

DMA 源端 Block-Chaining (Multi-Block) 特性使能控制

PAN1080 DMAC 硬件不支持,此参数无意义

dest_chaining_en

不用配置

DMA 目的端 Block-Chaining (Multi-Block) 特性使能控制

PAN1080 DMAC 硬件不支持,此参数无意义

linked_channel

不用配置

DMA 衔接通道

PAN1080 DMAC 硬件不支持,此参数无意义

source_data_size

必须配置

DMA 源端数据宽度(字节)

PAN1080 DMAC 支持的源端数据宽度有 1、2、4 字节:

  • 若 DMA 源端是 Memory,则此参数可以任意配置为上述 3 个可选参数之一

  • 若 DMA 源端是 Peripheral,则此参数应与对应外设传输的 Data 宽度保持一致

dest_data_size

必须配置

DMA 目的端数据宽度(字节)

PAN1080 DMAC 支持的目的端数据宽度有 1、2、4 字节:

  • 若 DMA 目的端是 Memory,则此参数可以任意配置为上述 3 个可选参数之一

  • 若 DMA 目的端是 Peripheral,则此参数应与对应外设传输的 Data 宽度保持一致

source_burst_length

必须配置

DMA 源端 Burst 长度

PAN1080 DMAC 支持的源端 Burst 长度有 1、4、8 字节:

  • 若 DMA 源端是 Memory,则此参数可以任意配置为上述 3 个可选参数之一

  • 若 DMA 源端是 Peripheral,则此参数不应超过对应外设的 FIFO 深度大小

dest_burst_length

必须配置

DMA 目的端 Burst 长度

PAN1080 DMAC 支持的目的端 Burst 长度有 1、4、8 字节:

  • 若 DMA 目的端是 Memory,则此参数可以任意配置为上述 3 个可选参数之一

  • 若 DMA 目的端是 Peripheral,则此参数不应超过对应外设的 FIFO 深度大小

block_count

必须配置

DMA 待传输的 Block 数量

PAN1080 DMAC 不支持 Chaining Multi-Block 特性,所以此参数必须配置为1

source_peripheral_id

可选配置

DMA 源端 Peripheral ID

当 DMA 源端被配置为 Peripheral 时(即 Peripheral-to-Memory 或 Peripheral-to-Peripheral 方式),此处用于指定对应外设的 Peripheral ID,PAN1080 所有支持 DMA 功能的外设 Periphal ID 定义详见: include\drivers\dma\dma_panchip.h

dest_peripheral_id

可选配置

DMA 目的端 Peripheral ID

当 DMA 目的端被配置为 Peripheral 时(即 Memory-to-Peripheral 或 Peripheral-to-Peripheral 方式),此处用于指定对应外设的 Peripheral ID,PAN1080 所有支持 DMA 功能的外设 Periphal ID 定义详见: include\drivers\dma\dma_panchip.h

head_block

必须配置

指向第一个 DMA Block 的配置参数

需要先定义一个 struct dma_block_config 类型的变量用于存放 Block 配置参数,然后将其指针赋给此 head_block 参数

user_data

可选配置

DMA Callback 的用户数据,

此参数会作为函数参数传进 dma_callback 指向的函数中,作为用户数据使用,在实际应用中,如果没有特殊需求,建议直接将此参数配置为0即可

dma_callback

可选配置

DMA Callback 函数的指针,

当 DMA 传输完成(或 Block 传输完成)后,会自动触发此参数指向的函数,在实际应用中,建议实现callback函数,以方便确认DMA传输成功

6 RAM/Flash资源使用情况

Memory region         Used Size  Region Size  %age Used
FLASH:       62068 B      1020 KB      5.94%
SRAM:       26936 B        64 KB     41.10%
IDT_LIST:          0 GB         2 KB      0.00%