FPGA 系统实战 · 03 运行第一笔 DDR 往返与软件校验

从 XSA 建立 standalone 应用,先接收后发送,加入超时、状态与实际长度检查。

先修知识完成 FPGA AXI4-Stream / 异步 FIFO 实战;会读 AXI 时序、C 指针和链接 map。

适用环境ZedBoard Zynq-7020 / PS DDR;Vivado、Vitis 2024.1 参考;本地 Python 3 + cc + Icarus Verilog。

验证状态13 项本地用例通过:C 主机后端与教学 FIFO / RTL 组合;Vivado、Vitis BSP、厂商 IP、DDR 和板上 ILA 尚未实测。

本文目录用同一份应用驱动两种后端建立平台并验证内存放置上板执行的四个检查点典型故障:应用永远等待接收

用同一份应用驱动两种后端

dma_app.c 包含真正要学习的缓冲区准备、所有权转移、命令顺序和结果校验。定义 HOST_TEST 时选择行为模型;目标板构建不定义该宏,使用 XAxiDma 驱动和 XTime 定时器。主机编译成功不等于 Vitis BSP 编译成功;目标环境仍须核对 xparameters.h 的实例宏、standalone 域以及 PS UART 输出配置。

先接收后发送,让下游已准备好;顺序本身不能替代协议正确性CPU 准备模式 + 缓冲区先启动 S2MM提供接收容量再启动 MM2S按长度产生单包双向完成读状态 + 校验一次软件命令对应一个流式包;仅 VALID && READY 时接受数据
先接收后发送,让下游已准备好;顺序本身不能替代协议正确性(原创示意,非器件引脚图;窄屏可横向滑动)

建立平台并验证内存放置

从匹配的 system.xsa 创建 ps7_cortexa9_0 standalone 平台和 Empty Application。把 dma_app.c/platform_hw.h 加入应用,不复制主机后端作为硬件驱动。链接脚本必须将数据数组放进 PS DDR,检查 map 而不只查看源文件中的 static 关键字;每块65536字节且64字节对齐,栈不能与其重叠。

配置与验收依据
项目设置 / 预期 / 处理
新式 SDT BSPXAxiDma_LookupConfig(XPAR_XAXIDMA_0_BASEADDR)
旧式 BSPXAxiDma_LookupConfig(XPAR_AXIDMA_0_DEVICE_ID)
地址打印 tx/rx,与 link map 和 HP0 DDR 可达范围对照
目标日志HARDWARE 开头;12条 PASS;suite=12 transfers
实际长度完成后读取 S2MM_LENGTH;必须等于请求长度

完整应用;需同时使用下载包中的 platform_hw.h 或 platform_host.h。

c
/* Original teaching application; identity-mapped standalone Cortex-A9 only.
 * HOST_TEST executes this exact orchestration with a cache/DDR behavioral model. */
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#define CAP 65536U
static uint8_t tx[CAP] __attribute__((aligned(64)));
static uint8_t rx[CAP] __attribute__((aligned(64)));
#ifdef HOST_TEST
#include "platform_host.h"
#else
#include "platform_hw.h"
#endif
static int transfer(unsigned n,unsigned seed) {
 for(unsigned i=0;i<n;i++)tx[i]=(uint8_t)(i*17+seed);
 memset(rx,0xcc,CAP);
 uint64_t begin=now();
 clean(tx,CAP); clean(rx,CAP); barrier();
 uint64_t start=now();
 if(submit_rx(rx,n)||submit_tx(tx,n))return fail("SUBMIT");
 while(busy()){
  if(errors())return fail("DMA_ERROR");
  if(now()-start>ticks_per_second())return fail("DMA_TIMEOUT");
 }
 if(errors())return fail("DMA_ERROR");
 uint64_t done=now();
 barrier(); invalidate(rx,CAP); barrier();
 uint64_t acquired=now();
 if(actual()!=n)return fail("LENGTH_MISMATCH");
 for(unsigned i=0;i<n;i++)if(rx[i]!=(uint8_t)(tx[i]^0xa5)){
  printf("mismatch offset=%u expected=%02x actual=%02x\n",i,(unsigned)(tx[i]^0xa5),(unsigned)rx[i]);
  return fail("DATA_MISMATCH");
 }
 printf("PASS bytes=%u actual=%u dma_ticks=%llu ownership_ticks=%llu timer_hz=%llu\n",
 n,actual(),(unsigned long long)(done-start),(unsigned long long)(acquired-begin),
 (unsigned long long)ticks_per_second());
 return 0;
}
int main(void){
 if(setup())return 1;
 const unsigned sizes[]={64,4094,4096,65536};
 for(unsigned s=0;s<4;s++)for(unsigned rep=0;rep<3;rep++)
  if(transfer(sizes[s],3+rep))return 1;
 puts("PASS suite=12 transfers; bytes checked; one TLAST per transfer");return 0;
}

上板执行的四个检查点

板卡上电并下载 PL 位流后,DDR 仍需要 PS 初始化。使用匹配 XSA 的 Vitis 调试启动配置执行 ps7_init/ps7_post_config,或使用同一硬件导出的 FSBL。不要拿旧工程的初始化脚本配新地址图。软件先提交 RX,再提交 TX,每轮等待两通道结束,同时检查错误状态;超时后保留现场并停止,不能继续比较旧缓冲区。

  1. 构建 platform/application

    预期与判定:BSP 编译通过,map 中 TX/RX 位于 DDR。

  2. 下载位流、关联 LTX 并初始化 PS

    预期与判定:UART 正常;DDR 可访问;ILA 时钟运行。

  3. 下载 ELF 并 Run

    预期与判定:看到 HARDWARE 和12次 PASS,无 LENGTH_MISMATCH。

  4. 保存日志和构建摘要

    预期与判定:包含 bit/xsa/elf 版本、地址、实际字节数和 timer_hz。

调用关系示意;请运行上方完整应用,不要省略其错误处理。

c
/* 目标驱动调用:先 RX,后 TX */
XAxiDma_SimpleTransfer(&dma, (UINTPTR)rx, n, XAXIDMA_DEVICE_TO_DMA);
XAxiDma_SimpleTransfer(&dma, (UINTPTR)tx, n, XAXIDMA_DMA_TO_DEVICE);
/* 每次调用必须检查返回值;完整封装会检查错误、超时及实际长度。 */

典型故障:应用永远等待接收

如果发送已结束而接收迟迟不结束,先保存两通道 DMASR、S2MM_LENGTH 和最后一次握手。缺失 TLAST 是一种原因,接收未启动、流式复位未释放或源被反压也可能表现为超时。程序用真实定时器限制等待约一秒;发生异常后复位 DMA 并停止,本轮数据不得作为有效输出。

完成检查

  • 已记录本章配置、版本及预期结果。
  • 能区分本地模型验证与实际板上证据。
  • 能复现典型故障并说明修复后的复测标准。

延伸阅读与来源

本文为黄河电子原创教学说明。产品命令与支持范围请以实际安装版本的官方文档为准。

继续阅读

FPGA

从 RTL 到板上运行的完整流程

建立约束、实现、下载与观测的最小工程闭环。

10 分钟
FPGA

第一个工程的引脚与时钟约束

区分电气连接与时间要求,避免“能生成位流就算正确”。

11 分钟
FPGA

如何读懂时序报告

从起点、终点、时钟和裕量找出失败路径的原因。

10 分钟