06 AD9361 LVDS接口实现和仿真

AD9361 LVDS接口程序框图

数据接收路径

  1. AD9361将数据输出到rx_frame_in_p/n和rx_data_in_p/n5:0
  2. rx_frame_in_p/n和rx_data_in_p/n5:0通过FPGA的IBUFDS将差分信号转换为单端信号。
  3. 转换为单端的rx_frame和rx_data通过FPGA的Idelay进行延时,以弥补时序误差。
  4. 经过Idelay进行延时后的rx_frame和rx_data通过FPGA的IDDR将双边沿信号转换为单边沿信号。
  5. 将转换为单端的rx_frame和rx_data送入Verilog逻辑进行组合便的到AD9361采集的数据。

数据发送路径

  1. FPGA内部将数据输出到dac_valid和dac_data。
  2. Verilog逻辑对dac_valid和dac_data进行拆分,得到ODDR上升沿时刻和下降沿时刻的值。
  3. 将拆分好的值送入FPGA的ODDR转换为双边沿信号。
  4. 将IDDR输出的双边沿信号送入FPGA的OBUFDS转换为差分信号。
  5. 将 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仿真时序:

相关推荐
仲南音16 小时前
Xilinx FPGA——时钟IP核MMCM、PLL
fpga开发
I'm a winner1 天前
基于Xilinx FPGA的LVDS高速串行通信系统(三)--数据训练及握手机制【文末源码】
fpga开发
upper20201 天前
coremark移植坑之-nostartfiles -nostdlib参数
学习·fpga开发·coremark移植·riscv设计
Eloudy2 天前
Vivado 纯命令行构建 FPGA 项目教程
fpga开发
Rambo.xia2 天前
AXI跨时钟域与数据一致性——CDC FIFO深度不够、写后读冒险、多Master仲裁、乱序返回,出了Bug根本查不出来
fpga开发·bug
upper20202 天前
FPGA部署卷积神经网络识别MINST图片
fpga开发·卷积神经网络·minst·minst识别·部署神经网络·神经网络识别图片
你是我的解忧王子2 天前
嵌入式 ADC采样 原始数据分析上层(平均值 波动率 正态分布 线性补偿 系统误差)
fpga开发·数据挖掘·数据分析
9527华安3 天前
FPGA纯verilog代码ISP图像处理培训教程,基于IMX214,提供工程源码+视频教程+FPGA开发板
图像处理·fpga开发·isp·imx214·mipi
千寻xun3 天前
二、实战篇-NVME SSD控制之ZYNQ实现(六)-读写NVME SSD硬盘数据
fpga开发·nvme·nvme ssd
千寻xun3 天前
二、实战篇-NVME SSD控制之ZYNQ实现(三)
fpga开发·nvme ssd·nvme协议