ARTICLE DETAIL

资讯详情

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

AD9910 SPI寄存器配置实战:从官方软件到Verilog代码的完整指南

AD9910 SPI寄存器配置实战:从官方软件到Verilog代码的完整指南 1. 为什么AD9910的SPI配置值得单独拿出来讲AD9910这颗芯片玩过DDS直接数字频率合成的人应该都不陌生。1GSPS的采样率、14位DAC、内置PLL、支持多种调制模式放在射频信号生成、雷达模拟、通信测试这些场景里性价比相当能打。但很多人在第一块板子打样回来之后卡住的地方往往不是DAC输出质量也不是时钟树设计而是SPI寄存器配置这一步。我见过太多这样的情况硬件焊接没问题电源纹波也测了参考时钟也锁了但芯片就是不出信号或者输出频率跟预期对不上。最后一查要么是寄存器地址写错了要么是SPI时序模式不对要么是写入顺序有问题。AD9910的寄存器映射表有几十个寄存器每个寄存器的位定义又各不相同光靠翻数据手册一页一页对效率低不说还容易漏掉关键位。所以这篇内容的核心目标很明确把AD9910的SPI寄存器配置这件事从官方软件的使用到Verilog代码的落地完整地串一遍。不管你是刚拿到AD9910评估板的新手还是已经在FPGA上调了一段时间但还有些细节没搞明白的工程师都能从中找到可以直接用的东西。先说一下整体思路。AD9910的SPI配置本质上就是通过串行接口往芯片内部的寄存器写数据。SPI本身不复杂四根线SCLK、SDIO、CS、SDO。但AD9910的SPI有一些自己的特点比如支持单字节和多字节传输、支持指令模式、寄存器地址和数据的组织方式也有特定格式。如果你用官方软件比如ADI的评估软件配置过会发现它生成的是一个寄存器配置列表你需要把这个列表翻译成Verilog代码里的状态机或者查找表。我个人的习惯是先用官方软件把寄存器值全部算好、验证好导出配置然后在FPGA里用一个ROM或者参数数组存起来上电之后按顺序写进去。这样做的好处是寄存器值的计算交给官方软件FPGA只负责时序和传输分工明确出问题也容易定位。下面我会从寄存器配置的核心逻辑开始逐步展开到Verilog实现中间会穿插一些我实际调试中踩过的坑和总结的技巧。2. AD9910寄存器配置的核心逻辑与官方软件使用2.1 寄存器映射的整体结构AD9910的内部寄存器分为几个大类控制寄存器CFR1、CFR2、CFR3、频率调谐字FTW、相位偏移字POW、幅度缩放因子ASF、以及各种Profile寄存器。每个寄存器的地址是7位数据宽度从8位到32位不等。CFR1是最关键的控制寄存器之一它决定了芯片的工作模式。比如 bit 1 是SDIO输入输出方向控制bit 3 是LSB优先还是MSB优先bit 8 是使能反sinc滤波器bit 9 是使能内部PLLbit 10 是PLL倍频比的高位等等。这些位如果配错芯片要么不工作要么输出频谱跟预期完全不一样。CFR2主要管一些辅助功能比如使能AM/FM调制、使能数字斜坡、选择扫频模式等。CFR3则涉及DAC的满量程电流、内部时钟配置等。FTW是频率调谐字32位直接决定输出频率。计算公式是FTW (f_out / f_sysclk) × 2^32。这里f_sysclk是系统时钟可能是外部参考时钟经过PLL倍频之后的频率。比如你用25MHz参考时钟PLL倍频到1000MHz想输出100MHz那FTW (100e6 / 1000e6) × 2^32 0.1 × 4294967296 ≈ 429496730转换成十六进制就是0x1999999A。POW是相位偏移字14位用于调整输出信号的初始相位。ASF是幅度缩放因子也是14位用于控制输出幅度。这两个寄存器在需要精确控制相位和幅度的场景下会用到。Profile寄存器Profile0到Profile7是AD9910的一个特色功能每个Profile可以存储一组FTW、POW、ASF和扫频参数通过外部引脚或者SPI切换。这在需要快速切换频率的场景下非常有用比如跳频通信。2.2 官方软件的使用要点ADI官方提供了一个评估软件可以直接在Windows上运行通过USB连接评估板来配置AD9910。这个软件的好处是界面直观寄存器值可以实时计算和写入还能导出配置文件。我一般的使用流程是这样的先打开软件连接评估板然后在界面上设置参考时钟频率、PLL倍频比、输出频率、幅度等参数。软件会自动计算出对应的寄存器值并在寄存器列表里显示出来。这时候不要急着点写入先检查几个关键点。第一确认系统时钟。软件里有一个System Clock的显示这个值必须跟你实际硬件上的时钟一致。如果你用的是25MHz参考时钟PLL倍频40倍那系统时钟就是1000MHz。如果这里设错了后面所有频率计算都是错的。第二检查CFR1的配置。特别是bit 9PLL使能和bit 10-11PLL倍频比。PLL倍频比的编码方式在数据手册里有详细说明比如倍频比40对应的编码是0x27这个不能搞错。第三确认SPI传输模式。AD9910支持MSB优先和LSB优先默认是MSB优先。如果你在FPGA里用的是LSB优先那CFR1的bit 3就要相应设置。第四导出配置文件。软件一般支持导出为文本或者CSV格式里面包含了所有寄存器的地址和值。这个文件就是你后面写Verilog代码的依据。注意官方软件生成的寄存器值是基于你输入的参数计算出来的但有些寄存器的默认值可能并不适合你的应用场景。比如CFR2里的数字斜坡使能位如果你不用扫频功能最好确认它是关闭的否则可能会影响输出。2.3 寄存器写入顺序的重要性AD9910的寄存器写入是有顺序要求的。一般来说先写CFR1、CFR2、CFR3这些控制寄存器再写FTW、POW、ASF这些参数寄存器最后写Profile寄存器。如果顺序反了可能会出现参数不生效的情况。我遇到过一个问题先写了FTW再写CFR1使能PLL结果输出频率不对。后来查了半天才发现PLL使能之后系统时钟变了之前写的FTW是基于旧时钟计算的自然不对。正确的做法是先配置好时钟相关的寄存器等PLL锁定之后再写频率参数。另外AD9910有一个IO_UPDATE引脚寄存器写入之后需要给这个引脚一个脉冲芯片才会把新的寄存器值加载到内部工作寄存器。这个脉冲的宽度和时序也有要求一般至少一个系统时钟周期。在Verilog里我通常会在SPI传输完成之后延时几个时钟周期然后产生一个IO_UPDATE脉冲。3. Verilog实现SPI寄存器配置的完整方案3.1 SPI接口的时序设计AD9910的SPI接口支持最高70MHz的SCLK频率但实际用的时候没必要跑那么快。我一般用10MHz到25MHz这样时序余量足布线也容易。SPI的时序模式AD9910支持CPOL0/CPHA0和CPOL1/CPHA1两种。我习惯用CPOL0/CPHA0也就是空闲时SCLK为低数据在SCLK上升沿采样。这个模式在Verilog里实现起来最直观。AD9910的SPI传输格式是这样的先发一个字节的指令包含读写位和寄存器地址然后是数据。指令字节的bit 7是读写标志1表示读0表示写。bit 6-0是寄存器地址。数据可以是1到4个字节具体取决于寄存器的宽度。比如要写CFR1地址0x00CFR1是32位所以需要发4个字节的数据。传输顺序是先发指令字节0x00写操作地址0x00然后发4个字节的数据MSB优先。在Verilog里我通常用一个状态机来实现这个流程。状态机的状态包括IDLE、SEND_INSTR、SEND_DATA、WAIT、UPDATE。IDLE状态等待启动信号SEND_INSTR状态发送指令字节SEND_DATA状态发送数据字节WAIT状态等待传输完成UPDATE状态产生IO_UPDATE脉冲。// SPI状态机核心片段 localparam IDLE 3d0; localparam SEND_INSTR 3d1; localparam SEND_DATA 3d2; localparam WAIT 3d3; localparam UPDATE 3d4; reg [2:0] state; reg [7:0] instr_byte; reg [31:0] data_word; reg [4:0] bit_cnt; reg [2:0] byte_cnt; always (posedge clk or posedge rst) begin if (rst) begin state IDLE; sclk 0; cs_n 1; sdio 0; end else begin case (state) IDLE: begin if (start) begin instr_byte {1b0, reg_addr}; data_word reg_data; byte_cnt data_len; state SEND_INSTR; end end SEND_INSTR: begin cs_n 0; // 发送8位指令 if (bit_cnt 8) begin sclk ~sclk; if (sclk 0) begin sdio instr_byte[7 - bit_cnt]; bit_cnt bit_cnt 1; end end else begin bit_cnt 0; state SEND_DATA; end end // ... 数据发送和后续状态 endcase end end上面这段代码是状态机的骨架实际用的时候还需要处理数据字节的发送、字节计数、以及IO_UPDATE的时序。关键点是SCLK的生成我一般用计数器分频来产生这样频率精确可控。3.2 寄存器配置数据的存储方式寄存器配置数据在Verilog里怎么存这个有很多种做法。最简单的是用参数数组把地址和数据一一对应存起来。比如// 寄存器配置表 localparam REG_CNT 20; reg [6:0] reg_addr_table [0:REG_CNT-1]; reg [31:0] reg_data_table [0:REG_CNT-1]; initial begin reg_addr_table[0] 7h00; reg_data_table[0] 32h00000000; // CFR1 reg_addr_table[1] 7h01; reg_data_table[1] 32h00000000; // CFR2 // ... 其他寄存器 end这种方式的优点是直观改起来方便。缺点是如果寄存器数量多initial块会很长。另一种方式是用ROM把配置数据存在一个ROM里上电之后顺序读取。ROM可以用FPGA的Block RAM实现也可以用分布式RAM。我一般用参数数组的方式因为AD9910的寄存器数量不算太多二十几个用数组完全够用。而且参数数组在综合的时候会被优化成常量不占额外的逻辑资源。需要注意的是有些寄存器的值是需要根据实际应用动态计算的比如FTW。这种情况下我会把FTW的计算放在一个单独的模块里用硬件乘法器或者查找表来实现。如果对频率精度要求不高也可以用移位和加法来近似。3.3 IO_UPDATE脉冲的时序控制IO_UPDATE是AD9910的一个关键引脚寄存器写入之后必须给这个引脚一个脉冲芯片才会把新的值加载到内部。这个脉冲的宽度要求是至少一个系统时钟周期但实际上我一般会给到10个时钟周期以上确保稳定。在Verilog里IO_UPDATE的产生可以放在SPI状态机的UPDATE状态。当所有寄存器都写完之后进入UPDATE状态拉高IO_UPDATE计数一段时间然后拉低回到IDLE。UPDATE: begin io_update 1; if (update_cnt 10) begin update_cnt update_cnt 1; end else begin io_update 0; update_cnt 0; state IDLE; end end这里有一个细节IO_UPDATE的脉冲应该是在CS拉高之后产生还是在CS拉高之前根据我的实测CS拉高之后产生IO_UPDATE更稳妥。因为CS拉高表示SPI传输结束这时候寄存器值已经稳定写入再给IO_UPDATE脉冲芯片就能正确加载。另外如果你需要多次更新寄存器比如先写频率再写幅度那每次写完都需要给一个IO_UPDATE脉冲。但要注意IO_UPDATE的频率不能太高两次之间至少间隔一个系统时钟周期。4. 常见问题与排查技巧实录4.1 SPI通信不生效的排查思路SPI通信不生效是调试AD9910时最常见的问题。现象一般是FPGA这边显示SPI传输完成了但AD9910没有任何反应输出信号不变或者根本没有输出。排查的时候我一般按以下顺序来第一步用示波器或者逻辑分析仪抓SPI波形。重点看四根线CS、SCLK、SDIO、SDO。CS是否在传输期间拉低SCLK是否有正确的时钟脉冲SDIO上的数据是否跟预期一致SDO上是否有芯片返回的数据第二步检查SPI模式。AD9910支持CPOL0/CPHA0和CPOL1/CPHA1如果你FPGA里用的是CPOL0/CPHA1那数据采样边沿就错了芯片收到的数据全是乱的。这个错误很隐蔽因为波形看起来很正常但数据就是不对。第三步检查指令字节的格式。AD9910的指令字节是bit 7为读写标志bit 6-0为地址。如果你把读写标志和地址搞反了比如把写操作当成读操作那芯片就不会写入数据。第四步检查IO_UPDATE。如果SPI传输没问题但IO_UPDATE没有产生或者时序不对寄存器值也不会生效。用示波器看一下IO_UPDATE引脚确认在SPI传输完成后有脉冲。第五步检查电源和参考时钟。AD9910的电源有多个引脚包括1.8V和3.3V任何一个电源不正常都会导致芯片不工作。参考时钟也要确认如果没有时钟输入芯片内部PLL不会锁定SPI配置也无法生效。下面这个表格是我整理的一些典型现象和对应的排查方向现象可能原因排查方法SPI波形正常但无输出IO_UPDATE未产生检查IO_UPDATE引脚波形输出频率与预期不符FTW计算错误或系统时钟不对核对FTW计算公式和PLL配置输出幅度异常ASF寄存器配置错误检查ASF值和DAC满量程电流设置SPI数据写入后读出为0SDIO方向配置错误检查CFR1的bit 1芯片发热严重电源接反或过压立即断电检查电源引脚4.2 寄存器值计算错误的典型场景寄存器值计算错误是另一个高频问题。我见过最多的就是FTW算错。FTW的计算公式是FTW (f_out / f_sysclk) × 2^32这里f_sysclk是系统时钟不是参考时钟。如果你用25MHz参考时钟PLL倍频40倍那f_sysclk 1000MHz。如果你误把f_sysclk当成25MHz来算那FTW就会大40倍输出频率也会大40倍。另一个容易错的是PLL倍频比的编码。AD9910的PLL倍频比不是直接写数值而是写一个编码。比如倍频比40对应的编码是0x27倍频比20对应的编码是0x13。这个编码表在数据手册里有但很多人会忽略直接写40结果PLL根本不锁定。还有一个是CFR1的bit 8反sinc滤波器使能位。如果你使能了反sinc滤波器DAC的输出幅度会随频率变化这在某些应用里是需要的但在另一些应用里会导致幅度不平坦。我一般默认关闭这个功能除非确实需要。4.3 实操心得与避坑技巧第一个心得先用官方软件把寄存器值全部算好导出配置文件然后在Verilog里直接引用。不要试图手动计算每个寄存器的值那样效率低而且容易出错。第二个心得SPI的SCLK频率不要设太高。虽然AD9910支持70MHz但实际用的时候10MHz到25MHz足够了。频率低一点时序余量大布线也容易调试的时候波形也好看。第三个心得IO_UPDATE的脉冲宽度给足。我一般给10个系统时钟周期以上确保芯片能可靠加载。有些资料说一个周期就够但实际测试下来宽一点更稳。第四个心得如果条件允许在FPGA里加一个SPI回读功能。AD9910支持读寄存器你可以写完之后再读回来对比一下是否一致。这个功能在调试的时候非常有用能快速定位是写入问题还是芯片问题。第五个心得注意PCB布局。SPI的信号线尽量短远离时钟线和电源线。如果SPI线太长容易引入噪声导致通信不稳定。我遇到过一块板子SPI线走了10厘米结果通信成功率只有50%后来缩短到3厘米问题就解决了。第六个心得AD9910的散热要注意。这颗芯片功耗不小特别是DAC满量程输出的时候。如果芯片温度过高可能会影响输出性能。我一般会在芯片下面加散热焊盘或者加一个小风扇。5. 从配置到验证的完整调试流程5.1 上电初始化的步骤AD9910的上电初始化流程我一般按以下步骤来第一步上电之后先等待一段时间让电源稳定。一般等100ms左右。第二步配置CFR1。设置PLL使能、倍频比、SDIO方向、LSB/MSB优先等。这一步决定了芯片的基本工作模式。第三步配置CFR2和CFR3。设置DAC满量程电流、内部时钟配置等。第四步等待PLL锁定。AD9910有一个PLL_LOCK引脚可以监测这个引脚来判断PLL是否锁定。如果没有这个引脚也可以等一段时间一般1ms左右。第五步配置FTW、POW、ASF。这些是输出参数根据你的应用需求来设置。第六步配置Profile寄存器。如果你用到了Profile功能把对应的参数写进去。第七步产生IO_UPDATE脉冲加载所有寄存器。第八步验证输出。用频谱仪或者示波器看输出信号确认频率、幅度、相位是否符合预期。5.2 用回读功能验证寄存器写入AD9910支持SPI回读这个功能在调试的时候非常有用。回读的流程是先发指令字节读写标志设为1地址设为要读的寄存器地址然后芯片会在SDO上返回数据。在Verilog里实现回读需要在SPI状态机里增加一个读状态。读状态的时候SDIO变成输入SDO变成输出FPGA采样SDO上的数据。// 回读状态机片段 localparam READ_DATA 3d5; READ_DATA: begin // 发送读指令后采样SDO if (bit_cnt 8) begin sclk ~sclk; if (sclk 1) begin read_data[7 - bit_cnt] sdo; bit_cnt bit_cnt 1; end end else begin bit_cnt 0; state IDLE; end end回读的时候要注意SDO是芯片的输出FPGA这边要把它设为输入。另外回读的数据是MSB优先还是LSB优先取决于CFR1的配置。我一般会在上电初始化完成之后把所有关键寄存器回读一遍跟写入的值对比。如果一致说明SPI通信没问题如果不一致就要检查SPI时序或者寄存器地址。5.3 输出信号的验证方法输出信号的验证最直接的方法是用频谱仪看频谱。如果你没有频谱仪也可以用示波器看时域波形但示波器的带宽要足够至少是被测信号频率的3倍以上。验证的时候我一般会设置几个测试点单频输出、频率扫描、幅度调制。单频输出是最基本的确认频率和幅度正确。频率扫描可以验证Profile功能和数字斜坡。幅度调制可以验证ASF寄存器和AM功能。如果输出信号不对比如频率偏差很大那就要回头检查FTW和系统时钟。如果幅度不对检查ASF和DAC满量程电流。如果频谱上有杂散可能是参考时钟不干净或者电源纹波太大。提示AD9910的输出频谱对参考时钟的质量非常敏感。如果参考时钟的相位噪声大输出信号的相位噪声也会大。建议用低相噪的晶振或者时钟发生器。5.4 性能优化的几个方向如果你对输出信号的质量有更高要求可以从以下几个方面优化第一参考时钟。用低相噪的OCXO或者TCXO可以显著改善输出信号的相位噪声。第二电源滤波。AD9910的电源引脚上要加足够的去耦电容特别是高频电容。我一般会在每个电源引脚旁边放一个100nF和一个10uF的电容。第三DAC输出滤波。AD9910的DAC输出是电流型需要外接一个运放或者变压器来转换成电压。输出滤波器要根据你的频率范围来设计截止频率一般设为最高输出频率的1.5倍左右。第四PCB布局。DAC输出走线要短尽量远离数字信号线。地平面要完整不要被分割。第五散热。如果芯片温度过高输出性能会下降。加散热片或者风扇把芯片温度控制在60度以下。6. 一些额外的经验分享AD9910这颗芯片我前前后后用了大概五六年从最初的评估板到后来的自制板踩过的坑不少。有一些经验是数据手册上不会写的但实际用的时候很关键。比如AD9910的SPI接口在CS拉高之后需要等待至少一个SCLK周期才能开始下一次传输。如果你连续传输中间没有足够的间隔芯片可能会丢失数据。我一般会在两次传输之间加10个时钟周期的延时。再比如AD9910的Profile切换如果用SPI来切换需要写Profile寄存器然后给IO_UPDATE脉冲。但如果用外部引脚来切换响应速度会快很多。在跳频应用里我一般用外部引脚切换SPI只负责初始化配置。还有AD9910的DAC输出是差分电流输出如果你只用单端需要接一个50欧姆的电阻到地把电流转换成电压。但这样会损失一半的幅度。如果对幅度要求高最好用变压器或者差分运放。最后说一个关于Verilog代码风格的事。SPI状态机我建议用三段式或者两段式来写不要用一段式。三段式的优点是时序清晰综合出来的电路也稳定。另外状态机的状态编码用独热码还是二进制码这个看你的资源情况。独热码速度快但占资源多二进制码省资源但速度慢。AD9910的SPI速率不高用二进制码完全够用。如果你在调试过程中遇到了其他问题或者有更好的实现方案欢迎一起交流。AD9910这颗芯片虽然出来很多年了但在很多场景下依然是个可靠的选择把SPI配置这一块吃透后面的事情就顺了。
返回列表