Lab 1: Five-Bit Numeric Display
Part A: Unoptimized and Optimized Display
Lab 1 is designed to give you experience designing, implementing, testing, and prototyping a simple Verilog hardware design. This lab will primarily leverage concepts from Topic 2: Combinational Logic and Topic 3: Boolean Algebra.
You will be implementing a five-bit numeric display that takes as input a five-bit binary value and displays this value as a decimal number using two seven-segment displays. This lab requires some very basic understanding of binary number systems which you should already be familiar with after reading Chapter 1 of Harris and Harris. If you have not read this chapter do so now, paying close attention to Section 1.4.1 and 1.4.2. You might also find this short primer useful:
Your implementation will exclusively use combinational logic gates. This five-bit numeric display will be reused extensively across all of the remaining labs. The lab includes five parts:
-
Part A: Unoptimized and Optimized Display
- Must be completed individually
- Due 9/13 @ 11:59pm via GitHub
- Students should work on Part A before, during, and after your assigned lab section during the week of 9/7
-
Part B: Breadboard and FPGA Primer
- Done with randomly assigned partner
- Due week of 9/7 during assigned lab section
- Even though completed with a partner, every student must turn in their own paper check-off sheet in their lab section!
-
Part C: Build System
- Must be completed individually
- Due 9/17 @ 11:59pm via GitHub
- Students should work on Part C before, during, and after your assigned lab section during the week of 9/14
-
Part D: FPGA Prototype
- Done with randomly assigned partner
- Due week of 9/14 during assigned lab section
- Even though completed with a partner, every student must turn in their own paper check-off sheet in their lab section!
-
Part E: Report
- Done with same partner as in Part D
- Due week of 9/14, three days after lab section @ 11:59pm via Canvas
- Post-lab survey on Canvas is due at the same time as the report
This handout assumes that you have read and understand the course
tutorials and that you have attended the discussion sections. To get
started, use VS Code to log into a specific ecelinux server, source the
setup script, and clone your individual remote repository from GitHub:
% source setup-ece2300.sh
% mkdir -p ${HOME}/ece2300
% cd ${HOME}/ece2300
% git clone git@github.com:cornell-ece2300/lab1-netid
% cd ${HOME}/ece2300/lab1-netid
% tree
where netid should be replaced with your NetID. You can both pull and
push to your individual remote repository. If you have already cloned
your individual remote repository, then use git pull to ensure you have
any recent updates before working on your lab assignment.
Your repo includes the following files in the lab1 subdirectory for
Part A. These files are for modeling real hardware using the
synthesizable subset of Verilog.
BinaryToSevenSegUnopt_GL.v: Binary-to-seven-segment converter (unoptimized)BinaryToSevenSegOpt_GL.v: Binary-to-seven-segment converter (optimized)BinaryToBinCodedDec_GL.v: Binary-to-BCD converterDisplayUnopt_GL.v: Five-bit numeric display (unoptimized)DisplayOpt_GL.v: Five-bit numeric display (optimized)
Your repo includes the following files in the lab1/test subdirectory
for part A. These files are for systematic and automatic testing of your
hardware using the non-synthesizable subset of Verilog.
BinaryToSevenSeg-test-cases.v: Shared test cases for binary-to-seven-segmentBinaryToSevenSegUnopt_GL-test.v: Test bench for binary-to-seven-segment (unoptimized)BinaryToSevenSegOpt_GL-test.v: Test bench for binary-to-seven-segment (optimized)BinaryToBinCodedDec_GL-test.v: Test bench for binary-to-BCD converterDisplay-test-cases.v: Shared test cases for five-bit numeric displayDisplayUnopt_GL-test.v: Test bench for five-bit numeric display (unoptimized)DisplayOpt_GL-test.v: Test bench for five-bit numeric display (optimized)
Your repo includes the following file in the lab1/sim subdirectory.
This file is for an interactive simulator for final testing of your
hardware in a similar context to what you will be doing in Part D on the
actual hardware in the lab.
display-sim.v: Interactive simulator for five-bit numeric display
The _GL suffix indicates that these hardware designs must be
implemented using explicit gate-level modeling. This means students are
only allowed to use these Verilog constructs in their Verilog hardware
designs:
wire(single bit and multiple bit)not,and,nand,or,nor,xor,xnor- literals (e.g.,
1'b0,1'b1) - wire slicing (e.g.,
x[0],x[1:0]) - concatentation (e.g.,
{x,y}) assignfor connecting wires (e.g.,assign x = y;);assignfor setting a wire to a constant value (e.g.,assign x = 1'b0;)- module instantiation
Using any other Verilog constructs in your Verilog hardware designs will result in significant penalties for code functionality and code quality. If you have any questions on what Verilog constructs can and cannot be used, please ask an instructor. There are no restrictions on Verilog constructs in test benches or interactive simulators.
Part A is divided into eight steps. Complete each step before moving on to the next step.
- Step 1. Construct truth table for binary-to-seven-segment converter
- Step 2. Implement and test
BinaryToSevenSegUnopt_GL - Step 3. Construct truth table for binary-to-BCD converter
- Step 4. Implement and test
BinaryToBinCodedDec_GL - Step 5. Implement and test
DisplayUnopt_GL - Step 6. Construct Karnaugh maps for optimized design
- Step 7. Implement and test
BinaryToSevenSegOpt_GL - Step 8. Implement and test
DisplayOpt_GL
Students will almost certainly need to spend significant time outside of their lab session to complete Part A. Students with a lab session early in the week can use their lab session to get started with the help of the course staff and then finish on their own before the deadline. Students with a lab session late in the week can get started on their own and use their lab session to finish their lab with the help of the course staff.
1. Five-Bit Numeric Display Interface
You will be implementing a five-bit numeric display that takes as input a five-bit binary value and displays this value as a decimal number using two seven-segment displays. This section describes the required interface for the five-bit numeric display (i.e., the ports for the module and the module's functional behavior), and the following sections describe the required implementation (i.e., what goes inside the module).
Your five-bit numeric display should take as input a five-bit binary value and display the corresponding two-digit decimal value. Recall that a five-bit binary number can represent the decimal numbers 0 through 31, and these decimal numbers use up to two decimal digits. We will use two seven-segment displays. Each seven-segment display will display one decimal digit.

A seven-segment display includes seven light-emitting diodes (or "segments") arranged in the following pattern.

Notice how each segment is numbered from 0 to 6. A seven-segment display has seven input bits (numbered 0 to 6), and each "segment" can be independently turned on or off by the corresponding input bit. This enables displaying the numbers from 0 to 9.

Follow the interface specification!
The decimal digits shown above are the interface specification. You must display the decimal digits exactly as show above. There are alternative ways to display some numbers. For example, 9 could potentially also be shown with segment 3 turned on, but then it would not follow the given interface specification. You must follow the interface specification!
The specific seven-segment displays we will be using in this course are active low. This means the segment is turned on when the corresponding bit is zero and the segment is turned off when the corresponding bit is one.
You should display a leading zero for numbers less than 10. So if the input to our five-bit numeric display is the binary value 00001 (decimal value 1), then the output for your five-bit numeric display should be 1000000 for the tens position since we want to display 0 (i.e., we want segments 0-5 on and segment 6 off). If the binary value 00001 (decimal value 1), then the output for your five-bit numeric display should be 1111001 for the ones position since we want to display 1 (i.e., we want segments 1-2 to be on, and the remaining segments to be off). These outputs would result in the following being displayed on the two seven-segment displays.

So if the input to our five-bit numeric display is the binary value 01111 (decimal value 15), then the output for your five-bit numeric display should be 1111001 for the tens position and 0010010 for the one's position (i.e., segments 1 and 4 are on and the remaining segments are off). These outputs would result in the following being displayed on the two seven-segment displays.

Ensure you understand these examples before continuing to work on the lab. Remember, a segment is turned on when the corresponding bit is zero and the segment is turned off when the corresponding bit is one.
2. Five-Bit Numeric Display Implementation
Implementing an optimized five-bit numeric display as a single monolithic
module would be quite complex, so we will instead use the abstraction
principles of modularity, hierarchy, and regularity to enable an
incremental design approach. The following block diagram illustrates how
we can implement the five-bit numeric display with two kinds of
submodules: a binary-to-seven-segment converter
(BinaryToSevenSegUnopt_GL) and a binary-to-BCD converter
(BinaryToBinCodedDec_GL).

2.1. Unoptimized Binary-to-Seven-Segment Converter
The binary-to-seven-segment converter has a four-bit binary input port and a seven-bit output port. Each bit of the output port corresponds to one bit of a seven-segment display. The converter should turn on (i.e., set to zero) all of the appropriate segments to display the input value as a decimal number. So if the input to the converter is binary value 0111 (decimal value 7) then the output of the converter should be 1111000 which turns on segments 0, 1, 2 displaying the number 7. If the input is invalid (i.e., greater than 9), all segments should be turned on. Fill in the truth table in the provided worksheet before implementing the binary-to-seven-segment converter (you will need to bring this worksheet to lab for Part D):
Once you have the truth table then you can systematically implement this truth table at the gate-level using what you learned in lecture and the discussion section. Hint: You will need four NOT gates. You will need one AND gate for every row in the truth table where the outputs are not all zero. You will need one OR gate for every output which ors together the outputs from the appropriate AND gates.
2.2. Binary-to-BCD Converter
The binary-to-BCD converter has a five-bit input port and two four-bit output ports: a tens output port and a ones output port. The converter should convert the input binary value into a decimal ones digit and a decimal tens digit, but these digits are encoded as binary values. So for example, if the input to the converter is binary value 01111 (decimal value 15), then the tens output should be binary value 0001 (decimal value 1) and the ones output should be binary value 0101 (decimal value 5). Fill in the truth table in the provided worksheet before implementing the binary-to-seven-segment converter (you will need to bring this worksheet to lab for Part D):
2.3. Unoptimized Display
Once you have implemented (and thoroughly tested!) your binary-to-seven-segment converter and binary-to-BCD converter, you can then compose them to implement the five-bit numeric display as shown above. You will instantiate the binary-to-seven-segment converter twice; once to display the tens digit and once to display the ones digit.
2.4. Optimized Implementation
Your initial design for the binary-to-seven-segment converter might not be terribly optimized; we want to explore implementing the same truth table with fewer combinational logic gates. In this part, you should use what you have learned in lecture on Karnaugh maps to see if you can reduce the amount of logic required to implement the binary-to-seven-segment converter. Fill in the Karnaugh maps in the provided worksheet before implementing the optimized design (you will need to bring this worksheet to lab for Part D):
You should use BinaryToSevenSegOpt_GL for your optimized
implementation. We strongly recommend taking a incremental design
approach! Start by copying your unoptimized implementation into
BinaryToSevenSegOpt_GL. Then pick a single output port to optimize.
Find the corresponding Karnaugh map in your worksheet. Then transform
this Boolean logic equation into a gate-level netlist which you can then
implement in Verilog using gate-level modeling. Replace just the logic
for that output port with the newly optimized version. Leave all of the
logic for the other output ports unoptimized. Then use your tests to
verify the converter is still working. If not, you know where to focus
your debugging. If so, you can move on to the next output port.
Once you have implemented (and thoroughly tested!) your optimized
binary-to-seven-segment converter, you can then instantiate this
optimized unit within DisplayOpt_GL to create an optimized five-digit
numeric display. Note that students do not need to optimize their
binary-to-BCD converter.
What if your "optimized" design ends up taking more logic?
It is ok if your "optimized" design actually ends up taking more logic gates than your "unoptimized" design. You should hopefully be able to figure out why this might happen. Focus on gaining experience using Karnaugh maps to simplifying Boolean logic equations as opposed to finding the absolutely most optimized implementation.
2. Testing Strategy
You are responsible for developing an effective testing strategy to ensure all implementations are correct. Writing tests is one of the most important and challenging aspects of designing hardware. Hardware engineers often spend far more time implementing tests than they do implementing the actual hardware design.
Continually having to use the same complex commands over and over can be tedious. We strongly recommend creating a few Bash shell scripts to enable you to automatically execute the commands to lint, compile, and run systematic test benches. See the discussion section for an example.
2.1. Linting
Verilog is a very permissive language. We will be using verilator for
linting which involves statically analyzing your hardware design to
help catch syntax bugs early. You can run verilator to lint your
BinaryToSevenSegUnopt_GL module like this:
Here is how to lint the other modules.
% cd ${HOME}/ece2300/lab1-netid
% verilator --Wall --lint-only lab1/BinaryToBinCodedDec_GL.v
% verilator --Wall --lint-only lab1/DisplayUnopt_GL.v
% verilator --Wall --lint-only lab1/BinaryToSevenSegOpt_GL.v
% verilator --Wall --lint-only lab1/DisplayOpt_GL.v
You should always lint your designs before trying to compile them into a simulator. You can also lint your test benches, but this requires a more complex command line option so we will defer this to Part C.
2.2. Systematic Unit Testing
We will be using a lightweight testing framework to support a systematic and automatic unit testing strategy for every module you implement in this part:
BinaryToSevenSegUnopt_GLBinaryToSevenSegOpt_GL.vBinaryToBinCodedDec_GLDisplayUnopt_GLDisplayOpt_GL.v
For each module, we provide a test bench for you to use along with a basic test case and an xprop test case. In future lab assignments, we will use directed and random testing, but in this lab assignment you can use exhaustive testing where you try every single possible input. We have provided you an empty test case for you to insert your checks for exhaustive testing in these files:
BinaryToSevenSeg-test-cases.vBinaryToBinCodedDec_GL-test.vDisplay-test-cases.v
Once you have added exhaustive testing (and linted your design!), you can compile your test bench using Icarus Verilog like this:
% cd ${HOME}/ece2300/lab1-netid
% iverilog -Wall -g2012 -o BinaryToSevenSegUnopt_GL-test lab1/test/BinaryToSevenSegUnopt_GL-test.v
% cd ${HOME}/ece2300/lab1-netid
% ./BinaryToSevenSegUnopt_GL-test
% ./BinaryToSevenSegUnopt_GL-test +test-case=1
% ./BinaryToSevenSegUnopt_GL-test +test-case=2
% ./BinaryToSevenSegUnopt_GL-test +test-case=2 +dump-vcd=waves.vcd
Every time you modify any of the files, you need to recompile your test bench with Icarus Verilog and then rerun the simulation. Here is how to compile and run the tests for the other modules.
% iverilog -Wall -g2012 -o BinaryToBinCodedDec_GL-test lab1/test/BinaryToBinCodedDec_GL-test.v
% ./BinaryToBinCodedDec_GL-test
% iverilog -Wall -g2012 -o DisplayUnopt_GL-test lab1/test/DisplayUnopt_GL-test.v
% ./DisplayUnopt_GL-test
% iverilog -Wall -g2012 -o BinaryToSevenSegOpt_GL-test lab1/test/BinaryToSevenSegOpt_GL-test.v
% ./BinaryToSevenSegOpt_GL-test
% iverilog -Wall -g2012 -o DisplayOpt_GL-test lab1/test/DisplayOpt_GL-test.v
% ./DisplayOpt_GL-test
2.3. Interactive Simulator
We have provided you a simple interactive simulator which will emulate what you will prototype on the FPGA in Part D. After finishing your implementation for the unoptimized display, you can run the display simulator like this:
% cd ${HOME}/ece2300/lab1-netid
% iverilog -Wall -g2012 -o display-sim lab1/sim/display-sim.v
% ./display-sim +switches=01111
The display simulator will show what the two seven segment displays would look like on the FPGA prototype.
3. Lab Code Submission
To submit your code you simply push your code to GitHub. You can push
your code as many times as you like before the deadline. Students are
responsible for going to the GitHub website for your repository, browsing
the source code, and confirming the code on GitHub is the code they want
to submit is on GitHub Be sure to verify your code is passing your tests
both on ecelinux and on GitHub Actions. Your design code will be
assessed both in terms of code quality, verification quality, and
functionality.
3.1. Code Quality
Your code quality score will be based on how well you follow the course coding conventions posted here:
Code quality for both Part A will not be assessed until after the Part C deadline.
3.3. Verification Quality
Verification quality is based on how well your testing enables making a compelling case for correctness. In this lab, we are using exhaustive testing so achieving high verification quality should be straight forward.
3.2. Functionality
Your functionality score will be determined by running your code against a series of tests developed by the instructors to test its correctness. Here is how we will be testing your final code submission for Part A.
% mkdir -p ${HOME}/ece2300
% cd ${HOME}/ece2300
% git clone git@github.com:cornell-ece2300/lab1-netid
% verilator -Wall --lint-only lab1/BinaryToSevenSegUnopt_GL.v
% iverilog -Wall -g2012 -o BinaryToSevenSegUnopt_GL-test lab1/test/BinaryToSevenSegUnopt_GL-test.v
% ./BinaryToSevenSegUnopt_GL-test
% verilator -Wall --lint-only lab1/BinaryToBinCodedDec_GL.v
% iverilog -Wall -g2012 -o BinaryToBinCodedDec_GL-test lab1/test/BinaryToBinCodedDec_GL-test.v
% ./BinaryToBinCodedDec_GL-test
% verilator -Wall --lint-only lab1/DisplayUnopt_GL.v
% iverilog -Wall -g2012 -o DisplayUnopt_GL-test lab1/test/DisplayUnopt_GL-test.v
% ./DisplayUnopt_GL-test
% verilator -Wall --lint-only lab1/BinaryToSevenSegOpt_GL.v
% iverilog -Wall -g2012 -o BinaryToSevenSegOpt_GL-test lab1/test/BinaryToSevenSegOpt_GL-test.v
% ./BinaryToSevenSegOpt_GL-test
% verilator -Wall --lint-only lab1/DisplayOpt_GL.v
% iverilog -Wall -g2012 -o DisplayOpt_GL-test lab1/test/DisplayOpt_GL-test.v
% ./DisplayOpt_GL-test