Clock jitter removal in VHDL

From Hackerspace ACKspace
Jump to: navigation, search
Project: Clock jitter removal in VHDL
Featured:
State Completed
Members Danny Witberg
GitHub No GitHub project defined. Add your project here.
Description Jitter removal in clock signals with VHDL implementation
Picture
Jitter1.gif

Unstable clock signals

Stable clock signals in digital domains is, generally speaking, something you should aim for, and desirable in most designs. A stable clock signal is reliable, and other circuits can depend on them. For instance, a reference signal to a PLL greatly depends on its stability in order for them to generate a higher frequency with a multiple oscillation frequency.

But what is this is not the case? What if your clock signal varies in frequency? This could really ruin your day! This project describes a way to stabilize a relative low frequency signal, with a much higher frequency, but stable, reference signal. To implement this inside an FPGA, an design is proposed in VHDL.

What is jitter?

A periodic variance in the frequency of a signal is called jitter. There can be a number of other unwanted characteristics to a clock signal, but jitter is one of the most common. The higher the jitter frequency, the more problems it could cause. Preferably, the jitter frequency would be 0Hz, but if you sample a clock with a higher frequency reference clock, you always end up with some sort of jitter. The VHDL implementation of this design converts a high jitter frequency to a lower one. Apart from the jitter frequency, also the amount that the jitter is influencing the signal should be reduced to a minimum. In short, the jitter frequency and amplitude must be reduced to increasethe clock quality.

Jitter1.gif

The problem

So how is this jitter caused? For example, let's take a data link. This datalink is clocked at 25MHz, and is transmitting frames of information. The frequency of these frame is very important, and every 20.8uS, a full frame is transmitted. Because there is no common divider between the 25MHz datalink frequency and the 48kHz (20.8uS) frame repetition, a frame duration can be 521x bits in the datalink, or 520. Additional timing issues even can introduce frames of 519 or 521 bits. Because this 48kHz is fed into a PLL, the frequency must be as stable as can be.

The solution

A common divider between 25MHz and 48kHz is 150MHz. 150.000.000/25.000.000 = 6 and 150.000.000/48.000 = 3125. This is a very good choice of frequency if you want to exactly rate match the frame clock to the bit clock. However, if another frame clock is chosen, this is no longer valid and another solution has to be formed. A general master clock of 100MHz is chosen from a stable crystal oscillator. This means that, no matter what we try to do in VHDL, there will Always be some amount of jitter present in the frame clock. We can however try to minimize the effectiveness of the jitter, to keep its frequency and amplitude as low as possible.

First, we will have to get a more precise measurement of the frame clock. We will use the assumed stable master clock for this, and if we count the amount of master clock cycles for one frame duration, we'll end up with 2083 cycles (100000100011 binary notation). Or 2084 cycles. To get more precision, more frames have to be measured to get a more precise measurement. If we measure 1024 frame times, the duration is 2133333 master clock cycles (1000001000110101010101 binary). If you look closer at the binary notation, you could notice that the start of the 1024 cycle measurement matches the 1 frame binary. We can call this the integer part. The remaining 10 bits we could call the fractional part. So the frame time we can express in integer:fractional time: 2083:341 bits.

So what exactly does the fractional part mean? It means that, if we send out 1024 times 2083 bits, we would still have to send out 341 bit times in order to match the original measured time of 1024 frame times. so if we send out 341x2084 bits and (1024-341=)683x2083 bits, we did not alter the frequency of the original signal. We can group these two distinct bit counts in order to minimise the jitter frequency, and make it more behave like wander instead of jitter. Wander is less perceptible and intrusive than jitter, so this is preferred.

VHDL code


library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;

entity clock_stabilizer is
 generic(
  integer_part: integer := 12;
  fractional_part : integer := 10
 );

 port(
  clk_100 : in std_logic;
  wordclock_in : in std_logic;
  wordclock_out : out std_logic
 );
end entity clock_stabilizer;

architecture behavioural of clock_stabilizer is

 signal wordclock_shift : std_logic_vector(1 downto 0);
 signal wordclock_in_counter : std_logic_vector(fractional_part-1 downto 0);
 signal wordclock_time_add : std_logic_vector(integer_part+fractional_part-1 downto 0);
 signal wordclock_time_total : std_logic_vector(integer_part+fractional_part-1 downto 0);
 signal wordclock_period_int_counter : std_logic_vector(integer_part-1 downto 0);
 signal wordclock_period_frac_counter : std_logic_vector(fractional_part-1 downto 0);

begin

 wordclock_in_processing : process (clk_100)
 begin
  if clk_100'event and clk_100 = '1' then
   wordclock_shift <= wordclock_shift(0) & wordclock_in;
   if wordclock_shift = "01" then
    if wordclock_in_counter = 2**wordclock_in_counter'length-1 then
     wordclock_time_total <= wordclock_time_add;
     wordclock_time_add <= (others => '0');
     wordclock_in_counter <= (others => '0');
    else
     wordclock_in_counter <= wordclock_in_counter + 1;
    end if;
   else
    wordclock_time_add <= wordclock_time_add + 1;
   end if;
  end if;
 end process wordclock_in_processing;
 
 wordclock_out_processing : process (clk_100)
 begin
  if clk_100'event and clk_100 = '1' then
   if wordclock_shift = "01" and wordclock_in_counter = 2**wordclock_in_counter'length-1 then
    wordclock_period_frac_counter <= (others => '0');
    wordclock_period_int_counter <= (others => '0');
    wordclock_out <= '1';
   else
    if wordclock_period_frac_counter <= wordclock_time_total(fractional_part-1 downto 0) + 1 then
     if wordclock_period_int_counter <= wordclock_time_total(integer_part+fractional_part-1 downto fractional_part) then
      wordclock_period_int_counter <= wordclock_period_int_counter + 1;
      if wordclock_period_int_counter = '0' & wordclock_time_total(integer_part+fractional_part-1 downto fractional_part+1) then
       wordclock_out <= '0';
      end if; 
     else
      wordclock_period_int_counter <= (others => '0');
      wordclock_out <= '1';
      wordclock_period_frac_counter <= wordclock_period_frac_counter + 1;
     end if;
    else
     if wordclock_period_int_counter < wordclock_time_total(integer_part+fractional_part-1 downto fractional_part) then
      wordclock_period_int_counter <= wordclock_period_int_counter + 1;
      if wordclock_period_int_counter = '0' & wordclock_time_total(integer_part+fractional_part-1 downto fractional_part+1) then
       wordclock_out <= '0';
      end if; 
     else
      wordclock_period_int_counter <= (others => '0');
      wordclock_out <= '1';
      wordclock_period_frac_counter <= wordclock_period_frac_counter + 1;
     end if;  
    end if;
   end if;
  end if;
 end process wordclock_out_processing;

end behavioural;