Go Board – UART (Universal Asynchronous Receiver/Transmitter)

Learn to Communicate with the Computer (Part 2 – Transmitter)

In the previous project, you learned how to receive data from the computer and display the received byte on the two digit 7-Segment display. You saw that the data being displayed was showing the ASCII hex characters of the keyboard presses being transmitted from the Computer to the Go Board. In this project, we are going to implement the UART Transmitter, so that the computer can receive data from the Go Board.

Picture of Go Board UART Project

UART Block Diagram between Computer and Go Board

The picture above shows the path that we completed for Part 1, and the path that we are now going to be designing in Part 2. Part 2 should be much easier, because now you know how a UART actually works. This project is going to take parallel data (as a byte) and serialize it. The serialized data gets sent out one bit at a time to the computer. The computer will then display the received byte on the terminal emulator screen.

Should you prefer to follow along with a YouTube video, well you can do that too! Please subscribe to my channel.

Project Description

Create a loopback of data sent by the computer. The Go Board should receive data from the computer, display it on the two digit 7-Segment display, and then transmit the received data back to the computer. The UART receiver should operate at 115200 baud, 8 data bits, no parity, 1 stop bit, no flow control.

As a refresher, let’s look at how this UART data is formatted for both the receiver and the transmitter:

UART Serial Data Stream

The project description states that we will be using loopback. Loopback is the routing of the data stream back to its original source without modification. In this case, the transmitter will echo the received characters back to the computer. This allows the keys that you press to be displayed on the terminal screen. If you were paying close attention during the previous project, you might have noticed that the keys you were pressing were not being displayed on the screen. That’s because the Go Board was not echoing them back to the Computer. This project is going to fix that.

The transmitter uses a state machine to progress through the data stream. First the start bit, then the data bits, then the stop bit. You need to keep track of where you are both in the individual bit, and where you are in the data stream as a whole. The state machine will help with that.

I recommend that you give this project a try on your own. You have the skills from the UART Receiver, so this one shouldn’t be too bad.

Note that this UART Transmitter is designed to be portable. Feel free to use it in any design you wish! Please give credit to www.nandland.com if you choose to use it! For the EDA Playground environment, I used the UART_RX module from the previous project and created the Transmitter from scratch. The Testbench instantiates both the TX and RX and simply performs a loopback of the received data into the transmit module. Take a look:

UART Transmitter and Testbench in VHDL

UART Transmitter and Testbench in Verilog



This testbench environment performs very similarly to the previous project. Run the simulation and look at the waveforms. Once you’re comfortable with your UART Transmitter design, you can instantiate it into the top level of your project. The code for both VHDL and Verilog is shown below.

VHDL Code – UART_Loopback_Top.vhd:

library ieee;
use ieee.std_logic_1164.all;

entity UART_Loopback_Top is
  port (
    -- Main Clock (25 MHz)
    i_Clk         : in std_logic;

    -- UART Data
    i_UART_RX : in  std_logic;
    o_UART_TX : out std_logic;
	
    -- Segment1 is upper digit, Segment2 is lower digit
    o_Segment1_A  : out std_logic;
    o_Segment1_B  : out std_logic;
    o_Segment1_C  : out std_logic;
    o_Segment1_D  : out std_logic;
    o_Segment1_E  : out std_logic;
    o_Segment1_F  : out std_logic;
    o_Segment1_G  : out std_logic;
    
    o_Segment2_A  : out std_logic;
    o_Segment2_B  : out std_logic;
    o_Segment2_C  : out std_logic;
    o_Segment2_D  : out std_logic;
    o_Segment2_E  : out std_logic;
    o_Segment2_F  : out std_logic;
    o_Segment2_G  : out std_logic
    );
end entity UART_Loopback_Top;

architecture RTL of UART_Loopback_Top is

  signal w_RX_DV     : std_logic;
  signal w_RX_Byte   : std_logic_vector(7 downto 0);
  signal w_TX_Active : std_logic;
  signal w_TX_Serial : std_logic;
  
  signal w_Segment1_A, w_Segment2_A : std_logic;
  signal w_Segment1_B, w_Segment2_B : std_logic;
  signal w_Segment1_C, w_Segment2_C : std_logic;
  signal w_Segment1_D, w_Segment2_D : std_logic;
  signal w_Segment1_E, w_Segment2_E : std_logic;
  signal w_Segment1_F, w_Segment2_F : std_logic;
  signal w_Segment1_G, w_Segment2_G : std_logic;

begin

  UART_RX_Inst : entity work.UART_RX
    generic map (
      g_CLKS_PER_BIT => 217)            -- 25,000,000 / 115,200
    port map (
      i_Clk       => i_Clk,
      i_RX_Serial => i_UART_RX,
      o_RX_DV     => w_RX_DV,
      o_RX_Byte   => w_RX_Byte);


  -- Creates a simple loopback to test TX and RX
  UART_TX_Inst : entity work.UART_TX
    generic map (
      g_CLKS_PER_BIT => 217)               -- 25,000,000 / 115,200 = 217
    port map (
      i_Clk       => i_Clk,
      i_TX_DV     => w_RX_DV,
      i_TX_Byte   => w_RX_Byte,
      o_TX_Active => w_TX_Active,
      o_TX_Serial => w_TX_Serial,
      o_TX_Done   => open
      );

  -- Drive UART line high when transmitter is not active
  o_UART_TX <= w_TX_Serial when w_TX_Active = '1' else '1';

  -- Binary to 7-Segment Converter for Upper Digit
  SevenSeg1_Inst : entity work.Binary_To_7Segment
    port map (
      i_Clk        => i_Clk,
      i_Binary_Num => w_RX_Byte(7 downto 4),
      o_Segment_A  => w_Segment1_A,
      o_Segment_B  => w_Segment1_B,
      o_Segment_C  => w_Segment1_C,
      o_Segment_D  => w_Segment1_D,
      o_Segment_E  => w_Segment1_E,
      o_Segment_F  => w_Segment1_F,
      o_Segment_G  => w_Segment1_G
      );
  
  o_Segment1_A <= not w_Segment1_A;
  o_Segment1_B <= not w_Segment1_B;
  o_Segment1_C <= not w_Segment1_C;
  o_Segment1_D <= not w_Segment1_D;
  o_Segment1_E <= not w_Segment1_E;
  o_Segment1_F <= not w_Segment1_F;
  o_Segment1_G <= not w_Segment1_G; 
    
  -- Binary to 7-Segment Converter for Lower Digit
  SevenSeg2_Inst : entity work.Binary_To_7Segment
    port map (
      i_Clk        => i_Clk,
      i_Binary_Num => w_RX_Byte(3 downto 0),
      o_Segment_A  => w_Segment2_A,
      o_Segment_B  => w_Segment2_B,
      o_Segment_C  => w_Segment2_C,
      o_Segment_D  => w_Segment2_D,
      o_Segment_E  => w_Segment2_E,
      o_Segment_F  => w_Segment2_F,
      o_Segment_G  => w_Segment2_G
      );
  
  o_Segment2_A <= not w_Segment2_A;
  o_Segment2_B <= not w_Segment2_B;
  o_Segment2_C <= not w_Segment2_C;
  o_Segment2_D <= not w_Segment2_D;
  o_Segment2_E <= not w_Segment2_E;
  o_Segment2_F <= not w_Segment2_F;
  o_Segment2_G <= not w_Segment2_G;
  
end architecture RTL;

Verilog Code – UART_Loopback_Top.v:

module UART_Loopback_Top
  (input  i_Clk,       // Main Clock
   input  i_UART_RX,   // UART RX Data
   output o_UART_TX,   // UART TX Data
   // Segment1 is upper digit, Segment2 is lower digit
   output o_Segment1_A,
   output o_Segment1_B,
   output o_Segment1_C,
   output o_Segment1_D,
   output o_Segment1_E,
   output o_Segment1_F,
   output o_Segment1_G,
   //
   output o_Segment2_A,
   output o_Segment2_B,
   output o_Segment2_C,
   output o_Segment2_D,
   output o_Segment2_E,
   output o_Segment2_F,
   output o_Segment2_G   
   ); 

  wire w_RX_DV;
  wire [7:0] w_RX_Byte;
  wire w_TX_Active, w_TX_Serial;

  wire w_Segment1_A, w_Segment2_A;
  wire w_Segment1_B, w_Segment2_B;
  wire w_Segment1_C, w_Segment2_C;
  wire w_Segment1_D, w_Segment2_D;
  wire w_Segment1_E, w_Segment2_E;
  wire w_Segment1_F, w_Segment2_F;
  wire w_Segment1_G, w_Segment2_G;
  
  // 25,000,000 / 115,200 = 217
  UART_RX #(.CLKS_PER_BIT(217)) UART_RX_Inst
  (.i_Clock(i_Clk),
   .i_RX_Serial(i_UART_RX),
   .o_RX_DV(w_RX_DV),
   .o_RX_Byte(w_RX_Byte));
   
  UART_TX #(.CLKS_PER_BIT(217)) UART_TX_Inst
  (.i_Clock(i_Clk),
   .i_TX_DV(w_RX_DV),      // Pass RX to TX module for loopback
   .i_TX_Byte(w_RX_Byte),  // Pass RX to TX module for loopback
   .o_TX_Active(w_TX_Active),
   .o_TX_Serial(w_TX_Serial),
   .o_TX_Done());
  
  // Drive UART line high when transmitter is not active
  assign o_UART_TX = w_TX_Active ? w_TX_Serial : 1'b1; 
  
  
  // Binary to 7-Segment Converter for Upper Digit
  Binary_To_7Segment SevenSeg1_Inst
  (.i_Clk(i_Clk),
   .i_Binary_Num(w_RX_Byte[7:4]),
   .o_Segment_A(w_Segment1_A),
   .o_Segment_B(w_Segment1_B),
   .o_Segment_C(w_Segment1_C),
   .o_Segment_D(w_Segment1_D),
   .o_Segment_E(w_Segment1_E),
   .o_Segment_F(w_Segment1_F),
   .o_Segment_G(w_Segment1_G));
   
  assign o_Segment1_A = ~w_Segment1_A;
  assign o_Segment1_B = ~w_Segment1_B;
  assign o_Segment1_C = ~w_Segment1_C;
  assign o_Segment1_D = ~w_Segment1_D;
  assign o_Segment1_E = ~w_Segment1_E;
  assign o_Segment1_F = ~w_Segment1_F;
  assign o_Segment1_G = ~w_Segment1_G;
  
  
  // Binary to 7-Segment Converter for Lower Digit
  Binary_To_7Segment SevenSeg2_Inst
  (.i_Clk(i_Clk),
   .i_Binary_Num(w_RX_Byte[3:0]),
   .o_Segment_A(w_Segment2_A),
   .o_Segment_B(w_Segment2_B),
   .o_Segment_C(w_Segment2_C),
   .o_Segment_D(w_Segment2_D),
   .o_Segment_E(w_Segment2_E),
   .o_Segment_F(w_Segment2_F),
   .o_Segment_G(w_Segment2_G));
  
  assign o_Segment2_A = ~w_Segment2_A;
  assign o_Segment2_B = ~w_Segment2_B;
  assign o_Segment2_C = ~w_Segment2_C;
  assign o_Segment2_D = ~w_Segment2_D;
  assign o_Segment2_E = ~w_Segment2_E;
  assign o_Segment2_F = ~w_Segment2_F;
  assign o_Segment2_G = ~w_Segment2_G;
  
endmodule

Now after building the code in iCEcube2, we should take a quick look at the synthesis results. How many LUTs and Registers (Flip-Flops) does your design use? Is there anything you can think of trying to reduce logic utilization? Let’s take a look at the programmed Go Board in action.

Gif of results

Keyboard Presses Are Looped Back To Terminal for Display

Success! Tera Term is relaying data that we send to the Go Board back to the computer. Now that you have the basics down for UARTs, you can probably think of a LOT more projects to do with them. How would you have the Go Board respond to different key presses from the computer? How might you have a menu displayed to the user? There’s lots to play with, so spend some time thinking about how you might be able to do fun projects with the Go Board and UARTs. If you come up with something cool, please post a link to your design in the comments section below!

We are nearing the end of the Go Board tutorials. The last topic to discuss is a culmination of everything we have learned so far. I would like to talk about how VGA works, and show you how to create PONG with the Go Board. So let’s get started!

Let’s talk about VGA!