Wiki

This version (24 May 2019 10:14) was approved by mhennerich.The Previously approved version (24 May 2019 10:02) is available.Diff

Changing the VCXO frequency and updating the default RF Transceiver Profile

Changing the VCXO frequency

The ADRV9009, ADRV9008-1, ADRV9008-2, AD9371, AD9375 evaluation boards contains an on-board VCXO (Voltage Controlled Crystal Oscillator) as well as the AD9528 chip responsible for the device clock and SYSREF signal generation and distribution. With the hardware configuration provided on the evaluation board, a user can generate device clock frequencies such as 122.88MHz, 153.6MHz, 184.32MHz, 245.76MHz, and 307.2MHz. There are limitations with the default hardware configuration in the scenario where a user desired device frequencies are not related to the on-board 122.88MHz VCXO by a rational fraction. Examples of such device clock frequency are: 125MHz, 133.33MHz, 250MHz and 266.66MHz. The document below outlines these limitations as well as explains how they can be overcome with an AD9371 evaluation board hardware modification. Pretty much the same things also apply for all the other devices listed above.

This page is supposed to be a system level addition to the aforementioned document with some extra tips and tricks.

Rational

Some reasons why someone would change the onboard VCXO:

  • Being able to generate device clocks which are not a rational fraction of the default 122.88MHz VCXO. For example realize 50, 100, 200 MSPS baseband rates instead of 61.44, 122.88, 245.76 MSPS.
  • Being able to synchronize the VCXO via AD9528 PLL1 with a reference which is not a rational fraction of the default 122.88MHz VCXO. For example with an external 10MHz reference clock, instead of an 30.72MHz reference.
  • Testing a better or worse clock jitter, stability VCXO.

Procedure

In the following example procedure the VCXO is changed to 80MHz to achieve 240, 200, 120, 100 MSPS baseband rates. In general, more common would be to use a 125MHz VCXO instead.

Physically replace the onboard VCXO

If you just want to test things without VCXO, there is the option to bypass the VCXO and PLL1 and directly feed an external clock into the AD9528 via the REF_CLK_IN SMA which is typically used to feed the external reference for PLL1. Some resistors and caps need to be flipped. Check the Eval board schematics for more details. In such case you would bypass PLL1 and use differential input.
 
&clk0_ad9528 {	
	/* PLL1 config */
	adi,pll1-bypass-enable;
	adi,osc-in-diff-enable;
}

Adjust the adi,vcxo-freq device tree property with the used VCXO frequency in Hz

&clk0_ad9528 {
	adi,vcxo-freq = <80000000>;
}

Plan the distribution clock

There are basically two configuration options. In both examples described below the distribution clock is set to 1200MHz. So each AD9528 output channel can be set to 1200MHz / N where N = 1..256.

Manual configuration

Manual configuration requires to understand the math behind the PLL.

Please also see:

&clk0_ad9528 { 
	/* Manual divider configuration /
        adi,vcxo-freq = <80000000>;
	adi,pll2-vco-div-m1 = <3>;
	adi,pll2-n2-div = <15>;
	adi,pll2-r1-div = <1>; 
}

Automatic configuration

Or to use the device driver automatic configuration. This way you only need to specify the adi,pll2-m1-frequency

&clk0_ad9528 { 
        adi,vcxo-freq = <80000000>;
	* Valid ranges based on VCO locking range:
	*   1150.000 MHz - 1341.666 MHz
	*    862.500 MHz - 1006.250 MHz
	*    690.000 MHz -  805.000 MHz
	*/
	
        adi,pll2-m1-frequency = <1200000000>;
}

Updating the default RF Transceiver Profile

Now that we know we can generate output clocks such as 80MHz (N=15) 100MHz (N=12) or 120MHz (N=10) we need to create a RF transceiver profile using:

This procedure is not going to describe this process, however once the profile is generated the user has to choose a Device Clock by selecting a possible Ref Clk Divider. In theory the RF Transceiver can handle all offered options, while typically higher rates are better from a phase noise point of view. But the baseband processor (FPGA) has lots of constrains as well. So practically these limitations must be taken into account as well before making a decision.

It’s a bit out of scope to cover capabilities and constrains of all FPGA vendor architectures. (Xilinx GTX, GTH, GTY type transceivers with their CPLLs and QPLLs or Intel ATX PLL, FPLL, CDR PLL, etc.)

But a good value from various perspectives (such as deterministic latency and link clock) is to choose Lane rate / 40.

Some typical example below, where TX and ORX run at the same baseband rate, and RX is ½ TX rate.

  • TX : FC=240 MSPS, M=4, L=4, N’=16, S=1:
    • Lane Rate = (M x S x N' x 10/8 x FC)/L = 4800Mbit/s
  • RX : FC=120 MSPS, M=4, L=2, N’=16, S=1:
    • Lane Rate = (M x S x N' x 10/8 x FC)/L = 4800Mbit/s
  • ORX: FC=240 MSPS, M=2, L=2, N’=16, S=1:
    • Lane Rate = (M x S x N' x 10/8 x FC)/L = 4800Mbit/s


So here Device Clock == FMC Clock = Lane rate / 40 = 120 MHz would be a perfect match. We choose 120MHz in this configuration.

Once we have changed the clock chip distribution clock, we must also update the default transceiver profile, which is configured for initial setup. Failure to do so can result in the transceiver device driver asking for a clock tree which is not achievable since the AD9528 clock chip driver dynamically controls the output divider but not the PLL2 VCO and distribution clock.

The MATLAB Filter Wizard / Profile Generator generates a XML style output file. It’s pretty easy to understand and can be easily matched to the Linux device tree properties. Besides single key value pairs there are also signed filter coefficients which must be converted to an array style. It’s important to know that signed values must be put in braces. Everything that differs in value must be updated.

Below you cab find a quick and dirty bash script, which allows you to extract the filter arrays:

#!/bin/bash
 
while IFS='' read -r line || [[ -n "$line" ]]; do
	if [[ $line == *"filter"* ]]; then
		echo
		start="1"
	fi
	if [[ $line == *"adc-profile"*"num"* ]] || [[ $line == *"AdcProfile"*"num"* ]]; then
		echo
		start="1"
	fi
	if [[ $line == *"</"* ]]; then
		echo
		start="0"
	fi
 
	if [ "$start" == "1" ];then
		echo -n "(`echo $line | xargs`) "
	fi
done < "$1"

In the example below we don’t change things directly in the provided default device trees. In fact there are several which recursively include them. Instead we create a new file (zynq-zc706-adv7511-adrv9371-vcxo80.dts), place it in the same folder, and we include the default dts file. Then we reference the devicetree phandles and either overwrite or delete properties. This way we only handle changes which matters and don't end up maintaining a complete new file.

Devicetrees can be found or must be placed here
ZYNQ & SoC FPGA linux/arch/arm/boot/dts/
ZYNQMP linux/arch/arm64/boot/dts/xilinx/

Instructions to build and deploy your new/modified devicetree can be found here:

The complete zynq-zc706-adv7511-adrv9371-vcxo80.dts file would look like this:

#include "zynq-zc706-adv7511-adrv9371.dts"

&clk0_ad9528 {
		adi,vcxo-freq = <80000000>;

		/*
		 * Valid ranges based on VCO locking range:
		 *   1150.000 MHz - 1341.666 MHz
		 *    862.500 MHz - 1006.250 MHz
		 *    690.000 MHz -  805.000 MHz
		 */
		adi,pll2-m1-frequency = <1200000000>;

		/* Manual divider configuration /
		/delete-proptery/ adi,pll2-ndiv-a-cnt;
		/delete-proptery/ adi,pll2-ndiv-b-cnt;
		/delete-proptery/ adi,pll2-vco-div-m1;
		/delete-proptery/ adi,pll2-n2-div;
		/delete-proptery/ adi,pll2-r1-div;

		/* PLL1 config */
		adi,pll1-feedback-div = <8>; /* 10 MHz Reference */
};

&trx0_ad9371 {
	adi,clocks-clk-pll-vco-freq_khz = <9600000>;
	adi,clocks-device-clock_khz = <120000>;

        adi,obs-settings-obs-rx-channels-enable = <3>; /* Disable Sniffer Profile */

	adi,rx-profile-iq-rate_khz = <120000>;
	adi,rx-profile-rf-bandwidth_hz = <80000000>;
	adi,rx-profile-rx-fir-gain_db = <(0)>;
	adi,rx-profile-rx-fir-num-fir-coefs = <48>;
	adi,rx-profile-rx-fir-coefs = /bits/ 16 <(0)(0)(1)(2)(-3)(-10)(12)(28)(-33)(-71)(81)(154)(-174)(-305)(343)(561)(-635)(-989)(1155)(1750)(-2330)(-4059)(4644)(16559)(16559)(4644)(-4059)(-2330)(1750)(1155)(-989)(-635)(561)(343)(-305)(-174)(154)(81)(-71)(-33)(28)(12)(-10)(-3)(2)(1)(0)(0)>;
	adi,rx-profile-custom-adc-profile = /bits/ 16  <(574)(382)(201)(98)(1280)(342)(1553)(180)(1285)(67)(784)(33)(48)(38)(23)(189)>;

	adi,obs-profile-iq-rate_khz = <240000>;
	adi,obs-profile-rf-bandwidth_hz = <160000000>;
	adi,obs-profile-rx-bbf-3db-corner_khz = <80000>;
	adi,obs-profile-rx-fir-gain_db = <6>;
	adi,obs-profile-rx-fir-num-fir-coefs = <24>;
	adi,obs-profile-rx-fir-coefs = /bits/ 16 <(-51)(-107)(90)(-29)(-72)(128)(-11)(-279)(178)(-46)(-2343)(21563)(-2343)(-46)(178)(-279)(-11)(128)(-72)(-29)(90)(-107)(-51)(0)>;
	adi,obs-profile-custom-adc-profile = /bits/ 16 <(499)(386)(201)(98)(1280)(534)(1741)(601)(1423)(456)(857)(27)(48)(38)(25)(205)>;
	adi,obs-settings-custom-loopback-adc-profile = /bits/ 16  <(581)(379)(201)(98)(1280)(304)(1544)(157)(1288)(59)(787)(34)(48)(39)(23)(189)>;

	adi,tx-profile-dac-div = <1>;
	adi,tx-profile-iq-rate_khz = <240000>;
	adi,tx-profile-rf-bandwidth_hz = <160000000>;
	adi,tx-profile-tx-bbf-3db-corner_khz = <80000>;
	adi,tx-profile-tx-dac-3db-corner_khz = <160000>;
	adi,tx-profile-tx-fir-gain_db = <6>;
	adi,tx-profile-tx-fir-num-fir-coefs = <16>;
	adi,tx-profile-tx-fir-coefs = /bits/ 16 <(-48)(290)(-51)(-251)(577)(167)(-3254)(21872)(-3254)(167)(577)(-251)(-51)(290)(-48)(0)>;
};

Troubleshooting

Requesting device clock failed

ad9371 spi32766.1: Requesting device clock 120000000 failed got 122880000

If you see something like this in your kernel startup messages (dmesg) this indicates that the clock chip distribution clock was not properly set. Check the AD9528 device tree properties.

Requesting [deframer|framer] lanerate failed

adrv9009 spi1.1: adrv9009_probe : enter
adrv9009 spi1.1: Request deframer lanerate 4800000 kHz failed (-22)
adrv9009 spi1.1: Request deframer lanerate 4800000 kHz failed (-22)
adrv9009: probe of spi1.1 failed with error -22 

Depending on the FPGA gigabit transceiver architecture GTX, GTH, GTY, etc. and the PLL used the VCO tuning ranges might not fit the requested rate. In this case ‘’Request [deframer|framer] lanerate” error is printed. And the driver probe errors with -EINVAL (-22).

For example GTX QPLL actually have two none overlapping frequency bands with a hole in the middle. GTH and GTY do have two QPLLs with slightly different VCO min/max.

  • To avoid these issues increasing/decreasing the number of Lanes used can be useful.
  • Switching from QPLL0 to QPLL1 might help as well.
  • Or using CPLL for both links can help, but only in case both links require the same rate.
  • Sometimes the requested value is just a bit out of the specified range. Hacking the driver to exceed the limits might work, but is not recommended!
index e425c12..81e86e9 100644
--- a/drivers/iio/jesd204/xilinx_transceiver.c
+++ b/drivers/iio/jesd204/xilinx_transceiver.c
@@ -6,7 +6,7 @@
  * Licensed under the GPL-2.
  *
  */
-
+#define DEBUG
#include <linux/device.h>
#include <linux/fpga/adi-axi-common.h>
#include <linux/kernel.h>
@@ -408,7 +408,7 @@ int xilinx_xcvr_calc_qpll_config(struct xilinx_xcvr *xcvr,
                N = N_gtx2;
                vco0_min = 5930000;
                vco0_max = 8000000;
-               vco1_min = 9800000;
+               vco1_min = 9600000; /* FIXME: 9800000 */
                vco1_max = 12500000;
                break;
        case XILINX_XCVR_TYPE_US_GTH3:
@@ -424,10 +424,10 @@ int xilinx_xcvr_calc_qpll_config(struct xilinx_xcvr *xcvr,
                return -EINVAL;
        }
 
-       if (AXI_PCORE_VER_MAJOR(xcvr->version) > 0x10)
-               xilinx_xcvr_setup_qpll_vco_range(xcvr,
-                                                &vco0_min, &vco0_max,
-                                                &vco1_min, &vco1_max);
+//     if (AXI_PCORE_VER_MAJOR(xcvr->version) > 0x10)
+//             xilinx_xcvr_setup_qpll_vco_range(xcvr,
+//                                              &vco0_min, &vco0_max,
+//                                              &vco1_min, &vco1_max);
 
        for (m = 1; m <= 4; m++) {
                for (d = 1; d <= 16; d <<= 1) {

ARM Mailbox Busy. Command not executed in MYKONOS_sendArmCommand()

ERROR: 256: ARM Mailbox Busy. Command not executed in MYKONOS_sendArmCommand()
ERROR: 256: ARM Mailbox Busy. Command not executed in MYKONOS_sendArmCommand()
ad9371 spi0.1: ARM Mailbox Busy. Command not executed in MYKONOS_sendArmCommand()
(256)
ERROR: 256: ARM Mailbox Busy. Command not executed in MYKONOS_sendArmCommand()
ERROR: 256: ARM Mailbox Busy. Command not executed in MYKONOS_sendArmCommand()
ad9371 spi0.1: ARM Mailbox Busy. Command not executed in MYKONOS_sendArmCommand()
(256)
ad9371: probe of spi0.1 failed with error -14

This indicates an ARM firmware internal error. Which can be caused by some erroneous or incomplete profile. Please double check all the settings in the devicetree.

resources/eval/user-guides/rf-trx-vcxo-and-profiles.txt · Last modified: 24 May 2019 10:14 by mhennerich