Friday, 17 April 2015

vhdl - UART Receiver glitches


I encounter problems with my UART receiver module. It is supposed to work at 9600 bauds without parity bit and only one stop bit.


The problem is that my UART misses some characters (or to indicate that it received some characters) in a non-deterministic way.


My two main but unsucessful ideas about this problem were


1) a problem with the reception with the clock at 9600Hz, it is maybe not accurate enough or I don't have any error margin. I tried oversampling but it did not solve the problem.


2) A problem with the acknowledge coming from the outside (CPU). I use a little trick with a DFF to let the CPU acknowledge correctly his reading while having a much higher clock that the receiver (50MHz vs 9600Hz).


I am perfectly sure that my clock is at 9600Hz (I am able to receive correctly a lot of characters).


Here is the small code of the receiver I use.


library ieee;

use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity UART_Receive is
port(
clk_9600Hz : in std_logic;
reset_n : in std_logic;
ackNewData : in std_logic;
ready : out std_logic;
dataToRead : out std_logic_vector(7 downto 0);

Rx : in std_logic
);
end entity UART_Receive;

architecture RTL of UART_Receive is
signal counter : unsigned(3 downto 0);
signal buffer_read : std_logic_vector(7 downto 0);
signal lastData : std_logic_vector(7 downto 0);
signal newDataReady: std_logic;
begin

dataToRead <= lastData;

process(ackNewData, newDataReady)
begin
if(ackNewData = '1') then
ready <= '0';
elsif(rising_edge(newDataReady)) then
ready <= '1';
end if;
end process;


process(clk_9600Hz, reset_n) is
begin
if reset_n = '0' then
lastData <= (others => '0');
counter <= (others => '0');
buffer_read <= (others => '0');

elsif rising_edge(clk_9600Hz) then
if counter = 0 and Rx = '0' then

counter <= counter + 1;
elsif counter = 9 then
counter <= (others => '0');
lastData <= buffer_read;
newDataReady <= '1';
elsif counter /= 0 then
buffer_read <= Rx & buffer_read(7 downto 1);
counter <= counter + 1;
end if;


end if;
end process reg_slow_process;

end architecture RTL

Answer



I once fell into the same trap myself.


Remember, the "A" in "UART" means "asynchronous"! You need to treat the Rx signal as an asynchronous input, and synchronize it to your internal clock by running it through a pair of flip-flops before you do anything else with it.


Note that in your code (specifically, if counter = 0 and Rx = '0' then), the Rx input affects multiple flip-flops (counter) in parallel. You can think of the four flip-flops that hold the count value as being driven by a multiplexer that selects either (others => '0') or counter + 1 to be loaded into them. The Rx signal feeds the logic that drives this multiplexer. If it is asynchronous, it could be changing just as the clock edge arrives. If this happens, some of the flip-flops will get bits from one value, and some of them will get bits from the other. The result will be a completely random state.


But also, as David Koontz points out, you really need to oversample Rx in the state machine so that you can ensure that the bits you're shifting into your shift register are NOT sampled close to the transitions. 4× oversampling should be considered a minimum, and the de-facto industry standard is to oversample at 16×, with optional "voting" among multiple samples near the center of each bit for extra noise immunity.


Here are some timing diagrams to help illustrate the issue that oversampling solves. In each case, the vertical line of '*' represents the point at which the state machine recognizes the incoming start bit, and the subsequent vertical lines of '|' represent when the bits are sampled. The Rx signal can arrive early or late with respect to the clock, so both extremes are shown.



With a 1× clock, at either extreme, the data bits might be sampled very close to where they might be changing. If the transmit clock phase drifts a bit with respect to the receive clock, bit errors will occur.


                  ______       *______       |______       |______
1x clock ______/ \______/ \______/ \______/ \______
_______ * ____________| ____________| _____________
Rx (early) \____________*X_____________X_____________X_____________
start * data | data | data
___________________ * |____________ |____________
Rx (late) \*____________X_____________X_____________X
* start | data | data


With a 2× clock, the problem with the "late" extreme has been eliminated, but the problem with the "early" extreme remains. There's still a possibility of bit errors.


                  ___    *___    |___     ___    |___     ___    |___    
2x clock ______/ \___/ \___/ \___/ \___/ \___/ \___/ \___
_______ * | ______________| ______________| _______
Rx (early) \______*_______|X______________|X______________|X_______
start | data | data | data
_____________ * | ________|______ ________|______ _
Rx (late) \*_______|______X________|______X________|______X_
* start| data | data |


The 4× clock completely eliminates the problem for both timing extremes. The bits will be sampled somewhere between 50% and 75% of the bit period.


                  __   *__    __   |__    __    __    __   |__    __    __   
4x clock ______/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/
_______ * | ________________|___ __________
Rx (early) \____*___________|______X________________|___X__________
start | data | data
___________ * | ____________|__________ ___
Rx (late) \*___________|__________X____________|__________X___
* start | data |

No comments:

Post a Comment

arduino - Can I use TI&#39;s cc2541 BLE as micro controller to perform operations/ processing instead of ATmega328P AU to save cost?

I am using arduino pro mini (which contains Atmega328p AU ) along with cc2541(HM-10) to process and transfer data over BLE to smartphone. I...