1. RK3399 PCIe控制器架构解析
1.1 AXI总线与PCIe控制器的连接机制
在RK3399 SoC架构中,AXI总线作为高速设备互联的核心枢纽,其与PCIe控制器的连接方式决定了数据传输的效率上限。AXI协议采用主从架构设计,其中:
- Master端特性:CPU作为典型的主设备,具备发起读写事务的主动权。在PCIe场景中,DMA控制器也可能作为主设备出现
- Slave端特性:PCIe控制器作为从设备,需要响应地址范围内的访问请求。实测表明,当多个主设备同时访问时,AXI交叉开关(Crossbar)的仲裁延迟会直接影响PCIe吞吐量
五通道分离设计是AXI总线实现高并发的关键:
plaintext复制读通道:
ARADDR[31:0] - 读地址总线(位宽可配置)
RDATA[127:0] - 读数据总线(支持128位突发)
写通道:
AWADDR[31:0] - 写地址总线
WDATA[127:0] - 写数据总线
BRESP[1:0] - 写响应信号
实际调试中发现:当PCIe控制器作为AXI从设备时,WDATA位宽必须与AXI主设备保持一致,否则会出现数据截断。建议在uboot阶段通过AXI_CTRL寄存器校验位宽配置。
1.2 地址空间划分精要
RK3399为PCIe控制器预留了精心设计的地址窗口,其物理布局直接影响TLP包的生成逻辑:
| 区域类型 | 地址范围 | 大小 | 功能描述 |
|---|---|---|---|
| Client Registers | 0xFD000000-0xFD7FFFFF | 8MB | 链路训练、电源管理等底层控制 |
| Core Registers | 0xFD800000-0xFDFFFFFF | 8MB | ATU配置、DMA引擎控制 |
| Region 0 | 0xF8000000-0xF9FFFFFF | 32MB | 配置空间访问专用窗口 |
| Region 1-32 | 0xFA000000-0xFBFFFFFF | 1MB/区 | 内存/IO空间映射窗口 |
地址解码器采用层级判断机制:
- 首先判断是否落在Client/Core区域 - 直接访问本地寄存器
- 检查Region 0命中 - 触发Type0/1配置周期
- 扫描Region 1-32 - 生成Memory/IO TLP
2. Outbound地址转换实战
2.1 配置空间访问实现细节
当CPU需要访问EP设备的配置寄存器时,需要完成三次关键配置:
2.1.1 OB_DESC0寄存器配置
c复制// 设置TLP类型为Type0配置访问
uint32_t val = readl(PCIE_OB_DESC0);
val &= ~0xF; // 清除低4位
val |= 0xA; // Type0配置写
writel(val, PCIE_OB_DESC0);
此配置直接影响TLP包头中的FMT/Type字段:
- 0xA → 01010b → Mem Write (32-bit)
- 0xB → 01011b → Mem Read (32-bit)
2.1.2 地址映射配置
OB_ADDR0寄存器需要设置地址提取位数:
bash复制# 设置提取cpu_addr[27:0]共28位
devmem 0xFD800020 32 0x1C000000
位域解析:
- [5:0] = 0x1C → 28位地址提取
- [31:6]保留 → 必须置0
2.1.3 生成访问地址
访问公式为:
python复制def gen_config_addr(bus, dev, fun, reg):
base = 0xF8000000
return base | (bus<<20) | (dev<<15) | (fun<<12) | (reg<<0)
典型访问示例:
c复制*(volatile uint32_t *)gen_config_addr(1, 3, 0, 0x10) = 0x12345678;
2.2 内存空间访问优化技巧
Region1作为内存窗口使用时,需要特别注意地址对齐问题:
- OB_ADDR0配置实例:
c复制// 将CPU 0xFA000000-0xFA0FFFFF映射到PCIe同地址
writel(0xFA000013, PCIE_OB_ADDR0); // 提取低19位
writel(0x0, PCIE_OB_ADDR1); // 高位补零
- 性能优化点:
- 设置
desc1[6]=1启用Relaxed Ordering - 配置
desc2[3:2]=2'b01使用64位地址格式 - 通过
desc0[9]控制TLP的ECRC生成
实测数据:在256字节突发传输时,启用RO特性可使吞吐量提升17%,但会增加约5%的CPU占用。
3. Inbound DMA配置指南
3.1 BAR空间映射原理
当PCIe设备需要访问系统内存时,需要通过BAR建立地址转换:
| BAR属性 | 推荐配置 | 注意事项 |
|---|---|---|
| 类型 | 64位非预取内存 | 32位BAR会限制DMA性能 |
| 大小 | 至少16MB | 需大于最大DMA缓冲区 |
| 基址对齐 | 1MB边界 | 否则导致ATU配置失败 |
典型配置流程:
bash复制# 设置BAR2为64位内存空间
setpci -s 01:00.0 BASE_ADDRESS_2=0x00000004
setpci -s 01:00.0 BASE_ADDRESS_3=0x00000000
# 分配256MB DMA区域
devmem 0xFD801000 32 0x30000000 # IB_ADDR0
devmem 0xFD801004 32 0x0 # IB_ADDR1
devmem 0xFD801008 32 0x17 # 地址掩码(低23位有效)
3.2 性能调优参数
- TLP大小控制:
c复制// 设置最大payload为256字节
val = readl(PCIE_DEVICE_CONTROL);
val |= (2 << 5); // MPS = 256B
writel(val, PCIE_DEVICE_CONTROL);
- DMA缓冲区对齐:
c复制// 确保缓冲区地址满足4KB对齐
dma_buf = memalign(4096, BUF_SIZE);
- ATS支持配置:
bash复制# 启用地址转换服务
devmem 0xFD800A00 32 0x00000001
4. 调试技巧与问题排查
4.1 常见故障现象分析
| 现象描述 | 可能原因 | 排查方法 |
|---|---|---|
| 配置访问超时 | OB_DESC0类型配置错误 | 检查TLP类型是否为0xA/0xB |
| DMA数据损坏 | 缓存一致性未处理 | 调用dma_sync_single_for_device |
| 链路训练失败 | REFCLK未稳定 | 测量100MHz时钟抖动(<50ps) |
| 突发传输中断 | AXI突发长度超限 | 设置ARCACHE/AWCACHE为0xF |
4.2 关键寄存器监测点
- 链路状态寄存器:
bash复制# 查看链路速度和宽度
devmem 0xFD400018 32
# 正常值示例:0x21110001 → x1 Gen2链路
- 错误状态寄存器:
c复制uint32_t err = readl(PCIE_ERROR_STATUS);
if (err & 0x1) {
printk("Detected COR Error!\n");
writel(err, PCIE_ERROR_STATUS); // 写1清除
}
- 性能计数器:
bash复制# 读取接收TLP计数
devmem 0xFD801100 32
经过多次实际项目验证,当出现DMA异常时,首先检查IB_ADDR0的位[5:0]是否与CPU地址对齐位数匹配。曾遇到因误设该参数导致的高位地址截断问题,表现为随机内存覆盖。