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: 
Visitor jjarmagost
Visitor
4,197 Views
Registered: ‎01-03-2013

LVDS source synchronous DDR edge aligned interface with *discontinuous* clock

Jump to solution

I am trying to capture data into a Spartan 6 from this ADC:  LTC2358.  The data is presented on a source synchronous, DDR, edge-aligned, LVDS interface.  To reduce noise the interface should only be active after a conversion is complete.  This implies that the clock from the ADC to the FPGA is discontinuous.  However, unless I'm missing something, all of the source synchronous deserialization documentation assumes a continuous clock.  

 

For example, in https://www.xilinx.com/support/documentation/application_notes/xapp1064.pdf Case 2 on page 2:

 

The data stream is a multiply by two of the incoming clock, commonly called Double Data Rate
(DDR) reception. A DDR data stream is shown in Figure 2. Each transition of the clock
indicates a change in the state of a data line. There are two ways of receiving this kind of data.
The first is to use a PLL and a BUFPLL (see Case 1), where the PLL is being used to multiply
the incoming clock by two and the BUFPLL allows use of the whole edge of a device. The other
method is to use the BUFIO2 primitive ... Two BUFIO2s must be used to multiply the incoming

DDR clock by two to generate an SDR capture clock.

 

Both methods involve multiplying the clock, which surely can't work if the clock is discontinuous.

 

Just for fun I've tried to use a couple of IODELAY2 elements to provide the required clock-data offset:

 

module lvds_adc_interface (
  input               scko_p,
  input               scko_n,
  input               sdo_p,
  input               sdo_n,
  output              iddr_data0,
  output              iddr_data1
);


  wire            scko;
  wire            scko_delayed;

  wire            sdo;
  wire            sdo_delayed;

  
  // clock path

  IBUFDS i_clock_ibufds (
    .I(scko_p),
    .IB(scko_n),
    .O(scko)
  );

  IODELAY2 #(
    .IDELAY_VALUE(5),
    .IDELAY_TYPE("FIXED")
  ) i_clock_IODELAY2 (
    .IDATAIN(scko),
    .T(1'b1),
    .ODATAIN(1'b0),
    .CAL(1'b0),
    .IOCLK0(1'b0),
    .IOCLK1(1'b0),
    .CLK(1'b0),
    .INC(1'b0),
    .CE(1'b0),
    .RST(1'b0),
    .BUSY(),
    .DATAOUT(scko_delayed),
    .DATAOUT2(),
    .TOUT(),
    .DOUT()
  );


  // data path

  IBUFDS i_data_ibufds (
    .I(sdo_p),
    .IB(sdo_n),
    .O(sdo)
  );

  IODELAY2 #(
    .IDELAY_VALUE(0),
    .IDELAY_TYPE("FIXED")
  ) i_data_IODELAY2 (
    .IDATAIN(sdo),
    .T(1'b1),
    .ODATAIN(1'b0),
    .CAL(1'b0),
    .IOCLK0(1'b0),
    .IOCLK1(1'b0),
    .CLK(1'b0),
    .INC(1'b0),
    .CE(1'b0),
    .RST(1'b0),
    .BUSY(),
    .DATAOUT(sdo_delayed),
    .DATAOUT2(),
    .TOUT(),
    .DOUT()
  );


  // iddr

  IDDR2 #(
    .DDR_ALIGNMENT("C0")
  ) i_data_iddr2 (
    .D(sdo_delayed),
    .C0(scko_delayed),
    .C1(~scko_delayed),
    .CE(1'b1),
    .R(1'b0),
    .S(1'b0),
    .Q0(iddr_data0),
    .Q1(iddr_data1)
  );


endmodule

But I get this error:

 

ERROR:Route:471 - 
   This design is unrouteable. Router will not continue. To evaluate the problem please use fpga_editor. The nets listed below can not be
   routed:
Unrouteable Net:scko_delayed

 

I'm sure that I'm missing something simple, but I can't find it.  What is the correct way to capture data from this type of interface?

 

 

 

 

 

 

 

 

0 Kudos
1 Solution

Accepted Solutions
Instructor
Instructor
7,180 Views
Registered: ‎08-14-2007

Re: LVDS source synchronous DDR edge aligned interface with *discontinuous* clock

Jump to solution

XAPP1064 only needs clock doubling because it's capturing an odd number of bits in the ISERDES.  Other than that, the real need for a continuous clock is the variable phase control in the PLL.  If you instead use variable IDELAY, which I believe they also talk about for per-data-line alignment, then there may be some applicable code in that XAPP.  I'm just not sure if the tap delay calibration is possible without a continuous clock.  This leads to an interesting question.  Do you have access to the clock that the ADC uses to generate its output clock?  That clock would presumably be free-running and isochronous to the data clock.  If you have access to it, you could possibly use it to re-create the data clock frequency (albeit at some unknown phase) of the data clock, and use that as the calibration clock for the IDELAY elements.

 

I also noticed that the section titled "Delaying Input Data and Clocks" talks about delaying the data rather than the clock to account for the 90 degree phase shift required.  In your case, this may mean you miss the last bit of input if the clock doesn't have at least one transition after the last valid data bit.

 

Have you tried asking your question on the TI forum to see if someone has tried to solve this issue using Spartan-6?

 

Finally, I have no idea how the memory controller in S6 aligns the sampling clock.  However my guess would be the same as yours, i.e. it's built into the hard memory controller block.

-- Gabor
8 Replies
Instructor
Instructor
4,183 Views
Registered: ‎08-14-2007

Re: LVDS source synchronous DDR edge aligned interface with *discontinuous* clock

Jump to solution

Take a look at the technology schematic to see if there is a BUFG, BUFR or BUFIO inferred between the IDELAY and your clock loads.  If not, you might need to instantiate one.  Also make sure that your input clocks are going to a clock capable pair.

 

On another note, using a global clock resource like BUFG inserts a significant delay in the signal.  Depending on your clock speed, it may not be feasible to find IDELAY values that give you a stable sampling window.  It may require variable input delay and training to get a usable sampling point.

-- Gabor
0 Kudos
Visitor jjarmagost
Visitor
4,164 Views
Registered: ‎01-03-2013

Re: LVDS source synchronous DDR edge aligned interface with *discontinuous* clock

Jump to solution

Thank you for the reply, Gabor.  

 

Just before reading your reply I found this other thread which I had not seen before:  https://forums.xilinx.com/t5/Spartan-Family-FPGAs/How-to-invert-sampled-clock-signal/td-p/115518

 

In it, Bob Elkind ran through several scenarios to see exactly what could drive the IDDR2 cell:

 

12-31-2010 01:06 PM - edited ‎01-01-2011 11:35 AM

Well, this was more fun than I thought...
I tried a quick experiment with ISE12.4 running on WinVista64, and here's what I've found...
 
These work fine:
1.  Drive IDDR2 .C0, .C1 from BUFG, using "~ddr_clk" at .C1 input
2.  Drive IDDR2 .C0, .C1 from IBUFG, using "~ddr_clk" at .C1 input
3.  Drive IDDR2 .C0 from BUFIO2/IOCLK, drive IDDR2 .C1 from 2nd BUFIO2/IOCLK (with I-INVERT="TRUE")
These do not work
4.  Drive IDDR2 .C0 from BUFIO2/IOCLK, Drive IDDR2 .C1 from 2nd BUFIO2/IOCLK (with I-INVERT="FALSE")
5.  Drive IDDR2 .C0, .C1 from single BUFIO2/IOCLK

 

Unfortunately, neither options 1 nor 3 work for me.  (Note that I'm on ISE 14.6, as opposed to 12.4 in the referenced thread.)

 

 


Option 1 (BUFG) :

 

`timescale 1 ps / 1 ps

module lvds_adc_interface (
  input               scko_p,
  input               scko_n,
  input               sdo_p,
  input               sdo_n,
  output              iddr_data0,
  output              iddr_data1
);


  wire            scko;
  wire            scko_delayed;
  wire            scko_delayed_buf;
  wire            scko_delayed_buf_p;
  wire            scko_delayed_buf_n;

  wire            sdo;
  wire            sdo_delayed;

  
  // clock path -----------------------------------

  IBUFDS i_clock_ibufds (
    .I(scko_p),
    .IB(scko_n),
    .O(scko)
  );

  IODELAY2 #(
    .IDELAY_VALUE(5),
    .IDELAY_TYPE("FIXED")
  ) i_clock_IODELAY2 (
    .IDATAIN(scko),
    .T(1'b1),
    .ODATAIN(1'b0),
    .CAL(1'b0),
    .IOCLK0(1'b0),
    .IOCLK1(1'b0),
    .CLK(1'b0),
    .INC(1'b0),
    .CE(1'b0),
    .RST(1'b0),
    .BUSY(),
    .DATAOUT(scko_delayed),
    .DATAOUT2(),
    .TOUT(),
    .DOUT()
  );

//  BUFIO2 #(
//    .DIVIDE(1),
//    .DIVIDE_BYPASS("TRUE"),
//    .I_INVERT("FALSE"),
//    .USE_DOUBLER("FALSE")
//  ) i_BUFIO2_p (
//    .I(scko_delayed),
//    .IOCLK(scko_delayed_buf_p),
//    .DIVCLK(/* open */),
//    .SERDESSTROBE(/* open */)
//  );

//  BUFIO2 #(
//    .DIVIDE(1),
//    .DIVIDE_BYPASS("TRUE"),
//    .I_INVERT("TRUE"),
//    .USE_DOUBLER("FALSE")
//  ) i_BUFIO2_n (
//    .I(scko_delayed),
//    .IOCLK(scko_delayed_buf_n),
//    .DIVCLK(/* open */),
//    .SERDESSTROBE(/* open */)
//  );

  BUFG i_BUFG (
    .I(scko_delayed),
    .O(scko_delayed_buf)
  );


  // data path -----------------------------------

  IBUFDS i_data_ibufds (
    .I(sdo_p),
    .IB(sdo_n),
    .O(sdo)
  );

  IODELAY2 #(
    .IDELAY_VALUE(0),
    .IDELAY_TYPE("FIXED")
  ) i_data_IODELAY2 (
    .IDATAIN(sdo),
    .T(1'b1),
    .ODATAIN(1'b0),
    .CAL(1'b0),
    .IOCLK0(1'b0),
    .IOCLK1(1'b0),
    .CLK(1'b0),
    .INC(1'b0),
    .CE(1'b0),
    .RST(1'b0),
    .BUSY(),
    .DATAOUT(sdo_delayed),
    .DATAOUT2(),
    .TOUT(),
    .DOUT()
  );


  // iddr ------------------------------------

  IDDR2 #(
    .DDR_ALIGNMENT("C0")
  ) i_data_iddr2 (
    .D(sdo_delayed),
    .C0(scko_delayed_buf),
    .C1(~scko_delayed_buf),
    .CE(1'b1),
    .R(1'b0),
    .S(1'b0),
    .Q0(iddr_data0),
    .Q1(iddr_data1)
  );


endmodule

bufg.PNG

 

ERROR:Route:471 - 
   This design is unrouteable. Router will not continue. To evaluate the problem please use fpga_editor. The nets listed below can not be
   routed:
Unrouteable Net:scko_delayed

 

 


Option 3  (multiple BUFIO2):

 

`timescale 1 ps / 1 ps

module lvds_adc_interface (
  input               scko_p,
  input               scko_n,
  input               sdo_p,
  input               sdo_n,
  output              iddr_data0,
  output              iddr_data1
);


  wire            scko;
  wire            scko_delayed;
  wire            scko_delayed_buf;
  wire            scko_delayed_buf_p;
  wire            scko_delayed_buf_n;

  wire            sdo;
  wire            sdo_delayed;

  
  // clock path -----------------------------------

  IBUFDS i_clock_ibufds (
    .I(scko_p),
    .IB(scko_n),
    .O(scko)
  );

  IODELAY2 #(
    .IDELAY_VALUE(5),
    .IDELAY_TYPE("FIXED")
  ) i_clock_IODELAY2 (
    .IDATAIN(scko),
    .T(1'b1),
    .ODATAIN(1'b0),
    .CAL(1'b0),
    .IOCLK0(1'b0),
    .IOCLK1(1'b0),
    .CLK(1'b0),
    .INC(1'b0),
    .CE(1'b0),
    .RST(1'b0),
    .BUSY(),
    .DATAOUT(scko_delayed),
    .DATAOUT2(),
    .TOUT(),
    .DOUT()
  );

  BUFIO2 #(
    .DIVIDE(1),
    .DIVIDE_BYPASS("TRUE"),
    .I_INVERT("FALSE"),
    .USE_DOUBLER("FALSE")
  ) i_BUFIO2_p (
    .I(scko_delayed),
    .IOCLK(scko_delayed_buf_p),
    .DIVCLK(/* open */),
    .SERDESSTROBE(/* open */)
  );

  BUFIO2 #(
    .DIVIDE(1),
    .DIVIDE_BYPASS("TRUE"),
    .I_INVERT("TRUE"),
    .USE_DOUBLER("FALSE")
  ) i_BUFIO2_n (
    .I(scko_delayed),
    .IOCLK(scko_delayed_buf_n),
    .DIVCLK(/* open */),
    .SERDESSTROBE(/* open */)
  );

//  BUFG i_BUFG (
//    .I(scko_delayed),
//    .O(scko_delayed_buf)
//  );


  // data path -----------------------------------

  IBUFDS i_data_ibufds (
    .I(sdo_p),
    .IB(sdo_n),
    .O(sdo)
  );

  IODELAY2 #(
    .IDELAY_VALUE(0),
    .IDELAY_TYPE("FIXED")
  ) i_data_IODELAY2 (
    .IDATAIN(sdo),
    .T(1'b1),
    .ODATAIN(1'b0),
    .CAL(1'b0),
    .IOCLK0(1'b0),
    .IOCLK1(1'b0),
    .CLK(1'b0),
    .INC(1'b0),
    .CE(1'b0),
    .RST(1'b0),
    .BUSY(),
    .DATAOUT(sdo_delayed),
    .DATAOUT2(),
    .TOUT(),
    .DOUT()
  );


  // iddr --------------------------------

  IDDR2 #(
    .DDR_ALIGNMENT("C0")
  ) i_data_iddr2 (
    .D(sdo_delayed),
    .C0(scko_delayed_buf_p),
    .C1(scko_delayed_buf_n),
    .CE(1'b1),
    .R(1'b0),
    .S(1'b0),
    .Q0(iddr_data0),
    .Q1(iddr_data1)
  );


endmodule

BUFIO2.PNG

ERROR:Place:1073 - Placer was unable to create RPM[BUFIO_RPMs] for the component
   i_BUFIO2_p of type BUFIO for the following reason.
   The reason for this issue:
   The structured logic has to be merged with another RPM which causes a
   placement violation for component i_BUFIO2_n. The following components are
   part of this structure:
      BUFIO   i_BUFIO2_p
      IODELAY   i_clock_IODELAY2

 

 

 Any suggestions?  Also, if I can't get the timing to work with the method I'm trying, how do I capture this data?  It occurs to me that this is very similar to the DDR controller capturing a read burst.  Can I copy that or is it something special buried in the hard IP block?

 

Thanks.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

0 Kudos
Visitor jjarmagost
Visitor
4,151 Views
Registered: ‎01-03-2013

Re: LVDS source synchronous DDR edge aligned interface with *discontinuous* clock

Jump to solution

... and at the risk of posting too much information I'll add this diagram from page 10 of a TI app note that I found, which shows that at least someone at some time thought that what I'm attempting should work.

 

http://www.ti.com/lit/an/sbaa205/sbaa205.pdf

 

Capture.PNG

0 Kudos
Instructor
Instructor
4,092 Views
Registered: ‎08-14-2007

Re: LVDS source synchronous DDR edge aligned interface with *discontinuous* clock

Jump to solution

Looking at the title of your attached image, "Using Variable Delays in an FPGA," it seems they also knew you'd need to train this interface (at least at high speeds) using the variable input delay.  Your code was using fixed delay, which probably doesn't give you enough window to sample data reliably.

-- Gabor
0 Kudos
Instructor
Instructor
4,086 Views
Registered: ‎08-14-2007

Re: LVDS source synchronous DDR edge aligned interface with *discontinuous* clock

Jump to solution

See attached code using IBUFDS_DIFF_OUT to solve the dual BUFIO2 issue.

-- Gabor
0 Kudos
Visitor jjarmagost
Visitor
4,075 Views
Registered: ‎01-03-2013

Re: LVDS source synchronous DDR edge aligned interface with *discontinuous* clock

Jump to solution

Thank you for your reply.

 

I found additional information from TI that clears up the picture a bit.  They are using Virtex parts with delay elements that are much more stable over PVT, from what I understand.  Here's a snippet from the message board talking about a Virtex 6:

 

We bring the DDR clock and the data lines into an LVDS input buffer with the 100 ohm 
termination resistor in the FPGA enabled. Then each LVDS line, both clock and data, pass
through an IDELAY element. This lets us individually skew the clock and/or the data to meet
setup time into whatever is used to latch the data. The clock has to pass through a clock buffer, so the data had an IDELAY setting to make the
data path match the clock path with regards to timing. Then we used an IDDR cell to latch
the data on the rising edge and falling edge, while reregistering the falling edge data over
to the rising edge. At this point, the sample data is ready to be used in the FPGA for
whatever you wish to do with it. And for meeting the timing of the setup and hold (finding the right settings for the IDELAY)
we take our best guess at what the IDELAY deafult tap settings should be and set that as
default powerup or reset IDELAY values. Then we check the static timing analysis report
after place and route, looking for timing violations. If there is a timing violation on the
DDR input data, then we look at the magnitude of the violation and that tells us how many
IDELAY tap settings we were off by on our initial guess. Then we rerun the timing tools to
verify.

 

Meanwhile back on Spartan 6 where I'm squatting in my cave and banging rocks together:

 

By using your helpful feedback I was able to implement the IDELAYs + IDDR method, but it didn't meet timing (as you correctly predicted).

 

For fun, though, I tried to implement the IDDR part in the fabric and this actually did meet timing after I found the magic IDELAY values.  I guess the additional flexibility of playing with routing delays enabled the tool to pull it off?  However, the margin was only 7 ps under artificially perfect conditions so I'm calling that a no-go.

 

So I guess I'm down to two questions that I'd be grateful to get answers on:

 

1.  Since my DDR clock is not continuous, am I correct that none of the methods in xapp1064 will work because they rely on clock doubling?

 

2.  Since my situation is very similar to capturing a DDR read burst, how does the Spartan 6 DDR memory controller do it?  I'm assuming it has different capture hardware baked into the hard IP.  Is this correct?

 

Thank you. 

0 Kudos
Instructor
Instructor
7,181 Views
Registered: ‎08-14-2007

Re: LVDS source synchronous DDR edge aligned interface with *discontinuous* clock

Jump to solution

XAPP1064 only needs clock doubling because it's capturing an odd number of bits in the ISERDES.  Other than that, the real need for a continuous clock is the variable phase control in the PLL.  If you instead use variable IDELAY, which I believe they also talk about for per-data-line alignment, then there may be some applicable code in that XAPP.  I'm just not sure if the tap delay calibration is possible without a continuous clock.  This leads to an interesting question.  Do you have access to the clock that the ADC uses to generate its output clock?  That clock would presumably be free-running and isochronous to the data clock.  If you have access to it, you could possibly use it to re-create the data clock frequency (albeit at some unknown phase) of the data clock, and use that as the calibration clock for the IDELAY elements.

 

I also noticed that the section titled "Delaying Input Data and Clocks" talks about delaying the data rather than the clock to account for the 90 degree phase shift required.  In your case, this may mean you miss the last bit of input if the clock doesn't have at least one transition after the last valid data bit.

 

Have you tried asking your question on the TI forum to see if someone has tried to solve this issue using Spartan-6?

 

Finally, I have no idea how the memory controller in S6 aligns the sampling clock.  However my guess would be the same as yours, i.e. it's built into the hard memory controller block.

-- Gabor
Visitor jjarmagost
Visitor
4,069 Views
Registered: ‎01-03-2013

Re: LVDS source synchronous DDR edge aligned interface with *discontinuous* clock

Jump to solution

 

I actually had the same idea about using the source clock as the calibration clock.  I'm currently trying to wade through the material in XAPP1064 to see if it's possible.  To say that this material is confusing is an understatement. 

 

Also, good idea about posting the question on other boards.  It certainly can't hurt.

 

Thank you for the great responses and help!

0 Kudos