jcloiacon Posted February 7, 2016 Share Posted February 7, 2016 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 More sharing options...
hamster Posted February 7, 2016 Share Posted February 7, 2016 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 More sharing options...
jcloiacon Posted February 7, 2016 Author Share Posted February 7, 2016 Extremely helpful as usual, hamster! As you know, I'm working on a synth project. I'm gonna try the FIFO option with the producer (oscillator summer) operating at the 100MHz sysclock and the consumer feeding this to the DAC at 12.228. Link to comment Share on other sites More sharing options...
Question
jcloiacon
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
Archived
This topic is now archived and is closed to further replies.