本文基于 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 读出并送回网络,需要回答下面几个工程问题:
- 一个 UDP 包应该写到 DDR 的什么地址?
- AXIS 只有数据流,地址和长度从哪里来?
- 一包数据不是 AXI 总线宽度的整数倍,最后一拍怎样写?
- 一次传输跨越 4 KB 地址边界时,Burst 怎样拆分?
- MIG 的 UI 时钟与以太网时钟不同,在哪里跨时钟域?
- DDR 来不及接收或者 UDP 暂时不能发送时,怎样反压而不丢包?
- 没有 MicroBlaze 时,怎样启动、管理和验证 DMA?
- 仿真通过以后,上板怎样判断问题出在 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
图中最关键的两个边界是:
- UDP 域到 DDR 域的 数据、描述符和复位跨时钟域;
- 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:生成 AXIWSTRB;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
注意两个细节:
plan_beats不能为 0,必须先满足启动条件再发AWVALID/ARVALID;- 如果允许非对齐地址,
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 包缓存,我更推荐:
- 数据写入环形数据区;
- 包完整写入后,向描述符 FIFO 提交
{addr, length, tag}; - 读端处理完一个描述符后再释放对应空间。
#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:
- 先测试一个对齐的 256 Byte 区域;
- 再测试 4 KB 边界附近;
- 再测试不同 Burst 长度;
- 再测试环形缓冲区回卷;
- 最后做大范围 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,可能让互连永久等待。更稳妥的流程是:
- 停止接受新描述符;
- 等待当前事务结束;
- 清 FIFO 与状态;
- 重新开放命令入口。
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
仿真平台维护一个参考内存:
- S2MM 输入每握手一个有效字节,就写入参考内存;
- 监控 AXI 写事务,确认地址、数据与
WSTRB正确; - MM2S 返回 AXIS 时,逐字节与参考内存比较;
- 检查
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 的本质,不是把两组端口"接起来",而是完成三种语义的转换:
- 包语义:一个 UDP 包从哪里开始、在哪里结束、属于谁;
- 流语义 :
TVALID/TREADY/TKEEP/TLAST下怎样持续传输; - 存储语义:从哪个地址开始、传多少字节、拆成多少个合法 Burst。
一套可靠的 FPGA DMA,至少要做好下面几件事:
- 用描述符把地址、长度和数据关联起来;
- 用帧 FIFO 保证网络包的完整性;
- 正确处理位宽转换与最后一拍字节掩码;
- 根据剩余长度、最大 Burst、4 KB 边界和 FIFO 水位规划事务;
- 分别遵守 AXI 五个通道的握手规则;
- 只有在 B/R 响应正确后才宣布事务完成;
- 把 CDC、复位、错误统计和 BIST 当成架构的一部分。
如果第一版能够稳定完成"对齐地址、单 Outstanding、完整错误检查",后续增加环形缓冲、多描述符、多 Outstanding 和更高带宽就会顺理成章。反过来,如果最基础的边界、握手和复位没有验证,再复杂的流水结构只会让错误更难定位。
下一篇可以继续展开 UDP 高速数据采集系统的完整工程实现:把包头协议、DDR 环形缓冲、上位机 Python 程序、丢包重传与性能统计真正组合成一个可运行项目。
参考资料
- Arm, AMBA AXI and ACE Protocol Specification
- AMD, AXI DMA LogiCORE IP Product Guide (PG021)
- AMD, AXI DataMover LogiCORE IP Product Guide (PG022)
- AMD, UltraScale Architecture-Based FPGAs Memory IP Product Guide (PG150)
文中的寄存器地址、描述符位域、数据宽度与时钟频率是便于说明的工程示例。移植时应以自己的 FPGA 型号、开发板原理图、DDR 器件、MIG 配置和 Vivado 版本为准。