草庐IT

DMA驱动开发---认识DMA

三境界 2023-07-17 原文

DMA定义:

DMA用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU的干预,通过DMA数据可以快速地移动。这就节省了CPU的资源来做其他操作。

DMA传输方式:

DMA的作用就是实现数据的直接传输,而去掉了传统数据传输需要CPU寄存器参与的环节,主要涉及四种情况的数据传输,但本质上是一样的,都是从内存的某一区域传输到内存的另一区域(外设的数据寄存器本质上就是内存的一个存储单元)。四种情况的数据传输如下:

  • 外设到内存
  • 内存到外设
  • 内存到内存
  • 外设到外设

DW_AHB_DMA 主要组件:

  1. DMA Hardware Requeset Interface
  2. AHB Master Interface
  3. AHB Slave Interface
  4. Arbiter(仲裁器)
  5. DMA Channel x
  6. FIFO of DMA Channel x
  7. DMA Controller

DMA的应用

DMA主适用于一些高速、大数据量的I/O设备。对于这类高速I/O设备,如果采用输入输出指令或采用中断的方式传输信息,会占用大量CPU的时间,同时也容易造成数据的丢失。DMA方式能使I/O设备直接和存储器进行成批数据的快速传输。
常见应用如下:

  • FLASH
  • AUDIO
  • VIDEO

DMA相关的基本定义

Source/Destination Peripheral

Source Peripheral :DMAC从AHB层读取数据的设备,然后DMAC将数据存储在通道FIFO中。

Source Peripheral 可以挂在AHB总线或者APB总线上。

Destination Peripheral DMAC 从FIFO中存储的数据的设备(从Source Peripheral读取的)

Destination Peripheral 可以挂在AHB总线或者APB总线上。

Memory/Non-Memory Peripheral

始终“准备就绪”进行DMA传输的Source Peripheral 或者 Destination Peripheral,不需要握手接口即可与DMA进行交互,仅当外围设备插入的等待状态不超过16个时,才应当将其定义为Memory Peripheral。反之,则应当定义为Non-Memory Peripheral并使用握手接口,以便在其准备好接收/发送数据时发出信号通知DMA。

Master/Slave Interface

Master Interface:DW_ahb_dmac是通过AHB总线上的一个master interface,DW_ahb_dmac通过AHB总线从source读取数据并写入数据到destination
Slave Interface:CPU编程DW_ahb_dmac的AHB接口。主要是作为从设备接口部分,供AHB总线上的其他主设备来访问自身控制寄存器。

Handshaking Interface

一组信号或软件寄存器,他们符合DMAC与Source Peripheral/Destination Peripheral之间的握手协议,以控制他们之间的Single/Brust 事务。

该接口用于请求、确认、控制DMAC事务。一般情况DMAC可以通过以下3种方式接收请求:

  • **Hardware handshaking interface: **使用硬件信号来控制DMAC与源或目标外围设备之间的Single/Brust 事务。
  • Software handshaking interface: 使用一组软件寄存器DMAC与源或目标外围设备之间的Single/Brust 事务。
  • Peripheral interrupt handshaking interface: 在这种模式下,来自外围设备的中断线被绑定到硬件握手接口的dma_req输入;忽略其他接口信号。

Channel

通道-在一个配置的AHB层上的源外设和同一或不同AHB层上的目标外设之间通过通道FIFO进行的读/写数据路径

如果源外设不是内存,则将源握手接口分配给通道。

如果目标外设不是内存,则将目标握手接口分配给通道。源和目标握手接口可以通过编程通道寄存器来动态分配

通道可以理解成一个小型的程序,它是一个简单的IO控制程序,只能处理对应外设,mem之间的数据传输,工作之前需要配置DMA里使用哪个通道。

每一个通道都有一个相应的FIFO,该FIFO的缓冲长度可以编程配置

DMA 传输层级结构

Non-Memory Peripherals

Memory

解释:

Block:

Block- DW_ahb_dmac数据的块,其数量即块长,由流量控制器决定。对于DW_ahb_dmac和内存之间的传输,块被直接分解为突发序列和单个传输。对于DW_ahb_dmac和非内存外设之间的传输,块被分解为DW_ahb_dmac事务序列(单个事务和突发事务)。这些又被分解成AHB传输序列。

Transaction:

事务——DW_ahb_dmac传输的基本单元,由硬件或软件握手接口决定

如果外设是非内存设备,则事务仅与DW_ahb_dmac与源外设或目标外设之间的传输相关。

有两种类型的事务:

  • 单个事务—单个事务的长度始终为1,并转换为单个AHB转移。
  • 突发事务—突发事务的长度被编程到DW_ahb_dmac中。突发事务被转换为一系列突发和AHB单传输。突发事务长度由程序控制,通常与dmac和源/目的外设中的FIFO大小有一定关系。
DMA传输方式

软件控制DMAC传输的块数量,在DMA传输完成时DW_AHB_DMAC硬件关闭通道并产生一个DMA传输完毕的中断信号,程序员可以为一个新的DMA传输重编程通道

根据传输块的分布情况可以分为两种

第一种:Single-block DMA transfer

一片连续的组合块

第二种:Multi-block DMA transfer

Multi-block DMA transfer可能由多个DW_ahb_dmac块组成。对多块DMA传输通过以下方式支持:

  • 块链(链表指针) linked list pointer
  • 自动重新加载通道寄存器
  • 连续块

源和目标可以独立地选择要使用的方法。

Scatter-gather DMA方式是与block DMA方式相对应的一种DMA方式。

在DMA传输数据的过程中,要求源物理地址和目标物理地址必须是连续的。但是在某些计算机体系中,如IA架构,连续的存储器地址在物理上不一定是连续的,所以DMA传输要分成多次完成。

如果在传输完一块物理上连续的数据后引起一次中断,然后再由主机进行下一块物理上连续的数据传输,那么这种方式就是block DMA方式。

Scatter-gather DMA方式则不同,它使用一个链表描述物理上不连续的存储空间(这个链表),然后把链表首地址告诉DMA master。DMA master在传输完一块物理连续的数据后,不用发起中断,而是根据链表来传输下一块物理上连续的数据,直到传输完毕后再发起一次中断。

感觉对于物理地址不连续离散的使用block dma和sg dma是不一样,sg dma效率更高,可是如果是连续的物理地址,感觉block dma和sg dma效率其实是差不多的。

S-G传输的两种场景:

将一片连续内存数据搬运到一片不连续的的内存空间(且间隔是相等的)

源内存:连续

目的内存:离散

这个时候就可以用到DMA的 Destination Scatter Transfer

场景2:

将一片内存区域中等间隔的多段数据拷贝到一段连续内存中(常见的2D矩形抠图就是这种场景的典型应用)

源内存:分散

目的内存:聚合

这种场景就可以使用DMA的Source Gather transfer


DMA传输相关设置

传输类型

  • Memory to Memory
  • Memory to Peripheral
  • Peripheral to Memory
  • Peripheral to Peripheral

传输控制器 — 流控制器 Flow Controller

流控制器:确定DMA块传输长度并终止它的设备

流控制器一般有以下选择:

  • DMAC
  • Source Peripheral
  • Destination Peripheral

流控制器设置规则:

如果在传输之前知道块大小,则应将DMAC设为流控。
如果在传输之前不知道块大小,则Source Peripheral/Destination Peripheral必须为流控

传输宽度

多数通用DMA是挂在AHB总线上的,即AHB总线的位宽则为DMA的传输宽度的上限。

以STM32单片机为例,DMA的传输宽度 <= 32。则常见有Byte-8bit、HalfWord-16bit、Word-32bit方式

DT53的AHB总线位宽为CPU位宽,同STM32一样都是32位,可以使用Byte-8bit、HalfWord-16bit、Word-32bit方式

传输时地址变化方式

DMA传输过程是没有CPU干预的,DMAC怎么知道下一个数据去哪里搬运,也需要事先告诉DMA地址的变化规律。

即三种地址方式:

  1. 递增
  2. 递减
  3. 不变

通道优先级

对于独立的通用DMA,都会有多条通道,每条通道有独立的FIFO、配置寄存器、中断等硬件。但是DMA的Master Interface仅有一个,故多个通道会竞争该Master Interface资源。

值得注意的是,高优先级通道可以随时提出对主总线接口的请求,但是仅在当前AHB传输(突发或单次)完成后才被授予。因此,当Master Interface正在为底优先级通道传输数据而高优先级通道请求服务时,它会在切换为高优先级通道传输数据之前完成低优先级通道当前的AHB传输(Single 或 Brust)。另外,当优先级相同时(寄存器配置),DMAC一般会根据通道号来选择优先运行的通道。

FIFO的作用

启用DMA的FIFO可以最大程度地避免数据传输过程中的溢出问题,可以减少DMA对内存的访问次数从而减少总线访问竞争

每个DMA stream都有4个字的FIFO可用。它用来暂存来自DMA源端的数据,每当FIFO里存放的数据达到设定的阈值后,数据就会被移走。阈值可以设置为从1个字到4个字的深度

突发模式和循环挪用模式

DMA和CPU共用总线,所以这两种模式是为解决如何共用总线提供了三种方法。

官方文档:

突发模式[编辑]以一个连续的顺序传输整个数据块。
一旦CPU授予DMA控制器对系统总线的访问权限,它就会在释放对系统总线的控制返回给CPU之前传输数据块中的所有字节数据,但会使CPU在相对较长的时间内处于非活动状态。该模式也称为“块传输模式”。它还用于停止不必要的数据。

周期挪用模式[编辑]周期挪用模式用于在突发传输模式所需的时间长度内不应禁用CPU的系统。在周期窃取模式中,DMA控制器以与突发模式相同的方式获得对系统总线的访问,使用BR(总线请求)和BG(总线授权)信号,这两个信号控制CPU和DMA控制器之间的接口。然而,在周期挪用模式下,在一个字节的数据传输之后,系统总线的控制通过BG解除对CPU的断言。然后通过BR不断地再次请求,每次请求传输一个字节的数据,直到整个数据块传输完毕。
通过不断地获得和释放对系统总线的控制,DMA控制器实质上交替的完成了指令和数据传输。CPU处理一条指令,然后DMA控制器传输一个数据值,以此类推。一方面,数据块在周期窃取模式中的传输速度不如在突发模式中那么快,但另一方面,CPU的空闲时间并不像突发模式中的那样长。
周期窃取模式对于实时监控数据的控制器很有用。

突发模式:

当DMA申请总线成功后会连续传送数据,而不给cpu使用总线的机会,直到数据传送完毕。比如stm32设置了4个节拍的突发传输,传输宽度位8位,则一个dma请求会连续传送4个字节,是单次传输的4倍。

Burst操作还是要通过CPU的参与的,与单独的一次读写操作相比,burst只需要提供一个其实地址就行了,以后的地址依次加1,而非burst操作每次都要给出地址,以及需要中间的一些应答、等待状态等等。如果是对地址连续的读取,burst效率高得多,但如果地址是跳跃的,则无法采用burst操作

dma实际上是一次一次的申请总线,把要传的数据总量分成一个一个小的数据块。比如要传64个字节,那么dma内部可能分为2次,一次传64/2=32个字节,这个2(a)次呢,就叫做burst。这个burst是可以设置的。这32个字节又可以分为32位 8或者16位16来传输。

循环挪用模式:

这应该是常用的dma模式,就是一个dma请求就申请一次总线,传输1个字节。

什么时候使用突发模式:

在封装/解封数据的过程中,如果在数据完全封装/解封前中断操作,则有数据损坏的危险。因此,为了确保数据一致性,可将数据流配置成生成突发传输:在这种情况下,属于一个突发的每组传输不可分割。

为什么封装/解封会有数据损坏的危险呢?举个例子,有个外设的一组寄存器是实时联动的,也就是随着时间的变化,这组寄存器会改变。如果我们第一时刻取前一半寄存器的值,后一时刻区取后后一半寄存器的值,将两者封装起来就会损坏数据。所以我们需要在同一时刻取出所有值,这时就要用到突发传输,因为在传输时只有dma用总线,寄存器不会改变。

DMA Relative Register

DW_AHB_DMAC的所有寄存器都是64位对齐的,都是64位宽,但是一般高32位都是Reserved。

DT53一共有两个DMAC,两条物理通道,每个DMAC有3条Channel。

通道配置寄存器

AHB有4个独立的AHB主接口,4个接口可以同时传输,可以在不同的AHB上存在主接口(multi-layer)。

源与目的可以在不同的AHB总线上

每个AHB主接口可配置的数据总线宽度(256bits)

通过APBbridge与APB外设通信

DMA编程示例

DMA传输可以由单个或多个块传输组成。

在多块传输的连续块上,DW_ahb_dmac中的SARx/DARx寄存器使用以下方法之一重新编程:

  1. 使用链表的块链
  2. Auto-reloading
  3. 块之间的连续地址

在多块传输的连续块上,DW_ahb_dmac中的CTLx寄存器使用以下方法之一重新编程:

  1. 使用链表的块链
  2. Auto-reloading

当块链时,使用链表是多块方法的选择。在连续的块上,DW_ahb_dmac中的LLPx寄存器使用带链表的块链重新编程。

块描述符由六个寄存器组成:SARx、DARx、LLPx、CTLx、SSTATx和DSTATx。DW_ahb_dmac使用前四个寄存器和CFGx寄存器来设置和描述块传输。

配置一个通道

  1. 读取ChEnReg通道使能寄存器,寻找当前空闲的通道,然后按照优先级使能该通道

  2. 通过向interrupt clear registers寄存器组中写值去清除上次DMA传输的通道中任何pending interrupts。读取中断原始状态和中断状态寄存器确认所有中断都已清除。

  3. 编程以下通道寄存器

    ​ a. 设置源起始地址,即配置SARx寄存器,x为通道号

    ​ b. 设置目的起始地址,即配置DARx寄存器,x为通道号

    ​ c. 配置CTLx=0和CFGx=1,配置LLPx寄存器为0(DMA传输时不使用链表)(根据表格的ROW1)

    ​ d. 配置CTLx寄存器以为DMA传输设置控制信息

    ​ i. 设置传输类型(memroy or non-memory peripheral for source and destination) 和流控制器设备

    ​ 通过编程CTLx寄存器的TT_FC域

    ​ 参考下表

​ ii. 设置传输特性,例如:

​ ★配置源端传输宽度,SRC_TR_WIDTH域

​ ★配置目的端传输宽度,DST_TR_WIDTH域

​ ★配置source master layer, SMS域

​ ★配置destination master layer,DMS域

​ ★配置源递增/递减/固定地址,SINC域

​ ★配置目的递增/递减/固定地址,DINC域

​ iii. 写入通道配置信息到CFGx寄存器

​ 为源和目标外设指定握手接口类型(硬件或软件);这不是内存所必需的。这一步需要分别编程HS_SEL_SRC/HS_SEL_DST位。

​ 写入0可以激活硬件握手接口来处理源/目标请求。写入1激活软件握手接口来处理源和目标请求。

​ 如果源或目标外设的硬件握手接口被激活,向源和目标外设提供握手接口:这是必需的分别编程SRC_PER和DEST_PER位。

​ f. 如果启用了gather(参数DMAH_CHx_SRC_GAT_EN = True, CTLx. log = True)。SRC_GATHER_EN已启用),为通道x编程SGRx寄存器。

​ g. 如果启用了scatter(参数DMAH CHx DST SCA EN-True和CTLx.DST_SCATTER_EN),则为通道x编程DSRxregister。

  1. 在写入ChEnReg之前,确保DmaCfgReg寄存器的第0位是启用的。
  2. 源和目标请求单个和突发DMA事务,以便传输数据块(假设是非内存外设)。DW_ahb_dmac在块中的每个事务(突发事务和单个事务)完成时确认,并执行块传输。
  3. 传输完成后,硬件设置中断并禁用通道。此时,您可以响应Block Complete或Transfer Complete中断,或者轮询传输完成原始中断状态寄存器(RawTfr[n], n=通道号),直到硬件设置完毕,以便检测传输何时完成。注意,如果使用这种轮询,软件必须确保在通道启用之前,通过写入中断清除寄存器ClearTfr[n]来清除传输完成中断。

需要与设计沟通的地方

每个芯片的DMA设计都不同,如SRC_Master 与DST_Master的选择。它们在代码中以idx的形式给出,设计对每个芯片选择的Master是不同的idx。(0/1/2/3)

handshaking也需要沟通,handshaking是一组信号或软件寄存器,他们符合DMAC与Source Peripheral/Destination Peripheral之间的握手协议,以控制他们之间的Single/Brust 事务。每款芯片的设计,其各个外设,不管是源外设还是目的外设,他们要控制DMAC都可以看作与DMAC的一个连线,这个连线是需要标记出来并设置到寄存器中的,每款芯片的设计不同,这些连线的标记也会不同。比如I2C要作为Source Peripheral去请求DMA,它和DMAC的连线可能在DT56中标记为1,在DT53中的标记为2。开发不同的芯片的DMAC驱动时一定要注意修改这一点。

裸板代码分析

在这里主要分析裸板代码的内存到内存的single传输:

代码4-1

void dw_ahb_dma_mem2mem_single(u32 idx, u32 ch, u32 cfg)
{
	struct xfer_buff *tr_buff = NULL;

	tr_buff = dw_ahb_dmac_get_buff(idx);

	/* 1.init src buffer once */
	if ((idx == 0) && (cfg == DW_AHB_CHANNEL_CFG_0)) {
		for (u32 i = 0; i < tr_buff->buff_size; i++) {
			tr_buff->src[i] = i & 0xff;
		}
	}

	/* 2.clear dest buffer */
	memset(tr_buff->dst, 0x0, tr_buff->buff_size);
	if (dma_cache_on) {
		cleanDCache();
	}

	/* 3.config dmac and start transferring */
	qw_cpu_dmac_cfg_t *cpu_dmac_cfg = &cpu_dmac_config[idx][ch];

	dw_ahb_dmac_handle_t dmac_handle;
	dw_ahb_dmac_priv_t *dmac_priv;

	dmac_handle = dw_ahb_dmac_initialize(idx, dw_ahb_dmac_test_callback);
	dmac_priv = (dw_ahb_dmac_priv_t *)dmac_handle;
	qw_cpu_dmac_reg_t *cpu_dmac = (qw_cpu_dmac_reg_t *)(uintptr_t)(dmac_priv->base);


	cpu_dmac_cfg->src_addr = (u32)tr_buff->src + ADDR_OFFSET;
	cpu_dmac_cfg->dst_addr = (u32)tr_buff->dst + ADDR_OFFSET;

	cpu_dmac_cfg->src_master = 1;//idx;
	cpu_dmac_cfg->dst_master = 1;//idx;
	if (cfg == DW_AHB_CHANNEL_CFG_0) {
		cpu_dmac_cfg->src_bus_width = CPU_DMAC_TR_WIDTH_8BITS;
		cpu_dmac_cfg->dst_bus_width = CPU_DMAC_TR_WIDTH_8BITS;
		cpu_dmac_cfg->src_burst_size = CPU_DMAC_MSIZE_8;
		cpu_dmac_cfg->dst_burst_size = CPU_DMAC_MSIZE_8;
		cpu_dmac_cfg->block_size = BLOCK_SIZE0;
	} else if (cfg == DW_AHB_CHANNEL_CFG_1) {
		cpu_dmac_cfg->src_bus_width = CPU_DMAC_TR_WIDTH_32BITS;
		cpu_dmac_cfg->dst_bus_width = CPU_DMAC_TR_WIDTH_32BITS;
		cpu_dmac_cfg->src_burst_size = CPU_DMAC_MSIZE_4;
		cpu_dmac_cfg->dst_burst_size = CPU_DMAC_MSIZE_4;
		cpu_dmac_cfg->block_size = BLOCK_SIZE1;
	} else if (cfg == DW_AHB_CHANNEL_CFG_2) {
		cpu_dmac_cfg->src_bus_width = CPU_DMAC_TR_WIDTH_32BITS;
		cpu_dmac_cfg->dst_bus_width = CPU_DMAC_TR_WIDTH_16BITS;
		cpu_dmac_cfg->src_burst_size = CPU_DMAC_MSIZE_4;
		cpu_dmac_cfg->dst_burst_size = CPU_DMAC_MSIZE_8;
		cpu_dmac_cfg->block_size = BLOCK_SIZE2;
	}
	dw_ahb_dmac_init_chn_stat(idx, ch,
		COUNT_MAX >> cpu_dmac_cfg->src_bus_width,
		cpu_dmac_cfg->block_size);

	cpu_dmac_cfg->src_addr_change_mode = CPU_DMAC_ADDR_INCREASE;
	cpu_dmac_cfg->dst_addr_change_mode = CPU_DMAC_ADDR_INCREASE;
	cpu_dmac_cfg->trans_mode = CPU_DMAC_CTL_TT_MEM2MEM;
	cpu_dmac_cfg->fifo_mode = CPU_DMAC_FIFO_MODE_EN;

	drv_cpu_dmac_init(cpu_dmac, ch, cpu_dmac_cfg);
	//pr_info("---dma%d DMAC_CTL0 0x%llx\r\n", idx, cpu_dmac->DMAC_CTL0);

	DMA_DBG("---src_bus_width 0x%x, src_burst_size 0x%x\r\n",
			cpu_dmac_cfg->src_bus_width, cpu_dmac_cfg->src_burst_size);
	DMA_DBG("---src_master 0x%x, dst_master 0x%x\r\n",
			cpu_dmac_cfg->src_master, cpu_dmac_cfg->dst_master);

	/* enable selected channel's interrupt */
	drv_cpu_dmac_enable_intr(cpu_dmac, ch, CPU_DMAC_BLOCK_INTR, CPU_DMAC_INTR_EN);

	/* enable dmac */
	drv_cpu_dmac_enable_dmac(cpu_dmac, CPU_DMAC_EN);

	/* enable selected channel */
	drv_cpu_dmac_enable_ch(cpu_dmac, ch, CPU_DMAC_CH_EN);
    
    
}

dw_ahb_dmac_initialize初始化一个DMAC控制器dw_ahb_dmac_priv_t* dmac_priv后,就要利用dmac_priv->base开始配置qw_cpu_dmac_reg_t *cpu_dmac

cpu_dmac是寄存器组,前面定义的qw_cpu_dmac_cfg_t *cpu_dmac_cfg是damc配置信息的对象。

DMA驱动的一大任务就是封装对dmac配置信息设置寄存器的函数。这些函数从dmac配置信息来具体地设置DMA寄存器。

drv_cpu_dmac_init就是做这件事:

代码4-2:

s32 drv_cpu_dmac_init(qw_cpu_dmac_reg_t *addr, u32 channel, qw_cpu_dmac_cfg_t *cpu_dmac_cfg)
{
	if (channel > CPU_DMAC_MAX_CHANNEL)
		return ERROR;

	/* set selected channel's source address */
	drv_cpu_dmac_set_src_addr(addr, channel, cpu_dmac_cfg->src_addr);
	/* set selected channel's destination address */
	drv_cpu_dmac_set_dst_addr(addr, channel, cpu_dmac_cfg->dst_addr);

	/* set selected channel's block size */
	drv_cpu_dmac_set_block_size(addr, channel, cpu_dmac_cfg->block_size);

	drv_cpu_dmac_set_src_master(addr, channel, cpu_dmac_cfg->src_master);
	drv_cpu_dmac_set_dst_master(addr, channel, cpu_dmac_cfg->dst_master);

	/* set selected channel's transfer mode */
	drv_cpu_dmac_set_trans_mode(addr, channel, cpu_dmac_cfg->trans_mode);

	/* set selected channel's source transfer bus width */
	drv_cpu_dmac_set_src_trans_bus_width(addr, channel, cpu_dmac_cfg->src_bus_width);
	/* set selected channel's destination transfer bus width */
	drv_cpu_dmac_set_dst_trans_bus_width(addr, channel, cpu_dmac_cfg->dst_bus_width);

	/* set selected channel's source burst transfer size */
	drv_cpu_dmac_set_src_burst_size(addr, channel, cpu_dmac_cfg->src_burst_size);
	/* set selected channel's destination burst transfer size */
	drv_cpu_dmac_set_dst_burst_size(addr, channel, cpu_dmac_cfg->dst_burst_size);

	/* set selected channel's source address change mode */
	drv_cpu_dmac_set_src_addr_change_mode(addr, channel, cpu_dmac_cfg->src_addr_change_mode);
	/* set selected channel's destination address change mode */
	drv_cpu_dmac_set_dst_addr_change_mode(addr, channel, cpu_dmac_cfg->dst_addr_change_mode);

	/* set selected channel's source handshake mode, soft or hard */
	drv_cpu_dmac_set_src_hs_mode(addr, channel, cpu_dmac_cfg->src_hs_mode);
	/* set selected channel's destination handshake mode, soft or hard */
	drv_cpu_dmac_set_dst_hs_mode(addr, channel, cpu_dmac_cfg->dst_hs_mode);

	/* set selected channel's source hard handshake number */
	drv_cpu_dmac_set_src_hs_num(addr, channel, cpu_dmac_cfg->src_hs_num);
	/* set selected channel's destination hard handshake number */
	drv_cpu_dmac_set_dst_hs_num(addr, channel, cpu_dmac_cfg->dst_hs_num);

	/* set selected channel's block size */
	drv_cpu_dmac_set_fifo_mode(addr, channel, cpu_dmac_cfg->fifo_mode);

	return OK;
}

先不具体看这些设置寄存器的函数,而继续回到代码4-1的dw_ahb_dma_mem2mem_single看它是如何配置cpu_dmac_cfg配置信息对象的。

首先是31-32行 配置源地址与目的地址,其中tr_buff对象是xfer_buff类型,保存了一次传输的基本信息,它由dw_ahb_dmac_get_buff(idx)函数配置,为传输准备源/目的内存空间。

struct xfer_buff {
  u8 *src;
  u8 *dst;
  u32 buff_size;
} g_xfer_buff[DW_AHB_DMAC_MAX];

34-46行 是根据AHB传输通道的不同选择来配置以下两点

  1. SRC_Master 与DST_Master,这里需要与芯片设计沟通,看mem2mem的传输应该具体如何选择。

  2. 设置传输特性,源端/目的端的传输宽度以及burst大小,以及每次传输的block大小

73行 需要使能被选择通道的中断,这里选择的是Block Complete中断

76和79行 前后使能DMAC和Channel

关于AHB传输通道的传输特性,各个参数之间的关系还需要进一步理解:

数据宽度设置为8bit,也就是transfer size=8bits = 1byte(SRC_TR_WIDTH)

一次传8个transfer size,即burst size=8 (SRC_MSIZE = Source Burst Transaction Length:Number of data items each of width CTLx.SRC_TR_WIDTH to be read from the source every time a burst transaferred request is made from either the corresponding hardware or software handshaking interface)

传输块大小设置为4092,也就是Block transfer size=4092(the total number of single transaction to perform for every block transfer

意味着一次传输,实际DMA 将会从源端传输4092个8bit也就是4092bytes

一次burst请求能传送 8bit*8 = 8个字节

4092字节在burst事务会分为4092字节/8字节=511…4 即进行512次burst事务传完一个block

基于FIFO的burst模式的DMA传输时,BURST的大小乘以数据宽度不得超过设置的FIFO阈值大小,否则会出错。

总的来说dw_ahb_dma_mem2mem_single的配置过程和前面DMA编程示例中给出的“配置一个通道”中的示例类似。

有关DMA驱动开发---认识DMA的更多相关文章

  1. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

    我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

  2. Ruby Sinatra 配置用于生产和开发 - 2

    我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm

  3. ruby - 是否可以覆盖 gemfile 进行本地开发? - 2

    我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI

  4. ruby - 在 Windows 机器上使用 Ruby 进行开发是否会适得其反? - 2

    这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby​​-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub

  5. ruby-on-rails - 在 Rails 开发环境中为 .ogv 文件设置 Mime 类型 - 2

    我正在玩HTML5视频并且在ERB中有以下片段:mp4视频从在我的开发环境中运行的服务器很好地流式传输到chrome。然而firefox显示带有海报图像的视频播放器,但带有一个大X。问题似乎是mongrel不确定ogv扩展的mime类型,并且只返回text/plain,如curl所示:$curl-Ihttp://0.0.0.0:3000/pr6.ogvHTTP/1.1200OKConnection:closeDate:Mon,19Apr201012:33:50GMTLast-Modified:Sun,18Apr201012:46:07GMTContent-Type:text/plain

  6. ruby-on-rails - 如何在 Ruby on Rails 中实现由 JSF 2.0 (Primefaces) 驱动的 UI 魔法 - 2

    按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭10年前。问题1)我想知道ruby​​onrails是否有功能类似于primefaces的gem。我问的原因是如果您使用primefaces(http://www.primefaces.org/showcase-labs/ui/home.jsf),开发人员无需担心javascript或jquery的东西。据我所知,JSF是一个规范,基于规范的各种可用实现,prim

  7. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  8. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  9. FOHEART H1数据手套驱动Optitrack光学动捕双手运动(Unity3D) - 2

    本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01  客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02  数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit

  10. 微信小程序开发入门与实战(Behaviors使用) - 2

    @作者:SYFStrive @博客首页:HomePage📜:微信小程序📌:个人社区(欢迎大佬们加入)👉:社区链接🔗📌:觉得文章不错可以点点关注👉:专栏连接🔗💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞👉微信小程序(🔥)目录自定义组件-behaviors    1、什么是behaviors    2、behaviors的工作方式    3、创建behavior    4、导入并使用behavior    5、behavior中所有可用的节点    6、同名字段的覆盖和组合规则总结最后自定义组件-behaviors    1、什么是behaviorsbehaviors是小程序中,用于实现

随机推荐