UPGRADE YOUR BROWSER

We have detected your current browser version is not the latest one. Xilinx.com uses the latest web technologies to bring you the best online experience possible. Please upgrade to a Xilinx.com supported browser:Chrome, Firefox, Internet Explorer 11, Safari. Thank you!

cancel
Showing results for 
Search instead for 
Did you mean: 
Highlighted
Adventurer
Adventurer
13,127 Views
Registered: ‎02-08-2013

XDC constraints - Source Synchronous ADC DDR

Hi guys,

 

I'm going round the bend with this, mainly as all examples I find are all UCF and not XDC which is what i'm using. 

 

The setup:

 

ADC supplies 250MHz clock and differential DDR data to the FPGA (Kintex-7). Clock to data skew according to ADC datasheet is -200ps / +200ps.

 

On chip the data routes through a BUFDS to an IDDR primative (created by the SelectIO Wizard) - routing delay here is .796ns.

 

The clock routes through an IBUFDS, then MMCM_ADV - frequency the same, but gives me the flexability to change the phase of the derived clock to meet timing (without the need for a 200MHz clock say, if using the delay taps in the SelectIO DDR wizard). From there the derived 250MHz clock routes through a BUFG then into the IDDR primative.

 

The basic XDC constraints I figured I needed are:

 

    • create_clock -period 4.000 -name adc250_diff [get_ports adcClk_p]
    • set_input_delay -clock adc250_diff -max 0.2 [get_ports adcData_p[*]]
    • set_input_delay -clock adc250_diff -max 0.2 [get_ports adcData_p[*]] -clock_fall -add_delay
    • set_input_delay -clock adc250_diff -min -0.2 [get_ports adcData_p[*]]
    • set_input_delay -clock adc250_diff -min -0.2 [get_ports adcData_p[*]] -clock_fall -add_delay


That's how UG903 'Using Constraints' suggests doing it.

 

First Run, no phase offset:

 

Setup timing fails for all DDR data bits coming into the chip. I analyse the paths, which report slack computed using:

 

    • Data Path:  coming into the chip, launched by the rising edge of adc250_diff
    • Destination Clock Path: Rising edge of the MMCM generated 250MHz clock

Slack is reported as -0.709. This to me suggests that the nearest rising edge clock to the arrival of data at the FF is .709ns before valid setup requirment.

 

Second Run, 90degree (1ns) phase offset:

 

I decide to reconfigure the MMCM to delay the generated clock by 1 ns. My theory is that, all routing delays being the same, I will shift the above rising edge to the right, giving me a slack of .291ns.

 

I run synthesis and timing. Timing still fails, negative slack! I notice that this time the failing path timing calculations are based on:

 

    • Data Path: coming into the chip, launched by the rising edge of adc250_diff
    • Destination Clock Path: FALLING edge of the MMCM generated 250MHz clock!!

It turns out no matter how much I offset the generated clock, I always get failing paths, looking at some other edge than the one I shift into the valid time zone for capture!

 

One thing I noticed was that using the 'Edit Timing Constraints' (Using Vivado 2012.4 by the way) the 4 input_delay constraints all show with 'rising edge'  as the edge type. When I select 'falling edge' for a max and min delay (as my manually set constraints are), it thinks about it, runs analysis and pops them all back to 'rising edge'!

 

Questions Questions!

 

    • Have I constrained the input data with respect to source synchronous clock properly using XDC?
    • Am I looking at a bug with the timing editor not wanting to accept falling edge input_delays?
    • Should I add false paths for rising->falling edge clocked data input and falling->rising edge clocked data input?

 

 

Many thanks!

0 Kudos
2 Replies
Historian
Historian
13,122 Views
Registered: ‎01-23-2009

Re: XDC constraints - Source Synchronous ADC DDR

All of your reasoning looks sound. The problem is how Vivado has to deal with phase shifts in the MMCM.

 

Whenever there is a timing path, the setup requirements on the path are derived from the relationship between the clock driving the source and the clock driving the destination. Basically, for any path, the tool looks for the smallest possible relationship between the clocks and uses this as the requirement for the path. So lets say you have a 100MHz clock driving the source and a 125MHz clock driving the destination. The tool will look at all possible edge combinations between the two clocks and will find that there is a 2ns separation between the rising edge of the 100MHz clock at time 30ns and the rising edge of the 125MHz clock at time 32ns - this is the tightest relationship between these two clocks, and hence becomes the requirement for the path.

 

So, what does this have to do with the MMCM (you may ask)?

 

First, lets look at your DDR interface with the PHASE shift of 0. Whenever the tool sees a clock reaching the input of an MMCM, it automatically creates a number of generated clocks - you can see them with the "report_clocks" command. If your original clock (adc_250_diff) has the edges {0.0 2.0} (which is standard for the rising and falling edge of a 250MHz clock), then the new clock (probably called clkout0) will also have edges at {0.0 and 2.0}. Your set_input_delay is referenced to adc_250_diff (which it should be), and the capture clock is clkout0 (also at {0.0 2.0}) so the requirement for your DDR input is

  - the data generated from the rising edge of adc_250_diff at time 0.0 is captured by the falling edge of clkout0 at time 2.0

  - the data generated from the falling edge of adc_250_diff at time 2.0 is captured by the rising edge of clkout0 at time 4.0

 

(both of these are 2ns requirements)

 

There are actually 2 additional (and somewhat redundent checks) done - this is due to the -add_delay in your constraint (which is also correct)

  - the data generated from the rising edge of adc_250_diff at time 0.0 is captured by the rising edge of clkout0 at time 4.0

  - the data generated from the falling edge of adc_250_diff at time 2.0 is captured by the falling edge of clkout0 at time 6.0

 

(both of these are 4ns requirements hence are essentially irrelevent). These paths are harmless, but we need to understand that they are there.

 

Now, how does the phase shift work? When the MMCM is changed to have a 90 degree phase shift, clkout0 now has edges at {1.0 3.0}. So what does this do to our edges?

 

  - the data generated from the rising edge of adc_250_diff at time 0.0 is captured by the rising edge of clkout0 at time 1.0

  - the data generated from the falling edge of adc_250_diff at time 2.0 is captured by the falling edge of clkout0 at time 3.0

 

Its wrong! This is NOT the correct edge relationship for this system. You want the data launched from the rising edge of adc_250_diff at time 0 to be captured by the falling edge of clkout0 at time 3.

 

So, how the heck do we do this? Its REALLY complicated (and I don't have the complete solution).

 

Lets start with the rising edge at 0 to rising edge at 1ns. This path needs to be modified. I am pretty sure the correct way to modify it is with

 

set shifted_clock [get_clocks -of_objects [get_pins <the_CLK_OUT_pin_of_the_mmcm_or_clocking_core>]

set_mutlicycle_path -from [get_ports adcData_p[*]] -to $shifted_clock 2;

 

This will make the setup check go from adc_250_diff at 0 to $shifted_clock at 5 - this is a redundant edge, but is VERY IMPORTANT. This setup check will result in a hold check between the rising edge of adc_250_diff at time 0 to the rising edge of $shifted_clock at time 1 - this is the correct hold check to do for this interface.

 

Unfortunately, the same MCP will also change the path from adc250_diff rise at 0 to clkout0 fall at 3 (which is the correct edge) to adc250_diff rise at 0 to clkout0 fall at 7... This is wrong. So we somehow have to limit the MCP to only fix one of the two sets of edge relationships. So instead of the MCP above, I THINK (but haven't tested any of this) you would need to do

 

set_multicycle_path -rise_from [get_clocks adc250_diff] -through [get_ports adcData_p[*]] -rise_to $shifted_clock 2;

 

You will need to do the same for the falling to falling

 

set_multicycle_path -fall_from [get_clocks adc250_diff] -through [get_ports adcData_p[*]] -fall_to $shifted_clock 2;

 

Yikes!

 

This is my best guess. Note: This is one place where you do NOT want to do another set_multicycle_path with the "-hold 1" - as shown above, the hold check should be correct...

 

If this isn't exactly right, then it is along the right path... Good luck.

 

Avrum

Adventurer
Adventurer
12,856 Views
Registered: ‎11-04-2010

Re: XDC constraints - Source Synchronous ADC DDR

I had a similar problem.  DDR input, edge-aligned clock and data.  I wanted to use the edge preceding the data eye to capture it - meaning I wanted the FPGA to delay the sampling clock into the data's sampling eye.

 

Here are the constraints I used:

 

set ins [get_ports {data}]
create_clock -name virt -period 8
set vclk [get_clocks virt]
create_clock -name clk_in -waveform {0.5 4.5} -period 8.000 [get_ports clk]
set_input_delay -clock $vclk -min 0.0 $ins
set_input_delay -clock $vclk -max 1.0 $ins -add_delay
set_input_delay -clock $vclk -min 0.0 -clock_fall $ins -add_delay
set_input_delay -clock $vclk -max 1.0 -clock_fall $ins -add_delay

So, I specified that the data was invalid from 0 to 1 ns, and that the rising edge of my clock occurs at 0.5 ns.  This is correct for edge-aligned source-synchronous clock/data with +/- 0.5 ns of skew.

 

The key to the constraint is that the setup and hold relationships from virt to clk_in are correct: Vivado knows that data launched by an edge of virt is to be sampled by the edge of clk_in that comes 0.5 ns later.

 

I used an IDELAY to delay the clock (/wince) until this constraint passed.