Forum Discussion

CLa_R's avatar
CLa_R
Icon for Occasional Contributor rankOccasional Contributor
7 years ago

Transmit on UART from FSM

This is a generic problem with VHDL and no for a specific FPGA device.

For now, I use a Cyclone IV FPGA.

I have created a FSM and I would that, for each state of FSM, to transmit a char (8 bit) from my UART. The UART transmitter is defined as:

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
 
entity UART_TX is
  generic (
    g_CLKS_PER_BIT : integer := 139     -- Needs to be set correctly
    );
  port (
    i_Clk       : in  std_logic;
    i_TX_DV     : in  std_logic;
    i_TX_Byte   : in  std_logic_vector(7 downto 0);
    o_TX_Active : out std_logic;
    o_TX_Serial : out std_logic;
    o_TX_Done   : out std_logic
    );
end UART_TX;
 
 
architecture RTL of UART_TX is
 
  type t_SM_Main is (s_Idle, s_TX_Start_Bit, s_TX_Data_Bits,
                     s_TX_Stop_Bit, s_Cleanup);
  signal r_SM_Main : t_SM_Main := s_Idle;
 
  signal r_Clk_Count : integer range 0 to g_CLKS_PER_BIT-1 := 0;
  signal r_Bit_Index : integer range 0 to 7 := 0;  -- 8 Bits Total
  signal r_TX_Data   : std_logic_vector(7 downto 0) := (others => '0');
  signal r_TX_Done   : std_logic := '0';
   
begin
 
   
  p_UART_TX : process (i_Clk)
  begin
    if rising_edge(i_Clk) then
         
      case r_SM_Main is
 
        when s_Idle =>
          o_TX_Active <= '0';
          o_TX_Serial <= '1';         -- Drive Line High for Idle
          r_TX_Done   <= '0';
          r_Clk_Count <= 0;
          r_Bit_Index <= 0;
 
          if i_TX_DV = '1' then
            r_TX_Data <= i_TX_Byte;
            r_SM_Main <= s_TX_Start_Bit;
          else
            r_SM_Main <= s_Idle;
          end if;
 
           
        -- Send out Start Bit. Start bit = 0
        when s_TX_Start_Bit =>
          o_TX_Active <= '1';
          o_TX_Serial <= '0';
 
          -- Wait g_CLKS_PER_BIT-1 clock cycles for start bit to finish
          if r_Clk_Count < g_CLKS_PER_BIT-1 then
            r_Clk_Count <= r_Clk_Count + 1;
            r_SM_Main   <= s_TX_Start_Bit;
          else
            r_Clk_Count <= 0;
            r_SM_Main   <= s_TX_Data_Bits;
          end if;
 
           
        -- Wait g_CLKS_PER_BIT-1 clock cycles for data bits to finish          
        when s_TX_Data_Bits =>
          o_TX_Serial <= r_TX_Data(r_Bit_Index);
           
          if r_Clk_Count < g_CLKS_PER_BIT-1 then
            r_Clk_Count <= r_Clk_Count + 1;
            r_SM_Main   <= s_TX_Data_Bits;
          else
            r_Clk_Count <= 0;
             
            -- Check if we have sent out all bits
            if r_Bit_Index < 7 then
              r_Bit_Index <= r_Bit_Index + 1;
              r_SM_Main   <= s_TX_Data_Bits;
            else
              r_Bit_Index <= 0;
              r_SM_Main   <= s_TX_Stop_Bit;
            end if;
          end if;
 
 
        -- Send out Stop bit.  Stop bit = 1
        when s_TX_Stop_Bit =>
          o_TX_Serial <= '1';
 
          -- Wait g_CLKS_PER_BIT-1 clock cycles for Stop bit to finish
          if r_Clk_Count < g_CLKS_PER_BIT-1 then
            r_Clk_Count <= r_Clk_Count + 1;
            r_SM_Main   <= s_TX_Stop_Bit;
          else
            r_TX_Done   <= '1';
            r_Clk_Count <= 0;
            r_SM_Main   <= s_Cleanup;
          end if;
 
                   
        -- Stay here 1 clock
        when s_Cleanup =>
          o_TX_Active <= '0';
          r_TX_Done   <= '1';
          r_SM_Main   <= s_Idle;
           
             
        when others =>
          r_SM_Main <= s_Idle;
 
      end case;
    end if;
  end process p_UART_TX;
 
  o_TX_Done <= r_TX_Done;
   
end RTL;

I need to send various char (byte) trough my transmitter, One Char for one state of my FSM.

For this reason I use this code:

--[cut]
 
type t_SM_serial  is (s_Start, s_COMMAND, s_BA, s_BB, s_BC);
 
signal next_state : t_SM_Serial;
signal r_SM_serial : t_SM_serial := s_Start;
 
 -- Low-level byte-write--------------------------------------------------------------------
  procedure UART_WRITE_BYTE ( i_data_in       : in  std_logic_vector(7 downto 0)  ) is
 begin
			r_TX_BYTE  <=  i_data_in;
			r_TX_DV  <= '1';
 end UART_WRITE_BYTE;
  --------------------------------------------------------------------------------------
 
 begin --------------architecture begin
 
 -- Instantiate UART transmitter
  UART_TX_INST : uart_tx
    generic map (
      g_CLKS_PER_BIT => 139 --c_CLKS_PER_BIT: 16MHz/115200Baud
      )
    port map (
      i_clk       => Clk,	
      i_tx_dv     => r_TX_DV, 
      i_tx_byte   => r_TX_BYTE,
      o_tx_active => open,
      o_tx_serial => out_serial_memory,
      o_tx_done   => w_TX_DONE
      );
  
		
	st: process (r_SM_serial,w_TX_DONE)
	begin
		case r_SM_Serial is
			when s_COMMAND => 
				if (w_TX_Done = '1' ) then 
					next_state <= S_BA;
				end if;
			when s_BA => 
				if (w_TX_Done = '1' ) then
			        	next_state <= S_BB;
			   end if;
			when s_BB => 
				if (w_TX_Done = '1' ) then
				      next_state <= S_start;
			   end if;
			when others =>
				next_state <= s_Start;
			end case;
	end process;
		
	----------------------------------------
	--  Output Logic 
	----------------------------------------
	OUT_LOGIC: process (r_SM_Serial) 
	 begin
			case r_SM_Serial is
				when s_Command => 
					UART_WRITE_BYTE(X"41");
				when s_BA => 
					UART_WRITE_BYTE(X"42");	
				when s_BB => 
					UART_WRITE_BYTE(X"43");	
				when s_Start =>
					r_TX_DV <= '0';
				when others =>	
					r_TX_DV <= '0';
			end case;
		end process;-- OUT_LOGIC; 
		
		
		
process (Clk,MSync)
  begin
  if msync = '1' then 
		r_SM_serial <= s_command;
 
  elsif rising_edge(CLK) and r_SM_serial /= s_start then
		r_SM_Serial <= NEXT_STATE;
	end if;

This does not work. This code transmits many characters together for each state and never stop .

Where am I wrong?

3 Replies

  • Abe's avatar
    Abe
    Icon for Frequent Contributor rankFrequent Contributor

    Try out this code and see. Its a simple UART implementation..

    -- Very simple, non-buffered implementation of 8 data bits, 0 parity, 1 stop bit
    -- serial communications channel. Should be able to work at any baud (with a 
    -- degree of error) by setting I_clk_baud_count respectively:
    --
    --    For a 50MHz I_clk:
    --            I_clk_baud_count := X"1458" -- 9600bps
    --            I_clk_baud_count := X"01B2" -- 115200bps
    --
    --    To generate other timings, perform calculation:
    --          <I_clk in Hz> / <expected baud> =  I_clk_baud_count
    --              50000000  /  9600           =  5208       (0x1458)
    -- 
    -- Inputs/Outputs:
    --
    --   SYS:
    --    I_clk            - system clock - at least 16x baud rate for recieve
    --                       but can be less if only using TX.
    --    I_clk_baud_count - the number of cycles of I_clk between baud ticks
    --                       used to set a known transmit/recieve baud rate.
    --    I_reset          - reset line. ideally, reset whilst changing baud.
    --
    --   TX:
    --    I_txData   - data to transmit.
    --    I_txSig    - signal to transmit (deassert when txReady low) or
    --                 change I_txData to stream out.
    --    O_txRdy    - '1' when idle, '0' when transmitting.
    --    O_tx       - actual serial output.
    --
    --   RX:
    --    I_rx           - actual serial input.
    --    I_rxCont       - receive enable/continue.
    --    O_rxData       - data received.
    --    O_rxSig        - data available signal - does not deassert until new
    --                     frame starts being received.
    --    O_rxFrameError - Stop bit invalid/frame error. Deasserts on next receive,
    --                     but the data received may still be invalid.
    --
     
    library IEEE;
    use IEEE.STD_LOGIC_1164.ALL;
    use IEEE.NUMERIC_STD.ALL;
     
    entity uart is
    	Port ( 
    		I_clk : in  STD_LOGIC;
    		I_clk_baud_count : in STD_LOGIC_VECTOR (15 downto 0);
     
    		I_reset: in  STD_LOGIC;
     
    		I_txData : in  STD_LOGIC_VECTOR (7 downto 0);
    		I_txSig : in  STD_LOGIC;
    		O_txRdy : out  STD_LOGIC;
    		O_tx : out  STD_LOGIC;
     
    		I_rx : in STD_LOGIC;
    		I_rxCont: in STD_LOGIC;
    		O_rxData : out STD_LOGIC_VECTOR (7 downto 0);
    		O_rxSig : out STD_LOGIC;
    		O_rxFrameError : out STD_LOGIC
     
         );
    end uart;
     
    architecture Behavioral of uart is
    	signal tx_data : STD_LOGIC_VECTOR(7 downto 0) := (others => '0');
    	signal tx_state: integer := 0;
    	signal tx_rdy : STD_LOGIC := '1';
    	signal tx: STD_LOGIC := '1';
    	
    	signal rx_sample_count : integer := 0;
    	signal rx_sample_offset : integer := 3;
    	signal rx_state: integer := 0;
    	signal rx_data : STD_LOGIC_VECTOR(7 downto 0) := (others => '0');
    	signal rx_sig : STD_LOGIC := '0';
    	signal rx_frameError : STD_LOGIC := '0';
    	
    	signal rx_clk_counter : integer := 0;
    	signal rx_clk_reset : STD_LOGIC := '0';
    	signal rx_clk_baud_tick: STD_LOGIC := '0';
    	
    	signal tx_clk_counter : integer := 0;
    	signal tx_clk : STD_LOGIC := '0';
    	
    	constant OFFSET_START_BIT: integer := 7;
    	constant OFFSET_DATA_BITS: integer := 15;
    	constant OFFSET_STOP_BIT: integer := 7;
    begin
     
    	clk_gen: process (I_clk)
    	begin
    		if rising_edge(I_clk) then
    		
    			-- RX baud 'ticks' generated for sampling, with reset
    			if rx_clk_counter = 0 then
    				-- x16 sampled - so chop off 4 LSB
    				rx_clk_counter <= to_integer(unsigned(I_clk_baud_count(15 downto 4)));
    				rx_clk_baud_tick <= '1';
    			else
    				if rx_clk_reset = '1' then
    					rx_clk_counter <= to_integer(unsigned(I_clk_baud_count(15 downto 4)));
    				else
    					rx_clk_counter <= rx_clk_counter - 1;
    				end if;
    				rx_clk_baud_tick <= '0';
    			end if;
    			
    			-- TX standard baud clock, no reset
    			if tx_clk_counter = 0 then
    				-- chop off LSB to get a clock
    				tx_clk_counter <= to_integer(unsigned(I_clk_baud_count(15 downto 1)));
    				tx_clk <= not tx_clk;
    			else
    				tx_clk_counter <= tx_clk_counter - 1;
    			end if;
    		end if;
    	end process;
    	
    	O_rxFrameError <= rx_frameError;
    	O_rxSig <= rx_sig;
    	
    	rx_proc: process (I_clk, I_reset, I_rx, I_rxCont)
    	begin
    		-- RX runs off the system clock, and operates on baud 'ticks'
    		if rising_edge(I_clk) then
    			if rx_clk_reset = '1' then
    				rx_clk_reset <= '0';
    			end if;
    			if I_reset = '1' then
    				rx_state <= 0;
    				rx_sig <= '0';
    				rx_sample_count <= 0;
    				rx_sample_offset <= OFFSET_START_BIT;
    				rx_data <= X"00";
    				O_rxData <= X"00";
    			elsif I_rx = '0' and rx_state = 0 and I_rxCont = '1' then
    				-- first encounter of falling edge start
    				rx_state <= 1; -- start bit sample stage
    				rx_sample_offset <= OFFSET_START_BIT;
    				rx_sample_count <= 0;
    				
    				-- need to reset the baud tick clock to line up with the start 
    				-- bit leading edge.
    				rx_clk_reset <= '1';
    			elsif rx_clk_baud_tick = '1' and I_rx = '0' and rx_state = 1 then
    				-- inc sample count
    				rx_sample_count <= rx_sample_count + 1;
    				if rx_sample_count = rx_sample_offset then
    					-- start bit sampled, time to enable data
    					
    					rx_sig <= '0';
    					rx_state <= 2;
    					rx_data <= X"00";
    					rx_sample_offset <= OFFSET_DATA_BITS; 
    					rx_sample_count <= 0;
    				end if;
    			elsif rx_clk_baud_tick = '1' and rx_state >= 2  and rx_state < 10 then
    				-- sampling data
    				if rx_sample_count = rx_sample_offset then
    					rx_data(6 downto 0) <= rx_data(7 downto 1);
    					rx_data(7) <= I_rx;
    					rx_sample_count <= 0;
    					rx_state <= rx_state + 1;
    				else
    					rx_sample_count <= rx_sample_count + 1;
    				end if;
    			elsif rx_clk_baud_tick = '1' and rx_state = 10 then
    				if rx_sample_count = OFFSET_STOP_BIT then
    					rx_state <= 0;
    					rx_sig <= '1';
    					O_rxData <= rx_data; -- latch data out
    					
    					if I_rx = '1' then 
    						-- stop bit correct
    						rx_frameError <= '0';
    					else
    						-- stop bit is always high, if we don't see it, there 
    						-- has been an issue. Signal an error.
    						rx_frameError <= '1';
    					end if;
    				else
    					rx_sample_count <= rx_sample_count + 1;
    				end if;
    			end if;
    		end if;
    	end process;
     
     
    	O_tx <= tx;
    	O_txRdy <= tx_rdy;
    	
    	tx_proc: process (tx_clk, I_reset, I_txSig, tx_state)
    	begin
    		-- TX runs off the TX baud clock
    		if rising_edge(tx_clk) then
    			if I_reset = '1' then
    				tx_state <= 0;
    				tx_data <= X"00";
    				tx_rdy <= '1';
    				tx <= '1';
    			else
    				if tx_state = 0 and I_txSig = '1' then
    					tx_state <= 1;
    					tx_data <= I_txData;
    					tx_rdy <= '0';
    					tx <= '0'; -- start bit
    				elsif tx_state < 9 and tx_rdy = '0' then
    					tx <= tx_data(0);
    					tx_data <= '0' & tx_data (7 downto 1);
    					tx_state <= tx_state + 1;
    				elsif tx_state = 9 and tx_rdy = '0' then
    					tx <= '1'; -- stop bit
    					tx_rdy <= '1';
    					tx_state <= 0;
    				end if;
    			end if;
    		end if;
    	end process;
     
    end Behavioral;
  • CLa_R's avatar
    CLa_R
    Icon for Occasional Contributor rankOccasional Contributor

    Thanks. Have you an example for use this UART?