Hi,
I'm using an AD9364 on a custom PCB, I programmed it with the No-Os Software implemented on a Digilent Zybo (Zynq7020).
The custom transceiver board I use output the baseband I/Q serialized on only one LVDS pair. (the rx_frame and rx_clk_in signal are also LVDS)
What I did then was to add a new source called : axi_ad9361_custom_if.v
Basically what I want to do is to implement a ISERDES primitive (ILOGICE3 primitive from Zynq7) so that the rx data path with look like : IBUFDS -> IDELAYE -> ISERDES (DDR or SDR) (by doing so all should be located in just one Tile of the SoC.)
exept for the rx data path the other signal path remain unchanged from the LVDS interface ( meaning using ad_data_in, ad_data_out, ad_data_clk).
I also made changes on the rx_clk_in because it is 6 time faster than the LVDS data clock should be because of the serialization, so Iimplemented a PLL (also tried with MMCM) to have my l_clk back to right output rate.
Now the issue I have is that the output rst out the IP core is stuck high. It is generetad from the up_core_preset, meaning the ADC_RST_REGISTER bit[0] which is enabled and disabled correctly in the adc_init function of the No-Os driver.
Please find attach a snippet of the custom intefarce I made from the axi_ad9361_lvds_if.v
// ***************************************************************************
// **************************************************************************
// Custom interface with integrated IBUF->IDELAYE2->ISERDES
// ***************************************************************************
// ***************************************************************************
// =========================================================================
// ad_data_clk.v
// =========================================================================
generate
begin
ad_data_clk #(
.SINGLE_ENDED (0)
) i_clk (
.rst (1'd0),
.locked (),
.clk_in_p (rx_clk_in_p),
.clk_in_n (rx_clk_in_n),
.clk (rx_clk_in));
end
endgenerate
// =========================================================================
// CUSTOM RX DATA PATH
// =========================================================================
wire clk_48mhz_raw; // Horloge série 48 MHz
wire clk_8mhz_raw; // Horloge intermédiaire 8 MHz
wire clk_48mhz; // Horloge 48 MHz après BUFG
wire clk_8mhz; // Horloge 8 MHz après BUFG
wire pll_locked;
// Génération des horloges avec MMCM ou PLL selon la configuration
generate
if (ELIOT_CLK_PLL_OR_MMCM_N == 0) begin : gen_mmcm
wire clk_fb;
MMCME2_ADV #(
.BANDWIDTH("OPTIMIZED"),
.CLKFBOUT_MULT_F(32.0), // VCO = 24 * 32 = 768 MHz
.DIVCLK_DIVIDE(1),
.CLKIN1_PERIOD(41.667), // 24 MHz input
.CLKOUT0_DIVIDE_F(16.0), // 768/16 = 48 MHz (horloge série)
.CLKOUT1_DIVIDE(96), // 768/96 = 8 MHz (horloge intermédiaire)
.CLKOUT0_DUTY_CYCLE(0.5),
.CLKOUT1_DUTY_CYCLE(0.5),
.CLKOUT0_PHASE(0.0),
.CLKOUT1_PHASE(0.0),
.COMPENSATION("ZHOLD"),
.STARTUP_WAIT("FALSE"),
.REF_JITTER1(0.010)
) mmcm_inst (
.CLKIN1(rx_clk_in), // 24 MHz depuis rx_clk_in
.CLKIN2(1'b0),
.CLKINSEL(1'b1),
.CLKFBIN(clk_fb),
.CLKFBOUT(clk_fb),
.CLKOUT0(clk_48mhz_raw), // 48 MHz
.CLKOUT1(clk_8mhz_raw), // 8 MHz
.LOCKED(pll_locked),
.PWRDWN(1'b0),
.RST(pll_rst),
.CLKFBOUTB(),
.CLKOUT0B(),
.CLKOUT1B(),
.CLKOUT2(),
.CLKOUT2B(),
.CLKOUT3(),
.CLKOUT3B(),
.CLKOUT4(),
.CLKOUT5(),
.CLKOUT6(),
.CLKINSTOPPED(),
.CLKFBSTOPPED(),
.DADDR(7'h0),
.DCLK(1'b0),
.DEN(1'b0),
.DI(16'h0),
.DO(),
.DRDY(),
.DWE(1'b0),
.PSCLK(1'b0),
.PSEN(1'b0),
.PSINCDEC(1'b0),
.PSDONE()
);
end else begin : gen_pll
wire clk_fb;
PLLE2_ADV #(
.BANDWIDTH("OPTIMIZED"),
.CLKFBOUT_MULT(32), // VCO = 24 * 32 = 768 MHz
.DIVCLK_DIVIDE(1),
.CLKIN1_PERIOD(41.667),
.CLKOUT0_DIVIDE(16), // 768/16 = 48 MHz
.CLKOUT1_DIVIDE(96), // 768/96 = 8 MHz
.CLKOUT0_DUTY_CYCLE(0.5),
.CLKOUT1_DUTY_CYCLE(0.5),
.CLKOUT0_PHASE(0.0),
.CLKOUT1_PHASE(0.0),
.COMPENSATION("ZHOLD"),
.STARTUP_WAIT("FALSE"),
.REF_JITTER1(0.010)
) pll_inst (
.CLKIN1(rx_clk_in),
.CLKIN2(1'b0),
.CLKINSEL(1'b1),
.CLKFBIN(clk_fb),
.CLKFBOUT(clk_fb),
.CLKOUT0(clk_48mhz_raw),
.CLKOUT1(clk_8mhz_raw),
.LOCKED(pll_locked),
.PWRDWN(1'b0),
.RST(pll_rst),
.CLKOUT2(),
.CLKOUT3(),
.CLKOUT4(),
.CLKOUT5(),
.DADDR(7'h0),
.DCLK(1'b0),
.DEN(1'b0),
.DI(16'h0),
.DO(),
.DRDY(),
.DWE(1'b0)
);
end
endgenerate
BUFG bufg_48mhz (
.I(clk_48mhz_raw),
.O(clk_48mhz)
);
BUFG bufg_8mhz (
.I(clk_8mhz_raw),
.O(clk_8mhz)
);
// 8 MHz -> 4 MHz
reg clk_4mhz_reg;
always @(posedge clk_8mhz or posedge rst) begin
if (rst)
clk_4mhz_reg <= 1'b0;
else
clk_4mhz_reg <= ~clk_4mhz_reg;
end
wire clk_4mhz;
BUFG bufg_4mhz (
.I(clk_4mhz_reg),
.O(clk_4mhz)
);
// 8 MHz -> 2 MHz
reg [1:0] clk_2mhz_reg;
always @(posedge clk_8mhz or posedge rst) begin
if (rst)
clk_2mhz_reg <= 2'b0;
else if (clk_2mhz_reg == 2'd3)
clk_2mhz_reg <= 2'b0;
else
clk_2mhz_reg <= clk_2mhz_reg + 1;
end
wire clk_2mhz_raw; // Signal brut avant le BUFG
assign clk_2mhz_raw = (clk_2mhz_reg == 2'd0) ? clk_8mhz : 1'b0;
wire clk_2mhz; // Signal après le BUFG
BUFG bufg_2mhz (
.I(clk_2mhz_raw),
.O(clk_2mhz)
);
// =========================================================================
// Rx Data path : IBUFDS -> IDELAYE -> ISERDES
// =========================================================================
// IBUFDS - Input Buffer
wire rx_data_ibuf;
IBUFDS i_rx_data_ibuf (
.I(rx_data_in_eliot_p),
.IB(rx_data_in_eliot_n),
.O(rx_data_ibuf)
);
// IDELAYE2 - Input Delay
wire rx_data_idelay;
(* IODELAY_GROUP = IO_DELAY_GROUP *)
IDELAYE2 #(
.CINVCTRL_SEL("FALSE"),
.DELAY_SRC("IDATAIN"),
.HIGH_PERFORMANCE_MODE("TRUE"),
.IDELAY_TYPE("VAR_LOAD"),
.IDELAY_VALUE(0),
.REFCLK_FREQUENCY(DELAY_REFCLK_FREQUENCY),
.PIPE_SEL("FALSE"),
.SIGNAL_PATTERN("DATA")
) i_rx_data_idelay (
.C(up_clk),
.IDATAIN(rx_data_ibuf),
.DATAOUT(rx_data_idelay),
.LD(up_adc_dld[0]),
.CNTVALUEIN(up_adc_dwdata[4:0]),
.CNTVALUEOUT(up_adc_drdata[4:0]),
.CE(1'b0),
.INC(1'b0),
.DATAIN(1'b0),
.LDPIPEEN(1'b0),
.CINVCTRL(1'b0),
.REGRST(1'b0)
);
// ISERDESE2 - Désérialiseur SDR
wire [5:0] iserdes_q;
ISERDESE2 #(
.DATA_RATE("SDR"),
.DATA_WIDTH(6),
.INTERFACE_TYPE("NETWORKING"),
.NUM_CE(1),
.SERDES_MODE("MASTER"),
.IOBDELAY("IFD")
) i_rx_data_iserdes (
.D(1'b0),
.DDLY(rx_data_idelay),
.CLK(clk_48mhz),
.CLKB(~clk_48mhz),
.CLKDIV(clk_4mhz),
.CE1(1'b1),
.CE2(1'b1),
.RST(rst),
.Q1(iserdes_q[0]),
.Q2(iserdes_q[1]),
.Q3(iserdes_q[2]),
.Q4(iserdes_q[3]),
.Q5(iserdes_q[4]),
.Q6(iserdes_q[5]),
.SHIFTIN1(1'b0),
.SHIFTIN2(1'b0),
.SHIFTOUT1(),
.SHIFTOUT2(),
.CLKDIVP(1'b0),
.OCLK(1'b0),
.OCLKB(1'b0),
.BITSLIP(1'b0),
.OFB(1'b0),
.DYNCLKDIVSEL(1'b0),
.DYNCLKSEL(1'b0)
);
// =========================================================================
// ISERDES -> Divide /2 -> AD9361 data type
// =========================================================================
reg [5:0] iserdes_q_reg;
always @(posedge clk_4mhz) begin
if (rst)
iserdes_q_reg <= 6'b0;
else
iserdes_q_reg <= iserdes_q;
end
reg [5:0] sample_phase0; // First sample in 2MHz period
reg [5:0] sample_phase1; // Second sample in 2MHz period
reg phase; // Phase
always @(posedge clk_4mhz) begin
if (rst) begin
sample_phase0 <= 6'b0;
sample_phase1 <= 6'b0;
phase <= 1'b0;
end else begin
if (phase == 1'b0) begin
sample_phase0 <= iserdes_q_reg;
phase <= 1'b1;
end else begin
sample_phase1 <= iserdes_q_reg;
phase <= 1'b0;
end
end
end
reg [5:0] rx_data_2mhz_p;
reg [5:0] rx_data_2mhz_n;
always @(posedge clk_2mhz) begin
if (rst) begin
rx_data_2mhz_p <= 6'b0;
rx_data_2mhz_n <= 6'b0;
end else begin
// Échantillonner au milieu de la période stable
rx_data_2mhz_p <= sample_phase1; // "P" side (bits pairs ou MSB)
rx_data_2mhz_n <= sample_phase0; // "N" side (bits impairs ou LSB)
end
end
assign rx_data_1_s = rx_data_2mhz_p;
assign rx_data_0_s = rx_data_2mhz_n;
// =========================================================================
// FRAME PATH (Unchanged from lvds interface )
// =========================================================================
// receive frame interface, ibuf -> idelay -> iddr
ad_data_in #(
.FPGA_TECHNOLOGY (FPGA_TECHNOLOGY),
.IODELAY_CTRL (IODELAY_CTRL),
.IODELAY_GROUP (IO_DELAY_GROUP),
.REFCLK_FREQUENCY (DELAY_REFCLK_FREQUENCY)
) i_rx_frame (
.rx_clk (rx_clk_in),
.rx_data_in_p (rx_frame_in_p),
.rx_data_in_n (rx_frame_in_n),
.rx_data_p (rx_frame_s[1]),
.rx_data_n (rx_frame_s[0]),
.up_clk (up_clk),
.up_dld (up_adc_dld[6]),
.up_dwdata (up_adc_dwdata[34:30]),
.up_drdata (up_adc_drdata[34:30]),
.delay_clk (delay_clk),
.delay_rst (delay_rst),
.delay_locked (locked_s));
// =========================================================================
// TX PATH: Use standard ad_data_out modules (unchanged)
// =========================================================================
// transmit data interface, oddr -> obuf
genvar i;
generate
for (i = 0; i < 6; i = i + 1) begin: g_tx_data
ad_data_out #(
.FPGA_TECHNOLOGY (FPGA_TECHNOLOGY),
.IODELAY_ENABLE (DAC_IODELAY_ENABLE),
.IODELAY_CTRL (0),
.IODELAY_GROUP (IO_DELAY_GROUP),
.REFCLK_FREQUENCY (DELAY_REFCLK_FREQUENCY)
) i_tx_data (
.tx_clk (l_clk),
.tx_data_p (tx_data_1[i]),
.tx_data_n (tx_data_0[i]),
.tx_data_out_p (tx_data_out_p[i]),
.tx_data_out_n (tx_data_out_n[i]),
.up_clk (up_clk),
.up_dld (up_dac_dld[i]),
.up_dwdata (up_dac_dwdata[((i*5)+4):(i*5)]),
.up_drdata (up_dac_drdata[((i*5)+4):(i*5)]),
.delay_clk (delay_clk),
.delay_rst (delay_rst),
.delay_locked ());
end
endgenerate
// transmit frame interface, oddr -> obufparameter DDR_SDR_N = 1,
ad_data_out #(
.FPGA_TECHNOLOGY (FPGA_TECHNOLOGY),
.IODELAY_ENABLE (DAC_IODELAY_ENABLE),
.IODELAY_CTRL (0),
.IODELAY_GROUP (IO_DELAY_GROUP),
.REFCLK_FREQUENCY (DELAY_REFCLK_FREQUENCY)
) i_tx_frame (
.tx_clk (l_clk),
.tx_data_p (tx_frame),
.tx_data_n (tx_frame),
.tx_data_out_p (tx_frame_out_p),
.tx_data_out_n (tx_frame_out_n),
.up_clk (up_clk),
.up_dld (up_dac_dld[6]),
.up_dwdata (up_dac_dwdata[34:30]),
.up_drdata (up_dac_drdata[34:30]),
.delay_clk (delay_clk),
.delay_rst (delay_rst),
.delay_locked ());
// transmit clock interface, oddr -> obuf
ad_data_out #(
.FPGA_TECHNOLOGY (FPGA_TECHNOLOGY),
.IODELAY_ENABLE (DAC_IODELAY_ENABLE),
.IODELAY_CTRL (0),
.IODELAY_GROUP (IO_DELAY_GROUP),
.REFCLK_FREQUENCY (DELAY_REFCLK_FREQUENCY)
) i_tx_clk (
.tx_clk (l_clk),
.tx_data_p (tx_clk[1]),
.tx_data_n (tx_clk[0]),
.tx_data_out_p (tx_clk_out_p),
.tx_data_out_n (tx_clk_out_n),
.up_clk (up_clk),
.up_dld (up_dac_dld[7]),
.up_dwdata (up_dac_dwdata[39:35]),
.up_drdata (up_dac_drdata[39:35]),
.delay_clk (delay_clk),
.delay_rst (delay_rst),
.delay_locked ());
// enable, oddr -> obuf
ad_data_out #(
.SINGLE_ENDED (1),
.FPGA_TECHNOLOGY (FPGA_TECHNOLOGY),
.IODELAY_ENABLE (DAC_IODELAY_ENABLE),
.IODELAY_CTRL (0),
.IODELAY_GROUP (IO_DELAY_GROUP),
.REFCLK_FREQUENCY (DELAY_REFCLK_FREQUENCY)
) i_enable (
.tx_clk (l_clk),
.tx_data_p (enable_int_p),
.tx_data_n (enable_int_p),
.tx_data_out_p (enable),
.tx_data_out_n (),
.up_clk (up_clk),
.up_dld (up_dac_dld[8]),
.up_dwdata (up_dac_dwdata[44:40]),
.up_drdata (up_dac_drdata[44:40]),
.delay_clk (delay_clk),
.delay_rst (delay_rst),
.delay_locked ());
// txnrx, oddr -> obuf
ad_data_out #(
.SINGLE_ENDED (1),
.FPGA_TECHNOLOGY (FPGA_TECHNOLOGY),
.IODELAY_ENABLE (DAC_IODELAY_ENABLE),
.IODELAY_CTRL (0),
.IODELAY_GROUP (IO_DELAY_GROUP),
.REFCLK_FREQUENCY (DELAY_REFCLK_FREQUENCY)
) i_txnrx (
.tx_clk (l_clk),
.tx_data_p (txnrx_int_p),
.tx_data_n (txnrx_int_p),
.tx_data_out_p (txnrx),
.tx_data_out_n (),
.up_clk (up_clk),
.up_dld (up_dac_dld[9]),
.up_dwdata (up_dac_dwdata[49:45]),
.up_drdata (up_dac_drdata[49:45]),
.delay_clk (delay_clk),
.delay_rst (delay_rst),
.delay_locked ());
//========= Moved Before PLL (exept l_clk assign ) ==========//
/*
// ad_data_clk.v
generate if (USE_SSI_CLK == 1) begin
ad_data_clk #(
.SINGLE_ENDED (0)
) i_clk (
.rst (1'd0),
.locked (),
.clk_in_p (rx_clk_in_p),
.clk_in_n (rx_clk_in_n),
.clk (rx_clk_in));
assign l_clk = clk_4mhz;
end else begin
assign l_clk = clk;
end
endgenerate
*/
assign l_clk = (USE_SSI_CLK == 1) ? clk_4mhz : clk;
endmodule