Lab 1: Five-Bit Numeric Display
Part A: Unoptimized Code
Lab 1 is a warmup designed to give you experience designing, implementing, testing, and prototyping a simple Verilog hardware design. This lab will focus primarily leverage on 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 four parts:
-
Part A: Unoptimized Code and Testing
- Must be completed individually
- Due 9/11 @ 11:59pm via GitHub
- Students should work on Part A before, during, and after your assigned lab section during the week of 9/8
-
Part B: Optimized Code and Testing
- Must be completed individually
- Due 9/18 @ 11:59pm via GitHub
- Plan to start on Part B during the week of 9/8
- Even though Part B is due on 9/18 you still need the code ready to go before your lab section the week of 9/15!
-
Part C: FPGA Analysis and Prototyping
- Done with randomly assigned partner
- Due week of 9/15 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 D: Report
- Done with same partner as in Part C
- Due week of 9/15, three days after lab section @ 11:59pm via Canvas
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/netid
% cd ${HOME}/ece2300/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)BinaryToBinCodedDec_GL.v
: Binary-to-BCD converterDisplayUnopt_GL.v
: Five-bit numeric display (unoptimized)
Your repo includes the following files in the lab1/test
subdirectory.
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)BinaryToBinCodedDec_GL-test.v
: Test bench for binary-to-BCD converterDisplayUnopt-test-cases.v
: Shared test cases for five-bit numeric displayDisplayUnopt_GL-test.v
: Test bench for five-bit numeric display (unoptimized)
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 C 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 should be
implemented using the logic gate or boolean equation layers of
abstraction. This means students are only allowed to use these Verilog
constructs in their Verilog hardware designs:
wire
not
,and
,nand
,or
,nor
,xor
,xnor
- literals (e.g.,
1'b0
,1'b1
) - wire slicing (e.g.,
x[0]
,x[1:0]
) assign
for connecting wires (e.g.,assign x = y;
);assign
for 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 five 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
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. Interface and Implementation Specification
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 first describes the required interface for the five-bit numeric display (i.e., the ports for the module and the module's functional behavior) before describing the required implementation (i.e., what goes inside the module).
1.1. Five-Bit Numeric Display Interface
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, which is the corresponding decimal value needs two digits. We will use two seven-segment displays to display the 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. You must display the decimal digits exactly as shown below!
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 111_1001 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.
1.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
).
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. You will need to construct an appropriate truth table before implementing the binary-to-seven-segment converter. We have provided you a template below. 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.
Input as Decimal Value |
Input as Binary Value |
Output 654 3210 |
---|---|---|
0 | 0000 | 100 0000 |
1 | 0001 | 111 1001 |
2 | 0010 | 010 0100 |
... | ... | ... |
15 | 1111 | 000 0000 |
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). You will need to construct an appropriate truth table before implementing the binary-to-BCD converter. We have provided you a template below. Once you have the truth table then you can systematically implement this truth table using what you learned in lecture and the discussion section.
Input as Decimal Value |
Input as Binary Value |
Tens | Ones |
---|---|---|---|
0 | 00000 | 0000 | 0000 |
1 | 00001 | 0000 | 0001 |
2 | 00010 | 0000 | 0010 |
... | ... | ... | ... |
10 | 01010 | 0001 | 0000 |
... | ... | ... | ... |
31 | 11111 | 0011 | 0001 |
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. 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/netid
% verilator --Wall --lint-only lab1/BinaryToBinCodedDec_GL.v
% verilator --Wall --lint-only lab1/DisplayUnopt_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 B. When you run the verilator
command, you should see output like this:
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_GL
BinaryToBinCodedDec_GL
DisplayUnopt_GL
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.v
BinaryToBinCodedDec_GL-test.v
Display-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/netid
% iverilog -Wall -g2012 -o BinaryToSevenSegUnopt_GL-test lab1/test/BinaryToSevenSegUnopt_GL-test.v
And you can then run your test bench like this:
% cd ${HOME}/ece2300/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
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
2.3. Interactive Simulator
We have provided you a simple interactive simulator which will emulate what you will prototype on the FPGA in Part C. After finishing your implementation for the unoptimized display, you can run the display simulator like this:
% cd ${HOME}/ece2300/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. 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 the way you format the text in your source files. Follow the coding convention as illustrated in the discussion sections and in the code provided to students. User proper indentation of two spaces. Use appropriate vertical white space.
Code quality will also factor in uploading the correct files to GitHub (only source files should be uploaded, no generated build files). Students must not commit executable binaries or any other unnecessary files. There should be no reason to add any new files to the repo.
Code quality will factor in proper use of comments. You should add comments to explain your code but not too many comments. Note that students must remove unnecessary instructional comments that are provided by instructors in the code distributed to students. This is an example of the kind of comment you need to remove:
//''' LAB ASSIGNMENT '''''''''''''''''''''''''''''''''''''''''''''''''''
// Implement the binary to binary coded decimal converter
//>'''
This comment is an instruction on what to implement. Once you have implemented the module then the comment makes no sense and must be removed.
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/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