Forum Discussion
ADC Timing Constraints
I feel like I see this question over and over, but can't quite figure out the answer.
I've got an 125 MHz clock coming in, from which I generate a 62.5 MHz clock using a TFF, which I then use to clock an ADC (ADS831). I sketched out a quick schematic showing what I'm talking about; the attached .gif. The Tcko on the ADC831 is 3.9-12 ns. I've got the following constraints in my SDC file:
create_generated_clock -name adc_clk -source -divide_by 2
# ADC Input
set_input_delay -clock adc_clk -max 12.0 ]
set_input_delay -clock adc_clk -min 3.9 ]
set_multicycle_path -setup -end -from -to 2
set_multicycle_path -hold -end -from -to 1
These constraints seem to be almost right, except I'm getting the always popular "Warning: No paths exist .... Assuming zero source clock latency." problem. I've got Rysc's User Guide and Source-Synchronous Timing guides up, as well as the Timequest Cookbook and the API, and I've got to say I'm still stumped. Can anyone see what I'm missing here?11 Replies
- Altera_Forum
Honored Contributor
The principles are same; Timimg tool needs to know relation of ADC data relative to its clock (the clock that clocks its registers).
if tCO of device is 3.9 ~ 12 ns then it tells the relation of data and ADC clock at device pins(not at fpga). if we assume there is 1 ns board delay for either data and ADC clock then ADC clock arrives 1 ns later at device, data is generated 3.9 ~ 12 ns from edge i.e. 4.9 ~ 13 ns with respect to fpga. Then data arrives at fpga 1 ns later i.e. relation now is 5.9 ~ 14 ns relative to ADC clock at fpga pins. However, ADC clock may not be the one that clocks data registers. The tool knows which clock to check and best practice is to fed correct set input delys then rotate PLL until you pass timing at data registers. - Altera_Forum
Honored Contributor
That's the part that keeps concerning me, is that I'm sort of, kind of, halfway treating this like a source synchronous interface even though it's not quite really one.
I feel like this is a problem that must have a simple, obvious solution, since it must come up all the time. Any time you want to run, for instance, a fast SPI link with the clock coming from a state machine, you're going to have a situation in which you need to close timing around an external device that outputs data in response to a clock coming off a registered FPGA output. - Altera_Forum
Honored Contributor
Sampling analogue signal needs very good quality clock such as that from oscillators. Practically for low Fs like 62.5 a PLL inside fpga may do. But combinatorially generated clock is doubtful and its jitter will vary from build to build and with temperature...etc.
Secondly for your timing constraints remember that your adc clk is opposite in direction to data and you need to be careful about using equations for source synchronous case where data and clock go together. - Altera_Forum
Honored Contributor
Primarily I haven't been using a PLL to generate ADC_CLK because I was trying to avoid adding extra things to the situation. Also I need to control the relationship between mc[0] and the ADC_CLK. That's resolvable, but it just seems to be adding more and more complexity to the problem. I was hoping for simple instead, since even with a PLL I'd need the tools to take into account the delay between the PLL and the actual PCB tracre.
- Altera_Forum
Honored Contributor
Nope, test design doesn't work. I tried the following VHDL file as the entirety of the design logic:
And the following constraints file:library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity temp is port ( CLK_125_IN : in std_logic; ADC_DATA : in std_logic_vector(7 downto 0); ADC_CLK : out std_logic; LATCHED_DATA : out std_logic_vector(7 downto 0) ); end entity temp; architecture Behavioral of temp is begin process (CLK_125_IN) variable tff : boolean; begin if rising_edge(CLK_125_IN) then if tff then LATCHED_DATA <= ADC_DATA; ADC_CLK <= '1'; else ADC_CLK <= '0'; end if; tff := not tff; end if; end process; end architecture Behavioral;
Still, no path to adc_clk. Same thing if I strike the tff_clk and just derive adc_clk straight from CLK_125_IN.set_time_format -unit ns -decimal_places 3 create_clock -name {CLK125} -period 8.000 create_generated_clock -name {tff_clk} -source -divide_by 2 create_generated_clock -name {adc_clk} -source set_input_delay -add_delay -rise -max -clock 12.000 ] set_input_delay -add_delay -rise -min -clock 3.900 ] set_multicycle_path -setup -end -from -to 2 set_multicycle_path -hold -end -from -to 1 - Altera_Forum
Honored Contributor
As an aside note, why not use PLL since ADC clocked by your clock will suffer jitter and you may get around timing issues you have.
- Altera_Forum
Honored Contributor
The IO_BUF and CLKCTRL(this is just the global driver) should not cause any problems. I'm really not sure what the issue is. If you try it with a test design, does it work?
- Altera_Forum
Honored Contributor
CLK_125_IN goes through an IO_IBUF to become CLK_125_IN~input , then a CLKCTRL to become CLK_125_IN~inputclkctrl , both of which were inferred by the tools. I'm not quite sure what the difference between them is. (Architecture is an Arria II GX, by the way).
Interestingly enough, the adc_clk register is being clocked by CLK_125_IN~input, whereas practically everything else in the design, including mc[0], is being clocked by CLK_125_IN~inputclkctrl. I'm not quite sure what difference that makes, other than probably being the problem. - Altera_Forum
Honored Contributor
Go to the technology map viewer and trace back from *mc*[0] to the source. Are you sure it's directly driven by CLK_125_IN, and that has a clock on it?
As you see, ADC_DATA is analyzed as it will make the generated clock, but it will be analyzed incorrectly, so definitely needs to be fixed. - Altera_Forum
Honored Contributor
That doesn't seem to have done it. The TFF in my simplified schematic is actually the LSB of an always-enabled master counter called mc. I tried running the following lines through the Tcl console in Timequest.
The query shows that I am getting a collection with a single entry as a result of my get_keepers call, as expected. But it's still telling me that there's no path between adc_clk and the source. [edit: correction; the ADC_DATA signals do still show on the clock transfers report when reading it all in from an SDC file.]set col ] query_collection -all $col create_generated_clock -name intl_adc_clk -source -divide_by 2 $col create_generated_clock -name adc_clk -source $col