Jump to content
  • 0

Generate and Synchronizing Clock


jcloiacon

Question

Evening, all. The code attempts to create a clock and synchronize it to sysclk. However, it fails timing analysis.

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Engineer: hamster, JUIXXXE
//////////////////////////////////////////////////////////////////////////////////
module clk_div(
    input clk,
    output wire mclk,
    output wire bclk,
    output wire lrclk
    );
wire clkfb;
reg [7:0] mclk_count = 0;
MMCME2_BASE #(
      .BANDWIDTH("OPTIMIZED"),   // Jitter programming (OPTIMIZED, HIGH, LOW)
      .CLKFBOUT_MULT_F(7.0),     // Multiply value for all CLKOUT (2.000-64.000).
      .CLKFBOUT_PHASE(0.0),      // Phase offset in degrees of CLKFB (-360.000-360.000).
      .CLKIN1_PERIOD(10.0),       // Input clock period in ns to ps resolution (i.e. 33.333 is 30 MHz).
      // CLKOUT0_DIVIDE - CLKOUT6_DIVIDE: Divide amount for each CLKOUT (1-128)
      .CLKOUT0_DIVIDE_F(57.0),    // Divide amount for CLKOUT0 (1.000-128.000).
      .CLKOUT1_DIVIDE(14),
      .CLKOUT2_DIVIDE(14),
      .CLKOUT3_DIVIDE(14),
      .CLKOUT4_DIVIDE(14),
      .CLKOUT5_DIVIDE(14),
      .CLKOUT6_DIVIDE(14),
      // CLKOUT0_DUTY_CYCLE - CLKOUT6_DUTY_CYCLE: Duty cycle for each CLKOUT (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 each CLKOUT (-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)
      .DIVCLK_DIVIDE(1),         // Master division value (1-106)
      .REF_JITTER1(0.0),         // Reference input jitter in UI (0.000-0.999).
      .STARTUP_WAIT("FALSE")     // Delays DONE until MMCM is locked (FALSE, TRUE)
   )
MMCME2_BASE_inst (
      // Clock Outputs: 1-bit (each) output: User configurable clock outputs
      .CLKOUT0(mclk),  // 1-bit output: CLKOUT0
      .CLKOUT0B(CLKOUT0B),   // 1-bit output: Inverted CLKOUT0
      .CLKOUT1(CLKOUT1),     // 1-bit output: CLKOUT1
      .CLKOUT1B(CLKOUT1B),   // 1-bit output: Inverted CLKOUT1
      .CLKOUT2(CLKOUT2),     // 1-bit output: CLKOUT2
      .CLKOUT2B(CLKOUT2B),   // 1-bit output: Inverted CLKOUT2
      .CLKOUT3(CLKOUT3),     // 1-bit output: CLKOUT3
      .CLKOUT3B(CLKOUT3B),   // 1-bit output: Inverted CLKOUT3
      .CLKOUT4(CLKOUT4),     // 1-bit output: CLKOUT4
      .CLKOUT5(CLKOUT5),     // 1-bit output: CLKOUT5
      .CLKOUT6(CLKOUT6),     // 1-bit output: CLKOUT6
      // Feedback Clocks: 1-bit (each) output: Clock feedback ports
      .CLKFBOUT(clkfb),   // 1-bit output: Feedback clock
      .CLKFBOUTB(CLKFBOUTB), // 1-bit output: Inverted CLKFBOUT
      // Status Ports: 1-bit (each) output: MMCM status ports
      .LOCKED(LOCKED),       // 1-bit output: LOCK
      // Clock Inputs: 1-bit (each) input: Clock input
      .CLKIN1(clk),       // 1-bit input: Clock
      // Control Ports: 1-bit (each) input: MMCM control ports
      .PWRDWN(1'b0),       // 1-bit input: Power-down
      .RST(1'b0),             // 1-bit input: Reset
      // Feedback Clocks: 1-bit (each) input: Clock feedback ports
      .CLKFBIN(clkfb)      // 1-bit input: Feedback clock
   );
                    
   assign bclk  = mclk_count[2];  // mclk / 8
   assign lrclk = mclk_count[7]; // mclk / 256
             
reg mclk_last;
always@(posedge(clk)) begin
       if(mclk & !mclk_last)
        mclk_count <= mclk_count + 1;
       mclk_last <= mclk;
end
endmodule

From CLKOUT0 to mclk_count fails timing analysis. How can the code be restructured to avoid this?

Link to comment
Share on other sites

2 answers to this question

Recommended Posts

Hi,

This might be a long answer......

The main clock 'clk' ticks at 100MHz - a period of 10ns, with 5.000 ns high, 5.000 ns low

The MMCM's VCO ticks at 7x this = 700MHz, a period of 1.4ns, with 0.715ns high and 0.715ns low.

The derived clock 'mclk' runs at 700MHz / 57 = 12.281 MHz, a period of 81.43 ns, with 40.715ns  high and 40.715ns low

The code as you have it needs timing of 0.715ns  (the time difference between the 40.715ns changes in mclk and four ticks of the 10ns main clock. To do this requires logic that runs at at least 1.4GHz to work properly, so it fails timing.

So how to make it work...

You shouldn't be using "mclk" as though it it is a logical signal - it's a clock. What you want to do is toggle a single bit in the mclk domain, and then sample that into the clk domain:

// flip mclk_toggle every cycle
always@(posedge(mclk)) begin
       mclk_toggle <= !mclk_toggle;
end

// sample mclk_toggle and add a two-stage synchronizer, followed with an edge detector..
always@(posedge(clk)) begin

       if(toggle_safe_last & !toggle_safe_last)
          mclk_count <= mclk_count + 1;

       toggle_safe_last       <= toggle_safe
       toggle_safe              <= toggle_almost_safe;
       toggle_almost_safe <= toggle_unsafe;

       toggle_unsafe         <= mclk_toggle;
end

So now you face three issues:

Issue 1)  the incrementing of mclk_count lags a few cycles (5 or 6? - calculating this makes my head hurt!) cycles from when the edge of mclk actually occurs. This might not be a problem for you. However, if you add a few more cycles of delay you can delay it 9 cycles (or 90 to 100ns) - into the next tick of mclk, but as mclk_toggle is ticking like clockwork this looks as though mclk_count increments sometime between 90ns - 81.43ns = 8.5ns and 100ns - 81.43ns = 18.5ns or after mclk changes. This might be of help.

Issue 2) You still have a timing failure between  'toggle_unsafe' and 'mclk_toggle', as it is still required to meet the 1.43 ns timing caused by running Vco at 700MHz. Because this is by design a safe clock domain crossing you might need to add a "timing ignore" constraint. See page 93 of http://www.xilinx.com/support/documentation/sw_manuals/xilinx2014_1/ug903-vivado-using-constraints.pdf for how to do this.

Issue 3) You need to set "async_reg" attribute for toggle_unsafe and toggle_almost_safe to ensure that the tools implement it properly - see page 37 of http://www.xilinx.com/support/documentation/sw_manuals/xilinx2013_2/ug901-vivado-synthesis.pdf

An easier solution

One simpler way might be to run 'clk' at a frequency that is more compatible with that of mclk. Consider if you generate a new clock of 700MHz/9 = 77.77MHz, then  one clock would be 700/9, and the other is 700/57, The timing window is the VCO frequency divided by greatest common factor for the two numerators - GCD(9, 57) = 3. This gives a timing constraint of about 1000/700*3 = 4.28ns, and the timing requirement would be 6 times longer than what it is now. That should easily make timing without any special effort.

The correct solution?

What is going on here feels wrong. The better solution might be to put a small dual-clock FIFO between the two domains, and have the slower domain read or write data every cycle (depending on if the slower domain is the producer or consumer) and have the faster domain only read or write to/from the FIFO when it needs to .(e.g when 'full' is not asserted).

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...