Showing results for 
Show  only  | Search instead for 
Did you mean: 

Zynq UltraScale+ RFSoC Gen3: Programming the CLK104 module from the RFSoC APU

8 2 3,030

Hello again and welcome to the latest RF Data Converter Blog.

The Release of the third Generation in the Zynq UltraScale+ RFSoC Product Family has brought with it some added flexibility on data converter clocking, namely the ability to simplify the PCB design by adding support for internal distribution of the RF Sampling clocks or the Tile PLL reference clocks.

The ZCU216 Evaluation board comes with a CLK104 add-on card. This provides a flexible clocking solution to evaluate the RFSoC ZU49DR clocking options. This can be programmed by the System Controller GUI that comes as part of the kit. However. in this blog I will show how the CLK104 module can instead be programmed by the APU on the RFSoC and in the process demonstrate some of the new internal clock distribution options on RFSoC Gen3.

So, let’s start with the hardware. For the purposes of this example I am using the ZCU216 board and the CLK104 Module. I have shown the block diagram of the CLK104 in the figure below.

In this demo, I will just highlight what I think is most relevant. For a complete reference you should look at the user guide for the CLK104 module, (UG1437).

The scheme here is similar to what is employed on the ZCU111. The LMK04828 is used as a jitter cleaner and clock generator. The input for the PLL1, circled in blue, is from either the TCXO on the card, an external input (this will facilitate syncing to an external input) or optionally you can take a recovered clock from the RFSoC device.




The LMK04828 produces the reference clocks for the LMX2594 PLLs that produce RF Sampling clocks for the RF ADCs and DACs. These two PLLs outputs come out to the SSMP header on the card (circled in yellow).

These connect to DAC tile 229 and ADC tile 225 using the Carlisle SSMP connector on the board.


LMK04828 also generates two reference clocks (circled in green) for the ADC and DAC Tile PLL on the RFSoC. This connection is routed to ADC Tile 226 and DAC tile 230 via the LPAM connector on the card.



Additionally, the SYSREF and the PL Reference clock that are used during the multi-Tile Synchronization process are outputs of the LMK04828.  

You will see that there is an option to program the CLK104 with the TI dongle. We also have a connection to the board via I2C (circled in Red in the block diagram above). This will allow us to program the CLK104 from the APU on the RFSoC.  

Gen 3 Clock Distribution

Before we move on, we should look at the new Gen3 clock distribution feature and how we can use the CLK104 module to demonstrate this feature.

The first thing to say is that on-die clock distribution is an additional feature of the data converter clocking on RFSoC Gen3 but you always have the option to clock each tile independently. This maintains full backwards compatibility with RFSoC Gen1 and Gen2.

However, the ability to simplify the clock routing on the PCB, Reduce the BOM cost and add flexibility is compelling so it makes sense to give customers the option of distributing a full rate clock or reference clock between tiles on the die.

In short there are three supported use-cases for clock distribution.


Distributing a Sample Clock from an external RF Source

You can bring a full rate sample clock from the board to one of the center DAC tiles and distribute it to all of the other DAC tiles. Likewise, you could route the ADC sample clock to one of the middle tiles and distribute it to all of the other ADC tiles. Bringing the clock to the center tile is done to ensure load and delay balancing of the distributed clock.

In this case we could program the two LMX PLLs on the CLK104 to provide the sample clocks and use the Carlisle loopback cable to bring the clocks to DAC tile 229 and ADC tile 225 respectively. We would then be able to distribute to the other ADC tiles from ADC tile 225 and all other DAC tiles from DAC tile 229.

Note that distributing a sample clock from a DAC tile to an ADC tile is not supported.

Generate and Distribute a Sample Clock from a tile PLL

The other way to distribute the sample clock between DAC or ADC tiles is to enable the PLL in one of the center DAC or ADC tiles and distribute the sample clock from the tile PLL to other DAC and ADC tiles.

For this we could use the two LMX PLLs on the CLK104 board to provide the reference clocks to the PLL in DAC tile 229 and ADC Tile 225. Alternatively, we could program the LMK output clocks that connect to DAC tile 230 and ADC tile 226 to provide the reference to the PLL in these tiles which could then be distributed amongst the DAC and ADC tile groups.

Note that there is a maximum frequency that we support of this reference clock and again there is no support for distribution of a DAC clock to an ADC tile.

Distribute a Reference Clock

Finally, there is the option to route a single reference clock from the board to one of the DAC tiles and distribute it to any tile PLL. In this case we could use the LMK to provide this reference to DAC tile 230 and distribute it to the input of all DAC and ADC tile PLLs.

Alternatively, the LMX PLL could be used to provide a higher frequency reference to DAC tile 229 for distribution.

Note that there is a maximum supported frequency for this reference.

For a complete description of the IP support for internal clock distribution you can refer to the clocking section in (PG269).

Programming the CLK104 Module

Let’s now turn our attention to programming the CLK104 Module.

I think it could be a good idea to pick the use-case where we bring in a reference clock to DAC tile 229 and distribute it to the PLLs in all of the other tiles. Let’s pick a reference clock of 491.52Mhz. This means that we will need to program the LMK(U2) and the LMX that drives the DAC Tile 229 (U7).

Fortunately, Xilinx provides a driver called XRFCLK to enable programming these devices over the I2C on the RFSoC boards, so we’ll use this to create an application that will program our desired settings to the CLK104. This driver is written using the libmetal framework so it can be used to create both Linux and Baremetal Applications.

The first step is to create a hardware design for ZCU216 that contains the RF data converter IP configured with our desired clock distribution.

In your design you should apply all of the board presets for the ZCU216. This ensures that the UART and the I2C that we need to access the CLK104 modules is set up before we proceed.

You will also need to include an AXI GPIO in the PL that can be used to control the SPI select mux on the CLK104


If we look at the system clock tab in the RF data converter IP in the 49DR design, we can configure the clock distribution between the tiles. Refer to the screenshot of the IP clocking tab below.



In this case we want all tiles clocked from the tile PLL with a 491.52Mhz reference clock. This is set in the fields in the blue box. Next, we go to the Distribute Clock column and set the DAC Tile 229 to distribute its reference clock (highlighted in Red). All other tiles are set to not distribute any clock.

Lastly, we specify that we want the source of all of the other tiles to come from DAC229.This is shown in the green box. 

That’s it for the RFDC IP.

Next we implement our hardware design and Export the XSA for Vitis.

I’m going to create a blank application in Vitis and use the example that comes with the XRFCLK driver as a template to program the CLK104 in order to provide the desired reference clock to the 49DR on ZCU216.



We’ll briefly look at how this software example is built using the XRFCLK driver. This will provide a better understanding of how you can use this to program the CLK104 in your own setup.

The structure of the application looks like this:


This should give you an appreciation of how the driver can be used.

xrfck.c is where the API calls used to build the application are implemented. This driver relies on the I2C and SPI drivers for communications so you can see that these are used to help implement the functionality.

This c code uses the LMK/LMX header files. These files contain structures that represent the register settings for a given configuration of the LMK or LMX PLLs. Your application includes the header file for xrfck.h, which allows you to make use of the API calls in the driver to program the CLK104.

We’ll refer to the example to show whats possible here.

XRFClk_Init is an API call here that initializes the I2C and SPI drivers, so this is mandatory.

After this you will see that there is an API used to reset the PLLs. XRFClk_ResetChip.

Next the application does a sanity check. It does a write followed by a read to each PLL to be to sure that the communication is good.

The syntax is as follows:

XRFClk_WriteReg(RFCLK_LMX2594_1, d) or XRFClk_ReadReg(RFCLK_LMX2594_1, d)

The API accepts the chip ID, so one of the LMX or the LMK, and it writes the value d that you pass to the API where d = {D2, D1, D0}

[D0,D1,D2] bits are:


  • bit [23] - 1-bit command field (R/W)
  • bits [22:21] - 2-bit multi-byte field (W1, W0)
  • bits [20:8] - 13-bit address field (A12 to A0)
  • bits [7-0]- 8-bit data field (D7 to D0).


  • bit [23] - 1-bit command field (R/W)
  • bits [22:16] - 7-bit address field (A6 to A0)
  • bits [15-0]- 16-bit data field (D15 to D0).

This shows that you can write to each PLL with a lot of granularity if needed.

However, to program the settings efficiently we need a better API.

This comes in the form of XRFClk_SetConfigOnOneChipFromConfigId(u32 ChipId, u32 ConfigId)

So in this case you must pass it the ID of the PLL you want to configure, the LMK or either of the two LMX PLLs along with the configuration you want to apply. This configuration comes from the LMK or LMX header file.

So you need to pick settings that work for our use case.

I picked setting 0 for the LMK as this will give me a 245.76Mhz clock to pass to the LMX.


Now I’ll select the 0 element in the LMX array to give me the 491.52Mhz output.



Here’s a snippet of the code used to configure the LMK/LMX PLLs using the configID from the structures.



It is also possible to do all PLLs at once with this API call. 

u32 XRFClk_SetConfigOnAllChipsFromConfigId(u32 ConfigId_LMK, u32 ConfigId_RF1,

#ifdef XPS_BOARD_ZCU111

u32 ConfigId_RF2, u32 ConfigId_RF3);


u32 ConfigId_RF2);



Here you pass it the configuration you want for each PLL and the API takes care of the rest.

After you run the programming it is possible to read back what you just wrote, so you can validate that the programming is correct.

Below is the output from my serial output.

You can see that each PLL is programmed correctly.


To summarize, we’ve looked at the CLK104 solution, the Gen3 Clocking cases we can support, and we’ve walked through the example.

I hope you have found this blog useful, stay tuned for more RF blogs coming in the near future.



Very good data, i was looking for same data from last one month.



Hi @klumsde 

What's the best way to bring in the XRFCLK example source? Obviously you could just copy the source from github but I note that the source also exists as part of the Vitis installation:


So should this source be able to be pulled in from within Vitis?