DMA详细分析

DMA分析

1 引言

本文对DMA的分析是基于三星的SOC芯片S3C2440。本人还是一个菜鸟,如果有什么讲得不对的地方的还望指点。DMA理解起来有些难度,本文根据S3C2440手册的DMA章节对DMA进行分析,并试图用通俗的语言说清楚DMA。

2 DMA功能

DMA(Direct Memory Access)的功能我个人的理解就是帮助CPU传输数据,减少CPU在传送数据上花费的时间。谈到传输数据,那肯定要知道数据来源(SRC)与传输目的地(DST)。S3C2440的DMA下面四种情况:
1. Both source and destination are in the system bus
2. The source is in the system bus while the destination is in the peripheral bus
3. The source is in the peripheral bus while the destination is in the system bus
4. Both source and destination are in the peripheral bus
简单来说,它支持的数据传输可以发生在AHB与AHB的设备、AHB与APB的设备、APB与APB设备之间。内存就属于AHB上的设备,而UART、IIC、SPI这些就属于APB上的设备。

3 DMA请求源

S3C2440的DMA源有两种:Software(S/W request mode)、Hardware(H/W request mode)。S/W request mode顾名思义就是软件请求DMA传输,而H/W request mdoe就是硬件请求DMA传输。而H/W模式下的DMA请求源还可以具体到如下图所示:
H/W模式下S3C2440的DMA请求源
图中的ch-n表示DMA通道,如你所见S3C2440有4个DMA通道。nXDREQ0 和nXDREQ1代表两个外部请求源(External Devices)。

4 DMA操作

在这里插入图片描述
这是S3C2440关于DMA操作的说明,下面我做一下简单的讲解。
DMA操作分为三个状态:
State-1. 初始化状态下,DMA等待一个DMA请求,此时DMA的响应信号ACK 和DMA的中断请求信号(INT REQ)都为0(这里为0就是没有、无效的意思, 1是有效的意思, 不要与下文时序图中的XnXDACK信号的高低混淆)。一旦请求到达,那么 DMA操作进入下一个状态。
State-2. 在这个状态下,DMA的ACK信号为1(有效),DCON[19:0]被装载到计数器(CURR_TC)。
State-3. 在这个状态下,用来处理DMA原子操作(Atomic operation,不可分割的操作就是原子操作)的次状态机被初始化。次状态机从数据源读取数据并且将数据写入目的地址。在一次原子操作中,Date size(DSZ)和Transfer Size(TSZ)需要设置,一次原子操作传输的数据字节数=DSZ×TSZ其中DSZ=BYte or Half Word or Word,TSZ=Single or Burst,实际上Single就是1,而Burst就是4)。DMA的服务模式有Single Service Mode(这里的Single与前面TSZ的Single无关)和Whole Service Mode。在Single Service Mode,一次DMA请求只触发一次原子操作,而在Whole Service Mode,一次DMA请求将反复执行原子操作直到CURR_TC自动减为0。主状态机负责原子操作的计数,次状态机每完成一次原子操作主状态机就将CURR_TC减1。当CURR_TC减为0并且DMA中断使能位(DCON[29])被设为1(Enable),主状态机触发中断请求信号(INT REQ)。在下面两种情况下DMA的ACK信号被撤销:1.在Whole Service Mode下CURR_TC减为0时;2.在Single Service Mode下一次原子操作完成时。
说到这里其实还没有说清楚DMA传输过程,不急,下面将会详细讲解。

5 DMA协议

5.1 DMA的基本时序图

在这里插入图片描述当一个设备想发送DMA请求的时候,这个设备将XnXDREQ拉低,此时DMA控制器开始处理,首先经过9.3ns 的XnXDREQ设置时间(Setup),然后经过至少两个XSCLK周期的XnXDREQ同步,以及6.8ns的延时时间(Delay)之后,DMA开始拉低XnXDACK,表明DMA开始对设备的请求作出响应。DMA开始响应时,DMA会向总线控制器请求总线的控制权,如果获得总线控制权,那么DMA就开始执行原子操作。原子操作结束之后,DMA会重新拉高XnXDACK,表明DMA响应结束。

5.2 DMA各种模式间的区别

1. DMA的请求模式(姑且这么称)分为Demand Mode和Handshake Mode;
2. DMA的服务模式分为Single Service Mode和Whole Service。

5.2.1 Comparison between Single Service Mode and Whole Service Mode

简单来说:
Single Service Mode下,一次Service只执行一次原子操作(CURR_TC = 0时什么也不执行),然后CURR_TC自动减1。
Whole Service Mode下,一次Service反复执行原子操作,每次原子操作完成会使CURR_TC减1,直到CURR_TC减为0,才结束本次Service。

5.2.2 Comparison between Demand Mode and Handshake Mode

在一次Service的最后,DMA检查XnXDREQ的状态,这时候两种模式将对不同的状态做出不同的处理。

5.2.2.1 Demand Mode:

–XnXDREQ仍然保持拉低的状态:下一次Service立即开始;
–XnXDREQ处于拉高的状态:那么就等待XnXDREQ再次被拉低。
官方不建议使用Demand Mode,它会导致无意的传输。

5.2.2.2 HandshakeMode:

–XnXDREQ仍然保持拉低的状态:那么就等待XnXDREQ被拉高;
–XnXDREQ处于拉高的状态:那么就等待XnXDREQ再次被拉低。
简单来说:
Demand Mode下,只要XnXDREQ保持拉低状态,那么一个Service执行完就会立即执行下一个Service,一直保持下去,直到XnXDREQ变成拉高状态。
Handshake Mode下,XnXDREQ被拉低一次,无论其保持多长时间,只会导致执行一次Service,要执行下一个Service就得拉高XnXDREQ然后再拉低XnXDREQ。

在这里插入图片描述

6 DMA流程

下面根据我个人得理解对整个DMA流程做一个阐述。

6.1 S3C2440DMA的相关寄存器与控制位

这里寄存器后面的n表示DMA通道

DISRCn

初始传输源寄存器,其中S_ADDR(DISRCn[30:0])表示传输源。这里是不是有点疑惑,在Arm32平台,CPU地址位数是32位,为什么DMA中传输的地址只需要31位?一开始我以为是把传输地址最低位去掉了,这样使传输过程保持2字节对齐。其实不是这样,在查看了S3C2440的Memory Map之后,我发现S3C2440的最大可用地址空间是0x0到0x40001000,这个地址范围实际只需要31就可完全表示。

DISRCCn

初始传输源控制寄存器,其中LOC位(DISRCCn[1])用来描述传输源所在的总线(AHB or APB);其中INC为(DISRCCn[0])用来描述传输过程中,传输源的地址是增加还是固定(Increment or Fixed )。

DIDSTn

初始传输目的地址寄存器,其中D_ADDR(DIDSTn[30:0])表示传输目的地址。

DIDSTCn

初始传输源控制寄存器,其中LOC位(DIDSTCn[1])用来描述传输源所在的总线(AHB or APB);其中INC位(DIDSTCn[0])用来描述传输过程中,传输源的地址是增加还是固定(Increment or Fixed );其中CHK_INT位(DIDSTCn[2])用来指示DMA中断发生的时间,是当CURR_TC变成0时就发生,还是在Reload发生之后再发生。

DCONn

[31] -DMD_HS --------0=Demand mode 1=HandShake mode;
[30]-SYNC -------------0=synchronized to PCLK 1=synchronized to HCLK
[29]-INT ----------------0=dsiable interrupt 1=enable
[28]-TSZ ----------------0=A unit transfer 1=A burst transfer of length four
[27]-SERVMODE -----0=single service mdoe 1=whole service mode
[26:24]-HWSRCSEL ----request source in H/W request mode
[23]-SWHW_SEL ------0=S/W request mode 1=H/W request mode
[22]-RELOAD -----------0=enable 1=disable 不建议enable
[21:20]-DSZ --------------Date size-00=byte 01=half word 10=word
[19:0]-TC ------------------transfer count
SYNC表示DMA需要与哪个总线同步,如果传输源与目的地址中有使用到APB,那就需要与PCLK同步,否则就是与HCLK同步。
INT表示是否开启DMA中断。
SWHW_SEL表示DMA请求是H/W还是S/W。
HWSRCSEL表示H/W下DMA请求源来自哪个设备。

其它已经提到过,不再赘述。

DSTATn

[21:20]-STAT-------这里指示DMA是否正在传输。
[19:0]-CURR_TC-----这是传输过程中用来计数的,最初是由TC装载进来的。

DCSRCn

[30:0]-CURR_SRC-----传输过程中用来指示当前传输的数据的源地址,最初是由S_ADDR装载进来的,后来根据源地址是否增加,做出相应变化。

DCDSTn

[30:0]-CURR_DST-----传输过程中用来指示当前传输的数据的目的地址,最初是由D_ADDR装载进来的,后来根据目的地址地址是否增加,做出相应变化。

DMASKTRIGn

[2]-STOP-------------用来停止DMA传输。
[1]-ON_OFF---------用来关闭DMA通道。
[0]-SW_TRIG-------在S/W模式下,用来发出DMA请求,也就是拉低XnXDREQ,DMA开始操作时,这位自动回复为0,即XnXDREQ拉高。在S/W下,DMA请求是来自软件的,而不是像H/W一样来自其他设备。软件想开始DMA传输时,就把这一位写为1。

6.2 DMA传输过程

**Note:
1.下面的地址值是认为假设。
2.如果不特别说明,过程都是由硬件来实现。
3.CURR_TC的装载条件为CURR_TC为0且XnXDACK被拉低,CURR_SRCCURR_DST装载条件类似。
**
Step-1 首先,软件配置S_ADDR为0x31000000,D_ADDR为0x30000000,配置DISRCCn、配置DIDSTCn、配置DCONn,最终配置ON_OFF为1(ON)。此时CURR_SRCCURR_DSTCURR_TC都为0。跳转到Step-2。
Step-2 等待XnXDREQ被拉低(S/W模式下,通过软件给SW_TRIG写1来拉低XnXDREQ)。如果被拉低就跳转到Step-3。
Step-3 经过硬件同步之后,DMA开始拉低XnXDACK。跳转到Step-4。
Step-4 如果装载条件满足,CURR_SRCCURR_DSTCURR_TC分别从S_ADDRD_ADDRTC载入值。跳转到Step-5。
Step-5 接着,DMA开始执行一次Service。如果是Single Service Mode,则跳转到Step-5。如果是Whole Service Mode,则跳转到Step-7。
Step-6 执行一个原子操作(一次原子操作传输DSZ×TSZ字节),然后CURR_TC被硬件减1,CURR_SRC根据LOC位(DISRCCn[1])位的值增加或不变,CURR_DST根据LOC位(DIDSTCn[1])位的值增加或不变。然后跳转到Step-8。
Step-7 反复执行原子操作,每执行一次CURR_TC被硬件减1,并且CURR_SRC根据LOC位(DISRCCn[1])位的值增加或不变、CURR_DST根据LOC位(DIDSTCn[1])位的值增加或不变。这个过程持续到CURR_TC为0。传输的总字节数为DSZ×TSZ×TC。跳转到Step-8。
Step-8 判断CURR_TC是否为0。如果不为0,在Demand Mode下,如果此时XnXDREQ为拉低状态则跳转到Step-5,如果XnXDREQ为拉高状态则跳转到Step-2,在Handshake Mode下,等待XnXDREQ被拉高,然后跳转到Step-2。如果为0,则跳转到Step-9。
Step-9 S_ADDRD_ADDRTC被硬件清为0。判断Reload位的值。如果为1,则跳转到Step-10。如果为0,并且处于Demand Mode且XnXDREQ处于拉低状态,则跳转到Step-3。如果为0,并且处于Demand Mode且XnXDREQ处于拉高状态,则跳转到Step-2。如果为0,并且处于Handshake Mode,则等待XnXDREQ被拉高,然后跳转到Step-2。
Step-10 ON_OFF位被硬件清0,DMA通道被关闭。结束。

7 DMA测试

下面将使用两个程序来测试。无关代码没有挂出。程序经过实测

7.1 使用DMA将内存复制到内存

void dma0_interrupt(unsigned int irq)
{
	printf("DMA transmission stop\n\r");
}

void dma_test1(void * src, void * dst, int size)
{
	printf("DMA transmission start\n\r");
	register_irq(INT_DMA0, dma0_interrupt);
	/*	
	DISRCn
	DISRCCn		LOC[1] the location of source 0=AHB 1=APB
				INC[0] 0=increment 1=fixed
	DIDSTn
	DIDSTCn		CHK_INT[2] inerrupt time
				LOC[1] the location of destination 0=AHB 1=APB
				INC[0] 0=increment 1=fixed
	DCONn		DMD_HS[31] 0=Demand mode 1=HandShake mode;
				SYNC[30] 0=synchronized to PCLK 1=synchronized to HCLK
				INT[29] 0=dsiable interrupt 1=enable
				TSZ[28] 0=A unit transfer 1=A burst transfer of length four
				SERVMODE[27] 0=single service mdoe 1=whole service mode
				HWSRCSEL[26:24] request source in H/W request mode
				SWHW_SEL[23] 0=S/W request mode 1=H/W request mode
				RELOAD[22] 0=enable 1=disable
				DSZ[21:20] Date size 00=byte 01=half word 10=word
				TC[19:0] transfer count
	DSTATn(R)	STAT[21:20] 0=ready 1=busy
				CURR_TC[19:0]
	DCSRCn(R)	CURR_SRC[30:0]
	DCDSTn(R)	CURR_DST[30:0]
	DMASKTRIGn	STOP[2]
				ON_OFF[1] 0=off 1=on
				SW_TRIG[0]
	 */
	DISRC0 = (unsigned int)src;
	DISRCC0 = (0 << 1) | (0 << 0);
	DIDST0 = (unsigned int)dst;
	DIDSTC0 = (0 << 1) | (0 << 0);
	DCON0 = (1 << 31) | (1 << 30) | (1 << 29) | (0 << 28) | (1 << 27) | (0 << 23) | (1 << 22) | (2 << 20) | ((size + 3) / 4);
	DMASKTRIG0 = (1 << 1) | (1 << 0);
}

char info[] = "The S3C2440A supports four-channel DMA controller located \
between the system bus and the peripheral bus.\n\r";
char info_copy[200];

int main(void)
{
	int i;
	dma_test1(info, info_copy, 106);
	i = 100;
	while(i--);
	printf("%s", info_copy);

	return 0;
}

上面的程序是使用S/W Handshake Whole Service Mode。这里使用Demand Mode也同样可以实现。如果使用Single Service Mode,那你将发现输出为

DMA transmission start
The<space>
DMA transmission stop

也就说Single Service Mode下一次请求只执行一次原子操作,你可以在printf("%s", info_copy);之后添加DMASKTRIG0 |= (1 << 0);printf("%s", info_copy);,最终的输出结果为

DMA transmission start
The<space>The<space>S3C2

7.2 使用DMA协助UART发送

void dma0_interrupt(unsigned int irq)
{
	printf("DMA transmission stop\n\r");
}

void dma_test2(void * tx_data, int size)
{
	//**debug
	printf("DMA transmission start\n\r");
	//**
	/*Step 1 configure UART0*/
	/* configure pin */
	GPHCON &= ~((3 << 6) | (3 << 4));
	GPHCON |= ((2 << 6) | (2 << 4));
	
	/*
	ULCONn		Infrared Mode[6] 0=normal 1=infrared mode
				Parity Mode[5:3] 0xx=no parity 100=old 101=even 110=parity forced/checked as 1 111=parity forced/checked as 0
				Number of Stop Bit[2] 0=one stop bit 1=two stop bit
				Word Length[1:0] 00=5bits 01=6bits 10=7bits 11=8bits
	UCONn		FCLK Divider[15:12] Divider value when clock source is FCLK/n
				Clock Selection[11:10] 00,10=PCLK 01=UEXTCLK 11=FCLK/n
				Tx interrupt type[9]
				Rx interrupt type[8]
				Rx Time Out Enable[7] 0=disable 1=enable
				Rx Error Status[6] Interrupt Enable 0=disable 1=enable
				Loopback Mode[5] !!TEST PURPOSES ONLY
				Send Break Signal[4] 0=normal 1=send !!break signal is a signal keeping low level
				Transmit Mode[3:2] 00=disable 01=interrupt request or polling mode 10=DMA0(for UART0)DMA3(for UART2) 11=DMA1(for UART1)
				Receive Mode[1:0] 00=disable 01=interrupt request or polling mode 10=DMA0(for UART0)DMA3(for UART2) 11=DMA1(for UART1)
	UFCONn		Tx FIFO Trigger Level[7:6] 00=empty 01=16byte 10=32byte 11=48byte
				Rx FIFO Trigger Level[5:4] 00=1byte 01=8byte 10=16byte 11=32byte
				Tx FIFO Reset[2] 0=normal 1=tx FIFO reset
				Rx FIFO Reset[1] 0=normal 1=rx FIFO reset
				FIFO Enable[0] 0=disable 1=enable
	UMCONn		AFC[4] 0=disable 1=enable
				Request to Send[0] available only when AFC is disabled 1=Activate nRTS
	UTRSTATn(R)	Transmitter empty[2] 1=empty
				Transmit buffer empty[1] 1=empty
				Receive buffer data ready[0] 0=empty 1=buffer has a received data
	UERSTATn(R)	Break Detect[3] 1=break signal receive
				Frame Error[2] 1=frame error
				Parity Error[1] 1=parity error
				Overrun Error 1=overrun error
	UFSTATn(R)	Tx FIFO Full[14] 0=0-byte≤Tx FIFO data≤63-byte 1=full
				Tx FIFO Count[13:8]
				Rx FIFO Full[6] 0=0-byte≤Rx FIFO data≤63-byte 1=full
				Rx FIFO Count[5:0]
	UMSTATn(R)	Delta CTS[4] 0=no changed 1=changed
				Clear to Send 1=CTS signal is activated
	UTXHn(R)	tx buffer
	URXHn(R)	rx buffer
	UBRDIVn		BERDIV[15:0] baud value division value
	*/
	
	/* configure baud rate */
	UCON0 = (0 << 10);
	UBRDIV0 = 0x1a;

	/* set frame format */
	ULCON0 = (0 << 3) | (0 << 2) | (3 << 0);//no parity;one stop bit;8 bits data
	
	/* mode */
	UCON0 |= (1 << 0) | (2 << 2);
	UFCON0 = 0;
	UMCON0 = 0;

	/* Step 2 configure DMA0*/
	/*	
	DISRCn		
	DISRCCn		LOC[1] the location of source 0=AHB 1=APB
				INC[0] 0=increment 1=fixed
	DIDSTn
	DIDSTCn		CHK_INT[2] inerrupt time
				LOC[1] the location of destination 0=AHB 1=APB
				INC[0] 0=increment 1=fixed
	DCONn		DMD_HS[31] 0=Demand mode 1=HandShake mode;
				SYNC[30] 0=synchronized to PCLK 1=synchronized to HCLK
				INT[29] 0=dsiable interrupt 1=enable
				TSZ[28] 0=A unit transfer 1=A burst transfer of length four
				SERVMODE[27] 0=single service mdoe 1=whole service mode
				HWSRCSEL[26:24] request source in H/W request mode
				SWHW_SEL[23] 0=S/W request mode 1=H/W request mode
				RELOAD[22] 0=enable 1=disable
				DSZ[21:20] Date size 00=byte 01=half word 10=word
				TC[19:0] transfer count
	DSTATn(R)	STAT[21:20] 0=ready 1=busy
				CURR_TC[19:0]
	DCSRCn(R)	CURR_SRC[30:0]
	DCDSTn(R)	CURR_DST[30:0]
	DMASKTRIGn	STOP[2]
				ON_OFF[1] 0=off 1=on
				SW_TRIG[0] only available in S/W mode
	 */
	register_irq(INT_DMA0, dma0_interrupt);
	DISRC0 = (unsigned int)tx_data;
	DISRCC0 = (0 << 1) | (0 << 0);
	DIDST0 = (unsigned int)0x50000020;
	DIDSTC0 = (0 << 2) | (1 << 1) | (1 << 0);
	DCON0 = (1 << 31) | (0 << 30) | (1 << 29) | (0 << 28) | (0 << 27) | (1 << 24) | (1 << 23) | (1 << 22) | (0 << 20) | size;
	DMASKTRIG0 = (0 << 2) | (1 << 1) | (0 << 0);
}

char info[] = "The S3C2440A supports four-channel DMA controller located \
between the system bus and the peripheral bus.\n\r";

int main(void)
{
	dma_test2(info, 106);
	
	while(1);
	
	return 0;
}

这个程序的输出结果如下:

DMA transmission start
The S3C2440A supports four-channel DMA controller located between the system bus and the peripheral bus.
DMA transmission stop

这里不做具体分析。

发布了1 篇原创文章 · 获赞 6 · 访问量 110

猜你喜欢

转载自blog.csdn.net/qq_36413840/article/details/104865105