06 AD9361 LVDS接口实现和仿真

AD9361 LVDS接口程序框图

数据接收路径

  1. AD9361将数据输出到rx_frame_in_p/n和rx_data_in_p/n[5:0]。
  2. rx_frame_in_p/n和rx_data_in_p/n[5: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仿真时序:

相关推荐
逻辑诗篇7 小时前
FMC122高速数据采集卡:雷达与SDR领域的高性能解决方案
fpga开发
寒秋花开曾相惜1 天前
(学习笔记)4.2 逻辑设计和硬件控制语言HCL(4.2.1 逻辑门&4.2.2 组合电路和HCL布尔表达式)
linux·网络·数据结构·笔记·学习·fpga开发
何如呢1 天前
tx_addheader(加前导)
fpga开发
北京青翼科技1 天前
青翼科技基于XCVU13P FPGA的4路FMC接口高性能信号处理平台丨嵌入式智能平台 · 通用嵌入式平台丨FPGA信号处理板
fpga开发·信号处理·信号处理板·图形处理板卡·pcie数据处理板·fpga板卡
HIZYUAN2 天前
FPGA/CPLD漫谈:2K LUT的功能定位与典型方案(一)
stm32·单片机·嵌入式硬件·fpga开发·国产mcu+fpga
FPGA_Linuxer2 天前
FPGA开发板 KU5P开发板 图像处理,硬件加速 PCIE3.0 100G光口
fpga开发
szxinmai主板定制专家2 天前
基于ZYNQ MPSOC多通道声音振动采集方案,替代NI9234和B&K
arm开发·人工智能·嵌入式硬件·fpga开发
ZYNQRFSOC3 天前
基于安路PH2A系列FPGA的JESD204B接口测试
嵌入式硬件·fpga开发
szxinmai主板定制专家3 天前
基于RK3588超小体积,轻巧,长续航的无人机AI模块,支持视频跟踪
arm开发·人工智能·嵌入式硬件·fpga开发·无人机