AD9361与Vivado工程实战:SPI、LVDS与DMA链路全解析

发布时间:2026/10/21 4:13:27
AD9361与Vivado工程实战:SPI、LVDS与DMA链路全解析 简介针对AD9361射频收发器与Xilinx Vivado 2016.4的完整例程包主要面向FPGA工程师、软件无线电开发者和射频通信技术爱好者。资源围绕Vivado工程展开覆盖AD9361从IP核集成、AXI4接口互联、时钟与数据通路配置、时序约束到仿真验证、Bitstream生成及硬件调试的完整流程能帮助用户快速建立自己的SDR通信原型。包内共2000个文件以VHDL/Verilog源码、TCL脚本、C/H驱动、makefile构建脚本及XDC约束文件为主体并附有大量仿真实例、LOG日志、工程位流和PDF说明文档各类型文件分工明确便于分类检索与复用。压缩包整体95.07MB承载了工程创建、IP定制、综合实现与下载调试各阶段产物。目前已有4861人学习适合希望深入理解AD9361配置方式或正在Zynq平台上进行射频开发的工程师可直接复用其中的IP配置、引脚约束、初始化代码与软件驱动显著降低从零开始搭建的难度和工作量。1. 射频收发工程的钥匙AD9361与Vivado中间那条看不见的链路做射频基带开发的朋友应该都有过这种体验板子上明明焊着AD9361Vivado里工程也编译通过了JTAG连着能看到芯片可ILA抓下去全是零寄存器怎么写都像石沉大海。那感觉就像面对一个黑匣子——你不清楚FPGA和AD9361之间到底该有哪些信号、什么时序、谁先谁后。这套ad9361_vivado例程要解决的正是这个问题。它把一个完整的AD9361数字侧参考实现拆给你看SPI配置通道怎么搭、LVDS数据通路怎么采样、初始化脚本怎么组织、上板之后用什么顺序验证。对刚接触软件无线电的开发者来说它能让你少走一个月弯路对已经有经验的工程师来说它是一份可以直接当成工程骨架来改的模板。芯片花几个小时焊完剩下的功夫全花在和Vivado打交道这件事上。这套例程的价值就是把那段“看不见的链路”变成看得见、能跑通、也能改的代码。2. 架构拆解SPI、LVDS与DMA是怎么把射频前端牵进FPGA的AD9361在FPGA侧看起来不是一个普通ADC/DAC它的内部已经完成了从天线到基带的模拟链路处理FPGA侧的引脚上直接出现的是数字I/Q数据。因此这套例程的架构核心是四条并行的通路配置通路、数据通路、时钟通路和控制通路。写代码之前先看这四条线整个工程就通了。2.1 模块地图先从顶层看例程由哪几块拼起来把例程解压后用Vivado打开Block Design能看到顶层通常由这么几类模块组成一个SPI控制器、一组LVDS或CMOS数据接收/发送逻辑、一个DMA搬运模块再加上若干时钟管理原语和复位逻辑。如果例程跑在Zynq平台上还会有一个AXI处理器系统负责跑初始化脚本如果是纯FPGA方案通常会挂一个MicroBlaze软核做同样的事。这里有个很容易被忽略的细节AD9361本身没有内部存储器所有射频参数——频率、带宽、增益、滤波器系数——都必须在上电后由外部芯片通过SPI写进去。所以例程里SPI相关代码永远是最先被执行的。把这四条通路画成一张接口清单一目了然接口组方向关键信号FPGA侧对应模块SPI配置FPGA → AD9361CSB、SCLK、SDI/SDOSPI控制器状态机数字数据FPGA ⇄ AD9361P0_D[11:0]、DATA_CLK、RX_FRAMEIDELAY 采集逻辑时钟AD9361 → FPGADATA_CLK、FB_CLKBUFG MMCM/PLL控制/状态FPGA ⇄ AD9361ENABLE、TXNRX、LOCK_DETECTGPIO控制器与状态寄存器我第一次拆这条例程时犯了个糊涂以为数据线和时钟线只要接到引脚上就能用结果ILA一片空白。后来才意识到RX_FRAME这个帧同步信号才是数据通路的“闸门”——它告诉FPGA哪一拍开始是有效的I/Q数据。没有它采回来的数据就是错位的。2.2 SPI控制通道寄存器配置的必经之路与指令字格式AD9361的SPI和普通存储器SPI有一点很不一样它的指令字是16位的而不只是发送一个地址。写操作要把“写标志 地址 数据”拼在一起按帧发出去读操作则要先发命令帧再收数据字节。这个格式如果不看数据手册光靠猜很容易翻车。例程里的SPI控制器一般不直接用Xilinx的IP因为那个IP是为通用存储器件设计的字节序和片选极性和AD9361对不上。常见做法是像这份例程一样在PL侧写一个十几行的小状态机把CSB拉低、逐位送出命令和数据、再拉高结束一帧。状态机的核心参数只有两个SCLK的极性和数据是上升沿采样还是下降沿采样。默认情况下AD9361在SCLK上升沿采样FPGA侧就应该在下降沿切换数据保证建立时间。这里的血泪经验是先把CSB、SCLK、SDI/SDO四个引脚用ILA单独抓一遍确认波形的帧格式符合手册再开始调射频参数。很多配置“看起来写进去了”但其实地址偏移一位导致所有寄存器写入都落在错误位置。2.3 LVDS数据通路数据、时钟、帧三者的主从关系AD9361的数字接口有CMOS和LVDS两种模式例程里常用的是LVDS因为数据速率高、抗干扰好。LVDS模式下FPGA侧不光要收数据和时钟还要处理帧信号。DATA_CLK是由AD9361送出的随路时钟RX_FRAME则标记I/Q数据的边界帧信号为高时P0_D上的数据是有效的I/Q采样点。FPGA侧接收时通常先在IBUFDS里把差分信号转成单端然后用IDELAYE2对数据线做可编程延迟最后在DATA_CLK的边沿上把12位数据采进来。为什么要加延迟因为数据和时钟虽然来自同一个芯片但经过板级布线后到达FPGA引脚的时间并不一致PCB上每组差分线的长度也不可能完全等长。这个几十皮秒到几百皮秒的skew在高数据速率下就是采错位的根源。IDELAYE2就是用来微调数据采样窗口的。2.4 DMA搬运ADC数据落进DDR之前的最后一段路数据从AD9361进FPGA之后如果目标是DDR内存例程里会接一个DMA控制器。Zynq平台上常见的做法是把LVDS接收逻辑的输出整理成AXI-Stream流再接到DMA的S2MM通道发送方向则是DMA的MM2S通道把DDR里的数据送到AD9361的发送接口。这块代码看时序很绕但实际工程里需要注意的就三件事数据位宽匹配、字节序一致、描述符缓冲足够。AD9361一个采样点是12位I加12位Q合起来24位为了带宽利用率例程往往会打包成32位或者64位再搬运。如果打包的字节序和DMA配置的字节序不一致后面在软件里解出来的I/Q就是颠倒的表现为频谱上的镜像。3. 核心代码逐段读配置时序、初始化脚本与采样对齐的实际写法这一章把例程里最值得抄的三段代码拆开讲SPI写寄存器状态机、初始化脚本的C语言结构、以及LVDS数据采样对齐的原语使用。代码都不长但每一段背后都有明确的时序要求出错方式和排查方法我放在第5章一起说。3.1 SPI写寄存器状态机一个完整写帧的Verilog实现例程里SPI写操作的状态机并不复杂核心就是用一个16位移位寄存器把命令和打包的数据依次送出去。下面这段代码是我按例程风格整理出的写操作核心片段// ad9361_spi_wr: 单次8位寄存器写操作 // cmd_frame 由写标志、寄存器地址和写入数据拼成 wire [15:0] cmd_frame {1b0, 3b000, reg_addr[11:0]}; reg [15:0] tx_buf; reg [4:0] bit_cnt; reg spi_active; always (posedge clk or negedge rst_n) begin if (!rst_n) begin csb 1b1; sclk 1b0; spi_active 1b0; end else begin if (wr_req !spi_active) begin csb 1b0; // 片选拉低开启一帧 tx_buf {cmd_frame[15:12], reg_addr[11:0], wdata[7:0]}; bit_cnt 5d23; // 12位命令 8位数据共20位有效 spi_active 1b1; end else if (spi_active) begin sclk ~sclk; // 分频产生SCLK这里简化为反相 if (!sclk) begin // 低电平期间切换数据 sdo tx_buf[bit_cnt]; bit_cnt bit_cnt - 1b1; if (bit_cnt 5d0) begin spi_active 1b0; wr_done 1b1; csb 1b1; // 片选拉高结束一帧 end end end else begin wr_done 1b0; end end end这段代码的关键在tx_buf的拼位顺序高4位是命令头中间12位是寄存器地址低8位是写入数据。这个顺序直接对应数据手册里SPI帧格式——写标志为0表示写操作地址从MSB开始送数据紧跟着地址。如果这里拼反了后面所有配置都会错位。实际例程里SCLK不会是简单反相而是一个分频时钟频率一般压在主时钟的1/8到1/16。我习惯把SCLK控制在10MHz以下因为AD9361的SPI口虽然支持更高频率但板级走线电容、逻辑分析仪探头的负载都会影响时序速度慢一点换取稳定性非常值。3.2 初始化脚本用C结构体组织几百条寄存器配置例程里真正“干活”的是一张很长的寄存器配置表。正确做法是用芯片厂商提供的寄存器配置工具生成初始化序列然后把它放在一个结构体数组里由软件按顺序写入。下面是我从例程里提炼出的写法// ad9361_init_seq: 初始化配置表顺序、地址、值一一对应 typedef struct { uint16_t addr; // 寄存器地址 uint8_t val; // 寄存器值 } reg_item; static const reg_item ad9361_init_seq[] { {0x000, 0x0F}, // 解除SPI锁定使能寄存器访问 {0x002, 0x01}, // 软复位等待内部状态机重启 {0x003, 0x61}, // 参考时钟分频配置与上电控制 // 中间几百行频率、带宽、滤波器系数等 // 每一行都由配置工具生成不建议手写 }; void ad9361_load_seq(const reg_item *tbl, int len) { for (int i 0; i len; i) { ad9361_spi_write(tbl[i].addr, tbl[i].val); } }这个表有几条经验。第一顺序很重要不能随便重排因为某些寄存器配置之间可能存在依赖关系比如先设置时钟分频再配置滤波器。第二写完最好回读一遍关键寄存器因为SPI在高速时偶发错误是正常的回读能提前发现问题。第三如果改了载波频率不要整表重发只需要更新频率相关的那几个寄存器并触发VCO校准效率完全不同。3.3 LVDS采样对齐IDELAYE2的使用与参数语义LVDS数据到达FPGA后第一个要解决的问题就是“在哪个时刻采样”。例程常规做法是把每根数据线都过一遍IDELAYE2用固定延迟把数据窗口移动到时钟边沿附近。下面是一段典型的例化代码// rx_data_delayed: 对单比特数据线做固定延迟 // 使用FIXED模式综合后不再调整方便时序收敛 (* IODELAY_GROUP ad9361_rx_if *) IDELAYE2 #( .IDELAY_TYPE (FIXED), .IDELAY_VALUE (16), .HIGH_PERFORMANCE_MODE (TRUE) ) u_idelay_rx_data ( .IDATAIN (lvds_rx_data_p), // 来自IBUFDS的差分转单端输出 .DATAOUT (rx_data_delayed), // 延迟后的数据送采集逻辑 .C (1b0), .CE (1b0), .INC (1b0), .CINVCTRL (1b0), .CNTVALUEIN (8d0), .CNTVALUEOUT(), .LD (1b0), .LDPIPEEN (1b0), .REGRST (1b0) );IDELAY_VALUE是延迟档位7系列FPGA里一个tap对应几十皮秒量级具体值需要根据板级时序实测来确定。例程里通常会先给一个中间值然后上板用ILA观察数据是否稳定再逐个tap调整。如果你发现数据时好时坏大概率就是延迟档位选在了数据跳变的边缘上挪一两个tap就能解决。4. 从建工程到上板验证照着例程的四步走很多人拿到例程的第一反应是直接开个新工程然后复制代码这其实是最容易出问题的路径。例程的工程文件、IP配置、约束文件之间是绑定的正确做法是先按例程带的方式重建工程跑通了再开始改自己的东西。4.1 先读目录结构再决定动哪个文件一份完整的AD9361 Vivado例程目录划分通常很清晰HDL源码单独放在一个目录Vivado工程和IP相关文件在另一个目录软件驱动代码如果带Zynq的话还会有独立的C工程目录。建议拿到手先花十几分钟把目录树过一遍重点看三样东西顶层模块文件名、约束文件里有没有针对AD9361引脚的位置约束、以及有没有专门的初始化C文件。我踩过的最大的坑是看到例程是给某开发板写的就直接把约束文件删了换成自己的板卡引脚。结果编译倒是很快通过上板后数据全乱。原因在于AD9361的DATA_CLK和数据线之间有时序关系约束文件里不仅有引脚位置还有input delay约束来控制数据采样窗口。删掉约束等于把采样相位交给运气。4.2 用构建脚本重建工程比手动加文件可靠得多例程一般都会带构建脚本常见形式是Tcl脚本或批处理文件。它会把HDL源码、IP核、约束文件、仿真文件一次性按正确参数生成Vivado工程比手动一个个添加文件可靠得多。一个典型调用长这样# 用批处理模式生成并编译工程 # 参数1是目标板卡型号参数2是顶层工程名 vivado -mode batch -source proj_gen.tcl -tclargs zc706 fmcomms2这段命令会把整个工程生成在指定目录下自动完成IP核例化和文件添加。生成完先检查一个问题Vivado版本是否和例程创建时一致。版本跨得太大IP核会弹出升级提示升级后部分IP的配置可能被重置直接影响时序。我一般会先看例程文档里标注的版本再决定用哪个Vivado打开。版本对不上时宁可在旧版本环境里跑也不要在新版本里硬升。4.3 上板前的四项确认时钟、电平、复位、电源顺序工程编译通过不代表硬件就绪。上板之前按例程文档里的说明逐项确认第一参考时钟是否给到了AD9361的REF_CLK引脚没有参考时钟芯片内部PLL无法工作第二LVDS引脚的电平标准是否设置成LVDSFMC接口上同一个bank如果被复用为别的电平标准会因为驱动不匹配导致数据完全无效第三复位逻辑是否释放干净很多例程的复位信号是由处理器控制GPIO拉高的如果软件没跑到这一步AD9361一直处于复位状态第四电源域的上电顺序是否满足要求。这四项里最容易忽略的是最后一项。AD9361的供电域比较多上电顺序不对芯片可能不会立即损坏但内部状态机起不来表现就是SPI能正常访问但射频链路完全没有输出而且这个故障是静默的没有任何寄存器会报告“上电顺序错误”。4.4 验证数据的正确顺序先数字后射频上板后不要急着看频谱先确认数字链路在动。我习惯的验证顺序是第一步用ILA抓AD9361送出的DATA_CLK和RX_FRAME确认有信号翻转第二步向AD9361写入一个简单的寄存器值比如写0xAA到0xFF地址再回读确认SPI链路双向都通第三步把AD9361设置为内部回环模式让发送链路的数据直接回到接收链路此时再抓数据总线看是否有连续的I/Q数据出现。只有这三步都过了才说明FPGA侧没问题可以去调载波频点和观察真实射频信号。用ILA抓数据时有一个细节尽量把采样时钟设为DATA_CLK域的信号而不是系统主时钟。因为AD9361的数据是随路时钟送出的用系统时钟去采异步数据等于以错误的时间基准去观察正确的事件看到的结果当然是一团乱麻。5. 常见问题排查AD9361与Vivado配合最常踩的五个坑这个芯片我和Vivado打了几年交道下面五个问题属于每次新项目几乎都会遇到的。按“现象、原因、解决”三段写可以直接当作排查手册用。5.1 现象ILA抓不到任何LVDS边沿数据全为0原因大概率是两个一是ILA的采样时钟选错了域用了系统主时钟去采AD9361随路时钟域的数据二是LVDS差分引脚没有经过IBUFDS直接以单端逻辑去读数据根本没被正确接收。解决先检查ILA例化里的时钟是否连接到了AD9361的DATA_CLK经过BUFG之后的信号上再检查引脚约束里数据线是否被综合成了真正的差分对。如果都用对了用IBUFDS的输出作为观察点再次抓取通常就能看到边沿。5.2 现象SPI读回全是0xFF或0x00先说原因0xFF通常是SDI引脚悬空或读写方向上配置错误0x00则是命令帧格式不对芯片根本没解析到有效地址。最常见的是指令字拼位顺序出错写标志、地址、数据三层拼到了一起后位移了一位。解决把SPI的四个信号全部拉进ILA对照数据手册一帧一帧看波形。重点核对片选拉低后第一位送出的值是不是1读标志或0写标志以及地址是否能和回读寄存器对上。这个方法看起来很土但确实能解决九成SPI问题。5.3 现象数据在动但解出来的频谱中心频率不对现象是ILA数据有数值变化但软件里做FFT后发现信号中心频率和设定值差了相当距离。原因多数出在初始化脚本上参考时钟频率和脚本里LTE的配置不一致或者VCO校准没有被正确触发。解决第一步核对板上的参考时钟频率这个值必须和初始化脚本开头的分频配置严格对应差了一倍所有频点都会随之偏移第二步检查初始化流程末尾是否有VCO校准触发命令如果漏了PLL可能锁定在错误谐波上。用频谱仪观察LO泄漏谱线可以快速确认本振是否落在预期位置。5.4 现象DMA通道没有数据处理器中断一直在空转首先确认DMA本身是不是好的最简单的方式是让DMA从DDR里读固定数据再写回然后比对内存内容能过说明通道本身没坏。如果DMA没问题重点查数据通路的位宽和字节序是否匹配——AD9361的输出是24位I/Q经过打包模块变成32位或64位时如果字节序和DMA描述符里配置的不一致DMA仍然会搬运但搬出来的数据是乱的表现出来就像没有可用数据。解决把打包模块的输出和DMA数据接口都接到ILA上暂停一次传输检查两侧总线上的字节序是否一致。这个检查做完DMA方向的坑基本都能排除。5.5 现象工程编译有大量违例告警上板后完全无信号先别忙着忽略告警AD9361例程对时序收敛要求不低大量VOID告警往往意味着关键路径没有被正确约束。最常见的元凶是LVDS引脚约束缺失或IO标准冲突FMC接口上bank电压和引脚标准不匹配导致输出驱动强度不足或接收端根本没有有效电平。解决综合后打开Implemented Design在IO规划器里把AD9361相关引脚全部检查一遍确认IO标准和bank电压相符。然后看时序报告里有没有长时间未收敛的路径如果有优先检查IDELAYE2的配置和input delay约束是否与板级走线匹配。6. 改造思路把官方例程变成你自己板卡的数字基带前端这套例程跑通之后下一步就是改成自己产品需要的形态。改法的核心是一个原则尽量保留SPI和LVDS数据链路不动只改参数和外围逻辑。因为这两块的时序是经过验证的动它们性价比最低。载体频率和带宽是最好改的部分正确做法是用寄存器配置工具生成新的初始化表替换例程里的结构体数组。改完记得重新生成整个工程别手动在配置表中间插入一行因为顺序依赖可能就在你不注意的那一行里。数据接口模式也常需要改CMOS和LVDS的区别集中在引脚约束、IBUFDS/IDDR逻辑和IDELAY配置上CMOS接口调试直观门槛低适合低速率原型验证正式产品再换LVDS也来得及。真正的二次开发重头在数据通路中间插自己的处理逻辑。例程里从RF到内存是一条直达的AXI-Stream流如果你要做数字下变频、滤波或解调就在LVDS接收逻辑和DMA之间插入你自研的IP通常需要做的只是把数据位宽对齐到AXI-Stream协议并在打包模块前加一个握手模块。发送方向同理。从那以后我每次拿到新的AD9361板卡都强制自己先跑一遍例程的默认配置把ILA时钟、SPI回读、LVDS采样相位这三件事全部确认完才允许自己动寄存器值。这个顺序看起来繁琐但帮我避免了多次“改了上百条寄存器还是没信号”的抓狂局面。希望这个走查顺序也能帮到你祝你的射频链路一次点亮。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询