Introduction: UART Communication on Basys 3, FPGA Dev Board Powered by Xilinx Artix 7 Part II
In this part, we will show how to build UART RX (receiving) hardware. 8 LEDs will be used to show the binary value of the ASCII character. When the key strobe on the keyboard (from the computer) is pressed, the 8 bits will transmit from the keyboard to FPGA through USB-UART port on Basys 3
Add a Teacher Note to share how you incorporated it into your lesson.
Step 1: Step 1: Inputs & Outputs of Receiver Module
There is only one module in the project.
clk - Master clock
reset - Reset signal
RxD - Serial input. The serial packages are received from this input
[7:0]data - 8 bits data transmitted. No parity bit
Step 2: Step 2: Internal Variables
reg [3:0]bitcounter - 4 bits counter to count if 10 bits data transmission complete or not
reg [13:0]counter - 14 bits counter to count the sample rate for UART receiving
reg [1:0]sample counter - 2 bits sample counter to count the sample up to 4 (0 to 3)
reg state - Initial state variable for Mealy State Machine
reg nextstate - Next state variable for Mealy State Machine
reg [9:0]rxshiftreg - 10 bits data needed to be shifted in during transmission. For storing the serial package and sending its bits one by one. 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 clear_bitcounter - Signal to clear the bit counter
reg inc_bitcounter - Signal to increment bit counter
reg inc_samplecounter - Signal to increment sample counter
reg clear_samplecounter - Signal to clear sample counter
Step 3: Step 3: Counter for Oversampling
An asynchronous receiver has to somehow get in-sync with the incoming signal (it doesn't have access to the clock used during transmission). To determine when a new data byte is coming, we look for the "start" bit by oversampling the signal at a multiple of the baud rate frequency. Once the "start" bit is detected, we sample the line at the known baud rate to acquire the data bits. Receivers typically oversample the incoming signal at 16 times the baud rate. Let's use 8 times here... For 115200 bauds, that gives a sampling rate of 921600Hz (http://www.fpga4fun.com/index.html)
From the picture, the falling edge of the start bit you "find" the start-bit's "middle" by counting to 8 then, each count of 16 thereafter you "sample" the UART received data to recreate the byte (or bytes) transmitted. Clearly if you had a 32x counter you'd get a tad more accuracy in determining the center-point of the bit and counting at a higher rate is going to work but the numbers get bigger and the power consumption rise.
The baud rate for the project is 9600. We need to consider the sample rate to oversample so that the receiver can get the bit. A counter sends bit by bit at a specified speed (baud rate). On the receiver side, this data will be sampled to make sure that the transmission was received without error. The sampling rate is four times that of the transmitter.
counter = FPGA clock speed / (baud rate*sample_rate)
Step 4: Step 4: UART Receiving Logic
The data we plan to receive over a line is stored in bit-registers on the FPGA. A bit register can be composed of many bits. Because we only want to use 1 output of the FPGA to send all the data, we need to have a special register that can shift the data each time one bit is received over the single line, so that the next one in line being transmitted can be received and no information loss happens. We need to know how many bits at a time we are receiving serially. The shift register size will have a start bit, data bits, stop bit. In this case it will be 1 data bit, 8 data bits and 1 stop bit with a total of 10 bits.
We have 10 bits, so we need a bitcounter register to count when we reach the number of bits sent so we can stop transmission. We also need a samplecounter register, something we do not have in the transmitter. This ensures that sampling goes smoothly.
Finite State Machine
We will use synchronous Mealy State Machine to implement the transmit module.
1. IDLE State (0):
When TxD sends a low transmission (zero), RxD moves from IDLE to RECEIVING and clear_samplecounter and clear_bitcounter signals are asserted (“1”). Otherwise, we will stay in the IDLE state. All signals are set to low (“0”).
2. RECEIVING State (1):
Looking over at state RECEIVING, the program is checking if the samplecounter is 1 and if it is, then shift is asserted (“1”). SHIFT is basically shifting rxshiftreg register and capturing the data that is being sent.
The program is also checking that samplecounter is equal to 3. If it is not equal to 3, then inc_samplecounter is set high, thus causing samplecounter to increment its value at each time counter reaches the sample time.
However, if the samplecounter is equal to 3, then it checks if the bitcounter is equal to 10. When the bitcounter is equal to 9, then the program knows that it has finished receiving data and it can now go into IDLE state.
When the bitcounter is not equal to 9, yet the samplecounter is equal to 3, then clear_samplecounter is set high, which resets samplecounter. This is the equivalent of receiving one bit of data out of the 10 bits being transmitted.
Step 5: Step 5: Verilog Code for Receiver Module
Attached is the verilog code for the receiver module
Step 6: Step 6: Run Synthesis, Implementation and Generate Bitstream
Attached is the project file. You can run synthesis and generate the bitstream. The modified master constraint file is also included.
Note: Learn how to do this at https://www.instructables.com/id/Simple-Logic-Desig...
Step 7: Step 7: Connect Basys 3 to Computer and Program the Bitstream
Connect the Basys 3 like the set up in the picture. Program the bitstream to FPGA on Basys 3 (see how to do this at https://www.instructables.com/id/Simple-Logic-Desig...
Step 8: Step 8: Turn on the Teraterm & Press the Key
See the step 11 at https://www.instructables.com/id/UART-Communication...
When you press "a" in from the keyboard, you will see the LEDS on at LD0, LD5 and LD6 (binary value: 01100001)