本文主要介绍Infineon Aurix2G TC3XX系列芯片DMA模块硬件原理,以及MCAL相关配置,和部分代码实现。
1 模块介绍
直接存储器访问(Direct Memory Access,DMA)是计算机系统中的一种机制,允许外部设备(如硬盘驱动器、网络适配器等)直接访问系统内存,而无需通过中央处理器(CPU)的干预。这可以提高数据传输的效率,因为数据可以直接在设备和内存之间传输,而不必通过CPU来中转。 DMA通常用于大容量数据传输,如文件传输、网络数据包处理等。
Aurix2G TC3XX系列中DMA模块不仅能够实现内存之间的数据搬运,还能支持外设数据的搬运,比如利用DMA进行SPI数据的异步发送。
2 功能介绍
2.1 模块总览
我们来通过框图看DMA整体结构。
DMA的整体结构还是比较简单的,首先看左下角的Configuration Interface,我们通过访问外设寄存器完成对DMA的配置;然后是上面的DMA Channels,里面存放所有DMA通道的属性;当存在软硬件搬运请求时,DMA模块会对请求队列进行仲裁,然后通过右边的Move Engine与内存总线进行交互,完成搬运。
DMA模块同样也会产生不同类型的中断,发送到中断路由模块。而中断路由模块可以将所有其他模块的中断信号路由到DMA作为硬件触发信号。
这里我们需要先了解一些缩写:
- CH:Channel,DMA通道;
- ME:Move Engine,搬运引擎,负责与总线交互进行数据搬运;
- RP:Resource Partition,资源分区,DMA支持分区权限管理,每个RP有指定的权限和内存区域,每个RP有自己的错误中断;
- TCS:Transaction Control Set,交换控制集;
和一些术语:
- DMA Hardware Request:由中断路由模块发起的搬运请求称为硬件请求;
- DMA Move:DMA数据搬运,一次Move包含从源地址读数据,和往目的地址写数据;
- DMA Transfer:一个Transfer包含1, 2, 3, 4, 5, 8, 9或16个Move;
- DMA Transaction:一个DMA Transaction包含至少一个Transfer;
- Linked List:一个链表包含同一个通道的多个Transaction;
所以DMA的一般结果是Transaction包含Transfer,Transfer包含Move,Move是数据处理的最小颗粒度;而Transfer是触发的最小颗粒度,也就是说你可以选择一次请求触发一次Transaction,或者只触发一个Transfer。如果你想搬运的数据并非连续或者规则的,比如有两块比较远的内存,需要在一次操作中处理,这个时候你就需要配置Linked List,一个Linked List包含多个Transaction。
2.2 DMA通道
DMA通道的请求有4种:
- DMA Software Request:软件请求,顾名思义就是软件写寄存器请求一次DMA搬运;
- DMA Hardware Request:硬件请求,其他外设的中断信号发送至中断路由模块IR,IR将信号路由至DMA模块,请求一次DMA搬运;
- DMA Daisy Chain Request:Daisy链请求,比如通道5使能了Daisy链配置,那它在完成之后会触发通道4的搬运请求;
- DMA Auto Start Request:用于Linked List,同一通道配置了多个TCS,第一个TCS完成之后会触发下一个TCS的搬运请求;
然后根据请求和执行的情况,DMA通道有不同的状态:
- Idle State:当前通道空闲,TSR.CH=0;
- Reset State:当前通道处于Reset状态,TSR.CH=0;
- Halt State:通道处于Halt状态,不工作,TSR.HLTACK = 1;
- Pending State:通道已经被请求,但是没有被服务,TSR.CH=1;
- Active State:通道的搬运正在执行;
前面提到DMA搬运触发的最小颗粒度是Transfer,通过CHCFGR.RROAT寄存器来配置:
- CHCFGR.RROAT=0:每个Transfer搬运完成之后请求即被清除;
- CHCFGR.RROAT=1:Transaction中的最后一个Transfer搬运完成之后请求被清除;
2.2.1 DMA Software Request软件请求
用户可以通过写寄存器进行软件搬运请求,在进行软件请求的时候需要通过将TSR.DCH写1关闭硬件请求。
软件请求的搬运流程如下图所示,图中有两个部分,区别在于CHCFGR.RROAT。
我们可以看到,图中上部分的搬运流程中,CHCFGR.RROAT=1,当软件进行一次请求后,硬件会装载Transfer Count寄存器的值CHSR.TCOUT,然后开始搬运,每搬运一个Transfer之后CHSR.TCOUNT的值便会减一,当TCOUT的值为0之后搬运完成,会向中断路由模块发送DMA通道搬运完成中断。
下部分的搬运流程中CHCFGR.RROAT=0,这里仅有的区别是每次请求只执行一次Transfer的搬运。
2.2.2 DMA Hardware Request硬件请求
硬件请求相对于软件请求多了一个硬件使能的相关配置,我们可以设置TSR.ECH=1来使能硬件请求,或者设置TSR.DCH=1来关闭硬件请求,通过只读寄存器TSR.HTRE查看中断使能状态。
另外还有一个重要的配置是CHCFGR.CHMODE,当CHMODE=0时,为单次模式,每次Transaction完成之后硬件使能会被自动关掉,需要手动再打开,否则硬件请求不会被处理;当CHMODE=1时,则为连续模式,不会关闭硬件请求使能。
关于硬件请求,需要在中断模块中进行配置,把其他外设的中断信号路由到DMA(SRC.TOS选择DMA),优先级SRC.SRPN设置为对应的DMA通道号即可。
其他方面就和软件请求一样了,我们来看搬运流程图。
图中的两个转换流程都是单次模式,在上面的搬运流程中CHCFGR.RROAT=1,需要先使能硬件请求,然后一个硬件搬运请求触发后,会完成整个Transaction,同样的TCOUNT会自动递减,归零后触发DMA通道完成中断。
下面的搬运流程中CHCFGR.RROAT=0,每次硬件请求只会完成一次Transfer,需要不停地触发;一个Transaction中的所有的Transfer完成之后硬件请求使能会被关闭。
连续模式下与上面的描述只有一个区别,就是硬件请求使能不会被硬件关闭,后续的硬件请求能够连续执行。
2.2.3 DMA Daisy Chain Request Daisy链请求
用户可以通过设置CHCFGR.PRSEL来配置Daisy链。当一个通道配置了Daisy链使能,在它的Transaction完成之后,会自动请求相邻的低优先级的通道搬运(DMA通道号即优先级,越高优先级越高)。所以我们可以通过对多个相邻的DMA通道配置使能Daisy链,完成多个DMA通道搬运的链式触发。
另外在配置Daisy过程中,除了最低优先级的通道以外,其他通道需要关闭中断使能,仅最后一个通道使能中断,也就是说,中断需要在Daisy链全部完成之后触发。
2.2.4 通道请求丢失中断
在以下两种情况下,会发生中断请求丢失(DMA Channel Transaction/Transfer Request Lost,TRL)事件:
- 当一个通道处于Pending状态(TSR.CH=1)时收到了通道请求;
- 当一个通道未使能硬件请求,但是收到了硬件搬运请求;
发生TRL之后TSR.TRL会置位,如果此时使能了所在RP的错误中断(TSR.ETRL=1),则会触发所在RP的错误中断。
2.2.5 请求仲裁
DMA仲裁单元会对所有处于Pending状态的通道进行仲裁,然后选择其中通道号最高的通道处理。如果有当前正在搬运的Transaction,而出现了高优先级的通道的Transaction,则会打断低优先级的,但是打断的最小颗粒度是Transfer,也就是正在处理的Transfer会完成。
Aurix2G的DMA模块有两个Move Engine,它们和通道的关系不是绑定的,而是选择空闲的使用,仲裁胜出的通道会优先送往Engine1,如果非空闲则选择Engine0。
2.3 搬运流程
当仲裁完成之后,DMA通道请求就会送到Move Engine去进行数据搬运,前面我们提到,Transaction包含多个Transfer,Transfer包含多个Move,而Move是搬运的最小颗粒度,也就是说,ME是按照Move逐个操作的,Move中定义源地址、目的地址,地址偏移等信息。
DMA的搬运源地址通过CHx.SADR寄存器设置,目的地是通过CHx.DADR设置。DMA每个Move在完成之后,源地址和目的地址都可以进行偏移,以支持我们搬运连续的内存空间,当然也可以原地保持不变,循环搬运。源地址和目的地址的偏移都是独立计算的。
地址的偏移有两种,一种是递增或递减,每次以固定的偏移值进行调节;另一种就是循环Buffer。
2.3.1 地址偏移模式
如果我们需要搬运的数据,它的地址是不连续的,比如ADC采样的结果寄存器,每个结果寄存器我们只需要前16个Bit,而我们可能需要多个通道的采样结果,那我们就需要通过设置Move的地址偏移,来进行搬运。
我们需要通过CHCFGR.CHDW设置Move的搬运宽度,数据宽度DataWidth=8*2^CHDW,单位是Bit,最大支持256Bit。在地址偏移模式中,每次Move完成之后偏移的地址值,是数据宽度的整倍数。
通道地址控制寄存器ADICR中包含了源地址、目的地址的偏移参数:
- SMF:Source Address Modification Factor,源地址偏移参数;
- INCS:Increment of Source Address,源地址偏移方向,递增或递减;
- DMF:Destination Address Modification Factor,目的地址偏移参数;
- INCD:Increment of Destination Address,目的地址偏移方向,递增或递减;
在地址偏移模式中,以上4个参数定义了地址偏移的格式。它的算法是这样的,偏移值=2^SMF*DataWidth,INCS为1就是增加,INCS为0就是减(源地址和目的地址相同算法相同)。我们可以看下面这个实例:
在这个示例中,搬运Move的数据宽度为16Bit,源地址的SMF=3,INCS=1,则每次搬运之后地址增加2^3*16=128Bit。而目的地址DMF=2,INCD=0,那每次搬运之后地址减少2^2*16=64Bit。
我们再来看下一个示例,它将一段连续地址空间的数据搬运到带间隙的地址空间上去。
这个示例中的数据宽度同样也是16bit,源地址的SMF=0,INCS=1,所以每次搬运之后地址增加2^0*16=16Bit,也就是没有间隙,依次读取数据。而目的地址的DMF=1,INCD=1,所以每次搬运之后地址增加2^1*16=32Bit,实现了间隙式搬运。
这里需要注意的是,在一个Transaction搬运完成之后,源地址和目的地址并不会重置,而是停留在最后一次搬运所指向的地址,如果要开启新一轮的搬运,则需要重新再设置地址。
2.3.2 循环Buffer模式
循环Buffer模式,和地址偏移模式的区别,就是增加了一个地址空间的上下限,超过这个限制就会按照设定的方向滚动。
同样我们需要在ADICR寄存器中设置相关参数,除了地址偏移模式的位域以外,还有以下位域:
- CBLS:Circular Buffer Length Source,源地址循环Buffer尺寸;
- CBLD:Destination Circular Buffer Enable,目的地址循环Buffer尺寸;
- SCBE:Source Circular Buffer Enable,源地址循环Buffer使能;
- DCBE:Destination Circular Buffer Enable,目的地址循环Buffer使能;
首先我们按照需求来使能源地址或者目的地址的循环Buffer,然后和偏移地址模式一样设置我们的偏移参数,最后通过CBLS或CBLD设置我们的Buffer尺寸。Buffer尺寸为2^CBLS,单位为Byte,最大为64kByte。
举个例子,比如我源地址为0x7000CC40,CBLS=4,则循环Buffer尺寸为2^4=16Byte,在搬运完0x7000CC4C~0x7000CC4F的4字节之后(每个Move4字节),源地址回到0x7000CC40。
另外如果想要地址保持不变,则设置CBLS为0即可。
这里有一个需要注意的地方是,Buffer的上下限必须是按Buffer尺寸字节对齐的,比如Buffer尺寸为16字节,则源地址必须为16字节对齐,如果源地址为0x7000CC44,则前4个字节不会被搬运。
3 MCAL配置示例
下面我们结合示例来说明DMA模块的原理,以及在MCAL中的相关配置。本实例是通过软件触发,把一块内存的数据搬运到另一块内存中。
3.1 DMA General
General界面没有什么特别的配置,一般都是软件相关接口使能。
3.2 Channel General
我们来到Channel之后,首先是一页General配置,这里配置通道的基本属性,比如通道ID等。
用户也可以配置Notification函数,在DMA完成中断中会触发该函数。
3.3 DmaChannelTransactionSet
TransactionSet这里是通道的详细配置,包括刚才提到的转换配置,地址偏移等,我们一个个介绍。
- DmaTcsIndex:这个参数是Transaction的Id,如果通道中只有一个,就是0就可以,如果有Linked List,存在多个Transaction,就需要依次排序;
- DmaTcsSourceAddress & DmaTcsDestinationAddress:搬运的源地址和目的地址,这里可以使用地址值,也可以使用变量、寄存器名等,但是前提是下面的头文件引用能够包含该信息;
- DmaUserHeaderFileWithExternDeclarations:引用头文件;
- DmaTcsMoveLength:Move搬运的数据宽度,这里我们一次搬运32bit;
- DmaPatternMode:数据匹配模式,能够进行数据匹配,这里未使用;
- DmaTcsTransferLength:一个Transfer中Move的数量;这里我们选择1;
- DmaTcsTransactionLength:一个Transaction中Transfer的数量,这里我们配置4;
- DmaTcsCircularBufferSourceEnable:源地址循环Buffer使能;
- DmaTcsCircularBufferDestinationEnable:目的地址循环Buffer使能;
- DmaTcsCircularBufferSourceLength:源地址循环Buffer尺寸,这里我们配置16字节循环;
- DmaTcsCircularBufferDestinationLength:目的地址循环Buffer尺寸,这里我们配置16字节循环;
- DmaTcsSourceAddressModificationFactor:源地址偏移值,这里我们配置1个Move宽度,不留间隙;
- DmaTcsDestinationAddressModificationFactor:目的地址偏移值,这里我们配置1个Move宽度,不留间隙;
- DmaTcsAppendTimeStamp:时间戳功能,不使用;
- DmaTcsSourceAddressMovement:源地址偏移方向,这里配置为增加;
- DmaTcsDestinationAddressMovement:目的地址偏移方向,这里配置为增加;
- DmaTcsShadowRegisterConfiguration:影子寄存器配置,未使用;
- DmaTcsTriggerFrequency:Transaction触发频率,也就是选择一次触发是完成一个Transfer还是完成一个Transaction,这里配置为Transaction;
- DmaTcsHardwareTrigger:是否使用硬件触发,单次还是连续;
- DmaTcsDaisyChaining:是否配置为Daisy链,如果勾选则完成后触发相邻的低通道的请求,这里不使用;
3.4 示例代码
示例代码这里我们还是使用Infineon原厂MCAL代码,首先是模块初始化:
Dma_Init(&Dma_Config);
然后我们声明转换需要的源数据和目的数据,这里Buffer为16字节,所以我加了对齐:
#pragma align 16
uint32 DmaTestSrc[DMATEST_BUFFERLEN];
uint32 DmaTestDes[DMATEST_BUFFERLEN];
#pragma align restore
然后就在运行过程中请求转换就可以了:
Dma_ChStartTransfer(19); /* 入参为通道号 */
然后我们调试看结果,我这里DmaTestSrc源地址为0x7000CC40,目的地址DmaTestDes为0x7000CC30。启动后将源数据进行了初始化:
然后我们启动一次转换,可以看到执行了4个Transfer,16字节数据全部搬运:
4 小结
本文介绍了Infineon Aurix2G DMA模块的硬件原理和搬运流程,对搬运的数据结构和地址逻辑进行了说明,最后通过实例展示了DMA模块的MCAL配置和使用方法,为读者进行实操提供了参考。
参考资料
- Infineon-AURIX_TC3xx_Part1-UserManual-v02_00-EN.pdf