Reprogramming a Series-7 MMCM for fractional division ratios

This post was written by eli on December 23, 2014
Posted Under: FPGA

Introduction

Xilinx’ Series-7 FPGAs (Virtex-7, Kintex-7, Atrix-7 and Zynq-7000) offer a rather flexible frequency synthesizer, (MMCE2) allowing steps of 0.125 for setting the VCO’s multiplier and one of its dividers. The MMCE can be reprogrammed through its DRP interface, so it can be used as a source of a variable clock frequency.

These are a few notes taken while implementing a reprogrammable frequency source using the MMCE2_ADV.

Resources

The main resource on this matter is Xilinx’ application note 888 (XAPP888) as well as the reference design that can be downloaded from Xilinx’ web site.

As of XAPP888 v1.3 (October 2014), there are a few typos:

  • Table 6: PHASE_MUX_F_CLKFB is on bits [13:11] and not [15:13]
  • Table 6: FRAC_WF_F_CLKFB is on bit 10 and not 12.
  • Table 7: FRAC_EN is related to CLKFBOUT, and not CLKOUT0

The reference design is written in synthesizable Verilog, but the parts that calculate the assignments to the DRP registers are written as Verilog functions, so they can’t be used as is for an arbitrary frequency clock generator. To make things even trickier, the coding style employed in this reference looks like a quiz in deciphering obscured code (or just a Verilog parody).

As a result, it’s somewhat difficult to obtain functional logic (or possibly a computer program) for setting the registers correctly for any allowed combination of parameters. The notes below may assist in getting things straight.

A sample set of DRP registers

For reference, an MMCE was implemented on a Kintex-7 device, after which the entire DRP space was read.

The instantiation of this MMCE was

MMCME2_ADV
 #(.BANDWIDTH          ("OPTIMIZED"),
 .CLKOUT4_CASCADE      ("FALSE"),
 .COMPENSATION         ("ZHOLD"),
 .STARTUP_WAIT         ("FALSE"),
 .DIVCLK_DIVIDE        (1),
 .CLKFBOUT_MULT_F      (5.125),
 .CLKFBOUT_PHASE       (0.000),
 .CLKFBOUT_USE_FINE_PS ("FALSE"),
 .CLKOUT0_DIVIDE_F     (40.250),
 .CLKOUT0_PHASE        (0.000),
 .CLKOUT0_DUTY_CYCLE   (0.500),
 .CLKOUT0_USE_FINE_PS  ("FALSE"),
 .CLKIN1_PERIOD        (5.0),
 .REF_JITTER1          (0.010)

The register map:

 00:  a600 0082 0003 0000 0127 9814 0041 0c40
 08:  14d3 2c00 0041 0040 0041 0040 0041 0040
 10:  0041 0040 0041 2440 1081 1880 1041 1041
 18:  03e8 3801 bbe9 0000 0000 0210 0000 01e9
 20:  0000 0000 0000 0000 0000 0000 0000 0000
 28:  9900 0000 0000 0000 0000 0000 0000 0000
 30:  0000 0000 0000 0000 0000 0000 0000 0000
 38:  0000 0000 0000 0000 0000 0000 0000 0000
 40:  0000 0000 8080 0000 0000 0800 0001 0000
 48:  0000 7800 01e9 0000 0000 0000 9108 1900

(Note to self: Use “predesign” git bundle, checkout e.g. ’138358c’, run build TCL script on Vivado 2014.1 and then on PC compile and run ./dump_drp_regs)

Fractional divider register settings

Two dividers in each MMCE2 allow a fractional division ratio: The feedback divider (CLKFBOUT_MULT_F, effectively the clock multiplier) and the output divider for one clocks (CLKOUT0_DIVIDE_F).

The reference design assign correct values in the relevant registers, but is exceptionally difficult to decipher.

The algorithm for calculating the register’s value is the same for CLKFBOUT_MULT_F and CLKOUT0_DIVIDE_F. The values obtained for all registers, except high_time and low_time, depend only on (8x mod 16), where x is either CLKFBOUT_MULT_F or CLKOUT0_DIVIDE_F, given as the actual division ratio.

The values of the registers as set by Vivado are given for the division ratio going from 4.000 to 5.875, in steps of 0.125. (high_time and low_time shown below may appear not to agree with this, but these are the actual numbers).

frac_en high_time low_time edge frac phase_mux_f frac_wf_r frac_wf_f
0 2 2 0 0 0 0 0
1 1 1 0 1 0 1 0
1 1 1 0 2 1 1 1
1 1 1 0 3 1 1 1
1 1 1 0 4 2 1 1
1 1 1 0 5 2 1 1
1 1 1 0 6 3 1 1
1 1 1 0 7 3 1 1
0 2 3 1 0 0 0 0
1 2 1 1 1 4 0 1
1 2 2 1 2 5 0 0
1 2 2 1 3 5 0 0
1 2 2 1 4 6 0 0
1 2 2 1 5 6 0 0
1 2 2 1 6 7 0 0
1 2 2 1 7 7 0 0

Loop filter and lock parameters

Depending on the feedback divider’s integer value (MULT_F in the table below), several registers, which are related to the lock detection and the loop filter, are set with values taken from a lookup table in the reference design. Comparing the values assigned by Vivado 2014.1 (again, by reading back the DRP registers) with those in the reference design for a selected set of MULT_Fs reveals a match as far as the lock detection registers are concerned. However the registers related to the digital loop filter were set to completely different values by Vivado. As there is no documentation available on these registers, it’s not clear what impact this difference has, if at all.

The following table shows the values assigned by Vivado 2014.1 for a set of MULT_F’s. The rightmost columns show the bits of of the loop filter bits, in the same order that they appear in the reference design (MSB to LSB, left to right). All other columns are given in plain decimal notation.

MULT_F LockRefDly LockFBDly LockCnt LockSatHigh UnlockCnt x x x x x x x x x x
4 11 11 1000 1001 1 0 1 1 1 0 1 1 1 0 0
8 22 22 1000 1001 1 1 1 1 1 0 0 1 1 0 0
12 31 31 825 1001 1 1 1 0 1 0 0 0 1 0 0
16 31 31 625 1001 1 1 1 1 1 1 0 0 1 0 0
20 31 31 500 1001 1 1 1 0 0 0 0 0 1 0 0
24 31 31 400 1001 1 0 1 0 1 1 1 0 0 0 0
28 31 31 350 1001 1 0 0 1 1 0 1 0 0 0 0
32 31 31 300 1001 1 0 0 1 1 0 1 0 0 0 0

Sporadic tests with setting these registers as if the MULT_F was completely different (e.g. as if MULT_F=64 for much lower actual setting) reveals that nothing apparent happens — no loss of locks, and no apparent difference in jitter performance (not measured though). Also, the VCO of the tested FPGA (with speed grade 2) remained firmly locked at frequencies going as low as 20 MHz (using non-fractional ratios) and as high as 3000 MHz (even though the datasheet ensures 600-1440 MHz only). This was run for several minutes on each frequency with a junction temperature of 56°C.

All in all, there’s an uncertainty regarding the loop filter parameters, but there’s reason to hope that this has no practical significance.

Reader Comments

Hi.

I implemented MMCM with the ability of dynamic reconfiguration to the settings which are not known in advance. When I know the required multiplier and dividers, I pass those numbers to the functions provided from Xilinx reference design and then program the MMCM. It works, but I get several critical warnings, because timing requirements are not met.

I thought I should rewrite those functions into modules to obtain the register values in more cycles. But that would be a lot of work.

Another option is to set a multicycle path constraint for these paths and take into account that in the design.

Is there any other option?

Thanks

#1 
Written By Matic on September 9th, 2017 @ 11:01

Add a Comment

required, use real name
required, will not be published
optional, your blog address