0% found this document useful (0 votes)
73 views

ECE 315 Computer Interfacing Lab #2: Serial Interfacing Using The Zynq-7000 UART Interface

This document provides instructions for Lab #2, which involves designing serial communication systems using the UART interface on the Zynq-7000 SoC. The lab has two exercises. Exercise 1 involves designing a Morse code decoder that receives encoded text from a PC over UART, decodes it using three FreeRTOS tasks, and sends the decoded text back. Exercise 2 enhances an interrupt-driven UART driver for transmitting and receiving bytes to/from the PC console using queues and interrupts. Students must demonstrate their designs the following week and submit a lab report by the due date.

Uploaded by

Shawn Shade
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
73 views

ECE 315 Computer Interfacing Lab #2: Serial Interfacing Using The Zynq-7000 UART Interface

This document provides instructions for Lab #2, which involves designing serial communication systems using the UART interface on the Zynq-7000 SoC. The lab has two exercises. Exercise 1 involves designing a Morse code decoder that receives encoded text from a PC over UART, decodes it using three FreeRTOS tasks, and sends the decoded text back. Exercise 2 enhances an interrupt-driven UART driver for transmitting and receiving bytes to/from the PC console using queues and interrupts. Students must demonstrate their designs the following week and submit a lab report by the due date.

Uploaded by

Shawn Shade
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 9

ECE 315 Computer Interfacing

Lab #2: Serial Interfacing Using the Zynq-7000 UART Interface


Winter 2022

Lab Dates, Demo Due Dates, and Report Dates


This lab exercise will be held on February 15 (LAB H21), February 16 (LAB H31), February 17 (LAB
H41), and February 18 (LAB H51). You must demonstrate your designs to a TA at the beginning of
the first session of Lab #3 (Mar. 1, 2, 3 and 4 for sections H21, H31, H41 and H51, respectively).
The lab report must be uploaded by 11:00 pm on the same day that the demonstration is due for
your lab section.

Objectives
• To gain experience with using an ASCII-encoded serial communications connection
between a PC host and the Zybo Z7 microcomputer board.
• To gain experience using the UART interface in the Zynq-7000 SoC.
• To gain experience with interfacing using both polling and hardware interrupts.
• To gain experience using queues to decouple the execution of software tasks.

Hardware Platform and Software Environment


• The hardware platform is a Digilent Zybo Z7 development board. As with all the labs in ECE
315, CPU0 will run the FreeRTOS real-time kernel while CPU1 will be left disabled.
• The fixed Zynq-7000 SoC hardware configuration and the initial skeleton source files for
Exercises 1 and 2 must be downloaded from the Lab #2 section of the lab eClass site. The
skeleton source files contain the initial application code in C that you will be modifying to
implement your designs in the two exercises.

Documentation
• Digilent Zybo Z7 Reference Manual: https://reference.digilentinc.com/reference/
programmable-logic/zybo-z7/reference-manual
• The FreeRTOS Reference Manual : https://www.freertos.org/fr-content-
src/uploads/2018/07/FreeRTOS_Reference_Manual_V10.0.0.

1
• Documentation for Xilinx's UART driver functions:
https://xilinx.github.io/embeddedsw.github.io/uartps/doc/html/api/globals.html

• Morse Code Chart :


https://www.electronicsnotes.com/articles/ham_radio/morse_code/characters-table-
chart.php

Note
Please read the initial comments and explanations provided in the source and header files for
Exercises 1 and 2.

Laboratory Exercise Instructions


Exercise 1: Design of a Morse code decoder system using polled UART interfacing
The Morse code is a character encoding scheme where each alphabet, number or special character
is encoded using a series of dots and dashes. Morse code was mainly used in telegraph, maritime
shipping, and navigation aids to communicate over long distances.

A Morse code chart comprising letters, numbers and special characters is provided at the end of
this manual as well as in the form of a separate header file that can be downloaded from the
eClass. For example, the Morse code encoding for WELCOME EVERYONE is .--|.|.-..|-.-.|---|--|.|
.|...-|.|.-.|-.--|---|-.|.|.-.-.-| (see the Morse code chart attached in the manual).

The objective of this exercise is to design application software for the Zybo Z7 board that (1)
receives Morse code messages (up to 500 characters long) using only dot, dash, and vertical bar
characters from the console terminal on the host PC over the serial connection, then (2) decodes
the Morse code message into its original alphanumeric message, and then (3) sends the decoded
message back to the console. The user can type in one or more lines of text into the console. When
they have finished typing in their morse encoded message, they press the ENTER key. Then the
user must type a # character at the start of the new line and then again press the ENTER key. At
that point, the input Morse encoded message is processed by the FreeRTOS task and the decoded
message is returned by the Zybo Z7 board over the serial connection and then displayed in the
console window on the host PC.

The software system is to be built using three tasks called TaskMorseMsgReceiver,


TaskMorseMsgProcessor and TaskDecodedMsgTransmitter. TaskMorseMsgReceiver’s job is to
receive a Morse code message from the UART1 interface using the polling method (operating at a
polling frequency of 50 Hz), and to write those characters into a 20-character-long queue of
unsigned 8-bit characters. The queue, called Queue12, is to be used to communicate characters

2
from TaskMorseMsgReceiver to TaskMorseMsgProcessor. TaskMorseMsgReceiver can be
implemented using the Xilinx’s UART driver functions XUartPs_RecvByte() or XUartPs_ReadReg().

TaskMorseMsgProcessor is responsible for decoding the Morse code message into the original
English text message. This task has a blocking read on Queue12 using the FreeRTOS function
xQueueReceive() to receive the incoming characters. When each character is received, it is to store
the character into a private array that can hold up to 500 characters. When the terminating
character sequence (namely "\r#\r") is detected, the TaskMorseMsgProcessor is to start
decoding the received Morse code sequence into the original message. This decoded message will
be then written into a 30-character-long queue, called Queue23, using the FreeRTOS function
xQueueSendToBack(). Queue23 is to be used to pass characters from TaskMorseMsgProcessor to
TaskDecodedMsgTransmitter. TaskMorseMsgProcessor must take care of the situation when the
user inputs more than 500 characters from the console. If the received message exceeds the
capacity of the private array, TaskMorseMsgProcessor is to abandon the message received so far
and is to send back to the user, through Queue23, the error message "Maximum message
length exceeded. Message ignored.\r Type in the termination sequence to
translate the 'excess' characters that went above the array limit and
caused overflow.\r\r\n".
TaskDecodedMsgTransmitter is to use the 50 Hz polling method to send processed messages back
to the console window on the host PC. The task is to use the Xilinx UART driver functions
XUartPs_IsTransmitFull() and XUartPs_SendByte().

Exercise 2: Interrupt-driven Input and Output Using the UART


Note: Along with the explanation in this lab handout, please read the comments provided at the
beginning of the source code files.
In this exercise you will be modifying and enhancing an interrupt-driven driver for UART1 in the
Zynq-7000 SoC.
The main objective of this exercise is to gain experience designing using the interrupt-driven
method to receive bytes from the SDK console in through UART1, and the interrupt-driven method
to transmit bytes to the SDK console out through the same UART. The skeleton driver,
uart_driver.h, is responsible for writing received bytes to the back of one queue (for the receive
direction) and reads bytes to be transmitted from the front of a second queue (for the transmit
direction). However, in your improved driver design, these two queues will be hidden from the
user tasks since they are implementation details that do not need to be disclosed to code outside
the driver. You also want to provide more efficient interrupt-driven operation in both the receive

3
and transmit directions. Four new driver functions will thus be designed in this exercise, to be
called MyIsReceiveData(), MyReceiveByte(), MyIsTransmitFull(), and
MySendByte(). The initial code for these four functions has been provided in the driver file
uart_driver.h on the eClass.
The file initialization.c contains the functions that initialize the UART and the Zynq Interrupt
System. The file initialization.h has constants, function prototypes, UART and Interrupt Instances.

File hierarchy :

Detailed definitions of these new functions, as well as the required changes to the interrupt service
routine are given in the steps below.

Step 1: Go to the lab website and download the provided skeleton software design for Exercise 2.
Start up Vivado and open the unchanged hardware design for the Zynq-7000 SoC. Then export the
design to Vivado SDK and use the provided skeleton software to create a new application project
for Exercise 2. Running this project just with the provided initial code will not display any results
on the SDK console.

Step 2: The given driver software is to be modified so that users do not see the receive direction
queue, which should be made a private data structure that is not disclosed to the user tasks. You
are to design the following two new functions that user tasks are to use to receive bytes:

BaseType_t MyIsReceiveData( void ) is to return pdTRUE if there is newly received, data;

otherwise, the function is to return pdFALSE. Function u8 MyReceiveByte( void ) is to return

the oldest 8-bit value that has been received by UART1 but not yet read by the user. The data is

read from the front of the receive queue and returned as the u8 return value for the function.

Be sure to protect any critical sections in the two new receive functions by using the FreeRTOS
macros taskENTER_CRITICAL() and taskEXIT_CRITICAL().
4
Step 3: Further modify your driver software by adding the code at necessary places for data
handling in the transmit and receive directions of the interrupt-driven method. An initial code has
been provided in the Interrupt Service Routine (ISR). That ISR must now be fully completed to
handle interrupts for both the receive and transmit directions. The changes to be made for
handling the receive interrupts include: (1) implementing the function to receive any bytes from
the UART, (2) sending these received bytes to the private receive queue data structure from the
ISR. Other changes to be made for handling the transmit interrupts include: (1) implementing the
mechanism for turning transmit interrupts on and off, (2) making the transmit queue a private data
structure (accessible only to the driver software) that is not disclosed to the user tasks, and (3)
providing two new driver functions for the transmit direction.

The TEMPTY (Transmitter Empty) UART interrupt status bit alone is to be used to trigger interrupts
in the transmit direction. The transmit direction queue is to be made a private data structure, like
the receive direction queue, which user tasks cannot access. Data is available to transmit if the
transmit direction queue is not empty; in that case the TEMPTY bit in the Interrupt Enable Register
should be 1, and the TEMPTY bit in the Interrupt Disable Register should be 0. On the other hand,
data is not available to transmit if the transmit direction queue is empty; in that case the TEMPTY
bit in the Interrupt Enable Register should be 0, and the TEMPTY bit in the Interrupt Disable
Register should be 1. It is not a good idea to require a user task to be responsible for enabling
TEMPTY interrupts whenever data is written to the transmit queue. Rather, enabling transmit
interrupts should be done automatically by the transmit driver function. You are to design the
following two new driver functions for the transmit direction:

BaseType_t MyIsTransmitFull( void ) is to return the value pdTRUE if the transmit queue

data structure is full; otherwise, the function is to return pdFALSE. void MySendByte( u8
Data ) is to write a byte of data to the back of the transmit queue and is to enable TEMPTY

interrupts by setting to 1 the TEMPTY bit in the interrupt mask, which is to be done as described
below. Note that the TEMPTY bit in the interrupt mask is cleared back to 0 later by the ISR.

Be sure to protect any critical sections in the two new transmit functions by using the FreeRTOS
macros taskENTER_CRITICAL() and taskEXIT_CRITICAL().

When a TEMPTY interrupt has occurred, the ISR must check the transmit queue data structure and
the transmitter FIFO (TxFIFO) to see if more data bytes can be transferred from the transmit queue
into the TxFIFO to keep UART1's transmitter busy.

5
(1) If the ISR finds that the transmit queue is not empty but the TxFIFO is full, then the TEMPTY
interrupts shall be left enabled and no bytes shall be transferred from the transmit queue. The full
condition of the TxFIFO shall be checked by calling the following Xilinx-provided macro:
XUartPs_IsTransmitFull( u32 BaseAddress ), which returns TRUE if the TxFIFO does

not have room for another byte and returns FALSE if there is room.

(2) If the ISR finds that the transmit queue is not empty and the TxFIFO is not full, then the byte
at the front of the transmit queue shall be read from the queue, and then written into the TxFIFO
using the following macro from Xilinx:
XUartPs_WriteReg( u32 BaseAddress, XUARTPS_FIFO_OFFSET, u8 Data );

Note that the value of the first argument of the two macros above is the base address of UART1
(used elsewhere in the skeleton driver software) and the second argument is a constant that is
defined in Xilinx's header file xuartps_hw.h. The process of transferring bytes from the transmit
queue to the TxFIFO is to be repeated until either the transmit queue becomes empty or the TxFIFO
becomes full.

(3) If the transmit queue is found by the ISR to be empty, then the TEMPTY interrupts shall be
disabled. The Xilinx driver functions XUartPs_GetInterruptMark() and
XUartPs_SetInterruptMask() as well as the binary vector XUARTPS_IXR_TXEMPTY should
be used to set or clear the TXEMPTY interrupt mask bit using bitwise operators in (e.g., bitwise
AND using &, and bitwise OR using |).
Step 4: Modify the interrupt service routine to update two global counter variables, to be called
CountRxIrq and CountTxIrq. These counters are to be initalized to zero when the application

code first starts running, and also cleared to zero after the sequence "\r%\r" has been received
from the SDK console. In the ISR, CountRxIrq is to be incremented by 1 each time that a receive
interrupt is processed, and CountTxIrq is to be incremented by 1 each time that a transmit
interrupt is processed.

Step 6: Modify the main source file, part2_lab2_main.c file to call the four new functions
MyIsReceiveData(), MyReceiveByte(), MyIsTransmitFull(), and MySendByte().

There is also a counter that counts the number of bytes processed by the system, Countbytes.
This counter needs to be initialized to zero whenever the application starts running and also needs
to be cleared to zero after the sequence "\r%\r" has been received from the SDK console.

6
The following three status messages will be displayed after the end-of-block sequence "\r#\r"
has been received.
Number of bytes processed: 40
Number of Rx interrupts: 32
Number of Tx interrupts: 25

Other changes to be made inside part2_lab2_main.c file include: (1) Change the capitalization of
the characters received from the SDK console. However, everything except the alphabetical
characters remains unchanged. (2) Implement the mechanism that detects "\r%\r" sequence.
After the sequence has been received, clear the variables CountRxIrq, CountTxIrq and
Countbytes to zero.

Questions to be answered briefly in the laboratory report:

1. Justify the need for each of the critical sections that you identified in the driver functions.
What could happen if a critical section is not protected?
2. Why should user tasks not be required to enable and disable interrupts in either the receive
or transmit directions?
3. In the interrupt service routine, does it matter if the transmit interrupts are handled before
the receive interrupts, or vice versa? Is there is a better order for handling interrupts in
the receive and transmit directions? Explain why one order is better.
4. Why must transmit interrupts be disabled when there is no more data to transmit? What
would happen if transmit interrupts were to be left enabled in that case?
5. Can receive interrupts be left on, or should they ever be disabled?
6. Justify the numbers that you found being produced by the three status messages for the
blocks of characters that were send to the Zybo Z7 board. If possible, use a small example
to explain these numbers.

Demonstrations
The marks for Exercises 1 and 2 include a demonstration that you will give to a TA at the start of
Lab #3. The TA will be providing their own blocks of characters to be copied into the console
window and transmitted to the Zybo Z7, with the results output back to the console. The
demonstration is worth 55% divided equally between Exercise 1 and 2.

7
Report Requirements
Your report must include the final versions of the commented source files that you used to
implement Exercises 1 and 2. Your report should briefly describe your two final designs and
include diagrams that illustrate the control relationships and data flows among the various
functions, data structures, tasks and (for Exercise 2) the interrupt service routine. Your report
should also answer the questions that are given at the end of Exercise 2.

The report must take the form of one file in pdf format that is uploaded by the report deadline to
eClass. Submit the code files that you have changed along with the report. Your added code should
contain a suitable number of comments to explain the algorithm, if required.

Marking Scheme
The form and general quality of the report (clarity, organization, tidiness, spelling, grammar,
sufficient explanatory comments in the code) will be considered as well when grading the reports.
The report is worth 45% of the lab.

Morse Code Chart


Character Morse Code
A .-
B -...
C -.-.
D -..
E .
F ..-.
G --.
H ....
I ..
J .---
K -.-
L .-..
M --
N -.
O ---
P .--.
8
Q --.-
R .-.
S ...
T -
U ..-
V ...-
W .--
X -..-
Y -.--
Z --..
1 .----
2 ..---
3 ...--
4 ....-
5 .....
6 -....
7 --...
8 ---..
9 ----.
0 -----
, --..--
. .-.-.-
? ..--..
@ .--.-.
: ---...
; -.-.-.
- -....-
‘ .----.
Unknown Character *

A Space will be treated as a space.


Every character will be separated by | symbol.

You might also like