BAR 空间主要给 Host/Linux 驱动控制 FPGA 用户逻辑 使用。
一句话:
BAR 空间 = Host 访问 FPGA 的控制面 / 状态面 / 调试面
对你的 PCIe Block DMA 来说,BAR 空间最应该给这些功能使用:DMA 控制、中断控制、状态查询、错误诊断、版本能力识别、调试统计。你前面流程里 probe() 映射 BAR、ioctl() 写 DMA 参数、irq_handler() 读 IRQ/DMA 状态,都是围绕 BAR 空间完成的。
1. 必须放 BAR 空间的功能
1.1 版本 / 能力识别寄存器
这类寄存器用于驱动确认 FPGA bitstream 是否正确。
推荐放:
VERSION
CAPABILITY
BUILD_TIME
IP_ID
REGISTER_MAP_VERSION
作用:
驱动 probe() 时先读 VERSION
→ 判断是不是正确 FPGA 版本
→ 判断寄存器布局是否匹配
→ 判断支持几通道 DMA、是否支持 64-bit 地址、是否支持 MSI-X
例如:
BAR0 + 0x0000 VERSION
BAR0 + 0x0004 CAPABILITY
这类寄存器非常适合放 BAR。
1.2 全局控制寄存器
用于控制整个 FPGA DMA IP。
推荐放:
GLOBAL_CTRL
GLOBAL_STATUS
SOFT_RESET
GLOBAL_ENABLE
IP_READY
作用:
驱动可以复位 DMA IP
驱动可以判断 FPGA 逻辑是否 ready
驱动可以关闭整个 DMA 模块
例如:
BAR0 + 0x0008 GLOBAL_CTRL
BAR0 + 0x000C GLOBAL_STATUS
1.3 DMA 控制寄存器
这是 BAR 空间最核心的用途。
推荐放:
DMA_CTRL
DMA_STATUS
DMA_DIR
DMA_CHANNEL
DMA_ADDR_LOW
DMA_ADDR_HIGH
DMA_LEN
DMA_START
DMA_STOP
DMA_RESET
作用:
驱动把 Host DMA buffer 地址写给 FPGA
驱动把 DMA 长度写给 FPGA
驱动告诉 FPGA 是 H2C 还是 C2H
驱动写 START 启动 DMA
驱动读 STATUS 判断 DMA 状态
例如:
BAR0 + 0x0010 DMA_CTRL
BAR0 + 0x0014 DMA_STATUS
BAR0 + 0x0018 DMA_DIR
BAR0 + 0x0020 DMA_ADDR_LOW
BAR0 + 0x0024 DMA_ADDR_HIGH
BAR0 + 0x0028 DMA_LEN
对于 Block DMA,这部分基本必须有。
1.4 中断控制寄存器
MSI/MSI-X 是 FPGA 通知 Host 的机制,但驱动仍然需要通过 BAR 管理中断状态。
推荐放:
IRQ_ENABLE
IRQ_STATUS
IRQ_CLEAR
IRQ_MASK
IRQ_VECTOR_MAP
作用:
驱动使能 DMA_DONE / DMA_ERROR 中断
FPGA 完成 DMA 后置 IRQ_STATUS
驱动中断处理中读取 IRQ_STATUS
驱动写 IRQ_CLEAR 清中断
例如:
BAR0 + 0x0030 IRQ_ENABLE
BAR0 + 0x0034 IRQ_STATUS
BAR0 + 0x0038 IRQ_CLEAR
没有这些寄存器,中断会很难管理:驱动不知道为什么中断,也不知道如何清中断。
1.5 错误状态寄存器
用于 DMA 或 PCIe 出错后的诊断。
推荐放:
ERROR_CODE
PCIE_ERR_STATUS
FIFO_STATUS
LAST_ERROR_ADDR_LOW
LAST_ERROR_ADDR_HIGH
TIMEOUT_STATUS
作用:
DMA 出错后,驱动可以读错误码
用户态可以通过 ioctl(GET_STATUS) 获取错误原因
调试 FPGA 时能判断是 PCIe 错、FIFO 错、地址错还是长度错
例如:
BAR0 + 0x0044 ERROR_CODE
BAR0 + 0x0050 PCIE_ERR_STATUS
BAR0 + 0x0054 FIFO_STATUS
2. 建议放 BAR 空间的功能
2.1 调试寄存器
这类不是必须,但非常有用。
推荐放:
DEBUG_STATE
FSM_STATE
LAST_DMA_ADDR
LAST_DMA_LEN
LAST_TLP_INFO
READ_REQ_COUNT
WRITE_REQ_COUNT
COMPLETION_COUNT
作用:
上板调试 DMA 卡死
查看状态机停在哪里
查看 PCIe 请求是否发出
查看 Completion 是否回来
查看 FIFO 是否堵住
例如:
BAR0 + 0x2000 DEBUG_STATE
BAR0 + 0x2004 FSM_STATE
BAR0 + 0x2008 READ_REQ_COUNT
BAR0 + 0x200C WRITE_REQ_COUNT
调试寄存器建议单独放一段,不要和正式控制寄存器混在一起。
2.2 性能统计寄存器
如果你要分析 DMA 带宽,可以放:
BYTES_DONE
TRANSFER_COUNT
DMA_CYCLE_COUNT
MAX_LATENCY
MIN_LATENCY
PCIE_READ_REQ_CNT
PCIE_WRITE_REQ_CNT
作用:
统计 DMA 传输量
估算 DMA 带宽
分析 PCIe 性能瓶颈
定位 FPGA 侧还是 Host 侧慢
例如:
BAR0 + 0x0040 BYTES_DONE
BAR0 + 0x3000 PERF_CYCLE_COUNT
BAR0 + 0x3004 PERF_BYTES_COUNT
2.3 多通道 DMA 管理
如果你以后支持多个 DMA channel,可以每个 channel 一块寄存器区域。
例如:
0x0100 ~ 0x01FF DMA channel 0
0x0200 ~ 0x02FF DMA channel 1
0x0300 ~ 0x03FF DMA channel 2
0x0400 ~ 0x04FF DMA channel 3
每个通道都有:
CH_DMA_CTRL
CH_DMA_STATUS
CH_DMA_ADDR_LOW
CH_DMA_ADDR_HIGH
CH_DMA_LEN
CH_DMA_DIR
CH_IRQ_STATUS
CH_ERROR_CODE
好处是驱动容易写:
#define CH_BASE(ch) (0x0100 + (ch) * 0x100)
#define CH_DMA_CTRL(ch) (CH_BASE(ch) + 0x00)
#define CH_DMA_STATUS(ch) (CH_BASE(ch) + 0x04)
#define CH_DMA_LEN(ch) (CH_BASE(ch) + 0x10)
2.4 Scatter-Gather DMA / Descriptor Ring 控制
如果以后从 Block DMA 升级到 SGDMA,BAR 也要放这些控制寄存器:
DESC_BASE_LOW
DESC_BASE_HIGH
DESC_RING_SIZE
DESC_HEAD
DESC_TAIL
DOORBELL
SGDMA_CTRL
SGDMA_STATUS
作用:
驱动把 descriptor ring 地址告诉 FPGA
驱动更新 tail
驱动敲 doorbell
FPGA 从 Host 内存读取 descriptor
即使 descriptor 本身放在 Host 内存里,descriptor base address / doorbell 仍然通常通过 BAR 寄存器下发。
3. 不适合放 BAR 空间的功能
3.1 大块数据 buffer 不建议放 BAR
不要把大规模业务数据放 BAR 里。
比如这些不适合放 BAR:
图像大帧数据
ADC 连续采样数据
网络包大流量数据
几 MB / 几百 MB 的数据缓存
原因:
BAR 适合控制寄存器,不适合高吞吐数据面
大数据应该走 DMA buffer
正确方式:
BAR:写 DMA 地址、长度、方向、START
DMA buffer:真正放大块数据
3.2 高频数据流不建议通过 BAR 读写
比如你让 CPU 不断:
readl(bar0 + DATA_REG);
去读 ADC 数据,这种效率很低。
问题:
每次 readl 都是 MMIO/PCIe 访问
吞吐低
CPU 占用高
延迟不可控
不适合连续数据流
高频数据应该走:
FPGA DMA → Host DMA buffer
3.3 PCIe 标准配置项不放 BAR
这些不应该由你自己放 BAR:
Vendor ID
Device ID
BAR Register
MSI Capability
MSI-X Capability
PCIe Capability
Link Status
Command Register
这些属于 PCIe 配置空间,由 PCIe IP / PCIe Core 管理,不是 BAR 用户寄存器。
4. 推荐 BAR0 功能分区
如果你做单通道 Block DMA,可以这样:
BAR0 64KB
0x0000 ~ 0x00FF 全局控制 / 版本 / 能力
0x0100 ~ 0x01FF DMA channel 0 控制寄存器
0x0200 ~ 0x02FF 预留 DMA channel 1
0x0300 ~ 0x03FF 预留 DMA channel 2
0x1000 ~ 0x1FFF IRQ / event 管理
0x2000 ~ 0x2FFF debug 调试寄存器
0x3000 ~ 0x3FFF performance 统计寄存器
0x4000 ~ 0xFFFF 保留扩展
5. 单通道 Block DMA 推荐寄存器
0x0000 VERSION
0x0004 CAPABILITY
0x0008 GLOBAL_CTRL
0x000C GLOBAL_STATUS
0x0100 CH0_DMA_CTRL
0x0104 CH0_DMA_STATUS
0x0108 CH0_DMA_ADDR_LOW
0x010C CH0_DMA_ADDR_HIGH
0x0110 CH0_DMA_LEN
0x0114 CH0_DMA_DIR
0x0118 CH0_DMA_CONFIG
0x011C CH0_BYTES_DONE
0x0120 CH0_ERROR_CODE
0x0124 CH0_TIMEOUT_CFG
0x1000 IRQ_ENABLE
0x1004 IRQ_STATUS
0x1008 IRQ_CLEAR
0x100C IRQ_MASK
0x2000 DEBUG_STATE
0x2004 DEBUG_FSM
0x2008 DEBUG_FIFO_LEVEL
0x200C DEBUG_PCIE_TAG_CNT
0x3000 PERF_DMA_CYCLES
0x3004 PERF_BYTES_TOTAL
0x3008 PERF_READ_REQ_CNT
0x300C PERF_WRITE_REQ_CNT
这个比全部塞到 0x0000 ~ 0x0050 更适合后续扩展。
6. 最小可用版本
如果你只想先跑通,最少需要这些:
VERSION
DMA_CTRL
DMA_STATUS
DMA_ADDR_LOW
DMA_ADDR_HIGH
DMA_LEN
DMA_DIR
IRQ_ENABLE
IRQ_STATUS
IRQ_CLEAR
ERROR_CODE
也就是:
BAR0 + 0x0000 VERSION
BAR0 + 0x0010 DMA_CTRL
BAR0 + 0x0014 DMA_STATUS
BAR0 + 0x0020 DMA_ADDR_LOW
BAR0 + 0x0024 DMA_ADDR_HIGH
BAR0 + 0x0028 DMA_LEN
BAR0 + 0x002C DMA_DIR
BAR0 + 0x0030 IRQ_ENABLE
BAR0 + 0x0034 IRQ_STATUS
BAR0 + 0x0038 IRQ_CLEAR
BAR0 + 0x0044 ERROR_CODE
这个版本能完成:
驱动识别 FPGA
驱动配置 DMA 地址
驱动配置 DMA 长度
驱动配置方向
驱动启动 DMA
FPGA 上报完成
驱动清中断
驱动查询错误
7. BAR 空间功能分配原则
| 控制类放 BAR | START、STOP、RESET、ENABLE |
| 状态类放 BAR | BUSY、DONE、ERROR、IDLE |
| 参数类放 BAR | DMA 地址、长度、方向、通道 |
| 中断类放 BAR | IRQ_ENABLE、IRQ_STATUS、IRQ_CLEAR |
| 错误类放 BAR | ERROR_CODE、PCIE_ERR_STATUS |
| 调试类可放 BAR | FSM_STATE、FIFO_LEVEL、计数器 |
| 大数据不要放 BAR | 大数据走 DMA buffer |
| PCIe 标准能力不放 BAR | Vendor ID、MSI Capability 属于配置空间 |
8. 最终结论
BAR 空间最应该给这些功能使用:
1. FPGA 版本 / 能力识别
2. 全局复位 / 使能 / 状态
3. DMA 地址 / 长度 / 方向 / 通道配置
4. DMA START / STOP / RESET 控制
5. DMA DONE / BUSY / ERROR 状态
6. IRQ_ENABLE / IRQ_STATUS / IRQ_CLEAR
7. ERROR_CODE / PCIE_ERR_STATUS / FIFO_STATUS
8. DEBUG_STATE / 统计计数器 / 性能计数器
9. SGDMA descriptor ring base / doorbell,若以后扩展 SGDMA
不应该给这些功能使用:
1. 大块数据缓存
2. 高频连续数据流
3. Host DMA buffer 本身
4. PCIe 标准配置空间内容
一句话:
BAR 空间用来做“控制和状态”;
DMA buffer 用来做“数据搬运”。






