版权声明:本文为CSDN博主「黑客三遍猪」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Zhu_Zhu_2009/article/details/80790252

Altera pcie-avmm dma IP寄存器
DMA Descriptor Controller Registers
DMA控制器读写均支持最多128个描述符,读写操作是以FPGA视角来看,读操作是从PCIe地址空间到FPGA Avalon-MM地址空间,写操作是从FPGA Avalon-MM地址空间到PCIe地址空间。
在DMA控制器寄存器里设置描述符表位于在PCIe地址空间里的地址和大小,DMA控制器用Read Data Mover首先将描述符复制到自己内部的FIFO中,然后在根据描述符来开始DMA传输。描述符在RC内的地址必须是32字节对齐的。
DMA控制器有寄存器指示读写描述符的完成状态,读和写分别有自己的状态寄存器表,每个表有128个连续的DWORD项,对应128个描述符。状态字占用512字节,位置在RC Read Status and Descriptor Base指定的地址偏移0处,而实际的描述符在0x200偏移处,DMA控制器项状态字的done位写1表示传输成功,DMA控制器在完成最后一个描述符后会发送一个MSI中断,在接收到中断之后,主机host软件可以轮询done位来判断描述符状态,但是DMA控制器不会设置done位或者发送MSI在每一个描述符完成的时候,它根据RD_DMA_LAST PTR和WR_DMA_LAST_PTR寄存器存储的描述符ID来操作,由于描述符支持PCIe完成包的乱序传输,所以done位置位的时候,描述符可能还没有传输完成。例如想在128个描述符的传输中间时刻和完成时候获得通知:

1、写入RD_DMA_LAST_PTR值63。
2、写入RD_DMA_LAST_PTR值127。
3、轮询第63个状态字。
4、轮询第127个状态字。

Read DMA Descriptor Controller Registers

地址偏移 寄存器 访问权限 描述
0x00 RC Read Status and Descriptor Base (Low) RW 低32位,在设置高32位之后设置,地址必须32位对齐,在传输完成的时候才能改变这个寄存器
0x04 RC Read Status and Descriptor Base (High) RW 高32位
0x08 EP Read Descriptor FIFO Base (Low) RW 低32位,指定存储描述符的FIFO地址,在设置高32位之后设置
0x0C EP Read Descriptor FIFO Base (High) RW 高32位
0x10 RD_DMA_LAST_PTR RW 读返回上次操作的描述符ID,如果没有DMA操作则返回0xFF,指定最后一个操作的描述符ID发起DMA,比如,读返回4,为了传输5个描述符,软件应该写入9
0x14 RD_TABLE_SIZE RW 设置读描述符表的大小,值为描述符数量减1,默认为127
0x18 RD_CONTROL RW 高31位保留,第0位设置上报每一个描述符的done位,但MSI都不会每次上报,否则根据RD_DMA_LAST_PTR来上报

Write DMA Descriptor Controller Registers

和读一样,地址偏移在0x100。

Read DMA and Write DMA Descriptor Format

每个描述符32字节,Read/Write Status and Descriptor Base + 0x200处。

地址偏移 寄存器 描述
0x00 RD_LOW_SRC_ADDR 低32位,DMA源地址,PCIe地址空间
0x04 RD_HIGH_SRC_ADDR 高32位
0x08 RD_CTRL_LOW_DEST_ADDR 低32位,Avalon-MM地址空间
0x0C RD_CTRL_HIGH_DEST_ADDR 高32位
0x10 CONTROL

[31:25] Reserved,必须为0
[24:18] ID,描述符ID,0-127
[17:00] SIZE,传输大小,以DWORD单位,最大传输大小是1MB-4bytes,超过最大传输大小,按最大值传输,否则传输设置值,这个字段最大值为0x40000-0x1

0x14~0x1C Reserved N/A

windows DMA编程

分配Descriptor内存,

 1     NTSTATUS status = WdfCommonBufferCreate(engine->parentDevice->dmaEnabler, bufferSize,
 2                                             WDF_NO_OBJECT_ATTRIBUTES, &engine->descBuffer);
 3     if (!NT_SUCCESS(status)) {
 4         TraceError(DBG_INIT, "WdfCommonBufferCreate failed: %!STATUS!", status);
 5         return status;
 6     }
 7
 8     PHYSICAL_ADDRESS descBufferLA = WdfCommonBufferGetAlignedLogicalAddress(engine->descBuffer);
 9     PUCHAR descBufferVA = (PUCHAR)WdfCommonBufferGetAlignedVirtualAddress(engine->descBuffer);
10     RtlZeroMemory(descBufferVA, bufferSize);

申请MSI/MSI-X中断,

1     NTSTATUS status = WdfInterruptCreate(xdma->wdfDevice, &config, &attribs,
2                                          &(xdma->channelInterrupts[index]));
3     if (!NT_SUCCESS(status)) {
4         TraceError(DBG_INIT, "WdfInterruptCreate failed: %!STATUS!", status);
5     }

申请DMA,这里设置了ADMA_MAX_TRANSFER_SIZE,

1     WdfDeviceSetAlignmentRequirement(adma->wdfDevice, FILE_32_BYTE_ALIGNMENT); //add by zhuce
2     WDF_DMA_ENABLER_CONFIG dmaConfig;
3     WDF_DMA_ENABLER_CONFIG_INIT(&dmaConfig, WdfDmaProfileScatterGather64Duplex, ADMA_MAX_TRANSFER_SIZE);
4     status = WdfDmaEnablerCreate(adma->wdfDevice, &dmaConfig, WDF_NO_OBJECT_ATTRIBUTES, &adma->dmaEnabler);
5     if (!NT_SUCCESS(status)) {
6         TraceError(DBG_INIT, " WdfDmaEnablerCreate() failed: %!STATUS!", status);
7         return status;
8     }

申请读/写队列,

 1     WDF_IO_QUEUE_CONFIG_INIT(&config, WdfIoQueueDispatchSequential);
 2     config.EvtIoWrite = EvtIoWriteDma;
 3     //config.EvtIoRead = EvtIoReadDma;
 4     WDF_OBJECT_ATTRIBUTES_INIT(&attribs);
 5     attribs.SynchronizationScope = WdfSynchronizationScopeQueue;
 6     WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&attribs, QUEUE_CONTEXT);
 7     status = WdfIoQueueCreate(device, &config, &attribs, queue);
 8     if (!NT_SUCCESS(status)) {
 9         TraceError(DBG_INIT, "WdfIoQueueCreate failed %d", status);
10         return status;
11     }

发起DMA,ADMA_EngineProgramDma作为回调函数,超过ADMA_MAX_TRANSFER_SIZE,传输会被分多次,WDF为我们做了很多工作,这些我们都是看不到的。

 1     status = WdfDmaTransactionInitializeUsingRequest(queue->engine->dmaTransaction, Request,
 2                                                      ADMA_EngineProgramDma,
 3                                                      WdfDmaDirectionReadFromDevice);
 4     if (!NT_SUCCESS(status)) {
 5         TraceError(DBG_IO, "WdfDmaTransactionInitializeUsingRequest failed: %!STATUS!",
 6                    status);
 7         goto ErrExit;
 8     }
 9     status = WdfDmaTransactionExecute(queue->engine->dmaTransaction, queue->engine);
10     if (!NT_SUCCESS(status)) {
11         TraceError(DBG_IO, "WdfDmaTransactionExecute failed: %!STATUS!", status);
12         goto ErrExit;
13     }

回调函数,填入描述符表。

 1     for (ULONG i = 0; i < SgList->NumberOfElements; i++) {
 2         descriptor[i].control = XDMA_DESC_MAGIC;
 3         descriptor[i].numBytes = SgList->Elements[i].Length;
 4         ULONG hostAddrLo = SgList->Elements[i].Address.LowPart;
 5         LONG hostAddrHi = SgList->Elements[i].Address.HighPart;
 6         if (Direction == WdfDmaDirectionWriteToDevice) {
 7             // source is host memory
 8             descriptor[i].srcAddrLo = hostAddrLo;
 9             descriptor[i].srcAddrHi = hostAddrHi;
10             descriptor[i].dstAddrLo = LIMIT_TO_32(deviceOffset);
11             descriptor[i].dstAddrHi = LIMIT_TO_32(deviceOffset >> 32);
12         } else {
13             // destination is host memory
14             descriptor[i].srcAddrLo = LIMIT_TO_32(deviceOffset);
15             descriptor[i].srcAddrHi = LIMIT_TO_32(deviceOffset >> 32);
16             descriptor[i].dstAddrLo = hostAddrLo;
17             descriptor[i].dstAddrHi = hostAddrHi;
18         }
19     }

转载于:https://www.cnblogs.com/tubujia/p/11430213.html

[转]windows驱动开发-基于WDF的Altera PCIe DMA驱动相关推荐

  1. windows驱动开发-基于WDF的Altera PCIe DMA驱动

    作者 QQ群:852283276 微信:arm80x86 微信公众号:青儿创客基地 B站:主页 https://space.bilibili.com/208826118 FPGA设计 参考我的博客:A ...

  2. STM32MP157驱动开发——Linux自带的LED灯驱动

    STM32MP157驱动开发--Linux自带的LED灯驱动 0.前言 一.Linux 内核自带 LED 驱动使能 二.驱动简介 1.LED灯驱动框架分析 2.module_platform_driv ...

  3. linux驱动开发篇(三)—— 总线设备驱动模型

    linux系列目录: linux基础篇(一)--GCC和Makefile编译过程 linux基础篇(二)--静态和动态链接 ARM裸机篇(一)--i.MX6ULL介绍 ARM裸机篇(二)--i.MX6 ...

  4. 基于WDF的PCI/PCIe接口卡Windows驱动程序(3)- 驱动程序代码(头文件)

    原文出处:http://www.cnblogs.com/jacklu/p/4679304.html 如果你觉得这篇博客对你的项目有用,请引用以下论文: Meng Shengwei, Lu Jianji ...

  5. linux驱动开发音频设备驱动,linux驱动开发—基于Device tree机制的驱动编写

    摘要:媒介 Device Tree是一种用去描绘硬件的数据布局,类似板级描绘说话,发源于OpenFirmware(OF).正在现在遍及应用的kernel 2.6.x版本中,对分歧仄台.分歧硬件,往] ...

  6. OpenHarmony HDF LED驱动开发 基于小熊派Micro

    文章目录 一.效果展示 二.led控制程序 2.1.led驱动程序 2.1.1.驱动程序 2.1.2.驱动配置 2.2.C应用程序 2.3.JS应用 2.3.1.JS代码 2.3.2.C++接口 一. ...

  7. Linux字符型驱动开发—基于友善之臂2416开发板

    驱动程序(Device Driver)是一种可以使计算机和设备通信的特殊程序,相当于内核和硬件之间的接口,操作系统只能通过这个接口,才能控制硬件设备的工作.驱动程序接受上层软件(应用程序.内核)的请求 ...

  8. [linux驱动开发] 基于gpiod API的platform总线多个led驱动开发

    gpiod API对platform-led进行驱动开发 修改设备树源码 如何在驱动中获取设备树节点信息 计算设备子节点数量 给私有属性分配内存 对子节点进行遍历 gpiod的获取 根据设备树字节给的 ...

  9. Linux 下wifi 驱动开发(三)—— SDIO接口WiFi驱动浅析

    SDIO-Wifi模块是基于SDIO接口的符合wifi无线网络标准的嵌入式模块,内置无线网络协议IEEE802.11协议栈以及TCP/IP协议栈,能够实现用户主平台数据通过SDIO口到无线网络之间的转 ...

最新文章

  1. char* p = 123,字符串在内存中的哪个位置?
  2. Java中的构造函数和重载
  3. 百练OJ:2965:玛雅历
  4. pandas算加权平均值_4000 字详解TCP超时与重传,看完没收获算我输
  5. 布隆过滤器(Bloom Filter)的原理和实现
  6. 2020/5/13号单词
  7. CentOS 7.2.1511 x64下载地址
  8. mariadb数据库增删改查
  9. 第九篇:Spring Boot整合Spring Data JPA_入门试炼04
  10. 计算机设计大赛国奖作品_4. 界面设计
  11. 语义信息增强的激光雷达SLAM
  12. 小勇机器人充电头坏了_崇安区管道机器人CCTV-管道爬行机器人,武汉天仪ty333,优质效率高...
  13. xmind电脑版免费_有哪些免费好用的电脑版剪辑软件?
  14. 耐福-NTP8849音频功放芯片有哪些功能?
  15. java护眼的颜色_爱护眼睛,从IDEA开始,护眼色设置走起-护眼设置
  16. 使用企业微信做微信消息通知
  17. 今天是星期五,上班已经三个礼拜了
  18. 沉默的大多数(王小波)
  19. 简单教学管理系统画E-R关系图
  20. controller 之@...

热门文章

  1. 物联网国赛LORA模块开发基础教程(通用库)—输出(LED)
  2. SUN 和Oracle公司
  3. 信息系统工程监理单位资质管理办法
  4. Eureka 集群启动报错
  5. oracle exp lrm00109,oracle11g ORA-01078 LRM-00109错误
  6. c 语言cad 二次开发,cad编程语言(AUTO CAD 得二次开发语言及工具)
  7. 5天学会Linux(实操练手+最全教程) Day1 环境搭建
  8. 打印机驱动程序无法使用怎么办?
  9. 数据分析师不能不知道的5种数据分析方法,解决90%分析难题!
  10. 济宁中考计算机考试试题,济宁市初中信息技术考试模拟题1.pdf