作者:ShownSun
工作室:时沿科技
FPGA实现ADC3444数据接口逻辑
1 引言
本文通过以高速ADC3444芯片为例进行数据接口逻辑代码的编写,利用SelectIO IP快速高效完成芯片驱动的生成。关于SelectIO IP的使用,可以参考Xilinx SelectIO IP使用说明(一)。
2 ADC3444
2.1 芯片简介
ADC344x 器件属于高线性度、超低功耗、四通道、14 位、25MSPS 至 125MSPS 模数转换器 (ADC) 系列。此类器件专门设计用于支持具有宽动态范围需求且要求 苛刻的高输入频率信号。输入时钟分频器可给予系统时钟架构设计更高的灵活性,同时SYSREF输入可实现整个系统同步。
ADC344x 系列支持串行低压差分信令(LVDS),从而减少接口线路的数量,实现高系统集成密度。串行LVDS接口为双线制,通过两个LVDS对串行输出每个ADC 数据。此外,也可提供单线制串行LVDS接口。内部锁相环(PLL)会将传入的 ADC 采样时钟加倍,以获得串行输出各通道的14位输出数据时所使用的位时钟。除了串行数据流之外,数据帧和位时钟也作为LVDS 输出进行传送。整体结构图如图 1 ADC3444整体结构图所示。
.四通道
.14 位分辨率
.单电源:1.8V
.串行低压差分信号(LVDS)接口
.支持 1 分频、2 分频和 4 分频的灵活输入时钟缓冲器
.fIN = 70MHz 时,信噪比 (SNR) = 72.4dBFS,无杂 散动态范围 (SFDR) = 87dBc
.超低功耗:
.125MSPS 时为每通道 98mW
.通道隔离:105dB
.内部抖动和斩波
.支持多芯片同步
.与 12 位版本器件之间具有引脚到引脚兼容性
.封装:超薄四方扁平无引线 (VQFN)-56 (8mm x 8mm)
2.2 参数配置
用户可以根据自己的需求将数据接口通过SPI配置成1 wire或2 wire接口。只需要通过配置软件设置即可,如图 2 ADC3444寄存器配置软件所示,获取软件需登录TI官网。
在进行数据验证时,也可以使用测试模式,对收发数据进行验证以保证系统的正确性。另外,还可以对输入时钟进行延时调节或者通过SelectIO的delay、delayctrl功能对时钟信号进行微调,以满足时序要求。芯片数据时钟与数据之间的时序可靠性也可以通过芯片内部的寄存器25h的LVDS SWING 进行配置,以此达到要求。
此外需要注意的是,系统上电复位后,必须要加载一些寄存器配置,按照一定要求配置寄存器后才能正常使用,详细的初始化寄存器如所示。
寄存器配置接口为SPI,读/写1位,地址位14位,数据位8位。详细的读写时序如图 3 串行寄存器写时序框图与图 4 串行寄存器读时序框图所示。
2.3 引脚
2.4 接口时序
以下使用的数据接口为1 wire与2 wire模式的时序图 6 1 wire输出时序框图与图 7 2 wire输出时序框图,可见1 wire模式下只需要使用1路数据接口,不过时间花费14个DCLK周期。2wire模式需要使用2路数据接口,不过时间只需花费7个DCLK周期。
3 参考代码
3.1 SelectIO配置
该部分使用2 wire输出接口时序,根据以上对ADC3444的了解,就可以轻松的配置SelectIO IP的GUI界面了。由于芯片是ADC芯片,所以接口类型选择Custom,数据总线方向选择Input。
根据上述参数配置部分,自然就选择DDR。数据接口包括时钟CLK、Frame对齐信号与差分数据端Data[08:0],要同时对FCLK与Data信号进行时序解析,所以端口宽度设置为9。
由于芯片引脚包含数据时钟引脚,可将此引脚用于SelestIO的外部时钟输入,以便整个系统的正常工作。
还需要注意的是,数据时钟DCLK可能与DATA、FCLK的建立保持时间不太匹配,需要在其之间添加延时模块delay与delayctrl,以保证满足建立保持时间。
3.2 程序代码
需要注意的是,虽然可以使用selectio完成串行与并行数据的转换,但是不知道数据的起始位置从哪一位开始,这时就要将解析后的FCLK14位数据与14’h3F80进行对比。如果不满足,需要对bitslip打拍子,使数据不断移位以满足要求。
always @(posedge adc_clk or negedge adc_resetn) begin
if(adc_resetn == 1'b0) begin
bitslip_int <= 1'b0;
end else if(bit_count == 3'b111) begin
bitslip_int <= 1'b0;
end else begin
case(device_data[125:112])
14'h3f80:
begin
bitslip_int <= 1'b0;
end
default:
begin
bitslip_int <= 1'b1;
end
endcase
end
end
selectio_wiz_adc_v #(
.SYS_W(9),
.DEV_W(14),
.DELAY_VAL(DELAY_VAL))
selectio_wiz_adc_v_inst (
.data_in_from_pins_p ({adc_fclk_p,adc_dxx_p}),
.data_in_from_pins_n ({adc_fclk_n,adc_dxx_n}),
.data_in_to_device (device_data),
.delay_locked (adc_resetn),
.ref_clock (delay_clk),
.bitslip (bitslip),
.clk_in_p (adc_dclk_p),
.clk_in_n (adc_dclk_n),
.clk_div_out (adc_clk),
.clk_reset (~sys_resetn),
.io_reset (~sys_resetn)
);
always @(posedge adc_clk or negedge adc_resetn) begin
if(adc_resetn == 1'b0) begin
adc_dataa0 <= 14'h0;
adc_dataa1 <= 14'h0;
adc_datab0 <= 14'h0;
adc_datab1 <= 14'h0;
adc_datac0 <= 14'h0;
adc_datac1 <= 14'h0;
adc_datad0 <= 14'h0;
adc_datad1 <= 14'h0;
end else begin
case(device_data[125:112])
14'h3f80:
begin
adc_dataa0 <= {device_data[20:14],device_data[06:00]};
adc_dataa1 <= {device_data[27:21],device_data[13:07]};
adc_datab0 <= {device_data[48:42],device_data[34:28]};
adc_datab1 <= {device_data[55:49],device_data[41:35]};
adc_datac0 <= {device_data[76:70],device_data[62:56]};
adc_datac1 <= {device_data[83:77],device_data[69:63]};
adc_datad0 <= {device_data[104:98],device_data[90:84]};
adc_datad1 <= {device_data[111:105],device_data[97:91]};
end
default:
begin
adc_dataa0 <= adc_dataa0;
adc_dataa1 <= adc_dataa1;
adc_datab0 <= adc_datab0;
adc_datab1 <= adc_datab1;
adc_datac0 <= adc_datac0;
adc_datac1 <= adc_datac1;
adc_datad0 <= adc_datad0;
adc_datad1 <= adc_datad1;
end
endcase
end
end