目录
1.器件手册
四通道,14bit,采样率为125/105M,ADC采用串行LVDS接口输出,结构图如下所示:
名称 | 方向 | 说明 |
CLK_P CLK_M | In | FPGA提供给ADC的差分时钟,范围5-125Mhz |
DCLK_P DCLK_M | Out | ADC提供给FPGA的位时钟,位时钟的变化处在数据的中间位置,以保持数据的稳定 |
FCLK_P FCLK_M | Out | ADC提供给FPGA的帧时钟,作用是判断完整的14bit数据 |
DA0_P DA0_M DA1_P DA1_M | Out | LVDS接口的数据输出,采用的是两线制的因此有4个接口 |
SEN SDATA SCLK RESET | In | 通过SPI配置ADC寄存器更改工作模式 SCLK最大20Mhz SDATA位5bit的寄存器地址+11bit的数据 |
寄存器配置:
(1)并行:根据电压的改变(电阻分压的方式)来配置,可简化编程。
(2)串行:SDATA(数据) 5bit的寄存器地址+11bit的数据
SCLK (时钟)DC<TYP<20MHZ
SEN (使能) RESET(复位)
输出方式:
(1)SDR :只在信号的上升沿进行数据的传输
(2)DDR:在信号的上升沿和下降沿都进行数据传输,是SDR的双倍
总结:给ADS6445芯片一个时钟,通过内部的PLL锁相环,自己会生成一个位时钟和一个时钟给FPGA,同时4通道(每个通道都有两条线即SDR、DDR可选)将数据传给FPGA
本文采用两线制、DDR的LVDS接口,帧时钟与FPGA给到ADC的时钟频率一致,位时钟是FPGA给到ADC的时钟频率的4倍,数据传输的速率位8倍的时钟频率。具体如图所示:
从图中可以看出,在时钟变换的上升、下降沿都进行了数据的传输并且处于中间位置,两线传输的数据为16bit其中有效的为14bit。
2.PCB电路板绘制
3.SPI配置
线性序列机的配置方式,时钟:20MHZ,SCLK工作时钟:10MHZ。
4.程序框架
如果直接采用 ADC 输出的位时钟 DCO±,帧时钟 FCO±驱动 ISERDESE2 对数据进行解串,那么由于 FPGA 内部布局布线所带来的延时,到达 ISERDESE2 相应管脚的帧时钟、位时钟与数据信号可能不再保持初始的相位关系,从而导致无法得到正确的串并转换数据,所以需要利用 SelectIO 资源使时钟与数据恢复对应的相位关系。本系统采用的 LVDS 接口模块电路框图如下图,主要由位对齐控制电路、帧同步控制电路和串并转换控制电路三个部分组成。
由上述的原理可以总结出以下三个问题:
1.因为FPGA内部走线的不同,会使得位时钟(DCO)、帧时钟(FCO)、数据到达每个转换模块ISERDESE的相位发生变化。
2.使用原语ISERDESE时本身的特点要求是DCO和FCO的时钟边沿必须是对齐的,即ISERDESE要求接口的帧、位时钟边沿对齐,而LVDS接口输出的帧、位时钟是不对齐的。
3.:使用BUFIO与BUFR后产生的clkd与clkdiv又会与原时钟有一定的延迟,导致与数据又会产生相位差,因此需要将clkd与clkdiv与原位、帧时钟对齐。
针对上述的三个问题进行解答:
首先 ADC 输出的 LVDS 信号通过 IOB 中的差分输入缓冲器(IBUFGDS)转化为 FPGA 可以处理的信号,这时由于 IOB 靠近信号的输入端口,所以通过差分输入缓冲器后的时钟与数据之间仍保持对应的相位关系,差分位时钟转成单端时钟 bitclk,帧时钟转成差分的 fcop、fcon,差分的数据信号转成差分的 doutp、doutn。
1:单端时钟bitclk通过 FPGA 的区域时钟资源 BUFIO 和 BUFR,其中 BUFR可以根据配置得到输入时钟的分频时钟输出,并且区域时钟可以保证时钟输出到达区域内每个ISERDESE2 模块之间的延迟相似,将得到的 clkd 以及N分频的 clkdiv 作为 ISERDESE2 的驱动时钟。
用这两个原语后的时钟可走全局时钟网络,相当于到每个ISERDESE的时间差是一样的,保证了到达每一个ISERDESE的距离是一样的。
2:因为区域时钟资源 BUFIO 和 BUFR 离 I/O 模块有一定的距离,那么 clkd、clkdiv 与源时钟之间就存在延时,无法保持初始的相位关系,从而导致解串错误,所以需要对延时进行一定的补偿。延时补偿也就是使用延时模块 IDELAYE2 对 bitclk 施加一定的延时,将延时后的时钟通过BUFIO 和 BUFR 产生 clkd 和 clkdiv,利用位对齐状态机控制 IDELAYE2 的延时量,直到 clkd,clkdiv重新对齐源时钟 bitclk。
在此在程序中引入例化VIO模块,并通过VIO对设计进行监控,当观测到bitclk和clkd的输出数据呈现0,1跳变时,此时认为已位对齐。结果由标题6展示。
3:位对齐完成以后,虽然这时 clkd 的上升沿已经对齐了 bitclk 的上升沿或者下降沿,但是由于增加了延时,clkdiv 与帧时钟的相位是未知的,将导致数据的错位,从而无法得到正确的解串数据。此时便引入了帧同步状态机对ISERDESE进行BITSLIP原语模块操作,将数据位置调整顺序(时钟相位不变)从而实现帧同步。
同上此时引入例化VIO模块,当观测到数据呈现11110000或00001111时表明实现了帧同步,达到了设计的要求。
BITSILP原语:
SDR模式:使能一次,数据左移一次,对于8bit并行数据,移动8次完成一个循环。
DDR模式:使能一次,数据先右移一次,再左移三次,两者交替运行,同样移动8次完成一个循环。
如果检测结果是我们想要的数据,则R_bit_slip 拉低,否则R_bit_slip拉高进行上图的移位操作。
5.具体程序(原语)
1.SPI寄存器配置
always@(posedge SPI_20M or negedge rst_n)begin
if(!rst_n)begin
SCLK <= 1'b1;
SEN <= 1'b1;
SDATA <= 1'b1;
spi_data <= 'd0;
end
else if(state_en)begin
case(state_cnt)
0:begin
SCLK <= 1'b1;
SEN <= 1'b1;
SDATA <= 1'b1;
spi_data <= spi_data;
end
1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31:begin
SCLK <= 1'b1;
SEN <= 1'b0;
SDATA <= spi_data[15];
spi_data <= spi_data;
end
2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32:begin
SCLK <= 1'b0;
SEN <= 1'b0;
SDATA <= SDATA;
spi_data <= spi_data << 1;
end
33:begin
SCLK <= 1'b1;
SEN <= 1'b1;
SDATA <= 1'b1;
case(data_state)
0:spi_data <= 16'b01000_0000_00000_00;//Register B 增益设置
1:spi_data <= 16'b01010_010_000_00000;//Register C 偏移二进制,正常模式16'b01010_010_000_00000/测试模式16'b01010_010_111_00000
2:spi_data <= 16'b01101_100_0001_0101;//Register F 串行模式,字节,高位在前_2Vpp,上升沿_16X,DDR
default:spi_data <= spi_data;
endcase
end
default:begin
SCLK <= 1'b1;
SEN <= 1'b1;
SDATA <= 1'b1;
spi_data <= spi_data;
end
endcase
end
else begin
SCLK <= 1'b1;
SEN <= 1'b1;
SDATA <= 1'b1;
end
end
2.OBUFDS原语
OBUFDS将标准单端信号转换成差分信号,输出端口需要直接对应到顶层模块的输出信号,和IBUFDS为一对互逆操作。
//FPGA给到ADC的时钟,OBUFDS:差分输出缓存器
OBUFDS #(
.IOSTANDARD("DEFAULT"), // Specify the output I/O standard指定输出I/O标准
.SLEW("SLOW") // Specify the output slew rate指定输出转换率
) OBUFDS_inst (
.O(clk_out_p), // Diff_p output (connect directly to top-level port)Diff_p输出(直接连接到顶级端口)
.OB(clk_out_n), // Diff_n output (connect directly to top-level port)Diff_n输出(直接连接到顶级端口)
.I(CLK_100M) // Buffer input
);
3.IBUFDS原语
IBUF是输入缓存,一般vivado会自动给输入信号加上,IBUFDS是IBUF的差分形式,支持低压差分信号(如LVCMOS、LVDS等)。在IBUFDS中,一个电平接口用两个独特的电平接口(I和IB)表示。一个可以认为是主信号,另一个可以认为是从信号。主信号和从信号是同一个逻辑信号,但是相位相反。
//差分转单端
IBUFDS #(
.DIFF_TERM("TRUE"), // Differential Termination
.IBUF_LOW_PWR("TRUE"), // Low power="TRUE", Highest performance="FALSE"
.IOSTANDARD("DEFAULT") // Specify the input I/O standard
) IBUFDS_DCO (
.O(W0_dc_clk), // Buffer output
.I(DCO_p), // Diff_p buffer input (connect directly to top-level port)
.IB(DCO_n) // Diff_n buffer input (connect directly to top-level port)
);
4. IDELAYE2原语,IDELAYCTRL原语
(1)IDELAYE2 原语:
每个I/O都包含一个IDELAYE2的可编程的原语。IDELAY可以连接到后续的模块。IDLEAYE2可以根据抽头系数来调整延时。这个抽头系数对应的最小分辨率可以根据7系列的手册上来看。
(2)IDELAYCTRL原语:
一般都是配合着I/ODELAY原语来使用的。如果使用了IDELAYE2或者ODELAYE2原语,那么IDELAYCTRL原语必须被例化。IDLAYCTRL使用用户提供的REFCLK来校准IDELAY和ODELAY。也就是前面说的,具体的延时的值,是根据输入到IDELAYCTRL的参考时钟来确定的。
(* IODELAY_GROUP = "delay1" *)
IDELAYCTRL IDELAYCTRL_DCO (
.RDY(W_delay_rdy), // 1-bit output: Ready output
.REFCLK(REF_CLK_200M), // 1-bit input: Reference clock input
.RST(~rst_n) // 1-bit input: Active high reset input
);
(* IODELAY_GROUP = "delay1" *)
IDELAYE2 #(
.CINVCTRL_SEL("FALSE"), // Enable dynamic clock inversion (FALSE, TRUE)
.DELAY_SRC("IDATAIN"), // Delay input (IDATAIN, DATAIN)
.HIGH_PERFORMANCE_MODE("TRUE"), // Reduced jitter ("TRUE"), Reduced power ("FALSE")
.IDELAY_TYPE("VAR_LOAD"), // FIXED, VARIABLE, VAR_LOAD, VAR_LOAD_PIPE
.IDELAY_VALUE(0), // Input delay tap setting (0-31)
.PIPE_SEL("FALSE"), // Select pipelined mode, FALSE, TRUE
.REFCLK_FREQUENCY(200.0), // IDELAYCTRL clock input frequency in MHz (190.0-210.0, 290.0-310.0).
.SIGNAL_PATTERN("CLOCK") // DATA, CLOCK input signal
)
IDELAYE2_DCO (
.CNTVALUEOUT(), // 5-bit output: Counter value output
.DATAOUT(W1_dc_clk), // 1-bit output: Delayed data output
.C(W_fc_clk), // 1-bit input: Clock input
.CE(1'b0), // 1-bit input: Active high enable increment/decrement input
.CINVCTRL(1'b0), // 1-bit input: Dynamic clock inversion input
.CNTVALUEIN('d11), // 5-bit input: Counter value input
.DATAIN(1'b0), // 1-bit input: Internal delay data input
.IDATAIN(W0_dc_clk), // 1-bit input: Data input from the I/O
.INC(1'b0), // 1-bit input: Increment / Decrement tap delay input
.LD(1'b1), // 1-bit input: Load IDELAY_VALUE input
.LDPIPEEN(1'b0), // 1-bit input: Enable PIPELINE register to load data input
.REGRST(1'b0) // 1-bit input: Active-high reset tap-delay input
);
5.BUFIO原语
本地IO时钟缓冲器.使用独立于全局时钟网络的专用时钟网络来驱动纵向IO管脚。非常适合同步数据采集;BUFIO要求时钟和相应的IO必须在同一时钟区域,而不同的时钟网络的驱动需要BUFR原语来实现.由于BUFIO引出的时钟只到达了IO列,所以不能来驱动逻辑资源,比如CLB和BRAM.
BUFIO BUFIO_DCO (
.O(W_dc_clk), // 1-bit output: Clock output (connect to I/O clock loads).
.I(W2_dc_clk) // 1-bit input: Clock input (connect to an IBUF or BUFMR).
);
6.BUFR原语
本地IO时钟逻辑缓冲器.与BUFIO一样是将驱动时钟引入某一时钟区域的专用时钟网络,而独立于全局时钟网络;不同的是,BUFR不仅可以跨越不同的时钟区域(最多3个),还能够驱动IO逻辑以及自身或邻近时钟区域的逻辑资源.BUFIO的输出和本地内部互联都能驱动BUFR组件.此外,BUFR能完成输入时钟1—8的整数分频.因些BUFR是同步设计中实现跨时钟域以及串并转换的最佳方案.
BUFR #(
.BUFR_DIVIDE("4"), // Values: "BYPASS, 1, 2, 3, 4, 5, 6, 7, 8" 分频
.SIM_DEVICE("7SERIES")// Must be set to "7SERIES"
)
BUFR_DCO (
.O(W_fc_clk), // 1-bit output: Clock output port
.CE(1'b1), // 1-bit input: Active high, clock enable (Divided modes only)
.CLR(1'b0), // 1-bit input: Active high, asynchronous clear (Divided modes only)
.I(W2_dc_clk) // 1-bit input: Clock buffer input driven by an IBUF, MMCM or local interconnect
);
7.ISERDESE2原语
ISERDESE2是FPGA的一个专用的串并联转换器,具有特定的时钟和逻辑特性,旨在促进高速源同步应用的实现。iserdes2避免了在FPGA结构中设计反序列化器时遇到的额外时序复杂性。其具有以下应用:
(1)专用解串器/串并转换器:
该转换器支持单数据速率(SDR)和双数据速率(DDR)模式。在SDR模式下,串并联转换器创建一个2-、3-、4-、5-、6-、7-或8位宽并行字。在DDR模式下,当使用一个ISERDESE2时,串并联转换器创建4、6、8位宽的并行字模式,当使用两个级联时创建10或14位宽的并行字模式
(2)Bitslip 子模块:
Bitslip子模块允许设计人员重新排列进入FPGA结构的并行数据流的序列。这可以用于训练包含训练模式的源同步接口。
(3)专用内存接口
ISERDESE2包含专用电路(包括OCLK输入引脚),在ISERDESE2块中完全处理频闪到fpga的时钟域交叉,更利于提高性能和简化设计。
(4)支持网络接口、DDR3接口、QDR接口、异步接口。
ISERDESE2 #(
.DATA_RATE("SDR"), // DDR, SDR
.DATA_WIDTH(8), // Parallel data width (2-8,10,14)
.DYN_CLKDIV_INV_EN("FALSE"), // Enable DYNCLKDIVINVSEL inversion (FALSE, TRUE)
.DYN_CLK_INV_EN("FALSE"), // Enable DYNCLKINVSEL inversion (FALSE, TRUE)
.INIT_Q1(1'b0),
.INIT_Q2(1'b0),
.INIT_Q3(1'b0),
.INIT_Q4(1'b0),
.INTERFACE_TYPE("NETWORKING"),// MEMORY, MEMORY_DDR3, MEMORY_QDR, NETWORKING, OVERSAMPLE
.IOBDELAY("IBUF"), // NONE, BOTH, IBUF, IFD
.NUM_CE(2), // Number of clock enables (1,2)
.OFB_USED("FALSE"), // Select OFB path (FALSE, TRUE)
.SERDES_MODE("MASTER"), // MASTER, SLAVE
.SRVAL_Q1(1'b0),
.SRVAL_Q2(1'b0),
.SRVAL_Q3(1'b0),
.SRVAL_Q4(1'b0)
)
ISERDESE2_DCO (
.O(W2_dc_clk), // 1-bit output: Combinatorial output
.Q1(W_allign_word[0]), // Q1 - Q8: 1-bit (each) output: Registered data outputs
.Q2(W_allign_word[1]),
.Q3(W_allign_word[2]),
.Q4(W_allign_word[3]),
.Q5(W_allign_word[4]),
.Q6(W_allign_word[5]),
.Q7(W_allign_word[6]),
.Q8(W_allign_word[7]),
.SHIFTOUT1(),
.SHIFTOUT2(),
.BITSLIP(),
.CE1(1'b1),
.CE2(1'b1),
.CLKDIVP(1'b0), // 1-bit input: TBD
.CLK(W_dc_clk), // 1-bit input: High-speed clock
.CLKB(~W_dc_clk), // 1-bit input: High-speed secondary clock
.CLKDIV(W_fc_clk), // 1-bit input: Divided clock
.OCLK(1'b0), // 1-bit input: High speed output clock used when INTERFACE_TYPE="MEMORY"
.DYNCLKDIVSEL(1'b0), // 1-bit input: Dynamic CLKDIV inversion
.DYNCLKSEL(1'b0), // 1-bit input: Dynamic CLK/CLKB inversion
.D(W0_dc_clk), // 1-bit input: Data input
.DDLY(W1_dc_clk), // 1-bit input: Serial data from IDELAYE2
.OFB(1'b0), // 1-bit input: Data feedback from OSERDESE2
.OCLKB(1'b0), // 1-bit input: High speed negative edge output clock
.RST(~rst_n), // 1-bit input: Active high asynchronous reset
.SHIFTIN1(1'b0),
.SHIFTIN2(1'b0)
);
6.测试结果
例化vio模块监控是否位对齐和输出数据达到了设计的要求,结果观测到bitclk和clkd的输出数据呈现0,1跳变,此时已位对齐,同时输出数据呈现11110000的结果:
最终只选择分别采集两路信号,分别为A、D通道和B、C通道
实物如下图所示:
参考博客:
Xilinx FPGA原语总结_obufds原语-CSDN博客
FPGA内部资源(Xilinx) ---- IDELAY(延时)-CSDN博客
Xilinx ISERDESE2应用笔记及仿真实操-CSDN博客
小白也是刚接触该设计,有些地方理解错误,可能有偏差,向大佬们请教!