In this lab you will implement a UART (Universal Asynchronous Receiver / Transmitter) device, otherwise known as a serial interface. This task will tie together the tools you have learned in the past 4 labs and will provide a design experience similar to the course project. In addition, your working UART from this lab will be used in your project as part of checkpoint 1.
You are permitted to work with a partner for this lab. It is recommended, but not required, that you work with the person that you intended to work with for the project.
For the prelab, do the following:
You are responsible only for implementing the transmit side of the UART for this lab. We have given you a complete receive side. You would be wise to model your solution after the design we have given you that implements the receive side because they will end up being quite similar.
Recall from lecture how serial devices work. On the ML505, the physical
signaling aspects (such as voltage level) of the serial connection are taken
care of by off-FPGA devices. From the FPGA's perspective, there are two signals,
FPGA_SERIAL_TX, which correspond to the receive-side and
transmit-side pins of the serial port. The FPGA's job is to correctly frame
characters going back and forth across the serial connection. The figure below
shows a single character frame and will be extremely useful in understanding
In the idle state the serial line is held high. When the TX side is ready to send a character, it pulls the line low. This is called the start bit. Because UART is an asynchronous protocol, all timing within the frame is relative to when the start bit is first sent (or detected, on the receive side). The frame is divided up in to 10 uniformly sized bits: the start bit, 8 data bits, and then the stop bit. The width of a bit in cycles of the system clock is then naturally given by the system clock frequency divided by the baudrate. Notice that both sides must agree on a baudrate for this scheme to be feasible.
Let us first think about sending a character using this scheme. Once we have a character that we want to send out, transmitting it is simply a matter of shifting each bit of the character, plus the start and stop bits, out of a shift register on to the serial line. Remember, the serial baudrate is much slower than the system clock, so we must wait SymbolEdgeTime = (ClockFreq / BaudRate) cycles between changing the character we're putting on the serial line. After we have shifted all 10 bits out of the shift register, we are done.
The receive side is a bit more complicated. Fortunately for you, it is already
implemented and is given to you as part of the lab distribution. You may want to
lab5/src/UAReceive.v and follow along as you are reading this section.
Like the transmit side, the receive side of the serial device is essentially
just a shift register, but this time we are shifting bits from the serial line
into the shift register. However, care must be taken into determining when to
shift bits in. If we attempt to sample the serial signal directly on the edge
between two symbols, we are exceedingly likely to sample on the wrong side of
the edge (or worse, when the signal is transitioning) and get the wrong value
for that bit. The correct solution is to wait halfway into a cycle (until
SampleTime on the diagram) before reading a bit in to the shift register.
One other subtlety of the receive side is correctly implementing the ready/valid interface. Once we have receive a full character over the serial port, we want to hold the valid signal high until the ready signal goes high, after which the valid signal should be low until we receive another character. This requires using an extra flip-flop that is set when the last character is shifted in to the shift register and cleared when the ready signal is asserted. This allows us to correctly implement the ready/valid handshake.
Although the receive side and transmit side of the UART you will be building are
essentially orthogonal, we are packaging them into one
UART module to keep
things tidy. If you look at
UART.v, you will see that this module is mostly
straightforward instantiations of
UATransmit, but there are
IORegisters that the serial lines are fed through. What are these
IORegister is simply a register that attempts to pack itself into a
special block called a
IOB, which are used to drive and sense from the IO
pins. Using an
IORegister helps ensure that you will have a nice, clean,
well-behaved off-chip signal to use as an input or output to your serial
You will use tools that you have learned in labs 1-4 to implement and test this lab. Make sure to refer back to these old labs if you have any questions about how to run any of these tools.
We have provided a simple testbench, called (predictably)
Testbench that will
run some basic tests on two instantiations of the
UART module with their
TX signals crossed so that they can talk to each other. There is also a
.do file provided that will run the test. You should note that this test bench
reporting success is not by itself a good indication that your UART is
working properly. Do to the way
x's are treated by Modelsim if a large number
of signals in your design are undefined (which they likely will be due to typos)
the testbench may erroneously pass. Make sure to look at the waveform to see
that everything appears to be working properly.
Your UART will eventually be used to interact with your CPU from your
workstation. Without a CPU, you need some other way to test that your UART works
on the board. We have provided this for you. The provided
contains a very simple finite state machine that simply send ever character it
receives back over the serial device. This will be extremely useful for testing.
Before programming your board, check with the provided
that everything works as it should in simulation.
Once you have echo working in simulation, it is time to try it on the board. Synthesize your design and program the board with it just like you have done in previous labs. Now, make sure the serial cable is plugged in between the ML505 and your workstation and then run
% screen $SERIALTTY 115200
screen, a highly versatile terminal emulator, to open up the serial
device with a baud rate of 115200. Now, if you have a properly working design,
you should be able to tap a few characters into the terminal and have them
displayed on the screen as they are echoed back to you.
screen, type Ctrl-a then shift-k and answer y to the confirmation prompt.
If you don't close screen properly, other students won't be able to access the serial port. Use
screen -x to re-attach an improperly closed screen session.
DataOutReadybeing set in between? You will have to look at the code for the instructor's receive-side module to answer this question.
Github authenticates you for access to your repository using ssh keys. You can
generate a ssh key using the
ssh-keygen command. Accepting the defaults
should be fine.
Once you have a key generated, go to your github account
settings. Then go to the
SSH Public Keys menu.
Add another public key. In this dialog insert the contents of
your public key file in to the
Key box. By default the public key file
will be in
~/.ssh/id_rsa.pub. Make sure you don't accidentally copy the
contents of the
~/.ssh/id_rsa file. This is your private key, which is
equivalent to your password. After you paste your key in, give it a name and
your are done.
The skeleton files for the project will be available through a git repository provided by the staff. The suggested way for initializing your repository with the skeleton files is as follows. First, set up your ssh keys as described above. Then,
% git clone email@example.com:EECS150/skeleton.git
% cd skeleton
% git remote add my-repo firstname.lastname@example.org:EECS150/teamX.git
% git push my-repo master
This will make a single commit to your repository with the base files, we suggest you then do the following,
% cd ..
% rm -rf skeleton
% git clone email@example.com:EECS150/teamX.git
% cd teamX
% git remote add staff firstname.lastname@example.org:EECS150/skeleton.git
This will delete the skeleton repository you cloned, clone your repository that now has a single commit, and add a remote repository named staff that points to the skeleton files to allow easy future merges of staff updates.
For the next part, if you are working with a partner, only one of you will need to do this. Here are commands to add the modules from this lab implementing UART to your git repository. Make sure to first change into the hardware src directory of the skeleton files. Similarly, you will want to add the ALU and ALUdec you wrote in Lab3 to your repository.
% cp ~/lab5/src/UATransmit.v .
% git add UATransmit.v
% git commit -m "Adding UART transmit module"
% git push