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

作者

QQ群:852283276
微信:arm80x86
微信公众号:青儿创客基地
B站:主页 https://space.bilibili.com/208826118

FPGA设计

参考我的博客:Altera FPGA PCIe Avalon-MM DMA设计

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

地址偏移寄存器访问权限描述
0x00RC Read Status and Descriptor Base (Low)R/W低32位,在设置高32位之后设置,地址必须32位对齐,在传输完成的时候才能改变这个寄存器
0x04RC Read Status and Descriptor Base (High)R/W高32位
0x08EP Read Descriptor FIFO Base (Low)RW低32位,指定存储描述符的FIFO地址,在设置高32位之后设置
0x0CEP Read Descriptor FIFO Base (High)RW高32位
0x10RD_DMA_LAST_PTRRW读返回上次操作的描述符ID,如果没有DMA操作则返回0xFF,指定最后一个操作的描述符ID发起DMA,比如,读返回4,为了传输5个描述符,软件应该写入9
0x14RD_TABLE_SIZERW设置读描述符表的大小,值为描述符数量减1,默认为127
0x18RD_CONTROLRW高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处,

地址偏移寄存器描述
0x00RD_LOW_SRC_ADDR低32位,DMA源地址,PCIe地址空间
0x04RD_HIGH_SRC_ADDR高32位
0x08RD_CTRL_LOW_DEST_ADDR低32位,Avalon-MM地址空间
0x0CRD_CTRL_HIGH_DEST_ADDR高32位
0x10CONTROL[31:25] Reserved,必须为0
[24:18] ID,描述符ID,0-127
[17:00] SIZE,传输大小,以DWORD单位,最大传输大小是1MB-4bytes,超过最大传输大小,按最大值传输,否则传输设置值,这个字段最大值为0x40000-0x1
0x14-0x1CReservedN/A

windows DMA编程

分配Descriptor内存,

    NTSTATUS status = WdfCommonBufferCreate(engine->parentDevice->dmaEnabler, bufferSize,
                                            WDF_NO_OBJECT_ATTRIBUTES, &engine->descBuffer);
    if (!NT_SUCCESS(status)) {
        TraceError(DBG_INIT, "WdfCommonBufferCreate failed: %!STATUS!", status);
        return status;
    }

    PHYSICAL_ADDRESS descBufferLA = WdfCommonBufferGetAlignedLogicalAddress(engine->descBuffer);
    PUCHAR descBufferVA = (PUCHAR)WdfCommonBufferGetAlignedVirtualAddress(engine->descBuffer);
    RtlZeroMemory(descBufferVA, bufferSize);

申请MSI/MSI-X中断,

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

申请DMA,这里设置了ADMA_MAX_TRANSFER_SIZE,

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

申请读/写队列,

	WDF_IO_QUEUE_CONFIG_INIT(&config, WdfIoQueueDispatchSequential);
	config.EvtIoWrite = EvtIoWriteDma;
	//config.EvtIoRead = EvtIoReadDma;
	WDF_OBJECT_ATTRIBUTES_INIT(&attribs);
	attribs.SynchronizationScope = WdfSynchronizationScopeQueue;
	WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&attribs, QUEUE_CONTEXT);
	status = WdfIoQueueCreate(device, &config, &attribs, queue);
	if (!NT_SUCCESS(status)) {
		TraceError(DBG_INIT, "WdfIoQueueCreate failed %d", status);
		return status;
	}

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

    status = WdfDmaTransactionInitializeUsingRequest(queue->engine->dmaTransaction, Request,
                                                     ADMA_EngineProgramDma,
                                                     WdfDmaDirectionReadFromDevice);
    if (!NT_SUCCESS(status)) {
        TraceError(DBG_IO, "WdfDmaTransactionInitializeUsingRequest failed: %!STATUS!",
                   status);
        goto ErrExit;
    }
    status = WdfDmaTransactionExecute(queue->engine->dmaTransaction, queue->engine);
    if (!NT_SUCCESS(status)) {
        TraceError(DBG_IO, "WdfDmaTransactionExecute failed: %!STATUS!", status);
        goto ErrExit;
    }

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

    for (ULONG i = 0; i < SgList->NumberOfElements; i++) {
        descriptor[i].control = XDMA_DESC_MAGIC;
        descriptor[i].numBytes = SgList->Elements[i].Length;
        ULONG hostAddrLo = SgList->Elements[i].Address.LowPart;
        LONG hostAddrHi = SgList->Elements[i].Address.HighPart;
        if (Direction == WdfDmaDirectionWriteToDevice) {
            // source is host memory
            descriptor[i].srcAddrLo = hostAddrLo;
            descriptor[i].srcAddrHi = hostAddrHi;
            descriptor[i].dstAddrLo = LIMIT_TO_32(deviceOffset);
            descriptor[i].dstAddrHi = LIMIT_TO_32(deviceOffset >> 32);
        } else {
            // destination is host memory
            descriptor[i].srcAddrLo = LIMIT_TO_32(deviceOffset);
            descriptor[i].srcAddrHi = LIMIT_TO_32(deviceOffset >> 32);
            descriptor[i].dstAddrLo = hostAddrLo;
            descriptor[i].dstAddrHi = hostAddrHi;
        }
    }
├─reference_design │ ├─vhdl │ │ │ stratix_enh_pll.vhd │ │ │ stratix_top.vhd │ │ │ vhdl_components.vhd │ │ │ │ │ ├─001 ddr_cntrl │ │ │ ddr_top.vhd │ │ │ │ │ ├─004 pci_local │ │ │ backend.vhd │ │ │ cnten.vhd │ │ │ datapath_fifo.vhd │ │ │ dma.vhd │ │ │ dma_reg.vhd │ │ │ dma_sm.vhd │ │ │ fifo_128x32.vhd │ │ │ fifo_128x4.vhd │ │ │ fifo_128x64.vhd │ │ │ last_gen.vhd │ │ │ mstr_cntrl.vhd │ │ │ mstr_fifo_cntrl.vhd │ │ │ mstr_perf.vhd │ │ │ targ_cntrl.vhd │ │ │ targ_fifo_cntrl.vhd │ │ │ targ_perf.vhd │ │ │ │ │ ├─002 ddr_intf │ │ │ adr_gen.vhd │ │ │ clk_sync.vhd │ │ │ cntrl_intf.vhd │ │ │ ddr_intf.vhd │ │ │ mr_sm.vhd │ │ │ mw_sm.vhd │ │ │ tr_sm.vhd │ │ │ tw_sm.vhd │ │ │ │ │ ├─003 flash_cntrl │ │ │ erase_sm.vhd │ │ │ flash_mem_cntrl.vhd │ │ │ read_sm.vhd │ │ │ write_sm.vhd │ │ │ │ │ └─005 pci_mt64 │ │ pci_top.vhd │ │ │ ├─001 sim │ │ │ modelsim.ini │ │ │ sim.do │ │ │ stratix_pci2ddr.mpf │ │ │ stratix_pci2ddr_tb.vhd │ │ │ trgt_tranx_mem_init.dat │ │ │ wave_stratix_pciddr.do │ │ │ │ │ ├─001 altera_lib │ │ │ altera_mf.vhd │ │ │ │ │ ├─002 ddr_dimm │ │ │ ddr_dimm_model.vhd │ │ │ mt46v32m8.vhd │ │ │ │ │ └─003 pci_bfm │ │ arbiter.vhd │ │ clk_gen.vhd │ │ log.vhd │ │ monitor.vhd │ │ mstr_pkg.vhd │ │ mstr_tranx.vhd │ │ pull_up.vhd │ │ trgt_tranx.vhd │ │ │ └─002 syn_1s25 │ stratix_top.csf │ stratix_top.esf │ stratix_top.psf │ stratix_top.quartus │ stratix_top.rbf │ stratix_top.sof │ ├─001 bin │ altera.inf │ megaicon.ico │ StratixPCI.exe │ STRATIX_KIT_APP_HELP.HLP │ wdreg.exe │ windrvr6.inf │ windrvr6.sys │ ├─002 constraints │ mt32_23_ep1s25f1020c5_66_03_04.tcl │ mt64_23_ep1s25f1020c5_66_03_04.tcl │ Stratix_PCI_Board_DDR_settings.tcl │ t32_23_ep1s25f1020c5_66_03_04.tcl │ ├─003 doc │ an223.pdf │ banner.jpg │ ds_StratixPciBd.pdf │ readmeStratixPciKit.htm │ StratixPciKitDocContents.pdf │ ug_StratixPciKit.pdf │ ├─004 max_config │ max_stratix_config.pof │ └─006 software ├─001 driver │ altera_lib.c │ altera_lib.h │ └─002 gui altera.aps altera.clw altera.cpp altera.dsp altera.dsw altera.h altera.ncb altera.odl altera.opt altera.plg altera.rc Altera.rgs alteraDlg.cpp alteraDlg.h alteralogo.bmp CBox.cpp CBox.h Ioctl.h megaicon.ico Meter.cpp Meter.h MonWnd.cpp MonWnd.h resource.h resource.hm StdAfx.cpp StdAfx.h
1 数据表 .............................................................................................................................. 5 1.1 Arria10 Avalon-MM DMA 接口PCIe 数据表................................................................... 5 1.2 特性..................................................................................................................... 6 1.3 比较Avalon-ST,Avalon-MM 和支持DMA 的Avalon-MM 接口............................................ 7 1.4 发布信息 ................................................................................................................9 1.5 器件系列支持 .......................................................................................................... 9 1.6 设计实例.................................................................................................................9 1.7 调试功能.................................................................................................................9 1.8 IP Core 验证 .........................................................................................................10 1.8.1 兼容性测试环境 ..........................................................................................10 1.9 性能和资源利用 ......................................................................................................10 1.10 建议的速度等级 .................................................................................................... 10 1.11 创建PCI Express 设计........................................................................................... 11 2 Avalon-MM DMA 入门 ...................................................................................................... 13 2.1 生成Testbench ..................................................................................................... 14 2.1.1 了解仿真已生成文件 .................................................................................... 15 2.1.2 了解仿真日志文件生成.................................................................................. 16 2.2 在ModelSim 中仿真实例
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值