====== Sp20 UART Transmitter ======
In this laboratory you will create an asynchronous serial transmitter that will allow you to send ASCII characters from the NEXYS4 board to your computer.
===== Learning Outcomes =====
* Learn how to implement state machines using behavioral SystemVerilog
* Understand how asynchronous communication works
===== Preliminary =====
Chapter 28 discusses the asynchronous transmitter in detail. You will want to review this chapter before beginning this lab and refer back to it when you have questions. Note that we will be using the method described in Section 28.3 (note there are at least two methods described in this section, we are doing the one reflected in Figures 28.8, 28.9, 28.11, 28.12, and programs 28.3.1).
In this lab we will operate the transmitter at a baud rate of 19,200 bits per second. We will transmit using odd parity and 8 bits of data.
How many clock cycles of the 100 MHz clock used on the FPGA board does it take to provide one bit of data using this baud rate?
How many bits are needed to count the clock cycles for each bit of data?
What is the maximum number of 8-bit ASCII characters that can be transmitted per second using your transmitter at a baud rate of 19,200? (make sure you consider the presence of the start bit, parity bit, and stop bit).
----
===== Exercises =====
==== Exercise #1 - Asynchronous Transmitter Module ====
Begin your laboratory assignment by creating a module that has the following parameters and ports:
^ Module Name = tx ^^^^
^ Parameter ^ Type (Default) ^^ Function ^
^ Port Name ^ Direction ^ Width ^ Function ^
| clk | Input | 1 | 100 MHz System Clock |
| Reset | Input | 1 | Reset signal to reset state machine (at a minimum) |
| Send | Input | 1 | Control signal to request a data transmission (corresponds to the 'req' signal in Figure 28.7) |
| Din | Input | 8 | 8-bit data in to send over transmitter |
| Sent | Output | 1 | Acknowledge that transmitter is done with the transmission (corresponds to the 'ack' signal in Figure 28.7) |
| Sout | Output | 1 | Transmitter serial output signal |
The transmitter consists of a control section and a data path section as shown in Figure 28.9 of the text. The code for the data path is given in Program 28.3.1.
The control section is outlined in Figure 28.11. As you can see, it requires two counters - one to time each bit period and one to count the bits.
** Baud Rate Timer **
Your transmitter will need a counter that counts the number of clock cycles needed for a single baud period. Create a counter using behavioral SystemVerilog with the following specifications:
* The size of the counter (in bits) is based on the baud rate and your system's 100MHz clock rate. The counter must have enough bits to represent the largest count value as determined by your calculations above.
The counter has an output signal that indicates when the counter has reached the last clock cycle of the baud period ('timerDone' in the system diagram). This signal is used as an input in the FSM. When the timer reaches this point, it must roll over to 0; the state machine design relies upon this.
** Parity Generator **
The parity calculation is actually embedded in the code of Program 28.3.1 - can you see where it is being done and how it is being done? Make sure you understand this.
** Bit Counter **
Create the Bit Counter which is responsible for counting the number of bits that have been transmitted. You can see in Figure 28.11 what signals the state machine uses to interact with the bit counter.
** Datapath **
Note that the data path outputs the correct bits needed in response to state machine outputs. Note that it is an always_ff block to ensure that the final output signal (sout) is registered to avoid glitching. The datapath code is outlined in Program 28.3.1.
** FSM **
Create the FSM as outlined in the following figure. Note, this is slightly different that the figure in the textbook, which as a small error in the BITS state.
{{ :labs:uart_tx:uart_tx_sm.png?600 |}}
**Use the following TCL script to simulate your ''tx'' module:**
restart
# Start clock
add_force clk {0} {1 5} -repeat_every 10
run 10ns
# Reset design
add_force Reset 1
add_force Send 0
run 10ns
add_force Reset 0
run 10ns
# Run for some time
run 50us
# Send a byte
add_force -radix hex Din 47
add_force Send 1
run 10ns
add_force Send 0
run 1ms
# Send another byte
add_force -radix hex Din 4F
add_force Send 1
run 10ns
add_force Send 0
run 1ms
Look at the waveform and make sure your module is operating correctly. Check the simulation that:
* For each byte sent, the 8 data bits are output correctly in the correct order.
* Check that the parity output is correct.
* Check that the start and stop output is correct.
* Check that both of your timers are operating correctly; that they reset and increment in the correct conditions.
**Exercise 1 Pass-off:** Show a TA your waveform and discuss how the waveform demonstrates the above points.
----
==== Exercise #2 - Testbench Validation ====
When it looks like your tx module is working properly use the following testbench file to test it.
{{ :labs:tb_tx.sv |}}
Include a copy of the simulation console in your laboratory report when your simulation passes the testbench with no errors
**Exercise 2 Pass-off:** You don't need a TA to pass of this exercise, but __make sure the testbench is working before moving on__.
----
==== Exercise #3 - Top-Level TX Circuit ====
After simulating your module and verifying it operates, download the following top-level module (just click the link below to download the file, then add it to your project.):
//////////////////////////////////////////////////////////////////////////////////
//
// Filename: tx_top.sv
//
// Author: Jeff Goeders
//
// Description: UART Transmitter top-level design
//
//
//////////////////////////////////////////////////////////////////////////////////
module tx_top(
input wire logic clk,
input wire logic CPU_RESETN,
input wire logic [7:0] sw,
input wire logic btnc,
output logic [7:0] anode,
output logic [7:0] segment,
output logic tx_out,
output logic tx_debug);
logic reset;
assign reset = ~CPU_RESETN;
assign tx_debug = tx_out;
logic btnc_r;
logic btnc_r2;
logic send_character;
// Button synchronizaer
always_ff@(posedge clk)
begin
btnc_r <= btnc;
btnc_r2 <= btnc_r;
end
// Debounce the start button
debounce debounce_inst(
.clk(clk),
.reset(reset),
.noisy(btnc_r2),
.debounced(send_character)
);
// Transmitter
tx tx_inst(
.clk (clk),
.Reset (reset),
.Send (send_character),
.Din (sw),
.Sent (),
.Sout (tx_out)
);
// Seven Segment Display
SevenSegmentControl SSC (
.clk(clk),
.reset(reset),
.dataIn({24'h0, sw}),
.digitDisplay(8'h03),
.digitPoint(8'h00),
.anode(anode),
.segment(segment)
);
endmodule
Look over the top-level file. You will see that it uses your debounce circuit from a previous lab, which ensures that when you hit the send button, only a single character is sent. The seven segment controller displays the current value of the switches to help quickly determine the hexadecimal value and compare it to an ASCII table.
/*
begin a new top-level module with the following ports:
^ Module Name = tx_top ^^^^
^ Port Name ^ Direction ^ Width ^ Function ^
| clk | Input | 1 | 100 MHz System Clock |
| sw | Input | 8 | 8 Slide switches to specify character to send |
| btnc | Input | 1 | Send character control signal |
| btnd | Input | 1 | Reset control signal |
| tx_out | Output | 1 | Transmit signal |
| segment | Output | 8 | Cathode signals for seven segment display |
| anode | Output | 8 | Anode signals for each of the eight digits |
| tx_debug | Output | 1 | TX Debug signal |
Instance your tx module and make the following connections to your tx module:
* Connect the sout output of your tx module to your top-level tx_out output
* Connect the sout output of your tx module to your top-level tx_debug output (yes, the same signal)
* Connect an unused signal to the 'sent' signal of your tx module (while we will not use it, the sent signal is normally important for handshaking protocol)
* Connect the 8 switches to the 'din' input of your tx module
* Connect the top-level clock to the clk input of your tx module
* Create a signal named 'send' and attach it to the 'send' input of your tx module. We will discuss what logic will be used to drive this input later.
* Create a signal named 'reset' and attach it to the 'reset' input of your tx module. This will be created the same way as the 'send' signal.
Instance your seven segment display controller and connect the inputs to your controller as follows:
* Attach the 8 switches to the lower 8-bits of the data in of the seven-segment controller. Assign the upper 24 bits of the data in to '0'.
* Only display the bottom two digits of the seven segment display
* Do not use any of the digit points
* Attach the anode and segment signals of the controller to the top-level outputs of the top-level module
Create debounced 'send' and 'reset' signals as follows:
* Using the debouncer done in a previous lab, debounce the 'btnc' input to create the 'send' signal and feed into your design.
* Using another debouncer instance, debounce the 'btnd' input to create the 'reset' signal.
**Exercise 3 Pass-off:** Show a TA your top level module and explain why we need to debounce the button inputs.\\ \\
*/
Before synthesizing your design, you will need to create an .xdc file that contains the pin locations of each port in your design. Most of the pins used in this design are the same as pins used in previous designs (buttons, switches, seven-segment display, etc.). However, you will be using a new FPGA pin to connect to the UART/USB transciever and the debug port.
The following example code demonstrates how these I/O pins can be attached to your top-level FPGA pins:
set_property -dict { PACKAGE_PIN D4 IOSTANDARD LVCMOS33 } [get_ports { tx_out }]; #IO_L11N_T1_SRCC_35 Sch=uart_rxd_out
set_property -dict { PACKAGE_PIN C17 IOSTANDARD LVCMOS33 } [get_ports { tx_debug }]; #IO_L20N_T3_A19_15 Sch=ja[1]
After completing your .xdc file, proceed to generate your bitstream.
Attach a copy of and then explain in word any ERRORS or CRITICAL WARNINGS that are in either the synthesis report from above. For each one, explain the following: What does this ERROR or CRITICAL WARNING refer to and do you understand why was it flagged? Is it OK or not for you to ignore it and assume the design will work in hardware (and if so, why)? Please explain.
**Exercise 3 Pass-off:** There is no pass-off for this exercise.\\ \\
==== Exercise #4 - Download and Putty ====
Once the bitstream has been generated, download your bitstream to the FPGA. To test the transmitter, you will need to run a program called "PuTTY". Follow the [[tutorials:putty_personal_machine|PuTTY tutorial]] to set it up if you are running on your own machine. If you are running on a lab machine it should already be installed.
After installing, specifically, follow the testing procedures outlined to ensure that PuTTY is working on your machine. That way, if your downloaded circuit design doesn't work you will know that PuTTY is not the reason.
Once your PuTTY terminal is configured properly you should be able to send characters to the terminal by selecting the [[http://www.asciitable.com/|ASCII]] value of the character to send on the switches and pressing the center button.
Interesting control commands:
* 8'h07 - Bell (a sound, not a character)
* 8'h09 - Tab
* 8'h0C - Clear screen
* 8'h0A - Move cursor down (Line Feed)
* 8'h0D - Carriage Return
The above are called "control characters" since they control the screen rather than draw characters. They are typically reached on normal keyboards using the "Control" key like a shift key (press concurrently with another key). Thus, when you hit Control-C to kill a program on a computer you are sending (or at least with older computers were sending) the ASCII character 8'h03.
===== Final Pass Off =====
Attach a video link to LearningSuite, demonstrating that transmitter circuit works. Test it against a variety of characters from the ASCII table at the end of your textbook - digits, punctuation, and both upper and lowercase letters. Show what happens when you send some of the special characters mentioned above - do you get interesting or meaningful stuff? How about for characters with values 8'h80 and above?
How many hours did you work on the lab?
Please provide any suggestions for improving this lab in the future.
Upload your tx.sv SystemVerilog module to Learning Suite. (Make sure your SystemVerilog conforms to the lab SystemVerilog coding standards).
/*
===== Personal Exploration =====
(The Personal Exploration is optional for this lab)
Here are some ideas for personal exploration in this laboratory:
* Experiment with different baud rates and see if you can communicate with the PC at a different baud rate.
* Augment your design to support either even or odd parity.
* Send a variety of interesting characters over the terminal and see how they respond.
* Hook up the tx debug signal to the oscilliscope and view the transfer of an 8-bit character.
*/
[[labs:ta:uart_tx|TA Notes and Feedback]]
/*
----
====== Lab 10: UART Transmitter ======
=====Explanation=====
The lab is the first of a two part series of labs involved in the construction of a Universal Asynchronous Receiver/Transmitter (UART). In this lab, you will design, code and test a serial transmitter. In the next lab, you will design, code, and test a serial receiver. To begin this laboratory, read the first half of page 10 of the [[http://ecen220wiki.groups.et.byu.net/dokuwiki/lib/exe/fetch.php?media=devel:nexys4ddr_rm.pdf|Nexys4 reference manual]] to learn how the USB-UART bridge port is hooked up on the Nexys4 board. After reading the Nexys4 manual, watch the first part of the following video to learn more about the UART transmitter and the lab.
{{http://ece320web.groups.et.byu.net/lab/UartTransmitter/uart_transmitter.mp4}}
[[ARE WE GOING TO ADD QUESTIONS? ]]
=====Pre-Lab=====
Be sure to watch the first part of the video above, and read the first half of page 10 of [[http://ecen220wiki.groups.et.byu.net/dokuwiki/lib/exe/fetch.php?media=devel:nexys4ddr_rm.pdf|Nexys4 reference manual]].
=====Procedure=====
====Transmitter Design====
Watch the second part of the UART video above to get a good overview of the UART transmitter design. In this lab we will be providing you with most of the design architecture. This part of the video describes a block diagram, a state machine description, and sufficient information for you to create a transmitter in Verilog. You will need to understand this design before you proceed with your Verilog coding.
{{devel:txblock.png}}
{{http://ece320web.groups.et.byu.net/lab/UartTransmitter/uart_lab_figures.pdf | UART Transmitter Slides}} are available for your reference. Go back and review these slides if you have questions about the design.
Once you have watched the UART transmitter design screencast, begin coding and simulating your reusable UART transmitter. Begin by creating a Verilog source file called tx.v (ensure that your module is named this way so it will work with the provided testbench). Create your module with the following input ports, and output ports. Be sure to include the following parameters as well (these are explained in the video above).
^ Port Name ^ Purpose ^
^ Inputs ^^
|clk|50 MHz clock|
|rst|Asynchronous reset|
|send_character|Control signal to initiate transmission of byte|
|data_in[7:0]|Byte to send over transmitter|
^ Outputs ^^
|tx_out|Serial data to be transmitted|
|tx_busy|Indicates that the transmitter is busy sending a character|
^ Parameters ^ Purpose ^
|CLK_RATE|Indicates the frequency of the input clock. Set to 100000000|
|BAUD_RATE|Baud rate of the transmitter. Set to 19200|
You will need to determine the width of your bit timer to measure the proper time for a bit period. You can determine this width based on the CLK_RATE and BAUD_RATE generics. To determine the width of this bit timer, you need a way to compute the base-2 logarithm. A function is included that we can use to compute the base-2 logarithm. Here is some sample code that demonstrates how to use this function to determine the size of your counter:
''parameter BIT_COUNTER_MAX_VAL = CLK_RATE / BAUD_RATE - 1;\\ parameter BIT_COUNTER_BITS = $clog2(BIT_COUNTER_MAX_VAL);''
Design each of the "datapath" components described in the design document individually (bit timer, shift register, and transmit out). In other words, create each of the components one at a time and make sure they work individually before you integrate them. Students frequently waste a lot of time integrating design components that do not work. Create the FSM as described in the video (again, do this after you have verified the correct function of the datapath components). You may want to review state machine design from [[SOME PART OF TEXT]]. After creating your VHDL, remove all compilation errors and perform a few simple tests to see if the circuit is working as you expect it to. Write a simple .tcl script to simulate the behavior of your circuit.
====Testbench Simulation of Transmitter====
A testbench is often used to simulate a circuit more fully than is possible with commands on the command line and a .tcl script. A testbench is a behavioral model that simulates the environment of the circuit and checks the circuit outputs for proper behavior.
Copy the contents of the testbench [[devel:transmitter_test_bench|here]], and add it to your current project by doing the following:
- In your project right click **Simulation Sources** in the //Sources// window.
- Click **Add Sources...**
- Make sure //Add or create simulation sources// is selected then click **Next**
- Add the testbench file just like you'd add any other file, but make sure it is a VHDL file type.
The
[[devel:transmitter_test_bench|testbench]] has been provided for you to test your transmitter under a number of conditions. This testbench will simulate your transmitter, sending several bytes and perform a number of checks to make sure you send the data correctly (i.e., the correct amount of time and the correct order). Make sure you simulate until the "DONE" message is printed. Simulate your design until it passes all of the tests.
====Top-Level Transmitter Design====
The second design required for this laboratory is a top-level module. This top-level file will contain your transmitter, the seven segment display controller, debounce circuitry, and connections between the I/O and the logic. This top-level design will allow you to send ASCII characters from your FPGA board to a terminal on your workstation. You will select the ASCII character by setting the switches to the binary value of the ASCII character you want to send. When you have selected your ASCII character, you will press a button to initiate the transfer.
Follow these guidelines to create this top-level design:
* Instance your UART transmitter that you created earlier in the lab.
* Instance the seven segment display controller that you created in a previous lab.
* Wire the board switch inputs to the data_in of your transmitter. Also wire the switches to the lowest 8 bits of the data_in input of your seven segment display. This will allow you to see the current HEX value of the switches and make sure you are sending the appropriate character.
* Use one of the buttons for the "send_character" signal of your transmitter. You will need to add debouncing logic to this button (see notes in the video on debouncing).
* Use one of the buttons for the asynchronously resets in your design.
A debouncer circuit is needed to remove the "Bouncing" of the input button. Watch //Part 3: Debouncer// of the video and create a debouncer for your design.
Carefully simulate your entire design to make sure the reset logic, debouncer, transmitter and display controller work correctly. You may need to change the 'debounce time' to make it easier to simulate the full circuit.
Create an .xdc file for your design that includes all the top-level ports used in the design. Make sure you have an entry for the tx signal. Also, make sure you have a timing constraint for the clock. Synthesize your design and carefully review the warnings generated during synthesis. Make sure you read and understand each one.
To connect your tx signal, you will connect it to PIN D4 under the ''USB-RS232 Interface'' section found in the Master XDC file.
====Testing the Transmitter on the Board====
Before testing your transmitter, you need to setup your workstation to accept data from the FPGA board. Follow the instructions in Part 4 of the video to setup your workstation terminal.
The last step of this lab is to synthesize your design and test it on the board. Review these guidelines and instructions when preparing to test your design.
Run Putty on the computer, according to the instructions in Part 4 of the video. Whatever ASCII code you put on the switches should be printed to the screen when you press the button you connected to send_character. If it works, then get passed off by a TA.
{{devel:asciifull.gif}}
====Pass Off====
Demonstrate the following to a TA to passoff your lab:
* Show your simulation passing the testbench
* Demonstrate to the TA a working circuit on your board. The TA will experiment with your circuit to see if it operates correctly under a number of circumstances (i.e. the TA will test the transmission of several different characters).
*/