polycodor
polycodor

Reputation: 101

How to correct a phase shift using a clock divider in VHDL?

I want to make a UART receiver that reads 8 consecutives bits with a parity bit at the end and with a simple stop bit. My FPGA have a clock of 100Mhz and the data that are transmitted to the uart have a rate of 56700 bauds. The dividing factor is 1736 (56700 * 1736 ≈ 100Mhz). The two outputs are the message of the input decoded by the uart and an error signal that indicates if the uart have correctly read the input. This is what I have :

    library ieee;
use ieee.std_logic_1164.ALL;
use ieee.numeric_std.all;

entity uart_receiver is
  generic (
    clksPerBit : integer := 1736     -- Needs to be set correctly
    );
  port (
    clk       : in  std_logic;
    clk_en_uart : in std_logic ;
    reset     : in std_logic;
    uart_rx : in  std_logic;
    error     : out std_logic;
    char   : out std_logic_vector(7 downto 0)
    );
end uart_receiver;


architecture uart_receiver_arch of uart_receiver is

  type etat is (init, start_bit, receiving_bits, parity_bit,
                     stop_bit );
  signal current_state : etat := init ;
  signal error_signal : std_logic := '0';
  signal clk_count : integer range 0 to clksPerBit-1 := 0;
  signal bit_index : integer range 0 to 7 := 0;  -- 8 Bits Total
  signal data_byte   : std_logic_vector(7 downto 0) := (others => '0');


begin

process (clk_en_uart)
  begin
    if rising_edge(clk_en_uart) then

    end if;
  end process;


process (clk,reset)
variable  check_parity : integer range 0 to 7 := 0;
   begin
     if (reset = '1') then 
             current_state <= init;
             error_signal <= '0';
             clk_count <= 0;
             bit_index <= 0;

             data_byte <= (others => '0');
     elsif rising_edge(clk) then
       case current_state  is
         when init =>

           clk_count <= 0;
           Bit_Index <= 0;

           if uart_rx = '0' then       -- Start bit detected
             current_state <= start_bit;
           else
             current_state <= init;
           end if;

          when start_bit =>
            if clk_count = (clksPerBit-1)/2 then
                 if uart_rx = '0' then
                   clk_count <= 0;  -- reset counter since we found the middle
                   current_state   <= receiving_bits;
                 else
                   current_state   <= init;
                 end if;
            else
                 clk_count <= clk_count + 1;
                 current_state <= start_bit;
               end if;               

         when receiving_bits =>
                  if clk_count < clksPerBit-1 then
                    clk_count <= clk_count + 1;
                    current_state   <= receiving_bits;
                  else
                    clk_count <= 0;
                    data_byte(bit_index) <= uart_rx;  
                 if bit_index < 7 then
                  bit_index <= bit_index + 1;
                  current_state   <= receiving_bits ;
                else
                  bit_index <= 0;
                  current_state <= parity_bit;
                end if;
              end if;

          when parity_bit =>
                if clk_count < clksPerBit-1 then
                  clk_count <= clk_count + 1;
                  current_state   <= parity_bit;
                else 
                   for k in 0 to 7 loop
                      if ( data_byte(k) = '1' ) then
                         check_parity := check_parity + 1 ;
                      end if;
                      end loop; 
                      if((uart_rx  = '1' and check_parity mod 2 = 0) or (uart_rx = '0' and check_parity mod 2 = 1)) then 
                            error_signal  <= '1' ;
                      else 
                            error_signal  <= '0';
                      end if ;
                  current_state <= stop_bit;
                end if;

               when stop_bit =>
                    if clk_count < clksPerBit-1 then
                      clk_count <= clk_count + 1;
                      current_state   <= stop_bit ;
                    else
                      clk_count <= 0;
                      current_state  <= init;
                    end if;

                when others => 
                     current_state <= init;
               end case;
         end if; 
     char <= data_byte ;    
     error <= error_signal ;  
     end process;                    
 end uart_receiver_arch;

So there's a phase shift between the data that is transmitted to the uart and his clock. If there's a phase shift, I'm not reading the data at the right time. I think that this code is sufficient to solve this problem. But, I've created a clock_divider and I can't seem to find a way to use it in this code. This is my clock divider :

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity clock_divider is
    generic (divfactor : positive := 1736);
    Port (clk,clk2, reset : in STD_LOGIC ;
          clkdiv, activationsig : out STD_LOGIC );
end clock_divider;

architecture clock_divider_arch of clock_divider is

begin
    process(clk,reset)
    variable  clksigv : std_logic := '0' ;
    variable  activationsigv : std_logic := '0' ;
    variable  count : integer := 0 ;
        begin
            if (reset = '1') then 
              clksigv := '0' ;
              activationsigv := '0' ;
              count := 0 ;
            elsif ( rising_edge(clk) ) then 
                count := count + 2 ;
                if (activationsigv = '1') then
                    activationsigv := '0';
                end if;
                if ( count >= divfactor - 1 ) then 
                    clksigv := not(clksigv) ;
                     if ( clksigv = '1' ) then 
                        activationsigv := '1' ;
                      end if;
                    count := 0 ;
                end if ;

            end if ;
            clkdiv <= clksigv ;
            activationsig <= activationsigv;
        end process ;           
end clock_divider_arch;

The outputs of this clock divider are the clock divided and the activation signal that, when it is at '1', I have to read the data in the uart. So, the two outputs should also be inputs of the uart. In the uart_recevier, clk_en_uart is actually the clock divided, but I'm not using it because I don't know how.

I think that the solution is to 'activate' this divided clock when I enter in the start_bit case so that I have two clocks with the same phase and the same frequency, but I also think that it impossible to set a phase for a clock.

I'm not sure that I've clearly adressed my problem. If there's something that you don't understand in my code or in my explanation, feel free to ask questions.

Thank you for your help, hoping that I find a solution.

Upvotes: 0

Views: 1292

Answers (1)

Morten Zilmer
Morten Zilmer

Reputation: 15924

Sounds like the suggested solution is complicated for this problem.

A usual approach is that the receiver justs look for the falling edge of the start bit, then count for half a bit time (1736 / 2 cycles in your case), then samples the start bit value there, and subsequently samples the data, parity and stop bit values after each full bit time (1736 cycles in your case). After that start over looking for a new falling edge of the start bit.

The difference between the transmitter and receiver frequencies are then (usually) so small that the sample time will be practically in the middle for messages of only 11 bits at relative low bitrate, and the counter restart at falling edge of start bit ensures that any effect of long time frequency difference is removed.

Upvotes: 0

Related Questions