everything is explained in the brief if you have further questions please ask. The software we are using in the lab is Xilinx Vivado version 2022.2. the answer is divided into 3 sections report, video to demonstrate the working hardware and upload design files. I have the hardware or the kit at home so we can work together.
Digital Design
Lab 6
Reading Values from an Onboard Sensor
1
Introduction
In this lab we will get more experience of using the Vivado tools to create a soft system-on-chip
system that measures the temperature using the onboard temperature sensor. We’ll also look at
how to use the GPIO controllers to drive the 7-segment display with signals generated in software.
The FPGA board has an ADT7420 temperature sensor in its bottom right hand corner. This is a device
that can measure temperatures in the range -40 C to +150 C. The ADT7420 communicates with the
FPGA through an IIC (inter-integrated circuit) bus. IIC is a low-cost bus that is commonly used in
embedded systems to communicate with peripheral devices. The FPGA design tools already have the
hardware and software needed for control of IIC built into their libraries, so our software will just
need to invoke these library functions with the appropriate parameter in order to achieve
communication. There can be many different peripherals on the IIC bus, and each peripheral is
distinguished by having a different address. The address of the ADT7420 is 0x4B.
2
Creating and synthesising the hardware
Start a new Vivado project, call it temp_sensor and associate with the board that you have been
issued (Nexys 4 DDR or Nexys A7). Follow the following steps to create the microprocessor system:
From the Flow Navigator, choose “Create Block Design”
In the “Sources” area, navigate to the “Board” tab to see the resources that are available on
your FPGA board that can be placed into your block diagram
Drag the “System Clock” onto the block diagram
When the “Clocking Wizard” has been placed in your design, double click on it to edit its
parameters. (Make sure that you double click on the body of the Wizard, not on the text
associated with its ports). Navigate to the “Output Clocks” tab, scroll to bottom of the tab, and
set reset to active low. The reset’s name should now change to resetn and an inversion circle
should now appear in front of the resetn port on the symbol.
Run connection automation (to connect the clock wizard’s resetn input to the “reset” input of
the board)
Click on the + icon (or type CTRL-I) to add IP, select Microblaze and place it into your design.
Run Block Automation and increase the microprocessor’s “Local Memory” to at least 32 KB.
Run connection orientation (to connect the processor reset system to the board’s reset pin)
Now we will place the peripherals. The order in which you place them will affect the way that they
are numbered in the software interface, so stick to the following order so that the code listings in
this lab sheet will work without needing modifications:
Drag “16 LEDs” onto the diagram
Drag “8 Anodes” onto the diagram (this is the digit select for the 7-segment display)
Drag “7 Segments” onto the diagram (this lights up the segments for each digit of the display)
Drag “5 Push Buttons” onto the diagram
Drag “USB UART” onto the diagram
Drag “Temp sensor” onto the diagram
Run connection automation and accept all suggestions.
Your diagram is probably a mess now, so regenerate the layout using the ⟳ tool
The resulting system should look something like this:
Interfaces view
To make the diagram easier to read, switch to “Interfaces view”.
Validate Design
The Microblaze processor communicates with peripheral hardware blocks by writing to and reading
from addresses. If you select the “Address Editor” tab, you can see the addresses that have been
allocated to each peripheral. The important one for this lab is the addtess for the IIC controller,
which is at a block of addresses starting from 0x40800000
Check the block diagram for errors by clicking the “Validate” tool. Before we can synthesise, we need
to add a VHDL top level wrapper around the block diagram. Right click on the name of your block
diagram and choose “Create HDL Wrapper”
Synthesise by clicking on “Generate Bitstream”. This will take maybe 5-7 minutes. When synthesis is
finished, you will see a dialog box saying “Bitstream Generation Completed” and providing
suggestions for what to do next. None of the suggestions provided is appropriate, so click “Cancel”.
Now that the synthesised hardware is complete, we need to export it to a file that the software tools
can use. You can do this from the File menu by selecting “Export” then “Export Hardware “. Accept
the default file name and location for the file, and make sure that you select the “Include bitstream”
option.
3
Creating the hardware/software platform
Now start up the software development kit (SDK). You can do this from the Vivado “File” menu,
selecting “Launch SDK”. You will eventually see the project summary screen:
From the “File” menu, select “New” then “Application project”. We’ll initially set the environment up
with a simple “Hello world” program, and then subsequently alter this program to achieve our goal.
Choose “Hello world and click “Finish”. A “Hello world” project will be set up for you:
We will need a terminal on the serial port to monitor output from our design. At the bottom of the
SDK screen, choose the “SDK Terminal” tab and then click on the green + icon. This will bring up a
window to define the parameters for a serial port connection:
The baud rate should be 9600 baud and you will need to figure out what COM port has been
automatically allocated on your computer.
Program the FPGA with your hardware design by clicking on the “Program FPGA” button on the
toolbar and then clicking “Program” in the resulting pop-up menu.
Program FPGA
Build the software project and run it on the FPGA hardware. The hammer icon is used to build the
software. The green button with an arrow launches the software.
Run software
Build software
Before you use these icons, you need to click (in the Project Explorer) on the name of the project
that you want to build (temp_sensor). The first time in a session that you launch the software, you
will need to show how you want it to launch: “Run As”, then “Launch on Hardware (System
Debugger). If everything has been set up correctly, you should see “Hello world” on the terminal.
4
Driving the display
In this section, we will illustrate the use of the 7-segment display. Finishing off the display and
making it user-friendly will be the subject of the lab exercises and the assignment.
A segment is turned on by a 0 and turned off by a 1. So, for example, if we want to display the
number 2:
0
5
1
6
4
2
3
7
We would need to turn on segments 0, 1, 3, 4, 6 and turn off the others. In binary this means we
would drive it with
segments = 0b10100100;
or in hexadecimal
segments = 0xA4;
We can incorporate the segment pattern required to represent any number in the range 0 to 15 (0x0
to 0xF) as an array:
uint8_t segments[] = {0xC0, 0xF9, 0xA4, 0xB0, // 0, 1, 2, 3
0x99, 0x92, 0x82, 0xF8, // 4, 5, 6, 7
0x80, 0x90, 0x88, 0x83, // 8, 9, 10, 11
0xC6, 0xA1, 0x86, 0x8E}; // 12, 13, 14, 15
And then if we want to find the values needed to drive the display to represent a number, we use:
segments[number]
For example, if number==2, then segments[number] is 0xA4, as required to display the
number 2.
The following code demonstrates how we could make the last digit on the display go through a
count sequence. Paste it into helloworld.c, then run it on your FPGA:
/**** Program to demonstrate 7-segment display *************/
#include “xgpio.h”
#include “sleep.h”
/**** Global variables *******************/
XGpio gpio_0, gpio_1;
// GPIO controller data structures
uint8_t segments[] = {0xC0, 0xF9, 0xA4, 0xB0, // 0, 1, 2, 3
0x99, 0x92, 0x82, 0xF8, // 4, 5, 6, 7
0x80, 0x90, 0x88, 0x83, // 8, 9, 10, 11
0xC6, 0xA1, 0x86, 0x8E}; // 12, 13, 14, 15
/**** Main function **********************************************/
int main(void)
{
// GPIO controller 0: channel 1 is LEDs, channel 2 is segments
// GPIO controller 1: channel 1 is digit select
XGpio_Initialize(&gpio_0, 0);
XGpio_Initialize(&gpio_1, 1);
XGpio_SetDataDirection(&gpio_0, 2, 0x00000000); // set segment to
output
XGpio_SetDataDirection(&gpio_1, 1, 0x00000000); // set digits to output
while(1) {
// Loop forever
for (int i=0; i 3;
return temp;
}
The temperature value will be displayed (as a binary multiple of 0.625°C) on the board’s LEDs:
After download, you may need to press the “CPU reset” button on the board before this will work
correctly and give you readings. In this example, our reading is 110000110, which means 24.375° C
(390 × 0.0625). Try putting your finger on the temperature sensor to make the temperature change.
5.4
Understanding the software
A lot of useful information is hidden away in header files. We can inspect the content of these files
by clicking on the name and then hitting key F3. For example, click on the name “xparameters.h” and
hit F3:
This will bring up a copy of the xparameters.h file in the editor. This contains symbolic constants that
describe all of the data and address information used in your hardware setup. Search down the
xparameters.h file for “IIC”:
You can see that the address of our IIC controller (which called AXI_IIC_0 in our block diagram) has
been given the symbolic name “XPAR_IIC_0_BASEADDR”. This is the address that we saw in the
address editor of the block diagram, 0x40800000.
The Microblaze API has a high-level function XIic_Recv that will carry out the read. Our code that
will read the temperature is:
#include “xiic_l.h”
uint8_t ReceiveBuffer[2];
XIic_Recv(XPAR_IIC_0_BASEADDR, // Microblaze address of IIC
0x4B,
// IIC bus address of sensor
ReceiveBuffer,
// Where will we put the read data
2,
// How many bytes do we read
XIIC_STOP);
// What to do after 2 bytes read?
int temp = ((ReceiveBuffer[0] > 3;
Let’s have a look at what each of the parameters for XIic_Recv means:
XPAR_IIC_0_BASEADDR : Each device that we place in our Microblaze system has an address in
the memory map. When we created our block diagram, addresses were automatically assigned.
The values of these address are made accessible to the software through the xparameters.h
header file. Within this file, XPAR_IIC_0_BASEADDR is a symbolic constant whose value is
0x40800000, the address of IIC controller 0.
0x4B: this is the address of the sensor on the IIC bus
ReceiveBfr: IIC sends data one byte at a time. ReceiveBfr is an array of 2 8-bitbytes that are
used to receive the data as they arrive on successive bus cycles.
2, XIIC_STOP: When we set up the transfer, we need to say how many bytes we want to be
transferred (2 in this case). We also need to say what should happen after the two bytes are
sent: do we want to terminate this transaction or leave the IIC ready to continue further
transfers within the same transaction. XIIC_STOP is a symbolic constant (read from header file
xiic_l.h) that indicates that we want to end the IIC transaction after this read is complete.
We receive the data as two separate bytes, which are read on successive bus cycles into the array
ReceiveBuffer. We need to merge these two bytes into a single temperature value, by shifting the
zeroth received item left by 8 buts and then adding it to the first received item:
mergedValue = (ReceiveBuffer[0] 3;
6
Exercises
So now we are doing temperature measurements. But the way we are displaying our data is not easy
to understand for a casual user:
The LEDs show that our reading is 110000110, which means 24.375° C (390 × 0.0625). It would be
much clearer to display this result on the 7-segment display. Binary 0b110000110 is hexadecimal
0x186.
6.1
Add a 7-segment display onto your design that will display the temperature to the user as
follows:
6.2
The solution of 6.1 still isn’t very clear for our user. Convert the hexadecimal reading to
degrees Celsius (0b110000110 is 24.375 Celsius) and round to the nearest integer. You can find
information about conversion to base 10 in the appendix at the end of this lab sheet. Modify your
display to show the result:
6.3
Now add a unit to your display to indicate that the temperature is displayed in celsius:
Appendix
Converting to base 10
The standard way to convert from one number base to another is to repeatedly do integer division
by the new number base, and the remainders that are generated are the digits of our number. For
example, suppose we want to convert 0xA56 to base 10 (which is 0xA in hexadecimal:
0xA56 ÷ A = 0x108 remainder 6
0x108 ÷ A = 0x1A remainder 4
0x1A ÷ A = 0x2 remainder 6
0x2 ÷ A = 0x0 remainder 2
So 0xA56 is 2,626 in base 10.
In the C language, the operator that does integer division is / and the operator that computes the
remainder after division is %. Suppose we have the following code:
int x, y, z;
x= 0xA56;
y = x / 10;
z = y % 10;
y would get the value 0x108 and z would get the value 6.
Installing Vitis/Vivado 2022.2
The version that we have installed in the lab is Vivado 2022.2. This contains Vivado for hardware
design and Vitis for software design. You will need to be careful how you install it in order to avoid it
taking up a ridiculously large amount of hard disk space, so follow the instructions below.
You will find the download at
https://www.xilinx.com/support/download/index.html/content/xilinx/en/downloadNav/vitis/20222.html.
Use the following option:
When the installer runs, select Vitis (which includes Vivado)
You will be prompted for which components you want to install. Many of these components are of
no use to you and take up a huge amount of disk space. Select the options shown below, then click
“Next”
The final install screen should look like this: