Forum Discussion

Altera_Forum's avatar
Altera_Forum
Icon for Honored Contributor rankHonored Contributor
10 years ago

How to properly constrain SPI - like interface if SCLK freq = FPGA main clk freq

Hi all!

My problem looks like wdshen'sproblem in that post: http://www.alteraforum.com/forum/archive/index.php/t-32549.html.

But it has some interesting details.

My ADC mode is here - (http://www.analog.com/static/imported-files/data_sheets/ad7986.pdf figure 26). So it's a problem than FPGA clocks data, which then goes out from ADC to FPGA. And there is no clock source to ADC except FPGA's SCLK. So for me there are in fact 2 interesting I/O ports - SDO (input for FPGA) and SCLK (output from FPGA). But for a timing purposes, my only FPGA clock has a frequency of 100 MHz. And for a high data rate purposes, SCLK is same = 100 MHz. So I'm using gated clock (altera recommended scheme) for creating SCLK. I know delays from SCLK port to ADC and from ADC to SDO input port. But in FPGA I need to constrain delays from gated signal sck to port SCLK, and I can't use fast outpur register because it has the same clock frequency as gated clock data signal. For SDO data I'm using DDIO and it puts in I/O block so I can take the delay into account without a problem.

So, what type of constraining such interface can you suggest?

Thank you very much for advice!

5 Replies

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    I had a similar issue with a similar type of ADC. What you need to do is account for setup and hold times of the various signals in the ADC (SDO,CS). These are defined in the ADC's data sheet usually by Tsu, Th, and Tco max, Tco min. You also need to account for the PCB trace length at high clock rates like yours - on FR4 signal speed is 6 inches / nanosecond.

    Using these values you can write your sdc script.

    1) Define values:

    set_time_format -unit ns -decimal_places 3

    set PCB_dly_max_CS "some# ";# trace delay for CS

    set PCB_dly_min_CS "some# ";# trace delay for CS

    set PCB_dly_max_SCK "some# ";# trace delay for SCK

    set PCB_dly_min_SCK "some# ";# trace delay for SCK

    set PCB_dly_max_SDO "some# ";# trace delay for SDO

    set PCB_dly_min_SDO "some# ";# trace delay for SDO

    set ADC_Tsu_CS "some# ";# setup for falling edge of SCK

    set ADC_Th_CS "some# ";# hold for falling edge of SCK

    set ADC_Tco_max_SDO "some# ";# Tco for falling edge of SCK

    set ADC_Tco_min_SDO "some# ";# min Tco for falling edge of SCK

    2) Define the clock source for your ADC. For this you want to create a virtual clock and define its sources. Something like this:

    create_generated_clock -name SPI_SCK_ext -source [get_pins {"register that is generating clock"|clk}] -divide_by N -multiply_by M [get_pins {"register that is generating clock"|q}]

    note: N and M are how your pll or whatever makes your clock. In your case it would be 1 and 1.

    create_generated_clock -name {SPI_SCK_to_ADC} -source [get_pins {"register that is generating the clock signal"|q}] -master_clock {SPI_SCK_ext} [get_ports {SCK}]

    3) Using your clocks and delay definitions you can calculate input and output delays:

    set_input_delay -clock { SPI_SCK_to_ADC } -clock_fall -min [expr $PCB_dly_min_SDO + $ADC_Tco_min_SDO - $PCB_dly_max_SCK] [get_ports {SDO}]

    set_input_delay -clock { SPI_SCK_to_ADC } -clock_fall -max [expr $PCB_dly_max_SDO + $ADC_Tco_max_SDO - $PCB_dly_min_SCK] [get_ports {SDO}]

    set_output_delay -clock { SPI_SCK_to_ADC } -clock_fall -max [expr $PCB_dly_max_CS + $ADC_Tsu_CS - $PCB_dly_min_SCK] [get_ports {CS}]

    set_output_delay -clock { SPI_SCK_to_ADC } -clock_fall -min [expr $PCB_dly_min_CS - $ADC_Th_CS - $PCB_dly_max_SCK] [get_ports {CS}]

    And that's pretty much it: Define delays, define clock source for SCK line, set input and output delays for CS and SDO lines.

    Good luck.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Thank you very much for your answer, krasner!

    In my current situation, I know PCB delays, Tco of ADC and so on.

    I wrote the code, and from one compilation to another compilation ADC works or not works.

    So it is because of delays on FPGA.

    The problem is, I want to be sure that FPGA delays are constant.

    Right now I solve the problem like this:

    1. I Take into account delays from SCK port and CNV port to ADC and from ADC to SDO port.

    2. I Use fast output register for cnv and fast input register for SDO.

    3. I can't use the fast output register for SCK, because I use gated clock for SCK like that :

    (always @ (negedge CLK)

    enable_sig_ff <= enable_sig;

    assign SCK = enable_sig_ff & CLK; )

    and can't put clock on register's data input.

    4. I'm using I/O buffer D3 delay assignment to finely adjust time of SDO input acquisition.

    5. I'm using Location assignment for a register "enable_sig_ff" and "&" element, Location choosed near SCK port.

    But it's not a true way, I want to solve it using proper constraints.

    So about your advice:

    1. Define values - no problem, I know these values.

    2. As I know, virtual clock is an external clock, but create generated clock command is for a internally created clocks in FPGA.

    Also there is no register that generates clock, it comes directly from PLL. So I can't use "get_pins" command.

    I can use get_keepers and find PLL output and use it as "source" part of command. But I need also to fill the "target" part of command,

    I don't know exactly what instance to use, I think it should be element "&" output.

    The second generated clock is port, I don't understand why I need two constraint and can't use one create generated clock command?

    Could you explain this point again?

    3. I need to use set_output_delay command from generated clock to input port SDO

    and set_output_delay command to output port CS (in my situation CNV) - that is what I need, thank you very much!

    Also am I right, you used "-clock_fall" option because ADC outputs data on falling edge?
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    A PLL also has an internal register structure (not sure if my terminology here is correct...). If you view your design in the RTL Netlist viewer, you would be able the see the hierarchical structure of the pll block. Based on this you can constrain your virtual clock:

    create_generated_clock -name "pll_clock" -source { "top level" | alt_pll | sd1 | pll7 | inclk[0] } -divide_by N -multiply_by M { "top level" | alt_pll | sd1 | pll7 | clk[0] }

    in this statement the pll clock is derived in the pll7 block, which resides in sd1 block, which resides in the alt_pll module, which resides in the top level. You would see this in RTL viewer. inclk[0] is the system clock you feed to the pll, while clk[0] or [1] or [2] or etc, is the derived output.

    in my application the pll clock that I made was 2x the required SPI clock, due to the data recovery algorithm. but your application may be different. In my application I created a clock SPI_SCK_ext that is half the rate of the pll in a register.

    The second statement relates this virtual clock to the SCK port that is physically connected to your ADC. Since your CNV and SDO ports are referenced to the SCK port, I created another virtual clock that can tie all of them together.

    The second statement basically says that the wire between the derived clock and the SCK port is actually a clock path that you will be referencing other ports too.

    Then once this is established, the input and output delays are set with respect to the SCK clock path, and no other clock path. This is important for routing purposes. Basically, you need to make sure that the SCK clock is being derived as close (physically) to the FPGA port as possible, otherwise you will have added latency.

    In my application I actually use SPI_SCK_int and SPI_SCK_ext, which technically are the same clock, but allow the fitter to have once clock physically close to the deserializer logic and one clock close to the SCK port.

    For your last question: yes my ADC outputs data on falling edge of SCK.

    It's hard to say exactly how to write the timing file for your application. In your case the SPI_SCK_ext register is really the output of your pll, so you can simply use the statement above instead of the statement in the previous reply:

    create_generated_clock -name SPI_SCK_ext -source [get_pins {"register that is generating clock"|clk}] -divide_by N -multiply_by M [get_pins {"register that is generating clock"|q}]

    hope this kinda help you with your constraints. It took me several tries to properly get the constraints correct.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Sorry for the late answer and thank you very very much for your answer, krasner!

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Did you get your timings to work properly? If so, can you post your constraints file for others to look at?