Multiplexers, Hierarchy, and HEX Displays
The Altera University web site at https://www.altera.com/support/training/university/materials-tutorials.html has tutorials that you should read on your own. For this lab, you should do the Verilog version of Quartus Introduction. Perform the tutorial steps outside of the lab using simulation only.
The purpose of this exercise is to learn the importance of simulations and hierarchies when writing in Verilog. We will use switches SW9 0 on the DE1-SoC board as inputs to the circuit. We will use light emitting diodes (LEDs) and 7-segment displays as output devices.
Note that we may refer to signals as SW9 0, i.e., with the subscripts, but when you write your Verilog, you will need to use SW, SW, etc.
Preparation Before the Lab
For this lab, and all future labs, you will be asked to prepare schematics, Verilog code and ModelSim simulations in your preparation. The schematics should show the structure of your Verilog code, much like the schematics in Lab 1 showed how your circuit should be built. Your Verilog code will consist of a number of modules and the schematic should show how the modules are wired together, and the input and output ports of your circuit, i.e., connections to switches, LEDs, displays, etc. Think of modules as just complex gates, such as the gates you wired together in Lab 1. All port names of the modules, wires and I/O ports should be clearly labeled. Figure 1 is an example. Your Verilog code should be well-commented. For your simulations, you should have a script, or number of scripts, that test important aspects of your design. Print out the waveforms from the simulator and paste them into your lab book. If the simulation is very long, just print out enough to show that key parts of your circuit are working and as evidence that you have done the simulations. It is not necessary to have pages and pages of waveforms. However, occasionally, you will be asked to demonstrate and explain your entire simulation to the TA in the lab, so be prepared for this.
For this lab do the following preparation:
Part I Do all of the simulation parts and prepare a bitstream that you can test when you go to the lab. You do not need to demonstrate your circuit working, but you may be asked questions about the preparation.
Part II and Part III You are required to write the Verilog code for both parts. For marking of the preparation, you are required to show the teaching assistants your schematics, Verilog code, and ModelSim simulations for Parts II and III.
You are required to implement and test all of Parts II and III of the lab. You need to demonstrate both parts to the teaching assistants.
In this part you are provided with two files: a Verilog file with a design example and a simulation script to show some basic commands for simulating the design.
Verilog File (.v):
The DE1-SoC board provides 10 toggle switches, called SW9 0, that can be used as inputs to a circuit, and 10 red lights, called LEDR9 0, that can be used to display output values.
The fundamental construct for defining a block of circuitry is the module (text Section A.8). A Verilog file has been provided for a 2 to 1 multiplexer (text Section 2.8.2). The top module mux has 3 inputs. SW is the input 0 signal, SW is the input 1 signal, and SW is the select signal. The output is displayed on LEDR.
module mux (SW, LEDR); //module name and port list
Note that the above module definition for mux actually declares more than 3 inputs and 1 output, contrary to the above statement. By using SW and LEDR in the module port definition, you actually declare all switches SW9 0 as inputs and all LEDR9 0 as outputs of the module, even though only a subset of the inputs and outputs are used. It is just being lazy, and does not affect the circuit created. Strictly speaking, according to the original statement, the declaration should have been:
module mux (SW, SW, SW, LEDR); //module name and port list
The top module, mux, is a very trivial example of using hierarchy (text Section 2.10.3) where it instantiates a single mux2to1 module uniquely identified as instance u0. In the more general case, any module can instantiate a number of interconnected modules, just like when you wired up a number of chips in Lab 1. However, in any circuit you build, there must be only one top-level module. The .port(connection) statements match the port names defined in the mux2to1 module to the connections inside the mux module. Think of the port name as the pin on a chip and you are connecting wires in the mux module to the pins of the chip, i.e., the ports of the mux2to1 module instance.
mux2to1 u0 (
.x(SW), // assign port SW to port x
.y(SW), // assign port SW to port y
.s(SW), // assign port SW to port s
.m(LEDR) // assign port LEDR to port m
Simulation File (.do):
After examining the Verilog file to understand what it is supposed to do, it is time to verify that the code functions properly. The Verilog file describes the structure and behaviour of a circuit. Before actually building the circuit, it is important to determine whether the Verilog description actually does what you intend. This is done by simulation of the circuit, which is done prior to actually building the circuit and testing it for real. We can perform a simulation using a script written in a .do file. This file is also provided by your instructor.
Inside the .do file, we start off by creating a working directory called work using the vlib command. We then compile the Verilog file using vlog and load it into the simulation with the vsim command. Lastly, to display all the signals on the waveform viewer, we put f/*g after add wave.
compile all verilog modules in mux.v to working dir
could also have multiple verilog files
load simulation using mux as the top level simulation module vsim mux
#log all signals and add some signals to waveform window log f/*g
add wave f/*g would add all items in top level simulation module add wave f/*g
Once everything is initiated, we can set the input signals to be a 1 or a 0 with the force command and run the simulation for x ns with the run command.
set input values using the force command, signal names need to be in brackets force fSWg 0 # force SW to 0
force fSWg 1 # force SW to 1 force fSWg 0 # force SW to 0
run simulation for a few ns
run 10ns # run for 10 ns
When you have familiarized yourself with the .do file, open ModelSim, and in the terminal window (near the bottom) change to the file’s working directory using the cd command and type do wave.do (or the file name you named your .do file).
Look at the simulation. You might be wondering how the time intervals are determined at this point. If we open the Verilog file again, we can see that the very first line states the timescale with the time unit and time precision. All time values are read as the time unit which is rounded to the nearest time precision.
Perform the following steps:
Run the default .do file given by your instructor.
Create your own test cases for the .do file and demonstrate that it works.
Create a new Quartus project for the Verilog code provided and test it on the board during your lab session. Do not forget that you will need the DE1 SoC.qsf file to define how the switches and LEDs connect to the pins.
Compare the output results from the board with the simulations you performed.
Did you notice a significant compilation time difference between ModelSim and the actual on board test re-sults? The difference becomes greater as the complexity of the circuit increases. Comment on this difference and its impact on debugging.
In this section, you will write Verilog to implement three 7400-series chips from Lab 1. Then, you will use the Verilog implementation of these chips to implement the 2-to-1 multiplexer in Part I above. You will implement the 2-to-1 multiplexer using the FPGA on the DE1-SoC board, and check its behaviour using switches and lights. Part of the rationale for this exercise is to emphasize that the hardware functionality within the discrete 7400-series chips you used in Lab 1 can also be realized on an FPGA. In essence, the FPGA becomes the “protoboard” (breadboard) where you build circuits and interconnect them with one another.
To complete this section, you will need to use the wire declaration (text Section A.6.1) to create wires that can be used to connect the multiple blocks together.
wire Connection; //creates a wire called Connection
The wire created above is called Connection and it can be used to connect the output of a module to the input of a module, the same way you used a physical wire in Lab 1 to connect the output of one gate to the input of another gate. Figure 1 shows a schematic of two modules using the wire Connection.
module block1(in1, out1);
module block2(in2, out2);
Figure 1: Using the wire Connection to make a connection between two modules
The following code fragment corresponds to Figure 1. It creates instances of modules block1 and block2, named B1 and B2, respectively. When using hierarchy, think of a module definition as the pattern for a sub-circuit and the definition of an instance of a module as creating an actual copy of that sub-circuit that you can use. You can create as many instances of a module as you need, which is like taking a number of the same type of chip from the cupboard and wiring them together to make a larger circuit. Here, B1 is a sub-circuit that has the functionality defined by module block1. The wire Connection is used to wire the module instances together.
wire Connection; //Declare the wire called Connection
. . . //Other stuff
block1 B1 (
.in1(SW), // assign port SW to port in1
.out1(Connection) // assign wire Connection to port out1
block2 B2 (
.in2(Connection), // assign wire Connection to port in2
.out2(LEDR) // assign port LEDR to port out2
Another way to make a connection is to use the assign statement. For example, if we wanted to connect the wire called Connection to LEDR0, we do the following:
assign LEDR = Connection; // joins wire Connection to LEDR
74LS04/05 (six inverters)
74LS08/09 (four 2-input AND gates) 74LS32 (four 2-input OR gates)
Refer to the Lab 1 handout for the specifications of the above chips.
As a hint, here is an example module declaration for the 74LS04/05:
module v7404 (input pin1, pin3, pin5, pin9, pin11, pin13, output pin2, pin4, pin6, pin8, pin10, pin12);
For another hint, see the Parts Handbook on the Piazza section of the website, which has a complete module definition example for a 74LS00/03 (four 2-input NAND gates). Note that you do not need to add power/ground pins to the module declarations, as you will be implementing their functionality on an FPGA chip.
After writing and simulating the three 7400-series modules, write another Verilog module that implements a 2-to-1 multiplexer using the three 7400-series modules. You will need one instance of each of your three 7400-series modules. The reason is that the logic function for the 2-to-1 multiplexer is: m = sx + sy, which requires NOT, AND, and OR. Note that although the 2-to-1 multiplexer will not use all of the internal gates of each 7400-series module, the 7400-series modules should be complete and contain all of the functionality of the original corresponding 7400-series chip. Using “named-port” instantiation will allow you to ignore the unused gates in the 7400-series chips in your top-level design of the 2-to-1 multiplexer.
Perform the following steps.
Draw a schematic showing how you will connect the three 7400-series modules to build the 2-to-1 multi-plexer. Your schematic should have a top-level module that represents the 2-to-1 multiplexer and inside the top-level module you should instantiate each of the 7400-series modules. Give names to all the wires that you need to make the connections and also give names to all the instances of the 7400-series modules. Label the used pin names on each module. Be prepared to explain your schematic to the TA as part of your prelab. The schematic should reflect exactly how you are going to write your Verilog code. For an example, look at Schematic 1 in Section 4.3.2 of the Parts Handbook on the Piazza site.
After drawing your schematic, write the Verilog code that corresponds to your schematic. Your Verilog code should use the same names for the wires and instances shown in the schematic. In total, you should have four Verilog modules.
Create a new Quartus project for your circuit.
Include your Verilog file for the circuit in your project. Use the same switches and light as you used in Part I of this lab. Do not forget that you will need the DE1 SoC.qsf file to define how the switches and LEDs connect to the pins.
Simulate the 7400-series modules and the top-level module with ModelSim for different input values. Do enough simulations to convince yourself that the circuit is working. You must show these to the TA as part of your prelab. You should have four simulations: one for each of the 7400-series modules, and one for the top-level module. Meaning, you should have four .do files, each simulating a different module (use the vsim command in the .do file to define which module to simulate).
Compile the project.
Download the compiled circuit into the FPGA chip. Test the functionality of the circuit by toggling the switches and observing the LEDs.
Figure 2: HEX decoder driving a HEX display
In this part of the lab, you are to design a HEX decoder for the 7-segment HEX display (text Section 2.8.3) as shown in Figure 2. In general, a decoder is a circuit that takes an input pattern and converts (decodes) it into a different pattern. A HEX decoder determines how to drive a HEX display according to the value at the input of the decoder as shown in Table 1. You should create a module for the HEX decoder so that you can instantiate it each time you need to drive a HEX display, i.e., instantiate one HEX decoder for every HEX display you are using. You will also use the HEX decoder in future labs because we always want to display some kind of output number.
To figure out the circuit for the HEX decoder, work through the following steps:
How many inputs to the decoder are there and what are they connected to?
How many outputs from the decoder are there and what are they connected to? Note that each segment in the HEX display can be individually controlled with a separate input to the HEX display.
Knowing the inputs and outputs of the HEX decoder will tell you what input and output ports should be in your module declaration for the HEX decoder.
For each of the outputs of your decoder, derive the truth table and then the logic expression corresponding to the truth table. For example, consider Segment 1. For each row in the truth table for Segment 1 figure out whether Segment 1 should be turned on or off. Do this for every segment. You should end up with a logic expression for every segment. These are the equations that will be inside your HEX decoder module.
The 7-segment display uses a common anode. What does common anode mean in terms of lighting up a segment? You should be able to find the answer online. Section 3.6.2 in the DE1-SoC User manual also tells you what is needed to turn on a segment.
Table 1: Truth table for HEX decoder
Perform the following steps:
Draw a schematic of the circuit for the HEX decoder module and be prepared to explain it to the TA as part of your prelab. You do not need to draw gates for each of your equations. In Verilog, you are just going to input the equations, so your schematic can just refer to Eqn 0, Eqn 1, . . . where the equations are defined from your derivations. Note that you do not have to simplify your equations. The tool will do that for you!
The schematic should reflect how you are going to write your Verilog code, so be sure to use the same names for your signals in the schematic and yor Verilog code. It can make debugging easier.
Create a new Quartus project for your circuit.
Create a Verilog module for the 7-segment decoder. Instantiate your decoder inside a top-level module. The top-level module should have the c3c2c1c0 inputs connected to switches SW3 0, and the outputs of the decoder connected to the HEX0 display on the DE1-SoC board. The segments in this display are called HEX00, HEX01, : : :, HEX06. You should declare the 7-bit port like this:
output [6:0] HEX0;
in your Verilog code so that the names of these outputs match the corresponding names in the DE1-SoC User Manual and the pin assignment DE1 SoC.qsf file.
Simulate your circuit with ModelSim for a variety of input settings, ensuring the output waveforms are correct. You must show this to the TA as part of your prelab.
Compile the project.
Download the compiled circuit into the FPGA chip. Test the functionality of the circuit by toggling the SW3 0 switches and observing the 7-segment display.