分类: LINUX
2010-07-09 10:28:22
DMA学习笔记
DMA:direct memory access 是一种无须CPU参与就可以让外设与系统之间进行双向数据传输的硬件机制.
DMA由DMAC控制,在传输期间,CPU可以并发的执行其他任务.
需注意:
1:DMA与cache的一致性;
DMA针对内存中的地址有与cache有没有重叠区域.如果没有则相安无事.但是如果有重叠区域,就得考虑,因为并发的话,DMA和cache就有可能从同一片内存中取数据,而取数据肯定会有时间先后的顺序.一旦cache取完了数据,但是DMA又对这一片内存空间进行了操作使得内存中的内容发生了改变,改变之前cache恰好还没有将没改变之前的内容传给CPU做处理.这样就会导致不一致性的错误.cache与内存中的数据一样就称之为一致,不一样就不一致.
现在这个问题还没有找到恰当的方法去修改.这样的错误系统是不会报错的,而且很难检测出来,所以一般的解决办法就是让DMA和cache不要使用同一片内存空间,做出这样的牺牲换取系统的正常稳定的工作.
内存分配了一致性区域(进行地址映射并保证一致性)
Void *dma_alloc_coherent(struct device *dev,size_t size,dma_addr_t *haddle,gfp_t gfp);
返回值:申请到的DMA缓冲区的虚拟地址
通过haddle返回缓冲区的总线地址(dma_addr_t是总线地址类型)
Void *dma_free_coherent()是与之对应的释放函数.
Cache的不一致性,不仅仅发生在DMA的情况下,尤其是带MMU功能的ARM处理器,开启MMU之前,需要先置CACHE无效.TLB也是如此(TLB?)
TLB:Translation lookaside buffer,即旁路转换缓冲,或称为页表缓冲;里面存放的是一些页表文件(虚拟地址到物理地址的转换表)。
X86保护模式下的寻址方式:段式逻辑地址—〉线形地址—〉页式地址;
页式地址=页面起始地址+页内偏移地址;
对应于虚拟地址:叫page(页面);对应于物理地址:叫frame(页框);
X86体系的系统内存里存放了两级页表,第一级页表称为页目录,第二级称为页表。
TLB和CPU里的一级、二级缓存之间不存在本质的区别,只不过前者缓存页表数据,而后两个缓存实际数据。()
DMA缓冲区 必须是物理上连续的内存空间
相关函数:
申请DMA缓冲区:
__get_dma_pages()是__get_free_pages()/*内核中的函数*/的一个宏定义
或者dma_mem_alloc()
基于DMA的硬件使用的是总线地址并不是物理地址,
总线地址:从设备的角度看到的物理地址.
物理地址:从CPU的角度看到的未经转换的内存地址.
物理地址-----总线地址------虚拟地址
内核提供了转换的函数进行总线地址<---->虚拟地址的转换
Virt_to_bus()和bus_to_virt()
IOMMU: input/output memory management unit
IOMMU针对外设地址和内存地址之间的转换.(主要作用就是使得外设可以看到虚拟地址)
问题:SG:service guaide?分段中的缓冲区地址对外设变的连续?
()
DMA映射工作:
1:分配一片DMA缓冲区
2:为这片缓冲区产生设备可访问的地址.
注意:设备并不一定能在所有的内存地址上执行DMA操作,这时要执行地址掩码:
int dma_set_mask(struct device *dev,u64 mask);
DMA的流程
1:在使用DMA之前需要申请DMA通道
int request_dma(unsigned int dmanr,const char *device_id);//申请通道
void free_dma(unsigned int dmanr);//释放通道
Request_dma 初始化DMAC 申请DMA缓冲区 进行DMA传输 中断处理(如果有) 释放dma缓冲区 Free_dma() 在设备驱动模块加载或open()函数中进行 在设备驱动模块卸载或release()函数中进行 在读写及硬件操作时运行
看完了DMA的理论的东西之后,心里有很多的疑问:
1:如何将DMA集成到现在要用的nandflash驱动中去!
2:DMA中有很多的函数,分配连续的空间的函数中是随机分配的么?如何要从某个固定的起始地址开始分配,如何去做?
今天天气不错哦,多云````
如何实际运用DMA去做驱动```
1:DMA中有自己的一套中断方式``
1:要安装中断服务程序
2:申请DMA缓冲区
3:初始化DMA
4:进行DMA操作
5:引发中断进行中断处理程序
这中间发生了很多事情,
桥:桥要负责所存地址,数据及控制信号,同时要进行二次译码及选择相应的APB设备.
什么是握手信号:
什么是握手信号? 什么是握手协议?
RS -232通行方式允许简单连接三线:Tx、Rx和地线。但是对于数据传输,双方必须对数据定时采用使用相同的波特率。尽管这种方法对于大多数应用已经足够,但是对于接收方过载的情况这种使用受到限制。这时需要串口的握手功能。在这一部分,我们讨论三种最常用的RS-232握手形式:软件握手、硬件握手和 Xmodem。
a,软件握手:我们讨论的第一种握手是软件握手。通常用在实际数据是控制字符的情况,类似于GPIB使用命令字符串的方式。必须的线仍然是三根:Tx, Rx和地线,因为控制字符在传输线上和普通字符没有区别,函数SetXModem允许用户使能或者禁止用户使用两个控制字符XON和OXFF。这些字符在通信中由接收方发送,使发送方暂停。
例如:假设发送方以高波特率发送数据。在传输中,接收方发现由于CPU忙于其他工作,输入buffer已经满了。为了暂时停止传输,接收方发送XOFF,典型的值是十进制19,即十六进制13,直到输入buffer空了。一旦接收方准备好接收,它发送XON,典型的值是十进制17,即十六进制11,继续通信。输入buffer半满时,LabWindows发送XOFF。此外,如果XOFF传输被打断,LabWindows会在buffer达到75%和 90%时发送XOFF。显然,发送方必须遵循此守则以保证传输继续。
b,硬件握手:第二种是使用硬件线握手。和Tx和Rx线一样,RTS/CTS和DTR/DSR一起工作,一个作为输出,另一个作为输入。第一组线是RTS (Request to Send)和CTS(Clear to Send)。当接收方准备好接收数据,它置高RTS线表示它准备好了,如果发送方也就绪,它置高CTS,表示它即将发送数据。另一组线是DTR(Data Terminal Ready)和DSR(Data Set Ready)。这些现主要用于Modem通信。使得串口和Modem通信他们的状态。例如:当Modem已经准备好接收来自PC的数据,它置高DTR线,表示和电话线的连接已经建立。读取DSR线置高,PC机开始发送数据。一个简单的规则是DTR/DSR用于表示系统通信就绪,而RTS/CTS用于单个数据包的传输。
在LabWindows,函数SetCTSMode使能或者禁止使用硬件握手。如果CTS模式使能,LabWindows使用如下规则:
当PC发送数据:
RS-232库必须检测CTS线高后才能发送数据。
当PC接收数据:
如果端口打开,且输入队列有空接收数据,库函数置高RTS和DTR。
如果输入队列90%满,库函数置低RTS,但使DTR维持高电平。
如果端口队列近乎空了,哭喊数置高RTS,但使DRT维持高电平。
如果端口关闭,库函数置低RTS和DTR。
c,XModem握手:最后讨论的握手叫做XModem文件传输协议。这个协议在Modem通信中非常通用。尽管它通常使用在Modem通信中, XModem协议能够直接在其他遵循这个协议的设备通信中使用。在LabWindows中,实际的XModem应用对用户隐藏了。只要PC和其他设备使用 XModem协议,在文件传输中就使用LabWindows的XModem函数。函数是XModemConfig,XModemSend和 XModemReceive。
XModem使用介于如下参数的协议:start_of_data、end_of_data、neg_ack、wait_delay、 start_delay、max_tries、packet_size。这些参数需要通信双方认定,标准的XModem有一个标准的定义:然而,可以通过 XModemConfig函数修改,以满足具体需要。这些参数的使用方法由接收方发送的字符neg_ack确定。这通知发送方其准备接收数据。它开始尝试发送,有一个超时参数start_delay;当超时的尝试超过max_ties次数,或者收到接收方发送的start_of_data,发送方停止尝试。如果从发送方收到start_of_data,接收方将读取后继信息数据包。包中含有包的数目、包数目的补码作为错误校验、packet_size字节大小的实际数据包,和进一步错误检查的求和校验值。在读取数据后,接收方会调用wait_delay,然后想发送方发送响应。如果发送方没有收到响应,它会重新发送数据包,直到收到响应或者超过重发次数的最大值max_tries。如果一直没有收到响应,发送方通知用户传输数据失败。
由于数据必须以pack_size个字节按包发送,当最后一个数据包发送时,如果数据不够放满一个数据包,后面会填充ASCII码NULL(0)字节。这导致接收的数据比原数据多。在XModem情况下一定不要使用XON/XOFF,因为XModem发送方发出包的数目很可能增加到XON/OFF控制字符的值,从而导致通信故障。
给我的感觉 硬件握手就是收发双方遵循一种传输协议```必须双方都准备好,就算是握手成功了.
filed:字段,现场.
当x = 0 到7时,通道寄存器包含下列部分:
l CFGx –通道x的配置寄存器
l CTLx –通道x的控制寄存器
l DARx –通道x的目的地址寄存器
l DSRx –通道x的目的分散加载寄存器
l DSTATx –通道x的目的状态寄存器
l DSTATARx –通道x的目的状态地址寄存器
l LLPx –通道x的链表指针寄存器
l SARx –通道x的源地址寄存器
l SGRx –通道x的源分散采集寄存器
l SSTATx –通道x的源状态寄存器
l SSTATARx –通道x的源状态地址寄存器
scatter/gather I/O
scatter/gather方式是与block dma方式相对应的一种dma方式。
在dma传输数据的过程中,要求源物理地址和目标物理地址必须是连续的。但在有的计算机体系中,如IA,连续的存储器地址在物理上不一定是连续的,则dma传输要分成多次完成。
如果传输完一块物理连续的数据后发起一次中断,同时主机进行下一块物理连续的传输,则这种方式即为block dma方式。
scatter/gather方式则不同,它是用一个链表描述物理不连续的存储器,然后把链表首地址告诉dma master。dma master传输完一块物理连续的数据后,就不用再发中断了,而是根据链表传输下一块物理连续的数据,最后发起一次中断。
很显然scatter/gather方式比block dma方式效率高。
http://blog.csdn.net/zhoujunyi/archive/2007/12/06/1920110.aspx
中断寄存器:
1.Intblock 块传输中断
当DMA向目的外设的块传输完成时,产生中断
2.IntDstTran 目的transaction完成中断
当位于目的端的握手接口(硬件或者软件握手接口)请求的single或者burst transaction中的最后的AHB传输完成后将产生中断
3.IntErr 错误中断
当在一个DMA传输中.从AHB slave处在HRESP总线接收一个错误的响应时,将产生这个中断,DMA传输将取消,通道也禁用.
4.IntSrcTran 源transaction完成中断
当位于源端的握手接口(硬件或者软件握手接口)请求的single或者burst transaction中的最后的AHB传输完成后将产生中断
注:如果操作的是内存 就应该忽略IntSrcTran/IntDstTran中断,因为对于内存来说.这里是没有”DMA transaction”概念的.
5.IntTfr DMA传输完成中断
当DMA向目的的外设传输完成时,会产生这个中断.
Raw :未经处理的
单块(single-block)传输配置
1. 首先使能DmaCfgReg寄存器的Bit 0,将该位置1,来使能DMAC。
2. 然后,选择一个目前没有使用的通道(channel),例如使用channel0。
3. 通过写以下寄存器:ClearTfr,ClearBlock,ClearSrcTran,ClearDstTran,和ClearErr来清除上次DMA传输可能产生的中断。
4. 通过写入SAR0寄存器,来配置DMAC所要进行传输的源端的起始地址;通过写入DAR0寄存器,来配置DMAC所要进行传输的目的端的起始地址。
5. 接下来配置CTL0寄存器,注意CTL0寄存器中的几个关键field。
l BLOCK_TS:该field决定了DMA所要传输的BLOCK的大小,例如将该field配置为0x18,同时源端的位宽是64bit,则表示DMA将会从源端传输24个64bit位宽的数据。
l SMS:该field决定了源端位于DMA的哪个master端口,对于目前的系统架构,只有DDR2是位于DMA的master1端口,其它设备位于DMA的master2端口。
l DMS:该field决定了目的端口是位于DMA的哪个master端口。
l TT_FC:此field决定了此次传输的类型,是memory到memory传输,还是memory到peripheral传输,或者其它情况,可以参照对应的编码表来具体选择。
l SINC:该field决定了源端传输数据的地址是递增,还是递减,或者不变。一般,外设的地址是不变的,memory的地址是递增,或者递减的。
l DINC:该field决定了目的端传输数据的地址是递增,还是递减,或者不变。
l SRC_TR_WIDTH:该field决定了源端的端口宽度,根据具体情况来确定。
l DST_TR_WIDTH:该field决定了目的端的端口宽度,根据具体情况来确定。
l 由于此处是single block传输下的配置,CTL0寄存器中的LLP_SRC_EN,LLP_DST_EN fields要置0
l 如果希望中断使能,则在INT_EN中使能该位
6. 下面配置CFG0寄存器,有下面的一些关键field:
l DEST_PER:如果目的端是外设,则需要使用hardware handshaking interface,此处是该外设对应的hardware handshaking interface接口号,每个外设的接口号都是唯一的,互不相同的。至于外设具体接到哪个接口号上,需要根据具体的硬件连接来确定。
l SRC_PER:如果源端是外设,则需要使用hardware handshaking interface,此处是该外设对应的hardware handshaking interface接口号,每个外设的接口号都是唯一的,互不相同的。至于外设具体接到哪个接口号上,需要根据具体的硬件连接来确定。
l HS_SEL_SRC:如果源端是外设,且需要hardware handshaking interface的情况下,则该位需要置0,来使能hardware handshaking interface。
l HS_SEL_DST:如果目的端是外设,且需要hardware handshaking interface的情况下,则该位需要置0,来使能hardware handshaking interface。
l 由于此处是single block传输下的配置,CFG0寄存器中的RELOAD_SRC,RELOAD_DST fields要置0
l 寄存器中的其它fields可以选用默认值
7. 由于此处不需要使用LLP0寄存器,可以将LLP0寄存器全部置0。
8. 如果希望产生中断,在使能CTL.INT_EN的基础上,将Mask寄存器(MaskBlock, MaskDstTran, MaskErr, MaskSrcTran, MaskTfr)中的屏蔽位不使能。
注意:
如果在DMA的传输中,通道的源端是memory的时候,应该屏蔽source transaction complete中断,即将MaskSrcTran寄存器的对应位屏蔽使能。
如果在DMA传输中,通道的目的端是memory的时候,应该屏蔽destination transaction complete中断,即将MaskDstTran寄存器的对应位屏蔽使能。
9. 最后配置ChEnReg,来使能channel0。注意其中的CH_EN_WE field与CH_EN要对应,并通过写1来使能对应的channel.