目录
前言
1、概述
1.1PCIE 学习入门概述
1.2 本文内容概述
2、IP CORE user interface接口说明
3、TLP包格式
3.1 、3DW/4DW相关说明
3.2 、TLP报文格式
3.3、CplD报文格式
3.4、TLP包解析示例
4、Example PIO 示例
4.1 Example PIO 架构
4.2 接收/发送引擎代码分析
参考文献
首先说明一下,本文是个人的学习和理解,为了方便自己时长温习而进行整理。
在本文最后,附上了参考的博客和书籍。参考文献 1-6 写的非常详细、很有深度,强烈建议逐一阅读原文!
推荐阅读《PCI Express 系统体系结构标准教材》[美]Ravi Budruk 等人著,田玉敏 等人译。
至于我写的博文,但当涉猎、浅尝辄止即可,还请读者多看看参考文献吧!
鉴于我也是一名初学者,本文在一些方面可能表述的不是很完整,还请谅解。以后,会不定期对本文进行补充和完善。
感谢读者的期待,学习之余互相分享、互相借鉴吧!
相关博客推荐阅读:
六、Xilinx PCIE DMA--Sparten6/Kintex-7 BMD
七、Xilinx PCIE DMA 仿真环境搭建
八、win10 jungo windriver
首先,关于如何入门PCIE做简单介绍吧。下面仅是笔者自己的学习经验,仅供参考!!!!
然后,明确一下PCIE的学习目标吧!
阶段一:理解掌握TLP报文格式;
阶段二:自己建工程,使用ISE14.4 generate一个PCIE IP core,分析其example PIO工程的状态转移等,并运行do sim.do运行modelsim仿真;
阶段三:基于xapp1052,分析其提供的128示例工程,分析其接收、发送、存储模块;并运行do sim.do,看看其PIO仿真。
阶段四:基于xapp1052提供的BMD文件,自己搭建DMA工程;搭建DMA仿真环境,之后,运行do sim.do,看看其PIO仿真。若pio仿真pass,则说明DMA工程工程搭建基本正确。
阶段五:基于xapp1052提供的PC端软件驱动,基于阶段四搭建的DMA工程,进行板卡测试;
板卡依次测试PIO功能,测试DMA功能。
在该阶段,需要自己写写PC端的PCIE驱动程序,这一过程或许是痛苦的,但是很有必要!
最后,详细学习过程如下:
(1)理解掌握TLP报文格式:3DW/4DW报文头格式;完成报文CplD报文格式。这部分资料,网上有很多。
(2)自己建一个工程,分析一下其example_design 中的PIO设计。可以参考本文最后的参考文献1.。这部分难度不大。
(3)使用modelsim 运行do sim.do,分析一下具体的PIO传输;分析一下官方的PIO仿真是如何搭建仿真环境的。这部分有点难度,也很有用,挺重要的。花点时间认真分析吧!
在深入理解PIO代码及testbench仿真环境搭建之后,可以开始学习DMA操作。
(4)以xilinx官方的xapp1052作为学习材料,分析其DMA代码,状态转移。这部分,仅是分析代码和状态机,难度不大。
(5)基于xapp1052 BMD文件自己搭建一个DMA工程,使用modelsim运行PIO仿真。若是过了的话,则说明DMA工程搭建基本正确。
(6)搭建DMA的仿真环境,使用modelsim 进行DMA仿真。这部分有难度,不好弄。弄不出来就可以跳过了。
(7)基于xapp1052提供的PC调试驱动软件,以及步骤5搭建的DMA工程,进行板卡测试。
在进行DMA的学习时,PC端的软件驱动,还是有必要写写的!在PC端实现如何开辟缓存、与PCIE卡进行通信等。
上述,仅是个人的学习计划安排,本文不会全部介绍。
关于PCIE的基础理论概念,可以参考文献2、4、5。
本文以Xilinx ISE K7芯片 PCIE IP core 生成的user design 作为学习例程。
vivado里关于PCIE 的IP CORE与ISE里 PCIE的IP core,其user design 逻辑一致。
本文主要内容包括:
(1)PCIE IP CORE 用户接口说明;
(2)TLP报文格式,包含:3DW/4DW的区别、TLP报文格式、TLP完成报文格式、TLP报文解析示例;
(3)IP CORE自带的example 工程简要分析。
IP CORE的generate过程,参考百度等即可,本文不做介绍。
IP core 的实例化,本文不做介绍。
本文主要目的,是在了解上述1-3的基础上,让读者对PCIE通信有个大致的概念和了解,对数据流的传输有个整体的概念。
想要对PCIE更加深入的了解的话,请认真阅读文后的参考文献!
本文正在整理中,有些说明不到位,望理解。
事务层包(TLP),数据链路层包(DLLP),物理层(PLP)产生于各自所在层,最后通过电或光等介质和另一方通讯。其中数据链路层包(DLLP),物理层(PLP)的包平常不需要关心,在 IP 核中封装好了。
在 FPGA 上做 PCIe 的功能,变成完成事务层包(TLP)的处理,即可!
PCIe板卡访问PC内存时,板卡向 PC 发送 TLP 包,例如 MWr 包,地址信息就是PC 的物理地址;如果是 MRd 包,那 PC 收到后回复一个完成包,板卡从完成包分析出数据即得到 MRd 读取地址的数据。这是PCIe板卡访问PC.
PC访问PCIe板卡,简单的解释,PC 启动是,BIOS 探测所有的外设。对 PCIe (PCI)设备来说,BIOS 检测到板卡有多少个 BAR 空间,每个空间有多大,然后对应为这些 BAR 空间分配地址。对 PC 设备来说,它能“看”到 PCIe 板卡的空间只有 BAR 空间,也就只能访问这些 BAR 空间。也就是说,板卡可以发送合法的 PCIe TLP 包,并得到 PC 端的相应;但是 PC 端访问板卡被局限在 BAR 空间。
下面举例(参考https://zhuanlan.zhihu.com/p/34096340)
CPU读取一个PCIE设备的memory
在PCIE的拓扑结构中,有一个非常重要的结构,它就是Root Complex(RC)结构。顾名思义,它负责将几个不同的总线协议聚合在一起,如内存的DDR总线,处理器的前端总线Front Side Bus(FSB)。在PCIE中,CPU的操作实际是由RC代替完成的,所以一定程度上也可以讲RC代表CPU。
所以当CPU想要访问Endpoint时
Step1:CPU让RC产生一个MRd,经过Switch A,Switch B(第一篇讲到过Switch),到达Endpoint。
Step2: Endpoint 接受数据包,进行数据读取。
Step3:Endpoint返回一个带有数据的Completion.
Step4: RC接受数据包,给CPU。
Xilinx PCIE PIO user design 举例的是CPU对PCIE设备的MEM读写访问事务和IO事务;PCIE设备也可以发起对PC存储器的MEM访问事务,下面暂未介绍。
下面介绍的是采用AXI总线传输TLP包的一些接口信号和时序图。
信号说明:
**_tdata[63:0]:是传输的有效数据。
**_tready:表示,IPCORE 已经准备好接收或发送**_tdata[63:0]上的数据。
**_tvalid:表示,**_tdata[63:0]上的数据是有效数据。
**_tlast:表示,一个TLP数据包的结束。该信号在**tvalid有效期间,该信号有效。
**_tkeep[7:0]:表示,发送数据字节使能,指示**_tdata[W-1:0]中数据的字节有效。该功能在**_tvalid和**_tready置位时有效。第0位对应**_tdata[W-1:0]的最低位,最高位对应**_tdata[W-1:0]中的最高位。例如,**_tkeep[0]==1b,则**_tdata[7:0]有效。**_tkeep[7]==0b,则**_tdata[63:56]无效。当**_tlast无效时,该值唯一的有效值是0xFF(6bit)或0xFFFF(128bit)。当**_tlast有效时,0xFF/0x0F(64bit)有效,或者0xFFFF/0x0FFF/0x00FF/0x000F(128bit)有效。
**_tuser[21:0]:
3DW (DW:双字)和4DW,主要是用来区分存储器的地址位宽。
32位宽的存储器,采用3DW;64位宽的存储器,采用4DW。
比如,下图是3DW存储器读请求TLP包头,即在下图中存储器地址位宽是32位的。
与上图对比,下图是 4DW存储器读请求TLP包头,即下图中存储器地址位宽是64位的。
在PCIE IP CORE 配置过程中,如若不勾选64bit,在默认BAR地址位宽是32位的,即对应3DW包头;
若勾选64bit,则BAR地址位宽是64位的,即对应4DW包头;
BAR地址配置界面如下图所示:
上面介绍了3DW、4DW头包与Base Address Registers基地址寄存器之间的关系。
下面区分一下用户接口的数据位宽,一般在GUI配置界面第一页会配置用户接口的数据位宽,一般可以选择64bit或者128bit。如下图所示。
一个TLP包(事物层),是由头标、数据和摘要组成,如下图所示。
下图举例的是3DW的包头,即存储器地址位宽是32位。
PCIe事务可以分为四大类:
a) 存储器事务
b) IO事务
c) 配置事务
d) 消息事务
下图附上常见的几种TLP包类型:
1、FMT[1:0]和TYPE[4:0],决定当前TLP报文的事务类型。
1 Length字段
在存储器读请求TLP中并不包含Data Payload,在该报文中,Length字段表示需要从目标设备数据区域读取的数据长度;而在存储器写TLP中,Length字段表示当前报文的DataPayload长度。Length字段的最小单位为DW。当该字段为n时,表示需要获得的数据长度或者当前报文的数据长度为n个DW,其中0£n£0x3FF。值得注意的是,当n等于0时,表示数据长度为1024个DW。
2 DWBE字段
PCIe总线以字节为基本单位进行数据传递,但是Length字段以DW为最小单位。为此TLP使用Last DW BE和First DW BE这两个字段进行字节使能,使得在一个TLP中,有效数据以字节为单位。
这两个DW BE字段各由4位组成,其中Last DW BE字段的每一位对应数据Payload最后一个双字的字节使能位;而First DW BE字段的每一位对应数据Payload第一个双字的字节使能位。
LastDW BE和First DW BE这两个字段的使用规则如下。
· 如果传送的数据长度在一个对界的双字(DW)之内,则Last DW BE字段为0b0000,而First DW BE的对应位置1;如果数据长度超过1DW,Last DW BE字段一定不能为0b0000。PCIe总线使用LastDW BE字段为0b0000表示所传送的数据在一个对界的DW之内。
· 如果传送的数据长度超过1DW,则First DW BE字段至少有一个位使能。不能出现First DW BE为0b0000的情况。
· 如果传送的数据长度大于等于3DW,则在First DW BE和Last DW BE字段中不能出现不连续的置1位。
· 如果传送的数据长度在1DW之内时,在First DW BE字段中允许有不连续的置1位。此时PCIe总线允许在TLP中传送1个DW的第1,3字节或者第0,2字节。
· 如果传送的数据长度为2DW之内时,则First DW BE字段和Last DW BE字段允许有不连续的置1位。
值得注意的是,PCIe总线支持一种特殊的读操作,即“Zero-Length”读请求。此时Length字段的长度为1DW,而First DW BE字段和LastDW BE字段都为0b0000,即所有字节都不使能。此时与这个存储器读请求TLP对应的读完成TLP中不包含有效数据。再次提醒读者注意“Zero-Length”读请求使用的Length字段为1,而不是为0,为0表示需要获得的数据长度为1024个DW。
“Zero-Length”读请求的引入是为了实现“读刷新”操作,该操作的主要目的是为了确保之前使用Posted方式所传送的数据,到达最终的目的地,与“Zero-Length”读对应的读完成报文中不含有负载,从而提高了PCIe链路的利用率。在PCIe总线中,使用Posted方式进行存储器写时,目标设备不需要向主设备发送回应报文,因此主设备并不知道这个存储器写是否已经达到目的地。而主设备可以使用“读刷新”操作,向目标设备进行读操作来保证存储器写最终到达目的地。
3 RequesterID字段
RequesterID字段包含“生成这个TLP报文”的PCIe设备的总线号(Bus Number)、设备号(Device Number)和功能号(Function Number),其格式如图5‑9所示。对于存储器写请求TLP,Requester ID字段并不是必须的,因为目标设备收到存储器写请求TLP后,不需要完成报文作为应答,因此Requester ID字段对于存储器写请求TLP并没有实际意义。
下图是3DW完成的头格式
下图是一个简单的TLP 包,即包含两个64位的数据。clock0时刻,采样的,64位宽的数据1,clock1时刻,采样的是64位宽的数据2。需要注意,下面的TLP报文是:01a0090f40000001+0403020100000010,这个字节发送顺序,是采用AXI Bit的顺序。因此,举例,若需要解析TLP保温里的Request ID,则其对应的TLP报文是第一个64位宽的数据的高16位,即“01a0=0000_0001_1010_0000”.
这是一个,由于一次发送64位,因此这个报文的前64位是标头,后64位是数据和地址。
按照上图的数据格式可知
Request ID:16’b0000_0001_1010_0000
Tag:8’b0000_1001
Last DW BE:4’b0000
First DW BE:4’b1111
Fmt:2’b10
Type:5’b0_0000
Length:10’b00_0000_0001
Data:32’h04030201
Address:32’h00000010(低两位无效)
特别说明:
上图中的Data:32’h04030201,这个仅是AXI的interface 接口顺序,实际PC写FPGA MEM的数据是32’h01020304.。
只有传输的数据data是颠倒的,其他的如 Request ID、 Length、 Address等,都是按照上图的字节顺序。
所以 ,要是需要对数据进行解析的话,是需要将data的顺序颠倒一下。比如DMA操作,可能就需要对data进行高低位颠倒一下。
下图中是PIO的仿真波形,RX_CTRL是PIO的APP模块数据接口。
具体分析如下:
接收到的TLP包为 : 01a0090f40000001+0403020100000010
下图是仿真报告,仿真报告里说写MEM的数据是32’h01020304,与上图显示的AXI用户接口传输的数据位是颠倒的。
下图是 PIO 的testcase,测试的写MEM数据是32’h01020304。
进入TSK_TX_MEMORY_WRITE_32这个写函数,可以看到TRN接口类型的数据拼接过程。
在TRN数据接口里,高32位存储的是address地址信号,低32位存储的是数据信息;
但是在低32位数据信息拼接过程中,其将要发送数据的data[0]位,放在TRN数据接口的trn_td[3],其他位也是依次对应。如下图所示。
然后PCIE IP CORE接收到给64'h的数据包后,
首先,将TRN数据接口里高32位数据放到AXI数据接口里的低32位;所以,AXI的用户数据接口64'h0403020100000010低32位是地址位。
其次,将TRN数据接口里低32位数据放到AXI数据接口里的高32位;所以,AXI的用户数据接口64'h0403020100000010高32位是数据位,并且,数据的顺序是颠倒的。
这样,就可以理解,为何modelsim里看到的m_axis_rx_data[63:0]里,唯有数据位的顺序是颠倒的,而其他位不是颠倒的了。
说明:上面仿真图、工程代码、testbench ,均是PCIE IP core 生成后的example的事例文件,即PIO。
读者可自行generate一个IP core看看里面的pio代码。这里不做过多分析了。
本文仅以PCIE IP核自带的example作为示例,进行学习分析
电脑与PCIE之间通过差分总线进行数据交互,这部分不需要用户太多关心。PCIE IP会自动将接受到的TLP包组合成并行数据,并通过AXI总线、或者BMD总线,发送给用户逻辑。用户逻辑如何对接受到的TLP包进行解析,这部分是需要关心的。
当PCIE IP CORE接收到一个TLP数据包时,其需要做的是, 首先,根据标头判断这个TLP是读存储器还是写存储器。
若是写存储器,就将下一个TLP中的地址和数据解析出来(因为这里一次发送64bit,所以第一个TLP中不包含地址和数据,第二个TLP中包含地址和数据),然后通过PIO_EP_MEM_ACCESS 模块将数据写入指定的地址中;
若是读存储器,就将下一个TLP中包含的地址解析出来,再通过PIO_EP_MEM_ACCESS 模块将数据从指定的地址中读取出来,然后经过发送引擎(PIO_TX_ENGINE )进行完成包拼接,最后通过事务层、数据链路层、物理层封装之后,通过差分发送接口(txn、txp)将数据发送出去。
下面两张图是Xilinx ISE 软件生成的example design PIO的架构连接情况。
这部分,暂不进行更新。
请读者参考下面的参考文献[1]即可!
文献[1]里关于PCIE Verilog代码讲解的很到位,不过其提供的代码下载与其博客内容完全一致,积分少的童鞋可以不用下载那个代码了。
>>点击这里返回导航页<<
作者 | 博客 |
1、CLGo | PCIe学习(一):PCIe基础及生成PIO例程分析 |
2、Spring | PCI-E协议 |
3、 | Xilinx FPGA 高速串行传输技术与应用 |
4、meper | 基于FPGA的PCIe接口设计---01_PCIe基本概念 |
5、Jack-Xu | 一步一步开始FPGA逻辑设计 - 高速接口之PCIe |
6、huan09900990 | Altera FPGA PCIE 例程仿真 |
7、fzhykx | xlinx_pcie_ip 使用笔记 |