多通道数据采集DMA设计详解

多通道数据采集 DMA 设计详解

8 通道 × 16-bit ADC 数据,如何通过纯 FPGA 逻辑实现低延时、高带宽的 DMA 搬运?本文从架构到状态机,逐层拆解。


这个 DMA 有什么特别?

在介绍 DMA 之前,先说一下它的前级------触发采集模块 。它实现了多板级联同步、预触发捕获(Pre-trigger)、5 种触发模式、微秒级确定性延时等特性,这些功能让采集到的数据"值得被搬运"。而本文的主角 DMA 引擎,负责把这些数据高效、可靠地搬进 DDR。

这个 DMA 引擎的核心设计亮点:

🚫 零 CPU 参与

全链路纯 FPGA 硬件逻辑,上位机只需配置 5 个 AXI Lite 寄存器,剩下的全部由状态机自动完成。没有中断、没有轮询、没有驱动开销。

🧩 跨时钟域无缝桥接

ADC 采样域(100MHz)、AXI 总线域(100MHz)、DDR 接口域(133MHz)三者异步,DMA 引擎通过异步 FIFO + 状态机握手,在三个时钟域之间安全搬运数据,无毛刺、无丢失。

🎯 一个 collect_end 信号统一三种采集模式

触发采集、实时采集、瞬时采集------三种模式的数据保存,巧妙通过同一个 collect_end 采样结束标志实现。DMA 状态机不关心"为什么结束",只关心"结束了怎么收尾",实现了采集策略与搬运逻辑的彻底解耦。配合双缓冲和自动回卷,同一条 DMA 通道即可覆盖从单次快照到连续流式采集的全部场景。

💡 双缓冲(Ping-pong Buffer)+ 自动回卷

DMA 写指针到达缓冲区末尾后自动归零,形成环形缓冲。同时自动维护半满标志位,上位机可在 DMA 写后半缓冲区的同时读取前半缓冲区。瞬时模式写满即停,实时模式循环写入不中断------同一套硬件逻辑,零修改切换模式。

📦 8 通道合并搬运

8 个触发通道的数据合并为 128-bit 总线,一次 AXI Burst 搬运所有通道的当前采样值,保证通道间时间同步性。

⚡ 固定 Burst 长度优化

每次 DMA 传输固定 256 word(4096 字节),对齐 AXI4 协议最大 Burst 限制,最大化总线利用率。

📊 400 倍带宽余量

AXI4 总线带宽 1.6 GB/s,实际数据率仅 4 MB/s,DMA 永远不会成为瓶颈。

如果你对"如何用纯硬件逻辑替代 CPU 做实时数据搬运"感兴趣,或者正在为多通道高速采集的 DMA 设计发愁,这篇文章值得一读。


一、为什么需要专门的 DMA 设计?

在数据采集系统中,ADC 采样时钟和 DDR 读写时钟往往不同,数据速率也存在突发特性。如果用 CPU 去搬运数据,不仅占用宝贵的处理带宽,还会引入不确定的延时。

DMA(Direct Memory Access)的核心价值: 让硬件逻辑自主完成"从 ADC 缓冲区 → DDR 存储"的数据搬运,CPU 只需配置参数、查询状态。

在我们的纯 FPGA 架构中,DMA 引擎需要解决三个核心问题:

  1. 跨时钟域:ADC 采样时钟(100MHz)、AXI 总线时钟(100MHz)、DDR 接口时钟(133MHz)三者异步
  2. 多通道并行:8 个通道独立触发、独立缓存,但共享一条 DMA 通道
  3. 突发效率:DDR 的读写效率依赖 Burst 长度,单次传输太短会浪费带宽

二、整体数据流架构

复制代码
                        ┌─────────────────────────────────────────┐
                        │            mstar_collect (采集顶层)       │
                        │                                         │
  CM2268 ADC (8ch)      │  ┌──────────┐  ┌──────────┐            │
  16-bit × 250kSPS      │  │ triggel  │  │ triggel  │  × 8 通道   │
       │                │  │ (触发判断) │  │ (触发判断) │            │
       ▼                │  └────┬─────┘  └────┬─────┘            │
  ad_driver             │       │tADdat[127:0] │                   │
  (采样时序控制)         │       │tADen         │                   │
       │                │       └──────┬───────┘                   │
       │ sADdat[127:0]  │              │                           │
       │ sADen          │              ▼                           │
       └───────────────▶│     数据缓存 (FIFO)                      │
                        │              │                           │
                        └──────────────┼───────────────────────────┘
                                       │
                        ┌──────────────┼───────────────────────────┐
                        │   mstar_collectdma (DMA 引擎)            │
                        │              │                           │
                        │    ┌─────────▼──────────┐                │
                        │    │  onedmafifo_con     │                │
                        │    │  (跨时钟域 FIFO +   │                │
                        │    │   DMA 状态机)        │                │
                        │    └─────────┬──────────┘                │
                        │              │ AXI4 Full                  │
                        │              │ 128-bit, Burst=256         │
                        │              ▼                           │
                        │    ┌──────────────────┐                  │
                        │    │  AXI Full Matrix  │ (总线仲裁)       │
                        │    └────────┬─────────┘                  │
                        └─────────────┼────────────────────────────┘
                                      │
                                      ▼
                              ┌──────────────┐
                              │  DDR3 SDRAM   │
                              │  (数据存储)    │
                              └──────┬───────┘
                                     │
                              ┌──────▼───────┐
                              │ 以太网桥接 IP  │
                              │ (DDR→UDP打包) │
                              └──────┬───────┘
                                     │
                                     ▼
                              RGMII PHY → 网线 → PC

关键设计决策:

  • 采集模块(mstar_collect)和 DMA 模块(mstar_collectdma)分离,各自独立时钟域
  • 8 通道触发后的数据合并为 128-bit 总线(8 × 16-bit),送入 DMA
  • DMA 只有一条通道,通过内部 FIFO 缓冲多通道突发

三、触发模块:triggel

每个通道独立拥有一个 triggel 模块,负责:

3.1 触发模式

模式 编码 行为
不触发 3'b000 采满缓存即结束,等价于"连续采集"
电平触发 3'b001 数据达到阈值时触发
上升沿触发 3'b010 数据从低于阈值跳变到高于阈值
下降沿触发 3'b011 数据从高于阈值跳变到低于阈值
连续采集 3'b100 不等待触发,持续采样

3.2 预触发缓存

触发模块的核心难点是预触发(pre-trigger)缓存------需要保留触发点之前的数据。

复制代码
采集窗口:
|← pre_cachsize →|← cachsize - pre_cachsize →|
     触发前数据          触发后数据
                 ↑
              触发点 (trig_relative_position)

实现方式:

  1. 通道使能后,数据持续写入环形缓冲区(r_position_counter 循环计数)
  2. 当计数器达到 pre_cachsize 时,r_firststorage_flg 置位,开始触发判断
  3. 触发条件满足后,记录 trig_relative_position
  4. 继续采集直到 cachsize - pre_cachsize 个数据,输出 colection_end

3.3 多板同步触发

除了内部触发,还支持外部触发输入(in_trigflg):

verilog 复制代码
// 外部触发上升沿检测
get_singer_edge#(.SING_WIDTH(1)) get_triger_edge_u0(
    .clk(ad_clk),
    .i_sing(_in_trigflg),
    .o_sing_up(_in_trigflg_up),
    ...
);

// 外部触发锁存:一旦触发,保持到采集结束
always @(posedge ad_clk) begin
    if (_in_trigflg_up && collect_en > 0)
        in_trigflg_r <= 1;       // 锁存
    if (collect_end_up)
        in_trigflg_r <= 0;       // 采集结束清零
end

触发后还可以通过 trig_out 输出信号级联触发下一块板卡,实现多板同步采集。


四、DMA 引擎:onedmafifo_con

这是整个数据采集 DMA 的核心模块。

4.1 跨时钟域 FIFO

ADC 数据在 ad_clk(100MHz)域产生,DMA 传输在 m_axi_clk(100MHz)域进行。两者虽然频率相同,但相位不确定,必须经过跨时钟域处理。

复制代码
ad_clk 域                          m_axi_clk 域
    │                                    │
    │  s_axis_tvalid/tdata[127:0]        │
    ▼                                    │
┌──────────────┐    异步 FIFO     ┌──────▼───────┐
│ axis_dmadat  │ ──────────────▶  │  DMA 状态机   │
│    _fifo     │  (FIFORDWN=10)  │  (地址生成)   │
└──────────────┘                  └──────────────┘

FIFO 深度 1024 word(FIFORDWN=10,2^10),足以吸收 ADC 采样和 DDR 突发之间的速率差异。

FIFO 满判断:

verilog 复制代码
// FIFO 可读数据量 >= 1/4 总深度时,允许发起 DMA
assign w_buf_dmaen_ch = (fifo_rdwn[FIFORDWN-1:FIFORDWN-2] > 0) && w_dma_channel_en;

当 FIFO 中积累了至少 256 word(高位 bit9:8 > 0)时,触发 DMA 传输。

4.2 DMA 状态机

复制代码
                    w_DMA_channel_enup
    ┌────────┐ ──────────────────────▶ ┌────────────┐
    │  IDLE  │                         │  DMAWAIT   │
    │ (空闲) │ ◀────────────────────── │ (等待FIFO) │
    └────────┘   r_dma_oksta &&        └─────┬──────┘
                     w_DMA_channel_enup       │
                                              │ w_buf_dmaen_ch
                                              │ (FIFO数据足够)
                                              ▼
                    control_done        ┌────────────┐
    ┌────────┐ ◀────────────────────── │   DMAST    │
    │  IDLE  │                         │ (启动传输)  │
    └────────┘   r_dma_numremain==0    └─────┬──────┘
                       ▲                      │ ~control_done
                       │                      ▼
                 ┌─────┴──────┐        ┌────────────┐
                 │   DMADO    │ ◀───── │   DMAST    │
                 │ (传输完成)  │        │ (等待启动)  │
                 └────────────┘        └────────────┘

四个状态详解:

状态 编码 功能
DMASTA_IDLE 3'b000 等待通道使能上升沿,清零所有指针
DMASTA_DMAWAIT 3'b001 等待 FIFO 数据足够,或采集结束信号
DMASTA_DMAST 3'b010 向 AXI Write Master 发起传输请求 (control_go=1)
DMASTA_DMADO 3'b100 等待 AXI 传输完成 (control_done)

4.3 地址生成与 Burst 控制

每次 DMA 传输固定 256 个 128-bit word(即 DMALEN=256),对应 AXI4 Burst 长度:

verilog 复制代码
localparam DMALEN = 256;

// DMAWAIT 状态:准备传输参数
if (w_buf_dmaen_ch) begin
    r_buf_curDatNum_ch <= r_buf_curDatNum_ch + DMALEN;  // 更新已传输计数
    r_dma_numremain <= 1;                                // 标记本次传输未完成
    r_control_write_length <= DMALEN;                    // 传输长度 = 256 word
    r_dmastate <= DMASTA_DMAST;
    control_go <= 1;                                     // 启动 AXI 传输
end

地址计算:

verilog 复制代码
// 目标地址 = 缓冲区基址 + 已传输数据量
control_write_base <= buf_base_ch[ADDRESSWIDTH-1:4] + r_buf_curDatNum_ch;

注意 buf_base_ch 是字节地址,但 AXI 数据宽度为 128-bit(16 字节),所以地址偏移以 16 字节为单位([ADDRESSWIDTH-1:4])。

4.4 传输完成与循环

verilog 复制代码
// DMADO 状态:AXI 传输完成
if (control_done) begin
    if (r_dma_numremain) begin
        // 本次 256 word 传输完成,回到 DMAWAIT 检查是否继续
        r_dmastate <= DMASTA_DMAWAIT;
        if (buf_length_ch[ADDRESSWIDTH-1:4] == r_buf_curDatNum_ch)
            r_buf_curDatNum_ch <= 0;  // 到达缓冲区末尾,地址回卷
    end else begin
        // 所有数据传输完成
        r_dmastate <= DMASTA_IDLE;
        r_dma_oksta <= 1;             // 置位 DMA 完成标志
        r_buf_curDatNum_ch <= 0;
    end
end

缓冲区回卷机制: 当 r_buf_curDatNum_ch 达到 buf_length_ch 时,自动归零,实现环形缓冲。上位机可以通过 AXI Lite 接口读取当前写入位置。

4.5 半满标志(DMA_half_flg)

verilog 复制代码
DMA_half_flg <= r_buf_curDatNum_ch >= buf_length_ch[ADDRESSWIDTH-1:5] 
                && buf_length_ch[ADDRESSWIDTH-1:5] > 0;

当 DMA 已写入数据达到缓冲区一半时,DMA_half_flg 置位。上位机可以据此判断:

  • 缓冲区前半部分已有数据,可以开始读取
  • 后半部分正在写入,不要覆盖

这是典型的双缓冲(ping-pong buffer)策略。


4.6 collect_end:一个信号统一三种采集模式

本 DMA 支持触发采集、实时采集、瞬时采集 三种模式,它们的区别在于"什么时候开始采"和"什么时候停止采",但对 DMA 引擎来说,只需要知道一件事------采集结束了 。这个信息通过一个 collect_end 信号传递,这是整个设计中非常巧妙的一个抽象。

三种采集模式的 collect_end 产生方式

复制代码
┌─────────────┬───────────────────────┬────────────────────────────┐
│  采集模式    │  何时产生 collect_end │  典型应用场景              │
├─────────────┼───────────────────────┼────────────────────────────┤
│ 瞬时采集    │ 缓冲区写满自动结束    │ 单次快照、设备调试          │
│ (无触发)    │ position == max_pos   │                            │
├─────────────┼───────────────────────┼────────────────────────────┤
│ 触发采集    │ 触发后采够 post 数据  │ 示波器、故障捕获、          │
│ (边沿/电平) │ 自动结束              │ 局部放电检测              │
├─────────────┼───────────────────────┼────────────────────────────┤
│ 实时采集    │ 上位机主动写          │ 连续监测、长时间记录、      │
│ (手动停止)  │ collect_endreq=1      │ 振动分析                  │
└─────────────┴───────────────────────┴────────────────────────────┘

三种模式产生 collect_end 的时机完全不同,但信号波形一样------都是一个单周期脉冲。DMA 状态机不需要知道"为什么结束",只需要知道"结束了",然后执行统一的收尾流程。

这就是策略与机制的解耦: 触发模块决定"何时停",DMA 模块负责"怎么收尾"。两者通过一个信号握手,互不侵入。

DMA 收到 collect_end 后的收尾流程

verilog 复制代码
// DMAWAIT 状态:检测到 collect_end
else if (r_collect_end && (~r_dma_oksta)) begin
    if (fifo_rdwn > 0) begin
        // FIFO 还有残留数据 → 发起最后一次 Burst,把数据搬完
        r_buf_curDatNum_ch   <= r_buf_curDatNum_ch + fifo_rdwn;
        r_control_write_length <= fifo_rdwn;
        r_dma_numremain      <= 0;     // 标记为最后一次传输
        r_dmastate           <= DMASTA_DMAST;
        control_go           <= 1;
    end else begin
        // FIFO 已空 → 直接结束
        r_dma_oksta <= 1;              // 置位完成标志,通知上位机
        r_dmastate  <= DMASTA_IDLE;
    end
end

关键设计点:

  1. 不丢数据 :即使收到 collect_end,DMA 也不会立即停止,而是先把 FIFO 中的残留数据搬完
  2. r_dma_numremain = 0:标记这次传输是最后一次,传输完成后直接进入 IDLE,不再循环
  3. r_dma_oksta = 1:通知上位机"数据已经全部在 DDR 里了,可以来读了"

collect_end + 双缓冲 + 自动回卷 = 三种模式无缝切换

DMA 的缓冲区管理有两个关键机制:

缓冲区自动回卷:

verilog 复制代码
// 每次 Burst 完成后检查:到达缓冲区末尾了吗?
if (buf_length_ch[ADDRESSWIDTH-1:4] == r_buf_curDatNum_ch)
    r_buf_curDatNum_ch <= 0;   // 地址归零,从头开始写

半满标志(双缓冲指示):

verilog 复制代码
DMA_half_flg <= r_buf_curDatNum_ch >= buf_length_ch[ADDRESSWIDTH-1:5]
                && buf_length_ch[ADDRESSWIDTH-1:5] > 0;

这两个机制配合 collect_end,让同一条 DMA 通道自然支持不同场景:

复制代码
场景 1:瞬时采集(单次快照)
─────────────────────────────────────────────
DDR 缓冲区:  [0 .................. buf_length]
DMA 写入:    |████████████████████| collect_end → DMA 停止
上位机读取:                        |← 一次性全部读出 →|

场景 2:触发采集(故障捕获)
─────────────────────────────────────────────
DDR 缓冲区:  [0 .................. buf_length]
DMA 写入:    |████████████████████| collect_end → DMA 停止
上位机读取:                        |← 触发前后数据全部读出 →|

场景 3:实时采集(连续流式)
─────────────────────────────────────────────
DDR 缓冲区:  [0 ........... half ........... buf_length]
DMA 写入 1:  |████████████| half_flg=1
上位机读取:               |← 读前半 →|
DMA 写入 2:               |████████████| 回卷到 0
上位机读取:  |← 读后半 →|
DMA 写入 3:  |████████████| half_flg=1
   ... 循环不中断,直到上位机发 collect_endreq

场景 3 的精髓: 实时采集时,collect_end 一直不来,DMA 写指针到达缓冲区末尾后自动归零,继续从头写入。上位机通过 DMA_half_flg 判断哪一半缓冲区可以安全读取,实现"DMA 写一半、上位机读另一半"的 ping-pong 操作。当上位机决定停止采集时,写入 collect_endreq=1,触发模块产生 collect_end,DMA 执行收尾流程,干净利落地结束。

三种模式,同一套硬件逻辑,零修改切换。 这就是纯 FPGA 架构的优势------不需要软件重新配置,不需要切换代码路径,只需要改变 collect_end 的产生时机。


五、AXI Lite 寄存器接口

DMA 引擎通过 ad_dma_axictrl 模块暴露 5 个 32-bit 寄存器,供上位机配置:

偏移地址 名称 读/写 说明
0x00 ADDR_ADMA_ID 只读 模块 ID(MODEL_ID),用于识别设备
0x04 ADDR_DMACON 读写 控制寄存器:[0] 通道使能,[1] 复位请求;状态:[16] 半满标志,[17] DMA 完成
0x08 ADDR_BUFBASE 读写 缓冲区基地址(DDR 物理地址)
0x0C ADDR_BUFLEN 读写 缓冲区长度(单位:128-bit word)
0x10 ADDR_PREBUF 读写 预触发缓冲区长度

寄存器操作时序:

复制代码
写操作(配置 DMA):
  AWADDR=0x04, WDATA=0x01  →  使能通道 0
  AWADDR=0x08, WDATA=0x1000_0000  →  设置缓冲区基址
  AWADDR=0x0C, WDATA=0x0010_0000  →  设置缓冲区长度 (1M word)

读操作(查询状态):
  ARADDR=0x04  →  RDATA[17:16] = {half_flg, dma_ok}
                  RDATA[1:0]  = {rst_req, channel_en}

控制逻辑的安全设计:

verilog 复制代码
// 使能和复位的互锁逻辑
if (dma_ok_up) begin
    DMA_channel_en <= 0;    // DMA 完成后自动清零使能
    DMA_rst_req <= 0;
end else if (w_hs && waddr == ADDR_DMACON) begin
    if (DMA_channel_en && WDATA[1])
        DMA_rst_req <= WDATA[1];       // 已使能时写 bit[1] = 请求复位
    else if (WDATA[0])
        DMA_channel_en <= WDATA[0];    // 写 bit[0] = 使能通道
end

必须先使能通道,才能请求复位;DMA 完成后自动清除使能,防止重复触发。


六、AXI4 Full 写通道

DMA 状态机通过 axi_dma_example_axi_write_master 模块驱动 AXI4 Full 写通道:

6.1 接口信号

复制代码
control_go        →  启动一次传输
control_done      ←  传输完成
control_addr_offset  →  目标地址 (字节对齐)
control_xfer_size_in_bytes  →  传输字节数

m_axi_awvalid/awready/awaddr/awlen  →  写地址通道
m_axiwvalid/wready/wdata/wstrb/wlast →  写数据通道
m_axi_bvalid/bready                  →  写响应通道

6.2 Burst 参数计算

verilog 复制代码
localparam integer LP_AXI_DW_BYTES = ADDRESSWIDTH / 8;  // 地址宽度/8 = 字节数
localparam integer LP_AXI_BURST_LEN = 4096 / LP_AXI_DW_BYTES < 256 
    ? 4096 / LP_AXI_DW_BYTES : 256;

AXI4 协议规定单次 Burst 最大 4096 字节。当地址宽度为 28-bit 时:

  • LP_AXI_DW_BYTES = 28 / 8 = 3(实际按 4 字节对齐)
  • LP_AXI_BURST_LEN = min(4096/4, 256) = 256

6.3 outstanding 传输

verilog 复制代码
localparam integer LP_AXI_BRAM_DEPTH = 512;
localparam integer LP_AXI_RD_MAX_OUTSTANDING = LP_AXI_BRAM_DEPTH / LP_AXI_BURST_LEN;

允许最多 512 / 256 = 2 个 outstanding 传输,提高总线利用率。


七、多通道合并与分时复用

8 个通道的触发数据通过 mstar_collect 顶层合并:

verilog 复制代码
// 8 通道触发后的数据合并为 128-bit
wire [127:0] tADdat;   // 8 × 16-bit = 128-bit
wire         tADen;     // 任一通道有数据时有效

assign tADen = |t_ADen;  // 8 通道 OR

分时复用策略:

  • 8 通道共享一条 128-bit 数据总线
  • 每个 ADC 采样周期,8 通道数据同时出现在总线上
  • DMA 以 128-bit 宽度接收,一次传输包含所有 8 通道的当前采样值

这种方式避免了 8 条独立 DMA 通道的资源竞争,同时保证了通道间的时间同步性。


八、性能分析

8.1 带宽计算

参数 值
ADC 采样率 250 kSPS/ch
通道数 8
每采样数据量 16 bit × 8 = 128 bit
原始数据率 250k × 128 = 32 Mbps = 4 MB/s
AXI4 总线带宽 100MHz × 128-bit = 1.6 GB/s
DDR 写带宽 133MHz × 128-bit × 2 (DDR) = 4.2 GB/s
带宽利用率 4 MB/s / 1.6 GB/s ≈ 0.25%

结论: DMA 带宽远大于实际需求,瓶颈在 ADC 采样率而非总线。

8.2 延时分析

环节 延时
ADC 采样 → 触发判断 1 个采样周期 (4μs @250kSPS)
触发 → FIFO 写入 1 个时钟周期 (10ns)
FIFO 积累 256 word 256 × 10ns = 2.56μs
AXI Burst 传输 256 × 10ns = 2.56μs
总 DMA 延时 ~5μs

从触发到数据写入 DDR,延时在微秒级别,远优于 CPU+OS 方案(通常毫秒级)。

8.3 资源占用

资源 用量
LUT ~60% (PH1P35)
FF ~40%
BRAM 2 个 (跨时钟域 FIFO)
时钟域 3 个 (50M/100M/133M)

九、设计经验与踩坑记录

9.1 跨时钟域信号必须打拍

verilog 复制代码
// 错误:直接用 ad_clk 域的信号控制 m_axi_clk 域的状态机
if (ad_collect_endflg)  // ❌ 跨时钟域直接采样

// 正确:两级寄存器同步
always @(posedge m_axi_clk) begin
    r_ad_collect_endflg <= {r_ad_collect_endflg[0], ad_collect_endflg};
end
assign w_ad_collect_endup = (r_ad_collect_endflg[1] ^ r_ad_collect_endflg[0]) 
                            & r_ad_collect_endflg[0];  // ✅ 上升沿检测

9.2 AXI 地址对齐

AXI4 协议要求地址按数据宽度对齐。128-bit 数据宽度下,地址低 4 位必须为 0:

verilog 复制代码
// buf_base_ch 是字节地址,传给 AXI Master 时需要右移 4 位
control_write_base <= buf_base_ch[ADDRESSWIDTH-1:4] + r_buf_curDatNum_ch;
// 实际地址 = {control_write_base, 4'b0000}

9.3 DMA 完成标志的清除时机

verilog 复制代码
// 错误:在 IDLE 状态清除完成标志
DMASTA_IDLE: begin
    r_dma_oksta <= 0;  // ❌ 一进入 IDLE 就清零,上位机可能还没读到

// 正确:在通道使能上升沿清除
if (w_DMA_channel_enup) begin
    r_dma_oksta <= 0;  // ✅ 上位机主动启动新一次采集时才清零
end

9.4 FIFO 满信号的处理

verilog 复制代码
// FIFO 满时不能简单丢弃数据,需要背压上游
assign tx_tready = !fifo_full && data_active;

// 但 ADC 采样不能暂停,所以 FIFO 深度要足够大
// 经验公式:FIFO_DEPTH >= (ADC采样率 / AXI时钟频率) × Burst长度 × 2
// = (250k / 100M) × 256 × 2 ≈ 1280 word
// 实际选择 1024 word (FIFORDWN=10),靠 DMA 状态机保证不溢出

十、总结

本文详解了一个纯 FPGA 实现的多通道数据采集 DMA 引擎,核心设计要点:

  1. 触发与 DMA 分离 :triggel 模块专注触发判断,onedmafifo_con 专注数据搬运,职责清晰
  2. collect_end 统一三种采集模式:触发采集、实时采集、瞬时采集,通过同一个信号完成策略与机制的解耦
  3. 跨时钟域 FIFO 缓冲:吸收 ADC 采样和 DDR 突发之间的速率差异
  4. 双缓冲 + 自动回卷:半满标志实现 ping-pong 读写,地址到末尾自动归零,支持连续流式采集
  5. 固定 Burst 长度:256 word/次,简化地址生成逻辑,提高总线效率
  6. AXI Lite 寄存器接口:上位机只需配置 5 个寄存器即可控制整个 DMA
  7. 多通道合并:8 通道共享 128-bit 总线,保证时间同步性

整个 DMA 引擎在 100MHz 时钟下,可实现 1.6 GB/s 的写带宽,远超 8 通道 250kSPS ADC 的 4 MB/s 数据率,留有 400 倍余量。

这就是纯 FPGA 架构做 DMA 的优势:确定性、低延时、高带宽、零 CPU 开销。


项目地址:


作者是一名 FPGA 工程师,专注于纯硬件架构的数据采集系统设计。如果你对"如何用 FPGA 替代 CPU 做实时数据处理"感兴趣,欢迎交流。

相关推荐
ACP广源盛139246256733 小时前
Type-C 扩展坞选型笔记:DP1.4a 转 HDMI2.0 桥接方案 GSV2201E @ACP评估
笔记·硬件架构·硬件工程·视频·hdmi·国产芯片
上海云盾-小余4 小时前
TCP 洪水与 UDP 反射 DDoS 原理对比:流量放大机制与检测逻辑详解
tcp/ip·udp·ddos
华允物联-HUAIOT5 小时前
DTU 和 LoRa 模块有什么区别?工业无线回传怎么选
网络·tcp/ip·udp
泡泡糖的中文规格书6 小时前
HDLbits 1.4 学习笔记
fpga开发
智感子21 小时前
FPGA触发采集:预触发、多通道同步与软硬件分工怎么落地
fpga开发
ACP广源盛139246256731 天前
HDMI2.1 同源双屏方案选型,GSV6502@ACP 工程评估笔记
笔记·硬件架构·音视频·硬件工程·hdmi·国产芯片
156082072192 天前
关于AGC性能测试
fpga开发
aixingkong9212 天前
UALink 技术白皮书解读
服务器·网络·硬件架构
2501_915909062 天前
抓包工具断点拦截是什么?如何半路拦截修改 HTTP 请求
网络协议·计算机网络·网络安全·adb·https·udp