培训过程记录,
| DDR(eg. 2GB) | | DDR(eg. 8GB) |
| |
|||||||||||||||||||| ||||||||||||||||||||
| Z7 | | V7 |
| CPU | PCIe | DMA |-|SATA Disk|
| DMA | | |
|||||||||||||||||||| ||||||||||||||||||||
DMA:Direct Memory Access
non OS:
unsigned int *buf = (unsigned int *)0x50000000 //phy
*buf -> ldr访存汇编指令
OS:
unsigned int *buf = malloc(64); //virt
V7通过PCIe给Z7提供了64MB可见内存
CPU上的OS执行物理地址到虚拟地址的映射,映射的时候可以设置是否需要Cache
CPU -> Cache -> Memory(DDR/PCIe)
CPU -> Memory(DDR/PCIe)
为什么需要不带Cache的内存,不带Cache速度慢
eg. 外设/IP的寄存器就不能带Cache
系统中除了CPU还有DMA具有主动访问内存的能力
Direct: 搬运连续内存
Source -> Destination
SG: 提供了一次操作访问不连续的内存的能力
| Descriptor0 | Descriptor1 | Descriptor2 | ... | DescriptorN |
Src -> Dst Src -> Dst Src -> Dst Src -> Dst
假设搬运同样长度的数据1MB,SG比Direct慢多少呢?
Direct搬运连续1MB内存,SG的1MB内存假设分为5个块,5*64B,相对于1MB,5*64B/1MB~0%
8GB DDR
V7_1
|
Z7(host) -- PCIe Switch -- V7_2 8GB DDR
/ | \
NVMe_1 NVMe_2 NVMe_3
CPU内存空间(物理地址)
0
DDR
2GB
PCIe 0~64MB(BAR0,数据传输) 64~65(BAR1,寄存器控制) V7_1 64~128 V7_2 128~129 NVMe_1 129~130 NVMe_2...
0x6c000000~0x6d000000
2GB+1GB
怎样用64MB的空间来访问V7板载的8GB的内存呢,显然需要FPGA实现一个滑动窗口,把这64MB的空间对准的8GB的任意一个位置
FPGA添加一个基地址寄存器比如base
|64MB|
|
-----------
base+ |
| | | |64MB| 8GB |
0x6c000000~0x6d000000 0~64MB
CPU addr0(虚拟地址) -> addr1(物理地址) -> addr2(8GB内存的地址)
|
base
P2P:
NVMe_1(内部包含DMA) -> addr1 -> addr2(8GB内存的地址)
|
base
CRP中PCIMEM用于实现base
V7_1
PLCIRCBUF0 = { 0GB, 1GB , add &PCIMEM0}
PLCIRCBUF1 = { 1GB, 1GB , add &PCIMEM0}
PLCIRCBUF2 = { 2GB, 1GB , add &PCIMEM0}
jsfsWrite(buf, len) buf > 64MB超过空间,所以在把buf写入文件系统之前,需要执行一个转换函数,把0~8GB的地址映射到0x6c000000~0x6d000000空间中
V7_2
PLCIRCBUF3 = { 0GB, 1GB , add &PCIMEM1}
PLCIRCBUF4 = { 1GB, 1GB , add &PCIMEM1}
PLCIRCBUF5 = { 2GB, 1GB , add &PCIMEM1}
假设一个地址在1GB+1MB的位置即0x40100000,转换函数实现流程:
offset = 0x40100000 & 64MB = 0x100000 1MB
base = 0x40100000 - offset = 0x40000000
jsfsWrite(0x6c000000 + offset, len)
or
base = 0x40100000
jsfsWrite(0x6c000000, len)
在ft2000/4上,jsfsWrite里面传入的buf地址是虚拟地址,64MB虚拟地址,
ipraid_write()/nvme_write()在访问CPU DDR时传入的地址是虚拟地址,在访问FPGA DDR传入的地址是物理地址
所以在驱动中需要判断该虚拟地址属于CPU DDR还是FPGA DDR,对于FPGA DDR,需要将虚拟地址转换为物理地址,再调用ipraid_write()/nvme_write()