AD9361 LVDS接口程序框图

数据接收路径
- AD9361将数据输出到rx_frame_in_p/n和rx_data_in_p/n[5:0]。
- rx_frame_in_p/n和rx_data_in_p/n[5:0]通过FPGA的IBUFDS将差分信号转换为单端信号。
- 转换为单端的rx_frame和rx_data通过FPGA的Idelay进行延时,以弥补时序误差。
- 经过Idelay进行延时后的rx_frame和rx_data通过FPGA的IDDR将双边沿信号转换为单边沿信号。
- 将转换为单端的rx_frame和rx_data送入Verilog逻辑进行组合便的到AD9361采集的数据。
数据发送路径
- FPGA内部将数据输出到dac_valid和dac_data。
- Verilog逻辑对dac_valid和dac_data进行拆分,得到ODDR上升沿时刻和下降沿时刻的值。
- 将拆分好的值送入FPGA的ODDR转换为双边沿信号。
- 将IDDR输出的双边沿信号送入FPGA的OBUFDS转换为差分信号。
- 将 OBUFDS输出的差分信号给AD9361。
常用的xilinx FPGA原语
IBUFDS
IBUFDS是xilinx FPGA的原语,用于将差分输入转换为单端输入

例化模板:
c
IBUFDS #(
.DIFF_TERM("FALSE"), // Differential Termination
.IBUF_LOW_PWR("TRUE"), // Low power="TRUE", Highest performance="FALSE"
.IOSTANDARD("DEFAULT") // Specify the input I/O standard
) IBUFDS_inst (
.O(O), // Buffer output
.I(I), // Diff_p buffer input (connect directly to top-level port)
.IB(IB) // Diff_n buffer input (connect directly to top-level port)
);
OBUFDS
OBUFDS是xilinx FPGA的原语,用于将单端输出转换为差分输出

例化模板:
c
OBUFDS #(
.IOSTANDARD("DEFAULT"), // Specify the output I/O standard
.SLEW("SLOW") // Specify the output slew rate
) OBUFDS_inst (
.O(O), // Diff_p output (connect directly to top-level port)
.OB(OB), // Diff_n output (connect directly to top-level port)
.I(I) // Buffer input
);
IDDR
IDDR是xilinx FPGA的原语,用于将双边沿信号转换为单边沿信号。
OPPOSITE_EDGE模式:

SAME_EDGE模式:

SAME_EDGE_PIPELINED模式:

原语框图:

信号定义:

参数定义:

例化模板:
c
IDDR #(
.DDR_CLK_EDGE("OPPOSITE_EDGE"), // "OPPOSITE_EDGE", "SAME_EDGE"
// or "SAME_EDGE_PIPELINED"
.INIT_Q1(1'b0), // Initial value of Q1: 1'b0 or 1'b1
.INIT_Q2(1'b0), // Initial value of Q2: 1'b0 or 1'b1
.SRTYPE("SYNC") // Set/Reset type: "SYNC" or "ASYNC"
) IDDR_inst (
.Q1(Q1), // 1-bit output for positive edge of clock
.Q2(Q2), // 1-bit output for negative edge of clock
.C(C), // 1-bit clock input
.CE(CE), // 1-bit clock enable input
.D(D), // 1-bit DDR data input
.R(R), // 1-bit reset
.S(S) // 1-bit set
);
ODDR
ODDR是xilinx FPGA的原语,用于将单边沿信号转换为双边沿信号。
OPPOSITE_EDGE模式:

SAME_EDGE模式:

原语框图:

信号定义:

参数定义:

例化模板:
c
ODDR #(
.DDR_CLK_EDGE("OPPOSITE_EDGE"), // "OPPOSITE_EDGE" or "SAME_EDGE"
.INIT(1'b0), // Initial value of Q: 1'b0 or 1'b1
.SRTYPE("SYNC") // Set/Reset type: "SYNC" or "ASYNC"
) ODDR_inst (
.Q(Q), // 1-bit DDR output
.C(C), // 1-bit clock input
.CE(CE), // 1-bit clock enable input
.D1(D1), // 1-bit data input (positive edge)
.D2(D2), // 1-bit data input (negative edge)
.R(R), // 1-bit reset
.S(S) // 1-bit set
);
IDELAYE2
IDELAYE2是xilinx FPGA的原语,用于实现IO输入延迟。
原语框图:

信号定义:

参数定义:

例化模板:
c
(* IODELAY_GROUP = <iodelay_group_name> *) // Specifies group name for associated IDELAYs/ODELAYs and IDELAYCTRL
IDELAYE2 #(
.CINVCTRL_SEL("FALSE"), // Enable dynamic clock inversion (FALSE, TRUE)
.DELAY_SRC("IDATAIN"), // Delay input (IDATAIN, DATAIN)
.HIGH_PERFORMANCE_MODE("FALSE"), // Reduced jitter ("TRUE"), Reduced power ("FALSE")
.IDELAY_TYPE("FIXED"), // 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("DATA") // DATA, CLOCK input signal
)
IDELAYE2_inst (
.CNTVALUEOUT(CNTVALUEOUT), // 5-bit output: Counter value output
.DATAOUT(DATAOUT), // 1-bit output: Delayed data output
.C(C), // 1-bit input: Clock input
.CE(CE), // 1-bit input: Active high enable increment/decrement input
.CINVCTRL(CINVCTRL), // 1-bit input: Dynamic clock inversion input
.CNTVALUEIN(CNTVALUEIN), // 5-bit input: Counter value input
.DATAIN(DATAIN), // 1-bit input: Internal delay data input
.IDATAIN(IDATAIN), // 1-bit input: Data input from the I/O
.INC(INC), // 1-bit input: Increment / Decrement tap delay input
.LD(LD), // 1-bit input: Load IDELAY_VALUE input
.LDPIPEEN(LDPIPEEN), // 1-bit input: Enable PIPELINE register to load data input
.REGRST(REGRST) // 1-bit input: Active-high reset tap-delay input
);
IDELAYCTRL
IDELAYCTRL是xilinx FPGA的原语,用于校准同一个IODELAY_GROUP中的IDELAYE2或ODELAYE2。
原语框图:

例化模板:
c
(* IODELAY_GROUP = <iodelay_group_name> *) // Specifies group name for associated IDELAYs/ODELAYs and IDELAYCTRL
IDELAYCTRL IDELAYCTRL_inst (
.RDY(RDY), // 1-bit output: Ready output
.REFCLK(REFCLK), // 1-bit input: Reference clock input
.RST(RST) // 1-bit input: Active high reset input
);
编码和仿真
编写1T1R模式的接口
实现1R1T模式下的LVDS时序,对于差分信号在分析时序时考虑P端即可。


c
`timescale 1ns / 1ps
module ad9361_interface_1r1t(
//idelay2接口
input wire idelay_ld_clk , //配置idelay延时参数的时钟
input wire [6:0] idelay_ld , //指示idelay_value作用于那个idelay,从0~6依次是rx_frame_in、rx_data_in[0]~rx_data_in[5]
input wire [4:0] idelay_value , //idelay的延时参数
//delay-cntrl接口
input wire delay_cntrl_rst , //delay-cntrl复位
input wire delay_cntrl_200m_clk , //delay-cntrl工作时钟,必须为200MHZ
output wire delay_cntrl_locked , //delay-cntrl锁定指示
//AD963x接收路径信号
input wire rx_clk_in_p , //ADC通道的时钟
input wire rx_clk_in_n , //
input wire rx_frame_in_p , //ADC通道的帧同步
input wire rx_frame_in_n , //
input wire [5:0] rx_data_in_p , //ADC通道的数据
input wire [5:0] rx_data_in_n , //
//AD963x发送路径信号
output wire tx_clk_out_p , //DAC通道的时钟,rx_clk_in的反馈版本
output wire tx_clk_out_n , //
output wire tx_frame_out_p , //DAC通道的帧同步
output wire tx_frame_out_n , //
output wire [5:0] tx_data_out_p , //DAC通道的数据
output wire [5:0] tx_data_out_n , //
//IQ数据域控制和时钟信号
output wire data_clk , //ADC和DAC数据参考时钟,同步到rx_clk_in
input wire data_clk_ce , //使能数据参考时钟
//ADC IQ数据
output reg adc_data_valid = 1'b0 , //ADC数据流效标志
output reg [15:0] adc_data_i1 = 16'b0 , //ADC CH1数据,为兼容AXI标准,所以采用16bit位宽,实际只使用了低12位
output reg [15:0] adc_data_q1 = 16'b0 , //
output reg adc_frame_state = 1'b0 , //ADC数据帧状态,检测到异常时为1
//DAC IQ数据
input wire dac_data_valid , //DAC数据流效标志
input wire [15:0] dac_data_i1 , //DAC CH1数据,为兼容AXI标准,所以采用16bit位宽,实际只使用了低12位
input wire [15:0] dac_data_q1 //
);
//定义IODELAY组
parameter AD936X_IDELAY_GROUP = "ad936x_1r1t_interface_delay_group";
//例化IDELAYCTRL,用于校准《AD936X_IDELAY_GROUP》组中的IDELAYE2和ODELAYE2
(* IODELAY_GROUP = AD936X_IDELAY_GROUP *)
IDELAYCTRL #(
.SIM_DEVICE("7SERIES")
) idelay_ctrl_inst (
.RST(delay_cntrl_rst), // 1-bit input: Active high reset input
.REFCLK(delay_cntrl_200m_clk), // 1-bit input: Reference clock input
.RDY(delay_cntrl_locked) // 1-bit output: Ready output
);
/******************************** 接收路径 ********************************/
//经过IBUFDS转换为单端的rx_clk
wire rx_clk_ibuf;
//经过BUFGCE后的rx_clk
wire rx_clk_bufg;
//经过IBUFDS转换为单端的rx_frame
wire rx_frame_ibuf;
//经过IDELAYE2延时后的rx_frame
wire rx_frame_idelay;
//经过IDDR将双边沿转换为单边沿的rx_frame,其中rx_frame_iddr_pos是上升沿、rx_frame_iddr_neg是下降沿
wire rx_frame_iddr_pos;
wire rx_frame_iddr_neg;
//rx_frame_iddr_neg延迟一拍,用于将上一个时钟的下降沿和当前时钟的上升沿拼接到一起
reg rx_frame_iddr_neg_delay = 1'b0;
//经过IBUFDS转换为单端的rx_data
wire [5:0] rx_data_ibuf;
//经过IDELAYE2延时后的rx_data
wire [5:0] rx_data_idelay;
//经过IDDR将双边沿转换为单边沿的rx_data,其中rx_data_iddr_pos是上升沿、rx_data_iddr_neg是下降沿
wire [5:0] rx_data_iddr_pos;
wire [5:0] rx_data_iddr_neg;
//rx_data_iddr_neg延迟一拍,用于将上一个时钟的下降沿和当前时钟的上升沿拼接到一起
reg [5:0] rx_data_iddr_neg_delay = 6'b0;
//rx_frame移位寄存器,用于暂存ADC传输过来的rx_frame信号
reg [3:0] rx_frame_shift = 4'b0;
//rx_data移位寄存器,用于暂存ADC传输过来的rx_data信号
reg [23:0] rx_data_shift = 24'b0;
//////////////时钟信号处理
//将差分的rx_clk转换为单端信号
IBUFDS #(
.DIFF_TERM("FALSE"), // Differential Termination
.IBUF_LOW_PWR("TRUE"), // Low power="TRUE", Highest performance="FALSE"
.IOSTANDARD("DEFAULT") // Specify the input I/O standard
) IBUFDS_rx_data_clk_inst (
.O(rx_clk_ibuf), // Buffer output
.I(rx_clk_in_p), // Diff_p buffer input (connect directly to top-level port)
.IB(rx_clk_in_n) // Diff_n buffer input (connect directly to top-level port)
);
//通过BUFGCE控制rx_clk
BUFGCE BUFGCE_rx_data_clk_inst (
.O(rx_clk_bufg), // 1-bit output: Clock output
.CE(data_clk_ce), // 1-bit input: Clock enable input for I0
.I(rx_clk_ibuf) // 1-bit input: Primary clock
);
//ADC和DAC数据参考时钟
assign data_clk = rx_clk_bufg;
//////////////将rx_frame_in_p/n和rx_data_in_p/n[5:0]通过FPGA的IBUFDS将差分信号转换为单端信号
//将rx_frame转换为单端
IBUFDS #(
.DIFF_TERM("FALSE"), // Differential Termination
.IBUF_LOW_PWR("TRUE"), // Low power="TRUE", Highest performance="FALSE"
.IOSTANDARD("DEFAULT") // Specify the input I/O standard
) IBUFDS_rx_frame_inst (
.O(rx_frame_ibuf), // Buffer output
.I(rx_frame_in_p), // Diff_p buffer input (connect directly to top-level port)
.IB(rx_frame_in_n) // Diff_n buffer input (connect directly to top-level port)
);
//使用循环生成语句生成将rx_data_in_p/n[5:0]转换为单端的逻辑代码
generate
genvar loop1;
for(loop1 = 0; loop1 < 6; loop1 = loop1 + 1)begin
//将rx_data[loop]转换为单端
IBUFDS #(
.DIFF_TERM("FALSE"), // Differential Termination
.IBUF_LOW_PWR("TRUE"), // Low power="TRUE", Highest performance="FALSE"
.IOSTANDARD("DEFAULT") // Specify the input I/O standard
) IBUFDS_rx_data_inst (
.O(rx_data_ibuf[loop1]), // Buffer output
.I(rx_data_in_p[loop1]), // Diff_p buffer input (connect directly to top-level port)
.IB(rx_data_in_n[loop1]) // Diff_n buffer input (connect directly to top-level port)
);
end
endgenerate
//////////////将转换为单端的rx_frame和rx_data通过FPGA的Idelay进行延时,以弥补时序误差
//对rx_frame进行延时
(* IODELAY_GROUP = AD936X_IDELAY_GROUP *)
IDELAYE2 #(
.CINVCTRL_SEL("FALSE"), // Enable dynamic clock inversion (FALSE, TRUE)
.DELAY_SRC("IDATAIN"), // Delay input (IDATAIN, DATAIN)
.HIGH_PERFORMANCE_MODE("FALSE"), // 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("DATA") // DATA, CLOCK input signal
) IDELAYE2_rx_frame_inst (
.CNTVALUEOUT(), // 5-bit output: Counter value output
.DATAOUT(rx_frame_idelay), // 1-bit output: Delayed data output
.C(idelay_ld_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(idelay_value), // 5-bit input: Counter value input
.DATAIN(1'b0), // 1-bit input: Internal delay data input
.IDATAIN(rx_frame_ibuf), // 1-bit input: Data input from the I/O
.INC(1'b0), // 1-bit input: Increment / Decrement tap delay input
.LD(idelay_ld[6]), // 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
);
//使用循环生成语句生成rx_data_in_p/n[5:0]延时的逻辑代码
generate
genvar loop2;
for(loop2 = 0; loop2 < 6; loop2 = loop2 + 1)begin
//对rx_data[loop]进行延时
(* IODELAY_GROUP = AD936X_IDELAY_GROUP *)
IDELAYE2 #(
.CINVCTRL_SEL("FALSE"), // Enable dynamic clock inversion (FALSE, TRUE)
.DELAY_SRC("IDATAIN"), // Delay input (IDATAIN, DATAIN)
.HIGH_PERFORMANCE_MODE("FALSE"), // 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("DATA") // DATA, CLOCK input signal
)IDELAYE2_rx_data_inst (
.CNTVALUEOUT(), // 5-bit output: Counter value output
.DATAOUT(rx_data_idelay[loop2]), // 1-bit output: Delayed data output
.C(idelay_ld_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(idelay_value), // 5-bit input: Counter value input
.DATAIN(1'b0), // 1-bit input: Internal delay data input
.IDATAIN(rx_data_ibuf[loop2]), // 1-bit input: Data input from the I/O
.INC(1'b0), // 1-bit input: Increment / Decrement tap delay input
.LD(idelay_ld[loop2]), // 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
);
end
endgenerate
//////////////将经过Idelay进行延时后的rx_frame和rx_data通过FPGA的IDDR将双边沿信号转换为单边沿信号
//将rx_frame转换为单边沿
IDDR #(
.DDR_CLK_EDGE("SAME_EDGE_PIPELINED"), // "OPPOSITE_EDGE", "SAME_EDGE" or "SAME_EDGE_PIPELINED"
.INIT_Q1(1'b0), // Initial value of Q1: 1'b0 or 1'b1
.INIT_Q2(1'b0), // Initial value of Q2: 1'b0 or 1'b1
.SRTYPE("SYNC") // Set/Reset type: "SYNC" or "ASYNC"
) IDDR_rx_frame_inst (
.Q1(rx_frame_iddr_pos), // 1-bit output for positive edge of clock
.Q2(rx_frame_iddr_neg), // 1-bit output for negative edge of clock
.C(data_clk), // 1-bit clock input
.CE(1'b1), // 1-bit clock enable input
.D(rx_frame_idelay), // 1-bit DDR data input
.R(1'b0), // 1-bit reset
.S(1'b0) // 1-bit set
);
//使用循环生成语句生成rx_data_in_p/n[5:0]转换为单边沿的逻辑代码
generate
genvar loop3;
for(loop3 = 0; loop3 < 6; loop3 = loop3 + 1)begin
//将rx_data[loop]转换为单边沿
IDDR #(
.DDR_CLK_EDGE("SAME_EDGE_PIPELINED"), // "OPPOSITE_EDGE", "SAME_EDGE" or "SAME_EDGE_PIPELINED"
.INIT_Q1(1'b0), // Initial value of Q1: 1'b0 or 1'b1
.INIT_Q2(1'b0), // Initial value of Q2: 1'b0 or 1'b1
.SRTYPE("SYNC") // Set/Reset type: "SYNC" or "ASYNC"
) IDDR_rx_data_inst (
.Q1(rx_data_iddr_pos[loop3]), // 1-bit output for positive edge of clock
.Q2(rx_data_iddr_neg[loop3]), // 1-bit output for negative edge of clock
.C(data_clk), // 1-bit clock input
.CE(1'b1), // 1-bit clock enable input
.D(rx_data_idelay[loop3]), // 1-bit DDR data input
.R(1'b0), // 1-bit reset
.S(1'b0) // 1-bit set
);
end
endgenerate
//////////////将转换为单端的rx_frame和rx_data送入Verilog逻辑进行组合便的到AD9361采集的数据
//rx_frame_iddr_neg延迟一拍
always @(posedge data_clk) begin
rx_frame_iddr_neg_delay <= rx_frame_iddr_neg;
end
//rx_data_iddr_neg延迟一拍
always @(posedge data_clk) begin
rx_data_iddr_neg_delay <= rx_data_iddr_neg;
end
//对rx_frame进行移位寄存储,将上一个时钟的下降沿和当前时钟的上升沿拼接在一起,其中上一个时钟的下降沿对应的是I,当前时钟上升沿对应的是Q
always @(posedge data_clk) begin
rx_frame_shift <= {rx_frame_shift[1:0], rx_frame_iddr_neg_delay, rx_frame_iddr_pos};
end
//对rx_data进行移位寄存储,将上一个时钟的下降沿和当前时钟的上升沿拼接在一起,其中上一个时钟的下降沿对应的是I,当前时钟上升沿对应的是Q
always @(posedge data_clk) begin
rx_data_shift <= {rx_data_shift[11:0], rx_data_iddr_neg_delay, rx_data_iddr_pos};
end
//正常情况下1R1T模式rx_frame_shift只有4'b1100和4'b0011两种取值,其他均为异常
always @(posedge data_clk) begin
case(rx_frame_shift)
4'b1100, 4'b0011 : adc_frame_state <= 1'b0;
default : adc_frame_state <= 1'b1;
endcase
end
//在1R1T模式下rx_frame_shift为4'b1100表示接收完一帧ADC数据
//对rx_data_shift进行重新组帧,得到I1和Q1
//其中rx_data_shift[23:18]是I[11:6],其中rx_data_shift[17:12]是Q[11:6],rx_data_shift[11:6]是I[5:0],rx_data_shift[5:0]是Q[5:0]
always @(posedge data_clk) begin
if(rx_frame_shift == 4'b1100) begin
adc_data_valid <= 1'b1;
adc_data_i1 <= {{4{rx_data_shift[23]}}, rx_data_shift[23:18], rx_data_shift[11:6]};
adc_data_q1 <= {{4{rx_data_shift[17]}}, rx_data_shift[17:12], rx_data_shift[5:0]};
end
else begin
adc_data_valid <= 1'b0;
adc_data_i1 <= adc_data_i1;
adc_data_q1 <= adc_data_q1;
end
end
/******************************** 发送路径 ********************************/
//经过ODDR前的tx_frame,其中tx_frame_pos是上升沿、tx_frame_neg是下降沿
reg tx_frame_pos = 1'b0;
reg tx_frame_neg = 1'b0;
//经过ODDR后的tx_frame
wire tx_frame_oddr;
//经过ODDR前的tx_data,其中tx_data_pos是上升沿、tx_data_neg是下降沿
reg [5:0] tx_data_pos = 6'b0;
reg [5:0] tx_data_neg = 6'b0;
//经过ODDR后的tx_data
wire [5:0] tx_data_oddr;
//经过ODDR后的tx_clk
wire tx_clk_oddr;
//发送忙标志
reg tx_busy_flag = 1'b0;
//发送计数
reg [0:0] tx_count = 1'b0;
//暂存待发送数据
reg [11:0] tx_data_i1 = 12'b0;
reg [11:0] tx_data_q1 = 12'b0;
//////////////暂存待发送的数据
//控制发送标志,输入数据有效设置发送标志,发送完成清除发送标志,
always @(posedge data_clk) begin
if(dac_data_valid == 1'b1)
tx_busy_flag <= 1'b1;
else if(tx_count == 1'b1)
tx_busy_flag <= 1'b0;
end
//暂存待发送数据,第一次输入数据会进行暂存,上次输入数据发送完成同时刚好输入新数据也进行暂存
always @(posedge data_clk) begin
if(((dac_data_valid == 1'b1) && (tx_busy_flag == 1'b0)) || ((dac_data_valid == 1'b1) && (tx_count == 1'b1))) begin
tx_data_i1 <= dac_data_i1[11:0];
tx_data_q1 <= dac_data_q1[11:0];
end
end
//////////////发送流程控制
//进行发送计数,因为计数到0~1,且只有1位,所以采用溢出自动清零
always @(posedge data_clk) begin
if(tx_busy_flag == 1'b1)
tx_count <= tx_count + 1'b1;
else
tx_count <= 1'b0;
end
//进行数据发送
always @(posedge data_clk) begin
case ({tx_busy_flag, tx_count})
2'b10 : begin
tx_frame_pos <= 1'b1;
tx_frame_neg <= 1'b1;
tx_data_pos <= tx_data_i1[11:6];
tx_data_neg <= tx_data_q1[11:6];
end
2'b11 : begin
tx_frame_pos <= 1'b0;
tx_frame_neg <= 1'b0;
tx_data_pos <= tx_data_i1[5:0];
tx_data_neg <= tx_data_q1[5:0];
end
default : begin
tx_frame_pos <= 1'b0;
tx_frame_neg <= 1'b0;
tx_data_pos <= 6'b0;
tx_data_neg <= 6'b0;
end
endcase
end
//////////////将逻辑输出数据转换为双边沿
//data_clk通过ODDR输出,以保证延时一致
ODDR #(
.DDR_CLK_EDGE("SAME_EDGE"), // "OPPOSITE_EDGE" or "SAME_EDGE"
.INIT(1'b0), // Initial value of Q: 1'b0 or 1'b1
.SRTYPE("SYNC") // Set/Reset type: "SYNC" or "ASYNC"
) ODDR_tx_clk_inst (
.Q(tx_clk_oddr), // 1-bit DDR output
.C(data_clk), // 1-bit clock input
.CE(1'b1), // 1-bit clock enable input
.D1(1'b1), // 1-bit data input (positive edge)
.D2(1'b0), // 1-bit data input (negative edge)
.R(1'b0), // 1-bit reset
.S(1'b0) // 1-bit set
);
//tx_frame通过ODDR转换为双边沿
ODDR #(
.DDR_CLK_EDGE("SAME_EDGE"), // "OPPOSITE_EDGE" or "SAME_EDGE"
.INIT(1'b0), // Initial value of Q: 1'b0 or 1'b1
.SRTYPE("SYNC") // Set/Reset type: "SYNC" or "ASYNC"
) ODDR_tx_frame_inst (
.Q(tx_frame_oddr), // 1-bit DDR output
.C(data_clk), // 1-bit clock input
.CE(1'b1), // 1-bit clock enable input
.D1(tx_frame_pos), // 1-bit data input (positive edge)
.D2(tx_frame_neg), // 1-bit data input (negative edge)
.R(1'b0), // 1-bit reset
.S(1'b0) // 1-bit set
);
//通过循环生成语句生成将tx_data[5:0]转换为双边沿的代码
generate
genvar loop4;
for(loop4 = 0; loop4 < 6; loop4 = loop4 + 1)begin
//tx_data[loop]通过ODDR转换为双边沿
ODDR #(
.DDR_CLK_EDGE("SAME_EDGE"), // "OPPOSITE_EDGE" or "SAME_EDGE"
.INIT(1'b0), // Initial value of Q: 1'b0 or 1'b1
.SRTYPE("SYNC") // Set/Reset type: "SYNC" or "ASYNC"
) ODDR_tx_data_inst (
.Q(tx_data_oddr[loop4]), // 1-bit DDR output
.C(data_clk), // 1-bit clock input
.CE(1'b1), // 1-bit clock enable input
.D1(tx_data_pos[loop4]), // 1-bit data input (positive edge)
.D2(tx_data_neg[loop4]), // 1-bit data input (negative edge)
.R(1'b0), // 1-bit reset
.S(1'b0) // 1-bit set
);
end
endgenerate
//////////////将单端转换为差分
//将tx_clk通过OBUFDS转换为差分
OBUFDS #(
.IOSTANDARD("DEFAULT"), // Specify the output I/O standard
.SLEW("SLOW") // Specify the output slew rate
) OBUFDS_tx_clk_inst (
.O(tx_clk_out_p), // Diff_p output (connect directly to top-level port)
.OB(tx_clk_out_n), // Diff_n output (connect directly to top-level port)
.I(tx_clk_oddr) // Buffer input
);
//将tx_frame通过OBUFDS转换为差分
OBUFDS #(
.IOSTANDARD("DEFAULT"), // Specify the output I/O standard
.SLEW("SLOW") // Specify the output slew rate
) OBUFDS_tx_frame_inst (
.O(tx_frame_out_p), // Diff_p output (connect directly to top-level port)
.OB(tx_frame_out_n), // Diff_n output (connect directly to top-level port)
.I(tx_frame_oddr) // Buffer input
);
//通过循环生成语句生成将tx_data[5:0]转换为差分的代码
generate
genvar loop5;
for(loop5 = 0; loop5 < 6; loop5 = loop5 + 1)begin
//tx_data[loop]通过OBUFDS转换为差分
OBUFDS #(
.IOSTANDARD("DEFAULT"), // Specify the output I/O standard
.SLEW("SLOW") // Specify the output slew rate
) OBUFDS_tx_data_inst (
.O(tx_data_out_p[loop5]), // Diff_p output (connect directly to top-level port)
.OB(tx_data_out_n[loop5]), // Diff_n output (connect directly to top-level port)
.I(tx_data_oddr[loop5]) // Buffer input
);
end
endgenerate
endmodule
编写2T2R模式的接口
实现2R2T模式下的LVDS时序,对于差分信号在分析时序时考虑P端即可


c
`timescale 1ns / 1ps
module ad9361_interface_2r2t(
//idelay2接口
input wire idelay_ld_clk , //配置idelay延时参数的时钟
input wire [6:0] idelay_ld , //指示idelay_value作用于那个idelay,从0~6依次是rx_frame_in、rx_data_in[0]~rx_data_in[5]
input wire [4:0] idelay_value , //idelay的延时参数
//delay-cntrl接口
input wire delay_cntrl_rst , //delay-cntrl复位
input wire delay_cntrl_200m_clk , //delay-cntrl工作时钟,必须为200MHZ
output wire delay_cntrl_locked , //delay-cntrl锁定指示
//AD963x接收路径信号
input wire rx_clk_in_p , //ADC通道的时钟
input wire rx_clk_in_n , //
input wire rx_frame_in_p , //ADC通道的帧同步
input wire rx_frame_in_n , //
input wire [5:0] rx_data_in_p , //ADC通道的数据
input wire [5:0] rx_data_in_n , //
//AD963x发送路径信号
output wire tx_clk_out_p , //DAC通道的时钟,rx_clk_in的反馈版本
output wire tx_clk_out_n , //
output wire tx_frame_out_p , //DAC通道的帧同步
output wire tx_frame_out_n , //
output wire [5:0] tx_data_out_p , //DAC通道的数据
output wire [5:0] tx_data_out_n , //
//IQ数据域控制和时钟信号
output wire data_clk , //ADC和DAC数据参考时钟,同步到rx_clk_in
input wire data_clk_ce , //使能数据参考时钟
//ADC IQ数据
output reg adc_data_valid = 1'b0 , //ADC数据流效标志
output reg [15:0] adc_data_i1 = 16'b0 , //ADC CH1数据,为兼容AXI标准,所以采用16bit位宽,实际只使用了低12位
output reg [15:0] adc_data_q1 = 16'b0 , //
output reg [15:0] adc_data_i2 = 16'b0 , //ADC CH2数据,为兼容AXI标准,所以采用16bit位宽,实际只使用了低12位
output reg [15:0] adc_data_q2 = 16'b0 , //
output reg adc_frame_state = 1'b0 , //ADC数据帧状态,检测到异常时为1
//DAC IQ数据
input wire dac_data_valid , //DAC数据流效标志
input wire [15:0] dac_data_i1 , //DAC CH1数据,为兼容AXI标准,所以采用16bit位宽,实际只使用了低12位
input wire [15:0] dac_data_q1 , //
input wire [15:0] dac_data_i2 , //DAC CH2数据,为兼容AXI标准,所以采用16bit位宽,实际只使用了低12位
input wire [15:0] dac_data_q2 //
);
//定义IODELAY组
parameter AD936X_IDELAY_GROUP = "ad936x_2r2t_interface_delay_group";
//例化IDELAYCTRL,用于校准《AD936X_IDELAY_GROUP》组中的IDELAYE2和ODELAYE2
(* IODELAY_GROUP = AD936X_IDELAY_GROUP *)
IDELAYCTRL #(
.SIM_DEVICE("7SERIES")
) idelay_ctrl_inst (
.RST(delay_cntrl_rst), // 1-bit input: Active high reset input
.REFCLK(delay_cntrl_200m_clk), // 1-bit input: Reference clock input
.RDY(delay_cntrl_locked) // 1-bit output: Ready output
);
/******************************** 接收路径 ********************************/
//经过IBUFDS转换为单端的rx_clk
wire rx_clk_ibuf;
//经过BUFGCE后的rx_clk
wire rx_clk_bufg;
//经过IBUFDS转换为单端的rx_frame
wire rx_frame_ibuf;
//经过IDELAYE2延时后的rx_frame
wire rx_frame_idelay;
//经过IDDR将双边沿转换为单边沿的rx_frame,其中rx_frame_iddr_pos是上升沿、rx_frame_iddr_neg是下降沿
wire rx_frame_iddr_pos;
wire rx_frame_iddr_neg;
//rx_frame_iddr_neg延迟一拍,用于将上一个时钟的下降沿和当前时钟的上升沿拼接到一起
reg rx_frame_iddr_neg_delay = 1'b0;
//经过IBUFDS转换为单端的rx_data
wire [5:0] rx_data_ibuf;
//经过IDELAYE2延时后的rx_data
wire [5:0] rx_data_idelay;
//经过IDDR将双边沿转换为单边沿的rx_data,其中rx_data_iddr_pos是上升沿、rx_data_iddr_neg是下降沿
wire [5:0] rx_data_iddr_pos;
wire [5:0] rx_data_iddr_neg;
//rx_data_iddr_neg延迟一拍,用于将上一个时钟的下降沿和当前时钟的上升沿拼接到一起
reg [5:0] rx_data_iddr_neg_delay = 6'b0;
//rx_frame移位寄存器,用于暂存ADC传输过来的rx_frame信号
reg [7:0] rx_frame_shift = 8'b0;
//rx_data移位寄存器,用于暂存ADC传输过来的rx_data信号
reg [47:0] rx_data_shift = 48'b0;
//////////////时钟信号处理
//将差分的rx_clk转换为单端信号
IBUFDS #(
.DIFF_TERM("FALSE"), // Differential Termination
.IBUF_LOW_PWR("TRUE"), // Low power="TRUE", Highest performance="FALSE"
.IOSTANDARD("DEFAULT") // Specify the input I/O standard
) IBUFDS_rx_data_clk_inst (
.O(rx_clk_ibuf), // Buffer output
.I(rx_clk_in_p), // Diff_p buffer input (connect directly to top-level port)
.IB(rx_clk_in_n) // Diff_n buffer input (connect directly to top-level port)
);
//通过BUFGCE控制rx_clk
BUFGCE BUFGCE_rx_data_clk_inst (
.O(rx_clk_bufg), // 1-bit output: Clock output
.CE(data_clk_ce), // 1-bit input: Clock enable input for I0
.I(rx_clk_ibuf) // 1-bit input: Primary clock
);
//ADC和DAC数据参考时钟
assign data_clk = rx_clk_bufg;
//////////////将rx_frame_in_p/n和rx_data_in_p/n[5:0]通过FPGA的IBUFDS将差分信号转换为单端信号
//将rx_frame转换为单端
IBUFDS #(
.DIFF_TERM("FALSE"), // Differential Termination
.IBUF_LOW_PWR("TRUE"), // Low power="TRUE", Highest performance="FALSE"
.IOSTANDARD("DEFAULT") // Specify the input I/O standard
) IBUFDS_rx_frame_inst (
.O(rx_frame_ibuf), // Buffer output
.I(rx_frame_in_p), // Diff_p buffer input (connect directly to top-level port)
.IB(rx_frame_in_n) // Diff_n buffer input (connect directly to top-level port)
);
//使用循环生成语句生成将rx_data_in_p/n[5:0]转换为单端的逻辑代码
generate
genvar loop1;
for(loop1 = 0; loop1 < 6; loop1 = loop1 + 1)begin
//将rx_data[loop]转换为单端
IBUFDS #(
.DIFF_TERM("FALSE"), // Differential Termination
.IBUF_LOW_PWR("TRUE"), // Low power="TRUE", Highest performance="FALSE"
.IOSTANDARD("DEFAULT") // Specify the input I/O standard
) IBUFDS_rx_data_inst (
.O(rx_data_ibuf[loop1]), // Buffer output
.I(rx_data_in_p[loop1]), // Diff_p buffer input (connect directly to top-level port)
.IB(rx_data_in_n[loop1]) // Diff_n buffer input (connect directly to top-level port)
);
end
endgenerate
//////////////将转换为单端的rx_frame和rx_data通过FPGA的Idelay进行延时,以弥补时序误差
//对rx_frame进行延时
(* IODELAY_GROUP = AD936X_IDELAY_GROUP *)
IDELAYE2 #(
.CINVCTRL_SEL("FALSE"), // Enable dynamic clock inversion (FALSE, TRUE)
.DELAY_SRC("IDATAIN"), // Delay input (IDATAIN, DATAIN)
.HIGH_PERFORMANCE_MODE("FALSE"), // 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("DATA") // DATA, CLOCK input signal
) IDELAYE2_rx_frame_inst (
.CNTVALUEOUT(), // 5-bit output: Counter value output
.DATAOUT(rx_frame_idelay), // 1-bit output: Delayed data output
.C(idelay_ld_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(idelay_value), // 5-bit input: Counter value input
.DATAIN(1'b0), // 1-bit input: Internal delay data input
.IDATAIN(rx_frame_ibuf), // 1-bit input: Data input from the I/O
.INC(1'b0), // 1-bit input: Increment / Decrement tap delay input
.LD(idelay_ld[6]), // 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
);
//使用循环生成语句生成rx_data_in_p/n[5:0]延时的逻辑代码
generate
genvar loop2;
for(loop2 = 0; loop2 < 6; loop2 = loop2 + 1)begin
//对rx_data[loop]进行延时
(* IODELAY_GROUP = AD936X_IDELAY_GROUP *)
IDELAYE2 #(
.CINVCTRL_SEL("FALSE"), // Enable dynamic clock inversion (FALSE, TRUE)
.DELAY_SRC("IDATAIN"), // Delay input (IDATAIN, DATAIN)
.HIGH_PERFORMANCE_MODE("FALSE"), // 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("DATA") // DATA, CLOCK input signal
)IDELAYE2_rx_data_inst (
.CNTVALUEOUT(), // 5-bit output: Counter value output
.DATAOUT(rx_data_idelay[loop2]), // 1-bit output: Delayed data output
.C(idelay_ld_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(idelay_value), // 5-bit input: Counter value input
.DATAIN(1'b0), // 1-bit input: Internal delay data input
.IDATAIN(rx_data_ibuf[loop2]), // 1-bit input: Data input from the I/O
.INC(1'b0), // 1-bit input: Increment / Decrement tap delay input
.LD(idelay_ld[loop2]), // 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
);
end
endgenerate
//////////////将经过Idelay进行延时后的rx_frame和rx_data通过FPGA的IDDR将双边沿信号转换为单边沿信号
//将rx_frame转换为单边沿
IDDR #(
.DDR_CLK_EDGE("SAME_EDGE_PIPELINED"), // "OPPOSITE_EDGE", "SAME_EDGE" or "SAME_EDGE_PIPELINED"
.INIT_Q1(1'b0), // Initial value of Q1: 1'b0 or 1'b1
.INIT_Q2(1'b0), // Initial value of Q2: 1'b0 or 1'b1
.SRTYPE("SYNC") // Set/Reset type: "SYNC" or "ASYNC"
) IDDR_rx_frame_inst (
.Q1(rx_frame_iddr_pos), // 1-bit output for positive edge of clock
.Q2(rx_frame_iddr_neg), // 1-bit output for negative edge of clock
.C(data_clk), // 1-bit clock input
.CE(1'b1), // 1-bit clock enable input
.D(rx_frame_idelay), // 1-bit DDR data input
.R(1'b0), // 1-bit reset
.S(1'b0) // 1-bit set
);
//使用循环生成语句生成rx_data_in_p/n[5:0]转换为单边沿的逻辑代码
generate
genvar loop3;
for(loop3 = 0; loop3 < 6; loop3 = loop3 + 1)begin
//将rx_data[loop]转换为单边沿
IDDR #(
.DDR_CLK_EDGE("SAME_EDGE_PIPELINED"), // "OPPOSITE_EDGE", "SAME_EDGE" or "SAME_EDGE_PIPELINED"
.INIT_Q1(1'b0), // Initial value of Q1: 1'b0 or 1'b1
.INIT_Q2(1'b0), // Initial value of Q2: 1'b0 or 1'b1
.SRTYPE("SYNC") // Set/Reset type: "SYNC" or "ASYNC"
) IDDR_rx_data_inst (
.Q1(rx_data_iddr_pos[loop3]), // 1-bit output for positive edge of clock
.Q2(rx_data_iddr_neg[loop3]), // 1-bit output for negative edge of clock
.C(data_clk), // 1-bit clock input
.CE(1'b1), // 1-bit clock enable input
.D(rx_data_idelay[loop3]), // 1-bit DDR data input
.R(1'b0), // 1-bit reset
.S(1'b0) // 1-bit set
);
end
endgenerate
//////////////将转换为单端的rx_frame和rx_data送入Verilog逻辑进行组合便的到AD9361采集的数据
//rx_frame_iddr_neg延迟一拍
always @(posedge data_clk) begin
rx_frame_iddr_neg_delay <= rx_frame_iddr_neg;
end
//rx_data_iddr_neg延迟一拍
always @(posedge data_clk) begin
rx_data_iddr_neg_delay <= rx_data_iddr_neg;
end
//对rx_frame进行移位寄存储,将上一个时钟的下降沿和当前时钟的上升沿拼接在一起,其中上一个时钟的下降沿对应的是I,当前时钟上升沿对应的是Q
always @(posedge data_clk) begin
rx_frame_shift <= {rx_frame_shift[5:0], rx_frame_iddr_neg_delay, rx_frame_iddr_pos};
end
//对rx_data进行移位寄存储,将上一个时钟的下降沿和当前时钟的上升沿拼接在一起,其中上一个时钟的下降沿对应的是I,当前时钟上升沿对应的是Q
always @(posedge data_clk) begin
rx_data_shift <= {rx_data_shift[35:0], rx_data_iddr_neg_delay, rx_data_iddr_pos};
end
//2R2T模式下rx_frame_shift只有8'b00000011、8'b00001111、8'b00111100、8'b11110000、'b11000011这几种取值,其他均为异常
always @(posedge data_clk) begin
case(rx_frame_shift)
8'b00000011, 8'b00001111, 8'b00111100, 8'b11110000, 8'b11000011 : adc_frame_state <= 1'b0;
default : adc_frame_state <= 1'b1;
endcase
end
//在2R2T模式下rx_frame_shift为8'b11110000表示接收完一帧ADC数据
//对rx_data_shift进行重新组帧,得到I1、Q1、I2、Q2
//其中rx_data_shift[47:42]是I1[11:6],其中rx_data_shift[41:36]是Q1[11:6],rx_data_shift[35:30]是I1[5:0],rx_data_shift[29:24]是Q1[5:0]
//其中rx_data_shift[23:18]是I2[11:6],其中rx_data_shift[17:12]是Q2[11:6],rx_data_shift[11:6]是I2[5:0],rx_data_shift[5:0]是Q2[5:0]
always @(posedge data_clk) begin
if(rx_frame_shift == 8'b11110000) begin
adc_data_valid <= 1'b1;
adc_data_i1 <= {{4{rx_data_shift[47]}}, rx_data_shift[47:42], rx_data_shift[35:30]};
adc_data_q1 <= {{4{rx_data_shift[41]}}, rx_data_shift[41:36], rx_data_shift[29:24]};
adc_data_i2 <= {{4{rx_data_shift[23]}}, rx_data_shift[23:18], rx_data_shift[11:6]};
adc_data_q2 <= {{4{rx_data_shift[17]}}, rx_data_shift[17:12], rx_data_shift[5:0]};
end
else begin
adc_data_valid <= 1'b0;
adc_data_i1 <= adc_data_i1;
adc_data_q1 <= adc_data_q1;
adc_data_i2 <= adc_data_i2;
adc_data_q2 <= adc_data_q2;
end
end
/******************************** 发送路径 ********************************/
//经过ODDR前的tx_frame,其中tx_frame_pos是上升沿、tx_frame_neg是下降沿
reg tx_frame_pos = 1'b0;
reg tx_frame_neg = 1'b0;
//经过ODDR后的tx_frame
wire tx_frame_oddr;
//经过ODDR前的tx_data,其中tx_data_pos是上升沿、tx_data_neg是下降沿
reg [5:0] tx_data_pos = 6'b0;
reg [5:0] tx_data_neg = 6'b0;
//经过ODDR后的tx_data
wire [5:0] tx_data_oddr;
//经过ODDR后的tx_clk
wire tx_clk_oddr;
//发送忙标志
reg tx_busy_flag = 1'b0;
//发送计数
reg [1:0] tx_count = 2'b0;
//暂存待发送数据
reg [11:0] tx_data_i1 = 12'b0;
reg [11:0] tx_data_q1 = 12'b0;
reg [11:0] tx_data_i2 = 12'b0;
reg [11:0] tx_data_q2 = 12'b0;
//////////////暂存待发送的数据
//控制发送标志,输入数据有效设置发送标志,发送完成清除发送标志,
always @(posedge data_clk) begin
if(dac_data_valid == 1'b1)
tx_busy_flag <= 1'b1;
else if(tx_count == 2'b11)
tx_busy_flag <= 1'b0;
end
//暂存待发送数据,第一次输入数据会进行暂存,上次输入数据发送完成同时刚好输入新数据也进行暂存
always @(posedge data_clk) begin
if(((dac_data_valid == 1'b1) && (tx_busy_flag == 1'b0)) || ((dac_data_valid == 1'b1) && (tx_count == 2'b11))) begin
tx_data_i1 <= dac_data_i1[11:0];
tx_data_q1 <= dac_data_q1[11:0];
tx_data_i2 <= dac_data_i2[11:0];
tx_data_q2 <= dac_data_q2[11:0];
end
end
//////////////发送流程控制
//进行发送计数,因为计数到0~3,且只有2位,所以采用溢出自动清零
always @(posedge data_clk) begin
if(tx_busy_flag == 1'b1)
tx_count <= tx_count + 2'b1;
else
tx_count <= 2'b0;
end
//进行数据发送
always @(posedge data_clk) begin
case ({tx_busy_flag, tx_count})
3'b100 : begin
tx_frame_pos <= 1'b1;
tx_frame_neg <= 1'b1;
tx_data_pos <= tx_data_i1[11:6];
tx_data_neg <= tx_data_q1[11:6];
end
3'b101 : begin
tx_frame_pos <= 1'b1;
tx_frame_neg <= 1'b1;
tx_data_pos <= tx_data_i1[5:0];
tx_data_neg <= tx_data_q1[5:0];
end
3'b110 : begin
tx_frame_pos <= 1'b0;
tx_frame_neg <= 1'b0;
tx_data_pos <= tx_data_i2[11:6];
tx_data_neg <= tx_data_q2[11:6];
end
3'b111 : begin
tx_frame_pos <= 1'b0;
tx_frame_neg <= 1'b0;
tx_data_pos <= tx_data_i2[5:0];
tx_data_neg <= tx_data_q2[5:0];
end
default : begin
tx_frame_pos <= 1'b0;
tx_frame_neg <= 1'b0;
tx_data_pos <= 6'b0;
tx_data_neg <= 6'b0;
end
endcase
end
//////////////将逻辑输出数据转换为双边沿
//data_clk通过ODDR输出,以保证延时一致
ODDR #(
.DDR_CLK_EDGE("SAME_EDGE"), // "OPPOSITE_EDGE" or "SAME_EDGE"
.INIT(1'b0), // Initial value of Q: 1'b0 or 1'b1
.SRTYPE("SYNC") // Set/Reset type: "SYNC" or "ASYNC"
) ODDR_tx_clk_inst (
.Q(tx_clk_oddr), // 1-bit DDR output
.C(data_clk), // 1-bit clock input
.CE(1'b1), // 1-bit clock enable input
.D1(1'b1), // 1-bit data input (positive edge)
.D2(1'b0), // 1-bit data input (negative edge)
.R(1'b0), // 1-bit reset
.S(1'b0) // 1-bit set
);
//tx_frame通过ODDR转换为双边沿
ODDR #(
.DDR_CLK_EDGE("SAME_EDGE"), // "OPPOSITE_EDGE" or "SAME_EDGE"
.INIT(1'b0), // Initial value of Q: 1'b0 or 1'b1
.SRTYPE("SYNC") // Set/Reset type: "SYNC" or "ASYNC"
) ODDR_tx_frame_inst (
.Q(tx_frame_oddr), // 1-bit DDR output
.C(data_clk), // 1-bit clock input
.CE(1'b1), // 1-bit clock enable input
.D1(tx_frame_pos), // 1-bit data input (positive edge)
.D2(tx_frame_neg), // 1-bit data input (negative edge)
.R(1'b0), // 1-bit reset
.S(1'b0) // 1-bit set
);
//通过循环生成语句生成将tx_data[loop]转换为双边沿的代码
generate
genvar loop4;
for(loop4 = 0; loop4 < 6; loop4 = loop4 + 1)begin
//tx_data[loop]通过ODDR转换为双边沿
ODDR #(
.DDR_CLK_EDGE("SAME_EDGE"), // "OPPOSITE_EDGE" or "SAME_EDGE"
.INIT(1'b0), // Initial value of Q: 1'b0 or 1'b1
.SRTYPE("SYNC") // Set/Reset type: "SYNC" or "ASYNC"
) ODDR_tx_data_inst (
.Q(tx_data_oddr[loop4]), // 1-bit DDR output
.C(data_clk), // 1-bit clock input
.CE(1'b1), // 1-bit clock enable input
.D1(tx_data_pos[loop4]), // 1-bit data input (positive edge)
.D2(tx_data_neg[loop4]), // 1-bit data input (negative edge)
.R(1'b0), // 1-bit reset
.S(1'b0) // 1-bit set
);
end
endgenerate
//////////////将单端转换为差分
//将tx_clk通过OBUFDS转换为差分
OBUFDS #(
.IOSTANDARD("DEFAULT"), // Specify the output I/O standard
.SLEW("SLOW") // Specify the output slew rate
) OBUFDS_tx_clk_inst (
.O(tx_clk_out_p), // Diff_p output (connect directly to top-level port)
.OB(tx_clk_out_n), // Diff_n output (connect directly to top-level port)
.I(tx_clk_oddr) // Buffer input
);
//将tx_frame通过OBUFDS转换为差分
OBUFDS #(
.IOSTANDARD("DEFAULT"), // Specify the output I/O standard
.SLEW("SLOW") // Specify the output slew rate
) OBUFDS_tx_frame_inst (
.O(tx_frame_out_p), // Diff_p output (connect directly to top-level port)
.OB(tx_frame_out_n), // Diff_n output (connect directly to top-level port)
.I(tx_frame_oddr) // Buffer input
);
//通过循环生成语句生成将tx_data[loop]转换为差分的代码
generate
genvar loop5;
for(loop5 = 0; loop5 < 6; loop5 = loop5 + 1)begin
//tx_data[loop]通过OBUFDS转换为差分
OBUFDS #(
.IOSTANDARD("DEFAULT"), // Specify the output I/O standard
.SLEW("SLOW") // Specify the output slew rate
) OBUFDS_tx_data_inst (
.O(tx_data_out_p[loop5]), // Diff_p output (connect directly to top-level port)
.OB(tx_data_out_n[loop5]), // Diff_n output (connect directly to top-level port)
.I(tx_data_oddr[loop5]) // Buffer input
);
end
endgenerate
endmodule
编写顶层接口
通过生成语句根据例化时传递的参数选择生成1R1T模式或2R2T模式的例化程序
c
`timescale 1ns / 1ps
module ad9361_interface #(
parameter MODE_1R1T = 1
)
(
//idelay2接口
input wire idelay_ld_clk , //配置idelay延时参数的时钟
input wire [6:0] idelay_ld , //指示idelay_value作用于那个idelay,从0~6依次是rx_frame_in、rx_data_in[0]~rx_data_in[5]
input wire [4:0] idelay_value , //idelay的延时参数
//delay-cntrl接口
input wire delay_cntrl_rst , //delay-cntrl复位
input wire delay_cntrl_200m_clk, //delay-cntrl工作时钟,必须为200MHZ
output wire delay_cntrl_locked , //delay-cntrl锁定指示
//AD963x接收路径信号
input wire rx_clk_in_p , //ADC通道的时钟
input wire rx_clk_in_n , //
input wire rx_frame_in_p , //ADC通道的帧同步
input wire rx_frame_in_n , //
input wire [5:0] rx_data_in_p , //ADC通道的数据
input wire [5:0] rx_data_in_n , //
//AD963x发送路径信号
output wire tx_clk_out_p , //DAC通道的时钟,rx_clk_in的反馈版本
output wire tx_clk_out_n , //
output wire tx_frame_out_p , //DAC通道的帧同步
output wire tx_frame_out_n , //
output wire [5:0] tx_data_out_p , //DAC通道的数据
output wire [5:0] tx_data_out_n , //
//IQ数据域控制和时钟信号
output wire data_clk , //ADC和DAC数据参考时钟,同步到rx_clk_in
input wire data_clk_ce , //使能数据参考时钟
//ADC IQ数据
output wire adc_data_valid , //ADC数据流效标志
output wire [15:0] adc_data_i1 , //ADC CH1数据,为兼容AXI标准,所以采用16bit位宽,实际只使用了低12位
output wire [15:0] adc_data_q1 , //
output wire [15:0] adc_data_i2 , //ADC CH2数据,为兼容AXI标准,所以采用16bit位宽,实际只使用了低12位
output wire [15:0] adc_data_q2 , //
output wire adc_frame_state , //ADC数据帧状态,检测到异常时为1
//DAC IQ数据
input wire dac_data_valid , //DAC数据流效标志
input wire [15:0] dac_data_i1 , //DAC CH1数据,为兼容AXI标准,所以采用16bit位宽,实际只使用了低12位
input wire [15:0] dac_data_q1 , //
input wire [15:0] dac_data_i2 , //DAC CH2数据,为兼容AXI标准,所以采用16bit位宽,实际只使用了低12位
input wire [15:0] dac_data_q2 //
);
//通过生成语句依据参数例化1R1T或2R2T
generate
if(MODE_1R1T != 0) begin
//例化1R1T
ad9361_interface_1r1t u_ad9361_interface_1r1t_inst0(
//idelay2接口
.idelay_ld_clk (idelay_ld_clk ), //配置idelay延时参数的时钟
.idelay_ld (idelay_ld ), //指示idelay_value作用于那个idelay,从0~6依次是rx_frame_in、rx_data_in[0]~rx_data_in[5]
.idelay_value (idelay_value ), //idelay的延时参数
//delay-cntrl接口
.delay_cntrl_rst (delay_cntrl_rst ), //delay-cntrl复位
.delay_cntrl_200m_clk(delay_cntrl_200m_clk), //delay-cntrl参考时钟,必须为200MHZ
.delay_cntrl_locked (delay_cntrl_locked ), //delay-cntrl锁定指示
//AD963x接收路径信号
.rx_clk_in_p (rx_clk_in_p ), //ADC通道的时钟
.rx_clk_in_n (rx_clk_in_n ), //
.rx_frame_in_p (rx_frame_in_p ), //ADC通道的帧同步
.rx_frame_in_n (rx_frame_in_n ), //
.rx_data_in_p (rx_data_in_p ), //ADC通道的数据
.rx_data_in_n (rx_data_in_n ), //
//AD963x发送路径信号
.tx_clk_out_p (tx_clk_out_p ), //DAC通道的时钟,rx_clk_in的反馈版本
.tx_clk_out_n (tx_clk_out_n ), //
.tx_frame_out_p (tx_frame_out_p ), //DAC通道的帧同步
.tx_frame_out_n (tx_frame_out_n ), //
.tx_data_out_p (tx_data_out_p ), //DAC通道的数据
.tx_data_out_n (tx_data_out_n ), //
//IQ数据域控制和时钟信号
.data_clk (data_clk ), //ADC和DAC数据参考时钟,同步到rx_clk_in
.data_clk_ce (data_clk_ce ), //使能数据参考时钟
//ADC IQ数据
.adc_data_valid (adc_data_valid ), //ADC数据流效标志
.adc_data_i1 (adc_data_i1 ), //ADC CH1数据
.adc_data_q1 (adc_data_q1 ), //
.adc_frame_state (adc_frame_state ), //ADC数据帧状态,检测到异常时为1
//DAC IQ数据
.dac_data_valid (dac_data_valid ), //DAC数据流效标志
.dac_data_i1 (dac_data_i1 ), //DAC CH1数据
.dac_data_q1 (dac_data_q1 ) //
);
//未使用信号赋0
assign adc_data_i2 = 16'b0;
assign adc_data_q2 = 16'b0;
end
else begin
//例化2R2T
ad9361_interface_2r2t u_ad9361_interface_2r2t_inst0(
//idelay2接口
.idelay_ld_clk (idelay_ld_clk ), //配置idelay延时参数的时钟
.idelay_ld (idelay_ld ), //指示idelay_value作用于那个idelay,从0~6依次是rx_frame_in、rx_data_in[0]~rx_data_in[5]
.idelay_value (idelay_value ), //idelay的延时参数
//delay-cntrl接口
.delay_cntrl_rst (delay_cntrl_rst ), //delay-cntrl复位
.delay_cntrl_200m_clk(delay_cntrl_200m_clk), //delay-cntrl参考时钟,必须为200MHZ
.delay_cntrl_locked (delay_cntrl_locked ), //delay-cntrl锁定指示
//AD963x接收路径信号
.rx_clk_in_p (rx_clk_in_p ), //ADC通道的时钟
.rx_clk_in_n (rx_clk_in_n ), //
.rx_frame_in_p (rx_frame_in_p ), //ADC通道的帧同步
.rx_frame_in_n (rx_frame_in_n ), //
.rx_data_in_p (rx_data_in_p ), //ADC通道的数据
.rx_data_in_n (rx_data_in_n ), //
//AD963x发送路径信号
.tx_clk_out_p (tx_clk_out_p ), //DAC通道的时钟,rx_clk_in的反馈版本
.tx_clk_out_n (tx_clk_out_n ), //
.tx_frame_out_p (tx_frame_out_p ), //DAC通道的帧同步
.tx_frame_out_n (tx_frame_out_n ), //
.tx_data_out_p (tx_data_out_p ), //DAC通道的数据
.tx_data_out_n (tx_data_out_n ), //
//IQ数据域控制和时钟信号
.data_clk (data_clk ), //ADC和DAC数据参考时钟,同步到rx_clk_in
.data_clk_ce (data_clk_ce ), //使能数据参考时钟
//ADC IQ数据
.adc_data_valid (adc_data_valid ), //ADC数据流效标志
.adc_data_i1 (adc_data_i1 ), //ADC CH1数据
.adc_data_q1 (adc_data_q1 ), //
.adc_data_i2 (adc_data_i2 ), //ADC CH2数据
.adc_data_q2 (adc_data_q2 ), //
.adc_frame_state (adc_frame_state ), //ADC数据帧状态,检测到异常时为1
//DAC IQ数据
.dac_data_valid (dac_data_valid ), //DAC数据流效标志
.dac_data_i1 (dac_data_i1 ), //DAC CH1数据
.dac_data_q1 (dac_data_q1 ), //
.dac_data_i2 (dac_data_i2 ), //DAC CH2数据
.dac_data_q2 (dac_data_q2 ) //
);
end
endgenerate
endmodule
编写1T1R仿真激励文件
仿真激励文件模拟生成配置idelay延时参数时序、delay-cntrl控制时序、AD9361 RX通道时序、AD9361 DAC发送数据,相应的代码如下:
c
`timescale 1ns / 1ps
module tb_ad9361_interface_1r1t( );
//idelay2接口
reg idelay_ld_clk = 0 ; //配置idelay延时参数的时钟
reg [6:0] idelay_ld = 0 ; //指示idelay_value作用于那个idelay,从0~6依次是rx_frame_in、rx_data_in[0]~rx_data_in[5]
reg [4:0] idelay_value = 0 ; //idelay的延时参数
//delay-cntrl接口
reg delay_cntrl_rst = 0; //delay-cntrl复位
reg delay_cntrl_200m_clk = 0; //delay-cntrl参考时钟,必须为200MHZ
wire delay_cntrl_locked ; //delay-cntrl锁定指示
//AD963x接收路径信号
wire rx_clk_in_p ; //ADC通道的时钟
wire rx_clk_in_n ; //
wire rx_frame_in_p ; //ADC通道的帧同步
wire rx_frame_in_n ; //
wire [5:0] rx_data_in_p ; //ADC通道的数据
wire [5:0] rx_data_in_n ; //
//AD963x发送路径信号
wire tx_clk_out_p ; //DAC通道的时钟,rx_clk_in的反馈版本
wire tx_clk_out_n ; //
wire tx_frame_out_p ; //DAC通道的帧同步
wire tx_frame_out_n ; //
wire [5:0] tx_data_out_p ; //DAC通道的数据
wire [5:0] tx_data_out_n ; //
//IQ数据域控制和时钟信号
wire data_clk ; //ADC和DAC数据参考时钟,同步到rx_clk_in
reg data_clk_ce = 0 ; //使能数据参考时钟
//ADC IQ数据
wire adc_data_valid ; //ADC数据流效标志
wire [15:0] adc_data_i1 ; //ADC CH1数据,为兼容AXI标准,所以采用16bit位宽,实际只使用了低12位
wire [15:0] adc_data_q1 ; //
wire [15:0] adc_data_i2 ; //ADC CH2数据,为兼容AXI标准,所以采用16bit位宽,实际只使用了低12位
wire [15:0] adc_data_q2 ; //
wire adc_frame_state ; //ADC数据帧状态,检测到异常时为1
//DAC IQ数据
reg dac_data_valid = 0 ; //DAC数据流效标志
reg [15:0] dac_data_i1 = 0 ; //DAC CH1数据,为兼容AXI标准,所以采用16bit位宽,实际只使用了低12位
reg [15:0] dac_data_q1 = 0 ; //
reg [15:0] dac_data_i2 = 0 ; //DAC CH2数据,为兼容AXI标准,所以采用16bit位宽,实际只使用了低12位
reg [15:0] dac_data_q2 = 0 ; //
/******************************** 用于生成RX通道模拟数据的变量 ********************************/
//生成RX通道模拟数据的时钟
reg rx_data_clk = 1'b0;
//生成RX通道模拟数据的复位信号
reg rx_data_rst_n = 1'b0;
//模拟数据发送计数
reg [0:0] rx_count = 1'b0;
//暂存ADC模拟数据
reg [11:0] rx_data_i1 = 12'h0;
reg [11:0] rx_data_q1 = 12'h2;
//经过ODDR前的rx_frame,其中rx_frame_pos是上升沿、rx_frame_neg是下降沿
reg rx_frame_pos = 1'b0;
reg rx_frame_neg = 1'b0;
//经过ODDR后的rx_frame
wire rx_frame_oddr;
//经过ODDR前的rx_data,其中rx_data_pos是上升沿、rx_data_neg是下降沿
reg [5:0] rx_data_pos = 6'd0;
reg [5:0] rx_data_neg = 6'd0;
//经过ODDR后的rx_data
wire [5:0] rx_data_oddr;
//经过ODDR后的rx_clk
wire rx_clk_oddr;
/******************************** 用于生成模拟生成AD9361发送数据的变量 ********************************/
//生成DAC模拟数据的复位信号
reg dac_data_rst_n = 1'b0;
//生成的DAC模拟数据计数
reg [0:0] dac_data_count = 1'd0;
/******************************** 配置idelay延时参数 ********************************/
//配置idelay延时参数的参考时钟,100M
initial begin
idelay_ld_clk = 0;
end
always #5 idelay_ld_clk = ~idelay_ld_clk;
//配置idelay延时参数
initial begin
idelay_ld = 7'b0;
idelay_value = 5'd0;
repeat(100) @(posedge idelay_ld_clk);
idelay_ld = 7'b1111111;
idelay_value = 5'd8;
@(posedge idelay_ld_clk);
idelay_ld = 7'b0;
idelay_value = 5'd8;
end
/******************************** delay-cntrl控制 ********************************/
//delay-cntrl工作时钟,200MHZ
initial begin
delay_cntrl_200m_clk = 0;
end
always #2.5 delay_cntrl_200m_clk = ~delay_cntrl_200m_clk;
//delay-cntrl复位
initial begin
delay_cntrl_rst = 1'b1;
repeat(5) @(posedge delay_cntrl_200m_clk);
delay_cntrl_rst = 1'b0;
end
/******************************** 数据参考时钟控制 ********************************/
//使能数据参考时钟
initial begin
data_clk_ce = 1'b0;
#50;
data_clk_ce = 1'b1;
end
/******************************** 模拟生成AD9361 RX通道时序 ********************************/
//RX通道模拟数据的时钟,50M
initial begin
rx_data_clk = 0;
end
always #10 rx_data_clk = ~rx_data_clk;
//RX通道模拟数据的复位信号
initial begin
rx_data_rst_n = 1'b0;
repeat(500) @(posedge rx_data_clk);
rx_data_rst_n = 1'b1;
end
//RX通道模拟数据计数,因为计数到0~1,且只有1位,所以采用溢出自动清零
always @(posedge rx_data_clk) begin
if(!rx_data_rst_n)
rx_count <= 1'b0;
else
rx_count <= rx_count + 1'b1;
end
//生成RX通道模拟数据
always @(posedge rx_data_clk) begin
if(!rx_data_rst_n) begin
rx_data_i1 <= 12'h200;
rx_data_q1 <= 12'h302;
end
else if(rx_count == 1'b1) begin
rx_data_i1 <= rx_data_i1 + 12'h1;
rx_data_q1 <= rx_data_q1 + 12'h1;
end
else begin
rx_data_i1 <= rx_data_i1;
rx_data_q1 <= rx_data_q1;
end
end
//将RX通道模拟数据按时序分解为上升沿和下降沿
always @(posedge rx_data_clk) begin
if(!rx_data_rst_n) begin
rx_frame_pos <= 1'b0;
rx_frame_neg <= 1'b0;
rx_data_pos <= 6'b0;
rx_data_neg <= 6'b0;
end
else begin
case (rx_count)
1'b0 : begin
rx_frame_pos <= 1'b1;
rx_frame_neg <= 1'b1;
rx_data_pos <= rx_data_i1[11:6];
rx_data_neg <= rx_data_q1[11:6];
end
1'b1 : begin
rx_frame_pos <= 1'b0;
rx_frame_neg <= 1'b0;
rx_data_pos <= rx_data_i1[5:0];
rx_data_neg <= rx_data_q1[5:0];
end
default : begin
rx_frame_pos <= 1'b0;
rx_frame_neg <= 1'b0;
rx_data_pos <= 6'b0;
rx_data_neg <= 6'b0;
end
endcase
end
end
//data_clk通过ODDR输出,以保证延时一致
ODDR #(
.DDR_CLK_EDGE("SAME_EDGE"), // "OPPOSITE_EDGE" or "SAME_EDGE"
.INIT(1'b0), // Initial value of Q: 1'b0 or 1'b1
.SRTYPE("SYNC") // Set/Reset type: "SYNC" or "ASYNC"
) ODDR_rx_clk_inst (
.Q(rx_clk_oddr), // 1-bit DDR output
.C(rx_data_clk), // 1-bit clock input
.CE(1'b1), // 1-bit clock enable input
.D1(1'b1), // 1-bit data input (positive edge)
.D2(1'b0), // 1-bit data input (negative edge)
.R(1'b0), // 1-bit reset
.S(1'b0) // 1-bit set
);
//rx_frame通过ODDR转换为双边沿
ODDR #(
.DDR_CLK_EDGE("SAME_EDGE"), // "OPPOSITE_EDGE" or "SAME_EDGE"
.INIT(1'b0), // Initial value of Q: 1'b0 or 1'b1
.SRTYPE("SYNC") // Set/Reset type: "SYNC" or "ASYNC"
) ODDR_rx_frame_inst (
.Q(rx_frame_oddr), // 1-bit DDR output
.C(rx_data_clk), // 1-bit clock input
.CE(1'b1), // 1-bit clock enable input
.D1(rx_frame_pos), // 1-bit data input (positive edge)
.D2(rx_frame_neg), // 1-bit data input (negative edge)
.R(1'b0), // 1-bit reset
.S(1'b0) // 1-bit set
);
//通过循环生成语句生成将rx_data[5:0]转换为双边沿的代码
generate
genvar loop1;
for(loop1 = 0; loop1 < 6; loop1 = loop1 + 1)begin
//rx_data[loop]通过ODDR转换为双边沿
ODDR #(
.DDR_CLK_EDGE("SAME_EDGE"), // "OPPOSITE_EDGE" or "SAME_EDGE"
.INIT(1'b0), // Initial value of Q: 1'b0 or 1'b1
.SRTYPE("SYNC") // Set/Reset type: "SYNC" or "ASYNC"
) ODDR_rx_data_inst (
.Q(rx_data_oddr[loop1]), // 1-bit DDR output
.C(rx_data_clk), // 1-bit clock input
.CE(1'b1), // 1-bit clock enable input
.D1(rx_data_pos[loop1]), // 1-bit data input (positive edge)
.D2(rx_data_neg[loop1]), // 1-bit data input (negative edge)
.R(1'b0), // 1-bit reset
.S(1'b0) // 1-bit set
);
end
endgenerate
//将rx_clk通过OBUFDS转换为差分
OBUFDS #(
.IOSTANDARD("DEFAULT"), // Specify the output I/O standard
.SLEW("SLOW") // Specify the output slew rate
) OBUFDS_rx_clk_inst (
.O(rx_clk_in_p), // Diff_p output (connect directly to top-level port)
.OB(rx_clk_in_n), // Diff_n output (connect directly to top-level port)
.I(rx_clk_oddr) // Buffer input
);
//将rx_frame通过OBUFDS转换为差分
OBUFDS #(
.IOSTANDARD("DEFAULT"), // Specify the output I/O standard
.SLEW("SLOW") // Specify the output slew rate
) OBUFDS_rx_frame_inst (
.O(rx_frame_in_p), // Diff_p output (connect directly to top-level port)
.OB(rx_frame_in_n), // Diff_n output (connect directly to top-level port)
.I(rx_frame_oddr) // Buffer input
);
//通过循环生成语句生成将rx_data[5:0]转换为差分的代码
generate
genvar loop2;
for(loop2 = 0; loop2 < 6; loop2 = loop2 + 1)begin
//rx_data[loop]通过OBUFDS转换为差分
OBUFDS #(
.IOSTANDARD("DEFAULT"), // Specify the output I/O standard
.SLEW("SLOW") // Specify the output slew rate
) OBUFDS_rx_data_inst (
.O(rx_data_in_p[loop2]), // Diff_p output (connect directly to top-level port)
.OB(rx_data_in_n[loop2]), // Diff_n output (connect directly to top-level port)
.I(rx_data_oddr[loop2]) // Buffer input
);
end
endgenerate
/******************************** 模拟生成AD9361发送数据 ********************************/
//生成DAC模拟数据的复位信号
initial begin
dac_data_rst_n = 1'b0;
repeat(500) @(posedge data_clk);
dac_data_rst_n = 1'b1;
end
//DAC模拟数据计数
always @(posedge data_clk) begin
if(!dac_data_rst_n)
dac_data_count <= 1'b0;
else
dac_data_count <= dac_data_count + 1'b1;
end
//生成DAC模拟数据
always @(posedge data_clk) begin
if(!dac_data_rst_n) begin
dac_data_valid <= 1'b0;
dac_data_i1 <= 16'h800;
dac_data_q1 <= 16'hC02;
end
else if(dac_data_count == 1'b1) begin
dac_data_valid <= 1'b1;
dac_data_i1 <= dac_data_i1 + 16'h1;
dac_data_q1 <= dac_data_q1 + 16'h1;
end
else begin
dac_data_valid <= 1'b0;
dac_data_i1 <= dac_data_i1;
dac_data_q1 <= dac_data_q1;
end
end
/******************************** 例化仿真验证模块 ********************************/
ad9361_interface #(
.MODE_1R1T(1)
) tb_ad9361_interface_inst0(
//idelay2接口
.idelay_ld_clk (idelay_ld_clk ), //配置idelay延时参数的时钟
.idelay_ld (idelay_ld ), //指示idelay_value作用于那个idelay,从0~6依次是rx_frame_in、rx_data_in[0]~rx_data_in[5]
.idelay_value (idelay_value ), //idelay的延时参数
//delay-cntrl接口
.delay_cntrl_rst (delay_cntrl_rst ), //delay-cntrl复位
.delay_cntrl_200m_clk(delay_cntrl_200m_clk), //delay-cntrl工作时钟,必须为200MHZ
.delay_cntrl_locked (delay_cntrl_locked ), //delay-cntrl锁定指示
//AD963x接收路径信号
.rx_clk_in_p (rx_clk_in_p ), //ADC通道的时钟
.rx_clk_in_n (rx_clk_in_n ), //
.rx_frame_in_p (rx_frame_in_p ), //ADC通道的帧同步
.rx_frame_in_n (rx_frame_in_n ), //
.rx_data_in_p (rx_data_in_p ), //ADC通道的数据
.rx_data_in_n (rx_data_in_n ), //
//AD963x发送路径信号
.tx_clk_out_p (tx_clk_out_p ), //DAC通道的时钟,rx_clk_in的反馈版本
.tx_clk_out_n (tx_clk_out_n ), //
.tx_frame_out_p (tx_frame_out_p ), //DAC通道的帧同步
.tx_frame_out_n (tx_frame_out_n ), //
.tx_data_out_p (tx_data_out_p ), //DAC通道的数据
.tx_data_out_n (tx_data_out_n ), //
//IQ数据域控制和时钟信号
.data_clk (data_clk ), //ADC和DAC数据参考时钟,同步到rx_clk_in
.data_clk_ce (data_clk_ce ), //使能数据参考时钟
//ADC IQ数据
.adc_data_valid (adc_data_valid ), //ADC数据流效标志
.adc_data_i1 (adc_data_i1 ), //ADC CH1数据,为兼容AXI标准,所以采用16bit位宽,实际只使用了低12位
.adc_data_q1 (adc_data_q1 ), //
.adc_data_i2 (adc_data_i2 ), //ADC CH2数据,为兼容AXI标准,所以采用16bit位宽,实际只使用了低12位
.adc_data_q2 (adc_data_q2 ), //
.adc_frame_state (adc_frame_state ), //ADC数据帧状态,检测到异常时为1
//DAC IQ数据
.dac_data_valid (dac_data_valid ), //DAC数据流效标志
.dac_data_i1 (dac_data_i1 ), //DAC CH1数据,为兼容AXI标准,所以采用16bit位宽,实际只使用了低12位
.dac_data_q1 (dac_data_q1 ), //
.dac_data_i2 (dac_data_i2 ), //DAC CH2数据,为兼容AXI标准,所以采用16bit位宽,实际只使用了低12位
.dac_data_q2 (dac_data_q2 ) //
);
endmodule
1T1R模式仿真输出
ADC仿真时序:

DAC仿真时序:

编写2T2R仿真激励文件
仿真激励文件模拟生成配置idelay延时参数时序、delay-cntrl控制时序、AD9361 RX通道时序、AD9361 DAC发送数据,相应的代码如下:
c
`timescale 1ns / 1ps
module tb_ad9361_interface_2r2t( );
//idelay2接口
reg idelay_ld_clk = 0 ; //配置idelay延时参数的时钟
reg [6:0] idelay_ld = 0 ; //指示idelay_value作用于那个idelay,从0~6依次是rx_frame_in、rx_data_in[0]~rx_data_in[5]
reg [4:0] idelay_value = 0 ; //idelay的延时参数
//delay-cntrl接口
reg delay_cntrl_rst = 0; //delay-cntrl复位
reg delay_cntrl_200m_clk = 0; //delay-cntrl参考时钟,必须为200MHZ
wire delay_cntrl_locked ; //delay-cntrl锁定指示
//AD963x接收路径信号
wire rx_clk_in_p ; //ADC通道的时钟
wire rx_clk_in_n ; //
wire rx_frame_in_p ; //ADC通道的帧同步
wire rx_frame_in_n ; //
wire [5:0] rx_data_in_p ; //ADC通道的数据
wire [5:0] rx_data_in_n ; //
//AD963x发送路径信号
wire tx_clk_out_p ; //DAC通道的时钟,rx_clk_in的反馈版本
wire tx_clk_out_n ; //
wire tx_frame_out_p ; //DAC通道的帧同步
wire tx_frame_out_n ; //
wire [5:0] tx_data_out_p ; //DAC通道的数据
wire [5:0] tx_data_out_n ; //
//IQ数据域控制和时钟信号
wire data_clk ; //ADC和DAC数据参考时钟,同步到rx_clk_in
reg data_clk_ce = 0 ; //使能数据参考时钟
//ADC IQ数据
wire adc_data_valid ; //ADC数据流效标志
wire [15:0] adc_data_i1 ; //ADC CH1数据,为兼容AXI标准,所以采用16bit位宽,实际只使用了低12位
wire [15:0] adc_data_q1 ; //
wire [15:0] adc_data_i2 ; //ADC CH2数据,为兼容AXI标准,所以采用16bit位宽,实际只使用了低12位
wire [15:0] adc_data_q2 ; //
wire adc_frame_state ; //ADC数据帧状态,检测到异常时为1
//DAC IQ数据
reg dac_data_valid = 0 ; //DAC数据流效标志
reg [15:0] dac_data_i1 = 0 ; //DAC CH1数据,为兼容AXI标准,所以采用16bit位宽,实际只使用了低12位
reg [15:0] dac_data_q1 = 0 ; //
reg [15:0] dac_data_i2 = 0 ; //DAC CH2数据,为兼容AXI标准,所以采用16bit位宽,实际只使用了低12位
reg [15:0] dac_data_q2 = 0 ; //
/******************************** 用于生成RX通道模拟数据的变量 ********************************/
//生成RX通道模拟数据的时钟
reg rx_data_clk = 1'b0;
//生成RX通道模拟数据的复位信号
reg rx_data_rst_n = 1'b0;
//模拟数据发送计数
reg [1:0] rx_count = 1'b0;
//暂存ADC模拟数据
reg [11:0] rx_data_i1 = 12'h0;
reg [11:0] rx_data_q1 = 12'h2;
reg [11:0] rx_data_i2 = 12'h20;
reg [11:0] rx_data_q2 = 12'h22;
//经过ODDR前的rx_frame,其中rx_frame_pos是上升沿、rx_frame_neg是下降沿
reg rx_frame_pos = 1'b0;
reg rx_frame_neg = 1'b0;
//经过ODDR后的rx_frame
wire rx_frame_oddr;
//经过ODDR前的rx_data,其中rx_data_pos是上升沿、rx_data_neg是下降沿
reg [5:0] rx_data_pos = 6'd0;
reg [5:0] rx_data_neg = 6'd0;
//经过ODDR后的rx_data
wire [5:0] rx_data_oddr;
//经过ODDR后的rx_clk
wire rx_clk_oddr;
/******************************** 用于生成模拟生成AD9361发送数据的变量 ********************************/
//生成DAC模拟数据的复位信号
reg dac_data_rst_n = 1'b0;
//生成的DAC模拟数据计数
reg [1:0] dac_data_count = 1'd0;
/******************************** 配置idelay延时参数 ********************************/
//配置idelay延时参数的参考时钟,100M
initial begin
idelay_ld_clk = 0;
end
always #5 idelay_ld_clk = ~idelay_ld_clk;
//配置idelay延时参数
initial begin
idelay_ld = 7'b0;
idelay_value = 5'd0;
repeat(100) @(posedge idelay_ld_clk);
idelay_ld = 7'b1111111;
idelay_value = 5'd8;
@(posedge idelay_ld_clk);
idelay_ld = 7'b0;
idelay_value = 5'd8;
end
/******************************** delay-cntrl控制 ********************************/
//delay-cntrl工作时钟,200MHZ
initial begin
delay_cntrl_200m_clk = 0;
end
always #2.5 delay_cntrl_200m_clk = ~delay_cntrl_200m_clk;
//delay-cntrl复位
initial begin
delay_cntrl_rst = 1'b1;
repeat(5) @(posedge delay_cntrl_200m_clk);
delay_cntrl_rst = 1'b0;
end
/******************************** 数据参考时钟控制 ********************************/
//使能数据参考时钟
initial begin
data_clk_ce = 1'b0;
#50;
data_clk_ce = 1'b1;
end
/******************************** 模拟生成AD9361 RX通道时序 ********************************/
//RX通道模拟数据的时钟,50M
initial begin
rx_data_clk = 0;
end
always #10 rx_data_clk = ~rx_data_clk;
//RX通道模拟数据的复位信号
initial begin
rx_data_rst_n = 1'b0;
repeat(500) @(posedge rx_data_clk);
rx_data_rst_n = 1'b1;
end
//RX通道模拟数据计数,因为计数到0~3,且只有2位,所以采用溢出自动清零
always @(posedge rx_data_clk) begin
if(!rx_data_rst_n)
rx_count <= 2'b0;
else
rx_count <= rx_count + 2'b1;
end
//生成RX通道模拟数据
always @(posedge rx_data_clk) begin
if(!rx_data_rst_n) begin
rx_data_i1 <= 12'h200;
rx_data_q1 <= 12'h302;
rx_data_i2 <= 12'h220;
rx_data_q2 <= 12'h322;
end
else if(rx_count == 2'b11) begin
rx_data_i1 <= rx_data_i1 + 12'h1;
rx_data_q1 <= rx_data_q1 + 12'h1;
rx_data_i2 <= rx_data_i2 + 12'h1;
rx_data_q2 <= rx_data_q2 + 12'h1;
end
else begin
rx_data_i1 <= rx_data_i1;
rx_data_q1 <= rx_data_q1;
rx_data_i2 <= rx_data_i2;
rx_data_q2 <= rx_data_q2;
end
end
//将RX通道模拟数据按时序分解为上升沿和下降沿
always @(posedge rx_data_clk) begin
if(!rx_data_rst_n) begin
rx_frame_pos <= 1'b0;
rx_frame_neg <= 1'b0;
rx_data_pos <= 6'b0;
rx_data_neg <= 6'b0;
end
else begin
case (rx_count)
2'b00 : begin
rx_frame_pos <= 1'b1;
rx_frame_neg <= 1'b1;
rx_data_pos <= rx_data_i1[11:6];
rx_data_neg <= rx_data_q1[11:6];
end
2'b01 : begin
rx_frame_pos <= 1'b1;
rx_frame_neg <= 1'b1;
rx_data_pos <= rx_data_i1[5:0];
rx_data_neg <= rx_data_q1[5:0];
end
2'b10 : begin
rx_frame_pos <= 1'b0;
rx_frame_neg <= 1'b0;
rx_data_pos <= rx_data_i2[11:6];
rx_data_neg <= rx_data_q2[11:6];
end
2'b11 : begin
rx_frame_pos <= 1'b0;
rx_frame_neg <= 1'b0;
rx_data_pos <= rx_data_i2[5:0];
rx_data_neg <= rx_data_q2[5:0];
end
default : begin
rx_frame_pos <= 1'b0;
rx_frame_neg <= 1'b0;
rx_data_pos <= 6'b0;
rx_data_neg <= 6'b0;
end
endcase
end
end
//data_clk通过ODDR输出,以保证延时一致
ODDR #(
.DDR_CLK_EDGE("SAME_EDGE"), // "OPPOSITE_EDGE" or "SAME_EDGE"
.INIT(1'b0), // Initial value of Q: 1'b0 or 1'b1
.SRTYPE("SYNC") // Set/Reset type: "SYNC" or "ASYNC"
) ODDR_rx_clk_inst (
.Q(rx_clk_oddr), // 1-bit DDR output
.C(rx_data_clk), // 1-bit clock input
.CE(1'b1), // 1-bit clock enable input
.D1(1'b1), // 1-bit data input (positive edge)
.D2(1'b0), // 1-bit data input (negative edge)
.R(1'b0), // 1-bit reset
.S(1'b0) // 1-bit set
);
//rx_frame通过ODDR转换为双边沿
ODDR #(
.DDR_CLK_EDGE("SAME_EDGE"), // "OPPOSITE_EDGE" or "SAME_EDGE"
.INIT(1'b0), // Initial value of Q: 1'b0 or 1'b1
.SRTYPE("SYNC") // Set/Reset type: "SYNC" or "ASYNC"
) ODDR_rx_frame_inst (
.Q(rx_frame_oddr), // 1-bit DDR output
.C(rx_data_clk), // 1-bit clock input
.CE(1'b1), // 1-bit clock enable input
.D1(rx_frame_pos), // 1-bit data input (positive edge)
.D2(rx_frame_neg), // 1-bit data input (negative edge)
.R(1'b0), // 1-bit reset
.S(1'b0) // 1-bit set
);
//通过循环生成语句生成将rx_data[5:0]转换为双边沿的代码
generate
genvar loop1;
for(loop1 = 0; loop1 < 6; loop1 = loop1 + 1)begin
//rx_data[loop]通过ODDR转换为双边沿
ODDR #(
.DDR_CLK_EDGE("SAME_EDGE"), // "OPPOSITE_EDGE" or "SAME_EDGE"
.INIT(1'b0), // Initial value of Q: 1'b0 or 1'b1
.SRTYPE("SYNC") // Set/Reset type: "SYNC" or "ASYNC"
) ODDR_rx_data_inst (
.Q(rx_data_oddr[loop1]), // 1-bit DDR output
.C(rx_data_clk), // 1-bit clock input
.CE(1'b1), // 1-bit clock enable input
.D1(rx_data_pos[loop1]), // 1-bit data input (positive edge)
.D2(rx_data_neg[loop1]), // 1-bit data input (negative edge)
.R(1'b0), // 1-bit reset
.S(1'b0) // 1-bit set
);
end
endgenerate
//将rx_clk通过OBUFDS转换为差分
OBUFDS #(
.IOSTANDARD("DEFAULT"), // Specify the output I/O standard
.SLEW("SLOW") // Specify the output slew rate
) OBUFDS_rx_clk_inst (
.O(rx_clk_in_p), // Diff_p output (connect directly to top-level port)
.OB(rx_clk_in_n), // Diff_n output (connect directly to top-level port)
.I(rx_clk_oddr) // Buffer input
);
//将rx_frame通过OBUFDS转换为差分
OBUFDS #(
.IOSTANDARD("DEFAULT"), // Specify the output I/O standard
.SLEW("SLOW") // Specify the output slew rate
) OBUFDS_rx_frame_inst (
.O(rx_frame_in_p), // Diff_p output (connect directly to top-level port)
.OB(rx_frame_in_n), // Diff_n output (connect directly to top-level port)
.I(rx_frame_oddr) // Buffer input
);
//通过循环生成语句生成将rx_data[5:0]转换为差分的代码
generate
genvar loop2;
for(loop2 = 0; loop2 < 6; loop2 = loop2 + 1)begin
//rx_data[loop]通过OBUFDS转换为差分
OBUFDS #(
.IOSTANDARD("DEFAULT"), // Specify the output I/O standard
.SLEW("SLOW") // Specify the output slew rate
) OBUFDS_rx_data_inst (
.O(rx_data_in_p[loop2]), // Diff_p output (connect directly to top-level port)
.OB(rx_data_in_n[loop2]), // Diff_n output (connect directly to top-level port)
.I(rx_data_oddr[loop2]) // Buffer input
);
end
endgenerate
/******************************** 模拟生成AD9361发送数据 ********************************/
//生成DAC模拟数据的复位信号
initial begin
dac_data_rst_n = 1'b0;
repeat(500) @(posedge data_clk);
dac_data_rst_n = 1'b1;
end
//DAC模拟数据计数
always @(posedge data_clk) begin
if(!dac_data_rst_n)
dac_data_count <= 2'b0;
else
dac_data_count <= dac_data_count + 2'b1;
end
//生成DAC模拟数据
always @(posedge data_clk) begin
if(!dac_data_rst_n) begin
dac_data_valid <= 1'b0;
dac_data_i1 <= 16'h800;
dac_data_q1 <= 16'hC02;
dac_data_i2 <= 16'h820;
dac_data_q2 <= 16'hC22;
end
else if(dac_data_count == 2'b11) begin
dac_data_valid <= 1'b1;
dac_data_i1 <= dac_data_i1 + 16'h1;
dac_data_q1 <= dac_data_q1 + 16'h1;
dac_data_i2 <= dac_data_i2 + 16'h1;
dac_data_q2 <= dac_data_q2 + 16'h1;
end
else begin
dac_data_valid <= 1'b0;
dac_data_i1 <= dac_data_i1;
dac_data_q1 <= dac_data_q1;
dac_data_i2 <= dac_data_i2;
dac_data_q2 <= dac_data_q2;
end
end
/******************************** 例化仿真验证模块 ********************************/
ad9361_interface #(
.MODE_1R1T(0)
) tb_ad9361_interface_inst0(
//idelay2接口
.idelay_ld_clk (idelay_ld_clk ), //配置idelay延时参数的时钟
.idelay_ld (idelay_ld ), //指示idelay_value作用于那个idelay,从0~6依次是rx_frame_in、rx_data_in[0]~rx_data_in[5]
.idelay_value (idelay_value ), //idelay的延时参数
//delay-cntrl接口
.delay_cntrl_rst (delay_cntrl_rst ), //delay-cntrl复位
.delay_cntrl_200m_clk(delay_cntrl_200m_clk), //delay-cntrl工作时钟,必须为200MHZ
.delay_cntrl_locked (delay_cntrl_locked ), //delay-cntrl锁定指示
//AD963x接收路径信号
.rx_clk_in_p (rx_clk_in_p ), //ADC通道的时钟
.rx_clk_in_n (rx_clk_in_n ), //
.rx_frame_in_p (rx_frame_in_p ), //ADC通道的帧同步
.rx_frame_in_n (rx_frame_in_n ), //
.rx_data_in_p (rx_data_in_p ), //ADC通道的数据
.rx_data_in_n (rx_data_in_n ), //
//AD963x发送路径信号
.tx_clk_out_p (tx_clk_out_p ), //DAC通道的时钟,rx_clk_in的反馈版本
.tx_clk_out_n (tx_clk_out_n ), //
.tx_frame_out_p (tx_frame_out_p ), //DAC通道的帧同步
.tx_frame_out_n (tx_frame_out_n ), //
.tx_data_out_p (tx_data_out_p ), //DAC通道的数据
.tx_data_out_n (tx_data_out_n ), //
//IQ数据域控制和时钟信号
.data_clk (data_clk ), //ADC和DAC数据参考时钟,同步到rx_clk_in
.data_clk_ce (data_clk_ce ), //使能数据参考时钟
//ADC IQ数据
.adc_data_valid (adc_data_valid ), //ADC数据流效标志
.adc_data_i1 (adc_data_i1 ), //ADC CH1数据,为兼容AXI标准,所以采用16bit位宽,实际只使用了低12位
.adc_data_q1 (adc_data_q1 ), //
.adc_data_i2 (adc_data_i2 ), //ADC CH2数据,为兼容AXI标准,所以采用16bit位宽,实际只使用了低12位
.adc_data_q2 (adc_data_q2 ), //
.adc_frame_state (adc_frame_state ), //ADC数据帧状态,检测到异常时为1
//DAC IQ数据
.dac_data_valid (dac_data_valid ), //DAC数据流效标志
.dac_data_i1 (dac_data_i1 ), //DAC CH1数据,为兼容AXI标准,所以采用16bit位宽,实际只使用了低12位
.dac_data_q1 (dac_data_q1 ), //
.dac_data_i2 (dac_data_i2 ), //DAC CH2数据,为兼容AXI标准,所以采用16bit位宽,实际只使用了低12位
.dac_data_q2 (dac_data_q2 ) //
);
endmodule
2T2R模式仿真输出
ADC仿真时序:

DAC仿真时序:
