0
votes

I am trying to build a generic baud rate generator process for a uart transmitter.

The transmitter works fine if I ignore the baud rate divider and pass in the clk signal in the sensitivity list. But I get errors (describe in code comments) if I try to implement the divider. I tried two different methods and both either gave an error, or did not have the expected output. Yes, the exact code posted will not work since I assign fbaud twice, I comment one out to test.

Perhaps I don't understand how the baud generator is supposed to work. From my understanding, the fpga clock runs at 50mHz which is to fast for the rs232 communication. So we need to wait a certain number of clock cycles before we can transmit our character.

In this case, we have a variable baud so we divide the stock clock by the baud generator to get the number of clock cycles we need to wait before sending our 'tick' signal to the transmit state machine.

The baud divider is set in the test bench to x"000008".


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

entity eds_uart is
   generic (width : positive := 16);
   port ( clk,reset: in std_logic ;
     din_wen: in 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 := '0'; -- 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
    signal fbaud: integer := 0;
    signal fbaud_counter: integer := 0;

begin
   --- process that is causing the issue. 
   process(clk, brd) begin
      fbaud <= (50000000)/to_integer(signed(brd)); -- 50,000,000 is the default clock Hz
      ------ error message  ------
      --# ** Warning: NUMERIC_STD.TO_INTEGER: metavalue detected, returning 0
      -- # Time: 0 ns  Iteration: 0  Instance: /eds_uart_tb/inst_uart
      -- # ** Fatal: (SIGFPE) Floating point exception.
      --#    Time: 0 ns  Iteration: 0  Process: /eds_uart_tb/inst_uart/line__29 File: 

      fbaud <= 50000;
      --- error ---
      -- this command simply does not work, it compiles and runs though
      -- I don't get any transitions in my output wave
      -- I don't think it is entering the transmit stage based on a clock signal

      if (rising_edge(clk)) then
         if (fbaud_counter = fbaud) then -- tick when proper number of counts have appeared
             tick <= '1';
         elsif (fbaud_counter < fbaud) then
             tick <= '0';
             fbaud_counter <= fbaud_counter + 1;
         end if;
      end if;
   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'; 
       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
       end if;
       if(rising_edge(tick)) then
          if (current_s = wait_s) then -- transition on clock signal
               current_s <= transmit_s;
          elsif (current_s = transmit_s) then -- 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 if;
   end process;
end architecture behaviour ;
2
Please show us your test bench as well. The metavalue detected warning typically means that your brd input is not being driven properly from the testbench (at least not at time 0). Additionally, it is considered bad practice to generate a clock this way because meeting timing will more difficult (not using clock routing) - look at using clock enables or an MMCM/PLL. You also seem to be using both asyncronous sets and resets on your state, tx_busy, and shift flops. If this even allowed for synthesis, it is also considered bad practice because it does not match the way the hardware works.QuantumRipple

2 Answers

1
votes

There are a few potential problems with this code, but what seems to be causing your failure is that you're declaring fbaud_counter as an integer but with no range limit specified, and more critically, you're not clearing it when it reached your fbaud count value. Since you never reset the value after the count is reached, it will continue counting through all 2^32 values before it wraps around and matches fbaud again. A range limit is probably a good idea anyway, but either way, if you don't reset it, your baud rate will not be correct. For instance:

if (rising_edge(clk)) then
  if (fbaud_counter = fbaud - 1) then
    tick <= '1';
    fbaud_counter <= 0;   
  else
    tick <= '0';
    fbaud_counter <= fbaud_counter + 1;
  end if;
end if;

Note that there's really no need for an elsif condition there, as there really isn't a condition where you don't otherwise want to set tick to '0' or increment your count. Note that I'm also counting to fbaud - 1 - If you start at 0, counting all the way to fbaud may put your rate off very slightly.

edit

I simulated the above with the changes I recommended to the process, and I got a response on txd (as well as a steady tick). Are you sure you're simulating long enough? Assuming your clock is set up to be 50 MHz as your code indicates it should be, setting brd to x"000008" will give you an incredibly slow tick rate (8 Hz, oddly enough). I reduced the numerator to 5000 just to speed things up a bit.

I also realize I didn't cover your floating point exception (thanks for pointing that out).

The warning is a hint as to the error. "metavalue detected, returning 0". The warning indicates to_integer is trying to convert a value that it can't resolve to a 1 or 0 (e.g. 'X', or another such std_logic value), and of course you can't divide by 0. This is most likely an initialization problem (note that your fatal error says Time: 0 ns Iteration: 0). In your testbench, how is brd driven initially? Can you give it a default value? If not, you will need to guard against this condition some other way.

0
votes

When you simulate it it sounds like your brd input is not initialized, so the Us are turning into 0s and you are dividing by zero - hence the exception.

Also, this will synthesise to something very big, resource-wise:

fbaud <= (50000000)/to_integer(signed(brd));

You are asking for this to be calculated every clock cycle, which is a big ask from the hardware.

The usual method is to accept a terminal count (your fbaud) as the input and let the controlling software figure out what the value should be. Or calculate it at compile time as a constant, depending on how flexible you need to be.