Lab 2 – Introduction to Xilinx Vivado and Digilent Basys 3
In this lab, you will be introduced to:
•
The Digilent Basys 3 development board equipped with a Xilinx Artix7 FPGA. The
term FPGA stands for Field Programmable Gate Array. FPGAs are programmable
ICs which incorporate a large number of logic blocks and interconnects which can
be electronically switched (programmed) so as to wire up the logical blocks
together in a certain way in order to perform the function of a circuit normally
involving many logic gates and flip-flops. We can thus produce any circuit that is a
combinational or sequential function solely with an FPGA. There are many types
of FPGAs in the market, each differ in electrical and physical characteristics such
as speed, capacity and power consumption. The Digilent Basys 3 development
board offers numerous I/O interfaces integrated onboard, which makes it ideal for
an introductory course such as this one.
•
Designing combinational circuits using logic gates from a minimised Boolean
function.
•
How you can use Verilog to implement a digital circuit design in Xilinx Vivado.
•
How you can simulate the operation of your digital circuit design using Vivado.
Prelab (must be completed before coming to the lab)
1. Complete the following Verilog numbers for representing the decimal number
11:
4’b __ __ __ __
4’d __ __
2. For the above two Verilog numbers, why do they both have a 4 at the beginning?
1
3. What is a Verilog Testbench?
4. How would you implement the Boolean function Y = AB + C in Verilog using an
assign statement?
Designing a Logic Circuit in Vivado
In this lab, we will design and simulate a combinational circuit with the following
specification:
Design Specification
Your lecturer has asked you to implement a climate-control system for their house.
Your task is to design a climate-control system for a house with three binary heat sensors
located in the Kitchen (K), Living Room (L), and Bedroom (B). Each sensor outputs HIGH
(1) if the room's temperature exceeds the preset target and LOW (0) otherwise.
The system has two outputs: Heating (H) and Cooling (C).
•
If more sensors report LOW than HIGH, set H = 1 and C = 0.
•
If more sensors report HIGH, set H = 0 and C = 1.
2
Before we start creating this project on Vivado, let us translate the specification into a
truth table and obtain a Boolean function.
K
L
B
Heating (H)
Cooling (C)
0
0
0
0
1
0
1
0
0
1
1
0
2
0
1
0
1
0
3
0
1
1
0
1
4
1
0
0
1
0
5
1
0
1
0
1
6
1
1
0
0
1
7
1
1
1
0
1
Table 2.1:
Truth table for the design specification
The Boolean functions can be written as the sum of minterms:
Μ
πΏΜ
+ πΎ
Μ
π΅Μ
+ πΏΜ
π΅Μ
π» = ∑ π(0,1,2,4) = πΎ
Μ
= ∑ π(3,5,6,7) = πΎπΏ + πΎπ΅ + πΏπ΅
πΆ=π»
Question: Prove the Boolean equation for π» by solving the three variable K-map below:
3
Setting up Vivado
We will now implement this circuit in Xilinx Vivado
Launch Vivado on the desktop.
Now we need to create a new project. To do so:
1. Select File -> Project -> New, this opens the New Project Wizard.
2. Press Next.
3. Type Climatecircuit into the Project Name field and save it in an appropriate
location (ideally onto a personal USB).
4. Press Next to move to the Project Type page.
5. Select RTL Project and leave all other boxes unchecked.
6. Press Next to move to the Add Sources page.
7. Set Target Language and Simulator Language to Verilog and press Next.
8. Press Next to skip the Add Constraints page.
9. Select the Boards tab and search and select Basys3. If it is not there, click the
refresh button in the bottom-right corner, see Figure 2.1.
10. Click Next and then click Finish.
Figure 2.1:
Board selection
4
Implementing a Digital Circuit in Verilog
Verilog is a hardware description language. It is essentially a way for us to produce a
written description of our circuit, which Vivado is able to interpret similarly to how we
would interpret a schematic. It uses this to simulate our circuit and to configure the
FPGA to implement it. Coding in Verilog is quite similar to programming in C
syntactically. For the moment, we will introduce you to a few key concepts which will
allow you to write simple Verilog modules and Testbenches on your own.
Firstly, numbers are specified in Verilog in the format n’Bxxxx where n represents the
number of bits in the value (bits, not digits!), B represents the base and xxxx represents
the number in the specified base. For example – 8 bits of 1 could be notated as
8’b11111111. Likewise, binary 1100 could be written as 4’b1100. Examples of base B are
b for binary, d for decimal, and h for hexadecimal.
Secondly, assignments can be made to inputs similar to C. For example, to assign
11111111 to A, you simply write A = 8’b11111111; (to be accurate, this is only valid
inside a procedural block, as you will learn later in the course).
Thirdly, to specify a delay, use #xxxx; where xxxx represents the number of time steps
to delay for. The default time step is 1ns (configured in the `timescale 1ns/1ps line).
You will not need to change this. If a 500ns delay is required, simply put #500; on a line
of its own, and the simulator will delay for 500ns when it reaches that line. It should be
noted that this type of delay is non-synthesizable – that is, it is only valid in simulation,
not in physical hardware.
For the moment, we will only cover the basics of writing code in Verilog. You will learn
more detailed Verilog programming later in the course.
To create a Verilog file:
1. Select File -> Add Sources to open the Add Sources Wizard.
2. Check Add or create design sources and then press Next.
3. Press Create file and set the filetype to Verilog and the file name to
Climatecircuit.
4. Press OK then Finish to create the file.
5
5. If the Define Module window comes up, press OK then Yes.
Open the Verilog file by locating it under the Design Sources folder in the Sources
window and then double-clicking.
Figure 2.2:
Design source location
The generated file will contain an empty module. You can think of a module like an IC
chip that you place on a breadboard. A module has inputs and outputs (like the leads on
an IC chip) and performs some function. You can imagine that it should be possible to
design a module that performs similarly to the SN74HC93 IC binary counter chip from
Lab 1. It is important to note that a module defines behaviour – and thus we can use the
same module multiple times within a design, just as we could place multiple binary
counter ICs on a breadboard if we so desired. A module is not similar to a function in C.
The syntax for declaring a module is as follows:
module <name> ([port_list]);
endmodule
Figure 2.3:
Verilog module syntax
6
We can see our module has the name Climatecircuit, but currently our module has no
inputs or outputs – that is to say, no ports. We will need to define them within the
parentheses as shown in the figure below.
module Climatecircuit(
input K,
input L,
input B,
output H,
output C
);
endmodule
Figure 2.4:
Adding a port list
There are two main types of ports – input and output. Note that each port declaration
ends in a comma, with the exception of the final declaration. We’ve defined K, L and B as
inputs, and H and C as an output. To visualise this, consider our module as a black box as
per the figure below.
K
H
Climatecircuit
L
B
Figure 2.5:
C
Black box representation of our module
The assign keyword allows us to simply describe combinational circuits.
You can use many operators with an assign statement:
•
assign a = b&c; // AND
•
assign d = e|f; // OR
•
assign g = h^i; // XOR
•
assign m = ~n; // NOT (Bit-wise inversion)
If we were to implement the equation for π» using an assign, it would look like:
7
assign H = (~K & ~L) | (~K & ~B) | (~L & ~B);
Figure 2.6:
Basic assign example
Parentheses can also be used when combining multiple operators to ensure the order of
operations is correct.
Now return to the Climatecircuit project. We will use an assign statement for each
output. Fill in the body of your module such that it now looks like the code below:
`timescale 1ns / 1ps
module Climatecircuit(
input K,
input L,
input B,
output H,
output C
);
assign H = (~K & ~L) | (~K & ~B) | (~L & ~B);
assign C = . . . // Fill this in
endmodule
Figure 2.7:
Climatecircuit implemented with assign statements
Simulation in Vivado
We would like to simulate the circuit to verify its correct operation. For this purpose, we
need to create a Testbench. Select File → Add Sources and create a new Simulation
Source. Name it Climatecircuit_tb. Now open the file, it should be located inside the
Simulation Sources folder.
8
Figure 2.8:
Simulation source location
A Testbench allows us to specify a set of signals to be input to the Unit Under Test (UUT –
otherwise known as the circuit that you are testing. We can specify values to be given to
certain input ports of our design and specify delays between signal changes in order to
simulate and observe the result of our circuit.
For your Climatecircuit testbench, we want to have signals scheduled as provided in
Table 2.2.
Time (ns)
K
B
L
0 – 100
1’b0
1’b0
1’b0
100 - 200
1’b0
1’b0
1’b1
200 - 300
1’b0
1’b1
1’b0
300 – 400
1’b0
1’b1
1’b1
400 - 500
1’b1
1’b0
1’b0
500 - 600
1’b1
1’b0
1’b1
600-700
1’b1
1’b1
1’b0
700-800
1’b1
1’b1
1’b1
Table 2.2:
Testbench signals
9
Note that 1’b in front of a number indicates that the number is specified in binary, and is
1 bit; likewise, 4’h in front of a number indicates that the number is in hexadecimal and
is 4 bits. When writing numbers in Verilog we must use this format by specifying the
number of bits and base explicitly – i.e. 0b00010000 will become 8’b00010000 and
0xA5 will become 8’hA5.
In order to produce the above test pattern, we must code this into our testbench. First of
all, we must define the inputs and outputs of our circuit. In Verilog, inputs are of type
reg and outputs are of type wire (more on these types later…). We then instantiate
the module we need to test using the following notation:
<Module name> UUT ([port_list]);
Figure 2.9:
Testbench module syntax
We set input values inside an initial block. Delays between different combinations
are created using the #<delay> expression. Note that <delay> is in nanoseconds.
Now we can begin adding our signals to our test bench. We want our signals to be 1 bit
each and equal to zero, so we get K=1’b0; L=1’b0; B=1’b0;. This condition is held
for 100ns, so after the three lines, and on its own line, put #100;.
Now, we repeat the same idea again, this time assigning appropriate values to K, L and
B and setting up another delay. This time, add B = 1’b1; and #100;. There is no need
to assign new values to K and L as they stay constant throughout this time. Similarly,
continue by entering the appropriate lines for the other conditions.
After this, your Verilog test fixture should look like the following:
10
`timescale 1ns / 1ps
module Climatecircuit_tb();
// Inputs
reg K;
reg L;
reg B;
// Outputs
wire H;
wire C;
// Instantiates module
Climatecircuit UUT(K, L, B, H, C);
// Set inputs
initial begin
K = 1'b0;
L = 1'b0;
B = 1'b0;
#100; // 100ns delay
B = 1'b1;
#100;
L = 1'b1;
B = 1'b0;
#100;
B = 1'b1;
#100;
K = 1'b1;
L = 1'b0;
B = 1'b0;
#100;
B = 1'b1;
#100;
L = 1'b1;
B = 1'b0;
#100;
B = 1'b1;
end
endmodule
Figure 2.10:
Testbench for Climatecircuit
11
Note that the order of the ports (K, L, B, H, C) in the following module instantiation is the
same as they are defined in the module itself.
Climatecircuit UUT(K, L, B, H, C);
-----------------------------------------module Climatecircuit(
input K,
input L,
input B,
output H,
output C
);
Figure 2.11:
Port order when instantiating Climatecircuit
Now run your simulation by selecting SIMULATION -> Run Simulation -> Run
Behavioural Simulation from the bar on the left.
Figure 2.12:
Running a simulation in Vivado
If your simulation fails to run, reopen your test bench file and inspect it carefully,
correct any errors and save it. Ask a demonstrator if you are unable to resolve the issue.
Once XSim has opened, you may right-click on the simulation window and select Full
View to see all of your results. They should be similar to the image below. Note that the
order of the signals displayed may be different.
12
Figure 2.13:
Expected simulation waveforms
From the result, you can see that H and C have the same values as the truth table in
Table 2.1 for all input combinations, which verifies the correct operation of the designed
combinational circuit. Note that the Verilog test bench for Climatecircuit can be
rewritten in a more concise way as below.
13
`timescale 1ns / 1p
module Climatecircuit_tb();
// Inputs
reg K;
reg L;
reg B;
// Outputs
wire H;
wire C;
// Instantiates module
Climatecircuit UUT(K, L, B, H, C);
// Set inputs
initial begin
K = 1'b0;
L = 1'b0;
B = 1'b0;
forever begin
#100;
{K, L, B} = {K, L, B} + 1;
end
end
endmodule
Figure 2.14:
Concise testbench for Climatecircuit
The {...} operator concatenates the signals it operates on into a single data bus. For
example, if K=1’b1, L=1’b0, B=1’b1 then {K,L,B}=3’b101.
Your Turn
Your lecturer has decided to purchase another temperature sensor, which she will place
in her dining room. The input from this sensor will be D. You now need to modify
Climatecircuit to work with a fourth sensor. The basic operation of the circuit will
remain the same with one exception: If two sensors are HIGH and two are LOW, then we
should not heat or cool (H -> 0, C -> 0).
Let’s start by filling in the truth table below. Some entries have been done for you.
14
Question: Complete the truth table
K
L
B
D
Heating (H)
Cooling (C)
0
0
0
0
0
1
0
1
0
0
0
1
1
0
2
0
0
1
0
1
0
3
0
0
1
1
0
0
4
0
1
0
0
5
0
1
0
1
0
0
6
0
1
1
0
7
0
1
1
1
8
1
0
0
0
1
0
9
1
0
0
1
0
0
10
1
0
1
0
11
1
0
1
1
0
1
12
1
1
0
0
13
1
1
0
1
14
1
1
1
0
0
1
15
1
1
1
1
0
1
Table 2.3:
Modified Climatecircuit truth table
15
Question: Get the Boolean equations for H and C by completing the K-maps below:
π» = ∑ π(
)
π»=
πΆ = ∑ π(
)
πΆ=
16
Now modify your Climatecircuit.v design source and your Climatecircuit_tb.v
testbench to implement and simulate the modified design. Show a demonstrator your
simulation results for marking.
Note: As there is a 100ns delay between inputs, your testbench will take 1600ns to
simulate all 16 inputs. You may have noticed that the simulation only runs for 1000ns
by default, to increase this:
1. Right-click on Run Simulation under the Simulation tab in left navigation bar.
2. Click on Simulation Settings.
3. Select the Simulation Tab.
4. Change the parameter xsim.simulate.runtime to 1600ns.
5. Press Apply and then OK.
6. Note that you will need to close any currently running simulations for the
changes to be applied.
Figure 2.15:
Location of simulator settings in Vivado, and changing the simulation runtime
17
Instantiating Modules in Verilog
Your lecturer, having just won lottery, has now decided that she will purchase an
apartment building as an investment property. The building has 4 apartments, and each
one requires a climate control circuit like you just designed (she loved your work in the
previous tasks!). Rather than use 4 separate climate circuits, she would like to have a
single Climate Unit that controls all 4 apartments. The only change is that she would like
one more output STATUS that is high if any of the heating or cooling circuits or on. It is
your task to design such a unit.
Create a new Verilog source and call it Climateunit. It should have the following ports:
module Climateunit(
input K1, K2, K3, K4, // Kitchen
input L1, L2, L3, L4, // Living room
input B1, B2, B3, B4, // Bathroom
input D1, D2, D3, D4, // Dining room
output H1, H2, H3, H4, // Heat
output C1, C2, C3, C4, // Cool
output STATUS
);
// TODO: Add module content
endmodule
Figure 2.16:
Climateunit ports
It would be a huge hassle to redo the whole design – ideally we’d just take our
Climatecircuit module and use it 4 times. And that’s exactly what we’ll do. Remember
that Verilog is a Hardware Description Language – and so we are really describing a
circuit. And just like we can place 4 opamps on a breadboard, we can also place 4
Climatecircuits in our Climateunit.
We call this instantiation. To instantiate a module, we use the following structure:
<module_name> <instance_name>([port_list]);
Figure 2.17:
Module instantiation syntax
18
So if we wanted to instantiate our Climatecircuit in our Climateunit, we would write:
module Climateunit(
input K1, K2, K3, K4, // Kitchen
input L1, L2, L3, L4, // Living room
input B1, B2, B3, B4, // Bathroom
input D1, D2, D3, D4, // Dining room
output H1, H2, H3, H4, // Heat
output C1, C2, C3, C4, // Cool
output STATUS
);
Climatecircuit c1(K1, L1, B1, D1, H1, C1);
// TODO: instantiate 3 more Climatecircuits
endmodule
Figure 2.18:
Instantiating Climatecircuit in Climateunit
Note that the instance name of the Climatecircuit instance in Figure 2.16 is c1. Recall on
a regular circuit schematic how resistors are labelled R1, R2, R3, … to distinguish them.
We must also do this in Verilog when we have multiple instances of the same module.
Creating an instance of a module in Verilog is not the same as calling a function in C –
remember we are placing physical copies of the module in our design (like we would
place components on a breadboard), and each time we create an instance it must have a
unique name.
Instantiate Climatecircuit three more times, ensuring each instantiation has a unique
instance name. Also implement the logic for STATUS using an assign statement.
Before we can test our design, there is one more thing we need to do. Now that we have
multiple Verilog files in our project, we need to tell Vivado which one of these is the
overarching, or ‘top’ Verilog module. The top-level module has a triangular box icon
next to it. Currently this is Climatecircuit.v. Now we are using Climatecircuit in
Climateunit, so we want our top-level module to be Climateunit.v. In Vivado, rightclick on Climateunit.v and select “Set as Top”. You can see the triangular box icon
now shows beside Climateunit.v.
19
Create and write a simple testbench Climateunit_tb to verify your work.
Show your simulation results from Climatecircuit and Climateunit to the demonstrator
for a checkpoint.
Checkpoint 2
Assessor
Marks (5)
— END —
20
Date
0
You can add this document to your study collection(s)
Sign in Available only to authorized usersYou can add this document to your saved list
Sign in Available only to authorized users(For complaints, use another form )