Digilent Basys 3, an Xilinx FPGA development board, has one USB-UART connector. To learn how to build UART communication between the FPGA board and the data terminal equipment (DTE) like computer terminal, I build two projects - UART transmitter and UART receiver. Part I focuses on the UART transmitter. We will transmit 8 bits data from Basys 3 to computers through USB-UART connector on Basys 3. When users turn on / off any switch, this form a binary value of an ASCII character. A byte (8 bits) character will be transmitted through the UART to the computer. The character will show in the terminal (Terterm). All transmission is triggered when a button is pressed.
Step 1: Step 1: UART Concept
You can review the basics of UART (Universal Asynchronous Receiver and Transmitter) communication at http://www.circuitbasics.com/basics-uart-communica...
Normally, A UART allows the communication between a computer and several kinds of devices (printer, modem, etc), interconnected via an RS-232 interface
Step 2: Step 2: Design Logic
There are three different modules - top, button and transmitter. The top module instantiate the button and transmitter modules. We can use the debounced button project for the button module and comment out signals that won’t be used. In the following steps, we will introduce each piece in the transmitter module and combine all pieces together.
Attached is the modified debounced button module
Step 3: Step 3: Inputs & Outputs of Transmitter Module
clk - master clock
reset - reset signal
transmit - btn signal to trigger the UART communication
[7:0]data - 8 bits data transmitted. No parity bit
TxD - Transmitter serial output. Serial data frames are transmitted via this pin. TxD will be held high during reset, or when no transmissions are taking place
Step 4: Step 4: Internal Variables of Transmitter Module
reg [3:0]bitcounter - 4 bits counter to count if 10 bits data transmission complete or not
reg [3:0]counter - 14 bits counter to count the baud rate
reg [1:0]state - Initial state variable for Mealy State Machine
reg [1:0]nextstate - Next state variable for Mealy State Machine
reg [9:0]rightshiftreg - 10 bits data needed to be shifted out during transmission. For storing the serial package and sending its bits one by one. When the load signal is 1, it is loaded with a new character to transmit (8 bits). The least significant bit is initialized with the binary value “0” (a start bit) A binary value “1” is introduced in the most significant bit
reg shift - Signal to show shifting data is ready
reg load - Signal to show loading data is ready
reg clear - Signal to clear all signals
Step 5: Step 5: Baud Rate Generator
The master clock frequency on Basys 3 is 100MHz. We need baud rate of 9600 for UART transmission. To make a baud rate generator, we will use a counter. For example, the baud rate is only 9600 bits per second (bps).
baud rate * counter = master clock frequency
Therefore, we create a counter value is 10,416 for the baud rate of 9600.
counter = master clock frequency / baud rate
When the counter for baud rate count up to 10415 (from 0 to 10415 = 10416) for a baud rate of 9600, we will reset the counter to be 0 and then change the state in the finite state machine. In other words, the counter is used to slow down the 100MHZ FPGA clock for our program to work at 9600 baud. The reason for doing this is to make the program synchronous to the clock (or counter).
Step 6: Step 6: Logic for Transmission
In step 5, we have created a counter for baud rate generator. Once the counter counts up to 10,416, check if clear, shift and load signals are asserted (“1”) one by one.
i) If the load signal is asserted (“1”), the rightshiftregister loads the data that is to be transmitted and add start and stop bits. This is why, once the state goes from IDLE to TRANSMIT, load goes high. It ensures that data is loaded into the register and is ready to be transmitted.
ii) If shift is asserted (“1”), rightshiftreg = rightshiftreg>>1. This shifts the data right one time. Bitcounter is also incremented once. This tells the program that one bit was already sent.
iii) If clear is asserted (“1”), we have finished transmitting and bitcounter goes back to zero; waiting for another new transmission.
We also create a synchronous Mealy State Machine
1. IDLE State (S0):
If the transmit is zero, we stay at the IDLE state and keep waiting until that button is pressed.
Set load, shift and clear signals to 0. This prevent shifting when the transmission doesn’t start. Do not set the bitcounter to zero. Otherwise, we can’t increment the bitcounter when we are transmitting the data. We do not have any data to load from just yet. TxD is held high to show that nothing is being transmitted (IDLE State).
We move to the TRNSMIT State when the input transmit is 1. This transmit is the push button on Basys 3 we press to tell the program we are going to transmit. Set the load to “1” (high) but keep shift and clear signal to be “0”(low).
2. TRANSMIT State (S1):
Remember, we have 8 data bits, 1 start bit and 1 stop bit. When the bitcounter reaches 10 (equal or greater than 10), it means that it has completed its task of transmitting. When we reach our goal of a 10 bit transmission, the clear must be “1” (high) to clear the bitcounter back to zero and get ready for the next transmission. Also the next state must go back to IDLE. Keep load and shift to be “0” (low)
If bitcounter is less than 10, we stay in TRANSMIT state. Also, we must put shift to 1 because we want to shift our rightshift register so the next bit can be shifted onto our TxD output pin for UART transmission. This is done in TxD = shiftreg. Load and shift are “0” (low).
Step 7: Step 7: Verilog Codes for Transmitter Module
The logic is referenced from http://www.ehow.com/how_5667684_create-uart-transm...
Attached is the verilog code for transmitter module
Step 8: Step 8: Create Top Module
Instantiate the transmitter and top module. In addition, we created the following outputs and make assignment to do debugging through the Logic Analyzer
output TxD_debug - TxD signal
output transmit_debug - Transmit signal
output button_debug - Button input signal
output clk_debug - Master clock signal
assign TxD_debug = TxD;
assign transmit_debug = transmit;
assign button_debug = btn1;
assign clk_debug = clk;
Attached is the top module.
Step 9: Step 9: Modify Master Constraint File
To implement the design, we need to create constraints. Digilent provides master constraint file for Basys 3.
We need to do following tasks. Attached is the modified master constraint file.
1) uncomment pin we need by removing # in front of "set property...."e.g. in the top module, our inputs are [7:0] sw, so we go to switches and uncomment "set property....." statement from sw to sw
2) change the signal name, e.g. we have two button inputs - btn0 and btn1. In the master constraint file, we change.the BTNU and BTNL to btn 0 and btn 1
set_property PACKAGE_PIN U18 [get_ports btn0]
set_property IOSTANDARD LVCMOS33 [get_ports btn0]
set_property PACKAGE_PIN T18 [get_ports btn1]
set_property IOSTANDARD LVCMOS33 [get_ports btn1]
3) Put the debug signals through Pmod JA port from JA1 to JA4
##Sch name = JA1
set_property PACKAGE_PIN J1 [get_ports TxD_debug]
set_property IOSTANDARD LVCMOS33 [get_ports TxD_debug]
##Sch name = JA2
set_property PACKAGE_PIN L2 [get_ports transmit_debug]
set_property IOSTANDARD LVCMOS33 [get_ports transmit_debug]
##Sch name = JA3
set_property PACKAGE_PIN J2 [get_ports button_debug]
set_property IOSTANDARD LVCMOS33 [get_ports button_debug]
##Sch name = JA4
set_property PACKAGE_PIN G2 [get_ports clk_debug]
set_property IOSTANDARD LVCMOS33 [get_ports clk_debug]
Step 10: Step 10: Run Synthesis, Implementation and Generate Bitstream
Attached is the project file.
If you forget how to run synthesis, implementation and generate bitstream in Xilinx Vivado, you can review last few steps at https://www.instructables.com/id/Simple-Logic-Design-w-Digilent-Nexys-4-Field-Progr/
Step 11: Step 11: Program the Bitstream to Xilinx FPGA on Basys 3 & Run Teraterm
Connect Basys 3 to computer through USB port (see attached picture) and program the .bit file to the FPGA on board.
Make sure you serial USB port is connected. You can find the right port at the Device Manager (if you have window PC). The port setting should be 9600 baud rate, (see attached picture)
Turn on Teraterm (http://download.cnet.com/Tera-Term/3000-20432_4-75766675.html) and make the connection. (see attached picture)
Step 12: Step 12: Configure Switches to Make ASCII Character
Check the binary value of the ASCII character at http://www.rapidtables.com/code/text/ascii-table.h...
Configure the switches. For example, we make the binary value of ASCII character "a". (see picture). Then, we push the BTNU on board to trigger the transmission. You will see "a" in the Teraterm.(see picture)
Note: There is still a debouncing issue. So, you will see multiple "a"
Step 13: Step 13: Scope Out Debug Signals to Logic Analyzer
We will use the Logic Analyzer from Digilent Analog Discovery 2
See the attached picture for the set up.
Download the Digilent Waveforms at http://store.digilentinc.com/waveforms-download-on...
Attached is the Waveforms workspace with preset parameters. We will debug the following signals. Set the time base to at least 100ms/div. You can also set the trigger of each signal.
1) Master clock- debug_clk (JA4)
2) TxD- debug_TxD (JA1)
3) transmit - debug_transmit (JA2)
4) button - debug_button (JA3)
Once you click "Run" in the Logic Analyzer and then push the button on board. You will see the timing diagram and analyze the logic. The root cause for the debouncing is because of the longer period for transmit signal. (see picture)