基于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如下:
image-107
所以系统启动时候,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主动读写。
image-119
image-120
image-121
image-108
image-109

首先,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对齐:

image-110
image-111

这样,当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

image-114

添加自定义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可以收到读写信号。

参:

posted @ 2026-05-14 10:52  原声带1993  阅读(55)  评论(0)    收藏  举报