Electronic – UART receiver in VHDL for nexys 4 ddr board

fpgavhdl

I'm trying to program my nexys 4 ddr board to receive 1 or 0 as ASCII charachters via UART and display it on one of its 7segment display ,the LED that shows when its getting data is flashing but it won't display any character, also i'm using tera term emulator to send the characters.If any of you guys have an idea why it isn't working please let me know. Here is my code:

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


    entity seg is
    Port ( 

    i_clk       :  in STD_LOGIC;
    i_rx_serial :  in STD_LOGIC;
    anod7 : out STD_LOGIC ;
    anod6 : out STD_LOGIC ;
    anod5 : out STD_LOGIC ;
    anod4 : out STD_LOGIC ;
    anod3 : out STD_LOGIC ;
    anod2 : out STD_LOGIC ;
    anod1 : out STD_LOGIC ;
    anod0 : out STD_LOGIC ;
    ca : out  STD_LOGIC := '0';
    cb : out  STD_LOGIC := '0';
    cc : out  STD_LOGIC := '0';
    cd : out  STD_LOGIC := '0';
    ce : out  STD_LOGIC := '0';
    cf : out  STD_LOGIC := '0';
    cg : out  STD_LOGIC := '0';
    dp : out  STD_LOGIC := '0'



    );
    end seg;

    architecture Behavioral of seg is




    signal r_main      : integer range 0 to 4 := 0 ;
    --signal  i_tx_byte  : std_logic_vector( 7 downto 0 );

    signal r_rx_data   : std_logic;
    signal o_rx_dv     : std_logic;

    signal r_clk_count : integer range 0 to 10416 ;
    signal r_bit_index : integer range 0 to 7 := 0;
    signal r_rx_byte     : std_logic_vector(  7 downto 0 ) := (others =>'0' );
    signal r_rx_dv       : std_logic := '0';
    signal verif        : std_logic;
    --transmisie




    begin
    --receptor 
    anod7 <= '0';
    anod6 <= '1';
    anod5 <= '1';
    anod4 <= '1';
    anod3 <= '1';
    anod2 <= '1';
    anod1 <= '1';
    anod0 <= '1';

    process(i_clk)
    begin

    r_rx_data <= i_rx_serial;


    end process;



    process(i_clk)



    begin
    if (i_clk = '1') then

    case r_main is

    when 0 =>

    r_rx_dv <= '0';
    r_clk_count <= 0;
    r_bit_index <= 0 ;

    if r_rx_data = '0' then -- conform protocolului uart detectarea bitului 
    0 este bitul de start
    r_main <= 1;
    else
    r_main <= 0;


    end if;

    -- verificam mijlocul perioadei de un bit sa vedem daca este tot 0

    when 1 => 
     if r_clk_count = 5208 then
      if r_rx_data = '0' then
        r_clk_count <= 0 ;  
       r_main <= 2;
      else
       r_main <= 0;
      end if;
     else
     r_clk_count <= r_clk_count + 1;
     r_main <= 1;
     end if;

     when  2 =>
     if r_clk_count < 10416  then

     r_clk_count <= r_clk_count +1;
     r_main <= 2;
     else
     r_clk_count <= 0;
     r_rx_byte(r_bit_index) <= r_rx_data;
     --verificam daca s au transmis toti bitii
       if r_bit_index < 7 then
        r_bit_index <= r_bit_index + 1;
        r_main <= 2;

        else
        r_bit_index <= 0;
        r_main <= 3;    

       end if;
     end if;
     --continua sa numere counterul si verificam cand bitul de stop ajunge la final
     when 3 =>
      if r_clk_count < 10416 then
        r_clk_count <= r_clk_count + 1 ;
       r_main <= 3;
        else
        r_rx_dv <= '1';
        r_clk_count <= 0;
        r_main <= 4;

       end if;

      when 4 =>
      r_main <= 0;
      r_rx_dv <= '0';
      verif <= not verif;





      when others =>
      r_main <= 0;

      end case;
       end if;
      end process;

      process(verif)

      begin

      if r_rx_byte = "00110000" then

      ca <= '1'; 
      cb <= '0'; 
      cc <= '0'; 
      cd <= '1'; 
      ce <= '1'; 
      cf <= '1'; 
      cg <= '1'; 
      dp <= '1'; 

      elsif r_rx_byte = "01100010" then

      ca <= '0'; 
      cb <= '0'; 
      cc <= '1'; 
      cd <= '0'; 
      ce <= '0'; 
      cf <= '1'; 
      cg <= '0'; 
      dp <= '1';

        else
      ca <= '0'; 
      cb <= '0'; 
      cc <= '0'; 
      cd <= '0'; 
      ce <= '0'; 
      cf <= '0'; 
      cg <= '0'; 
      dp <= '1';
       end if;
       end process;
      end Behavioral;

Best Answer

I made two categories of changes to your design in addition to reformatting it for easier readability.

  1. RTL Synthesis - A synthesis vendor expects certain constructs to imply edge sensitive sequential logic. This affected everywhere i_clk was evaluated. Part of this including changing verif to use it as an enable for sequential logic (registers). FPGA vendors strongly discourage the use of latches which get inferred by evaluating levels. Circuitry with feed back (however convoluted) will not work properly with latches. You get complex gated (striking) oscillators. An example would be when incrementing a counter.
  2. Using a generic to define the clock period - this allows different platforms as well as allowing faster simulation without evaluating tens of billions of 100 MHz clock events.

The changes to seg (some of which are purely cosmetic):

library ieee;
use ieee.std_logic_1164.all;
-- use ieee.numeric_std.all;      -- NOT USED
-- use ieee.std_logic_arith.all;  -- NOT USED

entity seg is
    generic (           -- ADDED generic for different clock rates (simulation)
        CLKS_PER_BAUD:  natural:=   10416  -- 9600 baud at 100 MHz
    );
    port ( 
        i_clk:          in  std_logic;  -- NEXYS4 100 MHz - 9600 baud
        i_rx_serial:    in  std_logic;
        anod7:          out std_logic;
        anod6:          out std_logic;
        anod5:          out std_logic;
        anod4:          out std_logic;
        anod3:          out std_logic;
        anod2:          out std_logic;
        anod1:          out std_logic;
        anod0:          out std_logic;
        ca:             out std_logic := '0';
        cb:             out std_logic := '0';
        cc:             out std_logic := '0';
        cd:             out std_logic := '0';
        ce:             out std_logic := '0';
        cf:             out std_logic := '0';
        cg:             out std_logic := '0';
        dp:             out std_logic := '0'
    );
end entity seg;

architecture behavioral of seg is
    signal r_main:       integer range 0 to 4 := 0;
    signal r_rx_data:    std_logic;
    signal o_rx_dv:      std_logic;
    signal r_clk_count:  integer range 0 to 10416;
    signal r_bit_index:  integer range 0 to 7 := 0;
    signal r_rx_byte:    std_logic_vector(7 downto 0) := (others =>'0');
    signal r_rx_dv:      std_logic := '0';
    signal verif:        std_logic;
    -- ADDED CONSTANTS:
    constant CENTERBD:   natural := CLKS_PER_BAUD/2;   -- 5208 for 100 MHz
    constant ENDBAUD:    natural := CLKS_PER_BAUD - 1; -- 0 identity 
begin
    anod7 <= '0';
    anod6 <= '1';
    anod5 <= '1';
    anod4 <= '1';
    anod3 <= '1';
    anod2 <= '1';
    anod1 <= '1';
    anod0 <= '1';

UNLABELLED_PROCESS_1:
    process (i_clk)
    begin
        if rising_edge (i_clk) then -- ADDED, RTL register synthesis
            r_rx_data <= i_rx_serial;
        end if;                     -- ADDED
    end process;

UNLABELLED_PROCESS_2:
    process (i_clk)
    begin
        -- if i_clk = '1' then
        if rising_edge (i_clk) then    -- CHANGED RTL register synthesis
            verif <= '0';              -- ADDED, use register not latch
            case r_main is
                when 0 =>
                    r_rx_dv <= '0';
                    r_clk_count <= 0;
                    r_bit_index <= 0;
                        if r_rx_data = '0' then 
    -- conform protocolului uart detectarea bitului 
    -- 0 este bitul de start  -- SHOULD BE (A | PART OF) COMMENT
    -- according to the bit detection protocol 0 is the start bit
                            r_main <= 1;
                        else
                            r_main <= 0;
                        end if;
    -- verificam mijlocul perioadei de un bit sa vedem daca este tot 0
    -- we check the middle of the one-bit period to see if it's still 0
                when 1 => 
                    if r_clk_count = CENTERBD then  -- CHANGED TO CONSTANT
                        if r_rx_data = '0' then
                            r_clk_count <= 0;  
                            r_main <= 2;
                        else
                            r_main <= 0;
                        end if;
                    else
                        r_clk_count <= r_clk_count + 1;
                        r_main <= 1;
                    end if;
                when  2 =>
                    if r_clk_count < ENDBAUD  then  -- CHANGED TO CONSTANT
                        r_clk_count <= r_clk_count + 1;
                        r_main <= 2;
                    else
                        r_clk_count <= 0;
                        r_rx_byte(r_bit_index) <= r_rx_data;
    -- verificam daca s au transmis toti bitii
    -- we check if all the bits have been transmitted
                        if r_bit_index < 7 then
                            r_bit_index <= r_bit_index + 1;
                            r_main <= 2;
                        else
                            r_bit_index <= 0;
                            r_main <= 3;    
                        end if;
                    end if;
    -- continua sa numere counterul si verificam cand bitul de stop ajunge la final
    -- continue to count the counter and check when the stop bit reaches the end
                when 3 =>
                    if r_clk_count < ENDBAUD then  -- CHANGED TO CONSTANT
                        r_clk_count <= r_clk_count + 1;
                        r_main <= 3;
                    else
                        r_rx_dv <= '1';
                        r_clk_count <= 0;
                        r_main <= 4;
                    end if;
                when 4 =>
                    r_main <= 0;
                    r_rx_dv <= '0';
                    -- verif <= not verif;  -- use register not latches for
                                            -- cathodes
                    verif <= '1';   -- '1' duration one clock
                when others =>
                    r_main <= 0;
            end case;
        end if;
    end process;

UNLABELLED_PPROCESS_3:
    -- process (verif)   CHANGED, RTL registers not latches
    process (i_clk)    -- SENSITIVITY LIST CHANGED
    begin
        if rising_edge (i_clk) then  -- ADDED
            if verif = '1' then      -- ADDED ENABLE
                if r_rx_byte = "00110000" then
                    ca <= '1'; 
                    cb <= '0'; 
                    cc <= '0'; 
                    cd <= '1'; 
                    ce <= '1'; 
                    cf <= '1'; 
                    cg <= '1'; 
                    dp <= '1'; 
                elsif r_rx_byte = "01100010" then
                    ca <= '0'; 
                    cb <= '0'; 
                    cc <= '1'; 
                    cd <= '0'; 
                    ce <= '0'; 
                    cf <= '1'; 
                    cg <= '0'; 
                    dp <= '1';
                else
                    ca <= '0'; 
                    cb <= '0'; 
                    cc <= '0'; 
                    cd <= '0'; 
                    ce <= '0'; 
                    cf <= '0'; 
                    cg <= '0'; 
                    dp <= '1';
                end if;
            end if;   -- ADDED
        end if;       -- ADDED
    end process;
end architecture behavioral;

In your source code there was part of a comment that was on it's own line although not preceded by "--".

Now testing it the modified circuit would either require a Nexys4 board (or other by setting the generic CLKS_PER_BAUD) or simulation.

Fortuitously having previously writtent a simple UART transmit and it's associated testbench, these were adapted to interface to your design:

library ieee;
use ieee.std_logic_1164.all;

entity uart_tx is
    port (
        reset:  in  std_logic;
        load:   in  std_logic;
        clk:    in  std_logic;  -- baud clock
        din:    in  std_logic_vector(7 downto 0);              
        tx:     out std_logic;  -- serial out
        ready:  out std_logic   -- ready to load
    );
end entity;

architecture foo  of uart_tx is
    signal shift_reg:   std_logic_vector (9 downto 0); -- NO PARITY
    signal done:        std_logic;
begin

    tx <= shift_reg(0) or done;
    ready <= done;

shift_register:    -- the left shift in signals done  
    process (reset, clk)
        variable empty: std_logic;
    begin 
        if reset = '1' then 
            shift_reg <= (others => '0');
            done <= '1';  
        elsif rising_edge(clk) then
            if load = '1' then
                shift_reg <= '1' & din & '0';
                done <= '0';
            else
                if shift_reg = "0000000001" then
                    done <= '1';
                end if;
                shift_reg <= '0' & shift_reg(9 downto 1);
            end if;
        end if;
    end process;
end architecture;

library ieee;
use ieee.std_logic_1164.all;

entity seg_tb is
end entity;

architecture foo of seg_tb is
    signal clk:             std_logic := '0';  -- 16 X BAUD CLOCK
    signal bdclk:           std_logic := '0';  -- TX BAUD CLOCK
    signal reset:           std_logic := '1';  -- TX UART STARTS IN RESET
    signal load:            std_logic := '0';
    signal din:             std_logic_vector(7 downto 0);              
    signal tx:              std_logic;
    signal ready:           std_logic;
    type slv_byte_array is  array (0 to 3) of std_logic_vector (7 downto 0);
    constant message:    
        slv_byte_array := (x"20", "00110000", x"20", "01100010");
    -- DUT outputs:
    signal anod7:           std_logic;
    signal anod6:           std_logic;
    signal anod5:           std_logic;
    signal anod4:           std_logic;
    signal anod3:           std_logic;
    signal anod2:           std_logic;
    signal anod1:           std_logic;
    signal anod0:           std_logic;
    signal ca:              std_logic;
    signal cb:              std_logic;
    signal cc:              std_logic;
    signal cd:              std_logic;
    signal ce:              std_logic;
    signal cf:              std_logic;
    signal cg:              std_logic;
    signal dp:              std_logic;
begin

UARTTX:
    entity work.uart_tx
    port map (
        reset => reset,
        load => load,
        clk => bdclk,
        din => din,
        tx => tx,
        ready => ready
    );
RST:
    process
    begin
        wait until rising_edge (clk);
        wait until rising_edge (clk);
        reset <= '0';
        wait;
    end process;
CLOCK:
    process
    begin
        wait for 52.35 us/16;  -- 9600 baud 16X clock period
        clk <= not clk;
        if now > 12 ms then
            wait;
        end if;
    end process;
BAUD_CLOCK:
    process (clk)
        variable counter16: natural range 0 to 15;
    begin
        if rising_edge (clk) then
            if counter16 = 15 then
                counter16 := 0;
            else
                counter16 := counter16 + 1;
            end if;
            if counter16 = 0 or counter16 = 15 then
                bdclk <= not bdclk;  -- square wave
            end if;
        end if;
    end process;

STIMULI:
    process
    begin
        wait until reset = '0';
        wait until rising_edge(bdclk);
        for i in 0 to message'length - 1 loop
            load <= '1';
            din <= message(i);
            wait until rising_edge(bdclk);
            load <= '0';
            wait until rising_edge(ready);
        end loop;
        wait;
    end process;
DUT:
    entity work.seg
        generic map (CLKS_PER_BAUD => 16)  -- as slow as possible for simulation
        port map (
        i_clk => clk,
        i_rx_serial => tx,
        anod7 => anod7,
        anod6 => anod6,
        anod5 => anod5,
        anod4 => anod4,
        anod3 => anod3,
        anod2 => anod2,
        anod1 => anod1,
        anod0 => anod0,
        ca => ca,
        cb => cb,
        cc => cc,
        cd => cd,
        ce => ce,
        cf => cf,
        cg => cg,
        dp => dp
        );
end architecture;

The UART TX was modified to eliminate a parity bit (it's a simple, dedicated purpose design, using a baud rate clock).

When simulated the testbench demonstrates your design is functional:

seg_tb testbench jpeg

Because your design has been modified to provide recognizable sequential logic constructs for RTL synthesis it should synthesize. It should be functional as long as the CLKS_PER_BAUD is the default value for the 100 MHz NEXYS4 board clock.

Your design should be functional using a clock as slow as 16 times the baud rate.

ISE and Vivado Documentation

Artix-7 devices from Xilinx such as that used on the Nexys4 board are supported by both ISE and Vivado.

For ISE the form of RTL description in VHDL to infer register based design can be found in Synthesis and Simulation Design Guide, UG626 (v 14.4) December 18, 2012, Ch. 5 Coding for the FPGA Design Flow, Registers in FPGA Design. Also see Latches in FPGA Design "Xilinx® recommends that you avoid using latches in FPGA designs, due to the more difficult timing analyses that take place when latches are used."

For Vivado Vivado Design Suite User GuideSynthesis UG901 (v2018.3) December 19, 2018, Ch. 4 HDL Coding Techniques, Flip-Flops, Registers and Latches, Flip-Flops and Registers Coding Examples. Also see Latches, "Inferred Latches are often the result of HDL coding mistakes, such as incomplete if or case statements." and "Vivado synthesis issues a warning for the instance shown in the following reporting example. This warning lets you verify that the inferred Latch functionality was intended."

The shown examples are a subset of those found in IEEE Std 1076.6-2004 IEEE Standard for VHDL Register Transfer Level (RTL) Synthesis (now withdrawn), Section 5. Modeling Hardware Elements, 6.1 Edge-sensitive sequential logic which are all likely (still) supported if not shown in Xilinx documents. You'd also find 6.2 Level-sensitive sequential logic, for latches.

Related Topic