前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >PCIe的XDMA应用

PCIe的XDMA应用

作者头像
根究FPGA
发布2020-06-30 11:08:30
4.1K0
发布2020-06-30 11:08:30
举报
文章被收录于专栏:根究FPGA根究FPGA

之前介绍的PCIe实物模型为PIO模式,可编程PIO模式,软件控制CPU在主机总线上发起一个存储器或IO读写总线周期,并以映射在PCIe设备地址空间的一个地址为目标,根据PCIe总线宽度的区别,在每个时钟周期内可以传输4个或者8个字节的数据。传输效率低且占用CPU周期。

另一种PCIe实物模型为DMA模型,直接存储器读取方式实现PCIe设备与系统存储器之间的数据传送,这种传输放大效率较高,因为在数据传送过程中不需要CPU参与,且传送一个数据只需要一个突发总线周期。

一、XDMA相关知识

绝对地址就是物理地址=段地址*16+偏移地址,也就是段地址<<4+偏移地址

主机host通过PCIe接口访问DMA,DMA即外部设备不通过CPU而直接与系统内存(DDR)交换数据。

PIO模式下硬盘和内存之间的数据传输是通过CPU来控制的,而在DMA模式下,CPU只需向DMA控制下达命令,让DMA来控制数据的发送,数据传送完毕后再把数据反馈给CPU,这样很大程度上减轻了 CPU的资源占有率。

DMA和PIO模式的区别就在于,DMA模式不过分依赖CPU,可以大大的节省系统资源,二者在传输速度上的差异并不明显。

PIO模式:CPU通过执行端口IO指令来进行数据读写的交换方式。CPU占有率高。

在例程中数据传输使用XDMA方式,与DMA相同,CPU通过向DMA发送指令完成数据的读写。

读写部分分为两种,一种是数据的读写,另一种是配置数据的读写,在数据读写部分,DMA通过MIG控制DDR完成数据读写。配置数据读写通过与BRAM通过AXI-lite总线连接完成,XDMA将PCIe配置信息存在BRAM,在进行配置信息读写时,将传入主机映射到用户逻辑的地址,然后与偏移地址处理(物理地址=段地址<<4+偏移地址),所以在bram设置时需要将其偏移地址设置的与主机地址映射的偏移地址相同。

对于DDR则不必,设置的话还减少了可使用的内存空间,只是一个袋子,写在哪里就从哪里读取即可,必须设置为0。

XDMA ip core的配置:

1、 Basic

Functional mode:功能模式,即DMA模式。

Mode:模式,选择basic即可,basic与advanced的区别在于advanced模式开放更多的可选选项与功能,basic的话为默认。

Device/Port Type:选择设备与端口类型,为端点设备。

PCIe Block Location:从可用的集成块中选择,以启用生成特定位置的约束文件和输出,产品能够pg054datasheet截取的位置说明P249。

Lane width:通道宽度,根据接口进行选择。

AXI Address width:AXI地址宽度选择,只支持64bit(用户手册73页)。

AXI Data Width:AXI总线上传输的数据宽度,可以是64bit、128bit、256bit、512bit(这个只有ultrascale+可以满足,一分钱一分货),根据datasheet进行配置即可P249,见下图。

DMA Interface option:DMA的接口类型用于数据传输,有两种AXI MM(memory mapped)与AXI ST(stream),二选一。

PCIe ID:

见之前推送。

PCIe BARs:

PCIe to AXI Lite Master Interface:使能,这样可以在主机一侧通过PCIe来访问用户逻辑侧寄存器或者其他AXI-Lite总线设备。

此处将配置信息存储到BRAM,通过AXI-lite总线读写Bram。

(1)、BAR为32bit,不使能64bit,prefetchable表示预读取,不使能。

(2)、映射空间选择1M,大小随意。

(3)、PCIe to AXI Translation:主机侧BAR地址与用户逻辑侧地址不同,通过设置转换地址实现BAR地址到AXI地址的转换。比如主机一侧BAR地址为0,则主机访问BAR地址0转换到AXI-Lite总线就是0x8000_0000.

PCIe to DMA Interface:数据传输宽度64bit,DMA控制器一般只支持数据8字节对齐的情况。

当数据从上位机通过PCIe接口发送到端点设备,XDMA内部自行解包对将数据与指令进行分析,得到读写操作的指令地址,并对DDR进行读写操作。操作的结果通过AXI接口返回XDMA,XDMA对数据进行组包,之后通过物理层发出,实现数据的DMA控制。

二、上位机设计

2.1 h2c_transfer函数

该函数的作用是向DDR中写入数据,写入大小固定的数据,根据写入时间计算传输的速度(MB/s)。

1、变量定义

Double型变量bd,用于寄存传输速度。

Double型变量time_sec,用于保存传输定量数据所需时间。

LARGE_INTEGER型变量变量start和stop用于保存频率计数值。还有freq,用于保存机器内部计时器的时钟频率。

2、函数操作

2.1 获取传输所需时间

获取传输所需时间,则需要三个量:机器时钟频率,传输开始前后计数器的计数值。

获取机器内部计时器的时钟

QueryPerformanceFrequency(&freq);

QueryPerformanceCounter(&start);

…… //数据传输

QueryPerformanceCounter(&stop);

头文件为<Windows.h>,函数原型为:

BOOL QueryPerformanceFrequency(LARGE_INTEGER*lpFrequency);

BOOL QueryPerformanceCounter (LARGE_INTEGER*lpCount);

在定时前先调用QueryPerformanceFrequency函数获得机器内部计时器的时钟频率,然后在严格计时的时间发生前后调用QueryPerformanceCounter函数,获得两次计数的差值经过转换得到事件耗费的精准时间。

LARGE_INTEGER为一个typedef定义的变量,其原型为:

代码语言:javascript
复制
typeef union _LARGE_INTEGER
{
struct
{
DWORD LowPart;
LONG HighPart;
};
LONGLONGQuadPart;
} LARGE_INTEGER;

2.2 数据写入操作

使用的是自定义的write_device()函数,操作语句为:write_device(h2c0_device,FPGA_DDR_START_ADDR+0x20000000,size,h2c_align_mem_tmp);四个参数分别为:设备句柄,传输目的地址,传输的数据大小(单位为字节),写入数据的缓存区地址。

在write_device函数中,重点是变量与函数语句:

变量:

DWORD wr_size:用于保存写入的字节数

unsigned int transfers;传输次数,每次传输的最大字节数为0x800000,也就是8MB,根据要传输的字节数计算所需传输的次数。

函数语句:

数据的写入通过WriteFile()函数完成,操作语句为WriteFile(device,(void*)(buffer+i*MAX_BYTES_PER_TRANSFER),MAX_BYTES_PER_TRANSFER,&wr_size,NULL),传入参数分别为:系统文件句柄,写入数据的存储地址,每次传输的字节数,成功写入的字节数。

WriteFile()函数的返回值为bool类型,操作成功为true,操作失败返回-1,实际写入的字节数与设定的写入字节数不同时返回-2。

在数据写入之前,需要先设置一个文件在设备的对应地址中的操作位置,FILE_BEGIN表示在文件的起始位置接收传输的数据。

代码语言:javascript
复制
unsignedinth2c_transfer(unsignedintsize)
{
 doublebd=0;
 doubletime_sec;
 LARGE_INTEGERstart;
 LARGE_INTEGERstop;
 LARGE_INTEGERfreq;
 QueryPerformanceFrequency(&freq);
 QueryPerformanceCounter(&start);write_device(h2c0_device,FPGA_DDR_START_ADDR+0x20000000,size,h2c_align_mem_tmp);
 QueryPerformanceCounter(&stop);
 time_sec=(unsignedlonglong)(stop.QuadPart-start.QuadPart)/(double)freq.QuadPart;
 bd=((double)size)/(double)(time_sec)/1024.0/1024.0;
 return(unsignedint)bd;
}

2.3 数据读取操作

与数据写入类似,读取的起始地址为写入时的起始地址。

代码语言:javascript
复制
unsignedintc2h_transfer(unsignedintsize)
{
 doublebd=0;
 doubletime_sec;
 LARGE_INTEGERstart;
 LARGE_INTEGERstop;
 LARGE_INTEGERfreq;
 QueryPerformanceFrequency(&freq);
 QueryPerformanceCounter(&start);
read_device(c2h0_device,FPGA_DDR_START_ADDR+0x10000000,size,c2h_align_mem_tmp);
 QueryPerformanceCounter(&stop);
 time_sec=(unsignedlonglong)(stop.QuadPart-start.QuadPart)/(double)freq.QuadPart;
 bd=((double)size)/(double)(time_sec)/1024.0/1024.0;
 return(unsignedint)bd;
}

三、基于PCIe的下位机设计

3.1 简介

首先是选用一个AD转换器进行电压数据的采集,之后自定义一个AXI—Full类型的IP封装,调用XDMA ip core连接AXI封装,AXI封装将连接一个fifo模块,从fifo中读取出来的数据通过AXI-Full总线送至XDMA的AXI主接口,XDMA会将数据进行自动封装,封装成TLP包,等到上位机发出数据读取请求时候,将数据发送到上位机进行波形显示。

PCIe设计模块:

分析其信号:

1.1 输入信号

pkg_rdy: package ready信号,表示数据部分准备好进行读取。

pkg_data[63:0]: 64bit的数据输入。

pcie_ref:XDMA的驱动时钟输入,双极性,经过一个缓冲之后接入到XDMA ip core。

pcie_rst_n: XDMA复位控制。

1.2 输出信号

pkg_rd_en: 由AXI Slave core发出的读取fifo使能信号。

pcie_mgt: PCIe电气接口信号,TX与RX,7030为PCIe2.0,lane width:x2。

axi_aclk:AXI总线驱动时钟,该时用于fifo的数据读取。

Clk_50M:通过时钟功能核clocking Wizard生成,作用是驱动AD7606的驱动。

Rst_n:复位信号,控制AD驱动与fifo的复位。

3.2 项目设计

1.1 AD驱动设计

AD采集芯片为AD7606,按照数据与例程设计。

1.2 fifo使用

将AD采集得到的数据存储到fifo中,fifo的接口类型为本地native类型,fifo的实现方式为独立时钟的块ram,即数据的读取与写入使用独立的时钟,写入的数据宽度为64bit,是AD0~AD3的16bit数据拼接得到的64bit写入数据,写入深度为4096,数据读取同。引出写入数据的计数器,用于通知AXI-Salve core数据读取允许。

通过AD采集模块的数据采集完成标志作为fifo数据写入的使能信号,写入的数据个数通过Write Data Count来指示,以下为摘取自fifo datasheet中关于该信号的介绍:

Write Data Count:

Write Data Count: This bus indicates the number of words writteninto the FIFO. The count is guaranteed to never under-report the number ofwords in the FIFO, to ensure you never overflow the FIFO. The exception to thisbehavior is when a write operation occurs at the rising edge of wr_clk/clk,that write operation will only be reflected on wr_data_count at the next risingclock edge. If D is less than log2(FIFO depth)-1, the bus is truncated byremoving the least-significant bits.

Note: wr_data_count is also available for UltraScale devices using acommon clock Block RAM-based FIFO when the Asymmetric Port Width option isenabled.

注:datasheet真是个好东西,问啥告诉啥!

使用fifo的半满信号作为AXI Slaveip core的包准备好信号。

AXI Slave ip core在输入的包读取使能信号(pkg_rdy)之后,将自身的valid信号置一,等待AXI总线可以进行数据读取,输入数据为fifo的内部数据。

四、上位机设计

上位机显示使用了QWT环境,QWT环境的设置可以参考博文:https://www.cnblogs.com/luxinshuo/p/12316037.html。

QT中的程序最重要的一点为设置一个定时器,定时器定时满后关联数据更新槽函数。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-02-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 根究FPGA 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、XDMA相关知识
  • 二、上位机设计
    • 2.1 h2c_transfer函数
      • 2.2 数据写入操作
        • 2.3 数据读取操作
        • 三、基于PCIe的下位机设计
          • 3.1 简介
            • 1.1 输入信号
            • 1.2 输出信号
          • 3.2 项目设计
            • 1.1 AD驱动设计
            • 1.2 fifo使用
        • 四、上位机设计
        相关产品与服务
        数据保险箱
        数据保险箱(Cloud Data Coffer Service,CDCS)为您提供更高安全系数的企业核心数据存储服务。您可以通过自定义过期天数的方法删除数据,避免误删带来的损害,还可以将数据跨地域存储,防止一些不可抗因素导致的数据丢失。数据保险箱支持通过控制台、API 等多样化方式快速简单接入,实现海量数据的存储管理。您可以使用数据保险箱对文件数据进行上传、下载,最终实现数据的安全存储和提取。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档