基于NXP S32G & MicroChip MPF300T 的PCIE调试
最近调了下NXP的S32G PCIE的功能,其实原本目的是验证FPAG的PCIE逻辑是否正常,是个FPGA的活,但是主要时间花在了CPU那边,原本软件那边给我烧的uboot压根不能用,都枚举不到pcie设备,没办法只能自己去调S32G了,简单记录下,也回顾下PCIE的基础知识,这里不涉及PCIE具体协议层面。
首先PCIE设备有两种模式,RC和EP,RC字面意思就是根节点,具体来说可以理解为CPU和PCIE终端设备的一个中间方,它可以接收CPU发出的PCIE相关的请求,转发给下游的终端PCIE设备,而下游的终端设备就是EP了,一张FPGA的板卡插在CPU板子的PCIE插槽上,通常FPGA扮演的就是EP,CPU扮演的就是RC,当然这是很粗的说法,我们这里只关注EP。
系统启动时候具体是如何枚举到设备的?这里就会引入pcie的配置空间了,就是所有的pcie设备,都会有固定的一个空间,存放设备信息,这个是协议定死的。这个配置空间是64Byte的header和192B的Capablitty组成,共256Byte,PCIE则更大有4KB。我们很多时候只关注前64Byte就可以了,EP的前64Byte如下:

所以系统启动时候,RC会顺序扫描总线上所有位置的pcie设备,本质上就是在读pcie配置空间,比如在bus0 device0处读配置,读到了设备ID 厂商信息等,系统就发现这里存在1个pcie设备,这就是枚举的大致过程,这个中间系统还会干一个很重要的事,就是分配地址空间。上面那个图里有6个基址,就是我们pcie设备内部具备的地址空间,可以给cpu去访问的,也就是大名鼎鼎的bar空间,也是我们在fpga的pcie相关的ip里常见到的配置项,pcie_bar指的是pcie地址域到axi地址域的方向,而axi_bar则是axi到pcie的方向,一般来说pcie_bar我们更常见,就是CPU做读写的发起方,对应的就是FPGA PCIE IP那边有个master的axi总线出去,而axi_bar则就是FPGA内部的逻辑做读写的主控方,通过IP的 AXI slave向CPU主动读写。





首先,CPU会向BAR0写入全1,因为bar低多少位是只读的,具体多少位取决于这个bar的空间大小,比如4KB的就是低12位只读,注意bar的低4位始终都是只读的,并且有特殊的含义:
- bit0:表示bar空间是映射到memory(0)还是io(1)
- bit1:reserved
- bit2:表示bar地址空间是32位(0)还是64位(1)
- bit3:表示允许prefetch预取,1可以,0不可以
低4位虽然有自己的功能定义但是也参与到空间大小的表示,所以有两重作用,然后bit4-bit11就是全0。
我们继续说回地址分配的事,CPU向BAR0写入全1后,因为低12位为只读,所以cpu发现bit4-bit11没变,还是0,就知道这个bar有4KB大小,然后在自己的系统内存空间中找4KB的地址空间分配给它,把这个地址写到bar0的bit12-bit31位上,图里这个例子还考虑了4K对齐:


这样,当CPU要访问PCIE设备空间时,其实CPU不知道这个空间是PCIE的空间,它只知道它要访问的具体地址,它把这个请求发给RC,RC发现这个地址是某个PCIE设备的地址空间映射,就会触发PCIE相关的消息给到对应的设备。从而最终实现pcie设备的读写,大致的流程就是这样。下面以S32G验证下,pcie端是配置了一个1MB大小的bar0的FPGA板卡:
-
FPGA配置:
![pcie1]()
![pcie2]()
-
首先在uboot里,查看能否枚举到设备:
![屏幕截图 2026-05-13 164917]()
-
查看bar空间,bar0的是300008,也就是大小1MB,32bit,prefetch,属性和fpga端配置相符。但是这里的300000应该不是映射之后的地址,首先地址太小,而且后面尝试对这个地址读写也是不成功的:
![屏幕截图 2026-05-13 165143]()
-
我们模拟下bar配置时写全F的操作,可以看到低20bit是不变的:
![屏幕截图 2026-05-13 165756]()
-
我们进到kernel里进行bar读写(uboot里读写有点问题),首先kernel里也检测到卡:
![屏幕截图 2026-05-13 170437]()
-
但是卡的属性不对,不支持memory读写,因为是Mem-:
![屏幕截图 2026-05-13 170509]()
-
在没有驱动的情况下通过setpci -s 01:00.0 COMMAND=0x06来配置属性强制开启mem读写:
![屏幕截图 2026-05-13 170707]()
-
借助pcimem读写:
![屏幕截图 2026-05-13 171034]()
-
fpga端也捕获到写的信号,地址和数据都和cpu端一致:
![wav2]()
![wav1]()
这里特别说下容易坑的地方
-
S32G那块开发板默认第二个口是不做RC,所以要修改,我是直接在做uboot时候改了配置:
![image-112]()
![image-122]()
-
S32G的BSP 46.0的版本做出来的uboot是有问题的,启动时候会报DDR的PANIC错误,直接起不来,我是降到42才解决了这个问题
-
microchip 2024之前的pcie驱动是有内存泄漏的问题的,在我的连续DMA数据搬运测试中,程序运行一段时间会挂掉,挂掉后重启程序会更快的崩溃,不符合用户程序内存泄漏的现象,dmesg显示pci alloc pages failed,经查,驱动侧在申请SG DMA 描述符连续物理地址时候,没有释放该物理地址,由于描述符每个只有8字节,所以终端查看内存泄漏的不明显,但是kernel连续物理地址比虚拟地址申请资源更有限,所以重新运行后用户程序会更快的挂掉,修改驱动添加pci_free_consistent,程序运行正常:
![image-4-1]()
![image-5-1]()
最后记下过程中使用的yocto指令,方便记忆:
- bitbake -e u-boot | grep "^S=" 查找u-boot的源码位置
- bitbake virtual/kernel -c menuconfig 配置
- bitbake u-boot -c configure
- bitbake u-boot -c devshell
- cd ../build/<defconfig>
- make menuconfig
- exit
- bitbake -c compile -f u-boot
- bitbake u-boot
- bitbake -e linux-xlnx | grep ^S= 查看yocto内核编译部分
- bitbake -e hello | grep ^SRC_URI 查找包的路径
- bitbake -g petalinux-image-minimal && cat pn-buildlist | grep -ve "native" | sort | uniq 等同于./build/tmp/deploy/images/<your_image>/<image_name>.manifest
- bitbake -s | grep XXX 查找包
- bitbake -c cleanall hw-hs -f && bitbake -c compile hw-hs -f
- bitbake-layers show-recipes
- bitbake-layers show-layers

添加自定义recipes并编进rootfs:
-
在sources下添加meta-custom目录,结构如下:
![image-116]()
-
layer.conf内容如下:
![image-117]()
-
pcimem.bb内容如下:
![image-118]()
先记到这里,整个过程其实大部分都是在折腾S32G,FPGA这边没怎么动。PCIE的整体的体系结构其实很复杂,这里没有涉及具体的协议层面,毕竟我们都用的现成的IP,等后面有机会深入的时候再补充,有错的地方请指正。这里推荐看下<<深入浅出SSD>>里的协议篇PCIE部分,讲的比较简单但通俗易懂。
-更新一下:
在uboot里是可以读写bar的,我先贴下启动log:
U-Boot 2022.04 (Sep 24 2026 - 06:38:52 +0000)
SoC: NXP S32G274A rev. 2.1
CPU: ARM Cortex-A53 r0p4 @ max 1000 MHz
Model: NXP S32G274A-RDB2
DRAM: 3.5 GiB
Core: 305 devices, 25 uclasses, devicetree: board
MMC: FSL_SDHC: 0
Loading Environment from SPIFlash... SF: Detected mx25uw51245g with page size 256 Bytes, erase size 64 KiB, total 64 MiB
OK
s32cc_serdes_phy serdes@40480000: Using mode 0 for SerDes subsystem
pci_s32cc pcie@40400000: Could not set DEVICE ID
pci_s32cc pcie@40400000: Configuring as RootComplex
pci_s32cc pcie@40400000: Failed to get link up
s32cc_serdes_phy serdes@44180000: Using mode 0 for SerDes subsystem
pci_s32cc pcie@44100000: Could not set DEVICE ID
pci_s32cc pcie@44100000: Configuring as RootComplex
pci_s32cc pcie@44100000: X2, Gen2
In: serial@401c8000
Out: serial@401c8000
Err: serial@401c8000
Board revision: RDB2
PCIe: BusDevFun VendorId DeviceId Device Class Sub-Class
__________________________________________________________________________
pcie@40400000 RootComplex
| `-- 02:00.00 0x1957 0x4002 Bridge device 0x04
pcie@44100000 RootComplex (X2, Gen2)
| `-- 04:00.00 0x1957 0x4002 Bridge device 0x04
| `-- 05:00.00 0x11aa 0x1556 Build before PCI Rev2.0 0x00
Net: ERROR: get_mux_freq: Mux (id:11) without a valid source (0)
ERROR: Failed to get the frequency of CGM MUX 10(CGM=0x40030000)
ERROR: Failed to get the frequency of CGM MUX 10(CGM=0x40030000)
eth0: ethernet@4033c000
Found PFE version 0x50300 (S32G2)
Too large ELF file, size: 4294836224
PFE firmware file '<NULL>@0x030a0000:<NULL>' loading failed: -27
Found PFE version 0x50300 (S32G2)
Too large ELF file, size: 4294836224
PFE firmware file '<NULL>@0x030a0000:<NULL>' loading failed: -27
Found PFE version 0x50300 (S32G2)
Too large ELF file, size: 4294836224
PFE firmware file '<NULL>@0x030a0000:<NULL>' loading failed: -27
Hit any key to stop autoboot: 0
Booting from flash...
Reading 26214400 byte(s) at offset 0x00000000
Reading 196608 byte(s) at offset 0x00000000
Reading 32505856 byte(s) at offset 0x00000000
Bad Linux ARM64 Image magic!
=> pci header 05:00.0
vendor ID = 0x11aa
device ID = 0x1556
command register ID = 0x0006
status register = 0x0010
revision ID = 0x00
class code = 0x00 (Build before PCI Rev2.0)
sub class code = 0x00
programming interface = 0x00
cache line = 0x08
latency time = 0x00
header type = 0x00
BIST = 0x00
base address 0 = 0x00300008
base address 1 = 0x00000000
base address 2 = 0x00000000
base address 3 = 0x00000000
base address 4 = 0x00000000
base address 5 = 0x00000000
cardBus CIS pointer = 0x00000000
sub system vendor ID = 0x0000
sub system ID = 0x0000
expansion ROM base address = 0x00000000
interrupt line = 0x00
interrupt pin = 0x04
min Grant = 0x00
max Latency = 0x00
=> pci bar 05:00.0
ID Base Size Width Type
----------------------------------------------------------
0 0x0000000000300000 0x0000000000100000 32 MEM Prefetchable
=> pci regions
=>
=> <INTERRUPT>
=> pci regions 4
Buses 04-05
# Bus start Phys start Size Flags
0 0x0000000000000000 0x0000004ffffe0000 0x0000000000010000 io io
1 0x0000000000000000 0x0000004800000000 0x00000007fffe0000 mem
2 0x0000000080000000 0x0000000080000000 0x0000000080000000 mem sysmem
3 0x0000000880000000 0x0000000880000000 0x0000000060000000 mem sysmem
=> pci regions 5
Buses 04-05
# Bus start Phys start Size Flags
0 0x0000000000000000 0x0000004ffffe0000 0x0000000000010000 io io
1 0x0000000000000000 0x0000004800000000 0x00000007fffe0000 mem
2 0x0000000080000000 0x0000000080000000 0x0000000080000000 mem sysmem
3 0x0000000880000000 0x0000000880000000 0x0000000060000000 mem sysmem
=> md.l 0x4800300000 1
4800300000: 00000000 ....
=> md.l 0x4800300000 1
4800300000: 00000000 ....
=> md.l 0x4800300000 1
4800300000: 00000000 ....
=> md.l 0x4800300000 0x12345678 1
md - memory display
这里pci bar 05:00.0 显示的 300000地址就是系统枚举时给fpga分配的一个pcie物理地址空间,系统探测到bar空间和大小后将这个地址写到pcie设备的bar配置地址寄存器里,pci header看到的300008是因为8表示这个地址空间的读写预取等属性值,地址就是300000,然后pci regions Buses列是pcie的地址空间分配,其中300000落在BUSes 1的范围,Flags属性也是mem,Phys Start是CPU侧的物理地址,所以对于S32G来说,bar0的地址就是4800300000,对这个地址读写就是读写fpga 的bar0了,实测在uboot中对这个地址读写,fpga可以收到读写信号。
参:
- https://iriscores.com/2021/07/14/understanding-pcie-to-axi-bridge/
- https://ww1.microchip.com/downloads/aemDocuments/documents/FPGA/ApplicationNotes/ApplicationNotes/microsemi_polarfire_fpga_pcie_endpoint_ddr3_ddr4_memory_controller_data_plane_demo_guide_dg0756_v10.pdf
- pg055-axi-bridge-pcie
- https://zhuanlan.zhihu.com/p/445877158
- 深入浅出SSD 协议篇



















浙公网安备 33010602011771号