第4章:PCIe与DMA:打通CPU与FPGA的数据高速公路

做低延迟交易系统,最头疼的问题是什么?

我个人觉得,就是CPU和FPGA之间的数据搬运。你想想看,FPGA处理数据再快,如果数据从CPU传过来要几十微秒,那前面所有的优化都白费了。这就像修了一条超级高铁,结果进站出站要花半小时——没人受得了。

4.1 为什么非得用PCIe?

很多刚入行的朋友问我:为什么不用以太网?或者直接用GPIO?

嗯,这个问题我当年也纠结过。以太网确实通用,但延迟太高。你想想,一个UDP包从网卡到应用层,光协议栈解析就要好几微秒。GPIO呢?延迟低,但带宽太小,数据量一大就卡死。

PCIe不一样。它直接挂在CPU的总线上,说白了就是CPU和FPGA之间的一条专用通道。延迟能做到几百纳秒级别,带宽更是几十Gbps起步。我在项目中遇到过用PCIe Gen3 x8的配置,实测带宽接近64Gbps,处理行情数据绰绰有余。

核心要点:PCIe的优势在于低延迟+高带宽+低CPU开销。交易系统中,这三者缺一不可。

4.2 DMA:让数据自己跑起来

光有PCIe还不够。如果没有DMA,CPU就得亲自参与每一次数据传输。你想想看,CPU每秒要处理几百万笔订单,如果每笔数据都要它亲自搬,那它哪还有时间做策略计算?

DMA(直接内存访问)就是干这个的。它让数据直接在FPGA和系统内存之间传输,CPU只需要在开始和结束时打个招呼就行。

我曾经在一个项目中,刚开始没用DMA,CPU占用率直接飙到80%以上。后来改成DMA,CPU占用率降到了5%以下。效果就是这么明显。

4.3 PCIe+DMA的典型架构

下面这张图是我自己总结的典型架构。你看一眼就明白了:

CPU 策略计算 订单管理 系统内存 DMA缓冲区 内存访问 PCIe Gen3 x8 DMA传输 FPGA PCIe硬核 DMA控制器 行情处理引擎 订单生成

这个架构图里,关键点有三个:

  • PCIe硬核:FPGA内部集成的PCIe物理层,负责协议解析
  • DMA控制器:管理数据传输,不需要CPU插手
  • DMA缓冲区:系统内存中预留的区域,FPGA可以直接读写

4.4 实战:配置DMA传输

光说不练假把式。下面是我在实际项目中用过的DMA配置代码片段。这是Xilinx的XDMA驱动,我稍微简化了一下:

// 初始化DMA通道
int dma_init(struct xdma_dev *dev, int channel) {
    struct xdma_desc *desc;
    dma_addr_t dma_handle;
    
    // 分配DMA缓冲区(4MB)
    desc = dma_alloc_coherent(dev->dev, 
                              DMA_BUF_SIZE, 
                              &dma_handle, 
                              GFP_KERNEL);
    if (!desc) {
        printk("DMA buffer allocation failed\n");
        return -ENOMEM;
    }
    
    // 配置DMA描述符
    desc->src_addr = dma_handle;      // FPGA写入地址
    desc->dst_addr = dma_handle;      // CPU读取地址
    desc->length   = DMA_BUF_SIZE;    // 传输大小
    desc->control  = DMA_CTRL_IE;     // 使能中断
    
    // 启动DMA传输
    xdma_channel_start(dev, channel, desc);
    
    return 0;
}

经验之谈:DMA缓冲区大小要仔细权衡。太小了频繁中断,太大了浪费内存。我一般用4MB,对于行情数据来说刚刚好。

4.5 延迟优化:几个关键点

做低延迟系统,光会用DMA还不够。你得知道怎么优化。我总结了几条实战经验:

  1. 中断合并:别让每个包都触发中断。我习惯攒够一批数据再通知CPU,这样能减少上下文切换的开销。
  2. 内存对齐:DMA缓冲区必须按64字节对齐。不对齐的话,PCIe会做额外的拆分重组,延迟直接翻倍。
  3. 预取数据:CPU可以在FPGA还没处理完时,提前把下一批数据从DMA缓冲区搬到L2缓存里。这个技巧我用了好几年,效果显著。
  4. 避免乒乓:很多人喜欢用双缓冲(乒乓操作),但在交易系统中,单缓冲+覆盖写的方式延迟更低。前提是你得保证CPU读数据的速度比FPGA写数据快。

避坑指南:我曾经在一个项目中,DMA传输总是丢数据。查了三天才发现是PCIe的Max Payload Size没配置对。默认是128字节,我改成256字节后问题就解决了。记住:这个参数必须和FPGA侧保持一致。

4.6 性能实测数据

下面是我在某个项目中实测的数据,用的是Xilinx KU060芯片,PCIe Gen3 x8:

传输方式 延迟(单向) 带宽 CPU占用率
PIO(程序控制) 2.3 μs 1.2 GB/s 85%
DMA(单通道) 0.8 μs 6.4 GB/s 12%
DMA(双通道) 0.6 μs 7.8 GB/s 8%

看到没?DMA不仅延迟低,CPU占用率也低了一大截。这就是为什么所有低延迟交易系统都在用DMA。

4.7 小结

PCIe和DMA的组合,说白了就是让数据在CPU和FPGA之间自由流动,不需要CPU操心。你想想看,CPU省下来的时间可以做什么?做策略计算、做风控、做订单管理——这才是它该干的事。

嗯,这一章的内容就到这里。记住我说的:DMA缓冲区要对齐、中断要合并、参数要匹配。这些细节做好了,你的系统延迟就能再降一个数量级。

一句话总结:PCIe是路,DMA是车。路要宽,车要快,关键是别让CPU当司机。

专注资料整理