ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

AD9122高速DAC实战:SPI配置与LVDS接口设计调试

AD9122高速DAC实战:SPI配置与LVDS接口设计调试 第一次在项目上用到AD9122时我的第一反应是“这芯片太挑接口了”。16位双通道、最高1200MSPS的更新率如果老老实实按并行CMOS方式喂数据接口速率得上Gbps级别一般的FPGA和PCB布线根本招架不住。所以LVDS数据接口与SPI配置寄存器就成了整个设计绕不开的两座山SPI负责让DAC听指挥LVDS负责把高速数据安稳送进去任何一边出问题输出端都是一片乱码。这篇文章把我调通AD9122过程中梳理出来的SPI配置流程、LVDS接口设计、FPGA侧实现和调试排障经验全部整理出来。适合正在做FPGADAC高速信号链的硬件工程师和逻辑工程师参考也适合刚接触ADI高速DAC的开发者快速建立整体认知。看完之后你至少能少踩一半的坑。1. 项目整体架构与设计思路1.1 为什么选择AD9122高速DAC在信号链路中的定位AD9122是ADI公司推出的一款双通道、16位、最高1200MSPS的高速DAC芯片。单看参数它不算极端但强在内部集成了完整的数字插值滤波器和灵活的输入接口可以直接接收来自FPGA的LVDS/CMOS并行数据再经过内部PLL和调制链路输出高质量模拟信号。实际项目中我们主要拿它做宽带波形发生、基带信号输出以及任意波发生器它在ADC/DAC构成的信号链里属于“数字域”和“模拟域”交界处的关键器件。选型的时候对比过其他几款DACAD9122的优势在于接口可配置性非常好。可以用标准CMOS接口也可以用LVDS接口支持单通道、双通道以及复用模式插值倍数可以从1倍到8倍可调。换句话说它并不会强迫你把接口速率拉到极限而是给你足够的配置空间去迁就FPGA的实际工作频率。这个特性在高速设计中非常值钱因为系统瓶颈往往不是DAC本身而是数字侧的数据传输能力。1.2 从DAC工作原理看系统瓶颈高速DAC的基本工作过程并不复杂每个DAC时钟周期数字输入端口收到一个采样值内部转换器把它变成对应的模拟输出电平理想情况下是一串时间上离散的模拟样本再由后级低通滤波器重建出连续波形。问题在于当DAC更新率越来越快前端并口数据速率就会成为“堵点”。做一个简单的计算。假设DAC要跑500MSPS数据位宽16bit如果使用CMOS并行接口那么每个时钟周期需要同时翻转16根单端信号线接口数据率相当于500M×168Gbps。更难受的是CMOS单端信号在高速翻转时容易引入地弹、串扰和EMI跑上300MHz就已经很挣扎了。如果改成LVDS DDR差分接口情况会好很多8对差分线在DDR模式下每根线的等效速率可以做到1GbpsDCI时钟只需500MHz整个接口的压力明显下降。如果你再打开AD9122内部的插值滤波器让数字输入速率降到DAC更新率的1/2、1/4甚至1/8那么接口时钟和数据率可以进一步大幅降低。比如DAC输出400MSPS使用4倍插值则数据输入只需要100MHz即可LVDS接口的时序余量瞬间变得宽裕。这也是为什么很多AD9122方案里FPGA主时钟通常只有100MHz~200MHz而DAC输出却能跑到几百兆甚至上GHz的原因。1.3 时钟树与数据流设计整体架构如何组织我这里画了一张“文字版系统框图”方便你对照着看系统参考时钟通常来自低抖动晶振或干净的外部时钟源输入给FPGA和AD9122的参考时钟引脚。FPGA内部产生基带数据DDS、调制器、波形表等送去LVDS发送逻辑。LVDS数据线连接FPGA与AD9122包含数据线和一对DCI数据时钟线。AD9122通过SPI接口接收FPGA或MCU的配置命令完成复位、时钟分频、插值、接口模式、使能等设置。DAC模拟输出经变压器/放大器送到外部接口或送入下一级射频链路。系统中每个模块的功能和设计考量我整理成了表格方便对照模块核心器件/接口设计关注点参考时钟源晶振/时钟芯片抖动、稳定性、与DAC时钟的同步关系数字数据源FPGA数据格式、位序、使能时序LVDS接口差分对、DCI阻抗、等长、端接、建立保持余量SPI配置接口SCLK/SDIO/CSB时序模式、上电顺序、回读验证DAC模拟输出变压器/LPF/运放插值镜像抑制、幅度、偏置、杂散整个系统里最容易被忽视却最关键的其实是时钟。AD9122的工作时钟可以由外部直接提供也可以通过内部PLL倍频/分频得到。数据DCI时钟和DAC采样时钟之间的相位关系直接影响输入数据的建立保持窗口。所以设计时钟树时我通常会先确定“DAC采样率”和“插值倍数”再反推数据输入带宽最后确定FPGA侧的逻辑时钟和DDR数据传输格式。顺序不能反否则接口时序很容易一塌糊涂。2. AD9122的SPI配置体系深入拆解2.1 SPI引脚、帧格式与上电时序AD9122的SPI控制口逻辑上非常简单只有四根线SCLK、SDIO、CSB以及可选的SDO。SDO用于回读数据如果你的系统对配置可靠性要求不高也可以只用三线模式但我在实际调试中强烈建议保留SDO因为很多寄存器配置错误可以通过回读快速定位。SPI帧格式值得多说两句。ADI很多DAC的寄存器帧不是普通的8bit地址8bit数据而是采用“R/W标志位6位或7位地址8位数据”的紧凑结构总帧长可能是12bit或16bit。以AD9122为例SPI帧按16bit组织最高位为读写标志1表示读0表示写中间是寄存器地址低8位是数据。同时默认MSB first也就是高位先发。这一点和STM32、MCU上常见的8bit SPI外设不同如果你直接套用HAL库的8bit模式最终配置进去的寄存器地址和值大概率是乱的。上电时序上我一般遵守这样的顺序先给AD9122供电并等待电源稳定再给RESET引脚一个有效的复位脉冲然后通过SPI写入软复位命令和初始化序列最后配置时钟、接口模式和输出使能。DAC前端的高速数字输入可以在初始化完成后再开启避免在寄存器配置过程中DAC处于未定义状态时收到无效数据。2.2 寄存器读写实现写一个可靠的SPI驱动因为AD9122的SPI配置速率不需要很高实际工作在10MHz甚至更低就完全够用我通常直接用GPIO模拟SPI不依赖硬件外设这样可移植性最好。下面这段代码是我惯用的模板你可以直接参考#define AD9122_SCLK_L() gpio_write(SCLK_PIN, 0) #define AD9122_SCLK_H() gpio_write(SCLK_PIN, 1) #define AD9122_CSB_L() gpio_write(CSB_PIN, 0) #define AD9122_CSB_H() gpio_write(CSB_PIN, 1) #define AD9122_SDIO_W(v) gpio_write(SDIO_PIN, v) #define AD9122_SDIO_R() gpio_read(SDIO_PIN) static void spi_delay(void) { // 空转几个周期保证SCLK频率不超过手册上限 for (volatile int i 0; i 8; i); } static void ad9122_spi_frame(uint16_t frame) { AD9122_CSB_L(); for (int i 15; i 0; i--) { AD9122_SCLK_L(); AD9122_SDIO_W((frame i) 0x01); spi_delay(); AD9122_SCLK_H(); spi_delay(); } AD9122_SCLK_L(); AD9122_CSB_H(); } int ad9122_spi_read_reg(uint8_t addr, uint8_t *val) { uint16_t frame (0x8000) | ((uint16_t)(addr 0x7F) 8); uint8_t rdata 0; AD9122_CSB_L(); // 先发地址帧 for (int i 15; i 8; i--) { AD9122_SCLK_L(); AD9122_SDIO_W((frame i) 0x01); spi_delay(); AD9122_SCLK_H(); spi_delay(); } AD9122_SCLK_L(); // 高阻释放总线转为接收模式 gpio_set_dir(SDIO_PIN, GPIO_IN); for (int i 7; i 0; i--) { AD9122_SCLK_H(); spi_delay(); rdata | (AD9122_SDIO_R() i); AD9122_SCLK_L(); spi_delay(); } gpio_set_dir(SDIO_PIN, GPIO_OUT); AD9122_CSB_H(); *val rdata; return 0; }这段代码用到了“先发地址帧再切换SDIO方向读数据”的方式。注意在读取阶段SDIO要切换成输入模式否则会拉死总线。如果你的MCU或者FPGA引脚方向切换不好操作也可以用独立的SDO引脚读寄存器时直接读SDO写寄存器时不理它逻辑上会简单很多。有一点要提醒初始化序列里经常需要重复写很多寄存器我习惯把寄存器表和值定义成结构体数组一行一个顺序执行。不要一边调试一边随手改代码很容易漏项。下面是一个初始化序列的示意写法typedef struct { uint8_t addr; uint8_t val; } reg_cfg_t; const reg_cfg_t ad9122_init_seq[] { {0x00, 0x18}, // 软复位并等待 {0x00, 0x00}, // 退出软复位 // 后续按手册配置时钟、插值、接口模式等 // {0x10, 0x20}, // {0x22, 0x00}, // ... };具体寄存器的地址和位域一定要以官方datasheet寄存器表为准。不同版本芯片或者不同封装个别保留位可能有差异不能照搬网上随意贴出来的配置列表。2.3 初始化流程与关键寄存器配置顺序AD9122的初始化虽然寄存器很多但配置顺序是有逻辑的。我总结的通用流程是芯片上电并完成硬复位或软复位。配置SPI接口本身如SDO使能、MSB/LSB优先级确保回读可用。配置DAC参考时钟与内部PLL/分频器确定DAC采样率。配置数字插值滤波器倍率和数据输入格式二进制补码或偏移二进制。配置输入接口模式LVDS/CMOS、DDR/SDR、双通道/单通道等。配置与DCI时钟相关的延迟或时钟对齐参数。解除DAC输出power-down使能各通道输出。回读关键寄存器确认配置全部生效。为什么要这么严格的顺序因为有些寄存器之间存在依赖关系比如接口模式寄存器可能依赖插值倍率而插值倍率又和数据通路宽度相关。如果先开输出再配时钟DAC可能在一小段时间内输出未知状态进而打坏后级放大器或者干扰其他电路。另外建议每次初始化完成后做一次全面回读。很多老工程师嫌麻烦但AD9122这类芯片寄存器一旦因时序冲突没写进去后面症状非常隐蔽输出没有、杂散大、数据乱序、通道串扰各种现象都有可能出现。回读一遍寄存器可以在十分钟内排除一大批低级错误。2.4 配置阶段最容易踩的三个坑第一个坑是软复位之后没有等待。AD9122的软复位和其他SPI器件一样需要若干个时钟周期才能真正完成如果复位命令刚写入就立即写下一个寄存器间隔太短可能会丢失配置。我通常会在软复位后加上至少1ms延时或者用回读方式确认复位完成。第二个坑是SDO/SDIO引脚复用关系不清。默认情况下SDIO是双向数据脚SDO可以输出回读数据。如果你使用三线模式SDO要悬空或做相应处理如果你使用四线模式SDIO和SDO的角色分工会变化。这个配置不弄对读数据永远是0xFF或者0x00。第三个坑是SPI时钟极性和相位的匹配。AD9122手册里明确给出了SCLK、SDIO和CSB之间的时序关系多数模式是CPOL0、CPHA0空闲低、上升沿采样。但如果你习惯用STM32的硬件SPI默认不一定匹配必须切换CPOL/CPHA。如果不确定先用GPIO模拟SPI最稳妥等系统跑通了再考虑换成硬件SPI或DMA可以省去大量排查时序问题的痛苦。3. LVDS数据接口设计与FPGA侧实现3.1 高速DAC输入接口的信号完整性设计AD9122的LVDS输入接口是整个系统里最容易出物理层问题的地方。首先要保证差分阻抗连续可控走线阻抗按100Ω差分设计FPGA输出到DAC接收端的整条路径上尽量少打过孔避免stub。DAC接收端一般需要就近放置100Ω端接电阻这个电阻的位置非常关键必须靠近DAC引脚否则反射会对时序和噪声产生明显影响。等长约束方面组内差分对的正负信号线长度差通常控制在5~10mil以内同一组数据lane之间尽量做到等长。如果FPGA和DAC距离较远组与组之间的skew可以通过调整IODELAY或DCLK相位来补偿但组内偏斜只能靠布线解决。我踩过一次很深的坑某块板上D0到D7的lane布线走线长度差异超过100mil结果在200MHz DDR速率下数据窗口变得非常窄几乎到了码间的临界状态最后只能降速运行。后来重新布线把等长控制在20mil以内问题立刻消失。电源和地平面也不容忽视。高速LVDS信号参考面必须连续不能跨分割。如果FPGA和DAC分别处在两块不同的地平面区域必须保证参考地平面的连通性否则回流路径会绕远产生共模噪声。很多“原理图没问题但波形惨不忍睹”的案例最后都查到是地平面问题。3.2 FPGA端LVDS/DDR数据发送实现FPGA侧通常只需要关注两件事数据按时序打出去的DCI时钟按正确相位送过去。在Xilinx的7系列FPGA上我习惯用OSERDESE2发送DDR数据用ODDR发送DCI时钟。下面是一个简单的发送逻辑示意// 并行数据进入DDR模式输出 OSERDESE2 #( .DATA_RATE_OQ(DDR), .DATA_RATE_TQ(BUF), .DATA_WIDTH(4), .INIT_OQ(1b0), .SERDES_MODE(MASTER) ) u_oserdes_d0 ( .D1(par_d0[0]), .D2(par_d0[1]), .D3(par_d0[2]), .D4(par_d0[3]), .OCE(1b1), .CLK(serdes_clk), .CLKDIV(par_clk), .OQ(lvds_d0_p), .OFB(), .T(1b0), .T1(1b0), .T2(1b0), .T3(1b0), .T4(1b0), .TBYTEIN(1b0), .TBYTEOUT(), .TCE(1b0), .RST(1b0) );DCI时钟使用ODDR输出让时钟信号在数据眼的中心位置翻转以保证DAC侧建立保持条件。具体相位差值要根据手册中tDS、tDH参数以及PCB走线延时来评估必要时在逻辑里插入可配置的延迟。ODDR #( .DDR_CLK_EDGE(SAME_EDGE), .INIT(1b0), .SRTYPE(SYNC) ) u_oddr_dci ( .Q(dci_p), .C(serdes_clk), .CE(1b1), .D1(1b1), .D2(1b0), .R(1b0), .S(1b0) );这里注意OSERDESE2的DATA_WIDTH选择4意味着每个并行时钟周期输出2bit到DDR线上。CLK频率是par_clk的两倍par_clk就是你DDR数据端口的并行时钟。如果数据位宽是16bit用8对LVDS每对lane对应两bit并行数据那么总共需要8个OSERDESE2实例。3.3 时序分析与约束设计FPGA工程里如果忘了加LVDS接口的时序约束工具大概率会给你“宽松但不可用”的结果跑起来后DAC端时序余量几乎为零。正确做法是给DCI时钟和数据都指定输出延迟约束。举个例子假设DCI时钟频率为100MHzDAC要求数据在时钟边沿前后分别满足tDS0.5ns、tDH0.5nsPCB走线延时差异控制在0.2ns以内那么输出延迟约束可以写成create_generated_clock -name dci_clk \ -source [get_pins u_pll/clkout] \ -divide_by 1 [get_ports {dci_p dci_n}] set_output_delay -clock dci_clk -max 0.9 \ [get_ports {lvds_data_p[*] lvds_data_n[*]}] set_output_delay -clock dci_clk -min 0.1 \ [get_ports {lvds_data_p[*] lvds_data_n[*]}]这里的max、min具体取值要根据DAC手册减去一个安全裕量同时还要考虑PCB走线延时。整体原则是“把预算留给接口”FPGA内部逻辑只要满足时序约束就可以保证数据在DAC端落在有效窗口内。时序跑完后建议在时序报告里重点看输出端口的slack如果裕量不足优先考虑调整输出相位而不是盲目增加逻辑流水。3.4 位对齐、延迟调整与训练序列高速并行接口还有一个隐性难题多根lane之间以及数据与DCI时钟之间可能存在相位偏移。如果DAC端没有自动对齐功能就需要在上电时做一个简单的训练流程FPGA发送一个固定pattern比如0xAAAA或者0xF0F0然后观察DAC输出的模拟波形或回读状态确认每个数据bit都落在了正确的位上。如果发现个别bit错位优先调整对应lane的IODELAY。Xilinx的IODELAY可以按tap级别约78ps精细调节AD9122这类DAC对几百ps的延迟调整非常敏感。也可以考虑调节DCI时钟的相位让整个数据窗口整体移动但这会影响所有lane通常作为粗调手段。我的经验是先用软件工具把DAC输入模式配成“交替校验”或“单通道测试”模式再用逻辑分析仪观测中间信号缩小问题范围。如果条件允许在FPGA内部例化一个ILA监测并行采样结果能直接看出哪一根lane对不上。没有逻辑分析仪时通过DAC输出正弦波判断相位错位会让输出波形出现明显毛刺或高频杂散严重时波形直接乱掉。训练序列配合频谱观察基本能把问题定位到具体lane。4. 系统调试方法与指标测试4.1 从SPI到输出的第一束波形新板子拿回来不要急着跑高速信号。先写一个极小的FPGA工程只做三件事初始化AD9122、发送直流电平码、低速输出方波。用示波器看DAC输出电压是否为正负交替或直流稳定这一步能快速验证SPI配置是否成功、输出通道是否正常。具体做法是先用SPI把AD9122配成单通道、低速率、无插值模式FPGA输出固定的16bit码值比如0x8000对应中间电平0xFFFF对应正满幅0x0000对应负满幅。观察DAC输出电压是否随码值变化如果输出不变先查SPI回读寄存器确认寄存器真的写进去了。再查power-down位很多初期调试“没输出”的问题都是输出使能位没打开。确认直流输出正常后再生成一个低频正弦查找表以1kHz或者10kHz频率输出。这时DAC输出应该是干净的低频正弦。如果正弦波形正常说明从FPGA到DAC的数据通道和DAC模拟输出链路基本打通可以逐步提高速率和插值倍数。4.2 频谱指标怎么看当信号跑起来以后不要只盯着示波器看“有没有波”要上频谱仪看“波形纯不纯”。AD9122的主要指标包括SFDR、谐波失真、杂散抑制、噪声基底等。对调试来说我通常会看三个地方一是基波旁边有没有明显的谐波分量比如HD2、HD3。谐波过大通常和DAC输出幅度、变压器匹配或者输出偏置有关也可能和DAC内部未校正电路有关需要排除。二是指定带宽内有没有非谐波相关的杂散尖峰。这些尖峰往往来自时钟串扰、电源噪声或者数字数据串扰可能需要检查时钟走线隔离、电源去耦是否到位。三是整个输出频谱的噪声基底是否平稳。如果基底出现异常隆起多半是数据源噪声或时钟抖动过大这时候就要优先优化参考时钟的质量而不是继续折腾DAC配置。插值滤波器有没有生效通过频谱也可以清楚看到。使用4倍插值后DAC输出频谱中靠近高频段的镜像分量会被推到原来4倍以外的位置如果在F_clk/4附近还能看到镜像能量说明插值配置可能不对或者输出重建滤波器带宽设置不合理。4.3 数据错位、毛刺与眼图劣化的现场排查调试中我遇到过最典型的问题输出正弦波在示波器上看起来大方向是对的但叠加了随机毛刺频谱上杂散明显增多。排查思路一般从“降低接口速率”开始。把DAC更新率和插值倍数降下来如果毛刺消失说明问题大概率来自数据时序或者信号完整性如果毛刺依旧则重点怀疑DAC配置或模拟输出链路。时序导致的毛刺有一个典型特征毛刺位置相对数据变化点固定和数据pattern强相关。你可以先把数据设置成全1或全0交替观察毛刺是否消失。数据交替时如果出现尖刺基本就是数据建立时间不足或lane间skew过大。这时候去检查眼图如果没有高速示波器也可以通过调节DCI相位扫描看毛刺的变化找到稳定的相位窗口。还有一种隐蔽情况明明LVDS布线没问题SPI寄存器也写进去了但输出仍然乱码。这时候要考虑DAC接收端的数据格式配置比如二进制补码和偏移二进制的差别。如果FPGA发送的是补码数据而DAC被配置成偏移二进制接收整条波形会直接反转或整体移位看起来像是“数据错位”。4.4 常见问题速查表现象可能原因处理方向DAC完全无输出供电/复位/输出使能位未配置检查电源、复位时序、回读寄存器输出波形整体相位反向数据格式补码/偏移二进制不匹配调整寄存器或FPGA数据格式输出正弦有规律毛刺lane间skew过大、DCI相位不对降低速率、调节IODELAY、检查等长杂散尖峰明显时钟串扰、电源噪声检查时钟隔离、去耦电容、参考时钟高频时输出幅度严重跌落插值倍率或重建滤波器不匹配核对插值配置与模拟滤波器带宽SPI回读全为0xFFSDO/片选/时钟极性错误检查SPI模式、SDO上下拉、接线这张表不能替代完整调试但基本覆盖了我调试过程中的高频问题适合打印出来贴在工位旁边。5. 工程实践中的几个重要提醒5.1 电源噪声对高速DAC的隐性影响AD9122内部既有数字电路、又有模拟电路还有PLL电源引脚非常敏感。如果给DAC供电的LDO或DC-DC噪声太大输出频谱上会直接体现出杂散和噪声基底恶化。我在一块板上遇到过“输出波形正常、SFDR很差”的问题查了很久才发现是附近一个DC-DC的开关频率耦合进了DAC电源走线。后来在DAC电源引脚前增加LC滤波并调整了PCB布局指标才恢复正常。建议从布局阶段就给DAC电源区域“画圈”数字电源和模拟电源严格分区去耦电容尽量靠近引脚地孔就近打过。5.2 时钟是整个系统的灵魂很多高速DAC的问题根源并不在DAC本身而在参考时钟。AD9122的时钟抖动会直接变成输出相位噪声进而影响SFDR和EVM。我见过有人为了省成本用FPGA内部PLL输出的时钟当DAC参考时钟结果频谱指标始终不过。参考时钟的抖动性能非常重要至少在几十飞秒量级才能保证好的性能所以外部时钟源选型要谨慎不能省。5.3 设计清单与上手建议如果你是从零开始做AD9122方案我建议按下面的顺序推进先花半天时间读懂datasheet里的SPI寄存器表和LVDS时序参数。画板前把差分对的阻抗、等长、端接方式全部确认好。FPGA工程分两步先只写SPI初始化验证寄存器读写再加LVDS数据发送从低速开始逐步提速。调试过程中始终用“回读确认寄存器”作为前提寄存器没确认之前不要去碰数据通路。遇到奇怪的模拟现象先怀疑时钟和电源再怀疑数据时序最后才怀疑芯片本身。根据我个人的体会AD9122这种高速DAC其实调通不难难的是在每个环节都保留足够的设计余量。SPI低速就稳扎稳打LVDS高速就认真做好等长和端接时钟和电源舍得下本钱。这个项目做完之后有一个习惯我一直留着每次布线前先把差分对约束建好再跑等长而不是等走完线再去补。省下来的时间足够你把频谱指标再测三轮。
返回列表