简介:本资源是一份面向FPGA工程师的AD9680高速ADC芯片实战开发指南,聚焦Vivado平台下Verilog实现1G采样率、4通道(Lane4)JESD204B接收的核心技术难点,适用于具备数字电路与FPGA基础、正开展高速数据采集系统调试的研发人员。内容覆盖AD9680寄存器初始化配置、PLL时钟生成与相位对齐、JESD204B链路同步及状态机接收设计三大关键模块,并附有可复用的Verilog代码片段、SPI配置时序要点、链路失锁排错思路等一线调试经验。资源为单文件PDF文档(111KB),结构清晰,含完整代码示例、信号时序说明与配置参数注释,便于快速查阅与工程移植。目前已有135人学习下载,是理解JESD204B协议在Xilinx FPGA中落地实现的高价值参考材料。
1. 这不是“能跑就行”的Demo:AD9680在1Gsps+Lane4下JESD204B链路一旦失锁,FPGA里抓到的就是一串不可逆的乱码
你手头刚焊好一块AD9680+Kintex-7/KU040的采集板,信号源接上,Vivado综合布线全过,ILA也连上了——但JESD204B的rx_sync信号死活不拉高,rx_valid永远为低,或者更糟:它偶尔跳变,数据流里夹着大量0x00000000和0xFFFFFFFF。这不是仿真没过,是物理层已经崩了。这套基于Vivado的AD9680测试程序,核心价值从来不是“点亮LED”,而是帮你把1G采样率、4 Lane、5Gbps线速率这条高速数据通路,从时钟树根、SPI配置时序、sysref相位对齐、字符锁定、多帧同步,到最终数据拼接,全部钉死在可复现、可验证、可量产的工程基线上。它面向的是正在调试真实高速采集系统(比如雷达回波采样、光通信前端、超声波波束合成)的FPGA工程师,不是Verilog新手入门课。代码全是Verilog原生实现,不依赖黑盒IP封装,每个状态机跳转、每个delay tap调整、每个ILA触发条件,都对应板级实测中踩过的坑。你照着跑通,拿到的不是“Hello World”,是能直接进FFT模块做ENOB分析、进AXI Stream接DDR3缓存、甚至挂上PCIe Gen3做实时回传的原始数据流。
2. AD9680寄存器配置:SPI时序不是“发完就完”,而是要轮询、回读、带延迟补偿的状态机闭环
AD9680的配置不是一次性写入10个寄存器就完事。它的SPI接口对建立/保持时间极其敏感,且上电后存在内部PLL锁定延迟;寄存器写入后必须等待足够时间才能生效,部分关键寄存器(如0x16 Lane配置、0x14采样模式)还要求写入顺序和回读校验。用Xilinx SPI IP核?在1G系统里容易翻车——IP核抽象层会引入不可控的时序抖动,且无法精细控制CS#脉冲宽度和CLK空闲电平。我们采用纯Verilog状态机实现,全程可控、可ILA抓取、可加delay tap微调。
2.1 配置状态机设计:四阶段闭环,含上电延时与回读校验
整个配置流程分为四个阶段:IDLE → POWER_UP_DELAY → CONFIG_WRITE → CONFIG_READBACK。重点在于POWER_UP_DELAY——AD9680手册明确要求上电后至少等待500μs才能发起首次SPI操作。这个延迟不能靠外部RC电路,必须由FPGA内部计数器实现,否则冷启动必失败。
// ad9680_spi_controller.v module ad9680_spi_controller ( input wire clk_25m, // 25MHz基准时钟(稳定度优于ADC参考时钟) input wire rst_n, output reg [7:0] spi_mosi, output reg spi_sclk, output reg spi_cs_n, input wire [7:0] spi_miso, output reg config_done, output reg config_error ); // 状态定义 typedef enum logic [2:0] { IDLE, POWER_UP_DELAY, CONFIG_WRITE, CONFIG_READBACK, ERROR } spi_state_t; spi_state_t state, next_state; reg [19:0] delay_cnt; // 25MHz下500us = 25e6 * 500e-6 = 12500计数 reg [3:0] write_idx; reg [7:0] reg_addr; reg [7:0] reg_data; reg [7:0] readback_data; // 寄存器配置表(精简版,实际项目含22个寄存器) localparam REG_CFG[21:0] = '{ {8'h01, 8'h03}, // 0x01: 模式寄存器,使能JESD204B {8'h14, 8'h10}, // 0x14: 采样率模式,0x10=1Gsps {8'h15, 8'hA3}, // 0x15: JESD参数,bit5-3=3'b100→Lane4,bit2-0=3'b011→分频比3→5Gbps线速 {8'h16, 8'h04}, // 0x16: Lane使能,bit2=1→Lane0-3全开 {8'h17, 8'h01}, // 0x17: SYSREF模式,0x01=脉冲模式 {8'h18, 8'h00}, // 0x18: 帧时钟分频,0x00=1:1 {8'h19, 8'h00}, // 0x19: 多帧时钟分频 {8'h1A, 8'h01}, // 0x1A: 加扰使能 {8'h1B, 8'h00}, // 0x1B: 测试模式禁用 {8'h1C, 8'h00}, // 0x1C: 通道增益 {8'h1D, 8'h00}, // 0x1D: 通道偏置 {8'h1E, 8'h00}, // 0x1E: 通道校准 {8'h1F, 8'h00}, // 0x1F: 全局校准 {8'h20, 8'h00}, // 0x20: 时钟分频 {8'h21, 8'h00}, // 0x21: 参考时钟选择 {8'h22, 8'h00}, // 0x22: PLL配置 {8'h23, 8'h00}, // 0x23: PLL锁定检测 {8'h24, 8'h00}, // 0x24: ADC时钟分频 {8'h25, 8'h00}, // 0x25: 数据格式 {8'h26, 8'h00}, // 0x26: 输出驱动强度 {8'h27, 8'h00}, // 0x27: 掉电控制 {8'h28, 8'h00} // 0x28: 软件复位 }; always @(posedge clk_25m or negedge rst_n) begin if (!rst_n) begin state <= IDLE; delay_cnt <= 0; write_idx <= 0; config_done <= 0; config_error <= 0; end else begin state <= next_state; case (state) IDLE: begin next_state <= POWER_UP_DELAY; delay_cnt <= 0; end POWER_UP_DELAY: begin if (delay_cnt == 12500) begin next_state <= CONFIG_WRITE; write_idx <= 0; end else begin delay_cnt <= delay_cnt + 1; next_state <= POWER_UP_DELAY; end end CONFIG_WRITE: begin if (write_idx < 22) begin reg_addr = REG_CFG[write_idx][15:8]; reg_data = REG_CFG[write_idx][7:0]; // 启动SPI写操作(细节见2.2节) next_state <= CONFIG_WRITE; write_idx <= write_idx + 1; end else begin next_state <= CONFIG_READBACK; write_idx <= 0; end end CONFIG_READBACK: begin if (write_idx < 22) begin // 发送读命令,读取REG_CFG[write_idx][15:8]地址 // 比较读回值与REG_CFG[write_idx][7:0] if (readback_data != REG_CFG[write_idx][7:0]) begin next_state <= ERROR; config_error <= 1; end else begin write_idx <= write_idx + 1; next_state <= CONFIG_READBACK; end end else begin next_state <= IDLE; config_done <= 1; end end ERROR: begin config_error <= 1; next_state <= IDLE; end endcase end end这段代码的核心逻辑是:所有配置必须闭环。POWER_UP_DELAY确保硬件上电稳定;CONFIG_WRITE按严格顺序写入;CONFIG_READBACK逐个回读校验——哪怕只错一个bit(比如0x15寄存器bit5-3本该是3'b100却读成3'b000),立刻报config_error并停机。这是防止“以为配好了,其实ADC还在默认模式瞎发数据”的后悔药。
2.2 SPI物理层时序控制:空闲低电平、CS#脉冲宽度、采样边沿全可控
AD9680的SPI要求CLK空闲为低电平(CPOL=0),数据在CLK上升沿采样(CPHA=0),且CS#脉冲宽度必须≥50ns。这些参数在状态机里硬编码:
// 在CONFIG_WRITE状态下,每写一个寄存器执行: // 步骤1:拉低CS# always @(posedge clk_25m) begin if (state == CONFIG_WRITE && write_idx == current_idx) begin spi_cs_n <= 0; spi_sclk <= 0; spi_mosi <= {1'b0, reg_addr}; // 地址字节,MSB first bit_cnt <= 0; sclk_phase <= 0; end end // 步骤2:生成8个SCLK周期发送地址 always @(posedge clk_25m) begin if (state == CONFIG_WRITE && write_idx == current_idx) begin if (bit_cnt < 8) begin if (sclk_phase == 0) begin spi_sclk <= 1; sclk_phase <= 1; end else begin spi_sclk <= 0; sclk_phase <= 0; bit_cnt <= bit_cnt + 1; if (bit_cnt == 7) begin spi_mosi <= reg_data; // 切换到数据字节 end else begin spi_mosi <= {spi_mosi[6:0], 1'b0}; end end end else if (bit_cnt < 16) begin // 同样逻辑发送数据字节 end else begin // 拉高CS#,等待tCYC=100ns spi_cs_n <= 1; end end end提示:
spi_sclk由25MHz时钟分频生成,实际SPI CLK频率为10MHz(满足AD9680最大30MHz要求)。关键点在于spi_cs_n拉高后,必须等待至少100ns(即1个25MHz周期)再进入下一个寄存器写入,否则ADC内部状态机可能未完成更新。
2.3 避坑:AD9680配置的5个血泪经验
现象:
config_done信号拉高,但JESD链路始终不sync。
原因:寄存器0x15(JESD参数)写入错误。8'hA3中bit5-3控制Lane数,必须为3'b100(Lane4),若误写为3'b000(Lane0),ADC会静默。
解决:在CONFIG_READBACK阶段强制打印ILA中reg_addr==8'h15时的readback_data,确认为0xA3。实测发现,用Verilog常量写比用parameter更不易出错。现象:上电后第一次配置成功,断电重上电失败率50%。
原因:POWER_UP_DELAY计数器未复位。冷启动时FPGA全局复位信号rst_n释放早于ADC供电稳定,导致计数器提前溢出。
解决:增加外部电源监控信号adc_pwr_ok作为delay_cnt使能条件,仅当adc_pwr_ok==1时计数器才工作。现象:ILA抓到SPI MOSI波形正确,但ADC无输出。
原因:SPI MISO未接或上拉电阻缺失。AD9680在写入后需返回ACK,若MISO悬空,内部状态机卡死。
解决:PCB设计时MISO必须接4.7kΩ上拉至ADC的IOVDD(1.8V),并在XDC中约束set_property IOSTANDARD LVDS_25 [get_ports spi_miso]。现象:配置完成后,JESD链路能sync但数据错位(如I/Q通道互换)。
原因:寄存器0x16(Lane使能)写入顺序错误。必须先写0x16使能Lane,再写0x15设置参数,反之则参数不生效。
解决:严格按REG_CFG数组索引顺序写入,write_idx递增不可跳变。现象:环境温度升高(>60℃)后,配置成功率下降。
原因:SPI时序余量不足。高温下FPGA IO延迟增大,CS#脉冲宽度临界。
解决:在XDC中添加set_output_delay -max 2.0 -clock [get_clocks clk_25m] [get_ports {spi_cs_n spi_sclk spi_mosi}],强制工具留足2ns余量。
3. 时钟架构:MMCM不是“填参数就完事”,而是要拆解为三路独立时钟+异步约束的精密系统
AD9680的1G采样率要求FPGA提供两路严格关联又物理隔离的时钟:一路是312.5MHz的Device Clock(供给ADC采样),另一路是625MHz的JESD204B参考时钟(供给GT收发器)。这两路时钟必须同源(避免亚稳态),但又不能让时序分析器把它们当成同步时钟处理(否则set_max_delay约束失效)。用一个MMCM生成两路时钟是标准做法,但参数设置、相位微调、约束写法,每一步都是玄学。
3.1 MMCM参数计算:从输入时钟推导VCO频率与输出分频
假设开发板输入时钟为100MHz(常见),目标输出:
adc_clk: 312.5MHz(Device Clock,驱动ADC采样)gt_refclk: 625MHz(JESD参考时钟,GT收发器要求)
计算过程:
- VCO频率 =
adc_clk×CLKFBOUT_MULT_F= 312.5MHz × 8 = 2500MHz(在Xilinx K7/KU系列VCO范围2.0~3.4GHz内) CLKOUT0_DIVIDE_F= VCO /adc_clk= 2500 / 312.5 = 8.0 → 输出312.5MHzCLKOUT1_DIVIDE_F= VCO /gt_refclk= 2500 / 625 = 4.0 → 输出625MHz
// mmcm_adv_inst.v mmcm_adv #( .BANDWIDTH("OPTIMIZED"), .CLKIN1_PERIOD(10.0), // 100MHz输入,周期10ns .CLKFBOUT_MULT_F(8.0), // VCO=2500MHz .DIVCLK_DIVIDE(1), .CLKOUT0_DIVIDE_F(8.0), // 312.5MHz .CLKOUT0_PHASE(0.0), .CLKOUT1_DIVIDE_F(4.0), // 625MHz .CLKOUT1_PHASE(0.0), .CLKOUT2_DIVIDE_F(2.0), // 125MHz,供FPGA逻辑使用 .CLKOUT2_PHASE(0.0) ) mmcm_adv_inst ( .CLKIN1(clk_100m), .CLKFBIN(clkfb_in), .CLKFBOUT(clkfb_out), .CLKOUT0(adc_clk), // 312.5MHz .CLKOUT1(gt_refclk), // 625MHz .CLKOUT2(sys_clk), // 125MHz .RESET(1'b0), .LOCKED(mmcm_locked) );注意:
CLKOUT0_PHASE和CLKOUT1_PHASE设为0.0,并非表示相位绝对对齐,而是指MMCM内部反馈路径的相位基准。实际板级相位差由PCB走线长度决定,必须用IDELAY/ODELAY动态补偿。
3.2 时序约束:set_clock_groups是救命稻草,不是可选项
Vivado默认将所有get_clocks视为潜在同步关系。若不显式声明adc_clk和gt_refclk为异步,则时序分析器会疯狂报hold time violation——因为GT收发器内部采样逻辑(gt_refclk域)与FPGA用户逻辑(adc_clk域)之间存在跨时钟域路径,而工具试图用同步约束去优化它,结果就是假路径泛滥。
必须在XDC中写死:
# clock_constraints.xdc create_clock -name clk_100m -period 10.0 [get_ports clk_100m] create_generated_clock -name adc_clk -source [get_pins mmcm_adv_inst/CLKIN1] -divide_by 1 [get_pins mmcm_adv_inst/CLKOUT0] create_generated_clock -name gt_refclk -source [get_pins mmcm_adv_inst/CLKIN1] -divide_by 1 [get_pins mmcm_adv_inst/CLKOUT1] # 关键!声明adc_clk与gt_refclk为异步 set_clock_groups -asynchronous -group [get_clocks adc_clk] -group [get_clocks gt_refclk] # 对GT收发器专用路径做false path(避免误报) set_false_path -from [get_clocks gt_refclk] -to [get_clocks adc_clk] set_false_path -from [get_clocks adc_clk] -to [get_clocks gt_refclk]提示:
set_clock_groups -asynchronous指令后,Vivado将完全忽略这两个时钟域之间的任何时序检查,转而依赖你在RTL中写的跨时钟域同步器(如两级触发器)。这是高速设计的铁律。
3.3 相位微调:IDELAYE2原语校准sysref与帧时钟的建立/保持时间
AD9680的SYSREF信号是JESD204B多器件同步的生命线。它必须在adc_clk的特定相位窗口内到达GT收发器,否则ILAS(Initial Lane Alignment Sequence)无法启动。手册要求SYSREF相对于adc_clk的建立时间≥1.5ns,保持时间≥0.5ns。PCB走线差异会导致相位漂移,必须用IDELAYE2动态补偿。
// sysref_align.v IDELAYE2 #( .CINVCTRL_SEL("FALSE"), .DELAY_SRC("IDATAIN"), .HIGH_PERFORMANCE_MODE("TRUE"), .IDELAY_TYPE("VAR_LOAD"), .IDELAY_VALUE(15), // 初始值15,对应约75ps/step × 15 = 1.125ns .PIPE_SEL("FALSE"), .REFCLK_FREQUENCY(312.5) ) idelay_sysref ( .CNTVALUEIN(delay_tap), // 可由ILA动态修改 .DATAIN(sysref_in), .DATAOUT(sysref_delayed), .IDATAIN(1'b0), .INC(1'b0), .LD(1'b1), .LDPIPEEN(1'b0), .CE(1'b0), .C(1'b0), .CINVCTRL(1'b0), .CNTVALUEOUT() ); // 顶层例化 wire sysref_aligned; assign sysref_aligned = sysref_delayed; // ILA触发点:当jesd_rx_core.sync拉低时,扫描delay_tap从0到31 // 找到jesd_status == GOOD时的最优tap值 always @(posedge adc_clk) begin if (scan_start) begin if (delay_tap < 31) begin delay_tap <= delay_tap + 1; end else begin scan_done <= 1; end end end实测中,delay_tap在12~18之间最稳定。这个值不是固定不变的——温度每升高10℃,需增加1~2个tap。因此,量产固件中应固化此值,并在启动时做一次温度补偿校准。
3.4 避坑:时钟管理的4个致命陷阱
现象:
mmcm_locked信号一直为低。
原因:CLKIN1_PERIOD参数错误。若输入时钟实为125MHz(周期8ns),却写成10.0,MMCM无法锁定。
解决:用示波器实测clk_100m周期,精确到0.1ns;在XDC中用create_clock -period定义,而非依赖Verilog参数。现象:
gt_refclk频率正确,但GT收发器眼图闭合。
原因:MMCM的BANDWIDTH设为LOW。低带宽抑制抖动但响应慢,导致VCO相位噪声恶化。
解决:强制设为"OPTIMIZED",并在XDC中添加set_property PHASE_ALIGNMENT true [get_cells mmcm_adv_inst]。现象:
set_clock_groups写了,但时序报告仍有adc_clk到gt_refclk路径报违例。
原因:约束文件未被Vivado正确加载。检查Project Settings → Constraints → Add Constraints是否勾选。
解决:在Tcl Console中执行read_xdc ./constraints/clock_constraints.xdc,确认无报错。现象:IDELAYE2调节
delay_tap,但sysref_aligned波形无变化。
原因:IDELAYE2未例化在正确的IO Bank。Xilinx要求IDELAYE2必须与GT收发器位于同一Bank(如K7的Bank 11/12)。
解决:检查sysref_in端口的IOSTANDARD和PACKAGE_PIN,确保其Bank号与GT收发器一致;在XDC中用set_property IOSTANDARD DIFF_SSTL15_T_DCI [get_ports sysref_in]。
4. JESD204B接收核心:Xilinx IP核不是终点,而是起点——状态机对齐、ILA触发、数据拼接全链路解析
Xilinx Vivado的JESD204B RX IP核(v5.2+)已非常成熟,但它输出的rx_data、rx_valid、rx_sync只是原始比特流。真正的挑战在于:如何确认链路已真正锁定(而不仅是IP核声称的rx_sync==1)?如何在4 Lane数据中精准定位帧边界?如何把分散在4个Lane的12bit样本拼成完整的32bit I/Q数据?这些必须用Verilog状态机+ILA深度抓取来闭环验证。
4.1 IP核关键参数配置:Line Rate、M/L/F、Scrambler必须与ADC端镜像
在Vivado IP Catalog中配置JESD204B RX Subsystem时,以下参数必须与AD9680寄存器0x14/0x15/0x16完全一致:
| 参数 | 值 | 说明 |
|---|---|---|
Line Rate | 5000.0Mbps | 1Gsps × 4 Lane × 10bit/8 = 5000Mbps(注意:AD9680用10b/8b编码) |
RX_JESD_M | 2 | 主通道数(AD9680双通道,M=2) |
RX_JESD_L | 4 | Lane数(物理通道数) |
RX_JESD_F | 2 | 每帧字节数(F=2 → 每帧2×12bit=24bit) |
RX_JESD_SCR | 1 | 加扰使能(必须与ADC端0x1A寄存器一致) |
RX_JESD_NP | 12 | 每样本位宽(AD9680为12bit) |
// jesd204b_rx_top.v jesd204b_rx #( .RX_JESD_M(2), .RX_JESD_L(4), .RX_JESD_F(2), .RX_JESD_SCR(1), .RX_JESD_NP(12), .RX_JESD_SUBCLASS(1), .RX_JESD_DEVICE_ID(0), .RX_JESD_LINK_ID(0) ) jesd_rx_inst ( .aclk(adc_clk), // 用户逻辑时钟(312.5MHz) .aresetn(rst_n), // 异步复位 .gt_refclk(gt_refclk), // GT参考时钟(625MHz) .sysref(sysref_aligned), // 校准后的SYSREF .rx_sync(rx_sync), // IP核输出的SYNC信号 .rx_valid(rx_valid), // 数据有效 .rx_data(rx_data), // 32bit并行数据(4 Lane × 8bit) .rx_charisk(rx_charisk), // 字符类型指示(K字符/数据字符) .rx_lane_sync(rx_lane_sync), // 每Lane同步状态 .rx_link_status(rx_link_status), // 链路状态 .rx_ilas_status(rx_ilas_status) // ILAS状态 );注意:
aclk必须接adc_clk(312.5MHz),而非sys_clk(125MHz)。因为IP核内部FIFO读写、状态机跳转均以此为基准,若用低频时钟,会导致rx_valid脉冲过窄,用户逻辑无法可靠采样。
4.2 Lane对齐状态机:用K28.5字符定位帧起始,拒绝IP核的“虚假同步”
Xilinx IP核的rx_sync信号只是表示ILAS完成,不代表数据流已对齐。真正的对齐必须检测K28.5(0xBC)同步字符。AD9680在每个多帧(Multiframe)开始处插入K28.5,4个Lane的K28.5必须严格对齐(偏差≤1个字符周期),否则数据拼接错位。
// lane_align_fsm.v module lane_align_fsm ( input wire rx_clk, input wire rst_n, input wire [31:0] rx_data, input wire [3:0] rx_charisk, input wire rx_sync, output reg lane_aligned, output reg [31:0] aligned_data ); typedef enum logic [1:0] { WAIT_SYNC, DETECT_K285, ALIGN_CHECK, ALIGNED } align_state_t; align_state_t state, next_state; reg [3:0] k285_cnt; // 记录4 Lane中检测到K28.5的Lane数 reg [3:0] k285_pos; // 记录每个Lane的K28.5位置 always @(posedge rx_clk or negedge rst_n) begin if (!rst_n) begin state <= WAIT_SYNC; lane_aligned <= 0; k285_cnt <= 0; k285_pos <= 0; end else begin state <= next_state; case (state) WAIT_SYNC: begin if (rx_sync) begin next_state <= DETECT_K285; k285_cnt <= 0; end else begin next_state <= WAIT_SYNC; end end DETECT_K285: begin // 检查4 Lane的rx_data[7:0], [15:8], [23:16], [31:24]是否为0xBC for (integer i = 0; i < 4; i = i + 1) begin if (rx_charisk[i] && rx_data[(i*8)+7:(i*8)] == 8'hBC) begin k285_cnt[i] <= 1; k285_pos[i] <= i*8; end end if (k285_cnt == 4'b1111) begin next_state <= ALIGN_CHECK; end else if ($countones(k285_cnt) >= 2) begin // 至少2个Lane对齐,继续等待 next_state <= DETECT_K285; end else begin // 无对齐,重启 next_state <= WAIT_SYNC; end end ALIGN_CHECK: begin // 验证4个K28.5位置是否连续(0,8,16,24) if (k285_pos == 4'b0000_0001_0010_0011) begin // 二进制0,1,2,3对应位置0,8,16,24 lane_aligned <= 1; next_state <= ALIGNED; end else begin next_state <= WAIT_SYNC; end end ALIGNED: begin aligned_data <= rx_data; next_state <= ALIGNED; end endcase end end // 输出对齐后的数据 assign aligned_data = (lane_aligned) ? rx_data : 32'h0;这段状态机的价值在于:它不信任IP核的rx_sync,而是用硬件逻辑逐字符扫描,只有当4个Lane的K28.5严格对齐时,才置位lane_aligned。这是数据拼接的前提。
4.3 数据拼接与验证:从4×12bit到2×32bit的位域重组
AD9680在Lane4模式下,每个Lane传输12bit样本,4 Lane组成一个完整多帧(包含2个通道×2个样本)。IP核输出的rx_data[31:0]是4 Lane的8bit并行数据(因JESD204B用8b/10b编码,实际有效数据为12bit,需解码)。我们需将其重组为{ch1_sample1, ch1_sample2, ch2_sample1, ch2_sample2}的32bit结构。
// data_repack.v reg [11:0] ch1_s1, ch1_s2, ch2_s1, ch2_s2; always @(posedge rx_clk) begin if (!rst_n) begin ch1_s1 <= 0; ch1_s2 <= 0; ch2_s1 <= 0; ch2_s2 <= 0; end else if (lane_aligned && rx_valid) begin // 假设rx_data格式:[Lane3[7:0], Lane2[7:0], Lane1[7:0], Lane0[7:0]] // 经8b/10b解码后,每个Lane的12bit样本位于: // Lane0: rx_data[11:0] → ch1_s1 // Lane1: rx_data[23:12] → ch1_s2 // Lane2: rx_data[35:24] → ch2_s1(需跨32bit边界) // Lane3: rx_data[47:36] → ch2_s2(需跨32bit边界) // 实际中,IP核已做解码,输出为raw 12bit,故直接拼接: ch1_s1 <= {rx_data[11:0]}; // Lane0样本1 ch1_s2 <= {rx_data[23:12]}; // Lane1样本1 ch2_s1 <= {rx_data[35:24]}; // Lane2样本1 ch2_s2 <= {rx_data[47:36]}; // Lane3样本1 end end // 最终输出:32bit I/Q数据(I=ch1_s1, Q=ch1_s2) wire [31:0] iq_data = {ch1_s1, ch1_s2, ch2_s1, ch2_s2};提示:
rx_data宽度取决于IP核配置。若配置为Data Width = 32,则4 Lane数据被压缩进32bit,需用{rx_data[11:0], rx_data[23:12], rx_data[31:24], 4'b0}拼接;若为64,则直接取高位。
4.4 避坑:JESD204B接收的6个高频翻车
本文还有配套的精品资源,点击获取