(I posted same question in stackoverflow but didn't get right answer)
I'm newbie in VHDL. So today, after UART Tx, I tried to read Datas that coming from Realterm terminal. I only wrote Rx codes to just see data. I also used 7 segment display and 7 LED to see what data I receieved. When you look testbench results, the problem is when 3 bit register is "110" twice times, program jumps into rx_data_bit without doing rx_start_bit <= '1'
EDIT 1: Codes and testbench results are updated. Now the issue is rx_data_bit never goes '0' so rx_stop_bit never goes '1'
EDIT 2: I think problem is here
if(rx_data_bit = '1') then
counter_del_half <= '0';
if(half_pulse = '1')then
rx_reg((rx_counter)) <= in_Data_rx;
if(rx_counter < 8 and baud_pulse = '1')then
rx_counter <= rx_counter + 1;
if(rx_counter = 8) then
rx_data_bit <= '0';
rx_stop_bit <= '1';
end if;
end if;
end if;
end if;
EDIT 3: Now, counter counts correctly but I got a question here. Should rx_counter's first bit (1) do last half bit long or 1 bit long? Also, rx_stop_bit must be '1', when rx_counter reaches 9.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.NUMERIC_STD.ALL;
entity UART_Rx is
port (
CLK: in std_logic;
nReset: in std_logic;
in_Data_Rx: in std_logic;
Segment7_1: out std_logic_vector (6 downto 0);
Segment7_2: out std_logic_vector (6 downto 0)
);
end UART_Rx;
architecture Behavioral of UART_Rx is
-- CLOCK AND BUTTON SIGNALS --
constant Baudrate: integer:= 9600;
constant CLK_Hiz: integer:= 50000000;
constant CLK_Bit: integer:= (CLK_Hiz / Baudrate) + 1;
signal counter_baud: integer range 0 to (CLK_Bit - 1):= 0;
signal counter_half: integer range 0 to ((CLK_Bit - 1)/2):= 0;
signal counter_del_baud: std_ulogic:= '0';
signal counter_del_half: std_ulogic:= '0';
signal baud_pulse: std_ulogic:= '0';
signal half_pulse: std_ulogic:= '0';
-- RX SIGNALS --
signal rx_reg: std_logic_vector (7 downto 0):= (others => '0');
signal rx_reg_counter: std_logic_vector (2 downto 0):= (others => '0');
signal rx_counter: integer range 0 to 9:= 0;
signal rx_first_bit: std_ulogic:= '1';
signal rx_start_bit: std_ulogic:= '0';
signal rx_data_bit: std_ulogic:= '0';
signal rx_stop_bit: std_ulogic:= '0';
signal rx_data_al: std_ulogic:= '0';
begin
-- CLOCK PROCESS --
clk_process: process(CLK,nReset)
begin
if(nReset = '0') then
baud_pulse <= '0';
counter_baud <= 0;
half_pulse <= '0';
counter_half <= 0;
elsif(rising_edge(CLK)) then
if(counter_baud < (CLK_Bit - 1)) then
counter_baud <= counter_baud + 1;
baud_pulse <= '0';
else
counter_baud <= 0;
baud_pulse <= '1';
end if;
if(counter_half < (CLK_Bit - 1)/2) then
counter_half <= counter_half + 1;
half_pulse <= '0';
else
counter_half <= 0;
half_pulse <= '1';
end if;
if(counter_del_baud = '1') then
counter_baud <= 0;
end if;
if(counter_del_half = '1') then
counter_half <= 0;
end if;
end if;
end process;
rx_process: process(CLK, nReset)
begin
if(rising_edge(CLK)) then
rx_reg_counter <= rx_reg_counter (1 downto 0) & in_Data_Rx;
if(rx_first_bit = '1' and rx_reg_counter (2 downto 0) = "110") then
counter_del_baud <= '1';
rx_start_bit <= '1';
rx_first_bit <= '0';
end if;
if(rx_start_bit = '1') then
counter_del_baud <= '0';
if(baud_pulse = '1') then
counter_del_baud <= '1';
rx_start_bit <= '0';
rx_data_bit <= '1';
counter_del_half <= '1';
end if;
end if;
if(rx_data_bit = '1') then
counter_del_half <= '0';
counter_del_baud <= '0';
if(half_pulse = '1')then
rx_data_bit <= '0';
rx_data_al <= '1';
rx_counter <= 1;
end if;
end if;
if(rx_data_al = '1') then
rx_reg(rx_counter - 1) <= in_Data_rx;
if(rx_counter > 0 and rx_counter < 9) then
if(baud_pulse = '1')then
rx_counter <= rx_counter + 1;
if(rx_counter = 9) then
rx_data_al <= '0';
rx_stop_bit <= '1';
end if;
end if;
end if;
end if;
if(rx_stop_bit = '1')then
rx_counter <= 0;
if(baud_pulse = '1') then
rx_stop_bit <= '0';
rx_reg_counter <= (others => '1');
rx_first_bit <= '1';
end if;
end if;
case rx_reg(3 downto 0) is --abcdefg--
when "0000" => segment7_1 <= "0000001"; -- '0'
when "0001" => segment7_1 <= "1001111"; -- '1'
when "0010" => segment7_1 <= "0010010"; -- '2'
when "0011" => segment7_1 <= "0000110"; -- '3'
when "0100" => segment7_1 <= "1001100"; -- '4'
when "0101" => segment7_1 <= "0100100"; -- '5'
when "0110" => segment7_1 <= "0100000"; -- '6'
when "0111" => segment7_1 <= "0001111"; -- '7'
when "1000" => segment7_1 <= "0000000"; -- '8'
when "1001" => segment7_1 <= "0000100"; -- '9'
when "1010" => segment7_1 <= "0001000"; -- 'A'
when "1011" => segment7_1 <= "1100000"; -- 'B'
when "1100" => segment7_1 <= "0110001"; -- 'C'
when "1101" => segment7_1 <= "1000010"; -- 'D'
when "1110" => segment7_1 <= "0110000"; -- 'E'
when "1111" => segment7_1 <= "0111000"; -- 'F'
when others => segment7_1 <= "1111111";
end case;
case rx_reg(7 downto 4) is
when "0000" => segment7_2 <= "0000001"; -- '0'
when "0001" => segment7_2 <= "1001111"; -- '1'
when "0010" => segment7_2 <= "0010010"; -- '2'
when "0011" => segment7_2 <= "0000110"; -- '3'
when "0100" => segment7_2 <= "1001100"; -- '4'
when "0101" => segment7_2 <= "0100100"; -- '5'
when "0110" => segment7_2 <= "0100000"; -- '6'
when "0111" => segment7_2 <= "0001111"; -- '7'
when "1000" => segment7_2 <= "0000000"; -- '8'
when "1001" => segment7_2 <= "0000100"; -- '9'
when "1010" => segment7_2 <= "0001000"; -- 'A'
when "1011" => segment7_2 <= "1100000"; -- 'B'
when "1100" => segment7_2 <= "0110001"; -- 'C'
when "1101" => segment7_2 <= "1000010"; -- 'D'
when "1110" => segment7_2 <= "0110000"; -- 'E'
when "1111" => segment7_2 <= "0111000"; -- 'F'
when others => segment7_2 <= "1111111";
end case;
rx_reg <= (others => '0');
end if;
end process;
end Behavioral;
Here is the testbench code:
library ieee;
use ieee.std_logic_1164.all;
entity tb_UART_Rx is
end tb_UART_Rx;
architecture tb of tb_UART_Rx is
component UART_Rx
port (CLK : in std_logic;
nReset : std_logic:='1';
in_Data_Rx : std_logic:='1';
Segment7_1 : out std_logic_vector (6 downto 0):= (others => '0');
Segment7_2 : out std_logic_vector (6 downto 0):= (others => '0'));
end component;
signal CLK : std_logic;
signal nReset : std_logic:='1';
signal in_Data_Rx : std_logic:='1';
signal rx_reg_counter: std_logic_vector (2 downto 0):= (others => '1');
signal Segment7_1 : std_logic_vector (6 downto 0):= (others => '0');
signal Segment7_2 : std_logic_vector (6 downto 0):= (others => '0');
constant TbPeriod : time := 20 ns;
signal TbSimEnded : std_logic := '0';
begin
dut : UART_Rx
port map (CLK => CLK,
nReset => nReset,
in_Data_Rx => in_Data_Rx,
Segment7_1 => Segment7_1,
Segment7_2 => Segment7_2);
clk_process: process
begin
CLK <= '0';
wait for TbPeriod/2;
CLK <= '1';
wait for TbPeriod/2;
end process;
stimuli : process
begin
nReset <= '0';
wait for 100 ns;
nReset <= '1';
wait for 100 ns;
in_Data_Rx <= '1';
wait for 0.104 ms;
in_Data_Rx <= '0';
wait for 0.104 ms;
in_Data_Rx <= '0';
wait for 0.104 ms;
in_Data_Rx <= '1';
wait for 0.104 ms;
in_Data_Rx <= '1';
wait for 0.104 ms;
in_Data_Rx <= '1';
wait for 0.104 ms;
in_Data_Rx <= '0';
wait for 0.104 ms;
in_Data_Rx <= '0';
wait for 0.104 ms;
in_Data_Rx <= '0';
wait for 0.104 ms;
in_Data_Rx <= '0';
wait for 0.104 ms;
in_Data_Rx <= '1';
wait;
end process;
end tb;
configuration cfg_tb_UART_Rx of tb_UART_Rx is
for tb
end for;
end cfg_tb_UART_Rx;
Here is the testbench results:
Thanks.
3 Answers 3
It helps to see UART as a clock recovery algorithm -- communication uses a single line, and clocking data is embedded with the data stream, so the receiver needs to determine the appropriate sampling points.
Each frame begins with a start bit, and ends on a stop bit. The beginning of the start bit is the only transition with a fixed position we are guaranteed; the other thing we know is that the line will go back to idle state for the stop bit at the latest (but earlier is possible, e.g. when transmitting 0xff
).
The ideal sampling point for each bit must be chosen to maximize the chance that the bit is valid. Since there is only one clock edge to synchronize to, we only synchronize the receiver clock phase at the beginning of each frame, and do not correct the frequency (a sophisticated receiver could do this though).
Assuming that the clock frequency can be wrong in either direction, and the bit time is long compared to the transition edges it makes sense to put the sampling point exactly in the middle of each bit. The clock offset will make the sampling point drift for consecutive bits, but because frames are short that does not usually matter.
So there should be 1.5 bit times between the falling edge at the beginning of the start bit and the first sampling point, then one bit time for each following bit.
For completeness, you should also verify the stop bit -- if the line is still low here, the last frame was not valid, and instead we detect a "break" state.
There are implementations allowing the stop bit to be only 0.5 bit times long, since the function of the stop bit is only to ensure that the following start bit begins with a high->low transition, and to allow the receiver to distinguish between a break and a transmission of all-zeros.
-
\$\begingroup\$ Fine answer! -- Do you really mean "Each frame [...] ends on a start bit."? Or is it the (first) stop bit? -- In 40+ years I never came across a 0.5 stop bit, only the defined lengths, 1, 1.5, 2. -- Speaking of experience, the only UART chip that synchronized on the edges in the data I found is the 68901 MFP. \$\endgroup\$the busybee– the busybee2023年10月31日 06:55:01 +00:00Commented Oct 31, 2023 at 6:55
-
\$\begingroup\$ Indeed, it ends on a stop bit. The 0.5 is really seldom, because it is annoying to do in receiver implementations. \$\endgroup\$Simon Richter– Simon Richter2023年10月31日 07:00:45 +00:00Commented Oct 31, 2023 at 7:00
From the simulation snapshot it seems you are working with Xilinx FPGA.
Xilinx offers ready VHDL/verilog codes for UART communication called the kcuart. You don't need to write a separate code for it (saves 'lot' of time). Just configure it for your UART specs and enjoy.
-
\$\begingroup\$ Thank you for information. But I only use ISE for testbench simulation. I normally use Quartus and I have Altera DE0 FPGA. In this project, UART code must belongs to me. \$\endgroup\$Griffo– Griffo2017年08月18日 11:37:37 +00:00Commented Aug 18, 2017 at 11:37
-
\$\begingroup\$ Okay. Even then I'd like you to go through that code for your reference. It might help solve your problem. Basically it is HDL code. \$\endgroup\$samjay– samjay2017年08月21日 04:12:00 +00:00Commented Aug 21, 2017 at 4:12
if(rx_first_bit <= '1' and rx_reg_counter (2 downto 0) = "110") then
counter_del_baud <= '0';
rx_start_bit <= '1';
rx_first_bit <= '0';
if(baud_pulse <= '1') then
rx_start_bit <= '0';
rx_data_bit <= '1';
counter_del_half <= '1';
end if;
All of these lines of code occur simultaneously, you have rx_start_bit driven twice as far as I am concerned whenever the if statement is true.
Also I am a novice but I am curious wherever baud_pulse <= '1'
and baud_pulse = '1'
are the same?
Cheers!
-
\$\begingroup\$ Thank you for your answer. In this part of code, I wanted to drive rx_start_bit for 1 clock bit but in test bench, rx_start_bit is driven 5 bit long. Also baud_pulse <= '1' is signal assignment operation and baud_pulse = '1' shows a statement. Thank you for remind me. \$\endgroup\$Griffo– Griffo2017年08月17日 15:12:42 +00:00Commented Aug 17, 2017 at 15:12
-
2\$\begingroup\$ @Griffo In an if-statement, <= is "less than or equal". So for a single bit, it's always true. \$\endgroup\$Blair Fonville– Blair Fonville2017年08月17日 15:40:36 +00:00Commented Aug 17, 2017 at 15:40
-
\$\begingroup\$ @BlairFonville Yep, I noticed it. I think using "end if"s wrong place causes the problem. \$\endgroup\$Griffo– Griffo2017年08月17日 15:43:45 +00:00Commented Aug 17, 2017 at 15:43
-
1\$\begingroup\$ I guess the main point I was trying to make is in in this snipper
rx_start_bit <= '0'
andrx_start_bit <= '1'
are not in statements that are mutually exclusive. So both could potentially happen, it's not a good practice to drive it that way. You could maybe find a flag that would differentiate the two ( if that's what you're looking for) \$\endgroup\$zoder– zoder2017年08月17日 16:15:18 +00:00Commented Aug 17, 2017 at 16:15 -
1\$\begingroup\$ @zoder -1 because your comments and initial concern are complete bullshit. There's nothing wrong with scheduling assignments like this in a properly written process. \$\endgroup\$DonFusili– DonFusili2018年07月30日 11:58:26 +00:00Commented Jul 30, 2018 at 11:58
Explore related questions
See similar questions with these tags.