• 0
rangaraj

MMCM dynamic clocking

Question

Hi,

 I need to create a clock dynamically from few kilo hertz to 75megaHz.

 I understood that I can use MMCM.

1.is it possible to dynamically change the MMCM.?

2.Will it work in few 10's of KHz ?

 Below is 52MHz clock. I want to change dynamically from KHz to Mhz  with the below code .

  -- Clocking PRIMITIVE
  --------------------------------------
  -- Instantiation of the MMCM PRIMITIVE
  --    * Unused inputs are tied off
  --    * Unused outputs are labeled unused
  mmcm_adv_inst : MMCME2_ADV
  generic map
   (BANDWIDTH            => "OPTIMIZED",
    CLKOUT4_CASCADE      => FALSE,
    COMPENSATION         => "ZHOLD",
    STARTUP_WAIT         => FALSE,
    DIVCLK_DIVIDE        => 1,
    CLKFBOUT_MULT_F      => 10.500,
    CLKFBOUT_PHASE       => 0.000,
    CLKFBOUT_USE_FINE_PS => FALSE,
    CLKOUT0_DIVIDE_F     => 8.750,
    CLKOUT0_PHASE        => 0.000,
    CLKOUT0_DUTY_CYCLE   => 0.500,
    CLKOUT0_USE_FINE_PS  => FALSE,
    CLKIN1_PERIOD        => 10.0,
    REF_JITTER1          => 0.000)
  port map
    -- Output clocks
   (
    CLKFBOUT            => clkfbout_clk_wiz_0,
    CLKFBOUTB           => clkfboutb_unused,
    CLKOUT0             => clk_out1_clk_wiz_0,
    CLKOUT0B            => clkout0b_unused,
    CLKOUT1             => clkout1_unused,
    CLKOUT1B            => clkout1b_unused,
    CLKOUT2             => clkout2_unused,
    CLKOUT2B            => clkout2b_unused,
    CLKOUT3             => clkout3_unused,
    CLKOUT3B            => clkout3b_unused,
    CLKOUT4             => clkout4_unused,
    CLKOUT5             => clkout5_unused,
    CLKOUT6             => clkout6_unused,
    -- Input clock control
    CLKFBIN             => clkfbout_buf_clk_wiz_0,
    CLKIN1              => clk_in1_clk_wiz_0,
    CLKIN2              => '0',
    -- Tied to always select the primary input clock
    CLKINSEL            => '1',
    -- Ports for dynamic reconfiguration
    DADDR               => (others => '0'),
    DCLK                => '0',
    DEN                 => '0',
    DI                  => (others => '0'),
    DO                  => do_unused,
    DRDY                => drdy_unused,
    DWE                 => '0',
    -- Ports for dynamic phase shift
    PSCLK               => '0',
    PSEN                => '0',
    PSINCDEC            => '0',
    PSDONE              => psdone_unused,
    -- Other control and status signals
    LOCKED              => locked_int,
    CLKINSTOPPED        => clkinstopped_unused,
    CLKFBSTOPPED        => clkfbstopped_unused,
    PWRDWN              => '0',
    RST                 => '0');


  -- Output buffering
  -------------------------------------

  clkf_buf : BUFG
  port map
   (O => clkfbout_buf_clk_wiz_0,
    I => clkfbout_clk_wiz_0);

 

  clkout1_buf : BUFG
  port map
   (O   => clk_out1,
    I   => clk_out1_clk_wiz_0);

 

Thanks and Regards

Lakshman.

 

 

 

 

Share this post


Link to post
Share on other sites

9 answers to this question

Recommended Posts

  • 0

1. Yes, the MMCM has ports that allow reprogramming - but it is quite involved. See around page 75 of  https://www.xilinx.com/support/documentation/user_guides/ug472_7Series_Clocking.pdf

Making sure that your timing constraints are correct is also quite tricky. 

2. No, you will not get that range out if it - it still has to be in spec for the MMCM (from a few MHz to a few hundred - check the datasheet).

Also switching will cause glitches if not done right - to avoid this you need to use a BUFGMUX, (NOTE: for a BUFGMUX to switch, you need to clock edges appearing on both of it's inputs!)

I've not done it myself, but I suggest you use use the MMCM to generate a few fixed frequencies (e.g. 33MHz, 10MHz) then have divide by 10 counter hung of them, to give you 3.3MHz, 1.0MHz, 333kHz, 100kHz and so on.

Then have two N:1 MUXes use a few to select which frequency clock you want to use. 

e.g:  case a is 

    when "000" =>
       clk_a <= clk_33000kHz;

    when "001" =>
       clk_a <= clk_10000kHz;

Then feed clk_a and clk_b into a BUFGMUX (you may need a BUFG to here to get the signals back on the clocking networks), and switch between them - first change the input used for the inactive clock to the desired frequency (e.g. if clk_a is being used, change the selection for clk_b) then change the select input to the BUFGMUX to switch over onto that clock.

Here's an experiment of mine, that might give you ideas: http://hamsterworks.co.nz/mediawiki/index.php/Single_Step

 

Share this post


Link to post
Share on other sites
  • 0

dear Hamster,

 I went through the guide. But I didn't get any clue how to change it.

I knew that we need to change the M & F to get the desired frequency as below.

       CLKFBOUT_MULT_F      => 10.500,
        CLKOUT0_DIVIDE_F     => 8.750,

I knew counter based solution where we fixed one clock & further divide the clock to get the desired clock.

I am looking for something like DCM http://hamsterworks.co.nz/mediawiki/index.php/FreqSwitch

is it possible ?

Thanks and Regards

Lakshman.

 

 

 

Share this post


Link to post
Share on other sites
  • 0

@rangaraj,

What you are asking about is something called "Dynamic Reconfiguration".  You'll find the details regarding "MMCM and PLL Dynamic Reconfiguration" in this App note.  As I recall, though, the MMCM needs to  generate an internal clock between 800MHz and 1600MHz, which you then divide down by an integer amount--something between 1 and 32, although I can't find these numbers right now.  That would limit your clock options to something between 1600MHz and 25 MHz.

There are alternatives that will generate even lower frequencies--alternatives that are appropriate for some applications.

For example, if you can tolerate the phase noise of half the generating clock interval, you can generate a "clock" signal using something like:

reg			stb;
reg [31:0]	counter;

always @(posedge i_clk)
	{ stb, counter } <= counter + programmable_delay;
assign output_clock = counter[31];

This will allow you the greatest range of clock generation possibilities (from your input clock rate down to Hz)-- if you just want something to happen at a programmable time.  Since it's a logic generated clock, you wouldn't want to transition on either it's positive or negative edges, but rather you'd want your logic to look more like

always @(posedge i_clk)
	if (stb)
	begin
		// your logic goes here
	end

Alternatively, you could use the "output_clock" value from the above clock generator, and feed that pin through the clock management circuitry.  You can then use it to actually generate a truly arbitrary clock.  Further, if you have to go that road, you can get the phase noise down by outputting the clock on a pin through the OSERDESE2 structure and then bringing it back in through a PLL--however, that'll bring you back to stuck within the limits of what a PLL or MMCM can provide.

Dan

Edited by D@n

Share this post


Link to post
Share on other sites
  • 0

I feel a bit bad about posting a minor novel here, but here is an example of going from "5 cycles on, 5 off" (i.e. divide by 10) to "10 on, 10 off" (device by 20).

The VCO is initially to 800 MHz with CLK0 being VCO divide by 8.... so after config you get 100MHz.

Push the button and you get 800/20 = 40MHz, release the button and you get 80MHz.

It is all really hairy in practice!

EDIT: Through experimentation I just found that you don't need to reset the MMCM if you are not changing the VCO frequency. So the 'rst' signal in the code below isn't needed (and LOCKED will stay asserted).

--------------------------------------------------------------------------------------------------------
-- Playing with the MMCM DRP ports.
-- see https://www.xilinx.com/support/documentation/application_notes/xapp888_7Series_DynamicRecon.pdf
-- for the Dynamic Reconviguration Port addresses
--------------------------------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

library UNISIM;
use UNISIM.VComponents.all;

entity mmcm_reset is
    Port ( clk_100 : in STD_LOGIC;
           btn_raw : in STD_LOGIC;
           led     : out STD_LOGIC_VECTOR (15 downto 0));
end mmcm_reset;

architecture Behavioral of mmcm_reset is
    signal btn_meta     : std_logic := '0';
    signal btn          : std_logic := '0';
    signal speed_select : std_logic := '0';
    signal counter      : unsigned(26 downto 0) := (others => '0');
    signal debounce     : unsigned(15 downto 0) := (others => '0');
    signal clk_switched : std_logic := '0';
    signal clk_fb       : std_logic := '0';
    
    type t_state is (state_idle_fast, state_go_slow_1, state_go_slow_2, state_go_slow_3,
                       state_idle_slow, state_go_fast_1, state_go_fast_2, state_go_fast_3);
    signal state   : t_state := state_idle_fast;
    
    
    -----------------------------------------------------------------------------
    --- This is the CLKOUT0 ClkReg1 address - the only register to be played with 
    -----------------------------------------------------------------------------
    signal daddr : std_logic_vector(6 downto 0) := "0001000";
    signal do    : std_logic_vector(15 downto 0) := (others => '0');
    signal drdy  : std_logic := '0';
    signal den   : std_logic := '0';
    signal di    : std_logic_vector(15 downto 0) := (others => '0');
    signal dwe   : std_logic := '0';
    signal rst   : std_logic := '0';
            
begin


   MMCME2_ADV_inst : MMCME2_ADV
   generic map (
      BANDWIDTH => "OPTIMIZED",      -- Jitter programming (OPTIMIZED, HIGH, LOW)
      CLKFBOUT_MULT_F => 8.0,        -- Multiply value for all CLKOUT (2.000-64.000).
      CLKFBOUT_PHASE => 0.0,         -- Phase offset in degrees of CLKFB (-360.000-360.000).
      -- CLKIN_PERIOD: Input clock period in ns to ps resolution (i.e. 33.333 is 30 MHz).
      CLKIN1_PERIOD => 10.0,
      CLKIN2_PERIOD => 0.0,
      -- CLKOUT0_DIVIDE - CLKOUT6_DIVIDE: Divide amount for CLKOUT (1-128)
      CLKOUT1_DIVIDE => 1,
      CLKOUT2_DIVIDE => 1,
      CLKOUT3_DIVIDE => 1,
      CLKOUT4_DIVIDE => 1,
      CLKOUT5_DIVIDE => 1,
      CLKOUT6_DIVIDE => 1,
      CLKOUT0_DIVIDE_F => 8.0,       -- Divide amount for CLKOUT0 (1.000-128.000).
      -- CLKOUT0_DUTY_CYCLE - CLKOUT6_DUTY_CYCLE: Duty cycle for CLKOUT outputs (0.01-0.99).
      CLKOUT0_DUTY_CYCLE => 0.5,
      CLKOUT1_DUTY_CYCLE => 0.5,
      CLKOUT2_DUTY_CYCLE => 0.5,
      CLKOUT3_DUTY_CYCLE => 0.5,
      CLKOUT4_DUTY_CYCLE => 0.5,
      CLKOUT5_DUTY_CYCLE => 0.5,
      CLKOUT6_DUTY_CYCLE => 0.5,
      -- CLKOUT0_PHASE - CLKOUT6_PHASE: Phase offset for CLKOUT outputs (-360.000-360.000).
      CLKOUT0_PHASE => 0.0,
      CLKOUT1_PHASE => 0.0,
      CLKOUT2_PHASE => 0.0,
      CLKOUT3_PHASE => 0.0,
      CLKOUT4_PHASE => 0.0,
      CLKOUT5_PHASE => 0.0,
      CLKOUT6_PHASE => 0.0,
      CLKOUT4_CASCADE => FALSE,      -- Cascade CLKOUT4 counter with CLKOUT6 (FALSE, TRUE)
      COMPENSATION => "ZHOLD",       -- ZHOLD, BUF_IN, EXTERNAL, INTERNAL
      DIVCLK_DIVIDE => 1,            -- Master division value (1-106)
      -- REF_JITTER: Reference input jitter in UI (0.000-0.999).
      REF_JITTER1 => 0.0,
      REF_JITTER2 => 0.0,
      STARTUP_WAIT => FALSE,         -- Delays DONE until MMCM is locked (FALSE, TRUE)
      -- Spread Spectrum: Spread Spectrum Attributes
      SS_EN => "FALSE",              -- Enables spread spectrum (FALSE, TRUE)
      SS_MODE => "CENTER_HIGH",      -- CENTER_HIGH, CENTER_LOW, DOWN_HIGH, DOWN_LOW
      SS_MOD_PERIOD => 10000,        -- Spread spectrum modulation period (ns) (VALUES)
      -- USE_FINE_PS: Fine phase shift enable (TRUE/FALSE)
      CLKFBOUT_USE_FINE_PS => FALSE,
      CLKOUT0_USE_FINE_PS => FALSE,
      CLKOUT1_USE_FINE_PS => FALSE,
      CLKOUT2_USE_FINE_PS => FALSE,
      CLKOUT3_USE_FINE_PS => FALSE,
      CLKOUT4_USE_FINE_PS => FALSE,
      CLKOUT5_USE_FINE_PS => FALSE,
      CLKOUT6_USE_FINE_PS => FALSE 
   )
   port map (
      -- Clock Outputs: 1-bit (each) output: User configurable clock outputs
      CLKOUT0  => clk_switched,
      CLKOUT0B => open,
      CLKOUT1  => open,
      CLKOUT1B => open,
      CLKOUT2  => open,
      CLKOUT2B => open,
      CLKOUT3  => open,
      CLKOUT3B => open,
      CLKOUT4  => open,
      CLKOUT5  => open,
      CLKOUT6  => open,
      -- Dynamic Phase Shift Ports: 1-bit (each) output: Ports used for dynamic phase shifting of the outputs
      PSDONE => open,
      -- Feedback Clocks: 1-bit (each) output: Clock feedback ports
      CLKFBOUT => clk_fb,
      CLKFBOUTB => open,
      -- Status Ports: 1-bit (each) output: MMCM status ports
      CLKFBSTOPPED => open,
      CLKINSTOPPED => open,
      LOCKED       => open,
      -- Clock Inputs: 1-bit (each) input: Clock inputs
      CLKIN1   => clk_100,
      CLKIN2   => '0', 
      -- Control Ports: 1-bit (each) input: MMCM control ports
      CLKINSEL => '1',
      PWRDWN   => '0',             -- 1-bit input: Power-down
      RST      => rst,                   -- 1-bit input: Reset

      -- DRP Ports: 16-bit (each) output: Dynamic reconfiguration ports
      DCLK  => clk_100,                 -- 1-bit input: DRP clock
      DO    => DO,                     -- 16-bit output: DRP data
      DRDY  => DRDY,                 -- 1-bit output: DRP ready
      -- DRP Ports: 7-bit (each) input: Dynamic reconfiguration ports
      DADDR => DADDR,               -- 7-bit input: DRP address
      DEN   => DEN,                   -- 1-bit input: DRP enable
      DI    => DI,                     -- 16-bit input: DRP data
      DWE   => DWE,                   -- 1-bit input: DRP write enable
      
 
      -- Dynamic Phase Shift Ports: 1-bit (each) input: Ports used for dynamic phase shifting of the outputs
      PSCLK    => '0',
      PSEN     => '0',
      PSINCDEC => '0',
      -- Feedback Clocks: 1-bit (each) input: Clock feedback ports
      CLKFBIN => clk_fb
   );

speed_change_fsm: process(clk_100)
    begin
        if rising_edge(clk_100) then
            di <= (others => '0');
            dwe <= '0';
            den <= '0';  
            case state is
                when state_idle_fast =>
                    if speed_select = '1'then
                       state <= state_go_slow_1;
                       --              High 10     Low 10 
                       di  <= "0001" & "001010" & "001010";
                       dwe <= '1';
                       den <= '1';  
                    end if;
                when state_go_slow_1 =>
                    if  drdy = '1' then
                        state <= state_go_slow_2;
                    end if;
                when state_go_slow_2 =>
                    rst <= '1';
                    state <= state_go_slow_3;
                when state_go_slow_3 =>
                    rst <= '0';
                    state <= state_idle_slow;
                    
                when state_idle_slow =>
                    di <= (others => '0');
                    if speed_select = '0' and drdy = '0' then
                       state <= state_go_fast_1;
                       --              High 5      Low 5 
                       di  <= "0001" & "000101" & "000101";
                       dwe <= '1';
                       den <= '1';  
                    end if;
                when state_go_fast_1 =>
                    if  drdy = '1' then
                        state <= state_go_fast_2;
                    end if;
                when state_go_fast_2 =>
                    rst <= '1';
                    state <= state_go_fast_3;
                when state_go_fast_3 =>
                    rst <= '0';
                    state <= state_idle_fast;    
            end case;
        end if;
    end process;
    
dbounce_proc: process(clk_100)
    begin
        if rising_edge(clk_100) then
            if speed_select = btn then
                debounce <= (others => '0');
            elsif debounce(debounce'high) = '1' then
                speed_select <= not speed_select;
            else
                debounce <= debounce + 1;
            end if;
            -- Syncronise the button
            btn      <= btn_meta;
            btn_meta <= btn_raw;
        end if;
    end process;

    

show_speed_proc: process(clk_switched) 
    begin
        if rising_edge(clk_switched) then
            counter <= counter + 1;            
            led(7 downto 0) <= std_logic_vector(counter(counter'high downto counter'high-7));
        end if;
    end process;
       led(15) <= speed_select;
end Behavioral;
Edited by hamster

Share this post


Link to post
Share on other sites
  • 0

Hey, something else I just saw when reading the clocking guide was:

MMCM Counter Cascading The CLKOUT6 divider (counter) can be cascaded with the CLKOUT4 divider. This provides a capability to have an output divider that is larger than 128. CLKOUT6 feeds the input of the CLKOUT4 divider. There is a static phase offset between the output of the cascaded divider and all other output dividers.

And:

CLKOUT4_CASCADE : Cascades the output divider (counter) CLKOUT6 into the input of the CLKOUT4 divider for an output clock divider that is greater than 128, effectively providing a total divide value of 16,384.

So that can divide a 600 MHz VCO down to 36.6 kHz.

Share this post


Link to post
Share on other sites
  • 0

Dear Dan & Hamster,

  Thank you for the reply. Thanks for sharing the application note & code.I understood from the application note we can derive 2pow6 different clocks( HIGH TIME & LOW TIME).

In my case, I need something like frequencies 51Mhz,50Mhz,49Mhz........1Mhz. The user will configure any of the frequencies dynamically. Meaning I will get even & odd frequencies.

I saw the implementation like this http://ecad.tu-sofia.bg/et/2014/ET2014/AJE_2014/104-I_Pandiev1.pdf

All the clock period must have 0.5 duty cycle.

i am unable to get all the clock frequencies with any of the implementation. does we have any generic clock generator using MMCM  or board clock (100 MHz) ?

 

kindly advise.

Note: I only know VHDL coding & i am not familiar with Verilog.

Thanks and Regards

Lakshman.

 

 

 

 

 

 

Share this post


Link to post
Share on other sites
  • 0

@rangaraj,

It's a shame you only know VHDL coding, since the Verilog code I posted above would give you the ability to generate an arbitrary clock--unencumbered by the constraints of the PLL, with frequency resolution in the milli-Hertz range (100MHz/2^32).  Perhaps you want to take another look at it?

Sure, it would have some phase noise, but ... that could be beaten now if necessary by using an OSERDESE2 component.  I've got an example design I'm working on that does just that and should knock the phase noise down to 1.25ns or better.

Dan

Share this post


Link to post
Share on other sites
  • 0
8 hours ago, D@n said:

@rangaraj,

It's a shame you only know VHDL coding, since the Verilog code I posted above would give you the ability to generate an arbitrary clock--unencumbered by the constraints of the PLL, with frequency resolution in the milli-Hertz range (100MHz/2^32).  Perhaps you want to take another look at it?

Sure, it would have some phase noise, but ... that could be beaten now if necessary by using an OSERDESE2 component.  I've got an example design I'm working on that does just that and should knock the phase noise down to 1.25ns or better.

Dan

Hi D@n!

Have you checked if output of a SERDESE2 can be looped back into the fabric? Or would you need to push it back through a pin.

The max jitter that an MMCM block is guaranteed to lock on is 1ns. Not sure if that is +/- 0.5ns RMS or not.

If the SERDES output could be looped back to the MMCM then the the MMCM could potentially wipe the jitter (but you might need to reset the MMCM when you change frequency

Share this post


Link to post
Share on other sites
  • 0

@hamster,

Always keeping me on my toes, no?  Good questions, good questions ...

The Basys-3 board is one of the 7-series Artix parts.  Each I/O port in the 7-series parts has an OSERDESE2 and an ISERDESE2 attached to it, as well as a bypass by which the OSERDESE2 can directly feed the pin input.  The approach suggested above to beat down the phase noise within an FPGA generated clock would necessarily sacrifice an unused pin in order to work.  I'd have to check the pins used on the Basys3, but I have to imagine there are some unused yet clock capable input pins which could be used for this.

As for the MMCM, my thought was to use a PLL instead--both are available side-by-side in each CMT as I recall.  A PLL would be useful to clean up from any ugly phase noise caused by the OSERDESE2 part.  (I was taught that the PLL lowers phase noise, the MMCM increases it ...)

Further, if you look through the ds181 data sheet for the Artix 7 FPGAs, it specifies that the maximum input clock period jitter is <20% of the clock input period or 1 ns Max.  I think what they mean is the maximum of 20% of the clock input period, or 1ns, hence a phase noise of 2ns would be the limit when generating a 100MHz clock.  Hence, if you drive a OSERDESE2 from the 100MHz on board oscillator, using 8x upsampling, you should be able to achieve better than 10ns/8 or about 1.25ns of phase noise per cycle maximum.  It's actually half of that, and better.  If averaged, the phase noise would wash out a lot more, and look more like a third of that (sigma = sqrt(1/12)/delta, etc.)

Anyway, that was my thought.  I'd been digging into this for the purpose of building a programmable clock for video.  The code to build this is so trivial, though, with the critical component listed above, that it's hardly worth posting any more to it--so I'll just say that it's possible.

Dan

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now