• 0

UART Transmitter with BASYS 3


Question

I want to send 8 bit data from FPGA to PC, 9600 baudrate, 8 bit data, 1 start&stop bit, no parity. I did coded my Basys3 Fpga and connected to PC. By using Tera Term, wanted to see how it works out. But probably something big I'm missing out. I just wrote a transmitter code and somewhere I saw that some people used button&top modules too. Do I need them to see a 8-bit data's ASCII equivalent on my PC? How can I handle? 
 

library ieee;
use ieee.std_logic_1164.all;


entity rs232_omo is
generic(clk_max:integer:=10400); --for baudrate
                                      

port(

clk : in std_logic;
rst : in std_logic;
start : in std_logic;
input : in std_logic_vector(7 downto 0);
done : out std_logic;
output : out std_logic;
showstates: out std_logic_vector(3 downto 0)

);
end entity;

architecture dataflow of rs232_omo is

type states is (idle_state,start_state,send_state,stop_state);
signal present_state,next_state : states;
signal data,data_next : std_logic;


begin


process(clk,rst)
variable count : integer range 0 to clk_max;
variable index : integer range 0 to 10;
begin
        
    if rst='1' then
        present_state<=idle_state;
        count:=0;
        data<='1';
        
        
    elsif rising_edge(clk) then
        
        present_state<=next_state;
        count:=count+1;
        index:=index+1;
        data<=data_next;

    end if;

end process;

process(present_state,data,clk,rst,start)
variable count : integer range 0 to clk_max;
variable index : integer range 0 to 10;
begin

done<='0';
data_next<='1';

    case present_state is
    
        when idle_state =>
            showstates<="1000";
            data_next<='1';
            
            if start='1' and rst='0' then
                count:=count+1;
                if count=clk_max then
                    next_state<=start_state;
                    count:=0;
                end if;    
            end if;
            
        when start_state =>
            showstates<="0100";
            data_next<='0';
           
            count:=count+1;
            if count=clk_max then
                next_state<=send_state;
                count:=0;
            end if;
        
        when send_state =>
            showstates<="0010";
            count:=count+1;
            data_next<=input(index);
            
            if count=clk_max then
                if index=7 then
                    index:=0;
                    next_state<=stop_state;
                else
                    index:=index+1;
                end if;
            count:=0;
            end if;

        when stop_state =>
            showstates<="0001";
            count:=count+1;
            if count=clk_max then
            next_state<=idle_state;
            done<='1';
            count:=0;
            end if;
            
end case;

end process;
output<=data;

end architecture;

Constraints:

set_property PACKAGE_PIN V17 [get_ports {input[0]}]
set_property PACKAGE_PIN V16 [get_ports {input[1]}]
set_property PACKAGE_PIN W16 [get_ports {input[2]}]
set_property PACKAGE_PIN W17 [get_ports {input[3]}]
set_property PACKAGE_PIN W15 [get_ports {input[4]}]
set_property PACKAGE_PIN V15 [get_ports {input[5]}]
set_property PACKAGE_PIN V14 [get_ports {input[6]}]
set_property PACKAGE_PIN W13 [get_ports {input[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports {input[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports {input[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {input[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {input[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {input[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {input[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {input[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {input[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {showstates[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {showstates[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {showstates[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {showstates[0]}]
set_property PACKAGE_PIN L1 [get_ports {showstates[3]}]
set_property PACKAGE_PIN P1 [get_ports {showstates[2]}]
set_property PACKAGE_PIN N3 [get_ports {showstates[1]}]
set_property PACKAGE_PIN P3 [get_ports {showstates[0]}]
set_property PACKAGE_PIN W5 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports clk]
set_property PACKAGE_PIN R2 [get_ports rst]
set_property PACKAGE_PIN T1 [get_ports start]
set_property IOSTANDARD LVCMOS33 [get_ports start]
set_property IOSTANDARD LVCMOS33 [get_ports rst]
set_property IOSTANDARD LVCMOS33 [get_ports done]
set_property IOSTANDARD LVCMOS33 [get_ports output]

set_property PACKAGE_PIN V3 [get_ports done]
set_property PACKAGE_PIN V13 [get_ports output]

My testbench simulation got attached. 

And on-board, apparently I stuck with 'idle_state'. 

For any kind of help, I thank y'all in advance.

 

 

IMG_6039.JPG

rs232.jpg

Edited by huytergan
Link to post
Share on other sites

10 answers to this question

Recommended Posts

  • 0

I haven't looks at the code too much, but I did notice that you don't appear to have synchronizers on your external connections that control the state machine (start and reset). 

Although this is most likely not your problem it will cause inconsistent behaviour.

Are you also sure you haven't got the Tx/Rx pins back to front, as naming depends on the designer perspective and whims on the day 

Link to post
Share on other sites
  • 0

I wrote a quick test bench. I couldn''t get it out of 'idle' state because of this code:

 

	if start='1' and rst='0' then
    	count:=count+1;
    	if count=clk_max then
            next_state<=start_state;
            count:=0;
        end if;    
    end if;

I asserted 'start' for only one cycle, and nothing happened. I see now that I need to assert it for up to 10,400 cycles before it will send data. Will let you know how testing progresses.

Also, the async reset is a slightly unusual design pattern for FPGA designs. Synchronous resets (with a properly synchronised reset signal) work better.

Here is the test bench so far.

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity tb is
end tb;

architecture Behavioral of tb is
    component rs232_omo is
        generic(clk_max:integer:=10400); --for baudrate
        port(
            clk        : in std_logic;
            rst        : in std_logic;
            start      : in std_logic;
            input      : in std_logic_vector(7 downto 0);
            done       : out std_logic;
            output     : out std_logic;
            showstates : out std_logic_vector(3 downto 0)
        );
    end component ;

    signal clk        : std_logic;
    signal rst        : std_logic := '0';
    signal start      : std_logic := '0';
    signal input      : std_logic_vector(7 downto 0) := x"55";
    signal done       : std_logic;
    signal output     : std_logic;
    signal showstates : std_logic_vector(3 downto 0);

begin

process
    begin
        clk <= '0';
        wait for 5ns;
        clk <= '1';
        wait for 5ns;
    end process;

process
    begin
        wait for 100ns;
        rst  <= '1';
        wait for 20ns;
        rst  <= '0';
        wait for 100ns;
        start <= '1';
        wait for 10ns;
        start <= '0';
        wait;
    end process;
    
uut: rs232_omo generic map (clk_max => 10400)
        port map(
            clk    => clk,
            rst    => rst,
            start  => start,
            input  => input,
            done   => done,
            output => output,
            showstates => showstates
        );

end Behavioral;

 

Link to post
Share on other sites
  • 0

Hi again,

Changed the XDC file, and to run it on a board - it didn't work for me either.

There are a few synthesis issues that need to be addressed (both of which were pointed out as warning in the logs, BTW):

1. input needs to be added on this process's sensitivity list:

         process(present_state,data,clk,rst,start, input)

2. The major problem - you are updating count and index_reg inside the async process:

                count:=count+1;

and 

                index:=index+1;

You can't do this, as conceptually the process is "run" each time any of the input signals change state. If you want to work with the similar structure you need to assign count_next and index_next inside the async process, then assign them to count and index in the clocked one.

Oh, and another tiny issue - if you want to do something every 10400 cycles, you need to count from 0 to 10399.

Edited by hamster
Link to post
Share on other sites
  • 0
6 hours ago, hamster said:

Hi again,

Changed the XDC file, and to run it on a board - it didn't work for me either.

There are a few synthesis issues that need to be addressed (both of which were pointed out as warning in the logs, BTW):

1. input needs to be added on this process's sensitivity list:

         process(present_state,data,clk,rst,start, input)

2. The major problem - you are updating count and index_reg inside the async process:

                count:=count+1;

and 

                index:=index+1;

You can't do this, as conceptually the process is "run" each time any of the input signals change state. If you want to work with the similar structure you need to assign count_next and index_next inside the async process, then assign them to count and index in the clocked one.

Oh, and another tiny issue - if you want to do something every 10400 cycles, you need to count from 0 to 10399.

Hi, Thanks for your interest, yes I realized that I did let them increase in first process which shouldn't have been. I deleted both count&index increment over there. Working well inside of simulation. But still nothing on-board. I share my testbench code with you, take a look please.

 

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity rs232omo_tb is
--  Port ( );
end rs232omo_tb;

architecture Behavioral of rs232omo_tb is

signal clk :  std_logic;
signal rst :  std_logic;
signal start :  std_logic;
signal input :  std_logic_vector(7 downto 0);
signal done :  std_logic;
signal output :  std_logic;
signal showstates : std_logic_vector(3 downto 0);

component rs232_omo is
port(
clk : in std_logic;
rst : in std_logic;
start : in std_logic;
input : in std_logic_vector(7 downto 0);
done : out std_logic;
output : out std_logic;
showstates: out std_logic_vector(3 downto 0)


);
end component;

begin

uut:rs232_omo port map(

clk=>clk,
rst=>rst,
start=>start,
done=>done,
output=>output,
input=>input,
showstates=>showstates

);

input<="00001111";
process
begin
rst<='1';
start<='0';
wait for 10 ns;
rst<='0';
start<='1';
wait;

end process;



process
begin
if clk/='0' then
clk<='0';
else
clk<='1';
end if;
wait for 10 ns;
end process;

end Behavioral;

 

**What I wonder is that Do I need a button trigger to send my 'Input' data to PC? This is the thing I think -> whatever I set my switches as I like them to be, "Tera Term" terminal should show the input's equivalent. Am I thinking wrong here?

Edited by huytergan
Link to post
Share on other sites
  • 0
6 hours ago, hamster said:

Hi again,

Changed the XDC file, and to run it on a board - it didn't work for me either.

There are a few synthesis issues that need to be addressed (both of which were pointed out as warning in the logs, BTW):

1. input needs to be added on this process's sensitivity list:

         process(present_state,data,clk,rst,start, input)

2. The major problem - you are updating count and index_reg inside the async process:

                count:=count+1;

and 

                index:=index+1;

You can't do this, as conceptually the process is "run" each time any of the input signals change state. If you want to work with the similar structure you need to assign count_next and index_next inside the async process, then assign them to count and index in the clocked one.

Oh, and another tiny issue - if you want to do something every 10400 cycles, you need to count from 0 to 10399.

Hi again, It's official now that is working on-board as well:D Thanks! But I don't get it why I needed count_next and index_next signals despite variable count and index. 

Link to post
Share on other sites
  • 0

As a gross oversimplification,  FPGAs only have clocked flipflops that can store state information.

You can use variables to store state rather than signals, but they have to be used in such a way that they can be mapped down to flipflops. 

Without using a clock edge in the HDL code this can't happen, and all sorts of weird stuff happens.

Link to post
Share on other sites
  • 0
4 hours ago, hamster said:

As a gross oversimplification,  FPGAs only have clocked flipflops that can store state information

I suppose that the first part of this sentence is a pass for the second part but I still had to wince at reading it.

You can have storage using combinational (un-clocked)  logic. If a synthesis tool decides that your code requires storage it will infer a latch; and provide a warning that it did so. The warning indicates that inferred latches are generally bad. One problem with learning your first HDL on your own from a book is that you might be tempted to use keywords without understanding the ramifications of your choices.

There is a place for variables and := in VHDL, but not for assigning values to logic elements like std_logic or std_logic_vector. A problem with offering a critique of code in a venue like the forum is that it's easy to provide advice that, while addressing an aspect of poor coding might cause more confusion than solution. There's only so much space and time for posting coding critiques and too often an attempt at making it concise and easy it ends up being misleading.

My point isn't to criticize hamster or anyone posting code; it's to suggest that there are better ways to learn how to use and HDL effectively than this format provides.

Edited by zygot
Link to post
Share on other sites
  • 0

Hi,

within the w11 project you'll find among many other things

The UART features

It supports up to 12 MBaud, and is in fact usually used with 12 MBaud.
In test benches it allows even 120 MBaud, which speeds to the simulation time of full systems.
It is usually used with FIFO buffering and separate system and serial clock domains (see  serport_2clock2.vhd).
It can of course be used in a simple single clock domain setup (see serport_1clock.vhd).

Cheers,   Walter

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now