从近似0基础开始FPGA开发——Part.23:AXI/AXIS 数据搬运、DMA 与 DDR 缓存架构设计

本文基于 Xilinx Kintex UltraScale+ XCKU5P-FFVB676-2-I + Vivado + DDR4 MIG 展开,承接前文的 UDP、AXI、DDR、跨时钟域、复位和 FIFO 设计。目标不是再讲一次 AXI 信号定义,而是把这些模块真正串成一条可以仿真、可以上板、可以测速的数据通路:

RGMII/UDP → AXI4-Stream → 自定义 DMA → AXI4 Full → MIG DDR4


1. 为什么还要单独讲"数据搬运"

在前面的文章中,我们已经分别讨论过:

  • UDP 数据怎样进入 FPGA;
  • AXI4-Stream、AXI4-Lite 和 AXI4 Full 的基本区别;
  • MIG 如何把 FPGA 内部总线转换成 DDR4 读写;
  • 异步 FIFO、复位释放和时序约束;
  • FIFO 如何承担突发吸收、位宽转换和跨时钟域缓存。

这些模块单独跑通,并不等于整个工程能够持续搬运数据。

真正把网络数据写入 DDR,再从 DDR 读出并送回网络,需要回答下面几个工程问题:

  1. 一个 UDP 包应该写到 DDR 的什么地址?
  2. AXIS 只有数据流,地址和长度从哪里来?
  3. 一包数据不是 AXI 总线宽度的整数倍,最后一拍怎样写?
  4. 一次传输跨越 4 KB 地址边界时,Burst 怎样拆分?
  5. MIG 的 UI 时钟与以太网时钟不同,在哪里跨时钟域?
  6. DDR 来不及接收或者 UDP 暂时不能发送时,怎样反压而不丢包?
  7. 没有 MicroBlaze 时,怎样启动、管理和验证 DMA?
  8. 仿真通过以后,上板怎样判断问题出在 UDP、DMA、MIG 还是时钟复位?

这些问题共同构成了本篇的主题:数据搬运架构。


2. 先把三个概念分清楚

2.1 AXI4-Stream:只描述"数据怎么流"

AXI4-Stream 适合连续数据通路。最常用的信号如下:

信号 方向 作用
TDATA Master → Slave 数据
TVALID Master → Slave 当前数据有效
TREADY Slave → Master 接收端可以接收
TLAST Master → Slave 当前拍为一帧最后一拍
TKEEP Master → Slave 每个字节是否有效
TUSER 自定义 错误、端口号、时间戳等旁带信息

一次传输只在下面条件成立时发生:

text 复制代码
axis_fire = s_axis_tvalid && s_axis_tready

AXIS 不携带存储地址。它只能告诉下游"这里有一拍数据",不能直接告诉 DDR"请写到 0x8000_1000"。

2.2 AXI4 Full:描述"存储器事务怎么完成"

AXI4 Full 面向地址映射空间,读写通道相互独立:

通道 主要信号 功能
写地址 AW AWADDR/AWLEN/AWSIZE/AWBURST/AWVALID/AWREADY 发出写 Burst 地址
写数据 W WDATA/WSTRB/WLAST/WVALID/WREADY 发送写数据
写响应 B BRESP/BVALID/BREADY 返回写结果
读地址 AR ARADDR/ARLEN/ARSIZE/ARBURST/ARVALID/ARREADY 发出读 Burst 地址
读数据 R RDATA/RRESP/RLAST/RVALID/RREADY 返回读数据与状态

要特别注意:AW 和 W 是两个独立通道 。AWREADY 与 WREADY 不保证同时到来,RTL 不能把二者写成"必须同拍握手"。

2.3 DMA:连接"流"与"存储器"的搬运引擎

DMA 并不是一种新的总线。它通常一侧接 AXI4-Stream,另一侧接 AXI4 Full,并负责:

  • 根据命令产生地址和长度;
  • 把长传输拆成合法 Burst;
  • 处理 4 KB 边界;
  • 产生 WSTRB 和 WLAST;
  • 统计完成字节数;
  • 检查 BRESP/RRESP;
  • 在 AXIS 与 AXI4 Full 之间进行流量控制。

常用方向命名如下:

  • S2MM(Stream to Memory-Mapped):AXIS 数据写入 DDR;
  • MM2S(Memory-Mapped to Stream):从 DDR 读数据并输出 AXIS。

#mermaid-svg-mhHOfudECFDQ9OWz{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-mhHOfudECFDQ9OWz .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-mhHOfudECFDQ9OWz .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-mhHOfudECFDQ9OWz .error-icon{fill:#552222;}#mermaid-svg-mhHOfudECFDQ9OWz .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-mhHOfudECFDQ9OWz .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-mhHOfudECFDQ9OWz .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-mhHOfudECFDQ9OWz .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-mhHOfudECFDQ9OWz .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-mhHOfudECFDQ9OWz .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-mhHOfudECFDQ9OWz .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-mhHOfudECFDQ9OWz .marker{fill:#333333;stroke:#333333;}#mermaid-svg-mhHOfudECFDQ9OWz .marker.cross{stroke:#333333;}#mermaid-svg-mhHOfudECFDQ9OWz svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-mhHOfudECFDQ9OWz p{margin:0;}#mermaid-svg-mhHOfudECFDQ9OWz .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-mhHOfudECFDQ9OWz .cluster-label text{fill:#333;}#mermaid-svg-mhHOfudECFDQ9OWz .cluster-label span{color:#333;}#mermaid-svg-mhHOfudECFDQ9OWz .cluster-label span p{background-color:transparent;}#mermaid-svg-mhHOfudECFDQ9OWz .label text,#mermaid-svg-mhHOfudECFDQ9OWz span{fill:#333;color:#333;}#mermaid-svg-mhHOfudECFDQ9OWz .node rect,#mermaid-svg-mhHOfudECFDQ9OWz .node circle,#mermaid-svg-mhHOfudECFDQ9OWz .node ellipse,#mermaid-svg-mhHOfudECFDQ9OWz .node polygon,#mermaid-svg-mhHOfudECFDQ9OWz .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-mhHOfudECFDQ9OWz .rough-node .label text,#mermaid-svg-mhHOfudECFDQ9OWz .node .label text,#mermaid-svg-mhHOfudECFDQ9OWz .image-shape .label,#mermaid-svg-mhHOfudECFDQ9OWz .icon-shape .label{text-anchor:middle;}#mermaid-svg-mhHOfudECFDQ9OWz .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-mhHOfudECFDQ9OWz .rough-node .label,#mermaid-svg-mhHOfudECFDQ9OWz .node .label,#mermaid-svg-mhHOfudECFDQ9OWz .image-shape .label,#mermaid-svg-mhHOfudECFDQ9OWz .icon-shape .label{text-align:center;}#mermaid-svg-mhHOfudECFDQ9OWz .node.clickable{cursor:pointer;}#mermaid-svg-mhHOfudECFDQ9OWz .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-mhHOfudECFDQ9OWz .arrowheadPath{fill:#333333;}#mermaid-svg-mhHOfudECFDQ9OWz .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-mhHOfudECFDQ9OWz .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-mhHOfudECFDQ9OWz .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mhHOfudECFDQ9OWz .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-mhHOfudECFDQ9OWz .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mhHOfudECFDQ9OWz .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-mhHOfudECFDQ9OWz .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-mhHOfudECFDQ9OWz .cluster text{fill:#333;}#mermaid-svg-mhHOfudECFDQ9OWz .cluster span{color:#333;}#mermaid-svg-mhHOfudECFDQ9OWz div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-mhHOfudECFDQ9OWz .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-mhHOfudECFDQ9OWz rect.text{fill:none;stroke-width:0;}#mermaid-svg-mhHOfudECFDQ9OWz .icon-shape,#mermaid-svg-mhHOfudECFDQ9OWz .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mhHOfudECFDQ9OWz .icon-shape p,#mermaid-svg-mhHOfudECFDQ9OWz .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-mhHOfudECFDQ9OWz .icon-shape .label rect,#mermaid-svg-mhHOfudECFDQ9OWz .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mhHOfudECFDQ9OWz .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-mhHOfudECFDQ9OWz .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-mhHOfudECFDQ9OWz :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} AXI4-Stream 输入
S2MM DMA
DDR 地址空间
MM2S DMA
AXI4-Stream 输出

AMD 的 AXI DMA IP 就是典型的 AXI4-Stream 与 AXI4 存储器之间的高带宽搬运模块;如果工程控制方式、描述符格式或资源预算不适合现成 IP,也可以自己实现一套精简 DMA。


3. 本工程的总体架构

本篇采用下面的工程目标:

  • UDP 接收端输出 64 bit AXIS,工作在 125 MHz;
  • DDR4 MIG AXI 数据宽度以实际配置为准,示例按 256 bit 说明;
  • MIG UI 时钟示例按 300 MHz 说明;
  • 支持 UDP 数据写 DDR、DDR 数据读出后再经 UDP 发送;
  • 第一版只允许单个未完成的读事务和写事务;
  • 没有 MicroBlaze 也可以通过 BIST 状态机自检;
  • 预留 AXI4-Lite 寄存器,后续可接 MicroBlaze、PCIe BAR 或 JTAG AXI Master。

3.1 数据通路

#mermaid-svg-mQp9jrJPdAGb9v35{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-mQp9jrJPdAGb9v35 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-mQp9jrJPdAGb9v35 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-mQp9jrJPdAGb9v35 .error-icon{fill:#552222;}#mermaid-svg-mQp9jrJPdAGb9v35 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-mQp9jrJPdAGb9v35 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-mQp9jrJPdAGb9v35 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-mQp9jrJPdAGb9v35 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-mQp9jrJPdAGb9v35 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-mQp9jrJPdAGb9v35 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-mQp9jrJPdAGb9v35 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-mQp9jrJPdAGb9v35 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-mQp9jrJPdAGb9v35 .marker.cross{stroke:#333333;}#mermaid-svg-mQp9jrJPdAGb9v35 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-mQp9jrJPdAGb9v35 p{margin:0;}#mermaid-svg-mQp9jrJPdAGb9v35 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-mQp9jrJPdAGb9v35 .cluster-label text{fill:#333;}#mermaid-svg-mQp9jrJPdAGb9v35 .cluster-label span{color:#333;}#mermaid-svg-mQp9jrJPdAGb9v35 .cluster-label span p{background-color:transparent;}#mermaid-svg-mQp9jrJPdAGb9v35 .label text,#mermaid-svg-mQp9jrJPdAGb9v35 span{fill:#333;color:#333;}#mermaid-svg-mQp9jrJPdAGb9v35 .node rect,#mermaid-svg-mQp9jrJPdAGb9v35 .node circle,#mermaid-svg-mQp9jrJPdAGb9v35 .node ellipse,#mermaid-svg-mQp9jrJPdAGb9v35 .node polygon,#mermaid-svg-mQp9jrJPdAGb9v35 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-mQp9jrJPdAGb9v35 .rough-node .label text,#mermaid-svg-mQp9jrJPdAGb9v35 .node .label text,#mermaid-svg-mQp9jrJPdAGb9v35 .image-shape .label,#mermaid-svg-mQp9jrJPdAGb9v35 .icon-shape .label{text-anchor:middle;}#mermaid-svg-mQp9jrJPdAGb9v35 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-mQp9jrJPdAGb9v35 .rough-node .label,#mermaid-svg-mQp9jrJPdAGb9v35 .node .label,#mermaid-svg-mQp9jrJPdAGb9v35 .image-shape .label,#mermaid-svg-mQp9jrJPdAGb9v35 .icon-shape .label{text-align:center;}#mermaid-svg-mQp9jrJPdAGb9v35 .node.clickable{cursor:pointer;}#mermaid-svg-mQp9jrJPdAGb9v35 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-mQp9jrJPdAGb9v35 .arrowheadPath{fill:#333333;}#mermaid-svg-mQp9jrJPdAGb9v35 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-mQp9jrJPdAGb9v35 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-mQp9jrJPdAGb9v35 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mQp9jrJPdAGb9v35 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-mQp9jrJPdAGb9v35 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mQp9jrJPdAGb9v35 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-mQp9jrJPdAGb9v35 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-mQp9jrJPdAGb9v35 .cluster text{fill:#333;}#mermaid-svg-mQp9jrJPdAGb9v35 .cluster span{color:#333;}#mermaid-svg-mQp9jrJPdAGb9v35 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-mQp9jrJPdAGb9v35 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-mQp9jrJPdAGb9v35 rect.text{fill:none;stroke-width:0;}#mermaid-svg-mQp9jrJPdAGb9v35 .icon-shape,#mermaid-svg-mQp9jrJPdAGb9v35 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mQp9jrJPdAGb9v35 .icon-shape p,#mermaid-svg-mQp9jrJPdAGb9v35 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-mQp9jrJPdAGb9v35 .icon-shape .label rect,#mermaid-svg-mQp9jrJPdAGb9v35 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mQp9jrJPdAGb9v35 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-mQp9jrJPdAGb9v35 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-mQp9jrJPdAGb9v35 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} RGMII PHY
以太网/IPv4/UDP 协议栈
RX AXIS 异步帧 FIFO
包解析与描述符生成
64→256 bit 数据打包
S2MM Burst 规划器
AXI4 写主机
SmartConnect / Clock Converter
MIG DDR4
AXI4 读主机
256→64 bit 数据拆包
TX AXIS 异步帧 FIFO

图中最关键的两个边界是:

  1. UDP 域到 DDR 域的 数据、描述符和复位跨时钟域;
  2. AXIS 帧语义到 AXI4 地址语义的 协议转换。

3.2 控制通路

数据通路之外,还需要一条低带宽控制通路:
#mermaid-svg-3qKphhAwXZQ1BRXk{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-3qKphhAwXZQ1BRXk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3qKphhAwXZQ1BRXk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3qKphhAwXZQ1BRXk .error-icon{fill:#552222;}#mermaid-svg-3qKphhAwXZQ1BRXk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3qKphhAwXZQ1BRXk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3qKphhAwXZQ1BRXk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3qKphhAwXZQ1BRXk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3qKphhAwXZQ1BRXk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3qKphhAwXZQ1BRXk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3qKphhAwXZQ1BRXk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3qKphhAwXZQ1BRXk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3qKphhAwXZQ1BRXk .marker.cross{stroke:#333333;}#mermaid-svg-3qKphhAwXZQ1BRXk svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3qKphhAwXZQ1BRXk p{margin:0;}#mermaid-svg-3qKphhAwXZQ1BRXk .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-3qKphhAwXZQ1BRXk .cluster-label text{fill:#333;}#mermaid-svg-3qKphhAwXZQ1BRXk .cluster-label span{color:#333;}#mermaid-svg-3qKphhAwXZQ1BRXk .cluster-label span p{background-color:transparent;}#mermaid-svg-3qKphhAwXZQ1BRXk .label text,#mermaid-svg-3qKphhAwXZQ1BRXk span{fill:#333;color:#333;}#mermaid-svg-3qKphhAwXZQ1BRXk .node rect,#mermaid-svg-3qKphhAwXZQ1BRXk .node circle,#mermaid-svg-3qKphhAwXZQ1BRXk .node ellipse,#mermaid-svg-3qKphhAwXZQ1BRXk .node polygon,#mermaid-svg-3qKphhAwXZQ1BRXk .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3qKphhAwXZQ1BRXk .rough-node .label text,#mermaid-svg-3qKphhAwXZQ1BRXk .node .label text,#mermaid-svg-3qKphhAwXZQ1BRXk .image-shape .label,#mermaid-svg-3qKphhAwXZQ1BRXk .icon-shape .label{text-anchor:middle;}#mermaid-svg-3qKphhAwXZQ1BRXk .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-3qKphhAwXZQ1BRXk .rough-node .label,#mermaid-svg-3qKphhAwXZQ1BRXk .node .label,#mermaid-svg-3qKphhAwXZQ1BRXk .image-shape .label,#mermaid-svg-3qKphhAwXZQ1BRXk .icon-shape .label{text-align:center;}#mermaid-svg-3qKphhAwXZQ1BRXk .node.clickable{cursor:pointer;}#mermaid-svg-3qKphhAwXZQ1BRXk .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-3qKphhAwXZQ1BRXk .arrowheadPath{fill:#333333;}#mermaid-svg-3qKphhAwXZQ1BRXk .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3qKphhAwXZQ1BRXk .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3qKphhAwXZQ1BRXk .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3qKphhAwXZQ1BRXk .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-3qKphhAwXZQ1BRXk .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3qKphhAwXZQ1BRXk .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-3qKphhAwXZQ1BRXk .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3qKphhAwXZQ1BRXk .cluster text{fill:#333;}#mermaid-svg-3qKphhAwXZQ1BRXk .cluster span{color:#333;}#mermaid-svg-3qKphhAwXZQ1BRXk div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-3qKphhAwXZQ1BRXk .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-3qKphhAwXZQ1BRXk rect.text{fill:none;stroke-width:0;}#mermaid-svg-3qKphhAwXZQ1BRXk .icon-shape,#mermaid-svg-3qKphhAwXZQ1BRXk .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3qKphhAwXZQ1BRXk .icon-shape p,#mermaid-svg-3qKphhAwXZQ1BRXk .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-3qKphhAwXZQ1BRXk .icon-shape .label rect,#mermaid-svg-3qKphhAwXZQ1BRXk .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3qKphhAwXZQ1BRXk .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-3qKphhAwXZQ1BRXk .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-3qKphhAwXZQ1BRXk :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} AXI4-Lite / UDP 命令 / BIST
命令仲裁
DMA 描述符 FIFO
S2MM / MM2S
状态与错误计数器

这里有三种常见控制来源:

控制方式 适用场景 特点
AXI4-Lite 寄存器 有 CPU、JTAG AXI Master 或 PCIe 易调试,一次配置一次传输
内存描述符链表 高吞吐、多缓冲区 灵活,但硬件和软件复杂
数据包内命令 无 CPU 的纯逻辑网络设备 可由上位机直接控制,要做好合法性校验

对当前无 MicroBlaze 的 KU5P 工程,建议先用 BIST + 简单 UDP 命令;AXI4-Lite 接口保留,但不让它成为上板验证的前置条件。


4. 为什么不能只接一个 FIFO

很多初版设计会采用下面的思路:

text 复制代码
UDP RX → FIFO → MIG AXI

FIFO 确实能存数据,却不知道这些数据应该写到哪里。只要加入"地址",问题马上变复杂:

  • 地址是每拍携带,还是每包携带?
  • 一个 UDP 包对应一个连续缓冲区,还是允许分段?
  • 包丢弃时,地址指针是否回退?
  • 数据 FIFO 与地址 FIFO 怎样保证一一对应?

工程上通常采用"描述符 + 负载数据"的方式。描述符只保存一次包级元数据,负载继续走 AXIS。

4.1 推荐的描述符结构

下面给出一个 128 bit 的示例,不是协议强制格式,可以按工程需求修改:

位域 名称 说明
[63:0] addr DDR 起始字节地址
[87:64] length 有效负载字节数,最大约 16 MB
[103:88] tag 包序号或事务编号
[104] sof 逻辑帧起始
[105] eof 逻辑帧结束
[106] irq_en 完成后产生中断/事件
[107] crc_en 启用负载 CRC 校验
[108] drop_ok 缓冲区不足时允许丢弃
[127:109] reserved 保留

如果 DDR 中保存的是多个客户端的 UDP 数据,还可以另外保存包元数据:

text 复制代码
{src_ip, src_port, dst_port, payload_length, buffer_address, packet_tag}

不要为了携带这些字段,在 AXIS 的每一拍都重复一份大 TUSER。更节省资源的做法是:

  • 一个 Payload FIFO 保存数据;
  • 一个 Metadata/Descriptor FIFO 每包写入一次元数据;
  • 两者以"完整帧提交"机制保持同步。

4.2 帧级 FIFO 比普通 FIFO 更适合 UDP

假设一个 UDP 包写到一半时 FIFO 满了。若已经把前半包交给 DMA,DDR 中就会留下半个包。后续软件无法区分这是有效短包,还是被截断的数据。

因此网络入口建议采用帧级策略:

  • 有足够空间:整包接收并提交;
  • 空间不足:整包丢弃;
  • 丢包时增加 rx_drop_count;
  • 不允许把"半包"描述符送给 DMA。

这也是 TLAST 不能被当成普通旁带信号的原因。


5. 位宽转换:64 bit AXIS 怎样写入 256 bit AXI

假设 UDP 侧每拍 8 Byte,MIG AXI 每拍 32 Byte,那么需要把 4 个 AXIS Beat 合并为 1 个 AXI Beat。
#mermaid-svg-4ODCbwWxQcgbfH1M{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-4ODCbwWxQcgbfH1M .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-4ODCbwWxQcgbfH1M .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-4ODCbwWxQcgbfH1M .error-icon{fill:#552222;}#mermaid-svg-4ODCbwWxQcgbfH1M .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4ODCbwWxQcgbfH1M .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-4ODCbwWxQcgbfH1M .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4ODCbwWxQcgbfH1M .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4ODCbwWxQcgbfH1M .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-4ODCbwWxQcgbfH1M .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4ODCbwWxQcgbfH1M .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4ODCbwWxQcgbfH1M .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4ODCbwWxQcgbfH1M .marker.cross{stroke:#333333;}#mermaid-svg-4ODCbwWxQcgbfH1M svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4ODCbwWxQcgbfH1M p{margin:0;}#mermaid-svg-4ODCbwWxQcgbfH1M .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-4ODCbwWxQcgbfH1M .cluster-label text{fill:#333;}#mermaid-svg-4ODCbwWxQcgbfH1M .cluster-label span{color:#333;}#mermaid-svg-4ODCbwWxQcgbfH1M .cluster-label span p{background-color:transparent;}#mermaid-svg-4ODCbwWxQcgbfH1M .label text,#mermaid-svg-4ODCbwWxQcgbfH1M span{fill:#333;color:#333;}#mermaid-svg-4ODCbwWxQcgbfH1M .node rect,#mermaid-svg-4ODCbwWxQcgbfH1M .node circle,#mermaid-svg-4ODCbwWxQcgbfH1M .node ellipse,#mermaid-svg-4ODCbwWxQcgbfH1M .node polygon,#mermaid-svg-4ODCbwWxQcgbfH1M .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-4ODCbwWxQcgbfH1M .rough-node .label text,#mermaid-svg-4ODCbwWxQcgbfH1M .node .label text,#mermaid-svg-4ODCbwWxQcgbfH1M .image-shape .label,#mermaid-svg-4ODCbwWxQcgbfH1M .icon-shape .label{text-anchor:middle;}#mermaid-svg-4ODCbwWxQcgbfH1M .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-4ODCbwWxQcgbfH1M .rough-node .label,#mermaid-svg-4ODCbwWxQcgbfH1M .node .label,#mermaid-svg-4ODCbwWxQcgbfH1M .image-shape .label,#mermaid-svg-4ODCbwWxQcgbfH1M .icon-shape .label{text-align:center;}#mermaid-svg-4ODCbwWxQcgbfH1M .node.clickable{cursor:pointer;}#mermaid-svg-4ODCbwWxQcgbfH1M .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-4ODCbwWxQcgbfH1M .arrowheadPath{fill:#333333;}#mermaid-svg-4ODCbwWxQcgbfH1M .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-4ODCbwWxQcgbfH1M .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-4ODCbwWxQcgbfH1M .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4ODCbwWxQcgbfH1M .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-4ODCbwWxQcgbfH1M .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4ODCbwWxQcgbfH1M .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-4ODCbwWxQcgbfH1M .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-4ODCbwWxQcgbfH1M .cluster text{fill:#333;}#mermaid-svg-4ODCbwWxQcgbfH1M .cluster span{color:#333;}#mermaid-svg-4ODCbwWxQcgbfH1M div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-4ODCbwWxQcgbfH1M .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-4ODCbwWxQcgbfH1M rect.text{fill:none;stroke-width:0;}#mermaid-svg-4ODCbwWxQcgbfH1M .icon-shape,#mermaid-svg-4ODCbwWxQcgbfH1M .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4ODCbwWxQcgbfH1M .icon-shape p,#mermaid-svg-4ODCbwWxQcgbfH1M .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-4ODCbwWxQcgbfH1M .icon-shape .label rect,#mermaid-svg-4ODCbwWxQcgbfH1M .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4ODCbwWxQcgbfH1M .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-4ODCbwWxQcgbfH1M .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-4ODCbwWxQcgbfH1M :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 64 bit beat 0
256 bit packer
64 bit beat 1
64 bit beat 2
64 bit beat 3
256 bit WDATA + 32 bit WSTRB

5.1 不要只拼 TDATA

打包模块至少要同时处理:

  • TDATA:拼接数据;
  • TKEEP:生成 AXI WSTRB;
  • TLAST:决定最后一个 256 bit 字是否为部分有效;
  • TVALID/TREADY:处理上游停顿与下游反压。

例如一包只有 18 Byte,有效字节分布为:

text 复制代码
第 0 个 64 bit beat:8 Byte 有效
第 1 个 64 bit beat:8 Byte 有效
第 2 个 64 bit beat:2 Byte 有效,TLAST=1

打包后只产生一个 256 bit AXI 写数据拍:

text 复制代码
WSTRB = 32'h0003_FFFF
WLAST = 1

也就是低 18 个字节有效,其余字节不能覆盖 DDR 原有内容。

如果 MIG 启用了 ECC,带掩码的部分写可能在控制器内部转化为读改写,性能会低于完整字写。是否启用 ECC、是否允许部分写,需要结合可靠性和吞吐要求权衡。

5.2 第一版建议限制地址对齐

支持任意非对齐地址需要数据重排器(Data Realignment Engine,DRE)。例如从 0x...03 开始写 18 Byte,数据既要右移,还可能跨越两个 AXI 字。

为了先跑通工程,第一版可以规定:

  • 起始地址按 AXI 数据宽度对齐;
  • 中间 Beat 必须全字节有效;
  • 仅最后一个 Beat 允许部分 WSTRB;
  • 非对齐命令直接置 align_error,不启动 AXI 事务。

在 256 bit 总线上,对齐条件为:

verilog 复制代码
wire addr_aligned = (cmd_addr[4:0] == 5'b0);  // 32 Byte 对齐

等基本版本稳定后,再增加非对齐读写支持。


6. Burst 规划:DMA 的核心逻辑

6.1 AXI4 的两个硬限制

AXI4 INCR Burst 最多可以包含 256 次传输,并且一个 Burst 不能跨越 4 KB 地址边界 。AxLEN 的编码是"Beat 数减 1":

text 复制代码
1 Beat   → AxLEN = 0
16 Beats → AxLEN = 15
256 Beats → AxLEN = 255

假设数据宽度为 256 bit,即每拍 32 Byte。协议允许的最大 256 Beat 看起来可以传 8192 Byte:

text 复制代码
256 × 32 Byte = 8192 Byte

但单个 Burst 不能跨 4 KB,所以从 4 KB 对齐地址开始,最多只能发:

text 复制代码
4096 / 32 = 128 Beats

因此 256 bit 数据总线常把最大 Burst 配成 128 Beat,规划逻辑仍然必须根据当前地址重新计算边界剩余空间。

6.2 通用拆分公式

每次准备发送 Burst 时,计算:

text 复制代码
BYTES_PER_BEAT = AXI_DATA_WIDTH / 8

beats_by_length = ceil(remaining_bytes / BYTES_PER_BEAT)
bytes_to_4k     = 4096 - (current_addr & 12'hFFF)
beats_to_4k     = floor(bytes_to_4k / BYTES_PER_BEAT)

burst_beats = min(
    beats_by_length,
    MAX_BURST_BEATS,
    beats_to_4k,
    beats_available_or_free
)

最后一项对写和读含义不同:

  • S2MM:输入 FIFO 至少有足够数据,或者已经确认这是该帧末尾;
  • MM2S:输出 FIFO 至少有足够空位,能够接住整个返回 Burst。

6.3 一个跨 4 KB 的例子

从地址 0x0000_0FC0 开始写 320 Byte,256 bit 总线每拍 32 Byte:

text 复制代码
当前地址距 4 KB 边界:0x1000 - 0x0FC0 = 64 Byte
第一个 Burst:64 / 32 = 2 Beats
剩余长度:320 - 64 = 256 Byte
第二个 Burst:256 / 32 = 8 Beats

#mermaid-svg-CIMbBdsdYTDhXIaG{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-CIMbBdsdYTDhXIaG .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-CIMbBdsdYTDhXIaG .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-CIMbBdsdYTDhXIaG .error-icon{fill:#552222;}#mermaid-svg-CIMbBdsdYTDhXIaG .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-CIMbBdsdYTDhXIaG .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-CIMbBdsdYTDhXIaG .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-CIMbBdsdYTDhXIaG .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-CIMbBdsdYTDhXIaG .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-CIMbBdsdYTDhXIaG .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-CIMbBdsdYTDhXIaG .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-CIMbBdsdYTDhXIaG .marker{fill:#333333;stroke:#333333;}#mermaid-svg-CIMbBdsdYTDhXIaG .marker.cross{stroke:#333333;}#mermaid-svg-CIMbBdsdYTDhXIaG svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-CIMbBdsdYTDhXIaG p{margin:0;}#mermaid-svg-CIMbBdsdYTDhXIaG .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-CIMbBdsdYTDhXIaG .cluster-label text{fill:#333;}#mermaid-svg-CIMbBdsdYTDhXIaG .cluster-label span{color:#333;}#mermaid-svg-CIMbBdsdYTDhXIaG .cluster-label span p{background-color:transparent;}#mermaid-svg-CIMbBdsdYTDhXIaG .label text,#mermaid-svg-CIMbBdsdYTDhXIaG span{fill:#333;color:#333;}#mermaid-svg-CIMbBdsdYTDhXIaG .node rect,#mermaid-svg-CIMbBdsdYTDhXIaG .node circle,#mermaid-svg-CIMbBdsdYTDhXIaG .node ellipse,#mermaid-svg-CIMbBdsdYTDhXIaG .node polygon,#mermaid-svg-CIMbBdsdYTDhXIaG .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-CIMbBdsdYTDhXIaG .rough-node .label text,#mermaid-svg-CIMbBdsdYTDhXIaG .node .label text,#mermaid-svg-CIMbBdsdYTDhXIaG .image-shape .label,#mermaid-svg-CIMbBdsdYTDhXIaG .icon-shape .label{text-anchor:middle;}#mermaid-svg-CIMbBdsdYTDhXIaG .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-CIMbBdsdYTDhXIaG .rough-node .label,#mermaid-svg-CIMbBdsdYTDhXIaG .node .label,#mermaid-svg-CIMbBdsdYTDhXIaG .image-shape .label,#mermaid-svg-CIMbBdsdYTDhXIaG .icon-shape .label{text-align:center;}#mermaid-svg-CIMbBdsdYTDhXIaG .node.clickable{cursor:pointer;}#mermaid-svg-CIMbBdsdYTDhXIaG .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-CIMbBdsdYTDhXIaG .arrowheadPath{fill:#333333;}#mermaid-svg-CIMbBdsdYTDhXIaG .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-CIMbBdsdYTDhXIaG .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-CIMbBdsdYTDhXIaG .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CIMbBdsdYTDhXIaG .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-CIMbBdsdYTDhXIaG .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CIMbBdsdYTDhXIaG .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-CIMbBdsdYTDhXIaG .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-CIMbBdsdYTDhXIaG .cluster text{fill:#333;}#mermaid-svg-CIMbBdsdYTDhXIaG .cluster span{color:#333;}#mermaid-svg-CIMbBdsdYTDhXIaG div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-CIMbBdsdYTDhXIaG .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-CIMbBdsdYTDhXIaG rect.text{fill:none;stroke-width:0;}#mermaid-svg-CIMbBdsdYTDhXIaG .icon-shape,#mermaid-svg-CIMbBdsdYTDhXIaG .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CIMbBdsdYTDhXIaG .icon-shape p,#mermaid-svg-CIMbBdsdYTDhXIaG .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-CIMbBdsdYTDhXIaG .icon-shape .label rect,#mermaid-svg-CIMbBdsdYTDhXIaG .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CIMbBdsdYTDhXIaG .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-CIMbBdsdYTDhXIaG .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-CIMbBdsdYTDhXIaG :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 0x0FC0:2 Beats
0x1000 边界
0x1000:8 Beats

不能把它写成一个 10 Beat Burst,因为这个 Burst 会跨越 0x1000。

6.4 可综合的规划器写法

下面代码展示计算思路。实际工程还需要根据位宽处理最后一拍、地址宽度和 FIFO 水位。

verilog 复制代码
localparam integer AXI_BYTES       = AXI_DATA_WIDTH / 8;
localparam integer AXI_SIZE        = $clog2(AXI_BYTES);
localparam integer MAX_BURST_BEATS = 128;

wire [12:0] bytes_to_4k = 13'd4096 - {1'b0, cur_addr[11:0]};
wire [8:0]  beats_to_4k = bytes_to_4k >> AXI_SIZE;
wire [24:0] beats_by_len =
    (remain_bytes + AXI_BYTES - 1) >> AXI_SIZE;

always @(*) begin
    plan_beats = beats_by_len;

    if (plan_beats > MAX_BURST_BEATS)
        plan_beats = MAX_BURST_BEATS;

    if (plan_beats > beats_to_4k)
        plan_beats = beats_to_4k;

    if (plan_beats > fifo_limit_beats)
        plan_beats = fifo_limit_beats;
end

assign m_axi_awlen  = plan_beats - 1'b1;
assign m_axi_awsize = AXI_SIZE[2:0];
assign m_axi_awburst = 2'b01; // INCR

注意两个细节:

  1. plan_beats 不能为 0,必须先满足启动条件再发 AWVALID/ARVALID;
  2. 如果允许非对齐地址,beats_to_4k 和总 Beat 数还要考虑首拍偏移,不能直接套用这个简化版本。

7. S2MM:把 UDP 数据写入 DDR

7.1 写状态机

推荐把写地址、写数据和写响应分开记录握手状态,而不是假设它们同步完成。
#mermaid-svg-gBLneM14sme0rTFH{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-gBLneM14sme0rTFH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-gBLneM14sme0rTFH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-gBLneM14sme0rTFH .error-icon{fill:#552222;}#mermaid-svg-gBLneM14sme0rTFH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-gBLneM14sme0rTFH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-gBLneM14sme0rTFH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-gBLneM14sme0rTFH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-gBLneM14sme0rTFH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-gBLneM14sme0rTFH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-gBLneM14sme0rTFH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-gBLneM14sme0rTFH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-gBLneM14sme0rTFH .marker.cross{stroke:#333333;}#mermaid-svg-gBLneM14sme0rTFH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-gBLneM14sme0rTFH p{margin:0;}#mermaid-svg-gBLneM14sme0rTFH defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-gBLneM14sme0rTFH g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-gBLneM14sme0rTFH g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-gBLneM14sme0rTFH g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-gBLneM14sme0rTFH g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-gBLneM14sme0rTFH g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-gBLneM14sme0rTFH .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-gBLneM14sme0rTFH .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-gBLneM14sme0rTFH .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-gBLneM14sme0rTFH .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-gBLneM14sme0rTFH .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-gBLneM14sme0rTFH .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-gBLneM14sme0rTFH .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-gBLneM14sme0rTFH .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gBLneM14sme0rTFH .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-gBLneM14sme0rTFH .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gBLneM14sme0rTFH .edgeLabel .label text{fill:#333;}#mermaid-svg-gBLneM14sme0rTFH .label div .edgeLabel{color:#333;}#mermaid-svg-gBLneM14sme0rTFH .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-gBLneM14sme0rTFH .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-gBLneM14sme0rTFH .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-gBLneM14sme0rTFH .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-gBLneM14sme0rTFH .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-gBLneM14sme0rTFH .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-gBLneM14sme0rTFH .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-gBLneM14sme0rTFH #statediagram-barbEnd{fill:#333333;}#mermaid-svg-gBLneM14sme0rTFH .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-gBLneM14sme0rTFH .cluster-label,#mermaid-svg-gBLneM14sme0rTFH .nodeLabel{color:#131300;}#mermaid-svg-gBLneM14sme0rTFH .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-gBLneM14sme0rTFH .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-gBLneM14sme0rTFH .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-gBLneM14sme0rTFH .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-gBLneM14sme0rTFH .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-gBLneM14sme0rTFH .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-gBLneM14sme0rTFH .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-gBLneM14sme0rTFH .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-gBLneM14sme0rTFH .note-edge{stroke-dasharray:5;}#mermaid-svg-gBLneM14sme0rTFH .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-gBLneM14sme0rTFH .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-gBLneM14sme0rTFH .statediagram-note text{fill:black;}#mermaid-svg-gBLneM14sme0rTFH .statediagram-note .nodeLabel{color:black;}#mermaid-svg-gBLneM14sme0rTFH .statediagram .edgeLabel{color:red;}#mermaid-svg-gBLneM14sme0rTFH #dependencyStart,#mermaid-svg-gBLneM14sme0rTFH #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-gBLneM14sme0rTFH .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-gBLneM14sme0rTFH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 取到描述符
Burst 参数有效
地址已握手
最后一拍已握手
仍有剩余数据
描述符完成
参数非法
BRESP 非 OKAY
软件/控制逻辑清除
IDLE
PLAN
AW
W
B
DONE
ERROR

为了提升性能,也可以让 AW 和 W 部分重叠,但第一版应优先保证协议正确。

7.2 VALID 的保持规则

当 Master 拉高 AWVALID 后,如果 Slave 没有拉高 AWREADY,地址和控制信号必须保持稳定:

verilog 复制代码
always @(posedge axi_clk) begin
    if (!axi_aresetn) begin
        m_axi_awvalid <= 1'b0;
    end else begin
        if (start_aw) begin
            m_axi_awaddr  <= planned_addr;
            m_axi_awlen   <= planned_beats - 1'b1;
            m_axi_awvalid <= 1'b1;
        end else if (m_axi_awvalid && m_axi_awready) begin
            m_axi_awvalid <= 1'b0;
        end
    end
end

WVALID/WDATA/WSTRB/WLAST 也遵守相同原则:在 WVALID=1 && WREADY=0 时保持不变。

7.3 WLAST 怎样计算

假设当前 Burst 有 burst_beats 拍,可以用 Burst 内部计数器:

verilog 复制代码
wire w_fire = m_axi_wvalid && m_axi_wready;
assign m_axi_wlast = m_axi_wvalid &&
                     (w_beat_count == burst_beats - 1'b1);

always @(posedge axi_clk) begin
    if (!axi_aresetn || start_burst)
        w_beat_count <= 0;
    else if (w_fire)
        w_beat_count <= w_beat_count + 1'b1;
end

计数器必须在真正握手 时递增,而不是在 WVALID 拉高时递增。否则一旦 WREADY 拉低,WLAST 就会提前。

7.4 必须检查 BRESP

完成最后一拍数据并不代表写事务已经成功。Master 仍要等待 B 通道:

BRESP 含义 建议处理
OKAY 正常完成 更新已写字节数
EXOKAY 独占访问成功 普通 DMA 一般不用
SLVERR Slave 错误 记录地址、停止当前描述符
DECERR 地址译码错误 检查地址映射与 SmartConnect

发生错误时至少保存:

text 复制代码
last_error_code
last_error_addr
last_error_tag
write_error_count

只看 init_calib_complete 和 ILA 中的数据波形,无法替代对 BRESP 的检查。


8. MM2S:从 DDR 读出并送回 UDP

读方向看似更简单,实际最容易出现"DDR 已经返回数据,但下游没有空间"的问题。

8.1 读状态机

#mermaid-svg-Y34gkShglp0pfPwh{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Y34gkShglp0pfPwh .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Y34gkShglp0pfPwh .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Y34gkShglp0pfPwh .error-icon{fill:#552222;}#mermaid-svg-Y34gkShglp0pfPwh .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Y34gkShglp0pfPwh .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Y34gkShglp0pfPwh .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Y34gkShglp0pfPwh .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Y34gkShglp0pfPwh .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Y34gkShglp0pfPwh .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Y34gkShglp0pfPwh .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Y34gkShglp0pfPwh .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Y34gkShglp0pfPwh .marker.cross{stroke:#333333;}#mermaid-svg-Y34gkShglp0pfPwh svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Y34gkShglp0pfPwh p{margin:0;}#mermaid-svg-Y34gkShglp0pfPwh defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-Y34gkShglp0pfPwh g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-Y34gkShglp0pfPwh g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-Y34gkShglp0pfPwh g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-Y34gkShglp0pfPwh g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-Y34gkShglp0pfPwh g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-Y34gkShglp0pfPwh .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-Y34gkShglp0pfPwh .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-Y34gkShglp0pfPwh .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-Y34gkShglp0pfPwh .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-Y34gkShglp0pfPwh .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-Y34gkShglp0pfPwh .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-Y34gkShglp0pfPwh .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-Y34gkShglp0pfPwh .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Y34gkShglp0pfPwh .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Y34gkShglp0pfPwh .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Y34gkShglp0pfPwh .edgeLabel .label text{fill:#333;}#mermaid-svg-Y34gkShglp0pfPwh .label div .edgeLabel{color:#333;}#mermaid-svg-Y34gkShglp0pfPwh .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-Y34gkShglp0pfPwh .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-Y34gkShglp0pfPwh .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-Y34gkShglp0pfPwh .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-Y34gkShglp0pfPwh .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-Y34gkShglp0pfPwh .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Y34gkShglp0pfPwh .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Y34gkShglp0pfPwh #statediagram-barbEnd{fill:#333333;}#mermaid-svg-Y34gkShglp0pfPwh .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Y34gkShglp0pfPwh .cluster-label,#mermaid-svg-Y34gkShglp0pfPwh .nodeLabel{color:#131300;}#mermaid-svg-Y34gkShglp0pfPwh .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-Y34gkShglp0pfPwh .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-Y34gkShglp0pfPwh .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-Y34gkShglp0pfPwh .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-Y34gkShglp0pfPwh .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-Y34gkShglp0pfPwh .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-Y34gkShglp0pfPwh .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-Y34gkShglp0pfPwh .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-Y34gkShglp0pfPwh .note-edge{stroke-dasharray:5;}#mermaid-svg-Y34gkShglp0pfPwh .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-Y34gkShglp0pfPwh .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-Y34gkShglp0pfPwh .statediagram-note text{fill:black;}#mermaid-svg-Y34gkShglp0pfPwh .statediagram-note .nodeLabel{color:black;}#mermaid-svg-Y34gkShglp0pfPwh .statediagram .edgeLabel{color:red;}#mermaid-svg-Y34gkShglp0pfPwh #dependencyStart,#mermaid-svg-Y34gkShglp0pfPwh #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-Y34gkShglp0pfPwh .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Y34gkShglp0pfPwh :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 取到读描述符
输出 FIFO 空间足够
地址已握手
一个 Burst 完成且仍有剩余
整个描述符完成
RRESP 错误或 RLAST 异常
清除错误
IDLE
PLAN
AR
R
DONE
ERROR

8.2 为什么发 AR 前要预留 FIFO 空间

AXI4 允许在读地址被接受后连续返回多个 Beat。虽然 Master 可以拉低 RREADY,但长期在 MIG 返回路径上制造停顿会降低效率,也会使系统行为更难分析。

推荐策略是:

text 复制代码
只有当输出 FIFO 的可用空间 ≥ 本次 Burst Beat 数时,才发 ARVALID。

如果 AXI 读宽 256 bit,而后级 FIFO 以 64 bit 计数,要先换算单位:

text 复制代码
1 个 256 bit AXI Beat = 4 个 64 bit AXIS Beat

不要直接比较两个不同时钟域、不同位宽单位的水位值。

8.3 检查 RLAST 和 RRESP

读通道需要同时验证:

  • RRESP 每拍是否为 OKAY;
  • RLAST 是否恰好出现在计划 Burst 的最后一拍;
  • 返回 Beat 数是否与 ARLEN+1 一致。

如果提前收到 RLAST 或最后一拍没有 RLAST,都应该记录协议错误并停止向 UDP 发送,避免把错误数据伪装成正常包。

8.4 重新生成 TKEEP 和 TLAST

DDR 不保存 AXIS 的 TLAST 语义。因此 MM2S 必须根据描述符中的 length 重新生成:

text 复制代码
TLAST:当前输出拍包含描述符的最后一个字节
TKEEP:最后一拍中哪些字节有效

如果 64 bit AXIS 要发送 18 Byte,则输出为 3 拍:

Beat TKEEP TLAST
0 8'hFF 0
1 8'hFF 0
2 8'h03 1

9. 环形缓冲区:连续 UDP 流怎样管理地址

只用固定地址反复读写,只适合最小自检。连续采集或网络缓存通常要使用环形缓冲区。

9.1 基本参数

text 复制代码
ring_base   :缓冲区首地址
ring_size   :缓冲区大小,建议为 2 的幂
write_ptr   :下一次写入位置
read_ptr    :下一次读出位置

当 ring_size 是 2 的幂时,可以用掩码回卷:

verilog 复制代码
next_ptr = ring_base + ((cur_ptr - ring_base + advance_bytes)
                       & (ring_size - 1'b1));

9.2 满和空不能只看指针相等

write_ptr == read_ptr 既可能表示空,也可能表示已经绕一圈后的满。常见解决办法有:

  • 额外保存一个 wrap bit;
  • 始终保留一个空槽;
  • 独立维护 used_bytes;
  • 数据区与描述符队列分别管理。

对 UDP 包缓存,我更推荐:

  1. 数据写入环形数据区;
  2. 包完整写入后,向描述符 FIFO 提交 {addr, length, tag};
  3. 读端处理完一个描述符后再释放对应空间。

#mermaid-svg-tAjvSlM66WT5gfAe{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-tAjvSlM66WT5gfAe .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tAjvSlM66WT5gfAe .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tAjvSlM66WT5gfAe .error-icon{fill:#552222;}#mermaid-svg-tAjvSlM66WT5gfAe .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tAjvSlM66WT5gfAe .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tAjvSlM66WT5gfAe .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tAjvSlM66WT5gfAe .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tAjvSlM66WT5gfAe .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tAjvSlM66WT5gfAe .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tAjvSlM66WT5gfAe .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tAjvSlM66WT5gfAe .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tAjvSlM66WT5gfAe .marker.cross{stroke:#333333;}#mermaid-svg-tAjvSlM66WT5gfAe svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tAjvSlM66WT5gfAe p{margin:0;}#mermaid-svg-tAjvSlM66WT5gfAe .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-tAjvSlM66WT5gfAe .cluster-label text{fill:#333;}#mermaid-svg-tAjvSlM66WT5gfAe .cluster-label span{color:#333;}#mermaid-svg-tAjvSlM66WT5gfAe .cluster-label span p{background-color:transparent;}#mermaid-svg-tAjvSlM66WT5gfAe .label text,#mermaid-svg-tAjvSlM66WT5gfAe span{fill:#333;color:#333;}#mermaid-svg-tAjvSlM66WT5gfAe .node rect,#mermaid-svg-tAjvSlM66WT5gfAe .node circle,#mermaid-svg-tAjvSlM66WT5gfAe .node ellipse,#mermaid-svg-tAjvSlM66WT5gfAe .node polygon,#mermaid-svg-tAjvSlM66WT5gfAe .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-tAjvSlM66WT5gfAe .rough-node .label text,#mermaid-svg-tAjvSlM66WT5gfAe .node .label text,#mermaid-svg-tAjvSlM66WT5gfAe .image-shape .label,#mermaid-svg-tAjvSlM66WT5gfAe .icon-shape .label{text-anchor:middle;}#mermaid-svg-tAjvSlM66WT5gfAe .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-tAjvSlM66WT5gfAe .rough-node .label,#mermaid-svg-tAjvSlM66WT5gfAe .node .label,#mermaid-svg-tAjvSlM66WT5gfAe .image-shape .label,#mermaid-svg-tAjvSlM66WT5gfAe .icon-shape .label{text-align:center;}#mermaid-svg-tAjvSlM66WT5gfAe .node.clickable{cursor:pointer;}#mermaid-svg-tAjvSlM66WT5gfAe .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-tAjvSlM66WT5gfAe .arrowheadPath{fill:#333333;}#mermaid-svg-tAjvSlM66WT5gfAe .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-tAjvSlM66WT5gfAe .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-tAjvSlM66WT5gfAe .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tAjvSlM66WT5gfAe .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-tAjvSlM66WT5gfAe .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tAjvSlM66WT5gfAe .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-tAjvSlM66WT5gfAe .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-tAjvSlM66WT5gfAe .cluster text{fill:#333;}#mermaid-svg-tAjvSlM66WT5gfAe .cluster span{color:#333;}#mermaid-svg-tAjvSlM66WT5gfAe div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-tAjvSlM66WT5gfAe .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-tAjvSlM66WT5gfAe rect.text{fill:none;stroke-width:0;}#mermaid-svg-tAjvSlM66WT5gfAe .icon-shape,#mermaid-svg-tAjvSlM66WT5gfAe .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tAjvSlM66WT5gfAe .icon-shape p,#mermaid-svg-tAjvSlM66WT5gfAe .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-tAjvSlM66WT5gfAe .icon-shape .label rect,#mermaid-svg-tAjvSlM66WT5gfAe .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tAjvSlM66WT5gfAe .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-tAjvSlM66WT5gfAe .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-tAjvSlM66WT5gfAe :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
预留连续空间
写入整包数据
BRESP 全部正常?
提交描述符并推进 write_ptr
丢弃本包并记录错误
MM2S 读取并发送
释放空间并推进 read_ptr

这相当于一个简化的"两阶段提交":数据成功落入 DDR 后,才让消费者看见描述符。

9.3 包在环尾放不下怎么办

假设环尾只剩 200 Byte,但下一个 UDP 负载为 1000 Byte,可以选择:

  • 把该包拆成环尾和环首两段,生成两个 DMA 描述符;
  • 在环尾填充,整包从环首开始;
  • 让 DMA 支持一个逻辑包对应两个物理片段。

第一版建议采用第二种方式。它会浪费少量空间,但软件和硬件最清楚,也方便波形定位。


10. AXI4-Lite 寄存器怎样设计

即使当前工程没有 MicroBlaze,也建议给 DMA 做一组稳定的控制/状态寄存器。它以后可以接:

  • JTAG AXI Master;
  • MicroBlaze;
  • PCIe BAR;
  • 片外 MCU;
  • UDP 寄存器访问模块。

10.1 示例寄存器表

偏移 名称 属性 说明
0x00 VERSION RO IP 版本与特性位
0x04 CONTROL RW enable、soft_reset、irq_enable
0x08 STATUS RO/W1C busy、done、error、calib_ok
0x0C COMMAND WO start_s2mm、start_mm2s、start_bist
0x10 ADDR_L RW 起始地址低 32 bit
0x14 ADDR_H RW 起始地址高 32 bit
0x18 LENGTH RW 字节长度
0x1C TAG RW 命令标识
0x20 RING_BASE_L RW 环形缓冲区基地址低位
0x24 RING_BASE_H RW 环形缓冲区基地址高位
0x28 RING_SIZE RW 环形缓冲区字节数
0x2C WRITE_PTR RO 当前写指针
0x30 READ_PTR RO 当前读指针
0x34 BYTES_WRITTEN RO 累计写入字节数
0x38 BYTES_READ RO 累计读出字节数
0x3C DROP_COUNT RO 丢包数
0x40 AXI_ERROR_COUNT RO AXI 响应错误数
0x44 LAST_ERROR_ADDR_L RO 最近错误地址低位
0x48 LAST_ERROR_ADDR_H RO 最近错误地址高位
0x4C FIFO_LEVEL RO 输入/输出 FIFO 水位摘要
0x50 PERF_CYCLES RO 性能统计周期数

10.2 跨时钟域的寄存器不能直接多位同步

如果 AXI4-Lite 工作在 100 MHz,而 DMA 工作在 MIG UI 时钟域:

  • 单 bit 电平状态可用双触发器同步;
  • 单周期脉冲应使用 toggle/握手同步;
  • 多 bit 命令应使用异步 FIFO 或"数据保持 + request/acknowledge"握手;
  • 64 bit 计数器若被异步读取,应使用快照机制,避免高低位来自不同时间。

仅把 Block Design 中接口的 FREQ_HZ 属性改成相同数值,不会产生跨时钟电路。


11. 没有 MicroBlaze,怎样做 DDR BIST

BIST(Built-In Self-Test)是纯 RTL 工程中最重要的分层验证工具。它绕开 UDP 与上位机,直接验证:

text 复制代码
DMA → AXI4 → MIG → DDR4 → MIG → AXI4 → DMA

11.1 BIST 状态机

#mermaid-svg-5IsgsXAheO7r01CL{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5IsgsXAheO7r01CL .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5IsgsXAheO7r01CL .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5IsgsXAheO7r01CL .error-icon{fill:#552222;}#mermaid-svg-5IsgsXAheO7r01CL .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5IsgsXAheO7r01CL .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5IsgsXAheO7r01CL .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5IsgsXAheO7r01CL .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5IsgsXAheO7r01CL .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5IsgsXAheO7r01CL .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5IsgsXAheO7r01CL .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5IsgsXAheO7r01CL .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5IsgsXAheO7r01CL .marker.cross{stroke:#333333;}#mermaid-svg-5IsgsXAheO7r01CL svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5IsgsXAheO7r01CL p{margin:0;}#mermaid-svg-5IsgsXAheO7r01CL defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-5IsgsXAheO7r01CL g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-5IsgsXAheO7r01CL g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-5IsgsXAheO7r01CL g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-5IsgsXAheO7r01CL g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-5IsgsXAheO7r01CL g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-5IsgsXAheO7r01CL .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-5IsgsXAheO7r01CL .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-5IsgsXAheO7r01CL .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-5IsgsXAheO7r01CL .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-5IsgsXAheO7r01CL .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-5IsgsXAheO7r01CL .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-5IsgsXAheO7r01CL .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-5IsgsXAheO7r01CL .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5IsgsXAheO7r01CL .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5IsgsXAheO7r01CL .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5IsgsXAheO7r01CL .edgeLabel .label text{fill:#333;}#mermaid-svg-5IsgsXAheO7r01CL .label div .edgeLabel{color:#333;}#mermaid-svg-5IsgsXAheO7r01CL .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-5IsgsXAheO7r01CL .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-5IsgsXAheO7r01CL .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-5IsgsXAheO7r01CL .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-5IsgsXAheO7r01CL .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-5IsgsXAheO7r01CL .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5IsgsXAheO7r01CL .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5IsgsXAheO7r01CL #statediagram-barbEnd{fill:#333333;}#mermaid-svg-5IsgsXAheO7r01CL .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5IsgsXAheO7r01CL .cluster-label,#mermaid-svg-5IsgsXAheO7r01CL .nodeLabel{color:#131300;}#mermaid-svg-5IsgsXAheO7r01CL .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-5IsgsXAheO7r01CL .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-5IsgsXAheO7r01CL .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-5IsgsXAheO7r01CL .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-5IsgsXAheO7r01CL .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-5IsgsXAheO7r01CL .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-5IsgsXAheO7r01CL .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-5IsgsXAheO7r01CL .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-5IsgsXAheO7r01CL .note-edge{stroke-dasharray:5;}#mermaid-svg-5IsgsXAheO7r01CL .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-5IsgsXAheO7r01CL .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-5IsgsXAheO7r01CL .statediagram-note text{fill:black;}#mermaid-svg-5IsgsXAheO7r01CL .statediagram-note .nodeLabel{color:black;}#mermaid-svg-5IsgsXAheO7r01CL .statediagram .edgeLabel{color:red;}#mermaid-svg-5IsgsXAheO7r01CL #dependencyStart,#mermaid-svg-5IsgsXAheO7r01CL #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-5IsgsXAheO7r01CL .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5IsgsXAheO7r01CL :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} init_calib_complete
全部写命令发出
全部 BRESP 正常
读数据返回
全部一致
首个不一致
WAIT_CALIB
WRITE
WAIT_WRITE
READ
COMPARE
PASS
FAIL

启动 BIST 前至少满足:

text 复制代码
global_reset_released = 1
init_calib_complete   = 1
MIG UI reset released = 1
相关 FIFO reset_busy  = 0
AXI Clock Converter 已退出复位

11.2 推荐测试图案

图案 作用
固定 0x0000.../0xFFFF... 检查固定值与整体连通
0xAA55.../0x55AA... 检查相邻位翻转
Walking 1 / Walking 0 检查数据位短路或粘连
地址即数据 检查地址译码、数据顺序
LFSR 伪随机 长时间压力测试

建议比较失败时锁存:

text 复制代码
first_fail_addr
expected_data
actual_data
fail_byte_mask
fail_count

只保留一个 pass/fail LED,定位价值很低。

11.3 测试范围要分层扩大

第一次上板不要直接扫完整 DDR:

  1. 先测试一个对齐的 256 Byte 区域;
  2. 再测试 4 KB 边界附近;
  3. 再测试不同 Burst 长度;
  4. 再测试环形缓冲区回卷;
  5. 最后做大范围 LFSR 循环。

这样一旦失败,可以快速判断是基本握手、边界拆分、地址推进还是长时间稳定性问题。


12. Vivado Block Design 的连接方法

12.1 推荐连接

#mermaid-svg-1eDlojojXdbrMF9m{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-1eDlojojXdbrMF9m .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-1eDlojojXdbrMF9m .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-1eDlojojXdbrMF9m .error-icon{fill:#552222;}#mermaid-svg-1eDlojojXdbrMF9m .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-1eDlojojXdbrMF9m .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-1eDlojojXdbrMF9m .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-1eDlojojXdbrMF9m .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-1eDlojojXdbrMF9m .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-1eDlojojXdbrMF9m .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-1eDlojojXdbrMF9m .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-1eDlojojXdbrMF9m .marker{fill:#333333;stroke:#333333;}#mermaid-svg-1eDlojojXdbrMF9m .marker.cross{stroke:#333333;}#mermaid-svg-1eDlojojXdbrMF9m svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-1eDlojojXdbrMF9m p{margin:0;}#mermaid-svg-1eDlojojXdbrMF9m .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-1eDlojojXdbrMF9m .cluster-label text{fill:#333;}#mermaid-svg-1eDlojojXdbrMF9m .cluster-label span{color:#333;}#mermaid-svg-1eDlojojXdbrMF9m .cluster-label span p{background-color:transparent;}#mermaid-svg-1eDlojojXdbrMF9m .label text,#mermaid-svg-1eDlojojXdbrMF9m span{fill:#333;color:#333;}#mermaid-svg-1eDlojojXdbrMF9m .node rect,#mermaid-svg-1eDlojojXdbrMF9m .node circle,#mermaid-svg-1eDlojojXdbrMF9m .node ellipse,#mermaid-svg-1eDlojojXdbrMF9m .node polygon,#mermaid-svg-1eDlojojXdbrMF9m .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-1eDlojojXdbrMF9m .rough-node .label text,#mermaid-svg-1eDlojojXdbrMF9m .node .label text,#mermaid-svg-1eDlojojXdbrMF9m .image-shape .label,#mermaid-svg-1eDlojojXdbrMF9m .icon-shape .label{text-anchor:middle;}#mermaid-svg-1eDlojojXdbrMF9m .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-1eDlojojXdbrMF9m .rough-node .label,#mermaid-svg-1eDlojojXdbrMF9m .node .label,#mermaid-svg-1eDlojojXdbrMF9m .image-shape .label,#mermaid-svg-1eDlojojXdbrMF9m .icon-shape .label{text-align:center;}#mermaid-svg-1eDlojojXdbrMF9m .node.clickable{cursor:pointer;}#mermaid-svg-1eDlojojXdbrMF9m .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-1eDlojojXdbrMF9m .arrowheadPath{fill:#333333;}#mermaid-svg-1eDlojojXdbrMF9m .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-1eDlojojXdbrMF9m .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-1eDlojojXdbrMF9m .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1eDlojojXdbrMF9m .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-1eDlojojXdbrMF9m .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1eDlojojXdbrMF9m .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-1eDlojojXdbrMF9m .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-1eDlojojXdbrMF9m .cluster text{fill:#333;}#mermaid-svg-1eDlojojXdbrMF9m .cluster span{color:#333;}#mermaid-svg-1eDlojojXdbrMF9m div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-1eDlojojXdbrMF9m .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-1eDlojojXdbrMF9m rect.text{fill:none;stroke-width:0;}#mermaid-svg-1eDlojojXdbrMF9m .icon-shape,#mermaid-svg-1eDlojojXdbrMF9m .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1eDlojojXdbrMF9m .icon-shape p,#mermaid-svg-1eDlojojXdbrMF9m .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-1eDlojojXdbrMF9m .icon-shape .label rect,#mermaid-svg-1eDlojojXdbrMF9m .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1eDlojojXdbrMF9m .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-1eDlojojXdbrMF9m .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-1eDlojojXdbrMF9m :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 差分 DDR 系统时钟
MIG
ui_clk / ui_clk_sync_rst
自定义 DMA AXI Master
AXI SmartConnect
125 MHz UDP 域
AXIS 异步 FIFO

几个经验结论:

  • 板上的 DDR 参考/系统时钟应按 MIG 配置直接进入合适的差分时钟管脚;
  • 不要为了"看起来统一",随意在 MIG 输入前再级联用户 MMCM;
  • DMA 若直接运行在 ui_clk,AXI4 到 MIG 之间可以减少一次 CDC;
  • UDP 的 125 MHz 数据通过 AXIS 异步 FIFO 跨入 ui_clk 域;
  • 如果 DMA 与 MIG 确实不同频,使用 AXI Clock Converter 或具备异步时钟支持的互连配置;
  • 地址范围在 Address Editor 中必须覆盖实际 DDR 地址空间。

12.2 SmartConnect 的作用

SmartConnect 可以完成多主机/多从机互连、位宽适配、部分流水与时钟域适配,但不要把所有责任都推给互连:

  • 4 KB 边界是否合法,DMA 仍应主动保证;
  • 包长度和 TLAST/TKEEP,互连并不知道;
  • 描述符与数据的一致性,必须由自定义逻辑保证;
  • 属性中的时钟频率声明不是 CDC 电路。

12.3 AXI DMA IP、DataMover 与自定义 DMA 怎样选

方案 优点 缺点 适用场景
AXI DMA 功能完整,支持 Simple/SG 寄存器与软件模型较重 有处理器、标准 AXIS 外设
AXI DataMover 专注命令到数据搬运,性能高 命令/状态接口需要上层管理 自定义硬件控制器
自定义 DMA 数据格式、缓存策略完全可控 验证工作量最大 固定功能、低资源、特殊包语义

AMD 文档说明,常规 AXI DMA 具有 4 KB 边界保护和自动 Burst 映射;但 Micro DMA 模式不提供同样的 4 KB 边界处理。DataMover 的完整配置支持边界保护和自动拆分,而 Basic 配置明确减少了相关能力。因此无论使用 IP 还是自研逻辑,都要根据具体配置核对产品手册,不能只看 IP 名称。


13. 复位顺序:能校准不等于能立刻搬运

建议把"可以启动 DMA"的条件集中生成:

verilog 复制代码
assign dma_run_enable = ext_reset_released &&
                        init_calib_complete &&
                        !ui_clk_sync_rst &&
                        !axis_fifo_wr_rst_busy &&
                        !axis_fifo_rd_rst_busy &&
                        !fatal_error;

推荐顺序为:
DMA FIFO/CDC MIG 外部复位 DMA FIFO/CDC MIG 外部复位 #mermaid-svg-TTUSIDKPQXsbQzhq{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-TTUSIDKPQXsbQzhq .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TTUSIDKPQXsbQzhq .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TTUSIDKPQXsbQzhq .error-icon{fill:#552222;}#mermaid-svg-TTUSIDKPQXsbQzhq .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TTUSIDKPQXsbQzhq .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TTUSIDKPQXsbQzhq .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TTUSIDKPQXsbQzhq .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TTUSIDKPQXsbQzhq .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TTUSIDKPQXsbQzhq .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TTUSIDKPQXsbQzhq .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TTUSIDKPQXsbQzhq .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TTUSIDKPQXsbQzhq .marker.cross{stroke:#333333;}#mermaid-svg-TTUSIDKPQXsbQzhq svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TTUSIDKPQXsbQzhq p{margin:0;}#mermaid-svg-TTUSIDKPQXsbQzhq .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-TTUSIDKPQXsbQzhq text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-TTUSIDKPQXsbQzhq .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-TTUSIDKPQXsbQzhq .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-TTUSIDKPQXsbQzhq .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-TTUSIDKPQXsbQzhq .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-TTUSIDKPQXsbQzhq #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-TTUSIDKPQXsbQzhq .sequenceNumber{fill:white;}#mermaid-svg-TTUSIDKPQXsbQzhq #sequencenumber{fill:#333;}#mermaid-svg-TTUSIDKPQXsbQzhq #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-TTUSIDKPQXsbQzhq .messageText{fill:#333;stroke:none;}#mermaid-svg-TTUSIDKPQXsbQzhq .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-TTUSIDKPQXsbQzhq .labelText,#mermaid-svg-TTUSIDKPQXsbQzhq .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-TTUSIDKPQXsbQzhq .loopText,#mermaid-svg-TTUSIDKPQXsbQzhq .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-TTUSIDKPQXsbQzhq .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-TTUSIDKPQXsbQzhq .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-TTUSIDKPQXsbQzhq .noteText,#mermaid-svg-TTUSIDKPQXsbQzhq .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-TTUSIDKPQXsbQzhq .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-TTUSIDKPQXsbQzhq .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-TTUSIDKPQXsbQzhq .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-TTUSIDKPQXsbQzhq .actorPopupMenu{position:absolute;}#mermaid-svg-TTUSIDKPQXsbQzhq .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-TTUSIDKPQXsbQzhq .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-TTUSIDKPQXsbQzhq .actor-man circle,#mermaid-svg-TTUSIDKPQXsbQzhq line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-TTUSIDKPQXsbQzhq :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 解除系统复位 DDR 初始化与训练 init_calib_complete 同步释放各时钟域复位 reset_busy 清零 清状态并等待命令

运行中的软复位要格外谨慎。如果还有未完成的 AXI 事务,突然清空计数器或撤销 BREADY/RREADY,可能让互连永久等待。更稳妥的流程是:

  1. 停止接受新描述符;
  2. 等待当前事务结束;
  3. 清 FIFO 与状态;
  4. 重新开放命令入口。

14. 仿真:不要只测 READY 永远为 1

14.1 最低限度的测试项

分类 测试内容
长度 1、7、8、9、31、32、33、255、256、4095、4096、4097 Byte
地址 对齐地址、0x...FC0、0x...FE0、环尾附近
反压 随机拉低 TREADY/WREADY/ARREADY/RVALID
通道独立 AWREADY 与 WREADY 独立随机
响应 注入 SLVERR/DECERR
帧 单包、连续包、最后一拍部分有效
复位 空闲复位、事务间复位、受控排空后软复位
环形缓冲 正常推进、回卷、满、空、丢包

14.2 Scoreboard

仿真平台维护一个参考内存:

  1. S2MM 输入每握手一个有效字节,就写入参考内存;
  2. 监控 AXI 写事务,确认地址、数据与 WSTRB 正确;
  3. MM2S 返回 AXIS 时,逐字节与参考内存比较;
  4. 检查 TLAST/TKEEP 是否与描述符长度一致。

这样能够把错误定位到具体字节,而不是只看到最终 CRC 不一致。

14.3 值得加入的断言

下面是断言目标,而不是特定验证语言的唯一写法:

text 复制代码
当 VALID=1 且 READY=0 时,payload 必须稳定
AW/AR Burst 不得跨越 4 KB
WLAST 只能出现在第 AWLEN+1 拍
RLAST 必须出现在第 ARLEN+1 拍
一个描述符完成的字节数必须等于 length
未收到正常 BRESP 前不得提交写描述符
输出帧最后一拍必须 TLAST=1,且 TKEEP 不得为 0

4 KB 边界可用下面的关系检查:

text 复制代码
start_addr[ADDR_W-1:12] == end_addr[ADDR_W-1:12]

其中:

text 复制代码
end_addr = start_addr + burst_beats × bytes_per_beat - 1

15. 上板调试步骤

不要一次把 UDP、DMA 和 DDR 全部打开。推荐按下面顺序逐层验证。

第 1 步:只看 MIG

  • init_calib_complete 是否稳定为 1;
  • ui_clk_sync_rst 是否释放;
  • Vivado Hardware Manager 中是否报告校准阶段失败;
  • DDR 时钟、复位、器件型号、位宽是否与原理图一致。

第 2 步:运行小范围 BIST

  • 固定地址写 256 Byte;
  • 读回并逐字比较;
  • 检查 BRESP/RRESP;
  • ILA 抓取 AW、W、B、AR、R 五个通道。

第 3 步:专测边界

至少覆盖:

text 复制代码
起始地址 0x0000_0FC0,长度 320 Byte
起始地址 0x0000_0FE0,长度 96 Byte
起始地址 0x0000_1000,长度 4096 Byte

观察 Burst 是否在 0x1000 正确断开。

第 4 步:接入 UDP → DDR

上位机发送带包序号和可预测负载的 UDP 数据:

text 复制代码
Header: magic + command + sequence + address + length
Payload: sequence + byte_index 或 LFSR 数据

FPGA 完成写入后返回 ACK:

text 复制代码
ACK: magic + sequence + status + bytes_written + error_code

ACK 必须在 B 通道成功响应后产生,不能在 UDP 数据刚进入 FIFO 时就返回"写成功"。

第 5 步:DDR → UDP 回读

上位机发送读命令,FPGA 从 DDR 读出指定范围并通过 UDP 返回。上位机检查:

  • 包序号连续;
  • 返回长度正确;
  • 每字节内容正确;
  • 超时包和乱序包数量;
  • FPGA 错误计数器是否增加。

第 6 步:做长时间压力测试

  • 连续发送不同长度 UDP 包;
  • 随机化读写地址,但限制在合法测试区;
  • 定时读取吞吐、丢包和 AXI 错误计数器;
  • 至少运行数十分钟,再逐步延长;
  • 改变温度、电压或链路负载时重新测试。

16. ILA 应该抓哪些信号

ILA 探针不是越多越好,而是要让一次触发能回答一个问题。

16.1 写通路 ILA

分组 建议信号
命令 cmd_valid/cmd_ready/addr/length/tag
规划 remain_bytes/plan_beats/bytes_to_4k
AW awvalid/awready/awaddr/awlen/awsize
W wvalid/wready/wlast/wstrb/w_beat_count
B bvalid/bready/bresp
FIFO level/full/empty/rst_busy
状态 state/error_code/bytes_written

16.2 读通路 ILA

分组 建议信号
AR arvalid/arready/araddr/arlen/arsize
R rvalid/rready/rlast/rresp/r_beat_count
AXIS tvalid/tready/tkeep/tlast
缓冲 free_space/level/almost_full
状态 state/error_code/bytes_read

推荐触发条件:

  • bvalid && bready && bresp != 0;
  • rvalid && rready && rresp != 0;
  • WLAST 与计划计数不一致;
  • 即将跨 4 KB 边界;
  • FIFO overflow/underflow;
  • 描述符完成字节数不等于 length。

17. 带宽怎样估算与实测

17.1 理论带宽

若 MIG AXI 数据宽度为 256 bit,UI 时钟为 300 MHz:

text 复制代码
理论单向数据带宽
= 256 / 8 × 300 MHz
= 9.6 GB/s

若 UDP AXIS 为 64 bit、125 MHz:

text 复制代码
理论流接口带宽
= 64 / 8 × 125 MHz
= 1.0 GB/s

对于千兆以太网,线路层极限还要扣除以太网前导码、帧间隙、MAC/IP/UDP 头等开销。因此本工程的数据瓶颈通常不是 DDR 峰值,而是网络链路和包长。

17.2 为什么实际带宽低于理论值

  • AXI 地址与响应通道存在开销;
  • Burst 太短;
  • 读写方向频繁切换;
  • DDR 行命中率低;
  • FIFO 水位策略过于保守;
  • 每个 Burst 都等待 B 响应后才规划下一次;
  • 只允许一个 Outstanding 事务;
  • UDP 使用大量小包;
  • 调试计数逻辑放在错误时钟域。

17.3 用硬件计数器测量

在同一时钟域内统计:

verilog 复制代码
if (measure_enable)
    perf_cycles <= perf_cycles + 1'b1;

if (m_axi_wvalid && m_axi_wready)
    perf_write_bytes <= perf_write_bytes + AXI_BYTES;

if (m_axi_rvalid && m_axi_rready)
    perf_read_bytes <= perf_read_bytes + AXI_BYTES;

吞吐计算:

text 复制代码
bandwidth = transferred_bytes × clock_frequency / measured_cycles

总线利用率:

text 复制代码
utilization = data_handshake_cycles / measured_cycles

如果最后一拍只有部分字节有效,精确统计应使用 WSTRB 或相应有效字节数,而不是固定累加 AXI_BYTES。


18. 第一版与高性能版怎样迭代

18.1 第一版:先保证正确

  • 对齐地址;
  • 单 Outstanding;
  • INCR Burst;
  • 最大 16/32/64/128 Beat;
  • 一个描述符完成后再取下一个;
  • 完整检查 B/R 响应;
  • BIST、错误锁存和 ILA 齐全。

这种架构性能不是最高,但波形清楚、状态可控,最适合第一次自研 DMA。

18.2 第二版:增加流水

  • 地址规划与数据发送并行;
  • AW 提前发送;
  • 多个读请求 Outstanding;
  • 描述符预取;
  • Ping-Pong 或多缓冲区;
  • 长 Burst 优先;
  • 读写分别设置仲裁配额。

18.3 第三版:多 Outstanding 与 ID

当同时发出多个读事务时,需要:

  • 为事务分配 AXI ID 或内部 tag;
  • 记录每个事务地址、长度和目标缓冲区;
  • 根据 AXI 排序规则处理返回数据;
  • 维护 scoreboard;
  • 错误发生时知道属于哪个描述符。

此时 DMA 已经从"状态机"升级为"小型事务处理器"。如果没有明确的吞吐需求,不要过早增加这部分复杂度。


19. 常见错误与定位思路

19.1 AWREADY 不来

检查:

  • MIG 是否完成校准;
  • AXI reset 是否释放;
  • SmartConnect 地址范围是否正确;
  • AWSIZE/AWLEN/AWBURST 是否合法;
  • 时钟关联属性与实际连线是否一致。

19.2 写了一些数据后卡死

高概率原因:

  • WLAST 少发或多发;
  • WVALID 在反压时没有保持;
  • 等待 AWREADY && WREADY 同拍成立;
  • 没有拉高 BREADY;
  • 跨 4 KB 的 Burst 被下游拒绝。

19.3 小包正确,大包错误

检查:

  • Burst 拆分后的地址和剩余长度;
  • AxLEN = beats - 1 是否少减或多减;
  • 4 KB 边界;
  • FIFO 水位单位;
  • 描述符长度位宽是否溢出。

19.4 32 Byte 整数倍正确,其他长度错误

几乎都与以下内容有关:

  • TKEEP → WSTRB 映射;
  • 最后一拍字节计数;
  • MM2S 重新生成 TKEEP/TLAST;
  • 非对齐地址却没有 DRE。

19.5 BIST 正确,UDP 写 DDR 错误

说明 MIG 和基础 DMA 大概率正常,重点检查:

  • UDP 与 DMA 间 CDC;
  • 帧 FIFO 是否整包提交;
  • 数据 FIFO 与描述符 FIFO 是否错位;
  • UDP payload 长度是否减去了协议头;
  • TLAST/TKEEP 是否在异步 FIFO 中完整传递。

19.6 改了 FREQ_HZ 后 DRC 消失,但板上仍不工作

这是典型的"修了属性,没有修电路"。重新确认两个接口是否真的由同一个时钟驱动;如果不是,必须加入合法 CDC 结构。


20. 推荐的 RTL 模块划分

不要把所有逻辑塞进一个 always 块。推荐如下划分:

模块 职责
udp_dma_cmd_parser 解析 UDP 命令并校验地址、长度
dma_desc_fifo 缓存读写描述符
axis_frame_fifo 整帧缓存、丢弃坏帧、CDC
axis_width_packer AXIS 窄转宽,生成字节有效掩码
axis_width_unpacker AXI 宽数据转 AXIS,生成 TKEEP/TLAST
dma_burst_planner 计算 Burst 长度与 4 KB 拆分
axi_s2mm_master AW/W/B 写事务
axi_mm2s_master AR/R 读事务
ddr_ring_manager 环形缓冲区分配与回收
dma_axi_lite_regs 配置、状态、错误寄存器
dma_bist 写、读、比较自检
dma_perf_monitor 字节、周期、停顿和错误统计

顶层只负责实例化、参数传递、时钟复位和接口连接。这样做的直接好处是:

  • Burst 规划器可以脱离 MIG 单独仿真;
  • AXI Master 可以接 AXI VIP 验证;
  • UDP 前端可以用 AXIS BFM 测试;
  • BIST 与真实 UDP 数据可以复用同一条 DMA 通路;
  • 出错时可以明确模块边界。

21. 工程检查清单

AXIS 入口

  • 只在 TVALID && TREADY 时接收数据;
  • TREADY=0 时上游数据保持稳定;
  • TLAST/TKEEP 进入 FIFO,不被丢失;
  • 空间不足时整帧丢弃,而不是留下半帧;
  • 描述符与数据一一对应。

S2MM

  • 地址满足当前版本的对齐要求;
  • AWLEN = Burst Beats - 1;
  • Burst 不跨 4 KB;
  • AW 与 W 独立握手;
  • WVALID 停顿时数据保持;
  • WLAST 位置正确;
  • 最后一拍 WSTRB 正确;
  • 检查并记录 BRESP。

MM2S

  • 发 AR 前确认输出 FIFO 空间;
  • ARLEN/ARSIZE/ARBURST 正确;
  • 检查每拍 RRESP;
  • 验证 RLAST 位置;
  • 根据描述符重新生成 TKEEP/TLAST;
  • 反压时返回数据不丢失。

DDR 与时钟复位

  • MIG 器件型号、位宽、时钟与原理图一致;
  • init_calib_complete 后才允许 DMA 启动;
  • 跨时钟域使用 AXIS FIFO、AXI Clock Converter 或握手;
  • 没有用修改 FREQ_HZ 代替 CDC;
  • 软复位前排空未完成事务。

验证与调试

  • BIST 能独立于 UDP 运行;
  • 覆盖 4 KB 边界和部分字节;
  • 仿真随机化 READY/VALID;
  • 有 AXI 响应错误注入;
  • ILA 能触发首个协议错误;
  • 有字节数、丢包、停顿和错误计数器。

22. 总结

从 UDP 到 DDR 的本质,不是把两组端口"接起来",而是完成三种语义的转换:

  1. 包语义:一个 UDP 包从哪里开始、在哪里结束、属于谁;
  2. 流语义 :TVALID/TREADY/TKEEP/TLAST 下怎样持续传输;
  3. 存储语义:从哪个地址开始、传多少字节、拆成多少个合法 Burst。

一套可靠的 FPGA DMA,至少要做好下面几件事:

  • 用描述符把地址、长度和数据关联起来;
  • 用帧 FIFO 保证网络包的完整性;
  • 正确处理位宽转换与最后一拍字节掩码;
  • 根据剩余长度、最大 Burst、4 KB 边界和 FIFO 水位规划事务;
  • 分别遵守 AXI 五个通道的握手规则;
  • 只有在 B/R 响应正确后才宣布事务完成;
  • 把 CDC、复位、错误统计和 BIST 当成架构的一部分。

如果第一版能够稳定完成"对齐地址、单 Outstanding、完整错误检查",后续增加环形缓冲、多描述符、多 Outstanding 和更高带宽就会顺理成章。反过来,如果最基础的边界、握手和复位没有验证,再复杂的流水结构只会让错误更难定位。

下一篇可以继续展开 UDP 高速数据采集系统的完整工程实现:把包头协议、DDR 环形缓冲、上位机 Python 程序、丢包重传与性能统计真正组合成一个可运行项目。


参考资料

  1. Arm, AMBA AXI and ACE Protocol Specification
  2. AMD, AXI DMA LogiCORE IP Product Guide (PG021)
  3. AMD, AXI DataMover LogiCORE IP Product Guide (PG022)
  4. AMD, UltraScale Architecture-Based FPGAs Memory IP Product Guide (PG150)

文中的寄存器地址、描述符位域、数据宽度与时钟频率是便于说明的工程示例。移植时应以自己的 FPGA 型号、开发板原理图、DDR 器件、MIG 配置和 Vivado 版本为准。

相关推荐
Jason_zhao_MR5 小时前
电力数据采集核心板选型指南
linux·嵌入式硬件·fpga开发·嵌入式
长春的KAKA7 小时前
Proxmark3 RDV4.01:Windows 11 下 Iceman 固件升级与完整使用指南
fpga开发
姜鱼问生15 小时前
Nginx 缓存命中率监控:从 X-Cache-Status 到实时统计
运维·nginx·缓存
奋斗的阿狸_19861 天前
ESP32-S3 + ES7210 四通道麦克风录音上传方案
c语言·开发语言·fpga开发
weixin199701080161 天前
《1688图片空间API踩坑:img.upload 与 album.* 的防盗链与CDN缓存问题》(附Python源码)
开发语言·python·缓存
hahaha60161 天前
FPGA+ARM实现AWB的全流程
人工智能·嵌入式硬件·算法·计算机视觉·fpga开发
hweiyu001 天前
Redis命令:BLPOP
redis·缓存
unicrom_深圳市由你创科技1 天前
板上调试,先抓哪些信号?排查顺序是什么?
fpga开发
小小龙学IT1 天前
C++ Redis 客户端深度解析(hiredis 与 redis-plus-plus)——工业数采实时缓存 C++ 侧实战
c++·redis·缓存