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_STATUS1.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_STATUS2. 建议放 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_COUNT2.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好处是驱动容易写:
#defineCH_BASE(ch)(0x0100+(ch)*0x100)#defineCH_DMA_CTRL(ch)(CH_BASE(ch)+0x00)#defineCH_DMA_STATUS(ch)(CH_BASE(ch)+0x04)#defineCH_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 buffer3.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 用来做“数据搬运”。