1.1 PIO内存读写操作TLP解析
PCIE有三种空间——内存空间、IO空间、配置空间,其中内存空间是目前PCIE设备常用的空间,IO空间是为了兼容以前的PCI设备(PCIE设备几乎不用IO空间了),配置空间是PCIE设备的信息空间(ID、BAR、等等都写在里面,主机内核程序初始化会读PCIE的配置空间信息,从而给Endpoint分配PICE总线域空间等一系列操作)。PIO并不是说读写IO空间,原理上还是读写内存空间,PIO读写类似读写寄存器一样,每次对内存特定地址写一个数据(数据大小一般为32bit)。使用官方驱动指令读写PIO的操作,实质和使用WinDriver操作Read/Write Memory无异,不过官方驱动把每次的数据长度限制在了1DW(32bit)内,大于1DW的数据,会分多次发送。
写1DW数据。
写2DW数据。
写3DW数据。
读1DW数据。
读2DW数据。
读3DW数据。
注意的是,根据PCIE协议的Mwr,一次Mwr/Mrd数据大小最大可以是4096Byte,上面大于4Byte分了多次Mwr发送是驱动程序的做法。
看到时序图的信号都是满足要求的,但是具体信号的数据是怎样的呢?现在以读写12Byte Memory为例来分析这些数据。
Write/Read Memory 使用到了三种类型的报文——Mwr/Mrd/CplD。在使用PCIE IP时,底层的东西可以不需要太关心,但是解析接收到的TLP解析和发送合乎要求的TLP是重点。
对于Mwr/Cpld,他们携带的数据就是数据,没什么需要分析的,需要分析的是TLP中的Header,Mrd报文只有Header,分析Header才是重点。
注意的是Mwr/Mrd是地址路由,而CplD是ID(Bus/Device/Function)路由,这是协议规定的,所谓路由简单理解就是RC根据地址/ID,能够在众多的PCIE传输路径中把数据送到目标设备,因为内核程序初始化给给每个BAR分配了PCIE总线域空间,每个BAR空间地址都是独立映射的,而ID根据(Bus/Device/Function)唯一确定。
对于Mwr/Mrd的Header格式如下图:
对于CplD的Header格式如下图:
Mwr:
Header:0x0000_0000_df20_2000_0000_000f_4000_0001
Data: 0x0000_0000_0000_0000_0000_0000_0102_0304
Header:0x0000_0000_df20_2004_0000_000f_4000_0001
Data:0x0000_0000_000_0000_0000_0000_0506_0708
Header:0x0000_0000_df20_2008_0000_000f_4000_0001
Data:0x0000_0000_0000_0000_0000_0000_090a_0b0c
对于Mwr的Header,对着协议格式分析,很容易知道第一个Header地址(32bit)是df20_2000,携带数据长度Length(以DW为单位)为1,报文类型[Fmt,Type]=0x40(表明为3DW Mwr),字节使能[last DW BE,first DW BE]=0x0f,其他信息对座入号就行了,特别注意的是TLP是大端格式,[Fmt,Type]=axis_master_tdata[31:24],实际代表的是0Byte的位置,其他DW数据亦如此。
Mrd:
Header:0x0000_0000_df20_2000_0000_000f_0000_0001
Header:0x0000_0000_df20_2004_0000_000f_0000_0001
Header:0x0000_0000_df20_2008_0000_000f_0000_0001
对于Mrd的Header,对着协议格式分析,很容易知道地址(32bit)是df20_2000,请求数据长度Length(以DW为单位)为1,报文类型[Fmt,Type]=0x00(表明为3DW Mrd),字节使能[last DW BE,first DW BE]=0x0f,其他信息对座入号就行了,深入了解可读PCIE协议规范。
CplD:
Header:0x0000_0000_0000_0000_0100_0004_4a00_0001
Data:0x0000_0000_0000_0000_0000_0000_0102_0304
Header:0x0000_0000_0000_0010_0100_0004_4a00_0001
Data:0x0000_0000_0000_0000_0000_0000_0506_0708
Header:0x0000_0000_0000_0020_0100_0004_4a00_0001
Data:0x0000_0000_0000_0000_0000_0000_090a_0b0c
对于Cpld的Header,采用的是ID路由,很容易知道Requester ID为0x0000,Completer ID为0x0100,回复数据长度Length(以DW为单位)为1,报文类型[Fmt,Type]=0x4a(表明为CplD),Byte Count为0x004,TAG为0x00,其他信息对座入号就行了。具体的含义可查询PCIE协议规范。
1.2 DMA读写操作TLP解析
DMA直接内存访问,内存访问的发起者是FPGA,所以主机要发数据给FPGA,首先是发送DMA读指令和数据在主机内存的首地址给FPGA,FPGA根据内存首地址和数据长度发起一个Mrd,主机再回复CplD,这样就把数据传送到FPGA,称为DMA读操作。与之相反的是,主机发送DMA写指令和数据要放主机内存首地址给FPGA,FPGA根据内存首地址和数据长度发起带数据Mwr,这样就数据传送到主机,称为DMA写操作。
1024Byte DMA读,主机到FPGA。
1024Byte DMA写,FPGA到主机。
2048Byte DMA读,主机到FPGA。
2048Byte DMA读,FPGA到主机。
通过上面的DMA读写时序图不难发现,实现DMA读,主机会首先发3次Mwr TLP给FPGA,FPGA解析MWr TLP后会发起Mrd TLP,主机再回复CplD TLP,这样就数据传输到FPGA了;实现DMA写,主机首先发3次Mwr TLP给FPGA,FPGA解析MWr TLP后会直接上发带数据的MWr TLP,这样就数据传到主机了。
主机的3次MWr TLP是什么呢?通过解读例程的DMA模块下的dma_controller模块,不难发现,BAR1+0x100偏移量的数据是指示FPGA发起带数据的Mwr/不带数据的Mrd(也包含数据长度等),BAR1+0x100偏移量的数据是搬运数据的主机物理内存首地址的低32位,BAR1+0x100偏移量的数据是搬运数据的主机物理内存首地址的高32位。DMA是直接存储器访问,要直接从主机内存搬数据/写数据,首先要知道主机内存首地址。一定是3次Mwr TLP通信握手吗?肯定不是的,这是生成IP例程的做法,自行设计DMA控制器的话,可以使用更复杂/更有效的握手机制来触发DMA操作。
时序图中Header:
DMA——主机的Mwr (以1024Byte为例)
第一次:
Header:0x0000_0000_df20_4100_0000_000f_4000_0001
Data:0x0000_0000_0000_0000_0000_0000_ff00_0100
第二次:
Header:0x0000_0000_df20_4110_0000_000f_4000_0001
Data:0x0000_0000_0000_0000_0000_0000_00a0_2647
第三次:
Header:0x0000_0000_df20_4120_0000_000f_4000_0001
Data:0x0000_0000_0000_0000_0000_0000_0100_0000
这个Mwr是32bit Address访问,存储器访问都是通过地址路由,Header的意义和上面Mwr分析是一致的。
DMA——主机的CplD(以1024Byte为例)
第1个Header:0x0000_0000_0100_0000_0000_0200_4a00_0020
第2个Header:0x0000_0000_0100_0000_0000_0180_4a00_0020
第3个Header:0x0000_0000_0100_0000_0000_0100_4a00_0020
第4个Header:0x0000_0000_0100_0000_0000_0080_4a00_0020
第5个Header:0x0000_0000_0100_0100_0000_0200_4a00_0020
第6个Header:0x0000_0000_0100_0100_0000_0180_4a00_0020
第7个Header:0x0000_0000_0100_0100_0000_0100_4a00_0020
第8个Header:0x0000_0000_0100_0100_0000_0080_4a00_0020
完成报文使用ID路由(协议规定,简单就是对于每一个BAR,都有自己的ID(Bus/Device/Function),通过这个唯一的ID可以找到接收目标)。CplD的Header只能是3DW组成,具体和上面CplD分析是一致的。需要注意的是协议规定CplD最大数据长度是1024DW(4096 Byte),但是我们IP配置页面默认设置Max Playload Size其实是128 Byte,所以大于128 Byte的数据需要分多次发送。需要注意的是前4 Header的TAG=0x00,后4 Header的TAG=0x01,为什么这样,因为FPGA发了两次Mrd,TAG就是记录发了Mrd,收到完成报文中TAG和Mrd一致,代表当前Mrd完成了。可以知道TAG是8bit,一旦Mrd的TAG=0xff,但是0x00的CplD还没回复,Mrd便不可再发送了(对于同一个的BAR的Mrd)。
可以看到Byte Count是变化的,这个很好理解,表示当前传输距离Mrd请求的数据量还有多少(这里不是我们最终的1024 Byte,而是一个Mrd的Byte数)。第一个Header为0x200,表示距离Mrd请求数据量还有512 Byte,第二个Header为0x180,表示距离Mrd请求数据量还有384 Byte,以此类推。一次Max Playload Size是128 Byte,传输一次CplD,距离目标就减少128 Byte。到了第5个Header,为什么回到512 Byte了,因为第5个Header CplD是新的Mrd请求了,同样请求长度为512 Byte。
DMA——FPGA的Mrd(以1024Byte为例)
Header:0x20d5_0400_0000_0000_0000_0001_0100_1c80
Header:0x20d5_0480_0000_0000_0000_0001_0100_3c80
这个Mrd是64bit Address访问,存储器访问都是通过地址路由,Header的意义和上面Mrd分析是一致的。这个64bit的地址是FPGA要读主机的内存的地址,低32bit放在第4DW,高32bit放在了第3个DW,这个地址肯定不是固定的,即使同一台电脑,主机每次读写开辟内存空间放数据的位置也不一样。
这里Mrd为什么需要分两次发送呢,因为PCIE IP设置的Max_rd_req_size为512 Byte,所以大于512 Byte的数据,需要分多次Mrd,例如向主机内存请求2048 Byte的数据,需要4次Mrd,每一个Mrd记录一个Tag,这样就对应上了CplD的Tag。
DMA——FPGA的Mwr(以1024Byte为例)
Mwr已经展开多次了,就不重复了。