• 0

Simple UART message repeater (not working)


Go to solution Solved by hamster,

Question

Title says it all. I've tried various baud rates. I've tried using the wizard to generate a clock that I then divide to see if it's more accurate. Right now, the latest attempt divides my 100MHz clock by 1k to get a clean 100k baud. I'm using the analog discovery 2 to spy on BB1 (first breadboard pin) on my Anvyl. My code waits for a button press to repeatedly send out a simple hard-coded message, which then resets on the button release. The LEDs are just for primitive version control (so I know what program is on the board). Here's the source for both the repeater and the clock divider:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
USE ieee.numeric_std.ALL;


entity msg_repeater is
    Port ( clk : in  STD_LOGIC;
			  btn : in STD_LOGIC;
           tx : out  STD_LOGIC;
			  led : out STD_LOGIC_VECTOR (3 DOWNTO 0));
end msg_repeater;

architecture Behavioral of msg_repeater is
	
	component clk_div is
    Port ( CLK_IN : in  STD_LOGIC;
           CLK_OUT : out  STD_LOGIC);
	end component;
	
	-- Binary message content:
	-- "01010100" T
	-- "01000101" E
	-- "01010011" S
	-- "01010100" T
	-- "00100000" space (0 and 1 added on sides of each character for UART protocol)

	signal msg : std_logic_vector (49 downto 0) := "00101010010010001011001010011100101010010001000001";
	signal msg_index : natural range 0 to 50 := 0;
	signal int_tick : std_logic := '0';
	signal baud_tick : std_logic := '0';

begin
		
	clk_div_inst : clk_div
		port map(
		CLK_IN	=>	clk,
		CLK_OUT	=>	baud_tick
		);
		
	process(baud_tick)
	begin
	if rising_edge(baud_tick) then
		
	if(btn = '1') then
		if(msg_index = 50) then
			msg_index <= 0;
		end if;
		
		tx <= msg(msg_index);
		msg_index <= msg_index + 1;
	else
		tx <= '1';
		msg_index <= 0;
	end if;
	
	end if;
	end process;

	led <= "1010";
end Behavioral;

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;

entity clk_div is
    Port ( CLK_IN : in  STD_LOGIC;
           CLK_OUT : out  STD_LOGIC);
end clk_div;

architecture Behavioral of clk_div is

	--signal clkCount : std_logic_vector (3 downto 0) := "0000"; --counter for input clock
	signal clkCount : std_logic_vector (9 downto 0) := "0000000000"; --counter for input clock
	signal outTick : std_logic := '0';
	
	--constant clk_check : std_logic_vector := "1010";
	constant clk_check : std_logic_vector := "1111101000";

begin

	process(CLK_IN)
	begin
	
	if CLK_IN'Event and CLK_IN = '1' then
	
		if(clkCount = clk_check) then
			outTick <= '1';
			clkCount <= "0000000000";
		else
			outTick <= '0';
			clkCount <= clkCount + 1;
		end if;
	
	end if;
	
	end process;
	
	CLK_OUT <= outTick;

end Behavioral;

 

Link to post
Share on other sites

6 answers to this question

Recommended Posts

  • 1
  • Solution
41 minutes ago, CPerez10 said:

Went through the code. It does indeed work, thank you. This raises a few questions though. Are VHDL logic vectors stored as little endian, or just iterated backwards? And is there a book/website on timing with VHDL? The button synchronization needs a bit of an explanation.

I think of std_logic_vector the same way I would think of digits in a number...  the rightmost digit is digit zero. It most likely isn't the best way set things up for this example, but it avoids the need to swap the bit ordering in the ASCII characters.

Oh, for the button synchronization...  signals take time to get across the chip (speed of light, capacitance and so on), so different parts of the design can see different values for the same signal as it change unless. As you can't control when the user might press the button you have to sample the value of the input signal on the clock edge,  holding that in a register. That registered value is then used drive the rest of your logic.

There is a slight complication - If the signal from the button changes state *exactly* on the clock edge, the flipflop might not be able to correctly register as a 1 or a 0, but could be in some weird "metastable" state that takes a short while to become either a 1 or a 0. To stop this causing bugs in the operation of the logic deeper in the deign, the output of that fliplfop is then sampled a second time to get a "known good, either 1 or 0" signal, that can get to where it needs to within a clock cycle.

Hence the design pattern... btn  gets sampled into btn_metastable (which is a bit dodgy if you use it), and then btn_metastable gets sampled into btn_synchronized, which is then used by the rest of the logic.

Link to post
Share on other sites
  • 1

Had a hack at it... tested working on BASYS3

 

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;


entity msg_repeater is
    Port ( clk : in  STD_LOGIC;
           btn : in  STD_LOGIC;
           tx  : out STD_LOGIC;
           led : out STD_LOGIC_VECTOR (3 DOWNTO 0));
end msg_repeater;

architecture Behavioral of msg_repeater is
   constant char_t     : std_logic_vector (7 downto 0) := "01010100";
   constant char_e     : std_logic_vector (7 downto 0) := "01000101";
   constant char_s     : std_logic_vector (7 downto 0) := "01010011";
   constant char_space : std_logic_vector (7 downto 0) := "00100000";
   
   -- Note message is sent from bit 0 to the highest
   signal msg : std_logic_vector (49 downto 0) := 
         char_space & "0" &   -- Character 4, with start bit
         "1" & char_t & "0" & -- Character 3, wrapped in start and stop bit
         "1" & char_s & "0" & -- Character 2, wrapped in start and stop bit
         "1" & char_e & "0" & -- Character 1, wrapped in start and stop bit
         "1" & char_t & "0" & -- Character 0, wrapped in start and stop bit 
         "1";                 -- Idle symbol and stop bit of the last character
       
   signal msg_index : unsigned( 7 downto 0) := (others =>'0');
   
   -- Should we send a message?
   signal triggered        : std_logic := '0';
   
   -- for generating the baud tick rate
   constant clock_rate     : natural := 100000000;
   constant baud_rate      : natural := 9600;
   signal baud_counter     : unsigned(27 downto 0) := (others => '0');
   signal baud_tick        : std_logic := '0';
   
   -- For the button synchonizer
   signal btn_synchronized : std_logic := '0';
   signal btn_metastable   : std_logic := '0';

begin

process(clk)
   begin
      if rising_edge(clk) then
         -- Set the serial output bit
         tx  <= msg(to_integer(msg_index));
         led <= "0001";
          
         -- Controlling the message index
         if baud_tick = '1' then            
            if msg_index = 0 then
               -- We are waiting to be triggered
               if triggered = '1' then
                  msg_index <= msg_index + 1;
               end if;
            elsif msg_index = msg'high then
               -- We have finished the message
               msg_index <= (others => '0');
               triggered <= '0';
            else               
               -- We are sending bits
               msg_index <= msg_index + 1;
            end if;
         end if;
         
         -- Generating the baud tick
          if baud_counter < baud_rate then
            baud_tick <= '1';
            baud_counter <=  baud_counter - baud_rate + clock_rate;
         else
            baud_tick <= '0';
            baud_counter <=  baud_counter - baud_rate;
         end if;
         
         -- Seeing if we are triggered
         if btn_synchronized  = '1' then
            triggered <= '1';
         end if;
         
         -- Synchronize the button with the clock domain
         btn_metastable <= btn;
         btn_synchronized <= btn_metastable;
      end if;
   end process;

end Behavioral;

 

Link to post
Share on other sites
  • 0

Oh, and it's standard polarity, 8 data bits, no parity, and 1 stop bit. Also, I noticed I left some artifacts from other attempts (int_tick and other commented division constant). Apologies.

Link to post
Share on other sites
  • 0

Away from my laptop at the moment, but they way I would do this:

A register sized to hold your clock rate (28 bits for 100MHz). 

If it is less than the baud_rate, set 'bit_tick' to '1' and add (clock_rate- baud_rate) to the register. Otherwise set baud_tick to '0' and subtract the baud rate from the register. 

That will give you 'bit_tick' that is 1 for the right number of cycles per second, and allow you to keep everything in the design running in the same clock domain. 

You also want to have a synchronizer on your button, to make it work reliably. 

You also have a problem in that when the button is lifted you will stop sending data straight away, so will most likely send an incomplete message. 

Due to the way that VHDL signal assignments work you will try to send out bit 50 of your message, so that might mess things up. 

Would you like me to have a crack at rewriting it for you, so you can see the difference?

Link to post
Share on other sites
  • 0

BTW a reliable decoding / recoding UART repeater is conceptually harder than you'd think: UART has a fairly high (percent range) tolerance for timing mismatch. If your source clock is slightly fast and data is uninterrupted, the repeater will eventually be forced to drop bytes.

 

Link to post
Share on other sites
  • 0
Posted (edited)

Went through the code. It does indeed work, thank you. This raises a few questions though. Are VHDL logic vectors stored as little endian, or just iterated backwards? And is there a book/website on timing with VHDL? The button synchronization needs a bit of an explanation.

Edited by CPerez10
Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now