Search code examples
vhdlstate-machinehardware

State Machine with VHDL for UA(R)T


I am trying to create a state machine in vhdl for UA(R)T (Only the sending portion).

I am having an issue with the flow of the program. I know the buad rate portion does not work at the moment. I am trying to get it working with just a clock at the moment, and then will implement the baud rate divider.

When I run it through my test bench (nothing complicated, just assign a couple of initial values reset = 1 for x time, din = z, baud = y, etc), nothing happens. My output txd stays at the initial '1' value that is set in the reset stage and if I set it to '0' it will stay like that for the cycles.

My issue that I had when designing the state machine is the it has two values that it will transition on BUT not in ever state.

Basically, what it is supposed to do is: reset: txd = 1, count = 1, busy = 0, we = 0 idle: when busy = 1 set shift = init values wait: transition on next clock signal trans: if count < 9, txd = shift(0), and shift shift if count = 9, busy = 0, count = 0 and back to idle

I think my issue is somehow related to the busy signal not being properly set.

-- Universal Asynch Receiver Transmitter
---------------------
library ieee;
use ieee.std_logic_1164.all;

entity eds_uart is
   generic (width : positive := 16);
   port ( clk,reset: in std_logic ;
     din_wen: buffer std_logic; -- state machine sets value thus buffer needed
     brd : in std_logic_vector(23 downto 0); -- buad rate dividor
     din : in std_logic_vector(7 downto 0); -- input value
     txd: out std_logic; -- sent data bit
     tx_busy : buffer std_logic -- sent data bit active
     );
end entity eds_uart;

architecture behaviour of eds_uart is
    type state_type is (idle_s, wait_s, transmit_s);  -- three possible states of uat
    signal current_s: state_type; 
    signal tick: std_logic; -- baud rate clock
    signal count: integer := 0; -- count number of characters sent
    signal shift: std_logic_vector(9 downto 0); -- intermediate vector to be shifted

begin
   -- assign tick value based on baud rate 
   -- need to implement divisor
   process(clk, brd) begin
      tick <= clk;
   end process; 

   process(tick, reset, din) begin 
       if (reset = '1') then
           current_s <= idle_s; -- default state
           count <= 0; -- reset character counter
           txd <= '1'; 
           tx_busy <= '0'; 
           din_wen <= '0'; -- able to start sending
       elsif (current_s = idle_s and din_wen = '1') then -- transition when write enable is high
           current_s <= wait_s; -- transition
           tx_busy <= '1';  
           shift <= '1' & din & '0'; -- init shift value
       elsif (current_s = wait_s and rising_edge(tick)) then -- transition on clock signal
            current_s <= transmit_s;
       elsif (current_s = transmit_s and rising_edge(tick)) then -- test transition on clock signal
            if (count < 9) then
                txd <= shift(0); -- output value
                shift <= '0' & shift(9 downto 1); -- shift to next value
                count <= count + 1; -- increment counter
                current_s <= transmit_s; -- dont change state
            elsif (count = 9) then 
                txd <= shift(0); -- send last element
                count <= 0;
                tx_busy <= '0'; -- reset busy signal
                current_s <= idle_s; -- start process again
           end if;
       end if;
   end process;
end architecture behaviour ;

Solution

  • The comments:

    -- state machine sets value thus buffer needed
    

    and

    -- transition when write enable is high
    

    suggest that you may be expecting to have an additional external driver for din_wen. If that is the case the buffer mode is not doing you any good as it only exposes the value of the internal driver of din_wen which is only ever driving '0'. Post VHDL-2002, buffer is effectively a fancy, readable version of out without the limitations from earlier standards. It does not implement an input port. More significantly, it does not let you see the external resolved value if you have additional signal driver(s) outside this entity.

    It isn't clear why you even need to drive din_wen internally since it is intended to be a control input that causes the transition into the wait_s state. Consider changing it to an in port mode and removing the reset assignment.

    Style note: You are courting danger with the mixture of synchronous and asynchronous logic described here. You should stick to the pattern of having a single call to rising_edge() in a top level if block that wraps all of your synchronous logic.