06-19-2018 06:18 AM
Hello,
i have a problem with pll accuracy which means :
For a ddr data transfer i generate two clocks with 180 degree phase.
The data is sampled at a possibly slower clock rate, so the data is transferred through a fifo.
I found, that the pll with 100MHz input, multiplicator 16, divider 16 -> 100MHz output doesn't generate exacly 100MHz output.
I added counter to fifo in and out and frequency in and out. Master counter counts 500000000 samples.
The result with pll is : output-count is 500000000, but input is 500004096 ( may vary a little). This means, that Pll-output is somewhat slower than input. I tested other multiplicators like 8 and 12 to check dependency of vco, but result is the quite the same (other counter values, but pll-output too slow every time).
At least it was expected that pll would be sometimes faster or slower depending on regulation, but it is always slower.
I also tested a MMCME2_BASE at multiplicator 12 (100MHz in, VCO 1200MHz, 100MHz out), but result is the same, output clock is slower, counter is 500010228..500010965 / 5000000000.
If i wipe out the pll and use equal clocks on both sides, every counter counts 500000000 (equal clocks means 100MHz by Oszillator and 100MHz by analog external PLL with Oscillator as input).
The count is done by generating a start_signal and a stop signal by the master counter counting 500000000.
Theese signal are syncronised to the second clock the other counter runs on.
This all means, that the fifo is written faster than read and i loose data by overflow.
Best regards,
Stefan Verse
The design is a little big - so here the code for the MMCME2_BASE instantiation, but the pll is quite the same :
MMCME2_BASE_inst : MMCME2_BASE
generic map (
BANDWIDTH => "LOW", -- Jitter programming (OPTIMIZED, HIGH, LOW)
CLKFBOUT_MULT_F => 12.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)
CLKOUT1_DIVIDE => 6,
CLKOUT2_DIVIDE => 1,
CLKOUT3_DIVIDE => 1,
CLKOUT4_DIVIDE => 1,
CLKOUT5_DIVIDE => 1,
CLKOUT6_DIVIDE => 1,
CLKOUT0_DIVIDE_F => 12.0, -- Divide amount for CLKOUT0 (1.000-128.000).
-- 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.1, -- Reference input jitter in UI (0.000-0.999).
STARTUP_WAIT => FALSE -- Delays DONE until MMCM is locked (FALSE, TRUE)
)
port map (
-- Clock Outputs: 1-bit (each) output: User configurable clock outputs
CLKOUT0 => clkout0_mmcm_filt, -- 1-bit output: CLKOUT0
CLKOUT0B => clkout1_mmcm_filt, -- 1-bit output: Inverted CLKOUT0
CLKOUT1 => idelay_ref_clk_s, -- 1-bit output: CLKOUT1
CLKOUT1B => open, -- 1-bit output: Inverted CLKOUT1
CLKOUT2 => open, -- 1-bit output: CLKOUT2
CLKOUT2B => open, -- 1-bit output: Inverted CLKOUT2
CLKOUT3 => open, -- 1-bit output: CLKOUT3
CLKOUT3B => open, -- 1-bit output: Inverted CLKOUT3
CLKOUT4 => open, -- 1-bit output: CLKOUT4
CLKOUT5 => open, -- 1-bit output: CLKOUT5
CLKOUT6 => open, -- 1-bit output: CLKOUT6
-- Feedback Clocks: 1-bit (each) output: Clock feedback ports
CLKFBOUT => mmc_fb_clk_s, -- 1-bit output: Feedback clock
CLKFBOUTB => open, -- 1-bit output: Inverted CLKFBOUT
-- Status Ports: 1-bit (each) output: MMCM status ports
LOCKED => dll2_locked_s, -- 1-bit output: LOCK
-- Clock Inputs: 1-bit (each) input: Clock input
CLKIN1 => tx_clk_i, -- 1-bit input: Clock
-- Control Ports: 1-bit (each) input: MMCM control ports
PWRDWN => '0', -- 1-bit input: Power-down
RST => dll2_reset_s, -- 1-bit input: Reset
-- Feedback Clocks: 1-bit (each) input: Clock feedback ports
CLKFBIN => mmc_fb_clk_s -- 1-bit input: Feedback clock
);
and the code for the two clocks ( thers are global buffer on .clkout0_mmcm_filt and clkout1_mmcm_filt) :
ddr_clk_i is pll-output,
clk_i is pll-input
the counters are free running and update time_store_s and freq_store_s every 5 seconds
signal time_cnt_s : std_logic_vector(31 downto 0);
signal freq_cnt_s : std_logic_vector(31 downto 0);
signal time_store_s : std_logic_vector(31 downto 0);
signal freq_store_s : std_logic_vector(31 downto 0);
signal cnt_active_s : std_logic;
signal cnt_active_p1_s : std_logic;
signal store_s : std_logic;
signal store_p1_s : std_logic;
signal store_p2_s : std_logic;
signal clear_s : std_logic;
signal clear_p1_s : std_logic;
signal clear_p2_s : std_logic;
signal state_s : std_logic_vector(2 downto 0);
constant c_idle : std_logic_vector(2 downto 0) := "000";
constant c_clear : std_logic_vector(2 downto 0) := "001";
constant c_run : std_logic_vector(2 downto 0) := "010";
constant c_store : std_logic_vector(2 downto 0) := "100";
process(reset_i,ddr_clk_i)
begin
if reset_i='1' then
state_s <= c_idle;
time_cnt_s <= (others => '0');
time_store_s <= (others => '0');
clear_s <= '1';
store_s <= '0';
cnt_active_s <= '0';
store_p2_s <= '0';
clear_p2_s <= '0';
elsif rising_edge(ddr_clk_i) then
store_p2_s <= store_p1_s;
clear_p2_s <= clear_p1_s;
if state_s=c_idle then
time_cnt_s <= (others => '0');
state_s <= c_clear;
clear_s <= '1';
store_s <= '0';
cnt_active_s <= '0';
elsif state_s=c_clear then
clear_s <= '1';
time_cnt_s <= (others => '0');
if clear_p2_s='1' then
state_s <= c_run;
clear_s <= '0';
store_s <= '0';
cnt_active_s <= '1';
end if;
elsif state_s=c_run then
cnt_active_s <= '1';
time_cnt_s <= time_cnt_s + '1';
if time_cnt_s = "00011101110011010110010100000000" then -- 500000000 -> 5 sec
state_s <= c_store;
clear_s <= '0';
store_s <= '1';
cnt_active_s <= '0';
time_store_s <= time_cnt_s;
end if;
elsif state_s=c_store then
store_s <= '1';
time_cnt_s <= (others => '0');
valid_cnt_s <= (others => '0');
if store_p2_s='1' then
state_s <= c_idle;
clear_s <= '0';
store_s <= '0';
cnt_active_s <= '0';
end if;
else
state_s <= c_idle;
end if;
end if;
end process;
process(clk_i)
begin
if rising_edge(clk_i) then
store_p1_s <= store_s;
clear_p1_s <= clear_s;
cnt_active_p1_s <= cnt_active_s;
if store_p1_s='1' then
freq_store_s <= freq_cnt_s;
end if;
if clear_p1_s='1' then
freq_cnt_s <= (others => '0');
elsif cnt_active_p1_s='1' then
freq_cnt_s <= freq_cnt_s + '1';
end if;
end if;
end process;
06-20-2018 05:37 AM
Problem solved. Confirm PLLs are ok.
The bug was - as so often- a copy paste error.
As mentionend before the design has more than one pll and uses a self resetting state machine if the pll looses lock.
So pll reset is set at startup and when pll looses lock - this is a freerunning counter, that reset the pll periodically until lock is active again.
By copy-paste this machine for the second pll the lines watching pll-locked of second pll were not renamed and so pll-lock wasn't observed. The states of the signals pll2_locked_p1_s and pll2_locked_p2_s were undefined.
Thanks for help,
sometimes we just need a hint to look somewhere else ....
and yes, the reasons for the bug is 50 cm in front of the monitor.
06-19-2018 06:27 AM
Are you waiting for the pll/MMCM to lock before starting both counters?
06-19-2018 07:22 AM
Is this real or simulation ?
if real, have you simulated ?
I'd suggest if you do to shorten the counters to 5ms instead of 5 seconds,
Suggest you also look at entering constants as constants,
e.g this would read better as a constant
"00011101110011010110010100000000"
such as
constant fred = B" "0001_1101_1100_1101_0110_0101_0000_0000";
As for the problem ,
I'd be very surprised if the PLL is loosing clocks.
I only have a small screen here, so looking at the code is difficult,
I hmaving difficulty working out how you have the two clocks connected , the state machine and the fifo
can you do a simple block diagram for us ?
06-20-2018 04:49 AM
Some news from the front.
I have extracted my code and set up a simple project with jhust the counters and a PLL.
Result is PLL is ok, counters are the same.
In my bigger project with the same clocks and PLL the counters differ from each other - i try to find out other reasons for it.
I'm sure the PLL is locked, but i will check it. Maybe i have a problem with a glitch on the pll-reset line or something like that.
Concerning the question to the state machine - this is quite easy.
The state machine starts in idle and goes to clear the reference counter is cleared. The slave counter gets clear_p1_s and clears the counter.
State machine waits for clear_p2_s to be shure slave counter is cleared, then moves on to run - reference counter counts to 500000000 and slave counter does also because of cnt_active_p1_flag.
State mschine moves to store and stores counter value and set store_s.
Slave counter sees store_p1_s and stops counting and stores the slave counter.
State machine wait for Handshake store_p2_s to be shure both counters are stored. The restarts from idle.
06-20-2018 05:37 AM
Problem solved. Confirm PLLs are ok.
The bug was - as so often- a copy paste error.
As mentionend before the design has more than one pll and uses a self resetting state machine if the pll looses lock.
So pll reset is set at startup and when pll looses lock - this is a freerunning counter, that reset the pll periodically until lock is active again.
By copy-paste this machine for the second pll the lines watching pll-locked of second pll were not renamed and so pll-lock wasn't observed. The states of the signals pll2_locked_p1_s and pll2_locked_p2_s were undefined.
Thanks for help,
sometimes we just need a hint to look somewhere else ....
and yes, the reasons for the bug is 50 cm in front of the monitor.
06-20-2018 05:45 AM
Oh I've been there so often ,
well done for comming back
and good luck